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,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
|
+
}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
// @feature ACA16 @scenario S-50
|
|
2
|
+
// Self-contained, fail-closed YAML subset parser for artifact workflow profiles.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Parse the deterministic YAML subset accepted by artifact-workflow-profile.
|
|
6
|
+
* Blocks may use any positive child indentation, but every sibling in one block
|
|
7
|
+
* must use the exact same indentation. Sequences contain scalar values only.
|
|
8
|
+
*
|
|
9
|
+
* @param {string} source
|
|
10
|
+
* @returns {object|null}
|
|
11
|
+
*/
|
|
12
|
+
export function parseMinimalYaml(source) {
|
|
13
|
+
if (typeof source !== 'string') {
|
|
14
|
+
throw new TypeError('parseMinimalYaml expects a string');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const lines = preprocess(source);
|
|
18
|
+
if (lines.length === 0) return null;
|
|
19
|
+
if (lines[0].indent !== 0) {
|
|
20
|
+
throw yamlError(lines[0], 'invalid top-level indentation');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const parsed = parseBlock(lines, 0, 0);
|
|
24
|
+
if (parsed.next !== lines.length) {
|
|
25
|
+
throw yamlError(lines[parsed.next], 'unexpected indentation');
|
|
26
|
+
}
|
|
27
|
+
if (Array.isArray(parsed.value)) {
|
|
28
|
+
throw yamlError(lines[0], 'top-level sequence is not supported');
|
|
29
|
+
}
|
|
30
|
+
return parsed.value;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function preprocess(source) {
|
|
34
|
+
const result = [];
|
|
35
|
+
const rawLines = source.replaceAll('\r\n', '\n').replaceAll('\r', '\n').split('\n');
|
|
36
|
+
|
|
37
|
+
for (let index = 0; index < rawLines.length; index += 1) {
|
|
38
|
+
const raw = rawLines[index];
|
|
39
|
+
if (/^[ ]*$/.test(raw) || /^[ ]*#/.test(raw)) continue;
|
|
40
|
+
|
|
41
|
+
const leading = raw.match(/^[\t ]*/)?.[0] ?? '';
|
|
42
|
+
if (leading.includes('\t')) {
|
|
43
|
+
throw new Error(`Unsupported YAML: tab indentation at line ${index + 1}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const text = raw.slice(leading.length);
|
|
47
|
+
if (text === '---') {
|
|
48
|
+
throw new Error(`Unsupported YAML: multi-document separator --- at line ${index + 1}`);
|
|
49
|
+
}
|
|
50
|
+
if (text === '...') {
|
|
51
|
+
throw new Error(`Unsupported YAML: document end marker ... at line ${index + 1}`);
|
|
52
|
+
}
|
|
53
|
+
result.push({ line: index + 1, indent: leading.length, text });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function parseBlock(lines, index, indent) {
|
|
60
|
+
const first = lines[index];
|
|
61
|
+
if (!first || first.indent !== indent) {
|
|
62
|
+
throw yamlError(first, 'invalid block indentation');
|
|
63
|
+
}
|
|
64
|
+
return first.text.startsWith('-')
|
|
65
|
+
? parseSequence(lines, index, indent)
|
|
66
|
+
: parseMapping(lines, index, indent);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Prototype pollution keys that must never appear as mapping keys.
|
|
70
|
+
const PROTOTYPE_POLLUTION_KEYS = new Set(['__proto__', 'prototype', 'constructor']);
|
|
71
|
+
|
|
72
|
+
function parseMapping(lines, start, indent) {
|
|
73
|
+
const value = Object.create(null);
|
|
74
|
+
let index = start;
|
|
75
|
+
|
|
76
|
+
while (index < lines.length) {
|
|
77
|
+
const line = lines[index];
|
|
78
|
+
if (line.indent < indent) break;
|
|
79
|
+
if (line.indent > indent) {
|
|
80
|
+
throw yamlError(line, `unexpected indentation; expected ${indent} spaces`);
|
|
81
|
+
}
|
|
82
|
+
if (line.text.startsWith('-')) {
|
|
83
|
+
throw yamlError(line, 'mixed mapping and sequence block');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const entry = parseMappingEntry(line);
|
|
87
|
+
if (Object.prototype.hasOwnProperty.call(value, entry.key)) {
|
|
88
|
+
throw yamlError(line, `duplicate key "${entry.key}"`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (entry.rawValue !== '') {
|
|
92
|
+
value[entry.key] = parseScalar(entry.rawValue, line);
|
|
93
|
+
index += 1;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const child = lines[index + 1];
|
|
98
|
+
if (!child || child.indent <= indent) {
|
|
99
|
+
throw yamlError(line, `key "${entry.key}" requires a nested block`);
|
|
100
|
+
}
|
|
101
|
+
const parsedChild = parseBlock(lines, index + 1, child.indent);
|
|
102
|
+
value[entry.key] = parsedChild.value;
|
|
103
|
+
index = parsedChild.next;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return { value, next: index };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function parseSequence(lines, start, indent) {
|
|
110
|
+
const value = [];
|
|
111
|
+
let index = start;
|
|
112
|
+
|
|
113
|
+
while (index < lines.length) {
|
|
114
|
+
const line = lines[index];
|
|
115
|
+
if (line.indent < indent) break;
|
|
116
|
+
if (line.indent > indent) {
|
|
117
|
+
throw yamlError(line, `unexpected indentation; expected ${indent} spaces`);
|
|
118
|
+
}
|
|
119
|
+
if (!line.text.startsWith('-')) {
|
|
120
|
+
throw yamlError(line, 'mixed sequence and mapping block');
|
|
121
|
+
}
|
|
122
|
+
if (!line.text.startsWith('- ')) {
|
|
123
|
+
throw yamlError(line, 'sequence items must use "- <scalar>"');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const rawValue = line.text.slice(2).trim();
|
|
127
|
+
if (rawValue === '') {
|
|
128
|
+
throw yamlError(line, 'empty or nested sequence items are not supported');
|
|
129
|
+
}
|
|
130
|
+
if (looksLikeMappingEntry(rawValue)) {
|
|
131
|
+
throw yamlError(line, 'mapping sequence items are not supported');
|
|
132
|
+
}
|
|
133
|
+
value.push(parseScalar(rawValue, line));
|
|
134
|
+
index += 1;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return { value, next: index };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function parseMappingEntry(line) {
|
|
141
|
+
if (/^["']/.test(line.text) || line.text.startsWith('?')) {
|
|
142
|
+
throw yamlError(line, 'complex key is not supported');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const separator = line.text.indexOf(':');
|
|
146
|
+
if (separator <= 0) {
|
|
147
|
+
throw yamlError(line, `unparseable mapping entry: ${line.text}`);
|
|
148
|
+
}
|
|
149
|
+
const key = line.text.slice(0, separator).trim();
|
|
150
|
+
if (!/^[A-Za-z0-9_.-]+$/.test(key)) {
|
|
151
|
+
throw yamlError(line, `unsupported mapping key "${key}"`);
|
|
152
|
+
}
|
|
153
|
+
if (PROTOTYPE_POLLUTION_KEYS.has(key)) {
|
|
154
|
+
throw yamlError(line, `prototype pollution key "${key}" is not allowed`);
|
|
155
|
+
}
|
|
156
|
+
return { key, rawValue: line.text.slice(separator + 1).trim() };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function looksLikeMappingEntry(value) {
|
|
160
|
+
return /^[A-Za-z0-9_.-]+\s*:/.test(value);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function parseScalar(raw, line) {
|
|
164
|
+
rejectUnsupportedScalar(raw, line);
|
|
165
|
+
|
|
166
|
+
if (raw === '{}') return Object.create(null);
|
|
167
|
+
if (raw === '[]') return [];
|
|
168
|
+
if (raw === 'true') return true;
|
|
169
|
+
if (raw === 'false') return false;
|
|
170
|
+
if (raw === 'null') return null;
|
|
171
|
+
if (/^-?\d+$/.test(raw)) return Number.parseInt(raw, 10);
|
|
172
|
+
|
|
173
|
+
if (raw.startsWith('"') || raw.endsWith('"')) {
|
|
174
|
+
if (!(raw.startsWith('"') && raw.endsWith('"'))) {
|
|
175
|
+
throw yamlError(line, 'unterminated double-quoted scalar');
|
|
176
|
+
}
|
|
177
|
+
try {
|
|
178
|
+
return JSON.parse(raw);
|
|
179
|
+
} catch {
|
|
180
|
+
throw yamlError(line, 'invalid double-quoted scalar');
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (raw.startsWith("'") || raw.endsWith("'")) {
|
|
185
|
+
if (!(raw.startsWith("'") && raw.endsWith("'"))) {
|
|
186
|
+
throw yamlError(line, 'unterminated single-quoted scalar');
|
|
187
|
+
}
|
|
188
|
+
const body = raw.slice(1, -1);
|
|
189
|
+
if (/(^|[^'])'(?!')/.test(body)) {
|
|
190
|
+
throw yamlError(line, 'invalid single-quoted scalar');
|
|
191
|
+
}
|
|
192
|
+
return body.replaceAll("''", "'");
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (/^[\[\]{},]/.test(raw)) {
|
|
196
|
+
throw yamlError(line, 'flow collections are not supported');
|
|
197
|
+
}
|
|
198
|
+
return raw;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function rejectUnsupportedScalar(raw, line) {
|
|
202
|
+
if (/^(?:&|\*)\S/.test(raw) || /(?:^|\s)(?:&|\*)\S/.test(raw)) {
|
|
203
|
+
throw yamlError(line, 'anchor or alias is not supported');
|
|
204
|
+
}
|
|
205
|
+
if (/^(?:!|!!)/.test(raw)) {
|
|
206
|
+
throw yamlError(line, 'tag is not supported');
|
|
207
|
+
}
|
|
208
|
+
if (raw === '|' || raw === '>' || /^[|>]\d?[+-]?$/.test(raw)) {
|
|
209
|
+
throw yamlError(line, 'block scalar is not supported');
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function yamlError(line, message) {
|
|
214
|
+
const suffix = line ? ` at line ${line.line}` : '';
|
|
215
|
+
return new Error(`Unsupported YAML: ${message}${suffix}`);
|
|
216
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export function validateCatalogPackageVersion(catalog, packageJson) {
|
|
2
|
+
const catalogVersion = catalog?.catalog?.version;
|
|
3
|
+
const packageVersion = packageJson?.version;
|
|
4
|
+
return catalogVersion === packageVersion
|
|
5
|
+
? []
|
|
6
|
+
: [`catalog.version ${String(catalogVersion)} must equal package version ${String(packageVersion)}`];
|
|
7
|
+
}
|