cyber-sdd 0.7.0 → 0.7.1

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.
Files changed (149) hide show
  1. package/.claude-plugin/plugin.json +1 -2
  2. package/.codex-plugin/plugin.json +1 -2
  3. package/agents/sdd-scanner.md +1 -1
  4. package/package.json +11 -13
  5. package/plugin.json +55 -0
  6. package/skills/align-spec/SKILL.md +1 -1
  7. package/skills/align-spec/scripts/align-spec.mjs +6755 -12
  8. package/skills/blast-estimate/README.md +3 -3
  9. package/skills/blast-estimate/scripts/blast-estimate.mjs +601 -12
  10. package/skills/check-field-mandates/scripts/check-field-mandates.mjs +253 -12
  11. package/skills/check-partition-quality/scripts/check-partition-quality.mjs +235 -12
  12. package/skills/check-plan-safety/SKILL.md +2 -3
  13. package/skills/check-plan-safety/scripts/check-plan-safety.mjs +109 -12
  14. package/skills/check-plugin-manifests/scripts/check-plugin-manifests.mjs +250 -12
  15. package/skills/check-project-specs/SKILL.md +1 -1
  16. package/skills/check-project-specs/scripts/check-project-specs.mjs +593 -12
  17. package/skills/check-retired-terms/SKILL.md +2 -3
  18. package/skills/check-retired-terms/scripts/check-retired-terms.mjs +186 -12
  19. package/skills/check-scenario-overlap/SKILL.md +1 -2
  20. package/skills/check-scenario-overlap/scripts/check-scenario-overlap.mjs +177 -12
  21. package/skills/check-spec-references/SKILL.md +2 -2
  22. package/skills/check-spec-references/scripts/check-spec-references.mjs +287 -12
  23. package/skills/check-spec-structure/SKILL.md +1 -2
  24. package/skills/check-spec-structure/scripts/check-spec-structure.mjs +243 -12
  25. package/skills/collision-ladder/README.md +2 -2
  26. package/skills/collision-ladder/scripts/collision-ladder.mjs +7047 -12
  27. package/skills/combat-log-governance/SKILL.md +1 -1
  28. package/skills/concept-index/SKILL.md +1 -1
  29. package/skills/concept-index/scripts/concept-index.mjs +183 -12
  30. package/skills/discover-plans/README.md +3 -3
  31. package/skills/discover-plans/SKILL.md +1 -1
  32. package/skills/discover-plans/scripts/discover-plans.mjs +137 -12
  33. package/skills/discover-specs/README.md +3 -3
  34. package/skills/discover-specs/SKILL.md +1 -1
  35. package/skills/discover-specs/scripts/discover-specs.mjs +321 -12
  36. package/skills/doctrine-loop/SKILL.md +1 -1
  37. package/skills/gate-validation-governance/SKILL.md +1 -1
  38. package/skills/init/README.md +2 -2
  39. package/skills/init/scripts/wire-statusline.mjs +168 -12
  40. package/skills/manage-ignore/README.md +3 -3
  41. package/skills/manage-ignore/SKILL.md +1 -2
  42. package/skills/manage-ignore/scripts/manage-ignore.mjs +285 -12
  43. package/skills/manage-scenario-bridge/README.md +3 -3
  44. package/skills/manage-scenario-bridge/SKILL.md +1 -1
  45. package/skills/manage-scenario-bridge/scripts/manage-scenario-bridge.mjs +239 -12
  46. package/skills/manage-spec-anchors/README.md +3 -3
  47. package/skills/manage-spec-anchors/SKILL.md +1 -1
  48. package/skills/manage-spec-anchors/scripts/manage-spec-anchors.mjs +310 -12
  49. package/skills/mission-graph/README.md +2 -2
  50. package/skills/mission-graph/SKILL.md +1 -1
  51. package/skills/mission-graph/scripts/mission-graph.mjs +1040 -12
  52. package/skills/pause-mission/SKILL.md +1 -1
  53. package/skills/place-node/SKILL.md +2 -2
  54. package/skills/place-node/scripts/place-node.mjs +117 -12
  55. package/skills/plan-retirement/README.md +2 -2
  56. package/skills/plan-retirement/scripts/retire-plans.mjs +123 -12
  57. package/skills/resolve-governances/README.md +3 -3
  58. package/skills/resolve-governances/SKILL.md +1 -1
  59. package/skills/resolve-governances/scripts/resolve-governances.mjs +352 -12
  60. package/skills/resolve-tracking/SKILL.md +1 -1
  61. package/skills/resolve-tracking/scripts/resolve-tracking.mjs +152 -12
  62. package/skills/scaffold-project-spec/SKILL.md +1 -1
  63. package/skills/spec-gate/README.md +12 -9
  64. package/skills/spec-gate/SKILL.md +2 -2
  65. package/skills/spec-gate/scripts/check-spec-state.mjs +422 -12
  66. package/skills/spec-gate/scripts/check-suite.mjs +6500 -12
  67. package/skills/spec-gate/scripts/classify-edit-class.mjs +6579 -12
  68. package/skills/spec-producer-governance/SKILL.md +2 -2
  69. package/skills/start-mission/SKILL.md +1 -1
  70. package/skills/suite-format-governance/README.md +1 -1
  71. package/skills/suite-format-governance/SKILL.md +1 -1
  72. package/skills/touch-set-correction/README.md +5 -5
  73. package/skills/touch-set-correction/SKILL.md +1 -1
  74. package/skills/touch-set-correction/scripts/touch-set-correction.mjs +6663 -12
  75. package/skills/verify-scenarios/README.md +6 -6
  76. package/skills/verify-scenarios/scripts/verify-scenarios.mjs +6539 -12
  77. package/.plugin/pins.json +0 -3
  78. package/.plugin/plugin.json +0 -19
  79. package/dist/align-spec/scripts/align-spec.js +0 -102
  80. package/dist/check-project-specs/scripts/check-project-specs.js +0 -312
  81. package/dist/check-scenario-overlap/scripts/check-scenario-overlap.js +0 -175
  82. package/dist/check-spec-references/scripts/check-spec-references.js +0 -285
  83. package/dist/check-spec-structure/scripts/check-spec-structure.js +0 -241
  84. package/dist/concept-index/scripts/concept-index.js +0 -181
  85. package/dist/discover-specs/scripts/discover-specs.js +0 -318
  86. package/dist/spec-gate/scripts/check-spec-state.js +0 -420
  87. package/dist/spec-gate/scripts/check-suite.js +0 -317
  88. package/dist/spec-gate/scripts/classify-edit-class.js +0 -263
  89. package/dist/spec-gate/vendor/gherkin-cli.js +0 -6369
  90. package/skills/align-spec/scripts/align-spec.bundle.mjs +0 -6753
  91. package/skills/align-spec/scripts/align-spec.mts +0 -188
  92. package/skills/blast-estimate/scripts/blast-estimate.bundle.mjs +0 -593
  93. package/skills/blast-estimate/scripts/blast-estimate.mts +0 -586
  94. package/skills/check-field-mandates/scripts/check-field-mandates.bundle.mjs +0 -251
  95. package/skills/check-field-mandates/scripts/check-field-mandates.mts +0 -337
  96. package/skills/check-partition-quality/scripts/check-partition-quality.bundle.mjs +0 -233
  97. package/skills/check-partition-quality/scripts/check-partition-quality.mts +0 -338
  98. package/skills/check-plan-safety/scripts/check-plan-safety.bundle.mjs +0 -107
  99. package/skills/check-plan-safety/scripts/check-plan-safety.mts +0 -148
  100. package/skills/check-plugin-manifests/scripts/check-plugin-manifests.bundle.mjs +0 -248
  101. package/skills/check-plugin-manifests/scripts/check-plugin-manifests.mts +0 -303
  102. package/skills/check-project-specs/scripts/check-project-specs.bundle.mjs +0 -584
  103. package/skills/check-project-specs/scripts/check-project-specs.mts +0 -370
  104. package/skills/check-retired-terms/scripts/check-retired-terms.bundle.mjs +0 -184
  105. package/skills/check-retired-terms/scripts/check-retired-terms.mts +0 -293
  106. package/skills/check-scenario-overlap/scripts/check-scenario-overlap.bundle.mjs +0 -175
  107. package/skills/check-scenario-overlap/scripts/check-scenario-overlap.mts +0 -250
  108. package/skills/check-spec-references/scripts/check-spec-references.bundle.mjs +0 -285
  109. package/skills/check-spec-references/scripts/check-spec-references.mts +0 -422
  110. package/skills/check-spec-structure/scripts/check-spec-structure.bundle.mjs +0 -241
  111. package/skills/check-spec-structure/scripts/check-spec-structure.mts +0 -347
  112. package/skills/collision-ladder/scripts/collision-ladder.bundle.mjs +0 -7041
  113. package/skills/collision-ladder/scripts/collision-ladder.mts +0 -662
  114. package/skills/concept-index/scripts/concept-index.bundle.mjs +0 -181
  115. package/skills/concept-index/scripts/concept-index.mts +0 -246
  116. package/skills/discover-plans/scripts/discover-plans.bundle.mjs +0 -135
  117. package/skills/discover-plans/scripts/discover-plans.mts +0 -215
  118. package/skills/discover-specs/scripts/discover-specs.bundle.mjs +0 -319
  119. package/skills/discover-specs/scripts/discover-specs.mts +0 -420
  120. package/skills/init/scripts/wire-statusline.bundle.mjs +0 -166
  121. package/skills/init/scripts/wire-statusline.mts +0 -279
  122. package/skills/manage-ignore/scripts/manage-ignore.bundle.mjs +0 -244
  123. package/skills/manage-ignore/scripts/manage-ignore.mts +0 -297
  124. package/skills/manage-scenario-bridge/scripts/manage-scenario-bridge.bundle.mjs +0 -237
  125. package/skills/manage-scenario-bridge/scripts/manage-scenario-bridge.mts +0 -159
  126. package/skills/manage-spec-anchors/scripts/manage-spec-anchors.bundle.mjs +0 -308
  127. package/skills/manage-spec-anchors/scripts/manage-spec-anchors.mts +0 -331
  128. package/skills/mission-graph/scripts/mission-graph.bundle.mjs +0 -1038
  129. package/skills/mission-graph/scripts/mission-graph.mts +0 -1333
  130. package/skills/place-node/scripts/place-node.bundle.mjs +0 -115
  131. package/skills/place-node/scripts/place-node.mts +0 -158
  132. package/skills/plan-retirement/scripts/retire-plans.bundle.mjs +0 -121
  133. package/skills/plan-retirement/scripts/retire-plans.mts +0 -205
  134. package/skills/resolve-governances/scripts/resolve-governances.bundle.mjs +0 -350
  135. package/skills/resolve-governances/scripts/resolve-governances.mts +0 -518
  136. package/skills/resolve-tracking/scripts/resolve-tracking.bundle.mjs +0 -150
  137. package/skills/resolve-tracking/scripts/resolve-tracking.mts +0 -214
  138. package/skills/spec-gate/scripts/check-spec-state.bundle.mjs +0 -420
  139. package/skills/spec-gate/scripts/check-spec-state.mts +0 -637
  140. package/skills/spec-gate/scripts/check-suite.bundle.mjs +0 -6498
  141. package/skills/spec-gate/scripts/check-suite.mts +0 -538
  142. package/skills/spec-gate/scripts/classify-edit-class.bundle.mjs +0 -6577
  143. package/skills/spec-gate/scripts/classify-edit-class.mts +0 -414
  144. package/skills/spec-gate/vendor/gherkin-cli.d.mts +0 -2
  145. package/skills/spec-gate/vendor/gherkin-cli.mjs +0 -6545
  146. package/skills/touch-set-correction/scripts/touch-set-correction.bundle.mjs +0 -6655
  147. package/skills/touch-set-correction/scripts/touch-set-correction.mts +0 -429
  148. package/skills/verify-scenarios/scripts/verify-scenarios.bundle.mjs +0 -6537
  149. package/skills/verify-scenarios/scripts/verify-scenarios.mts +0 -470
