dsh-gate-agent 0.1.2 → 0.1.4

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.
package/lib/index.js CHANGED
@@ -36,18 +36,36 @@ var Config = {
36
36
  }
37
37
  };
38
38
 
39
+ // src/note-decisions.ts
40
+ var MECHANICAL_DECISION = "mechanical";
41
+ var NOTE_DECISIONS = ["new", "update", "in-place", MECHANICAL_DECISION];
42
+ function isNoteDecision(value) {
43
+ return typeof value === "string" && NOTE_DECISIONS.includes(value);
44
+ }
45
+ var NOTE_DECISION_LIFECYCLE = {
46
+ new: "proposed",
47
+ update: "proposed",
48
+ "in-place": "implemented",
49
+ mechanical: null
50
+ };
51
+ function isMechanicalDecision(value) {
52
+ return value === MECHANICAL_DECISION;
53
+ }
54
+
39
55
  // src/protocol.ts
40
- var DISCUSS_ORCHESTRATION = `## Discuss
56
+ var DISCUSS_ORCHESTRATION = `## discuss
41
57
 
42
58
  discuss \u662F\u9ED8\u8BA4\u9636\u6BB5\uFF0C\u4E0D\u9700\u8981\u547D\u4EE4\u8FDB\u5165\u3002
43
59
 
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`;
60
+ \u8BA8\u8BBA\u4E0D\u662F\u88AB\u52A8\u8BBF\u8C08\uFF1B\u4F18\u5148\u81EA\u884C\u5206\u6790\u5DF2\u6709\u9879\u76EE\u4E0A\u4E0B\u6587\uFF0C\u4E0D\u628A\u53EF\u5224\u65AD\u7684\u95EE\u9898\u4EA4\u7ED9\u7528\u6237\u3002\u4E0D\u8981\u8981\u6C42\u7528\u6237\u91CD\u590D\u5DF2\u6709\u4FE1\u606F\uFF0C\u4E5F\u4E0D\u8981\u6C42\u5728 discuss \u4E2D\u786E\u5B9A\u6240\u6709\u5B9E\u73B0\u7EC6\u8282\u3002
61
+
62
+ - \u5148\u8BFB\u76F8\u5173\u4EE3\u7801\u3001\u6587\u6863\u3001\u5386\u53F2 Note \u4E0E\u9879\u76EE\u7EA6\u675F\u3002
63
+ - \u660E\u786E\u5B9E\u9645\u95EE\u9898\u3001\u7EA6\u675F\u3001\u5DF2\u6709\u51B3\u5B9A\u4E0E\u51B2\u7A81\u3002
64
+ - \u7ED9\u51FA\u6709\u5B9E\u8D28\u5DEE\u5F02\u7684\u53EF\u884C\u65B9\u6848\u3001\u63A8\u8350\u4E0E\u5173\u952E\u6743\u8861\u3002
65
+ - \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
66
+ - \u4E0D\u521B\u5EFA\u63D0\u6848\u3001Note \u6216\u89C4\u683C\u6587\u4EF6\uFF1B\u6587\u4EF6\u9ED8\u8BA4\u53EA\u8BFB\uFF0C\u4E0D\u5199\u5DE5\u4F5C\u533A\u3002\u63D0\u4EA4\u5DF2\u6709\u6539\u52A8\uFF08git add/commit/push\uFF09\u4E0D\u53D7\u6587\u4EF6\u53EA\u8BFB\u5F71\u54CD\u3002
67
+ - \u6C47\u62A5\uFF1A\u5DF2\u89E3\u51B3\u4E8B\u9879\u3001\u5269\u4F59\u672A\u51B3\u4E8B\u9879\u3001\u5EFA\u8BAE\u7684\u4E0B\u4E00\u9636\u6BB5\u3002
68
+ - \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\u4E2D\u8981\u6C42\u7528\u6237\u518D\u8F93\u5165 slash command\u6216 ask_user_question \u4E2D\u7ED9\u51FA\uFF1B\u83B7\u5F97\u6388\u6743\u540E\u8FDB\u5165 plan/implement\u3002`;
51
69
  var PLAN_ORCHESTRATION = `## /gate-enter-plan \u7F16\u6392
52
70
  \u4E3B\u4EE3\u7406\u6D3E worker\uFF1Bgate \u4E0D\u6D3E\u3002
53
71
 
@@ -57,7 +75,7 @@ var PLAN_ORCHESTRATION = `## /gate-enter-plan \u7F16\u6392
57
75
  \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
76
  \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
77
  \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
78
+ 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 \u7528 noteDecision:'mechanical' \u4E14 notePaths \u4E3A\u7A7A\uFF1B\u4E0D\u9700\u8981 task \u524D\u7F00\u3002
61
79
 
62
80
  ## Step 2 \u2014 Plan Review
63
81
  \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
