artifact-chain-assistant 0.8.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/.agents/plugins/marketplace.json +20 -0
- package/.claude-plugin/marketplace.json +20 -0
- package/.claude-plugin/plugin.json +11 -0
- package/.codex-plugin/plugin.json +27 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +26 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +19 -0
- package/.github/pull_request_template.md +14 -0
- package/.github/workflows/ci.yml +23 -0
- package/CHANGELOG.md +196 -0
- package/CODE_OF_CONDUCT.md +11 -0
- package/CONTRIBUTING.md +24 -0
- package/EXTENDED-ARTIFACT-CATALOG.md +312 -0
- package/EXTENDED-ARTIFACT-CATALOG.zh-CN.md +289 -0
- package/INSTALL.md +1340 -0
- package/LICENSE +186 -0
- package/NOTICE +18 -0
- package/README.md +426 -0
- package/README.zh-CN.md +409 -0
- package/SECURITY.md +19 -0
- package/adapters/claude/.claude-plugin/plugin.json +11 -0
- package/adapters/claude/EXTENDED-ARTIFACT-CATALOG.md +312 -0
- package/adapters/claude/EXTENDED-ARTIFACT-CATALOG.zh-CN.md +289 -0
- package/adapters/claude/INSTALL.md +1340 -0
- package/adapters/claude/agent-methods/catalog.yaml +260 -0
- package/adapters/claude/bin/version-lock-audit.sh +21 -0
- package/adapters/claude/bin/version-lock-refresh.sh +21 -0
- package/adapters/claude/commands/version-lock-audit.md +9 -0
- package/adapters/claude/commands/version-lock-refresh.md +9 -0
- package/adapters/claude/compatibility.json +9 -0
- package/adapters/claude/hooks/hooks.json +16 -0
- package/adapters/claude/hooks/version-lock-stop.mjs +77 -0
- package/adapters/claude/schemas/artifact-workflow-profile.schema.json +72 -0
- package/adapters/claude/scripts/batch-merge.mjs +276 -0
- package/adapters/claude/scripts/batch-split.mjs +184 -0
- package/adapters/claude/scripts/check-workflow-profile.mjs +64 -0
- package/adapters/claude/scripts/doctor.mjs +44 -0
- package/adapters/claude/scripts/lib/artifact-graph-runtime.mjs +189 -0
- package/adapters/claude/scripts/lib/compatibility-policy.mjs +435 -0
- package/adapters/claude/scripts/lib/inline-yaml-parser.mjs +216 -0
- package/adapters/claude/scripts/lib/method-registry-version.mjs +7 -0
- package/adapters/claude/scripts/lib/workflow-profile.mjs +645 -0
- package/adapters/claude/scripts/run-artifact-workflow.mjs +366 -0
- package/adapters/claude/skills/artifact-audit/SKILL.md +80 -0
- package/adapters/claude/skills/artifact-batch/SKILL.md +115 -0
- package/adapters/claude/skills/artifact-chain-bootstrap/SKILL.md +343 -0
- package/adapters/claude/skills/artifact-chain-maintainer/SKILL.md +167 -0
- package/adapters/claude/skills/artifact-generate/SKILL.md +89 -0
- package/adapters/claude/skills/artifact-repair/SKILL.md +124 -0
- package/adapters/claude/skills/artifact-review/SKILL.md +122 -0
- package/adapters/claude/skills/artifact-workflow-worker/SKILL.md +90 -0
- package/adapters/claude/skills/prd-feature/SKILL.md +68 -0
- package/adapters/claude/skills/prd-feature/author/SKILL.md +92 -0
- package/adapters/claude/skills/prd-feature/references/compose.md +43 -0
- package/adapters/claude/skills/prd-feature/references/inspect.md +94 -0
- package/adapters/claude/skills/prd-feature/references/validate.md +65 -0
- package/adapters/claude/skills/prd-feature/repair/SKILL.md +135 -0
- package/adapters/claude/skills/prd-feature/review/SKILL.md +134 -0
- package/adapters/claude/skills/scenario-script/SKILL.md +76 -0
- package/adapters/claude/skills/scenario-script/author/SKILL.md +111 -0
- package/adapters/claude/skills/scenario-script/references/compose.md +43 -0
- package/adapters/claude/skills/scenario-script/references/inspect.md +105 -0
- package/adapters/claude/skills/scenario-script/references/validate.md +72 -0
- package/adapters/claude/skills/scenario-script/repair/SKILL.md +135 -0
- package/adapters/claude/skills/scenario-script/review/SKILL.md +137 -0
- package/adapters/claude/skills/where-am-i/SKILL.md +233 -0
- package/adapters/claude/templates/README.md +159 -0
- package/adapters/claude/templates/core/decision/review-checklist.md +35 -0
- package/adapters/claude/templates/core/decision/starter.md +66 -0
- package/adapters/claude/templates/core/design/review-checklist.md +41 -0
- package/adapters/claude/templates/core/design/starter.md +84 -0
- package/adapters/claude/templates/core/e2e_test/review-checklist.md +43 -0
- package/adapters/claude/templates/core/e2e_test/starter.md +70 -0
- package/adapters/claude/templates/core/feature/review-checklist.md +29 -0
- package/adapters/claude/templates/core/feature/starter.md +43 -0
- package/adapters/claude/templates/core/scenario/review-checklist.md +29 -0
- package/adapters/claude/templates/core/scenario/starter.md +57 -0
- package/adapters/claude/templates/core/test/review-checklist.md +37 -0
- package/adapters/claude/templates/core/test/starter.md +70 -0
- package/adapters/claude/templates/extended/ADOPTION-GUIDE.md +263 -0
- package/adapters/claude/templates/extended/README.md +261 -0
- package/adapters/claude/templates/extended/agent/agent_skill/review-checklist.md +51 -0
- package/adapters/claude/templates/extended/agent/agent_skill/starter.md +133 -0
- package/adapters/claude/templates/extended/agent/hook_policy/review-checklist.md +58 -0
- package/adapters/claude/templates/extended/agent/hook_policy/starter.md +134 -0
- package/adapters/claude/templates/extended/agent/prompt_packet/review-checklist.md +54 -0
- package/adapters/claude/templates/extended/agent/prompt_packet/starter.md +106 -0
- package/adapters/claude/templates/extended/contracts/api_contract/review-checklist.md +43 -0
- package/adapters/claude/templates/extended/contracts/api_contract/starter.md +92 -0
- package/adapters/claude/templates/extended/contracts/batch_job_contract/review-checklist.md +82 -0
- package/adapters/claude/templates/extended/contracts/batch_job_contract/starter.md +158 -0
- package/adapters/claude/templates/extended/contracts/cli_contract/review-checklist.md +43 -0
- package/adapters/claude/templates/extended/contracts/cli_contract/starter.md +93 -0
- package/adapters/claude/templates/extended/contracts/data_contract/review-checklist.md +45 -0
- package/adapters/claude/templates/extended/contracts/data_contract/starter.md +88 -0
- package/adapters/claude/templates/extended/contracts/integration_contract/review-checklist.md +77 -0
- package/adapters/claude/templates/extended/contracts/integration_contract/starter.md +158 -0
- package/adapters/claude/templates/extended/contracts/ipc_contract/review-checklist.md +78 -0
- package/adapters/claude/templates/extended/contracts/ipc_contract/starter.md +214 -0
- package/adapters/claude/templates/extended/contracts/report_contract/review-checklist.md +61 -0
- package/adapters/claude/templates/extended/contracts/report_contract/starter.md +97 -0
- package/adapters/claude/templates/extended/contracts/ui_contract/review-checklist.md +70 -0
- package/adapters/claude/templates/extended/contracts/ui_contract/starter.md +150 -0
- package/adapters/claude/templates/extended/domain/database_migration/review-checklist.md +51 -0
- package/adapters/claude/templates/extended/domain/database_migration/starter.md +115 -0
- package/adapters/claude/templates/extended/domain/domain_model/review-checklist.md +43 -0
- package/adapters/claude/templates/extended/domain/domain_model/starter.md +83 -0
- package/adapters/claude/templates/extended/governance/oss_compliance/review-checklist.md +51 -0
- package/adapters/claude/templates/extended/governance/oss_compliance/starter.md +84 -0
- package/adapters/claude/templates/extended/governance/publish_skill/review-checklist.md +58 -0
- package/adapters/claude/templates/extended/governance/publish_skill/starter.md +172 -0
- package/adapters/claude/templates/extended/governance/release_policy/review-checklist.md +57 -0
- package/adapters/claude/templates/extended/governance/release_policy/starter.md +131 -0
- package/adapters/claude/templates/extended/ops/deployment_manifest/review-checklist.md +51 -0
- package/adapters/claude/templates/extended/ops/deployment_manifest/starter.md +117 -0
- package/adapters/claude/templates/extended/ops/migration_plan/review-checklist.md +63 -0
- package/adapters/claude/templates/extended/ops/migration_plan/starter.md +117 -0
- package/adapters/claude/templates/extended/ops/performance_budget/review-checklist.md +50 -0
- package/adapters/claude/templates/extended/ops/performance_budget/starter.md +117 -0
- package/adapters/claude/templates/extended/ops/runbook/review-checklist.md +51 -0
- package/adapters/claude/templates/extended/ops/runbook/starter.md +101 -0
- package/adapters/claude/templates/extended/ops/security_review/review-checklist.md +56 -0
- package/adapters/claude/templates/extended/ops/security_review/starter.md +125 -0
- package/adapters/codex/.codex-plugin/plugin.json +27 -0
- package/adapters/codex/EXTENDED-ARTIFACT-CATALOG.md +312 -0
- package/adapters/codex/EXTENDED-ARTIFACT-CATALOG.zh-CN.md +289 -0
- package/adapters/codex/INSTALL.md +1340 -0
- package/adapters/codex/agent-methods/catalog.yaml +260 -0
- package/adapters/codex/compatibility.json +9 -0
- package/adapters/codex/schemas/artifact-workflow-profile.schema.json +72 -0
- package/adapters/codex/scripts/batch-merge.mjs +276 -0
- package/adapters/codex/scripts/batch-split.mjs +184 -0
- package/adapters/codex/scripts/check-workflow-profile.mjs +64 -0
- package/adapters/codex/scripts/doctor.mjs +44 -0
- package/adapters/codex/scripts/lib/artifact-graph-runtime.mjs +189 -0
- package/adapters/codex/scripts/lib/compatibility-policy.mjs +435 -0
- package/adapters/codex/scripts/lib/inline-yaml-parser.mjs +216 -0
- package/adapters/codex/scripts/lib/method-registry-version.mjs +7 -0
- package/adapters/codex/scripts/lib/workflow-profile.mjs +645 -0
- package/adapters/codex/scripts/run-artifact-workflow.mjs +366 -0
- package/adapters/codex/skills/artifact-audit/SKILL.md +80 -0
- package/adapters/codex/skills/artifact-batch/SKILL.md +115 -0
- package/adapters/codex/skills/artifact-chain-bootstrap/SKILL.md +343 -0
- package/adapters/codex/skills/artifact-chain-maintainer/SKILL.md +167 -0
- package/adapters/codex/skills/artifact-generate/SKILL.md +89 -0
- package/adapters/codex/skills/artifact-repair/SKILL.md +124 -0
- package/adapters/codex/skills/artifact-review/SKILL.md +122 -0
- package/adapters/codex/skills/artifact-workflow-worker/SKILL.md +90 -0
- package/adapters/codex/skills/prd-feature/SKILL.md +68 -0
- package/adapters/codex/skills/prd-feature/author/SKILL.md +92 -0
- package/adapters/codex/skills/prd-feature/references/compose.md +43 -0
- package/adapters/codex/skills/prd-feature/references/inspect.md +94 -0
- package/adapters/codex/skills/prd-feature/references/validate.md +65 -0
- package/adapters/codex/skills/prd-feature/repair/SKILL.md +135 -0
- package/adapters/codex/skills/prd-feature/review/SKILL.md +134 -0
- package/adapters/codex/skills/scenario-script/SKILL.md +76 -0
- package/adapters/codex/skills/scenario-script/author/SKILL.md +111 -0
- package/adapters/codex/skills/scenario-script/references/compose.md +43 -0
- package/adapters/codex/skills/scenario-script/references/inspect.md +105 -0
- package/adapters/codex/skills/scenario-script/references/validate.md +72 -0
- package/adapters/codex/skills/scenario-script/repair/SKILL.md +135 -0
- package/adapters/codex/skills/scenario-script/review/SKILL.md +137 -0
- package/adapters/codex/skills/where-am-i/SKILL.md +233 -0
- package/adapters/codex/templates/README.md +159 -0
- package/adapters/codex/templates/core/decision/review-checklist.md +35 -0
- package/adapters/codex/templates/core/decision/starter.md +66 -0
- package/adapters/codex/templates/core/design/review-checklist.md +41 -0
- package/adapters/codex/templates/core/design/starter.md +84 -0
- package/adapters/codex/templates/core/e2e_test/review-checklist.md +43 -0
- package/adapters/codex/templates/core/e2e_test/starter.md +70 -0
- package/adapters/codex/templates/core/feature/review-checklist.md +29 -0
- package/adapters/codex/templates/core/feature/starter.md +43 -0
- package/adapters/codex/templates/core/scenario/review-checklist.md +29 -0
- package/adapters/codex/templates/core/scenario/starter.md +57 -0
- package/adapters/codex/templates/core/test/review-checklist.md +37 -0
- package/adapters/codex/templates/core/test/starter.md +70 -0
- package/adapters/codex/templates/extended/ADOPTION-GUIDE.md +263 -0
- package/adapters/codex/templates/extended/README.md +261 -0
- package/adapters/codex/templates/extended/agent/agent_skill/review-checklist.md +51 -0
- package/adapters/codex/templates/extended/agent/agent_skill/starter.md +133 -0
- package/adapters/codex/templates/extended/agent/hook_policy/review-checklist.md +58 -0
- package/adapters/codex/templates/extended/agent/hook_policy/starter.md +134 -0
- package/adapters/codex/templates/extended/agent/prompt_packet/review-checklist.md +54 -0
- package/adapters/codex/templates/extended/agent/prompt_packet/starter.md +106 -0
- package/adapters/codex/templates/extended/contracts/api_contract/review-checklist.md +43 -0
- package/adapters/codex/templates/extended/contracts/api_contract/starter.md +92 -0
- package/adapters/codex/templates/extended/contracts/batch_job_contract/review-checklist.md +82 -0
- package/adapters/codex/templates/extended/contracts/batch_job_contract/starter.md +158 -0
- package/adapters/codex/templates/extended/contracts/cli_contract/review-checklist.md +43 -0
- package/adapters/codex/templates/extended/contracts/cli_contract/starter.md +93 -0
- package/adapters/codex/templates/extended/contracts/data_contract/review-checklist.md +45 -0
- package/adapters/codex/templates/extended/contracts/data_contract/starter.md +88 -0
- package/adapters/codex/templates/extended/contracts/integration_contract/review-checklist.md +77 -0
- package/adapters/codex/templates/extended/contracts/integration_contract/starter.md +158 -0
- package/adapters/codex/templates/extended/contracts/ipc_contract/review-checklist.md +78 -0
- package/adapters/codex/templates/extended/contracts/ipc_contract/starter.md +214 -0
- package/adapters/codex/templates/extended/contracts/report_contract/review-checklist.md +61 -0
- package/adapters/codex/templates/extended/contracts/report_contract/starter.md +97 -0
- package/adapters/codex/templates/extended/contracts/ui_contract/review-checklist.md +70 -0
- package/adapters/codex/templates/extended/contracts/ui_contract/starter.md +150 -0
- package/adapters/codex/templates/extended/domain/database_migration/review-checklist.md +51 -0
- package/adapters/codex/templates/extended/domain/database_migration/starter.md +115 -0
- package/adapters/codex/templates/extended/domain/domain_model/review-checklist.md +43 -0
- package/adapters/codex/templates/extended/domain/domain_model/starter.md +83 -0
- package/adapters/codex/templates/extended/governance/oss_compliance/review-checklist.md +51 -0
- package/adapters/codex/templates/extended/governance/oss_compliance/starter.md +84 -0
- package/adapters/codex/templates/extended/governance/publish_skill/review-checklist.md +58 -0
- package/adapters/codex/templates/extended/governance/publish_skill/starter.md +172 -0
- package/adapters/codex/templates/extended/governance/release_policy/review-checklist.md +57 -0
- package/adapters/codex/templates/extended/governance/release_policy/starter.md +131 -0
- package/adapters/codex/templates/extended/ops/deployment_manifest/review-checklist.md +51 -0
- package/adapters/codex/templates/extended/ops/deployment_manifest/starter.md +117 -0
- package/adapters/codex/templates/extended/ops/migration_plan/review-checklist.md +63 -0
- package/adapters/codex/templates/extended/ops/migration_plan/starter.md +117 -0
- package/adapters/codex/templates/extended/ops/performance_budget/review-checklist.md +50 -0
- package/adapters/codex/templates/extended/ops/performance_budget/starter.md +117 -0
- package/adapters/codex/templates/extended/ops/runbook/review-checklist.md +51 -0
- package/adapters/codex/templates/extended/ops/runbook/starter.md +101 -0
- package/adapters/codex/templates/extended/ops/security_review/review-checklist.md +56 -0
- package/adapters/codex/templates/extended/ops/security_review/starter.md +125 -0
- package/agent-methods/catalog.yaml +260 -0
- package/compatibility.json +9 -0
- package/package.json +85 -0
- package/runtime/claude/version-lock-stop.mjs +77 -0
- package/schemas/artifact-workflow-profile.schema.json +72 -0
- package/scripts/batch-merge.mjs +276 -0
- package/scripts/batch-split.mjs +184 -0
- package/scripts/build-adapters.mjs +195 -0
- package/scripts/build-runtime-bundles.mjs +134 -0
- package/scripts/check-compatibility.mjs +19 -0
- package/scripts/check-method-registry.mjs +461 -0
- package/scripts/check-templates.mjs +348 -0
- package/scripts/check-workflow-profile.mjs +64 -0
- package/scripts/doctor.mjs +44 -0
- package/scripts/install-git-hooks.mjs +21 -0
- package/scripts/lib/artifact-graph-runtime.mjs +189 -0
- package/scripts/lib/compatibility-policy.mjs +435 -0
- package/scripts/lib/inline-yaml-parser.mjs +216 -0
- package/scripts/lib/method-registry-version.mjs +7 -0
- package/scripts/lib/workflow-profile.mjs +645 -0
- package/scripts/run-artifact-workflow.mjs +366 -0
- package/scripts/sync-skills.mjs +149 -0
- package/skills-src/artifact-audit/SKILL.md +80 -0
- package/skills-src/artifact-batch/SKILL.md +115 -0
- package/skills-src/artifact-chain-bootstrap/SKILL.md +343 -0
- package/skills-src/artifact-chain-maintainer/SKILL.md +167 -0
- package/skills-src/artifact-generate/SKILL.md +89 -0
- package/skills-src/artifact-repair/SKILL.md +124 -0
- package/skills-src/artifact-review/SKILL.md +122 -0
- package/skills-src/artifact-workflow-worker/SKILL.md +90 -0
- package/skills-src/prd-feature/SKILL.md +68 -0
- package/skills-src/prd-feature/author/SKILL.md +92 -0
- package/skills-src/prd-feature/references/compose.md +43 -0
- package/skills-src/prd-feature/references/inspect.md +94 -0
- package/skills-src/prd-feature/references/validate.md +65 -0
- package/skills-src/prd-feature/repair/SKILL.md +135 -0
- package/skills-src/prd-feature/review/SKILL.md +134 -0
- package/skills-src/scenario-script/SKILL.md +76 -0
- package/skills-src/scenario-script/author/SKILL.md +111 -0
- package/skills-src/scenario-script/references/compose.md +43 -0
- package/skills-src/scenario-script/references/inspect.md +105 -0
- package/skills-src/scenario-script/references/validate.md +72 -0
- package/skills-src/scenario-script/repair/SKILL.md +135 -0
- package/skills-src/scenario-script/review/SKILL.md +137 -0
- package/skills-src/where-am-i/SKILL.md.tpl +233 -0
- package/templates/README.md +159 -0
- package/templates/claude/marketplace.json.tpl +20 -0
- package/templates/claude/plugin.json.tpl +11 -0
- package/templates/claude/settings.json.tpl +16 -0
- package/templates/codex/marketplace.json.tpl +20 -0
- package/templates/codex/plugin.json.tpl +27 -0
- package/templates/core/decision/review-checklist.md +35 -0
- package/templates/core/decision/starter.md +66 -0
- package/templates/core/design/review-checklist.md +41 -0
- package/templates/core/design/starter.md +84 -0
- package/templates/core/e2e_test/review-checklist.md +43 -0
- package/templates/core/e2e_test/starter.md +70 -0
- package/templates/core/feature/review-checklist.md +29 -0
- package/templates/core/feature/starter.md +43 -0
- package/templates/core/scenario/review-checklist.md +29 -0
- package/templates/core/scenario/starter.md +57 -0
- package/templates/core/test/review-checklist.md +37 -0
- package/templates/core/test/starter.md +70 -0
- package/templates/extended/ADOPTION-GUIDE.md +263 -0
- package/templates/extended/README.md +261 -0
- package/templates/extended/agent/agent_skill/review-checklist.md +51 -0
- package/templates/extended/agent/agent_skill/starter.md +133 -0
- package/templates/extended/agent/hook_policy/review-checklist.md +58 -0
- package/templates/extended/agent/hook_policy/starter.md +134 -0
- package/templates/extended/agent/prompt_packet/review-checklist.md +54 -0
- package/templates/extended/agent/prompt_packet/starter.md +106 -0
- package/templates/extended/contracts/api_contract/review-checklist.md +43 -0
- package/templates/extended/contracts/api_contract/starter.md +92 -0
- package/templates/extended/contracts/batch_job_contract/review-checklist.md +82 -0
- package/templates/extended/contracts/batch_job_contract/starter.md +158 -0
- package/templates/extended/contracts/cli_contract/review-checklist.md +43 -0
- package/templates/extended/contracts/cli_contract/starter.md +93 -0
- package/templates/extended/contracts/data_contract/review-checklist.md +45 -0
- package/templates/extended/contracts/data_contract/starter.md +88 -0
- package/templates/extended/contracts/integration_contract/review-checklist.md +77 -0
- package/templates/extended/contracts/integration_contract/starter.md +158 -0
- package/templates/extended/contracts/ipc_contract/review-checklist.md +78 -0
- package/templates/extended/contracts/ipc_contract/starter.md +214 -0
- package/templates/extended/contracts/report_contract/review-checklist.md +61 -0
- package/templates/extended/contracts/report_contract/starter.md +97 -0
- package/templates/extended/contracts/ui_contract/review-checklist.md +70 -0
- package/templates/extended/contracts/ui_contract/starter.md +150 -0
- package/templates/extended/domain/database_migration/review-checklist.md +51 -0
- package/templates/extended/domain/database_migration/starter.md +115 -0
- package/templates/extended/domain/domain_model/review-checklist.md +43 -0
- package/templates/extended/domain/domain_model/starter.md +83 -0
- package/templates/extended/governance/oss_compliance/review-checklist.md +51 -0
- package/templates/extended/governance/oss_compliance/starter.md +84 -0
- package/templates/extended/governance/publish_skill/review-checklist.md +58 -0
- package/templates/extended/governance/publish_skill/starter.md +172 -0
- package/templates/extended/governance/release_policy/review-checklist.md +57 -0
- package/templates/extended/governance/release_policy/starter.md +131 -0
- package/templates/extended/ops/deployment_manifest/review-checklist.md +51 -0
- package/templates/extended/ops/deployment_manifest/starter.md +117 -0
- package/templates/extended/ops/migration_plan/review-checklist.md +63 -0
- package/templates/extended/ops/migration_plan/starter.md +117 -0
- package/templates/extended/ops/performance_budget/review-checklist.md +50 -0
- package/templates/extended/ops/performance_budget/starter.md +117 -0
- package/templates/extended/ops/runbook/review-checklist.md +51 -0
- package/templates/extended/ops/runbook/starter.md +101 -0
- package/templates/extended/ops/security_review/review-checklist.md +56 -0
- package/templates/extended/ops/security_review/starter.md +125 -0
- package/templates/git-hooks/pre-commit.sh +43 -0
- package/templates/git-hooks/pre-push.sh +22 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// @scenario S-44 @feature ACA13 @feature ACA3
|
|
3
|
+
// @decision D-ACA-15
|
|
4
|
+
import { access, readFile, realpath } from 'node:fs/promises';
|
|
5
|
+
import { delimiter, join, dirname, resolve } from 'node:path';
|
|
6
|
+
import { loadCompatibilityPolicy } from './compatibility-policy.mjs';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Locate the artifact-graph CLI in a target project, resolve its package version,
|
|
10
|
+
* and compare against the plugin's compatibility policy.
|
|
11
|
+
*
|
|
12
|
+
* Resolution order:
|
|
13
|
+
* 1. <projectRoot>/node_modules/.bin/artifact-graph
|
|
14
|
+
* 2. PATH lookup for artifact-graph
|
|
15
|
+
* 3. ARTIFACT_GRAPH_LEGACY_CLI env var (explicit fallback)
|
|
16
|
+
*
|
|
17
|
+
* For the resolved CLI, look up its owning package.json (name === "artifact-graph")
|
|
18
|
+
* and compare the version field against policy.artifactGraph.verifiedVersion.
|
|
19
|
+
*
|
|
20
|
+
* @param {{ projectRoot: string, env?: Record<string,string> }} opts
|
|
21
|
+
* @returns {Promise<{ok: boolean, cliPath?: string, packagePath?: string, actualVersion?: string, expectedVersion?: string, reason?: string, remediation?: string}>}
|
|
22
|
+
*/
|
|
23
|
+
export async function inspectArtifactGraphRuntime({ projectRoot, env }) {
|
|
24
|
+
const pluginRoot = resolve(import.meta.dirname, '..', '..');
|
|
25
|
+
let policy;
|
|
26
|
+
try {
|
|
27
|
+
policy = await loadCompatibilityPolicy(pluginRoot);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
return {
|
|
30
|
+
ok: false,
|
|
31
|
+
reason: 'policy_load_failed',
|
|
32
|
+
remediation: `Failed to load compatibility policy: ${error.message}`,
|
|
33
|
+
expectedVersion: null,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const expectedVersion = policy.artifactGraph.verifiedVersion;
|
|
38
|
+
const installSpec = policy.artifactGraph.installSpec;
|
|
39
|
+
|
|
40
|
+
// 1. Try project-local CLI first
|
|
41
|
+
const localBin = join(projectRoot, 'node_modules', '.bin', 'artifact-graph');
|
|
42
|
+
let cliPath = await resolveIfExecutable(localBin);
|
|
43
|
+
|
|
44
|
+
// 2. Try PATH
|
|
45
|
+
if (!cliPath) {
|
|
46
|
+
cliPath = await findOnPath(env ?? process.env);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 3. Try explicit legacy CLI override
|
|
50
|
+
if (!cliPath) {
|
|
51
|
+
const legacy = (env ?? process.env).ARTIFACT_GRAPH_LEGACY_CLI;
|
|
52
|
+
if (legacy) {
|
|
53
|
+
cliPath = await resolveIfExecutable(legacy);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!cliPath) {
|
|
58
|
+
return {
|
|
59
|
+
ok: false,
|
|
60
|
+
reason: 'cli_not_found',
|
|
61
|
+
expectedVersion,
|
|
62
|
+
remediation: `artifact-graph CLI not found. Run: pnpm add -D ${installSpec}`,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 4. Resolve the owning package.json
|
|
67
|
+
const { packagePath, version } = await resolvePackageVersion(cliPath);
|
|
68
|
+
|
|
69
|
+
if (!version) {
|
|
70
|
+
return {
|
|
71
|
+
ok: false,
|
|
72
|
+
cliPath,
|
|
73
|
+
packagePath,
|
|
74
|
+
reason: 'version_unresolved',
|
|
75
|
+
expectedVersion,
|
|
76
|
+
remediation: `Could not determine artifact-graph version from ${packagePath || cliPath}. Run: pnpm add -D ${installSpec}`,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (version !== expectedVersion) {
|
|
81
|
+
return {
|
|
82
|
+
ok: false,
|
|
83
|
+
cliPath,
|
|
84
|
+
packagePath,
|
|
85
|
+
actualVersion: version,
|
|
86
|
+
expectedVersion,
|
|
87
|
+
reason: 'version_mismatch',
|
|
88
|
+
remediation: `artifact-graph version is ${version}, expected ${expectedVersion}. Run: pnpm add -D ${installSpec}`,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
ok: true,
|
|
94
|
+
cliPath,
|
|
95
|
+
packagePath,
|
|
96
|
+
actualVersion: version,
|
|
97
|
+
expectedVersion,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Resolve a path to its realpath if it exists and is executable (X_OK).
|
|
103
|
+
*/
|
|
104
|
+
async function resolveIfExecutable(path) {
|
|
105
|
+
try {
|
|
106
|
+
const real = await realpath(path);
|
|
107
|
+
await access(real, 1); // X_OK = 1
|
|
108
|
+
return real;
|
|
109
|
+
} catch {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Search PATH for artifact-graph binary.
|
|
116
|
+
* Does NOT check ARTIFACT_GRAPH_LEGACY_CLI — that's the caller's fallback.
|
|
117
|
+
*/
|
|
118
|
+
export async function findOnPath(env) {
|
|
119
|
+
const pathEnv = env.PATH || '';
|
|
120
|
+
const pathDirs = pathEnv.split(delimiter);
|
|
121
|
+
|
|
122
|
+
for (const dir of pathDirs) {
|
|
123
|
+
if (!dir) continue;
|
|
124
|
+
const candidate = join(dir, 'artifact-graph');
|
|
125
|
+
const resolved = await resolveIfExecutable(candidate);
|
|
126
|
+
if (resolved) return resolved;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Given a CLI path, find the owning artifact-graph package.json and its version.
|
|
134
|
+
*
|
|
135
|
+
* Strategy:
|
|
136
|
+
* 1. Walk up from the CLI directory looking for package.json with name === "artifact-graph".
|
|
137
|
+
* 2. If CLI is under .bin/, also try walking up from the sibling artifact-graph/ package directory.
|
|
138
|
+
*
|
|
139
|
+
* Returns { packagePath, version } or { packagePath: null, version: null }.
|
|
140
|
+
*/
|
|
141
|
+
export async function resolvePackageVersion(cliPath) {
|
|
142
|
+
// Strategy 1: walk up from CLI location
|
|
143
|
+
const result = await walkUpForPackage(dirname(cliPath));
|
|
144
|
+
if (result.packagePath) return result;
|
|
145
|
+
|
|
146
|
+
// Strategy 2: if CLI is under a .bin/ directory, check sibling package directories
|
|
147
|
+
const dir = dirname(cliPath);
|
|
148
|
+
const parentDir = dirname(dir);
|
|
149
|
+
if (dir.endsWith('.bin') || dir.endsWith('.bin/')) {
|
|
150
|
+
// Check parentDir/artifact-graph/package.json directly
|
|
151
|
+
const siblingPkg = join(parentDir, 'artifact-graph', 'package.json');
|
|
152
|
+
try {
|
|
153
|
+
const raw = await readFile(siblingPkg, 'utf8');
|
|
154
|
+
const pkg = JSON.parse(raw);
|
|
155
|
+
if (pkg.name === 'artifact-graph') {
|
|
156
|
+
return { packagePath: siblingPkg, version: pkg.version || null };
|
|
157
|
+
}
|
|
158
|
+
} catch {
|
|
159
|
+
// No sibling package found
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return { packagePath: null, version: null };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async function walkUpForPackage(startDir) {
|
|
167
|
+
let dir = startDir;
|
|
168
|
+
const seen = new Set();
|
|
169
|
+
|
|
170
|
+
while (dir && !seen.has(dir)) {
|
|
171
|
+
seen.add(dir);
|
|
172
|
+
const pkgPath = join(dir, 'package.json');
|
|
173
|
+
try {
|
|
174
|
+
const raw = await readFile(pkgPath, 'utf8');
|
|
175
|
+
const pkg = JSON.parse(raw);
|
|
176
|
+
if (pkg.name === 'artifact-graph') {
|
|
177
|
+
return { packagePath: pkgPath, version: pkg.version || null };
|
|
178
|
+
}
|
|
179
|
+
} catch {
|
|
180
|
+
// No package.json at this level, or not valid JSON
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const parent = dirname(dir);
|
|
184
|
+
if (parent === dir) break;
|
|
185
|
+
dir = parent;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return { packagePath: null, version: null };
|
|
189
|
+
}
|
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// @scenario S-44 @feature ACA13
|
|
3
|
+
// @decision D-ACA-15
|
|
4
|
+
import { readFile, readdir } from 'node:fs/promises';
|
|
5
|
+
import { join, relative } from 'node:path';
|
|
6
|
+
|
|
7
|
+
const REQUIRED_SCHEMA_VERSION = '1.0';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Load and validate the compatibility policy from pluginRoot.
|
|
11
|
+
* Returns a frozen policy object.
|
|
12
|
+
*
|
|
13
|
+
* @param {string} pluginRoot
|
|
14
|
+
* @returns {Promise<{schemaVersion: string, pluginVersion: string, artifactGraph: {verifiedVersion: string, installSpec: string, githubFallbackSpec: string}}>}
|
|
15
|
+
*/
|
|
16
|
+
export async function loadCompatibilityPolicy(pluginRoot) {
|
|
17
|
+
const policyPath = join(pluginRoot, 'compatibility.json');
|
|
18
|
+
let raw;
|
|
19
|
+
try {
|
|
20
|
+
raw = await readFile(policyPath, 'utf8');
|
|
21
|
+
} catch (error) {
|
|
22
|
+
throw new Error(`compatibility.json not found at ${policyPath}: ${error.message}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let policy;
|
|
26
|
+
try {
|
|
27
|
+
policy = JSON.parse(raw);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
throw new Error(`compatibility.json is not valid JSON: ${error.message}`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (policy.schemaVersion !== REQUIRED_SCHEMA_VERSION) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
`compatibility.json schemaVersion must be "${REQUIRED_SCHEMA_VERSION}", got "${policy.schemaVersion}"`,
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!policy.pluginVersion || typeof policy.pluginVersion !== 'string') {
|
|
39
|
+
throw new Error('compatibility.json must have a non-empty pluginVersion string');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!policy.artifactGraph || typeof policy.artifactGraph !== 'object') {
|
|
43
|
+
throw new Error('compatibility.json must have an artifactGraph object');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const { verifiedVersion, installSpec, githubFallbackSpec } = policy.artifactGraph;
|
|
47
|
+
if (!verifiedVersion || typeof verifiedVersion !== 'string') {
|
|
48
|
+
throw new Error('compatibility.json artifactGraph must have a non-empty verifiedVersion string');
|
|
49
|
+
}
|
|
50
|
+
if (!installSpec || typeof installSpec !== 'string') {
|
|
51
|
+
throw new Error('compatibility.json artifactGraph must have a non-empty installSpec string');
|
|
52
|
+
}
|
|
53
|
+
if (!githubFallbackSpec || typeof githubFallbackSpec !== 'string') {
|
|
54
|
+
throw new Error('compatibility.json artifactGraph must have a non-empty githubFallbackSpec string');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return deepFreeze(policy);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Validate plugin version consistency, install specs, and documentation against the policy.
|
|
62
|
+
* Returns an array of issue strings. Empty array means no issues.
|
|
63
|
+
*
|
|
64
|
+
* @param {string} pluginRoot
|
|
65
|
+
* @returns {Promise<string[]>}
|
|
66
|
+
*/
|
|
67
|
+
export async function validateCompatibilityPolicy(pluginRoot) {
|
|
68
|
+
const issues = [];
|
|
69
|
+
let policy;
|
|
70
|
+
try {
|
|
71
|
+
policy = await loadCompatibilityPolicy(pluginRoot);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
issues.push(`Policy load failed: ${error.message}`);
|
|
74
|
+
return issues;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const { pluginVersion, artifactGraph } = policy;
|
|
78
|
+
const { verifiedVersion, installSpec, githubFallbackSpec } = artifactGraph;
|
|
79
|
+
|
|
80
|
+
// Check installSpec is precise
|
|
81
|
+
const expectedInstallSpec = `artifact-graph@${verifiedVersion}`;
|
|
82
|
+
if (installSpec !== expectedInstallSpec) {
|
|
83
|
+
issues.push(
|
|
84
|
+
`installSpec must be "${expectedInstallSpec}", got "${installSpec}"`,
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Check githubFallbackSpec ends with correct tag
|
|
89
|
+
const expectedTag = `artifact-graph-v${verifiedVersion}`;
|
|
90
|
+
if (!githubFallbackSpec.endsWith(`#${expectedTag}`)) {
|
|
91
|
+
issues.push(
|
|
92
|
+
`githubFallbackSpec must end with "#${expectedTag}", got "${githubFallbackSpec}"`,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Check package.json version
|
|
97
|
+
await checkJsonField(pluginRoot, 'package.json', 'version', pluginVersion, issues);
|
|
98
|
+
|
|
99
|
+
// Check Claude marketplace metadata.version (real schema)
|
|
100
|
+
await checkClaudeMarketplaceVersion(pluginRoot, pluginVersion, issues);
|
|
101
|
+
|
|
102
|
+
// Check Codex marketplace plugin source path (real schema has no version field)
|
|
103
|
+
await checkCodexMarketplaceSource(pluginRoot, issues);
|
|
104
|
+
|
|
105
|
+
// Check adapter manifest versions (real paths: .codex-plugin/plugin.json, .claude-plugin/plugin.json)
|
|
106
|
+
for (const [adapter, manifestRel] of [
|
|
107
|
+
['codex', join('adapters', 'codex', '.codex-plugin', 'plugin.json')],
|
|
108
|
+
['claude', join('adapters', 'claude', '.claude-plugin', 'plugin.json')],
|
|
109
|
+
]) {
|
|
110
|
+
await checkAdapterManifest(pluginRoot, adapter, manifestRel, pluginVersion, issues);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Check adapter compatibility.json deep match against root policy
|
|
114
|
+
for (const adapter of ['codex', 'claude']) {
|
|
115
|
+
await checkAdapterCompatibility(pluginRoot, adapter, policy, issues);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Check adapter doctor and lib files exist
|
|
119
|
+
for (const adapter of ['codex', 'claude']) {
|
|
120
|
+
await checkAdapterDoctorFiles(pluginRoot, adapter, issues);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Check docs for forbidden install patterns (real public snapshot at plugin root)
|
|
124
|
+
await checkDocsForForbiddenPatterns(pluginRoot, artifactGraph, issues);
|
|
125
|
+
|
|
126
|
+
return issues;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async function checkJsonField(pluginRoot, relativePath, field, expected, issues) {
|
|
130
|
+
const filePath = join(pluginRoot, relativePath);
|
|
131
|
+
try {
|
|
132
|
+
const raw = await readFile(filePath, 'utf8');
|
|
133
|
+
const json = JSON.parse(raw);
|
|
134
|
+
if (json[field] !== expected) {
|
|
135
|
+
issues.push(
|
|
136
|
+
`${relativePath} ${field} must be "${expected}", got "${json[field]}"`,
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
} catch (error) {
|
|
140
|
+
if (error.code === 'ENOENT') {
|
|
141
|
+
issues.push(`${relativePath} is missing (required)`);
|
|
142
|
+
} else if (error instanceof SyntaxError) {
|
|
143
|
+
issues.push(`${relativePath} is not valid JSON: ${error.message}`);
|
|
144
|
+
} else {
|
|
145
|
+
issues.push(`${relativePath} error: ${error.message}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Check Claude marketplace metadata.version matches plugin version.
|
|
152
|
+
* Real schema: { metadata: { version: "x.y.z" } }
|
|
153
|
+
*/
|
|
154
|
+
async function checkClaudeMarketplaceVersion(pluginRoot, expectedVersion, issues) {
|
|
155
|
+
const relPath = join('.claude-plugin', 'marketplace.json');
|
|
156
|
+
const filePath = join(pluginRoot, relPath);
|
|
157
|
+
try {
|
|
158
|
+
const raw = await readFile(filePath, 'utf8');
|
|
159
|
+
const json = JSON.parse(raw);
|
|
160
|
+
const actual = json.metadata?.version;
|
|
161
|
+
if (actual === undefined) {
|
|
162
|
+
issues.push(`${relPath} metadata.version is missing (required)`);
|
|
163
|
+
} else if (actual !== expectedVersion) {
|
|
164
|
+
issues.push(`${relPath} metadata.version must be "${expectedVersion}", got "${actual}"`);
|
|
165
|
+
}
|
|
166
|
+
} catch (error) {
|
|
167
|
+
if (error.code === 'ENOENT') {
|
|
168
|
+
issues.push(`${relPath} is missing (required)`);
|
|
169
|
+
} else if (error instanceof SyntaxError) {
|
|
170
|
+
issues.push(`${relPath} is not valid JSON: ${error.message}`);
|
|
171
|
+
} else {
|
|
172
|
+
issues.push(`${relPath} error: ${error.message}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Check Codex marketplace plugin source path points to ./adapters/codex.
|
|
179
|
+
* Real schema has NO version field; validate source.path instead.
|
|
180
|
+
*/
|
|
181
|
+
async function checkCodexMarketplaceSource(pluginRoot, issues) {
|
|
182
|
+
const relPath = join('.agents', 'plugins', 'marketplace.json');
|
|
183
|
+
const filePath = join(pluginRoot, relPath);
|
|
184
|
+
try {
|
|
185
|
+
const raw = await readFile(filePath, 'utf8');
|
|
186
|
+
const json = JSON.parse(raw);
|
|
187
|
+
const plugin = json.plugins?.[0];
|
|
188
|
+
if (!plugin) {
|
|
189
|
+
issues.push(`${relPath} has no plugins array entries`);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const sourcePath = plugin.source?.path;
|
|
193
|
+
if (sourcePath !== './adapters/codex') {
|
|
194
|
+
issues.push(
|
|
195
|
+
`${relPath} plugins[0].source.path must be "./adapters/codex", got "${sourcePath}"`,
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
} catch (error) {
|
|
199
|
+
if (error.code === 'ENOENT') {
|
|
200
|
+
issues.push(`${relPath} is missing (required)`);
|
|
201
|
+
} else if (error instanceof SyntaxError) {
|
|
202
|
+
issues.push(`${relPath} is not valid JSON: ${error.message}`);
|
|
203
|
+
} else {
|
|
204
|
+
issues.push(`${relPath} error: ${error.message}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Check adapter plugin manifest version.
|
|
211
|
+
* Fail-closed: missing file or invalid JSON produces an issue, never silently skipped.
|
|
212
|
+
*/
|
|
213
|
+
async function checkAdapterManifest(pluginRoot, adapter, manifestRel, expectedVersion, issues) {
|
|
214
|
+
const filePath = join(pluginRoot, manifestRel);
|
|
215
|
+
try {
|
|
216
|
+
const raw = await readFile(filePath, 'utf8');
|
|
217
|
+
const json = JSON.parse(raw);
|
|
218
|
+
if (json.version !== expectedVersion) {
|
|
219
|
+
issues.push(
|
|
220
|
+
`${adapter} adapter manifest version must be "${expectedVersion}", got "${json.version}"`,
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
} catch (error) {
|
|
224
|
+
if (error.code === 'ENOENT') {
|
|
225
|
+
issues.push(`${adapter} adapter manifest is missing at ${manifestRel}`);
|
|
226
|
+
} else if (error instanceof SyntaxError) {
|
|
227
|
+
issues.push(`${adapter} adapter manifest is not valid JSON: ${error.message}`);
|
|
228
|
+
} else {
|
|
229
|
+
issues.push(`${adapter} adapter manifest error: ${error.message}`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Check adapter compatibility.json matches root policy (deep equality).
|
|
236
|
+
* Fail-closed: missing file produces an issue.
|
|
237
|
+
*/
|
|
238
|
+
async function checkAdapterCompatibility(pluginRoot, adapter, rootPolicy, issues) {
|
|
239
|
+
const compatRel = join('adapters', adapter, 'compatibility.json');
|
|
240
|
+
const filePath = join(pluginRoot, compatRel);
|
|
241
|
+
try {
|
|
242
|
+
const raw = await readFile(filePath, 'utf8');
|
|
243
|
+
const json = JSON.parse(raw);
|
|
244
|
+
// Deep compare all fields that matter
|
|
245
|
+
if (json.schemaVersion !== rootPolicy.schemaVersion) {
|
|
246
|
+
issues.push(`${compatRel} schemaVersion must be "${rootPolicy.schemaVersion}", got "${json.schemaVersion}"`);
|
|
247
|
+
}
|
|
248
|
+
if (json.pluginVersion !== rootPolicy.pluginVersion) {
|
|
249
|
+
issues.push(`${compatRel} pluginVersion must be "${rootPolicy.pluginVersion}", got "${json.pluginVersion}"`);
|
|
250
|
+
}
|
|
251
|
+
const ag = json.artifactGraph;
|
|
252
|
+
const rag = rootPolicy.artifactGraph;
|
|
253
|
+
if (!ag) {
|
|
254
|
+
issues.push(`${compatRel} missing artifactGraph object`);
|
|
255
|
+
} else {
|
|
256
|
+
if (ag.verifiedVersion !== rag.verifiedVersion) {
|
|
257
|
+
issues.push(`${compatRel} artifactGraph.verifiedVersion must be "${rag.verifiedVersion}", got "${ag.verifiedVersion}"`);
|
|
258
|
+
}
|
|
259
|
+
if (ag.installSpec !== rag.installSpec) {
|
|
260
|
+
issues.push(`${compatRel} artifactGraph.installSpec must be "${rag.installSpec}", got "${ag.installSpec}"`);
|
|
261
|
+
}
|
|
262
|
+
if (ag.githubFallbackSpec !== rag.githubFallbackSpec) {
|
|
263
|
+
issues.push(`${compatRel} artifactGraph.githubFallbackSpec must be "${rag.githubFallbackSpec}", got "${ag.githubFallbackSpec}"`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
} catch (error) {
|
|
267
|
+
if (error.code === 'ENOENT') {
|
|
268
|
+
issues.push(`${compatRel} is missing (required for adapter ${adapter})`);
|
|
269
|
+
} else if (error instanceof SyntaxError) {
|
|
270
|
+
issues.push(`${compatRel} is not valid JSON: ${error.message}`);
|
|
271
|
+
} else {
|
|
272
|
+
issues.push(`${compatRel} error: ${error.message}`);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// ── Install spec classification (argument-order agnostic) ─────────────
|
|
278
|
+
|
|
279
|
+
// Recognized package-manager install command prefixes.
|
|
280
|
+
const INSTALL_CMD_PREFIXES = [
|
|
281
|
+
['npm', 'install'],
|
|
282
|
+
['npm', 'i'],
|
|
283
|
+
['pnpm', 'add'],
|
|
284
|
+
];
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Classify a single code-block line for forbidden install specs.
|
|
288
|
+
* Returns a description string if the spec is forbidden, null otherwise.
|
|
289
|
+
*
|
|
290
|
+
* Strategy: split the line into tokens, match a known install command prefix,
|
|
291
|
+
* then scan ALL remaining tokens for an artifact-graph package spec — no
|
|
292
|
+
* enumeration of flag orderings.
|
|
293
|
+
*
|
|
294
|
+
* Only exact matches against expectedInstallSpec and expectedGithubSpec are allowed.
|
|
295
|
+
*
|
|
296
|
+
* @param {string} line
|
|
297
|
+
* @param {string} expectedInstallSpec - e.g. "artifact-graph@0.3.1"
|
|
298
|
+
* @param {string} expectedGithubSpec - e.g. "github:ifoohoo/artifact-graph#artifact-graph-v0.3.1"
|
|
299
|
+
*/
|
|
300
|
+
function classifyInstallSpecViolation(line, expectedInstallSpec, expectedGithubSpec) {
|
|
301
|
+
const tokens = line.split(/\s+/);
|
|
302
|
+
|
|
303
|
+
// Standalone GitHub repo (no install command prefix needed)
|
|
304
|
+
// Match both the current ifoohoo slug and the pre-migration mzdbxqh slug so
|
|
305
|
+
// stale references are still flagged as mismatches.
|
|
306
|
+
if (tokens.length === 1 && /^github:(?:mzdbxqh|ifoohoo)\/artifact-graph/.test(tokens[0])) {
|
|
307
|
+
if (tokens[0] === expectedGithubSpec) return null;
|
|
308
|
+
return `GitHub spec mismatch: got "${tokens[0]}", expected "${expectedGithubSpec}"`;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (tokens.length < 3) return null;
|
|
312
|
+
|
|
313
|
+
// Match command prefix (e.g. "npm install", "pnpm add")
|
|
314
|
+
let cmdLen = 0;
|
|
315
|
+
for (const [bin, sub] of INSTALL_CMD_PREFIXES) {
|
|
316
|
+
if (tokens[0] === bin && tokens[1] === sub) {
|
|
317
|
+
cmdLen = 2;
|
|
318
|
+
break;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
if (cmdLen === 0) return null;
|
|
322
|
+
|
|
323
|
+
// Scan remaining tokens for a package spec containing artifact-graph
|
|
324
|
+
for (const tok of tokens.slice(cmdLen)) {
|
|
325
|
+
// Skip flags
|
|
326
|
+
if (tok.startsWith('-')) continue;
|
|
327
|
+
|
|
328
|
+
// GitHub spec: github:ifoohoo/artifact-graph or github:ifoohoo/artifact-graph#tag
|
|
329
|
+
if (/^github:(?:mzdbxqh|ifoohoo)\/artifact-graph/.test(tok)) {
|
|
330
|
+
if (tok === expectedGithubSpec) continue;
|
|
331
|
+
return `GitHub spec mismatch: got "${tok}", expected "${expectedGithubSpec}"`;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// npm/pnpm package spec
|
|
335
|
+
if (/^artifact-graph(@|$)/.test(tok)) {
|
|
336
|
+
if (tok === expectedInstallSpec) continue;
|
|
337
|
+
return `install spec mismatch: got "${tok}", expected "${expectedInstallSpec}"`;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return null;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Check public docs for forbidden install patterns.
|
|
346
|
+
* Scans real public snapshot files at plugin root:
|
|
347
|
+
* INSTALL.md, README.md, README.zh-CN.md, adapters/codex/INSTALL.md, adapters/claude/INSTALL.md
|
|
348
|
+
*
|
|
349
|
+
* For each fenced code block line, extracts the artifact-graph package spec
|
|
350
|
+
* from any argument position and classifies it — no enumeration of flag orderings.
|
|
351
|
+
*
|
|
352
|
+
* @param {string} pluginRoot
|
|
353
|
+
* @param {{installSpec: string, githubFallbackSpec: string}} artifactGraphPolicy
|
|
354
|
+
* @param {string[]} issues
|
|
355
|
+
*/
|
|
356
|
+
async function checkDocsForForbiddenPatterns(pluginRoot, artifactGraphPolicy, issues) {
|
|
357
|
+
const { installSpec: expectedInstallSpec, githubFallbackSpec: expectedGithubSpec } = artifactGraphPolicy;
|
|
358
|
+
const docPaths = [
|
|
359
|
+
'INSTALL.md',
|
|
360
|
+
'README.md',
|
|
361
|
+
'README.zh-CN.md',
|
|
362
|
+
join('adapters', 'codex', 'INSTALL.md'),
|
|
363
|
+
join('adapters', 'claude', 'INSTALL.md'),
|
|
364
|
+
];
|
|
365
|
+
|
|
366
|
+
for (const relPath of docPaths) {
|
|
367
|
+
const filePath = join(pluginRoot, relPath);
|
|
368
|
+
let content;
|
|
369
|
+
try {
|
|
370
|
+
content = await readFile(filePath, 'utf8');
|
|
371
|
+
} catch (error) {
|
|
372
|
+
if (error.code === 'ENOENT') {
|
|
373
|
+
issues.push(`${relPath} is missing (required public doc)`);
|
|
374
|
+
}
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const lines = extractCodeBlocks(content)
|
|
379
|
+
.split('\n')
|
|
380
|
+
.map((l) => l.trim())
|
|
381
|
+
.filter(Boolean);
|
|
382
|
+
|
|
383
|
+
for (const line of lines) {
|
|
384
|
+
const violation = classifyInstallSpecViolation(line, expectedInstallSpec, expectedGithubSpec);
|
|
385
|
+
if (violation) {
|
|
386
|
+
issues.push(`${relPath} code blocks contain forbidden install pattern: ${violation}`);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function extractCodeBlocks(markdown) {
|
|
393
|
+
const blocks = [];
|
|
394
|
+
const re = /```[\w-]*\n([\s\S]*?)```/g;
|
|
395
|
+
let m;
|
|
396
|
+
while ((m = re.exec(markdown)) !== null) {
|
|
397
|
+
blocks.push(m[1]);
|
|
398
|
+
}
|
|
399
|
+
return blocks.join('\n');
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Check adapter doctor and lib files exist.
|
|
404
|
+
* Fail-closed: missing file produces an issue.
|
|
405
|
+
*/
|
|
406
|
+
async function checkAdapterDoctorFiles(pluginRoot, adapter, issues) {
|
|
407
|
+
const requiredFiles = [
|
|
408
|
+
join('adapters', adapter, 'scripts', 'doctor.mjs'),
|
|
409
|
+
join('adapters', adapter, 'scripts', 'lib', 'compatibility-policy.mjs'),
|
|
410
|
+
join('adapters', adapter, 'scripts', 'lib', 'artifact-graph-runtime.mjs'),
|
|
411
|
+
];
|
|
412
|
+
|
|
413
|
+
for (const relPath of requiredFiles) {
|
|
414
|
+
const filePath = join(pluginRoot, relPath);
|
|
415
|
+
try {
|
|
416
|
+
await readFile(filePath, 'utf8');
|
|
417
|
+
} catch (error) {
|
|
418
|
+
if (error.code === 'ENOENT') {
|
|
419
|
+
issues.push(`${relPath} is missing (required for adapter ${adapter})`);
|
|
420
|
+
} else {
|
|
421
|
+
issues.push(`${relPath} error: ${error.message}`);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
function deepFreeze(obj) {
|
|
428
|
+
Object.freeze(obj);
|
|
429
|
+
for (const value of Object.values(obj)) {
|
|
430
|
+
if (value && typeof value === 'object' && !Object.isFrozen(value)) {
|
|
431
|
+
deepFreeze(value);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
return obj;
|
|
435
|
+
}
|