@iservu-inc/adf-cli 0.3.0 → 0.4.12
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/complete/2025-10-03_AI-PROVIDER-INTEGRATION.md +568 -0
- package/.project/chats/complete/2025-10-03_FRAMEWORK-UPDATE-SYSTEM.md +497 -0
- package/.project/chats/complete/2025-10-04_CONFIG-COMMAND.md +503 -0
- package/.project/chats/current/2025-10-04_PHASE-4-1-SMART-FILTERING.md +381 -0
- package/.project/chats/current/SESSION-STATUS.md +168 -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/PHASE-4-2-LEARNING-SYSTEM.md +881 -0
- package/.project/docs/PROJECT-STRUCTURE-EXPLANATION.md +500 -0
- package/.project/docs/SMART-FILTERING-SYSTEM.md +385 -0
- package/.project/docs/architecture/SYSTEM-DESIGN.md +122 -1
- package/.project/docs/goals/PROJECT-VISION.md +61 -34
- package/CHANGELOG.md +257 -1
- package/README.md +476 -292
- package/bin/adf.js +7 -0
- package/lib/ai/ai-client.js +328 -0
- package/lib/ai/ai-config.js +398 -0
- package/lib/analyzers/project-analyzer.js +380 -0
- package/lib/commands/config.js +221 -0
- package/lib/commands/init.js +56 -10
- package/lib/filters/question-filter.js +480 -0
- package/lib/frameworks/interviewer.js +271 -12
- package/lib/frameworks/progress-tracker.js +8 -1
- package/lib/learning/learning-manager.js +447 -0
- package/lib/learning/pattern-detector.js +376 -0
- package/lib/learning/rule-generator.js +304 -0
- package/lib/learning/skip-tracker.js +260 -0
- package/lib/learning/storage.js +296 -0
- package/package.json +70 -57
- package/tests/learning-storage.test.js +184 -0
- package/tests/pattern-detector.test.js +297 -0
- package/tests/project-analyzer.test.js +221 -0
- package/tests/question-filter.test.js +297 -0
- package/tests/skip-tracker.test.js +198 -0
|
@@ -2,9 +2,16 @@ const fs = require('fs-extra');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const inquirer = require('inquirer');
|
|
4
4
|
const chalk = require('chalk');
|
|
5
|
+
const ora = require('ora');
|
|
5
6
|
const { getQuestionsForFramework } = require('./questions');
|
|
6
7
|
const ProgressTracker = require('./progress-tracker');
|
|
7
8
|
const AnswerQualityAnalyzer = require('./answer-quality-analyzer');
|
|
9
|
+
const { analyzeProject, getProjectSummary } = require('../analyzers/project-analyzer');
|
|
10
|
+
const { filterQuestions, getFilteringSummary } = require('../filters/question-filter');
|
|
11
|
+
const { SkipTracker } = require('../learning/skip-tracker');
|
|
12
|
+
const { detectPatterns } = require('../learning/pattern-detector');
|
|
13
|
+
const { updateLearnedRules, getActiveRules, getRuleExplanations } = require('../learning/rule-generator');
|
|
14
|
+
const { getLearningConfig } = require('../learning/storage');
|
|
8
15
|
|
|
9
16
|
/**
|
|
10
17
|
* Conversational AI Interviewer
|
|
@@ -13,9 +20,10 @@ const AnswerQualityAnalyzer = require('./answer-quality-analyzer');
|
|
|
13
20
|
*/
|
|
14
21
|
|
|
15
22
|
class Interviewer {
|
|
16
|
-
constructor(framework, projectPath, existingSession = null) {
|
|
23
|
+
constructor(framework, projectPath, existingSession = null, aiConfig = null) {
|
|
17
24
|
this.framework = framework;
|
|
18
25
|
this.projectPath = projectPath;
|
|
26
|
+
this.aiConfig = aiConfig; // Store AI configuration
|
|
19
27
|
|
|
20
28
|
if (existingSession) {
|
|
21
29
|
// Resuming existing session
|
|
@@ -24,6 +32,8 @@ class Interviewer {
|
|
|
24
32
|
this.answers = existingSession.progress.answers || {};
|
|
25
33
|
this.transcript = existingSession.progress.transcript || [];
|
|
26
34
|
this.isResuming = true;
|
|
35
|
+
// Use stored AI config from session if available
|
|
36
|
+
this.aiConfig = existingSession.progress.aiConfig || aiConfig;
|
|
27
37
|
} else {
|
|
28
38
|
// New session
|
|
29
39
|
this.sessionId = this.generateSessionId();
|
|
@@ -34,6 +44,9 @@ class Interviewer {
|
|
|
34
44
|
}
|
|
35
45
|
|
|
36
46
|
this.progressTracker = null;
|
|
47
|
+
this.aiClient = null; // Will be initialized in start()
|
|
48
|
+
this.skipTracker = null; // Will be initialized in start() with project context
|
|
49
|
+
this.learnedRules = []; // Will be loaded in start()
|
|
37
50
|
}
|
|
38
51
|
|
|
39
52
|
generateSessionId() {
|
|
@@ -48,19 +61,162 @@ class Interviewer {
|
|
|
48
61
|
console.log(chalk.gray(`Session: ${this.sessionId}`));
|
|
49
62
|
console.log(chalk.gray(`Files will be saved to: .adf/sessions/${this.sessionId}/\n`));
|
|
50
63
|
|
|
64
|
+
// Initialize AI Client
|
|
65
|
+
if (!this.aiClient && this.aiConfig) {
|
|
66
|
+
const AIClient = require('../ai/ai-client');
|
|
67
|
+
this.aiClient = new AIClient(this.aiConfig);
|
|
68
|
+
console.log(chalk.green(`✓ AI Provider: ${this.aiConfig.providerName} (${this.aiConfig.model})\n`));
|
|
69
|
+
} else if (!this.aiConfig) {
|
|
70
|
+
console.log(chalk.yellow('\n⚠️ No AI configuration - continuing without AI assistance\n'));
|
|
71
|
+
// Allow interview to continue without AI (graceful degradation)
|
|
72
|
+
}
|
|
73
|
+
|
|
51
74
|
// Create session directory
|
|
52
75
|
await fs.ensureDir(this.sessionPath);
|
|
53
76
|
await fs.ensureDir(path.join(this.sessionPath, 'qa-responses'));
|
|
54
77
|
await fs.ensureDir(path.join(this.sessionPath, 'outputs'));
|
|
55
78
|
|
|
79
|
+
// Analyze project for smart filtering
|
|
80
|
+
const spinner = ora('Analyzing your project for context...').start();
|
|
81
|
+
let projectContext = null;
|
|
82
|
+
let enableSmartFiltering = false;
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
projectContext = await analyzeProject(this.projectPath);
|
|
86
|
+
spinner.succeed(chalk.green(`✓ Project analyzed: ${getProjectSummary(projectContext)}`));
|
|
87
|
+
|
|
88
|
+
// Initialize skip tracker with project context
|
|
89
|
+
this.skipTracker = new SkipTracker(this.projectPath, {
|
|
90
|
+
projectType: projectContext.type,
|
|
91
|
+
frameworks: projectContext.frameworks,
|
|
92
|
+
languages: projectContext.languages
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Load learning configuration and learned rules
|
|
96
|
+
const learningConfig = await getLearningConfig(this.projectPath);
|
|
97
|
+
if (learningConfig.enabled && learningConfig.applyLearnedFilters) {
|
|
98
|
+
this.learnedRules = await getActiveRules(this.projectPath);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (projectContext.confidence >= 50) {
|
|
102
|
+
// Show project context
|
|
103
|
+
console.log(chalk.gray(`\n📊 Project Context:`));
|
|
104
|
+
console.log(chalk.gray(` Type: ${projectContext.type}`));
|
|
105
|
+
if (projectContext.frameworks.length > 0) {
|
|
106
|
+
console.log(chalk.gray(` Frameworks: ${projectContext.frameworks.join(', ')}`));
|
|
107
|
+
}
|
|
108
|
+
if (projectContext.languages.length > 0) {
|
|
109
|
+
console.log(chalk.gray(` Languages: ${projectContext.languages.join(', ')}`));
|
|
110
|
+
}
|
|
111
|
+
console.log(chalk.gray(` Confidence: ${projectContext.confidence}%`));
|
|
112
|
+
|
|
113
|
+
// Show learning status
|
|
114
|
+
if (this.learnedRules.length > 0) {
|
|
115
|
+
console.log(chalk.gray(` Learning: ${this.learnedRules.length} learned rule${this.learnedRules.length > 1 ? 's' : ''} active\n`));
|
|
116
|
+
} else {
|
|
117
|
+
console.log('');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Ask user if they want smart filtering
|
|
121
|
+
const { useSmartFiltering } = await inquirer.prompt([
|
|
122
|
+
{
|
|
123
|
+
type: 'confirm',
|
|
124
|
+
name: 'useSmartFiltering',
|
|
125
|
+
message: 'Enable smart filtering to skip irrelevant questions? (Recommended)',
|
|
126
|
+
default: true
|
|
127
|
+
}
|
|
128
|
+
]);
|
|
129
|
+
|
|
130
|
+
enableSmartFiltering = useSmartFiltering;
|
|
131
|
+
|
|
132
|
+
// If user enables filtering and we have learned rules, ask about applying them
|
|
133
|
+
if (enableSmartFiltering && this.learnedRules.length > 0) {
|
|
134
|
+
const ruleExplanations = getRuleExplanations(this.learnedRules);
|
|
135
|
+
console.log(chalk.cyan('\n🧠 I\'ve learned from your previous sessions:'));
|
|
136
|
+
for (const exp of ruleExplanations.slice(0, 3)) { // Show top 3
|
|
137
|
+
console.log(chalk.gray(` • ${exp.message}`));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const { applyLearning } = await inquirer.prompt([
|
|
141
|
+
{
|
|
142
|
+
type: 'confirm',
|
|
143
|
+
name: 'applyLearning',
|
|
144
|
+
message: 'Apply learned preferences? (You can manage these in: adf config learning)',
|
|
145
|
+
default: true
|
|
146
|
+
}
|
|
147
|
+
]);
|
|
148
|
+
|
|
149
|
+
if (!applyLearning) {
|
|
150
|
+
this.learnedRules = []; // User declined
|
|
151
|
+
console.log(chalk.gray('\n○ Learning disabled for this session\n'));
|
|
152
|
+
} else {
|
|
153
|
+
console.log(chalk.green('\n✓ Learning enabled - applying your preferences\n'));
|
|
154
|
+
}
|
|
155
|
+
} else if (enableSmartFiltering) {
|
|
156
|
+
console.log(chalk.green('\n✓ Smart filtering enabled - I\'ll focus on relevant questions\n'));
|
|
157
|
+
} else {
|
|
158
|
+
console.log(chalk.gray('\n○ Smart filtering disabled - I\'ll ask all questions\n'));
|
|
159
|
+
}
|
|
160
|
+
} else {
|
|
161
|
+
spinner.warn(chalk.yellow(`Project analysis incomplete (${projectContext.confidence}% confidence) - asking all questions`));
|
|
162
|
+
console.log('');
|
|
163
|
+
}
|
|
164
|
+
} catch (error) {
|
|
165
|
+
spinner.fail(chalk.yellow('Could not analyze project - asking all questions'));
|
|
166
|
+
console.log('');
|
|
167
|
+
projectContext = { type: 'unknown', frameworks: [], languages: [], confidence: 0 };
|
|
168
|
+
|
|
169
|
+
// Still initialize skip tracker for basic tracking
|
|
170
|
+
this.skipTracker = new SkipTracker(this.projectPath, {
|
|
171
|
+
projectType: 'unknown',
|
|
172
|
+
frameworks: [],
|
|
173
|
+
languages: []
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
56
177
|
// Get questions for framework
|
|
57
|
-
|
|
178
|
+
let questions = getQuestionsForFramework(this.framework);
|
|
179
|
+
|
|
180
|
+
// Apply smart filtering if enabled
|
|
181
|
+
let filteringResult = null;
|
|
182
|
+
if (enableSmartFiltering && projectContext.confidence >= 50) {
|
|
183
|
+
filteringResult = filterQuestions(questions, projectContext, {
|
|
184
|
+
enableSmartFiltering: true,
|
|
185
|
+
minRelevanceScore: 50,
|
|
186
|
+
aiClient: this.aiClient,
|
|
187
|
+
learnedRules: this.learnedRules // Pass learned rules
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
questions = filteringResult.questions.map(q => ({
|
|
191
|
+
...q,
|
|
192
|
+
relevance: q.relevance // Keep relevance score
|
|
193
|
+
}));
|
|
194
|
+
|
|
195
|
+
// Record filtered questions to skip tracker
|
|
196
|
+
if (this.skipTracker && filteringResult.skipped.length > 0) {
|
|
197
|
+
this.skipTracker.recordFilteredQuestions(filteringResult.skipped);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Show filtering summary
|
|
201
|
+
if (filteringResult.summary.skipped > 0) {
|
|
202
|
+
console.log(chalk.cyan(`🎯 Smart Filtering Results:`));
|
|
203
|
+
console.log(chalk.green(` ✓ ${filteringResult.summary.kept} relevant questions selected`));
|
|
204
|
+
console.log(chalk.gray(` ○ ${filteringResult.summary.skipped} questions skipped`));
|
|
205
|
+
|
|
206
|
+
// Show learning-based skips if any
|
|
207
|
+
if (filteringResult.summary.learningActive && filteringResult.summary.skippedByLearning > 0) {
|
|
208
|
+
console.log(chalk.magenta(` 🧠 ${filteringResult.summary.skippedByLearning} skipped based on your learning history`));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
console.log(chalk.blue(` ⏱️ Estimated time saved: ~${filteringResult.summary.timeSaved} minutes\n`));
|
|
212
|
+
}
|
|
213
|
+
}
|
|
58
214
|
|
|
59
215
|
// Group questions into blocks by phase
|
|
60
216
|
const questionBlocks = this.groupQuestionsIntoBlocks(questions);
|
|
61
217
|
|
|
62
218
|
// Initialize progress tracker
|
|
63
|
-
this.progressTracker = new ProgressTracker(this.sessionPath, questionBlocks.length, this.framework);
|
|
219
|
+
this.progressTracker = new ProgressTracker(this.sessionPath, questionBlocks.length, this.framework, this.aiConfig);
|
|
64
220
|
const isResumable = await this.progressTracker.initialize();
|
|
65
221
|
|
|
66
222
|
if (isResumable && this.isResuming) {
|
|
@@ -119,6 +275,28 @@ class Interviewer {
|
|
|
119
275
|
// Save session metadata
|
|
120
276
|
await this.saveMetadata();
|
|
121
277
|
|
|
278
|
+
// Save learning data and update patterns (Phase 4.2)
|
|
279
|
+
if (this.skipTracker) {
|
|
280
|
+
try {
|
|
281
|
+
// Save skip tracker session
|
|
282
|
+
await this.skipTracker.saveSession();
|
|
283
|
+
|
|
284
|
+
// Detect patterns and update learned rules
|
|
285
|
+
const learningConfig = await getLearningConfig(this.projectPath);
|
|
286
|
+
if (learningConfig.enabled && learningConfig.detectPatterns) {
|
|
287
|
+
const patterns = await detectPatterns(this.projectPath);
|
|
288
|
+
const updateResult = await updateLearnedRules(this.projectPath, patterns);
|
|
289
|
+
|
|
290
|
+
if (updateResult.added > 0) {
|
|
291
|
+
console.log(chalk.cyan(`\n🧠 Learning System: Detected ${updateResult.added} new pattern${updateResult.added > 1 ? 's' : ''}`));
|
|
292
|
+
console.log(chalk.gray(` View with: adf config learning\n`));
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
} catch (error) {
|
|
296
|
+
console.warn(chalk.yellow('⚠️ Could not save learning data:', error.message));
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
122
300
|
// Mark session as complete
|
|
123
301
|
await this.progressTracker.complete();
|
|
124
302
|
|
|
@@ -250,7 +428,13 @@ class Interviewer {
|
|
|
250
428
|
// Show guidance
|
|
251
429
|
console.log(chalk.gray(`💡 ${question.guidance}`));
|
|
252
430
|
console.log(chalk.green(` ✓ Good: ${question.goodExample}`));
|
|
253
|
-
console.log(chalk.red(` ✗ Bad: ${question.badExample}
|
|
431
|
+
console.log(chalk.red(` ✗ Bad: ${question.badExample}`));
|
|
432
|
+
console.log(chalk.gray(` (Type "skip" to skip remaining questions in this block)\n`));
|
|
433
|
+
|
|
434
|
+
// Start tracking time for this question
|
|
435
|
+
if (this.skipTracker) {
|
|
436
|
+
this.skipTracker.startQuestion(question.id);
|
|
437
|
+
}
|
|
254
438
|
|
|
255
439
|
// Get answer
|
|
256
440
|
const { answer } = await inquirer.prompt([
|
|
@@ -260,7 +444,7 @@ class Interviewer {
|
|
|
260
444
|
message: 'Your answer:',
|
|
261
445
|
validate: (input) => {
|
|
262
446
|
if (!input || input.trim().length === 0) {
|
|
263
|
-
return 'Please provide an answer or type "skip"
|
|
447
|
+
return 'Please provide an answer or type "skip"';
|
|
264
448
|
}
|
|
265
449
|
return true;
|
|
266
450
|
}
|
|
@@ -268,21 +452,61 @@ class Interviewer {
|
|
|
268
452
|
]);
|
|
269
453
|
|
|
270
454
|
if (answer.toLowerCase() === 'skip') {
|
|
455
|
+
// Record skip event
|
|
456
|
+
if (this.skipTracker) {
|
|
457
|
+
this.skipTracker.recordSkip(question, 'manual');
|
|
458
|
+
}
|
|
271
459
|
return null; // Signal to skip remaining questions
|
|
272
460
|
}
|
|
273
461
|
|
|
274
|
-
// Analyze answer quality
|
|
275
|
-
|
|
462
|
+
// Analyze answer quality (use AI if available, fallback to heuristic)
|
|
463
|
+
let qualityMetrics;
|
|
464
|
+
try {
|
|
465
|
+
if (this.aiClient) {
|
|
466
|
+
const aiAnalysis = await this.aiClient.analyzeAnswerQuality(question.text, answer);
|
|
467
|
+
qualityMetrics = {
|
|
468
|
+
wordCount: answer.trim().split(/\s+/).length,
|
|
469
|
+
qualityScore: aiAnalysis.score,
|
|
470
|
+
isComprehensive: aiAnalysis.score >= 70,
|
|
471
|
+
canSkipFollowUps: aiAnalysis.score >= 85,
|
|
472
|
+
issues: aiAnalysis.issues,
|
|
473
|
+
suggestions: aiAnalysis.suggestions,
|
|
474
|
+
missingElements: aiAnalysis.missingElements
|
|
475
|
+
};
|
|
476
|
+
} else {
|
|
477
|
+
// Fallback to heuristic analysis
|
|
478
|
+
qualityMetrics = AnswerQualityAnalyzer.analyze(answer, question);
|
|
479
|
+
}
|
|
480
|
+
} catch (error) {
|
|
481
|
+
// If AI analysis fails, use heuristic
|
|
482
|
+
console.log(chalk.yellow(`⚠️ AI analysis failed, using fallback method`));
|
|
483
|
+
qualityMetrics = AnswerQualityAnalyzer.analyze(answer, question);
|
|
484
|
+
}
|
|
276
485
|
|
|
277
486
|
// Show quality feedback
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
487
|
+
if (qualityMetrics.issues && qualityMetrics.issues.length > 0 && qualityMetrics.qualityScore < 70) {
|
|
488
|
+
console.log(chalk.yellow(`\n💡 Quality: ${qualityMetrics.qualityScore}/100`));
|
|
489
|
+
if (qualityMetrics.suggestions && qualityMetrics.suggestions.length > 0) {
|
|
490
|
+
console.log(chalk.gray(` Suggestion: ${qualityMetrics.suggestions[0]}`));
|
|
491
|
+
}
|
|
492
|
+
} else {
|
|
493
|
+
const feedback = AnswerQualityAnalyzer.getFeedback(qualityMetrics);
|
|
494
|
+
if (feedback) {
|
|
495
|
+
console.log(chalk.cyan(`${feedback}`));
|
|
496
|
+
}
|
|
281
497
|
}
|
|
282
498
|
|
|
283
499
|
// Track answer with quality metrics
|
|
284
500
|
await this.progressTracker.answerQuestion(question.id, question.text, answer, qualityMetrics);
|
|
285
501
|
|
|
502
|
+
// Record answer to skip tracker (Phase 4.2)
|
|
503
|
+
if (this.skipTracker) {
|
|
504
|
+
this.skipTracker.recordAnswer(question, answer, {
|
|
505
|
+
qualityScore: qualityMetrics.qualityScore,
|
|
506
|
+
richness: qualityMetrics.isComprehensive ? 'comprehensive' : 'basic'
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
|
|
286
510
|
// Check if answer is comprehensive enough to skip follow-ups
|
|
287
511
|
if (qualityMetrics.canSkipFollowUps) {
|
|
288
512
|
console.log(chalk.green('\n✓ Saved\n'));
|
|
@@ -290,7 +514,26 @@ class Interviewer {
|
|
|
290
514
|
}
|
|
291
515
|
|
|
292
516
|
// Check if answer needs follow-up
|
|
293
|
-
|
|
517
|
+
let followUp = null;
|
|
518
|
+
|
|
519
|
+
// Try AI-generated follow-up first
|
|
520
|
+
if (this.aiClient && qualityMetrics.issues && qualityMetrics.issues.length > 0 && qualityMetrics.qualityScore < 70) {
|
|
521
|
+
try {
|
|
522
|
+
const aiFollowUp = await this.aiClient.generateFollowUp(question.text, answer, qualityMetrics.issues);
|
|
523
|
+
if (aiFollowUp) {
|
|
524
|
+
followUp = {
|
|
525
|
+
message: "Let me ask a more specific question:",
|
|
526
|
+
question: aiFollowUp
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
} catch (error) {
|
|
530
|
+
// If AI fails, use heuristic fallback
|
|
531
|
+
followUp = this.determineFollowUp(question, answer);
|
|
532
|
+
}
|
|
533
|
+
} else {
|
|
534
|
+
// Use heuristic follow-up
|
|
535
|
+
followUp = this.determineFollowUp(question, answer);
|
|
536
|
+
}
|
|
294
537
|
|
|
295
538
|
if (followUp) {
|
|
296
539
|
console.log(chalk.yellow(`\n🤖 ${followUp.message}\n`));
|
|
@@ -308,7 +551,23 @@ class Interviewer {
|
|
|
308
551
|
const combined = `${answer} | Follow-up: ${followUpAnswer}`;
|
|
309
552
|
|
|
310
553
|
// Re-analyze combined answer
|
|
311
|
-
|
|
554
|
+
let combinedMetrics;
|
|
555
|
+
try {
|
|
556
|
+
if (this.aiClient) {
|
|
557
|
+
const aiAnalysis = await this.aiClient.analyzeAnswerQuality(question.text, combined);
|
|
558
|
+
combinedMetrics = {
|
|
559
|
+
wordCount: combined.trim().split(/\s+/).length,
|
|
560
|
+
qualityScore: aiAnalysis.score,
|
|
561
|
+
isComprehensive: aiAnalysis.score >= 70,
|
|
562
|
+
canSkipFollowUps: aiAnalysis.score >= 85
|
|
563
|
+
};
|
|
564
|
+
} else {
|
|
565
|
+
combinedMetrics = AnswerQualityAnalyzer.analyze(combined, question);
|
|
566
|
+
}
|
|
567
|
+
} catch {
|
|
568
|
+
combinedMetrics = AnswerQualityAnalyzer.analyze(combined, question);
|
|
569
|
+
}
|
|
570
|
+
|
|
312
571
|
await this.progressTracker.answerQuestion(question.id, question.text, combined, combinedMetrics);
|
|
313
572
|
|
|
314
573
|
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(),
|