@iservu-inc/adf-cli 0.4.35 → 0.4.36
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/CHANGELOG.md +61 -0
- package/lib/commands/config.js +127 -4
- package/lib/generators/cursor-generator.js +77 -6
- package/lib/generators/vscode-generator.js +75 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,67 @@ All notable changes to `@iservu-inc/adf-cli` will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.4.36] - 2025-10-04
|
|
9
|
+
|
|
10
|
+
### ✨ Multi-IDE Improvements + Config Command Enhancement
|
|
11
|
+
|
|
12
|
+
**1. Added IDE Deployment to `adf config`**
|
|
13
|
+
- New category: "IDE Deployment"
|
|
14
|
+
- Deploy to multiple IDEs without re-running `adf init`
|
|
15
|
+
- Shows which IDEs are already deployed
|
|
16
|
+
- Pre-selects already deployed IDEs in checkbox
|
|
17
|
+
- Allows quick re-deployment or deployment to new IDEs
|
|
18
|
+
|
|
19
|
+
**New Config Menu:**
|
|
20
|
+
```
|
|
21
|
+
⚙️ ADF Configuration
|
|
22
|
+
|
|
23
|
+
? Select configuration category:
|
|
24
|
+
> AI Provider Setup - ✓ Configured (Openai)
|
|
25
|
+
IDE Deployment - ✓ Deployed (Windsurf, Cursor)
|
|
26
|
+
Learning System - ✓ Active (5 sessions, 12 patterns)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Usage:**
|
|
30
|
+
```bash
|
|
31
|
+
adf config
|
|
32
|
+
# Select "IDE Deployment"
|
|
33
|
+
# Choose IDEs to deploy to
|
|
34
|
+
# Done!
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**2. Improved Cursor Generator**
|
|
38
|
+
- Added `loadSessionAnswers()` - reads actual session data
|
|
39
|
+
- Added `extractTechStackFromAnswers()` - detects tech in answers
|
|
40
|
+
- Added `extractWhatBuildingFromAnswers()` - finds project goals
|
|
41
|
+
- Falls back to session answers when PRP sections empty
|
|
42
|
+
- Better content instead of "See PRP for details"
|
|
43
|
+
|
|
44
|
+
**3. Improved VSCode/Copilot Generator**
|
|
45
|
+
- Same answer extraction improvements as Cursor
|
|
46
|
+
- Better Copilot instructions with real content
|
|
47
|
+
- Falls back to session answers
|
|
48
|
+
- Improved tech stack detection
|
|
49
|
+
|
|
50
|
+
**Code Changes:**
|
|
51
|
+
- config.js:18-27 - Added IDE_DEPLOYMENT category
|
|
52
|
+
- config.js:221-331 - IDE deployment configuration logic
|
|
53
|
+
- cursor-generator.js:41-51 - Made generateRules async
|
|
54
|
+
- cursor-generator.js:53-129 - Enhanced generateRulesPRP with answer extraction
|
|
55
|
+
- cursor-generator.js:373-424 - Added helper methods
|
|
56
|
+
- vscode-generator.js:40-50 - Made generateCopilotInstructions async
|
|
57
|
+
- vscode-generator.js:52-124 - Enhanced generateCopilotPRP with answer extraction
|
|
58
|
+
- vscode-generator.js:402-451 - Added helper methods
|
|
59
|
+
|
|
60
|
+
**Impact:**
|
|
61
|
+
- ✅ `adf config` now lets you deploy to IDEs
|
|
62
|
+
- ✅ Cursor files now have real content
|
|
63
|
+
- ✅ VSCode Copilot instructions have real content
|
|
64
|
+
- ✅ All generators use session answers as fallback
|
|
65
|
+
- ✅ No more "See PRP for details" placeholders
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
8
69
|
## [0.4.35] - 2025-10-04
|
|
9
70
|
|
|
10
71
|
### 🔧 MAJOR FIX: Windsurf Configuration - Activation Modes & Content Generation
|
package/lib/commands/config.js
CHANGED
|
@@ -15,14 +15,16 @@ const CONFIG_CATEGORIES = {
|
|
|
15
15
|
description: 'Configure AI provider (Anthropic, OpenAI, Google Gemini, OpenRouter)',
|
|
16
16
|
value: 'ai-provider'
|
|
17
17
|
},
|
|
18
|
+
IDE_DEPLOYMENT: {
|
|
19
|
+
name: 'IDE Deployment',
|
|
20
|
+
description: 'Deploy requirements to IDEs (Windsurf, Cursor, VSCode, etc.)',
|
|
21
|
+
value: 'ide-deployment'
|
|
22
|
+
},
|
|
18
23
|
LEARNING_SYSTEM: {
|
|
19
24
|
name: 'Learning System',
|
|
20
25
|
description: 'Manage interview learning data and preferences',
|
|
21
26
|
value: 'learning'
|
|
22
27
|
}
|
|
23
|
-
// Future config categories can be added here:
|
|
24
|
-
// PROJECT_SETTINGS: { name: 'Project Settings', description: '...', value: 'project' },
|
|
25
|
-
// DEPLOYMENT: { name: 'Deployment Preferences', description: '...', value: 'deployment' },
|
|
26
28
|
};
|
|
27
29
|
|
|
28
30
|
/**
|
|
@@ -118,6 +120,7 @@ async function config() {
|
|
|
118
120
|
|
|
119
121
|
// Check configuration status for all categories
|
|
120
122
|
const aiStatus = await isAIConfigured(cwd);
|
|
123
|
+
const deploymentStatus = await getDeploymentStatus(cwd);
|
|
121
124
|
const learningStatus = await getLearningStatus(cwd);
|
|
122
125
|
|
|
123
126
|
// Build choices with status indicators
|
|
@@ -127,6 +130,11 @@ async function config() {
|
|
|
127
130
|
value: CONFIG_CATEGORIES.AI_PROVIDER.value,
|
|
128
131
|
short: CONFIG_CATEGORIES.AI_PROVIDER.name
|
|
129
132
|
},
|
|
133
|
+
{
|
|
134
|
+
name: `${CONFIG_CATEGORIES.IDE_DEPLOYMENT.name} - ${displayDeploymentStatus(deploymentStatus)}`,
|
|
135
|
+
value: CONFIG_CATEGORIES.IDE_DEPLOYMENT.value,
|
|
136
|
+
short: CONFIG_CATEGORIES.IDE_DEPLOYMENT.name
|
|
137
|
+
},
|
|
130
138
|
{
|
|
131
139
|
name: `${CONFIG_CATEGORIES.LEARNING_SYSTEM.name} - ${displayLearningStatus(learningStatus)}`,
|
|
132
140
|
value: CONFIG_CATEGORIES.LEARNING_SYSTEM.value,
|
|
@@ -160,11 +168,14 @@ async function config() {
|
|
|
160
168
|
await configureAIProviderCategory(cwd, aiStatus);
|
|
161
169
|
break;
|
|
162
170
|
|
|
171
|
+
case 'ide-deployment':
|
|
172
|
+
await configureIDEDeploymentCategory(cwd, deploymentStatus);
|
|
173
|
+
break;
|
|
174
|
+
|
|
163
175
|
case 'learning':
|
|
164
176
|
await configureLearningCategory(cwd, learningStatus);
|
|
165
177
|
break;
|
|
166
178
|
|
|
167
|
-
// Future categories will be handled here
|
|
168
179
|
default:
|
|
169
180
|
console.log(chalk.red('\n❌ Configuration category not implemented yet.\n'));
|
|
170
181
|
}
|
|
@@ -207,6 +218,118 @@ async function configureAIProviderCategory(cwd, aiStatus) {
|
|
|
207
218
|
}
|
|
208
219
|
}
|
|
209
220
|
|
|
221
|
+
/**
|
|
222
|
+
* Check deployment status
|
|
223
|
+
*/
|
|
224
|
+
async function getDeploymentStatus(projectPath = process.cwd()) {
|
|
225
|
+
const adfDir = path.join(projectPath, '.adf');
|
|
226
|
+
|
|
227
|
+
if (!await fs.pathExists(adfDir)) {
|
|
228
|
+
return { hasSession: false, tools: [] };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Check for latest session
|
|
232
|
+
const sessionsDir = path.join(adfDir, 'sessions');
|
|
233
|
+
if (!await fs.pathExists(sessionsDir)) {
|
|
234
|
+
return { hasSession: false, tools: [] };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const sessions = await fs.readdir(sessionsDir);
|
|
238
|
+
if (sessions.length === 0) {
|
|
239
|
+
return { hasSession: false, tools: [] };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Check which IDE tools have been deployed
|
|
243
|
+
const deployedTools = [];
|
|
244
|
+
const ideMarkers = [
|
|
245
|
+
{ name: 'Windsurf', path: '.windsurf/rules' },
|
|
246
|
+
{ name: 'Cursor', path: '.cursor/rules' },
|
|
247
|
+
{ name: 'VSCode', path: '.github/copilot-instructions.md' },
|
|
248
|
+
{ name: 'Claude Code', path: '.claude/commands' }
|
|
249
|
+
];
|
|
250
|
+
|
|
251
|
+
for (const marker of ideMarkers) {
|
|
252
|
+
if (await fs.pathExists(path.join(projectPath, marker.path))) {
|
|
253
|
+
deployedTools.push(marker.name);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return {
|
|
258
|
+
hasSession: true,
|
|
259
|
+
sessionCount: sessions.length,
|
|
260
|
+
tools: deployedTools
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Display deployment status
|
|
266
|
+
*/
|
|
267
|
+
function displayDeploymentStatus(status) {
|
|
268
|
+
if (status.hasSession && status.tools.length > 0) {
|
|
269
|
+
return `${chalk.green('✓ Deployed')} ${chalk.gray(`(${status.tools.join(', ')})`)}`;
|
|
270
|
+
} else if (status.hasSession) {
|
|
271
|
+
return chalk.yellow('○ Session exists, not deployed');
|
|
272
|
+
} else {
|
|
273
|
+
return chalk.gray('○ No sessions yet');
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Configure IDE Deployment category
|
|
279
|
+
*/
|
|
280
|
+
async function configureIDEDeploymentCategory(cwd, deploymentStatus) {
|
|
281
|
+
const { deployToTool } = require('./deploy');
|
|
282
|
+
|
|
283
|
+
console.log(chalk.gray('\n' + '─'.repeat(60) + '\n'));
|
|
284
|
+
|
|
285
|
+
if (!deploymentStatus.hasSession) {
|
|
286
|
+
console.log(chalk.yellow('⚠️ No ADF sessions found.\n'));
|
|
287
|
+
console.log(chalk.gray('You need to run `adf init` first to create requirements.'));
|
|
288
|
+
console.log(chalk.gray('Then you can deploy them to your IDE.\n'));
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
console.log(chalk.cyan(`Found ${deploymentStatus.sessionCount} session(s)\n`));
|
|
293
|
+
|
|
294
|
+
if (deploymentStatus.tools.length > 0) {
|
|
295
|
+
console.log(chalk.green('✓ Already deployed to:'));
|
|
296
|
+
deploymentStatus.tools.forEach(tool => {
|
|
297
|
+
console.log(chalk.gray(` • ${tool}`));
|
|
298
|
+
});
|
|
299
|
+
console.log('');
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Ask which tools to deploy to
|
|
303
|
+
const { tools } = await inquirer.prompt([
|
|
304
|
+
{
|
|
305
|
+
type: 'checkbox',
|
|
306
|
+
name: 'tools',
|
|
307
|
+
message: 'Select IDEs to deploy to (space to select, enter to confirm):',
|
|
308
|
+
choices: [
|
|
309
|
+
{ name: 'Windsurf', value: 'windsurf', checked: deploymentStatus.tools.includes('Windsurf') },
|
|
310
|
+
{ name: 'Cursor', value: 'cursor', checked: deploymentStatus.tools.includes('Cursor') },
|
|
311
|
+
{ name: 'VSCode/Copilot', value: 'vscode', checked: deploymentStatus.tools.includes('VSCode') },
|
|
312
|
+
{ name: 'Claude Code', value: 'claude-code', checked: deploymentStatus.tools.includes('Claude Code') },
|
|
313
|
+
{ name: 'Gemini CLI', value: 'gemini-cli' }
|
|
314
|
+
],
|
|
315
|
+
validate: (answer) => {
|
|
316
|
+
if (answer.length === 0) {
|
|
317
|
+
return 'You must choose at least one IDE.';
|
|
318
|
+
}
|
|
319
|
+
return true;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
]);
|
|
323
|
+
|
|
324
|
+
// Deploy to each selected tool
|
|
325
|
+
for (const tool of tools) {
|
|
326
|
+
console.log('');
|
|
327
|
+
await deployToTool(tool, { silent: false });
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
console.log(chalk.green.bold('\n✅ Deployment complete!\n'));
|
|
331
|
+
}
|
|
332
|
+
|
|
210
333
|
/**
|
|
211
334
|
* Configure Learning System category
|
|
212
335
|
*/
|
|
@@ -42,7 +42,7 @@ class CursorGenerator extends ToolConfigGenerator {
|
|
|
42
42
|
await this.ensureDir('.cursor');
|
|
43
43
|
|
|
44
44
|
const content = this.framework === 'rapid' ?
|
|
45
|
-
this.generateRulesPRP() :
|
|
45
|
+
await this.generateRulesPRP() :
|
|
46
46
|
this.framework === 'balanced' ?
|
|
47
47
|
this.generateRulesBalanced() :
|
|
48
48
|
this.generateRulesBMAD();
|
|
@@ -50,9 +50,27 @@ class CursorGenerator extends ToolConfigGenerator {
|
|
|
50
50
|
return await this.writeToProject('.cursor/rules', content);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
generateRulesPRP() {
|
|
53
|
+
async generateRulesPRP() {
|
|
54
54
|
const projectName = this.getProjectName();
|
|
55
55
|
const sections = this.outputs.sections || {};
|
|
56
|
+
const answers = await this.loadSessionAnswers();
|
|
57
|
+
|
|
58
|
+
// Extract content with fallback to answers
|
|
59
|
+
let goal = sections['1._goal_definition'] || sections['goal_definition'];
|
|
60
|
+
let techStack = this.extractTechStack(sections);
|
|
61
|
+
let blueprint = sections['4._implementation_blueprint'] || sections['implementation_blueprint'];
|
|
62
|
+
let validation = sections['5._validation'] || sections['validation'];
|
|
63
|
+
|
|
64
|
+
// Fallback to extracted answers if sections are empty
|
|
65
|
+
if (!goal || goal.length < 20) {
|
|
66
|
+
const whatBuilding = this.extractWhatBuildingFromAnswers(answers);
|
|
67
|
+
if (whatBuilding) goal = whatBuilding;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (!techStack || techStack.includes('See framework')) {
|
|
71
|
+
const techFromAnswers = this.extractTechStackFromAnswers(answers);
|
|
72
|
+
if (techFromAnswers) techStack = techFromAnswers;
|
|
73
|
+
}
|
|
56
74
|
|
|
57
75
|
return `# ${projectName} - Cursor Rules
|
|
58
76
|
|
|
@@ -60,19 +78,19 @@ You are a senior developer working on ${projectName}.
|
|
|
60
78
|
|
|
61
79
|
## Project Goal
|
|
62
80
|
|
|
63
|
-
${
|
|
81
|
+
${goal || 'See PRP for complete details'}
|
|
64
82
|
|
|
65
83
|
## Tech Stack
|
|
66
84
|
|
|
67
|
-
${
|
|
85
|
+
${techStack || 'See PRP for tech stack details'}
|
|
68
86
|
|
|
69
87
|
## Implementation Blueprint
|
|
70
88
|
|
|
71
|
-
${
|
|
89
|
+
${blueprint || 'See PRP for implementation details'}
|
|
72
90
|
|
|
73
91
|
## Success Criteria
|
|
74
92
|
|
|
75
|
-
${
|
|
93
|
+
${validation || 'See PRP for validation criteria'}
|
|
76
94
|
|
|
77
95
|
## Before Implementing Features
|
|
78
96
|
|
|
@@ -369,6 +387,59 @@ You can delete this .cursorrules file.
|
|
|
369
387
|
return '0.3.0';
|
|
370
388
|
}
|
|
371
389
|
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Load session answers from _progress.json
|
|
393
|
+
*/
|
|
394
|
+
async loadSessionAnswers() {
|
|
395
|
+
const fs = require('fs-extra');
|
|
396
|
+
const path = require('path');
|
|
397
|
+
const progressPath = path.join(this.sessionPath, '_progress.json');
|
|
398
|
+
|
|
399
|
+
try {
|
|
400
|
+
if (await fs.pathExists(progressPath)) {
|
|
401
|
+
const progress = await fs.readJson(progressPath);
|
|
402
|
+
return progress.answers || {};
|
|
403
|
+
}
|
|
404
|
+
} catch (error) {
|
|
405
|
+
// Fall back to empty if can't load
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return {};
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Extract tech stack from any answer
|
|
413
|
+
*/
|
|
414
|
+
extractTechStackFromAnswers(answers) {
|
|
415
|
+
for (const [questionId, answer] of Object.entries(answers)) {
|
|
416
|
+
if (typeof answer === 'string') {
|
|
417
|
+
const lower = answer.toLowerCase();
|
|
418
|
+
if (lower.includes('react') || lower.includes('vue') || lower.includes('angular') ||
|
|
419
|
+
lower.includes('node') || lower.includes('python') || lower.includes('next') ||
|
|
420
|
+
lower.includes('postgres') || lower.includes('mongo') || lower.includes('mysql')) {
|
|
421
|
+
return answer;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
return null;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Extract "what are you building" from any answer
|
|
430
|
+
*/
|
|
431
|
+
extractWhatBuildingFromAnswers(answers) {
|
|
432
|
+
for (const [questionId, answer] of Object.entries(answers)) {
|
|
433
|
+
if (questionId.toLowerCase().includes('goal') ||
|
|
434
|
+
questionId.toLowerCase().includes('building') ||
|
|
435
|
+
questionId.toLowerCase().includes('project')) {
|
|
436
|
+
if (typeof answer === 'string' && answer.length > 20) {
|
|
437
|
+
return answer;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return null;
|
|
442
|
+
}
|
|
372
443
|
}
|
|
373
444
|
|
|
374
445
|
module.exports = CursorGenerator;
|
|
@@ -41,7 +41,7 @@ class VSCodeGenerator extends ToolConfigGenerator {
|
|
|
41
41
|
await this.ensureDir('.github');
|
|
42
42
|
|
|
43
43
|
const content = this.framework === 'rapid' ?
|
|
44
|
-
this.generateCopilotPRP() :
|
|
44
|
+
await this.generateCopilotPRP() :
|
|
45
45
|
this.framework === 'balanced' ?
|
|
46
46
|
this.generateCopilotBalanced() :
|
|
47
47
|
this.generateCopilotBMAD();
|
|
@@ -49,23 +49,41 @@ class VSCodeGenerator extends ToolConfigGenerator {
|
|
|
49
49
|
return await this.writeToProject('.github/copilot-instructions.md', content);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
generateCopilotPRP() {
|
|
52
|
+
async generateCopilotPRP() {
|
|
53
53
|
const projectName = this.getProjectName();
|
|
54
54
|
const sections = this.outputs.sections || {};
|
|
55
|
+
const answers = await this.loadSessionAnswers();
|
|
56
|
+
|
|
57
|
+
// Extract content with fallback to answers
|
|
58
|
+
let goal = sections['1._goal_definition'] || sections['goal_definition'];
|
|
59
|
+
let techStack = this.extractTechStack(sections);
|
|
60
|
+
let blueprint = sections['4._implementation_blueprint'] || sections['implementation_blueprint'];
|
|
61
|
+
let validation = sections['5._validation'] || sections['validation'];
|
|
62
|
+
|
|
63
|
+
// Fallback to extracted answers if sections are empty
|
|
64
|
+
if (!goal || goal.length < 20) {
|
|
65
|
+
const whatBuilding = this.extractWhatBuildingFromAnswers(answers);
|
|
66
|
+
if (whatBuilding) goal = whatBuilding;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (!techStack || techStack.includes('See framework')) {
|
|
70
|
+
const techFromAnswers = this.extractTechStackFromAnswers(answers);
|
|
71
|
+
if (techFromAnswers) techStack = techFromAnswers;
|
|
72
|
+
}
|
|
55
73
|
|
|
56
74
|
return `# Copilot Instructions for ${projectName}
|
|
57
75
|
|
|
58
76
|
## Project Overview
|
|
59
77
|
|
|
60
|
-
${
|
|
78
|
+
${goal || 'Software development project'}
|
|
61
79
|
|
|
62
80
|
## Tech Stack
|
|
63
81
|
|
|
64
|
-
${
|
|
82
|
+
${techStack || 'See PRP for tech stack'}
|
|
65
83
|
|
|
66
84
|
## Implementation Blueprint
|
|
67
85
|
|
|
68
|
-
${
|
|
86
|
+
${blueprint || 'See PRP for implementation details'}
|
|
69
87
|
|
|
70
88
|
## Code Style
|
|
71
89
|
|
|
@@ -91,7 +109,7 @@ ${sections['4._implementation_blueprint'] || sections['implementation_blueprint'
|
|
|
91
109
|
|
|
92
110
|
## Success Criteria
|
|
93
111
|
|
|
94
|
-
${
|
|
112
|
+
${validation || 'See PRP for validation criteria'}
|
|
95
113
|
|
|
96
114
|
Ensure all code meets these criteria before considering it complete.
|
|
97
115
|
|
|
@@ -398,6 +416,57 @@ ${this.extractSection(prd, 'Performance') || '- Optimize critical paths\n- Monit
|
|
|
398
416
|
return '0.3.0';
|
|
399
417
|
}
|
|
400
418
|
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Load session answers from _progress.json
|
|
422
|
+
*/
|
|
423
|
+
async loadSessionAnswers() {
|
|
424
|
+
const progressPath = path.join(this.sessionPath, '_progress.json');
|
|
425
|
+
|
|
426
|
+
try {
|
|
427
|
+
if (await fs.pathExists(progressPath)) {
|
|
428
|
+
const progress = await fs.readJson(progressPath);
|
|
429
|
+
return progress.answers || {};
|
|
430
|
+
}
|
|
431
|
+
} catch (error) {
|
|
432
|
+
// Fall back to empty if can't load
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
return {};
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Extract tech stack from any answer
|
|
440
|
+
*/
|
|
441
|
+
extractTechStackFromAnswers(answers) {
|
|
442
|
+
for (const [questionId, answer] of Object.entries(answers)) {
|
|
443
|
+
if (typeof answer === 'string') {
|
|
444
|
+
const lower = answer.toLowerCase();
|
|
445
|
+
if (lower.includes('react') || lower.includes('vue') || lower.includes('angular') ||
|
|
446
|
+
lower.includes('node') || lower.includes('python') || lower.includes('next') ||
|
|
447
|
+
lower.includes('postgres') || lower.includes('mongo') || lower.includes('mysql')) {
|
|
448
|
+
return answer;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
return null;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Extract "what are you building" from any answer
|
|
457
|
+
*/
|
|
458
|
+
extractWhatBuildingFromAnswers(answers) {
|
|
459
|
+
for (const [questionId, answer] of Object.entries(answers)) {
|
|
460
|
+
if (questionId.toLowerCase().includes('goal') ||
|
|
461
|
+
questionId.toLowerCase().includes('building') ||
|
|
462
|
+
questionId.toLowerCase().includes('project')) {
|
|
463
|
+
if (typeof answer === 'string' && answer.length > 20) {
|
|
464
|
+
return answer;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
return null;
|
|
469
|
+
}
|
|
401
470
|
}
|
|
402
471
|
|
|
403
472
|
module.exports = VSCodeGenerator;
|