dsh-gate-agent 0.1.3 → 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 +20 -17
- package/lib/index.js +232 -75
- package/package.json +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-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/README.md
CHANGED
|
@@ -73,7 +73,7 @@ Host 插件:用 phase/node 门禁组织讨论、规划、实施、评审、验
|
|
|
73
73
|
| `reviewing/proposal-review` | 单独提案评审 | review worker;`gate-plan-review` |
|
|
74
74
|
| `reviewing/verify-standalone` | 单独只读验收 | verify worker;`gate-user-verify` |
|
|
75
75
|
| `docs` | 文档治理 | root;不调用 gate 技能 |
|
|
76
|
-
| `blocked` | 冻结写入/派 worker,等待用户决定 | root;`/gate-resume`
|
|
76
|
+
| `blocked` | 冻结写入/派 worker,等待用户决定 | root;`/gate-resume` |
|
|
77
77
|
| `complete` | 工作完成 | root;可重新进入 plan/implement |
|
|
78
78
|
|
|
79
79
|
进入阶段不等于执行。主代理按当前 procedure 加载技能、派 worker,再用 `gate_record` 推进节点。
|
|
@@ -109,23 +109,23 @@ Host 插件:用 phase/node 门禁组织讨论、规划、实施、评审、验
|
|
|
109
109
|
| 命令 | 参数 | 作用 |
|
|
110
110
|
|---|---|---|
|
|
111
111
|
| `/gate-init` | 无 | 初始化 notes、AGENTS.md、`verify-notes`、CI、git 与基础 `.gitignore`,保持 `discuss` |
|
|
112
|
-
| `/gate-enter-plan [
|
|
113
|
-
| `/gate-enter-implement [
|
|
114
|
-
| `/gate-docs` | 无 |
|
|
115
|
-
| `/gate-implement-review [
|
|
116
|
-
| `/gate-code-verify [
|
|
117
|
-
| `/gate-fix-bug [
|
|
118
|
-
| `/gate-propose-review [
|
|
112
|
+
| `/gate-enter-plan [具体工作事项或上下文语义]` | 可选 | 基于讨论共识、既有决策与用户输入进入 `orchestrating/plan` |
|
|
113
|
+
| `/gate-enter-implement [具体工作事项或上下文语义]` | 可选 | 基于已确认计划或讨论后的实施说明进入 `orchestrating/implement` |
|
|
114
|
+
| `/gate-docs` | 无 | 进入文档治理,按项目文档规范处理 |
|
|
115
|
+
| `/gate-implement-review [具体工作事项或上下文语义]` | 可选 | 对当前改动做独立只读 code review,只输出 findings |
|
|
116
|
+
| `/gate-code-verify [具体工作事项或上下文语义]` | 可选 | 对当前改动做独立只读验收(gate-user-verify) |
|
|
117
|
+
| `/gate-fix-bug [具体工作事项或上下文语义]` | 可选 | 基于描述或当前上下文派 fix worker 修复已授权缺陷 |
|
|
118
|
+
| `/gate-propose-review [具体工作事项或上下文语义]` | 可选 | 对提案做独立只读三轴评审 |
|
|
119
119
|
| `/gate-note-verify` | 无 | 只读检查 notes tree/format/lifecycle/archived |
|
|
120
120
|
| `/gate-note-archive <note> [--superseded-by <new>]` | 必需 Note 路径 | 归档 implemented Note,写 Archived、移动、封印、报告入站链接 |
|
|
121
121
|
| `/gate-note-board [输出路径] [标题]` | 可选 | 生成决策看板 `board.html` |
|
|
122
122
|
| `/gate-note-seal` | 无 | 只给 `archived/` 中已有 Note 补写 `manifest.json` 的 SHA-256 封印 |
|
|
123
123
|
| `/gate-block [原因]` | 可选 | 手动暂停,保留当前节点 |
|
|
124
124
|
| `/gate-resume` | 无 | 解冻 blocked,回到 block 前的节点;轮次耗尽回 discuss |
|
|
125
|
-
| `/gate-return-discuss` | 无 |
|
|
126
|
-
| `/gate-status` | 无 | 只读显示当前 phase/node
|
|
125
|
+
| `/gate-return-discuss` | 无 | 放弃当前节点,回到 discuss |
|
|
126
|
+
| `/gate-status` | 无 | 只读显示当前 phase/node、procedure、access 与下一步命令 |
|
|
127
127
|
|
|
128
|
-
直接 implement(`/gate-enter-implement [
|
|
128
|
+
直接 implement(`/gate-enter-implement [具体工作事项或上下文语义]` 或 `gate_start_phase({node:'implement',brief})`)会自动标记 `mechanical`,跳过 Note lifecycle;不需要 `mechanical:` 前缀。plan/plan-revise 手动选择 `mechanical` 时也不要求前缀,只需 `notePaths: []`。
|
|
129
129
|
|
|
130
130
|
离开 discuss 的授权可来自对话(包括要求用户再输入 slash command)或 `ask_user_question`;明确授权后,主代理可调用 `gate_start_phase({node:'plan'|'implement', brief})`。
|
|
131
131
|
|
|
@@ -190,7 +190,7 @@ gate 不自动 `git add`/`git commit`;完成 note-lifecycle 后由用户或主
|
|
|
190
190
|
### 单独 code review
|
|
191
191
|
|
|
192
192
|
```text
|
|
193
|
-
/gate-implement-review [
|
|
193
|
+
/gate-implement-review [具体工作事项或上下文语义]
|
|
194
194
|
```
|
|
195
195
|
|
|
196
196
|
只读 review,不修改被评审对象;findings 直接输出。
|
|
@@ -198,7 +198,7 @@ gate 不自动 `git add`/`git commit`;完成 note-lifecycle 后由用户或主
|
|
|
198
198
|
### 单独验收
|
|
199
199
|
|
|
200
200
|
```text
|
|
201
|
-
/gate-code-verify [
|
|
201
|
+
/gate-code-verify [具体工作事项或上下文语义]
|
|
202
202
|
```
|
|
203
203
|
|
|
204
204
|
主代理派只读 verify worker,使用 `gate-user-verify`;PASS 后 `gate_record({node:'verify-standalone', verification, workerSessionIds})` 回 discuss。
|
|
@@ -252,17 +252,20 @@ review worker 使用 `gate-plan-review` 做 Standards/Spec/Architecture 三轴
|
|
|
252
252
|
```text
|
|
253
253
|
/gate-block 等待产品决定
|
|
254
254
|
/gate-resume
|
|
255
|
-
/gate-return-discuss
|
|
256
255
|
/gate-status
|
|
257
256
|
```
|
|
258
257
|
|
|
259
258
|
- `/gate-block`:保留当前节点,冻结写入与 worker 派发,对话仍可继续。
|
|
260
|
-
- `/gate-resume
|
|
261
|
-
- `/gate-return-discuss
|
|
259
|
+
- `/gate-resume`:唯一的 blocked 恢复入口;回到 block 前的节点,review 轮次耗尽时回 discuss。
|
|
260
|
+
- `/gate-return-discuss`:放弃当前节点回 discuss;blocked 时不可直接用,先 `/gate-resume`。
|
|
262
261
|
- `/gate-status`:只读查看当前 phase/node 和 procedure。
|
|
263
262
|
|
|
264
263
|
## 工具
|
|
265
264
|
|
|
265
|
+
### gate_help
|
|
266
|
+
|
|
267
|
+
只读,root-only。列出全部 `/gate-*` 命令、`gate_*` 工具用途和当前 phase/node,并说明命令由用户执行、工具由模型调用。不确定命令名或工具用途时使用。
|
|
268
|
+
|
|
266
269
|
### gate_start_phase
|
|
267
270
|
|
|
268
271
|
只允许 root 调用,在用户已授权时从 `discuss`/`complete` 进入 `plan` 或 `implement`:
|
|
@@ -297,7 +300,7 @@ gate_record({
|
|
|
297
300
|
|
|
298
301
|
### gate_status
|
|
299
302
|
|
|
300
|
-
只读,root 和 worker 可调用。root 返回当前 procedure
|
|
303
|
+
只读,root 和 worker 可调用。root 返回当前 procedure、状态和 nextCommands;worker 只返回状态,不返回 root procedure。
|
|
301
304
|
|
|
302
305
|
```text
|
|
303
306
|
gate_status()
|
package/lib/index.js
CHANGED
|
@@ -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);
|
|
@@ -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
|
}
|
|
@@ -1450,16 +1488,70 @@ function provisionCi(root) {
|
|
|
1450
1488
|
|
|
1451
1489
|
// src/commands.ts
|
|
1452
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
|
|
1453
1541
|
function registerCommands(ctx, deps) {
|
|
1454
1542
|
const { store, reviewLimit, wake } = deps;
|
|
1455
1543
|
ctx.inject(["commands"], (scope) => {
|
|
1456
|
-
const
|
|
1457
|
-
name
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
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) => {
|
|
1463
1555
|
const workspace = workspaceBoundary(ctx, invocation.agent);
|
|
1464
1556
|
if (workspace === void 0) return { kind: "error", text: "\u65E0\u6CD5\u786E\u5B9A\u5F53\u524D\u4F1A\u8BDD\u5DE5\u4F5C\u533A" };
|
|
1465
1557
|
const root = path9.resolve(workspace);
|
|
@@ -1483,15 +1575,15 @@ verify-notes\uFF1A${projectScripts.command}
|
|
|
1483
1575
|
CI\uFF1A${projectCi.workflow}
|
|
1484
1576
|
git\uFF1A${projectGit.git}${projectGit.gitError === null ? "" : `\uFF08${projectGit.gitError}\uFF09`}
|
|
1485
1577
|
ignore\uFF1A${projectGit.ignore}` };
|
|
1486
|
-
}
|
|
1487
|
-
|
|
1578
|
+
});
|
|
1579
|
+
command2("gate-enter-plan", async (invocation) => {
|
|
1488
1580
|
const result = enterPhase(ctx, invocation.agent, "plan", reviewLimit, store, void 0);
|
|
1489
1581
|
if (result.kind === "error") return result;
|
|
1490
1582
|
const brief = String(invocation.rawInput ?? "").trim();
|
|
1491
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");
|
|
1492
1584
|
return { kind: "success", text: "\u8FDB\u5165 planning\u3002" };
|
|
1493
|
-
}
|
|
1494
|
-
|
|
1585
|
+
});
|
|
1586
|
+
command2("gate-enter-implement", async (invocation) => {
|
|
1495
1587
|
const brief = String(invocation.rawInput ?? "").trim();
|
|
1496
1588
|
const gate = resolveGate(ctx, invocation.agent);
|
|
1497
1589
|
const state = store.read(gate.root, invocation.agent);
|
|
@@ -1513,8 +1605,8 @@ ignore\uFF1A${projectGit.ignore}` };
|
|
|
1513
1605
|
const constraints = direct ? "\uFF08\u672C\u53E5\u5373\u5DE5\u4F5C\u9879\u7B80\u8FF0\uFF09" : brief === "" ? "\uFF08\u65E0\u989D\u5916\u7EA6\u675F\uFF09" : brief;
|
|
1514
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");
|
|
1515
1607
|
return { kind: "success", text: `\u8FDB\u5165 implement\uFF1A${result.state.task}${direct ? "\uFF08\u76F4\u63A5\u5B9E\u65BD\uFF09" : ""}` };
|
|
1516
|
-
}
|
|
1517
|
-
|
|
1608
|
+
});
|
|
1609
|
+
command2("gate-fix-bug", async (invocation) => {
|
|
1518
1610
|
const brief = String(invocation.rawInput ?? "").trim();
|
|
1519
1611
|
const gate = resolveGate(ctx, invocation.agent);
|
|
1520
1612
|
const state = store.read(gate.root, invocation.agent);
|
|
@@ -1530,8 +1622,8 @@ ignore\uFF1A${projectGit.ignore}` };
|
|
|
1530
1622
|
store.write(gate.root, invocation.agent, state);
|
|
1531
1623
|
wake(invocation, state, `Gate \u5DF2\u8FDB\u5165 fix-bug\uFF1A${scope2}\u3002`, "gate-fix-bug");
|
|
1532
1624
|
return { kind: "success", text: `\u8FDB\u5165 fix-bug\uFF1A${scope2}` };
|
|
1533
|
-
}
|
|
1534
|
-
|
|
1625
|
+
});
|
|
1626
|
+
command2("gate-docs", async (invocation) => {
|
|
1535
1627
|
const gate = resolveGate(ctx, invocation.agent);
|
|
1536
1628
|
const state = store.read(gate.root, invocation.agent);
|
|
1537
1629
|
if (state.phase !== "discuss" && state.phase !== "complete") return { kind: "error", text: `\u5F53\u524D phase \u662F ${state.phase}` };
|
|
@@ -1545,8 +1637,8 @@ ignore\uFF1A${projectGit.ignore}` };
|
|
|
1545
1637
|
store.write(gate.root, invocation.agent, state);
|
|
1546
1638
|
wake(invocation, state, "Gate \u5DF2\u8FDB\u5165 docs\u3002", "gate-docs");
|
|
1547
1639
|
return { kind: "success", text: "\u8FDB\u5165 docs\uFF1A\u6587\u6863\u6CBB\u7406" };
|
|
1548
|
-
}
|
|
1549
|
-
|
|
1640
|
+
});
|
|
1641
|
+
command2("gate-implement-review", async (invocation) => {
|
|
1550
1642
|
const brief = String(invocation.rawInput ?? "").trim();
|
|
1551
1643
|
const gate = resolveGate(ctx, invocation.agent);
|
|
1552
1644
|
const state = store.read(gate.root, invocation.agent);
|
|
@@ -1563,7 +1655,7 @@ ignore\uFF1A${projectGit.ignore}` };
|
|
|
1563
1655
|
wake(invocation, state, `Gate \u5DF2\u8FDB\u5165\u53EA\u8BFB code review\uFF1A${scope2}\u3002`, "gate-implement-review");
|
|
1564
1656
|
return { kind: "success", text: `\u8FDB\u5165\u53EA\u8BFB code review\uFF1A${scope2}` };
|
|
1565
1657
|
});
|
|
1566
|
-
|
|
1658
|
+
command2("gate-code-verify", async (invocation) => {
|
|
1567
1659
|
const brief = String(invocation.rawInput ?? "").trim();
|
|
1568
1660
|
const gate = resolveGate(ctx, invocation.agent);
|
|
1569
1661
|
const state = store.read(gate.root, invocation.agent);
|
|
@@ -1580,7 +1672,7 @@ ignore\uFF1A${projectGit.ignore}` };
|
|
|
1580
1672
|
wake(invocation, state, `Gate \u5DF2\u8FDB\u5165\u72EC\u7ACB\u9A8C\u6536\uFF1A${scope2}\u3002`, "gate-code-verify");
|
|
1581
1673
|
return { kind: "success", text: `\u8FDB\u5165\u72EC\u7ACB\u9A8C\u6536\uFF1A${scope2}` };
|
|
1582
1674
|
});
|
|
1583
|
-
|
|
1675
|
+
command2("gate-propose-review", async (invocation) => {
|
|
1584
1676
|
const brief = String(invocation.rawInput ?? "").trim();
|
|
1585
1677
|
const gate = resolveGate(ctx, invocation.agent);
|
|
1586
1678
|
const state = store.read(gate.root, invocation.agent);
|
|
@@ -1597,7 +1689,7 @@ ignore\uFF1A${projectGit.ignore}` };
|
|
|
1597
1689
|
wake(invocation, state, `Gate \u5DF2\u8FDB\u5165\u63D0\u6848\u8BC4\u5BA1\uFF1A${scope2}\u3002`, "gate-propose-review");
|
|
1598
1690
|
return { kind: "success", text: `\u8FDB\u5165\u63D0\u6848\u8BC4\u5BA1\uFF1A${scope2}` };
|
|
1599
1691
|
});
|
|
1600
|
-
|
|
1692
|
+
command2("gate-block", async (invocation) => {
|
|
1601
1693
|
const gate = resolveGate(ctx, invocation.agent);
|
|
1602
1694
|
const state = store.read(gate.root, invocation.agent);
|
|
1603
1695
|
if (state.phase === "blocked") return { kind: "success", text: "\u5DF2\u7ECF\u662F blocked" };
|
|
@@ -1609,9 +1701,9 @@ ignore\uFF1A${projectGit.ignore}` };
|
|
|
1609
1701
|
store.write(gate.root, invocation.agent, state);
|
|
1610
1702
|
return { kind: "success", text: `\u5DF2\u6682\u505C\uFF1A${state.blockReason}
|
|
1611
1703
|
\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
|
-
|
|
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) => {
|
|
1615
1707
|
const gate = resolveGate(ctx, invocation.agent);
|
|
1616
1708
|
const state = store.read(gate.root, invocation.agent);
|
|
1617
1709
|
if (state.phase !== "blocked") return { kind: "error", text: "\u5F53\u524D\u4E0D\u662F blocked" };
|
|
@@ -1628,8 +1720,8 @@ ignore\uFF1A${projectGit.ignore}` };
|
|
|
1628
1720
|
}
|
|
1629
1721
|
const exhausted = reason === "plan-review-rounds-exhausted" || reason === "code-review-rounds-exhausted";
|
|
1630
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"}` };
|
|
1631
|
-
}
|
|
1632
|
-
|
|
1723
|
+
});
|
|
1724
|
+
command2("gate-return-discuss", async (invocation) => {
|
|
1633
1725
|
const gate = resolveGate(ctx, invocation.agent);
|
|
1634
1726
|
const state = store.read(gate.root, invocation.agent);
|
|
1635
1727
|
if (state.phase === "blocked") return { kind: "error", text: `\u5F53\u524D blocked\uFF1A${state.blockReason ?? "unknown"}\uFF1B\u5148 /gate-resume` };
|
|
@@ -1651,21 +1743,22 @@ ignore\uFF1A${projectGit.ignore}` };
|
|
|
1651
1743
|
state.pending = {};
|
|
1652
1744
|
store.write(gate.root, invocation.agent, state);
|
|
1653
1745
|
return { kind: "success", text: "\u5DF2\u56DE\u5230 discuss" };
|
|
1654
|
-
}
|
|
1655
|
-
|
|
1746
|
+
});
|
|
1747
|
+
command2("gate-status", async (invocation) => {
|
|
1656
1748
|
const gate = resolveGate(ctx, invocation.agent);
|
|
1657
1749
|
const root = rootAgentOf(ctx, invocation.agent);
|
|
1658
1750
|
const state = store.read(gate.root, root);
|
|
1659
1751
|
const worker = root?.id !== invocation.agent?.id;
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
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) => {
|
|
1663
1756
|
const gate = resolveGate(ctx, invocation.agent);
|
|
1664
1757
|
const check = runNotesScripts(gate.root);
|
|
1665
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
|
|
1666
1759
|
${JSON.stringify(check.results, null, 2)}` };
|
|
1667
|
-
}
|
|
1668
|
-
|
|
1760
|
+
});
|
|
1761
|
+
command2("gate-note-archive", async (invocation) => {
|
|
1669
1762
|
const gate = resolveGate(ctx, invocation.agent);
|
|
1670
1763
|
const raw = String(invocation.rawInput ?? "").trim();
|
|
1671
1764
|
const args = raw === "" ? [] : raw.split(/\s+/u);
|
|
@@ -1679,8 +1772,8 @@ ${JSON.stringify(check.results, null, 2)}` };
|
|
|
1679
1772
|
} catch (error) {
|
|
1680
1773
|
return { kind: "error", text: String(error?.stderr ?? error?.message ?? error).trim() };
|
|
1681
1774
|
}
|
|
1682
|
-
}
|
|
1683
|
-
|
|
1775
|
+
});
|
|
1776
|
+
command2("gate-note-board", async (invocation) => {
|
|
1684
1777
|
const gate = resolveGate(ctx, invocation.agent);
|
|
1685
1778
|
const raw = String(invocation.rawInput ?? "").trim();
|
|
1686
1779
|
const args = raw === "" ? [] : raw.split(/\s+/u);
|
|
@@ -1695,8 +1788,8 @@ ${JSON.stringify(check.results, null, 2)}` };
|
|
|
1695
1788
|
} catch (error) {
|
|
1696
1789
|
return { kind: "error", text: String(error?.stderr ?? error?.message ?? error).trim() };
|
|
1697
1790
|
}
|
|
1698
|
-
}
|
|
1699
|
-
|
|
1791
|
+
});
|
|
1792
|
+
command2("gate-note-seal", async (invocation) => {
|
|
1700
1793
|
const gate = resolveGate(ctx, invocation.agent);
|
|
1701
1794
|
const node = resolveNodeBinary();
|
|
1702
1795
|
if (node === void 0) return { kind: "error", text: "notes-runtime-unavailable" };
|
|
@@ -1707,7 +1800,7 @@ ${JSON.stringify(check.results, null, 2)}` };
|
|
|
1707
1800
|
} catch (error) {
|
|
1708
1801
|
return { kind: "error", text: String(error?.stderr ?? error?.message ?? error).trim() };
|
|
1709
1802
|
}
|
|
1710
|
-
}
|
|
1803
|
+
});
|
|
1711
1804
|
});
|
|
1712
1805
|
}
|
|
1713
1806
|
|
|
@@ -2081,8 +2174,10 @@ function registerStatus(ctx, deps) {
|
|
|
2081
2174
|
const worker = root?.id !== exec.agent?.id;
|
|
2082
2175
|
const access = accessFor(state, worker);
|
|
2083
2176
|
if (worker) {
|
|
2084
|
-
return { ok: true, procedure: null, method: null, access, text: [accessText(access), statusText(state, reviewLimit)].join("\n\n") };
|
|
2177
|
+
return { ok: true, procedure: null, method: null, access, nextCommands: [], text: [accessText(access), statusText(state, reviewLimit)].join("\n\n") };
|
|
2085
2178
|
}
|
|
2179
|
+
const nextCommands = nextCommandsFor(state);
|
|
2180
|
+
const nextText = nextCommandsText(nextCommands);
|
|
2086
2181
|
const procedure = procedureFor(state, { reviewLimit });
|
|
2087
2182
|
const includeRounds = !procedure.hasRounds;
|
|
2088
2183
|
return {
|
|
@@ -2090,7 +2185,8 @@ function registerStatus(ctx, deps) {
|
|
|
2090
2185
|
procedure,
|
|
2091
2186
|
method: procedure.method,
|
|
2092
2187
|
access,
|
|
2093
|
-
|
|
2188
|
+
nextCommands,
|
|
2189
|
+
text: procedureResultText(procedure, [statusText(state, reviewLimit, { includeRounds }), nextText, accessText(access)].filter(Boolean).join("\n\n"))
|
|
2094
2190
|
};
|
|
2095
2191
|
}
|
|
2096
2192
|
});
|
|
@@ -2251,12 +2347,73 @@ function registerNoteTools(ctx, deps) {
|
|
|
2251
2347
|
});
|
|
2252
2348
|
}
|
|
2253
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
|
+
|
|
2254
2410
|
// src/tools/index.ts
|
|
2255
2411
|
function registerTools(ctx, deps) {
|
|
2256
2412
|
registerStartPhase(ctx, deps);
|
|
2257
2413
|
registerRecord(ctx, deps);
|
|
2258
2414
|
registerStatus(ctx, deps);
|
|
2259
2415
|
registerNoteTools(ctx, deps);
|
|
2416
|
+
registerHelp(ctx, deps);
|
|
2260
2417
|
}
|
|
2261
2418
|
|
|
2262
2419
|
// src/index.ts
|
package/package.json
CHANGED
|
@@ -56,11 +56,10 @@ user-invocable: false
|
|
|
56
56
|
- 产品范围、行为与验收问题报告给工作项;
|
|
57
57
|
- 本次规划的技术设计问题报告给 Agent Note;引用输入的问题按项目配置反馈其属主;
|
|
58
58
|
- 状态、评审与批准证据报告给工作项;
|
|
59
|
-
- 领域术语问题报告给 技能`gate-domain-modeling` 的属主,技术决定报告给相关 Agent Note。
|
|
60
59
|
|
|
61
60
|
由 planning worker 决定并执行修正;本 worker 不更新工作项、Note 或就绪状态。
|
|
62
61
|
|
|
63
|
-
|
|
62
|
+
纯行为规则归产品合同;普通实现细节不因“重要”就自动生成 Note。
|
|
64
63
|
|
|
65
64
|
设计仍依赖临时产品假设时保持非权威。修订产品合同后,检查所有承接同一产品合同的设计,不只修改最先暴露问题的那一份。
|
|
66
65
|
|
|
@@ -64,8 +64,6 @@ user-invocable: false
|
|
|
64
64
|
|
|
65
65
|
设计定稿遵循 技术设计规则([rfc.md](./rfc.md) ) 的合同稳定性与评审条件。Note 的 proposed/implemented 表示交付生命周期,不代替评审和批准证据。
|
|
66
66
|
|
|
67
|
-
使用项目既有领域语言和适用架构约束。领域术语与模型变化使用 `gate-domain-modeling`;需要为架构决定单独建档时,使用 `adr` 的准入条件判断,在相关 Agent Note 中记录决定。
|
|
68
|
-
|
|
69
67
|
### 决定权限
|
|
70
68
|
|
|
71
69
|
在已同意的产品合同、既有架构和项目约束内,自主解决普通技术选择。
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
# CONTEXT.md Format
|
|
2
|
-
|
|
3
|
-
## Structure
|
|
4
|
-
|
|
5
|
-
```md
|
|
6
|
-
# {Context Name}
|
|
7
|
-
|
|
8
|
-
{One or two sentence description of what this context is and why it exists.}
|
|
9
|
-
|
|
10
|
-
## Language
|
|
11
|
-
|
|
12
|
-
**Order**:
|
|
13
|
-
{A one or two sentence description of the term}
|
|
14
|
-
_Avoid_: Purchase, transaction
|
|
15
|
-
|
|
16
|
-
**Invoice**:
|
|
17
|
-
A request for payment sent to a customer after delivery.
|
|
18
|
-
_Avoid_: Bill, payment request
|
|
19
|
-
|
|
20
|
-
**Customer**:
|
|
21
|
-
A person or organization that places orders.
|
|
22
|
-
_Avoid_: Client, buyer, account
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## Rules
|
|
26
|
-
|
|
27
|
-
- **Be opinionated.** When multiple words exist for the same concept, pick the best one and list the others under `_Avoid_`.
|
|
28
|
-
- **Keep definitions tight.** One or two sentences max. Define what it IS, not what it does.
|
|
29
|
-
- **Only include terms specific to this project's context.** General programming concepts (timeouts, error types, utility patterns) don't belong even if the project uses them extensively. Before adding a term, ask: is this a concept unique to this context, or a general programming concept? Only the former belongs.
|
|
30
|
-
- **Group terms under subheadings** when natural clusters emerge. If all terms belong to a single cohesive area, a flat list is fine.
|
|
31
|
-
|
|
32
|
-
## Single vs multi-context repos
|
|
33
|
-
|
|
34
|
-
**Single context (most repos):** One `CONTEXT.md` at the repo root.
|
|
35
|
-
|
|
36
|
-
**Multiple contexts:** A `CONTEXT-MAP.md` at the repo root lists the contexts, where they live, and how they relate to each other:
|
|
37
|
-
|
|
38
|
-
```md
|
|
39
|
-
# Context Map
|
|
40
|
-
|
|
41
|
-
## Contexts
|
|
42
|
-
|
|
43
|
-
- [Ordering](./src/ordering/CONTEXT.md): receives and tracks customer orders
|
|
44
|
-
- [Billing](./src/billing/CONTEXT.md): generates invoices and processes payments
|
|
45
|
-
- [Fulfillment](./src/fulfillment/CONTEXT.md): manages warehouse picking and shipping
|
|
46
|
-
|
|
47
|
-
## Relationships
|
|
48
|
-
|
|
49
|
-
- **Ordering → Fulfillment**: Ordering emits `OrderPlaced` events; Fulfillment consumes them to start picking
|
|
50
|
-
- **Fulfillment → Billing**: Fulfillment emits `ShipmentDispatched` events; Billing consumes them to generate invoices
|
|
51
|
-
- **Ordering ↔ Billing**: Shared types for `CustomerId` and `Money`
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
The skill infers which structure applies:
|
|
55
|
-
|
|
56
|
-
Resolve the root from project instructions before these checks; the current working directory may be a subproject. Use the project documentation hierarchy for ownership; the context map does not create separate record categories. Paths shown in the map are Markdown links relative to the map file.
|
|
57
|
-
|
|
58
|
-
- If `CONTEXT-MAP.md` exists, read it to find contexts
|
|
59
|
-
- If only a root `CONTEXT.md` exists, single context
|
|
60
|
-
- If neither exists, create a root `CONTEXT.md` lazily when the first term is resolved
|
|
61
|
-
|
|
62
|
-
When multiple contexts exist, infer which one the current topic relates to. If unclear, ask.
|
|
63
|
-
|
|
64
|
-
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
# GLOSSARY.md Format
|
|
2
|
-
|
|
3
|
-
## Structure
|
|
4
|
-
|
|
5
|
-
```md
|
|
6
|
-
# {Context Name}
|
|
7
|
-
|
|
8
|
-
{One or two sentence description of what this context is and why it exists.}
|
|
9
|
-
|
|
10
|
-
## Language
|
|
11
|
-
|
|
12
|
-
**Order**:
|
|
13
|
-
{A one or two sentence description of the term}
|
|
14
|
-
_Avoid_: Purchase, transaction
|
|
15
|
-
|
|
16
|
-
**Invoice**:
|
|
17
|
-
A request for payment sent to a customer after delivery.
|
|
18
|
-
_Avoid_: Bill, payment request
|
|
19
|
-
|
|
20
|
-
**Customer**:
|
|
21
|
-
A person or organization that places orders.
|
|
22
|
-
_Avoid_: Client, buyer, account
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## Rules
|
|
26
|
-
|
|
27
|
-
- **Be opinionated.** When multiple words exist for the same concept, pick the best one and list the others under `_Avoid_`.
|
|
28
|
-
- **Keep definitions tight.** One or two sentences max. Define what it IS, not what it does.
|
|
29
|
-
- **Only include terms specific to this project's context.** General programming concepts (timeouts, error types, utility patterns) don't belong even if the project uses them extensively. Before adding a term, ask: is this a concept unique to this context, or a general programming concept? Only the former belongs.
|
|
30
|
-
- **Group terms under subheadings** when natural clusters emerge. If all terms belong to a single cohesive area, a flat list is fine.
|
|
31
|
-
|
|
32
|
-
## Single vs multi-context repos
|
|
33
|
-
|
|
34
|
-
**Single context (most repos):** One `GLOSSARY.md` at the repo root.
|
|
35
|
-
|
|
36
|
-
**Multiple contexts:** A `GLOSSARY-MAP.md` at the repo root lists the contexts, where they live, and how they relate to each other:
|
|
37
|
-
|
|
38
|
-
```md
|
|
39
|
-
# Glossary Map
|
|
40
|
-
|
|
41
|
-
## Contexts
|
|
42
|
-
|
|
43
|
-
- [Ordering](./src/ordering/GLOSSARY.md): receives and tracks customer orders
|
|
44
|
-
- [Billing](./src/billing/GLOSSARY.md): generates invoices and processes payments
|
|
45
|
-
- [Fulfillment](./src/fulfillment/GLOSSARY.md): manages warehouse picking and shipping
|
|
46
|
-
|
|
47
|
-
## Relationships
|
|
48
|
-
|
|
49
|
-
- **Ordering → Fulfillment**: Ordering emits `OrderPlaced` events; Fulfillment consumes them to start picking
|
|
50
|
-
- **Fulfillment → Billing**: Fulfillment emits `ShipmentDispatched` events; Billing consumes them to generate invoices
|
|
51
|
-
- **Ordering ↔ Billing**: Shared types for `CustomerId` and `Money`
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
The skill infers which structure applies:
|
|
55
|
-
|
|
56
|
-
Resolve the root from project instructions before these checks; the current working directory may be a subproject. Use the project documentation hierarchy for ownership; the context map does not create separate record categories. Paths shown in the map are Markdown links relative to the map file.
|
|
57
|
-
|
|
58
|
-
- If `GLOSSARY-MAP.md` exists, read it to find contexts
|
|
59
|
-
- If only a root `GLOSSARY.md` exists, single context
|
|
60
|
-
- If neither exists, create a root `GLOSSARY.md` lazily when the first term is resolved
|
|
61
|
-
|
|
62
|
-
When multiple contexts exist, infer which one the current topic relates to. If unclear, ask.
|
|
63
|
-
|
|
64
|
-
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: gate-domain-modeling
|
|
3
|
-
description: Build and sharpen a project's domain model. Use when discussing codebase terminology, writing or editing a GLOSSARY.md, or recording a durable decision in its selected owner.
|
|
4
|
-
user-invocable: false
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Domain Modeling
|
|
8
|
-
|
|
9
|
-
Actively build and sharpen the project's domain model as you design. This is the *active* discipline: challenging terms, inventing edge-case scenarios, and writing the glossary and decisions down the moment they crystallise. (Merely *reading* `GLOSSARY.md` for vocabulary is not this skill: that's a one-line habit any skill can do. This skill is for when you're changing the model, not just consuming it.)
|
|
10
|
-
|
|
11
|
-
## File structure
|
|
12
|
-
|
|
13
|
-
Follow the document ownership entry referenced by project instructions for glossary paths and decisions applicable to the model being discussed. If a required input's owner or scope is unclear, report the configuration gap.
|
|
14
|
-
|
|
15
|
-
Resolve the repository management root from project instructions, using the Git top-level only as a fallback. Working inside a subproject does not change it. A root `GLOSSARY-MAP.md` selects domain documents when the repository has multiple contexts. Do not create a separate record system per context or derive record categories automatically from contexts.
|
|
16
|
-
|
|
17
|
-
A single-context project may use a root `GLOSSARY.md`. A root `GLOSSARY-MAP.md`, when present, points to the selected contexts' glossaries; context paths need not be under `src/`. Resolve decision paths separately from the document ownership entry and, for Agent Notes, its configured record roots. Create a glossary or decision record only when there is content for its selected owner. Use the `adr` skill when a separate architecture decision record is warranted.
|
|
18
|
-
|
|
19
|
-
## During the session
|
|
20
|
-
|
|
21
|
-
### Challenge against the glossary
|
|
22
|
-
|
|
23
|
-
When the user uses a term that conflicts with the existing language in `GLOSSARY.md`, call it out immediately. "Your glossary defines 'cancellation' as X, but you seem to mean Y. Which is it?"
|
|
24
|
-
|
|
25
|
-
### Sharpen fuzzy language
|
|
26
|
-
|
|
27
|
-
When the user uses vague or overloaded terms, propose a precise canonical term. "You're saying 'account': do you mean the Customer or the User? Those are different things."
|
|
28
|
-
|
|
29
|
-
### Discuss concrete scenarios
|
|
30
|
-
|
|
31
|
-
When domain relationships are being discussed, stress-test them with specific scenarios. Invent scenarios that probe edge cases and force the user to be precise about the boundaries between concepts.
|
|
32
|
-
|
|
33
|
-
### Cross-reference with code
|
|
34
|
-
|
|
35
|
-
When the user states how something works, check whether the code agrees. If you find a contradiction, surface it: "Your code cancels entire Orders, but you just said partial cancellation is possible. Which is right?"
|
|
36
|
-
|
|
37
|
-
### Update GLOSSARY.md inline
|
|
38
|
-
|
|
39
|
-
When a term is resolved, update `GLOSSARY.md` right there. Don't batch these up: capture them as they happen. Use the format in [GLOSSARY-FORMAT.md](./GLOSSARY-FORMAT.md).
|
|
40
|
-
|
|
41
|
-
`GLOSSARY.md` should be totally devoid of implementation details. Do not treat `GLOSSARY.md` as a spec, a scratch pad, or a repository for implementation decisions. It is a glossary and nothing else.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|