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,353 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spec-to-File Mapper
|
|
3
|
+
*
|
|
4
|
+
* Reads existing OpenSpec specs and builds a bidirectional mapping
|
|
5
|
+
* between spec domains and source files. This is the core data structure
|
|
6
|
+
* that enables drift detection.
|
|
7
|
+
*/
|
|
8
|
+
import { readFile, readdir } from 'node:fs/promises';
|
|
9
|
+
import { join, relative } from 'node:path';
|
|
10
|
+
import logger from '../../utils/logger.js';
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// SPEC PARSING
|
|
13
|
+
// ============================================================================
|
|
14
|
+
/**
|
|
15
|
+
* Extract source files from the spec header line:
|
|
16
|
+
* > Source files: src/foo.ts, src/bar.ts
|
|
17
|
+
*/
|
|
18
|
+
export function parseSpecHeader(content) {
|
|
19
|
+
const lines = content.split('\n');
|
|
20
|
+
const sourceFiles = [];
|
|
21
|
+
let generatedDate = null;
|
|
22
|
+
for (const line of lines) {
|
|
23
|
+
// Match "> Source files: ..." line
|
|
24
|
+
const sourceMatch = line.match(/^>\s*Source files?:\s*(.+)/i);
|
|
25
|
+
if (sourceMatch) {
|
|
26
|
+
const files = sourceMatch[1]
|
|
27
|
+
.split(',')
|
|
28
|
+
.map(f => f.trim().replace(/`/g, ''))
|
|
29
|
+
.filter(Boolean);
|
|
30
|
+
sourceFiles.push(...files);
|
|
31
|
+
}
|
|
32
|
+
// Match "> Generated by spec-gen ... on YYYY-MM-DD"
|
|
33
|
+
const dateMatch = line.match(/^>\s*Generated.*on\s+(\d{4}-\d{2}-\d{2})/i);
|
|
34
|
+
if (dateMatch) {
|
|
35
|
+
generatedDate = dateMatch[1];
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return { sourceFiles, generatedDate };
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Extract file references, requirements, and entities from spec body
|
|
42
|
+
*/
|
|
43
|
+
export function parseSpecReferences(content) {
|
|
44
|
+
const files = [];
|
|
45
|
+
const requirements = [];
|
|
46
|
+
const entities = [];
|
|
47
|
+
// Extract files from Technical Notes: **Implementation**: `file1`, `file2`
|
|
48
|
+
// Also handles multi-line formats where backtick-wrapped files continue on subsequent lines
|
|
49
|
+
const lines = content.split('\n');
|
|
50
|
+
let inImplBlock = false;
|
|
51
|
+
for (const line of lines) {
|
|
52
|
+
if (/\*\*Implementation\*\*:/.test(line)) {
|
|
53
|
+
inImplBlock = true;
|
|
54
|
+
// Extract backtick refs from this line
|
|
55
|
+
const fileRefs = line.match(/`([^`]+)`/g);
|
|
56
|
+
if (fileRefs) {
|
|
57
|
+
files.push(...fileRefs.filter(f => f !== '**Implementation**').map(f => f.replace(/`/g, '').trim()));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
else if (inImplBlock) {
|
|
61
|
+
// Continue capturing backtick refs from continuation lines (indented, list items, etc.)
|
|
62
|
+
if (line.match(/^\s*[-*]?\s*`[^`]+`/) || line.match(/^\s+`[^`]+`/)) {
|
|
63
|
+
const fileRefs = line.match(/`([^`]+)`/g);
|
|
64
|
+
if (fileRefs) {
|
|
65
|
+
files.push(...fileRefs.map(f => f.replace(/`/g, '').trim()));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
// Non-continuation line — stop scanning this Implementation block
|
|
70
|
+
inImplBlock = false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Extract requirement names: ### Requirement: Name
|
|
75
|
+
const reqMatches = content.matchAll(/^###\s+Requirement:\s*(.+)/gm);
|
|
76
|
+
for (const match of reqMatches) {
|
|
77
|
+
requirements.push(match[1].trim());
|
|
78
|
+
}
|
|
79
|
+
// Extract entity names from ## Entities section
|
|
80
|
+
// Look for ### headings that follow ## Entities
|
|
81
|
+
const entitySection = content.match(/## Entities\s*\n([\s\S]*?)(?=\n## |\n$)/);
|
|
82
|
+
if (entitySection) {
|
|
83
|
+
const entityMatches = entitySection[1].matchAll(/^###\s+(\w+)/gm);
|
|
84
|
+
for (const match of entityMatches) {
|
|
85
|
+
entities.push(match[1].trim());
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return { files, requirements, entities };
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Try to infer a domain for a file path based on directory structure.
|
|
92
|
+
* Uses word-boundary matching to avoid false positives (e.g., domain "auth"
|
|
93
|
+
* should match "auth-service.ts" but not "authorize.ts").
|
|
94
|
+
*/
|
|
95
|
+
export function inferDomainFromPath(filePath, knownDomains) {
|
|
96
|
+
const normalizedPath = filePath.toLowerCase();
|
|
97
|
+
const parts = normalizedPath.split('/');
|
|
98
|
+
// Sort domains by length (longest first) so more specific names match first
|
|
99
|
+
const sortedDomains = [...knownDomains].sort((a, b) => b.length - a.length);
|
|
100
|
+
for (const domain of sortedDomains) {
|
|
101
|
+
const normalizedDomain = domain.toLowerCase();
|
|
102
|
+
// Direct directory match: src/auth/... → auth
|
|
103
|
+
if (parts.some(p => p === normalizedDomain)) {
|
|
104
|
+
return domain;
|
|
105
|
+
}
|
|
106
|
+
// Word-boundary prefix match on filename: "auth-handler" or "auth_service" → auth
|
|
107
|
+
// Requires the domain name to be followed by a separator (-, _, .) or end of string.
|
|
108
|
+
// This prevents "auth" from matching "authorize" or "authentication".
|
|
109
|
+
const fileName = parts[parts.length - 1].replace(/\.[^.]+$/, '');
|
|
110
|
+
if (fileName === normalizedDomain) {
|
|
111
|
+
return domain;
|
|
112
|
+
}
|
|
113
|
+
// Check for word-boundary after domain name: auth-service, auth_handler, auth.utils
|
|
114
|
+
const afterDomain = fileName.slice(normalizedDomain.length);
|
|
115
|
+
if (fileName.startsWith(normalizedDomain) && afterDomain.length > 0 && /^[-_.]/.test(afterDomain)) {
|
|
116
|
+
return domain;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
// ============================================================================
|
|
122
|
+
// SPEC MAP BUILDER
|
|
123
|
+
// ============================================================================
|
|
124
|
+
/**
|
|
125
|
+
* Build a bidirectional map between spec domains and source files
|
|
126
|
+
*/
|
|
127
|
+
export async function buildSpecMap(options) {
|
|
128
|
+
const { rootPath, openspecPath } = options;
|
|
129
|
+
const specsDir = join(openspecPath, 'specs');
|
|
130
|
+
const byDomain = new Map();
|
|
131
|
+
const byFile = new Map();
|
|
132
|
+
// Read all spec domain directories
|
|
133
|
+
let entries;
|
|
134
|
+
try {
|
|
135
|
+
entries = await readdir(specsDir, { withFileTypes: true });
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
logger.debug('No specs directory found');
|
|
139
|
+
return { byDomain, byFile, domainCount: 0, totalMappedFiles: 0 };
|
|
140
|
+
}
|
|
141
|
+
// Skip meta-directories that aren't domain specs (overview/architecture are
|
|
142
|
+
// structural docs, not source-file-owning domains). Note: 'api' is NOT skipped
|
|
143
|
+
// because many projects legitimately have an api domain with source files.
|
|
144
|
+
const skipDomains = new Set(['overview', 'architecture']);
|
|
145
|
+
for (const entry of entries) {
|
|
146
|
+
if (!entry.isDirectory())
|
|
147
|
+
continue;
|
|
148
|
+
const domain = String(entry.name);
|
|
149
|
+
const specPath = join(specsDir, domain, 'spec.md');
|
|
150
|
+
const relativeSpecPath = relative(rootPath, specPath);
|
|
151
|
+
let content;
|
|
152
|
+
try {
|
|
153
|
+
content = await readFile(specPath, 'utf-8');
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
logger.debug(`Could not read spec: ${specPath}`);
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
// Parse the spec for source files
|
|
160
|
+
const header = parseSpecHeader(content);
|
|
161
|
+
const refs = parseSpecReferences(content);
|
|
162
|
+
// Combine declared (header) and inferred (body) source files, deduplicated
|
|
163
|
+
const declaredSourceFiles = header.sourceFiles;
|
|
164
|
+
const inferredSourceFiles = refs.files.filter(f => !declaredSourceFiles.includes(f));
|
|
165
|
+
const allSourceFiles = [...new Set([...declaredSourceFiles, ...inferredSourceFiles])];
|
|
166
|
+
const mapping = {
|
|
167
|
+
domain,
|
|
168
|
+
specPath: relativeSpecPath,
|
|
169
|
+
declaredSourceFiles,
|
|
170
|
+
inferredSourceFiles,
|
|
171
|
+
allSourceFiles,
|
|
172
|
+
requirements: refs.requirements,
|
|
173
|
+
entities: refs.entities,
|
|
174
|
+
};
|
|
175
|
+
byDomain.set(domain, mapping);
|
|
176
|
+
// Build reverse map (skip meta-domains for file mapping)
|
|
177
|
+
if (!skipDomains.has(domain)) {
|
|
178
|
+
for (const file of allSourceFiles) {
|
|
179
|
+
const existing = byFile.get(file) ?? [];
|
|
180
|
+
if (!existing.includes(domain)) {
|
|
181
|
+
existing.push(domain);
|
|
182
|
+
byFile.set(file, existing);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// If repo-structure.json exists, enhance mapping with directory-based inference
|
|
188
|
+
if (options.repoStructurePath) {
|
|
189
|
+
try {
|
|
190
|
+
const repoContent = await readFile(options.repoStructurePath, 'utf-8');
|
|
191
|
+
const repoStructure = JSON.parse(repoContent);
|
|
192
|
+
if (repoStructure.domains && Array.isArray(repoStructure.domains)) {
|
|
193
|
+
for (const rsDomain of repoStructure.domains) {
|
|
194
|
+
const domainName = rsDomain.name?.toLowerCase();
|
|
195
|
+
if (!domainName || skipDomains.has(domainName))
|
|
196
|
+
continue;
|
|
197
|
+
const mapping = byDomain.get(domainName);
|
|
198
|
+
if (mapping && rsDomain.files) {
|
|
199
|
+
for (const file of rsDomain.files) {
|
|
200
|
+
if (!mapping.allSourceFiles.includes(file)) {
|
|
201
|
+
mapping.inferredSourceFiles.push(file);
|
|
202
|
+
mapping.allSourceFiles.push(file);
|
|
203
|
+
}
|
|
204
|
+
const existing = byFile.get(file) ?? [];
|
|
205
|
+
if (!existing.includes(domainName)) {
|
|
206
|
+
existing.push(domainName);
|
|
207
|
+
byFile.set(file, existing);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
logger.debug('Could not load repo-structure.json for enhanced mapping');
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
// Count total unique mapped files
|
|
219
|
+
const totalMappedFiles = byFile.size;
|
|
220
|
+
return {
|
|
221
|
+
byDomain,
|
|
222
|
+
byFile,
|
|
223
|
+
domainCount: byDomain.size,
|
|
224
|
+
totalMappedFiles,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Load spec content for a domain, reading the spec file from disk.
|
|
229
|
+
* Returns the markdown content truncated to maxChars, or null if unavailable.
|
|
230
|
+
*/
|
|
231
|
+
export async function getSpecContent(domain, specMap, rootPath, maxChars = 4000) {
|
|
232
|
+
const mapping = specMap.byDomain.get(domain);
|
|
233
|
+
if (!mapping)
|
|
234
|
+
return null;
|
|
235
|
+
try {
|
|
236
|
+
const fullPath = join(rootPath, mapping.specPath);
|
|
237
|
+
const content = await readFile(fullPath, 'utf-8');
|
|
238
|
+
return content.length > maxChars
|
|
239
|
+
? content.slice(0, maxChars) + '\n... (truncated)'
|
|
240
|
+
: content;
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
logger.debug(`Could not read spec for domain: ${domain}`);
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Find which domains a file maps to, using direct lookup + directory inference
|
|
249
|
+
*/
|
|
250
|
+
export function matchFileToDomains(filePath, specMap) {
|
|
251
|
+
// Direct lookup
|
|
252
|
+
const direct = specMap.byFile.get(filePath);
|
|
253
|
+
if (direct && direct.length > 0) {
|
|
254
|
+
return direct;
|
|
255
|
+
}
|
|
256
|
+
// Directory inference: try matching against known domain names
|
|
257
|
+
const knownDomains = [...specMap.byDomain.keys()];
|
|
258
|
+
const inferred = inferDomainFromPath(filePath, knownDomains);
|
|
259
|
+
if (inferred) {
|
|
260
|
+
return [inferred];
|
|
261
|
+
}
|
|
262
|
+
return [];
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Parse the ## Related section from an ADR file to extract domains and layers.
|
|
266
|
+
*/
|
|
267
|
+
export function parseADRRelated(content) {
|
|
268
|
+
const domains = [];
|
|
269
|
+
const layers = [];
|
|
270
|
+
const domainsMatch = content.match(/\*\*Domains\*\*:\s*(.+)/);
|
|
271
|
+
if (domainsMatch) {
|
|
272
|
+
domains.push(...domainsMatch[1].split(',').map(d => d.trim()).filter(Boolean));
|
|
273
|
+
}
|
|
274
|
+
const layersMatch = content.match(/\*\*Layers\*\*:\s*(.+)/);
|
|
275
|
+
if (layersMatch) {
|
|
276
|
+
layers.push(...layersMatch[1].split(',').map(l => l.trim()).filter(Boolean));
|
|
277
|
+
}
|
|
278
|
+
return { domains, layers };
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Parse ADR title and status from content.
|
|
282
|
+
*/
|
|
283
|
+
function parseADRHeader(content) {
|
|
284
|
+
let id = '';
|
|
285
|
+
let title = '';
|
|
286
|
+
let status = 'accepted';
|
|
287
|
+
// Parse title: # ADR-001: Some Title
|
|
288
|
+
const titleMatch = content.match(/^#\s+(ADR-\d+):\s*(.+)/m);
|
|
289
|
+
if (titleMatch) {
|
|
290
|
+
id = titleMatch[1];
|
|
291
|
+
title = titleMatch[2].trim();
|
|
292
|
+
}
|
|
293
|
+
// Parse status from ## Status section
|
|
294
|
+
const statusMatch = content.match(/## Status\s+(\w+)/);
|
|
295
|
+
if (statusMatch) {
|
|
296
|
+
status = statusMatch[1].toLowerCase();
|
|
297
|
+
}
|
|
298
|
+
return { id, title, status };
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Build an ADR map by reading all ADR files from the decisions directory.
|
|
302
|
+
* Returns null if no decisions directory exists.
|
|
303
|
+
*/
|
|
304
|
+
export async function buildADRMap(options) {
|
|
305
|
+
const decisionsDir = join(options.openspecPath, 'decisions');
|
|
306
|
+
const byId = new Map();
|
|
307
|
+
const byDomain = new Map();
|
|
308
|
+
let entries;
|
|
309
|
+
try {
|
|
310
|
+
entries = await readdir(decisionsDir);
|
|
311
|
+
}
|
|
312
|
+
catch {
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
const adrFiles = entries.filter(f => f.startsWith('adr-') && f.endsWith('.md'));
|
|
316
|
+
if (adrFiles.length === 0)
|
|
317
|
+
return null;
|
|
318
|
+
for (const fileName of adrFiles) {
|
|
319
|
+
try {
|
|
320
|
+
const filePath = join(decisionsDir, fileName);
|
|
321
|
+
const content = await readFile(filePath, 'utf-8');
|
|
322
|
+
const { id, title, status } = parseADRHeader(content);
|
|
323
|
+
if (!id)
|
|
324
|
+
continue;
|
|
325
|
+
// Skip superseded/deprecated ADRs
|
|
326
|
+
if (status === 'superseded' || status === 'deprecated')
|
|
327
|
+
continue;
|
|
328
|
+
const { domains, layers } = parseADRRelated(content);
|
|
329
|
+
const adrPath = relative(options.rootPath, filePath);
|
|
330
|
+
const mapping = {
|
|
331
|
+
id,
|
|
332
|
+
title,
|
|
333
|
+
adrPath,
|
|
334
|
+
relatedDomains: domains,
|
|
335
|
+
relatedLayers: layers,
|
|
336
|
+
status,
|
|
337
|
+
};
|
|
338
|
+
byId.set(id, mapping);
|
|
339
|
+
for (const domain of domains) {
|
|
340
|
+
const existing = byDomain.get(domain) ?? [];
|
|
341
|
+
existing.push(id);
|
|
342
|
+
byDomain.set(domain, existing);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
catch {
|
|
346
|
+
logger.debug(`Could not read ADR file: ${fileName}`);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
if (byId.size === 0)
|
|
350
|
+
return null;
|
|
351
|
+
return { byId, byDomain };
|
|
352
|
+
}
|
|
353
|
+
//# sourceMappingURL=spec-mapper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec-mapper.js","sourceRoot":"","sources":["../../../src/core/drift/spec-mapper.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ADR Generator
|
|
3
|
+
*
|
|
4
|
+
* Converts enriched Architecture Decision Records from the LLM pipeline
|
|
5
|
+
* into formatted markdown files following the standard ADR template.
|
|
6
|
+
*/
|
|
7
|
+
import type { PipelineResult } from './spec-pipeline.js';
|
|
8
|
+
import type { GeneratedSpec } from './openspec-format-generator.js';
|
|
9
|
+
export interface ADRGeneratorOptions {
|
|
10
|
+
/** Version string for headers */
|
|
11
|
+
version?: string;
|
|
12
|
+
/** Include Mermaid architecture diagrams */
|
|
13
|
+
includeMermaid?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare class ADRGenerator {
|
|
16
|
+
private options;
|
|
17
|
+
constructor(options?: ADRGeneratorOptions);
|
|
18
|
+
/**
|
|
19
|
+
* Generate all ADR specs from pipeline results.
|
|
20
|
+
* Returns individual ADR files plus an index file.
|
|
21
|
+
*/
|
|
22
|
+
generateADRs(result: PipelineResult): GeneratedSpec[];
|
|
23
|
+
/**
|
|
24
|
+
* Generate a single ADR markdown file.
|
|
25
|
+
*/
|
|
26
|
+
private generateSingleADR;
|
|
27
|
+
/**
|
|
28
|
+
* Generate the ADR index file with a table of all decisions.
|
|
29
|
+
*/
|
|
30
|
+
private generateADRIndex;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=adr-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adr-generator.d.ts","sourceRoot":"","sources":["../../../src/core/generator/adr-generator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAsC,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAC7F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAMpE,MAAM,WAAW,mBAAmB;IAClC,iCAAiC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAsBD,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAgC;gBAEnC,OAAO,GAAE,mBAAwB;IAO7C;;;OAGG;IACH,YAAY,CAAC,MAAM,EAAE,cAAc,GAAG,aAAa,EAAE;IAcrD;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAmHzB;;OAEG;IACH,OAAO,CAAC,gBAAgB;CA2CzB"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ADR Generator
|
|
3
|
+
*
|
|
4
|
+
* Converts enriched Architecture Decision Records from the LLM pipeline
|
|
5
|
+
* into formatted markdown files following the standard ADR template.
|
|
6
|
+
*/
|
|
7
|
+
// ============================================================================
|
|
8
|
+
// HELPERS
|
|
9
|
+
// ============================================================================
|
|
10
|
+
/**
|
|
11
|
+
* Convert a title to a kebab-case slug for file naming.
|
|
12
|
+
*/
|
|
13
|
+
function titleToSlug(title) {
|
|
14
|
+
return title
|
|
15
|
+
.toLowerCase()
|
|
16
|
+
.replace(/[^a-z0-9\s-]/g, '')
|
|
17
|
+
.replace(/\s+/g, '-')
|
|
18
|
+
.replace(/-+/g, '-')
|
|
19
|
+
.replace(/^-|-$/g, '');
|
|
20
|
+
}
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// ADR GENERATOR
|
|
23
|
+
// ============================================================================
|
|
24
|
+
export class ADRGenerator {
|
|
25
|
+
options;
|
|
26
|
+
constructor(options = {}) {
|
|
27
|
+
this.options = {
|
|
28
|
+
version: options.version ?? '1.0.0',
|
|
29
|
+
includeMermaid: options.includeMermaid ?? true,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Generate all ADR specs from pipeline results.
|
|
34
|
+
* Returns individual ADR files plus an index file.
|
|
35
|
+
*/
|
|
36
|
+
generateADRs(result) {
|
|
37
|
+
if (!result.adrs || result.adrs.length === 0)
|
|
38
|
+
return [];
|
|
39
|
+
const specs = [];
|
|
40
|
+
for (const adr of result.adrs) {
|
|
41
|
+
specs.push(this.generateSingleADR(adr, result.architecture));
|
|
42
|
+
}
|
|
43
|
+
specs.push(this.generateADRIndex(result.adrs));
|
|
44
|
+
return specs;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Generate a single ADR markdown file.
|
|
48
|
+
*/
|
|
49
|
+
generateSingleADR(adr, architecture) {
|
|
50
|
+
const lines = [];
|
|
51
|
+
const date = new Date().toISOString().split('T')[0];
|
|
52
|
+
const slug = titleToSlug(adr.title);
|
|
53
|
+
const adrNum = adr.id.replace(/[^0-9]/g, '').padStart(4, '0');
|
|
54
|
+
// Defensive defaults for arrays that the LLM may omit from its response
|
|
55
|
+
const consequences = adr.consequences ?? [];
|
|
56
|
+
const alternatives = adr.alternatives ?? [];
|
|
57
|
+
const relatedLayers = adr.relatedLayers ?? [];
|
|
58
|
+
const relatedDomains = adr.relatedDomains ?? [];
|
|
59
|
+
// Header
|
|
60
|
+
lines.push(`# ${adr.id}: ${adr.title}`);
|
|
61
|
+
lines.push('');
|
|
62
|
+
lines.push(`> Generated by spec-gen v${this.options.version} on ${date}`);
|
|
63
|
+
lines.push('');
|
|
64
|
+
// Status
|
|
65
|
+
lines.push('## Status');
|
|
66
|
+
lines.push('');
|
|
67
|
+
lines.push(capitalize(adr.status));
|
|
68
|
+
lines.push('');
|
|
69
|
+
// Context
|
|
70
|
+
lines.push('## Context');
|
|
71
|
+
lines.push('');
|
|
72
|
+
lines.push(adr.context);
|
|
73
|
+
lines.push('');
|
|
74
|
+
// Decision
|
|
75
|
+
lines.push('## Decision');
|
|
76
|
+
lines.push('');
|
|
77
|
+
lines.push(adr.decision);
|
|
78
|
+
lines.push('');
|
|
79
|
+
// Consequences
|
|
80
|
+
lines.push('## Consequences');
|
|
81
|
+
lines.push('');
|
|
82
|
+
if (consequences.length > 0) {
|
|
83
|
+
for (const consequence of consequences) {
|
|
84
|
+
lines.push(`- ${consequence}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
lines.push('No consequences identified.');
|
|
89
|
+
}
|
|
90
|
+
lines.push('');
|
|
91
|
+
// Alternatives Considered
|
|
92
|
+
if (alternatives.length > 0) {
|
|
93
|
+
lines.push('## Alternatives Considered');
|
|
94
|
+
lines.push('');
|
|
95
|
+
for (const alt of alternatives) {
|
|
96
|
+
lines.push(`- ${alt}`);
|
|
97
|
+
}
|
|
98
|
+
lines.push('');
|
|
99
|
+
}
|
|
100
|
+
// Architecture Impact (Mermaid diagram)
|
|
101
|
+
if (this.options.includeMermaid && relatedLayers.length > 0 && architecture.layerMap.length > 0) {
|
|
102
|
+
lines.push('## Architecture Impact');
|
|
103
|
+
lines.push('');
|
|
104
|
+
lines.push('```mermaid');
|
|
105
|
+
lines.push('graph TB');
|
|
106
|
+
// Filter to affected layers
|
|
107
|
+
const affectedLayers = architecture.layerMap.filter(l => relatedLayers.some(rl => l.name.toLowerCase().includes(rl.toLowerCase())));
|
|
108
|
+
// Fall back to all layers if filter matched nothing
|
|
109
|
+
const layers = affectedLayers.length > 0 ? affectedLayers : architecture.layerMap;
|
|
110
|
+
for (let i = 0; i < layers.length; i++) {
|
|
111
|
+
const layer = layers[i];
|
|
112
|
+
const layerId = layer.name.replace(/\s+/g, '');
|
|
113
|
+
const isAffected = affectedLayers.length > 0;
|
|
114
|
+
lines.push(` ${layerId}["${layer.name}"]`);
|
|
115
|
+
if (i < layers.length - 1) {
|
|
116
|
+
const nextLayerId = layers[i + 1].name.replace(/\s+/g, '');
|
|
117
|
+
lines.push(` ${layerId} --> ${nextLayerId}`);
|
|
118
|
+
}
|
|
119
|
+
if (isAffected) {
|
|
120
|
+
lines.push(` style ${layerId} fill:#f9f,stroke:#333`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
lines.push('```');
|
|
124
|
+
lines.push('');
|
|
125
|
+
}
|
|
126
|
+
// Related
|
|
127
|
+
lines.push('## Related');
|
|
128
|
+
lines.push('');
|
|
129
|
+
if (relatedLayers.length > 0) {
|
|
130
|
+
lines.push(`- **Layers**: ${relatedLayers.join(', ')}`);
|
|
131
|
+
}
|
|
132
|
+
if (relatedDomains.length > 0) {
|
|
133
|
+
lines.push(`- **Domains**: ${relatedDomains.join(', ')}`);
|
|
134
|
+
}
|
|
135
|
+
if (relatedLayers.length === 0 && relatedDomains.length === 0) {
|
|
136
|
+
lines.push('No specific layers or domains identified.');
|
|
137
|
+
}
|
|
138
|
+
lines.push('');
|
|
139
|
+
return {
|
|
140
|
+
path: `openspec/decisions/adr-${adrNum}-${slug}.md`,
|
|
141
|
+
content: lines.join('\n'),
|
|
142
|
+
domain: 'decisions',
|
|
143
|
+
type: 'adr',
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Generate the ADR index file with a table of all decisions.
|
|
148
|
+
*/
|
|
149
|
+
generateADRIndex(adrs) {
|
|
150
|
+
const lines = [];
|
|
151
|
+
const date = new Date().toISOString().split('T')[0];
|
|
152
|
+
lines.push('# Architecture Decision Records');
|
|
153
|
+
lines.push('');
|
|
154
|
+
lines.push(`> Generated by spec-gen v${this.options.version} on ${date}`);
|
|
155
|
+
lines.push('');
|
|
156
|
+
lines.push('This directory contains Architecture Decision Records (ADRs) that document');
|
|
157
|
+
lines.push('the key architectural decisions observed in this codebase.');
|
|
158
|
+
lines.push('');
|
|
159
|
+
lines.push('## Decisions');
|
|
160
|
+
lines.push('');
|
|
161
|
+
lines.push('| ID | Decision | Status | Layers |');
|
|
162
|
+
lines.push('|----|----------|--------|--------|');
|
|
163
|
+
for (const adr of adrs) {
|
|
164
|
+
const adrNum = adr.id.replace(/[^0-9]/g, '').padStart(4, '0');
|
|
165
|
+
const slug = titleToSlug(adr.title);
|
|
166
|
+
const fileName = `adr-${adrNum}-${slug}.md`;
|
|
167
|
+
const layers = (adr.relatedLayers ?? []).join(', ') || '-';
|
|
168
|
+
lines.push(`| [${adr.id}](./${fileName}) | ${adr.title} | ${capitalize(adr.status)} | ${layers} |`);
|
|
169
|
+
}
|
|
170
|
+
lines.push('');
|
|
171
|
+
lines.push('## About ADRs');
|
|
172
|
+
lines.push('');
|
|
173
|
+
lines.push('Architecture Decision Records capture the *why* behind architectural choices.');
|
|
174
|
+
lines.push('Each ADR documents the context, decision, and consequences of a significant');
|
|
175
|
+
lines.push('technical decision. Unlike specifications (which describe *what* the system does),');
|
|
176
|
+
lines.push('ADRs explain *why* it was built that way.');
|
|
177
|
+
lines.push('');
|
|
178
|
+
lines.push('ADRs are **immutable records**. When a decision is reversed, the original ADR');
|
|
179
|
+
lines.push('is marked as "Superseded" and a new ADR is created referencing it.');
|
|
180
|
+
lines.push('');
|
|
181
|
+
return {
|
|
182
|
+
path: 'openspec/decisions/index.md',
|
|
183
|
+
content: lines.join('\n'),
|
|
184
|
+
domain: 'decisions',
|
|
185
|
+
type: 'adr',
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
function capitalize(s) {
|
|
190
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=adr-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adr-generator.js","sourceRoot":"","sources":["../../../src/core/generator/adr-generator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAgBH,+EAA+E;AAC/E,UAAU;AACV,+EAA+E;AAE/E;;GAEG;AACH,SAAS,WAAW,CAAC,KAAa;IAChC,OAAO,KAAK;SACT,WAAW,EAAE;SACb,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC;SAC5B,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AAC3B,CAAC;AAED,+EAA+E;AAC/E,gBAAgB;AAChB,+EAA+E;AAE/E,MAAM,OAAO,YAAY;IACf,OAAO,CAAgC;IAE/C,YAAY,UAA+B,EAAE;QAC3C,IAAI,CAAC,OAAO,GAAG;YACb,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,OAAO;YACnC,cAAc,EAAE,OAAO,CAAC,cAAc,IAAI,IAAI;SAC/C,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,YAAY,CAAC,MAAsB;QACjC,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAExD,MAAM,KAAK,GAAoB,EAAE,CAAC;QAElC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;YAC9B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,GAAG,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;QAC/D,CAAC;QAED,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;QAE/C,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;OAEG;IACK,iBAAiB,CAAC,GAAgB,EAAE,YAAmC;QAC7E,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACpD,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACpC,MAAM,MAAM,GAAG,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAE9D,wEAAwE;QACxE,MAAM,YAAY,GAAG,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC;QAC5C,MAAM,YAAY,GAAG,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC;QAC5C,MAAM,aAAa,GAAG,GAAG,CAAC,aAAa,IAAI,EAAE,CAAC;QAC9C,MAAM,cAAc,GAAG,GAAG,CAAC,cAAc,IAAI,EAAE,CAAC;QAEhD,SAAS;QACT,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;QACxC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,4BAA4B,IAAI,CAAC,OAAO,CAAC,OAAO,OAAO,IAAI,EAAE,CAAC,CAAC;QAC1E,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAEf,SAAS;QACT,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACxB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;QACnC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAEf,UAAU;QACV,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACzB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACxB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAEf,WAAW;QACX,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC1B,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACzB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAEf,eAAe;QACf,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAC9B,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5B,KAAK,MAAM,WAAW,IAAI,YAAY,EAAE,CAAC;gBACvC,KAAK,CAAC,IAAI,CAAC,KAAK,WAAW,EAAE,CAAC,CAAC;YACjC,CAAC;QACH,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;QAC5C,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAEf,0BAA0B;QAC1B,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5B,KAAK,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;YACzC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACf,KAAK,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;gBAC/B,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC;YACzB,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACjB,CAAC;QAED,wCAAwC;QACxC,IAAI,IAAI,CAAC,OAAO,CAAC,cAAc,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,IAAI,YAAY,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAChG,KAAK,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;YACrC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACf,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACzB,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAEvB,4BAA4B;YAC5B,MAAM,cAAc,GAAG,YAAY,CAAC,QAAQ,CAAC,MAAM,CACjD,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,CAC/E,CAAC;YAEF,oDAAoD;YACpD,MAAM,MAAM,GAAG,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC;YAElF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACvC,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;gBACxB,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;gBAC/C,MAAM,UAAU,GAAG,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC;gBAC7C,KAAK,CAAC,IAAI,CAAC,OAAO,OAAO,KAAK,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC;gBAE9C,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAC1B,MAAM,WAAW,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;oBAC3D,KAAK,CAAC,IAAI,CAAC,OAAO,OAAO,QAAQ,WAAW,EAAE,CAAC,CAAC;gBAClD,CAAC;gBAED,IAAI,UAAU,EAAE,CAAC;oBACf,KAAK,CAAC,IAAI,CAAC,aAAa,OAAO,wBAAwB,CAAC,CAAC;gBAC3D,CAAC;YACH,CAAC;YAED,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAClB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACjB,CAAC;QAED,UAAU;QACV,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACzB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7B,KAAK,CAAC,IAAI,CAAC,iBAAiB,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC1D,CAAC;QACD,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9B,KAAK,CAAC,IAAI,CAAC,kBAAkB,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC5D,CAAC;QACD,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9D,KAAK,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAC;QAC1D,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAEf,OAAO;YACL,IAAI,EAAE,0BAA0B,MAAM,IAAI,IAAI,KAAK;YACnD,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;YACzB,MAAM,EAAE,WAAW;YACnB,IAAI,EAAE,KAAK;SACZ,CAAC;IACJ,CAAC;IAED;;OAEG;IACK,gBAAgB,CAAC,IAAmB;QAC1C,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAEpD,KAAK,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;QAC9C,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,4BAA4B,IAAI,CAAC,OAAO,CAAC,OAAO,OAAO,IAAI,EAAE,CAAC,CAAC;QAC1E,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,4EAA4E,CAAC,CAAC;QACzF,KAAK,CAAC,IAAI,CAAC,4DAA4D,CAAC,CAAC;QACzE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC3B,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;QAClD,KAAK,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;QAElD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,MAAM,GAAG,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;YAC9D,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACpC,MAAM,QAAQ,GAAG,OAAO,MAAM,IAAI,IAAI,KAAK,CAAC;YAC5C,MAAM,MAAM,GAAG,CAAC,GAAG,CAAC,aAAa,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC;YAC3D,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,OAAO,QAAQ,OAAO,GAAG,CAAC,KAAK,MAAM,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,MAAM,IAAI,CAAC,CAAC;QACtG,CAAC;QAED,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAC5B,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,+EAA+E,CAAC,CAAC;QAC5F,KAAK,CAAC,IAAI,CAAC,6EAA6E,CAAC,CAAC;QAC1F,KAAK,CAAC,IAAI,CAAC,oFAAoF,CAAC,CAAC;QACjG,KAAK,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAC;QACxD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,+EAA+E,CAAC,CAAC;QAC5F,KAAK,CAAC,IAAI,CAAC,oEAAoE,CAAC,CAAC;QACjF,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAEf,OAAO;YACL,IAAI,EAAE,6BAA6B;YACnC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;YACzB,MAAM,EAAE,WAAW;YACnB,IAAI,EAAE,KAAK;SACZ,CAAC;IACJ,CAAC;CACF;AAED,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAChD,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generator module index
|
|
3
|
+
*/
|
|
4
|
+
export * from './spec-pipeline.js';
|
|
5
|
+
export * from './openspec-format-generator.js';
|
|
6
|
+
export { OpenSpecValidator, OpenSpecConfigManager, validateFullSpec, normalizeDomainName, buildDetectedContext, type OpenSpecConfig, type SpecGenMetadata, type DetectedContext, } from './openspec-compat.js';
|
|
7
|
+
export * from './openspec-writer.js';
|
|
8
|
+
export * from './adr-generator.js';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/generator/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,oBAAoB,CAAC;AACnC,cAAc,gCAAgC,CAAC;AAC/C,OAAO,EAGL,iBAAiB,EACjB,qBAAqB,EACrB,gBAAgB,EAChB,mBAAmB,EACnB,oBAAoB,EACpB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,eAAe,GACrB,MAAM,sBAAsB,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generator module index
|
|
3
|
+
*/
|
|
4
|
+
export * from './spec-pipeline.js';
|
|
5
|
+
export * from './openspec-format-generator.js';
|
|
6
|
+
export {
|
|
7
|
+
// Explicitly export from openspec-compat, excluding ValidationResult
|
|
8
|
+
// which is already exported from openspec-format-generator
|
|
9
|
+
OpenSpecValidator, OpenSpecConfigManager, validateFullSpec, normalizeDomainName, buildDetectedContext, } from './openspec-compat.js';
|
|
10
|
+
export * from './openspec-writer.js';
|
|
11
|
+
export * from './adr-generator.js';
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/generator/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,oBAAoB,CAAC;AACnC,cAAc,gCAAgC,CAAC;AAC/C,OAAO;AACL,qEAAqE;AACrE,2DAA2D;AAC3D,iBAAiB,EACjB,qBAAqB,EACrB,gBAAgB,EAChB,mBAAmB,EACnB,oBAAoB,GAIrB,MAAM,sBAAsB,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC"}
|