@iservu-inc/adf-cli 0.4.34 → 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 +149 -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/lib/generators/windsurf-generator.js +225 -15
- package/package.json +1 -1
- package/screenshots/windsurf-rules-agents-do-not-work.jpg +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,155 @@ 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
|
+
|
|
69
|
+
## [0.4.35] - 2025-10-04
|
|
70
|
+
|
|
71
|
+
### 🔧 MAJOR FIX: Windsurf Configuration - Activation Modes & Content Generation
|
|
72
|
+
|
|
73
|
+
**Fixed: Empty/Useless Windsurf Files**
|
|
74
|
+
- **Problem:** Generated Windsurf files had wrong activation modes and empty content
|
|
75
|
+
- **Issues:**
|
|
76
|
+
1. Rules had `Manual` activation (useless) instead of `Always On`
|
|
77
|
+
2. Workflows had no activation mode frontmatter
|
|
78
|
+
3. Content showed "Not provided" instead of actual answers
|
|
79
|
+
4. Not reading session data to extract meaningful information
|
|
80
|
+
|
|
81
|
+
**Solution:**
|
|
82
|
+
1. **Added Proper Frontmatter with Activation Modes:**
|
|
83
|
+
- Rules: `trigger: always_on` (default for most rules)
|
|
84
|
+
- Workflows: `trigger: manual` with descriptions
|
|
85
|
+
- Follows Windsurf best practices
|
|
86
|
+
|
|
87
|
+
2. **Reads Actual Session Answers:**
|
|
88
|
+
- New `loadSessionAnswers()` loads `_progress.json`
|
|
89
|
+
- Extracts real answers from interview session
|
|
90
|
+
- Falls back gracefully if data unavailable
|
|
91
|
+
|
|
92
|
+
3. **Intelligent Content Parsing:**
|
|
93
|
+
- New `parseImplementationBlueprint()` extracts structured sections
|
|
94
|
+
- New `extractSubsection()` finds content by keywords
|
|
95
|
+
- New `extractTechStackFromAnswers()` finds tech mentions
|
|
96
|
+
- New `extractWhatBuildingFromAnswers()` finds project goals
|
|
97
|
+
|
|
98
|
+
**Before v0.4.35 (Broken):**
|
|
99
|
+
```markdown
|
|
100
|
+
# Architecture - adf-test-project
|
|
101
|
+
|
|
102
|
+
## Implementation Blueprint
|
|
103
|
+
Not provided
|
|
104
|
+
|
|
105
|
+
## Data Model
|
|
106
|
+
Not provided
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**After v0.4.35 (Fixed):**
|
|
110
|
+
```markdown
|
|
111
|
+
---
|
|
112
|
+
trigger: always_on
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
# Architecture - adf-test-project
|
|
116
|
+
|
|
117
|
+
## Implementation Blueprint
|
|
118
|
+
|
|
119
|
+
### Implementation Steps
|
|
120
|
+
**Project Goal:**
|
|
121
|
+
A performance website with nextjs frontend, postgres database...
|
|
122
|
+
|
|
123
|
+
### Technical Approach
|
|
124
|
+
**Tech Stack:**
|
|
125
|
+
Next.js for frontend and backend, PostgreSQL for database...
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Code Changes:**
|
|
129
|
+
- windsurf-generator.js:217-289 - Added frontmatter to project-context rule
|
|
130
|
+
- windsurf-generator.js:296-359 - Added frontmatter & parsing to architecture rule
|
|
131
|
+
- windsurf-generator.js:381-437 - Added frontmatter to coding-standards rule
|
|
132
|
+
- windsurf-generator.js:465-545 - Added frontmatter & descriptions to workflows
|
|
133
|
+
- windsurf-generator.js:633-754 - NEW helper methods for answer parsing
|
|
134
|
+
|
|
135
|
+
**New Helper Methods:**
|
|
136
|
+
- `loadSessionAnswers()` - Reads _progress.json
|
|
137
|
+
- `parseImplementationBlueprint()` - Parses blueprint into sections
|
|
138
|
+
- `extractSubsection()` - Finds content by keyword matching
|
|
139
|
+
- `extractTechStackFromAnswers()` - Detects tech stack in answers
|
|
140
|
+
- `extractWhatBuildingFromAnswers()` - Finds project goals
|
|
141
|
+
|
|
142
|
+
**Activation Modes Guide:**
|
|
143
|
+
- **Always On:** Default for rules - always active
|
|
144
|
+
- **Glob:** For file/pattern matching (not used yet)
|
|
145
|
+
- **Manual:** For workflows - user must activate
|
|
146
|
+
- **Model Decide:** AI chooses when to activate (requires description)
|
|
147
|
+
|
|
148
|
+
**Impact:**
|
|
149
|
+
- ✅ Windsurf rules now work properly with `Always On` activation
|
|
150
|
+
- ✅ Files contain actual project information, not placeholders
|
|
151
|
+
- ✅ Workflows have proper frontmatter with descriptions
|
|
152
|
+
- ✅ Better content extraction from session answers
|
|
153
|
+
- ✅ Fallback to PRP/specification if answers unavailable
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
8
157
|
## [0.4.34] - 2025-10-04
|
|
9
158
|
|
|
10
159
|
### ✨ Feature: Multi-Tool Deployment Selection
|
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;
|
|
@@ -214,7 +214,11 @@ ${this.extractSection(this.outputs.architecture || '', 'System Overview') || 'Se
|
|
|
214
214
|
let content;
|
|
215
215
|
if (this.framework === 'rapid') {
|
|
216
216
|
const sections = this.outputs.sections || {};
|
|
217
|
-
content =
|
|
217
|
+
content = `---
|
|
218
|
+
trigger: always_on
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
# Project Context - ${projectName}
|
|
218
222
|
|
|
219
223
|
## Goal
|
|
220
224
|
|
|
@@ -235,7 +239,11 @@ All requirements are documented in: \`.adf/sessions/${this.getSessionId()}/outpu
|
|
|
235
239
|
Read this file before implementing any features.
|
|
236
240
|
`;
|
|
237
241
|
} else if (this.framework === 'balanced') {
|
|
238
|
-
content =
|
|
242
|
+
content = `---
|
|
243
|
+
trigger: always_on
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
# Project Context - ${projectName}
|
|
239
247
|
|
|
240
248
|
## Overview
|
|
241
249
|
|
|
@@ -257,7 +265,11 @@ Review these files before implementing:
|
|
|
257
265
|
- Technical Plan: \`.adf/sessions/${this.getSessionId()}/outputs/plan.md\`
|
|
258
266
|
`;
|
|
259
267
|
} else {
|
|
260
|
-
content =
|
|
268
|
+
content = `---
|
|
269
|
+
trigger: always_on
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
# Project Context - ${projectName}
|
|
261
273
|
|
|
262
274
|
## Product Overview
|
|
263
275
|
|
|
@@ -282,21 +294,52 @@ Review these files before implementing:
|
|
|
282
294
|
async generateArchitectureRule() {
|
|
283
295
|
const projectName = this.getProjectName();
|
|
284
296
|
|
|
297
|
+
// Load actual session answers for better content
|
|
298
|
+
const answers = await this.loadSessionAnswers();
|
|
299
|
+
|
|
285
300
|
let content;
|
|
286
301
|
if (this.framework === 'rapid') {
|
|
287
302
|
const sections = this.outputs.sections || {};
|
|
288
|
-
|
|
303
|
+
const blueprint = sections['4._implementation_blueprint'] || sections['implementation_blueprint'];
|
|
304
|
+
|
|
305
|
+
// Parse blueprint into structured sections
|
|
306
|
+
const parsed = this.parseImplementationBlueprint(blueprint || '', answers);
|
|
307
|
+
|
|
308
|
+
content = `---
|
|
309
|
+
trigger: always_on
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
# Architecture - ${projectName}
|
|
289
313
|
|
|
290
314
|
## Implementation Blueprint
|
|
291
315
|
|
|
292
|
-
|
|
316
|
+
### Implementation Steps
|
|
317
|
+
${parsed.steps || 'Not provided'}
|
|
318
|
+
|
|
319
|
+
### Data Model
|
|
320
|
+
${parsed.dataModel || 'Not provided'}
|
|
321
|
+
|
|
322
|
+
### API Design
|
|
323
|
+
${parsed.apiDesign || 'Not provided'}
|
|
324
|
+
|
|
325
|
+
### Edge Cases & Error Handling
|
|
326
|
+
${parsed.errorHandling || 'Not provided'}
|
|
327
|
+
|
|
328
|
+
### Technical Approach
|
|
329
|
+
${parsed.technicalApproach || 'Not provided'}
|
|
330
|
+
|
|
331
|
+
---
|
|
293
332
|
|
|
294
333
|
## Reference
|
|
295
334
|
|
|
296
335
|
Full implementation details: \`.adf/sessions/${this.getSessionId()}/outputs/prp.md\`
|
|
297
336
|
`;
|
|
298
337
|
} else if (this.framework === 'balanced') {
|
|
299
|
-
content =
|
|
338
|
+
content = `---
|
|
339
|
+
trigger: always_on
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
# Architecture - ${projectName}
|
|
300
343
|
|
|
301
344
|
${this.extractSection(this.outputs.specification || '', 'Architecture') || 'See specification'}
|
|
302
345
|
|
|
@@ -309,7 +352,11 @@ ${this.extractSection(this.outputs.plan || '', 'Architecture') || 'See technical
|
|
|
309
352
|
Full architecture: \`.adf/sessions/${this.getSessionId()}/outputs/specification.md\`
|
|
310
353
|
`;
|
|
311
354
|
} else {
|
|
312
|
-
content =
|
|
355
|
+
content = `---
|
|
356
|
+
trigger: always_on
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
# Architecture - ${projectName}
|
|
313
360
|
|
|
314
361
|
${this.extractSection(this.outputs.architecture || '', 'System Overview') || 'See architecture.md'}
|
|
315
362
|
|
|
@@ -331,7 +378,11 @@ Full architecture: \`.adf/sessions/${this.getSessionId()}/outputs/architecture.m
|
|
|
331
378
|
|
|
332
379
|
let content;
|
|
333
380
|
if (this.framework === 'rapid') {
|
|
334
|
-
content =
|
|
381
|
+
content = `---
|
|
382
|
+
trigger: always_on
|
|
383
|
+
---
|
|
384
|
+
|
|
385
|
+
# Coding Standards - ${projectName}
|
|
335
386
|
|
|
336
387
|
## General Guidelines
|
|
337
388
|
|
|
@@ -346,7 +397,11 @@ Full architecture: \`.adf/sessions/${this.getSessionId()}/outputs/architecture.m
|
|
|
346
397
|
See implementation blueprint in: \`.adf/sessions/${this.getSessionId()}/outputs/prp.md\`
|
|
347
398
|
`;
|
|
348
399
|
} else if (this.framework === 'balanced') {
|
|
349
|
-
content =
|
|
400
|
+
content = `---
|
|
401
|
+
trigger: always_on
|
|
402
|
+
---
|
|
403
|
+
|
|
404
|
+
# Coding Standards - ${projectName}
|
|
350
405
|
|
|
351
406
|
${this.extractSection(this.outputs.plan || '', 'Code Style') || this.extractSection(this.outputs.plan || '', 'Coding Standards') || ''}
|
|
352
407
|
|
|
@@ -359,7 +414,11 @@ ${this.extractSection(this.outputs.constitution || '', 'Core Principles') || 'Fo
|
|
|
359
414
|
Full standards: \`.adf/sessions/${this.getSessionId()}/outputs/plan.md\`
|
|
360
415
|
`;
|
|
361
416
|
} else {
|
|
362
|
-
content =
|
|
417
|
+
content = `---
|
|
418
|
+
trigger: always_on
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
# Coding Standards - ${projectName}
|
|
363
422
|
|
|
364
423
|
${this.extractSection(this.outputs.prd || '', 'Code Quality') || ''}
|
|
365
424
|
|
|
@@ -403,7 +462,12 @@ Full requirements: \`.adf/sessions/${this.getSessionId()}/outputs/prd.md\`
|
|
|
403
462
|
let content;
|
|
404
463
|
|
|
405
464
|
if (this.framework === 'rapid') {
|
|
406
|
-
content =
|
|
465
|
+
content = `---
|
|
466
|
+
trigger: manual
|
|
467
|
+
description: Review project requirements before implementing
|
|
468
|
+
---
|
|
469
|
+
|
|
470
|
+
# Review Requirements
|
|
407
471
|
|
|
408
472
|
Use this workflow to review requirements before implementing.
|
|
409
473
|
|
|
@@ -424,7 +488,12 @@ Use this workflow to review requirements before implementing.
|
|
|
424
488
|
- How will success be measured?
|
|
425
489
|
`;
|
|
426
490
|
} else if (this.framework === 'balanced') {
|
|
427
|
-
content =
|
|
491
|
+
content = `---
|
|
492
|
+
trigger: manual
|
|
493
|
+
description: Review project requirements before implementing
|
|
494
|
+
---
|
|
495
|
+
|
|
496
|
+
# Review Requirements
|
|
428
497
|
|
|
429
498
|
Use this workflow to review requirements before implementing.
|
|
430
499
|
|
|
@@ -447,7 +516,12 @@ Use this workflow to review requirements before implementing.
|
|
|
447
516
|
- Are there any dependencies?
|
|
448
517
|
`;
|
|
449
518
|
} else {
|
|
450
|
-
content =
|
|
519
|
+
content = `---
|
|
520
|
+
trigger: manual
|
|
521
|
+
description: Review project requirements before implementing
|
|
522
|
+
---
|
|
523
|
+
|
|
524
|
+
# Review Requirements
|
|
451
525
|
|
|
452
526
|
Use this workflow to review requirements before implementing.
|
|
453
527
|
|
|
@@ -477,7 +551,12 @@ Use this workflow to review requirements before implementing.
|
|
|
477
551
|
let content;
|
|
478
552
|
|
|
479
553
|
if (this.framework === 'balanced') {
|
|
480
|
-
content =
|
|
554
|
+
content = `---
|
|
555
|
+
trigger: manual
|
|
556
|
+
description: Workflow for implementing a new feature
|
|
557
|
+
---
|
|
558
|
+
|
|
559
|
+
# Implement Feature
|
|
481
560
|
|
|
482
561
|
Use this workflow when implementing a feature.
|
|
483
562
|
|
|
@@ -511,7 +590,12 @@ Use this workflow when implementing a feature.
|
|
|
511
590
|
- Tasks: \`.adf/sessions/${this.getSessionId()}/outputs/tasks.md\`
|
|
512
591
|
`;
|
|
513
592
|
} else {
|
|
514
|
-
content =
|
|
593
|
+
content = `---
|
|
594
|
+
trigger: manual
|
|
595
|
+
description: Workflow for implementing a user story
|
|
596
|
+
---
|
|
597
|
+
|
|
598
|
+
# Implement User Story
|
|
515
599
|
|
|
516
600
|
Use this workflow when implementing a user story.
|
|
517
601
|
|
|
@@ -591,6 +675,132 @@ Use this workflow when implementing a user story.
|
|
|
591
675
|
return '0.3.0';
|
|
592
676
|
}
|
|
593
677
|
}
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
* Load session answers from _progress.json
|
|
681
|
+
*/
|
|
682
|
+
async loadSessionAnswers() {
|
|
683
|
+
const fs = require('fs-extra');
|
|
684
|
+
const progressPath = path.join(this.sessionPath, '_progress.json');
|
|
685
|
+
|
|
686
|
+
try {
|
|
687
|
+
if (await fs.pathExists(progressPath)) {
|
|
688
|
+
const progress = await fs.readJson(progressPath);
|
|
689
|
+
return progress.answers || {};
|
|
690
|
+
}
|
|
691
|
+
} catch (error) {
|
|
692
|
+
// Fall back to empty if can't load
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
return {};
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
/**
|
|
699
|
+
* Parse implementation blueprint into structured sections
|
|
700
|
+
*/
|
|
701
|
+
parseImplementationBlueprint(blueprint, answers) {
|
|
702
|
+
const parsed = {
|
|
703
|
+
steps: null,
|
|
704
|
+
dataModel: null,
|
|
705
|
+
apiDesign: null,
|
|
706
|
+
errorHandling: null,
|
|
707
|
+
technicalApproach: null
|
|
708
|
+
};
|
|
709
|
+
|
|
710
|
+
// If blueprint exists, parse it
|
|
711
|
+
if (blueprint && blueprint.length > 20) {
|
|
712
|
+
parsed.steps = this.extractSubsection(blueprint, ['implementation steps', 'steps to implement', 'development steps']);
|
|
713
|
+
parsed.dataModel = this.extractSubsection(blueprint, ['data model', 'database schema', 'data structure']);
|
|
714
|
+
parsed.apiDesign = this.extractSubsection(blueprint, ['api design', 'api endpoints', 'routes']);
|
|
715
|
+
parsed.errorHandling = this.extractSubsection(blueprint, ['error handling', 'edge cases']);
|
|
716
|
+
parsed.technicalApproach = this.extractSubsection(blueprint, ['technical approach', 'architecture', 'approach']);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// If still empty, try to extract from answers
|
|
720
|
+
if (!parsed.steps && answers) {
|
|
721
|
+
// Look for tech stack, implementation details in any answer
|
|
722
|
+
const techStack = this.extractTechStackFromAnswers(answers);
|
|
723
|
+
const whatBuilding = this.extractWhatBuildingFromAnswers(answers);
|
|
724
|
+
|
|
725
|
+
if (techStack) {
|
|
726
|
+
parsed.technicalApproach = `**Tech Stack:**\n${techStack}`;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
if (whatBuilding) {
|
|
730
|
+
parsed.steps = `**Project Goal:**\n${whatBuilding}`;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
return parsed;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* Extract subsection from content by looking for keywords
|
|
739
|
+
*/
|
|
740
|
+
extractSubsection(content, keywords) {
|
|
741
|
+
const lines = content.split('\n');
|
|
742
|
+
let inSection = false;
|
|
743
|
+
let sectionContent = [];
|
|
744
|
+
|
|
745
|
+
for (let i = 0; i < lines.length; i++) {
|
|
746
|
+
const line = lines[i];
|
|
747
|
+
const lowerLine = line.toLowerCase();
|
|
748
|
+
|
|
749
|
+
// Check if this line is a heading matching our keywords
|
|
750
|
+
if (line.match(/^#{2,4}\s+/)) {
|
|
751
|
+
const headingText = line.replace(/^#{2,4}\s+/, '').toLowerCase();
|
|
752
|
+
|
|
753
|
+
if (keywords.some(kw => headingText.includes(kw))) {
|
|
754
|
+
inSection = true;
|
|
755
|
+
continue;
|
|
756
|
+
} else if (inSection) {
|
|
757
|
+
// Hit a new heading, stop
|
|
758
|
+
break;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
if (inSection && line.trim()) {
|
|
763
|
+
sectionContent.push(line);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
const result = sectionContent.join('\n').trim();
|
|
768
|
+
return result.length > 10 ? result : null;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
/**
|
|
772
|
+
* Extract tech stack from any answer
|
|
773
|
+
*/
|
|
774
|
+
extractTechStackFromAnswers(answers) {
|
|
775
|
+
for (const [questionId, answer] of Object.entries(answers)) {
|
|
776
|
+
if (typeof answer === 'string') {
|
|
777
|
+
const lower = answer.toLowerCase();
|
|
778
|
+
// Look for mentions of frameworks, languages, databases
|
|
779
|
+
if (lower.includes('react') || lower.includes('vue') || lower.includes('angular') ||
|
|
780
|
+
lower.includes('node') || lower.includes('python') || lower.includes('next') ||
|
|
781
|
+
lower.includes('postgres') || lower.includes('mongo') || lower.includes('mysql')) {
|
|
782
|
+
return answer;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
return null;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
/**
|
|
790
|
+
* Extract "what are you building" from any answer
|
|
791
|
+
*/
|
|
792
|
+
extractWhatBuildingFromAnswers(answers) {
|
|
793
|
+
for (const [questionId, answer] of Object.entries(answers)) {
|
|
794
|
+
if (questionId.toLowerCase().includes('goal') ||
|
|
795
|
+
questionId.toLowerCase().includes('building') ||
|
|
796
|
+
questionId.toLowerCase().includes('project')) {
|
|
797
|
+
if (typeof answer === 'string' && answer.length > 20) {
|
|
798
|
+
return answer;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
return null;
|
|
803
|
+
}
|
|
594
804
|
}
|
|
595
805
|
|
|
596
806
|
module.exports = WindsurfGenerator;
|
package/package.json
CHANGED
|
Binary file
|