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
|
@@ -1,35 +1,58 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* import-resolver.ts — TS/JS import specifier
|
|
2
|
+
* import-resolver.ts — TS/JS import specifier 单一权威解析(Feature 181 收口)
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* - node_modules / 外部包:返回 null(不解析外部依赖)
|
|
15
|
-
* 2. detectImportType — 从 ts-morph 节点派生 ImportType 字面量
|
|
16
|
-
* - 用于 CodeSkeleton.imports[].importType + ModuleEdge.importType
|
|
4
|
+
* 背景(Feature 181 单一权威收口):
|
|
5
|
+
* 历史上 `core/import-resolver.ts` 与 `knowledge-graph/import-resolver.ts` 各有一份
|
|
6
|
+
* `resolveTsJsImport`,行为分叉(core 返回绝对路径无 projectRoot 守卫 + ESM ext map;
|
|
7
|
+
* kg 返回相对 POSIX + kind + isInsideProjectRoot 守卫)。两者经 AST 路径
|
|
8
|
+
* (ast-analyzer / tree-sitter-*)与 batch 路径(collect / module-derivation)都进 graph.json。
|
|
9
|
+
* 本模块收口为唯一权威实现,合并两侧能力:
|
|
10
|
+
* - 相对解析:ESM ext map(`./foo.js`→`foo.ts`)+ 直接命中 + .mjs/.cjs(源自 core)
|
|
11
|
+
* - alias/baseUrl:tsconfig paths(最长前缀 + 多候选 + baseUrl 叠加,TS 官方语义)
|
|
12
|
+
* - projectRoot 边界守卫(防图污染,源自 kg;relative / disk-absolute 分支启用)
|
|
13
|
+
* - kind 分类 + external/unresolved 区分 + .json/.d.ts → external
|
|
17
14
|
*
|
|
18
15
|
* 设计约束:
|
|
19
|
-
* -
|
|
20
|
-
* - 不抛异常(无法解析返回 null
|
|
21
|
-
* - 跨平台:
|
|
16
|
+
* - 纯 node:fs / node:path(+ typescript 仅用于 tsconfig 解析,含 extends 链)
|
|
17
|
+
* - 不抛异常(无法解析返回 { resolvedPath: null, kind: 'unresolved' })
|
|
18
|
+
* - 跨平台:resolvedPath 输出统一为相对 projectRoot 的 POSIX 路径;
|
|
19
|
+
* projectRoot 为空(独立 AST 调用)时返回绝对路径且跳过守卫(保持历史 core 行为)
|
|
20
|
+
*
|
|
21
|
+
* 注:Python import 解析(resolvePythonImport)不在本次收口范围,仍在
|
|
22
|
+
* knowledge-graph/import-resolver.ts;其依赖的 ResolveResult / toPosix / isInsideProjectRoot
|
|
23
|
+
* 由本模块导出供其下行 import(knowledge-graph → core,层级方向干净)。
|
|
22
24
|
*/
|
|
23
25
|
import * as fs from 'node:fs';
|
|
24
26
|
import * as path from 'node:path';
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
// 复用 ts-morph 再导出的 typescript 命名空间(避免新增对 'typescript' 的直接运行时依赖;
|
|
28
|
+
// ts-morph 已是本仓运行时依赖)
|
|
29
|
+
import { ts } from 'ts-morph';
|
|
30
|
+
import { createLogger } from '../panoramic/utils/logger.js';
|
|
31
|
+
// F183 修复 2:buildTsConfigContext 失败时不再静默——warn 出 configPath + error 摘要。
|
|
32
|
+
const logger = createLogger('import-resolver');
|
|
33
|
+
// 仅缓存已 warn 的 configPath 做 emission 限频(monorepo 同一损坏 tsconfig 会被每个
|
|
34
|
+
// import-resolve 循环触发,造成日志洪泛)。注意:仅限频 warn,绝不跳过解析——
|
|
35
|
+
// buildTsConfigContext 始终尝试解析,失败仍 return null。
|
|
36
|
+
const warnedConfigPaths = new Set();
|
|
37
|
+
/** 同一 configPath 仅发一次 warn(emission 限频,不影响解析)。 */
|
|
38
|
+
function warnTsConfigFailureOnce(configPath, errorSummary) {
|
|
39
|
+
if (warnedConfigPaths.has(configPath))
|
|
40
|
+
return;
|
|
41
|
+
logger.warn(`[import-resolver] buildTsConfigContext 失败(${configPath}):${errorSummary}`);
|
|
42
|
+
warnedConfigPaths.add(configPath);
|
|
43
|
+
}
|
|
44
|
+
// ───────────────────────────────────────────────────────────
|
|
45
|
+
// 常量
|
|
46
|
+
// ───────────────────────────────────────────────────────────
|
|
47
|
+
/** TS/JS 候补扩展名顺序(含 .mjs/.cjs,源自历史 core 实现) */
|
|
48
|
+
const TS_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'];
|
|
49
|
+
/** index 文件名顺序 */
|
|
50
|
+
const TS_INDEX_FILES = ['index.ts', 'index.tsx', 'index.js', 'index.jsx'];
|
|
51
|
+
/** alias-like 前缀列表(C-2):以这些前缀开头的 moduleSpec 无 paths 命中时返回 unresolved */
|
|
52
|
+
const ALIAS_PREFIXES = ['~/', '#/', '$/'];
|
|
28
53
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* 当 specifier 以 JS 扩展名结尾时,先尝试对应的 TS 扩展名候选;命中则返回,否则 fallback
|
|
32
|
-
* 到原扩展名继续走标准解析流程(保持非 TS 项目的兼容性)。
|
|
54
|
+
* TypeScript ESM 惯例:`import './foo.js'` 实际指向 `./foo.ts`(Feature 156 CRIT-1)。
|
|
55
|
+
* specifier 以 JS 扩展名结尾时,先尝试对应 TS 扩展名候选;命中即返回,否则 fallback 标准解析。
|
|
33
56
|
*/
|
|
34
57
|
const ESM_TS_EXT_MAP = {
|
|
35
58
|
'.js': ['.ts', '.tsx'],
|
|
@@ -38,107 +61,65 @@ const ESM_TS_EXT_MAP = {
|
|
|
38
61
|
'.cjs': ['.cts'],
|
|
39
62
|
};
|
|
40
63
|
// ───────────────────────────────────────────────────────────
|
|
41
|
-
//
|
|
64
|
+
// 共享辅助(导出供 knowledge-graph Python resolver 下行复用)
|
|
42
65
|
// ───────────────────────────────────────────────────────────
|
|
66
|
+
/** 将路径转换为 POSIX 格式(确保跨平台 resolvedPath 始终用 '/' 分隔符)。 */
|
|
67
|
+
export function toPosix(p) {
|
|
68
|
+
return p.split(path.sep).join('/');
|
|
69
|
+
}
|
|
43
70
|
/**
|
|
44
|
-
*
|
|
71
|
+
* 判断 candidate 路径是否严格在 projectRoot 子树内(C-5 + N-2)。
|
|
45
72
|
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* 2. tsconfig path alias 命中 → 替换前缀后按相对路径流程解析
|
|
49
|
-
* 3. 相对路径(`./xxx` / `../xxx` / `/xxx`)→ 基于 fromFile 拼接
|
|
50
|
-
*
|
|
51
|
-
* 文件存在性检查:
|
|
52
|
-
* - 直接命中(specifier 已含 .ts/.js 等扩展名)
|
|
53
|
-
* - 扩展名补全(依次尝试 options.extensions)
|
|
54
|
-
* - 目录 + index 文件 fallback(如果 specifier 指向目录)
|
|
55
|
-
*
|
|
56
|
-
* 失败时静默返回 null,不抛异常(FR-28 best-effort 语义)。
|
|
57
|
-
*
|
|
58
|
-
* @param specifier - import 字符串字面量(如 './foo')
|
|
59
|
-
* @param fromFile - 发起 import 的源文件绝对路径
|
|
60
|
-
* @param projectRoot - 项目根目录(用于 alias 相对路径解析;可为空字符串,alias 失效)
|
|
61
|
-
* @param options - 扩展名 / index 文件 / alias 配置
|
|
62
|
-
* @returns 解析到的绝对路径;外部模块或解析失败返回 null
|
|
73
|
+
* 用 path.relative 逐 component 检查第一段是否为 '..',避免字典序误判(/proj vs /projection)。
|
|
74
|
+
* candidate === projectRoot 返回 false(projectRoot 自身不算"在子树内")。
|
|
63
75
|
*/
|
|
64
|
-
export function
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
for (const cand of aliasCandidates) {
|
|
75
|
-
const hit = tryFilePathVariants(cand, extensions, indexFiles);
|
|
76
|
-
if (hit)
|
|
77
|
-
return hit;
|
|
78
|
-
}
|
|
79
|
-
// 3. 相对路径前缀('.', '..', '/')才进入文件系统解析
|
|
80
|
-
if (specifier.startsWith('.') || specifier.startsWith('/')) {
|
|
81
|
-
const fromDir = path.dirname(path.resolve(fromFile));
|
|
82
|
-
const baseSpecifier = path.resolve(fromDir, specifier);
|
|
83
|
-
return tryFilePathVariants(baseSpecifier, extensions, indexFiles);
|
|
84
|
-
}
|
|
85
|
-
// 4. baseUrl fallback(CRIT-2 v2):未命中 alias / 非相对路径,
|
|
86
|
-
// 若 baseUrl 提供,则按非相对 module 解析(baseUrl + specifier)
|
|
87
|
-
if (options?.baseUrl) {
|
|
88
|
-
const baseRel = path.resolve(options.baseUrl, specifier);
|
|
89
|
-
const hit = tryFilePathVariants(baseRel, extensions, indexFiles);
|
|
90
|
-
if (hit)
|
|
91
|
-
return hit;
|
|
92
|
-
}
|
|
93
|
-
// 5. 不带相对前缀、无 alias / baseUrl 命中:视为 npm 包 / 外部模块 → null
|
|
94
|
-
return null;
|
|
76
|
+
export function isInsideProjectRoot(candidate, projectRoot) {
|
|
77
|
+
const rel = path.relative(projectRoot, candidate);
|
|
78
|
+
if (rel.length === 0)
|
|
79
|
+
return false; // candidate === projectRoot
|
|
80
|
+
if (path.isAbsolute(rel))
|
|
81
|
+
return false; // 跨盘符(Windows)
|
|
82
|
+
const firstSeg = rel.split(path.sep)[0];
|
|
83
|
+
if (firstSeg === '..')
|
|
84
|
+
return false;
|
|
85
|
+
return true;
|
|
95
86
|
}
|
|
96
87
|
/**
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
* 改进:
|
|
100
|
-
* 1. 支持单值与多候选数组(`Record<string, string | readonly string[]>`)
|
|
101
|
-
* 2. 最长前缀匹配(`@app/utils/foo` 命中 `@app/utils/*` 优先于 `@app/*`)
|
|
102
|
-
* 3. 一个 alias 可对应多个目标,全部作为候选返回(调用方逐个 tryFilePathVariants)
|
|
103
|
-
*
|
|
104
|
-
* @returns 候选绝对路径列表(按优先级排序:最长前缀优先 + 配置顺序);未命中返回空数组
|
|
88
|
+
* 判断路径是否指向非源文件目标(W-1)。
|
|
89
|
+
* .json 和 .d.ts 文件不进入 callSites graph,返回 external。
|
|
105
90
|
*/
|
|
106
|
-
function
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const remainder = specifier.slice(aliasPrefix.length);
|
|
116
|
-
const targets = Array.isArray(targetValue) ? targetValue : [targetValue];
|
|
117
|
-
matched.push({ prefixLen: aliasPrefix.length, remainder, targets });
|
|
91
|
+
export function isNonSourceTarget(p) {
|
|
92
|
+
return p.endsWith('.json') || p.endsWith('.d.ts');
|
|
93
|
+
}
|
|
94
|
+
// ───────────────────────────────────────────────────────────
|
|
95
|
+
// 内部:文件候选尝试(合并 core ESM ext map + 直接命中 + 扩展名 + index)
|
|
96
|
+
// ───────────────────────────────────────────────────────────
|
|
97
|
+
function existsAndIsFile(p) {
|
|
98
|
+
try {
|
|
99
|
+
return fs.statSync(p).isFile();
|
|
118
100
|
}
|
|
119
|
-
|
|
120
|
-
return
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
101
|
+
catch {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function existsAndIsDir(p) {
|
|
106
|
+
try {
|
|
107
|
+
return fs.statSync(p).isDirectory();
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return false;
|
|
130
111
|
}
|
|
131
|
-
return candidates;
|
|
132
112
|
}
|
|
133
113
|
/**
|
|
134
|
-
*
|
|
135
|
-
* 1. base
|
|
136
|
-
* 2. base
|
|
137
|
-
* 3. base
|
|
114
|
+
* 给定 base 路径,依次尝试(顺序严格复刻历史 core,护 graph.json byte-identical):
|
|
115
|
+
* 1. ESM TS ext map(若 base 以 JS 扩展名结尾,先试 .ts/.tsx 等 TS 候选)
|
|
116
|
+
* 2. base 直接命中(已含扩展名)
|
|
117
|
+
* 3. base + 扩展名(.ts → .cjs)
|
|
118
|
+
* 4. base/index.{ts,tsx,js,jsx}
|
|
119
|
+
* @returns 命中的绝对路径;否则 null
|
|
138
120
|
*/
|
|
139
|
-
function tryFilePathVariants(base
|
|
140
|
-
//
|
|
141
|
-
// 命中则直接返回;不命中再 fallback 到原 base 走后续标准解析流程
|
|
121
|
+
function tryFilePathVariants(base) {
|
|
122
|
+
// 1. ESM TS ext map(如 foo.js → foo.ts)
|
|
142
123
|
const sourceExt = path.extname(base);
|
|
143
124
|
const tsCandidatesExts = ESM_TS_EXT_MAP[sourceExt];
|
|
144
125
|
if (tsCandidatesExts) {
|
|
@@ -148,20 +129,19 @@ function tryFilePathVariants(base, extensions, indexFiles) {
|
|
|
148
129
|
if (existsAndIsFile(tsCandidate))
|
|
149
130
|
return tsCandidate;
|
|
150
131
|
}
|
|
151
|
-
// TS 候选未命中:继续 fallback 到下方标准解析(直接命中 / 扩展名补全 / index)
|
|
152
132
|
}
|
|
153
|
-
//
|
|
133
|
+
// 2. 直接命中
|
|
154
134
|
if (existsAndIsFile(base))
|
|
155
135
|
return base;
|
|
156
|
-
//
|
|
157
|
-
for (const ext of
|
|
136
|
+
// 3. 扩展名补全
|
|
137
|
+
for (const ext of TS_EXTENSIONS) {
|
|
158
138
|
const candidate = base + ext;
|
|
159
139
|
if (existsAndIsFile(candidate))
|
|
160
140
|
return candidate;
|
|
161
141
|
}
|
|
162
|
-
//
|
|
142
|
+
// 4. 目录 + index 文件
|
|
163
143
|
if (existsAndIsDir(base)) {
|
|
164
|
-
for (const idxFile of
|
|
144
|
+
for (const idxFile of TS_INDEX_FILES) {
|
|
165
145
|
const candidate = path.join(base, idxFile);
|
|
166
146
|
if (existsAndIsFile(candidate))
|
|
167
147
|
return candidate;
|
|
@@ -169,105 +149,266 @@ function tryFilePathVariants(base, extensions, indexFiles) {
|
|
|
169
149
|
}
|
|
170
150
|
return null;
|
|
171
151
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
152
|
+
/**
|
|
153
|
+
* 把命中绝对路径归一为 ResolveResult。
|
|
154
|
+
* projectRoot 为真:相对 POSIX;projectRoot 为空:保留绝对路径(历史 core 独立调用行为)。
|
|
155
|
+
*/
|
|
156
|
+
function makeHit(absHit, projectRoot, kind) {
|
|
157
|
+
return {
|
|
158
|
+
resolvedPath: projectRoot ? toPosix(path.relative(projectRoot, absHit)) : absHit,
|
|
159
|
+
kind,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
// ───────────────────────────────────────────────────────────
|
|
163
|
+
// Public API — resolveTsJsImport(单一权威)
|
|
164
|
+
// ───────────────────────────────────────────────────────────
|
|
165
|
+
/**
|
|
166
|
+
* 解析 TypeScript/JavaScript import 路径(单一权威实现,Feature 181)。
|
|
167
|
+
*
|
|
168
|
+
* 解析顺序:
|
|
169
|
+
* 1. node: 内置 → external
|
|
170
|
+
* 2. 磁盘绝对路径('/' 前缀)→ 文件候选 + projectRoot 守卫
|
|
171
|
+
* 3. 相对路径('./' '../')→ 文件候选 + projectRoot 守卫;.json/.d.ts → external
|
|
172
|
+
* 4. 非相对:tsconfig paths alias(最长前缀 + 多候选 + baseUrl 叠加)→ baseUrl 解析 → external/unresolved
|
|
173
|
+
*
|
|
174
|
+
* 守卫策略(逐分支复刻历史 kg):relative / disk-absolute 分支启用 isInsideProjectRoot
|
|
175
|
+
* (仅 projectRoot 为真时);paths-alias / baseUrl 分支不加守卫(保持 alias 语义)。
|
|
176
|
+
*
|
|
177
|
+
* @param moduleSpec - import 说明符(如 './engine'、'~/utils'、'express')
|
|
178
|
+
* @param callerFile - 调用方文件的绝对路径
|
|
179
|
+
* @param projectRoot - 项目根目录绝对路径(空字符串:返回绝对路径 + 跳过守卫)
|
|
180
|
+
* @param tsConfigContext - tsconfig 解析上下文(null/undefined:仅走相对/绝对路径)
|
|
181
|
+
* @returns ResolveResult(失败返回 { resolvedPath: null, kind: 'unresolved' },不抛异常)
|
|
182
|
+
*/
|
|
183
|
+
export function resolveTsJsImport(moduleSpec, callerFile, projectRoot, tsConfigContext) {
|
|
184
|
+
if (!moduleSpec || typeof moduleSpec !== 'string') {
|
|
185
|
+
return { resolvedPath: null, kind: 'unresolved' };
|
|
176
186
|
}
|
|
177
|
-
|
|
178
|
-
|
|
187
|
+
// 1. node 内置模块 → external(不解析外部依赖)
|
|
188
|
+
if (moduleSpec.startsWith('node:')) {
|
|
189
|
+
return { resolvedPath: null, kind: 'external' };
|
|
179
190
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
191
|
+
// 2. 磁盘绝对路径(罕见)
|
|
192
|
+
if (moduleSpec.startsWith('/')) {
|
|
193
|
+
const hit = tryFilePathVariants(moduleSpec);
|
|
194
|
+
if (hit) {
|
|
195
|
+
// 守卫:候选必须在 projectRoot 内(仅 projectRoot 为真时启用)
|
|
196
|
+
if (projectRoot && !isInsideProjectRoot(hit, projectRoot)) {
|
|
197
|
+
return { resolvedPath: null, kind: 'unresolved' };
|
|
198
|
+
}
|
|
199
|
+
return makeHit(hit, projectRoot, 'absolute');
|
|
200
|
+
}
|
|
201
|
+
return { resolvedPath: null, kind: 'unresolved' };
|
|
185
202
|
}
|
|
186
|
-
|
|
187
|
-
|
|
203
|
+
// 3. 相对路径(./ 或 ../)
|
|
204
|
+
if (moduleSpec.startsWith('./') || moduleSpec.startsWith('../') || moduleSpec.startsWith('.')) {
|
|
205
|
+
// W-1:moduleSpec 自身已带 .json / .d.ts 后缀 → external
|
|
206
|
+
if (isNonSourceTarget(moduleSpec)) {
|
|
207
|
+
return { resolvedPath: null, kind: 'external' };
|
|
208
|
+
}
|
|
209
|
+
const base = path.resolve(path.dirname(callerFile), moduleSpec);
|
|
210
|
+
const hit = tryFilePathVariants(base);
|
|
211
|
+
if (hit) {
|
|
212
|
+
if (projectRoot && !isInsideProjectRoot(hit, projectRoot)) {
|
|
213
|
+
return { resolvedPath: null, kind: 'unresolved' };
|
|
214
|
+
}
|
|
215
|
+
// 命中 .json / .d.ts(如 base 无扩展名却命中同名 .d.ts)双保险 external
|
|
216
|
+
if (isNonSourceTarget(hit)) {
|
|
217
|
+
return { resolvedPath: null, kind: 'external' };
|
|
218
|
+
}
|
|
219
|
+
return makeHit(hit, projectRoot, 'relative');
|
|
220
|
+
}
|
|
221
|
+
// 候补失败但 base 本身指向 .json / .d.ts → external
|
|
222
|
+
if (isNonSourceTarget(base)) {
|
|
223
|
+
return { resolvedPath: null, kind: 'external' };
|
|
224
|
+
}
|
|
225
|
+
return { resolvedPath: null, kind: 'unresolved' };
|
|
226
|
+
}
|
|
227
|
+
// 4. 非相对路径:先 paths alias,再 baseUrl,再判定 external/unresolved
|
|
228
|
+
if (tsConfigContext) {
|
|
229
|
+
const aliasHit = resolvePathsAlias(moduleSpec, projectRoot, tsConfigContext);
|
|
230
|
+
if (aliasHit)
|
|
231
|
+
return aliasHit;
|
|
232
|
+
// baseUrl 解析(baseUrl 为字符串时)
|
|
233
|
+
if (typeof tsConfigContext.baseUrl === 'string') {
|
|
234
|
+
const absBase = path.resolve(tsConfigContext.configDir, tsConfigContext.baseUrl, moduleSpec);
|
|
235
|
+
const hit = tryFilePathVariants(absBase);
|
|
236
|
+
if (hit) {
|
|
237
|
+
return makeHit(hit, projectRoot, 'absolute');
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
// C-2:区分 alias-like(unresolved)与 bare npm package(external)
|
|
242
|
+
if (ALIAS_PREFIXES.some((p) => moduleSpec.startsWith(p)) || moduleSpec.startsWith('@/')) {
|
|
243
|
+
return { resolvedPath: null, kind: 'unresolved' };
|
|
244
|
+
}
|
|
245
|
+
// 合法 npm 包判定
|
|
246
|
+
const firstSeg = moduleSpec.split('/')[0] ?? '';
|
|
247
|
+
if (firstSeg.startsWith('@')) {
|
|
248
|
+
// scoped 包形如 @org/lib
|
|
249
|
+
return { resolvedPath: null, kind: 'external' };
|
|
250
|
+
}
|
|
251
|
+
if (/^[a-z0-9][a-z0-9-_.]*$/.test(firstSeg)) {
|
|
252
|
+
return { resolvedPath: null, kind: 'external' };
|
|
188
253
|
}
|
|
254
|
+
// 含 '/' 但首段不是合法 npm 包名 → alias-like 配置缺失
|
|
255
|
+
return { resolvedPath: null, kind: 'unresolved' };
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* AST 消费方便捷封装:解析并归一为**绝对路径**。
|
|
259
|
+
*
|
|
260
|
+
* ast-analyzer / tree-sitter-analyzer / tree-sitter-fallback 三处历史上把
|
|
261
|
+
* resolvedPath 以绝对路径写入 CodeSkeleton.imports[](module-derivation.normalizeSkeletonPaths
|
|
262
|
+
* 期望绝对输入再相对化;collect EC-10 亦绝对)。本封装保持该形态:
|
|
263
|
+
* - projectRoot 为真:把权威 resolver 的相对 POSIX 结果 path.resolve 回绝对
|
|
264
|
+
* - projectRoot 为空:权威 resolver 已返回绝对路径,原样返回
|
|
265
|
+
* external/unresolved(resolvedPath=null)统一返回 null(与历史 core string|null 语义一致)。
|
|
266
|
+
*/
|
|
267
|
+
export function resolveTsJsImportToAbsolute(moduleSpec, callerFile, projectRoot, tsConfigContext) {
|
|
268
|
+
const r = resolveTsJsImport(moduleSpec, callerFile, projectRoot, tsConfigContext);
|
|
269
|
+
if (!r.resolvedPath)
|
|
270
|
+
return null;
|
|
271
|
+
return path.isAbsolute(r.resolvedPath)
|
|
272
|
+
? r.resolvedPath
|
|
273
|
+
: path.resolve(projectRoot, r.resolvedPath);
|
|
189
274
|
}
|
|
190
|
-
// ───────────────────────────────────────────────────────────
|
|
191
|
-
// detectImportType — 从 ts-morph 节点派生 ImportType
|
|
192
|
-
// ───────────────────────────────────────────────────────────
|
|
193
275
|
/**
|
|
194
|
-
*
|
|
276
|
+
* tsconfig paths alias 解析(最长前缀 + 多候选 + baseUrl 叠加,TS 官方语义)。
|
|
195
277
|
*
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
*
|
|
278
|
+
* 合并历史两侧:
|
|
279
|
+
* - 最长前缀匹配(core:`@app/utils/*` 优先于 `@app/*`;精确 key 因 prefixLen 最长天然居首)
|
|
280
|
+
* - 多候选数组逐个尝试(core + kg)
|
|
281
|
+
* - replacement 叠加 baseUrl:path.resolve(configDir, baseUrl ?? '.', replacement)(kg C-3)
|
|
282
|
+
* - 文件候选用统一 tryFilePathVariants(含 ESM ext map / .mjs.cjs / index)
|
|
201
283
|
*
|
|
202
|
-
*
|
|
203
|
-
* (tree-sitter typescript-mapper 仅处理 import_statement,importType 始终为 'static' 或 'type-only',
|
|
204
|
-
* 由 isTypeOnly 字段派生)。
|
|
284
|
+
* @returns 命中的 ResolveResult(kind='paths-alias');未命中返回 null
|
|
205
285
|
*/
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
286
|
+
function resolvePathsAlias(moduleSpec, projectRoot, ctx) {
|
|
287
|
+
// 收集所有命中 alias,排序规则(TS 官方语义):
|
|
288
|
+
// 1. 最长前缀优先(`@app/utils/*` 胜过 `@app/*`)
|
|
289
|
+
// 2. 同前缀长度时精确 key(无 wildcard)优先于 wildcard(`react` 胜过 `react*`)
|
|
290
|
+
const matched = [];
|
|
291
|
+
for (const [pattern, replacements] of ctx.paths) {
|
|
292
|
+
const tail = matchPathsPattern(moduleSpec, pattern);
|
|
293
|
+
if (tail === null)
|
|
294
|
+
continue;
|
|
295
|
+
const isWildcard = pattern.includes('*');
|
|
296
|
+
const prefixLen = isWildcard ? pattern.indexOf('*') : pattern.length;
|
|
297
|
+
matched.push({ prefixLen, isWildcard, tail, replacements });
|
|
209
298
|
}
|
|
210
|
-
if (
|
|
211
|
-
return
|
|
299
|
+
if (matched.length === 0)
|
|
300
|
+
return null;
|
|
301
|
+
matched.sort((a, b) => {
|
|
302
|
+
if (b.prefixLen !== a.prefixLen)
|
|
303
|
+
return b.prefixLen - a.prefixLen;
|
|
304
|
+
return (a.isWildcard ? 1 : 0) - (b.isWildcard ? 1 : 0); // 同前缀长度:exact 优先
|
|
305
|
+
});
|
|
306
|
+
const baseUrl = typeof ctx.baseUrl === 'string' ? ctx.baseUrl : '.';
|
|
307
|
+
for (const m of matched) {
|
|
308
|
+
for (const replacement of m.replacements) {
|
|
309
|
+
const resolved = replacement.includes('*') ? replacement.replace('*', m.tail) : replacement;
|
|
310
|
+
const absBase = path.resolve(ctx.configDir, baseUrl, resolved);
|
|
311
|
+
const hit = tryFilePathVariants(absBase);
|
|
312
|
+
if (hit) {
|
|
313
|
+
return makeHit(hit, projectRoot, 'paths-alias');
|
|
314
|
+
}
|
|
315
|
+
}
|
|
212
316
|
}
|
|
213
|
-
|
|
214
|
-
return 'static';
|
|
317
|
+
return null;
|
|
215
318
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
return 'type-only';
|
|
319
|
+
/**
|
|
320
|
+
* 匹配 tsconfig paths pattern(精确 key + wildcard)。
|
|
321
|
+
* @returns null(不匹配),或 wildcard 截取的尾缀(精确 key 时为 '')
|
|
322
|
+
*/
|
|
323
|
+
function matchPathsPattern(moduleSpec, pattern) {
|
|
324
|
+
if (pattern.includes('*')) {
|
|
325
|
+
const starIdx = pattern.indexOf('*');
|
|
326
|
+
const prefix = pattern.slice(0, starIdx);
|
|
327
|
+
const suffix = pattern.slice(starIdx + 1);
|
|
328
|
+
if (moduleSpec.startsWith(prefix) && moduleSpec.endsWith(suffix) &&
|
|
329
|
+
moduleSpec.length >= prefix.length + suffix.length) {
|
|
330
|
+
return moduleSpec.slice(prefix.length, moduleSpec.length - suffix.length);
|
|
331
|
+
}
|
|
332
|
+
return null;
|
|
231
333
|
}
|
|
232
|
-
return '
|
|
334
|
+
return moduleSpec === pattern ? '' : null;
|
|
233
335
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
336
|
+
// ───────────────────────────────────────────────────────────
|
|
337
|
+
// Public API — tsconfig loader(单一实现:ts.parseJsonConfigFileContent,含 extends 链)
|
|
338
|
+
// ───────────────────────────────────────────────────────────
|
|
339
|
+
/**
|
|
340
|
+
* 从 filePath 向上查找最近的 tsconfig.json(monorepo-aware)。
|
|
341
|
+
*
|
|
342
|
+
* 上溯范围:从 path.dirname(filePath) 向上,直到 projectRoot 边界(含 projectRoot 本身)。
|
|
343
|
+
* 越过 projectRoot 停止,不抛异常。
|
|
344
|
+
*
|
|
345
|
+
* @returns tsconfig.json 的绝对路径;未找到返回 null
|
|
346
|
+
*/
|
|
347
|
+
export function findNearestTsConfig(filePath, projectRoot) {
|
|
348
|
+
let dir = path.dirname(filePath);
|
|
349
|
+
// eslint-disable-next-line no-constant-condition
|
|
350
|
+
while (true) {
|
|
351
|
+
// 边界检查先于 fs 操作(C-3:避免越界路径误读)
|
|
352
|
+
if (dir !== projectRoot && !isInsideProjectRoot(dir, projectRoot)) {
|
|
353
|
+
break;
|
|
354
|
+
}
|
|
355
|
+
const candidate = path.join(dir, 'tsconfig.json');
|
|
356
|
+
if (fs.existsSync(candidate)) {
|
|
357
|
+
return candidate;
|
|
358
|
+
}
|
|
359
|
+
if (dir === projectRoot)
|
|
360
|
+
break;
|
|
361
|
+
const parent = path.dirname(dir);
|
|
362
|
+
if (parent === dir)
|
|
363
|
+
break; // 文件系统根
|
|
364
|
+
dir = parent;
|
|
365
|
+
}
|
|
366
|
+
return null;
|
|
245
367
|
}
|
|
246
368
|
/**
|
|
247
|
-
*
|
|
369
|
+
* 解析 tsconfig.json 为 TsConfigResolutionContext(单一 loader,ts API 处理 extends 链)。
|
|
370
|
+
*
|
|
371
|
+
* 用 ts.readConfigFile + ts.parseJsonConfigFileContent:
|
|
372
|
+
* - 自动处理 extends 链(优于历史 kg 手写 JSON.parse 的 YAGNI 无 extends)
|
|
373
|
+
* - baseUrl 解析为绝对路径(alias 算法 path.resolve 正确覆盖 configDir)
|
|
374
|
+
* - paths 保留原始相对字符串(由 alias 算法做唯一一次 resolve,避免双重 resolve)
|
|
248
375
|
*
|
|
249
|
-
*
|
|
250
|
-
* 在 imports 收集完毕后批量 resolve;提取此 helper 避免重复实现。
|
|
376
|
+
* 失败时返回 null(不抛异常,按未配置 alias 处理)。
|
|
251
377
|
*
|
|
252
|
-
* -
|
|
253
|
-
* - 仅处理 TS/JS 语言;其他语言(Python / Go / Java)应跳过此函数。
|
|
378
|
+
* @param configPath - tsconfig.json 的绝对路径(由 findNearestTsConfig / 调用方提供)
|
|
254
379
|
*/
|
|
255
|
-
export function
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
380
|
+
export function buildTsConfigContext(configPath) {
|
|
381
|
+
try {
|
|
382
|
+
const configFile = ts.readConfigFile(configPath, ts.sys.readFile);
|
|
383
|
+
if (configFile.error || !configFile.config) {
|
|
384
|
+
// 解析失败仍 return null(行为不变),但不再静默——首次为该 configPath 发一次 warn
|
|
385
|
+
const errorSummary = configFile.error
|
|
386
|
+
? ts.flattenDiagnosticMessageText(configFile.error.messageText, ' ')
|
|
387
|
+
: 'config 为空';
|
|
388
|
+
warnTsConfigFailureOnce(configPath, errorSummary);
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
391
|
+
const configDir = path.dirname(configPath);
|
|
392
|
+
const parsed = ts.parseJsonConfigFileContent(configFile.config, ts.sys, configDir);
|
|
393
|
+
const compilerOptions = parsed.options;
|
|
394
|
+
const baseUrl = typeof compilerOptions.baseUrl === 'string' ? compilerOptions.baseUrl : null;
|
|
395
|
+
const paths = new Map();
|
|
396
|
+
if (compilerOptions.paths) {
|
|
397
|
+
for (const [key, value] of Object.entries(compilerOptions.paths)) {
|
|
398
|
+
if (Array.isArray(value)) {
|
|
399
|
+
const strs = value.filter((s) => typeof s === 'string');
|
|
400
|
+
if (strs.length > 0)
|
|
401
|
+
paths.set(key, strs);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
return { configDir, baseUrl, paths };
|
|
406
|
+
}
|
|
407
|
+
catch (err) {
|
|
408
|
+
// 解析抛错仍 return null(行为不变),但不再静默——首次为该 configPath 发一次 warn
|
|
409
|
+
const errorSummary = err instanceof Error ? err.message : String(err);
|
|
410
|
+
warnTsConfigFailureOnce(configPath, errorSummary);
|
|
411
|
+
return null;
|
|
412
|
+
}
|
|
272
413
|
}
|
|
273
414
|
//# sourceMappingURL=import-resolver.js.map
|