coding-agent-skills 0.2.8
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/AGENTS.md +44 -0
- package/CHANGELOG.md +205 -0
- package/CONTRIBUTING.md +54 -0
- package/LICENSE +21 -0
- package/README.md +85 -0
- package/ROADMAP.md +87 -0
- package/RUNBOOK.md +47 -0
- package/bin/coding-agent-skills +75 -0
- package/contracts/evidence-pack/README.md +22 -0
- package/contracts/evidence-pack/evidence-pack.example.json +60 -0
- package/contracts/evidence-pack/evidence-pack.example.md +49 -0
- package/contracts/evidence-pack/evidence-pack.schema.json +156 -0
- package/docs/adapters/README.md +82 -0
- package/docs/adapters/discovery.md +50 -0
- package/docs/adapters/external-adapters.md +42 -0
- package/docs/adapters/project-installation.md +135 -0
- package/docs/adapters/real-project-adoption.md +193 -0
- package/docs/adapters/upgrade-evidence.md +67 -0
- package/docs/adapters/upgrades.md +83 -0
- package/docs/architecture/README.md +23 -0
- package/docs/authoring/README.md +54 -0
- package/docs/evidence-bundles/README.md +94 -0
- package/docs/privacy/README.md +26 -0
- package/docs/release/README.md +42 -0
- package/docs/release/npm-package.md +85 -0
- package/docs/safety/README.md +94 -0
- package/docs/testing/README.md +100 -0
- package/docs/usage/README.md +89 -0
- package/docs/versioning/README.md +30 -0
- package/docs/versioning/adapter-compatibility.md +54 -0
- package/examples/README.md +12 -0
- package/examples/adapters/README.md +9 -0
- package/examples/adapters/documentation-precedence.json +62 -0
- package/examples/adapters/narrow-repo-map.json +64 -0
- package/examples/adapters/runtime-status-hints.json +76 -0
- package/examples/command-policies/README.md +3 -0
- package/examples/command-policies/build-verify.json +57 -0
- package/examples/command-policies/git-preflight.json +44 -0
- package/examples/command-policies/llm-drift-control.json +45 -0
- package/examples/command-policies/repo-map.json +59 -0
- package/examples/command-policies/runtime-truth.json +59 -0
- package/examples/evidence-packs/README.md +3 -0
- package/examples/evidence-packs/build-verify.json +68 -0
- package/examples/evidence-packs/git-preflight.json +55 -0
- package/examples/evidence-packs/llm-drift-control.json +55 -0
- package/examples/evidence-packs/repo-map.json +55 -0
- package/examples/evidence-packs/runtime-truth.json +55 -0
- package/examples/manifests/README.md +3 -0
- package/examples/manifests/build-verify.json +14 -0
- package/examples/manifests/git-preflight.json +14 -0
- package/examples/manifests/llm-drift-control.json +14 -0
- package/examples/manifests/repo-map.json +14 -0
- package/examples/manifests/runtime-truth.json +14 -0
- package/examples/upgrade-evidence/README.md +14 -0
- package/examples/upgrade-evidence/chain-fail.evidence.json +155 -0
- package/examples/upgrade-evidence/chain-fail.evidence.md +14 -0
- package/examples/upgrade-evidence/chain-pass.evidence.json +156 -0
- package/examples/upgrade-evidence/stale-pin.evidence.json +117 -0
- package/examples/upgrade-evidence/unsafe-upgrade.evidence.json +128 -0
- package/examples/upgrade-evidence/valid-upgrade.evidence.json +105 -0
- package/examples/upgrade-evidence/valid-upgrade.evidence.md +13 -0
- package/examples/workflows/README.md +3 -0
- package/examples/workflows/build-verify.md +20 -0
- package/examples/workflows/git-preflight.md +18 -0
- package/examples/workflows/llm-drift-control.md +16 -0
- package/examples/workflows/repo-map.md +20 -0
- package/examples/workflows/runtime-truth.md +17 -0
- package/package.json +58 -0
- package/runs/skill-runs.md +162 -0
- package/schemas/adapter-upgrade-evidence.schema.json +443 -0
- package/schemas/archive-index.schema.json +174 -0
- package/schemas/archive-report.schema.json +322 -0
- package/schemas/command-policy.schema.json +125 -0
- package/schemas/evidence-bundle.schema.json +394 -0
- package/schemas/project-adapter-installation.schema.json +127 -0
- package/schemas/project-adapter.schema.json +328 -0
- package/schemas/skill-manifest.schema.json +40 -0
- package/scripts/check-adapter-upgrade-chain.mjs +32 -0
- package/scripts/check-adapter-upgrade.mjs +31 -0
- package/scripts/lib/adapter-discovery.mjs +441 -0
- package/scripts/lib/adapter-repo-map.mjs +358 -0
- package/scripts/lib/adapter-upgrade-chain.mjs +261 -0
- package/scripts/lib/adapter-upgrade.mjs +434 -0
- package/scripts/lib/evidence-bundle.mjs +831 -0
- package/scripts/lib/pack-rules.mjs +704 -0
- package/scripts/lib/project-adapter-installation.mjs +327 -0
- package/scripts/lib/safe-evidence-output.mjs +92 -0
- package/scripts/lib/schema-validator.mjs +146 -0
- package/scripts/lib/semver.mjs +54 -0
- package/scripts/lib/upgrade-evidence.mjs +276 -0
- package/scripts/render-adapter-repo-map.mjs +8 -0
- package/scripts/render-evidence-archive-report.mjs +18 -0
- package/scripts/run-next +220 -0
- package/scripts/test-pack.mjs +2232 -0
- package/scripts/validate-adapters.mjs +10 -0
- package/scripts/validate-maintainer-loop.mjs +146 -0
- package/scripts/validate-pack.mjs +950 -0
- package/scripts/validate-project-adapters.mjs +8 -0
- package/scripts/verify-evidence-bundle.mjs +18 -0
- package/skills/build-verify/SKILL.md +62 -0
- package/skills/build-verify/adapter-interface.md +7 -0
- package/skills/build-verify/agents/openai.yaml +4 -0
- package/skills/build-verify/checklist.md +12 -0
- package/skills/build-verify/evidence-template.md +11 -0
- package/skills/build-verify/examples.md +16 -0
- package/skills/build-verify/failure-modes.md +14 -0
- package/skills/git-preflight/SKILL.md +65 -0
- package/skills/git-preflight/adapter-interface.md +7 -0
- package/skills/git-preflight/agents/openai.yaml +4 -0
- package/skills/git-preflight/checklist.md +11 -0
- package/skills/git-preflight/evidence-template.md +10 -0
- package/skills/git-preflight/examples.md +18 -0
- package/skills/git-preflight/failure-modes.md +13 -0
- package/skills/llm-drift-control/SKILL.md +67 -0
- package/skills/llm-drift-control/adapter-interface.md +7 -0
- package/skills/llm-drift-control/agents/openai.yaml +4 -0
- package/skills/llm-drift-control/checklist.md +11 -0
- package/skills/llm-drift-control/evidence-template.md +13 -0
- package/skills/llm-drift-control/examples.md +15 -0
- package/skills/llm-drift-control/failure-modes.md +13 -0
- package/skills/repo-map/SKILL.md +71 -0
- package/skills/repo-map/adapter-interface.md +18 -0
- package/skills/repo-map/agents/openai.yaml +4 -0
- package/skills/repo-map/checklist.md +15 -0
- package/skills/repo-map/evidence-template.md +29 -0
- package/skills/repo-map/examples.md +19 -0
- package/skills/repo-map/failure-modes.md +16 -0
- package/skills/runtime-truth/SKILL.md +62 -0
- package/skills/runtime-truth/adapter-interface.md +7 -0
- package/skills/runtime-truth/agents/openai.yaml +4 -0
- package/skills/runtime-truth/checklist.md +11 -0
- package/skills/runtime-truth/evidence-template.md +12 -0
- package/skills/runtime-truth/examples.md +20 -0
- package/skills/runtime-truth/failure-modes.md +13 -0
- package/tests/README.md +44 -0
- package/tests/adapters/README.md +15 -0
- package/tests/completion/README.md +15 -0
- package/tests/evidence/README.md +15 -0
- package/tests/fixtures/README.md +23 -0
- package/tests/fixtures/adapters/allow-deploy.json +60 -0
- package/tests/fixtures/adapters/allow-git-push.json +60 -0
- package/tests/fixtures/adapters/expand-scope.json +53 -0
- package/tests/fixtures/adapters/expose-secrets.json +53 -0
- package/tests/fixtures/adapters/incompatible-version.json +53 -0
- package/tests/fixtures/adapters/override-audit-only.json +53 -0
- package/tests/fixtures/adapters/redefine-completion.json +53 -0
- package/tests/fixtures/adapters/remove-required-evidence.json +53 -0
- package/tests/fixtures/adapters/suppress-failures.json +53 -0
- package/tests/fixtures/adapters/valid-narrowing.json +53 -0
- package/tests/fixtures/adapters/valid-repo-map.json +53 -0
- package/tests/fixtures/adapters/weakening-repo-map.json +42 -0
- package/tests/fixtures/completion/cases.json +143 -0
- package/tests/fixtures/completion/false-complete.json +51 -0
- package/tests/fixtures/evidence-bundles/advisory-review-soon/archive/evidence-archive-index.json +52 -0
- package/tests/fixtures/evidence-bundles/advisory-review-soon/evidence/repo-map.evidence.json +68 -0
- package/tests/fixtures/evidence-bundles/advisory-review-soon/evidence/valid-upgrade.evidence.json +105 -0
- package/tests/fixtures/evidence-bundles/advisory-review-soon/evidence-bundle.json +109 -0
- package/tests/fixtures/evidence-bundles/invalid-archive/archive/evidence-archive-index.json +52 -0
- package/tests/fixtures/evidence-bundles/invalid-archive/evidence/repo-map.evidence.json +68 -0
- package/tests/fixtures/evidence-bundles/invalid-archive/evidence/valid-upgrade.evidence.json +105 -0
- package/tests/fixtures/evidence-bundles/invalid-archive/evidence-bundle.json +109 -0
- package/tests/fixtures/evidence-bundles/invalid-archive-index/archive/evidence-archive-index.json +52 -0
- package/tests/fixtures/evidence-bundles/invalid-archive-index/evidence/repo-map.evidence.json +68 -0
- package/tests/fixtures/evidence-bundles/invalid-archive-index/evidence/valid-upgrade.evidence.json +105 -0
- package/tests/fixtures/evidence-bundles/invalid-archive-index/evidence-bundle.json +109 -0
- package/tests/fixtures/evidence-bundles/invalid-hash/archive/evidence-archive-index.json +52 -0
- package/tests/fixtures/evidence-bundles/invalid-hash/evidence/repo-map.evidence.json +68 -0
- package/tests/fixtures/evidence-bundles/invalid-hash/evidence/valid-upgrade.evidence.json +105 -0
- package/tests/fixtures/evidence-bundles/invalid-hash/evidence-bundle.json +109 -0
- package/tests/fixtures/evidence-bundles/invalid-missing-entry/archive/evidence-archive-index.json +52 -0
- package/tests/fixtures/evidence-bundles/invalid-missing-entry/evidence/repo-map.evidence.json +68 -0
- package/tests/fixtures/evidence-bundles/invalid-missing-entry/evidence/valid-upgrade.evidence.json +105 -0
- package/tests/fixtures/evidence-bundles/invalid-missing-entry/evidence-bundle.json +109 -0
- package/tests/fixtures/evidence-bundles/invalid-path/archive/evidence-archive-index.json +52 -0
- package/tests/fixtures/evidence-bundles/invalid-path/evidence/repo-map.evidence.json +68 -0
- package/tests/fixtures/evidence-bundles/invalid-path/evidence/valid-upgrade.evidence.json +105 -0
- package/tests/fixtures/evidence-bundles/invalid-path/evidence-bundle.json +109 -0
- package/tests/fixtures/evidence-bundles/invalid-provenance/archive/evidence-archive-index.json +52 -0
- package/tests/fixtures/evidence-bundles/invalid-provenance/evidence/repo-map.evidence.json +68 -0
- package/tests/fixtures/evidence-bundles/invalid-provenance/evidence/valid-upgrade.evidence.json +105 -0
- package/tests/fixtures/evidence-bundles/invalid-provenance/evidence-bundle.json +109 -0
- package/tests/fixtures/evidence-bundles/invalid-regression/archive/evidence-archive-index.json +52 -0
- package/tests/fixtures/evidence-bundles/invalid-regression/evidence/repo-map.evidence.json +68 -0
- package/tests/fixtures/evidence-bundles/invalid-regression/evidence/valid-upgrade.evidence.json +105 -0
- package/tests/fixtures/evidence-bundles/invalid-regression/evidence-bundle.json +113 -0
- package/tests/fixtures/evidence-bundles/invalid-retention/archive/evidence-archive-index.json +52 -0
- package/tests/fixtures/evidence-bundles/invalid-retention/evidence/repo-map.evidence.json +68 -0
- package/tests/fixtures/evidence-bundles/invalid-retention/evidence/valid-upgrade.evidence.json +105 -0
- package/tests/fixtures/evidence-bundles/invalid-retention/evidence-bundle.json +109 -0
- package/tests/fixtures/evidence-bundles/invalid-signature-plan/archive/evidence-archive-index.json +52 -0
- package/tests/fixtures/evidence-bundles/invalid-signature-plan/evidence/repo-map.evidence.json +68 -0
- package/tests/fixtures/evidence-bundles/invalid-signature-plan/evidence/valid-upgrade.evidence.json +105 -0
- package/tests/fixtures/evidence-bundles/invalid-signature-plan/evidence-bundle.json +109 -0
- package/tests/fixtures/evidence-bundles/valid-bundle/archive/evidence-archive-index.json +52 -0
- package/tests/fixtures/evidence-bundles/valid-bundle/evidence/repo-map.evidence.json +68 -0
- package/tests/fixtures/evidence-bundles/valid-bundle/evidence/valid-upgrade.evidence.json +105 -0
- package/tests/fixtures/evidence-bundles/valid-bundle/evidence-bundle.json +109 -0
- package/tests/fixtures/external-adapters/empty/README.md +3 -0
- package/tests/fixtures/external-adapters/invalid-completion-override/.coding-agent/adapters/completion/adapter.json +53 -0
- package/tests/fixtures/external-adapters/invalid-deploy/.coding-agent/adapters/deploy/adapter.json +60 -0
- package/tests/fixtures/external-adapters/invalid-evidence-suppression/.coding-agent/adapters/evidence/adapter.json +53 -0
- package/tests/fixtures/external-adapters/invalid-failure-suppression/.coding-agent/adapters/failures/adapter.json +53 -0
- package/tests/fixtures/external-adapters/invalid-git-push/.coding-agent/adapters/publish/adapter.json +60 -0
- package/tests/fixtures/external-adapters/invalid-malformed/.coding-agent/adapters/malformed/adapter.json +1 -0
- package/tests/fixtures/external-adapters/invalid-malformed/malformed-adapter.txt +1 -0
- package/tests/fixtures/external-adapters/invalid-mode-escalation/.coding-agent/adapters/mode/adapter.json +53 -0
- package/tests/fixtures/external-adapters/invalid-path-traversal/.coding-agent/adapters/path/adapter.json +53 -0
- package/tests/fixtures/external-adapters/invalid-restriction-removal/.coding-agent/adapters/restrictions/adapter.json +52 -0
- package/tests/fixtures/external-adapters/invalid-scope-expansion/.coding-agent/adapters/scope/adapter.json +53 -0
- package/tests/fixtures/external-adapters/invalid-secret-exposure/.coding-agent/adapters/secrets/adapter.json +53 -0
- package/tests/fixtures/external-adapters/invalid-skill-id/.coding-agent/adapters/skill/adapter.json +53 -0
- package/tests/fixtures/external-adapters/invalid-skill-version/.coding-agent/adapters/skill-version/adapter.json +53 -0
- package/tests/fixtures/external-adapters/invalid-unknown-manifest/.coding-agent/adapters/unknown/manifest.json +1 -0
- package/tests/fixtures/external-adapters/invalid-version/.coding-agent/adapters/version/adapter.json +53 -0
- package/tests/fixtures/external-adapters/mixed/.coding-agent/adapters/invalid/adapter.json +60 -0
- package/tests/fixtures/external-adapters/mixed/.coding-agent/adapters/valid/adapter.json +53 -0
- package/tests/fixtures/external-adapters/valid-basic/.coding-agent/adapters/basic/adapter.json +53 -0
- package/tests/fixtures/external-adapters/valid-doc-precedence/coding-agent/adapters/docs/adapter.json +53 -0
- package/tests/fixtures/external-adapters/valid-runtime-status/adapters/coding-agent/runtime/adapter.json +65 -0
- package/tests/fixtures/mutation/cases.json +87 -0
- package/tests/fixtures/mutation/snapshot-target/README.md +3 -0
- package/tests/fixtures/mutation/snapshot-target/state.json +4 -0
- package/tests/fixtures/policy/commands.json +164 -0
- package/tests/fixtures/policy/properties.json +126 -0
- package/tests/fixtures/privacy/cases.json +47 -0
- package/tests/fixtures/project-adapter-installation/invalid-adapter-location/.agents/adapters/basic/adapter.json +53 -0
- package/tests/fixtures/project-adapter-installation/invalid-adapter-location/.coding-agent/skills.json +23 -0
- package/tests/fixtures/project-adapter-installation/invalid-adapter-schema-version/.coding-agent/adapters/basic/adapter.json +53 -0
- package/tests/fixtures/project-adapter-installation/invalid-adapter-schema-version/.coding-agent/skills.json +23 -0
- package/tests/fixtures/project-adapter-installation/invalid-adapter-version-mismatch/.coding-agent/adapters/basic/adapter.json +53 -0
- package/tests/fixtures/project-adapter-installation/invalid-adapter-version-mismatch/.coding-agent/skills.json +23 -0
- package/tests/fixtures/project-adapter-installation/invalid-bad-semver/.coding-agent/adapters/basic/adapter.json +53 -0
- package/tests/fixtures/project-adapter-installation/invalid-bad-semver/.coding-agent/skills.json +23 -0
- package/tests/fixtures/project-adapter-installation/invalid-completion-override/.coding-agent/adapters/basic/adapter.json +53 -0
- package/tests/fixtures/project-adapter-installation/invalid-completion-override/.coding-agent/skills.json +23 -0
- package/tests/fixtures/project-adapter-installation/invalid-failure-suppression/.coding-agent/adapters/basic/adapter.json +53 -0
- package/tests/fixtures/project-adapter-installation/invalid-failure-suppression/.coding-agent/skills.json +23 -0
- package/tests/fixtures/project-adapter-installation/invalid-missing-declaration/.coding-agent/adapters/basic/adapter.json +53 -0
- package/tests/fixtures/project-adapter-installation/invalid-mode-escalation/.coding-agent/adapters/basic/adapter.json +53 -0
- package/tests/fixtures/project-adapter-installation/invalid-mode-escalation/.coding-agent/skills.json +23 -0
- package/tests/fixtures/project-adapter-installation/invalid-path-traversal/.coding-agent/adapters/basic/adapter.json +53 -0
- package/tests/fixtures/project-adapter-installation/invalid-path-traversal/.coding-agent/skills.json +23 -0
- package/tests/fixtures/project-adapter-installation/invalid-scope-expansion/.coding-agent/adapters/basic/adapter.json +53 -0
- package/tests/fixtures/project-adapter-installation/invalid-scope-expansion/.coding-agent/skills.json +23 -0
- package/tests/fixtures/project-adapter-installation/invalid-secret-exposure/.coding-agent/adapters/basic/adapter.json +53 -0
- package/tests/fixtures/project-adapter-installation/invalid-secret-exposure/.coding-agent/skills.json +23 -0
- package/tests/fixtures/project-adapter-installation/invalid-skill-mismatch/.coding-agent/adapters/basic/adapter.json +53 -0
- package/tests/fixtures/project-adapter-installation/invalid-skill-mismatch/.coding-agent/skills.json +23 -0
- package/tests/fixtures/project-adapter-installation/invalid-unknown-skill/.coding-agent/adapters/basic/adapter.json +53 -0
- package/tests/fixtures/project-adapter-installation/invalid-unknown-skill/.coding-agent/skills.json +23 -0
- package/tests/fixtures/project-adapter-installation/invalid-unsupported-core-version/.coding-agent/adapters/basic/adapter.json +53 -0
- package/tests/fixtures/project-adapter-installation/invalid-unsupported-core-version/.coding-agent/skills.json +23 -0
- package/tests/fixtures/project-adapter-installation/invalid-weakens-restrictions/.coding-agent/adapters/basic/adapter.json +52 -0
- package/tests/fixtures/project-adapter-installation/invalid-weakens-restrictions/.coding-agent/skills.json +23 -0
- package/tests/fixtures/project-adapter-installation/valid-compatible-range/coding-agent/adapters/docs/adapter.json +53 -0
- package/tests/fixtures/project-adapter-installation/valid-compatible-range/coding-agent.skills.json +23 -0
- package/tests/fixtures/project-adapter-installation/valid-exact-pin/.coding-agent/adapters/basic/adapter.json +53 -0
- package/tests/fixtures/project-adapter-installation/valid-exact-pin/.coding-agent/skills.json +23 -0
- package/tests/fixtures/project-adapter-installation/valid-multiple-adapters/.coding-agent/skills.json +28 -0
- package/tests/fixtures/project-adapter-installation/valid-multiple-adapters/adapters/coding-agent/repo/adapter.json +53 -0
- package/tests/fixtures/project-adapter-installation/valid-multiple-adapters/adapters/coding-agent/runtime/adapter.json +58 -0
- package/tests/fixtures/project-adapter-upgrade-chains/broken-compatibility-chain/01-current/.coding-agent/adapters/fixture-chain-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrade-chains/broken-compatibility-chain/01-current/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrade-chains/broken-compatibility-chain/02-incompatible/.coding-agent/adapters/fixture-chain-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrade-chains/broken-compatibility-chain/02-incompatible/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrade-chains/broken-compatibility-chain/03-target/.coding-agent/adapters/fixture-chain-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrade-chains/broken-compatibility-chain/03-target/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrade-chains/schema-drift-chain/01-current/.coding-agent/adapters/fixture-chain-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrade-chains/schema-drift-chain/01-current/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrade-chains/schema-drift-chain/02-schema-drift/.coding-agent/adapters/fixture-chain-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrade-chains/schema-drift-chain/02-schema-drift/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrade-chains/skill-drift-chain/01-current/.coding-agent/adapters/fixture-chain-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrade-chains/skill-drift-chain/01-current/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrade-chains/skill-drift-chain/02-skill-drift/.coding-agent/adapters/fixture-chain-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrade-chains/skill-drift-chain/02-skill-drift/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrade-chains/stale-pin-chain/01-current/.coding-agent/adapters/fixture-chain-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrade-chains/stale-pin-chain/01-current/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrade-chains/stale-pin-chain/02-stale/.coding-agent/adapters/fixture-chain-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrade-chains/stale-pin-chain/02-stale/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrade-chains/stale-pin-chain/03-target/.coding-agent/adapters/fixture-chain-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrade-chains/stale-pin-chain/03-target/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrade-chains/unsafe-weakening-chain/01-current/.coding-agent/adapters/fixture-chain-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrade-chains/unsafe-weakening-chain/01-current/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrade-chains/unsafe-weakening-chain/02-safe/.coding-agent/adapters/fixture-chain-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrade-chains/unsafe-weakening-chain/02-safe/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrade-chains/unsafe-weakening-chain/03-weakens-restrictions/.coding-agent/adapters/fixture-chain-adapter/adapter.json +69 -0
- package/tests/fixtures/project-adapter-upgrade-chains/unsafe-weakening-chain/03-weakens-restrictions/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrade-chains/valid-chain/01-current/.coding-agent/adapters/fixture-chain-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrade-chains/valid-chain/01-current/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrade-chains/valid-chain/02-upgrade/.coding-agent/adapters/fixture-chain-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrade-chains/valid-chain/02-upgrade/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrade-chains/valid-chain/03-upgrade/.coding-agent/adapters/fixture-chain-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrade-chains/valid-chain/03-upgrade/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrade-chains/valid-chain/04-upgrade/.coding-agent/adapters/fixture-chain-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrade-chains/valid-chain/04-upgrade/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrade-chains/valid-chain/05-upgrade/.coding-agent/adapters/fixture-chain-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrade-chains/valid-chain/05-upgrade/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrade-chains/valid-chain/06-upgrade/.coding-agent/adapters/fixture-chain-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrade-chains/valid-chain/06-upgrade/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrade-chains/valid-chain/07-upgrade/.coding-agent/adapters/fixture-chain-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrade-chains/valid-chain/07-upgrade/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrades/adapter-schema-drift/after/.coding-agent/adapters/fixture-upgrade-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrades/adapter-schema-drift/after/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrades/adapter-schema-drift/before/.coding-agent/adapters/fixture-upgrade-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrades/adapter-schema-drift/before/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrades/safe-upgrade-preserves-restrictions/after/.coding-agent/adapters/fixture-upgrade-adapter/adapter.json +71 -0
- package/tests/fixtures/project-adapter-upgrades/safe-upgrade-preserves-restrictions/after/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrades/safe-upgrade-preserves-restrictions/before/.coding-agent/adapters/fixture-upgrade-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrades/safe-upgrade-preserves-restrictions/before/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrades/skill-compatibility-drift/after/.coding-agent/adapters/fixture-upgrade-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrades/skill-compatibility-drift/after/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrades/skill-compatibility-drift/before/.coding-agent/adapters/fixture-upgrade-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrades/skill-compatibility-drift/before/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrades/stale-compatible-range/after/.coding-agent/adapters/fixture-upgrade-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrades/stale-compatible-range/after/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrades/stale-compatible-range/before/.coding-agent/adapters/fixture-upgrade-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrades/stale-compatible-range/before/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrades/stale-exact-pin/after/.coding-agent/adapters/fixture-upgrade-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrades/stale-exact-pin/after/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrades/stale-exact-pin/before/.coding-agent/adapters/fixture-upgrade-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrades/stale-exact-pin/before/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrades/unsafe-upgrade-mode-escalation/after/.coding-agent/adapters/fixture-upgrade-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrades/unsafe-upgrade-mode-escalation/after/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrades/unsafe-upgrade-mode-escalation/before/.coding-agent/adapters/fixture-upgrade-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrades/unsafe-upgrade-mode-escalation/before/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrades/unsafe-upgrade-removes-evidence/after/.coding-agent/adapters/fixture-upgrade-adapter/adapter.json +69 -0
- package/tests/fixtures/project-adapter-upgrades/unsafe-upgrade-removes-evidence/after/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrades/unsafe-upgrade-removes-evidence/before/.coding-agent/adapters/fixture-upgrade-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrades/unsafe-upgrade-removes-evidence/before/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrades/unsafe-upgrade-weakens-restrictions/after/.coding-agent/adapters/fixture-upgrade-adapter/adapter.json +69 -0
- package/tests/fixtures/project-adapter-upgrades/unsafe-upgrade-weakens-restrictions/after/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrades/unsafe-upgrade-weakens-restrictions/before/.coding-agent/adapters/fixture-upgrade-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrades/unsafe-upgrade-weakens-restrictions/before/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrades/unsupported-future-core/after/.coding-agent/adapters/fixture-upgrade-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrades/unsupported-future-core/after/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrades/unsupported-future-core/before/.coding-agent/adapters/fixture-upgrade-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrades/unsupported-future-core/before/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrades/unsupported-old-core/after/.coding-agent/adapters/fixture-upgrade-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrades/unsupported-old-core/after/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrades/unsupported-old-core/before/.coding-agent/adapters/fixture-upgrade-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrades/unsupported-old-core/before/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrades/valid-upgrade/after/.coding-agent/adapters/fixture-upgrade-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrades/valid-upgrade/after/.coding-agent/skills.json +27 -0
- package/tests/fixtures/project-adapter-upgrades/valid-upgrade/before/.coding-agent/adapters/fixture-upgrade-adapter/adapter.json +70 -0
- package/tests/fixtures/project-adapter-upgrades/valid-upgrade/before/.coding-agent/skills.json +27 -0
- package/tests/fixtures/sample-repo/.env.example +1 -0
- package/tests/fixtures/sample-repo/README.md +4 -0
- package/tests/fixtures/sample-repo/docs/architecture.md +3 -0
- package/tests/fixtures/sample-repo/package.json +11 -0
- package/tests/fixtures/sample-repo/src/index.js +3 -0
- package/tests/fixtures/sample-repo/test/index.test.js +8 -0
- package/tests/fixtures/triggers/cases.json +101 -0
- package/tests/policy/README.md +16 -0
- package/tests/privacy/README.md +14 -0
- package/tests/safety/README.md +17 -0
- package/tests/trigger/README.md +11 -0
- package/work-ledger.md +159 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
ADAPTER_SCHEMA_VERSION,
|
|
7
|
+
EXTERNAL_ADAPTER_LOCATIONS,
|
|
8
|
+
readSafeJsonFile,
|
|
9
|
+
validateExternalAdapters,
|
|
10
|
+
} from "./adapter-discovery.mjs";
|
|
11
|
+
import { PILOT_SKILLS, PILOT_VERSION } from "./pack-rules.mjs";
|
|
12
|
+
import { validateValue } from "./schema-validator.mjs";
|
|
13
|
+
import { parseSemver, parseVersionPin, satisfiesVersionPin } from "./semver.mjs";
|
|
14
|
+
|
|
15
|
+
export const PROJECT_DECLARATION_VERSION = "1.0.0";
|
|
16
|
+
export const PROJECT_DECLARATION_LOCATIONS = [
|
|
17
|
+
".coding-agent/skills.json",
|
|
18
|
+
"coding-agent.skills.json",
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const DEFAULT_CORE_ROOT = path.resolve(
|
|
22
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
23
|
+
"..",
|
|
24
|
+
"..",
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
function inside(root, candidate) {
|
|
28
|
+
const relative = path.relative(root, candidate);
|
|
29
|
+
return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function baseFailure(code) {
|
|
33
|
+
return {
|
|
34
|
+
ok: false,
|
|
35
|
+
status: "failed",
|
|
36
|
+
acceptedAdapters: 0,
|
|
37
|
+
acceptedSkills: [],
|
|
38
|
+
codes: [code],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function inspectRelativePath(root, relative) {
|
|
43
|
+
if (
|
|
44
|
+
typeof relative !== "string" ||
|
|
45
|
+
relative.startsWith("/") ||
|
|
46
|
+
relative.split(/[\\/]+/).includes("..") ||
|
|
47
|
+
/(^|\/)\.env(?:\.|$)/.test(relative)
|
|
48
|
+
) {
|
|
49
|
+
return ["unsafe-project-path"];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const candidate = path.resolve(root, relative);
|
|
53
|
+
if (!inside(root, candidate)) return ["unsafe-project-path"];
|
|
54
|
+
|
|
55
|
+
let current = root;
|
|
56
|
+
for (const segment of path.relative(root, candidate).split(path.sep).filter(Boolean)) {
|
|
57
|
+
current = path.join(current, segment);
|
|
58
|
+
if (!fs.existsSync(current)) break;
|
|
59
|
+
if (fs.lstatSync(current).isSymbolicLink()) return ["symlink-escape"];
|
|
60
|
+
}
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function discoverDeclaration(projectRoot) {
|
|
65
|
+
const candidates = [];
|
|
66
|
+
const failures = [];
|
|
67
|
+
|
|
68
|
+
for (const relative of PROJECT_DECLARATION_LOCATIONS) {
|
|
69
|
+
const pathIssues = inspectRelativePath(projectRoot, relative);
|
|
70
|
+
if (pathIssues.length) {
|
|
71
|
+
failures.push(...pathIssues);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const file = path.join(projectRoot, relative);
|
|
75
|
+
if (!fs.existsSync(file)) continue;
|
|
76
|
+
const stat = fs.lstatSync(file);
|
|
77
|
+
if (stat.isSymbolicLink()) {
|
|
78
|
+
failures.push("symlink-escape");
|
|
79
|
+
} else if (!stat.isFile()) {
|
|
80
|
+
failures.push("declaration-not-regular-file");
|
|
81
|
+
} else {
|
|
82
|
+
candidates.push({ file, relative });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (failures.length) return { candidate: null, codes: failures };
|
|
87
|
+
if (candidates.length === 0) {
|
|
88
|
+
return { candidate: null, codes: ["missing-project-declaration"] };
|
|
89
|
+
}
|
|
90
|
+
if (candidates.length > 1) {
|
|
91
|
+
return { candidate: null, codes: ["ambiguous-project-declaration"] };
|
|
92
|
+
}
|
|
93
|
+
return { candidate: candidates[0], codes: [] };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function declarationSchema(coreRoot) {
|
|
97
|
+
const file = path.join(
|
|
98
|
+
coreRoot,
|
|
99
|
+
"schemas",
|
|
100
|
+
"project-adapter-installation.schema.json",
|
|
101
|
+
);
|
|
102
|
+
if (!fs.existsSync(file)) return null;
|
|
103
|
+
return readSafeJsonFile(file, { scanSensitive: false }).value;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function schemaCodes(errors) {
|
|
107
|
+
const codes = new Set();
|
|
108
|
+
for (const error of errors) {
|
|
109
|
+
codes.add("declaration-schema");
|
|
110
|
+
if (/adapterRoot/.test(error)) codes.add("invalid-adapter-location");
|
|
111
|
+
if (/expectedVersion|versionPin/.test(error)) codes.add("invalid-core-version");
|
|
112
|
+
if (/compatibleSkillIds|skillIds/.test(error)) codes.add("unsupported-skill-id");
|
|
113
|
+
if (/adapterSchemaVersion/.test(error)) codes.add("unsupported-adapter-version");
|
|
114
|
+
if (/noSecrets/.test(error)) codes.add("secret-guarantee-missing");
|
|
115
|
+
if (/evidenceOutput|approvalPolicyReference/.test(error)) {
|
|
116
|
+
codes.add("unsafe-project-path");
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return [...codes];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function versionCodes(declaration, coreVersion) {
|
|
123
|
+
const codes = [];
|
|
124
|
+
const expected = declaration.core?.expectedVersion;
|
|
125
|
+
const pin = declaration.core?.versionPin;
|
|
126
|
+
|
|
127
|
+
if (!parseSemver(expected) || !parseVersionPin(pin)) {
|
|
128
|
+
codes.push("invalid-semver");
|
|
129
|
+
return codes;
|
|
130
|
+
}
|
|
131
|
+
if (expected !== coreVersion) codes.push("unsupported-core-version");
|
|
132
|
+
if (!satisfiesVersionPin(coreVersion, pin)) codes.push("core-pin-mismatch");
|
|
133
|
+
if (!satisfiesVersionPin(expected, pin)) codes.push("expected-version-outside-pin");
|
|
134
|
+
return codes;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function declarationCompatibilityCodes(declaration, discovery) {
|
|
138
|
+
const codes = [];
|
|
139
|
+
const declaredAdapters = new Map();
|
|
140
|
+
|
|
141
|
+
for (const adapter of declaration.adapters ?? []) {
|
|
142
|
+
if (declaredAdapters.has(adapter.id)) codes.push("duplicate-adapter-declaration");
|
|
143
|
+
declaredAdapters.set(adapter.id, adapter);
|
|
144
|
+
for (const skill of adapter.skillIds ?? []) {
|
|
145
|
+
if (!(declaration.compatibleSkillIds ?? []).includes(skill)) {
|
|
146
|
+
codes.push("skill-not-declared-compatible");
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (declaration.adapterSchemaVersion !== ADAPTER_SCHEMA_VERSION) {
|
|
152
|
+
codes.push("unsupported-adapter-version");
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const actualAdapters = new Map();
|
|
156
|
+
for (const adapter of discovery.accepted) {
|
|
157
|
+
if (actualAdapters.has(adapter.adapterId)) codes.push("duplicate-installed-adapter");
|
|
158
|
+
actualAdapters.set(adapter.adapterId, adapter);
|
|
159
|
+
if (adapter.rootLocation !== declaration.adapterRoot) {
|
|
160
|
+
codes.push("adapter-location-mismatch");
|
|
161
|
+
}
|
|
162
|
+
if (adapter.projectId !== declaration.projectId) {
|
|
163
|
+
codes.push("adapter-project-mismatch");
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const declared = declaredAdapters.get(adapter.adapterId);
|
|
167
|
+
if (!declared) {
|
|
168
|
+
codes.push("installed-adapter-not-declared");
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (declared.version !== adapter.adapterVersion) {
|
|
172
|
+
codes.push("adapter-version-mismatch");
|
|
173
|
+
}
|
|
174
|
+
const declaredSkills = [...(declared.skillIds ?? [])].sort();
|
|
175
|
+
if (JSON.stringify(declaredSkills) !== JSON.stringify(adapter.skills)) {
|
|
176
|
+
codes.push("adapter-skill-mismatch");
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
for (const adapterId of declaredAdapters.keys()) {
|
|
181
|
+
if (!actualAdapters.has(adapterId)) codes.push("declared-adapter-not-installed");
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const actualSkills = [
|
|
185
|
+
...new Set(discovery.accepted.flatMap((adapter) => adapter.skills)),
|
|
186
|
+
].sort();
|
|
187
|
+
const declaredSkills = [...new Set(declaration.compatibleSkillIds ?? [])].sort();
|
|
188
|
+
if (JSON.stringify(actualSkills) !== JSON.stringify(declaredSkills)) {
|
|
189
|
+
codes.push("project-skill-set-mismatch");
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return codes;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export function readProjectAdapterDeclaration(projectRootInput) {
|
|
196
|
+
const input = String(projectRootInput ?? "");
|
|
197
|
+
if (!input.trim()) return { ok: false, codes: ["missing-project-root"] };
|
|
198
|
+
if (input.split(/[\\/]+/).includes("..")) {
|
|
199
|
+
return { ok: false, codes: ["root-path-traversal"] };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const projectRoot = path.resolve(input);
|
|
203
|
+
if (!fs.existsSync(projectRoot)) {
|
|
204
|
+
return { ok: false, codes: ["project-root-not-found"] };
|
|
205
|
+
}
|
|
206
|
+
const rootStat = fs.lstatSync(projectRoot);
|
|
207
|
+
if (rootStat.isSymbolicLink()) return { ok: false, codes: ["symlink-escape"] };
|
|
208
|
+
if (!rootStat.isDirectory()) {
|
|
209
|
+
return { ok: false, codes: ["project-root-not-directory"] };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const realRoot = fs.realpathSync(projectRoot);
|
|
213
|
+
const declarationRecord = discoverDeclaration(realRoot);
|
|
214
|
+
if (!declarationRecord.candidate) {
|
|
215
|
+
return {
|
|
216
|
+
ok: false,
|
|
217
|
+
codes: [...new Set(declarationRecord.codes)],
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const parsed = readSafeJsonFile(declarationRecord.candidate.file);
|
|
222
|
+
if (!parsed.value) {
|
|
223
|
+
return {
|
|
224
|
+
ok: false,
|
|
225
|
+
codes: parsed.codes,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return {
|
|
230
|
+
ok: true,
|
|
231
|
+
projectRoot: realRoot,
|
|
232
|
+
declarationPath: declarationRecord.candidate.relative,
|
|
233
|
+
declaration: parsed.value,
|
|
234
|
+
codes: [],
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export function validateProjectAdapters(projectRootInput, options = {}) {
|
|
239
|
+
const loaded = readProjectAdapterDeclaration(projectRootInput);
|
|
240
|
+
if (!loaded.ok) {
|
|
241
|
+
return {
|
|
242
|
+
...baseFailure(loaded.codes[0]),
|
|
243
|
+
codes: loaded.codes,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const coreRoot = path.resolve(options.coreRoot ?? DEFAULT_CORE_ROOT);
|
|
248
|
+
const coreVersion = options.coreVersion ?? PILOT_VERSION;
|
|
249
|
+
const schema = declarationSchema(coreRoot);
|
|
250
|
+
if (!schema) return baseFailure("project-declaration-schema-unavailable");
|
|
251
|
+
|
|
252
|
+
const realRoot = loaded.projectRoot;
|
|
253
|
+
const declaration = loaded.declaration;
|
|
254
|
+
const codes = new Set(schemaCodes(validateValue(schema, declaration)));
|
|
255
|
+
|
|
256
|
+
if (declaration.declarationVersion !== PROJECT_DECLARATION_VERSION) {
|
|
257
|
+
codes.add("unsupported-declaration-version");
|
|
258
|
+
}
|
|
259
|
+
if (!EXTERNAL_ADAPTER_LOCATIONS.includes(declaration.adapterRoot)) {
|
|
260
|
+
codes.add("invalid-adapter-location");
|
|
261
|
+
}
|
|
262
|
+
for (const relative of [
|
|
263
|
+
declaration.adapterRoot,
|
|
264
|
+
declaration.evidenceOutput,
|
|
265
|
+
declaration.approvalPolicyReference,
|
|
266
|
+
]) {
|
|
267
|
+
for (const code of inspectRelativePath(realRoot, relative)) codes.add(code);
|
|
268
|
+
}
|
|
269
|
+
for (const code of versionCodes(declaration, coreVersion)) codes.add(code);
|
|
270
|
+
if (declaration.noSecrets !== true) codes.add("secret-guarantee-missing");
|
|
271
|
+
if (
|
|
272
|
+
declaration.validationCommand !==
|
|
273
|
+
"node <shared-core>/scripts/validate-project-adapters.mjs <project-root>"
|
|
274
|
+
) {
|
|
275
|
+
codes.add("invalid-validation-command");
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const discovery = validateExternalAdapters(realRoot, { coreRoot, coreVersion });
|
|
279
|
+
for (const record of [...discovery.rejected, ...discovery.failures]) {
|
|
280
|
+
for (const code of record.codes) codes.add(code);
|
|
281
|
+
}
|
|
282
|
+
if (discovery.discovered === 0) codes.add("no-project-adapters");
|
|
283
|
+
for (const code of declarationCompatibilityCodes(declaration, discovery)) {
|
|
284
|
+
codes.add(code);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const acceptedSkills = [
|
|
288
|
+
...new Set(discovery.accepted.flatMap((adapter) => adapter.skills)),
|
|
289
|
+
].filter((skill) => PILOT_SKILLS.includes(skill)).sort();
|
|
290
|
+
return {
|
|
291
|
+
ok: codes.size === 0,
|
|
292
|
+
status: codes.size === 0 ? "complete" : "failed",
|
|
293
|
+
acceptedAdapters: discovery.accepted.length,
|
|
294
|
+
acceptedSkills,
|
|
295
|
+
codes: [...codes].sort(),
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export function formatProjectAdapterSummary(result) {
|
|
300
|
+
if (result.ok) {
|
|
301
|
+
return [
|
|
302
|
+
`project adapter validation complete: ${result.acceptedAdapters} adapters, ` +
|
|
303
|
+
`${result.acceptedSkills.length} skills, core pin accepted`,
|
|
304
|
+
];
|
|
305
|
+
}
|
|
306
|
+
return [
|
|
307
|
+
`project adapter validation failed: ${result.acceptedAdapters} adapters accepted`,
|
|
308
|
+
`rejection codes: ${result.codes.join(",")}`,
|
|
309
|
+
];
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export function projectAdapterCliResult(projectRoot, options = {}) {
|
|
313
|
+
if (!projectRoot) {
|
|
314
|
+
return {
|
|
315
|
+
exitCode: 2,
|
|
316
|
+
stream: "stderr",
|
|
317
|
+
lines: ["usage: node scripts/validate-project-adapters.mjs <project-root>"],
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
const result = validateProjectAdapters(projectRoot, options);
|
|
321
|
+
return {
|
|
322
|
+
exitCode: result.ok ? 0 : 1,
|
|
323
|
+
stream: result.ok ? "stdout" : "stderr",
|
|
324
|
+
lines: formatProjectAdapterSummary(result),
|
|
325
|
+
result,
|
|
326
|
+
};
|
|
327
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
function inside(root, candidate) {
|
|
5
|
+
const relative = path.relative(root, candidate);
|
|
6
|
+
return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function resolveSafeEvidenceOutput(outputPath, options = {}) {
|
|
10
|
+
const value = String(outputPath ?? "");
|
|
11
|
+
if (!value.trim()) return { ok: false, code: "missing-output-path" };
|
|
12
|
+
if (path.isAbsolute(value) || value.split(/[\\/]+/).includes("..")) {
|
|
13
|
+
return { ok: false, code: "unsafe-output-path" };
|
|
14
|
+
}
|
|
15
|
+
if (
|
|
16
|
+
value.split(/[\\/]+/).some((segment) => /^\.env(?:\.|$)/.test(segment)) ||
|
|
17
|
+
path.extname(value) !== ".json"
|
|
18
|
+
) {
|
|
19
|
+
return { ok: false, code: "unsafe-output-path" };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const base = path.resolve(options.baseDirectory ?? process.cwd());
|
|
23
|
+
if (
|
|
24
|
+
!fs.existsSync(base) ||
|
|
25
|
+
fs.lstatSync(base).isSymbolicLink() ||
|
|
26
|
+
!fs.lstatSync(base).isDirectory()
|
|
27
|
+
) {
|
|
28
|
+
return { ok: false, code: "output-base-unavailable" };
|
|
29
|
+
}
|
|
30
|
+
const target = path.resolve(base, value);
|
|
31
|
+
if (!inside(base, target)) return { ok: false, code: "unsafe-output-path" };
|
|
32
|
+
|
|
33
|
+
let current = base;
|
|
34
|
+
for (const segment of path.relative(base, target).split(path.sep).filter(Boolean)) {
|
|
35
|
+
current = path.join(current, segment);
|
|
36
|
+
if (!fs.existsSync(current)) break;
|
|
37
|
+
if (fs.lstatSync(current).isSymbolicLink()) {
|
|
38
|
+
return { ok: false, code: "output-symlink-escape" };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const parent = path.dirname(target);
|
|
43
|
+
if (!fs.existsSync(parent) || !fs.lstatSync(parent).isDirectory()) {
|
|
44
|
+
return { ok: false, code: "output-parent-unavailable" };
|
|
45
|
+
}
|
|
46
|
+
if (fs.existsSync(target)) return { ok: false, code: "output-already-exists" };
|
|
47
|
+
return { ok: true, target };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function writeSafeEvidenceJson(outputPath, evidence, options = {}) {
|
|
51
|
+
const resolved = resolveSafeEvidenceOutput(outputPath, options);
|
|
52
|
+
if (!resolved.ok) return resolved;
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
fs.writeFileSync(resolved.target, `${JSON.stringify(evidence, null, 2)}\n`, {
|
|
56
|
+
encoding: "utf8",
|
|
57
|
+
flag: "wx",
|
|
58
|
+
mode: 0o600,
|
|
59
|
+
});
|
|
60
|
+
} catch {
|
|
61
|
+
return { ok: false, code: "output-write-failed" };
|
|
62
|
+
}
|
|
63
|
+
return { ok: true, code: "evidence-output-written" };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function parseEvidenceCliArguments(argv, positionalCount) {
|
|
67
|
+
const positional = argv.slice(0, positionalCount);
|
|
68
|
+
const flags = argv.slice(positionalCount);
|
|
69
|
+
let json = false;
|
|
70
|
+
let output = null;
|
|
71
|
+
|
|
72
|
+
for (let index = 0; index < flags.length; index += 1) {
|
|
73
|
+
const flag = flags[index];
|
|
74
|
+
if (flag === "--json" && !json && output === null) {
|
|
75
|
+
json = true;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (flag === "--output" && !json && output === null && flags[index + 1]) {
|
|
79
|
+
output = flags[index + 1];
|
|
80
|
+
index += 1;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
return { ok: false, positional, json: false, output: null };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
ok: positional.every((value) => typeof value === "string" && value.length > 0),
|
|
88
|
+
positional,
|
|
89
|
+
json,
|
|
90
|
+
output,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from "node:util";
|
|
2
|
+
|
|
3
|
+
function valueType(value) {
|
|
4
|
+
if (value === null) return "null";
|
|
5
|
+
if (Array.isArray(value)) return "array";
|
|
6
|
+
if (Number.isInteger(value)) return "integer";
|
|
7
|
+
return typeof value;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function matchesType(value, expected) {
|
|
11
|
+
if (expected === "number") return typeof value === "number" && Number.isFinite(value);
|
|
12
|
+
if (expected === "integer") return Number.isInteger(value);
|
|
13
|
+
if (expected === "object") {
|
|
14
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
15
|
+
}
|
|
16
|
+
return valueType(value) === expected;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function decodePointerPart(part) {
|
|
20
|
+
return part.replaceAll("~1", "/").replaceAll("~0", "~");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function resolveLocalReference(rootSchema, reference) {
|
|
24
|
+
if (!reference.startsWith("#/")) {
|
|
25
|
+
throw new Error(`only local schema references are supported: ${reference}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return reference
|
|
29
|
+
.slice(2)
|
|
30
|
+
.split("/")
|
|
31
|
+
.map(decodePointerPart)
|
|
32
|
+
.reduce((current, part) => current?.[part], rootSchema);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function childPath(parent, key) {
|
|
36
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(String(key))
|
|
37
|
+
? `${parent}.${key}`
|
|
38
|
+
: `${parent}[${JSON.stringify(String(key))}]`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function validateNode(schema, value, currentPath, rootSchema, errors) {
|
|
42
|
+
if (typeof schema === "boolean") {
|
|
43
|
+
if (!schema) errors.push(`${currentPath}: value is rejected by the schema`);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (schema.$ref) {
|
|
48
|
+
const referenced = resolveLocalReference(rootSchema, schema.$ref);
|
|
49
|
+
if (!referenced) {
|
|
50
|
+
errors.push(`${currentPath}: unresolved schema reference ${schema.$ref}`);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
validateNode(referenced, value, currentPath, rootSchema, errors);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if ("const" in schema && !isDeepStrictEqual(value, schema.const)) {
|
|
57
|
+
errors.push(`${currentPath}: expected constant ${JSON.stringify(schema.const)}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (schema.enum && !schema.enum.some((candidate) => isDeepStrictEqual(candidate, value))) {
|
|
61
|
+
errors.push(`${currentPath}: value is not in the allowed enum`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (schema.type) {
|
|
65
|
+
const expectedTypes = Array.isArray(schema.type) ? schema.type : [schema.type];
|
|
66
|
+
if (!expectedTypes.some((expected) => matchesType(value, expected))) {
|
|
67
|
+
errors.push(
|
|
68
|
+
`${currentPath}: expected ${expectedTypes.join(" or ")}, received ${valueType(value)}`,
|
|
69
|
+
);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (typeof value === "string") {
|
|
75
|
+
if (schema.minLength !== undefined && value.length < schema.minLength) {
|
|
76
|
+
errors.push(`${currentPath}: string is shorter than ${schema.minLength}`);
|
|
77
|
+
}
|
|
78
|
+
if (schema.pattern && !new RegExp(schema.pattern).test(value)) {
|
|
79
|
+
errors.push(`${currentPath}: string does not match ${schema.pattern}`);
|
|
80
|
+
}
|
|
81
|
+
if (
|
|
82
|
+
schema.format === "date-time" &&
|
|
83
|
+
(!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/.test(value) ||
|
|
84
|
+
Number.isNaN(Date.parse(value)))
|
|
85
|
+
) {
|
|
86
|
+
errors.push(`${currentPath}: invalid UTC date-time`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (Array.isArray(value)) {
|
|
91
|
+
if (schema.minItems !== undefined && value.length < schema.minItems) {
|
|
92
|
+
errors.push(`${currentPath}: expected at least ${schema.minItems} items`);
|
|
93
|
+
}
|
|
94
|
+
if (schema.uniqueItems) {
|
|
95
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
96
|
+
if (value.slice(0, index).some((item) => isDeepStrictEqual(item, value[index]))) {
|
|
97
|
+
errors.push(`${currentPath}[${index}]: duplicate array item`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (schema.items) {
|
|
102
|
+
value.forEach((item, index) => {
|
|
103
|
+
validateNode(schema.items, item, `${currentPath}[${index}]`, rootSchema, errors);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
109
|
+
for (const required of schema.required ?? []) {
|
|
110
|
+
if (!Object.hasOwn(value, required)) {
|
|
111
|
+
errors.push(`${currentPath}: missing required property ${required}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
for (const [key, item] of Object.entries(value)) {
|
|
116
|
+
if (schema.properties?.[key]) {
|
|
117
|
+
validateNode(
|
|
118
|
+
schema.properties[key],
|
|
119
|
+
item,
|
|
120
|
+
childPath(currentPath, key),
|
|
121
|
+
rootSchema,
|
|
122
|
+
errors,
|
|
123
|
+
);
|
|
124
|
+
} else if (schema.additionalProperties === false) {
|
|
125
|
+
errors.push(`${childPath(currentPath, key)}: additional property is not allowed`);
|
|
126
|
+
} else if (
|
|
127
|
+
schema.additionalProperties &&
|
|
128
|
+
typeof schema.additionalProperties === "object"
|
|
129
|
+
) {
|
|
130
|
+
validateNode(
|
|
131
|
+
schema.additionalProperties,
|
|
132
|
+
item,
|
|
133
|
+
childPath(currentPath, key),
|
|
134
|
+
rootSchema,
|
|
135
|
+
errors,
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function validateValue(schema, value) {
|
|
143
|
+
const errors = [];
|
|
144
|
+
validateNode(schema, value, "$", schema, errors);
|
|
145
|
+
return errors;
|
|
146
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const NUMBER = "(?:0|[1-9]\\d*)";
|
|
2
|
+
const VERSION_PATTERN = new RegExp(`^(${NUMBER})\\.(${NUMBER})\\.(${NUMBER})$`);
|
|
3
|
+
const COMPARATOR_PATTERN = new RegExp(
|
|
4
|
+
`^(>=|<=|>|<|=)?(${NUMBER}\\.${NUMBER}\\.${NUMBER})$`,
|
|
5
|
+
);
|
|
6
|
+
|
|
7
|
+
export function parseSemver(value) {
|
|
8
|
+
const match = VERSION_PATTERN.exec(String(value ?? ""));
|
|
9
|
+
if (!match) return null;
|
|
10
|
+
return match.slice(1).map(Number);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function compareSemver(left, right) {
|
|
14
|
+
const a = Array.isArray(left) ? left : parseSemver(left);
|
|
15
|
+
const b = Array.isArray(right) ? right : parseSemver(right);
|
|
16
|
+
if (!a || !b) return null;
|
|
17
|
+
for (let index = 0; index < 3; index += 1) {
|
|
18
|
+
if (a[index] < b[index]) return -1;
|
|
19
|
+
if (a[index] > b[index]) return 1;
|
|
20
|
+
}
|
|
21
|
+
return 0;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function parseVersionPin(value) {
|
|
25
|
+
const text = String(value ?? "").trim();
|
|
26
|
+
if (parseSemver(text)) return [{ operator: "=", version: text }];
|
|
27
|
+
|
|
28
|
+
const tokens = text.split(/\s+/).filter(Boolean);
|
|
29
|
+
if (tokens.length < 2 || tokens.length > 4) return null;
|
|
30
|
+
|
|
31
|
+
const comparators = [];
|
|
32
|
+
for (const token of tokens) {
|
|
33
|
+
const match = COMPARATOR_PATTERN.exec(token);
|
|
34
|
+
if (!match) return null;
|
|
35
|
+
comparators.push({ operator: match[1] || "=", version: match[2] });
|
|
36
|
+
}
|
|
37
|
+
return comparators;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function satisfiesVersionPin(version, pin) {
|
|
41
|
+
if (!parseSemver(version)) return false;
|
|
42
|
+
const comparators = parseVersionPin(pin);
|
|
43
|
+
if (!comparators) return false;
|
|
44
|
+
|
|
45
|
+
return comparators.every(({ operator, version: target }) => {
|
|
46
|
+
const comparison = compareSemver(version, target);
|
|
47
|
+
if (operator === "=") return comparison === 0;
|
|
48
|
+
if (operator === ">") return comparison > 0;
|
|
49
|
+
if (operator === ">=") return comparison >= 0;
|
|
50
|
+
if (operator === "<") return comparison < 0;
|
|
51
|
+
if (operator === "<=") return comparison <= 0;
|
|
52
|
+
return false;
|
|
53
|
+
});
|
|
54
|
+
}
|