@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.
Files changed (36) hide show
  1. package/.project/chats/{current → complete}/2025-10-03_AGENTS-MD-AND-TOOL-GENERATORS.md +82 -17
  2. package/.project/chats/complete/2025-10-03_AI-PROVIDER-INTEGRATION.md +568 -0
  3. package/.project/chats/complete/2025-10-03_FRAMEWORK-UPDATE-SYSTEM.md +497 -0
  4. package/.project/chats/complete/2025-10-04_CONFIG-COMMAND.md +503 -0
  5. package/.project/chats/current/2025-10-04_PHASE-4-1-SMART-FILTERING.md +381 -0
  6. package/.project/chats/current/SESSION-STATUS.md +168 -0
  7. package/.project/docs/AI-PROVIDER-INTEGRATION.md +600 -0
  8. package/.project/docs/FRAMEWORK-UPDATE-INTEGRATION.md +421 -0
  9. package/.project/docs/FRAMEWORK-UPDATE-SYSTEM.md +832 -0
  10. package/.project/docs/PHASE-4-2-LEARNING-SYSTEM.md +881 -0
  11. package/.project/docs/PROJECT-STRUCTURE-EXPLANATION.md +500 -0
  12. package/.project/docs/SMART-FILTERING-SYSTEM.md +385 -0
  13. package/.project/docs/architecture/SYSTEM-DESIGN.md +122 -1
  14. package/.project/docs/goals/PROJECT-VISION.md +61 -34
  15. package/CHANGELOG.md +257 -1
  16. package/README.md +476 -292
  17. package/bin/adf.js +7 -0
  18. package/lib/ai/ai-client.js +328 -0
  19. package/lib/ai/ai-config.js +398 -0
  20. package/lib/analyzers/project-analyzer.js +380 -0
  21. package/lib/commands/config.js +221 -0
  22. package/lib/commands/init.js +56 -10
  23. package/lib/filters/question-filter.js +480 -0
  24. package/lib/frameworks/interviewer.js +271 -12
  25. package/lib/frameworks/progress-tracker.js +8 -1
  26. package/lib/learning/learning-manager.js +447 -0
  27. package/lib/learning/pattern-detector.js +376 -0
  28. package/lib/learning/rule-generator.js +304 -0
  29. package/lib/learning/skip-tracker.js +260 -0
  30. package/lib/learning/storage.js +296 -0
  31. package/package.json +70 -57
  32. package/tests/learning-storage.test.js +184 -0
  33. package/tests/pattern-detector.test.js +297 -0
  34. package/tests/project-analyzer.test.js +221 -0
  35. package/tests/question-filter.test.js +297 -0
  36. package/tests/skip-tracker.test.js +198 -0
