@sdd-method/maintain-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.
- package/LICENSE +201 -0
- package/NOTICE +17 -0
- package/README.md +6 -2
- package/dist/lib/adr-origin.js +59 -0
- package/dist/lib/agent-harness.js +28 -0
- package/dist/lib/briefing/index.js +13 -0
- package/dist/lib/briefing/render.js +123 -0
- package/dist/lib/briefing/variables.js +92 -0
- package/dist/lib/bundle.js +123 -0
- package/dist/lib/catalogue/aggregate.js +2404 -0
- package/dist/lib/catalogue/aggregation-manifest-loader.js +173 -0
- package/dist/lib/catalogue/application-archetype/expectations.js +119 -0
- package/dist/lib/catalogue/application-archetype/vocabulary.js +177 -0
- package/dist/lib/catalogue/archetype/maps/android.yaml +66 -0
- package/dist/lib/catalogue/archetype/maps/swiftui.yaml +58 -0
- package/dist/lib/catalogue/archetype/maps/uikit.yaml +48 -0
- package/dist/lib/catalogue/archetype/maps/web-generic.yaml +86 -0
- package/dist/lib/catalogue/archetype/openui-archetype-vocabulary.yaml +46 -0
- package/dist/lib/catalogue/archetype/resolver.js +103 -0
- package/dist/lib/catalogue/archetype/wireframe.js +418 -0
- package/dist/lib/catalogue/baseline-check.js +151 -0
- package/dist/lib/catalogue/build.js +1502 -0
- package/dist/lib/catalogue/builders/adr-supersedes.js +103 -0
- package/dist/lib/catalogue/builders/adrs.js +222 -0
- package/dist/lib/catalogue/builders/application-conformance-coverage.js +47 -0
- package/dist/lib/catalogue/builders/application-data-plane-bindings.js +115 -0
- package/dist/lib/catalogue/builders/c4-components.js +229 -0
- package/dist/lib/catalogue/builders/c4-containers.js +240 -0
- package/dist/lib/catalogue/builders/c4-entity-rollups.js +87 -0
- package/dist/lib/catalogue/builders/c4-external-systems.js +194 -0
- package/dist/lib/catalogue/builders/c4-l1-candidates.js +107 -0
- package/dist/lib/catalogue/builders/c4-systems.js +98 -0
- package/dist/lib/catalogue/builders/capabilities.js +488 -0
- package/dist/lib/catalogue/builders/capability-domain-model.js +74 -0
- package/dist/lib/catalogue/builders/capability-edges.js +256 -0
- package/dist/lib/catalogue/builders/capability-spec-walker.js +99 -0
- package/dist/lib/catalogue/builders/capability-spec.js +348 -0
- package/dist/lib/catalogue/builders/container-dependency.js +183 -0
- package/dist/lib/catalogue/builders/contracts.js +402 -0
- package/dist/lib/catalogue/builders/data-items.js +344 -0
- package/dist/lib/catalogue/builders/data-store-mapping.js +113 -0
- package/dist/lib/catalogue/builders/domain-models.js +175 -0
- package/dist/lib/catalogue/builders/domains.js +119 -0
- package/dist/lib/catalogue/builders/edge-label-merge.js +19 -0
- package/dist/lib/catalogue/builders/entities-manifest.js +160 -0
- package/dist/lib/catalogue/builders/entities-mermaid.js +189 -0
- package/dist/lib/catalogue/builders/entity-persistence.js +102 -0
- package/dist/lib/catalogue/builders/entity-slug.js +47 -0
- package/dist/lib/catalogue/builders/event-catalog-walker.js +186 -0
- package/dist/lib/catalogue/builders/event-edges.js +327 -0
- package/dist/lib/catalogue/builders/events.js +168 -0
- package/dist/lib/catalogue/builders/evidence-class.js +43 -0
- package/dist/lib/catalogue/builders/evidence-target-repo.js +67 -0
- package/dist/lib/catalogue/builders/external-system-exemption.js +120 -0
- package/dist/lib/catalogue/builders/feature-contract.js +77 -0
- package/dist/lib/catalogue/builders/feature-domain-model.js +50 -0
- package/dist/lib/catalogue/builders/feature-grounding-walker.js +254 -0
- package/dist/lib/catalogue/builders/feature-grounding.js +391 -0
- package/dist/lib/catalogue/builders/feature-groups.js +63 -0
- package/dist/lib/catalogue/builders/feature-subscription-channels.js +228 -0
- package/dist/lib/catalogue/builders/feature-tree-walker.js +295 -0
- package/dist/lib/catalogue/builders/features.js +300 -0
- package/dist/lib/catalogue/builders/glossary-xref.js +378 -0
- package/dist/lib/catalogue/builders/glossary.js +204 -0
- package/dist/lib/catalogue/builders/implementations.js +214 -0
- package/dist/lib/catalogue/builders/index.js +64 -0
- package/dist/lib/catalogue/builders/integration-components.js +344 -0
- package/dist/lib/catalogue/builders/integrations.js +181 -0
- package/dist/lib/catalogue/builders/persona-c4system.js +149 -0
- package/dist/lib/catalogue/builders/persona-container.js +133 -0
- package/dist/lib/catalogue/builders/persona-journey-edges.js +133 -0
- package/dist/lib/catalogue/builders/personas.js +152 -0
- package/dist/lib/catalogue/builders/product-edges.js +106 -0
- package/dist/lib/catalogue/builders/product-platform-dependencies.js +405 -0
- package/dist/lib/catalogue/builders/products.js +162 -0
- package/dist/lib/catalogue/builders/quality-scores.js +635 -0
- package/dist/lib/catalogue/builders/repo-capability-exemption.js +61 -0
- package/dist/lib/catalogue/builders/repo-dependency.js +127 -0
- package/dist/lib/catalogue/builders/repo-edges.js +340 -0
- package/dist/lib/catalogue/builders/repositories.js +458 -0
- package/dist/lib/catalogue/builders/scaffolds.js +197 -0
- package/dist/lib/catalogue/builders/screen-tree.js +222 -0
- package/dist/lib/catalogue/builders/screens.js +396 -0
- package/dist/lib/catalogue/builders/service-capability-exemption.js +63 -0
- package/dist/lib/catalogue/builders/service-contract-consumes.js +186 -0
- package/dist/lib/catalogue/builders/service-contract.js +74 -0
- package/dist/lib/catalogue/builders/service-entity-tables.js +187 -0
- package/dist/lib/catalogue/builders/services.js +232 -0
- package/dist/lib/catalogue/builders/system-external-dependency.js +115 -0
- package/dist/lib/catalogue/builders/user-journeys.js +253 -0
- package/dist/lib/catalogue/canonical-schema.yaml +4117 -0
- package/dist/lib/catalogue/cli-version.js +10 -0
- package/dist/lib/catalogue/contract-id.js +49 -0
- package/dist/lib/catalogue/contract-path-resolution.js +149 -0
- package/dist/lib/catalogue/coverage-predicates.js +603 -0
- package/dist/lib/catalogue/coverage-render.js +552 -0
- package/dist/lib/catalogue/coverage-run.js +115 -0
- package/dist/lib/catalogue/csv-reader.js +97 -0
- package/dist/lib/catalogue/csv-writer.js +96 -0
- package/dist/lib/catalogue/data-item-coverage.js +142 -0
- package/dist/lib/catalogue/data-plane-coherence.js +77 -0
- package/dist/lib/catalogue/ddl-generator.js +233 -0
- package/dist/lib/catalogue/dedupe-keyed-tables.js +0 -0
- package/dist/lib/catalogue/engine-validator.js +239 -0
- package/dist/lib/catalogue/ext-id.js +76 -0
- package/dist/lib/catalogue/feature-grounding-aliases-loader.js +65 -0
- package/dist/lib/catalogue/feature-repo-zero-resolution-summary.js +31 -0
- package/dist/lib/catalogue/finding-identity.js +183 -0
- package/dist/lib/catalogue/findings.js +46 -0
- package/dist/lib/catalogue/fk-validator.js +136 -0
- package/dist/lib/catalogue/freshness.js +69 -0
- package/dist/lib/catalogue/gate-checks.js +338 -0
- package/dist/lib/catalogue/graph-projection.js +346 -0
- package/dist/lib/catalogue/graph-run.js +64 -0
- package/dist/lib/catalogue/impact-run.js +228 -0
- package/dist/lib/catalogue/manifest-loader.js +435 -0
- package/dist/lib/catalogue/platform-scope.js +7 -0
- package/dist/lib/catalogue/population-checks.js +87 -0
- package/dist/lib/catalogue/relationship-semantics.js +66 -0
- package/dist/lib/catalogue/reverse-product-quality.js +99 -0
- package/dist/lib/catalogue/rubric-coverage-map.yaml +871 -0
- package/dist/lib/catalogue/runway-vocabulary-loader.js +56 -0
- package/dist/lib/catalogue/scaffold-delta.js +261 -0
- package/dist/lib/catalogue/schema-compat.js +52 -0
- package/dist/lib/catalogue/schema-loader.js +87 -0
- package/dist/lib/catalogue/scores-coverage.js +573 -0
- package/dist/lib/catalogue/screen-validation-loader.js +89 -0
- package/dist/lib/catalogue/service-architecture-parser.js +193 -0
- package/dist/lib/catalogue/service-contract-aliases-loader.js +109 -0
- package/dist/lib/catalogue/staleness-verdicts.js +345 -0
- package/dist/lib/catalogue/traceability-loader.js +350 -0
- package/dist/lib/catalogue/validate.js +200 -0
- package/dist/lib/catalogue/version.js +18 -0
- package/dist/lib/ci-binding/index.js +220 -0
- package/dist/lib/ci-binding/module.js +204 -0
- package/dist/lib/conformance/binding.js +70 -0
- package/dist/lib/context-pull/index.js +151 -0
- package/dist/lib/context-pull/resolve.js +70 -0
- package/dist/lib/generate-briefing/index.js +77 -0
- package/dist/lib/hooks-seed/copilot.js +167 -0
- package/dist/lib/hooks-seed/entries.js +47 -0
- package/dist/lib/hooks-seed/index.js +17 -0
- package/dist/lib/hooks-seed/lifecycle-mapping.js +28 -0
- package/dist/lib/hooks-seed/merge.js +128 -0
- package/dist/lib/hooks-seed/types.js +20 -0
- package/dist/lib/init/ci-workflow.js +98 -0
- package/dist/lib/list/discover.js +100 -0
- package/dist/lib/list/format.js +84 -0
- package/dist/lib/list/index.js +16 -0
- package/dist/lib/lock-file.js +437 -0
- package/dist/lib/mcp-seed/entries.js +14 -0
- package/dist/lib/mcp-seed/index.js +28 -0
- package/dist/lib/mcp-seed/merge.js +99 -0
- package/dist/lib/mcp-seed/types.js +13 -0
- package/dist/lib/pack/builder.js +303 -0
- package/dist/lib/pack/index.js +87 -0
- package/dist/lib/pack/leakage.js +144 -0
- package/dist/lib/pack/log.js +18 -0
- package/dist/lib/pack/publish-transform.js +233 -0
- package/dist/lib/pack/reference-closure.js +125 -0
- package/dist/lib/pack/schema.js +121 -0
- package/dist/lib/pack/seed.js +182 -0
- package/dist/lib/pack/validate.js +49 -0
- package/dist/lib/package-version.js +92 -0
- package/dist/lib/repo-kind.js +129 -0
- package/dist/lib/scripts.js +7 -0
- package/dist/lib/snapshot/catalogue-diff.js +219 -0
- package/dist/lib/snapshot/manifest.js +206 -0
- package/dist/lib/sync/adr-transitions.js +250 -0
- package/dist/lib/sync/apply.js +91 -0
- package/dist/lib/sync/briefing-merge.js +90 -0
- package/dist/lib/sync/bundle-cache.js +109 -0
- package/dist/lib/sync/bundle-source.js +61 -0
- package/dist/lib/sync/config.js +72 -0
- package/dist/lib/sync/copilot-hooks-merge.js +108 -0
- package/dist/lib/sync/diff.js +251 -0
- package/dist/lib/sync/index.js +818 -0
- package/dist/lib/sync/settings-merge.js +216 -0
- package/dist/lib/sync/yaml-block-merge.js +120 -0
- package/dist/lib/validate/aggregate.js +51 -0
- package/dist/lib/validate/bundle-coherence.js +74 -0
- package/dist/lib/validate/dispatch.js +243 -0
- package/dist/lib/validate/index.js +110 -0
- package/dist/lib/validate/plan.js +54 -0
- package/dist/lib/validate/runner.js +85 -0
- package/dist/lib/validate/yaml-parse.js +112 -0
- package/dist/maintain-bin.js +8 -0
- package/dist/maintain-program.js +91 -0
- package/dist/maintain-version.json +4 -0
- package/dist/verbs/catalogue-maintain.js +110 -0
- package/dist/verbs/catalogue-verify.js +114 -0
- package/dist/verbs/context.js +39 -0
- package/dist/verbs/generate-briefing.js +29 -0
- package/dist/verbs/list.js +45 -0
- package/dist/verbs/pack-build.js +31 -0
- package/dist/verbs/pack-validate.js +17 -0
- package/dist/verbs/path-option.js +25 -0
- package/dist/verbs/render.js +87 -0
- package/dist/verbs/sync.js +110 -0
- package/dist/verbs/validate.js +48 -0
- package/dist/verify-program.js +122 -0
- package/dist/verify-version.json +4 -0
- package/package.json +32 -4
- package/scripts/validate-app-dependencies.sh +664 -0
- package/scripts/validate-app-docs.sh +199 -0
- package/scripts/validate-integration-docs.sh +505 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// Fourth entrypoint: the MAINTAIN surface — M-ADR 0218.
|
|
2
|
+
//
|
|
3
|
+
// A binary that brings an SDD current and rebuilds its derived model, and cannot change
|
|
4
|
+
// what the SDD says. It is to unattended sync waves what sdd-verify is to CI.
|
|
5
|
+
//
|
|
6
|
+
// A verb is here when everything it writes is DERIVED or METHOD-MANAGED, deterministically
|
|
7
|
+
// (M-ADR 0218 §4.2–4.3): the method payload, lock, CI block and briefing (`sync`,
|
|
8
|
+
// `generate-briefing`), the derived model (`catalogue build | aggregate | graph | diff`), an
|
|
9
|
+
// output the caller names (`pack build`, `render screen`), and a code repository's briefing
|
|
10
|
+
// (`context pull`). Plus the whole verify surface, because the personas nest.
|
|
11
|
+
//
|
|
12
|
+
// ⚠ As with sdd-verify, the boundary is ABSENCE, not a menu: the package ships only the import
|
|
13
|
+
// closure of maintain-bin.js, so authoring verbs (init, scaffold, plan, mcp, conformance,
|
|
14
|
+
// contracts, api-surface, snapshot, metamodel, migration-map) are not present to be reached.
|
|
15
|
+
// That is why every registration below imports a per-subcommand registrar, never a barrel.
|
|
16
|
+
// Unclassified verbs default to AUTHOR (M-ADR 0218 §4.2) and stay out until classified.
|
|
17
|
+
import { Command, CommanderError } from "commander";
|
|
18
|
+
import { getPackageVersion } from "./lib/package-version.js";
|
|
19
|
+
import { registerVerifySurface, rejectOutsideSurface, VERIFY_SURFACE } from "./verify-program.js";
|
|
20
|
+
import { registerCatalogueAggregate, registerCatalogueBuild, registerCatalogueDiff, registerCatalogueGraph } from "./verbs/catalogue-maintain.js";
|
|
21
|
+
import { registerContext } from "./verbs/context.js";
|
|
22
|
+
import { registerGenerateBriefing } from "./verbs/generate-briefing.js";
|
|
23
|
+
import { registerPackBuild } from "./verbs/pack-build.js";
|
|
24
|
+
import { registerRender } from "./verbs/render.js";
|
|
25
|
+
import { registerSync } from "./verbs/sync.js";
|
|
26
|
+
const SPEC_URL = "https://github.com/tomfosteruk/sdd-method/blob/main/docs/method/cli/sdd-cli-specification.md";
|
|
27
|
+
/** The verbs maintain adds to verify, as `<verb>` or `<verb> <subcommand>`. Asserted by test. */
|
|
28
|
+
export const MAINTAIN_ONLY_SURFACE = [
|
|
29
|
+
"sync",
|
|
30
|
+
"generate-briefing",
|
|
31
|
+
"context pull",
|
|
32
|
+
"catalogue build",
|
|
33
|
+
"catalogue aggregate",
|
|
34
|
+
"catalogue graph",
|
|
35
|
+
"catalogue diff",
|
|
36
|
+
"pack build",
|
|
37
|
+
"render screen",
|
|
38
|
+
];
|
|
39
|
+
/** The full surface: verify's, plus maintain's own. */
|
|
40
|
+
export const MAINTAIN_SURFACE = [...VERIFY_SURFACE, ...MAINTAIN_ONLY_SURFACE];
|
|
41
|
+
const OUTSIDE = "is not part of sdd-maintain, which can bring a repository current and rebuild its derived model but cannot author specification content. " +
|
|
42
|
+
"Authoring verbs are in @sdd-method/sdd-cli.";
|
|
43
|
+
export async function runMaintain(argv) {
|
|
44
|
+
const program = buildMaintainProgram();
|
|
45
|
+
if (argv.length === 0) {
|
|
46
|
+
program.outputHelp();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
rejectOutsideSurface(program, argv, OUTSIDE);
|
|
50
|
+
try {
|
|
51
|
+
await program.parseAsync([...argv], { from: "user" });
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
if (err instanceof CommanderError) {
|
|
55
|
+
if (err.code === "commander.help" || err.code === "commander.helpDisplayed" || err.code === "commander.version") {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
throw err;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export function buildMaintainProgram() {
|
|
63
|
+
const program = new Command();
|
|
64
|
+
program
|
|
65
|
+
.name("sdd-maintain")
|
|
66
|
+
.description("Bring an SDD repository current and rebuild its derived model — sync, briefing, catalogue build and aggregate, pack build, context pull — plus every read-only verify verb. Cannot author specification content.")
|
|
67
|
+
.version(getPackageVersion(), "-v, --version")
|
|
68
|
+
.addHelpText("after", `\nSpec: ${SPEC_URL}\nAuthoring verbs (init, scaffold, plan, mcp, conformance, …) are in @sdd-method/sdd-cli.`)
|
|
69
|
+
.exitOverride()
|
|
70
|
+
.configureOutput({
|
|
71
|
+
writeOut: (str) => process.stdout.write(str),
|
|
72
|
+
writeErr: (str) => process.stderr.write(str),
|
|
73
|
+
});
|
|
74
|
+
// The verify surface first, from verify's own registration: the nesting is structural.
|
|
75
|
+
registerVerifySurface(program);
|
|
76
|
+
registerSync(program);
|
|
77
|
+
registerGenerateBriefing(program);
|
|
78
|
+
registerContext(program);
|
|
79
|
+
registerRender(program);
|
|
80
|
+
const catalogue = program.commands.find((c) => c.name() === "catalogue");
|
|
81
|
+
catalogue.description("Build, aggregate, validate and query per-SDD catalogues (ADR 0134) — the derived model");
|
|
82
|
+
registerCatalogueBuild(catalogue);
|
|
83
|
+
registerCatalogueAggregate(catalogue);
|
|
84
|
+
registerCatalogueGraph(catalogue);
|
|
85
|
+
registerCatalogueDiff(catalogue);
|
|
86
|
+
const pack = program.commands.find((c) => c.name() === "pack");
|
|
87
|
+
pack.description("Build and validate adopter-shipped context-pack bundles (ADR 0151)");
|
|
88
|
+
registerPackBuild(pack);
|
|
89
|
+
return program;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=maintain-program.js.map
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// The catalogue subcommands that write the derived model — MAINTAIN under M-ADR 0218 §4.4:
|
|
2
|
+
// a deterministic function of the committed specs, written only under catalogues/,
|
|
3
|
+
// catalogues-aggregated/ and the graph projection beside them. One registrar per
|
|
4
|
+
// subcommand, importing each run* from its OWN module and never the catalogue barrel
|
|
5
|
+
// (which also re-exports migration-map), so the maintain package's import closure
|
|
6
|
+
// carries exactly these and no authoring module.
|
|
7
|
+
import { resolve } from "node:path";
|
|
8
|
+
import { runCatalogueBuild } from "../lib/catalogue/build.js";
|
|
9
|
+
import { runCatalogueAggregate } from "../lib/catalogue/aggregate.js";
|
|
10
|
+
import { runCatalogueGraph } from "../lib/catalogue/graph-run.js";
|
|
11
|
+
import { runCatalogueDiff } from "../lib/snapshot/catalogue-diff.js";
|
|
12
|
+
import { checkCataloguesFresh } from "../lib/catalogue/freshness.js";
|
|
13
|
+
export function registerCatalogueBuild(catalogue) {
|
|
14
|
+
catalogue
|
|
15
|
+
.command("build")
|
|
16
|
+
.description("Build catalogues for the current SDD repo from method-defined sources")
|
|
17
|
+
.option("--sdd-path <path>", "Path to the SDD repo root (defaults to cwd)")
|
|
18
|
+
.option("--out <path>", "Output directory for catalogues/ (defaults to <sdd-path>/catalogues)")
|
|
19
|
+
.option("--strict", "Treat findings as failures (default: only internal FK FAIL exits non-zero)")
|
|
20
|
+
.option("--snapshot-store <dir>", "Snapshot store root the binding's snapshot_area is relative to (default: $SDD_SNAPSHOT_STORE; M-ADR 0214)")
|
|
21
|
+
.option("--check", "Freshness check: build into a temporary directory, compare with the committed catalogues/, write nothing; exit 1 listing every stale file (M-ADR 0218 §5)")
|
|
22
|
+
.action(async (opts) => {
|
|
23
|
+
const snapshotStore = opts.snapshotStore ?? process.env.SDD_SNAPSHOT_STORE;
|
|
24
|
+
if (opts.check === true) {
|
|
25
|
+
if (opts.out !== undefined) {
|
|
26
|
+
process.stderr.write("--check builds into a temporary directory; it cannot be combined with --out\n");
|
|
27
|
+
process.exit(2);
|
|
28
|
+
}
|
|
29
|
+
const r = await checkCataloguesFresh(resolve(opts.sddPath ?? process.cwd()), {
|
|
30
|
+
...(opts.strict !== undefined ? { strict: opts.strict } : {}),
|
|
31
|
+
...(snapshotStore !== undefined ? { snapshotStore } : {}),
|
|
32
|
+
}, (s) => process.stdout.write(s));
|
|
33
|
+
if (r.exitCode !== 0)
|
|
34
|
+
process.exit(r.exitCode);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const result = await runCatalogueBuild({
|
|
38
|
+
...(opts.sddPath !== undefined ? { sddPath: opts.sddPath } : {}),
|
|
39
|
+
...(opts.out !== undefined ? { outDir: opts.out } : {}),
|
|
40
|
+
...(opts.strict !== undefined ? { strict: opts.strict } : {}),
|
|
41
|
+
...(snapshotStore !== undefined ? { snapshotStore } : {}),
|
|
42
|
+
}, (s) => process.stdout.write(s));
|
|
43
|
+
if (result.exitCode !== 0)
|
|
44
|
+
process.exit(result.exitCode);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
export function registerCatalogueAggregate(catalogue) {
|
|
48
|
+
catalogue
|
|
49
|
+
.command("aggregate")
|
|
50
|
+
.description("Aggregate sibling SDD catalogues into a single ecosystem catalogue")
|
|
51
|
+
.option("--sdd-path <path>", "Path to the ecosystem-root SDD repo (defaults to cwd)")
|
|
52
|
+
.option("--manifest <path>", "Path to the aggregation manifest (defaults to orchestration/aggregation-manifest.yaml)")
|
|
53
|
+
.option("--out <path>", "Output directory for aggregated catalogues/ (defaults to <sdd-path>/catalogues-aggregated)")
|
|
54
|
+
.option("--strict", "Treat findings as failures")
|
|
55
|
+
.action(async (opts) => {
|
|
56
|
+
const result = await runCatalogueAggregate({
|
|
57
|
+
...(opts.sddPath !== undefined ? { sddPath: opts.sddPath } : {}),
|
|
58
|
+
...(opts.manifest !== undefined
|
|
59
|
+
? { manifestPath: opts.manifest }
|
|
60
|
+
: {}),
|
|
61
|
+
...(opts.out !== undefined ? { outDir: opts.out } : {}),
|
|
62
|
+
...(opts.strict !== undefined ? { strict: opts.strict } : {}),
|
|
63
|
+
}, (s) => process.stdout.write(s));
|
|
64
|
+
if (result.exitCode !== 0)
|
|
65
|
+
process.exit(result.exitCode);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
export function registerCatalogueDiff(catalogue) {
|
|
69
|
+
catalogue
|
|
70
|
+
.command("diff")
|
|
71
|
+
.description("Build the catalogues at a git ref and at the working tree in ONE process (so one CLI), and attribute the change at file grain: every SDD source file changed since the ref must be a target of some delta.yaml finding. Exit 1 on any unattributed change. Writes nothing into the SDD.")
|
|
72
|
+
.requiredOption("--from <ref>", "the git ref of the SDD before the snapshot update")
|
|
73
|
+
.requiredOption("--attribute <delta.yaml>", "the snapshot's delta.yaml, whose findings' targets attribute the change")
|
|
74
|
+
.option("--sdd-path <path>", "Path to the SDD repo root (defaults to cwd)")
|
|
75
|
+
.action(async (opts) => {
|
|
76
|
+
const r = await runCatalogueDiff({ sddPath: resolve(opts.sddPath ?? process.cwd()), from: opts.from, attribute: resolve(opts.attribute) }, (s) => process.stdout.write(s));
|
|
77
|
+
if (r.exitCode !== 0)
|
|
78
|
+
process.exit(r.exitCode);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
export function registerCatalogueGraph(catalogue) {
|
|
82
|
+
catalogue
|
|
83
|
+
.command("graph")
|
|
84
|
+
.description("Emit the graph projection (typed node/edge JSON Lines) derived from built catalogues (M-ADR 0205 §4.6) — regenerated every run, digest-stamped, never committed")
|
|
85
|
+
.option("--sdd-path <path>", "Path to the SDD repo root (defaults to cwd)")
|
|
86
|
+
.option("--catalogues-dir <dir>", "Catalogues directory relative to the SDD root (default: catalogues, or catalogues-aggregated at --tier aggregate)")
|
|
87
|
+
.option("--tier <tier>", "Tier, explicit never inferred (member | aggregate; default member)")
|
|
88
|
+
.option("--out <path>", "Output file (default <catalogues-dir>-graph.jsonl beside the catalogues directory)")
|
|
89
|
+
.action(async (opts) => {
|
|
90
|
+
if (opts.tier !== undefined &&
|
|
91
|
+
opts.tier !== "member" &&
|
|
92
|
+
opts.tier !== "aggregate") {
|
|
93
|
+
process.stderr.write(`--tier must be 'member' or 'aggregate' (got: ${opts.tier})\n`);
|
|
94
|
+
process.exit(2);
|
|
95
|
+
}
|
|
96
|
+
const result = await runCatalogueGraph({
|
|
97
|
+
...(opts.sddPath !== undefined ? { sddPath: opts.sddPath } : {}),
|
|
98
|
+
...(opts.cataloguesDir !== undefined
|
|
99
|
+
? { cataloguesDir: opts.cataloguesDir }
|
|
100
|
+
: {}),
|
|
101
|
+
...(opts.tier !== undefined
|
|
102
|
+
? { tier: opts.tier }
|
|
103
|
+
: {}),
|
|
104
|
+
...(opts.out !== undefined ? { out: opts.out } : {}),
|
|
105
|
+
}, (s) => process.stdout.write(s));
|
|
106
|
+
if (result.exitCode !== 0)
|
|
107
|
+
process.exit(result.exitCode);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=catalogue-maintain.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,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, the harness wrapper's method-managed block and MCP_SETUP.md 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,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,31 @@
|
|
|
1
|
+
// `pack build` alone — MAINTAIN under M-ADR 0218 §4.4: it publishes a tarball to an output
|
|
2
|
+
// path, outside the producer's tracked content. Its own module, so the maintain package can
|
|
3
|
+
// carry it without the rest of the full CLI.
|
|
4
|
+
import { resolvePathOption } from "./path-option.js";
|
|
5
|
+
import { runPackBuild } from "../lib/pack/index.js";
|
|
6
|
+
export function registerPackBuild(pack) {
|
|
7
|
+
pack
|
|
8
|
+
.command("build")
|
|
9
|
+
.description("Build a context-pack tarball from a seed file. Runs producer-side leakage check before emit.")
|
|
10
|
+
.requiredOption("--seed <path>", "path to the seed file (orchestration/distribution/{name}-seed.yaml)")
|
|
11
|
+
.option("--producer-repo <dir>", "adopter repo root (managed_paths are resolved relative to this; defaults to cwd)", process.cwd())
|
|
12
|
+
.option("--out-dir <dir>", "output directory for the tarball (defaults to <producer-repo>/dist/<pack-name>/)", resolvePathOption)
|
|
13
|
+
.option("--forward-base-managed-paths <path>", "path to forward-base MANAGED_PATHS.txt for the leakage check (defaults to extracting from method-baseline.lock)")
|
|
14
|
+
.option("--publish-transform", "apply the point-in-time / producer-agnostic publish transform (ADR 0183/0184): drop non-Accepted ADRs, strip lineage + stripped-class fields, flatten cross-repo refs, fail closed on leaks. For producer-published bundles.")
|
|
15
|
+
.option("--origin-prefix", "with the publish transform, apply the range-aware origin prefix (ADR 0140 §4.1): 0NNN→M-ADR, 9NNN→RA-ADR, adopter 1NNN stays ADR; ADR files renamed to match. Implies --publish-transform. The RA-ADR bundle cutover switch.")
|
|
16
|
+
.action(async (opts) => {
|
|
17
|
+
const { exitCode } = await runPackBuild({
|
|
18
|
+
seedPath: opts.seed,
|
|
19
|
+
producerRepo: opts.producerRepo ?? process.cwd(),
|
|
20
|
+
...(opts.outDir && { outDir: opts.outDir }),
|
|
21
|
+
...(opts.forwardBaseManagedPaths && {
|
|
22
|
+
forwardBaseManagedPathsFile: opts.forwardBaseManagedPaths,
|
|
23
|
+
}),
|
|
24
|
+
...(opts.publishTransform && { publishTransform: true }),
|
|
25
|
+
...(opts.originPrefix && { originPrefix: true }),
|
|
26
|
+
});
|
|
27
|
+
if (exitCode !== 0)
|
|
28
|
+
process.exit(exitCode);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=pack-build.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,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
|