pi-shepherd 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.en.md +206 -83
  2. package/README.md +209 -88
  3. package/index.ts +262 -229
  4. package/package.json +60 -58
  5. package/rules.json +197 -218
  6. package/shepherd/compaction.ts +76 -0
  7. package/shepherd/conditions.ts +98 -0
  8. package/shepherd/ephemeral.ts +55 -52
  9. package/shepherd/git.ts +64 -0
  10. package/shepherd/index.ts +14 -0
  11. package/shepherd/line-count.ts +86 -86
  12. package/shepherd/message-end.ts +120 -0
  13. package/shepherd/rules-editor.ts +250 -135
  14. package/shepherd/rules-tool-helpers.ts +119 -0
  15. package/shepherd/rules-tool-list.ts +124 -0
  16. package/shepherd/rules-tool.ts +182 -99
  17. package/shepherd/rules-validate.ts +89 -44
  18. package/shepherd/rules.ts +366 -283
  19. package/shepherd/tool-event-types.ts +27 -14
  20. package/shepherd/tool-hooks.ts +165 -176
  21. package/shepherd/worktree-check.ts +130 -130
  22. package/tsconfig.json +21 -14
  23. package/node_modules/@pi-atelier/shared-utils/README.en.md +0 -182
  24. package/node_modules/@pi-atelier/shared-utils/README.md +0 -182
  25. package/node_modules/@pi-atelier/shared-utils/package.json +0 -51
  26. package/node_modules/@pi-atelier/shared-utils/src/__tests__/agents.test.ts +0 -120
  27. package/node_modules/@pi-atelier/shared-utils/src/__tests__/ephemeral.test.ts +0 -100
  28. package/node_modules/@pi-atelier/shared-utils/src/__tests__/file-lock.test.ts +0 -152
  29. package/node_modules/@pi-atelier/shared-utils/src/__tests__/filter-match.test.ts +0 -187
  30. package/node_modules/@pi-atelier/shared-utils/src/__tests__/memory-parser.test.ts +0 -170
  31. package/node_modules/@pi-atelier/shared-utils/src/__tests__/paths.test.ts +0 -126
  32. package/node_modules/@pi-atelier/shared-utils/src/__tests__/project-config-edge.test.ts +0 -138
  33. package/node_modules/@pi-atelier/shared-utils/src/__tests__/project-config.test.ts +0 -257
  34. package/node_modules/@pi-atelier/shared-utils/src/__tests__/project-tools-mcp.test.ts +0 -189
  35. package/node_modules/@pi-atelier/shared-utils/src/__tests__/project-tools.test.ts +0 -204
  36. package/node_modules/@pi-atelier/shared-utils/src/__tests__/settings-backup-advanced.test.ts +0 -269
  37. package/node_modules/@pi-atelier/shared-utils/src/__tests__/settings-backup-array.test.ts +0 -267
  38. package/node_modules/@pi-atelier/shared-utils/src/__tests__/settings-backup.test.ts +0 -520
  39. package/node_modules/@pi-atelier/shared-utils/src/__tests__/settings-read.test.ts +0 -116
  40. package/node_modules/@pi-atelier/shared-utils/src/__tests__/settings-write.test.ts +0 -119
  41. package/node_modules/@pi-atelier/shared-utils/src/__tests__/tool-output.test.ts +0 -145
  42. package/node_modules/@pi-atelier/shared-utils/src/agents.ts +0 -39
  43. package/node_modules/@pi-atelier/shared-utils/src/ephemeral.ts +0 -42
  44. package/node_modules/@pi-atelier/shared-utils/src/file-lock.ts +0 -62
  45. package/node_modules/@pi-atelier/shared-utils/src/filter-match.ts +0 -100
  46. package/node_modules/@pi-atelier/shared-utils/src/index.ts +0 -71
  47. package/node_modules/@pi-atelier/shared-utils/src/memory-parser.ts +0 -96
  48. package/node_modules/@pi-atelier/shared-utils/src/paths.ts +0 -23
  49. package/node_modules/@pi-atelier/shared-utils/src/project-config.ts +0 -241
  50. package/node_modules/@pi-atelier/shared-utils/src/project-tools.ts +0 -191
  51. package/node_modules/@pi-atelier/shared-utils/src/settings-array.ts +0 -73
  52. package/node_modules/@pi-atelier/shared-utils/src/settings-backup-rollback.ts +0 -104
  53. package/node_modules/@pi-atelier/shared-utils/src/settings-backup-utils.ts +0 -75
  54. package/node_modules/@pi-atelier/shared-utils/src/settings-backup.ts +0 -172
  55. package/node_modules/@pi-atelier/shared-utils/src/settings.ts +0 -104
  56. package/node_modules/@pi-atelier/shared-utils/src/tool-output.ts +0 -149
  57. package/node_modules/@pi-atelier/shared-utils/tsconfig.json +0 -9
  58. package/node_modules/@pi-atelier/shared-utils/vitest.config.ts +0 -24
package/rules.json CHANGED
@@ -20,7 +20,7 @@
20
20
  "flags": ""
21
21
  }
22
22
  ],
23
- "reason": "编辑了 Python 文件,必须:1) 运行 ruff check <file> 做格式检查;2) 跑覆盖该代码的单元测试(如无测试则先补充),修复所有测试问题确保通过。",
23
+ "reason": "编辑了 Python 文件,必须:1) 运行 ruff check <file> 做格式检查;2) 验证测试——迭代场景用 fast 档 `uv run python scripts/run_test_gate.py --fast`(按 diff 只跑受影响子项目,分钟级反馈);change 收尾/宣称「全量测试通过」SHALL 跑全量 `uv run python scripts/run_test_gate.py`(fast 凭证 .test-gate-fast-state.json 不属于全量绿证据)。",
24
24
  "enabled": true