@@ -134,10 +152,10 @@ var NODE_INFO = {
134
152
  var NODE_METHODS = Object.fromEntries(Object.entries(NODE_INFO).map(([node, info]) => [node, info.method]));
135
153
  var WORKER_NODES = new Set(GATE_RECORD_NODES.filter((node) => node !== "docs" && node !== "note-lifecycle"));
136
154
  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`;
155
+ - \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
156
+ - \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
157
+ - 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
158
+ - \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
159
  var REVIEWING_CODE_PROCEDURE = `## /gate-implement-review \u7F16\u6392
142
160
  \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
161
  var REVIEWING_PROPOSAL_PROCEDURE = `## /gate-propose-review \u7F16\u6392
@@ -171,44 +189,80 @@ var NODE_PROCEDURES = {
171
189
  "proposal-review": REVIEWING_PROPOSAL_PROCEDURE,
172
190
  "verify-standalone": REVIEWING_VERIFY_PROCEDURE
173
191
  };
192
+ var METHODOLOGY_KEYS = [
193
+ "discuss",
194
+ "orchestrating/plan",
195
+ "orchestrating/plan-review",
196
+ "orchestrating/plan-revise",
197
+ "orchestrating/implement",
198
+ "orchestrating/code-review",
199
+ "orchestrating/implement-revise",
200
+ "orchestrating/verify",
201
+ "orchestrating/note-lifecycle",
202
+ "orchestrating/fix-bug",
203
+ "docs",
204
+ "reviewing/code-review-standalone",
205
+ "reviewing/proposal-review",
206
+ "reviewing/verify-standalone",
207
+ "blocked",
208
+ "complete",
209
+ "orchestrating",
210
+ "reviewing"
211
+ ];
212
+ var METHODOLOGY_KEY_SET = new Set(METHODOLOGY_KEYS);
213
+ function methodologyKeyFor(state) {
214
+ if (state.phase === "orchestrating" || state.phase === "reviewing") {
215
+ const key = state.node === null ? state.phase : `${state.phase}/${state.node}`;
216
+ return METHODOLOGY_KEY_SET.has(key) ? key : state.phase;
217
+ }
218
+ return state.phase;
219
+ }
174
220
  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
221
+ \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\u4E0D\u786E\u5B9A\u6709\u54EA\u4E9B /gate-* \u547D\u4EE4\u6216\u5DE5\u5177\u7528\u9014\u65F6\uFF0C\u8C03\u7528\u53EA\u8BFB gate_help\u3002
176
222
 
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
223
+ ${DISCUSS_ORCHESTRATION}
179
224
 
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
225
+ ## orchestrating/plan \xB7 orchestrating/plan-review \xB7 orchestrating/plan-revise
226
+ \u628A\u8BA8\u8BBA\u5171\u8BC6\u3001\u65E2\u6709\u51B3\u7B56\u4E0E\u7528\u6237\u9700\u6C42\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
227
 
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
228
+ ## orchestrating/implement \xB7 orchestrating/implement-revise
229
+ \u6309\u5DF2\u786E\u8BA4\u7684\u8BA1\u5212\u6216\u8BA8\u8BBA\u540E\u7684\u5B9E\u65BD\u8BF4\u660E\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
230
 
186
- ## code-review
231
+ ## orchestrating/code-review
187
232
  \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
233
 
189
- ## verify
234
+ ## orchestrating/verify
190
235
  \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
236
 
192
- ## note-lifecycle
237
+ ## orchestrating/note-lifecycle
193
238
  \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
239
 
195
- ## fix-bug
240
+ ## orchestrating/fix-bug
196
241
  \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
242
 
198
243
  ## docs
199
244
  \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
245
 
201
- ## reviewing-code
246
+ ## reviewing/code-review-standalone
202
247
  \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
248
 
204
- ## reviewing-proposal
249
+ ## reviewing/proposal-review
205
250
  \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
251
 
207
- ## reviewing-verify
252
+ ## reviewing/verify-standalone
208
253
  \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
254
 
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`;
255
+ ## blocked
256
+ blocked \u8868\u793A\u51BB\u7ED3\uFF1A\u7B49\u5F85 /gate-resume\u3002
257
+
258
+ ## complete
259
+ complete \u8868\u793A\u5F53\u524D\u4EFB\u52A1\u5B8C\u6210\uFF0C\u65B0\u4EFB\u52A1\u9700\u8981\u91CD\u65B0\u6388\u6743\u3002
260
+
261
+ ## orchestrating
262
+ \u5F53\u524D\u5904\u4E8E orchestrating \u4F46\u6CA1\u6709\u5339\u914D node\uFF1B\u8C03\u7528 gate_status \u91CD\u65B0\u8BFB\u53D6\uFF1B\u82E5\u4ECD\u4E0D\u4E00\u81F4\uFF0C\u56DE discuss \u4FEE\u590D Gate \u72B6\u6001\u3002
263
+
264
+ ## reviewing
265
+ \u5F53\u524D\u5904\u4E8E reviewing \u4F46\u6CA1\u6709\u5339\u914D node\uFF1B\u8C03\u7528 gate_status \u91CD\u65B0\u8BFB\u53D6\uFF1B\u82E5\u4ECD\u4E0D\u4E00\u81F4\uFF0C\u56DE discuss \u4FEE\u590D Gate \u72B6\u6001\u3002`;
212
266
  function protocolVariant(state) {
213
267
  if (state.phase === "orchestrating") return NODE_INFO[state.node ?? ""]?.variant ?? state.phase;
214
268
  if (state.phase === "reviewing") return NODE_INFO[state.node ?? ""]?.variant ?? "reviewing-code";
@@ -219,9 +273,9 @@ function procedureFor(state, { reviewLimit = DEFAULT_REVIEW_LIMIT } = {}) {
219
273
  const method = NODE_METHODS[state.node ?? ""] ?? "\u65E0";
220
274
  let text;
221
275
  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";
276
+ text = "discuss \u4E0D\u6D3E worker\uFF1B\u6587\u4EF6\u9ED8\u8BA4\u53EA\u8BFB\uFF0C\u63D0\u4EA4\u5DF2\u6709\u6539\u52A8\u4E0D\u53D7\u5F71\u54CD\uFF1B\u7528\u6237\u6388\u6743\u540E\u8C03\u7528 gate_start_phase \u8FDB\u5165 plan/implement\u3002";
223
277
  } 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`;
278
+ 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\u3002`;
225
279
  } else if (variant === "complete") {
226
280
  text = "Gate \u5DF2\u5B8C\u6210\uFF1B\u65B0\u4EFB\u52A1\u9700\u8981\u91CD\u65B0\u6388\u6743\u3002";
227
281
  } else if (variant === "docs") {
@@ -239,7 +293,7 @@ function procedureFor(state, { reviewLimit = DEFAULT_REVIEW_LIMIT } = {}) {
239
293
  text = [text, `## \u5F53\u524D\u8F6E\u6B21
240
294
  ${roundsText(state, reviewLimit)}`].filter(Boolean).join("\n\n");
241
295
  }
242
- return { phase: state.phase, node: state.node, method, text, hasRounds: variant === "plan" || variant === "implement" };
296
+ return { phase: state.phase, node: state.node, methodologyKey: methodologyKeyFor(state), method, text, hasRounds: variant === "plan" || variant === "implement" };
243
297
  }
244
298
  function procedureFailure(state, reviewLimit, fields) {
245
299
  const procedure = procedureFor(state, { reviewLimit });
@@ -271,7 +325,7 @@ function statusText(state, reviewLimit = DEFAULT_REVIEW_LIMIT, { includeRounds =
271
325
  `planReady\uFF1A${state.planReady ? "\u662F" : "\u5426"}`,
272
326
  ...includeRounds ? [`rounds\uFF1A${roundsText(state, reviewLimit)}`] : []
273
327
  ];
274
- if (state.notePaths?.length > 0 || state.noteDecision === "mechanical") {
328
+ if (state.notePaths?.length > 0 || isMechanicalDecision(state.noteDecision)) {
275
329
  lines.push(`notes\uFF1A${state.noteDecision ?? "\u65E0"} ${state.notePaths?.length ?? 0} \u7BC7\uFF0Cverified=${state.noteVerified === true ? "\u662F" : "\u5426"}`);
276
330
  }
277
331
  if (state.exhausted?.plan === true || state.exhausted?.code === true) {
@@ -284,6 +338,7 @@ function statusText(state, reviewLimit = DEFAULT_REVIEW_LIMIT, { includeRounds =
284
338
  }
285
339
  function procedureBlock(procedure) {
286
340
  return `\u5F53\u524D phase/node\uFF1A${procedure.phase} / ${procedure.node ?? "\u65E0"}
341
+ \u65B9\u6CD5\u8BBA\uFF1A${procedure.methodologyKey}
287
342
  \u65B9\u6CD5\uFF1A${procedure.method}
288
343
 
289
344
  ${procedure.text}`;
@@ -356,7 +411,7 @@ function normalizeState(value) {
356
411
  verification: source.verification !== null && typeof source.verification === "object" && !Array.isArray(source.verification) ? clone(source.verification) : null,
357
412
  blockedFrom,
358
413
  notePaths: Array.isArray(source.notePaths) ? source.notePaths.map((entry) => entry !== null && typeof entry === "object" && !Array.isArray(entry) ? { ...entry } : entry) : [],
359
- noteDecision: textOrNull(source.noteDecision),
414
+ noteDecision: isNoteDecision(source.noteDecision) ? source.noteDecision : null,
360
415
  noteSearch: textOrNull(source.noteSearch),
361
416
  noteVerified: source.noteVerified === true,
362
417
  noteFinalized: source.noteFinalized === true,
@@ -652,10 +707,10 @@ function normalizeNotePaths(value) {
652
707
  });
653
708
  }
654
709
  function notePathsForDecision(value, decision) {
655
- return decision === "mechanical" ? value : normalizeNotePaths(value);
710
+ return isMechanicalDecision(decision) ? value : normalizeNotePaths(value);
656
711
  }
657
712
  function validateNotePaths(root, value, decision) {
658
- if (decision === "mechanical") {
713
+ if (isMechanicalDecision(decision)) {
659
714
  if (!Array.isArray(value) || value.length !== 0) return { code: "note-paths-required", reason: "mechanical \u7684 notePaths \u5FC5\u987B\u4E3A\u7A7A\u3002" };
660
715
  return void 0;
661
716
  }
@@ -667,9 +722,10 @@ function validateNotePaths(root, value, decision) {
667
722
  }
668
723
  if (!NOTE_CLASSES.has(entry.class)) return { code: "invalid-note-class", class: entry.class };
669
724
  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" };
725
+ const expectedLifecycle = NOTE_DECISION_LIFECYCLE[decision];
726
+ if (expectedLifecycle !== null && entry.lifecycle !== expectedLifecycle) {
727
+ return { code: "note-decision-mismatch", reason: `${decision} \u53EA\u80FD ${expectedLifecycle}\u3002` };
728
+ }
673
729
  const normalized = entry.path.replaceAll("\\", "/");
674
730
  if (normalized.startsWith("/") || normalized.includes("..")) return { code: "invalid-note-path", path: entry.path };
675
731
  const parts = normalized.split("/");
@@ -881,10 +937,148 @@ function effectiveState(ctx, agent, gate, store) {
881
937
  return { root, state: store.read(gate.root, root), worker: root?.id !== agent?.id };
882
938
  }
883
939
 
884
- // src/runtime/gate.ts
885
- function isMechanical(task) {
886
- return /^mechanical:/iu.test(String(task ?? "").trim());
940
+ // src/classify.ts
941
+ import path6 from "node:path";
942
+ function classifyPath(root, value, cwd = root) {
943
+ const absolute = path6.resolve(cwd, String(value ?? ""));
944
+ const relative = path6.relative(root, absolute).replaceAll("\\", "/");
945
+ const outside = relative === ".." || relative.startsWith("../") || path6.isAbsolute(relative);
946
+ return { absolute, relative, outside };
947
+ }
948
+ var GIT_VALUE_OPTIONS = /* @__PURE__ */ new Set(["-C", "-c", "--git-dir", "--work-tree", "--namespace", "--config-env"]);
949
+ var GIT_FILE_WRITE = /* @__PURE__ */ new Set(["checkout", "switch", "reset", "clean", "restore", "stash", "merge", "rebase", "cherry-pick", "apply", "pull"]);
950
+ var GIT_NON_WRITE = /* @__PURE__ */ new Set(["status", "add", "commit", "push"]);
951
+ function gitSubcommands(command2) {
952
+ const subcommands = /* @__PURE__ */ new Set();
953
+ for (const segment of String(command2).split(/&&|\|\||;|\r?\n|\|/u)) {
954
+ const tokens = [...segment.matchAll(/"([^"]*)"|'([^']*)'|(\S+)/gu)].map((match) => match[1] ?? match[2] ?? match[3] ?? "");
955
+ const gitIndex = tokens.findIndex((token) => /^git(?:\.exe)?$/iu.test(token));
956
+ if (gitIndex < 0) continue;
957
+ let index = gitIndex + 1;
958
+ while (index < tokens.length) {
959
+ const token = tokens[index] ?? "";
960
+ if (token === "--") {
961
+ index += 1;
962
+ break;
963
+ }
964
+ if (!token.startsWith("-")) break;
965
+ const option = token.split("=", 1)[0] ?? token;
966
+ if (GIT_VALUE_OPTIONS.has(option) && !token.includes("=")) index += 2;
967
+ else index += 1;
968
+ }
969
+ const subcommand = tokens[index]?.toLowerCase();
970
+ if (subcommand !== void 0) subcommands.add(subcommand);
971
+ }
972
+ return subcommands;
887
973
  }
974
+ 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;
975
+ function pathArgs(text) {
976
+ const beforePipe = String(text).split("|")[0] ?? "";
977
+ return [...beforePipe.matchAll(/(?:'([^']+)'|"([^"]+)"|(\S+))/gu)].map((match) => match[1] ?? match[2] ?? match[3]).filter((value) => value !== void 0 && !value.startsWith("-"));
978
+ }
979
+ function assignments(text) {
980
+ const map = /* @__PURE__ */ new Map();
981
+ for (const match of String(text).matchAll(/(?:^|[;\n])\s*\$?([A-Za-z_]\w*)\s*=\s*(?:'([^']*)'|"([^"]*)"|(\S+))/gu)) {
982
+ const key = match[1];
983
+ if (key !== void 0) map.set(key, match[2] ?? match[3] ?? match[4] ?? "");
984
+ }
985
+ return map;
986
+ }
987
+ function resolveToken(token, map) {
988
+ const variable = /^\$([A-Za-z_]\w*)$/u.exec(token);
989
+ if (variable === null) return token;
990
+ const key = variable[1];
991
+ return (key === void 0 ? token : map.get(key)) ?? token;
992
+ }
993
+ function redirectionTargets(text) {
994
+ return [...String(text).matchAll(/(?:^|\s)>{1,2}\s*(?:'([^']+)'|"([^"]+)"|([^\s;&|]+))/gu)].map((match) => match[1] ?? match[2] ?? match[3]).filter((value) => value !== void 0);
995
+ }
996
+ function pipeTargets(text) {
997
+ const targets = [];
998
+ for (const part of String(text).split("|").slice(1)) {
999
+ const body = part.replace(/^\s*(?:tee|Set-Content|Out-File|Add-Content)\b/iu, "");
1000
+ if (body === part) continue;
1001
+ for (const token of pathArgs(body)) targets.push(token);
1002
+ }
1003
+ return targets;
1004
+ }
1005
+ function addTarget(targets, token, map) {
1006
+ if (token === void 0 || token === "") return;
1007
+ if (token.startsWith("$")) {
1008
+ const resolved = resolveToken(token, map);
1009
+ if (resolved !== token) targets.push(resolved);
1010
+ return;
1011
+ }
1012
+ targets.push(token);
1013
+ }
1014
+ function segmentAccess(text, root, cwd, map) {
1015
+ const git = gitSubcommands(text);
1016
+ if ([...git].some((command2) => GIT_FILE_WRITE.has(command2))) return { write: true, outsideTargets: [] };
1017
+ if ([...git].some((command2) => GIT_NON_WRITE.has(command2))) return { write: false, outsideTargets: [] };
1018
+ const raw = [];
1019
+ const verb = WRITE_VERB.exec(text);
1020
+ if (verb !== null) {
1021
+ const tail = text.slice(verb.index + verb[0].length);
1022
+ for (const token of pathArgs(tail)) addTarget(raw, token, map);
1023
+ }
1024
+ for (const token of redirectionTargets(text)) addTarget(raw, token, map);
1025
+ for (const token of pipeTargets(text)) addTarget(raw, token, map);
1026
+ const outsideTargets = [];
1027
+ for (const value of raw) {
1028
+ const target = classifyPath(root, path6.resolve(cwd, value));
1029
+ if (target.outside) outsideTargets.push(target.absolute);
1030
+ }
1031
+ return { write: raw.length > 0 || WRITE_VERB.test(text), outsideTargets };
1032
+ }
1033
+ function segments(text) {
1034
+ return String(text).split(/&&|\|\||;|\r?\n/u).map((part) => part.trim()).filter(Boolean);
1035
+ }
1036
+ function shellAccess(command2, root, cwd = root) {
1037
+ const map = assignments(command2);
1038
+ const parts = segments(command2).map((part) => segmentAccess(part, root, cwd, map)).filter((item) => item.write);
1039
+ return {
1040
+ write: parts.length > 0,
1041
+ outsideTargets: parts.flatMap((item) => item.outsideTargets ?? [])
1042
+ };
1043
+ }
1044
+
1045
+ // src/access.ts
1046
+ function accessFor(state, worker) {
1047
+ if (state.phase === "blocked") {
1048
+ return { read: true, write: "none", noteWrite: false, shell: "read", repoWrite: worker ? "none" : "push" };
1049
+ }
1050
+ if (!worker) {
1051
+ if (state.phase === "discuss") {
1052
+ return { read: true, write: "none", noteWrite: false, shell: "read", repoWrite: "push" };
1053
+ }
1054
+ return {
1055
+ read: true,
1056
+ write: "workspace",
1057
+ noteWrite: state.node === "note-lifecycle",
1058
+ shell: "write",
1059
+ repoWrite: "push"
1060
+ };
1061
+ }
1062
+ if (state.node === "plan" || state.node === "plan-revise") {
1063
+ return { read: true, write: "none", noteWrite: true, shell: "read", repoWrite: "none" };
1064
+ }
1065
+ if (state.node === "implement" || state.node === "implement-revise" || state.node === "fix-bug") {
1066
+ return { read: true, write: "workspace", noteWrite: false, shell: "write", repoWrite: "push" };
1067
+ }
1068
+ return { read: true, write: "none", noteWrite: false, shell: "read", repoWrite: "none" };
1069
+ }
1070
+ function accessText(access) {
1071
+ return `\u6743\u9650\uFF1Aread=${access.read ? "yes" : "no"} write=${access.write} noteWrite=${access.noteWrite ? "yes" : "no"} shell=${access.shell} repoWrite=${access.repoWrite}`;
1072
+ }
1073
+ function repoCommands(command2) {
1074
+ const commands = /* @__PURE__ */ new Set();
1075
+ for (const subcommand of gitSubcommands(command2)) {
1076
+ if (subcommand === "add" || subcommand === "commit" || subcommand === "push") commands.add(subcommand);
1077
+ }
1078
+ return commands;
1079
+ }
1080
+
1081
+ // src/runtime/gate.ts
888
1082
  function enterPhase(ctx, agent, node, reviewLimit, store, current) {
889
1083
  const gate = resolveGate(ctx, agent);
890
1084
  const state = current ?? store.read(gate.root, agent);
@@ -929,7 +1123,7 @@ function enterPhase(ctx, agent, node, reviewLimit, store, current) {
929
1123
  store.write(gate.root, agent, state);
930
1124
  return { gate, state, reviewLimit };
931
1125
  }
932
- function wakeGateAgent(invocation, text, command) {
1126
+ function wakeGateAgent(invocation, text, command2) {
933
1127
  const followup = invocation?.agent?.followup;
934
1128
  if (typeof followup !== "function") return;
935
1129
  try {
@@ -937,17 +1131,19 @@ function wakeGateAgent(invocation, text, command) {
937
1131
  id: randomUUID2(),
938
1132
  role: "user",
939
1133
  content: [{ type: "text", text }],
940
- source: { kind: "gate-command", command }
1134
+ source: { kind: "gate-command", command: command2 }
941
1135
  });
942
1136
  } catch {
943
1137
  }
944
1138
  }
945
1139
  function makeWake(reviewLimit) {
946
- return (invocation, state, text, command) => {
1140
+ return (invocation, state, text, command2) => {
947
1141
  const procedure = procedureFor(state, { reviewLimit });
948
1142
  wakeGateAgent(invocation, `${procedureBlock(procedure)}
949
1143
 
950
- ${text}`, command);
1144
+ ${accessText(accessFor(state, false))}
1145
+
1146
+ ${text}`, command2);
951
1147
  };