@@ -1,12 +1,310 @@
1
- // Launcher for the engine of the same name. Run it as `node <skill>/scripts/<engine>.mjs`.
2
- // A checkout runs the .mts source. An npm install keeps the skill under node_modules, where Node
3
- // will not strip types, so it runs the self-contained bundle built for the package.
4
- import { spawnSync } from 'node:child_process'
5
- import { fileURLToPath } from 'node:url'
6
-
7
- const self = fileURLToPath(import.meta.url)
8
- const installed = /[\\/]node_modules[\\/]/.test(self)
9
- const target = self.replace(/\.mjs$/, installed ? '.bundle.mjs' : '.mts')
10
- const run = spawnSync(process.execPath, [...process.execArgv, target, ...process.argv.slice(2)], { stdio: 'inherit' })
11
- if (run.error) throw run.error
12
- process.exitCode = run.status ?? 1
1
+ #!/usr/bin/env node
2
+ // Compiled by `universal-plugin plugin build` from src/manage-spec-anchors/manage-spec-anchors.mts. Edit the source, not this file.
3
+ globalThis.__SKILL_SCRIPT__ = {"package":null,"bin":null,"version":"0.7.1","entry":"skills/manage-spec-anchors/scripts/manage-spec-anchors.mjs","commands":{}};
4
+ import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, statSync, writeFileSync } from "node:fs";
5
+ import { dirname, join } from "node:path";
6
+ import { pathToFileURL } from "node:url";
7
+ //#region src/manage-spec-anchors/manage-spec-anchors.mts
8
+ const ANCHORS_CONFIG = ".agents/sdd/spec-anchors.toml";
9
+ const LIFECYCLE_STATUSES = /* @__PURE__ */ new Set([
10
+ "draft",
11
+ "approved",
12
+ "implemented",
13
+ "deprecated"
14
+ ]);
15
+ const SKIP_DIRS = /* @__PURE__ */ new Set([
16
+ "node_modules",
17
+ ".git",
18
+ "dist",
19
+ ".turbo",
20
+ ".next",
21
+ "coverage"
22
+ ]);
23
+ const FIXED_ANCHORS = [
24
+ {
25
+ pattern: ".agents/spec/",
26
+ kind: "fixed",
27
+ explanation: "the repo-root single-project spec"
28
+ },
29
+ {
30
+ pattern: ".agents/specs/<project>/",
31
+ kind: "fixed",
32
+ explanation: "each repo-root multi-project spec (the folder names the project)"
33
+ },
34
+ {
35
+ pattern: "<project-path>/.agents/spec/",
36
+ kind: "fixed",
37
+ explanation: "a nested project's hoisted spec, at any depth"
38
+ }
39
+ ];
40
+ function parseAnchorsToml(text) {
41
+ const m = /(^|\n)\s*anchors\s*=\s*\[([\s\S]*?)\]/.exec(text);
42
+ if (!m) return [];
43
+ const out = [];
44
+ for (const q of m[2].matchAll(/"([^"]*)"|'([^']*)'/g)) out.push((q[1] ?? q[2]).trim());
45
+ return out.filter((s) => s !== "");
46
+ }
47
+ function readCustomAnchors(root) {
48
+ const file = join(root, ANCHORS_CONFIG);
49
+ if (!existsSync(file)) return [];
50
+ try {
51
+ return parseAnchorsToml(readFileSync(file, "utf8"));
52
+ } catch {
53
+ return [];
54
+ }
55
+ }
56
+ function serializeAnchors(patterns) {
57
+ if (patterns.length === 0) return "anchors = []\n";
58
+ return `anchors = [\n${patterns.map((p) => ` "${p}",`).join("\n")}\n]\n`;
59
+ }
60
+ function writeCustomAnchors(root, patterns) {
61
+ const file = join(root, ANCHORS_CONFIG);
62
+ mkdirSync(dirname(file), { recursive: true });
63
+ writeFileSync(file, serializeAnchors(patterns));
64
+ }
65
+ function isValidPattern(pattern) {
66
+ const p = pattern.trim().replace(/\\/g, "/");
67
+ if (p === "" || p.startsWith("/") || /(^|\/)\.\.(\/|$)/.test(p)) return false;
68
+ const segs = p.replace(/^\/+|\/+$/g, "").split("/");
69
+ if (segs.some((s) => s === "")) return false;
70
+ return segs.every((s) => s === "*" || s === "**" || s === "<project>" || !/[<>]/.test(s));
71
+ }
72
+ function isFixedConvention(pattern) {
73
+ const p = pattern.trim().replace(/\\/g, "/").replace(/^\/+|\/+$/g, "");
74
+ return p === ".agents/spec" || p === ".agents/specs/<project>" || p === ".agents/specs/*" || p.endsWith("/.agents/spec") || p.endsWith("/.agents/specs/<project>");
75
+ }
76
+ function listAnchors(root) {
77
+ const custom = readCustomAnchors(root).map((pattern) => ({
78
+ pattern,
79
+ kind: "custom",
80
+ explanation: "a declared extra anchor"
81
+ }));
82
+ return [...FIXED_ANCHORS, ...custom];
83
+ }
84
+ function addAnchor(root, pattern) {
85
+ const p = pattern.trim();
86
+ if (isFixedConvention(p)) return {
87
+ ok: false,
88
+ reason: "that is a fixed convention — always scanned, never curated"
89
+ };
90
+ if (!isValidPattern(p)) return {
91
+ ok: false,
92
+ reason: "malformed anchor pattern"
93
+ };
94
+ const current = readCustomAnchors(root);
95
+ if (current.includes(p)) return {
96
+ ok: true,
97
+ changed: false,
98
+ anchors: current,
99
+ message: "already present"
100
+ };
101
+ const next = [...current, p];
102
+ writeCustomAnchors(root, next);
103
+ return {
104
+ ok: true,
105
+ changed: true,
106
+ anchors: next,
107
+ message: `added ${p}`
108
+ };
109
+ }
110
+ function removeAnchor(root, pattern) {
111
+ const p = pattern.trim();
112
+ const current = readCustomAnchors(root);
113
+ if (!current.includes(p)) return {
114
+ ok: true,
115
+ changed: false,
116
+ anchors: current,
117
+ message: "nothing was removed"
118
+ };
119
+ const next = current.filter((a) => a !== p);
120
+ writeCustomAnchors(root, next);
121
+ return {
122
+ ok: true,
123
+ changed: true,
124
+ anchors: next,
125
+ message: `removed ${p}`
126
+ };
127
+ }
128
+ function editAnchor(root, oldPattern, newPattern) {
129
+ const oldP = oldPattern.trim();
130
+ const newP = newPattern.trim();
131
+ if (isFixedConvention(oldP)) return {
132
+ ok: false,
133
+ reason: "cannot edit a fixed convention"
134
+ };
135
+ if (!isValidPattern(newP)) return {
136
+ ok: false,
137
+ reason: "malformed replacement pattern"
138
+ };
139
+ if (isFixedConvention(newP)) return {
140
+ ok: false,
141
+ reason: "replacement is a fixed convention"
142
+ };
143
+ const current = readCustomAnchors(root);
144
+ if (!current.includes(oldP)) return {
145
+ ok: false,
146
+ reason: `no such custom anchor: ${oldP}`
147
+ };
148
+ const next = current.map((a) => a === oldP ? newP : a);
149
+ writeCustomAnchors(root, next);
150
+ return {
151
+ ok: true,
152
+ changed: true,
153
+ anchors: next,
154
+ message: `edited ${oldP} -> ${newP}`
155
+ };
156
+ }
157
+ function inducePatterns(root, samplePath) {
158
+ const rel = samplePath.trim().replace(/\\/g, "/").replace(/^\/+|\/+$/g, "");
159
+ if (rel === "" || /(^|\/)\.\.(\/|$)/.test(rel)) return {
160
+ ok: false,
161
+ reason: "path is not repo-relative"
162
+ };
163
+ let ok = false;
164
+ try {
165
+ ok = statSync(join(root, rel)).isDirectory();
166
+ } catch {
167
+ ok = false;
168
+ }
169
+ if (!ok) return {
170
+ ok: false,
171
+ reason: `sample path does not resolve to a directory under the repo: ${rel}`
172
+ };
173
+ const generalized = [...rel.split("/").slice(0, -1), "<project>"].join("/");
174
+ return {
175
+ ok: true,
176
+ candidates: generalized === rel ? [rel] : [rel, generalized]
177
+ };
178
+ }
179
+ function collectDescendants(root, startDir) {
180
+ const out = [startDir];
181
+ let entries;
182
+ try {
183
+ entries = readdirSync(join(root, startDir), { withFileTypes: true });
184
+ } catch {
185
+ return out;
186
+ }
187
+ for (const e of entries) {
188
+ if (!e.isDirectory() || SKIP_DIRS.has(e.name)) continue;
189
+ out.push(...collectDescendants(root, startDir ? `${startDir}/${e.name}` : e.name));
190
+ }
191
+ return out;
192
+ }
193
+ function previewPattern(root, pattern) {
194
+ const p = pattern.trim();
195
+ if (!isValidPattern(p)) return {
196
+ ok: false,
197
+ reason: "malformed candidate pattern"
198
+ };
199
+ const segs = p.replace(/\\/g, "/").replace(/^\/+|\/+$/g, "").split("/").filter(Boolean);
200
+ let frontier = [{ dir: "" }];
201
+ for (const seg of segs) {
202
+ const next = [];
203
+ if (seg === "**") {
204
+ for (const node of frontier) for (const dir of collectDescendants(root, node.dir)) next.push({
205
+ dir,
206
+ capturedName: node.capturedName
207
+ });
208
+ frontier = next;
209
+ continue;
210
+ }
211
+ const isGlob = seg === "*" || seg === "<project>";
212
+ for (const node of frontier) if (isGlob) {
213
+ let entries;
214
+ try {
215
+ entries = readdirSync(join(root, node.dir), { withFileTypes: true });
216
+ } catch {
217
+ continue;
218
+ }
219
+ for (const e of entries) {
220
+ if (!e.isDirectory() || SKIP_DIRS.has(e.name)) continue;
221
+ next.push({
222
+ dir: node.dir ? `${node.dir}/${e.name}` : e.name,
223
+ capturedName: seg === "<project>" ? e.name : node.capturedName
224
+ });
225
+ }
226
+ } else next.push({
227
+ dir: node.dir ? `${node.dir}/${seg}` : seg,
228
+ capturedName: node.capturedName
229
+ });
230
+ frontier = next;
231
+ }
232
+ const matches = [];
233
+ for (const node of frontier) {
234
+ const rel = node.dir ? `${node.dir}/spec.md` : "spec.md";
235
+ let status;
236
+ try {
237
+ status = /(^|\n)status:\s*["']?([a-z]+)/.exec(readFileSync(join(root, rel), "utf8"))?.[2];
238
+ } catch {
239
+ continue;
240
+ }
241
+ if (!status || !LIFECYCLE_STATUSES.has(status)) continue;
242
+ matches.push({
243
+ path: node.dir,
244
+ name: node.capturedName ?? node.dir.split("/").pop() ?? node.dir
245
+ });
246
+ }
247
+ matches.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
248
+ return {
249
+ ok: true,
250
+ matches
251
+ };
252
+ }
253
+ function flag(argv, name) {
254
+ const i = argv.indexOf(name);
255
+ return i === -1 ? void 0 : argv[i + 1];
256
+ }
257
+ function main(argv) {
258
+ const root = flag(argv, "--root") ?? ".";
259
+ const w = (s) => process.stdout.write(`${s}\n`);
260
+ if (argv.includes("--list")) {
261
+ w("Spec anchors discovery scans (fixed conventions are always scanned):");
262
+ for (const a of listAnchors(root)) w(` [${a.kind}] ${a.pattern} — ${a.explanation}`);
263
+ return 0;
264
+ }
265
+ if (argv.includes("--add")) {
266
+ const r = addAnchor(root, flag(argv, "--add") ?? "");
267
+ w(r.ok ? r.message : `refused: ${r.reason}`);
268
+ return r.ok ? 0 : 1;
269
+ }
270
+ if (argv.includes("--remove")) {
271
+ const r = removeAnchor(root, flag(argv, "--remove") ?? "");
272
+ w(r.ok ? r.message : `refused: ${r.reason}`);
273
+ return r.ok ? 0 : 1;
274
+ }
275
+ if (argv.includes("--edit")) {
276
+ const i = argv.indexOf("--edit");
277
+ const r = editAnchor(root, argv[i + 1] ?? "", argv[i + 2] ?? "");
278
+ w(r.ok ? r.message : `refused: ${r.reason}`);
279
+ return r.ok ? 0 : 1;
280
+ }
281
+ if (argv.includes("--induce")) {
282
+ const r = inducePatterns(root, flag(argv, "--induce") ?? "");
283
+ if (!r.ok) {
284
+ w(`unusable: ${r.reason}`);
285
+ return 1;
286
+ }
287
+ w("Candidate anchor patterns:");
288
+ for (const c of r.candidates) w(` ${c}`);
289
+ return 0;
290
+ }
291
+ if (argv.includes("--preview")) {
292
+ const r = previewPattern(root, flag(argv, "--preview") ?? "");
293
+ if (!r.ok) {
294
+ w(`invalid: ${r.reason}`);
295
+ return 1;
296
+ }
297
+ if (r.matches.length === 0) {
298
+ w("This pattern matches no project.");
299
+ return 0;
300
+ }
301
+ w("This pattern would discover:");
302
+ for (const m of r.matches) w(` ${m.path} (${m.name})`);
303
+ return 0;
304
+ }
305
+ w("usage: manage-spec-anchors --list | --add <p> | --remove <p> | --edit <old> <new> | --induce <path> | --preview <p>");
306
+ return 0;
307
+ }
308
+ if (process.argv[1] && import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href) process.exit(main(process.argv.slice(2)));
309
+ //#endregion
310
+ export { FIXED_ANCHORS, addAnchor, editAnchor, inducePatterns, isFixedConvention, isValidPattern, listAnchors, main, parseAnchorsToml, previewPattern, readCustomAnchors, removeAnchor, serializeAnchors };
@@ -8,6 +8,6 @@ cyberplace repository, not shipped in this package) carries the authoritative be
8
8
  and the frozen 36-scenario contract.
