#!/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:]))