952
1148
  }
953
1149
 
@@ -957,7 +1153,7 @@ function workerProtocol(node) {
957
1153
  "## Gate worker",
958
1154
  "\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
1155
  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" : ""
1156
+ 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 write/edit/apply_patch \u548C\u4ED3\u5E93\u5199\u88AB\u62D2\u7EDD\uFF0C\u53EA\u8BFB shell \u53EF\u7528\u3002" : ""
961
1157
  ].filter(Boolean).join("\n\n");
962
1158
  }
963
1159
  var REVIEW_NODES = /* @__PURE__ */ new Set(["plan-review", "proposal-review", "code-review", "code-review-standalone"]);
@@ -1028,104 +1224,21 @@ function installSystemPrompt(ctx, deps) {
1028
1224
  text: (context) => {
1029
1225
  const gate = resolveGate(ctx, context.agent);
1030
1226
  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");
1227
+ if (effective.worker) return ["## Gate", "Gate worker\uFF1A\u53EA\u6267\u884C\u5F53\u524D node\uFF0C\u4E0D\u63A8\u8FDB phase\u3002", accessText(accessFor(effective.state, true)), workerProtocol(effective.state.node ?? "")].filter(Boolean).join("\n\n");
1032
1228
  return GATE_METHODOLOGY;
1033
1229
  }
1034
1230
  });
1035
1231
  });
1036
1232
  }
1037
1233
 
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
1234
  // src/runtime/guards.ts
1121
1235
  import path7 from "node:path";
