create-claude-context 1.2.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.
- package/README.md +29 -6
- package/bin/create-claude-context.js +15 -1
- package/lib/ai-orchestrator.js +423 -0
- package/lib/call-tracer.js +444 -0
- package/lib/detector.js +83 -0
- package/lib/environment-detector.js +239 -0
- package/lib/index.js +128 -27
- package/lib/placeholder.js +80 -19
- package/lib/static-analyzer.js +729 -0
- package/lib/template-populator.js +835 -0
- package/package.json +1 -1
- package/templates/base/settings.json +1 -77
|
@@ -0,0 +1,835 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template Populator
|
|
3
|
+
*
|
|
4
|
+
* Takes analysis results and populates all template files
|
|
5
|
+
* with real, project-specific content.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const { glob } = require('glob');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Slugify a string for use in filenames
|
|
14
|
+
* @param {string} str - String to slugify
|
|
15
|
+
* @returns {string}
|
|
16
|
+
*/
|
|
17
|
+
function slugify(str) {
|
|
18
|
+
return str
|
|
19
|
+
.toLowerCase()
|
|
20
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
21
|
+
.replace(/^-|-$/g, '');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Populate all templates with analysis results
|
|
26
|
+
* @param {string} claudeDir - .claude directory path
|
|
27
|
+
* @param {object} analysis - Analysis results from static analyzer
|
|
28
|
+
* @param {object} config - Configuration from CLI
|
|
29
|
+
* @returns {Promise<object>} Results of population
|
|
30
|
+
*/
|
|
31
|
+
async function populateAllTemplates(claudeDir, analysis, config) {
|
|
32
|
+
const results = {
|
|
33
|
+
populated: [],
|
|
34
|
+
skipped: [],
|
|
35
|
+
errors: [],
|
|
36
|
+
created: []
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const projectRoot = path.dirname(claudeDir);
|
|
40
|
+
const projectName = config.projectName || path.basename(projectRoot);
|
|
41
|
+
|
|
42
|
+
// 1. Populate CLAUDE.md at project root
|
|
43
|
+
try {
|
|
44
|
+
await populateClaudeMd(projectRoot, analysis, config);
|
|
45
|
+
results.populated.push('CLAUDE.md');
|
|
46
|
+
} catch (error) {
|
|
47
|
+
results.errors.push({ file: 'CLAUDE.md', error: error.message });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 2. Generate ARCHITECTURE_SNAPSHOT.md
|
|
51
|
+
try {
|
|
52
|
+
const content = generateArchitectureSnapshot(analysis, config);
|
|
53
|
+
const filePath = path.join(claudeDir, 'context', 'ARCHITECTURE_SNAPSHOT.md');
|
|
54
|
+
ensureDir(path.dirname(filePath));
|
|
55
|
+
fs.writeFileSync(filePath, content);
|
|
56
|
+
results.populated.push('context/ARCHITECTURE_SNAPSHOT.md');
|
|
57
|
+
} catch (error) {
|
|
58
|
+
results.errors.push({ file: 'ARCHITECTURE_SNAPSHOT.md', error: error.message });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 3. Generate WORKFLOW_INDEX.md
|
|
62
|
+
try {
|
|
63
|
+
const content = generateWorkflowIndex(analysis, config);
|
|
64
|
+
const filePath = path.join(claudeDir, 'context', 'WORKFLOW_INDEX.md');
|
|
65
|
+
ensureDir(path.dirname(filePath));
|
|
66
|
+
fs.writeFileSync(filePath, content);
|
|
67
|
+
results.populated.push('context/WORKFLOW_INDEX.md');
|
|
68
|
+
} catch (error) {
|
|
69
|
+
results.errors.push({ file: 'WORKFLOW_INDEX.md', error: error.message });
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 4. Generate CODE_TO_WORKFLOW_MAP.md
|
|
73
|
+
try {
|
|
74
|
+
const content = generateCodeToWorkflowMap(analysis, config);
|
|
75
|
+
const filePath = path.join(claudeDir, 'context', 'CODE_TO_WORKFLOW_MAP.md');
|
|
76
|
+
ensureDir(path.dirname(filePath));
|
|
77
|
+
fs.writeFileSync(filePath, content);
|
|
78
|
+
results.populated.push('context/CODE_TO_WORKFLOW_MAP.md');
|
|
79
|
+
} catch (error) {
|
|
80
|
+
results.errors.push({ file: 'CODE_TO_WORKFLOW_MAP.md', error: error.message });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// 5. Generate individual workflow files
|
|
84
|
+
const workflowsDir = path.join(claudeDir, 'context', 'workflows');
|
|
85
|
+
ensureDir(workflowsDir);
|
|
86
|
+
|
|
87
|
+
for (const workflow of (analysis.workflows || [])) {
|
|
88
|
+
try {
|
|
89
|
+
const content = generateWorkflowFile(workflow, analysis, config);
|
|
90
|
+
const filename = `${slugify(workflow.name)}.md`;
|
|
91
|
+
const filePath = path.join(workflowsDir, filename);
|
|
92
|
+
fs.writeFileSync(filePath, content);
|
|
93
|
+
results.created.push(`context/workflows/${filename}`);
|
|
94
|
+
} catch (error) {
|
|
95
|
+
results.errors.push({ file: `workflow:${workflow.name}`, error: error.message });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// 6. Update category indexes
|
|
100
|
+
try {
|
|
101
|
+
await updateCategoryIndexes(claudeDir, analysis, config);
|
|
102
|
+
results.populated.push('indexes/workflows/CATEGORY_INDEX.md');
|
|
103
|
+
} catch (error) {
|
|
104
|
+
results.errors.push({ file: 'CATEGORY_INDEX.md', error: error.message });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return results;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Ensure directory exists
|
|
112
|
+
* @param {string} dirPath - Directory path
|
|
113
|
+
*/
|
|
114
|
+
function ensureDir(dirPath) {
|
|
115
|
+
if (!fs.existsSync(dirPath)) {
|
|
116
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Populate CLAUDE.md with real project info
|
|
122
|
+
* @param {string} projectRoot - Project root directory
|
|
123
|
+
* @param {object} analysis - Analysis results
|
|
124
|
+
* @param {object} config - Configuration
|
|
125
|
+
*/
|
|
126
|
+
async function populateClaudeMd(projectRoot, analysis, config) {
|
|
127
|
+
const claudeMdPath = path.join(projectRoot, 'CLAUDE.md');
|
|
128
|
+
|
|
129
|
+
if (!fs.existsSync(claudeMdPath)) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
let content = fs.readFileSync(claudeMdPath, 'utf-8');
|
|
134
|
+
const projectName = config.projectName || path.basename(projectRoot);
|
|
135
|
+
|
|
136
|
+
// Build replacement map from analysis
|
|
137
|
+
const replacements = {
|
|
138
|
+
'{{PROJECT_NAME}}': projectName,
|
|
139
|
+
'{{PROJECT_DESCRIPTION}}': `${projectName} application`,
|
|
140
|
+
'{{TECH_STACK}}': analysis.techStack?.summary || config.techStack?.summary || 'Unknown',
|
|
141
|
+
'{{PROJECT_STATUS}}': 'Development',
|
|
142
|
+
'{{PRODUCTION_URL}}': `https://${slugify(projectName)}.example.com`,
|
|
143
|
+
'{{API_URL}}': `https://api.${slugify(projectName)}.example.com`,
|
|
144
|
+
'{{REPO_URL}}': `https://github.com/user/${slugify(projectName)}`,
|
|
145
|
+
'{{DEPLOYMENT_PLATFORM}}': detectDeploymentPlatform(projectRoot),
|
|
146
|
+
'{{DATE}}': new Date().toISOString().split('T')[0],
|
|
147
|
+
|
|
148
|
+
// Commands
|
|
149
|
+
'{{INSTALL_COMMAND}}': getInstallCommand(analysis.techStack || config.techStack),
|
|
150
|
+
'{{DEV_START_COMMAND}}': getDevCommand(analysis.techStack || config.techStack),
|
|
151
|
+
'{{TEST_COMMAND}}': getTestCommand(analysis.techStack || config.techStack),
|
|
152
|
+
'{{TEST_E2E_COMMAND}}': 'npm run test:e2e',
|
|
153
|
+
'{{TEST_COVERAGE_COMMAND}}': 'npm run test:coverage',
|
|
154
|
+
'{{MIGRATION_CREATE_COMMAND}}': getMigrationCreateCommand(analysis.techStack || config.techStack),
|
|
155
|
+
'{{MIGRATION_RUN_COMMAND}}': getMigrationRunCommand(analysis.techStack || config.techStack),
|
|
156
|
+
'{{DEPLOY_COMMAND}}': 'npm run deploy',
|
|
157
|
+
|
|
158
|
+
// Paths
|
|
159
|
+
'{{MODELS_PATH}}': findModelsPath(analysis),
|
|
160
|
+
'{{MIGRATIONS_PATH}}': findMigrationsPath(analysis),
|
|
161
|
+
'{{CORE_FILES_LIST}}': formatCoreFiles(analysis),
|
|
162
|
+
|
|
163
|
+
// Counts
|
|
164
|
+
'{{WORKFLOWS_COUNT}}': String(analysis.workflows?.length || 0),
|
|
165
|
+
'{{AGENTS_COUNT}}': '6',
|
|
166
|
+
'{{COMMANDS_COUNT}}': '11',
|
|
167
|
+
'{{INDEX_FILES_COUNT}}': '15',
|
|
168
|
+
'{{WORKFLOW_DOMAINS_COUNT}}': String(analysis.workflows?.length || 0),
|
|
169
|
+
'{{CODE_DOMAINS_COUNT}}': String(analysis.architecture?.layers?.length || 0),
|
|
170
|
+
|
|
171
|
+
// Architecture
|
|
172
|
+
'{{ARCHITECTURE_DIAGRAM}}': generateAsciiArchitecture(analysis),
|
|
173
|
+
|
|
174
|
+
// Misc
|
|
175
|
+
'{{EXTERNAL_INTEGRATIONS_LIST}}': formatExternalIntegrations(analysis),
|
|
176
|
+
'{{CONFIG_SEARCH_PATTERN}}': 'grep -r "process.env" --include="*.js" --include="*.ts"',
|
|
177
|
+
'{{URL_SEARCH_PATTERN}}': 'grep -rE "https?://" --include="*.js" --include="*.ts" --include="*.json"',
|
|
178
|
+
|
|
179
|
+
// Placeholders for manual filling
|
|
180
|
+
'{{EXAMPLE_REFACTOR_TASK}}': 'Refactor the authentication flow',
|
|
181
|
+
'{{EXAMPLE_LOWLEVEL_TASK}}': 'Fix hardcoded API URL in config',
|
|
182
|
+
'{{EXAMPLE_FEATURE_TASK}}': 'Add user notifications feature',
|
|
183
|
+
'{{CRITICAL_URLS}}': `- Production: https://${slugify(projectName)}.example.com`,
|
|
184
|
+
'{{BUSINESS_CONSTANTS}}': '- TBD (document key business constants)',
|
|
185
|
+
'{{DEBUGGING_QUICK_REFS}}': 'KNOWN_GOTCHAS.md, logs/',
|
|
186
|
+
'{{AGENT_ROUTING_TABLE}}': '@context-engineer for setup, @core-architect for design',
|
|
187
|
+
'{{GOTCHA_CATEGORY_1}}': 'Database',
|
|
188
|
+
'{{GOTCHA_1_ITEMS}}': '- TBD (document database gotchas)',
|
|
189
|
+
'{{GOTCHA_CATEGORY_2}}': 'API',
|
|
190
|
+
'{{GOTCHA_2_ITEMS}}': '- TBD (document API gotchas)',
|
|
191
|
+
'{{PRODUCTION_PLATFORM}}': detectDeploymentPlatform(projectRoot),
|
|
192
|
+
'{{PRODUCTION_SERVICES}}': 'Web, API, Database',
|
|
193
|
+
'{{MONITORING_COMMANDS}}': 'Check logs, health endpoints',
|
|
194
|
+
'{{MIGRATION_CONSTRAINTS}}': 'Always backup before migrations',
|
|
195
|
+
'{{TESTING_CONSTRAINTS}}': 'Run tests before merging',
|
|
196
|
+
'{{SECURITY_CONSTRAINTS}}': 'Never commit secrets',
|
|
197
|
+
'{{CONTACT_INFO}}': 'TBD (add contact info)'
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
// Apply replacements
|
|
201
|
+
for (const [placeholder, value] of Object.entries(replacements)) {
|
|
202
|
+
content = content.replace(new RegExp(escapeRegex(placeholder), 'g'), value);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
fs.writeFileSync(claudeMdPath, content);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Escape regex special characters
|
|
210
|
+
* @param {string} str - String to escape
|
|
211
|
+
* @returns {string}
|
|
212
|
+
*/
|
|
213
|
+
function escapeRegex(str) {
|
|
214
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Generate ARCHITECTURE_SNAPSHOT.md content
|
|
219
|
+
* @param {object} analysis - Analysis results
|
|
220
|
+
* @param {object} config - Configuration
|
|
221
|
+
* @returns {string}
|
|
222
|
+
*/
|
|
223
|
+
function generateArchitectureSnapshot(analysis, config) {
|
|
224
|
+
const projectName = config.projectName || 'Project';
|
|
225
|
+
const date = new Date().toISOString().split('T')[0];
|
|
226
|
+
|
|
227
|
+
return `# Architecture Snapshot - ${projectName}
|
|
228
|
+
|
|
229
|
+
**Purpose:** High-level system map for rapid orientation
|
|
230
|
+
**Load:** When starting a new session or onboarding
|
|
231
|
+
**Size:** ~10k tokens (5% of 200k budget)
|
|
232
|
+
**Last Updated:** ${date}
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## System Overview
|
|
237
|
+
|
|
238
|
+
\`\`\`
|
|
239
|
+
${generateAsciiArchitecture(analysis)}
|
|
240
|
+
\`\`\`
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## Technology Stack
|
|
245
|
+
|
|
246
|
+
| Layer | Technology | Purpose |
|
|
247
|
+
|-------|------------|---------|
|
|
248
|
+
${formatTechStackTable(analysis)}
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## Core Components
|
|
253
|
+
|
|
254
|
+
${formatComponents(analysis)}
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Directory Structure
|
|
259
|
+
|
|
260
|
+
\`\`\`
|
|
261
|
+
${analysis.architecture?.directoryTree || 'TBD - Run AI analysis for complete structure'}
|
|
262
|
+
\`\`\`
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## Entry Points
|
|
267
|
+
|
|
268
|
+
| Type | Count | Key Files |
|
|
269
|
+
|------|-------|-----------|
|
|
270
|
+
| API Routes | ${analysis.entryPoints?.length || 0} | ${getTopEntryPointFiles(analysis)} |
|
|
271
|
+
| Workflows | ${analysis.workflows?.length || 0} | See WORKFLOW_INDEX.md |
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## External Integrations
|
|
276
|
+
|
|
277
|
+
${formatExternalIntegrations(analysis)}
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Data Flow
|
|
282
|
+
|
|
283
|
+
\`\`\`
|
|
284
|
+
Request → Router → Controller → Service → Repository → Database
|
|
285
|
+
↓
|
|
286
|
+
External APIs
|
|
287
|
+
\`\`\`
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## Key Metrics
|
|
292
|
+
|
|
293
|
+
| Metric | Value |
|
|
294
|
+
|--------|-------|
|
|
295
|
+
| Total Source Files | ${analysis.sourceFiles || 'TBD'} |
|
|
296
|
+
| Lines of Code | ${analysis.linesOfCode?.total || 'TBD'} |
|
|
297
|
+
| Dependencies | ${analysis.dependencies?.length || 0} |
|
|
298
|
+
| Entry Points | ${analysis.entryPoints?.length || 0} |
|
|
299
|
+
| Workflows | ${analysis.workflows?.length || 0} |
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
*Auto-generated by create-claude-context. Enhance with AI analysis for complete details.*
|
|
304
|
+
`;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Generate WORKFLOW_INDEX.md content
|
|
309
|
+
* @param {object} analysis - Analysis results
|
|
310
|
+
* @param {object} config - Configuration
|
|
311
|
+
* @returns {string}
|
|
312
|
+
*/
|
|
313
|
+
function generateWorkflowIndex(analysis, config) {
|
|
314
|
+
const projectName = config.projectName || 'Project';
|
|
315
|
+
const date = new Date().toISOString().split('T')[0];
|
|
316
|
+
const workflows = analysis.workflows || [];
|
|
317
|
+
|
|
318
|
+
return `# Workflow Index - ${projectName}
|
|
319
|
+
|
|
320
|
+
**Purpose:** Master catalog of all documented workflows
|
|
321
|
+
**Load:** At session start for navigation
|
|
322
|
+
**Size:** ~15k tokens (7.5% of 200k budget)
|
|
323
|
+
**Last Updated:** ${date}
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## Quick Navigation
|
|
328
|
+
|
|
329
|
+
| Workflow | Category | Complexity | Files | Link |
|
|
330
|
+
|----------|----------|------------|-------|------|
|
|
331
|
+
${workflows.map(wf =>
|
|
332
|
+
`| ${wf.name} | ${wf.category} | ${wf.complexity} | ${wf.fileCount || wf.files?.length || 0} | [Details](./workflows/${slugify(wf.name)}.md) |`
|
|
333
|
+
).join('\n') || '| *No workflows discovered* | - | - | - | - |'}
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## Workflows by Category
|
|
338
|
+
|
|
339
|
+
${formatWorkflowsByCategory(workflows)}
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## Entry Points Summary
|
|
344
|
+
|
|
345
|
+
| Entry Point | Workflow | Method |
|
|
346
|
+
|-------------|----------|--------|
|
|
347
|
+
${formatEntryPointsTable(analysis.entryPoints)}
|
|
348
|
+
|
|
349
|
+
---
|
|
350
|
+
|
|
351
|
+
## Cross-Reference: Files → Workflows
|
|
352
|
+
|
|
353
|
+
See [CODE_TO_WORKFLOW_MAP.md](./CODE_TO_WORKFLOW_MAP.md) for reverse lookup.
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
## Maintenance
|
|
358
|
+
|
|
359
|
+
- **Verification Frequency:** Weekly spot-checks, quarterly full review
|
|
360
|
+
- **Last Verified:** ${date}
|
|
361
|
+
- **Next Review:** TBD
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
*Auto-generated by create-claude-context. Run AI analysis for complete workflow documentation.*
|
|
366
|
+
`;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Generate CODE_TO_WORKFLOW_MAP.md content
|
|
371
|
+
* @param {object} analysis - Analysis results
|
|
372
|
+
* @param {object} config - Configuration
|
|
373
|
+
* @returns {string}
|
|
374
|
+
*/
|
|
375
|
+
function generateCodeToWorkflowMap(analysis, config) {
|
|
376
|
+
const projectName = config.projectName || 'Project';
|
|
377
|
+
const date = new Date().toISOString().split('T')[0];
|
|
378
|
+
|
|
379
|
+
// Build file to workflow mapping
|
|
380
|
+
const fileMap = buildFileToWorkflowMap(analysis);
|
|
381
|
+
|
|
382
|
+
return `# Code to Workflow Map - ${projectName}
|
|
383
|
+
|
|
384
|
+
**Purpose:** Reverse lookup - find documentation for any file
|
|
385
|
+
**Load:** When investigating a specific file
|
|
386
|
+
**Size:** ~20k tokens (10% of 200k budget)
|
|
387
|
+
**Last Updated:** ${date}
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## How to Use
|
|
392
|
+
|
|
393
|
+
When you modify a file:
|
|
394
|
+
1. Find it in this index
|
|
395
|
+
2. Check which workflows reference it
|
|
396
|
+
3. Update those workflow docs accordingly
|
|
397
|
+
|
|
398
|
+
---
|
|
399
|
+
|
|
400
|
+
## File Index
|
|
401
|
+
|
|
402
|
+
${formatFileIndex(fileMap)}
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
## Undocumented Files
|
|
407
|
+
|
|
408
|
+
The following files are not yet mapped to workflows:
|
|
409
|
+
|
|
410
|
+
${formatUndocumentedFiles(analysis, fileMap)}
|
|
411
|
+
|
|
412
|
+
---
|
|
413
|
+
|
|
414
|
+
## Update Checklist
|
|
415
|
+
|
|
416
|
+
After modifying code:
|
|
417
|
+
|
|
418
|
+
- [ ] Find affected file(s) in this index
|
|
419
|
+
- [ ] Update line numbers in referenced workflows
|
|
420
|
+
- [ ] Verify function signatures still match
|
|
421
|
+
- [ ] Run \`/verify-docs-current\` to validate
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
*Auto-generated by create-claude-context. Run \`/auto-sync --rebuild-map\` to refresh.*
|
|
426
|
+
`;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Generate individual workflow file
|
|
431
|
+
* @param {object} workflow - Workflow data
|
|
432
|
+
* @param {object} analysis - Full analysis
|
|
433
|
+
* @param {object} config - Configuration
|
|
434
|
+
* @returns {string}
|
|
435
|
+
*/
|
|
436
|
+
function generateWorkflowFile(workflow, analysis, config) {
|
|
437
|
+
const date = new Date().toISOString().split('T')[0];
|
|
438
|
+
|
|
439
|
+
// Find entry points for this workflow
|
|
440
|
+
const workflowEntryPoints = (analysis.entryPoints || []).filter(ep =>
|
|
441
|
+
workflow.files?.some(f => ep.file.includes(f) || f.includes(ep.file))
|
|
442
|
+
);
|
|
443
|
+
|
|
444
|
+
return `---
|
|
445
|
+
name: ${slugify(workflow.name)}
|
|
446
|
+
category: ${workflow.category || 'general'}
|
|
447
|
+
complexity: ${workflow.complexity || 'MEDIUM'}
|
|
448
|
+
last_updated: ${date}
|
|
449
|
+
status: discovered
|
|
450
|
+
---
|
|
451
|
+
|
|
452
|
+
# ${workflow.name}
|
|
453
|
+
|
|
454
|
+
## Overview
|
|
455
|
+
|
|
456
|
+
**Purpose:** ${getWorkflowPurpose(workflow)}
|
|
457
|
+
**Complexity:** ${workflow.complexity || 'MEDIUM'}
|
|
458
|
+
**Confidence:** ${workflow.confidence || 0}%
|
|
459
|
+
**Files Involved:** ${workflow.fileCount || workflow.files?.length || 0}
|
|
460
|
+
|
|
461
|
+
---
|
|
462
|
+
|
|
463
|
+
## Entry Points
|
|
464
|
+
|
|
465
|
+
${workflowEntryPoints.length > 0 ? formatWorkflowEntryPoints(workflowEntryPoints) : '*Entry points to be documented during AI analysis*'}
|
|
466
|
+
|
|
467
|
+
---
|
|
468
|
+
|
|
469
|
+
## Key Files
|
|
470
|
+
|
|
471
|
+
| File | Purpose |
|
|
472
|
+
|------|---------|
|
|
473
|
+
${(workflow.files || []).slice(0, 20).map(f => `| \`${f}\` | TBD |`).join('\n') || '| *No files mapped* | - |'}
|
|
474
|
+
|
|
475
|
+
---
|
|
476
|
+
|
|
477
|
+
## Call Chain
|
|
478
|
+
|
|
479
|
+
\`\`\`
|
|
480
|
+
${workflow.name.toLowerCase().replace(/\s+/g, '_')}()
|
|
481
|
+
├─ [To be traced during AI analysis]
|
|
482
|
+
└─ [To be traced during AI analysis]
|
|
483
|
+
\`\`\`
|
|
484
|
+
|
|
485
|
+
---
|
|
486
|
+
|
|
487
|
+
## Database Operations
|
|
488
|
+
|
|
489
|
+
*Database operations to be documented during AI analysis*
|
|
490
|
+
|
|
491
|
+
---
|
|
492
|
+
|
|
493
|
+
## External Dependencies
|
|
494
|
+
|
|
495
|
+
*External dependencies to be documented during AI analysis*
|
|
496
|
+
|
|
497
|
+
---
|
|
498
|
+
|
|
499
|
+
## Test Coverage
|
|
500
|
+
|
|
501
|
+
| Test File | Type | Coverage |
|
|
502
|
+
|-----------|------|----------|
|
|
503
|
+
| *TBD* | - | - |
|
|
504
|
+
|
|
505
|
+
---
|
|
506
|
+
|
|
507
|
+
## Known Gotchas
|
|
508
|
+
|
|
509
|
+
*No gotchas documented yet. Add as discovered.*
|
|
510
|
+
|
|
511
|
+
---
|
|
512
|
+
|
|
513
|
+
## Maintenance
|
|
514
|
+
|
|
515
|
+
- **Last Verified:** ${date}
|
|
516
|
+
- **Verification Method:** Auto-discovered
|
|
517
|
+
- **Next Review:** TBD
|
|
518
|
+
|
|
519
|
+
---
|
|
520
|
+
|
|
521
|
+
*Auto-generated by create-claude-context. Enhance with AI analysis for complete documentation.*
|
|
522
|
+
`;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Update category indexes
|
|
527
|
+
* @param {string} claudeDir - .claude directory path
|
|
528
|
+
* @param {object} analysis - Analysis results
|
|
529
|
+
* @param {object} config - Configuration
|
|
530
|
+
*/
|
|
531
|
+
async function updateCategoryIndexes(claudeDir, analysis, config) {
|
|
532
|
+
const indexPath = path.join(claudeDir, 'indexes', 'workflows', 'CATEGORY_INDEX.md');
|
|
533
|
+
ensureDir(path.dirname(indexPath));
|
|
534
|
+
|
|
535
|
+
const workflows = analysis.workflows || [];
|
|
536
|
+
const categories = [...new Set(workflows.map(w => w.category))];
|
|
537
|
+
const date = new Date().toISOString().split('T')[0];
|
|
538
|
+
|
|
539
|
+
const content = `# Workflow Category Index
|
|
540
|
+
|
|
541
|
+
**Purpose:** Quick navigation to workflow categories
|
|
542
|
+
**Load:** At session start
|
|
543
|
+
**Last Updated:** ${date}
|
|
544
|
+
|
|
545
|
+
---
|
|
546
|
+
|
|
547
|
+
## Categories
|
|
548
|
+
|
|
549
|
+
${categories.map(cat => {
|
|
550
|
+
const catWorkflows = workflows.filter(w => w.category === cat);
|
|
551
|
+
return `### ${cat.charAt(0).toUpperCase() + cat.slice(1)}
|
|
552
|
+
|
|
553
|
+
| Workflow | Complexity | Link |
|
|
554
|
+
|----------|------------|------|
|
|
555
|
+
${catWorkflows.map(w => `| ${w.name} | ${w.complexity} | [Details](../../context/workflows/${slugify(w.name)}.md) |`).join('\n')}
|
|
556
|
+
`;
|
|
557
|
+
}).join('\n') || '*No workflows discovered yet*'}
|
|
558
|
+
|
|
559
|
+
---
|
|
560
|
+
|
|
561
|
+
## Summary
|
|
562
|
+
|
|
563
|
+
| Category | Count |
|
|
564
|
+
|----------|-------|
|
|
565
|
+
${categories.map(cat => `| ${cat} | ${workflows.filter(w => w.category === cat).length} |`).join('\n') || '| - | 0 |'}
|
|
566
|
+
|
|
567
|
+
---
|
|
568
|
+
|
|
569
|
+
*Auto-generated by create-claude-context*
|
|
570
|
+
`;
|
|
571
|
+
|
|
572
|
+
fs.writeFileSync(indexPath, content);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// Helper functions
|
|
576
|
+
|
|
577
|
+
function getInstallCommand(techStack) {
|
|
578
|
+
const languages = techStack?.languages || [];
|
|
579
|
+
if (languages.includes('python')) return 'pip install -r requirements.txt';
|
|
580
|
+
if (languages.includes('go')) return 'go mod download';
|
|
581
|
+
if (languages.includes('rust')) return 'cargo build';
|
|
582
|
+
if (languages.includes('ruby')) return 'bundle install';
|
|
583
|
+
return 'npm install';
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
function getDevCommand(techStack) {
|
|
587
|
+
const languages = techStack?.languages || [];
|
|
588
|
+
if (languages.includes('python')) return 'python main.py';
|
|
589
|
+
if (languages.includes('go')) return 'go run .';
|
|
590
|
+
if (languages.includes('rust')) return 'cargo run';
|
|
591
|
+
if (languages.includes('ruby')) return 'rails server';
|
|
592
|
+
return 'npm run dev';
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
function getTestCommand(techStack) {
|
|
596
|
+
const languages = techStack?.languages || [];
|
|
597
|
+
if (languages.includes('python')) return 'pytest';
|
|
598
|
+
if (languages.includes('go')) return 'go test ./...';
|
|
599
|
+
if (languages.includes('rust')) return 'cargo test';
|
|
600
|
+
if (languages.includes('ruby')) return 'rspec';
|
|
601
|
+
return 'npm test';
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
function getMigrationCreateCommand(techStack) {
|
|
605
|
+
const languages = techStack?.languages || [];
|
|
606
|
+
if (languages.includes('python')) return 'alembic revision --autogenerate -m "description"';
|
|
607
|
+
if (languages.includes('ruby')) return 'rails generate migration MigrationName';
|
|
608
|
+
return 'npm run migration:create';
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
function getMigrationRunCommand(techStack) {
|
|
612
|
+
const languages = techStack?.languages || [];
|
|
613
|
+
if (languages.includes('python')) return 'alembic upgrade head';
|
|
614
|
+
if (languages.includes('ruby')) return 'rails db:migrate';
|
|
615
|
+
return 'npm run migration:run';
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
function detectDeploymentPlatform(projectRoot) {
|
|
619
|
+
if (fs.existsSync(path.join(projectRoot, 'vercel.json'))) return 'Vercel';
|
|
620
|
+
if (fs.existsSync(path.join(projectRoot, 'netlify.toml'))) return 'Netlify';
|
|
621
|
+
if (fs.existsSync(path.join(projectRoot, 'Dockerfile'))) return 'Docker';
|
|
622
|
+
if (fs.existsSync(path.join(projectRoot, 'fly.toml'))) return 'Fly.io';
|
|
623
|
+
if (fs.existsSync(path.join(projectRoot, 'render.yaml'))) return 'Render';
|
|
624
|
+
if (fs.existsSync(path.join(projectRoot, 'railway.json'))) return 'Railway';
|
|
625
|
+
return 'TBD';
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
function findModelsPath(analysis) {
|
|
629
|
+
const layers = analysis.architecture?.layers || [];
|
|
630
|
+
const domainLayer = layers.find(l => l.name === 'domain');
|
|
631
|
+
if (domainLayer?.directories?.length > 0) {
|
|
632
|
+
return domainLayer.directories[0] + '/';
|
|
633
|
+
}
|
|
634
|
+
return 'models/';
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
function findMigrationsPath(analysis) {
|
|
638
|
+
return 'migrations/';
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
function formatCoreFiles(analysis) {
|
|
642
|
+
const entryPoints = analysis.entryPoints || [];
|
|
643
|
+
const uniqueFiles = [...new Set(entryPoints.map(ep => ep.file))].slice(0, 5);
|
|
644
|
+
return uniqueFiles.map(f => `- \`${f}\``).join('\n') || '- TBD';
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
function generateAsciiArchitecture(analysis) {
|
|
648
|
+
const layers = analysis.architecture?.layers || [];
|
|
649
|
+
|
|
650
|
+
if (layers.length === 0) {
|
|
651
|
+
return `┌─────────────────────────────────────┐
|
|
652
|
+
│ [Application] │
|
|
653
|
+
│ │
|
|
654
|
+
│ ┌───────────┐ ┌───────────┐ │
|
|
655
|
+
│ │ API │ │ Services │ │
|
|
656
|
+
│ └───────────┘ └───────────┘ │
|
|
657
|
+
│ │
|
|
658
|
+
│ ┌───────────┐ ┌───────────┐ │
|
|
659
|
+
│ │ Models │ │ Database │ │
|
|
660
|
+
│ └───────────┘ └───────────┘ │
|
|
661
|
+
└─────────────────────────────────────┘`;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
let diagram = '┌─────────────────────────────────────┐\n';
|
|
665
|
+
for (const layer of layers.slice(0, 4)) {
|
|
666
|
+
diagram += `│ [${layer.name.padEnd(30)}] │\n`;
|
|
667
|
+
diagram += `│ ${layer.directories?.slice(0, 2).join(', ').padEnd(32)}│\n`;
|
|
668
|
+
}
|
|
669
|
+
diagram += '└─────────────────────────────────────┘';
|
|
670
|
+
|
|
671
|
+
return diagram;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
function formatTechStackTable(analysis) {
|
|
675
|
+
const techStack = analysis.techStack || {};
|
|
676
|
+
const rows = [];
|
|
677
|
+
|
|
678
|
+
if (techStack.languages?.length > 0) {
|
|
679
|
+
rows.push(`| **Languages** | ${techStack.languages.join(', ')} | Core development |`);
|
|
680
|
+
}
|
|
681
|
+
if (techStack.frameworks?.length > 0) {
|
|
682
|
+
rows.push(`| **Frameworks** | ${techStack.frameworks.join(', ')} | Application framework |`);
|
|
683
|
+
}
|
|
684
|
+
if (techStack.databases?.length > 0) {
|
|
685
|
+
rows.push(`| **Databases** | ${techStack.databases.join(', ')} | Data persistence |`);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
return rows.join('\n') || '| *TBD* | - | - |';
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
function formatComponents(analysis) {
|
|
692
|
+
const layers = analysis.architecture?.layers || [];
|
|
693
|
+
return layers.map(layer => `
|
|
694
|
+
### ${layer.name.charAt(0).toUpperCase() + layer.name.slice(1)}
|
|
695
|
+
|
|
696
|
+
**Purpose:** ${layer.purpose || 'TBD'}
|
|
697
|
+
**Directories:** ${layer.directories?.join(', ') || 'TBD'}
|
|
698
|
+
`).join('\n') || '*Components will be documented during AI analysis*';
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
function getTopEntryPointFiles(analysis) {
|
|
702
|
+
const entryPoints = analysis.entryPoints || [];
|
|
703
|
+
const files = [...new Set(entryPoints.map(ep => ep.file))].slice(0, 3);
|
|
704
|
+
return files.join(', ') || 'TBD';
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
function formatExternalIntegrations(analysis) {
|
|
708
|
+
const deps = analysis.dependencies || [];
|
|
709
|
+
const integrations = deps.filter(d =>
|
|
710
|
+
['stripe', 'aws', 'firebase', 'twilio', 'sendgrid', 'redis', 'elasticsearch'].some(
|
|
711
|
+
int => d.name.toLowerCase().includes(int)
|
|
712
|
+
)
|
|
713
|
+
);
|
|
714
|
+
|
|
715
|
+
if (integrations.length === 0) {
|
|
716
|
+
return '*No external integrations detected. Document manually if present.*';
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
return integrations.map(i => `- **${i.name}** (${i.version})`).join('\n');
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
function formatWorkflowsByCategory(workflows) {
|
|
723
|
+
const categories = [...new Set(workflows.map(w => w.category))];
|
|
724
|
+
|
|
725
|
+
return categories.map(cat => {
|
|
726
|
+
const catWorkflows = workflows.filter(w => w.category === cat);
|
|
727
|
+
return `### ${cat.charAt(0).toUpperCase() + cat.slice(1)}
|
|
728
|
+
|
|
729
|
+
${catWorkflows.map(w => `- [${w.name}](./workflows/${slugify(w.name)}.md) - ${w.complexity}`).join('\n')}
|
|
730
|
+
`;
|
|
731
|
+
}).join('\n') || '*No workflows by category*';
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
function formatEntryPointsTable(entryPoints) {
|
|
735
|
+
if (!entryPoints || entryPoints.length === 0) {
|
|
736
|
+
return '| *TBD* | - | - |';
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
return entryPoints.slice(0, 20).map(ep =>
|
|
740
|
+
`| \`${ep.file}:${ep.line}\` | ${ep.route || 'TBD'} | ${ep.method || '-'} |`
|
|
741
|
+
).join('\n');
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
function buildFileToWorkflowMap(analysis) {
|
|
745
|
+
const map = {};
|
|
746
|
+
const workflows = analysis.workflows || [];
|
|
747
|
+
|
|
748
|
+
for (const workflow of workflows) {
|
|
749
|
+
for (const file of (workflow.files || [])) {
|
|
750
|
+
if (!map[file]) map[file] = [];
|
|
751
|
+
map[file].push(workflow.name);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
return map;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
function formatFileIndex(fileMap) {
|
|
759
|
+
const entries = Object.entries(fileMap);
|
|
760
|
+
|
|
761
|
+
if (entries.length === 0) {
|
|
762
|
+
return '*No files mapped to workflows yet. Run AI analysis to populate.*';
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// Group by directory
|
|
766
|
+
const byDir = {};
|
|
767
|
+
for (const [file, workflows] of entries) {
|
|
768
|
+
const dir = path.dirname(file) || '.';
|
|
769
|
+
if (!byDir[dir]) byDir[dir] = [];
|
|
770
|
+
byDir[dir].push({ file, workflows });
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
return Object.entries(byDir).map(([dir, files]) => `
|
|
774
|
+
### ${dir}/
|
|
775
|
+
|
|
776
|
+
| File | Workflows |
|
|
777
|
+
|------|-----------|
|
|
778
|
+
${files.map(f => `| \`${path.basename(f.file)}\` | ${f.workflows.join(', ')} |`).join('\n')}
|
|
779
|
+
`).join('\n');
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
function formatUndocumentedFiles(analysis, fileMap) {
|
|
783
|
+
const mappedFiles = new Set(Object.keys(fileMap));
|
|
784
|
+
const allEntryFiles = (analysis.entryPoints || []).map(ep => ep.file);
|
|
785
|
+
const unmapped = allEntryFiles.filter(f => !mappedFiles.has(f));
|
|
786
|
+
|
|
787
|
+
if (unmapped.length === 0) {
|
|
788
|
+
return '*All entry point files are mapped to workflows.*';
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
return unmapped.slice(0, 20).map(f => `- \`${f}\``).join('\n');
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
function getWorkflowPurpose(workflow) {
|
|
795
|
+
const purposes = {
|
|
796
|
+
authentication: 'Handles user authentication and session management',
|
|
797
|
+
userManagement: 'Manages user accounts, profiles, and preferences',
|
|
798
|
+
payments: 'Processes payments, billing, and subscriptions',
|
|
799
|
+
dataProcessing: 'Handles background jobs and data pipelines',
|
|
800
|
+
apiEndpoints: 'Exposes API endpoints for external consumption',
|
|
801
|
+
database: 'Manages database operations and queries',
|
|
802
|
+
notifications: 'Sends notifications via email, SMS, or push',
|
|
803
|
+
fileHandling: 'Handles file uploads, storage, and downloads',
|
|
804
|
+
search: 'Provides search functionality across the application',
|
|
805
|
+
analytics: 'Tracks metrics and generates analytics',
|
|
806
|
+
testing: 'Contains test suites and fixtures',
|
|
807
|
+
configuration: 'Manages application configuration'
|
|
808
|
+
};
|
|
809
|
+
|
|
810
|
+
return purposes[workflow.type] || `Handles ${workflow.name.toLowerCase()} functionality`;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
function formatWorkflowEntryPoints(entryPoints) {
|
|
814
|
+
return entryPoints.slice(0, 10).map(ep => `
|
|
815
|
+
### \`${ep.file}:${ep.line}\`
|
|
816
|
+
|
|
817
|
+
**Route:** ${ep.route || 'N/A'}
|
|
818
|
+
**Method:** ${ep.method || 'N/A'}
|
|
819
|
+
|
|
820
|
+
\`\`\`
|
|
821
|
+
${ep.context || 'No context available'}
|
|
822
|
+
\`\`\`
|
|
823
|
+
`).join('\n');
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
module.exports = {
|
|
827
|
+
populateAllTemplates,
|
|
828
|
+
populateClaudeMd,
|
|
829
|
+
generateArchitectureSnapshot,
|
|
830
|
+
generateWorkflowIndex,
|
|
831
|
+
generateCodeToWorkflowMap,
|
|
832
|
+
generateWorkflowFile,
|
|
833
|
+
updateCategoryIndexes,
|
|
834
|
+
slugify
|
|
835
|
+
};
|