create-universal-ai-context 2.4.0 → 2.6.0-final

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 (153) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +331 -294
  3. package/bin/create-ai-context.js +1507 -764
  4. package/lib/adapters/aider.js +131 -131
  5. package/lib/adapters/antigravity.js +205 -205
  6. package/lib/adapters/claude.js +397 -397
  7. package/lib/adapters/cline.js +125 -125
  8. package/lib/adapters/continue.js +138 -138
  9. package/lib/adapters/copilot.js +131 -131
  10. package/lib/adapters/index.js +78 -78
  11. package/lib/adapters/windsurf.js +138 -138
  12. package/lib/ai-context-generator.js +234 -234
  13. package/lib/ai-orchestrator.js +432 -432
  14. package/lib/call-tracer.js +444 -444
  15. package/lib/content-preservation.js +243 -243
  16. package/lib/cross-tool-sync/file-watcher.js +274 -274
  17. package/lib/cross-tool-sync/index.js +41 -40
  18. package/lib/cross-tool-sync/sync-manager.js +540 -512
  19. package/lib/cross-tool-sync/sync-service.js +297 -297
  20. package/lib/detector.js +726 -726
  21. package/lib/doc-discovery.js +741 -741
  22. package/lib/drift-checker.js +920 -920
  23. package/lib/environment-detector.js +239 -239
  24. package/lib/index.js +399 -399
  25. package/lib/install-hooks.js +82 -82
  26. package/lib/installer.js +419 -419
  27. package/lib/migrate.js +328 -328
  28. package/lib/placeholder.js +632 -632
  29. package/lib/prompts.js +341 -341
  30. package/lib/smart-merge.js +540 -540
  31. package/lib/spinner.js +60 -60
  32. package/lib/static-analyzer.js +729 -729
  33. package/lib/template-coordination.js +148 -148
  34. package/lib/template-populator.js +843 -843
  35. package/lib/template-renderer.js +392 -392
  36. package/lib/utils/fs-wrapper.js +79 -79
  37. package/lib/utils/path-utils.js +60 -60
  38. package/lib/validate.js +155 -155
  39. package/package.json +1 -1
  40. package/templates/AI_CONTEXT.md.template +245 -245
  41. package/templates/base/README.md +260 -257
  42. package/templates/base/RPI_WORKFLOW_PLAN.md +325 -320
  43. package/templates/base/agents/api-developer.md +76 -76
  44. package/templates/base/agents/context-engineer.md +525 -525
  45. package/templates/base/agents/core-architect.md +76 -76
  46. package/templates/base/agents/database-ops.md +76 -76
  47. package/templates/base/agents/deployment-ops.md +76 -76
  48. package/templates/base/agents/integration-hub.md +76 -76
  49. package/templates/base/analytics/README.md +114 -114
  50. package/templates/base/automation/config.json +58 -58
  51. package/templates/base/automation/generators/code-mapper.js +308 -308
  52. package/templates/base/automation/generators/index-builder.js +321 -321
  53. package/templates/base/automation/hooks/post-commit.sh +83 -83
  54. package/templates/base/automation/hooks/pre-commit.sh +103 -103
  55. package/templates/base/ci-templates/README.md +108 -108
  56. package/templates/base/ci-templates/github-actions/context-check.yml +144 -144
  57. package/templates/base/ci-templates/github-actions/validate-docs.yml +105 -105
  58. package/templates/base/commands/analytics.md +238 -238
  59. package/templates/base/commands/auto-sync.md +172 -172
  60. package/templates/base/commands/collab.md +194 -194
  61. package/templates/base/commands/context-optimize.md +226 -0
  62. package/templates/base/commands/help.md +485 -450
  63. package/templates/base/commands/rpi-implement.md +164 -115
  64. package/templates/base/commands/rpi-plan.md +147 -93
  65. package/templates/base/commands/rpi-research.md +145 -88
  66. package/templates/base/commands/session-resume.md +144 -144
  67. package/templates/base/commands/session-save.md +112 -112
  68. package/templates/base/commands/validate-all.md +77 -77
  69. package/templates/base/commands/verify-docs-current.md +86 -86
  70. package/templates/base/config/base.json +57 -57
  71. package/templates/base/config/environments/development.json +13 -13
  72. package/templates/base/config/environments/production.json +17 -17
  73. package/templates/base/config/environments/staging.json +13 -13
  74. package/templates/base/config/local.json.example +21 -21
  75. package/templates/base/context/.meta/generated-at.json +18 -18
  76. package/templates/base/context/ARCHITECTURE_SNAPSHOT.md +156 -156
  77. package/templates/base/context/CODE_TO_WORKFLOW_MAP.md +94 -94
  78. package/templates/base/context/FILE_OWNERSHIP.md +57 -57
  79. package/templates/base/context/INTEGRATION_POINTS.md +92 -92
  80. package/templates/base/context/KNOWN_GOTCHAS.md +195 -195
  81. package/templates/base/context/TESTING_MAP.md +95 -95
  82. package/templates/base/context/WORKFLOW_INDEX.md +129 -129
  83. package/templates/base/context/workflows/WORKFLOW_TEMPLATE.md +294 -294
  84. package/templates/base/indexes/agents/CAPABILITY_MATRIX.md +255 -255
  85. package/templates/base/indexes/agents/CATEGORY_INDEX.md +44 -44
  86. package/templates/base/indexes/code/CATEGORY_INDEX.md +38 -38
  87. package/templates/base/indexes/routing/CATEGORY_INDEX.md +39 -39
  88. package/templates/base/indexes/search/CATEGORY_INDEX.md +39 -39
  89. package/templates/base/indexes/workflows/CATEGORY_INDEX.md +38 -38
  90. package/templates/base/knowledge/README.md +98 -98
  91. package/templates/base/knowledge/sessions/README.md +88 -88
  92. package/templates/base/knowledge/sessions/TEMPLATE.md +150 -150
  93. package/templates/base/knowledge/shared/decisions/0001-adopt-context-engineering.md +144 -144
  94. package/templates/base/knowledge/shared/decisions/README.md +49 -49
  95. package/templates/base/knowledge/shared/decisions/TEMPLATE.md +123 -123
  96. package/templates/base/knowledge/shared/patterns/README.md +62 -62
  97. package/templates/base/knowledge/shared/patterns/TEMPLATE.md +120 -120
  98. package/templates/base/plans/PLAN_TEMPLATE.md +316 -250
  99. package/templates/base/research/RESEARCH_TEMPLATE.md +245 -153
  100. package/templates/base/schemas/agent.schema.json +141 -141
  101. package/templates/base/schemas/anchors.schema.json +54 -54
  102. package/templates/base/schemas/automation.schema.json +93 -93
  103. package/templates/base/schemas/command.schema.json +134 -134
  104. package/templates/base/schemas/hashes.schema.json +40 -40
  105. package/templates/base/schemas/manifest.schema.json +117 -117
  106. package/templates/base/schemas/plan.schema.json +136 -136
  107. package/templates/base/schemas/research.schema.json +115 -115
  108. package/templates/base/schemas/roles.schema.json +34 -34
  109. package/templates/base/schemas/session.schema.json +77 -77
  110. package/templates/base/schemas/settings.schema.json +244 -244
  111. package/templates/base/schemas/staleness.schema.json +53 -53
  112. package/templates/base/schemas/team-config.schema.json +42 -42
  113. package/templates/base/schemas/workflow.schema.json +126 -126
  114. package/templates/base/session/checkpoints/.gitkeep +2 -2
  115. package/templates/base/session/current/state.json +20 -20
  116. package/templates/base/session/history/.gitkeep +2 -2
  117. package/templates/base/settings.json +3 -3
  118. package/templates/base/standards/COMPATIBILITY.md +219 -219
  119. package/templates/base/standards/EXTENSION_GUIDELINES.md +280 -280
  120. package/templates/base/standards/QUALITY_CHECKLIST.md +211 -211
  121. package/templates/base/standards/README.md +66 -66
  122. package/templates/base/sync/anchors.json +6 -6
  123. package/templates/base/sync/hashes.json +6 -6
  124. package/templates/base/sync/staleness.json +10 -10
  125. package/templates/base/team/README.md +168 -168
  126. package/templates/base/team/config.json +79 -79
  127. package/templates/base/team/roles.json +145 -145
  128. package/templates/base/tools/bin/claude-context.js +151 -151
  129. package/templates/base/tools/lib/anchor-resolver.js +276 -276
  130. package/templates/base/tools/lib/config-loader.js +363 -363
  131. package/templates/base/tools/lib/detector.js +350 -350
  132. package/templates/base/tools/lib/diagnose.js +206 -206
  133. package/templates/base/tools/lib/drift-detector.js +373 -373
  134. package/templates/base/tools/lib/errors.js +199 -199
  135. package/templates/base/tools/lib/index.js +36 -36
  136. package/templates/base/tools/lib/init.js +192 -192
  137. package/templates/base/tools/lib/logger.js +230 -230
  138. package/templates/base/tools/lib/placeholder.js +201 -201
  139. package/templates/base/tools/lib/session-manager.js +354 -354
  140. package/templates/base/tools/lib/validate.js +521 -521
  141. package/templates/base/tools/package.json +49 -49
  142. package/templates/handlebars/aider-config.hbs +146 -80
  143. package/templates/handlebars/antigravity.hbs +377 -377
  144. package/templates/handlebars/claude.hbs +183 -183
  145. package/templates/handlebars/cline.hbs +62 -62
  146. package/templates/handlebars/continue-config.hbs +116 -116
  147. package/templates/handlebars/copilot.hbs +130 -130
  148. package/templates/handlebars/partials/gotcha-list.hbs +11 -11
  149. package/templates/handlebars/partials/header.hbs +3 -3
  150. package/templates/handlebars/partials/workflow-summary.hbs +16 -16
  151. package/templates/handlebars/windsurf-rules.hbs +69 -69
  152. package/templates/hooks/post-commit.hbs +28 -29
  153. package/templates/hooks/pre-commit.hbs +46 -46
