create-universal-ai-context 2.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 +171 -0
- package/bin/create-ai-context.js +337 -0
- package/lib/adapters/antigravity.js +160 -0
- package/lib/adapters/claude.js +122 -0
- package/lib/adapters/cline.js +111 -0
- package/lib/adapters/copilot.js +117 -0
- package/lib/adapters/index.js +69 -0
- package/lib/ai-context-generator.js +234 -0
- package/lib/ai-orchestrator.js +431 -0
- package/lib/call-tracer.js +444 -0
- package/lib/detector.js +726 -0
- package/lib/environment-detector.js +239 -0
- package/lib/index.js +310 -0
- package/lib/installer.js +418 -0
- package/lib/migrate.js +319 -0
- package/lib/placeholder.js +541 -0
- package/lib/prompts.js +287 -0
- package/lib/spinner.js +60 -0
- package/lib/static-analyzer.js +729 -0
- package/lib/template-populator.js +843 -0
- package/lib/template-renderer.js +382 -0
- package/lib/validate.js +155 -0
- package/package.json +70 -0
- package/templates/AI_CONTEXT.md.template +245 -0
- package/templates/base/README.md +257 -0
- package/templates/base/RPI_WORKFLOW_PLAN.md +320 -0
- package/templates/base/agents/api-developer.md +76 -0
- package/templates/base/agents/context-engineer.md +525 -0
- package/templates/base/agents/core-architect.md +76 -0
- package/templates/base/agents/database-ops.md +76 -0
- package/templates/base/agents/deployment-ops.md +76 -0
- package/templates/base/agents/integration-hub.md +76 -0
- package/templates/base/analytics/README.md +114 -0
- package/templates/base/automation/config.json +58 -0
- package/templates/base/automation/generators/code-mapper.js +308 -0
- package/templates/base/automation/generators/index-builder.js +321 -0
- package/templates/base/automation/hooks/post-commit.sh +83 -0
- package/templates/base/automation/hooks/pre-commit.sh +103 -0
- package/templates/base/ci-templates/README.md +108 -0
- package/templates/base/ci-templates/github-actions/context-check.yml +144 -0
- package/templates/base/ci-templates/github-actions/validate-docs.yml +105 -0
- package/templates/base/commands/analytics.md +238 -0
- package/templates/base/commands/auto-sync.md +172 -0
- package/templates/base/commands/collab.md +194 -0
- package/templates/base/commands/help.md +450 -0
- package/templates/base/commands/rpi-implement.md +115 -0
- package/templates/base/commands/rpi-plan.md +93 -0
- package/templates/base/commands/rpi-research.md +88 -0
- package/templates/base/commands/session-resume.md +144 -0
- package/templates/base/commands/session-save.md +112 -0
- package/templates/base/commands/validate-all.md +77 -0
- package/templates/base/commands/verify-docs-current.md +86 -0
- package/templates/base/config/base.json +57 -0
- package/templates/base/config/environments/development.json +13 -0
- package/templates/base/config/environments/production.json +17 -0
- package/templates/base/config/environments/staging.json +13 -0
- package/templates/base/config/local.json.example +21 -0
- package/templates/base/context/.meta/generated-at.json +18 -0
- package/templates/base/context/ARCHITECTURE_SNAPSHOT.md +156 -0
- package/templates/base/context/CODE_TO_WORKFLOW_MAP.md +94 -0
- package/templates/base/context/FILE_OWNERSHIP.md +57 -0
- package/templates/base/context/INTEGRATION_POINTS.md +92 -0
- package/templates/base/context/KNOWN_GOTCHAS.md +195 -0
- package/templates/base/context/TESTING_MAP.md +95 -0
- package/templates/base/context/WORKFLOW_INDEX.md +129 -0
- package/templates/base/context/workflows/WORKFLOW_TEMPLATE.md +294 -0
- package/templates/base/indexes/agents/CAPABILITY_MATRIX.md +255 -0
- package/templates/base/indexes/agents/CATEGORY_INDEX.md +44 -0
- package/templates/base/indexes/code/CATEGORY_INDEX.md +38 -0
- package/templates/base/indexes/routing/CATEGORY_INDEX.md +39 -0
- package/templates/base/indexes/search/CATEGORY_INDEX.md +39 -0
- package/templates/base/indexes/workflows/CATEGORY_INDEX.md +38 -0
- package/templates/base/knowledge/README.md +98 -0
- package/templates/base/knowledge/sessions/README.md +88 -0
- package/templates/base/knowledge/sessions/TEMPLATE.md +150 -0
- package/templates/base/knowledge/shared/decisions/0001-adopt-context-engineering.md +144 -0
- package/templates/base/knowledge/shared/decisions/README.md +49 -0
- package/templates/base/knowledge/shared/decisions/TEMPLATE.md +123 -0
- package/templates/base/knowledge/shared/patterns/README.md +62 -0
- package/templates/base/knowledge/shared/patterns/TEMPLATE.md +120 -0
- package/templates/base/plans/PLAN_TEMPLATE.md +250 -0
- package/templates/base/plans/active/.gitkeep +0 -0
- package/templates/base/plans/completed/.gitkeep +0 -0
- package/templates/base/research/RESEARCH_TEMPLATE.md +153 -0
- package/templates/base/research/active/.gitkeep +0 -0
- package/templates/base/research/completed/.gitkeep +0 -0
- package/templates/base/schemas/agent.schema.json +141 -0
- package/templates/base/schemas/anchors.schema.json +54 -0
- package/templates/base/schemas/automation.schema.json +93 -0
- package/templates/base/schemas/command.schema.json +134 -0
- package/templates/base/schemas/hashes.schema.json +40 -0
- package/templates/base/schemas/manifest.schema.json +117 -0
- package/templates/base/schemas/plan.schema.json +136 -0
- package/templates/base/schemas/research.schema.json +115 -0
- package/templates/base/schemas/roles.schema.json +34 -0
- package/templates/base/schemas/session.schema.json +77 -0
- package/templates/base/schemas/settings.schema.json +244 -0
- package/templates/base/schemas/staleness.schema.json +53 -0
- package/templates/base/schemas/team-config.schema.json +42 -0
- package/templates/base/schemas/workflow.schema.json +126 -0
- package/templates/base/session/checkpoints/.gitkeep +2 -0
- package/templates/base/session/current/state.json +20 -0
- package/templates/base/session/history/.gitkeep +2 -0
- package/templates/base/settings.json +3 -0
- package/templates/base/standards/COMPATIBILITY.md +219 -0
- package/templates/base/standards/EXTENSION_GUIDELINES.md +280 -0
- package/templates/base/standards/QUALITY_CHECKLIST.md +211 -0
- package/templates/base/standards/README.md +66 -0
- package/templates/base/sync/anchors.json +6 -0
- package/templates/base/sync/hashes.json +6 -0
- package/templates/base/sync/staleness.json +10 -0
- package/templates/base/team/README.md +168 -0
- package/templates/base/team/config.json +79 -0
- package/templates/base/team/roles.json +145 -0
- package/templates/base/tools/bin/claude-context.js +151 -0
- package/templates/base/tools/lib/anchor-resolver.js +276 -0
- package/templates/base/tools/lib/config-loader.js +363 -0
- package/templates/base/tools/lib/detector.js +350 -0
- package/templates/base/tools/lib/diagnose.js +206 -0
- package/templates/base/tools/lib/drift-detector.js +373 -0
- package/templates/base/tools/lib/errors.js +199 -0
- package/templates/base/tools/lib/index.js +36 -0
- package/templates/base/tools/lib/init.js +192 -0
- package/templates/base/tools/lib/logger.js +230 -0
- package/templates/base/tools/lib/placeholder.js +201 -0
- package/templates/base/tools/lib/session-manager.js +354 -0
- package/templates/base/tools/lib/validate.js +521 -0
- package/templates/base/tools/package.json +49 -0
- package/templates/handlebars/antigravity.hbs +337 -0
- package/templates/handlebars/claude.hbs +184 -0
- package/templates/handlebars/cline.hbs +63 -0
- package/templates/handlebars/copilot.hbs +131 -0
- package/templates/handlebars/partials/gotcha-list.hbs +11 -0
- package/templates/handlebars/partials/header.hbs +3 -0
- package/templates/handlebars/partials/workflow-summary.hbs +16 -0
|
@@ -0,0 +1,729 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static Analyzer
|
|
3
|
+
*
|
|
4
|
+
* Performs comprehensive static analysis of a codebase without AI.
|
|
5
|
+
* Discovers entry points, workflows, architecture, and dependencies.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const { glob } = require('glob');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Entry point patterns by framework
|
|
14
|
+
*/
|
|
15
|
+
const ENTRY_PATTERNS = {
|
|
16
|
+
// Express.js
|
|
17
|
+
express: {
|
|
18
|
+
patterns: [
|
|
19
|
+
/\.(get|post|put|delete|patch|all|use)\s*\(\s*['"]/gi,
|
|
20
|
+
/router\.(get|post|put|delete|patch|all|use)\s*\(\s*['"]/gi,
|
|
21
|
+
/app\.(get|post|put|delete|patch|all|use)\s*\(\s*['"]/gi
|
|
22
|
+
],
|
|
23
|
+
filePatterns: ['**/routes/**/*.js', '**/router/**/*.js', '**/api/**/*.js', '**/controllers/**/*.js']
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
// FastAPI (Python)
|
|
27
|
+
fastapi: {
|
|
28
|
+
patterns: [
|
|
29
|
+
/@(app|router)\.(get|post|put|delete|patch)\s*\(/gi,
|
|
30
|
+
/@router\.api_route\s*\(/gi
|
|
31
|
+
],
|
|
32
|
+
filePatterns: ['**/routes/**/*.py', '**/api/**/*.py', '**/routers/**/*.py', '**/endpoints/**/*.py']
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
// Next.js (App Router)
|
|
36
|
+
nextjs: {
|
|
37
|
+
patterns: [
|
|
38
|
+
/export\s+(async\s+)?function\s+(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)\s*\(/gi,
|
|
39
|
+
/export\s+const\s+(GET|POST|PUT|DELETE|PATCH)\s*=/gi
|
|
40
|
+
],
|
|
41
|
+
filePatterns: ['**/app/**/route.ts', '**/app/**/route.js', '**/pages/api/**/*.ts', '**/pages/api/**/*.js']
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
// Django
|
|
45
|
+
django: {
|
|
46
|
+
patterns: [
|
|
47
|
+
/path\s*\(\s*['"]/gi,
|
|
48
|
+
/url\s*\(\s*r?['"]/gi,
|
|
49
|
+
/@api_view\s*\(\s*\[/gi
|
|
50
|
+
],
|
|
51
|
+
filePatterns: ['**/urls.py', '**/views.py', '**/api/**/*.py']
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
// Rails
|
|
55
|
+
rails: {
|
|
56
|
+
patterns: [
|
|
57
|
+
/(get|post|put|patch|delete|resources|resource)\s+['"]/gi,
|
|
58
|
+
/match\s+['"]/gi
|
|
59
|
+
],
|
|
60
|
+
filePatterns: ['**/config/routes.rb', '**/app/controllers/**/*.rb']
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
// NestJS
|
|
64
|
+
nestjs: {
|
|
65
|
+
patterns: [
|
|
66
|
+
/@(Get|Post|Put|Delete|Patch|All)\s*\(/gi,
|
|
67
|
+
/@Controller\s*\(/gi
|
|
68
|
+
],
|
|
69
|
+
filePatterns: ['**/*.controller.ts', '**/controllers/**/*.ts']
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
// Gin (Go)
|
|
73
|
+
gin: {
|
|
74
|
+
patterns: [
|
|
75
|
+
/\.(GET|POST|PUT|DELETE|PATCH|Handle)\s*\(\s*"/gi,
|
|
76
|
+
/router\.(GET|POST|PUT|DELETE|PATCH)\s*\(/gi
|
|
77
|
+
],
|
|
78
|
+
filePatterns: ['**/routes/**/*.go', '**/handlers/**/*.go', '**/api/**/*.go', '**/main.go']
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
// Flask (Python)
|
|
82
|
+
flask: {
|
|
83
|
+
patterns: [
|
|
84
|
+
/@(app|bp|blueprint)\.(route|get|post|put|delete|patch)\s*\(/gi
|
|
85
|
+
],
|
|
86
|
+
filePatterns: ['**/routes/**/*.py', '**/views/**/*.py', '**/api/**/*.py', '**/app.py']
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Workflow discovery heuristics
|
|
92
|
+
*/
|
|
93
|
+
const WORKFLOW_HEURISTICS = {
|
|
94
|
+
authentication: {
|
|
95
|
+
name: 'User Authentication',
|
|
96
|
+
category: 'security',
|
|
97
|
+
complexity: 'HIGH',
|
|
98
|
+
keywords: ['login', 'logout', 'auth', 'authenticate', 'session', 'token', 'jwt', 'oauth', 'sso', 'password'],
|
|
99
|
+
filePatterns: ['**/auth/**', '**/login/**', '**/session/**', '**/oauth/**'],
|
|
100
|
+
priority: 1
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
userManagement: {
|
|
104
|
+
name: 'User Management',
|
|
105
|
+
category: 'core',
|
|
106
|
+
complexity: 'MEDIUM',
|
|
107
|
+
keywords: ['user', 'profile', 'account', 'registration', 'signup', 'register', 'onboarding'],
|
|
108
|
+
filePatterns: ['**/users/**', '**/profile/**', '**/accounts/**', '**/members/**'],
|
|
109
|
+
priority: 2
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
payments: {
|
|
113
|
+
name: 'Payment Processing',
|
|
114
|
+
category: 'core',
|
|
115
|
+
complexity: 'HIGH',
|
|
116
|
+
keywords: ['payment', 'stripe', 'paypal', 'invoice', 'billing', 'subscription', 'checkout', 'cart', 'order'],
|
|
117
|
+
filePatterns: ['**/payments/**', '**/billing/**', '**/checkout/**', '**/orders/**', '**/subscriptions/**'],
|
|
118
|
+
priority: 1
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
dataProcessing: {
|
|
122
|
+
name: 'Data Processing',
|
|
123
|
+
category: 'infrastructure',
|
|
124
|
+
complexity: 'HIGH',
|
|
125
|
+
keywords: ['process', 'transform', 'pipeline', 'batch', 'worker', 'job', 'queue', 'task', 'etl'],
|
|
126
|
+
filePatterns: ['**/workers/**', '**/jobs/**', '**/tasks/**', '**/pipelines/**', '**/queues/**'],
|
|
127
|
+
priority: 2
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
apiEndpoints: {
|
|
131
|
+
name: 'API Endpoints',
|
|
132
|
+
category: 'core',
|
|
133
|
+
complexity: 'MEDIUM',
|
|
134
|
+
keywords: ['api', 'endpoint', 'route', 'controller', 'handler', 'resource'],
|
|
135
|
+
filePatterns: ['**/routes/**', '**/api/**', '**/controllers/**', '**/handlers/**'],
|
|
136
|
+
priority: 3
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
database: {
|
|
140
|
+
name: 'Database Operations',
|
|
141
|
+
category: 'infrastructure',
|
|
142
|
+
complexity: 'MEDIUM',
|
|
143
|
+
keywords: ['model', 'schema', 'migration', 'repository', 'dao', 'orm', 'query', 'database'],
|
|
144
|
+
filePatterns: ['**/models/**', '**/schemas/**', '**/migrations/**', '**/repositories/**', '**/entities/**'],
|
|
145
|
+
priority: 2
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
notifications: {
|
|
149
|
+
name: 'Notifications',
|
|
150
|
+
category: 'features',
|
|
151
|
+
complexity: 'MEDIUM',
|
|
152
|
+
keywords: ['notification', 'email', 'sms', 'push', 'alert', 'message', 'mail', 'notify'],
|
|
153
|
+
filePatterns: ['**/notifications/**', '**/emails/**', '**/mailers/**', '**/messaging/**'],
|
|
154
|
+
priority: 3
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
fileHandling: {
|
|
158
|
+
name: 'File Handling',
|
|
159
|
+
category: 'features',
|
|
160
|
+
complexity: 'MEDIUM',
|
|
161
|
+
keywords: ['upload', 'download', 'file', 'storage', 's3', 'blob', 'attachment', 'media'],
|
|
162
|
+
filePatterns: ['**/uploads/**', '**/storage/**', '**/files/**', '**/media/**'],
|
|
163
|
+
priority: 3
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
search: {
|
|
167
|
+
name: 'Search',
|
|
168
|
+
category: 'features',
|
|
169
|
+
complexity: 'MEDIUM',
|
|
170
|
+
keywords: ['search', 'filter', 'query', 'elasticsearch', 'algolia', 'index', 'find'],
|
|
171
|
+
filePatterns: ['**/search/**', '**/filters/**'],
|
|
172
|
+
priority: 3
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
analytics: {
|
|
176
|
+
name: 'Analytics',
|
|
177
|
+
category: 'features',
|
|
178
|
+
complexity: 'MEDIUM',
|
|
179
|
+
keywords: ['analytics', 'tracking', 'metrics', 'stats', 'dashboard', 'report', 'insight'],
|
|
180
|
+
filePatterns: ['**/analytics/**', '**/tracking/**', '**/metrics/**', '**/reports/**'],
|
|
181
|
+
priority: 4
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
testing: {
|
|
185
|
+
name: 'Testing',
|
|
186
|
+
category: 'infrastructure',
|
|
187
|
+
complexity: 'LOW',
|
|
188
|
+
keywords: ['test', 'spec', 'mock', 'fixture', 'factory', 'stub'],
|
|
189
|
+
filePatterns: ['**/tests/**', '**/test/**', '**/__tests__/**', '**/spec/**'],
|
|
190
|
+
priority: 5
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
configuration: {
|
|
194
|
+
name: 'Configuration',
|
|
195
|
+
category: 'infrastructure',
|
|
196
|
+
complexity: 'LOW',
|
|
197
|
+
keywords: ['config', 'setting', 'env', 'constant', 'option'],
|
|
198
|
+
filePatterns: ['**/config/**', '**/settings/**', '**/constants/**'],
|
|
199
|
+
priority: 5
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Source file extensions by language
|
|
205
|
+
*/
|
|
206
|
+
const SOURCE_EXTENSIONS = {
|
|
207
|
+
javascript: ['.js', '.jsx', '.mjs', '.cjs'],
|
|
208
|
+
typescript: ['.ts', '.tsx', '.mts', '.cts'],
|
|
209
|
+
python: ['.py', '.ipynb', '.pyw'],
|
|
210
|
+
go: ['.go'],
|
|
211
|
+
rust: ['.rs'],
|
|
212
|
+
ruby: ['.rb'],
|
|
213
|
+
java: ['.java'],
|
|
214
|
+
csharp: ['.cs'],
|
|
215
|
+
php: ['.php']
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Directories to exclude from analysis
|
|
220
|
+
*/
|
|
221
|
+
const EXCLUDED_DIRS = [
|
|
222
|
+
'node_modules',
|
|
223
|
+
'.git',
|
|
224
|
+
'dist',
|
|
225
|
+
'build',
|
|
226
|
+
'out',
|
|
227
|
+
'.next',
|
|
228
|
+
'__pycache__',
|
|
229
|
+
'.venv',
|
|
230
|
+
'venv',
|
|
231
|
+
'vendor',
|
|
232
|
+
'target',
|
|
233
|
+
'.cache',
|
|
234
|
+
'coverage',
|
|
235
|
+
'.nyc_output'
|
|
236
|
+
];
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Find all source files in the project
|
|
240
|
+
* @param {string} projectRoot - Project root directory
|
|
241
|
+
* @param {string[]} languages - Languages to include
|
|
242
|
+
* @returns {Promise<string[]>}
|
|
243
|
+
*/
|
|
244
|
+
async function findSourceFiles(projectRoot, languages = null) {
|
|
245
|
+
const extensions = languages
|
|
246
|
+
? languages.flatMap(lang => SOURCE_EXTENSIONS[lang] || [])
|
|
247
|
+
: Object.values(SOURCE_EXTENSIONS).flat();
|
|
248
|
+
|
|
249
|
+
const pattern = `**/*{${extensions.join(',')}}`;
|
|
250
|
+
|
|
251
|
+
const files = await glob(pattern, {
|
|
252
|
+
cwd: projectRoot,
|
|
253
|
+
nodir: true,
|
|
254
|
+
ignore: EXCLUDED_DIRS.map(d => `**/${d}/**`)
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
return files;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Discover entry points in the codebase
|
|
262
|
+
* @param {string} projectRoot - Project root directory
|
|
263
|
+
* @param {string[]} sourceFiles - List of source files
|
|
264
|
+
* @param {object} techStack - Detected tech stack
|
|
265
|
+
* @returns {Promise<object[]>}
|
|
266
|
+
*/
|
|
267
|
+
async function discoverEntryPoints(projectRoot, sourceFiles, techStack) {
|
|
268
|
+
const entryPoints = [];
|
|
269
|
+
const frameworks = techStack.frameworks || [];
|
|
270
|
+
|
|
271
|
+
// Get relevant pattern sets
|
|
272
|
+
const patternSets = [];
|
|
273
|
+
for (const framework of frameworks) {
|
|
274
|
+
if (ENTRY_PATTERNS[framework]) {
|
|
275
|
+
patternSets.push(ENTRY_PATTERNS[framework]);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// If no specific frameworks, try all patterns
|
|
280
|
+
if (patternSets.length === 0) {
|
|
281
|
+
patternSets.push(...Object.values(ENTRY_PATTERNS));
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
for (const file of sourceFiles) {
|
|
285
|
+
const filePath = path.join(projectRoot, file);
|
|
286
|
+
|
|
287
|
+
// Check if file matches any file patterns
|
|
288
|
+
let isRelevant = false;
|
|
289
|
+
for (const patternSet of patternSets) {
|
|
290
|
+
for (const fp of patternSet.filePatterns) {
|
|
291
|
+
if (minimatch(file, fp)) {
|
|
292
|
+
isRelevant = true;
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if (isRelevant) break;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// For efficiency, skip if not a likely entry point file
|
|
300
|
+
if (!isRelevant && sourceFiles.length > 100) {
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
try {
|
|
305
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
306
|
+
const lines = content.split('\n');
|
|
307
|
+
|
|
308
|
+
for (const patternSet of patternSets) {
|
|
309
|
+
for (const pattern of patternSet.patterns) {
|
|
310
|
+
let match;
|
|
311
|
+
pattern.lastIndex = 0;
|
|
312
|
+
|
|
313
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
314
|
+
const lineNumber = content.substring(0, match.index).split('\n').length;
|
|
315
|
+
const lineContent = lines[lineNumber - 1] || '';
|
|
316
|
+
|
|
317
|
+
// Extract route/path from the match context
|
|
318
|
+
const routeMatch = lineContent.match(/['"]([^'"]+)['"]/);
|
|
319
|
+
const route = routeMatch ? routeMatch[1] : null;
|
|
320
|
+
|
|
321
|
+
entryPoints.push({
|
|
322
|
+
file,
|
|
323
|
+
line: lineNumber,
|
|
324
|
+
match: match[0].trim(),
|
|
325
|
+
context: lineContent.trim(),
|
|
326
|
+
route,
|
|
327
|
+
method: extractMethod(match[0]),
|
|
328
|
+
framework: Object.keys(ENTRY_PATTERNS).find(k =>
|
|
329
|
+
ENTRY_PATTERNS[k].patterns.includes(pattern)
|
|
330
|
+
) || 'unknown'
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
} catch (e) {
|
|
336
|
+
// Skip files that can't be read
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Deduplicate by file:line
|
|
341
|
+
const seen = new Set();
|
|
342
|
+
return entryPoints.filter(ep => {
|
|
343
|
+
const key = `${ep.file}:${ep.line}`;
|
|
344
|
+
if (seen.has(key)) return false;
|
|
345
|
+
seen.add(key);
|
|
346
|
+
return true;
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Extract HTTP method from pattern match
|
|
352
|
+
* @param {string} matchText - The matched text
|
|
353
|
+
* @returns {string|null}
|
|
354
|
+
*/
|
|
355
|
+
function extractMethod(matchText) {
|
|
356
|
+
const methodMatch = matchText.match(/\.(get|post|put|delete|patch|all|use|GET|POST|PUT|DELETE|PATCH)/i);
|
|
357
|
+
return methodMatch ? methodMatch[1].toUpperCase() : null;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Simple minimatch implementation for glob patterns
|
|
362
|
+
* @param {string} file - File path
|
|
363
|
+
* @param {string} pattern - Glob pattern
|
|
364
|
+
* @returns {boolean}
|
|
365
|
+
*/
|
|
366
|
+
function minimatch(file, pattern) {
|
|
367
|
+
// Convert glob to regex
|
|
368
|
+
const regexPattern = pattern
|
|
369
|
+
.replace(/\*\*/g, '{{DOUBLE}}')
|
|
370
|
+
.replace(/\*/g, '[^/]*')
|
|
371
|
+
.replace(/{{DOUBLE}}/g, '.*')
|
|
372
|
+
.replace(/\//g, '\\/');
|
|
373
|
+
|
|
374
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
375
|
+
return regex.test(file);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Discover workflows in the codebase
|
|
380
|
+
* @param {string} projectRoot - Project root directory
|
|
381
|
+
* @param {string[]} sourceFiles - List of source files
|
|
382
|
+
* @returns {Promise<object[]>}
|
|
383
|
+
*/
|
|
384
|
+
async function discoverWorkflows(projectRoot, sourceFiles) {
|
|
385
|
+
const workflows = [];
|
|
386
|
+
|
|
387
|
+
for (const [workflowType, heuristics] of Object.entries(WORKFLOW_HEURISTICS)) {
|
|
388
|
+
const matchingFiles = new Set();
|
|
389
|
+
let keywordScore = 0;
|
|
390
|
+
|
|
391
|
+
// Check file patterns
|
|
392
|
+
for (const pattern of heuristics.filePatterns) {
|
|
393
|
+
for (const file of sourceFiles) {
|
|
394
|
+
if (minimatch(file, pattern)) {
|
|
395
|
+
matchingFiles.add(file);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Check keywords in file content (sample first 50 matching files)
|
|
401
|
+
const filesToCheck = Array.from(matchingFiles).slice(0, 50);
|
|
402
|
+
|
|
403
|
+
for (const file of filesToCheck) {
|
|
404
|
+
try {
|
|
405
|
+
const content = fs.readFileSync(path.join(projectRoot, file), 'utf-8').toLowerCase();
|
|
406
|
+
const matchCount = heuristics.keywords.filter(kw => content.includes(kw)).length;
|
|
407
|
+
keywordScore += matchCount;
|
|
408
|
+
} catch {
|
|
409
|
+
// Skip unreadable files
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Also check remaining source files for keyword matches
|
|
414
|
+
if (filesToCheck.length === 0) {
|
|
415
|
+
const sampled = sourceFiles.slice(0, 200);
|
|
416
|
+
for (const file of sampled) {
|
|
417
|
+
try {
|
|
418
|
+
const content = fs.readFileSync(path.join(projectRoot, file), 'utf-8').toLowerCase();
|
|
419
|
+
const matchCount = heuristics.keywords.filter(kw => content.includes(kw)).length;
|
|
420
|
+
if (matchCount >= 2) {
|
|
421
|
+
matchingFiles.add(file);
|
|
422
|
+
keywordScore += matchCount;
|
|
423
|
+
}
|
|
424
|
+
} catch {
|
|
425
|
+
// Skip unreadable files
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (matchingFiles.size > 0 || keywordScore >= 3) {
|
|
431
|
+
workflows.push({
|
|
432
|
+
type: workflowType,
|
|
433
|
+
name: heuristics.name,
|
|
434
|
+
category: heuristics.category,
|
|
435
|
+
complexity: heuristics.complexity,
|
|
436
|
+
priority: heuristics.priority,
|
|
437
|
+
files: Array.from(matchingFiles),
|
|
438
|
+
fileCount: matchingFiles.size,
|
|
439
|
+
keywordScore,
|
|
440
|
+
confidence: calculateConfidence(matchingFiles.size, keywordScore),
|
|
441
|
+
status: 'discovered'
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// Sort by priority and confidence
|
|
447
|
+
workflows.sort((a, b) => {
|
|
448
|
+
if (a.priority !== b.priority) return a.priority - b.priority;
|
|
449
|
+
return b.confidence - a.confidence;
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
return workflows;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Calculate workflow confidence score
|
|
457
|
+
* @param {number} fileCount - Number of matching files
|
|
458
|
+
* @param {number} keywordScore - Keyword match score
|
|
459
|
+
* @returns {number}
|
|
460
|
+
*/
|
|
461
|
+
function calculateConfidence(fileCount, keywordScore) {
|
|
462
|
+
// Scale: 0-100
|
|
463
|
+
const fileScore = Math.min(fileCount * 10, 50);
|
|
464
|
+
const kwScore = Math.min(keywordScore * 5, 50);
|
|
465
|
+
return fileScore + kwScore;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Map the architecture of the codebase
|
|
470
|
+
* @param {string} projectRoot - Project root directory
|
|
471
|
+
* @returns {Promise<object>}
|
|
472
|
+
*/
|
|
473
|
+
async function mapArchitecture(projectRoot) {
|
|
474
|
+
const architecture = {
|
|
475
|
+
directories: [],
|
|
476
|
+
layers: [],
|
|
477
|
+
components: [],
|
|
478
|
+
directoryTree: ''
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
// Get top-level directories
|
|
482
|
+
const entries = fs.readdirSync(projectRoot, { withFileTypes: true });
|
|
483
|
+
const dirs = entries
|
|
484
|
+
.filter(e => e.isDirectory() && !EXCLUDED_DIRS.includes(e.name) && !e.name.startsWith('.'))
|
|
485
|
+
.map(e => e.name);
|
|
486
|
+
|
|
487
|
+
architecture.directories = dirs;
|
|
488
|
+
|
|
489
|
+
// Identify architectural layers
|
|
490
|
+
const layerPatterns = {
|
|
491
|
+
presentation: ['components', 'views', 'pages', 'ui', 'templates', 'layouts', 'frontend'],
|
|
492
|
+
application: ['services', 'usecases', 'handlers', 'controllers', 'actions'],
|
|
493
|
+
domain: ['models', 'entities', 'domain', 'core', 'business'],
|
|
494
|
+
infrastructure: ['repositories', 'database', 'db', 'cache', 'external', 'integrations', 'adapters'],
|
|
495
|
+
api: ['api', 'routes', 'endpoints', 'rest', 'graphql'],
|
|
496
|
+
config: ['config', 'settings', 'constants'],
|
|
497
|
+
tests: ['tests', 'test', '__tests__', 'spec']
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
for (const [layer, patterns] of Object.entries(layerPatterns)) {
|
|
501
|
+
const matchingDirs = dirs.filter(d =>
|
|
502
|
+
patterns.some(p => d.toLowerCase().includes(p))
|
|
503
|
+
);
|
|
504
|
+
if (matchingDirs.length > 0) {
|
|
505
|
+
architecture.layers.push({
|
|
506
|
+
name: layer,
|
|
507
|
+
directories: matchingDirs,
|
|
508
|
+
purpose: getLayerPurpose(layer)
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// Build directory tree (max 3 levels)
|
|
514
|
+
architecture.directoryTree = buildDirectoryTree(projectRoot, 3);
|
|
515
|
+
|
|
516
|
+
return architecture;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Get purpose description for a layer
|
|
521
|
+
* @param {string} layer - Layer name
|
|
522
|
+
* @returns {string}
|
|
523
|
+
*/
|
|
524
|
+
function getLayerPurpose(layer) {
|
|
525
|
+
const purposes = {
|
|
526
|
+
presentation: 'User interface components and views',
|
|
527
|
+
application: 'Application logic and service orchestration',
|
|
528
|
+
domain: 'Core business logic and domain models',
|
|
529
|
+
infrastructure: 'External systems and data persistence',
|
|
530
|
+
api: 'API endpoints and route handling',
|
|
531
|
+
config: 'Configuration and environment settings',
|
|
532
|
+
tests: 'Test files and fixtures'
|
|
533
|
+
};
|
|
534
|
+
return purposes[layer] || 'Unknown';
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Build ASCII directory tree
|
|
539
|
+
* @param {string} dir - Directory path
|
|
540
|
+
* @param {number} maxDepth - Maximum depth
|
|
541
|
+
* @param {string} prefix - Current prefix
|
|
542
|
+
* @param {number} depth - Current depth
|
|
543
|
+
* @returns {string}
|
|
544
|
+
*/
|
|
545
|
+
function buildDirectoryTree(dir, maxDepth, prefix = '', depth = 0) {
|
|
546
|
+
if (depth >= maxDepth) return '';
|
|
547
|
+
|
|
548
|
+
let tree = '';
|
|
549
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
550
|
+
const filtered = entries.filter(e =>
|
|
551
|
+
!EXCLUDED_DIRS.includes(e.name) &&
|
|
552
|
+
!e.name.startsWith('.') &&
|
|
553
|
+
e.isDirectory()
|
|
554
|
+
);
|
|
555
|
+
|
|
556
|
+
filtered.forEach((entry, index) => {
|
|
557
|
+
const isLast = index === filtered.length - 1;
|
|
558
|
+
const connector = isLast ? '└── ' : '├── ';
|
|
559
|
+
const childPrefix = isLast ? ' ' : '│ ';
|
|
560
|
+
|
|
561
|
+
tree += `${prefix}${connector}${entry.name}/\n`;
|
|
562
|
+
tree += buildDirectoryTree(
|
|
563
|
+
path.join(dir, entry.name),
|
|
564
|
+
maxDepth,
|
|
565
|
+
prefix + childPrefix,
|
|
566
|
+
depth + 1
|
|
567
|
+
);
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
return tree;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* Extract dependencies from the project
|
|
575
|
+
* @param {string} projectRoot - Project root directory
|
|
576
|
+
* @returns {Promise<object[]>}
|
|
577
|
+
*/
|
|
578
|
+
async function extractDependencies(projectRoot) {
|
|
579
|
+
const dependencies = [];
|
|
580
|
+
|
|
581
|
+
// Check package.json (Node.js)
|
|
582
|
+
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
583
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
584
|
+
try {
|
|
585
|
+
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
586
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
587
|
+
|
|
588
|
+
for (const [name, version] of Object.entries(deps)) {
|
|
589
|
+
dependencies.push({
|
|
590
|
+
name,
|
|
591
|
+
version,
|
|
592
|
+
type: pkg.devDependencies?.[name] ? 'dev' : 'runtime',
|
|
593
|
+
ecosystem: 'npm'
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
} catch {
|
|
597
|
+
// Ignore parse errors
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// Check requirements.txt (Python)
|
|
602
|
+
const requirementsPath = path.join(projectRoot, 'requirements.txt');
|
|
603
|
+
if (fs.existsSync(requirementsPath)) {
|
|
604
|
+
try {
|
|
605
|
+
const content = fs.readFileSync(requirementsPath, 'utf-8');
|
|
606
|
+
const lines = content.split('\n').filter(l => l.trim() && !l.startsWith('#'));
|
|
607
|
+
|
|
608
|
+
for (const line of lines) {
|
|
609
|
+
const match = line.match(/^([a-zA-Z0-9_-]+)([=<>!~]+.*)?/);
|
|
610
|
+
if (match) {
|
|
611
|
+
dependencies.push({
|
|
612
|
+
name: match[1],
|
|
613
|
+
version: match[2] || '*',
|
|
614
|
+
type: 'runtime',
|
|
615
|
+
ecosystem: 'pip'
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
} catch {
|
|
620
|
+
// Ignore parse errors
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// Check go.mod (Go)
|
|
625
|
+
const goModPath = path.join(projectRoot, 'go.mod');
|
|
626
|
+
if (fs.existsSync(goModPath)) {
|
|
627
|
+
try {
|
|
628
|
+
const content = fs.readFileSync(goModPath, 'utf-8');
|
|
629
|
+
const requireMatch = content.match(/require\s*\(([\s\S]*?)\)/);
|
|
630
|
+
if (requireMatch) {
|
|
631
|
+
const lines = requireMatch[1].split('\n').filter(l => l.trim());
|
|
632
|
+
for (const line of lines) {
|
|
633
|
+
const match = line.trim().match(/^([^\s]+)\s+([^\s]+)/);
|
|
634
|
+
if (match) {
|
|
635
|
+
dependencies.push({
|
|
636
|
+
name: match[1],
|
|
637
|
+
version: match[2],
|
|
638
|
+
type: 'runtime',
|
|
639
|
+
ecosystem: 'go'
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
} catch {
|
|
645
|
+
// Ignore parse errors
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
return dependencies;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Count lines of code by file
|
|
654
|
+
* @param {string} projectRoot - Project root directory
|
|
655
|
+
* @param {string[]} sourceFiles - List of source files
|
|
656
|
+
* @returns {Promise<object>}
|
|
657
|
+
*/
|
|
658
|
+
async function countLinesOfCode(projectRoot, sourceFiles) {
|
|
659
|
+
const locByFile = {};
|
|
660
|
+
let totalLoc = 0;
|
|
661
|
+
|
|
662
|
+
for (const file of sourceFiles.slice(0, 500)) { // Limit for performance
|
|
663
|
+
try {
|
|
664
|
+
const content = fs.readFileSync(path.join(projectRoot, file), 'utf-8');
|
|
665
|
+
const lines = content.split('\n').filter(l => l.trim()).length;
|
|
666
|
+
locByFile[file] = lines;
|
|
667
|
+
totalLoc += lines;
|
|
668
|
+
} catch {
|
|
669
|
+
// Skip unreadable files
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
return { byFile: locByFile, total: totalLoc };
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
/**
|
|
677
|
+
* Main analysis function
|
|
678
|
+
* @param {string} projectRoot - Project root directory
|
|
679
|
+
* @param {object} options - Analysis options
|
|
680
|
+
* @returns {Promise<object>}
|
|
681
|
+
*/
|
|
682
|
+
async function analyzeCodebase(projectRoot, options = {}) {
|
|
683
|
+
const { techStack = {} } = options;
|
|
684
|
+
|
|
685
|
+
// Find all source files
|
|
686
|
+
const sourceFiles = await findSourceFiles(projectRoot, techStack.languages);
|
|
687
|
+
|
|
688
|
+
// Run all analyses
|
|
689
|
+
const [entryPoints, workflows, architecture, dependencies, loc] = await Promise.all([
|
|
690
|
+
discoverEntryPoints(projectRoot, sourceFiles, techStack),
|
|
691
|
+
discoverWorkflows(projectRoot, sourceFiles),
|
|
692
|
+
mapArchitecture(projectRoot),
|
|
693
|
+
extractDependencies(projectRoot),
|
|
694
|
+
countLinesOfCode(projectRoot, sourceFiles)
|
|
695
|
+
]);
|
|
696
|
+
|
|
697
|
+
return {
|
|
698
|
+
projectRoot,
|
|
699
|
+
sourceFiles: sourceFiles.length,
|
|
700
|
+
entryPoints,
|
|
701
|
+
workflows,
|
|
702
|
+
architecture,
|
|
703
|
+
dependencies,
|
|
704
|
+
linesOfCode: loc,
|
|
705
|
+
analyzedAt: new Date().toISOString(),
|
|
706
|
+
summary: {
|
|
707
|
+
totalFiles: sourceFiles.length,
|
|
708
|
+
entryPointCount: entryPoints.length,
|
|
709
|
+
workflowCount: workflows.length,
|
|
710
|
+
layerCount: architecture.layers.length,
|
|
711
|
+
dependencyCount: dependencies.length,
|
|
712
|
+
totalLoc: loc.total
|
|
713
|
+
}
|
|
714
|
+
};
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
module.exports = {
|
|
718
|
+
analyzeCodebase,
|
|
719
|
+
findSourceFiles,
|
|
720
|
+
discoverEntryPoints,
|
|
721
|
+
discoverWorkflows,
|
|
722
|
+
mapArchitecture,
|
|
723
|
+
extractDependencies,
|
|
724
|
+
countLinesOfCode,
|
|
725
|
+
ENTRY_PATTERNS,
|
|
726
|
+
WORKFLOW_HEURISTICS,
|
|
727
|
+
SOURCE_EXTENSIONS,
|
|
728
|
+
EXCLUDED_DIRS
|
|
729
|
+
};
|