@sdd-method/author-cli 0.0.0-stage → 0.1.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 (203) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +17 -0
  3. package/README.md +6 -2
  4. package/dist/author-bin.js +8 -0
  5. package/dist/author-program.js +99 -0
  6. package/dist/lib/adr-origin.js +59 -0
  7. package/dist/lib/agent-harness.js +37 -0
  8. package/dist/lib/api-surface/__fixtures__/serverless-repo/serverless/serverless.yml +18 -0
  9. package/dist/lib/api-surface/analyzers/library.js +95 -0
  10. package/dist/lib/api-surface/analyzers/serverless.js +336 -0
  11. package/dist/lib/api-surface/analyzers/spring.js +237 -0
  12. package/dist/lib/api-surface/analyzers/terraform.js +109 -0
  13. package/dist/lib/api-surface/detect.js +25 -0
  14. package/dist/lib/api-surface/emit.js +109 -0
  15. package/dist/lib/api-surface/file-set.js +43 -0
  16. package/dist/lib/api-surface/index.js +134 -0
  17. package/dist/lib/api-surface/types.js +17 -0
  18. package/dist/lib/briefing/index.js +13 -0
  19. package/dist/lib/briefing/render.js +132 -0
  20. package/dist/lib/briefing/variables.js +92 -0
  21. package/dist/lib/catalogue/application-archetype/vocabulary.js +177 -0
  22. package/dist/lib/catalogue/archetype/maps/android.yaml +66 -0
  23. package/dist/lib/catalogue/archetype/maps/swiftui.yaml +58 -0
  24. package/dist/lib/catalogue/archetype/maps/uikit.yaml +48 -0
  25. package/dist/lib/catalogue/archetype/maps/web-generic.yaml +86 -0
  26. package/dist/lib/catalogue/archetype/openui-archetype-vocabulary.yaml +46 -0
  27. package/dist/lib/catalogue/archetype/resolver.js +103 -0
  28. package/dist/lib/catalogue/archetype/wireframe.js +418 -0
  29. package/dist/lib/catalogue/baseline-check.js +151 -0
  30. package/dist/lib/catalogue/build.js +1502 -0
  31. package/dist/lib/catalogue/builders/adr-supersedes.js +103 -0
  32. package/dist/lib/catalogue/builders/adrs.js +222 -0
  33. package/dist/lib/catalogue/builders/application-conformance-coverage.js +47 -0
  34. package/dist/lib/catalogue/builders/application-data-plane-bindings.js +115 -0
  35. package/dist/lib/catalogue/builders/c4-components.js +229 -0
  36. package/dist/lib/catalogue/builders/c4-containers.js +240 -0
  37. package/dist/lib/catalogue/builders/c4-entity-rollups.js +87 -0
  38. package/dist/lib/catalogue/builders/c4-external-systems.js +194 -0
  39. package/dist/lib/catalogue/builders/c4-l1-candidates.js +107 -0
  40. package/dist/lib/catalogue/builders/c4-systems.js +98 -0
  41. package/dist/lib/catalogue/builders/capabilities.js +488 -0
  42. package/dist/lib/catalogue/builders/capability-domain-model.js +74 -0
  43. package/dist/lib/catalogue/builders/capability-edges.js +256 -0
  44. package/dist/lib/catalogue/builders/capability-spec-walker.js +99 -0
  45. package/dist/lib/catalogue/builders/capability-spec.js +348 -0
  46. package/dist/lib/catalogue/builders/container-dependency.js +183 -0
  47. package/dist/lib/catalogue/builders/contracts.js +402 -0
  48. package/dist/lib/catalogue/builders/data-items.js +344 -0
  49. package/dist/lib/catalogue/builders/data-store-mapping.js +113 -0
  50. package/dist/lib/catalogue/builders/domain-models.js +175 -0
  51. package/dist/lib/catalogue/builders/domains.js +119 -0
  52. package/dist/lib/catalogue/builders/edge-label-merge.js +19 -0
  53. package/dist/lib/catalogue/builders/entities-manifest.js +160 -0
  54. package/dist/lib/catalogue/builders/entities-mermaid.js +189 -0
  55. package/dist/lib/catalogue/builders/entity-persistence.js +102 -0
  56. package/dist/lib/catalogue/builders/entity-slug.js +47 -0
  57. package/dist/lib/catalogue/builders/event-catalog-walker.js +186 -0
  58. package/dist/lib/catalogue/builders/event-edges.js +327 -0
  59. package/dist/lib/catalogue/builders/events.js +168 -0
  60. package/dist/lib/catalogue/builders/evidence-class.js +43 -0
  61. package/dist/lib/catalogue/builders/evidence-target-repo.js +67 -0
  62. package/dist/lib/catalogue/builders/external-system-exemption.js +120 -0
  63. package/dist/lib/catalogue/builders/feature-contract.js +77 -0
  64. package/dist/lib/catalogue/builders/feature-domain-model.js +50 -0
  65. package/dist/lib/catalogue/builders/feature-grounding-walker.js +254 -0
  66. package/dist/lib/catalogue/builders/feature-grounding.js +391 -0
  67. package/dist/lib/catalogue/builders/feature-groups.js +63 -0
  68. package/dist/lib/catalogue/builders/feature-subscription-channels.js +228 -0
  69. package/dist/lib/catalogue/builders/feature-tree-walker.js +295 -0
  70. package/dist/lib/catalogue/builders/features.js +300 -0
  71. package/dist/lib/catalogue/builders/glossary-xref.js +378 -0
  72. package/dist/lib/catalogue/builders/glossary.js +204 -0
  73. package/dist/lib/catalogue/builders/implementations.js +214 -0
  74. package/dist/lib/catalogue/builders/index.js +64 -0
  75. package/dist/lib/catalogue/builders/integration-components.js +344 -0
  76. package/dist/lib/catalogue/builders/integrations.js +181 -0
  77. package/dist/lib/catalogue/builders/persona-c4system.js +149 -0
  78. package/dist/lib/catalogue/builders/persona-container.js +133 -0
  79. package/dist/lib/catalogue/builders/persona-journey-edges.js +133 -0
  80. package/dist/lib/catalogue/builders/personas.js +152 -0
  81. package/dist/lib/catalogue/builders/product-edges.js +106 -0
  82. package/dist/lib/catalogue/builders/product-platform-dependencies.js +405 -0
  83. package/dist/lib/catalogue/builders/products.js +162 -0
  84. package/dist/lib/catalogue/builders/quality-scores.js +635 -0
  85. package/dist/lib/catalogue/builders/repo-capability-exemption.js +61 -0
  86. package/dist/lib/catalogue/builders/repo-dependency.js +127 -0
  87. package/dist/lib/catalogue/builders/repo-edges.js +340 -0
  88. package/dist/lib/catalogue/builders/repositories.js +458 -0
  89. package/dist/lib/catalogue/builders/scaffolds.js +197 -0
  90. package/dist/lib/catalogue/builders/screen-tree.js +222 -0
  91. package/dist/lib/catalogue/builders/screens.js +396 -0
  92. package/dist/lib/catalogue/builders/service-capability-exemption.js +63 -0
  93. package/dist/lib/catalogue/builders/service-contract-consumes.js +186 -0
  94. package/dist/lib/catalogue/builders/service-contract.js +74 -0
  95. package/dist/lib/catalogue/builders/service-entity-tables.js +187 -0
  96. package/dist/lib/catalogue/builders/services.js +232 -0
  97. package/dist/lib/catalogue/builders/system-external-dependency.js +115 -0
  98. package/dist/lib/catalogue/builders/user-journeys.js +253 -0
  99. package/dist/lib/catalogue/canonical-schema.yaml +4117 -0
  100. package/dist/lib/catalogue/cli-version.js +10 -0
  101. package/dist/lib/catalogue/contract-id.js +49 -0
  102. package/dist/lib/catalogue/coverage-predicates.js +603 -0
  103. package/dist/lib/catalogue/coverage-render.js +552 -0
  104. package/dist/lib/catalogue/coverage-run.js +115 -0
  105. package/dist/lib/catalogue/csv-reader.js +97 -0
  106. package/dist/lib/catalogue/csv-writer.js +96 -0
  107. package/dist/lib/catalogue/data-plane-coherence.js +77 -0
  108. package/dist/lib/catalogue/ddl-generator.js +233 -0
  109. package/dist/lib/catalogue/dedupe-keyed-tables.js +0 -0
  110. package/dist/lib/catalogue/engine-validator.js +239 -0
  111. package/dist/lib/catalogue/ext-id.js +76 -0
  112. package/dist/lib/catalogue/feature-grounding-aliases-loader.js +65 -0
  113. package/dist/lib/catalogue/feature-repo-zero-resolution-summary.js +31 -0
  114. package/dist/lib/catalogue/finding-identity.js +183 -0
  115. package/dist/lib/catalogue/findings.js +46 -0
  116. package/dist/lib/catalogue/fk-validator.js +136 -0
  117. package/dist/lib/catalogue/freshness.js +70 -0
  118. package/dist/lib/catalogue/gate-checks.js +338 -0
  119. package/dist/lib/catalogue/graph-projection.js +346 -0
  120. package/dist/lib/catalogue/impact-run.js +228 -0
  121. package/dist/lib/catalogue/manifest-loader.js +435 -0
  122. package/dist/lib/catalogue/platform-scope.js +7 -0
  123. package/dist/lib/catalogue/population-checks.js +87 -0
  124. package/dist/lib/catalogue/relationship-semantics.js +66 -0
  125. package/dist/lib/catalogue/reverse-product-quality.js +99 -0
  126. package/dist/lib/catalogue/rubric-coverage-map.yaml +871 -0
  127. package/dist/lib/catalogue/runway-vocabulary-loader.js +56 -0
  128. package/dist/lib/catalogue/scaffold-delta.js +261 -0
  129. package/dist/lib/catalogue/schema-compat.js +52 -0
  130. package/dist/lib/catalogue/schema-loader.js +87 -0
  131. package/dist/lib/catalogue/scores-coverage.js +573 -0
  132. package/dist/lib/catalogue/screen-validation-loader.js +89 -0
  133. package/dist/lib/catalogue/service-architecture-parser.js +193 -0
  134. package/dist/lib/catalogue/service-contract-aliases-loader.js +109 -0
  135. package/dist/lib/catalogue/staleness-verdicts.js +345 -0
  136. package/dist/lib/catalogue/traceability-loader.js +350 -0
  137. package/dist/lib/catalogue/validate.js +200 -0
  138. package/dist/lib/catalogue/version.js +18 -0
  139. package/dist/lib/conformance/binding.js +70 -0
  140. package/dist/lib/conformance/finding.js +69 -0
  141. package/dist/lib/conformance/index.js +578 -0
  142. package/dist/lib/context-pull/index.js +191 -0
  143. package/dist/lib/context-pull/resolve.js +70 -0
  144. package/dist/lib/context-pull/retired-mcp-setup.js +7 -0
  145. package/dist/lib/contracts-availability/index.js +267 -0
  146. package/dist/lib/generate-briefing/index.js +77 -0
  147. package/dist/lib/list/discover.js +100 -0
  148. package/dist/lib/list/format.js +84 -0
  149. package/dist/lib/list/index.js +16 -0
  150. package/dist/lib/lock-file.js +437 -0
  151. package/dist/lib/pack/log.js +18 -0
  152. package/dist/lib/pack/schema.js +121 -0
  153. package/dist/lib/pack/seed.js +182 -0
  154. package/dist/lib/pack/validate.js +49 -0
  155. package/dist/lib/package-version.js +92 -0
  156. package/dist/lib/plan/index.js +69 -0
  157. package/dist/lib/plan/read-sdd.js +102 -0
  158. package/dist/lib/plan/render-template.js +84 -0
  159. package/dist/lib/repo-kind.js +129 -0
  160. package/dist/lib/scaffold/domain.js +234 -0
  161. package/dist/lib/scaffold/registry.js +136 -0
  162. package/dist/lib/scripts.js +7 -0
  163. package/dist/lib/sdd-index/repo-config.js +245 -0
  164. package/dist/lib/sdd-reader.js +91 -0
  165. package/dist/lib/snapshot/advance.js +454 -0
  166. package/dist/lib/snapshot/diff.js +190 -0
  167. package/dist/lib/snapshot/fold.js +208 -0
  168. package/dist/lib/snapshot/land.js +178 -0
  169. package/dist/lib/snapshot/manifest.js +206 -0
  170. package/dist/lib/snapshot/targets.js +169 -0
  171. package/dist/lib/snapshot/verify.js +91 -0
  172. package/dist/lib/sync/briefing-merge.js +106 -0
  173. package/dist/lib/sync/bundle-cache.js +109 -0
  174. package/dist/lib/sync/yaml-block-merge.js +120 -0
  175. package/dist/lib/validate/aggregate.js +51 -0
  176. package/dist/lib/validate/bundle-coherence.js +74 -0
  177. package/dist/lib/validate/dispatch.js +243 -0
  178. package/dist/lib/validate/index.js +110 -0
  179. package/dist/lib/validate/plan.js +54 -0
  180. package/dist/lib/validate/runner.js +85 -0
  181. package/dist/lib/validate/yaml-parse.js +112 -0
  182. package/dist/maintain-version.json +4 -0
  183. package/dist/verbs/api-surface.js +71 -0
  184. package/dist/verbs/catalogue-check.js +28 -0
  185. package/dist/verbs/catalogue-verify.js +114 -0
  186. package/dist/verbs/conformance.js +34 -0
  187. package/dist/verbs/context.js +39 -0
  188. package/dist/verbs/contracts.js +34 -0
  189. package/dist/verbs/generate-briefing.js +29 -0
  190. package/dist/verbs/list.js +45 -0
  191. package/dist/verbs/pack-validate.js +17 -0
  192. package/dist/verbs/path-option.js +25 -0
  193. package/dist/verbs/plan.js +42 -0
  194. package/dist/verbs/render.js +87 -0
  195. package/dist/verbs/scaffold.js +83 -0
  196. package/dist/verbs/snapshot.js +134 -0
  197. package/dist/verbs/validate.js +48 -0
  198. package/dist/verify-program.js +122 -0
  199. package/dist/verify-version.json +4 -0
  200. package/package.json +32 -4
  201. package/scripts/validate-app-dependencies.sh +664 -0
  202. package/scripts/validate-app-docs.sh +199 -0
  203. package/scripts/validate-integration-docs.sh +505 -0
