diff --git a/session-mechanism/SKILL.md b/session-mechanism/SKILL.md index eba4b92..d76008a 100644 --- a/session-mechanism/SKILL.md +++ b/session-mechanism/SKILL.md @@ -960,7 +960,8 @@ assets/ board.html design-tokens.css ⚠️ 逐文件清单 + 来源(provenance)⇒ `references/manifest.md`(**唯一权威**,⛔ 别按本树猜)。 -**⛔ 已无必需外部依赖(2026-10-04)**:原先声明「依赖 `agent-operating-rules`」——现已把它的**排版核心块内联**为 `references/03-回复排版-核心块.md`、把 `_env.py` 的技能库识别特征改为**候选数组(首个=本包自己)** ⇒ **本包自包含**。📌 那个技能若同时装着,属**可选增强**(多了跨项目排版/去 AI 味的完整版),⛔ 不装也能跑。 +**⛔ 已无必需外部依赖(2026-10-04)**:原先声明「依赖 `agent-operating-rules`」——现已把**排版核心块收进本包并定为权威** ⇒ `references/03-回复排版-核心块.md` +(2026-10-06 校正:原措辞是「内联副本」,与 03 号自称「权威源」打架 ⇒ 权威声明分裂)、把 `_env.py` 的技能库识别特征改为**候选数组(首个=本包自己)** ⇒ **本包自包含**。📌 那个技能若同时装着,属**可选增强**(多了跨项目排版/去 AI 味的完整版),⛔ 不装也能跑。 --- diff --git a/session-mechanism/references/03-回复排版-核心块.md b/session-mechanism/references/03-回复排版-核心块.md index 523908e..ba02514 100644 --- a/session-mechanism/references/03-回复排版-核心块.md +++ b/session-mechanism/references/03-回复排版-核心块.md @@ -1,8 +1,11 @@ # 回复排版与格式 · 核心块(**机器可读 · 权威源**) -> **这份是权威源**(跨工作区、跨机器)。两个消费者: -> ① 钩子 `scripts/hooks/reply-style-guard.py` **每轮现读本块**注入会话; -> ② 注入器 `scripts/apply-reply-rules.py` 把它写进**各环境的规则文件**(`CODEBUDDY.md` / `AGENTS.md`)。 +> 🔴🔴 **权威源 = 本文件**(`session-mechanism/references/03-回复排版-核心块.md`,跨工作区、跨机器)。 +> **两个消费者(都在本包内)**: +> ① 钩子 `scripts/hooks/reply-style-guard.py` —— **每轮现读本块**注入会话(包内优先,②级回退); +> ② 注入器 `scripts/apply-reply-rules.py` —— 把它写进**各环境的规则文件**(`CODEBUDDY.md` / `AGENTS.md`)。 +> ⚠️ `agent-operating-rules/references/回复排版-核心块.md` 是**同内容副本**,⛔ **改口径只改本文件**, +> 改完同步副本 + 用 ② 重生成各环境文件。 > ⛔ **不要在本文件之外再抄一份规则文本** —— 抄了就成第二真相源,两边必然漂。 > 用户令(2026-10-02):「所有会话中回复排版和格式要求和规则,也要整合到会话技能中,使用时配置到对应环境文件中」。 diff --git a/session-mechanism/scripts/apply-reply-rules.py b/session-mechanism/scripts/apply-reply-rules.py new file mode 100644 index 0000000..8715437 --- /dev/null +++ b/session-mechanism/scripts/apply-reply-rules.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""apply-reply-rules.py —— 把「回复排版与格式」核心块**配置到目标环境的规则文件**里 + +🔴 2026-10-06:本脚本**归位到 session-mechanism**(原先只在 agent-operating-rules 里, + 而 03 号核心块却自称权威源却指着一个不在本包的注入器 ⇒ 权威声明分裂)。 + 权威源 = 本包 `references/03-回复排版-核心块.md`(**唯一**)。 + +它解决什么 +────────── +用户 2026-10-02 令(逐字): + 「**所有会话中**回复排版和格式要求和规则,也要**整合到会话技能中**, + 使用时**配置到对应环境文件中**」 +⇒ 三层分工(⛔ 别混): + · **技能**(本脚本所在包 `agent-operating-rules`)=**权威源**,跨工作区、跨机器。 + · **环境文件**(`CODEBUDDY.md` / `AGENTS.md`)=该环境的**落地配置**,由本脚本生成。 + · **每个会话**=由钩子 `reply-style-guard.py` **每轮**把核心块注入(优先用本环境的落地副本)。 + +为什么需要"注入"这一步(⛔ 不只是钩子就够) +──────────────────────────────────────────── +钩子管的是"**每一轮**紧贴用户消息",而环境文件管的是"**这个环境自己的规则**": + · 换机 / 换工作区时,钩子可能还没装,但规则文件先被读到 ⇒ 规则不能只活在钩子里; + · 环境可能有**本地定稿**(如某工作区把骨架改过)⇒ 落地副本是它自己的那份,钩子优先读它。 + +安全设计 +──────── +1. **只动标记块**:只替换 ` … ` 之间; + 块外(人工撰写的解释 / 别的规则)**一字不动**。 +2. **默认不建块**:目标里没有标记块时**只报告**(rc=1),要建得显式加 `--init` + (追加到文末,不改既有内容)—— 避免与人工撰写的内容重复成两处。 +3. **单向**:核心块内容由**技能**决定,⛔ 不许反过来把环境文件当源。 + ⇒ 本地确实要改措辞 ⇒ 改技能里那份,再对所有环境重跑本脚本。 +4. **回读校验**:写完复读并断言块在、长度变化符合预期;异常 ⇒ 不写盘。 + +用法 +──── + apply-reply-rules.py # 默认目标 = 当前目录的 CODEBUDDY.md(只报告) + apply-reply-rules.py --ws <工作区> # 目标 = <工作区>/CODEBUDDY.md + apply-reply-rules.py --target <文件> # 指定环境文件(AGENTS.md 等) + apply-reply-rules.py --check # 只校验:块在不在、是否与技能一致 + apply-reply-rules.py --apply # 真写(需先抢锁 —— 环境文件通常属机制层) + apply-reply-rules.py --apply --init # 目标没有标记块时,追加建块 +退出码:0 = 一致/成功|1 = 有差异 / 需要人看|2 = 用法或环境异常 +""" +import hashlib +import io +import os +import re +import shutil +import sys +import time + +HERE = os.path.dirname(os.path.abspath(__file__)) +SKILL = os.path.dirname(HERE) # = <技能库>/agent-operating-rules +# 🔴 2026-10-06 本包内版本:权威源就在本包 ⇒ 默认读 `references/03-回复排版-核心块.md` +CORE = os.path.join(SKILL, 'references', '03-回复排版-核心块.md') + +BEGIN = '' +BANNER = ('') + + +def rd(p, nl=''): + try: + return io.open(p, encoding='utf-8', newline=nl).read() + except OSError: + return '' + + +def md5s(s): + return hashlib.md5(s.encode('utf-8')).hexdigest()[:12] + + +def core_text(): + t = rd(CORE) + if not t: + raise SystemExit('ERROR: 缺权威源 %s' % CORE) + i = t.find(BEGIN) + if i < 0: + raise SystemExit('ERROR: 权威源里没有 %s 标记' % BEGIN) + i = t.find('\n', i) + j = t.find(END, i + 1) + return t[i + 1:j].strip() + + +def block(body, bare=False): + if bare: + return BEGIN + ' (由 agent-operating-rules 技能生成) -->\n' + body + '\n' + END + '\n' + return (BEGIN + ' (由 agent-operating-rules 技能生成 · 与技能内那份同源) -->\n' + + body + '\n' + END + '\n') + + +def cur_block(t): + i = t.find(BEGIN) + if i < 0: + return '' + i = t.find('\n', i) + j = t.find(END, i + 1) + if i < 0 or j < 0: + return '' + return t[i + 1:j].strip() + + +def main(argv): + tgt, apply_, init_, chk = '', '--apply' in argv, '--init' in argv, '--check' in argv + if '--target' in argv: + tgt = argv[argv.index('--target') + 1] + elif '--ws' in argv: + tgt = os.path.join(argv[argv.index('--ws') + 1], 'CODEBUDDY.md') + else: + tgt = os.path.join(os.getcwd(), 'CODEBUDDY.md') + tgt = os.path.normpath(tgt.replace('\\', '/')) + + body = core_text() + t = rd(tgt) + print('权威源:%s(核心块 %d 字符 / md5=%s)' % (CORE, len(body), md5s(body))) + print('目标 :%s(%s)' % (tgt, '存在' if t else '**不存在**')) + if not t: + print('⛔ 目标文件不存在 ⇒ 停手(本脚本只在既有规则文件里注入,⛔ 不凭空造规则文件)') + return 2 + + have = cur_block(t) + if have: + same = have == body + print('块状态:已存在,与技能%s(正文 md5 %s vs %s)' + % ('**一致** ✅' if same else '**不一致** ⚠️', md5s(have), md5s(body))) + if chk: + return 0 if same else 1 + if same and not apply_: + print('⇒ 无需改动') + return 0 + else: + print('块状态:**没有标记块**') + if chk: + return 1 + if not init_: + print('⇒ 默认**不建块**(避免与人工撰写内容重复成两处)。要建:加 --init(追加到文末)') + return 1 + + if not apply_: + print('⇒ 将写入(--apply 才真写):%s' % ('追加标记块' if not have else '替换块内内容')) + return 0 + + b = tgt + '.bak-replycore-' + time.strftime('%Y%m%d-%H%M%S') + shutil.copy2(tgt, b) + if have: + new = re.sub(re.escape(BEGIN) + r'.*?' + re.escape(END) + r'\n?', + block(body), t, flags=re.S) + else: + new = t.rstrip('\n') + '\n\n' + BANNER + '\n' + block(body) + io.open(tgt, 'w', encoding='utf-8', newline='').write(new) + back = rd(tgt) + assert cur_block(back) == body, '回读不符 ⇒ 已中止(备份 %s)' % b + print('✓ 已写入并回读校验通过(备份 %s)' % os.path.basename(b)) + print(' ⚠️ 环境文件通常属机制层 ⇒ 本轮若没抢到锁,写入动作本身即违规;请用 --check 先看状态。') + return 0 + + +if __name__ == '__main__': + sys.stdout.reconfigure(encoding='utf-8', errors='replace') + raise SystemExit(main(sys.argv[1:])) diff --git a/session-mechanism/scripts/hooks/reply-style-guard.py b/session-mechanism/scripts/hooks/reply-style-guard.py index 8f0795a..21eaef9 100644 --- a/session-mechanism/scripts/hooks/reply-style-guard.py +++ b/session-mechanism/scripts/hooks/reply-style-guard.py @@ -50,7 +50,9 @@ except Exception: # 🔴 找不到就报错(⛔ 不静默 % os.path.dirname(os.path.abspath(__file__))) raise -CORE_REL = os.path.join('agent-operating-rules', 'references', '回复排版-核心块.md') +# 🔴 2026-10-06:第③级兜底原指向**另一个技能**(agent-operating-rules)⇒ 与「本包自包含」矛盾, +# 且权威已定为包内 03 号 ⇒ 这里指回包内;包内路径由第②级处理,本行只是 env 覆盖时的兜底。 +CORE_REL = os.path.join('session-mechanism', 'references', '03-回复排版-核心块.md') BEGIN = '' MAX_CHARS = 1400