@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
|
@@ -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
|
+
};
|
package/lib/commands/config.js
CHANGED
|
@@ -3,6 +3,8 @@ 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 LearningManager = require('../learning/learning-manager');
|
|
7
|
+
const { getLearningStats, getLearningConfig } = require('../learning/storage');
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* Configuration categories available in ADF CLI
|
|
@@ -12,6 +14,11 @@ const CONFIG_CATEGORIES = {
|
|
|
12
14
|
name: 'AI Provider Setup',
|
|
13
15
|
description: 'Configure AI provider (Anthropic, OpenAI, Google Gemini, OpenRouter)',
|
|
14
16
|
value: 'ai-provider'
|
|
17
|
+
},
|
|
18
|
+
LEARNING_SYSTEM: {
|
|
19
|
+
name: 'Learning System',
|
|
20
|
+
description: 'Manage interview learning data and preferences',
|
|
21
|
+
value: 'learning'
|
|
15
22
|
}
|
|
16
23
|
// Future config categories can be added here:
|
|
17
24
|
// PROJECT_SETTINGS: { name: 'Project Settings', description: '...', value: 'project' },
|
|
@@ -50,6 +57,30 @@ async function isAIConfigured(projectPath = process.cwd()) {
|
|
|
50
57
|
return { configured: false };
|
|
51
58
|
}
|
|
52
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
|
+
|
|
53
84
|
/**
|
|
54
85
|
* Display configuration status for AI provider
|
|
55
86
|
*/
|
|
@@ -62,6 +93,21 @@ function displayAIStatus(status) {
|
|
|
62
93
|
}
|
|
63
94
|
}
|
|
64
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
|
+
|
|
65
111
|
/**
|
|
66
112
|
* Main config command
|
|
67
113
|
*/
|
|
@@ -70,8 +116,9 @@ async function config() {
|
|
|
70
116
|
|
|
71
117
|
const cwd = process.cwd();
|
|
72
118
|
|
|
73
|
-
// Check
|
|
119
|
+
// Check configuration status for all categories
|
|
74
120
|
const aiStatus = await isAIConfigured(cwd);
|
|
121
|
+
const learningStatus = await getLearningStatus(cwd);
|
|
75
122
|
|
|
76
123
|
// Build choices with status indicators
|
|
77
124
|
const choices = [
|
|
@@ -80,6 +127,11 @@ async function config() {
|
|
|
80
127
|
value: CONFIG_CATEGORIES.AI_PROVIDER.value,
|
|
81
128
|
short: CONFIG_CATEGORIES.AI_PROVIDER.name
|
|
82
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
|
+
},
|
|
83
135
|
new inquirer.Separator(),
|
|
84
136
|
{
|
|
85
137
|
name: chalk.gray('← Back'),
|
|
@@ -108,6 +160,10 @@ async function config() {
|
|
|
108
160
|
await configureAIProviderCategory(cwd, aiStatus);
|
|
109
161
|
break;
|
|
110
162
|
|
|
163
|
+
case 'learning':
|
|
164
|
+
await configureLearningCategory(cwd, learningStatus);
|
|
165
|
+
break;
|
|
166
|
+
|
|
111
167
|
// Future categories will be handled here
|
|
112
168
|
default:
|
|
113
169
|
console.log(chalk.red('\n❌ Configuration category not implemented yet.\n'));
|
|
@@ -151,4 +207,15 @@ async function configureAIProviderCategory(cwd, aiStatus) {
|
|
|
151
207
|
}
|
|
152
208
|
}
|
|
153
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
|
+
|
|
154
221
|
module.exports = config;
|
package/lib/commands/init.js
CHANGED
|
@@ -105,28 +105,9 @@ async function init(options) {
|
|
|
105
105
|
// Create .adf directory
|
|
106
106
|
await fs.ensureDir(adfDir);
|
|
107
107
|
|
|
108
|
-
//
|
|
109
|
-
|
|
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)
|
|
127
|
-
console.log(chalk.gray('\n' + '━'.repeat(60)) + '\n');
|
|
128
|
-
|
|
129
|
-
const interviewer = new Interviewer(workflow, cwd, null, aiConfig);
|
|
108
|
+
// AI will be configured as first block in the interview
|
|
109
|
+
// Start interview
|
|
110
|
+
const interviewer = new Interviewer(workflow, cwd, null, null);
|
|
130
111
|
const sessionPath = await interviewer.start();
|
|
131
112
|
|
|
132
113
|
// Show completion message
|