@ybotok/loam 0.2.0-alpha.7 → 0.2.0-alpha.9
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 +1315 -1
- package/COMPARISON.md +91 -3
- package/CONTRIBUTING.md +37 -0
- package/MIGRATING-from-OpenSpec.md +40 -22
- package/QUICKSTART.md +181 -9
- package/README.md +256 -57
- package/ROADMAP.md +83 -7
- package/SCHEMA.md +569 -98
- package/WORKFLOW.md +272 -62
- package/dist/cli.js +26 -8
- package/dist/cli.js.map +1 -1
- package/dist/commands/adopt/adopt.js +5 -10
- package/dist/commands/adopt/adopt.js.map +1 -1
- package/dist/commands/archive/archive.js +15 -18
- package/dist/commands/archive/archive.js.map +1 -1
- package/dist/commands/archive/plan/contracts/asyncapi.js +27 -41
- package/dist/commands/archive/plan/contracts/asyncapi.js.map +1 -1
- package/dist/commands/archive/plan/contracts/consumers.js +56 -0
- package/dist/commands/archive/plan/contracts/consumers.js.map +1 -0
- package/dist/commands/archive/plan/contracts/openapi.js +25 -42
- package/dist/commands/archive/plan/contracts/openapi.js.map +1 -1
- package/dist/commands/archive/plan/contracts/verdict-lines.js +41 -0
- package/dist/commands/archive/plan/contracts/verdict-lines.js.map +1 -0
- package/dist/commands/archive/plan/gate.js +3 -7
- package/dist/commands/archive/plan/gate.js.map +1 -1
- package/dist/commands/archive/plan/landscape.js +4 -4
- package/dist/commands/archive/plan/landscape.js.map +1 -1
- package/dist/commands/archive/plan/refusal.js +8 -18
- package/dist/commands/archive/plan/refusal.js.map +1 -1
- package/dist/commands/archive/plan/specs.js +134 -25
- package/dist/commands/archive/plan/specs.js.map +1 -1
- package/dist/commands/archive/plan/state.js +1 -0
- package/dist/commands/archive/plan/state.js.map +1 -1
- package/dist/commands/archive/run.js +33 -11
- package/dist/commands/archive/run.js.map +1 -1
- package/dist/commands/archive/shipped.js +49 -0
- package/dist/commands/archive/shipped.js.map +1 -0
- package/dist/commands/context/context.js +4 -7
- package/dist/commands/context/context.js.map +1 -1
- package/dist/commands/context/print.js +2 -2
- package/dist/commands/context/print.js.map +1 -1
- package/dist/commands/delta/delta.js +44 -12
- package/dist/commands/delta/delta.js.map +1 -1
- package/dist/commands/delta/print.js +1 -1
- package/dist/commands/delta/print.js.map +1 -1
- package/dist/commands/dependencies.js +3 -7
- package/dist/commands/dependencies.js.map +1 -1
- package/dist/commands/diff/diff.js +20 -27
- package/dist/commands/diff/diff.js.map +1 -1
- package/dist/commands/explore.js +4 -8
- package/dist/commands/explore.js.map +1 -1
- package/dist/commands/gate/gate.js +45 -8
- package/dist/commands/gate/gate.js.map +1 -1
- package/dist/commands/gate/print.js +15 -3
- package/dist/commands/gate/print.js.map +1 -1
- package/dist/commands/gherkin/commit.js +13 -54
- package/dist/commands/gherkin/commit.js.map +1 -1
- package/dist/commands/gherkin/gherkin.js +36 -20
- package/dist/commands/gherkin/gherkin.js.map +1 -1
- package/dist/commands/gherkin/reconcile.js +10 -13
- package/dist/commands/gherkin/reconcile.js.map +1 -1
- package/dist/commands/init/first-hour.js +44 -0
- package/dist/commands/init/first-hour.js.map +1 -1
- package/dist/commands/init/init.js +18 -22
- package/dist/commands/init/init.js.map +1 -1
- package/dist/commands/list/campaign/campaign.js +4 -6
- package/dist/commands/list/campaign/campaign.js.map +1 -1
- package/dist/commands/list/json.js +0 -4
- package/dist/commands/list/json.js.map +1 -1
- package/dist/commands/list/list.js +38 -23
- package/dist/commands/list/list.js.map +1 -1
- package/dist/commands/list/print.js +38 -6
- package/dist/commands/list/print.js.map +1 -1
- package/dist/commands/list/suites/suites.js +140 -0
- package/dist/commands/list/suites/suites.js.map +1 -0
- package/dist/commands/list/views.js +28 -33
- package/dist/commands/list/views.js.map +1 -1
- package/dist/commands/mcp/dispatch.js +91 -36
- package/dist/commands/mcp/dispatch.js.map +1 -1
- package/dist/commands/mcp/mcp.js +56 -15
- package/dist/commands/mcp/mcp.js.map +1 -1
- package/dist/commands/mcp/serve.js.map +1 -1
- package/dist/commands/migrate-openspec/materialize/active.js +27 -22
- package/dist/commands/migrate-openspec/materialize/active.js.map +1 -1
- package/dist/commands/migrate-openspec/materialize/stage.js +4 -3
- package/dist/commands/migrate-openspec/materialize/stage.js.map +1 -1
- package/dist/commands/migrate-openspec/materialize/target.js +2 -2
- package/dist/commands/migrate-openspec/materialize/target.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/decisions.js +9 -14
- package/dist/commands/migrate-openspec/openspec/decisions.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/error.js +7 -8
- package/dist/commands/migrate-openspec/openspec/error.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/mapping.js +1 -9
- package/dist/commands/migrate-openspec/openspec/mapping.js.map +1 -1
- package/dist/commands/migrate-openspec/openspec/paths.js +3 -6
- package/dist/commands/migrate-openspec/openspec/paths.js.map +1 -1
- package/dist/commands/new/capability-template.js +118 -0
- package/dist/commands/new/capability-template.js.map +1 -0
- package/dist/commands/new/commit.js +8 -22
- package/dist/commands/new/commit.js.map +1 -1
- package/dist/commands/new/modifies.js +206 -0
- package/dist/commands/new/modifies.js.map +1 -0
- package/dist/commands/new/new.js +59 -14
- package/dist/commands/new/new.js.map +1 -1
- package/dist/commands/new/templates.js +48 -124
- package/dist/commands/new/templates.js.map +1 -1
- package/dist/commands/open.js +3 -7
- package/dist/commands/open.js.map +1 -1
- package/dist/commands/policy/gate.js +27 -0
- package/dist/commands/policy/gate.js.map +1 -1
- package/dist/commands/rebase/plan.js +2 -2
- package/dist/commands/rebase/plan.js.map +1 -1
- package/dist/commands/rebase/rebase.js +60 -35
- package/dist/commands/rebase/rebase.js.map +1 -1
- package/dist/commands/seed/commit.js +21 -25
- package/dist/commands/seed/commit.js.map +1 -1
- package/dist/commands/seed/seed.js +6 -9
- package/dist/commands/seed/seed.js.map +1 -1
- package/dist/commands/show/show.js +3 -7
- package/dist/commands/show/show.js.map +1 -1
- package/dist/commands/status/print.js +13 -4
- package/dist/commands/status/print.js.map +1 -1
- package/dist/commands/status/status.js +3 -7
- package/dist/commands/status/status.js.map +1 -1
- package/dist/commands/steps/steps.js +3 -7
- package/dist/commands/steps/steps.js.map +1 -1
- package/dist/commands/subsystem/edit.js +3 -5
- package/dist/commands/subsystem/edit.js.map +1 -1
- package/dist/commands/subsystem/move.js +5 -8
- package/dist/commands/subsystem/move.js.map +1 -1
- package/dist/commands/subsystem/query.js +2 -4
- package/dist/commands/subsystem/query.js.map +1 -1
- package/dist/commands/subsystem/subsystem.js +4 -7
- package/dist/commands/subsystem/subsystem.js.map +1 -1
- package/dist/commands/subsystem/sync.js +14 -41
- package/dist/commands/subsystem/sync.js.map +1 -1
- package/dist/commands/subsystem/txn/txn.js +17 -38
- package/dist/commands/subsystem/txn/txn.js.map +1 -1
- package/dist/commands/subsystem/txn/views.js +32 -5
- package/dist/commands/subsystem/txn/views.js.map +1 -1
- package/dist/commands/tasks/render.js +116 -0
- package/dist/commands/tasks/render.js.map +1 -0
- package/dist/commands/tasks/tasks.js +113 -0
- package/dist/commands/tasks/tasks.js.map +1 -0
- package/dist/commands/unarchive/manifest.js +6 -9
- package/dist/commands/unarchive/manifest.js.map +1 -1
- package/dist/commands/unarchive/restore.js +17 -28
- package/dist/commands/unarchive/restore.js.map +1 -1
- package/dist/commands/unarchive/unarchive.js +22 -30
- package/dist/commands/unarchive/unarchive.js.map +1 -1
- package/dist/commands/validate/arch-coverage.js +6 -15
- package/dist/commands/validate/arch-coverage.js.map +1 -1
- package/dist/commands/validate/checks/fleet-shape.js +4 -49
- package/dist/commands/validate/checks/fleet-shape.js.map +1 -1
- package/dist/commands/validate/checks/requirements.js +18 -8
- package/dist/commands/validate/checks/requirements.js.map +1 -1
- package/dist/commands/validate/checks/sources.js +6 -2
- package/dist/commands/validate/checks/sources.js.map +1 -1
- package/dist/commands/validate/feature.js +23 -19
- package/dist/commands/validate/feature.js.map +1 -1
- package/dist/commands/validate/fleet/landscape.js +23 -23
- package/dist/commands/validate/fleet/landscape.js.map +1 -1
- package/dist/commands/validate/fleet/map/attest.js +6 -10
- package/dist/commands/validate/fleet/map/attest.js.map +1 -1
- package/dist/commands/validate/fleet/map/bindings.js +1 -3
- package/dist/commands/validate/fleet/map/bindings.js.map +1 -1
- package/dist/commands/validate/fleet/map/usecases.js +5 -11
- package/dist/commands/validate/fleet/map/usecases.js.map +1 -1
- package/dist/commands/validate/fleet/obligations.js +1 -5
- package/dist/commands/validate/fleet/obligations.js.map +1 -1
- package/dist/commands/validate/fleet/scorecard/print.js +5 -1
- package/dist/commands/validate/fleet/scorecard/print.js.map +1 -1
- package/dist/commands/validate/fleet/scorecard/scorecard.js +17 -11
- package/dist/commands/validate/fleet/scorecard/scorecard.js.map +1 -1
- package/dist/commands/validate/fleet/usecases/usecases.js +6 -4
- package/dist/commands/validate/fleet/usecases/usecases.js.map +1 -1
- package/dist/commands/validate/fleet/views/fleet-project.js.map +1 -1
- package/dist/commands/validate/fleet/views/root-documents.js +8 -28
- package/dist/commands/validate/fleet/views/root-documents.js.map +1 -1
- package/dist/commands/validate/links/corpus.js +1 -4
- package/dist/commands/validate/links/corpus.js.map +1 -1
- package/dist/commands/validate/links/glossary.js.map +1 -1
- package/dist/commands/validate/report.js +5 -4
- package/dist/commands/validate/report.js.map +1 -1
- package/dist/commands/validate/scope.js +113 -0
- package/dist/commands/validate/scope.js.map +1 -0
- package/dist/commands/validate/service/api.js +16 -104
- package/dist/commands/validate/service/api.js.map +1 -1
- package/dist/commands/validate/service/contracts/generated.js +134 -0
- package/dist/commands/validate/service/contracts/generated.js.map +1 -0
- package/dist/commands/validate/service/events/events.js +1 -1
- package/dist/commands/validate/service/events/events.js.map +1 -1
- package/dist/commands/validate/service/events/message-producers.js +1 -1
- package/dist/commands/validate/service/events/message-producers.js.map +1 -1
- package/dist/commands/validate/service/evidence-pins.js +56 -10
- package/dist/commands/validate/service/evidence-pins.js.map +1 -1
- package/dist/commands/validate/service/service.js +16 -26
- package/dist/commands/validate/service/service.js.map +1 -1
- package/dist/commands/validate/service/specs.js +31 -38
- package/dist/commands/validate/service/specs.js.map +1 -1
- package/dist/commands/validate/service/spine.js +8 -11
- package/dist/commands/validate/service/spine.js.map +1 -1
- package/dist/commands/validate/service/usecases/usecases.js +3 -3
- package/dist/commands/validate/service/usecases/usecases.js.map +1 -1
- package/dist/commands/validate/validate.js +44 -26
- package/dist/commands/validate/validate.js.map +1 -1
- package/dist/commands/verify/cross/diff.js +2 -6
- package/dist/commands/verify/cross/diff.js.map +1 -1
- package/dist/commands/verify/evidence/contract.js +1 -1
- package/dist/commands/verify/evidence/contract.js.map +1 -1
- package/dist/commands/verify/evidence/read.js +12 -3
- package/dist/commands/verify/evidence/read.js.map +1 -1
- package/dist/commands/verify/frozen.js +45 -23
- package/dist/commands/verify/frozen.js.map +1 -1
- package/dist/commands/verify/record.js +69 -17
- package/dist/commands/verify/record.js.map +1 -1
- package/dist/commands/verify/report.js +27 -44
- package/dist/commands/verify/report.js.map +1 -1
- package/dist/commands/verify/results.js +112 -30
- package/dist/commands/verify/results.js.map +1 -1
- package/dist/commands/verify/verify.js +54 -51
- package/dist/commands/verify/verify.js.map +1 -1
- package/dist/commands/vouch/pack/pack.js +2 -2
- package/dist/commands/vouch/pack/pack.js.map +1 -1
- package/dist/commands/vouch/pack/print.js +1 -6
- package/dist/commands/vouch/pack/print.js.map +1 -1
- package/dist/commands/vouch/pack/sections.js +1 -2
- package/dist/commands/vouch/pack/sections.js.map +1 -1
- package/dist/commands/vouch/run.js +26 -81
- package/dist/commands/vouch/run.js.map +1 -1
- package/dist/commands/vouch/sample/plan.js +20 -34
- package/dist/commands/vouch/sample/plan.js.map +1 -1
- package/dist/commands/vouch/vet/verify.js +39 -7
- package/dist/commands/vouch/vet/verify.js.map +1 -1
- package/dist/commands/vouch/vouch.js +10 -17
- package/dist/commands/vouch/vouch.js.map +1 -1
- package/dist/core/agent/agents-md/command-map.js +56 -101
- package/dist/core/agent/agents-md/command-map.js.map +1 -1
- package/dist/core/agent/agents-md/map/explain.js +4 -3
- package/dist/core/agent/agents-md/map/explain.js.map +1 -1
- package/dist/core/agent/agents-md/map/lenses/context.js +4 -3
- package/dist/core/agent/agents-md/map/lenses/context.js.map +1 -1
- package/dist/core/agent/agents-md/map/lenses/diff.js +33 -20
- package/dist/core/agent/agents-md/map/lenses/diff.js.map +1 -1
- package/dist/core/agent/agents-md/map/lenses/gate.js +13 -5
- package/dist/core/agent/agents-md/map/lenses/gate.js.map +1 -1
- package/dist/core/agent/agents-md/map/mcp.js +10 -7
- package/dist/core/agent/agents-md/map/mcp.js.map +1 -1
- package/dist/core/agent/agents-md/map/status.js +9 -3
- package/dist/core/agent/agents-md/map/status.js.map +1 -1
- package/dist/core/agent/agents-md/map/subsystem.js +4 -3
- package/dist/core/agent/agents-md/map/subsystem.js.map +1 -1
- package/dist/core/agent/agents-md/map/usecases.js +24 -6
- package/dist/core/agent/agents-md/map/usecases.js.map +1 -1
- package/dist/core/agent/agents-md/refusals.js +32 -18
- package/dist/core/agent/agents-md/refusals.js.map +1 -1
- package/dist/core/agent/agents-md.js +9 -3
- package/dist/core/agent/agents-md.js.map +1 -1
- package/dist/core/agent/agents-stamp.js +10 -0
- package/dist/core/agent/agents-stamp.js.map +1 -1
- package/dist/core/agent/scaffold.js +10 -15
- package/dist/core/agent/scaffold.js.map +1 -1
- package/dist/core/agent/tools/dialects.js +1 -0
- package/dist/core/agent/tools/dialects.js.map +1 -1
- package/dist/core/agent/tools/registry.js +41 -76
- package/dist/core/agent/tools/registry.js.map +1 -1
- package/dist/core/agent/workflows/adopt.js +5 -3
- package/dist/core/agent/workflows/adopt.js.map +1 -1
- package/dist/core/agent/workflows/check.js +20 -16
- package/dist/core/agent/workflows/check.js.map +1 -1
- package/dist/core/agent/workflows/closing.js +54 -21
- package/dist/core/agent/workflows/closing.js.map +1 -1
- package/dist/core/agent/workflows/feature.js +79 -11
- package/dist/core/agent/workflows/feature.js.map +1 -1
- package/dist/core/agent/workflows/implement.js +91 -13
- package/dist/core/agent/workflows/implement.js.map +1 -1
- package/dist/core/agent/workflows/reference/authoring.js +3 -1
- package/dist/core/agent/workflows/reference/authoring.js.map +1 -1
- package/dist/core/agent/workflows/reference/codes.js +8 -6
- package/dist/core/agent/workflows/reference/codes.js.map +1 -1
- package/dist/core/agent/workflows/reference/done-check.js +37 -15
- package/dist/core/agent/workflows/reference/done-check.js.map +1 -1
- package/dist/core/agent/workflows/reference/reference.js.map +1 -1
- package/dist/core/agent/workflows/reference/spine.js +50 -18
- package/dist/core/agent/workflows/reference/spine.js.map +1 -1
- package/dist/core/asyncapi/baseline/plan.js +14 -81
- package/dist/core/asyncapi/baseline/plan.js.map +1 -1
- package/dist/core/asyncapi/digest.js +30 -25
- package/dist/core/asyncapi/digest.js.map +1 -1
- package/dist/core/asyncapi/merge/components.js +5 -4
- package/dist/core/asyncapi/merge/components.js.map +1 -1
- package/dist/core/asyncapi/merge/error.js +7 -2
- package/dist/core/asyncapi/merge/error.js.map +1 -1
- package/dist/core/asyncapi/merge/markers.js +30 -50
- package/dist/core/asyncapi/merge/markers.js.map +1 -1
- package/dist/core/asyncapi/merge/merge.js +31 -53
- package/dist/core/asyncapi/merge/merge.js.map +1 -1
- package/dist/core/asyncapi/merge/pin.js +10 -36
- package/dist/core/asyncapi/merge/pin.js.map +1 -1
- package/dist/core/asyncapi/model.js.map +1 -1
- package/dist/core/asyncapi/read.js +36 -12
- package/dist/core/asyncapi/read.js.map +1 -1
- package/dist/core/brief/brief.js +6 -18
- package/dist/core/brief/brief.js.map +1 -1
- package/dist/core/brief/landscape.js +8 -12
- package/dist/core/brief/landscape.js.map +1 -1
- package/dist/core/brief/map/extend.js +8 -5
- package/dist/core/brief/map/extend.js.map +1 -1
- package/dist/core/brief/map/owed.js +8 -26
- package/dist/core/brief/map/owed.js.map +1 -1
- package/dist/core/brief/targets.js +15 -5
- package/dist/core/brief/targets.js.map +1 -1
- package/dist/core/c4/likec4.js +147 -16
- package/dist/core/c4/likec4.js.map +1 -1
- package/dist/core/c4/locate/unlocated.js +150 -0
- package/dist/core/c4/locate/unlocated.js.map +1 -0
- package/dist/core/c4/parsed/values.js +54 -1
- package/dist/core/c4/parsed/values.js.map +1 -1
- package/dist/core/c4/prefetch/fleet.js +1 -9
- package/dist/core/c4/prefetch/fleet.js.map +1 -1
- package/dist/core/c4/project/documents.js +31 -6
- package/dist/core/c4/project/documents.js.map +1 -1
- package/dist/core/c4/project/load.js +6 -17
- package/dist/core/c4/project/load.js.map +1 -1
- package/dist/core/c4/resolve/external.js +51 -0
- package/dist/core/c4/resolve/external.js.map +1 -0
- package/dist/core/c4/resolve/service.js +25 -1
- package/dist/core/c4/resolve/service.js.map +1 -1
- package/dist/core/c4/seed/template.js +15 -1
- package/dist/core/c4/seed/template.js.map +1 -1
- package/dist/core/c4/service-model/shape.js +2 -22
- package/dist/core/c4/service-model/shape.js.map +1 -1
- package/dist/core/c4/source-mask.js +288 -4
- package/dist/core/c4/source-mask.js.map +1 -1
- package/dist/core/c4/source-scan.js +203 -147
- package/dist/core/c4/source-scan.js.map +1 -1
- package/dist/core/c4/splice/authored-source.js +19 -0
- package/dist/core/c4/splice/authored-source.js.map +1 -1
- package/dist/core/c4/splice/delta-blocks.js +5 -18
- package/dist/core/c4/splice/delta-blocks.js.map +1 -1
- package/dist/core/c4/splice/landscape-merge.js +13 -8
- package/dist/core/c4/splice/landscape-merge.js.map +1 -1
- package/dist/core/c4/splice/model/merge.js +10 -6
- package/dist/core/c4/splice/model/merge.js.map +1 -1
- package/dist/core/c4/splice/placement.js +108 -18
- package/dist/core/c4/splice/placement.js.map +1 -1
- package/dist/core/c4/workspace.js +13 -22
- package/dist/core/c4/workspace.js.map +1 -1
- package/dist/core/capabilities/capabilities.js +6 -6
- package/dist/core/capabilities/capabilities.js.map +1 -1
- package/dist/core/capabilities/delta/doc.js +7 -10
- package/dist/core/capabilities/delta/doc.js.map +1 -1
- package/dist/core/capabilities/findings.js +15 -20
- package/dist/core/capabilities/findings.js.map +1 -1
- package/dist/core/capabilities/realizes/findings.js +12 -19
- package/dist/core/capabilities/realizes/findings.js.map +1 -1
- package/dist/core/capabilities/realizes/join.js +0 -20
- package/dist/core/capabilities/realizes/join.js.map +1 -1
- package/dist/core/capabilities/rollup.js +6 -0
- package/dist/core/capabilities/rollup.js.map +1 -1
- package/dist/core/capabilities/tree.js +6 -28
- package/dist/core/capabilities/tree.js.map +1 -1
- package/dist/core/capabilities/usecase-join.js +3 -13
- package/dist/core/capabilities/usecase-join.js.map +1 -1
- package/dist/core/coherence/authoring/scaffold.js +11 -3
- package/dist/core/coherence/authoring/scaffold.js.map +1 -1
- package/dist/core/coherence/authoring/sentinels.js +2 -1
- package/dist/core/coherence/authoring/sentinels.js.map +1 -1
- package/dist/core/coherence/coherence.js +30 -16
- package/dist/core/coherence/coherence.js.map +1 -1
- package/dist/core/coherence/consumed/operations.js +70 -0
- package/dist/core/coherence/consumed/operations.js.map +1 -0
- package/dist/core/coherence/consumed/refusal.js +38 -0
- package/dist/core/coherence/consumed/refusal.js.map +1 -0
- package/dist/core/coherence/declared.js +60 -34
- package/dist/core/coherence/declared.js.map +1 -1
- package/dist/core/coherence/events/declared.js +44 -59
- package/dist/core/coherence/events/declared.js.map +1 -1
- package/dist/core/coherence/events/events.js +119 -8
- package/dist/core/coherence/events/events.js.map +1 -1
- package/dist/core/coherence/events/grades.js +31 -8
- package/dist/core/coherence/events/grades.js.map +1 -1
- package/dist/core/coherence/events/lookups.js +14 -82
- package/dist/core/coherence/events/lookups.js.map +1 -1
- package/dist/core/coherence/lookups.js +18 -78
- package/dist/core/coherence/lookups.js.map +1 -1
- package/dist/core/coherence/retired/archived.js +285 -0
- package/dist/core/coherence/retired/archived.js.map +1 -0
- package/dist/core/coherence/retired/event-pair.js +62 -0
- package/dist/core/coherence/retired/event-pair.js.map +1 -0
- package/dist/core/coherence/retired/merged.js +79 -0
- package/dist/core/coherence/retired/merged.js.map +1 -0
- package/dist/core/coherence/retired/retirement.js +49 -0
- package/dist/core/coherence/retired/retirement.js.map +1 -0
- package/dist/core/coherence/retired/revival/arrivals.js +131 -0
- package/dist/core/coherence/retired/revival/arrivals.js.map +1 -0
- package/dist/core/coherence/retired/revival/revived.js +297 -0
- package/dist/core/coherence/retired/revival/revived.js.map +1 -0
- package/dist/core/consumers/after.js +113 -0
- package/dist/core/consumers/after.js.map +1 -0
- package/dist/core/consumers/consumers.js +296 -0
- package/dist/core/consumers/consumers.js.map +1 -0
- package/dist/core/consumers/current.js +211 -0
- package/dist/core/consumers/current.js.map +1 -0
- package/dist/core/consumers/scan.js +46 -0
- package/dist/core/consumers/scan.js.map +1 -0
- package/dist/core/consumers/state.js +27 -0
- package/dist/core/consumers/state.js.map +1 -0
- package/dist/core/delta/delta.js +1 -1
- package/dist/core/delta/delta.js.map +1 -1
- package/dist/core/deployment/delta.js +3 -6
- package/dist/core/deployment/delta.js.map +1 -1
- package/dist/core/diff/base-git.js +94 -12
- package/dist/core/diff/base-git.js.map +1 -1
- package/dist/core/diff/base-state.js +28 -16
- package/dist/core/diff/base-state.js.map +1 -1
- package/dist/core/diff/joins.js +136 -0
- package/dist/core/diff/joins.js.map +1 -0
- package/dist/core/diff/scope/changed-paths.js +22 -6
- package/dist/core/diff/scope/changed-paths.js.map +1 -1
- package/dist/core/diff/scope/edges.js +82 -0
- package/dist/core/diff/scope/edges.js.map +1 -0
- package/dist/core/diff/semantic.js +100 -31
- package/dist/core/diff/semantic.js.map +1 -1
- package/dist/core/doctor/agents.js +34 -18
- package/dist/core/doctor/agents.js.map +1 -1
- package/dist/core/doctor/report.js.map +1 -1
- package/dist/core/document/apply.js +10 -2
- package/dist/core/document/apply.js.map +1 -1
- package/dist/core/document/frontmatter.js +55 -0
- package/dist/core/document/frontmatter.js.map +1 -1
- package/dist/core/document/parse.js +29 -6
- package/dist/core/document/parse.js.map +1 -1
- package/dist/core/document/scenarios.js +17 -0
- package/dist/core/document/scenarios.js.map +1 -1
- package/dist/core/document/spec.js +4 -11
- package/dist/core/document/spec.js.map +1 -1
- package/dist/core/envelope/config.js +10 -0
- package/dist/core/envelope/config.js.map +1 -1
- package/dist/core/envelope/json.js +34 -0
- package/dist/core/envelope/json.js.map +1 -1
- package/dist/core/explain/families.js +17 -12
- package/dist/core/explain/families.js.map +1 -1
- package/dist/core/explain/refusals.js +3 -3
- package/dist/core/explain/refusals.js.map +1 -1
- package/dist/core/explain/terms.js +18 -9
- package/dist/core/explain/terms.js.map +1 -1
- package/dist/core/explore/explore.js +2 -7
- package/dist/core/explore/explore.js.map +1 -1
- package/dist/core/fleet-context.js +34 -95
- package/dist/core/fleet-context.js.map +1 -1
- package/dist/core/gate/checks.js +5 -2
- package/dist/core/gate/checks.js.map +1 -1
- package/dist/core/gate/partners.js +4 -2
- package/dist/core/gate/partners.js.map +1 -1
- package/dist/core/gate/report.js.map +1 -1
- package/dist/core/gate/verification.js +75 -12
- package/dist/core/gate/verification.js.map +1 -1
- package/dist/core/gherkin/stale.js +13 -37
- package/dist/core/gherkin/stale.js.map +1 -1
- package/dist/core/glossary/tree.js +6 -19
- package/dist/core/glossary/tree.js.map +1 -1
- package/dist/core/kernel/concurrency.js +19 -0
- package/dist/core/kernel/concurrency.js.map +1 -1
- package/dist/core/kernel/records.js +11 -0
- package/dist/core/kernel/records.js.map +1 -1
- package/dist/core/links/backlinks.js +1 -3
- package/dist/core/links/backlinks.js.map +1 -1
- package/dist/core/links/corpus.js +5 -36
- package/dist/core/links/corpus.js.map +1 -1
- package/dist/core/mcp/argv.js +101 -0
- package/dist/core/mcp/argv.js.map +1 -1
- package/dist/core/mcp/author-tools.js +2 -1
- package/dist/core/mcp/author-tools.js.map +1 -1
- package/dist/core/mcp/protocol.js +2 -2
- package/dist/core/mcp/protocol.js.map +1 -1
- package/dist/core/mcp/tools.js +43 -33
- package/dist/core/mcp/tools.js.map +1 -1
- package/dist/core/openapi/baseline/gate.js +53 -12
- package/dist/core/openapi/baseline/gate.js.map +1 -1
- package/dist/core/openapi/baseline/plan.js +22 -8
- package/dist/core/openapi/baseline/plan.js.map +1 -1
- package/dist/core/openapi/baseline/record.js +0 -1
- package/dist/core/openapi/baseline/record.js.map +1 -1
- package/dist/core/openapi/contract-text.js +33 -0
- package/dist/core/openapi/contract-text.js.map +1 -0
- package/dist/core/openapi/contract-tree.js +56 -0
- package/dist/core/openapi/contract-tree.js.map +1 -0
- package/dist/core/openapi/depth.js +38 -24
- package/dist/core/openapi/depth.js.map +1 -1
- package/dist/core/openapi/digest.js +52 -2
- package/dist/core/openapi/digest.js.map +1 -1
- package/dist/core/openapi/doc.js +12 -4
- package/dist/core/openapi/doc.js.map +1 -1
- package/dist/core/openapi/merge/components.js +25 -60
- package/dist/core/openapi/merge/components.js.map +1 -1
- package/dist/core/openapi/merge/error.js +3 -13
- package/dist/core/openapi/merge/error.js.map +1 -1
- package/dist/core/openapi/merge/markers.js +26 -30
- package/dist/core/openapi/merge/markers.js.map +1 -1
- package/dist/core/openapi/merge/merge.js +26 -52
- package/dist/core/openapi/merge/merge.js.map +1 -1
- package/dist/core/openapi/merge/pin.js +15 -35
- package/dist/core/openapi/merge/pin.js.map +1 -1
- package/dist/core/openspec/decide/verdict.js.map +1 -1
- package/dist/core/openspec/inventory.js +4 -5
- package/dist/core/openspec/inventory.js.map +1 -1
- package/dist/core/openspec/model/mapping.js +30 -28
- package/dist/core/openspec/model/mapping.js.map +1 -1
- package/dist/core/openspec/model/model.js +12 -7
- package/dist/core/openspec/model/model.js.map +1 -1
- package/dist/core/openspec/scan/changes.js +10 -4
- package/dist/core/openspec/scan/changes.js.map +1 -1
- package/dist/core/openspec/scan/config.js +7 -16
- package/dist/core/openspec/scan/config.js.map +1 -1
- package/dist/core/openspec/scan/shape.js +63 -19
- package/dist/core/openspec/scan/shape.js.map +1 -1
- package/dist/core/openspec/scan/walk.js +21 -38
- package/dist/core/openspec/scan/walk.js.map +1 -1
- package/dist/core/pack/living.js +2 -16
- package/dist/core/pack/living.js.map +1 -1
- package/dist/core/provenance/findings.js +48 -12
- package/dist/core/provenance/findings.js.map +1 -1
- package/dist/core/provenance/git.js +33 -57
- package/dist/core/provenance/git.js.map +1 -1
- package/dist/core/provenance/gitq/moves.js +71 -114
- package/dist/core/provenance/gitq/moves.js.map +1 -1
- package/dist/core/provenance/gitq/vouched-ancestor.js +2 -2
- package/dist/core/provenance/gitq/vouched-ancestor.js.map +1 -1
- package/dist/core/provenance/sources.js +23 -6
- package/dist/core/provenance/sources.js.map +1 -1
- package/dist/core/provenance/stamp.js +5 -17
- package/dist/core/provenance/stamp.js.map +1 -1
- package/dist/core/repo/entries.js.map +1 -1
- package/dist/core/repo/repo.js +24 -14
- package/dist/core/repo/repo.js.map +1 -1
- package/dist/core/repo/tree/find.js +12 -3
- package/dist/core/repo/tree/find.js.map +1 -1
- package/dist/core/repo/tree/fs.js +27 -1
- package/dist/core/repo/tree/fs.js.map +1 -1
- package/dist/core/repo/tree/walk.js +8 -30
- package/dist/core/repo/tree/walk.js.map +1 -1
- package/dist/core/results.js +47 -12
- package/dist/core/results.js.map +1 -1
- package/dist/core/staging/commit.js +10 -12
- package/dist/core/staging/commit.js.map +1 -1
- package/dist/core/staging/lock.js +74 -12
- package/dist/core/staging/lock.js.map +1 -1
- package/dist/core/staging/recovery/intent.js +2 -5
- package/dist/core/staging/recovery/intent.js.map +1 -1
- package/dist/core/staging/recovery/recover.js +60 -1
- package/dist/core/staging/recovery/recover.js.map +1 -1
- package/dist/core/staging/snapshot/manifest.js +152 -0
- package/dist/core/staging/snapshot/manifest.js.map +1 -0
- package/dist/core/staging/{snapshot.js → snapshot/snapshot.js} +10 -34
- package/dist/core/staging/snapshot/snapshot.js.map +1 -0
- package/dist/core/status/actions/execution.js +11 -0
- package/dist/core/status/actions/execution.js.map +1 -1
- package/dist/core/status/feature/artifacts.js +2 -2
- package/dist/core/status/feature/artifacts.js.map +1 -1
- package/dist/core/status/feature/feature.js +5 -11
- package/dist/core/status/feature/feature.js.map +1 -1
- package/dist/core/status/fleet/fleet.js +6 -2
- package/dist/core/status/fleet/fleet.js.map +1 -1
- package/dist/core/status/fleet/next.js +26 -3
- package/dist/core/status/fleet/next.js.map +1 -1
- package/dist/core/status/report.js.map +1 -1
- package/dist/core/status/verification.js +30 -15
- package/dist/core/status/verification.js.map +1 -1
- package/dist/core/tasks/packet.js +174 -0
- package/dist/core/tasks/packet.js.map +1 -0
- package/dist/core/tasks/record.js +43 -0
- package/dist/core/tasks/record.js.map +1 -0
- package/dist/core/tasks/sources.js +181 -0
- package/dist/core/tasks/sources.js.map +1 -0
- package/dist/core/tasks/stamps.js +76 -0
- package/dist/core/tasks/stamps.js.map +1 -0
- package/dist/core/tasks/unit.js +0 -0
- package/dist/core/tasks/unit.js.map +1 -0
- package/dist/core/usecases/capability.js +11 -14
- package/dist/core/usecases/capability.js.map +1 -1
- package/dist/core/usecases/delta/flows.js +5 -7
- package/dist/core/usecases/delta/flows.js.map +1 -1
- package/dist/core/usecases/fleet.js +178 -31
- package/dist/core/usecases/fleet.js.map +1 -1
- package/dist/core/usecases/operations.js +5 -4
- package/dist/core/usecases/operations.js.map +1 -1
- package/dist/core/usecases/service/flows.js +9 -6
- package/dist/core/usecases/service/flows.js.map +1 -1
- package/dist/core/usecases/suites/participants.js +83 -0
- package/dist/core/usecases/suites/participants.js.map +1 -0
- package/dist/core/usecases/suites/suites.js +45 -0
- package/dist/core/usecases/suites/suites.js.map +1 -0
- package/dist/core/verify/answers.js +12 -5
- package/dist/core/verify/answers.js.map +1 -1
- package/dist/core/verify/build.js +102 -82
- package/dist/core/verify/build.js.map +1 -1
- package/dist/core/verify/checklist.js +3 -0
- package/dist/core/verify/checklist.js.map +1 -1
- package/dist/core/verify/location.js +67 -0
- package/dist/core/verify/location.js.map +1 -0
- package/dist/core/verify/pins/lint.js +3 -2
- package/dist/core/verify/pins/lint.js.map +1 -1
- package/dist/core/verify/pins/pin.js +1 -1
- package/dist/core/verify/pins/pin.js.map +1 -1
- package/dist/core/verify/read/file.js +224 -0
- package/dist/core/verify/read/file.js.map +1 -0
- package/dist/core/verify/{file.js → read/shape.js} +16 -104
- package/dist/core/verify/read/shape.js.map +1 -0
- package/dist/core/verify/read/union.js +105 -0
- package/dist/core/verify/read/union.js.map +1 -0
- package/dist/core/verify/record.js +35 -12
- package/dist/core/verify/record.js.map +1 -1
- package/dist/core/verify/store/commit.js +40 -30
- package/dist/core/verify/store/commit.js.map +1 -1
- package/dist/core/verify/store/render.js +8 -4
- package/dist/core/verify/store/render.js.map +1 -1
- package/dist/core/vocabulary/issue.js +1 -0
- package/dist/core/vocabulary/issue.js.map +1 -1
- package/dist/core/vocabulary/maturity.js +12 -2
- package/dist/core/vocabulary/maturity.js.map +1 -1
- package/examples/README.md +21 -6
- package/examples/docs/architecture/landscape.likec4 +8 -0
- package/examples/docs/architecture/usecases/order-notification.likec4 +6 -2
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-service/asyncapi.yaml +11 -8
- package/examples/docs/features/FEAT-101-payment-splitting/specs/payment-split-service/openapi.yaml +6 -6
- package/examples/docs/features/archive/FEAT-088-refunds/specs/payment-service/openapi.yaml +13 -10
- package/examples/docs/services/order-service/asyncapi.yaml +3 -2
- package/examples/docs/services/platform/notification-service/asyncapi.yaml +2 -1
- package/package.json +1 -1
- package/dist/commands/validate/checks/vocabulary.js +0 -16
- package/dist/commands/validate/checks/vocabulary.js.map +0 -1
- package/dist/core/agent/agents-md/artifacts.js +0 -221
- package/dist/core/agent/agents-md/artifacts.js.map +0 -1
- package/dist/core/agent/agents-md/cycle.js +0 -180
- package/dist/core/agent/agents-md/cycle.js.map +0 -1
- package/dist/core/agent/agents-md/shipped/archive-gate.js +0 -98
- package/dist/core/agent/agents-md/shipped/archive-gate.js.map +0 -1
- package/dist/core/agent/agents-md/spine.js +0 -40
- package/dist/core/agent/agents-md/spine.js.map +0 -1
- package/dist/core/coherence/events/merged.js +0 -40
- package/dist/core/coherence/events/merged.js.map +0 -1
- package/dist/core/diff/current-state.js +0 -96
- package/dist/core/diff/current-state.js.map +0 -1
- package/dist/core/diff/victims.js +0 -256
- package/dist/core/diff/victims.js.map +0 -1
- package/dist/core/staging/snapshot.js.map +0 -1
- package/dist/core/verify/file.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"records.js","sourceRoot":"","sources":["../../../src/core/kernel/records.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,+DAA+D;AAC/D,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;AACxC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,UAAU;IACxB,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,CAAsB,CAAC;AAClD,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,QAAQ,CAAI,MAAyB,EAAE,GAAW;IAChE,OAAO,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACrF,CAAC","sourcesContent":["/**\n * The shapes an unknown parsed document is asked about, in one place.\n *\n * Every YAML and JSON file loam reads arrives as `unknown`, and every reader\n * has to answer the same first question before it can look at a key: is this an\n * object at all? Six modules each carried their own copy of the answer, two of\n * them spelled with a cast — and a cast is exactly what this test exists to\n * make unnecessary, since the predicate form hands the caller a narrowed type\n * that tsc keeps checking. A leaf with no imports, so any layer can ask.\n */\n\n/** An object with string keys — not null, and not an array. */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** The same question where the answer is used rather than branched on. */\nexport function asRecord(value: unknown): Record<string, unknown> | null {\n return isRecord(value) ? value : null;\n}\n\n/**\n * A map whose keys come from a document somebody else wrote.\n *\n * `Object.create(null)` rather than `{}` because a key read out of untrusted\n * YAML can be `__proto__` or `constructor`, and on a normal object those either\n * mutate the prototype or read back a function that was never in the file.\n */\nexport function dictionary<T>(): Record<string, T> {\n return Object.create(null) as Record<string, T>;\n}\n\n/** The value a record actually holds for `key`, never one it inherited. */\nexport function ownValue<T>(record: Record<string, T>, key: string): T | undefined {\n return Object.prototype.hasOwnProperty.call(record, key) ? record[key] : undefined;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"records.js","sourceRoot":"","sources":["../../../src/core/kernel/records.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,+DAA+D;AAC/D,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;AACxC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC;AACjF,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,UAAU;IACxB,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,CAAsB,CAAC;AAClD,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,QAAQ,CAAI,MAAyB,EAAE,GAAW;IAChE,OAAO,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACrF,CAAC","sourcesContent":["/**\n * The shapes an unknown parsed document is asked about, in one place.\n *\n * Every YAML and JSON file loam reads arrives as `unknown`, and every reader\n * has to answer the same first question before it can look at a key: is this an\n * object at all? Six modules each carried their own copy of the answer, two of\n * them spelled with a cast — and a cast is exactly what this test exists to\n * make unnecessary, since the predicate form hands the caller a narrowed type\n * that tsc keeps checking. A leaf with no imports, so any layer can ask.\n */\n\n/** An object with string keys — not null, and not an array. */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** The same question where the answer is used rather than branched on. */\nexport function asRecord(value: unknown): Record<string, unknown> | null {\n return isRecord(value) ? value : null;\n}\n\n/**\n * A list of strings. `Array.isArray` narrows an `unknown` to `any[]`, so an\n * element check written below it proved nothing to tsc and the elements had to\n * be asserted back to `string` at the point of use — how a checked element\n * type went on being asserted back in a line later. Deciding both halves in\n * one predicate makes the narrowing carry the check. (It was written twice,\n * once in the OpenSpec scan and once in the migration's mapping reader.)\n */\nexport function isStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.every((item) => typeof item === \"string\");\n}\n\n/**\n * A map whose keys come from a document somebody else wrote.\n *\n * `Object.create(null)` rather than `{}` because a key read out of untrusted\n * YAML can be `__proto__` or `constructor`, and on a normal object those either\n * mutate the prototype or read back a function that was never in the file.\n */\nexport function dictionary<T>(): Record<string, T> {\n return Object.create(null) as Record<string, T>;\n}\n\n/** The value a record actually holds for `key`, never one it inherited. */\nexport function ownValue<T>(record: Record<string, T>, key: string): T | undefined {\n return Object.prototype.hasOwnProperty.call(record, key) ? record[key] : undefined;\n}\n"]}
|
|
@@ -20,11 +20,9 @@
|
|
|
20
20
|
* file already earns `link.unreadable`; `loam list` has no such finding, so it
|
|
21
21
|
* needs the list to say what it did not read.
|
|
22
22
|
*/
|
|
23
|
-
import { readFile } from "node:fs/promises";
|
|
24
23
|
import { existsSync } from "node:fs";
|
|
25
24
|
import { resolve } from "node:path";
|
|
26
25
|
import { repoPath } from "../envelope/json.js";
|
|
27
|
-
import { decodeDocument } from "../kernel/document-bytes.js";
|
|
28
26
|
import { pathCaseIndex } from "./case.js";
|
|
29
27
|
import { allAuthoredDocuments } from "./corpus.js";
|
|
30
28
|
import { resolveLinks } from "./findings.js";
|
|
@@ -44,7 +42,7 @@ export async function backlinkIndex(docsDir, fleet) {
|
|
|
44
42
|
continue;
|
|
45
43
|
let text;
|
|
46
44
|
try {
|
|
47
|
-
text =
|
|
45
|
+
text = await fleet.readText(path);
|
|
48
46
|
}
|
|
49
47
|
catch {
|
|
50
48
|
unreadable.push(repoPath(docsDir, path));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backlinks.js","sourceRoot":"","sources":["../../../src/core/links/backlinks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"backlinks.js","sourceRoot":"","sources":["../../../src/core/links/backlinks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAG/C,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAS7C;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAAgB,EAAE,KAAmB;IACvE,MAAM,OAAO,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC/C,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,MAAM,KAAK,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE,CAAC;IAClD,KAAK,MAAM,IAAI,IAAI,MAAM,oBAAoB,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC;QAC9D,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,SAAS;QAChC,IAAI,IAAY,CAAC;QACjB,IAAI,CAAC;YACH,IAAI,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;YACzC,SAAS;QACX,CAAC;QACD,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QACrC,KAAK,MAAM,EAAE,MAAM,EAAE,IAAI,YAAY,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC;YACtE,MAAM,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;YAC5B,IAAI,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC/B,IAAI,OAAO,KAAK,SAAS;gBAAE,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,OAAO,GAAG,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC;YACnE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IACD,UAAU,CAAC,IAAI,EAAE,CAAC;IAClB,OAAO;QACL,SAAS,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE;QACvE,UAAU;KACX,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Who links to what, across the whole repository — the link graph read\n * backwards.\n *\n * `link.unresolved` asks each document about its own links. This asks the\n * opposite question, and it is the one the glossary is built on: \"which\n * documents use this term\" is exactly \"which documents link to `glossary/<term>.md`\",\n * and no single target can answer it. A term is cited from a service's spec, a\n * capability document and a feature's intent, so the index is fleet-scope by\n * construction.\n *\n * IT IS THE SAME WALK AS THE FINDING, deliberately — `resolveLinks` produces\n * both halves of one answer, and the corpus is `core/links/corpus.ts`'s single\n * definition. An index built from a second enumeration would report a term as\n * unused while the document citing it was being graded one target over.\n *\n * AN UNREADABLE DOCUMENT IS RETURNED, not swallowed. A term can only be shown as\n * cited by nobody if loam actually read everybody, and a UTF-16 spec.md would\n * otherwise turn into a silently missing citation. Under `validate` the same\n * file already earns `link.unreadable`; `loam list` has no such finding, so it\n * needs the list to say what it did not read.\n */\nimport { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { repoPath } from \"../envelope/json.js\";\nimport type { FleetContext } from \"../fleet-context.js\";\nimport type { DocsDir } from \"../kernel/ids/dirs.js\";\nimport { pathCaseIndex } from \"./case.js\";\nimport { allAuthoredDocuments } from \"./corpus.js\";\nimport { resolveLinks } from \"./findings.js\";\n\nexport interface BacklinkIndex {\n /** Repo-relative paths of the documents linking to `target`, ordered, without duplicates. */\n linkersOf(target: string): string[];\n /** Repo-relative paths of documents whose links could not be read at all. */\n unreadable: string[];\n}\n\n/**\n * Build the index over every authored document in the repository.\n *\n * One `pathCaseIndex` for the whole walk, because this one really is fleet-wide\n * and the same directories are asked about hundreds of times. Still not a\n * module-level cache: it lives exactly as long as this call.\n */\nexport async function backlinkIndex(docsDir: DocsDir, fleet: FleetContext): Promise<BacklinkIndex> {\n const linkers = new Map<string, Set<string>>();\n const unreadable: string[] = [];\n const scope = { docsDir, cases: pathCaseIndex() };\n for (const path of await allAuthoredDocuments(docsDir, fleet)) {\n if (!existsSync(path)) continue;\n let text: string;\n try {\n text = await fleet.readText(path);\n } catch {\n unreadable.push(repoPath(docsDir, path));\n continue;\n }\n const from = repoPath(docsDir, path);\n for (const { target } of resolveLinks({ path, text }, scope).resolved) {\n const key = resolve(target);\n let sources = linkers.get(key);\n if (sources === undefined) linkers.set(key, (sources = new Set()));\n sources.add(from);\n }\n }\n unreadable.sort();\n return {\n linkersOf: (target) => [...(linkers.get(resolve(target)) ?? [])].sort(),\n unreadable,\n };\n}\n"]}
|
|
@@ -1,42 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WHICH documents loam reads links out of — the corpus, spelled once.
|
|
3
|
-
*
|
|
4
|
-
* Two readers need it and must not disagree. `commands/validate/links/` grades
|
|
5
|
-
* each target's own documents for `link.unresolved`; `core/glossary/backlinks.ts`
|
|
6
|
-
* reads the WHOLE fleet's, because "which documents use this term" is a question
|
|
7
|
-
* no single target can answer. A corpus that differed between them would report
|
|
8
|
-
* a term as unused while the very document citing it was being graded one target
|
|
9
|
-
* over.
|
|
10
|
-
*
|
|
11
|
-
* THE CORPUS IS EVERY AUTHORED MARKDOWN DOCUMENT, and the word doing the work is
|
|
12
|
-
* authored. `AGENTS.md` and the scaffolded `README.md` are excluded because loam
|
|
13
|
-
* WRITES them: a finding against generated prose names a defect its reader
|
|
14
|
-
* cannot fix by editing, since the next `loam init` restores it. Their links are
|
|
15
|
-
* loam's own problem, held on this side of the boundary by
|
|
16
|
-
* `test/docs-facts.test.ts` and `test/package-docs.test.ts`.
|
|
17
|
-
*
|
|
18
|
-
* ADRs AND RUNBOOKS ARE IN IT, and they are the reason the convention was
|
|
19
|
-
* written down at all: an ADR that supersedes another says which, by linking to
|
|
20
|
-
* it. They are also the first documents loam reads that it previously only
|
|
21
|
-
* counted, which is what `link.unreadable` exists for.
|
|
22
|
-
*
|
|
23
|
-
* `delta.likec4` IS NOT, and neither is any YAML: this is a markdown question.
|
|
24
|
-
* The one thing a delta holds that addresses another file — `metadata { op }` —
|
|
25
|
-
* has its own resolution and its own findings.
|
|
26
|
-
*/
|
|
27
|
-
import { featureCapabilityDeltas } from "../capabilities/delta/tree.js";
|
|
28
|
-
import { readCapabilityTree } from "../capabilities/tree.js";
|
|
29
1
|
import { featureGlossary } from "../glossary/delta.js";
|
|
30
2
|
import { readGlossary } from "../glossary/tree.js";
|
|
31
3
|
import { featurePaths, featureSpecPaths, fleetAdrsDir, servicePathsAt } from "../repo/paths.js";
|
|
32
|
-
import {
|
|
4
|
+
import { glossaryDir } from "../repo/authored/paths.js";
|
|
33
5
|
import { featureSpecServices, listFeatures, listServices } from "../repo/repo.js";
|
|
34
6
|
import { markdownFiles } from "../repo/tree/fs.js";
|
|
35
7
|
/**
|
|
36
8
|
* One service's authored documents: the two requirement specs, the runbook, and
|
|
37
|
-
* every ADR. The specs cost nothing extra
|
|
38
|
-
*
|
|
39
|
-
* same text.
|
|
9
|
+
* every ADR. The specs cost nothing extra: the run's `FleetContext` has already
|
|
10
|
+
* read both to parse their requirements, and the memo hands back the same text.
|
|
40
11
|
*/
|
|
41
12
|
export async function serviceDocuments(paths) {
|
|
42
13
|
return [paths.spec, paths.archSpec, paths.runbook, ...(await markdownFiles(paths.adrsDir))];
|
|
@@ -52,7 +23,7 @@ export async function featureDocuments(featureDir, fleet) {
|
|
|
52
23
|
const spec = featureSpecPaths(featureDir, svc);
|
|
53
24
|
docs.push(spec.spec, spec.archSpec);
|
|
54
25
|
}
|
|
55
|
-
const capabilities =
|
|
26
|
+
const capabilities = await fleet.featureCapabilityDeltas(featureDir);
|
|
56
27
|
docs.push(...capabilities.docs.map((d) => d.spec));
|
|
57
28
|
// The definitions this feature introduces. Their own links are graded like
|
|
58
29
|
// any other document's — a term that cites a term the feature is not adding
|
|
@@ -70,9 +41,7 @@ export async function featureDocuments(featureDir, fleet) {
|
|
|
70
41
|
* the fleet repeated on every service target is the report.
|
|
71
42
|
*/
|
|
72
43
|
export async function fleetDocuments(docsDir, fleet) {
|
|
73
|
-
const capabilities = fleet
|
|
74
|
-
? await readCapabilityTree(capabilityDocsDir(docsDir))
|
|
75
|
-
: (await fleet.capabilities(docsDir)).tree;
|
|
44
|
+
const capabilities = (await fleet.capabilities(docsDir)).tree;
|
|
76
45
|
const glossary = await readGlossary(glossaryDir(docsDir));
|
|
77
46
|
return [
|
|
78
47
|
...(await markdownFiles(fleetAdrsDir(docsDir))),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"corpus.js","sourceRoot":"","sources":["../../../src/core/links/corpus.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"corpus.js","sourceRoot":"","sources":["../../../src/core/links/corpus.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * WHICH documents loam reads links out of — the corpus, spelled once.\n *\n * Two readers need it and must not disagree. `commands/validate/links/` grades\n * each target's own documents for `link.unresolved`; `core/glossary/backlinks.ts`\n * reads the WHOLE fleet's, because \"which documents use this term\" is a question\n * no single target can answer. A corpus that differed between them would report\n * a term as unused while the very document citing it was being graded one target\n * over.\n *\n * THE CORPUS IS EVERY AUTHORED MARKDOWN DOCUMENT, and the word doing the work is\n * authored. `AGENTS.md` and the scaffolded `README.md` are excluded because loam\n * WRITES them: a finding against generated prose names a defect its reader\n * cannot fix by editing, since the next `loam init` restores it. Their links are\n * loam's own problem, held on this side of the boundary by\n * `test/docs-facts.test.ts` and `test/package-docs.test.ts`.\n *\n * ADRs AND RUNBOOKS ARE IN IT, and they are the reason the convention was\n * written down at all: an ADR that supersedes another says which, by linking to\n * it. They are also the first documents loam reads that it previously only\n * counted, which is what `link.unreadable` exists for.\n *\n * `delta.likec4` IS NOT, and neither is any YAML: this is a markdown question.\n * The one thing a delta holds that addresses another file — `metadata { op }` —\n * has its own resolution and its own findings.\n */\nimport type { FleetContext } from \"../fleet-context.js\";\nimport { featureGlossary } from \"../glossary/delta.js\";\nimport { readGlossary } from \"../glossary/tree.js\";\nimport type { DocsDir, FeatureDir } from \"../kernel/ids/dirs.js\";\nimport { featurePaths, featureSpecPaths, fleetAdrsDir, servicePathsAt, type ServicePaths } from \"../repo/paths.js\";\nimport { glossaryDir } from \"../repo/authored/paths.js\";\nimport { featureSpecServices, listFeatures, listServices } from \"../repo/repo.js\";\nimport { markdownFiles } from \"../repo/tree/fs.js\";\n\n/**\n * One service's authored documents: the two requirement specs, the runbook, and\n * every ADR. The specs cost nothing extra: the run's `FleetContext` has already\n * read both to parse their requirements, and the memo hands back the same text.\n */\nexport async function serviceDocuments(paths: ServicePaths): Promise<string[]> {\n return [paths.spec, paths.archSpec, paths.runbook, ...(await markdownFiles(paths.adrsDir))];\n}\n\n/**\n * One feature's authored documents: the intent, each addressed service's two\n * spec deltas, every ADR, and each capability delta document.\n */\nexport async function featureDocuments(featureDir: FeatureDir, fleet: FleetContext): Promise<string[]> {\n const paths = featurePaths(featureDir);\n const docs = [paths.intent, ...(await markdownFiles(paths.adrsDir))];\n for (const svc of await featureSpecServices(featureDir, fleet)) {\n const spec = featureSpecPaths(featureDir, svc);\n docs.push(spec.spec, spec.archSpec);\n }\n const capabilities = await fleet.featureCapabilityDeltas(featureDir);\n docs.push(...capabilities.docs.map((d) => d.spec));\n // The definitions this feature introduces. Their own links are graded like\n // any other document's — a term that cites a term the feature is not adding\n // is a broken join whether or not it has shipped yet.\n docs.push(...(await featureGlossary(featureDir)).terms.map((t) => t.path));\n return docs;\n}\n\n/**\n * The documents that belong to no service and no feature: the fleet's own\n * decision records, the living capability tree, and the glossary.\n *\n * Graded at fleet scope, so `validate --all` reports them once. A single-target\n * run says nothing about them — the rule `permissions.unenforced` and\n * `capability.invalid` already follow, and for the same reason: a finding about\n * the fleet repeated on every service target is the report.\n */\nexport async function fleetDocuments(docsDir: DocsDir, fleet: FleetContext): Promise<string[]> {\n const capabilities = (await fleet.capabilities(docsDir)).tree;\n const glossary = await readGlossary(glossaryDir(docsDir));\n return [\n ...(await markdownFiles(fleetAdrsDir(docsDir))),\n ...capabilities.docs.map((d) => d.spec),\n ...glossary.terms.map((t) => t.path),\n ];\n}\n\n/**\n * Every authored document in the repository — the union of the three above over\n * every service and every in-flight feature.\n *\n * ARCHIVED FEATURES ARE OUT, and the omission is the point rather than an\n * oversight. `features/archive/<FEAT>/` is the evolution history: its documents\n * describe a change that already shipped, its links point at whatever the tree\n * looked like then, and grading them would make every directory rename a\n * retroactive error against a record nobody may edit. `listFeatures` excludes\n * them by default, which is the same judgement one enumeration up.\n */\nexport async function allAuthoredDocuments(docsDir: DocsDir, fleet: FleetContext): Promise<string[]> {\n const docs = await fleetDocuments(docsDir, fleet);\n for (const service of await listServices(docsDir, fleet)) {\n docs.push(...(await serviceDocuments(servicePathsAt(service.dir))));\n }\n for (const feature of await listFeatures(docsDir, {}, fleet)) {\n docs.push(...(await featureDocuments(feature.dir, fleet)));\n }\n return docs;\n}\n"]}
|
package/dist/core/mcp/argv.js
CHANGED
|
@@ -1,3 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The MCP inputSchema for one tool: a closed object, so a typo'd argument
|
|
3
|
+
* refuses instead of vanishing, with the required set and the positional order
|
|
4
|
+
* `toArgv` enforces declared beside it.
|
|
5
|
+
*/
|
|
6
|
+
export function toInputSchema(tool) {
|
|
7
|
+
const properties = {};
|
|
8
|
+
for (const positional of tool.positionals) {
|
|
9
|
+
properties[positional.property] = positional.variadic
|
|
10
|
+
? { type: "array", items: { type: "string" }, description: positional.description }
|
|
11
|
+
: { type: "string", description: positional.description };
|
|
12
|
+
}
|
|
13
|
+
for (const flag of tool.flags) {
|
|
14
|
+
properties[flag.property] =
|
|
15
|
+
flag.kind === "boolean"
|
|
16
|
+
? { type: "boolean", description: flag.description }
|
|
17
|
+
: flag.kind === "strings"
|
|
18
|
+
? { type: "array", items: { type: "string" }, description: flag.description }
|
|
19
|
+
: { type: "string", description: flag.description };
|
|
20
|
+
}
|
|
21
|
+
const required = [
|
|
22
|
+
...tool.positionals.filter((p) => p.required).map((p) => p.property),
|
|
23
|
+
...tool.flags.filter((f) => f.required === true).map((f) => f.property),
|
|
24
|
+
];
|
|
25
|
+
return {
|
|
26
|
+
type: "object",
|
|
27
|
+
properties,
|
|
28
|
+
...(required.length > 0 ? { required } : {}),
|
|
29
|
+
...orderRule(tool),
|
|
30
|
+
additionalProperties: false,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The rule `positionalTokens` enforces, declared: a positional after an
|
|
35
|
+
* optional one needs it, because positionals reach argv by position and the
|
|
36
|
+
* later value would otherwise be read in the earlier one's slot. Without it a
|
|
37
|
+
* client that validated `{name: "smoke"}` for `loam_list` against the schema
|
|
38
|
+
* it was given passed, and was then refused `-32602` by the server.
|
|
39
|
+
*
|
|
40
|
+
* Read off the declaration order rather than spelled per tool, so a tool that
|
|
41
|
+
* gains a positional after an optional one is declared the day it is refused.
|
|
42
|
+
* Each later positional needs every earlier OPTIONAL one; an earlier required
|
|
43
|
+
* one is in `required` already.
|
|
44
|
+
*
|
|
45
|
+
* Two keywords, because presence means two things. A scalar that is present
|
|
46
|
+
* always yields a token, so presence is the whole condition and
|
|
47
|
+
* `dependentRequired` says exactly that. A variadic yields tokens only when it
|
|
48
|
+
* has members: an empty one moves nothing into anybody's slot, and
|
|
49
|
+
* `positionalTokens` serves it — `{args: []}` with no `workflow` is the
|
|
50
|
+
* workflow list. `dependentRequired` there would forbid a call the server
|
|
51
|
+
* answers, the same disagreement turned the other way, so the variadic's rule
|
|
52
|
+
* sits under `dependentSchemas`: once it is present, either the earlier ones
|
|
53
|
+
* are too or it is empty. `anyOf` rather than `if`/`then`, which says the same
|
|
54
|
+
* but puts a `then` key on an object, and a thenable literal is one `await`
|
|
55
|
+
* away from being called.
|
|
56
|
+
*/
|
|
57
|
+
function orderRule(tool) {
|
|
58
|
+
const dependentRequired = {};
|
|
59
|
+
const dependentSchemas = {};
|
|
60
|
+
const earlier = [];
|
|
61
|
+
for (const positional of tool.positionals) {
|
|
62
|
+
if (earlier.length > 0 && positional.variadic) {
|
|
63
|
+
dependentSchemas[positional.property] = {
|
|
64
|
+
anyOf: [{ required: [...earlier] }, { properties: { [positional.property]: { maxItems: 0 } } }],
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
else if (earlier.length > 0) {
|
|
68
|
+
dependentRequired[positional.property] = [...earlier];
|
|
69
|
+
}
|
|
70
|
+
if (!positional.required)
|
|
71
|
+
earlier.push(positional.property);
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
...(Object.keys(dependentRequired).length > 0 ? { dependentRequired } : {}),
|
|
75
|
+
...(Object.keys(dependentSchemas).length > 0 ? { dependentSchemas } : {}),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
1
78
|
/**
|
|
2
79
|
* A string value that argv would read as a flag is refused before any command
|
|
3
80
|
* sees it: {"target": "--type"} must not become a parsed option. Commander
|
|
@@ -30,13 +107,30 @@ function checkedStrings(property, value) {
|
|
|
30
107
|
}
|
|
31
108
|
return { ok: true, tokens };
|
|
32
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* The positionals, in declaration order — and refused when one would land in
|
|
112
|
+
* another's slot.
|
|
113
|
+
*
|
|
114
|
+
* A positional reaches argv by POSITION, so an optional one left out leaves no
|
|
115
|
+
* gap: whatever follows it moves up into its place. `loam_list` with `{name:
|
|
116
|
+
* "services"}` built `list services` and answered ok:true with the services
|
|
117
|
+
* listing, a wrong answer with nothing in it saying so; `loam_instructions`
|
|
118
|
+
* with `{args: [...]}` and no `workflow` read the first arg as the workflow.
|
|
119
|
+
* Refused here instead, as the invalid-params problem a missing REQUIRED
|
|
120
|
+
* positional already is, naming the slot the caller left empty. A later
|
|
121
|
+
* positional that yields no token — an empty variadic — moves nothing up, so
|
|
122
|
+
* it passes. `orderRule` above declares the same rule in the advertised
|
|
123
|
+
* schema; the two change together.
|
|
124
|
+
*/
|
|
33
125
|
function positionalTokens(tool, args) {
|
|
34
126
|
const tokens = [];
|
|
127
|
+
let absent;
|
|
35
128
|
for (const positional of tool.positionals) {
|
|
36
129
|
const value = args[positional.property];
|
|
37
130
|
if (value === undefined) {
|
|
38
131
|
if (positional.required)
|
|
39
132
|
return { ok: false, problem: `required argument '${positional.property}' is missing` };
|
|
133
|
+
absent ??= positional.property;
|
|
40
134
|
continue;
|
|
41
135
|
}
|
|
42
136
|
const checked = positional.variadic
|
|
@@ -44,6 +138,13 @@ function positionalTokens(tool, args) {
|
|
|
44
138
|
: checkedString(positional.property, value);
|
|
45
139
|
if (!checked.ok)
|
|
46
140
|
return checked;
|
|
141
|
+
if (absent !== undefined && checked.tokens.length > 0) {
|
|
142
|
+
return {
|
|
143
|
+
ok: false,
|
|
144
|
+
problem: `argument '${positional.property}' requires '${absent}' — positionals are read in order, ` +
|
|
145
|
+
`so without '${absent}' this value would be read as '${absent}'`,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
47
148
|
tokens.push(...checked.tokens);
|
|
48
149
|
}
|
|
49
150
|
return { ok: true, tokens };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"argv.js","sourceRoot":"","sources":["../../../src/core/mcp/argv.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * How one MCP tool call's JSON arguments become an argv the CLI already\n * trusts — the boundary where an argument value could otherwise smuggle a\n * flag. The tool TABLE (what the tools are) lives in `./tools.ts`; this\n * module owns the crossing.\n */\nimport type { McpTool } from \"./tools.js\";\n\n/** One checked step of argv construction: the tokens it yields, or the invalid-params problem. */\ntype Checked =\n | { readonly ok: true; readonly tokens: readonly string[] }\n | { readonly ok: false; readonly problem: string };\n\nexport type ArgvOutcome =\n | { readonly ok: true; readonly argv: readonly string[] }\n | { readonly ok: false; readonly problem: string };\n\n/**\n * A string value that argv would read as a flag is refused before any command\n * sees it: {\"target\": \"--type\"} must not become a parsed option. Commander\n * cannot make this distinction after the fact — by then the token IS a flag —\n * so the boundary is here, on every string that crosses, array members\n * included. No shell is ever involved, so this is the only smuggling path.\n */\nfunction smuggled(property: string, value: string): string | null {\n return value.startsWith(\"-\")\n ? `argument '${property}' value '${value}' begins with '-' — a value argv would read as a flag is refused`\n : null;\n}\n\n/** One string value checked for type and smuggling. */\nfunction checkedString(property: string, value: unknown): Checked {\n if (typeof value !== \"string\") return { ok: false, problem: `argument '${property}' must be a string` };\n const bad = smuggled(property, value);\n return bad === null ? { ok: true, tokens: [value] } : { ok: false, problem: bad };\n}\n\n/** An array value checked member by member — rebuilt, not cast, so the claim is earned. */\nfunction checkedStrings(property: string, value: unknown): Checked {\n if (!Array.isArray(value)) return { ok: false, problem: `argument '${property}' must be an array of strings` };\n const tokens: string[] = [];\n for (const entry of value) {\n const checked = checkedString(property, entry);\n if (!checked.ok) return checked;\n tokens.push(...checked.tokens);\n }\n return { ok: true, tokens };\n}\n\nfunction positionalTokens(tool: McpTool, args: Record<string, unknown>): Checked {\n const tokens: string[] = [];\n for (const positional of tool.positionals) {\n const value = args[positional.property];\n if (value === undefined) {\n if (positional.required) return { ok: false, problem: `required argument '${positional.property}' is missing` };\n continue;\n }\n const checked = positional.variadic\n ? checkedStrings(positional.property, value)\n : checkedString(positional.property, value);\n if (!checked.ok) return checked;\n tokens.push(...checked.tokens);\n }\n return { ok: true, tokens };\n}\n\nfunction flagTokens(tool: McpTool, args: Record<string, unknown>): Checked {\n const tokens: string[] = [];\n for (const flag of tool.flags) {\n const value = args[flag.property];\n if (value === undefined) {\n // The same refusal absent required positionals get: the client learns\n // \"-32602 missing argument\", not an invalid-option envelope after the\n // command already refused.\n if (flag.required === true) return { ok: false, problem: `required argument '${flag.property}' is missing` };\n continue;\n }\n if (flag.kind === \"boolean\") {\n if (typeof value !== \"boolean\") return { ok: false, problem: `argument '${flag.property}' must be a boolean` };\n if (value) tokens.push(flag.flag);\n continue;\n }\n const checked = flag.kind === \"strings\"\n ? checkedStrings(flag.property, value)\n : checkedString(flag.property, value);\n if (!checked.ok) return checked;\n // A repeatable flag repeats per value; a single-valued one appears once.\n for (const token of checked.tokens) tokens.push(flag.flag, token);\n }\n return { ok: true, tokens };\n}\n\n/**\n * The argv for one tool call — or the invalid-params problem that stops it.\n * `--json` is always appended: the envelope is the tool-result contract, and\n * a client cannot opt out of it into the human rendering. Unknown argument\n * names refuse rather than vanish — the schema says additionalProperties\n * false, and a server that enforced that only in the schema would silently\n * drop the typo'd argument of any client that skipped validation.\n */\nexport function toArgv(tool: McpTool, args: Record<string, unknown>): ArgvOutcome {\n const known = new Set([\n ...tool.positionals.map((p) => p.property),\n ...tool.flags.map((f) => f.property),\n ]);\n for (const property of Object.keys(args)) {\n if (!known.has(property)) {\n const allowed = known.size === 0 ? \"no arguments\" : [...known].join(\", \");\n return { ok: false, problem: `unknown argument '${property}' for ${tool.name} — expected ${allowed}` };\n }\n }\n const positionals = positionalTokens(tool, args);\n if (!positionals.ok) return { ok: false, problem: positionals.problem };\n const flags = flagTokens(tool, args);\n if (!flags.ok) return { ok: false, problem: flags.problem };\n return {\n ok: true,\n argv: [tool.command, ...positionals.tokens, ...flags.tokens, ...(tool.fixed ?? []), \"--json\"],\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"argv.js","sourceRoot":"","sources":["../../../src/core/mcp/argv.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * How one MCP tool call's JSON arguments become an argv the CLI already\n * trusts — the boundary where an argument value could otherwise smuggle a\n * flag. The tool TABLE (what the tools are) lives in `./tools.ts`; this\n * module owns the crossing, and the inputSchema that declares it: the schema\n * a client validates against and the argv builder that refuses what it\n * forbids are one contract written twice, so they sit in one module.\n */\nimport type { McpTool } from \"./tools.js\";\n\n/**\n * The MCP inputSchema for one tool: a closed object, so a typo'd argument\n * refuses instead of vanishing, with the required set and the positional order\n * `toArgv` enforces declared beside it.\n */\nexport function toInputSchema(tool: McpTool): Record<string, unknown> {\n const properties: Record<string, unknown> = {};\n for (const positional of tool.positionals) {\n properties[positional.property] = positional.variadic\n ? { type: \"array\", items: { type: \"string\" }, description: positional.description }\n : { type: \"string\", description: positional.description };\n }\n for (const flag of tool.flags) {\n properties[flag.property] =\n flag.kind === \"boolean\"\n ? { type: \"boolean\", description: flag.description }\n : flag.kind === \"strings\"\n ? { type: \"array\", items: { type: \"string\" }, description: flag.description }\n : { type: \"string\", description: flag.description };\n }\n const required = [\n ...tool.positionals.filter((p) => p.required).map((p) => p.property),\n ...tool.flags.filter((f) => f.required === true).map((f) => f.property),\n ];\n return {\n type: \"object\",\n properties,\n ...(required.length > 0 ? { required } : {}),\n ...orderRule(tool),\n additionalProperties: false,\n };\n}\n\n/**\n * The rule `positionalTokens` enforces, declared: a positional after an\n * optional one needs it, because positionals reach argv by position and the\n * later value would otherwise be read in the earlier one's slot. Without it a\n * client that validated `{name: \"smoke\"}` for `loam_list` against the schema\n * it was given passed, and was then refused `-32602` by the server.\n *\n * Read off the declaration order rather than spelled per tool, so a tool that\n * gains a positional after an optional one is declared the day it is refused.\n * Each later positional needs every earlier OPTIONAL one; an earlier required\n * one is in `required` already.\n *\n * Two keywords, because presence means two things. A scalar that is present\n * always yields a token, so presence is the whole condition and\n * `dependentRequired` says exactly that. A variadic yields tokens only when it\n * has members: an empty one moves nothing into anybody's slot, and\n * `positionalTokens` serves it — `{args: []}` with no `workflow` is the\n * workflow list. `dependentRequired` there would forbid a call the server\n * answers, the same disagreement turned the other way, so the variadic's rule\n * sits under `dependentSchemas`: once it is present, either the earlier ones\n * are too or it is empty. `anyOf` rather than `if`/`then`, which says the same\n * but puts a `then` key on an object, and a thenable literal is one `await`\n * away from being called.\n */\nfunction orderRule(tool: McpTool): Record<string, unknown> {\n const dependentRequired: Record<string, string[]> = {};\n const dependentSchemas: Record<string, unknown> = {};\n const earlier: string[] = [];\n for (const positional of tool.positionals) {\n if (earlier.length > 0 && positional.variadic) {\n dependentSchemas[positional.property] = {\n anyOf: [{ required: [...earlier] }, { properties: { [positional.property]: { maxItems: 0 } } }],\n };\n } else if (earlier.length > 0) {\n dependentRequired[positional.property] = [...earlier];\n }\n if (!positional.required) earlier.push(positional.property);\n }\n return {\n ...(Object.keys(dependentRequired).length > 0 ? { dependentRequired } : {}),\n ...(Object.keys(dependentSchemas).length > 0 ? { dependentSchemas } : {}),\n };\n}\n\n/** One checked step of argv construction: the tokens it yields, or the invalid-params problem. */\ntype Checked =\n | { readonly ok: true; readonly tokens: readonly string[] }\n | { readonly ok: false; readonly problem: string };\n\nexport type ArgvOutcome =\n | { readonly ok: true; readonly argv: readonly string[] }\n | { readonly ok: false; readonly problem: string };\n\n/**\n * A string value that argv would read as a flag is refused before any command\n * sees it: {\"target\": \"--type\"} must not become a parsed option. Commander\n * cannot make this distinction after the fact — by then the token IS a flag —\n * so the boundary is here, on every string that crosses, array members\n * included. No shell is ever involved, so this is the only smuggling path.\n */\nfunction smuggled(property: string, value: string): string | null {\n return value.startsWith(\"-\")\n ? `argument '${property}' value '${value}' begins with '-' — a value argv would read as a flag is refused`\n : null;\n}\n\n/** One string value checked for type and smuggling. */\nfunction checkedString(property: string, value: unknown): Checked {\n if (typeof value !== \"string\") return { ok: false, problem: `argument '${property}' must be a string` };\n const bad = smuggled(property, value);\n return bad === null ? { ok: true, tokens: [value] } : { ok: false, problem: bad };\n}\n\n/** An array value checked member by member — rebuilt, not cast, so the claim is earned. */\nfunction checkedStrings(property: string, value: unknown): Checked {\n if (!Array.isArray(value)) return { ok: false, problem: `argument '${property}' must be an array of strings` };\n const tokens: string[] = [];\n for (const entry of value) {\n const checked = checkedString(property, entry);\n if (!checked.ok) return checked;\n tokens.push(...checked.tokens);\n }\n return { ok: true, tokens };\n}\n\n/**\n * The positionals, in declaration order — and refused when one would land in\n * another's slot.\n *\n * A positional reaches argv by POSITION, so an optional one left out leaves no\n * gap: whatever follows it moves up into its place. `loam_list` with `{name:\n * \"services\"}` built `list services` and answered ok:true with the services\n * listing, a wrong answer with nothing in it saying so; `loam_instructions`\n * with `{args: [...]}` and no `workflow` read the first arg as the workflow.\n * Refused here instead, as the invalid-params problem a missing REQUIRED\n * positional already is, naming the slot the caller left empty. A later\n * positional that yields no token — an empty variadic — moves nothing up, so\n * it passes. `orderRule` above declares the same rule in the advertised\n * schema; the two change together.\n */\nfunction positionalTokens(tool: McpTool, args: Record<string, unknown>): Checked {\n const tokens: string[] = [];\n let absent: string | undefined;\n for (const positional of tool.positionals) {\n const value = args[positional.property];\n if (value === undefined) {\n if (positional.required) return { ok: false, problem: `required argument '${positional.property}' is missing` };\n absent ??= positional.property;\n continue;\n }\n const checked = positional.variadic\n ? checkedStrings(positional.property, value)\n : checkedString(positional.property, value);\n if (!checked.ok) return checked;\n if (absent !== undefined && checked.tokens.length > 0) {\n return {\n ok: false,\n problem:\n `argument '${positional.property}' requires '${absent}' — positionals are read in order, ` +\n `so without '${absent}' this value would be read as '${absent}'`,\n };\n }\n tokens.push(...checked.tokens);\n }\n return { ok: true, tokens };\n}\n\nfunction flagTokens(tool: McpTool, args: Record<string, unknown>): Checked {\n const tokens: string[] = [];\n for (const flag of tool.flags) {\n const value = args[flag.property];\n if (value === undefined) {\n // The same refusal absent required positionals get: the client learns\n // \"-32602 missing argument\", not an invalid-option envelope after the\n // command already refused.\n if (flag.required === true) return { ok: false, problem: `required argument '${flag.property}' is missing` };\n continue;\n }\n if (flag.kind === \"boolean\") {\n if (typeof value !== \"boolean\") return { ok: false, problem: `argument '${flag.property}' must be a boolean` };\n if (value) tokens.push(flag.flag);\n continue;\n }\n const checked = flag.kind === \"strings\"\n ? checkedStrings(flag.property, value)\n : checkedString(flag.property, value);\n if (!checked.ok) return checked;\n // A repeatable flag repeats per value; a single-valued one appears once.\n for (const token of checked.tokens) tokens.push(flag.flag, token);\n }\n return { ok: true, tokens };\n}\n\n/**\n * The argv for one tool call — or the invalid-params problem that stops it.\n * `--json` is always appended: the envelope is the tool-result contract, and\n * a client cannot opt out of it into the human rendering. Unknown argument\n * names refuse rather than vanish — the schema says additionalProperties\n * false, and a server that enforced that only in the schema would silently\n * drop the typo'd argument of any client that skipped validation.\n */\nexport function toArgv(tool: McpTool, args: Record<string, unknown>): ArgvOutcome {\n const known = new Set([\n ...tool.positionals.map((p) => p.property),\n ...tool.flags.map((f) => f.property),\n ]);\n for (const property of Object.keys(args)) {\n if (!known.has(property)) {\n const allowed = known.size === 0 ? \"no arguments\" : [...known].join(\", \");\n return { ok: false, problem: `unknown argument '${property}' for ${tool.name} — expected ${allowed}` };\n }\n }\n const positionals = positionalTokens(tool, args);\n if (!positionals.ok) return { ok: false, problem: positionals.problem };\n const flags = flagTokens(tool, args);\n if (!flags.ok) return { ok: false, problem: flags.problem };\n return {\n ok: true,\n argv: [tool.command, ...positionals.tokens, ...flags.tokens, ...(tool.fixed ?? []), \"--json\"],\n };\n}\n"]}
|
|
@@ -13,7 +13,7 @@ export const MCP_AUTHOR_TOOLS = [
|
|
|
13
13
|
name: "loam_new",
|
|
14
14
|
command: "new",
|
|
15
15
|
description: "Scaffold a feature in the docs repo. Opt-in writer; refuses an existing feature. " +
|
|
16
|
-
"CLI equivalent: loam new <featureId> [--title <text>] [--touches <id>...] [--new-service <id>...] [--capability <id>...] --json",
|
|
16
|
+
"CLI equivalent: loam new <featureId> [--title <text>] [--touches <id>...] [--new-service <id>...] [--capability <id>...] [--modifies <Req-ID>...] --json",
|
|
17
17
|
positionals: [
|
|
18
18
|
{ property: "featureId", required: true, variadic: false, description: "new feature id, e.g. FEAT-101" },
|
|
19
19
|
],
|
|
@@ -22,6 +22,7 @@ export const MCP_AUTHOR_TOOLS = [
|
|
|
22
22
|
{ property: "touches", flag: "--touches", kind: "strings", description: "existing services changed; repeatable" },
|
|
23
23
|
{ property: "newServices", flag: "--new-service", kind: "strings", description: "services introduced; repeatable" },
|
|
24
24
|
{ property: "capabilities", flag: "--capability", kind: "strings", description: "business capabilities changed; repeatable" },
|
|
25
|
+
{ property: "modifies", flag: "--modifies", kind: "strings", description: "living Requirement-IDs of touched services this feature rewrites, scaffolded as pinned MODIFIED blocks; repeatable" },
|
|
25
26
|
],
|
|
26
27
|
annotations: WRITE_ANNOTATIONS,
|
|
27
28
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"author-tools.js","sourceRoot":"","sources":["../../../src/core/mcp/author-tools.ts"],"names":[],"mappings":"AAGA,MAAM,iBAAiB,GAAG;IACxB,YAAY,EAAE,KAAK;IACnB,eAAe,EAAE,KAAK;IACtB,cAAc,EAAE,KAAK;IACrB,aAAa,EAAE,KAAK;CACZ,CAAC;AAEX,MAAM,sBAAsB,GAAG;IAC7B,GAAG,iBAAiB;IACpB,cAAc,EAAE,IAAI;CACZ,CAAC;AAEX,MAAM,CAAC,MAAM,gBAAgB,GAAuB;IAClD;QACE,IAAI,EAAE,UAAU;QAChB,OAAO,EAAE,KAAK;QACd,WAAW,EACT,mFAAmF;YACnF,
|
|
1
|
+
{"version":3,"file":"author-tools.js","sourceRoot":"","sources":["../../../src/core/mcp/author-tools.ts"],"names":[],"mappings":"AAGA,MAAM,iBAAiB,GAAG;IACxB,YAAY,EAAE,KAAK;IACnB,eAAe,EAAE,KAAK;IACtB,cAAc,EAAE,KAAK;IACrB,aAAa,EAAE,KAAK;CACZ,CAAC;AAEX,MAAM,sBAAsB,GAAG;IAC7B,GAAG,iBAAiB;IACpB,cAAc,EAAE,IAAI;CACZ,CAAC;AAEX,MAAM,CAAC,MAAM,gBAAgB,GAAuB;IAClD;QACE,IAAI,EAAE,UAAU;QAChB,OAAO,EAAE,KAAK;QACd,WAAW,EACT,mFAAmF;YACnF,0JAA0J;QAC5J,WAAW,EAAE;YACX,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,+BAA+B,EAAE;SACzG;QACD,KAAK,EAAE;YACL,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,gCAAgC,EAAE;YACrG,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,uCAAuC,EAAE;YACjH,EAAE,QAAQ,EAAE,aAAa,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,iCAAiC,EAAE;YACnH,EAAE,QAAQ,EAAE,cAAc,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,2CAA2C,EAAE;YAC7H,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,oHAAoH,EAAE;SACjM;QACD,WAAW,EAAE,iBAAiB;KAC/B;IACD;QACE,IAAI,EAAE,aAAa;QACnB,OAAO,EAAE,QAAQ;QACjB,WAAW,EACT,4GAA4G;YAC5G,iEAAiE;QACnE,yEAAyE;QACzE,sEAAsE;QACtE,2EAA2E;QAC3E,0EAA0E;QAC1E,gDAAgD;QAChD,WAAW,EAAE;YACX,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,0CAA0C,EAAE;SACrH;QACD,KAAK,EAAE;YACL,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,iCAAiC,EAAE;SAC3G;QACD,WAAW,EAAE,sBAAsB;KACpC;IACD;QACE,IAAI,EAAE,cAAc;QACpB,OAAO,EAAE,SAAS;QAClB,WAAW,EACT,2HAA2H;YAC3H,kEAAkE;QACpE,WAAW,EAAE;YACX,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,uCAAuC,EAAE;SAClH;QACD,KAAK,EAAE;YACL,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,qBAAqB,EAAE;SAC/F;QACD,WAAW,EAAE,sBAAsB;KACpC;IACD;QACE,IAAI,EAAE,mBAAmB;QACzB,OAAO,EAAE,SAAS;QAClB,WAAW,EACT,oFAAoF;YACpF,2DAA2D;QAC7D,WAAW,EAAE;YACX,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,sBAAsB,EAAE;SAChG;QACD,KAAK,EAAE,EAAE;QACT,KAAK,EAAE,CAAC,WAAW,CAAC;QACpB,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE;KAC1D;CACF,CAAC","sourcesContent":["/** Opt-in MCP writers. Vouch and a committing archive are never exposed. */\nimport type { McpTool } from \"./tools.js\";\n\nconst WRITE_ANNOTATIONS = {\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: false,\n} as const;\n\nconst SAFE_WRITE_ANNOTATIONS = {\n ...WRITE_ANNOTATIONS,\n idempotentHint: true,\n} as const;\n\nexport const MCP_AUTHOR_TOOLS: readonly McpTool[] = [\n {\n name: \"loam_new\",\n command: \"new\",\n description:\n \"Scaffold a feature in the docs repo. Opt-in writer; refuses an existing feature. \" +\n \"CLI equivalent: loam new <featureId> [--title <text>] [--touches <id>...] [--new-service <id>...] [--capability <id>...] [--modifies <Req-ID>...] --json\",\n positionals: [\n { property: \"featureId\", required: true, variadic: false, description: \"new feature id, e.g. FEAT-101\" },\n ],\n flags: [\n { property: \"title\", flag: \"--title\", kind: \"string\", description: \"human title and directory slug\" },\n { property: \"touches\", flag: \"--touches\", kind: \"strings\", description: \"existing services changed; repeatable\" },\n { property: \"newServices\", flag: \"--new-service\", kind: \"strings\", description: \"services introduced; repeatable\" },\n { property: \"capabilities\", flag: \"--capability\", kind: \"strings\", description: \"business capabilities changed; repeatable\" },\n { property: \"modifies\", flag: \"--modifies\", kind: \"strings\", description: \"living Requirement-IDs of touched services this feature rewrites, scaffolded as pinned MODIFIED blocks; repeatable\" },\n ],\n annotations: WRITE_ANNOTATIONS,\n },\n {\n name: \"loam_rebase\",\n command: \"rebase\",\n description:\n \"Pin a feature delta to the living baselines after the agent has reconciled them. Opt-in journaled writer. \" +\n \"CLI equivalent: loam rebase [featureId] [--service <id>] --json\",\n // Optional because the REGISTERED argument is — `loam rebase` relaxed it\n // for `--living`, which names no feature — and the schema mirrors the\n // command it wraps rather than the subset this tool exposes. `--living` is\n // not offered here, so an omitted id reaches the CLI's own refusal, which\n // names the flag that makes the omission legal.\n positionals: [\n { property: \"featureId\", required: false, variadic: false, description: \"feature id whose baselines were reviewed\" },\n ],\n flags: [\n { property: \"service\", flag: \"--service\", kind: \"string\", description: \"restrict to one touched service\" },\n ],\n annotations: SAFE_WRITE_ANNOTATIONS,\n },\n {\n name: \"loam_gherkin\",\n command: \"gherkin\",\n description:\n \"Generate loam-owned Gherkin files in the bound service repository. Opt-in journaled writer; refuses ownership conflicts. \" +\n \"CLI equivalent: loam gherkin [featureId] [--service <id>] --json\",\n positionals: [\n { property: \"featureId\", required: false, variadic: false, description: \"feature id; omit for the living suite\" },\n ],\n flags: [\n { property: \"service\", flag: \"--service\", kind: \"string\", description: \"service to emit for\" },\n ],\n annotations: SAFE_WRITE_ANNOTATIONS,\n },\n {\n name: \"loam_archive_plan\",\n command: \"archive\",\n description:\n \"Compute the complete archive plan without writing. The server enforces --dry-run. \" +\n \"CLI equivalent: loam archive <featureId> --dry-run --json\",\n positionals: [\n { property: \"featureId\", required: true, variadic: false, description: \"feature id to review\" },\n ],\n flags: [],\n fixed: [\"--dry-run\"],\n annotations: { readOnlyHint: true, openWorldHint: false },\n },\n];\n"]}
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { isRecord } from "../kernel/records.js";
|
|
18
18
|
import { LOAM_VERSION } from "../envelope/version.js";
|
|
19
|
-
import { toArgv } from "./argv.js";
|
|
20
|
-
import { MCP_TOOLS, READ_ONLY_ANNOTATIONS,
|
|
19
|
+
import { toArgv, toInputSchema } from "./argv.js";
|
|
20
|
+
import { MCP_TOOLS, READ_ONLY_ANNOTATIONS, toolByName, } from "./tools.js";
|
|
21
21
|
import { AGENTS_MD } from "../agent/agents-md.js";
|
|
22
22
|
import { PROTOCOLS } from "../agent/protocol.js";
|
|
23
23
|
import { withoutFixTables } from "../explain/fix-tables.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../../src/core/mcp/protocol.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Pure JSON-RPC 2.0 + MCP routing for `loam mcp` — parse one inbound frame,\n * decide the reply. Computes and returns only: the stream loop, the stdout\n * writes and the dispatcher live in `src/commands/mcp/`, so every decision\n * here is unit-testable without a stream in sight.\n *\n * Spec revision assumed: MCP 2025-06-18 (framing note in `./framing.ts`).\n * The lifecycle is deliberately NOT policed: this server is a stateless\n * facade over a stateless CLI, its capabilities never vary, and refusing a\n * tools/call that arrives before `initialize` would protect nothing — so\n * every request is answered on its own terms, whatever came first. What IS\n * refused: JSON-RPC batch arrays (removed in 2025-06-18; the older revisions\n * this server also negotiates allowed them, and that gap is deliberate — a\n * clear -32600 with the reason beats a half-implemented batch path no real\n * MCP client exercises).\n */\nimport { isRecord } from \"../kernel/records.js\";\nimport { LOAM_VERSION } from \"../envelope/version.js\";\nimport { toArgv } from \"./argv.js\";\nimport {\n MCP_TOOLS,\n READ_ONLY_ANNOTATIONS,\n toInputSchema,\n toolByName,\n type McpTool,\n} from \"./tools.js\";\nimport { AGENTS_MD } from \"../agent/agents-md.js\";\nimport { PROTOCOLS } from \"../agent/protocol.js\";\nimport { withoutFixTables } from \"../explain/fix-tables.js\";\n\n/** JSON-RPC 2.0 error codes this server emits. */\nexport const PARSE_ERROR = -32700;\nexport const INVALID_REQUEST = -32600;\nexport const METHOD_NOT_FOUND = -32601;\nexport const INVALID_PARAMS = -32602;\nexport const INTERNAL_ERROR = -32603;\n\n/**\n * Newest first. `initialize` echoes the client's requested version when it is\n * one of these, else answers the newest — the negotiation rule the 2025-06-18\n * lifecycle spec states for servers. All three revisions are identical in\n * everything this facade implements (stdio framing, tools/list, tools/call\n * with text content) except batching, refused above.\n */\nexport const SUPPORTED_PROTOCOL_VERSIONS = [\"2025-06-18\", \"2025-03-26\", \"2024-11-05\"] as const;\nexport const LATEST_PROTOCOL_VERSION = SUPPORTED_PROTOCOL_VERSIONS[0];\n\nconst TOOL_OUTPUT_SCHEMA = {\n type: \"object\",\n required: [\"contractVersion\", \"version\", \"ok\"],\n properties: {\n contractVersion: { const: \"1.0\" },\n version: { type: \"string\" },\n ok: { type: \"boolean\" },\n error: {\n type: \"object\",\n properties: { code: { type: \"string\" }, message: { type: \"string\" } },\n required: [\"code\", \"message\"],\n additionalProperties: true,\n },\n },\n additionalProperties: true,\n} as const;\n\nconst RESOURCE_TEXT: Readonly<Record<string, string>> = {\n \"loam://orientation\": AGENTS_MD,\n ...Object.fromEntries(\n Object.entries(PROTOCOLS).map(([name, body]) => [`loam://instructions/${name}`, body]),\n ),\n \"loam://instructions/loam-check/compact\": withoutFixTables(PROTOCOLS[\"loam-check\"]!),\n};\n\n/** A response id: echoed from the request, or null when no id could be read (parse errors). */\nexport type JsonRpcId = string | number;\n\nexport interface JsonRpcResponse extends Record<string, unknown> {\n readonly jsonrpc: \"2.0\";\n readonly id: JsonRpcId | null;\n}\n\nexport function resultResponse(id: JsonRpcId | null, result: unknown): JsonRpcResponse {\n return { jsonrpc: \"2.0\", id, result };\n}\n\n/**\n * `rpcCode`, not `code`: the stable-code drift collector\n * (test/helpers/stable-codes.ts) treats every function with a parameter named\n * `code` as an emitter of loam's stable code STRINGS and demands a literal at\n * each call site. A JSON-RPC numeric code is a different vocabulary — naming\n * the parameter into that scan would make the guard demand string literals\n * where the protocol requires numbers.\n */\nexport function errorResponse(id: JsonRpcId | null, rpcCode: number, message: string): JsonRpcResponse {\n return { jsonrpc: \"2.0\", id, error: { code: rpcCode, message } };\n}\n\n/**\n * What one inbound frame asks of the serve loop:\n * - `ignore`: a blank line, or a notification — nothing goes back.\n * - `reply`: answered here, in pure code; write the response.\n * - `call`: a validated tools/call — run `argv` through the dispatcher and\n * wrap what comes back. The argv is fully built here so the loop never\n * interprets arguments.\n */\nexport type RouteOutcome =\n | { readonly kind: \"ignore\" }\n | { readonly kind: \"reply\"; readonly response: JsonRpcResponse }\n | { readonly kind: \"call\"; readonly id: JsonRpcId; readonly toolName: string; readonly argv: readonly string[] };\n\nconst reply = (response: JsonRpcResponse): RouteOutcome => ({ kind: \"reply\", response });\n\n/** One line off the wire → what to do about it. Total: every input maps to an outcome, nothing throws. */\nexport function routeLine(line: string, tools: readonly McpTool[] = MCP_TOOLS): RouteOutcome {\n if (line.trim() === \"\") return { kind: \"ignore\" };\n let message: unknown;\n try {\n message = JSON.parse(line);\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n return reply(errorResponse(null, PARSE_ERROR, `Parse error: ${detail}`));\n }\n if (Array.isArray(message)) {\n // JSON-RPC batching was removed in MCP 2025-06-18; see the module comment\n // for why the older negotiated revisions refuse it too instead of half-supporting it.\n return reply(errorResponse(null, INVALID_REQUEST, \"Invalid Request: JSON-RPC batch arrays are not supported\"));\n }\n if (!isRecord(message)) {\n return reply(errorResponse(null, INVALID_REQUEST, \"Invalid Request: a message must be a JSON object\"));\n }\n const rawId = message[\"id\"];\n // Three id states, kept apart because they demand opposite answers. ABSENT\n // (or an explicit null, reserved for unanswerable errors) is a notification.\n // A PRESENT id that is not a string or a finite number — an object, a bool,\n // an array, a JSON `1e999` that parsed to Infinity — is a malformed REQUEST:\n // the client is waiting on a reply, and folding it into the notification\n // branch answers with silence, which for a request is a hang, not an error.\n const validId = typeof rawId === \"string\" || (typeof rawId === \"number\" && Number.isFinite(rawId)) ? rawId : null;\n if (message[\"jsonrpc\"] !== \"2.0\") {\n return reply(errorResponse(validId, INVALID_REQUEST, \"Invalid Request: jsonrpc must be the string \\\"2.0\\\"\"));\n }\n const method = message[\"method\"];\n if (typeof method !== \"string\") {\n return reply(errorResponse(validId, INVALID_REQUEST, \"Invalid Request: method must be a string\"));\n }\n if (!(\"id\" in message) || rawId === null) return { kind: \"ignore\" };\n if (validId === null) {\n // id null in the response: the request's own id is unusable, and null is\n // exactly the id JSON-RPC reserves for that answer.\n return reply(errorResponse(null, INVALID_REQUEST, \"Invalid Request: id must be a string or a finite number\"));\n }\n return routeRequest(validId, method, message[\"params\"], tools);\n}\n\nfunction routeRequest(\n id: JsonRpcId,\n method: string,\n params: unknown,\n tools: readonly McpTool[],\n): RouteOutcome {\n switch (method) {\n case \"initialize\":\n return reply(resultResponse(id, initializeResult(params)));\n case \"ping\":\n return reply(resultResponse(id, {}));\n case \"tools/list\":\n // No pagination: the whole tool table fits in one page, so `nextCursor`\n // is omitted rather than emitted empty — the spec reads an absent\n // cursor as \"done\".\n return reply(resultResponse(id, {\n tools: tools.map((tool) => ({\n name: tool.name,\n description: tool.description,\n inputSchema: toInputSchema(tool),\n outputSchema: TOOL_OUTPUT_SCHEMA,\n // Base tools share the read-only default; opt-in authoring tools\n // carry their own mutation annotations in author-tools.ts.\n annotations: tool.annotations ?? READ_ONLY_ANNOTATIONS,\n })),\n }));\n case \"resources/list\":\n return reply(resultResponse(id, {\n resources: Object.keys(RESOURCE_TEXT).map((uri) => ({\n uri,\n name: uri === \"loam://orientation\" ? \"loam orientation\" : uri.slice(\"loam://instructions/\".length),\n description:\n uri === \"loam://orientation\"\n ? \"Small always-loaded map of a loam repository\"\n : \"Version-matched loam workflow, support protocol or reference page\",\n mimeType: \"text/markdown\",\n })),\n }));\n case \"resources/read\":\n return routeResourceRead(id, params);\n case \"tools/call\":\n return routeToolCall(id, params, tools);\n default:\n // Everything else — including a literal `shutdown`, which is LSP's\n // lifecycle and not MCP's (the MCP stdio lifecycle ends at stdin EOF).\n return reply(errorResponse(id, METHOD_NOT_FOUND, `Method not found: ${method}`));\n }\n}\n\nfunction routeResourceRead(id: JsonRpcId, params: unknown): RouteOutcome {\n if (!isRecord(params) || typeof params[\"uri\"] !== \"string\") {\n return reply(errorResponse(id, INVALID_PARAMS, \"resources/read requires params.uri\"));\n }\n const uri = params[\"uri\"];\n const text = RESOURCE_TEXT[uri];\n if (text === undefined) {\n return reply(errorResponse(id, INVALID_PARAMS, `Unknown resource '${uri}'`));\n }\n return reply(resultResponse(id, { contents: [{ uri, mimeType: \"text/markdown\", text }] }));\n}\n\nfunction routeToolCall(\n id: JsonRpcId,\n params: unknown,\n tools: readonly McpTool[],\n): RouteOutcome {\n if (!isRecord(params) || typeof params[\"name\"] !== \"string\") {\n return reply(errorResponse(id, INVALID_PARAMS, \"tools/call requires params.name naming a tool\"));\n }\n const tool = toolByName(params[\"name\"], tools);\n if (tool === undefined) {\n const names = tools.map((t) => t.name).join(\", \");\n return reply(errorResponse(id, INVALID_PARAMS, `Unknown tool '${params[\"name\"]}' — this server offers: ${names}`));\n }\n const rawArgs = params[\"arguments\"] ?? {};\n if (!isRecord(rawArgs)) {\n return reply(errorResponse(id, INVALID_PARAMS, \"tools/call params.arguments must be an object when present\"));\n }\n const built = toArgv(tool, rawArgs);\n if (!built.ok) return reply(errorResponse(id, INVALID_PARAMS, built.problem));\n return { kind: \"call\", id, toolName: tool.name, argv: built.argv };\n}\n\n/**\n * The `initialize` result. The version rule: echo the requested revision when\n * this server supports it, otherwise answer the newest it does — the client\n * then decides whether it can live with that (2025-06-18 lifecycle,\n * \"Version Negotiation\").\n */\nexport function initializeResult(params: unknown): Record<string, unknown> {\n const requested = isRecord(params) ? params[\"protocolVersion\"] : undefined;\n const negotiated =\n typeof requested === \"string\" && (SUPPORTED_PROTOCOL_VERSIONS as readonly string[]).includes(requested)\n ? requested\n : LATEST_PROTOCOL_VERSION;\n return {\n protocolVersion: negotiated,\n capabilities: {\n // The sets never change while the server lives, so no\n // listChanged notification is declared — announcing one and never\n // sending it would be a promise, not a capability.\n tools: {},\n resources: {},\n },\n serverInfo: { name: \"loam\", version: LOAM_VERSION },\n instructions:\n \"Every tool result is a loam --json envelope (contractVersion 1.0): branch on ok, \" +\n \"and on error.code when ok is false — the same machine contract the CLI prints.\",\n };\n}\n\n/** What the dispatcher hands back from one tool call, before it becomes a protocol reply. */\nexport interface ToolRunOutcome {\n readonly stdout: string;\n readonly stderr: string;\n readonly code: number;\n}\n\nexport type ToolReply =\n | { readonly kind: \"result\"; readonly result: Record<string, unknown> }\n | { readonly kind: \"error\"; readonly code: number; readonly message: string };\n\n/**\n * One tool run wrapped as an MCP tool result. The envelope rides VERBATIM in\n * content[0].text — one machine contract, not two — and again parsed as\n * structuredContent for clients that read that field.\n *\n * `isError` follows the ENVELOPE's `ok`, and the exit code only when no\n * envelope could be parsed. The two disagree on purpose for half the tool\n * set: validate, doctor, gate, context and delta all have graded answers\n * that exit 1 while reporting `ok: true` — an invalid fleet, a blocked\n * doctor, a `verdict: \"fail\"` deploy gate. Those exits are CI levers, not\n * call failures, and an MCP host renders `isError: true` as \"the call\n * failed\" and may discard structuredContent — which for loam_gate would\n * discard the exact verdict the caller asked for. `ok: false` is the one\n * signal that means \"loam refused to answer\", so it is the one isError\n * mirrors; the exit-code fallback exists only for stdout that parsed as no\n * envelope at all.\n *\n * An EMPTY stdout is a protocol error, not a quiet result: with `--json` on\n * every argv, a command that printed no envelope is a command whose answer\n * was lost, and \"nobody could look\" must stay distinguishable from any\n * genuine envelope — even a refusing one.\n */\nexport function toolReply(run: ToolRunOutcome): ToolReply {\n if (run.stdout.trim() === \"\") {\n const hint = run.stderr.trim() === \"\" ? \"\" : ` stderr: ${run.stderr.trim()}`;\n return {\n kind: \"error\",\n code: INTERNAL_ERROR,\n message: `the command exited ${run.code} without printing a --json envelope, so there is no result to return.${hint}`,\n };\n }\n let structured: Record<string, unknown> | undefined;\n try {\n const parsed: unknown = JSON.parse(run.stdout);\n // structuredContent must be an object; the envelope always is.\n structured = isRecord(parsed) ? parsed : undefined;\n } catch {\n // The fallback below handles both invalid JSON and valid non-object JSON.\n }\n if (structured === undefined) {\n // Preserve the bytes in content, but keep the declared output contract:\n // an MCP client should never have to branch on the absence of\n // structuredContent after tools/list promised a schema.\n structured = {\n contractVersion: \"1.0\",\n version: LOAM_VERSION,\n ok: false,\n error: {\n code: \"internal\",\n message: \"the command did not print a JSON object envelope\",\n },\n raw: run.stdout,\n };\n }\n const envelopeOk = structured?.[\"ok\"];\n return {\n kind: \"result\",\n result: {\n content: [{ type: \"text\", text: run.stdout }],\n structuredContent: structured,\n isError: typeof envelopeOk === \"boolean\" ? !envelopeOk : run.code !== 0,\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../../src/core/mcp/protocol.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Pure JSON-RPC 2.0 + MCP routing for `loam mcp` — parse one inbound frame,\n * decide the reply. Computes and returns only: the stream loop, the stdout\n * writes and the dispatcher live in `src/commands/mcp/`, so every decision\n * here is unit-testable without a stream in sight.\n *\n * Spec revision assumed: MCP 2025-06-18 (framing note in `./framing.ts`).\n * The lifecycle is deliberately NOT policed: this server is a stateless\n * facade over a stateless CLI, its capabilities never vary, and refusing a\n * tools/call that arrives before `initialize` would protect nothing — so\n * every request is answered on its own terms, whatever came first. What IS\n * refused: JSON-RPC batch arrays (removed in 2025-06-18; the older revisions\n * this server also negotiates allowed them, and that gap is deliberate — a\n * clear -32600 with the reason beats a half-implemented batch path no real\n * MCP client exercises).\n */\nimport { isRecord } from \"../kernel/records.js\";\nimport { LOAM_VERSION } from \"../envelope/version.js\";\nimport { toArgv, toInputSchema } from \"./argv.js\";\nimport {\n MCP_TOOLS,\n READ_ONLY_ANNOTATIONS,\n toolByName,\n type McpTool,\n} from \"./tools.js\";\nimport { AGENTS_MD } from \"../agent/agents-md.js\";\nimport { PROTOCOLS } from \"../agent/protocol.js\";\nimport { withoutFixTables } from \"../explain/fix-tables.js\";\n\n/** JSON-RPC 2.0 error codes this server emits. */\nexport const PARSE_ERROR = -32700;\nexport const INVALID_REQUEST = -32600;\nexport const METHOD_NOT_FOUND = -32601;\nexport const INVALID_PARAMS = -32602;\nexport const INTERNAL_ERROR = -32603;\n\n/**\n * Newest first. `initialize` echoes the client's requested version when it is\n * one of these, else answers the newest — the negotiation rule the 2025-06-18\n * lifecycle spec states for servers. All three revisions are identical in\n * everything this facade implements (stdio framing, tools/list, tools/call\n * with text content) except batching, refused above.\n */\nexport const SUPPORTED_PROTOCOL_VERSIONS = [\"2025-06-18\", \"2025-03-26\", \"2024-11-05\"] as const;\nexport const LATEST_PROTOCOL_VERSION = SUPPORTED_PROTOCOL_VERSIONS[0];\n\nconst TOOL_OUTPUT_SCHEMA = {\n type: \"object\",\n required: [\"contractVersion\", \"version\", \"ok\"],\n properties: {\n contractVersion: { const: \"1.0\" },\n version: { type: \"string\" },\n ok: { type: \"boolean\" },\n error: {\n type: \"object\",\n properties: { code: { type: \"string\" }, message: { type: \"string\" } },\n required: [\"code\", \"message\"],\n additionalProperties: true,\n },\n },\n additionalProperties: true,\n} as const;\n\nconst RESOURCE_TEXT: Readonly<Record<string, string>> = {\n \"loam://orientation\": AGENTS_MD,\n ...Object.fromEntries(\n Object.entries(PROTOCOLS).map(([name, body]) => [`loam://instructions/${name}`, body]),\n ),\n \"loam://instructions/loam-check/compact\": withoutFixTables(PROTOCOLS[\"loam-check\"]!),\n};\n\n/** A response id: echoed from the request, or null when no id could be read (parse errors). */\nexport type JsonRpcId = string | number;\n\nexport interface JsonRpcResponse extends Record<string, unknown> {\n readonly jsonrpc: \"2.0\";\n readonly id: JsonRpcId | null;\n}\n\nexport function resultResponse(id: JsonRpcId | null, result: unknown): JsonRpcResponse {\n return { jsonrpc: \"2.0\", id, result };\n}\n\n/**\n * `rpcCode`, not `code`: the stable-code drift collector\n * (test/helpers/stable-codes.ts) treats every function with a parameter named\n * `code` as an emitter of loam's stable code STRINGS and demands a literal at\n * each call site. A JSON-RPC numeric code is a different vocabulary — naming\n * the parameter into that scan would make the guard demand string literals\n * where the protocol requires numbers.\n */\nexport function errorResponse(id: JsonRpcId | null, rpcCode: number, message: string): JsonRpcResponse {\n return { jsonrpc: \"2.0\", id, error: { code: rpcCode, message } };\n}\n\n/**\n * What one inbound frame asks of the serve loop:\n * - `ignore`: a blank line, or a notification — nothing goes back.\n * - `reply`: answered here, in pure code; write the response.\n * - `call`: a validated tools/call — run `argv` through the dispatcher and\n * wrap what comes back. The argv is fully built here so the loop never\n * interprets arguments.\n */\nexport type RouteOutcome =\n | { readonly kind: \"ignore\" }\n | { readonly kind: \"reply\"; readonly response: JsonRpcResponse }\n | { readonly kind: \"call\"; readonly id: JsonRpcId; readonly toolName: string; readonly argv: readonly string[] };\n\nconst reply = (response: JsonRpcResponse): RouteOutcome => ({ kind: \"reply\", response });\n\n/** One line off the wire → what to do about it. Total: every input maps to an outcome, nothing throws. */\nexport function routeLine(line: string, tools: readonly McpTool[] = MCP_TOOLS): RouteOutcome {\n if (line.trim() === \"\") return { kind: \"ignore\" };\n let message: unknown;\n try {\n message = JSON.parse(line);\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n return reply(errorResponse(null, PARSE_ERROR, `Parse error: ${detail}`));\n }\n if (Array.isArray(message)) {\n // JSON-RPC batching was removed in MCP 2025-06-18; see the module comment\n // for why the older negotiated revisions refuse it too instead of half-supporting it.\n return reply(errorResponse(null, INVALID_REQUEST, \"Invalid Request: JSON-RPC batch arrays are not supported\"));\n }\n if (!isRecord(message)) {\n return reply(errorResponse(null, INVALID_REQUEST, \"Invalid Request: a message must be a JSON object\"));\n }\n const rawId = message[\"id\"];\n // Three id states, kept apart because they demand opposite answers. ABSENT\n // (or an explicit null, reserved for unanswerable errors) is a notification.\n // A PRESENT id that is not a string or a finite number — an object, a bool,\n // an array, a JSON `1e999` that parsed to Infinity — is a malformed REQUEST:\n // the client is waiting on a reply, and folding it into the notification\n // branch answers with silence, which for a request is a hang, not an error.\n const validId = typeof rawId === \"string\" || (typeof rawId === \"number\" && Number.isFinite(rawId)) ? rawId : null;\n if (message[\"jsonrpc\"] !== \"2.0\") {\n return reply(errorResponse(validId, INVALID_REQUEST, \"Invalid Request: jsonrpc must be the string \\\"2.0\\\"\"));\n }\n const method = message[\"method\"];\n if (typeof method !== \"string\") {\n return reply(errorResponse(validId, INVALID_REQUEST, \"Invalid Request: method must be a string\"));\n }\n if (!(\"id\" in message) || rawId === null) return { kind: \"ignore\" };\n if (validId === null) {\n // id null in the response: the request's own id is unusable, and null is\n // exactly the id JSON-RPC reserves for that answer.\n return reply(errorResponse(null, INVALID_REQUEST, \"Invalid Request: id must be a string or a finite number\"));\n }\n return routeRequest(validId, method, message[\"params\"], tools);\n}\n\nfunction routeRequest(\n id: JsonRpcId,\n method: string,\n params: unknown,\n tools: readonly McpTool[],\n): RouteOutcome {\n switch (method) {\n case \"initialize\":\n return reply(resultResponse(id, initializeResult(params)));\n case \"ping\":\n return reply(resultResponse(id, {}));\n case \"tools/list\":\n // No pagination: the whole tool table fits in one page, so `nextCursor`\n // is omitted rather than emitted empty — the spec reads an absent\n // cursor as \"done\".\n return reply(resultResponse(id, {\n tools: tools.map((tool) => ({\n name: tool.name,\n description: tool.description,\n inputSchema: toInputSchema(tool),\n outputSchema: TOOL_OUTPUT_SCHEMA,\n // Base tools share the read-only default; opt-in authoring tools\n // carry their own mutation annotations in author-tools.ts.\n annotations: tool.annotations ?? READ_ONLY_ANNOTATIONS,\n })),\n }));\n case \"resources/list\":\n return reply(resultResponse(id, {\n resources: Object.keys(RESOURCE_TEXT).map((uri) => ({\n uri,\n name: uri === \"loam://orientation\" ? \"loam orientation\" : uri.slice(\"loam://instructions/\".length),\n description:\n uri === \"loam://orientation\"\n ? \"Small always-loaded map of a loam repository\"\n : \"Version-matched loam workflow, support protocol or reference page\",\n mimeType: \"text/markdown\",\n })),\n }));\n case \"resources/read\":\n return routeResourceRead(id, params);\n case \"tools/call\":\n return routeToolCall(id, params, tools);\n default:\n // Everything else — including a literal `shutdown`, which is LSP's\n // lifecycle and not MCP's (the MCP stdio lifecycle ends at stdin EOF).\n return reply(errorResponse(id, METHOD_NOT_FOUND, `Method not found: ${method}`));\n }\n}\n\nfunction routeResourceRead(id: JsonRpcId, params: unknown): RouteOutcome {\n if (!isRecord(params) || typeof params[\"uri\"] !== \"string\") {\n return reply(errorResponse(id, INVALID_PARAMS, \"resources/read requires params.uri\"));\n }\n const uri = params[\"uri\"];\n const text = RESOURCE_TEXT[uri];\n if (text === undefined) {\n return reply(errorResponse(id, INVALID_PARAMS, `Unknown resource '${uri}'`));\n }\n return reply(resultResponse(id, { contents: [{ uri, mimeType: \"text/markdown\", text }] }));\n}\n\nfunction routeToolCall(\n id: JsonRpcId,\n params: unknown,\n tools: readonly McpTool[],\n): RouteOutcome {\n if (!isRecord(params) || typeof params[\"name\"] !== \"string\") {\n return reply(errorResponse(id, INVALID_PARAMS, \"tools/call requires params.name naming a tool\"));\n }\n const tool = toolByName(params[\"name\"], tools);\n if (tool === undefined) {\n const names = tools.map((t) => t.name).join(\", \");\n return reply(errorResponse(id, INVALID_PARAMS, `Unknown tool '${params[\"name\"]}' — this server offers: ${names}`));\n }\n const rawArgs = params[\"arguments\"] ?? {};\n if (!isRecord(rawArgs)) {\n return reply(errorResponse(id, INVALID_PARAMS, \"tools/call params.arguments must be an object when present\"));\n }\n const built = toArgv(tool, rawArgs);\n if (!built.ok) return reply(errorResponse(id, INVALID_PARAMS, built.problem));\n return { kind: \"call\", id, toolName: tool.name, argv: built.argv };\n}\n\n/**\n * The `initialize` result. The version rule: echo the requested revision when\n * this server supports it, otherwise answer the newest it does — the client\n * then decides whether it can live with that (2025-06-18 lifecycle,\n * \"Version Negotiation\").\n */\nexport function initializeResult(params: unknown): Record<string, unknown> {\n const requested = isRecord(params) ? params[\"protocolVersion\"] : undefined;\n const negotiated =\n typeof requested === \"string\" && (SUPPORTED_PROTOCOL_VERSIONS as readonly string[]).includes(requested)\n ? requested\n : LATEST_PROTOCOL_VERSION;\n return {\n protocolVersion: negotiated,\n capabilities: {\n // The sets never change while the server lives, so no\n // listChanged notification is declared — announcing one and never\n // sending it would be a promise, not a capability.\n tools: {},\n resources: {},\n },\n serverInfo: { name: \"loam\", version: LOAM_VERSION },\n instructions:\n \"Every tool result is a loam --json envelope (contractVersion 1.0): branch on ok, \" +\n \"and on error.code when ok is false — the same machine contract the CLI prints.\",\n };\n}\n\n/** What the dispatcher hands back from one tool call, before it becomes a protocol reply. */\nexport interface ToolRunOutcome {\n readonly stdout: string;\n readonly stderr: string;\n readonly code: number;\n}\n\nexport type ToolReply =\n | { readonly kind: \"result\"; readonly result: Record<string, unknown> }\n | { readonly kind: \"error\"; readonly code: number; readonly message: string };\n\n/**\n * One tool run wrapped as an MCP tool result. The envelope rides VERBATIM in\n * content[0].text — one machine contract, not two — and again parsed as\n * structuredContent for clients that read that field.\n *\n * `isError` follows the ENVELOPE's `ok`, and the exit code only when no\n * envelope could be parsed. The two disagree on purpose for half the tool\n * set: validate, doctor, gate, context and delta all have graded answers\n * that exit 1 while reporting `ok: true` — an invalid fleet, a blocked\n * doctor, a `verdict: \"fail\"` deploy gate. Those exits are CI levers, not\n * call failures, and an MCP host renders `isError: true` as \"the call\n * failed\" and may discard structuredContent — which for loam_gate would\n * discard the exact verdict the caller asked for. `ok: false` is the one\n * signal that means \"loam refused to answer\", so it is the one isError\n * mirrors; the exit-code fallback exists only for stdout that parsed as no\n * envelope at all.\n *\n * An EMPTY stdout is a protocol error, not a quiet result: with `--json` on\n * every argv, a command that printed no envelope is a command whose answer\n * was lost, and \"nobody could look\" must stay distinguishable from any\n * genuine envelope — even a refusing one.\n */\nexport function toolReply(run: ToolRunOutcome): ToolReply {\n if (run.stdout.trim() === \"\") {\n const hint = run.stderr.trim() === \"\" ? \"\" : ` stderr: ${run.stderr.trim()}`;\n return {\n kind: \"error\",\n code: INTERNAL_ERROR,\n message: `the command exited ${run.code} without printing a --json envelope, so there is no result to return.${hint}`,\n };\n }\n let structured: Record<string, unknown> | undefined;\n try {\n const parsed: unknown = JSON.parse(run.stdout);\n // structuredContent must be an object; the envelope always is.\n structured = isRecord(parsed) ? parsed : undefined;\n } catch {\n // The fallback below handles both invalid JSON and valid non-object JSON.\n }\n if (structured === undefined) {\n // Preserve the bytes in content, but keep the declared output contract:\n // an MCP client should never have to branch on the absence of\n // structuredContent after tools/list promised a schema.\n structured = {\n contractVersion: \"1.0\",\n version: LOAM_VERSION,\n ok: false,\n error: {\n code: \"internal\",\n message: \"the command did not print a JSON object envelope\",\n },\n raw: run.stdout,\n };\n }\n const envelopeOk = structured?.[\"ok\"];\n return {\n kind: \"result\",\n result: {\n content: [{ type: \"text\", text: run.stdout }],\n structuredContent: structured,\n isError: typeof envelopeOk === \"boolean\" ? !envelopeOk : run.code !== 0,\n },\n };\n}\n"]}
|