spectra-cli 4.2.0 → 4.3.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/README.md +12 -5
- package/dist/.spectra-build-meta.json +9 -0
- package/dist/adapters/language-adapter.d.ts +7 -12
- package/dist/adapters/language-adapter.d.ts.map +1 -1
- package/dist/adapters/python-adapter.d.ts.map +1 -1
- package/dist/adapters/python-adapter.js +11 -2
- package/dist/adapters/python-adapter.js.map +1 -1
- package/dist/batch/batch-orchestrator.d.ts +44 -1
- package/dist/batch/batch-orchestrator.d.ts.map +1 -1
- package/dist/batch/batch-orchestrator.js +312 -76
- package/dist/batch/batch-orchestrator.js.map +1 -1
- package/dist/batch/delta-regenerator.d.ts.map +1 -1
- package/dist/batch/delta-regenerator.js +21 -28
- package/dist/batch/delta-regenerator.js.map +1 -1
- package/dist/batch/module-grouper.d.ts +7 -0
- package/dist/batch/module-grouper.d.ts.map +1 -1
- package/dist/batch/module-grouper.js +2 -0
- package/dist/batch/module-grouper.js.map +1 -1
- package/dist/batch/regen-plan.d.ts +78 -0
- package/dist/batch/regen-plan.d.ts.map +1 -0
- package/dist/batch/regen-plan.js +93 -0
- package/dist/batch/regen-plan.js.map +1 -0
- package/dist/cli/commands/batch.d.ts.map +1 -1
- package/dist/cli/commands/batch.js +36 -8
- package/dist/cli/commands/batch.js.map +1 -1
- package/dist/cli/commands/scaffold-kb.d.ts +9 -0
- package/dist/cli/commands/scaffold-kb.d.ts.map +1 -0
- package/dist/cli/commands/scaffold-kb.js +179 -0
- package/dist/cli/commands/scaffold-kb.js.map +1 -0
- package/dist/cli/index.js +24 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/utils/error-handler.d.ts +0 -6
- package/dist/cli/utils/error-handler.d.ts.map +1 -1
- package/dist/cli/utils/error-handler.js +0 -13
- package/dist/cli/utils/error-handler.js.map +1 -1
- package/dist/cli/utils/parse-args.d.ts +43 -3
- package/dist/cli/utils/parse-args.d.ts.map +1 -1
- package/dist/cli/utils/parse-args.js +98 -12
- package/dist/cli/utils/parse-args.js.map +1 -1
- package/dist/cli/version-meta.d.ts +16 -0
- package/dist/cli/version-meta.d.ts.map +1 -0
- package/dist/cli/version-meta.js +28 -0
- package/dist/cli/version-meta.js.map +1 -0
- package/dist/core/ast-analyzer.d.ts.map +1 -1
- package/dist/core/ast-analyzer.js +7 -11
- package/dist/core/ast-analyzer.js.map +1 -1
- package/dist/core/import-resolver.d.ts +70 -67
- package/dist/core/import-resolver.d.ts.map +1 -1
- package/dist/core/import-resolver.js +341 -200
- package/dist/core/import-resolver.js.map +1 -1
- package/dist/core/single-spec-orchestrator.d.ts +21 -0
- package/dist/core/single-spec-orchestrator.d.ts.map +1 -1
- package/dist/core/single-spec-orchestrator.js +41 -14
- package/dist/core/single-spec-orchestrator.js.map +1 -1
- package/dist/core/skeleton-hash.d.ts +33 -0
- package/dist/core/skeleton-hash.d.ts.map +1 -0
- package/dist/core/skeleton-hash.js +76 -0
- package/dist/core/skeleton-hash.js.map +1 -0
- package/dist/core/tree-sitter-analyzer.d.ts +3 -2
- package/dist/core/tree-sitter-analyzer.d.ts.map +1 -1
- package/dist/core/tree-sitter-analyzer.js +5 -6
- package/dist/core/tree-sitter-analyzer.js.map +1 -1
- package/dist/core/tree-sitter-fallback.d.ts +7 -2
- package/dist/core/tree-sitter-fallback.d.ts.map +1 -1
- package/dist/core/tree-sitter-fallback.js +18 -15
- package/dist/core/tree-sitter-fallback.js.map +1 -1
- package/dist/extraction/image-extractor.d.ts.map +1 -1
- package/dist/extraction/image-extractor.js +3 -1
- package/dist/extraction/image-extractor.js.map +1 -1
- package/dist/extraction/markdown-extractor.d.ts.map +1 -1
- package/dist/extraction/markdown-extractor.js +3 -1
- package/dist/extraction/markdown-extractor.js.map +1 -1
- package/dist/generator/frontmatter.d.ts +5 -0
- package/dist/generator/frontmatter.d.ts.map +1 -1
- package/dist/generator/frontmatter.js +4 -0
- package/dist/generator/frontmatter.js.map +1 -1
- package/dist/kb-mcp/index.d.ts +14 -0
- package/dist/kb-mcp/index.d.ts.map +1 -0
- package/dist/kb-mcp/index.js +24 -0
- package/dist/kb-mcp/index.js.map +1 -0
- package/dist/kb-mcp/lib/kb-error.d.ts +21 -0
- package/dist/kb-mcp/lib/kb-error.d.ts.map +1 -0
- package/dist/kb-mcp/lib/kb-error.js +28 -0
- package/dist/kb-mcp/lib/kb-error.js.map +1 -0
- package/dist/kb-mcp/lib/kb-locator.d.ts +58 -0
- package/dist/kb-mcp/lib/kb-locator.d.ts.map +1 -0
- package/dist/kb-mcp/lib/kb-locator.js +85 -0
- package/dist/kb-mcp/lib/kb-locator.js.map +1 -0
- package/dist/kb-mcp/lib/result-merger.d.ts +35 -0
- package/dist/kb-mcp/lib/result-merger.d.ts.map +1 -0
- package/dist/kb-mcp/lib/result-merger.js +64 -0
- package/dist/kb-mcp/lib/result-merger.js.map +1 -0
- package/dist/kb-mcp/server.d.ts +12 -0
- package/dist/kb-mcp/server.d.ts.map +1 -0
- package/dist/kb-mcp/server.js +29 -0
- package/dist/kb-mcp/server.js.map +1 -0
- package/dist/kb-mcp/tools/kb-api-lookup.d.ts +28 -0
- package/dist/kb-mcp/tools/kb-api-lookup.d.ts.map +1 -0
- package/dist/kb-mcp/tools/kb-api-lookup.js +237 -0
- package/dist/kb-mcp/tools/kb-api-lookup.js.map +1 -0
- package/dist/kb-mcp/tools/kb-doc-lookup.d.ts +20 -0
- package/dist/kb-mcp/tools/kb-doc-lookup.d.ts.map +1 -0
- package/dist/kb-mcp/tools/kb-doc-lookup.js +92 -0
- package/dist/kb-mcp/tools/kb-doc-lookup.js.map +1 -0
- package/dist/kb-mcp/tools/kb-search.d.ts +23 -0
- package/dist/kb-mcp/tools/kb-search.d.ts.map +1 -0
- package/dist/kb-mcp/tools/kb-search.js +126 -0
- package/dist/kb-mcp/tools/kb-search.js.map +1 -0
- package/dist/knowledge-graph/import-resolver.d.ts +11 -86
- package/dist/knowledge-graph/import-resolver.d.ts.map +1 -1
- package/dist/knowledge-graph/import-resolver.js +10 -333
- package/dist/knowledge-graph/import-resolver.js.map +1 -1
- package/dist/knowledge-graph/incremental.d.ts +25 -4
- package/dist/knowledge-graph/incremental.d.ts.map +1 -1
- package/dist/knowledge-graph/incremental.js +37 -15
- package/dist/knowledge-graph/incremental.js.map +1 -1
- package/dist/knowledge-graph/index.d.ts +11 -0
- package/dist/knowledge-graph/index.d.ts.map +1 -1
- package/dist/knowledge-graph/index.js +43 -1
- package/dist/knowledge-graph/index.js.map +1 -1
- package/dist/knowledge-graph/module-derivation.d.ts +14 -0
- package/dist/knowledge-graph/module-derivation.d.ts.map +1 -1
- package/dist/knowledge-graph/module-derivation.js +44 -44
- package/dist/knowledge-graph/module-derivation.js.map +1 -1
- package/dist/knowledge-graph/persistence.d.ts +56 -16
- package/dist/knowledge-graph/persistence.d.ts.map +1 -1
- package/dist/knowledge-graph/persistence.js +86 -32
- package/dist/knowledge-graph/persistence.js.map +1 -1
- package/dist/knowledge-graph/query-helpers.d.ts +39 -12
- package/dist/knowledge-graph/query-helpers.d.ts.map +1 -1
- package/dist/knowledge-graph/query-helpers.js +199 -34
- package/dist/knowledge-graph/query-helpers.js.map +1 -1
- package/dist/knowledge-graph/relativize.d.ts +51 -0
- package/dist/knowledge-graph/relativize.d.ts.map +1 -0
- package/dist/knowledge-graph/relativize.js +104 -0
- package/dist/knowledge-graph/relativize.js.map +1 -0
- package/dist/mcp/agent-context-tools.d.ts +3 -54
- package/dist/mcp/agent-context-tools.d.ts.map +1 -1
- package/dist/mcp/agent-context-tools.js +141 -189
- package/dist/mcp/agent-context-tools.js.map +1 -1
- package/dist/mcp/file-nav-tools.d.ts +42 -0
- package/dist/mcp/file-nav-tools.d.ts.map +1 -0
- package/dist/mcp/file-nav-tools.js +318 -0
- package/dist/mcp/file-nav-tools.js.map +1 -0
- package/dist/mcp/graph-tools.d.ts +2 -0
- package/dist/mcp/graph-tools.d.ts.map +1 -1
- package/dist/mcp/graph-tools.js +134 -123
- package/dist/mcp/graph-tools.js.map +1 -1
- package/dist/mcp/lib/file-nav-helpers.d.ts +128 -0
- package/dist/mcp/lib/file-nav-helpers.d.ts.map +1 -0
- package/dist/mcp/lib/file-nav-helpers.js +305 -0
- package/dist/mcp/lib/file-nav-helpers.js.map +1 -0
- package/dist/mcp/lib/telemetry.d.ts +69 -0
- package/dist/mcp/lib/telemetry.d.ts.map +1 -0
- package/dist/mcp/lib/telemetry.js +113 -0
- package/dist/mcp/lib/telemetry.js.map +1 -0
- package/dist/mcp/lib/tool-response.d.ts +46 -0
- package/dist/mcp/lib/tool-response.d.ts.map +1 -0
- package/dist/mcp/lib/tool-response.js +83 -0
- package/dist/mcp/lib/tool-response.js.map +1 -0
- package/dist/mcp/server.d.ts +13 -2
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +209 -150
- package/dist/mcp/server.js.map +1 -1
- package/dist/models/code-skeleton.d.ts +4 -2
- package/dist/models/code-skeleton.d.ts.map +1 -1
- package/dist/models/code-skeleton.js +2 -1
- package/dist/models/code-skeleton.js.map +1 -1
- package/dist/models/module-spec.d.ts +18 -0
- package/dist/models/module-spec.d.ts.map +1 -1
- package/dist/models/module-spec.js +6 -0
- package/dist/models/module-spec.js.map +1 -1
- package/dist/panoramic/builders/doc-graph-builder.d.ts +13 -0
- package/dist/panoramic/builders/doc-graph-builder.d.ts.map +1 -1
- package/dist/panoramic/builders/doc-graph-builder.js +20 -0
- package/dist/panoramic/builders/doc-graph-builder.js.map +1 -1
- package/dist/panoramic/graph/graph-builder.d.ts +72 -2
- package/dist/panoramic/graph/graph-builder.d.ts.map +1 -1
- package/dist/panoramic/graph/graph-builder.js +257 -51
- package/dist/panoramic/graph/graph-builder.js.map +1 -1
- package/dist/panoramic/graph/graph-query.d.ts +16 -0
- package/dist/panoramic/graph/graph-query.d.ts.map +1 -1
- package/dist/panoramic/graph/graph-query.js +49 -1
- package/dist/panoramic/graph/graph-query.js.map +1 -1
- package/dist/panoramic/graph/index.d.ts +2 -1
- package/dist/panoramic/graph/index.d.ts.map +1 -1
- package/dist/panoramic/graph/index.js +1 -1
- package/dist/panoramic/graph/index.js.map +1 -1
- package/dist/panoramic/pipelines/adr-evidence-verifier.d.ts.map +1 -1
- package/dist/panoramic/pipelines/adr-evidence-verifier.js +1 -29
- package/dist/panoramic/pipelines/adr-evidence-verifier.js.map +1 -1
- package/dist/panoramic/query.d.ts +1 -0
- package/dist/panoramic/query.d.ts.map +1 -1
- package/dist/panoramic/query.js +5 -0
- package/dist/panoramic/query.js.map +1 -1
- package/dist/scaffold-kb/api-entities-serializer.d.ts +12 -0
- package/dist/scaffold-kb/api-entities-serializer.d.ts.map +1 -0
- package/dist/scaffold-kb/api-entities-serializer.js +144 -0
- package/dist/scaffold-kb/api-entities-serializer.js.map +1 -0
- package/dist/scaffold-kb/arbitration.d.ts +44 -0
- package/dist/scaffold-kb/arbitration.d.ts.map +1 -0
- package/dist/scaffold-kb/arbitration.js +144 -0
- package/dist/scaffold-kb/arbitration.js.map +1 -0
- package/dist/scaffold-kb/chunk-splitter.d.ts +20 -0
- package/dist/scaffold-kb/chunk-splitter.d.ts.map +1 -0
- package/dist/scaffold-kb/chunk-splitter.js +309 -0
- package/dist/scaffold-kb/chunk-splitter.js.map +1 -0
- package/dist/scaffold-kb/doc-graph-builder.d.ts +26 -0
- package/dist/scaffold-kb/doc-graph-builder.d.ts.map +1 -0
- package/dist/scaffold-kb/doc-graph-builder.js +72 -0
- package/dist/scaffold-kb/doc-graph-builder.js.map +1 -0
- package/dist/scaffold-kb/entity-extractor.d.ts +40 -0
- package/dist/scaffold-kb/entity-extractor.d.ts.map +1 -0
- package/dist/scaffold-kb/entity-extractor.js +0 -0
- package/dist/scaffold-kb/entity-extractor.js.map +1 -0
- package/dist/scaffold-kb/entity-heuristic.d.ts +14 -0
- package/dist/scaffold-kb/entity-heuristic.d.ts.map +1 -0
- package/dist/scaffold-kb/entity-heuristic.js +107 -0
- package/dist/scaffold-kb/entity-heuristic.js.map +1 -0
- package/dist/scaffold-kb/entity-matcher.d.ts +20 -0
- package/dist/scaffold-kb/entity-matcher.d.ts.map +1 -0
- package/dist/scaffold-kb/entity-matcher.js +53 -0
- package/dist/scaffold-kb/entity-matcher.js.map +1 -0
- package/dist/scaffold-kb/entity-util.d.ts +11 -0
- package/dist/scaffold-kb/entity-util.d.ts.map +1 -0
- package/dist/scaffold-kb/entity-util.js +23 -0
- package/dist/scaffold-kb/entity-util.js.map +1 -0
- package/dist/scaffold-kb/evidence-envelope.d.ts +17 -0
- package/dist/scaffold-kb/evidence-envelope.d.ts.map +1 -0
- package/dist/scaffold-kb/evidence-envelope.js +32 -0
- package/dist/scaffold-kb/evidence-envelope.js.map +1 -0
- package/dist/scaffold-kb/index.d.ts +20 -0
- package/dist/scaffold-kb/index.d.ts.map +1 -0
- package/dist/scaffold-kb/index.js +102 -0
- package/dist/scaffold-kb/index.js.map +1 -0
- package/dist/scaffold-kb/ingest/ingest-core.d.ts +47 -0
- package/dist/scaffold-kb/ingest/ingest-core.d.ts.map +1 -0
- package/dist/scaffold-kb/ingest/ingest-core.js +231 -0
- package/dist/scaffold-kb/ingest/ingest-core.js.map +1 -0
- package/dist/scaffold-kb/ingest/office-parser.d.ts +33 -0
- package/dist/scaffold-kb/ingest/office-parser.d.ts.map +1 -0
- package/dist/scaffold-kb/ingest/office-parser.js +187 -0
- package/dist/scaffold-kb/ingest/office-parser.js.map +1 -0
- package/dist/scaffold-kb/ingest/url-fetcher.d.ts +43 -0
- package/dist/scaffold-kb/ingest/url-fetcher.d.ts.map +1 -0
- package/dist/scaffold-kb/ingest/url-fetcher.js +245 -0
- package/dist/scaffold-kb/ingest/url-fetcher.js.map +1 -0
- package/dist/scaffold-kb/ingester.d.ts +38 -0
- package/dist/scaffold-kb/ingester.d.ts.map +1 -0
- package/dist/scaffold-kb/ingester.js +261 -0
- package/dist/scaffold-kb/ingester.js.map +1 -0
- package/dist/scaffold-kb/injection-format.d.ts +25 -0
- package/dist/scaffold-kb/injection-format.d.ts.map +1 -0
- package/dist/scaffold-kb/injection-format.js +55 -0
- package/dist/scaffold-kb/injection-format.js.map +1 -0
- package/dist/scaffold-kb/kb-writer.d.ts +13 -0
- package/dist/scaffold-kb/kb-writer.d.ts.map +1 -0
- package/dist/scaffold-kb/kb-writer.js +69 -0
- package/dist/scaffold-kb/kb-writer.js.map +1 -0
- package/dist/scaffold-kb/keyword-extract.d.ts +16 -0
- package/dist/scaffold-kb/keyword-extract.d.ts.map +1 -0
- package/dist/scaffold-kb/keyword-extract.js +49 -0
- package/dist/scaffold-kb/keyword-extract.js.map +1 -0
- package/dist/scaffold-kb/query-sanitizer.d.ts +28 -0
- package/dist/scaffold-kb/query-sanitizer.d.ts.map +1 -0
- package/dist/scaffold-kb/query-sanitizer.js +38 -0
- package/dist/scaffold-kb/query-sanitizer.js.map +1 -0
- package/dist/scaffold-kb/recall-eval.d.ts +48 -0
- package/dist/scaffold-kb/recall-eval.d.ts.map +1 -0
- package/dist/scaffold-kb/recall-eval.js +55 -0
- package/dist/scaffold-kb/recall-eval.js.map +1 -0
- package/dist/scaffold-kb/schema-compat.d.ts +22 -0
- package/dist/scaffold-kb/schema-compat.d.ts.map +1 -0
- package/dist/scaffold-kb/schema-compat.js +32 -0
- package/dist/scaffold-kb/schema-compat.js.map +1 -0
- package/dist/scaffold-kb/search-core.d.ts +42 -0
- package/dist/scaffold-kb/search-core.d.ts.map +1 -0
- package/dist/scaffold-kb/search-core.js +89 -0
- package/dist/scaffold-kb/search-core.js.map +1 -0
- package/dist/scaffold-kb/sqlite-engine.d.ts +58 -0
- package/dist/scaffold-kb/sqlite-engine.d.ts.map +1 -0
- package/dist/scaffold-kb/sqlite-engine.js +61 -0
- package/dist/scaffold-kb/sqlite-engine.js.map +1 -0
- package/dist/scaffold-kb/sqlite-writer.d.ts +18 -0
- package/dist/scaffold-kb/sqlite-writer.d.ts.map +1 -0
- package/dist/scaffold-kb/sqlite-writer.js +76 -0
- package/dist/scaffold-kb/sqlite-writer.js.map +1 -0
- package/dist/scaffold-kb/tokenizer.d.ts +26 -0
- package/dist/scaffold-kb/tokenizer.d.ts.map +1 -0
- package/dist/scaffold-kb/tokenizer.js +97 -0
- package/dist/scaffold-kb/tokenizer.js.map +1 -0
- package/dist/scaffold-kb/types.d.ts +157 -0
- package/dist/scaffold-kb/types.d.ts.map +1 -0
- package/dist/scaffold-kb/types.js +5 -0
- package/dist/scaffold-kb/types.js.map +1 -0
- package/dist/utils/file-scanner.d.ts +15 -0
- package/dist/utils/file-scanner.d.ts.map +1 -1
- package/dist/utils/file-scanner.js +17 -0
- package/dist/utils/file-scanner.js.map +1 -1
- package/dist/utils/string-distance.d.ts +20 -0
- package/dist/utils/string-distance.d.ts.map +1 -0
- package/dist/utils/string-distance.js +38 -0
- package/dist/utils/string-distance.js.map +1 -0
- package/package.json +11 -2
- package/plugins/demo-kb-en/.claude-plugin/plugin.json +11 -0
- package/plugins/demo-kb-en/.mcp.json +8 -0
- package/plugins/demo-kb-en/FIXTURE.json +23 -0
- package/plugins/demo-kb-en/FIXTURE.md +53 -0
- package/plugins/demo-kb-en/ingest-samples/meeting-notes.md +13 -0
- package/plugins/demo-kb-en/kb/api-entities.json +3445 -0
- package/plugins/demo-kb-en/kb/chunks.sqlite +0 -0
- package/plugins/demo-kb-en/kb/doc-graph.json +113 -0
- package/plugins/demo-kb-zh/.claude-plugin/plugin.json +11 -0
- package/plugins/demo-kb-zh/.mcp.json +8 -0
- package/plugins/demo-kb-zh/FIXTURE.json +23 -0
- package/plugins/demo-kb-zh/FIXTURE.md +63 -0
- package/plugins/demo-kb-zh/ingest-samples/meeting-notes.md +13 -0
- package/plugins/demo-kb-zh/kb/api-entities.json +990 -0
- package/plugins/demo-kb-zh/kb/chunks.sqlite +0 -0
- package/plugins/demo-kb-zh/kb/doc-graph.json +113 -0
- package/plugins/spec-driver/.claude-plugin/plugin.json +1 -1
- package/plugins/spec-driver/README.md +33 -1
- package/plugins/spec-driver/agents/verify.md +88 -0
- package/plugins/spec-driver/config/orchestration.yaml +39 -31
- package/plugins/spec-driver/contracts/orchestration-overrides-contract.yaml +27 -0
- package/plugins/spec-driver/contracts/orchestration-schema.mjs +272 -227
- package/plugins/spec-driver/contracts/wrapper-source-of-truth.yaml +3 -0
- package/plugins/spec-driver/hooks/hooks.json +9 -0
- package/plugins/spec-driver/hooks/stop-fix-compliance-check.sh +36 -0
- package/plugins/spec-driver/lib/delegation-contract.mjs +84 -0
- package/plugins/spec-driver/lib/orchestration-resolver.mjs +49 -0
- package/plugins/spec-driver/lib/orchestrator.mjs +29 -1
- package/plugins/spec-driver/scripts/codex-skills.sh +28 -28
- package/plugins/spec-driver/scripts/dev/spike-fix-compliance-e2e.mjs +130 -0
- package/plugins/spec-driver/scripts/fix-compliance-judge.mjs +415 -0
- package/plugins/spec-driver/scripts/generate-adoption-insights.mjs +48 -1
- package/plugins/spec-driver/scripts/goal-loop-cli.mjs +307 -0
- package/plugins/spec-driver/scripts/init-project.sh +56 -0
- package/plugins/spec-driver/scripts/kb-prequery.mjs +125 -0
- package/plugins/spec-driver/scripts/lib/config-schema.mjs +200 -94
- package/plugins/spec-driver/scripts/lib/ensure-gitignore.sh +277 -0
- package/plugins/spec-driver/scripts/lib/extract-wrapper-body.mjs +136 -0
- package/plugins/spec-driver/scripts/lib/fix-compliance-core.mjs +434 -0
- package/plugins/spec-driver/scripts/lib/fix-compliance-io.mjs +335 -0
- package/plugins/spec-driver/scripts/lib/goal-loop-core.mjs +783 -0
- package/plugins/spec-driver/scripts/lib/init-project-output.sh +28 -0
- package/plugins/spec-driver/scripts/lib/load-zod.mjs +62 -0
- package/plugins/spec-driver/scripts/lib/project-profile-resolver.mjs +141 -49
- package/plugins/spec-driver/scripts/lib/project-profile-schema.mjs +88 -54
- package/plugins/spec-driver/scripts/postinstall.sh +13 -1
- package/plugins/spec-driver/scripts/record-workflow-run.mjs +75 -0
- package/plugins/spec-driver/scripts/sync-delegation-contract.mjs +187 -0
- package/plugins/spec-driver/scripts/validate-orchestrator-models.mjs +156 -0
- package/plugins/spec-driver/scripts/validate-wrapper-sources.mjs +46 -1
- package/plugins/spec-driver/skills/spec-driver-feature/SKILL.md +292 -0
- package/plugins/spec-driver/skills/spec-driver-fix/SKILL.md +87 -8
- package/plugins/spec-driver/skills/spec-driver-implement/SKILL.md +8 -0
- package/plugins/spec-driver/skills/spec-driver-resume/SKILL.md +12 -1
- package/plugins/spec-driver/skills/spec-driver-story/SKILL.md +20 -0
- package/plugins/spec-driver/templates/delegation-contract.md +27 -0
- package/plugins/spec-driver/templates/goal-loop-override-template.yaml +284 -0
- package/plugins/spec-driver/templates/specify-base/project-context-template.yaml +10 -0
- package/plugins/spectra/.claude-plugin/plugin.json +1 -1
- package/plugins/spectra/README.md +1 -1
- package/templates/module-spec.hbs +1 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* sync-delegation-contract — 从 templates/delegation-contract.md 单一事实源,把委派硬约束块
|
|
4
|
+
* 按各 SKILL 显式锚点注入 5 个主编排器 SKILL.md 的 BEGIN/END marker 之间。
|
|
5
|
+
*
|
|
6
|
+
* 用法:
|
|
7
|
+
* node plugins/spec-driver/scripts/sync-delegation-contract.mjs --write # 生成/更新(默认)
|
|
8
|
+
* node plugins/spec-driver/scripts/sync-delegation-contract.mjs --check # 漂移检测(CI/repo:check)
|
|
9
|
+
*
|
|
10
|
+
* ⚠️ --write 只写 plugins 层 5 个源 SKILL;.codex wrapper 由 repo:sync 的
|
|
11
|
+
* spec-driver-codex-wrappers 步骤再生(顺序已编排在本注入之后)。模板变更后单跑
|
|
12
|
+
* --write 再跑 --check 会因 .codex stale 而 fail——这是预期信号,跑 `npm run repo:sync` 消除。
|
|
13
|
+
*
|
|
14
|
+
* 注入锚点(已实测,per-SKILL 精确匹配;锚点未找到 → fail-loud):
|
|
15
|
+
* fix/story/implement → '## 工作流定义'
|
|
16
|
+
* feature → '## 工作流执行(动态模式)'
|
|
17
|
+
* resume → '## 恢复后执行流程'
|
|
18
|
+
* 已有 marker:仅替换 marker 之间内容(锚点不移动,幂等)。
|
|
19
|
+
*
|
|
20
|
+
* 退出码:0 = 无漂移 / 写入完成;1 = --check 检测到漂移。
|
|
21
|
+
*/
|
|
22
|
+
import fs from 'node:fs';
|
|
23
|
+
import path from 'node:path';
|
|
24
|
+
import { fileURLToPath } from 'node:url';
|
|
25
|
+
import {
|
|
26
|
+
extractCanonicalBlock,
|
|
27
|
+
wrapWithMarkers,
|
|
28
|
+
computeExpectedSkillContent,
|
|
29
|
+
BEGIN_MARKER,
|
|
30
|
+
END_MARKER,
|
|
31
|
+
} from '../lib/delegation-contract.mjs';
|
|
32
|
+
|
|
33
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
34
|
+
const PLUGIN_DIR = path.resolve(__dirname, '..');
|
|
35
|
+
|
|
36
|
+
/** 显式 per-SKILL 注入锚点 map(已实测)。 */
|
|
37
|
+
const SKILL_ANCHORS = {
|
|
38
|
+
fix: '## 工作流定义',
|
|
39
|
+
story: '## 工作流定义',
|
|
40
|
+
feature: '## 工作流执行(动态模式)',
|
|
41
|
+
implement: '## 工作流定义',
|
|
42
|
+
resume: '## 恢复后执行流程',
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
function resolvePaths(projectRoot) {
|
|
46
|
+
const root = projectRoot ? path.resolve(projectRoot) : path.resolve(PLUGIN_DIR, '../..');
|
|
47
|
+
const pluginDir = path.join(root, 'plugins/spec-driver');
|
|
48
|
+
return {
|
|
49
|
+
root,
|
|
50
|
+
pluginDir,
|
|
51
|
+
templatePath: path.join(pluginDir, 'templates/delegation-contract.md'),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function skillPath(pluginDir, mode) {
|
|
56
|
+
return path.join(pluginDir, 'skills', `spec-driver-${mode}`, 'SKILL.md');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function codexWrapperPath(root, mode) {
|
|
60
|
+
return path.join(root, '.codex/skills', `spec-driver-${mode}`, 'SKILL.md');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 供 repo:check(repo-maintenance-core)复用的漂移校验:
|
|
65
|
+
* 返回 { status, checks, warnings, errors },与 aggregateValidation 契约一致。
|
|
66
|
+
*/
|
|
67
|
+
export function validateDelegationContract({ projectRoot } = {}) {
|
|
68
|
+
const { root, pluginDir, templatePath } = resolvePaths(projectRoot);
|
|
69
|
+
const errors = [];
|
|
70
|
+
const checks = [];
|
|
71
|
+
|
|
72
|
+
if (!fs.existsSync(templatePath)) {
|
|
73
|
+
return {
|
|
74
|
+
status: 'fail',
|
|
75
|
+
checks: [{ id: 'skill-block-sync', title: 'delegation-contract 单一源同步', status: 'fail', evidence: { reason: 'template 缺失' } }],
|
|
76
|
+
warnings: [],
|
|
77
|
+
errors: ['template 缺失: templates/delegation-contract.md'],
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const templateText = fs.readFileSync(templatePath, 'utf-8');
|
|
81
|
+
const drifted = [];
|
|
82
|
+
for (const [mode, anchor] of Object.entries(SKILL_ANCHORS)) {
|
|
83
|
+
const p = skillPath(pluginDir, mode);
|
|
84
|
+
if (!fs.existsSync(p)) {
|
|
85
|
+
drifted.push(`${mode}(缺失)`);
|
|
86
|
+
errors.push(`SKILL 文件缺失: spec-driver-${mode}/SKILL.md`);
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const actual = fs.readFileSync(p, 'utf-8');
|
|
90
|
+
let expected;
|
|
91
|
+
try {
|
|
92
|
+
expected = computeExpectedSkillContent(actual, templateText, anchor);
|
|
93
|
+
} catch (err) {
|
|
94
|
+
drifted.push(`${mode}(锚点错)`);
|
|
95
|
+
errors.push(`spec-driver-${mode}/SKILL.md: ${err.message}`);
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (actual !== expected) {
|
|
99
|
+
drifted.push(mode);
|
|
100
|
+
errors.push(`spec-driver-${mode}/SKILL.md delegation-contract 块与 template 漂移;运行 sync-delegation-contract.mjs --write`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const status = drifted.length > 0 ? 'fail' : 'pass';
|
|
104
|
+
checks.push({
|
|
105
|
+
id: 'skill-block-sync',
|
|
106
|
+
title: '5 SKILL delegation-contract 块与单一源一致',
|
|
107
|
+
status,
|
|
108
|
+
evidence: { drifted },
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// .codex 双层守护(codex Warning-4):resume 原始事故正是 .codex 层 stale。
|
|
112
|
+
// 断言每个 .codex wrapper 内嵌的委派块与单一源一致——若 source 改了但未跑 repo:sync
|
|
113
|
+
// 再生 wrapper,这里 fail-loud(不依赖 codex-skills.sh 的 marker-only 校验)。
|
|
114
|
+
const expectedBlock = wrapWithMarkers(extractCanonicalBlock(templateText));
|
|
115
|
+
const codexDrifted = [];
|
|
116
|
+
for (const mode of Object.keys(SKILL_ANCHORS)) {
|
|
117
|
+
const cp = codexWrapperPath(root, mode);
|
|
118
|
+
if (!fs.existsSync(cp)) {
|
|
119
|
+
codexDrifted.push(`${mode}(缺失)`);
|
|
120
|
+
errors.push(`.codex wrapper 缺失: .codex/skills/spec-driver-${mode}/SKILL.md`);
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (!fs.readFileSync(cp, 'utf-8').includes(expectedBlock)) {
|
|
124
|
+
codexDrifted.push(mode);
|
|
125
|
+
errors.push(`.codex/skills/spec-driver-${mode}/SKILL.md 未含最新委派块(stale wrapper);运行 npm run repo:sync 再生`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const codexStatus = codexDrifted.length > 0 ? 'fail' : 'pass';
|
|
129
|
+
checks.push({
|
|
130
|
+
id: 'codex-wrapper-block-sync',
|
|
131
|
+
title: '5 .codex wrapper 委派块与单一源一致',
|
|
132
|
+
status: codexStatus,
|
|
133
|
+
evidence: { codexDrifted },
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
return { status: status === 'fail' || codexStatus === 'fail' ? 'fail' : 'pass', checks, warnings: [], errors };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* 程序化写入(repo:sync 复用):按 template 重新注入 5 SKILL 的 delegation-contract 块。
|
|
141
|
+
* 返回 { written: string[] }。
|
|
142
|
+
*/
|
|
143
|
+
export function syncDelegationContract({ projectRoot } = {}) {
|
|
144
|
+
const { pluginDir, templatePath } = resolvePaths(projectRoot);
|
|
145
|
+
if (!fs.existsSync(templatePath)) {
|
|
146
|
+
throw new Error(`[sync-delegation-contract] template 不存在: ${templatePath}`);
|
|
147
|
+
}
|
|
148
|
+
const templateText = fs.readFileSync(templatePath, 'utf-8');
|
|
149
|
+
const written = [];
|
|
150
|
+
for (const [mode, anchor] of Object.entries(SKILL_ANCHORS)) {
|
|
151
|
+
const p = skillPath(pluginDir, mode);
|
|
152
|
+
if (!fs.existsSync(p)) throw new Error(`[sync-delegation-contract] SKILL 文件不存在: ${p}`);
|
|
153
|
+
const actual = fs.readFileSync(p, 'utf-8');
|
|
154
|
+
const expected = computeExpectedSkillContent(actual, templateText, anchor);
|
|
155
|
+
if (actual !== expected) { fs.writeFileSync(p, expected, 'utf-8'); written.push(mode); }
|
|
156
|
+
}
|
|
157
|
+
return { written };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function run(mode) {
|
|
161
|
+
if (mode === 'check') {
|
|
162
|
+
const result = validateDelegationContract({});
|
|
163
|
+
if (result.errors.length > 0) {
|
|
164
|
+
result.errors.forEach((e) => console.error(`[sync-delegation-contract] ${e}`));
|
|
165
|
+
console.error(' 运行 `node plugins/spec-driver/scripts/sync-delegation-contract.mjs --write` 重新同步');
|
|
166
|
+
process.exit(1);
|
|
167
|
+
}
|
|
168
|
+
console.log('[sync-delegation-contract] 无漂移 ✅');
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
const { written } = syncDelegationContract({});
|
|
172
|
+
written.forEach((m) => console.log(`[sync-delegation-contract] 已更新 spec-driver-${m}/SKILL.md`));
|
|
173
|
+
console.log(`[sync-delegation-contract] 完成(更新 ${written.length} 个 SKILL 文件)`);
|
|
174
|
+
if (written.length > 0) {
|
|
175
|
+
console.log('[sync-delegation-contract] ⚠️ .codex wrapper 未在此步再生;跑 `npm run repo:sync` 同步双层(--check 含 .codex 校验)');
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// 显式 re-export 供测试直接消费(避免测试重复 import lib)
|
|
180
|
+
export { extractCanonicalBlock, wrapWithMarkers, computeExpectedSkillContent, BEGIN_MARKER, END_MARKER };
|
|
181
|
+
|
|
182
|
+
const isCliEntry = process.argv[1] != null
|
|
183
|
+
&& path.resolve(process.argv[1]).endsWith('sync-delegation-contract.mjs');
|
|
184
|
+
if (isCliEntry) {
|
|
185
|
+
const cliMode = process.argv.includes('--check') ? 'check' : 'write';
|
|
186
|
+
run(cliMode);
|
|
187
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* validate-orchestrator-models — 断言 5 个主编排器 SKILL 的双层 frontmatter `model: opus`。
|
|
4
|
+
*
|
|
5
|
+
* 显式 allowlist(不要用"任何含 Task 的 SKILL"):fix/story/feature/implement/resume。
|
|
6
|
+
* sync/doc 也委派子代理但设计上保持 sonnet(轻编排器),全量断言会误伤。
|
|
7
|
+
*
|
|
8
|
+
* 校验双层:
|
|
9
|
+
* - plugins/spec-driver/skills/spec-driver-<m>/SKILL.md
|
|
10
|
+
* - .codex/skills/spec-driver-<m>/SKILL.md
|
|
11
|
+
* 任一层 model ≠ opus → status fail + error 明示哪个文件哪层。
|
|
12
|
+
*
|
|
13
|
+
* 背景:F176 实测 sonnet 编排器无视委派硬约束;6281a27 sonnet→opus 漂移多时无人发现的根因
|
|
14
|
+
* 是 frontmatter model 不在任何 contract/check 管辖——本断言机制化守护。
|
|
15
|
+
*
|
|
16
|
+
* 退出码:0 = 全 opus;1 = 检测到非 opus。
|
|
17
|
+
*/
|
|
18
|
+
import fs from 'node:fs';
|
|
19
|
+
import path from 'node:path';
|
|
20
|
+
import { fileURLToPath } from 'node:url';
|
|
21
|
+
|
|
22
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
23
|
+
const PLUGIN_DIR = path.resolve(__dirname, '..');
|
|
24
|
+
|
|
25
|
+
/** 显式 allowlist:仅这 5 个主编排器要求 opus。 */
|
|
26
|
+
const ORCHESTRATOR_MODES = ['fix', 'story', 'feature', 'implement', 'resume'];
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 已记录的豁免:在 allowed-tools 声明了 Task(具备委派能力)但**不**纳入 F185 opus 硬断言的 mode。
|
|
30
|
+
* key = mode,value = 豁免理由(fail-loud 时回显,杜绝"无说明的漏网",对应 codex Warning-3)。
|
|
31
|
+
* - refactor:已是 opus 且委派 batch implement,但 F185 硬约束块注入范围限定 5 个核心模式;
|
|
32
|
+
* refactor 委派契约纳入视后续 milestone(当前已 opus,无 model 漂移风险)。
|
|
33
|
+
* 注:sync / doc 的 allowed-tools **不含 Task**(无委派能力),天然不进入此覆盖断言。
|
|
34
|
+
*/
|
|
35
|
+
const DOCUMENTED_EXCEPTIONS = {
|
|
36
|
+
refactor: 'F185 硬约束范围限定 5 核心模式;refactor 已 opus,委派契约纳入视后续',
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 动态枚举全部 SKILL 目录 mode(去 spec-driver- 前缀)。
|
|
41
|
+
* 不用硬编码列表(codex Warning:新增 skill 目录若漏更新列表,coverage 守护对其失效)。
|
|
42
|
+
*/
|
|
43
|
+
function listAllModes(root) {
|
|
44
|
+
const skillsDir = path.join(root, 'plugins/spec-driver/skills');
|
|
45
|
+
if (!fs.existsSync(skillsDir)) return [];
|
|
46
|
+
return fs.readdirSync(skillsDir, { withFileTypes: true })
|
|
47
|
+
.filter((d) => d.isDirectory() && d.name.startsWith('spec-driver-'))
|
|
48
|
+
.map((d) => d.name.slice('spec-driver-'.length));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** 判断某 mode 的 plugins 层 SKILL 是否在 allowed-tools 声明了 Task(即具备委派能力)。 */
|
|
52
|
+
function declaresTaskTool(root, mode) {
|
|
53
|
+
const p = path.join(root, 'plugins/spec-driver/skills', `spec-driver-${mode}`, 'SKILL.md');
|
|
54
|
+
if (!fs.existsSync(p)) return false;
|
|
55
|
+
const fm = String(fs.readFileSync(p, 'utf-8')).match(/^---\n([\s\S]*?)\n---/);
|
|
56
|
+
if (!fm) return false;
|
|
57
|
+
const at = fm[1].match(/^allowed-tools:\s*\[(.*)\]/m);
|
|
58
|
+
return at ? /\bTask\b/.test(at[1]) : false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** 从 SKILL.md frontmatter 解析 `model:` 值(仅 frontmatter 首块内)。 */
|
|
62
|
+
function parseFrontmatterModel(skillText) {
|
|
63
|
+
const text = String(skillText ?? '');
|
|
64
|
+
// 仅匹配文件开头 --- ... --- 之间的 frontmatter
|
|
65
|
+
const fm = text.match(/^---\n([\s\S]*?)\n---/);
|
|
66
|
+
if (!fm) return null;
|
|
67
|
+
const m = fm[1].match(/^model:\s*(\S+)\s*$/m);
|
|
68
|
+
return m ? m[1] : null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function resolveRoot(projectRoot) {
|
|
72
|
+
return projectRoot ? path.resolve(projectRoot) : path.resolve(PLUGIN_DIR, '../..');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** 返回某 mode 的双层 SKILL 路径(plugins + .codex)。 */
|
|
76
|
+
function layerPaths(root, mode) {
|
|
77
|
+
return [
|
|
78
|
+
{
|
|
79
|
+
layer: 'plugins',
|
|
80
|
+
path: path.join(root, 'plugins/spec-driver/skills', `spec-driver-${mode}`, 'SKILL.md'),
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
layer: '.codex',
|
|
84
|
+
path: path.join(root, '.codex/skills', `spec-driver-${mode}`, 'SKILL.md'),
|
|
85
|
+
},
|
|
86
|
+
];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* 供 repo:check(repo-maintenance-core)复用的 model 断言:
|
|
91
|
+
* 返回 { status, checks, warnings, errors },与 aggregateValidation 契约一致。
|
|
92
|
+
* check id 形如 `orchestrator-model-<m>`。
|
|
93
|
+
*/
|
|
94
|
+
export function validateOrchestratorModels({ projectRoot } = {}) {
|
|
95
|
+
const root = resolveRoot(projectRoot);
|
|
96
|
+
const errors = [];
|
|
97
|
+
const checks = [];
|
|
98
|
+
|
|
99
|
+
for (const mode of ORCHESTRATOR_MODES) {
|
|
100
|
+
const offenders = [];
|
|
101
|
+
for (const { layer, path: p } of layerPaths(root, mode)) {
|
|
102
|
+
if (!fs.existsSync(p)) {
|
|
103
|
+
offenders.push(`${layer}(缺失)`);
|
|
104
|
+
errors.push(`spec-driver-${mode} ${layer} 层 SKILL.md 缺失: ${path.relative(root, p)}`);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const model = parseFrontmatterModel(fs.readFileSync(p, 'utf-8'));
|
|
108
|
+
if (model !== 'opus') {
|
|
109
|
+
offenders.push(`${layer}=${model ?? '未声明'}`);
|
|
110
|
+
errors.push(`spec-driver-${mode} ${layer} 层 frontmatter model=${model ?? '未声明'},要求 opus(${path.relative(root, p)})`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
checks.push({
|
|
114
|
+
id: `orchestrator-model-${mode}`,
|
|
115
|
+
title: `spec-driver-${mode} 双层 frontmatter model=opus`,
|
|
116
|
+
status: offenders.length > 0 ? 'fail' : 'pass',
|
|
117
|
+
evidence: { offenders },
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// 覆盖断言(codex Warning-3):任何在 allowed-tools 声明 Task 的 mode,必须要么在
|
|
122
|
+
// opus-allowlist,要么在已记录豁免里——杜绝未来新增委派 SKILL 静默漏出硬约束管辖。
|
|
123
|
+
const classified = new Set([...ORCHESTRATOR_MODES, ...Object.keys(DOCUMENTED_EXCEPTIONS)]);
|
|
124
|
+
const unclassified = listAllModes(root).filter((m) => declaresTaskTool(root, m) && !classified.has(m));
|
|
125
|
+
for (const m of unclassified) {
|
|
126
|
+
errors.push(`spec-driver-${m} 在 allowed-tools 声明了 Task(具备委派能力)但未分类:需加入 ORCHESTRATOR_MODES(要求 opus)或 DOCUMENTED_EXCEPTIONS(注明理由)`);
|
|
127
|
+
}
|
|
128
|
+
checks.push({
|
|
129
|
+
id: 'orchestrator-task-coverage',
|
|
130
|
+
title: '所有含 Task 委派的 SKILL 均已分类(allowlist 或已记录豁免)',
|
|
131
|
+
status: unclassified.length > 0 ? 'fail' : 'pass',
|
|
132
|
+
evidence: { unclassified, exceptions: Object.keys(DOCUMENTED_EXCEPTIONS) },
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
status: errors.length > 0 ? 'fail' : 'pass',
|
|
137
|
+
checks,
|
|
138
|
+
warnings: [],
|
|
139
|
+
errors,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function run() {
|
|
144
|
+
const result = validateOrchestratorModels({});
|
|
145
|
+
if (result.errors.length > 0) {
|
|
146
|
+
result.errors.forEach((e) => console.error(`[validate-orchestrator-models] ${e}`));
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
console.log('[validate-orchestrator-models] 5 编排器双层 model=opus ✅');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const isCliEntry = process.argv[1] != null
|
|
153
|
+
&& path.resolve(process.argv[1]).endsWith('validate-orchestrator-models.mjs');
|
|
154
|
+
if (isCliEntry) {
|
|
155
|
+
run();
|
|
156
|
+
}
|
|
@@ -2,6 +2,7 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
import { parseYamlDocument } from './lib/simple-yaml.mjs';
|
|
5
|
+
import { computeWrapperBodySha256 } from './lib/extract-wrapper-body.mjs';
|
|
5
6
|
|
|
6
7
|
function parseArgs(argv) {
|
|
7
8
|
const options = {
|
|
@@ -69,6 +70,9 @@ function validateWrapperMarkers(projectRoot, entries, errors) {
|
|
|
69
70
|
const checked = [];
|
|
70
71
|
const missingFiles = [];
|
|
71
72
|
const invalidMarkers = [];
|
|
73
|
+
// F186 T2:wrapper body sha256 漂移检测
|
|
74
|
+
const shaMismatches = [];
|
|
75
|
+
const missingSha = [];
|
|
72
76
|
|
|
73
77
|
for (const entry of entries) {
|
|
74
78
|
const targetPath = path.resolve(projectRoot, entry.target);
|
|
@@ -96,6 +100,25 @@ function validateWrapperMarkers(projectRoot, entries, errors) {
|
|
|
96
100
|
continue;
|
|
97
101
|
}
|
|
98
102
|
|
|
103
|
+
// F186 T2:解析 wrapper 内嵌的 Source SHA256,对 source 用同一 helper 重算比对。
|
|
104
|
+
// 缺 `Source SHA256:` 行 → fail(FINAL CRITICAL-2:缺 sha 即视为漂移/被手改,不是 warn)。
|
|
105
|
+
const shaMatch = content.match(/^- Source SHA256:\s*([0-9a-f]{64})\s*$/m);
|
|
106
|
+
if (!shaMatch) {
|
|
107
|
+
missingSha.push(entry.target);
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const sourcePath = path.resolve(projectRoot, entry.source);
|
|
111
|
+
if (!fs.existsSync(sourcePath)) {
|
|
112
|
+
// source 缺失由 source-skills check 单独报;此处不重复,但 sha 无法校验 → 视为不通过
|
|
113
|
+
shaMismatches.push({ target: entry.target, expected: shaMatch[1], actual: '<source-missing>' });
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const actualSha = computeWrapperBodySha256(sourcePath);
|
|
117
|
+
if (actualSha !== shaMatch[1]) {
|
|
118
|
+
shaMismatches.push({ target: entry.target, expected: shaMatch[1], actual: actualSha });
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
|
|
99
122
|
checked.push(entry.target);
|
|
100
123
|
}
|
|
101
124
|
|
|
@@ -113,14 +136,36 @@ function validateWrapperMarkers(projectRoot, entries, errors) {
|
|
|
113
136
|
);
|
|
114
137
|
}
|
|
115
138
|
|
|
139
|
+
if (missingSha.length > 0) {
|
|
140
|
+
errors.push(
|
|
141
|
+
`以下 Codex 包装技能缺少 \`Source SHA256:\` 行(疑似漂移/被手改,请重新运行 npm run codex:spec-driver:install):${missingSha.join(', ')}`,
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (shaMismatches.length > 0) {
|
|
146
|
+
errors.push(
|
|
147
|
+
`以下 Codex 包装技能 body sha256 不匹配(source 已变更但 wrapper 未重生成):${shaMismatches
|
|
148
|
+
.map((item) => `${item.target}(期望 ${item.expected.slice(0, 12)}…,实际 ${item.actual.slice(0, 12)}…)`)
|
|
149
|
+
.join(', ')}`,
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const ok =
|
|
154
|
+
missingFiles.length === 0 &&
|
|
155
|
+
invalidMarkers.length === 0 &&
|
|
156
|
+
missingSha.length === 0 &&
|
|
157
|
+
shaMismatches.length === 0;
|
|
158
|
+
|
|
116
159
|
return createCheck(
|
|
117
160
|
'codex-wrapper-markers',
|
|
118
161
|
'Codex 包装技能 source-of-truth 标记',
|
|
119
|
-
|
|
162
|
+
ok ? 'pass' : 'fail',
|
|
120
163
|
{
|
|
121
164
|
checkedCount: checked.length,
|
|
122
165
|
missingFiles,
|
|
123
166
|
invalidMarkers,
|
|
167
|
+
missingSha,
|
|
168
|
+
shaMismatches,
|
|
124
169
|
},
|
|
125
170
|
);
|
|
126
171
|
}
|