jettypod 4.1.4 → 4.1.6
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/cucumber.js +0 -1
- package/lib/current-work.js +104 -52
- package/lib/migrations/016-worktree-sessions-table.js +84 -0
- package/lib/worktree-manager.js +27 -2
- package/lib/worktree-sessions.js +186 -0
- package/package.json +1 -1
- package/skills-templates/feature-planning/SKILL.md +68 -180
- package/skills-templates/speed-mode/SKILL.md +224 -93
- package/skills-templates/stable-mode/SKILL.md +189 -158
|
@@ -5,6 +5,19 @@ description: Guide implementation of stable mode chores with comprehensive testi
|
|
|
5
5
|
|
|
6
6
|
# Stable Mode Skill
|
|
7
7
|
|
|
8
|
+
```
|
|
9
|
+
┌─────────────────────────────────────────────────────────────────────┐
|
|
10
|
+
│ Mode Progression Flow │
|
|
11
|
+
│ │
|
|
12
|
+
│ Feature Planning → Speed Mode → [STABLE MODE] → Production Mode │
|
|
13
|
+
│ ▲▲▲▲▲▲▲▲▲▲▲▲▲ │
|
|
14
|
+
│ YOU ARE HERE │
|
|
15
|
+
│ │
|
|
16
|
+
│ Next: After stable mode implementation, feature can be marked │
|
|
17
|
+
│ complete, or elevated to production mode if needed. │
|
|
18
|
+
└─────────────────────────────────────────────────────────────────────┘
|
|
19
|
+
```
|
|
20
|
+
|
|
8
21
|
Guides Claude Code through stable mode implementation with comprehensive testing focus. Users confirm approach but Claude Code writes the code.
|
|
9
22
|
|
|
10
23
|
## Instructions
|
|
@@ -62,6 +75,23 @@ When this skill is activated, you are helping implement a stable mode chore to a
|
|
|
62
75
|
|
|
63
76
|
---
|
|
64
77
|
|
|
78
|
+
## Quick Reference: Async Boundaries
|
|
79
|
+
|
|
80
|
+
**Where Claude Code MUST wait for user confirmation:**
|
|
81
|
+
|
|
82
|
+
| Phase | Location | Why |
|
|
83
|
+
|-------|----------|-----|
|
|
84
|
+
| Step 3 Phase 1 | Before implementing | User confirms implementation approach |
|
|
85
|
+
|
|
86
|
+
**Where Claude Code executes autonomously:**
|
|
87
|
+
- Step 0: Create additional scenarios (if first stable chore)
|
|
88
|
+
- Step 1: Scenario analysis
|
|
89
|
+
- Step 2: Speed mode implementation review
|
|
90
|
+
- Step 3 Phase 2: Autonomous execution loop
|
|
91
|
+
- Step 4: Completion check and routing
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
65
95
|
## ⚠️ CRITICAL: Stable Mode Workflow Requirements for External Products
|
|
66
96
|
|
|
67
97
|
**If the product is EXTERNAL (project_state: 'external'), DO NOT mark features as complete after stable mode.**
|
|
@@ -93,6 +123,11 @@ The validation will require you to either:
|
|
|
93
123
|
|
|
94
124
|
## Implementation Steps
|
|
95
125
|
|
|
126
|
+
<!-- ═══════════════════════════════════════════════════════════════════════════
|
|
127
|
+
PHASE 1: AUTONOMOUS SETUP
|
|
128
|
+
No user input required - Claude Code executes independently
|
|
129
|
+
═══════════════════════════════════════════════════════════════════════════ -->
|
|
130
|
+
|
|
96
131
|
### Step 0: Create Additional Scenarios (If First Stable Chore)
|
|
97
132
|
|
|
98
133
|
**CRITICAL:** If this is the FIRST stable mode chore for this feature, you must ADD edge case scenarios and step definitions.
|
|
@@ -144,43 +179,43 @@ Scenario: [Edge case title]
|
|
|
144
179
|
**Code to get scenario (with error handling):**
|
|
145
180
|
|
|
146
181
|
```javascript
|
|
182
|
+
// --- Imports ---
|
|
147
183
|
const { getCurrentWork } = require('../../lib/current-work');
|
|
148
184
|
const { getDb } = require('../../lib/database');
|
|
149
185
|
const fs = require('fs');
|
|
150
186
|
const path = require('path');
|
|
151
187
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
// Error handling: Check if current work exists
|
|
156
|
-
if (!currentWork) {
|
|
157
|
-
console.error('❌ No current work found. Run: jettypod work start <chore-id>');
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
188
|
+
(async () => {
|
|
189
|
+
try {
|
|
190
|
+
const currentWork = await getCurrentWork();
|
|
160
191
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
192
|
+
// --- Validate current work ---
|
|
193
|
+
if (!currentWork) {
|
|
194
|
+
console.error('❌ No current work found. Run: jettypod work start <chore-id>');
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
166
197
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
// Error handling: Database errors
|
|
170
|
-
if (err) {
|
|
171
|
-
console.error('❌ Database error:', err.message);
|
|
172
|
-
db.close();
|
|
198
|
+
if (!currentWork.parent_id) {
|
|
199
|
+
console.error('❌ Current work has no parent feature. This chore must be part of a feature.');
|
|
173
200
|
return;
|
|
174
201
|
}
|
|
175
202
|
|
|
176
|
-
//
|
|
203
|
+
// --- Get parent feature ---
|
|
204
|
+
const db = getDb();
|
|
205
|
+
const feature = await new Promise((resolve, reject) => {
|
|
206
|
+
db.get('SELECT * FROM work_items WHERE id = ?', [currentWork.parent_id], (err, row) => {
|
|
207
|
+
if (err) reject(err);
|
|
208
|
+
else resolve(row);
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// --- Validate feature ---
|
|
177
213
|
if (!feature) {
|
|
178
214
|
console.error('❌ Parent feature not found in database.');
|
|
179
215
|
db.close();
|
|
180
216
|
return;
|
|
181
217
|
}
|
|
182
218
|
|
|
183
|
-
// Error handling: No scenario file
|
|
184
219
|
if (!feature.scenario_file) {
|
|
185
220
|
console.error('❌ Feature has no scenario_file. Cannot determine what to implement.');
|
|
186
221
|
console.log('Suggestion: Create a scenario file and update the feature.');
|
|
@@ -188,9 +223,9 @@ try {
|
|
|
188
223
|
return;
|
|
189
224
|
}
|
|
190
225
|
|
|
226
|
+
// --- Validate scenario file exists ---
|
|
191
227
|
const scenarioPath = path.join(process.cwd(), feature.scenario_file);
|
|
192
228
|
|
|
193
|
-
// Error handling: Scenario file doesn't exist
|
|
194
229
|
if (!fs.existsSync(scenarioPath)) {
|
|
195
230
|
console.error(`❌ Scenario file not found: ${scenarioPath}`);
|
|
196
231
|
console.log('Suggestion: Create the scenario file or update the feature.scenario_file path.');
|
|
@@ -198,7 +233,7 @@ try {
|
|
|
198
233
|
return;
|
|
199
234
|
}
|
|
200
235
|
|
|
201
|
-
//
|
|
236
|
+
// --- Read scenario content ---
|
|
202
237
|
let scenarioContent;
|
|
203
238
|
try {
|
|
204
239
|
scenarioContent = fs.readFileSync(scenarioPath, 'utf8');
|
|
@@ -208,7 +243,7 @@ try {
|
|
|
208
243
|
return;
|
|
209
244
|
}
|
|
210
245
|
|
|
211
|
-
//
|
|
246
|
+
// --- Validate content ---
|
|
212
247
|
if (!scenarioContent || scenarioContent.trim().length === 0) {
|
|
213
248
|
console.error('❌ Scenario file is empty.');
|
|
214
249
|
db.close();
|
|
@@ -217,28 +252,27 @@ try {
|
|
|
217
252
|
|
|
218
253
|
// Parse all scenarios...
|
|
219
254
|
db.close();
|
|
220
|
-
})
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
255
|
+
} catch (err) {
|
|
256
|
+
console.error('❌ Unexpected error in Step 1:', err.message);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
})();
|
|
225
260
|
```
|
|
226
261
|
|
|
227
262
|
**Identify target scenario (with error handling):**
|
|
228
263
|
|
|
229
264
|
```javascript
|
|
230
|
-
// Parse scenarios from Gherkin
|
|
265
|
+
// --- Parse scenarios from Gherkin ---
|
|
231
266
|
const scenarios = [];
|
|
232
267
|
const scenarioBlocks = scenarioContent.split(/\nScenario:/);
|
|
233
268
|
|
|
234
|
-
// Error handling: No scenarios found
|
|
235
269
|
if (scenarioBlocks.length < 2) {
|
|
236
270
|
console.error('❌ No scenarios found in scenario file.');
|
|
237
271
|
console.log('Suggestion: Add Gherkin scenarios to the feature file.');
|
|
238
272
|
return;
|
|
239
273
|
}
|
|
240
274
|
|
|
241
|
-
//
|
|
275
|
+
// --- Extract scenario titles ---
|
|
242
276
|
for (let i = 1; i < scenarioBlocks.length; i++) {
|
|
243
277
|
const block = 'Scenario:' + scenarioBlocks[i];
|
|
244
278
|
const titleMatch = block.match(/Scenario:\s*(.+)/);
|
|
@@ -246,11 +280,11 @@ for (let i = 1; i < scenarioBlocks.length; i++) {
|
|
|
246
280
|
scenarios.push({ title, content: block });
|
|
247
281
|
}
|
|
248
282
|
|
|
249
|
-
// Match scenario to chore
|
|
283
|
+
// --- Match scenario to chore ---
|
|
250
284
|
const choreDesc = currentWork.description.toLowerCase();
|
|
251
285
|
let targetScenario = null;
|
|
252
286
|
|
|
253
|
-
// Try
|
|
287
|
+
// --- Try match by scenario number ---
|
|
254
288
|
const scenarioNumMatch = choreDesc.match(/scenario\s+(\d+)/);
|
|
255
289
|
if (scenarioNumMatch) {
|
|
256
290
|
const num = parseInt(scenarioNumMatch[1]);
|
|
@@ -259,14 +293,12 @@ if (scenarioNumMatch) {
|
|
|
259
293
|
}
|
|
260
294
|
}
|
|
261
295
|
|
|
262
|
-
// Try
|
|
296
|
+
// --- Try match by keywords ---
|
|
263
297
|
if (!targetScenario) {
|
|
264
298
|
for (const scenario of scenarios) {
|
|
265
299
|
const scenarioLower = scenario.title.toLowerCase();
|
|
266
|
-
// Skip happy path
|
|
267
|
-
if (scenarios.indexOf(scenario) === 0) continue;
|
|
300
|
+
if (scenarios.indexOf(scenario) === 0) continue; // Skip happy path
|
|
268
301
|
|
|
269
|
-
// Match keywords from chore description
|
|
270
302
|
const keywords = choreDesc.split(/\s+/).filter(w => w.length > 3);
|
|
271
303
|
const matches = keywords.filter(k => scenarioLower.includes(k));
|
|
272
304
|
|
|
@@ -277,7 +309,7 @@ if (!targetScenario) {
|
|
|
277
309
|
}
|
|
278
310
|
}
|
|
279
311
|
|
|
280
|
-
//
|
|
312
|
+
// --- Handle no match ---
|
|
281
313
|
if (!targetScenario) {
|
|
282
314
|
console.error('❌ Cannot match chore to any scenario in feature file.');
|
|
283
315
|
console.log('Available scenarios:');
|
|
@@ -318,35 +350,28 @@ Now reviewing speed mode implementation...
|
|
|
318
350
|
**Find speed mode files (with error handling):**
|
|
319
351
|
|
|
320
352
|
```javascript
|
|
353
|
+
// --- Imports and setup ---
|
|
321
354
|
const { exec } = require('child_process');
|
|
322
355
|
const util = require('util');
|
|
323
356
|
const execPromise = util.promisify(exec);
|
|
324
357
|
|
|
325
|
-
// Try to find files from git history
|
|
326
358
|
let speedModeFiles = [];
|
|
327
359
|
|
|
328
360
|
try {
|
|
329
|
-
//
|
|
361
|
+
// --- Search git history for feature commits ---
|
|
330
362
|
const featureName = feature.title.toLowerCase().replace(/\s+/g, '-');
|
|
331
363
|
const { stdout: gitLog } = await execPromise(
|
|
332
364
|
`git log --oneline --all --grep="${featureName}" -10`
|
|
333
365
|
);
|
|
334
366
|
|
|
335
|
-
//
|
|
367
|
+
// --- Handle no commits found ---
|
|
336
368
|
if (!gitLog || gitLog.trim().length === 0) {
|
|
337
369
|
console.log('⚠️ No git commits found for this feature.');
|
|
338
|
-
console.log('Asking user for files created in speed mode...');
|
|
339
|
-
|
|
340
|
-
// Ask user which files were created
|
|
341
370
|
console.log('\n📝 Which files did speed mode create/modify for this feature?');
|
|
342
|
-
console.log('(List file paths, one per line, or type "none" if no files exist)\n');
|
|
343
|
-
|
|
344
|
-
// Wait for user response...
|
|
345
|
-
// If user says "none", handle gracefully
|
|
346
371
|
return;
|
|
347
372
|
}
|
|
348
373
|
|
|
349
|
-
//
|
|
374
|
+
// --- Extract files from commits ---
|
|
350
375
|
const commits = gitLog.trim().split('\n').map(line => line.split(' ')[0]);
|
|
351
376
|
|
|
352
377
|
for (const commit of commits) {
|
|
@@ -355,24 +380,16 @@ try {
|
|
|
355
380
|
const fileList = files.trim().split('\n').filter(f => f.length > 0);
|
|
356
381
|
speedModeFiles.push(...fileList);
|
|
357
382
|
} catch (diffErr) {
|
|
358
|
-
// Ignore errors for individual commits
|
|
359
383
|
continue;
|
|
360
384
|
}
|
|
361
385
|
}
|
|
362
386
|
|
|
363
|
-
// Remove duplicates
|
|
387
|
+
// --- Remove duplicates and validate ---
|
|
364
388
|
speedModeFiles = [...new Set(speedModeFiles)];
|
|
365
389
|
|
|
366
|
-
// Error handling: No files found
|
|
367
390
|
if (speedModeFiles.length === 0) {
|
|
368
391
|
console.error('❌ No files found in git history for this feature.');
|
|
369
|
-
console.log('
|
|
370
|
-
console.log(' 1. Specify files manually');
|
|
371
|
-
console.log(' 2. Check if speed mode committed changes');
|
|
372
|
-
console.log(' 3. Start fresh if speed mode was not completed\n');
|
|
373
|
-
|
|
374
|
-
// Ask user for files
|
|
375
|
-
console.log('📝 Which files should be reviewed? (or type "start-fresh" to begin from scratch)');
|
|
392
|
+
console.log('📝 Which files should be reviewed?');
|
|
376
393
|
return;
|
|
377
394
|
}
|
|
378
395
|
|
|
@@ -380,26 +397,22 @@ try {
|
|
|
380
397
|
|
|
381
398
|
} catch (gitErr) {
|
|
382
399
|
console.error('⚠️ Git error:', gitErr.message);
|
|
383
|
-
console.log('Falling back to manual file specification...\n');
|
|
384
|
-
|
|
385
400
|
console.log('📝 Which files did speed mode create/modify?');
|
|
386
401
|
return;
|
|
387
402
|
}
|
|
388
403
|
|
|
389
|
-
// Validate files are readable
|
|
404
|
+
// --- Validate files are readable ---
|
|
390
405
|
const readableFiles = [];
|
|
391
406
|
const unreadableFiles = [];
|
|
392
407
|
|
|
393
408
|
for (const filePath of speedModeFiles) {
|
|
394
409
|
const fullPath = path.join(process.cwd(), filePath);
|
|
395
410
|
|
|
396
|
-
// Error handling: File doesn't exist
|
|
397
411
|
if (!fs.existsSync(fullPath)) {
|
|
398
412
|
console.log(`⚠️ File no longer exists: ${filePath}`);
|
|
399
413
|
continue;
|
|
400
414
|
}
|
|
401
415
|
|
|
402
|
-
// Error handling: File not readable
|
|
403
416
|
try {
|
|
404
417
|
fs.accessSync(fullPath, fs.constants.R_OK);
|
|
405
418
|
readableFiles.push(filePath);
|
|
@@ -409,16 +422,15 @@ for (const filePath of speedModeFiles) {
|
|
|
409
422
|
}
|
|
410
423
|
}
|
|
411
424
|
|
|
412
|
-
//
|
|
425
|
+
// --- Final validation ---
|
|
413
426
|
if (readableFiles.length === 0) {
|
|
414
427
|
console.error('❌ No readable speed mode files found.');
|
|
415
|
-
console.log('
|
|
416
|
-
console.log('\nSuggestion: Verify that speed mode was completed or start implementation from scratch.');
|
|
428
|
+
console.log('Suggestion: Verify speed mode was completed or start from scratch.');
|
|
417
429
|
return;
|
|
418
430
|
}
|
|
419
431
|
|
|
420
432
|
if (unreadableFiles.length > 0) {
|
|
421
|
-
console.log(`⚠️ ${unreadableFiles.length} files cannot be read - skipping
|
|
433
|
+
console.log(`⚠️ ${unreadableFiles.length} files cannot be read - skipping`);
|
|
422
434
|
}
|
|
423
435
|
|
|
424
436
|
speedModeFiles = readableFiles;
|
|
@@ -452,6 +464,11 @@ Now proposing comprehensive implementation...
|
|
|
452
464
|
|
|
453
465
|
**Move to Step 3 automatically.**
|
|
454
466
|
|
|
467
|
+
<!-- ═══════════════════════════════════════════════════════════════════════════
|
|
468
|
+
PHASE 2: USER CONFIRMATION REQUIRED
|
|
469
|
+
⚡ ASYNC BOUNDARY - Must wait for user response before proceeding
|
|
470
|
+
═══════════════════════════════════════════════════════════════════════════ -->
|
|
471
|
+
|
|
455
472
|
### Step 3: Propose and Execute Comprehensive Implementation
|
|
456
473
|
|
|
457
474
|
**Two phases: Propose (get user confirmation) → Execute (autonomous)**
|
|
@@ -490,26 +507,40 @@ Sound good? I'll implement this autonomously once you confirm.
|
|
|
490
507
|
|
|
491
508
|
If user adjusts: revise proposal and confirm again.
|
|
492
509
|
|
|
510
|
+
<!-- ═══════════════════════════════════════════════════════════════════════════
|
|
511
|
+
PHASE 3: AUTONOMOUS EXECUTION
|
|
512
|
+
User has confirmed - Claude Code executes iteration loop independently
|
|
513
|
+
═══════════════════════════════════════════════════════════════════════════ -->
|
|
514
|
+
|
|
493
515
|
#### Phase 2: Autonomous Execution
|
|
494
516
|
|
|
495
517
|
**CRITICAL:** After user confirms, Claude Code executes autonomously - no permission needed for individual code changes.
|
|
496
518
|
|
|
497
519
|
**Execution loop (with iteration limits and error handling):**
|
|
498
520
|
|
|
521
|
+
<!-- ┌─────────────────────────────────────────────────────────────────────────┐
|
|
522
|
+
│ 🔄 ITERATION LOOP: Stable Mode Scenario │
|
|
523
|
+
│ │
|
|
524
|
+
│ Progress Tracking: │
|
|
525
|
+
│ • Display: "Iteration X/10" at start of each cycle │
|
|
526
|
+
│ • Track: scenarios passing, steps passing, newly passing │
|
|
527
|
+
│ • Goal: Target scenario + all scenarios pass │
|
|
528
|
+
│ │
|
|
529
|
+
│ Return Points: │
|
|
530
|
+
│ • CHECKPOINT_ITERATION: Resume at specific iteration number │
|
|
531
|
+
│ • CHECKPOINT_SCENARIO: Resume with known scenario status │
|
|
532
|
+
│ • If session interrupted, can resume from last known iteration │
|
|
533
|
+
└─────────────────────────────────────────────────────────────────────────┘ -->
|
|
534
|
+
|
|
499
535
|
```javascript
|
|
500
|
-
//
|
|
536
|
+
// --- Imports ---
|
|
501
537
|
const {
|
|
502
|
-
MAX_ITERATIONS,
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
runBddScenarioWithTimeout,
|
|
506
|
-
getScenarioLineByName,
|
|
507
|
-
parseTestProgress,
|
|
508
|
-
extractErrors,
|
|
509
|
-
findNewlyPassingSteps
|
|
538
|
+
MAX_ITERATIONS, TEST_TIMEOUT, runBddTestWithTimeout,
|
|
539
|
+
runBddScenarioWithTimeout, getScenarioLineByName,
|
|
540
|
+
parseTestProgress, extractErrors, findNewlyPassingSteps
|
|
510
541
|
} = require('../../.claude/skills/speed-mode/test-runner');
|
|
511
542
|
|
|
512
|
-
//
|
|
543
|
+
// --- Find scenario line number ---
|
|
513
544
|
const scenarioLine = getScenarioLineByName(feature.scenario_file, targetScenario.title);
|
|
514
545
|
if (!scenarioLine) {
|
|
515
546
|
console.error('❌ Cannot find scenario line number for:', targetScenario.title);
|
|
@@ -520,42 +551,37 @@ let iteration = 0;
|
|
|
520
551
|
let scenarioPasses = false;
|
|
521
552
|
let previousResult = null;
|
|
522
553
|
|
|
554
|
+
// --- Main iteration loop ---
|
|
555
|
+
// 📊 PROGRESS: Iteration {iteration}/{MAX_ITERATIONS} | Scenario: {scenarioPasses ? 'PASS' : 'FAIL'}
|
|
523
556
|
while (!scenarioPasses && iteration < MAX_ITERATIONS) {
|
|
524
557
|
iteration++;
|
|
525
558
|
console.log(`\n🔄 Iteration ${iteration}/${MAX_ITERATIONS}`);
|
|
526
559
|
|
|
527
|
-
//
|
|
560
|
+
// --- Make code changes ---
|
|
528
561
|
try {
|
|
529
|
-
// Add error handling, validation, error messages, etc.
|
|
530
562
|
console.log('✍️ Adding error handling to [file]...');
|
|
531
563
|
// ... use Edit tool ...
|
|
532
564
|
console.log('✅ Updated [file]');
|
|
533
565
|
} catch (editErr) {
|
|
534
566
|
console.error('❌ Error modifying files:', editErr.message);
|
|
535
|
-
console.log('Retrying with adjusted approach...');
|
|
536
567
|
continue;
|
|
537
568
|
}
|
|
538
569
|
|
|
539
|
-
//
|
|
570
|
+
// --- Run tests ---
|
|
540
571
|
console.log('🧪 Running tests...');
|
|
541
|
-
|
|
542
572
|
const result = await runBddScenarioWithTimeout(feature.scenario_file, scenarioLine, TEST_TIMEOUT);
|
|
543
573
|
|
|
544
|
-
//
|
|
574
|
+
// --- Handle timeout ---
|
|
545
575
|
if (result.timedOut) {
|
|
546
576
|
console.error('❌ Tests timed out after 60 seconds');
|
|
547
|
-
console.log('This might indicate an infinite loop or hung process.');
|
|
548
577
|
console.log('Suggestion: Check for blocking operations or missing async/await');
|
|
549
578
|
break;
|
|
550
579
|
}
|
|
551
580
|
|
|
552
|
-
// Parse
|
|
581
|
+
// --- Parse and track progress ---
|
|
553
582
|
const currentResult = parseTestProgress(result.stdout);
|
|
554
|
-
|
|
555
|
-
// Track progress
|
|
556
583
|
const newlyPassing = findNewlyPassingSteps(previousResult, currentResult);
|
|
557
584
|
|
|
558
|
-
// Display progress
|
|
559
585
|
console.log(`\n📊 Progress: ${currentResult.passed}/${currentResult.total} steps passing`);
|
|
560
586
|
|
|
561
587
|
if (newlyPassing.length > 0) {
|
|
@@ -563,11 +589,11 @@ while (!scenarioPasses && iteration < MAX_ITERATIONS) {
|
|
|
563
589
|
newlyPassing.forEach(step => console.log(` • ${step}`));
|
|
564
590
|
}
|
|
565
591
|
|
|
566
|
-
// Check
|
|
592
|
+
// --- Check for success ---
|
|
567
593
|
if (currentResult.passed === currentResult.total && currentResult.total > 0) {
|
|
568
594
|
console.log('\n✅ Target scenario passing!');
|
|
569
595
|
|
|
570
|
-
// Run full verification
|
|
596
|
+
// --- Run full verification ---
|
|
571
597
|
console.log('\n🔍 Running full verification (all scenarios)...');
|
|
572
598
|
const fullResult = await runBddTestWithTimeout(feature.scenario_file, TEST_TIMEOUT);
|
|
573
599
|
|
|
@@ -575,31 +601,27 @@ while (!scenarioPasses && iteration < MAX_ITERATIONS) {
|
|
|
575
601
|
console.log('⚠️ Full verification timed out');
|
|
576
602
|
} else if (fullResult.exitCode !== 0) {
|
|
577
603
|
const fullProgress = parseTestProgress(fullResult.stdout + fullResult.stderr);
|
|
578
|
-
console.log(`⚠️
|
|
579
|
-
console.log('Continuing iterations to fix regressions...');
|
|
604
|
+
console.log(`⚠️ Regressions found: ${fullProgress.total - fullProgress.passed} scenarios failing`);
|
|
580
605
|
scenarioPasses = false;
|
|
581
606
|
} else {
|
|
582
|
-
console.log('✅ Full verification passed
|
|
607
|
+
console.log('✅ Full verification passed!');
|
|
583
608
|
scenarioPasses = true;
|
|
584
609
|
}
|
|
585
610
|
} else {
|
|
611
|
+
// --- Display errors ---
|
|
586
612
|
console.log(`\n❌ ${currentResult.total - currentResult.passed} scenarios still failing`);
|
|
587
|
-
|
|
588
|
-
// Extract and display errors
|
|
589
613
|
const errors = extractErrors(result.stdout + result.stderr);
|
|
590
614
|
if (errors.errors.length > 0) {
|
|
591
615
|
console.log('\n🔧 Next failure to address:');
|
|
592
|
-
|
|
593
|
-
console.log(`
|
|
594
|
-
console.log(` Error: ${firstError.message}`);
|
|
616
|
+
console.log(` Step: ${errors.errors[0].step}`);
|
|
617
|
+
console.log(` Error: ${errors.errors[0].message}`);
|
|
595
618
|
}
|
|
596
619
|
}
|
|
597
620
|
|
|
598
|
-
// Update previousResult for next iteration
|
|
599
621
|
previousResult = currentResult;
|
|
600
622
|
}
|
|
601
623
|
|
|
602
|
-
//
|
|
624
|
+
// --- Handle max iterations reached ---
|
|
603
625
|
if (!scenarioPasses && iteration >= MAX_ITERATIONS) {
|
|
604
626
|
console.error('\n❌ Maximum iterations reached without passing scenario');
|
|
605
627
|
console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
@@ -609,15 +631,7 @@ if (!scenarioPasses && iteration >= MAX_ITERATIONS) {
|
|
|
609
631
|
console.log('• Scenario requirements may need clarification');
|
|
610
632
|
console.log('• Implementation approach may need rethinking');
|
|
611
633
|
console.log('• External dependencies may be missing');
|
|
612
|
-
console.log('
|
|
613
|
-
console.log('\nSuggestions:');
|
|
614
|
-
console.log('1. Review the scenario and verify it\'s achievable');
|
|
615
|
-
console.log('2. Check test output for specific failure patterns');
|
|
616
|
-
console.log('3. Try a different implementation approach');
|
|
617
|
-
console.log('4. Ask for help if stuck\n');
|
|
618
|
-
|
|
619
|
-
// Ask user how to proceed
|
|
620
|
-
console.log('How would you like to proceed?');
|
|
634
|
+
console.log('\nHow would you like to proceed?');
|
|
621
635
|
console.log(' 1. Review changes made so far');
|
|
622
636
|
console.log(' 2. Try a different approach');
|
|
623
637
|
console.log(' 3. Debug manually');
|
|
@@ -657,31 +671,16 @@ Implementation complete:
|
|
|
657
671
|
• All scenarios: ✅ Passing
|
|
658
672
|
```
|
|
659
673
|
|
|
660
|
-
**Run unit test coverage
|
|
661
|
-
```
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
// List of .js files modified during implementation (exclude test files)
|
|
667
|
-
];
|
|
668
|
-
|
|
669
|
-
console.log('\n📊 Running unit test coverage...');
|
|
670
|
-
const coverageResult = trackFeatureCoverage(implementationFiles, {
|
|
671
|
-
coverageThreshold: 80, // 80% coverage target
|
|
672
|
-
verbose: false
|
|
673
|
-
});
|
|
674
|
-
|
|
675
|
-
console.log(coverageResult.report);
|
|
676
|
-
|
|
677
|
-
if (!coverageResult.success) {
|
|
678
|
-
console.log('\n⚠️ Coverage below 80% threshold');
|
|
679
|
-
console.log('Consider adding more unit tests to cover edge cases');
|
|
680
|
-
console.log('\nTest files:');
|
|
681
|
-
coverageResult.testFiles.forEach(f => console.log(` • ${f}`));
|
|
682
|
-
}
|
|
674
|
+
**Run unit test coverage (if configured):**
|
|
675
|
+
```bash
|
|
676
|
+
# If project has coverage configured, run it:
|
|
677
|
+
npm run test:coverage
|
|
678
|
+
# Or with Jest:
|
|
679
|
+
npx jest --coverage
|
|
683
680
|
```
|
|
684
681
|
|
|
682
|
+
Note: Coverage tracking is project-specific. Check your package.json for the appropriate coverage command.
|
|
683
|
+
|
|
685
684
|
**Display coverage summary:**
|
|
686
685
|
```
|
|
687
686
|
📊 Unit Test Coverage Report
|
|
@@ -694,45 +693,77 @@ Lines: ✅ 84.90%
|
|
|
694
693
|
✅ All coverage metrics meet 80% threshold
|
|
695
694
|
```
|
|
696
695
|
|
|
696
|
+
<!-- ═══════════════════════════════════════════════════════════════════════════
|
|
697
|
+
PHASE 4: COMPLETION AND ROUTING
|
|
698
|
+
Conditional phase - route based on project state (internal vs external)
|
|
699
|
+
═══════════════════════════════════════════════════════════════════════════ -->
|
|
700
|
+
|
|
697
701
|
### Step 4: Check for Production Mode (When All Stable Chores Complete)
|
|
698
702
|
|
|
699
703
|
**CRITICAL: This step ONLY happens when ALL stable chores for the feature are complete. Otherwise skip to marking chore as done.**
|
|
700
704
|
|
|
705
|
+
<!-- ┌─────────────────────────────────────────────────────────────────────────┐
|
|
706
|
+
│ 🔍 COMPLETION CHECK: Stable Chores │
|
|
707
|
+
│ │
|
|
708
|
+
│ Progress Tracking: │
|
|
709
|
+
│ • Query: Count incomplete chores for feature │
|
|
710
|
+
│ • Display: "X chores remaining" or "All complete" │
|
|
711
|
+
│ • Action: Route based on project state (internal vs external) │
|
|
712
|
+
│ │
|
|
713
|
+
│ Return Point: │
|
|
714
|
+
│ • CHECKPOINT_CHORE_COUNT: Known incomplete count from last check │
|
|
715
|
+
│ • CHECKPOINT_PROJECT_STATE: Known project state for routing │
|
|
716
|
+
│ • If session interrupted, re-query to get current counts │
|
|
717
|
+
└─────────────────────────────────────────────────────────────────────────┘ -->
|
|
718
|
+
|
|
701
719
|
**Check completion status and project state:**
|
|
702
720
|
```javascript
|
|
721
|
+
// --- Imports ---
|
|
703
722
|
const { getDb } = require('../../lib/database');
|
|
704
723
|
const { getCurrentWork } = require('../../lib/current-work');
|
|
705
724
|
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
const
|
|
710
|
-
db
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
725
|
+
(async () => {
|
|
726
|
+
// --- Get current work context ---
|
|
727
|
+
const currentWork = await getCurrentWork();
|
|
728
|
+
const featureId = currentWork.parent_id;
|
|
729
|
+
const db = getDb();
|
|
730
|
+
|
|
731
|
+
// --- Query for incomplete chores ---
|
|
732
|
+
const result = await new Promise((resolve, reject) => {
|
|
733
|
+
db.get(`
|
|
734
|
+
SELECT COUNT(*) as incomplete_count
|
|
735
|
+
FROM work_items
|
|
736
|
+
WHERE parent_id = ?
|
|
737
|
+
AND type = 'chore'
|
|
738
|
+
AND status NOT IN ('done', 'cancelled')
|
|
739
|
+
`, [featureId], (err, row) => {
|
|
740
|
+
if (err) reject(err);
|
|
741
|
+
else resolve(row);
|
|
742
|
+
});
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
// --- Handle all chores complete ---
|
|
717
746
|
if (result.incomplete_count === 0) {
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
// Internal project - stable mode is complete
|
|
724
|
-
console.log('Internal project - no production mode needed');
|
|
725
|
-
} else {
|
|
726
|
-
// External project - invoke production-mode skill
|
|
727
|
-
console.log('External project - auto-generating production chores from standards');
|
|
728
|
-
}
|
|
747
|
+
const config = await new Promise((resolve, reject) => {
|
|
748
|
+
db.get('SELECT project_state FROM project_config WHERE id = 1', (err, row) => {
|
|
749
|
+
if (err) reject(err);
|
|
750
|
+
else resolve(row);
|
|
751
|
+
});
|
|
729
752
|
});
|
|
753
|
+
const projectState = config?.project_state || 'internal';
|
|
754
|
+
|
|
755
|
+
// --- Route based on project state ---
|
|
756
|
+
if (projectState === 'internal') {
|
|
757
|
+
console.log('Internal project - no production mode needed');
|
|
758
|
+
} else {
|
|
759
|
+
console.log('External project - auto-generating production chores');
|
|
760
|
+
}
|
|
730
761
|
} else {
|
|
731
|
-
// More chores remain - just mark this one done
|
|
732
762
|
console.log(`\n📋 ${result.incomplete_count} stable mode chores remaining`);
|
|
733
763
|
}
|
|
764
|
+
|
|
734
765
|
db.close();
|
|
735
|
-
});
|
|
766
|
+
})();
|
|
736
767
|
```
|
|
737
768
|
|
|
738
769
|
**CRITICAL: Check project state to determine next step:**
|