@iservu-inc/adf-cli 0.4.35 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.project/chats/current/2025-10-05_MULTI-IDE-IMPROVEMENTS.md +415 -0
- package/.project/docs/ROADMAP.md +150 -17
- package/CHANGELOG.md +220 -0
- package/lib/analysis/answer-analyzer.js +304 -0
- package/lib/analysis/dynamic-pipeline.js +262 -0
- package/lib/analysis/knowledge-graph.js +227 -0
- package/lib/analysis/question-mapper.js +293 -0
- package/lib/commands/config.js +127 -4
- package/lib/frameworks/interviewer.js +59 -0
- package/lib/generators/cursor-generator.js +77 -6
- package/lib/generators/vscode-generator.js +75 -6
- package/package.json +1 -1
- package/tests/answer-analyzer.test.js +262 -0
- package/tests/dynamic-pipeline.test.js +332 -0
- package/tests/knowledge-graph.test.js +322 -0
- package/tests/question-mapper.test.js +342 -0
- /package/.project/chats/{current → complete}/2025-10-04_CRITICAL-MODEL-FETCHING-BUG.md +0 -0
- /package/.project/chats/{current → complete}/2025-10-04_PHASE-4-2-COMPLETION-AND-ROADMAP.md +0 -0
- /package/.project/chats/{current → complete}/2025-10-04_PHASE-4-2-LEARNING-SYSTEM.md +0 -0
|
@@ -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;
|
package/package.json
CHANGED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
const AnswerAnalyzer = require('../lib/analysis/answer-analyzer');
|
|
2
|
+
|
|
3
|
+
describe('AnswerAnalyzer', () => {
|
|
4
|
+
let analyzer;
|
|
5
|
+
let mockAIClient;
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
mockAIClient = {
|
|
9
|
+
sendMessage: jest.fn()
|
|
10
|
+
};
|
|
11
|
+
analyzer = new AnswerAnalyzer(mockAIClient);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
describe('heuristicExtraction', () => {
|
|
15
|
+
it('should extract tech stack from answer', () => {
|
|
16
|
+
const answer = 'I am building a React frontend with PostgreSQL database and Node.js backend';
|
|
17
|
+
const questionId = 'what-building';
|
|
18
|
+
|
|
19
|
+
const extracted = analyzer.heuristicExtraction(answer, questionId);
|
|
20
|
+
|
|
21
|
+
const techStack = extracted.find(e => e.type === 'tech_stack');
|
|
22
|
+
expect(techStack).toBeDefined();
|
|
23
|
+
expect(techStack.extractedTerms).toContain('react');
|
|
24
|
+
expect(techStack.extractedTerms).toContain('postgresql');
|
|
25
|
+
expect(techStack.extractedTerms).toContain('node');
|
|
26
|
+
expect(techStack.confidence).toBeGreaterThan(60);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should extract architecture patterns', () => {
|
|
30
|
+
const answer = 'I will build a frontend with a separate backend API using microservices';
|
|
31
|
+
const questionId = 'architecture';
|
|
32
|
+
|
|
33
|
+
const extracted = analyzer.heuristicExtraction(answer, questionId);
|
|
34
|
+
|
|
35
|
+
const frontendBackend = extracted.find(e =>
|
|
36
|
+
e.type === 'architecture' && e.pattern === 'frontend-backend-separation'
|
|
37
|
+
);
|
|
38
|
+
const microservices = extracted.find(e =>
|
|
39
|
+
e.type === 'architecture' && e.pattern === 'microservices'
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
expect(frontendBackend).toBeDefined();
|
|
43
|
+
expect(microservices).toBeDefined();
|
|
44
|
+
expect(frontendBackend.confidence).toBeGreaterThan(70);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should extract platform information', () => {
|
|
48
|
+
const answer = 'This is a mobile app for iOS and Android using React Native';
|
|
49
|
+
const questionId = 'platform';
|
|
50
|
+
|
|
51
|
+
const extracted = analyzer.heuristicExtraction(answer, questionId);
|
|
52
|
+
|
|
53
|
+
const platform = extracted.find(e => e.type === 'platform');
|
|
54
|
+
expect(platform).toBeDefined();
|
|
55
|
+
expect(platform.platform).toBe('mobile');
|
|
56
|
+
expect(platform.confidence).toBeGreaterThan(70);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should extract project goal from goal questions', () => {
|
|
60
|
+
const answer = 'Create a social media platform for developers to share code snippets';
|
|
61
|
+
const questionId = 'what-is-goal';
|
|
62
|
+
|
|
63
|
+
const extracted = analyzer.heuristicExtraction(answer, questionId);
|
|
64
|
+
|
|
65
|
+
const goal = extracted.find(e => e.type === 'project_goal');
|
|
66
|
+
expect(goal).toBeDefined();
|
|
67
|
+
expect(goal.confidence).toBe(95);
|
|
68
|
+
expect(goal.content).toBe(answer);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should extract timeline information', () => {
|
|
72
|
+
const answer = 'I need this completed in 3 weeks for a product launch';
|
|
73
|
+
const questionId = 'timeline';
|
|
74
|
+
|
|
75
|
+
const extracted = analyzer.heuristicExtraction(answer, questionId);
|
|
76
|
+
|
|
77
|
+
const timeline = extracted.find(e => e.type === 'timeline');
|
|
78
|
+
expect(timeline).toBeDefined();
|
|
79
|
+
expect(timeline.confidence).toBeGreaterThan(70);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should extract target users', () => {
|
|
83
|
+
const answer = 'The target audience is small businesses and their customers';
|
|
84
|
+
const questionId = 'users';
|
|
85
|
+
|
|
86
|
+
const extracted = analyzer.heuristicExtraction(answer, questionId);
|
|
87
|
+
|
|
88
|
+
const users = extracted.find(e => e.type === 'target_users');
|
|
89
|
+
expect(users).toBeDefined();
|
|
90
|
+
expect(users.confidence).toBe(70);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should return empty array for uninformative answers', () => {
|
|
94
|
+
const answer = 'yes';
|
|
95
|
+
const questionId = 'question';
|
|
96
|
+
|
|
97
|
+
const extracted = analyzer.heuristicExtraction(answer, questionId);
|
|
98
|
+
|
|
99
|
+
expect(extracted).toEqual([]);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should handle multiple tech mentions with higher confidence', () => {
|
|
103
|
+
const answer = 'Using React, Next.js, TypeScript, PostgreSQL, Redis, and AWS';
|
|
104
|
+
const questionId = 'tech';
|
|
105
|
+
|
|
106
|
+
const extracted = analyzer.heuristicExtraction(answer, questionId);
|
|
107
|
+
|
|
108
|
+
const techStack = extracted.find(e => e.type === 'tech_stack');
|
|
109
|
+
expect(techStack).toBeDefined();
|
|
110
|
+
expect(techStack.extractedTerms.length).toBeGreaterThanOrEqual(5);
|
|
111
|
+
expect(techStack.confidence).toBeGreaterThan(80);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe('aiExtraction', () => {
|
|
116
|
+
it('should extract information using AI when available', async () => {
|
|
117
|
+
const mockAIResponse = JSON.stringify([
|
|
118
|
+
{
|
|
119
|
+
type: 'tech_stack',
|
|
120
|
+
content: 'React and PostgreSQL',
|
|
121
|
+
confidence: 95,
|
|
122
|
+
reasoning: 'Explicitly mentioned'
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
type: 'architecture',
|
|
126
|
+
content: 'separate backend API',
|
|
127
|
+
confidence: 85,
|
|
128
|
+
reasoning: 'Implies frontend-backend separation'
|
|
129
|
+
}
|
|
130
|
+
]);
|
|
131
|
+
|
|
132
|
+
mockAIClient.sendMessage.mockResolvedValue(mockAIResponse);
|
|
133
|
+
|
|
134
|
+
const extracted = await analyzer.aiExtraction(
|
|
135
|
+
'What tech stack?',
|
|
136
|
+
'React frontend with separate backend API using PostgreSQL',
|
|
137
|
+
'tech-stack'
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
expect(mockAIClient.sendMessage).toHaveBeenCalled();
|
|
141
|
+
expect(extracted).toHaveLength(2);
|
|
142
|
+
expect(extracted[0].type).toBe('tech_stack');
|
|
143
|
+
expect(extracted[0].method).toBe('ai');
|
|
144
|
+
expect(extracted[0].source).toBe('tech-stack');
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('should handle AI extraction errors gracefully', async () => {
|
|
148
|
+
mockAIClient.sendMessage.mockRejectedValue(new Error('AI error'));
|
|
149
|
+
|
|
150
|
+
const extracted = await analyzer.aiExtraction(
|
|
151
|
+
'What are you building?',
|
|
152
|
+
'A web app',
|
|
153
|
+
'goal'
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
expect(extracted).toEqual([]);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('should handle invalid JSON from AI', async () => {
|
|
160
|
+
mockAIClient.sendMessage.mockResolvedValue('This is not JSON');
|
|
161
|
+
|
|
162
|
+
const extracted = await analyzer.aiExtraction(
|
|
163
|
+
'What tech?',
|
|
164
|
+
'React and Node',
|
|
165
|
+
'tech'
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
expect(extracted).toEqual([]);
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
describe('analyzeAnswer', () => {
|
|
173
|
+
it('should combine heuristic and AI results', async () => {
|
|
174
|
+
const mockAIResponse = JSON.stringify([
|
|
175
|
+
{
|
|
176
|
+
type: 'tech_stack',
|
|
177
|
+
content: 'React and Node.js',
|
|
178
|
+
confidence: 90,
|
|
179
|
+
reasoning: 'Explicitly stated'
|
|
180
|
+
}
|
|
181
|
+
]);
|
|
182
|
+
|
|
183
|
+
mockAIClient.sendMessage.mockResolvedValue(mockAIResponse);
|
|
184
|
+
|
|
185
|
+
const extracted = await analyzer.analyzeAnswer(
|
|
186
|
+
'What tech stack?',
|
|
187
|
+
'I will use React for frontend and Node.js for backend',
|
|
188
|
+
'tech-stack'
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
expect(extracted.length).toBeGreaterThan(0);
|
|
192
|
+
// Should have at least tech_stack from both methods
|
|
193
|
+
const techStackItems = extracted.filter(e => e.type === 'tech_stack');
|
|
194
|
+
expect(techStackItems.length).toBeGreaterThanOrEqual(1);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('should use AI result if confidence is higher', async () => {
|
|
198
|
+
const mockAIResponse = JSON.stringify([
|
|
199
|
+
{
|
|
200
|
+
type: 'tech_stack',
|
|
201
|
+
content: 'Full tech stack analysis',
|
|
202
|
+
confidence: 95,
|
|
203
|
+
reasoning: 'Comprehensive analysis'
|
|
204
|
+
}
|
|
205
|
+
]);
|
|
206
|
+
|
|
207
|
+
mockAIClient.sendMessage.mockResolvedValue(mockAIResponse);
|
|
208
|
+
|
|
209
|
+
const extracted = await analyzer.analyzeAnswer(
|
|
210
|
+
'What tech?',
|
|
211
|
+
'React and Node',
|
|
212
|
+
'tech'
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
const techStack = extracted.find(e => e.type === 'tech_stack');
|
|
216
|
+
expect(techStack.confidence).toBeGreaterThanOrEqual(85);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('should work without AI client', async () => {
|
|
220
|
+
const analyzerWithoutAI = new AnswerAnalyzer(null);
|
|
221
|
+
|
|
222
|
+
const extracted = await analyzerWithoutAI.analyzeAnswer(
|
|
223
|
+
'What are you building?',
|
|
224
|
+
'A React web application with PostgreSQL database',
|
|
225
|
+
'goal'
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
expect(extracted.length).toBeGreaterThan(0);
|
|
229
|
+
const techStack = extracted.find(e => e.type === 'tech_stack');
|
|
230
|
+
expect(techStack).toBeDefined();
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
describe('getSummary', () => {
|
|
235
|
+
it('should generate human-readable summary', () => {
|
|
236
|
+
const extractedInfo = [
|
|
237
|
+
{ type: 'tech_stack', confidence: 90 },
|
|
238
|
+
{ type: 'architecture', confidence: 85 },
|
|
239
|
+
{ type: 'project_goal', confidence: 95 }
|
|
240
|
+
];
|
|
241
|
+
|
|
242
|
+
const summary = analyzer.getSummary(extractedInfo);
|
|
243
|
+
|
|
244
|
+
expect(summary).toContain('TECH STACK: 90% confidence');
|
|
245
|
+
expect(summary).toContain('ARCHITECTURE: 85% confidence');
|
|
246
|
+
expect(summary).toContain('PROJECT GOAL: 95% confidence');
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it('should handle multiple items of same type', () => {
|
|
250
|
+
const extractedInfo = [
|
|
251
|
+
{ type: 'tech_stack', confidence: 90 },
|
|
252
|
+
{ type: 'tech_stack', confidence: 80 },
|
|
253
|
+
{ type: 'architecture', confidence: 85 }
|
|
254
|
+
];
|
|
255
|
+
|
|
256
|
+
const summary = analyzer.getSummary(extractedInfo);
|
|
257
|
+
|
|
258
|
+
// Should show highest confidence
|
|
259
|
+
expect(summary).toContain('TECH STACK: 90% confidence');
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
});
|