@@ -1,632 +1,632 @@
1
- /**
2
- * AI Context Engineering - Placeholder Replacement Engine
3
- *
4
- * Finds and replaces {{PLACEHOLDER}} patterns in template files.
5
- */
6
-
7
- const fs = require('fs');
8
- const path = require('path');
9
- const { glob } = require('glob');
10
- const chalk = require('chalk');
11
-
12
- /**
13
- * Context directory and file names
14
- */
15
- const AI_CONTEXT_DIR = '.ai-context';
16
- const AI_CONTEXT_FILE = 'AI_CONTEXT.md';
17
-
18
- /**
19
- * Known placeholders with their descriptions
20
- */
21
- const KNOWN_PLACEHOLDERS = {
22
- // Project identity
23
- PROJECT_NAME: { description: 'Project name', example: 'my-awesome-app' },
24
- PROJECT_DESCRIPTION: { description: 'Brief project description', example: 'A web application for...' },
25
- TECH_STACK: { description: 'Technologies used', example: 'Python 3.11, FastAPI, PostgreSQL' },
26
- PRODUCTION_URL: { description: 'Production URL', example: 'https://api.example.com' },
27
- PROJECT_STATUS: { description: 'Project status', example: 'Production' },
28
-
29
- // URLs
30
- API_URL: { description: 'API base URL', example: 'https://api.example.com/v1' },
31
- REPO_URL: { description: 'Repository URL', example: 'https://github.com/user/repo' },
32
- DEPLOYMENT_PLATFORM: { description: 'Where deployed', example: 'AWS ECS' },
33
-
34
- // Commands
35
- INSTALL_COMMAND: { description: 'Install dependencies command', example: 'npm install' },
36
- DEV_START_COMMAND: { description: 'Start dev server command', example: 'npm run dev' },
37
- TEST_COMMAND: { description: 'Run tests command', example: 'npm test' },
38
- TEST_E2E_COMMAND: { description: 'Run E2E tests command', example: 'npm run test:e2e' },
39
- TEST_COVERAGE_COMMAND: { description: 'Run coverage command', example: 'npm run coverage' },
40
- MIGRATION_CREATE_COMMAND: { description: 'Create migration command', example: 'npm run db:migrate:create' },
41
- MIGRATION_RUN_COMMAND: { description: 'Run migrations command', example: 'npm run db:migrate' },
42
- DEPLOY_COMMAND: { description: 'Deploy command', example: 'npm run deploy' },
43
-
44
- // Paths
45
- MODELS_PATH: { description: 'Models directory path', example: 'src/models/' },
46
- MIGRATIONS_PATH: { description: 'Migrations directory path', example: 'src/migrations/' },
47
- CORE_FILES_LIST: { description: 'List of core files', example: '- src/services/\n- src/models/' },
48
-
49
- // Counts
50
- WORKFLOWS_COUNT: { description: 'Number of workflows', example: '12' },
51
- AGENTS_COUNT: { description: 'Number of agents', example: '6' },
52
- COMMANDS_COUNT: { description: 'Number of commands', example: '8' },
53
-
54
- // Meta
55
- DATE: { description: 'Current date', example: '2025-01-24' },
56
- AGENT_TABLE_ROWS: { description: 'Agent table rows', example: '| core-architect | System design |' },
57
- };
58
-
59
- /**
60
- * Get default placeholder values based on config, tech stack, and analysis
61
- * @param {object} config - Configuration from CLI (includes discoveredValues from merge)
62
- * @param {object} techStack - Detected tech stack
63
- * @param {object} analysis - Codebase analysis results
64
- * @returns {object} Placeholder values
65
- */
66
- function getDefaultValues(config = {}, techStack = {}, analysis = {}) {
67
- // Get discovered values from merge phase (if available)
68
- const discoveredValues = config.discoveredValues || {};
69
- const today = new Date().toISOString().split('T')[0];
70
- const projectName = config.projectName || 'my-project';
71
- const projectSlug = projectName.toLowerCase().replace(/[^a-z0-9]+/g, '-');
72
-
73
- // Get LOC information from tech stack or analysis
74
- const loc = techStack.loc || analysis.linesOfCode || { total: 0, code: 0, files: 0 };
75
-
76
- // Get file purposes from tech stack
77
- const filePurposes = techStack.filePurposes || {};
78
-
79
- // Determine commands based on tech stack
80
- let commands = {
81
- install: 'npm install',
82
- dev: 'npm run dev',
83
- test: 'npm test',
84
- testE2e: 'npm run test:e2e',
85
- testCoverage: 'npm run test:coverage',
86
- migrateCreate: 'npm run migration:create',
87
- migrateRun: 'npm run migration:run',
88
- deploy: 'npm run deploy'
89
- };
90
-
91
- if (techStack.commands) {
92
- commands = { ...commands, ...techStack.commands };
93
- } else if (techStack.languages?.includes('python')) {
94
- commands = {
95
- install: 'pip install -r requirements.txt',
96
- dev: 'python main.py',
97
- test: 'pytest',
98
- testE2e: 'pytest tests/e2e/',
99
- testCoverage: 'pytest --cov',
100
- migrateCreate: 'alembic revision --autogenerate',
101
- migrateRun: 'alembic upgrade head',
102
- deploy: 'docker-compose up -d'
103
- };
104
- } else if (techStack.languages?.includes('go')) {
105
- commands = {
106
- install: 'go mod download',
107
- dev: 'go run .',
108
- test: 'go test ./...',
109
- testE2e: 'go test ./e2e/...',
110
- testCoverage: 'go test -cover ./...',
111
- migrateCreate: 'migrate create -ext sql',
112
- migrateRun: 'migrate -path migrations up',
113
- deploy: 'docker-compose up -d'
114
- };
115
- } else if (techStack.languages?.includes('rust')) {
116
- commands = {
117
- install: 'cargo build',
118
- dev: 'cargo run',
119
- test: 'cargo test',
120
- testE2e: 'cargo test --test e2e',
121
- testCoverage: 'cargo tarpaulin',
122
- migrateCreate: 'sqlx migrate add',
123
- migrateRun: 'sqlx migrate run',
124
- deploy: 'cargo build --release'
125
- };
126
- }
127
-
128
- // Build core files list from analysis
129
- let coreFilesList = '- src/\n- config/';
130
- if (analysis.entryPoints && analysis.entryPoints.length > 0) {
131
- const uniqueFiles = [...new Set(analysis.entryPoints.map(e => e.file))];
132
- coreFilesList = uniqueFiles.slice(0, 10).map(f => `- \`${f}\``).join('\n');
133
- }
134
-
135
- // Get workflow count from analysis
136
- const workflowCount = analysis.workflows?.length || 0;
137
-
138
- // Detect deployment platform from project files
139
- const deploymentPlatform = detectDeploymentPlatform(config.targetDir);
140
-
141
- // Build models and migrations paths from file purposes
142
- const modelsPath = buildPathFromPurpose(filePurposes, 'model', 'models/');
143
- const migrationsPath = buildPathFromPurpose(filePurposes, 'migration', 'migrations/');
144
-
145
- // Build external integrations list from dependencies
146
- const externalIntegrations = buildExternalIntegrations(techStack.databases || [], analysis.dependencies || []);
147
-
148
- // Build architecture diagram from analysis
149
- const architectureDiagram = buildArchitectureDiagram(analysis.architecture || {}, filePurposes);
150
-
151
- // Determine search patterns based on language
152
- const searchPatterns = getSearchPatterns(techStack.languages || []);
153
-
154
- // Entry point count
155
- const entryPointCount = analysis.entryPoints?.length || techStack.entryPoints?.length || 0;
156
-
157
- const defaults = {
158
- // Project identity
159
- PROJECT_NAME: projectName,
160
- PROJECT_SLUG: projectSlug,
161
- PROJECT_DESCRIPTION: `${projectName} application`,
162
- TECH_STACK: techStack.summary || techStack.stack || 'Not detected',
163
- PRODUCTION_URL: `https://${projectSlug}.example.com`,
164
- PROJECT_STATUS: 'Development',
165
-
166
- // URLs
167
- API_URL: `https://api.${projectSlug}.example.com`,
168
- REPO_URL: `https://github.com/user/${projectSlug}`,
169
- DEPLOYMENT_PLATFORM: deploymentPlatform,
170
-
171
- // Commands
172
- INSTALL_COMMAND: commands.install,
173
- DEV_START_COMMAND: commands.dev,
174
- TEST_COMMAND: commands.test,
175
- TEST_E2E_COMMAND: commands.testE2e,
176
- TEST_COVERAGE_COMMAND: commands.testCoverage,
177
- MIGRATION_CREATE_COMMAND: commands.migrateCreate,
178
- MIGRATION_RUN_COMMAND: commands.migrateRun,
179
- DEPLOY_COMMAND: commands.deploy,
180
-
181
- // Paths (from analysis)
182
- MODELS_PATH: modelsPath,
183
- MIGRATIONS_PATH: migrationsPath,
184
- CORE_FILES_LIST: coreFilesList,
185
-
186
- // Counts (from analysis)
187
- WORKFLOWS_COUNT: String(workflowCount),
188
- WORKFLOW_DOMAINS_COUNT: String(workflowCount > 0 ? Math.min(workflowCount, 5) : 0),
189
- CODE_DOMAINS_COUNT: String(Object.keys(filePurposes.counts || {}).length),
190
- ENTRY_POINTS_COUNT: String(entryPointCount),
191
- SOURCE_FILES_COUNT: String(loc.files || 0),
192
- LINES_OF_CODE: String(loc.code || loc.total || 0),
193
- AGENTS_COUNT: '6',
194
- COMMANDS_COUNT: '11',
195
- INDEX_FILES_COUNT: '15',
196
-
197
- // Meta
198
- DATE: today,
199
- AGENT_TABLE_ROWS: '',
200
- AGENT_ROUTING_TABLE: '@context-engineer for setup, @core-architect for design',
201
- DEBUGGING_QUICK_REFS: 'KNOWN_GOTCHAS.md, logs/',
202
-
203
- // Examples (based on detected stack)
204
- EXAMPLE_REFACTOR_TASK: buildExampleTask('refactor', techStack),
205
- EXAMPLE_LOWLEVEL_TASK: buildExampleTask('lowlevel', techStack),
206
- EXAMPLE_FEATURE_TASK: buildExampleTask('feature', techStack),
207
-
208
- // Search patterns (language-aware)
209
- CONFIG_SEARCH_PATTERN: searchPatterns.config,
210
- URL_SEARCH_PATTERN: searchPatterns.url,
211
-
212
- // External integrations (from dependencies)
213
- EXTERNAL_INTEGRATIONS_LIST: externalIntegrations,
214
-
215
- // Architecture (from analysis)
216
- ARCHITECTURE_DIAGRAM: architectureDiagram,
217
-
218
- // URLs
219
- CRITICAL_URLS: `- Production: https://${projectSlug}.example.com`,
220
- BUSINESS_CONSTANTS: '- TBD (document key business constants)',
221
-
222
- // Gotchas
223
- GOTCHA_CATEGORY_1: 'Database',
224
- GOTCHA_1_ITEMS: '- TBD (document database gotchas)',
225
- GOTCHA_CATEGORY_2: 'API',
226
- GOTCHA_2_ITEMS: '- TBD (document API gotchas)',
227
-
228
- // Production
229
- PRODUCTION_PLATFORM: deploymentPlatform,
230
- PRODUCTION_SERVICES: buildProductionServices(techStack),
231
- MONITORING_COMMANDS: 'Check logs, health endpoints',
232
-
233
- // Constraints
234
- MIGRATION_CONSTRAINTS: 'Always backup before migrations',
235
- TESTING_CONSTRAINTS: 'Run tests before merging',
236
- SECURITY_CONSTRAINTS: 'Never commit secrets',
237
- CONTACT_INFO: 'TBD (add contact info)',
238
-
239
- // Languages and frameworks (for templates)
240
- PRIMARY_LANGUAGE: techStack.languages?.[0] || 'javascript',
241
- PRIMARY_FRAMEWORK: techStack.frameworks?.[0] || '',
242
- DATABASE_TYPE: techStack.databases?.[0] || '',
243
- };
244
-
245
- // Merge with discovered values - discovered values take precedence
246
- // This preserves user customizations from existing documentation
247
- return { ...defaults, ...discoveredValues };
248
- }
249
-
250
- /**
251
- * Detect deployment platform from project files
252
- */
253
- function detectDeploymentPlatform(projectRoot) {
254
- if (!projectRoot) return 'TBD';
255
-
256
- const platforms = [
257
- { file: 'vercel.json', name: 'Vercel' },
258
- { file: 'netlify.toml', name: 'Netlify' },
259
- { file: 'fly.toml', name: 'Fly.io' },
260
- { file: 'render.yaml', name: 'Render' },
261
- { file: 'railway.json', name: 'Railway' },
262
- { file: 'Dockerfile', name: 'Docker' },
263
- { file: 'docker-compose.yml', name: 'Docker Compose' },
264
- { file: 'docker-compose.yaml', name: 'Docker Compose' },
265
- { file: 'heroku.yml', name: 'Heroku' },
266
- { file: 'Procfile', name: 'Heroku' },
267
- { file: 'app.yaml', name: 'Google Cloud' },
268
- { file: 'serverless.yml', name: 'Serverless' },
269
- { file: 'terraform', name: 'Terraform' },
270
- ];
271
-
272
- for (const platform of platforms) {
273
- try {
274
- if (fs.existsSync(path.join(projectRoot, platform.file))) {
275
- return platform.name;
276
- }
277
- } catch {
278
- // Ignore errors
279
- }
280
- }
281
-
282
- return 'TBD';
283
- }
284
-
285
- /**
286
- * Build path from file purposes
287
- */
288
- function buildPathFromPurpose(filePurposes, purpose, defaultPath) {
289
- const files = filePurposes?.files?.[purpose];
290
- if (files && files.length > 0) {
291
- const dir = path.dirname(files[0]);
292
- return dir === '.' ? defaultPath : dir + '/';
293
- }
294
- return defaultPath;
295
- }
296
-
297
- /**
298
- * Build external integrations list from dependencies
299
- */
300
- function buildExternalIntegrations(databases, dependencies) {
301
- const integrations = [];
302
-
303
- // Add databases
304
- for (const db of databases) {
305
- integrations.push(`- **${capitalize(db)}**: Database`);
306
- }
307
-
308
- // Check dependencies for known integrations
309
- const knownIntegrations = {
310
- stripe: 'Payment processing',
311
- 'aws-sdk': 'AWS services',
312
- '@aws-sdk': 'AWS services',
313
- firebase: 'Firebase services',
314
- twilio: 'SMS/Voice',
315
- sendgrid: 'Email delivery',
316
- '@sendgrid': 'Email delivery',
317
- nodemailer: 'Email',
318
- redis: 'Caching',
319
- ioredis: 'Caching',
320
- elasticsearch: 'Search',
321
- '@elastic': 'Search',
322
- 'socket.io': 'Real-time',
323
- pusher: 'Real-time',
324
- cloudinary: 'Media storage',
325
- 's3': 'File storage',
326
- };
327
-
328
- for (const dep of dependencies) {
329
- const depName = dep.name || dep;
330
- for (const [key, desc] of Object.entries(knownIntegrations)) {
331
- if (depName.toLowerCase().includes(key)) {
332
- integrations.push(`- **${depName}**: ${desc}`);
333
- break;
334
- }
335
- }
336
- }
337
-
338
- if (integrations.length === 0) {
339
- return '*No external integrations detected. Document manually if present.*';
340
- }
341
-
342
- return integrations.slice(0, 10).join('\n');
343
- }
344
-
345
- /**
346
- * Build architecture diagram from analysis
347
- */
348
- function buildArchitectureDiagram(architecture, filePurposes) {
349
- const layers = architecture?.layers || [];
350
- const purposeCounts = filePurposes?.counts || {};
351
-
352
- // Build diagram based on discovered layers or file purposes
353
- if (layers.length > 0) {
354
- let diagram = '┌─────────────────────────────────────┐\n';
355
- for (const layer of layers.slice(0, 4)) {
356
- diagram += `│ [${(layer.name || 'Layer').padEnd(30)}] │\n`;
357
- }
358
- diagram += '└─────────────────────────────────────┘';
359
- return diagram;
360
- }
361
-
362
- // Build from file purposes
363
- const hasControllers = purposeCounts.controller > 0 || purposeCounts.route > 0;
364
- const hasServices = purposeCounts.service > 0;
365
- const hasModels = purposeCounts.model > 0;
366
- const hasRepos = purposeCounts.repository > 0;
367
-
368
- return `┌─────────────────────────────────────┐
369
- │ [Application] │
370
- │ │
371
- ${hasControllers ? '│ ┌───────────┐ ┌───────────┐ │\n│ │ API │ │ Routes │ │\n│ └───────────┘ └───────────┘ │\n' : ''}${hasServices ? '│ ┌───────────────────────────┐ │\n│ │ Services │ │\n│ └───────────────────────────┘ │\n' : ''}${hasModels || hasRepos ? '│ ┌───────────┐ ┌───────────┐ │\n│ │ Models │ │ Database │ │\n│ └───────────┘ └───────────┘ │\n' : ''}└─────────────────────────────────────┘`;
372
- }
373
-
374
- /**
375
- * Get search patterns based on languages
376
- */
377
- function getSearchPatterns(languages) {
378
- if (languages.includes('python')) {
379
- return {
380
- config: 'grep -r "os.environ" --include="*.py"',
381
- url: 'grep -rE "https?://" --include="*.py" --include="*.json"'
382
- };
383
- }
384
- if (languages.includes('go')) {
385
- return {
386
- config: 'grep -r "os.Getenv" --include="*.go"',
387
- url: 'grep -rE "https?://" --include="*.go" --include="*.json"'
388
- };
389
- }
390
- if (languages.includes('rust')) {
391
- return {
392
- config: 'grep -r "env::var" --include="*.rs"',
393
- url: 'grep -rE "https?://" --include="*.rs" --include="*.toml"'
394
- };
395
- }
396
- // Default: JavaScript/TypeScript
397
- return {
398
- config: 'grep -r "process.env" --include="*.js" --include="*.ts"',
399
- url: 'grep -rE "https?://" --include="*.js" --include="*.ts" --include="*.json"'
400
- };
401
- }
402
-
403
- /**
404
- * Build example task based on tech stack
405
- */
406
- function buildExampleTask(type, techStack) {
407
- const framework = techStack.frameworks?.[0] || '';
408
- const language = techStack.languages?.[0] || 'javascript';
409
-
410
- const examples = {
411
- refactor: {
412
- express: 'Refactor the authentication middleware',
413
- fastapi: 'Refactor the dependency injection system',
414
- django: 'Refactor the view decorators',
415
- nextjs: 'Refactor the API routes to app router',
416
- default: 'Refactor the authentication flow'
417
- },
418
- lowlevel: {
419
- express: 'Fix hardcoded timeout in request handler',
420
- fastapi: 'Fix hardcoded API URL in config',
421
- django: 'Fix hardcoded secret key in settings',
422
- default: 'Fix hardcoded API URL in config'
423
- },
424
- feature: {
425
- express: 'Add rate limiting middleware',
426
- fastapi: 'Add WebSocket support for real-time updates',
427
- django: 'Add user notifications feature',
428
- nextjs: 'Add server-side caching',
429
- default: 'Add user notifications feature'
430
- }
431
- };
432
-
433
- return examples[type][framework] || examples[type].default;
434
- }
435
-
436
- /**
437
- * Build production services list
438
- */
439
- function buildProductionServices(techStack) {
440
- const services = ['Web'];
441
-
442
- if (techStack.frameworks?.some(f => ['express', 'fastapi', 'django', 'rails'].includes(f))) {
443
- services.push('API');
444
- }
445
-
446
- if (techStack.databases?.length > 0) {
447
- services.push('Database');
448
- }
449
-
450
- if (techStack.databases?.includes('redis')) {
451
- services.push('Cache');
452
- }
453
-
454
- return services.join(', ');
455
- }
456
-
457
- /**
458
- * Capitalize first letter
459
- */
460
- function capitalize(str) {
461
- return str.charAt(0).toUpperCase() + str.slice(1);
462
- }
463
-
464
- /**
465
- * Replace placeholders in all files in a directory
466
- */
467
- /**
468
- * Replace all placeholders in all files
469
- * @param {string} targetDir - Target directory
470
- * @param {object} config - Configuration options
471
- * @param {boolean} config.failOnUnreplaced - Throw if placeholders remain
472
- * @param {boolean} config.verbose - Log warnings for unreplaced placeholders
473
- * @returns {object} Results { totalReplaced: number, unreplaced: Array, unreplacedCount: number }
474
- */
475
- async function replacePlaceholders(targetDir, config = {}) {
476
- const { failOnUnreplaced = false, verbose = false } = config;
477
- const contextDir = path.join(targetDir, AI_CONTEXT_DIR);
478
- const values = getDefaultValues(config, config.techStack || {}, config.analysis || {});
479
-
480
- // Find all markdown and JSON files
481
- const files = await glob('**/*.{md,json}', {
482
- cwd: contextDir,
483
- ignore: ['node_modules/**', '.git/**'],
484
- nodir: true,
485
- absolute: true
486
- });
487
-
488
- let totalReplaced = 0;
489
- const unreplacedDetails = [];
490
-
491
- for (const filePath of files) {
492
- try {
493
- let content = fs.readFileSync(filePath, 'utf8');
494
- const originalContent = content;
495
-
496
- // Replace all placeholders
497
- for (const [key, value] of Object.entries(values)) {
498
- const pattern = new RegExp(`\\{\\{${key}\\}\\}`, 'g');
499
- content = content.replace(pattern, String(value));
500
- }
501
-
502
- if (content !== originalContent) {
503
- fs.writeFileSync(filePath, content, 'utf8');
504
- totalReplaced++;
505
- }
506
-
507
- // Check for remaining placeholders
508
- const remaining = findPlaceholdersInContent(content, filePath);
509
- unreplacedDetails.push(...remaining);
510
- } catch (error) {
511
- // Skip files that can't be read
512
- }
513
- }
514
-
515
- // Also replace in AI_CONTEXT.md at root
516
- const aiContextPath = path.join(targetDir, AI_CONTEXT_FILE);
517
- if (fs.existsSync(aiContextPath)) {
518
- try {
519
- let content = fs.readFileSync(aiContextPath, 'utf8');
520
- const originalContent = content;
521
-
522
- for (const [key, value] of Object.entries(values)) {
523
- const pattern = new RegExp(`\\{\\{${key}\\}\\}`, 'g');
524
- content = content.replace(pattern, String(value));
525
- }
526
-
527
- if (content !== originalContent) {
528
- fs.writeFileSync(aiContextPath, content, 'utf8');
529
- totalReplaced++;
530
- }
531
-
532
- // Check for remaining placeholders
533
- const remaining = findPlaceholdersInContent(content, aiContextPath);
534
- unreplacedDetails.push(...remaining);
535
- } catch (error) {
536
- // Skip if can't read
537
- }
538
- }
539
-
540
- // Deduplicate unreplaced items
541
- const uniqueUnreplaced = deduplicateUnreplaced(unreplacedDetails);
542
- const unreplacedCount = uniqueUnreplaced.length;
543
-
544
- // Handle unreplaced placeholders
545
- if (unreplacedCount > 0) {
546
- const placeholders = uniqueUnreplaced.map(u => u.placeholder).join(', ');
547
- const message = `${unreplacedCount} placeholder${unreplacedCount > 1 ? 's' : ''} not replaced: ${placeholders}`;
548
-
549
- if (verbose) {
550
- console.warn(chalk.yellow(`⚠ ${message}`));
551
- uniqueUnreplaced.forEach(u => {
552
- console.warn(chalk.gray(` - ${u.placeholder} in ${u.file}`));
553
- });
554
- }
555
-
556
- if (failOnUnreplaced) {
557
- throw new Error(message);
558
- }
559
- }
560
-
561
- return { totalReplaced, unreplaced: uniqueUnreplaced, unreplacedCount };
562
- }
563
-
564
- /**
565
- * Find placeholders in content (helper for replacePlaceholders)
566
- * @param {string} content - File content
567
- * @param {string} filePath - File path
568
- * @returns {Array} List of unreplaced placeholder info
569
- */
570
- function findPlaceholdersInContent(content, filePath) {
571
- const placeholderPattern = /\{\{([A-Z_]+)\}\}/g;
572
- const found = [];
573
- let match;
574
-
575
- while ((match = placeholderPattern.exec(content)) !== null) {
576
- found.push({
577
- placeholder: match[0],
578
- name: match[1],
579
- file: path.relative(process.cwd(), filePath),
580
- index: match.index,
581
- known: KNOWN_PLACEHOLDERS.hasOwnProperty(match[1])
582
- });
583
- }
584
-
585
- return found;
586
- }
587
-
588
- /**
589
- * Deduplicate unreplaced placeholder list
590
- * @param {Array} unreplaced - List of unreplaced items
591
- * @returns {Array} Deduplicated list
592
- */
593
- function deduplicateUnreplaced(unreplaced) {
594
- const seen = new Set();
595
- return unreplaced.filter(item => {
596
- const key = `${item.placeholder}:${item.file}`;
597
- if (seen.has(key)) {
598
- return false;
599
- }
600
- seen.add(key);
601
- return true;
602
- });
603
- }
604
-
605
- /**
606
- * Find all placeholders in a file
607
- */
608
- function findPlaceholders(filePath) {
609
- const content = fs.readFileSync(filePath, 'utf8');
610
- const placeholderPattern = /\{\{([A-Z_]+)\}\}/g;
611
-
612
- const found = [];
613
- let match;
614
-
615
- while ((match = placeholderPattern.exec(content)) !== null) {
616
- found.push({
617
- placeholder: match[0],
618
- name: match[1],
619
- index: match.index,
620
- known: KNOWN_PLACEHOLDERS.hasOwnProperty(match[1]),
621
- });
622
- }
623
-
624
- return found;
625
- }
626
-
627
- module.exports = {
628
- replacePlaceholders,
629
- findPlaceholders,
630
- getDefaultValues,
631
- KNOWN_PLACEHOLDERS,
632
- };
1
+ /**
2
+ * AI Context Engineering - Placeholder Replacement Engine
3
+ *
4
+ * Finds and replaces {{PLACEHOLDER}} patterns in template files.
5
+ */
6
+
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+ const { glob } = require('glob');
10
+ const chalk = require('chalk');
11
+
12
+ /**
13
+ * Context directory and file names
14
+ */
15
+ const AI_CONTEXT_DIR = '.ai-context';
16
+ const AI_CONTEXT_FILE = 'AI_CONTEXT.md';
17
+
18
+ /**
19
+ * Known placeholders with their descriptions
20
+ */
21
+ const KNOWN_PLACEHOLDERS = {
22
+ // Project identity
23
+ PROJECT_NAME: { description: 'Project name', example: 'my-awesome-app' },
24
+ PROJECT_DESCRIPTION: { description: 'Brief project description', example: 'A web application for...' },
25
+ TECH_STACK: { description: 'Technologies used', example: 'Python 3.11, FastAPI, PostgreSQL' },
26
+ PRODUCTION_URL: { description: 'Production URL', example: 'https://api.example.com' },
27
+ PROJECT_STATUS: { description: 'Project status', example: 'Production' },
28
+
29
+ // URLs
30
+ API_URL: { description: 'API base URL', example: 'https://api.example.com/v1' },
31
+ REPO_URL: { description: 'Repository URL', example: 'https://github.com/user/repo' },
32
+ DEPLOYMENT_PLATFORM: { description: 'Where deployed', example: 'AWS ECS' },
33
+
34
+ // Commands
35
+ INSTALL_COMMAND: { description: 'Install dependencies command', example: 'npm install' },
36
+ DEV_START_COMMAND: { description: 'Start dev server command', example: 'npm run dev' },
37
+ TEST_COMMAND: { description: 'Run tests command', example: 'npm test' },
38
+ TEST_E2E_COMMAND: { description: 'Run E2E tests command', example: 'npm run test:e2e' },
39
+ TEST_COVERAGE_COMMAND: { description: 'Run coverage command', example: 'npm run coverage' },
40
+ MIGRATION_CREATE_COMMAND: { description: 'Create migration command', example: 'npm run db:migrate:create' },
41
+ MIGRATION_RUN_COMMAND: { description: 'Run migrations command', example: 'npm run db:migrate' },
42
+ DEPLOY_COMMAND: { description: 'Deploy command', example: 'npm run deploy' },
43
+
44
+ // Paths
45
+ MODELS_PATH: { description: 'Models directory path', example: 'src/models/' },
46
+ MIGRATIONS_PATH: { description: 'Migrations directory path', example: 'src/migrations/' },
47
+ CORE_FILES_LIST: { description: 'List of core files', example: '- src/services/\n- src/models/' },
48
+
49
+ // Counts
50
+ WORKFLOWS_COUNT: { description: 'Number of workflows', example: '12' },
51
+ AGENTS_COUNT: { description: 'Number of agents', example: '6' },
52
+ COMMANDS_COUNT: { description: 'Number of commands', example: '8' },
53
+
54
+ // Meta
55
+ DATE: { description: 'Current date', example: '2025-01-24' },
56
+ AGENT_TABLE_ROWS: { description: 'Agent table rows', example: '| core-architect | System design |' },
57
+ };
58
+
59
+ /**
60
+ * Get default placeholder values based on config, tech stack, and analysis
61
+ * @param {object} config - Configuration from CLI (includes discoveredValues from merge)
62
+ * @param {object} techStack - Detected tech stack
63
+ * @param {object} analysis - Codebase analysis results
64
+ * @returns {object} Placeholder values
65
+ */
66
+ function getDefaultValues(config = {}, techStack = {}, analysis = {}) {
67
+ // Get discovered values from merge phase (if available)
68
+ const discoveredValues = config.discoveredValues || {};
69
+ const today = new Date().toISOString().split('T')[0];
70
+ const projectName = config.projectName || 'my-project';
71
+ const projectSlug = projectName.toLowerCase().replace(/[^a-z0-9]+/g, '-');
72
+
73
+ // Get LOC information from tech stack or analysis
74
+ const loc = techStack.loc || analysis.linesOfCode || { total: 0, code: 0, files: 0 };
75
+
76
+ // Get file purposes from tech stack
77
+ const filePurposes = techStack.filePurposes || {};
78
+
79
+ // Determine commands based on tech stack
80
+ let commands = {
81
+ install: 'npm install',
82
+ dev: 'npm run dev',
83
+ test: 'npm test',
84
+ testE2e: 'npm run test:e2e',
85
+ testCoverage: 'npm run test:coverage',
86
+ migrateCreate: 'npm run migration:create',
87
+ migrateRun: 'npm run migration:run',
88
+ deploy: 'npm run deploy'
89
+ };
90
+
91
+ if (techStack.commands) {
92
+ commands = { ...commands, ...techStack.commands };
93
+ } else if (techStack.languages?.includes('python')) {
94
+ commands = {
95
+ install: 'pip install -r requirements.txt',
96
+ dev: 'python main.py',
97
+ test: 'pytest',
98
+ testE2e: 'pytest tests/e2e/',
99
+ testCoverage: 'pytest --cov',
100
+ migrateCreate: 'alembic revision --autogenerate',
101
+ migrateRun: 'alembic upgrade head',
102
+ deploy: 'docker-compose up -d'
103
+ };
104
+ } else if (techStack.languages?.includes('go')) {
105
+ commands = {
106
+ install: 'go mod download',
107
+ dev: 'go run .',
108
+ test: 'go test ./...',
109
+ testE2e: 'go test ./e2e/...',
110
+ testCoverage: 'go test -cover ./...',
111
+ migrateCreate: 'migrate create -ext sql',
112
+ migrateRun: 'migrate -path migrations up',
113
+ deploy: 'docker-compose up -d'
114
+ };
115
+ } else if (techStack.languages?.includes('rust')) {
116
+ commands = {
117
+ install: 'cargo build',
118
+ dev: 'cargo run',
119
+ test: 'cargo test',
120
+ testE2e: 'cargo test --test e2e',
121
+ testCoverage: 'cargo tarpaulin',
122
+ migrateCreate: 'sqlx migrate add',
123
+ migrateRun: 'sqlx migrate run',
124
+ deploy: 'cargo build --release'
125
+ };
126
+ }
127
+
128
+ // Build core files list from analysis
129
+ let coreFilesList = '- src/\n- config/';
130
+ if (analysis.entryPoints && analysis.entryPoints.length > 0) {
131
+ const uniqueFiles = [...new Set(analysis.entryPoints.map(e => e.file))];
132
+ coreFilesList = uniqueFiles.slice(0, 10).map(f => `- \`${f}\``).join('\n');
133
+ }
134
+
135
+ // Get workflow count from analysis
136
+ const workflowCount = analysis.workflows?.length || 0;
137
+
138
+ // Detect deployment platform from project files
139
+ const deploymentPlatform = detectDeploymentPlatform(config.targetDir);
140
+
141
+ // Build models and migrations paths from file purposes
142
+ const modelsPath = buildPathFromPurpose(filePurposes, 'model', 'models/');
143
+ const migrationsPath = buildPathFromPurpose(filePurposes, 'migration', 'migrations/');
144
+
145
+ // Build external integrations list from dependencies
146
+ const externalIntegrations = buildExternalIntegrations(techStack.databases || [], analysis.dependencies || []);
147
+
148
+ // Build architecture diagram from analysis
149
+ const architectureDiagram = buildArchitectureDiagram(analysis.architecture || {}, filePurposes);
150
+
151
+ // Determine search patterns based on language
152
+ const searchPatterns = getSearchPatterns(techStack.languages || []);
153
+
154
+ // Entry point count
155
+ const entryPointCount = analysis.entryPoints?.length || techStack.entryPoints?.length || 0;
156
+
157
+ const defaults = {
158
+ // Project identity
159
+ PROJECT_NAME: projectName,
160
+ PROJECT_SLUG: projectSlug,
161
+ PROJECT_DESCRIPTION: `${projectName} application`,
162
+ TECH_STACK: techStack.summary || techStack.stack || 'Not detected',
163
+ PRODUCTION_URL: `https://${projectSlug}.example.com`,
164
+ PROJECT_STATUS: 'Development',
165
+
166
+ // URLs
167
+ API_URL: `https://api.${projectSlug}.example.com`,
168
+ REPO_URL: `https://github.com/user/${projectSlug}`,
169
+ DEPLOYMENT_PLATFORM: deploymentPlatform,
170
+
171
+ // Commands
172
+ INSTALL_COMMAND: commands.install,
173
+ DEV_START_COMMAND: commands.dev,
174
+ TEST_COMMAND: commands.test,
175
+ TEST_E2E_COMMAND: commands.testE2e,
176
+ TEST_COVERAGE_COMMAND: commands.testCoverage,
177
+ MIGRATION_CREATE_COMMAND: commands.migrateCreate,
178
+ MIGRATION_RUN_COMMAND: commands.migrateRun,
179
+ DEPLOY_COMMAND: commands.deploy,
180
+
181
+ // Paths (from analysis)
182
+ MODELS_PATH: modelsPath,
183
+ MIGRATIONS_PATH: migrationsPath,
184
+ CORE_FILES_LIST: coreFilesList,
185
+
186
+ // Counts (from analysis)
187
+ WORKFLOWS_COUNT: String(workflowCount),
188
+ WORKFLOW_DOMAINS_COUNT: String(workflowCount > 0 ? Math.min(workflowCount, 5) : 0),
189
+ CODE_DOMAINS_COUNT: String(Object.keys(filePurposes.counts || {}).length),
190
+ ENTRY_POINTS_COUNT: String(entryPointCount),
191
+ SOURCE_FILES_COUNT: String(loc.files || 0),
192
+ LINES_OF_CODE: String(loc.code || loc.total || 0),
193
+ AGENTS_COUNT: '6',
194
+ COMMANDS_COUNT: '11',
195
+ INDEX_FILES_COUNT: '15',
196
+
197
+ // Meta
198
+ DATE: today,
199
+ AGENT_TABLE_ROWS: '',
200
+ AGENT_ROUTING_TABLE: '@context-engineer for setup, @core-architect for design',
201
+ DEBUGGING_QUICK_REFS: 'KNOWN_GOTCHAS.md, logs/',
202
+
203
+ // Examples (based on detected stack)
204
+ EXAMPLE_REFACTOR_TASK: buildExampleTask('refactor', techStack),
205
+ EXAMPLE_LOWLEVEL_TASK: buildExampleTask('lowlevel', techStack),
206
+ EXAMPLE_FEATURE_TASK: buildExampleTask('feature', techStack),
207
+
208
+ // Search patterns (language-aware)
209
+ CONFIG_SEARCH_PATTERN: searchPatterns.config,
210
+ URL_SEARCH_PATTERN: searchPatterns.url,
211
+
212
+ // External integrations (from dependencies)
213
+ EXTERNAL_INTEGRATIONS_LIST: externalIntegrations,
214
+
215
+ // Architecture (from analysis)
216
+ ARCHITECTURE_DIAGRAM: architectureDiagram,
217
+
218
+ // URLs
219
+ CRITICAL_URLS: `- Production: https://${projectSlug}.example.com`,
220
+ BUSINESS_CONSTANTS: '- TBD (document key business constants)',
221
+
222
+ // Gotchas
223
+ GOTCHA_CATEGORY_1: 'Database',
224
+ GOTCHA_1_ITEMS: '- TBD (document database gotchas)',
225
+ GOTCHA_CATEGORY_2: 'API',
226
+ GOTCHA_2_ITEMS: '- TBD (document API gotchas)',
227
+
228
+ // Production
229
+ PRODUCTION_PLATFORM: deploymentPlatform,
230
+ PRODUCTION_SERVICES: buildProductionServices(techStack),
231
+ MONITORING_COMMANDS: 'Check logs, health endpoints',
232
+
233
+ // Constraints
234
+ MIGRATION_CONSTRAINTS: 'Always backup before migrations',
235
+ TESTING_CONSTRAINTS: 'Run tests before merging',
236
+ SECURITY_CONSTRAINTS: 'Never commit secrets',
237
+ CONTACT_INFO: 'TBD (add contact info)',
238
+
239
+ // Languages and frameworks (for templates)
240
+ PRIMARY_LANGUAGE: techStack.languages?.[0] || 'javascript',
241
+ PRIMARY_FRAMEWORK: techStack.frameworks?.[0] || '',
242
+ DATABASE_TYPE: techStack.databases?.[0] || '',
243
+ };
244
+
245
+ // Merge with discovered values - discovered values take precedence
246
+ // This preserves user customizations from existing documentation
247
+ return { ...defaults, ...discoveredValues };
248
+ }
249
+
250
+ /**
251
+ * Detect deployment platform from project files
252
+ */
253
+ function detectDeploymentPlatform(projectRoot) {
254
+ if (!projectRoot) return 'TBD';
255
+
256
+ const platforms = [
257
+ { file: 'vercel.json', name: 'Vercel' },
258
+ { file: 'netlify.toml', name: 'Netlify' },
259
+ { file: 'fly.toml', name: 'Fly.io' },
260
+ { file: 'render.yaml', name: 'Render' },
261
+ { file: 'railway.json', name: 'Railway' },
262
+ { file: 'Dockerfile', name: 'Docker' },
263
+ { file: 'docker-compose.yml', name: 'Docker Compose' },
264
+ { file: 'docker-compose.yaml', name: 'Docker Compose' },
265
+ { file: 'heroku.yml', name: 'Heroku' },
266
+ { file: 'Procfile', name: 'Heroku' },
267
+ { file: 'app.yaml', name: 'Google Cloud' },
268
+ { file: 'serverless.yml', name: 'Serverless' },
269
+ { file: 'terraform', name: 'Terraform' },
270
+ ];
271
+
272
+ for (const platform of platforms) {
273
+ try {
274
+ if (fs.existsSync(path.join(projectRoot, platform.file))) {
275
+ return platform.name;
276
+ }
277
+ } catch {
278
+ // Ignore errors
279
+ }
280
+ }
281
+
282
+ return 'TBD';
283
+ }
284
+
285
+ /**
286
+ * Build path from file purposes
287
+ */
288
+ function buildPathFromPurpose(filePurposes, purpose, defaultPath) {
289
+ const files = filePurposes?.files?.[purpose];
290
+ if (files && files.length > 0) {
291
+ const dir = path.dirname(files[0]);
292
+ return dir === '.' ? defaultPath : dir + '/';
293
+ }
294
+ return defaultPath;
295
+ }
296
+
297
+ /**
298
+ * Build external integrations list from dependencies
299
+ */
300
+ function buildExternalIntegrations(databases, dependencies) {
301
+ const integrations = [];
302
+
303
+ // Add databases
304
+ for (const db of databases) {
305
+ integrations.push(`- **${capitalize(db)}**: Database`);
306
+ }
307
+
308
+ // Check dependencies for known integrations
309
+ const knownIntegrations = {
310
+ stripe: 'Payment processing',
311
+ 'aws-sdk': 'AWS services',
312
+ '@aws-sdk': 'AWS services',
313
+ firebase: 'Firebase services',
314
+ twilio: 'SMS/Voice',
315
+ sendgrid: 'Email delivery',
316
+ '@sendgrid': 'Email delivery',
317
+ nodemailer: 'Email',
318
+ redis: 'Caching',
319
+ ioredis: 'Caching',
320
+ elasticsearch: 'Search',
321
+ '@elastic': 'Search',
322
+ 'socket.io': 'Real-time',
323
+ pusher: 'Real-time',
324
+ cloudinary: 'Media storage',
325
+ 's3': 'File storage',
326
+ };
327
+
328
+ for (const dep of dependencies) {
329
+ const depName = dep.name || dep;
330
+ for (const [key, desc] of Object.entries(knownIntegrations)) {
331
+ if (depName.toLowerCase().includes(key)) {
332
+ integrations.push(`- **${depName}**: ${desc}`);
333
+ break;
334
+ }
335
+ }
336
+ }
337
+
338
+ if (integrations.length === 0) {
339
+ return '*No external integrations detected. Document manually if present.*';
340
+ }
341
+
342
+ return integrations.slice(0, 10).join('\n');
343
+ }
344
+
345
+ /**
346
+ * Build architecture diagram from analysis
347
+ */
348
+ function buildArchitectureDiagram(architecture, filePurposes) {
349
+ const layers = architecture?.layers || [];
350
+ const purposeCounts = filePurposes?.counts || {};
351
+
352
+ // Build diagram based on discovered layers or file purposes
353
+ if (layers.length > 0) {
354
+ let diagram = '┌─────────────────────────────────────┐\n';
355
+ for (const layer of layers.slice(0, 4)) {
356
+ diagram += `│ [${(layer.name || 'Layer').padEnd(30)}] │\n`;
357
+ }
358
+ diagram += '└─────────────────────────────────────┘';
359
+ return diagram;
360
+ }
361
+
362
+ // Build from file purposes
363
+ const hasControllers = purposeCounts.controller > 0 || purposeCounts.route > 0;
364
+ const hasServices = purposeCounts.service > 0;
365
+ const hasModels = purposeCounts.model > 0;
366
+ const hasRepos = purposeCounts.repository > 0;
367
+
368
+ return `┌─────────────────────────────────────┐
369
+ │ [Application] │
370
+ │ │
371
+ ${hasControllers ? '│ ┌───────────┐ ┌───────────┐ │\n│ │ API │ │ Routes │ │\n│ └───────────┘ └───────────┘ │\n' : ''}${hasServices ? '│ ┌───────────────────────────┐ │\n│ │ Services │ │\n│ └───────────────────────────┘ │\n' : ''}${hasModels || hasRepos ? '│ ┌───────────┐ ┌───────────┐ │\n│ │ Models │ │ Database │ │\n│ └───────────┘ └───────────┘ │\n' : ''}└─────────────────────────────────────┘`;
372
+ }
373
+
374
+ /**
375
+ * Get search patterns based on languages
376
+ */
377
+ function getSearchPatterns(languages) {
378
+ if (languages.includes('python')) {
379
+ return {
380
+ config: 'grep -r "os.environ" --include="*.py"',
381
+ url: 'grep -rE "https?://" --include="*.py" --include="*.json"'
382
+ };
383
+ }
384
+ if (languages.includes('go')) {
385
+ return {
386
+ config: 'grep -r "os.Getenv" --include="*.go"',
387
+ url: 'grep -rE "https?://" --include="*.go" --include="*.json"'
388
+ };
389
+ }
390
+ if (languages.includes('rust')) {
391
+ return {
392
+ config: 'grep -r "env::var" --include="*.rs"',
393
+ url: 'grep -rE "https?://" --include="*.rs" --include="*.toml"'
394
+ };
395
+ }
396
+ // Default: JavaScript/TypeScript
397
+ return {
398
+ config: 'grep -r "process.env" --include="*.js" --include="*.ts"',
399
+ url: 'grep -rE "https?://" --include="*.js" --include="*.ts" --include="*.json"'
400
+ };
401
+ }
402
+
403
+ /**
404
+ * Build example task based on tech stack
405
+ */
406
+ function buildExampleTask(type, techStack) {
407
+ const framework = techStack.frameworks?.[0] || '';
408
+ const language = techStack.languages?.[0] || 'javascript';
409
+
410
+ const examples = {
411
+ refactor: {
412
+ express: 'Refactor the authentication middleware',
413
+ fastapi: 'Refactor the dependency injection system',
414
+ django: 'Refactor the view decorators',
415
+ nextjs: 'Refactor the API routes to app router',
416
+ default: 'Refactor the authentication flow'
417
+ },
418
+ lowlevel: {
419
+ express: 'Fix hardcoded timeout in request handler',
420
+ fastapi: 'Fix hardcoded API URL in config',
421
+ django: 'Fix hardcoded secret key in settings',
422
+ default: 'Fix hardcoded API URL in config'
423
+ },
424
+ feature: {
425
+ express: 'Add rate limiting middleware',
426
+ fastapi: 'Add WebSocket support for real-time updates',
427
+ django: 'Add user notifications feature',
428
+ nextjs: 'Add server-side caching',
429
+ default: 'Add user notifications feature'
430
+ }
431
+ };
432
+
433
+ return examples[type][framework] || examples[type].default;
434
+ }
435
+
436
+ /**
437
+ * Build production services list
438
+ */
439
+ function buildProductionServices(techStack) {
440
+ const services = ['Web'];
441
+
442
+ if (techStack.frameworks?.some(f => ['express', 'fastapi', 'django', 'rails'].includes(f))) {
443
+ services.push('API');
444
+ }
445
+
446
+ if (techStack.databases?.length > 0) {
447
+ services.push('Database');
448
+ }
449
+
450
+ if (techStack.databases?.includes('redis')) {
451
+ services.push('Cache');
452
+ }
453
+
454
+ return services.join(', ');
455
+ }
456
+
457
+ /**
458
+ * Capitalize first letter
459
+ */
460
+ function capitalize(str) {
461
+ return str.charAt(0).toUpperCase() + str.slice(1);
462
+ }
463
+
464
+ /**
465
+ * Replace placeholders in all files in a directory
466
+ */
467
+ /**
468
+ * Replace all placeholders in all files
469
+ * @param {string} targetDir - Target directory
470
+ * @param {object} config - Configuration options
471
+ * @param {boolean} config.failOnUnreplaced - Throw if placeholders remain
472
+ * @param {boolean} config.verbose - Log warnings for unreplaced placeholders
473
+ * @returns {object} Results { totalReplaced: number, unreplaced: Array, unreplacedCount: number }
474
+ */
475
+ async function replacePlaceholders(targetDir, config = {}) {
476
+ const { failOnUnreplaced = false, verbose = false } = config;
477
+ const contextDir = path.join(targetDir, AI_CONTEXT_DIR);
478
+ const values = getDefaultValues(config, config.techStack || {}, config.analysis || {});
479
+
480
+ // Find all markdown and JSON files
481
+ const files = await glob('**/*.{md,json}', {
482
+ cwd: contextDir,
483
+ ignore: ['node_modules/**', '.git/**'],
484
+ nodir: true,
485
+ absolute: true
486
+ });
487
+
488
+ let totalReplaced = 0;
489
+ const unreplacedDetails = [];
490
+
491
+ for (const filePath of files) {
492
+ try {
493
+ let content = fs.readFileSync(filePath, 'utf8');
494
+ const originalContent = content;
495
+
496
+ // Replace all placeholders
497
+ for (const [key, value] of Object.entries(values)) {
498
+ const pattern = new RegExp(`\\{\\{${key}\\}\\}`, 'g');
499
+ content = content.replace(pattern, String(value));
500
+ }
501
+
502
+ if (content !== originalContent) {
503
+ fs.writeFileSync(filePath, content, 'utf8');
504
+ totalReplaced++;
505
+ }
506
+
507
+ // Check for remaining placeholders
508
+ const remaining = findPlaceholdersInContent(content, filePath);
509
+ unreplacedDetails.push(...remaining);
510
+ } catch (error) {
511
+ // Skip files that can't be read
512
+ }
513
+ }
514
+
515
+ // Also replace in AI_CONTEXT.md at root
516
+ const aiContextPath = path.join(targetDir, AI_CONTEXT_FILE);
517
+ if (fs.existsSync(aiContextPath)) {
518
+ try {
519
+ let content = fs.readFileSync(aiContextPath, 'utf8');
520
+ const originalContent = content;
521
+
522
+ for (const [key, value] of Object.entries(values)) {
523
+ const pattern = new RegExp(`\\{\\{${key}\\}\\}`, 'g');
524
+ content = content.replace(pattern, String(value));
525
+ }
526
+
527
+ if (content !== originalContent) {
528
+ fs.writeFileSync(aiContextPath, content, 'utf8');
529
+ totalReplaced++;
530
+ }
531
+
532
+ // Check for remaining placeholders
533
+ const remaining = findPlaceholdersInContent(content, aiContextPath);
534
+ unreplacedDetails.push(...remaining);
535
+ } catch (error) {
536
+ // Skip if can't read
537
+ }
538
+ }
539
+
540
+ // Deduplicate unreplaced items
541
+ const uniqueUnreplaced = deduplicateUnreplaced(unreplacedDetails);
542
+ const unreplacedCount = uniqueUnreplaced.length;
543
+
544
+ // Handle unreplaced placeholders
545
+ if (unreplacedCount > 0) {
546
+ const placeholders = uniqueUnreplaced.map(u => u.placeholder).join(', ');
547
+ const message = `${unreplacedCount} placeholder${unreplacedCount > 1 ? 's' : ''} not replaced: ${placeholders}`;
548
+
549
+ if (verbose) {
550
+ console.warn(chalk.yellow(`⚠ ${message}`));
551
+ uniqueUnreplaced.forEach(u => {
552
+ console.warn(chalk.gray(` - ${u.placeholder} in ${u.file}`));
553
+ });
554
+ }
555
+
556
+ if (failOnUnreplaced) {
557
+ throw new Error(message);
558
+ }
559
+ }
560
+
561
+ return { totalReplaced, unreplaced: uniqueUnreplaced, unreplacedCount };
562
+ }
563
+
564
+ /**
565
+ * Find placeholders in content (helper for replacePlaceholders)
566
+ * @param {string} content - File content
567
+ * @param {string} filePath - File path
568
+ * @returns {Array} List of unreplaced placeholder info
569
+ */
570
+ function findPlaceholdersInContent(content, filePath) {
571
+ const placeholderPattern = /\{\{([A-Z_]+)\}\}/g;
572
+ const found = [];
573
+ let match;
574
+
575
+ while ((match = placeholderPattern.exec(content)) !== null) {
576
+ found.push({
577
+ placeholder: match[0],
578
+ name: match[1],
579
+ file: path.relative(process.cwd(), filePath),
580
+ index: match.index,
581
+ known: KNOWN_PLACEHOLDERS.hasOwnProperty(match[1])
582
+ });
583
+ }
584
+
585
+ return found;
586
+ }
587
+
588
+ /**
589
+ * Deduplicate unreplaced placeholder list
590
+ * @param {Array} unreplaced - List of unreplaced items
591
+ * @returns {Array} Deduplicated list
592
+ */
593
+ function deduplicateUnreplaced(unreplaced) {
594
+ const seen = new Set();
595
+ return unreplaced.filter(item => {
596
+ const key = `${item.placeholder}:${item.file}`;
597
+ if (seen.has(key)) {
598
+ return false;
599
+ }
600
+ seen.add(key);
601
+ return true;
602
+ });
603
+ }
604
+
605
+ /**
606
+ * Find all placeholders in a file
607
+ */
608
+ function findPlaceholders(filePath) {
609
+ const content = fs.readFileSync(filePath, 'utf8');
610
+ const placeholderPattern = /\{\{([A-Z_]+)\}\}/g;
611
+
612
+ const found = [];
613
+ let match;
614
+
615
+ while ((match = placeholderPattern.exec(content)) !== null) {
616
+ found.push({
617
+ placeholder: match[0],
618
+ name: match[1],
619
+ index: match.index,
620
+ known: KNOWN_PLACEHOLDERS.hasOwnProperty(match[1]),
621
+ });
622
+ }
623
+
624
+ return found;
625
+ }
626
+
627
+ module.exports = {
628
+ replacePlaceholders,
629
+ findPlaceholders,
630
+ getDefaultValues,
631
+ KNOWN_PLACEHOLDERS,
632
+ };