@staff0rd/assist 0.486.1 → 0.487.1

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.486.1",
9
+ version: "0.487.1",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -6829,7 +6829,6 @@ function consumePause(itemId2) {
6829
6829
  }
6830
6830
 
6831
6831
  // src/commands/backlog/executePhase.ts
6832
- import { randomUUID } from "crypto";
6833
6832
  import chalk44 from "chalk";
6834
6833
 
6835
6834
  // src/shared/awaitClaude.ts
@@ -7000,6 +6999,13 @@ function logHookFired(status3, sessionId, source) {
7000
6999
  );
7001
7000
  }
7002
7001
 
7002
+ // src/commands/backlog/assertCodexResumeSupported.ts
7003
+ function assertCodexResumeSupported(options2) {
7004
+ if (options2?.resumeSessionId && options2.harness === "codex") {
7005
+ throw new Error("Codex backlog sessions cannot be resumed yet");
7006
+ }
7007
+ }
7008
+
7003
7009
  // src/commands/backlog/buildCommentLines.ts
7004
7010
  function buildCommentLines(comments3) {
7005
7011
  if (!comments3?.length) return [];
@@ -7142,6 +7148,101 @@ function buildPhasePrompt(item, phaseNumber, phase) {
7142
7148
  });
7143
7149
  }
7144
7150
 
7151
+ // src/shared/harnesses.ts
7152
+ import * as os from "os";
7153
+ import * as path22 from "path";
7154
+
7155
+ // src/shared/checkCliAvailable.ts
7156
+ import { execSync as execSync30 } from "child_process";
7157
+ function checkCliAvailable(cli) {
7158
+ const binary = cli.split(/\s+/)[0];
7159
+ const opts = {
7160
+ encoding: "utf8",
7161
+ stdio: ["ignore", "pipe", "pipe"]
7162
+ };
7163
+ try {
7164
+ execSync30(`command -v ${binary}`, opts);
7165
+ return true;
7166
+ } catch {
7167
+ try {
7168
+ execSync30(`where ${binary}`, opts);
7169
+ return true;
7170
+ } catch {
7171
+ return false;
7172
+ }
7173
+ }
7174
+ }
7175
+
7176
+ // src/shared/harnesses.ts
7177
+ var harnesses = {
7178
+ claude: {
7179
+ kind: "claude",
7180
+ command: "claude",
7181
+ homeDir: path22.join(os.homedir(), ".claude"),
7182
+ sync: {
7183
+ agentsFile: "CLAUDE.md",
7184
+ commandDest: (name) => path22.join("commands", `${name}.md`)
7185
+ }
7186
+ },
7187
+ codex: {
7188
+ kind: "codex",
7189
+ command: "codex",
7190
+ homeDir: path22.join(os.homedir(), ".codex"),
7191
+ sync: {
7192
+ agentsFile: "AGENTS.md",
7193
+ commandDest: (name) => path22.join("skills", name, "SKILL.md")
7194
+ }
7195
+ },
7196
+ pi: {
7197
+ kind: "pi",
7198
+ command: "pi",
7199
+ homeDir: path22.join(os.homedir(), ".pi", "agent"),
7200
+ sync: {
7201
+ agentsFile: "AGENTS.md",
7202
+ commandDest: (name) => path22.join("prompts", `${name}.md`)
7203
+ }
7204
+ }
7205
+ };
7206
+ function isHarnessAvailable(kind) {
7207
+ return checkCliAvailable(harnesses[kind].command);
7208
+ }
7209
+
7210
+ // src/shared/spawnCodex.ts
7211
+ function spawnCodex(prompt, options2 = {}) {
7212
+ const cwd = options2.cwd ?? process.cwd();
7213
+ return spawnInherit(harnesses.codex.command, [
7214
+ "-C",
7215
+ cwd,
7216
+ "--sandbox",
7217
+ options2.sandbox ?? "workspace-write",
7218
+ prompt
7219
+ ]);
7220
+ }
7221
+
7222
+ // src/shared/spawnPi.ts
7223
+ function spawnPi(prompt, options2 = {}) {
7224
+ const cwd = options2.cwd ?? process.cwd();
7225
+ return spawnInherit(harnesses.pi.command, [prompt], { cwd });
7226
+ }
7227
+
7228
+ // src/shared/spawnHarness.ts
7229
+ function spawnHarness(harness, prompt, options2 = {}) {
7230
+ if (harness === "codex") {
7231
+ return spawnCodex(prompt, {
7232
+ cwd: options2.cwd,
7233
+ sandbox: options2.allowEdits === false ? "read-only" : "workspace-write"
7234
+ });
7235
+ }
7236
+ if (harness === "pi") {
7237
+ return spawnPi(prompt, { cwd: options2.cwd });
7238
+ }
7239
+ return spawnClaude(prompt, {
7240
+ allowEdits: options2.allowEdits ?? true,
7241
+ sessionId: options2.sessionId,
7242
+ resumeSessionId: options2.resumeSessionId
7243
+ });
7244
+ }
7245
+
7145
7246
  // src/commands/backlog/watchForMarker.ts
7146
7247
  import { existsSync as existsSync24, unwatchFile, watchFile } from "fs";
7147
7248
 
@@ -7150,8 +7251,8 @@ import { existsSync as existsSync23, readFileSync as readFileSync17 } from "fs";
7150
7251
 
7151
7252
  // src/commands/backlog/writeSignal.ts
7152
7253
  import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync18 } from "fs";
7153
- import { homedir as homedir8 } from "os";
7154
- import { dirname as dirname16, join as join20 } from "path";
7254
+ import { homedir as homedir9 } from "os";
7255
+ import { dirname as dirname16, join as join21 } from "path";
7155
7256
  import chalk41 from "chalk";
7156
7257
 
7157
7258
  // src/commands/backlog/recordSignalOwner.ts
@@ -7162,10 +7263,10 @@ import {
7162
7263
  rmSync,
7163
7264
  writeFileSync as writeFileSync17
7164
7265
  } from "fs";
7165
- import { homedir as homedir7 } from "os";
7166
- import { dirname as dirname15, join as join19 } from "path";
7266
+ import { homedir as homedir8 } from "os";
7267
+ import { dirname as dirname15, join as join20 } from "path";
7167
7268
  function getOwnerPath(itemId2) {
7168
- return join19(homedir7(), ".assist", "signals", `owner-${itemId2}.json`);
7269
+ return join20(homedir8(), ".assist", "signals", `owner-${itemId2}.json`);
7169
7270
  }