1236
+ var DISCUSS_READ_ONLY = "discuss \u6587\u4EF6\u53EA\u8BFB\uFF1B\u8981\u4FEE\u6539\u8BF7\u5148\u53D6\u5F97\u7528\u6237\u6388\u6743\uFF0C\u7136\u540E gate_start_phase({node:'plan'|'implement', brief}) \u6216 /gate-enter-plan / /gate-enter-implement\u3002\u63D0\u4EA4\u5DF2\u6709\u6539\u52A8\u4E0D\u53D7\u5F71\u54CD\u3002";
1122
1237
  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;
1238
+ const access = accessFor(state, worker);
1239
+ const deny = [];
1240
+ if (access.write === "none") deny.push("write", "edit", "apply_patch");
1241
+ return deny.length > 0 ? { deny } : void 0;
1129
1242
  }
1130
1243
  var restrictionState = /* @__PURE__ */ new WeakMap();
1131
1244
  function applyRestrictions(ctx, agent, store) {
@@ -1170,21 +1283,28 @@ function applyRestrictions(ctx, agent, store) {
1170
1283
  } catch {
1171
1284
  }
1172
1285
  }
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
1286
  function targetList(name, args) {
1184
1287
  if (name !== "apply_patch") return [args?.file_path];
1185
1288
  return [...String(args?.input ?? "").matchAll(/^\*\*\* (?:Add File|Update File|Delete File|Move to):\s*(.+)$/gmu)].map((match) => String(match[1] ?? "").trim());
1186
1289
  }