25
25
  },
26
26
  {
@@ -34,13 +34,13 @@
34
34
  "pattern": "(>>|>|tee|sed\\s+-i|cp\\s|mv\\s).*settings\\.json|settings\\.json\\s*(>>|>)"
35
35
  }
36
36
  ],
37
- "reason": "直接编辑 settings.json 曾导致配置丢失(119行变3行)。必须使用 patchSettingsSectionWithBackup 或 settings_rollback。",
37
+ "reason": "直接编辑 settings.json 曾导致配置丢失(119行变3行)。必须使用 settings_patch 工具(自动备份+文件锁+校验),或 settings_rollback 回滚。",
38
38
  "enabled": true
39
39
  },
40
40
  {
41
- "comment": "[Rust] 编辑后必须格式检查 + 跑测试",
41
+ "comment": "[Rust] 编辑/写入后必须格式检查 + 跑测试",
42
42
  "hook": "tool_result",
43
- "tool": "edit",
43
+ "tool": "edit|write",
44
44
  "action": "notify",
45
45
  "conditions": [
46
46
  {
@@ -50,27 +50,12 @@
50
50
  }
51
51
  ],
52
52
  "reason": "编辑了 Rust 文件,必须:1) 运行 cargo clippy 做格式和 lint 检查;2) 跑覆盖该代码的单元测试(如无测试则先补充),修复所有测试问题确保通过。",
53
- "enabled": true
54
- },
55
- {
56
- "comment": "[Rust][write] 写入后必须格式检查 + 跑测试",
57
- "hook": "tool_result",
58
- "tool": "write",
59
- "action": "notify",
60
- "conditions": [
61
- {
62
- "field": "path",
63
- "pattern": "\\.rs$",
64
- "flags": ""
65
- }
66
- ],
67
- "reason": "写入了 Rust 文件,必须:1) 运行 cargo clippy 做格式和 lint 检查;2) 跑覆盖该代码的单元测试(如无测试则先补充),修复所有测试问题确保通过。",
68
- "enabled": true
53
+ "enabled": false
69
54
  },
