@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.
Files changed (29) hide show
  1. package/.project/chats/{current → complete}/2025-10-03_AI-PROVIDER-INTEGRATION.md +34 -35
  2. package/.project/chats/complete/2025-10-04_CONFIG-COMMAND.md +503 -0
  3. package/.project/chats/complete/2025-10-04_PHASE-4-1-SMART-FILTERING.md +381 -0
  4. package/.project/chats/current/2025-10-04_PHASE-4-2-COMPLETION-AND-ROADMAP.md +344 -0
  5. package/.project/chats/current/2025-10-04_PHASE-4-2-LEARNING-SYSTEM.md +239 -0
  6. package/.project/chats/current/SESSION-STATUS.md +142 -33
  7. package/.project/docs/PHASE-4-2-LEARNING-SYSTEM.md +881 -0
  8. package/.project/docs/ROADMAP.md +540 -0
  9. package/.project/docs/SMART-FILTERING-SYSTEM.md +385 -0
  10. package/.project/docs/goals/PROJECT-VISION.md +63 -11
  11. package/CHANGELOG.md +125 -1
  12. package/README.md +476 -381
  13. package/lib/analyzers/project-analyzer.js +380 -0
  14. package/lib/commands/config.js +68 -1
  15. package/lib/commands/init.js +3 -22
  16. package/lib/filters/question-filter.js +480 -0
  17. package/lib/frameworks/interviewer.js +208 -4
  18. package/lib/learning/learning-manager.js +447 -0
  19. package/lib/learning/pattern-detector.js +376 -0
  20. package/lib/learning/rule-generator.js +304 -0
  21. package/lib/learning/skip-tracker.js +260 -0
  22. package/lib/learning/storage.js +296 -0
  23. package/package.json +70 -69
  24. package/tests/learning-storage.test.js +184 -0
  25. package/tests/pattern-detector.test.js +297 -0
  26. package/tests/project-analyzer.test.js +221 -0
  27. package/tests/question-filter.test.js +297 -0
  28. package/tests/skip-tracker.test.js +198 -0
  29. /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(`✓ AI Provider: ${this.aiConfig.providerName} (${this.aiConfig.model})\n`));
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.red('\n✖ Error: No AI configuration provided. Cannot start interview.\n'));
62
- process.exit(1);
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
- const questions = getQuestionsForFramework(this.framework);
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'));