dsh-gate-agent 0.0.0-stage → 0.1.1

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 (65) hide show
  1. package/README.md +284 -2
  2. package/cordis.patch.yml +144 -0
  3. package/icon.svg +4 -0
  4. package/launchers/verify-notes.mjs +20 -0
  5. package/launchers/verify-notes.yml +34 -0
  6. package/lib/index.js +2202 -0
  7. package/locale/en.json +6 -0
  8. package/locale/zh.json +6 -0
  9. package/package.json +32 -4
  10. package/skills/gate-code-review/SKILL.md +115 -0
  11. package/skills/gate-code-review/assets/worker-briefs/gate-implement-review.md +19 -0
  12. package/skills/gate-diagnosing-bugs/SKILL.md +143 -0
  13. package/skills/gate-diagnosing-bugs/scripts/hitl-loop.template.sh +44 -0
  14. package/skills/gate-domain-modeling/CONTEXT-FORMAT.md +64 -0
  15. package/skills/gate-domain-modeling/GLOSSARY-FORMAT.md +64 -0
  16. package/skills/gate-domain-modeling/SKILL.md +47 -0
  17. package/skills/gate-fix-bug/SKILL.md +99 -0
  18. package/skills/gate-fix-bug/assets/worker-briefs/fix-bug-review.md +23 -0
  19. package/skills/gate-fix-bug/assets/worker-briefs/gate-fix-bug.md +33 -0
  20. package/skills/gate-handoff/SKILL.md +20 -0
  21. package/skills/gate-implement/SKILL.md +82 -0
  22. package/skills/gate-implement/assets/worker-briefs/gate-enter-implement.md +24 -0
  23. package/skills/gate-implement/tickets.md +114 -0
  24. package/skills/gate-plan-review/SKILL.md +87 -0
  25. package/skills/gate-plan-review/assets/worker-briefs/plan-review.md +19 -0
  26. package/skills/gate-planning/SKILL.md +96 -0
  27. package/skills/gate-planning/assets/worker-briefs/gate-enter-plan.md +24 -0
  28. package/skills/gate-planning/proposal.md +51 -0
  29. package/skills/gate-planning/rfc.md +73 -0
  30. package/skills/gate-playwright-e2e/SKILL.md +178 -0
  31. package/skills/gate-playwright-e2e/reference/config.md +129 -0
  32. package/skills/gate-playwright-e2e/reference/patterns.md +83 -0
  33. package/skills/gate-prd/SKILL.md +42 -0
  34. package/skills/gate-research/SKILL.md +17 -0
  35. package/skills/gate-tdd/SKILL.md +39 -0
  36. package/skills/gate-tdd/mocking.md +61 -0
  37. package/skills/gate-tdd/tests.md +79 -0
  38. package/skills/gate-user-verify/DIAGNOSE.md +75 -0
  39. package/skills/gate-user-verify/SKILL.md +185 -0
  40. package/skills/gate-user-verify/assets/worker-briefs/gate-code-verify.md +32 -0
  41. package/skills/gate-verify/SKILL.md +73 -0
  42. package/skills/gate-verify/assets/worker-briefs/gate-verify.md +21 -0
  43. package/skills/gate-write-agent-notes/SKILL.md +199 -0
  44. package/skills/gate-write-agent-notes/assets/agent-notes-board.html +1779 -0
  45. package/skills/gate-write-agent-notes/references/archiving.md +113 -0
  46. package/skills/gate-write-agent-notes/references/classification.md +34 -0
  47. package/skills/gate-write-agent-notes/references/note-format.md +72 -0
  48. package/skills/gate-write-agent-notes/references/prose-checklist.md +52 -0
  49. package/skills/gate-write-agent-notes/references/quality-gate.md +60 -0
  50. package/skills/gate-write-agent-notes/references/simplification-checklist.md +27 -0
  51. package/skills/gate-write-agent-notes/references/verification.md +47 -0
  52. package/skills/gate-write-agent-notes/references/when-to-write.md +86 -0
  53. package/skills/gate-write-agent-notes/scripts/agent-note-tree.ts +85 -0
  54. package/skills/gate-write-agent-notes/scripts/archive-agent-note.ts +218 -0
  55. package/skills/gate-write-agent-notes/scripts/build-board.ts +240 -0
  56. package/skills/gate-write-agent-notes/scripts/check-note-anchors.ts +105 -0
  57. package/skills/gate-write-agent-notes/scripts/verify-agent-note-format.ts +158 -0
  58. package/skills/gate-write-agent-notes/scripts/verify-agent-note-lifecycle.ts +74 -0
  59. package/skills/gate-write-agent-notes/scripts/verify-agent-note-tree.ts +56 -0
  60. package/skills/gate-write-agent-notes/scripts/verify-archived-agent-notes.ts +127 -0
  61. package/skills/gate-write-agent-notes/templates/implemented.md +35 -0
  62. package/skills/gate-write-agent-notes/templates/proposed.md +41 -0
  63. package/skills/gate-write-agent-notes/templates/rejected.md +30 -0
  64. package/skills/gate-writing-for-agents/SKILL-MECHANICS.md +24 -0
  65. package/skills/gate-writing-for-agents/SKILL.md +85 -0
