@staff0rd/assist 0.260.0 → 0.262.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/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { Command } from "commander";
6
6
  // package.json
7
7
  var package_default = {
8
8
  name: "@staff0rd/assist",
9
- version: "0.260.0",
9
+ version: "0.262.0",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -2721,11 +2721,153 @@ function blockedByHandover(cwd = process.cwd()) {
2721
2721
  }
2722
2722
 
2723
2723
  // src/commands/backlog/acquireLock.ts
2724
- import { existsSync as existsSync17, readFileSync as readFileSync10, unlinkSync as unlinkSync2, writeFileSync as writeFileSync11 } from "fs";
2725
- import { join as join13 } from "path";
2724
+ import {
2725
+ existsSync as existsSync14,
2726
+ mkdirSync as mkdirSync4,
2727
+ readFileSync as readFileSync9,
2728
+ unlinkSync as unlinkSync2,
2729
+ writeFileSync as writeFileSync11
2730
+ } from "fs";
2731
+ import { homedir as homedir3 } from "os";
2732
+ import { join as join10 } from "path";
2733
+ function getLocksDir() {
2734
+ return join10(homedir3(), ".assist", "locks");
2735
+ }
2736
+ function getLockPath(itemId) {
2737
+ return join10(getLocksDir(), `lock-${itemId}.json`);
2738
+ }
2739
+ function isProcessAlive(pid) {
2740
+ try {
2741
+ process.kill(pid, 0);
2742
+ return true;
2743
+ } catch {
2744
+ return false;
2745
+ }
2746
+ }
2747
+ function isLockedByOther(itemId) {
2748
+ const lockPath = getLockPath(itemId);
2749
+ if (!existsSync14(lockPath)) return false;
2750
+ try {
2751
+ const lock = JSON.parse(readFileSync9(lockPath, "utf-8"));
2752
+ if (lock.pid === process.pid) return false;
2753
+ return isProcessAlive(lock.pid);
2754
+ } catch {
2755
+ return false;
2756
+ }
2757
+ }
2758
+ function acquireLock(itemId) {
2759
+ mkdirSync4(getLocksDir(), { recursive: true });
2760
+ writeFileSync11(
2761
+ getLockPath(itemId),
2762
+ JSON.stringify({ pid: process.pid, timestamp: (/* @__PURE__ */ new Date()).toISOString() })
2763
+ );
2764
+ }
2765
+ function releaseLock(itemId) {
2766
+ const lockPath = getLockPath(itemId);
2767
+ try {
2768
+ unlinkSync2(lockPath);
2769
+ } catch {
2770
+ }
2771
+ }
2772
+
2773
+ // src/commands/backlog/list/shared.ts
2774
+ import chalk27 from "chalk";
2775
+ function statusIcon(status2) {
2776
+ switch (status2) {
2777
+ case "todo":
2778
+ return chalk27.dim("[ ]");
2779
+ case "in-progress":
2780
+ return chalk27.yellow("[~]");
2781
+ case "done":
2782
+ return chalk27.green("[x]");
2783
+ case "wontdo":
2784
+ return chalk27.dim("[-]");
2785
+ }
2786
+ }
2787
+ function typeLabel(type) {
2788
+ switch (type) {
2789
+ case "bug":
2790
+ return chalk27.magenta("Bug");
2791
+ case "story":
2792
+ return chalk27.cyan("Story");
2793
+ }
2794
+ }
2795
+ function phaseLabel(item) {
2796
+ if (!item.plan) return "";
2797
+ return chalk27.dim(` (phase ${item.currentPhase ?? 1}/${item.plan.length})`);
2798
+ }
2799
+ function isBlocked(item, items2) {
2800
+ const deps2 = (item.links ?? []).filter((l) => l.type === "depends-on");
2801
+ return deps2.some((dep) => {
2802
+ const target = items2.find((i) => i.id === dep.targetId);
2803
+ return target !== void 0 && target.status !== "done";
2804
+ });
2805
+ }
2806
+ function dependencyLabel(item, items2) {
2807
+ const deps2 = (item.links ?? []).filter((l) => l.type === "depends-on");
2808
+ if (deps2.length === 0) return "";
2809
+ if (isBlocked(item, items2)) return chalk27.red(" [blocked]");
2810
+ return chalk27.dim(` [${deps2.length} dep${deps2.length > 1 ? "s" : ""}]`);
2811
+ }
2812
+ function printVerboseDetails(item) {
2813
+ if (item.description) {
2814
+ console.log(` ${chalk27.dim("Description:")} ${item.description}`);
2815
+ }
2816
+ if (item.acceptanceCriteria.length > 0) {
2817
+ console.log(` ${chalk27.dim("Acceptance criteria:")}`);
2818
+ for (const [i, criterion] of item.acceptanceCriteria.entries()) {
2819
+ console.log(` ${i + 1}. ${criterion}`);
2820
+ }
2821
+ }
2822
+ console.log();
2823
+ }
2824
+
2825
+ // src/commands/backlog/findResumable.ts
2826
+ function findResumable(items2) {
2827
+ return items2.find(
2828
+ (i) => i.status === "in-progress" && i.plan && !isLockedByOther(i.id) && !isBlocked(i, items2)
2829
+ );
2830
+ }
2831
+
2832
+ // src/commands/backlog/findUnblockedTodos.ts
2833
+ import chalk28 from "chalk";
2834
+ function findUnblockedTodos(items2) {
2835
+ const todo = items2.filter((i) => i.status === "todo");
2836
+ if (todo.length === 0) {
2837
+ console.log(chalk28.green("All backlog items complete."));
2838
+ return void 0;
2839
+ }
2840
+ const unblocked = todo.filter((i) => !isBlocked(i, items2));
2841
+ if (unblocked.length === 0) {
2842
+ console.log(
2843
+ chalk28.yellow("All remaining todo items are blocked by dependencies.")
2844
+ );
2845
+ return void 0;
2846
+ }
2847
+ return unblocked;
2848
+ }
2849
+
2850
+ // src/commands/backlog/run.ts
2851
+ import chalk35 from "chalk";
2852
+
2853
+ // src/commands/backlog/prepareRun.ts
2854
+ import chalk32 from "chalk";
2855
+
2856
+ // src/commands/backlog/resolvePlan.ts
2857
+ function resolvePlan(item) {
2858
+ if (item.plan && item.plan.length > 0) {
2859
+ return item.plan;
2860
+ }
2861
+ return [
2862
+ {
2863
+ name: "Implement",
2864
+ tasks: item.acceptanceCriteria.map((ac) => ({ task: ac }))
2865
+ }
2866
+ ];
2867
+ }
2726
2868
 
2727
2869
  // src/commands/backlog/shared.ts
2728
- import chalk29 from "chalk";
2870
+ import chalk31 from "chalk";
2729
2871
 
2730
2872
  // src/commands/backlog/deleteItem.ts
2731
2873
  import { eq } from "drizzle-orm";
@@ -2735,19 +2877,19 @@ async function deleteItem(orm, id) {
2735
2877
  }
2736
2878
 
2737
2879
  // src/commands/backlog/migrateLocalBacklog.ts
2738
- import { existsSync as existsSync15 } from "fs";
2739
- import { join as join11 } from "path";
2740
- import chalk28 from "chalk";
2880
+ import { existsSync as existsSync16 } from "fs";
2881
+ import { join as join12 } from "path";
2882
+ import chalk30 from "chalk";
2741
2883
 
2742
2884
  // src/commands/backlog/backupLocalBacklogFiles.ts
2743
- import { existsSync as existsSync14, renameSync } from "fs";
2744
- import { join as join10 } from "path";
2885
+ import { existsSync as existsSync15, renameSync } from "fs";
2886
+ import { join as join11 } from "path";
2745
2887
  var LOCAL_FILES = ["backlog.jsonl", "backlog.db"];
2746
2888
  function backupLocalBacklogFiles(dir) {
2747
2889
  const moved = [];
2748
2890
  for (const name of LOCAL_FILES) {
2749
- const path53 = join10(dir, ".assist", name);
2750
- if (existsSync14(path53)) {
2891
+ const path53 = join11(dir, ".assist", name);
2892
+ if (existsSync15(path53)) {
2751
2893
  renameSync(path53, `${path53}.bak`);
2752
2894
  moved.push(`${name} \u2192 ${name}.bak`);
2753
2895
  }
@@ -2757,7 +2899,7 @@ function backupLocalBacklogFiles(dir) {
2757
2899
 
2758
2900
  // src/commands/backlog/gitPullBacklog.ts
2759
2901
  import { execSync as execSync16 } from "child_process";
2760
- import chalk27 from "chalk";
2902
+ import chalk29 from "chalk";
2761
2903
  function gitPullBacklog(dir) {
2762
2904
  try {
2763
2905
  execSync16("git pull --ff-only", {
@@ -2766,7 +2908,7 @@ function gitPullBacklog(dir) {
2766
2908
  });
2767
2909
  } catch {
2768
2910
  console.error(
2769
- chalk27.yellow(
2911
+ chalk29.yellow(
2770
2912
  "backlog migrate: git pull skipped (no upstream or pull failed); using the local file."
2771
2913
  )
2772
2914
  );
@@ -2995,7 +3137,7 @@ async function loadAllItems(orm, origin) {
2995
3137
  }
2996
3138
 
2997
3139
  // src/commands/backlog/parseBacklogJsonl.ts
2998
- import { readFileSync as readFileSync9 } from "fs";
3140
+ import { readFileSync as readFileSync10 } from "fs";
2999
3141
 
3000
3142
  // src/commands/backlog/types.ts
3001
3143
  import { z as z3 } from "zod";
@@ -3039,14 +3181,14 @@ var backlogFileSchema = z3.array(backlogItemSchema);
3039
3181
 
3040
3182
  // src/commands/backlog/parseBacklogJsonl.ts
3041
3183
  function parseBacklogJsonl(path53) {
3042
- const content = readFileSync9(path53, "utf-8").trim();
3184
+ const content = readFileSync10(path53, "utf-8").trim();
3043
3185
  if (content.length === 0) return [];
3044
3186
  return content.split("\n").map((line) => line.trim()).filter(Boolean).map((line) => backlogItemSchema.parse(JSON.parse(line)));
3045
3187
  }
3046
3188
 
3047
3189
  // src/commands/backlog/migrateLocalBacklog.ts
3048
3190
  function jsonlPath(dir) {
3049
- return join11(dir, ".assist", "backlog.jsonl");
3191
+ return join12(dir, ".assist", "backlog.jsonl");
3050
3192
  }
3051
3193
  async function verifyImport(orm, origin, items2, imported) {
3052
3194
  const reloaded = await loadAllItems(orm, origin);
@@ -3062,12 +3204,12 @@ async function verifyImport(orm, origin, items2, imported) {
3062
3204
  }
3063
3205
  }
3064
3206
  async function migrateLocalBacklog(orm, dir, origin) {
3065
- if (!existsSync15(jsonlPath(dir))) return;
3207
+ if (!existsSync16(jsonlPath(dir))) return;
3066
3208
  const existing = (await loadAllItems(orm, origin)).length;
3067
3209
  if (existing > 0) {
3068
3210
  const moved2 = backupLocalBacklogFiles(dir);
3069
3211
  console.error(
3070
- chalk28.yellow(
3212
+ chalk30.yellow(
3071
3213
  `backlog migrate: Postgres already has ${existing} item(s) for ${origin}; skipped import to avoid duplicates and archived the local file (${moved2.join(", ")}).`
3072
3214
  )
3073
3215
  );
@@ -3079,7 +3221,7 @@ async function migrateLocalBacklog(orm, dir, origin) {
3079
3221
  await verifyImport(orm, origin, items2, imported);
3080
3222
  const moved = backupLocalBacklogFiles(dir);
3081
3223
  console.error(
3082
- chalk28.green(
3224
+ chalk30.green(
3083
3225
  `backlog migrate: imported ${imported} item(s) into Postgres (${moved.join(", ")}).`
3084
3226
  )
3085
3227
  );
@@ -3094,17 +3236,17 @@ async function ensureMigrated(orm, dir, origin) {
3094
3236
  }
3095
3237
 
3096
3238
  // src/commands/backlog/findBacklogUp.ts
3097
- import { existsSync as existsSync16 } from "fs";
3098
- import { dirname as dirname13, join as join12 } from "path";
3239
+ import { existsSync as existsSync17 } from "fs";
3240
+ import { dirname as dirname13, join as join13 } from "path";
3099
3241
  var BACKLOG_MARKERS = [
3100
- join12(".assist", "backlog.db"),
3101
- join12(".assist", "backlog.jsonl"),
3242
+ join13(".assist", "backlog.db"),
3243
+ join13(".assist", "backlog.jsonl"),
3102
3244
  "assist.backlog.yml"
3103
3245
  ];
3104
3246
  function findBacklogUp(startDir) {
3105
3247
  let current = startDir;
3106
3248
  while (current !== dirname13(current)) {
3107
- if (BACKLOG_MARKERS.some((marker) => existsSync16(join12(current, marker)))) {
3249
+ if (BACKLOG_MARKERS.some((marker) => existsSync17(join13(current, marker)))) {
3108
3250
  return current;
3109
3251
  }
3110
3252
  current = dirname13(current);
@@ -3241,7 +3383,7 @@ async function findOneItem(id) {
3241
3383
  const { orm } = await getReady();
3242
3384
  const item = Number.isNaN(numId) ? void 0 : await loadItem(orm, numId);
3243
3385
  if (!item) {
3244
- console.log(chalk29.red(`Item #${id} not found.`));
3386
+ console.log(chalk31.red(`Item #${id} not found.`));
3245
3387
  return void 0;
3246
3388
  }
3247
3389
  return { orm, item };
@@ -3249,7 +3391,7 @@ async function findOneItem(id) {
3249
3391
  async function setStatus(id, status2) {
3250
3392
  const { orm } = await getReady();
3251
3393
  const name = await updateStatus(orm, Number.parseInt(id, 10), status2);
3252
- if (name === void 0) console.log(chalk29.red(`Item #${id} not found.`));
3394
+ if (name === void 0) console.log(chalk31.red(`Item #${id} not found.`));
3253
3395
  return name;
3254
3396
  }
3255
3397
  async function setCurrentPhase(id, phase) {
@@ -3259,143 +3401,10 @@ async function setCurrentPhase(id, phase) {
3259
3401
  async function removeItem(id) {
3260
3402
  const { orm } = await getReady();
3261
3403
  const name = await deleteItem(orm, Number.parseInt(id, 10));
3262
- if (name === void 0) console.log(chalk29.red(`Item #${id} not found.`));
3404
+ if (name === void 0) console.log(chalk31.red(`Item #${id} not found.`));
3263
3405
  return name;
3264
3406
  }
3265
3407
 
3266
- // src/commands/backlog/acquireLock.ts
3267
- function getLockPath(itemId) {
3268
- return join13(getBacklogDir(), `.assist-lock-${itemId}.json`);
3269
- }
3270
- function isProcessAlive(pid) {
3271
- try {
3272
- process.kill(pid, 0);
3273
- return true;
3274
- } catch {
3275
- return false;
3276
- }
3277
- }
3278
- function isLockedByOther(itemId) {
3279
- const lockPath = getLockPath(itemId);
3280
- if (!existsSync17(lockPath)) return false;
3281
- try {
3282
- const lock = JSON.parse(readFileSync10(lockPath, "utf-8"));
3283
- if (lock.pid === process.pid) return false;
3284
- return isProcessAlive(lock.pid);
3285
- } catch {
3286
- return false;
3287
- }
3288
- }
3289
- function acquireLock(itemId) {
3290
- writeFileSync11(
3291
- getLockPath(itemId),
3292
- JSON.stringify({ pid: process.pid, timestamp: (/* @__PURE__ */ new Date()).toISOString() })
3293
- );
3294
- }
3295
- function releaseLock(itemId) {
3296
- const lockPath = getLockPath(itemId);
3297
- try {
3298
- unlinkSync2(lockPath);
3299
- } catch {
3300
- }
3301
- }
3302
-
3303
- // src/commands/backlog/list/shared.ts
3304
- import chalk30 from "chalk";
3305
- function statusIcon(status2) {
3306
- switch (status2) {
3307
- case "todo":
3308
- return chalk30.dim("[ ]");
3309
- case "in-progress":
3310
- return chalk30.yellow("[~]");
3311
- case "done":
3312
- return chalk30.green("[x]");
3313
- case "wontdo":
3314
- return chalk30.dim("[-]");
3315
- }
3316
- }
3317
- function typeLabel(type) {
3318
- switch (type) {
3319
- case "bug":
3320
- return chalk30.magenta("Bug");
3321
- case "story":
3322
- return chalk30.cyan("Story");
3323
- }
3324
- }
3325
- function phaseLabel(item) {
3326
- if (!item.plan) return "";
3327
- return chalk30.dim(` (phase ${item.currentPhase ?? 1}/${item.plan.length})`);
3328
- }
3329
- function isBlocked(item, items2) {
3330
- const deps2 = (item.links ?? []).filter((l) => l.type === "depends-on");
3331
- return deps2.some((dep) => {
3332
- const target = items2.find((i) => i.id === dep.targetId);
3333
- return target !== void 0 && target.status !== "done";
3334
- });
3335
- }
3336
- function dependencyLabel(item, items2) {
3337
- const deps2 = (item.links ?? []).filter((l) => l.type === "depends-on");
3338
- if (deps2.length === 0) return "";
3339
- if (isBlocked(item, items2)) return chalk30.red(" [blocked]");
3340
- return chalk30.dim(` [${deps2.length} dep${deps2.length > 1 ? "s" : ""}]`);
3341
- }
3342
- function printVerboseDetails(item) {
3343
- if (item.description) {
3344
- console.log(` ${chalk30.dim("Description:")} ${item.description}`);
3345
- }
3346
- if (item.acceptanceCriteria.length > 0) {
3347
- console.log(` ${chalk30.dim("Acceptance criteria:")}`);
3348
- for (const [i, criterion] of item.acceptanceCriteria.entries()) {
3349
- console.log(` ${i + 1}. ${criterion}`);
3350
- }
3351
- }
3352
- console.log();
3353
- }
3354
-
3355
- // src/commands/backlog/findResumable.ts
3356
- function findResumable(items2) {
3357
- return items2.find(
3358
- (i) => i.status === "in-progress" && i.plan && !isLockedByOther(i.id) && !isBlocked(i, items2)
3359
- );
3360
- }
3361
-
3362
- // src/commands/backlog/findUnblockedTodos.ts
3363
- import chalk31 from "chalk";
3364
- function findUnblockedTodos(items2) {
3365
- const todo = items2.filter((i) => i.status === "todo");
3366
- if (todo.length === 0) {
3367
- console.log(chalk31.green("All backlog items complete."));
3368
- return void 0;
3369
- }
3370
- const unblocked = todo.filter((i) => !isBlocked(i, items2));
3371
- if (unblocked.length === 0) {
3372
- console.log(
3373
- chalk31.yellow("All remaining todo items are blocked by dependencies.")
3374
- );
3375
- return void 0;
3376
- }
3377
- return unblocked;
3378
- }
3379
-
3380
- // src/commands/backlog/run.ts
3381
- import chalk35 from "chalk";
3382
-
3383
- // src/commands/backlog/prepareRun.ts
3384
- import chalk32 from "chalk";
3385
-
3386
- // src/commands/backlog/resolvePlan.ts
3387
- function resolvePlan(item) {
3388
- if (item.plan && item.plan.length > 0) {
3389
- return item.plan;
3390
- }
3391
- return [
3392
- {
3393
- name: "Implement",
3394
- tasks: item.acceptanceCriteria.map((ac) => ({ task: ac }))
3395
- }
3396
- ];
3397
- }
3398
-
3399
3408
  // src/commands/backlog/prepareRun.ts
3400
3409
  async function prepareRun(id) {
3401
3410
  const found = await findOneItem(id);
@@ -3428,16 +3437,17 @@ async function reloadPlan(id) {
3428
3437
  import chalk34 from "chalk";
3429
3438
 
3430
3439
  // src/shared/emitActivity.ts
3431
- import { mkdirSync as mkdirSync4, readFileSync as readFileSync11, rmSync, writeFileSync as writeFileSync12 } from "fs";
3440
+ import { mkdirSync as mkdirSync5, readFileSync as readFileSync11, rmSync, writeFileSync as writeFileSync12 } from "fs";
3441
+ import { homedir as homedir4 } from "os";
3432
3442
  import { dirname as dirname14, join as join14 } from "path";
3433
- function activityPath(cwd, sessionId) {
3434
- return join14(cwd, ".assist", `activity-${sessionId}.json`);
3443
+ function activityPath(sessionId) {
3444
+ return join14(homedir4(), ".assist", "activity", `activity-${sessionId}.json`);
3435
3445
  }
3436
3446
  function emitActivity(activity2) {
3437
3447
  const sessionId = process.env.ASSIST_ACTIVITY_ID;
3438
3448
  if (!sessionId) return;
3439
- const path53 = activityPath(process.cwd(), sessionId);
3440
- mkdirSync4(dirname14(path53), { recursive: true });
3449
+ const path53 = activityPath(sessionId);
3450
+ mkdirSync5(dirname14(path53), { recursive: true });
3441
3451
  writeFileSync12(path53, JSON.stringify({ ...activity2, startedAt: Date.now() }));
3442
3452
  }
3443
3453
  function readActivity(path53) {
@@ -3447,9 +3457,9 @@ function readActivity(path53) {
3447
3457
  return void 0;
3448
3458
  }
3449
3459
  }
3450
- function removeActivity(cwd, sessionId) {
3460
+ function removeActivity(sessionId) {
3451
3461
  try {
3452
- rmSync(activityPath(cwd, sessionId));
3462
+ rmSync(activityPath(sessionId));
3453
3463
  } catch {
3454
3464
  }
3455
3465
  }
@@ -4145,7 +4155,7 @@ function startWebServer(label2, port, handler, initialPath) {
4145
4155
  import { spawn as spawn4 } from "child_process";
4146
4156
  import {
4147
4157
  closeSync,
4148
- mkdirSync as mkdirSync5,
4158
+ mkdirSync as mkdirSync6,
4149
4159
  openSync,
4150
4160
  statSync,
4151
4161
  unlinkSync as unlinkSync4,
@@ -4156,9 +4166,9 @@ import {
4156
4166
  import * as net from "net";
4157
4167
 
4158
4168
  // src/commands/sessions/daemon/daemonPaths.ts
4159
- import { homedir as homedir3 } from "os";
4169
+ import { homedir as homedir5 } from "os";
4160
4170
  import { join as join16 } from "path";
4161
- var DAEMON_DIR = join16(homedir3(), ".assist", "daemon");
4171
+ var DAEMON_DIR = join16(homedir5(), ".assist", "daemon");
4162
4172
  var daemonPaths = {
4163
4173
  dir: DAEMON_DIR,
4164
4174
  socket: process.platform === "win32" ? "\\\\.\\pipe\\assist-sessions-daemon" : join16(DAEMON_DIR, "daemon.sock"),
@@ -4190,7 +4200,7 @@ var RETRY_DELAY_MS = 200;
4190
4200
  var STALE_LOCK_MS = SPAWN_TIMEOUT_MS + 5e3;
4191
4201
  async function ensureDaemonRunning(reason = "unspecified") {
4192
4202
  if (await isDaemonRunning()) return;
4193
- mkdirSync5(daemonPaths.dir, { recursive: true });
4203
+ mkdirSync6(daemonPaths.dir, { recursive: true });
4194
4204
  const holdsLock = acquireSpawnLock();
4195
4205
  if (holdsLock) spawnDaemon(reason);
4196
4206
  try {
@@ -5648,6 +5658,21 @@ import enquirer7 from "enquirer";
5648
5658
  // src/commands/backlog/launchMode.ts
5649
5659
  import chalk61 from "chalk";
5650
5660
 
5661
+ // src/commands/backlog/surfaceCreatedItem.ts
5662
+ async function surfaceCreatedItem(slashCommand, id) {
5663
+ const numericId = Number.parseInt(id, 10);
5664
+ if (Number.isNaN(numericId)) return;
5665
+ const { orm } = await getReady();
5666
+ const item = await loadItem(orm, numericId);
5667
+ if (!item) return;
5668
+ emitActivity({
5669
+ kind: "command",
5670
+ name: slashCommand,
5671
+ itemId: numericId,
5672
+ itemName: item.name
5673
+ });
5674
+ }
5675
+
5651
5676
  // src/commands/backlog/tryRunById.ts
5652
5677
  import chalk60 from "chalk";
5653
5678
  async function tryRunById(id, options2) {
@@ -5698,6 +5723,9 @@ async function launchMode(slashCommand, options2) {
5698
5723
  stopWatching();
5699
5724
  const signal = readSignal();
5700
5725
  cleanupSignal();
5726
+ if (signal?.event === "done" && typeof signal.id === "string" && signal.id) {
5727
+ await surfaceCreatedItem(slashCommand, signal.id);
5728
+ }
5701
5729
  if (signal?.event === "next") {
5702
5730
  if (typeof signal.id === "string" && signal.id) {
5703
5731
  if (await tryRunById(signal.id, { allowEdits: true })) return;
@@ -6436,13 +6464,13 @@ function stripEnvPrefix(parts) {
6436
6464
  }
6437
6465
 
6438
6466
  // src/commands/cliHook/logDeniedToolCall.ts
6439
- import { mkdirSync as mkdirSync6 } from "fs";
6440
- import { homedir as homedir4 } from "os";
6467
+ import { mkdirSync as mkdirSync7 } from "fs";
6468
+ import { homedir as homedir6 } from "os";
6441
6469
  import { join as join19 } from "path";
6442
6470
  import Database from "better-sqlite3";
6443
6471
  var _db;
6444
6472
  function getDbDir() {
6445
- return join19(homedir4(), ".assist");
6473
+ return join19(homedir6(), ".assist");
6446
6474
  }
6447
6475
  function initSchema(db) {
6448
6476
  db.exec(`
@@ -6460,7 +6488,7 @@ function initSchema(db) {
6460
6488
  function openPromptsDb(dir) {
6461
6489
  if (_db) return _db;
6462
6490
  const dbDir = dir ?? getDbDir();
6463
- mkdirSync6(dbDir, { recursive: true });
6491
+ mkdirSync7(dbDir, { recursive: true });
6464
6492
  const db = new Database(join19(dbDir, "assist.db"));
6465
6493
  db.pragma("journal_mode = WAL");
6466
6494
  initSchema(db);
@@ -6620,11 +6648,11 @@ function findCliWrite(command) {
6620
6648
 
6621
6649
  // src/shared/readSettingsPerms.ts
6622
6650
  import { existsSync as existsSync22, readFileSync as readFileSync17 } from "fs";
6623
- import { homedir as homedir5 } from "os";
6651
+ import { homedir as homedir7 } from "os";
6624
6652
  import { join as join20 } from "path";
6625
6653
  function readSettingsPerms(key) {
6626
6654
  const paths = [
6627
- join20(homedir5(), ".claude", "settings.json"),
6655
+ join20(homedir7(), ".claude", "settings.json"),
6628
6656
  join20(process.cwd(), ".claude", "settings.json"),
6629
6657
  join20(process.cwd(), ".claude", "settings.local.json")
6630
6658
  ];
@@ -6917,8 +6945,8 @@ ${reasons.join("\n")}`);
6917
6945
  }
6918
6946
 
6919
6947
  // src/commands/permitCliReads/index.ts
6920
- import { existsSync as existsSync23, mkdirSync as mkdirSync7, readFileSync as readFileSync18, writeFileSync as writeFileSync16 } from "fs";
6921
- import { homedir as homedir6 } from "os";
6948
+ import { existsSync as existsSync23, mkdirSync as mkdirSync8, readFileSync as readFileSync18, writeFileSync as writeFileSync16 } from "fs";
6949
+ import { homedir as homedir8 } from "os";
6922
6950
  import { join as join21 } from "path";
6923
6951
 
6924
6952
  // src/shared/checkCliAvailable.ts
@@ -7208,7 +7236,7 @@ function updateSettings(cli, commands) {
7208
7236
  // src/commands/permitCliReads/index.ts
7209
7237
  function logPath(cli) {
7210
7238
  const safeName = cli.replace(/\s+/g, "-");
7211
- return join21(homedir6(), ".assist", `cli-discover-${safeName}.log`);
7239
+ return join21(homedir8(), ".assist", `cli-discover-${safeName}.log`);
7212
7240
  }
7213
7241
  function readCache(cli) {
7214
7242
  const path53 = logPath(cli);
@@ -7216,8 +7244,8 @@ function readCache(cli) {
7216
7244
  return readFileSync18(path53, "utf-8");
7217
7245
  }
7218
7246
  function writeCache(cli, output) {
7219
- const dir = join21(homedir6(), ".assist");
7220
- mkdirSync7(dir, { recursive: true });
7247
+ const dir = join21(homedir8(), ".assist");
7248
+ mkdirSync8(dir, { recursive: true });
7221
7249
  writeFileSync16(logPath(cli), output);
7222
7250
  }
7223
7251
  async function permitCliReads(cli, options2 = { noCache: false }) {
@@ -8043,9 +8071,9 @@ import { execFileSync } from "child_process";
8043
8071
  import { basename as basename4 } from "path";
8044
8072
 
8045
8073
  // src/commands/devlog/loadBlogSkipDays.ts
8046
- import { homedir as homedir7 } from "os";
8074
+ import { homedir as homedir9 } from "os";
8047
8075
  import { join as join22 } from "path";
8048
- var BLOG_REPO_ROOT = join22(homedir7(), "git/blog");
8076
+ var BLOG_REPO_ROOT = join22(homedir9(), "git/blog");
8049
8077
  function loadBlogSkipDays(repoName) {
8050
8078
  const config = loadRawYaml(join22(BLOG_REPO_ROOT, "assist.yml"));
8051
8079
  const devlog = config.devlog;
@@ -9389,7 +9417,7 @@ function registerGithub(program2) {
9389
9417
  }
9390
9418
 
9391
9419
  // src/commands/handover/archive.ts
9392
- import { existsSync as existsSync30, mkdirSync as mkdirSync8, renameSync as renameSync2 } from "fs";
9420
+ import { existsSync as existsSync30, mkdirSync as mkdirSync9, renameSync as renameSync2 } from "fs";
9393
9421
  import { join as join30 } from "path";
9394
9422
 
9395
9423
  // src/commands/handover/formatArchiveTimestamp.ts
@@ -9436,7 +9464,7 @@ function archive(options2 = {}) {
9436
9464
  const handoverPath = getHandoverPath(cwd);
9437
9465
  if (!existsSync30(handoverPath)) return void 0;
9438
9466
  const archiveDir = getHandoverArchiveDir(cwd);
9439
- mkdirSync8(archiveDir, { recursive: true });
9467
+ mkdirSync9(archiveDir, { recursive: true });
9440
9468
  const timestamp = formatArchiveTimestamp(options2.now);
9441
9469
  const destination = resolveCollisionPath(
9442
9470
  archiveDir,
@@ -9731,11 +9759,11 @@ function acceptanceCriteria(issueKey) {
9731
9759
  import { execSync as execSync28 } from "child_process";
9732
9760
 
9733
9761
  // src/shared/loadJson.ts
9734
- import { existsSync as existsSync32, mkdirSync as mkdirSync9, readFileSync as readFileSync27, writeFileSync as writeFileSync20 } from "fs";
9735
- import { homedir as homedir8 } from "os";
9762
+ import { existsSync as existsSync32, mkdirSync as mkdirSync10, readFileSync as readFileSync27, writeFileSync as writeFileSync20 } from "fs";
9763
+ import { homedir as homedir10 } from "os";
9736
9764
  import { join as join31 } from "path";
9737
9765
  function getStoreDir() {
9738
- return join31(homedir8(), ".assist");
9766
+ return join31(homedir10(), ".assist");
9739
9767
  }
9740
9768
  function getStorePath(filename) {
9741
9769
  return join31(getStoreDir(), filename);
@@ -9754,7 +9782,7 @@ function loadJson(filename) {
9754
9782
  function saveJson(filename, data) {
9755
9783
  const dir = getStoreDir();
9756
9784
  if (!existsSync32(dir)) {
9757
- mkdirSync9(dir, { recursive: true });
9785
+ mkdirSync10(dir, { recursive: true });
9758
9786
  }
9759
9787
  writeFileSync20(getStorePath(filename), JSON.stringify(data, null, 2));
9760
9788
  }
@@ -9889,7 +9917,7 @@ function registerList(program2) {
9889
9917
  }
9890
9918
 
9891
9919
  // src/commands/mermaid/index.ts
9892
- import { mkdirSync as mkdirSync10, readdirSync as readdirSync5 } from "fs";
9920
+ import { mkdirSync as mkdirSync11, readdirSync as readdirSync5 } from "fs";
9893
9921
  import { resolve as resolve10 } from "path";
9894
9922
  import chalk114 from "chalk";
9895
9923
 
@@ -9958,7 +9986,7 @@ function extractMermaidBlocks(markdown) {
9958
9986
  async function mermaidExport(file, options2 = {}) {
9959
9987
  const { mermaid } = loadConfig();
9960
9988
  const outDir = resolve10(process.cwd(), options2.out ?? ".");
9961
- mkdirSync10(outDir, { recursive: true });
9989
+ mkdirSync11(outDir, { recursive: true });
9962
9990
  if (options2.index !== void 0) {
9963
9991
  if (!Number.isInteger(options2.index) || options2.index < 1) {
9964
9992
  console.error(
@@ -10571,7 +10599,7 @@ function fixed(commentId, sha) {
10571
10599
  }
10572
10600
 
10573
10601
  // src/commands/prs/listComments/index.ts
10574
- import { existsSync as existsSync34, mkdirSync as mkdirSync11, writeFileSync as writeFileSync24 } from "fs";
10602
+ import { existsSync as existsSync34, mkdirSync as mkdirSync12, writeFileSync as writeFileSync24 } from "fs";
10575
10603
  import { join as join35 } from "path";
10576
10604
  import { stringify } from "yaml";
10577
10605
 
@@ -10698,7 +10726,7 @@ function printComments2(result) {
10698
10726
  function writeCommentsCache(prNumber, comments3) {
10699
10727
  const assistDir = join35(process.cwd(), ".assist");
10700
10728
  if (!existsSync34(assistDir)) {
10701
- mkdirSync11(assistDir, { recursive: true });
10729
+ mkdirSync12(assistDir, { recursive: true });
10702
10730
  }
10703
10731
  const cacheData = {
10704
10732
  prNumber,
@@ -13510,14 +13538,14 @@ async function handlePostSynthesis(synthesisPath, options2) {
13510
13538
  }
13511
13539
 
13512
13540
  // src/commands/review/prepareReviewDir.ts
13513
- import { existsSync as existsSync36, mkdirSync as mkdirSync12, unlinkSync as unlinkSync11, writeFileSync as writeFileSync26 } from "fs";
13541
+ import { existsSync as existsSync36, mkdirSync as mkdirSync13, unlinkSync as unlinkSync11, writeFileSync as writeFileSync26 } from "fs";
13514
13542
  function clearReviewFiles(paths) {
13515
13543
  for (const path53 of [paths.claudePath, paths.codexPath, paths.synthesisPath]) {
13516
13544
  if (existsSync36(path53)) unlinkSync11(path53);
13517
13545
  }
13518
13546
  }
13519
13547
  function prepareReviewDir(paths, requestBody, force) {
13520
- mkdirSync12(paths.reviewDir, { recursive: true });
13548
+ mkdirSync13(paths.reviewDir, { recursive: true });
13521
13549
  if (force) clearReviewFiles(paths);
13522
13550
  writeFileSync26(paths.requestPath, requestBody);
13523
13551
  }
@@ -14918,8 +14946,8 @@ function registerSignal(program2) {
14918
14946
  writeSignal("next", id ? { id } : void 0);
14919
14947
  console.log("Signal written.");
14920
14948
  });
14921
- signalCommand.command("done").description("Write a done signal to end a --once launch session").action(() => {
14922
- writeSignal("done");
14949
+ signalCommand.command("done").argument("[id]", "Backlog item ID created by the session").description("Write a done signal to end a --once launch session").action((id) => {
14950
+ writeSignal("done", id ? { id } : void 0);
14923
14951
  console.log("Signal written.");
14924
14952
  });
14925
14953
  }
@@ -15381,7 +15409,7 @@ async function fixInvalidDatePrefixes(vttFiles) {
15381
15409
  }
15382
15410
 
15383
15411
  // src/commands/transcript/format/processVttFile/index.ts
15384
- import { existsSync as existsSync40, mkdirSync as mkdirSync13, readFileSync as readFileSync33, writeFileSync as writeFileSync28 } from "fs";
15412
+ import { existsSync as existsSync40, mkdirSync as mkdirSync14, readFileSync as readFileSync33, writeFileSync as writeFileSync28 } from "fs";
15385
15413
  import { basename as basename7, dirname as dirname21, join as join41 } from "path";
15386
15414
 
15387
15415
  // src/commands/transcript/cleanText.ts
@@ -15607,7 +15635,7 @@ function logSkipped(relativeDir, mdFile) {
15607
15635
  }
15608
15636
  function ensureDirectory(dir, label2) {
15609
15637
  if (!existsSync40(dir)) {
15610
- mkdirSync13(dir, { recursive: true });
15638
+ mkdirSync14(dir, { recursive: true });
15611
15639
  console.log(`Created ${label2}: ${dir}`);
15612
15640
  }
15613
15641
  }
@@ -15706,7 +15734,7 @@ import { basename as basename8, dirname as dirname23, join as join43, relative a
15706
15734
  // src/commands/transcript/summarise/processStagedFile/index.ts
15707
15735
  import {
15708
15736
  existsSync as existsSync42,
15709
- mkdirSync as mkdirSync14,
15737
+ mkdirSync as mkdirSync15,
15710
15738
  readFileSync as readFileSync34,
15711
15739
  renameSync as renameSync4,
15712
15740
  rmSync as rmSync2
@@ -15767,7 +15795,7 @@ function processStagedFile() {
15767
15795
  const destPath = join42(summaryDir, matchingTranscript.relativePath);
15768
15796
  const destDir = dirname22(destPath);
15769
15797
  if (!existsSync42(destDir)) {
15770
- mkdirSync14(destDir, { recursive: true });
15798
+ mkdirSync15(destDir, { recursive: true });
15771
15799
  }
15772
15800
  renameSync4(stagedFile.absolutePath, destPath);
15773
15801
  const remaining = findMdFilesRecursive(STAGING_DIR);
@@ -15867,11 +15895,11 @@ import { spawnSync as spawnSync4 } from "child_process";
15867
15895
  import { join as join45 } from "path";
15868
15896
 
15869
15897
  // src/commands/voice/shared.ts
15870
- import { homedir as homedir9 } from "os";
15898
+ import { homedir as homedir11 } from "os";
15871
15899
  import { dirname as dirname24, join as join44 } from "path";
15872
15900
  import { fileURLToPath as fileURLToPath6 } from "url";
15873
15901
  var __dirname6 = dirname24(fileURLToPath6(import.meta.url));
15874
- var VOICE_DIR = join44(homedir9(), ".assist", "voice");
15902
+ var VOICE_DIR = join44(homedir11(), ".assist", "voice");
15875
15903
  var voicePaths = {
15876
15904
  dir: VOICE_DIR,
15877
15905
  pid: join44(VOICE_DIR, "voice.pid"),
@@ -15929,12 +15957,12 @@ function logs(options2) {
15929
15957
 
15930
15958
  // src/commands/voice/setup.ts
15931
15959
  import { spawnSync as spawnSync5 } from "child_process";
15932
- import { mkdirSync as mkdirSync16 } from "fs";
15960
+ import { mkdirSync as mkdirSync17 } from "fs";
15933
15961
  import { join as join47 } from "path";
15934
15962
 
15935
15963
  // src/commands/voice/checkLockFile.ts
15936
15964
  import { execSync as execSync44 } from "child_process";
15937
- import { existsSync as existsSync45, mkdirSync as mkdirSync15, readFileSync as readFileSync36, writeFileSync as writeFileSync29 } from "fs";
15965
+ import { existsSync as existsSync45, mkdirSync as mkdirSync16, readFileSync as readFileSync36, writeFileSync as writeFileSync29 } from "fs";
15938
15966
  import { join as join46 } from "path";
15939
15967
  function isProcessAlive2(pid) {
15940
15968
  try {
@@ -15972,7 +16000,7 @@ function bootstrapVenv() {
15972
16000
  }
15973
16001
  function writeLockFile(pid) {
15974
16002
  const lockFile = getLockFile();
15975
- mkdirSync15(join46(lockFile, ".."), { recursive: true });
16003
+ mkdirSync16(join46(lockFile, ".."), { recursive: true });
15976
16004
  writeFileSync29(
15977
16005
  lockFile,
15978
16006
  JSON.stringify({
@@ -15985,7 +16013,7 @@ function writeLockFile(pid) {
15985
16013
 
15986
16014
  // src/commands/voice/setup.ts
15987
16015
  function setup() {
15988
- mkdirSync16(voicePaths.dir, { recursive: true });
16016
+ mkdirSync17(voicePaths.dir, { recursive: true });
15989
16017
  bootstrapVenv();
15990
16018
  console.log("\nDownloading models...\n");
15991
16019
  const script = join47(getPythonDir(), "setup_models.py");
@@ -16001,7 +16029,7 @@ function setup() {
16001
16029
 
16002
16030
  // src/commands/voice/start.ts
16003
16031
  import { spawn as spawn7 } from "child_process";
16004
- import { mkdirSync as mkdirSync17, writeFileSync as writeFileSync30 } from "fs";
16032
+ import { mkdirSync as mkdirSync18, writeFileSync as writeFileSync30 } from "fs";
16005
16033
  import { join as join48 } from "path";
16006
16034
 
16007
16035
  // src/commands/voice/buildDaemonEnv.ts
@@ -16035,7 +16063,7 @@ function spawnBackground(python, script, env) {
16035
16063
  console.log(`Voice daemon started (PID ${pid})`);
16036
16064
  }
16037
16065
  function start2(options2) {
16038
- mkdirSync17(voicePaths.dir, { recursive: true });
16066
+ mkdirSync18(voicePaths.dir, { recursive: true });
16039
16067
  checkLockFile();
16040
16068
  bootstrapVenv();
16041
16069
  const debug = options2.debug || options2.foreground || process.platform === "win32";
@@ -16590,7 +16618,7 @@ async function run3(name, args) {
16590
16618
  }
16591
16619
 
16592
16620
  // src/commands/run/add.ts
16593
- import { mkdirSync as mkdirSync18, writeFileSync as writeFileSync31 } from "fs";
16621
+ import { mkdirSync as mkdirSync19, writeFileSync as writeFileSync31 } from "fs";
16594
16622
  import { join as join51 } from "path";
16595
16623
 
16596
16624
  // src/commands/run/extractOption.ts
@@ -16653,7 +16681,7 @@ function saveNewRunConfig(name, command, args, cwd) {
16653
16681
  }
16654
16682
  function createCommandFile(name) {
16655
16683
  const dir = join51(".claude", "commands");
16656
- mkdirSync18(dir, { recursive: true });
16684
+ mkdirSync19(dir, { recursive: true });
16657
16685
  const content = `---
16658
16686
  description: Run ${name}
16659
16687
  ---
@@ -16772,7 +16800,7 @@ function registerRun(program2) {
16772
16800
 
16773
16801
  // src/commands/screenshot/index.ts
16774
16802
  import { execSync as execSync47 } from "child_process";
16775
- import { existsSync as existsSync50, mkdirSync as mkdirSync19, unlinkSync as unlinkSync16, writeFileSync as writeFileSync32 } from "fs";
16803
+ import { existsSync as existsSync50, mkdirSync as mkdirSync20, unlinkSync as unlinkSync16, writeFileSync as writeFileSync32 } from "fs";
16776
16804
  import { tmpdir as tmpdir7 } from "os";
16777
16805
  import { join as join53, resolve as resolve13 } from "path";
16778
16806
  import chalk157 from "chalk";
@@ -16905,7 +16933,7 @@ Write-Output $OutputPath
16905
16933
  // src/commands/screenshot/index.ts
16906
16934
  function buildOutputPath(outputDir, processName) {
16907
16935
  if (!existsSync50(outputDir)) {
16908
- mkdirSync19(outputDir, { recursive: true });
16936
+ mkdirSync20(outputDir, { recursive: true });
16909
16937
  }
16910
16938
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
16911
16939
  return resolve13(outputDir, `${processName}-${timestamp}.png`);
@@ -17087,7 +17115,7 @@ async function restartDaemon() {
17087
17115
  }
17088
17116
 
17089
17117
  // src/commands/sessions/daemon/runDaemon.ts
17090
- import { mkdirSync as mkdirSync21 } from "fs";
17118
+ import { mkdirSync as mkdirSync22 } from "fs";
17091
17119
 
17092
17120
  // src/commands/sessions/daemon/createAutoExit.ts
17093
17121
  var DEFAULT_GRACE_MS = 6e4;
@@ -17223,19 +17251,6 @@ async function discoverSessions() {
17223
17251
  return sessions;
17224
17252
  }
17225
17253
 
17226
- // src/commands/sessions/daemon/shouldAutoDismiss.ts
17227
- function shouldAutoDismiss(session, exitCode) {
17228
- const args = session.assistArgs;
17229
- return session.status === "done" && exitCode === 0 && args !== void 0 && args.includes("--once") && args[0] !== "next";
17230
- }
17231
-
17232
- // src/commands/sessions/daemon/applyStatusChange.ts
17233
- function applyStatusChange(session, status2, exitCode, dismiss, notify2) {
17234
- session.status = status2;
17235
- if (shouldAutoDismiss(session, exitCode)) dismiss(session.id);
17236
- else notify2();
17237
- }
17238
-
17239
17254
  // src/commands/sessions/daemon/broadcast.ts
17240
17255
  function sendTo(client, msg) {
17241
17256
  client.send(JSON.stringify(msg));
@@ -17247,6 +17262,88 @@ function broadcast(clients, msg) {
17247
17262
  }
17248
17263
  }
17249
17264
 
17265
+ // src/commands/sessions/daemon/loadPersistedSessions.ts
17266
+ import { z as z4 } from "zod";
17267
+ var SESSIONS_FILE = "sessions.json";
17268
+ var persistedSessionSchema = z4.object({
17269
+ name: z4.string(),
17270
+ commandType: z4.enum(["claude", "run", "assist"]),
17271
+ cwd: z4.string(),
17272
+ startedAt: z4.number(),
17273
+ claudeSessionId: z4.string().optional(),
17274
+ runName: z4.string().optional(),
17275
+ runArgs: z4.array(z4.string()).optional(),
17276
+ assistArgs: z4.array(z4.string()).optional()
17277
+ });
17278
+ function loadPersistedSessions() {
17279
+ const data = loadJson(SESSIONS_FILE);
17280
+ if (!Array.isArray(data)) return [];
17281
+ return data.flatMap((entry) => {
17282
+ const parsed = persistedSessionSchema.safeParse(entry);
17283
+ return parsed.success ? [parsed.data] : [];
17284
+ });
17285
+ }
17286
+ function savePersistedSessions(sessions) {
17287
+ saveJson(SESSIONS_FILE, sessions);
17288
+ }
17289
+ function persistLiveSessions(sessions) {
17290
+ savePersistedSessions(
17291
+ [...sessions.values()].filter((s) => s.pty && s.status !== "done").map(toPersistedSession)
17292
+ );
17293
+ }
17294
+ function toPersistedSession(session) {
17295
+ return {
17296
+ name: session.name,
17297
+ commandType: session.commandType,
17298
+ cwd: session.cwd ?? process.cwd(),
17299
+ startedAt: session.startedAt,
17300
+ claudeSessionId: session.claudeSessionId,
17301
+ runName: session.runName,
17302
+ runArgs: session.runArgs,
17303
+ assistArgs: session.assistArgs
17304
+ };
17305
+ }
17306
+
17307
+ // src/commands/sessions/daemon/toSessionInfo.ts
17308
+ function toSessionInfo({
17309
+ id,
17310
+ name,
17311
+ commandType,
17312
+ status: status2,
17313
+ startedAt,
17314
+ runName,
17315
+ runArgs,
17316
+ assistArgs,
17317
+ cwd,
17318
+ restored,
17319
+ activity: activity2,
17320
+ autoRun
17321
+ }) {
17322
+ return {
17323
+ id,
17324
+ name,
17325
+ commandType,
17326
+ status: status2,
17327
+ startedAt,
17328
+ runName,
17329
+ runArgs,
17330
+ assistArgs,
17331
+ cwd,
17332
+ restored,
17333
+ activity: activity2,
17334
+ autoRun
17335
+ };
17336
+ }
17337
+
17338
+ // src/commands/sessions/daemon/broadcastSessions.ts
17339
+ function broadcastSessions(sessions, clients) {
17340
+ persistLiveSessions(sessions);
17341
+ broadcast(clients, {
17342
+ type: "sessions",
17343
+ sessions: [...sessions.values()].map(toSessionInfo)
17344
+ });
17345
+ }
17346
+
17250
17347
  // src/commands/sessions/daemon/spawnPty.ts
17251
17348
  import * as pty from "node-pty";
17252
17349
 
@@ -17372,46 +17469,34 @@ function greetClient(client, sessions, list4) {
17372
17469
  replayScrollback(sessions, client);
17373
17470
  }
17374
17471
 
17375
- // src/commands/sessions/daemon/loadPersistedSessions.ts
17376
- import { z as z4 } from "zod";
17377
- var SESSIONS_FILE = "sessions.json";
17378
- var persistedSessionSchema = z4.object({
17379
- name: z4.string(),
17380
- commandType: z4.enum(["claude", "run", "assist"]),
17381
- cwd: z4.string(),
17382
- startedAt: z4.number(),
17383
- claudeSessionId: z4.string().optional(),
17384
- runName: z4.string().optional(),
17385
- runArgs: z4.array(z4.string()).optional(),
17386
- assistArgs: z4.array(z4.string()).optional()
17387
- });
17388
- function loadPersistedSessions() {
17389
- const data = loadJson(SESSIONS_FILE);
17390
- if (!Array.isArray(data)) return [];
17391
- return data.flatMap((entry) => {
17392
- const parsed = persistedSessionSchema.safeParse(entry);
17393
- return parsed.success ? [parsed.data] : [];
17394
- });
17472
+ // src/commands/sessions/daemon/shouldAutoDismiss.ts
17473
+ function shouldAutoDismiss(session, exitCode) {
17474
+ const args = session.assistArgs;
17475
+ return session.status === "done" && exitCode === 0 && args !== void 0 && args.includes("--once") && args[0] !== "next";
17395
17476
  }
17396
- function savePersistedSessions(sessions) {
17397
- saveJson(SESSIONS_FILE, sessions);
17477
+
17478
+ // src/commands/sessions/daemon/shouldAutoRun.ts
17479
+ function shouldAutoRun(session, exitCode) {
17480
+ if (!session.autoRun) return false;
17481
+ if (session.status !== "done" || exitCode !== 0) return false;
17482
+ if (session.commandType !== "assist") return false;
17483
+ const cmd = session.assistArgs?.[0];
17484
+ if (cmd !== "draft" && cmd !== "bug") return false;
17485
+ return session.activity?.itemId != null;
17398
17486
  }
17399
- function persistLiveSessions(sessions) {
17400
- savePersistedSessions(
17401
- [...sessions.values()].filter((s) => s.pty && s.status !== "done").map(toPersistedSession)
17402
- );
17487
+
17488
+ // src/commands/sessions/daemon/applyStatusChange.ts
17489
+ function applyStatusChange(session, status2, exitCode, dismiss, notify2, spawnRun2) {
17490
+ session.status = status2;
17491
+ if (shouldAutoRun(session, exitCode) && session.activity?.itemId != null)
17492
+ spawnRun2(session.activity.itemId, session.cwd);
17493
+ if (shouldAutoDismiss(session, exitCode)) dismiss(session.id);
17494
+ else notify2();
17403
17495
  }
17404
- function toPersistedSession(session) {
17405
- return {
17406
- name: session.name,
17407
- commandType: session.commandType,
17408
- cwd: session.cwd ?? process.cwd(),
17409
- startedAt: session.startedAt,
17410
- claudeSessionId: session.claudeSessionId,
17411
- runName: session.runName,
17412
- runArgs: session.runArgs,
17413
- assistArgs: session.assistArgs
17414
- };
17496
+
17497
+ // src/commands/sessions/daemon/makeStatusChangeHandler.ts
17498
+ function makeStatusChangeHandler(dismiss, notify2, spawnRun2) {
17499
+ return (s, status2, exitCode) => applyStatusChange(s, status2, exitCode, dismiss, notify2, spawnRun2);
17415
17500
  }
17416
17501
 
17417
17502
  // src/commands/sessions/daemon/restoreSession.ts
@@ -17483,6 +17568,40 @@ function clearIdle(session) {
17483
17568
  if (session.idleTimer) clearTimeout(session.idleTimer);
17484
17569
  }
17485
17570
 
17571
+ // src/commands/sessions/daemon/watchActivity.ts
17572
+ import { existsSync as existsSync52, mkdirSync as mkdirSync21, watch } from "fs";
17573
+ import { dirname as dirname27 } from "path";
17574
+ var DEBOUNCE_MS = 50;
17575
+ function watchActivity(session, notify2) {
17576
+ if (session.commandType !== "assist" || !session.cwd) return;
17577
+ const path53 = activityPath(session.id);
17578
+ const dir = dirname27(path53);
17579
+ try {
17580
+ mkdirSync21(dir, { recursive: true });
17581
+ } catch {
17582
+ return;
17583
+ }
17584
+ let timer = null;
17585
+ const read = () => {
17586
+ timer = null;
17587
+ const activity2 = readActivity(path53);
17588
+ if (!activity2) return;
17589
+ session.activity = activity2;
17590
+ notify2();
17591
+ };
17592
+ session.activityWatcher = watch(dir, (_event, filename) => {
17593
+ if (filename && !path53.endsWith(filename)) return;
17594
+ if (timer) clearTimeout(timer);
17595
+ timer = setTimeout(read, DEBOUNCE_MS);
17596
+ });
17597
+ if (existsSync52(path53)) read();
17598
+ }
17599
+ function refreshActivity(session) {
17600
+ if (session.commandType !== "assist" || !session.cwd) return;
17601
+ const activity2 = readActivity(activityPath(session.id));
17602
+ if (activity2) session.activity = activity2;
17603
+ }
17604
+
17486
17605
  // src/commands/sessions/daemon/wirePtyEvents.ts
17487
17606
  var MAX_SCROLLBACK = 256 * 1024;
17488
17607
  var RESIZE_GRACE_MS = 500;
@@ -17505,6 +17624,7 @@ function wirePtyEvents(session, clients, onStatusChange) {
17505
17624
  });
17506
17625
  session.pty.onExit(({ exitCode }) => {
17507
17626
  clearIdle(session);
17627
+ refreshActivity(session);
17508
17628
  onStatusChange(session, "done", exitCode);
17509
17629
  });
17510
17630
  scheduleIdle(session, () => onStatusChange(session, "waiting"));
@@ -17542,64 +17662,6 @@ function shutdownSessions(sessions) {
17542
17662
  }
17543
17663
  }
17544
17664
 
17545
- // src/commands/sessions/daemon/toSessionInfo.ts
17546
- function toSessionInfo({
17547
- id,
17548
- name,
17549
- commandType,
17550
- status: status2,
17551
- startedAt,
17552
- runName,
17553
- runArgs,
17554
- assistArgs,
17555
- cwd,
17556
- restored,
17557
- activity: activity2
17558
- }) {
17559
- return {
17560
- id,
17561
- name,
17562
- commandType,
17563
- status: status2,
17564
- startedAt,
17565
- runName,
17566
- runArgs,
17567
- assistArgs,
17568
- cwd,
17569
- restored,
17570
- activity: activity2
17571
- };
17572
- }
17573
-
17574
- // src/commands/sessions/daemon/watchActivity.ts
17575
- import { existsSync as existsSync52, mkdirSync as mkdirSync20, watch } from "fs";
17576
- import { dirname as dirname27 } from "path";
17577
- var DEBOUNCE_MS = 50;
17578
- function watchActivity(session, notify2) {
17579
- if (session.commandType !== "assist" || !session.cwd) return;
17580
- const path53 = activityPath(session.cwd, session.id);
17581
- const dir = dirname27(path53);
17582
- try {
17583
- mkdirSync20(dir, { recursive: true });
17584
- } catch {
17585
- return;
17586
- }
17587
- let timer = null;
17588
- const read = () => {
17589
- timer = null;
17590
- const activity2 = readActivity(path53);
17591
- if (!activity2) return;
17592
- session.activity = activity2;
17593
- notify2();
17594
- };
17595
- session.activityWatcher = watch(dir, (_event, filename) => {
17596
- if (filename && !path53.endsWith(filename)) return;
17597
- if (timer) clearTimeout(timer);
17598
- timer = setTimeout(read, DEBOUNCE_MS);
17599
- });
17600
- if (existsSync52(path53)) read();
17601
- }
17602
-
17603
17665
  // src/commands/sessions/daemon/discoverClaudeSessionId.ts
17604
17666
  import * as fs26 from "fs";
17605
17667
  import * as path48 from "path";
@@ -17665,13 +17727,19 @@ function resizeSession(sessions, id, cols, rows) {
17665
17727
  s.pty?.resize(cols, rows);
17666
17728
  }
17667
17729
  }
17730
+ function setAutoRun(sessions, id, enabled) {
17731
+ const s = sessions.get(id);
17732
+ if (!s) return false;
17733
+ s.autoRun = enabled;
17734
+ return true;
17735
+ }
17668
17736
  function dismissSession(sessions, id) {
17669
17737
  const s = sessions.get(id);
17670
17738
  if (!s) return false;
17671
17739
  if (s.status !== "done") s.pty?.kill();
17672
17740
  clearIdle(s);
17673
17741
  s.activityWatcher?.close();
17674
- if (s.cwd) removeActivity(s.cwd, s.id);
17742
+ removeActivity(s.id);
17675
17743
  sessions.delete(id);
17676
17744
  return true;
17677
17745
  }
@@ -17729,7 +17797,11 @@ var SessionManager = class {
17729
17797
  resume(sessionId, cwd, name) {
17730
17798
  return this.add(resumeSession(String(this.nextId++), sessionId, cwd, name));
17731
17799
  }
17732
- onStatusChange = (s, status2, exitCode) => applyStatusChange(s, status2, exitCode, this.dismissSession, this.notify);
17800
+ onStatusChange = makeStatusChangeHandler(
17801
+ (id) => this.dismissSession(id),
17802
+ () => this.notify(),
17803
+ (itemId, cwd) => this.spawnAssist(["backlog", "run", String(itemId)], cwd)
17804
+ );
17733
17805
  wire(session) {
17734
17806
  this.sessions.set(session.id, session);
17735
17807
  wirePtyEvents(session, this.clients, this.onStatusChange);
@@ -17748,6 +17820,9 @@ var SessionManager = class {
17748
17820
  dismissSession = (id) => {
17749
17821
  if (dismissSession(this.sessions, id)) this.notify();
17750
17822
  };
17823
+ setAutoRun(id, enabled) {
17824
+ if (setAutoRun(this.sessions, id, enabled)) this.notify();
17825
+ }
17751
17826
  listSessions() {
17752
17827
  return [...this.sessions.values()].map(toSessionInfo);
17753
17828
  }
@@ -17756,11 +17831,7 @@ var SessionManager = class {
17756
17831
  }
17757
17832
  notify = () => {
17758
17833
  if (this.shuttingDown) return;
17759
- persistLiveSessions(this.sessions);
17760
- broadcast(this.clients, {
17761
- type: "sessions",
17762
- sessions: this.listSessions()
17763
- });
17834
+ broadcastSessions(this.sessions, this.clients);
17764
17835
  this.onIdleChange?.(this.isIdle());
17765
17836
  };
17766
17837
  };
@@ -17835,7 +17906,8 @@ var handlers = {
17835
17906
  input: (_client, m, d) => m.writeToSession(d.sessionId, d.data),
17836
17907
  resize: (_client, m, d) => m.resizeSession(d.sessionId, d.cols, d.rows),
17837
17908
  retry: (_client, m, d) => m.retrySession(d.sessionId),
17838
- dismiss: (_client, m, d) => m.dismissSession(d.sessionId)
17909
+ dismiss: (_client, m, d) => m.dismissSession(d.sessionId),
17910
+ "set-autorun": (_client, m, d) => m.setAutoRun(d.sessionId, d.enabled)
17839
17911
  };
17840
17912
  function dispatchMessage(client, manager, data) {
17841
17913
  handlers[data.type]?.(client, manager, data);
@@ -17958,7 +18030,7 @@ async function recoverFromAddrInUse(server, manager, checkAutoExit) {
17958
18030
 
17959
18031
  // src/commands/sessions/daemon/runDaemon.ts
17960
18032
  async function runDaemon() {
17961
- mkdirSync21(daemonPaths.dir, { recursive: true });
18033
+ mkdirSync22(daemonPaths.dir, { recursive: true });
17962
18034
  daemonLog(
17963
18035
  `starting (reason: ${process.env.ASSIST_DAEMON_SPAWN_REASON ?? "manual"})`
17964
18036
  );