@ybotok/loam 0.1.0-beta.2 → 0.2.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2570 -61
- package/COMPARISON.md +80 -63
- package/CONTRIBUTING.md +84 -0
- package/MIGRATING-from-OpenSpec.md +213 -54
- package/QUICKSTART.md +125 -0
- package/README.md +450 -150
- package/ROADMAP.md +494 -0
- package/SCHEMA.md +2362 -245
- package/SECURITY.md +21 -3
- package/WORKFLOW.md +701 -0
- package/dist/cli.js +113 -19
- package/dist/cli.js.map +1 -1
- package/dist/commands/adopt/adopt.js +209 -0
- package/dist/commands/adopt/adopt.js.map +1 -0
- package/dist/commands/adopt/render.js +268 -0
- package/dist/commands/adopt/render.js.map +1 -0
- package/dist/commands/archive/archive.js +77 -0
- package/dist/commands/archive/archive.js.map +1 -0
- package/dist/commands/archive/plan/contracts/asyncapi.js +178 -0
- package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -0
- package/dist/commands/archive/plan/contracts/openapi.js +159 -0
- package/dist/commands/archive/plan/contracts/openapi.js.map +1 -0
- package/dist/commands/archive/plan/gate.js +317 -0
- package/dist/commands/archive/plan/gate.js.map +1 -0
- package/dist/commands/archive/plan/landscape.js +161 -0
- package/dist/commands/archive/plan/landscape.js.map +1 -0
- package/dist/commands/archive/plan/refusal.js +111 -0
- package/dist/commands/archive/plan/refusal.js.map +1 -0
- package/dist/commands/archive/plan/specs.js +237 -0
- package/dist/commands/archive/plan/specs.js.map +1 -0
- package/dist/commands/archive/plan/state.js +27 -0
- package/dist/commands/archive/plan/state.js.map +1 -0
- package/dist/commands/archive/run.js +251 -0
- package/dist/commands/archive/run.js.map +1 -0
- package/dist/commands/context/context.js +99 -0
- package/dist/commands/context/context.js.map +1 -0
- package/dist/commands/context/print.js +283 -0
- package/dist/commands/context/print.js.map +1 -0
- package/dist/commands/delta/delta.js +279 -0
- package/dist/commands/delta/delta.js.map +1 -0
- package/dist/commands/delta/print.js +205 -0
- package/dist/commands/delta/print.js.map +1 -0
- package/dist/commands/dependencies.js +9 -7
- package/dist/commands/dependencies.js.map +1 -1
- package/dist/commands/diff/diff.js +93 -0
- package/dist/commands/diff/diff.js.map +1 -0
- package/dist/commands/diff/print.js +38 -0
- package/dist/commands/diff/print.js.map +1 -0
- package/dist/commands/doctor.js +5 -4
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/explain/explain.js +151 -0
- package/dist/commands/explain/explain.js.map +1 -0
- package/dist/commands/explore.js +29 -24
- package/dist/commands/explore.js.map +1 -1
- package/dist/commands/gate/gate.js +115 -0
- package/dist/commands/gate/gate.js.map +1 -0
- package/dist/commands/gate/print.js +54 -0
- package/dist/commands/gate/print.js.map +1 -0
- package/dist/commands/gherkin/commit.js +146 -0
- package/dist/commands/gherkin/commit.js.map +1 -0
- package/dist/commands/gherkin/gherkin.js +246 -0
- package/dist/commands/gherkin/gherkin.js.map +1 -0
- package/dist/commands/gherkin/reconcile.js +149 -0
- package/dist/commands/gherkin/reconcile.js.map +1 -0
- package/dist/commands/gherkin/render.js +77 -0
- package/dist/commands/gherkin/render.js.map +1 -0
- package/dist/commands/init/example.js +204 -0
- package/dist/commands/init/example.js.map +1 -0
- package/dist/commands/init/first-hour.js +28 -0
- package/dist/commands/init/first-hour.js.map +1 -0
- package/dist/commands/{init.js → init/init.js} +146 -71
- package/dist/commands/init/init.js.map +1 -0
- package/dist/commands/init/mcp.js +131 -0
- package/dist/commands/init/mcp.js.map +1 -0
- package/dist/commands/init/options.js +78 -0
- package/dist/commands/init/options.js.map +1 -0
- package/dist/commands/instructions.js +82 -14
- package/dist/commands/instructions.js.map +1 -1
- package/dist/commands/list/campaign/campaign.js +65 -0
- package/dist/commands/list/campaign/campaign.js.map +1 -0
- package/dist/commands/list/campaign/owners.js +65 -0
- package/dist/commands/list/campaign/owners.js.map +1 -0
- package/dist/commands/list/json.js +157 -0
- package/dist/commands/list/json.js.map +1 -0
- package/dist/commands/list/list.js +343 -0
- package/dist/commands/list/list.js.map +1 -0
- package/dist/commands/list/print.js +301 -0
- package/dist/commands/list/print.js.map +1 -0
- package/dist/commands/list/review.js +120 -0
- package/dist/commands/list/review.js.map +1 -0
- package/dist/commands/list/views.js +105 -0
- package/dist/commands/list/views.js.map +1 -0
- package/dist/commands/mcp/dispatch.js +183 -0
- package/dist/commands/mcp/dispatch.js.map +1 -0
- package/dist/commands/mcp/mcp.js +34 -0
- package/dist/commands/mcp/mcp.js.map +1 -0
- package/dist/commands/mcp/serve.js +55 -0
- package/dist/commands/mcp/serve.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/active.js +170 -0
- package/dist/commands/migrate-openspec/materialize/active.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/feature.js +41 -0
- package/dist/commands/migrate-openspec/materialize/feature.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/living.js +62 -0
- package/dist/commands/migrate-openspec/materialize/living.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/stage.js +142 -0
- package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -0
- package/dist/commands/migrate-openspec/materialize/target.js +219 -0
- package/dist/commands/migrate-openspec/materialize/target.js.map +1 -0
- package/dist/commands/migrate-openspec/migrate-openspec.js +142 -0
- package/dist/commands/migrate-openspec/migrate-openspec.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/decisions.js +91 -0
- package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/error.js +19 -0
- package/dist/commands/migrate-openspec/openspec/error.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/mapping.js +170 -0
- package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/paths.js +43 -0
- package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -0
- package/dist/commands/migrate-openspec/openspec/read.js +49 -0
- package/dist/commands/migrate-openspec/openspec/read.js.map +1 -0
- package/dist/commands/migrate-openspec/print.js +89 -0
- package/dist/commands/migrate-openspec/print.js.map +1 -0
- package/dist/commands/new/commit.js +76 -0
- package/dist/commands/new/commit.js.map +1 -0
- package/dist/commands/new/new.js +283 -0
- package/dist/commands/new/new.js.map +1 -0
- package/dist/commands/new/templates.js +355 -0
- package/dist/commands/new/templates.js.map +1 -0
- package/dist/commands/open.js +203 -0
- package/dist/commands/open.js.map +1 -0
- package/dist/commands/policy/format.js +152 -0
- package/dist/commands/policy/format.js.map +1 -0
- package/dist/commands/{docs-repo-gate.js → policy/gate.js} +11 -11
- package/dist/commands/policy/gate.js.map +1 -0
- package/dist/commands/rebase/edit.js +106 -0
- package/dist/commands/rebase/edit.js.map +1 -0
- package/dist/commands/rebase/living.js +185 -0
- package/dist/commands/rebase/living.js.map +1 -0
- package/dist/commands/rebase/plan.js +245 -0
- package/dist/commands/rebase/plan.js.map +1 -0
- package/dist/commands/rebase/rebase.js +275 -0
- package/dist/commands/rebase/rebase.js.map +1 -0
- package/dist/commands/seed/commit.js +192 -0
- package/dist/commands/seed/commit.js.map +1 -0
- package/dist/commands/seed/plan.js +133 -0
- package/dist/commands/seed/plan.js.map +1 -0
- package/dist/commands/seed/seed.js +172 -0
- package/dist/commands/seed/seed.js.map +1 -0
- package/dist/commands/show/feature.js +178 -0
- package/dist/commands/show/feature.js.map +1 -0
- package/dist/commands/show/marks.js +24 -0
- package/dist/commands/show/marks.js.map +1 -0
- package/dist/commands/{show.js → show/service.js} +63 -152
- package/dist/commands/show/service.js.map +1 -0
- package/dist/commands/show/show.js +77 -0
- package/dist/commands/show/show.js.map +1 -0
- package/dist/commands/status/json.js +45 -0
- package/dist/commands/status/json.js.map +1 -0
- package/dist/commands/status/print.js +157 -0
- package/dist/commands/status/print.js.map +1 -0
- package/dist/commands/status/status.js +119 -0
- package/dist/commands/status/status.js.map +1 -0
- package/dist/commands/steps/steps.js +189 -0
- package/dist/commands/steps/steps.js.map +1 -0
- package/dist/commands/subsystem/edit.js +192 -0
- package/dist/commands/subsystem/edit.js.map +1 -0
- package/dist/commands/subsystem/move.js +265 -0
- package/dist/commands/subsystem/move.js.map +1 -0
- package/dist/commands/subsystem/query.js +115 -0
- package/dist/commands/subsystem/query.js.map +1 -0
- package/dist/commands/subsystem/subsystem.js +101 -0
- package/dist/commands/subsystem/subsystem.js.map +1 -0
- package/dist/commands/subsystem/sync.js +103 -0
- package/dist/commands/subsystem/sync.js.map +1 -0
- package/dist/commands/subsystem/txn/txn.js +94 -0
- package/dist/commands/subsystem/txn/txn.js.map +1 -0
- package/dist/commands/subsystem/txn/views.js +70 -0
- package/dist/commands/subsystem/txn/views.js.map +1 -0
- package/dist/commands/unarchive/manifest.js +160 -0
- package/dist/commands/unarchive/manifest.js.map +1 -0
- package/dist/commands/{unarchive.js → unarchive/restore.js} +47 -160
- package/dist/commands/unarchive/restore.js.map +1 -0
- package/dist/commands/unarchive/unarchive.js +67 -0
- package/dist/commands/unarchive/unarchive.js.map +1 -0
- package/dist/commands/validate/arch-coverage.js +194 -0
- package/dist/commands/validate/arch-coverage.js.map +1 -0
- package/dist/commands/validate/checks/fleet-shape.js +296 -0
- package/dist/commands/validate/checks/fleet-shape.js.map +1 -0
- package/dist/commands/validate/checks/health-deps.js +55 -0
- package/dist/commands/validate/checks/health-deps.js.map +1 -0
- package/dist/commands/validate/checks/requirements.js +254 -0
- package/dist/commands/validate/checks/requirements.js.map +1 -0
- package/dist/commands/validate/checks/sources.js +76 -0
- package/dist/commands/validate/checks/sources.js.map +1 -0
- package/dist/commands/validate/checks/vocabulary.js +31 -0
- package/dist/commands/validate/checks/vocabulary.js.map +1 -0
- package/dist/commands/validate/feature.js +216 -0
- package/dist/commands/validate/feature.js.map +1 -0
- package/dist/commands/validate/fleet/census.js +69 -0
- package/dist/commands/validate/fleet/census.js.map +1 -0
- package/dist/commands/validate/fleet/kind-tags.js +39 -0
- package/dist/commands/validate/fleet/kind-tags.js.map +1 -0
- package/dist/commands/validate/fleet/landscape.js +353 -0
- package/dist/commands/validate/fleet/landscape.js.map +1 -0
- package/dist/commands/validate/fleet/load.js +52 -0
- package/dist/commands/validate/fleet/load.js.map +1 -0
- package/dist/commands/validate/fleet/obligations.js +236 -0
- package/dist/commands/validate/fleet/obligations.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/adoption.js +102 -0
- package/dist/commands/validate/fleet/scorecard/adoption.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/contracts.js +98 -0
- package/dist/commands/validate/fleet/scorecard/contracts.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/print.js +63 -0
- package/dist/commands/validate/fleet/scorecard/print.js.map +1 -0
- package/dist/commands/validate/fleet/scorecard/scorecard.js +156 -0
- package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/capability-tag.js +94 -0
- package/dist/commands/validate/fleet/usecases/capability-tag.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/place.js +13 -0
- package/dist/commands/validate/fleet/usecases/place.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/requirement-tag.js +95 -0
- package/dist/commands/validate/fleet/usecases/requirement-tag.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/steps.js +197 -0
- package/dist/commands/validate/fleet/usecases/steps.js.map +1 -0
- package/dist/commands/validate/fleet/usecases/usecases.js +99 -0
- package/dist/commands/validate/fleet/usecases/usecases.js.map +1 -0
- package/dist/commands/validate/fleet/views/ids.js +40 -0
- package/dist/commands/validate/fleet/views/ids.js.map +1 -0
- package/dist/commands/validate/fleet/views/stale.js +59 -0
- package/dist/commands/validate/fleet/views/stale.js.map +1 -0
- package/dist/commands/validate/links/corpus.js +92 -0
- package/dist/commands/validate/links/corpus.js.map +1 -0
- package/dist/commands/validate/links/glossary.js +75 -0
- package/dist/commands/validate/links/glossary.js.map +1 -0
- package/dist/commands/validate/report.js +269 -0
- package/dist/commands/validate/report.js.map +1 -0
- package/dist/commands/validate/service/api.js +338 -0
- package/dist/commands/validate/service/api.js.map +1 -0
- package/dist/commands/validate/service/events/events.js +219 -0
- package/dist/commands/validate/service/events/events.js.map +1 -0
- package/dist/commands/validate/service/events/message-producers.js +77 -0
- package/dist/commands/validate/service/events/message-producers.js.map +1 -0
- package/dist/commands/validate/service/evidence-pins.js +131 -0
- package/dist/commands/validate/service/evidence-pins.js.map +1 -0
- package/dist/commands/validate/service/service.js +250 -0
- package/dist/commands/validate/service/service.js.map +1 -0
- package/dist/commands/validate/service/specs.js +219 -0
- package/dist/commands/validate/service/specs.js.map +1 -0
- package/dist/commands/validate/service/spine.js +113 -0
- package/dist/commands/validate/service/spine.js.map +1 -0
- package/dist/commands/validate/validate.js +338 -0
- package/dist/commands/validate/validate.js.map +1 -0
- package/dist/commands/verify/cross/diff.js +182 -0
- package/dist/commands/verify/cross/diff.js.map +1 -0
- package/dist/commands/verify/evidence/contract.js +32 -0
- package/dist/commands/verify/evidence/contract.js.map +1 -0
- package/dist/commands/verify/evidence/pins.js +52 -0
- package/dist/commands/verify/evidence/pins.js.map +1 -0
- package/dist/commands/verify/evidence/read.js +72 -0
- package/dist/commands/verify/evidence/read.js.map +1 -0
- package/dist/commands/verify/frozen.js +241 -0
- package/dist/commands/verify/frozen.js.map +1 -0
- package/dist/commands/verify/record.js +226 -0
- package/dist/commands/verify/record.js.map +1 -0
- package/dist/commands/verify/report.js +223 -0
- package/dist/commands/verify/report.js.map +1 -0
- package/dist/commands/verify/results.js +246 -0
- package/dist/commands/verify/results.js.map +1 -0
- package/dist/commands/verify/verify.js +217 -0
- package/dist/commands/verify/verify.js.map +1 -0
- package/dist/commands/vouch/contract.js +11 -0
- package/dist/commands/vouch/contract.js.map +1 -0
- package/dist/commands/vouch/pack/contract.js +2 -0
- package/dist/commands/vouch/pack/contract.js.map +1 -0
- package/dist/commands/vouch/pack/pack.js +266 -0
- package/dist/commands/vouch/pack/pack.js.map +1 -0
- package/dist/commands/vouch/pack/print.js +271 -0
- package/dist/commands/vouch/pack/print.js.map +1 -0
- package/dist/commands/vouch/pack/sections.js +142 -0
- package/dist/commands/vouch/pack/sections.js.map +1 -0
- package/dist/commands/vouch/prompt/confirm.js +46 -0
- package/dist/commands/vouch/prompt/confirm.js.map +1 -0
- package/dist/commands/vouch/report.js +101 -0
- package/dist/commands/vouch/report.js.map +1 -0
- package/dist/commands/vouch/run.js +285 -0
- package/dist/commands/vouch/run.js.map +1 -0
- package/dist/commands/vouch/sample/plan.js +125 -0
- package/dist/commands/vouch/sample/plan.js.map +1 -0
- package/dist/commands/vouch/sample/print.js +109 -0
- package/dist/commands/vouch/sample/print.js.map +1 -0
- package/dist/commands/vouch/vet/verify.js +123 -0
- package/dist/commands/vouch/vet/verify.js.map +1 -0
- package/dist/commands/vouch/vouch.js +176 -0
- package/dist/commands/vouch/vouch.js.map +1 -0
- package/dist/core/agent/agents-md/artifacts.js +220 -0
- package/dist/core/agent/agents-md/artifacts.js.map +1 -0
- package/dist/core/agent/agents-md/command-map.js +324 -0
- package/dist/core/agent/agents-md/command-map.js.map +1 -0
- package/dist/core/agent/agents-md/cycle.js +165 -0
- package/dist/core/agent/agents-md/cycle.js.map +1 -0
- package/dist/core/agent/agents-md/map/explain.js +34 -0
- package/dist/core/agent/agents-md/map/explain.js.map +1 -0
- package/dist/core/agent/agents-md/map/lenses/context.js +41 -0
- package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -0
- package/dist/core/agent/agents-md/map/lenses/diff.js +46 -0
- package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -0
- package/dist/core/agent/agents-md/map/lenses/gate.js +57 -0
- package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -0
- package/dist/core/agent/agents-md/map/mcp.js +41 -0
- package/dist/core/agent/agents-md/map/mcp.js.map +1 -0
- package/dist/core/agent/agents-md/map/status.js +62 -0
- package/dist/core/agent/agents-md/map/status.js.map +1 -0
- package/dist/core/agent/agents-md/map/subsystem.js +32 -0
- package/dist/core/agent/agents-md/map/subsystem.js.map +1 -0
- package/dist/core/agent/agents-md/map/usecases.js +60 -0
- package/dist/core/agent/agents-md/map/usecases.js.map +1 -0
- package/dist/core/agent/agents-md/refusals.js +246 -0
- package/dist/core/agent/agents-md/refusals.js.map +1 -0
- package/dist/core/agent/agents-md/shipped/archive-gate.js +98 -0
- package/dist/core/agent/agents-md/shipped/archive-gate.js.map +1 -0
- package/dist/core/agent/agents-md/spine.js +40 -0
- package/dist/core/agent/agents-md/spine.js.map +1 -0
- package/dist/core/agent/agents-md.js +170 -0
- package/dist/core/agent/agents-md.js.map +1 -0
- package/dist/core/agent/agents-stamp.js +155 -0
- package/dist/core/agent/agents-stamp.js.map +1 -0
- package/dist/core/agent/contract.js +2 -0
- package/dist/core/agent/contract.js.map +1 -0
- package/dist/core/agent/protocol.js +193 -0
- package/dist/core/agent/protocol.js.map +1 -0
- package/dist/core/agent/scaffold.js +259 -0
- package/dist/core/agent/scaffold.js.map +1 -0
- package/dist/core/agent/support/report.js +103 -0
- package/dist/core/agent/support/report.js.map +1 -0
- package/dist/core/agent/tools/dialects.js +150 -0
- package/dist/core/agent/tools/dialects.js.map +1 -0
- package/dist/core/agent/tools/registry.js +228 -0
- package/dist/core/agent/tools/registry.js.map +1 -0
- package/dist/core/agent/workflows/adopt.js +133 -0
- package/dist/core/agent/workflows/adopt.js.map +1 -0
- package/dist/core/agent/workflows/check.js +334 -0
- package/dist/core/agent/workflows/check.js.map +1 -0
- package/dist/core/agent/workflows/closing.js +262 -0
- package/dist/core/agent/workflows/closing.js.map +1 -0
- package/dist/core/agent/workflows/feature.js +105 -0
- package/dist/core/agent/workflows/feature.js.map +1 -0
- package/dist/core/agent/workflows/implement.js +105 -0
- package/dist/core/agent/workflows/implement.js.map +1 -0
- package/dist/core/agent/workflows/reference/authoring.js +209 -0
- package/dist/core/agent/workflows/reference/authoring.js.map +1 -0
- package/dist/core/agent/workflows/reference/codes.js +65 -0
- package/dist/core/agent/workflows/reference/codes.js.map +1 -0
- package/dist/core/agent/workflows/reference/done-check.js +188 -0
- package/dist/core/agent/workflows/reference/done-check.js.map +1 -0
- package/dist/core/agent/workflows/reference/reference.js +16 -0
- package/dist/core/agent/workflows/reference/reference.js.map +1 -0
- package/dist/core/agent/workflows/reference/spine.js +297 -0
- package/dist/core/agent/workflows/reference/spine.js.map +1 -0
- package/dist/core/asyncapi/baseline/plan.js +110 -0
- package/dist/core/asyncapi/baseline/plan.js.map +1 -0
- package/dist/core/asyncapi/baseline/surfaces.js +54 -0
- package/dist/core/asyncapi/baseline/surfaces.js.map +1 -0
- package/dist/core/asyncapi/depth.js +164 -0
- package/dist/core/asyncapi/depth.js.map +1 -0
- package/dist/core/asyncapi/digest.js +141 -0
- package/dist/core/asyncapi/digest.js.map +1 -0
- package/dist/core/asyncapi/merge/components.js +91 -0
- package/dist/core/asyncapi/merge/components.js.map +1 -0
- package/dist/core/asyncapi/merge/error.js +33 -0
- package/dist/core/asyncapi/merge/error.js.map +1 -0
- package/dist/core/asyncapi/merge/markers.js +308 -0
- package/dist/core/asyncapi/merge/markers.js.map +1 -0
- package/dist/core/asyncapi/merge/merge.js +215 -0
- package/dist/core/asyncapi/merge/merge.js.map +1 -0
- package/dist/core/asyncapi/merge/pin.js +107 -0
- package/dist/core/asyncapi/merge/pin.js.map +1 -0
- package/dist/core/asyncapi/model.js +11 -0
- package/dist/core/asyncapi/model.js.map +1 -0
- package/dist/core/asyncapi/producers.js +50 -0
- package/dist/core/asyncapi/producers.js.map +1 -0
- package/dist/core/asyncapi/read.js +239 -0
- package/dist/core/asyncapi/read.js.map +1 -0
- package/dist/core/brief/brief.js +225 -0
- package/dist/core/brief/brief.js.map +1 -0
- package/dist/core/brief/checks.js +250 -0
- package/dist/core/brief/checks.js.map +1 -0
- package/dist/core/brief/landscape.js +194 -0
- package/dist/core/brief/landscape.js.map +1 -0
- package/dist/core/brief/targets.js +226 -0
- package/dist/core/brief/targets.js.map +1 -0
- package/dist/core/brief/unchecked.js +47 -0
- package/dist/core/brief/unchecked.js.map +1 -0
- package/dist/core/c4/arch.js +282 -0
- package/dist/core/c4/arch.js.map +1 -0
- package/dist/core/c4/likec4.js +88 -0
- package/dist/core/c4/likec4.js.map +1 -0
- package/dist/core/c4/parsed/dynamic-views.js +132 -0
- package/dist/core/c4/parsed/dynamic-views.js.map +1 -0
- package/dist/core/c4/parsed/specification.js +78 -0
- package/dist/core/c4/parsed/specification.js.map +1 -0
- package/dist/core/c4/parsed/values.js +57 -0
- package/dist/core/c4/parsed/values.js.map +1 -0
- package/dist/core/c4/parsed/view-ids.js +75 -0
- package/dist/core/c4/parsed/view-ids.js.map +1 -0
- package/dist/core/c4/project/architecture.js +45 -0
- package/dist/core/c4/project/architecture.js.map +1 -0
- package/dist/core/c4/project/documents.js +58 -0
- package/dist/core/c4/project/documents.js.map +1 -0
- package/dist/core/c4/project/load.js +183 -0
- package/dist/core/c4/project/load.js.map +1 -0
- package/dist/core/c4/resolve/service.js +113 -0
- package/dist/core/c4/resolve/service.js.map +1 -0
- package/dist/core/c4/seed/fleet-file.js +201 -0
- package/dist/core/c4/seed/fleet-file.js.map +1 -0
- package/dist/core/c4/seed/items.js +91 -0
- package/dist/core/c4/seed/items.js.map +1 -0
- package/dist/core/c4/seed/stamp.js +48 -0
- package/dist/core/c4/seed/stamp.js.map +1 -0
- package/dist/core/c4/seed/template.js +109 -0
- package/dist/core/c4/seed/template.js.map +1 -0
- package/dist/core/c4/source-mask.js +82 -0
- package/dist/core/c4/source-mask.js.map +1 -0
- package/dist/core/c4/source-scan.js +227 -0
- package/dist/core/c4/source-scan.js.map +1 -0
- package/dist/core/c4/splice/authored-source.js +101 -0
- package/dist/core/c4/splice/authored-source.js.map +1 -0
- package/dist/core/c4/splice/contract.js +10 -0
- package/dist/core/c4/splice/contract.js.map +1 -0
- package/dist/core/c4/splice/delta-blocks.js +126 -0
- package/dist/core/c4/splice/delta-blocks.js.map +1 -0
- package/dist/core/c4/splice/identity/edges.js +36 -0
- package/dist/core/c4/splice/identity/edges.js.map +1 -0
- package/dist/core/c4/splice/landscape-merge.js +269 -0
- package/dist/core/c4/splice/landscape-merge.js.map +1 -0
- package/dist/core/c4/splice/placement.js +194 -0
- package/dist/core/c4/splice/placement.js.map +1 -0
- package/dist/core/c4/workspace.js +172 -0
- package/dist/core/c4/workspace.js.map +1 -0
- package/dist/core/capabilities/authoring/notes.js +141 -0
- package/dist/core/capabilities/authoring/notes.js.map +1 -0
- package/dist/core/capabilities/capabilities.js +124 -0
- package/dist/core/capabilities/capabilities.js.map +1 -0
- package/dist/core/capabilities/delta/doc.js +50 -0
- package/dist/core/capabilities/delta/doc.js.map +1 -0
- package/dist/core/capabilities/delta/overlay.js +129 -0
- package/dist/core/capabilities/delta/overlay.js.map +1 -0
- package/dist/core/capabilities/delta/summary.js +57 -0
- package/dist/core/capabilities/delta/summary.js.map +1 -0
- package/dist/core/capabilities/delta/tree.js +43 -0
- package/dist/core/capabilities/delta/tree.js.map +1 -0
- package/dist/core/capabilities/delta/uncovered.js +222 -0
- package/dist/core/capabilities/delta/uncovered.js.map +1 -0
- package/dist/core/capabilities/findings.js +342 -0
- package/dist/core/capabilities/findings.js.map +1 -0
- package/dist/core/capabilities/realizes/findings.js +181 -0
- package/dist/core/capabilities/realizes/findings.js.map +1 -0
- package/dist/core/capabilities/realizes/join.js +143 -0
- package/dist/core/capabilities/realizes/join.js.map +1 -0
- package/dist/core/capabilities/rollup.js +199 -0
- package/dist/core/capabilities/rollup.js.map +1 -0
- package/dist/core/capabilities/tree.js +126 -0
- package/dist/core/capabilities/tree.js.map +1 -0
- package/dist/core/capabilities/usecase-join.js +277 -0
- package/dist/core/capabilities/usecase-join.js.map +1 -0
- package/dist/core/coherence/authoring/scaffold.js +177 -0
- package/dist/core/coherence/authoring/scaffold.js.map +1 -0
- package/dist/core/coherence/authoring/sentinels.js +74 -0
- package/dist/core/coherence/authoring/sentinels.js.map +1 -0
- package/dist/core/coherence/coherence.js +251 -0
- package/dist/core/coherence/coherence.js.map +1 -0
- package/dist/core/coherence/declared.js +269 -0
- package/dist/core/coherence/declared.js.map +1 -0
- package/dist/core/coherence/events/declared.js +206 -0
- package/dist/core/coherence/events/declared.js.map +1 -0
- package/dist/core/coherence/events/events.js +204 -0
- package/dist/core/coherence/events/events.js.map +1 -0
- package/dist/core/coherence/events/grades.js +166 -0
- package/dist/core/coherence/events/grades.js.map +1 -0
- package/dist/core/coherence/events/lookups.js +122 -0
- package/dist/core/coherence/events/lookups.js.map +1 -0
- package/dist/core/coherence/events/merged.js +40 -0
- package/dist/core/coherence/events/merged.js.map +1 -0
- package/dist/core/coherence/living.js +198 -0
- package/dist/core/coherence/living.js.map +1 -0
- package/dist/core/coherence/lookups.js +155 -0
- package/dist/core/coherence/lookups.js.map +1 -0
- package/dist/core/coherence/pending.js +39 -0
- package/dist/core/coherence/pending.js.map +1 -0
- package/dist/core/conflict-markers.js +60 -0
- package/dist/core/conflict-markers.js.map +1 -0
- package/dist/core/delta/claims.js +73 -0
- package/dist/core/delta/claims.js.map +1 -0
- package/dist/core/delta/delta.js +296 -0
- package/dist/core/delta/delta.js.map +1 -0
- package/dist/core/delta/document.js +177 -0
- package/dist/core/delta/document.js.map +1 -0
- package/dist/core/delta/scope.js +48 -0
- package/dist/core/delta/scope.js.map +1 -0
- package/dist/core/delta/select.js +237 -0
- package/dist/core/delta/select.js.map +1 -0
- package/dist/core/dependencies/dependencies.js +141 -0
- package/dist/core/dependencies/dependencies.js.map +1 -0
- package/dist/core/dependencies/facts.js +165 -0
- package/dist/core/dependencies/facts.js.map +1 -0
- package/dist/core/dependencies/fanin.js +88 -0
- package/dist/core/dependencies/fanin.js.map +1 -0
- package/dist/core/dependencies/graph.js +106 -0
- package/dist/core/dependencies/graph.js.map +1 -0
- package/dist/core/diff/base-git.js +148 -0
- package/dist/core/diff/base-git.js.map +1 -0
- package/dist/core/diff/base-state.js +209 -0
- package/dist/core/diff/base-state.js.map +1 -0
- package/dist/core/diff/current-state.js +96 -0
- package/dist/core/diff/current-state.js.map +1 -0
- package/dist/core/diff/scope/changed-paths.js +188 -0
- package/dist/core/diff/scope/changed-paths.js.map +1 -0
- package/dist/core/diff/semantic.js +243 -0
- package/dist/core/diff/semantic.js.map +1 -0
- package/dist/core/diff/victims.js +247 -0
- package/dist/core/diff/victims.js.map +1 -0
- package/dist/core/docs.js +59 -55
- package/dist/core/docs.js.map +1 -1
- package/dist/core/doctor/agents.js +222 -0
- package/dist/core/doctor/agents.js.map +1 -0
- package/dist/core/doctor/config.js +135 -0
- package/dist/core/doctor/config.js.map +1 -0
- package/dist/core/doctor/doctor.js +275 -0
- package/dist/core/doctor/doctor.js.map +1 -0
- package/dist/core/doctor/report.js +13 -0
- package/dist/core/doctor/report.js.map +1 -0
- package/dist/core/doctor/residue.js +110 -0
- package/dist/core/doctor/residue.js.map +1 -0
- package/dist/core/document/apply.js +77 -0
- package/dist/core/document/apply.js.map +1 -0
- package/dist/core/{frontmatter.js → document/frontmatter.js} +48 -2
- package/dist/core/document/frontmatter.js.map +1 -0
- package/dist/core/document/grammar/directives.js +143 -0
- package/dist/core/document/grammar/directives.js.map +1 -0
- package/dist/core/document/parse.js +263 -0
- package/dist/core/document/parse.js.map +1 -0
- package/dist/core/document/scenarios.js +221 -0
- package/dist/core/document/scenarios.js.map +1 -0
- package/dist/core/document/spec.js +241 -0
- package/dist/core/document/spec.js.map +1 -0
- package/dist/core/{config.js → envelope/config.js} +79 -18
- package/dist/core/envelope/config.js.map +1 -0
- package/dist/core/envelope/json.js +175 -0
- package/dist/core/envelope/json.js.map +1 -0
- package/dist/core/{version.js → envelope/version.js} +3 -3
- package/dist/core/envelope/version.js.map +1 -0
- package/dist/core/explain/families.js +175 -0
- package/dist/core/explain/families.js.map +1 -0
- package/dist/core/explain/fix-tables.js +178 -0
- package/dist/core/explain/fix-tables.js.map +1 -0
- package/dist/core/explain/lookup.js +181 -0
- package/dist/core/explain/lookup.js.map +1 -0
- package/dist/core/explain/refusals.js +49 -0
- package/dist/core/explain/refusals.js.map +1 -0
- package/dist/core/explain/terms.js +144 -0
- package/dist/core/explain/terms.js.map +1 -0
- package/dist/core/explore/describe.js +105 -0
- package/dist/core/explore/describe.js.map +1 -0
- package/dist/core/{explore.js → explore/explore.js} +50 -101
- package/dist/core/explore/explore.js.map +1 -0
- package/dist/core/fleet-context.js +132 -77
- package/dist/core/fleet-context.js.map +1 -1
- package/dist/core/gate/checks.js +201 -0
- package/dist/core/gate/checks.js.map +1 -0
- package/dist/core/gate/partners.js +205 -0
- package/dist/core/gate/partners.js.map +1 -0
- package/dist/core/gate/report.js +47 -0
- package/dist/core/gate/report.js.map +1 -0
- package/dist/core/gate/verification.js +113 -0
- package/dist/core/gate/verification.js.map +1 -0
- package/dist/core/gherkin/digest.js +60 -0
- package/dist/core/gherkin/digest.js.map +1 -0
- package/dist/core/gherkin/emit.js +177 -0
- package/dist/core/gherkin/emit.js.map +1 -0
- package/dist/core/gherkin/read.js +76 -0
- package/dist/core/gherkin/read.js.map +1 -0
- package/dist/core/gherkin/stale.js +234 -0
- package/dist/core/gherkin/stale.js.map +1 -0
- package/dist/core/gherkin/stamp.js +77 -0
- package/dist/core/gherkin/stamp.js.map +1 -0
- package/dist/core/gherkin/steps/catalogue.js +154 -0
- package/dist/core/gherkin/steps/catalogue.js.map +1 -0
- package/dist/core/gherkin/steps/inventory.js +98 -0
- package/dist/core/gherkin/steps/inventory.js.map +1 -0
- package/dist/core/gherkin/steps/phrase.js +72 -0
- package/dist/core/gherkin/steps/phrase.js.map +1 -0
- package/dist/core/glossary/delta.js +106 -0
- package/dist/core/glossary/delta.js.map +1 -0
- package/dist/core/glossary/tree.js +86 -0
- package/dist/core/glossary/tree.js.map +1 -0
- package/dist/core/kernel/concurrency.js.map +1 -0
- package/dist/core/{document-bytes.js → kernel/document-bytes.js} +1 -1
- package/dist/core/kernel/document-bytes.js.map +1 -0
- package/dist/core/kernel/fences.js +42 -0
- package/dist/core/kernel/fences.js.map +1 -0
- package/dist/core/kernel/ids/capability.js +72 -0
- package/dist/core/kernel/ids/capability.js.map +1 -0
- package/dist/core/kernel/ids/dirs.js +33 -0
- package/dist/core/kernel/ids/dirs.js.map +1 -0
- package/dist/core/kernel/ids/feature.js +75 -0
- package/dist/core/kernel/ids/feature.js.map +1 -0
- package/dist/core/kernel/ids/service.js +156 -0
- package/dist/core/kernel/ids/service.js.map +1 -0
- package/dist/core/kernel/ids/subsystem.js +63 -0
- package/dist/core/kernel/ids/subsystem.js.map +1 -0
- package/dist/core/{path-safety.js → kernel/path-safety.js} +18 -4
- package/dist/core/kernel/path-safety.js.map +1 -0
- package/dist/core/kernel/records.js.map +1 -0
- package/dist/core/links/backlinks.js +68 -0
- package/dist/core/links/backlinks.js.map +1 -0
- package/dist/core/links/case.js +74 -0
- package/dist/core/links/case.js.map +1 -0
- package/dist/core/links/corpus.js +104 -0
- package/dist/core/links/corpus.js.map +1 -0
- package/dist/core/links/findings.js +110 -0
- package/dist/core/links/findings.js.map +1 -0
- package/dist/core/links/parse.js +236 -0
- package/dist/core/links/parse.js.map +1 -0
- package/dist/core/mcp/argv.js +111 -0
- package/dist/core/mcp/argv.js.map +1 -0
- package/dist/core/mcp/author-tools.js +72 -0
- package/dist/core/mcp/author-tools.js.map +1 -0
- package/dist/core/mcp/framing.js +46 -0
- package/dist/core/mcp/framing.js.map +1 -0
- package/dist/core/mcp/protocol.js +280 -0
- package/dist/core/mcp/protocol.js.map +1 -0
- package/dist/core/mcp/tools.js +314 -0
- package/dist/core/mcp/tools.js.map +1 -0
- package/dist/core/obligations/obligations.js +106 -0
- package/dist/core/obligations/obligations.js.map +1 -0
- package/dist/core/openapi/baseline/gate.js +166 -0
- package/dist/core/openapi/baseline/gate.js.map +1 -0
- package/dist/core/openapi/baseline/plan.js +112 -0
- package/dist/core/openapi/baseline/plan.js.map +1 -0
- package/dist/core/openapi/baseline/record.js +175 -0
- package/dist/core/openapi/baseline/record.js.map +1 -0
- package/dist/core/openapi/depth.js +122 -0
- package/dist/core/openapi/depth.js.map +1 -0
- package/dist/core/openapi/digest.js +189 -0
- package/dist/core/openapi/digest.js.map +1 -0
- package/dist/core/{openapi.js → openapi/doc.js} +44 -106
- package/dist/core/openapi/doc.js.map +1 -0
- package/dist/core/openapi/merge/components.js +234 -0
- package/dist/core/openapi/merge/components.js.map +1 -0
- package/dist/core/openapi/merge/error.js +26 -0
- package/dist/core/openapi/merge/error.js.map +1 -0
- package/dist/core/openapi/merge/markers.js +208 -0
- package/dist/core/openapi/merge/markers.js.map +1 -0
- package/dist/core/openapi/merge/merge.js +256 -0
- package/dist/core/openapi/merge/merge.js.map +1 -0
- package/dist/core/openapi/merge/pin.js +128 -0
- package/dist/core/openapi/merge/pin.js.map +1 -0
- package/dist/core/openspec/decide/capabilities.js +244 -0
- package/dist/core/openspec/decide/capabilities.js.map +1 -0
- package/dist/core/openspec/decide/dispositions.js +63 -0
- package/dist/core/openspec/decide/dispositions.js.map +1 -0
- package/dist/core/openspec/decide/renames.js +179 -0
- package/dist/core/openspec/decide/renames.js.map +1 -0
- package/dist/core/openspec/decide/verdict.js +84 -0
- package/dist/core/openspec/decide/verdict.js.map +1 -0
- package/dist/core/openspec/inventory.js +221 -0
- package/dist/core/openspec/inventory.js.map +1 -0
- package/dist/core/openspec/model/mapping.js +76 -0
- package/dist/core/openspec/model/mapping.js.map +1 -0
- package/dist/core/openspec/model/model.js +65 -0
- package/dist/core/openspec/model/model.js.map +1 -0
- package/dist/core/openspec/scan/changes.js +235 -0
- package/dist/core/openspec/scan/changes.js.map +1 -0
- package/dist/core/openspec/scan/config.js +200 -0
- package/dist/core/openspec/scan/config.js.map +1 -0
- package/dist/core/openspec/scan/shape.js +260 -0
- package/dist/core/openspec/scan/shape.js.map +1 -0
- package/dist/core/openspec/scan/walk.js +133 -0
- package/dist/core/openspec/scan/walk.js.map +1 -0
- package/dist/core/openspec/scan/workspace.js +71 -0
- package/dist/core/openspec/scan/workspace.js.map +1 -0
- package/dist/core/owners/codeowners.js +141 -0
- package/dist/core/owners/codeowners.js.map +1 -0
- package/dist/core/pack/features.js +120 -0
- package/dist/core/pack/features.js.map +1 -0
- package/dist/core/pack/joins.js +112 -0
- package/dist/core/pack/joins.js.map +1 -0
- package/dist/core/pack/living.js +118 -0
- package/dist/core/pack/living.js.map +1 -0
- package/dist/core/pack/pack.js +76 -0
- package/dist/core/pack/pack.js.map +1 -0
- package/dist/core/permissions/permissions.js +87 -0
- package/dist/core/permissions/permissions.js.map +1 -0
- package/dist/core/projection/api.js +84 -0
- package/dist/core/projection/api.js.map +1 -0
- package/dist/core/projection/arch-slice.js +63 -0
- package/dist/core/projection/arch-slice.js.map +1 -0
- package/dist/core/projection/events.js +34 -0
- package/dist/core/projection/events.js.map +1 -0
- package/dist/core/provenance/findings.js +208 -0
- package/dist/core/provenance/findings.js.map +1 -0
- package/dist/core/provenance/git.js +225 -0
- package/dist/core/provenance/git.js.map +1 -0
- package/dist/core/provenance/gitq/moves.js +182 -0
- package/dist/core/provenance/gitq/moves.js.map +1 -0
- package/dist/core/provenance/gitq/vouched-ancestor.js +179 -0
- package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -0
- package/dist/core/provenance/sample/scope.js +238 -0
- package/dist/core/provenance/sample/scope.js.map +1 -0
- package/dist/core/provenance/sample/sections.js +61 -0
- package/dist/core/provenance/sample/sections.js.map +1 -0
- package/dist/core/provenance/sources.js +263 -0
- package/dist/core/provenance/sources.js.map +1 -0
- package/dist/core/provenance/stamp.js +232 -0
- package/dist/core/provenance/stamp.js.map +1 -0
- package/dist/core/provenance/walk.js +157 -0
- package/dist/core/provenance/walk.js.map +1 -0
- package/dist/core/repo/authored/paths.js +192 -0
- package/dist/core/repo/authored/paths.js.map +1 -0
- package/dist/core/repo/entries.js +126 -0
- package/dist/core/repo/entries.js.map +1 -0
- package/dist/core/repo/paths.js +236 -0
- package/dist/core/repo/paths.js.map +1 -0
- package/dist/core/repo/repo.js +248 -0
- package/dist/core/repo/repo.js.map +1 -0
- package/dist/core/repo/service-target.js +153 -0
- package/dist/core/repo/service-target.js.map +1 -0
- package/dist/core/repo/state.js +58 -0
- package/dist/core/repo/state.js.map +1 -0
- package/dist/core/repo/tree/find.js +52 -0
- package/dist/core/repo/tree/find.js.map +1 -0
- package/dist/core/repo/tree/fs.js +64 -0
- package/dist/core/repo/tree/fs.js.map +1 -0
- package/dist/core/repo/tree/marker.js +61 -0
- package/dist/core/repo/tree/marker.js.map +1 -0
- package/dist/core/repo/tree/views.js +172 -0
- package/dist/core/repo/tree/views.js.map +1 -0
- package/dist/core/repo/tree/walk.js +234 -0
- package/dist/core/repo/tree/walk.js.map +1 -0
- package/dist/core/results.js +99 -2
- package/dist/core/results.js.map +1 -1
- package/dist/core/scaffold/landscape.js +131 -0
- package/dist/core/scaffold/landscape.js.map +1 -0
- package/dist/core/scaffold/readme.js +84 -0
- package/dist/core/scaffold/readme.js.map +1 -0
- package/dist/core/staging/commit.js +241 -0
- package/dist/core/staging/commit.js.map +1 -0
- package/dist/core/staging/interrupted.js +96 -0
- package/dist/core/staging/interrupted.js.map +1 -0
- package/dist/core/staging/lock.js +206 -0
- package/dist/core/staging/lock.js.map +1 -0
- package/dist/core/staging/recovery/finding.js +35 -0
- package/dist/core/staging/recovery/finding.js.map +1 -0
- package/dist/core/staging/recovery/intent.js +98 -0
- package/dist/core/staging/recovery/intent.js.map +1 -0
- package/dist/core/staging/recovery/recover.js +126 -0
- package/dist/core/staging/recovery/recover.js.map +1 -0
- package/dist/core/staging/recovery/residue.js +62 -0
- package/dist/core/staging/recovery/residue.js.map +1 -0
- package/dist/core/staging/snapshot.js +202 -0
- package/dist/core/staging/snapshot.js.map +1 -0
- package/dist/core/staging/txn/forward.js +171 -0
- package/dist/core/staging/txn/forward.js.map +1 -0
- package/dist/core/staging/txn/journal.js +139 -0
- package/dist/core/staging/txn/journal.js.map +1 -0
- package/dist/core/staging/txn/transaction.js +75 -0
- package/dist/core/staging/txn/transaction.js.map +1 -0
- package/dist/core/staging/writes.js +113 -0
- package/dist/core/staging/writes.js.map +1 -0
- package/dist/core/status/actions/execution.js +49 -0
- package/dist/core/status/actions/execution.js.map +1 -0
- package/dist/core/status/contracts.js +102 -0
- package/dist/core/status/contracts.js.map +1 -0
- package/dist/core/status/feature/artifacts.js +171 -0
- package/dist/core/status/feature/artifacts.js.map +1 -0
- package/dist/core/status/feature/feature.js +207 -0
- package/dist/core/status/feature/feature.js.map +1 -0
- package/dist/core/status/feature/next.js +150 -0
- package/dist/core/status/feature/next.js.map +1 -0
- package/dist/core/status/feature/state.js +21 -0
- package/dist/core/status/feature/state.js.map +1 -0
- package/dist/core/status/fleet/fleet.js +187 -0
- package/dist/core/status/fleet/fleet.js.map +1 -0
- package/dist/core/status/fleet/next.js +207 -0
- package/dist/core/status/fleet/next.js.map +1 -0
- package/dist/core/status/interrupted.js +97 -0
- package/dist/core/status/interrupted.js.map +1 -0
- package/dist/core/status/report.js +31 -0
- package/dist/core/status/report.js.map +1 -0
- package/dist/core/status/scan.js +41 -0
- package/dist/core/status/scan.js.map +1 -0
- package/dist/core/status/verification.js +201 -0
- package/dist/core/status/verification.js.map +1 -0
- package/dist/core/usecases/capability.js +169 -0
- package/dist/core/usecases/capability.js.map +1 -0
- package/dist/core/usecases/delta/claims.js +116 -0
- package/dist/core/usecases/delta/claims.js.map +1 -0
- package/dist/core/usecases/delta/flows.js +103 -0
- package/dist/core/usecases/delta/flows.js.map +1 -0
- package/dist/core/usecases/delta/overlay.js +235 -0
- package/dist/core/usecases/delta/overlay.js.map +1 -0
- package/dist/core/usecases/fleet.js +137 -0
- package/dist/core/usecases/fleet.js.map +1 -0
- package/dist/core/usecases/operations.js +111 -0
- package/dist/core/usecases/operations.js.map +1 -0
- package/dist/core/usecases/place.js +44 -0
- package/dist/core/usecases/place.js.map +1 -0
- package/dist/core/usecases/touch.js +101 -0
- package/dist/core/usecases/touch.js.map +1 -0
- package/dist/core/verify/answers.js +173 -0
- package/dist/core/verify/answers.js.map +1 -0
- package/dist/core/verify/build.js +150 -0
- package/dist/core/verify/build.js.map +1 -0
- package/dist/core/verify/checklist.js +201 -0
- package/dist/core/verify/checklist.js.map +1 -0
- package/dist/core/verify/claims/identity.js +61 -0
- package/dist/core/verify/claims/identity.js.map +1 -0
- package/dist/core/verify/cross/diff.js +97 -0
- package/dist/core/verify/cross/diff.js.map +1 -0
- package/dist/core/verify/evidence/contract.js +169 -0
- package/dist/core/verify/evidence/contract.js.map +1 -0
- package/dist/core/verify/file.js +257 -0
- package/dist/core/verify/file.js.map +1 -0
- package/dist/core/verify/pins/lint.js +157 -0
- package/dist/core/verify/pins/lint.js.map +1 -0
- package/dist/core/verify/pins/pin.js +95 -0
- package/dist/core/verify/pins/pin.js.map +1 -0
- package/dist/core/verify/record.js +142 -0
- package/dist/core/verify/record.js.map +1 -0
- package/dist/core/verify/store/commit.js +89 -0
- package/dist/core/verify/store/commit.js.map +1 -0
- package/dist/core/verify/store/render.js +57 -0
- package/dist/core/verify/store/render.js.map +1 -0
- package/dist/core/vocabulary/health.js +91 -0
- package/dist/core/vocabulary/health.js.map +1 -0
- package/dist/core/vocabulary/issue.js +55 -0
- package/dist/core/vocabulary/issue.js.map +1 -0
- package/dist/core/vocabulary/maturity.js +145 -0
- package/dist/core/vocabulary/maturity.js.map +1 -0
- package/dist/core/{report.js → vocabulary/report.js} +30 -2
- package/dist/core/vocabulary/report.js.map +1 -0
- package/dist/core/vocabulary/steps.js +266 -0
- package/dist/core/vocabulary/steps.js.map +1 -0
- package/dist/core/workspace/discover.js +239 -0
- package/dist/core/workspace/discover.js.map +1 -0
- package/dist/core/workspace/render.js +52 -0
- package/dist/core/workspace/render.js.map +1 -0
- package/examples/README.md +189 -0
- package/examples/docs/architecture/adrs/0001-transactional-outbox.md +33 -0
- package/examples/docs/architecture/capabilities.yaml +31 -0
- package/examples/docs/architecture/landscape.likec4 +280 -0
- package/examples/docs/architecture/obligations.yaml +23 -0
- package/examples/docs/architecture/permissions.yaml +50 -0
- package/examples/docs/architecture/subsystems.likec4 +11 -0
- package/examples/docs/architecture/usecases/order-notification.likec4 +65 -0
- package/examples/docs/capabilities/checkout/spec.md +55 -0
- package/examples/docs/capabilities/identity/tokens/spec.md +42 -0
- package/examples/docs/capabilities/order-notifications/spec.md +43 -0
- package/examples/docs/features/FEAT-101-payment-splitting/adrs/0001-dedicated-split-service.md +18 -0
- package/examples/docs/features/FEAT-101-payment-splitting/delta.likec4 +94 -0
- package/examples/docs/features/FEAT-101-payment-splitting/intent.md +44 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/checkout-web/spec.md +21 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +129 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/spec.md +36 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/arch.spec.md +40 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +116 -0
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/spec.md +55 -0
- package/examples/docs/features/FEAT-112-retire-order-v1/intent.md +35 -0
- package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/openapi.yaml +28 -0
- package/examples/docs/features/FEAT-112-retire-order-v1/specs/order-service/spec.md +17 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/architecture/landscape.likec4 +253 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/order-service/spec.md +121 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/openapi.yaml +111 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/files/services/payment-service/spec.md +58 -0
- package/examples/docs/features/archive/FEAT-088-refunds/.loam-before/manifest.json +32 -0
- package/examples/docs/features/archive/FEAT-088-refunds/adrs/0001-refunds-stay-in-payment-service.md +26 -0
- package/examples/docs/features/archive/FEAT-088-refunds/delta.likec4 +28 -0
- package/examples/docs/features/archive/FEAT-088-refunds/intent.md +26 -0
- package/examples/docs/features/archive/FEAT-088-refunds/specs/order-service/spec.md +37 -0
- package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +70 -0
- package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/spec.md +23 -0
- package/examples/docs/features/archive/FEAT-088-refunds/verification.yaml +76 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/architecture/landscape.likec4 +275 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/asyncapi.yaml +96 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/payment-service/spec.md +82 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/asyncapi.yaml +164 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/files/services/platform/notification-service/spec.md +91 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/.loam-before/manifest.json +46 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/delta.likec4 +36 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/intent.md +22 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/scenario-report.json +10 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/asyncapi.yaml +208 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/notification-service/spec.md +24 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/asyncapi.yaml +137 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/specs/payment-service/spec.md +31 -0
- package/examples/docs/features/archive/FEAT-120-refund-notification/verification.yaml +84 -0
- package/examples/docs/glossary/order.md +15 -0
- package/examples/docs/glossary/payments/authorization.md +14 -0
- package/examples/docs/likec4.config.json +9 -0
- package/examples/docs/loam.json +3 -0
- package/examples/docs/services/checkout-web/model.likec4 +109 -0
- package/examples/docs/services/checkout-web/spec.md +91 -0
- package/examples/docs/services/checkout-web/ui/pages/checkout.page.yaml +40 -0
- package/examples/docs/services/checkout-web/ui/pages/order-status.page.yaml +24 -0
- package/examples/docs/services/checkout-web/ui/pages/split-payment.page.yaml +26 -0
- package/examples/docs/services/order-service/adrs/0001-orchestrated-order-lifecycle.md +52 -0
- package/examples/docs/services/order-service/arch.spec.md +127 -0
- package/examples/docs/services/order-service/asyncapi.yaml +108 -0
- package/examples/docs/services/order-service/health.yaml +57 -0
- package/examples/docs/services/order-service/model.likec4 +138 -0
- package/examples/docs/services/order-service/openapi.yaml +257 -0
- package/examples/docs/services/order-service/runbook.md +62 -0
- package/examples/docs/services/order-service/spec.md +134 -0
- package/examples/docs/services/payment-service/adrs/0001-payments-outbox.md +18 -0
- package/examples/docs/services/payment-service/adrs/0002-idempotency-keys-on-authorization.md +27 -0
- package/examples/docs/services/payment-service/arch.spec.md +73 -0
- package/examples/docs/services/payment-service/asyncapi.yaml +130 -0
- package/examples/docs/services/payment-service/health.yaml +39 -0
- package/examples/docs/services/payment-service/model.likec4 +67 -0
- package/examples/docs/services/payment-service/openapi.yaml +151 -0
- package/examples/docs/services/payment-service/runbook.md +37 -0
- package/examples/docs/services/payment-service/spec.md +88 -0
- package/examples/docs/services/platform/identity-service/adrs/0001-token-introspection-over-local-validation.md +60 -0
- package/examples/docs/services/platform/identity-service/arch.spec.md +117 -0
- package/examples/docs/services/platform/identity-service/health.yaml +70 -0
- package/examples/docs/services/platform/identity-service/model.likec4 +83 -0
- package/examples/docs/services/platform/identity-service/openapi.yaml +197 -0
- package/examples/docs/services/platform/identity-service/runbook.md +95 -0
- package/examples/docs/services/platform/identity-service/spec.md +119 -0
- package/examples/docs/services/platform/notification-service/asyncapi.yaml +199 -0
- package/examples/docs/services/platform/notification-service/health.yaml +35 -0
- package/examples/docs/services/platform/notification-service/model.likec4 +118 -0
- package/examples/docs/services/platform/notification-service/runbook.md +48 -0
- package/examples/docs/services/platform/notification-service/spec.md +112 -0
- package/examples/docs/services/platform/subsystem.yaml +2 -0
- package/examples/fleet.yaml +57 -0
- package/package.json +19 -6
- package/dist/commands/adopt.js +0 -240
- package/dist/commands/adopt.js.map +0 -1
- package/dist/commands/archive.js +0 -1313
- package/dist/commands/archive.js.map +0 -1
- package/dist/commands/delta.js +0 -388
- package/dist/commands/delta.js.map +0 -1
- package/dist/commands/docs-repo-gate.js.map +0 -1
- package/dist/commands/format.js +0 -17
- package/dist/commands/format.js.map +0 -1
- package/dist/commands/gherkin.js +0 -309
- package/dist/commands/gherkin.js.map +0 -1
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/list.js +0 -291
- package/dist/commands/list.js.map +0 -1
- package/dist/commands/migrate-openspec.js +0 -950
- package/dist/commands/migrate-openspec.js.map +0 -1
- package/dist/commands/new.js +0 -413
- package/dist/commands/new.js.map +0 -1
- package/dist/commands/rebase.js +0 -356
- package/dist/commands/rebase.js.map +0 -1
- package/dist/commands/show.js.map +0 -1
- package/dist/commands/status.js +0 -254
- package/dist/commands/status.js.map +0 -1
- package/dist/commands/unarchive.js.map +0 -1
- package/dist/commands/validate.js +0 -1591
- package/dist/commands/validate.js.map +0 -1
- package/dist/commands/verify.js +0 -775
- package/dist/commands/verify.js.map +0 -1
- package/dist/commands/vouch.js +0 -343
- package/dist/commands/vouch.js.map +0 -1
- package/dist/core/agent.js +0 -2065
- package/dist/core/agent.js.map +0 -1
- package/dist/core/agents-stamp.js +0 -68
- package/dist/core/agents-stamp.js.map +0 -1
- package/dist/core/arch.js +0 -130
- package/dist/core/arch.js.map +0 -1
- package/dist/core/brief.js +0 -733
- package/dist/core/brief.js.map +0 -1
- package/dist/core/coherence.js +0 -577
- package/dist/core/coherence.js.map +0 -1
- package/dist/core/concurrency.js.map +0 -1
- package/dist/core/config.js.map +0 -1
- package/dist/core/delta.js +0 -476
- package/dist/core/delta.js.map +0 -1
- package/dist/core/dependencies.js +0 -356
- package/dist/core/dependencies.js.map +0 -1
- package/dist/core/doctor.js +0 -559
- package/dist/core/doctor.js.map +0 -1
- package/dist/core/document-bytes.js.map +0 -1
- package/dist/core/explore.js.map +0 -1
- package/dist/core/frontmatter.js.map +0 -1
- package/dist/core/gherkin.js +0 -463
- package/dist/core/gherkin.js.map +0 -1
- package/dist/core/health.js +0 -67
- package/dist/core/health.js.map +0 -1
- package/dist/core/ids.js +0 -122
- package/dist/core/ids.js.map +0 -1
- package/dist/core/issue.js +0 -9
- package/dist/core/issue.js.map +0 -1
- package/dist/core/json.js +0 -74
- package/dist/core/json.js.map +0 -1
- package/dist/core/likec4.js +0 -479
- package/dist/core/likec4.js.map +0 -1
- package/dist/core/maturity.js +0 -66
- package/dist/core/maturity.js.map +0 -1
- package/dist/core/openapi-merge.js +0 -552
- package/dist/core/openapi-merge.js.map +0 -1
- package/dist/core/openapi.js.map +0 -1
- package/dist/core/openspec-inventory.js +0 -1440
- package/dist/core/openspec-inventory.js.map +0 -1
- package/dist/core/path-safety.js.map +0 -1
- package/dist/core/provenance.js +0 -720
- package/dist/core/provenance.js.map +0 -1
- package/dist/core/records.js.map +0 -1
- package/dist/core/repo.js +0 -436
- package/dist/core/repo.js.map +0 -1
- package/dist/core/report.js.map +0 -1
- package/dist/core/spec.js +0 -481
- package/dist/core/spec.js.map +0 -1
- package/dist/core/staging.js +0 -890
- package/dist/core/staging.js.map +0 -1
- package/dist/core/status.js +0 -1104
- package/dist/core/status.js.map +0 -1
- package/dist/core/steps.js +0 -72
- package/dist/core/steps.js.map +0 -1
- package/dist/core/verify.js +0 -807
- package/dist/core/verify.js.map +0 -1
- package/dist/core/version.js.map +0 -1
- /package/dist/core/{concurrency.js → kernel/concurrency.js} +0 -0
- /package/dist/core/{records.js → kernel/records.js} +0 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The human view of an inventory, shared by both verbs.
|
|
3
|
+
*
|
|
4
|
+
* `audit-openspec` and `migrate-openspec` print the same block and differ only
|
|
5
|
+
* in the word above it, so the renderer takes the mode rather than existing
|
|
6
|
+
* twice — the audit's readiness line and the migration's are the same claim and
|
|
7
|
+
* have to stay the same claim.
|
|
8
|
+
*
|
|
9
|
+
* The local `plural` is not a duplicate of `../format.ts`'s. That one spells
|
|
10
|
+
* the regular English plural only, and says so: an irregular noun belongs at
|
|
11
|
+
* the call site. "capability"/"capabilities" is exactly that noun, so this
|
|
12
|
+
* renderer carries the two-form variant instead of misspelling a count.
|
|
13
|
+
*/
|
|
14
|
+
import {} from "../../core/openspec/model/model.js";
|
|
15
|
+
function plural(count, one, many = `${one}s`) {
|
|
16
|
+
return `${count} ${count === 1 ? one : many}`;
|
|
17
|
+
}
|
|
18
|
+
export function printInventory(inventory, mode) {
|
|
19
|
+
const status = inventory.ready
|
|
20
|
+
? "ready"
|
|
21
|
+
: inventory.mechanicallyCompatible
|
|
22
|
+
? "compatible; explicit mapping/identity decisions remain"
|
|
23
|
+
: "living or active blockers found";
|
|
24
|
+
console.log(`OpenSpec ${mode} inventory — ${status}`);
|
|
25
|
+
console.log(` root ${inventory.root}`);
|
|
26
|
+
console.log(` baseline release ${inventory.baselines.release.version}@${inventory.baselines.release.commit} · main canary ${inventory.baselines.mainCanary.commit.slice(0, 7)}`);
|
|
27
|
+
console.log(` living ${plural(inventory.living.capabilities.length, "capability", "capabilities")} · ${plural(inventory.living.specFiles, "spec file")} · ${plural(inventory.living.requirements, "requirement")} · ${plural(inventory.living.scenarios, "scenario")}`);
|
|
28
|
+
console.log(` changes ${inventory.changes.counts.active} active · ${inventory.changes.counts.archived} archived`);
|
|
29
|
+
console.log(` readiness living ${inventory.readiness.living.compatible ? "ok" : "blocked"} · active ${inventory.readiness.active.compatible ? "ok" : "blocked"} · source ${inventory.readiness.sourceCurrent ? "current" : "stale"} · capabilities ${inventory.readiness.mappingsResolved ? "resolved" : "open"} · changes ${inventory.readiness.changesResolved ? "resolved" : "open"} · dispositions ${inventory.readiness.dispositionsResolved ? "resolved" : "open"} · archive ${inventory.archiveDiagnostics.length} diagnostic(s)`);
|
|
30
|
+
if (inventory.needsMapping.length > 0) {
|
|
31
|
+
console.log("\n capability → service decisions");
|
|
32
|
+
for (const decision of inventory.needsMapping) {
|
|
33
|
+
console.log(` ? ${decision.capability} → ${decision.suggestedService} [${decision.status}]`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (inventory.needsChangeMapping.length > 0) {
|
|
37
|
+
console.log("\n change → feature decisions");
|
|
38
|
+
for (const decision of inventory.needsChangeMapping) {
|
|
39
|
+
console.log(` ? ${decision.change} → ${decision.suggestedFeature} [${decision.status}]`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const activeRenames = inventory.renamed.filter((rename) => rename.scope === "active");
|
|
43
|
+
if (activeRenames.length > 0) {
|
|
44
|
+
console.log("\n RENAMED identity decisions");
|
|
45
|
+
for (const rename of activeRenames) {
|
|
46
|
+
console.log(` ${rename.status === "mapped" ? "✓" : "?"} ${rename.key} ${rename.from ?? "<missing FROM>"} → ${rename.to ?? "<missing TO>"}${rename.requirementId ? ` [${rename.requirementId}]` : ""}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (inventory.needsDisposition.length > 0) {
|
|
50
|
+
console.log(`\n authored artifact decisions (${inventory.needsDisposition.length})`);
|
|
51
|
+
for (const decision of inventory.needsDisposition.slice(0, 12)) {
|
|
52
|
+
console.log(` ? ${decision.path} → ${decision.suggestedDisposition} [${decision.status}]`);
|
|
53
|
+
}
|
|
54
|
+
if (inventory.needsDisposition.length > 12) {
|
|
55
|
+
console.log(` … ${inventory.needsDisposition.length - 12} more in --json/mapping output`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (inventory.mappingIssues.length > 0) {
|
|
59
|
+
console.log("\n mapping issues");
|
|
60
|
+
for (const item of inventory.mappingIssues)
|
|
61
|
+
console.log(` ✗ ${item.code} ${item.message}`);
|
|
62
|
+
}
|
|
63
|
+
if (inventory.unsupported.length > 0) {
|
|
64
|
+
console.log("\n living/active blockers");
|
|
65
|
+
for (const item of inventory.unsupported) {
|
|
66
|
+
console.log(` ✗ ${item.code} ${item.path} — ${item.message}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (inventory.archiveDiagnostics.length > 0) {
|
|
70
|
+
console.log(`\n archive diagnostics (non-blocking, ${inventory.archiveDiagnostics.length})`);
|
|
71
|
+
for (const item of inventory.archiveDiagnostics.slice(0, 12)) {
|
|
72
|
+
console.log(` · ${item.code} ${item.path} — ${item.message}`);
|
|
73
|
+
}
|
|
74
|
+
if (inventory.archiveDiagnostics.length > 12) {
|
|
75
|
+
console.log(` … ${inventory.archiveDiagnostics.length - 12} more in --json output`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const dispositions = new Map();
|
|
79
|
+
for (const artifact of inventory.artifacts) {
|
|
80
|
+
dispositions.set(artifact.disposition, (dispositions.get(artifact.disposition) ?? 0) + 1);
|
|
81
|
+
}
|
|
82
|
+
if (dispositions.size > 0) {
|
|
83
|
+
console.log("\n artifact disposition");
|
|
84
|
+
for (const [disposition, count] of [...dispositions].sort(([a], [b]) => a.localeCompare(b))) {
|
|
85
|
+
console.log(` · ${disposition} ${count}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=print.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"print.js","sourceRoot":"","sources":["../../../src/commands/migrate-openspec/print.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The human view of an inventory, shared by both verbs.\n *\n * `audit-openspec` and `migrate-openspec` print the same block and differ only\n * in the word above it, so the renderer takes the mode rather than existing\n * twice — the audit's readiness line and the migration's are the same claim and\n * have to stay the same claim.\n *\n * The local `plural` is not a duplicate of `../format.ts`'s. That one spells\n * the regular English plural only, and says so: an irregular noun belongs at\n * the call site. \"capability\"/\"capabilities\" is exactly that noun, so this\n * renderer carries the two-form variant instead of misspelling a count.\n */\nimport { type OpenSpecInventory } from \"../../core/openspec/model/model.js\";\n\nfunction plural(count: number, one: string, many = `${one}s`): string {\n return `${count} ${count === 1 ? one : many}`;\n}\n\nexport function printInventory(inventory: OpenSpecInventory, mode: \"audit\" | \"migration\"): void {\n const status = inventory.ready\n ? \"ready\"\n : inventory.mechanicallyCompatible\n ? \"compatible; explicit mapping/identity decisions remain\"\n : \"living or active blockers found\";\n console.log(`OpenSpec ${mode} inventory — ${status}`);\n console.log(` root ${inventory.root}`);\n console.log(\n ` baseline release ${inventory.baselines.release.version}@${inventory.baselines.release.commit} · main canary ${inventory.baselines.mainCanary.commit.slice(0, 7)}`,\n );\n console.log(\n ` living ${plural(inventory.living.capabilities.length, \"capability\", \"capabilities\")} · ${plural(inventory.living.specFiles, \"spec file\")} · ${plural(inventory.living.requirements, \"requirement\")} · ${plural(inventory.living.scenarios, \"scenario\")}`,\n );\n console.log(\n ` changes ${inventory.changes.counts.active} active · ${inventory.changes.counts.archived} archived`,\n );\n console.log(\n ` readiness living ${inventory.readiness.living.compatible ? \"ok\" : \"blocked\"} · active ${inventory.readiness.active.compatible ? \"ok\" : \"blocked\"} · source ${inventory.readiness.sourceCurrent ? \"current\" : \"stale\"} · capabilities ${inventory.readiness.mappingsResolved ? \"resolved\" : \"open\"} · changes ${inventory.readiness.changesResolved ? \"resolved\" : \"open\"} · dispositions ${inventory.readiness.dispositionsResolved ? \"resolved\" : \"open\"} · archive ${inventory.archiveDiagnostics.length} diagnostic(s)`,\n );\n\n if (inventory.needsMapping.length > 0) {\n console.log(\"\\n capability → service decisions\");\n for (const decision of inventory.needsMapping) {\n console.log(` ? ${decision.capability} → ${decision.suggestedService} [${decision.status}]`);\n }\n }\n if (inventory.needsChangeMapping.length > 0) {\n console.log(\"\\n change → feature decisions\");\n for (const decision of inventory.needsChangeMapping) {\n console.log(` ? ${decision.change} → ${decision.suggestedFeature} [${decision.status}]`);\n }\n }\n const activeRenames = inventory.renamed.filter((rename) => rename.scope === \"active\");\n if (activeRenames.length > 0) {\n console.log(\"\\n RENAMED identity decisions\");\n for (const rename of activeRenames) {\n console.log(\n ` ${rename.status === \"mapped\" ? \"✓\" : \"?\"} ${rename.key} ${rename.from ?? \"<missing FROM>\"} → ${rename.to ?? \"<missing TO>\"}${rename.requirementId ? ` [${rename.requirementId}]` : \"\"}`,\n );\n }\n }\n if (inventory.needsDisposition.length > 0) {\n console.log(`\\n authored artifact decisions (${inventory.needsDisposition.length})`);\n for (const decision of inventory.needsDisposition.slice(0, 12)) {\n console.log(` ? ${decision.path} → ${decision.suggestedDisposition} [${decision.status}]`);\n }\n if (inventory.needsDisposition.length > 12) {\n console.log(` … ${inventory.needsDisposition.length - 12} more in --json/mapping output`);\n }\n }\n if (inventory.mappingIssues.length > 0) {\n console.log(\"\\n mapping issues\");\n for (const item of inventory.mappingIssues) console.log(` ✗ ${item.code} ${item.message}`);\n }\n if (inventory.unsupported.length > 0) {\n console.log(\"\\n living/active blockers\");\n for (const item of inventory.unsupported) {\n console.log(` ✗ ${item.code} ${item.path} — ${item.message}`);\n }\n }\n if (inventory.archiveDiagnostics.length > 0) {\n console.log(`\\n archive diagnostics (non-blocking, ${inventory.archiveDiagnostics.length})`);\n for (const item of inventory.archiveDiagnostics.slice(0, 12)) {\n console.log(` · ${item.code} ${item.path} — ${item.message}`);\n }\n if (inventory.archiveDiagnostics.length > 12) {\n console.log(` … ${inventory.archiveDiagnostics.length - 12} more in --json output`);\n }\n }\n const dispositions = new Map<string, number>();\n for (const artifact of inventory.artifacts) {\n dispositions.set(artifact.disposition, (dispositions.get(artifact.disposition) ?? 0) + 1);\n }\n if (dispositions.size > 0) {\n console.log(\"\\n artifact disposition\");\n for (const [disposition, count] of [...dispositions].sort(([a], [b]) => a.localeCompare(b))) {\n console.log(` · ${disposition} ${count}`);\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The scaffold's commit window: lock, recover, the one existence check, the
|
|
3
|
+
* exclusive-create transaction. Split from new.ts on the same seam vouch's
|
|
4
|
+
* commitStamp and gherkin's commitEmission sit on — everything in here holds
|
|
5
|
+
* the docs lock, nothing outside it does — and, as there, the split keeps the
|
|
6
|
+
* window's extent visible in the code shape. This module prints nothing; the
|
|
7
|
+
* caller renders every arm.
|
|
8
|
+
*/
|
|
9
|
+
import {} from "../../core/repo/entries.js";
|
|
10
|
+
import {} from "../../core/kernel/ids/dirs.js";
|
|
11
|
+
import { resolveFeature } from "../../core/repo/repo.js";
|
|
12
|
+
import { DocsRepoUnavailableError } from "../../core/repo/state.js";
|
|
13
|
+
import { stageWrites } from "../../core/staging/commit.js";
|
|
14
|
+
import { InterruptedCommitError } from "../../core/staging/interrupted.js";
|
|
15
|
+
import { acquireDocsLockWaiting, DocsBusyError, LOCK_WAIT_MS } from "../../core/staging/lock.js";
|
|
16
|
+
import { recoverInterruptedCommit } from "../../core/staging/recovery/recover.js";
|
|
17
|
+
import { commitStaged } from "../../core/staging/txn/transaction.js";
|
|
18
|
+
import {} from "../../core/staging/writes.js";
|
|
19
|
+
export async function commitScaffold(docsDir, featureId, writes) {
|
|
20
|
+
// The commit window holds the docs lock: two `new`s for one id serialise
|
|
21
|
+
// into a stable already-exists, and a predecessor's interrupted commit is
|
|
22
|
+
// rolled forward (or refused) before this one stages anything.
|
|
23
|
+
let releaseLock;
|
|
24
|
+
try {
|
|
25
|
+
releaseLock = await acquireDocsLockWaiting(docsDir, LOCK_WAIT_MS);
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
if (err instanceof DocsBusyError)
|
|
29
|
+
return { ok: false, code: "docs-busy", message: err.message };
|
|
30
|
+
throw err;
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
let recovered;
|
|
34
|
+
try {
|
|
35
|
+
recovered = await recoverInterruptedCommit(docsDir);
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
if (!(err instanceof InterruptedCommitError))
|
|
39
|
+
throw err;
|
|
40
|
+
return { ok: false, code: "commit-interrupted", message: err.message };
|
|
41
|
+
}
|
|
42
|
+
// The one existence check, UNDER the lock and AFTER recovery: two racing
|
|
43
|
+
// runs serialise on it, and a recovered half-scaffold has been completed
|
|
44
|
+
// by the recovery above, so this refusal is truthful.
|
|
45
|
+
let existing;
|
|
46
|
+
try {
|
|
47
|
+
existing = await resolveFeature(docsDir, featureId, "include");
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
// The command's gate already refused both broken states, so reaching
|
|
51
|
+
// here means the docs repo went away between that check and this read.
|
|
52
|
+
// Same breach, same two codes — the alternative is a stack trace under
|
|
53
|
+
// `internal` that names neither.
|
|
54
|
+
if (!(err instanceof DocsRepoUnavailableError))
|
|
55
|
+
throw err;
|
|
56
|
+
return { ok: false, repoGone: err };
|
|
57
|
+
}
|
|
58
|
+
if (existing)
|
|
59
|
+
return { ok: false, code: "already-exists", existing, recovered };
|
|
60
|
+
const staged = await stageWrites(writes);
|
|
61
|
+
const committed = await commitStaged({ root: docsDir, command: "new", rerun: `loam new ${featureId}`, target: featureId }, staged, "scaffolded");
|
|
62
|
+
if (!committed.ok) {
|
|
63
|
+
// A lost exclusive-create race IS the feature existing: the other `new`
|
|
64
|
+
// won, and this refusal is the same stable answer the check above gives
|
|
65
|
+
// when it sees the winner first.
|
|
66
|
+
return committed.raced
|
|
67
|
+
? { ok: false, code: "already-exists", existing: null, recovered }
|
|
68
|
+
: { ok: false, code: committed.code, message: committed.message };
|
|
69
|
+
}
|
|
70
|
+
return { ok: true, recovered };
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
await releaseLock();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=commit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commit.js","sourceRoot":"","sources":["../../../src/commands/new/commit.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAqB,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAgB,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAuB,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAChG,OAAO,EAAE,sBAAsB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AACjG,OAAO,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAC;AAClF,OAAO,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AACrE,OAAO,EAAqB,MAAM,8BAA8B,CAAC;AAmBjE,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAAgB,EAChB,SAAiB,EACjB,MAAsB;IAEtB,yEAAyE;IACzE,0EAA0E;IAC1E,+DAA+D;IAC/D,IAAI,WAAgC,CAAC;IACrC,IAAI,CAAC;QACH,WAAW,GAAG,MAAM,sBAAsB,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IACpE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,aAAa;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC;QAChG,MAAM,GAAG,CAAC;IACZ,CAAC;IACD,IAAI,CAAC;QACH,IAAI,SAAgC,CAAC;QACrC,IAAI,CAAC;YACH,SAAS,GAAG,MAAM,wBAAwB,CAAC,OAAO,CAAC,CAAC;QACtD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,CAAC,GAAG,YAAY,sBAAsB,CAAC;gBAAE,MAAM,GAAG,CAAC;YACxD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,oBAAoB,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC;QACzE,CAAC;QACD,yEAAyE;QACzE,yEAAyE;QACzE,sDAAsD;QACtD,IAAI,QAA6B,CAAC;QAClC,IAAI,CAAC;YACH,QAAQ,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;QACjE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,qEAAqE;YACrE,uEAAuE;YACvE,uEAAuE;YACvE,iCAAiC;YACjC,IAAI,CAAC,CAAC,GAAG,YAAY,wBAAwB,CAAC;gBAAE,MAAM,GAAG,CAAC;YAC1D,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC;QACtC,CAAC;QACD,IAAI,QAAQ;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,gBAAgB,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;QAChF,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;QACzC,MAAM,SAAS,GAAG,MAAM,YAAY,CAClC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,SAAS,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,EACpF,MAAM,EACN,YAAY,CACb,CAAC;QACF,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC;YAClB,wEAAwE;YACxE,wEAAwE;YACxE,iCAAiC;YACjC,OAAO,SAAS,CAAC,KAAK;gBACpB,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,gBAAgB,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE;gBAClE,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,CAAC,OAAO,EAAE,CAAC;QACtE,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IACjC,CAAC;YAAS,CAAC;QACT,MAAM,WAAW,EAAE,CAAC;IACtB,CAAC;AACH,CAAC","sourcesContent":["/**\n * The scaffold's commit window: lock, recover, the one existence check, the\n * exclusive-create transaction. Split from new.ts on the same seam vouch's\n * commitStamp and gherkin's commitEmission sit on — everything in here holds\n * the docs lock, nothing outside it does — and, as there, the split keeps the\n * window's extent visible in the code shape. This module prints nothing; the\n * caller renders every arm.\n */\nimport { type FeatureEntry } from \"../../core/repo/entries.js\";\nimport { type DocsDir } from \"../../core/kernel/ids/dirs.js\";\nimport { resolveFeature } from \"../../core/repo/repo.js\";\nimport { DocsRepoUnavailableError } from \"../../core/repo/state.js\";\nimport { stageWrites } from \"../../core/staging/commit.js\";\nimport { type CommitRecovery, InterruptedCommitError } from \"../../core/staging/interrupted.js\";\nimport { acquireDocsLockWaiting, DocsBusyError, LOCK_WAIT_MS } from \"../../core/staging/lock.js\";\nimport { recoverInterruptedCommit } from \"../../core/staging/recovery/recover.js\";\nimport { commitStaged } from \"../../core/staging/txn/transaction.js\";\nimport { type PlannedWrite } from \"../../core/staging/writes.js\";\n\nexport type ScaffoldCommit =\n | { ok: true; recovered: CommitRecovery | null }\n | {\n ok: false;\n code: \"docs-busy\" | \"commit-interrupted\" | \"merge-failed\" | \"rollback-incomplete\";\n message: string;\n }\n /** The feature exists — found by the under-lock check, or by losing the create race. */\n | { ok: false; code: \"already-exists\"; existing: FeatureEntry | null; recovered: CommitRecovery | null }\n /**\n * The docs repo went away between the command's gate and the locked read.\n * Deliberately NOT a `code:` arm: the caller renders it through the shared\n * docs-repo reporter, and the codes-drift collector reads every `code:`\n * literal as a stable emission — which this discriminant is not.\n */\n | { ok: false; repoGone: DocsRepoUnavailableError };\n\nexport async function commitScaffold(\n docsDir: DocsDir,\n featureId: string,\n writes: PlannedWrite[],\n): Promise<ScaffoldCommit> {\n // The commit window holds the docs lock: two `new`s for one id serialise\n // into a stable already-exists, and a predecessor's interrupted commit is\n // rolled forward (or refused) before this one stages anything.\n let releaseLock: () => Promise<void>;\n try {\n releaseLock = await acquireDocsLockWaiting(docsDir, LOCK_WAIT_MS);\n } catch (err) {\n if (err instanceof DocsBusyError) return { ok: false, code: \"docs-busy\", message: err.message };\n throw err;\n }\n try {\n let recovered: CommitRecovery | null;\n try {\n recovered = await recoverInterruptedCommit(docsDir);\n } catch (err) {\n if (!(err instanceof InterruptedCommitError)) throw err;\n return { ok: false, code: \"commit-interrupted\", message: err.message };\n }\n // The one existence check, UNDER the lock and AFTER recovery: two racing\n // runs serialise on it, and a recovered half-scaffold has been completed\n // by the recovery above, so this refusal is truthful.\n let existing: FeatureEntry | null;\n try {\n existing = await resolveFeature(docsDir, featureId, \"include\");\n } catch (err) {\n // The command's gate already refused both broken states, so reaching\n // here means the docs repo went away between that check and this read.\n // Same breach, same two codes — the alternative is a stack trace under\n // `internal` that names neither.\n if (!(err instanceof DocsRepoUnavailableError)) throw err;\n return { ok: false, repoGone: err };\n }\n if (existing) return { ok: false, code: \"already-exists\", existing, recovered };\n const staged = await stageWrites(writes);\n const committed = await commitStaged(\n { root: docsDir, command: \"new\", rerun: `loam new ${featureId}`, target: featureId },\n staged,\n \"scaffolded\",\n );\n if (!committed.ok) {\n // A lost exclusive-create race IS the feature existing: the other `new`\n // won, and this refusal is the same stable answer the check above gives\n // when it sees the winner first.\n return committed.raced\n ? { ok: false, code: \"already-exists\", existing: null, recovered }\n : { ok: false, code: committed.code, message: committed.message };\n }\n return { ok: true, recovered };\n } finally {\n await releaseLock();\n }\n}\n"]}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import {} from "../../core/staging/writes.js";
|
|
2
|
+
import { commitScaffold } from "./commit.js";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { loadConfig } from "../../core/envelope/config.js";
|
|
5
|
+
import { parseFeatureId } from "../../core/kernel/ids/feature.js";
|
|
6
|
+
import { parseServiceIds } from "../../core/kernel/ids/service.js";
|
|
7
|
+
import { emitJson, fail, repoPath, reportNoConfig } from "../../core/envelope/json.js";
|
|
8
|
+
import { UnsafePathError, resolveInside } from "../../core/kernel/path-safety.js";
|
|
9
|
+
import { featureIdFromDirName, nearestIds } from "../../core/repo/entries.js";
|
|
10
|
+
import { featuresDir } from "../../core/repo/paths.js";
|
|
11
|
+
import { DocsRepoUnavailableError } from "../../core/repo/state.js";
|
|
12
|
+
import { listServices } from "../../core/repo/repo.js";
|
|
13
|
+
import { docsRepoReady, reportDocsRepoError } from "../policy/gate.js";
|
|
14
|
+
import { sayRecovered } from "../policy/format.js";
|
|
15
|
+
import { capabilityIdProblem } from "../../core/kernel/ids/capability.js";
|
|
16
|
+
import { capabilityNotes } from "../../core/capabilities/authoring/notes.js";
|
|
17
|
+
import { archSpecTemplate, capabilityDeltaTemplate, deltaTemplate, intentTemplate, openapiTemplate, specTemplate, } from "./templates.js";
|
|
18
|
+
export function registerNew(program) {
|
|
19
|
+
program
|
|
20
|
+
.command("new")
|
|
21
|
+
.argument("<featureId>", "feature id, e.g. FEAT-101")
|
|
22
|
+
.description("Scaffold a feature: intent, C4 delta, a requirement delta per service, a capability delta per promise")
|
|
23
|
+
.option("--title <text>", "human title; also becomes the directory slug")
|
|
24
|
+
// `--touches`, not `--service`: everywhere else `--service` selects the ONE
|
|
25
|
+
// operating target (defaulting to config.service), while this is a repeatable
|
|
26
|
+
// list of services the feature touches — a different arity and a different
|
|
27
|
+
// meaning deserve a different name.
|
|
28
|
+
.option("--touches <id>", "a service this feature touches (repeatable)", collect, [])
|
|
29
|
+
.option("--new-service <id>", "a service this feature introduces (repeatable)", collect, [])
|
|
30
|
+
// The INVERSION of the two flags above, and it composes with them rather
|
|
31
|
+
// than excluding them. `--touches` asks which services change before the
|
|
32
|
+
// business change has been written; `--capability` opens the document that
|
|
33
|
+
// changes and lets the service work be derived from it. A feature that
|
|
34
|
+
// does both — the analyst writes the promise, and one team already knows it
|
|
35
|
+
// owns part of the mechanism — is an ordinary shape, and refusing the
|
|
36
|
+
// combination would fight this axis's own archive gate: `capability.uncovered`
|
|
37
|
+
// refuses a promise nothing in the same feature keeps, so the fix it names
|
|
38
|
+
// is precisely a `--touches` service's `Realizes:` line living beside the
|
|
39
|
+
// capability delta.
|
|
40
|
+
//
|
|
41
|
+
// Repeatable, like both flags above: a business change that moves two
|
|
42
|
+
// promises is as real as a feature touching two services, and the flag that
|
|
43
|
+
// is not repeatable is the one that sends an author to mkdir by hand.
|
|
44
|
+
.option("--capability <id>", "a business capability this feature changes (repeatable)", collect, [])
|
|
45
|
+
.option("--json", "emit the machine contract instead of the human view")
|
|
46
|
+
.action(async (featureId, opts) => {
|
|
47
|
+
const json = opts.json === true;
|
|
48
|
+
// The grammar's verdict arrives as the brand: `featureDirName` demands
|
|
49
|
+
// a FeatureId, so an argv string cannot reach the directory derivation
|
|
50
|
+
// without passing through this parse — the producer the brand was
|
|
51
|
+
// missing while `isFeatureId` narrowed to plain string.
|
|
52
|
+
const parsedId = parseFeatureId(featureId, "feature id");
|
|
53
|
+
if (!parsedId.ok) {
|
|
54
|
+
return fail(json, "invalid-option", parsedId.problem);
|
|
55
|
+
}
|
|
56
|
+
const dirName = featureDirName(parsedId.id, opts.title);
|
|
57
|
+
// The round-trip is checked beside the grammar, not inside it: the
|
|
58
|
+
// grammar is a fact about the id, while `featureIdFromDirName` depends on
|
|
59
|
+
// the TITLE this command is about to slug into the directory name. Only
|
|
60
|
+
// `new` creates that directory, so only `new` owes that second half.
|
|
61
|
+
if (featureIdFromDirName(dirName) !== featureId) {
|
|
62
|
+
return fail(json, "invalid-option", `'${featureId}' does not survive the directory round-trip with this title.`);
|
|
63
|
+
}
|
|
64
|
+
// Service ids are validated BEFORE the config is even loaded, and long
|
|
65
|
+
// before anything is written: every one of them is interpolated into
|
|
66
|
+
// `specs/<id>/` under the new feature directory, so `--touches ../../x`
|
|
67
|
+
// was a writer pointed outside the docs repo. One grammar (core/kernel/ids/service.ts),
|
|
68
|
+
// the same one adopt/init/vouch refuse on, so a service that is legal to
|
|
69
|
+
// create is legal to name here and nowhere the two disagree.
|
|
70
|
+
// The LIST, not each item: a loop narrows the element and leaves the
|
|
71
|
+
// array unbranded, so what reached the writer below was `string[]` no
|
|
72
|
+
// matter how thoroughly each id had been checked. --touches first, so the
|
|
73
|
+
// id reported first is the one it was before.
|
|
74
|
+
const touches = parseServiceIds(opts.touches, "--touches");
|
|
75
|
+
if (!touches.ok)
|
|
76
|
+
return fail(json, "invalid-option", touches.problem);
|
|
77
|
+
const newServices = parseServiceIds(opts.newService, "--new-service");
|
|
78
|
+
if (!newServices.ok)
|
|
79
|
+
return fail(json, "invalid-option", newServices.problem);
|
|
80
|
+
// Same reasoning as the two lists above, one tree over. A capability id
|
|
81
|
+
// becomes `features/<dirName>/capabilities/<id>/` — a chain of
|
|
82
|
+
// directories, one per `/`-separated segment — so `--capability
|
|
83
|
+
// ../../evil` resolves to `features/evil/spec.md`, a directory
|
|
84
|
+
// `listFeatures` then enumerates as a feature, and one `..` further
|
|
85
|
+
// reaches the docs-repo root. `resolveInside` below cannot refuse either:
|
|
86
|
+
// both are still inside the repo. Checked here for that reason, and
|
|
87
|
+
// against the same directory-name grammar a service id passes
|
|
88
|
+
// (core/kernel/ids/capability.ts).
|
|
89
|
+
const capabilities = opts.capability;
|
|
90
|
+
for (const id of capabilities) {
|
|
91
|
+
const problem = capabilityIdProblem(id);
|
|
92
|
+
if (problem !== null)
|
|
93
|
+
return fail(json, "invalid-option", problem);
|
|
94
|
+
}
|
|
95
|
+
const loaded = await loadConfig();
|
|
96
|
+
if (loaded.kind !== "loaded") {
|
|
97
|
+
reportNoConfig(json, loaded);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const config = loaded.config;
|
|
101
|
+
const { docsDir } = config;
|
|
102
|
+
// Before a single file is scaffolded: a docsDir that is not a docs repo is
|
|
103
|
+
// refused, never written into. `features/<id>/**` lands happily in any
|
|
104
|
+
// directory, so without this gate `new` reported `ok: true` over a
|
|
105
|
+
// scaffold that no enumeration downstream will ever see — and a docsDir
|
|
106
|
+
// that is simply absent escaped as `internal` (the enumeration below
|
|
107
|
+
// throws) carrying the very sentence `list` and `show` report as
|
|
108
|
+
// `docs-missing`. See docs-repo-gate.ts's docsRepoReady. `services`, not
|
|
109
|
+
// `docs`: every id named by --touches/--new-service is a claim about
|
|
110
|
+
// `services/<id>/`, and the near-miss note below is computed against that
|
|
111
|
+
// directory, so a run without it is guessing.
|
|
112
|
+
if (!docsRepoReady(json, docsDir, "services"))
|
|
113
|
+
return;
|
|
114
|
+
// No unlocked existence fast-path, deliberately: staging creates the
|
|
115
|
+
// feature directory at plan time, so a HALF-scaffold from a killed run
|
|
116
|
+
// resolves like a finished feature — and an unlocked refusal here made
|
|
117
|
+
// \`loam new <id>\` answer already-exists over its own wreckage without
|
|
118
|
+
// ever reaching the recovery that would complete it, while doctor's fix
|
|
119
|
+
// printed exactly that re-run. Existence is asked once, under the lock,
|
|
120
|
+
// after recovery has run.
|
|
121
|
+
// A service named both ways is new — that is the more specific claim.
|
|
122
|
+
const created = new Set(newServices.ids);
|
|
123
|
+
const touched = touches.ids.filter((s) => !created.has(s));
|
|
124
|
+
const dir = join(featuresDir(docsDir), dirName);
|
|
125
|
+
const files = {
|
|
126
|
+
"intent.md": intentTemplate(featureId, opts.title),
|
|
127
|
+
"delta.likec4": deltaTemplate(featureId, touched, [...created]),
|
|
128
|
+
};
|
|
129
|
+
for (const svc of [...touched, ...created]) {
|
|
130
|
+
files[join("specs", svc, "spec.md")] = specTemplate(featureId, svc);
|
|
131
|
+
}
|
|
132
|
+
// The business axis, and the ONE file `--capability` adds. No service
|
|
133
|
+
// spec rides with it, and that is the inversion rather than an omission:
|
|
134
|
+
// the whole point of opening the document that changes first is that the
|
|
135
|
+
// services realizing it are not known until the promise is written.
|
|
136
|
+
// `--touches` is still how a service the author already knows about gets
|
|
137
|
+
// its delta, which is why the two compose.
|
|
138
|
+
//
|
|
139
|
+
// Spelled relative, mirroring `featureCapabilityDeltasDir` +
|
|
140
|
+
// `capabilityDocPathsAt` in core/repo/authored/paths.ts — the path builders take a
|
|
141
|
+
// FeatureDir, whose provenance is an enumeration that read the directory,
|
|
142
|
+
// and this directory is about to be created rather than read. One
|
|
143
|
+
// directory per segment, exactly as the living tree spells nesting.
|
|
144
|
+
for (const cap of capabilities) {
|
|
145
|
+
files[join("capabilities", ...cap.split("/"), "spec.md")] = capabilityDeltaTemplate(featureId, cap);
|
|
146
|
+
}
|
|
147
|
+
for (const svc of created) {
|
|
148
|
+
files[join("specs", svc, "openapi.yaml")] = openapiTemplate(svc);
|
|
149
|
+
// The architecture axis is scaffolded only for a service this feature
|
|
150
|
+
// INTRODUCES, because that is the case where `c4.uncovered` will fire
|
|
151
|
+
// the moment the delta is written: a brand-new tagged element nothing
|
|
152
|
+
// covers. Handing an author a blank page for the outbox/retries/alerts
|
|
153
|
+
// requirement is how that axis stays empty across a whole fleet.
|
|
154
|
+
files[join("specs", svc, "arch.spec.md")] = archSpecTemplate(featureId, svc);
|
|
155
|
+
}
|
|
156
|
+
// The COMPLETE plan in memory before a byte lands on disk: every path
|
|
157
|
+
// resolved (belt and braces over the id grammar — resolveInside proves
|
|
158
|
+
// containment at the moment of the write, symlink cases included), and
|
|
159
|
+
// every write an exclusive create, because the feature must not exist.
|
|
160
|
+
// The sequential writeFile loop this replaces could die mid-scaffold and
|
|
161
|
+
// leave a partial feature the next run refused as already-exists.
|
|
162
|
+
const writes = [];
|
|
163
|
+
for (const [rel, content] of Object.entries(files)) {
|
|
164
|
+
let path;
|
|
165
|
+
try {
|
|
166
|
+
path = resolveInside(docsDir, join("features", dirName, rel), "feature file");
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
if (!(err instanceof UnsafePathError))
|
|
170
|
+
throw err;
|
|
171
|
+
return fail(json, "invalid-option", err.message);
|
|
172
|
+
}
|
|
173
|
+
writes.push({ path, content, exclusive: true });
|
|
174
|
+
}
|
|
175
|
+
const committed = await commitScaffold(docsDir, featureId, writes);
|
|
176
|
+
if (!committed.ok) {
|
|
177
|
+
if ("repoGone" in committed) {
|
|
178
|
+
reportDocsRepoError(json, committed.repoGone);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (committed.code === "already-exists") {
|
|
182
|
+
// After a recovery the refusal must SAY the repair happened: doctor
|
|
183
|
+
// told the operator to re-run this exact command, and "already
|
|
184
|
+
// exists" alone reads as the fix having failed.
|
|
185
|
+
return fail(json, "already-exists", `Feature '${featureId}' already exists at ${repoPath(docsDir, committed.existing?.dir ?? dir)}.` +
|
|
186
|
+
(committed.recovered === null ? "" : ` (${sayRecovered(committed.recovered)} The scaffold is complete.)`));
|
|
187
|
+
}
|
|
188
|
+
return fail(json, committed.code, committed.message);
|
|
189
|
+
}
|
|
190
|
+
const recovered = committed.recovered;
|
|
191
|
+
const written = writes.map((w) => repoPath(docsDir, w.path));
|
|
192
|
+
// Both note families in ONE list, and the services first so an existing
|
|
193
|
+
// `--touches` consumer reads the same first element it always did. They
|
|
194
|
+
// are the same kind of statement — "the thing you named is not there yet,
|
|
195
|
+
// and here is what that means" — so splitting them into two payload keys
|
|
196
|
+
// would ask a consumer to learn a second shape for one idea.
|
|
197
|
+
const notes = [
|
|
198
|
+
...(await unknownServiceNotes(docsDir, touched)),
|
|
199
|
+
...(await capabilityNotes(docsDir, { dirName, ids: capabilities })),
|
|
200
|
+
];
|
|
201
|
+
if (json) {
|
|
202
|
+
emitJson({
|
|
203
|
+
command: "new",
|
|
204
|
+
feature: featureId,
|
|
205
|
+
path: repoPath(docsDir, dir),
|
|
206
|
+
created: written,
|
|
207
|
+
...(recovered === null ? {} : { recovered }),
|
|
208
|
+
// Not an error and not a finding: `--touches` on a service that does
|
|
209
|
+
// not exist yet is legal (adopt it later, or say `--new-service`),
|
|
210
|
+
// and `--capability` on a promise nobody has named is the ordinary
|
|
211
|
+
// way a new business area starts. Both are reported because the
|
|
212
|
+
// silent alternative is a delta that will never merge into anything,
|
|
213
|
+
// or a near-miss spelling filed as a second capability.
|
|
214
|
+
notes,
|
|
215
|
+
});
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
console.log(`${featureId} scaffolded at ${repoPath(docsDir, dir)}`);
|
|
219
|
+
for (const w of written)
|
|
220
|
+
console.log(` + ${w}`);
|
|
221
|
+
for (const note of notes)
|
|
222
|
+
console.log(`\n note: ${note}`);
|
|
223
|
+
console.log(`\nNext: fill in the delta, then \`loam validate --feature ${featureId}\`.`);
|
|
224
|
+
console.log("If this feature changes no architecture, delete delta.likec4 — a requirements-only\n" +
|
|
225
|
+
"feature is complete without it, and an empty `model {}` validates clean too.");
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
function collect(value, previous) {
|
|
229
|
+
return [...previous, value];
|
|
230
|
+
}
|
|
231
|
+
/* ------------------------------------------------------------------ */
|
|
232
|
+
/* Hints */
|
|
233
|
+
/* ------------------------------------------------------------------ */
|
|
234
|
+
/**
|
|
235
|
+
* `--touches <id>` naming neither a living `services/<id>/` nor a `--new-service`
|
|
236
|
+
* is almost always a typo, and it is silent by construction: the scaffold writes
|
|
237
|
+
* `specs/<id>/spec.md` happily, `loam validate` grades a delta against a living
|
|
238
|
+
* spec that is not there, and the mistake surfaces at archive time as a service
|
|
239
|
+
* appearing from nowhere. A note, not a refusal — naming a service that will be
|
|
240
|
+
* adopted next week is legitimate — but the near-miss is spelled out, because
|
|
241
|
+
* `order-api` vs `orders-api` is exactly the pair a person cannot see.
|
|
242
|
+
*/
|
|
243
|
+
async function unknownServiceNotes(docsDir, touched) {
|
|
244
|
+
if (touched.length === 0)
|
|
245
|
+
return [];
|
|
246
|
+
let known;
|
|
247
|
+
try {
|
|
248
|
+
known = (await listServices(docsDir)).map((s) => s.id);
|
|
249
|
+
}
|
|
250
|
+
catch (err) {
|
|
251
|
+
// No docs repo to compare against is a different diagnosis entirely, and
|
|
252
|
+
// `loam doctor` owns it. Saying nothing here beats inventing a typo hint
|
|
253
|
+
// out of an enumeration that never ran.
|
|
254
|
+
if (!(err instanceof DocsRepoUnavailableError))
|
|
255
|
+
throw err;
|
|
256
|
+
return [];
|
|
257
|
+
}
|
|
258
|
+
const notes = [];
|
|
259
|
+
for (const id of touched) {
|
|
260
|
+
if (known.includes(id))
|
|
261
|
+
continue;
|
|
262
|
+
const near = nearestIds(id, known);
|
|
263
|
+
notes.push(`--touches '${id}' matches no services/${id}/ in the docs repo` +
|
|
264
|
+
(near.length === 0 ? "" : ` — did you mean ${near.map((n) => `'${n}'`).join(" or ")}?`) +
|
|
265
|
+
` If ${id} is introduced by this feature, pass --new-service ${id} instead.`);
|
|
266
|
+
}
|
|
267
|
+
return notes;
|
|
268
|
+
}
|
|
269
|
+
/* ------------------------------------------------------------------ */
|
|
270
|
+
/* Naming */
|
|
271
|
+
/* ------------------------------------------------------------------ */
|
|
272
|
+
function featureDirName(featureId, title) {
|
|
273
|
+
const slug = slugify(title ?? "");
|
|
274
|
+
return slug ? `${featureId}-${slug}` : featureId;
|
|
275
|
+
}
|
|
276
|
+
function slugify(text) {
|
|
277
|
+
return text
|
|
278
|
+
.toLowerCase()
|
|
279
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
280
|
+
.replace(/^-+|-+$/g, "");
|
|
281
|
+
}
|
|
282
|
+
/** A LikeC4 identifier for a service name: `payment-split-service` -> `paymentSplitService`. */
|
|
283
|
+
//# sourceMappingURL=new.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"new.js","sourceRoot":"","sources":["../../../src/commands/new/new.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Command } from \"commander\";\nimport { type PlannedWrite } from \"../../core/staging/writes.js\";\nimport { commitScaffold } from \"./commit.js\";\nimport { join } from \"node:path\";\nimport { loadConfig } from \"../../core/envelope/config.js\";\nimport { type FeatureId, parseFeatureId } from \"../../core/kernel/ids/feature.js\";\nimport { parseServiceIds } from \"../../core/kernel/ids/service.js\";\nimport { emitJson, fail, repoPath, reportNoConfig } from \"../../core/envelope/json.js\";\nimport { UnsafePathError, resolveInside } from \"../../core/kernel/path-safety.js\";\nimport { featureIdFromDirName, nearestIds } from \"../../core/repo/entries.js\";\nimport { featuresDir } from \"../../core/repo/paths.js\";\nimport { DocsRepoUnavailableError } from \"../../core/repo/state.js\";\nimport { listServices } from \"../../core/repo/repo.js\";\nimport { docsRepoReady, reportDocsRepoError } from \"../policy/gate.js\";\nimport { sayRecovered } from \"../policy/format.js\";\nimport type { DocsDir } from \"../../core/kernel/ids/dirs.js\";\nimport { capabilityIdProblem } from \"../../core/kernel/ids/capability.js\";\nimport { capabilityNotes } from \"../../core/capabilities/authoring/notes.js\";\nimport {\n archSpecTemplate,\n capabilityDeltaTemplate,\n deltaTemplate,\n intentTemplate,\n openapiTemplate,\n specTemplate,\n} from \"./templates.js\";\n\ninterface NewOptions {\n title?: string;\n touches: string[];\n newService: string[];\n capability: string[];\n json?: boolean;\n}\n\nexport function registerNew(program: Command): void {\n program\n .command(\"new\")\n .argument(\"<featureId>\", \"feature id, e.g. FEAT-101\")\n .description(\"Scaffold a feature: intent, C4 delta, a requirement delta per service, a capability delta per promise\")\n .option(\"--title <text>\", \"human title; also becomes the directory slug\")\n // `--touches`, not `--service`: everywhere else `--service` selects the ONE\n // operating target (defaulting to config.service), while this is a repeatable\n // list of services the feature touches — a different arity and a different\n // meaning deserve a different name.\n .option(\"--touches <id>\", \"a service this feature touches (repeatable)\", collect, [])\n .option(\"--new-service <id>\", \"a service this feature introduces (repeatable)\", collect, [])\n // The INVERSION of the two flags above, and it composes with them rather\n // than excluding them. `--touches` asks which services change before the\n // business change has been written; `--capability` opens the document that\n // changes and lets the service work be derived from it. A feature that\n // does both — the analyst writes the promise, and one team already knows it\n // owns part of the mechanism — is an ordinary shape, and refusing the\n // combination would fight this axis's own archive gate: `capability.uncovered`\n // refuses a promise nothing in the same feature keeps, so the fix it names\n // is precisely a `--touches` service's `Realizes:` line living beside the\n // capability delta.\n //\n // Repeatable, like both flags above: a business change that moves two\n // promises is as real as a feature touching two services, and the flag that\n // is not repeatable is the one that sends an author to mkdir by hand.\n .option(\"--capability <id>\", \"a business capability this feature changes (repeatable)\", collect, [])\n .option(\"--json\", \"emit the machine contract instead of the human view\")\n .action(async (featureId: string, opts: NewOptions) => {\n const json = opts.json === true;\n\n // The grammar's verdict arrives as the brand: `featureDirName` demands\n // a FeatureId, so an argv string cannot reach the directory derivation\n // without passing through this parse — the producer the brand was\n // missing while `isFeatureId` narrowed to plain string.\n const parsedId = parseFeatureId(featureId, \"feature id\");\n if (!parsedId.ok) {\n return fail(json, \"invalid-option\", parsedId.problem);\n }\n const dirName = featureDirName(parsedId.id, opts.title);\n // The round-trip is checked beside the grammar, not inside it: the\n // grammar is a fact about the id, while `featureIdFromDirName` depends on\n // the TITLE this command is about to slug into the directory name. Only\n // `new` creates that directory, so only `new` owes that second half.\n if (featureIdFromDirName(dirName) !== featureId) {\n return fail(\n json,\n \"invalid-option\",\n `'${featureId}' does not survive the directory round-trip with this title.`,\n );\n }\n\n // Service ids are validated BEFORE the config is even loaded, and long\n // before anything is written: every one of them is interpolated into\n // `specs/<id>/` under the new feature directory, so `--touches ../../x`\n // was a writer pointed outside the docs repo. One grammar (core/kernel/ids/service.ts),\n // the same one adopt/init/vouch refuse on, so a service that is legal to\n // create is legal to name here and nowhere the two disagree.\n // The LIST, not each item: a loop narrows the element and leaves the\n // array unbranded, so what reached the writer below was `string[]` no\n // matter how thoroughly each id had been checked. --touches first, so the\n // id reported first is the one it was before.\n const touches = parseServiceIds(opts.touches, \"--touches\");\n if (!touches.ok) return fail(json, \"invalid-option\", touches.problem);\n const newServices = parseServiceIds(opts.newService, \"--new-service\");\n if (!newServices.ok) return fail(json, \"invalid-option\", newServices.problem);\n\n // Same reasoning as the two lists above, one tree over. A capability id\n // becomes `features/<dirName>/capabilities/<id>/` — a chain of\n // directories, one per `/`-separated segment — so `--capability\n // ../../evil` resolves to `features/evil/spec.md`, a directory\n // `listFeatures` then enumerates as a feature, and one `..` further\n // reaches the docs-repo root. `resolveInside` below cannot refuse either:\n // both are still inside the repo. Checked here for that reason, and\n // against the same directory-name grammar a service id passes\n // (core/kernel/ids/capability.ts).\n const capabilities = opts.capability;\n for (const id of capabilities) {\n const problem = capabilityIdProblem(id);\n if (problem !== null) return fail(json, \"invalid-option\", problem);\n }\n\n const loaded = await loadConfig();\n if (loaded.kind !== \"loaded\") {\n reportNoConfig(json, loaded);\n return;\n }\n const config = loaded.config;\n const { docsDir } = config;\n // Before a single file is scaffolded: a docsDir that is not a docs repo is\n // refused, never written into. `features/<id>/**` lands happily in any\n // directory, so without this gate `new` reported `ok: true` over a\n // scaffold that no enumeration downstream will ever see — and a docsDir\n // that is simply absent escaped as `internal` (the enumeration below\n // throws) carrying the very sentence `list` and `show` report as\n // `docs-missing`. See docs-repo-gate.ts's docsRepoReady. `services`, not\n // `docs`: every id named by --touches/--new-service is a claim about\n // `services/<id>/`, and the near-miss note below is computed against that\n // directory, so a run without it is guessing.\n if (!docsRepoReady(json, docsDir, \"services\")) return;\n\n // No unlocked existence fast-path, deliberately: staging creates the\n // feature directory at plan time, so a HALF-scaffold from a killed run\n // resolves like a finished feature — and an unlocked refusal here made\n // \\`loam new <id>\\` answer already-exists over its own wreckage without\n // ever reaching the recovery that would complete it, while doctor's fix\n // printed exactly that re-run. Existence is asked once, under the lock,\n // after recovery has run.\n\n // A service named both ways is new — that is the more specific claim.\n const created = new Set(newServices.ids);\n const touched = touches.ids.filter((s) => !created.has(s));\n const dir = join(featuresDir(docsDir), dirName);\n\n const files: Record<string, string> = {\n \"intent.md\": intentTemplate(featureId, opts.title),\n \"delta.likec4\": deltaTemplate(featureId, touched, [...created]),\n };\n for (const svc of [...touched, ...created]) {\n files[join(\"specs\", svc, \"spec.md\")] = specTemplate(featureId, svc);\n }\n // The business axis, and the ONE file `--capability` adds. No service\n // spec rides with it, and that is the inversion rather than an omission:\n // the whole point of opening the document that changes first is that the\n // services realizing it are not known until the promise is written.\n // `--touches` is still how a service the author already knows about gets\n // its delta, which is why the two compose.\n //\n // Spelled relative, mirroring `featureCapabilityDeltasDir` +\n // `capabilityDocPathsAt` in core/repo/authored/paths.ts — the path builders take a\n // FeatureDir, whose provenance is an enumeration that read the directory,\n // and this directory is about to be created rather than read. One\n // directory per segment, exactly as the living tree spells nesting.\n for (const cap of capabilities) {\n files[join(\"capabilities\", ...cap.split(\"/\"), \"spec.md\")] = capabilityDeltaTemplate(featureId, cap);\n }\n for (const svc of created) {\n files[join(\"specs\", svc, \"openapi.yaml\")] = openapiTemplate(svc);\n // The architecture axis is scaffolded only for a service this feature\n // INTRODUCES, because that is the case where `c4.uncovered` will fire\n // the moment the delta is written: a brand-new tagged element nothing\n // covers. Handing an author a blank page for the outbox/retries/alerts\n // requirement is how that axis stays empty across a whole fleet.\n files[join(\"specs\", svc, \"arch.spec.md\")] = archSpecTemplate(featureId, svc);\n }\n\n // The COMPLETE plan in memory before a byte lands on disk: every path\n // resolved (belt and braces over the id grammar — resolveInside proves\n // containment at the moment of the write, symlink cases included), and\n // every write an exclusive create, because the feature must not exist.\n // The sequential writeFile loop this replaces could die mid-scaffold and\n // leave a partial feature the next run refused as already-exists.\n const writes: PlannedWrite[] = [];\n for (const [rel, content] of Object.entries(files)) {\n let path: string;\n try {\n path = resolveInside(docsDir, join(\"features\", dirName, rel), \"feature file\");\n } catch (err) {\n if (!(err instanceof UnsafePathError)) throw err;\n return fail(json, \"invalid-option\", err.message);\n }\n writes.push({ path, content, exclusive: true });\n }\n\n const committed = await commitScaffold(docsDir, featureId, writes);\n if (!committed.ok) {\n if (\"repoGone\" in committed) {\n reportDocsRepoError(json, committed.repoGone);\n return;\n }\n if (committed.code === \"already-exists\") {\n // After a recovery the refusal must SAY the repair happened: doctor\n // told the operator to re-run this exact command, and \"already\n // exists\" alone reads as the fix having failed.\n return fail(\n json,\n \"already-exists\",\n `Feature '${featureId}' already exists at ${repoPath(docsDir, committed.existing?.dir ?? dir)}.` +\n (committed.recovered === null ? \"\" : ` (${sayRecovered(committed.recovered)} The scaffold is complete.)`),\n );\n }\n return fail(json, committed.code, committed.message);\n }\n const recovered = committed.recovered;\n const written = writes.map((w) => repoPath(docsDir, w.path));\n\n // Both note families in ONE list, and the services first so an existing\n // `--touches` consumer reads the same first element it always did. They\n // are the same kind of statement — \"the thing you named is not there yet,\n // and here is what that means\" — so splitting them into two payload keys\n // would ask a consumer to learn a second shape for one idea.\n const notes = [\n ...(await unknownServiceNotes(docsDir, touched)),\n ...(await capabilityNotes(docsDir, { dirName, ids: capabilities })),\n ];\n\n if (json) {\n emitJson({\n command: \"new\",\n feature: featureId,\n path: repoPath(docsDir, dir),\n created: written,\n ...(recovered === null ? {} : { recovered }),\n // Not an error and not a finding: `--touches` on a service that does\n // not exist yet is legal (adopt it later, or say `--new-service`),\n // and `--capability` on a promise nobody has named is the ordinary\n // way a new business area starts. Both are reported because the\n // silent alternative is a delta that will never merge into anything,\n // or a near-miss spelling filed as a second capability.\n notes,\n });\n return;\n }\n console.log(`${featureId} scaffolded at ${repoPath(docsDir, dir)}`);\n for (const w of written) console.log(` + ${w}`);\n for (const note of notes) console.log(`\\n note: ${note}`);\n console.log(`\\nNext: fill in the delta, then \\`loam validate --feature ${featureId}\\`.`);\n console.log(\n \"If this feature changes no architecture, delete delta.likec4 — a requirements-only\\n\" +\n \"feature is complete without it, and an empty `model {}` validates clean too.\",\n );\n });\n}\n\nfunction collect(value: string, previous: string[]): string[] {\n return [...previous, value];\n}\n\n/* ------------------------------------------------------------------ */\n/* Hints */\n/* ------------------------------------------------------------------ */\n\n/**\n * `--touches <id>` naming neither a living `services/<id>/` nor a `--new-service`\n * is almost always a typo, and it is silent by construction: the scaffold writes\n * `specs/<id>/spec.md` happily, `loam validate` grades a delta against a living\n * spec that is not there, and the mistake surfaces at archive time as a service\n * appearing from nowhere. A note, not a refusal — naming a service that will be\n * adopted next week is legitimate — but the near-miss is spelled out, because\n * `order-api` vs `orders-api` is exactly the pair a person cannot see.\n */\nasync function unknownServiceNotes(docsDir: DocsDir, touched: string[]): Promise<string[]> {\n if (touched.length === 0) return [];\n let known: string[];\n try {\n known = (await listServices(docsDir)).map((s) => s.id);\n } catch (err) {\n // No docs repo to compare against is a different diagnosis entirely, and\n // `loam doctor` owns it. Saying nothing here beats inventing a typo hint\n // out of an enumeration that never ran.\n if (!(err instanceof DocsRepoUnavailableError)) throw err;\n return [];\n }\n const notes: string[] = [];\n for (const id of touched) {\n if (known.includes(id)) continue;\n const near = nearestIds(id, known);\n notes.push(\n `--touches '${id}' matches no services/${id}/ in the docs repo` +\n (near.length === 0 ? \"\" : ` — did you mean ${near.map((n) => `'${n}'`).join(\" or \")}?`) +\n ` If ${id} is introduced by this feature, pass --new-service ${id} instead.`,\n );\n }\n return notes;\n}\n\n/* ------------------------------------------------------------------ */\n/* Naming */\n/* ------------------------------------------------------------------ */\n\nfunction featureDirName(featureId: FeatureId, title: string | undefined): string {\n const slug = slugify(title ?? \"\");\n return slug ? `${featureId}-${slug}` : featureId;\n}\n\nfunction slugify(text: string): string {\n return text\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n}\n\n/** A LikeC4 identifier for a service name: `payment-split-service` -> `paymentSplitService`. */\n"]}
|