@iservu-inc/adf-cli 0.4.36 → 0.5.1
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/2025-10-05_INTELLIGENT-ANSWER-ANALYSIS.md +649 -0
- package/.project/chats/current/2025-10-05_MULTI-IDE-IMPROVEMENTS.md +415 -0
- package/.project/chats/current/SESSION-STATUS.md +166 -71
- package/.project/docs/ROADMAP.md +150 -17
- package/CHANGELOG.md +243 -0
- package/lib/analysis/answer-analyzer.js +304 -0
- package/lib/analysis/dynamic-pipeline.js +262 -0
- package/lib/analysis/knowledge-graph.js +227 -0
- package/lib/analysis/question-mapper.js +293 -0
- package/lib/commands/init.js +137 -8
- package/lib/frameworks/interviewer.js +59 -0
- package/package.json +1 -1
- package/tests/answer-analyzer.test.js +262 -0
- package/tests/dynamic-pipeline.test.js +332 -0
- package/tests/knowledge-graph.test.js +322 -0
- package/tests/question-mapper.test.js +342 -0
- /package/.project/chats/{current → complete}/2025-10-04_CRITICAL-MODEL-FETCHING-BUG.md +0 -0
- /package/.project/chats/{current → complete}/2025-10-04_PHASE-4-2-COMPLETION-AND-ROADMAP.md +0 -0
- /package/.project/chats/{current → complete}/2025-10-04_PHASE-4-2-LEARNING-SYSTEM.md +0 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Question Mapper
|
|
3
|
+
*
|
|
4
|
+
* Maps each question to the information types it's designed to gather.
|
|
5
|
+
* This allows the system to determine which questions can be skipped
|
|
6
|
+
* if that information has already been extracted from previous answers.
|
|
7
|
+
*
|
|
8
|
+
* For example:
|
|
9
|
+
* - "What are you building?" -> [PROJECT_GOAL, TECH_STACK, FEATURES, PLATFORM]
|
|
10
|
+
* - "What tech stack will you use?" -> [TECH_STACK, ARCHITECTURE]
|
|
11
|
+
* - "Who are your users?" -> [TARGET_USERS, PROJECT_GOAL]
|
|
12
|
+
*/
|
|
13
|
+
class QuestionMapper {
|
|
14
|
+
constructor() {
|
|
15
|
+
// Information types (should match AnswerAnalyzer)
|
|
16
|
+
this.INFO_TYPES = {
|
|
17
|
+
TECH_STACK: 'tech_stack',
|
|
18
|
+
ARCHITECTURE: 'architecture',
|
|
19
|
+
PROJECT_GOAL: 'project_goal',
|
|
20
|
+
TARGET_USERS: 'target_users',
|
|
21
|
+
FEATURES: 'features',
|
|
22
|
+
CONSTRAINTS: 'constraints',
|
|
23
|
+
TIMELINE: 'timeline',
|
|
24
|
+
TEAM_SIZE: 'team_size',
|
|
25
|
+
PLATFORM: 'platform',
|
|
26
|
+
DEPLOYMENT: 'deployment',
|
|
27
|
+
SECURITY: 'security',
|
|
28
|
+
PERFORMANCE: 'performance'
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// Build mappings based on question patterns
|
|
32
|
+
this.buildMappings();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Build mappings for common question patterns
|
|
37
|
+
*/
|
|
38
|
+
buildMappings() {
|
|
39
|
+
this.patterns = [
|
|
40
|
+
// Goal/Purpose questions
|
|
41
|
+
{
|
|
42
|
+
keywords: ['goal', 'building', 'purpose', 'create', 'develop', 'project'],
|
|
43
|
+
types: [this.INFO_TYPES.PROJECT_GOAL],
|
|
44
|
+
priority: 1 // Higher priority = more fundamental question
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
// Tech Stack questions
|
|
48
|
+
{
|
|
49
|
+
keywords: ['tech', 'stack', 'technology', 'framework', 'language', 'tools'],
|
|
50
|
+
types: [this.INFO_TYPES.TECH_STACK],
|
|
51
|
+
priority: 2
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
// Architecture questions
|
|
55
|
+
{
|
|
56
|
+
keywords: ['architecture', 'structure', 'design', 'organize', 'components'],
|
|
57
|
+
types: [this.INFO_TYPES.ARCHITECTURE],
|
|
58
|
+
priority: 2
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
// User questions
|
|
62
|
+
{
|
|
63
|
+
keywords: ['users', 'audience', 'customers', 'personas', 'who will use'],
|
|
64
|
+
types: [this.INFO_TYPES.TARGET_USERS],
|
|
65
|
+
priority: 3
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
// Feature questions
|
|
69
|
+
{
|
|
70
|
+
keywords: ['features', 'functionality', 'capabilities', 'do', 'functions'],
|
|
71
|
+
types: [this.INFO_TYPES.FEATURES],
|
|
72
|
+
priority: 3
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
// Platform questions
|
|
76
|
+
{
|
|
77
|
+
keywords: ['platform', 'web', 'mobile', 'desktop', 'where', 'run'],
|
|
78
|
+
types: [this.INFO_TYPES.PLATFORM],
|
|
79
|
+
priority: 2
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
// Timeline questions
|
|
83
|
+
{
|
|
84
|
+
keywords: ['timeline', 'deadline', 'when', 'schedule', 'launch'],
|
|
85
|
+
types: [this.INFO_TYPES.TIMELINE],
|
|
86
|
+
priority: 4
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
// Team questions
|
|
90
|
+
{
|
|
91
|
+
keywords: ['team', 'developers', 'people', 'resources', 'who'],
|
|
92
|
+
types: [this.INFO_TYPES.TEAM_SIZE],
|
|
93
|
+
priority: 4
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
// Constraints questions
|
|
97
|
+
{
|
|
98
|
+
keywords: ['constraints', 'limitations', 'requirements', 'must', 'cannot'],
|
|
99
|
+
types: [this.INFO_TYPES.CONSTRAINTS],
|
|
100
|
+
priority: 3
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
// Deployment questions
|
|
104
|
+
{
|
|
105
|
+
keywords: ['deploy', 'hosting', 'infrastructure', 'production', 'server'],
|
|
106
|
+
types: [this.INFO_TYPES.DEPLOYMENT, this.INFO_TYPES.TECH_STACK],
|
|
107
|
+
priority: 4
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
// Security questions
|
|
111
|
+
{
|
|
112
|
+
keywords: ['security', 'authentication', 'authorization', 'secure', 'privacy'],
|
|
113
|
+
types: [this.INFO_TYPES.SECURITY],
|
|
114
|
+
priority: 3
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
// Performance questions
|
|
118
|
+
{
|
|
119
|
+
keywords: ['performance', 'speed', 'scale', 'optimization', 'fast'],
|
|
120
|
+
types: [this.INFO_TYPES.PERFORMANCE],
|
|
121
|
+
priority: 4
|
|
122
|
+
}
|
|
123
|
+
];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Map a question to information types it gathers
|
|
128
|
+
*
|
|
129
|
+
* @param {Object} question - Question object with id and text
|
|
130
|
+
* @returns {Object} - { types: [], priority: number, confidence: number }
|
|
131
|
+
*/
|
|
132
|
+
mapQuestion(question) {
|
|
133
|
+
const questionText = (question.text || '').toLowerCase();
|
|
134
|
+
const questionId = (question.id || '').toLowerCase();
|
|
135
|
+
const combinedText = `${questionText} ${questionId}`;
|
|
136
|
+
|
|
137
|
+
const matchedTypes = new Set();
|
|
138
|
+
let highestPriority = 5; // Lower number = higher priority
|
|
139
|
+
|
|
140
|
+
// Check each pattern
|
|
141
|
+
this.patterns.forEach(pattern => {
|
|
142
|
+
const matches = pattern.keywords.some(keyword =>
|
|
143
|
+
combinedText.includes(keyword)
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
if (matches) {
|
|
147
|
+
pattern.types.forEach(type => matchedTypes.add(type));
|
|
148
|
+
if (pattern.priority < highestPriority) {
|
|
149
|
+
highestPriority = pattern.priority;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// If no matches found, make a best guess based on question structure
|
|
155
|
+
if (matchedTypes.size === 0) {
|
|
156
|
+
matchedTypes.add(this.INFO_TYPES.PROJECT_GOAL); // Default assumption
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
types: Array.from(matchedTypes),
|
|
161
|
+
priority: highestPriority,
|
|
162
|
+
confidence: matchedTypes.size > 0 ? 85 : 40 // How confident are we in this mapping
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Check if a question's information types are already satisfied in the knowledge graph
|
|
168
|
+
*
|
|
169
|
+
* @param {Object} question - Question object
|
|
170
|
+
* @param {KnowledgeGraph} knowledgeGraph - Knowledge graph instance
|
|
171
|
+
* @param {number} minConfidence - Minimum confidence threshold (default 70)
|
|
172
|
+
* @returns {Object} - { canSkip: boolean, reason: string, satisfiedTypes: [], missingTypes: [] }
|
|
173
|
+
*/
|
|
174
|
+
canSkipQuestion(question, knowledgeGraph, minConfidence = 70) {
|
|
175
|
+
const mapping = this.mapQuestion(question);
|
|
176
|
+
const satisfiedTypes = [];
|
|
177
|
+
const missingTypes = [];
|
|
178
|
+
|
|
179
|
+
mapping.types.forEach(type => {
|
|
180
|
+
if (knowledgeGraph.has(type, minConfidence)) {
|
|
181
|
+
satisfiedTypes.push({
|
|
182
|
+
type,
|
|
183
|
+
confidence: knowledgeGraph.getConfidence(type)
|
|
184
|
+
});
|
|
185
|
+
} else {
|
|
186
|
+
missingTypes.push(type);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// Can skip if ALL required types are satisfied
|
|
191
|
+
const canSkip = missingTypes.length === 0 && satisfiedTypes.length > 0;
|
|
192
|
+
|
|
193
|
+
let reason = '';
|
|
194
|
+
if (canSkip) {
|
|
195
|
+
const typeLabels = satisfiedTypes.map(t => t.type.replace(/_/g, ' ')).join(', ');
|
|
196
|
+
const avgConfidence = Math.round(
|
|
197
|
+
satisfiedTypes.reduce((sum, t) => sum + t.confidence, 0) / satisfiedTypes.length
|
|
198
|
+
);
|
|
199
|
+
reason = `Already have: ${typeLabels} (${avgConfidence}% confidence)`;
|
|
200
|
+
} else if (satisfiedTypes.length > 0) {
|
|
201
|
+
const partial = satisfiedTypes.map(t => t.type.replace(/_/g, ' ')).join(', ');
|
|
202
|
+
const missing = missingTypes.map(t => t.replace(/_/g, ' ')).join(', ');
|
|
203
|
+
reason = `Partial: have ${partial}, need ${missing}`;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
canSkip,
|
|
208
|
+
reason,
|
|
209
|
+
satisfiedTypes,
|
|
210
|
+
missingTypes,
|
|
211
|
+
questionPriority: mapping.priority
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Reorder questions based on knowledge graph
|
|
217
|
+
* Prioritize questions that gather missing information
|
|
218
|
+
*
|
|
219
|
+
* @param {Array} questions - Array of question objects
|
|
220
|
+
* @param {KnowledgeGraph} knowledgeGraph - Knowledge graph instance
|
|
221
|
+
* @returns {Array} - Reordered questions with skip recommendations
|
|
222
|
+
*/
|
|
223
|
+
reorderQuestions(questions, knowledgeGraph) {
|
|
224
|
+
const scoredQuestions = questions.map(question => {
|
|
225
|
+
const skipInfo = this.canSkipQuestion(question, knowledgeGraph);
|
|
226
|
+
|
|
227
|
+
// Calculate relevance score
|
|
228
|
+
// Higher score = more important to ask
|
|
229
|
+
let score = 100;
|
|
230
|
+
|
|
231
|
+
if (skipInfo.canSkip) {
|
|
232
|
+
// Can skip entirely
|
|
233
|
+
score = 0;
|
|
234
|
+
} else if (skipInfo.satisfiedTypes.length > 0) {
|
|
235
|
+
// Partially satisfied, lower priority
|
|
236
|
+
const percentSatisfied = skipInfo.satisfiedTypes.length /
|
|
237
|
+
(skipInfo.satisfiedTypes.length + skipInfo.missingTypes.length);
|
|
238
|
+
score = Math.round((1 - percentSatisfied) * 100);
|
|
239
|
+
} else {
|
|
240
|
+
// Not satisfied at all, full priority
|
|
241
|
+
score = 100;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Boost score for high-priority questions
|
|
245
|
+
if (skipInfo.questionPriority === 1) {
|
|
246
|
+
score = Math.min(100, score * 1.3);
|
|
247
|
+
} else if (skipInfo.questionPriority === 2) {
|
|
248
|
+
score = Math.min(100, score * 1.15);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return {
|
|
252
|
+
question,
|
|
253
|
+
skipInfo,
|
|
254
|
+
relevanceScore: score
|
|
255
|
+
};
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// Sort by relevance score (highest first)
|
|
259
|
+
scoredQuestions.sort((a, b) => b.relevanceScore - a.relevanceScore);
|
|
260
|
+
|
|
261
|
+
return scoredQuestions;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Get statistics about question mappings
|
|
266
|
+
*/
|
|
267
|
+
getStats(questions, knowledgeGraph) {
|
|
268
|
+
let canSkip = 0;
|
|
269
|
+
let partial = 0;
|
|
270
|
+
let needed = 0;
|
|
271
|
+
|
|
272
|
+
questions.forEach(question => {
|
|
273
|
+
const skipInfo = this.canSkipQuestion(question, knowledgeGraph);
|
|
274
|
+
if (skipInfo.canSkip) {
|
|
275
|
+
canSkip++;
|
|
276
|
+
} else if (skipInfo.satisfiedTypes.length > 0) {
|
|
277
|
+
partial++;
|
|
278
|
+
} else {
|
|
279
|
+
needed++;
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
return {
|
|
284
|
+
total: questions.length,
|
|
285
|
+
canSkip,
|
|
286
|
+
partial,
|
|
287
|
+
needed,
|
|
288
|
+
estimatedTimeSaved: canSkip * 1.5 // Assume 1.5 min per question
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
module.exports = QuestionMapper;
|
package/lib/commands/init.js
CHANGED
|
@@ -67,21 +67,108 @@ async function init(options) {
|
|
|
67
67
|
const hasContent = await hasMeaningfulContent(adfDir);
|
|
68
68
|
|
|
69
69
|
if (hasContent) {
|
|
70
|
-
|
|
70
|
+
// Show what exists
|
|
71
|
+
const existingContent = await getExistingContent(adfDir);
|
|
72
|
+
console.log(chalk.cyan('\n📦 Existing ADF Project Detected\n'));
|
|
73
|
+
|
|
74
|
+
if (existingContent.sessions > 0) {
|
|
75
|
+
console.log(chalk.gray(` Sessions: ${existingContent.sessions} session(s)`));
|
|
76
|
+
}
|
|
77
|
+
if (existingContent.outputs > 0) {
|
|
78
|
+
console.log(chalk.gray(` Outputs: ${existingContent.outputs} file(s)`));
|
|
79
|
+
}
|
|
80
|
+
if (existingContent.learning) {
|
|
81
|
+
console.log(chalk.gray(` Learning data: Present`));
|
|
82
|
+
}
|
|
83
|
+
console.log('');
|
|
84
|
+
|
|
85
|
+
const { action } = await inquirer.prompt([
|
|
71
86
|
{
|
|
72
|
-
type: '
|
|
73
|
-
name: '
|
|
74
|
-
message:
|
|
75
|
-
|
|
87
|
+
type: 'list',
|
|
88
|
+
name: 'action',
|
|
89
|
+
message: 'What would you like to do?',
|
|
90
|
+
choices: [
|
|
91
|
+
{
|
|
92
|
+
name: 'Continue with Existing Project',
|
|
93
|
+
value: 'continue',
|
|
94
|
+
short: 'Continue'
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: 'Reset this Project (delete all data)',
|
|
98
|
+
value: 'reset',
|
|
99
|
+
short: 'Reset'
|
|
100
|
+
},
|
|
101
|
+
new inquirer.Separator(),
|
|
102
|
+
{
|
|
103
|
+
name: chalk.gray('← Don\'t change & Exit'),
|
|
104
|
+
value: 'exit',
|
|
105
|
+
short: 'Exit'
|
|
106
|
+
}
|
|
107
|
+
],
|
|
108
|
+
default: 'continue'
|
|
76
109
|
}
|
|
77
110
|
]);
|
|
78
111
|
|
|
79
|
-
if (
|
|
80
|
-
console.log(chalk.
|
|
112
|
+
if (action === 'exit') {
|
|
113
|
+
console.log(chalk.gray('\n← Exited without changes.\n'));
|
|
81
114
|
return;
|
|
82
115
|
}
|
|
83
116
|
|
|
84
|
-
|
|
117
|
+
if (action === 'reset') {
|
|
118
|
+
// Confirm deletion
|
|
119
|
+
const { confirmReset } = await inquirer.prompt([
|
|
120
|
+
{
|
|
121
|
+
type: 'confirm',
|
|
122
|
+
name: 'confirmReset',
|
|
123
|
+
message: chalk.red('⚠️ This will permanently delete all sessions and data. Continue?'),
|
|
124
|
+
default: false
|
|
125
|
+
}
|
|
126
|
+
]);
|
|
127
|
+
|
|
128
|
+
if (!confirmReset) {
|
|
129
|
+
console.log(chalk.gray('\n← Reset cancelled. Exited without changes.\n'));
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
await fs.remove(adfDir);
|
|
134
|
+
console.log(chalk.yellow('\n✓ Project reset. Starting fresh...\n'));
|
|
135
|
+
} else if (action === 'continue') {
|
|
136
|
+
// Continue with existing project - show sessions and prompt to resume
|
|
137
|
+
console.log(chalk.green('\n✓ Continuing with existing project...\n'));
|
|
138
|
+
|
|
139
|
+
// The SessionManager.promptToResume() was already called above (line 29)
|
|
140
|
+
// But it returned null (no resumable sessions), so let them see what exists
|
|
141
|
+
// and choose to start a new session or view existing outputs
|
|
142
|
+
|
|
143
|
+
const { continueAction } = await inquirer.prompt([
|
|
144
|
+
{
|
|
145
|
+
type: 'list',
|
|
146
|
+
name: 'continueAction',
|
|
147
|
+
message: 'What would you like to do?',
|
|
148
|
+
choices: [
|
|
149
|
+
{
|
|
150
|
+
name: 'Start a new session (keeps existing data)',
|
|
151
|
+
value: 'new-session',
|
|
152
|
+
short: 'New Session'
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
name: chalk.gray('← Exit'),
|
|
156
|
+
value: 'exit',
|
|
157
|
+
short: 'Exit'
|
|
158
|
+
}
|
|
159
|
+
],
|
|
160
|
+
default: 'new-session'
|
|
161
|
+
}
|
|
162
|
+
]);
|
|
163
|
+
|
|
164
|
+
if (continueAction === 'exit') {
|
|
165
|
+
console.log(chalk.gray('\n← Exited.\n'));
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Continue to start a new session below (fall through)
|
|
170
|
+
console.log('');
|
|
171
|
+
}
|
|
85
172
|
} else {
|
|
86
173
|
// Only .env file exists - safe to continue without prompting
|
|
87
174
|
console.log(chalk.gray('✓ Using existing .adf directory\n'));
|
|
@@ -218,4 +305,46 @@ async function hasMeaningfulContent(adfDir) {
|
|
|
218
305
|
}
|
|
219
306
|
}
|
|
220
307
|
|
|
308
|
+
/**
|
|
309
|
+
* Get detailed information about existing ADF content
|
|
310
|
+
* Returns object with counts and details
|
|
311
|
+
*/
|
|
312
|
+
async function getExistingContent(adfDir) {
|
|
313
|
+
const result = {
|
|
314
|
+
sessions: 0,
|
|
315
|
+
outputs: 0,
|
|
316
|
+
learning: false,
|
|
317
|
+
details: []
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
try {
|
|
321
|
+
const contents = await fs.readdir(adfDir);
|
|
322
|
+
|
|
323
|
+
for (const item of contents) {
|
|
324
|
+
const itemPath = path.join(adfDir, item);
|
|
325
|
+
const stats = await fs.stat(itemPath);
|
|
326
|
+
|
|
327
|
+
if (stats.isDirectory()) {
|
|
328
|
+
if (item === 'sessions') {
|
|
329
|
+
// Count session directories
|
|
330
|
+
const sessions = await fs.readdir(itemPath);
|
|
331
|
+
result.sessions = sessions.filter(s => !s.startsWith('.')).length;
|
|
332
|
+
} else if (item === 'outputs') {
|
|
333
|
+
// Count output files
|
|
334
|
+
const outputs = await fs.readdir(itemPath);
|
|
335
|
+
result.outputs = outputs.filter(o => !o.startsWith('.')).length;
|
|
336
|
+
} else if (item === 'learning') {
|
|
337
|
+
// Check for learning data
|
|
338
|
+
const learningFiles = await fs.readdir(itemPath);
|
|
339
|
+
result.learning = learningFiles.length > 0;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
return result;
|
|
345
|
+
} catch (error) {
|
|
346
|
+
return result;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
221
350
|
module.exports = init;
|
|
@@ -12,6 +12,7 @@ const { SkipTracker } = require('../learning/skip-tracker');
|
|
|
12
12
|
const { detectPatterns } = require('../learning/pattern-detector');
|
|
13
13
|
const { updateLearnedRules, getActiveRules, getRuleExplanations } = require('../learning/rule-generator');
|
|
14
14
|
const { getLearningConfig } = require('../learning/storage');
|
|
15
|
+
const DynamicPipeline = require('../analysis/dynamic-pipeline');
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* Conversational AI Interviewer
|
|
@@ -47,6 +48,7 @@ class Interviewer {
|
|
|
47
48
|
this.aiClient = null; // Will be initialized in start()
|
|
48
49
|
this.skipTracker = null; // Will be initialized in start() with project context
|
|
49
50
|
this.learnedRules = []; // Will be loaded in start()
|
|
51
|
+
this.dynamicPipeline = null; // Will be initialized in start() with AI client
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
generateSessionId() {
|
|
@@ -125,6 +127,15 @@ class Interviewer {
|
|
|
125
127
|
// Allow interview to continue without AI (graceful degradation)
|
|
126
128
|
}
|
|
127
129
|
|
|
130
|
+
// 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();
|
|
138
|
+
|
|
128
139
|
// Create session directory
|
|
129
140
|
await fs.ensureDir(this.sessionPath);
|
|
130
141
|
await fs.ensureDir(path.join(this.sessionPath, 'qa-responses'));
|
|
@@ -318,6 +329,11 @@ class Interviewer {
|
|
|
318
329
|
|
|
319
330
|
// Save block answers
|
|
320
331
|
await this.saveBlockAnswers(block);
|
|
332
|
+
|
|
333
|
+
// Show knowledge summary every 2 blocks
|
|
334
|
+
if (this.dynamicPipeline && (i + 1) % 2 === 0 && i + 1 < questionBlocks.length) {
|
|
335
|
+
this.dynamicPipeline.displayKnowledgeSummary();
|
|
336
|
+
}
|
|
321
337
|
}
|
|
322
338
|
|
|
323
339
|
// Generate framework outputs
|
|
@@ -354,6 +370,11 @@ class Interviewer {
|
|
|
354
370
|
// Mark session as complete
|
|
355
371
|
await this.progressTracker.complete();
|
|
356
372
|
|
|
373
|
+
// Display Dynamic Pipeline stats
|
|
374
|
+
if (this.dynamicPipeline) {
|
|
375
|
+
this.dynamicPipeline.displayFinalStats();
|
|
376
|
+
}
|
|
377
|
+
|
|
357
378
|
console.log(chalk.green.bold('\n✨ Requirements gathering complete!\n'));
|
|
358
379
|
console.log(chalk.cyan(`📁 Session saved to: .adf/sessions/${this.sessionId}/\n`));
|
|
359
380
|
|
|
@@ -470,9 +491,38 @@ class Interviewer {
|
|
|
470
491
|
async askBlockQuestions(block, currentBlock, totalBlocks) {
|
|
471
492
|
const blockAnswers = {};
|
|
472
493
|
let questionsAnswered = 0;
|
|
494
|
+
let questionsSkipped = 0;
|
|
473
495
|
|
|
474
496
|
for (let i = 0; i < block.questions.length; i++) {
|
|
475
497
|
const question = block.questions[i];
|
|
498
|
+
|
|
499
|
+
// Check if we should skip this question based on knowledge graph
|
|
500
|
+
if (this.dynamicPipeline) {
|
|
501
|
+
const skipCheck = this.dynamicPipeline.shouldSkipQuestion(question);
|
|
502
|
+
|
|
503
|
+
if (skipCheck.shouldSkip) {
|
|
504
|
+
console.log(chalk.cyan(`Question ${i + 1}/${block.questions.length}`) + chalk.gray(` (Block ${currentBlock}/${totalBlocks})`) + '\n');
|
|
505
|
+
console.log(chalk.gray('━'.repeat(60)));
|
|
506
|
+
console.log(chalk.yellow(`\n⏭️ Skipping: ${question.text}`));
|
|
507
|
+
console.log(chalk.green(` ✓ ${skipCheck.reason}\n`));
|
|
508
|
+
console.log(chalk.gray('━'.repeat(60)) + '\n');
|
|
509
|
+
|
|
510
|
+
questionsSkipped++;
|
|
511
|
+
|
|
512
|
+
// Log skip to transcript
|
|
513
|
+
this.transcript.push({
|
|
514
|
+
type: 'question-skipped-intelligent',
|
|
515
|
+
question: question.text,
|
|
516
|
+
questionId: question.id,
|
|
517
|
+
reason: skipCheck.reason,
|
|
518
|
+
confidence: skipCheck.confidence,
|
|
519
|
+
timestamp: new Date().toISOString()
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
476
526
|
const answer = await this.askQuestion(question, i + 1, block.questions.length, currentBlock, totalBlocks);
|
|
477
527
|
|
|
478
528
|
if (answer === null) {
|
|
@@ -494,6 +544,10 @@ class Interviewer {
|
|
|
494
544
|
});
|
|
495
545
|
}
|
|
496
546
|
|
|
547
|
+
if (questionsSkipped > 0 && questionsAnswered > 0) {
|
|
548
|
+
console.log(chalk.cyan(`\n📊 Block Summary: ${questionsAnswered} answered, ${questionsSkipped} intelligently skipped\n`));
|
|
549
|
+
}
|
|
550
|
+
|
|
497
551
|
return questionsAnswered;
|
|
498
552
|
}
|
|
499
553
|
|
|
@@ -583,6 +637,11 @@ class Interviewer {
|
|
|
583
637
|
});
|
|
584
638
|
}
|
|
585
639
|
|
|
640
|
+
// Process answer with Dynamic Pipeline (Phase 4.4)
|
|
641
|
+
if (this.dynamicPipeline) {
|
|
642
|
+
await this.dynamicPipeline.processAnswer(question.id, question.text, answer);
|
|
643
|
+
}
|
|
644
|
+
|
|
586
645
|
// Check if answer is comprehensive enough to skip follow-ups
|
|
587
646
|
if (qualityMetrics.canSkipFollowUps) {
|
|
588
647
|
console.log(chalk.green('\n✓ Saved\n'));
|