@iservu-inc/adf-cli 0.3.0 → 0.3.6
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 → complete}/2025-10-03_AGENTS-MD-AND-TOOL-GENERATORS.md +82 -17
- package/.project/chats/current/2025-10-03_AI-PROVIDER-INTEGRATION.md +569 -0
- package/.project/chats/current/2025-10-03_FRAMEWORK-UPDATE-SYSTEM.md +497 -0
- package/.project/chats/current/SESSION-STATUS.md +127 -0
- package/.project/docs/AI-PROVIDER-INTEGRATION.md +600 -0
- package/.project/docs/FRAMEWORK-UPDATE-INTEGRATION.md +421 -0
- package/.project/docs/FRAMEWORK-UPDATE-SYSTEM.md +832 -0
- package/.project/docs/PROJECT-STRUCTURE-EXPLANATION.md +500 -0
- package/.project/docs/architecture/SYSTEM-DESIGN.md +122 -1
- package/.project/docs/goals/PROJECT-VISION.md +33 -28
- package/CHANGELOG.md +148 -0
- package/README.md +100 -11
- package/bin/adf.js +7 -0
- package/lib/ai/ai-client.js +328 -0
- package/lib/ai/ai-config.js +398 -0
- package/lib/commands/config.js +154 -0
- package/lib/commands/init.js +56 -10
- package/lib/frameworks/interviewer.js +89 -11
- package/lib/frameworks/progress-tracker.js +8 -1
- package/package.json +15 -3
|
@@ -13,9 +13,10 @@ const AnswerQualityAnalyzer = require('./answer-quality-analyzer');
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
class Interviewer {
|
|
16
|
-
constructor(framework, projectPath, existingSession = null) {
|
|
16
|
+
constructor(framework, projectPath, existingSession = null, aiConfig = null) {
|
|
17
17
|
this.framework = framework;
|
|
18
18
|
this.projectPath = projectPath;
|
|
19
|
+
this.aiConfig = aiConfig; // Store AI configuration
|
|
19
20
|
|
|
20
21
|
if (existingSession) {
|
|
21
22
|
// Resuming existing session
|
|
@@ -24,6 +25,8 @@ class Interviewer {
|
|
|
24
25
|
this.answers = existingSession.progress.answers || {};
|
|
25
26
|
this.transcript = existingSession.progress.transcript || [];
|
|
26
27
|
this.isResuming = true;
|
|
28
|
+
// Use stored AI config from session if available
|
|
29
|
+
this.aiConfig = existingSession.progress.aiConfig || aiConfig;
|
|
27
30
|
} else {
|
|
28
31
|
// New session
|
|
29
32
|
this.sessionId = this.generateSessionId();
|
|
@@ -34,6 +37,7 @@ class Interviewer {
|
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
this.progressTracker = null;
|
|
40
|
+
this.aiClient = null; // Will be initialized in start()
|
|
37
41
|
}
|
|
38
42
|
|
|
39
43
|
generateSessionId() {
|
|
@@ -48,6 +52,16 @@ class Interviewer {
|
|
|
48
52
|
console.log(chalk.gray(`Session: ${this.sessionId}`));
|
|
49
53
|
console.log(chalk.gray(`Files will be saved to: .adf/sessions/${this.sessionId}/\n`));
|
|
50
54
|
|
|
55
|
+
// Initialize AI Client
|
|
56
|
+
if (!this.aiClient && this.aiConfig) {
|
|
57
|
+
const AIClient = require('../ai/ai-client');
|
|
58
|
+
this.aiClient = new AIClient(this.aiConfig);
|
|
59
|
+
console.log(chalk.green(`✓ AI Provider: ${this.aiConfig.providerName} (${this.aiConfig.model})\n`));
|
|
60
|
+
} else if (!this.aiConfig) {
|
|
61
|
+
console.log(chalk.red('\n✖ Error: No AI configuration provided. Cannot start interview.\n'));
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
|
|
51
65
|
// Create session directory
|
|
52
66
|
await fs.ensureDir(this.sessionPath);
|
|
53
67
|
await fs.ensureDir(path.join(this.sessionPath, 'qa-responses'));
|
|
@@ -60,7 +74,7 @@ class Interviewer {
|
|
|
60
74
|
const questionBlocks = this.groupQuestionsIntoBlocks(questions);
|
|
61
75
|
|
|
62
76
|
// Initialize progress tracker
|
|
63
|
-
this.progressTracker = new ProgressTracker(this.sessionPath, questionBlocks.length, this.framework);
|
|
77
|
+
this.progressTracker = new ProgressTracker(this.sessionPath, questionBlocks.length, this.framework, this.aiConfig);
|
|
64
78
|
const isResumable = await this.progressTracker.initialize();
|
|
65
79
|
|
|
66
80
|
if (isResumable && this.isResuming) {
|
|
@@ -250,7 +264,8 @@ class Interviewer {
|
|
|
250
264
|
// Show guidance
|
|
251
265
|
console.log(chalk.gray(`💡 ${question.guidance}`));
|
|
252
266
|
console.log(chalk.green(` ✓ Good: ${question.goodExample}`));
|
|
253
|
-
console.log(chalk.red(` ✗ Bad: ${question.badExample}
|
|
267
|
+
console.log(chalk.red(` ✗ Bad: ${question.badExample}`));
|
|
268
|
+
console.log(chalk.gray(` (Type "skip" to skip remaining questions in this block)\n`));
|
|
254
269
|
|
|
255
270
|
// Get answer
|
|
256
271
|
const { answer } = await inquirer.prompt([
|
|
@@ -260,7 +275,7 @@ class Interviewer {
|
|
|
260
275
|
message: 'Your answer:',
|
|
261
276
|
validate: (input) => {
|
|
262
277
|
if (!input || input.trim().length === 0) {
|
|
263
|
-
return 'Please provide an answer or type "skip"
|
|
278
|
+
return 'Please provide an answer or type "skip"';
|
|
264
279
|
}
|
|
265
280
|
return true;
|
|
266
281
|
}
|
|
@@ -271,13 +286,41 @@ class Interviewer {
|
|
|
271
286
|
return null; // Signal to skip remaining questions
|
|
272
287
|
}
|
|
273
288
|
|
|
274
|
-
// Analyze answer quality
|
|
275
|
-
|
|
289
|
+
// Analyze answer quality (use AI if available, fallback to heuristic)
|
|
290
|
+
let qualityMetrics;
|
|
291
|
+
try {
|
|
292
|
+
if (this.aiClient) {
|
|
293
|
+
const aiAnalysis = await this.aiClient.analyzeAnswerQuality(question.text, answer);
|
|
294
|
+
qualityMetrics = {
|
|
295
|
+
wordCount: answer.trim().split(/\s+/).length,
|
|
296
|
+
qualityScore: aiAnalysis.score,
|
|
297
|
+
isComprehensive: aiAnalysis.score >= 70,
|
|
298
|
+
canSkipFollowUps: aiAnalysis.score >= 85,
|
|
299
|
+
issues: aiAnalysis.issues,
|
|
300
|
+
suggestions: aiAnalysis.suggestions,
|
|
301
|
+
missingElements: aiAnalysis.missingElements
|
|
302
|
+
};
|
|
303
|
+
} else {
|
|
304
|
+
// Fallback to heuristic analysis
|
|
305
|
+
qualityMetrics = AnswerQualityAnalyzer.analyze(answer, question);
|
|
306
|
+
}
|
|
307
|
+
} catch (error) {
|
|
308
|
+
// If AI analysis fails, use heuristic
|
|
309
|
+
console.log(chalk.yellow(`⚠️ AI analysis failed, using fallback method`));
|
|
310
|
+
qualityMetrics = AnswerQualityAnalyzer.analyze(answer, question);
|
|
311
|
+
}
|
|
276
312
|
|
|
277
313
|
// Show quality feedback
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
314
|
+
if (qualityMetrics.issues && qualityMetrics.issues.length > 0 && qualityMetrics.qualityScore < 70) {
|
|
315
|
+
console.log(chalk.yellow(`\n💡 Quality: ${qualityMetrics.qualityScore}/100`));
|
|
316
|
+
if (qualityMetrics.suggestions && qualityMetrics.suggestions.length > 0) {
|
|
317
|
+
console.log(chalk.gray(` Suggestion: ${qualityMetrics.suggestions[0]}`));
|
|
318
|
+
}
|
|
319
|
+
} else {
|
|
320
|
+
const feedback = AnswerQualityAnalyzer.getFeedback(qualityMetrics);
|
|
321
|
+
if (feedback) {
|
|
322
|
+
console.log(chalk.cyan(`${feedback}`));
|
|
323
|
+
}
|
|
281
324
|
}
|
|
282
325
|
|
|
283
326
|
// Track answer with quality metrics
|
|
@@ -290,7 +333,26 @@ class Interviewer {
|
|
|
290
333
|
}
|
|
291
334
|
|
|
292
335
|
// Check if answer needs follow-up
|
|
293
|
-
|
|
336
|
+
let followUp = null;
|
|
337
|
+
|
|
338
|
+
// Try AI-generated follow-up first
|
|
339
|
+
if (this.aiClient && qualityMetrics.issues && qualityMetrics.issues.length > 0 && qualityMetrics.qualityScore < 70) {
|
|
340
|
+
try {
|
|
341
|
+
const aiFollowUp = await this.aiClient.generateFollowUp(question.text, answer, qualityMetrics.issues);
|
|
342
|
+
if (aiFollowUp) {
|
|
343
|
+
followUp = {
|
|
344
|
+
message: "Let me ask a more specific question:",
|
|
345
|
+
question: aiFollowUp
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
} catch (error) {
|
|
349
|
+
// If AI fails, use heuristic fallback
|
|
350
|
+
followUp = this.determineFollowUp(question, answer);
|
|
351
|
+
}
|
|
352
|
+
} else {
|
|
353
|
+
// Use heuristic follow-up
|
|
354
|
+
followUp = this.determineFollowUp(question, answer);
|
|
355
|
+
}
|
|
294
356
|
|
|
295
357
|
if (followUp) {
|
|
296
358
|
console.log(chalk.yellow(`\n🤖 ${followUp.message}\n`));
|
|
@@ -308,7 +370,23 @@ class Interviewer {
|
|
|
308
370
|
const combined = `${answer} | Follow-up: ${followUpAnswer}`;
|
|
309
371
|
|
|
310
372
|
// Re-analyze combined answer
|
|
311
|
-
|
|
373
|
+
let combinedMetrics;
|
|
374
|
+
try {
|
|
375
|
+
if (this.aiClient) {
|
|
376
|
+
const aiAnalysis = await this.aiClient.analyzeAnswerQuality(question.text, combined);
|
|
377
|
+
combinedMetrics = {
|
|
378
|
+
wordCount: combined.trim().split(/\s+/).length,
|
|
379
|
+
qualityScore: aiAnalysis.score,
|
|
380
|
+
isComprehensive: aiAnalysis.score >= 70,
|
|
381
|
+
canSkipFollowUps: aiAnalysis.score >= 85
|
|
382
|
+
};
|
|
383
|
+
} else {
|
|
384
|
+
combinedMetrics = AnswerQualityAnalyzer.analyze(combined, question);
|
|
385
|
+
}
|
|
386
|
+
} catch {
|
|
387
|
+
combinedMetrics = AnswerQualityAnalyzer.analyze(combined, question);
|
|
388
|
+
}
|
|
389
|
+
|
|
312
390
|
await this.progressTracker.answerQuestion(question.id, question.text, combined, combinedMetrics);
|
|
313
391
|
|
|
314
392
|
console.log(chalk.green('\n✓ Saved\n'));
|
|
@@ -8,7 +8,7 @@ const chalk = require('chalk');
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
class ProgressTracker {
|
|
11
|
-
constructor(sessionPath, totalBlocks, framework = null) {
|
|
11
|
+
constructor(sessionPath, totalBlocks, framework = null, aiConfig = null) {
|
|
12
12
|
this.sessionPath = sessionPath;
|
|
13
13
|
this.progressFile = path.join(sessionPath, '_progress.json');
|
|
14
14
|
this.progressBackupFile = path.join(sessionPath, '_progress.backup.json');
|
|
@@ -17,6 +17,13 @@ class ProgressTracker {
|
|
|
17
17
|
this.progress = {
|
|
18
18
|
sessionId: path.basename(sessionPath),
|
|
19
19
|
framework: framework, // Store framework for resume
|
|
20
|
+
aiConfig: aiConfig ? {
|
|
21
|
+
provider: aiConfig.provider,
|
|
22
|
+
providerName: aiConfig.providerName,
|
|
23
|
+
model: aiConfig.model,
|
|
24
|
+
envVar: aiConfig.envVar
|
|
25
|
+
// Note: We don't store the API key for security
|
|
26
|
+
} : null,
|
|
20
27
|
status: 'in-progress',
|
|
21
28
|
startedAt: new Date().toISOString(),
|
|
22
29
|
lastUpdated: new Date().toISOString(),
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iservu-inc/adf-cli",
|
|
3
|
-
"version": "0.3.
|
|
4
|
-
"description": "CLI tool for AgentDevFramework - AI-assisted development framework",
|
|
3
|
+
"version": "0.3.6",
|
|
4
|
+
"description": "CLI tool for AgentDevFramework - AI-assisted development framework with multi-provider AI support",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"adf": "bin/adf.js"
|
|
@@ -24,15 +24,27 @@
|
|
|
24
24
|
"tdd",
|
|
25
25
|
"windsurf",
|
|
26
26
|
"cursor",
|
|
27
|
-
"claude-code"
|
|
27
|
+
"claude-code",
|
|
28
|
+
"anthropic",
|
|
29
|
+
"openai",
|
|
30
|
+
"gemini",
|
|
31
|
+
"openrouter",
|
|
32
|
+
"llm",
|
|
33
|
+
"requirements-gathering"
|
|
28
34
|
],
|
|
29
35
|
"author": "iServU",
|
|
30
36
|
"license": "MIT",
|
|
31
37
|
"dependencies": {
|
|
38
|
+
"@anthropic-ai/sdk": "^0.32.0",
|
|
39
|
+
"@google/generative-ai": "^0.21.0",
|
|
32
40
|
"chalk": "^4.1.2",
|
|
33
41
|
"commander": "^11.1.0",
|
|
42
|
+
"dotenv": "^17.2.3",
|
|
34
43
|
"fs-extra": "^11.2.0",
|
|
35
44
|
"inquirer": "^8.2.5",
|
|
45
|
+
"inquirer-autocomplete-prompt": "^2.0.1",
|
|
46
|
+
"node-fetch": "^2.7.0",
|
|
47
|
+
"openai": "^4.73.0",
|
|
36
48
|
"ora": "^5.4.1"
|
|
37
49
|
},
|
|
38
50
|
"engines": {
|