@iservu-inc/adf-cli 0.3.6 → 0.4.13
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_AI-PROVIDER-INTEGRATION.md +34 -35
- package/.project/chats/complete/2025-10-04_CONFIG-COMMAND.md +503 -0
- package/.project/chats/complete/2025-10-04_PHASE-4-1-SMART-FILTERING.md +381 -0
- package/.project/chats/current/2025-10-04_PHASE-4-2-COMPLETION-AND-ROADMAP.md +344 -0
- package/.project/chats/current/2025-10-04_PHASE-4-2-LEARNING-SYSTEM.md +239 -0
- package/.project/chats/current/SESSION-STATUS.md +142 -33
- package/.project/docs/PHASE-4-2-LEARNING-SYSTEM.md +881 -0
- package/.project/docs/ROADMAP.md +540 -0
- package/.project/docs/SMART-FILTERING-SYSTEM.md +385 -0
- package/.project/docs/goals/PROJECT-VISION.md +63 -11
- package/CHANGELOG.md +125 -1
- package/README.md +476 -381
- package/lib/analyzers/project-analyzer.js +380 -0
- package/lib/commands/config.js +68 -1
- package/lib/commands/init.js +3 -22
- package/lib/filters/question-filter.js +480 -0
- package/lib/frameworks/interviewer.js +208 -4
- 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 -69
- 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
- /package/.project/chats/{current → complete}/2025-10-03_FRAMEWORK-UPDATE-SYSTEM.md +0 -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
|
|
@@ -38,6 +45,8 @@ class Interviewer {
|
|
|
38
45
|
|
|
39
46
|
this.progressTracker = null;
|
|
40
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()
|
|
41
50
|
}
|
|
42
51
|
|
|
43
52
|
generateSessionId() {
|
|
@@ -52,14 +61,37 @@ class Interviewer {
|
|
|
52
61
|
console.log(chalk.gray(`Session: ${this.sessionId}`));
|
|
53
62
|
console.log(chalk.gray(`Files will be saved to: .adf/sessions/${this.sessionId}/\n`));
|
|
54
63
|
|
|
64
|
+
// Configure AI if not already configured (new sessions only)
|
|
65
|
+
if (!this.aiConfig && !this.isResuming) {
|
|
66
|
+
console.log(chalk.cyan.bold('━'.repeat(60)));
|
|
67
|
+
console.log(chalk.cyan.bold('\n📋 Block 1: AI Configuration\n'));
|
|
68
|
+
console.log(chalk.gray('Configure your AI provider for intelligent follow-up questions\n'));
|
|
69
|
+
|
|
70
|
+
const { configureAI } = await inquirer.prompt([
|
|
71
|
+
{
|
|
72
|
+
type: 'confirm',
|
|
73
|
+
name: 'configureAI',
|
|
74
|
+
message: 'Configure AI provider now? (Recommended for best experience)',
|
|
75
|
+
default: true
|
|
76
|
+
}
|
|
77
|
+
]);
|
|
78
|
+
|
|
79
|
+
if (configureAI) {
|
|
80
|
+
const { configureAIProvider } = require('../ai/ai-config');
|
|
81
|
+
this.aiConfig = await configureAIProvider(this.projectPath);
|
|
82
|
+
} else {
|
|
83
|
+
console.log(chalk.yellow('\n💡 You can configure AI later with: adf config\n'));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
55
87
|
// Initialize AI Client
|
|
56
88
|
if (!this.aiClient && this.aiConfig) {
|
|
57
89
|
const AIClient = require('../ai/ai-client');
|
|
58
90
|
this.aiClient = new AIClient(this.aiConfig);
|
|
59
|
-
console.log(chalk.green(
|
|
91
|
+
console.log(chalk.green(`\n✓ AI Provider: ${this.aiConfig.providerName} (${this.aiConfig.model})\n`));
|
|
60
92
|
} else if (!this.aiConfig) {
|
|
61
|
-
console.log(chalk.
|
|
62
|
-
|
|
93
|
+
console.log(chalk.yellow('⚠️ No AI configuration - continuing without AI assistance\n'));
|
|
94
|
+
// Allow interview to continue without AI (graceful degradation)
|
|
63
95
|
}
|
|
64
96
|
|
|
65
97
|
// Create session directory
|
|
@@ -67,8 +99,141 @@ class Interviewer {
|
|
|
67
99
|
await fs.ensureDir(path.join(this.sessionPath, 'qa-responses'));
|
|
68
100
|
await fs.ensureDir(path.join(this.sessionPath, 'outputs'));
|
|
69
101
|
|
|
102
|
+
// Analyze project for smart filtering
|
|
103
|
+
const spinner = ora('Analyzing your project for context...').start();
|
|
104
|
+
let projectContext = null;
|
|
105
|
+
let enableSmartFiltering = false;
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
projectContext = await analyzeProject(this.projectPath);
|
|
109
|
+
spinner.succeed(chalk.green(`✓ Project analyzed: ${getProjectSummary(projectContext)}`));
|
|
110
|
+
|
|
111
|
+
// Initialize skip tracker with project context
|
|
112
|
+
this.skipTracker = new SkipTracker(this.projectPath, {
|
|
113
|
+
projectType: projectContext.type,
|
|
114
|
+
frameworks: projectContext.frameworks,
|
|
115
|
+
languages: projectContext.languages
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// Load learning configuration and learned rules
|
|
119
|
+
const learningConfig = await getLearningConfig(this.projectPath);
|
|
120
|
+
if (learningConfig.enabled && learningConfig.applyLearnedFilters) {
|
|
121
|
+
this.learnedRules = await getActiveRules(this.projectPath);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (projectContext.confidence >= 50) {
|
|
125
|
+
// Show project context
|
|
126
|
+
console.log(chalk.gray(`\n📊 Project Context:`));
|
|
127
|
+
console.log(chalk.gray(` Type: ${projectContext.type}`));
|
|
128
|
+
if (projectContext.frameworks.length > 0) {
|
|
129
|
+
console.log(chalk.gray(` Frameworks: ${projectContext.frameworks.join(', ')}`));
|
|
130
|
+
}
|
|
131
|
+
if (projectContext.languages.length > 0) {
|
|
132
|
+
console.log(chalk.gray(` Languages: ${projectContext.languages.join(', ')}`));
|
|
133
|
+
}
|
|
134
|
+
console.log(chalk.gray(` Confidence: ${projectContext.confidence}%`));
|
|
135
|
+
|
|
136
|
+
// Show learning status
|
|
137
|
+
if (this.learnedRules.length > 0) {
|
|
138
|
+
console.log(chalk.gray(` Learning: ${this.learnedRules.length} learned rule${this.learnedRules.length > 1 ? 's' : ''} active\n`));
|
|
139
|
+
} else {
|
|
140
|
+
console.log('');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Ask user if they want smart filtering
|
|
144
|
+
const { useSmartFiltering } = await inquirer.prompt([
|
|
145
|
+
{
|
|
146
|
+
type: 'confirm',
|
|
147
|
+
name: 'useSmartFiltering',
|
|
148
|
+
message: 'Enable smart filtering to skip irrelevant questions? (Recommended)',
|
|
149
|
+
default: true
|
|
150
|
+
}
|
|
151
|
+
]);
|
|
152
|
+
|
|
153
|
+
enableSmartFiltering = useSmartFiltering;
|
|
154
|
+
|
|
155
|
+
// If user enables filtering and we have learned rules, ask about applying them
|
|
156
|
+
if (enableSmartFiltering && this.learnedRules.length > 0) {
|
|
157
|
+
const ruleExplanations = getRuleExplanations(this.learnedRules);
|
|
158
|
+
console.log(chalk.cyan('\n🧠 I\'ve learned from your previous sessions:'));
|
|
159
|
+
for (const exp of ruleExplanations.slice(0, 3)) { // Show top 3
|
|
160
|
+
console.log(chalk.gray(` • ${exp.message}`));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const { applyLearning } = await inquirer.prompt([
|
|
164
|
+
{
|
|
165
|
+
type: 'confirm',
|
|
166
|
+
name: 'applyLearning',
|
|
167
|
+
message: 'Apply learned preferences? (You can manage these in: adf config learning)',
|
|
168
|
+
default: true
|
|
169
|
+
}
|
|
170
|
+
]);
|
|
171
|
+
|
|
172
|
+
if (!applyLearning) {
|
|
173
|
+
this.learnedRules = []; // User declined
|
|
174
|
+
console.log(chalk.gray('\n○ Learning disabled for this session\n'));
|
|
175
|
+
} else {
|
|
176
|
+
console.log(chalk.green('\n✓ Learning enabled - applying your preferences\n'));
|
|
177
|
+
}
|
|
178
|
+
} else if (enableSmartFiltering) {
|
|
179
|
+
console.log(chalk.green('\n✓ Smart filtering enabled - I\'ll focus on relevant questions\n'));
|
|
180
|
+
} else {
|
|
181
|
+
console.log(chalk.gray('\n○ Smart filtering disabled - I\'ll ask all questions\n'));
|
|
182
|
+
}
|
|
183
|
+
} else {
|
|
184
|
+
spinner.warn(chalk.yellow(`Project analysis incomplete (${projectContext.confidence}% confidence) - asking all questions`));
|
|
185
|
+
console.log('');
|
|
186
|
+
}
|
|
187
|
+
} catch (error) {
|
|
188
|
+
spinner.fail(chalk.yellow('Could not analyze project - asking all questions'));
|
|
189
|
+
console.log('');
|
|
190
|
+
projectContext = { type: 'unknown', frameworks: [], languages: [], confidence: 0 };
|
|
191
|
+
|
|
192
|
+
// Still initialize skip tracker for basic tracking
|
|
193
|
+
this.skipTracker = new SkipTracker(this.projectPath, {
|
|
194
|
+
projectType: 'unknown',
|
|
195
|
+
frameworks: [],
|
|
196
|
+
languages: []
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
70
200
|
// Get questions for framework
|
|
71
|
-
|
|
201
|
+
let questions = getQuestionsForFramework(this.framework);
|
|
202
|
+
|
|
203
|
+
// Apply smart filtering if enabled
|
|
204
|
+
let filteringResult = null;
|
|
205
|
+
if (enableSmartFiltering && projectContext.confidence >= 50) {
|
|
206
|
+
filteringResult = filterQuestions(questions, projectContext, {
|
|
207
|
+
enableSmartFiltering: true,
|
|
208
|
+
minRelevanceScore: 50,
|
|
209
|
+
aiClient: this.aiClient,
|
|
210
|
+
learnedRules: this.learnedRules // Pass learned rules
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
questions = filteringResult.questions.map(q => ({
|
|
214
|
+
...q,
|
|
215
|
+
relevance: q.relevance // Keep relevance score
|
|
216
|
+
}));
|
|
217
|
+
|
|
218
|
+
// Record filtered questions to skip tracker
|
|
219
|
+
if (this.skipTracker && filteringResult.skipped.length > 0) {
|
|
220
|
+
this.skipTracker.recordFilteredQuestions(filteringResult.skipped);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Show filtering summary
|
|
224
|
+
if (filteringResult.summary.skipped > 0) {
|
|
225
|
+
console.log(chalk.cyan(`🎯 Smart Filtering Results:`));
|
|
226
|
+
console.log(chalk.green(` ✓ ${filteringResult.summary.kept} relevant questions selected`));
|
|
227
|
+
console.log(chalk.gray(` ○ ${filteringResult.summary.skipped} questions skipped`));
|
|
228
|
+
|
|
229
|
+
// Show learning-based skips if any
|
|
230
|
+
if (filteringResult.summary.learningActive && filteringResult.summary.skippedByLearning > 0) {
|
|
231
|
+
console.log(chalk.magenta(` 🧠 ${filteringResult.summary.skippedByLearning} skipped based on your learning history`));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
console.log(chalk.blue(` ⏱️ Estimated time saved: ~${filteringResult.summary.timeSaved} minutes\n`));
|
|
235
|
+
}
|
|
236
|
+
}
|
|
72
237
|
|
|
73
238
|
// Group questions into blocks by phase
|
|
74
239
|
const questionBlocks = this.groupQuestionsIntoBlocks(questions);
|
|
@@ -133,6 +298,28 @@ class Interviewer {
|
|
|
133
298
|
// Save session metadata
|
|
134
299
|
await this.saveMetadata();
|
|
135
300
|
|
|
301
|
+
// Save learning data and update patterns (Phase 4.2)
|
|
302
|
+
if (this.skipTracker) {
|
|
303
|
+
try {
|
|
304
|
+
// Save skip tracker session
|
|
305
|
+
await this.skipTracker.saveSession();
|
|
306
|
+
|
|
307
|
+
// Detect patterns and update learned rules
|
|
308
|
+
const learningConfig = await getLearningConfig(this.projectPath);
|
|
309
|
+
if (learningConfig.enabled && learningConfig.detectPatterns) {
|
|
310
|
+
const patterns = await detectPatterns(this.projectPath);
|
|
311
|
+
const updateResult = await updateLearnedRules(this.projectPath, patterns);
|
|
312
|
+
|
|
313
|
+
if (updateResult.added > 0) {
|
|
314
|
+
console.log(chalk.cyan(`\n🧠 Learning System: Detected ${updateResult.added} new pattern${updateResult.added > 1 ? 's' : ''}`));
|
|
315
|
+
console.log(chalk.gray(` View with: adf config learning\n`));
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
} catch (error) {
|
|
319
|
+
console.warn(chalk.yellow('⚠️ Could not save learning data:', error.message));
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
136
323
|
// Mark session as complete
|
|
137
324
|
await this.progressTracker.complete();
|
|
138
325
|
|
|
@@ -267,6 +454,11 @@ class Interviewer {
|
|
|
267
454
|
console.log(chalk.red(` ✗ Bad: ${question.badExample}`));
|
|
268
455
|
console.log(chalk.gray(` (Type "skip" to skip remaining questions in this block)\n`));
|
|
269
456
|
|
|
457
|
+
// Start tracking time for this question
|
|
458
|
+
if (this.skipTracker) {
|
|
459
|
+
this.skipTracker.startQuestion(question.id);
|
|
460
|
+
}
|
|
461
|
+
|
|
270
462
|
// Get answer
|
|
271
463
|
const { answer } = await inquirer.prompt([
|
|
272
464
|
{
|
|
@@ -283,6 +475,10 @@ class Interviewer {
|
|
|
283
475
|
]);
|
|
284
476
|
|
|
285
477
|
if (answer.toLowerCase() === 'skip') {
|
|
478
|
+
// Record skip event
|
|
479
|
+
if (this.skipTracker) {
|
|
480
|
+
this.skipTracker.recordSkip(question, 'manual');
|
|
481
|
+
}
|
|
286
482
|
return null; // Signal to skip remaining questions
|
|
287
483
|
}
|
|
288
484
|
|
|
@@ -326,6 +522,14 @@ class Interviewer {
|
|
|
326
522
|
// Track answer with quality metrics
|
|
327
523
|
await this.progressTracker.answerQuestion(question.id, question.text, answer, qualityMetrics);
|
|
328
524
|
|
|
525
|
+
// Record answer to skip tracker (Phase 4.2)
|
|
526
|
+
if (this.skipTracker) {
|
|
527
|
+
this.skipTracker.recordAnswer(question, answer, {
|
|
528
|
+
qualityScore: qualityMetrics.qualityScore,
|
|
529
|
+
richness: qualityMetrics.isComprehensive ? 'comprehensive' : 'basic'
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
|
|
329
533
|
// Check if answer is comprehensive enough to skip follow-ups
|
|
330
534
|
if (qualityMetrics.canSkipFollowUps) {
|
|
331
535
|
console.log(chalk.green('\n✓ Saved\n'));
|