@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,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two verbs of the OpenSpec on-ramp, and nothing else.
|
|
3
|
+
*
|
|
4
|
+
* They are one module because they are two halves of one procedure and share
|
|
5
|
+
* the failure mapping below: `audit-openspec` reads a workspace and offers the
|
|
6
|
+
* decision skeleton, `migrate-openspec` takes that skeleton back filled in and
|
|
7
|
+
* — only with `--apply` — materializes a staged target. Finding
|
|
8
|
+
* incompatibilities is a successful audit, so exit status is decided here and
|
|
9
|
+
* only here; every phase under `./openspec/` and `./materialize/` refuses by
|
|
10
|
+
* throwing `OpenSpecCommandError` and never touches `process.exitCode`.
|
|
11
|
+
*/
|
|
12
|
+
import { dirname, join, resolve } from "node:path";
|
|
13
|
+
import { fail, emitJson } from "../../core/envelope/json.js";
|
|
14
|
+
import { inventoryOpenSpec } from "../../core/openspec/inventory.js";
|
|
15
|
+
import { createOpenSpecMappingSkeleton } from "../../core/openspec/model/mapping.js";
|
|
16
|
+
import { OpenSpecRootError } from "../../core/openspec/model/model.js";
|
|
17
|
+
import { writeMappingSkeleton } from "./openspec/decisions.js";
|
|
18
|
+
import { OpenSpecCommandError } from "./openspec/error.js";
|
|
19
|
+
import { readMapping } from "./openspec/mapping.js";
|
|
20
|
+
import { planMigrationWrites, writeMigrationTarget } from "./materialize/target.js";
|
|
21
|
+
import { printInventory } from "./print.js";
|
|
22
|
+
/** Registers the read-only audit and the separate, mapping-driven migration planner. */
|
|
23
|
+
export function registerMigrateOpenSpec(program) {
|
|
24
|
+
program
|
|
25
|
+
.command("audit-openspec")
|
|
26
|
+
.argument("<root>", "OpenSpec root, Store checkout, or workspace containing openspec/")
|
|
27
|
+
.description("Audit an OpenSpec workspace without treating human mapping decisions as command failure")
|
|
28
|
+
.option("--write-mapping <path>", "write a non-overwriting, digest-bound decision skeleton outside the source workspace")
|
|
29
|
+
.option("--json", "emit the machine contract instead of the human view")
|
|
30
|
+
.action(async (root, opts) => {
|
|
31
|
+
const json = opts.json === true;
|
|
32
|
+
try {
|
|
33
|
+
const inventory = await inventoryOpenSpec(root);
|
|
34
|
+
const skeleton = createOpenSpecMappingSkeleton(inventory);
|
|
35
|
+
const mappingWritten = opts.writeMapping === undefined
|
|
36
|
+
? null
|
|
37
|
+
: await writeMappingSkeleton(inventory, opts.writeMapping, skeleton);
|
|
38
|
+
if (json) {
|
|
39
|
+
emitJson({
|
|
40
|
+
command: "audit-openspec",
|
|
41
|
+
readOnly: mappingWritten === null,
|
|
42
|
+
mappingWritten,
|
|
43
|
+
mappingSkeleton: skeleton,
|
|
44
|
+
...inventory,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
printInventory(inventory, "audit");
|
|
49
|
+
if (mappingWritten !== null)
|
|
50
|
+
console.log(`\n mapping skeleton ${mappingWritten}`);
|
|
51
|
+
else
|
|
52
|
+
console.log("\n read-only audit: no files were created or changed");
|
|
53
|
+
}
|
|
54
|
+
// Finding incompatibilities is a successful audit result. Root/YAML/IO
|
|
55
|
+
// failures still exit 1 through the catch below; migration readiness has
|
|
56
|
+
// its own boolean and migrate-openspec exit status.
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
reportCommandError(json, error);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
program
|
|
63
|
+
.command("migrate-openspec")
|
|
64
|
+
.argument("<root>", "OpenSpec root, Store checkout, or workspace containing openspec/")
|
|
65
|
+
.description("Validate explicit migration mappings and optionally materialize staged migration docs")
|
|
66
|
+
.option("--map <path>", "YAML mapping created from `audit-openspec --write-mapping`")
|
|
67
|
+
.option("--mapping <path>", "deprecated alias for --map")
|
|
68
|
+
.option("--apply", "materialize staged migration docs; dry-run is the default")
|
|
69
|
+
.option("--target <directory>", "empty standalone target for --apply; never writes into the OpenSpec source or live loam docs")
|
|
70
|
+
.option("--json", "emit the machine contract instead of the human view")
|
|
71
|
+
.action(async (root, opts) => {
|
|
72
|
+
const json = opts.json === true;
|
|
73
|
+
try {
|
|
74
|
+
if (opts.map !== undefined && opts.mapping !== undefined) {
|
|
75
|
+
throw new OpenSpecCommandError("invalid-option", "Pass --map or its deprecated --mapping alias, not both.");
|
|
76
|
+
}
|
|
77
|
+
const mapArg = opts.map ?? opts.mapping;
|
|
78
|
+
if (mapArg === undefined) {
|
|
79
|
+
throw new OpenSpecCommandError("invalid-option", "migrate-openspec requires --map <path>; run `loam audit-openspec <root> --write-mapping <path>` first.");
|
|
80
|
+
}
|
|
81
|
+
if (opts.apply === true && opts.target === undefined) {
|
|
82
|
+
throw new OpenSpecCommandError("invalid-option", "--apply requires an explicit --target <empty-directory>.");
|
|
83
|
+
}
|
|
84
|
+
if (opts.apply !== true && opts.target !== undefined) {
|
|
85
|
+
throw new OpenSpecCommandError("invalid-option", "--target only has effect with explicit --apply.");
|
|
86
|
+
}
|
|
87
|
+
const mappingPath = resolve(mapArg);
|
|
88
|
+
const mapping = await readMapping(mappingPath);
|
|
89
|
+
// Re-audit the source at the point of use. inventoryOpenSpec compares
|
|
90
|
+
// the fresh digest and canonical root to the binding in the mapping.
|
|
91
|
+
const inventory = await inventoryOpenSpec(root, { mapping });
|
|
92
|
+
const target = opts.apply === true
|
|
93
|
+
? resolve(opts.target)
|
|
94
|
+
: join(dirname(mappingPath), ".loam-openspec-dry-run-target");
|
|
95
|
+
const planned = inventory.ready
|
|
96
|
+
? await planMigrationWrites(inventory, mapping, target)
|
|
97
|
+
: null;
|
|
98
|
+
let applied = null;
|
|
99
|
+
if (opts.apply === true) {
|
|
100
|
+
if (!inventory.ready || planned === null) {
|
|
101
|
+
throw new OpenSpecCommandError("invalid-option", "Migration cannot be applied until living/active shapes, capability mappings, change-to-feature decisions, active RENAMED identities, authored artifact dispositions, and source digest binding are resolved.");
|
|
102
|
+
}
|
|
103
|
+
applied = await writeMigrationTarget(inventory, mapping, target, planned);
|
|
104
|
+
}
|
|
105
|
+
if (json) {
|
|
106
|
+
emitJson({
|
|
107
|
+
command: "migrate-openspec",
|
|
108
|
+
dryRun: applied === null,
|
|
109
|
+
mappingPath,
|
|
110
|
+
applied,
|
|
111
|
+
...inventory,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
printInventory(inventory, "migration");
|
|
116
|
+
if (applied === null)
|
|
117
|
+
console.log("\n dry-run: no files were created or changed");
|
|
118
|
+
else {
|
|
119
|
+
console.log(`\n staged migration docs ${applied.directory}`);
|
|
120
|
+
console.log(` follow-up blockers ${applied.followUpBlockers.length} (see FOLLOW-UP.md)`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (!inventory.ready)
|
|
124
|
+
process.exitCode = 1;
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
reportCommandError(json, error);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
function reportCommandError(json, error) {
|
|
132
|
+
if (error instanceof OpenSpecRootError) {
|
|
133
|
+
fail(json, "unknown-target", error.message);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (error instanceof OpenSpecCommandError) {
|
|
137
|
+
fail(json, error.code, error.message);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
throw error;
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=migrate-openspec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrate-openspec.js","sourceRoot":"","sources":["../../../src/commands/migrate-openspec/migrate-openspec.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The two verbs of the OpenSpec on-ramp, and nothing else.\n *\n * They are one module because they are two halves of one procedure and share\n * the failure mapping below: `audit-openspec` reads a workspace and offers the\n * decision skeleton, `migrate-openspec` takes that skeleton back filled in and\n * — only with `--apply` — materializes a staged target. Finding\n * incompatibilities is a successful audit, so exit status is decided here and\n * only here; every phase under `./openspec/` and `./materialize/` refuses by\n * throwing `OpenSpecCommandError` and never touches `process.exitCode`.\n */\nimport { dirname, join, resolve } from \"node:path\";\nimport type { Command } from \"commander\";\nimport { fail, emitJson } from \"../../core/envelope/json.js\";\nimport { inventoryOpenSpec } from \"../../core/openspec/inventory.js\";\nimport { createOpenSpecMappingSkeleton } from \"../../core/openspec/model/mapping.js\";\nimport { OpenSpecRootError } from \"../../core/openspec/model/model.js\";\nimport { writeMappingSkeleton } from \"./openspec/decisions.js\";\nimport { OpenSpecCommandError } from \"./openspec/error.js\";\nimport { readMapping } from \"./openspec/mapping.js\";\nimport { planMigrationWrites, writeMigrationTarget } from \"./materialize/target.js\";\nimport { printInventory } from \"./print.js\";\n\ninterface AuditOpenSpecOptions {\n json?: boolean;\n writeMapping?: string;\n}\n\ninterface MigrateOpenSpecOptions {\n json?: boolean;\n map?: string;\n mapping?: string;\n apply?: boolean;\n target?: string;\n}\n\n/** Registers the read-only audit and the separate, mapping-driven migration planner. */\nexport function registerMigrateOpenSpec(program: Command): void {\n program\n .command(\"audit-openspec\")\n .argument(\"<root>\", \"OpenSpec root, Store checkout, or workspace containing openspec/\")\n .description(\"Audit an OpenSpec workspace without treating human mapping decisions as command failure\")\n .option(\"--write-mapping <path>\", \"write a non-overwriting, digest-bound decision skeleton outside the source workspace\")\n .option(\"--json\", \"emit the machine contract instead of the human view\")\n .action(async (root: string, opts: AuditOpenSpecOptions) => {\n const json = opts.json === true;\n try {\n const inventory = await inventoryOpenSpec(root);\n const skeleton = createOpenSpecMappingSkeleton(inventory);\n const mappingWritten = opts.writeMapping === undefined\n ? null\n : await writeMappingSkeleton(inventory, opts.writeMapping, skeleton);\n if (json) {\n emitJson({\n command: \"audit-openspec\",\n readOnly: mappingWritten === null,\n mappingWritten,\n mappingSkeleton: skeleton,\n ...inventory,\n });\n } else {\n printInventory(inventory, \"audit\");\n if (mappingWritten !== null) console.log(`\\n mapping skeleton ${mappingWritten}`);\n else console.log(\"\\n read-only audit: no files were created or changed\");\n }\n // Finding incompatibilities is a successful audit result. Root/YAML/IO\n // failures still exit 1 through the catch below; migration readiness has\n // its own boolean and migrate-openspec exit status.\n } catch (error) {\n reportCommandError(json, error);\n }\n });\n\n program\n .command(\"migrate-openspec\")\n .argument(\"<root>\", \"OpenSpec root, Store checkout, or workspace containing openspec/\")\n .description(\"Validate explicit migration mappings and optionally materialize staged migration docs\")\n .option(\"--map <path>\", \"YAML mapping created from `audit-openspec --write-mapping`\")\n .option(\"--mapping <path>\", \"deprecated alias for --map\")\n .option(\"--apply\", \"materialize staged migration docs; dry-run is the default\")\n .option(\"--target <directory>\", \"empty standalone target for --apply; never writes into the OpenSpec source or live loam docs\")\n .option(\"--json\", \"emit the machine contract instead of the human view\")\n .action(async (root: string, opts: MigrateOpenSpecOptions) => {\n const json = opts.json === true;\n try {\n if (opts.map !== undefined && opts.mapping !== undefined) {\n throw new OpenSpecCommandError(\"invalid-option\", \"Pass --map or its deprecated --mapping alias, not both.\");\n }\n const mapArg = opts.map ?? opts.mapping;\n if (mapArg === undefined) {\n throw new OpenSpecCommandError(\n \"invalid-option\",\n \"migrate-openspec requires --map <path>; run `loam audit-openspec <root> --write-mapping <path>` first.\",\n );\n }\n if (opts.apply === true && opts.target === undefined) {\n throw new OpenSpecCommandError(\"invalid-option\", \"--apply requires an explicit --target <empty-directory>.\");\n }\n if (opts.apply !== true && opts.target !== undefined) {\n throw new OpenSpecCommandError(\"invalid-option\", \"--target only has effect with explicit --apply.\");\n }\n const mappingPath = resolve(mapArg);\n const mapping = await readMapping(mappingPath);\n // Re-audit the source at the point of use. inventoryOpenSpec compares\n // the fresh digest and canonical root to the binding in the mapping.\n const inventory = await inventoryOpenSpec(root, { mapping });\n const target = opts.apply === true\n ? resolve(opts.target!)\n : join(dirname(mappingPath), \".loam-openspec-dry-run-target\");\n const planned = inventory.ready\n ? await planMigrationWrites(inventory, mapping, target)\n : null;\n let applied: { directory: string; files: string[]; followUpBlockers: string[] } | null = null;\n if (opts.apply === true) {\n if (!inventory.ready || planned === null) {\n throw new OpenSpecCommandError(\n \"invalid-option\",\n \"Migration cannot be applied until living/active shapes, capability mappings, change-to-feature decisions, active RENAMED identities, authored artifact dispositions, and source digest binding are resolved.\",\n );\n }\n applied = await writeMigrationTarget(inventory, mapping, target, planned);\n }\n\n if (json) {\n emitJson({\n command: \"migrate-openspec\",\n dryRun: applied === null,\n mappingPath,\n applied,\n ...inventory,\n });\n } else {\n printInventory(inventory, \"migration\");\n if (applied === null) console.log(\"\\n dry-run: no files were created or changed\");\n else {\n console.log(`\\n staged migration docs ${applied.directory}`);\n console.log(` follow-up blockers ${applied.followUpBlockers.length} (see FOLLOW-UP.md)`);\n }\n }\n if (!inventory.ready) process.exitCode = 1;\n } catch (error) {\n reportCommandError(json, error);\n }\n });\n}\n\nfunction reportCommandError(json: boolean, error: unknown): void {\n if (error instanceof OpenSpecRootError) {\n fail(json, \"unknown-target\", error.message);\n return;\n }\n if (error instanceof OpenSpecCommandError) {\n fail(json, error.code, error.message);\n return;\n }\n throw error;\n}\n"]}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The mapping document on the way out: the blank skeleton `audit-openspec`
|
|
3
|
+
* offers, the normalized record `migrate-openspec --apply` writes beside the
|
|
4
|
+
* staged docs, and the one query that reads a decision back.
|
|
5
|
+
*
|
|
6
|
+
* `selectedServices` belongs here rather than beside either materializer
|
|
7
|
+
* because it is the single spelling of the routing rule. It was written out
|
|
8
|
+
* twice once — once on the living side and once on the delta side — and the two
|
|
9
|
+
* copies drifted, so a split capability routed a living requirement and its own
|
|
10
|
+
* delta to different services. Both materializers now call this one.
|
|
11
|
+
*/
|
|
12
|
+
import { existsSync } from "node:fs";
|
|
13
|
+
import { mkdir, realpath, writeFile } from "node:fs/promises";
|
|
14
|
+
import { dirname, resolve } from "node:path";
|
|
15
|
+
import { stringify as stringifyYaml } from "yaml";
|
|
16
|
+
import { dictionary, ownValue } from "../../../core/kernel/records.js";
|
|
17
|
+
import {} from "../../../core/openspec/model/mapping.js";
|
|
18
|
+
import {} from "../../../core/openspec/model/model.js";
|
|
19
|
+
import { OpenSpecCommandError } from "./error.js";
|
|
20
|
+
import { canonicalForCreate, contains } from "./paths.js";
|
|
21
|
+
export async function writeMappingSkeleton(inventory, outputArg, skeleton) {
|
|
22
|
+
const output = resolve(outputArg);
|
|
23
|
+
const canonicalOutput = await canonicalForCreate(output);
|
|
24
|
+
const canonicalSource = await realpath(inventory.inputRoot);
|
|
25
|
+
if (contains(canonicalSource, canonicalOutput)) {
|
|
26
|
+
throw new OpenSpecCommandError("invalid-option", `Refusing to write the mapping inside the OpenSpec source workspace: ${output}`);
|
|
27
|
+
}
|
|
28
|
+
if (existsSync(output)) {
|
|
29
|
+
throw new OpenSpecCommandError("already-exists", `Mapping output already exists; refusing to overwrite it: ${output}`);
|
|
30
|
+
}
|
|
31
|
+
await mkdir(dirname(output), { recursive: true });
|
|
32
|
+
try {
|
|
33
|
+
await writeFile(output, stringifyYaml(skeleton, { lineWidth: 0 }), { encoding: "utf8", flag: "wx" });
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
if (existsSync(output)) {
|
|
37
|
+
throw new OpenSpecCommandError("already-exists", `Mapping output already exists; refusing to overwrite it: ${output}`);
|
|
38
|
+
}
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
return output;
|
|
42
|
+
}
|
|
43
|
+
export function normalizedMapping(inventory, mapping) {
|
|
44
|
+
return {
|
|
45
|
+
version: 1,
|
|
46
|
+
source: { root: inventory.root, inventoryDigest: inventory.inventoryDigest },
|
|
47
|
+
capabilities: Object.fromEntries(inventory.mappingDecisions.map((decision) => [
|
|
48
|
+
decision.capability,
|
|
49
|
+
{
|
|
50
|
+
services: ownValue(mapping.capabilities, decision.capability)?.services ?? [],
|
|
51
|
+
requirementServices: ownValue(mapping.capabilities, decision.capability)?.requirementServices ?? dictionary(),
|
|
52
|
+
},
|
|
53
|
+
])),
|
|
54
|
+
changes: Object.fromEntries(inventory.changeDecisions.map((decision) => [
|
|
55
|
+
decision.change,
|
|
56
|
+
{
|
|
57
|
+
feature: ownValue(mapping.changes, decision.change)?.feature ?? null,
|
|
58
|
+
// The decision's title, which is the mapping's own title trimmed.
|
|
59
|
+
// `readMapping` (`./mapping.ts`) trims `feature` but not `title`, and
|
|
60
|
+
// materialization writes the trimmed one into the frontmatter, the H1
|
|
61
|
+
// and the directory slug — so recording the raw one made the plan
|
|
62
|
+
// disagree with the files beside it about what the feature is called.
|
|
63
|
+
title: decision.title,
|
|
64
|
+
},
|
|
65
|
+
])),
|
|
66
|
+
renames: Object.fromEntries(inventory.renamed
|
|
67
|
+
.filter((rename) => rename.scope === "active")
|
|
68
|
+
.map((rename) => [rename.key, {
|
|
69
|
+
from: rename.from,
|
|
70
|
+
to: rename.to,
|
|
71
|
+
existingRequirementId: rename.existingRequirementId,
|
|
72
|
+
requirementId: rename.requirementId,
|
|
73
|
+
}])),
|
|
74
|
+
artifacts: Object.fromEntries(inventory.artifactDecisions.map((decision) => [
|
|
75
|
+
decision.path,
|
|
76
|
+
{
|
|
77
|
+
kind: decision.kind,
|
|
78
|
+
disposition: ownValue(mapping.artifacts, decision.path) ?? null,
|
|
79
|
+
},
|
|
80
|
+
])),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
export function selectedServices(mapping, capability, requirement) {
|
|
84
|
+
const selected = ownValue(mapping.capabilities, capability);
|
|
85
|
+
if (selected === undefined)
|
|
86
|
+
return [];
|
|
87
|
+
return selected.services.length === 1
|
|
88
|
+
? selected.services
|
|
89
|
+
: ownValue(selected.requirementServices, requirement) ?? [];
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=decisions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decisions.js","sourceRoot":"","sources":["../../../../src/commands/migrate-openspec/openspec/decisions.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The mapping document on the way out: the blank skeleton `audit-openspec`\n * offers, the normalized record `migrate-openspec --apply` writes beside the\n * staged docs, and the one query that reads a decision back.\n *\n * `selectedServices` belongs here rather than beside either materializer\n * because it is the single spelling of the routing rule. It was written out\n * twice once — once on the living side and once on the delta side — and the two\n * copies drifted, so a split capability routed a living requirement and its own\n * delta to different services. Both materializers now call this one.\n */\nimport { existsSync } from \"node:fs\";\nimport { mkdir, realpath, writeFile } from \"node:fs/promises\";\nimport { dirname, resolve } from \"node:path\";\nimport { stringify as stringifyYaml } from \"yaml\";\nimport { dictionary, ownValue } from \"../../../core/kernel/records.js\";\nimport { type OpenSpecMapping, type OpenSpecMappingSkeleton } from \"../../../core/openspec/model/mapping.js\";\nimport { type OpenSpecInventory } from \"../../../core/openspec/model/model.js\";\nimport { OpenSpecCommandError } from \"./error.js\";\nimport { canonicalForCreate, contains } from \"./paths.js\";\n\nexport async function writeMappingSkeleton(\n inventory: OpenSpecInventory,\n outputArg: string,\n skeleton: OpenSpecMappingSkeleton,\n): Promise<string> {\n const output = resolve(outputArg);\n const canonicalOutput = await canonicalForCreate(output);\n const canonicalSource = await realpath(inventory.inputRoot);\n if (contains(canonicalSource, canonicalOutput)) {\n throw new OpenSpecCommandError(\n \"invalid-option\",\n `Refusing to write the mapping inside the OpenSpec source workspace: ${output}`,\n );\n }\n if (existsSync(output)) {\n throw new OpenSpecCommandError(\"already-exists\", `Mapping output already exists; refusing to overwrite it: ${output}`);\n }\n await mkdir(dirname(output), { recursive: true });\n try {\n await writeFile(output, stringifyYaml(skeleton, { lineWidth: 0 }), { encoding: \"utf8\", flag: \"wx\" });\n } catch (error) {\n if (existsSync(output)) {\n throw new OpenSpecCommandError(\"already-exists\", `Mapping output already exists; refusing to overwrite it: ${output}`);\n }\n throw error;\n }\n return output;\n}\n\nexport function normalizedMapping(inventory: OpenSpecInventory, mapping: OpenSpecMapping): Record<string, unknown> {\n return {\n version: 1,\n source: { root: inventory.root, inventoryDigest: inventory.inventoryDigest },\n capabilities: Object.fromEntries(inventory.mappingDecisions.map((decision) => [\n decision.capability,\n {\n services: ownValue(mapping.capabilities, decision.capability)?.services ?? [],\n requirementServices: ownValue(mapping.capabilities, decision.capability)?.requirementServices ?? dictionary(),\n },\n ])),\n changes: Object.fromEntries(inventory.changeDecisions.map((decision) => [\n decision.change,\n {\n feature: ownValue(mapping.changes, decision.change)?.feature ?? null,\n // The decision's title, which is the mapping's own title trimmed.\n // `readMapping` (`./mapping.ts`) trims `feature` but not `title`, and\n // materialization writes the trimmed one into the frontmatter, the H1\n // and the directory slug — so recording the raw one made the plan\n // disagree with the files beside it about what the feature is called.\n title: decision.title,\n },\n ])),\n renames: Object.fromEntries(\n inventory.renamed\n .filter((rename) => rename.scope === \"active\")\n .map((rename) => [rename.key, {\n from: rename.from,\n to: rename.to,\n existingRequirementId: rename.existingRequirementId,\n requirementId: rename.requirementId,\n }]),\n ),\n artifacts: Object.fromEntries(inventory.artifactDecisions.map((decision) => [\n decision.path,\n {\n kind: decision.kind,\n disposition: ownValue(mapping.artifacts, decision.path) ?? null,\n },\n ])),\n };\n}\n\nexport function selectedServices(\n mapping: OpenSpecMapping,\n capability: string,\n requirement: string,\n): string[] {\n const selected = ownValue(mapping.capabilities, capability);\n if (selected === undefined) return [];\n return selected.services.length === 1\n ? selected.services\n : ownValue(selected.requirementServices, requirement) ?? [];\n}\n"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {} from "../../../core/envelope/json.js";
|
|
2
|
+
/**
|
|
3
|
+
* The one refusal shape both verbs raise, in a module of its own.
|
|
4
|
+
*
|
|
5
|
+
* It cannot live beside the commander wiring that catches it: every phase of a
|
|
6
|
+
* migration throws it, and a phase that imported the wiring to reach it would
|
|
7
|
+
* put the command's registration on the evaluation path of the modules being
|
|
8
|
+
* registered. So the error is the package's lowest leaf, and the wiring — which
|
|
9
|
+
* maps it onto an exit code in `reportCommandError` — sits above everything.
|
|
10
|
+
*/
|
|
11
|
+
export class OpenSpecCommandError extends Error {
|
|
12
|
+
code;
|
|
13
|
+
constructor(code, message) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.code = code;
|
|
16
|
+
this.name = "OpenSpecCommandError";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=error.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error.js","sourceRoot":"","sources":["../../../../src/commands/migrate-openspec/openspec/error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,MAAM,gCAAgC,CAAC;AAEhE;;;;;;;;GAQG;AACH,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IACxB,IAAI;IAAzB,YAAqB,IAAe,EAAE,OAAe;QACnD,KAAK,CAAC,OAAO,CAAC,CAAC;oBADI,IAAI;QAEvB,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACrC,CAAC;CACF","sourcesContent":["import { type ErrorCode } from \"../../../core/envelope/json.js\";\n\n/**\n * The one refusal shape both verbs raise, in a module of its own.\n *\n * It cannot live beside the commander wiring that catches it: every phase of a\n * migration throws it, and a phase that imported the wiring to reach it would\n * put the command's registration on the evaluation path of the modules being\n * registered. So the error is the package's lowest leaf, and the wiring — which\n * maps it onto an exit code in `reportCommandError` — sits above everything.\n */\nexport class OpenSpecCommandError extends Error {\n constructor(readonly code: ErrorCode, message: string) {\n super(message);\n this.name = \"OpenSpecCommandError\";\n }\n}\n"]}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The mapping document on the way in: YAML a human edited, graded into an
|
|
3
|
+
* `OpenSpecMapping` or refused with the line to fix.
|
|
4
|
+
*
|
|
5
|
+
* Split from `./decisions.ts`, which is the same document on the way out — the
|
|
6
|
+
* blank skeleton, the normalized record written beside a migration, and the
|
|
7
|
+
* routing query. Reading is where every refusal lives and writing is where none
|
|
8
|
+
* do, so the two halves have almost no vocabulary in common beyond the type.
|
|
9
|
+
*/
|
|
10
|
+
import { readFile } from "node:fs/promises";
|
|
11
|
+
import { parse as parseYaml } from "yaml";
|
|
12
|
+
import { serviceIdProblem } from "../../../core/kernel/ids/service.js";
|
|
13
|
+
import { asRecord, dictionary } from "../../../core/kernel/records.js";
|
|
14
|
+
import {} from "../../../core/openspec/model/mapping.js";
|
|
15
|
+
import { message } from "../../../core/staging/commit.js";
|
|
16
|
+
import { OpenSpecCommandError } from "./error.js";
|
|
17
|
+
import { decodeSource } from "./read.js";
|
|
18
|
+
/**
|
|
19
|
+
* `Array.isArray` narrows an `unknown` to `any[]`, so the element check below it
|
|
20
|
+
* proved nothing to tsc and the elements had to be asserted back to `string` at
|
|
21
|
+
* the point of use. One predicate makes the narrowing carry the check.
|
|
22
|
+
*/
|
|
23
|
+
function isStringArray(value) {
|
|
24
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
25
|
+
}
|
|
26
|
+
function stringList(value, label) {
|
|
27
|
+
if (typeof value === "string")
|
|
28
|
+
return value.trim() === "" ? [] : [value.trim()];
|
|
29
|
+
if (!isStringArray(value)) {
|
|
30
|
+
throw new OpenSpecCommandError("invalid-option", `${label} must be a string or an array of service ids.`);
|
|
31
|
+
}
|
|
32
|
+
return [...new Set(value.map((item) => item.trim()).filter(Boolean))];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The service ids a mapping may name, checked against the ONE grammar.
|
|
36
|
+
*
|
|
37
|
+
* This used to carry its own copy of the rule — the same alphabet regex, the
|
|
38
|
+
* `.`/`..` tests, the trailing dot and the Windows device names — which is how
|
|
39
|
+
* the two spellings drifted apart to begin with: for a while migrate was the
|
|
40
|
+
* STRICTER of the two, so the primary authoring path accepted ids a migration
|
|
41
|
+
* refused. ids.ts owns the grammar now, so a mapping can only ever name a
|
|
42
|
+
* `services/<id>/` the authoring commands can still address.
|
|
43
|
+
*
|
|
44
|
+
* The refusal keeps migrate's own shape — `OpenSpecCommandError` with
|
|
45
|
+
* `invalid-option`, not an `Issue` — and `serviceIdProblem`'s label carries the
|
|
46
|
+
* mapping key, so the message still points at the line of the YAML to edit.
|
|
47
|
+
*/
|
|
48
|
+
function serviceList(value, label) {
|
|
49
|
+
const services = stringList(value, label);
|
|
50
|
+
for (const service of services) {
|
|
51
|
+
const problem = serviceIdProblem(service, label);
|
|
52
|
+
if (problem !== null)
|
|
53
|
+
throw new OpenSpecCommandError("invalid-option", problem);
|
|
54
|
+
}
|
|
55
|
+
return services;
|
|
56
|
+
}
|
|
57
|
+
export async function readMapping(path) {
|
|
58
|
+
let bytes;
|
|
59
|
+
try {
|
|
60
|
+
bytes = await readFile(path);
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
throw new OpenSpecCommandError("unknown-target", `Cannot read OpenSpec mapping ${path}: ${message(error)}`);
|
|
64
|
+
}
|
|
65
|
+
// Decoded rather than assumed: a mapping saved as UTF-16 parses as YAML with
|
|
66
|
+
// no capabilities block, and the refusal would name a missing decision
|
|
67
|
+
// instead of the encoding that hid it.
|
|
68
|
+
const raw = decodeSource(bytes, path, "OpenSpec mapping");
|
|
69
|
+
let parsed;
|
|
70
|
+
try {
|
|
71
|
+
parsed = parseYaml(raw);
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
throw new OpenSpecCommandError("invalid-option", `OpenSpec mapping ${path} is invalid YAML: ${message(error)}`);
|
|
75
|
+
}
|
|
76
|
+
const document = asRecord(parsed);
|
|
77
|
+
if (document === null || document.version !== 1) {
|
|
78
|
+
throw new OpenSpecCommandError("invalid-option", `OpenSpec mapping ${path} must be a YAML mapping with version: 1.`);
|
|
79
|
+
}
|
|
80
|
+
const sourceNode = asRecord(document.source);
|
|
81
|
+
const source = sourceNode !== null
|
|
82
|
+
&& typeof sourceNode.root === "string"
|
|
83
|
+
&& typeof sourceNode.inventoryDigest === "string"
|
|
84
|
+
? { root: sourceNode.root, inventoryDigest: sourceNode.inventoryDigest }
|
|
85
|
+
: null;
|
|
86
|
+
const capabilitiesNode = asRecord(document.capabilities);
|
|
87
|
+
if (capabilitiesNode === null) {
|
|
88
|
+
throw new OpenSpecCommandError("invalid-option", `OpenSpec mapping ${path} must contain a capabilities mapping.`);
|
|
89
|
+
}
|
|
90
|
+
const capabilities = dictionary();
|
|
91
|
+
for (const [capability, value] of Object.entries(capabilitiesNode)) {
|
|
92
|
+
const record = asRecord(value);
|
|
93
|
+
const services = serviceList(record?.services ?? value, `capabilities.${capability}.services`);
|
|
94
|
+
const requirementServices = dictionary();
|
|
95
|
+
if (record?.requirementServices !== undefined) {
|
|
96
|
+
const allocations = asRecord(record.requirementServices);
|
|
97
|
+
if (allocations === null) {
|
|
98
|
+
throw new OpenSpecCommandError("invalid-option", `capabilities.${capability}.requirementServices must be a mapping.`);
|
|
99
|
+
}
|
|
100
|
+
for (const [requirement, selected] of Object.entries(allocations)) {
|
|
101
|
+
requirementServices[requirement] = serviceList(selected, `capabilities.${capability}.requirementServices.${requirement}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
capabilities[capability] = { services, requirementServices };
|
|
105
|
+
}
|
|
106
|
+
const changesNode = asRecord(document.changes);
|
|
107
|
+
if (changesNode === null) {
|
|
108
|
+
throw new OpenSpecCommandError("invalid-option", `OpenSpec mapping ${path} must contain a changes mapping.`);
|
|
109
|
+
}
|
|
110
|
+
const changes = dictionary();
|
|
111
|
+
for (const [change, value] of Object.entries(changesNode)) {
|
|
112
|
+
const record = asRecord(value);
|
|
113
|
+
if (record === null) {
|
|
114
|
+
throw new OpenSpecCommandError("invalid-option", `changes.${change} must be a mapping with explicit feature and title fields.`);
|
|
115
|
+
}
|
|
116
|
+
const featureValue = record.feature;
|
|
117
|
+
if (featureValue !== null && typeof featureValue !== "string") {
|
|
118
|
+
throw new OpenSpecCommandError("invalid-option", `changes.${change}.feature must be a string or null.`);
|
|
119
|
+
}
|
|
120
|
+
if (typeof record.title !== "string") {
|
|
121
|
+
throw new OpenSpecCommandError("invalid-option", `changes.${change}.title must be a string.`);
|
|
122
|
+
}
|
|
123
|
+
changes[change] = {
|
|
124
|
+
feature: typeof featureValue === "string" && featureValue.trim() !== "" ? featureValue.trim() : null,
|
|
125
|
+
title: record.title,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
const renames = dictionary();
|
|
129
|
+
if (document.renames !== undefined) {
|
|
130
|
+
const renamesNode = asRecord(document.renames);
|
|
131
|
+
if (renamesNode === null) {
|
|
132
|
+
throw new OpenSpecCommandError("invalid-option", `OpenSpec mapping ${path} renames must be a mapping.`);
|
|
133
|
+
}
|
|
134
|
+
for (const [key, value] of Object.entries(renamesNode)) {
|
|
135
|
+
const record = asRecord(value);
|
|
136
|
+
const requirementId = record !== null && "requirementId" in record ? record.requirementId : value;
|
|
137
|
+
// A freshly generated skeleton deliberately uses null: it remains a
|
|
138
|
+
// needsIdentity decision rather than becoming a malformed mapping.
|
|
139
|
+
if (requirementId === null || requirementId === "")
|
|
140
|
+
continue;
|
|
141
|
+
if (typeof requirementId !== "string") {
|
|
142
|
+
throw new OpenSpecCommandError("invalid-option", `renames.${key}.requirementId must be a string or null.`);
|
|
143
|
+
}
|
|
144
|
+
renames[key] = requirementId.trim();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Recorded as written. Naming the disposition union here only meant asserting
|
|
148
|
+
// an unreviewed string into it: `mapping.invalid-artifact-disposition` is what
|
|
149
|
+
// actually grades this value, and it has to see what the human typed to be
|
|
150
|
+
// able to name it back to them.
|
|
151
|
+
const artifacts = dictionary();
|
|
152
|
+
if (document.artifacts !== undefined) {
|
|
153
|
+
const artifactsNode = asRecord(document.artifacts);
|
|
154
|
+
if (artifactsNode === null) {
|
|
155
|
+
throw new OpenSpecCommandError("invalid-option", `OpenSpec mapping ${path} artifacts must be a mapping.`);
|
|
156
|
+
}
|
|
157
|
+
for (const [artifactPath, value] of Object.entries(artifactsNode)) {
|
|
158
|
+
const record = asRecord(value);
|
|
159
|
+
const disposition = record !== null && "disposition" in record ? record.disposition : value;
|
|
160
|
+
if (disposition === null || disposition === "")
|
|
161
|
+
continue;
|
|
162
|
+
if (typeof disposition !== "string") {
|
|
163
|
+
throw new OpenSpecCommandError("invalid-option", `artifacts.${artifactPath}.disposition must be a string or null.`);
|
|
164
|
+
}
|
|
165
|
+
artifacts[artifactPath] = disposition;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return { source, capabilities, changes, renames, artifacts };
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=mapping.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mapping.js","sourceRoot":"","sources":["../../../../src/commands/migrate-openspec/openspec/mapping.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAC;AACvE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,EAAwB,MAAM,yCAAyC,CAAC;AAC/E,OAAO,EAAE,OAAO,EAAE,MAAM,iCAAiC,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAEzC;;;;GAIG;AACH,SAAS,aAAa,CAAC,KAAc;IACnC,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC;AACjF,CAAC;AAED,SAAS,UAAU,CAAC,KAAc,EAAE,KAAa;IAC/C,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IAChF,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,oBAAoB,CAAC,gBAAgB,EAAE,GAAG,KAAK,+CAA+C,CAAC,CAAC;IAC5G,CAAC;IACD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AACxE,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,SAAS,WAAW,CAAC,KAAc,EAAE,KAAa;IAChD,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC1C,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,OAAO,GAAG,gBAAgB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACjD,IAAI,OAAO,KAAK,IAAI;YAAE,MAAM,IAAI,oBAAoB,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;IAClF,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAY;IAC5C,IAAI,KAAa,CAAC;IAClB,IAAI,CAAC;QACH,KAAK,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,oBAAoB,CAAC,gBAAgB,EAAE,gCAAgC,IAAI,KAAK,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC9G,CAAC;IACD,6EAA6E;IAC7E,uEAAuE;IACvE,uCAAuC;IACvC,MAAM,GAAG,GAAG,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE,kBAAkB,CAAC,CAAC;IAC1D,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,oBAAoB,CAAC,gBAAgB,EAAE,oBAAoB,IAAI,qBAAqB,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAClH,CAAC;IACD,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;IAClC,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,CAAC,OAAO,KAAK,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,oBAAoB,CAAC,gBAAgB,EAAE,oBAAoB,IAAI,0CAA0C,CAAC,CAAC;IACvH,CAAC;IACD,MAAM,UAAU,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC7C,MAAM,MAAM,GAAG,UAAU,KAAK,IAAI;WAC7B,OAAO,UAAU,CAAC,IAAI,KAAK,QAAQ;WACnC,OAAO,UAAU,CAAC,eAAe,KAAK,QAAQ;QACjD,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,eAAe,EAAE,UAAU,CAAC,eAAe,EAAE;QACxE,CAAC,CAAC,IAAI,CAAC;IACT,MAAM,gBAAgB,GAAG,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;IACzD,IAAI,gBAAgB,KAAK,IAAI,EAAE,CAAC;QAC9B,MAAM,IAAI,oBAAoB,CAAC,gBAAgB,EAAE,oBAAoB,IAAI,uCAAuC,CAAC,CAAC;IACpH,CAAC;IACD,MAAM,YAAY,GAAG,UAAU,EAA2C,CAAC;IAC3E,KAAK,MAAM,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACnE,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;QAC/B,MAAM,QAAQ,GAAG,WAAW,CAAC,MAAM,EAAE,QAAQ,IAAI,KAAK,EAAE,gBAAgB,UAAU,WAAW,CAAC,CAAC;QAC/F,MAAM,mBAAmB,GAAG,UAAU,EAAY,CAAC;QACnD,IAAI,MAAM,EAAE,mBAAmB,KAAK,SAAS,EAAE,CAAC;YAC9C,MAAM,WAAW,GAAG,QAAQ,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;YACzD,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;gBACzB,MAAM,IAAI,oBAAoB,CAC5B,gBAAgB,EAChB,gBAAgB,UAAU,yCAAyC,CACpE,CAAC;YACJ,CAAC;YACD,KAAK,MAAM,CAAC,WAAW,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;gBAClE,mBAAmB,CAAC,WAAW,CAAC,GAAG,WAAW,CAC5C,QAAQ,EACR,gBAAgB,UAAU,wBAAwB,WAAW,EAAE,CAChE,CAAC;YACJ,CAAC;QACH,CAAC;QACD,YAAY,CAAC,UAAU,CAAC,GAAG,EAAE,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAC/D,CAAC;IAED,MAAM,WAAW,GAAG,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC/C,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;QACzB,MAAM,IAAI,oBAAoB,CAAC,gBAAgB,EAAE,oBAAoB,IAAI,kCAAkC,CAAC,CAAC;IAC/G,CAAC;IACD,MAAM,OAAO,GAAG,UAAU,EAAsC,CAAC;IACjE,KAAK,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;QAC1D,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;QAC/B,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACpB,MAAM,IAAI,oBAAoB,CAC5B,gBAAgB,EAChB,WAAW,MAAM,4DAA4D,CAC9E,CAAC;QACJ,CAAC;QACD,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC;QACpC,IAAI,YAAY,KAAK,IAAI,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE,CAAC;YAC9D,MAAM,IAAI,oBAAoB,CAAC,gBAAgB,EAAE,WAAW,MAAM,oCAAoC,CAAC,CAAC;QAC1G,CAAC;QACD,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YACrC,MAAM,IAAI,oBAAoB,CAAC,gBAAgB,EAAE,WAAW,MAAM,0BAA0B,CAAC,CAAC;QAChG,CAAC;QACD,OAAO,CAAC,MAAM,CAAC,GAAG;YAChB,OAAO,EAAE,OAAO,YAAY,KAAK,QAAQ,IAAI,YAAY,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI;YACpG,KAAK,EAAE,MAAM,CAAC,KAAK;SACpB,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,UAAU,EAAU,CAAC;IACrC,IAAI,QAAQ,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QACnC,MAAM,WAAW,GAAG,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC/C,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;YACzB,MAAM,IAAI,oBAAoB,CAAC,gBAAgB,EAAE,oBAAoB,IAAI,6BAA6B,CAAC,CAAC;QAC1G,CAAC;QACD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;YACvD,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;YAC/B,MAAM,aAAa,GAAG,MAAM,KAAK,IAAI,IAAI,eAAe,IAAI,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC;YAClG,oEAAoE;YACpE,mEAAmE;YACnE,IAAI,aAAa,KAAK,IAAI,IAAI,aAAa,KAAK,EAAE;gBAAE,SAAS;YAC7D,IAAI,OAAO,aAAa,KAAK,QAAQ,EAAE,CAAC;gBACtC,MAAM,IAAI,oBAAoB,CAAC,gBAAgB,EAAE,WAAW,GAAG,0CAA0C,CAAC,CAAC;YAC7G,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,GAAG,aAAa,CAAC,IAAI,EAAE,CAAC;QACtC,CAAC;IACH,CAAC;IACD,8EAA8E;IAC9E,+EAA+E;IAC/E,2EAA2E;IAC3E,gCAAgC;IAChC,MAAM,SAAS,GAAG,UAAU,EAAU,CAAC;IACvC,IAAI,QAAQ,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QACrC,MAAM,aAAa,GAAG,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QACnD,IAAI,aAAa,KAAK,IAAI,EAAE,CAAC;YAC3B,MAAM,IAAI,oBAAoB,CAAC,gBAAgB,EAAE,oBAAoB,IAAI,+BAA+B,CAAC,CAAC;QAC5G,CAAC;QACD,KAAK,MAAM,CAAC,YAAY,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;YAClE,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;YAC/B,MAAM,WAAW,GAAG,MAAM,KAAK,IAAI,IAAI,aAAa,IAAI,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC;YAC5F,IAAI,WAAW,KAAK,IAAI,IAAI,WAAW,KAAK,EAAE;gBAAE,SAAS;YACzD,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE,CAAC;gBACpC,MAAM,IAAI,oBAAoB,CAAC,gBAAgB,EAAE,aAAa,YAAY,wCAAwC,CAAC,CAAC;YACtH,CAAC;YACD,SAAS,CAAC,YAAY,CAAC,GAAG,WAAW,CAAC;QACxC,CAAC;IACH,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;AAC/D,CAAC","sourcesContent":["/**\n * The mapping document on the way in: YAML a human edited, graded into an\n * `OpenSpecMapping` or refused with the line to fix.\n *\n * Split from `./decisions.ts`, which is the same document on the way out — the\n * blank skeleton, the normalized record written beside a migration, and the\n * routing query. Reading is where every refusal lives and writing is where none\n * do, so the two halves have almost no vocabulary in common beyond the type.\n */\nimport { readFile } from \"node:fs/promises\";\nimport { parse as parseYaml } from \"yaml\";\nimport { serviceIdProblem } from \"../../../core/kernel/ids/service.js\";\nimport { asRecord, dictionary } from \"../../../core/kernel/records.js\";\nimport { type OpenSpecMapping } from \"../../../core/openspec/model/mapping.js\";\nimport { message } from \"../../../core/staging/commit.js\";\nimport { OpenSpecCommandError } from \"./error.js\";\nimport { decodeSource } from \"./read.js\";\n\n/**\n * `Array.isArray` narrows an `unknown` to `any[]`, so the element check below it\n * proved nothing to tsc and the elements had to be asserted back to `string` at\n * the point of use. One predicate makes the narrowing carry the check.\n */\nfunction isStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.every((item) => typeof item === \"string\");\n}\n\nfunction stringList(value: unknown, label: string): string[] {\n if (typeof value === \"string\") return value.trim() === \"\" ? [] : [value.trim()];\n if (!isStringArray(value)) {\n throw new OpenSpecCommandError(\"invalid-option\", `${label} must be a string or an array of service ids.`);\n }\n return [...new Set(value.map((item) => item.trim()).filter(Boolean))];\n}\n\n/**\n * The service ids a mapping may name, checked against the ONE grammar.\n *\n * This used to carry its own copy of the rule — the same alphabet regex, the\n * `.`/`..` tests, the trailing dot and the Windows device names — which is how\n * the two spellings drifted apart to begin with: for a while migrate was the\n * STRICTER of the two, so the primary authoring path accepted ids a migration\n * refused. ids.ts owns the grammar now, so a mapping can only ever name a\n * `services/<id>/` the authoring commands can still address.\n *\n * The refusal keeps migrate's own shape — `OpenSpecCommandError` with\n * `invalid-option`, not an `Issue` — and `serviceIdProblem`'s label carries the\n * mapping key, so the message still points at the line of the YAML to edit.\n */\nfunction serviceList(value: unknown, label: string): string[] {\n const services = stringList(value, label);\n for (const service of services) {\n const problem = serviceIdProblem(service, label);\n if (problem !== null) throw new OpenSpecCommandError(\"invalid-option\", problem);\n }\n return services;\n}\n\nexport async function readMapping(path: string): Promise<OpenSpecMapping> {\n let bytes: Buffer;\n try {\n bytes = await readFile(path);\n } catch (error) {\n throw new OpenSpecCommandError(\"unknown-target\", `Cannot read OpenSpec mapping ${path}: ${message(error)}`);\n }\n // Decoded rather than assumed: a mapping saved as UTF-16 parses as YAML with\n // no capabilities block, and the refusal would name a missing decision\n // instead of the encoding that hid it.\n const raw = decodeSource(bytes, path, \"OpenSpec mapping\");\n let parsed: unknown;\n try {\n parsed = parseYaml(raw);\n } catch (error) {\n throw new OpenSpecCommandError(\"invalid-option\", `OpenSpec mapping ${path} is invalid YAML: ${message(error)}`);\n }\n const document = asRecord(parsed);\n if (document === null || document.version !== 1) {\n throw new OpenSpecCommandError(\"invalid-option\", `OpenSpec mapping ${path} must be a YAML mapping with version: 1.`);\n }\n const sourceNode = asRecord(document.source);\n const source = sourceNode !== null\n && typeof sourceNode.root === \"string\"\n && typeof sourceNode.inventoryDigest === \"string\"\n ? { root: sourceNode.root, inventoryDigest: sourceNode.inventoryDigest }\n : null;\n const capabilitiesNode = asRecord(document.capabilities);\n if (capabilitiesNode === null) {\n throw new OpenSpecCommandError(\"invalid-option\", `OpenSpec mapping ${path} must contain a capabilities mapping.`);\n }\n const capabilities = dictionary<OpenSpecMapping[\"capabilities\"][string]>();\n for (const [capability, value] of Object.entries(capabilitiesNode)) {\n const record = asRecord(value);\n const services = serviceList(record?.services ?? value, `capabilities.${capability}.services`);\n const requirementServices = dictionary<string[]>();\n if (record?.requirementServices !== undefined) {\n const allocations = asRecord(record.requirementServices);\n if (allocations === null) {\n throw new OpenSpecCommandError(\n \"invalid-option\",\n `capabilities.${capability}.requirementServices must be a mapping.`,\n );\n }\n for (const [requirement, selected] of Object.entries(allocations)) {\n requirementServices[requirement] = serviceList(\n selected,\n `capabilities.${capability}.requirementServices.${requirement}`,\n );\n }\n }\n capabilities[capability] = { services, requirementServices };\n }\n\n const changesNode = asRecord(document.changes);\n if (changesNode === null) {\n throw new OpenSpecCommandError(\"invalid-option\", `OpenSpec mapping ${path} must contain a changes mapping.`);\n }\n const changes = dictionary<OpenSpecMapping[\"changes\"][string]>();\n for (const [change, value] of Object.entries(changesNode)) {\n const record = asRecord(value);\n if (record === null) {\n throw new OpenSpecCommandError(\n \"invalid-option\",\n `changes.${change} must be a mapping with explicit feature and title fields.`,\n );\n }\n const featureValue = record.feature;\n if (featureValue !== null && typeof featureValue !== \"string\") {\n throw new OpenSpecCommandError(\"invalid-option\", `changes.${change}.feature must be a string or null.`);\n }\n if (typeof record.title !== \"string\") {\n throw new OpenSpecCommandError(\"invalid-option\", `changes.${change}.title must be a string.`);\n }\n changes[change] = {\n feature: typeof featureValue === \"string\" && featureValue.trim() !== \"\" ? featureValue.trim() : null,\n title: record.title,\n };\n }\n\n const renames = dictionary<string>();\n if (document.renames !== undefined) {\n const renamesNode = asRecord(document.renames);\n if (renamesNode === null) {\n throw new OpenSpecCommandError(\"invalid-option\", `OpenSpec mapping ${path} renames must be a mapping.`);\n }\n for (const [key, value] of Object.entries(renamesNode)) {\n const record = asRecord(value);\n const requirementId = record !== null && \"requirementId\" in record ? record.requirementId : value;\n // A freshly generated skeleton deliberately uses null: it remains a\n // needsIdentity decision rather than becoming a malformed mapping.\n if (requirementId === null || requirementId === \"\") continue;\n if (typeof requirementId !== \"string\") {\n throw new OpenSpecCommandError(\"invalid-option\", `renames.${key}.requirementId must be a string or null.`);\n }\n renames[key] = requirementId.trim();\n }\n }\n // Recorded as written. Naming the disposition union here only meant asserting\n // an unreviewed string into it: `mapping.invalid-artifact-disposition` is what\n // actually grades this value, and it has to see what the human typed to be\n // able to name it back to them.\n const artifacts = dictionary<string>();\n if (document.artifacts !== undefined) {\n const artifactsNode = asRecord(document.artifacts);\n if (artifactsNode === null) {\n throw new OpenSpecCommandError(\"invalid-option\", `OpenSpec mapping ${path} artifacts must be a mapping.`);\n }\n for (const [artifactPath, value] of Object.entries(artifactsNode)) {\n const record = asRecord(value);\n const disposition = record !== null && \"disposition\" in record ? record.disposition : value;\n if (disposition === null || disposition === \"\") continue;\n if (typeof disposition !== \"string\") {\n throw new OpenSpecCommandError(\"invalid-option\", `artifacts.${artifactPath}.disposition must be a string or null.`);\n }\n artifacts[artifactPath] = disposition;\n }\n }\n return { source, capabilities, changes, renames, artifacts };\n}\n"]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The path arithmetic every write decision in this command rests on.
|
|
3
|
+
*
|
|
4
|
+
* Three rules, kept together because they answer one question in two
|
|
5
|
+
* directions: is a path inside a tree it must stay out of (`contains`), what
|
|
6
|
+
* tree is it really in once symlinks are resolved (`canonicalForCreate`), and
|
|
7
|
+
* does a path claimed by the OpenSpec source stay inside the directory it was
|
|
8
|
+
* listed under (`safeArtifactRelative`). `materialize/stage.ts` builds the
|
|
9
|
+
* staging refusals out of the first two; `openspec/decisions.ts` uses them to
|
|
10
|
+
* keep a mapping skeleton out of the workspace it describes.
|
|
11
|
+
*/
|
|
12
|
+
import { existsSync } from "node:fs";
|
|
13
|
+
import { realpath } from "node:fs/promises";
|
|
14
|
+
import { basename, dirname, isAbsolute, relative, resolve, sep } from "node:path";
|
|
15
|
+
import { OpenSpecCommandError } from "./error.js";
|
|
16
|
+
export function contains(parent, candidate) {
|
|
17
|
+
const rel = relative(parent, candidate);
|
|
18
|
+
return rel === "" || (!rel.startsWith(`..${sep}`) && rel !== ".." && !isAbsolute(rel));
|
|
19
|
+
}
|
|
20
|
+
/** Resolve symlinks in the nearest existing ancestor of a not-yet-created path. */
|
|
21
|
+
export async function canonicalForCreate(path) {
|
|
22
|
+
let cursor = path;
|
|
23
|
+
const suffix = [];
|
|
24
|
+
while (!existsSync(cursor)) {
|
|
25
|
+
const parent = dirname(cursor);
|
|
26
|
+
if (parent === cursor)
|
|
27
|
+
break;
|
|
28
|
+
suffix.unshift(basename(cursor));
|
|
29
|
+
cursor = parent;
|
|
30
|
+
}
|
|
31
|
+
return resolve(await realpath(cursor), ...suffix);
|
|
32
|
+
}
|
|
33
|
+
export function safeArtifactRelative(artifactPath, prefix, owner) {
|
|
34
|
+
if (!artifactPath.startsWith(prefix)) {
|
|
35
|
+
throw new OpenSpecCommandError("invalid-option", `${owner} contains an artifact outside its own directory: ${artifactPath}.`);
|
|
36
|
+
}
|
|
37
|
+
const segments = artifactPath.slice(prefix.length).split("/");
|
|
38
|
+
if (segments.length === 0 || segments.some((segment) => segment === "" || segment === "." || segment === "..")) {
|
|
39
|
+
throw new OpenSpecCommandError("invalid-option", `OpenSpec artifact has an unsafe relative path: ${artifactPath}.`);
|
|
40
|
+
}
|
|
41
|
+
return segments;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../../../src/commands/migrate-openspec/openspec/paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAClF,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAElD,MAAM,UAAU,QAAQ,CAAC,MAAc,EAAE,SAAiB;IACxD,MAAM,GAAG,GAAG,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACxC,OAAO,GAAG,KAAK,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,GAAG,EAAE,CAAC,IAAI,GAAG,KAAK,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;AACzF,CAAC;AAED,mFAAmF;AACnF,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,IAAY;IACnD,IAAI,MAAM,GAAG,IAAI,CAAC;IAClB,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;QAC/B,IAAI,MAAM,KAAK,MAAM;YAAE,MAAM;QAC7B,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;QACjC,MAAM,GAAG,MAAM,CAAC;IAClB,CAAC;IACD,OAAO,OAAO,CAAC,MAAM,QAAQ,CAAC,MAAM,CAAC,EAAE,GAAG,MAAM,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,YAAoB,EAAE,MAAc,EAAE,KAAa;IACtF,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,oBAAoB,CAC5B,gBAAgB,EAChB,GAAG,KAAK,oDAAoD,YAAY,GAAG,CAC5E,CAAC;IACJ,CAAC;IACD,MAAM,QAAQ,GAAG,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC9D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,KAAK,EAAE,IAAI,OAAO,KAAK,GAAG,IAAI,OAAO,KAAK,IAAI,CAAC,EAAE,CAAC;QAC/G,MAAM,IAAI,oBAAoB,CAAC,gBAAgB,EAAE,kDAAkD,YAAY,GAAG,CAAC,CAAC;IACtH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC","sourcesContent":["/**\n * The path arithmetic every write decision in this command rests on.\n *\n * Three rules, kept together because they answer one question in two\n * directions: is a path inside a tree it must stay out of (`contains`), what\n * tree is it really in once symlinks are resolved (`canonicalForCreate`), and\n * does a path claimed by the OpenSpec source stay inside the directory it was\n * listed under (`safeArtifactRelative`). `materialize/stage.ts` builds the\n * staging refusals out of the first two; `openspec/decisions.ts` uses them to\n * keep a mapping skeleton out of the workspace it describes.\n */\nimport { existsSync } from \"node:fs\";\nimport { realpath } from \"node:fs/promises\";\nimport { basename, dirname, isAbsolute, relative, resolve, sep } from \"node:path\";\nimport { OpenSpecCommandError } from \"./error.js\";\n\nexport function contains(parent: string, candidate: string): boolean {\n const rel = relative(parent, candidate);\n return rel === \"\" || (!rel.startsWith(`..${sep}`) && rel !== \"..\" && !isAbsolute(rel));\n}\n\n/** Resolve symlinks in the nearest existing ancestor of a not-yet-created path. */\nexport async function canonicalForCreate(path: string): Promise<string> {\n let cursor = path;\n const suffix: string[] = [];\n while (!existsSync(cursor)) {\n const parent = dirname(cursor);\n if (parent === cursor) break;\n suffix.unshift(basename(cursor));\n cursor = parent;\n }\n return resolve(await realpath(cursor), ...suffix);\n}\n\nexport function safeArtifactRelative(artifactPath: string, prefix: string, owner: string): string[] {\n if (!artifactPath.startsWith(prefix)) {\n throw new OpenSpecCommandError(\n \"invalid-option\",\n `${owner} contains an artifact outside its own directory: ${artifactPath}.`,\n );\n }\n const segments = artifactPath.slice(prefix.length).split(\"/\");\n if (segments.length === 0 || segments.some((segment) => segment === \"\" || segment === \".\" || segment === \"..\")) {\n throw new OpenSpecCommandError(\"invalid-option\", `OpenSpec artifact has an unsafe relative path: ${artifactPath}.`);\n }\n return segments;\n}\n"]}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bytes out of somebody else's repository, decoded or refused by name.
|
|
3
|
+
*
|
|
4
|
+
* Every other phase reads its input through here rather than calling `readFile`
|
|
5
|
+
* itself, which is the point: the encoding rule below is one line of code, and
|
|
6
|
+
* the defect it prevents is a migration that reports success over requirements
|
|
7
|
+
* it silently dropped. A read added later to a materializer has to come through
|
|
8
|
+
* this module to reach the source at all.
|
|
9
|
+
*/
|
|
10
|
+
import { readFile } from "node:fs/promises";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { decodeDocument, NotUtf8DocumentError } from "../../../core/kernel/document-bytes.js";
|
|
13
|
+
import { OpenSpecCommandError } from "./error.js";
|
|
14
|
+
/**
|
|
15
|
+
* A document this command is about to migrate, decoded or refused BY NAME.
|
|
16
|
+
*
|
|
17
|
+
* Every other read on this path used `readFile(…, "utf8")`, which never fails:
|
|
18
|
+
* bytes that are not UTF-8 become U+FFFD and a UTF-16 spec.md becomes text with
|
|
19
|
+
* no headings in it. Downstream that is not a wrong answer but a silent one —
|
|
20
|
+
* `parseRequirements` returns [], the capability materializes as a `spec.md`
|
|
21
|
+
* with an empty `## Requirements`, and the migration reports success over a file
|
|
22
|
+
* whose every requirement was dropped. Migration is the one command whose input
|
|
23
|
+
* is somebody else's repository, written by tooling loam does not control, so
|
|
24
|
+
* this is where lossy decoding is both most likely and least acceptable.
|
|
25
|
+
*
|
|
26
|
+
* The ingest scan already refuses non-UTF-8 artifacts up front
|
|
27
|
+
* (`openspec.non-utf8-artifact`); holding the same rule at the point of use
|
|
28
|
+
* means a read added later cannot quietly skip it, and it is what catches the
|
|
29
|
+
* encoding the scan's `isUtf8` cannot see — UTF-16 written without a BOM, whose
|
|
30
|
+
* bytes are valid UTF-8 with a NUL between every character.
|
|
31
|
+
*/
|
|
32
|
+
export function decodeSource(bytes, path, label) {
|
|
33
|
+
try {
|
|
34
|
+
return decodeDocument(bytes, path);
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
if (!(error instanceof NotUtf8DocumentError))
|
|
38
|
+
throw error;
|
|
39
|
+
// `error.message` already opens with the path; the label says which of the
|
|
40
|
+
// two inputs it is — the corpus being migrated, or the mapping about it.
|
|
41
|
+
throw new OpenSpecCommandError("invalid-option", `${label}: ${error.message}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** A path under the audited OpenSpec root, decoded the same way. */
|
|
45
|
+
export async function readSourceArtifact(root, artifactPath) {
|
|
46
|
+
const absolute = join(root, artifactPath);
|
|
47
|
+
return decodeSource(await readFile(absolute), absolute, "OpenSpec source artifact");
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=read.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read.js","sourceRoot":"","sources":["../../../../src/commands/migrate-openspec/openspec/read.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAC;AAC9F,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAElD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,YAAY,CAAC,KAAa,EAAE,IAAY,EAAE,KAAa;IACrE,IAAI,CAAC;QACH,OAAO,cAAc,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACrC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,CAAC,KAAK,YAAY,oBAAoB,CAAC;YAAE,MAAM,KAAK,CAAC;QAC1D,2EAA2E;QAC3E,yEAAyE;QACzE,MAAM,IAAI,oBAAoB,CAAC,gBAAgB,EAAE,GAAG,KAAK,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IACjF,CAAC;AACH,CAAC;AAED,oEAAoE;AACpE,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,IAAY,EAAE,YAAoB;IACzE,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;IAC1C,OAAO,YAAY,CAAC,MAAM,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,0BAA0B,CAAC,CAAC;AACtF,CAAC","sourcesContent":["/**\n * Bytes out of somebody else's repository, decoded or refused by name.\n *\n * Every other phase reads its input through here rather than calling `readFile`\n * itself, which is the point: the encoding rule below is one line of code, and\n * the defect it prevents is a migration that reports success over requirements\n * it silently dropped. A read added later to a materializer has to come through\n * this module to reach the source at all.\n */\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { decodeDocument, NotUtf8DocumentError } from \"../../../core/kernel/document-bytes.js\";\nimport { OpenSpecCommandError } from \"./error.js\";\n\n/**\n * A document this command is about to migrate, decoded or refused BY NAME.\n *\n * Every other read on this path used `readFile(…, \"utf8\")`, which never fails:\n * bytes that are not UTF-8 become U+FFFD and a UTF-16 spec.md becomes text with\n * no headings in it. Downstream that is not a wrong answer but a silent one —\n * `parseRequirements` returns [], the capability materializes as a `spec.md`\n * with an empty `## Requirements`, and the migration reports success over a file\n * whose every requirement was dropped. Migration is the one command whose input\n * is somebody else's repository, written by tooling loam does not control, so\n * this is where lossy decoding is both most likely and least acceptable.\n *\n * The ingest scan already refuses non-UTF-8 artifacts up front\n * (`openspec.non-utf8-artifact`); holding the same rule at the point of use\n * means a read added later cannot quietly skip it, and it is what catches the\n * encoding the scan's `isUtf8` cannot see — UTF-16 written without a BOM, whose\n * bytes are valid UTF-8 with a NUL between every character.\n */\nexport function decodeSource(bytes: Buffer, path: string, label: string): string {\n try {\n return decodeDocument(bytes, path);\n } catch (error) {\n if (!(error instanceof NotUtf8DocumentError)) throw error;\n // `error.message` already opens with the path; the label says which of the\n // two inputs it is — the corpus being migrated, or the mapping about it.\n throw new OpenSpecCommandError(\"invalid-option\", `${label}: ${error.message}`);\n }\n}\n\n/** A path under the audited OpenSpec root, decoded the same way. */\nexport async function readSourceArtifact(root: string, artifactPath: string): Promise<string> {\n const absolute = join(root, artifactPath);\n return decodeSource(await readFile(absolute), absolute, \"OpenSpec source artifact\");\n}\n"]}
|