@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.
- package/.project/chats/current/2025-10-03_ADF-CLI-QUALITY-BASED-PROGRESS-AND-RESUME.md +399 -0
- package/.project/docs/architecture/SYSTEM-DESIGN.md +369 -0
- package/.project/docs/frameworks/FRAMEWORK-METHODOLOGIES.md +449 -0
- package/.project/docs/goals/PROJECT-VISION.md +112 -0
- package/.project/docs/tool-integrations/IDE-CUSTOMIZATIONS.md +578 -0
- package/CHANGELOG.md +115 -0
- package/jest.config.js +20 -0
- package/lib/commands/init.js +41 -113
- package/lib/frameworks/answer-quality-analyzer.js +216 -0
- package/lib/frameworks/interviewer.js +447 -0
- package/lib/frameworks/output-generators.js +345 -0
- package/lib/frameworks/progress-tracker.js +239 -0
- package/lib/frameworks/questions.js +664 -0
- package/lib/frameworks/session-manager.js +100 -0
- package/package.json +10 -5
- package/tests/answer-quality-analyzer.test.js +173 -0
- package/tests/progress-tracker.test.js +205 -0
- package/tests/session-manager.test.js +162 -0
|
@@ -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;
|