70
55
  {
71
- "comment": "[TypeScript] 编辑后必须跑测试",
56
+ "comment": "[TypeScript] 编辑/写入后必须跑测试",
72
57
  "hook": "tool_result",
73
- "tool": "edit",
58
+ "tool": "edit|write",
74
59
  "action": "notify",
75
60
  "conditions": [
76
61
  {
@@ -83,41 +68,33 @@
83
68
  "enabled": true
84
69
  },
85
70
  {
86
- "comment": "[TypeScript][write] 写入后必须跑测试",
87
- "hook": "tool_result",
88
- "tool": "write",
71
+ "comment": "[arch] 会话结束前检查 git 状态(已停用:多会话并行时仓库级 git_dirty 误伤其他会话任务,收尾检查移入 opsx-archive 归档流程)",
72
+ "hook": "agent_end",
73
+ "check": "",
89
74
  "action": "notify",
75
+ "reason": "Git 检测到未提交或未跟踪的文件。收尾时请确认:\n1. git add + commit(必须带 session ID)\n2. 如有重要决策/发现 → 更新记忆文件",
76
+ "enabled": false,
77
+ "subagent": false,
90
78
  "conditions": [
91
79
  {
92
- "field": "path",
93
- "pattern": "\\.ts$",
94
- "flags": ""
80
+ "builtin": "git_dirty"
81
+ },
82
+ {
83
+ "builtin": "git_untracked"
95
84
  }
96
85
  ],
97
- "reason": "写入了 TypeScript 文件,必须跑覆盖该代码的单元测试(如无测试则先补充),修复所有测试问题确保通过。",
98
- "enabled": true
99
- },
100
- {
101
- "comment": "收尾规则 — agent_end 时提醒 commit + 记忆 + 总结,commit 必须带 session ID",
102
- "hook": "agent_end",
103
- "action": "notify",
104
- "check": "has_edits",
105
- "reason": "⚠️ shepherd: 检测到文件编辑,执行收尾工作:\n1️⃣ **Git commit**:如有未提交的 git 改动 → git add + git commit。**⚠️ commit message 末尾必须加 `session:${PI_SESSION_ID}`** — 这是硬性要求,不是建议。session ID 用于会话分析和 roadmap 追踪,缺少会导致无法追溯。格式示例:`feat: xxx session:019e71c9-b340-7fae-a330-98d837e72099`\n2️⃣ **记忆更新**:如有值得记住的知识 → memory_update\n3️⃣ **会话总结**:简要总结本次会话做了什么",
106
- "stopReason": [
107
- "stop"
108
- ],
109
- "enabled": true
86
+ "conditionLogic": "or"
110
87
  },
111
88
  {
112
- "comment": "[memory] git commit 后提醒更新记忆",
89
+ "comment": "[memory] git commit 后提醒更新记忆(已停用:记忆更新检查移入 opsx-archive 归档流程收口)",
113
90
  "hook": "tool_result",
114
91
  "tool": "bash",
115
92
  "pattern": "git\\s+commit",
116
93
  "action": "steer",
117
- "reason": "📝 刚执行了 git commit。检查本轮是否有值得跨会话保留的知识(架构决策、踩坑教训、结论),如果有 → 用 memory_update 工具写入。用法:①先调 memory_index 看已有文件,判断重复/合并/新建;②调 memory_update(fileName, content, scope) 一步写入文件+更新索引。fileName 格式:topic--kw1,kw2,kw3.md。scope:L1=跨项目通用,L2=项目级(默认)。单文件不超 200 行。如果没有新知识则忽略。",
94
+ "reason": "📝 刚执行了 git commit。请检查:**记忆更新** — 是否有值得跨会话保留的知识(架构决策、踩坑教训、结论),如果有 → 用 memory_update 写入。用法:①先调 memory_index 看已有文件;②调 memory_update(fileName, content, scope) 一步写入。fileName 格式:topic--kw1,kw2,kw3.md。scope:L1=跨项目通用,L2=项目级(默认)。单文件不超 200 行。如不需要则忽略。",
118
95
  "subagent": false,
119
96
  "requireSuccess": true,
120
- "enabled": true
97
+ "enabled": false
121
98
  },
122
99
  {
123
100
  "comment": "[arch] enforce-read-over-bash-cat:bash 中 cat|head/tail 读文件时强制用 read 工具",
@@ -142,65 +119,33 @@
142
119
  "subagent": false
143
120
  },
144
121
  {
145
- "comment": "[dev] 编辑扩展代码时提醒参考规范",
146
- "hook": "tool_result",
147
- "tool": "edit",
148
- "conditions": [
149
- {
150
- "field": "path",
151
- "pattern": "extensions/.*\\.ts$"
152
- }
153
- ],
154
- "action": "steer",
155
- "reason": "📝 编辑了扩展代码。如果尚未阅读扩展开发规范,请先 read 根目录或项目目录下 extensions/README.md;如果已阅读且有新知识,更新到该文件。已阅读且无新知识则无视。",
156
- "requireSuccess": true,
157
- "subagent": false,
158
- "enabled": true
159
- },
160
- {
161
- "comment": "[dev] 写入扩展代码时提醒参考规范",
122
+ "comment": "[dev] 编辑/写入扩展代码时提醒参考规范",
162
123
  "hook": "tool_result",
163
- "tool": "write",
164
- "conditions": [
165
- {
166
- "field": "path",
167
- "pattern": "extensions/.*\\.ts$"
168
- }
169
- ],
170
- "action": "steer",
171
- "reason": "📝 写入了扩展代码。如果尚未阅读扩展开发规范,请先 read 根目录或项目目录下 extensions/README.md;如果已阅读且有新知识,更新到该文件。已阅读且无新知识则无视。",
172
- "requireSuccess": true,
173
- "subagent": false,
174
- "enabled": true
175
- },
176
- {
177
- "comment": "[dev] 编辑扩展代码后提醒跑 typecheck",
178
- "hook": "tool_result",
179
- "tool": "edit",
124
+ "tool": "edit|write",
180
125
  "conditions": [
181
126
  {
182
127
  "field": "path",
183
- "pattern": "extensions/.*\\.ts$"
128
+ "pattern": "extensions[/\\\\].*\\.ts$"
184
129
  }
185
130
  ],
186
131
  "action": "steer",
187
- "reason": "📝 编辑了扩展 TypeScript 代码。请运行 `cd extensions && bash check.sh <扩展名> --typecheck` 确认无类型错误。如果已跑过则无视。",
132
+ "reason": "📝 编辑/写入了扩展代码。如果尚未阅读扩展开发规范,请先 read 根目录或项目目录下 extensions/README.md;如果已阅读且有新知识,更新到该文件。已阅读且无新知识则无视。",
188
133
  "requireSuccess": true,
189
134
  "subagent": false,
190
135
  "enabled": true
191
136
  },
192
137
  {
193
- "comment": "[dev] 写入扩展代码后提醒跑 typecheck",
138
+ "comment": "[dev] 编辑/写入扩展代码后提醒跑 typecheck",
194
139
  "hook": "tool_result",
195
- "tool": "write",
140
+ "tool": "edit|write",
196
141
  "conditions": [
197
142
  {
198
143
  "field": "path",
199
- "pattern": "extensions/.*\\.ts$"
144
+ "pattern": "extensions[/\\\\].*\\.ts$"
200
145
  }
201
146
  ],
202
147
  "action": "steer",
203
- "reason": "📝 写入了扩展 TypeScript 代码。请运行 `cd extensions && bash check.sh <扩展名> --typecheck` 确认无类型错误。如果已跑过则无视。",
148
+ "reason": "📝 编辑/写入了扩展 TypeScript 代码。请运行 `cd extensions && bash check.sh <扩展名> --typecheck` 确认无类型错误。如果已跑过则无视。",
204
149
  "requireSuccess": true,
205
150
  "subagent": false,
206
151
  "enabled": true
@@ -208,25 +153,7 @@
208
153
  {
209
154
  "comment": "[TS] 禁止空格缩进 — TS 文件必须用 Tab",
210
155
  "hook": "tool_call",
211
- "tool": "edit",
212
- "action": "notify",
213
- "conditions": [
214
- {
215
- "field": "path",
216
- "pattern": "\\.ts$"
217
- },
218
- {
219
- "field": "text",
220
- "pattern": "\\n [\\S ]"
221
- }
222
- ],
223
- "reason": "❌ TS 文件要求 Tab 缩进,不是空格。请用 Tab 缩进重写代码。",
224
- "enabled": true
225
- },
226
- {
227
- "comment": "[TS][write] 禁止空格缩进 — TS 文件必须用 Tab",
228
- "hook": "tool_call",
229
- "tool": "write",
156
+ "tool": "edit|write",
230
157
  "action": "notify",
231
158
  "conditions": [
232
159
  {
@@ -244,25 +171,7 @@
244
171
  {
245
172
  "comment": "[Python] 禁止 Tab 缩进 — Python 必须用 4 空格",
246
173
  "hook": "tool_call",
247
- "tool": "edit",
248
- "action": "notify",
249
- "conditions": [
250
- {
251
- "field": "path",
252
- "pattern": "\\.py$"
253
- },
254
- {
255
- "field": "text",
256
- "pattern": "\\n\\t"
257
- }
258
- ],
259
- "reason": "❌ Python 文件禁止 Tab 缩进,必须用 4 空格(PEP 8)。",
260
- "enabled": true
261
- },
262
- {
263
- "comment": "[Python][write] 禁止 Tab 缩进 — Python 必须用 4 空格",
264
- "hook": "tool_call",
265
- "tool": "write",
174
+ "tool": "edit|write",
266
175
  "action": "notify",
267
176
  "conditions": [
268
177
  {
@@ -280,25 +189,7 @@
280
189
  {
281
190
  "comment": "[Rust] 禁止 Tab 缩进 — Rust 必须用 4 空格",
282
191
  "hook": "tool_call",
283
- "tool": "edit",
284
- "action": "notify",
285
- "conditions": [
286
- {
287
- "field": "path",
288
- "pattern": "\\.rs$"
289
- },
290
- {
291
- "field": "text",
292
- "pattern": "\\n\\t"
293
- }
294
- ],
295
- "reason": "❌ Rust 文件禁止 Tab 缩进,必须用 4 空格(rustfmt)。",
296
- "enabled": true
297
- },
298
- {
299
- "comment": "[Rust][write] 禁止 Tab 缩进 — Rust 必须用 4 空格",
300
- "hook": "tool_call",
301
- "tool": "write",
192
+ "tool": "edit|write",
302
193
  "action": "notify",
303
194
  "conditions": [
304
195
  {
@@ -320,7 +211,7 @@
320
211
  "action": "notify",
321
212
  "pattern": ".",
322
213
  "flags": "s",
323
- "reason": "推荐用 code-graph 替代 grep 搜代码——code-graph 理解 AST 语义,能按符号名、调用链、引用关系精准定位,比 grep 逐行匹配快得多且不漏不误报。选工具方法:①模糊搜索 → semantic_code_search(默认首选);②精确符号名 → get_ast_node;③引用追踪 → find_references;④调用链 → get_call_graph;⑤模块结构 → module_overview。grep 仅适合搜字面量字符串(TODO、配置值、错误消息)。如果当前搜索的就是字面量字符串则忽略此提醒。如果目标目录没有被 code-graph 索引(搜索返回空结果),调用 setup_codegraph({ directory: \"目标目录路径\" }) 一键建索引。",
214
+ "reason": "推荐用 code-graph 替代 grep 搜代码——理解 AST 语义,按符号名、调用链、引用关系精准定位。选工具:①模糊搜索 → semantic_code_search;②精确符号 → get_ast_node;③引用 → find_references;④调用链 → get_call_graph;⑤模块结构 → module_overview。grep 仅适合字面量(TODO、配置值),搜字面量则忽略此提醒。\n\n**涉及字段血缘/数据链路类任务?** → 使用 field-lineage 技能。\n\n**code-graph 故障排查:**\n- **搜不到东西?** → 可能是目标目录没索引。用 `code_graph_project_map()` 查看当前索引范围,不在范围内就调 `setup_codegraph({ directory: \"目标目录绝对路径\" })` 一键建索引(创建软链接+增量索引,即时可搜)。\n- **路径报错 `must be relative`?** → path 参数必须用相对路径(相对于 CWD),不能用绝对路径。\n- **刚改了代码搜不到?** → 索引可能过期,调 `setup_codegraph({ directory: \"项目路径\" })` 重建索引(已索引目录重复调用有效,增量更新)。\n- **确实没有结果** → 忽略以上提醒,可能是符号确实不存在。",
324
215
  "requiresTools": [
325
216
  "code_graph_semantic_code_search"
326
217
  ],
@@ -333,7 +224,7 @@
333
224
  "action": "notify",
334
225
  "pattern": "\\b(grep|rg)\\b.*\\.(py|rs|ts|js|toml)(?=[\\s'\"|)]|$)",
335
226
  "flags": "",
336
- "reason": "💡 bash grep 搜代码文件不如用 pi 内置 grep 工具或 code-graph MCP 工具——code-graph 理解 AST 语义,能精准匹配符号名和调用关系。选工具:①模糊搜索 → semantic_code_search;②精确符号 → get_ast_node;③引用追踪 → find_references;④调用链 → get_call_graph。如果搜的是字面量字符串(TODO、配置值)则忽略。如果目标目录没有被 code-graph 索引,调用 setup_codegraph({ directory: \"目标目录路径\" }) 一键建索引。",
227
+ "reason": "💡 bash grep 搜代码文件不如用 pi 内置 grep 工具或 code-graph MCP 工具——code-graph 理解 AST 语义,能精准匹配符号名和调用关系。选工具:①模糊搜索 → semantic_code_search;②精确符号 → get_ast_node;③引用追踪 → find_references;④调用链 → get_call_graph。如果搜的是字面量字符串(TODO、配置值)则忽略。\n\n**code-graph 故障排查:**\n- **搜不到东西?** → 可能是目标目录没索引。用 `code_graph_project_map()` 查看当前索引范围,不在范围内就调 `setup_codegraph({ directory: \"目标目录绝对路径\" })` 一键建索引。\n- **路径报错?** → path 参数必须用相对路径(相对于 CWD),不能用绝对路径。\n- **刚改了代码搜不到?** → 调 `setup_codegraph({ directory: \"项目路径\" })` 重建索引。",
337
228
  "enabled": true
338
229
  },
339
230
  {
@@ -347,13 +238,13 @@
347
238
  "pattern": "\\.(ts|js|py|rs)$"
348
239
  }
349
240
  ],
350
- "reason": "💡 搜代码文件推荐用 code-graph。如果 code-graph 返回空结果,可能是目标目录没有被索引。调用 setup_codegraph({ directory: \"目标目录路径\" }) 一键建索引(软链接+增量索引,当前会话即刻可搜)。用法参考 code-graph 技能:semantic_code_search(模糊搜索)、get_ast_node(精确符号)、get_call_graph(调用链)、module_overview(模块结构)。",
241
+ "reason": "💡 搜代码文件推荐用 code-graph。如果 code-graph 搜不到,排查步骤:\n1. `code_graph_project_map()` 查看当前索引了哪些目录\n2. 不在范围内 → `setup_codegraph({ directory: \"目标目录绝对路径\" })` 建索引\n3. 路径报错 → 用相对路径,不用绝对路径\n4. 刚改过代码 → `setup_codegraph({ directory: \"项目路径\" })` 重建索引\n用法:semantic_code_search(模糊搜索)、get_ast_node(精确符号)、get_call_graph(调用链)、module_overview(模块结构)。",
351
242
  "enabled": true
352
243
  },
353
244
  {
354
- "comment": "[deps] 编辑 package.json 时提醒依赖合规检查",
245
+ "comment": "[deps] 编辑/写入 package.json 时提醒依赖合规检查",
355
246
  "hook": "tool_result",
356
- "tool": "edit",
247
+ "tool": "edit|write",
357
248
  "action": "notify",
358
249
  "conditions": [
359
250
  {
@@ -364,20 +255,6 @@
364
255
  "reason": "📦 编辑了 package.json 依赖字段。请对照 DEPENDENCY-GUIDE.md 检查合规性:① pi SDK 必须放 peerDependencies + optionalDependencies,禁止放 dependencies/devDependencies;② shared-utils 运行时引用放 dependencies(开发放 devDependencies);③ 检查 files 白名单是否存在;④ 添加新依赖后运行 scan-deps.sh 验证无嵌套/版本冲突。",
365
256
  "enabled": true
366
257
  },
367
- {
368
- "comment": "[deps][write] 写入 package.json 时提醒依赖合规检查",
369
- "hook": "tool_result",
370
- "tool": "write",
371
- "action": "notify",
372
- "conditions": [
373
- {
374
- "field": "path",
375
- "pattern": "package\\.json$"
376
- }
377
- ],
378
- "reason": "📦 写入了 package.json 依赖字段。请对照 DEPENDENCY-GUIDE.md 检查合规性:① pi SDK 必须放 peerDependencies + optionalDependencies,禁止放 dependencies/devDependencies;② shared-utils 运行时引用放 dependencies(开发放 devDependencies);③ 检查 files 白名单是否存在;④ 添加新依赖后运行 scan-deps.sh 验证无嵌套/版本冲突。",
379
- "enabled": true
380
- },
381
258
  {
382
259
  "comment": "[通用] 脚本执行失败时提醒先 --help",
383
260
  "hook": "tool_result",
@@ -394,9 +271,9 @@
394
271
  "enabled": true
395
272
  },
396
273
  {
397
- "comment": "[roadmap] 禁止直接编辑 roadmap JSON — 必须用 roadmap 工具",
274
+ "comment": "[roadmap] 禁止直接编辑/写入 roadmap JSON — 必须用 roadmap 工具",
398
275
  "hook": "tool_call",
399
- "tool": "edit",
276
+ "tool": "edit|write",
400
277
  "action": "block",
401
278
  "conditions": [
402
279
  {
@@ -405,112 +282,214 @@
405
282
  }
406
283
  ],
407
284
  "reason": "⛔ 禁止直接编辑 roadmap JSON!请用 roadmap 工具操作(自动处理时间戳/ID分配/状态级联/归档/doing同步):\n查看:roadmap_list(列表)、roadmap_show(详情)、roadmap_next(待办)\n创建:roadmap_plan(完整JSON创建/更新)、roadmap_create + roadmap_add_epic/story/task(逐步构建)\n修改:roadmap_update(roadmapId, item_id, {status/title/description/priority}) — 更新单个项\n完成:roadmap_done(roadmapId, taskId) — 标记完成并级联更新\n归档:roadmap_archive(roadmapId) — 归档已完成项\n直接改 JSON 会绕过这些机制导致数据不一致。",
408
- "enabled": true
285
+ "enabled": false
409
286
  },
410
287
  {
411
- "comment": "[roadmap] 禁止直接写入 roadmap JSON — 必须用 roadmap 工具",
288
+ "comment": "[roadmap] 禁止通过 bash/python 脚本直接读写 roadmap JSON — 必须用 roadmap 工具",
412
289
  "hook": "tool_call",
413
- "tool": "write",
290
+ "tool": "bash",
414
291
  "action": "block",
415
292
  "conditions": [
416
293
  {
417
- "field": "path",
418
- "pattern": "\\.roadmap\\.json$|\\broadmap/roadmap\\.json$"
294
+ "field": "command",
295
+ "pattern": "((roadmap\\.json|\\.roadmap\\.json)[\\s\\S]*(open\\(|json\\.load|json\\.dump|write|sed|awk|jq|cat))|((open\\(|json\\.load|json\\.dump|write|sed|awk|jq|cat)[\\s\\S]*(roadmap\\.json|\\.roadmap\\.json))"
419
296
  }
420
297
  ],
421
- "reason": "⛔ 禁止直接写入 roadmap JSON!请用 roadmap 工具操作(自动处理时间戳/ID分配/状态级联/归档/doing同步):\n查看:roadmap_list(列表)、roadmap_show(详情)、roadmap_next(待办)\n创建:roadmap_plan(完整JSON创建/更新)、roadmap_create + roadmap_add_epic/story/task(逐步构建)\n修改:roadmap_update(roadmapId, item_id, {status/title/description/priority}) — 更新单个项\n完成:roadmap_done(roadmapId, taskId) — 标记完成并级联更新\n归档:roadmap_archive(roadmapId) — 归档已完成项\n直接改 JSON 会绕过这些机制导致数据不一致。",
422
- "enabled": true
298
+ "reason": "⛔ 禁止通过 bash/python 脚本直接读写 roadmap JSON!请用 roadmap 工具操作(自动处理时间戳/ID分配/状态级联/归档/doing同步):\n查看:roadmap_list(列表)、roadmap_show(详情)、roadmap_next(待办)\n创建:roadmap_plan(完整JSON创建/更新)、roadmap_create + roadmap_add_epic/story/task(逐步构建)\n修改:roadmap_update(roadmapId, item_id, {status/title/description/priority}) — 更新单个项\n完成:roadmap_done(roadmapId, taskId) — 标记完成并级联更新\n归档:roadmap_archive(roadmapId) — 归档已完成项\n直接碰底层 JSON 会绕过这些机制导致数据不一致。",
299
+ "enabled": false
423
300
  },
424
301
  {
425
- "comment": "[roadmap] 禁止通过 bash/python 脚本直接读写 roadmap JSON — 必须用 roadmap 工具",
426
- "hook": "tool_call",
427
- "tool": "bash",
428
302
  "action": "block",
303
+ "comment": "[settings] 禁止 bash 直接修改 settings.json — 必须用 patchSettingsSectionWithBackup 或 settings_rollback",
429
304
  "conditions": [
430
305
  {
431
306
  "field": "command",
432
- "pattern": "((roadmap\\.json|\\.roadmap\\.json)[\\s\\S]*(open\\(|json\\.load|json\\.dump|write|sed|awk|jq|cat))|((open\\(|json\\.load|json\\.dump|write|sed|awk|jq|cat)[\\s\\S]*(roadmap\\.json|\\.roadmap\\.json))"
307
+ "pattern": "^(?!.*\\b(git|grep|find|ls|cat|head|tail|diff|test|python3.*json\\.load)\\b).*settings\\.json",
308
+ "flags": "s"
433
309
  }
434
310
  ],
435
- "reason": "⛔ 禁止通过 bash/python 脚本直接读写 roadmap JSON!请用 roadmap 工具操作(自动处理时间戳/ID分配/状态级联/归档/doing同步):\n查看:roadmap_list(列表)、roadmap_show(详情)、roadmap_next(待办)\n创建:roadmap_plan(完整JSON创建/更新)、roadmap_create + roadmap_add_epic/story/task(逐步构建)\n修改:roadmap_update(roadmapId, item_id, {status/title/description/priority}) — 更新单个项\n完成:roadmap_done(roadmapId, taskId) — 标记完成并级联更新\n归档:roadmap_archive(roadmapId) — 归档已完成项\n直接碰底层 JSON 会绕过这些机制导致数据不一致。",
436
- "enabled": true
311
+ "hook": "tool_call",
312
+ "message": "⛔ 禁止通过 bash 修改 settings.json!使用 settings_patch 工具安全修改(支持全局 scope='global' 和项目级 scope='project'),或 settings_rollback 回滚。",
313
+ "reason": "禁止通过 bash 直接写入 settings.json(如 echo > / sed -i / python 写入),但允许 git 操作(status/diff/reset/checkout/restore/add/rm/log)和只读命令(grep/find/ls/cat/head/tail/diff/test/python3 解析读取)。必须使用 settings_patch 工具。",
314
+ "tool": "bash",
315
+ "enabled": false
437
316
  },
438
317
  {
439
- "comment": "[GDScript] 编辑 .gd 文件后提醒写/跑 GUT 测试",
440
- "reason": "跟 Python/Rust/TS 规则一致——编辑代码后提醒补充单元测试。触发条件:edit 工具的 file 参数匹配 .gd 扩展名。提醒内容:是否有对应的 GUT 测试?如果没有,建议补写。如果有,提醒跑 run_unit_tests.sh 验证。",
441
- "trigger": {
442
- "tool": "edit",
443
- "event": "tool_result",
444
- "match": "\\.gd$"
445
- },
446
- "action": "notify"
318
+ "comment": "[git] commit 成功后提醒 push",
319
+ "reason": "项目已有远程仓库 origin,commit 后应同步推送避免本地积压",
320
+ "trigger": "bash",
321
+ "event": "tool_result",
322
+ "condition": "bash command contains 'git commit' and exit code is 0",
323
+ "action": "notify",
324
+ "message": "commit 成功。检查是否有远程仓库(git remote),有的话立即 git push 同步到远程。"
325
+ },
326
+ {
327
+ "comment": "[arch] 编辑 YAML 规则后提醒同步安全网",
328
+ "hook": "tool_result",
329
+ "tool": "edit|write",
330
+ "action": "steer",
331
+ "conditions": [
332
+ {
333
+ "field": "path",
334
+ "pattern": "docs[/\\\\]agent[/\\\\]api[/\\\\].*\\.yaml$"
335
+ }
336
+ ],
337
+ "reason": "📝 架构 artifacts YAML 已变更!请执行以下闭环步骤:\n1. python scripts/govern_sync.py --preview(预览安全网变化)\n2. python scripts/govern_sync.py --apply(应用更新)\n3. python scripts/govern_check.py(验证闭环完整性)",
338
+ "enabled": false,
339
+ "subagent": false
447
340
  },
448
341
  {
342
+ "comment": "[quality] 大量代码编辑后提醒运行治理检查",
343
+ "hook": "tool_result",
344
+ "tool": "edit|write",
449
345
  "action": "notify",
450
- "comment": "[GDScript][write] 写入 .gd 文件后提醒写/跑 GUT 测试",
451
- "reason": "跟 edit 规则配对。新建 .gd 文件时提醒创建对应的测试文件。",
452
- "trigger": {
453
- "event": "tool_result",
454
- "match": "\\.gd$",
455
- "tool": "write"
456
- }
346
+ "conditions": [
347
+ {
348
+ "field": "path",
349
+ "pattern": "src[/\\\\].*\\.rs$"
350
+ }
351
+ ],
352
+ "reason": "📝 编辑了源码。请确认:\n1. cargo clippy --all-targets -- -D warnings\n2. cargo test\n3. bash scripts/check_arch.sh(架构合规)\n4. python scripts/govern_check.py(治理健康度)",
353
+ "enabled": false,
354
+ "subagent": false
457
355
  },
458
356
  {
459
- "comment": "[settings] 禁止 edit settings.json — 必须用 patchSettingsSectionWithBackup 或 settings_rollback",
460
- "reason": "直接编辑 settings.json 曾导致配置丢失(119 行变 3 行)。必须使用 patchSettingsSectionWithBackup 或 settings_rollback。",
461
- "tool": "edit",
462
- "hook": "tool_call",
463
- "action": "block",
464
- "message": "⛔ 禁止直接 edit settings.json!使用 patchSettingsSectionWithBackup() 安全修改,或 settings_rollback 回滚。",
357
+ "comment": "[roadmap] 读计划文件时提醒标记 task 为 doing",
358
+ "hook": "tool_result",
359
+ "tool": "read",
360
+ "action": "steer",
361
+ "conditions": [
362
+ {
363
+ "field": "path",
364
+ "pattern": "\\.pi[/\\\\]plans[/\\\\].*\\.md$"
365
+ }
366
+ ],
367
+ "reason": "📖 你正在读取计划文件,说明即将开始执行某个 Task。请在开始编码之前先用 roadmap_update(roadmapId, taskId, {status: 'doing'}) 将对应 Task 标记为 🔄 doing,这样其他会话能看到你正在做什么,避免冲突。如果已经标记过则无视。",
368
+ "subagent": false,
369
+ "requireSuccess": true,
370
+ "enabled": false
371
+ },
372
+ {
373
+ "comment": "[code-graph] 编辑代码文件后提醒更新索引",
374
+ "hook": "tool_result",
375
+ "tool": "edit|write",
376
+ "action": "steer",
465
377
  "conditions": [
466
378
  {
467
379
  "field": "path",
468
- "pattern": "settings\\.json$|settings\\.json\\.bak"
380
+ "pattern": "\\.(ts|js|py|rs)$"
469
381
  }
470
- ]
382
+ ],
383
+ "reason": "📝 编辑了代码文件,code-graph 索引可能已过期(新增/重命名/删除的符号不会被搜到)。如果接下来需要用 code-graph 搜索,先调 `setup_codegraph({ directory: \"项目路径\" })` 重建索引。如果只是编辑不再搜索,则忽略。",
384
+ "subagent": false,
385
+ "requireSuccess": true,
386
+ "enabled": true
471
387
  },
472
388
  {
389
+ "comment": "[settings] 禁止 edit/write settings.json — 必须用 patchSettingsSectionWithBackup",
473
390
  "action": "block",
474
- "comment": "[settings] 禁止 write settings.json — 必须用 patchSettingsSectionWithBackup 或 settings_rollback",
391
+ "hook": "tool_call",
392
+ "tool": "edit|write",
475
393
  "conditions": [
476
394
  {
477
395
  "field": "path",
478
- "pattern": "settings\\.json$|settings\\.json\\.bak"
396
+ "pattern": "settings\\.json$",
397
+ "flags": "i"
479
398
  }
480
399
  ],
400
+ "message": "⛔ shepherd: 直接编辑 settings.json 曾导致配置丢失(119行变3行)。必须使用 settings_patch 工具(自动备份+文件锁+校验),或 settings_rollback 回滚。",
401
+ "reason": "直接 edit/write settings.json 曾导致配置丢失(119行变3行)。必须使用 settings_patch 工具。",
402
+ "enabled": false
403
+ },
404
+ {
405
+ "comment": "归因猜测提醒:AI 回复含可能时提醒验证",
406
+ "hook": "message_end",
407
+ "conditions": [
408
+ {
409
+ "field": "text",
410
+ "pattern": "可能"
411
+ }
412
+ ],
413
+ "action": "notify",
414
+ "reason": "💡 如果你正在做归因猜测,请先找到确切证据验证(看源码、加 println!、断点调试),不要凭猜测下结论。如果你不是在做归因猜测,请无视此提示。如果你正在等待用户的决策或反馈,当前提示不代表用户同意或确认,请继续询问用户,不要自作主张继续执行。",
415
+ "enabled": false
416
+ },
417
+ {
418
+ "action": "block",
419
+ "check": null,
420
+ "comment": "禁止手动复制 pyd/dll 文件到 .venv",
421
+ "pattern": "cp.*\\.pyd|cp.*quant_py\\.dll|copy.*\\.pyd",
422
+ "reason": "Rust 编译产物必须通过 maturin develop 安装,禁止手动 cp。正确方式:rm -rf .venv/Lib/site-packages/quant_py/ && uv run maturin develop --release"
423
+ },
424
+ {
425
+ "action": "block",
426
+ "check": null,
427
+ "comment": "禁止在子项目目录创建 .venv",
428
+ "pattern": "quant-strategy/\\.venv|quant-fund/\\.venv|quant-base/\\.venv",
429
+ "reason": "AGENTS.md 规定只有根目录 .venv/ 是唯一的虚拟环境。子目录 .venv 会导致 maturin 装错位置。如果发现子目录 .venv 立即删除。"
430
+ },
431
+ {
432
+ "action": "steer",
433
+ "check": null,
434
+ "comment": "Rust 编译后必须验证 pyd 更新",
435
+ "pattern": "maturin develop",
436
+ "reason": "maturin develop 后必须验证 .pyd 时间戳+字节:ls -la --time-style=long-iso .venv/Lib/site-packages/quant_py/*.pyd(编译后查一次,跑完 uv run 再查一次)。pyd 过期有三种根因症状相同:① 目录错(必须根目录+uv run maturin develop);② uv run 按 uv.lock 同步把新 pyd 覆盖回旧产物(字节 3066880→2920960 实证,须 --no-sync);③ 改完 Rust 忘重编译。时间戳没更新 → rm -rf .venv/Lib/site-packages/quant_py/ 再重跑 maturin develop(禁止手动 cp pyd)。详见记忆 rust-comprehensive。"
437
+ },
438
+ {
439
+ "comment": "[shepherd] 禁止直接 edit/write rules.json — 必须用 shepherd_rules 工具",
481
440
  "hook": "tool_call",
482
- "message": "⛔ 禁止直接 write settings.json!使用 patchSettingsSectionWithBackup() 安全修改,或 settings_rollback 回滚。",
483
- "reason": "直接编辑 settings.json 曾导致配置丢失(119 行变 3 行)。必须使用 patchSettingsSectionWithBackup 或 settings_rollback。",
484
- "tool": "write"
441
+ "tool": "edit|write",
442
+ "action": "block",
443
+ "conditions": [
444
+ {
445
+ "field": "path",
446
+ "pattern": "shepherd/rules\\.json$"
447
+ }
448
+ ],
449
+ "reason": "⛔ 禁止直接编辑 rules.json!请用 shepherd_rules 工具操作(自动备份+校验+回滚):\nlist: shepherd_rules(action='list') — 列出所有规则\nadd: shepherd_rules(action='add', rule={...}) — 添加规则\nupdate: shepherd_rules(action='update', index=N, changes={...}) — 更新规则\ndelete: shepherd_rules(action='delete', index=N) — 删除规则\n直接改 JSON 会绕过安全机制(备份/校验/去重检测/签名覆盖)。",
450
+ "enabled": true
485
451
  },
486
452
  {
487
453
  "action": "block",
488
- "comment": "[settings] 禁止 bash 直接修改 settings.json — 必须用 patchSettingsSectionWithBackup 或 settings_rollback",
454
+ "comment": "[shepherd] 禁止通过 bash/python 脚本直接读写 rules.json — 必须用 shepherd_rules 工具",
489
455
  "conditions": [
490
456
  {
491
457
  "field": "command",
492
- "pattern": "settings\\.json"
458
+ "pattern": "(rules\\.json[\\s\\S]*(open\\(|json\\.load|json\\.dump|write|sed|awk|jq|cat))|((open\\(|json\\.load|json\\.dump|write|sed|awk|jq|cat)[\\s\\S]*rules\\.json)"
493
459
  }
494
460
  ],
461
+ "enabled": true,
495
462
  "hook": "tool_call",
496
- "message": "⛔ 禁止通过 bash 修改 settings.json!使用 patchSettingsSectionWithBackup() 安全修改,或 settings_rollback 回滚。",
497
- "reason": "禁止 bash 直接修改 settings.json — 必须用 patchSettingsSectionWithBackup 或 settings_rollback",
463
+ "reason": "⛔ 禁止通过脚本绕过直接读写 rules.json!请用 shepherd_rules 工具操作(自动备份+校验+回滚+去重检测)。",
498
464
  "tool": "bash"
499
465
  },
500
466
  {
501
- "comment": "[git] commit 成功后提醒 push",
502
- "reason": "项目已有远程仓库 origin,commit 后应同步推送避免本地积压",
503
- "trigger": "bash",
504
- "event": "tool_result",
505
- "condition": "bash command contains 'git commit' and exit code is 0",
467
+ "comment": "[code-graph] module_overview 返回空结果时提醒检查路径前缀",
468
+ "hook": "tool_result",
469
+ "tool": "code_graph_module_overview",
470
+ "conditions": [
471
+ {
472
+ "field": "result",
473
+ "pattern": "0 active \\+ 0 inactive"
474
+ }
475
+ ],
506
476
  "action": "notify",
507
- "message": "commit 成功。检查是否有远程仓库(git remote),有的话立即 git push 同步到远程。"
477
+ "message": "⚠️ code-graph 返回空结果。如果是通过 setup_codegraph 索引的外部目录,请用路径 _codegraph_links/<name> 而不是裸名。例如: code_graph_module_overview(path='_codegraph_links/pi-intercom')",
478
+ "reason": "AI 常误用裸名调用 module_overview(如 path='pi-intercom'),但软链接目录在索引中的路径前缀是 _codegraph_links/"
508
479
  },
509
480
  {
510
- "comment": "UI 布局改动提醒",
511
- "reason": "编辑 GameScene.gd 或 UI 相关代码时,先查 debug_summary().ui_layout 确认位置,改完用 debug_toggle_borders() 验证无重叠",
512
- "pattern": "GameScene\\.gd",
513
- "type": "notify",
514
- "action": "notify"
481
+ "action": "steer",
482
+ "check": null,
483
+ "comment": "纯查询优先走 ro_query 助手(锁感知只读)",
484
+ "flags": "",
485
+ "pattern": "DataStore\\s*\\(\\s*\\)",
486
+ "reason": "检测到内联 DataStore() 默认(RW)构造做纯查询——历史撞锁事故高发模式(会话 a89c 连续撞锁 2 次)。纯查询请改用锁感知助手:uv run python quant-base/scripts/ro_query.py --sql \"...\"(RO+限时重试+持锁 PID 诊断+查完即关,支持 --db 指定 live.duckdb)。若确需 RW 写操作或 DataStore.reader() 网络服务场景,继续即可。"
487
+ },
488
+ {
489
+ "action": "steer",
490
+ "comment": "compact-skill-reminder",
491
+ "enabled": true,
492
+ "hook": "session_compact",
493
+ "reason": "上下文刚被压缩:此前 read 过的 SKILL.md 技能正文已随历史摘要化挥发。system prompt 里的技能索引(名字/描述/路径)仍在——若当前任务涉及某技能,请重新 read 其 SKILL.md 获取操作指引。"
515
494
  }
516
495
  ]