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,305 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feature 171 — File Navigation 纯计算/IO helper(对标 response-helpers.ts,便于 ≥95% 单测)
|
|
3
|
+
*
|
|
4
|
+
* 把 view_file / search_in_file / list_directory 的全部可独立测试逻辑下沉到本模块,
|
|
5
|
+
* 让 file-nav-tools.ts 的 handler 保持薄编排。
|
|
6
|
+
*
|
|
7
|
+
* 🔴 路径安全(resolveSafePath)是 LFI 红线:词法 containment 先于 fs,realpath 穿透 symlink,
|
|
8
|
+
* path.relative 防前缀碰撞,全部错误码脱敏(不回传绝对路径)。
|
|
9
|
+
*/
|
|
10
|
+
import * as path from 'node:path';
|
|
11
|
+
import { realpathSync, readdirSync, statSync } from 'node:fs';
|
|
12
|
+
// ============================================================
|
|
13
|
+
// 常量(REFACTOR 阶段集中)
|
|
14
|
+
// ============================================================
|
|
15
|
+
/** view_file 无定位参数时的默认窗口(对标 OpenHands 前 200 行) */
|
|
16
|
+
export const DEFAULT_VIEW_WINDOW = 200;
|
|
17
|
+
/** search_in_file 默认返回命中数 */
|
|
18
|
+
export const DEFAULT_MAX_MATCHES = 50;
|
|
19
|
+
/** maxMatches 安全上界(clamp) */
|
|
20
|
+
export const MAX_MATCHES_CAP = 1000;
|
|
21
|
+
/** 用户 pattern 长度上界(ReDoS 缓解:限制输入) */
|
|
22
|
+
export const MAX_PATTERN_LENGTH = 200;
|
|
23
|
+
/** contextLines 上界 */
|
|
24
|
+
export const MAX_CONTEXT_LINES = 20;
|
|
25
|
+
/** list_directory depth 上界 */
|
|
26
|
+
export const MAX_DIR_DEPTH = 10;
|
|
27
|
+
/** list_directory 单次最多返回条目(防超大目录灌爆 payload) */
|
|
28
|
+
export const MAX_DIR_ENTRIES = 5000;
|
|
29
|
+
/** isBinary 探测前缀字节数 */
|
|
30
|
+
const BINARY_SNIFF_BYTES = 8000;
|
|
31
|
+
/** path 参数长度上界(防超长 path 灌爆响应 / 异常输入) */
|
|
32
|
+
export const MAX_PATH_LENGTH = 4096;
|
|
33
|
+
/** 用户正则可搜索的内容字节上界(ReDoS 缓解:限制 regex 作用的输入规模) */
|
|
34
|
+
export const MAX_REGEX_CONTENT_BYTES = 2_000_000;
|
|
35
|
+
/**
|
|
36
|
+
* 解析并校验用户路径在 projectRoot 内(含 symlink 穿透)。
|
|
37
|
+
*
|
|
38
|
+
* 判定顺序(FR-013,修 Codex PATH-CLASSIFICATION):
|
|
39
|
+
* 1. NUL 字节 → invalid-input
|
|
40
|
+
* 2. realRoot = realpath(projectRoot)(projectRoot 自身 symlink 也解析)
|
|
41
|
+
* 3. 词法 containment(path.relative)→ 越界(含不存在)立即 path-outside-root,不触 fs
|
|
42
|
+
* 4. realpath 穿透候选 → ENOENT/error → file-not-found(脱敏)
|
|
43
|
+
* 5. realpath 后再 containment → symlink 逃逸 → path-outside-root
|
|
44
|
+
* posix-only(Windows 归一化 YAGNI)。
|
|
45
|
+
*/
|
|
46
|
+
export function resolveSafePath(projectRoot, userPath) {
|
|
47
|
+
if (typeof userPath !== 'string' || userPath.length === 0 || userPath.length > MAX_PATH_LENGTH || userPath.includes('\0')) {
|
|
48
|
+
return { ok: false, code: 'invalid-input' };
|
|
49
|
+
}
|
|
50
|
+
let realRoot;
|
|
51
|
+
try {
|
|
52
|
+
realRoot = realpathSync.native(projectRoot);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return { ok: false, code: 'file-not-found' };
|
|
56
|
+
}
|
|
57
|
+
const candidate = path.resolve(realRoot, userPath);
|
|
58
|
+
// 词法 containment 先于任何 fs(越界且不存在的路径也归类 path-outside-root,不泄露 fs 状态)
|
|
59
|
+
if (isOutside(realRoot, candidate)) {
|
|
60
|
+
return { ok: false, code: 'path-outside-root' };
|
|
61
|
+
}
|
|
62
|
+
let realCandidate;
|
|
63
|
+
try {
|
|
64
|
+
realCandidate = realpathSync.native(candidate);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return { ok: false, code: 'file-not-found' };
|
|
68
|
+
}
|
|
69
|
+
// realpath 后再判,拦截 symlink 逃逸
|
|
70
|
+
if (isOutside(realRoot, realCandidate)) {
|
|
71
|
+
return { ok: false, code: 'path-outside-root' };
|
|
72
|
+
}
|
|
73
|
+
return { ok: true, realPath: realCandidate };
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* 用 path.relative 判定 target 是否在 root 外。
|
|
77
|
+
* 禁止裸 startsWith(避免 /repo vs /repo2 前缀碰撞)。rel==='' 视为 root 本身,contained。
|
|
78
|
+
*/
|
|
79
|
+
function isOutside(root, target) {
|
|
80
|
+
const rel = path.relative(root, target);
|
|
81
|
+
return rel.startsWith('..') || path.isAbsolute(rel);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* 将内容拆为行数组:按 \r?\n 切分;末尾换行不计为额外空行;空内容 → []。
|
|
85
|
+
*/
|
|
86
|
+
export function splitLines(content) {
|
|
87
|
+
if (content === '')
|
|
88
|
+
return [];
|
|
89
|
+
const parts = content.split(/\r?\n/);
|
|
90
|
+
if (parts.length > 0 && parts[parts.length - 1] === '')
|
|
91
|
+
parts.pop();
|
|
92
|
+
return parts;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* 按行区间切片,带行号前缀。无定位参数时返回前 defaultWindow 行 + truncated 标志。
|
|
96
|
+
* 区间端点被安全 clamp 到 [1, totalLines],超界不抛错。
|
|
97
|
+
*/
|
|
98
|
+
export function sliceLines(content, opts = {}) {
|
|
99
|
+
const allLines = splitLines(content);
|
|
100
|
+
const totalLines = allLines.length;
|
|
101
|
+
if (totalLines === 0) {
|
|
102
|
+
return { lines: [], startLine: 0, endLine: 0, totalLines: 0, truncated: false };
|
|
103
|
+
}
|
|
104
|
+
const window = opts.defaultWindow ?? DEFAULT_VIEW_WINDOW;
|
|
105
|
+
let start;
|
|
106
|
+
let end;
|
|
107
|
+
let truncated = false;
|
|
108
|
+
if (opts.startLine === undefined && opts.endLine === undefined) {
|
|
109
|
+
start = 1;
|
|
110
|
+
end = Math.min(totalLines, window);
|
|
111
|
+
truncated = totalLines > window;
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
start = opts.startLine ?? 1;
|
|
115
|
+
end = opts.endLine ?? totalLines;
|
|
116
|
+
}
|
|
117
|
+
start = Math.max(1, Math.min(Math.trunc(start), totalLines));
|
|
118
|
+
end = Math.max(start, Math.min(Math.trunc(end), totalLines));
|
|
119
|
+
const lines = allLines.slice(start - 1, end).map((t, i) => `${start + i}\t${t}`);
|
|
120
|
+
return { lines, startLine: start, endLine: end, totalLines, truncated };
|
|
121
|
+
}
|
|
122
|
+
/** UTF-8 byte / 4 的 token 代理(改名避开 token-counter.ts 的 estimateTokens 命名碰撞) */
|
|
123
|
+
export function estimateUtf8ByteTokens(text) {
|
|
124
|
+
return Math.ceil(Buffer.byteLength(text, 'utf-8') / 4);
|
|
125
|
+
}
|
|
126
|
+
/** 二进制探测:前缀字节含 NUL 即判二进制 */
|
|
127
|
+
export function isBinary(buf) {
|
|
128
|
+
const n = Math.min(buf.length, BINARY_SNIFF_BYTES);
|
|
129
|
+
for (let i = 0; i < n; i++) {
|
|
130
|
+
if (buf[i] === 0)
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
// ============================================================
|
|
136
|
+
// 数值 clamp(FR-008)
|
|
137
|
+
// ============================================================
|
|
138
|
+
/** 把数值 clamp 到 [min, max],非法值(NaN/缺省)回退 fallback;返回是否被 clamp */
|
|
139
|
+
export function clampInt(value, min, max, fallback) {
|
|
140
|
+
if (typeof value !== 'number' || Number.isNaN(value)) {
|
|
141
|
+
return { value: fallback, clamped: true };
|
|
142
|
+
}
|
|
143
|
+
const int = Math.trunc(value);
|
|
144
|
+
if (int < min)
|
|
145
|
+
return { value: min, clamped: true };
|
|
146
|
+
if (int > max)
|
|
147
|
+
return { value: max, clamped: true };
|
|
148
|
+
return { value: int, clamped: int !== value };
|
|
149
|
+
}
|
|
150
|
+
/** 转义 literal pattern 中的正则元字符 */
|
|
151
|
+
function escapeRegExp(s) {
|
|
152
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* ReDoS 启发式:探测高危结构,命中则拒绝。覆盖:
|
|
156
|
+
* - 嵌套量词组后接量词/有界重复:(a+)+ / (.*)* / (a{1,99}){1,99}
|
|
157
|
+
* - 量化的交替组:(a|a)+ / (a|b)*
|
|
158
|
+
* 非完整 ReDoS 证明(配合 pattern 长度 + content byte 上界,FR-005 明示)。
|
|
159
|
+
*/
|
|
160
|
+
export function isRiskyRegex(pattern) {
|
|
161
|
+
const innerQuantGroup = /\([^)]*[+*{][^)]*\)/; // 组内含量词
|
|
162
|
+
const outerRepeat = /\)\s*([+*]|\{\d+,?\d*\})/; // 组后接 + * 或 {n,m}
|
|
163
|
+
if (innerQuantGroup.test(pattern) && outerRepeat.test(pattern))
|
|
164
|
+
return true;
|
|
165
|
+
if (/\([^)]*\|[^)]*\)\s*[+*]/.test(pattern))
|
|
166
|
+
return true; // 量化交替组
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* 在内容内逐行匹配 pattern,返回带上下文行的命中列表。
|
|
171
|
+
* 失败(空/超长/非法正则/高危正则)返回 { ok:false, reason },由 handler 映射 invalid-input。
|
|
172
|
+
*/
|
|
173
|
+
export function matchInFile(content, pattern, opts = {}) {
|
|
174
|
+
if (typeof pattern !== 'string' || pattern.length === 0) {
|
|
175
|
+
return { ok: false, reason: 'pattern 必填且非空' };
|
|
176
|
+
}
|
|
177
|
+
if (pattern.length > MAX_PATTERN_LENGTH) {
|
|
178
|
+
return { ok: false, reason: `pattern 过长(上限 ${MAX_PATTERN_LENGTH})` };
|
|
179
|
+
}
|
|
180
|
+
const isRegex = opts.isRegex ?? false;
|
|
181
|
+
const contextLines = clampInt(opts.contextLines ?? 0, 0, MAX_CONTEXT_LINES, 0);
|
|
182
|
+
const maxMatches = clampInt(opts.maxMatches ?? DEFAULT_MAX_MATCHES, 1, MAX_MATCHES_CAP, DEFAULT_MAX_MATCHES);
|
|
183
|
+
const warnings = [];
|
|
184
|
+
if (maxMatches.clamped)
|
|
185
|
+
warnings.push('maxMatches-clamped');
|
|
186
|
+
if (contextLines.clamped)
|
|
187
|
+
warnings.push('contextLines-clamped');
|
|
188
|
+
let regex;
|
|
189
|
+
if (isRegex) {
|
|
190
|
+
if (isRiskyRegex(pattern)) {
|
|
191
|
+
return { ok: false, reason: 'pattern 含高危结构(嵌套/量化分组,ReDoS 风险)' };
|
|
192
|
+
}
|
|
193
|
+
// ReDoS 缓解:限制用户正则作用的内容规模(literal 搜索无回溯风险,不限制)
|
|
194
|
+
if (Buffer.byteLength(content, 'utf-8') > MAX_REGEX_CONTENT_BYTES) {
|
|
195
|
+
return { ok: false, reason: `内容超 ${MAX_REGEX_CONTENT_BYTES} 字节,正则搜索受限,请用 literal 或先 view_file 缩小范围` };
|
|
196
|
+
}
|
|
197
|
+
try {
|
|
198
|
+
regex = new RegExp(pattern);
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
return { ok: false, reason: '非法正则' };
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
regex = new RegExp(escapeRegExp(pattern));
|
|
206
|
+
}
|
|
207
|
+
const lines = splitLines(content);
|
|
208
|
+
const matches = [];
|
|
209
|
+
let totalMatches = 0;
|
|
210
|
+
for (let i = 0; i < lines.length; i++) {
|
|
211
|
+
if (regex.test(lines[i])) {
|
|
212
|
+
totalMatches++;
|
|
213
|
+
if (matches.length < maxMatches.value) {
|
|
214
|
+
matches.push({
|
|
215
|
+
line: i + 1,
|
|
216
|
+
text: lines[i],
|
|
217
|
+
before: lines.slice(Math.max(0, i - contextLines.value), i),
|
|
218
|
+
after: lines.slice(i + 1, Math.min(lines.length, i + 1 + contextLines.value)),
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (totalMatches > matches.length)
|
|
224
|
+
warnings.push('matches-truncated');
|
|
225
|
+
return { ok: true, matches, totalMatches, returnedMatches: matches.length, warnings };
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* 列举目录条目(可递归 depth 层)。默认仅过滤 .git;includeIgnored=true 含 .git。
|
|
229
|
+
* 超 MAX_DIR_ENTRIES 截断并加 listing-truncated warning。readdir 失败的子目录静默跳过。
|
|
230
|
+
*/
|
|
231
|
+
export function buildDirListing(absDir, opts = {}) {
|
|
232
|
+
const depth = clampInt(opts.depth ?? 1, 1, MAX_DIR_DEPTH, 1);
|
|
233
|
+
const includeIgnored = opts.includeIgnored ?? false;
|
|
234
|
+
// maxEntries 仅供测试注入截断阈值;生产默认 MAX_DIR_ENTRIES
|
|
235
|
+
const maxEntries = opts.maxEntries && opts.maxEntries > 0 ? opts.maxEntries : MAX_DIR_ENTRIES;
|
|
236
|
+
const warnings = [];
|
|
237
|
+
if (depth.clamped)
|
|
238
|
+
warnings.push('depth-clamped');
|
|
239
|
+
const entries = [];
|
|
240
|
+
let truncated = false;
|
|
241
|
+
const walk = (dir, rel, level) => {
|
|
242
|
+
if (truncated)
|
|
243
|
+
return;
|
|
244
|
+
let dirents;
|
|
245
|
+
try {
|
|
246
|
+
dirents = readdirSync(dir, { withFileTypes: true });
|
|
247
|
+
}
|
|
248
|
+
catch {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
dirents.sort((a, b) => a.name.localeCompare(b.name));
|
|
252
|
+
for (const d of dirents) {
|
|
253
|
+
if (!includeIgnored && d.name === '.git')
|
|
254
|
+
continue;
|
|
255
|
+
if (entries.length >= maxEntries) {
|
|
256
|
+
truncated = true;
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
const relName = rel ? `${rel}/${d.name}` : d.name;
|
|
260
|
+
const type = d.isSymbolicLink() ? 'symlink' : d.isDirectory() ? 'dir' : 'file';
|
|
261
|
+
let size = null;
|
|
262
|
+
if (type === 'file') {
|
|
263
|
+
try {
|
|
264
|
+
size = statSync(path.join(dir, d.name)).size;
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
size = null;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
entries.push({ name: relName, type, size });
|
|
271
|
+
if (type === 'dir' && level < depth.value) {
|
|
272
|
+
walk(path.join(dir, d.name), relName, level + 1);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
walk(absDir, '', 1);
|
|
277
|
+
if (truncated)
|
|
278
|
+
warnings.push('listing-truncated');
|
|
279
|
+
return { entries, warnings };
|
|
280
|
+
}
|
|
281
|
+
// ============================================================
|
|
282
|
+
// nextStepHint(FR-040,独立实现不碰 response-helpers.ts)
|
|
283
|
+
// ============================================================
|
|
284
|
+
/** 生成 file-nav 工具的下一步引导(风格对齐 generateNextStepHint,闭环 view_file ↔ context) */
|
|
285
|
+
export function buildFileNavHint(toolName, data) {
|
|
286
|
+
if (toolName === 'view_file') {
|
|
287
|
+
const startLine = data['startLine'] ?? 0;
|
|
288
|
+
const endLine = data['endLine'] ?? 0;
|
|
289
|
+
const totalLines = data['totalLines'] ?? 0;
|
|
290
|
+
if (data['truncated'] === true) {
|
|
291
|
+
return `已显示前 ${endLine}/${totalLines} 行(截断),建议用 startLine/endLine 翻页,或调 context 看该 symbol 的调用方与依赖`;
|
|
292
|
+
}
|
|
293
|
+
return `已显示第 ${startLine}-${endLine}/${totalLines} 行,建议调 context 查看该区域 symbol 的调用方与依赖`;
|
|
294
|
+
}
|
|
295
|
+
if (toolName === 'search_in_file') {
|
|
296
|
+
const total = data['totalMatches'] ?? 0;
|
|
297
|
+
if (total === 0) {
|
|
298
|
+
return '未找到匹配,建议放宽 pattern、改用 isRegex,或确认文件路径';
|
|
299
|
+
}
|
|
300
|
+
return `找到 ${total} 处匹配,建议用 view_file 按某处行号查看完整上下文行段`;
|
|
301
|
+
}
|
|
302
|
+
const count = data['entryCount'] ?? 0;
|
|
303
|
+
return `目录含 ${count} 项,建议用 view_file 查看具体文件,或 search_in_file 在文件内定位 symbol`;
|
|
304
|
+
}
|
|
305
|
+
//# sourceMappingURL=file-nav-helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-nav-helpers.js","sourceRoot":"","sources":["../../../src/mcp/lib/file-nav-helpers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feature 158 — MCP Telemetry(从 agent-context-tools.ts 抽出,Feature 171)
|
|
3
|
+
*
|
|
4
|
+
* 抽到独立模块的动机(Feature 171 Codex TELEMETRY-COUPLING):
|
|
5
|
+
* file-nav-tools.ts 需复用 recordAndReturn,若从 agent-context-tools.ts import
|
|
6
|
+
* 会把整个 graph 查询依赖图拉进来。抽到本轻量模块后 file-nav 只依赖 tool-response + telemetry。
|
|
7
|
+
*
|
|
8
|
+
* agent-context-tools.ts 保留向后兼容 re-export(既有 telemetry.test.ts 从该路径 import)。
|
|
9
|
+
*/
|
|
10
|
+
import { type ToolResult } from './tool-response.js';
|
|
11
|
+
/**
|
|
12
|
+
* Telemetry entry — 单次 handler 调用的可观测数据,按行写入 JSONL。
|
|
13
|
+
* 由评测脚本通过 SPECTRA_MCP_TELEMETRY_PATH 注入路径,SPECTRA_MCP_RUN_ID 标识 run。
|
|
14
|
+
*/
|
|
15
|
+
export interface TelemetryEntry {
|
|
16
|
+
ts: string;
|
|
17
|
+
toolName: string;
|
|
18
|
+
requestSize: number;
|
|
19
|
+
responseSize: number;
|
|
20
|
+
durationMs: number;
|
|
21
|
+
runId: string;
|
|
22
|
+
errorCode?: string;
|
|
23
|
+
responseSummary?: Record<string, number>;
|
|
24
|
+
responseSamples?: {
|
|
25
|
+
symbols?: string[];
|
|
26
|
+
files?: string[];
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 写入 telemetry JSONL —— 静默降级:
|
|
31
|
+
* - env 未设置 → no-op
|
|
32
|
+
* - 写入失败 → 静默吞,不影响 MCP response
|
|
33
|
+
*/
|
|
34
|
+
export declare function writeTelemetry(entry: TelemetryEntry): void;
|
|
35
|
+
/**
|
|
36
|
+
* 从 ToolResult 中提取 errorCode(仅 isError=true 时存在)。
|
|
37
|
+
* 解析失败时返回 undefined(telemetry 字段缺省)。
|
|
38
|
+
*/
|
|
39
|
+
export declare function extractErrorCode(result: ToolResult): string | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Wrapper:记录 handler 调用 telemetry 后返回原 result。
|
|
42
|
+
* 包裹所有 return 路径(含 buildErrorResponse 早 return)。
|
|
43
|
+
*/
|
|
44
|
+
export declare function recordAndReturn(toolName: string, startTimeMs: number, requestSize: number, result: ToolResult, responseSummary?: Record<string, number>, responseSamples?: {
|
|
45
|
+
symbols?: string[];
|
|
46
|
+
files?: string[];
|
|
47
|
+
}): ToolResult;
|
|
48
|
+
/**
|
|
49
|
+
* Feature 177 — 注册层 telemetry 装饰器。
|
|
50
|
+
*
|
|
51
|
+
* 把 F158 telemetry 采样从"handler 内部自调 recordAndReturn"推广到**注册层**,
|
|
52
|
+
* 用于无富采样需求(responseSummary/responseSamples)的工具(graph 6 + server 5 共 11 个)。
|
|
53
|
+
*
|
|
54
|
+
* 职责:
|
|
55
|
+
* 1. 入口记 start / requestSize
|
|
56
|
+
* 2. 出口经 recordAndReturn 透传(含 errorCode 提取)
|
|
57
|
+
* 3. 顶层未预期异常 → 脱敏 internal-error 安全网(不泄漏 err.message/stack,spec C-4)
|
|
58
|
+
*
|
|
59
|
+
* 不变量(spec AD-1 双源分区):被本装饰器包裹的工具**不得**在 handler 内再调 recordAndReturn;
|
|
60
|
+
* 已在 handler 内自采样的工具(agent-context / file-nav)**不得**被本装饰器包裹。
|
|
61
|
+
* → 每个到达 handler 的调用恰写 1 行 telemetry(锁死双发射 EC-1)。
|
|
62
|
+
*
|
|
63
|
+
* 范围(spec EC-10):MCP SDK 在 handler 前的 schema 校验失败走 SDK 自身错误,
|
|
64
|
+
* 不进入本装饰器,不在 F177 telemetry 覆盖范围内。
|
|
65
|
+
*
|
|
66
|
+
* 不接受泛型(YAGNI):args 用 unknown;SDK 传入的 extra 形参被忽略(17 工具均不用 extra)。
|
|
67
|
+
*/
|
|
68
|
+
export declare function withTelemetry(toolName: string, handler: (args: unknown) => Promise<ToolResult> | ToolResult): (args: unknown) => Promise<ToolResult>;
|
|
69
|
+
//# sourceMappingURL=telemetry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../../../src/mcp/lib/telemetry.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAsB,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEzE;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,eAAe,CAAC,EAAE;QAChB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QACnB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;KAClB,CAAC;CACH;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAQ1D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,GAAG,SAAS,CAUvE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,UAAU,EAClB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACxC,eAAe,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GACzD,UAAU,CAqBZ;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,UAAU,CAAC,GAAG,UAAU,GAC3D,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,UAAU,CAAC,CAsBxC"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feature 158 — MCP Telemetry(从 agent-context-tools.ts 抽出,Feature 171)
|
|
3
|
+
*
|
|
4
|
+
* 抽到独立模块的动机(Feature 171 Codex TELEMETRY-COUPLING):
|
|
5
|
+
* file-nav-tools.ts 需复用 recordAndReturn,若从 agent-context-tools.ts import
|
|
6
|
+
* 会把整个 graph 查询依赖图拉进来。抽到本轻量模块后 file-nav 只依赖 tool-response + telemetry。
|
|
7
|
+
*
|
|
8
|
+
* agent-context-tools.ts 保留向后兼容 re-export(既有 telemetry.test.ts 从该路径 import)。
|
|
9
|
+
*/
|
|
10
|
+
import { appendFileSync } from 'node:fs';
|
|
11
|
+
import { buildErrorResponse } from './tool-response.js';
|
|
12
|
+
/**
|
|
13
|
+
* 写入 telemetry JSONL —— 静默降级:
|
|
14
|
+
* - env 未设置 → no-op
|
|
15
|
+
* - 写入失败 → 静默吞,不影响 MCP response
|
|
16
|
+
*/
|
|
17
|
+
export function writeTelemetry(entry) {
|
|
18
|
+
const telPath = process.env['SPECTRA_MCP_TELEMETRY_PATH'];
|
|
19
|
+
if (telPath === undefined || telPath.length === 0)
|
|
20
|
+
return;
|
|
21
|
+
try {
|
|
22
|
+
appendFileSync(telPath, JSON.stringify(entry) + '\n', 'utf-8');
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// FR-G-002: silent degrade — 写入失败时不抛异常,不影响 handler 返回
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 从 ToolResult 中提取 errorCode(仅 isError=true 时存在)。
|
|
30
|
+
* 解析失败时返回 undefined(telemetry 字段缺省)。
|
|
31
|
+
*/
|
|
32
|
+
export function extractErrorCode(result) {
|
|
33
|
+
if (result.isError !== true)
|
|
34
|
+
return undefined;
|
|
35
|
+
const text = result.content?.[0]?.text;
|
|
36
|
+
if (typeof text !== 'string')
|
|
37
|
+
return undefined;
|
|
38
|
+
try {
|
|
39
|
+
const parsed = JSON.parse(text);
|
|
40
|
+
return typeof parsed.code === 'string' ? parsed.code : undefined;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Wrapper:记录 handler 调用 telemetry 后返回原 result。
|
|
48
|
+
* 包裹所有 return 路径(含 buildErrorResponse 早 return)。
|
|
49
|
+
*/
|
|
50
|
+
export function recordAndReturn(toolName, startTimeMs, requestSize, result, responseSummary, responseSamples) {
|
|
51
|
+
const responseText = result.content?.[0]?.text ?? '';
|
|
52
|
+
const responseSize = typeof responseText === 'string' ? Buffer.byteLength(responseText, 'utf-8') : 0;
|
|
53
|
+
const entry = {
|
|
54
|
+
ts: new Date().toISOString(),
|
|
55
|
+
toolName,
|
|
56
|
+
requestSize,
|
|
57
|
+
responseSize,
|
|
58
|
+
durationMs: Date.now() - startTimeMs,
|
|
59
|
+
runId: process.env['SPECTRA_MCP_RUN_ID'] ?? 'unknown',
|
|
60
|
+
};
|
|
61
|
+
const errorCode = extractErrorCode(result);
|
|
62
|
+
if (errorCode !== undefined)
|
|
63
|
+
entry.errorCode = errorCode;
|
|
64
|
+
if (responseSummary !== undefined && errorCode === undefined) {
|
|
65
|
+
entry.responseSummary = responseSummary;
|
|
66
|
+
}
|
|
67
|
+
if (responseSamples !== undefined && errorCode === undefined) {
|
|
68
|
+
entry.responseSamples = responseSamples;
|
|
69
|
+
}
|
|
70
|
+
writeTelemetry(entry);
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Feature 177 — 注册层 telemetry 装饰器。
|
|
75
|
+
*
|
|
76
|
+
* 把 F158 telemetry 采样从"handler 内部自调 recordAndReturn"推广到**注册层**,
|
|
77
|
+
* 用于无富采样需求(responseSummary/responseSamples)的工具(graph 6 + server 5 共 11 个)。
|
|
78
|
+
*
|
|
79
|
+
* 职责:
|
|
80
|
+
* 1. 入口记 start / requestSize
|
|
81
|
+
* 2. 出口经 recordAndReturn 透传(含 errorCode 提取)
|
|
82
|
+
* 3. 顶层未预期异常 → 脱敏 internal-error 安全网(不泄漏 err.message/stack,spec C-4)
|
|
83
|
+
*
|
|
84
|
+
* 不变量(spec AD-1 双源分区):被本装饰器包裹的工具**不得**在 handler 内再调 recordAndReturn;
|
|
85
|
+
* 已在 handler 内自采样的工具(agent-context / file-nav)**不得**被本装饰器包裹。
|
|
86
|
+
* → 每个到达 handler 的调用恰写 1 行 telemetry(锁死双发射 EC-1)。
|
|
87
|
+
*
|
|
88
|
+
* 范围(spec EC-10):MCP SDK 在 handler 前的 schema 校验失败走 SDK 自身错误,
|
|
89
|
+
* 不进入本装饰器,不在 F177 telemetry 覆盖范围内。
|
|
90
|
+
*
|
|
91
|
+
* 不接受泛型(YAGNI):args 用 unknown;SDK 传入的 extra 形参被忽略(17 工具均不用 extra)。
|
|
92
|
+
*/
|
|
93
|
+
export function withTelemetry(toolName, handler) {
|
|
94
|
+
return async (args) => {
|
|
95
|
+
const startTimeMs = Date.now();
|
|
96
|
+
let requestSize = 0;
|
|
97
|
+
try {
|
|
98
|
+
requestSize = JSON.stringify(args).length;
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
// 循环引用等无法序列化 → requestSize 记 0
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
const result = await handler(args);
|
|
105
|
+
return recordAndReturn(toolName, startTimeMs, requestSize, result);
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// 顶层未预期异常 → 脱敏 internal-error(不回传 err.message/stack)
|
|
109
|
+
return recordAndReturn(toolName, startTimeMs, requestSize, buildErrorResponse('internal-error', `${toolName} 内部错误`));
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=telemetry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetry.js","sourceRoot":"","sources":["../../../src/mcp/lib/telemetry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feature 171 — MCP 共享响应原语(tool-response)
|
|
3
|
+
*
|
|
4
|
+
* 从 agent-context-tools.ts 抽出的共享层,解决 Feature 171 Codex C-4:
|
|
5
|
+
* ToolResult / ErrorCode / buildErrorResponse / buildSuccessResponse / PAYLOAD_CAP_BYTES
|
|
6
|
+
* 原为 agent-context-tools.ts 私有符号,file-nav-tools.ts 无法复用。
|
|
7
|
+
* 抽到本模块后 agent-context-tools 与 file-nav 双向导入(去重,非污染)。
|
|
8
|
+
*
|
|
9
|
+
* ErrorCode union 在既有 9 码基础上新增 Feature 171 的 3 码:
|
|
10
|
+
* path-outside-root / binary-file / file-not-found
|
|
11
|
+
*/
|
|
12
|
+
/** 统一错误码(Feature 155 既有 9 码 + Feature 171 新增 3 码) */
|
|
13
|
+
export type ErrorCode = 'graph-not-built' | 'symbol-not-found' | 'invalid-symbol-id' | 'invalid-input' | 'invalid-diff' | 'payload-too-large' | 'git-spawn-failed' | 'git-timeout' | 'internal-error' | 'path-outside-root' | 'binary-file' | 'file-not-found' | 'graph-query-failed' | 'graph-format-stale';
|
|
14
|
+
/** MCP tool 统一响应 envelope */
|
|
15
|
+
export interface ToolResult {
|
|
16
|
+
[key: string]: unknown;
|
|
17
|
+
content: Array<{
|
|
18
|
+
type: 'text';
|
|
19
|
+
text: string;
|
|
20
|
+
[key: string]: unknown;
|
|
21
|
+
}>;
|
|
22
|
+
isError?: true;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 构造错误响应 envelope。
|
|
26
|
+
* 调用方负责脱敏:message/hint/context 不得包含绝对路径、stack、raw errno path
|
|
27
|
+
* (Feature 171 FR-014)。
|
|
28
|
+
*/
|
|
29
|
+
export declare function buildErrorResponse(code: ErrorCode, message: string, hint?: string, context?: Record<string, unknown>): ToolResult;
|
|
30
|
+
/** payload 字节上限:超限时截断可截断数组并加 payload-truncated warning */
|
|
31
|
+
export declare const PAYLOAD_CAP_BYTES = 1000000;
|
|
32
|
+
/**
|
|
33
|
+
* 构造成功响应 envelope。
|
|
34
|
+
* 当序列化超 PAYLOAD_CAP_BYTES 且提供了 truncatableArrayKeys 时,
|
|
35
|
+
* 按比例收缩这些数组并追加 warnings 含 'payload-truncated'。
|
|
36
|
+
*
|
|
37
|
+
* 注意:本函数始终返回 success(不返回 payload-too-large)。
|
|
38
|
+
* 若收缩后仍超 cap,由调用方(handler)复核 byteLength 后决定是否改返 payload-too-large。
|
|
39
|
+
*/
|
|
40
|
+
export declare function buildSuccessResponse(data: Record<string, unknown>, truncatableArrayKeys?: string[], warningsKey?: string): ToolResult;
|
|
41
|
+
/**
|
|
42
|
+
* 复核 ToolResult 的序列化字节是否仍超 cap(Feature 171 FR-021)。
|
|
43
|
+
* handler 在 buildSuccessResponse 收缩后调用本函数,仍超则改返 payload-too-large。
|
|
44
|
+
*/
|
|
45
|
+
export declare function exceedsPayloadCap(result: ToolResult): boolean;
|
|
46
|
+
//# sourceMappingURL=tool-response.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-response.d.ts","sourceRoot":"","sources":["../../../src/mcp/lib/tool-response.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,qDAAqD;AACrD,MAAM,MAAM,SAAS,GAEjB,iBAAiB,GACjB,kBAAkB,GAClB,mBAAmB,GACnB,eAAe,GACf,cAAc,GACd,mBAAmB,GACnB,kBAAkB,GAClB,aAAa,GACb,gBAAgB,GAEhB,mBAAmB,GACnB,aAAa,GACb,gBAAgB,GAGhB,oBAAoB,GAGpB,oBAAoB,CAAC;AAEzB,6BAA6B;AAC7B,MAAM,WAAW,UAAU;IACzB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC,CAAC;IACvE,OAAO,CAAC,EAAE,IAAI,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,UAAU,CAQZ;AAED,0DAA0D;AAC1D,eAAO,MAAM,iBAAiB,UAAY,CAAC;AAE3C;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,oBAAoB,GAAE,MAAM,EAAO,EACnC,WAAW,GAAE,MAAmB,GAC/B,UAAU,CAkCZ;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAG7D"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feature 171 — MCP 共享响应原语(tool-response)
|
|
3
|
+
*
|
|
4
|
+
* 从 agent-context-tools.ts 抽出的共享层,解决 Feature 171 Codex C-4:
|
|
5
|
+
* ToolResult / ErrorCode / buildErrorResponse / buildSuccessResponse / PAYLOAD_CAP_BYTES
|
|
6
|
+
* 原为 agent-context-tools.ts 私有符号,file-nav-tools.ts 无法复用。
|
|
7
|
+
* 抽到本模块后 agent-context-tools 与 file-nav 双向导入(去重,非污染)。
|
|
8
|
+
*
|
|
9
|
+
* ErrorCode union 在既有 9 码基础上新增 Feature 171 的 3 码:
|
|
10
|
+
* path-outside-root / binary-file / file-not-found
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* 构造错误响应 envelope。
|
|
14
|
+
* 调用方负责脱敏:message/hint/context 不得包含绝对路径、stack、raw errno path
|
|
15
|
+
* (Feature 171 FR-014)。
|
|
16
|
+
*/
|
|
17
|
+
export function buildErrorResponse(code, message, hint, context) {
|
|
18
|
+
const payload = { code, message };
|
|
19
|
+
if (hint !== undefined)
|
|
20
|
+
payload['hint'] = hint;
|
|
21
|
+
if (context !== undefined)
|
|
22
|
+
payload['context'] = context;
|
|
23
|
+
return {
|
|
24
|
+
isError: true,
|
|
25
|
+
content: [{ type: 'text', text: JSON.stringify(payload) }],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/** payload 字节上限:超限时截断可截断数组并加 payload-truncated warning */
|
|
29
|
+
export const PAYLOAD_CAP_BYTES = 1_000_000;
|
|
30
|
+
/**
|
|
31
|
+
* 构造成功响应 envelope。
|
|
32
|
+
* 当序列化超 PAYLOAD_CAP_BYTES 且提供了 truncatableArrayKeys 时,
|
|
33
|
+
* 按比例收缩这些数组并追加 warnings 含 'payload-truncated'。
|
|
34
|
+
*
|
|
35
|
+
* 注意:本函数始终返回 success(不返回 payload-too-large)。
|
|
36
|
+
* 若收缩后仍超 cap,由调用方(handler)复核 byteLength 后决定是否改返 payload-too-large。
|
|
37
|
+
*/
|
|
38
|
+
export function buildSuccessResponse(data, truncatableArrayKeys = [], warningsKey = 'warnings') {
|
|
39
|
+
let text = JSON.stringify(data);
|
|
40
|
+
let bytes = Buffer.byteLength(text, 'utf-8');
|
|
41
|
+
if (bytes > PAYLOAD_CAP_BYTES && truncatableArrayKeys.length > 0) {
|
|
42
|
+
let truncated = false;
|
|
43
|
+
let safety = 0;
|
|
44
|
+
while (bytes > PAYLOAD_CAP_BYTES && safety < 8) {
|
|
45
|
+
safety++;
|
|
46
|
+
let progressed = false;
|
|
47
|
+
for (const key of truncatableArrayKeys) {
|
|
48
|
+
const arr = data[key];
|
|
49
|
+
if (!Array.isArray(arr) || arr.length === 0)
|
|
50
|
+
continue;
|
|
51
|
+
// 按比例 0.7 收缩,至少减 1
|
|
52
|
+
const ratio = PAYLOAD_CAP_BYTES / bytes;
|
|
53
|
+
const newLen = Math.max(0, Math.min(arr.length - 1, Math.floor(arr.length * ratio * 0.7)));
|
|
54
|
+
if (newLen < arr.length) {
|
|
55
|
+
data[key] = arr.slice(0, newLen);
|
|
56
|
+
truncated = true;
|
|
57
|
+
progressed = true;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (!progressed)
|
|
61
|
+
break;
|
|
62
|
+
text = JSON.stringify(data);
|
|
63
|
+
bytes = Buffer.byteLength(text, 'utf-8');
|
|
64
|
+
}
|
|
65
|
+
if (truncated) {
|
|
66
|
+
const warnings = data[warningsKey] ?? [];
|
|
67
|
+
if (!warnings.includes('payload-truncated')) {
|
|
68
|
+
data[warningsKey] = [...warnings, 'payload-truncated'];
|
|
69
|
+
text = JSON.stringify(data);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return { content: [{ type: 'text', text }] };
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* 复核 ToolResult 的序列化字节是否仍超 cap(Feature 171 FR-021)。
|
|
77
|
+
* handler 在 buildSuccessResponse 收缩后调用本函数,仍超则改返 payload-too-large。
|
|
78
|
+
*/
|
|
79
|
+
export function exceedsPayloadCap(result) {
|
|
80
|
+
const text = result.content?.[0]?.text ?? '';
|
|
81
|
+
return Buffer.byteLength(text, 'utf-8') > PAYLOAD_CAP_BYTES;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=tool-response.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-response.js","sourceRoot":"","sources":["../../../src/mcp/lib/tool-response.ts"],"names":[],"mappings":"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"}
|