@@ -0,0 +1,380 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+ const chalk = require('chalk');
4
+
5
+ /**
6
+ * Project Analyzer - Analyzes existing project files for context
7
+ *
8
+ * This module provides intelligent project analysis to:
9
+ * - Detect project type (web app, CLI, library, API, etc.)
10
+ * - Identify tech stack and frameworks
11
+ * - Extract project metadata
12
+ * - Provide context for smart question filtering
13
+ */
14
+
15
+ /**
16
+ * Project types that can be detected
17
+ */
18
+ const PROJECT_TYPES = {
19
+ WEB_APP: 'web-app',
20
+ API_SERVER: 'api-server',
21
+ CLI_TOOL: 'cli-tool',
22
+ LIBRARY: 'library',
23
+ MOBILE_APP: 'mobile-app',
24
+ DESKTOP_APP: 'desktop-app',
25
+ FULLSTACK: 'fullstack',
26
+ MICROSERVICE: 'microservice',
27
+ UNKNOWN: 'unknown'
28
+ };
29
+
30
+ /**
31
+ * Analyze project and extract context
32
+ * @param {string} projectPath - Path to project root
33
+ * @returns {Promise<Object>} Project context object
34
+ */
35
+ async function analyzeProject(projectPath) {
36
+ const context = {
37
+ type: PROJECT_TYPES.UNKNOWN,
38
+ subtype: null,
39
+ frameworks: [],
40
+ languages: [],
41
+ dependencies: {},
42
+ devDependencies: {},
43
+ scripts: {},
44
+ hasTests: false,
45
+ hasCI: false,
46
+ hasDocker: false,
47
+ teamSize: 'unknown',
48
+ description: null,
49
+ confidence: 0
50
+ };
51
+
52
+ // Analyze package.json if exists (Node.js project)
53
+ const packageJsonPath = path.join(projectPath, 'package.json');
54
+ if (await fs.pathExists(packageJsonPath)) {
55
+ const packageData = await analyzePackageJson(packageJsonPath);
56
+ Object.assign(context, packageData);
57
+ context.languages.push('JavaScript/TypeScript');
58
+ }
59
+
60
+ // Analyze Python projects
61
+ const requirementsTxt = path.join(projectPath, 'requirements.txt');
62
+ const pipfile = path.join(projectPath, 'Pipfile');
63
+ if (await fs.pathExists(requirementsTxt) || await fs.pathExists(pipfile)) {
64
+ const pythonData = await analyzePythonProject(projectPath);
65
+ Object.assign(context, pythonData);
66
+ context.languages.push('Python');
67
+ }
68
+
69
+ // Analyze file structure
70
+ const fileStructure = await analyzeFileStructure(projectPath);
71
+ context.hasTests = context.hasTests || fileStructure.hasTests; // Preserve if already detected from package.json
72
+ context.hasCI = fileStructure.hasCI;
73
+ context.hasDocker = fileStructure.hasDocker;
74
+
75
+ // Try to read README for description
76
+ const readmePath = await findReadme(projectPath);
77
+ if (readmePath) {
78
+ context.description = await extractDescriptionFromReadme(readmePath);
79
+ }
80
+
81
+ // Detect project type if still unknown
82
+ if (context.type === PROJECT_TYPES.UNKNOWN) {
83
+ context.type = inferProjectType(context);
84
+ }
85
+
86
+ // Calculate confidence score (0-100)
87
+ context.confidence = calculateConfidence(context);
88
+
89
+ return context;
90
+ }
91
+
92
+ /**
93
+ * Analyze package.json for Node.js projects
94
+ */
95
+ async function analyzePackageJson(packageJsonPath) {
96
+ try {
97
+ const packageJson = await fs.readJSON(packageJsonPath);
98
+ const context = {
99
+ dependencies: packageJson.dependencies || {},
100
+ devDependencies: packageJson.devDependencies || {},
101
+ scripts: packageJson.scripts || {},
102
+ frameworks: [],
103
+ type: PROJECT_TYPES.UNKNOWN
104
+ };
105
+
106
+ // Detect frameworks
107
+ const deps = { ...context.dependencies, ...context.devDependencies };
108
+
109
+ // Frontend frameworks
110
+ if (deps.react) context.frameworks.push('React');
111
+ if (deps.vue) context.frameworks.push('Vue');
112
+ if (deps.angular || deps['@angular/core']) context.frameworks.push('Angular');
113
+ if (deps.svelte) context.frameworks.push('Svelte');
114
+ if (deps.next) context.frameworks.push('Next.js');
115
+ if (deps.nuxt) context.frameworks.push('Nuxt');
116
+ if (deps.gatsby) context.frameworks.push('Gatsby');
117
+
118
+ // Backend frameworks
119
+ if (deps.express) context.frameworks.push('Express');
120
+ if (deps.koa) context.frameworks.push('Koa');
121
+ if (deps.fastify) context.frameworks.push('Fastify');
122
+ if (deps.hapi || deps['@hapi/hapi']) context.frameworks.push('Hapi');
123
+ if (deps.nestjs || deps['@nestjs/core']) context.frameworks.push('NestJS');
124
+
125
+ // Testing frameworks
126
+ if (deps.jest || deps.mocha || deps.jasmine || deps.vitest) {
127
+ context.hasTests = true;
128
+ }
129
+
130
+ // Detect project type from package.json
131
+ if (packageJson.bin) {
132
+ context.type = PROJECT_TYPES.CLI_TOOL;
133
+ context.subtype = 'cli';
134
+ } else if (packageJson.main && !packageJson.private) {
135
+ context.type = PROJECT_TYPES.LIBRARY;
136
+ context.subtype = 'npm-package';
137
+ } else if (context.frameworks.some(f => ['React', 'Vue', 'Angular', 'Svelte'].includes(f))) {
138
+ if (context.frameworks.some(f => ['Express', 'NestJS', 'Fastify'].includes(f))) {
139
+ context.type = PROJECT_TYPES.FULLSTACK;
140
+ context.subtype = 'frontend-backend';
141
+ } else {
142
+ context.type = PROJECT_TYPES.WEB_APP;
143
+ context.subtype = 'frontend';
144
+ }
145
+ } else if (context.frameworks.some(f => ['Express', 'NestJS', 'Fastify', 'Koa', 'Hapi'].includes(f))) {
146
+ // Check if it's an API server
147
+ if (deps.cors || packageJson.name?.includes('api') || packageJson.description?.toLowerCase().includes('api')) {
148
+ context.type = PROJECT_TYPES.API_SERVER;
149
+ context.subtype = 'rest-api';
150
+ } else {
151
+ context.type = PROJECT_TYPES.WEB_APP;
152
+ context.subtype = 'backend';
153
+ }
154
+ }
155
+
156
+ context.description = packageJson.description || null;
157
+
158
+ return context;
159
+ } catch (error) {
160
+ console.warn(chalk.yellow(`Warning: Could not parse package.json: ${error.message}`));
161
+ return {};
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Analyze Python project
167
+ */
168
+ async function analyzePythonProject(projectPath) {
169
+ const context = {
170
+ frameworks: [],
171
+ type: PROJECT_TYPES.UNKNOWN
172
+ };
173
+
174
+ // Check for common Python frameworks
175
+ const requirementsTxt = path.join(projectPath, 'requirements.txt');
176
+ if (await fs.pathExists(requirementsTxt)) {
177
+ const requirements = await fs.readFile(requirementsTxt, 'utf-8');
178
+
179
+ if (requirements.includes('django')) context.frameworks.push('Django');
180
+ if (requirements.includes('flask')) context.frameworks.push('Flask');
181
+ if (requirements.includes('fastapi')) context.frameworks.push('FastAPI');
182
+ if (requirements.includes('streamlit')) context.frameworks.push('Streamlit');
183
+
184
+ // Detect type
185
+ if (context.frameworks.some(f => ['Django', 'Flask', 'FastAPI'].includes(f))) {
186
+ context.type = PROJECT_TYPES.WEB_APP;
187
+ context.subtype = 'python-web';
188
+ }
189
+ }
190
+
191
+ return context;
192
+ }
193
+
194
+ /**
195
+ * Analyze file structure for indicators
196
+ */
197
+ async function analyzeFileStructure(projectPath) {
198
+ const structure = {
199
+ hasTests: false,
200
+ hasCI: false,
201
+ hasDocker: false
202
+ };
203
+
204
+ try {
205
+ const files = await fs.readdir(projectPath);
206
+
207
+ // Check for test directories
208
+ if (files.includes('test') || files.includes('tests') || files.includes('__tests__') || files.includes('spec')) {
209
+ structure.hasTests = true;
210
+ }
211
+
212
+ // Check for CI configuration
213
+ if (files.includes('.github') || files.includes('.gitlab-ci.yml') || files.includes('.travis.yml') || files.includes('.circleci')) {
214
+ structure.hasCI = true;
215
+ }
216
+
217
+ // Check for Docker
218
+ if (files.includes('Dockerfile') || files.includes('docker-compose.yml')) {
219
+ structure.hasDocker = true;
220
+ }
221
+ } catch (error) {
222
+ // Silently fail - not critical
223
+ }
224
+
225
+ return structure;
226
+ }
227
+
228
+ /**
229
+ * Find README file (case-insensitive)
230
+ */
231
+ async function findReadme(projectPath) {
232
+ const readmeNames = ['README.md', 'readme.md', 'README.MD', 'README', 'Readme.md'];
233
+
234
+ for (const name of readmeNames) {
235
+ const readmePath = path.join(projectPath, name);
236
+ if (await fs.pathExists(readmePath)) {
237
+ return readmePath;
238
+ }
239
+ }
240
+
241
+ return null;
242
+ }
243
+
244
+ /**
245
+ * Extract description from README (first paragraph)
246
+ */
247
+ async function extractDescriptionFromReadme(readmePath) {
248
+ try {
249
+ const content = await fs.readFile(readmePath, 'utf-8');
250
+
251
+ // Remove title (first # line)
252
+ const lines = content.split('\n');
253
+ let description = '';
254
+ let foundTitle = false;
255
+
256
+ for (const line of lines) {
257
+ const trimmed = line.trim();
258
+
259
+ // Skip title
260
+ if (!foundTitle && trimmed.startsWith('#')) {
261
+ foundTitle = true;
262
+ continue;
263
+ }
264
+
265
+ // Skip empty lines
266
+ if (!trimmed) continue;
267
+
268
+ // Stop at next heading or code block
269
+ if (trimmed.startsWith('#') || trimmed.startsWith('```')) break;
270
+
271
+ // Add to description
272
+ description += trimmed + ' ';
273
+
274
+ // Stop after first paragraph (roughly 200 chars)
275
+ if (description.length > 200) break;
276
+ }
277
+
278
+ return description.trim().substring(0, 300) || null;
279
+ } catch (error) {
280
+ return null;
281
+ }
282
+ }
283
+
284
+ /**
285
+ * Infer project type from context when not explicitly detected
286
+ */
287
+ function inferProjectType(context) {
288
+ // If we have frameworks, make educated guess
289
+ if (context.frameworks.length > 0) {
290
+ const frontend = ['React', 'Vue', 'Angular', 'Svelte', 'Next.js', 'Nuxt', 'Gatsby'];
291
+ const backend = ['Express', 'NestJS', 'Fastify', 'Koa', 'Django', 'Flask', 'FastAPI'];
292
+
293
+ const hasFrontend = context.frameworks.some(f => frontend.includes(f));
294
+ const hasBackend = context.frameworks.some(f => backend.includes(f));
295
+
296
+ if (hasFrontend && hasBackend) return PROJECT_TYPES.FULLSTACK;
297
+ if (hasFrontend) return PROJECT_TYPES.WEB_APP;
298
+ if (hasBackend) return PROJECT_TYPES.API_SERVER;
299
+ }
300
+
301
+ // Check description for hints
302
+ if (context.description) {
303
+ const desc = context.description.toLowerCase();
304
+ if (desc.includes('api') || desc.includes('server')) return PROJECT_TYPES.API_SERVER;
305
+ if (desc.includes('cli') || desc.includes('command-line')) return PROJECT_TYPES.CLI_TOOL;
306
+ if (desc.includes('library') || desc.includes('package')) return PROJECT_TYPES.LIBRARY;
307
+ if (desc.includes('web') || desc.includes('app')) return PROJECT_TYPES.WEB_APP;
308
+ }
309
+
310
+ return PROJECT_TYPES.UNKNOWN;
311
+ }
312
+
313
+ /**
314
+ * Calculate confidence score (0-100) for the analysis
315
+ */
316
+ function calculateConfidence(context) {
317
+ let score = 0;
318
+
319
+ // Type detection (+30)
320
+ if (context.type !== PROJECT_TYPES.UNKNOWN) score += 30;
321
+
322
+ // Framework detection (+20)
323
+ if (context.frameworks.length > 0) score += 20;
324
+
325
+ // Language detection (+15)
326
+ if (context.languages.length > 0) score += 15;
327
+
328
+ // Dependencies (+10)
329
+ if (Object.keys(context.dependencies).length > 0) score += 10;
330
+
331
+ // Description (+15)
332
+ if (context.description) score += 15;
333
+
334
+ // Additional indicators (+10)
335
+ if (context.hasTests) score += 3;
336
+ if (context.hasCI) score += 3;
337
+ if (context.hasDocker) score += 4;
338
+
339
+ return Math.min(score, 100);
340
+ }
341
+
342
+ /**
343
+ * Get human-readable project summary
344
+ */
345
+ function getProjectSummary(context) {
346
+ const parts = [];
347
+
348
+ // Type
349
+ const typeNames = {
350
+ [PROJECT_TYPES.WEB_APP]: 'Web Application',
351
+ [PROJECT_TYPES.API_SERVER]: 'API Server',
352
+ [PROJECT_TYPES.CLI_TOOL]: 'CLI Tool',
353
+ [PROJECT_TYPES.LIBRARY]: 'Library/Package',
354
+ [PROJECT_TYPES.MOBILE_APP]: 'Mobile App',
355
+ [PROJECT_TYPES.DESKTOP_APP]: 'Desktop App',
356
+ [PROJECT_TYPES.FULLSTACK]: 'Full-stack Application',
357
+ [PROJECT_TYPES.MICROSERVICE]: 'Microservice',
358
+ [PROJECT_TYPES.UNKNOWN]: 'Unknown Type'
359
+ };
360
+
361
+ parts.push(typeNames[context.type] || 'Unknown');
362
+
363
+ // Frameworks
364
+ if (context.frameworks.length > 0) {
365
+ parts.push(`using ${context.frameworks.join(', ')}`);
366
+ }
367
+
368
+ // Languages
369
+ if (context.languages.length > 0) {
370
+ parts.push(`(${context.languages.join(', ')})`);
371
+ }
372
+
373
+ return parts.join(' ');
374
+ }
375
+
376
+ module.exports = {
377
+ analyzeProject,
378
+ getProjectSummary,
379
+ PROJECT_TYPES
380
+ };
@@ -0,0 +1,221 @@
1
+ const inquirer = require('inquirer');
2
+ const chalk = require('chalk');
3
+ const fs = require('fs-extra');
4
+ const path = require('path');
5
+ const { configureAIProvider, getEnvFilePath, loadEnvFile } = require('../ai/ai-config');
6
+ const LearningManager = require('../learning/learning-manager');
7
+ const { getLearningStats, getLearningConfig } = require('../learning/storage');
8
+
9
+ /**
10
+ * Configuration categories available in ADF CLI
11
+ */
12
+ const CONFIG_CATEGORIES = {
13
+ AI_PROVIDER: {
14
+ name: 'AI Provider Setup',
15
+ description: 'Configure AI provider (Anthropic, OpenAI, Google Gemini, OpenRouter)',
16
+ value: 'ai-provider'
17
+ },
18
+ LEARNING_SYSTEM: {
19
+ name: 'Learning System',
20
+ description: 'Manage interview learning data and preferences',
21
+ value: 'learning'
22
+ }
23
+ // Future config categories can be added here:
24
+ // PROJECT_SETTINGS: { name: 'Project Settings', description: '...', value: 'project' },
25
+ // DEPLOYMENT: { name: 'Deployment Preferences', description: '...', value: 'deployment' },
26
+ };
27
+
28
+ /**
29
+ * Check if AI provider is already configured
30
+ */
31
+ async function isAIConfigured(projectPath = process.cwd()) {
32
+ const envPath = getEnvFilePath(projectPath);
33
+
34
+ if (!await fs.pathExists(envPath)) {
35
+ return { configured: false };
36
+ }
37
+
38
+ const envVars = await loadEnvFile(envPath);
39
+
40
+ // Check if any AI provider key exists
41
+ const aiKeys = [
42
+ 'ANTHROPIC_API_KEY',
43
+ 'OPENAI_API_KEY',
44
+ 'GOOGLE_API_KEY',
45
+ 'OPENROUTER_API_KEY'
46
+ ];
47
+
48
+ for (const key of aiKeys) {
49
+ if (envVars[key] && envVars[key].length > 0) {
50
+ return {
51
+ configured: true,
52
+ provider: key.replace('_API_KEY', '').toLowerCase()
53
+ };
54
+ }
55
+ }
56
+
57
+ return { configured: false };
58
+ }
59
+
60
+ /**
61
+ * Check if Learning System has data
62
+ */
63
+ async function getLearningStatus(projectPath = process.cwd()) {
64
+ try {
65
+ const stats = await getLearningStats(projectPath);
66
+ const config = await getLearningConfig(projectPath);
67
+
68
+ return {
69
+ hasData: stats.totalSessions > 0,
70
+ enabled: config.enabled,
71
+ totalSessions: stats.totalSessions,
72
+ totalPatterns: stats.totalPatterns
73
+ };
74
+ } catch (error) {
75
+ return {
76
+ hasData: false,
77
+ enabled: true,
78
+ totalSessions: 0,
79
+ totalPatterns: 0
80
+ };
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Display configuration status for AI provider
86
+ */
87
+ function displayAIStatus(status) {
88
+ if (status.configured) {
89
+ const providerName = status.provider.charAt(0).toUpperCase() + status.provider.slice(1);
90
+ return `${chalk.green('✓ Configured')} ${chalk.gray(`(${providerName})`)}`;
91
+ } else {
92
+ return chalk.yellow('○ Not configured');
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Display configuration status for Learning System
98
+ */
99
+ function displayLearningStatus(status) {
100
+ if (status.hasData && status.enabled) {
101
+ return `${chalk.green('✓ Active')} ${chalk.gray(`(${status.totalSessions} sessions, ${status.totalPatterns} patterns)`)}`;
102
+ } else if (status.hasData && !status.enabled) {
103
+ return `${chalk.yellow('○ Disabled')} ${chalk.gray(`(${status.totalSessions} sessions)`)}`;
104
+ } else if (!status.hasData && status.enabled) {
105
+ return chalk.gray('○ No data yet');
106
+ } else {
107
+ return chalk.gray('○ Not configured');
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Main config command
113
+ */
114
+ async function config() {
115
+ console.log(chalk.cyan.bold('\n⚙️ ADF Configuration\n'));
116
+
117
+ const cwd = process.cwd();
118
+
119
+ // Check configuration status for all categories
120
+ const aiStatus = await isAIConfigured(cwd);
121
+ const learningStatus = await getLearningStatus(cwd);
122
+
123
+ // Build choices with status indicators
124
+ const choices = [
125
+ {
126
+ name: `${CONFIG_CATEGORIES.AI_PROVIDER.name} - ${displayAIStatus(aiStatus)}`,
127
+ value: CONFIG_CATEGORIES.AI_PROVIDER.value,
128
+ short: CONFIG_CATEGORIES.AI_PROVIDER.name
129
+ },
130
+ {
131
+ name: `${CONFIG_CATEGORIES.LEARNING_SYSTEM.name} - ${displayLearningStatus(learningStatus)}`,
132
+ value: CONFIG_CATEGORIES.LEARNING_SYSTEM.value,
133
+ short: CONFIG_CATEGORIES.LEARNING_SYSTEM.name
134
+ },
135
+ new inquirer.Separator(),
136
+ {
137
+ name: chalk.gray('← Back'),
138
+ value: 'back'
139
+ }
140
+ ];
141
+
142
+ const { category } = await inquirer.prompt([
143
+ {
144
+ type: 'list',
145
+ name: 'category',
146
+ message: 'Select configuration category:',
147
+ choices: choices,
148
+ pageSize: 10
149
+ }
150
+ ]);
151
+
152
+ if (category === 'back') {
153
+ console.log(chalk.yellow('\n✋ Configuration cancelled.\n'));
154
+ return;
155
+ }
156
+
157
+ // Handle selected category
158
+ switch (category) {
159
+ case 'ai-provider':
160
+ await configureAIProviderCategory(cwd, aiStatus);
161
+ break;
162
+
163
+ case 'learning':
164
+ await configureLearningCategory(cwd, learningStatus);
165
+ break;
166
+
167
+ // Future categories will be handled here
168
+ default:
169
+ console.log(chalk.red('\n❌ Configuration category not implemented yet.\n'));
170
+ }
171
+ }
172
+
173
+ /**
174
+ * Configure AI Provider category
175
+ */
176
+ async function configureAIProviderCategory(cwd, aiStatus) {
177
+ console.log(chalk.gray('\n' + '─'.repeat(60) + '\n'));
178
+
179
+ if (aiStatus.configured) {
180
+ const providerName = aiStatus.provider.charAt(0).toUpperCase() + aiStatus.provider.slice(1);
181
+ console.log(chalk.green(`✓ AI Provider already configured: ${providerName}\n`));
182
+
183
+ const { reconfigure } = await inquirer.prompt([
184
+ {
185
+ type: 'confirm',
186
+ name: 'reconfigure',
187
+ message: 'Do you want to reconfigure your AI provider?',
188
+ default: false
189
+ }
190
+ ]);
191
+
192
+ if (!reconfigure) {
193
+ console.log(chalk.yellow('\n✋ Configuration unchanged.\n'));
194
+ return;
195
+ }
196
+ }
197
+
198
+ // Run AI provider configuration
199
+ console.log('');
200
+ const aiConfig = await configureAIProvider(cwd);
201
+
202
+ if (aiConfig) {
203
+ console.log(chalk.green.bold('\n✅ AI Provider configured successfully!\n'));
204
+ console.log(chalk.gray(` Provider: ${aiConfig.providerName}`));
205
+ console.log(chalk.gray(` Model: ${aiConfig.model}`));
206
+ console.log(chalk.gray(` Config saved to: ${getEnvFilePath(cwd)}\n`));
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Configure Learning System category
212
+ */
213
+ async function configureLearningCategory(cwd, learningStatus) {
214
+ console.log(chalk.gray('\n' + '─'.repeat(60) + '\n'));
215
+
216
+ // Create and show learning manager menu
217
+ const learningManager = new LearningManager(cwd);
218
+ await learningManager.showMenu();
219
+ }
220
+
221
+ module.exports = config;
@@ -10,6 +10,7 @@ const {
10
10
  const Interviewer = require('../frameworks/interviewer');
11
11
  const SessionManager = require('../frameworks/session-manager');
12
12
  const { deployToTool } = require('./deploy');
13
+ const { configureAIProvider, loadEnvIntoProcess, getEnvFilePath } = require('../ai/ai-config');
13
14
 
14
15
  async function init(options) {
15
16
  console.log(chalk.cyan.bold('\n🚀 AgentDevFramework - Software Development Requirements\n'));
@@ -17,13 +18,41 @@ async function init(options) {
17
18
  const cwd = process.cwd();
18
19
  const adfDir = path.join(cwd, '.adf');
19
20
 
21
+ // Load .env file if it exists (for API keys)
22
+ const envPath = getEnvFilePath(cwd);
23
+ if (await fs.pathExists(envPath)) {
24
+ loadEnvIntoProcess(envPath);
25
+ }
26
+
20
27
  // Check for resumable sessions FIRST (before asking to overwrite)
21
28
  const sessionManager = new SessionManager(cwd);
22
29
  const existingSession = await sessionManager.promptToResume();
23
30
 
24
31
  if (existingSession) {
25
32
  // Resume existing session
26
- const interviewer = new Interviewer(existingSession.progress.framework || 'balanced', cwd, existingSession);
33
+ // Check if session has AI config (from resumed session)
34
+ let aiConfig = existingSession.progress.aiConfig;
35
+
36
+ if (aiConfig) {
37
+ // We have AI config from session, but need to verify API key exists
38
+ const apiKey = process.env[aiConfig.envVar];
39
+ if (!apiKey) {
40
+ console.log(chalk.yellow(`\n⚠️ Previous session used ${aiConfig.providerName}`));
41
+ console.log(chalk.yellow(`Please configure API key to resume...\n`));
42
+ aiConfig = await configureAIProvider(cwd);
43
+ } else {
44
+ // Add API key to config (it's not stored in session for security)
45
+ aiConfig.apiKey = apiKey;
46
+ console.log(chalk.green(`\n✓ Resuming with ${aiConfig.providerName} (${aiConfig.model})\n`));
47
+ }
48
+ } else {
49
+ // Old session without AI config, configure now
50
+ console.log(chalk.yellow('\n⚠️ This session was created before AI provider integration.'));
51
+ console.log(chalk.yellow('Please configure AI provider to continue...\n'));
52
+ aiConfig = await configureAIProvider(cwd);
53
+ }
54
+
55
+ const interviewer = new Interviewer(existingSession.progress.framework || 'balanced', cwd, existingSession, aiConfig);
27
56
  const sessionPath = await interviewer.start();
28
57
 
29
58
  console.log(chalk.green.bold('\n✨ Requirements gathering complete!\n'));
@@ -76,17 +105,34 @@ async function init(options) {
76
105
  // Create .adf directory
77
106
  await fs.ensureDir(adfDir);
78
107
 
79
- // Start AI-guided interview
108
+ // Configure AI Provider (OPTIONAL - can be done later with 'adf config')
109
+ let aiConfig = null;
110
+
111
+ const { configureAI } = await inquirer.prompt([
112
+ {
113
+ type: 'confirm',
114
+ name: 'configureAI',
115
+ message: 'Configure AI provider now? (Enables intelligent follow-up questions)',
116
+ default: true
117
+ }
118
+ ]);
119
+
120
+ if (configureAI) {
121
+ aiConfig = await configureAIProvider(cwd);
122
+ } else {
123
+ console.log(chalk.yellow('\n💡 You can configure AI later by running: adf config\n'));
124
+ }
125
+
126
+ // Start interview (with or without AI)
80
127
  console.log(chalk.gray('\n' + '━'.repeat(60)) + '\n');
81
128
 
82
- const interviewer = new Interviewer(workflow, cwd);
129
+ const interviewer = new Interviewer(workflow, cwd, null, aiConfig);
83
130
  const sessionPath = await interviewer.start();
84
131
 
85
- // Show next steps
86
- console.log(chalk.cyan('📋 Next Steps:\n'));
87
- console.log(chalk.gray(` 1. Review your requirements: ${sessionPath}/outputs/`));
88
- console.log(chalk.gray(` 2. Share the output files with your AI coding assistant`));
89
- console.log(chalk.gray(` 3. Start building based on the detailed requirements\n`));
132
+ // Show completion message
133
+ console.log(chalk.cyan('📋 Requirements Complete!\n'));
134
+ console.log(chalk.gray(` Files saved to: ${sessionPath}/outputs/`));
135
+ console.log(chalk.gray(` You can review your requirements anytime\n`));
90
136
 
91
137
  // Optional: Deploy to tool
92
138
  if (options.tool) {
@@ -97,8 +143,8 @@ async function init(options) {
97
143
  {
98
144
  type: 'confirm',
99
145
  name: 'deployNow',
100
- message: 'Deploy framework to a development tool?',
101
- default: false
146
+ message: 'Automatically deploy to your IDE? (I\'ll configure everything for you)',
147
+ default: true
102
148
  }
103
149
  ]);
104
150