cyber-sdd 0.6.1 → 0.7.0

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 (126) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +2 -2
  3. package/.plugin/plugin.json +1 -1
  4. package/agents/sdd-spec-judge.md +3 -3
  5. package/agents/sdd-warden.md +8 -0
  6. package/dist/check-project-specs/scripts/check-project-specs.js +1 -0
  7. package/package.json +2 -2
  8. package/skills/align-spec/README.md +3 -3
  9. package/skills/align-spec/SKILL.md +1 -1
  10. package/skills/align-spec/scripts/align-spec.bundle.mjs +6753 -0
  11. package/skills/align-spec/scripts/align-spec.mjs +12 -0
  12. package/skills/architect-impl-governance/SKILL.md +3 -1
  13. package/skills/architect-spec-governance/SKILL.md +5 -1
  14. package/skills/blast-estimate/README.md +1 -1
  15. package/skills/blast-estimate/SKILL.md +1 -1
  16. package/skills/blast-estimate/scripts/blast-estimate.bundle.mjs +593 -0
  17. package/skills/blast-estimate/scripts/blast-estimate.mjs +12 -0
  18. package/skills/check-field-mandates/SKILL.md +1 -1
  19. package/skills/check-field-mandates/scripts/check-field-mandates.bundle.mjs +251 -0
  20. package/skills/check-field-mandates/scripts/check-field-mandates.mjs +12 -0
  21. package/skills/check-partition-quality/SKILL.md +1 -1
  22. package/skills/check-partition-quality/scripts/check-partition-quality.bundle.mjs +233 -0
  23. package/skills/check-partition-quality/scripts/check-partition-quality.mjs +12 -0
  24. package/skills/check-plan-safety/README.md +3 -3
  25. package/skills/check-plan-safety/SKILL.md +1 -1
  26. package/skills/check-plan-safety/scripts/check-plan-safety.bundle.mjs +107 -0
  27. package/skills/check-plan-safety/scripts/check-plan-safety.mjs +12 -0
  28. package/skills/check-plugin-manifests/SKILL.md +1 -1
  29. package/skills/check-plugin-manifests/scripts/check-plugin-manifests.bundle.mjs +248 -0
  30. package/skills/check-plugin-manifests/scripts/check-plugin-manifests.mjs +12 -0
  31. package/skills/check-project-specs/README.md +3 -3
  32. package/skills/check-project-specs/SKILL.md +2 -2
  33. package/skills/check-project-specs/scripts/check-project-specs.bundle.mjs +584 -0
  34. package/skills/check-project-specs/scripts/check-project-specs.mjs +12 -0
  35. package/skills/check-project-specs/scripts/check-project-specs.mts +1 -0
  36. package/skills/check-retired-terms/README.md +2 -2
  37. package/skills/check-retired-terms/SKILL.md +2 -2
  38. package/skills/check-retired-terms/scripts/check-retired-terms.bundle.mjs +184 -0
  39. package/skills/check-retired-terms/scripts/check-retired-terms.mjs +12 -0
  40. package/skills/check-scenario-overlap/README.md +2 -2
  41. package/skills/check-scenario-overlap/SKILL.md +1 -1
  42. package/skills/check-scenario-overlap/scripts/check-scenario-overlap.bundle.mjs +175 -0
  43. package/skills/check-scenario-overlap/scripts/check-scenario-overlap.mjs +12 -0
  44. package/skills/check-spec-references/README.md +1 -1
  45. package/skills/check-spec-references/SKILL.md +1 -1
  46. package/skills/check-spec-references/scripts/check-spec-references.bundle.mjs +285 -0
  47. package/skills/check-spec-references/scripts/check-spec-references.mjs +12 -0
  48. package/skills/check-spec-structure/README.md +2 -2
  49. package/skills/check-spec-structure/SKILL.md +1 -1
  50. package/skills/check-spec-structure/scripts/check-spec-structure.bundle.mjs +241 -0
  51. package/skills/check-spec-structure/scripts/check-spec-structure.mjs +12 -0
  52. package/skills/collision-ladder/SKILL.md +2 -2
  53. package/skills/collision-ladder/scripts/collision-ladder.bundle.mjs +7041 -0
  54. package/skills/collision-ladder/scripts/collision-ladder.mjs +12 -0
  55. package/skills/collision-ladder/scripts/collision-ladder.mts +4 -2
  56. package/skills/concept-index/README.md +2 -2
  57. package/skills/concept-index/SKILL.md +1 -1
  58. package/skills/concept-index/scripts/concept-index.bundle.mjs +181 -0
  59. package/skills/concept-index/scripts/concept-index.mjs +12 -0
  60. package/skills/discover-plans/README.md +2 -2
  61. package/skills/discover-plans/SKILL.md +1 -1
  62. package/skills/discover-plans/scripts/discover-plans.bundle.mjs +135 -0
  63. package/skills/discover-plans/scripts/discover-plans.mjs +12 -0
  64. package/skills/discover-specs/README.md +1 -1
  65. package/skills/discover-specs/SKILL.md +1 -1
  66. package/skills/discover-specs/scripts/discover-specs.bundle.mjs +319 -0
  67. package/skills/discover-specs/scripts/discover-specs.mjs +12 -0
  68. package/skills/doctrine-loop/SKILL.md +2 -2
  69. package/skills/formation-loop/SKILL.md +10 -0
  70. package/skills/gate-validation-governance/SKILL.md +1 -1
  71. package/skills/init/README.md +3 -3
  72. package/skills/init/SKILL.md +3 -3
  73. package/skills/init/scripts/wire-statusline.bundle.mjs +166 -0
  74. package/skills/init/scripts/wire-statusline.mjs +12 -0
  75. package/skills/manage-ignore/README.md +5 -5
  76. package/skills/manage-ignore/SKILL.md +1 -1
  77. package/skills/manage-ignore/scripts/manage-ignore.bundle.mjs +244 -0
  78. package/skills/manage-ignore/scripts/manage-ignore.mjs +12 -0
  79. package/skills/manage-scenario-bridge/README.md +3 -3
  80. package/skills/manage-scenario-bridge/SKILL.md +1 -1
  81. package/skills/manage-scenario-bridge/scripts/manage-scenario-bridge.bundle.mjs +237 -0
  82. package/skills/manage-scenario-bridge/scripts/manage-scenario-bridge.mjs +12 -0
  83. package/skills/manage-spec-anchors/README.md +4 -4
  84. package/skills/manage-spec-anchors/SKILL.md +1 -1
  85. package/skills/manage-spec-anchors/scripts/manage-spec-anchors.bundle.mjs +308 -0
  86. package/skills/manage-spec-anchors/scripts/manage-spec-anchors.mjs +12 -0
  87. package/skills/mission-graph/SKILL.md +9 -9
  88. package/skills/mission-graph/scripts/mission-graph.bundle.mjs +1038 -0
  89. package/skills/mission-graph/scripts/mission-graph.mjs +12 -0
  90. package/skills/pause-mission/SKILL.md +1 -1
  91. package/skills/place-node/README.md +1 -1
  92. package/skills/place-node/SKILL.md +1 -1
  93. package/skills/place-node/scripts/place-node.bundle.mjs +115 -0
  94. package/skills/place-node/scripts/place-node.mjs +12 -0
  95. package/skills/plan-retirement/SKILL.md +1 -1
  96. package/skills/plan-retirement/scripts/retire-plans.bundle.mjs +121 -0
  97. package/skills/plan-retirement/scripts/retire-plans.mjs +12 -0
  98. package/skills/plan-retirement/scripts/retire-plans.mts +6 -0
  99. package/skills/resolve-governances/README.md +1 -1
  100. package/skills/resolve-governances/SKILL.md +1 -1
  101. package/skills/resolve-governances/scripts/resolve-governances.bundle.mjs +350 -0
  102. package/skills/resolve-governances/scripts/resolve-governances.mjs +12 -0
  103. package/skills/resolve-tracking/SKILL.md +1 -1
  104. package/skills/resolve-tracking/scripts/resolve-tracking.bundle.mjs +150 -0
  105. package/skills/resolve-tracking/scripts/resolve-tracking.mjs +12 -0
  106. package/skills/scaffold-project-spec/SKILL.md +2 -2
  107. package/skills/sdd/SKILL.md +1 -1
  108. package/skills/spec-gate/SKILL.md +4 -4
  109. package/skills/spec-gate/scripts/check-spec-state.bundle.mjs +420 -0
  110. package/skills/spec-gate/scripts/check-spec-state.mjs +12 -0
  111. package/skills/spec-gate/scripts/check-suite.bundle.mjs +6498 -0
  112. package/skills/spec-gate/scripts/check-suite.mjs +12 -0
  113. package/skills/spec-gate/scripts/classify-edit-class.bundle.mjs +6577 -0
  114. package/skills/spec-gate/scripts/classify-edit-class.mjs +12 -0
  115. package/skills/spec-producer-governance/SKILL.md +2 -2
  116. package/skills/spec-structure-governance/README.md +4 -2
  117. package/skills/spec-structure-governance/SKILL.md +42 -7
  118. package/skills/start-mission/SKILL.md +1 -1
  119. package/skills/touch-set-correction/SKILL.md +1 -1
  120. package/skills/touch-set-correction/scripts/touch-set-correction.bundle.mjs +6655 -0
  121. package/skills/touch-set-correction/scripts/touch-set-correction.mjs +12 -0
  122. package/skills/touch-set-correction/scripts/touch-set-correction.mts +12 -3
  123. package/skills/verify-scenarios/README.md +3 -3
  124. package/skills/verify-scenarios/SKILL.md +2 -2
  125. package/skills/verify-scenarios/scripts/verify-scenarios.bundle.mjs +6537 -0
  126. package/skills/verify-scenarios/scripts/verify-scenarios.mjs +12 -0
