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.
Files changed (2) hide show
  1. package/jettypod.js +275 -18
  2. 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 === 'resume') {
2301
- const { getDb } = require('./lib/database');
2302
- const { getCheckpoint, getCurrentBranch } = require('./lib/workflow-checkpoint');
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
- const branchName = getCurrentBranch();
2307
- const checkpoint = await getCheckpoint(db, branchName);
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('No interrupted workflow found');
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
- const stepInfo = checkpoint.total_steps
2313
- ? `Step ${checkpoint.current_step} of ${checkpoint.total_steps}`
2314
- : `Step ${checkpoint.current_step}`;
2469
+ // No ID - check current branch
2470
+ checkpoint = await getCheckpoint(db, currentBranch);
2315
2471
 
2316
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2317
- console.log('Found interrupted workflow');
2318
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2319
- console.log(`Skill: ${checkpoint.skill_name}`);
2320
- console.log(stepInfo);
2321
- if (checkpoint.work_item_id) {
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 resume');
2584
+ console.log('Usage: jettypod workflow <command>');
2331
2585
  console.log('');
2332
2586
  console.log('Commands:');
2333
- console.log(' resume Check for and resume interrupted workflows');
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jettypod",
3
- "version": "4.4.34",
3
+ "version": "4.4.35",
4
4
  "description": "AI-powered development workflow manager with TDD, BDD, and automatic test generation",
5
5
  "main": "jettypod.js",
6
6
  "bin": {