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