spec-gen-cli 1.0.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/LICENSE +21 -0
- package/README.md +1078 -0
- package/dist/api/analyze.d.ts +17 -0
- package/dist/api/analyze.d.ts.map +1 -0
- package/dist/api/analyze.js +109 -0
- package/dist/api/analyze.js.map +1 -0
- package/dist/api/drift.d.ts +21 -0
- package/dist/api/drift.d.ts.map +1 -0
- package/dist/api/drift.js +145 -0
- package/dist/api/drift.js.map +1 -0
- package/dist/api/generate.d.ts +18 -0
- package/dist/api/generate.d.ts.map +1 -0
- package/dist/api/generate.js +251 -0
- package/dist/api/generate.js.map +1 -0
- package/dist/api/index.d.ts +39 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +32 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/init.d.ts +18 -0
- package/dist/api/init.d.ts.map +1 -0
- package/dist/api/init.js +82 -0
- package/dist/api/init.js.map +1 -0
- package/dist/api/run.d.ts +19 -0
- package/dist/api/run.d.ts.map +1 -0
- package/dist/api/run.js +291 -0
- package/dist/api/run.js.map +1 -0
- package/dist/api/specs.d.ts +49 -0
- package/dist/api/specs.d.ts.map +1 -0
- package/dist/api/specs.js +136 -0
- package/dist/api/specs.js.map +1 -0
- package/dist/api/types.d.ts +176 -0
- package/dist/api/types.d.ts.map +1 -0
- package/dist/api/types.js +9 -0
- package/dist/api/types.js.map +1 -0
- package/dist/api/verify.d.ts +20 -0
- package/dist/api/verify.d.ts.map +1 -0
- package/dist/api/verify.js +117 -0
- package/dist/api/verify.js.map +1 -0
- package/dist/cli/commands/analyze.d.ts +27 -0
- package/dist/cli/commands/analyze.d.ts.map +1 -0
- package/dist/cli/commands/analyze.js +485 -0
- package/dist/cli/commands/analyze.js.map +1 -0
- package/dist/cli/commands/drift.d.ts +9 -0
- package/dist/cli/commands/drift.d.ts.map +1 -0
- package/dist/cli/commands/drift.js +540 -0
- package/dist/cli/commands/drift.js.map +1 -0
- package/dist/cli/commands/generate.d.ts +9 -0
- package/dist/cli/commands/generate.d.ts.map +1 -0
- package/dist/cli/commands/generate.js +633 -0
- package/dist/cli/commands/generate.js.map +1 -0
- package/dist/cli/commands/init.d.ts +9 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +171 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/mcp.d.ts +638 -0
- package/dist/cli/commands/mcp.d.ts.map +1 -0
- package/dist/cli/commands/mcp.js +574 -0
- package/dist/cli/commands/mcp.js.map +1 -0
- package/dist/cli/commands/run.d.ts +24 -0
- package/dist/cli/commands/run.d.ts.map +1 -0
- package/dist/cli/commands/run.js +546 -0
- package/dist/cli/commands/run.js.map +1 -0
- package/dist/cli/commands/verify.d.ts +9 -0
- package/dist/cli/commands/verify.d.ts.map +1 -0
- package/dist/cli/commands/verify.js +417 -0
- package/dist/cli/commands/verify.js.map +1 -0
- package/dist/cli/commands/view.d.ts +9 -0
- package/dist/cli/commands/view.d.ts.map +1 -0
- package/dist/cli/commands/view.js +511 -0
- package/dist/cli/commands/view.js.map +1 -0
- package/dist/cli/index.d.ts +9 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +83 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/core/analyzer/architecture-writer.d.ts +67 -0
- package/dist/core/analyzer/architecture-writer.d.ts.map +1 -0
- package/dist/core/analyzer/architecture-writer.js +209 -0
- package/dist/core/analyzer/architecture-writer.js.map +1 -0
- package/dist/core/analyzer/artifact-generator.d.ts +222 -0
- package/dist/core/analyzer/artifact-generator.d.ts.map +1 -0
- package/dist/core/analyzer/artifact-generator.js +726 -0
- package/dist/core/analyzer/artifact-generator.js.map +1 -0
- package/dist/core/analyzer/call-graph.d.ts +83 -0
- package/dist/core/analyzer/call-graph.d.ts.map +1 -0
- package/dist/core/analyzer/call-graph.js +827 -0
- package/dist/core/analyzer/call-graph.js.map +1 -0
- package/dist/core/analyzer/code-shaper.d.ts +33 -0
- package/dist/core/analyzer/code-shaper.d.ts.map +1 -0
- package/dist/core/analyzer/code-shaper.js +149 -0
- package/dist/core/analyzer/code-shaper.js.map +1 -0
- package/dist/core/analyzer/dependency-graph.d.ts +179 -0
- package/dist/core/analyzer/dependency-graph.d.ts.map +1 -0
- package/dist/core/analyzer/dependency-graph.js +574 -0
- package/dist/core/analyzer/dependency-graph.js.map +1 -0
- package/dist/core/analyzer/duplicate-detector.d.ts +52 -0
- package/dist/core/analyzer/duplicate-detector.d.ts.map +1 -0
- package/dist/core/analyzer/duplicate-detector.js +279 -0
- package/dist/core/analyzer/duplicate-detector.js.map +1 -0
- package/dist/core/analyzer/embedding-service.d.ts +50 -0
- package/dist/core/analyzer/embedding-service.d.ts.map +1 -0
- package/dist/core/analyzer/embedding-service.js +104 -0
- package/dist/core/analyzer/embedding-service.js.map +1 -0
- package/dist/core/analyzer/file-walker.d.ts +78 -0
- package/dist/core/analyzer/file-walker.d.ts.map +1 -0
- package/dist/core/analyzer/file-walker.js +531 -0
- package/dist/core/analyzer/file-walker.js.map +1 -0
- package/dist/core/analyzer/import-parser.d.ts +91 -0
- package/dist/core/analyzer/import-parser.d.ts.map +1 -0
- package/dist/core/analyzer/import-parser.js +720 -0
- package/dist/core/analyzer/import-parser.js.map +1 -0
- package/dist/core/analyzer/index.d.ts +10 -0
- package/dist/core/analyzer/index.d.ts.map +1 -0
- package/dist/core/analyzer/index.js +10 -0
- package/dist/core/analyzer/index.js.map +1 -0
- package/dist/core/analyzer/refactor-analyzer.d.ts +80 -0
- package/dist/core/analyzer/refactor-analyzer.d.ts.map +1 -0
- package/dist/core/analyzer/refactor-analyzer.js +339 -0
- package/dist/core/analyzer/refactor-analyzer.js.map +1 -0
- package/dist/core/analyzer/repository-mapper.d.ts +150 -0
- package/dist/core/analyzer/repository-mapper.d.ts.map +1 -0
- package/dist/core/analyzer/repository-mapper.js +731 -0
- package/dist/core/analyzer/repository-mapper.js.map +1 -0
- package/dist/core/analyzer/signature-extractor.d.ts +31 -0
- package/dist/core/analyzer/signature-extractor.d.ts.map +1 -0
- package/dist/core/analyzer/signature-extractor.js +387 -0
- package/dist/core/analyzer/signature-extractor.js.map +1 -0
- package/dist/core/analyzer/significance-scorer.d.ts +79 -0
- package/dist/core/analyzer/significance-scorer.d.ts.map +1 -0
- package/dist/core/analyzer/significance-scorer.js +407 -0
- package/dist/core/analyzer/significance-scorer.js.map +1 -0
- package/dist/core/analyzer/subgraph-extractor.d.ts +43 -0
- package/dist/core/analyzer/subgraph-extractor.d.ts.map +1 -0
- package/dist/core/analyzer/subgraph-extractor.js +129 -0
- package/dist/core/analyzer/subgraph-extractor.js.map +1 -0
- package/dist/core/analyzer/vector-index.d.ts +63 -0
- package/dist/core/analyzer/vector-index.d.ts.map +1 -0
- package/dist/core/analyzer/vector-index.js +169 -0
- package/dist/core/analyzer/vector-index.js.map +1 -0
- package/dist/core/drift/drift-detector.d.ts +102 -0
- package/dist/core/drift/drift-detector.d.ts.map +1 -0
- package/dist/core/drift/drift-detector.js +597 -0
- package/dist/core/drift/drift-detector.js.map +1 -0
- package/dist/core/drift/git-diff.d.ts +55 -0
- package/dist/core/drift/git-diff.d.ts.map +1 -0
- package/dist/core/drift/git-diff.js +356 -0
- package/dist/core/drift/git-diff.js.map +1 -0
- package/dist/core/drift/index.d.ts +12 -0
- package/dist/core/drift/index.d.ts.map +1 -0
- package/dist/core/drift/index.js +9 -0
- package/dist/core/drift/index.js.map +1 -0
- package/dist/core/drift/spec-mapper.d.ts +73 -0
- package/dist/core/drift/spec-mapper.d.ts.map +1 -0
- package/dist/core/drift/spec-mapper.js +353 -0
- package/dist/core/drift/spec-mapper.js.map +1 -0
- package/dist/core/generator/adr-generator.d.ts +32 -0
- package/dist/core/generator/adr-generator.d.ts.map +1 -0
- package/dist/core/generator/adr-generator.js +192 -0
- package/dist/core/generator/adr-generator.js.map +1 -0
- package/dist/core/generator/index.d.ts +9 -0
- package/dist/core/generator/index.d.ts.map +1 -0
- package/dist/core/generator/index.js +12 -0
- package/dist/core/generator/index.js.map +1 -0
- package/dist/core/generator/mapping-generator.d.ts +54 -0
- package/dist/core/generator/mapping-generator.d.ts.map +1 -0
- package/dist/core/generator/mapping-generator.js +239 -0
- package/dist/core/generator/mapping-generator.js.map +1 -0
- package/dist/core/generator/openspec-compat.d.ts +160 -0
- package/dist/core/generator/openspec-compat.d.ts.map +1 -0
- package/dist/core/generator/openspec-compat.js +523 -0
- package/dist/core/generator/openspec-compat.js.map +1 -0
- package/dist/core/generator/openspec-format-generator.d.ts +111 -0
- package/dist/core/generator/openspec-format-generator.d.ts.map +1 -0
- package/dist/core/generator/openspec-format-generator.js +817 -0
- package/dist/core/generator/openspec-format-generator.js.map +1 -0
- package/dist/core/generator/openspec-writer.d.ts +131 -0
- package/dist/core/generator/openspec-writer.d.ts.map +1 -0
- package/dist/core/generator/openspec-writer.js +379 -0
- package/dist/core/generator/openspec-writer.js.map +1 -0
- package/dist/core/generator/prompts.d.ts +35 -0
- package/dist/core/generator/prompts.d.ts.map +1 -0
- package/dist/core/generator/prompts.js +212 -0
- package/dist/core/generator/prompts.js.map +1 -0
- package/dist/core/generator/spec-pipeline.d.ts +94 -0
- package/dist/core/generator/spec-pipeline.d.ts.map +1 -0
- package/dist/core/generator/spec-pipeline.js +474 -0
- package/dist/core/generator/spec-pipeline.js.map +1 -0
- package/dist/core/generator/stages/stage1-survey.d.ts +19 -0
- package/dist/core/generator/stages/stage1-survey.d.ts.map +1 -0
- package/dist/core/generator/stages/stage1-survey.js +105 -0
- package/dist/core/generator/stages/stage1-survey.js.map +1 -0
- package/dist/core/generator/stages/stage2-entities.d.ts +11 -0
- package/dist/core/generator/stages/stage2-entities.d.ts.map +1 -0
- package/dist/core/generator/stages/stage2-entities.js +67 -0
- package/dist/core/generator/stages/stage2-entities.js.map +1 -0
- package/dist/core/generator/stages/stage3-services.d.ts +11 -0
- package/dist/core/generator/stages/stage3-services.d.ts.map +1 -0
- package/dist/core/generator/stages/stage3-services.js +75 -0
- package/dist/core/generator/stages/stage3-services.js.map +1 -0
- package/dist/core/generator/stages/stage4-api.d.ts +11 -0
- package/dist/core/generator/stages/stage4-api.d.ts.map +1 -0
- package/dist/core/generator/stages/stage4-api.js +65 -0
- package/dist/core/generator/stages/stage4-api.js.map +1 -0
- package/dist/core/generator/stages/stage5-architecture.d.ts +10 -0
- package/dist/core/generator/stages/stage5-architecture.d.ts.map +1 -0
- package/dist/core/generator/stages/stage5-architecture.js +62 -0
- package/dist/core/generator/stages/stage5-architecture.js.map +1 -0
- package/dist/core/generator/stages/stage6-adr.d.ts +8 -0
- package/dist/core/generator/stages/stage6-adr.d.ts.map +1 -0
- package/dist/core/generator/stages/stage6-adr.js +41 -0
- package/dist/core/generator/stages/stage6-adr.js.map +1 -0
- package/dist/core/services/chat-agent.d.ts +45 -0
- package/dist/core/services/chat-agent.d.ts.map +1 -0
- package/dist/core/services/chat-agent.js +310 -0
- package/dist/core/services/chat-agent.js.map +1 -0
- package/dist/core/services/chat-tools.d.ts +32 -0
- package/dist/core/services/chat-tools.d.ts.map +1 -0
- package/dist/core/services/chat-tools.js +270 -0
- package/dist/core/services/chat-tools.js.map +1 -0
- package/dist/core/services/config-manager.d.ts +61 -0
- package/dist/core/services/config-manager.d.ts.map +1 -0
- package/dist/core/services/config-manager.js +143 -0
- package/dist/core/services/config-manager.js.map +1 -0
- package/dist/core/services/gitignore-manager.d.ts +29 -0
- package/dist/core/services/gitignore-manager.d.ts.map +1 -0
- package/dist/core/services/gitignore-manager.js +106 -0
- package/dist/core/services/gitignore-manager.js.map +1 -0
- package/dist/core/services/index.d.ts +8 -0
- package/dist/core/services/index.d.ts.map +1 -0
- package/dist/core/services/index.js +8 -0
- package/dist/core/services/index.js.map +1 -0
- package/dist/core/services/llm-service.d.ts +336 -0
- package/dist/core/services/llm-service.d.ts.map +1 -0
- package/dist/core/services/llm-service.js +1155 -0
- package/dist/core/services/llm-service.js.map +1 -0
- package/dist/core/services/mcp-handlers/analysis.d.ts +42 -0
- package/dist/core/services/mcp-handlers/analysis.d.ts.map +1 -0
- package/dist/core/services/mcp-handlers/analysis.js +300 -0
- package/dist/core/services/mcp-handlers/analysis.js.map +1 -0
- package/dist/core/services/mcp-handlers/graph.d.ts +65 -0
- package/dist/core/services/mcp-handlers/graph.d.ts.map +1 -0
- package/dist/core/services/mcp-handlers/graph.js +509 -0
- package/dist/core/services/mcp-handlers/graph.js.map +1 -0
- package/dist/core/services/mcp-handlers/semantic.d.ts +38 -0
- package/dist/core/services/mcp-handlers/semantic.d.ts.map +1 -0
- package/dist/core/services/mcp-handlers/semantic.js +172 -0
- package/dist/core/services/mcp-handlers/semantic.js.map +1 -0
- package/dist/core/services/mcp-handlers/utils.d.ts +21 -0
- package/dist/core/services/mcp-handlers/utils.d.ts.map +1 -0
- package/dist/core/services/mcp-handlers/utils.js +62 -0
- package/dist/core/services/mcp-handlers/utils.js.map +1 -0
- package/dist/core/services/project-detector.d.ts +32 -0
- package/dist/core/services/project-detector.d.ts.map +1 -0
- package/dist/core/services/project-detector.js +111 -0
- package/dist/core/services/project-detector.js.map +1 -0
- package/dist/core/verifier/index.d.ts +5 -0
- package/dist/core/verifier/index.d.ts.map +1 -0
- package/dist/core/verifier/index.js +5 -0
- package/dist/core/verifier/index.js.map +1 -0
- package/dist/core/verifier/verification-engine.d.ts +226 -0
- package/dist/core/verifier/verification-engine.d.ts.map +1 -0
- package/dist/core/verifier/verification-engine.js +681 -0
- package/dist/core/verifier/verification-engine.js.map +1 -0
- package/dist/types/index.d.ts +252 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +5 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/pipeline.d.ts +148 -0
- package/dist/types/pipeline.d.ts.map +1 -0
- package/dist/types/pipeline.js +5 -0
- package/dist/types/pipeline.js.map +1 -0
- package/dist/utils/errors.d.ts +51 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +128 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/logger.d.ts +149 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +331 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/progress.d.ts +142 -0
- package/dist/utils/progress.d.ts.map +1 -0
- package/dist/utils/progress.js +280 -0
- package/dist/utils/progress.js.map +1 -0
- package/dist/utils/prompts.d.ts +53 -0
- package/dist/utils/prompts.d.ts.map +1 -0
- package/dist/utils/prompts.js +199 -0
- package/dist/utils/prompts.js.map +1 -0
- package/dist/utils/shutdown.d.ts +89 -0
- package/dist/utils/shutdown.d.ts.map +1 -0
- package/dist/utils/shutdown.js +237 -0
- package/dist/utils/shutdown.js.map +1 -0
- package/package.json +114 -0
- package/src/viewer/InteractiveGraphViewer.jsx +1486 -0
- package/src/viewer/app/index.html +17 -0
- package/src/viewer/app/main.jsx +13 -0
- package/src/viewer/components/ArchitectureView.jsx +177 -0
- package/src/viewer/components/ChatPanel.jsx +448 -0
- package/src/viewer/components/ClusterGraph.jsx +441 -0
- package/src/viewer/components/FilterBar.jsx +179 -0
- package/src/viewer/components/FlatGraph.jsx +275 -0
- package/src/viewer/components/MicroComponents.jsx +83 -0
- package/src/viewer/hooks/usePanZoom.js +79 -0
- package/src/viewer/utils/constants.js +47 -0
- package/src/viewer/utils/graph-helpers.js +291 -0
|
@@ -0,0 +1,597 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drift Detection Engine
|
|
3
|
+
*
|
|
4
|
+
* Compares code changes (from git) against existing specs (from spec-mapper)
|
|
5
|
+
* to detect gaps, stale specs, uncovered files, and orphaned specs.
|
|
6
|
+
*/
|
|
7
|
+
import { access } from 'node:fs/promises';
|
|
8
|
+
import { join, basename } from 'node:path';
|
|
9
|
+
import { matchFileToDomains, getSpecContent } from './spec-mapper.js';
|
|
10
|
+
import { getFileDiff } from './git-diff.js';
|
|
11
|
+
import logger from '../../utils/logger.js';
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// FILE RELEVANCE
|
|
14
|
+
// ============================================================================
|
|
15
|
+
/** High-value filename patterns that indicate spec-relevant source files */
|
|
16
|
+
const HIGH_VALUE_PATTERNS = [
|
|
17
|
+
/service/i, /controller/i, /handler/i, /middleware/i,
|
|
18
|
+
/model/i, /schema/i, /entity/i, /repository/i,
|
|
19
|
+
/route/i, /api/i, /auth/i, /database/i,
|
|
20
|
+
];
|
|
21
|
+
/**
|
|
22
|
+
* Determine if a changed file is relevant for spec drift detection.
|
|
23
|
+
* Filters out test files, generated files, lock files, assets, etc.
|
|
24
|
+
* @param openspecRelPath - relative path to the openspec directory (default: "openspec")
|
|
25
|
+
*/
|
|
26
|
+
export function isSpecRelevantChange(file, openspecRelPath = 'openspec') {
|
|
27
|
+
// Always skip tests and generated files
|
|
28
|
+
if (file.isTest || file.isGenerated)
|
|
29
|
+
return false;
|
|
30
|
+
// Skip openspec directory changes (those are spec updates, not drift)
|
|
31
|
+
// Normalize leading "./" from config paths to match git-reported paths
|
|
32
|
+
const normalizedSpecPath = openspecRelPath.replace(/^\.\//, '').replace(/\/$/, '');
|
|
33
|
+
const specPrefix = normalizedSpecPath + '/';
|
|
34
|
+
if (file.path.startsWith(specPrefix) || file.path.startsWith('.spec-gen/'))
|
|
35
|
+
return false;
|
|
36
|
+
// Skip markdown files: docs, changelogs, readmes, contributing guides, etc.
|
|
37
|
+
// Only source-embedded .md in non-root src directories could be spec-relevant.
|
|
38
|
+
if (file.extension === '.md') {
|
|
39
|
+
const fileName = basename(file.path).toLowerCase();
|
|
40
|
+
const dir = file.path.split('/')[0];
|
|
41
|
+
// Skip docs directories
|
|
42
|
+
if (dir === 'docs' || dir === 'doc')
|
|
43
|
+
return false;
|
|
44
|
+
// Skip common standalone markdown files
|
|
45
|
+
if (/^(readme|changelog|changes|history|contributing|license|authors|code.of.conduct|security|todo)/i.test(fileName)) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
// Skip root-level markdown (not inside src/)
|
|
49
|
+
if (!file.path.includes('/'))
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
// Skip static assets and lock files (already filtered by git-diff, but double-check)
|
|
53
|
+
const skipExtensions = new Set([
|
|
54
|
+
'.lock', '.lockb', '.map', '.min.js', '.min.css',
|
|
55
|
+
'.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico',
|
|
56
|
+
'.woff', '.woff2', '.ttf',
|
|
57
|
+
]);
|
|
58
|
+
if (skipExtensions.has(file.extension))
|
|
59
|
+
return false;
|
|
60
|
+
// Skip CI/CD configs
|
|
61
|
+
const fileName = basename(file.path);
|
|
62
|
+
if (fileName === '.github' || file.path.startsWith('.github/'))
|
|
63
|
+
return false;
|
|
64
|
+
if (fileName === '.gitlab-ci.yml' || fileName === 'Dockerfile')
|
|
65
|
+
return false;
|
|
66
|
+
// Config files: some are relevant (config.ts, settings.ts), some aren't
|
|
67
|
+
if (file.isConfig) {
|
|
68
|
+
// Manifest files with new dependencies can signal new capabilities
|
|
69
|
+
if (fileName === 'package.json')
|
|
70
|
+
return true;
|
|
71
|
+
// Build configs are not spec-relevant
|
|
72
|
+
if (/tsconfig|eslint|prettier|babel|jest|vitest|webpack|rollup|vite\.config/i.test(fileName)) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
// App config files are relevant
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Check if a file is a high-value source file (services, controllers, models, etc.)
|
|
82
|
+
*/
|
|
83
|
+
function isHighValueFile(filePath) {
|
|
84
|
+
const fileName = basename(filePath);
|
|
85
|
+
return HIGH_VALUE_PATTERNS.some(p => p.test(fileName));
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Compute severity based on issue kind and change magnitude
|
|
89
|
+
*/
|
|
90
|
+
export function computeSeverity(kind, file) {
|
|
91
|
+
const totalChanges = file.additions + file.deletions;
|
|
92
|
+
switch (kind) {
|
|
93
|
+
case 'gap':
|
|
94
|
+
if (totalChanges > 30 && isHighValueFile(file.path))
|
|
95
|
+
return 'error';
|
|
96
|
+
if (totalChanges > 5)
|
|
97
|
+
return 'warning';
|
|
98
|
+
return 'info';
|
|
99
|
+
case 'stale':
|
|
100
|
+
if (file.status === 'deleted')
|
|
101
|
+
return 'error';
|
|
102
|
+
return 'warning';
|
|
103
|
+
case 'uncovered':
|
|
104
|
+
if (isHighValueFile(file.path))
|
|
105
|
+
return 'warning';
|
|
106
|
+
return 'info';
|
|
107
|
+
case 'orphaned-spec':
|
|
108
|
+
return 'warning';
|
|
109
|
+
default:
|
|
110
|
+
return 'info';
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// ============================================================================
|
|
114
|
+
// DETECTION ALGORITHMS
|
|
115
|
+
// ============================================================================
|
|
116
|
+
/**
|
|
117
|
+
* Extract domain names whose specs were also changed in this changeset.
|
|
118
|
+
* Must be called on the FULL changedFiles list (before isSpecRelevantChange
|
|
119
|
+
* filtering), because spec files live under openspec/ which isSpecRelevantChange
|
|
120
|
+
* intentionally excludes from source-drift analysis.
|
|
121
|
+
*
|
|
122
|
+
* @param openspecRelPath - relative path prefix for spec files (default: "openspec")
|
|
123
|
+
*/
|
|
124
|
+
export function extractChangedSpecDomains(changedFiles, openspecRelPath = 'openspec') {
|
|
125
|
+
const domains = new Set();
|
|
126
|
+
// Normalize: strip leading "./" and trailing slash, escape for regex
|
|
127
|
+
const prefix = openspecRelPath.replace(/^\.\//, '').replace(/\/$/, '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
128
|
+
const pattern = new RegExp(`^${prefix}/specs/([^/]+)/`);
|
|
129
|
+
for (const file of changedFiles) {
|
|
130
|
+
const specMatch = file.path.match(pattern);
|
|
131
|
+
if (specMatch) {
|
|
132
|
+
domains.add(specMatch[1]);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return domains;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Detect gaps: code changed in a domain but spec wasn't updated.
|
|
139
|
+
* Pass pre-computed changedSpecDomains to avoid depending on openspec/ files
|
|
140
|
+
* being present in changedFiles (they are excluded by isSpecRelevantChange).
|
|
141
|
+
*/
|
|
142
|
+
export function detectGaps(changedFiles, specMap, changedSpecDomains, openspecRelPath) {
|
|
143
|
+
const issues = [];
|
|
144
|
+
// Use pre-computed set if available, otherwise extract from full list
|
|
145
|
+
const updatedSpecDomains = changedSpecDomains ?? extractChangedSpecDomains(changedFiles);
|
|
146
|
+
for (const file of changedFiles) {
|
|
147
|
+
if (!isSpecRelevantChange(file, openspecRelPath))
|
|
148
|
+
continue;
|
|
149
|
+
if (file.status === 'deleted')
|
|
150
|
+
continue; // Deletions handled by detectStaleSpecs
|
|
151
|
+
const domains = matchFileToDomains(file.path, specMap);
|
|
152
|
+
if (domains.length === 0)
|
|
153
|
+
continue; // No spec coverage — handled by detectUncoveredFiles
|
|
154
|
+
for (const domain of domains) {
|
|
155
|
+
// If the spec was also updated, no gap
|
|
156
|
+
if (updatedSpecDomains.has(domain))
|
|
157
|
+
continue;
|
|
158
|
+
const mapping = specMap.byDomain.get(domain);
|
|
159
|
+
const severity = computeSeverity('gap', file);
|
|
160
|
+
issues.push({
|
|
161
|
+
id: `gap:${file.path}:${domain}`,
|
|
162
|
+
kind: 'gap',
|
|
163
|
+
severity,
|
|
164
|
+
message: `File \`${file.path}\` changed (+${file.additions}/-${file.deletions} lines) but spec \`${mapping?.specPath ?? domain}\` was not updated`,
|
|
165
|
+
filePath: file.path,
|
|
166
|
+
domain,
|
|
167
|
+
specPath: mapping?.specPath ?? null,
|
|
168
|
+
changedLines: { added: file.additions, removed: file.deletions },
|
|
169
|
+
suggestion: `Review the ${domain} spec to ensure it still accurately describes the behavior in ${file.path}`,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return issues;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Detect stale specs: spec references files that were deleted or heavily modified
|
|
177
|
+
*/
|
|
178
|
+
export function detectStaleSpecs(changedFiles, specMap) {
|
|
179
|
+
const issues = [];
|
|
180
|
+
// Build a lookup of changed files by path
|
|
181
|
+
const changedByPath = new Map();
|
|
182
|
+
for (const file of changedFiles) {
|
|
183
|
+
changedByPath.set(file.path, file);
|
|
184
|
+
if (file.oldPath) {
|
|
185
|
+
changedByPath.set(file.oldPath, file);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
for (const [domain, mapping] of specMap.byDomain) {
|
|
189
|
+
for (const sourceFile of mapping.declaredSourceFiles) {
|
|
190
|
+
const changed = changedByPath.get(sourceFile);
|
|
191
|
+
if (!changed)
|
|
192
|
+
continue;
|
|
193
|
+
if (changed.status === 'deleted') {
|
|
194
|
+
issues.push({
|
|
195
|
+
id: `stale:${sourceFile}:${domain}`,
|
|
196
|
+
kind: 'stale',
|
|
197
|
+
severity: 'error',
|
|
198
|
+
message: `Spec \`${mapping.specPath}\` references deleted file \`${sourceFile}\``,
|
|
199
|
+
filePath: sourceFile,
|
|
200
|
+
domain,
|
|
201
|
+
specPath: mapping.specPath,
|
|
202
|
+
suggestion: `Update the ${domain} spec to remove references to ${sourceFile}`,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
else if (changed.status === 'renamed') {
|
|
206
|
+
issues.push({
|
|
207
|
+
id: `stale:${sourceFile}:${domain}`,
|
|
208
|
+
kind: 'stale',
|
|
209
|
+
severity: 'warning',
|
|
210
|
+
message: `Spec \`${mapping.specPath}\` references \`${sourceFile}\` which was renamed to \`${changed.path}\``,
|
|
211
|
+
filePath: sourceFile,
|
|
212
|
+
domain,
|
|
213
|
+
specPath: mapping.specPath,
|
|
214
|
+
suggestion: `Update the ${domain} spec to reference the new path \`${changed.path}\``,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return issues;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Detect uncovered files: new files that don't map to any spec domain
|
|
223
|
+
*/
|
|
224
|
+
export function detectUncoveredFiles(changedFiles, specMap, openspecRelPath) {
|
|
225
|
+
const issues = [];
|
|
226
|
+
for (const file of changedFiles) {
|
|
227
|
+
if (!isSpecRelevantChange(file, openspecRelPath))
|
|
228
|
+
continue;
|
|
229
|
+
if (file.status !== 'added')
|
|
230
|
+
continue;
|
|
231
|
+
const domains = matchFileToDomains(file.path, specMap);
|
|
232
|
+
if (domains.length > 0)
|
|
233
|
+
continue; // Already covered by a domain
|
|
234
|
+
const severity = computeSeverity('uncovered', file);
|
|
235
|
+
issues.push({
|
|
236
|
+
id: `uncovered:${file.path}`,
|
|
237
|
+
kind: 'uncovered',
|
|
238
|
+
severity,
|
|
239
|
+
message: `New file \`${file.path}\` has no matching spec domain`,
|
|
240
|
+
filePath: file.path,
|
|
241
|
+
domain: null,
|
|
242
|
+
specPath: null,
|
|
243
|
+
changedLines: { added: file.additions, removed: 0 },
|
|
244
|
+
suggestion: `Consider adding \`${file.path}\` to an existing spec domain or creating a new spec`,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
return issues;
|
|
248
|
+
}
|
|
249
|
+
/** Meta-domains that describe architecture/overview rather than owning source files */
|
|
250
|
+
const META_DOMAINS = new Set(['overview', 'architecture']);
|
|
251
|
+
/**
|
|
252
|
+
* Detect orphaned specs: spec declares source files that don't exist on disk.
|
|
253
|
+
* Skips meta-domains (overview, architecture) which often have generic paths.
|
|
254
|
+
*/
|
|
255
|
+
export async function detectOrphanedSpecs(specMap, rootPath) {
|
|
256
|
+
const issues = [];
|
|
257
|
+
for (const [domain, mapping] of specMap.byDomain) {
|
|
258
|
+
// Skip meta-domains — their "source files" are structural, not ownable
|
|
259
|
+
if (META_DOMAINS.has(domain))
|
|
260
|
+
continue;
|
|
261
|
+
for (const sourceFile of mapping.declaredSourceFiles) {
|
|
262
|
+
const fullPath = join(rootPath, sourceFile);
|
|
263
|
+
try {
|
|
264
|
+
await access(fullPath);
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
issues.push({
|
|
268
|
+
id: `orphaned-spec:${sourceFile}:${domain}`,
|
|
269
|
+
kind: 'orphaned-spec',
|
|
270
|
+
severity: 'warning',
|
|
271
|
+
message: `Spec \`${mapping.specPath}\` declares source file \`${sourceFile}\` which does not exist`,
|
|
272
|
+
filePath: sourceFile,
|
|
273
|
+
domain,
|
|
274
|
+
specPath: mapping.specPath,
|
|
275
|
+
suggestion: `Update the ${domain} spec to remove the reference to \`${sourceFile}\`, or restore the file`,
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return issues;
|
|
281
|
+
}
|
|
282
|
+
// ============================================================================
|
|
283
|
+
// ADR DRIFT DETECTION
|
|
284
|
+
// ============================================================================
|
|
285
|
+
/**
|
|
286
|
+
* Extract ADR IDs that were changed in this changeset.
|
|
287
|
+
* Matches files like: openspec/decisions/adr-0001-foo.md
|
|
288
|
+
*/
|
|
289
|
+
export function extractChangedADRIds(changedFiles, openspecRelPath = 'openspec') {
|
|
290
|
+
const ids = new Set();
|
|
291
|
+
const prefix = openspecRelPath.replace(/^\.\//, '').replace(/\/$/, '');
|
|
292
|
+
const pattern = new RegExp(`^${prefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}/decisions/adr-(\\d+)-`);
|
|
293
|
+
for (const file of changedFiles) {
|
|
294
|
+
const match = file.path.match(pattern);
|
|
295
|
+
if (match) {
|
|
296
|
+
ids.add(`ADR-${match[1].replace(/^0+/, '') || '0'}`);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return ids;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Detect ADR gaps: code changed in a domain referenced by an ADR,
|
|
303
|
+
* but the ADR wasn't updated. Reports once per ADR, not per file.
|
|
304
|
+
* Severity is always 'info' — most code changes don't invalidate decisions.
|
|
305
|
+
*/
|
|
306
|
+
export function detectADRGaps(changedFiles, adrMap, specMap, changedADRIds, openspecRelPath) {
|
|
307
|
+
const issues = [];
|
|
308
|
+
const reportedADRs = new Set();
|
|
309
|
+
// Collect all domains that have changed code
|
|
310
|
+
const changedDomains = new Set();
|
|
311
|
+
for (const file of changedFiles) {
|
|
312
|
+
if (!isSpecRelevantChange(file, openspecRelPath))
|
|
313
|
+
continue;
|
|
314
|
+
if (file.status === 'deleted')
|
|
315
|
+
continue;
|
|
316
|
+
const domains = matchFileToDomains(file.path, specMap);
|
|
317
|
+
for (const d of domains)
|
|
318
|
+
changedDomains.add(d);
|
|
319
|
+
}
|
|
320
|
+
// For each ADR, check if any of its related domains had code changes
|
|
321
|
+
for (const [id, mapping] of adrMap.byId) {
|
|
322
|
+
if (changedADRIds.has(id))
|
|
323
|
+
continue;
|
|
324
|
+
if (reportedADRs.has(id))
|
|
325
|
+
continue;
|
|
326
|
+
const affectedDomains = mapping.relatedDomains.filter(d => changedDomains.has(d));
|
|
327
|
+
if (affectedDomains.length === 0)
|
|
328
|
+
continue;
|
|
329
|
+
reportedADRs.add(id);
|
|
330
|
+
issues.push({
|
|
331
|
+
id: `adr-gap:${mapping.adrPath}:${id}`,
|
|
332
|
+
kind: 'adr-gap',
|
|
333
|
+
severity: 'info',
|
|
334
|
+
message: `Code changed in domain(s) ${affectedDomains.join(', ')} referenced by ${id}: "${mapping.title}"`,
|
|
335
|
+
filePath: mapping.adrPath,
|
|
336
|
+
domain: affectedDomains[0],
|
|
337
|
+
specPath: mapping.adrPath,
|
|
338
|
+
suggestion: `Review ${id} to ensure the decision still applies after changes to ${affectedDomains.join(', ')}`,
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
return issues;
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Detect orphaned ADRs: ADR references domains that no longer exist in specs.
|
|
345
|
+
*/
|
|
346
|
+
export function detectADROrphaned(adrMap, specMap) {
|
|
347
|
+
const issues = [];
|
|
348
|
+
for (const [id, mapping] of adrMap.byId) {
|
|
349
|
+
const orphanedDomains = mapping.relatedDomains.filter(d => !specMap.byDomain.has(d));
|
|
350
|
+
if (orphanedDomains.length === 0)
|
|
351
|
+
continue;
|
|
352
|
+
issues.push({
|
|
353
|
+
id: `adr-orphaned:${mapping.adrPath}:${id}`,
|
|
354
|
+
kind: 'adr-orphaned',
|
|
355
|
+
severity: 'info',
|
|
356
|
+
message: `${id}: "${mapping.title}" references domain(s) ${orphanedDomains.join(', ')} which no longer exist in specs`,
|
|
357
|
+
filePath: mapping.adrPath,
|
|
358
|
+
domain: null,
|
|
359
|
+
specPath: mapping.adrPath,
|
|
360
|
+
suggestion: `Review ${id} — the referenced domain(s) ${orphanedDomains.join(', ')} may have been removed or renamed`,
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
return issues;
|
|
364
|
+
}
|
|
365
|
+
// ============================================================================
|
|
366
|
+
// SEVERITY COMPARISON
|
|
367
|
+
// ============================================================================
|
|
368
|
+
const SEVERITY_RANK = {
|
|
369
|
+
error: 3,
|
|
370
|
+
warning: 2,
|
|
371
|
+
info: 1,
|
|
372
|
+
};
|
|
373
|
+
/**
|
|
374
|
+
* Check if an issue meets or exceeds the given severity threshold
|
|
375
|
+
*/
|
|
376
|
+
function meetsThreshold(issue, threshold) {
|
|
377
|
+
return SEVERITY_RANK[issue.severity] >= SEVERITY_RANK[threshold];
|
|
378
|
+
}
|
|
379
|
+
// ============================================================================
|
|
380
|
+
// LLM ENHANCEMENT
|
|
381
|
+
// ============================================================================
|
|
382
|
+
const LLM_SYSTEM_PROMPT = `You are a spec drift analyzer. Given a code diff and a specification, determine if the code change affects behavior described in the spec.
|
|
383
|
+
|
|
384
|
+
Respond with JSON only: { "relevant": boolean, "confidence": "high" | "medium" | "low", "reason": "one sentence explanation" }
|
|
385
|
+
|
|
386
|
+
- relevant=true: The change modifies, adds, or removes behavior covered by the spec
|
|
387
|
+
- relevant=false: The change is internal (refactoring, formatting, comments, test-only, perf optimization) and doesn't affect spec-described behavior
|
|
388
|
+
|
|
389
|
+
Be conservative: if unsure, mark as relevant.`;
|
|
390
|
+
/**
|
|
391
|
+
* Post-process gap issues using an LLM to determine if code changes
|
|
392
|
+
* actually affect spec-documented behavior. This reduces false positives
|
|
393
|
+
* by dismissing purely internal changes (refactoring, formatting, etc.).
|
|
394
|
+
*
|
|
395
|
+
* Only processes 'gap' issues. Other issue types (stale, uncovered, orphaned)
|
|
396
|
+
* are structural and don't benefit from semantic analysis.
|
|
397
|
+
*/
|
|
398
|
+
export async function enhanceGapsWithLLM(issues, options) {
|
|
399
|
+
const { llm, rootPath, specMap, baseRef, maxLlmCalls = 10 } = options;
|
|
400
|
+
const getDiff = options._getDiff ?? getFileDiff;
|
|
401
|
+
const getSpec = options._getSpec ?? getSpecContent;
|
|
402
|
+
// Separate gap issues from non-gap issues
|
|
403
|
+
const gapIssues = issues.filter(i => i.kind === 'gap');
|
|
404
|
+
const nonGapIssues = issues.filter(i => i.kind !== 'gap');
|
|
405
|
+
if (gapIssues.length === 0) {
|
|
406
|
+
return issues;
|
|
407
|
+
}
|
|
408
|
+
// Prioritize: errors first, then warnings, then info
|
|
409
|
+
const sortedGaps = [...gapIssues].sort((a, b) => SEVERITY_RANK[b.severity] - SEVERITY_RANK[a.severity]);
|
|
410
|
+
// Cap at maxLlmCalls
|
|
411
|
+
const toProcess = sortedGaps.slice(0, maxLlmCalls);
|
|
412
|
+
const skipped = sortedGaps.slice(maxLlmCalls);
|
|
413
|
+
const enhancedGaps = [];
|
|
414
|
+
for (const issue of toProcess) {
|
|
415
|
+
try {
|
|
416
|
+
// Get the code diff for this file
|
|
417
|
+
const diff = await getDiff(rootPath, issue.filePath, baseRef);
|
|
418
|
+
if (!diff) {
|
|
419
|
+
enhancedGaps.push(issue);
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
422
|
+
// Get the spec content for this domain
|
|
423
|
+
const specContent = issue.domain
|
|
424
|
+
? await getSpec(issue.domain, specMap, rootPath)
|
|
425
|
+
: null;
|
|
426
|
+
if (!specContent) {
|
|
427
|
+
enhancedGaps.push(issue);
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
// Ask the LLM
|
|
431
|
+
const userPrompt = `## Code Diff\n\`\`\`diff\n${diff}\n\`\`\`\n\n## Specification (${issue.domain})\n${specContent}`;
|
|
432
|
+
const response = await llm.complete({
|
|
433
|
+
systemPrompt: LLM_SYSTEM_PROMPT,
|
|
434
|
+
userPrompt,
|
|
435
|
+
temperature: 0.1,
|
|
436
|
+
maxTokens: 200,
|
|
437
|
+
responseFormat: 'json',
|
|
438
|
+
});
|
|
439
|
+
// Parse LLM response
|
|
440
|
+
const classification = parseLLMClassification(response.content);
|
|
441
|
+
if (classification) {
|
|
442
|
+
if (!classification.relevant && classification.confidence === 'high') {
|
|
443
|
+
// Downgrade to info — change is not spec-relevant
|
|
444
|
+
enhancedGaps.push({
|
|
445
|
+
...issue,
|
|
446
|
+
severity: 'info',
|
|
447
|
+
suggestion: `[LLM] Not spec-relevant: ${classification.reason}`,
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
else if (classification.relevant) {
|
|
451
|
+
// Keep severity, enrich suggestion with LLM reasoning
|
|
452
|
+
enhancedGaps.push({
|
|
453
|
+
...issue,
|
|
454
|
+
suggestion: `${issue.suggestion} [LLM: ${classification.reason}]`,
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
else {
|
|
458
|
+
// Low/medium confidence non-relevant — keep as-is but annotate
|
|
459
|
+
enhancedGaps.push({
|
|
460
|
+
...issue,
|
|
461
|
+
suggestion: `${issue.suggestion} [LLM (${classification.confidence} confidence): possibly not spec-relevant — ${classification.reason}]`,
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
else {
|
|
466
|
+
// Could not parse LLM response — keep issue unchanged
|
|
467
|
+
enhancedGaps.push(issue);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
catch (error) {
|
|
471
|
+
logger.debug(`LLM enhancement failed for ${issue.filePath}: ${error.message}`);
|
|
472
|
+
enhancedGaps.push(issue);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
// Recombine: non-gap issues + enhanced gaps + skipped gaps (unchanged)
|
|
476
|
+
return [...nonGapIssues, ...enhancedGaps, ...skipped];
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Parse the LLM's JSON response into a classification object.
|
|
480
|
+
* Handles markdown code blocks, extra whitespace, etc.
|
|
481
|
+
*/
|
|
482
|
+
function parseLLMClassification(content) {
|
|
483
|
+
try {
|
|
484
|
+
// Strip markdown code blocks if present
|
|
485
|
+
let jsonStr = content.trim();
|
|
486
|
+
const codeBlockMatch = jsonStr.match(/```(?:json)?\s*\n?([\s\S]*?)\n?\s*```/);
|
|
487
|
+
if (codeBlockMatch) {
|
|
488
|
+
jsonStr = codeBlockMatch[1].trim();
|
|
489
|
+
}
|
|
490
|
+
const parsed = JSON.parse(jsonStr);
|
|
491
|
+
// Validate expected shape
|
|
492
|
+
if (typeof parsed.relevant !== 'boolean')
|
|
493
|
+
return null;
|
|
494
|
+
if (!['high', 'medium', 'low'].includes(parsed.confidence)) {
|
|
495
|
+
parsed.confidence = 'medium'; // Default if invalid
|
|
496
|
+
}
|
|
497
|
+
if (typeof parsed.reason !== 'string') {
|
|
498
|
+
parsed.reason = 'No reason provided';
|
|
499
|
+
}
|
|
500
|
+
return {
|
|
501
|
+
relevant: parsed.relevant,
|
|
502
|
+
confidence: parsed.confidence,
|
|
503
|
+
reason: parsed.reason,
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
catch {
|
|
507
|
+
return null;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
// ============================================================================
|
|
511
|
+
// MAIN DETECTION
|
|
512
|
+
// ============================================================================
|
|
513
|
+
/**
|
|
514
|
+
* Run all drift detection algorithms and produce a combined result
|
|
515
|
+
*/
|
|
516
|
+
export async function detectDrift(options) {
|
|
517
|
+
const startTime = Date.now();
|
|
518
|
+
const { specMap, changedFiles, failOn, rootPath, domainFilter, openspecRelPath } = options;
|
|
519
|
+
// Pre-compute which spec domains were also updated in this changeset.
|
|
520
|
+
// This must happen before any filtering, since openspec/ files are excluded
|
|
521
|
+
// by isSpecRelevantChange but needed for gap detection.
|
|
522
|
+
const changedSpecDomains = extractChangedSpecDomains(changedFiles, openspecRelPath);
|
|
523
|
+
// Run all detection algorithms
|
|
524
|
+
const gaps = detectGaps(changedFiles, specMap, changedSpecDomains, openspecRelPath);
|
|
525
|
+
const stale = detectStaleSpecs(changedFiles, specMap);
|
|
526
|
+
const uncovered = detectUncoveredFiles(changedFiles, specMap, openspecRelPath);
|
|
527
|
+
const orphaned = await detectOrphanedSpecs(specMap, rootPath);
|
|
528
|
+
// ADR drift detection (when ADR map is provided)
|
|
529
|
+
let adrGaps = [];
|
|
530
|
+
let adrOrphanedIssues = [];
|
|
531
|
+
if (options.adrMap) {
|
|
532
|
+
const changedADRIds = extractChangedADRIds(changedFiles, openspecRelPath);
|
|
533
|
+
adrGaps = detectADRGaps(changedFiles, options.adrMap, specMap, changedADRIds, openspecRelPath);
|
|
534
|
+
adrOrphanedIssues = detectADROrphaned(options.adrMap, specMap);
|
|
535
|
+
}
|
|
536
|
+
// Combine all issues
|
|
537
|
+
let allIssues = [...gaps, ...stale, ...uncovered, ...orphaned, ...adrGaps, ...adrOrphanedIssues];
|
|
538
|
+
// Apply domain filter if provided — exclude null-domain issues too,
|
|
539
|
+
// since the user only wants results for the specified domains
|
|
540
|
+
if (domainFilter && domainFilter.length > 0) {
|
|
541
|
+
allIssues = allIssues.filter(issue => issue.domain !== null && domainFilter.includes(issue.domain));
|
|
542
|
+
}
|
|
543
|
+
// Deduplicate by id
|
|
544
|
+
const seen = new Set();
|
|
545
|
+
let dedupedIssues = [];
|
|
546
|
+
for (const issue of allIssues) {
|
|
547
|
+
if (!seen.has(issue.id)) {
|
|
548
|
+
seen.add(issue.id);
|
|
549
|
+
dedupedIssues.push(issue);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
// LLM enhancement: post-process gap issues to reduce false positives
|
|
553
|
+
if (options.llm && options.baseRef) {
|
|
554
|
+
dedupedIssues = await enhanceGapsWithLLM(dedupedIssues, {
|
|
555
|
+
llm: options.llm,
|
|
556
|
+
rootPath,
|
|
557
|
+
specMap,
|
|
558
|
+
baseRef: options.baseRef,
|
|
559
|
+
maxLlmCalls: options.maxLlmCalls,
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
// Sort by severity (error > warning > info), then domain, then file path
|
|
563
|
+
dedupedIssues.sort((a, b) => {
|
|
564
|
+
const severityDiff = SEVERITY_RANK[b.severity] - SEVERITY_RANK[a.severity];
|
|
565
|
+
if (severityDiff !== 0)
|
|
566
|
+
return severityDiff;
|
|
567
|
+
const domainDiff = (a.domain ?? '').localeCompare(b.domain ?? '');
|
|
568
|
+
if (domainDiff !== 0)
|
|
569
|
+
return domainDiff;
|
|
570
|
+
return a.filePath.localeCompare(b.filePath);
|
|
571
|
+
});
|
|
572
|
+
// Count spec-relevant changed files
|
|
573
|
+
const specRelevantFiles = changedFiles.filter(f => isSpecRelevantChange(f, openspecRelPath)).length;
|
|
574
|
+
// Determine if drift was detected based on failOn threshold
|
|
575
|
+
const hasDrift = dedupedIssues.some(issue => meetsThreshold(issue, failOn));
|
|
576
|
+
const duration = Date.now() - startTime;
|
|
577
|
+
return {
|
|
578
|
+
timestamp: new Date().toISOString(),
|
|
579
|
+
baseRef: '', // Will be filled by the caller
|
|
580
|
+
totalChangedFiles: changedFiles.length,
|
|
581
|
+
specRelevantFiles,
|
|
582
|
+
issues: dedupedIssues,
|
|
583
|
+
summary: {
|
|
584
|
+
gaps: dedupedIssues.filter(i => i.kind === 'gap').length,
|
|
585
|
+
stale: dedupedIssues.filter(i => i.kind === 'stale').length,
|
|
586
|
+
uncovered: dedupedIssues.filter(i => i.kind === 'uncovered').length,
|
|
587
|
+
orphanedSpecs: dedupedIssues.filter(i => i.kind === 'orphaned-spec').length,
|
|
588
|
+
adrGaps: dedupedIssues.filter(i => i.kind === 'adr-gap').length,
|
|
589
|
+
adrOrphaned: dedupedIssues.filter(i => i.kind === 'adr-orphaned').length,
|
|
590
|
+
total: dedupedIssues.length,
|
|
591
|
+
},
|
|
592
|
+
hasDrift,
|
|
593
|
+
duration,
|
|
594
|
+
mode: options.llm ? 'llm-enhanced' : 'static',
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
//# sourceMappingURL=drift-detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drift-detector.js","sourceRoot":"","sources":["../../../src/core/drift/drift-detector.ts"],"names":[],"mappings":"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"}
|