7170
7271
  function recordSignalOwner(itemId2) {
7171
7272
  const sessionId = process.env.ASSIST_SESSION_ID;
@@ -7195,7 +7296,7 @@ function clearSignalOwner(itemId2) {
7195
7296
  // src/commands/backlog/writeSignal.ts
7196
7297
  function getSignalPath(sessionId = process.env.ASSIST_SESSION_ID) {
7197
7298
  if (!sessionId) return void 0;
7198
- return join20(homedir8(), ".assist", "signals", `signal-${sessionId}.json`);
7299
+ return join21(homedir9(), ".assist", "signals", `signal-${sessionId}.json`);
7199
7300
  }
7200
7301
  function resolveSignalTarget(event, data) {
7201
7302
  const caller = process.env.ASSIST_SESSION_ID;
@@ -7264,8 +7365,11 @@ function stopWatching() {
7264
7365
  }
7265
7366
 
7266
7367
  // src/commands/backlog/launchPhaseClaude.ts
7267
- async function launchPhaseClaude(prompt, spawnOptions, context) {
7268
- const { child, done: done2 } = spawnClaude(prompt, spawnOptions);
7368
+ async function launchPhaseClaude(prompt, spawnOptions, context, harness = "claude") {
7369
+ const { child, done: done2 } = spawnHarness(harness, prompt, {
7370
+ ...spawnOptions,
7371
+ cwd: process.cwd()
7372
+ });
7269
7373
  watchForMarker(child);
7270
7374
  const exitCode = await awaitClaude(done2, context);
7271
7375
  stopWatching();
@@ -7284,16 +7388,77 @@ function resumeNudge() {
7284
7388
 
7285
7389
  // src/commands/backlog/launchPhaseSession.ts
7286
7390
  function launchPhaseSession(item, phaseNumber, phase, phaseLabel2, claudeSessionId, spawnOptions) {
7287
- const resumeSessionId = spawnOptions?.resumeSessionId;
7391
+ const harness = spawnOptions?.harness;
7392
+ const { harness: _harness, ...phaseOptions } = spawnOptions ?? {};
7393
+ const resumeSessionId = phaseOptions.resumeSessionId;
7288
7394
  return launchPhaseClaude(
7289
7395
  resumeSessionId ? resumeNudge() : buildPhasePrompt(item, phaseNumber, phase),
7290
- resumeSessionId ? spawnOptions ?? {} : { ...spawnOptions, sessionId: claudeSessionId },
7291
- phaseLabel2
7396
+ resumeSessionId ? phaseOptions : { ...phaseOptions, sessionId: claudeSessionId },
7397
+ phaseLabel2,
7398
+ harness
7399
+ );
7400
+ }
7401
+
7402
+ // src/commands/backlog/resolvePhaseResult.ts
7403
+ import { existsSync as existsSync25, unlinkSync as unlinkSync4 } from "fs";
7404
+ import chalk42 from "chalk";
7405
+
7406
+ // src/commands/backlog/handleIncompletePhase.ts
7407
+ import enquirer4 from "enquirer";
7408
+ async function handleIncompletePhase() {
7409
+ const { action } = await exitOnCancel(
7410
+ enquirer4.prompt({
7411
+ type: "select",
7412
+ name: "action",
7413
+ message: "Phase was not marked complete. What would you like to do?",
7414
+ choices: ["Retry this phase", "Skip to next phase", "Abort"]
7415
+ })
7292
7416
  );
7417
+ if (action === "Retry this phase") return "retry";
7418
+ if (action === "Skip to next phase") return "skip";
7419
+ return "abort";
7420
+ }
7421
+
7422
+ // src/commands/backlog/resolvePhaseResult.ts
7423
+ function cleanupSignal() {
7424
+ const statusPath = getSignalPath();
7425
+ if (statusPath && existsSync25(statusPath)) {
7426
+ unlinkSync4(statusPath);
7427
+ }
7428
+ }
7429
+ async function isTerminalStatus(itemId2) {
7430
+ const { orm } = await getReady();
7431
+ const item = await loadItem(orm, itemId2);
7432
+ return item?.status === "done" || item?.status === "wontdo";
7433
+ }
7434
+ async function resolvePhaseResult(phaseIndex, itemId2) {
7435
+ const signalPath = getSignalPath();
7436
+ if (!signalPath || !existsSync25(signalPath)) {
7437
+ if (await isTerminalStatus(itemId2)) return { kind: "abort" };
7438
+ const action = await handleIncompletePhase();
7439
+ if (action === "abort") return { kind: "abort" };
7440
+ return action === "skip" ? { kind: "skip" } : { kind: "retry" };
7441
+ }
7442
+ const signal = readSignal();
7443
+ cleanupSignal();
7444
+ if (signal?.event === "rewind") {
7445
+ const targetPhase = signal.targetPhase;
7446
+ const targetPhaseNumber = targetPhase + 1;
7447
+ console.log(chalk42.yellow(`
7448
+ Rewinding to phase ${targetPhaseNumber}.`));
7449
+ return { kind: "rewind", targetPhase };
7450
+ }
7451
+ const phaseNumber = phaseIndex + 1;
7452
+ console.log(chalk42.green(`
7453
+ Phase ${phaseNumber} completed.`));
7454
+ return { kind: "advance" };
7293
7455
  }
7294
7456
 
7457
+ // src/commands/backlog/preparePhaseSession.ts
7458
+ import { randomUUID } from "crypto";
7459
+
7295
7460
  // src/commands/backlog/persistPhaseSession.ts
7296
- import os from "os";
7461
+ import os2 from "os";
7297
7462
 
7298
7463
  // src/shared/db/recordPhaseSession.ts
7299
7464
  import { sql as sql12 } from "drizzle-orm";
@@ -7318,8 +7483,8 @@ async function persistPhaseSession(itemId2, phaseIdx, claudeSessionId) {
7318
7483
  itemId2,
7319
7484
  phaseIdx,
7320
7485
  claudeSessionId,
7321
- os.hostname(),
7322
- os.userInfo().username
7486
+ os2.hostname(),
7487
+ os2.userInfo().username
7323
7488
  );
7324
7489
  } catch (error) {
7325
7490
  console.error(
@@ -7348,8 +7513,8 @@ async function persistPhaseSessionId(itemId2, phaseIdx, claudeSessionId) {
7348
7513
 
7349
7514
  // src/shared/emitActivity.ts
7350
7515
  import { mkdirSync as mkdirSync9, readFileSync as readFileSync18, rmSync as rmSync2, writeFileSync as writeFileSync19 } from "fs";
7351
- import { homedir as homedir9 } from "os";
7352
- import { dirname as dirname17, join as join21 } from "path";
7516
+ import { homedir as homedir10 } from "os";
7517
+ import { dirname as dirname17, join as join22 } from "path";
7353
7518
  import { z as z4 } from "zod";
7354
7519
  var activitySchema = z4.object({
7355
7520
  kind: z4.enum(["command", "backlog"]),
@@ -7364,7 +7529,7 @@ var activitySchema = z4.object({
7364
7529
  startedAt: z4.number()
7365
7530
  });
7366
7531
  function activityPath(sessionId) {
7367
- return join21(homedir9(), ".assist", "activity", `activity-${sessionId}.json`);
7532
+ return join22(homedir10(), ".assist", "activity", `activity-${sessionId}.json`);
7368
7533
  }
7369
7534
  function emitActivity(activity2) {
7370
7535
  const sessionId = process.env.ASSIST_ACTIVITY_ID;
@@ -7410,75 +7575,20 @@ function reportPhaseActivity(item, phaseNumber, totalPhases, phase, claudeSessio
7410
7575
  });
7411
7576
  }
7412
7577
 
7413
- // src/commands/backlog/resolvePhaseResult.ts
7414
- import { existsSync as existsSync25, unlinkSync as unlinkSync4 } from "fs";
7415
- import chalk42 from "chalk";
7416
-
7417
- // src/commands/backlog/handleIncompletePhase.ts
7418
- import enquirer4 from "enquirer";
7419
- async function handleIncompletePhase() {
7420
- const { action } = await exitOnCancel(
7421
- enquirer4.prompt({
7422
- type: "select",
7423
- name: "action",
7424
- message: "Phase was not marked complete. What would you like to do?",
7425
- choices: ["Retry this phase", "Skip to next phase", "Abort"]
7426
- })
7427
- );
7428
- if (action === "Retry this phase") return "retry";
7429
- if (action === "Skip to next phase") return "skip";
7430
- return "abort";
7431
- }
7432
-
7433
- // src/commands/backlog/resolvePhaseResult.ts
7434
- function cleanupSignal() {
7435
- const statusPath = getSignalPath();
7436
- if (statusPath && existsSync25(statusPath)) {
7437
- unlinkSync4(statusPath);
7438
- }
7439
- }
7440
- async function isTerminalStatus(itemId2) {
7441
- const { orm } = await getReady();
7442
- const item = await loadItem(orm, itemId2);
7443
- return item?.status === "done" || item?.status === "wontdo";
7444
- }
7445
- async function resolvePhaseResult(phaseIndex, itemId2) {
7446
- const signalPath = getSignalPath();
7447
- if (!signalPath || !existsSync25(signalPath)) {
7448
- if (await isTerminalStatus(itemId2)) return { kind: "abort" };
7449
- const action = await handleIncompletePhase();
7450
- if (action === "abort") return { kind: "abort" };
7451
- return action === "skip" ? { kind: "skip" } : { kind: "retry" };
7452
- }
7453
- const signal = readSignal();
7454
- cleanupSignal();
7455
- if (signal?.event === "rewind") {
7456
- const targetPhase = signal.targetPhase;
7457
- const targetPhaseNumber = targetPhase + 1;
7458
- console.log(chalk42.yellow(`
7459
- Rewinding to phase ${targetPhaseNumber}.`));
7460
- return { kind: "rewind", targetPhase };
7461
- }
7462
- const phaseNumber = phaseIndex + 1;
7463
- console.log(chalk42.green(`
7464
- Phase ${phaseNumber} completed.`));
7465
- return { kind: "advance" };
7466
- }
7467
-
7468
7578
  // src/commands/backlog/verifyResumeConversation.ts
7469
7579
  import chalk43 from "chalk";
7470
7580
 
7471
7581
  // src/commands/sessions/shared/findSessionJsonlPath.ts
7472
- import * as path24 from "path";
7582
+ import * as path25 from "path";
7473
7583
 
7474
7584
  // src/commands/sessions/shared/discoverSessions.ts
7475
7585
  import * as fs18 from "fs";
7476
- import * as os2 from "os";
7477
- import * as path23 from "path";
7586
+ import * as os3 from "os";
7587
+ import * as path24 from "path";
7478
7588
 
7479
7589
  // src/commands/sessions/shared/parseSessionFile.ts
7480
7590
  import * as fs17 from "fs";
7481
- import * as path22 from "path";
7591
+ import * as path23 from "path";
7482
7592
 
7483
7593
  // src/commands/sessions/shared/deriveHistoryFields.ts
7484
7594
  var KNOWN = [
@@ -7602,10 +7712,10 @@ async function readHeadLines(handle) {
7602
7712
  }
7603
7713
  function deriveProject(cwd, filePath, origin) {
7604
7714
  if (!cwd) return dirNameToProject(filePath);
7605
- return origin === "windows" ? path22.win32.basename(cwd) : path22.basename(cwd);
7715
+ return origin === "windows" ? path23.win32.basename(cwd) : path23.basename(cwd);
7606
7716
  }
7607
7717
  function dirNameToProject(filePath) {
7608
- const dirName = path22.basename(path22.dirname(filePath));
7718
+ const dirName = path23.basename(path23.dirname(filePath));
7609
7719
  const parts = dirName.split("--");
7610
7720
  return parts[parts.length - 1].replace(/-/g, "/");
7611
7721
  }
@@ -7613,7 +7723,7 @@ function dirNameToProject(filePath) {
7613
7723
  // src/commands/sessions/shared/discoverSessions.ts
7614
7724
  function sessionRoots() {
7615
7725
  const roots = [
7616
- { dir: path23.join(os2.homedir(), ".claude", "projects"), origin: "wsl" }
7726
+ { dir: path24.join(os3.homedir(), ".claude", "projects"), origin: "wsl" }
7617
7727
  ];
7618
7728
  const windowsRoot = loadConfig().sessions?.windowsProjectsRoot;
7619
7729
  if (windowsRoot) roots.push({ dir: windowsRoot, origin: "windows" });
@@ -7631,7 +7741,7 @@ async function discoverSessionJsonlPaths() {
7631
7741
  }
7632
7742
  await Promise.all(
7633
7743
  projectDirs.map(async (dirName) => {
7634
- const dirPath = path23.join(dir, dirName);
7744
+ const dirPath = path24.join(dir, dirName);
7635
7745
  let entries;
7636
7746
  try {
7637
7747
  entries = await fs18.promises.readdir(dirPath);
@@ -7640,7 +7750,7 @@ async function discoverSessionJsonlPaths() {
7640
7750
  }
7641
7751
  for (const file of entries) {
7642
7752
  if (file.endsWith(".jsonl"))
7643
- results.push({ path: path23.join(dirPath, file), origin });
7753
+ results.push({ path: path24.join(dirPath, file), origin });
7644
7754
  }
7645
7755
  })
7646
7756
  );
@@ -7670,7 +7780,7 @@ async function discoverSessions() {
7670
7780
  async function findSessionJsonlPath(sessionId) {
7671
7781
  const paths = await discoverSessionJsonlPaths();
7672
7782
  const direct = paths.find(
7673
- (p) => path24.basename(p.path, ".jsonl") === sessionId
7783
+ (p) => path25.basename(p.path, ".jsonl") === sessionId
7674
7784
  );
7675
7785
  if (direct) return direct.path;
7676
7786
  for (const p of paths) {
@@ -7703,6 +7813,21 @@ async function verifyPhaseResume(itemId2, resumeSessionId, phaseLabel2) {
7703
7813
  return true;
7704
7814
  }
7705
7815
 
7816
+ // src/commands/backlog/preparePhaseSession.ts
7817
+ async function preparePhaseSession(item, phase, phaseIndex, phaseNumber, totalPhases, phaseLabel2, spawnOptions) {
7818
+ const resumeSessionId = spawnOptions?.resumeSessionId;
7819
+ const claudeSessionId = resumeSessionId ?? randomUUID();
7820
+ if (resumeSessionId && !await verifyPhaseResume(item.id, resumeSessionId, phaseLabel2)) {
7821
+ return void 0;
7822
+ }
7823
+ reportPhaseActivity(item, phaseNumber, totalPhases, phase, claudeSessionId);
7824
+ if (!resumeSessionId) {
7825
+ await persistPhaseSessionId(item.id, phaseIndex, claudeSessionId);
7826
+ }
7827
+ await persistPhaseSession(item.id, phaseIndex, claudeSessionId);
7828
+ return { claudeSessionId };
7829
+ }
7830
+
7706
7831
  // src/commands/backlog/executePhase.ts
7707
7832
  async function executePhase(item, phaseIndex, phases, spawnOptions, totalPhases = phases.length) {
7708
7833
  const phase = phases[phaseIndex];
@@ -7714,27 +7839,27 @@ async function executePhase(item, phaseIndex, phases, spawnOptions, totalPhases
7714
7839
  `
7715
7840
  )
7716
7841
  );
7717
- const resumeSessionId = spawnOptions?.resumeSessionId;
7718
- const claudeSessionId = resumeSessionId ?? randomUUID();
7842
+ assertCodexResumeSupported(spawnOptions);
7719
7843
  process.env.ASSIST_SESSION_ID ??= String(process.pid);
7720
7844
  process.env.ASSIST_BACKLOG_ITEM_ID = String(item.id);
7721
7845
  recordSignalOwner(item.id);
7722
7846
  const phaseLabel2 = `phase ${phaseNumber}/${totalPhases}`;
7723
- if (resumeSessionId) {
7724
- if (!await verifyPhaseResume(item.id, resumeSessionId, phaseLabel2))
7725
- return { kind: "abort" };
7726
- }
7727
- reportPhaseActivity(item, phaseNumber, totalPhases, phase, claudeSessionId);
7728
- if (!resumeSessionId) {
7729
- await persistPhaseSessionId(item.id, phaseIndex, claudeSessionId);
7730
- }
7731
- await persistPhaseSession(item.id, phaseIndex, claudeSessionId);
7847
+ const phaseSession = await preparePhaseSession(
7848
+ item,
7849
+ phase,
7850
+ phaseIndex,
7851
+ phaseNumber,
7852
+ totalPhases,
7853
+ phaseLabel2,
7854
+ spawnOptions
7855
+ );
7856
+ if (!phaseSession) return { kind: "abort" };
7732
7857
  const exitCode = await launchPhaseSession(
7733
7858
  item,
7734
7859
  phaseNumber,
7735
7860
  phase,
7736
7861
  phaseLabel2,
7737
- claudeSessionId,
7862
+ phaseSession.claudeSessionId,
7738
7863
  spawnOptions
7739
7864
  );
7740
7865
  if (exitCode === CLAUDE_SPAWN_FAILED) return { kind: "abort" };
@@ -8300,7 +8425,7 @@ import chalk61 from "chalk";
8300
8425
  import { WebSocketServer } from "ws";
8301
8426
 
8302
8427
  // src/shared/getInstallDir.ts
8303
- import { execSync as execSync30 } from "child_process";
8428
+ import { execSync as execSync31 } from "child_process";
8304
8429
  import { dirname as dirname19, resolve as resolve8 } from "path";
8305
8430
  import { fileURLToPath as fileURLToPath3 } from "url";
8306
8431
  var __filename2 = fileURLToPath3(import.meta.url);
@@ -8310,7 +8435,7 @@ function getInstallDir() {
8310
8435
  }
8311
8436
  function isGitRepo(dir) {
8312
8437
  try {
8313
- const result = execSync30("git rev-parse --show-toplevel", {
8438
+ const result = execSync31("git rev-parse --show-toplevel", {
8314
8439
  cwd: dir,
8315
8440
  stdio: "pipe"
8316
8441
  }).toString().trim();
@@ -8327,11 +8452,11 @@ import {
8327
8452
  import chalk57 from "chalk";
8328
8453
 
8329
8454
  // src/lib/openBrowser.ts
8330
- import { execSync as execSync31 } from "child_process";
8455
+ import { execSync as execSync32 } from "child_process";
8331
8456
  function tryExec(commands) {
8332
8457
  for (const cmd of commands) {
8333
8458
  try {
8334
- execSync31(cmd, { stdio: "ignore" });
8459
+ execSync32(cmd, { stdio: "ignore" });
8335
8460
  return true;
8336
8461
  } catch {
8337
8462
  }
@@ -8632,13 +8757,13 @@ function gitCommonDir(cwd) {
8632
8757
 
8633
8758
  // src/shared/loadJson.ts
8634
8759
  import { existsSync as existsSync26, mkdirSync as mkdirSync11, readFileSync as readFileSync19, writeFileSync as writeFileSync20 } from "fs";
8635
- import { homedir as homedir11 } from "os";
8636
- import { join as join23 } from "path";
8760
+ import { homedir as homedir12 } from "os";
8761
+ import { join as join24 } from "path";
8637
8762
  function getStoreDir() {
8638
- return join23(homedir11(), ".assist");
8763
+ return join24(homedir12(), ".assist");
8639
8764
  }
8640
8765
  function getStorePath(filename) {
8641
- return join23(getStoreDir(), filename);
8766
+ return join24(getStoreDir(), filename);
8642
8767
  }
8643
8768
  function loadJson(filename) {
8644
8769
  const path71 = getStorePath(filename);
@@ -8709,10 +8834,10 @@ function hostedGroup(cwd, origin, clone) {
8709
8834
  // src/shared/createBundleHandler.ts
8710
8835
  import { createHash } from "crypto";
8711
8836
  import { readFileSync as readFileSync20, statSync as statSync3 } from "fs";
8712
- import { dirname as dirname20, join as join24 } from "path";
8837
+ import { dirname as dirname20, join as join25 } from "path";
8713
8838
  import { fileURLToPath as fileURLToPath4 } from "url";
8714
8839
  function createBundleHandler(importMetaUrl, bundlePath, contentType = "application/javascript") {
8715
- const file = join24(dirname20(fileURLToPath4(importMetaUrl)), bundlePath);
8840
+ const file = join25(dirname20(fileURLToPath4(importMetaUrl)), bundlePath);
8716
8841
  let cache4;
8717
8842
  return (req, res) => {
8718
8843
  const mtimeMs = statSync3(file).mtimeMs;
@@ -8849,12 +8974,12 @@ async function loadItemSummaries(orm, origin) {
8849
8974
  import { existsSync as existsSync27 } from "fs";
8850
8975
 
8851
8976
  // src/commands/backlog/cloneTargetDir.ts
8852
- import { join as join25, resolve as resolve9 } from "path";
8977
+ import { join as join26, resolve as resolve9 } from "path";
8853
8978
  function cloneTargetDir(origin, baseDir) {
8854
8979
  if (origin.startsWith("local:")) return null;
8855
8980
  const repoName = origin.split("/").filter(Boolean).pop();
8856
8981
  if (!repoName) return null;
8857
- return resolve9(join25(baseDir, repoName));
8982
+ return resolve9(join26(baseDir, repoName));
8858
8983
  }
8859
8984
 
8860
8985
  // src/commands/backlog/resolveRepoLocation.ts
@@ -9841,19 +9966,19 @@ function handleServerRuns(req, res) {
9841
9966
  // src/commands/sessions/web/getReviewSynthesis.ts
9842
9967
  import { execFile as execFile4 } from "child_process";
9843
9968
  import { readFileSync as readFileSync21 } from "fs";
9844
- import { homedir as homedir12 } from "os";
9845
- import { basename as basename8, join as join27 } from "path";
9969
+ import { homedir as homedir13 } from "os";
9970
+ import { basename as basename8, join as join28 } from "path";
9846
9971
  import { promisify as promisify3 } from "util";
9847
9972
 
9848
9973
  // src/commands/sessions/web/findSynthesisForBranch.ts
9849
9974
  import { existsSync as existsSync28, readdirSync as readdirSync2, statSync as statSync4 } from "fs";
9850
- import { basename as basename7, dirname as dirname21, join as join26 } from "path";
9975
+ import { basename as basename7, dirname as dirname21, join as join27 } from "path";
9851
9976
  function findSynthesisForBranch(repoReviewsDir, branch2) {
9852
- const branchKeyPath = join26(repoReviewsDir, `${branch2}-`);
9977
+ const branchKeyPath = join27(repoReviewsDir, `${branch2}-`);
9853
9978
  const parent = dirname21(branchKeyPath);
9854
9979
  const branchPrefix = basename7(branchKeyPath);
9855
9980
  if (!existsSync28(parent)) return null;
9856
- const synthesisFiles = readdirSync2(parent).filter((name) => name.startsWith(branchPrefix)).map((name) => join26(parent, name, "synthesis.md")).filter((path71) => existsSync28(path71)).map((path71) => ({ path: path71, mtime: statSync4(path71).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
9981
+ const synthesisFiles = readdirSync2(parent).filter((name) => name.startsWith(branchPrefix)).map((name) => join27(parent, name, "synthesis.md")).filter((path71) => existsSync28(path71)).map((path71) => ({ path: path71, mtime: statSync4(path71).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
9857
9982
  return synthesisFiles[0]?.path ?? null;
9858
9983
  }
9859
9984
 
@@ -9870,8 +9995,8 @@ async function resolveSynthesisPath(cwd) {
9870
9995
  runGit(cwd, ["rev-parse", "--show-toplevel"]),
9871
9996
  runGit(cwd, ["rev-parse", "--abbrev-ref", "HEAD"])
9872
9997
  ]);
9873
- const repoReviewsDir = join27(
9874
- homedir12(),
9998
+ const repoReviewsDir = join28(
9999
+ homedir13(),
9875
10000
  ".assist",
9876
10001
  "reviews",
9877
10002
  basename8(repoRoot)
@@ -10013,65 +10138,6 @@ async function gitStatus(req, res) {
10013
10138
  }
10014
10139
  }
10015
10140
 
10016
- // src/shared/harnesses.ts
10017
- import * as os3 from "os";
10018
- import * as path25 from "path";
10019
-
10020
- // src/shared/checkCliAvailable.ts
10021
- import { execSync as execSync32 } from "child_process";
10022
- function checkCliAvailable(cli) {
10023
- const binary = cli.split(/\s+/)[0];
10024
- const opts = {
10025
- encoding: "utf8",
10026
- stdio: ["ignore", "pipe", "pipe"]
10027
- };
10028
- try {
10029
- execSync32(`command -v ${binary}`, opts);
10030
- return true;
10031
- } catch {
10032
- try {
10033
- execSync32(`where ${binary}`, opts);
10034
- return true;
10035
- } catch {
10036
- return false;
10037
- }
10038
- }
10039
- }
10040
-
10041
- // src/shared/harnesses.ts
10042
- var harnesses = {
10043
- claude: {
10044
- kind: "claude",
10045
- command: "claude",
10046
- homeDir: path25.join(os3.homedir(), ".claude"),
10047
- sync: {
10048
- agentsFile: "CLAUDE.md",
10049
- commandDest: (name) => path25.join("commands", `${name}.md`)
10050
- }
10051
- },
10052
- codex: {
10053
- kind: "codex",
10054
- command: "codex",
10055
- homeDir: path25.join(os3.homedir(), ".codex"),
10056
- sync: {
10057
- agentsFile: "AGENTS.md",
10058
- commandDest: (name) => path25.join("skills", name, "SKILL.md")
10059
- }
10060
- },
10061
- pi: {
10062
- kind: "pi",
10063
- command: "pi",
10064
- homeDir: path25.join(os3.homedir(), ".pi", "agent"),
10065
- sync: {
10066
- agentsFile: "AGENTS.md",
10067
- commandDest: (name) => path25.join("prompts", `${name}.md`)
10068
- }
10069
- }
10070
- };
10071
- function isHarnessAvailable(kind) {
10072
- return checkCliAvailable(harnesses[kind].command);
10073
- }
10074
-
10075
10141
  // src/commands/sessions/web/harnessCapabilities.ts
10076
10142
  function harnessCapabilities(_req, res) {
10077
10143
  const config = loadConfig().harness;
@@ -12989,33 +13055,6 @@ import enquirer7 from "enquirer";
12989
13055
  // src/commands/backlog/launchMode.ts
12990
13056
  import { randomUUID as randomUUID4 } from "crypto";
12991
13057
 
12992
- // src/shared/spawnCodex.ts
12993
- function spawnCodex(prompt, options2 = {}) {
12994
- const cwd = options2.cwd ?? process.cwd();
12995
- return spawnInherit(harnesses.codex.command, ["-C", cwd, prompt]);
12996
- }
12997
-
12998
- // src/shared/spawnPi.ts
12999
- function spawnPi(prompt, options2 = {}) {
13000
- const cwd = options2.cwd ?? process.cwd();
13001
- return spawnInherit(harnesses.pi.command, [prompt], { cwd });
13002
- }
13003
-
13004
- // src/shared/spawnHarness.ts
13005
- function spawnHarness(harness, prompt, options2 = {}) {
13006
- if (harness === "codex") {
13007
- return spawnCodex(prompt, { cwd: options2.cwd });
13008
- }
13009
- if (harness === "pi") {
13010
- return spawnPi(prompt, { cwd: options2.cwd });
13011
- }
13012
- return spawnClaude(prompt, {
13013
- allowEdits: true,
13014
- sessionId: options2.sessionId,
13015
- resumeSessionId: options2.resumeSessionId
13016
- });
13017
- }
13018
-
13019
13058
  // src/commands/backlog/handleLaunchSignal.ts
13020
13059
  import chalk86 from "chalk";
13021
13060
 
@@ -13217,7 +13256,10 @@ function registerRewindCommand(cmd) {
13217
13256
 
13218
13257
  // src/commands/backlog/registerRunCommand.ts
13219
13258
  function registerRunCommand(cmd) {
13220
- cmd.command("run <id>").description("Run a backlog item's plan phase-by-phase with Claude").option("-w, --write", "Run Claude with auto permission mode (default)").option("--no-write", "Run Claude without auto permission mode").option(
13259
+ cmd.command("run <id>").description("Run a backlog item's plan phase-by-phase").option("-w, --write", "Run the harness with write access (default)").option("--no-write", "Run the harness without write access").option(
13260
+ "--harness <harness>",
13261
+ "Coding harness to launch (claude|codex|pi); defaults to the configured harness.engine"
13262
+ ).option(
13221
13263
  "--resume-session <id>",
13222
13264
  "Resume an interrupted Claude session for the current phase (used by the sessions daemon on restart)"
13223
13265
  ).action(
@@ -13225,6 +13267,7 @@ function registerRunCommand(cmd) {
13225
13267
  if (!opts.resumeSession) pullIfConfigured();
13226
13268
  await run2(id, {
13227
13269
  allowEdits: opts.write !== false,
13270
+ harness: resolveHarness(opts.harness),
13228
13271
  resumeSessionId: opts.resumeSession
13229
13272
  });
13230
13273
  }
@@ -20863,6 +20906,25 @@ function fetchLineComments(org, repo, prNumber, threadInfo) {
20863
20906
  import { mkdirSync as mkdirSync18, writeFileSync as writeFileSync35 } from "fs";
20864
20907
  import { dirname as dirname28 } from "path";
20865
20908
  import { stringify } from "yaml";
20909
+
20910
+ // src/commands/prs/removeStaleCommentsCaches.ts
20911
+ import { readdirSync as readdirSync10, unlinkSync as unlinkSync16 } from "fs";
20912
+ import { join as join58 } from "path";
20913
+ var STALE_PATTERN = /^pr-\d+-comments\.yaml$/;
20914
+ function removeStaleCommentsCaches(cwd = process.cwd()) {
20915
+ const dir = join58(cwd, ".assist");
20916
+ let entries;
20917
+ try {
20918
+ entries = readdirSync10(dir);
20919
+ } catch {
20920
+ return;
20921
+ }
20922
+ for (const entry of entries.filter((e) => STALE_PATTERN.test(e))) {
20923
+ unlinkSync16(join58(dir, entry));
20924
+ }
20925
+ }
20926
+
20927
+ // src/commands/prs/listComments/updateCommentsCache.ts
20866
20928
  function writeCommentsCache(org, repo, prNumber, comments3) {
20867
20929
  const cachePath = commentsCachePath(org, repo, prNumber);
20868
20930
  mkdirSync18(dirname28(cachePath), { recursive: true });
@@ -20874,6 +20936,7 @@ function writeCommentsCache(org, repo, prNumber, comments3) {
20874
20936
  writeFileSync35(cachePath, stringify(cacheData));
20875
20937
  }
20876
20938
  function updateCommentsCache(org, repo, prNumber, comments3) {
20939
+ removeStaleCommentsCaches();
20877
20940
  if (comments3.some((c) => c.type === "line")) {
20878
20941
  writeCommentsCache(org, repo, prNumber, comments3);
20879
20942
  } else {
@@ -23741,9 +23804,9 @@ ${annotateDiffWithLineNumbers(context.diff.trimEnd())}
23741
23804
 
23742
23805
  // src/commands/review/buildReviewPaths.ts
23743
23806
  import { homedir as homedir21 } from "os";
23744
- import { basename as basename16, join as join58 } from "path";
23807
+ import { basename as basename16, join as join59 } from "path";
23745
23808
  function buildReviewPaths(repoRoot, key) {
23746
- const reviewDir = join58(
23809
+ const reviewDir = join59(
23747
23810
  homedir21(),
23748
23811
  ".assist",
23749
23812
  "reviews",
@@ -23752,10 +23815,10 @@ function buildReviewPaths(repoRoot, key) {
23752
23815
  );
23753
23816
  return {
23754
23817
  reviewDir,
23755
- requestPath: join58(reviewDir, "request.md"),
23756
- claudePath: join58(reviewDir, "claude.md"),
23757
- codexPath: join58(reviewDir, "codex.md"),
23758
- synthesisPath: join58(reviewDir, "synthesis.md")
23818
+ requestPath: join59(reviewDir, "request.md"),
23819
+ claudePath: join59(reviewDir, "claude.md"),
23820
+ codexPath: join59(reviewDir, "codex.md"),
23821
+ synthesisPath: join59(reviewDir, "synthesis.md")
23759
23822
  };
23760
23823
  }
23761
23824
 
@@ -24294,10 +24357,10 @@ async function handlePostSynthesis(synthesisPath, options2) {
24294
24357
  }
24295
24358
 
24296
24359
  // src/commands/review/prepareReviewDir.ts
24297
- import { existsSync as existsSync49, mkdirSync as mkdirSync19, unlinkSync as unlinkSync16, writeFileSync as writeFileSync36 } from "fs";
24360
+ import { existsSync as existsSync49, mkdirSync as mkdirSync19, unlinkSync as unlinkSync17, writeFileSync as writeFileSync36 } from "fs";
24298
24361
  function clearReviewFiles(paths) {
24299
24362
  for (const path71 of [paths.claudePath, paths.codexPath, paths.synthesisPath]) {
24300
- if (existsSync49(path71)) unlinkSync16(path71);
24363
+ if (existsSync49(path71)) unlinkSync17(path71);
24301
24364
  }
24302
24365
  }
24303
24366
  function prepareReviewDir(paths, requestBody, force) {
@@ -24582,7 +24645,7 @@ function printReviewerFailures(results) {
24582
24645
  }
24583
24646
 
24584
24647
  // src/commands/review/runAndSynthesise.ts
24585
- import { existsSync as existsSync51, unlinkSync as unlinkSync18 } from "fs";
24648
+ import { existsSync as existsSync51, unlinkSync as unlinkSync19 } from "fs";
24586
24649
 
24587
24650
  // src/commands/review/buildReviewerStdin.ts
24588
24651
  var REVIEW_PROMPT = `You are acting as a reviewer for a proposed code change made by another engineer. The full review request \u2014 branch, base, changed files, and unified diff \u2014 is in the request file whose absolute path is given below.
@@ -25002,7 +25065,7 @@ function resolveClaude(args) {
25002
25065
  }
25003
25066
 
25004
25067
  // src/commands/review/runCodexReviewer.ts
25005
- import { existsSync as existsSync50, unlinkSync as unlinkSync17 } from "fs";
25068
+ import { existsSync as existsSync50, unlinkSync as unlinkSync18 } from "fs";
25006
25069
 
25007
25070
  // src/commands/review/parseCodexEvent.ts
25008
25071
  function isItemStarted(value) {
@@ -25055,7 +25118,7 @@ async function runCodexReviewer(spec) {
25055
25118
  }
25056
25119
  });
25057
25120
  if (result.exitCode !== 0 && existsSync50(spec.outputPath)) {
25058
- unlinkSync17(spec.outputPath);
25121
+ unlinkSync18(spec.outputPath);
25059
25122
  }
25060
25123
  return finaliseReviewerRun({ ...spec, command }, spinner, result);
25061
25124
  }
@@ -25201,7 +25264,7 @@ async function runAndSynthesise(args) {
25201
25264
  return { ok: false, failures };
25202
25265
  }
25203
25266
  if (anyFresh && existsSync51(paths.synthesisPath)) {
25204
- unlinkSync18(paths.synthesisPath);
25267
+ unlinkSync19(paths.synthesisPath);
25205
25268
  }
25206
25269
  const synthesisResult = await synthesise(paths, { multi });
25207
25270
  if (synthesisResult.exitCode !== 0) failures.push(synthesisResult);
@@ -26073,14 +26136,14 @@ async function configure() {
26073
26136
  }
26074
26137
 
26075
26138
  // src/commands/transcript/list.ts
26076
- import { existsSync as existsSync52, readdirSync as readdirSync10, statSync as statSync9 } from "fs";
26077
- import { join as join59 } from "path";
26139
+ import { existsSync as existsSync52, readdirSync as readdirSync11, statSync as statSync9 } from "fs";
26140
+ import { join as join60 } from "path";
26078
26141
  function list4() {
26079
26142
  const { vttDir } = getTranscriptConfig();
26080
26143
  if (!existsSync52(vttDir)) return;
26081
- for (const entry of readdirSync10(vttDir)) {
26144
+ for (const entry of readdirSync11(vttDir)) {
26082
26145
  if (!entry.endsWith(".vtt")) continue;
26083
- if (statSync9(join59(vttDir, entry)).isDirectory()) continue;
26146
+ if (statSync9(join60(vttDir, entry)).isDirectory()) continue;
26084
26147
  console.log(entry);
26085
26148
  }
26086
26149
  }
@@ -26093,7 +26156,7 @@ import {
26093
26156
  renameSync as renameSync2,
26094
26157
  writeFileSync as writeFileSync38
26095
26158
  } from "fs";
26096
- import { basename as basename17, join as join60 } from "path";
26159
+ import { basename as basename17, join as join61 } from "path";
26097
26160
 
26098
26161
  // src/commands/transcript/cleanText.ts
26099
26162
  function cleanText(text17) {
@@ -26306,9 +26369,9 @@ function convertVttToMarkdown(inputPath) {
26306
26369
  return formatChatLog(messages);
26307
26370
  }
26308
26371
  function archiveRawVtt(vttDir, sourcePath, filename) {
26309
- const processedDir = join60(vttDir, "processed");
26372
+ const processedDir = join61(vttDir, "processed");
26310
26373
  mkdirSync20(processedDir, { recursive: true });
26311
- renameSync2(sourcePath, join60(processedDir, filename));
26374
+ renameSync2(sourcePath, join61(processedDir, filename));
26312
26375
  }
26313
26376
  function move(file, options2) {
26314
26377
  const { date, client } = options2;
@@ -26318,19 +26381,19 @@ function move(file, options2) {
26318
26381
  }
26319
26382
  const { vttDir, transcriptsDir, summaryDir } = getTranscriptConfig();
26320
26383
  const filename = basename17(file);
26321
- const sourcePath = join60(vttDir, filename);
26384
+ const sourcePath = join61(vttDir, filename);
26322
26385
  if (!existsSync53(sourcePath)) {
26323
26386
  console.error(`Error: VTT file not found: ${sourcePath}`);
26324
26387
  process.exit(1);
26325
26388
  }
26326
26389
  const base = basename17(filename, ".vtt").replace(/ Transcription$/, "");
26327
26390
  const outputName = `${date} ${base}.md`;
26328
- const formattedDir = join60(transcriptsDir, client);
26391
+ const formattedDir = join61(transcriptsDir, client);
26329
26392
  mkdirSync20(formattedDir, { recursive: true });
26330
- const formattedPath = join60(formattedDir, outputName);
26393
+ const formattedPath = join61(formattedDir, outputName);
26331
26394
  writeFileSync38(formattedPath, convertVttToMarkdown(sourcePath), "utf8");
26332
26395
  archiveRawVtt(vttDir, sourcePath, filename);
26333
- const summaryPath = join60(summaryDir, client, outputName);
26396
+ const summaryPath = join61(summaryDir, client, outputName);
26334
26397
  console.log(`Formatted transcript: ${formattedPath}`);
26335
26398
  console.log(`Summary target: ${summaryPath}`);
26336
26399
  }
@@ -26450,38 +26513,38 @@ function registerVerify(program2) {
26450
26513
 
26451
26514
  // src/commands/voice/devices.ts
26452
26515
  import { spawnSync as spawnSync6 } from "child_process";
26453
- import { join as join62 } from "path";
26516
+ import { join as join63 } from "path";
26454
26517
 
26455
26518
  // src/commands/voice/shared.ts
26456
26519
  import { homedir as homedir22 } from "os";
26457
- import { dirname as dirname30, join as join61 } from "path";
26520
+ import { dirname as dirname30, join as join62 } from "path";
26458
26521
  import { fileURLToPath as fileURLToPath7 } from "url";
26459
26522
  var __dirname5 = dirname30(fileURLToPath7(import.meta.url));
26460
- var VOICE_DIR = join61(homedir22(), ".assist", "voice");
26523
+ var VOICE_DIR = join62(homedir22(), ".assist", "voice");
26461
26524
  var voicePaths = {
26462
26525
  dir: VOICE_DIR,
26463
- pid: join61(VOICE_DIR, "voice.pid"),
26464
- log: join61(VOICE_DIR, "voice.log"),
26465
- venv: join61(VOICE_DIR, ".venv"),
26466
- lock: join61(VOICE_DIR, "voice.lock")
26526
+ pid: join62(VOICE_DIR, "voice.pid"),
26527
+ log: join62(VOICE_DIR, "voice.log"),
26528
+ venv: join62(VOICE_DIR, ".venv"),
26529
+ lock: join62(VOICE_DIR, "voice.lock")
26467
26530
  };
26468
26531
  function getPythonDir() {
26469
- return join61(__dirname5, "commands", "voice", "python");
26532
+ return join62(__dirname5, "commands", "voice", "python");
26470
26533
  }
26471
26534
  function getVenvPython() {
26472
- return process.platform === "win32" ? join61(voicePaths.venv, "Scripts", "python.exe") : join61(voicePaths.venv, "bin", "python");
26535
+ return process.platform === "win32" ? join62(voicePaths.venv, "Scripts", "python.exe") : join62(voicePaths.venv, "bin", "python");
26473
26536
  }
26474
26537
  function getLockDir() {
26475
26538
  const config = loadConfig();
26476
26539
  return config.voice?.lockDir ?? VOICE_DIR;
26477
26540
  }
26478
26541
  function getLockFile() {
26479
- return join61(getLockDir(), "voice.lock");
26542
+ return join62(getLockDir(), "voice.lock");
26480
26543
  }
26481
26544
 
26482
26545
  // src/commands/voice/devices.ts
26483
26546
  function devices() {
26484
- const script = join62(getPythonDir(), "list_devices.py");
26547
+ const script = join63(getPythonDir(), "list_devices.py");
26485
26548
  spawnSync6(getVenvPython(), [script], { stdio: "inherit" });
26486
26549
  }
26487
26550
 
@@ -26516,12 +26579,12 @@ function logs(options2) {
26516
26579
  // src/commands/voice/setup.ts
26517
26580
  import { spawnSync as spawnSync7 } from "child_process";
26518
26581
  import { mkdirSync as mkdirSync22 } from "fs";
26519
- import { join as join64 } from "path";
26582
+ import { join as join65 } from "path";
26520
26583
 
26521
26584
  // src/commands/voice/checkLockFile.ts
26522
26585
  import { execSync as execSync59 } from "child_process";
26523
26586
  import { existsSync as existsSync55, mkdirSync as mkdirSync21, readFileSync as readFileSync45, writeFileSync as writeFileSync39 } from "fs";
26524
- import { join as join63 } from "path";
26587
+ import { join as join64 } from "path";
26525
26588
  function isProcessAlive2(pid) {
26526
26589
  try {
26527
26590
  process.kill(pid, 0);
@@ -26558,7 +26621,7 @@ function bootstrapVenv() {
26558
26621
  }
26559
26622
  function writeLockFile(pid) {
26560
26623
  const lockFile = getLockFile();
26561
- mkdirSync21(join63(lockFile, ".."), { recursive: true });
26624
+ mkdirSync21(join64(lockFile, ".."), { recursive: true });
26562
26625
  writeFileSync39(
26563
26626
  lockFile,
26564
26627
  JSON.stringify({
@@ -26574,7 +26637,7 @@ function setup() {
26574
26637
  mkdirSync22(voicePaths.dir, { recursive: true });
26575
26638
  bootstrapVenv();
26576
26639
  console.log("\nDownloading models...\n");
26577
- const script = join64(getPythonDir(), "setup_models.py");
26640
+ const script = join65(getPythonDir(), "setup_models.py");
26578
26641
  const result = spawnSync7(getVenvPython(), [script], {
26579
26642
  stdio: "inherit",
26580
26643
  env: { ...process.env, VOICE_LOG_FILE: voicePaths.log }
@@ -26588,7 +26651,7 @@ function setup() {
26588
26651
  // src/commands/voice/start.ts
26589
26652
  import { spawn as spawn8 } from "child_process";
26590
26653
  import { mkdirSync as mkdirSync23, writeFileSync as writeFileSync40 } from "fs";
26591
- import { join as join65 } from "path";
26654
+ import { join as join66 } from "path";
26592
26655
 
26593
26656
  // src/commands/voice/buildDaemonEnv.ts
26594
26657
  function buildDaemonEnv(options2) {
@@ -26626,7 +26689,7 @@ function start2(options2) {
26626
26689
  bootstrapVenv();
26627
26690
  const debug = options2.debug || options2.foreground || process.platform === "win32";
26628
26691
  const env = buildDaemonEnv({ debug });
26629
- const script = join65(getPythonDir(), "voice_daemon.py");
26692
+ const script = join66(getPythonDir(), "voice_daemon.py");
26630
26693
  const python = getVenvPython();
26631
26694
  if (options2.foreground) {
26632
26695
  spawnForeground(python, script, env);
@@ -26674,7 +26737,7 @@ function status2() {
26674
26737
  }
26675
26738
 
26676
26739
  // src/commands/voice/stop.ts
26677
- import { existsSync as existsSync57, readFileSync as readFileSync47, unlinkSync as unlinkSync19 } from "fs";
26740
+ import { existsSync as existsSync57, readFileSync as readFileSync47, unlinkSync as unlinkSync20 } from "fs";
26678
26741
  function stop2() {
26679
26742
  if (!existsSync57(voicePaths.pid)) {
26680
26743
  console.log("Voice daemon is not running (no PID file)");
@@ -26688,12 +26751,12 @@ function stop2() {
26688
26751
  console.log(`Voice daemon (PID ${pid}) is not running`);
26689
26752
  }
26690
26753
  try {
26691
- unlinkSync19(voicePaths.pid);
26754
+ unlinkSync20(voicePaths.pid);
26692
26755
  } catch {
26693
26756
  }
26694
26757
  try {
26695
26758
  const lockFile = getLockFile();
26696
- if (existsSync57(lockFile)) unlinkSync19(lockFile);
26759
+ if (existsSync57(lockFile)) unlinkSync20(lockFile);
26697
26760
  } catch {
26698
26761
  }
26699
26762
  console.log("Voice daemon stopped");
@@ -27153,17 +27216,17 @@ async function auth() {
27153
27216
 
27154
27217
  // src/commands/roam/postRoamActivity.ts
27155
27218
  import { execFileSync as execFileSync12 } from "child_process";
27156
- import { readdirSync as readdirSync11, readFileSync as readFileSync48, statSync as statSync10 } from "fs";
27157
- import { join as join66 } from "path";
27219
+ import { readdirSync as readdirSync12, readFileSync as readFileSync48, statSync as statSync10 } from "fs";
27220
+ import { join as join67 } from "path";
27158
27221
  function findPortFile(roamDir) {
27159
27222
  let entries;
27160
27223
  try {
27161
- entries = readdirSync11(roamDir);
27224
+ entries = readdirSync12(roamDir);
27162
27225
  } catch {
27163
27226
  return void 0;
27164
27227
  }
27165
27228
  const candidates = entries.filter((name) => /^roam-local-api(-[^.]+)?\.port$/.test(name)).map((name) => {
27166
- const path71 = join66(roamDir, name);
27229
+ const path71 = join67(roamDir, name);
27167
27230
  try {
27168
27231
  return { path: path71, mtimeMs: statSync10(path71).mtimeMs };
27169
27232
  } catch {
@@ -27175,7 +27238,7 @@ function findPortFile(roamDir) {
27175
27238
  function postRoamActivity(app, event) {
27176
27239
  const appData = process.env.APPDATA;
27177
27240
  if (!appData) return;
27178
- const portFile = findPortFile(join66(appData, "Roam"));
27241
+ const portFile = findPortFile(join67(appData, "Roam"));
27179
27242
  if (!portFile) return;
27180
27243
  let port;
27181
27244
  try {
@@ -27408,13 +27471,13 @@ function runPreCommands(pre, cwd) {
27408
27471
  // src/commands/run/spawnRunCommand.ts
27409
27472
  import { execFileSync as execFileSync13, spawn as spawn9 } from "child_process";
27410
27473
  import { existsSync as existsSync58 } from "fs";
27411
- import { dirname as dirname31, join as join67, resolve as resolve16 } from "path";
27474
+ import { dirname as dirname31, join as join68, resolve as resolve16 } from "path";
27412
27475
  function resolveCommand2(command) {
27413
27476
  if (process.platform !== "win32" || command !== "bash") return command;
27414
27477
  try {
27415
27478
  const gitPath = execFileSync13("where", ["git"], { encoding: "utf8" }).trim().split("\r\n")[0];
27416
27479
  const gitRoot = resolve16(dirname31(gitPath), "..");
27417
- const gitBash = join67(gitRoot, "bin", "bash.exe");
27480
+ const gitBash = join68(gitRoot, "bin", "bash.exe");
27418
27481
  if (existsSync58(gitBash)) return gitBash;
27419
27482
  } catch {
27420
27483
  }
@@ -27503,7 +27566,7 @@ async function run3(name, args) {
27503
27566
 
27504
27567
  // src/commands/run/add.ts
27505
27568
  import { mkdirSync as mkdirSync24, writeFileSync as writeFileSync41 } from "fs";
27506
- import { join as join68 } from "path";
27569
+ import { join as join69 } from "path";
27507
27570
 
27508
27571
  // src/commands/run/extractOption.ts
27509
27572
  function extractOption(args, flag) {
@@ -27564,7 +27627,7 @@ function saveNewRunConfig(name, command, args, cwd) {
27564
27627
  saveConfig(config);
27565
27628
  }
27566
27629
  function createCommandFile(name) {
27567
- const dir = join68(".claude", "commands");
27630
+ const dir = join69(".claude", "commands");
27568
27631
  mkdirSync24(dir, { recursive: true });
27569
27632
  const content = `---
27570
27633
  description: Run ${name}
@@ -27572,7 +27635,7 @@ description: Run ${name}
27572
27635
 
27573
27636
  Run \`assist run ${name} $ARGUMENTS 2>&1\`.
27574
27637
  `;
27575
- const filePath = join68(dir, `${name}.md`);
27638
+ const filePath = join69(dir, `${name}.md`);
27576
27639
  writeFileSync41(filePath, content);
27577
27640
  console.log(`Created command file: ${filePath}`);
27578
27641
  }
@@ -27628,8 +27691,8 @@ function link2() {
27628
27691
  }
27629
27692
 
27630
27693
  // src/commands/run/remove.ts
27631
- import { existsSync as existsSync59, unlinkSync as unlinkSync20 } from "fs";
27632
- import { join as join69 } from "path";
27694
+ import { existsSync as existsSync59, unlinkSync as unlinkSync21 } from "fs";
27695
+ import { join as join70 } from "path";
27633
27696
  function findRemoveIndex() {
27634
27697
  const idx = process.argv.indexOf("remove");
27635
27698
  if (idx === -1 || idx + 1 >= process.argv.length) return -1;
@@ -27644,9 +27707,9 @@ function parseRemoveName() {
27644
27707
  return process.argv[idx + 1];
27645
27708
  }
27646
27709
  function deleteCommandFile(name) {
27647
- const filePath = join69(".claude", "commands", `${name}.md`);
27710
+ const filePath = join70(".claude", "commands", `${name}.md`);
27648
27711
  if (existsSync59(filePath)) {
27649
- unlinkSync20(filePath);
27712
+ unlinkSync21(filePath);
27650
27713
  console.log(`Deleted command file: ${filePath}`);
27651
27714
  }
27652
27715
  }
@@ -27699,9 +27762,9 @@ function registerRun(program2) {
27699
27762
 
27700
27763
  // src/commands/screenshot/index.ts
27701
27764
  import { execSync as execSync61 } from "child_process";
27702
- import { existsSync as existsSync60, mkdirSync as mkdirSync25, unlinkSync as unlinkSync21, writeFileSync as writeFileSync42 } from "fs";
27765
+ import { existsSync as existsSync60, mkdirSync as mkdirSync25, unlinkSync as unlinkSync22, writeFileSync as writeFileSync42 } from "fs";
27703
27766
  import { tmpdir as tmpdir8 } from "os";
27704
- import { join as join70, resolve as resolve18 } from "path";
27767
+ import { join as join71, resolve as resolve18 } from "path";
27705
27768
  import chalk209 from "chalk";
27706
27769
 
27707
27770
  // src/commands/screenshot/captureWindowPs1.ts
@@ -27838,7 +27901,7 @@ function buildOutputPath(outputDir, processName) {
27838
27901
  return resolve18(outputDir, `${processName}-${timestamp6}.png`);
27839
27902
  }
27840
27903
  function runPowerShellScript(processName, outputPath) {
27841
- const scriptPath = join70(tmpdir8(), `assist-screenshot-${Date.now()}.ps1`);
27904
+ const scriptPath = join71(tmpdir8(), `assist-screenshot-${Date.now()}.ps1`);
27842
27905
  writeFileSync42(scriptPath, captureWindowPs1, "utf8");
27843
27906
  try {
27844
27907
  execSync61(
@@ -27846,7 +27909,7 @@ function runPowerShellScript(processName, outputPath) {
27846
27909
  { stdio: ["ignore", "pipe", "pipe"], encoding: "utf8" }
27847
27910
  );
27848
27911
  } finally {
27849
- unlinkSync21(scriptPath);
27912
+ unlinkSync22(scriptPath);
27850
27913
  }
27851
27914
  }
27852
27915
  function screenshot(processName) {
@@ -28503,12 +28566,12 @@ import { basename as basename18 } from "path";
28503
28566
 
28504
28567
  // src/commands/sessions/daemon/worktree/deleteStrandedTree.ts
28505
28568
  import { existsSync as existsSync62 } from "fs";
28506
- import { join as join73 } from "path";
28569
+ import { join as join74 } from "path";
28507
28570
 
28508
28571
  // src/commands/sessions/daemon/worktree/deleteTreeDirectly.ts
28509
28572
  import { statSync as statSync12 } from "fs";
28510
28573
  import { rm as rm2 } from "fs/promises";
28511
- import { join as join72 } from "path";
28574
+ import { join as join73 } from "path";
28512
28575
  async function deleteTreeDirectly(clone, worktreePath, why) {
28513
28576
  if (holdsAGitDirectoryRatherThanALink(worktreePath)) {
28514
28577
  daemonLog(
@@ -28535,7 +28598,7 @@ async function deleteTreeDirectly(clone, worktreePath, why) {
28535
28598
  return true;
28536
28599
  }
28537
28600
  function holdsAGitDirectoryRatherThanALink(worktreePath) {
28538
- return statSync12(join72(worktreePath, ".git"), {
28601
+ return statSync12(join73(worktreePath, ".git"), {
28539
28602
  throwIfNoEntry: false
28540
28603
  })?.isDirectory() === true;
28541
28604
  }
@@ -28571,7 +28634,7 @@ async function deleteStrandedTree(clone, worktreePath, cause) {
28571
28634
  );
28572
28635
  }
28573
28636
  function strandedReason(worktreePath, cause) {
28574
- if (!existsSync62(join73(worktreePath, ".git")))
28637
+ if (!existsSync62(join74(worktreePath, ".git")))
28575
28638
  return "its .git link is already gone";
28576
28639
  if (/not a working tree|not a git repository/i.test(reason2(cause)))
28577
28640
  return "git no longer recognises it as a working tree";
@@ -31775,7 +31838,7 @@ var SessionManager = class {
31775
31838
  };
31776
31839
 
31777
31840
  // src/commands/sessions/daemon/startDaemonServer.ts
31778
- import { unlinkSync as unlinkSync23 } from "fs";
31841
+ import { unlinkSync as unlinkSync24 } from "fs";
31779
31842
  import * as net3 from "net";
31780
31843
 
31781
31844
  // src/commands/sessions/daemon/handleConnection.ts
@@ -32106,7 +32169,7 @@ function handleConnection(socket, manager) {
32106
32169
  }
32107
32170
 
32108
32171
  // src/commands/sessions/daemon/onListening.ts
32109
- import { unlinkSync as unlinkSync22, writeFileSync as writeFileSync43 } from "fs";
32172
+ import { unlinkSync as unlinkSync23, writeFileSync as writeFileSync43 } from "fs";
32110
32173
 
32111
32174
  // src/commands/sessions/daemon/startPidFileWatchdog.ts
32112
32175
  import { readFileSync as readFileSync52 } from "fs";
@@ -32149,12 +32212,12 @@ function onListening(manager, checkAutoExit) {
32149
32212
  function cleanupOwnedFiles() {
32150
32213
  if (!ownsPidFile()) return;
32151
32214
  try {
32152
- unlinkSync22(daemonPaths.pid);
32215
+ unlinkSync23(daemonPaths.pid);
32153
32216
  } catch {
32154
32217
  }
32155
32218
  if (process.platform !== "win32") {
32156
32219
  try {
32157
- unlinkSync22(daemonPaths.socket);
32220
+ unlinkSync23(daemonPaths.socket);
32158
32221
  } catch {
32159
32222
  }
32160
32223
  }
@@ -32201,7 +32264,7 @@ async function recoverFromAddrInUse(server, manager, checkAutoExit) {
32201
32264
  daemonLog("removing stale socket left by a crashed daemon");
32202
32265
  if (process.platform !== "win32") {
32203
32266
  try {
32204
- unlinkSync23(daemonPaths.socket);
32267
+ unlinkSync24(daemonPaths.socket);
32205
32268
  } catch {
32206
32269
  }
32207
32270
  }
@@ -32538,9 +32601,9 @@ function buildLimitsSegment(rateLimits) {
32538
32601
 
32539
32602
  // src/commands/readGitBranch.ts
32540
32603
  import { readFileSync as readFileSync54, statSync as statSync14 } from "fs";
32541
- import { isAbsolute as isAbsolute4, join as join75, resolve as resolve19 } from "path";
32604
+ import { isAbsolute as isAbsolute4, join as join76, resolve as resolve19 } from "path";
32542
32605
  function resolveGitDir(cwd) {
32543
- const dotGit = join75(cwd, ".git");
32606
+ const dotGit = join76(cwd, ".git");
32544
32607
  let stat3;
32545
32608
  try {
32546
32609
  stat3 = statSync14(dotGit);
@@ -32570,7 +32633,7 @@ function readGitBranch(cwd) {
32570
32633
  }
32571
32634
  let head;
32572
32635
  try {
32573
- head = readFileSync54(join75(gitDir, "HEAD"), "utf8");
32636
+ head = readFileSync54(join76(gitDir, "HEAD"), "utf8");
32574
32637
  } catch {
32575
32638
  return null;
32576
32639
  }