@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.
@@ -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}\n`));
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" to skip remaining questions in this block';
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
- const qualityMetrics = AnswerQualityAnalyzer.analyze(answer, question);
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
- const feedback = AnswerQualityAnalyzer.getFeedback(qualityMetrics);
279
- if (feedback) {
280
- console.log(chalk.cyan(`${feedback}`));
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
- const followUp = this.determineFollowUp(question, answer);
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
- const combinedMetrics = AnswerQualityAnalyzer.analyze(combined, question);
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.0",
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": {