jettypod 4.4.34 → 4.4.35
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/jettypod.js +275 -18
- package/package.json +1 -1
package/jettypod.js
CHANGED
|
@@ -257,6 +257,11 @@ Skills auto-activate and MUST complete their full workflow:
|
|
|
257
257
|
❌ DO NOT manually create chores when a skill should generate them
|
|
258
258
|
✅ ALWAYS let skills complete autonomously before taking manual actions
|
|
259
259
|
|
|
260
|
+
## 🔄 Session Start: Check for Interrupted Workflows
|
|
261
|
+
On session start, run: \`jettypod workflow resume\`
|
|
262
|
+
If an interrupted workflow is found, ASK the user if they want to resume it.
|
|
263
|
+
Do not assume - the user may want to start fresh or work on something else.
|
|
264
|
+
|
|
260
265
|
## Basic Commands (for non-workflow operations)
|
|
261
266
|
jettypod work create epic "<title>"
|
|
262
267
|
jettypod work create feature "<title>" --parent=<id>
|
|
@@ -2297,40 +2302,292 @@ Quick commands:
|
|
|
2297
2302
|
case 'workflow': {
|
|
2298
2303
|
const workflowSubcommand = args[0];
|
|
2299
2304
|
|
|
2300
|
-
if (workflowSubcommand === '
|
|
2301
|
-
const { getDb } = require('./lib/database');
|
|
2302
|
-
const {
|
|
2305
|
+
if (workflowSubcommand === 'status') {
|
|
2306
|
+
const { getDb, waitForMigrations } = require('./lib/database');
|
|
2307
|
+
const { getCurrentBranch } = require('./lib/workflow-checkpoint');
|
|
2308
|
+
|
|
2309
|
+
try {
|
|
2310
|
+
const db = getDb();
|
|
2311
|
+
await waitForMigrations();
|
|
2312
|
+
const currentBranch = getCurrentBranch();
|
|
2313
|
+
|
|
2314
|
+
const checkpoints = await new Promise((resolve, reject) => {
|
|
2315
|
+
db.all(
|
|
2316
|
+
`SELECT * FROM workflow_checkpoints ORDER BY updated_at DESC`,
|
|
2317
|
+
[],
|
|
2318
|
+
(err, rows) => {
|
|
2319
|
+
if (err) reject(err);
|
|
2320
|
+
else resolve(rows || []);
|
|
2321
|
+
}
|
|
2322
|
+
);
|
|
2323
|
+
});
|
|
2324
|
+
|
|
2325
|
+
if (checkpoints.length === 0) {
|
|
2326
|
+
console.log('No interrupted workflows found.');
|
|
2327
|
+
} else {
|
|
2328
|
+
console.log('Interrupted Workflows');
|
|
2329
|
+
console.log('─'.repeat(90));
|
|
2330
|
+
console.log('');
|
|
2331
|
+
console.log(
|
|
2332
|
+
'ID'.padEnd(4) +
|
|
2333
|
+
'Skill'.padEnd(18) +
|
|
2334
|
+
'Step'.padEnd(10) +
|
|
2335
|
+
'Work Item'.padEnd(12) +
|
|
2336
|
+
'Branch'.padEnd(38) +
|
|
2337
|
+
'Last Active'
|
|
2338
|
+
);
|
|
2339
|
+
console.log('─'.repeat(90));
|
|
2340
|
+
|
|
2341
|
+
for (const cp of checkpoints) {
|
|
2342
|
+
const stepInfo = cp.total_steps
|
|
2343
|
+
? `${cp.current_step}/${cp.total_steps}`
|
|
2344
|
+
: `${cp.current_step}`;
|
|
2345
|
+
const isCurrent = cp.branch_name === currentBranch;
|
|
2346
|
+
const branchDisplay = cp.branch_name.length > 35
|
|
2347
|
+
? cp.branch_name.substring(0, 32) + '...'
|
|
2348
|
+
: cp.branch_name;
|
|
2349
|
+
const branchWithMarker = branchDisplay + (isCurrent ? ' *' : '');
|
|
2350
|
+
|
|
2351
|
+
// Format time ago
|
|
2352
|
+
const updatedAt = new Date(cp.updated_at);
|
|
2353
|
+
const now = new Date();
|
|
2354
|
+
const diffMs = now - updatedAt;
|
|
2355
|
+
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
|
|
2356
|
+
const diffDays = Math.floor(diffHours / 24);
|
|
2357
|
+
const timeAgo = diffDays > 0 ? `${diffDays}d ago` : diffHours > 0 ? `${diffHours}h ago` : 'just now';
|
|
2358
|
+
|
|
2359
|
+
console.log(
|
|
2360
|
+
String(cp.id).padEnd(4) +
|
|
2361
|
+
cp.skill_name.padEnd(18) +
|
|
2362
|
+
stepInfo.padEnd(10) +
|
|
2363
|
+
`#${cp.work_item_id || '-'}`.padEnd(12) +
|
|
2364
|
+
branchWithMarker.padEnd(38) +
|
|
2365
|
+
timeAgo
|
|
2366
|
+
);
|
|
2367
|
+
}
|
|
2368
|
+
|
|
2369
|
+
console.log('');
|
|
2370
|
+
console.log(`${checkpoints.length} interrupted workflow(s)`);
|
|
2371
|
+
console.log('');
|
|
2372
|
+
console.log('Commands:');
|
|
2373
|
+
console.log(' jettypod workflow show <id> Show workflow details');
|
|
2374
|
+
console.log(' jettypod workflow resume <id> Resume a workflow');
|
|
2375
|
+
console.log(' jettypod workflow abort <id> Abort and clean up a workflow');
|
|
2376
|
+
}
|
|
2377
|
+
} catch (err) {
|
|
2378
|
+
console.error(`Error: ${err.message}`);
|
|
2379
|
+
process.exit(1);
|
|
2380
|
+
}
|
|
2381
|
+
} else if (workflowSubcommand === 'show') {
|
|
2382
|
+
const workflowId = parseInt(args[1]);
|
|
2383
|
+
|
|
2384
|
+
if (!workflowId) {
|
|
2385
|
+
console.log('Usage: jettypod workflow show <id>');
|
|
2386
|
+
console.log('');
|
|
2387
|
+
console.log('Use "jettypod workflow status" to see available workflow IDs.');
|
|
2388
|
+
process.exit(1);
|
|
2389
|
+
}
|
|
2390
|
+
|
|
2391
|
+
const { getDb, waitForMigrations } = require('./lib/database');
|
|
2303
2392
|
|
|
2304
2393
|
try {
|
|
2305
2394
|
const db = getDb();
|
|
2306
|
-
|
|
2307
|
-
|
|
2395
|
+
await waitForMigrations();
|
|
2396
|
+
|
|
2397
|
+
const checkpoint = await new Promise((resolve, reject) => {
|
|
2398
|
+
db.get(
|
|
2399
|
+
`SELECT * FROM workflow_checkpoints WHERE id = ?`,
|
|
2400
|
+
[workflowId],
|
|
2401
|
+
(err, row) => {
|
|
2402
|
+
if (err) reject(err);
|
|
2403
|
+
else resolve(row);
|
|
2404
|
+
}
|
|
2405
|
+
);
|
|
2406
|
+
});
|
|
2308
2407
|
|
|
2309
2408
|
if (!checkpoint) {
|
|
2310
|
-
console.log(
|
|
2409
|
+
console.log(`Error: No workflow found with ID ${workflowId}`);
|
|
2410
|
+
console.log('');
|
|
2411
|
+
console.log('Use "jettypod workflow status" to see available workflows.');
|
|
2412
|
+
process.exit(1);
|
|
2413
|
+
}
|
|
2414
|
+
|
|
2415
|
+
const stepInfo = checkpoint.total_steps
|
|
2416
|
+
? `Step ${checkpoint.current_step} of ${checkpoint.total_steps}`
|
|
2417
|
+
: `Step ${checkpoint.current_step}`;
|
|
2418
|
+
|
|
2419
|
+
console.log('Workflow Details');
|
|
2420
|
+
console.log('─'.repeat(50));
|
|
2421
|
+
console.log('');
|
|
2422
|
+
console.log(` ID: ${checkpoint.id}`);
|
|
2423
|
+
console.log(` Skill: ${checkpoint.skill_name}`);
|
|
2424
|
+
console.log(` Progress: ${stepInfo}`);
|
|
2425
|
+
console.log(` Work Item: ${checkpoint.work_item_id ? '#' + checkpoint.work_item_id : '-'}`);
|
|
2426
|
+
console.log(` Branch: ${checkpoint.branch_name}`);
|
|
2427
|
+
console.log(` Started: ${checkpoint.created_at}`);
|
|
2428
|
+
console.log(` Last Active: ${checkpoint.updated_at}`);
|
|
2429
|
+
console.log('');
|
|
2430
|
+
console.log('Actions:');
|
|
2431
|
+
console.log(` jettypod workflow resume ${workflowId} Continue this workflow`);
|
|
2432
|
+
console.log(` jettypod workflow abort ${workflowId} Cancel and clean up`);
|
|
2433
|
+
} catch (err) {
|
|
2434
|
+
console.error(`Error: ${err.message}`);
|
|
2435
|
+
process.exit(1);
|
|
2436
|
+
}
|
|
2437
|
+
} else if (workflowSubcommand === 'resume') {
|
|
2438
|
+
const { getDb, waitForMigrations } = require('./lib/database');
|
|
2439
|
+
const { getCheckpoint, getCurrentBranch } = require('./lib/workflow-checkpoint');
|
|
2440
|
+
|
|
2441
|
+
try {
|
|
2442
|
+
const db = getDb();
|
|
2443
|
+
await waitForMigrations();
|
|
2444
|
+
const currentBranch = getCurrentBranch();
|
|
2445
|
+
const workflowId = args[1] ? parseInt(args[1]) : null;
|
|
2446
|
+
|
|
2447
|
+
let checkpoint;
|
|
2448
|
+
|
|
2449
|
+
if (workflowId) {
|
|
2450
|
+
// Resume by ID - fetch specific workflow
|
|
2451
|
+
checkpoint = await new Promise((resolve, reject) => {
|
|
2452
|
+
db.get(
|
|
2453
|
+
`SELECT * FROM workflow_checkpoints WHERE id = ?`,
|
|
2454
|
+
[workflowId],
|
|
2455
|
+
(err, row) => {
|
|
2456
|
+
if (err) reject(err);
|
|
2457
|
+
else resolve(row);
|
|
2458
|
+
}
|
|
2459
|
+
);
|
|
2460
|
+
});
|
|
2461
|
+
|
|
2462
|
+
if (!checkpoint) {
|
|
2463
|
+
console.log(`Error: No workflow found with ID ${workflowId}`);
|
|
2464
|
+
console.log('');
|
|
2465
|
+
console.log('Use "jettypod workflow status" to see available workflows.');
|
|
2466
|
+
process.exit(1);
|
|
2467
|
+
}
|
|
2311
2468
|
} else {
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
: `Step ${checkpoint.current_step}`;
|
|
2469
|
+
// No ID - check current branch
|
|
2470
|
+
checkpoint = await getCheckpoint(db, currentBranch);
|
|
2315
2471
|
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
console.log(`Work Item: #${checkpoint.work_item_id}`);
|
|
2472
|
+
if (!checkpoint) {
|
|
2473
|
+
console.log('No interrupted workflow on current branch.');
|
|
2474
|
+
console.log('');
|
|
2475
|
+
console.log('Use "jettypod workflow status" to see workflows on other branches,');
|
|
2476
|
+
console.log('or specify an ID: "jettypod workflow resume <id>"');
|
|
2477
|
+
process.exit(0);
|
|
2323
2478
|
}
|
|
2324
2479
|
}
|
|
2480
|
+
|
|
2481
|
+
// Display resume info
|
|
2482
|
+
const stepInfo = checkpoint.total_steps
|
|
2483
|
+
? `Step ${checkpoint.current_step} of ${checkpoint.total_steps}`
|
|
2484
|
+
: `Step ${checkpoint.current_step}`;
|
|
2485
|
+
|
|
2486
|
+
const needsBranchSwitch = checkpoint.branch_name !== currentBranch;
|
|
2487
|
+
|
|
2488
|
+
console.log('Resuming Workflow');
|
|
2489
|
+
console.log('─'.repeat(50));
|
|
2490
|
+
console.log('');
|
|
2491
|
+
console.log(` Skill: ${checkpoint.skill_name}`);
|
|
2492
|
+
console.log(` Progress: ${stepInfo}`);
|
|
2493
|
+
console.log(` Work Item: ${checkpoint.work_item_id ? '#' + checkpoint.work_item_id : '-'}`);
|
|
2494
|
+
console.log(` Branch: ${checkpoint.branch_name}`);
|
|
2495
|
+
console.log('');
|
|
2496
|
+
|
|
2497
|
+
if (needsBranchSwitch) {
|
|
2498
|
+
console.log(`Switching to branch: ${checkpoint.branch_name}`);
|
|
2499
|
+
console.log('');
|
|
2500
|
+
}
|
|
2501
|
+
|
|
2502
|
+
console.log('[Triggering skill continuation]');
|
|
2503
|
+
// Note: Actual skill triggering would be implemented here
|
|
2504
|
+
// For now, this displays the intent to continue the workflow
|
|
2505
|
+
} catch (err) {
|
|
2506
|
+
console.error(`Error: ${err.message}`);
|
|
2507
|
+
process.exit(1);
|
|
2508
|
+
}
|
|
2509
|
+
} else if (workflowSubcommand === 'abort') {
|
|
2510
|
+
const workflowId = parseInt(args[1]);
|
|
2511
|
+
|
|
2512
|
+
if (!workflowId) {
|
|
2513
|
+
console.log('Usage: jettypod workflow abort <id>');
|
|
2514
|
+
console.log('');
|
|
2515
|
+
console.log('Use "jettypod workflow status" to see workflow IDs.');
|
|
2516
|
+
process.exit(1);
|
|
2517
|
+
}
|
|
2518
|
+
|
|
2519
|
+
const { getDb, waitForMigrations } = require('./lib/database');
|
|
2520
|
+
|
|
2521
|
+
try {
|
|
2522
|
+
const db = getDb();
|
|
2523
|
+
await waitForMigrations();
|
|
2524
|
+
|
|
2525
|
+
// First fetch the checkpoint to display info
|
|
2526
|
+
const checkpoint = await new Promise((resolve, reject) => {
|
|
2527
|
+
db.get(
|
|
2528
|
+
`SELECT * FROM workflow_checkpoints WHERE id = ?`,
|
|
2529
|
+
[workflowId],
|
|
2530
|
+
(err, row) => {
|
|
2531
|
+
if (err) reject(err);
|
|
2532
|
+
else resolve(row);
|
|
2533
|
+
}
|
|
2534
|
+
);
|
|
2535
|
+
});
|
|
2536
|
+
|
|
2537
|
+
if (!checkpoint) {
|
|
2538
|
+
console.log(`Error: No workflow found with ID ${workflowId}`);
|
|
2539
|
+
console.log('');
|
|
2540
|
+
console.log('Use "jettypod workflow status" to see available workflows.');
|
|
2541
|
+
process.exit(1);
|
|
2542
|
+
}
|
|
2543
|
+
|
|
2544
|
+
// Delete the checkpoint
|
|
2545
|
+
await new Promise((resolve, reject) => {
|
|
2546
|
+
db.run(
|
|
2547
|
+
`DELETE FROM workflow_checkpoints WHERE id = ?`,
|
|
2548
|
+
[workflowId],
|
|
2549
|
+
(err) => {
|
|
2550
|
+
if (err) reject(err);
|
|
2551
|
+
else resolve();
|
|
2552
|
+
}
|
|
2553
|
+
);
|
|
2554
|
+
});
|
|
2555
|
+
|
|
2556
|
+
// Display abort confirmation
|
|
2557
|
+
const stepInfo = checkpoint.total_steps
|
|
2558
|
+
? `Step ${checkpoint.current_step} of ${checkpoint.total_steps}`
|
|
2559
|
+
: `Step ${checkpoint.current_step}`;
|
|
2560
|
+
|
|
2561
|
+
console.log('Abort Workflow');
|
|
2562
|
+
console.log('─'.repeat(50));
|
|
2563
|
+
console.log('');
|
|
2564
|
+
console.log(` Skill: ${checkpoint.skill_name}`);
|
|
2565
|
+
console.log(` Progress: ${stepInfo}`);
|
|
2566
|
+
console.log(` Work Item: ${checkpoint.work_item_id ? '#' + checkpoint.work_item_id : '-'}`);
|
|
2567
|
+
console.log(` Branch: ${checkpoint.branch_name}`);
|
|
2568
|
+
console.log('');
|
|
2569
|
+
console.log('This will:');
|
|
2570
|
+
console.log(' - Delete the checkpoint from the database');
|
|
2571
|
+
console.log(' - NOT delete the branch or any code changes');
|
|
2572
|
+
console.log(' - Allow you to restart the skill fresh');
|
|
2573
|
+
console.log('');
|
|
2574
|
+
console.log('Checkpoint deleted. Workflow aborted.');
|
|
2575
|
+
console.log('');
|
|
2576
|
+
if (checkpoint.work_item_id) {
|
|
2577
|
+
console.log(`To restart: jettypod work start ${checkpoint.work_item_id}`);
|
|
2578
|
+
}
|
|
2325
2579
|
} catch (err) {
|
|
2326
2580
|
console.error(`Error: ${err.message}`);
|
|
2327
2581
|
process.exit(1);
|
|
2328
2582
|
}
|
|
2329
2583
|
} else {
|
|
2330
|
-
console.log('Usage: jettypod workflow
|
|
2584
|
+
console.log('Usage: jettypod workflow <command>');
|
|
2331
2585
|
console.log('');
|
|
2332
2586
|
console.log('Commands:');
|
|
2333
|
-
console.log('
|
|
2587
|
+
console.log(' status List all interrupted workflows');
|
|
2588
|
+
console.log(' show <id> Show workflow details');
|
|
2589
|
+
console.log(' resume [id] Resume a workflow');
|
|
2590
|
+
console.log(' abort <id> Abort and clean up a workflow');
|
|
2334
2591
|
}
|
|
2335
2592
|
break;
|
|
2336
2593
|
}
|