@iservu-inc/adf-cli 0.7.1 → 0.9.0

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.
@@ -0,0 +1,418 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+ const chalk = require('chalk');
4
+ const inquirer = require('inquirer');
5
+
6
+ /**
7
+ * AI Analysis Configuration Manager
8
+ * Controls performance vs intelligence tradeoffs during interviews
9
+ */
10
+
11
+ const DEFAULT_CONFIG = {
12
+ enabled: true,
13
+ performanceMode: 'balanced', // 'fast', 'balanced', 'comprehensive'
14
+ features: {
15
+ qualityAnalysis: true, // AI-powered answer quality analysis
16
+ intelligentReordering: true, // Dynamic question reordering based on answers
17
+ followUpQuestions: true, // AI-generated follow-up questions
18
+ patternDetection: true, // Learn from answer patterns
19
+ smartFiltering: true // Skip redundant questions intelligently
20
+ }
21
+ };
22
+
23
+ const PERFORMANCE_MODES = {
24
+ fast: {
25
+ name: 'Fast Mode',
26
+ description: 'Minimal AI analysis - faster but less intelligent',
27
+ features: {
28
+ qualityAnalysis: false,
29
+ intelligentReordering: false,
30
+ followUpQuestions: false,
31
+ patternDetection: false,
32
+ smartFiltering: false
33
+ }
34
+ },
35
+ balanced: {
36
+ name: 'Balanced Mode',
37
+ description: 'Moderate AI analysis - good balance of speed and intelligence',
38
+ features: {
39
+ qualityAnalysis: true,
40
+ intelligentReordering: false,
41
+ followUpQuestions: true,
42
+ patternDetection: true,
43
+ smartFiltering: true
44
+ }
45
+ },
46
+ comprehensive: {
47
+ name: 'Comprehensive Mode',
48
+ description: 'Full AI analysis - slower but most intelligent',
49
+ features: {
50
+ qualityAnalysis: true,
51
+ intelligentReordering: true,
52
+ followUpQuestions: true,
53
+ patternDetection: true,
54
+ smartFiltering: true
55
+ }
56
+ }
57
+ };
58
+
59
+ /**
60
+ * Get config file path
61
+ */
62
+ function getAnalysisConfigPath(projectPath = process.cwd()) {
63
+ return path.join(projectPath, '.adf', 'analysis-config.json');
64
+ }
65
+
66
+ /**
67
+ * Load analysis configuration
68
+ */
69
+ async function loadAnalysisConfig(projectPath = process.cwd()) {
70
+ const configPath = getAnalysisConfigPath(projectPath);
71
+
72
+ if (!await fs.pathExists(configPath)) {
73
+ return DEFAULT_CONFIG;
74
+ }
75
+
76
+ try {
77
+ const config = await fs.readJson(configPath);
78
+ // Merge with defaults to ensure all keys exist
79
+ return {
80
+ ...DEFAULT_CONFIG,
81
+ ...config,
82
+ features: {
83
+ ...DEFAULT_CONFIG.features,
84
+ ...(config.features || {})
85
+ }
86
+ };
87
+ } catch (error) {
88
+ console.log(chalk.yellow(`⚠️ Failed to load analysis config: ${error.message}`));
89
+ return DEFAULT_CONFIG;
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Save analysis configuration
95
+ */
96
+ async function saveAnalysisConfig(config, projectPath = process.cwd()) {
97
+ const configPath = getAnalysisConfigPath(projectPath);
98
+
99
+ try {
100
+ await fs.ensureDir(path.dirname(configPath));
101
+ await fs.writeJson(configPath, config, { spaces: 2 });
102
+ return true;
103
+ } catch (error) {
104
+ console.log(chalk.red(`❌ Failed to save analysis config: ${error.message}`));
105
+ return false;
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Get feature status summary
111
+ */
112
+ function getFeatureStatusSummary(config) {
113
+ const features = config.features;
114
+ const enabledCount = Object.values(features).filter(v => v).length;
115
+ const totalCount = Object.keys(features).length;
116
+
117
+ if (enabledCount === totalCount) {
118
+ return chalk.green(`All features enabled (${totalCount}/${totalCount})`);
119
+ } else if (enabledCount === 0) {
120
+ return chalk.red(`All features disabled (0/${totalCount})`);
121
+ } else {
122
+ return chalk.yellow(`${enabledCount}/${totalCount} features enabled`);
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Interactive configuration menu
128
+ */
129
+ async function configureAnalysisSettings(projectPath = process.cwd()) {
130
+ console.log(chalk.cyan.bold('\n🧠 AI Analysis Settings\n'));
131
+ console.log(chalk.gray('Control the balance between performance and intelligence during interviews\n'));
132
+
133
+ // Load current config
134
+ const currentConfig = await loadAnalysisConfig(projectPath);
135
+
136
+ // Main menu
137
+ while (true) {
138
+ console.log(chalk.gray('─'.repeat(60)));
139
+ console.log(chalk.cyan('\n📊 Current Configuration:\n'));
140
+ console.log(chalk.gray('Performance Mode: ') + chalk.white.bold(PERFORMANCE_MODES[currentConfig.performanceMode].name));
141
+ console.log(chalk.gray('Status: ') + getFeatureStatusSummary(currentConfig));
142
+ console.log('');
143
+
144
+ const { action } = await inquirer.prompt([
145
+ {
146
+ type: 'list',
147
+ name: 'action',
148
+ message: 'What would you like to do?',
149
+ choices: [
150
+ {
151
+ name: '🚀 Change Performance Mode (Fast/Balanced/Comprehensive)',
152
+ value: 'mode',
153
+ short: 'Change Mode'
154
+ },
155
+ {
156
+ name: '🎛️ Configure Individual Features',
157
+ value: 'features',
158
+ short: 'Configure Features'
159
+ },
160
+ {
161
+ name: '📖 View Feature Descriptions',
162
+ value: 'info',
163
+ short: 'View Info'
164
+ },
165
+ {
166
+ name: '♻️ Reset to Defaults',
167
+ value: 'reset',
168
+ short: 'Reset'
169
+ },
170
+ new inquirer.Separator(),
171
+ {
172
+ name: chalk.green('✓ Save and Exit'),
173
+ value: 'save',
174
+ short: 'Save'
175
+ },
176
+ {
177
+ name: chalk.gray('← Cancel'),
178
+ value: 'cancel',
179
+ short: 'Cancel'
180
+ }
181
+ ],
182
+ pageSize: 12
183
+ }
184
+ ]);
185
+
186
+ if (action === 'save') {
187
+ const saved = await saveAnalysisConfig(currentConfig, projectPath);
188
+ if (saved) {
189
+ console.log(chalk.green.bold('\n✅ AI Analysis settings saved!\n'));
190
+ console.log(chalk.gray(`Config saved to: ${getAnalysisConfigPath(projectPath)}\n`));
191
+ }
192
+ break;
193
+ } else if (action === 'cancel') {
194
+ console.log(chalk.yellow('\n✋ Changes discarded.\n'));
195
+ break;
196
+ } else if (action === 'mode') {
197
+ await changePerformanceMode(currentConfig);
198
+ } else if (action === 'features') {
199
+ await configureIndividualFeatures(currentConfig);
200
+ } else if (action === 'info') {
201
+ await showFeatureInfo();
202
+ } else if (action === 'reset') {
203
+ const { confirm } = await inquirer.prompt([
204
+ {
205
+ type: 'confirm',
206
+ name: 'confirm',
207
+ message: 'Reset all settings to defaults?',
208
+ default: false
209
+ }
210
+ ]);
211
+
212
+ if (confirm) {
213
+ currentConfig.performanceMode = DEFAULT_CONFIG.performanceMode;
214
+ currentConfig.features = { ...DEFAULT_CONFIG.features };
215
+ console.log(chalk.green('\n✓ Reset to defaults\n'));
216
+ }
217
+ }
218
+ }
219
+ }
220
+
221
+ /**
222
+ * Change performance mode
223
+ */
224
+ async function changePerformanceMode(currentConfig) {
225
+ console.log(chalk.gray('\n' + '─'.repeat(60) + '\n'));
226
+
227
+ const { mode } = await inquirer.prompt([
228
+ {
229
+ type: 'list',
230
+ name: 'mode',
231
+ message: 'Select performance mode:',
232
+ choices: [
233
+ {
234
+ name: `${chalk.cyan('⚡ Fast Mode')} - ${PERFORMANCE_MODES.fast.description}`,
235
+ value: 'fast',
236
+ short: 'Fast'
237
+ },
238
+ {
239
+ name: `${chalk.yellow('⚖️ Balanced Mode')} - ${PERFORMANCE_MODES.balanced.description}`,
240
+ value: 'balanced',
241
+ short: 'Balanced'
242
+ },
243
+ {
244
+ name: `${chalk.green('🎯 Comprehensive Mode')} - ${PERFORMANCE_MODES.comprehensive.description}`,
245
+ value: 'comprehensive',
246
+ short: 'Comprehensive'
247
+ },
248
+ new inquirer.Separator(),
249
+ {
250
+ name: chalk.gray('← Back'),
251
+ value: 'back'
252
+ }
253
+ ]
254
+ }
255
+ ]);
256
+
257
+ if (mode !== 'back') {
258
+ currentConfig.performanceMode = mode;
259
+ currentConfig.features = { ...PERFORMANCE_MODES[mode].features };
260
+ console.log(chalk.green(`\n✓ Mode changed to: ${PERFORMANCE_MODES[mode].name}\n`));
261
+ }
262
+ }
263
+
264
+ /**
265
+ * Configure individual features
266
+ */
267
+ async function configureIndividualFeatures(currentConfig) {
268
+ console.log(chalk.gray('\n' + '─'.repeat(60) + '\n'));
269
+ console.log(chalk.yellow('⚠️ Changing individual features will set mode to "Custom"\n'));
270
+
271
+ const { features } = await inquirer.prompt([
272
+ {
273
+ type: 'checkbox',
274
+ name: 'features',
275
+ message: 'Select features to enable (space to toggle, enter to confirm):',
276
+ choices: [
277
+ {
278
+ name: 'AI-Powered Quality Analysis',
279
+ value: 'qualityAnalysis',
280
+ checked: currentConfig.features.qualityAnalysis
281
+ },
282
+ {
283
+ name: 'Intelligent Question Reordering',
284
+ value: 'intelligentReordering',
285
+ checked: currentConfig.features.intelligentReordering
286
+ },
287
+ {
288
+ name: 'AI-Generated Follow-Up Questions',
289
+ value: 'followUpQuestions',
290
+ checked: currentConfig.features.followUpQuestions
291
+ },
292
+ {
293
+ name: 'Pattern Detection & Learning',
294
+ value: 'patternDetection',
295
+ checked: currentConfig.features.patternDetection
296
+ },
297
+ {
298
+ name: 'Smart Question Filtering',
299
+ value: 'smartFiltering',
300
+ checked: currentConfig.features.smartFiltering
301
+ }
302
+ ]
303
+ }
304
+ ]);
305
+
306
+ // Update features based on selection
307
+ currentConfig.features.qualityAnalysis = features.includes('qualityAnalysis');
308
+ currentConfig.features.intelligentReordering = features.includes('intelligentReordering');
309
+ currentConfig.features.followUpQuestions = features.includes('followUpQuestions');
310
+ currentConfig.features.patternDetection = features.includes('patternDetection');
311
+ currentConfig.features.smartFiltering = features.includes('smartFiltering');
312
+
313
+ // Check if matches any preset mode
314
+ let matchedMode = null;
315
+ for (const [mode, config] of Object.entries(PERFORMANCE_MODES)) {
316
+ if (JSON.stringify(config.features) === JSON.stringify(currentConfig.features)) {
317
+ matchedMode = mode;
318
+ break;
319
+ }
320
+ }
321
+
322
+ if (matchedMode) {
323
+ currentConfig.performanceMode = matchedMode;
324
+ console.log(chalk.green(`\n✓ Features updated (matches ${PERFORMANCE_MODES[matchedMode].name})\n`));
325
+ } else {
326
+ currentConfig.performanceMode = 'custom';
327
+ console.log(chalk.green('\n✓ Features updated (Custom mode)\n'));
328
+ }
329
+ }
330
+
331
+ /**
332
+ * Show feature information
333
+ */
334
+ async function showFeatureInfo() {
335
+ console.log(chalk.gray('\n' + '─'.repeat(60)));
336
+ console.log(chalk.cyan.bold('\n📚 Feature Descriptions\n'));
337
+
338
+ const features = [
339
+ {
340
+ name: chalk.white.bold('AI-Powered Quality Analysis'),
341
+ description: 'Uses AI to analyze your answer quality in real-time',
342
+ impact: 'Provides feedback on answer completeness and suggests improvements',
343
+ performance: chalk.yellow('Medium impact') + ' - adds 1-2 seconds per answer'
344
+ },
345
+ {
346
+ name: chalk.white.bold('Intelligent Question Reordering'),
347
+ description: 'Dynamically reorders upcoming questions based on your answers',
348
+ impact: 'Asks more relevant questions based on what you\'ve already said',
349
+ performance: chalk.red('High impact') + ' - adds 2-3 seconds per answer'
350
+ },
351
+ {
352
+ name: chalk.white.bold('AI-Generated Follow-Up Questions'),
353
+ description: 'Generates contextual follow-up questions when answers need clarification',
354
+ impact: 'Gets more detailed information through targeted follow-ups',
355
+ performance: chalk.yellow('Medium impact') + ' - adds 1-2 seconds when triggered'
356
+ },
357
+ {
358
+ name: chalk.white.bold('Pattern Detection & Learning'),
359
+ description: 'Learns from your answer patterns to improve future interviews',
360
+ impact: 'Remembers your preferences and skips questions you typically skip',
361
+ performance: chalk.green('Low impact') + ' - minimal performance cost'
362
+ },
363
+ {
364
+ name: chalk.white.bold('Smart Question Filtering'),
365
+ description: 'Automatically skips questions already answered elsewhere',
366
+ impact: 'Reduces redundancy by not asking questions you\'ve already covered',
367
+ performance: chalk.green('Low impact') + ' - minimal performance cost'
368
+ }
369
+ ];
370
+
371
+ features.forEach((feature, i) => {
372
+ console.log(chalk.cyan(`${i + 1}. ${feature.name}`));
373
+ console.log(chalk.gray(` ${feature.description}`));
374
+ console.log(chalk.gray(` Impact: ${feature.impact}`));
375
+ console.log(chalk.gray(` Performance: ${feature.performance}`));
376
+ console.log('');
377
+ });
378
+
379
+ console.log(chalk.gray('─'.repeat(60)));
380
+
381
+ await inquirer.prompt([
382
+ {
383
+ type: 'input',
384
+ name: 'continue',
385
+ message: 'Press Enter to continue...',
386
+ default: ''
387
+ }
388
+ ]);
389
+
390
+ console.log('');
391
+ }
392
+
393
+ /**
394
+ * Get feature status for display in config menu
395
+ */
396
+ async function getAnalysisStatus(projectPath = process.cwd()) {
397
+ const config = await loadAnalysisConfig(projectPath);
398
+ const mode = PERFORMANCE_MODES[config.performanceMode];
399
+ const enabledCount = Object.values(config.features).filter(v => v).length;
400
+
401
+ return {
402
+ configured: true,
403
+ mode: config.performanceMode,
404
+ modeName: mode ? mode.name : 'Custom',
405
+ enabledCount: enabledCount,
406
+ totalCount: Object.keys(config.features).length
407
+ };
408
+ }
409
+
410
+ module.exports = {
411
+ loadAnalysisConfig,
412
+ saveAnalysisConfig,
413
+ configureAnalysisSettings,
414
+ getAnalysisStatus,
415
+ getAnalysisConfigPath,
416
+ DEFAULT_CONFIG,
417
+ PERFORMANCE_MODES
418
+ };
@@ -3,6 +3,7 @@ const chalk = require('chalk');
3
3
  const fs = require('fs-extra');
4
4
  const path = require('path');
5
5
  const { configureAIProvider, getEnvFilePath, loadEnvFile } = require('../ai/ai-config');
6
+ const { configureAnalysisSettings, getAnalysisStatus } = require('../ai/analysis-config');
6
7
  const LearningManager = require('../learning/learning-manager');
7
8
  const { getLearningStats, getLearningConfig } = require('../learning/storage');
8
9
 
@@ -15,6 +16,11 @@ const CONFIG_CATEGORIES = {
15
16
  description: 'Configure AI provider (Anthropic, OpenAI, Google Gemini, OpenRouter)',
16
17
  value: 'ai-provider'
17
18
  },
19
+ AI_ANALYSIS: {
20
+ name: 'AI Analysis Settings',
21
+ description: 'Control AI analysis features and performance modes',
22
+ value: 'ai-analysis'
23
+ },
18
24
  IDE_DEPLOYMENT: {
19
25
  name: 'IDE Deployment',
20
26
  description: 'Deploy requirements to IDEs (Windsurf, Cursor, VSCode, etc.)',
@@ -110,6 +116,17 @@ function displayLearningStatus(status) {
110
116
  }
111
117
  }
112
118
 
119
+ /**
120
+ * Display configuration status for AI Analysis
121
+ */
122
+ function displayAnalysisStatus(status) {
123
+ if (status.configured) {
124
+ return `${chalk.green('✓ Configured')} ${chalk.gray(`(${status.modeName}, ${status.enabledCount}/${status.totalCount} features)`)}`;
125
+ } else {
126
+ return chalk.gray('○ Default settings');
127
+ }
128
+ }
129
+
113
130
  /**
114
131
  * Main config command
115
132
  */
@@ -120,6 +137,7 @@ async function config() {
120
137
 
121
138
  // Check configuration status for all categories
122
139
  const aiStatus = await isAIConfigured(cwd);
140
+ const analysisStatus = await getAnalysisStatus(cwd);
123
141
  const deploymentStatus = await getDeploymentStatus(cwd);
124
142
  const learningStatus = await getLearningStatus(cwd);
125
143
 
@@ -130,6 +148,11 @@ async function config() {
130
148
  value: CONFIG_CATEGORIES.AI_PROVIDER.value,
131
149
  short: CONFIG_CATEGORIES.AI_PROVIDER.name
132
150
  },
151
+ {
152
+ name: `${CONFIG_CATEGORIES.AI_ANALYSIS.name} - ${displayAnalysisStatus(analysisStatus)}`,
153
+ value: CONFIG_CATEGORIES.AI_ANALYSIS.value,
154
+ short: CONFIG_CATEGORIES.AI_ANALYSIS.name
155
+ },
133
156
  {
134
157
  name: `${CONFIG_CATEGORIES.IDE_DEPLOYMENT.name} - ${displayDeploymentStatus(deploymentStatus)}`,
135
158
  value: CONFIG_CATEGORIES.IDE_DEPLOYMENT.value,
@@ -168,6 +191,10 @@ async function config() {
168
191
  await configureAIProviderCategory(cwd, aiStatus);
169
192
  break;
170
193
 
194
+ case 'ai-analysis':
195
+ await configureAnalysisSettings(cwd);
196
+ break;
197
+
171
198
  case 'ide-deployment':
172
199
  await configureIDEDeploymentCategory(cwd, deploymentStatus);
173
200
  break;
@@ -63,6 +63,34 @@ class Interviewer {
63
63
  console.log(chalk.gray(`Session: ${this.sessionId}`));
64
64
  console.log(chalk.gray(`Files will be saved to: .adf/sessions/${this.sessionId}/\n`));
65
65
 
66
+ // Load AI Analysis configuration
67
+ const { loadAnalysisConfig } = require('../ai/analysis-config');
68
+ this.analysisConfig = await loadAnalysisConfig(this.projectPath);
69
+
70
+ // Show performance mode
71
+ const modeEmoji = this.analysisConfig.performanceMode === 'fast' ? '⚡' :
72
+ this.analysisConfig.performanceMode === 'comprehensive' ? '🎯' : '⚖️';
73
+ console.log(chalk.gray(`${modeEmoji} Analysis Mode: ${this.analysisConfig.performanceMode}`));
74
+ console.log(chalk.gray(` (Configure with: adf config)\n`));
75
+
76
+ // Setup global Ctrl+C handler for graceful exit from anywhere
77
+ const exitHandler = async () => {
78
+ console.log(chalk.yellow('\n\n💾 Saving progress and exiting...'));
79
+
80
+ // Ensure progress is saved
81
+ if (this.progressTracker) {
82
+ await this.progressTracker.save();
83
+ }
84
+
85
+ console.log(chalk.green('✓ Progress saved!'));
86
+ console.log(chalk.cyan('Resume anytime with: adf init\n'));
87
+ process.exit(0);
88
+ };
89
+
90
+ // Remove any existing SIGINT listeners to avoid duplicates
91
+ process.removeAllListeners('SIGINT');
92
+ process.on('SIGINT', exitHandler);
93
+
66
94
  // Configure AI if not already configured (new sessions only)
67
95
  if (!this.aiConfig && !this.isResuming) {
68
96
  // Check if user already configured AI via 'adf config'
@@ -117,82 +145,101 @@ class Interviewer {
117
145
  }
118
146
  }
119
147
 
120
- // Initialize AI Client
121
- if (!this.aiClient && this.aiConfig) {
148
+ // Initialize AI Client (only if quality analysis or follow-ups are enabled)
149
+ const needsAI = this.analysisConfig.features.qualityAnalysis ||
150
+ this.analysisConfig.features.followUpQuestions ||
151
+ this.analysisConfig.features.intelligentReordering;
152
+
153
+ if (!this.aiClient && this.aiConfig && needsAI) {
122
154
  const AIClient = require('../ai/ai-client');
123
155
  this.aiClient = new AIClient(this.aiConfig);
124
156
  console.log(chalk.green(`\n✓ AI Provider: ${this.aiConfig.providerName} (${this.aiConfig.model})\n`));
125
- } else if (!this.aiConfig) {
126
- console.log(chalk.yellow('⚠️ No AI configuration - continuing without AI assistance\n'));
127
- // Allow interview to continue without AI (graceful degradation)
157
+ } else if (!this.aiConfig && needsAI) {
158
+ console.log(chalk.yellow('⚠️ No AI configuration - AI features disabled\n'));
159
+ // Disable AI-dependent features
160
+ this.analysisConfig.features.qualityAnalysis = false;
161
+ this.analysisConfig.features.followUpQuestions = false;
162
+ this.analysisConfig.features.intelligentReordering = false;
163
+ } else if (!needsAI) {
164
+ console.log(chalk.gray('ℹ️ AI features disabled by configuration\n'));
128
165
  }
129
166
 
130
167
  // Initialize Dynamic Pipeline (Intelligent Question System)
131
- this.dynamicPipeline = new DynamicPipeline(this.sessionPath, this.aiClient, {
132
- enabled: true,
133
- minSkipConfidence: 75,
134
- showAnalysis: true,
135
- verbose: false
136
- });
137
- await this.dynamicPipeline.initialize();
168
+ // Only enable if intelligentReordering is enabled
169
+ if (this.analysisConfig.features.intelligentReordering) {
170
+ this.dynamicPipeline = new DynamicPipeline(this.sessionPath, this.aiClient, {
171
+ enabled: true,
172
+ minSkipConfidence: 75,
173
+ showAnalysis: true,
174
+ verbose: false
175
+ });
176
+ await this.dynamicPipeline.initialize();
177
+ } else {
178
+ // Create disabled pipeline
179
+ this.dynamicPipeline = null;
180
+ }
138
181
 
139
182
  // Create session directory
140
183
  await fs.ensureDir(this.sessionPath);
141
184
  await fs.ensureDir(path.join(this.sessionPath, 'qa-responses'));
142
185
  await fs.ensureDir(path.join(this.sessionPath, 'outputs'));
143
186
 
144
- // Analyze project for smart filtering
145
- const spinner = ora('Analyzing your project for context...').start();
187
+ // Analyze project for smart filtering (only if smart filtering is enabled)
146
188
  let projectContext = null;
147
189
  let enableSmartFiltering = false;
148
190
 
149
- try {
150
- projectContext = await analyzeProject(this.projectPath);
151
- spinner.succeed(chalk.green(`✓ Project analyzed: ${getProjectSummary(projectContext)}`));
152
-
153
- // Initialize skip tracker with project context
154
- this.skipTracker = new SkipTracker(this.projectPath, {
155
- projectType: projectContext.type,
156
- frameworks: projectContext.frameworks,
157
- languages: projectContext.languages
158
- });
191
+ if (this.analysisConfig.features.smartFiltering) {
192
+ const spinner = ora('Analyzing your project for context...').start();
159
193
 
160
- // Load learning configuration and learned rules
161
- const learningConfig = await getLearningConfig(this.projectPath);
162
- if (learningConfig.enabled && learningConfig.applyLearnedFilters) {
163
- this.learnedRules = await getActiveRules(this.projectPath);
164
- }
194
+ try {
195
+ projectContext = await analyzeProject(this.projectPath);
196
+ spinner.succeed(chalk.green(`✓ Project analyzed: ${getProjectSummary(projectContext)}`));
197
+
198
+ // Initialize skip tracker with project context (only if pattern detection enabled)
199
+ if (this.analysisConfig.features.patternDetection) {
200
+ this.skipTracker = new SkipTracker(this.projectPath, {
201
+ projectType: projectContext.type,
202
+ frameworks: projectContext.frameworks,
203
+ languages: projectContext.languages
204
+ });
165
205
 
166
- if (projectContext.confidence >= 50) {
167
- // Show project context
168
- console.log(chalk.gray(`\n📊 Project Context:`));
169
- console.log(chalk.gray(` Type: ${projectContext.type}`));
170
- if (projectContext.frameworks.length > 0) {
171
- console.log(chalk.gray(` Frameworks: ${projectContext.frameworks.join(', ')}`));
172
- }
173
- if (projectContext.languages.length > 0) {
174
- console.log(chalk.gray(` Languages: ${projectContext.languages.join(', ')}`));
206
+ // Load learning configuration and learned rules
207
+ const learningConfig = await getLearningConfig(this.projectPath);
208
+ if (learningConfig.enabled && learningConfig.applyLearnedFilters) {
209
+ this.learnedRules = await getActiveRules(this.projectPath);
210
+ }
175
211
  }
176
- console.log(chalk.gray(` Confidence: ${projectContext.confidence}%`));
177
212
 
178
- // Show learning status
179
- if (this.learnedRules.length > 0) {
180
- console.log(chalk.gray(` Learning: ${this.learnedRules.length} learned rule${this.learnedRules.length > 1 ? 's' : ''} active\n`));
181
- } else {
182
- console.log('');
183
- }
213
+ if (projectContext.confidence >= 50) {
214
+ // Show project context
215
+ console.log(chalk.gray(`\n📊 Project Context:`));
216
+ console.log(chalk.gray(` Type: ${projectContext.type}`));
217
+ if (projectContext.frameworks.length > 0) {
218
+ console.log(chalk.gray(` Frameworks: ${projectContext.frameworks.join(', ')}`));
219
+ }
220
+ if (projectContext.languages.length > 0) {
221
+ console.log(chalk.gray(` Languages: ${projectContext.languages.join(', ')}`));
222
+ }
223
+ console.log(chalk.gray(` Confidence: ${projectContext.confidence}%`));
184
224
 
185
- // Ask user if they want smart filtering
186
- const { useSmartFiltering } = await inquirer.prompt([
187
- {
188
- type: 'confirm',
189
- name: 'useSmartFiltering',
190
- message: 'Enable smart filtering to skip irrelevant questions? (Recommended)',
191
- default: true
225
+ // Show learning status
226
+ if (this.learnedRules && this.learnedRules.length > 0) {
227
+ console.log(chalk.gray(` Learning: ${this.learnedRules.length} learned rule${this.learnedRules.length > 1 ? 's' : ''} active\n`));
228
+ } else {
229
+ console.log('');
192
230
  }
193
- ]);
194
231
 
195
- enableSmartFiltering = useSmartFiltering;
232
+ // Ask user if they want smart filtering
233
+ const { useSmartFiltering } = await inquirer.prompt([
234
+ {
235
+ type: 'confirm',
236
+ name: 'useSmartFiltering',
237
+ message: 'Enable smart filtering to skip irrelevant questions? (Recommended)',
238
+ default: true
239
+ }
240
+ ]);
241
+
242
+ enableSmartFiltering = useSmartFiltering;
196
243
 
197
244
  // If user enables filtering and we have learned rules, ask about applying them
198
245
  if (enableSmartFiltering && this.learnedRules.length > 0) {
@@ -496,6 +543,22 @@ class Interviewer {
496
543
  for (let i = 0; i < block.questions.length; i++) {
497
544
  const question = block.questions[i];
498
545
 
546
+ // Check if this question was already answered (resume functionality)
547
+ if (this.answers[question.id]) {
548
+ const answerText = typeof this.answers[question.id] === 'string'
549
+ ? this.answers[question.id]
550
+ : this.answers[question.id].text;
551
+
552
+ console.log(chalk.cyan(`Question ${i + 1}/${block.questions.length}`) + chalk.gray(` (Block ${currentBlock}/${totalBlocks})`) + '\n');
553
+ console.log(chalk.gray('━'.repeat(60)));
554
+ console.log(chalk.green(`\n✓ Already answered: ${question.text}`));
555
+ console.log(chalk.gray(` Answer: ${answerText.substring(0, 80)}${answerText.length > 80 ? '...' : ''}\n`));
556
+ console.log(chalk.gray('━'.repeat(60)) + '\n');
557
+
558
+ questionsAnswered++;
559
+ continue;
560
+ }
561
+
499
562
  // Check if we should skip this question based on knowledge graph
500
563
  if (this.dynamicPipeline) {
501
564
  const skipCheck = this.dynamicPipeline.shouldSkipQuestion(question);
@@ -567,7 +630,7 @@ class Interviewer {
567
630
 
568
631
  console.log('');
569
632
  console.log(chalk.gray('─'.repeat(60)));
570
- console.log(chalk.yellow('💡 Type "skip" to skip • Type "exit" to save & exit (resume with: adf init)'));
633
+ console.log(chalk.yellow('💡 Type "skip" to skip • Type "exit" or press Ctrl+C to save & exit (resume with: adf init)'));
571
634
  console.log(chalk.gray('─'.repeat(60)));
572
635
 
573
636
  const { answer } = await inquirer.prompt([
@@ -603,10 +666,10 @@ class Interviewer {
603
666
  process.exit(0);
604
667
  }
605
668
 
606
- // Analyze answer quality (use AI if available, fallback to heuristic)
669
+ // Analyze answer quality (use AI if enabled and available, fallback to heuristic)
607
670
  let qualityMetrics;
608
671
  try {
609
- if (this.aiClient) {
672
+ if (this.analysisConfig.features.qualityAnalysis && this.aiClient) {
610
673
  const aiAnalysis = await this.aiClient.analyzeAnswerQuality(question.text, answer);
611
674
  qualityMetrics = {
612
675
  wordCount: answer.trim().split(/\s+/).length,
@@ -627,16 +690,18 @@ class Interviewer {
627
690
  qualityMetrics = AnswerQualityAnalyzer.analyze(answer, question);
628
691
  }
629
692
 
630
- // Show quality feedback
631
- if (qualityMetrics.issues && qualityMetrics.issues.length > 0 && qualityMetrics.qualityScore < 70) {
632
- console.log(chalk.yellow(`\n💡 Quality: ${qualityMetrics.qualityScore}/100`));
633
- if (qualityMetrics.suggestions && qualityMetrics.suggestions.length > 0) {
634
- console.log(chalk.gray(` Suggestion: ${qualityMetrics.suggestions[0]}`));
635
- }
636
- } else {
637
- const feedback = AnswerQualityAnalyzer.getFeedback(qualityMetrics);
638
- if (feedback) {
639
- console.log(chalk.cyan(`${feedback}`));
693
+ // Show quality feedback (only if quality analysis is enabled)
694
+ if (this.analysisConfig.features.qualityAnalysis) {
695
+ if (qualityMetrics.issues && qualityMetrics.issues.length > 0 && qualityMetrics.qualityScore < 70) {
696
+ console.log(chalk.yellow(`\n💡 Quality: ${qualityMetrics.qualityScore}/100`));
697
+ if (qualityMetrics.suggestions && qualityMetrics.suggestions.length > 0) {
698
+ console.log(chalk.gray(` Suggestion: ${qualityMetrics.suggestions[0]}`));
699
+ }
700
+ } else {
701
+ const feedback = AnswerQualityAnalyzer.getFeedback(qualityMetrics);
702
+ if (feedback) {
703
+ console.log(chalk.cyan(`${feedback}`));
704
+ }
640
705
  }
641
706
  }
642
707
 
@@ -662,26 +727,28 @@ class Interviewer {
662
727
  return answer;
663
728
  }
664
729
 
665
- // Check if answer needs follow-up
730
+ // Check if answer needs follow-up (only if follow-up questions are enabled)
666
731
  let followUp = null;
667
732
 
668
- // Try AI-generated follow-up first
669
- if (this.aiClient && qualityMetrics.issues && qualityMetrics.issues.length > 0 && qualityMetrics.qualityScore < 70) {
670
- try {
671
- const aiFollowUp = await this.aiClient.generateFollowUp(question.text, answer, qualityMetrics.issues);
672
- if (aiFollowUp) {
673
- followUp = {
674
- message: "Let me ask a more specific question:",
675
- question: aiFollowUp
676
- };
733
+ if (this.analysisConfig.features.followUpQuestions) {
734
+ // Try AI-generated follow-up first
735
+ if (this.aiClient && qualityMetrics.issues && qualityMetrics.issues.length > 0 && qualityMetrics.qualityScore < 70) {
736
+ try {
737
+ const aiFollowUp = await this.aiClient.generateFollowUp(question.text, answer, qualityMetrics.issues);
738
+ if (aiFollowUp) {
739
+ followUp = {
740
+ message: "Let me ask a more specific question:",
741
+ question: aiFollowUp
742
+ };
743
+ }
744
+ } catch (error) {
745
+ // If AI fails, use heuristic fallback
746
+ followUp = this.determineFollowUp(question, answer);
677
747
  }
678
- } catch (error) {
679
- // If AI fails, use heuristic fallback
748
+ } else {
749
+ // Use heuristic follow-up
680
750
  followUp = this.determineFollowUp(question, answer);
681
751
  }
682
- } else {
683
- // Use heuristic follow-up
684
- followUp = this.determineFollowUp(question, answer);
685
752
  }
686
753
 
687
754
  if (followUp) {
@@ -689,7 +756,7 @@ class Interviewer {
689
756
  console.log(chalk.yellow(` ${followUp.question}\n`));
690
757
 
691
758
  console.log(chalk.gray('─'.repeat(60)));
692
- console.log(chalk.yellow('💡 Type "exit" to save & exit (resume with: adf init)'));
759
+ console.log(chalk.yellow('💡 Type "exit" or press Ctrl+C to save & exit (resume with: adf init)'));
693
760
  console.log(chalk.gray('─'.repeat(60)));
694
761
 
695
762
  const { followUpAnswer } = await inquirer.prompt([
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iservu-inc/adf-cli",
3
- "version": "0.7.1",
3
+ "version": "0.9.0",
4
4
  "description": "CLI tool for AgentDevFramework - AI-assisted development framework with multi-provider AI support",
5
5
  "main": "index.js",
6
6
  "bin": {