dsh-gate-agent 0.1.3 → 0.1.5
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 +209 -20
- package/lib/index.js +534 -103
- package/package.json +1 -1
- package/skills/gate-handoff/SKILL.md +1 -1
- 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-playwright-e2e/SKILL.md +4 -0
- package/skills/gate-prd/SKILL.md +1 -1
- package/skills/gate-research/SKILL.md +3 -6
- package/skills/gate-writing-for-agents/SKILL.md +2 -0
- 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
|
@@ -126,7 +126,7 @@ var FIX_BUG_ORCHESTRATION = `## /gate-fix-bug \u7F16\u6392
|
|
|
126
126
|
## Step 4 \u2014 Record
|
|
127
127
|
\u5B8C\u6210\u540E\u8C03 gate_record({node:'fix-bug', workerSessionIds})\uFF0CworkerSessionIds \u5305\u542B fix \u4E0E review worker\uFF1Bgate \u4E0D\u518D\u81EA\u52A8\u63D0\u4EA4\u3002`;
|
|
128
128
|
var DOCS_ORCHESTRATION = `## /gate-docs \u7F16\u6392
|
|
129
|
-
\
|
|
129
|
+
\u8303\u56F4\u7531\u6A21\u578B\u6309\u9879\u76EE\u5B9E\u9645\u6587\u6863\u9700\u8981\u5224\u65AD\u3002
|
|
130
130
|
\u5B8C\u6210\u540E\u8C03 gate_record({node:'docs'}) \u56DE\u5230 discuss\u3002`;
|
|
131
131
|
var NOTE_LIFECYCLE_ORCHESTRATION = `## Note lifecycle
|
|
132
132
|
Verify \u5DF2 PASS\uFF1B\u5F53\u524D\u4E0D\u6D3E worker\uFF0C\u4E5F\u4E0D\u5199\u4E1A\u52A1\u4EE3\u7801\u3002
|
|
@@ -189,43 +189,80 @@ var NODE_PROCEDURES = {
|
|
|
189
189
|
"proposal-review": REVIEWING_PROPOSAL_PROCEDURE,
|
|
190
190
|
"verify-standalone": REVIEWING_VERIFY_PROCEDURE
|
|
191
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
|
+
}
|
|
192
220
|
var GATE_METHODOLOGY = `## Gate \u65B9\u6CD5\u8BBA
|
|
193
|
-
\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
|
|
194
222
|
|
|
195
223
|
${DISCUSS_ORCHESTRATION}
|
|
196
224
|
|
|
197
|
-
## plan
|
|
198
|
-
\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
|
|
199
227
|
|
|
200
|
-
## implement
|
|
201
|
-
\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
|
|
202
230
|
|
|
203
|
-
## code-review
|
|
231
|
+
## orchestrating/code-review
|
|
204
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
|
|
205
233
|
|
|
206
|
-
## verify
|
|
234
|
+
## orchestrating/verify
|
|
207
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
|
|
208
236
|
|
|
209
|
-
## note-lifecycle
|
|
237
|
+
## orchestrating/note-lifecycle
|
|
210
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
|
|
211
239
|
|
|
212
|
-
## fix-bug
|
|
240
|
+
## orchestrating/fix-bug
|
|
213
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
|
|
214
242
|
|
|
215
243
|
## docs
|
|
216
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
|
|
217
245
|
|
|
218
|
-
## reviewing-
|
|
246
|
+
## reviewing/code-review-standalone
|
|
219
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
|
|
220
248
|
|
|
221
|
-
## reviewing-
|
|
249
|
+
## reviewing/proposal-review
|
|
222
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
|
|
223
251
|
|
|
224
|
-
## reviewing-
|
|
252
|
+
## reviewing/verify-standalone
|
|
225
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
|
|
226
254
|
|
|
227
|
-
## blocked
|
|
228
|
-
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`;
|
|
229
266
|
function protocolVariant(state) {
|
|
230
267
|
if (state.phase === "orchestrating") return NODE_INFO[state.node ?? ""]?.variant ?? state.phase;
|
|
231
268
|
if (state.phase === "reviewing") return NODE_INFO[state.node ?? ""]?.variant ?? "reviewing-code";
|
|
@@ -238,7 +275,7 @@ function procedureFor(state, { reviewLimit = DEFAULT_REVIEW_LIMIT } = {}) {
|
|
|
238
275
|
if (variant === "discuss") {
|
|
239
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";
|
|
240
277
|
} else if (variant === "blocked") {
|
|
241
|
-
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`;
|
|
242
279
|
} else if (variant === "complete") {
|
|
243
280
|
text = "Gate \u5DF2\u5B8C\u6210\uFF1B\u65B0\u4EFB\u52A1\u9700\u8981\u91CD\u65B0\u6388\u6743\u3002";
|
|
244
281
|
} else if (variant === "docs") {
|
|
@@ -256,7 +293,7 @@ function procedureFor(state, { reviewLimit = DEFAULT_REVIEW_LIMIT } = {}) {
|
|
|
256
293
|
text = [text, `## \u5F53\u524D\u8F6E\u6B21
|
|
257
294
|
${roundsText(state, reviewLimit)}`].filter(Boolean).join("\n\n");
|
|
258
295
|
}
|
|
259
|
-
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" };
|
|
260
297
|
}
|
|
261
298
|
function procedureFailure(state, reviewLimit, fields) {
|
|
262
299
|
const procedure = procedureFor(state, { reviewLimit });
|
|
@@ -301,6 +338,7 @@ function statusText(state, reviewLimit = DEFAULT_REVIEW_LIMIT, { includeRounds =
|
|
|
301
338
|
}
|
|
302
339
|
function procedureBlock(procedure) {
|
|
303
340
|
return `\u5F53\u524D phase/node\uFF1A${procedure.phase} / ${procedure.node ?? "\u65E0"}
|
|
341
|
+
\u65B9\u6CD5\u8BBA\uFF1A${procedure.methodologyKey}
|
|
304
342
|
\u65B9\u6CD5\uFF1A${procedure.method}
|
|
305
343
|
|
|
306
344
|
${procedure.text}`;
|
|
@@ -910,9 +948,9 @@ function classifyPath(root, value, cwd = root) {
|
|
|
910
948
|
var GIT_VALUE_OPTIONS = /* @__PURE__ */ new Set(["-C", "-c", "--git-dir", "--work-tree", "--namespace", "--config-env"]);
|
|
911
949
|
var GIT_FILE_WRITE = /* @__PURE__ */ new Set(["checkout", "switch", "reset", "clean", "restore", "stash", "merge", "rebase", "cherry-pick", "apply", "pull"]);
|
|
912
950
|
var GIT_NON_WRITE = /* @__PURE__ */ new Set(["status", "add", "commit", "push"]);
|
|
913
|
-
function gitSubcommands(
|
|
951
|
+
function gitSubcommands(command2) {
|
|
914
952
|
const subcommands = /* @__PURE__ */ new Set();
|
|
915
|
-
for (const segment of String(
|
|
953
|
+
for (const segment of String(command2).split(/&&|\|\||;|\r?\n|\|/u)) {
|
|
916
954
|
const tokens = [...segment.matchAll(/"([^"]*)"|'([^']*)'|(\S+)/gu)].map((match) => match[1] ?? match[2] ?? match[3] ?? "");
|
|
917
955
|
const gitIndex = tokens.findIndex((token) => /^git(?:\.exe)?$/iu.test(token));
|
|
918
956
|
if (gitIndex < 0) continue;
|
|
@@ -975,8 +1013,8 @@ function addTarget(targets, token, map) {
|
|
|
975
1013
|
}
|
|
976
1014
|
function segmentAccess(text, root, cwd, map) {
|
|
977
1015
|
const git = gitSubcommands(text);
|
|
978
|
-
if ([...git].some((
|
|
979
|
-
if ([...git].some((
|
|
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: [] };
|
|
980
1018
|
const raw = [];
|
|
981
1019
|
const verb = WRITE_VERB.exec(text);
|
|
982
1020
|
if (verb !== null) {
|
|
@@ -995,9 +1033,9 @@ function segmentAccess(text, root, cwd, map) {
|
|
|
995
1033
|
function segments(text) {
|
|
996
1034
|
return String(text).split(/&&|\|\||;|\r?\n/u).map((part) => part.trim()).filter(Boolean);
|
|
997
1035
|
}
|
|
998
|
-
function shellAccess(
|
|
999
|
-
const map = assignments(
|
|
1000
|
-
const parts = segments(
|
|
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);
|
|
1001
1039
|
return {
|
|
1002
1040
|
write: parts.length > 0,
|
|
1003
1041
|
outsideTargets: parts.flatMap((item) => item.outsideTargets ?? [])
|
|
@@ -1032,9 +1070,9 @@ function accessFor(state, worker) {
|
|
|
1032
1070
|
function accessText(access) {
|
|
1033
1071
|
return `\u6743\u9650\uFF1Aread=${access.read ? "yes" : "no"} write=${access.write} noteWrite=${access.noteWrite ? "yes" : "no"} shell=${access.shell} repoWrite=${access.repoWrite}`;
|
|
1034
1072
|
}
|
|
1035
|
-
function repoCommands(
|
|
1073
|
+
function repoCommands(command2) {
|
|
1036
1074
|
const commands = /* @__PURE__ */ new Set();
|
|
1037
|
-
for (const subcommand of gitSubcommands(
|
|
1075
|
+
for (const subcommand of gitSubcommands(command2)) {
|
|
1038
1076
|
if (subcommand === "add" || subcommand === "commit" || subcommand === "push") commands.add(subcommand);
|
|
1039
1077
|
}
|
|
1040
1078
|
return commands;
|
|
@@ -1085,7 +1123,7 @@ function enterPhase(ctx, agent, node, reviewLimit, store, current) {
|
|
|
1085
1123
|
store.write(gate.root, agent, state);
|
|
1086
1124
|
return { gate, state, reviewLimit };
|
|
1087
1125
|
}
|
|
1088
|
-
function wakeGateAgent(invocation, text,
|
|
1126
|
+
function wakeGateAgent(invocation, text, command2) {
|
|
1089
1127
|
const followup = invocation?.agent?.followup;
|
|
1090
1128
|
if (typeof followup !== "function") return;
|
|
1091
1129
|
try {
|
|
@@ -1093,19 +1131,19 @@ function wakeGateAgent(invocation, text, command) {
|
|
|
1093
1131
|
id: randomUUID2(),
|
|
1094
1132
|
role: "user",
|
|
1095
1133
|
content: [{ type: "text", text }],
|
|
1096
|
-
source: { kind: "gate-command", command }
|
|
1134
|
+
source: { kind: "gate-command", command: command2 }
|
|
1097
1135
|
});
|
|
1098
1136
|
} catch {
|
|
1099
1137
|
}
|
|
1100
1138
|
}
|
|
1101
1139
|
function makeWake(reviewLimit) {
|
|
1102
|
-
return (invocation, state, text,
|
|
1140
|
+
return (invocation, state, text, command2) => {
|
|
1103
1141
|
const procedure = procedureFor(state, { reviewLimit });
|
|
1104
1142
|
wakeGateAgent(invocation, `${procedureBlock(procedure)}
|
|
1105
1143
|
|
|
1106
1144
|
${accessText(accessFor(state, false))}
|
|
1107
1145
|
|
|
1108
|
-
${text}`,
|
|
1146
|
+
${text}`, command2);
|
|
1109
1147
|
};
|
|
1110
1148
|
}
|
|
1111
1149
|
|
|
@@ -1249,7 +1287,7 @@ function targetList(name, args) {
|
|
|
1249
1287
|
if (name !== "apply_patch") return [args?.file_path];
|
|
1250
1288
|
return [...String(args?.input ?? "").matchAll(/^\*\*\* (?:Add File|Update File|Delete File|Move to):\s*(.+)$/gmu)].map((match) => String(match[1] ?? "").trim());
|
|
1251
1289
|
}
|
|
1252
|
-
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";
|
|
1253
1291
|
function fileWriteDenied(state, effective) {
|
|
1254
1292
|
if (state.phase === "blocked") return GATE_FROZEN_REASON;
|
|
1255
1293
|
if (!effective.worker && state.phase === "discuss") return DISCUSS_READ_ONLY;
|
|
@@ -1303,10 +1341,10 @@ function gateDenyReason(ctx, exec, store) {
|
|
|
1303
1341
|
const base = sessionCwd(ctx, exec.agent, gate.workspace);
|
|
1304
1342
|
const requested = exec.arguments?.workdir;
|
|
1305
1343
|
const cwd = typeof requested === "string" && requested !== "" ? path7.resolve(base, requested) : base;
|
|
1306
|
-
const
|
|
1307
|
-
const repos = repoCommands(
|
|
1344
|
+
const command2 = String(exec.arguments?.command ?? "");
|
|
1345
|
+
const repos = repoCommands(command2);
|
|
1308
1346
|
if (repos.size > 0 && access.repoWrite === "none") return repoDenied(state, effective, [...repos].join("/"));
|
|
1309
|
-
const shell = shellAccess(
|
|
1347
|
+
const shell = shellAccess(command2, gate.workspace, cwd);
|
|
1310
1348
|
if (!shell.write) return void 0;
|
|
1311
1349
|
if (shell.outsideTargets.length > 0) return "\u5DE5\u4F5C\u533A\u5916\u5199\u5165\u88AB\u62D2\u7EDD";
|
|
1312
1350
|
if (access.write === "none") return shellWriteDenied(state, effective);
|
|
@@ -1327,7 +1365,7 @@ function installRestrictions(ctx, deps) {
|
|
|
1327
1365
|
}
|
|
1328
1366
|
|
|
1329
1367
|
// src/commands.ts
|
|
1330
|
-
import
|
|
1368
|
+
import path10 from "node:path";
|
|
1331
1369
|
|
|
1332
1370
|
// src/notes/provision.ts
|
|
1333
1371
|
import { copyFileSync, existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "node:fs";
|
|
@@ -1380,15 +1418,15 @@ function provisionProjectScripts(root) {
|
|
|
1380
1418
|
if (pkg === null || typeof pkg !== "object" || Array.isArray(pkg)) throw new Error("package.json \u5FC5\u987B\u662F\u5BF9\u8C61");
|
|
1381
1419
|
}
|
|
1382
1420
|
pkg.scripts = pkg.scripts !== null && typeof pkg.scripts === "object" && !Array.isArray(pkg.scripts) ? pkg.scripts : {};
|
|
1383
|
-
const
|
|
1421
|
+
const command2 = "node .agents/gate/verify-notes.mjs";
|
|
1384
1422
|
const existing = pkg.scripts["verify-notes"];
|
|
1385
|
-
if (existing === void 0) pkg.scripts["verify-notes"] =
|
|
1423
|
+
if (existing === void 0) pkg.scripts["verify-notes"] = command2;
|
|
1386
1424
|
writeFileSync3(packagePath, `${JSON.stringify(pkg, null, 2)}
|
|
1387
1425
|
`);
|
|
1388
1426
|
return {
|
|
1389
1427
|
launcher: path8.relative(root, launcher).replaceAll("\\", "/"),
|
|
1390
1428
|
package: path8.relative(root, packagePath).replaceAll("\\", "/"),
|
|
1391
|
-
command: existing === void 0 ?
|
|
1429
|
+
command: existing === void 0 ? command2 : String(existing),
|
|
1392
1430
|
changed: existing === void 0
|
|
1393
1431
|
};
|
|
1394
1432
|
}
|
|
@@ -1401,6 +1439,7 @@ var BASE_GITIGNORE = `node_modules/
|
|
|
1401
1439
|
Thumbs.db
|
|
1402
1440
|
coverage/
|
|
1403
1441
|
dist/
|
|
1442
|
+
.scratch/
|
|
1404
1443
|
.agents/notes/.gate-finalize-*
|
|
1405
1444
|
.agents/notes/.gate-validate-*
|
|
1406
1445
|
.agents/notes/*.gate-backup
|
|
@@ -1448,50 +1487,243 @@ function provisionCi(root) {
|
|
|
1448
1487
|
return { workflow: path8.relative(root, workflow).replaceAll("\\", "/"), scripts: path8.relative(root, ciDir).replaceAll("\\", "/"), changed: !existing };
|
|
1449
1488
|
}
|
|
1450
1489
|
|
|
1490
|
+
// src/artifacts/policy.ts
|
|
1491
|
+
import { existsSync as existsSync4, lstatSync as lstatSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "node:fs";
|
|
1492
|
+
import path9 from "node:path";
|
|
1493
|
+
var ARTIFACT_POLICY_PATH = ".agents/gate/artifact-policy.json";
|
|
1494
|
+
var BASE_ARTIFACT_POLICY = {
|
|
1495
|
+
version: 1,
|
|
1496
|
+
artifacts: {
|
|
1497
|
+
handoff: { phases: ["*"], roles: ["root"], paths: [".scratch/handoff/**"] },
|
|
1498
|
+
prd: { phases: ["discuss"], roles: ["root"], paths: ["docs/prd/**", ".scratch/prd/**"] },
|
|
1499
|
+
research: {
|
|
1500
|
+
phases: ["discuss", "orchestrating/plan"],
|
|
1501
|
+
roles: ["root", "worker"],
|
|
1502
|
+
paths: ["docs/research/**", ".scratch/research/**"]
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
};
|
|
1506
|
+
var PHASES2 = new Set(METHODOLOGY_KEYS);
|
|
1507
|
+
var RESERVED = [".agents/gate/artifact-policy.json", ".agents/notes/"];
|
|
1508
|
+
function normalizeArtifactPath(value) {
|
|
1509
|
+
return path9.posix.normalize(String(value).replaceAll("\\", "/").replace(/^\.\//u, ""));
|
|
1510
|
+
}
|
|
1511
|
+
function isReservedPath(value) {
|
|
1512
|
+
const normalized = value.toLowerCase();
|
|
1513
|
+
return RESERVED.some((prefix) => normalized === prefix || normalized.startsWith(prefix.endsWith("/") ? prefix : `${prefix}/`));
|
|
1514
|
+
}
|
|
1515
|
+
function artifactPolicyText(policy = BASE_ARTIFACT_POLICY) {
|
|
1516
|
+
return `${JSON.stringify(policy, null, 2)}
|
|
1517
|
+
`;
|
|
1518
|
+
}
|
|
1519
|
+
function provisionArtifactPolicy(root) {
|
|
1520
|
+
const file = path9.join(root, ARTIFACT_POLICY_PATH);
|
|
1521
|
+
if (pathHasSymlink(root, file)) throw new Error("artifact policy \u8DEF\u5F84\u5305\u542B symlink");
|
|
1522
|
+
mkdirSync4(path9.dirname(file), { recursive: true });
|
|
1523
|
+
if (existsSync4(file)) return { path: ARTIFACT_POLICY_PATH, status: "existing" };
|
|
1524
|
+
writeFileSync4(file, artifactPolicyText());
|
|
1525
|
+
return { path: ARTIFACT_POLICY_PATH, status: "created" };
|
|
1526
|
+
}
|
|
1527
|
+
function parseArtifactPolicy(value) {
|
|
1528
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return { error: "artifact policy \u5FC5\u987B\u662F\u5BF9\u8C61" };
|
|
1529
|
+
const source = value;
|
|
1530
|
+
if (source.version !== 1) return { error: "artifact policy version \u5FC5\u987B\u4E3A 1" };
|
|
1531
|
+
if (source.artifacts === null || typeof source.artifacts !== "object" || Array.isArray(source.artifacts)) {
|
|
1532
|
+
return { error: "artifact policy artifacts \u5FC5\u987B\u662F\u5BF9\u8C61" };
|
|
1533
|
+
}
|
|
1534
|
+
for (const key of Object.keys(source)) {
|
|
1535
|
+
if (key !== "version" && key !== "artifacts") return { error: `artifact policy \u672A\u77E5\u5B57\u6BB5\uFF1A${key}` };
|
|
1536
|
+
}
|
|
1537
|
+
const artifacts = {};
|
|
1538
|
+
for (const [kind, raw] of Object.entries(source.artifacts)) {
|
|
1539
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return { error: `artifact ${kind} \u5FC5\u987B\u662F\u5BF9\u8C61` };
|
|
1540
|
+
const rule = raw;
|
|
1541
|
+
for (const key of Object.keys(rule)) {
|
|
1542
|
+
if (key !== "phases" && key !== "roles" && key !== "paths") return { error: `artifact ${kind} \u672A\u77E5\u5B57\u6BB5\uFF1A${key}` };
|
|
1543
|
+
}
|
|
1544
|
+
if (!Array.isArray(rule.phases) || rule.phases.length === 0 || !rule.phases.every((phase) => phase === "*" || typeof phase === "string" && PHASES2.has(phase))) {
|
|
1545
|
+
return { error: `artifact ${kind} phases \u975E\u6CD5` };
|
|
1546
|
+
}
|
|
1547
|
+
if (!Array.isArray(rule.roles) || rule.roles.length === 0 || !rule.roles.every((role) => role === "root" || role === "worker")) {
|
|
1548
|
+
return { error: `artifact ${kind} roles \u975E\u6CD5` };
|
|
1549
|
+
}
|
|
1550
|
+
if (!Array.isArray(rule.paths) || rule.paths.length === 0) return { error: `artifact ${kind} paths \u975E\u6CD5` };
|
|
1551
|
+
const paths = [];
|
|
1552
|
+
for (const item of rule.paths) {
|
|
1553
|
+
if (typeof item !== "string" || item.trim() === "") return { error: `artifact ${kind} path \u975E\u6CD5` };
|
|
1554
|
+
const normalized = normalizeArtifactPath(item);
|
|
1555
|
+
if (path9.isAbsolute(normalized) || normalized.split("/").includes("..")) return { error: `artifact ${kind} path \u8D8A\u754C\uFF1A${item}` };
|
|
1556
|
+
if (isReservedPath(normalized)) return { error: `artifact ${kind} path \u662F\u4FDD\u7559\u8DEF\u5F84\uFF1A${item}` };
|
|
1557
|
+
paths.push(normalized);
|
|
1558
|
+
}
|
|
1559
|
+
artifacts[kind] = { phases: [...rule.phases], roles: [...rule.roles], paths };
|
|
1560
|
+
}
|
|
1561
|
+
return { policy: { version: 1, artifacts } };
|
|
1562
|
+
}
|
|
1563
|
+
function loadArtifactPolicy(root) {
|
|
1564
|
+
const file = path9.join(root, ARTIFACT_POLICY_PATH);
|
|
1565
|
+
if (!existsSync4(file)) return { error: "artifact policy \u4E0D\u5B58\u5728\uFF1B\u5148\u8FD0\u884C /gate-init \u5E76\u586B\u5199 .agents/gate/artifact-policy.json" };
|
|
1566
|
+
if (lstatSync4(file).isSymbolicLink()) return { error: "artifact policy \u4E0D\u80FD\u662F symlink" };
|
|
1567
|
+
try {
|
|
1568
|
+
return parseArtifactPolicy(JSON.parse(readFileSync3(file, "utf8")));
|
|
1569
|
+
} catch {
|
|
1570
|
+
return { error: "artifact policy \u4E0D\u662F\u6709\u6548 JSON" };
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
function segmentMatches(pattern, value) {
|
|
1574
|
+
const regex = new RegExp(`^${pattern.split("").map((char) => {
|
|
1575
|
+
if (char === "*") return "[^/]*";
|
|
1576
|
+
if (char === "?") return "[^/]";
|
|
1577
|
+
return char.replace(/[\\^$.*+?()[\]{}|]/gu, "\\$&");
|
|
1578
|
+
}).join("")}$`, "u");
|
|
1579
|
+
return regex.test(value);
|
|
1580
|
+
}
|
|
1581
|
+
function pathMatches(pattern, relative) {
|
|
1582
|
+
const patternParts = pattern.split("/").filter(Boolean);
|
|
1583
|
+
const valueParts = relative.split("/").filter(Boolean);
|
|
1584
|
+
const walk = (p, v) => {
|
|
1585
|
+
if (p === patternParts.length) return v === valueParts.length;
|
|
1586
|
+
const part = patternParts[p] ?? "";
|
|
1587
|
+
if (part === "**") return walk(p + 1, v) || v < valueParts.length && walk(p, v + 1);
|
|
1588
|
+
if (v >= valueParts.length) return false;
|
|
1589
|
+
if (!segmentMatches(part, valueParts[v] ?? "")) return false;
|
|
1590
|
+
return walk(p + 1, v + 1);
|
|
1591
|
+
};
|
|
1592
|
+
return walk(0, 0);
|
|
1593
|
+
}
|
|
1594
|
+
function artifactRuleAllows(policy, state, worker, kind) {
|
|
1595
|
+
if (state.phase === "blocked") return { error: "blocked \u4E0D\u5141\u8BB8 artifact \u5199\u5165" };
|
|
1596
|
+
const rule = policy.artifacts[kind];
|
|
1597
|
+
if (rule === void 0) return { error: `artifact kind \u672A\u6388\u6743\uFF1A${kind}` };
|
|
1598
|
+
const phaseKey = methodologyKeyFor(state);
|
|
1599
|
+
if (!rule.phases.includes("*") && !rule.phases.includes(phaseKey)) return { error: `\u5F53\u524D\u9636\u6BB5 ${phaseKey} \u4E0D\u5141\u8BB8 artifact ${kind}` };
|
|
1600
|
+
if (!rule.roles.includes(worker ? "worker" : "root")) return { error: `\u5F53\u524D\u89D2\u8272\u4E0D\u5141\u8BB8 artifact ${kind}` };
|
|
1601
|
+
return { rule };
|
|
1602
|
+
}
|
|
1603
|
+
function checkArtifactPath(root, rule, requested, kind) {
|
|
1604
|
+
const normalized = normalizeArtifactPath(requested);
|
|
1605
|
+
if (path9.isAbsolute(normalized) || normalized.split("/").includes("..")) return { error: "artifact path \u8D8A\u754C" };
|
|
1606
|
+
if (isReservedPath(normalized)) return { error: "artifact path \u662F\u4FDD\u7559\u8DEF\u5F84" };
|
|
1607
|
+
if (!rule.paths.some((pattern) => pathMatches(pattern, normalized))) return { error: `artifact ${kind} path \u4E0D\u5728\u767D\u540D\u5355\uFF1A${requested}` };
|
|
1608
|
+
const absolute = path9.resolve(root, normalized);
|
|
1609
|
+
if (!isInside(root, absolute)) return { error: "artifact path \u8D8A\u754C" };
|
|
1610
|
+
if (pathHasSymlink(root, absolute)) return { error: "artifact path \u5305\u542B symlink" };
|
|
1611
|
+
return { absolute, relative: path9.relative(root, absolute).replaceAll("\\", "/") };
|
|
1612
|
+
}
|
|
1613
|
+
function writeArtifact(root, requested, content) {
|
|
1614
|
+
const normalized = normalizeArtifactPath(requested);
|
|
1615
|
+
if (path9.isAbsolute(normalized) || normalized.split("/").includes("..") || isReservedPath(normalized)) return { error: "artifact path \u88AB\u62D2\u7EDD" };
|
|
1616
|
+
const target = path9.resolve(root, normalized);
|
|
1617
|
+
if (!isInside(root, target)) return { error: "artifact path \u8D8A\u754C" };
|
|
1618
|
+
if (pathHasSymlink(root, target)) return { error: "artifact path \u5305\u542B symlink" };
|
|
1619
|
+
mkdirSync4(path9.dirname(target), { recursive: true });
|
|
1620
|
+
writeFileSync4(target, content);
|
|
1621
|
+
return { relative: path9.relative(root, target).replaceAll("\\", "/") };
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1451
1624
|
// src/commands.ts
|
|
1452
1625
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
1626
|
+
import { readFileSync as readFileSync4 } from "node:fs";
|
|
1627
|
+
|
|
1628
|
+
// src/command-catalog.ts
|
|
1629
|
+
var GATE_COMMANDS = [
|
|
1630
|
+
{ name: "gate-init", description: "\u521D\u59CB\u5316 notes\u3001AGENTS.md\u3001verify-notes\u3001CI\u3001git \u4E0E\u57FA\u7840 .gitignore", hint: null, when: "any" },
|
|
1631
|
+
{ name: "gate-enter-plan", description: "\u57FA\u4E8E\u8BA8\u8BBA\u5171\u8BC6\u3001\u65E2\u6709\u51B3\u7B56\u4E0E\u7528\u6237\u9700\u6C42\u8FDB\u5165 planning", hint: "<\u5177\u4F53\u5DE5\u4F5C\u4E8B\u9879\u6216\u4E0A\u4E0B\u6587\u8BED\u4E49>", when: "discuss/complete" },
|
|
1632
|
+
{ 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" },
|
|
1633
|
+
{ name: "gate-prd", description: "\u5728 discuss \u4E2D\u6309\u7528\u6237\u8981\u6C42\u521B\u5EFA\u6216\u66F4\u65B0 PRD", hint: "[\u4E3B\u9898\u6216\u4E0A\u4E0B\u6587\u8BED\u4E49]", when: "discuss" },
|
|
1634
|
+
{ 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" },
|
|
1635
|
+
{ name: "gate-docs", description: "\u8FDB\u5165\u6587\u6863\u6CBB\u7406\uFF0C\u6309\u9879\u76EE\u6587\u6863\u89C4\u8303\u5904\u7406", hint: "[\u6587\u6863\u8303\u56F4\u6216\u4E0A\u4E0B\u6587\u8BED\u4E49]", when: "discuss/complete" },
|
|
1636
|
+
{ 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" },
|
|
1637
|
+
{ 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" },
|
|
1638
|
+
{ 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" },
|
|
1639
|
+
{ 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" },
|
|
1640
|
+
{ 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" },
|
|
1641
|
+
{ name: "gate-return-discuss", description: "\u653E\u5F03\u5F53\u524D\u8282\u70B9\uFF0C\u56DE\u5230 discuss", hint: null, when: "non-blocked" },
|
|
1642
|
+
{ name: "gate-status", description: "\u67E5\u770B\u5F53\u524D phase/node\u3001procedure\u3001access \u4E0E\u4E0B\u4E00\u6B65\u547D\u4EE4", hint: null, when: "any" },
|
|
1643
|
+
{ name: "gate-note-verify", description: "\u53EA\u8BFB\u68C0\u67E5 notes tree/format/lifecycle/archived", hint: null, when: "any" },
|
|
1644
|
+
{ 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" },
|
|
1645
|
+
{ 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" },
|
|
1646
|
+
{ 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" }
|
|
1647
|
+
];
|
|
1648
|
+
function commandSpec(name) {
|
|
1649
|
+
return GATE_COMMANDS.find((command2) => command2.name === name);
|
|
1650
|
+
}
|
|
1651
|
+
function commandLabel(spec) {
|
|
1652
|
+
return `/${spec.name}`;
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
// src/command-next.ts
|
|
1656
|
+
function command(name, reason) {
|
|
1657
|
+
const spec = commandSpec(name);
|
|
1658
|
+
return { command: spec === void 0 ? `/${name}` : commandLabel(spec), reason };
|
|
1659
|
+
}
|
|
1660
|
+
function nextCommandsFor(state) {
|
|
1661
|
+
if (state.phase === "blocked") {
|
|
1662
|
+
return [command("gate-resume", "\u89E3\u51BB\u540E\u56DE\u5230\u6682\u505C\u524D\u8282\u70B9\uFF1B\u8F6E\u6B21\u8017\u5C3D\u65F6\u56DE discuss\u3002")];
|
|
1663
|
+
}
|
|
1664
|
+
if (state.phase === "discuss" || state.phase === "complete") {
|
|
1665
|
+
return [
|
|
1666
|
+
command("gate-enter-plan", "\u9700\u8981\u7528\u6237\u6388\u6743\u540E\u8FDB\u5165 planning\u3002"),
|
|
1667
|
+
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")
|
|
1668
|
+
];
|
|
1669
|
+
}
|
|
1670
|
+
return [];
|
|
1671
|
+
}
|
|
1672
|
+
function nextCommandsText(commands) {
|
|
1673
|
+
if (commands.length === 0) return "";
|
|
1674
|
+
return `\u4E0B\u4E00\u6B65\u547D\u4EE4\uFF1A${commands.map((item) => `${item.command}\uFF08${item.reason}\uFF09`).join("\uFF1B")}`;
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
// src/commands.ts
|
|
1453
1678
|
function registerCommands(ctx, deps) {
|
|
1454
1679
|
const { store, reviewLimit, wake } = deps;
|
|
1455
1680
|
ctx.inject(["commands"], (scope) => {
|
|
1456
|
-
const
|
|
1457
|
-
name
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1681
|
+
const command2 = (name, handler) => {
|
|
1682
|
+
const spec = commandSpec(name);
|
|
1683
|
+
if (spec === void 0) throw new Error(`unknown gate command: ${name}`);
|
|
1684
|
+
scope.commands.register({
|
|
1685
|
+
name: spec.name,
|
|
1686
|
+
description: spec.description,
|
|
1687
|
+
...spec.hint === null ? {} : { input: { hint: spec.hint } },
|
|
1688
|
+
handler
|
|
1689
|
+
});
|
|
1690
|
+
};
|
|
1691
|
+
command2("gate-init", async (invocation) => {
|
|
1463
1692
|
const workspace = workspaceBoundary(ctx, invocation.agent);
|
|
1464
1693
|
if (workspace === void 0) return { kind: "error", text: "\u65E0\u6CD5\u786E\u5B9A\u5F53\u524D\u4F1A\u8BDD\u5DE5\u4F5C\u533A" };
|
|
1465
|
-
const root =
|
|
1694
|
+
const root = path10.resolve(workspace);
|
|
1466
1695
|
let provisioned;
|
|
1467
1696
|
let projectScripts;
|
|
1468
1697
|
let projectCi;
|
|
1469
1698
|
let projectGit;
|
|
1699
|
+
let artifactPolicy;
|
|
1470
1700
|
try {
|
|
1471
1701
|
provisioned = provisionNotes(root);
|
|
1702
|
+
artifactPolicy = provisionArtifactPolicy(root);
|
|
1472
1703
|
projectCi = provisionCi(root);
|
|
1473
1704
|
projectScripts = provisionProjectScripts(root);
|
|
1474
1705
|
projectGit = provisionGit(root);
|
|
1475
1706
|
} catch (error) {
|
|
1476
1707
|
return { kind: "error", text: `notes \u521D\u59CB\u5316\u5931\u8D25\uFF1A${error instanceof Error ? error.message : String(error)}` };
|
|
1477
1708
|
}
|
|
1478
|
-
wake(invocation, store.read(root, invocation.agent), `Gate \u5DF2\u521D\u59CB\u5316\uFF1A${root}\uFF08phase=discuss\uFF09\uFF0Cnotes \u5DF2\u521D\u59CB\u5316\u4E3A ${provisioned.notes}\uFF0CAGENTS.md \u5DF2\u66F4\u65B0\uFF0Cverify-notes \u5DF2\u914D\u7F6E\u4E3A ${projectScripts.command}\uFF0CCI \u5DF2\u914D\u7F6E\u4E3A ${projectCi.workflow}\uFF0Cgit\uFF1A${projectGit.git}${projectGit.gitError === null ? "" : `\uFF08${projectGit.gitError}\uFF09`}\uFF0Cignore\uFF1A${projectGit.ignore}\u3002`, "gate-init");
|
|
1709
|
+
wake(invocation, store.read(root, invocation.agent), `Gate \u5DF2\u521D\u59CB\u5316\uFF1A${root}\uFF08phase=discuss\uFF09\uFF0Cnotes \u5DF2\u521D\u59CB\u5316\u4E3A ${provisioned.notes}\uFF0Cartifact policy \u4E3A ${artifactPolicy.path}\uFF08${artifactPolicy.status}\uFF09\uFF0CAGENTS.md \u5DF2\u66F4\u65B0\uFF0Cverify-notes \u5DF2\u914D\u7F6E\u4E3A ${projectScripts.command}\uFF0CCI \u5DF2\u914D\u7F6E\u4E3A ${projectCi.workflow}\uFF0Cgit\uFF1A${projectGit.git}${projectGit.gitError === null ? "" : `\uFF08${projectGit.gitError}\uFF09`}\uFF0Cignore\uFF1A${projectGit.ignore}\u3002`, "gate-init");
|
|
1479
1710
|
return { kind: "success", text: `Gate \u5DF2\u521D\u59CB\u5316\uFF1A${root}\uFF08phase=discuss\uFF09
|
|
1480
1711
|
notes\uFF1A${provisioned.notes}
|
|
1712
|
+
artifact policy\uFF1A${artifactPolicy.path}\uFF08${artifactPolicy.status}\uFF09
|
|
1481
1713
|
AGENTS\uFF1A${provisioned.agents}
|
|
1482
1714
|
verify-notes\uFF1A${projectScripts.command}
|
|
1483
1715
|
CI\uFF1A${projectCi.workflow}
|
|
1484
1716
|
git\uFF1A${projectGit.git}${projectGit.gitError === null ? "" : `\uFF08${projectGit.gitError}\uFF09`}
|
|
1485
1717
|
ignore\uFF1A${projectGit.ignore}` };
|
|
1486
|
-
}
|
|
1487
|
-
|
|
1718
|
+
});
|
|
1719
|
+
command2("gate-enter-plan", async (invocation) => {
|
|
1488
1720
|
const result = enterPhase(ctx, invocation.agent, "plan", reviewLimit, store, void 0);
|
|
1489
1721
|
if (result.kind === "error") return result;
|
|
1490
1722
|
const brief = String(invocation.rawInput ?? "").trim();
|
|
1491
1723
|
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");
|
|
1492
1724
|
return { kind: "success", text: "\u8FDB\u5165 planning\u3002" };
|
|
1493
|
-
}
|
|
1494
|
-
|
|
1725
|
+
});
|
|
1726
|
+
command2("gate-enter-implement", async (invocation) => {
|
|
1495
1727
|
const brief = String(invocation.rawInput ?? "").trim();
|
|
1496
1728
|
const gate = resolveGate(ctx, invocation.agent);
|
|
1497
1729
|
const state = store.read(gate.root, invocation.agent);
|
|
@@ -1513,8 +1745,21 @@ ignore\uFF1A${projectGit.ignore}` };
|
|
|
1513
1745
|
const constraints = direct ? "\uFF08\u672C\u53E5\u5373\u5DE5\u4F5C\u9879\u7B80\u8FF0\uFF09" : brief === "" ? "\uFF08\u65E0\u989D\u5916\u7EA6\u675F\uFF09" : brief;
|
|
1514
1746
|
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");
|
|
1515
1747
|
return { kind: "success", text: `\u8FDB\u5165 implement\uFF1A${result.state.task}${direct ? "\uFF08\u76F4\u63A5\u5B9E\u65BD\uFF09" : ""}` };
|
|
1516
|
-
}
|
|
1517
|
-
|
|
1748
|
+
});
|
|
1749
|
+
command2("gate-prd", async (invocation) => {
|
|
1750
|
+
const gate = resolveGate(ctx, invocation.agent);
|
|
1751
|
+
const state = store.read(gate.root, invocation.agent);
|
|
1752
|
+
if (state.phase !== "discuss") return { kind: "error", text: "gate-prd \u53EA\u5728 discuss \u9636\u6BB5\u53EF\u7528" };
|
|
1753
|
+
const topic = String(invocation.rawInput ?? "").trim() || "\u5F53\u524D\u8BA8\u8BBA";
|
|
1754
|
+
let skill = "";
|
|
1755
|
+
try {
|
|
1756
|
+
skill = readFileSync4(path10.join(SKILLS, "gate-prd", "SKILL.md"), "utf8");
|
|
1757
|
+
} catch {
|
|
1758
|
+
}
|
|
1759
|
+
wake(invocation, state, ["\u8BF7\u6309 gate-prd \u6280\u80FD\u521B\u5EFA\u6216\u66F4\u65B0 PRD\u3002", "\u4E3B\u9898/\u4E0A\u4E0B\u6587\uFF1A" + topic, skill].filter(Boolean).join("\n\n"), "gate-prd");
|
|
1760
|
+
return { kind: "success", text: "\u5DF2\u8BF7\u6C42 PRD\uFF1A" + topic };
|
|
1761
|
+
});
|
|
1762
|
+
command2("gate-fix-bug", async (invocation) => {
|
|
1518
1763
|
const brief = String(invocation.rawInput ?? "").trim();
|
|
1519
1764
|
const gate = resolveGate(ctx, invocation.agent);
|
|
1520
1765
|
const state = store.read(gate.root, invocation.agent);
|
|
@@ -1530,23 +1775,24 @@ ignore\uFF1A${projectGit.ignore}` };
|
|
|
1530
1775
|
store.write(gate.root, invocation.agent, state);
|
|
1531
1776
|
wake(invocation, state, `Gate \u5DF2\u8FDB\u5165 fix-bug\uFF1A${scope2}\u3002`, "gate-fix-bug");
|
|
1532
1777
|
return { kind: "success", text: `\u8FDB\u5165 fix-bug\uFF1A${scope2}` };
|
|
1533
|
-
}
|
|
1534
|
-
|
|
1778
|
+
});
|
|
1779
|
+
command2("gate-docs", async (invocation) => {
|
|
1780
|
+
const scope2 = String(invocation.rawInput ?? "").trim() || "\u6587\u6863\u6CBB\u7406";
|
|
1535
1781
|
const gate = resolveGate(ctx, invocation.agent);
|
|
1536
1782
|
const state = store.read(gate.root, invocation.agent);
|
|
1537
1783
|
if (state.phase !== "discuss" && state.phase !== "complete") return { kind: "error", text: `\u5F53\u524D phase \u662F ${state.phase}` };
|
|
1538
1784
|
if (state.work === "blocked" || state.blockReason !== null) return { kind: "error", text: `\u5F53\u524D\u88AB blocked\uFF1A${state.blockReason ?? "unknown"}\uFF1B\u5148 /gate-resume` };
|
|
1539
1785
|
state.phase = "docs";
|
|
1540
|
-
state.task =
|
|
1786
|
+
state.task = scope2;
|
|
1541
1787
|
state.work = "active";
|
|
1542
1788
|
state.blockReason = null;
|
|
1543
1789
|
state.blockedFrom = null;
|
|
1544
1790
|
state.node = null;
|
|
1545
1791
|
store.write(gate.root, invocation.agent, state);
|
|
1546
|
-
wake(invocation, state,
|
|
1547
|
-
return { kind: "success", text:
|
|
1548
|
-
}
|
|
1549
|
-
|
|
1792
|
+
wake(invocation, state, `Gate \u5DF2\u8FDB\u5165 docs\uFF1A${scope2}\u3002`, "gate-docs");
|
|
1793
|
+
return { kind: "success", text: `\u8FDB\u5165 docs\uFF1A${scope2}` };
|
|
1794
|
+
});
|
|
1795
|
+
command2("gate-implement-review", async (invocation) => {
|
|
1550
1796
|
const brief = String(invocation.rawInput ?? "").trim();
|
|
1551
1797
|
const gate = resolveGate(ctx, invocation.agent);
|
|
1552
1798
|
const state = store.read(gate.root, invocation.agent);
|
|
@@ -1563,7 +1809,7 @@ ignore\uFF1A${projectGit.ignore}` };
|
|
|
1563
1809
|
wake(invocation, state, `Gate \u5DF2\u8FDB\u5165\u53EA\u8BFB code review\uFF1A${scope2}\u3002`, "gate-implement-review");
|
|
1564
1810
|
return { kind: "success", text: `\u8FDB\u5165\u53EA\u8BFB code review\uFF1A${scope2}` };
|
|
1565
1811
|
});
|
|
1566
|
-
|
|
1812
|
+
command2("gate-code-verify", async (invocation) => {
|
|
1567
1813
|
const brief = String(invocation.rawInput ?? "").trim();
|
|
1568
1814
|
const gate = resolveGate(ctx, invocation.agent);
|
|
1569
1815
|
const state = store.read(gate.root, invocation.agent);
|
|
@@ -1580,7 +1826,7 @@ ignore\uFF1A${projectGit.ignore}` };
|
|
|
1580
1826
|
wake(invocation, state, `Gate \u5DF2\u8FDB\u5165\u72EC\u7ACB\u9A8C\u6536\uFF1A${scope2}\u3002`, "gate-code-verify");
|
|
1581
1827
|
return { kind: "success", text: `\u8FDB\u5165\u72EC\u7ACB\u9A8C\u6536\uFF1A${scope2}` };
|
|
1582
1828
|
});
|
|
1583
|
-
|
|
1829
|
+
command2("gate-propose-review", async (invocation) => {
|
|
1584
1830
|
const brief = String(invocation.rawInput ?? "").trim();
|
|
1585
1831
|
const gate = resolveGate(ctx, invocation.agent);
|
|
1586
1832
|
const state = store.read(gate.root, invocation.agent);
|
|
@@ -1597,7 +1843,7 @@ ignore\uFF1A${projectGit.ignore}` };
|
|
|
1597
1843
|
wake(invocation, state, `Gate \u5DF2\u8FDB\u5165\u63D0\u6848\u8BC4\u5BA1\uFF1A${scope2}\u3002`, "gate-propose-review");
|
|
1598
1844
|
return { kind: "success", text: `\u8FDB\u5165\u63D0\u6848\u8BC4\u5BA1\uFF1A${scope2}` };
|
|
1599
1845
|
});
|
|
1600
|
-
|
|
1846
|
+
command2("gate-block", async (invocation) => {
|
|
1601
1847
|
const gate = resolveGate(ctx, invocation.agent);
|
|
1602
1848
|
const state = store.read(gate.root, invocation.agent);
|
|
1603
1849
|
if (state.phase === "blocked") return { kind: "success", text: "\u5DF2\u7ECF\u662F blocked" };
|
|
@@ -1609,9 +1855,9 @@ ignore\uFF1A${projectGit.ignore}` };
|
|
|
1609
1855
|
store.write(gate.root, invocation.agent, state);
|
|
1610
1856
|
return { kind: "success", text: `\u5DF2\u6682\u505C\uFF1A${state.blockReason}
|
|
1611
1857
|
\u89E3\u51BB\u540E\uFF1A${state.blockedFrom?.phase ?? "discuss"}${state.blockedFrom?.node === null || state.blockedFrom?.node === void 0 ? "" : ` / ${state.blockedFrom.node}`}
|
|
1612
|
-
\u540C\u4E00\u4E8B\u9879\uFF1A/gate-resume \u56DE\u5230\u8BE5\u8282\u70B9\uFF1B\u6539\u53D8\u65B9\u6848\uFF1A/gate-return-discuss\u3002` };
|
|
1613
|
-
}
|
|
1614
|
-
|
|
1858
|
+
\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` };
|
|
1859
|
+
});
|
|
1860
|
+
command2("gate-resume", async (invocation) => {
|
|
1615
1861
|
const gate = resolveGate(ctx, invocation.agent);
|
|
1616
1862
|
const state = store.read(gate.root, invocation.agent);
|
|
1617
1863
|
if (state.phase !== "blocked") return { kind: "error", text: "\u5F53\u524D\u4E0D\u662F blocked" };
|
|
@@ -1628,8 +1874,8 @@ ignore\uFF1A${projectGit.ignore}` };
|
|
|
1628
1874
|
}
|
|
1629
1875
|
const exhausted = reason === "plan-review-rounds-exhausted" || reason === "code-review-rounds-exhausted";
|
|
1630
1876
|
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"}` };
|
|
1631
|
-
}
|
|
1632
|
-
|
|
1877
|
+
});
|
|
1878
|
+
command2("gate-return-discuss", async (invocation) => {
|
|
1633
1879
|
const gate = resolveGate(ctx, invocation.agent);
|
|
1634
1880
|
const state = store.read(gate.root, invocation.agent);
|
|
1635
1881
|
if (state.phase === "blocked") return { kind: "error", text: `\u5F53\u524D blocked\uFF1A${state.blockReason ?? "unknown"}\uFF1B\u5148 /gate-resume` };
|
|
@@ -1651,21 +1897,22 @@ ignore\uFF1A${projectGit.ignore}` };
|
|
|
1651
1897
|
state.pending = {};
|
|
1652
1898
|
store.write(gate.root, invocation.agent, state);
|
|
1653
1899
|
return { kind: "success", text: "\u5DF2\u56DE\u5230 discuss" };
|
|
1654
|
-
}
|
|
1655
|
-
|
|
1900
|
+
});
|
|
1901
|
+
command2("gate-status", async (invocation) => {
|
|
1656
1902
|
const gate = resolveGate(ctx, invocation.agent);
|
|
1657
1903
|
const root = rootAgentOf(ctx, invocation.agent);
|
|
1658
1904
|
const state = store.read(gate.root, root);
|
|
1659
1905
|
const worker = root?.id !== invocation.agent?.id;
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1906
|
+
const next = worker ? "" : nextCommandsText(nextCommandsFor(state));
|
|
1907
|
+
return { kind: "success", text: [accessText(accessFor(state, worker)), statusText(state, reviewLimit), next].filter(Boolean).join("\n\n") };
|
|
1908
|
+
});
|
|
1909
|
+
command2("gate-note-verify", async (invocation) => {
|
|
1663
1910
|
const gate = resolveGate(ctx, invocation.agent);
|
|
1664
1911
|
const check = runNotesScripts(gate.root);
|
|
1665
1912
|
return check.ok ? { kind: "success", text: check.results.map((item) => `${item.name}: ok`).join("\n") } : { kind: "error", text: `notes \u6821\u9A8C\u5931\u8D25\uFF1A
|
|
1666
1913
|
${JSON.stringify(check.results, null, 2)}` };
|
|
1667
|
-
}
|
|
1668
|
-
|
|
1914
|
+
});
|
|
1915
|
+
command2("gate-note-archive", async (invocation) => {
|
|
1669
1916
|
const gate = resolveGate(ctx, invocation.agent);
|
|
1670
1917
|
const raw = String(invocation.rawInput ?? "").trim();
|
|
1671
1918
|
const args = raw === "" ? [] : raw.split(/\s+/u);
|
|
@@ -1673,41 +1920,41 @@ ${JSON.stringify(check.results, null, 2)}` };
|
|
|
1673
1920
|
const node = resolveNodeBinary();
|
|
1674
1921
|
if (node === void 0) return { kind: "error", text: "notes-runtime-unavailable" };
|
|
1675
1922
|
try {
|
|
1676
|
-
const script =
|
|
1677
|
-
const output = execFileSync3(node, [script, "--root",
|
|
1923
|
+
const script = path10.join(NOTES_SKILL_DIR, "scripts", "archive-agent-note.ts");
|
|
1924
|
+
const output = execFileSync3(node, [script, "--root", path10.join(gate.root, ".agents", "notes"), ...args], { cwd: gate.root, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
|
|
1678
1925
|
return { kind: "success", text: String(output).trim() || "Note \u5DF2\u5F52\u6863" };
|
|
1679
1926
|
} catch (error) {
|
|
1680
1927
|
return { kind: "error", text: String(error?.stderr ?? error?.message ?? error).trim() };
|
|
1681
1928
|
}
|
|
1682
|
-
}
|
|
1683
|
-
|
|
1929
|
+
});
|
|
1930
|
+
command2("gate-note-board", async (invocation) => {
|
|
1684
1931
|
const gate = resolveGate(ctx, invocation.agent);
|
|
1685
1932
|
const raw = String(invocation.rawInput ?? "").trim();
|
|
1686
1933
|
const args = raw === "" ? [] : raw.split(/\s+/u);
|
|
1687
1934
|
const target = args[0] ?? "board.html";
|
|
1688
|
-
const title = args[1] ?? `${
|
|
1935
|
+
const title = args[1] ?? `${path10.basename(gate.root)} \u51B3\u7B56\u770B\u677F`;
|
|
1689
1936
|
const node = resolveNodeBinary();
|
|
1690
1937
|
if (node === void 0) return { kind: "error", text: "notes-runtime-unavailable" };
|
|
1691
1938
|
try {
|
|
1692
|
-
const script =
|
|
1939
|
+
const script = path10.join(NOTES_SKILL_DIR, "scripts", "build-board.ts");
|
|
1693
1940
|
const output = execFileSync3(node, [script, "--init", target, title], { cwd: gate.root, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
|
|
1694
1941
|
return { kind: "success", text: String(output).trim() || `\u51B3\u7B56\u770B\u677F\u5DF2\u751F\u6210\uFF1A${target}` };
|
|
1695
1942
|
} catch (error) {
|
|
1696
1943
|
return { kind: "error", text: String(error?.stderr ?? error?.message ?? error).trim() };
|
|
1697
1944
|
}
|
|
1698
|
-
}
|
|
1699
|
-
|
|
1945
|
+
});
|
|
1946
|
+
command2("gate-note-seal", async (invocation) => {
|
|
1700
1947
|
const gate = resolveGate(ctx, invocation.agent);
|
|
1701
1948
|
const node = resolveNodeBinary();
|
|
1702
1949
|
if (node === void 0) return { kind: "error", text: "notes-runtime-unavailable" };
|
|
1703
1950
|
try {
|
|
1704
|
-
const script =
|
|
1705
|
-
const output = execFileSync3(node, [script, "--root",
|
|
1951
|
+
const script = path10.join(NOTES_SKILL_DIR, "scripts", "verify-archived-agent-notes.ts");
|
|
1952
|
+
const output = execFileSync3(node, [script, "--root", path10.join(gate.root, ".agents", "notes"), "--write"], { cwd: gate.root, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
|
|
1706
1953
|
return { kind: "success", text: String(output).trim() || "archived manifest \u5DF2\u5199\u5165" };
|
|
1707
1954
|
} catch (error) {
|
|
1708
1955
|
return { kind: "error", text: String(error?.stderr ?? error?.message ?? error).trim() };
|
|
1709
1956
|
}
|
|
1710
|
-
}
|
|
1957
|
+
});
|
|
1711
1958
|
});
|
|
1712
1959
|
}
|
|
1713
1960
|
|
|
@@ -1771,7 +2018,7 @@ function registerStartPhase(ctx, deps) {
|
|
|
1771
2018
|
|
|
1772
2019
|
// src/tools/record.ts
|
|
1773
2020
|
import { createHash } from "node:crypto";
|
|
1774
|
-
import { existsSync as
|
|
2021
|
+
import { existsSync as existsSync5, lstatSync as lstatSync5, readFileSync as readFileSync5 } from "node:fs";
|
|
1775
2022
|
function registerRecord(ctx, deps) {
|
|
1776
2023
|
const { store, reviewLimit, wake } = deps;
|
|
1777
2024
|
ctx.tools.register({
|
|
@@ -1977,11 +2224,11 @@ function registerRecord(ctx, deps) {
|
|
|
1977
2224
|
for (const item of evidence) {
|
|
1978
2225
|
const target = classifyPath(gate.workspace, item);
|
|
1979
2226
|
try {
|
|
1980
|
-
if (target.outside || !
|
|
2227
|
+
if (target.outside || !existsSync5(target.absolute) || !lstatSync5(target.absolute).isFile()) {
|
|
1981
2228
|
invalidEvidence.push(item);
|
|
1982
2229
|
continue;
|
|
1983
2230
|
}
|
|
1984
|
-
const bytes =
|
|
2231
|
+
const bytes = readFileSync5(target.absolute);
|
|
1985
2232
|
records.push({ path: target.relative, size: bytes.length, sha256: createHash("sha256").update(bytes).digest("hex") });
|
|
1986
2233
|
} catch {
|
|
1987
2234
|
invalidEvidence.push(item);
|
|
@@ -2081,8 +2328,10 @@ function registerStatus(ctx, deps) {
|
|
|
2081
2328
|
const worker = root?.id !== exec.agent?.id;
|
|
2082
2329
|
const access = accessFor(state, worker);
|
|
2083
2330
|
if (worker) {
|
|
2084
|
-
return { ok: true, procedure: null, method: null, access, text: [accessText(access), statusText(state, reviewLimit)].join("\n\n") };
|
|
2331
|
+
return { ok: true, procedure: null, method: null, access, nextCommands: [], text: [accessText(access), statusText(state, reviewLimit)].join("\n\n") };
|
|
2085
2332
|
}
|
|
2333
|
+
const nextCommands = nextCommandsFor(state);
|
|
2334
|
+
const nextText = nextCommandsText(nextCommands);
|
|
2086
2335
|
const procedure = procedureFor(state, { reviewLimit });
|
|
2087
2336
|
const includeRounds = !procedure.hasRounds;
|
|
2088
2337
|
return {
|
|
@@ -2090,15 +2339,16 @@ function registerStatus(ctx, deps) {
|
|
|
2090
2339
|
procedure,
|
|
2091
2340
|
method: procedure.method,
|
|
2092
2341
|
access,
|
|
2093
|
-
|
|
2342
|
+
nextCommands,
|
|
2343
|
+
text: procedureResultText(procedure, [statusText(state, reviewLimit, { includeRounds }), nextText, accessText(access)].filter(Boolean).join("\n\n"))
|
|
2094
2344
|
};
|
|
2095
2345
|
}
|
|
2096
2346
|
});
|
|
2097
2347
|
}
|
|
2098
2348
|
|
|
2099
2349
|
// src/tools/notes.ts
|
|
2100
|
-
import { existsSync as
|
|
2101
|
-
import
|
|
2350
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
|
|
2351
|
+
import path11 from "node:path";
|
|
2102
2352
|
function registerNoteTools(ctx, deps) {
|
|
2103
2353
|
const { store, reviewLimit, wake } = deps;
|
|
2104
2354
|
ctx.tools.register({
|
|
@@ -2124,7 +2374,7 @@ function registerNoteTools(ctx, deps) {
|
|
|
2124
2374
|
return { ok: false, code: "planning-only", reason: "gate_note_write \u53EA\u5141\u8BB8 planning worker \u5728 plan/plan-revise \u8282\u70B9\u4F7F\u7528\u3002" };
|
|
2125
2375
|
}
|
|
2126
2376
|
let relative = String(args.path ?? "").replaceAll("\\", "/").replace(/^\.?\//u, "");
|
|
2127
|
-
if (relative === "" || relative.includes("..") ||
|
|
2377
|
+
if (relative === "" || relative.includes("..") || path11.isAbsolute(relative) || !relative.endsWith(".md")) {
|
|
2128
2378
|
return { ok: false, code: "invalid-path" };
|
|
2129
2379
|
}
|
|
2130
2380
|
let lifecycle = "proposed";
|
|
@@ -2136,16 +2386,16 @@ function registerNoteTools(ctx, deps) {
|
|
|
2136
2386
|
if (!/^(?:feature|bug-fix|simplification|architecture|process|testing)\/\d{4}-\d{2}-\d{2}-.+\.md$/u.test(relative)) {
|
|
2137
2387
|
return { ok: false, code: "invalid-path", reason: "\u9700\u8981 <class>/yyyy-mm-dd-topic.md\uFF0C\u6216 proposed|implemented/<class>/...\u3002" };
|
|
2138
2388
|
}
|
|
2139
|
-
const base =
|
|
2140
|
-
const absolute =
|
|
2389
|
+
const base = path11.join(gate.root, ".agents", "notes", String(lifecycle));
|
|
2390
|
+
const absolute = path11.resolve(base, relative);
|
|
2141
2391
|
if (!isInside(base, absolute)) return { ok: false, code: "invalid-path" };
|
|
2142
2392
|
if (pathHasSymlink(base, absolute)) return { ok: false, code: "invalid-path" };
|
|
2143
|
-
if (lifecycle === "implemented" && !
|
|
2393
|
+
if (lifecycle === "implemented" && !existsSync6(absolute)) {
|
|
2144
2394
|
return { ok: false, code: "implemented-must-exist", reason: "implemented Note \u53EA\u80FD\u539F\u5730\u7F16\u8F91\uFF0C\u4E0D\u80FD\u65B0\u5EFA\u3002" };
|
|
2145
2395
|
}
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
return { ok: true, path:
|
|
2396
|
+
mkdirSync5(path11.dirname(absolute), { recursive: true });
|
|
2397
|
+
writeFileSync5(absolute, String(args.content ?? ""));
|
|
2398
|
+
return { ok: true, path: path11.relative(gate.root, absolute).replaceAll("\\", "/"), lifecycle };
|
|
2149
2399
|
}
|
|
2150
2400
|
});
|
|
2151
2401
|
ctx.tools.register({
|
|
@@ -2168,7 +2418,7 @@ function registerNoteTools(ctx, deps) {
|
|
|
2168
2418
|
if (!check.ok) return { ...check, state: normalizeState(state) };
|
|
2169
2419
|
if (state.node === "note-lifecycle") {
|
|
2170
2420
|
if (state.noteFinalized !== true) return { ok: false, code: "note-lifecycle-incomplete", reason: "\u5148\u8C03\u7528 gate_note_finalize\u3002", state: normalizeState(state) };
|
|
2171
|
-
const implemented = state.notePaths.length > 0 && state.notePaths.every((entry) => entry.lifecycle === "implemented" &&
|
|
2421
|
+
const implemented = state.notePaths.length > 0 && state.notePaths.every((entry) => entry.lifecycle === "implemented" && existsSync6(path11.join(gate.root, entry.path)));
|
|
2172
2422
|
if (!implemented) return { ok: false, code: "note-lifecycle-incomplete", reason: "notePaths \u5C1A\u672A\u5168\u90E8\u8FDB\u5165 implemented\u3002", results: check.results, state: normalizeState(state) };
|
|
2173
2423
|
state.noteVerified = true;
|
|
2174
2424
|
store.write(gate.root, exec.agent, state);
|
|
@@ -2216,9 +2466,9 @@ function registerNoteTools(ctx, deps) {
|
|
|
2216
2466
|
const recovered = recoverFinalize(gate.root, sessionId);
|
|
2217
2467
|
for (const entry of state.notePaths) {
|
|
2218
2468
|
if (entry.lifecycle !== "proposed") continue;
|
|
2219
|
-
const source =
|
|
2469
|
+
const source = path11.join(gate.root, entry.path);
|
|
2220
2470
|
const targetRelative = entry.path.replaceAll("\\", "/").replace(".agents/notes/proposed/", ".agents/notes/implemented/");
|
|
2221
|
-
if (!
|
|
2471
|
+
if (!existsSync6(source) && existsSync6(path11.join(gate.root, targetRelative))) {
|
|
2222
2472
|
entry.path = targetRelative;
|
|
2223
2473
|
entry.lifecycle = "implemented";
|
|
2224
2474
|
recovered.push("adopted-target");
|
|
@@ -2251,12 +2501,193 @@ function registerNoteTools(ctx, deps) {
|
|
|
2251
2501
|
});
|
|
2252
2502
|
}
|
|
2253
2503
|
|
|
2504
|
+
// src/tools/catalog.ts
|
|
2505
|
+
var GATE_TOOLS = [
|
|
2506
|
+
{ name: "gate_status", summary: "\u8BFB\u53D6\u5F53\u524D phase/node\u3001procedure\u3001access \u548C nextCommands\uFF1Broot/worker \u5747\u53EF\u8C03\u7528\u3002" },
|
|
2507
|
+
{ name: "gate_record", summary: "\u8BB0\u5F55 worker \u7ED3\u679C\u5E76\u63A8\u8FDB gate\uFF1B\u4EC5 root \u53EF\u8C03\u7528\u3002" },
|
|
2508
|
+
{ 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" },
|
|
2509
|
+
{ name: "gate_note_write", summary: "planning worker \u5199 proposed Note \u6216\u539F\u5730\u66F4\u65B0 implemented Note\u3002" },
|
|
2510
|
+
{ name: "gate_note_verify", summary: "\u5728 note-lifecycle \u4E2D\u8FD0\u884C notes \u6821\u9A8C\uFF1Broot-only\u3002" },
|
|
2511
|
+
{ name: "gate_note_finalize", summary: "\u628A proposed Note \u79FB\u5230 implemented \u5E76\u66F4\u65B0\u5934\u5757\uFF1Broot-only\u3002" },
|
|
2512
|
+
{ name: "gate_help", summary: "\u5217\u51FA gate \u547D\u4EE4\u3001\u5DE5\u5177\u548C\u5F53\u524D phase/node\uFF1B\u53EA\u8BFB\uFF0Croot-only\u3002" },
|
|
2513
|
+
{ name: "gate_artifact_write", summary: "\u6309 artifact policy \u5199\u767D\u540D\u5355\u6587\u4EF6\uFF1B\u4E0D\u8D70\u666E\u901A workspace write\u3002" },
|
|
2514
|
+
{ name: "gate_research_dispatch", summary: "root-only \u5728 discuss/plan \u6D3E research worker\uFF1Bworker \u53EA\u5199 research artifact\u3002" }
|
|
2515
|
+
];
|
|
2516
|
+
|
|
2517
|
+
// src/tools/help.ts
|
|
2518
|
+
function commandLine(spec) {
|
|
2519
|
+
const hint = spec.hint === null ? "" : ` ${spec.hint}`;
|
|
2520
|
+
return `- ${commandLabel(spec)}${hint} \u2014 ${spec.description}\uFF08${spec.when}\uFF09`;
|
|
2521
|
+
}
|
|
2522
|
+
function helpText(phase, node) {
|
|
2523
|
+
return [
|
|
2524
|
+
"## Gate help",
|
|
2525
|
+
`\u5F53\u524D phase/node\uFF1A${phase} / ${node ?? "\u65E0"}`,
|
|
2526
|
+
"",
|
|
2527
|
+
"\u547D\u4EE4\uFF08\u7531\u7528\u6237\u6267\u884C\uFF09\uFF1A",
|
|
2528
|
+
...GATE_COMMANDS.map(commandLine),
|
|
2529
|
+
"",
|
|
2530
|
+
"\u5DE5\u5177\uFF08\u7531\u6A21\u578B\u8C03\u7528\uFF09\uFF1A",
|
|
2531
|
+
...GATE_TOOLS.map((tool) => `- ${tool.name} \u2014 ${tool.summary}`),
|
|
2532
|
+
"",
|
|
2533
|
+
"\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"
|
|
2534
|
+
].join("\n");
|
|
2535
|
+
}
|
|
2536
|
+
function registerHelp(ctx, deps) {
|
|
2537
|
+
const { store } = deps;
|
|
2538
|
+
ctx.tools.register({
|
|
2539
|
+
name: "gate_help",
|
|
2540
|
+
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",
|
|
2541
|
+
parameters: { type: "object", additionalProperties: false, properties: {} },
|
|
2542
|
+
output: {
|
|
2543
|
+
schema: { type: "object", additionalProperties: true },
|
|
2544
|
+
render: (_args, value) => [{ type: "text", text: typeof value?.text === "string" ? value.text : JSON.stringify(value) }]
|
|
2545
|
+
},
|
|
2546
|
+
async execute(_args, exec) {
|
|
2547
|
+
const root = rootAgentOf(ctx, exec.agent);
|
|
2548
|
+
if (root?.id !== exec.agent?.id) return { ok: false, code: "root-only", reason: "gate_help \u53EA\u80FD\u7531 root agent \u8C03\u7528\u3002" };
|
|
2549
|
+
const gate = resolveGate(ctx, exec.agent);
|
|
2550
|
+
if (gate.root === void 0) return { ok: false, code: "state-unavailable", reason: "\u65E0\u6CD5\u786E\u5B9A\u5DE5\u4F5C\u533A\u3002" };
|
|
2551
|
+
const state = store.read(gate.root, root);
|
|
2552
|
+
return {
|
|
2553
|
+
ok: true,
|
|
2554
|
+
phase: state.phase,
|
|
2555
|
+
node: state.node,
|
|
2556
|
+
current: { phase: state.phase, node: state.node },
|
|
2557
|
+
note: "\u547D\u4EE4\u7531\u7528\u6237\u6267\u884C\uFF1B\u5DE5\u5177\u7531\u6A21\u578B\u8C03\u7528\u3002",
|
|
2558
|
+
commands: GATE_COMMANDS.map((spec) => ({ ...spec, command: commandLabel(spec) })),
|
|
2559
|
+
tools: GATE_TOOLS,
|
|
2560
|
+
text: helpText(state.phase, state.node)
|
|
2561
|
+
};
|
|
2562
|
+
}
|
|
2563
|
+
});
|
|
2564
|
+
}
|
|
2565
|
+
|
|
2566
|
+
// src/tools/artifact.ts
|
|
2567
|
+
function registerArtifactTools(ctx, deps) {
|
|
2568
|
+
const { store } = deps;
|
|
2569
|
+
ctx.tools.register({
|
|
2570
|
+
name: "gate_artifact_write",
|
|
2571
|
+
description: "\u6309 .agents/gate/artifact-policy.json \u5199\u767D\u540D\u5355 artifact\uFF08handoff/prd/research \u7B49\uFF09\u3002\u53EA\u5199\u6587\u4EF6\uFF0C\u4E0D\u8D70\u666E\u901A workspace write \u6743\u9650\u3002",
|
|
2572
|
+
parameters: {
|
|
2573
|
+
type: "object",
|
|
2574
|
+
additionalProperties: false,
|
|
2575
|
+
properties: {
|
|
2576
|
+
kind: { type: "string", description: "policy \u4E2D\u7684 artifact kind\u3002" },
|
|
2577
|
+
path: { type: "string", description: "workspace \u5185\u76F8\u5BF9\u8DEF\u5F84\u3002" },
|
|
2578
|
+
content: { type: "string" }
|
|
2579
|
+
},
|
|
2580
|
+
required: ["kind", "path", "content"]
|
|
2581
|
+
},
|
|
2582
|
+
output: {
|
|
2583
|
+
schema: { type: "object", additionalProperties: true },
|
|
2584
|
+
render: (_args, value) => [{ type: "text", text: typeof value?.text === "string" ? value.text : JSON.stringify(value) }]
|
|
2585
|
+
},
|
|
2586
|
+
async execute(args, exec) {
|
|
2587
|
+
const gate = resolveGate(ctx, exec.agent);
|
|
2588
|
+
if (gate.root === void 0) return { ok: false, code: "state-unavailable", reason: "\u65E0\u6CD5\u786E\u5B9A\u5DE5\u4F5C\u533A\u3002" };
|
|
2589
|
+
const effective = effectiveState(ctx, exec.agent, gate, store);
|
|
2590
|
+
const loaded = loadArtifactPolicy(gate.root);
|
|
2591
|
+
if (loaded.policy === void 0) return { ok: false, code: "artifact-policy-invalid", reason: loaded.error };
|
|
2592
|
+
const allowed = artifactRuleAllows(loaded.policy, effective.state, effective.worker, String(args?.kind ?? ""));
|
|
2593
|
+
if (allowed.rule === void 0) return { ok: false, code: "artifact-denied", reason: allowed.error };
|
|
2594
|
+
const checked = checkArtifactPath(gate.root, allowed.rule, String(args?.path ?? ""), String(args?.kind ?? ""));
|
|
2595
|
+
if (checked.absolute === void 0) return { ok: false, code: "artifact-denied", reason: checked.error };
|
|
2596
|
+
const written = writeArtifact(gate.root, checked.relative ?? String(args?.path ?? ""), String(args?.content ?? ""));
|
|
2597
|
+
if (written.relative === void 0) return { ok: false, code: "artifact-write-failed", reason: written.error };
|
|
2598
|
+
return { ok: true, kind: String(args.kind), path: written.relative, bytes: Buffer.byteLength(String(args.content ?? "")) };
|
|
2599
|
+
}
|
|
2600
|
+
});
|
|
2601
|
+
}
|
|
2602
|
+
|
|
2603
|
+
// src/tools/research.ts
|
|
2604
|
+
function registerResearchDispatch(ctx, deps) {
|
|
2605
|
+
const { store } = deps;
|
|
2606
|
+
ctx.tools.register({
|
|
2607
|
+
name: "gate_research_dispatch",
|
|
2608
|
+
description: "root-only\uFF1A\u5728 discuss \u6216 orchestrating/plan \u6D3E research worker\uFF0C\u7B49\u5F85\u7ED3\u679C\uFF1Bworker \u53EA\u80FD\u901A\u8FC7 artifact policy \u5199 research \u6587\u6863\u3002",
|
|
2609
|
+
parameters: {
|
|
2610
|
+
type: "object",
|
|
2611
|
+
additionalProperties: false,
|
|
2612
|
+
properties: {
|
|
2613
|
+
question: { type: "string" },
|
|
2614
|
+
purpose: { type: "string" }
|
|
2615
|
+
},
|
|
2616
|
+
required: ["question"]
|
|
2617
|
+
},
|
|
2618
|
+
output: {
|
|
2619
|
+
schema: { type: "object", additionalProperties: true },
|
|
2620
|
+
render: (_args, value) => [{ type: "text", text: typeof value?.text === "string" ? value.text : JSON.stringify(value) }]
|
|
2621
|
+
},
|
|
2622
|
+
async execute(args, exec) {
|
|
2623
|
+
const root = rootAgentOf(ctx, exec.agent);
|
|
2624
|
+
if (root?.id !== exec.agent?.id) return { ok: false, code: "root-only", reason: "gate_research_dispatch \u53EA\u80FD\u7531 root agent \u8C03\u7528\u3002" };
|
|
2625
|
+
const gate = resolveGate(ctx, exec.agent);
|
|
2626
|
+
if (gate.root === void 0) return { ok: false, code: "state-unavailable", reason: "\u65E0\u6CD5\u786E\u5B9A\u5DE5\u4F5C\u533A\u3002" };
|
|
2627
|
+
const state = store.read(gate.root, root);
|
|
2628
|
+
if (!(state.phase === "discuss" || state.phase === "orchestrating" && state.node === "plan")) {
|
|
2629
|
+
return { ok: false, code: "wrong-phase", reason: "research dispatch \u53EA\u5141\u8BB8 discuss \u6216 orchestrating/plan\u3002" };
|
|
2630
|
+
}
|
|
2631
|
+
const loaded = loadArtifactPolicy(gate.root);
|
|
2632
|
+
if (loaded.policy === void 0) return { ok: false, code: "artifact-policy-invalid", reason: loaded.error };
|
|
2633
|
+
const allowed = artifactRuleAllows(loaded.policy, state, true, "research");
|
|
2634
|
+
if (allowed.rule === void 0) return { ok: false, code: "artifact-denied", reason: allowed.error };
|
|
2635
|
+
const subagents = typeof ctx.get === "function" ? ctx.get("subagents") : void 0;
|
|
2636
|
+
if (subagents?.startActivation === void 0 || subagents?.getProvider === void 0) {
|
|
2637
|
+
return { ok: false, code: "subagents-unavailable", reason: "ctx.subagents \u4E0D\u53EF\u7528\uFF0C\u65E0\u6CD5\u6D3E research worker\u3002" };
|
|
2638
|
+
}
|
|
2639
|
+
const provider = subagents.getProvider("spawn");
|
|
2640
|
+
if (provider === void 0) return { ok: false, code: "provider-unavailable", reason: "ctx.subagents \u6CA1\u6709 spawn provider\u3002" };
|
|
2641
|
+
const question = String(args?.question ?? "").trim();
|
|
2642
|
+
if (question === "") return { ok: false, code: "question-required", reason: "research \u9700\u8981 question\u3002" };
|
|
2643
|
+
const purpose = String(args?.purpose ?? "").trim();
|
|
2644
|
+
const prompt = [{
|
|
2645
|
+
type: "text",
|
|
2646
|
+
text: [
|
|
2647
|
+
"Research worker task. Investigate primary sources and write a Markdown report.",
|
|
2648
|
+
`Question: ${question}`,
|
|
2649
|
+
purpose === "" ? "" : `Purpose: ${purpose}`,
|
|
2650
|
+
"Use gate_artifact_write({ kind: 'research', path, content }) to save the report.",
|
|
2651
|
+
"Prefer project docs research location; otherwise .scratch/research/. Cite sources. Do not modify product code."
|
|
2652
|
+
].filter(Boolean).join("\n")
|
|
2653
|
+
}];
|
|
2654
|
+
let activation;
|
|
2655
|
+
try {
|
|
2656
|
+
activation = await subagents.startActivation({
|
|
2657
|
+
provider: "spawn",
|
|
2658
|
+
label: "gate-research",
|
|
2659
|
+
prompt,
|
|
2660
|
+
parent: exec.agent,
|
|
2661
|
+
signal: exec.signal,
|
|
2662
|
+
delivery: "caller"
|
|
2663
|
+
});
|
|
2664
|
+
const result = await activation.result;
|
|
2665
|
+
const output = result?.output;
|
|
2666
|
+
const text = typeof output === "string" ? output : JSON.stringify(output ?? result);
|
|
2667
|
+
return { ok: true, name: activation.name, text };
|
|
2668
|
+
} catch (error) {
|
|
2669
|
+
return { ok: false, code: "research-dispatch-failed", reason: error instanceof Error ? error.message : String(error) };
|
|
2670
|
+
} finally {
|
|
2671
|
+
if (activation?.dispose !== void 0) {
|
|
2672
|
+
try {
|
|
2673
|
+
await activation.dispose();
|
|
2674
|
+
} catch {
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
});
|
|
2680
|
+
}
|
|
2681
|
+
|
|
2254
2682
|
// src/tools/index.ts
|
|
2255
2683
|
function registerTools(ctx, deps) {
|
|
2256
2684
|
registerStartPhase(ctx, deps);
|
|
2257
2685
|
registerRecord(ctx, deps);
|
|
2258
2686
|
registerStatus(ctx, deps);
|
|
2259
2687
|
registerNoteTools(ctx, deps);
|
|
2688
|
+
registerHelp(ctx, deps);
|
|
2689
|
+
registerArtifactTools(ctx, deps);
|
|
2690
|
+
registerResearchDispatch(ctx, deps);
|
|
2260
2691
|
}
|
|
2261
2692
|
|
|
2262
2693
|
// src/index.ts
|