1187
- var GATE_FROZEN_REASON = "Gate \u5DF2\u51BB\u7ED3\uFF1A\u5148 /gate-resume \u6216 /gate-return-discuss";
1290
+ var GATE_FROZEN_REASON = "Gate \u5DF2\u51BB\u7ED3\uFF1A\u5148 /gate-resume";
1291
+ function fileWriteDenied(state, effective) {
1292
+ if (state.phase === "blocked") return GATE_FROZEN_REASON;
1293
+ if (!effective.worker && state.phase === "discuss") return DISCUSS_READ_ONLY;
1294
+ if (effective.worker && (state.node === "plan" || state.node === "plan-revise")) return "planning worker \u53EA\u5141\u8BB8 gate_note_write\uFF1B\u4EA7\u54C1\u6587\u4EF6\u548C\u4ED3\u5E93\u5199\u88AB\u62D2\u7EDD\u3002";
1295
+ if (effective.worker) return `\u5F53\u524D ${state.node ?? "worker"} worker \u4E0D\u5141\u8BB8\u5199\u6587\u4EF6\uFF1Breview/verify worker \u53EA\u8BFB\u3002`;
1296
+ return "\u5F53\u524D\u89D2\u8272\u4E0D\u5141\u8BB8\u5199\u6587\u4EF6";
1297
+ }
1298
+ function shellWriteDenied(state, effective) {
1299
+ if (state.phase === "blocked") return GATE_FROZEN_REASON;
1300
+ if (!effective.worker && state.phase === "discuss") return DISCUSS_READ_ONLY;
1301
+ if (effective.worker) return `worker ${state.node ?? "unknown"} \u4E0D\u5141\u8BB8\u5199\u578B shell\u3002`;
1302
+ return "\u5F53\u524D\u89D2\u8272\u4E0D\u5141\u8BB8\u5199\u578B shell";
1303
+ }
1304
+ function repoDenied(state, effective, commands) {
1305
+ if (effective.worker) return `\u5F53\u524D ${state.node ?? "worker"} worker \u4E0D\u5141\u8BB8\u4ED3\u5E93\u5199\uFF08${commands}\uFF09\u3002`;
1306
+ return `\u5F53\u524D root \u4E0D\u5141\u8BB8\u4ED3\u5E93\u5199\uFF08${commands}\uFF09\u3002`;
1307
+ }
1188
1308
  function gateDenyReason(ctx, exec, store) {
1189
1309
  if (exec.name === "gate_record") {
1190
1310
  const root = rootAgentOf(ctx, exec.agent);
@@ -1202,17 +1322,14 @@ function gateDenyReason(ctx, exec, store) {
1202
1322
  const gate = resolveGate(ctx, exec.agent);
1203
1323
  const effective = effectiveState(ctx, exec.agent, gate, store);
1204
1324
  const state = effective.state;
1325
+ const access = accessFor(state, effective.worker);
1205
1326
  const cwd = sessionCwd(ctx, exec.agent, gate.workspace);
1206
1327
  const targets = targetList(exec.name, exec.arguments).filter((value) => typeof value === "string" && value !== "");
1207
1328
  if (targets.length === 0) return void 0;
1208
1329
  for (const value of targets) {
1209
1330
  const target = classifyPath(gate.workspace, value, cwd);
1210
1331
  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;
1332
+ if (access.write === "none") return fileWriteDenied(state, effective);
1216
1333
  }
1217
1334
  return void 0;
1218
1335
  }
@@ -1220,16 +1337,18 @@ function gateDenyReason(ctx, exec, store) {
1220
1337
  const gate = resolveGate(ctx, exec.agent);
1221
1338
  const effective = effectiveState(ctx, exec.agent, gate, store);
1222
1339
  const state = effective.state;
1340
+ const access = accessFor(state, effective.worker);
1223
1341
  const base = sessionCwd(ctx, exec.agent, gate.workspace);
1224
1342
  const requested = exec.arguments?.workdir;
1225
1343
  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`;
1344
+ const command2 = String(exec.arguments?.command ?? "");
1345
+ const repos = repoCommands(command2);
1346
+ if (repos.size > 0 && access.repoWrite === "none") return repoDenied(state, effective, [...repos].join("/"));
1347
+ const shell = shellAccess(command2, gate.workspace, cwd);
1348
+ if (!shell.write) return void 0;
1349
+ if (shell.outsideTargets.length > 0) return "\u5DE5\u4F5C\u533A\u5916\u5199\u5165\u88AB\u62D2\u7EDD";
1350
+ if (access.write === "none") return shellWriteDenied(state, effective);
1351
+ if (effective.worker && access.shell !== "write") return shellWriteDenied(state, effective);
1233
1352
  if (state.phase === "blocked") return GATE_FROZEN_REASON;
1234
1353
  return void 0;
1235
1354
  }
@@ -1299,15 +1418,15 @@ function provisionProjectScripts(root) {
1299
1418
  if (pkg === null || typeof pkg !== "object" || Array.isArray(pkg)) throw new Error("package.json \u5FC5\u987B\u662F\u5BF9\u8C61");
1300
1419
  }
1301
1420
  pkg.scripts = pkg.scripts !== null && typeof pkg.scripts === "object" && !Array.isArray(pkg.scripts) ? pkg.scripts : {};
1302
- const command = "node .agents/gate/verify-notes.mjs";
1421
+ const command2 = "node .agents/gate/verify-notes.mjs";
1303
1422
  const existing = pkg.scripts["verify-notes"];
1304
- if (existing === void 0) pkg.scripts["verify-notes"] = command;
1423
+ if (existing === void 0) pkg.scripts["verify-notes"] = command2;
1305
1424
  writeFileSync3(packagePath, `${JSON.stringify(pkg, null, 2)}
1306
1425
  `);
1307
1426
  return {
1308
1427
  launcher: path8.relative(root, launcher).replaceAll("\\", "/"),
1309
1428
  package: path8.relative(root, packagePath).replaceAll("\\", "/"),
1310
- command: existing === void 0 ? command : String(existing),
1429
+ command: existing === void 0 ? command2 : String(existing),
1311
1430
  changed: existing === void 0
1312
1431
  };
1313
1432
  }
@@ -1369,16 +1488,70 @@ function provisionCi(root) {
1369
1488
 
1370
1489
  // src/commands.ts
1371
1490
  import { execFileSync as execFileSync3 } from "node:child_process";
1491
+
1492
+ // src/command-catalog.ts
1493
+ var GATE_COMMANDS = [
1494
+ { name: "gate-init", description: "\u521D\u59CB\u5316 notes\u3001AGENTS.md\u3001verify-notes\u3001CI\u3001git \u4E0E\u57FA\u7840 .gitignore", hint: null, when: "any" },
1495
+ { name: "gate-enter-plan", description: "\u57FA\u4E8E\u8BA8\u8BBA\u5171\u8BC6\u3001\u65E2\u6709\u51B3\u7B56\u4E0E\u7528\u6237\u8F93\u5165\u8FDB\u5165 planning", hint: "<\u5177\u4F53\u5DE5\u4F5C\u4E8B\u9879\u6216\u4E0A\u4E0B\u6587\u8BED\u4E49>", when: "discuss/complete" },
1496
+ { name: "gate-enter-implement", description: "\u57FA\u4E8E\u5DF2\u786E\u8BA4\u8BA1\u5212\u6216\u8BA8\u8BBA\u540E\u7684\u5B9E\u65BD\u8BF4\u660E\u8FDB\u5165 implementation", hint: "<\u5177\u4F53\u5DE5\u4F5C\u4E8B\u9879\u6216\u4E0A\u4E0B\u6587\u8BED\u4E49>", when: "discuss/complete" },
1497
+ { name: "gate-fix-bug", description: "\u4E3A\u5DF2\u6388\u6743\u7F3A\u9677\u6D3E\u53D1 fix worker\uFF0C\u57FA\u4E8E\u63CF\u8FF0\u6216\u5F53\u524D\u4E0A\u4E0B\u6587\u4FEE\u590D", hint: "<\u5177\u4F53\u5DE5\u4F5C\u4E8B\u9879\u6216\u4E0A\u4E0B\u6587\u8BED\u4E49>", when: "discuss/complete" },
1498
+ { name: "gate-docs", description: "\u8FDB\u5165\u6587\u6863\u6CBB\u7406\uFF0C\u6309\u9879\u76EE\u6587\u6863\u89C4\u8303\u5904\u7406", hint: null, when: "discuss/complete" },
1499
+ { name: "gate-implement-review", description: "\u5BF9\u5F53\u524D\u6539\u52A8\u505A\u72EC\u7ACB\u53EA\u8BFB code review", hint: "<\u5177\u4F53\u5DE5\u4F5C\u4E8B\u9879\u6216\u4E0A\u4E0B\u6587\u8BED\u4E49>", when: "discuss/complete" },
1500
+ { name: "gate-code-verify", description: "\u5BF9\u5F53\u524D\u6539\u52A8\u505A\u72EC\u7ACB\u53EA\u8BFB\u9A8C\u6536\uFF08gate-user-verify\uFF09", hint: "<\u5177\u4F53\u5DE5\u4F5C\u4E8B\u9879\u6216\u4E0A\u4E0B\u6587\u8BED\u4E49>", when: "discuss/complete" },
1501
+ { name: "gate-propose-review", description: "\u5BF9\u63D0\u6848\u505A\u72EC\u7ACB\u53EA\u8BFB\u4E09\u8F74\u8BC4\u5BA1", hint: "<\u5177\u4F53\u5DE5\u4F5C\u4E8B\u9879\u6216\u4E0A\u4E0B\u6587\u8BED\u4E49>", when: "discuss/complete" },
1502
+ { name: "gate-block", description: "\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", hint: "[\u539F\u56E0]", when: "any" },
1503
+ { name: "gate-resume", description: "\u89E3\u51BB blocked\uFF1A\u56DE\u5230 block \u524D\u7684\u8282\u70B9\uFF1B\u8F6E\u6B21\u8017\u5C3D\u56DE discuss", hint: null, when: "blocked" },
1504
+ { name: "gate-return-discuss", description: "\u653E\u5F03\u5F53\u524D\u8282\u70B9\uFF0C\u56DE\u5230 discuss", hint: null, when: "non-blocked" },
1505
+ { name: "gate-status", description: "\u67E5\u770B\u5F53\u524D phase/node\u3001procedure\u3001access \u4E0E\u4E0B\u4E00\u6B65\u547D\u4EE4", hint: null, when: "any" },
1506
+ { name: "gate-note-verify", description: "\u53EA\u8BFB\u68C0\u67E5 notes tree/format/lifecycle/archived", hint: null, when: "any" },
1507
+ { name: "gate-note-archive", description: "\u5C06 implemented Note \u5F52\u6863\u5230 archived/\uFF1A\u52A0 Archived\u3001\u79FB\u52A8\u3001\u5C01\u5370\u5E76\u62A5\u544A\u5165\u7AD9\u94FE\u63A5", hint: "<implemented-note> [--superseded-by <new-note>]", when: "any" },
1508
+ { name: "gate-note-board", description: "\u751F\u6210\u51B3\u7B56\u770B\u677F board.html\uFF08\u8F7B\u91CF\u6A21\u5F0F\uFF0C\u8FDE\u63A5\u672C\u5730 notes \u76EE\u5F55\uFF09", hint: "[\u8F93\u51FA\u8DEF\u5F84] [\u6807\u9898]", when: "any" },
1509
+ { name: "gate-note-seal", description: "\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", hint: null, when: "any" }
1510
+ ];
1511
+ function commandSpec(name) {
1512
+ return GATE_COMMANDS.find((command2) => command2.name === name);
1513
+ }
1514
+ function commandLabel(spec) {
1515
+ return `/${spec.name}`;
1516
+ }
1517
+
1518
+ // src/command-next.ts
1519
+ function command(name, reason) {
1520
+ const spec = commandSpec(name);
1521
+ return { command: spec === void 0 ? `/${name}` : commandLabel(spec), reason };
1522
+ }
1523
+ function nextCommandsFor(state) {
1524
+ if (state.phase === "blocked") {
1525
+ return [command("gate-resume", "\u89E3\u51BB\u540E\u56DE\u5230\u6682\u505C\u524D\u8282\u70B9\uFF1B\u8F6E\u6B21\u8017\u5C3D\u65F6\u56DE discuss\u3002")];
1526
+ }
1527
+ if (state.phase === "discuss" || state.phase === "complete") {
1528
+ return [
1529
+ command("gate-enter-plan", "\u9700\u8981\u7528\u6237\u6388\u6743\u540E\u8FDB\u5165 planning\u3002"),
1530
+ command("gate-enter-implement", "\u9700\u8981\u7528\u6237\u6388\u6743\u540E\u8FDB\u5165\u5B9E\u65BD\uFF1B\u590D\u7528\u5DF2\u901A\u8FC7\u7684 plan task\uFF0C\u6216\u76F4\u63A5\u4ECE\u7528\u6237\u7B80\u8FF0\u5B9E\u65BD\u3002")
1531
+ ];
1532
+ }
1533
+ return [];
1534
+ }
1535
+ function nextCommandsText(commands) {
1536
+ if (commands.length === 0) return "";
1537
+ return `\u4E0B\u4E00\u6B65\u547D\u4EE4\uFF1A${commands.map((item) => `${item.command}\uFF08${item.reason}\uFF09`).join("\uFF1B")}`;
1538
+ }
1539
+
1540
+ // src/commands.ts
1372
1541
  function registerCommands(ctx, deps) {
1373
1542
  const { store, reviewLimit, wake } = deps;
1374
1543
  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) => {
1544
+ const command2 = (name, handler) => {
1545
+ const spec = commandSpec(name);
1546
+ if (spec === void 0) throw new Error(`unknown gate command: ${name}`);
1547
+ scope.commands.register({
1548
+ name: spec.name,
1549
+ description: spec.description,
1550
+ ...spec.hint === null ? {} : { input: { hint: spec.hint } },
1551
+ handler
1552
+ });
1553
+ };
1554
+ command2("gate-init", async (invocation) => {
1382
1555
  const workspace = workspaceBoundary(ctx, invocation.agent);
1383
1556
  if (workspace === void 0) return { kind: "error", text: "\u65E0\u6CD5\u786E\u5B9A\u5F53\u524D\u4F1A\u8BDD\u5DE5\u4F5C\u533A" };
1384
1557
  const root = path9.resolve(workspace);
@@ -1402,15 +1575,15 @@ verify-notes\uFF1A${projectScripts.command}
1402
1575
  CI\uFF1A${projectCi.workflow}
1403
1576
  git\uFF1A${projectGit.git}${projectGit.gitError === null ? "" : `\uFF08${projectGit.gitError}\uFF09`}
1404
1577
  ignore\uFF1A${projectGit.ignore}` };
1405
- }, null);
1406
- register("gate-enter-plan", "\u6309\u7528\u6237\u7B80\u8FF0\u8FDB\u5165 planning", async (invocation) => {
1578
+ });
1579
+ command2("gate-enter-plan", async (invocation) => {
1407
1580
  const result = enterPhase(ctx, invocation.agent, "plan", reviewLimit, store, void 0);
1408
1581
  if (result.kind === "error") return result;
1409
1582
  const brief = String(invocation.rawInput ?? "").trim();
1410
1583
  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
1584
  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) => {
1585
+ });
1586
+ command2("gate-enter-implement", async (invocation) => {
1414
1587
  const brief = String(invocation.rawInput ?? "").trim();
1415
1588
  const gate = resolveGate(ctx, invocation.agent);
1416
1589
  const state = store.read(gate.root, invocation.agent);
@@ -1419,7 +1592,7 @@ ignore\uFF1A${projectGit.ignore}` };
1419
1592
  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
1593
  state.task = brief;
1421
1594
  state.planReady = false;
1422
- state.noteDecision = null;
1595
+ state.noteDecision = MECHANICAL_DECISION;
1423
1596
  state.notePaths = [];
1424
1597
  state.noteSearch = null;
1425
1598
  state.noteVerified = false;
@@ -1432,8 +1605,8 @@ ignore\uFF1A${projectGit.ignore}` };
1432
1605
  const constraints = direct ? "\uFF08\u672C\u53E5\u5373\u5DE5\u4F5C\u9879\u7B80\u8FF0\uFF09" : brief === "" ? "\uFF08\u65E0\u989D\u5916\u7EA6\u675F\uFF09" : brief;
1433
1606
  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
1607
  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) => {
1608
+ });
1609
+ command2("gate-fix-bug", async (invocation) => {
1437
1610
  const brief = String(invocation.rawInput ?? "").trim();
1438
1611
  const gate = resolveGate(ctx, invocation.agent);
1439
1612
  const state = store.read(gate.root, invocation.agent);
@@ -1449,8 +1622,8 @@ ignore\uFF1A${projectGit.ignore}` };
1449
1622
  store.write(gate.root, invocation.agent, state);
1450
1623
  wake(invocation, state, `Gate \u5DF2\u8FDB\u5165 fix-bug\uFF1A${scope2}\u3002`, "gate-fix-bug");
1451
1624
  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) => {
1625
+ });
1626
+ command2("gate-docs", async (invocation) => {
1454
1627
  const gate = resolveGate(ctx, invocation.agent);
1455
1628
  const state = store.read(gate.root, invocation.agent);
1456
1629
  if (state.phase !== "discuss" && state.phase !== "complete") return { kind: "error", text: `\u5F53\u524D phase \u662F ${state.phase}` };
@@ -1464,8 +1637,8 @@ ignore\uFF1A${projectGit.ignore}` };
1464
1637
  store.write(gate.root, invocation.agent, state);
1465
1638
  wake(invocation, state, "Gate \u5DF2\u8FDB\u5165 docs\u3002", "gate-docs");
1466
1639
  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) => {
1640
+ });
1641
+ command2("gate-implement-review", async (invocation) => {
1469
1642
  const brief = String(invocation.rawInput ?? "").trim();
1470
1643
  const gate = resolveGate(ctx, invocation.agent);
1471
1644
  const state = store.read(gate.root, invocation.agent);
@@ -1482,7 +1655,7 @@ ignore\uFF1A${projectGit.ignore}` };
1482
1655
  wake(invocation, state, `Gate \u5DF2\u8FDB\u5165\u53EA\u8BFB code review\uFF1A${scope2}\u3002`, "gate-implement-review");
1483
1656
  return { kind: "success", text: `\u8FDB\u5165\u53EA\u8BFB code review\uFF1A${scope2}` };
1484
1657
  });
1485
- register("gate-code-verify", "\u5F00\u59CB\u72EC\u7ACB\u53EA\u8BFB\u9A8C\u6536", async (invocation) => {
1658
+ command2("gate-code-verify", async (invocation) => {
1486
1659
  const brief = String(invocation.rawInput ?? "").trim();
1487
1660
  const gate = resolveGate(ctx, invocation.agent);
1488
1661
  const state = store.read(gate.root, invocation.agent);
@@ -1499,7 +1672,7 @@ ignore\uFF1A${projectGit.ignore}` };
1499
1672
  wake(invocation, state, `Gate \u5DF2\u8FDB\u5165\u72EC\u7ACB\u9A8C\u6536\uFF1A${scope2}\u3002`, "gate-code-verify");
1500
1673
  return { kind: "success", text: `\u8FDB\u5165\u72EC\u7ACB\u9A8C\u6536\uFF1A${scope2}` };
1501
1674
  });
