groundcrew-cli 0.15.9 → 0.15.10

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/dist/index.js CHANGED
@@ -336,6 +336,44 @@ async function destroyAll() {
336
336
  }
337
337
  console.log(green("All session data and history deleted."));
338
338
  }
339
+ async function prune() {
340
+ const active = await readActiveSessions();
341
+ const activeIds = new Set(Object.keys(active));
342
+ let allDirs = [];
343
+ try {
344
+ allDirs = await fs.readdir(SESSIONS_DIR);
345
+ } catch {
346
+ }
347
+ if (allDirs.length === 0) {
348
+ console.log(dim("No sessions to prune."));
349
+ return;
350
+ }
351
+ let pruned = 0;
352
+ for (const dir of allDirs) {
353
+ if (activeIds.has(dir)) {
354
+ const entry = active[dir];
355
+ try {
356
+ process.kill(entry.pid, 0);
357
+ continue;
358
+ } catch {
359
+ }
360
+ }
361
+ const sessionDir = path.join(SESSIONS_DIR, dir);
362
+ await fs.rm(sessionDir, { recursive: true, force: true });
363
+ console.log(` ${green("pruned")} ${cyan(dir)}`);
364
+ pruned++;
365
+ if (activeIds.has(dir)) {
366
+ delete active[dir];
367
+ }
368
+ }
369
+ await fs.writeFile(ACTIVE_SESSIONS_FILE, JSON.stringify(active, null, 2));
370
+ if (pruned === 0) {
371
+ console.log(dim("No stale sessions to prune. All sessions are active."));
372
+ } else {
373
+ console.log(green(`
374
+ Pruned ${pruned} stale session(s).`));
375
+ }
376
+ }
339
377
  async function listSessionChoices() {
340
378
  const active = await readActiveSessions();
341
379
  const choices = [];
@@ -696,6 +734,7 @@ ${bold("Usage:")}
696
734
  groundcrew clear Clear all pending tasks
697
735
  groundcrew stop Stop all active sessions
698
736
  groundcrew stop --session <id> Stop a specific session
737
+ groundcrew prune Remove stale/ended sessions (keeps active)
699
738
  groundcrew destroy Delete all sessions, history, and data
700
739
  groundcrew destroy --session <id> Delete a specific session
701
740
 
@@ -754,6 +793,9 @@ async function main() {
754
793
  await destroyAll();
755
794
  }
756
795
  return;
796
+ case "prune":
797
+ await prune();
798
+ return;
757
799
  case "help":
758
800
  case "--help":
759
801
  case "-h":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "groundcrew-cli",
3
- "version": "0.15.9",
3
+ "version": "0.15.10",
4
4
  "description": "CLI companion for Groundcrew — queue tasks, send feedback, monitor your Copilot agent from another terminal.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/index.ts CHANGED
@@ -435,6 +435,54 @@ async function destroyAll(): Promise<void> {
435
435
  console.log(green("All session data and history deleted."));
436
436
  }
437
437
 
438
+ async function prune(): Promise<void> {
439
+ const active = await readActiveSessions();
440
+ const activeIds = new Set(Object.keys(active));
441
+
442
+ let allDirs: string[] = [];
443
+ try { allDirs = await fs.readdir(SESSIONS_DIR); } catch {}
444
+
445
+ if (allDirs.length === 0) {
446
+ console.log(dim("No sessions to prune."));
447
+ return;
448
+ }
449
+
450
+ let pruned = 0;
451
+
452
+ for (const dir of allDirs) {
453
+ // Skip active/parked sessions (registered in active-sessions.json)
454
+ if (activeIds.has(dir)) {
455
+ // Check if session is still alive (PID running)
456
+ const entry = active[dir];
457
+ try {
458
+ process.kill(entry.pid, 0); // test if process exists
459
+ continue; // still running, skip
460
+ } catch {
461
+ // PID not running — session is stale, safe to prune
462
+ }
463
+ }
464
+
465
+ const sessionDir = path.join(SESSIONS_DIR, dir);
466
+ await fs.rm(sessionDir, { recursive: true, force: true });
467
+ console.log(` ${green("pruned")} ${cyan(dir)}`);
468
+ pruned++;
469
+
470
+ // Remove from active sessions if it was there
471
+ if (activeIds.has(dir)) {
472
+ delete active[dir];
473
+ }
474
+ }
475
+
476
+ // Update active sessions file
477
+ await fs.writeFile(ACTIVE_SESSIONS_FILE, JSON.stringify(active, null, 2));
478
+
479
+ if (pruned === 0) {
480
+ console.log(dim("No stale sessions to prune. All sessions are active."));
481
+ } else {
482
+ console.log(green(`\nPruned ${pruned} stale session(s).`));
483
+ }
484
+ }
485
+
438
486
  // ── Chat Mode ────────────────────────────────────────────────────────────────
439
487
 
440
488
  interface SessionChoice {
@@ -883,6 +931,7 @@ ${bold("Usage:")}
883
931
  groundcrew clear Clear all pending tasks
884
932
  groundcrew stop Stop all active sessions
885
933
  groundcrew stop --session <id> Stop a specific session
934
+ groundcrew prune Remove stale/ended sessions (keeps active)
886
935
  groundcrew destroy Delete all sessions, history, and data
887
936
  groundcrew destroy --session <id> Delete a specific session
888
937
 
@@ -948,6 +997,9 @@ async function main(): Promise<void> {
948
997
  await destroyAll();
949
998
  }
950
999
  return;
1000
+ case "prune":
1001
+ await prune();
1002
+ return;
951
1003
  case "help":
952
1004
  case "--help":
953
1005
  case "-h":