package/lib/index.js ADDED
@@ -0,0 +1,2202 @@
1
+ // src/config.ts
2
+ var DEFAULT_REVIEW_LIMIT = 5;
3
+ var SCHEMASTERY = /* @__PURE__ */ Symbol.for("schemastery");
4
+ var Config = {
5
+ [SCHEMASTERY]: true,
6
+ type: "object",
7
+ meta: { default: {} },
8
+ dict: {
9
+ reviewLimit: {
10
+ [SCHEMASTERY]: true,
11
+ type: "number",
12
+ meta: { default: DEFAULT_REVIEW_LIMIT, min: 1, max: 20, step: 1 }
13
+ }
14
+ },
15
+ "~standard": {
16
+ version: 1,
17
+ vendor: "dsh-gate-agent",
18
+ validate(value) {
19
+ if (value === void 0 || value === null) return { value: {} };
20
+ if (typeof value !== "object" || Array.isArray(value)) {
21
+ return { issues: [{ message: "gate config must be an object" }] };
22
+ }
23
+ const unknown = Object.keys(value).filter((key) => key !== "reviewLimit");
24
+ if (unknown.length > 0) {
25
+ return { issues: [{ message: `unknown gate config keys: ${unknown.join(", ")}` }] };
26
+ }
27
+ const out = {};
28
+ if (value.reviewLimit !== void 0) {
29
+ if (!Number.isInteger(value.reviewLimit) || value.reviewLimit < 1 || value.reviewLimit > 20) {
30
+ return { issues: [{ message: "reviewLimit must be an integer from 1 to 20" }] };
31
+ }
32
+ out.reviewLimit = value.reviewLimit;
33
+ }
34
+ return { value: out };
35
+ }
36
+ }
37
+ };
38
+
39
+ // src/protocol.ts
40
+ var DISCUSS_ORCHESTRATION = `## Discuss
41
+
42
+ discuss \u662F\u9ED8\u8BA4\u9636\u6BB5\uFF0C\u4E0D\u9700\u8981\u547D\u4EE4\u8FDB\u5165\u3002
43
+
44
+ 1. \u5148\u8BFB\u76F8\u5173\u4EE3\u7801\u3001\u6587\u6863\u3001\u5386\u53F2 Note \u4E0E\u9879\u76EE\u7EA6\u675F\u3002
45
+ 2. \u660E\u786E\u5B9E\u9645\u95EE\u9898\u3001\u7EA6\u675F\u3001\u5DF2\u6709\u51B3\u5B9A\u4E0E\u51B2\u7A81\u3002
46
+ 3. \u7ED9\u51FA\u6709\u5B9E\u8D28\u5DEE\u5F02\u7684\u53EF\u884C\u65B9\u6848\u3001\u63A8\u8350\u4E0E\u5173\u952E\u6743\u8861\u3002
47
+ 4. \u53EA\u5BF9\u672A\u51B3\u7684\u4EA7\u54C1\u3001\u8303\u56F4\u3001\u517C\u5BB9\u6027\u3001\u6210\u672C\u3001\u98CE\u9669\u7B49\u4EF7\u503C\u5224\u65AD\u63D0\u95EE\u3002
48
+ 5. \u4E0D\u521B\u5EFA\u63D0\u6848\u3001Note \u6216\u89C4\u683C\u6587\u4EF6\u3002
49
+ 6. \u6C47\u62A5\uFF1A\u5DF2\u89E3\u51B3\u4E8B\u9879\u3001\u5269\u4F59\u672A\u51B3\u4E8B\u9879\u3001\u5EFA\u8BAE\u7684\u4E0B\u4E00\u9636\u6BB5\u3002
50
+ 7. \u4E0D\u81EA\u884C\u79BB\u5F00 discuss\uFF1B\u79BB\u5F00\u5FC5\u987B\u7531\u7528\u6237\u660E\u786E\u6388\u6743\u3002\u6388\u6743\u53EF\u4EE5\u5728\u5BF9\u8BDD\u6216 ask_user_question \u4E2D\u7ED9\u51FA\uFF1B\u83B7\u5F97\u6388\u6743\u540E\u8FDB\u5165 plan/implement\uFF0C\u4E0D\u8981\u8981\u6C42\u7528\u6237\u518D\u8F93\u5165 slash command\u3002`;
51
+ var PLAN_ORCHESTRATION = `## /gate-enter-plan \u7F16\u6392
52
+ \u4E3B\u4EE3\u7406\u6D3E worker\uFF1Bgate \u4E0D\u6D3E\u3002
53
+
54
+ ## Step 1 \u2014 Planning
55
+ \u4E3B agent \u9996\u6B21\u8FDB\u5165 planning \u65F6\u52A0\u8F7D\u6280\u80FD gate-planning \u548C gate-write-agent-notes\uFF1B\u82E5\u672C\u4F1A\u8BDD\u5DF2\u52A0\u8F7D\u4E14\u5185\u5BB9\u4ECD\u5728\u4E0A\u6587\uFF0C\u76F4\u63A5\u590D\u7528\uFF0C\u4E0D\u8981\u91CD\u590D\u8C03\u7528 skill\u3002\u8BFB\u53D6 gate-planning \u7684 SKILL.md\u3001proposal.md \u548C assets/worker-briefs/gate-enter-plan.md \u5199 planning \u4EFB\u52A1\u3002
56
+ \u82E5\u5DE5\u4F5C\u9879\u8981\u5199\u8BBE\u8BA1\u8282\uFF08Note \u7684 \u201C## Design\u201D / \u201C### \u8BBE\u8BA1\u201D / \u81EA\u7531\u8BBE\u8BA1\u8282\uFF09\uFF0C\u5FC5\u987B\u5148\u8BFB\u53D6 rfc.md\uFF0C\u6309\u5176\u68C0\u67E5\u9879\u6536\u655B\uFF0C\u5E76\u628A\u9002\u7528\u68C0\u67E5\u70B9\u5199\u8FDB worker prompt\uFF1Bmechanical \u6216\u4E0D\u5199\u8BBE\u8BA1\u8282\u7684\u5DE5\u4F5C\u9879\u4E0D\u5F3A\u5236\u8BFB rfc\u3002
57
+ \u5148\u6309 write-agent-notes \xA73 \u68C0\u7D22\u5386\u53F2\uFF0C\u518D\u6309 \xA74 \u5224\u5B9A\u5F52\u5C5E\u4E0E class\uFF1B\u7528 gate_note_write \u5199 Note\uFF0C\u8DEF\u5F84\u4E0E class \u5FC5\u987B\u4E00\u81F4\u3002\u53D1\u73B0\u65E7 implemented Note \u88AB\u65B0\u51B3\u5B9A\u5B8C\u5168\u53D6\u4EE3\u65F6\uFF0C\u8BB0\u4E0B\u5F52\u6863\u5019\u9009\uFF1B\u65B0 Note \u5C1A\u672A\u843D\u5730\u524D\u4E0D\u8981\u5F52\u6863\u3002
58
+ \u628A\u5DE5\u4F5C\u9879\u3001\u7EA6\u675F\u548C\u63D0\u70BC\u540E\u7684\u89C4\u5212\u65B9\u6CD5\u5199\u8FDB subagent prompt\uFF1Bplanning worker \u4E0D\u52A0\u8F7D gate \u6280\u80FD\u3002
59
+ \u5B8C\u6210\u540E\u8C03 gate_record({node:'plan', task, notePaths, noteDecision, noteSearch, workerSessionIds})\u3002
60
+ notePaths \u662F [{path,class,lifecycle}] \u6570\u7EC4\uFF1Bpath \u63A5\u53D7 .agents/notes/<lifecycle>/<class>/...\u3001<lifecycle>/<class>/... \u6216 <class>/...\uFF0Cgate_record \u5F52\u4E00\u4E3A\u5B8C\u6574\u8DEF\u5F84\uFF1Bmechanical:<\u8BF4\u660E> \u7528 noteDecision:'mechanical' \u4E14 notePaths \u4E3A\u7A7A\u3002
61
+
62
+ ## Step 2 \u2014 Plan Review
63
+ \u4E3B agent \u9996\u6B21\u8FDB\u5165 plan-review \u65F6\u52A0\u8F7D\u6280\u80FD gate-plan-review\uFF1B\u82E5\u672C\u4F1A\u8BDD\u5DF2\u52A0\u8F7D\u4E14\u5185\u5BB9\u4ECD\u5728\u4E0A\u6587\uFF0C\u540E\u7EED\u591A\u8F6E review \u76F4\u63A5\u590D\u7528\uFF0C\u4E0D\u8981\u91CD\u590D\u8C03\u7528 skill\u3002\u8BFB\u53D6\u8BE5\u6280\u80FD\u7684 assets/worker-briefs/plan-review.md \u5199 review \u4EFB\u52A1\uFF0C\u6309\u4EFB\u52A1\u89C4\u6A21\u548C\u98CE\u9669\u81EA\u884C\u51B3\u5B9A\u6D3E\u4E00\u4E2A\u6216\u591A\u4E2A\u53EA\u8BFB review worker\uFF0C\u8986\u76D6 Standards\u3001Spec\u3001Architecture \u4E09\u8F74\u3002
64
+ \u7ED9 worker \u5DE5\u4F5C\u9879\u3001Note\u3001\u56FA\u5B9A\u70B9\u548C\u8BC4\u5BA1\u65B9\u6CD5\uFF1B\u4E0D\u8981\u5728 review worker prompt \u4E2D\u9644\u5E26\u9879\u76EE\u73AF\u5883\u9A8C\u8BC1\uFF08package.json\u3001Node \u7248\u672C\u3001git \u72B6\u6001\u3001\u6587\u4EF6\u5B58\u5728\u6027\u3001\u521D\u59CB\u5316 smoke \u7ED3\u679C\uFF09\u3002
65
+ \u5B8C\u6210\u540E\u8C03 gate_record({node:'plan-review',findings,reviewAxes,workerSessionIds,requiresDiscussion})\u3002
66
+ reviewAxes \u5FC5\u987B\u5305\u542B standards\u3001spec\u3001architecture \u4E09\u8F74\u3002
67
+ \u9700\u8981\u7528\u6237\u51B3\u5B9A\uFF1A\u8FDB\u5165 blocked\uFF0C\u7B49\u5F85 /gate-resume\uFF1B\u4ECD\u6709 findings\uFF1A\u5148 gate_record \u672C\u8F6E plan-review\uFF0C\u786E\u8BA4 node \u5DF2\u53D8\u4E3A plan-revise\uFF0C\u518D\u590D\u7528\u5DF2\u52A0\u8F7D\u7684 gate-planning\uFF0C\u53EA\u5E26 findings \u548C Note \u8DEF\u5F84\u6D3E revise\uFF0C\u518D review\u3002
68
+
69
+ ## Step 3 \u2014 \u56DE\u5230 Discuss
70
+ review \u4E3A\u7A7A\u65F6\u56DE\u5230 discuss\uFF0CplanReady=true\u3002
71
+ \u5B9E\u65BD\u9700\u8981\u7528\u6237\u53E6\u884C\u6267\u884C /gate-enter-implement\uFF1B\u4E0D\u8981\u5728 planning \u91CC\u5F00\u59CB\u5B9E\u73B0\u3002`;
72
+ var IMPLEMENT_ORCHESTRATION = `## /gate-enter-implement \u7F16\u6392
73
+ \u4E3B\u4EE3\u7406\u6D3E worker\uFF1Bgate \u4E0D\u6D3E\u3002
74
+
75
+ ## Step 1 \u2014 Implement
76
+ \u4E3B agent \u9996\u6B21\u8FDB\u5165 implement \u65F6\u52A0\u8F7D\u6280\u80FD gate-implement\uFF1B\u82E5\u672C\u4F1A\u8BDD\u5DF2\u52A0\u8F7D\u4E14\u5185\u5BB9\u4ECD\u5728\u4E0A\u6587\uFF0C\u76F4\u63A5\u590D\u7528\uFF0C\u4E0D\u8981\u91CD\u590D\u8C03\u7528 skill\u3002\u8BFB\u53D6\u8BE5\u6280\u80FD\u7684 assets/worker-briefs/gate-enter-implement.md \u5199 implement \u4EFB\u52A1\u3002\u91C7\u7528\u6D4B\u8BD5\u4F18\u5148\u65F6\u540C\u65F6\u52A0\u8F7D gate-tdd\uFF0C\u5E76\u628A\u76F8\u5173\u89C4\u5219\u4EA4\u7ED9 implement worker\u3002
77
+ worker \u4E0D\u52A0\u8F7D gate \u6280\u80FD\u3002
78
+ \u5B8C\u6210\u540E\u8C03 gate_record({node:'implement', workerSessionIds})\u3002
79
+
80
+ ## Step 2 \u2014 Implementation Review
81
+ \u4E3B agent \u9996\u6B21\u8FDB\u5165 code-review \u65F6\u52A0\u8F7D\u6280\u80FD gate-code-review\uFF1B\u82E5\u672C\u4F1A\u8BDD\u5DF2\u52A0\u8F7D\u4E14\u5185\u5BB9\u4ECD\u5728\u4E0A\u6587\uFF0C\u540E\u7EED review \u8F6E\u6B21\u76F4\u63A5\u590D\u7528\uFF0C\u4E0D\u8981\u91CD\u590D\u8C03\u7528 skill\u3002\u8BFB\u53D6\u8BE5\u6280\u80FD\u7684 assets/worker-briefs/gate-implement-review.md \u5199 review \u4EFB\u52A1\uFF0C\u6309\u6539\u52A8\u89C4\u6A21\u81EA\u884C\u51B3\u5B9A\u6D3E\u4E00\u4E2A\u6216\u591A\u4E2A\u53EA\u8BFB review worker\uFF0C\u8986\u76D6 Standards \u4E0E Spec \u4E24\u8F74\u3002
82
+ \u5B8C\u6210\u540E\u8C03 gate_record({node:'code-review', findings, reviewAxes, workerSessionIds})\uFF1B\u6709 findings \u65F6\u5148\u8BB0\u5F55\uFF0C\u786E\u8BA4 node \u5DF2\u53D8\u4E3A implement-revise\uFF0C\u518D\u6D3E revise worker\u3002
83
+ \u5BA1\u67E5\u8981\u6C42\u6EE1\u8DB3\u540E\u7EE7\u7EED Step 3\uFF0C\u65E0\u9700\u518D\u6B21\u6388\u6743\u3002
84
+ \u5B9E\u73B0\u4E2D\u53D1\u73B0\u8303\u56F4\u5916\u95EE\u9898\u3001\u5408\u540C\u53D8\u5316\u6216\u9700\u8981\u91CD\u65B0\u8BA8\u8BBA\u65F6\uFF0Cworker \u505C\u6B62\u5E76\u8FD4\u56DE receipt\uFF1B\u4E3B agent \u5411\u7528\u6237\u8BF4\u660E\uFF0C\u4F7F\u7528 gate_record \u7684 requiresDiscussion \u6216\u63D0\u793A\u7528\u6237\u6267\u884C /gate-return-discuss\u3002\u4E0D\u8981\u505C\u5728 implement \u91CC\u7EE7\u7EED\u6269\u5927\u8303\u56F4\u3002
85
+
86
+ ## Step 3 \u2014 Verify
87
+ \u4E3B agent \u9996\u6B21\u8FDB\u5165 verify \u65F6\u52A0\u8F7D\u6280\u80FD gate-verify\uFF1B\u82E5\u672C\u4F1A\u8BDD\u5DF2\u52A0\u8F7D\u4E14\u5185\u5BB9\u4ECD\u5728\u4E0A\u6587\uFF0C\u76F4\u63A5\u590D\u7528\uFF0C\u4E0D\u8981\u91CD\u590D\u8C03\u7528 skill\u3002\u8BFB\u53D6\u8BE5\u6280\u80FD\u7684 assets/worker-briefs/gate-verify.md \u5199 verify \u4EFB\u52A1\uFF0C\u6D3E verify worker \u5B8C\u6210\u6700\u7EC8\u9A8C\u6536\u3002
88
+ \u5B8C\u6210\u540E\u8C03 gate_record({node:'verify', verification, workerSessionIds})\uFF1Bverification.toolResults \u5FC5\u987B\u662F verify worker session log \u4E2D\u771F\u5B9E tool/result \u7684 call_... id\uFF0Cevidence \u662F workspace \u5185\u7684\u666E\u901A\u6587\u4EF6\u3002
89
+ \u53EA\u6709\u6700\u7EC8 PASS \u624D\u5B8C\u6210\u4EA4\u4ED8\uFF1BFAIL \u56DE\u5230 implement-revise\u3002
90
+ \u72B6\u6001\u66F4\u65B0\u9075\u5FAA\u9879\u76EE\u767B\u8BB0\u7684\u5DE5\u4F5C\u9879\u4E0E\u51B3\u7B56\u8BB0\u5F55\u751F\u547D\u5468\u671F\u3002
91
+
92
+ ## Step 4 \u2014 Note lifecycle
93
+ Verify PASS \u540E\u8FDB\u5165 note-lifecycle\uFF0C\u4E0D\u5F97\u76F4\u63A5 complete\u3002
94
+ \u4E3B agent \u8C03 gate_note_finalize\uFF0C\u518D\u8C03 gate_note_verify\uFF1B\u901A\u8FC7\u540E gate_record({node:'note-lifecycle'})\u3002
95
+ mechanical \u5DE5\u4F5C\u9879\u81EA\u52A8\u8DF3\u8FC7\uFF1B\u8DE8\u4F1A\u8BDD journal \u53EA\u62A5\u544A\u4E0D\u6062\u590D\uFF1Barchived manifest \u53EA\u7531\u7528\u6237\u6267\u884C /gate-note-seal\u3002note-lifecycle \u5B8C\u6210\u540E\u5EFA\u8BAE\u63D0\u4EA4\u672C\u6B21\u5B9E\u73B0\u4E0E Note\uFF1Bgate \u4E0D\u81EA\u52A8\u6267\u884C git add/commit\u3002`;
96
+ var FIX_BUG_ORCHESTRATION = `## /gate-fix-bug \u7F16\u6392
97
+ \u4E3B agent \u8BFB\u53D6 gate-fix-bug\uFF1B\u4E3B agent \u53EA\u6C47\u603B\uFF0C\u4E0D\u81EA\u884C\u4FEE\u590D\u3002
98
+
99
+ ## Step 1 \u2014 Fix
100
+ \u4E3B agent \u9996\u6B21\u8FDB\u5165 fix-bug \u65F6\u8BFB\u53D6/\u52A0\u8F7D gate-fix-bug\uFF1B\u5DF2\u52A0\u8F7D\u5219\u590D\u7528\uFF0C\u4E0D\u8981\u91CD\u590D\u8C03\u7528 skill\u3002\u8BFB\u53D6 gate-fix-bug \u6280\u80FD\u7684 assets/worker-briefs/gate-fix-bug.md\uFF0C\u6D3E fix worker\u3002fix worker \u4F7F\u7528 gate-fix-bug \u6267\u884C Process 1-9\uFF1A\u590D\u73B0\u3001\u56DE\u5F52\u8986\u76D6\u3001\u6839\u56E0\u3001\u6700\u5C0F\u4FEE\u590D\u3001\u539F\u590D\u73B0\u4E0E\u76F8\u5173\u6D4B\u8BD5\u3002\u5B8C\u6210\u540E\u8FD4\u56DE\u4FEE\u590D receipt\uFF1B\u4E0D\u8981\u81EA\u884C\u5B8C\u6210 Step 10-12\uFF0C\u4E5F\u4E0D\u8981\u63D0\u4EA4\u3002
101
+
102
+ ## Step 2 \u2014 Independent review
103
+ \u4E3B agent \u9996\u6B21\u8FDB\u5165 review \u65F6\u52A0\u8F7D gate-code-review\uFF1B\u5DF2\u52A0\u8F7D\u5219\u590D\u7528\uFF0C\u4E0D\u8981\u91CD\u590D\u8C03\u7528 skill\u3002\u63D0\u70BC\u4E24\u8F74\u8BC4\u5BA1\u8981\u6C42\uFF0C\u518D\u8BFB\u53D6 gate-fix-bug \u6280\u80FD\u7684 assets/worker-briefs/fix-bug-review.md\uFF0C\u6D3E\u4E00\u4E2A\u72EC\u7ACB review worker\uFF1B\u4FEE\u590D\u57FA\u7EBF\u3001\u9009\u5B9A\u5931\u8D25\u3001\u5408\u540C\u5F15\u7528\u548C\u5B8C\u6574 diff \u5199\u8FDB\u7B80\u62A5\u3002review \u53EA\u62A5 findings\u3002
104
+
105
+ ## Step 3 \u2014 Resolve
106
+ \u628A material in-scope findings \u4EA4\u56DE fix worker\uFF08send_message \u6216\u65B0 worker\uFF09\uFF0C\u5B8C\u6210 gate-fix-bug Step 11-12\uFF0C\u91CD\u8DD1\u539F\u590D\u73B0\u548C\u76F8\u5173\u6D4B\u8BD5\uFF1B\u91CD\u590D Step 2-3 \u76F4\u5230\u6CA1\u6709 material findings\u3002
107
+
108
+ ## Step 4 \u2014 Record
109
+ \u5B8C\u6210\u540E\u8C03 gate_record({node:'fix-bug', workerSessionIds})\uFF0CworkerSessionIds \u5305\u542B fix \u4E0E review worker\uFF1Bgate \u4E0D\u518D\u81EA\u52A8\u63D0\u4EA4\u3002`;
110
+ var DOCS_ORCHESTRATION = `## /gate-docs \u7F16\u6392
111
+ \u4E0D\u52A0\u8F7D gate \u6280\u80FD\uFF1B\u8303\u56F4\u7531\u6A21\u578B\u6309\u9879\u76EE\u5B9E\u9645\u6587\u6863\u9700\u8981\u5224\u65AD\u3002
112
+ \u5B8C\u6210\u540E\u8C03 gate_record({node:'docs'}) \u56DE\u5230 discuss\u3002`;
113
+ var NOTE_LIFECYCLE_ORCHESTRATION = `## Note lifecycle
114
+ Verify \u5DF2 PASS\uFF1B\u5F53\u524D\u4E0D\u6D3E worker\uFF0C\u4E5F\u4E0D\u5199\u4E1A\u52A1\u4EE3\u7801\u3002
115
+ \u6309\u987A\u5E8F\u6267\u884C\uFF1Agate_note_finalize \u2192 gate_note_verify \u2192 gate_record({node:'note-lifecycle'})\u3002
116
+ finalize/verify \u5931\u8D25\u7559\u5728\u672C\u8282\u70B9\u4FEE\u590D\uFF1Barchived manifest \u7F3A\u5931\u65F6\u63D0\u793A\u7528\u6237\u6267\u884C /gate-note-seal\u3002
117
+ \u53EA\u6709 noteVerified=true \u624D\u80FD gate_record \u5B8C\u6210\uFF1Bmechanical \u5DE5\u4F5C\u9879\u53EF\u8DF3\u8FC7 finalize\u3002\u5B8C\u6210\u540E\u5EFA\u8BAE\u63D0\u4EA4\u672C\u6B21\u5B9E\u73B0\u4E0E Note\uFF1Bgate \u4E0D\u81EA\u52A8 git add/commit\u3002\u5B8C\u6210\u540E\u68C0\u67E5\u672C\u6B21\u65B0 Note \u662F\u5426\u5B8C\u5168\u53D6\u4EE3\u4E86\u67D0\u7BC7 implemented Note\uFF1A\u6709\u72EC\u7ACB\u53C2\u8003\u4EF7\u503C\u5C31\u5728\u6700\u7EC8\u6C47\u62A5\u4E2D\u7ED9\u51FA\u5F52\u6863\u5019\u9009\u548C\u76EE\u6807\u547D\u4EE4\uFF0C\u6CA1\u6709\u72EC\u7ACB\u4EF7\u503C\u5219\u5EFA\u8BAE\u5220\u9664\uFF1B\u4E0D\u8981\u81EA\u52A8\u5F52\u6863\u3002`;
118
+ var GATE_RECORD_NODES = ["plan", "plan-review", "proposal-review", "plan-revise", "implement", "code-review", "implement-revise", "verify", "verify-standalone", "note-lifecycle", "docs", "code-review-standalone", "fix-bug"];
119
+ var NODE_INFO = {
120
+ plan: { variant: "plan", method: "`gate-planning` + `gate-write-agent-notes`" },
121
+ "plan-review": { variant: "plan", method: "`gate-plan-review`" },
122
+ "plan-revise": { variant: "plan", method: "`gate-planning` + `gate-write-agent-notes`" },
123
+ "proposal-review": { variant: "reviewing-proposal", method: "`gate-plan-review`\uFF08\u4E09\u8F74\uFF09" },
124
+ implement: { variant: "implement", method: "`gate-implement`" },
125
+ "implement-revise": { variant: "implement", method: "`gate-implement`" },
126
+ "code-review": { variant: "implement", method: "`gate-code-review` (one skill, both axes)" },
127
+ "code-review-standalone": { variant: "reviewing-code", method: "`gate-code-review` (one skill, both axes)" },
128
+ verify: { variant: "implement", method: "`gate-verify`" },
129
+ "verify-standalone": { variant: "reviewing-verify", method: "\u65E0" },
130
+ "note-lifecycle": { variant: "note-lifecycle", method: "`gate-write-agent-notes`\uFF08lifecycle \u5DE5\u5177\uFF0C\u4E0D\u662F worker\uFF09" },
131
+ docs: { variant: "docs", method: "\u65E0" },
132
+ "fix-bug": { variant: "fix-bug", method: "`gate-fix-bug`" }
133
+ };
134
+ var NODE_METHODS = Object.fromEntries(Object.entries(NODE_INFO).map(([node, info]) => [node, info.method]));
135
+ var WORKER_NODES = new Set(GATE_RECORD_NODES.filter((node) => node !== "docs" && node !== "note-lifecycle"));
136
+ var DISPATCH_RULE = `## \u6280\u80FD\u52A0\u8F7D\u89C4\u5219
137
+ 1. \u672C\u4F1A\u8BDD\u5DF2\u7ECF\u901A\u8FC7 skill \u5DE5\u5177\u52A0\u8F7D\u8FC7\u7684\u6280\u80FD\uFF0C\u5185\u5BB9\u4ECD\u5728\u4E0A\u6587\u65F6\u76F4\u63A5\u590D\u7528\uFF0C\u4E0D\u8981\u91CD\u590D\u8C03\u7528 skill\u3002
138
+ 2. \u53EA\u6709\u4EE5\u4E0B\u60C5\u51B5\u624D\u91CD\u65B0\u52A0\u8F7D\uFF1A\u672C\u4F1A\u8BDD\u9996\u6B21\u8FDB\u5165\u8BE5\u6280\u80FD\uFF1Bcompaction \u540E\u5185\u5BB9\u5DF2\u4E0D\u5728\u4E0A\u4E0B\u6587\uFF1B\u6280\u80FD\u6587\u4EF6/\u7248\u672C\u53D1\u751F\u53D8\u5316\uFF1B\u7528\u6237\u660E\u786E\u8981\u6C42\u5237\u65B0\u3002
139
+ 3. gate_record / gate_status \u8FD4\u56DE\u65B0\u7684 procedure \u53EA\u4EE3\u8868 node\u3001\u8F6E\u6B21\u6216 findings \u53D8\u5316\uFF0C\u4E0D\u4EE3\u8868\u8981\u91CD\u65B0\u52A0\u8F7D\u6280\u80FD\u3002
140
+ 4. \u591A\u8F6E review/revise \u590D\u7528\u540C\u4E00\u4EFD\u6280\u80FD\u5185\u5BB9\uFF1B\u628A\u65B0\u7684 findings\u3001\u6587\u4EF6\u8303\u56F4\u548C\u8981\u6C42\u4F20\u7ED9 worker\uFF0C\u4E0D\u8981\u91CD\u65B0\u52A0\u8F7D\u6280\u80FD\u3002`;
141
+ var REVIEWING_CODE_PROCEDURE = `## /gate-implement-review \u7F16\u6392
142
+ \u4E3B agent \u9996\u6B21\u8FDB\u5165\u65F6\u52A0\u8F7D gate-code-review\uFF1B\u5DF2\u52A0\u8F7D\u5219\u590D\u7528\uFF0C\u4E0D\u8981\u91CD\u590D\u8C03\u7528 skill\u3002\u6D3E\u53EA\u8BFB review worker \u8986\u76D6 Standards \u4E0E Spec \u4E24\u8F74\uFF1B\u5B8C\u6210\u540E gate_record({node:'code-review-standalone', findings, reviewAxes, workerSessionIds}) \u56DE discuss\u3002`;
143
+ var REVIEWING_PROPOSAL_PROCEDURE = `## /gate-propose-review \u7F16\u6392
144
+ \u4E3B agent \u9996\u6B21\u8FDB\u5165\u65F6\u52A0\u8F7D gate-plan-review\uFF1B\u5DF2\u52A0\u8F7D\u5219\u590D\u7528\uFF0C\u4E0D\u8981\u91CD\u590D\u8C03\u7528 skill\u3002\u6D3E\u53EA\u8BFB review worker \u8986\u76D6 Standards/Spec/Architecture \u4E09\u8F74\uFF1B\u5B8C\u6210\u540E gate_record({node:'proposal-review', findings, reviewAxes, workerSessionIds}) \u56DE discuss\u3002`;
145
+ var REVIEWING_VERIFY_PROCEDURE = `## /gate-code-verify \u7F16\u6392
146
+ \u6D3E\u5B50\u4EE3\u7406\u52A0\u8F7D\u4F7F\u7528 gate-user-verify \u6309 Step 6 \u6C47\u62A5\uFF1Bverification.toolResults \u5FC5\u987B\u662F verify worker session log \u4E2D\u771F\u5B9E tool/result \u7684 call_... id\uFF0Cevidence \u662F workspace \u5185\u7684\u666E\u901A\u6587\u4EF6\uFF1B\u5B8C\u6210\u540E gate_record({node:'verify-standalone', verification, workerSessionIds}) \u56DE discuss\u3002`;
147
+ function stepSection(text, from, to) {
148
+ const start = text.indexOf(from);
149
+ if (start < 0) return "";
150
+ const end = to === void 0 ? text.length : text.indexOf(to, start);
151
+ return text.slice(start, end < 0 ? text.length : end).trim();
152
+ }
153
+ var PLAN_STEP_1 = stepSection(PLAN_ORCHESTRATION, "## Step 1", "## Step 2");
154
+ var PLAN_STEP_2 = stepSection(PLAN_ORCHESTRATION, "## Step 2", "## Step 3");
155
+ var IMPLEMENT_STEP_1 = stepSection(IMPLEMENT_ORCHESTRATION, "## Step 1", "## Step 2");
156
+ var IMPLEMENT_STEP_2 = stepSection(IMPLEMENT_ORCHESTRATION, "## Step 2", "## Step 3");
157
+ var IMPLEMENT_STEP_3 = stepSection(IMPLEMENT_ORCHESTRATION, "## Step 3", "## Step 4");
158
+ var NODE_PROCEDURES = {
159
+ plan: PLAN_STEP_1,
160
+ "plan-review": PLAN_STEP_2,
161
+ "plan-revise": `## Plan revise
162
+ \u6839\u636E findings \u7528 gate_note_write \u66F4\u65B0 Note\uFF1B\u5B8C\u6210\u540E gate_record({node:'plan-revise'}) \u56DE\u5230 plan-review\uFF0C\u518D\u6309 plan-review procedure review\u3002`,
163
+ implement: IMPLEMENT_STEP_1,
164
+ "code-review": IMPLEMENT_STEP_2,
165
+ "implement-revise": `## Implement revise
166
+ \u4FEE\u590D findings\uFF1B\u91CD\u8DD1\u6D4B\u8BD5\uFF1B\u5B8C\u6210\u540E gate_record({node:'implement-revise'}) \u56DE\u5230 code-review\uFF0C\u518D\u6309 code-review procedure review\u3002`,
167
+ verify: IMPLEMENT_STEP_3,
168
+ "note-lifecycle": NOTE_LIFECYCLE_ORCHESTRATION,
169
+ "fix-bug": FIX_BUG_ORCHESTRATION,
170
+ "code-review-standalone": REVIEWING_CODE_PROCEDURE,
171
+ "proposal-review": REVIEWING_PROPOSAL_PROCEDURE,
172
+ "verify-standalone": REVIEWING_VERIFY_PROCEDURE
173
+ };
174
+ var GATE_METHODOLOGY = `## Gate \u65B9\u6CD5\u8BBA
175
+ \u672C\u6BB5\u53EA\u5B9A\u4E49\u5404\u9636\u6BB5\u7684\u5DE5\u4F5C\u65B9\u6CD5\u4E0E\u8D28\u91CF\u6807\u51C6\u3002\u5F53\u524D phase/node\u3001procedure \u548C\u8981\u52A0\u8F7D\u7684\u6280\u80FD\u6765\u81EA\u6700\u8FD1\u4E00\u6B21 gate_status\u3001gate_record \u6216\u547D\u4EE4\u7ED3\u679C\uFF1B\u53EA\u6267\u884C\u5F53\u524D phase \u7684 procedure\uFF0C\u5176\u4ED6\u9636\u6BB5\u7684\u65B9\u6CD5\u8BBA\u4EC5\u4F5C\u80CC\u666F\u53C2\u8003\uFF1Bcompaction \u540E\u5FC5\u987B\u91CD\u65B0\u83B7\u53D6 procedure\uFF0C\u5E76\u786E\u8BA4\u6280\u80FD\u5185\u5BB9\u662F\u5426\u4ECD\u5728\u4E0A\u4E0B\u6587\uFF1A\u9996\u6B21\u9700\u8981\u65F6\u52A0\u8F7D\uFF0C\u5DF2\u5728\u4E0A\u6587\u5219\u590D\u7528\uFF0C\u4E0D\u8981\u56E0 procedure \u66F4\u65B0\u91CD\u590D\u52A0\u8F7D\u3002
176
+
177
+ ## discuss
178
+ discuss \u662F\u9ED8\u8BA4\u9636\u6BB5\u3002\u5148\u8BFB\u76F8\u5173\u4EE3\u7801\u3001\u6587\u6863\u3001\u5386\u53F2 Note \u4E0E\u9879\u76EE\u7EA6\u675F\uFF1B\u660E\u786E\u5B9E\u9645\u95EE\u9898\u3001\u7EA6\u675F\u3001\u5DF2\u6709\u51B3\u5B9A\u4E0E\u51B2\u7A81\uFF1B\u7ED9\u51FA\u6709\u5B9E\u8D28\u5DEE\u5F02\u7684\u53EF\u884C\u65B9\u6848\u3001\u63A8\u8350\u4E0E\u5173\u952E\u6743\u8861\uFF1B\u53EA\u5BF9\u672A\u51B3\u7684\u4EA7\u54C1\u3001\u8303\u56F4\u3001\u517C\u5BB9\u6027\u3001\u6210\u672C\u3001\u98CE\u9669\u7B49\u4EF7\u503C\u5224\u65AD\u63D0\u95EE\uFF1B\u6C47\u62A5\u5DF2\u89E3\u51B3\u3001\u672A\u51B3\u4E8B\u9879\u548C\u5EFA\u8BAE\u4E0B\u4E00\u9636\u6BB5\u3002\u4E0D\u8FDB\u5165\u5B9E\u65BD\uFF0C\u53EA\u6709\u7528\u6237\u660E\u786E\u6388\u6743\u624D\u79BB\u5F00\u3002
179
+
180
+ ## plan
181
+ \u628A\u7528\u6237\u7B80\u8FF0\u6536\u655B\u6210\u53EF\u5B9E\u65BD\u8BA1\u5212\u4E0E proposed Note\uFF1B\u8BA1\u5212\u5FC5\u987B\u5199\u6E05\u8303\u56F4\u3001\u9A8C\u6536\u3001\u5907\u9009\u4E0E\u98CE\u9669\u3002Plan Review \u72EC\u7ACB\u5224\u65AD\uFF1Bfindings \u5148\u8BB0\u5F55\u518D revise\uFF1Breview \u901A\u8FC7\u540E\u56DE discuss\u3002\u4E0D\u8981\u5728\u89C4\u5212\u91CC\u5F00\u59CB\u5B9E\u73B0\u3002
182
+
183
+ ## implement
184
+ \u6309\u5DF2\u786E\u8BA4\u7684\u8BA1\u5212\u5B9E\u65BD\u5E76\u5B8C\u6210\u6D4B\u8BD5\uFF1B\u5B9E\u73B0\u540E\u505A\u72EC\u7ACB review\uFF0C\u518D\u7528\u53EF\u6267\u884C\u8BC1\u636E verify\uFF1B\u6700\u540E\u5B8C\u6210 Note lifecycle\u3002\u4E0D\u6269\u5927\u8303\u56F4\uFF0C\u4E0D\u5728\u5B9E\u73B0\u4E2D\u91CD\u65B0\u89C4\u5212\u3002
185
+
186
+ ## code-review
187
+ \u72EC\u7ACB\u5224\u65AD\u6539\u52A8\u662F\u5426\u6EE1\u8DB3\u5408\u540C\u4E0E\u6807\u51C6\uFF1B\u53EA\u62A5 findings\uFF0C\u4E0D\u4FEE\u4EE3\u7801\u3001\u4E0D\u66FF\u4EE3 verify\uFF1Bfindings \u8BB0\u5F55\u540E\u518D\u8FDB\u5165 revise\u3002
188
+
189
+ ## verify
190
+ \u7528\u53EF\u6267\u884C\u8BC1\u636E\u5224\u5B9A PASS/FAIL\uFF1B\u4E0D\u4FEE\u6539\u4EA7\u54C1/\u6D4B\u8BD5\u8BA9\u7ED3\u679C\u53D8\u7EFF\uFF1BFAIL \u56DE implement-revise\u3002
191
+
192
+ ## note-lifecycle
193
+ \u5B8C\u6210 Note \u7684 finalize \u4E0E\u6821\u9A8C\uFF1B\u4E0D\u5199\u4E1A\u52A1\u4EE3\u7801\uFF1B\u68C0\u67E5\u662F\u5426\u4EA7\u751F\u5F52\u6863\u5019\u9009\uFF1B\u5B8C\u6210\u540E\u56DE discuss\u3002
194
+
195
+ ## fix-bug
196
+ \u5BF9\u5DF2\u6388\u6743\u7684\u5B9E\u73B0\u7F3A\u9677\uFF1A\u590D\u73B0\u3001\u56DE\u5F52\u8986\u76D6\u3001\u6839\u56E0\u3001\u6700\u5C0F\u4FEE\u590D\u3001\u72EC\u7ACB review\uFF1B\u4E0D\u505A\u65B0\u529F\u80FD\u3001\u4E0D\u6269\u5927\u8303\u56F4\uFF1B\u5B8C\u6210\u540E\u56DE discuss\u3002
197
+
198
+ ## docs
199
+ \u6587\u6863\u6CBB\u7406\uFF1B\u4E0D\u6539\u53D8\u529F\u80FD\u884C\u4E3A\uFF1B\u8303\u56F4\u7531\u6A21\u578B\u6309\u9879\u76EE\u5B9E\u9645\u6587\u6863\u9700\u8981\u5224\u65AD\uFF1B\u5B8C\u6210\u540E\u56DE discuss\u3002
200
+
201
+ ## reviewing-code
202
+ \u53EA\u8BFB code review\uFF1A\u72EC\u7ACB\u5224\u65AD Standards \u4E0E Spec \u4E24\u8F74\uFF0C\u53EA\u8F93\u51FA findings\uFF0C\u4E0D\u4FEE\u4EE3\u7801\u3001\u4E0D\u66FF\u4EE3 verify\u3002
203
+
204
+ ## reviewing-proposal
205
+ \u53EA\u8BFB\u63D0\u6848\u8BC4\u5BA1\uFF1A\u8986\u76D6 Standards\u3001Spec\u3001Architecture \u4E09\u8F74\uFF0C\u53EA\u8F93\u51FA\u5224\u65AD\uFF0C\u4E0D\u4FEE\u6539\u89C4\u5212\u3001\u4E0D\u81EA\u52A8 revise\u3002
206
+
207
+ ## reviewing-verify
208
+ \u53EA\u8BFB\u72EC\u7ACB\u9A8C\u6536\uFF1A\u4E0D\u6539\u4EE3\u7801\u3001\u4E0D\u66FF\u4EE3 implement\uFF0C\u53EA\u8F93\u51FA PASS/FAIL \u4E0E\u8BC1\u636E\u5224\u65AD\u3002
209
+
210
+ ## blocked / complete
211
+ blocked \u8868\u793A\u51BB\u7ED3\uFF1A\u7B49\u5F85 /gate-resume \u6216 /gate-return-discuss\u3002complete \u8868\u793A\u5F53\u524D\u4EFB\u52A1\u5B8C\u6210\uFF0C\u65B0\u4EFB\u52A1\u9700\u8981\u91CD\u65B0\u6388\u6743\u3002`;
212
+ function protocolVariant(state) {
213
+ if (state.phase === "orchestrating") return NODE_INFO[state.node ?? ""]?.variant ?? state.phase;
214
+ if (state.phase === "reviewing") return NODE_INFO[state.node ?? ""]?.variant ?? "reviewing-code";
215
+ return state.phase;
216
+ }
217
+ function procedureFor(state, { reviewLimit = DEFAULT_REVIEW_LIMIT } = {}) {
218
+ const variant = protocolVariant(state);
219
+ const method = NODE_METHODS[state.node ?? ""] ?? "\u65E0";
220
+ let text;
221
+ if (variant === "discuss") {
222
+ text = "discuss \u4E0D\u6D3E worker\uFF1B\u4E3B\u4EE3\u7406\u53EF\u81EA\u884C\u8BFB/\u5199\uFF1B\u7528\u6237\u6388\u6743\u540E\u8C03\u7528 gate_start_phase \u8FDB\u5165 plan/implement\u3002";
223
+ } else if (variant === "blocked") {
224
+ text = `Gate \u5DF2\u51BB\u7ED3\uFF1A${state.blockReason ?? "unknown"}\uFF1B\u89E3\u51BB\u540E\u56DE ${state.blockedFrom?.phase ?? "discuss"} / ${state.blockedFrom?.node ?? "\u65E0"}\uFF1B\u7528 /gate-resume \u6216 /gate-return-discuss\u3002`;
225
+ } else if (variant === "complete") {
226
+ text = "Gate \u5DF2\u5B8C\u6210\uFF1B\u65B0\u4EFB\u52A1\u9700\u8981\u91CD\u65B0\u6388\u6743\u3002";
227
+ } else if (variant === "docs") {
228
+ text = DOCS_ORCHESTRATION;
229
+ } else {
230
+ text = NODE_PROCEDURES[state.node ?? ""] ?? "";
231
+ }
232
+ if (text === "") {
233
+ text = "\u5F53\u524D phase/node \u6CA1\u6709\u5339\u914D\u7684 procedure\uFF1B\u8C03\u7528 gate_status \u91CD\u65B0\u8BFB\u53D6\uFF1B\u82E5\u4ECD\u4E0D\u4E00\u81F4\uFF0C\u56DE discuss \u4FEE\u590D Gate \u72B6\u6001\u3002";
234
+ }
235
+ if (["plan", "implement", "fix-bug", "reviewing-code", "reviewing-proposal", "reviewing-verify"].includes(variant)) {
236
+ text = [text, DISPATCH_RULE].filter(Boolean).join("\n\n");
237
+ }
238
+ if (variant === "plan" || variant === "implement") {
239
+ text = [text, `## \u5F53\u524D\u8F6E\u6B21
240
+ ${roundsText(state, reviewLimit)}`].filter(Boolean).join("\n\n");
241
+ }
242
+ return { phase: state.phase, node: state.node, method, text, hasRounds: variant === "plan" || variant === "implement" };
243
+ }
244
+ function procedureFailure(state, reviewLimit, fields) {
245
+ const procedure = procedureFor(state, { reviewLimit });
246
+ return { ok: false, ...fields, procedure, method: procedure.method };
247
+ }
248
+ function roundNumbers(state) {
249
+ const inPlan = state.node === "plan-review";
250
+ const inCode = state.node === "code-review";
251
+ return {
252
+ inPlan,
253
+ inCode,
254
+ planRound: (state.rounds?.planReview ?? 0) + (inPlan ? 1 : 0),
255
+ codeRound: (state.rounds?.codeReview ?? 0) + (inCode ? 1 : 0)
256
+ };
257
+ }
258
+ function roundsText(state, reviewLimit) {
259
+ const { inPlan, inCode, planRound, codeRound } = roundNumbers(state);
260
+ return `plan ${planRound}/${reviewLimit}${inPlan ? "\uFF08\u5F53\u524D\uFF09" : ""}, code ${codeRound}/${reviewLimit}${inCode ? "\uFF08\u5F53\u524D\uFF09" : ""}`;
261
+ }
262
+ function statusText(state, reviewLimit = DEFAULT_REVIEW_LIMIT, { includeRounds = true } = {}) {
263
+ const { inPlan, inCode } = roundNumbers(state);
264
+ const lines = [
265
+ `\u72B6\u6001\uFF1A${state.phase}`,
266
+ `\u8282\u70B9\uFF1A${state.node ?? "\u65E0"}`,
267
+ `\u4E8B\u9879\uFF1A${state.task ?? "\u65E0"}`,
268
+ ...state.phase === "blocked" ? [`\u89E3\u51BB\u540E\uFF1A${state.blockedFrom?.phase ?? "discuss"}${state.blockedFrom?.node === null || state.blockedFrom?.node === void 0 ? "" : ` / ${state.blockedFrom.node}`}`] : [],
269
+ `work\uFF1A${state.work}`,
270
+ `blockReason\uFF1A${state.blockReason ?? "\u65E0"}`,
271
+ `planReady\uFF1A${state.planReady ? "\u662F" : "\u5426"}`,
272
+ ...includeRounds ? [`rounds\uFF1A${roundsText(state, reviewLimit)}`] : []
273
+ ];
274
+ if (state.notePaths?.length > 0 || state.noteDecision === "mechanical") {
275
+ lines.push(`notes\uFF1A${state.noteDecision ?? "\u65E0"} ${state.notePaths?.length ?? 0} \u7BC7\uFF0Cverified=${state.noteVerified === true ? "\u662F" : "\u5426"}`);
276
+ }
277
+ if (state.exhausted?.plan === true || state.exhausted?.code === true) {
278
+ lines.push(`exhausted\uFF1Aplan ${state.exhausted.plan ? "\u662F" : "\u5426"}, code ${state.exhausted.code ? "\u662F" : "\u5426"}`);
279
+ }
280
+ if (state.verification !== null) {
281
+ lines.push(`verify\uFF1A${state.verification.passed === true ? "PASS" : "FAIL"}\uFF08${state.verification.selfReported === true ? "self-reported" : "verified"}\uFF09`);
282
+ }
283
+ return lines.join("\n");
284
+ }
285
+ function procedureBlock(procedure) {
286
+ return `\u5F53\u524D phase/node\uFF1A${procedure.phase} / ${procedure.node ?? "\u65E0"}
287
+ \u65B9\u6CD5\uFF1A${procedure.method}
288
+
289
+ ${procedure.text}`;
290
+ }
291
+ function procedureResultText(procedure, trailing) {
292
+ return [procedureBlock(procedure), trailing].filter(Boolean).join("\n\n---\n\n");
293
+ }
294
+ function renderProcedureResult(value) {
295
+ const { procedure, ...rest } = value ?? {};
296
+ const trailing = JSON.stringify(rest);
297
+ return [{ type: "text", text: procedure !== null && procedure !== void 0 ? procedureResultText(procedure, trailing) : trailing }];
298
+ }
299
+
300
+ // src/state.ts
301
+ var PHASES = ["discuss", "orchestrating", "reviewing", "docs", "blocked", "complete"];
302
+ var states = /* @__PURE__ */ new WeakMap();
303
+ var idStates = /* @__PURE__ */ new Map();
304
+ function idKey(root, agent) {
305
+ return `${root}\0${String(agent?.id ?? "anonymous")}`;
306
+ }
307
+ function clone(value) {
308
+ return JSON.parse(JSON.stringify(value));
309
+ }
310
+ function defaultState() {
311
+ return {
312
+ phase: "discuss",
313
+ node: null,
314
+ task: null,
315
+ work: "active",
316
+ blockReason: null,
317
+ rounds: { planReview: 0, codeReview: 0 },
318
+ exhausted: { plan: false, code: false },
319
+ planReady: false,
320
+ verification: null,
321
+ blockedFrom: null,
322
+ notePaths: [],
323
+ noteDecision: null,
324
+ noteSearch: null,
325
+ noteVerified: false,
326
+ noteFinalized: false,
327
+ noteFinalizeRecovery: [],
328
+ pending: {},
329
+ updatedAt: null
330
+ };
331
+ }
332
+ function textOrNull(value) {
333
+ return typeof value === "string" ? value : null;
334
+ }
335
+ function normalizeState(value) {
336
+ const base = defaultState();
337
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return base;
338
+ const source = value;
339
+ const phase = PHASES.includes(source.phase) ? source.phase : "discuss";
340
+ const blockedFrom = source.blockedFrom !== null && typeof source.blockedFrom === "object" && !Array.isArray(source.blockedFrom) ? { phase: textOrNull(source.blockedFrom.phase), node: textOrNull(source.blockedFrom.node) } : null;
341
+ return {
342
+ phase,
343
+ node: textOrNull(source.node),
344
+ task: textOrNull(source.task),
345
+ work: textOrNull(source.work) ?? "active",
346
+ blockReason: textOrNull(source.blockReason),
347
+ rounds: {
348
+ planReview: Number(source.rounds?.planReview) || 0,
349
+ codeReview: Number(source.rounds?.codeReview) || 0
350
+ },
351
+ exhausted: {
352
+ plan: source.exhausted?.plan === true,
353
+ code: source.exhausted?.code === true
354
+ },
355
+ planReady: source.planReady === true,
356
+ verification: source.verification !== null && typeof source.verification === "object" && !Array.isArray(source.verification) ? clone(source.verification) : null,
357
+ blockedFrom,
358
+ notePaths: Array.isArray(source.notePaths) ? source.notePaths.map((entry) => entry !== null && typeof entry === "object" && !Array.isArray(entry) ? { ...entry } : entry) : [],
359
+ noteDecision: textOrNull(source.noteDecision),
360
+ noteSearch: textOrNull(source.noteSearch),
361
+ noteVerified: source.noteVerified === true,
362
+ noteFinalized: source.noteFinalized === true,
363
+ noteFinalizeRecovery: Array.isArray(source.noteFinalizeRecovery) ? source.noteFinalizeRecovery : [],
364
+ pending: source.pending !== null && typeof source.pending === "object" && !Array.isArray(source.pending) ? source.pending : {},
365
+ updatedAt: textOrNull(source.updatedAt)
366
+ };
367
+ }
368
+ function loadState(root, agent) {
369
+ const session = agent?.session;
370
+ if (session !== null && typeof session === "object" && states.has(session)) {
371
+ return clone(states.get(session));
372
+ }
373
+ return clone(idStates.get(idKey(root, agent)) ?? defaultState());
374
+ }
375
+ function saveState(root, agent, state) {
376
+ const next = normalizeState({ ...state, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
377
+ const session = agent?.session;
378
+ if (session !== null && typeof session === "object") states.set(session, clone(next));
379
+ idStates.set(idKey(root, agent), clone(next));
380
+ return clone(next);
381
+ }
382
+
383
+ // src/notes/note-files.ts
384
+ import { existsSync as existsSync2, lstatSync as lstatSync2, mkdirSync as mkdirSync2, readdirSync, readFileSync, renameSync, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
385
+ import path4 from "node:path";
386
+
387
+ // src/notes/fs.ts
388
+ import { existsSync, lstatSync } from "node:fs";
389
+ import path2 from "node:path";
390
+
391
+ // src/paths.ts
392
+ import path from "node:path";
393
+ import { fileURLToPath } from "node:url";
394
+ var PACKAGE_ROOT = fileURLToPath(new URL("..", import.meta.url));
395
+ var SKILLS = path.join(PACKAGE_ROOT, "skills");
396
+ var NOTES_SKILL_DIR = path.join(SKILLS, "gate-write-agent-notes");
397
+ var NOTES_LAUNCHER_TEMPLATE = path.join(PACKAGE_ROOT, "launchers", "verify-notes.mjs");
398
+ var NOTES_CI_TEMPLATE = path.join(PACKAGE_ROOT, "launchers", "verify-notes.yml");
399
+ var NOTES_CI_SCRIPTS = [
400
+ "agent-note-tree.ts",
401
+ "verify-agent-note-tree.ts",
402
+ "verify-agent-note-format.ts",
403
+ "verify-agent-note-lifecycle.ts",
404
+ "verify-archived-agent-notes.ts"
405
+ ];
406
+
407
+ // src/notes/fs.ts
408
+ function isInside(root, target) {
409
+ const relative = path2.relative(root, target);
410
+ return relative === "" || !relative.startsWith(`..${path2.sep}`) && relative !== ".." && !path2.isAbsolute(relative);
411
+ }
412
+ function resolveNotesSkill() {
413
+ const skill = path2.join(NOTES_SKILL_DIR, "SKILL.md");
414
+ if (!existsSync(skill)) return void 0;
415
+ const template = path2.join(NOTES_SKILL_DIR, "templates", "proposed.md");
416
+ return { skill, template: existsSync(template) ? template : void 0 };
417
+ }
418
+ function resolveNodeBinary() {
419
+ if (!process.versions?.electron) return process.execPath;
420
+ const configured = process.env.DSH_DESKTOP_NODE_EXECUTABLE;
421
+ if (typeof configured === "string" && configured !== "" && existsSync(configured)) return configured;
422
+ if (typeof process.resourcesPath === "string" && process.resourcesPath !== "") {
423
+ const bin = path2.join(process.resourcesPath, "runtime", "primary-runtime", "dependencies", "node", "bin", process.platform === "win32" ? "node.exe" : "node");
424
+ if (existsSync(bin)) return bin;
425
+ }
426
+ return void 0;
427
+ }
428
+ function pathHasSymlink(base, absolute) {
429
+ const relative = path2.relative(base, absolute);
430
+ if (relative === "" || relative.startsWith("..")) return true;
431
+ let current = base;
432
+ if (existsSync(current) && lstatSync(current).isSymbolicLink()) return true;
433
+ for (const part of relative.split(path2.sep)) {
434
+ if (part === "") continue;
435
+ current = path2.join(current, part);
436
+ if (existsSync(current) && lstatSync(current).isSymbolicLink()) return true;
437
+ }
438
+ return false;
439
+ }
440
+
441
+ // src/notes/scripts.ts
442
+ import { execFileSync } from "node:child_process";
443
+ import { mkdirSync, writeFileSync } from "node:fs";
444
+ import path3 from "node:path";
445
+ import { randomUUID } from "node:crypto";
446
+ import { rmSync } from "node:fs";
447
+ function runNotesScripts(root) {
448
+ const node = resolveNodeBinary();
449
+ if (node === void 0) return { ok: false, code: "notes-runtime-unavailable", results: [] };
450
+ const notes = path3.join(root, ".agents", "notes");
451
+ const scripts = [
452
+ "verify-agent-note-tree.ts",
453
+ "verify-agent-note-format.ts",
454
+ "verify-agent-note-lifecycle.ts",
455
+ "verify-archived-agent-notes.ts"
456
+ ];
457
+ const results = [];
458
+ for (const name of scripts) {
459
+ const script = path3.join(NOTES_SKILL_DIR, "scripts", name);
460
+ try {
461
+ const output = execFileSync(node, [script, "--root", notes], { cwd: root, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
462
+ results.push({ name, ok: true, output: String(output).trim() });
463
+ } catch (error) {
464
+ return {
465
+ ok: false,
466
+ code: "note-verification-failed",
467
+ results: [...results, { name, ok: false, output: String(error?.stdout ?? "").trim(), error: String(error?.stderr ?? error?.message ?? error).trim() }]
468
+ };
469
+ }
470
+ }
471
+ return { ok: true, results };
472
+ }
473
+ function runFormatOnNote(root, entry, content) {
474
+ const node = resolveNodeBinary();
475
+ if (node === void 0) return { code: "notes-runtime-unavailable", reason: "\u65E0\u6CD5\u5B9A\u4F4D Node \u53EF\u6267\u884C\u6587\u4EF6\u3002" };
476
+ const temp = path3.join(root, ".agents", "notes", `.gate-validate-${randomUUID()}`);
477
+ const relative = path3.join(String(entry.lifecycle ?? "proposed"), String(entry.class ?? "feature"), path3.basename(String(entry.path)));
478
+ try {
479
+ mkdirSync(path3.dirname(path3.join(temp, relative)), { recursive: true });
480
+ writeFileSync(path3.join(temp, relative), content);
481
+ execFileSync(node, [path3.join(NOTES_SKILL_DIR, "scripts", "verify-agent-note-format.ts"), "--root", temp], { cwd: root, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
482
+ return void 0;
483
+ } catch (error) {
484
+ return { code: "note-format-invalid", path: entry.path, reason: String(error?.stderr ?? error?.message ?? error).trim() };
485
+ } finally {
486
+ rmSync(temp, { recursive: true, force: true });
487
+ }
488
+ }
489
+
490
+ // src/notes/note-files.ts
491
+ function splitNoteSections(content) {
492
+ const lines = content.replace(/\r\n?/gu, "\n").split("\n");
493
+ const preamble = [];
494
+ const sections = [];
495
+ let current = null;
496
+ let fenced = false;
497
+ for (const line of lines) {
498
+ if (/^\s*```/u.test(line)) fenced = !fenced;
499
+ if (!fenced && /^## /u.test(line)) {
500
+ current = { heading: line, body: [] };
501
+ sections.push(current);
502
+ } else if (current === null) {
503
+ preamble.push(line);
504
+ } else {
505
+ current.body.push(line);
506
+ }
507
+ }
508
+ return { preamble, sections };
509
+ }
510
+ function transformImplemented(content) {
511
+ const { preamble, sections } = splitNoteSections(content);
512
+ const statusIndex = preamble.findIndex((line) => /^Status:/u.test(line));
513
+ if (statusIndex >= 0) preamble[statusIndex] = "Status: implemented";
514
+ const kept = [];
515
+ const extras = [];
516
+ let hasDecision = false;
517
+ let consequences = null;
518
+ for (const section of sections) {
519
+ if (/^## Proposal\s*$/u.test(section.heading)) {
520
+ section.heading = "## Decision";
521
+ hasDecision = true;
522
+ }
523
+ if (/^## Plan\s*$/u.test(section.heading)) continue;
524
+ if (/^## Acceptance criteria\s*$/u.test(section.heading)) {
525
+ extras.push({ heading: "### Acceptance criteria", body: section.body.join("\n").trim() });
526
+ continue;
527
+ }
528
+ if (/^## Risks\s*$/u.test(section.heading)) {
529
+ extras.push({ heading: "### Risks", body: section.body.join("\n").trim() });
530
+ continue;
531
+ }
532
+ if (/^## Consequences\s*$/u.test(section.heading)) consequences = section;
533
+ kept.push(section);
534
+ }
535
+ if (!hasDecision) throw new Error("Note \u7F3A\u5C11 ## Proposal\uFF0C\u65E0\u6CD5\u8F6C\u6210 ## Decision");
536
+ const extraText = extras.filter((item) => item.body !== "").map((item) => `${item.heading}
537
+
538
+ ${item.body}`).join("\n\n");
539
+ if (extraText !== "") {
540
+ if (consequences === null) {
541
+ consequences = { heading: "## Consequences", body: [] };
542
+ kept.push(consequences);
543
+ }
544
+ consequences.body.push("", extraText);
545
+ }
546
+ const out = [...preamble, ...kept.flatMap((section) => [section.heading, ...section.body])];
547
+ return `${out.join("\n").replace(/\s+$/u, "")}
548
+ `;
549
+ }
550
+ function replaceSectionBody(content, heading, body) {
551
+ const { preamble, sections } = splitNoteSections(content);
552
+ const target = sections.find((section) => section.heading === heading);
553
+ if (target === void 0) throw new Error(`\u7F3A\u5C11 ${heading}`);
554
+ target.body = ["", body];
555
+ return `${[...preamble, ...sections.flatMap((section) => [section.heading, ...section.body])].join("\n").replace(/\s+$/u, "")}
556
+ `;
557
+ }
558
+ function recoverFinalize(root, sessionId) {
559
+ const journal = path4.join(root, ".agents", "notes", `.gate-finalize-${sessionId}.json`);
560
+ if (!existsSync2(journal)) return [];
561
+ let data;
562
+ try {
563
+ data = JSON.parse(readFileSync(journal, "utf8"));
564
+ } catch {
565
+ return [];
566
+ }
567
+ if (data?.owner !== sessionId) return [];
568
+ const recovered = [];
569
+ const source = path4.join(root, data.source);
570
+ const backup = path4.join(root, data.backup);
571
+ const target = path4.join(root, data.target);
572
+ try {
573
+ if (existsSync2(backup)) {
574
+ if (!existsSync2(source) && !existsSync2(target)) {
575
+ renameSync(backup, source);
576
+ recovered.push("restored-source");
577
+ } else if (!existsSync2(source) && existsSync2(target)) {
578
+ const check = runNotesScripts(root);
579
+ if (!check.ok) {
580
+ rmSync2(target, { force: true });
581
+ renameSync(backup, source);
582
+ recovered.push("restored-source-after-bad-target");
583
+ } else {
584
+ rmSync2(backup, { force: true });
585
+ recovered.push("kept-target");
586
+ }
587
+ } else if (existsSync2(source) && !existsSync2(target)) {
588
+ rmSync2(backup, { force: true });
589
+ recovered.push("removed-backup");
590
+ }
591
+ }
592
+ const temp = typeof data.temp === "string" ? path4.join(root, data.temp) : void 0;
593
+ if (!existsSync2(backup) && (temp === void 0 || !existsSync2(temp))) recovered.push("journal-orphan");
594
+ rmSync2(journal, { force: true });
595
+ } catch {
596
+ }
597
+ return recovered;
598
+ }
599
+ function finalizeNote(root, entry, sessionId, decisionBody) {
600
+ const sourceRelative = entry.path.replaceAll("\\", "/");
601
+ const source = path4.join(root, sourceRelative);
602
+ const targetRelative = sourceRelative.replace(".agents/notes/proposed/", ".agents/notes/implemented/");
603
+ const target = path4.join(root, targetRelative);
604
+ if (existsSync2(target)) throw new Error(`\u76EE\u6807\u5DF2\u5B58\u5728\uFF1A${targetRelative}`);
605
+ if (pathHasSymlink(root, source) || pathHasSymlink(root, target)) throw new Error("finalize \u8DEF\u5F84\u542B symlink");
606
+ const content = readFileSync(source, "utf8");
607
+ let next = transformImplemented(content);
608
+ if (typeof decisionBody === "string" && decisionBody !== "") next = replaceSectionBody(next, "## Decision", decisionBody);
609
+ const sourceFromNotes = sourceRelative.replace(/^\.agents\/notes\//u, "");
610
+ const targetFromNotes = targetRelative.replace(/^\.agents\/notes\//u, "");
611
+ next = next.replaceAll(sourceRelative, targetRelative).replaceAll(sourceFromNotes, targetFromNotes);
612
+ const dir = path4.dirname(target);
613
+ mkdirSync2(dir, { recursive: true });
614
+ const journalRelative = path4.join(".agents", "notes", `.gate-finalize-${sessionId}.json`).replaceAll("\\", "/");
615
+ const journal = path4.join(root, journalRelative);
616
+ const backup = `${source}.gate-backup`;
617
+ const temp = path4.join(dir, `.gate-finalize-${path4.basename(target)}.tmp`);
618
+ writeFileSync2(journal, JSON.stringify({ owner: sessionId, source: sourceRelative, target: targetRelative, backup: path4.relative(root, backup).replaceAll("\\", "/"), temp: path4.relative(root, temp).replaceAll("\\", "/") }, null, 2));
619
+ try {
620
+ writeFileSync2(temp, next);
621
+ renameSync(source, backup);
622
+ renameSync(temp, target);
623
+ const check = runNotesScripts(root);
624
+ if (!check.ok) throw new Error(`finalize \u540E\u7684 notes \u6821\u9A8C\u5931\u8D25\uFF1A${JSON.stringify(check.results)}`);
625
+ rmSync2(backup, { force: true });
626
+ rmSync2(journal, { force: true });
627
+ return targetRelative.replaceAll("\\", "/");
628
+ } catch (error) {
629
+ rmSync2(target, { force: true });
630
+ rmSync2(temp, { force: true });
631
+ if (existsSync2(backup) && !existsSync2(source)) renameSync(backup, source);
632
+ rmSync2(journal, { force: true });
633
+ throw error;
634
+ }
635
+ }
636
+ var NOTE_CLASSES = /* @__PURE__ */ new Set(["feature", "bug-fix", "simplification", "architecture", "process", "testing"]);
637
+ function noteTopic(file) {
638
+ return path4.basename(file).replace(/^\d{4}-\d{2}-\d{2}-/u, "").replace(/\.md$/u, "").replace(/-implemented$/u, "").toLowerCase().replace(/[^a-z0-9]+/gu, "-").replace(/^-|-$/gu, "");
639
+ }
640
+ function normalizeNotePaths(value) {
641
+ if (!Array.isArray(value)) return value;
642
+ return value.map((entry) => {
643
+ if (entry === null || typeof entry !== "object" || typeof entry.path !== "string") return entry;
644
+ const normalized = entry.path.replaceAll("\\", "/").replace(/^\.\//u, "");
645
+ if (normalized.startsWith(".agents/notes/")) return { ...entry, path: normalized };
646
+ const parts = normalized.split("/");
647
+ if (parts[0] === "proposed" || parts[0] === "implemented") return { ...entry, path: `.agents/notes/${normalized}` };
648
+ if (typeof entry.class === "string" && parts[0] === entry.class && typeof entry.lifecycle === "string") {
649
+ return { ...entry, path: `.agents/notes/${entry.lifecycle}/${normalized}` };
650
+ }
651
+ return { ...entry, path: normalized };
652
+ });
653
+ }
654
+ function notePathsForDecision(value, decision) {
655
+ return decision === "mechanical" ? value : normalizeNotePaths(value);
656
+ }
657
+ function validateNotePaths(root, value, decision) {
658
+ if (decision === "mechanical") {
659
+ if (!Array.isArray(value) || value.length !== 0) return { code: "note-paths-required", reason: "mechanical \u7684 notePaths \u5FC5\u987B\u4E3A\u7A7A\u3002" };
660
+ return void 0;
661
+ }
662
+ if (!Array.isArray(value) || value.length === 0) return { code: "note-paths-required", reason: "\u975E mechanical \u5DE5\u4F5C\u9879\u81F3\u5C11\u9700\u8981\u4E00\u4E2A notePath\u3002" };
663
+ const seenTopics = /* @__PURE__ */ new Set();
664
+ for (const entry of value) {
665
+ if (entry === null || typeof entry !== "object" || typeof entry.path !== "string" || typeof entry.class !== "string" || typeof entry.lifecycle !== "string") {
666
+ return { code: "invalid-note-path", reason: "notePaths[] \u9700\u8981 {path,class,lifecycle}\u3002" };
667
+ }
668
+ if (!NOTE_CLASSES.has(entry.class)) return { code: "invalid-note-class", class: entry.class };
669
+ if (!["proposed", "implemented"].includes(entry.lifecycle)) return { code: "invalid-note-lifecycle", lifecycle: entry.lifecycle };
670
+ if (decision === "new" && entry.lifecycle !== "proposed") return { code: "note-decision-mismatch", reason: "new \u53EA\u80FD proposed\u3002" };
671
+ if (decision === "update" && entry.lifecycle !== "proposed") return { code: "note-decision-mismatch", reason: "update \u53EA\u80FD proposed\u3002" };
672
+ if (decision === "in-place" && entry.lifecycle !== "implemented") return { code: "note-decision-mismatch", reason: "in-place \u53EA\u80FD implemented\u3002" };
673
+ const normalized = entry.path.replaceAll("\\", "/");
674
+ if (normalized.startsWith("/") || normalized.includes("..")) return { code: "invalid-note-path", path: entry.path };
675
+ const parts = normalized.split("/");
676
+ if (parts.length !== 5 || parts[0] !== ".agents" || parts[1] !== "notes" || parts[2] !== entry.lifecycle || parts[3] !== entry.class) {
677
+ return { code: "invalid-note-path", path: entry.path, reason: "\u9700\u8981 .agents/notes/<lifecycle>/<class>/yyyy-mm-dd-topic.md\u3002" };
678
+ }
679
+ const filePart = parts[4];
680
+ if (!/^\d{4}-\d{2}-\d{2}-.+\.md$/u.test(String(filePart))) return { code: "invalid-note-path", path: entry.path, reason: "\u6587\u4EF6\u540D\u9700\u8981 yyyy-mm-dd-topic.md\u3002" };
681
+ const absolute = path4.join(root, ...parts);
682
+ if (!isInside(root, absolute) || pathHasSymlink(root, absolute)) return { code: "invalid-note-path", path: entry.path, reason: "\u8DEF\u5F84\u8D8A\u754C\u6216\u542B symlink\u3002" };
683
+ if (!existsSync2(absolute) || !lstatSync2(absolute).isFile()) return { code: "note-missing", path: entry.path };
684
+ const content = readFileSync(absolute, "utf8");
685
+ const status = content.match(/^Status:\s*(proposed|implemented)\s*$/mu)?.[1];
686
+ if (status !== entry.lifecycle) return { code: "note-status-mismatch", path: entry.path, expected: entry.lifecycle, got: status ?? "missing" };
687
+ const format = runFormatOnNote(root, entry, content);
688
+ if (format !== void 0) return format;
689
+ const topic = noteTopic(String(filePart));
690
+ if (seenTopics.has(topic)) return { code: "note-topic-duplicate", topic, reason: "\u540C\u4E00 topic \u53EA\u80FD\u6709\u4E00\u7BC7\u3002" };
691
+ seenTopics.add(topic);
692
+ for (const other of ["proposed", "implemented", "rejected"]) {
693
+ const dir = path4.join(root, ".agents", "notes", other, entry.class);
694
+ if (!existsSync2(dir)) continue;
695
+ for (const name of readdirSync(dir)) {
696
+ if (!name.endsWith(".md") || name === path4.basename(String(filePart))) continue;
697
+ if (noteTopic(name) === topic) return { code: "note-topic-duplicate", topic, other: path4.join(dir, name) };
698
+ }
699
+ }
700
+ }
701
+ return void 0;
702
+ }
703
+
704
+ // src/runtime/store.ts
705
+ function applyCommandEvent(state, name, args) {
706
+ const text = String(args ?? "").trim();
707
+ switch (name) {
708
+ case "gate-enter-plan":
709
+ return {
710
+ ...state,
711
+ phase: "orchestrating",
712
+ node: "plan",
713
+ task: null,
714
+ work: "active",
715
+ blockReason: null,
716
+ blockedFrom: null,
717
+ rounds: { planReview: 0, codeReview: 0 },
718
+ exhausted: { plan: false, code: false },
719
+ planReady: false,
720
+ verification: null,
721
+ notePaths: [],
722
+ noteDecision: null,
723
+ noteSearch: null,
724
+ noteVerified: false,
725
+ noteFinalizeRecovery: []
726
+ };
727
+ case "gate-enter-implement":
728
+ return {
729
+ ...state,
730
+ phase: "orchestrating",
731
+ node: "implement",
732
+ work: "active",
733
+ task: state.planReady !== true && text !== "" ? text : state.task,
734
+ blockReason: null,
735
+ blockedFrom: null,
736
+ verification: null,
737
+ noteVerified: false,
738
+ noteFinalizeRecovery: []
739
+ };
740
+ case "gate-fix-bug":
741
+ return { ...state, phase: "orchestrating", task: text === "" ? state.task ?? "\u5F53\u524D\u4E0A\u4E0B\u6587\u4E2D\u7684\u7F3A\u9677" : text, node: "fix-bug", work: "active", blockReason: null, blockedFrom: null };
742
+ case "gate-docs":
743
+ return { ...state, phase: "docs", task: text, node: null, work: "active", blockReason: null, blockedFrom: null };
744
+ case "gate-implement-review":
745
+ return { ...state, phase: "reviewing", task: text, node: "code-review-standalone", work: "active", blockReason: null, blockedFrom: null };
746
+ case "gate-code-verify":
747
+ return { ...state, phase: "reviewing", task: text, node: "verify-standalone", work: "active", blockReason: null, blockedFrom: null };
748
+ case "gate-propose-review":
749
+ return { ...state, phase: "reviewing", task: text, node: "proposal-review", work: "active", blockReason: null, blockedFrom: null };
750
+ case "gate-block":
751
+ return {
752
+ ...state,
753
+ blockedFrom: { phase: state.phase, node: state.node },
754
+ phase: "blocked",
755
+ work: "blocked",
756
+ node: null,
757
+ blockReason: text === "" ? "user-blocked" : text
758
+ };
759
+ case "gate-resume":
760
+ return {
761
+ ...state,
762
+ phase: state.blockedFrom?.phase ?? "discuss",
763
+ node: state.blockedFrom?.node ?? null,
764
+ work: "active",
765
+ blockReason: null,
766
+ blockedFrom: null
767
+ };
768
+ case "gate-return-discuss":
769
+ return { ...state, phase: "discuss", node: null, work: "active", blockReason: null, blockedFrom: null };
770
+ default:
771
+ return state;
772
+ }
773
+ }
774
+ function applyProjectionEvent(state, event) {
775
+ if (event.type === "command/run" && typeof event.data?.name === "string" && event.data.name.startsWith("gate-")) {
776
+ return {
777
+ ...state,
778
+ pending: { ...state.pending ?? {}, [event.data.commandId]: { name: event.data.name, args: event.data.args ?? "" } }
779
+ };
780
+ }
781
+ if (event.type === "command/done") {
782
+ const pending = state.pending?.[event.data?.commandId];
783
+ if (pending === void 0) return state;
784
+ const next = { ...state, pending: { ...state.pending } };
785
+ delete next.pending[event.data.commandId];
786
+ if (event.data.kind !== "success") return next;
787
+ return applyCommandEvent(next, pending.name, pending.args);
788
+ }
789
+ if (event.type === "tool/result") {
790
+ const snapshot = event.data?.meta?.gate?.state;
791
+ if (snapshot !== void 0) return normalizeState(snapshot);
792
+ }
793
+ return state;
794
+ }
795
+ function createStateRuntime(ctx) {
796
+ let handle;
797
+ ctx.inject(["sessionProjections"], async (scope) => {
798
+ try {
799
+ handle = await scope.sessionProjections.register({
800
+ key: "gate",
801
+ stateSchema: { parse: normalizeState },
802
+ init: () => defaultState(),
803
+ apply: applyProjectionEvent,
804
+ stateVersion: 1
805
+ });
806
+ } catch {
807
+ handle = void 0;
808
+ }
809
+ });
810
+ return {
811
+ read(root, agent) {
812
+ if (handle !== void 0 && agent?.session !== void 0) {
813
+ try {
814
+ return normalizeState(handle.stateOf(agent.session));
815
+ } catch {
816
+ }
817
+ }
818
+ return loadState(root, agent);
819
+ },
820
+ write(root, agent, state) {
821
+ if (handle !== void 0) return state;
822
+ return saveState(root, agent, state);
823
+ },
824
+ get source() {
825
+ return handle === void 0 ? "memory" : "projection";
826
+ },
827
+ get projection() {
828
+ return handle;
829
+ }
830
+ };
831
+ }
832
+
833
+ // src/runtime/gate.ts
834
+ import { randomUUID as randomUUID2 } from "node:crypto";
835
+
836
+ // src/runtime/session.ts
837
+ import path5 from "node:path";
838
+ function workspaceBoundary(ctx, agent) {
839
+ try {
840
+ const policy = ctx.get("sandboxPolicy");
841
+ const resolved = policy?.resolve?.({ session: agent?.session });
842
+ if (resolved?.workspaceRoot !== void 0) return resolved.workspaceRoot;
843
+ } catch {
844
+ }
845
+ return agent?.session?.header?.cwd;
846
+ }
847
+ function resolveGate(ctx, agent) {
848
+ const workspace = workspaceBoundary(ctx, agent);
849
+ const root = workspace === void 0 ? void 0 : path5.resolve(workspace);
850
+ if (root === void 0) return { workspace, root: void 0 };
851
+ return { workspace: root, root };
852
+ }
853
+ function sessionCwd(ctx, agent, fallback) {
854
+ try {
855
+ const cwd = ctx.get("workingDirectory")?.get?.(agent?.session);
856
+ if (typeof cwd === "string" && cwd !== "") return cwd;
857
+ } catch {
858
+ }
859
+ return fallback;
860
+ }
861
+ function rootAgentOf(ctx, agent) {
862
+ const header = agent?.session?.header;
863
+ const delegated = header?.origin === "subagent" && typeof header.parentSession === "string";
864
+ try {
865
+ const root = ctx.get("subagents")?.rootOf?.(agent);
866
+ if (root !== void 0 && root.id !== agent?.id) return root;
867
+ } catch {
868
+ }
869
+ if (delegated) {
870
+ try {
871
+ const live = ctx.get("agents")?.get?.(header.parentSession);
872
+ if (live !== void 0) return live;
873
+ } catch {
874
+ }
875
+ return { id: header.parentSession };
876
+ }
877
+ return agent;
878
+ }
879
+ function effectiveState(ctx, agent, gate, store) {
880
+ const root = rootAgentOf(ctx, agent);
881
+ return { root, state: store.read(gate.root, root), worker: root?.id !== agent?.id };
882
+ }
883
+
884
+ // src/runtime/gate.ts
885
+ function isMechanical(task) {
886
+ return /^mechanical:/iu.test(String(task ?? "").trim());
887
+ }
888
+ function enterPhase(ctx, agent, node, reviewLimit, store, current) {
889
+ const gate = resolveGate(ctx, agent);
890
+ const state = current ?? store.read(gate.root, agent);
891
+ if (state.phase !== "discuss" && state.phase !== "complete") return { kind: "error", text: `\u5F53\u524D phase \u662F ${state.phase}` };
892
+ if (state.work === "blocked" || state.blockReason !== null) {
893
+ return { kind: "error", text: `\u5F53\u524D\u88AB blocked\uFF1A${state.blockReason ?? "unknown"}\uFF1B\u5148 /gate-resume` };
894
+ }
895
+ if (node === "plan") {
896
+ const notes = resolveNotesSkill();
897
+ if (notes === void 0) return { kind: "error", code: "notes-builtin-missing", text: "\u5185\u7F6E write-agent-notes \u7F3A\u5931\uFF1B\u91CD\u88C5\u63D2\u4EF6\u3002" };
898
+ }
899
+ if (node === "implement") {
900
+ if (typeof state.task !== "string" || state.task === "") return { kind: "error", text: "\u6CA1\u6709\u53EF\u5B9E\u65BD\u7684\u5185\u5BB9\uFF1A\u5148\u7528\u4E00\u53E5\u8BDD\u8BF4\u660E\u8981\u6539\u4EC0\u4E48" };
901
+ if (state.exhausted?.plan === true || state.exhausted?.code === true) {
902
+ return { kind: "error", text: "review \u8F6E\u6B21\u5DF2\u8017\u5C3D\uFF1B\u9700\u8981\u7528\u6237\u51B3\u5B9A\u540E\u91CD\u65B0 /gate-enter-plan" };
903
+ }
904
+ }
905
+ state.phase = "orchestrating";
906
+ state.node = node;
907
+ state.work = "active";
908
+ state.blockReason = null;
909
+ state.blockedFrom = null;
910
+ if (node === "plan") {
911
+ state.rounds = { planReview: 0, codeReview: 0 };
912
+ state.exhausted = { plan: false, code: false };
913
+ state.task = null;
914
+ state.planReady = false;
915
+ state.verification = null;
916
+ state.notePaths = [];
917
+ state.noteDecision = null;
918
+ state.noteSearch = null;
919
+ state.noteVerified = false;
920
+ state.noteFinalized = false;
921
+ state.noteFinalizeRecovery = [];
922
+ }
923
+ if (node === "implement") {
924
+ state.verification = null;
925
+ state.noteVerified = false;
926
+ state.noteFinalized = false;
927
+ state.noteFinalizeRecovery = [];
928
+ }
929
+ store.write(gate.root, agent, state);
930
+ return { gate, state, reviewLimit };
931
+ }
932
+ function wakeGateAgent(invocation, text, command) {
933
+ const followup = invocation?.agent?.followup;
934
+ if (typeof followup !== "function") return;
935
+ try {
936
+ followup.call(invocation.agent, {
937
+ id: randomUUID2(),
938
+ role: "user",
939
+ content: [{ type: "text", text }],
940
+ source: { kind: "gate-command", command }
941
+ });
942
+ } catch {
943
+ }
944
+ }
945
+ function makeWake(reviewLimit) {
946
+ return (invocation, state, text, command) => {
947
+ const procedure = procedureFor(state, { reviewLimit });
948
+ wakeGateAgent(invocation, `${procedureBlock(procedure)}
949
+
950
+ ${text}`, command);
951
+ };
952
+ }
953
+
954
+ // src/runtime/workers.ts
955
+ function workerProtocol(node) {
956
+ return [
957
+ "## Gate worker",
958
+ "\u4F60\u662F gate \u4E3A\u8BE5\u8282\u70B9\u6D3E\u53D1\u7684 worker\u3002\u53EA\u6267\u884C\u8BE5\u8282\u70B9\uFF1B\u4E0D\u8981\u8C03\u7528 gate_record\uFF0C\u4E0D\u8981\u63A8\u8FDB gate\u3002",
959
+ node === "verify-standalone" ? "\u52A0\u8F7D\u4F7F\u7528 gate-user-verify \u5B8C\u6210\u9A8C\u6536\uFF0C\u5E76\u6309\u8BE5\u6280\u80FD Step 6 \u6C47\u62A5\u3002\u4EFB\u52A1\u4E0E\u6C47\u62A5\u6A21\u677F\u89C1\u8BE5\u6280\u80FD assets/worker-briefs/gate-code-verify.md\u3002\u4E0D\u4FEE\u6539\u4EA7\u54C1\u4EE3\u7801\u3001\u6D4B\u8BD5\u3001\u9700\u6C42\u6216\u8BBE\u8BA1\uFF1B\u53EF\u4EE5\u8FD0\u884C\u6D4B\u8BD5/\u547D\u4EE4\u4F5C\u4E3A\u8BC1\u636E\uFF0C\u4F46\u4E0D\u5F97\u4FEE\u6539\u3001\u653E\u5BBD\u6216\u91CD\u5199\u6D4B\u8BD5\uFF1B\u4E0D\u8981\u81EA\u884C\u63D0\u4EA4\u3002" : node === "fix-bug" ? "\u4E3B\u4EE3\u7406\u5DF2\u8BFB\u53D6 gate-fix-bug\uFF1B\u4F60\u6267\u884C gate-fix-bug \u7684 Process 1-9\uFF08\u590D\u73B0\u3001\u56DE\u5F52\u8986\u76D6\u3001\u6839\u56E0\u3001\u6700\u5C0F\u4FEE\u590D\u3001\u6D4B\u8BD5\uFF09\uFF0C\u8FD4\u56DE\u4FEE\u590D receipt\uFF1B\u4E0D\u8981\u81EA\u884C\u5B8C\u6210 Step 10-12\uFF0C\u4E0D\u8981\u63D0\u4EA4\u3002\u4E0D\u6539\u4EA7\u54C1\u5408\u540C\u3001\u4E0D\u6269\u5927\u8303\u56F4\u3002" : "\u4E3B\u4EE3\u7406\u5DF2\u7ECF\u52A0\u8F7D\u6240\u9700 gate \u6280\u80FD\uFF0C\u5E76\u628A\u5177\u4F53\u4EFB\u52A1\u4E0E\u76F8\u5173\u65B9\u6CD5\u8981\u6C42\u4EA4\u7ED9\u4F60\u3002\u4E0D\u8981\u81EA\u5DF1\u52A0\u8F7D gate \u6280\u80FD\u3002",
960
+ node === "plan" || node === "plan-revise" ? "\u7528 gate_note_write \u5728 .agents/notes/proposed \u4E0B\u521B\u5EFA\u6216\u66F4\u65B0 proposed Agent Note\uFF1B\u539F\u5730\u540C\u6B65\u65E2\u6709 implemented Note \u65F6\u8DEF\u5F84\u5199 implemented/<class>/...\uFF1Bplanning \u4E2D\u901A\u7528 write/edit/shell \u88AB\u9650\u5236\u3002" : ""
961
+ ].filter(Boolean).join("\n\n");
962
+ }
963
+ var REVIEW_NODES = /* @__PURE__ */ new Set(["plan-review", "proposal-review", "code-review", "code-review-standalone"]);
964
+ function reviewAxesError(args) {
965
+ if (!REVIEW_NODES.has(args.node)) return void 0;
966
+ const axes = args.reviewAxes;
967
+ const required = args.node === "plan-review" || args.node === "proposal-review" ? ["standards", "spec", "architecture"] : ["standards", "spec"];
968
+ if (axes === null || typeof axes !== "object" || Array.isArray(axes)) return { code: "review-axes-required", reason: `review \u8282\u70B9\u9700\u8981 ${required.join("\u3001")} \u8F74\u7ED3\u679C\u3002` };
969
+ for (const axis of required) {
970
+ const value = axes[axis];
971
+ if (value === null || typeof value !== "object" || Array.isArray(value) || !Array.isArray(value.findings)) {
972
+ return { code: "review-axes-required", reason: `${axis} \u8F74\u9700\u8981 findings \u6570\u7EC4\u3002` };
973
+ }
974
+ }
975
+ return void 0;
976
+ }
977
+ async function workerEvidenceError(ctx, root, args) {
978
+ if (!WORKER_NODES.has(args.node)) return void 0;
979
+ const ids = Array.isArray(args.workerSessionIds) ? [...new Set(args.workerSessionIds.filter((value) => typeof value === "string" && value !== ""))] : [];
980
+ if (ids.length === 0) return { code: "worker-required", reason: "gate_record \u9700\u8981 workerSessionIds\uFF1B\u6CA1\u6709 worker \u4E0D\u80FD\u63A8\u8FDB\u3002" };
981
+ const service = ctx.get("subagents");
982
+ if (service === void 0 || typeof service.listDescendants !== "function") {
983
+ return { code: "worker-evidence-unavailable", reason: "subagents.listDescendants \u4E0D\u53EF\u7528\uFF0C\u65E0\u6CD5\u9A8C\u8BC1 worker\u3002" };
984
+ }
985
+ let entries;
986
+ try {
987
+ entries = await service.listDescendants(root.id);
988
+ } catch {
989
+ return { code: "worker-evidence-unavailable", reason: "\u8BFB\u53D6 subagent tree \u5931\u8D25\uFF0C\u65E0\u6CD5\u9A8C\u8BC1 worker\u3002" };
990
+ }
991
+ const children = Array.isArray(entries) ? entries.filter((entry) => entry?.kind === "child") : [];
992
+ for (const id of ids) {
993
+ const row = children.find((entry) => String(entry.id) === String(id));
994
+ if (row === void 0) return { code: "worker-not-descendant", workerSessionId: id, reason: "workerSessionId \u4E0D\u5728\u5F53\u524D root \u7684 subagent tree \u4E2D\u3002" };
995
+ }
996
+ return void 0;
997
+ }
998
+ async function bindToolResults(ctx, workerSessionIds, toolResults) {
999
+ const query = ctx.get("sessionQuery");
1000
+ if (query === void 0 || typeof query.readSession !== "function") {
1001
+ return { code: "verification-required", reason: "sessionQuery \u4E0D\u53EF\u7528\uFF0C\u65E0\u6CD5\u7ED1\u5B9A toolResults\u3002" };
1002
+ }
1003
+ const found = /* @__PURE__ */ new Set();
1004
+ for (const sessionId of workerSessionIds) {
1005
+ try {
1006
+ const snapshot = await query.readSession(sessionId);
1007
+ for (const event of snapshot?.events ?? []) {
1008
+ if (event?.type !== "tool/result") continue;
1009
+ const callId = event.data?.message?.toolCallId ?? event.data?.callId;
1010
+ if (callId !== void 0) found.add(String(callId));
1011
+ }
1012
+ } catch {
1013
+ }
1014
+ }
1015
+ const missing = toolResults.filter((id) => !found.has(String(id)));
1016
+ if (missing.length > 0) {
1017
+ return { code: "verification-required", unboundToolResults: missing, reason: "toolResults \u672A\u5728 worker session log \u4E2D\u627E\u5230\u3002" };
1018
+ }
1019
+ return void 0;
1020
+ }
1021
+
1022
+ // src/runtime/prompt.ts
1023
+ function installSystemPrompt(ctx, deps) {
1024
+ ctx.inject(["systemPrompt"], (scope) => {
1025
+ scope.systemPrompt.section({
1026
+ name: "gate:protocol",
1027
+ order: scope.systemPrompt.getSectionOrder("PLAN_POLICY") + 1,
1028
+ text: (context) => {
1029
+ const gate = resolveGate(ctx, context.agent);
1030
+ const effective = effectiveState(ctx, context.agent, gate, deps.store);
1031
+ if (effective.worker) return ["## Gate", "Gate worker\uFF1A\u53EA\u6267\u884C\u5F53\u524D node\uFF0C\u4E0D\u63A8\u8FDB phase\u3002", workerProtocol(effective.state.node ?? "")].filter(Boolean).join("\n\n");
1032
+ return GATE_METHODOLOGY;
1033
+ }
1034
+ });
1035
+ });
1036
+ }
1037
+
1038
+ // src/classify.ts
1039
+ import path6 from "node:path";
1040
+ function classifyPath(root, value, cwd = root) {
1041
+ const absolute = path6.resolve(cwd, String(value ?? ""));
1042
+ const relative = path6.relative(root, absolute).replaceAll("\\", "/");
1043
+ const outside = relative === ".." || relative.startsWith("../") || path6.isAbsolute(relative);
1044
+ return { absolute, relative, outside };
1045
+ }
1046
+ var WRITE_VERB = /(?:^|[\s;(])(Set-Content|Add-Content|Out-File|New-Item|Remove-Item|Move-Item|Copy-Item|Rename-Item|Clear-Content|Set-Item|mkdir|md|touch|tee|rm|mv|cp|sed\s+-i)\b/iu;
1047
+ function pathArgs(text) {
1048
+ const beforePipe = String(text).split("|")[0] ?? "";
1049
+ return [...beforePipe.matchAll(/(?:'([^']+)'|"([^"]+)"|(\S+))/gu)].map((match) => match[1] ?? match[2] ?? match[3]).filter((value) => value !== void 0 && !value.startsWith("-"));
1050
+ }
1051
+ function assignments(text) {
1052
+ const map = /* @__PURE__ */ new Map();
1053
+ for (const match of String(text).matchAll(/(?:^|[;\n])\s*\$?([A-Za-z_]\w*)\s*=\s*(?:'([^']*)'|"([^"]*)"|(\S+))/gu)) {
1054
+ const key = match[1];
1055
+ if (key !== void 0) map.set(key, match[2] ?? match[3] ?? match[4] ?? "");
1056
+ }
1057
+ return map;
1058
+ }
1059
+ function resolveToken(token, map) {
1060
+ const variable = /^\$([A-Za-z_]\w*)$/u.exec(token);
1061
+ if (variable === null) return token;
1062
+ const key = variable[1];
1063
+ return (key === void 0 ? token : map.get(key)) ?? token;
1064
+ }
1065
+ function redirectionTargets(text) {
1066
+ return [...String(text).matchAll(/(?:^|\s)>{1,2}\s*(?:'([^']+)'|"([^"]+)"|([^\s;&|]+))/gu)].map((match) => match[1] ?? match[2] ?? match[3]).filter((value) => value !== void 0);
1067
+ }
1068
+ function pipeTargets(text) {
1069
+ const targets = [];
1070
+ for (const part of String(text).split("|").slice(1)) {
1071
+ const body = part.replace(/^\s*(?:tee|Set-Content|Out-File|Add-Content)\b/iu, "");
1072
+ if (body === part) continue;
1073
+ for (const token of pathArgs(body)) targets.push(token);
1074
+ }
1075
+ return targets;
1076
+ }
1077
+ function addTarget(targets, token, map) {
1078
+ if (token === void 0 || token === "") return;
1079
+ if (token.startsWith("$")) {
1080
+ const resolved = resolveToken(token, map);
1081
+ if (resolved !== token) targets.push(resolved);
1082
+ return;
1083
+ }
1084
+ targets.push(token);
1085
+ }
1086
+ function segmentAccess(text, root, cwd, map) {
1087
+ if (/\bgit\s+(?:status|add|commit|push)\b/iu.test(text)) {
1088
+ return { write: false, outsideTargets: [] };
1089
+ }
1090
+ if (/\bgit\s+(?:checkout|reset|clean)\b/iu.test(text)) {
1091
+ return { write: true, outsideTargets: [] };
1092
+ }
1093
+ const raw = [];
1094
+ const verb = WRITE_VERB.exec(text);
1095
+ if (verb !== null) {
1096
+ const tail = text.slice(verb.index + verb[0].length);
1097
+ for (const token of pathArgs(tail)) addTarget(raw, token, map);
1098
+ }
1099
+ for (const token of redirectionTargets(text)) addTarget(raw, token, map);
1100
+ for (const token of pipeTargets(text)) addTarget(raw, token, map);
1101
+ const outsideTargets = [];
1102
+ for (const value of raw) {
1103
+ const target = classifyPath(root, path6.resolve(cwd, value));
1104
+ if (target.outside) outsideTargets.push(target.absolute);
1105
+ }
1106
+ return { write: raw.length > 0 || WRITE_VERB.test(text), outsideTargets };
1107
+ }
1108
+ function segments(text) {
1109
+ return String(text).split(/&&|\|\||;|\r?\n/u).map((part) => part.trim()).filter(Boolean);
1110
+ }
1111
+ function shellAccess(command, root, cwd = root) {
1112
+ const map = assignments(command);
1113
+ const parts = segments(command).map((part) => segmentAccess(part, root, cwd, map)).filter((item) => item.write);
1114
+ return {
1115
+ write: parts.length > 0,
1116
+ outsideTargets: parts.flatMap((item) => item.outsideTargets ?? [])
1117
+ };
1118
+ }
1119
+
1120
+ // src/runtime/guards.ts
1121
+ import path7 from "node:path";
1122
+ function restrictionFilter(state, worker) {
1123
+ const writeTools = ["write", "edit", "apply_patch", "pwsh", "bash"];
1124
+ if (worker) {
1125
+ if (state.node === "implement" || state.node === "implement-revise" || state.node === "fix-bug") return void 0;
1126
+ return { deny: writeTools };
1127
+ }
1128
+ return state.phase === "blocked" ? { deny: ["write", "edit", "apply_patch"] } : void 0;
1129
+ }
1130
+ var restrictionState = /* @__PURE__ */ new WeakMap();
1131
+ function applyRestrictions(ctx, agent, store) {
1132
+ const tools = agent?.ctx?.tools;
1133
+ if (typeof tools?.restrict !== "function") return;
1134
+ try {
1135
+ const gate = resolveGate(ctx, agent);
1136
+ const previous = restrictionState.get(agent);
1137
+ const effective = effectiveState(ctx, agent, gate, store);
1138
+ const filter = restrictionFilter(effective.state, effective.worker);
1139
+ const key = `${effective.state.phase}/${effective.state.node ?? ""}/${effective.worker ? "worker" : "root"}/${JSON.stringify(filter ?? null)}`;
1140
+ if (previous?.key === key) return;
1141
+ previous?.disposer?.();
1142
+ restrictionState.delete(agent);
1143
+ if (filter !== void 0) {
1144
+ const names = filter.deny.filter((name) => {
1145
+ try {
1146
+ return typeof tools.get !== "function" || tools.get(name) !== void 0;
1147
+ } catch {
1148
+ return false;
1149
+ }
1150
+ });
1151
+ if (names.length > 0) {
1152
+ let disposer;
1153
+ try {
1154
+ disposer = tools.restrict({ deny: names });
1155
+ } catch {
1156
+ const disposers = [];
1157
+ for (const name of names) {
1158
+ try {
1159
+ disposers.push(tools.restrict({ deny: [name] }));
1160
+ } catch {
1161
+ }
1162
+ }
1163
+ if (disposers.length > 0) disposer = () => {
1164
+ for (const release of disposers) release();
1165
+ };
1166
+ }
1167
+ if (disposer !== void 0) restrictionState.set(agent, { key, disposer });
1168
+ }
1169
+ }
1170
+ } catch {
1171
+ }
1172
+ }
1173
+ function workerWriteAllowed(state, target) {
1174
+ if (state.phase !== "orchestrating") return false;
1175
+ if (state.node === "plan" || state.node === "plan-revise") return false;
1176
+ if (state.node === "implement" || state.node === "implement-revise" || state.node === "fix-bug") return true;
1177
+ return false;
1178
+ }
1179
+ function workerShellAllowed(state, access) {
1180
+ if (state.phase !== "orchestrating") return false;
1181
+ return state.node === "implement" || state.node === "implement-revise" || state.node === "fix-bug";
1182
+ }
1183
+ function targetList(name, args) {
1184
+ if (name !== "apply_patch") return [args?.file_path];
1185
+ return [...String(args?.input ?? "").matchAll(/^\*\*\* (?:Add File|Update File|Delete File|Move to):\s*(.+)$/gmu)].map((match) => String(match[1] ?? "").trim());
1186
+ }
1187
+ var GATE_FROZEN_REASON = "Gate \u5DF2\u51BB\u7ED3\uFF1A\u5148 /gate-resume \u6216 /gate-return-discuss";
1188
+ function gateDenyReason(ctx, exec, store) {
1189
+ if (exec.name === "gate_record") {
1190
+ const root = rootAgentOf(ctx, exec.agent);
1191
+ return root?.id === exec.agent?.id ? void 0 : "gate_record \u53EA\u80FD\u7531 root agent \u8C03\u7528";
1192
+ }
1193
+ if (["subagent", "subagent_fork", "workflow"].includes(exec.name)) {
1194
+ const gate = resolveGate(ctx, exec.agent);
1195
+ const state = store.read(gate.root, exec.agent);
1196
+ if (state.phase === "blocked") return GATE_FROZEN_REASON;
1197
+ if (state.phase === "discuss") return "discuss \u4E0D\u6D3E worker";
1198
+ if (state.node === "note-lifecycle") return "note-lifecycle \u53EA\u505A Note \u6536\u5C3E\uFF0C\u4E0D\u6D3E worker";
1199
+ return void 0;
1200
+ }
1201
+ if (exec.name === "write" || exec.name === "edit" || exec.name === "apply_patch") {
1202
+ const gate = resolveGate(ctx, exec.agent);
1203
+ const effective = effectiveState(ctx, exec.agent, gate, store);
1204
+ const state = effective.state;
1205
+ const cwd = sessionCwd(ctx, exec.agent, gate.workspace);
1206
+ const targets = targetList(exec.name, exec.arguments).filter((value) => typeof value === "string" && value !== "");
1207
+ if (targets.length === 0) return void 0;
1208
+ for (const value of targets) {
1209
+ const target = classifyPath(gate.workspace, value, cwd);
1210
+ if (target.outside) return "\u5DE5\u4F5C\u533A\u5916\u5199\u5165\u88AB\u62D2\u7EDD";
1211
+ if (effective.worker) {
1212
+ if (!workerWriteAllowed(state, target)) return `worker ${state.node ?? "unknown"} \u4E0D\u5141\u8BB8\u5199\u8BE5\u76EE\u6807`;
1213
+ continue;
1214
+ }
1215
+ if (state.phase === "blocked") return GATE_FROZEN_REASON;
1216
+ }
1217
+ return void 0;
1218
+ }
1219
+ if (exec.name === "pwsh" || exec.name === "bash") {
1220
+ const gate = resolveGate(ctx, exec.agent);
1221
+ const effective = effectiveState(ctx, exec.agent, gate, store);
1222
+ const state = effective.state;
1223
+ const base = sessionCwd(ctx, exec.agent, gate.workspace);
1224
+ const requested = exec.arguments?.workdir;
1225
+ const cwd = typeof requested === "string" && requested !== "" ? path7.resolve(base, requested) : base;
1226
+ const access = shellAccess(exec.arguments?.command ?? "", gate.workspace, cwd);
1227
+ if (/\bgit(?:\.exe)?\b[^\n;&|]*\scommit\b/iu.test(String(exec.arguments?.command ?? "")) && effective.worker && !["implement", "implement-revise", "fix-bug"].includes(state.node ?? "")) {
1228
+ return `worker ${state.node ?? "unknown"} \u4E0D\u5141\u8BB8 git commit`;
1229
+ }
1230
+ if (!access.write) return void 0;
1231
+ if (access.outsideTargets.length > 0) return "\u5DE5\u4F5C\u533A\u5916\u5199\u5165\u88AB\u62D2\u7EDD";
1232
+ if (effective.worker) return workerShellAllowed(state, access) ? void 0 : `worker ${state.node ?? "unknown"} \u4E0D\u5141\u8BB8\u5199\u8BE5\u76EE\u6807`;
1233
+ if (state.phase === "blocked") return GATE_FROZEN_REASON;
1234
+ return void 0;
1235
+ }
1236
+ return void 0;
1237
+ }
1238
+ function installGuard(ctx, deps) {
1239
+ ctx.tools.guard((exec) => gateDenyReason(ctx, exec, deps.store));
1240
+ }
1241
+ function installRestrictions(ctx, deps) {
1242
+ ctx.on("agent/pre-step", async (payload, next) => {
1243
+ applyRestrictions(ctx, payload.agent, deps.store);
1244
+ return next();
1245
+ });
1246
+ }
1247
+
1248
+ // src/commands.ts
1249
+ import path9 from "node:path";
1250
+
1251
+ // src/notes/provision.ts
1252
+ import { copyFileSync, existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "node:fs";
1253
+ import path8 from "node:path";
1254
+ import { execFileSync as execFileSync2 } from "node:child_process";
1255
+ import { lstatSync as lstatSync3 } from "node:fs";
1256
+ var NOTES_BLOCK_START = "<!-- gate-notes:start -->";
1257
+ var NOTES_BLOCK_END = "<!-- gate-notes:end -->";
1258
+ var NOTES_AGENTS_TEXT = `${NOTES_BLOCK_START}
1259
+ ## \u91CD\u8981\u6539\u52A8\u5FC5\u987B\u7559\u7B14\u8BB0
1260
+
1261
+ 1. \u975E\u5E73\u51E1\u6539\u52A8\uFF08\u884C\u4E3A\u3001\u67B6\u6784\u3001\u8DE8\u6587\u4EF6\u5951\u7EA6\u3001\u6D41\u7A0B/\u5DE5\u5177\u94FE\u3001\u6D4B\u8BD5\u7B56\u7565\u3001\u843D\u76D8/\u7F51\u7EDC/\u914D\u7F6E\u683C\u5F0F\uFF09\u5728\u52A8\u624B\u524D\u4F7F\u7528\u5185\u7F6E\u6280\u80FD \`gate-write-agent-notes\`\uFF0C\u6309 \xA73 \u68C0\u7D22\u3001\xA74 \u5224\u5B9A\u5F52\u5C5E\u4E0E class\uFF1B\u673A\u68B0\u6027\u5C0F\u6539\u76F4\u63A5\u63D0\u4EA4\u3002
1262
+ 2. \u65E2\u6709\u51B3\u5B9A\u672A\u53D8\u65F6\u539F\u5730\u540C\u6B65 \`implemented/\` Note\uFF1B\u65B0\u60F3\u6CD5\u5148\u653E \`proposed/\`\uFF1B\u843D\u5730\u968F\u540C\u4EE3\u7801\u8F6C \`implemented/\`\u3002
1263
+ 3. \u88AB\u653E\u5F03\u7684\u65B9\u6848\u5148\u5199\u6700\u5F3A\u7406\u7531\u518D\u5426\u51B3\u3002
1264
+ 4. \u63D0\u4EA4\u524D\u8FD0\u884C \`npm run verify-notes\` \u68C0\u67E5\uFF1Bgate \u4F1A\u8BDD\u5185\u4E5F\u53EF\u7528 \`/gate-note-verify\`\u3002gate \u7684 lifecycle \u7531 \`gate_note_finalize\` \u2192 \`gate_note_verify\` \u2192 \`gate_record({node:'note-lifecycle'})\` \u5B8C\u6210\u3002
1265
+ ${NOTES_BLOCK_END}`;
1266
+ function provisionNotes(root) {
1267
+ const notes = path8.join(root, ".agents", "notes");
1268
+ if (pathHasSymlink(root, notes)) throw new Error("notes \u8DEF\u5F84\u5305\u542B symlink");
1269
+ for (const lifecycle of ["proposed", "implemented", "rejected", "archived"]) {
1270
+ const dir = path8.join(notes, lifecycle);
1271
+ if (pathHasSymlink(root, dir)) throw new Error(`${lifecycle} \u8DEF\u5F84\u5305\u542B symlink`);
1272
+ mkdirSync3(dir, { recursive: true });
1273
+ const keep = path8.join(dir, ".gitkeep");
1274
+ if (!existsSync3(keep)) writeFileSync3(keep, "");
1275
+ }
1276
+ const agents = path8.join(root, "AGENTS.md");
1277
+ if (existsSync3(agents) && lstatSync3(agents).isSymbolicLink()) throw new Error("AGENTS.md \u662F symlink");
1278
+ const existing = existsSync3(agents) ? readFileSync2(agents, "utf8") : "";
1279
+ const block = /<!-- gate-notes:start -->[\s\S]*?<!-- gate-notes:end -->/u;
1280
+ const next = block.test(existing) ? existing.replace(block, NOTES_AGENTS_TEXT) : `${existing}${existing === "" || existing.endsWith("\n") ? "" : "\n"}
1281
+ ${NOTES_AGENTS_TEXT}
1282
+ `;
1283
+ writeFileSync3(agents, next);
1284
+ return { notes: path8.relative(root, notes).replaceAll("\\", "/"), agents: path8.relative(root, agents).replaceAll("\\", "/") };
1285
+ }
1286
+ function provisionProjectScripts(root) {
1287
+ const dir = path8.join(root, ".agents", "gate");
1288
+ const launcher = path8.join(dir, "verify-notes.mjs");
1289
+ mkdirSync3(dir, { recursive: true });
1290
+ copyFileSync(NOTES_LAUNCHER_TEMPLATE, launcher);
1291
+ const packagePath = path8.join(root, "package.json");
1292
+ let pkg = { private: true };
1293
+ if (existsSync3(packagePath)) {
1294
+ try {
1295
+ pkg = JSON.parse(readFileSync2(packagePath, "utf8"));
1296
+ } catch {
1297
+ throw new Error("package.json \u4E0D\u662F\u6709\u6548 JSON");
1298
+ }
1299
+ if (pkg === null || typeof pkg !== "object" || Array.isArray(pkg)) throw new Error("package.json \u5FC5\u987B\u662F\u5BF9\u8C61");
1300
+ }
1301
+ pkg.scripts = pkg.scripts !== null && typeof pkg.scripts === "object" && !Array.isArray(pkg.scripts) ? pkg.scripts : {};
1302
+ const command = "node .agents/gate/verify-notes.mjs";
1303
+ const existing = pkg.scripts["verify-notes"];
1304
+ if (existing === void 0) pkg.scripts["verify-notes"] = command;
1305
+ writeFileSync3(packagePath, `${JSON.stringify(pkg, null, 2)}
1306
+ `);
1307
+ return {
1308
+ launcher: path8.relative(root, launcher).replaceAll("\\", "/"),
1309
+ package: path8.relative(root, packagePath).replaceAll("\\", "/"),
1310
+ command: existing === void 0 ? command : String(existing),
1311
+ changed: existing === void 0
1312
+ };
1313
+ }
1314
+ var BASE_GITIGNORE = `node_modules/
1315
+ .env
1316
+ .env.*
1317
+ !.env.example
1318
+ *.log
1319
+ .DS_Store
1320
+ Thumbs.db
1321
+ coverage/
1322
+ dist/
1323
+ .agents/notes/.gate-finalize-*
1324
+ .agents/notes/.gate-validate-*
1325
+ .agents/notes/*.gate-backup
1326
+ `;
1327
+ function provisionGit(root) {
1328
+ const ignore = path8.join(root, ".gitignore");
1329
+ let ignoreStatus = "existing";
1330
+ if (!existsSync3(ignore)) {
1331
+ writeFileSync3(ignore, BASE_GITIGNORE);
1332
+ ignoreStatus = "created";
1333
+ }
1334
+ const git = (args, options = {}) => execFileSync2("git", args, { cwd: root, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], ...options });
1335
+ let gitError = null;
1336
+ const init = () => {
1337
+ try {
1338
+ git(["init", "-b", "main"]);
1339
+ } catch (first) {
1340
+ try {
1341
+ git(["init"]);
1342
+ } catch (second) {
1343
+ gitError = String(second?.stderr ?? second?.message ?? first?.stderr ?? first?.message ?? second).trim();
1344
+ return "init-failed";
1345
+ }
1346
+ }
1347
+ return "initialized";
1348
+ };
1349
+ let gitStatus;
1350
+ try {
1351
+ gitStatus = git(["rev-parse", "--is-inside-work-tree"]).trim() === "true" ? "existing" : init();
1352
+ } catch (error) {
1353
+ gitStatus = error?.code === "ENOENT" ? "unavailable" : init();
1354
+ }
1355
+ return { git: gitStatus, ignore: ignoreStatus, gitError };
1356
+ }
1357
+ function provisionCi(root) {
1358
+ const ciDir = path8.join(root, ".agents", "gate", "ci");
1359
+ mkdirSync3(ciDir, { recursive: true });
1360
+ for (const name of NOTES_CI_SCRIPTS) {
1361
+ copyFileSync(path8.join(NOTES_SKILL_DIR, "scripts", name), path8.join(ciDir, name));
1362
+ }
1363
+ const workflow = path8.join(root, ".github", "workflows", "verify-notes.yml");
1364
+ mkdirSync3(path8.dirname(workflow), { recursive: true });
1365
+ const existing = existsSync3(workflow);
1366
+ if (!existing) copyFileSync(NOTES_CI_TEMPLATE, workflow);
1367
+ return { workflow: path8.relative(root, workflow).replaceAll("\\", "/"), scripts: path8.relative(root, ciDir).replaceAll("\\", "/"), changed: !existing };
1368
+ }
1369
+
1370
+ // src/commands.ts
1371
+ import { execFileSync as execFileSync3 } from "node:child_process";
1372
+ function registerCommands(ctx, deps) {
1373
+ const { store, reviewLimit, wake } = deps;
1374
+ ctx.inject(["commands"], (scope) => {
1375
+ const register = (name, description, handler, input = { hint: "<\u5DE5\u4F5C\u9879>" }) => scope.commands.register({
1376
+ name,
1377
+ description,
1378
+ ...input === null ? {} : { input },
1379
+ handler
1380
+ });
1381
+ register("gate-init", "\u521D\u59CB\u5316 notes\u3001AGENTS.md\u3001verify-notes \u4E0E GitHub Actions CI", async (invocation) => {
1382
+ const workspace = workspaceBoundary(ctx, invocation.agent);
1383
+ if (workspace === void 0) return { kind: "error", text: "\u65E0\u6CD5\u786E\u5B9A\u5F53\u524D\u4F1A\u8BDD\u5DE5\u4F5C\u533A" };
1384
+ const root = path9.resolve(workspace);
1385
+ let provisioned;
1386
+ let projectScripts;
1387
+ let projectCi;
1388
+ let projectGit;
1389
+ try {
1390
+ provisioned = provisionNotes(root);
1391
+ projectCi = provisionCi(root);
1392
+ projectScripts = provisionProjectScripts(root);
1393
+ projectGit = provisionGit(root);
1394
+ } catch (error) {
1395
+ return { kind: "error", text: `notes \u521D\u59CB\u5316\u5931\u8D25\uFF1A${error instanceof Error ? error.message : String(error)}` };
1396
+ }
1397
+ wake(invocation, store.read(root, invocation.agent), `Gate \u5DF2\u521D\u59CB\u5316\uFF1A${root}\uFF08phase=discuss\uFF09\uFF0Cnotes \u5DF2\u521D\u59CB\u5316\u4E3A ${provisioned.notes}\uFF0CAGENTS.md \u5DF2\u66F4\u65B0\uFF0Cverify-notes \u5DF2\u914D\u7F6E\u4E3A ${projectScripts.command}\uFF0CCI \u5DF2\u914D\u7F6E\u4E3A ${projectCi.workflow}\uFF0Cgit\uFF1A${projectGit.git}${projectGit.gitError === null ? "" : `\uFF08${projectGit.gitError}\uFF09`}\uFF0Cignore\uFF1A${projectGit.ignore}\u3002`, "gate-init");
1398
+ return { kind: "success", text: `Gate \u5DF2\u521D\u59CB\u5316\uFF1A${root}\uFF08phase=discuss\uFF09
1399
+ notes\uFF1A${provisioned.notes}
1400
+ AGENTS\uFF1A${provisioned.agents}
1401
+ verify-notes\uFF1A${projectScripts.command}
1402
+ CI\uFF1A${projectCi.workflow}
1403
+ git\uFF1A${projectGit.git}${projectGit.gitError === null ? "" : `\uFF08${projectGit.gitError}\uFF09`}
1404
+ ignore\uFF1A${projectGit.ignore}` };
1405
+ }, null);
1406
+ register("gate-enter-plan", "\u6309\u7528\u6237\u7B80\u8FF0\u8FDB\u5165 planning", async (invocation) => {
1407
+ const result = enterPhase(ctx, invocation.agent, "plan", reviewLimit, store, void 0);
1408
+ if (result.kind === "error") return result;
1409
+ const brief = String(invocation.rawInput ?? "").trim();
1410
+ wake(invocation, result.state, `Gate \u5DF2\u8FDB\u5165 plan\u3002\u7528\u6237\u89C4\u5212\u8F93\u5165\uFF1A${brief === "" ? "\uFF08\u672A\u63D0\u4F9B\u989D\u5916\u8F93\u5165\uFF09" : brief}\u3002`, "gate-enter-plan");
1411
+ return { kind: "success", text: "\u8FDB\u5165 planning\u3002" };
1412
+ }, { hint: "[\u7B80\u5355\u9700\u6C42]" });
1413
+ register("gate-enter-implement", "\u4ECE\u5DF2\u786E\u8BA4\u89C4\u5212\u6216\u7528\u6237\u7B80\u8FF0\u8FDB\u5165 implementation", async (invocation) => {
1414
+ const brief = String(invocation.rawInput ?? "").trim();
1415
+ const gate = resolveGate(ctx, invocation.agent);
1416
+ const state = store.read(gate.root, invocation.agent);
1417
+ const direct = state.planReady !== true;
1418
+ if (direct) {
1419
+ if (brief === "") return { kind: "error", text: "\u8BF7\u7528\u4E00\u53E5\u8BDD\u8BF4\u660E\u8981\u5B9E\u65BD\u7684\u5185\u5BB9\uFF1B\u4E0D\u8981\u6C42\u5DE5\u4F5C\u9879\u683C\u5F0F\u3002" };
1420
+ state.task = brief;
1421
+ state.planReady = false;
1422
+ state.noteDecision = null;
1423
+ state.notePaths = [];
1424
+ state.noteSearch = null;
1425
+ state.noteVerified = false;
1426
+ state.noteFinalized = false;
1427
+ state.noteFinalizeRecovery = [];
1428
+ store.write(gate.root, invocation.agent, state);
1429
+ }
1430
+ const result = enterPhase(ctx, invocation.agent, "implement", reviewLimit, store, direct ? state : void 0);
1431
+ if (result.kind === "error") return result;
1432
+ const constraints = direct ? "\uFF08\u672C\u53E5\u5373\u5DE5\u4F5C\u9879\u7B80\u8FF0\uFF09" : brief === "" ? "\uFF08\u65E0\u989D\u5916\u7EA6\u675F\uFF09" : brief;
1433
+ wake(invocation, result.state, `Gate \u5DF2\u8FDB\u5165 implement\uFF1A${result.state.task ?? ""}${direct ? "\uFF08\u76F4\u63A5\u5B9E\u65BD\uFF0C\u672A\u8D70 planning\uFF09" : ""}\u3002\u7528\u6237\u8BF4\u660E\uFF1A${constraints}\u3002`, "gate-enter-implement");
1434
+ return { kind: "success", text: `\u8FDB\u5165 implement\uFF1A${result.state.task}${direct ? "\uFF08\u76F4\u63A5\u5B9E\u65BD\uFF09" : ""}` };
1435
+ }, { hint: "[\u7B80\u5355\u8BF4\u660E]" });
1436
+ register("gate-fix-bug", "\u4E3A\u5DF2\u6388\u6743\u7684\u5B9E\u73B0\u7F3A\u9677\u6D3E\u53D1 fix worker", async (invocation) => {
1437
+ const brief = String(invocation.rawInput ?? "").trim();
1438
+ const gate = resolveGate(ctx, invocation.agent);
1439
+ const state = store.read(gate.root, invocation.agent);
1440
+ if (state.phase !== "discuss" && state.phase !== "complete") return { kind: "error", text: `\u5F53\u524D phase \u662F ${state.phase}` };
1441
+ if (state.work === "blocked" || state.blockReason !== null) return { kind: "error", text: `\u5F53\u524D\u88AB blocked\uFF1A${state.blockReason ?? "unknown"}\uFF1B\u5148 /gate-resume` };
1442
+ const scope2 = brief === "" ? state.task ?? "\u5F53\u524D\u4E0A\u4E0B\u6587\u4E2D\u7684\u7F3A\u9677" : brief;
1443
+ state.phase = "orchestrating";
1444
+ state.node = "fix-bug";
1445
+ state.task = scope2;
1446
+ state.work = "active";
1447
+ state.blockReason = null;
1448
+ state.blockedFrom = null;
1449
+ store.write(gate.root, invocation.agent, state);
1450
+ wake(invocation, state, `Gate \u5DF2\u8FDB\u5165 fix-bug\uFF1A${scope2}\u3002`, "gate-fix-bug");
1451
+ return { kind: "success", text: `\u8FDB\u5165 fix-bug\uFF1A${scope2}` };
1452
+ }, { hint: "[\u7F3A\u9677\u7B80\u8FF0]" });
1453
+ register("gate-docs", "\u5F00\u59CB\u6587\u6863\u6CBB\u7406", async (invocation) => {
1454
+ const gate = resolveGate(ctx, invocation.agent);
1455
+ const state = store.read(gate.root, invocation.agent);
1456
+ if (state.phase !== "discuss" && state.phase !== "complete") return { kind: "error", text: `\u5F53\u524D phase \u662F ${state.phase}` };
1457
+ if (state.work === "blocked" || state.blockReason !== null) return { kind: "error", text: `\u5F53\u524D\u88AB blocked\uFF1A${state.blockReason ?? "unknown"}\uFF1B\u5148 /gate-resume` };
1458
+ state.phase = "docs";
1459
+ state.task = "\u6587\u6863\u6CBB\u7406";
1460
+ state.work = "active";
1461
+ state.blockReason = null;
1462
+ state.blockedFrom = null;
1463
+ state.node = null;
1464
+ store.write(gate.root, invocation.agent, state);
1465
+ wake(invocation, state, "Gate \u5DF2\u8FDB\u5165 docs\u3002", "gate-docs");
1466
+ return { kind: "success", text: "\u8FDB\u5165 docs\uFF1A\u6587\u6863\u6CBB\u7406" };
1467
+ }, null);
1468
+ register("gate-implement-review", "\u5F00\u59CB\u72EC\u7ACB\u53EA\u8BFB code review", async (invocation) => {
1469
+ const brief = String(invocation.rawInput ?? "").trim();
1470
+ const gate = resolveGate(ctx, invocation.agent);
1471
+ const state = store.read(gate.root, invocation.agent);
1472
+ if (state.phase !== "discuss" && state.phase !== "complete") return { kind: "error", text: `\u5F53\u524D phase \u662F ${state.phase}` };
1473
+ if (state.work === "blocked" || state.blockReason !== null) return { kind: "error", text: `\u5F53\u524D\u88AB blocked\uFF1A${state.blockReason ?? "unknown"}\uFF1B\u5148 /gate-resume` };
1474
+ const scope2 = brief === "" ? state.task ?? "\u5F53\u524D\u4E0A\u4E0B\u6587\u4E2D\u7684\u6539\u52A8" : brief;
1475
+ state.phase = "reviewing";
1476
+ state.task = scope2;
1477
+ state.node = "code-review-standalone";
1478
+ state.work = "active";
1479
+ state.blockReason = null;
1480
+ state.blockedFrom = null;
1481
+ store.write(gate.root, invocation.agent, state);
1482
+ wake(invocation, state, `Gate \u5DF2\u8FDB\u5165\u53EA\u8BFB code review\uFF1A${scope2}\u3002`, "gate-implement-review");
1483
+ return { kind: "success", text: `\u8FDB\u5165\u53EA\u8BFB code review\uFF1A${scope2}` };
1484
+ });
1485
+ register("gate-code-verify", "\u5F00\u59CB\u72EC\u7ACB\u53EA\u8BFB\u9A8C\u6536", async (invocation) => {
1486
+ const brief = String(invocation.rawInput ?? "").trim();
1487
+ const gate = resolveGate(ctx, invocation.agent);
1488
+ const state = store.read(gate.root, invocation.agent);
1489
+ if (state.phase !== "discuss" && state.phase !== "complete") return { kind: "error", text: `\u5F53\u524D phase \u662F ${state.phase}` };
1490
+ if (state.work === "blocked" || state.blockReason !== null) return { kind: "error", text: `\u5F53\u524D\u88AB blocked\uFF1A${state.blockReason ?? "unknown"}\uFF1B\u5148 /gate-resume` };
1491
+ const scope2 = brief === "" ? state.task ?? "\u5F53\u524D\u4E0A\u4E0B\u6587\u4E2D\u7684\u6539\u52A8" : brief;
1492
+ state.phase = "reviewing";
1493
+ state.task = scope2;
1494
+ state.node = "verify-standalone";
1495
+ state.work = "active";
1496
+ state.blockReason = null;
1497
+ state.blockedFrom = null;
1498
+ store.write(gate.root, invocation.agent, state);
1499
+ wake(invocation, state, `Gate \u5DF2\u8FDB\u5165\u72EC\u7ACB\u9A8C\u6536\uFF1A${scope2}\u3002`, "gate-code-verify");
1500
+ return { kind: "success", text: `\u8FDB\u5165\u72EC\u7ACB\u9A8C\u6536\uFF1A${scope2}` };
1501
+ });
1502
+ register("gate-propose-review", "\u5F00\u59CB\u72EC\u7ACB\u63D0\u6848\u8BC4\u5BA1", async (invocation) => {
1503
+ const brief = String(invocation.rawInput ?? "").trim();
1504
+ const gate = resolveGate(ctx, invocation.agent);
1505
+ const state = store.read(gate.root, invocation.agent);
1506
+ if (state.phase !== "discuss" && state.phase !== "complete") return { kind: "error", text: `\u5F53\u524D phase \u662F ${state.phase}` };
1507
+ if (state.work === "blocked" || state.blockReason !== null) return { kind: "error", text: `\u5F53\u524D\u88AB blocked\uFF1A${state.blockReason ?? "unknown"}\uFF1B\u5148 /gate-resume` };
1508
+ const scope2 = brief === "" ? state.task ?? "\u5F53\u524D\u63D0\u6848/Note" : brief;
1509
+ state.phase = "reviewing";
1510
+ state.task = scope2;
1511
+ state.node = "proposal-review";
1512
+ state.work = "active";
1513
+ state.blockReason = null;
1514
+ state.blockedFrom = null;
1515
+ store.write(gate.root, invocation.agent, state);
1516
+ wake(invocation, state, `Gate \u5DF2\u8FDB\u5165\u63D0\u6848\u8BC4\u5BA1\uFF1A${scope2}\u3002`, "gate-propose-review");
1517
+ return { kind: "success", text: `\u8FDB\u5165\u63D0\u6848\u8BC4\u5BA1\uFF1A${scope2}` };
1518
+ });
1519
+ register("gate-block", "\u624B\u52A8\u6682\u505C\uFF1A\u4FDD\u7559\u5F53\u524D\u8282\u70B9\u5E76\u505C\u6B62\u5199\u5165/\u6D3E worker\uFF1B\u5BF9\u8BDD\u4ECD\u53EF\u7EE7\u7EED", async (invocation) => {
1520
+ const gate = resolveGate(ctx, invocation.agent);
1521
+ const state = store.read(gate.root, invocation.agent);
1522
+ if (state.phase === "blocked") return { kind: "success", text: "\u5DF2\u7ECF\u662F blocked" };
1523
+ state.blockedFrom = { phase: state.phase, node: state.node };
1524
+ state.phase = "blocked";
1525
+ state.work = "blocked";
1526
+ state.node = null;
1527
+ state.blockReason = String(invocation.rawInput ?? "").trim() || "user-blocked";
1528
+ store.write(gate.root, invocation.agent, state);
1529
+ return { kind: "success", text: `\u5DF2\u6682\u505C\uFF1A${state.blockReason}
1530
+ \u89E3\u51BB\u540E\uFF1A${state.blockedFrom?.phase ?? "discuss"}${state.blockedFrom?.node === null || state.blockedFrom?.node === void 0 ? "" : ` / ${state.blockedFrom.node}`}
1531
+ \u540C\u4E00\u4E8B\u9879\uFF1A/gate-resume \u56DE\u5230\u8BE5\u8282\u70B9\uFF1B\u6539\u53D8\u65B9\u6848\uFF1A/gate-return-discuss\u3002` };
1532
+ }, { hint: "[\u539F\u56E0]" });
1533
+ register("gate-resume", "\u89E3\u51BB blocked\uFF1A\u56DE\u5230 block \u524D\u7684\u8282\u70B9\uFF1B\u8F6E\u6B21\u8017\u5C3D\u56DE discuss", async (invocation) => {
1534
+ const gate = resolveGate(ctx, invocation.agent);
1535
+ const state = store.read(gate.root, invocation.agent);
1536
+ if (state.phase !== "blocked") return { kind: "error", text: "\u5F53\u524D\u4E0D\u662F blocked" };
1537
+ const from = state.blockedFrom;
1538
+ const reason = state.blockReason;
1539
+ state.phase = from?.phase ?? "discuss";
1540
+ state.node = from?.node ?? null;
1541
+ state.work = "active";
1542
+ state.blockReason = null;
1543
+ state.blockedFrom = null;
1544
+ store.write(gate.root, invocation.agent, state);
1545
+ if (state.phase === "orchestrating" || state.phase === "reviewing" || state.phase === "docs") {
1546
+ wake(invocation, state, `Gate \u5DF2\u6062\u590D\u5230 ${state.phase}${state.node === null ? "" : ` / ${state.node}`}\u3002`, "gate-resume");
1547
+ }
1548
+ const exhausted = reason === "plan-review-rounds-exhausted" || reason === "code-review-rounds-exhausted";
1549
+ return { kind: "success", text: `\u5DF2\u89E3\u9664 blocked\uFF1A${state.phase}${state.node === null ? "" : ` / ${state.node}`}${exhausted ? "\nreview \u8F6E\u6B21\u5DF2\u8017\u5C3D\uFF0C\u4E0D\u80FD\u7EE7\u7EED\u539F\u8282\u70B9\uFF1B\u7528 /gate-enter-plan \u91CD\u65B0\u89C4\u5212\u3002" : "\n\u5DF2\u56DE\u5230\u6682\u505C\u524D\u8282\u70B9\uFF0C\u6309\u5F53\u524D procedure \u7EE7\u7EED\u3002"}` };
1550
+ }, null);
1551
+ register("gate-return-discuss", "\u56DE\u5230 discuss", async (invocation) => {
1552
+ const gate = resolveGate(ctx, invocation.agent);
1553
+ const state = store.read(gate.root, invocation.agent);
1554
+ if (state.phase === "blocked") return { kind: "error", text: `\u5F53\u524D blocked\uFF1A${state.blockReason ?? "unknown"}\uFF1B\u5148 /gate-resume` };
1555
+ state.phase = "discuss";
1556
+ state.node = null;
1557
+ state.work = "active";
1558
+ state.blockReason = null;
1559
+ state.blockedFrom = null;
1560
+ state.task = null;
1561
+ state.planReady = false;
1562
+ state.rounds = { planReview: 0, codeReview: 0 };
1563
+ state.verification = null;
1564
+ state.notePaths = [];
1565
+ state.noteDecision = null;
1566
+ state.noteSearch = null;
1567
+ state.noteVerified = false;
1568
+ state.noteFinalized = false;
1569
+ state.noteFinalizeRecovery = [];
1570
+ state.pending = {};
1571
+ store.write(gate.root, invocation.agent, state);
1572
+ return { kind: "success", text: "\u5DF2\u56DE\u5230 discuss" };
1573
+ }, null);
1574
+ register("gate-status", "\u67E5\u770B\u5F53\u524D gate \u72B6\u6001", async (invocation) => {
1575
+ const gate = resolveGate(ctx, invocation.agent);
1576
+ const root = rootAgentOf(ctx, invocation.agent);
1577
+ return { kind: "success", text: statusText(store.read(gate.root, root), reviewLimit) };
1578
+ }, null);
1579
+ register("gate-note-verify", "\u53EA\u8BFB\u68C0\u67E5 notes tree/format/lifecycle/archived", async (invocation) => {
1580
+ const gate = resolveGate(ctx, invocation.agent);
1581
+ const check = runNotesScripts(gate.root);
1582
+ return check.ok ? { kind: "success", text: check.results.map((item) => `${item.name}: ok`).join("\n") } : { kind: "error", text: `notes \u6821\u9A8C\u5931\u8D25\uFF1A
1583
+ ${JSON.stringify(check.results, null, 2)}` };
1584
+ }, null);
1585
+ register("gate-note-archive", "\u5C06 implemented Note \u5F52\u6863\u5230 archived/\uFF1A\u52A0 Archived\u3001\u79FB\u52A8\u3001\u5C01\u5370\u5E76\u62A5\u544A\u5165\u7AD9\u94FE\u63A5", async (invocation) => {
1586
+ const gate = resolveGate(ctx, invocation.agent);
1587
+ const raw = String(invocation.rawInput ?? "").trim();
1588
+ const args = raw === "" ? [] : raw.split(/\s+/u);
1589
+ if (args.length === 0) return { kind: "error", text: "\u7528\u6CD5\uFF1A/gate-note-archive implemented/<class>/<note>.md [--superseded-by implemented/<class>/<new>.md]" };
1590
+ const node = resolveNodeBinary();
1591
+ if (node === void 0) return { kind: "error", text: "notes-runtime-unavailable" };
1592
+ try {
1593
+ const script = path9.join(NOTES_SKILL_DIR, "scripts", "archive-agent-note.ts");
1594
+ const output = execFileSync3(node, [script, "--root", path9.join(gate.root, ".agents", "notes"), ...args], { cwd: gate.root, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
1595
+ return { kind: "success", text: String(output).trim() || "Note \u5DF2\u5F52\u6863" };
1596
+ } catch (error) {
1597
+ return { kind: "error", text: String(error?.stderr ?? error?.message ?? error).trim() };
1598
+ }
1599
+ }, { hint: "<implemented-note> [--superseded-by <new-note>]" });
1600
+ register("gate-note-board", "\u751F\u6210\u51B3\u7B56\u770B\u677F board.html\uFF08\u8F7B\u91CF\u6A21\u5F0F\uFF0C\u8FDE\u63A5\u672C\u5730 notes \u76EE\u5F55\uFF09", async (invocation) => {
1601
+ const gate = resolveGate(ctx, invocation.agent);
1602
+ const raw = String(invocation.rawInput ?? "").trim();
1603
+ const args = raw === "" ? [] : raw.split(/\s+/u);
1604
+ const target = args[0] ?? "board.html";
1605
+ const title = args[1] ?? `${path9.basename(gate.root)} \u51B3\u7B56\u770B\u677F`;
1606
+ const node = resolveNodeBinary();
1607
+ if (node === void 0) return { kind: "error", text: "notes-runtime-unavailable" };
1608
+ try {
1609
+ const script = path9.join(NOTES_SKILL_DIR, "scripts", "build-board.ts");
1610
+ const output = execFileSync3(node, [script, "--init", target, title], { cwd: gate.root, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
1611
+ return { kind: "success", text: String(output).trim() || `\u51B3\u7B56\u770B\u677F\u5DF2\u751F\u6210\uFF1A${target}` };
1612
+ } catch (error) {
1613
+ return { kind: "error", text: String(error?.stderr ?? error?.message ?? error).trim() };
1614
+ }
1615
+ }, { hint: "[\u8F93\u51FA\u8DEF\u5F84] [\u6807\u9898]" });
1616
+ register("gate-note-seal", "\u53EA\u7ED9 archived/ \u4E2D\u5DF2\u6709 Note \u8865\u5199 manifest.json \u7684 SHA-256 \u5C01\u5370\uFF1B\u4E0D\u5F52\u6863\u3001\u4E0D\u6539 Note \u5185\u5BB9", async (invocation) => {
1617
+ const gate = resolveGate(ctx, invocation.agent);
1618
+ const node = resolveNodeBinary();
1619
+ if (node === void 0) return { kind: "error", text: "notes-runtime-unavailable" };
1620
+ try {
1621
+ const script = path9.join(NOTES_SKILL_DIR, "scripts", "verify-archived-agent-notes.ts");
1622
+ const output = execFileSync3(node, [script, "--root", path9.join(gate.root, ".agents", "notes"), "--write"], { cwd: gate.root, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
1623
+ return { kind: "success", text: String(output).trim() || "archived manifest \u5DF2\u5199\u5165" };
1624
+ } catch (error) {
1625
+ return { kind: "error", text: String(error?.stderr ?? error?.message ?? error).trim() };
1626
+ }
1627
+ }, null);
1628
+ });
1629
+ }
1630
+
1631
+ // src/tools/start-phase.ts
1632
+ function registerStartPhase(ctx, deps) {
1633
+ const { store, reviewLimit, wake } = deps;
1634
+ ctx.tools.register({
1635
+ name: "gate_start_phase",
1636
+ description: "\u5728\u7528\u6237\u660E\u786E\u6388\u6743\u540E\u4ECE discuss/complete \u8FDB\u5165 plan \u6216 implement\u3002\u4EC5 root agent \u53EF\u8C03\u7528\uFF1B\u6388\u6743\u53EF\u6765\u81EA\u5BF9\u8BDD\u6216 ask_user_question\uFF0C\u4E0D\u8981\u6C42\u7528\u6237\u8F93\u5165 slash command\u3002",
1637
+ parameters: {
1638
+ type: "object",
1639
+ additionalProperties: false,
1640
+ properties: {
1641
+ node: { type: "string", enum: ["plan", "implement"] },
1642
+ brief: { type: "string" }
1643
+ },
1644
+ required: ["node"]
1645
+ },
1646
+ output: {
1647
+ schema: { type: "object", additionalProperties: true },
1648
+ render: (_args, value) => renderProcedureResult(value),
1649
+ presentationMeta: (_args, value) => value?.state === void 0 ? { gate: {} } : { gate: { state: value.state } }
1650
+ },
1651
+ async execute(args, exec) {
1652
+ const root = rootAgentOf(ctx, exec.agent);
1653
+ if (root?.id !== exec.agent?.id) return { ok: false, code: "root-only", reason: "gate_start_phase \u53EA\u80FD\u7531 root agent \u8C03\u7528\u3002" };
1654
+ const gate = resolveGate(ctx, exec.agent);
1655
+ const state = store.read(gate.root, exec.agent);
1656
+ const fail = (fields) => procedureFailure(state, reviewLimit, fields);
1657
+ if (args.node !== "plan" && args.node !== "implement") return fail({ code: "invalid-node", reason: "node \u5FC5\u987B\u662F plan \u6216 implement\u3002" });
1658
+ const direct = args.node === "implement" && state.planReady !== true;
1659
+ if (direct) {
1660
+ const brief = String(args.brief ?? "").trim();
1661
+ if (brief === "") return fail({ code: "brief-required", reason: "\u76F4\u63A5 implement \u9700\u8981\u4E00\u53E5\u8BF4\u660E\u3002" });
1662
+ state.task = brief;
1663
+ state.planReady = false;
1664
+ state.noteDecision = null;
1665
+ state.notePaths = [];
1666
+ state.noteSearch = null;
1667
+ state.noteVerified = false;
1668
+ state.noteFinalized = false;
1669
+ state.noteFinalizeRecovery = [];
1670
+ store.write(gate.root, exec.agent, state);
1671
+ }
1672
+ const result = enterPhase(ctx, exec.agent, args.node, reviewLimit, store, direct ? state : void 0);
1673
+ if (result.kind === "error") return fail({ code: "phase-enter-failed", reason: result.text });
1674
+ const procedure = procedureFor(result.state, { reviewLimit });
1675
+ return {
1676
+ ok: true,
1677
+ phase: result.state.phase,
1678
+ node: result.state.node,
1679
+ task: result.state.task,
1680
+ procedure,
1681
+ method: procedure.method,
1682
+ state: normalizeState(result.state)
1683
+ };
1684
+ }
1685
+ });
1686
+ }
1687
+
1688
+ // src/tools/record.ts
1689
+ import { createHash } from "node:crypto";
1690
+ import { existsSync as existsSync4, lstatSync as lstatSync4, readFileSync as readFileSync3 } from "node:fs";
1691
+ function registerRecord(ctx, deps) {
1692
+ const { store, reviewLimit, wake } = deps;
1693
+ ctx.tools.register({
1694
+ name: "gate_record",
1695
+ description: "\u8BB0\u5F55 root agent \u6D3E\u53D1\u7684\u8282\u70B9 worker \u7ED3\u679C\u5E76\u63A8\u8FDB gate \u72B6\u6001\u3002worker \u8282\u70B9\u8981\u6C42 workerSessionIds\uFF1Breview \u8282\u70B9\u8981\u6C42\u4E24\u8F74\u7ED3\u679C\uFF1Bverify \u8981\u6C42 toolResults\u3002\u4EC5 root agent \u53EF\u8C03\u7528\u3002",
1696
+ parameters: {
1697
+ type: "object",
1698
+ additionalProperties: false,
1699
+ properties: {
1700
+ node: { type: "string", enum: GATE_RECORD_NODES },
1701
+ workerSessionIds: { type: "array", items: { type: "string" } },
1702
+ reviewAxes: { type: "object", additionalProperties: true },
1703
+ task: { type: "string" },
1704
+ findings: { type: "array", items: { type: "object", additionalProperties: true } },
1705
+ requiresDiscussion: { type: "boolean" },
1706
+ discussionReason: { type: "string" },
1707
+ notePaths: {
1708
+ type: "array",
1709
+ items: {
1710
+ type: "object",
1711
+ additionalProperties: false,
1712
+ properties: {
1713
+ path: { type: "string", description: "\u63A5\u53D7 .agents/notes/<lifecycle>/<class>/...\u3001<lifecycle>/<class>/... \u6216 <class>/...\uFF1Bstate \u5F52\u4E00\u4E3A\u5B8C\u6574\u8DEF\u5F84\u3002" },
1714
+ class: { type: "string", enum: ["feature", "bug-fix", "simplification", "architecture", "process", "testing"] },
1715
+ lifecycle: { type: "string", enum: ["proposed", "implemented"] }
1716
+ },
1717
+ required: ["path", "class", "lifecycle"]
1718
+ }
1719
+ },
1720
+ noteDecision: { type: "string", enum: ["new", "update", "in-place", "mechanical"] },
1721
+ noteSearch: { type: "string" },
1722
+ verification: {
1723
+ type: "object",
1724
+ additionalProperties: false,
1725
+ properties: {
1726
+ commands: { type: "array", items: { type: "string" } },
1727
+ evidence: { type: "array", items: { type: "string", description: "gate workspace \u5185\u666E\u901A\u6587\u4EF6\u8DEF\u5F84\u3002" } },
1728
+ toolResults: { type: "array", items: { type: "string", description: "verify worker session log \u4E2D\u771F\u5B9E tool/result \u7684 call_... id\u3002" } },
1729
+ passed: { type: "boolean" }
1730
+ },
1731
+ required: ["commands", "evidence", "toolResults", "passed"]
1732
+ }
1733
+ },
1734
+ required: ["node"]
1735
+ },
1736
+ output: {
1737
+ schema: { type: "object", additionalProperties: true },
1738
+ render: (_args, value) => renderProcedureResult(value),
1739
+ presentationMeta: (_args, value) => value?.state === void 0 ? { gate: {} } : { gate: { state: value.state } }
1740
+ },
1741
+ async execute(args, exec) {
1742
+ const root = rootAgentOf(ctx, exec.agent);
1743
+ if (root?.id !== exec.agent?.id) return { ok: false, code: "root-only", reason: "gate_record \u53EA\u80FD\u7531 root agent \u8C03\u7528\uFF1Bworker \u5B8C\u6210\u5F53\u524D node \u540E\u8FD4\u56DE receipt\u3002" };
1744
+ const gate = resolveGate(ctx, exec.agent);
1745
+ const state = store.read(gate.root, exec.agent);
1746
+ const fail = (fields) => procedureFailure(state, reviewLimit, fields);
1747
+ if (state.phase === "docs") {
1748
+ if (args.node !== "docs") return fail({ code: "wrong-node", expected: "docs", got: args.node ?? null, reason: 'docs \u9636\u6BB5\u53EA\u80FD\u901A\u8FC7 gate_record({node:"docs"}) \u7ED3\u675F\u3002' });
1749
+ state.phase = "discuss";
1750
+ state.node = null;
1751
+ state.work = "active";
1752
+ state.blockReason = null;
1753
+ state.blockedFrom = null;
1754
+ store.write(gate.root, exec.agent, state);
1755
+ const procedure2 = procedureFor(state, { reviewLimit });
1756
+ return { ok: true, phase: state.phase, node: state.node, procedure: procedure2, method: procedure2.method, state: normalizeState(state) };
1757
+ }
1758
+ if (state.phase !== "orchestrating" && state.phase !== "reviewing") {
1759
+ return fail({ code: "not-orchestrating", phase: state.phase, stateSource: store.source, reason: `\u5F53\u524D phase \u662F ${state.phase}\uFF1B\u5148\u8FDB\u5165\u5BF9\u5E94\u9636\u6BB5\u3002` });
1760
+ }
1761
+ if (state.node !== args.node) return fail({ code: "wrong-node", expected: state.node ?? null, got: args.node ?? null, reason: `\u5F53\u524D\u8282\u70B9\u662F ${state.node ?? "\u65E0"}\uFF0C\u8BF7\u6C42\u7684 node \u662F ${args.node ?? "\u672A\u63D0\u4F9B"}\u3002` });
1762
+ const axesError = reviewAxesError(args);
1763
+ if (axesError !== void 0) return fail(axesError);
1764
+ const workerError = await workerEvidenceError(ctx, root, args);
1765
+ if (workerError !== void 0) return fail(workerError);
1766
+ const topFindings = Array.isArray(args.findings) ? args.findings : [];
1767
+ const axesFindings = REVIEW_NODES.has(args.node) ? [
1768
+ ...args.reviewAxes.standards.findings,
1769
+ ...args.reviewAxes.spec.findings,
1770
+ ...args.node === "plan-review" || args.node === "proposal-review" ? args.reviewAxes.architecture.findings : []
1771
+ ] : [];
1772
+ const findings = [...axesFindings, ...topFindings];
1773
+ let commitHint = null;
1774
+ if (args.requiresDiscussion === true) {
1775
+ if (args.node === "plan-review") state.planReady = false;
1776
+ state.blockedFrom = { phase: state.phase, node: state.node };
1777
+ state.phase = "blocked";
1778
+ state.node = null;
1779
+ state.work = "blocked";
1780
+ state.blockReason = "needs-user-decision";
1781
+ store.write(gate.root, exec.agent, state);
1782
+ const procedure2 = procedureFor(state, { reviewLimit });
1783
+ return { ok: true, phase: state.phase, blockReason: state.blockReason, reason: args.discussionReason ?? null, procedure: procedure2, method: procedure2.method, state: normalizeState(state) };
1784
+ }
1785
+ if (args.node === "proposal-review") {
1786
+ state.phase = "discuss";
1787
+ state.node = null;
1788
+ state.work = "active";
1789
+ state.blockReason = null;
1790
+ state.blockedFrom = null;
1791
+ } else if (args.node === "code-review-standalone") {
1792
+ state.phase = "discuss";
1793
+ state.node = null;
1794
+ state.work = "active";
1795
+ state.blockReason = null;
1796
+ } else if (args.node === "plan") {
1797
+ if (typeof args.task !== "string" || args.task.trim() === "") {
1798
+ return fail({ code: "task-required", reason: "node=plan \u5FC5\u987B\u643A\u5E26 task\uFF08planning \u4EA7\u51FA\u7684\u5DE5\u4F5C\u9879\u6216 mechanical:<\u8BF4\u660E>\uFF09\u3002" });
1799
+ }
1800
+ const decision = args.noteDecision;
1801
+ if (!["new", "update", "in-place", "mechanical"].includes(decision)) {
1802
+ return fail({ code: "note-decision-required", reason: "node=plan \u9700\u8981 noteDecision\uFF08new/update/in-place/mechanical\uFF09\u3002" });
1803
+ }
1804
+ if (decision === "mechanical" && !isMechanical(args.task)) {
1805
+ return fail({ code: "mechanical-required", reason: "mechanical \u53EA\u5141\u8BB8 task \u4EE5 mechanical: \u5F00\u5934\u3002" });
1806
+ }
1807
+ const optionNotePaths = notePathsForDecision(args.notePaths, decision);
1808
+ const noteError = validateNotePaths(gate.root, optionNotePaths, decision);
1809
+ if (noteError !== void 0) return fail(noteError);
1810
+ state.task = args.task.trim();
1811
+ state.notePaths = decision === "mechanical" ? [] : optionNotePaths;
1812
+ state.noteDecision = decision;
1813
+ state.noteSearch = typeof args.noteSearch === "string" ? args.noteSearch : null;
1814
+ state.noteVerified = decision === "mechanical";
1815
+ state.node = "plan-review";
1816
+ } else if (args.node === "plan-revise") {
1817
+ if (args.noteDecision !== void 0 || args.notePaths !== void 0) {
1818
+ const decision = args.noteDecision ?? state.noteDecision;
1819
+ if (!["new", "update", "in-place", "mechanical"].includes(decision)) {
1820
+ return fail({ code: "note-decision-required", reason: "plan-revise \u9700\u8981\u5408\u6CD5 noteDecision\u3002" });
1821
+ }
1822
+ if (decision === "mechanical" && !isMechanical(state.task)) {
1823
+ return fail({ code: "mechanical-required", reason: "mechanical \u53EA\u5141\u8BB8 task \u4EE5 mechanical: \u5F00\u5934\u3002" });
1824
+ }
1825
+ const requestedNotePaths = args.notePaths ?? state.notePaths;
1826
+ const optionNotePaths = notePathsForDecision(requestedNotePaths, decision);
1827
+ const noteError = validateNotePaths(gate.root, optionNotePaths, decision);
1828
+ if (noteError !== void 0) return fail(noteError);
1829
+ state.notePaths = decision === "mechanical" ? [] : optionNotePaths;
1830
+ state.noteDecision = decision;
1831
+ if (typeof args.noteSearch === "string") state.noteSearch = args.noteSearch;
1832
+ state.noteVerified = decision === "mechanical";
1833
+ }
1834
+ state.node = "plan-review";
1835
+ } else if (args.node === "plan-review") {
1836
+ state.rounds.planReview += 1;
1837
+ if (findings.length === 0) {
1838
+ state.phase = "discuss";
1839
+ state.node = null;
1840
+ state.planReady = true;
1841
+ state.work = "active";
1842
+ state.blockReason = null;
1843
+ commitHint = "\u5EFA\u8BAE\u63D0\u4EA4 planning baseline\uFF08Note + \u8BA1\u5212\uFF09";
1844
+ } else if (state.rounds.planReview >= reviewLimit) {
1845
+ state.exhausted.plan = true;
1846
+ state.blockedFrom = { phase: "discuss", node: null };
1847
+ state.phase = "blocked";
1848
+ state.node = null;
1849
+ state.work = "blocked";
1850
+ state.blockReason = "plan-review-rounds-exhausted";
1851
+ } else state.node = "plan-revise";
1852
+ } else if (args.node === "implement") {
1853
+ if (args.noteDecision === "mechanical") {
1854
+ state.noteDecision = "mechanical";
1855
+ state.notePaths = [];
1856
+ state.noteVerified = true;
1857
+ } else {
1858
+ if (Array.isArray(args.notePaths)) {
1859
+ const nextNotePaths = normalizeNotePaths(args.notePaths);
1860
+ const noteError = validateNotePaths(gate.root, nextNotePaths, args.noteDecision ?? state.noteDecision);
1861
+ if (noteError !== void 0) return fail(noteError);
1862
+ state.notePaths = nextNotePaths;
1863
+ }
1864
+ if (["new", "update", "in-place"].includes(args.noteDecision)) state.noteDecision = args.noteDecision;
1865
+ }
1866
+ state.node = "code-review";
1867
+ } else if (args.node === "implement-revise") state.node = "code-review";
1868
+ else if (args.node === "code-review") {
1869
+ state.rounds.codeReview += 1;
1870
+ if (findings.length === 0) state.node = "verify";
1871
+ else if (state.rounds.codeReview >= reviewLimit) {
1872
+ state.exhausted.code = true;
1873
+ state.blockedFrom = { phase: "discuss", node: null };
1874
+ state.phase = "blocked";
1875
+ state.node = null;
1876
+ state.work = "blocked";
1877
+ state.blockReason = "code-review-rounds-exhausted";
1878
+ } else state.node = "implement-revise";
1879
+ } else if (args.node === "verify" || args.node === "verify-standalone") {
1880
+ const verification = args.verification;
1881
+ const commands = Array.isArray(verification?.commands) ? verification.commands.filter((item) => typeof item === "string" && item.trim() !== "") : [];
1882
+ const evidence = Array.isArray(verification?.evidence) ? verification.evidence.filter((item) => typeof item === "string" && item.trim() !== "") : [];
1883
+ const toolResults = Array.isArray(verification?.toolResults) ? verification.toolResults.filter((item) => typeof item === "string" && item.trim() !== "") : [];
1884
+ const unbound = await bindToolResults(ctx, Array.isArray(args.workerSessionIds) ? args.workerSessionIds : [], toolResults);
1885
+ if (unbound !== void 0) return fail(unbound);
1886
+ const records = [];
1887
+ const invalidEvidence = [];
1888
+ for (const item of evidence) {
1889
+ const target = classifyPath(gate.workspace, item);
1890
+ try {
1891
+ if (target.outside || !existsSync4(target.absolute) || !lstatSync4(target.absolute).isFile()) {
1892
+ invalidEvidence.push(item);
1893
+ continue;
1894
+ }
1895
+ const bytes = readFileSync3(target.absolute);
1896
+ records.push({ path: target.relative, size: bytes.length, sha256: createHash("sha256").update(bytes).digest("hex") });
1897
+ } catch {
1898
+ invalidEvidence.push(item);
1899
+ }
1900
+ }
1901
+ if (commands.length === 0 || evidence.length === 0 || toolResults.length === 0 || invalidEvidence.length > 0 || records.length !== evidence.length || typeof verification?.passed !== "boolean") {
1902
+ return fail({ code: "verification-required", invalidEvidence, commands, evidence, toolResults, reason: "verify \u9700\u8981\u975E\u7A7A commands\u3001workspace \u5185\u7684\u666E\u901A evidence \u6587\u4EF6\u3001toolResults\uFF0C\u4EE5\u53CA\u660E\u786E\u7684 passed\u3002" });
1903
+ }
1904
+ state.verification = {
1905
+ commands,
1906
+ evidence: records,
1907
+ toolResults,
1908
+ passed: verification.passed,
1909
+ selfReported: false,
1910
+ executionBound: true,
1911
+ verifiedAt: (/* @__PURE__ */ new Date()).toISOString()
1912
+ };
1913
+ if (args.node === "verify-standalone") {
1914
+ state.phase = "discuss";
1915
+ state.node = null;
1916
+ state.work = "active";
1917
+ state.blockReason = null;
1918
+ state.blockedFrom = null;
1919
+ } else if (verification.passed !== true) {
1920
+ state.node = "implement-revise";
1921
+ state.noteVerified = false;
1922
+ } else if (state.noteDecision === "mechanical") {
1923
+ state.noteVerified = true;
1924
+ state.noteFinalized = true;
1925
+ commitHint = "\u5EFA\u8BAE\u63D0\u4EA4\u5B9E\u73B0";
1926
+ state.phase = "discuss";
1927
+ state.node = null;
1928
+ state.work = "active";
1929
+ state.blockReason = null;
1930
+ state.blockedFrom = null;
1931
+ state.task = null;
1932
+ state.planReady = false;
1933
+ } else {
1934
+ state.phase = "orchestrating";
1935
+ state.node = "note-lifecycle";
1936
+ state.work = "active";
1937
+ state.noteVerified = false;
1938
+ state.noteFinalized = false;
1939
+ state.noteFinalizeRecovery = [];
1940
+ }
1941
+ } else if (args.node === "fix-bug") {
1942
+ state.phase = "discuss";
1943
+ state.node = null;
1944
+ state.work = "active";
1945
+ state.blockReason = null;
1946
+ state.blockedFrom = null;
1947
+ state.task = null;
1948
+ state.planReady = false;
1949
+ } else if (args.node === "note-lifecycle") {
1950
+ if (state.noteVerified !== true) {
1951
+ return fail({ code: "note-lifecycle-incomplete", reason: "note-lifecycle \u9700\u8981\u5148 gate_note_finalize + gate_note_verify\u3002" });
1952
+ }
1953
+ commitHint = "\u5EFA\u8BAE\u63D0\u4EA4\u672C\u6B21\u5B9E\u73B0\u4E0E Note";
1954
+ state.phase = "discuss";
1955
+ state.node = null;
1956
+ state.work = "active";
1957
+ state.blockReason = null;
1958
+ state.blockedFrom = null;
1959
+ state.task = null;
1960
+ state.planReady = false;
1961
+ }
1962
+ store.write(gate.root, exec.agent, state);
1963
+ const next = normalizeState(state);
1964
+ const procedure = procedureFor(next, { reviewLimit });
1965
+ return { ok: true, phase: state.phase, node: state.node, rounds: state.rounds, blockReason: state.blockReason, verification: state.verification, procedure, method: procedure.method, state: next, ...commitHint === null ? {} : { commitHint } };
1966
+ }
1967
+ });
1968
+ }
1969
+
1970
+ // src/tools/status.ts
1971
+ function registerStatus(ctx, deps) {
1972
+ const { store, reviewLimit, wake } = deps;
1973
+ ctx.tools.register({
1974
+ name: "gate_status",
1975
+ description: "\u8BFB\u53D6 Gate \u5F53\u524D\u72B6\u6001\u4E0E\u5F53\u524D procedure\uFF08root\uFF09\u3002\u53EA\u8BFB\uFF1B\u6D3E\u53D1 worker \u524D\u3001\u63A8\u8FDB\u8282\u70B9\u524D\u3001compaction \u540E\u3001\u5931\u8D25\u540E\u6062\u590D\u65F6\u4F7F\u7528\u3002worker \u8C03\u7528\u8FD4\u56DE root \u72B6\u6001\u4F46\u4E0D\u8FD4\u56DE root procedure\u3002",
1976
+ parameters: { type: "object", additionalProperties: false, properties: {} },
1977
+ output: {
1978
+ schema: { type: "object", additionalProperties: true },
1979
+ render: (_args, value) => [{ type: "text", text: typeof value?.text === "string" ? value.text : JSON.stringify(value) }]
1980
+ },
1981
+ async execute(_args, exec) {
1982
+ const root = rootAgentOf(ctx, exec.agent);
1983
+ if (exec.agent?.session?.header?.origin === "subagent" && root?.id === exec.agent?.id) {
1984
+ return { ok: false, code: "root-unresolved", reason: "\u65E0\u6CD5\u89E3\u6790 worker \u7684 root session\uFF1B\u4E0D\u8FD4\u56DE worker \u81EA\u8EAB\u7684\u72B6\u6001\u3002" };
1985
+ }
1986
+ if (root?.session === void 0) {
1987
+ return { ok: false, code: "root-unresolved", reason: "root session \u4E0D\u5B8C\u6574\uFF1B\u65E0\u6CD5\u8BFB\u53D6 Gate \u72B6\u6001\u3002" };
1988
+ }
1989
+ const gate = resolveGate(ctx, exec.agent);
1990
+ if (gate.root === void 0) return { ok: false, code: "state-unavailable", reason: "\u65E0\u6CD5\u786E\u5B9A\u5DE5\u4F5C\u533A\u3002" };
1991
+ const state = store.read(gate.root, root);
1992
+ if (root?.id !== exec.agent?.id) {
1993
+ return { ok: true, procedure: null, method: null, text: statusText(state, reviewLimit) };
1994
+ }
1995
+ const procedure = procedureFor(state, { reviewLimit });
1996
+ const includeRounds = !procedure.hasRounds;
1997
+ return {
1998
+ ok: true,
1999
+ procedure,
2000
+ method: procedure.method,
2001
+ text: procedureResultText(procedure, statusText(state, reviewLimit, { includeRounds }))
2002
+ };
2003
+ }
2004
+ });
2005
+ }
2006
+
2007
+ // src/tools/notes.ts
2008
+ import { existsSync as existsSync5, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "node:fs";
2009
+ import path10 from "node:path";
2010
+ function registerNoteTools(ctx, deps) {
2011
+ const { store, reviewLimit, wake } = deps;
2012
+ ctx.tools.register({
2013
+ name: "gate_note_write",
2014
+ description: "\u5199 proposed Agent Note\uFF0C\u6216\u5728 planning \u539F\u5730\u66F4\u65B0\u5DF2\u5B58\u5728\u7684 implemented Note\u3002\u4EC5 planning worker\uFF1B\u901A\u7528 write/edit/shell \u88AB\u9650\u5236\u3002",
2015
+ parameters: {
2016
+ type: "object",
2017
+ additionalProperties: false,
2018
+ properties: {
2019
+ path: { type: "string", description: "\u76F8\u5BF9 .agents/notes \u7684\u8DEF\u5F84\uFF1Bproposed/ \u53EF\u65B0\u5EFA\uFF0Cimplemented/ \u53EA\u80FD\u7F16\u8F91\u5DF2\u5B58\u5728\u6587\u4EF6\u3002" },
2020
+ content: { type: "string" }
2021
+ },
2022
+ required: ["path", "content"]
2023
+ },
2024
+ output: {
2025
+ schema: { type: "object", additionalProperties: true },
2026
+ render: (_args, value) => [{ type: "text", text: JSON.stringify(value) }]
2027
+ },
2028
+ async execute(args, exec) {
2029
+ const gate = resolveGate(ctx, exec.agent);
2030
+ const effective = effectiveState(ctx, exec.agent, gate, store);
2031
+ if (!effective.worker || !["plan", "plan-revise"].includes(effective.state.node ?? "")) {
2032
+ return { ok: false, code: "planning-only", reason: "gate_note_write \u53EA\u5141\u8BB8 planning worker \u5728 plan/plan-revise \u8282\u70B9\u4F7F\u7528\u3002" };
2033
+ }
2034
+ let relative = String(args.path ?? "").replaceAll("\\", "/").replace(/^\.?\//u, "");
2035
+ if (relative === "" || relative.includes("..") || path10.isAbsolute(relative) || !relative.endsWith(".md")) {
2036
+ return { ok: false, code: "invalid-path" };
2037
+ }
2038
+ let lifecycle = "proposed";
2039
+ if (relative.startsWith("proposed/")) relative = relative.slice("proposed/".length);
2040
+ else if (relative.startsWith("implemented/")) {
2041
+ lifecycle = "implemented";
2042
+ relative = relative.slice("implemented/".length);
2043
+ }
2044
+ if (!/^(?:feature|bug-fix|simplification|architecture|process|testing)\/\d{4}-\d{2}-\d{2}-.+\.md$/u.test(relative)) {
2045
+ return { ok: false, code: "invalid-path", reason: "\u9700\u8981 <class>/yyyy-mm-dd-topic.md\uFF0C\u6216 proposed|implemented/<class>/...\u3002" };
2046
+ }
2047
+ const base = path10.join(gate.root, ".agents", "notes", String(lifecycle));
2048
+ const absolute = path10.resolve(base, relative);
2049
+ if (!isInside(base, absolute)) return { ok: false, code: "invalid-path" };
2050
+ if (pathHasSymlink(base, absolute)) return { ok: false, code: "invalid-path" };
2051
+ if (lifecycle === "implemented" && !existsSync5(absolute)) {
2052
+ return { ok: false, code: "implemented-must-exist", reason: "implemented Note \u53EA\u80FD\u539F\u5730\u7F16\u8F91\uFF0C\u4E0D\u80FD\u65B0\u5EFA\u3002" };
2053
+ }
2054
+ mkdirSync4(path10.dirname(absolute), { recursive: true });
2055
+ writeFileSync4(absolute, String(args.content ?? ""));
2056
+ return { ok: true, path: path10.relative(gate.root, absolute).replaceAll("\\", "/"), lifecycle };
2057
+ }
2058
+ });
2059
+ ctx.tools.register({
2060
+ name: "gate_note_verify",
2061
+ description: "\u8FD0\u884C\u5185\u7F6E notes \u6821\u9A8C\uFF1Bnote-lifecycle \u4E2D\u901A\u8FC7\u540E\u8BBE\u7F6E noteVerified\u3002root-only\u3002",
2062
+ parameters: { type: "object", additionalProperties: false, properties: {} },
2063
+ output: {
2064
+ schema: { type: "object", additionalProperties: true },
2065
+ render: (_args, value) => [{ type: "text", text: JSON.stringify(value) }],
2066
+ presentationMeta: (_args, value) => value?.state === void 0 ? { gate: {} } : { gate: { state: value.state } }
2067
+ },
2068
+ async execute(_args, exec) {
2069
+ const root = rootAgentOf(ctx, exec.agent);
2070
+ if (root?.id !== exec.agent?.id) return { ok: false, code: "root-only", reason: "gate_note_verify \u53EA\u80FD\u7531 root agent \u8C03\u7528\u3002" };
2071
+ const gate = resolveGate(ctx, exec.agent);
2072
+ const state = store.read(gate.root, exec.agent);
2073
+ const recovered = recoverFinalize(gate.root, String(exec.agent?.session?.id ?? exec.agent?.id ?? "unknown"));
2074
+ if (recovered.length > 0) state.noteFinalizeRecovery = [...state.noteFinalizeRecovery, ...recovered];
2075
+ const check = runNotesScripts(gate.root);
2076
+ if (!check.ok) return { ...check, state: normalizeState(state) };
2077
+ if (state.node === "note-lifecycle") {
2078
+ if (state.noteFinalized !== true) return { ok: false, code: "note-lifecycle-incomplete", reason: "\u5148\u8C03\u7528 gate_note_finalize\u3002", state: normalizeState(state) };
2079
+ const implemented = state.notePaths.length > 0 && state.notePaths.every((entry) => entry.lifecycle === "implemented" && existsSync5(path10.join(gate.root, entry.path)));
2080
+ if (!implemented) return { ok: false, code: "note-lifecycle-incomplete", reason: "notePaths \u5C1A\u672A\u5168\u90E8\u8FDB\u5165 implemented\u3002", results: check.results, state: normalizeState(state) };
2081
+ state.noteVerified = true;
2082
+ store.write(gate.root, exec.agent, state);
2083
+ return { ok: true, noteVerified: true, results: check.results, state: normalizeState(state) };
2084
+ }
2085
+ return { ok: true, noteVerified: false, results: check.results, state: normalizeState(state) };
2086
+ }
2087
+ });
2088
+ ctx.tools.register({
2089
+ name: "gate_note_finalize",
2090
+ description: "\u628A note-lifecycle \u4E2D\u7684 proposed Note \u79FB\u5230 implemented \u5E76\u66F4\u65B0\u5934\u5757\uFF1Broot-only\u3002",
2091
+ parameters: {
2092
+ type: "object",
2093
+ additionalProperties: false,
2094
+ properties: {
2095
+ decisionBodies: {
2096
+ type: "array",
2097
+ items: {
2098
+ type: "object",
2099
+ additionalProperties: false,
2100
+ properties: { path: { type: "string" }, body: { type: "string" } },
2101
+ required: ["path", "body"]
2102
+ }
2103
+ }
2104
+ }
2105
+ },
2106
+ output: {
2107
+ schema: { type: "object", additionalProperties: true },
2108
+ render: (_args, value) => [{ type: "text", text: JSON.stringify(value) }],
2109
+ presentationMeta: (_args, value) => value?.state === void 0 ? { gate: {} } : { gate: { state: value.state } }
2110
+ },
2111
+ async execute(args, exec) {
2112
+ const root = rootAgentOf(ctx, exec.agent);
2113
+ if (root?.id !== exec.agent?.id) return { ok: false, code: "root-only", reason: "gate_note_finalize \u53EA\u80FD\u7531 root agent \u8C03\u7528\u3002" };
2114
+ const gate = resolveGate(ctx, exec.agent);
2115
+ const state = store.read(gate.root, exec.agent);
2116
+ if (state.node !== "note-lifecycle") return { ok: false, code: "not-note-lifecycle", reason: "\u53EA\u5728 note-lifecycle \u8282\u70B9\u53EF\u7528\u3002" };
2117
+ if (state.noteDecision === "mechanical") {
2118
+ state.noteVerified = true;
2119
+ state.noteFinalized = true;
2120
+ store.write(gate.root, exec.agent, state);
2121
+ return { ok: true, skipped: true, state: normalizeState(state) };
2122
+ }
2123
+ const sessionId = String(exec.agent?.session?.id ?? exec.agent?.id ?? "unknown");
2124
+ const recovered = recoverFinalize(gate.root, sessionId);
2125
+ for (const entry of state.notePaths) {
2126
+ if (entry.lifecycle !== "proposed") continue;
2127
+ const source = path10.join(gate.root, entry.path);
2128
+ const targetRelative = entry.path.replaceAll("\\", "/").replace(".agents/notes/proposed/", ".agents/notes/implemented/");
2129
+ if (!existsSync5(source) && existsSync5(path10.join(gate.root, targetRelative))) {
2130
+ entry.path = targetRelative;
2131
+ entry.lifecycle = "implemented";
2132
+ recovered.push("adopted-target");
2133
+ }
2134
+ }
2135
+ const moved = [];
2136
+ const decisionBodies = new Map((Array.isArray(args.decisionBodies) ? args.decisionBodies : []).map((item) => [String(item.path).replaceAll("\\", "/"), String(item.body ?? "")]));
2137
+ try {
2138
+ for (const entry of state.notePaths) {
2139
+ if (entry.lifecycle !== "proposed") continue;
2140
+ const original = entry.path.replaceAll("\\", "/");
2141
+ const relative = finalizeNote(gate.root, entry, sessionId, decisionBodies.get(original));
2142
+ moved.push({ from: original, to: relative });
2143
+ entry.path = relative;
2144
+ entry.lifecycle = "implemented";
2145
+ }
2146
+ const finalCheck = runNotesScripts(gate.root);
2147
+ if (!finalCheck.ok) throw new Error(`finalize/decisionBodies \u540E\u7684 notes \u6821\u9A8C\u5931\u8D25\uFF1A${JSON.stringify(finalCheck.results)}`);
2148
+ state.noteFinalizeRecovery = recovered;
2149
+ state.noteVerified = false;
2150
+ state.noteFinalized = true;
2151
+ store.write(gate.root, exec.agent, state);
2152
+ return { ok: true, moved, recovered, state: normalizeState(state) };
2153
+ } catch (error) {
2154
+ state.noteFinalizeRecovery = recovered;
2155
+ store.write(gate.root, exec.agent, state);
2156
+ return { ok: false, code: "note-finalize-failed", reason: error instanceof Error ? error.message : String(error), moved, recovered, state: normalizeState(state) };
2157
+ }
2158
+ }
2159
+ });
2160
+ }
2161
+
2162
+ // src/tools/index.ts
2163
+ function registerTools(ctx, deps) {
2164
+ registerStartPhase(ctx, deps);
2165
+ registerRecord(ctx, deps);
2166
+ registerStatus(ctx, deps);
2167
+ registerNoteTools(ctx, deps);
2168
+ }
2169
+
2170
+ // src/index.ts
2171
+ var inject = ["tools"];
2172
+ function apply(ctx, config = {}) {
2173
+ const reviewLimit = Number.isInteger(config?.reviewLimit) ? config.reviewLimit : DEFAULT_REVIEW_LIMIT;
2174
+ const store = createStateRuntime(ctx);
2175
+ const wake = makeWake(reviewLimit);
2176
+ const deps = { ctx, store, reviewLimit, wake };
2177
+ installSystemPrompt(ctx, deps);
2178
+ installGuard(ctx, deps);
2179
+ installRestrictions(ctx, deps);
2180
+ registerCommands(ctx, deps);
2181
+ registerTools(ctx, deps);
2182
+ }
2183
+ export {
2184
+ Config,
2185
+ DISCUSS_ORCHESTRATION,
2186
+ DOCS_ORCHESTRATION,
2187
+ FIX_BUG_ORCHESTRATION,
2188
+ GATE_RECORD_NODES,
2189
+ IMPLEMENT_ORCHESTRATION,
2190
+ NODE_METHODS,
2191
+ NOTE_LIFECYCLE_ORCHESTRATION,
2192
+ PHASES,
2193
+ PLAN_ORCHESTRATION,
2194
+ WORKER_NODES,
2195
+ apply,
2196
+ defaultState,
2197
+ inject,
2198
+ loadState,
2199
+ normalizeState,
2200
+ saveState,
2201
+ transformImplemented
2202
+ };