1502
- register("gate-propose-review", "\u5F00\u59CB\u72EC\u7ACB\u63D0\u6848\u8BC4\u5BA1", async (invocation) => {
1675
+ command2("gate-propose-review", async (invocation) => {
1503
1676
  const brief = String(invocation.rawInput ?? "").trim();
1504
1677
  const gate = resolveGate(ctx, invocation.agent);
1505
1678
  const state = store.read(gate.root, invocation.agent);
@@ -1516,7 +1689,7 @@ ignore\uFF1A${projectGit.ignore}` };
1516
1689
  wake(invocation, state, `Gate \u5DF2\u8FDB\u5165\u63D0\u6848\u8BC4\u5BA1\uFF1A${scope2}\u3002`, "gate-propose-review");
1517
1690
  return { kind: "success", text: `\u8FDB\u5165\u63D0\u6848\u8BC4\u5BA1\uFF1A${scope2}` };
1518
1691
  });
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) => {
1692
+ command2("gate-block", async (invocation) => {
1520
1693
  const gate = resolveGate(ctx, invocation.agent);
1521
1694
  const state = store.read(gate.root, invocation.agent);
1522
1695
  if (state.phase === "blocked") return { kind: "success", text: "\u5DF2\u7ECF\u662F blocked" };
@@ -1528,9 +1701,9 @@ ignore\uFF1A${projectGit.ignore}` };
1528
1701
  store.write(gate.root, invocation.agent, state);
1529
1702
  return { kind: "success", text: `\u5DF2\u6682\u505C\uFF1A${state.blockReason}
1530
1703
  \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) => {
1704
+ \u540C\u4E00\u4E8B\u9879\uFF1A/gate-resume \u56DE\u5230\u8BE5\u8282\u70B9\uFF1B\u6539\u53D8\u65B9\u6848\uFF1A\u5148 /gate-resume\uFF0C\u518D /gate-return-discuss\u3002` };
1705
+ });
1706
+ command2("gate-resume", async (invocation) => {
1534
1707
  const gate = resolveGate(ctx, invocation.agent);
1535
1708
  const state = store.read(gate.root, invocation.agent);
1536
1709
  if (state.phase !== "blocked") return { kind: "error", text: "\u5F53\u524D\u4E0D\u662F blocked" };
@@ -1547,8 +1720,8 @@ ignore\uFF1A${projectGit.ignore}` };
1547
1720
  }
1548
1721
  const exhausted = reason === "plan-review-rounds-exhausted" || reason === "code-review-rounds-exhausted";
1549
1722
  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) => {
1723
+ });
1724
+ command2("gate-return-discuss", async (invocation) => {
1552
1725
  const gate = resolveGate(ctx, invocation.agent);
1553
1726
  const state = store.read(gate.root, invocation.agent);
1554
1727
  if (state.phase === "blocked") return { kind: "error", text: `\u5F53\u524D blocked\uFF1A${state.blockReason ?? "unknown"}\uFF1B\u5148 /gate-resume` };
@@ -1570,19 +1743,22 @@ ignore\uFF1A${projectGit.ignore}` };
1570
1743
  state.pending = {};
1571
1744
  store.write(gate.root, invocation.agent, state);
1572
1745
  return { kind: "success", text: "\u5DF2\u56DE\u5230 discuss" };
1573
- }, null);
1574
- register("gate-status", "\u67E5\u770B\u5F53\u524D gate \u72B6\u6001", async (invocation) => {
1746
+ });
1747
+ command2("gate-status", async (invocation) => {
1575
1748
  const gate = resolveGate(ctx, invocation.agent);
1576
1749
  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) => {
1750
+ const state = store.read(gate.root, root);
1751
+ const worker = root?.id !== invocation.agent?.id;
1752
+ const next = worker ? "" : nextCommandsText(nextCommandsFor(state));
1753
+ return { kind: "success", text: [accessText(accessFor(state, worker)), statusText(state, reviewLimit), next].filter(Boolean).join("\n\n") };
1754
+ });
1755
+ command2("gate-note-verify", async (invocation) => {
1580
1756
  const gate = resolveGate(ctx, invocation.agent);
1581
1757
  const check = runNotesScripts(gate.root);
1582
1758
  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
1759
  ${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) => {
1760
+ });
1761
+ command2("gate-note-archive", async (invocation) => {
1586
1762
  const gate = resolveGate(ctx, invocation.agent);
1587
1763
  const raw = String(invocation.rawInput ?? "").trim();
1588
1764
  const args = raw === "" ? [] : raw.split(/\s+/u);
@@ -1596,8 +1772,8 @@ ${JSON.stringify(check.results, null, 2)}` };
1596
1772
  } catch (error) {
1597
1773
  return { kind: "error", text: String(error?.stderr ?? error?.message ?? error).trim() };
1598
1774
  }
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) => {
1775
+ });
1776
+ command2("gate-note-board", async (invocation) => {
1601
1777
  const gate = resolveGate(ctx, invocation.agent);
1602
1778
  const raw = String(invocation.rawInput ?? "").trim();
1603
1779
  const args = raw === "" ? [] : raw.split(/\s+/u);
@@ -1612,8 +1788,8 @@ ${JSON.stringify(check.results, null, 2)}` };
1612
1788
  } catch (error) {
1613
1789
  return { kind: "error", text: String(error?.stderr ?? error?.message ?? error).trim() };
1614
1790
  }
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) => {
1791
+ });
1792
+ command2("gate-note-seal", async (invocation) => {
1617
1793
  const gate = resolveGate(ctx, invocation.agent);
1618
1794
  const node = resolveNodeBinary();
1619
1795
  if (node === void 0) return { kind: "error", text: "notes-runtime-unavailable" };
@@ -1624,7 +1800,7 @@ ${JSON.stringify(check.results, null, 2)}` };
1624
1800
  } catch (error) {
1625
1801
  return { kind: "error", text: String(error?.stderr ?? error?.message ?? error).trim() };
1626
1802
  }
1627
- }, null);
1803
+ });
1628
1804
  });
1629
1805
  }
1630
1806
 
@@ -1633,7 +1809,7 @@ function registerStartPhase(ctx, deps) {
1633
1809
  const { store, reviewLimit, wake } = deps;
1634
1810
  ctx.tools.register({
1635
1811
  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",
1812
+ 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\u4EE5\u5728\u5BF9\u8BDD\u4E2D\u8981\u6C42\u7528\u6237\u518D\u8F93\u5165 slash command\u6216 ask_user_question \u4E2D\u7ED9\u51FA\u3002",
1637
1813
  parameters: {
1638
1814
  type: "object",
1639
1815
  additionalProperties: false,
@@ -1661,7 +1837,7 @@ function registerStartPhase(ctx, deps) {
1661
1837
  if (brief === "") return fail({ code: "brief-required", reason: "\u76F4\u63A5 implement \u9700\u8981\u4E00\u53E5\u8BF4\u660E\u3002" });
1662
1838
  state.task = brief;
1663
1839
  state.planReady = false;
1664
- state.noteDecision = null;
1840
+ state.noteDecision = MECHANICAL_DECISION;
1665
1841
  state.notePaths = [];
1666
1842
  state.noteSearch = null;
1667
1843
  state.noteVerified = false;
@@ -1679,6 +1855,7 @@ function registerStartPhase(ctx, deps) {
1679
1855
  task: result.state.task,
1680
1856
  procedure,
1681
1857
  method: procedure.method,
1858
+ access: accessFor(normalizeState(result.state), false),
1682
1859
  state: normalizeState(result.state)
1683
1860
  };
1684
1861
  }
@@ -1717,7 +1894,7 @@ function registerRecord(ctx, deps) {
1717
1894
  required: ["path", "class", "lifecycle"]
1718
1895
  }
1719
1896
  },
1720
- noteDecision: { type: "string", enum: ["new", "update", "in-place", "mechanical"] },
1897
+ noteDecision: { type: "string", enum: [...NOTE_DECISIONS] },
1721
1898
  noteSearch: { type: "string" },
1722
1899
  verification: {
1723
1900
  type: "object",
@@ -1753,7 +1930,7 @@ function registerRecord(ctx, deps) {
1753
1930
  state.blockedFrom = null;
1754
1931
  store.write(gate.root, exec.agent, state);
1755
1932
  const procedure2 = procedureFor(state, { reviewLimit });
1756
- return { ok: true, phase: state.phase, node: state.node, procedure: procedure2, method: procedure2.method, state: normalizeState(state) };
1933
+ return { ok: true, phase: state.phase, node: state.node, procedure: procedure2, method: procedure2.method, access: accessFor(normalizeState(state), false), state: normalizeState(state) };
1757
1934
  }
1758
1935
  if (state.phase !== "orchestrating" && state.phase !== "reviewing") {
1759
1936
  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` });
