archgen-mcp 0.0.6 → 0.0.7
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 +14 -5
- package/assets/harness-mcp.json +285 -55
- package/assets/references/artifact-templates.md +20 -6
- package/assets/references/platforms.md +7 -5
- package/bin/archgen-mcp.mjs +6 -4
- package/package.json +1 -1
- package/src/context.mjs +43 -3
- package/src/core/briefs.mjs +13 -0
- package/src/core/codegraph-manager.mjs +112 -48
- package/src/core/compat.mjs +13 -3
- package/src/core/doc-navigator.mjs +26 -7
- package/src/core/fs-safety.mjs +98 -10
- package/src/core/graph.mjs +8 -3
- package/src/core/harness-wiring.mjs +132 -18
- package/src/core/mcp-server-manager.mjs +2 -3
- package/src/core/plan-coverage.mjs +43 -6
- package/src/core/plan-grapher.mjs +3 -3
- package/src/core/platform-caps.mjs +9 -2
- package/src/core/resume-snapshot.mjs +16 -4
- package/src/core/scope.mjs +39 -5
- package/src/core/status-mutator.mjs +34 -3
- package/src/core/ui-channel.mjs +55 -4
- package/src/core/validator.mjs +16 -3
- package/src/core/verifier.mjs +3 -9
- package/src/core/worker-contract.mjs +16 -2
- package/src/core/yaml.mjs +31 -6
- package/src/tools/_register.mjs +5 -4
- package/src/tools/codegraph.mjs +0 -2
- package/src/tools/docs.mjs +6 -6
- package/src/tools/help.mjs +1 -1
- package/src/tools/plan.mjs +44 -7
- package/src/tools/setup.mjs +7 -3
- package/src/tools/tasks.mjs +38 -5
package/README.md
CHANGED
|
@@ -42,7 +42,7 @@ Requires **Node.js >= 20** (the official MCP SDK v2 floor).
|
|
|
42
42
|
| `archgen_tasks` | next wave · set status · list · resume snapshot · **contract** (the assembled worker prompt) |
|
|
43
43
|
| `archgen_docs` | navigate markdown artifacts: section index, one section, token map, reference integrity |
|
|
44
44
|
| `archgen_registry` | refresh / read the AGENTS.md features registry |
|
|
45
|
-
| `archgen_codegraph` | install
|
|
45
|
+
| `archgen_codegraph` | install CodeGraph if it is missing · wire its MCP · index the project |
|
|
46
46
|
| `archgen_setup` | doctor · bootstrap servers · install/remove the MCP · list harnesses · **harness_info** (a platform's capabilities) |
|
|
47
47
|
| `archgen_ui` | drive the VS Code extension in real time |
|
|
48
48
|
| `archgen_skills` | match / install / load external capability skills |
|
|
@@ -90,7 +90,7 @@ The skill declares the server version it needs:
|
|
|
90
90
|
```yaml
|
|
91
91
|
metadata:
|
|
92
92
|
requires:
|
|
93
|
-
archgen_mcp: ">=
|
|
93
|
+
archgen_mcp: ">=x.y.z"
|
|
94
94
|
```
|
|
95
95
|
|
|
96
96
|
`archgen_setup { action: 'doctor' }` reports a mismatch, `bootstrap` warns
|
|
@@ -112,14 +112,23 @@ implementation of any capability, only a second way to reach it.
|
|
|
112
112
|
|
|
113
113
|
## Safety
|
|
114
114
|
|
|
115
|
+
- The server owns a **boundary**: the directory it was started in. A tool's
|
|
116
|
+
`projectRoot` argument selects a directory *inside* that boundary — it
|
|
117
|
+
cannot redefine it, so a model cannot move the edge by naming a different
|
|
118
|
+
root.
|
|
115
119
|
- Every path argument is resolved against the project root and refused if it
|
|
116
|
-
escapes; symlinked targets are never written through
|
|
120
|
+
escapes; symlinked targets are never written through, and a symlinked
|
|
121
|
+
*parent* is caught by comparing real paths.
|
|
122
|
+
- The artifact space is anchored to the project: a `.archgen` directory
|
|
123
|
+
elsewhere on the machine is not this project's artifacts.
|
|
117
124
|
- MCP-config writes are approval-gated (`approve: true` / `--yes`); without
|
|
118
125
|
approval the tool reports its **plan** and writes nothing.
|
|
119
126
|
- A config file that cannot be parsed is reported, never rewritten.
|
|
120
127
|
- Writes are atomic (temp file + rename): readers see old-or-new, never a
|
|
121
|
-
partial file.
|
|
122
|
-
|
|
128
|
+
partial file. A read-modify-write — a status update, a config merge — runs
|
|
129
|
+
under an exclusive lock, so two workers cannot lose each other's change.
|
|
130
|
+
- UI commands are allowlisted, workspace-scoped, expiring, and idempotent;
|
|
131
|
+
the command queue is pruned, so it cannot grow without bound.
|
|
123
132
|
|
|
124
133
|
## License
|
|
125
134
|
|
package/assets/harness-mcp.json
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"mcp (opencode), context_servers (zed). Codex CLI uses TOML ([mcp_servers.<id>]).",
|
|
16
16
|
"",
|
|
17
17
|
"COMMAND SHAPE: the server entries below are the ones the official installers",
|
|
18
|
-
"write
|
|
18
|
+
"write \u2014 keep them byte-compatible, or the MCP server never connects.",
|
|
19
19
|
"The CodeGraph server also runs a FILE WATCHER, so the index stays live after",
|
|
20
20
|
"the one-time init; nothing is re-indexed by hand.",
|
|
21
21
|
"",
|
|
@@ -25,135 +25,365 @@
|
|
|
25
25
|
],
|
|
26
26
|
"package": "@colbymchenry/codegraph",
|
|
27
27
|
"entries": {
|
|
28
|
-
"stdio": {
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
"stdio": {
|
|
29
|
+
"type": "stdio",
|
|
30
|
+
"command": "codegraph",
|
|
31
|
+
"args": [
|
|
32
|
+
"serve",
|
|
33
|
+
"--mcp"
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
"stdioNoType": {
|
|
37
|
+
"command": "codegraph",
|
|
38
|
+
"args": [
|
|
39
|
+
"serve",
|
|
40
|
+
"--mcp"
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
"opencodeLocal": {
|
|
44
|
+
"type": "local",
|
|
45
|
+
"command": [
|
|
46
|
+
"codegraph",
|
|
47
|
+
"serve",
|
|
48
|
+
"--mcp"
|
|
49
|
+
],
|
|
50
|
+
"enabled": true
|
|
51
|
+
}
|
|
31
52
|
},
|
|
32
53
|
"npxEntries": {
|
|
33
|
-
"stdio": {
|
|
34
|
-
|
|
35
|
-
|
|
54
|
+
"stdio": {
|
|
55
|
+
"type": "stdio",
|
|
56
|
+
"command": "npx",
|
|
57
|
+
"args": [
|
|
58
|
+
"-y",
|
|
59
|
+
"@colbymchenry/codegraph",
|
|
60
|
+
"serve",
|
|
61
|
+
"--mcp"
|
|
62
|
+
]
|
|
63
|
+
},
|
|
64
|
+
"stdioNoType": {
|
|
65
|
+
"command": "npx",
|
|
66
|
+
"args": [
|
|
67
|
+
"-y",
|
|
68
|
+
"@colbymchenry/codegraph",
|
|
69
|
+
"serve",
|
|
70
|
+
"--mcp"
|
|
71
|
+
]
|
|
72
|
+
},
|
|
73
|
+
"opencodeLocal": {
|
|
74
|
+
"type": "local",
|
|
75
|
+
"command": [
|
|
76
|
+
"npx",
|
|
77
|
+
"-y",
|
|
78
|
+
"@colbymchenry/codegraph",
|
|
79
|
+
"serve",
|
|
80
|
+
"--mcp"
|
|
81
|
+
],
|
|
82
|
+
"enabled": true
|
|
83
|
+
}
|
|
36
84
|
},
|
|
37
85
|
"archgenPackage": "archgen-mcp",
|
|
38
86
|
"archgenEntries": {
|
|
39
|
-
"stdio": {
|
|
40
|
-
|
|
41
|
-
|
|
87
|
+
"stdio": {
|
|
88
|
+
"type": "stdio",
|
|
89
|
+
"command": "archgen-mcp",
|
|
90
|
+
"args": []
|
|
91
|
+
},
|
|
92
|
+
"stdioNoType": {
|
|
93
|
+
"command": "archgen-mcp",
|
|
94
|
+
"args": []
|
|
95
|
+
},
|
|
96
|
+
"opencodeLocal": {
|
|
97
|
+
"type": "local",
|
|
98
|
+
"command": [
|
|
99
|
+
"archgen-mcp"
|
|
100
|
+
],
|
|
101
|
+
"enabled": true
|
|
102
|
+
}
|
|
42
103
|
},
|
|
43
104
|
"archgenNpxEntries": {
|
|
44
|
-
"stdio": {
|
|
45
|
-
|
|
46
|
-
|
|
105
|
+
"stdio": {
|
|
106
|
+
"type": "stdio",
|
|
107
|
+
"command": "npx",
|
|
108
|
+
"args": [
|
|
109
|
+
"-y",
|
|
110
|
+
"archgen-mcp"
|
|
111
|
+
]
|
|
112
|
+
},
|
|
113
|
+
"stdioNoType": {
|
|
114
|
+
"command": "npx",
|
|
115
|
+
"args": [
|
|
116
|
+
"-y",
|
|
117
|
+
"archgen-mcp"
|
|
118
|
+
]
|
|
119
|
+
},
|
|
120
|
+
"opencodeLocal": {
|
|
121
|
+
"type": "local",
|
|
122
|
+
"command": [
|
|
123
|
+
"npx",
|
|
124
|
+
"-y",
|
|
125
|
+
"archgen-mcp"
|
|
126
|
+
],
|
|
127
|
+
"enabled": true
|
|
128
|
+
}
|
|
47
129
|
},
|
|
48
130
|
"harnesses": {
|
|
49
131
|
"claude": {
|
|
50
132
|
"label": "Claude Code",
|
|
51
133
|
"codegraphTarget": "claude",
|
|
52
|
-
"detectHome": [
|
|
53
|
-
|
|
134
|
+
"detectHome": [
|
|
135
|
+
".claude",
|
|
136
|
+
".claude.json"
|
|
137
|
+
],
|
|
138
|
+
"detectProject": [
|
|
139
|
+
".claude",
|
|
140
|
+
".mcp.json"
|
|
141
|
+
],
|
|
54
142
|
"configs": {
|
|
55
|
-
"global": {
|
|
56
|
-
|
|
143
|
+
"global": {
|
|
144
|
+
"path": "~/.claude.json",
|
|
145
|
+
"format": "json",
|
|
146
|
+
"key": "mcpServers",
|
|
147
|
+
"entry": "stdio"
|
|
148
|
+
},
|
|
149
|
+
"project": {
|
|
150
|
+
"path": ".mcp.json",
|
|
151
|
+
"format": "json",
|
|
152
|
+
"key": "mcpServers",
|
|
153
|
+
"entry": "stdio"
|
|
154
|
+
}
|
|
57
155
|
}
|
|
58
156
|
},
|
|
59
157
|
"cursor": {
|
|
60
158
|
"label": "Cursor",
|
|
61
159
|
"codegraphTarget": "cursor",
|
|
62
|
-
"detectHome": [
|
|
63
|
-
|
|
160
|
+
"detectHome": [
|
|
161
|
+
".cursor"
|
|
162
|
+
],
|
|
163
|
+
"detectProject": [
|
|
164
|
+
".cursor"
|
|
165
|
+
],
|
|
64
166
|
"configs": {
|
|
65
|
-
"global": {
|
|
66
|
-
|
|
167
|
+
"global": {
|
|
168
|
+
"path": "~/.cursor/mcp.json",
|
|
169
|
+
"format": "json",
|
|
170
|
+
"key": "mcpServers",
|
|
171
|
+
"entry": "stdio"
|
|
172
|
+
},
|
|
173
|
+
"project": {
|
|
174
|
+
"path": ".cursor/mcp.json",
|
|
175
|
+
"format": "json",
|
|
176
|
+
"key": "mcpServers",
|
|
177
|
+
"entry": "stdio"
|
|
178
|
+
}
|
|
67
179
|
}
|
|
68
180
|
},
|
|
69
181
|
"opencode": {
|
|
70
182
|
"label": "OpenCode",
|
|
71
183
|
"codegraphTarget": "opencode",
|
|
72
|
-
"detectHome": [
|
|
73
|
-
|
|
74
|
-
|
|
184
|
+
"detectHome": [
|
|
185
|
+
".config/opencode"
|
|
186
|
+
],
|
|
187
|
+
"detectProject": [
|
|
188
|
+
".opencode",
|
|
189
|
+
"opencode.json",
|
|
190
|
+
"opencode.jsonc"
|
|
191
|
+
],
|
|
192
|
+
"_note": "OpenCode's global config is JSONC (opencode.jsonc) \u2014 codegraph's own installer writes it correctly; our writer refuses non-JSON files on purpose, so this entry is a fallback only.",
|
|
75
193
|
"configs": {
|
|
76
|
-
"global": {
|
|
77
|
-
|
|
194
|
+
"global": {
|
|
195
|
+
"path": "~/.config/opencode/opencode.json",
|
|
196
|
+
"format": "json",
|
|
197
|
+
"key": "mcp",
|
|
198
|
+
"entry": "opencodeLocal"
|
|
199
|
+
},
|
|
200
|
+
"project": {
|
|
201
|
+
"path": "opencode.json",
|
|
202
|
+
"format": "json",
|
|
203
|
+
"key": "mcp",
|
|
204
|
+
"entry": "opencodeLocal"
|
|
205
|
+
}
|
|
78
206
|
}
|
|
79
207
|
},
|
|
80
208
|
"codex": {
|
|
81
209
|
"label": "Codex CLI",
|
|
82
210
|
"codegraphTarget": "codex",
|
|
83
|
-
"detectHome": [
|
|
84
|
-
|
|
211
|
+
"detectHome": [
|
|
212
|
+
".codex"
|
|
213
|
+
],
|
|
214
|
+
"detectProject": [
|
|
215
|
+
".codex"
|
|
216
|
+
],
|
|
85
217
|
"configs": {
|
|
86
|
-
"global": {
|
|
87
|
-
|
|
218
|
+
"global": {
|
|
219
|
+
"path": "~/.codex/config.toml",
|
|
220
|
+
"format": "toml",
|
|
221
|
+
"entry": "stdioNoType"
|
|
222
|
+
},
|
|
223
|
+
"project": {
|
|
224
|
+
"path": ".codex/config.toml",
|
|
225
|
+
"format": "toml",
|
|
226
|
+
"entry": "stdioNoType"
|
|
227
|
+
}
|
|
88
228
|
}
|
|
89
229
|
},
|
|
90
230
|
"gemini": {
|
|
91
231
|
"label": "Gemini CLI",
|
|
92
232
|
"codegraphTarget": "gemini",
|
|
93
|
-
"detectHome": [
|
|
94
|
-
|
|
233
|
+
"detectHome": [
|
|
234
|
+
".gemini"
|
|
235
|
+
],
|
|
236
|
+
"detectProject": [
|
|
237
|
+
".gemini"
|
|
238
|
+
],
|
|
95
239
|
"configs": {
|
|
96
|
-
"global": {
|
|
97
|
-
|
|
240
|
+
"global": {
|
|
241
|
+
"path": "~/.gemini/settings.json",
|
|
242
|
+
"format": "json",
|
|
243
|
+
"key": "mcpServers",
|
|
244
|
+
"entry": "stdio"
|
|
245
|
+
},
|
|
246
|
+
"project": {
|
|
247
|
+
"path": ".gemini/settings.json",
|
|
248
|
+
"format": "json",
|
|
249
|
+
"key": "mcpServers",
|
|
250
|
+
"entry": "stdio"
|
|
251
|
+
}
|
|
98
252
|
}
|
|
99
253
|
},
|
|
100
254
|
"antigravity": {
|
|
101
255
|
"label": "Antigravity",
|
|
102
256
|
"codegraphTarget": "antigravity",
|
|
103
|
-
"detectHome": [
|
|
104
|
-
|
|
257
|
+
"detectHome": [
|
|
258
|
+
".gemini/config"
|
|
259
|
+
],
|
|
260
|
+
"detectProject": [
|
|
261
|
+
".agents/mcp_config.json"
|
|
262
|
+
],
|
|
105
263
|
"configs": {
|
|
106
|
-
"global": {
|
|
107
|
-
|
|
264
|
+
"global": {
|
|
265
|
+
"path": "~/.gemini/config/mcp_config.json",
|
|
266
|
+
"format": "json",
|
|
267
|
+
"key": "mcpServers",
|
|
268
|
+
"entry": "stdio"
|
|
269
|
+
},
|
|
270
|
+
"project": {
|
|
271
|
+
"path": ".agents/mcp_config.json",
|
|
272
|
+
"format": "json",
|
|
273
|
+
"key": "mcpServers",
|
|
274
|
+
"entry": "stdio"
|
|
275
|
+
}
|
|
108
276
|
}
|
|
109
277
|
},
|
|
110
278
|
"kiro": {
|
|
111
279
|
"label": "Kiro",
|
|
112
280
|
"codegraphTarget": "kiro",
|
|
113
|
-
"detectHome": [
|
|
114
|
-
|
|
281
|
+
"detectHome": [
|
|
282
|
+
".kiro"
|
|
283
|
+
],
|
|
284
|
+
"detectProject": [
|
|
285
|
+
".kiro"
|
|
286
|
+
],
|
|
115
287
|
"configs": {
|
|
116
|
-
"global": {
|
|
117
|
-
|
|
288
|
+
"global": {
|
|
289
|
+
"path": "~/.kiro/settings/mcp.json",
|
|
290
|
+
"format": "json",
|
|
291
|
+
"key": "mcpServers",
|
|
292
|
+
"entry": "stdioNoType"
|
|
293
|
+
},
|
|
294
|
+
"project": {
|
|
295
|
+
"path": ".kiro/settings/mcp.json",
|
|
296
|
+
"format": "json",
|
|
297
|
+
"key": "mcpServers",
|
|
298
|
+
"entry": "stdioNoType"
|
|
299
|
+
}
|
|
118
300
|
}
|
|
119
301
|
},
|
|
120
302
|
"windsurf": {
|
|
121
303
|
"label": "Windsurf",
|
|
122
304
|
"codegraphTarget": null,
|
|
123
|
-
"detectHome": [
|
|
124
|
-
|
|
305
|
+
"detectHome": [
|
|
306
|
+
".codeium/windsurf"
|
|
307
|
+
],
|
|
308
|
+
"detectProject": [
|
|
309
|
+
".windsurf"
|
|
310
|
+
],
|
|
125
311
|
"configs": {
|
|
126
|
-
"global": {
|
|
312
|
+
"global": {
|
|
313
|
+
"path": "~/.codeium/windsurf/mcp_config.json",
|
|
314
|
+
"format": "json",
|
|
315
|
+
"key": "mcpServers",
|
|
316
|
+
"entry": "stdioNoType"
|
|
317
|
+
}
|
|
127
318
|
}
|
|
128
319
|
},
|
|
129
320
|
"vscode": {
|
|
130
321
|
"label": "VS Code / Copilot",
|
|
131
322
|
"codegraphTarget": null,
|
|
132
323
|
"detectHome": [],
|
|
133
|
-
"detectProject": [
|
|
324
|
+
"detectProject": [
|
|
325
|
+
".vscode",
|
|
326
|
+
".github/copilot-instructions.md"
|
|
327
|
+
],
|
|
134
328
|
"configs": {
|
|
135
|
-
"project": {
|
|
329
|
+
"project": {
|
|
330
|
+
"path": ".vscode/mcp.json",
|
|
331
|
+
"format": "json",
|
|
332
|
+
"key": "servers",
|
|
333
|
+
"entry": "stdio"
|
|
334
|
+
}
|
|
136
335
|
}
|
|
137
336
|
},
|
|
138
337
|
"zed": {
|
|
139
338
|
"label": "Zed",
|
|
140
339
|
"codegraphTarget": null,
|
|
141
|
-
"detectHome": [
|
|
142
|
-
|
|
340
|
+
"detectHome": [
|
|
341
|
+
".config/zed"
|
|
342
|
+
],
|
|
343
|
+
"detectProject": [
|
|
344
|
+
".zed"
|
|
345
|
+
],
|
|
143
346
|
"configs": {
|
|
144
|
-
"global": {
|
|
145
|
-
|
|
347
|
+
"global": {
|
|
348
|
+
"path": "~/.config/zed/settings.json",
|
|
349
|
+
"format": "json",
|
|
350
|
+
"key": "context_servers",
|
|
351
|
+
"entry": "stdioNoType"
|
|
352
|
+
},
|
|
353
|
+
"project": {
|
|
354
|
+
"path": ".zed/settings.json",
|
|
355
|
+
"format": "json",
|
|
356
|
+
"key": "context_servers",
|
|
357
|
+
"entry": "stdioNoType"
|
|
358
|
+
}
|
|
146
359
|
}
|
|
147
360
|
},
|
|
148
361
|
"trae": {
|
|
149
362
|
"label": "Trae",
|
|
150
363
|
"codegraphTarget": null,
|
|
151
|
-
"detectHome": [
|
|
152
|
-
|
|
364
|
+
"detectHome": [
|
|
365
|
+
".trae"
|
|
366
|
+
],
|
|
367
|
+
"detectProject": [
|
|
368
|
+
".trae"
|
|
369
|
+
],
|
|
153
370
|
"configs": {
|
|
154
|
-
"project": {
|
|
371
|
+
"project": {
|
|
372
|
+
"path": ".trae/mcp.json",
|
|
373
|
+
"format": "json",
|
|
374
|
+
"key": "mcpServers",
|
|
375
|
+
"entry": "stdioNoType"
|
|
376
|
+
}
|
|
155
377
|
}
|
|
156
378
|
}
|
|
157
379
|
},
|
|
158
|
-
"codegraphNativeTargets": [
|
|
380
|
+
"codegraphNativeTargets": [
|
|
381
|
+
"claude",
|
|
382
|
+
"cursor",
|
|
383
|
+
"opencode",
|
|
384
|
+
"codex",
|
|
385
|
+
"gemini",
|
|
386
|
+
"antigravity",
|
|
387
|
+
"kiro"
|
|
388
|
+
]
|
|
159
389
|
}
|
|
@@ -6,9 +6,17 @@ Begin both files with three YAML comment lines. Comments are ignored by every
|
|
|
6
6
|
consumer (the MCP server, the VS Code extension, any YAML toolchain), so this is
|
|
7
7
|
purely additive metadata — it can never break a parser:
|
|
8
8
|
|
|
9
|
+
**NEVER type the version yourself.** The `scaffold` action writes the generator
|
|
10
|
+
line with the RUNNING server's version, and you paste its output verbatim. A
|
|
11
|
+
version you write by hand is either stale or made up — and the provenance
|
|
12
|
+
header exists precisely so it is never either.
|
|
13
|
+
|
|
14
|
+
The shape of what `scaffold` returns (the version below is a sample of the
|
|
15
|
+
running server's, not a value to copy):
|
|
16
|
+
|
|
9
17
|
```yaml
|
|
10
18
|
# schema_version: 1
|
|
11
|
-
# generator: archgen
|
|
19
|
+
# generator: archgen v1.2.3
|
|
12
20
|
# generated_at: 2026-08-26T09:00:00.000Z
|
|
13
21
|
```
|
|
14
22
|
|
|
@@ -16,11 +24,17 @@ The scaffold action returns these three lines ready to paste.
|
|
|
16
24
|
|
|
17
25
|
Version token rules — code-defensive, NEVER guess:
|
|
18
26
|
|
|
19
|
-
1.
|
|
20
|
-
|
|
21
|
-
2.
|
|
22
|
-
|
|
23
|
-
|
|
27
|
+
1. The `scaffold` action already resolved the version for you. Copy its
|
|
28
|
+
header as-is.
|
|
29
|
+
2. If you must write the header yourself (backfilling an artifact the tool
|
|
30
|
+
did not create), resolve the version in this order and never invent one:
|
|
31
|
+
a. The `.archgen-version` stamp next to the installed `SKILL.md`, when it
|
|
32
|
+
reads as exact `MAJOR.MINOR.PATCH`.
|
|
33
|
+
b. Otherwise the `"version"` field of an adjacent `archgen.config.json`,
|
|
34
|
+
when it is semver-shaped.
|
|
35
|
+
c. Otherwise omit the version token entirely: plain `# generator: archgen`.
|
|
36
|
+
3. `npx archgen-skill migrate --apply` applies rules 2(a)–(c) to existing
|
|
37
|
+
artifacts for you, and backs each file up before it touches it.
|
|
24
38
|
|
|
25
39
|
`generated_at` is one ISO-8601 timestamp per generation run (all files written
|
|
26
40
|
by the same run share it). Keep the header at the very top of the file; never
|
|
@@ -111,12 +111,14 @@ release and refreshes it in place. The MCP server exposes its own tool set to
|
|
|
111
111
|
the agent, so the skill does not restate the CLI's command surface — read it
|
|
112
112
|
from the installed CLI (`codegraph help`) or the MCP tools themselves.
|
|
113
113
|
|
|
114
|
-
> **Entry point — the `archgen_codegraph` tool.** It installs
|
|
115
|
-
>
|
|
116
|
-
>
|
|
117
|
-
>
|
|
114
|
+
> **Entry point — the `archgen_codegraph` tool.** It installs the CLI **if it
|
|
115
|
+
> is missing** (an installed CodeGraph is the user's to manage and is never
|
|
116
|
+
> upgraded behind their back), wires every DETECTED harness with the correct
|
|
117
|
+
> per-platform shape, and builds the project index — idempotently, with an
|
|
118
|
+
> approval gate before any config write. It drives the official CLI; it never
|
|
119
|
+
> reimplements it.
|
|
118
120
|
> ```
|
|
119
|
-
> archgen_codegraph { action: 'ensure' } # install + wire + index
|
|
121
|
+
> archgen_codegraph { action: 'ensure' } # install if missing + wire + index
|
|
120
122
|
> archgen_codegraph { action: 'status' } # what is installed/wired/indexed
|
|
121
123
|
> ```
|
|
122
124
|
|
package/bin/archgen-mcp.mjs
CHANGED
|
@@ -16,11 +16,13 @@ import { createServer } from '../src/server.mjs';
|
|
|
16
16
|
import { runCli } from '../src/cli.mjs';
|
|
17
17
|
|
|
18
18
|
const argv = process.argv.slice(2);
|
|
19
|
-
const CLI_COMMANDS = new Set(['exec', 'bootstrap', 'doctor', 'uninstall-mcp', 'help']);
|
|
20
19
|
const first = argv[0];
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
|
|
20
|
+
// The stdio server is the DEFAULT, and only for no argument at all or the
|
|
21
|
+
// explicit word 'serve'. Anything else is the command-line facade — a known
|
|
22
|
+
// command, a flag, or an unknown word, which must say "unknown command"
|
|
23
|
+
// rather than silently start a server and block on stdin (which is what it
|
|
24
|
+
// used to do, making the mistake look like a hang).
|
|
25
|
+
const wantsCli = !(first === undefined || first === 'serve');
|
|
24
26
|
|
|
25
27
|
if (wantsCli) {
|
|
26
28
|
const code = await runCli(argv);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "archgen-mcp",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"description": "MCP server for archgen \u2014 the single runtime that owns architecture-plan validation, wave execution, artifact navigation, CodeGraph bootstrap, harness MCP wiring, and live UI control for coding agents.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
package/src/context.mjs
CHANGED
|
@@ -11,7 +11,7 @@ import { homedir } from 'node:os';
|
|
|
11
11
|
import { dirname, isAbsolute, join, resolve } from 'node:path';
|
|
12
12
|
import { fileURLToPath } from 'node:url';
|
|
13
13
|
import { invalidInput } from './core/errors.mjs';
|
|
14
|
-
import { containPath } from './core/fs-safety.mjs';
|
|
14
|
+
import { assertWithinRoot, containPath } from './core/fs-safety.mjs';
|
|
15
15
|
|
|
16
16
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
17
17
|
|
|
@@ -148,19 +148,59 @@ export function loadAsset(name) {
|
|
|
148
148
|
|
|
149
149
|
/**
|
|
150
150
|
* The per-call context: project root + resolved skill + versions.
|
|
151
|
+
*
|
|
152
|
+
* THE ANCHOR. `anchorRoot` is the boundary the server works inside. It is
|
|
153
|
+
* set when the server starts — the directory the server was launched in, or
|
|
154
|
+
* the project the CLI named — and it does NOT change per call.
|
|
155
|
+
*
|
|
156
|
+
* `projectRoot` selects a directory INSIDE the anchor. It is a selector, not
|
|
157
|
+
* a boundary: naming a different root cannot move the edge of what the
|
|
158
|
+
* server may read or write. Before this, every containment check was
|
|
159
|
+
* relative to a root the MODEL chose, so the guard defended nothing — a
|
|
160
|
+
* model that named `/etc` simply moved the boundary to `/etc`.
|
|
151
161
|
*/
|
|
152
162
|
export class Context {
|
|
153
163
|
/**
|
|
154
|
-
* @param {{projectRoot?: string, home?: string, env?: Record<string, string|undefined>}} [opts]
|
|
164
|
+
* @param {{projectRoot?: string, anchorRoot?: string, home?: string, env?: Record<string, string|undefined>}} [opts]
|
|
155
165
|
*/
|
|
156
166
|
constructor(opts = {}) {
|
|
157
167
|
this.env = opts.env ?? process.env;
|
|
158
168
|
this.home = opts.home ?? homedir();
|
|
159
|
-
|
|
169
|
+
// The anchor is the server's DECLARED root: what the server was started
|
|
170
|
+
// with. The cwd is only the fallback for a server started without one.
|
|
171
|
+
this.anchorRoot = resolve(
|
|
172
|
+
opts.anchorRoot ?? opts.projectRoot ?? this.env.ARCHGEN_PROJECT_ROOT ?? process.cwd(),
|
|
173
|
+
);
|
|
174
|
+
this.projectRoot = resolve(opts.projectRoot ?? this.anchorRoot);
|
|
160
175
|
this.skill = resolveSkill(this.projectRoot, this.home, this.env);
|
|
161
176
|
this.serverVersion = serverVersion();
|
|
162
177
|
}
|
|
163
178
|
|
|
179
|
+
/**
|
|
180
|
+
* A context for a directory inside the anchor.
|
|
181
|
+
*
|
|
182
|
+
* The candidate is resolved and proved to be inside the anchor; a path
|
|
183
|
+
* outside it is refused with PERMISSION_DENIED. The returned context
|
|
184
|
+
* keeps the SAME anchor, so containment checks made with it cannot be
|
|
185
|
+
* widened either.
|
|
186
|
+
*
|
|
187
|
+
* @param {string} candidate
|
|
188
|
+
* @returns {Context}
|
|
189
|
+
*/
|
|
190
|
+
withProjectRoot(candidate) {
|
|
191
|
+
if (typeof candidate !== 'string' || candidate.trim() === '') {
|
|
192
|
+
throw invalidInput('projectRoot must be a non-empty string', { projectRoot: candidate });
|
|
193
|
+
}
|
|
194
|
+
const abs = resolve(this.anchorRoot, candidate);
|
|
195
|
+
assertWithinRoot(this.anchorRoot, abs);
|
|
196
|
+
return new Context({
|
|
197
|
+
projectRoot: abs,
|
|
198
|
+
anchorRoot: this.anchorRoot,
|
|
199
|
+
env: this.env,
|
|
200
|
+
home: this.home,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
164
204
|
/**
|
|
165
205
|
* Resolve a caller-supplied path and prove it stays inside the project root.
|
|
166
206
|
* Relative paths are resolved against the project root.
|
package/src/core/briefs.mjs
CHANGED
|
@@ -36,6 +36,19 @@ export function loadBrief(briefsDir, kind, vars = {}) {
|
|
|
36
36
|
return { kind: key, brief: text };
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* The placeholder names a brief still contains.
|
|
41
|
+
*
|
|
42
|
+
* A dispatched brief must not carry a placeholder: a worker told to read
|
|
43
|
+
* `<slug dir>` looks for a directory with that literal name.
|
|
44
|
+
*
|
|
45
|
+
* @param {string} text
|
|
46
|
+
* @returns {string[]}
|
|
47
|
+
*/
|
|
48
|
+
export function remainingPlaceholders(text) {
|
|
49
|
+
return [...new Set([...String(text ?? '').matchAll(/<([a-z][a-z. ]*)>/g)].map((m) => m[1]))];
|
|
50
|
+
}
|
|
51
|
+
|
|
39
52
|
/**
|
|
40
53
|
* Every brief, for a caller that wants them all.
|
|
41
54
|
* @param {string} briefsDir
|