create-claude-context 1.0.0 → 1.2.1

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