@@ -1780,7 +1957,7 @@ function registerRecord(ctx, deps) {
1780
1957
  state.blockReason = "needs-user-decision";
1781
1958
  store.write(gate.root, exec.agent, state);
1782
1959
  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) };
1960
+ return { ok: true, phase: state.phase, blockReason: state.blockReason, reason: args.discussionReason ?? null, procedure: procedure2, method: procedure2.method, access: accessFor(normalizeState(state), false), state: normalizeState(state) };
1784
1961
  }
1785
1962
  if (args.node === "proposal-review") {
1786
1963
  state.phase = "discuss";
@@ -1795,41 +1972,35 @@ function registerRecord(ctx, deps) {
1795
1972
  state.blockReason = null;
1796
1973
  } else if (args.node === "plan") {
1797
1974
  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" });
1975
+ return fail({ code: "task-required", reason: "node=plan \u5FC5\u987B\u643A\u5E26 task\uFF08\u5DE5\u4F5C\u9879\u7B80\u8FF0\uFF1Bmechanical \u7528 noteDecision \u8868\u793A\uFF09\u3002" });
1799
1976
  }
1800
1977
  const decision = args.noteDecision;
1801
- if (!["new", "update", "in-place", "mechanical"].includes(decision)) {
1978
+ if (!isNoteDecision(decision)) {
1802
1979
  return fail({ code: "note-decision-required", reason: "node=plan \u9700\u8981 noteDecision\uFF08new/update/in-place/mechanical\uFF09\u3002" });
1803
1980
  }
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
1981
  const optionNotePaths = notePathsForDecision(args.notePaths, decision);
1808
1982
  const noteError = validateNotePaths(gate.root, optionNotePaths, decision);
1809
1983
  if (noteError !== void 0) return fail(noteError);
1810
1984
  state.task = args.task.trim();
1811
- state.notePaths = decision === "mechanical" ? [] : optionNotePaths;
1985
+ state.notePaths = isMechanicalDecision(decision) ? [] : optionNotePaths;
1812
1986
  state.noteDecision = decision;
1813
1987
  state.noteSearch = typeof args.noteSearch === "string" ? args.noteSearch : null;
1814
- state.noteVerified = decision === "mechanical";
1988
+ state.noteVerified = isMechanicalDecision(decision);
1815
1989
  state.node = "plan-review";
1816
1990
  } else if (args.node === "plan-revise") {
1817
1991
  if (args.noteDecision !== void 0 || args.notePaths !== void 0) {
1818
1992
  const decision = args.noteDecision ?? state.noteDecision;
1819
- if (!["new", "update", "in-place", "mechanical"].includes(decision)) {
1993
+ if (!isNoteDecision(decision)) {
1820
1994
  return fail({ code: "note-decision-required", reason: "plan-revise \u9700\u8981\u5408\u6CD5 noteDecision\u3002" });
1821
1995
  }
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
1996
  const requestedNotePaths = args.notePaths ?? state.notePaths;
1826
1997
  const optionNotePaths = notePathsForDecision(requestedNotePaths, decision);
1827
1998
  const noteError = validateNotePaths(gate.root, optionNotePaths, decision);
1828
1999
  if (noteError !== void 0) return fail(noteError);
1829
- state.notePaths = decision === "mechanical" ? [] : optionNotePaths;
2000
+ state.notePaths = isMechanicalDecision(decision) ? [] : optionNotePaths;
1830
2001
  state.noteDecision = decision;
1831
2002
  if (typeof args.noteSearch === "string") state.noteSearch = args.noteSearch;
1832
- state.noteVerified = decision === "mechanical";
2003
+ state.noteVerified = isMechanicalDecision(decision);
1833
2004
  }
1834
2005
  state.node = "plan-review";
1835
2006
  } else if (args.node === "plan-review") {
@@ -1850,18 +2021,29 @@ function registerRecord(ctx, deps) {
1850
2021
  state.blockReason = "plan-review-rounds-exhausted";
1851
2022
  } else state.node = "plan-revise";
1852
2023
  } else if (args.node === "implement") {
1853
- if (args.noteDecision === "mechanical") {
1854
- state.noteDecision = "mechanical";
2024
+ if (args.noteDecision !== void 0 && !isNoteDecision(args.noteDecision)) {
2025
+ return fail({ code: "note-decision-required", reason: "implement \u7684 noteDecision \u5FC5\u987B\u662F new/update/in-place/mechanical\u3002" });
2026
+ }
2027
+ const decision = args.noteDecision ?? state.noteDecision;
2028
+ if (decision !== null && !isNoteDecision(decision)) {
2029
+ return fail({ code: "note-decision-required", reason: "implement \u7684 noteDecision \u5FC5\u987B\u662F new/update/in-place/mechanical\u3002" });
2030
+ }
2031
+ if (isMechanicalDecision(args.noteDecision)) {
2032
+ state.noteDecision = MECHANICAL_DECISION;
1855
2033
  state.notePaths = [];
1856
2034
  state.noteVerified = true;
2035
+ } else if (args.noteDecision === void 0 && isMechanicalDecision(state.noteDecision)) {
1857
2036
  } else {
1858
2037
  if (Array.isArray(args.notePaths)) {
2038
+ if (!isNoteDecision(decision)) {
2039
+ return fail({ code: "note-decision-required", reason: "implement \u5199 notePaths \u9700\u8981\u5408\u6CD5 noteDecision\u3002" });
2040
+ }
1859
2041
  const nextNotePaths = normalizeNotePaths(args.notePaths);
1860
- const noteError = validateNotePaths(gate.root, nextNotePaths, args.noteDecision ?? state.noteDecision);
2042
+ const noteError = validateNotePaths(gate.root, nextNotePaths, decision);
1861
2043
  if (noteError !== void 0) return fail(noteError);
1862
2044
  state.notePaths = nextNotePaths;
1863
2045
  }
1864
- if (["new", "update", "in-place"].includes(args.noteDecision)) state.noteDecision = args.noteDecision;
2046
+ if (isNoteDecision(args.noteDecision) && !isMechanicalDecision(args.noteDecision)) state.noteDecision = args.noteDecision;
1865
2047
  }
1866
2048
  state.node = "code-review";
1867
2049
  } else if (args.node === "implement-revise") state.node = "code-review";
@@ -1919,7 +2101,7 @@ function registerRecord(ctx, deps) {
1919
2101
  } else if (verification.passed !== true) {
1920
2102
  state.node = "implement-revise";
1921
2103
  state.noteVerified = false;
1922
- } else if (state.noteDecision === "mechanical") {
2104
+ } else if (isMechanicalDecision(state.noteDecision)) {
1923
2105
  state.noteVerified = true;
1924
2106
  state.noteFinalized = true;
1925
2107
  commitHint = "\u5EFA\u8BAE\u63D0\u4EA4\u5B9E\u73B0";
@@ -1962,7 +2144,7 @@ function registerRecord(ctx, deps) {
1962
2144
  store.write(gate.root, exec.agent, state);
1963
2145
  const next = normalizeState(state);
1964
2146
  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 } };
2147
+ return { ok: true, phase: state.phase, node: state.node, rounds: state.rounds, blockReason: state.blockReason, verification: state.verification, procedure, method: procedure.method, access: accessFor(next, false), state: next, ...commitHint === null ? {} : { commitHint } };
1966
2148
  }
1967
2149
  });
