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.
Files changed (136) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +171 -0
  3. package/bin/create-ai-context.js +337 -0
  4. package/lib/adapters/antigravity.js +160 -0
  5. package/lib/adapters/claude.js +122 -0
  6. package/lib/adapters/cline.js +111 -0
  7. package/lib/adapters/copilot.js +117 -0
  8. package/lib/adapters/index.js +69 -0
  9. package/lib/ai-context-generator.js +234 -0
  10. package/lib/ai-orchestrator.js +431 -0
  11. package/lib/call-tracer.js +444 -0
  12. package/lib/detector.js +726 -0
  13. package/lib/environment-detector.js +239 -0
  14. package/lib/index.js +310 -0
  15. package/lib/installer.js +418 -0
  16. package/lib/migrate.js +319 -0
  17. package/lib/placeholder.js +541 -0
  18. package/lib/prompts.js +287 -0
  19. package/lib/spinner.js +60 -0
  20. package/lib/static-analyzer.js +729 -0
  21. package/lib/template-populator.js +843 -0
  22. package/lib/template-renderer.js +382 -0
  23. package/lib/validate.js +155 -0
  24. package/package.json +70 -0
  25. package/templates/AI_CONTEXT.md.template +245 -0
  26. package/templates/base/README.md +257 -0
  27. package/templates/base/RPI_WORKFLOW_PLAN.md +320 -0
  28. package/templates/base/agents/api-developer.md +76 -0
  29. package/templates/base/agents/context-engineer.md +525 -0
  30. package/templates/base/agents/core-architect.md +76 -0
  31. package/templates/base/agents/database-ops.md +76 -0
  32. package/templates/base/agents/deployment-ops.md +76 -0
  33. package/templates/base/agents/integration-hub.md +76 -0
  34. package/templates/base/analytics/README.md +114 -0
  35. package/templates/base/automation/config.json +58 -0
  36. package/templates/base/automation/generators/code-mapper.js +308 -0
  37. package/templates/base/automation/generators/index-builder.js +321 -0
  38. package/templates/base/automation/hooks/post-commit.sh +83 -0
  39. package/templates/base/automation/hooks/pre-commit.sh +103 -0
  40. package/templates/base/ci-templates/README.md +108 -0
  41. package/templates/base/ci-templates/github-actions/context-check.yml +144 -0
  42. package/templates/base/ci-templates/github-actions/validate-docs.yml +105 -0
  43. package/templates/base/commands/analytics.md +238 -0
  44. package/templates/base/commands/auto-sync.md +172 -0
  45. package/templates/base/commands/collab.md +194 -0
  46. package/templates/base/commands/help.md +450 -0
  47. package/templates/base/commands/rpi-implement.md +115 -0
  48. package/templates/base/commands/rpi-plan.md +93 -0
  49. package/templates/base/commands/rpi-research.md +88 -0
  50. package/templates/base/commands/session-resume.md +144 -0
  51. package/templates/base/commands/session-save.md +112 -0
  52. package/templates/base/commands/validate-all.md +77 -0
  53. package/templates/base/commands/verify-docs-current.md +86 -0
  54. package/templates/base/config/base.json +57 -0
  55. package/templates/base/config/environments/development.json +13 -0
  56. package/templates/base/config/environments/production.json +17 -0
  57. package/templates/base/config/environments/staging.json +13 -0
  58. package/templates/base/config/local.json.example +21 -0
  59. package/templates/base/context/.meta/generated-at.json +18 -0
  60. package/templates/base/context/ARCHITECTURE_SNAPSHOT.md +156 -0
  61. package/templates/base/context/CODE_TO_WORKFLOW_MAP.md +94 -0
  62. package/templates/base/context/FILE_OWNERSHIP.md +57 -0
  63. package/templates/base/context/INTEGRATION_POINTS.md +92 -0
  64. package/templates/base/context/KNOWN_GOTCHAS.md +195 -0
  65. package/templates/base/context/TESTING_MAP.md +95 -0
  66. package/templates/base/context/WORKFLOW_INDEX.md +129 -0
  67. package/templates/base/context/workflows/WORKFLOW_TEMPLATE.md +294 -0
  68. package/templates/base/indexes/agents/CAPABILITY_MATRIX.md +255 -0
  69. package/templates/base/indexes/agents/CATEGORY_INDEX.md +44 -0
  70. package/templates/base/indexes/code/CATEGORY_INDEX.md +38 -0
  71. package/templates/base/indexes/routing/CATEGORY_INDEX.md +39 -0
  72. package/templates/base/indexes/search/CATEGORY_INDEX.md +39 -0
  73. package/templates/base/indexes/workflows/CATEGORY_INDEX.md +38 -0
  74. package/templates/base/knowledge/README.md +98 -0
  75. package/templates/base/knowledge/sessions/README.md +88 -0
  76. package/templates/base/knowledge/sessions/TEMPLATE.md +150 -0
  77. package/templates/base/knowledge/shared/decisions/0001-adopt-context-engineering.md +144 -0
  78. package/templates/base/knowledge/shared/decisions/README.md +49 -0
  79. package/templates/base/knowledge/shared/decisions/TEMPLATE.md +123 -0
  80. package/templates/base/knowledge/shared/patterns/README.md +62 -0
  81. package/templates/base/knowledge/shared/patterns/TEMPLATE.md +120 -0
  82. package/templates/base/plans/PLAN_TEMPLATE.md +250 -0
  83. package/templates/base/plans/active/.gitkeep +0 -0
  84. package/templates/base/plans/completed/.gitkeep +0 -0
  85. package/templates/base/research/RESEARCH_TEMPLATE.md +153 -0
  86. package/templates/base/research/active/.gitkeep +0 -0
  87. package/templates/base/research/completed/.gitkeep +0 -0
  88. package/templates/base/schemas/agent.schema.json +141 -0
  89. package/templates/base/schemas/anchors.schema.json +54 -0
  90. package/templates/base/schemas/automation.schema.json +93 -0
  91. package/templates/base/schemas/command.schema.json +134 -0
  92. package/templates/base/schemas/hashes.schema.json +40 -0
  93. package/templates/base/schemas/manifest.schema.json +117 -0
  94. package/templates/base/schemas/plan.schema.json +136 -0
  95. package/templates/base/schemas/research.schema.json +115 -0
  96. package/templates/base/schemas/roles.schema.json +34 -0
  97. package/templates/base/schemas/session.schema.json +77 -0
  98. package/templates/base/schemas/settings.schema.json +244 -0
  99. package/templates/base/schemas/staleness.schema.json +53 -0
  100. package/templates/base/schemas/team-config.schema.json +42 -0
  101. package/templates/base/schemas/workflow.schema.json +126 -0
  102. package/templates/base/session/checkpoints/.gitkeep +2 -0
  103. package/templates/base/session/current/state.json +20 -0
  104. package/templates/base/session/history/.gitkeep +2 -0
  105. package/templates/base/settings.json +3 -0
  106. package/templates/base/standards/COMPATIBILITY.md +219 -0
  107. package/templates/base/standards/EXTENSION_GUIDELINES.md +280 -0
  108. package/templates/base/standards/QUALITY_CHECKLIST.md +211 -0
  109. package/templates/base/standards/README.md +66 -0
  110. package/templates/base/sync/anchors.json +6 -0
  111. package/templates/base/sync/hashes.json +6 -0
  112. package/templates/base/sync/staleness.json +10 -0
  113. package/templates/base/team/README.md +168 -0
  114. package/templates/base/team/config.json +79 -0
  115. package/templates/base/team/roles.json +145 -0
  116. package/templates/base/tools/bin/claude-context.js +151 -0
  117. package/templates/base/tools/lib/anchor-resolver.js +276 -0
  118. package/templates/base/tools/lib/config-loader.js +363 -0
  119. package/templates/base/tools/lib/detector.js +350 -0
  120. package/templates/base/tools/lib/diagnose.js +206 -0
  121. package/templates/base/tools/lib/drift-detector.js +373 -0
  122. package/templates/base/tools/lib/errors.js +199 -0
  123. package/templates/base/tools/lib/index.js +36 -0
  124. package/templates/base/tools/lib/init.js +192 -0
  125. package/templates/base/tools/lib/logger.js +230 -0
  126. package/templates/base/tools/lib/placeholder.js +201 -0
  127. package/templates/base/tools/lib/session-manager.js +354 -0
  128. package/templates/base/tools/lib/validate.js +521 -0
  129. package/templates/base/tools/package.json +49 -0
  130. package/templates/handlebars/antigravity.hbs +337 -0
  131. package/templates/handlebars/claude.hbs +184 -0
  132. package/templates/handlebars/cline.hbs +63 -0
  133. package/templates/handlebars/copilot.hbs +131 -0
  134. package/templates/handlebars/partials/gotcha-list.hbs +11 -0
  135. package/templates/handlebars/partials/header.hbs +3 -0
  136. 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
+ };