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/README.md +350 -315
- package/lib/index.js +478 -229
- package/locale/en.json +6 -6
- package/locale/zh.json +6 -6
- package/package.json +2 -2
- package/skills/gate-plan-review/SKILL.md +1 -2
- package/skills/gate-planning/SKILL.md +0 -2
- package/skills/gate-planning/rfc.md +0 -2
- package/skills/gate-research/SKILL.md +0 -3
- package/skills/gate-domain-modeling/CONTEXT-FORMAT.md +0 -64
- package/skills/gate-domain-modeling/GLOSSARY-FORMAT.md +0 -64
- package/skills/gate-domain-modeling/SKILL.md +0 -47
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 = `##
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
|
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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
-
|
|
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\
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
211
|
-
blocked \u8868\u793A\u51BB\u7ED3\uFF1A\u7B49\u5F85 /gate-resume
|
|
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\
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
710
|
+
return isMechanicalDecision(decision) ? value : normalizeNotePaths(value);
|
|
656
711
|
}
|
|
657
712
|
function validateNotePaths(root, value, decision) {
|
|
658
|
-
if (decision
|
|
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
|
-
|
|
671
|
-
if (
|
|
672
|
-
|
|
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/
|
|
885
|
-
|
|
886
|
-
|
|
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,
|
|
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,
|
|
1140
|
+
return (invocation, state, text, command2) => {
|
|
947
1141
|
const procedure = procedureFor(state, { reviewLimit });
|
|
948
1142
|
wakeGateAgent(invocation, `${procedureBlock(procedure)}
|
|
949
1143
|
|
|
950
|
-
${
|
|
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
|
|
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
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
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
|
|
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 (
|
|
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
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
if (!
|
|
1231
|
-
if (
|
|
1232
|
-
if (
|
|
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
|
|
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"] =
|
|
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 ?
|
|
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
|
|
1376
|
-
name
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
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
|
-
}
|
|
1406
|
-
|
|
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
|
-
}
|
|
1413
|
-
|
|
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 =
|
|
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
|
-
}
|
|
1436
|
-
|
|
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
|
-
}
|
|
1453
|
-
|
|
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
|
-
}
|
|
1468
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
}
|
|
1533
|
-
|
|
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
|
-
}
|
|
1551
|
-
|
|
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
|
-
}
|
|
1574
|
-
|
|
1746
|
+
});
|
|
1747
|
+
command2("gate-status", async (invocation) => {
|
|
1575
1748
|
const gate = resolveGate(ctx, invocation.agent);
|
|
1576
1749
|
const root = rootAgentOf(ctx, invocation.agent);
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
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
|
-
}
|
|
1585
|
-
|
|
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
|
-
}
|
|
1600
|
-
|
|
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
|
-
}
|
|
1616
|
-
|
|
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
|
-
}
|
|
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\
|
|
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 =
|
|
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: [
|
|
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\
|
|
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 (!
|
|
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
|
|
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
|
|
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 (!
|
|
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
|
|
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
|
|
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
|
|
1854
|
-
|
|
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,
|
|
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 (
|
|
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
|
|
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
|
-
|
|
1993
|
-
|
|
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
|
-
|
|
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/
|
|
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
|
|
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
|