1968
2150
  }
@@ -1989,16 +2171,22 @@ function registerStatus(ctx, deps) {
1989
2171
  const gate = resolveGate(ctx, exec.agent);
1990
2172
  if (gate.root === void 0) return { ok: false, code: "state-unavailable", reason: "\u65E0\u6CD5\u786E\u5B9A\u5DE5\u4F5C\u533A\u3002" };
1991
2173
  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) };
2174
+ const worker = root?.id !== exec.agent?.id;
2175
+ const access = accessFor(state, worker);
2176
+ if (worker) {
2177
+ return { ok: true, procedure: null, method: null, access, nextCommands: [], text: [accessText(access), statusText(state, reviewLimit)].join("\n\n") };
1994
2178
  }
2179
+ const nextCommands = nextCommandsFor(state);
2180
+ const nextText = nextCommandsText(nextCommands);
1995
2181
  const procedure = procedureFor(state, { reviewLimit });
1996
2182
  const includeRounds = !procedure.hasRounds;
1997
2183
  return {
1998
2184
  ok: true,
1999
2185
  procedure,
2000
2186
  method: procedure.method,
2001
- text: procedureResultText(procedure, statusText(state, reviewLimit, { includeRounds }))
2187
+ access,
2188
+ nextCommands,
2189
+ text: procedureResultText(procedure, [statusText(state, reviewLimit, { includeRounds }), nextText, accessText(access)].filter(Boolean).join("\n\n"))
2002
2190
  };
2003
2191
  }
2004
2192
  });
@@ -2011,7 +2199,7 @@ function registerNoteTools(ctx, deps) {
2011
2199
  const { store, reviewLimit, wake } = deps;
2012
2200
  ctx.tools.register({
2013
2201
  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",
2202
+ 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/apply_patch \u548C\u4ED3\u5E93\u5199\u88AB\u62D2\u7EDD\uFF0C\u53EA\u8BFB shell \u53EF\u7528\u3002",
2015
2203
  parameters: {
2016
2204
  type: "object",
2017
2205
  additionalProperties: false,
@@ -2114,7 +2302,7 @@ function registerNoteTools(ctx, deps) {
2114
2302
  const gate = resolveGate(ctx, exec.agent);
2115
2303
  const state = store.read(gate.root, exec.agent);
2116
2304
  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") {
2305
+ if (isMechanicalDecision(state.noteDecision)) {
2118
2306
  state.noteVerified = true;
2119
2307
  state.noteFinalized = true;
2120
2308
  store.write(gate.root, exec.agent, state);
@@ -2159,12 +2347,73 @@ function registerNoteTools(ctx, deps) {
2159
2347
  });
2160
2348
  }
2161
2349
 
2350
+ // src/tools/catalog.ts
2351
+ var GATE_TOOLS = [
2352
+ { name: "gate_status", summary: "\u8BFB\u53D6\u5F53\u524D phase/node\u3001procedure\u3001access \u548C nextCommands\uFF1Broot/worker \u5747\u53EF\u8C03\u7528\u3002" },
2353
+ { name: "gate_record", summary: "\u8BB0\u5F55 worker \u7ED3\u679C\u5E76\u63A8\u8FDB gate\uFF1B\u4EC5 root \u53EF\u8C03\u7528\u3002" },
2354
+ { name: "gate_start_phase", summary: "\u5728\u7528\u6237\u6388\u6743\u540E\u8FDB\u5165 plan/implement\uFF1Bplan \u57FA\u4E8E\u8BA8\u8BBA\u5171\u8BC6\uFF0Cimplement \u57FA\u4E8E\u5DF2\u786E\u8BA4\u8BA1\u5212\u6216\u5B9E\u65BD\u8BF4\u660E\uFF1B\u4EC5 root \u53EF\u8C03\u7528\u3002" },
2355
+ { name: "gate_note_write", summary: "planning worker \u5199 proposed Note \u6216\u539F\u5730\u66F4\u65B0 implemented Note\u3002" },
2356
+ { name: "gate_note_verify", summary: "\u5728 note-lifecycle \u4E2D\u8FD0\u884C notes \u6821\u9A8C\uFF1Broot-only\u3002" },
2357
+ { name: "gate_note_finalize", summary: "\u628A proposed Note \u79FB\u5230 implemented \u5E76\u66F4\u65B0\u5934\u5757\uFF1Broot-only\u3002" },
2358
+ { name: "gate_help", summary: "\u5217\u51FA gate \u547D\u4EE4\u3001\u5DE5\u5177\u548C\u5F53\u524D phase/node\uFF1B\u53EA\u8BFB\uFF0Croot-only\u3002" }
2359
+ ];
2360
+
2361
+ // src/tools/help.ts
2362
+ function commandLine(spec) {
2363
+ const hint = spec.hint === null ? "" : ` ${spec.hint}`;
2364
+ return `- ${commandLabel(spec)}${hint} \u2014 ${spec.description}\uFF08${spec.when}\uFF09`;
2365
+ }
2366
+ function helpText(phase, node) {
2367
+ return [
2368
+ "## Gate help",
2369
+ `\u5F53\u524D phase/node\uFF1A${phase} / ${node ?? "\u65E0"}`,
2370
+ "",
2371
+ "\u547D\u4EE4\uFF08\u7531\u7528\u6237\u6267\u884C\uFF09\uFF1A",
2372
+ ...GATE_COMMANDS.map(commandLine),
2373
+ "",
2374
+ "\u5DE5\u5177\uFF08\u7531\u6A21\u578B\u8C03\u7528\uFF09\uFF1A",
2375
+ ...GATE_TOOLS.map((tool) => `- ${tool.name} \u2014 ${tool.summary}`),
2376
+ "",
2377
+ "\u547D\u4EE4\u7531\u7528\u6237\u6267\u884C\uFF1B\u5DE5\u5177\u7531\u6A21\u578B\u8C03\u7528\u3002\u547D\u4EE4\u4E0D\u4F1A\u4F5C\u4E3A tool \u51FA\u73B0\u5728\u5DE5\u5177\u76EE\u5F55\u3002"
2378
+ ].join("\n");
2379
+ }
2380
+ function registerHelp(ctx, deps) {
2381
+ const { store } = deps;
2382
+ ctx.tools.register({
2383
+ name: "gate_help",
2384
+ description: "\u53EA\u8BFB\u5217\u51FA gate \u547D\u4EE4\u3001\u5DE5\u5177\u7528\u9014\u548C\u5F53\u524D phase/node\u3002\u4EC5 root agent\uFF1B\u53EA\u5728\u4E0D\u786E\u5B9A\u547D\u4EE4\u540D/\u5DE5\u5177\u7528\u9014\u65F6\u4F7F\u7528\u3002",
2385
+ parameters: { type: "object", additionalProperties: false, properties: {} },
2386
+ output: {
2387
+ schema: { type: "object", additionalProperties: true },
2388
+ render: (_args, value) => [{ type: "text", text: typeof value?.text === "string" ? value.text : JSON.stringify(value) }]
2389
+ },
2390
+ async execute(_args, exec) {
2391
+ const root = rootAgentOf(ctx, exec.agent);
2392
+ if (root?.id !== exec.agent?.id) return { ok: false, code: "root-only", reason: "gate_help \u53EA\u80FD\u7531 root agent \u8C03\u7528\u3002" };
2393
+ const gate = resolveGate(ctx, exec.agent);
2394
+ if (gate.root === void 0) return { ok: false, code: "state-unavailable", reason: "\u65E0\u6CD5\u786E\u5B9A\u5DE5\u4F5C\u533A\u3002" };
2395
+ const state = store.read(gate.root, root);
2396
+ return {
2397
+ ok: true,
2398
+ phase: state.phase,
2399
+ node: state.node,
2400
+ current: { phase: state.phase, node: state.node },
2401
+ note: "\u547D\u4EE4\u7531\u7528\u6237\u6267\u884C\uFF1B\u5DE5\u5177\u7531\u6A21\u578B\u8C03\u7528\u3002",
2402
+ commands: GATE_COMMANDS.map((spec) => ({ ...spec, command: commandLabel(spec) })),
2403
+ tools: GATE_TOOLS,
2404
+ text: helpText(state.phase, state.node)
2405
+ };
2406
+ }
2407
+ });
2408
+ }
2409
+
2162
2410
  // src/tools/index.ts
2163
2411
  function registerTools(ctx, deps) {
2164
2412
  registerStartPhase(ctx, deps);
2165
2413
  registerRecord(ctx, deps);
2166
2414
  registerStatus(ctx, deps);
2167
2415
  registerNoteTools(ctx, deps);
2416
+ registerHelp(ctx, deps);
2168
2417
  }
2169
2418
 
2170
2419
  // src/index.ts