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
package/lib/detector.js CHANGED
@@ -1,373 +1,456 @@
1
- /**
2
- * Claude Context Engineering - Tech Stack Detector
3
- *
4
- * Auto-detects technology stack from project files.
5
- */
6
-
7
- const fs = require('fs');
8
- const path = require('path');
9
- const { glob } = require('glob');
10
-
11
- /**
12
- * Tech stack detection signatures
13
- */
14
- const TECH_SIGNATURES = {
15
- // Languages
16
- languages: {
17
- python: {
18
- files: ['requirements.txt', 'pyproject.toml', 'setup.py', 'Pipfile', 'poetry.lock'],
19
- extensions: ['.py'],
20
- },
21
- javascript: {
22
- files: ['package.json', 'yarn.lock', 'pnpm-lock.yaml'],
23
- extensions: ['.js', '.mjs', '.cjs'],
24
- },
25
- typescript: {
26
- files: ['tsconfig.json'],
27
- extensions: ['.ts', '.tsx'],
28
- },
29
- go: {
30
- files: ['go.mod', 'go.sum'],
31
- extensions: ['.go'],
32
- },
33
- rust: {
34
- files: ['Cargo.toml', 'Cargo.lock'],
35
- extensions: ['.rs'],
36
- },
37
- java: {
38
- files: ['pom.xml', 'build.gradle', 'build.gradle.kts'],
39
- extensions: ['.java'],
40
- },
41
- csharp: {
42
- files: ['*.csproj', '*.sln'],
43
- extensions: ['.cs'],
44
- },
45
- ruby: {
46
- files: ['Gemfile', 'Gemfile.lock'],
47
- extensions: ['.rb'],
48
- },
49
- php: {
50
- files: ['composer.json', 'composer.lock'],
51
- extensions: ['.php'],
52
- },
53
- },
54
-
55
- // Frameworks
56
- frameworks: {
57
- // Python
58
- fastapi: {
59
- language: 'python',
60
- patterns: ['fastapi', 'FastAPI', '@router', 'APIRouter'],
61
- files: [],
62
- },
63
- django: {
64
- language: 'python',
65
- patterns: ['django.conf', 'INSTALLED_APPS', 'from django'],
66
- files: ['manage.py'],
67
- },
68
- flask: {
69
- language: 'python',
70
- patterns: ['Flask(__name__)', '@app.route', 'from flask'],
71
- files: [],
72
- },
73
-
74
- // JavaScript/TypeScript
75
- react: {
76
- language: 'javascript',
77
- patterns: ['react', 'React', 'useState', 'useEffect'],
78
- packageDeps: ['react', 'react-dom'],
79
- },
80
- nextjs: {
81
- language: 'javascript',
82
- patterns: ['next/'],
83
- files: ['next.config.js', 'next.config.mjs'],
84
- packageDeps: ['next'],
85
- },
86
- express: {
87
- language: 'javascript',
88
- patterns: ['express()', 'app.use', 'app.get', 'router.'],
89
- packageDeps: ['express'],
90
- },
91
- nestjs: {
92
- language: 'typescript',
93
- patterns: ['@nestjs/', '@Controller', '@Injectable'],
94
- packageDeps: ['@nestjs/core'],
95
- },
96
- vue: {
97
- language: 'javascript',
98
- patterns: ['<template>', 'Vue.'],
99
- packageDeps: ['vue'],
100
- },
101
-
102
- // Go
103
- gin: {
104
- language: 'go',
105
- patterns: ['gin.', 'gin-gonic'],
106
- files: [],
107
- },
108
- echo: {
109
- language: 'go',
110
- patterns: ['echo.', 'labstack/echo'],
111
- files: [],
112
- },
113
-
114
- // Ruby
115
- rails: {
116
- language: 'ruby',
117
- patterns: ['Rails.application', 'ActionController', 'ActiveRecord'],
118
- files: ['config/routes.rb'],
119
- },
120
-
121
- // Rust
122
- actix: {
123
- language: 'rust',
124
- patterns: ['actix_web', 'actix-web'],
125
- files: [],
126
- },
127
- axum: {
128
- language: 'rust',
129
- patterns: ['axum::'],
130
- files: [],
131
- },
132
- },
133
-
134
- // Databases
135
- databases: {
136
- postgresql: {
137
- patterns: ['postgresql', 'postgres', 'psycopg', 'pg'],
138
- envVars: ['DATABASE_URL', 'POSTGRES_'],
139
- },
140
- mysql: {
141
- patterns: ['mysql', 'mariadb'],
142
- envVars: ['MYSQL_'],
143
- },
144
- mongodb: {
145
- patterns: ['mongodb', 'mongoose', 'pymongo'],
146
- envVars: ['MONGO_'],
147
- },
148
- redis: {
149
- patterns: ['redis', 'ioredis'],
150
- envVars: ['REDIS_'],
151
- },
152
- sqlite: {
153
- patterns: ['sqlite', 'sqlite3'],
154
- files: ['*.db', '*.sqlite'],
155
- },
156
- },
157
- };
158
-
159
- /**
160
- * Detect technology stack
161
- */
162
- async function detectTechStack(projectRoot, options = {}) {
163
- const { hint = null } = options;
164
-
165
- const result = {
166
- stack: '',
167
- summary: '',
168
- languages: [],
169
- frameworks: [],
170
- databases: [],
171
- projectName: path.basename(projectRoot),
172
- fileCount: 0,
173
- loc: 0,
174
- };
175
-
176
- // If hint provided, use it
177
- if (hint) {
178
- result.stack = hint;
179
- result.summary = hint;
180
- return result;
181
- }
182
-
183
- // Detect languages
184
- for (const [lang, signature] of Object.entries(TECH_SIGNATURES.languages)) {
185
- // Check for signature files
186
- for (const sigFile of signature.files) {
187
- if (fs.existsSync(path.join(projectRoot, sigFile))) {
188
- if (!result.languages.includes(lang)) {
189
- result.languages.push(lang);
190
- }
191
- break;
192
- }
193
- }
194
-
195
- // Check for file extensions
196
- if (signature.extensions) {
197
- for (const ext of signature.extensions) {
198
- try {
199
- const files = await glob(`**/*${ext}`, {
200
- cwd: projectRoot,
201
- ignore: ['node_modules/**', 'vendor/**', '.git/**', 'dist/**', 'build/**'],
202
- nodir: true,
203
- });
204
-
205
- if (files.length > 0) {
206
- if (!result.languages.includes(lang)) {
207
- result.languages.push(lang);
208
- }
209
- result.fileCount += files.length;
210
- break;
211
- }
212
- } catch {
213
- // Ignore glob errors
214
- }
215
- }
216
- }
217
- }
218
-
219
- // Detect frameworks
220
- for (const [framework, signature] of Object.entries(TECH_SIGNATURES.frameworks)) {
221
- // Check for package.json dependencies
222
- if (signature.packageDeps) {
223
- const pkgPath = path.join(projectRoot, 'package.json');
224
- if (fs.existsSync(pkgPath)) {
225
- try {
226
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
227
- const allDeps = {
228
- ...pkg.dependencies,
229
- ...pkg.devDependencies,
230
- };
231
-
232
- for (const dep of signature.packageDeps) {
233
- if (allDeps[dep]) {
234
- if (!result.frameworks.includes(framework)) {
235
- result.frameworks.push(framework);
236
- }
237
- break;
238
- }
239
- }
240
- } catch {
241
- // Ignore JSON parse errors
242
- }
243
- }
244
- }
245
-
246
- // Check for signature files
247
- if (signature.files) {
248
- for (const sigFile of signature.files) {
249
- if (fs.existsSync(path.join(projectRoot, sigFile))) {
250
- if (!result.frameworks.includes(framework)) {
251
- result.frameworks.push(framework);
252
- }
253
- break;
254
- }
255
- }
256
- }
257
-
258
- // Check for patterns in source files (sample)
259
- if (signature.patterns && result.languages.includes(signature.language)) {
260
- // Sample a few source files
261
- const ext = TECH_SIGNATURES.languages[signature.language]?.extensions?.[0];
262
- if (ext) {
263
- try {
264
- const sampleFiles = await glob(`**/*${ext}`, {
265
- cwd: projectRoot,
266
- ignore: ['node_modules/**', 'vendor/**', '.git/**'],
267
- nodir: true,
268
- });
269
-
270
- // Check first 5 files
271
- for (const file of sampleFiles.slice(0, 5)) {
272
- try {
273
- const content = fs.readFileSync(path.join(projectRoot, file), 'utf8');
274
-
275
- for (const pattern of signature.patterns) {
276
- if (content.includes(pattern)) {
277
- if (!result.frameworks.includes(framework)) {
278
- result.frameworks.push(framework);
279
- }
280
- break;
281
- }
282
- }
283
- } catch {
284
- // Ignore file read errors
285
- }
286
- }
287
- } catch {
288
- // Ignore glob errors
289
- }
290
- }
291
- }
292
- }
293
-
294
- // Detect databases
295
- for (const [db, signature] of Object.entries(TECH_SIGNATURES.databases)) {
296
- // Check environment files
297
- const envFiles = ['.env', '.env.example', '.env.local'];
298
- for (const envFile of envFiles) {
299
- const envPath = path.join(projectRoot, envFile);
300
- if (fs.existsSync(envPath)) {
301
- try {
302
- const content = fs.readFileSync(envPath, 'utf8');
303
-
304
- if (signature.envVars) {
305
- for (const envVar of signature.envVars) {
306
- if (content.includes(envVar)) {
307
- if (!result.databases.includes(db)) {
308
- result.databases.push(db);
309
- }
310
- break;
311
- }
312
- }
313
- }
314
- } catch {
315
- // Ignore read errors
316
- }
317
- }
318
- }
319
-
320
- // Check for patterns in config files
321
- if (signature.patterns) {
322
- const configFiles = ['package.json', 'requirements.txt', 'pyproject.toml', 'Cargo.toml'];
323
- for (const configFile of configFiles) {
324
- const configPath = path.join(projectRoot, configFile);
325
- if (fs.existsSync(configPath)) {
326
- try {
327
- const content = fs.readFileSync(configPath, 'utf8').toLowerCase();
328
-
329
- for (const pattern of signature.patterns) {
330
- if (content.includes(pattern.toLowerCase())) {
331
- if (!result.databases.includes(db)) {
332
- result.databases.push(db);
333
- }
334
- break;
335
- }
336
- }
337
- } catch {
338
- // Ignore read errors
339
- }
340
- }
341
- }
342
- }
343
- }
344
-
345
- // Build stack string
346
- const stackParts = [];
347
- if (result.languages.length > 0) {
348
- stackParts.push(result.languages.map(l => capitalize(l)).join(', '));
349
- }
350
- if (result.frameworks.length > 0) {
351
- stackParts.push(result.frameworks.map(f => capitalize(f)).join(', '));
352
- }
353
- if (result.databases.length > 0) {
354
- stackParts.push(result.databases.map(d => capitalize(d)).join(', '));
355
- }
356
-
357
- result.stack = stackParts.join(' + ') || 'Unknown';
358
- result.summary = result.stack;
359
-
360
- return result;
361
- }
362
-
363
- /**
364
- * Capitalize first letter
365
- */
366
- function capitalize(str) {
367
- return str.charAt(0).toUpperCase() + str.slice(1);
368
- }
369
-
370
- module.exports = {
371
- detectTechStack,
372
- TECH_SIGNATURES,
373
- };
1
+ /**
2
+ * Claude Context Engineering - Tech Stack Detector
3
+ *
4
+ * Auto-detects technology stack from project files.
5
+ */
6
+
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+ const { glob } = require('glob');
10
+
11
+ /**
12
+ * Tech stack detection signatures
13
+ */
14
+ const TECH_SIGNATURES = {
15
+ // Languages
16
+ languages: {
17
+ python: {
18
+ files: ['requirements.txt', 'pyproject.toml', 'setup.py', 'Pipfile', 'poetry.lock'],
19
+ extensions: ['.py'],
20
+ },
21
+ javascript: {
22
+ files: ['package.json', 'yarn.lock', 'pnpm-lock.yaml'],
23
+ extensions: ['.js', '.mjs', '.cjs'],
24
+ },
25
+ typescript: {
26
+ files: ['tsconfig.json'],
27
+ extensions: ['.ts', '.tsx'],
28
+ },
29
+ go: {
30
+ files: ['go.mod', 'go.sum'],
31
+ extensions: ['.go'],
32
+ },
33
+ rust: {
34
+ files: ['Cargo.toml', 'Cargo.lock'],
35
+ extensions: ['.rs'],
36
+ },
37
+ java: {
38
+ files: ['pom.xml', 'build.gradle', 'build.gradle.kts'],
39
+ extensions: ['.java'],
40
+ },
41
+ csharp: {
42
+ files: ['*.csproj', '*.sln'],
43
+ extensions: ['.cs'],
44
+ },
45
+ ruby: {
46
+ files: ['Gemfile', 'Gemfile.lock'],
47
+ extensions: ['.rb'],
48
+ },
49
+ php: {
50
+ files: ['composer.json', 'composer.lock'],
51
+ extensions: ['.php'],
52
+ },
53
+ },
54
+
55
+ // Frameworks
56
+ frameworks: {
57
+ // Python
58
+ fastapi: {
59
+ language: 'python',
60
+ patterns: ['fastapi', 'FastAPI', '@router', 'APIRouter'],
61
+ files: [],
62
+ },
63
+ django: {
64
+ language: 'python',
65
+ patterns: ['django.conf', 'INSTALLED_APPS', 'from django'],
66
+ files: ['manage.py'],
67
+ },
68
+ flask: {
69
+ language: 'python',
70
+ patterns: ['Flask(__name__)', '@app.route', 'from flask'],
71
+ files: [],
72
+ },
73
+
74
+ // JavaScript/TypeScript
75
+ react: {
76
+ language: 'javascript',
77
+ patterns: ['react', 'React', 'useState', 'useEffect'],
78
+ packageDeps: ['react', 'react-dom'],
79
+ },
80
+ nextjs: {
81
+ language: 'javascript',
82
+ patterns: ['next/'],
83
+ files: ['next.config.js', 'next.config.mjs'],
84
+ packageDeps: ['next'],
85
+ },
86
+ express: {
87
+ language: 'javascript',
88
+ patterns: ['express()', 'app.use', 'app.get', 'router.'],
89
+ packageDeps: ['express'],
90
+ },
91
+ nestjs: {
92
+ language: 'typescript',
93
+ patterns: ['@nestjs/', '@Controller', '@Injectable'],
94
+ packageDeps: ['@nestjs/core'],
95
+ },
96
+ vue: {
97
+ language: 'javascript',
98
+ patterns: ['<template>', 'Vue.'],
99
+ packageDeps: ['vue'],
100
+ },
101
+
102
+ // Go
103
+ gin: {
104
+ language: 'go',
105
+ patterns: ['gin.', 'gin-gonic'],
106
+ files: [],
107
+ },
108
+ echo: {
109
+ language: 'go',
110
+ patterns: ['echo.', 'labstack/echo'],
111
+ files: [],
112
+ },
113
+
114
+ // Ruby
115
+ rails: {
116
+ language: 'ruby',
117
+ patterns: ['Rails.application', 'ActionController', 'ActiveRecord'],
118
+ files: ['config/routes.rb'],
119
+ },
120
+
121
+ // Rust
122
+ actix: {
123
+ language: 'rust',
124
+ patterns: ['actix_web', 'actix-web'],
125
+ files: [],
126
+ },
127
+ axum: {
128
+ language: 'rust',
129
+ patterns: ['axum::'],
130
+ files: [],
131
+ },
132
+ },
133
+
134
+ // Databases
135
+ databases: {
136
+ postgresql: {
137
+ patterns: ['postgresql', 'postgres', 'psycopg', 'pg'],
138
+ envVars: ['DATABASE_URL', 'POSTGRES_'],
139
+ },
140
+ mysql: {
141
+ patterns: ['mysql', 'mariadb'],
142
+ envVars: ['MYSQL_'],
143
+ },
144
+ mongodb: {
145
+ patterns: ['mongodb', 'mongoose', 'pymongo'],
146
+ envVars: ['MONGO_'],
147
+ },
148
+ redis: {
149
+ patterns: ['redis', 'ioredis'],
150
+ envVars: ['REDIS_'],
151
+ },
152
+ sqlite: {
153
+ patterns: ['sqlite', 'sqlite3'],
154
+ files: ['*.db', '*.sqlite'],
155
+ },
156
+ },
157
+ };
158
+
159
+ /**
160
+ * Entry point patterns for different frameworks
161
+ */
162
+ const ENTRY_POINT_PATTERNS = {
163
+ express: {
164
+ regex: /app\.(get|post|put|delete|patch)\s*\(\s*['"`]([^'"`]+)['"`]/g,
165
+ extractor: (match) => ({ method: match[1].toUpperCase(), route: match[2] })
166
+ },
167
+ fastapi: {
168
+ regex: /@app\.(get|post|put|delete|patch)\s*\(\s*['"`]([^'"`]+)['"`]/g,
169
+ extractor: (match) => ({ method: match[1].toUpperCase(), route: match[2] })
170
+ },
171
+ nextjs: {
172
+ regex: /export\s+(async\s+)?function\s+(GET|POST|PUT|DELETE|PATCH)/g,
173
+ extractor: (match) => ({ method: match[2], route: 'app-router' })
174
+ },
175
+ django: {
176
+ regex: /path\s*\(\s*['"`]([^'"`]+)['"`]/g,
177
+ extractor: (match) => ({ method: 'ANY', route: match[1] })
178
+ },
179
+ rails: {
180
+ regex: /(get|post|put|delete|patch)\s+['"`]([^'"`]+)['"`]/g,
181
+ extractor: (match) => ({ method: match[1].toUpperCase(), route: match[2] })
182
+ },
183
+ nestjs: {
184
+ regex: /@(Get|Post|Put|Delete|Patch)\s*\(\s*['"`]?([^'"`)\s]*)['"`]?\s*\)/g,
185
+ extractor: (match) => ({ method: match[1].toUpperCase(), route: match[2] || '/' })
186
+ }
187
+ };
188
+
189
+ /**
190
+ * Detect entry points in source files
191
+ */
192
+ async function detectEntryPoints(projectRoot, frameworks) {
193
+ const entryPoints = [];
194
+
195
+ for (const framework of frameworks) {
196
+ const pattern = ENTRY_POINT_PATTERNS[framework];
197
+ if (!pattern) continue;
198
+
199
+ // Get appropriate file extension
200
+ const lang = TECH_SIGNATURES.frameworks[framework]?.language || 'javascript';
201
+ const ext = TECH_SIGNATURES.languages[lang]?.extensions?.[0] || '.js';
202
+
203
+ try {
204
+ const files = await glob(`**/*${ext}`, {
205
+ cwd: projectRoot,
206
+ ignore: ['node_modules/**', 'vendor/**', '.git/**', 'dist/**', 'build/**', '__pycache__/**'],
207
+ nodir: true,
208
+ });
209
+
210
+ for (const file of files.slice(0, 20)) { // Limit to 20 files
211
+ try {
212
+ const content = fs.readFileSync(path.join(projectRoot, file), 'utf8');
213
+ let match;
214
+
215
+ while ((match = pattern.regex.exec(content)) !== null) {
216
+ const entry = pattern.extractor(match);
217
+ entryPoints.push({
218
+ ...entry,
219
+ file,
220
+ line: content.substring(0, match.index).split('\n').length,
221
+ framework
222
+ });
223
+ }
224
+ pattern.regex.lastIndex = 0; // Reset regex
225
+ } catch {
226
+ // Skip unreadable files
227
+ }
228
+ }
229
+ } catch {
230
+ // Skip glob errors
231
+ }
232
+ }
233
+
234
+ return entryPoints;
235
+ }
236
+
237
+ /**
238
+ * Detect technology stack
239
+ */
240
+ async function detectTechStack(projectRoot, options = {}) {
241
+ const { hint = null } = options;
242
+
243
+ const result = {
244
+ stack: '',
245
+ summary: '',
246
+ languages: [],
247
+ frameworks: [],
248
+ databases: [],
249
+ projectName: path.basename(projectRoot),
250
+ fileCount: 0,
251
+ loc: 0,
252
+ };
253
+
254
+ // If hint provided, use it
255
+ if (hint) {
256
+ result.stack = hint;
257
+ result.summary = hint;
258
+ return result;
259
+ }
260
+
261
+ // Detect languages
262
+ for (const [lang, signature] of Object.entries(TECH_SIGNATURES.languages)) {
263
+ // Check for signature files
264
+ for (const sigFile of signature.files) {
265
+ if (fs.existsSync(path.join(projectRoot, sigFile))) {
266
+ if (!result.languages.includes(lang)) {
267
+ result.languages.push(lang);
268
+ }
269
+ break;
270
+ }
271
+ }
272
+
273
+ // Check for file extensions
274
+ if (signature.extensions) {
275
+ for (const ext of signature.extensions) {
276
+ try {
277
+ const files = await glob(`**/*${ext}`, {
278
+ cwd: projectRoot,
279
+ ignore: ['node_modules/**', 'vendor/**', '.git/**', 'dist/**', 'build/**'],
280
+ nodir: true,
281
+ });
282
+
283
+ if (files.length > 0) {
284
+ if (!result.languages.includes(lang)) {
285
+ result.languages.push(lang);
286
+ }
287
+ result.fileCount += files.length;
288
+ break;
289
+ }
290
+ } catch {
291
+ // Ignore glob errors
292
+ }
293
+ }
294
+ }
295
+ }
296
+
297
+ // Detect frameworks
298
+ for (const [framework, signature] of Object.entries(TECH_SIGNATURES.frameworks)) {
299
+ // Check for package.json dependencies
300
+ if (signature.packageDeps) {
301
+ const pkgPath = path.join(projectRoot, 'package.json');
302
+ if (fs.existsSync(pkgPath)) {
303
+ try {
304
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
305
+ const allDeps = {
306
+ ...pkg.dependencies,
307
+ ...pkg.devDependencies,
308
+ };
309
+
310
+ for (const dep of signature.packageDeps) {
311
+ if (allDeps[dep]) {
312
+ if (!result.frameworks.includes(framework)) {
313
+ result.frameworks.push(framework);
314
+ }
315
+ break;
316
+ }
317
+ }
318
+ } catch {
319
+ // Ignore JSON parse errors
320
+ }
321
+ }
322
+ }
323
+
324
+ // Check for signature files
325
+ if (signature.files) {
326
+ for (const sigFile of signature.files) {
327
+ if (fs.existsSync(path.join(projectRoot, sigFile))) {
328
+ if (!result.frameworks.includes(framework)) {
329
+ result.frameworks.push(framework);
330
+ }
331
+ break;
332
+ }
333
+ }
334
+ }
335
+
336
+ // Check for patterns in source files (sample)
337
+ if (signature.patterns && result.languages.includes(signature.language)) {
338
+ // Sample a few source files
339
+ const ext = TECH_SIGNATURES.languages[signature.language]?.extensions?.[0];
340
+ if (ext) {
341
+ try {
342
+ const sampleFiles = await glob(`**/*${ext}`, {
343
+ cwd: projectRoot,
344
+ ignore: ['node_modules/**', 'vendor/**', '.git/**'],
345
+ nodir: true,
346
+ });
347
+
348
+ // Check first 5 files
349
+ for (const file of sampleFiles.slice(0, 5)) {
350
+ try {
351
+ const content = fs.readFileSync(path.join(projectRoot, file), 'utf8');
352
+
353
+ for (const pattern of signature.patterns) {
354
+ if (content.includes(pattern)) {
355
+ if (!result.frameworks.includes(framework)) {
356
+ result.frameworks.push(framework);
357
+ }
358
+ break;
359
+ }
360
+ }
361
+ } catch {
362
+ // Ignore file read errors
363
+ }
364
+ }
365
+ } catch {
366
+ // Ignore glob errors
367
+ }
368
+ }
369
+ }
370
+ }
371
+
372
+ // Detect databases
373
+ for (const [db, signature] of Object.entries(TECH_SIGNATURES.databases)) {
374
+ // Check environment files
375
+ const envFiles = ['.env', '.env.example', '.env.local'];
376
+ for (const envFile of envFiles) {
377
+ const envPath = path.join(projectRoot, envFile);
378
+ if (fs.existsSync(envPath)) {
379
+ try {
380
+ const content = fs.readFileSync(envPath, 'utf8');
381
+
382
+ if (signature.envVars) {
383
+ for (const envVar of signature.envVars) {
384
+ if (content.includes(envVar)) {
385
+ if (!result.databases.includes(db)) {
386
+ result.databases.push(db);
387
+ }
388
+ break;
389
+ }
390
+ }
391
+ }
392
+ } catch {
393
+ // Ignore read errors
394
+ }
395
+ }
396
+ }
397
+
398
+ // Check for patterns in config files
399
+ if (signature.patterns) {
400
+ const configFiles = ['package.json', 'requirements.txt', 'pyproject.toml', 'Cargo.toml'];
401
+ for (const configFile of configFiles) {
402
+ const configPath = path.join(projectRoot, configFile);
403
+ if (fs.existsSync(configPath)) {
404
+ try {
405
+ const content = fs.readFileSync(configPath, 'utf8').toLowerCase();
406
+
407
+ for (const pattern of signature.patterns) {
408
+ if (content.includes(pattern.toLowerCase())) {
409
+ if (!result.databases.includes(db)) {
410
+ result.databases.push(db);
411
+ }
412
+ break;
413
+ }
414
+ }
415
+ } catch {
416
+ // Ignore read errors
417
+ }
418
+ }
419
+ }
420
+ }
421
+ }
422
+
423
+ // Detect entry points for discovered frameworks
424
+ result.entryPoints = await detectEntryPoints(projectRoot, result.frameworks);
425
+
426
+ // Build stack string
427
+ const stackParts = [];
428
+ if (result.languages.length > 0) {
429
+ stackParts.push(result.languages.map(l => capitalize(l)).join(', '));
430
+ }
431
+ if (result.frameworks.length > 0) {
432
+ stackParts.push(result.frameworks.map(f => capitalize(f)).join(', '));
433
+ }
434
+ if (result.databases.length > 0) {
435
+ stackParts.push(result.databases.map(d => capitalize(d)).join(', '));
436
+ }
437
+
438
+ result.stack = stackParts.join(' + ') || 'Unknown';
439
+ result.summary = result.stack;
440
+
441
+ return result;
442
+ }
443
+
444
+ /**
445
+ * Capitalize first letter
446
+ */
447
+ function capitalize(str) {
448
+ return str.charAt(0).toUpperCase() + str.slice(1);
449
+ }
450
+
451
+ module.exports = {
452
+ detectTechStack,
453
+ detectEntryPoints,
454
+ TECH_SIGNATURES,
455
+ ENTRY_POINT_PATTERNS,
456
+ };