@@ -0,0 +1,71 @@
1
+ import { existsSync } from "node:fs";
2
+ import { mkdir, writeFile } from "node:fs/promises";
3
+ import { dirname } from "node:path";
4
+ import { analyzeRepo } from "../lib/api-surface/index.js";
5
+ import { toMarkdownBlock, toYaml } from "../lib/api-surface/emit.js";
6
+ export function registerApiSurface(program) {
7
+ program
8
+ .command("api-surface")
9
+ .description("Extract operation-level interface + dependency + deployment evidence from a source repo — the " +
10
+ "four things the reverse repo-doc-quality rubric needs for interface_and_dependency_coverage=2: " +
11
+ "endpoints (method+path+auth), the upstream/downstream dependency graph, external contracts, and " +
12
+ "deployment coupling (shared infra/DB/queue). Detects the stack (node-serverless | java-spring | " +
13
+ "terraform | library) and emits contracts/api-surface.yaml (extends the existing shape).")
14
+ .requiredOption("--source <dir>", "source repo root (a <repo>-master@<sha>/ dir or any repo root)")
15
+ .option("--repo <name>", "repo slug (default: derived from the source dir name)")
16
+ .option("--domain <d>", "domain tag for the emitted surface")
17
+ .option("--profile <p>", "profile tag (default: reverse)")
18
+ .option("--out <file>", "write YAML to a file (default: stdout)")
19
+ .option("--emit-md", "also emit the markdown interface block")
20
+ .option("--dry-run", "force stdout even when --out is given")
21
+ .action(async (opts) => {
22
+ if (!existsSync(opts.source)) {
23
+ process.stderr.write(`[FAIL] source dir not found: ${opts.source}\n`);
24
+ process.exit(1);
25
+ return;
26
+ }
27
+ let surface;
28
+ try {
29
+ surface = await analyzeRepo({
30
+ source: opts.source,
31
+ ...(opts.repo ? { repo: opts.repo } : {}),
32
+ ...(opts.domain ? { domain: opts.domain } : {}),
33
+ ...(opts.profile ? { profile: opts.profile } : {}),
34
+ });
35
+ }
36
+ catch (err) {
37
+ process.stderr.write(`[FAIL] api-surface analysis failed: ${err.message}\n`);
38
+ process.exit(1);
39
+ return;
40
+ }
41
+ const yaml = toYaml(surface);
42
+ const md = opts.emitMd === true ? toMarkdownBlock(surface) : "";
43
+ const toStdout = opts.dryRun === true || opts.out === undefined;
44
+ if (toStdout) {
45
+ process.stdout.write(yaml);
46
+ if (!yaml.endsWith("\n"))
47
+ process.stdout.write("\n");
48
+ if (md) {
49
+ process.stdout.write("\n");
50
+ process.stdout.write(md);
51
+ if (!md.endsWith("\n"))
52
+ process.stdout.write("\n");
53
+ }
54
+ }
55
+ else {
56
+ await mkdir(dirname(opts.out), { recursive: true });
57
+ await writeFile(opts.out, yaml.endsWith("\n") ? yaml : yaml + "\n", "utf-8");
58
+ process.stdout.write(`[OK] wrote ${opts.out}\n`);
59
+ if (md) {
60
+ const mdPath = opts.out.replace(/\.ya?ml$/i, ".md");
61
+ await writeFile(mdPath, md.endsWith("\n") ? md : md + "\n", "utf-8");
62
+ process.stdout.write(`[OK] wrote ${mdPath}\n`);
63
+ }
64
+ }
65
+ const epCount = surface.endpoints.length;
66
+ const depCount = surface.dependencies.downstream.length;
67
+ process.stderr.write(`[OK] ${surface.repo} (${surface.stack}): ${epCount} endpoint(s), ${depCount} downstream dep(s), ` +
68
+ `runtime=${surface.deployment.runtime || "?"}\n`);
69
+ });
70
+ }
71
+ //# sourceMappingURL=api-surface.js.map
@@ -0,0 +1,28 @@
1
+ import { resolve } from "node:path";
2
+ import { checkCataloguesFresh } from "../lib/catalogue/freshness.js";
3
+ /**
4
+ * `catalogue check` (M-ADR 0220 §4.2, ruling 3): whether the committed catalogues are what the
5
+ * committed specifications build to. It builds into temporary space and writes nothing to the
6
+ * repository: `catalogue build --check` under its own name, so the author's surface holds no verb
7
+ * that writes the model. Its own registrar, so an artefact can carry it without the build,
8
+ * aggregate and graph registrations in catalogue-maintain.ts.
9
+ */
10
+ export function registerCatalogueCheck(catalogue) {
11
+ catalogue
12
+ .command("check")
13
+ .description("Report whether the committed catalogues/ are what the committed specifications build to; builds in a temporary directory and writes nothing (exit 1 lists every stale file)")
14
+ .option("--sdd-path <path>", "Path to the SDD repo root (defaults to cwd)")
15
+ .option("--strict", "Treat build findings as failures")
16
+ .option("--snapshot-store <dir>", "Snapshot store root the binding's snapshot_area is relative to (default: $SDD_SNAPSHOT_STORE; M-ADR 0214)")
17
+ .action(async (opts) => {
18
+ const snapshotStore = opts.snapshotStore ?? process.env.SDD_SNAPSHOT_STORE;
19
+ const r = await checkCataloguesFresh(resolve(opts.sddPath ?? process.cwd()), {
20
+ label: "catalogue check",
21
+ ...(opts.strict !== undefined ? { strict: opts.strict } : {}),
22
+ ...(snapshotStore !== undefined ? { snapshotStore } : {}),
23
+ }, (s) => process.stdout.write(s));
24
+ if (r.exitCode !== 0)
25
+ process.exit(r.exitCode);
26
+ });
27
+ }
28
+ //# sourceMappingURL=catalogue-check.js.map
@@ -0,0 +1,114 @@
1
+ // The read-only `catalogue` subcommands — validate (incl. --coverage), impact,
2
+ // baseline-check, version — split from `catalogue.ts` so a program can register
3
+ // them without importing the catalogue BUILD path. The verify-only CLI ships
4
+ // these and must not ship code that writes the derived model (M-ADR 0212 §4.3b).
5
+ // `registerCatalogue` calls each at its original position, so sdd-cli's
6
+ // `catalogue` surface and help order are unchanged.
7
+ //
8
+ // ⚠ Import each run* from its own module, NEVER from `../lib/catalogue/index.js`:
9
+ // that barrel re-exports the build and aggregate paths, and one convenience
10
+ // import from it would silently put authoring code back into the verify package.
11
+ import { runBaselineCheck } from "../lib/catalogue/baseline-check.js";
12
+ import { runCatalogueCoverage } from "../lib/catalogue/coverage-run.js";
13
+ import { runCatalogueImpact } from "../lib/catalogue/impact-run.js";
14
+ import { runCatalogueValidate } from "../lib/catalogue/validate.js";
15
+ import { runCatalogueVersion } from "../lib/catalogue/version.js";
16
+ export function registerCatalogueValidate(catalogue) {
17
+ catalogue
18
+ .command("validate")
19
+ .description("Re-run column-shape + FK validation against existing catalogues/ (no rebuild)")
20
+ .option("--sdd-path <path>", "Path to the SDD repo root (defaults to cwd)")
21
+ .option("--strict", "Treat findings as failures")
22
+ .option("--coverage", "Coverage mode: run the engine coverage predicates instead (same lines the gate scripts printed; exit 0 — gating stays the baseline's job)")
23
+ .option("--tier <tier>", "Coverage tier, explicit never inferred (member | aggregate; default member)")
24
+ .option("--catalogues-dir <dir>", "Coverage mode: catalogues directory relative to the SDD root (default catalogues)")
25
+ .action(async (opts) => {
26
+ if (opts.coverage === true) {
27
+ if (opts.tier !== undefined &&
28
+ opts.tier !== "member" &&
29
+ opts.tier !== "aggregate") {
30
+ process.stderr.write(`--tier must be 'member' or 'aggregate' (got: ${opts.tier})\n`);
31
+ process.exit(2);
32
+ }
33
+ const result = await runCatalogueCoverage({
34
+ ...(opts.sddPath !== undefined ? { sddPath: opts.sddPath } : {}),
35
+ ...(opts.cataloguesDir !== undefined
36
+ ? { cataloguesDir: opts.cataloguesDir }
37
+ : {}),
38
+ ...(opts.tier !== undefined
39
+ ? { tier: opts.tier }
40
+ : {}),
41
+ }, (s) => process.stdout.write(s));
42
+ if (result.exitCode !== 0)
43
+ process.exit(result.exitCode);
44
+ return;
45
+ }
46
+ const result = await runCatalogueValidate({
47
+ ...(opts.sddPath !== undefined ? { sddPath: opts.sddPath } : {}),
48
+ ...(opts.strict !== undefined ? { strict: opts.strict } : {}),
49
+ }, (s) => process.stdout.write(s));
50
+ if (result.exitCode !== 0)
51
+ process.exit(result.exitCode);
52
+ });
53
+ }
54
+ export function registerCatalogueImpact(catalogue) {
55
+ catalogue
56
+ .command("impact")
57
+ .description("Change-impact closure of a node (M-ADR 0206): each relationship is traversed per its declared impact-flow from the rubric-coverage-map — dependency edges reach their consumers, membership edges propagate part-to-whole only, xref/lineage records are inert — computed by recursive SQL on the embedded engine, with the co-directionality invariant asserted on every run")
58
+ .requiredOption("--of <node-id>", 'Node to compute impact of, in the projection\'s "<catalogue>/<id>" form (e.g. contracts/orders-api)')
59
+ .option("--sdd-path <path>", "Path to the SDD repo root (defaults to cwd)")
60
+ .option("--catalogues-dir <dir>", "Catalogues directory relative to the SDD root (default: catalogues, or catalogues-aggregated at --tier aggregate)")
61
+ .option("--tier <tier>", "Tier, explicit never inferred (member | aggregate; default member)")
62
+ .action(async (opts) => {
63
+ if (opts.tier !== undefined &&
64
+ opts.tier !== "member" &&
65
+ opts.tier !== "aggregate") {
66
+ process.stderr.write(`--tier must be 'member' or 'aggregate' (got: ${opts.tier})\n`);
67
+ process.exit(2);
68
+ }
69
+ const result = await runCatalogueImpact({
70
+ of: opts.of,
71
+ ...(opts.sddPath !== undefined ? { sddPath: opts.sddPath } : {}),
72
+ ...(opts.cataloguesDir !== undefined
73
+ ? { cataloguesDir: opts.cataloguesDir }
74
+ : {}),
75
+ ...(opts.tier !== undefined
76
+ ? { tier: opts.tier }
77
+ : {}),
78
+ }, (s) => process.stdout.write(s));
79
+ if (result.exitCode !== 0)
80
+ process.exit(result.exitCode);
81
+ });
82
+ }
83
+ export function registerCatalogueBaselineCheck(catalogue) {
84
+ catalogue
85
+ .command("baseline-check")
86
+ .description("Compare emitted findings against the repo's findings baseline BY IDENTITY (M-ADR 0132 §9) — a reworded finding reports as reworded, not as one closed and one new")
87
+ .option("--sdd-path <path>", "Path to the SDD repo root (defaults to cwd)")
88
+ .option("--findings <path>", "File of findings to compare (defaults to stdin, so it composes with `catalogue validate --coverage`)")
89
+ .option("--baseline <path>", "Baseline file (defaults to <sdd-path>/orchestration/catalogue-findings-baseline.txt)")
90
+ .action(async (opts) => {
91
+ const result = await runBaselineCheck({
92
+ ...(opts.sddPath !== undefined ? { sddPath: opts.sddPath } : {}),
93
+ ...(opts.findings !== undefined ? { findings: opts.findings } : {}),
94
+ ...(opts.baseline !== undefined ? { baseline: opts.baseline } : {}),
95
+ stdin: async () => {
96
+ const chunks = [];
97
+ for await (const c of process.stdin)
98
+ chunks.push(Buffer.from(c));
99
+ return Buffer.concat(chunks).toString("utf-8");
100
+ },
101
+ }, (s) => process.stdout.write(s));
102
+ if (result.exitCode !== 0)
103
+ process.exit(result.exitCode);
104
+ });
105
+ }
106
+ export function registerCatalogueVersion(catalogue) {
107
+ catalogue
108
+ .command("version")
109
+ .description("Print the catalogue schema version + sdd-cli version pin")
110
+ .action(async () => {
111
+ await runCatalogueVersion((s) => process.stdout.write(s));
112
+ });
113
+ }
114
+ //# sourceMappingURL=catalogue-verify.js.map
@@ -0,0 +1,34 @@
1
+ import { runConformance } from "../lib/conformance/index.js";
2
+ import { resolvePathOption } from "./path-option.js";
3
+ export function registerConformance(program) {
4
+ program
5
+ .command("conformance")
6
+ .description("Conformance-reverse: diff the declared surface (spec/binding/contract) against the observed " +
7
+ "surface (code) and report drift with PROVISIONAL verdicts (ADR 0180). Advisory by default — " +
8
+ "disposition (code-defect vs spec-stale vs divergence) is a triage step, not this verb's call. " +
9
+ "Checks are derived from the spec-implementation binding (.sdd/implementation-binding.yaml, " +
10
+ "ADR 0181) when present; else .sdd/conformance-checks.yaml.")
11
+ .option("--target-repo <dir>", "target SDD repo", resolvePathOption, process.cwd())
12
+ .option("--config <path>", "explicit checks file (overrides the binding)")
13
+ .option("--code-root <dir>", "checkout root for implementation member repos (resolves @code/ sources)", resolvePathOption)
14
+ .option("--check <id>", "run only the named check")
15
+ .option("--json", "machine-readable report (CI ingestion)")
16
+ .option("--reconcile", "for binding-vs-reality checks, emit the binding fragment regenerated from the observed-set (ADR 0181 §2.3)")
17
+ .option("--emit-finding <dir>", "write the conformance_finding catalogue CSV into <dir> (federates via aggregate)")
18
+ .option("--strict", "exit non-zero when any check needs triage (default: advisory, exit 0)")
19
+ .action(async (opts) => {
20
+ const { exitCode } = await runConformance({
21
+ targetRepo: opts.targetRepo,
22
+ ...(opts.config ? { config: opts.config } : {}),
23
+ ...(opts.codeRoot ? { codeRoot: opts.codeRoot } : {}),
24
+ ...(opts.check ? { check: opts.check } : {}),
25
+ json: opts.json === true,
26
+ reconcile: opts.reconcile === true,
27
+ strict: opts.strict === true,
28
+ ...(opts.emitFinding ? { emitFinding: opts.emitFinding } : {}),
29
+ });
30
+ if (exitCode !== 0)
31
+ process.exit(exitCode);
32
+ });
33
+ }
34
+ //# sourceMappingURL=conformance.js.map
@@ -0,0 +1,39 @@
1
+ import { resolvePathOption } from "./path-option.js";
2
+ import { AGENT_HARNESSES, resolveAgentHarness } from "../lib/agent-harness.js";
3
+ import { runContextPull } from "../lib/context-pull/index.js";
4
+ const collect = (value, previous = []) => [...previous, resolvePathOption(value)];
5
+ export function registerContext(program) {
6
+ const context = program
7
+ .command("context")
8
+ .description("Briefing for a code repository an SDD specifies (M-ADR 0128 §1, M-ADR 0150 §4.4)");
9
+ context
10
+ .command("pull")
11
+ .description("Write agent-instructions.md and the harness wrapper's method-managed block into this code repository, rendered from the templates its upstream SDD carries. The SDD is found by looking this repository up in the catalogues/repositories.csv of the SDDs beside it. Writes nothing else.")
12
+ .option("--target-repo <dir>", "the code repository to write into", resolvePathOption, process.cwd())
13
+ .option("--sdd <dir>", "the upstream SDD, skipping discovery", resolvePathOption)
14
+ .option("--repo <id>", "this repository's id in the SDD's catalogue (default: its git top-level directory name)")
15
+ .option("--search <dir>", "discover SDDs under this directory (repeatable; default: the code repository's parent)", collect)
16
+ .option("--agent-harness <name>", `target agent harness. Default: claude-code. Supported: ${AGENT_HARNESSES.join(", ")}`)
17
+ .option("--allow-overwrite", "replace a harness wrapper that has no method-managed markers")
18
+ .action(async (opts) => {
19
+ let agentHarness;
20
+ try {
21
+ agentHarness = resolveAgentHarness(opts.agentHarness);
22
+ }
23
+ catch (err) {
24
+ process.stderr.write(`[FAIL] ${err.message}\n`);
25
+ process.exit(1);
26
+ }
27
+ const { exitCode } = await runContextPull({
28
+ codeRepo: opts.targetRepo,
29
+ agentHarness,
30
+ allowOverwrite: opts.allowOverwrite === true,
31
+ sddPath: opts.sdd,
32
+ repoName: opts.repo,
33
+ searchRoots: opts.search,
34
+ });
35
+ if (exitCode !== 0)
36
+ process.exit(exitCode);
37
+ });
38
+ }
39
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1,34 @@
1
+ import { runAttest } from "../lib/contracts-availability/index.js";
2
+ import { resolvePathOption } from "./path-option.js";
3
+ export function registerContracts(program) {
4
+ const contracts = program
5
+ .command("contracts")
6
+ .description("Contract artefact operations (M-ADR 0202)");
7
+ contracts
8
+ .command("attest")
9
+ .description("Maintain/verify .sdd/contract-availability.yaml — the committed attestation for " +
10
+ "contract artefacts living in sibling repositories (M-ADR 0202). --refresh runs " +
11
+ "where the siblings are checked out and writes entries (path, sha256, repo@commit); " +
12
+ "--verify runs anywhere: present artefacts are digest-checked for drift, " +
13
+ "absent-but-attested is satisfied, referenced-but-unattested-and-absent fails.")
14
+ .option("--target-repo <dir>", "target SDD repo", resolvePathOption, process.cwd())
15
+ .option("--code-root <dir>", "root under which sibling-resident declared paths resolve (default: parent of target repo)", resolvePathOption)
16
+ .option("--refresh", "attest what is visible from this machine and update the manifest")
17
+ .option("--verify", "check references and attestations without writing (default)")
18
+ .action(async (opts) => {
19
+ if (opts.refresh && opts.verify) {
20
+ process.stderr.write("[FAIL] --refresh and --verify are mutually exclusive\n");
21
+ process.exit(1);
22
+ }
23
+ const { exitCode, log } = await runAttest({
24
+ targetRepo: opts.targetRepo,
25
+ mode: opts.refresh ? "refresh" : "verify",
26
+ ...(opts.codeRoot ? { codeRoot: opts.codeRoot } : {}),
27
+ });
28
+ for (const line of log)
29
+ process.stdout.write(line + "\n");
30
+ if (exitCode !== 0)
31
+ process.exit(exitCode);
32
+ });
33
+ }
34
+ //# sourceMappingURL=contracts.js.map
@@ -0,0 +1,29 @@
1
+ import { resolvePathOption } from "./path-option.js";
2
+ import { AGENT_HARNESSES, resolveAgentHarness, } from "../lib/agent-harness.js";
3
+ import { runGenerateBriefing } from "../lib/generate-briefing/index.js";
4
+ export function registerGenerateBriefing(program) {
5
+ program
6
+ .command("generate-briefing")
7
+ .description("Regenerate agent-instructions.md + the harness wrapper for an existing SDD repository (no bundle sync)")
8
+ .option("--target-dir <dir>", "target SDD repo", resolvePathOption, process.cwd())
9
+ .option("--agent-harness <name>", `target agent harness. Default: claude-code. Supported: ${AGENT_HARNESSES.join(", ")}`)
10
+ .option("--allow-overwrite", "accept overwrites of locally-modified wrapper files")
11
+ .action(async (opts) => {
12
+ let agentHarness;
13
+ try {
14
+ agentHarness = resolveAgentHarness(opts.agentHarness);
15
+ }
16
+ catch (err) {
17
+ process.stderr.write(`[FAIL] ${err.message}\n`);
18
+ process.exit(1);
19
+ }
20
+ const { exitCode } = await runGenerateBriefing({
21
+ targetDir: opts.targetDir,
22
+ agentHarness,
23
+ allowOverwrite: opts.allowOverwrite === true,
24
+ });
25
+ if (exitCode !== 0)
26
+ process.exit(exitCode);
27
+ });
28
+ }
29
+ //# sourceMappingURL=generate-briefing.js.map
@@ -0,0 +1,45 @@
1
+ import { runList } from "../lib/list/index.js";
2
+ const VALID_PROFILES = [
3
+ "platform",
4
+ "application",
5
+ "integration",
6
+ ];
7
+ export function registerList(program) {
8
+ program
9
+ .command("list")
10
+ .description("Enumerate SDD repositories discoverable in the current workspace, including adopter pack provenance (ADR 0151)")
11
+ .option("--profile <profile>", "filter by profile: platform | application | integration")
12
+ .option("--workspace-root <path>", "workspace directory to walk (repeatable; defaults to cwd)", collectRoots, [])
13
+ .option("--format <format>", "output format: text | json", "text")
14
+ .option("--verbose", "show managed_path counts per bundle (text format)")
15
+ .action(async (opts) => {
16
+ const profileFilter = parseProfile(opts.profile);
17
+ const format = parseFormat(opts.format);
18
+ const { exitCode } = await runList({
19
+ workspaceRoots: opts.workspaceRoot ?? [],
20
+ format,
21
+ verbose: opts.verbose === true,
22
+ ...(profileFilter ? { profileFilter } : {}),
23
+ }, (s) => process.stdout.write(s));
24
+ if (exitCode !== 0)
25
+ process.exit(exitCode);
26
+ });
27
+ }
28
+ function collectRoots(value, prior) {
29
+ prior.push(value);
30
+ return prior;
31
+ }
32
+ function parseProfile(value) {
33
+ if (value === undefined)
34
+ return undefined;
35
+ if (!VALID_PROFILES.includes(value)) {
36
+ throw new Error(`Unknown --profile "${value}". Expected one of: ${VALID_PROFILES.join(", ")}`);
37
+ }
38
+ return value;
39
+ }
40
+ function parseFormat(value) {
41
+ if (value === "text" || value === "json")
42
+ return value;
43
+ throw new Error(`Unknown --format "${value}". Expected "text" or "json".`);
44
+ }
45
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1,17 @@
1
+ // `pack validate` registration, split from `pack.ts` so a program can register
2
+ // it without importing `runPackBuild` — the verify-only CLI must not ship the
3
+ // code that writes a pack (M-ADR 0212 §4.3b). `registerPack` calls this, so
4
+ // sdd-cli's `pack` surface and help order are unchanged.
5
+ import { runPackValidate } from "../lib/pack/validate.js";
6
+ export function registerPackValidate(pack) {
7
+ pack
8
+ .command("validate")
9
+ .description("Validate a context-pack seed file against the canonical schema. Fast feedback for authoring.")
10
+ .requiredOption("--seed <path>", "path to the seed file (orchestration/distribution/{name}-seed.yaml)")
11
+ .action(async (opts) => {
12
+ const { exitCode } = await runPackValidate({ seedPath: opts.seed });
13
+ if (exitCode !== 0)
14
+ process.exit(exitCode);
15
+ });
16
+ }
17
+ //# sourceMappingURL=pack-validate.js.map
@@ -0,0 +1,25 @@
1
+ import { resolve } from "node:path";
2
+ /**
3
+ * Commander parse-arg coercion for an option naming a DIRECTORY ON DISK.
4
+ *
5
+ * Resolves the operand to an absolute path ONCE, at the option boundary, before
6
+ * any verb sees it. That placement is the fix, not a detail: B6 was a relative
7
+ * `--target-repo` that resolved far enough to succeed at the first read and
8
+ * then failed at every later one.
9
+ *
10
+ * `validate` read `.sdd-repo-kind` relative to the process cwd — which WORKS,
11
+ * so the run announced the correct profile and dispatched the full validator
12
+ * set — then handed the same unresolved string to bash validators running from
13
+ * a different working directory, which reported `App root not found` and exit
14
+ * 127. A healthy repo came back `passed=0 failed=5`, so the operator saw a
15
+ * broken REPO rather than a bad ARGUMENT. Repairing those messages would have
16
+ * been the wrong fix; the path is what is wrong, and it is wrong exactly once.
17
+ *
18
+ * Resolving at the boundary also means a verb cannot forget: there is no
19
+ * per-consumer `resolve()` to omit, and a new consumer of the option inherits
20
+ * an absolute path by construction.
21
+ */
22
+ export function resolvePathOption(value) {
23
+ return resolve(value);
24
+ }
25
+ //# sourceMappingURL=path-option.js.map
@@ -0,0 +1,42 @@
1
+ import { resolvePathOption } from "./path-option.js";
2
+ import { runPlanPilot } from "../lib/plan/index.js";
3
+ export function registerPlan(program) {
4
+ program
5
+ .command("plan")
6
+ .description("Scaffold a plan markdown file from a method-distributed template. Currently supports --pilot for pilot-kickoff plans; future subcommands may add --initiative / --migration.")
7
+ .option("--pilot", "scaffold a pilot-kickoff plan (requires --service, --capability, --domain)", false)
8
+ .option("--service <name>", "service / pilot name, e.g. party-management")
9
+ .option("--capability <name>", "capability under test, e.g. organization-management")
10
+ .option("--domain <name>", "domain the capability belongs to, e.g. party")
11
+ .option("--workspace-root <path>", "SDD root (defaults to cwd)", resolvePathOption)
12
+ .option("--output <path>", "destination file (defaults to content/product/plans/{service}-pilot-kickoff.md)")
13
+ .option("--dry-run", "print rendered output to stdout without writing", false)
14
+ .action(async (opts) => {
15
+ if (opts.pilot !== true) {
16
+ process.stderr.write("[FAIL] plan: no mode specified. Pass --pilot (other modes not yet implemented).\n");
17
+ process.exit(1);
18
+ }
19
+ const missing = [];
20
+ if (!opts.service)
21
+ missing.push("--service");
22
+ if (!opts.capability)
23
+ missing.push("--capability");
24
+ if (!opts.domain)
25
+ missing.push("--domain");
26
+ if (missing.length > 0) {
27
+ process.stderr.write(`[FAIL] plan --pilot: missing required flag(s): ${missing.join(", ")}\n`);
28
+ process.exit(1);
29
+ }
30
+ const result = await runPlanPilot({
31
+ sddRoot: opts.workspaceRoot ?? process.cwd(),
32
+ service: opts.service,
33
+ capability: opts.capability,
34
+ domain: opts.domain,
35
+ ...(opts.output !== undefined ? { outputPath: opts.output } : {}),
36
+ ...(opts.dryRun === true ? { dryRun: true } : {}),
37
+ }, (s) => process.stdout.write(s));
38
+ if (result.exitCode !== 0)
39
+ process.exit(result.exitCode);
40
+ });
41
+ }
42
+ //# sourceMappingURL=plan.js.map
@@ -0,0 +1,87 @@
1
+ import { readFile, writeFile } from "node:fs/promises";
2
+ import { basename, dirname, join, resolve } from "node:path";
3
+ import { existsSync } from "node:fs";
4
+ import { parse as parseYaml } from "yaml";
5
+ import { renderWireframe } from "../lib/catalogue/archetype/wireframe.js";
6
+ import { loadScreenValidationConfig } from "../lib/catalogue/screen-validation-loader.js";
7
+ export function registerRender(program) {
8
+ const render = program
9
+ .command("render")
10
+ .description("Render method artefacts to visual form");
11
+ render
12
+ .command("screen <file>")
13
+ .description("Render a screen-spec's openui-lang block to a structural wireframe SVG (ADR 0169). " +
14
+ "Platform comes from the `platform:` frontmatter; an adopter overlay is read from " +
15
+ ".sdd/screen-validation.yaml when present.")
16
+ .option("--out <file>", "write the SVG to a file (default: stdout)")
17
+ .action(async (file, opts) => {
18
+ let text;
19
+ try {
20
+ text = await readFile(file, "utf-8");
21
+ }
22
+ catch (err) {
23
+ process.stderr.write(`[FAIL] cannot read ${file}: ${err.message}\n`);
24
+ process.exit(1);
25
+ return;
26
+ }
27
+ const block = extractBlock(text);
28
+ if (block === null) {
29
+ process.stderr.write(`[FAIL] ${file} has no \`\`\`openui-lang block\n`);
30
+ process.exit(1);
31
+ return;
32
+ }
33
+ const fm = parseFrontmatter(text);
34
+ const platform = typeof fm.platform === "string" ? fm.platform : undefined;
35
+ const title = typeof fm.screen === "string" ? fm.screen : basename(file).replace(/\.md$/i, "");
36
+ // An overlay lives in the SDD's .sdd/; when the file sits under screens/,
37
+ // the SDD root is its grandparent. Best-effort — absent → no overlay.
38
+ const overlay = platform !== undefined ? await loadOverlay(file, platform) : undefined;
39
+ const svg = renderWireframe({ block, platform, overlay, title });
40
+ if (opts.out !== undefined) {
41
+ await writeFile(opts.out, svg, "utf-8");
42
+ process.stdout.write(`[OK] wrote ${opts.out}\n`);
43
+ }
44
+ else {
45
+ process.stdout.write(svg + "\n");
46
+ }
47
+ });
48
+ }
49
+ function extractBlock(text) {
50
+ const m = /```openui-lang\r?\n([\s\S]*?)```/.exec(text);
51
+ return m ? (m[1] ?? "") : null;
52
+ }
53
+ function parseFrontmatter(text) {
54
+ const m = /^---\r?\n([\s\S]*?)\r?\n---/.exec(text);
55
+ if (!m)
56
+ return {};
57
+ try {
58
+ const parsed = parseYaml(m[1] ?? "");
59
+ return parsed && typeof parsed === "object" ? parsed : {};
60
+ }
61
+ catch {
62
+ return {};
63
+ }
64
+ }
65
+ export async function loadOverlay(file, platform) {
66
+ // The overlay lives in the SDD's .sdd/. Screens may sit directly under
67
+ // screens/ or in a subfolder (screens/<group>/<file>.md per the grouping
68
+ // convention), so walk up from the file to the nearest ancestor that
69
+ // contains a .sdd/ directory. Best-effort — absent → no overlay.
70
+ let dir = dirname(resolve(file));
71
+ for (let depth = 0; depth < 16; depth++) {
72
+ if (existsSync(join(dir, ".sdd")))
73
+ break;
74
+ const parent = dirname(dir);
75
+ if (parent === dir)
76
+ break;
77
+ dir = parent;
78
+ }
79
+ try {
80
+ const cfg = await loadScreenValidationConfig(dir);
81
+ return cfg?.archetypeOverlays.get(platform);
82
+ }
83
+ catch {
84
+ return undefined;
85
+ }
86
+ }
87
+ //# sourceMappingURL=render.js.map