@ybotok/loam 0.1.0-beta.3 → 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 +2548 -86
- 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 +2355 -256
- 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 +72 -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/{agents-stamp.js → agent/agents-stamp.js} +50 -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.js → asyncapi/read.js} +95 -109
- 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 +7 -64
- 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 +120 -78
- 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 -295
- 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 -262
- package/dist/commands/status.js.map +0 -1
- package/dist/commands/unarchive.js.map +0 -1
- package/dist/commands/validate.js +0 -1773
- 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 -421
- package/dist/commands/vouch.js.map +0 -1
- package/dist/core/agent.js +0 -2173
- package/dist/core/agent.js.map +0 -1
- 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/asyncapi.js.map +0 -1
- package/dist/core/brief.js +0 -755
- 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 -596
- 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 -489
- 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 -787
- package/dist/core/provenance.js.map +0 -1
- package/dist/core/records.js.map +0 -1
- package/dist/core/repo.js +0 -438
- package/dist/core/repo.js.map +0 -1
- package/dist/core/report.js.map +0 -1
- package/dist/core/spec.js +0 -505
- 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 -1132
- 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
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { readFile, writeFile } from "node:fs/promises";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { dirname, join, parse as parsePath, resolve } from "node:path";
|
|
4
|
-
import { resolveInside } from "
|
|
5
|
-
import {
|
|
4
|
+
import { resolveInside } from "../kernel/path-safety.js";
|
|
5
|
+
import { docsDirOf } from "../kernel/ids/dirs.js";
|
|
6
|
+
import { parseServiceId } from "../kernel/ids/service.js";
|
|
6
7
|
/** Local config, committed at the root of a service repo (or the docs repo itself). */
|
|
7
8
|
export const CONFIG_FILENAME = "loam.json";
|
|
8
9
|
/**
|
|
@@ -103,15 +104,26 @@ export function parseConfig(raw, configDir) {
|
|
|
103
104
|
// Same discipline as docsDir: a malformed fact refuses the whole config
|
|
104
105
|
// rather than being silently defaulted over — `5` or `""` here is a typo,
|
|
105
106
|
// and defaulting would send generated files somewhere nobody chose.
|
|
107
|
+
// `parseServiceId` rather than a bare check: the same call both refuses the
|
|
108
|
+
// malformed value and produces the branded one below, so there is no window
|
|
109
|
+
// in which a checked name is still typed as an unchecked string. It takes a
|
|
110
|
+
// `string`, hence the shape guard first — the id grammar's own message is
|
|
111
|
+
// about spelling, and `5` is not a spelling.
|
|
112
|
+
let service;
|
|
106
113
|
if (record.service !== undefined) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
114
|
+
if (typeof record.service !== "string") {
|
|
115
|
+
throw new ConfigError("service", `${file}: "service" must be a string.`);
|
|
116
|
+
}
|
|
117
|
+
const parsed = parseServiceId(record.service, '"service"');
|
|
118
|
+
if (!parsed.ok)
|
|
119
|
+
throw new ConfigError("service", `${file}: ${parsed.problem}`);
|
|
120
|
+
service = parsed.id;
|
|
110
121
|
}
|
|
111
122
|
if (record.gherkinDir !== undefined
|
|
112
123
|
&& (typeof record.gherkinDir !== "string" || record.gherkinDir === "")) {
|
|
113
124
|
throw new ConfigError("gherkinDir", `${file}: "gherkinDir" must be a non-empty string when present.`);
|
|
114
125
|
}
|
|
126
|
+
const contracts = readContracts(record.contracts, file);
|
|
115
127
|
// Shape only. Which ids are legal is the registry's question, and asking it
|
|
116
128
|
// here would make a config written by a newer binary — one that knows a tool
|
|
117
129
|
// this one does not — unloadable for every command, not just for `init`.
|
|
@@ -120,6 +132,21 @@ export function parseConfig(raw, configDir) {
|
|
|
120
132
|
|| record.agentTools.some((t) => typeof t !== "string" || t === ""))) {
|
|
121
133
|
throw new ConfigError("agentTools", `${file}: "agentTools" must be an array of non-empty strings when present.`);
|
|
122
134
|
}
|
|
135
|
+
if (record.agentFiles !== undefined) {
|
|
136
|
+
if (record.agentFiles === null || typeof record.agentFiles !== "object" || Array.isArray(record.agentFiles)) {
|
|
137
|
+
throw new ConfigError("agentFiles", `${file}: "agentFiles" must be a path-to-digest mapping when present.`);
|
|
138
|
+
}
|
|
139
|
+
const entries = Object.entries(record.agentFiles);
|
|
140
|
+
if (entries.some(([path, digest]) => path === "" || typeof digest !== "string" || !/^[a-f0-9]{64}$/.test(digest))) {
|
|
141
|
+
throw new ConfigError("agentFiles", `${file}: "agentFiles" must map non-empty paths to lowercase sha256 digests.`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (record.agentProfile !== undefined
|
|
145
|
+
&& record.agentProfile !== "full"
|
|
146
|
+
&& record.agentProfile !== "service"
|
|
147
|
+
&& record.agentProfile !== "docs") {
|
|
148
|
+
throw new ConfigError("agentProfile", `${file}: "agentProfile" must be one of: full, service, docs.`);
|
|
149
|
+
}
|
|
123
150
|
if (typeof record.gherkinDir === "string") {
|
|
124
151
|
// Validate the owned output directory, not merely its spelling. This also
|
|
125
152
|
// catches an otherwise-contained path whose existing parent is a symlink
|
|
@@ -133,11 +160,19 @@ export function parseConfig(raw, configDir) {
|
|
|
133
160
|
}
|
|
134
161
|
return {
|
|
135
162
|
// Resolved here, against the file's own directory, so the doc comment on
|
|
136
|
-
// `docsDir` is true no matter where a caller later resolves the path from
|
|
137
|
-
|
|
138
|
-
|
|
163
|
+
// `docsDir` is true no matter where a caller later resolves the path from —
|
|
164
|
+
// which is exactly the provenance `docsDirOf` demands.
|
|
165
|
+
docsDir: docsDirOf(resolve(configDir, record.docsDir)),
|
|
166
|
+
...(service === undefined ? {} : { service }),
|
|
139
167
|
...(record.gherkinDir === undefined ? {} : { gherkinDir: record.gherkinDir }),
|
|
168
|
+
...(contracts === undefined ? {} : { contracts }),
|
|
140
169
|
...(record.agentTools === undefined ? {} : { agentTools: record.agentTools }),
|
|
170
|
+
...(record.agentFiles === undefined
|
|
171
|
+
? {}
|
|
172
|
+
: { agentFiles: record.agentFiles }),
|
|
173
|
+
...(record.agentProfile === undefined
|
|
174
|
+
? {}
|
|
175
|
+
: { agentProfile: record.agentProfile }),
|
|
141
176
|
root: resolve(configDir),
|
|
142
177
|
docsDirAsWritten: record.docsDir,
|
|
143
178
|
};
|
|
@@ -145,7 +180,7 @@ export function parseConfig(raw, configDir) {
|
|
|
145
180
|
export async function loadConfig(cwd = process.cwd()) {
|
|
146
181
|
const p = findConfigPath(cwd);
|
|
147
182
|
if (p === null)
|
|
148
|
-
return
|
|
183
|
+
return { kind: "absent" };
|
|
149
184
|
try {
|
|
150
185
|
// The read belongs INSIDE the try: `existsSync` only says the name is
|
|
151
186
|
// taken, not that it names a file this process can read. A loam.json that
|
|
@@ -153,17 +188,11 @@ export async function loadConfig(cwd = process.cwd()) {
|
|
|
153
188
|
// deleted between the search and the read (ENOENT) all fail here rather
|
|
154
189
|
// than in `parseConfig` — and read outside, each of them escaped every
|
|
155
190
|
// command as an `internal` crash carrying a bare errno. Caught here they
|
|
156
|
-
// join the same "exists but cannot be believed" arm the parse failures
|
|
157
|
-
|
|
158
|
-
// designed `config-invalid` (or `no-config` if the file really has gone).
|
|
159
|
-
return parseConfig(await readFile(p, "utf8"), dirname(p));
|
|
191
|
+
// join the same "exists but cannot be believed" arm the parse failures take.
|
|
192
|
+
return { kind: "loaded", config: parseConfig(await readFile(p, "utf8"), dirname(p)) };
|
|
160
193
|
}
|
|
161
194
|
catch (err) {
|
|
162
|
-
|
|
163
|
-
// treat it as absent — commands fail with their normal hint, and `loam init`
|
|
164
|
-
// (which spreads the old config) can rewrite a corrupt file instead of dying on it.
|
|
165
|
-
console.error(`Invalid ${CONFIG_FILENAME}: ${err instanceof Error ? err.message : String(err)}`);
|
|
166
|
-
return null;
|
|
195
|
+
return { kind: "invalid", path: p, problem: err instanceof Error ? err.message : String(err) };
|
|
167
196
|
}
|
|
168
197
|
}
|
|
169
198
|
/**
|
|
@@ -177,4 +206,36 @@ export async function saveConfig(config, cwd = process.cwd()) {
|
|
|
177
206
|
await writeFile(p, JSON.stringify(stored, null, 2) + "\n", "utf8");
|
|
178
207
|
return p;
|
|
179
208
|
}
|
|
209
|
+
/**
|
|
210
|
+
* The optional `contracts` block, validated to shape only.
|
|
211
|
+
*
|
|
212
|
+
* SHAPE ONLY, exactly as `agentTools` is, and for a sharper version of the same
|
|
213
|
+
* reason: containment is not checked here because a build output legitimately
|
|
214
|
+
* sits outside the repo root's own subtree in some layouts, and the reader that
|
|
215
|
+
* resolves it is where an escaping path must be refused — with the service in
|
|
216
|
+
* hand, so the refusal can name it. A config that throws takes down every
|
|
217
|
+
* command, including the ones with nothing to do with contracts.
|
|
218
|
+
*/
|
|
219
|
+
function readContracts(value, file) {
|
|
220
|
+
if (value === undefined)
|
|
221
|
+
return undefined;
|
|
222
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
223
|
+
throw new ConfigError("contracts", `${file}: "contracts" must be a mapping with an optional "openapi" path.`);
|
|
224
|
+
}
|
|
225
|
+
const record = value;
|
|
226
|
+
// Only `openapi` today. The event axis gets a key when it gets a check —
|
|
227
|
+
// a config key nothing reads is worse than a missing one, because a fleet
|
|
228
|
+
// sets it and believes something is being graded.
|
|
229
|
+
const out = {};
|
|
230
|
+
for (const key of ["openapi"]) {
|
|
231
|
+
const spelled = record[key];
|
|
232
|
+
if (spelled === undefined)
|
|
233
|
+
continue;
|
|
234
|
+
if (typeof spelled !== "string" || spelled === "") {
|
|
235
|
+
throw new ConfigError("contracts", `${file}: "contracts.${key}" must be a non-empty string when present.`);
|
|
236
|
+
}
|
|
237
|
+
out[key] = spelled;
|
|
238
|
+
}
|
|
239
|
+
return out.openapi === undefined ? undefined : out;
|
|
240
|
+
}
|
|
180
241
|
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/core/envelope/config.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFile, writeFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { dirname, join, parse as parsePath, resolve } from \"node:path\";\nimport { resolveInside } from \"../kernel/path-safety.js\";\nimport { docsDirOf, type DocsDir } from \"../kernel/ids/dirs.js\";\nimport { parseServiceId, type ServiceId } from \"../kernel/ids/service.js\";\n\n/** Local config, committed at the root of a service repo (or the docs repo itself). */\nexport const CONFIG_FILENAME = \"loam.json\";\n\nexport interface LoamConfig {\n /**\n * Path to the single shared docs repo (the source of truth). A LOADED config\n * carries it resolved — `parseConfig` resolves the file's spelling against\n * the config file's own directory, which is what earns the brand. The\n * spelling itself is `docsDirAsWritten`, and the shape `saveConfig` persists\n * (`StoredConfig`) keeps it a plain `string`: the stored form is deliberately\n * unresolved (see `storedDocsDir` in commands/init/options.ts), and branding\n * it would state a resolution that never happened.\n */\n docsDir: DocsDir;\n /**\n * Canonical id of the service in the current repo, if this is a service repo.\n *\n * Branded because `parseConfig` below is the one place that checks it, so\n * every command reading `config.service` is reading a name that has already\n * passed the grammar — which is why the config arm of\n * `opts.service ?? config.service` needs no second parse.\n */\n service?: ServiceId;\n /**\n * Where Gherkin lives in this service repo, relative to the repo root\n * (default \"features\" — the cucumber convention). `loam gherkin` writes only\n * inside `<gherkinDir>/loam/`; the rest of the directory stays the team's.\n * Left unresolved on purpose: commands resolve it against the repo they run\n * in, which is where the config file lives.\n */\n gherkinDir?: string;\n /**\n * Where this repo's BUILD writes its contract documents, relative to the\n * repo root — the springdoc/FastAPI/NestJS output, not a second copy anybody\n * maintains.\n *\n * loam reads these and compares a digest; it never writes them, never copies\n * them into the docs repo, and never derives meaning from them. That is what\n * keeps the field on the right side of the no-extractor line: it parses a\n * standard document somebody else generated, at a path a human named, in\n * exactly the way `verify --results` already ingests a cucumber report.\n *\n * The committed `services/<id>/openapi.yaml` stays the contract, and the copy\n * stays a human `cp` reviewed in a pull request. Automating the copy would\n * make the docs-repo document a cache of the build; leaving it manual and\n * merely REPORTING the divergence keeps it a document somebody agreed to.\n */\n contracts?: { openapi?: string };\n /**\n * The agent tools `loam init` has written command and skill files for in this\n * repo, by AGENT_TOOLS id.\n *\n * Recorded because the two ways a generated file can be absent look identical\n * on disk: the binary grew a command nobody has re-run `init` for, or nobody\n * ever selected that tool here. Only this list tells them apart, and `loam\n * doctor` needs the difference to know which absence is worth reporting.\n *\n * ACCUMULATED, never replaced — a later `init --tools cursor` does not\n * un-write the claude files an earlier run left on disk, so the record of\n * what this repo holds has to keep them too.\n *\n * Optional, and unvalidated against the registry on purpose: a config written\n * by an older binary has no list at all, and one written by a NEWER binary\n * may name a tool this one has never heard of. Neither may refuse to load.\n */\n agentTools?: string[];\n /**\n * Digests of generated agent files loam may safely refresh. Keys are\n * repo-relative portable paths; a file whose bytes differ from its recorded\n * digest is human-owned and is never overwritten.\n */\n agentFiles?: Record<string, string>;\n /** Which workflow subset `loam init` manages; absent means the compatible full set. */\n agentProfile?: \"full\" | \"service\" | \"docs\";\n /**\n * The directory the config was actually FOUND in — the repo root, which is\n * not necessarily the cwd now that discovery walks upward. Everything a\n * command resolves against \"this repo\" (gherkinDir, relative `sources:`)\n * belongs here, not against process.cwd(): running `loam gherkin` from\n * `src/deep/` must write the same files as running it from the root.\n *\n * Derived, never stored: `saveConfig` strips it, so it can never be spelled\n * in loam.json and drift from where the file really is.\n */\n root?: string;\n /**\n * `docsDir` exactly as the file spells it, before resolution. Kept because\n * \"relative\" and \"absolute\" are not recoverable from the resolved path, and\n * the difference is the whole point: an absolute docsDir in a COMMITTED\n * config is a path that only exists on the machine that ran `loam init`.\n *\n * Derived, never stored — same contract as `root`.\n */\n docsDirAsWritten?: string;\n}\n\n/**\n * A loam.json that exists but cannot be believed. Carries the field at fault\n * (null when the whole document is), because \"which field\" is the difference\n * between a fix and a guess — and because both `loadConfig` and `loam doctor`\n * report the same sentence from it.\n */\nexport class ConfigError extends Error {\n constructor(\n readonly field: string | null,\n message: string,\n ) {\n super(message);\n this.name = \"ConfigError\";\n }\n}\n\n/**\n * The directories a config search may look in: `start`, then each ancestor, up\n * to AND INCLUDING the git root — or the filesystem root when there is no git\n * repo above.\n *\n * The git root is the stop because it is the boundary the user already thinks\n * in: a loam.json outside it belongs to some other checkout (or, worse, to a\n * home directory), and silently adopting it would point a service repo's\n * commands at a docs repo nobody in this repo ever chose.\n */\nfunction searchDirs(start: string): string[] {\n const out: string[] = [];\n let current = resolve(start);\n const root = parsePath(current).root;\n for (;;) {\n out.push(current);\n // `.git` is a directory in a normal clone and a FILE in a worktree or\n // submodule — existsSync answers both without caring which.\n if (existsSync(join(current, \".git\"))) return out;\n const parent = dirname(current);\n if (parent === current || current === root) return out;\n current = parent;\n }\n}\n\n/** Where a config written HERE goes — never a discovered one. `init` and `saveConfig` use this. */\nexport function localConfigPath(cwd: string = process.cwd()): string {\n return resolve(cwd, CONFIG_FILENAME);\n}\n\n/**\n * The nearest loam.json at or above `cwd`, or null. Exported because `init` has\n * to be able to ask a different question from \"is there one here\": a config in\n * an ancestor already governs this directory, and a second one written below it\n * would quietly shadow the first for every command run from the subtree.\n */\nexport function findConfigPath(cwd: string = process.cwd()): string | null {\n for (const dir of searchDirs(cwd)) {\n const candidate = join(dir, CONFIG_FILENAME);\n if (existsSync(candidate)) return candidate;\n }\n return null;\n}\n\n/**\n * The config that governs `cwd`: the discovered one, or — when there is none —\n * where it would have to be written. The fallback keeps this total, which is\n * what lets a caller distinguish \"absent\" from \"present but unloadable\" with a\n * single existsSync (see `reportNoConfig`).\n */\nexport function configPath(cwd: string = process.cwd()): string {\n return findConfigPath(cwd) ?? localConfigPath(cwd);\n}\n\n/**\n * THE config validator. One implementation, because two of them are two\n * opinions: `loam doctor` used to carry its own copy, and a config doctor\n * called healthy could still be one every other command refused to load.\n *\n * `configDir` is the directory the file lives in — every relative path in the\n * document is resolved against it, so the doc comments above stay true no\n * matter where the caller happens to be standing.\n */\nexport function parseConfig(raw: string, configDir: string): LoamConfig {\n const file = join(configDir, CONFIG_FILENAME);\n let value: unknown;\n try {\n value = JSON.parse(raw);\n } catch (err) {\n throw new ConfigError(null, `${file} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);\n }\n // Checked before any field access: `JSON.parse(\"null\")` and `JSON.parse(\"5\")`\n // are valid JSON, and reaching for `.docsDir` on them is how a config typo\n // used to surface as a raw Node TypeError instead of a named field.\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) {\n throw new ConfigError(null, `${file} must contain a JSON object.`);\n }\n const record = value as Record<string, unknown>;\n\n if (typeof record.docsDir !== \"string\" || record.docsDir === \"\") {\n throw new ConfigError(\"docsDir\", `${file}: \"docsDir\" must be a non-empty string.`);\n }\n // Same discipline as docsDir: a malformed fact refuses the whole config\n // rather than being silently defaulted over — `5` or `\"\"` here is a typo,\n // and defaulting would send generated files somewhere nobody chose.\n // `parseServiceId` rather than a bare check: the same call both refuses the\n // malformed value and produces the branded one below, so there is no window\n // in which a checked name is still typed as an unchecked string. It takes a\n // `string`, hence the shape guard first — the id grammar's own message is\n // about spelling, and `5` is not a spelling.\n let service: ServiceId | undefined;\n if (record.service !== undefined) {\n if (typeof record.service !== \"string\") {\n throw new ConfigError(\"service\", `${file}: \"service\" must be a string.`);\n }\n const parsed = parseServiceId(record.service, '\"service\"');\n if (!parsed.ok) throw new ConfigError(\"service\", `${file}: ${parsed.problem}`);\n service = parsed.id;\n }\n if (record.gherkinDir !== undefined\n && (typeof record.gherkinDir !== \"string\" || record.gherkinDir === \"\")) {\n throw new ConfigError(\"gherkinDir\", `${file}: \"gherkinDir\" must be a non-empty string when present.`);\n }\n const contracts = readContracts(record.contracts, file);\n // Shape only. Which ids are legal is the registry's question, and asking it\n // here would make a config written by a newer binary — one that knows a tool\n // this one does not — unloadable for every command, not just for `init`.\n if (record.agentTools !== undefined\n && (!Array.isArray(record.agentTools)\n || record.agentTools.some((t) => typeof t !== \"string\" || t === \"\"))) {\n throw new ConfigError(\n \"agentTools\",\n `${file}: \"agentTools\" must be an array of non-empty strings when present.`,\n );\n }\n if (record.agentFiles !== undefined) {\n if (record.agentFiles === null || typeof record.agentFiles !== \"object\" || Array.isArray(record.agentFiles)) {\n throw new ConfigError(\"agentFiles\", `${file}: \"agentFiles\" must be a path-to-digest mapping when present.`);\n }\n const entries = Object.entries(record.agentFiles as Record<string, unknown>);\n if (entries.some(([path, digest]) => path === \"\" || typeof digest !== \"string\" || !/^[a-f0-9]{64}$/.test(digest))) {\n throw new ConfigError(\n \"agentFiles\",\n `${file}: \"agentFiles\" must map non-empty paths to lowercase sha256 digests.`,\n );\n }\n }\n if (record.agentProfile !== undefined\n && record.agentProfile !== \"full\"\n && record.agentProfile !== \"service\"\n && record.agentProfile !== \"docs\") {\n throw new ConfigError(\n \"agentProfile\",\n `${file}: \"agentProfile\" must be one of: full, service, docs.`,\n );\n }\n if (typeof record.gherkinDir === \"string\") {\n // Validate the owned output directory, not merely its spelling. This also\n // catches an otherwise-contained path whose existing parent is a symlink\n // out of the service repo.\n try {\n resolveInside(configDir, join(record.gherkinDir, \"loam\"), `\"gherkinDir\"`);\n } catch (err) {\n throw new ConfigError(\"gherkinDir\", `${file}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n return {\n // Resolved here, against the file's own directory, so the doc comment on\n // `docsDir` is true no matter where a caller later resolves the path from —\n // which is exactly the provenance `docsDirOf` demands.\n docsDir: docsDirOf(resolve(configDir, record.docsDir)),\n ...(service === undefined ? {} : { service }),\n ...(record.gherkinDir === undefined ? {} : { gherkinDir: record.gherkinDir as string }),\n ...(contracts === undefined ? {} : { contracts }),\n ...(record.agentTools === undefined ? {} : { agentTools: record.agentTools as string[] }),\n ...(record.agentFiles === undefined\n ? {}\n : { agentFiles: record.agentFiles as Record<string, string> }),\n ...(record.agentProfile === undefined\n ? {}\n : { agentProfile: record.agentProfile as \"full\" | \"service\" | \"docs\" }),\n root: resolve(configDir),\n docsDirAsWritten: record.docsDir,\n };\n}\n\n/**\n * Every way asking for the config can come out, as data. It used to be\n * `LoamConfig | null` with a `console.error` on the invalid arm — core's one\n * print outside the envelope adapter, and a null that collapsed \"no config\"\n * and \"a config nobody can read\" into one answer `reportNoConfig` then had to\n * re-derive from the filesystem (a TOCTOU: the file could change between the\n * load and the re-check). The union says which case it is, carries the parse\n * problem for the command layer to render, and prints nothing.\n */\nexport type ConfigLoad =\n | { kind: \"loaded\"; config: LoamConfig }\n | { kind: \"absent\" }\n | { kind: \"invalid\"; path: string; problem: string };\n\nexport async function loadConfig(cwd: string = process.cwd()): Promise<ConfigLoad> {\n const p = findConfigPath(cwd);\n if (p === null) return { kind: \"absent\" };\n try {\n // The read belongs INSIDE the try: `existsSync` only says the name is\n // taken, not that it names a file this process can read. A loam.json that\n // is a directory (EISDIR), one whose permissions refuse us (EACCES), or one\n // deleted between the search and the read (ENOENT) all fail here rather\n // than in `parseConfig` — and read outside, each of them escaped every\n // command as an `internal` crash carrying a bare errno. Caught here they\n // join the same \"exists but cannot be believed\" arm the parse failures take.\n return { kind: \"loaded\", config: parseConfig(await readFile(p, \"utf8\"), dirname(p)) };\n } catch (err) {\n return { kind: \"invalid\", path: p, problem: err instanceof Error ? err.message : String(err) };\n }\n}\n\n/**\n * What `saveConfig` persists: `LoamConfig` with `docsDir` back to a plain\n * `string`, because the stored form is the SPELLING (deliberately relative —\n * `storedDocsDir`), not the resolved `DocsDir` a loaded config carries. The\n * two derived fields ride along as optional so a caller may spread a loaded\n * config forward — `saveConfig` strips them on the way out either way.\n */\nexport type StoredConfig = Omit<LoamConfig, \"docsDir\"> & { docsDir: string };\n\n/**\n * Write the config for the repo rooted at `cwd`. The derived fields are dropped\n * on the way out: they are facts about where the file was found and how it was\n * spelled, and a stored copy could only ever disagree with the file itself.\n */\nexport async function saveConfig(config: StoredConfig, cwd: string = process.cwd()): Promise<string> {\n const p = localConfigPath(cwd);\n const { root: _root, docsDirAsWritten: _asWritten, ...stored } = config;\n await writeFile(p, JSON.stringify(stored, null, 2) + \"\\n\", \"utf8\");\n return p;\n}\n\n/**\n * The optional `contracts` block, validated to shape only.\n *\n * SHAPE ONLY, exactly as `agentTools` is, and for a sharper version of the same\n * reason: containment is not checked here because a build output legitimately\n * sits outside the repo root's own subtree in some layouts, and the reader that\n * resolves it is where an escaping path must be refused — with the service in\n * hand, so the refusal can name it. A config that throws takes down every\n * command, including the ones with nothing to do with contracts.\n */\nfunction readContracts(value: unknown, file: string): { openapi?: string } | undefined {\n if (value === undefined) return undefined;\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new ConfigError(\"contracts\", `${file}: \"contracts\" must be a mapping with an optional \"openapi\" path.`);\n }\n const record = value as Record<string, unknown>;\n // Only `openapi` today. The event axis gets a key when it gets a check —\n // a config key nothing reads is worse than a missing one, because a fleet\n // sets it and believes something is being graded.\n const out: { openapi?: string } = {};\n for (const key of [\"openapi\"] as const) {\n const spelled = record[key];\n if (spelled === undefined) continue;\n if (typeof spelled !== \"string\" || spelled === \"\") {\n throw new ConfigError(\"contracts\", `${file}: \"contracts.${key}\" must be a non-empty string when present.`);\n }\n out[key] = spelled;\n }\n return out.openapi === undefined ? undefined : out;\n}\n"]}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `--json` envelope — loam's machine contract.
|
|
3
|
+
*
|
|
4
|
+
* Every JSON response carries `ok`. Success spreads the payload alongside it;
|
|
5
|
+
* failure carries a stable `error.code` a caller can branch on without parsing
|
|
6
|
+
* prose. Errors go to stdout too: a consumer reads one stream and always gets
|
|
7
|
+
* valid JSON, whether the command succeeded or not. The exit code still tells
|
|
8
|
+
* the shell what happened.
|
|
9
|
+
*
|
|
10
|
+
* Three keys identify the envelope before its payload is read, and they answer
|
|
11
|
+
* three different questions: `contractVersion` is the SHAPE's version,
|
|
12
|
+
* `version` is the BINARY's, and `command` (on every success payload) says
|
|
13
|
+
* which verb produced it. The first two are stamped by the emitters below so
|
|
14
|
+
* no command can forget them; the third is the payload's own first key,
|
|
15
|
+
* because only the command knows its name.
|
|
16
|
+
*
|
|
17
|
+
* Key casing: camelCase, with one deliberate exception — a key that mirrors a
|
|
18
|
+
* frontmatter field verbatim keeps that field's snake_case spelling
|
|
19
|
+
* (`last_verified`, `sources_digest` in vouch's payload), so the envelope and
|
|
20
|
+
* the document it describes spell the same fact the same way. snake_case only
|
|
21
|
+
* where a key mirrors a frontmatter field verbatim; camelCase everywhere else.
|
|
22
|
+
*/
|
|
23
|
+
import { relative } from "node:path";
|
|
24
|
+
import { LOAM_VERSION } from "./version.js";
|
|
25
|
+
/**
|
|
26
|
+
* Version of the top-level JSON envelope, independent of the CLI/package
|
|
27
|
+
* version. Additive payload fields do not require a bump; incompatible changes
|
|
28
|
+
* to `ok`, `error`, or their semantics do.
|
|
29
|
+
*/
|
|
30
|
+
export const JSON_CONTRACT_VERSION = "1.0";
|
|
31
|
+
/**
|
|
32
|
+
* `version` is stamped HERE rather than by each command for the reason the
|
|
33
|
+
* `command` discriminator was not, and had to be retrofitted one call site at a
|
|
34
|
+
* time: a key every envelope owes is a key no command can be trusted to
|
|
35
|
+
* remember. Two lines in the two emitters give it to every command and every
|
|
36
|
+
* refusal at once, and a command added tomorrow cannot ship without it.
|
|
37
|
+
*
|
|
38
|
+
* Why a consumer needs it at all: `docs.binary-behind` exists to say that a
|
|
39
|
+
* green run from an old binary is worth less than it looks, and a caller
|
|
40
|
+
* holding only a `--json` payload could not tell which loam produced it — so it
|
|
41
|
+
* could not apply that caution, and could not see a mixed-version fleet in the
|
|
42
|
+
* contract. It sits beside `contractVersion`, never inside it: the envelope
|
|
43
|
+
* SHAPE and the binary that filled it version independently, and collapsing
|
|
44
|
+
* them would make every release look like a contract break.
|
|
45
|
+
*/
|
|
46
|
+
export function emitJson(payload) {
|
|
47
|
+
console.log(JSON.stringify({ contractVersion: JSON_CONTRACT_VERSION, version: LOAM_VERSION, ok: true, ...payload }, null, 2));
|
|
48
|
+
}
|
|
49
|
+
/** Paths in the contract are repo-relative, with forward slashes: diffable across machines. */
|
|
50
|
+
export function repoPath(docsDir, abs) {
|
|
51
|
+
return relative(docsDir, abs).split(/[\\/]/).join("/");
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Emit a failure envelope and set the exit code. Returns false, to `return`
|
|
55
|
+
* from a caller.
|
|
56
|
+
*
|
|
57
|
+
* `version` is on this path too, and deliberately: a refusal is the envelope a
|
|
58
|
+
* consumer is most likely to be holding at the moment it needs to know which
|
|
59
|
+
* binary answered — "this loam does not have that flag / that code" is a
|
|
60
|
+
* question about the build, and an unversioned refusal makes it unanswerable.
|
|
61
|
+
*
|
|
62
|
+
* There is no `command` here, and adding one is not a small change. The error
|
|
63
|
+
* emitter has no command in scope: `fail()` is called from deep inside core-ish
|
|
64
|
+
* helpers that never learn which verb the user typed, so threading it means
|
|
65
|
+
* either module-level mutable state — a hazard this codebase names outright,
|
|
66
|
+
* because a value cached at import time leaks across the forked test processes
|
|
67
|
+
* and across invocations in a long-running host — or one more argument threaded
|
|
68
|
+
* through `fail()`, `reportNoConfig()` and every deep helper that refuses, each
|
|
69
|
+
* of which would then carry a value it has no other use for. A consumer that
|
|
70
|
+
* needs to know which command refused already knows: it made the call.
|
|
71
|
+
*/
|
|
72
|
+
export function emitJsonError(code, message, details = {}) {
|
|
73
|
+
console.log(JSON.stringify({ contractVersion: JSON_CONTRACT_VERSION, version: LOAM_VERSION, ok: false, error: { code, message }, ...details }, null, 2));
|
|
74
|
+
process.exitCode = 1;
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The refusals that print no `loam explain` pointer, each with the reason it is
|
|
79
|
+
* here. ONE named set rather than a condition per call site: "is this refusal
|
|
80
|
+
* worth looking up?" is a single question, and answering it in seven places is
|
|
81
|
+
* how two of them come to disagree.
|
|
82
|
+
*
|
|
83
|
+
* - `vouch-declined` — loam asked whether to stamp, and a person said no.
|
|
84
|
+
* Nothing failed and there is nothing to fix, so a pointer would invite
|
|
85
|
+
* somebody to go read the meaning of their own answer.
|
|
86
|
+
*
|
|
87
|
+
* `internal` was weighed and deliberately LEFT OUT. It is the one code with no
|
|
88
|
+
* stable meaning, which reads like a reason to stay silent — but its
|
|
89
|
+
* explanation carries the only instruction available to a person staring at an
|
|
90
|
+
* unexpected throw (a repeatable `internal` is a loam defect worth reporting),
|
|
91
|
+
* and that is worth more than the silence the old behaviour gave them.
|
|
92
|
+
*
|
|
93
|
+
* Exported so test/explain.test.ts can pin the membership: an entry added here
|
|
94
|
+
* silently removes a pointer nobody would notice was gone.
|
|
95
|
+
*/
|
|
96
|
+
export const NO_EXPLAIN_POINTER = new Set(["vouch-declined"]);
|
|
97
|
+
/**
|
|
98
|
+
* The one-line pointer from a refusal to the command that explains it.
|
|
99
|
+
*
|
|
100
|
+
* Measured over eight wrong invocations a person makes in their first hour —
|
|
101
|
+
* no loam.json, an unknown service, an unknown feature, an unbound `gherkin`,
|
|
102
|
+
* a malformed feature id — every single one printed prose and no code, and
|
|
103
|
+
* only `loam validate` mentioned `loam explain` at all. `explain` is the best
|
|
104
|
+
* usability asset loam has and the message that most needs it could not reach
|
|
105
|
+
* it: to look a refusal up you first had to re-run the whole command with
|
|
106
|
+
* `--json` and read `error.code` out of the envelope, which is precisely the
|
|
107
|
+
* knowledge a first-hour user does not have. The code is printed as well as
|
|
108
|
+
* the invocation because the two are different needs: the bare code is what
|
|
109
|
+
* goes into a bug report, a grep or a CI branch, and the invocation is what
|
|
110
|
+
* answers it here.
|
|
111
|
+
*
|
|
112
|
+
* STDERR, never stdout, and that is the load-bearing half: a refusal's
|
|
113
|
+
* diagnostic must not pollute a piped payload. `loam show svc > out.json`
|
|
114
|
+
* already keeps its message off stdout, and a pointer that broke that rule
|
|
115
|
+
* would corrupt exactly the pipelines text mode was careful never to touch.
|
|
116
|
+
* (Findings do the opposite — their codes go to stdout with the report, in
|
|
117
|
+
* document order, for validate/report.ts's own reason. Same fact, two streams,
|
|
118
|
+
* because a finding is the answer and a refusal is the absence of one.)
|
|
119
|
+
*
|
|
120
|
+
* Nothing runs here under `--json`: the envelope already carries `error.code`
|
|
121
|
+
* as data, and a machine reader has no use for being told a second command
|
|
122
|
+
* exists — the same doctrine `EXPLAIN_FOOTER` follows in
|
|
123
|
+
* commands/policy/format.ts.
|
|
124
|
+
*
|
|
125
|
+
* No ANSI escape, though the line is meant to READ as dim. Nothing in this
|
|
126
|
+
* codebase emits escape sequences, the output is graded by tests that compare
|
|
127
|
+
* strings, and a colour that survives into a redirected file or a CI log is
|
|
128
|
+
* noise nobody asked for. The typography does the dimming: the code, a spaced
|
|
129
|
+
* middot, the command — an aside, not a sentence.
|
|
130
|
+
*/
|
|
131
|
+
export function sayExplain(code) {
|
|
132
|
+
if (NO_EXPLAIN_POINTER.has(code))
|
|
133
|
+
return;
|
|
134
|
+
console.error(`${code} · loam explain ${code}`);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Report a failure in whichever mode the caller is in. Text mode goes to
|
|
138
|
+
* stderr as it always has — now with the code and its lookup on a second line;
|
|
139
|
+
* JSON mode goes into the envelope, whose stdout bytes are unchanged.
|
|
140
|
+
*/
|
|
141
|
+
export function fail(json, code, message) {
|
|
142
|
+
if (json) {
|
|
143
|
+
emitJsonError(code, message);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
console.error(message);
|
|
147
|
+
sayExplain(code);
|
|
148
|
+
process.exitCode = 1;
|
|
149
|
+
}
|
|
150
|
+
/** The message every command prints when there is no loam.json. */
|
|
151
|
+
const NO_CONFIG_MESSAGE = "No loam.json found. Run `loam init --docs <dir>` first.";
|
|
152
|
+
/**
|
|
153
|
+
* The message the service-scoped commands print when neither `--service` nor
|
|
154
|
+
* loam.json names one. Four commands ask the same question and owe the same two
|
|
155
|
+
* answers, and a fifth would have been written by hand. `loam delta` keeps its
|
|
156
|
+
* own longer sentence on purpose — it can say which feature was being read.
|
|
157
|
+
*/
|
|
158
|
+
export const NO_SERVICE_MESSAGE = "No service. Pass --service <id> or set it in loam.json.";
|
|
159
|
+
/**
|
|
160
|
+
* Report "no config" in whichever mode the caller is in — distinguishing a
|
|
161
|
+
* config that is absent from one that exists but would not load, because the
|
|
162
|
+
* two point at opposite fixes: a missing config wants `loam init`, a corrupt
|
|
163
|
+
* one wants repair, and `init` would silently rewrite the corrupt file. The
|
|
164
|
+
* distinction arrives as DATA on the load outcome rather than being re-derived
|
|
165
|
+
* from the filesystem here — the file could change between the load and a
|
|
166
|
+
* re-check, and the parse problem itself is worth showing.
|
|
167
|
+
*/
|
|
168
|
+
export function reportNoConfig(json, load) {
|
|
169
|
+
if (load.kind === "invalid") {
|
|
170
|
+
fail(json, "config-invalid", `Invalid loam.json: ${load.problem}. Fix it (or delete it and re-run \`loam init\`).`);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
fail(json, "no-config", NO_CONFIG_MESSAGE);
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=json.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.js","sourceRoot":"","sources":["../../../src/core/envelope/json.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,KAAK,CAAC;AA0H3C;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,QAAQ,CAAC,OAAgC;IACvD,OAAO,CAAC,GAAG,CACT,IAAI,CAAC,SAAS,CAAC,EAAE,eAAe,EAAE,qBAAqB,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CACjH,CAAC;AACJ,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,QAAQ,CAAC,OAAe,EAAE,GAAW;IACnD,OAAO,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzD,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,aAAa,CAC3B,IAAe,EACf,OAAe,EACf,OAAO,GAA4B,EAAE;IAErC,OAAO,CAAC,GAAG,CACT,IAAI,CAAC,SAAS,CACZ,EAAE,eAAe,EAAE,qBAAqB,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,GAAG,OAAO,EAAE,EAClH,IAAI,EACJ,CAAC,CACF,CACF,CAAC;IACF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACrB,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAA2B,IAAI,GAAG,CAAY,CAAC,gBAAgB,CAAC,CAAC,CAAC;AAEjG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,MAAM,UAAU,UAAU,CAAC,IAAe;IACxC,IAAI,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO;IACzC,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,qBAAqB,IAAI,EAAE,CAAC,CAAC;AACpD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,IAAI,CAAC,IAAa,EAAE,IAAe,EAAE,OAAe;IAClE,IAAI,IAAI,EAAE,CAAC;QACT,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC7B,OAAO;IACT,CAAC;IACD,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACvB,UAAU,CAAC,IAAI,CAAC,CAAC;IACjB,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC;AAED,mEAAmE;AACnE,MAAM,iBAAiB,GAAG,yDAAyD,CAAC;AAEpF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,yDAAyD,CAAC;AAE5F;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAC,IAAa,EAAE,IAA6E;IACzH,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC5B,IAAI,CACF,IAAI,EACJ,gBAAgB,EAChB,sBAAsB,IAAI,CAAC,OAAO,mDAAmD,CACtF,CAAC;QACF,OAAO;IACT,CAAC;IACD,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,iBAAiB,CAAC,CAAC;AAC7C,CAAC","sourcesContent":["/**\n * The `--json` envelope — loam's machine contract.\n *\n * Every JSON response carries `ok`. Success spreads the payload alongside it;\n * failure carries a stable `error.code` a caller can branch on without parsing\n * prose. Errors go to stdout too: a consumer reads one stream and always gets\n * valid JSON, whether the command succeeded or not. The exit code still tells\n * the shell what happened.\n *\n * Three keys identify the envelope before its payload is read, and they answer\n * three different questions: `contractVersion` is the SHAPE's version,\n * `version` is the BINARY's, and `command` (on every success payload) says\n * which verb produced it. The first two are stamped by the emitters below so\n * no command can forget them; the third is the payload's own first key,\n * because only the command knows its name.\n *\n * Key casing: camelCase, with one deliberate exception — a key that mirrors a\n * frontmatter field verbatim keeps that field's snake_case spelling\n * (`last_verified`, `sources_digest` in vouch's payload), so the envelope and\n * the document it describes spell the same fact the same way. snake_case only\n * where a key mirrors a frontmatter field verbatim; camelCase everywhere else.\n */\nimport { relative } from \"node:path\";\nimport { LOAM_VERSION } from \"./version.js\";\n\n/**\n * Version of the top-level JSON envelope, independent of the CLI/package\n * version. Additive payload fields do not require a bump; incompatible changes\n * to `ok`, `error`, or their semantics do.\n */\nexport const JSON_CONTRACT_VERSION = \"1.0\";\n\n/**\n * Stable failure codes. Prose may change; these may not.\n *\n * The `sources-*` pair mirrors the `sources.*` finding codes on purpose: the\n * same breach is recognisable whether it arrives as a refusal from `vouch` or\n * as a finding from `validate`.\n *\n * The `feature-active` / `snapshot-*` group is `unarchive` refusing to guess:\n * each names a different reason the undo is not one, and a caller has to tell\n * them apart to know whether re-running could ever work. Its commit failures\n * split the same way archive's do: `restore-failed` means nothing was restored\n * or everything was rolled back — the living docs are unchanged and re-running\n * can work; `rollback-incomplete` (shared with archive, same meaning) means\n * some files could not be put back, and the message lists them.\n *\n * The `answers-*` group is `loam verify --record` refusing an answer set that\n * does not answer the current checklist. They are separated for the same reason:\n * an unreadable file is a bug in whatever wrote it, a mismatch means the feature\n * moved and the claims have to be answered again, and an unevidenced\n * confirmation is the one an agent can fix on the spot.\n *\n * The archive group is `loam archive` refusing or failing, and each code is a\n * different answer to \"what do I fix, and can I trust the repo?\": `not-coherent`\n * is the gate — issues in the FEATURE, carried in `issues[]`. Most yield to\n * `--approve`; the ones that do not name an illegal service id — a\n * `specs/<svc>/` directory name (`delta.service-id-invalid`) or a\n * `metadata { service }` binding (`c4.service-binding-invalid`) — because the\n * name IS the path the merge would write, and the fix is a rename, never a\n * flag. A consumer branches on data, not prose: coherence issues carry the\n * additive `overridable` key resolved per issue, and a refusal `--approve`\n * cannot move says \"--approve does not override this\" in `error.message`;\n * `living-outside-requirements` is the refusal whose fault is not the\n * feature's — the LIVING spec holds requirements outside `## Requirements`,\n * and the merge would duplicate them, so the fix is in the living docs, and\n * `--approve` does not move it either; `archive-exists` is a\n * destination collision under `features/archive/`; `merge-failed` is a merge\n * that could not be computed or was rolled back — either way the living docs\n * are unchanged; `rollback-incomplete` is the one that demands a human: the\n * merge failed AND some files could not be restored.\n *\n * `config-invalid` is distinct from `no-config` because the fixes point in\n * opposite directions: a missing config wants `loam init`, a corrupt one wants\n * repair — and an agent that ran `init` on a corrupt file would silently\n * rewrite it.\n *\n * `internal` is the one code with no stable meaning: an unexpected throw. It\n * exists so a `--json` consumer still receives an envelope instead of a stack\n * trace on stdout's sibling stream.\n */\nexport type ErrorCode =\n | \"no-config\"\n | \"config-invalid\"\n | \"unknown-target\"\n | \"invalid-option\"\n | \"already-exists\"\n | \"sources-absent\"\n | \"sources-path-missing\"\n | \"not-coherent\"\n | \"living-outside-requirements\"\n | \"archive-exists\"\n | \"merge-failed\"\n | \"rollback-incomplete\"\n | \"feature-active\"\n | \"snapshot-missing\"\n | \"snapshot-stale\"\n /** A snapshot pre-image whose bytes no longer match the digest archive recorded for them: `unarchive` will not restore text nobody wrote. */\n | \"snapshot-corrupt\"\n /** A `loam archive`/`unarchive` was killed mid-commit: the living docs are half-written, and this run refuses rather than writing over the evidence. */\n | \"commit-interrupted\"\n | \"restore-failed\"\n | \"answers-unreadable\"\n | \"answers-mismatch\"\n | \"answers-unevidenced\"\n | \"service-mismatch\"\n | \"unknown-service\"\n | \"repository-unavailable\"\n /** `loam verify --record` without `--service` over a federated (schema 2) record: it would erase other repositories' attestations. */\n | \"record-federated\"\n /** A `verification.yaml` that exists but cannot be read as a record — never overwritten, never reported as absent. */\n | \"record-unreadable\"\n /** `loam verify --record` found the record changed between its locked read and the swap — an editor or a lock-ignoring writer landed first. Nothing was written; re-running merges over the record as it now stands. */\n | \"record-raced\"\n /** `loam gherkin <FEAT>` refusing to overwrite a `.feature` file owned by another feature still in flight. */\n | \"gherkin-conflict\"\n /** `loam vouch` found the spec changed under it between reading and stamping — another vouch or an edit landed first, and nothing was written. */\n | \"vouch-raced\"\n /** `loam vouch` with nothing on the other end of stdin and no `--yes`: the stamp is a person's claim, and nobody was asked. */\n | \"vouch-unattended\"\n /** `loam vouch` could not learn who is vouching — git names no `user.email` here, so the stamp would record a claim with nobody behind it. */\n | \"vouch-unattributable\"\n /** `loam vouch` was asked to confirm and the answer was no. Nothing was stamped; this is a successful refusal, not a failure. */\n | \"vouch-declined\"\n /** `docsDir` in loam.json points at nothing: the docs repo was never cloned, or the path is wrong. A read command refuses rather than reporting an empty fleet. */\n | \"docs-missing\"\n /** `docsDir` is a directory but has no `services/`: it is some other directory, most often the service repo itself after a typo. */\n | \"services-missing\"\n /** Another loam writer holds the docs repo's advisory lock: nothing was read or written, and re-running once it finishes works. `verify --record` waits out a short holder before giving this answer. */\n | \"docs-busy\"\n /** `loam subsystem rm` refusing a subsystem that still holds members — services or child subsystems, named in the message. A destructive command never picks targets the caller did not name: move them out (or remove the children) first, then re-run. */\n | \"subsystem-not-empty\"\n /** `loam subsystem move`/`rename` refusing because git reports uncommitted or untracked paths under a directory being moved — the ONLY move-specific refusal: the rename would sweep them into a move nobody reviewed. Commit them, `git stash -u` (plain `stash` leaves untracked files behind), or remove them, then re-run. When git cannot answer at all the move proceeds — refusal needs positive evidence. */\n | \"move-uncommitted\"\n /** A subsystem move/rename failed and was rolled back cleanly: every rename undone, the generated views file restored, the docs unchanged — re-running can work. Distinct from `merge-failed` because no merge was computed; a failure that could NOT be fully undone is `rollback-incomplete`, exactly as for archive. */\n | \"move-failed\"\n /** `loam open` found no service checkout bound to this docs repo under any scanned root — the workspace would hold only the docs repo. Re-running can succeed once a bound checkout exists beside it, or with `--root` pointing where the checkouts live. */\n | \"no-members\"\n /** Two discovered repositories' committed loam.json files declare the same `service` for this docs repo — two checkouts of one service, or a copied config. loam will not guess which checkout speaks for the service; narrow the scan with `--root` or fix the stray binding, then re-run. */\n | \"binding-duplicate\"\n /** `loam list --owners` could not use the user-named CODEOWNERS file: the path cannot be read, or a line in it cannot be parsed as `pattern owner…` (the message names the line). Fail-closed like `answers-unreadable` for the other user-named file — a half-read ownership file must never file a service under the wrong team. Re-running succeeds once the path or the line is fixed. */\n | \"owners-unreadable\"\n /** `loam seed`'s fleet file is missing, unreadable, not YAML, the wrong shape, carries an illegal id or name, or no longer names every existing `services/<id>/` (that arm carries the additive `missingServices` payload key). The message names the file and, where one exists, the line; editing the file and re-running succeeds. */\n | \"seed-file-invalid\"\n /** fleet.yaml declares one name twice — as two services, or as both a service and an external/subsystem. Service ids, subsystem names and externals share one flat namespace (a call endpoint must name exactly one thing); the message names both declaration lines. Rename one and re-run. */\n | \"seed-duplicate-service\"\n /** A service's `subsystem:` in fleet.yaml names nothing `subsystems:` declares; the message carries a did-you-mean hint over the names the file really declares. Fix the spelling (or add the subsystem) and re-run. */\n | \"seed-unknown-subsystem\"\n /** architecture/landscape.likec4 carries hand edits (the line-1 stamp's digest no longer matches) or was authored some other way (no stamp, and not the scaffold's untouched stub). Seed never overwrites human work and nothing was written; re-running cannot succeed until the file is deleted or the edits are folded into fleet.yaml. */\n | \"seed-landscape-edited\"\n | \"internal\";\n\n/**\n * `version` is stamped HERE rather than by each command for the reason the\n * `command` discriminator was not, and had to be retrofitted one call site at a\n * time: a key every envelope owes is a key no command can be trusted to\n * remember. Two lines in the two emitters give it to every command and every\n * refusal at once, and a command added tomorrow cannot ship without it.\n *\n * Why a consumer needs it at all: `docs.binary-behind` exists to say that a\n * green run from an old binary is worth less than it looks, and a caller\n * holding only a `--json` payload could not tell which loam produced it — so it\n * could not apply that caution, and could not see a mixed-version fleet in the\n * contract. It sits beside `contractVersion`, never inside it: the envelope\n * SHAPE and the binary that filled it version independently, and collapsing\n * them would make every release look like a contract break.\n */\nexport function emitJson(payload: Record<string, unknown>): void {\n console.log(\n JSON.stringify({ contractVersion: JSON_CONTRACT_VERSION, version: LOAM_VERSION, ok: true, ...payload }, null, 2),\n );\n}\n\n/** Paths in the contract are repo-relative, with forward slashes: diffable across machines. */\nexport function repoPath(docsDir: string, abs: string): string {\n return relative(docsDir, abs).split(/[\\\\/]/).join(\"/\");\n}\n\n/**\n * Emit a failure envelope and set the exit code. Returns false, to `return`\n * from a caller.\n *\n * `version` is on this path too, and deliberately: a refusal is the envelope a\n * consumer is most likely to be holding at the moment it needs to know which\n * binary answered — \"this loam does not have that flag / that code\" is a\n * question about the build, and an unversioned refusal makes it unanswerable.\n *\n * There is no `command` here, and adding one is not a small change. The error\n * emitter has no command in scope: `fail()` is called from deep inside core-ish\n * helpers that never learn which verb the user typed, so threading it means\n * either module-level mutable state — a hazard this codebase names outright,\n * because a value cached at import time leaks across the forked test processes\n * and across invocations in a long-running host — or one more argument threaded\n * through `fail()`, `reportNoConfig()` and every deep helper that refuses, each\n * of which would then carry a value it has no other use for. A consumer that\n * needs to know which command refused already knows: it made the call.\n */\nexport function emitJsonError(\n code: ErrorCode,\n message: string,\n details: Record<string, unknown> = {},\n): false {\n console.log(\n JSON.stringify(\n { contractVersion: JSON_CONTRACT_VERSION, version: LOAM_VERSION, ok: false, error: { code, message }, ...details },\n null,\n 2,\n ),\n );\n process.exitCode = 1;\n return false;\n}\n\n/**\n * The refusals that print no `loam explain` pointer, each with the reason it is\n * here. ONE named set rather than a condition per call site: \"is this refusal\n * worth looking up?\" is a single question, and answering it in seven places is\n * how two of them come to disagree.\n *\n * - `vouch-declined` — loam asked whether to stamp, and a person said no.\n * Nothing failed and there is nothing to fix, so a pointer would invite\n * somebody to go read the meaning of their own answer.\n *\n * `internal` was weighed and deliberately LEFT OUT. It is the one code with no\n * stable meaning, which reads like a reason to stay silent — but its\n * explanation carries the only instruction available to a person staring at an\n * unexpected throw (a repeatable `internal` is a loam defect worth reporting),\n * and that is worth more than the silence the old behaviour gave them.\n *\n * Exported so test/explain.test.ts can pin the membership: an entry added here\n * silently removes a pointer nobody would notice was gone.\n */\nexport const NO_EXPLAIN_POINTER: ReadonlySet<ErrorCode> = new Set<ErrorCode>([\"vouch-declined\"]);\n\n/**\n * The one-line pointer from a refusal to the command that explains it.\n *\n * Measured over eight wrong invocations a person makes in their first hour —\n * no loam.json, an unknown service, an unknown feature, an unbound `gherkin`,\n * a malformed feature id — every single one printed prose and no code, and\n * only `loam validate` mentioned `loam explain` at all. `explain` is the best\n * usability asset loam has and the message that most needs it could not reach\n * it: to look a refusal up you first had to re-run the whole command with\n * `--json` and read `error.code` out of the envelope, which is precisely the\n * knowledge a first-hour user does not have. The code is printed as well as\n * the invocation because the two are different needs: the bare code is what\n * goes into a bug report, a grep or a CI branch, and the invocation is what\n * answers it here.\n *\n * STDERR, never stdout, and that is the load-bearing half: a refusal's\n * diagnostic must not pollute a piped payload. `loam show svc > out.json`\n * already keeps its message off stdout, and a pointer that broke that rule\n * would corrupt exactly the pipelines text mode was careful never to touch.\n * (Findings do the opposite — their codes go to stdout with the report, in\n * document order, for validate/report.ts's own reason. Same fact, two streams,\n * because a finding is the answer and a refusal is the absence of one.)\n *\n * Nothing runs here under `--json`: the envelope already carries `error.code`\n * as data, and a machine reader has no use for being told a second command\n * exists — the same doctrine `EXPLAIN_FOOTER` follows in\n * commands/policy/format.ts.\n *\n * No ANSI escape, though the line is meant to READ as dim. Nothing in this\n * codebase emits escape sequences, the output is graded by tests that compare\n * strings, and a colour that survives into a redirected file or a CI log is\n * noise nobody asked for. The typography does the dimming: the code, a spaced\n * middot, the command — an aside, not a sentence.\n */\nexport function sayExplain(code: ErrorCode): void {\n if (NO_EXPLAIN_POINTER.has(code)) return;\n console.error(`${code} · loam explain ${code}`);\n}\n\n/**\n * Report a failure in whichever mode the caller is in. Text mode goes to\n * stderr as it always has — now with the code and its lookup on a second line;\n * JSON mode goes into the envelope, whose stdout bytes are unchanged.\n */\nexport function fail(json: boolean, code: ErrorCode, message: string): void {\n if (json) {\n emitJsonError(code, message);\n return;\n }\n console.error(message);\n sayExplain(code);\n process.exitCode = 1;\n}\n\n/** The message every command prints when there is no loam.json. */\nconst NO_CONFIG_MESSAGE = \"No loam.json found. Run `loam init --docs <dir>` first.\";\n\n/**\n * The message the service-scoped commands print when neither `--service` nor\n * loam.json names one. Four commands ask the same question and owe the same two\n * answers, and a fifth would have been written by hand. `loam delta` keeps its\n * own longer sentence on purpose — it can say which feature was being read.\n */\nexport const NO_SERVICE_MESSAGE = \"No service. Pass --service <id> or set it in loam.json.\";\n\n/**\n * Report \"no config\" in whichever mode the caller is in — distinguishing a\n * config that is absent from one that exists but would not load, because the\n * two point at opposite fixes: a missing config wants `loam init`, a corrupt\n * one wants repair, and `init` would silently rewrite the corrupt file. The\n * distinction arrives as DATA on the load outcome rather than being re-derived\n * from the filesystem here — the file could change between the load and a\n * re-check, and the parse problem itself is worth showing.\n */\nexport function reportNoConfig(json: boolean, load: { kind: \"absent\" } | { kind: \"invalid\"; path: string; problem: string }): void {\n if (load.kind === \"invalid\") {\n fail(\n json,\n \"config-invalid\",\n `Invalid loam.json: ${load.problem}. Fix it (or delete it and re-run \\`loam init\\`).`,\n );\n return;\n }\n fail(json, \"no-config\", NO_CONFIG_MESSAGE);\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* One version string, owned by package.json — read once, imported everywhere a
|
|
3
|
-
* version is spoken.
|
|
4
|
-
* from dist/core/ in the published layout alike.
|
|
3
|
+
* version is spoken. `../../../package.json` resolves from src/core/envelope/ in
|
|
4
|
+
* dev and from dist/core/envelope/ in the published layout alike.
|
|
5
5
|
*/
|
|
6
6
|
import { createRequire } from "node:module";
|
|
7
7
|
/**
|
|
@@ -20,5 +20,5 @@ function readVersion(pkg) {
|
|
|
20
20
|
}
|
|
21
21
|
return version;
|
|
22
22
|
}
|
|
23
|
-
export const LOAM_VERSION = readVersion(createRequire(import.meta.url)("
|
|
23
|
+
export const LOAM_VERSION = readVersion(createRequire(import.meta.url)("../../../package.json"));
|
|
24
24
|
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../../src/core/envelope/version.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C;;;;;;;;GAQG;AACH,SAAS,WAAW,CAAC,GAAY;IAC/B,MAAM,OAAO,GAAG,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,SAAS,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IACtG,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CACb,gGAAgG,CACjG,CAAC;IACJ,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,CAAC,MAAM,YAAY,GAAG,WAAW,CAAC,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC","sourcesContent":["/**\n * One version string, owned by package.json — read once, imported everywhere a\n * version is spoken. `../../../package.json` resolves from src/core/envelope/ in\n * dev and from dist/core/envelope/ in the published layout alike.\n */\nimport { createRequire } from \"node:module\";\n\n/**\n * The read is checked rather than asserted because the failure it would let\n * through is silent, permanent, and invisible on the machine that caused it: an\n * absent or empty `version` stamps `vundefined` into every file loam generates,\n * `agentsStampVersion` then cannot parse that stamp back, and every repository\n * built by that build reports its agent files stale forever with nothing to\n * point at. A build that cannot say what it is has nothing useful to do, so it\n * says so once, loudly, while its own module is being evaluated.\n */\nfunction readVersion(pkg: unknown): string {\n const version = pkg !== null && typeof pkg === \"object\" && \"version\" in pkg ? pkg.version : undefined;\n if (typeof version !== \"string\" || version === \"\") {\n throw new Error(\n \"readVersion: package.json declares no non-empty 'version', so this build cannot say what it is\",\n );\n }\n return version;\n}\n\nexport const LOAM_VERSION = readVersion(createRequire(import.meta.url)(\"../../../package.json\"));\n"]}
|