@@ -0,0 +1,350 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, readFileSync, readdirSync, realpathSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { pathToFileURL } from "node:url";
5
+ //#region skills/resolve-governances/scripts/resolve-governances.mts
6
+ const ROLE_KEYS = [
7
+ "spec-producer",
8
+ "solution-producer",
9
+ "spec-judge",
10
+ "impl-producer",
11
+ "impl-judge"
12
+ ];
13
+ const BAR_KEYS = [
14
+ "oracle-spec",
15
+ "builder-spec",
16
+ "builder-impl",
17
+ "architect-spec",
18
+ "architect-impl"
19
+ ];
20
+ const ACTORS = [
21
+ "oracle",
22
+ "builder",
23
+ "architect"
24
+ ];
25
+ const GATES = ["spec", "impl"];
26
+ const ROLE_LOADOUT = {
27
+ "spec-producer": { bars: ["oracle-spec", "builder-spec"] },
28
+ "solution-producer": { bars: ["architect-spec"] },
29
+ "spec-judge": { bars: [
30
+ "oracle-spec",
31
+ "builder-spec",
32
+ "architect-spec"
33
+ ] },
34
+ "impl-producer": { bars: ["builder-impl", "architect-impl"] },
35
+ "impl-judge": { bars: ["builder-impl", "architect-impl"] }
36
+ };
37
+ const SDD_DEFAULT_AGENT = {
38
+ "spec-producer": null,
39
+ "solution-producer": null,
40
+ "spec-judge": "sdd-spec-judge",
41
+ "impl-producer": null,
42
+ "impl-judge": "sdd-impl-judge"
43
+ };
44
+ function migrateRoles(rolesIn) {
45
+ const roles = { ...rolesIn ?? {} };
46
+ if ("plan-producer" in roles) {
47
+ const legacy = roles["plan-producer"];
48
+ if (!("solution-producer" in roles)) roles["solution-producer"] = legacy;
49
+ delete roles["plan-producer"];
50
+ }
51
+ return roles;
52
+ }
53
+ function migrateGovernances(gIn) {
54
+ const g = gIn ?? {};
55
+ return "oracle" in g || "builder" in g || "architect" in g ? {
56
+ "oracle-spec": g.oracle ?? null,
57
+ "builder-spec": g.builder ?? null,
58
+ "builder-impl": g.builder ?? null,
59
+ "architect-spec": g.architect ?? null,
60
+ "architect-impl": g.architect ?? null
61
+ } : { ...g };
62
+ }
63
+ function migrateSquad(sq) {
64
+ const types = sq["artifact-types"] ?? sq.domains ?? [];
65
+ return {
66
+ "artifact-types": Array.isArray(types) ? types : [],
67
+ roles: migrateRoles(sq.roles),
68
+ governances: migrateGovernances(sq.governances)
69
+ };
70
+ }
71
+ function migrateEntry(entry) {
72
+ const squads = Array.isArray(entry.squads) ? entry.squads.map(migrateSquad) : [migrateSquad({
73
+ "artifact-types": entry.domains,
74
+ roles: entry.roles,
75
+ governances: entry.governances
76
+ })];
77
+ return {
78
+ name: entry.name,
79
+ version: entry.version,
80
+ squads
81
+ };
82
+ }
83
+ function parseRegistry(text) {
84
+ let raw;
85
+ try {
86
+ raw = JSON.parse(text);
87
+ } catch (e) {
88
+ throw new Error(`registry is not valid JSON: ${e.message}`);
89
+ }
90
+ if (!raw || typeof raw !== "object" || !Array.isArray(raw["sdd-plugins"])) throw new Error("registry has no sdd-plugins array");
91
+ return { "sdd-plugins": raw["sdd-plugins"].map(migrateEntry) };
92
+ }
93
+ function loadRegistry(root) {
94
+ const path = join(root, ".agents", "universal-plugin.json");
95
+ if (!existsSync(path)) return { "sdd-plugins": [] };
96
+ return parseRegistry(readFileSync(path, "utf8"));
97
+ }
98
+ function parseArtifactTypeMap(text) {
99
+ const out = [];
100
+ for (const raw of text.split("\n")) {
101
+ const line = raw.trim();
102
+ if (!line || line.startsWith("#") || line.startsWith("[")) continue;
103
+ const m = /^(?:"([^"]+)"|'([^']+)'|(\S+))\s*=\s*(?:"([^"]+)"|'([^']+)')\s*(?:#.*)?$/.exec(line);
104
+ if (!m) continue;
105
+ const glob = m[1] ?? m[2] ?? m[3];
106
+ const type = m[4] ?? m[5];
107
+ if (glob && type) out.push({
108
+ glob,
109
+ type
110
+ });
111
+ }
112
+ return out;
113
+ }
114
+ function globToRegExp(glob) {
115
+ const re = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*+/g, (m) => m.length > 1 ? ".*" : "[^/]*").replace(/\?/g, ".");
116
+ return new RegExp(`^${re}$`);
117
+ }
118
+ function resolveArtifactTypeFromMap(map, path) {
119
+ const matches = map.filter((b) => globToRegExp(b.glob).test(path));
120
+ if (matches.length === 0) return null;
121
+ const literal = (g) => g.replace(/[*?]/g, "").length;
122
+ matches.sort((a, b) => literal(b.glob) - literal(a.glob) || b.glob.length - a.glob.length);
123
+ return matches[0].type;
124
+ }
125
+ function loadArtifactTypeMap(root) {
126
+ const path = join(root, ".agents", "sdd", "artifact-types.toml");
127
+ if (!existsSync(path)) return [];
128
+ return parseArtifactTypeMap(readFileSync(path, "utf8"));
129
+ }
130
+ function frontmatter(text) {
131
+ const m = /^---\n([\s\S]*?)\n---/.exec(text);
132
+ return m ? m[1].split("\n") : [];
133
+ }
134
+ const unquote = (s) => s.trim().replace(/^["']|["']$/g, "");
135
+ function parseGovernanceFrontmatter(text) {
136
+ const lines = frontmatter(text);
137
+ let inMeta = false;
138
+ let artifactType = null;
139
+ let actor = null;
140
+ let gate = null;
141
+ let compose = "union";
142
+ for (const line of lines) {
143
+ if (/^metadata:\s*$/.test(line)) {
144
+ inMeta = true;
145
+ continue;
146
+ }
147
+ if (inMeta && /^\S/.test(line)) inMeta = false;
148
+ if (!inMeta) continue;
149
+ const at = /^\s+artifact-type:\s*(.+)$/.exec(line);
150
+ if (at) artifactType = unquote(at[1]);
151
+ const ac = /^\s+actor:\s*(.+)$/.exec(line);
152
+ if (ac) actor = unquote(ac[1]);
153
+ const ga = /^\s+gate:\s*(.+)$/.exec(line);
154
+ if (ga) gate = unquote(ga[1]);
155
+ const co = /^\s+compose:\s*(.+)$/.exec(line);
156
+ if (co) compose = unquote(co[1]) === "replace" ? "replace" : "union";
157
+ }
158
+ if (!actor || !gate) return null;
159
+ return {
160
+ artifactType,
161
+ actor,
162
+ gate,
163
+ compose
164
+ };
165
+ }
166
+ function collectAnchorGovernances(anchors) {
167
+ const out = [];
168
+ for (const { tier, root } of anchors) {
169
+ const dir = join(root, ".agents", "governances");
170
+ let entries;
171
+ try {
172
+ entries = readdirSync(dir, { withFileTypes: true });
173
+ } catch {
174
+ continue;
175
+ }
176
+ for (const e of entries) {
177
+ if (!e.isFile() || !e.name.endsWith(".md")) continue;
178
+ const meta = parseGovernanceFrontmatter(readFileSync(join(dir, e.name), "utf8"));
179
+ if (meta) out.push({
180
+ ...meta,
181
+ tier,
182
+ path: join(root, ".agents", "governances", e.name)
183
+ });
184
+ }
185
+ }
186
+ return out;
187
+ }
188
+ function matchSquad(registry, artifactType) {
189
+ if (!artifactType) return {
190
+ match: null,
191
+ ambiguous: []
192
+ };
193
+ const matches = [];
194
+ for (const p of registry["sdd-plugins"]) for (const sq of p.squads ?? []) if (sq["artifact-types"]?.includes(artifactType)) matches.push({
195
+ plugin: p.name,
196
+ squad: sq
197
+ });
198
+ if (matches.length === 0) return {
199
+ match: null,
200
+ ambiguous: []
201
+ };
202
+ if (matches.length === 1) return {
203
+ match: matches[0],
204
+ ambiguous: []
205
+ };
206
+ return {
207
+ match: null,
208
+ ambiguous: [...new Set(matches.map((m) => m.plugin))]
209
+ };
210
+ }
211
+ function matchBar(artifactType, actor, gate, ctx) {
212
+ const key = `${actor}-${gate}`;
213
+ const bucket = (tier) => ctx.projectGovs.filter((c) => c.tier === tier && c.actor === actor && c.gate === gate).filter((c) => c.artifactType === artifactType || c.artifactType === null).map((c) => c.path);
214
+ const pluginBar = ctx.match?.squad.governances?.[key];
215
+ return {
216
+ key,
217
+ candidates: {
218
+ project: bucket("project"),
219
+ "project-root": bucket("project-root"),
220
+ plugin: ctx.match && pluginBar ? `${ctx.match.plugin}:${pluginBar}` : null,
221
+ sdd: `sdd:${key}-governance`
222
+ }
223
+ };
224
+ }
225
+ function resolveAgent(role, match) {
226
+ if (!match) return {
227
+ source: "sdd",
228
+ ref: SDD_DEFAULT_AGENT[role]
229
+ };
230
+ if (role in match.squad.roles) {
231
+ const named = match.squad.roles[role];
232
+ if (named) return {
233
+ source: "plugin",
234
+ ref: named
235
+ };
236
+ return {
237
+ source: "sdd",
238
+ ref: SDD_DEFAULT_AGENT[role]
239
+ };
240
+ }
241
+ return {
242
+ source: "plugin",
243
+ ref: `${match.plugin}-${role}`
244
+ };
245
+ }
246
+ function resolveRole(role, artifactType, ctx) {
247
+ const bars = ROLE_LOADOUT[role].bars.map((b) => {
248
+ const [actor, gate] = b.split("-");
249
+ return matchBar(artifactType, actor, gate, ctx);
250
+ });
251
+ return {
252
+ role,
253
+ agent: resolveAgent(role, ctx.match),
254
+ bars
255
+ };
256
+ }
257
+ function buildLoadPlan(artifactType, registry, projectGovs) {
258
+ const { match, ambiguous } = matchSquad(registry, artifactType);
259
+ if (ambiguous.length > 0) return {
260
+ artifactType,
261
+ status: "needs-input",
262
+ plugin: null,
263
+ ambiguous,
264
+ roles: []
265
+ };
266
+ const ctx = {
267
+ match,
268
+ projectGovs
269
+ };
270
+ return {
271
+ artifactType,
272
+ status: "complete",
273
+ plugin: match?.plugin ?? null,
274
+ ambiguous: [],
275
+ roles: ROLE_KEYS.map((r) => resolveRole(r, artifactType, ctx))
276
+ };
277
+ }
278
+ function validateRegistry(registry) {
279
+ const v = [];
280
+ const typeOwners = /* @__PURE__ */ new Map();
281
+ for (const entry of registry["sdd-plugins"]) {
282
+ const where = entry.name || "<unnamed plugin>";
283
+ if (!entry.name) v.push("an sdd-plugins entry has no name");
284
+ if (!Array.isArray(entry.squads)) {
285
+ v.push(`${where}: squads is not an array`);
286
+ continue;
287
+ }
288
+ const seenTypes = /* @__PURE__ */ new Set();
289
+ for (const sq of entry.squads) {
290
+ if (!Array.isArray(sq["artifact-types"])) v.push(`${where}: a squad's artifact-types is not an array`);
291
+ for (const r of Object.keys(sq.roles ?? {})) if (!ROLE_KEYS.includes(r)) v.push(`${where}: unknown role key "${r}"`);
292
+ for (const k of Object.keys(sq.governances ?? {})) if (!BAR_KEYS.includes(k)) v.push(`${where}: unknown governance key "${k}"`);
293
+ for (const t of sq["artifact-types"] ?? []) {
294
+ if (seenTypes.has(t)) v.push(`${where}: artifact-type "${t}" appears in more than one squad`);
295
+ seenTypes.add(t);
296
+ }
297
+ }
298
+ for (const t of seenTypes) typeOwners.set(t, [...typeOwners.get(t) ?? [], where]);
299
+ }
300
+ for (const [t, owners] of typeOwners) if (owners.length > 1) v.push(`artifact-type "${t}" is claimed by ${owners.length} plugins (${owners.join(", ")}) — ambiguous`);
301
+ return v;
302
+ }
303
+ function main(argv) {
304
+ const root = argv.includes("--root") ? argv[argv.indexOf("--root") + 1] : ".";
305
+ const explicitType = argv.includes("--artifact-type") ? argv[argv.indexOf("--artifact-type") + 1] : null;
306
+ const pathArg = argv.includes("--path") ? argv[argv.indexOf("--path") + 1] : null;
307
+ const projectArg = argv.includes("--project") ? argv[argv.indexOf("--project") + 1] : root;
308
+ const projectRootArg = argv.includes("--project-root") ? argv[argv.indexOf("--project-root") + 1] : null;
309
+ const anchors = [{
310
+ tier: "project",
311
+ root: projectArg
312
+ }];
313
+ if (projectRootArg) anchors.push({
314
+ tier: "project-root",
315
+ root: projectRootArg
316
+ });
317
+ let artifactType = explicitType;
318
+ if (!artifactType && pathArg) artifactType = resolveArtifactTypeFromMap(loadArtifactTypeMap(root), pathArg);
319
+ let registry;
320
+ try {
321
+ registry = loadRegistry(root);
322
+ } catch (e) {
323
+ console.error(`✗ ${e.message}`);
324
+ return 1;
325
+ }
326
+ if (!artifactType && pathArg) {
327
+ process.stdout.write(`${JSON.stringify({
328
+ path: pathArg,
329
+ artifactType: null,
330
+ note: "no tiebreaker match — classify by convention"
331
+ }, null, 2)}\n`);
332
+ return 0;
333
+ }
334
+ if (!artifactType) {
335
+ const violations = validateRegistry(registry);
336
+ if (violations.length) {
337
+ for (const line of violations) console.error(`✗ ${line}`);
338
+ return 1;
339
+ }
340
+ process.stdout.write("governance registry OK\n");
341
+ return 0;
342
+ }
343
+ const plan = buildLoadPlan(artifactType, registry, collectAnchorGovernances(anchors));
344
+ process.stdout.write(`${JSON.stringify(plan, null, 2)}\n`);
345
+ if (plan.status === "needs-input") console.error(`needs-input: artifact-type "${artifactType}" is claimed by ${plan.ambiguous.join(", ")}`);
346
+ return 0;
347
+ }
348
+ if (process.argv[1] && import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href) process.exit(main(process.argv.slice(2)));
349
+ //#endregion
350
+ export { ACTORS, BAR_KEYS, GATES, ROLE_KEYS, ROLE_LOADOUT, SDD_DEFAULT_AGENT, buildLoadPlan, collectAnchorGovernances, loadArtifactTypeMap, loadRegistry, main, matchBar, matchSquad, migrateEntry, parseArtifactTypeMap, parseGovernanceFrontmatter, parseRegistry, resolveAgent, resolveArtifactTypeFromMap, resolveRole, validateRegistry };
@@ -0,0 +1,12 @@
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
@@ -18,7 +18,7 @@ It carries a self-contained `.mts` script (the repo's node-≥23.6 / no-deps con
18
18
  ## Run the resolution
19
19
 
20
20
  ```bash
21
- node "<skill>/scripts/resolve-tracking.mts" --root . --path <repo-relative-path> [--artifact-type <type>] [--explicit tracked|ignored]
21
+ node "<skill>/scripts/resolve-tracking.mjs" --root . --path <repo-relative-path> [--artifact-type <type>] [--explicit tracked|ignored]
22
22
  ```
23
23
 
24
24
  - `--root` is the repo root (default `.`) — where `.agents/sdd/.sddignore` is read from.
@@ -0,0 +1,150 @@
1
+ import { existsSync, readFileSync, realpathSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { pathToFileURL } from "node:url";
4
+ //#region skills/resolve-tracking/scripts/resolve-tracking.mts
5
+ function globToRegExpBody(glob) {
6
+ return glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*+/g, (m) => m.length > 1 ? ".*" : "[^/]*").replace(/\?/g, "[^/]");
7
+ }
8
+ function parseIgnoreLine(raw) {
9
+ const trimmed = raw.replace(/\r$/, "").trim();
10
+ if (!trimmed || trimmed.startsWith("#")) return "skip";
11
+ let body = trimmed;
12
+ const negated = body.startsWith("!");
13
+ if (negated) body = body.slice(1);
14
+ if (body.endsWith("/")) body = body.slice(0, -1);
15
+ let anchored = false;
16
+ if (body.startsWith("/")) {
17
+ anchored = true;
18
+ body = body.slice(1);
19
+ } else if (body.includes("/")) anchored = true;
20
+ if (body === "") return null;
21
+ const prefix = anchored ? "^" : "^(?:.*/)?";
22
+ const core = globToRegExpBody(body);
23
+ const suffix = "(?:/.*)?$";
24
+ try {
25
+ return {
26
+ negated,
27
+ regex: new RegExp(prefix + core + suffix),
28
+ source: trimmed
29
+ };
30
+ } catch {
31
+ return null;
32
+ }
33
+ }
34
+ function parseIgnoreFile(text) {
35
+ const out = [];
36
+ for (const raw of text.split("\n")) {
37
+ const rule = parseIgnoreLine(raw);
38
+ if (rule && rule !== "skip") out.push(rule);
39
+ }
40
+ return out;
41
+ }
42
+ function loadIgnoreRules(root) {
43
+ const path = join(root, ".agents", "sdd", ".sddignore");
44
+ if (!existsSync(path)) return [];
45
+ return parseIgnoreFile(readFileSync(path, "utf8"));
46
+ }
47
+ function resolveFromIgnore(rules, path) {
48
+ let value = null;
49
+ for (const rule of rules) if (rule.regex.test(path)) value = rule.negated ? "tracked" : "ignored";
50
+ return value;
51
+ }
52
+ const KIND_DEFAULT_GLOBS = {
53
+ skill: {
54
+ ignored: [".agents/skills/**"],
55
+ tracked: [
56
+ "skills/**",
57
+ "plugins/*/skills/**",
58
+ "packages/*/skills/**"
59
+ ]
60
+ },
61
+ subagent: {
62
+ ignored: [".agents/agents/**"],
63
+ tracked: ["plugins/*/agents/**", "packages/*/agents/**"]
64
+ },
65
+ command: {
66
+ ignored: [".agents/commands/**"],
67
+ tracked: ["plugins/*/commands/**", "packages/*/commands/**"]
68
+ }
69
+ };
70
+ function globMatch(glob, path) {
71
+ return new RegExp(`^${globToRegExpBody(glob)}$`).test(path);
72
+ }
73
+ function resolveKindDefault(artifactType, path) {
74
+ if (!artifactType) return null;
75
+ const kind = KIND_DEFAULT_GLOBS[artifactType];
76
+ if (!kind) return null;
77
+ if (kind.ignored.some((g) => globMatch(g, path))) return "ignored";
78
+ if (kind.tracked.some((g) => globMatch(g, path))) return "tracked";
79
+ return null;
80
+ }
81
+ function resolveTracking(input) {
82
+ if (input.explicit) return {
83
+ value: input.explicit,
84
+ reason: "explicit override"
85
+ };
86
+ const ignoreMatch = resolveFromIgnore(loadIgnoreRules(input.root), input.path);
87
+ if (ignoreMatch) return {
88
+ value: ignoreMatch,
89
+ reason: ".sddignore"
90
+ };
91
+ const kindDefault = resolveKindDefault(input.artifactType, input.path);
92
+ if (kindDefault) return {
93
+ value: kindDefault,
94
+ reason: `kind-default (${input.artifactType})`
95
+ };
96
+ return {
97
+ value: "tracked",
98
+ reason: "fail-closed (no signal)"
99
+ };
100
+ }
101
+ function validateIgnoreFile(root) {
102
+ const path = join(root, ".agents", "sdd", ".sddignore");
103
+ if (!existsSync(path)) return {
104
+ ok: true,
105
+ message: ".sddignore OK (absent, fine)"
106
+ };
107
+ const text = readFileSync(path, "utf8");
108
+ const notes = [];
109
+ text.split("\n").forEach((raw, i) => {
110
+ if (parseIgnoreLine(raw) === null) notes.push(`line ${i + 1}: not a valid gitignore pattern: ${raw.trim()}`);
111
+ });
112
+ if (notes.length > 0) return {
113
+ ok: false,
114
+ message: notes.join("\n")
115
+ };
116
+ return {
117
+ ok: true,
118
+ message: `.sddignore OK (${parseIgnoreFile(text).length} rule(s))`
119
+ };
120
+ }
121
+ function parseArgs(argv) {
122
+ const out = {};
123
+ for (let i = 0; i < argv.length; i++) if (argv[i].startsWith("--")) {
124
+ out[argv[i].slice(2)] = argv[i + 1];
125
+ i++;
126
+ }
127
+ return out;
128
+ }
129
+ function main(argv) {
130
+ const args = parseArgs(argv);
131
+ const root = args.root ?? ".";
132
+ if (!args.path) {
133
+ const result = validateIgnoreFile(root);
134
+ process.stdout.write(`${result.message}\n`);
135
+ if (!result.ok) process.exitCode = 1;
136
+ return;
137
+ }
138
+ const explicit = args.explicit === "tracked" || args.explicit === "ignored" ? args.explicit : void 0;
139
+ const result = resolveTracking({
140
+ root,
141
+ path: args.path,
142
+ artifactType: args["artifact-type"],
143
+ explicit
144
+ });
145
+ process.stdout.write(`${result.value}\n`);
146
+ process.stdout.write(`reason: ${result.reason}\n`);
147
+ }
148
+ if (process.argv[1] && import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href) main(process.argv.slice(2));
149
+ //#endregion
150
+ export { loadIgnoreRules, main, parseIgnoreFile, parseIgnoreLine, resolveFromIgnore, resolveKindDefault, resolveTracking, validateIgnoreFile };
@@ -0,0 +1,12 @@
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
@@ -13,7 +13,7 @@ homes. It is **internal** — reached through `start-mission`, never a user entr
13
13
  `status: draft`; it authors no node's `## Use Cases`/`.feature`, renders no gate verdict, and freezes nothing.
14
14
 
15
15
  **Load `sdd:spec-structure-governance`** for the layout law it applies: the node taxonomy (three
16
- spec-types, the concept axis), the two-level depth cap, screaming architecture and the
16
+ spec-types, the concept axis), the two-level depth cap on capability nodes, screaming architecture and the
17
17
  non-capability folders, root-files-not-folders, and the colocate-or-hoist test. This skill does not
18
18
  restate that law — it **runs** it, and owns only what is genuinely its own: the **strategy menu**,
19
19
  the **shared envelope**, and the seven-step procedure below.
@@ -138,7 +138,7 @@ Then the chosen strategy's **top-level skeleton** of **stub node READMEs**, each
138
138
  `.feature`);
139
139
  - an **index / rule / structural grouping** → **descriptive** (no marker).
140
140
 
141
- The skeleton obeys the **two-level depth cap** under **every** strategy: a node is
141
+ The skeleton obeys the **two-level depth cap** under **every** strategy: a capability node is
142
142
  `<capability>/<unit>` and **never three deep** — a sub-grouping inside a capability is a `concept:` tag
143
143
  recovered by the by-concept index, not a third folder level. Under **mirror-source**, mirror **only to the
144
144
  unit boundary**: a folder with a testable surface becomes one behavioral leaf that owns its subtree; create
@@ -85,7 +85,7 @@ The **fresh spawn per mission is deliberate** — each automaton's context dies
85
85
  - **Escape.** A task that is **not a CR** escapes: create no draft, invoke no gate, and **write no record** (a non-CR is not SDD's to track; a spec-prose-only change is already in git). Two independent triggers: **no suite-relevant behavior**, or an **ignored** touched artifact. Escaping does not mean stopping — if the artifact-type has a producer with an escaped-request entry point (e.g. `define-skill` for `skill`), invoke it directly to do the actual work; only state "leaving the lifecycle" and stop when no such producer exists. For tracking, run the `resolve-tracking` skill (`intake/resolve-tracking`'s concrete engine) per touched artifact:
86
86
 
87
87
  ```bash
88
- node "<resolve-tracking skill>/scripts/resolve-tracking.mts" --root . --path <repo-relative-path> [--artifact-type <type>] [--explicit tracked|ignored]
88
+ node "<resolve-tracking skill>/scripts/resolve-tracking.mjs" --root . --path <repo-relative-path> [--artifact-type <type>] [--explicit tracked|ignored]
89
89
  ```
90
90
 
91
91
  `--explicit` carries a tracking statement the requester made directly in the prompt/CR, when present. Pass `--artifact-type` when convention already makes it obvious (`skill` under `skills/`, `subagent` under `agents/`, …) — a full `resolve-governances` classification is not needed just to gate the escape check. An `ignored` result escapes the same as no-suite-relevant-behavior; a `tracked` result (including the fail-closed default) proceeds toward CR classification. This is a **read**, the same category as counting strategy lines or scanning specs — the thin-classifier rule still holds. Recognition is the **grill + impact analysis**, not a gateway classifier — the grill may carve a CR out of the tracked parts of a mixed request and escape the ignored ones. Ambiguity defaults *into* the lifecycle and is decided during explore.
@@ -26,7 +26,7 @@ self-clear-vs-escalate bar and the four-C floor are the conductor's autonomy bar
26
26
  Run before any verdict work; structural validity **fails closed**, availability only **flags**:
27
27
 
28
28
  ```bash
29
- node "<skill>/scripts/check-spec-state.mts" [--root <specs-dir>]
29
+ node "<skill>/scripts/check-spec-state.mjs" [--root <specs-dir>]
30
30
  ```
31
31
 
32
32
  Exit `0` = legal; exit `1` prints each violation as `✗ <slug>: <reason>` — fix before continuing.
@@ -41,7 +41,7 @@ the CR touches any `.feature`, run it here **fail-closed, before spawning the co
41
41
  the CR's touched files:
42
42
 
43
43
  ```bash
44
- node "<skill>/scripts/check-suite.mts" --files <the CR's touched .feature files>
44
+ node "<skill>/scripts/check-suite.mjs" --files <the CR's touched .feature files>
45
45
  ```
46
46
 
47
47
  Exit `0` = form clean; exit `1` prints each `✗ <file>: <reason>` — **advance nothing and do not
@@ -67,7 +67,7 @@ or a nested `<project-path>/.agents/spec/` — so a touched `.md` outside the sp
67
67
  swept):
68
68
 
69
69
  ```bash
70
- node "<skill>/scripts/check-spec-state.mts" --files <the CR's touched .md files> [--base <baseref>]
70
+ node "<skill>/scripts/check-spec-state.mjs" --files <the CR's touched .md files> [--base <baseref>]
71
71
  ```
72
72
 
73
73
  Exit `0` prints each `⚠ <file>: introduces unresolved reference ...` finding (if any) plus the
@@ -166,7 +166,7 @@ Clearance path above; it fires no verdict itself. Run it over the CR's touched f
166
166
  to read the edit class before applying the verb:
167
167
 
168
168
  ```bash
169
- node "<skill>/scripts/classify-edit-class.mts" --files <the CR's touched .feature files> [--base <baseref>]
169
+ node "<skill>/scripts/classify-edit-class.mjs" --files <the CR's touched .feature files> [--base <baseref>]
170
170
  ```
171
171
 
172
172
  A `narrowing`/`mixed` result on a still-`@frozen` file routes to **Clearance** (escalated unless the CR