archgen-skill 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 +8 -1
- package/bin/archgen.mjs +123 -13
- package/lib/block.js +51 -10
- package/lib/bootstrap.js +8 -4
- package/lib/config.js +3 -2
- package/lib/doctor.js +156 -79
- package/lib/init.js +45 -16
- package/lib/install.js +60 -7
- package/lib/mcp-server.js +88 -6
- package/lib/migrations/001-stamp-provenance.mjs +12 -5
- package/lib/migrations/index.mjs +19 -8
- package/lib/restore.js +52 -16
- package/lib/spawn-npm.js +49 -4
- package/lib/store.js +100 -23
- package/lib/uninstall-project.js +22 -4
- package/lib/version.js +9 -1
- package/package.json +1 -1
- package/vendor/skills/archgen/SKILL.md +2 -2
- package/vendor/skills/archgen/archgen.config.json +1 -1
package/README.md
CHANGED
|
@@ -42,6 +42,13 @@ copies are backed up to `.archgen/.backup/<timestamp>/` before refresh, and lega
|
|
|
42
42
|
dual-copy layouts are migrated to symlinks automatically. Verify anytime with
|
|
43
43
|
`archgen-skill doctor`.
|
|
44
44
|
|
|
45
|
+
Every destructive step is behind a rail: the project root is asserted **before**
|
|
46
|
+
anything is written or deleted, a symlinked target is never written through, the
|
|
47
|
+
store swap is atomic (the old store is moved aside, not deleted, and restored if
|
|
48
|
+
the swap fails), and a restore puts back exactly the paths the snapshot recorded
|
|
49
|
+
— never a sibling skill. `archgen-skill uninstall <dir>` removes that project;
|
|
50
|
+
the global uninstall happens only when no project is named.
|
|
51
|
+
|
|
45
52
|
**Or install globally** into every detected harness:
|
|
46
53
|
|
|
47
54
|
```bash
|
|
@@ -80,7 +87,7 @@ by git.
|
|
|
80
87
|
| `npx archgen-skill init [dir]` | Prepare a project: canonical skill store at `.agents/skills/archgen`, Claude symlink adapter, AGENTS.md hub + CLAUDE.md bridge |
|
|
81
88
|
| `npx archgen-skill install [--copy]` | Install into global harness skill dirs (symlinks by default, real copies with `--copy`) |
|
|
82
89
|
| `npx archgen-skill uninstall` | Remove globally-installed copies (manifest-recorded, safe removal) |
|
|
83
|
-
| `npx archgen-skill uninstall
|
|
90
|
+
| `npx archgen-skill uninstall [dir]` | Remove a **project** install (same as `--project [dir]`): strips managed blocks, symlink, and store **only if unmodified** — `.archgen/` feature folders and all user content are preserved |
|
|
84
91
|
| `npx archgen-skill doctor [dir] [--check]` | Verify and auto-repair an installation: store integrity, version stamp, Claude link, managed blocks, manifest (`--check` reports without fixing) |
|
|
85
92
|
| `npx archgen-skill update [dir]` | Check npm for a newer `archgen-skill`, upgrade the global install if outdated, then re-init the project and run doctor against the new version |
|
|
86
93
|
| `npx archgen-skill restore` | List backup snapshots; `--snapshot <ts>` restores one (current state is backed up first) |
|
package/bin/archgen.mjs
CHANGED
|
@@ -22,7 +22,7 @@ import { uninstallProject } from '../lib/uninstall-project.js';
|
|
|
22
22
|
import { restoreMain } from '../lib/restore.js';
|
|
23
23
|
import { migrateMain } from '../lib/migrate.js';
|
|
24
24
|
import { compareSemver, fetchLatestVersion } from '../lib/version.js';
|
|
25
|
-
import { npmInvocation, whichOnPath } from '../lib/spawn-npm.js';
|
|
25
|
+
import { npmInvocation, whichOnPath, globalBinPath } from '../lib/spawn-npm.js';
|
|
26
26
|
import { spawnSync } from 'node:child_process';
|
|
27
27
|
import { readFileSync } from 'node:fs';
|
|
28
28
|
|
|
@@ -35,7 +35,13 @@ function cliVersion() {
|
|
|
35
35
|
function runInitAndDoctor(dir) {
|
|
36
36
|
const r = initProject(dir, PACKAGE_ROOT);
|
|
37
37
|
console.log(' = store refreshed at ' + r.storePath);
|
|
38
|
-
doctorProject(dir, PACKAGE_ROOT, {});
|
|
38
|
+
const d = doctorProject(dir, PACKAGE_ROOT, {});
|
|
39
|
+
// A failed doctor must fail the update too: the project was NOT brought to
|
|
40
|
+
// the new version.
|
|
41
|
+
if (d.failures > 0) {
|
|
42
|
+
console.error(' ! doctor reported ' + d.failures + ' failure(s)');
|
|
43
|
+
process.exitCode = 1;
|
|
44
|
+
}
|
|
39
45
|
}
|
|
40
46
|
|
|
41
47
|
const HELP = `archgen — architecture generation & autonomous task execution for coding agents
|
|
@@ -45,6 +51,8 @@ Usage:
|
|
|
45
51
|
store at .agents/skills/archgen, symlink
|
|
46
52
|
.claude/skills/archgen -> ../../.agents/skills/archgen,
|
|
47
53
|
and generate AGENTS.md / CLAUDE.md pointer blocks.
|
|
54
|
+
[--project <dir>] the project directory (same as the positional
|
|
55
|
+
form; give it once, not both)
|
|
48
56
|
[--force] proceed automatically; divergent copies are
|
|
49
57
|
backed up to .archgen/.backup/ (never destroyed)
|
|
50
58
|
[--no-codegraph] skip the CodeGraph bootstrap
|
|
@@ -52,13 +60,17 @@ Usage:
|
|
|
52
60
|
npx archgen-skill doctor [dir] Health-check a project install: store integrity,
|
|
53
61
|
version stamp, claude link, managed blocks,
|
|
54
62
|
manifest. Repairs safe issues in place.
|
|
63
|
+
[--project <dir>] the project directory (same as the positional form)
|
|
55
64
|
[--check] report only - change nothing
|
|
56
65
|
npx archgen-skill install Install into global harness skill dirs
|
|
57
66
|
[--copy] real copies instead of symlinks
|
|
58
67
|
[--uninstall] remove manifest-recorded entries
|
|
59
68
|
[--no-codegraph] skip the CodeGraph bootstrap
|
|
60
69
|
[--no-mcp] skip wiring the archgen MCP server
|
|
61
|
-
npx archgen-skill uninstall
|
|
70
|
+
npx archgen-skill uninstall [dir] Remove archgen from a project (the same
|
|
71
|
+
as the positional form); without a directory
|
|
72
|
+
the globally-installed copies are removed
|
|
73
|
+
(manifest-based)
|
|
62
74
|
--project [dir] remove archgen from a project: claude link,
|
|
63
75
|
managed blocks, canonical store (kept when
|
|
64
76
|
customized) and manifest; .archgen feature
|
|
@@ -89,8 +101,63 @@ function flag(name) {
|
|
|
89
101
|
return true;
|
|
90
102
|
}
|
|
91
103
|
|
|
92
|
-
|
|
93
|
-
|
|
104
|
+
/**
|
|
105
|
+
* Consume an option that takes a value: `--name <value>` or `--name=<value>`.
|
|
106
|
+
*
|
|
107
|
+
* Returns null when the option is absent, '' when it is present with no
|
|
108
|
+
* value ("use the default"), and the value otherwise.
|
|
109
|
+
*/
|
|
110
|
+
function takeValue(name) {
|
|
111
|
+
const eq = rest.findIndex((a) => a.startsWith(name + '='));
|
|
112
|
+
if (eq !== -1) {
|
|
113
|
+
const v = rest[eq].slice(name.length + 1);
|
|
114
|
+
rest.splice(eq, 1);
|
|
115
|
+
return v;
|
|
116
|
+
}
|
|
117
|
+
const i = rest.indexOf(name);
|
|
118
|
+
if (i === -1) return null;
|
|
119
|
+
rest.splice(i, 1);
|
|
120
|
+
const v = rest[i];
|
|
121
|
+
if (v === undefined || v.startsWith('-')) return '';
|
|
122
|
+
rest.splice(i, 1);
|
|
123
|
+
return v;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* The project directory for a command.
|
|
128
|
+
*
|
|
129
|
+
* Accepts a POSITIONAL dir (`init myproj`) and `--project <dir>` alike. The
|
|
130
|
+
* same flag name is used by `uninstall` and `restore`, and the MCP server's
|
|
131
|
+
* own commands take `--project` too — so a user who typed one form must
|
|
132
|
+
* never get a directory literally named "--project" created in their cwd.
|
|
133
|
+
*
|
|
134
|
+
* Extra positional arguments are REFUSED: silently ignoring the second one
|
|
135
|
+
* would install into a directory the user did not name.
|
|
136
|
+
*/
|
|
137
|
+
function targetDir() {
|
|
138
|
+
const explicit = takeValue('--project');
|
|
139
|
+
const positionals = rest.filter((a) => !a.startsWith('-'));
|
|
140
|
+
if (positionals.length > 1) {
|
|
141
|
+
throw new Error(`unexpected argument: ${positionals[1]} — give the project directory once`);
|
|
142
|
+
}
|
|
143
|
+
const positional = positionals[0];
|
|
144
|
+
const explicitDir = explicit === null ? null : (explicit === '' ? process.cwd() : resolve(explicit));
|
|
145
|
+
if (explicitDir !== null && positional !== undefined && explicitDir !== resolve(positional)) {
|
|
146
|
+
throw new Error('give the project directory once: either positionally or with --project');
|
|
147
|
+
}
|
|
148
|
+
return explicitDir ?? (positional !== undefined ? resolve(positional) : process.cwd());
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Refuse an option we do not understand.
|
|
153
|
+
*
|
|
154
|
+
* An unknown flag is a TYPO. It is never a directory name — creating a
|
|
155
|
+
* directory called "--no-mcp" because the flag was misspelled is exactly the
|
|
156
|
+
* kind of silent mess this check exists to prevent.
|
|
157
|
+
*/
|
|
158
|
+
function assertNoUnknownFlags() {
|
|
159
|
+
const unknown = rest.find((a) => a.startsWith('-'));
|
|
160
|
+
if (unknown) throw new Error(`unknown option: ${unknown}`);
|
|
94
161
|
}
|
|
95
162
|
|
|
96
163
|
try {
|
|
@@ -99,7 +166,9 @@ try {
|
|
|
99
166
|
flag('--force');
|
|
100
167
|
const noCodegraph = flag('--no-codegraph');
|
|
101
168
|
const noMcp = flag('--no-mcp');
|
|
102
|
-
const
|
|
169
|
+
const dir = targetDir();
|
|
170
|
+
assertNoUnknownFlags();
|
|
171
|
+
const r = initProject(dir, PACKAGE_ROOT, { codegraph: !noCodegraph, mcp: !noMcp });
|
|
103
172
|
console.log('archgen: project ready (single-store layout)');
|
|
104
173
|
console.log(' + store ' + r.storePath);
|
|
105
174
|
if (r.claudeLink === 'created') console.log(' + link .claude/skills/archgen -> ../../.agents/skills/archgen');
|
|
@@ -117,7 +186,9 @@ try {
|
|
|
117
186
|
}
|
|
118
187
|
case 'doctor': {
|
|
119
188
|
const check = flag('--check');
|
|
120
|
-
const
|
|
189
|
+
const dir = targetDir();
|
|
190
|
+
assertNoUnknownFlags();
|
|
191
|
+
const r = doctorProject(dir, PACKAGE_ROOT, { check });
|
|
121
192
|
console.log('archgen doctor — ' + r.root + (check ? ' (check mode: no changes made)' : ''));
|
|
122
193
|
for (const c of r.checks) console.log(` [${c.status.toLowerCase().padEnd(9)}] ${c.msg}`);
|
|
123
194
|
const t = r.tally;
|
|
@@ -129,7 +200,11 @@ try {
|
|
|
129
200
|
const copy = flag('--copy');
|
|
130
201
|
const noCodegraph = flag('--no-codegraph');
|
|
131
202
|
const noMcp = flag('--no-mcp');
|
|
132
|
-
|
|
203
|
+
const uninstallMode = flag('--uninstall');
|
|
204
|
+
assertNoUnknownFlags();
|
|
205
|
+
const stray = rest.find((a) => !a.startsWith('-'));
|
|
206
|
+
if (stray) throw new Error(`unexpected argument: ${stray} — install takes no directory`);
|
|
207
|
+
if (uninstallMode) { uninstall(); break; }
|
|
133
208
|
const r = installGlobal({ copy, codegraph: !noCodegraph, mcp: !noMcp });
|
|
134
209
|
console.log('archgen: global harness install (mode: ' + (copy ? 'copy' : 'symlink') + ')');
|
|
135
210
|
for (const [status, mode, path] of r.rows) console.log(` ${status.padEnd(7)} ${mode.padEnd(5)} ${path}`);
|
|
@@ -138,8 +213,25 @@ try {
|
|
|
138
213
|
break;
|
|
139
214
|
}
|
|
140
215
|
case 'uninstall': {
|
|
141
|
-
|
|
142
|
-
|
|
216
|
+
// `--project` may carry a directory (`uninstall --project myproj`) or
|
|
217
|
+
// stand alone (`uninstall --project` → this directory). A POSITIONAL
|
|
218
|
+
// directory (`uninstall myproj`) is a project uninstall as well: the
|
|
219
|
+
// GLOBAL uninstall happens only when no project was named at all.
|
|
220
|
+
const explicit = takeValue('--project');
|
|
221
|
+
const positionals = rest.filter((a) => !a.startsWith('-'));
|
|
222
|
+
assertNoUnknownFlags();
|
|
223
|
+
if (positionals.length > 1) {
|
|
224
|
+
throw new Error(`unexpected argument: ${positionals[1]} — give the project directory once`);
|
|
225
|
+
}
|
|
226
|
+
const positional = positionals[0];
|
|
227
|
+
const projectTarget = explicit !== null
|
|
228
|
+
? (explicit === '' ? process.cwd() : resolve(explicit))
|
|
229
|
+
: (positional !== undefined ? resolve(positional) : null);
|
|
230
|
+
if (explicit !== null && positional !== undefined && projectTarget !== resolve(positional)) {
|
|
231
|
+
throw new Error('give the project directory once: either positionally or with --project');
|
|
232
|
+
}
|
|
233
|
+
if (projectTarget !== null) {
|
|
234
|
+
const r = uninstallProject(projectTarget, PACKAGE_ROOT);
|
|
143
235
|
console.log('archgen: project uninstall — ' + r.root);
|
|
144
236
|
if (r.linkRemoved) console.log(' - .claude/skills/archgen (link)');
|
|
145
237
|
if (r.strippedBlocks.length) console.log(' - managed blocks stripped from ' + r.strippedBlocks.join(', '));
|
|
@@ -166,7 +258,8 @@ try {
|
|
|
166
258
|
break;
|
|
167
259
|
}
|
|
168
260
|
case 'update': {
|
|
169
|
-
const dir =
|
|
261
|
+
const dir = targetDir();
|
|
262
|
+
assertNoUnknownFlags();
|
|
170
263
|
const current = cliVersion();
|
|
171
264
|
console.log('archgen update — running v' + current + ', checking npm registry…');
|
|
172
265
|
const latest = fetchLatestVersion('archgen-skill');
|
|
@@ -175,6 +268,14 @@ try {
|
|
|
175
268
|
break;
|
|
176
269
|
}
|
|
177
270
|
const cmp = compareSemver(latest, current);
|
|
271
|
+
if (cmp === null) {
|
|
272
|
+
// The registry answered with something that is not a semver (e.g. a
|
|
273
|
+
// "beta" dist-tag). It is NOT "already the latest version" — refuse
|
|
274
|
+
// instead, and never interpolate it into a command line.
|
|
275
|
+
console.error(' ! registry returned a non-semver version string; refusing: ' + JSON.stringify(latest));
|
|
276
|
+
process.exitCode = 1;
|
|
277
|
+
break;
|
|
278
|
+
}
|
|
178
279
|
if (cmp === 0) {
|
|
179
280
|
console.log(' = already on the latest published version (' + latest + ').');
|
|
180
281
|
console.log(' Refreshing this project against it anyway…');
|
|
@@ -204,7 +305,9 @@ try {
|
|
|
204
305
|
break;
|
|
205
306
|
}
|
|
206
307
|
// The running process is the OLD code; re-exec the freshly installed binary.
|
|
207
|
-
|
|
308
|
+
// The PATH search would find the FIRST match — often the OLD global
|
|
309
|
+
// install — so the binary npm actually wrote is preferred.
|
|
310
|
+
const freshBin = globalBinPath('archgen-skill') ?? whichOnPath('archgen-skill');
|
|
208
311
|
if (!freshBin) {
|
|
209
312
|
console.log('\nUpgraded to v' + latest + '. Now finish per project with:\n npx archgen-skill@latest init');
|
|
210
313
|
break;
|
|
@@ -218,7 +321,13 @@ try {
|
|
|
218
321
|
process.exitCode = upInit.status ?? 1;
|
|
219
322
|
break;
|
|
220
323
|
}
|
|
221
|
-
spawnSync(freshBin, ['doctor', dir], { stdio: 'inherit' });
|
|
324
|
+
const upDoctor = spawnSync(freshBin, ['doctor', dir], { stdio: 'inherit' });
|
|
325
|
+
if (upDoctor.status !== 0) {
|
|
326
|
+
// A failed doctor must fail the update too — the project was NOT
|
|
327
|
+
// brought to the new version.
|
|
328
|
+
console.error(' ! doctor failed during update (see message above)');
|
|
329
|
+
process.exitCode = upDoctor.status ?? 1;
|
|
330
|
+
}
|
|
222
331
|
break;
|
|
223
332
|
}
|
|
224
333
|
case '--version': case '-v': {
|
|
@@ -243,5 +352,6 @@ function uninstall() {
|
|
|
243
352
|
const r = uninstallGlobal();
|
|
244
353
|
if (r.noop) { console.log('archgen: nothing to uninstall (no manifest found).'); return; }
|
|
245
354
|
console.log(`archgen: removed ${r.removed} entr${r.removed === 1 ? 'y' : 'ies'}` + (r.failed ? `, ${r.failed} FAILED` : ''));
|
|
355
|
+
for (const b of r.backups ?? []) console.log(' ~ backup ' + b + ' (edited copy saved before removal)');
|
|
246
356
|
if (r.failed > 0) process.exitCode = 1;
|
|
247
357
|
}
|
package/lib/block.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// place on the next init/doctor pass and detectBlockVersion() lets doctor
|
|
17
17
|
// report stale formats precisely.
|
|
18
18
|
|
|
19
|
-
import { existsSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
19
|
+
import { existsSync, readFileSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
20
20
|
import { loadConfig } from './config.js';
|
|
21
21
|
import { assertNotSymlink } from './store.js';
|
|
22
22
|
|
|
@@ -171,11 +171,14 @@ export function upsertBlock(existing, block, eol = '\n') {
|
|
|
171
171
|
if (s === -1 || e === -1) {
|
|
172
172
|
throw new Error('found only one archgen marker - fix or remove it manually');
|
|
173
173
|
}
|
|
174
|
+
const prefix = trimmed.slice(0, s);
|
|
174
175
|
const tail = stripCompleteBlocks(trimmed.slice(e + END.length));
|
|
175
|
-
|
|
176
|
+
// An orphan marker ANYWHERE (before the first block as well as after it)
|
|
177
|
+
// would survive the rewrite and leave the file broken — refuse instead.
|
|
178
|
+
if (prefix.includes(START) || prefix.includes(END) || tail.includes(START) || tail.includes(END)) {
|
|
176
179
|
throw new Error('found only one archgen marker - fix or remove it manually');
|
|
177
180
|
}
|
|
178
|
-
return
|
|
181
|
+
return prefix + fresh + tail;
|
|
179
182
|
}
|
|
180
183
|
|
|
181
184
|
const startIdx = trimmed.indexOf(START);
|
|
@@ -209,13 +212,37 @@ function splitBom(raw) {
|
|
|
209
212
|
return { bom, body: bom ? raw.slice(1) : raw };
|
|
210
213
|
}
|
|
211
214
|
|
|
212
|
-
/**
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
215
|
+
/**
|
|
216
|
+
* Write via sibling temp file + rename so readers never observe partial content.
|
|
217
|
+
* @param {string} absPath target file
|
|
218
|
+
* @param {string} data contents
|
|
219
|
+
*/
|
|
220
|
+
export function atomicWrite(absPath, data) {
|
|
221
|
+
let tmp = absPath + '.tmp-' + process.pid + '-' + Date.now();
|
|
222
|
+
for (let attempt = 0; ; attempt++) {
|
|
223
|
+
try {
|
|
224
|
+
// 'wx' (O_CREAT|O_EXCL): if the tmp name already exists — e.g. an attacker
|
|
225
|
+
// planted a symlink there to redirect the write — fail instead of following.
|
|
226
|
+
writeFileSync(tmp, data, { flag: 'wx' });
|
|
227
|
+
break;
|
|
228
|
+
} catch (e) {
|
|
229
|
+
// A collision (two writes inside the same millisecond) is not a failure:
|
|
230
|
+
// retry with a fresh name instead of failing the whole write.
|
|
231
|
+
if (e && e.code === 'EEXIST' && attempt < 10) {
|
|
232
|
+
tmp = absPath + '.tmp-' + process.pid + '-' + Date.now() + '-' + (attempt + 1);
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
throw e;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
try {
|
|
239
|
+
renameSync(tmp, absPath);
|
|
240
|
+
} catch (e) {
|
|
241
|
+
// Never leave the temp file behind: a failed rename must not litter the
|
|
242
|
+
// project with half-written siblings.
|
|
243
|
+
try { unlinkSync(tmp); } catch { /* original intact; leftover tmp is harmless */ }
|
|
244
|
+
throw e;
|
|
245
|
+
}
|
|
219
246
|
}
|
|
220
247
|
|
|
221
248
|
/**
|
|
@@ -250,6 +277,20 @@ export function importsAgents(absPath) {
|
|
|
250
277
|
return body.split(/\r?\n/).some((l) => l.trim() === '@AGENTS.md');
|
|
251
278
|
}
|
|
252
279
|
|
|
280
|
+
/**
|
|
281
|
+
* True when the file carries a COMPLETE managed block (both markers).
|
|
282
|
+
*
|
|
283
|
+
* A file with a hand-written `@AGENTS.md` import has none — that import is
|
|
284
|
+
* the user's, not ours, and must survive untouched. A file WITH a managed
|
|
285
|
+
* block is ours: its provenance stamp is refreshed on every init, exactly
|
|
286
|
+
* like the AGENTS.md block.
|
|
287
|
+
*/
|
|
288
|
+
export function hasManagedBlock(absPath) {
|
|
289
|
+
let raw = '';
|
|
290
|
+
try { raw = readFileSync(absPath, 'utf8'); } catch { return false; }
|
|
291
|
+
return raw.includes(START) && raw.includes(END);
|
|
292
|
+
}
|
|
293
|
+
|
|
253
294
|
/** Strip the managed block from a file on disk, preserving BOM. */
|
|
254
295
|
export function stripManagedFile(absPath) {
|
|
255
296
|
assertNotSymlink(absPath);
|
package/lib/bootstrap.js
CHANGED
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
// 2. ensures CodeGraph: its CLI, its MCP entry, and the project index
|
|
12
12
|
//
|
|
13
13
|
// NEVER FATAL: no npm, no network, a read-only config, or a missing MCP
|
|
14
|
-
// server must still leave a working skill install. Failures are REPORTED
|
|
15
|
-
// never
|
|
14
|
+
// server must still leave a working skill install. Failures are REPORTED to
|
|
15
|
+
// the caller (ok:false + detail) and rendered as a warning by it — never
|
|
16
|
+
// thrown, and never swallowed.
|
|
16
17
|
import { bootstrapMcp, resolveMcpBinary } from './mcp-server.js';
|
|
17
18
|
|
|
18
19
|
/**
|
|
@@ -63,8 +64,11 @@ export function bootstrapServers(opts) {
|
|
|
63
64
|
});
|
|
64
65
|
|
|
65
66
|
if (!r.report) {
|
|
67
|
+
// The bootstrap did NOT run: report it as a failure so the caller tells
|
|
68
|
+
// the user. The skill install itself still succeeded — this is a warning,
|
|
69
|
+
// not a fatal error — but it must never be swallowed silently.
|
|
66
70
|
return {
|
|
67
|
-
ok:
|
|
71
|
+
ok: false,
|
|
68
72
|
detail: `server bootstrap incomplete: ${r.detail ?? 'unknown error'}`,
|
|
69
73
|
};
|
|
70
74
|
}
|
|
@@ -96,7 +100,7 @@ export function bootstrapServers(opts) {
|
|
|
96
100
|
return {
|
|
97
101
|
ok: false,
|
|
98
102
|
report,
|
|
99
|
-
detail: 'one or more server bootstrap steps failed',
|
|
103
|
+
detail: r.detail ? `server bootstrap incomplete: ${r.detail}` : 'one or more server bootstrap steps failed',
|
|
100
104
|
};
|
|
101
105
|
}
|
|
102
106
|
return { ok: true, report };
|
package/lib/config.js
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
//
|
|
3
3
|
// WHY: the version, managed-block marker strings, and well-known filenames
|
|
4
4
|
// were hardcoded independently in block.js, store.js, install.js, install.sh,
|
|
5
|
-
// and SKILL.md frontmatter — and drifted (SKILL.md advertised
|
|
6
|
-
// CLI shipped
|
|
5
|
+
// and SKILL.md frontmatter — and drifted (SKILL.md advertised a version the
|
|
6
|
+
// CLI had never shipped). The canonical file lives at
|
|
7
|
+
// <repo-root>/archgen.config.json;
|
|
7
8
|
// a byte-identical derived copy ships inside the published npm package at
|
|
8
9
|
// vendor/skills/archgen/archgen.config.json (kept in sync by
|
|
9
10
|
// scripts/sync-config.mjs + scripts/sync-vendor.mjs).
|