@iservu-inc/adf-cli 0.1.6 → 0.2.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,345 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+
4
+ /**
5
+ * Output Generators
6
+ * Transform Q&A responses into framework-specific documents
7
+ */
8
+
9
+ async function generatePRP(answers, outputPath) {
10
+ const prp = `# Product Requirement Prompt (PRP)
11
+
12
+ **Generated:** ${new Date().toISOString()}
13
+
14
+ ---
15
+
16
+ ## 1. Goal Definition
17
+
18
+ ### What We're Building
19
+ ${answers['prp-1'] || 'Not provided'}
20
+
21
+ ### Primary User Action
22
+ ${answers['prp-2'] || 'Not provided'}
23
+
24
+ ### Problem Being Solved
25
+ ${answers['prp-3'] || 'Not provided'}
26
+
27
+ ### Success Criteria
28
+ ${answers['prp-4'] || 'Not provided'}
29
+
30
+ ---
31
+
32
+ ## 2. Business Justification
33
+
34
+ ### Value to Users
35
+ ${answers['prp-5'] || 'Not provided'}
36
+
37
+ ### Cost of NOT Building
38
+ ${answers['prp-6'] || 'Not provided'}
39
+
40
+ ### Usage Pattern
41
+ ${answers['prp-7'] || 'Not provided'}
42
+
43
+ ---
44
+
45
+ ## 3. Contextual Intelligence
46
+
47
+ ### Technology Stack
48
+ ${answers['prp-8'] || 'Not provided'}
49
+
50
+ ### Files & Directories
51
+ ${answers['prp-9'] || 'Not provided'}
52
+
53
+ ### Libraries & Packages
54
+ ${answers['prp-10'] || 'Not provided'}
55
+
56
+ ### Code Patterns to Follow
57
+ ${answers['prp-11'] || 'Not provided'}
58
+
59
+ ### Documentation References
60
+ ${answers['prp-12'] || 'Not provided'}
61
+
62
+ ---
63
+
64
+ ## 4. Implementation Blueprint
65
+
66
+ ### Implementation Steps
67
+ ${answers['prp-13'] || 'Not provided'}
68
+
69
+ ### Data Model
70
+ ${answers['prp-14'] || 'Not provided'}
71
+
72
+ ### API Design
73
+ ${answers['prp-15'] || 'Not provided'}
74
+
75
+ ### Edge Cases & Error Handling
76
+ ${answers['prp-16'] || 'Not provided'}
77
+
78
+ ### Technical Approach
79
+ ${answers['prp-17'] || 'Not provided'}
80
+
81
+ ---
82
+
83
+ ## 5. Validation
84
+
85
+ ### Manual Testing Steps
86
+ ${answers['prp-18'] || 'Not provided'}
87
+
88
+ ### Automated Tests Required
89
+ ${answers['prp-19'] || 'Not provided'}
90
+
91
+ ### Definition of Done
92
+ ${answers['prp-20'] || 'Not provided'}
93
+
94
+ ---
95
+
96
+ ## AI Agent Instructions
97
+
98
+ **Implement the above feature following this blueprint:**
99
+
100
+ 1. Start with the implementation steps in Section 4
101
+ 2. Reference the code patterns and documentation in Section 3
102
+ 3. Implement the data model and API design as specified
103
+ 4. Handle all edge cases listed
104
+ 5. Write tests as specified in Section 5
105
+ 6. Validate against the Definition of Done
106
+
107
+ **Success Criteria:** ${answers['prp-4'] || 'See Section 1'}
108
+ `;
109
+
110
+ await fs.writeFile(path.join(outputPath, 'prp.md'), prp, 'utf-8');
111
+ }
112
+
113
+ async function generateBalanced(answers, outputPath) {
114
+ // Constitution
115
+ const constitution = `# Project Constitution
116
+
117
+ **Generated:** ${new Date().toISOString()}
118
+
119
+ ## Code Quality Standards
120
+ ${answers['bal-21'] || 'Not provided'}
121
+
122
+ ## Testing Requirements
123
+ ${answers['bal-22'] || 'Not provided'}
124
+
125
+ ## Documentation Standards
126
+ ${answers['bal-23'] || 'Not provided'}
127
+
128
+ ## UX/Accessibility Principles
129
+ ${answers['bal-24'] || 'Not provided'}
130
+
131
+ ## Security Standards
132
+ ${answers['bal-25'] || 'Not provided'}
133
+
134
+ ## Decision-Making Guidelines
135
+ ${answers['bal-26'] || 'Not provided'}
136
+ `;
137
+
138
+ // Specification
139
+ const specification = `# Specification
140
+
141
+ **Generated:** ${new Date().toISOString()}
142
+
143
+ ---
144
+
145
+ ## Overview
146
+
147
+ ### Goal
148
+ ${answers['prp-1'] || 'Not provided'}
149
+
150
+ ### Problem
151
+ ${answers['prp-3'] || 'Not provided'}
152
+
153
+ ### Success Criteria
154
+ ${answers['prp-4'] || 'Not provided'}
155
+
156
+ ---
157
+
158
+ ## Features & Requirements
159
+
160
+ ### All Features with Priorities
161
+ ${answers['bal-27'] || 'Not provided'}
162
+
163
+ ### Feature Behaviors
164
+ ${answers['bal-28'] || 'Not provided'}
165
+
166
+ ### Out of Scope
167
+ ${answers['bal-29'] || 'Not provided'}
168
+
169
+ ### Integration Requirements
170
+ ${answers['bal-30'] || 'Not provided'}
171
+
172
+ ### User Workflow
173
+ ${answers['bal-31'] || 'Not provided'}
174
+
175
+ ### Edge Cases
176
+ ${answers['bal-32'] || 'Not provided'}
177
+
178
+ ### UI/UX Specifications
179
+ ${answers['bal-33'] || 'Not provided'}
180
+
181
+ ### Assumptions
182
+ ${answers['bal-34'] || 'Not provided'}
183
+
184
+ ### Open Questions
185
+ ${answers['bal-35'] || 'Not provided'}
186
+
187
+ ---
188
+
189
+ ## Technical Context
190
+
191
+ ### Technology Stack
192
+ ${answers['prp-8'] || 'Not provided'}
193
+
194
+ ### Files & Directories
195
+ ${answers['prp-9'] || 'Not provided'}
196
+
197
+ ### Dependencies
198
+ ${answers['prp-10'] || 'Not provided'}
199
+
200
+ ### Code Patterns
201
+ ${answers['prp-11'] || 'Not provided'}
202
+
203
+ ### Documentation
204
+ ${answers['prp-12'] || 'Not provided'}
205
+ `;
206
+
207
+ // Technical Plan
208
+ const plan = `# Technical Plan
209
+
210
+ **Generated:** ${new Date().toISOString()}
211
+
212
+ ---
213
+
214
+ ## Architecture
215
+
216
+ ### Technology Choice Justification
217
+ ${answers['bal-36'] || 'Not provided'}
218
+
219
+ ### Architectural Pattern
220
+ ${answers['bal-37'] || 'Not provided'}
221
+
222
+ ### Database Design
223
+ ${answers['bal-38'] || 'Not provided'}
224
+
225
+ ### API Design
226
+ ${answers['bal-39'] || 'Not provided'}
227
+
228
+ ### Authentication/Authorization
229
+ ${answers['bal-40'] || 'Not provided'}
230
+
231
+ ### Third-Party Services
232
+ ${answers['bal-41'] || 'Not provided'}
233
+
234
+ ### Deployment Architecture
235
+ ${answers['bal-42'] || 'Not provided'}
236
+
237
+ ### Performance Strategy
238
+ ${answers['bal-43'] || 'Not provided'}
239
+
240
+ ---
241
+
242
+ ## Implementation Details
243
+
244
+ ### Steps
245
+ ${answers['prp-13'] || 'Not provided'}
246
+
247
+ ### Data Model
248
+ ${answers['prp-14'] || 'Not provided'}
249
+
250
+ ### API Endpoints
251
+ ${answers['prp-15'] || 'Not provided'}
252
+
253
+ ### Error Handling
254
+ ${answers['prp-16'] || 'Not provided'}
255
+
256
+ ### Technical Approach
257
+ ${answers['prp-17'] || 'Not provided'}
258
+ `;
259
+
260
+ // Tasks
261
+ const tasks = `# Implementation Tasks
262
+
263
+ **Generated:** ${new Date().toISOString()}
264
+
265
+ ---
266
+
267
+ ## Task Breakdown
268
+
269
+ ### All Tasks (Dependency Order)
270
+ ${answers['bal-44'] || 'Not provided'}
271
+
272
+ ### Critical Path
273
+ ${answers['bal-45'] || 'Not provided'}
274
+
275
+ ### Parallel Tasks
276
+ ${answers['bal-46'] || 'Not provided'}
277
+
278
+ ### Milestones
279
+ ${answers['bal-47'] || 'Not provided'}
280
+
281
+ ### Testing Strategy
282
+ ${answers['bal-48'] || 'Not provided'}
283
+
284
+ ### Effort Estimates
285
+ ${answers['bal-49'] || 'Not provided'}
286
+
287
+ ---
288
+
289
+ ## Validation
290
+
291
+ ### Manual Testing
292
+ ${answers['prp-18'] || 'Not provided'}
293
+
294
+ ### Automated Tests
295
+ ${answers['prp-19'] || 'Not provided'}
296
+
297
+ ### Project Definition of Done
298
+ ${answers['bal-50'] || 'Not provided'}
299
+ `;
300
+
301
+ await fs.writeFile(path.join(outputPath, 'constitution.md'), constitution, 'utf-8');
302
+ await fs.writeFile(path.join(outputPath, 'specification.md'), specification, 'utf-8');
303
+ await fs.writeFile(path.join(outputPath, 'plan.md'), plan, 'utf-8');
304
+ await fs.writeFile(path.join(outputPath, 'tasks.md'), tasks, 'utf-8');
305
+ }
306
+
307
+ async function generateBMAD(answers, outputPath) {
308
+ // For now, BMAD uses the balanced outputs + additional strategic docs
309
+ // Full BMAD implementation would include PRD, Architecture, and Stories
310
+ await generateBalanced(answers, outputPath);
311
+
312
+ const prd = `# Product Requirements Document
313
+
314
+ **Generated:** ${new Date().toISOString()}
315
+
316
+ ## Business Overview
317
+ [BMAD questions would populate this section]
318
+
319
+ ## Market Analysis
320
+ [BMAD questions would populate this section]
321
+
322
+ ## Complete Requirements
323
+ See specification.md for detailed requirements.
324
+ `;
325
+
326
+ const architecture = `# Architecture Document
327
+
328
+ **Generated:** ${new Date().toISOString()}
329
+
330
+ ## System Architecture
331
+ See plan.md for technical architecture details.
332
+
333
+ ## Advanced Architecture Considerations
334
+ [BMAD-specific architecture questions would populate this]
335
+ `;
336
+
337
+ await fs.writeFile(path.join(outputPath, 'prd.md'), prd, 'utf-8');
338
+ await fs.writeFile(path.join(outputPath, 'architecture.md'), architecture, 'utf-8');
339
+ }
340
+
341
+ module.exports = {
342
+ generatePRP,
343
+ generateBalanced,
344
+ generateBMAD
345
+ };
@@ -0,0 +1,239 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+ const chalk = require('chalk');
4
+
5
+ /**
6
+ * Real-time Progress Tracker
7
+ * Tracks interview progress and enables resume capability
8
+ */
9
+
10
+ class ProgressTracker {
11
+ constructor(sessionPath, totalBlocks, framework = null) {
12
+ this.sessionPath = sessionPath;
13
+ this.progressFile = path.join(sessionPath, '_progress.json');
14
+ this.progressBackupFile = path.join(sessionPath, '_progress.backup.json');
15
+ this.progressLogFile = path.join(sessionPath, '_progress-log.md');
16
+ this.totalBlocks = totalBlocks;
17
+ this.progress = {
18
+ sessionId: path.basename(sessionPath),
19
+ framework: framework, // Store framework for resume
20
+ status: 'in-progress',
21
+ startedAt: new Date().toISOString(),
22
+ lastUpdated: new Date().toISOString(),
23
+ completedAt: null,
24
+ totalBlocks: totalBlocks,
25
+ currentBlock: 0,
26
+ completedBlocks: [],
27
+ skippedBlocks: [],
28
+ totalQuestionsAnswered: 0,
29
+ informationRichness: 0, // Quality metric 0-100
30
+ averageAnswerQuality: 0, // Average quality score of all answers
31
+ totalWordCount: 0,
32
+ comprehensiveAnswers: 0, // Answers with quality >= 70
33
+ canResume: true,
34
+ lastQuestion: null,
35
+ answers: {}, // Store all answers here for resume
36
+ transcript: [] // Store transcript for resume
37
+ };
38
+ }
39
+
40
+ async initialize() {
41
+ // Check if progress file exists (resuming session)
42
+ if (await fs.pathExists(this.progressFile)) {
43
+ this.progress = await fs.readJson(this.progressFile);
44
+ this.progress.canResume = true;
45
+ return true; // Session can be resumed
46
+ }
47
+
48
+ // New session - create progress file
49
+ await this.save();
50
+ await this.initializeLog();
51
+ return false; // New session
52
+ }
53
+
54
+ async initializeLog() {
55
+ const log = `# Interview Progress Log
56
+
57
+ **Session:** ${this.progress.sessionId}
58
+ **Started:** ${this.progress.startedAt}
59
+ **Status:** ${this.progress.status}
60
+
61
+ ---
62
+
63
+ ## Progress Timeline
64
+
65
+ `;
66
+ await fs.writeFile(this.progressLogFile, log, 'utf-8');
67
+ }
68
+
69
+ async startBlock(blockNumber, blockTitle) {
70
+ this.progress.currentBlock = blockNumber;
71
+ this.progress.lastUpdated = new Date().toISOString();
72
+
73
+ await this.save();
74
+ await this.appendToLog(`\n### Block ${blockNumber}: ${blockTitle}\n**Started:** ${this.progress.lastUpdated}\n`);
75
+ }
76
+
77
+ async completeBlock(blockNumber, blockTitle, questionsAnswered) {
78
+ this.progress.completedBlocks.push({
79
+ number: blockNumber,
80
+ title: blockTitle,
81
+ questionsAnswered: questionsAnswered,
82
+ completedAt: new Date().toISOString()
83
+ });
84
+ this.progress.totalQuestionsAnswered += questionsAnswered;
85
+ this.progress.lastUpdated = new Date().toISOString();
86
+
87
+ await this.save();
88
+ await this.appendToLog(`**Completed:** ${this.progress.lastUpdated} | Questions answered: ${questionsAnswered}\n`);
89
+
90
+ this.displayProgress();
91
+ }
92
+
93
+ async skipBlock(blockNumber, blockTitle) {
94
+ this.progress.skippedBlocks.push({
95
+ number: blockNumber,
96
+ title: blockTitle,
97
+ skippedAt: new Date().toISOString()
98
+ });
99
+ this.progress.lastUpdated = new Date().toISOString();
100
+
101
+ await this.save();
102
+ await this.appendToLog(`**Skipped:** ${this.progress.lastUpdated}\n`);
103
+
104
+ this.displayProgress();
105
+ }
106
+
107
+ async answerQuestion(questionId, questionText, answer, qualityMetrics) {
108
+ this.progress.lastQuestion = {
109
+ id: questionId,
110
+ text: questionText,
111
+ answeredAt: new Date().toISOString()
112
+ };
113
+ this.progress.lastUpdated = new Date().toISOString();
114
+
115
+ // Store answer for resume capability
116
+ this.progress.answers[questionId] = {
117
+ text: answer,
118
+ quality: qualityMetrics,
119
+ timestamp: new Date().toISOString()
120
+ };
121
+
122
+ // Update quality metrics
123
+ this.progress.totalWordCount += qualityMetrics.wordCount;
124
+ if (qualityMetrics.isComprehensive) {
125
+ this.progress.comprehensiveAnswers++;
126
+ }
127
+
128
+ // Recalculate average quality
129
+ const allScores = Object.values(this.progress.answers).map(a => a.quality.qualityScore);
130
+ this.progress.averageAnswerQuality = Math.round(
131
+ allScores.reduce((sum, score) => sum + score, 0) / allScores.length
132
+ );
133
+
134
+ // Calculate information richness (weighted average)
135
+ const completionFactor = this.progress.completedBlocks.length / this.totalBlocks;
136
+ const qualityFactor = this.progress.averageAnswerQuality / 100;
137
+ this.progress.informationRichness = Math.round((completionFactor * 0.4 + qualityFactor * 0.6) * 100);
138
+
139
+ // Triple-redundant save
140
+ await this.saveWithBackup();
141
+ }
142
+
143
+ async saveWithBackup() {
144
+ try {
145
+ // Save 1: Main progress file
146
+ await fs.writeJson(this.progressFile, this.progress, { spaces: 2 });
147
+
148
+ // Save 2: Backup file (in case main gets corrupted)
149
+ await fs.writeJson(this.progressBackupFile, this.progress, { spaces: 2 });
150
+
151
+ // Save 3: Append to log (human-readable backup)
152
+ const timestamp = new Date().toISOString();
153
+ await fs.appendFile(
154
+ this.progressLogFile,
155
+ `\nšŸ’¾ Auto-save: ${timestamp} | Questions: ${this.progress.totalQuestionsAnswered} | Quality: ${this.progress.averageAnswerQuality}% | Richness: ${this.progress.informationRichness}%\n`,
156
+ 'utf-8'
157
+ );
158
+ } catch (error) {
159
+ console.error(chalk.red('\nāš ļø Save error (attempting recovery):'), error.message);
160
+
161
+ // Emergency save to timestamped file
162
+ const emergencyFile = path.join(this.sessionPath, `_emergency-${Date.now()}.json`);
163
+ await fs.writeJson(emergencyFile, this.progress, { spaces: 2 });
164
+ console.log(chalk.yellow(` Emergency backup saved to: ${path.basename(emergencyFile)}\n`));
165
+ }
166
+ }
167
+
168
+ async addTranscriptEntry(entry) {
169
+ this.progress.transcript.push({
170
+ ...entry,
171
+ timestamp: new Date().toISOString()
172
+ });
173
+ await this.saveWithBackup();
174
+ }
175
+
176
+ async complete() {
177
+ this.progress.status = 'completed';
178
+ this.progress.completedAt = new Date().toISOString();
179
+ this.progress.canResume = false;
180
+
181
+ await this.save();
182
+ await this.appendToLog(`\n---\n\n## Session Completed\n**Finished:** ${this.progress.completedAt}\n**Total Questions Answered:** ${this.progress.totalQuestionsAnswered}\n`);
183
+ }
184
+
185
+ async save() {
186
+ await fs.writeJson(this.progressFile, this.progress, { spaces: 2 });
187
+ }
188
+
189
+ async appendToLog(text) {
190
+ await fs.appendFile(this.progressLogFile, text, 'utf-8');
191
+ }
192
+
193
+ displayProgress() {
194
+ const completed = this.progress.completedBlocks.length;
195
+ const skipped = this.progress.skippedBlocks.length;
196
+ const remaining = this.totalBlocks - completed - skipped;
197
+ const blockPercentage = Math.round((completed / this.totalBlocks) * 100);
198
+
199
+ // Show information richness (quality-based metric)
200
+ const richnessEmoji = this.progress.informationRichness >= 80 ? '🌟' :
201
+ this.progress.informationRichness >= 60 ? '✨' :
202
+ this.progress.informationRichness >= 40 ? 'šŸ’«' : '⭐';
203
+
204
+ console.log(chalk.gray(`\nšŸ“Š Information Richness: ${richnessEmoji} ${this.progress.informationRichness}% | Avg Quality: ${this.progress.averageAnswerQuality}%`));
205
+ console.log(chalk.gray(` Blocks: ${completed}/${this.totalBlocks} (${blockPercentage}%) | Questions: ${this.progress.totalQuestionsAnswered} | Words: ${this.progress.totalWordCount}`));
206
+
207
+ if (remaining > 0) {
208
+ console.log(chalk.gray(` Remaining: ${remaining} blocks\n`));
209
+ } else {
210
+ console.log(chalk.green(` šŸŽ‰ All blocks completed!\n`));
211
+ }
212
+ }
213
+
214
+ async saveCheckpoint(message) {
215
+ await this.appendToLog(`\nšŸ’¾ **Checkpoint:** ${message} - ${new Date().toISOString()}\n`);
216
+ await this.save();
217
+ }
218
+
219
+ getProgress() {
220
+ return this.progress;
221
+ }
222
+
223
+ canResume() {
224
+ return this.progress.canResume && this.progress.status === 'in-progress';
225
+ }
226
+
227
+ getResumeInfo() {
228
+ return {
229
+ sessionId: this.progress.sessionId,
230
+ lastUpdated: this.progress.lastUpdated,
231
+ completedBlocks: this.progress.completedBlocks.length,
232
+ totalBlocks: this.totalBlocks,
233
+ totalQuestionsAnswered: this.progress.totalQuestionsAnswered,
234
+ currentBlock: this.progress.currentBlock
235
+ };
236
+ }
237
+ }
238
+
239
+ module.exports = ProgressTracker;