@intentius/chant 0.79.0 → 0.81.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audit/core.d.ts.map +1 -1
- package/dist/audit/discover.d.ts +30 -0
- package/dist/audit/discover.d.ts.map +1 -1
- package/dist/audit/report-html.d.ts +5 -0
- package/dist/audit/report-html.d.ts.map +1 -1
- package/dist/audit/report-model.d.ts +13 -0
- package/dist/audit/report-model.d.ts.map +1 -1
- package/dist/audit/terraform-state.d.ts +13 -0
- package/dist/audit/terraform-state.d.ts.map +1 -1
- package/dist/cli/command-group.d.ts +6 -0
- package/dist/cli/command-group.d.ts.map +1 -1
- package/dist/cli/commands/audit.d.ts +44 -3
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon-examples.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon.d.ts +8 -0
- package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/import-agents.d.ts.map +1 -1
- package/dist/cli/commands/init.d.ts +6 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/commands/onboard.d.ts.map +1 -1
- package/dist/cli/commands/update.d.ts +6 -0
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/commands/vendor.d.ts +5 -0
- package/dist/cli/commands/vendor.d.ts.map +1 -1
- package/dist/cli/conflict-check.d.ts +6 -0
- package/dist/cli/conflict-check.d.ts.map +1 -1
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/components.d.ts.map +1 -1
- package/dist/cli/handlers/dev.d.ts.map +1 -1
- package/dist/cli/handlers/fan-out.d.ts.map +1 -1
- package/dist/cli/handlers/init.d.ts.map +1 -1
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/handlers/misc.d.ts.map +1 -1
- package/dist/cli/handlers/operator.d.ts +7 -0
- package/dist/cli/handlers/operator.d.ts.map +1 -1
- package/dist/cli/handlers/promote.d.ts +20 -0
- package/dist/cli/handlers/promote.d.ts.map +1 -0
- package/dist/cli/handlers/run-generate.d.ts +39 -0
- package/dist/cli/handlers/run-generate.d.ts.map +1 -0
- package/dist/cli/handlers/run.d.ts +4 -1
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/handlers/vendor.d.ts +8 -3
- package/dist/cli/handlers/vendor.d.ts.map +1 -1
- package/dist/cli/main.d.ts +12 -0
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/mcp/op-tools.d.ts.map +1 -1
- package/dist/cli/plugins.d.ts +14 -0
- package/dist/cli/plugins.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +46 -1
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/codegen/fetch.d.ts +10 -0
- package/dist/codegen/fetch.d.ts.map +1 -1
- package/dist/components/capability-plugin-loader.d.ts.map +1 -1
- package/dist/components/cli-support.d.ts +3 -1
- package/dist/components/cli-support.d.ts.map +1 -1
- package/dist/components/discover.d.ts.map +1 -1
- package/dist/components/driver.d.ts +16 -2
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/fan-out-support.d.ts.map +1 -1
- package/dist/components/gate-plan.d.ts +40 -0
- package/dist/components/gate-plan.d.ts.map +1 -0
- package/dist/components/pilots/alb-ecs.pilot.d.ts +7 -1
- package/dist/components/pilots/alb-ecs.pilot.d.ts.map +1 -1
- package/dist/components/presets/ecs-fargate.d.ts +10 -4
- package/dist/components/presets/ecs-fargate.d.ts.map +1 -1
- package/dist/components/promote.d.ts +264 -0
- package/dist/components/promote.d.ts.map +1 -0
- package/dist/components/sandbox/driver.d.ts.map +1 -1
- package/dist/components/sandbox/run.d.ts.map +1 -1
- package/dist/components/verbs/build-archive.d.ts +22 -11
- package/dist/components/verbs/build-archive.d.ts.map +1 -1
- package/dist/components/verbs/run-agent.d.ts.map +1 -1
- package/dist/config-static.d.ts +59 -0
- package/dist/config-static.d.ts.map +1 -0
- package/dist/config.d.ts +104 -5
- package/dist/config.d.ts.map +1 -1
- package/dist/content-digest.d.ts +17 -0
- package/dist/content-digest.d.ts.map +1 -0
- package/dist/discovery/files.d.ts +48 -4
- package/dist/discovery/files.d.ts.map +1 -1
- package/dist/discovery/fold-import.d.ts +32 -1
- package/dist/discovery/fold-import.d.ts.map +1 -1
- package/dist/discovery/index.d.ts.map +1 -1
- package/dist/discovery/sandbox/driver.d.ts +20 -0
- package/dist/discovery/sandbox/driver.d.ts.map +1 -1
- package/dist/discovery/sandbox/fork.d.ts +20 -0
- package/dist/discovery/sandbox/fork.d.ts.map +1 -1
- package/dist/discovery/walk.d.ts +119 -0
- package/dist/discovery/walk.d.ts.map +1 -0
- package/dist/graph-ir.d.ts +17 -1
- package/dist/graph-ir.d.ts.map +1 -1
- package/dist/join-key.d.ts +31 -0
- package/dist/join-key.d.ts.map +1 -0
- package/dist/lexicon-module.d.ts +110 -0
- package/dist/lexicon-module.d.ts.map +1 -0
- package/dist/lexicon.d.ts +82 -1
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/build-ledger-store.d.ts.map +1 -1
- package/dist/lifecycle/digest.d.ts +5 -1
- package/dist/lifecycle/digest.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.d.ts +20 -5
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/git.d.ts +64 -0
- package/dist/lifecycle/git.d.ts.map +1 -1
- package/dist/lifecycle/lease.d.ts +8 -1
- package/dist/lifecycle/lease.d.ts.map +1 -1
- package/dist/lifecycle/legacy-digest.d.ts +59 -0
- package/dist/lifecycle/legacy-digest.d.ts.map +1 -0
- package/dist/lifecycle/member-ledger.d.ts +67 -0
- package/dist/lifecycle/member-ledger.d.ts.map +1 -0
- package/dist/lifecycle/plan-digest.d.ts +4 -3
- package/dist/lifecycle/plan-digest.d.ts.map +1 -1
- package/dist/lifecycle/release-ledger.d.ts +42 -1
- package/dist/lifecycle/release-ledger.d.ts.map +1 -1
- package/dist/lifecycle/status.d.ts +90 -5
- package/dist/lifecycle/status.d.ts.map +1 -1
- package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts +2 -3
- package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts.map +1 -1
- package/dist/op/activities/activity-contracts.d.ts +32 -0
- package/dist/op/activities/activity-contracts.d.ts.map +1 -1
- package/dist/op/activities/index.d.ts +2 -0
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/activities/propose-upgrade.d.ts +75 -0
- package/dist/op/activities/propose-upgrade.d.ts.map +1 -0
- package/dist/op/activity-contract-registry.d.ts.map +1 -1
- package/dist/op/activity-registry.d.ts.map +1 -1
- package/dist/op/discover.d.ts.map +1 -1
- package/dist/op/gate-approval.d.ts.map +1 -1
- package/dist/op/gate-name.d.ts +7 -0
- package/dist/op/gate-name.d.ts.map +1 -1
- package/dist/op/gate-summary.d.ts +16 -0
- package/dist/op/gate-summary.d.ts.map +1 -1
- package/dist/op/gate.d.ts +16 -2
- package/dist/op/gate.d.ts.map +1 -1
- package/dist/op/generate-pipeline.d.ts.map +1 -1
- package/dist/op/runtimes/local.d.ts.map +1 -1
- package/dist/project-root.d.ts +26 -0
- package/dist/project-root.d.ts.map +1 -1
- package/dist/workspace/__fixtures__/contract-repo.d.ts +37 -0
- package/dist/workspace/__fixtures__/contract-repo.d.ts.map +1 -0
- package/dist/workspace/__fixtures__/two-member-workspace.d.ts +11 -0
- package/dist/workspace/__fixtures__/two-member-workspace.d.ts.map +1 -0
- package/dist/workspace/checks/generated.d.ts +156 -0
- package/dist/workspace/checks/generated.d.ts.map +1 -0
- package/dist/workspace/checks/ledgers.d.ts +78 -0
- package/dist/workspace/checks/ledgers.d.ts.map +1 -0
- package/dist/workspace/checks/links.d.ts +25 -0
- package/dist/workspace/checks/links.d.ts.map +1 -0
- package/dist/workspace/checks/pipelines.d.ts +76 -0
- package/dist/workspace/checks/pipelines.d.ts.map +1 -0
- package/dist/workspace/checks.d.ts +161 -0
- package/dist/workspace/checks.d.ts.map +1 -0
- package/dist/workspace/compose-graph.d.ts +115 -0
- package/dist/workspace/compose-graph.d.ts.map +1 -0
- package/dist/workspace/compose-reports.d.ts +68 -0
- package/dist/workspace/compose-reports.d.ts.map +1 -0
- package/dist/workspace/declaration.d.ts +220 -0
- package/dist/workspace/declaration.d.ts.map +1 -0
- package/dist/workspace/declaration.schema.json +434 -0
- package/dist/workspace/generated-files.d.ts +65 -0
- package/dist/workspace/generated-files.d.ts.map +1 -0
- package/dist/workspace/glob.d.ts +13 -0
- package/dist/workspace/glob.d.ts.map +1 -0
- package/dist/workspace/graph-cli.d.ts +75 -0
- package/dist/workspace/graph-cli.d.ts.map +1 -0
- package/dist/workspace/init.d.ts +96 -0
- package/dist/workspace/init.d.ts.map +1 -0
- package/dist/workspace/jsonc.d.ts +38 -0
- package/dist/workspace/jsonc.d.ts.map +1 -0
- package/dist/workspace/kinds.d.ts +192 -0
- package/dist/workspace/kinds.d.ts.map +1 -0
- package/dist/workspace/lineage-check.d.ts +84 -0
- package/dist/workspace/lineage-check.d.ts.map +1 -0
- package/dist/workspace/lineage-cli.d.ts +31 -0
- package/dist/workspace/lineage-cli.d.ts.map +1 -0
- package/dist/workspace/lineage-init.d.ts +101 -0
- package/dist/workspace/lineage-init.d.ts.map +1 -0
- package/dist/workspace/lineage-lock.d.ts +258 -0
- package/dist/workspace/lineage-lock.d.ts.map +1 -0
- package/dist/workspace/lineage-merge.d.ts +14 -0
- package/dist/workspace/lineage-merge.d.ts.map +1 -0
- package/dist/workspace/lineage-migrations.d.ts +178 -0
- package/dist/workspace/lineage-migrations.d.ts.map +1 -0
- package/dist/workspace/lineage-update.d.ts +59 -0
- package/dist/workspace/lineage-update.d.ts.map +1 -0
- package/dist/workspace/lineage-upgrade-cli.d.ts +40 -0
- package/dist/workspace/lineage-upgrade-cli.d.ts.map +1 -0
- package/dist/workspace/lineage-upgrade.d.ts +146 -0
- package/dist/workspace/lineage-upgrade.d.ts.map +1 -0
- package/dist/workspace/lineage-vendor.d.ts +63 -0
- package/dist/workspace/lineage-vendor.d.ts.map +1 -0
- package/dist/workspace/lineage-version.d.ts +32 -0
- package/dist/workspace/lineage-version.d.ts.map +1 -0
- package/dist/workspace/links.d.ts +150 -0
- package/dist/workspace/links.d.ts.map +1 -0
- package/dist/workspace/ls.d.ts +99 -0
- package/dist/workspace/ls.d.ts.map +1 -0
- package/dist/workspace/member-commands.d.ts +139 -0
- package/dist/workspace/member-commands.d.ts.map +1 -0
- package/dist/workspace/member-handles.d.ts +54 -0
- package/dist/workspace/member-handles.d.ts.map +1 -0
- package/dist/workspace/member-pipeline.d.ts +143 -0
- package/dist/workspace/member-pipeline.d.ts.map +1 -0
- package/dist/workspace/member-run.d.ts +67 -0
- package/dist/workspace/member-run.d.ts.map +1 -0
- package/dist/workspace/reason-codes.d.ts +65 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -0
- package/dist/workspace/record-source.d.ts +22 -0
- package/dist/workspace/record-source.d.ts.map +1 -0
- package/dist/workspace/records-cli.d.ts +81 -0
- package/dist/workspace/records-cli.d.ts.map +1 -0
- package/dist/workspace/records.d.ts +118 -0
- package/dist/workspace/records.d.ts.map +1 -0
- package/dist/workspace/root-refusal.d.ts +53 -0
- package/dist/workspace/root-refusal.d.ts.map +1 -0
- package/dist/workspace/status.d.ts +150 -0
- package/dist/workspace/status.d.ts.map +1 -0
- package/dist/workspace/template-manifest.d.ts +90 -0
- package/dist/workspace/template-manifest.d.ts.map +1 -0
- package/dist/workspace/tree.d.ts +36 -0
- package/dist/workspace/tree.d.ts.map +1 -0
- package/dist/workspace/trust/attestor.d.ts +67 -0
- package/dist/workspace/trust/attestor.d.ts.map +1 -0
- package/dist/workspace/trust/policy.d.ts +111 -0
- package/dist/workspace/trust/policy.d.ts.map +1 -0
- package/dist/workspace/trust/provenance.d.ts +67 -0
- package/dist/workspace/trust/provenance.d.ts.map +1 -0
- package/dist/workspace/trust/ssh-commit.d.ts +43 -0
- package/dist/workspace/trust/ssh-commit.d.ts.map +1 -0
- package/dist/workspace/trust/test-repo.d.ts +42 -0
- package/dist/workspace/trust/test-repo.d.ts.map +1 -0
- package/dist/workspace/trust/verify-cli.d.ts +9 -0
- package/dist/workspace/trust/verify-cli.d.ts.map +1 -0
- package/dist/workspace/trust/verify.d.ts +66 -0
- package/dist/workspace/trust/verify.d.ts.map +1 -0
- package/dist/workspace/which-chant.d.ts +58 -0
- package/dist/workspace/which-chant.d.ts.map +1 -0
- package/dist/workspace/workspace-kinds.schema.json +133 -0
- package/package.json +4 -3
- package/src/audit/core.ts +13 -2
- package/src/audit/discover.ts +102 -36
- package/src/audit/report-html.ts +3 -1
- package/src/audit/report-model.ts +14 -1
- package/src/audit/terraform-state.test.ts +81 -1
- package/src/audit/terraform-state.ts +25 -0
- package/src/cli/command-group.ts +7 -0
- package/src/cli/commands/audit-truncation.test.ts +167 -0
- package/src/cli/commands/audit.ts +132 -29
- package/src/cli/commands/check-lexicon-examples.ts +2 -1
- package/src/cli/commands/check-lexicon.ts +24 -0
- package/src/cli/commands/doctor.test.ts +30 -0
- package/src/cli/commands/doctor.ts +21 -0
- package/src/cli/commands/import-agents.ts +15 -4
- package/src/cli/commands/init.ts +76 -25
- package/src/cli/commands/lint.test.ts +52 -1
- package/src/cli/commands/lint.ts +41 -71
- package/src/cli/commands/onboard.ts +15 -0
- package/src/cli/commands/update.ts +18 -5
- package/src/cli/commands/vendor.ts +11 -6
- package/src/cli/conflict-check.test.ts +18 -1
- package/src/cli/conflict-check.ts +13 -0
- package/src/cli/discovery-skip.test.ts +168 -0
- package/src/cli/handlers/__snapshots__/build-member.test.ts.snap +38 -0
- package/src/cli/handlers/build-member.test.ts +127 -0
- package/src/cli/handlers/build.test.ts +11 -0
- package/src/cli/handlers/build.ts +35 -1
- package/src/cli/handlers/components.test.ts +127 -0
- package/src/cli/handlers/components.ts +70 -8
- package/src/cli/handlers/dev.ts +6 -1
- package/src/cli/handlers/fan-out.ts +9 -2
- package/src/cli/handlers/graph.test.ts +20 -0
- package/src/cli/handlers/graph.ts +4 -2
- package/src/cli/handlers/init.ts +54 -1
- package/src/cli/handlers/lifecycle.ts +2 -1
- package/src/cli/handlers/misc.ts +16 -0
- package/src/cli/handlers/operator.test.ts +68 -0
- package/src/cli/handlers/operator.ts +39 -10
- package/src/cli/handlers/promote.test.ts +377 -0
- package/src/cli/handlers/promote.ts +391 -0
- package/src/cli/handlers/run-generate-member.test.ts +83 -0
- package/src/cli/handlers/run-generate.test.ts +158 -0
- package/src/cli/handlers/run-generate.ts +168 -0
- package/src/cli/handlers/run.test.ts +47 -0
- package/src/cli/handlers/run.ts +41 -6
- package/src/cli/handlers/vendor.ts +147 -43
- package/src/cli/main.test.ts +137 -1
- package/src/cli/main.ts +290 -24
- package/src/cli/mcp/op-tools.ts +2 -1
- package/src/cli/path-lexicon-messages.test.ts +152 -0
- package/src/cli/plugins.ts +43 -5
- package/src/cli/registry.ts +46 -1
- package/src/cli/static-config-read.test.ts +208 -0
- package/src/codegen/fetch.test.ts +63 -1
- package/src/codegen/fetch.ts +30 -1
- package/src/components/__fixtures__/alb-ecs-service.json +0 -8
- package/src/components/capability-plugin-loader.ts +3 -1
- package/src/components/cli-support.ts +9 -3
- package/src/components/discover.test.ts +27 -57
- package/src/components/discover.ts +20 -51
- package/src/components/driver.test.ts +90 -12
- package/src/components/driver.ts +43 -5
- package/src/components/fan-out-support.ts +2 -1
- package/src/components/gate-plan.ts +53 -0
- package/src/components/pilots/README.md +1 -1
- package/src/components/pilots/alb-ecs.pilot.ts +7 -7
- package/src/components/presets/ecs-fargate.ts +10 -5
- package/src/components/promote.test.ts +645 -0
- package/src/components/promote.ts +641 -0
- package/src/components/sandbox/driver.ts +8 -8
- package/src/components/sandbox/run.ts +4 -1
- package/src/components/verbs/build-archive.ts +27 -21
- package/src/components/verbs/run-agent.ts +3 -5
- package/src/config-static.test.ts +136 -0
- package/src/config-static.ts +232 -0
- package/src/config.test.ts +40 -0
- package/src/config.ts +161 -11
- package/src/content-digest.test.ts +120 -0
- package/src/content-digest.ts +21 -0
- package/src/discovery/files.test.ts +127 -1
- package/src/discovery/files.ts +91 -59
- package/src/discovery/fold-import.ts +127 -9
- package/src/discovery/index.ts +1 -0
- package/src/discovery/sandbox/driver.ts +73 -35
- package/src/discovery/sandbox/fork-diagnostic.test.ts +136 -13
- package/src/discovery/sandbox/fork.ts +83 -43
- package/src/discovery/sandbox/path-lexicon-multifile.test.ts +285 -0
- package/src/discovery/sandbox/path-lexicon-parity.test.ts +162 -0
- package/src/discovery/walk.test.ts +263 -0
- package/src/discovery/walk.ts +334 -0
- package/src/effect-receipt.ts +2 -2
- package/src/graph-ir.ts +18 -1
- package/src/join-key.test.ts +25 -0
- package/src/join-key.ts +39 -0
- package/src/lexicon-module.test.ts +203 -0
- package/src/lexicon-module.ts +215 -0
- package/src/lexicon.ts +105 -1
- package/src/lifecycle/build-ledger-store.ts +13 -2
- package/src/lifecycle/digest.ts +9 -5
- package/src/lifecycle/gate-ledger.test.ts +20 -0
- package/src/lifecycle/gate-ledger.ts +28 -6
- package/src/lifecycle/git.test.ts +13 -1
- package/src/lifecycle/git.ts +221 -176
- package/src/lifecycle/lease.ts +25 -10
- package/src/lifecycle/legacy-digest.test.ts +206 -0
- package/src/lifecycle/legacy-digest.ts +103 -0
- package/src/lifecycle/member-ledger.test.ts +319 -0
- package/src/lifecycle/member-ledger.ts +145 -0
- package/src/lifecycle/plan-digest.ts +7 -7
- package/src/lifecycle/release-ledger.test.ts +20 -0
- package/src/lifecycle/release-ledger.ts +53 -3
- package/src/lifecycle/status.test.ts +151 -1
- package/src/lifecycle/status.ts +228 -7
- package/src/lint/rules/comp/comp003-mutating-no-rollback.ts +2 -3
- package/src/op/activities/activity-contracts.ts +37 -0
- package/src/op/activities/index.ts +10 -0
- package/src/op/activities/propose-upgrade.ts +206 -0
- package/src/op/activity-contract-registry.ts +18 -4
- package/src/op/activity-registry.ts +26 -0
- package/src/op/discover.test.ts +14 -4
- package/src/op/discover.ts +19 -23
- package/src/op/gate-approval.ts +3 -1
- package/src/op/gate-name.ts +8 -0
- package/src/op/gate-summary.test.ts +14 -0
- package/src/op/gate-summary.ts +25 -2
- package/src/op/gate.ts +36 -7
- package/src/op/generate-pipeline.test.ts +14 -5
- package/src/op/generate-pipeline.ts +2 -1
- package/src/op/runtimes/local.ts +2 -1
- package/src/project-root.test.ts +66 -1
- package/src/project-root.ts +48 -0
- package/src/workspace/__fixtures__/contract-repo.ts +100 -0
- package/src/workspace/__fixtures__/two-member-workspace.ts +35 -0
- package/src/workspace/behold-kinds.test.ts +162 -0
- package/src/workspace/check-contract.test.ts +127 -0
- package/src/workspace/check.schema.json +347 -0
- package/src/workspace/checks/generated.test.ts +287 -0
- package/src/workspace/checks/generated.ts +475 -0
- package/src/workspace/checks/ledgers.test.ts +139 -0
- package/src/workspace/checks/ledgers.ts +246 -0
- package/src/workspace/checks/links.ts +183 -0
- package/src/workspace/checks/pipelines.test.ts +133 -0
- package/src/workspace/checks/pipelines.ts +225 -0
- package/src/workspace/checks-repo.test.ts +71 -0
- package/src/workspace/checks.test.ts +450 -0
- package/src/workspace/checks.ts +461 -0
- package/src/workspace/compose-graph.test.ts +146 -0
- package/src/workspace/compose-graph.ts +228 -0
- package/src/workspace/compose-reports.test.ts +109 -0
- package/src/workspace/compose-reports.ts +186 -0
- package/src/workspace/declaration.schema.json +434 -0
- package/src/workspace/declaration.test.ts +347 -0
- package/src/workspace/declaration.ts +623 -0
- package/src/workspace/generated-files.test.ts +114 -0
- package/src/workspace/generated-files.ts +94 -0
- package/src/workspace/glob.ts +49 -0
- package/src/workspace/graph-cli.ts +220 -0
- package/src/workspace/graph-contract.test.ts +204 -0
- package/src/workspace/graph.schema.json +662 -0
- package/src/workspace/init.test.ts +223 -0
- package/src/workspace/init.ts +585 -0
- package/src/workspace/jsonc.test.ts +65 -0
- package/src/workspace/jsonc.ts +231 -0
- package/src/workspace/kinds.test.ts +288 -0
- package/src/workspace/kinds.ts +506 -0
- package/src/workspace/lineage-check.ts +264 -0
- package/src/workspace/lineage-cli.ts +114 -0
- package/src/workspace/lineage-init.test.ts +231 -0
- package/src/workspace/lineage-init.ts +365 -0
- package/src/workspace/lineage-lock.test.ts +224 -0
- package/src/workspace/lineage-lock.ts +357 -0
- package/src/workspace/lineage-merge.ts +44 -0
- package/src/workspace/lineage-migrations.test.ts +178 -0
- package/src/workspace/lineage-migrations.ts +343 -0
- package/src/workspace/lineage-update.ts +160 -0
- package/src/workspace/lineage-upgrade-cli.ts +151 -0
- package/src/workspace/lineage-upgrade.test.ts +474 -0
- package/src/workspace/lineage-upgrade.ts +624 -0
- package/src/workspace/lineage-vendor.test.ts +126 -0
- package/src/workspace/lineage-vendor.ts +215 -0
- package/src/workspace/lineage-version.ts +127 -0
- package/src/workspace/links.test.ts +344 -0
- package/src/workspace/links.ts +331 -0
- package/src/workspace/ls-contract.test.ts +200 -0
- package/src/workspace/ls.schema.json +190 -0
- package/src/workspace/ls.ts +254 -0
- package/src/workspace/member-commands.e2e.test.ts +244 -0
- package/src/workspace/member-commands.test.ts +217 -0
- package/src/workspace/member-commands.ts +540 -0
- package/src/workspace/member-handles.test.ts +76 -0
- package/src/workspace/member-handles.ts +228 -0
- package/src/workspace/member-pipeline.test.ts +135 -0
- package/src/workspace/member-pipeline.ts +317 -0
- package/src/workspace/member-run.ts +146 -0
- package/src/workspace/read-contract.test.ts +129 -0
- package/src/workspace/reason-codes.test.ts +113 -0
- package/src/workspace/reason-codes.ts +81 -0
- package/src/workspace/record-source.ts +117 -0
- package/src/workspace/records-cli.ts +198 -0
- package/src/workspace/records-contract.test.ts +93 -0
- package/src/workspace/records.schema.json +150 -0
- package/src/workspace/records.test.ts +243 -0
- package/src/workspace/records.ts +384 -0
- package/src/workspace/root-refusal.ts +97 -0
- package/src/workspace/status-contract.test.ts +308 -0
- package/src/workspace/status.schema.json +210 -0
- package/src/workspace/status.ts +412 -0
- package/src/workspace/template-manifest.test.ts +79 -0
- package/src/workspace/template-manifest.ts +221 -0
- package/src/workspace/tree.ts +132 -0
- package/src/workspace/trust/attestor.ts +93 -0
- package/src/workspace/trust/policy.ts +306 -0
- package/src/workspace/trust/provenance.ts +181 -0
- package/src/workspace/trust/ssh-commit.ts +138 -0
- package/src/workspace/trust/test-repo.ts +138 -0
- package/src/workspace/trust/trust.test.ts +397 -0
- package/src/workspace/trust/verify-cli.ts +56 -0
- package/src/workspace/trust/verify.ts +194 -0
- package/src/workspace/which-chant.test.ts +117 -0
- package/src/workspace/which-chant.ts +151 -0
- package/src/workspace/workspace-kinds.schema.json +133 -0
|
@@ -6,14 +6,15 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { existsSync, readFileSync, statSync, writeFileSync } from "fs";
|
|
9
|
-
import { join } from "path";
|
|
9
|
+
import { dirname, join, resolve } from "path";
|
|
10
10
|
import { auditFiles, type AuditInput, type AuditFinding, type ChecksProvider, type SuppressionStats } from "../../audit/core";
|
|
11
|
-
import { AUDIT_LEXICONS, classifyFiles,
|
|
11
|
+
import { AUDIT_LEXICONS, classifyFiles, loadAuditPlugins, unclaimedFiles, walkCandidates, type CandidateWalk, type DetectPlugin, type RepoFile, type UnclaimedFile } from "../../audit/discover";
|
|
12
12
|
import { RULE_CATALOG, resolveAuditCatalog, type RuleMeta } from "../../audit/catalog";
|
|
13
13
|
import { scanForSecrets, parseSecretsConfig, type SecretsScanOptions } from "../../audit/secrets";
|
|
14
14
|
import { auditWranglerConfigs } from "../../audit/wrangler";
|
|
15
15
|
import { auditNginxConfigs } from "../../audit/nginx";
|
|
16
16
|
import { auditTerraformState } from "../../audit/terraform-state";
|
|
17
|
+
import { workspaceMemberDirs } from "../../discovery/walk";
|
|
17
18
|
import { renderMarkdown } from "../../audit/report";
|
|
18
19
|
import { renderHtml, type ReportTheme } from "../../audit/report-html";
|
|
19
20
|
import { buildReportJson, REPORT_SCHEMA_VERSION, type AuditSnapshot } from "../../audit/report-model";
|
|
@@ -21,6 +22,9 @@ import { fetchRepoFiles, resolveActionSha, resolveImageDigest, resolveRepoCommit
|
|
|
21
22
|
import { extractUnpinnedActions, extractUnpinnedImages } from "../../audit/proof";
|
|
22
23
|
import type { ProveOptions } from "../../audit/proof";
|
|
23
24
|
import type { Severity } from "../../lint/rule";
|
|
25
|
+
import { formatWarning } from "../format";
|
|
26
|
+
import { lexiconPackagesToInstall, lexiconSourceLabel, pathLexiconMap, recordedLexiconModules } from "../../lexicon-module";
|
|
27
|
+
import { readLexiconDeclarationsStatically, unknownPathLexiconsNotice } from "../../config-static";
|
|
24
28
|
|
|
25
29
|
export type AuditFormat = "stylish" | "json" | "sarif" | "markdown" | "html";
|
|
26
30
|
export type AuditTier = "merge-worthy" | "all";
|
|
@@ -68,6 +72,23 @@ export interface AuditCommandOptions {
|
|
|
68
72
|
* present (URL targets have no local config file to read).
|
|
69
73
|
*/
|
|
70
74
|
secretsScan?: SecretsScanOptions;
|
|
75
|
+
/**
|
|
76
|
+
* Files the local walk takes before it stops, counting every file
|
|
77
|
+
* (`--max-files`, #2528). Defaults to 1000. Local paths only.
|
|
78
|
+
*/
|
|
79
|
+
maxFiles?: number;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* A diagnostic about the run rather than the repository. Printed on stderr by
|
|
84
|
+
* `printAuditResult`, never part of `output`, so the report on stdout is the
|
|
85
|
+
* same with or without one.
|
|
86
|
+
*/
|
|
87
|
+
export interface AuditWarning {
|
|
88
|
+
/** The path the warning is about, when there is one. */
|
|
89
|
+
file?: string;
|
|
90
|
+
message: string;
|
|
91
|
+
hint?: string;
|
|
71
92
|
}
|
|
72
93
|
|
|
73
94
|
export interface AuditCommandResult {
|
|
@@ -96,14 +117,45 @@ export interface AuditCommandResult {
|
|
|
96
117
|
* summary line.
|
|
97
118
|
*/
|
|
98
119
|
suppressedCount?: number;
|
|
120
|
+
/** Run diagnostics for stderr: a truncated scan in a format that cannot carry it (#2528). */
|
|
121
|
+
warnings?: AuditWarning[];
|
|
99
122
|
}
|
|
100
123
|
|
|
101
124
|
/** Exit code when the audit had no lexicons to look with. Distinct from 1 (findings / failure). */
|
|
102
125
|
export const NO_LEXICONS_EXIT_CODE = 2;
|
|
103
126
|
|
|
104
|
-
/**
|
|
105
|
-
|
|
106
|
-
|
|
127
|
+
/**
|
|
128
|
+
* What to tell the user about lexicons that did not load: an `npm i` line for
|
|
129
|
+
* the package-backed ones, and the declared path for any lexicon
|
|
130
|
+
* `chant.config.ts` names by path (#2520), which has no package to install
|
|
131
|
+
* (chant#2578).
|
|
132
|
+
*/
|
|
133
|
+
function missingLexiconRemedy(names: readonly string[], paths: ReadonlyMap<string, string>): string {
|
|
134
|
+
const parts: string[] = [];
|
|
135
|
+
const pkgs = lexiconPackagesToInstall(names, paths);
|
|
136
|
+
if (pkgs.length > 0) parts.push(`npm i ${pkgs.join(" ")}`);
|
|
137
|
+
for (const name of names) {
|
|
138
|
+
if (paths.has(name)) parts.push(`${name} is declared by path: ${lexiconSourceLabel(name, process.cwd(), paths)}`);
|
|
139
|
+
}
|
|
140
|
+
return parts.join("; ");
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* chant#2589 — the lexicons a local target's chant.config declares by path,
|
|
145
|
+
* with any recorded earlier in this process. The config is read statically
|
|
146
|
+
* (../../config-static.ts) and never evaluated, and the paths are not
|
|
147
|
+
* recorded for the loaders: audit names them in its messages, but never
|
|
148
|
+
* imports a project's lexicon module. `notice` is set when the config's
|
|
149
|
+
* `lexicons` could not be read that way.
|
|
150
|
+
*/
|
|
151
|
+
export function auditPathLexicons(target: string, isUrl: boolean): { paths: Map<string, string>; notice?: string } {
|
|
152
|
+
const paths = recordedLexiconModules();
|
|
153
|
+
if (isUrl) return { paths };
|
|
154
|
+
const read = readLexiconDeclarationsStatically(resolve(target));
|
|
155
|
+
if (read.status === "read") {
|
|
156
|
+
for (const [name, path] of pathLexiconMap(read.entries, dirname(read.configPath))) paths.set(name, path);
|
|
157
|
+
}
|
|
158
|
+
return { paths, notice: unknownPathLexiconsNotice(read) };
|
|
107
159
|
}
|
|
108
160
|
|
|
109
161
|
/** Missing audit lexicons the unclaimed files pointed at, in first-seen order. */
|
|
@@ -116,25 +168,31 @@ function wantedLexicons(unclaimed: UnclaimedFile[]): string[] {
|
|
|
116
168
|
* lexicons it needs. Every wanted lexicon is a `-p` package so npx puts all of
|
|
117
169
|
* them on the same resolution path.
|
|
118
170
|
*/
|
|
119
|
-
export function installLine(
|
|
120
|
-
|
|
171
|
+
export function installLine(
|
|
172
|
+
lexicons: string[],
|
|
173
|
+
target: string,
|
|
174
|
+
paths: ReadonlyMap<string, string> = recordedLexiconModules(),
|
|
175
|
+
): string {
|
|
176
|
+
// chant#2578 — a lexicon declared by path has no package for npx to fetch.
|
|
177
|
+
const pkgs = ["@intentius/chant", ...lexiconPackagesToInstall(lexicons, paths)];
|
|
121
178
|
return `npx ${pkgs.map((p) => `-p ${p}`).join(" ")} chant audit ${target}`;
|
|
122
179
|
}
|
|
123
180
|
|
|
124
181
|
/** One-line coverage hint for the partial case: some lexicons loaded, others wanted by files on disk. */
|
|
125
|
-
function missingLexiconHint(unclaimed: UnclaimedFile[]): string | undefined {
|
|
182
|
+
function missingLexiconHint(unclaimed: UnclaimedFile[], paths: ReadonlyMap<string, string>): string | undefined {
|
|
126
183
|
const wanted = wantedLexicons(unclaimed);
|
|
127
184
|
if (wanted.length === 0) return undefined;
|
|
128
185
|
const n = unclaimed.length;
|
|
129
186
|
return `${n} file${n === 1 ? " looks" : "s look"} like ${wanted.join("/")} but ${wanted.length === 1 ? "that lexicon is" : "those lexicons are"} not installed, so ${n === 1 ? "it was" : "they were"} skipped` +
|
|
130
|
-
` (
|
|
187
|
+
` (${missingLexiconRemedy(wanted, paths)}).`;
|
|
131
188
|
}
|
|
132
189
|
|
|
133
190
|
/** Human-readable diagnostic for the zero-lexicon case. */
|
|
134
|
-
function renderNoLexicons(target: string, unclaimed: UnclaimedFile[]): string {
|
|
191
|
+
function renderNoLexicons(target: string, unclaimed: UnclaimedFile[], paths: ReadonlyMap<string, string>, truncated?: AuditTruncation): string {
|
|
135
192
|
const lines: string[] = [];
|
|
136
193
|
lines.push(`chant audit had nothing to look with: no audit lexicon is installed, so nothing under ${target} was inspected.`);
|
|
137
194
|
lines.push("This is not a clean result. Detection and checks live in the lexicon packages.");
|
|
195
|
+
if (truncated) lines.push("", `Note: ${truncationNote(truncated, target)}`);
|
|
138
196
|
const wanted = wantedLexicons(unclaimed);
|
|
139
197
|
if (unclaimed.length > 0) {
|
|
140
198
|
lines.push("", "Files that wanted a lexicon:");
|
|
@@ -145,12 +203,17 @@ function renderNoLexicons(target: string, unclaimed: UnclaimedFile[]): string {
|
|
|
145
203
|
lines.push("", "No file under the target looked like CI, Kubernetes, Helm, Docker, CloudFormation, ARM, Config Connector, fountain, or Terraform either.");
|
|
146
204
|
}
|
|
147
205
|
lines.push("", "Run it with the lexicons those files need:");
|
|
148
|
-
|
|
206
|
+
const lexicons = wanted.length > 0 ? wanted : [...AUDIT_LEXICONS];
|
|
207
|
+
lines.push(` ${installLine(lexicons, target, paths)}`);
|
|
208
|
+
// chant#2589 — a lexicon declared by path is left out of the line above.
|
|
209
|
+
for (const name of lexicons) {
|
|
210
|
+
if (paths.has(name)) lines.push(` (${name} is declared by path: ${lexiconSourceLabel(name, process.cwd(), paths)})`);
|
|
211
|
+
}
|
|
149
212
|
return lines.join("\n");
|
|
150
213
|
}
|
|
151
214
|
|
|
152
215
|
/** Machine-readable form of the zero-lexicon diagnostic (`status: "no-lexicons"`). */
|
|
153
|
-
function renderNoLexiconsJson(target: string, unclaimed: UnclaimedFile[]): string {
|
|
216
|
+
function renderNoLexiconsJson(target: string, unclaimed: UnclaimedFile[], paths: ReadonlyMap<string, string>, truncated?: AuditTruncation): string {
|
|
154
217
|
const wanted = wantedLexicons(unclaimed);
|
|
155
218
|
return JSON.stringify(
|
|
156
219
|
{
|
|
@@ -162,7 +225,8 @@ function renderNoLexiconsJson(target: string, unclaimed: UnclaimedFile[]): strin
|
|
|
162
225
|
findings: [],
|
|
163
226
|
unclaimed,
|
|
164
227
|
missingLexicons: wanted,
|
|
165
|
-
install: installLine(wanted.length > 0 ? wanted : [...AUDIT_LEXICONS], target),
|
|
228
|
+
install: installLine(wanted.length > 0 ? wanted : [...AUDIT_LEXICONS], target, paths),
|
|
229
|
+
truncated,
|
|
166
230
|
},
|
|
167
231
|
null,
|
|
168
232
|
2,
|
|
@@ -372,6 +436,28 @@ async function buildSnapshot(options: AuditCommandOptions, files: string[], isUr
|
|
|
372
436
|
};
|
|
373
437
|
}
|
|
374
438
|
|
|
439
|
+
/** The flag that raises the local walk's file limit, named wherever a truncated scan is reported. */
|
|
440
|
+
export const MAX_FILES_FLAG = "--max-files";
|
|
441
|
+
|
|
442
|
+
/** How a report states that the local walk stopped at its limit (#2528). JSON carries it as `truncated`. */
|
|
443
|
+
export interface AuditTruncation {
|
|
444
|
+
limit: number;
|
|
445
|
+
flag: typeof MAX_FILES_FLAG;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/** The truncation a walk reports, or undefined for a whole scan. */
|
|
449
|
+
export function walkTruncation(walk: CandidateWalk): AuditTruncation | undefined {
|
|
450
|
+
return walk.truncated ? { limit: walk.maxFiles, flag: MAX_FILES_FLAG } : undefined;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/** The text-report note for a truncated scan. */
|
|
454
|
+
export function truncationNote(t: AuditTruncation, target: string): string {
|
|
455
|
+
return (
|
|
456
|
+
`The scan of ${target} stopped at ${t.limit} files, so part of the tree was not audited. ` +
|
|
457
|
+
`The limit counts every file the walk reaches, not only the ones it audits. Raise it with ${t.flag} <n>.`
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
|
|
375
461
|
/** Run the audit and produce a rendered result. */
|
|
376
462
|
export async function auditCommand(options: AuditCommandOptions): Promise<AuditCommandResult> {
|
|
377
463
|
const format = options.format ?? "stylish";
|
|
@@ -379,6 +465,8 @@ export async function auditCommand(options: AuditCommandOptions): Promise<AuditC
|
|
|
379
465
|
const failOn = options.failOn ?? "none";
|
|
380
466
|
|
|
381
467
|
const isUrl = /^https?:\/\//.test(options.path);
|
|
468
|
+
// chant#2589 — path lexicons the target declares, read without running its config.
|
|
469
|
+
const pathLexicons = auditPathLexicons(options.path, isUrl);
|
|
382
470
|
|
|
383
471
|
// Detection lives in the lexicon plugins (each one's `detectTemplate`), so a
|
|
384
472
|
// lexicon that isn't installed can't claim its files. Every branch below
|
|
@@ -387,6 +475,7 @@ export async function auditCommand(options: AuditCommandOptions): Promise<AuditC
|
|
|
387
475
|
// render the same way (#1623).
|
|
388
476
|
const plugins = options.plugins ?? (await loadAuditPlugins());
|
|
389
477
|
let candidates: RepoFile[];
|
|
478
|
+
let truncated: AuditTruncation | undefined;
|
|
390
479
|
if (isUrl) {
|
|
391
480
|
try {
|
|
392
481
|
// Fetch the whole repo's candidate files (all lexicons, not just CI) and
|
|
@@ -406,8 +495,19 @@ export async function auditCommand(options: AuditCommandOptions): Promise<AuditC
|
|
|
406
495
|
// One walk, plugin-delegated detection. CI (path), Dockerfiles (name), and
|
|
407
496
|
// Helm charts (bundle) are special-cased by the classifier since content
|
|
408
497
|
// shape alone can't disambiguate them.
|
|
409
|
-
|
|
498
|
+
// Workspace members leave the root's audit only when a declaration exists
|
|
499
|
+
// (#2525 rule 3); without one this is a few existsSync calls (#2527).
|
|
500
|
+
const excludeDirs = await workspaceMemberDirs(options.path);
|
|
501
|
+
const walk = walkCandidates(options.path, { maxFiles: options.maxFiles, excludeDirs });
|
|
502
|
+
candidates = walk.files;
|
|
503
|
+
truncated = walkTruncation(walk);
|
|
410
504
|
}
|
|
505
|
+
// Every format but SARIF states a truncated scan in the report itself
|
|
506
|
+
// (#2528). SARIF has no slot chant fills for it, so there it goes to stderr.
|
|
507
|
+
const warnings: AuditWarning[] =
|
|
508
|
+
truncated && format === "sarif" ? [{ message: truncationNote(truncated, options.path) }] : [];
|
|
509
|
+
if (pathLexicons.notice !== undefined) warnings.push({ message: pathLexicons.notice });
|
|
510
|
+
const withWarnings = warnings.length > 0 ? { warnings } : {};
|
|
411
511
|
// A local target hands its directory to the classifier, so an input whose
|
|
412
512
|
// path names a directory carries the directory itself (#2217): terraform's
|
|
413
513
|
// parse-to-graph follows a root module's local `module` calls from there. A
|
|
@@ -438,15 +538,19 @@ export async function auditCommand(options: AuditCommandOptions): Promise<AuditC
|
|
|
438
538
|
const terraformStateFindings = auditTerraformState(candidates);
|
|
439
539
|
|
|
440
540
|
if (plugins.length === 0) {
|
|
441
|
-
const output =
|
|
442
|
-
|
|
541
|
+
const output =
|
|
542
|
+
format === "json"
|
|
543
|
+
? renderNoLexiconsJson(options.path, unclaimed, pathLexicons.paths, truncated)
|
|
544
|
+
: renderNoLexicons(options.path, unclaimed, pathLexicons.paths, truncated);
|
|
545
|
+
return { success: true, status: "no-lexicons", output, findings: [...secretsFindings, ...wranglerFindings, ...nginxFindings, ...terraformStateFindings], scanned: [], unclaimed, exitCode: NO_LEXICONS_EXIT_CODE, stream: format === "json" ? "stdout" : "stderr", ...withWarnings };
|
|
443
546
|
}
|
|
444
547
|
|
|
445
|
-
const missingLexiconNote = missingLexiconHint(unclaimed);
|
|
548
|
+
const missingLexiconNote = missingLexiconHint(unclaimed, pathLexicons.paths);
|
|
446
549
|
|
|
447
550
|
if (inputs.length === 0 && secretsFindings.length === 0 && wranglerFindings.length === 0 && nginxFindings.length === 0 && terraformStateFindings.length === 0) {
|
|
448
|
-
|
|
449
|
-
|
|
551
|
+
let output = `No auditable files found under ${options.path}.${missingLexiconNote ? ` ${missingLexiconNote}` : ""}`;
|
|
552
|
+
if (truncated) output += `\n\nNote: ${truncationNote(truncated, options.path)}`;
|
|
553
|
+
return { success: true, status: "ok", output, findings: [], scanned: [], unclaimed, exitCode: 0, ...withWarnings };
|
|
450
554
|
}
|
|
451
555
|
|
|
452
556
|
let findings: AuditFinding[] = [];
|
|
@@ -472,6 +576,7 @@ export async function auditCommand(options: AuditCommandOptions): Promise<AuditC
|
|
|
472
576
|
|
|
473
577
|
if (tier === "merge-worthy") findings = findings.filter((f) => isMergeWorthy(f, catalog));
|
|
474
578
|
const notes = coverageNotes(inputs);
|
|
579
|
+
if (truncated) notes.unshift(truncationNote(truncated, options.path));
|
|
475
580
|
if (missingLexiconNote) notes.push(missingLexiconNote);
|
|
476
581
|
if (suppressionStats.count > 0) {
|
|
477
582
|
notes.push(
|
|
@@ -520,7 +625,7 @@ export async function auditCommand(options: AuditCommandOptions): Promise<AuditC
|
|
|
520
625
|
switch (format) {
|
|
521
626
|
case "json": {
|
|
522
627
|
const snapshot = await buildSnapshot(options, scanned, isUrl);
|
|
523
|
-
output = JSON.stringify(buildReportJson(findings, { snapshot, toolVersion: options.toolVersion, catalog, unclaimed }), null, 2);
|
|
628
|
+
output = JSON.stringify(buildReportJson(findings, { snapshot, toolVersion: options.toolVersion, catalog, unclaimed, truncated }), null, 2);
|
|
524
629
|
break;
|
|
525
630
|
}
|
|
526
631
|
case "sarif":
|
|
@@ -531,7 +636,7 @@ export async function auditCommand(options: AuditCommandOptions): Promise<AuditC
|
|
|
531
636
|
break;
|
|
532
637
|
case "html": {
|
|
533
638
|
const snapshot = await buildSnapshot(options, scanned, isUrl);
|
|
534
|
-
output = renderHtml(findings, { files, resolveSha, resolveDigest, notes, snapshot, theme: options.theme, template: options.template, catalog });
|
|
639
|
+
output = renderHtml(findings, { files, resolveSha, resolveDigest, notes, snapshot, theme: options.theme, template: options.template, catalog, truncated });
|
|
535
640
|
break;
|
|
536
641
|
}
|
|
537
642
|
default:
|
|
@@ -545,22 +650,20 @@ export async function auditCommand(options: AuditCommandOptions): Promise<AuditC
|
|
|
545
650
|
} catch (err) {
|
|
546
651
|
return { success: false, output, findings, scanned, exitCode: 1, error: `Failed to write ${options.output}: ${err instanceof Error ? err.message : String(err)}` };
|
|
547
652
|
}
|
|
548
|
-
return { success: true, status: "ok", output, findings, scanned, unclaimed, exitCode, wroteTo: options.output, suppressedCount: suppressionStats.count };
|
|
653
|
+
return { success: true, status: "ok", output, findings, scanned, unclaimed, exitCode, wroteTo: options.output, suppressedCount: suppressionStats.count, ...withWarnings };
|
|
549
654
|
}
|
|
550
655
|
|
|
551
|
-
return { success: true, status: "ok", output, findings, scanned, unclaimed, exitCode, suppressedCount: suppressionStats.count };
|
|
656
|
+
return { success: true, status: "ok", output, findings, scanned, unclaimed, exitCode, suppressedCount: suppressionStats.count, ...withWarnings };
|
|
552
657
|
}
|
|
553
658
|
|
|
554
|
-
/** Print an audit result to stdout. */
|
|
659
|
+
/** Print an audit result to stdout, and its warnings to stderr. */
|
|
555
660
|
export function printAuditResult(result: AuditCommandResult): void {
|
|
556
661
|
if (!result.success) {
|
|
557
662
|
console.error(result.error ?? "Audit failed");
|
|
558
663
|
return;
|
|
559
664
|
}
|
|
560
|
-
if (result.wroteTo) {
|
|
561
|
-
|
|
562
|
-
return;
|
|
563
|
-
}
|
|
564
|
-
if (result.stream === "stderr") console.error(result.output);
|
|
665
|
+
if (result.wroteTo) console.error(`Wrote report to ${result.wroteTo}`);
|
|
666
|
+
else if (result.stream === "stderr") console.error(result.output);
|
|
565
667
|
else console.log(result.output);
|
|
668
|
+
for (const w of result.warnings ?? []) console.error(formatWarning(w));
|
|
566
669
|
}
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
* validates against its own schemas.
|
|
34
34
|
*/
|
|
35
35
|
|
|
36
|
+
import { lexiconNames as declaredLexiconNames } from "../../lexicon-module";
|
|
36
37
|
import { existsSync, readdirSync } from "fs";
|
|
37
38
|
import { join } from "path";
|
|
38
39
|
import { build, type BuildResult } from "../../build";
|
|
@@ -141,7 +142,7 @@ export async function checkExamplesBuild(lexiconDir: string): Promise<ExampleBui
|
|
|
141
142
|
const { config: exampleConfig } = await loadChantConfig(join(examplesDir, entry.name));
|
|
142
143
|
const lexiconNames =
|
|
143
144
|
exampleConfig.lexicons && exampleConfig.lexicons.length > 0
|
|
144
|
-
? exampleConfig.lexicons
|
|
145
|
+
? declaredLexiconNames(exampleConfig.lexicons)
|
|
145
146
|
: await detectLexicons(files);
|
|
146
147
|
if (lexiconNames.length === 0) {
|
|
147
148
|
results.push({
|
|
@@ -106,6 +106,28 @@ export async function coverageReportCheck(plugin: LexiconPlugin | undefined): Pr
|
|
|
106
106
|
};
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
/**
|
|
110
|
+
* #2535 — a lexicon that supplies member kinds publishes them as data at a
|
|
111
|
+
* `./workspace-kinds` subpath (ws-031). The file must be JSON, validate as
|
|
112
|
+
* kind data, and ship in the package. A lexicon without the subpath passes
|
|
113
|
+
* vacuously, like `coverageReport()` above. The workspace module loads only
|
|
114
|
+
* here, when the check runs.
|
|
115
|
+
*/
|
|
116
|
+
export async function workspaceKindsCheck(dir: string): Promise<CheckItem> {
|
|
117
|
+
const { readPackageKinds, kindsFileShips } = await import("../../workspace/kinds");
|
|
118
|
+
const read = readPackageKinds(dir);
|
|
119
|
+
const problems = [...read.problems];
|
|
120
|
+
if (read.file && problems.length === 0 && !kindsFileShips(dir, read.file)) {
|
|
121
|
+
problems.push(`${basename(read.file)} is not covered by package.json "files", so the published package would not carry it`);
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
name: "Any ./workspace-kinds subpath holds valid kind data",
|
|
125
|
+
tier: 1,
|
|
126
|
+
pass: problems.length === 0,
|
|
127
|
+
detail: problems.length > 0 ? problems.join("; ") : read.file ? `${read.kinds.length} kind(s): ${read.kinds.map((k) => k.name).join(", ")}` : undefined,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
109
131
|
// ── Check runner ─────────────────────────────────────────────────────
|
|
110
132
|
|
|
111
133
|
/**
|
|
@@ -434,6 +456,8 @@ export async function checkLexicon(dir: string): Promise<CheckResult> {
|
|
|
434
456
|
// now states the fact directly via `coverageReport()`.
|
|
435
457
|
items.push(await coverageReportCheck(plugin));
|
|
436
458
|
|
|
459
|
+
items.push(await workspaceKindsCheck(dir));
|
|
460
|
+
|
|
437
461
|
// ── Tier 2: Recommended ────────────────────────────────────────
|
|
438
462
|
|
|
439
463
|
const pluginContent = readOr(join(dir, "src/plugin.ts"));
|
|
@@ -396,3 +396,33 @@ describe("package-type-module (#1421)", () => {
|
|
|
396
396
|
});
|
|
397
397
|
});
|
|
398
398
|
});
|
|
399
|
+
|
|
400
|
+
describe("doctorCommand in a declared workspace (#2535)", () => {
|
|
401
|
+
test("shows a member of kind other as a warning, and adds nothing outside a workspace", async () => {
|
|
402
|
+
await withTestDir(async (testDir) => {
|
|
403
|
+
mkdirSync(join(testDir, ".git"));
|
|
404
|
+
mkdirSync(join(testDir, "app"));
|
|
405
|
+
mkdirSync(join(testDir, "docs"));
|
|
406
|
+
writeFileSync(join(testDir, "app", "chant.config.json"), "{}");
|
|
407
|
+
const before = await doctorCommand(join(testDir, "app"));
|
|
408
|
+
expect(before.checks.filter((c) => c.name.startsWith("workspace-"))).toEqual([]);
|
|
409
|
+
|
|
410
|
+
writeFileSync(
|
|
411
|
+
join(testDir, "chant.workspace.json"),
|
|
412
|
+
JSON.stringify({
|
|
413
|
+
name: "acme",
|
|
414
|
+
schema: 1,
|
|
415
|
+
members: [
|
|
416
|
+
{ name: "app", dir: "app", kind: "chant" },
|
|
417
|
+
{ name: "docs", dir: "docs", kind: "other", because: "a static site" },
|
|
418
|
+
],
|
|
419
|
+
}),
|
|
420
|
+
);
|
|
421
|
+
const report = await doctorCommand(join(testDir, "app"));
|
|
422
|
+
const workspace = report.checks.filter((c) => c.name.startsWith("workspace-"));
|
|
423
|
+
expect(workspace).toEqual([
|
|
424
|
+
{ name: "workspace-WSP009", status: "warn", message: expect.stringMatching(/^member docs \(docs\) is kind other, which chant does not read: a static site \(.*chant\.workspace\.json:1\)$/) },
|
|
425
|
+
]);
|
|
426
|
+
});
|
|
427
|
+
});
|
|
428
|
+
});
|
|
@@ -8,6 +8,7 @@ import { loadPlugins, resolveProjectLexicons } from "../plugins";
|
|
|
8
8
|
import { MCP_CONFIG_FILENAME, MCP_SETUP_COMMAND, mcpConfigPath } from "../mcp-config";
|
|
9
9
|
import { loadCapabilityPlugin } from "../../components/capability-plugin-loader";
|
|
10
10
|
import { isCapabilityPlugin } from "../../components/capability-plugin";
|
|
11
|
+
import { findWorkspaceRoot } from "../../project-root";
|
|
11
12
|
|
|
12
13
|
export interface DoctorCheck {
|
|
13
14
|
name: string;
|
|
@@ -95,6 +96,9 @@ export async function doctorCommand(path: string): Promise<DoctorReport> {
|
|
|
95
96
|
const lexicons = config?.lexicons as string[] | undefined;
|
|
96
97
|
if (lexicons && Array.isArray(lexicons)) {
|
|
97
98
|
for (const lex of lexicons) {
|
|
99
|
+
// chant #2520 — a lexicon declared by module path is project source,
|
|
100
|
+
// not a package `chant update` copies types from.
|
|
101
|
+
if (typeof lex !== "string") continue;
|
|
98
102
|
const lexDir = join(projectPath, ".chant", "types", `lexicon-${lex}`);
|
|
99
103
|
if (!existsSync(lexDir)) {
|
|
100
104
|
checks.push({ name: `lexicon-${lex}-types`, status: "fail", message: `.chant/types/lexicon-${lex}/ not found — run chant update` });
|
|
@@ -319,6 +323,23 @@ export async function doctorCommand(path: string): Promise<DoctorReport> {
|
|
|
319
323
|
}
|
|
320
324
|
}
|
|
321
325
|
|
|
326
|
+
// #2535: in a declared workspace, doctor shows the declaration checks, so
|
|
327
|
+
// a member of kind other shows as a warning. The walk up only tests
|
|
328
|
+
// whether files exist; nothing under workspace/ loads without a
|
|
329
|
+
// declaration, so level 0 is unchanged (#2525).
|
|
330
|
+
const workspace = findWorkspaceRoot(projectPath);
|
|
331
|
+
if (workspace) {
|
|
332
|
+
const { runDeclarationChecks } = await import("../../workspace/checks");
|
|
333
|
+
// The declaration and kind checks only: `chant workspace check` runs the
|
|
334
|
+
// member checks, which read every member's files.
|
|
335
|
+
const report = await runDeclarationChecks(workspace.dir, (file) => join(workspace.dir, file), { gather: false });
|
|
336
|
+
const shown = report.diagnostics.filter((d) => d.severity !== "info");
|
|
337
|
+
for (const d of shown) {
|
|
338
|
+
checks.push({ name: `workspace-${d.ruleId}`, status: d.severity === "error" ? "fail" : "warn", message: `${d.message} (${d.file}:${d.line})` });
|
|
339
|
+
}
|
|
340
|
+
if (shown.length === 0) checks.push({ name: "workspace-declaration", status: "pass", message: report.file });
|
|
341
|
+
}
|
|
342
|
+
|
|
322
343
|
return {
|
|
323
344
|
checks,
|
|
324
345
|
success: checks.every(c => c.status !== "fail"),
|
|
@@ -23,7 +23,8 @@ import { scanAgentConfigs } from "../../agents/discover";
|
|
|
23
23
|
import type { AgentRuntime, AgentScope } from "../../agents/types";
|
|
24
24
|
import type { AgentImportOutcome } from "../../agents/importer";
|
|
25
25
|
import type { LexiconPlugin } from "../../lexicon";
|
|
26
|
-
import { loadPlugin } from "../plugins";
|
|
26
|
+
import { loadPlugin, recordProjectLexicons, unknownPathLexiconsNotice } from "../plugins";
|
|
27
|
+
import { lexiconModulePath, lexiconSourceLabel } from "../../lexicon-module";
|
|
27
28
|
|
|
28
29
|
/** The lexicon used when `--lexicon` isn't given: the one that models agent workloads. */
|
|
29
30
|
export const DEFAULT_AGENT_LEXICON = "fountain";
|
|
@@ -91,18 +92,28 @@ export async function importAgentsCommand(opts: ImportAgentsOptions = {}): Promi
|
|
|
91
92
|
};
|
|
92
93
|
}
|
|
93
94
|
|
|
95
|
+
// chant#2578 — a lexicon the project's chant.config declares by path loads
|
|
96
|
+
// from that module, so read the declarations before loading the plugin.
|
|
97
|
+
// chant#2591 — read without running chant.config.ts.
|
|
98
|
+
if (!opts.pluginLoader) {
|
|
99
|
+
const notice = unknownPathLexiconsNotice(recordProjectLexicons(process.cwd()));
|
|
100
|
+
if (notice !== undefined) warnings.push(notice);
|
|
101
|
+
}
|
|
102
|
+
|
|
94
103
|
let plugin: LexiconPlugin;
|
|
95
104
|
try {
|
|
96
105
|
plugin = await (opts.pluginLoader ?? loadPlugin)(lexiconName);
|
|
97
106
|
} catch (err) {
|
|
107
|
+
const remedy =
|
|
108
|
+
lexiconModulePath(lexiconName) !== undefined
|
|
109
|
+
? `The ${lexiconName} lexicon is declared by path in chant.config.ts: ${lexiconSourceLabel(lexiconName)}`
|
|
110
|
+
: `Install it with: npm i @intentius/chant-lexicon-${lexiconName}`;
|
|
98
111
|
return {
|
|
99
112
|
success: false,
|
|
100
113
|
generatedFiles: [],
|
|
101
114
|
summary: { discovered: scan.sites.length, mapped: 0 },
|
|
102
115
|
warnings,
|
|
103
|
-
error:
|
|
104
|
-
`Could not load the ${lexiconName} lexicon: ${err instanceof Error ? err.message : String(err)}\n` +
|
|
105
|
-
`Install it with: npm i @intentius/chant-lexicon-${lexiconName}`,
|
|
116
|
+
error: `Could not load the ${lexiconName} lexicon: ${err instanceof Error ? err.message : String(err)}\n${remedy}`,
|
|
106
117
|
};
|
|
107
118
|
}
|
|
108
119
|
|
package/src/cli/commands/init.ts
CHANGED
|
@@ -3,7 +3,8 @@ import { join, resolve, dirname } from "path";
|
|
|
3
3
|
import { fileURLToPath } from "url";
|
|
4
4
|
import { createInterface } from "readline";
|
|
5
5
|
import { formatSuccess, formatWarning } from "../format";
|
|
6
|
-
import { loadPlugin } from "../plugins";
|
|
6
|
+
import { loadPlugin, recordProjectLexicons, unknownPathLexiconsNotice } from "../plugins";
|
|
7
|
+
import { lexiconModulePath, lexiconSourceLabel } from "../../lexicon-module";
|
|
7
8
|
import { MCP_CONFIG_FILENAME, detectPackageManager, generateMcpConfig, mcpConfigPath } from "../mcp-config";
|
|
8
9
|
|
|
9
10
|
/** Read the current chant package version from our own package.json. */
|
|
@@ -53,16 +54,27 @@ export interface InitResult {
|
|
|
53
54
|
warnings: string[];
|
|
54
55
|
/** Error message if failed */
|
|
55
56
|
error?: string;
|
|
57
|
+
/**
|
|
58
|
+
* chant#2578 — where the lexicon loads from when the directory's existing
|
|
59
|
+
* chant.config declares it by path. There is no package to depend on or
|
|
60
|
+
* install for it.
|
|
61
|
+
*/
|
|
62
|
+
lexiconModule?: string;
|
|
56
63
|
}
|
|
57
64
|
|
|
58
65
|
/**
|
|
59
66
|
* Generate package.json content
|
|
60
67
|
*/
|
|
61
|
-
function generatePackageJson(
|
|
68
|
+
function generatePackageJson(
|
|
69
|
+
lexicon: string,
|
|
70
|
+
extraScripts?: Record<string, string>,
|
|
71
|
+
lexiconIsPackage = true,
|
|
72
|
+
): string {
|
|
62
73
|
const ver = getChantVersion();
|
|
63
74
|
const dependencies: Record<string, string> = {
|
|
64
75
|
"@intentius/chant": `^${ver}`,
|
|
65
|
-
|
|
76
|
+
// chant#2578 — a lexicon declared by path is not a package to depend on.
|
|
77
|
+
...(lexiconIsPackage ? { [`@intentius/chant-lexicon-${lexicon}`]: `^${ver}` } : {}),
|
|
66
78
|
};
|
|
67
79
|
|
|
68
80
|
const pkg = {
|
|
@@ -252,6 +264,20 @@ export async function initCommand(options: InitOptions): Promise<InitResult> {
|
|
|
252
264
|
mkdirSync(targetDir, { recursive: true });
|
|
253
265
|
}
|
|
254
266
|
|
|
267
|
+
// chant#2578 — `--force` into an existing project keeps its chant.config.
|
|
268
|
+
// When that config declares the lexicon by path, it loads from the module
|
|
269
|
+
// and there is no package to depend on or install. chant#2591 — the config
|
|
270
|
+
// is read without running it; when that fails, init says so and treats the
|
|
271
|
+
// lexicon as a package.
|
|
272
|
+
let lexiconModule: string | undefined;
|
|
273
|
+
if (existsSync(join(targetDir, "chant.config.ts")) || existsSync(join(targetDir, "chant.config.json"))) {
|
|
274
|
+
const notice = unknownPathLexiconsNotice(recordProjectLexicons(targetDir));
|
|
275
|
+
if (notice !== undefined) warnings.push(notice);
|
|
276
|
+
if (lexiconModulePath(options.lexicon) !== undefined) {
|
|
277
|
+
lexiconModule = lexiconSourceLabel(options.lexicon, targetDir);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
255
281
|
// Load plugin early to get template set (used for scripts + source files)
|
|
256
282
|
let templateSet: import("../../lexicon").InitTemplateSet | undefined;
|
|
257
283
|
try {
|
|
@@ -272,7 +298,7 @@ export async function initCommand(options: InitOptions): Promise<InitResult> {
|
|
|
272
298
|
// Generate package.json
|
|
273
299
|
writeIfNotExists(
|
|
274
300
|
join(targetDir, "package.json"),
|
|
275
|
-
generatePackageJson(options.lexicon, templateSet?.scripts),
|
|
301
|
+
generatePackageJson(options.lexicon, templateSet?.scripts, lexiconModule === undefined),
|
|
276
302
|
"package.json",
|
|
277
303
|
createdFiles,
|
|
278
304
|
warnings,
|
|
@@ -361,29 +387,32 @@ export async function initCommand(options: InitOptions): Promise<InitResult> {
|
|
|
361
387
|
warnings,
|
|
362
388
|
);
|
|
363
389
|
|
|
364
|
-
// Scaffold .chant/types/lexicon-{lexicon}/ stub
|
|
365
|
-
|
|
366
|
-
|
|
390
|
+
// Scaffold .chant/types/lexicon-{lexicon}/ stub. A lexicon declared by path
|
|
391
|
+
// is imported by its path, so it has no package name to stub (chant#2578).
|
|
392
|
+
if (lexiconModule === undefined) {
|
|
393
|
+
const lexiconTypesDir = join(targetDir, ".chant", "types", `lexicon-${options.lexicon}`);
|
|
394
|
+
mkdirSync(lexiconTypesDir, { recursive: true });
|
|
367
395
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
396
|
+
writeIfNotExists(
|
|
397
|
+
join(lexiconTypesDir, "package.json"),
|
|
398
|
+
JSON.stringify(
|
|
399
|
+
{ name: `@intentius/chant-lexicon-${options.lexicon}`, version: "0.0.0", types: "./index.d.ts" },
|
|
400
|
+
null,
|
|
401
|
+
2,
|
|
402
|
+
),
|
|
403
|
+
`.chant/types/lexicon-${options.lexicon}/package.json`,
|
|
404
|
+
createdFiles,
|
|
405
|
+
warnings,
|
|
406
|
+
);
|
|
379
407
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
408
|
+
writeIfNotExists(
|
|
409
|
+
join(lexiconTypesDir, "index.d.ts"),
|
|
410
|
+
`// Lexicon type stubs — run "chant update" to sync full types\nexport {};\n`,
|
|
411
|
+
`.chant/types/lexicon-${options.lexicon}/index.d.ts`,
|
|
412
|
+
createdFiles,
|
|
413
|
+
warnings,
|
|
414
|
+
);
|
|
415
|
+
}
|
|
387
416
|
|
|
388
417
|
// Generate the project's MCP config. It lands in the directory init was
|
|
389
418
|
// pointed at, like everything else init produces, and at the path
|
|
@@ -421,10 +450,27 @@ export async function initCommand(options: InitOptions): Promise<InitResult> {
|
|
|
421
450
|
// Skills are optional — don't fail init if plugin isn't installed yet
|
|
422
451
|
}
|
|
423
452
|
|
|
453
|
+
// `--template` records where the files came from (#2540, ws-047). Plain
|
|
454
|
+
// init writes no lock and never loads the lineage module.
|
|
455
|
+
if (options.template && templateSet) {
|
|
456
|
+
const { writeTemplateLock } = await import("../../workspace/lineage-init");
|
|
457
|
+
const lock = writeTemplateLock({
|
|
458
|
+
targetDir,
|
|
459
|
+
lexicon: options.lexicon,
|
|
460
|
+
template: options.template,
|
|
461
|
+
createdFiles,
|
|
462
|
+
chantVersion: getChantVersion(),
|
|
463
|
+
lexiconModule,
|
|
464
|
+
});
|
|
465
|
+
if (lock) createdFiles.push(lock);
|
|
466
|
+
else warnings.push(".chant/workspace.lock.json already exists, skipping");
|
|
467
|
+
}
|
|
468
|
+
|
|
424
469
|
return {
|
|
425
470
|
success: true,
|
|
426
471
|
createdFiles,
|
|
427
472
|
warnings,
|
|
473
|
+
...(lexiconModule !== undefined ? { lexiconModule } : {}),
|
|
428
474
|
};
|
|
429
475
|
}
|
|
430
476
|
|
|
@@ -453,6 +499,11 @@ export async function printInitResult(
|
|
|
453
499
|
|
|
454
500
|
console.log("");
|
|
455
501
|
|
|
502
|
+
if (result.lexiconModule !== undefined) {
|
|
503
|
+
console.log(`The lexicon loads from ${result.lexiconModule}, as chant.config.ts declares. It has no package to install.`);
|
|
504
|
+
console.log("");
|
|
505
|
+
}
|
|
506
|
+
|
|
456
507
|
const pm = detectPackageManager(options?.cwd);
|
|
457
508
|
|
|
458
509
|
// Interactive install prompt
|