9
9
 
10
10
  - **Skill contract:** [`SKILL.md`](./SKILL.md)
11
- - **Script:** [`scripts/mission-graph.mts`](./scripts/mission-graph.mts)
12
- - **Tests:** [`scripts/mission-graph.test.mts`](./scripts/mission-graph.test.mts) (`node:test`) —
11
+ - **Script:** [`scripts/mission-graph.mjs`](./scripts/mission-graph.mjs)
12
+ - **Tests:** [`src/mission-graph/mission-graph.test.mts`](../../src/mission-graph/mission-graph.test.mts) (`node:test`) —
13
13
  one test per frozen scenario, titled `scenario: <verbatim frozen scenario name>`.
@@ -23,7 +23,7 @@ plus a zero-dependency **fold** into two read-only views and one write-time guar
23
23
  ⊆ the declared set), what's its release floor (the closure alone — support members don't gate
24
24
  it), and its completed/total progress.
25
25
 
26
- It carries a self-contained `.mts` script (the repo's node-≥23.6 / no-deps convention). Pure
26
+ It carries a self-contained `.mjs` script (compiled from `src/`; no runtime deps, Node ≥22.11). Pure
27
27
  derivations (`fold`, `ready`, `cycles`, `checkOperation`, `proposeEdge`) take and return plain data
28
28
  only — no fs access — kept apart from a thin store-IO seam with **two backends**: an in-tree JSONL
29
29
  file and the branch-independent orphan ref `refs/sdd/mission-graph`, resolved at run time. The swap