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,731 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repository Mapper
|
|
3
|
+
*
|
|
4
|
+
* Combines file walking and significance scoring into a comprehensive "map"
|
|
5
|
+
* of the repository that guides all subsequent analysis.
|
|
6
|
+
*/
|
|
7
|
+
import { readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
8
|
+
import { join, basename } from 'node:path';
|
|
9
|
+
import { FileWalker } from './file-walker.js';
|
|
10
|
+
import { SignificanceScorer } from './significance-scorer.js';
|
|
11
|
+
const FRAMEWORK_DETECTORS = [
|
|
12
|
+
// JavaScript/TypeScript Frameworks
|
|
13
|
+
{
|
|
14
|
+
name: 'React',
|
|
15
|
+
category: 'frontend',
|
|
16
|
+
detect: (files, pkg) => {
|
|
17
|
+
const evidence = [];
|
|
18
|
+
const deps = { ...(pkg?.dependencies || {}), ...(pkg?.devDependencies || {}) };
|
|
19
|
+
if (deps['react'])
|
|
20
|
+
evidence.push('react in dependencies');
|
|
21
|
+
if (files.some(f => f.extension === '.jsx' || f.extension === '.tsx')) {
|
|
22
|
+
evidence.push('.jsx/.tsx files found');
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
detected: evidence.length > 0,
|
|
26
|
+
confidence: evidence.length >= 2 ? 'high' : 'medium',
|
|
27
|
+
evidence,
|
|
28
|
+
};
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'Next.js',
|
|
33
|
+
category: 'frontend',
|
|
34
|
+
detect: (files, pkg) => {
|
|
35
|
+
const evidence = [];
|
|
36
|
+
const deps = { ...(pkg?.dependencies || {}), ...(pkg?.devDependencies || {}) };
|
|
37
|
+
if (deps['next'])
|
|
38
|
+
evidence.push('next in dependencies');
|
|
39
|
+
if (files.some(f => f.name.startsWith('next.config'))) {
|
|
40
|
+
evidence.push('next.config.* found');
|
|
41
|
+
}
|
|
42
|
+
if (files.some(f => f.directory === 'pages' || f.directory === 'app' || f.directory.startsWith('pages/') || f.directory.startsWith('app/'))) {
|
|
43
|
+
evidence.push('pages/ or app/ directory found');
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
detected: evidence.length > 0,
|
|
47
|
+
confidence: evidence.length >= 2 ? 'high' : 'medium',
|
|
48
|
+
evidence,
|
|
49
|
+
};
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: 'Express',
|
|
54
|
+
category: 'backend',
|
|
55
|
+
detect: (files, pkg) => {
|
|
56
|
+
const evidence = [];
|
|
57
|
+
const deps = { ...(pkg?.dependencies || {}), ...(pkg?.devDependencies || {}) };
|
|
58
|
+
if (deps['express'])
|
|
59
|
+
evidence.push('express in dependencies');
|
|
60
|
+
return {
|
|
61
|
+
detected: evidence.length > 0,
|
|
62
|
+
confidence: 'high',
|
|
63
|
+
evidence,
|
|
64
|
+
};
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: 'NestJS',
|
|
69
|
+
category: 'backend',
|
|
70
|
+
detect: (files, pkg) => {
|
|
71
|
+
const evidence = [];
|
|
72
|
+
const deps = { ...(pkg?.dependencies || {}), ...(pkg?.devDependencies || {}) };
|
|
73
|
+
if (Object.keys(deps).some(d => d.startsWith('@nestjs/'))) {
|
|
74
|
+
evidence.push('@nestjs/* packages in dependencies');
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
detected: evidence.length > 0,
|
|
78
|
+
confidence: 'high',
|
|
79
|
+
evidence,
|
|
80
|
+
};
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: 'Vue',
|
|
85
|
+
category: 'frontend',
|
|
86
|
+
detect: (files, pkg) => {
|
|
87
|
+
const evidence = [];
|
|
88
|
+
const deps = { ...(pkg?.dependencies || {}), ...(pkg?.devDependencies || {}) };
|
|
89
|
+
if (deps['vue'])
|
|
90
|
+
evidence.push('vue in dependencies');
|
|
91
|
+
if (files.some(f => f.extension === '.vue')) {
|
|
92
|
+
evidence.push('.vue files found');
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
detected: evidence.length > 0,
|
|
96
|
+
confidence: evidence.length >= 2 ? 'high' : 'medium',
|
|
97
|
+
evidence,
|
|
98
|
+
};
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: 'Angular',
|
|
103
|
+
category: 'frontend',
|
|
104
|
+
detect: (files, pkg) => {
|
|
105
|
+
const evidence = [];
|
|
106
|
+
const deps = { ...(pkg?.dependencies || {}), ...(pkg?.devDependencies || {}) };
|
|
107
|
+
if (Object.keys(deps).some(d => d.startsWith('@angular/'))) {
|
|
108
|
+
evidence.push('@angular/* packages in dependencies');
|
|
109
|
+
}
|
|
110
|
+
if (files.some(f => f.name === 'angular.json')) {
|
|
111
|
+
evidence.push('angular.json found');
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
detected: evidence.length > 0,
|
|
115
|
+
confidence: evidence.length >= 2 ? 'high' : 'medium',
|
|
116
|
+
evidence,
|
|
117
|
+
};
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
// Testing Frameworks
|
|
121
|
+
{
|
|
122
|
+
name: 'Jest',
|
|
123
|
+
category: 'testing',
|
|
124
|
+
detect: (files, pkg) => {
|
|
125
|
+
const evidence = [];
|
|
126
|
+
const deps = { ...(pkg?.dependencies || {}), ...(pkg?.devDependencies || {}) };
|
|
127
|
+
if (deps['jest'])
|
|
128
|
+
evidence.push('jest in dependencies');
|
|
129
|
+
if (files.some(f => f.name.startsWith('jest.config'))) {
|
|
130
|
+
evidence.push('jest.config.* found');
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
detected: evidence.length > 0,
|
|
134
|
+
confidence: 'high',
|
|
135
|
+
evidence,
|
|
136
|
+
};
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
name: 'Vitest',
|
|
141
|
+
category: 'testing',
|
|
142
|
+
detect: (files, pkg) => {
|
|
143
|
+
const evidence = [];
|
|
144
|
+
const deps = { ...(pkg?.dependencies || {}), ...(pkg?.devDependencies || {}) };
|
|
145
|
+
if (deps['vitest'])
|
|
146
|
+
evidence.push('vitest in dependencies');
|
|
147
|
+
if (files.some(f => f.name.startsWith('vitest.config'))) {
|
|
148
|
+
evidence.push('vitest.config.* found');
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
detected: evidence.length > 0,
|
|
152
|
+
confidence: 'high',
|
|
153
|
+
evidence,
|
|
154
|
+
};
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
// Database
|
|
158
|
+
{
|
|
159
|
+
name: 'PostgreSQL',
|
|
160
|
+
category: 'database',
|
|
161
|
+
detect: (files, pkg) => {
|
|
162
|
+
const evidence = [];
|
|
163
|
+
const deps = { ...(pkg?.dependencies || {}), ...(pkg?.devDependencies || {}) };
|
|
164
|
+
if (deps['pg'] || deps['postgres'] || deps['@prisma/client']) {
|
|
165
|
+
evidence.push('PostgreSQL client in dependencies');
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
detected: evidence.length > 0,
|
|
169
|
+
confidence: 'medium',
|
|
170
|
+
evidence,
|
|
171
|
+
};
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
name: 'MongoDB',
|
|
176
|
+
category: 'database',
|
|
177
|
+
detect: (files, pkg) => {
|
|
178
|
+
const evidence = [];
|
|
179
|
+
const deps = { ...(pkg?.dependencies || {}), ...(pkg?.devDependencies || {}) };
|
|
180
|
+
if (deps['mongodb'] || deps['mongoose']) {
|
|
181
|
+
evidence.push('MongoDB client in dependencies');
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
detected: evidence.length > 0,
|
|
185
|
+
confidence: 'high',
|
|
186
|
+
evidence,
|
|
187
|
+
};
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
// Auth
|
|
191
|
+
{
|
|
192
|
+
name: 'JWT Auth',
|
|
193
|
+
category: 'auth',
|
|
194
|
+
detect: (files, pkg) => {
|
|
195
|
+
const evidence = [];
|
|
196
|
+
const deps = { ...(pkg?.dependencies || {}), ...(pkg?.devDependencies || {}) };
|
|
197
|
+
if (deps['jsonwebtoken'] || deps['jose']) {
|
|
198
|
+
evidence.push('JWT library in dependencies');
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
detected: evidence.length > 0,
|
|
202
|
+
confidence: 'high',
|
|
203
|
+
evidence,
|
|
204
|
+
};
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
name: 'Passport',
|
|
209
|
+
category: 'auth',
|
|
210
|
+
detect: (files, pkg) => {
|
|
211
|
+
const evidence = [];
|
|
212
|
+
const deps = { ...(pkg?.dependencies || {}), ...(pkg?.devDependencies || {}) };
|
|
213
|
+
if (deps['passport']) {
|
|
214
|
+
evidence.push('passport in dependencies');
|
|
215
|
+
}
|
|
216
|
+
return {
|
|
217
|
+
detected: evidence.length > 0,
|
|
218
|
+
confidence: 'high',
|
|
219
|
+
evidence,
|
|
220
|
+
};
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
// CI/CD
|
|
224
|
+
{
|
|
225
|
+
name: 'GitHub Actions',
|
|
226
|
+
category: 'ci',
|
|
227
|
+
detect: (files) => {
|
|
228
|
+
const evidence = [];
|
|
229
|
+
if (files.some(f => f.path.includes('.github/workflows'))) {
|
|
230
|
+
evidence.push('.github/workflows directory found');
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
detected: evidence.length > 0,
|
|
234
|
+
confidence: 'high',
|
|
235
|
+
evidence,
|
|
236
|
+
};
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
name: 'GitLab CI',
|
|
241
|
+
category: 'ci',
|
|
242
|
+
detect: (files) => {
|
|
243
|
+
const evidence = [];
|
|
244
|
+
if (files.some(f => f.name === '.gitlab-ci.yml')) {
|
|
245
|
+
evidence.push('.gitlab-ci.yml found');
|
|
246
|
+
}
|
|
247
|
+
return {
|
|
248
|
+
detected: evidence.length > 0,
|
|
249
|
+
confidence: 'high',
|
|
250
|
+
evidence,
|
|
251
|
+
};
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
];
|
|
255
|
+
// ============================================================================
|
|
256
|
+
// LAYER DETECTION
|
|
257
|
+
// ============================================================================
|
|
258
|
+
const LAYER_PATTERNS = {
|
|
259
|
+
presentation: [
|
|
260
|
+
/\/(components?|views?|pages?|ui|screens?|layouts?)\//i,
|
|
261
|
+
/\.(jsx|tsx|vue)$/,
|
|
262
|
+
/\.component\./,
|
|
263
|
+
/\.page\./,
|
|
264
|
+
],
|
|
265
|
+
business: [
|
|
266
|
+
/\/(services?|domain|business|core|logic|usecases?)\//i,
|
|
267
|
+
/\.service\./,
|
|
268
|
+
/\.usecase\./,
|
|
269
|
+
],
|
|
270
|
+
data: [
|
|
271
|
+
/\/(models?|entities?|repositories?|schemas?|db|database|data)\//i,
|
|
272
|
+
/\.model\./,
|
|
273
|
+
/\.entity\./,
|
|
274
|
+
/\.repository\./,
|
|
275
|
+
/\.schema\./,
|
|
276
|
+
],
|
|
277
|
+
infrastructure: [
|
|
278
|
+
/\/(config|utils?|helpers?|lib|middleware|infra|infrastructure)\//i,
|
|
279
|
+
/\.config\./,
|
|
280
|
+
/\.util\./,
|
|
281
|
+
/\.helper\./,
|
|
282
|
+
/\.middleware\./,
|
|
283
|
+
],
|
|
284
|
+
};
|
|
285
|
+
function detectLayer(file) {
|
|
286
|
+
const pathAndName = file.path + '/' + file.name;
|
|
287
|
+
for (const [layer, patterns] of Object.entries(LAYER_PATTERNS)) {
|
|
288
|
+
for (const pattern of patterns) {
|
|
289
|
+
if (pattern.test(pathAndName)) {
|
|
290
|
+
return layer;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
// ============================================================================
|
|
297
|
+
// DOMAIN INFERENCE
|
|
298
|
+
// ============================================================================
|
|
299
|
+
/**
|
|
300
|
+
* Infer domains from file paths and names
|
|
301
|
+
*/
|
|
302
|
+
function inferDomains(files) {
|
|
303
|
+
const domains = {};
|
|
304
|
+
// Common domain prefixes to look for
|
|
305
|
+
const domainPrefixes = new Map();
|
|
306
|
+
for (const file of files) {
|
|
307
|
+
// Skip test and config files for domain inference
|
|
308
|
+
if (file.isTest || file.isConfig)
|
|
309
|
+
continue;
|
|
310
|
+
// Extract potential domain from path
|
|
311
|
+
const pathParts = file.path.split('/');
|
|
312
|
+
// Check for domain-like directory names (skip common non-domain dirs)
|
|
313
|
+
const skipDirs = new Set(['src', 'lib', 'app', 'core', 'common', 'shared', 'utils', 'helpers', 'config']);
|
|
314
|
+
for (const part of pathParts) {
|
|
315
|
+
if (part && !skipDirs.has(part.toLowerCase()) && !part.startsWith('.')) {
|
|
316
|
+
// This could be a domain
|
|
317
|
+
const domain = part.toLowerCase();
|
|
318
|
+
if (!domains[domain]) {
|
|
319
|
+
domains[domain] = [];
|
|
320
|
+
}
|
|
321
|
+
domains[domain].push(file);
|
|
322
|
+
break; // Only use first domain-like directory
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
// Also check file name prefixes (e.g., user-service.ts -> user)
|
|
326
|
+
const nameWithoutExt = file.name.replace(/\.[^.]+$/, '');
|
|
327
|
+
const nameParts = nameWithoutExt.split(/[-_.]/);
|
|
328
|
+
if (nameParts.length > 1) {
|
|
329
|
+
const prefix = nameParts[0].toLowerCase();
|
|
330
|
+
if (prefix.length > 2 && !skipDirs.has(prefix)) {
|
|
331
|
+
if (!domainPrefixes.has(prefix)) {
|
|
332
|
+
domainPrefixes.set(prefix, []);
|
|
333
|
+
}
|
|
334
|
+
domainPrefixes.get(prefix).push(file.path);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
// Add domains from file prefixes if they have multiple files
|
|
339
|
+
for (const [prefix, filePaths] of domainPrefixes) {
|
|
340
|
+
if (filePaths.length >= 2 && !domains[prefix]) {
|
|
341
|
+
domains[prefix] = files.filter(f => filePaths.includes(f.path));
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
// Remove domains with only 1 file
|
|
345
|
+
for (const domain of Object.keys(domains)) {
|
|
346
|
+
if (domains[domain].length < 2) {
|
|
347
|
+
delete domains[domain];
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return domains;
|
|
351
|
+
}
|
|
352
|
+
// ============================================================================
|
|
353
|
+
// REPOSITORY MAPPER CLASS
|
|
354
|
+
// ============================================================================
|
|
355
|
+
export class RepositoryMapper {
|
|
356
|
+
rootPath;
|
|
357
|
+
options;
|
|
358
|
+
packageJson = null;
|
|
359
|
+
constructor(rootPath, options = {}) {
|
|
360
|
+
this.rootPath = rootPath;
|
|
361
|
+
this.options = {
|
|
362
|
+
maxFiles: options.maxFiles ?? 500,
|
|
363
|
+
includePatterns: options.includePatterns ?? [],
|
|
364
|
+
excludePatterns: options.excludePatterns ?? [],
|
|
365
|
+
scoringConfig: options.scoringConfig ?? {},
|
|
366
|
+
onProgress: options.onProgress ?? (() => { }),
|
|
367
|
+
outputDir: options.outputDir ?? join(rootPath, '.spec-gen', 'analysis'),
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Load package.json if it exists
|
|
372
|
+
*/
|
|
373
|
+
async loadPackageJson() {
|
|
374
|
+
try {
|
|
375
|
+
const content = await readFile(join(this.rootPath, 'package.json'), 'utf-8');
|
|
376
|
+
this.packageJson = JSON.parse(content);
|
|
377
|
+
}
|
|
378
|
+
catch {
|
|
379
|
+
this.packageJson = null;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Get project name from package.json or directory name
|
|
384
|
+
*/
|
|
385
|
+
getProjectName() {
|
|
386
|
+
if (this.packageJson?.name && typeof this.packageJson.name === 'string') {
|
|
387
|
+
return this.packageJson.name;
|
|
388
|
+
}
|
|
389
|
+
return basename(this.rootPath);
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Detect project type
|
|
393
|
+
*/
|
|
394
|
+
detectProjectType(files) {
|
|
395
|
+
// Check for language-specific files
|
|
396
|
+
const hasPackageJson = files.some(f => f.name === 'package.json');
|
|
397
|
+
const hasPyproject = files.some(f => f.name === 'pyproject.toml' || f.name === 'setup.py');
|
|
398
|
+
const hasCargoToml = files.some(f => f.name === 'Cargo.toml');
|
|
399
|
+
const hasGoMod = files.some(f => f.name === 'go.mod');
|
|
400
|
+
if (hasPackageJson)
|
|
401
|
+
return 'nodejs';
|
|
402
|
+
if (hasPyproject)
|
|
403
|
+
return 'python';
|
|
404
|
+
if (hasCargoToml)
|
|
405
|
+
return 'rust';
|
|
406
|
+
if (hasGoMod)
|
|
407
|
+
return 'go';
|
|
408
|
+
return 'unknown';
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Calculate language breakdown
|
|
412
|
+
*/
|
|
413
|
+
calculateLanguages(files) {
|
|
414
|
+
const extCounts = {};
|
|
415
|
+
const extToLang = {
|
|
416
|
+
'.ts': 'TypeScript',
|
|
417
|
+
'.tsx': 'TypeScript (React)',
|
|
418
|
+
'.js': 'JavaScript',
|
|
419
|
+
'.jsx': 'JavaScript (React)',
|
|
420
|
+
'.py': 'Python',
|
|
421
|
+
'.rs': 'Rust',
|
|
422
|
+
'.go': 'Go',
|
|
423
|
+
'.java': 'Java',
|
|
424
|
+
'.rb': 'Ruby',
|
|
425
|
+
'.php': 'PHP',
|
|
426
|
+
'.vue': 'Vue',
|
|
427
|
+
'.svelte': 'Svelte',
|
|
428
|
+
'.css': 'CSS',
|
|
429
|
+
'.scss': 'SCSS',
|
|
430
|
+
'.less': 'LESS',
|
|
431
|
+
'.html': 'HTML',
|
|
432
|
+
'.json': 'JSON',
|
|
433
|
+
'.yaml': 'YAML',
|
|
434
|
+
'.yml': 'YAML',
|
|
435
|
+
'.md': 'Markdown',
|
|
436
|
+
'.sql': 'SQL',
|
|
437
|
+
'.graphql': 'GraphQL',
|
|
438
|
+
'.prisma': 'Prisma',
|
|
439
|
+
};
|
|
440
|
+
for (const file of files) {
|
|
441
|
+
const ext = file.extension.toLowerCase();
|
|
442
|
+
extCounts[ext] = (extCounts[ext] ?? 0) + 1;
|
|
443
|
+
}
|
|
444
|
+
const total = files.length;
|
|
445
|
+
const languages = [];
|
|
446
|
+
for (const [ext, count] of Object.entries(extCounts)) {
|
|
447
|
+
if (count > 0) {
|
|
448
|
+
languages.push({
|
|
449
|
+
language: extToLang[ext] ?? ext.slice(1).toUpperCase(),
|
|
450
|
+
extension: ext,
|
|
451
|
+
fileCount: count,
|
|
452
|
+
percentage: Math.round((count / total) * 100),
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
// Sort by file count descending
|
|
457
|
+
languages.sort((a, b) => b.fileCount - a.fileCount);
|
|
458
|
+
return languages;
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Detect frameworks
|
|
462
|
+
*/
|
|
463
|
+
detectFrameworks(files) {
|
|
464
|
+
const frameworks = [];
|
|
465
|
+
for (const detector of FRAMEWORK_DETECTORS) {
|
|
466
|
+
const result = detector.detect(files, this.packageJson);
|
|
467
|
+
if (result.detected) {
|
|
468
|
+
frameworks.push({
|
|
469
|
+
name: detector.name,
|
|
470
|
+
category: detector.category,
|
|
471
|
+
confidence: result.confidence,
|
|
472
|
+
evidence: result.evidence,
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
return frameworks;
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Calculate directory statistics
|
|
480
|
+
*/
|
|
481
|
+
calculateDirectoryStats(files) {
|
|
482
|
+
const dirStats = {};
|
|
483
|
+
for (const file of files) {
|
|
484
|
+
const dir = file.directory || '(root)';
|
|
485
|
+
if (!dirStats[dir]) {
|
|
486
|
+
dirStats[dir] = { files: [], totalScore: 0 };
|
|
487
|
+
}
|
|
488
|
+
dirStats[dir].files.push(file);
|
|
489
|
+
dirStats[dir].totalScore += file.score;
|
|
490
|
+
}
|
|
491
|
+
const stats = [];
|
|
492
|
+
for (const [path, data] of Object.entries(dirStats)) {
|
|
493
|
+
// Infer purpose from directory name
|
|
494
|
+
let purpose = 'unknown';
|
|
495
|
+
const pathLower = path.toLowerCase();
|
|
496
|
+
if (pathLower.includes('test') || pathLower.includes('spec'))
|
|
497
|
+
purpose = 'tests';
|
|
498
|
+
else if (pathLower.includes('component'))
|
|
499
|
+
purpose = 'components';
|
|
500
|
+
else if (pathLower.includes('service'))
|
|
501
|
+
purpose = 'services';
|
|
502
|
+
else if (pathLower.includes('model') || pathLower.includes('entity'))
|
|
503
|
+
purpose = 'models';
|
|
504
|
+
else if (pathLower.includes('route') || pathLower.includes('api'))
|
|
505
|
+
purpose = 'api';
|
|
506
|
+
else if (pathLower.includes('config'))
|
|
507
|
+
purpose = 'configuration';
|
|
508
|
+
else if (pathLower.includes('util') || pathLower.includes('helper'))
|
|
509
|
+
purpose = 'utilities';
|
|
510
|
+
else if (pathLower.includes('middleware'))
|
|
511
|
+
purpose = 'middleware';
|
|
512
|
+
else if (path === '(root)')
|
|
513
|
+
purpose = 'root';
|
|
514
|
+
else if (pathLower === 'src' || pathLower === 'lib')
|
|
515
|
+
purpose = 'source';
|
|
516
|
+
stats.push({
|
|
517
|
+
path,
|
|
518
|
+
fileCount: data.files.length,
|
|
519
|
+
purpose,
|
|
520
|
+
avgScore: Math.round(data.totalScore / data.files.length),
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
// Sort by file count descending
|
|
524
|
+
stats.sort((a, b) => b.fileCount - a.fileCount);
|
|
525
|
+
return stats;
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Cluster files by various dimensions
|
|
529
|
+
*/
|
|
530
|
+
clusterFiles(files) {
|
|
531
|
+
// By directory
|
|
532
|
+
const byDirectory = {};
|
|
533
|
+
for (const file of files) {
|
|
534
|
+
const dir = file.directory || '(root)';
|
|
535
|
+
if (!byDirectory[dir]) {
|
|
536
|
+
byDirectory[dir] = [];
|
|
537
|
+
}
|
|
538
|
+
byDirectory[dir].push(file);
|
|
539
|
+
}
|
|
540
|
+
// By domain (inferred)
|
|
541
|
+
const byDomain = inferDomains(files);
|
|
542
|
+
// By layer
|
|
543
|
+
const byLayer = {
|
|
544
|
+
presentation: [],
|
|
545
|
+
business: [],
|
|
546
|
+
data: [],
|
|
547
|
+
infrastructure: [],
|
|
548
|
+
};
|
|
549
|
+
for (const file of files) {
|
|
550
|
+
const layer = detectLayer(file);
|
|
551
|
+
if (layer) {
|
|
552
|
+
byLayer[layer].push(file);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
return { byDirectory, byDomain, byLayer };
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Generate the repository map
|
|
559
|
+
*/
|
|
560
|
+
async map() {
|
|
561
|
+
this.options.onProgress?.('loading', 0);
|
|
562
|
+
// Load package.json
|
|
563
|
+
await this.loadPackageJson();
|
|
564
|
+
this.options.onProgress?.('walking', 10);
|
|
565
|
+
// Walk the directory
|
|
566
|
+
const walkerOptions = {
|
|
567
|
+
maxFiles: this.options.maxFiles,
|
|
568
|
+
includePatterns: this.options.includePatterns,
|
|
569
|
+
excludePatterns: this.options.excludePatterns,
|
|
570
|
+
onProgress: (progress) => {
|
|
571
|
+
const pct = 10 + Math.round((progress.filesFound / (this.options.maxFiles ?? 500)) * 30);
|
|
572
|
+
this.options.onProgress?.('walking', Math.min(pct, 40));
|
|
573
|
+
},
|
|
574
|
+
};
|
|
575
|
+
const walker = new FileWalker(this.rootPath, walkerOptions);
|
|
576
|
+
const walkResult = await walker.walk();
|
|
577
|
+
this.options.onProgress?.('scoring', 40);
|
|
578
|
+
// Score all files
|
|
579
|
+
const scorer = new SignificanceScorer(this.options.scoringConfig);
|
|
580
|
+
const scoredFiles = await scorer.scoreFiles(walkResult.files);
|
|
581
|
+
this.options.onProgress?.('analyzing', 70);
|
|
582
|
+
// Detect project type and frameworks
|
|
583
|
+
const projectType = this.detectProjectType(walkResult.files);
|
|
584
|
+
const languages = this.calculateLanguages(walkResult.files);
|
|
585
|
+
const frameworks = this.detectFrameworks(walkResult.files);
|
|
586
|
+
// Extract special file categories
|
|
587
|
+
const highValueFiles = scoredFiles.slice(0, 50); // Top 50
|
|
588
|
+
const entryPoints = scoredFiles.filter(f => f.isEntryPoint);
|
|
589
|
+
const schemaFiles = scoredFiles.filter(f => f.tags.includes('schema') ||
|
|
590
|
+
f.name.toLowerCase().includes('model') ||
|
|
591
|
+
f.name.toLowerCase().includes('entity') ||
|
|
592
|
+
f.name.toLowerCase().includes('schema'));
|
|
593
|
+
const configFiles = scoredFiles.filter(f => f.isConfig);
|
|
594
|
+
// Calculate directory stats and clusters
|
|
595
|
+
const directories = this.calculateDirectoryStats(scoredFiles);
|
|
596
|
+
const clusters = this.clusterFiles(scoredFiles);
|
|
597
|
+
this.options.onProgress?.('complete', 100);
|
|
598
|
+
// Build the map
|
|
599
|
+
const map = {
|
|
600
|
+
metadata: {
|
|
601
|
+
projectName: this.getProjectName(),
|
|
602
|
+
projectType,
|
|
603
|
+
rootPath: this.rootPath,
|
|
604
|
+
analyzedAt: new Date().toISOString(),
|
|
605
|
+
version: '1.0.0',
|
|
606
|
+
},
|
|
607
|
+
summary: {
|
|
608
|
+
totalFiles: walkResult.summary.totalFiles + walkResult.summary.skippedCount,
|
|
609
|
+
analyzedFiles: walkResult.summary.totalFiles,
|
|
610
|
+
skippedFiles: walkResult.summary.skippedCount,
|
|
611
|
+
languages,
|
|
612
|
+
frameworks,
|
|
613
|
+
directories,
|
|
614
|
+
},
|
|
615
|
+
highValueFiles,
|
|
616
|
+
entryPoints,
|
|
617
|
+
schemaFiles,
|
|
618
|
+
configFiles,
|
|
619
|
+
clusters,
|
|
620
|
+
allFiles: scoredFiles,
|
|
621
|
+
};
|
|
622
|
+
return map;
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Write repository map to output directory
|
|
626
|
+
*/
|
|
627
|
+
async writeOutput(map) {
|
|
628
|
+
// Ensure output directory exists
|
|
629
|
+
await mkdir(this.options.outputDir, { recursive: true });
|
|
630
|
+
// Write JSON map
|
|
631
|
+
const mapPath = join(this.options.outputDir, 'repository-map.json');
|
|
632
|
+
await writeFile(mapPath, JSON.stringify(map, null, 2), 'utf-8');
|
|
633
|
+
// Write summary markdown
|
|
634
|
+
const summaryPath = join(this.options.outputDir, 'SUMMARY.md');
|
|
635
|
+
const summary = this.generateSummaryMarkdown(map);
|
|
636
|
+
await writeFile(summaryPath, summary, 'utf-8');
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Generate human-readable summary
|
|
640
|
+
*/
|
|
641
|
+
generateSummaryMarkdown(map) {
|
|
642
|
+
const lines = [];
|
|
643
|
+
lines.push(`# Repository Analysis: ${map.metadata.projectName}`);
|
|
644
|
+
lines.push('');
|
|
645
|
+
lines.push(`> Generated by spec-gen v${map.metadata.version} on ${map.metadata.analyzedAt}`);
|
|
646
|
+
lines.push('');
|
|
647
|
+
// Overview
|
|
648
|
+
lines.push('## Overview');
|
|
649
|
+
lines.push('');
|
|
650
|
+
lines.push(`- **Project Type**: ${map.metadata.projectType}`);
|
|
651
|
+
lines.push(`- **Total Files**: ${map.summary.totalFiles}`);
|
|
652
|
+
lines.push(`- **Analyzed Files**: ${map.summary.analyzedFiles}`);
|
|
653
|
+
lines.push(`- **Skipped Files**: ${map.summary.skippedFiles}`);
|
|
654
|
+
lines.push('');
|
|
655
|
+
// Languages
|
|
656
|
+
lines.push('## Languages');
|
|
657
|
+
lines.push('');
|
|
658
|
+
lines.push('| Language | Files | Percentage |');
|
|
659
|
+
lines.push('|----------|-------|------------|');
|
|
660
|
+
for (const lang of map.summary.languages.slice(0, 10)) {
|
|
661
|
+
lines.push(`| ${lang.language} | ${lang.fileCount} | ${lang.percentage}% |`);
|
|
662
|
+
}
|
|
663
|
+
lines.push('');
|
|
664
|
+
// Frameworks
|
|
665
|
+
if (map.summary.frameworks.length > 0) {
|
|
666
|
+
lines.push('## Detected Frameworks');
|
|
667
|
+
lines.push('');
|
|
668
|
+
for (const fw of map.summary.frameworks) {
|
|
669
|
+
lines.push(`- **${fw.name}** (${fw.category}, ${fw.confidence} confidence)`);
|
|
670
|
+
for (const evidence of fw.evidence) {
|
|
671
|
+
lines.push(` - ${evidence}`);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
lines.push('');
|
|
675
|
+
}
|
|
676
|
+
// High Value Files
|
|
677
|
+
lines.push('## High Value Files (Top 20)');
|
|
678
|
+
lines.push('');
|
|
679
|
+
lines.push('| File | Score | Tags |');
|
|
680
|
+
lines.push('|------|-------|------|');
|
|
681
|
+
for (const file of map.highValueFiles.slice(0, 20)) {
|
|
682
|
+
const tags = file.tags.join(', ') || '-';
|
|
683
|
+
lines.push(`| ${file.path} | ${file.score} | ${tags} |`);
|
|
684
|
+
}
|
|
685
|
+
lines.push('');
|
|
686
|
+
// Entry Points
|
|
687
|
+
if (map.entryPoints.length > 0) {
|
|
688
|
+
lines.push('## Entry Points');
|
|
689
|
+
lines.push('');
|
|
690
|
+
for (const file of map.entryPoints.slice(0, 10)) {
|
|
691
|
+
lines.push(`- ${file.path} (score: ${file.score})`);
|
|
692
|
+
}
|
|
693
|
+
lines.push('');
|
|
694
|
+
}
|
|
695
|
+
// Inferred Domains
|
|
696
|
+
const domains = Object.keys(map.clusters.byDomain);
|
|
697
|
+
if (domains.length > 0) {
|
|
698
|
+
lines.push('## Inferred Domains');
|
|
699
|
+
lines.push('');
|
|
700
|
+
lines.push('These domains may become separate spec files:');
|
|
701
|
+
lines.push('');
|
|
702
|
+
for (const domain of domains) {
|
|
703
|
+
const files = map.clusters.byDomain[domain];
|
|
704
|
+
lines.push(`- **${domain}** (${files.length} files)`);
|
|
705
|
+
}
|
|
706
|
+
lines.push('');
|
|
707
|
+
}
|
|
708
|
+
// Directory Structure
|
|
709
|
+
lines.push('## Directory Structure');
|
|
710
|
+
lines.push('');
|
|
711
|
+
lines.push('| Directory | Files | Purpose | Avg Score |');
|
|
712
|
+
lines.push('|-----------|-------|---------|-----------|');
|
|
713
|
+
for (const dir of map.summary.directories.slice(0, 15)) {
|
|
714
|
+
lines.push(`| ${dir.path} | ${dir.fileCount} | ${dir.purpose} | ${dir.avgScore} |`);
|
|
715
|
+
}
|
|
716
|
+
lines.push('');
|
|
717
|
+
return lines.join('\n');
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* Convenience function to map a repository
|
|
722
|
+
*/
|
|
723
|
+
export async function mapRepository(rootPath, options) {
|
|
724
|
+
const mapper = new RepositoryMapper(rootPath, options);
|
|
725
|
+
const map = await mapper.map();
|
|
726
|
+
if (options?.outputDir !== undefined || options?.outputDir === undefined) {
|
|
727
|
+
await mapper.writeOutput(map);
|
|
728
|
+
}
|
|
729
|
+
return map;
|
|
730
|
+
}
|
|
731
|
+
//# sourceMappingURL=repository-mapper.js.map
|