cyber-sdd 0.7.0 → 0.7.2
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/.claude-plugin/plugin.json +1 -2
- package/.codex-plugin/plugin.json +1 -2
- package/CHANGELOG.md +320 -0
- package/agents/sdd-scanner.md +1 -1
- package/package.json +11 -12
- package/plugin.json +55 -0
- package/skills/align-spec/SKILL.md +1 -1
- package/skills/align-spec/scripts/align-spec.mjs +6755 -12
- package/skills/blast-estimate/README.md +3 -3
- package/skills/blast-estimate/scripts/blast-estimate.mjs +601 -12
- package/skills/check-field-mandates/scripts/check-field-mandates.mjs +253 -12
- package/skills/check-partition-quality/scripts/check-partition-quality.mjs +235 -12
- package/skills/check-plan-safety/SKILL.md +2 -3
- package/skills/check-plan-safety/scripts/check-plan-safety.mjs +109 -12
- package/skills/check-plugin-manifests/scripts/check-plugin-manifests.mjs +250 -12
- package/skills/check-project-specs/SKILL.md +1 -1
- package/skills/check-project-specs/scripts/check-project-specs.mjs +593 -12
- package/skills/check-retired-terms/SKILL.md +2 -3
- package/skills/check-retired-terms/scripts/check-retired-terms.mjs +186 -12
- package/skills/check-scenario-overlap/SKILL.md +1 -2
- package/skills/check-scenario-overlap/scripts/check-scenario-overlap.mjs +177 -12
- package/skills/check-spec-references/SKILL.md +2 -2
- package/skills/check-spec-references/scripts/check-spec-references.mjs +287 -12
- package/skills/check-spec-structure/SKILL.md +1 -2
- package/skills/check-spec-structure/scripts/check-spec-structure.mjs +243 -12
- package/skills/collision-ladder/README.md +2 -2
- package/skills/collision-ladder/scripts/collision-ladder.mjs +7047 -12
- package/skills/combat-log-governance/SKILL.md +1 -1
- package/skills/concept-index/SKILL.md +1 -1
- package/skills/concept-index/scripts/concept-index.mjs +183 -12
- package/skills/discover-plans/README.md +3 -3
- package/skills/discover-plans/SKILL.md +1 -1
- package/skills/discover-plans/scripts/discover-plans.mjs +137 -12
- package/skills/discover-specs/README.md +3 -3
- package/skills/discover-specs/SKILL.md +1 -1
- package/skills/discover-specs/scripts/discover-specs.mjs +321 -12
- package/skills/doctrine-loop/SKILL.md +1 -1
- package/skills/gate-validation-governance/SKILL.md +1 -1
- package/skills/init/README.md +2 -2
- package/skills/init/scripts/wire-statusline.mjs +168 -12
- package/skills/manage-ignore/README.md +3 -3
- package/skills/manage-ignore/SKILL.md +1 -2
- package/skills/manage-ignore/scripts/manage-ignore.mjs +285 -12
- package/skills/manage-scenario-bridge/README.md +3 -3
- package/skills/manage-scenario-bridge/SKILL.md +1 -1
- package/skills/manage-scenario-bridge/scripts/manage-scenario-bridge.mjs +239 -12
- package/skills/manage-spec-anchors/README.md +3 -3
- package/skills/manage-spec-anchors/SKILL.md +1 -1
- package/skills/manage-spec-anchors/scripts/manage-spec-anchors.mjs +310 -12
- package/skills/mission-graph/README.md +2 -2
- package/skills/mission-graph/SKILL.md +1 -1
- package/skills/mission-graph/scripts/mission-graph.mjs +1040 -12
- package/skills/pause-mission/SKILL.md +1 -1
- package/skills/place-node/SKILL.md +2 -2
- package/skills/place-node/scripts/place-node.mjs +117 -12
- package/skills/plan-retirement/README.md +2 -2
- package/skills/plan-retirement/scripts/retire-plans.mjs +123 -12
- package/skills/resolve-governances/README.md +3 -3
- package/skills/resolve-governances/SKILL.md +1 -1
- package/skills/resolve-governances/scripts/resolve-governances.mjs +352 -12
- package/skills/resolve-tracking/SKILL.md +1 -1
- package/skills/resolve-tracking/scripts/resolve-tracking.mjs +152 -12
- package/skills/scaffold-project-spec/SKILL.md +1 -1
- package/skills/spec-gate/README.md +12 -9
- package/skills/spec-gate/SKILL.md +2 -2
- package/skills/spec-gate/scripts/check-spec-state.mjs +422 -12
- package/skills/spec-gate/scripts/check-suite.mjs +6500 -12
- package/skills/spec-gate/scripts/classify-edit-class.mjs +6579 -12
- package/skills/spec-producer-governance/SKILL.md +2 -2
- package/skills/start-mission/SKILL.md +1 -1
- package/skills/suite-format-governance/README.md +1 -1
- package/skills/suite-format-governance/SKILL.md +1 -1
- package/skills/touch-set-correction/README.md +5 -5
- package/skills/touch-set-correction/SKILL.md +1 -1
- package/skills/touch-set-correction/scripts/touch-set-correction.mjs +6663 -12
- package/skills/verify-scenarios/README.md +6 -6
- package/skills/verify-scenarios/scripts/verify-scenarios.mjs +6539 -12
- package/.plugin/pins.json +0 -3
- package/.plugin/plugin.json +0 -19
- package/dist/align-spec/scripts/align-spec.js +0 -102
- package/dist/check-project-specs/scripts/check-project-specs.js +0 -312
- package/dist/check-scenario-overlap/scripts/check-scenario-overlap.js +0 -175
- package/dist/check-spec-references/scripts/check-spec-references.js +0 -285
- package/dist/check-spec-structure/scripts/check-spec-structure.js +0 -241
- package/dist/concept-index/scripts/concept-index.js +0 -181
- package/dist/discover-specs/scripts/discover-specs.js +0 -318
- package/dist/spec-gate/scripts/check-spec-state.js +0 -420
- package/dist/spec-gate/scripts/check-suite.js +0 -317
- package/dist/spec-gate/scripts/classify-edit-class.js +0 -263
- package/dist/spec-gate/vendor/gherkin-cli.js +0 -6369
- package/skills/align-spec/scripts/align-spec.bundle.mjs +0 -6753
- package/skills/align-spec/scripts/align-spec.mts +0 -188
- package/skills/blast-estimate/scripts/blast-estimate.bundle.mjs +0 -593
- package/skills/blast-estimate/scripts/blast-estimate.mts +0 -586
- package/skills/check-field-mandates/scripts/check-field-mandates.bundle.mjs +0 -251
- package/skills/check-field-mandates/scripts/check-field-mandates.mts +0 -337
- package/skills/check-partition-quality/scripts/check-partition-quality.bundle.mjs +0 -233
- package/skills/check-partition-quality/scripts/check-partition-quality.mts +0 -338
- package/skills/check-plan-safety/scripts/check-plan-safety.bundle.mjs +0 -107
- package/skills/check-plan-safety/scripts/check-plan-safety.mts +0 -148
- package/skills/check-plugin-manifests/scripts/check-plugin-manifests.bundle.mjs +0 -248
- package/skills/check-plugin-manifests/scripts/check-plugin-manifests.mts +0 -303
- package/skills/check-project-specs/scripts/check-project-specs.bundle.mjs +0 -584
- package/skills/check-project-specs/scripts/check-project-specs.mts +0 -370
- package/skills/check-retired-terms/scripts/check-retired-terms.bundle.mjs +0 -184
- package/skills/check-retired-terms/scripts/check-retired-terms.mts +0 -293
- package/skills/check-scenario-overlap/scripts/check-scenario-overlap.bundle.mjs +0 -175
- package/skills/check-scenario-overlap/scripts/check-scenario-overlap.mts +0 -250
- package/skills/check-spec-references/scripts/check-spec-references.bundle.mjs +0 -285
- package/skills/check-spec-references/scripts/check-spec-references.mts +0 -422
- package/skills/check-spec-structure/scripts/check-spec-structure.bundle.mjs +0 -241
- package/skills/check-spec-structure/scripts/check-spec-structure.mts +0 -347
- package/skills/collision-ladder/scripts/collision-ladder.bundle.mjs +0 -7041
- package/skills/collision-ladder/scripts/collision-ladder.mts +0 -662
- package/skills/concept-index/scripts/concept-index.bundle.mjs +0 -181
- package/skills/concept-index/scripts/concept-index.mts +0 -246
- package/skills/discover-plans/scripts/discover-plans.bundle.mjs +0 -135
- package/skills/discover-plans/scripts/discover-plans.mts +0 -215
- package/skills/discover-specs/scripts/discover-specs.bundle.mjs +0 -319
- package/skills/discover-specs/scripts/discover-specs.mts +0 -420
- package/skills/init/scripts/wire-statusline.bundle.mjs +0 -166
- package/skills/init/scripts/wire-statusline.mts +0 -279
- package/skills/manage-ignore/scripts/manage-ignore.bundle.mjs +0 -244
- package/skills/manage-ignore/scripts/manage-ignore.mts +0 -297
- package/skills/manage-scenario-bridge/scripts/manage-scenario-bridge.bundle.mjs +0 -237
- package/skills/manage-scenario-bridge/scripts/manage-scenario-bridge.mts +0 -159
- package/skills/manage-spec-anchors/scripts/manage-spec-anchors.bundle.mjs +0 -308
- package/skills/manage-spec-anchors/scripts/manage-spec-anchors.mts +0 -331
- package/skills/mission-graph/scripts/mission-graph.bundle.mjs +0 -1038
- package/skills/mission-graph/scripts/mission-graph.mts +0 -1333
- package/skills/place-node/scripts/place-node.bundle.mjs +0 -115
- package/skills/place-node/scripts/place-node.mts +0 -158
- package/skills/plan-retirement/scripts/retire-plans.bundle.mjs +0 -121
- package/skills/plan-retirement/scripts/retire-plans.mts +0 -205
- package/skills/resolve-governances/scripts/resolve-governances.bundle.mjs +0 -350
- package/skills/resolve-governances/scripts/resolve-governances.mts +0 -518
- package/skills/resolve-tracking/scripts/resolve-tracking.bundle.mjs +0 -150
- package/skills/resolve-tracking/scripts/resolve-tracking.mts +0 -214
- package/skills/spec-gate/scripts/check-spec-state.bundle.mjs +0 -420
- package/skills/spec-gate/scripts/check-spec-state.mts +0 -637
- package/skills/spec-gate/scripts/check-suite.bundle.mjs +0 -6498
- package/skills/spec-gate/scripts/check-suite.mts +0 -538
- package/skills/spec-gate/scripts/classify-edit-class.bundle.mjs +0 -6577
- package/skills/spec-gate/scripts/classify-edit-class.mts +0 -414
- package/skills/spec-gate/vendor/gherkin-cli.d.mts +0 -2
- package/skills/spec-gate/vendor/gherkin-cli.mjs +0 -6545
- package/skills/touch-set-correction/scripts/touch-set-correction.bundle.mjs +0 -6655
- package/skills/touch-set-correction/scripts/touch-set-correction.mts +0 -429
- package/skills/verify-scenarios/scripts/verify-scenarios.bundle.mjs +0 -6537
- package/skills/verify-scenarios/scripts/verify-scenarios.mts +0 -470
|
@@ -1,12 +1,310 @@
|
|
|
1
|
-
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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.2","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.
|
|
12
|
-
- **Tests:** [`
|
|
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 `.
|
|
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
|