@staff0rd/assist 0.774.1 → 0.775.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.774.1",
9
+ version: "0.775.0",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -10943,209 +10943,11 @@ function delay(ms) {
10943
10943
  return new Promise((resolve28) => setTimeout(resolve28, ms));
10944
10944
  }
10945
10945
 
10946
- // src/commands/sessions/daemon/originForCwd.ts
10947
- var cache = /* @__PURE__ */ new Map();
10948
- function originResolutionForCwd(cwd) {
10949
- if (!cwd) return void 0;
10950
- const cached = cache.get(cwd);
10951
- if (cached !== void 0) return { origin: cached, stable: true };
10952
- const resolved = resolveCurrentOrigin(cwd);
10953
- if (resolved.stable) cache.set(cwd, resolved.origin);
10954
- return resolved;
10955
- }
10956
- function originForCwd(cwd) {
10957
- return originResolutionForCwd(cwd)?.origin;
10958
- }
10959
-
10960
- // src/commands/sessions/daemon/repoDirExists.ts
10961
- import { existsSync as existsSync31 } from "fs";
10962
- function repoDirExists(cwd) {
10963
- return existsSync31(cwd);
10964
- }
10965
-
10966
- // src/commands/sessions/daemon/worktree/git.ts
10967
- import { execFile as execFile2, execFileSync as execFileSync2 } from "child_process";
10968
- function isWindowsPath(cwd) {
10969
- return /^[A-Za-z]:[\\/]/.test(cwd);
10970
- }
10971
- function gitInvocation(cwd, args) {
10972
- const windows = isWindowsPath(cwd);
10973
- return {
10974
- file: windows ? "git.exe" : "git",
10975
- argv: windows ? ["-C", cwd, ...args] : args,
10976
- options: {
10977
- encoding: "utf8",
10978
- windowsHide: true,
10979
- ...windows ? {} : { cwd }
10980
- }
10981
- };
10982
- }
10983
- function git(cwd, args) {
10984
- const { file, argv, options: options2 } = gitInvocation(cwd, args);
10985
- return new Promise((resolve28, reject) => {
10986
- execFile2(file, argv, options2, (error, stdout) => {
10987
- if (error) reject(error);
10988
- else resolve28(stdout.toString());
10989
- });
10990
- });
10991
- }
10992
- async function gitOrNull(cwd, args) {
10993
- try {
10994
- return (await git(cwd, args)).trim() || null;
10995
- } catch {
10996
- return null;
10997
- }
10998
- }
10999
- async function gitResult(cwd, args) {
11000
- try {
11001
- return { ok: true, out: (await git(cwd, args)).trim() };
11002
- } catch (error) {
11003
- return {
11004
- ok: false,
11005
- error: error instanceof Error ? error.message : String(error)
11006
- };
11007
- }
11008
- }
11009
- function gitSyncResult(cwd, args) {
11010
- try {
11011
- return { ok: true, out: gitSync(cwd, args).trim() };
11012
- } catch (error) {
11013
- return {
11014
- ok: false,
11015
- error: error instanceof Error ? error.message : String(error)
11016
- };
11017
- }
11018
- }
11019
- function gitSync(cwd, args) {
11020
- const { file, argv, options: options2 } = gitInvocation(cwd, args);
11021
- return execFileSync2(file, argv, {
11022
- ...options2,
11023
- stdio: ["pipe", "pipe", "pipe"]
11024
- }).toString();
11025
- }
11026
- function gitSyncOrNull(cwd, args) {
11027
- try {
11028
- return gitSync(cwd, args).trim() || null;
11029
- } catch {
11030
- return null;
11031
- }
11032
- }
11033
-
11034
- // src/commands/sessions/daemon/worktree/listWorktreePaths.ts
11035
- function listWorktreePaths(cwd) {
11036
- const out = gitSyncOrNull(cwd, ["worktree", "list", "--porcelain"]);
11037
- if (!out) return [];
11038
- return out.split("\n").filter((line) => line.startsWith("worktree ")).map((line) => line.slice("worktree ".length).trim());
11039
- }
11040
- function mainWorktree(cwd) {
11041
- return listWorktreePaths(cwd)[0] ?? null;
11042
- }
11043
- function listLocalBranches(cwd) {
11044
- const out = gitSyncOrNull(cwd, [
11045
- "for-each-ref",
11046
- "--format=%(refname:short)",
11047
- "refs/heads"
11048
- ]);
11049
- return out ? out.split("\n").map((line) => line.trim()) : [];
11050
- }
11051
- function gitCommonDir(cwd) {
11052
- return gitSyncOrNull(cwd, [
11053
- "rev-parse",
11054
- "--path-format=absolute",
11055
- "--git-common-dir"
11056
- ]);
11057
- }
11058
-
11059
- // src/shared/loadJson.ts
11060
- import { existsSync as existsSync32, mkdirSync as mkdirSync11, readFileSync as readFileSync20, writeFileSync as writeFileSync21 } from "fs";
11061
- import { homedir as homedir14 } from "os";
11062
- import { join as join36 } from "path";
11063
- function getStoreDir() {
11064
- return process.env.ASSIST_STORE_DIR || join36(homedir14(), ".assist");
11065
- }
11066
- function getStorePath(filename) {
11067
- return join36(getStoreDir(), filename);
11068
- }
11069
- function loadJson(filename) {
11070
- const path106 = getStorePath(filename);
11071
- if (existsSync32(path106)) {
11072
- try {
11073
- return JSON.parse(readFileSync20(path106, "utf8"));
11074
- } catch {
11075
- return {};
11076
- }
11077
- }
11078
- return {};
11079
- }
11080
- function saveJson(filename, data) {
11081
- const dir = getStoreDir();
11082
- if (!existsSync32(dir)) {
11083
- mkdirSync11(dir, { recursive: true });
11084
- }
11085
- writeFileSync21(getStorePath(filename), JSON.stringify(data, null, 2));
11086
- }
11087
-
11088
- // src/commands/sessions/daemon/worktree/readWorktreeRegistry.ts
11089
- var REGISTRY_FILE = "worktrees.json";
11090
- function readWorktreeRegistry() {
11091
- const data = loadJson(REGISTRY_FILE);
11092
- return Object.entries(data).filter(([, record]) => !record.reaped).map(([path106, record]) => ({ path: path106, ...record }));
11093
- }
11094
- function worktreeAttributionIncludingReaped(path106) {
11095
- const record = loadJson(REGISTRY_FILE)[path106];
11096
- return record ? { clone: record.clone, origin: record.origin } : void 0;
11097
- }
11098
- function recordWorktree(path106, clone, origin) {
11099
- const data = loadJson(REGISTRY_FILE);
11100
- data[path106] = { clone, origin };
11101
- saveJson(REGISTRY_FILE, data);
11102
- }
11103
- function forgetWorktree(path106) {
11104
- const data = loadJson(REGISTRY_FILE);
11105
- const record = data[path106];
11106
- if (!record || record.reaped) return;
11107
- data[path106] = { ...record, reaped: true };
11108
- saveJson(REGISTRY_FILE, data);
11109
- }
11110
-
11111
- // src/commands/sessions/daemon/repoGroupForCwd.ts
11112
- var cache2 = /* @__PURE__ */ new Map();
11113
- function repoGroupForCwd(cwd) {
11114
- if (!cwd) return void 0;
11115
- if (cache2.has(cwd)) return cache2.get(cwd);
11116
- const { group, stable } = resolveRepoGroup(cwd);
11117
- if (stable) cache2.set(cwd, group);
11118
- return group;
11119
- }
11120
- function resolveRepoGroup(cwd) {
11121
- const clone = mainWorktree(cwd);
11122
- const origin = clone ? originResolutionForCwd(clone) : void 0;
11123
- if (clone && origin)
11124
- return {
11125
- group: hostedGroup(origin.origin, clone),
11126
- stable: origin.stable
11127
- };
11128
- const reaped = worktreeAttributionIncludingReaped(cwd);
11129
- if (!reaped) return { group: void 0, stable: repoDirExists(cwd) };
11130
- const current = currentOriginOfClone(reaped.clone);
11131
- return {
11132
- group: hostedGroup(current?.origin ?? reaped.origin, reaped.clone),
11133
- stable: current?.stable === true
11134
- };
11135
- }
11136
- function currentOriginOfClone(clone) {
11137
- const main = mainWorktree(clone);
11138
- return main ? originResolutionForCwd(main) : void 0;
11139
- }
11140
- function hostedGroup(origin, clone) {
11141
- return { origin, clone };
11142
- }
11143
-
11144
10946
  // src/commands/sessions/nodes/tailscaleStatus.ts
11145
- import { execFile as execFile3 } from "child_process";
11146
- import { existsSync as existsSync33 } from "fs";
10947
+ import { execFile as execFile2 } from "child_process";
10948
+ import { existsSync as existsSync31 } from "fs";
11147
10949
  import { promisify as promisify2 } from "util";
11148
- var execFileAsync2 = promisify2(execFile3);
10950
+ var execFileAsync2 = promisify2(execFile2);
11149
10951
  var STATUS_TIMEOUT_MS = 1e4;
11150
10952
  var INSTALLED_CLI = {
11151
10953
  wsl: "/mnt/c/Program Files/Tailscale/tailscale.exe",
@@ -11155,7 +10957,7 @@ var INSTALLED_CLI = {
11155
10957
  function tailscaleCli() {
11156
10958
  const platform = detectPlatform();
11157
10959
  const installed = INSTALLED_CLI[platform];
11158
- if (installed && existsSync33(installed)) return installed;
10960
+ if (installed && existsSync31(installed)) return installed;
11159
10961
  return platform === "wsl" ? "tailscale.exe" : "tailscale";
11160
10962
  }
11161
10963
  async function tailscaleStatus() {
@@ -11214,9 +11016,9 @@ async function runningStatus(status2) {
11214
11016
  }
11215
11017
 
11216
11018
  // src/commands/sessions/nodes/runTailscale.ts
11217
- import { execFile as execFile4 } from "child_process";
11019
+ import { execFile as execFile3 } from "child_process";
11218
11020
  import { promisify as promisify3 } from "util";
11219
- var execFileAsync3 = promisify3(execFile4);
11021
+ var execFileAsync3 = promisify3(execFile3);
11220
11022
  async function runTailscale(args) {
11221
11023
  const { stdout } = await execFileAsync3(tailscaleCli(), args, {
11222
11024
  encoding: "utf8",
@@ -11433,16 +11235,16 @@ function attachSessionSocket(server, ctx) {
11433
11235
 
11434
11236
  // src/shared/createBundleHandler.ts
11435
11237
  import { createHash } from "crypto";
11436
- import { readFileSync as readFileSync21, statSync as statSync5 } from "fs";
11437
- import { dirname as dirname21, join as join37 } from "path";
11238
+ import { readFileSync as readFileSync20, statSync as statSync5 } from "fs";
11239
+ import { dirname as dirname21, join as join36 } from "path";
11438
11240
  import { fileURLToPath as fileURLToPath4 } from "url";
11439
11241
  function createBundleHandler(importMetaUrl, bundlePath, contentType = "application/javascript") {
11440
- const file = join37(dirname21(fileURLToPath4(importMetaUrl)), bundlePath);
11242
+ const file = join36(dirname21(fileURLToPath4(importMetaUrl)), bundlePath);
11441
11243
  let cache6;
11442
11244
  return (req, res) => {
11443
11245
  const mtimeMs = statSync5(file).mtimeMs;
11444
11246
  if (cache6?.mtimeMs !== mtimeMs) {
11445
- const body = readFileSync21(file, "utf8");
11247
+ const body = readFileSync20(file, "utf8");
11446
11248
  const etag = `"${createHash("sha256").update(body).digest("hex").slice(0, 16)}"`;
11447
11249
  cache6 = { body, etag, mtimeMs };
11448
11250
  }
@@ -11482,12 +11284,12 @@ function createFallbackHandler(routes3, htmlHandler2, extra) {
11482
11284
  }
11483
11285
 
11484
11286
  // src/commands/backlog/resolveRepoLocation.ts
11485
- import { existsSync as existsSync34 } from "fs";
11287
+ import { existsSync as existsSync32 } from "fs";
11486
11288
  function resolveRepoLocation(origin, knownCwd, baseDir) {
11487
11289
  if (knownCwd) return { cwd: knownCwd };
11488
11290
  const target = cloneTargetDir(origin, baseDir);
11489
11291
  if (!target) return {};
11490
- if (existsSync34(target) && getCurrentOrigin(target) === origin)
11292
+ if (existsSync32(target) && getCurrentOrigin(target) === origin)
11491
11293
  return { cwd: target };
11492
11294
  return { cloneTarget: target };
11493
11295
  }
@@ -11896,9 +11698,9 @@ function getSessionParam(req) {
11896
11698
  }
11897
11699
 
11898
11700
  // src/commands/sessions/web/execGit.ts
11899
- import { execFile as execFile5 } from "child_process";
11701
+ import { execFile as execFile4 } from "child_process";
11900
11702
  import { promisify as promisify4 } from "util";
11901
- var execFileAsync4 = promisify4(execFile5);
11703
+ var execFileAsync4 = promisify4(execFile4);
11902
11704
  async function execGit(cwd, args, opts = {}) {
11903
11705
  const { maxBuffer, allowFailure } = opts;
11904
11706
  const { file, argv } = /^[A-Za-z]:[\\/]/.test(cwd) ? { file: "git.exe", argv: ["-C", cwd, ...args] } : { file: "git", argv: args };
@@ -12087,17 +11889,17 @@ async function commitPaths(cwd, sha) {
12087
11889
  }
12088
11890
 
12089
11891
  // src/commands/sessions/web/committedPaths.ts
12090
- var cache3 = /* @__PURE__ */ new Map();
11892
+ var cache = /* @__PURE__ */ new Map();
12091
11893
  var inFlight = /* @__PURE__ */ new Map();
12092
11894
  var CACHE_TTL_MS = 1e4;
12093
11895
  async function committedPaths(cwd, sessionId) {
12094
11896
  const key = `${cwd} ${sessionId}`;
12095
- const cached = cache3.get(key);
11897
+ const cached = cache.get(key);
12096
11898
  if (cached && cached.expiresAt > Date.now()) return cached.value;
12097
11899
  const pending = inFlight.get(key);
12098
11900
  if (pending) return pending;
12099
11901
  const lookup = loadCommittedPaths(cwd, sessionId).then((value) => {
12100
- cache3.set(key, { value, expiresAt: Date.now() + CACHE_TTL_MS });
11902
+ cache.set(key, { value, expiresAt: Date.now() + CACHE_TTL_MS });
12101
11903
  return value;
12102
11904
  }).finally(() => inFlight.delete(key));
12103
11905
  inFlight.set(key, lookup);
@@ -13549,26 +13351,26 @@ function handleServerRuns(req, res) {
13549
13351
  }
13550
13352
 
13551
13353
  // src/commands/sessions/web/getReviewSynthesis.ts
13552
- import { execFile as execFile6 } from "child_process";
13553
- import { readFileSync as readFileSync22 } from "fs";
13554
- import { homedir as homedir15 } from "os";
13555
- import { basename as basename11, join as join39 } from "path";
13354
+ import { execFile as execFile5 } from "child_process";
13355
+ import { readFileSync as readFileSync21 } from "fs";
13356
+ import { homedir as homedir14 } from "os";
13357
+ import { basename as basename11, join as join38 } from "path";
13556
13358
  import { promisify as promisify5 } from "util";
13557
13359
 
13558
13360
  // src/commands/sessions/web/findSynthesisForBranch.ts
13559
- import { existsSync as existsSync35, readdirSync as readdirSync5, statSync as statSync6 } from "fs";
13560
- import { basename as basename10, dirname as dirname22, join as join38 } from "path";
13361
+ import { existsSync as existsSync33, readdirSync as readdirSync5, statSync as statSync6 } from "fs";
13362
+ import { basename as basename10, dirname as dirname22, join as join37 } from "path";
13561
13363
  function findSynthesisForBranch(repoReviewsDir, branch2) {
13562
- const branchKeyPath = join38(repoReviewsDir, `${branch2}-`);
13364
+ const branchKeyPath = join37(repoReviewsDir, `${branch2}-`);
13563
13365
  const parent = dirname22(branchKeyPath);
13564
13366
  const branchPrefix = basename10(branchKeyPath);
13565
- if (!existsSync35(parent)) return null;
13566
- const synthesisFiles = readdirSync5(parent).filter((name) => name.startsWith(branchPrefix)).map((name) => join38(parent, name, "synthesis.md")).filter((path106) => existsSync35(path106)).map((path106) => ({ path: path106, mtime: statSync6(path106).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
13367
+ if (!existsSync33(parent)) return null;
13368
+ const synthesisFiles = readdirSync5(parent).filter((name) => name.startsWith(branchPrefix)).map((name) => join37(parent, name, "synthesis.md")).filter((path106) => existsSync33(path106)).map((path106) => ({ path: path106, mtime: statSync6(path106).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
13567
13369
  return synthesisFiles[0]?.path ?? null;
13568
13370
  }
13569
13371
 
13570
13372
  // src/commands/sessions/web/getReviewSynthesis.ts
13571
- var execFileAsync5 = promisify5(execFile6);
13373
+ var execFileAsync5 = promisify5(execFile5);
13572
13374
  function runGit2(cwd, args) {
13573
13375
  return execFileAsync5("git", args, {
13574
13376
  encoding: "utf8",
@@ -13581,8 +13383,8 @@ async function resolveSynthesisPath(cwd) {
13581
13383
  runGit2(cwd, ["rev-parse", "--show-toplevel"]),
13582
13384
  runGit2(cwd, ["rev-parse", "--abbrev-ref", "HEAD"])
13583
13385
  ]);
13584
- const repoReviewsDir = join39(
13585
- homedir15(),
13386
+ const repoReviewsDir = join38(
13387
+ homedir14(),
13586
13388
  ".assist",
13587
13389
  "reviews",
13588
13390
  basename11(repoRoot2)
@@ -13598,7 +13400,7 @@ async function getReviewSynthesis(req, res) {
13598
13400
  respondJson(res, 404, { error: "No synthesis found" });
13599
13401
  return;
13600
13402
  }
13601
- respondJson(res, 200, { synthesis: readFileSync22(path106, "utf8") });
13403
+ respondJson(res, 200, { synthesis: readFileSync21(path106, "utf8") });
13602
13404
  } catch {
13603
13405
  respondJson(res, 404, { error: "No synthesis found" });
13604
13406
  }
@@ -13621,7 +13423,7 @@ var EMPTY = {
13621
13423
  defaultBranch: null,
13622
13424
  onDefaultBranch: false
13623
13425
  };
13624
- var cache4 = /* @__PURE__ */ new Map();
13426
+ var cache2 = /* @__PURE__ */ new Map();
13625
13427
  async function currentBranch2(cwd) {
13626
13428
  try {
13627
13429
  const branch2 = (await execGit(cwd, ["rev-parse", "--abbrev-ref", "HEAD"])).trim();
@@ -13644,7 +13446,7 @@ async function resolve11(cwd) {
13644
13446
  }
13645
13447
  async function gitBranchInfo(cwd) {
13646
13448
  const now = Date.now();
13647
- const cached = cache4.get(cwd);
13449
+ const cached = cache2.get(cwd);
13648
13450
  if (cached && cached.expires > now) return cached.value;
13649
13451
  let value = EMPTY;
13650
13452
  try {
@@ -13652,7 +13454,7 @@ async function gitBranchInfo(cwd) {
13652
13454
  } catch {
13653
13455
  value = EMPTY;
13654
13456
  }
13655
- cache4.set(cwd, { value, expires: now + CACHE_TTL_MS2 });
13457
+ cache2.set(cwd, { value, expires: now + CACHE_TTL_MS2 });
13656
13458
  return value;
13657
13459
  }
13658
13460
 
@@ -13808,10 +13610,10 @@ function protocolMismatched(msg) {
13808
13610
  return typeof msg.protocol === "number" && msg.protocol !== PROTOCOL_VERSION;
13809
13611
  }
13810
13612
 
13811
- // src/commands/sessions/nodes/queryNodes.ts
13613
+ // src/commands/sessions/daemon/requestDaemonReply.ts
13812
13614
  import { createInterface as createInterface5 } from "readline";
13813
13615
  var QUERY_TIMEOUT_MS = 3e3;
13814
- async function queryNodes() {
13616
+ async function requestDaemonReply(request, replyType) {
13815
13617
  let socket;
13816
13618
  try {
13817
13619
  socket = await connectToDaemon();
@@ -13831,16 +13633,21 @@ async function queryNodes() {
13831
13633
  lines2.on("line", (line) => {
13832
13634
  try {
13833
13635
  const data = JSON.parse(line);
13834
- if (data.type === "nodes") finish2(data);
13636
+ if (data.type === replyType) finish2(data);
13835
13637
  } catch {
13836
13638
  }
13837
13639
  });
13838
13640
  socket.on("error", () => finish2(void 0));
13839
- socket.write(`${JSON.stringify({ type: "nodes" })}
13641
+ socket.write(`${JSON.stringify(request)}
13840
13642
  `);
13841
13643
  });
13842
13644
  }
13843
13645
 
13646
+ // src/commands/sessions/nodes/queryNodes.ts
13647
+ function queryNodes() {
13648
+ return requestDaemonReply({ type: "nodes" }, "nodes");
13649
+ }
13650
+
13844
13651
  // src/commands/sessions/web/health.ts
13845
13652
  async function health(_req, res) {
13846
13653
  const [reachable, nodes] = await Promise.all([
@@ -13871,6 +13678,35 @@ function hotkeyBindings(_req, res) {
13871
13678
  respondJson(res, 200, resolveHotkeys(loadConfig().sessions?.hotkeys));
13872
13679
  }
13873
13680
 
13681
+ // src/shared/loadJson.ts
13682
+ import { existsSync as existsSync34, mkdirSync as mkdirSync11, readFileSync as readFileSync22, writeFileSync as writeFileSync21 } from "fs";
13683
+ import { homedir as homedir15 } from "os";
13684
+ import { join as join39 } from "path";
13685
+ function getStoreDir() {
13686
+ return process.env.ASSIST_STORE_DIR || join39(homedir15(), ".assist");
13687
+ }
13688
+ function getStorePath(filename) {
13689
+ return join39(getStoreDir(), filename);
13690
+ }
13691
+ function loadJson(filename) {
13692
+ const path106 = getStorePath(filename);
13693
+ if (existsSync34(path106)) {
13694
+ try {
13695
+ return JSON.parse(readFileSync22(path106, "utf8"));
13696
+ } catch {
13697
+ return {};
13698
+ }
13699
+ }
13700
+ return {};
13701
+ }
13702
+ function saveJson(filename, data) {
13703
+ const dir = getStoreDir();
13704
+ if (!existsSync34(dir)) {
13705
+ mkdirSync11(dir, { recursive: true });
13706
+ }
13707
+ writeFileSync21(getStorePath(filename), JSON.stringify(data, null, 2));
13708
+ }
13709
+
13874
13710
  // src/commands/sessions/web/jiraSite.ts
13875
13711
  function jiraSite(_req, res) {
13876
13712
  const config = loadJson("jira.json");
@@ -14086,11 +13922,11 @@ function newsNavConfig(_req, res) {
14086
13922
  }
14087
13923
 
14088
13924
  // src/commands/sessions/web/releases/ghJson.ts
14089
- import { execFile as execFile7 } from "child_process";
13925
+ import { execFile as execFile6 } from "child_process";
14090
13926
  import { promisify as promisify6 } from "util";
14091
- var execFileAsync6 = promisify6(execFile7);
13927
+ var execFileAsync6 = promisify6(execFile6);
14092
13928
  var CACHE_TTL_MS4 = 3e4;
14093
- var cache5 = /* @__PURE__ */ new Map();
13929
+ var cache3 = /* @__PURE__ */ new Map();
14094
13930
  function partialGraphqlData(error) {
14095
13931
  const stdout = error.stdout;
14096
13932
  if (typeof stdout !== "string") return null;
@@ -14118,12 +13954,12 @@ async function runGh(cwd, args) {
14118
13954
  }
14119
13955
  function ghJson(cwd, args) {
14120
13956
  const key = [cwd, ...args].join("\0");
14121
- const hit = cache5.get(key);
13957
+ const hit = cache3.get(key);
14122
13958
  if (hit && Date.now() - hit.at < CACHE_TTL_MS4)
14123
13959
  return hit.result;
14124
13960
  const result = runGh(cwd, args);
14125
- cache5.set(key, { at: Date.now(), result });
14126
- result.catch(() => cache5.delete(key));
13961
+ cache3.set(key, { at: Date.now(), result });
13962
+ result.catch(() => cache3.delete(key));
14127
13963
  return result;
14128
13964
  }
14129
13965
 
@@ -14649,7 +14485,7 @@ function assertProjectScope() {
14649
14485
  }
14650
14486
 
14651
14487
  // src/commands/github/issue/assignIssueToSelf.ts
14652
- import { execFileSync as execFileSync3 } from "child_process";
14488
+ import { execFileSync as execFileSync2 } from "child_process";
14653
14489
  function assignIssueToSelf(number, repo) {
14654
14490
  const args = [
14655
14491
  "issue",
@@ -14661,7 +14497,7 @@ function assignIssueToSelf(number, repo) {
14661
14497
  "@me"
14662
14498
  ];
14663
14499
  try {
14664
- execFileSync3("gh", args, {
14500
+ execFileSync2("gh", args, {
14665
14501
  stdio: ["ignore", "ignore", "pipe"],
14666
14502
  encoding: "utf8"
14667
14503
  });
@@ -14831,7 +14667,7 @@ async function nextPickup(req, res) {
14831
14667
  import path27 from "path";
14832
14668
 
14833
14669
  // src/commands/rules/readScopedRules.ts
14834
- import { existsSync as existsSync37, readFileSync as readFileSync23 } from "fs";
14670
+ import { existsSync as existsSync36, readFileSync as readFileSync23 } from "fs";
14835
14671
  import path26 from "path";
14836
14672
 
14837
14673
  // src/commands/rules/rulesSectionRange.ts
@@ -14867,24 +14703,24 @@ function parseRulesSection(content) {
14867
14703
  const lines2 = content.split(/\r?\n/);
14868
14704
  const range = rulesSectionRange(lines2);
14869
14705
  if (!range) return [];
14870
- const rules3 = [];
14706
+ const rules2 = [];
14871
14707
  for (const line of lines2.slice(range.start + 1, range.end)) {
14872
14708
  const match = RULE_BULLET.exec(line);
14873
14709
  if (!match) continue;
14874
14710
  const code = match[1].trim();
14875
14711
  const rest = match[2].trim();
14876
14712
  if (code === "" || rest === "") continue;
14877
- rules3.push({ code, ...splitTitle(rest) });
14713
+ rules2.push({ code, ...splitTitle(rest) });
14878
14714
  }
14879
- return rules3;
14715
+ return rules2;
14880
14716
  }
14881
14717
 
14882
14718
  // src/commands/rules/scopeDirectory.ts
14883
- import { existsSync as existsSync36, statSync as statSync7 } from "fs";
14719
+ import { existsSync as existsSync35, statSync as statSync7 } from "fs";
14884
14720
  import path25 from "path";
14885
14721
  function scopeDirectory(target) {
14886
14722
  const resolved = path25.resolve(target);
14887
- return existsSync36(resolved) && statSync7(resolved).isDirectory() ? resolved : path25.dirname(resolved);
14723
+ return existsSync35(resolved) && statSync7(resolved).isDirectory() ? resolved : path25.dirname(resolved);
14888
14724
  }
14889
14725
 
14890
14726
  // src/commands/rules/readScopedRules.ts
@@ -14895,7 +14731,7 @@ function scopedClaudeFiles(target) {
14895
14731
  let current = startDir;
14896
14732
  while (true) {
14897
14733
  const candidate = path26.join(current, "CLAUDE.md");
14898
- if (existsSync37(candidate)) files.push(candidate);
14734
+ if (existsSync36(candidate)) files.push(candidate);
14899
14735
  if (current === root2 || current === path26.dirname(current)) break;
14900
14736
  current = path26.dirname(current);
14901
14737
  }
@@ -15206,9 +15042,9 @@ async function openInCode(req, res) {
15206
15042
  }
15207
15043
 
15208
15044
  // src/commands/sessions/web/createCachedGhJson.ts
15209
- import { execFile as execFile8 } from "child_process";
15045
+ import { execFile as execFile7 } from "child_process";
15210
15046
  import { promisify as promisify8 } from "util";
15211
- var execFileAsync7 = promisify8(execFile8);
15047
+ var execFileAsync7 = promisify8(execFile7);
15212
15048
  var CACHE_TTL_MS5 = 3e4;
15213
15049
  function createCachedGhJson(args, parse5, fallback, options2 = {}) {
15214
15050
  const { cacheFallback = true } = options2;
@@ -15306,7 +15142,7 @@ function peerTimeoutFor(pathname) {
15306
15142
  }
15307
15143
 
15308
15144
  // src/commands/sessions/web/isQuietPoll.ts
15309
- var POLLED_PATHS = /* @__PURE__ */ new Set(["/api/git-status"]);
15145
+ var POLLED_PATHS = /* @__PURE__ */ new Set(["/api/git-status", "/api/updates"]);
15310
15146
  function isQuietPoll(pathname, status2) {
15311
15147
  return POLLED_PATHS.has(pathname) && status2 >= 200 && status2 < 300;
15312
15148
  }
@@ -15459,9 +15295,9 @@ async function previewImage(req, res) {
15459
15295
  }
15460
15296
 
15461
15297
  // src/commands/sessions/web/prStatus.ts
15462
- import { execFile as execFile9 } from "child_process";
15298
+ import { execFile as execFile8 } from "child_process";
15463
15299
  import { promisify as promisify9 } from "util";
15464
- var execFileAsync8 = promisify9(execFile9);
15300
+ var execFileAsync8 = promisify9(execFile8);
15465
15301
  function toPrSummary(parsed) {
15466
15302
  if (typeof parsed.number !== "number") return null;
15467
15303
  return {
@@ -16070,19 +15906,19 @@ async function revertDiffFile(req, res) {
16070
15906
 
16071
15907
  // src/commands/run/runCommandToCompletion.ts
16072
15908
  import { spawn as spawn4 } from "child_process";
16073
- import { existsSync as existsSync39 } from "fs";
15909
+ import { existsSync as existsSync38 } from "fs";
16074
15910
 
16075
15911
  // src/commands/run/resolveCommand.ts
16076
- import { execFileSync as execFileSync4 } from "child_process";
16077
- import { existsSync as existsSync38 } from "fs";
15912
+ import { execFileSync as execFileSync3 } from "child_process";
15913
+ import { existsSync as existsSync37 } from "fs";
16078
15914
  import { dirname as dirname23, join as join40, resolve as resolve13 } from "path";
16079
15915
  function resolveCommand(command) {
16080
15916
  if (process.platform !== "win32" || command !== "bash") return command;
16081
15917
  try {
16082
- const gitPath = execFileSync4("where", ["git"], { encoding: "utf8" }).trim().split("\r\n")[0];
15918
+ const gitPath = execFileSync3("where", ["git"], { encoding: "utf8" }).trim().split("\r\n")[0];
16083
15919
  const gitRoot = resolve13(dirname23(gitPath), "..");
16084
15920
  const gitBash = join40(gitRoot, "bin", "bash.exe");
16085
- if (existsSync38(gitBash)) return gitBash;
15921
+ if (existsSync37(gitBash)) return gitBash;
16086
15922
  } catch {
16087
15923
  return command;
16088
15924
  }
@@ -16092,7 +15928,7 @@ function resolveCommand(command) {
16092
15928
  // src/commands/run/runCommandToCompletion.ts
16093
15929
  function runCommandToCompletion(command, args, env, cwd, quiet) {
16094
15930
  return new Promise((resolveResult) => {
16095
- if (cwd && !existsSync39(cwd)) {
15931
+ if (cwd && !existsSync38(cwd)) {
16096
15932
  resolveResult({
16097
15933
  kind: "failed",
16098
15934
  message: `Failed to execute command: cwd ${cwd} does not exist`
@@ -17164,6 +17000,194 @@ function unsetConfig(req, res) {
17164
17000
  });
17165
17001
  }
17166
17002
 
17003
+ // src/commands/watch/resolveUpstream.ts
17004
+ import { execFileSync as execFileSync4 } from "child_process";
17005
+ function runGit3(args, cwd) {
17006
+ return execFileSync4("git", args, {
17007
+ encoding: "utf8",
17008
+ stdio: ["pipe", "pipe", "pipe"],
17009
+ cwd
17010
+ }).trim();
17011
+ }
17012
+ function resolveUpstream(cwd) {
17013
+ try {
17014
+ runGit3(["rev-parse", "--is-inside-work-tree"], cwd);
17015
+ } catch {
17016
+ throw new Error(
17017
+ "not a git repository \u2014 run assist watch wait from inside a repo"
17018
+ );
17019
+ }
17020
+ let branch2;
17021
+ try {
17022
+ branch2 = runGit3(["symbolic-ref", "--quiet", "--short", "HEAD"], cwd);
17023
+ } catch {
17024
+ throw new Error(
17025
+ "HEAD is detached \u2014 check out a branch before waiting on its upstream"
17026
+ );
17027
+ }
17028
+ try {
17029
+ return {
17030
+ branch: branch2,
17031
+ upstream: runGit3(
17032
+ ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"],
17033
+ cwd
17034
+ )
17035
+ };
17036
+ } catch {
17037
+ throw new Error(
17038
+ `branch "${branch2}" has no upstream \u2014 set one with: git push -u origin ${branch2}`
17039
+ );
17040
+ }
17041
+ }
17042
+
17043
+ // src/commands/watch/changedPaths.ts
17044
+ function changedPaths(from, cwd) {
17045
+ return runGit3(["diff", "--name-only", `${from}..HEAD`], cwd).split("\n").filter((line) => line.length > 0);
17046
+ }
17047
+
17048
+ // src/commands/watch/readBuiltVersion.ts
17049
+ import { join as join41 } from "path";
17050
+ function readBuiltVersion(cwd) {
17051
+ try {
17052
+ const root2 = runGit3(["rev-parse", "--show-toplevel"], cwd);
17053
+ return readPackageJson(join41(root2, "package.json")).version ?? "unknown";
17054
+ } catch {
17055
+ return "unknown";
17056
+ }
17057
+ }
17058
+
17059
+ // src/commands/watch/restartRules.ts
17060
+ var webUiPrefix = "src/commands/sessions/web/ui/";
17061
+ var sessionsPrefix = "src/commands/sessions/";
17062
+ var restartRules = [
17063
+ {
17064
+ target: "webserver",
17065
+ matches: (path106) => path106.startsWith(webUiPrefix),
17066
+ advice: "restart the web server, then hard-reload the browser tab"
17067
+ },
17068
+ {
17069
+ target: "daemon",
17070
+ matches: (path106) => path106.startsWith(sessionsPrefix) && !path106.startsWith(webUiPrefix),
17071
+ advice: "restart the daemon"
17072
+ }
17073
+ ];
17074
+
17075
+ // src/commands/watch/restartTargets.ts
17076
+ function restartTargets(paths) {
17077
+ return restartRules.filter((rule) => paths.some(rule.matches)).map((rule) => rule.target);
17078
+ }
17079
+
17080
+ // src/commands/sessions/web/updates/pendingRestarts.ts
17081
+ function targetsSince(commit2, target, changedSince2) {
17082
+ return commit2 !== void 0 && restartTargets(changedSince2(commit2)).includes(target);
17083
+ }
17084
+ function pendingRestarts(input) {
17085
+ const { daemonVersion, built, changedSince: changedSince2 } = input;
17086
+ const daemon = daemonVersion !== void 0 && (built !== "unknown" && daemonVersion !== built || targetsSince(input.daemonStartCommit, "daemon", changedSince2));
17087
+ const web3 = targetsSince(input.webStartCommit, "webserver", changedSince2);
17088
+ return [
17089
+ ...daemon ? ["daemon"] : [],
17090
+ ...web3 ? ["webserver"] : []
17091
+ ];
17092
+ }
17093
+
17094
+ // src/shared/stripAnsi.ts
17095
+ var ANSI = new RegExp(
17096
+ `${String.fromCharCode(27)}\\[[0-9;?]*[ -/]*[@-~]`,
17097
+ "g"
17098
+ );
17099
+ function stripAnsi(text17) {
17100
+ return text17.replace(ANSI, "");
17101
+ }
17102
+
17103
+ // src/commands/sessions/daemon/worktree/watcherLogPath.ts
17104
+ import { createHash as createHash2 } from "crypto";
17105
+ import { homedir as homedir16 } from "os";
17106
+ import { basename as basename12, join as join42 } from "path";
17107
+
17108
+ // src/commands/sessions/daemon/worktree/canonicalTreePath.ts
17109
+ import { realpathSync as realpathSync2 } from "fs";
17110
+ import { resolve as resolve14 } from "path";
17111
+ function canonicalTreePath(path106) {
17112
+ try {
17113
+ return realpathSync2(path106);
17114
+ } catch {
17115
+ return resolve14(path106);
17116
+ }
17117
+ }
17118
+
17119
+ // src/commands/sessions/daemon/worktree/watcherLogPath.ts
17120
+ function watcherLogPath(cwd) {
17121
+ const clone = canonicalTreePath(cwd);
17122
+ const key = createHash2("sha1").update(clone).digest("hex").slice(0, 8);
17123
+ const dir = process.env.ASSIST_WATCHER_LOG_DIR ?? join42(homedir16(), ".assist", "watchers");
17124
+ return join42(dir, `${basename12(clone) || "root"}-${key}.log`);
17125
+ }
17126
+
17127
+ // src/commands/sessions/web/updates/readUpdateHistory.ts
17128
+ var HISTORY_LINES = 200;
17129
+ function readUpdateHistory(installDir) {
17130
+ return readLogTail(watcherLogPath(installDir), HISTORY_LINES * 2).flatMap((line) => line.split("\r")).map((line) => stripAnsi(line).trimEnd()).filter((line) => line.trim().length > 0).slice(-HISTORY_LINES);
17131
+ }
17132
+
17133
+ // src/commands/watch/headCommit.ts
17134
+ function headCommit3(cwd) {
17135
+ try {
17136
+ return runGit3(["rev-parse", "HEAD"], cwd);
17137
+ } catch {
17138
+ return void 0;
17139
+ }
17140
+ }
17141
+
17142
+ // src/commands/sessions/web/updates/webStartCommit.ts
17143
+ var captured;
17144
+ var webStartCommit = {
17145
+ capture: () => {
17146
+ captured = headCommit3(getInstallDir());
17147
+ },
17148
+ value: () => captured
17149
+ };
17150
+
17151
+ // src/commands/sessions/web/updates/updates.ts
17152
+ function changedSince(installDir) {
17153
+ return (commit2) => {
17154
+ try {
17155
+ return changedPaths(commit2, installDir);
17156
+ } catch {
17157
+ return [];
17158
+ }
17159
+ };
17160
+ }
17161
+ async function updates(_req, res) {
17162
+ const installDir = getInstallDir();
17163
+ const reply2 = await requestDaemonReply(
17164
+ { type: "auto-update" },
17165
+ "auto-update"
17166
+ );
17167
+ const built = readBuiltVersion(installDir);
17168
+ const status2 = {
17169
+ nodeName: resolveNodeName(),
17170
+ installDir,
17171
+ running: reply2?.version ?? ASSIST_VERSION,
17172
+ built,
17173
+ restart: pendingRestarts({
17174
+ daemonVersion: reply2?.version,
17175
+ daemonStartCommit: reply2?.startCommit,
17176
+ webStartCommit: webStartCommit.value(),
17177
+ built,
17178
+ changedSince: changedSince(installDir)
17179
+ }),
17180
+ daemonReachable: reply2 !== void 0,
17181
+ loop: reply2?.loop ?? {
17182
+ phase: "off",
17183
+ since: (/* @__PURE__ */ new Date()).toISOString(),
17184
+ reason: "daemon not reachable"
17185
+ },
17186
+ history: readUpdateHistory(installDir)
17187
+ };
17188
+ respondJson(res, 200, status2);
17189
+ }
17190
+
17167
17191
  // src/commands/sessions/web/readRequestBuffer.ts
17168
17192
  async function readRequestBuffer(req, limit) {
17169
17193
  const chunks = [];
@@ -17185,7 +17209,7 @@ async function readRequestBuffer(req, limit) {
17185
17209
 
17186
17210
  // src/commands/sessions/web/stageAttachment.ts
17187
17211
  import { mkdir as mkdir4, mkdtemp, writeFile as writeFile2 } from "fs/promises";
17188
- import { extname as extname2, join as join41 } from "path";
17212
+ import { extname as extname2, join as join43 } from "path";
17189
17213
  function extensionFromMime(contentType) {
17190
17214
  const mime = contentType.split(";")[0].trim().toLowerCase();
17191
17215
  const known = attachmentMimeTypes[mime];
@@ -17213,9 +17237,9 @@ function safeBaseName(name) {
17213
17237
  async function stageAttachment(name, contentType, body) {
17214
17238
  const ext = pickExtension(name, contentType);
17215
17239
  await mkdir4(stagedAttachmentsDir, { recursive: true });
17216
- const dir = await mkdtemp(join41(stagedAttachmentsDir, "upload-"));
17240
+ const dir = await mkdtemp(join43(stagedAttachmentsDir, "upload-"));
17217
17241
  const alt = safeBaseName(name);
17218
- const filePath = join41(dir, `${alt}.${ext}`);
17242
+ const filePath = join43(dir, `${alt}.${ext}`);
17219
17243
  await writeFile2(filePath, body);
17220
17244
  return { dir, filePath, alt };
17221
17245
  }
@@ -17263,17 +17287,17 @@ async function uploadPrImage(req, res) {
17263
17287
  import { readFile as readFile3, stat as stat4, writeFile as writeFile3 } from "fs/promises";
17264
17288
 
17265
17289
  // src/commands/sessions/web/formatWithOxfmt.ts
17266
- import { execFile as execFile10 } from "child_process";
17267
- import { existsSync as existsSync40 } from "fs";
17268
- import { dirname as dirname24, join as join42 } from "path";
17290
+ import { execFile as execFile9 } from "child_process";
17291
+ import { existsSync as existsSync39 } from "fs";
17292
+ import { dirname as dirname24, join as join44 } from "path";
17269
17293
  import { promisify as promisify10 } from "util";
17270
- var execFileAsync9 = promisify10(execFile10);
17294
+ var execFileAsync9 = promisify10(execFile9);
17271
17295
  var TIMEOUT_MS = 15e3;
17272
17296
  function findOxfmtScript(root2) {
17273
17297
  let dir = root2;
17274
17298
  for (; ; ) {
17275
- const candidate = join42(dir, "node_modules", "oxfmt", "bin", "oxfmt");
17276
- if (existsSync40(candidate)) return candidate;
17299
+ const candidate = join44(dir, "node_modules", "oxfmt", "bin", "oxfmt");
17300
+ if (existsSync39(candidate)) return candidate;
17277
17301
  const parent = dirname24(dir);
17278
17302
  if (parent === dir) return void 0;
17279
17303
  dir = parent;
@@ -17343,7 +17367,7 @@ async function writeFileContent(req, res) {
17343
17367
  }
17344
17368
 
17345
17369
  // src/commands/sessions/web/createCssHandler.ts
17346
- import { createHash as createHash2 } from "crypto";
17370
+ import { createHash as createHash3 } from "crypto";
17347
17371
  import { readFileSync as readFileSync24 } from "fs";
17348
17372
  import { createRequire } from "module";
17349
17373
  var require2 = createRequire(import.meta.url);
@@ -17353,7 +17377,7 @@ function createCssHandler(packageEntry) {
17353
17377
  if (!cache6) {
17354
17378
  const resolved = require2.resolve(packageEntry);
17355
17379
  const body = readFileSync24(resolved, "utf8");
17356
- const etag = `"${createHash2("sha256").update(body).digest("hex").slice(0, 16)}"`;
17380
+ const etag = `"${createHash3("sha256").update(body).digest("hex").slice(0, 16)}"`;
17357
17381
  cache6 = { body, etag };
17358
17382
  }
17359
17383
  const headers = { ETag: cache6.etag, "Cache-Control": "no-cache" };
@@ -17436,7 +17460,8 @@ var routes2 = {
17436
17460
  "GET /api/config": getConfig,
17437
17461
  "POST /api/config/set": setConfig,
17438
17462
  "POST /api/config/unset": unsetConfig,
17439
- "GET /api/review/synthesis": getReviewSynthesis
17463
+ "GET /api/review/synthesis": getReviewSynthesis,
17464
+ "GET /api/updates": updates
17440
17465
  };
17441
17466
  var handleRequest = createFallbackHandler(
17442
17467
  routes2,
@@ -17705,11 +17730,183 @@ function scheduleReconnect() {
17705
17730
  setTimeout(() => void connect2(), RECONNECT_MS).unref();
17706
17731
  }
17707
17732
 
17708
- // src/commands/sessions/web.ts
17733
+ // src/commands/sessions/daemon/originForCwd.ts
17734
+ var cache4 = /* @__PURE__ */ new Map();
17735
+ function originResolutionForCwd(cwd) {
17736
+ if (!cwd) return void 0;
17737
+ const cached = cache4.get(cwd);
17738
+ if (cached !== void 0) return { origin: cached, stable: true };
17739
+ const resolved = resolveCurrentOrigin(cwd);
17740
+ if (resolved.stable) cache4.set(cwd, resolved.origin);
17741
+ return resolved;
17742
+ }
17743
+ function originForCwd(cwd) {
17744
+ return originResolutionForCwd(cwd)?.origin;
17745
+ }
17746
+
17747
+ // src/commands/sessions/daemon/repoDirExists.ts
17748
+ import { existsSync as existsSync40 } from "fs";
17749
+ function repoDirExists(cwd) {
17750
+ return existsSync40(cwd);
17751
+ }
17752
+
17753
+ // src/commands/sessions/daemon/worktree/git.ts
17754
+ import { execFile as execFile10, execFileSync as execFileSync5 } from "child_process";
17755
+ function isWindowsPath(cwd) {
17756
+ return /^[A-Za-z]:[\\/]/.test(cwd);
17757
+ }
17758
+ function gitInvocation(cwd, args) {
17759
+ const windows = isWindowsPath(cwd);
17760
+ return {
17761
+ file: windows ? "git.exe" : "git",
17762
+ argv: windows ? ["-C", cwd, ...args] : args,
17763
+ options: {
17764
+ encoding: "utf8",
17765
+ windowsHide: true,
17766
+ ...windows ? {} : { cwd }
17767
+ }
17768
+ };
17769
+ }
17770
+ function git(cwd, args) {
17771
+ const { file, argv, options: options2 } = gitInvocation(cwd, args);
17772
+ return new Promise((resolve28, reject) => {
17773
+ execFile10(file, argv, options2, (error, stdout) => {
17774
+ if (error) reject(error);
17775
+ else resolve28(stdout.toString());
17776
+ });
17777
+ });
17778
+ }
17779
+ async function gitOrNull(cwd, args) {
17780
+ try {
17781
+ return (await git(cwd, args)).trim() || null;
17782
+ } catch {
17783
+ return null;
17784
+ }
17785
+ }
17786
+ async function gitResult(cwd, args) {
17787
+ try {
17788
+ return { ok: true, out: (await git(cwd, args)).trim() };
17789
+ } catch (error) {
17790
+ return {
17791
+ ok: false,
17792
+ error: error instanceof Error ? error.message : String(error)
17793
+ };
17794
+ }
17795
+ }
17796
+ function gitSyncResult(cwd, args) {
17797
+ try {
17798
+ return { ok: true, out: gitSync(cwd, args).trim() };
17799
+ } catch (error) {
17800
+ return {
17801
+ ok: false,
17802
+ error: error instanceof Error ? error.message : String(error)
17803
+ };
17804
+ }
17805
+ }
17806
+ function gitSync(cwd, args) {
17807
+ const { file, argv, options: options2 } = gitInvocation(cwd, args);
17808
+ return execFileSync5(file, argv, {
17809
+ ...options2,
17810
+ stdio: ["pipe", "pipe", "pipe"]
17811
+ }).toString();
17812
+ }
17813
+ function gitSyncOrNull(cwd, args) {
17814
+ try {
17815
+ return gitSync(cwd, args).trim() || null;
17816
+ } catch {
17817
+ return null;
17818
+ }
17819
+ }
17820
+
17821
+ // src/commands/sessions/daemon/worktree/listWorktreePaths.ts
17822
+ function listWorktreePaths(cwd) {
17823
+ const out = gitSyncOrNull(cwd, ["worktree", "list", "--porcelain"]);
17824
+ if (!out) return [];
17825
+ return out.split("\n").filter((line) => line.startsWith("worktree ")).map((line) => line.slice("worktree ".length).trim());
17826
+ }
17827
+ function mainWorktree(cwd) {
17828
+ return listWorktreePaths(cwd)[0] ?? null;
17829
+ }
17830
+ function listLocalBranches(cwd) {
17831
+ const out = gitSyncOrNull(cwd, [
17832
+ "for-each-ref",
17833
+ "--format=%(refname:short)",
17834
+ "refs/heads"
17835
+ ]);
17836
+ return out ? out.split("\n").map((line) => line.trim()) : [];
17837
+ }
17838
+ function gitCommonDir(cwd) {
17839
+ return gitSyncOrNull(cwd, [
17840
+ "rev-parse",
17841
+ "--path-format=absolute",
17842
+ "--git-common-dir"
17843
+ ]);
17844
+ }
17845
+
17846
+ // src/commands/sessions/daemon/worktree/readWorktreeRegistry.ts
17847
+ var REGISTRY_FILE = "worktrees.json";
17848
+ function readWorktreeRegistry() {
17849
+ const data = loadJson(REGISTRY_FILE);
17850
+ return Object.entries(data).filter(([, record]) => !record.reaped).map(([path106, record]) => ({ path: path106, ...record }));
17851
+ }
17852
+ function worktreeAttributionIncludingReaped(path106) {
17853
+ const record = loadJson(REGISTRY_FILE)[path106];
17854
+ return record ? { clone: record.clone, origin: record.origin } : void 0;
17855
+ }
17856
+ function recordWorktree(path106, clone, origin) {
17857
+ const data = loadJson(REGISTRY_FILE);
17858
+ data[path106] = { clone, origin };
17859
+ saveJson(REGISTRY_FILE, data);
17860
+ }
17861
+ function forgetWorktree(path106) {
17862
+ const data = loadJson(REGISTRY_FILE);
17863
+ const record = data[path106];
17864
+ if (!record || record.reaped) return;
17865
+ data[path106] = { ...record, reaped: true };
17866
+ saveJson(REGISTRY_FILE, data);
17867
+ }
17868
+
17869
+ // src/commands/sessions/daemon/repoGroupForCwd.ts
17870
+ var cache5 = /* @__PURE__ */ new Map();
17871
+ function repoGroupForCwd(cwd) {
17872
+ if (!cwd) return void 0;
17873
+ if (cache5.has(cwd)) return cache5.get(cwd);
17874
+ const { group, stable } = resolveRepoGroup(cwd);
17875
+ if (stable) cache5.set(cwd, group);
17876
+ return group;
17877
+ }
17878
+ function resolveRepoGroup(cwd) {
17879
+ const clone = mainWorktree(cwd);
17880
+ const origin = clone ? originResolutionForCwd(clone) : void 0;
17881
+ if (clone && origin)
17882
+ return {
17883
+ group: hostedGroup(origin.origin, clone),
17884
+ stable: origin.stable
17885
+ };
17886
+ const reaped = worktreeAttributionIncludingReaped(cwd);
17887
+ if (!reaped) return { group: void 0, stable: repoDirExists(cwd) };
17888
+ const current = currentOriginOfClone(reaped.clone);
17889
+ return {
17890
+ group: hostedGroup(current?.origin ?? reaped.origin, reaped.clone),
17891
+ stable: current?.stable === true
17892
+ };
17893
+ }
17894
+ function currentOriginOfClone(clone) {
17895
+ const main = mainWorktree(clone);
17896
+ return main ? originResolutionForCwd(main) : void 0;
17897
+ }
17898
+ function hostedGroup(origin, clone) {
17899
+ return { origin, clone };
17900
+ }
17901
+
17902
+ // src/commands/sessions/repoEntryCwd.ts
17709
17903
  function repoEntryCwd(serverCwd) {
17710
17904
  return repoGroupForCwd(serverCwd)?.clone ?? serverCwd;
17711
17905
  }
17906
+
17907
+ // src/commands/sessions/web.ts
17712
17908
  async function web(options2) {
17909
+ webStartCommit.capture();
17713
17910
  const port = Number.parseInt(options2.port, 10);
17714
17911
  const server = startWebServer(
17715
17912
  "Assist",
@@ -18115,7 +18312,7 @@ function registerBranch(program2) {
18115
18312
  }
18116
18313
 
18117
18314
  // src/commands/cliHook.ts
18118
- import { basename as basename12 } from "path";
18315
+ import { basename as basename13 } from "path";
18119
18316
 
18120
18317
  // src/shared/splitCompound.ts
18121
18318
  import { parse } from "shell-quote";
@@ -18691,7 +18888,7 @@ function findTruncatedReadDenyRaw(rawCommand) {
18691
18888
  }
18692
18889
 
18693
18890
  // src/shared/isApprovedRead.ts
18694
- import { resolve as resolve15, sep as sep2 } from "path";
18891
+ import { resolve as resolve16, sep as sep2 } from "path";
18695
18892
 
18696
18893
  // src/shared/appliesFixStructure.ts
18697
18894
  var APPLY_RE = /^assist\s+github\s+issue\s+fix-structure\b.*\s--apply\b/;
@@ -18745,7 +18942,7 @@ function extractGraphqlQuery(args) {
18745
18942
 
18746
18943
  // src/shared/loadCliReads.ts
18747
18944
  import { existsSync as existsSync41, readFileSync as readFileSync26, writeFileSync as writeFileSync22 } from "fs";
18748
- import { dirname as dirname25, resolve as resolve14 } from "path";
18945
+ import { dirname as dirname25, resolve as resolve15 } from "path";
18749
18946
  import { fileURLToPath as fileURLToPath5 } from "url";
18750
18947
  var __filename3 = fileURLToPath5(import.meta.url);
18751
18948
  var __dirname4 = dirname25(__filename3);
@@ -18760,13 +18957,13 @@ var cachedReads;
18760
18957
  var cachedWrites;
18761
18958
  function getCliReadsLines() {
18762
18959
  if (!cachedReads) {
18763
- cachedReads = readLines(resolve14(packageRoot(), "allowed.cli-reads"));
18960
+ cachedReads = readLines(resolve15(packageRoot(), "allowed.cli-reads"));
18764
18961
  }
18765
18962
  return cachedReads;
18766
18963
  }
18767
18964
  function getCliWritesLines() {
18768
18965
  if (!cachedWrites) {
18769
- cachedWrites = readLines(resolve14(packageRoot(), "allowed.cli-writes"));
18966
+ cachedWrites = readLines(resolve15(packageRoot(), "allowed.cli-writes"));
18770
18967
  }
18771
18968
  return cachedWrites;
18772
18969
  }
@@ -18775,7 +18972,7 @@ function loadCliReads() {
18775
18972
  }
18776
18973
  function saveCliReads(commands) {
18777
18974
  writeFileSync22(
18778
- resolve14(packageRoot(), "allowed.cli-reads"),
18975
+ resolve15(packageRoot(), "allowed.cli-reads"),
18779
18976
  `${commands.join("\n")}
18780
18977
  `
18781
18978
  );
@@ -18801,13 +18998,13 @@ function findCliWrite(command) {
18801
18998
 
18802
18999
  // src/shared/readSettingsPerms.ts
18803
19000
  import { existsSync as existsSync42, readFileSync as readFileSync27 } from "fs";
18804
- import { homedir as homedir16 } from "os";
18805
- import { join as join43 } from "path";
19001
+ import { homedir as homedir17 } from "os";
19002
+ import { join as join45 } from "path";
18806
19003
  function readSettingsPerms(key) {
18807
19004
  const paths = [
18808
- join43(homedir16(), ".claude", "settings.json"),
18809
- join43(process.cwd(), ".claude", "settings.json"),
18810
- join43(process.cwd(), ".claude", "settings.local.json")
19005
+ join45(homedir17(), ".claude", "settings.json"),
19006
+ join45(process.cwd(), ".claude", "settings.json"),
19007
+ join45(process.cwd(), ".claude", "settings.local.json")
18811
19008
  ];
18812
19009
  const entries = [];
18813
19010
  for (const p of paths) {
@@ -18913,19 +19110,19 @@ function isCdToCwd(command) {
18913
19110
  const parts = command.split(/\s+/);
18914
19111
  if (parts[0] !== "cd" || parts.length > 2) return false;
18915
19112
  if (parts.length === 1) return false;
18916
- const resolved = resolve15(normalizeMsysPath(parts[1]));
18917
- return resolved === resolve15(process.cwd());
19113
+ const resolved = resolve16(normalizeMsysPath(parts[1]));
19114
+ return resolved === resolve16(process.cwd());
18918
19115
  }
18919
19116
  function isCdToReadAllowedDir(command) {
18920
19117
  const parts = command.split(/\s+/);
18921
19118
  if (parts[0] !== "cd" || parts.length !== 2) return void 0;
18922
- const target = resolve15(normalizeMsysPath(parts[1]));
19119
+ const target = resolve16(normalizeMsysPath(parts[1]));
18923
19120
  for (const entry of readSettingsPerms("allow")) {
18924
19121
  const m = entry.match(READ_RE);
18925
19122
  if (!m) continue;
18926
19123
  const base = globBaseDir(m[1]);
18927
19124
  if (!base) continue;
18928
- const resolved = resolve15(normalizeMsysPath(base));
19125
+ const resolved = resolve16(normalizeMsysPath(base));
18929
19126
  if (target === resolved || target.startsWith(resolved + sep2)) {
18930
19127
  return `cd to Read-allowed directory: ${entry}`;
18931
19128
  }
@@ -19030,12 +19227,12 @@ function decideCommand(toolName, rawCommand) {
19030
19227
 
19031
19228
  // src/commands/cliHook/logDeniedToolCall.ts
19032
19229
  import { mkdirSync as mkdirSync12 } from "fs";
19033
- import { homedir as homedir17 } from "os";
19034
- import { join as join44 } from "path";
19230
+ import { homedir as homedir18 } from "os";
19231
+ import { join as join46 } from "path";
19035
19232
  import Database from "better-sqlite3";
19036
19233
  var _db;
19037
19234
  function getDbDir() {
19038
- return join44(homedir17(), ".assist");
19235
+ return join46(homedir18(), ".assist");
19039
19236
  }
19040
19237
  function initSchema(db) {
19041
19238
  db.exec(`
@@ -19054,7 +19251,7 @@ function openPromptsDb(dir) {
19054
19251
  if (_db) return _db;
19055
19252
  const dbDir = dir ?? getDbDir();
19056
19253
  mkdirSync12(dbDir, { recursive: true });
19057
- const db = new Database(join44(dbDir, "assist.db"));
19254
+ const db = new Database(join46(dbDir, "assist.db"));
19058
19255
  db.pragma("journal_mode = WAL");
19059
19256
  initSchema(db);
19060
19257
  _db = db;
@@ -19125,7 +19322,7 @@ async function cliHook() {
19125
19322
  logDeniedToolCall({
19126
19323
  tool: input.toolName,
19127
19324
  command: input.command,
19128
- repo: basename12(process.cwd()),
19325
+ repo: basename13(process.cwd()),
19129
19326
  sessionId: process.env.CLAUDE_SESSION_ID,
19130
19327
  denyReason: decision.permissionDecisionReason
19131
19328
  });
@@ -19174,8 +19371,8 @@ ${reasons.join("\n")}`);
19174
19371
 
19175
19372
  // src/commands/permitCliReads.ts
19176
19373
  import { existsSync as existsSync43, mkdirSync as mkdirSync13, readFileSync as readFileSync28, writeFileSync as writeFileSync23 } from "fs";
19177
- import { homedir as homedir18 } from "os";
19178
- import { join as join45 } from "path";
19374
+ import { homedir as homedir19 } from "os";
19375
+ import { join as join47 } from "path";
19179
19376
 
19180
19377
  // src/commands/permitCliReads/assertCliExists.ts
19181
19378
  function assertCliExists(cli) {
@@ -19438,7 +19635,7 @@ function updateSettings(cli, commands) {
19438
19635
  // src/commands/permitCliReads.ts
19439
19636
  function logPath(cli) {
19440
19637
  const safeName = cli.replace(/\s+/g, "-");
19441
- return join45(homedir18(), ".assist", `cli-discover-${safeName}.log`);
19638
+ return join47(homedir19(), ".assist", `cli-discover-${safeName}.log`);
19442
19639
  }
19443
19640
  function readCache(cli) {
19444
19641
  const path106 = logPath(cli);
@@ -19446,7 +19643,7 @@ function readCache(cli) {
19446
19643
  return readFileSync28(path106, "utf8");
19447
19644
  }
19448
19645
  function writeCache(cli, output) {
19449
- const dir = join45(homedir18(), ".assist");
19646
+ const dir = join47(homedir19(), ".assist");
19450
19647
  mkdirSync13(dir, { recursive: true });
19451
19648
  writeFileSync23(logPath(cli), output);
19452
19649
  }
@@ -19610,18 +19807,18 @@ function isHashCommentFile(filePath) {
19610
19807
  }
19611
19808
 
19612
19809
  // src/commands/codeComment/getRestrictedDir.ts
19613
- import { homedir as homedir19 } from "os";
19614
- import { join as join46 } from "path";
19810
+ import { homedir as homedir20 } from "os";
19811
+ import { join as join48 } from "path";
19615
19812
  function getRestrictedDir() {
19616
- return join46(homedir19(), ".assist", "restricted");
19813
+ return join48(homedir20(), ".assist", "restricted");
19617
19814
  }
19618
19815
  function getPinStatePath(pin2) {
19619
- return join46(getRestrictedDir(), `code-comment-${pin2}.json`);
19816
+ return join48(getRestrictedDir(), `code-comment-${pin2}.json`);
19620
19817
  }
19621
19818
 
19622
19819
  // src/commands/codeComment/sweepRestrictedDir.ts
19623
19820
  import { readdirSync as readdirSync6, statSync as statSync8, unlinkSync as unlinkSync7 } from "fs";
19624
- import { join as join47 } from "path";
19821
+ import { join as join49 } from "path";
19625
19822
  var STALE_AFTER_MS = 30 * 60 * 1e3;
19626
19823
  function sweepRestrictedDir(dir = getRestrictedDir()) {
19627
19824
  let entries;
@@ -19632,7 +19829,7 @@ function sweepRestrictedDir(dir = getRestrictedDir()) {
19632
19829
  }
19633
19830
  const cutoff = Date.now() - STALE_AFTER_MS;
19634
19831
  for (const entry of entries) {
19635
- const path106 = join47(dir, entry);
19832
+ const path106 = join49(dir, entry);
19636
19833
  try {
19637
19834
  if (statSync8(path106).mtimeMs < cutoff) unlinkSync7(path106);
19638
19835
  } catch {
@@ -21286,18 +21483,18 @@ import chalk137 from "chalk";
21286
21483
 
21287
21484
  // src/commands/criteriaExtension/criteriaExtensionDir.ts
21288
21485
  import { existsSync as existsSync46 } from "fs";
21289
- import { dirname as dirname26, join as join48 } from "path";
21486
+ import { dirname as dirname26, join as join50 } from "path";
21290
21487
  import { fileURLToPath as fileURLToPath6 } from "url";
21291
21488
  var moduleDir = dirname26(fileURLToPath6(import.meta.url));
21292
21489
  function criteriaExtensionDir() {
21293
- const bundled = join48(
21490
+ const bundled = join50(
21294
21491
  moduleDir,
21295
21492
  "commands",
21296
21493
  "criteriaExtension",
21297
21494
  "criteria-extension"
21298
21495
  );
21299
21496
  if (existsSync46(bundled)) return bundled;
21300
- return join48(moduleDir, "..", "..", "..", "criteria-extension");
21497
+ return join50(moduleDir, "..", "..", "..", "criteria-extension");
21301
21498
  }
21302
21499
 
21303
21500
  // src/commands/criteriaExtension/criteriaExtension.ts
@@ -21325,24 +21522,24 @@ async function criteriaExtension() {
21325
21522
 
21326
21523
  // src/commands/criteriaExtension/printCriteriaExtensionUrl.ts
21327
21524
  import { readFile as readFile4 } from "fs/promises";
21328
- import { join as join49 } from "path";
21525
+ import { join as join51 } from "path";
21329
21526
  import chalk138 from "chalk";
21330
21527
 
21331
21528
  // src/commands/criteriaExtension/latestUpdate.ts
21332
- function latestUpdate(manifest, updates) {
21529
+ function latestUpdate(manifest, updates2) {
21333
21530
  const { id } = JSON.parse(manifest).browser_specific_settings.gecko;
21334
- return JSON.parse(updates).addons[id]?.updates.at(-1);
21531
+ return JSON.parse(updates2).addons[id]?.updates.at(-1);
21335
21532
  }
21336
21533
 
21337
21534
  // src/commands/criteriaExtension/latestUpdateLink.ts
21338
- function latestUpdateLink(manifest, updates) {
21339
- return latestUpdate(manifest, updates)?.update_link;
21535
+ function latestUpdateLink(manifest, updates2) {
21536
+ return latestUpdate(manifest, updates2)?.update_link;
21340
21537
  }
21341
21538
 
21342
21539
  // src/commands/criteriaExtension/printCriteriaExtensionUrl.ts
21343
21540
  async function printCriteriaExtensionUrl() {
21344
21541
  const manifest = await readFile4(
21345
- join49(criteriaExtensionDir(), "manifest.json"),
21542
+ join51(criteriaExtensionDir(), "manifest.json"),
21346
21543
  "utf8"
21347
21544
  );
21348
21545
  const { update_url } = JSON.parse(manifest).browser_specific_settings.gecko;
@@ -21358,16 +21555,16 @@ async function printCriteriaExtensionUrl() {
21358
21555
 
21359
21556
  // src/commands/criteriaExtension/signCriteriaExtension.ts
21360
21557
  import { rm as rm2 } from "fs/promises";
21361
- import { homedir as homedir20, tmpdir as tmpdir3 } from "os";
21362
- import { join as join55 } from "path";
21558
+ import { homedir as homedir21, tmpdir as tmpdir3 } from "os";
21559
+ import { join as join57 } from "path";
21363
21560
  import chalk140 from "chalk";
21364
21561
 
21365
21562
  // src/commands/criteriaExtension/copySignedXpi.ts
21366
21563
  import { copyFile } from "fs/promises";
21367
- import { join as join50 } from "path";
21564
+ import { join as join52 } from "path";
21368
21565
  var SIGNED_XPI_NAME = "criteria-extension.xpi";
21369
21566
  async function copySignedXpi(xpi, dir) {
21370
- const fixed2 = join50(dir, SIGNED_XPI_NAME);
21567
+ const fixed2 = join52(dir, SIGNED_XPI_NAME);
21371
21568
  if (fixed2 === xpi) return fixed2;
21372
21569
  await copyFile(xpi, fixed2);
21373
21570
  return fixed2;
@@ -21376,11 +21573,11 @@ async function copySignedXpi(xpi, dir) {
21376
21573
  // src/commands/criteriaExtension/findSignedXpi.ts
21377
21574
  import { existsSync as existsSync47 } from "fs";
21378
21575
  import { readdir } from "fs/promises";
21379
- import { join as join51 } from "path";
21576
+ import { join as join53 } from "path";
21380
21577
  async function findSignedXpi(dir) {
21381
21578
  if (!existsSync47(dir)) return null;
21382
21579
  const name = (await readdir(dir)).find((entry) => entry.endsWith(".xpi"));
21383
- return name ? join51(dir, name) : null;
21580
+ return name ? join53(dir, name) : null;
21384
21581
  }
21385
21582
 
21386
21583
  // src/commands/criteriaExtension/signedAddonInstallPath.ts
@@ -21403,10 +21600,10 @@ async function signedAddonInstallPath(xpi) {
21403
21600
 
21404
21601
  // src/commands/criteriaExtension/signPreflightProblem.ts
21405
21602
  import { existsSync as existsSync48 } from "fs";
21406
- import { join as join52 } from "path";
21603
+ import { join as join54 } from "path";
21407
21604
  var KEY_URL = "https://addons.mozilla.org/en-US/developers/addon/api/key/";
21408
21605
  function signPreflightProblem(source) {
21409
- if (!existsSync48(join52(source, "content.js")))
21606
+ if (!existsSync48(join54(source, "content.js")))
21410
21607
  return {
21411
21608
  message: `no content.js in ${source}`,
21412
21609
  hint: "run npm run build to bundle the content script"
@@ -21421,7 +21618,7 @@ function signPreflightProblem(source) {
21421
21618
 
21422
21619
  // src/commands/criteriaExtension/stageCriteriaExtension.ts
21423
21620
  import { cp as cp3, mkdir as mkdir5, readFile as readFile5, rm, writeFile as writeFile4 } from "fs/promises";
21424
- import { basename as basename13, join as join53 } from "path";
21621
+ import { basename as basename14, join as join55 } from "path";
21425
21622
 
21426
21623
  // src/commands/criteriaExtension/stampManifestVersion.ts
21427
21624
  function stampManifestVersion(manifest, version2) {
@@ -21436,9 +21633,9 @@ async function stageCriteriaExtension(source, staging, version2) {
21436
21633
  await mkdir5(staging, { recursive: true });
21437
21634
  await cp3(source, staging, {
21438
21635
  recursive: true,
21439
- filter: (path106) => basename13(path106) !== "updates.json"
21636
+ filter: (path106) => basename14(path106) !== "updates.json"
21440
21637
  });
21441
- const manifest = join53(staging, "manifest.json");
21638
+ const manifest = join55(staging, "manifest.json");
21442
21639
  const stamped = stampManifestVersion(
21443
21640
  await readFile5(manifest, "utf8"),
21444
21641
  version2
@@ -21448,13 +21645,13 @@ async function stageCriteriaExtension(source, staging, version2) {
21448
21645
 
21449
21646
  // src/commands/criteriaExtension/writeUpdateManifest.ts
21450
21647
  import { readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
21451
- import { join as join54 } from "path";
21648
+ import { join as join56 } from "path";
21452
21649
 
21453
21650
  // src/commands/criteriaExtension/buildUpdateManifest.ts
21454
21651
  var RELEASE_DOWNLOAD_URL = "https://github.com/staff0rd/assist/releases/download";
21455
21652
  function buildUpdateManifest(manifest, version2) {
21456
21653
  const { id } = JSON.parse(manifest).browser_specific_settings.gecko;
21457
- const updates = {
21654
+ const updates2 = {
21458
21655
  addons: {
21459
21656
  [id]: {
21460
21657
  updates: [
@@ -21466,24 +21663,24 @@ function buildUpdateManifest(manifest, version2) {
21466
21663
  }
21467
21664
  }
21468
21665
  };
21469
- return `${JSON.stringify(updates, null, " ")}
21666
+ return `${JSON.stringify(updates2, null, " ")}
21470
21667
  `;
21471
21668
  }
21472
21669
 
21473
21670
  // src/commands/criteriaExtension/writeUpdateManifest.ts
21474
21671
  async function writeUpdateManifest(source, dir, version2) {
21475
21672
  await writeFile5(
21476
- join54(dir, "updates.json"),
21673
+ join56(dir, "updates.json"),
21477
21674
  buildUpdateManifest(
21478
- await readFile6(join54(source, "manifest.json"), "utf8"),
21675
+ await readFile6(join56(source, "manifest.json"), "utf8"),
21479
21676
  version2
21480
21677
  )
21481
21678
  );
21482
21679
  }
21483
21680
 
21484
21681
  // src/commands/criteriaExtension/signCriteriaExtension.ts
21485
- var ARTIFACTS_DIR = join55(homedir20(), ".assist", "criteria-extension");
21486
- var STAGING_DIR = join55(tmpdir3(), "assist-criteria-extension");
21682
+ var ARTIFACTS_DIR = join57(homedir21(), ".assist", "criteria-extension");
21683
+ var STAGING_DIR = join57(tmpdir3(), "assist-criteria-extension");
21487
21684
  async function signCriteriaExtension() {
21488
21685
  const source = criteriaExtensionDir();
21489
21686
  const problem = signPreflightProblem(source);
@@ -21671,12 +21868,12 @@ import { unlinkSync as unlinkSync9, writeFileSync as writeFileSync26 } from "fs"
21671
21868
  import chalk143 from "chalk";
21672
21869
 
21673
21870
  // src/commands/dbMigration/getMigrationPinPath.ts
21674
- import { join as join56 } from "path";
21871
+ import { join as join58 } from "path";
21675
21872
  function getMigrationPinPath(pin2) {
21676
- return join56(getRestrictedDir(), `db-migration-pin-${pin2}.json`);
21873
+ return join58(getRestrictedDir(), `db-migration-pin-${pin2}.json`);
21677
21874
  }
21678
21875
  function getMigrationApprovalPath(migrationId) {
21679
- return join56(getRestrictedDir(), `db-migration-approval-${migrationId}.json`);
21876
+ return join58(getRestrictedDir(), `db-migration-approval-${migrationId}.json`);
21680
21877
  }
21681
21878
 
21682
21879
  // src/commands/dbMigration/readMigrationPinState.ts
@@ -21809,15 +22006,15 @@ function registerDeploy(program2) {
21809
22006
  }
21810
22007
 
21811
22008
  // src/commands/devlog/list.ts
21812
- import { execFileSync as execFileSync5 } from "child_process";
21813
- import { basename as basename15 } from "path";
22009
+ import { execFileSync as execFileSync6 } from "child_process";
22010
+ import { basename as basename16 } from "path";
21814
22011
 
21815
22012
  // src/commands/devlog/loadBlogSkipDays.ts
21816
- import { homedir as homedir21 } from "os";
21817
- import { join as join57 } from "path";
21818
- var BLOG_REPO_ROOT = join57(homedir21(), "git/blog");
22013
+ import { homedir as homedir22 } from "os";
22014
+ import { join as join59 } from "path";
22015
+ var BLOG_REPO_ROOT = join59(homedir22(), "git/blog");
21819
22016
  function loadBlogSkipDays(repoName) {
21820
- const config = loadRawYaml(join57(BLOG_REPO_ROOT, "assist.yml"));
22017
+ const config = loadRawYaml(join59(BLOG_REPO_ROOT, "assist.yml"));
21821
22018
  const devlog = config.devlog;
21822
22019
  const skip2 = devlog?.skip;
21823
22020
  return new Set(skip2?.[repoName]);
@@ -21829,13 +22026,13 @@ import chalk146 from "chalk";
21829
22026
 
21830
22027
  // src/shared/getRepoName.ts
21831
22028
  import { existsSync as existsSync51, readFileSync as readFileSync33 } from "fs";
21832
- import { basename as basename14, join as join58 } from "path";
22029
+ import { basename as basename15, join as join60 } from "path";
21833
22030
  function getRepoName() {
21834
22031
  const config = loadConfig();
21835
22032
  if (config.devlog?.name) {
21836
22033
  return config.devlog.name;
21837
22034
  }
21838
- const packageJsonPath = join58(process.cwd(), "package.json");
22035
+ const packageJsonPath = join60(process.cwd(), "package.json");
21839
22036
  if (existsSync51(packageJsonPath)) {
21840
22037
  try {
21841
22038
  const content = readFileSync33(packageJsonPath, "utf8");
@@ -21846,13 +22043,13 @@ function getRepoName() {
21846
22043
  } catch {
21847
22044
  }
21848
22045
  }
21849
- return basename14(process.cwd());
22046
+ return basename15(process.cwd());
21850
22047
  }
21851
22048
 
21852
22049
  // src/commands/devlog/loadDevlogEntries.ts
21853
22050
  import { readdirSync as readdirSync7, readFileSync as readFileSync34 } from "fs";
21854
- import { join as join59 } from "path";
21855
- var DEVLOG_DIR = join59(BLOG_REPO_ROOT, "src/content/devlog");
22051
+ import { join as join61 } from "path";
22052
+ var DEVLOG_DIR = join61(BLOG_REPO_ROOT, "src/content/devlog");
21856
22053
  function extractFrontmatter(content) {
21857
22054
  const fm = content.match(/^---\n([\s\S]*?)\n---/);
21858
22055
  return fm?.[1] ?? null;
@@ -21880,7 +22077,7 @@ function readDevlogFiles(callback) {
21880
22077
  try {
21881
22078
  const files = readdirSync7(DEVLOG_DIR).filter((f) => f.endsWith(".md"));
21882
22079
  for (const file of files) {
21883
- const content = readFileSync34(join59(DEVLOG_DIR, file), "utf8");
22080
+ const content = readFileSync34(join61(DEVLOG_DIR, file), "utf8");
21884
22081
  const parsed = parseFrontmatter(content, file);
21885
22082
  if (parsed) callback(parsed);
21886
22083
  }
@@ -21982,14 +22179,14 @@ function list2(options2) {
21982
22179
  const config = loadConfig();
21983
22180
  const days = options2.days ?? 30;
21984
22181
  const ignore3 = options2.ignore ?? config.devlog?.ignore ?? [];
21985
- const repoName = basename15(process.cwd());
22182
+ const repoName = basename16(process.cwd());
21986
22183
  const skipDays = loadBlogSkipDays(repoName);
21987
22184
  const devlogEntries = loadDevlogEntries(repoName);
21988
22185
  const args = ["log"];
21989
22186
  if (options2.reverse) args.push("--reverse");
21990
22187
  else args.push("-n", "500");
21991
22188
  args.push("--pretty=format:%ad|%h|%s", "--date=short");
21992
- const output = execFileSync5("git", args, { encoding: "utf8" });
22189
+ const output = execFileSync6("git", args, { encoding: "utf8" });
21993
22190
  const commitsByDate = parseGitLogCommits(output, ignore3);
21994
22191
  let dateCount = 0;
21995
22192
  let isFirst = true;
@@ -22012,7 +22209,7 @@ function list2(options2) {
22012
22209
  }
22013
22210
 
22014
22211
  // src/commands/devlog/getLastVersionInfo.ts
22015
- import { execFileSync as execFileSync6, execSync as execSync32 } from "child_process";
22212
+ import { execFileSync as execFileSync7, execSync as execSync32 } from "child_process";
22016
22213
  import semver from "semver";
22017
22214
  function getVersionAtCommit(hash) {
22018
22215
  try {
@@ -22031,7 +22228,7 @@ function stripToMinor(version2) {
22031
22228
  }
22032
22229
  function getLastVersionInfoFromGit() {
22033
22230
  try {
22034
- const output = execFileSync6(
22231
+ const output = execFileSync7(
22035
22232
  "git",
22036
22233
  ["log", "-1", "--pretty=format:%ad|%h", "--date=short"],
22037
22234
  {
@@ -22076,7 +22273,7 @@ function bumpVersion(version2, type) {
22076
22273
  }
22077
22274
 
22078
22275
  // src/commands/devlog/next/displayNextEntry.ts
22079
- import { execFileSync as execFileSync7 } from "child_process";
22276
+ import { execFileSync as execFileSync8 } from "child_process";
22080
22277
  import chalk149 from "chalk";
22081
22278
 
22082
22279
  // src/commands/devlog/next/displayNextEntry/displayVersion.ts
@@ -22110,7 +22307,7 @@ function findTargetDate(commitsByDate, skipDays) {
22110
22307
  return Array.from(commitsByDate.keys()).filter((d) => !skipDays.has(d)).sort()[0];
22111
22308
  }
22112
22309
  function fetchCommitsByDate(ignore3, lastDate) {
22113
- const output = execFileSync7(
22310
+ const output = execFileSync8(
22114
22311
  "git",
22115
22312
  ["log", "--pretty=format:%ad|%h|%s", "--date=short", "-n", "500"],
22116
22313
  { encoding: "utf8" }
@@ -22268,11 +22465,11 @@ function repos(options2) {
22268
22465
 
22269
22466
  // src/commands/devlog/skip.ts
22270
22467
  import { writeFileSync as writeFileSync29 } from "fs";
22271
- import { join as join60 } from "path";
22468
+ import { join as join62 } from "path";
22272
22469
  import chalk151 from "chalk";
22273
22470
  import { stringify as stringifyYaml3 } from "yaml";
22274
22471
  function getBlogConfigPath() {
22275
- return join60(BLOG_REPO_ROOT, "assist.yml");
22472
+ return join62(BLOG_REPO_ROOT, "assist.yml");
22276
22473
  }
22277
22474
  function skip(date) {
22278
22475
  if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) {
@@ -22334,7 +22531,7 @@ function registerDevlog(program2) {
22334
22531
 
22335
22532
  // src/commands/dotnet/checkBuildLocks.ts
22336
22533
  import { closeSync as closeSync4, openSync as openSync5, readdirSync as readdirSync8 } from "fs";
22337
- import { join as join61 } from "path";
22534
+ import { join as join63 } from "path";
22338
22535
  import chalk153 from "chalk";
22339
22536
  var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "packages"]);
22340
22537
  function isLockedDll(debugDir) {
@@ -22346,7 +22543,7 @@ function isLockedDll(debugDir) {
22346
22543
  }
22347
22544
  for (const file of files) {
22348
22545
  if (!file.toLowerCase().endsWith(".dll")) continue;
22349
- const dllPath = join61(debugDir, file);
22546
+ const dllPath = join63(debugDir, file);
22350
22547
  try {
22351
22548
  const fd = openSync5(dllPath, "r+");
22352
22549
  closeSync4(fd);
@@ -22364,13 +22561,13 @@ function findFirstLockedDll(dir) {
22364
22561
  return null;
22365
22562
  }
22366
22563
  if (entries.includes("bin")) {
22367
- const locked = isLockedDll(join61(dir, "bin", "Debug"));
22564
+ const locked = isLockedDll(join63(dir, "bin", "Debug"));
22368
22565
  if (locked) return locked;
22369
22566
  }
22370
22567
  for (const entry of entries) {
22371
22568
  if (SKIP_DIRS.has(entry) || entry === "bin" || entry.startsWith("."))
22372
22569
  continue;
22373
- const found = findFirstLockedDll(join61(dir, entry));
22570
+ const found = findFirstLockedDll(join63(dir, entry));
22374
22571
  if (found) return found;
22375
22572
  }
22376
22573
  return null;
@@ -22717,11 +22914,11 @@ import chalk159 from "chalk";
22717
22914
 
22718
22915
  // src/commands/dotnet/findSolution.ts
22719
22916
  import { readdirSync as readdirSync10 } from "fs";
22720
- import { dirname as dirname27, join as join62 } from "path";
22917
+ import { dirname as dirname27, join as join64 } from "path";
22721
22918
  import chalk158 from "chalk";
22722
22919
  function findSlnInDir(dir) {
22723
22920
  try {
22724
- return readdirSync10(dir).filter((f) => f.endsWith(".sln")).map((f) => join62(dir, f));
22921
+ return readdirSync10(dir).filter((f) => f.endsWith(".sln")).map((f) => join64(dir, f));
22725
22922
  } catch {
22726
22923
  return [];
22727
22924
  }
@@ -23716,7 +23913,7 @@ function aggregateCommitters(authorLists) {
23716
23913
  import { spawnSync as spawnSync6 } from "child_process";
23717
23914
  import { unlinkSync as unlinkSync12, writeFileSync as writeFileSync30 } from "fs";
23718
23915
  import { tmpdir as tmpdir5 } from "os";
23719
- import { join as join63 } from "path";
23916
+ import { join as join65 } from "path";
23720
23917
  function buildArgs2(queryFile, vars) {
23721
23918
  const args = ["api", "graphql", "-F", `query=@${queryFile}`];
23722
23919
  for (const [key, value] of Object.entries(vars)) {
@@ -23726,7 +23923,7 @@ function buildArgs2(queryFile, vars) {
23726
23923
  return args;
23727
23924
  }
23728
23925
  function runGhGraphql(mutation, vars) {
23729
- const queryFile = join63(tmpdir5(), `gh-query-${Date.now()}.graphql`);
23926
+ const queryFile = join65(tmpdir5(), `gh-query-${Date.now()}.graphql`);
23730
23927
  writeFileSync30(queryFile, mutation);
23731
23928
  try {
23732
23929
  const result = spawnSync6("gh", buildArgs2(queryFile, vars), {
@@ -24439,7 +24636,7 @@ Anything nested below the leaf level fails the run before a single write, naming
24439
24636
  }
24440
24637
 
24441
24638
  // src/commands/github/issue/commentIssue.ts
24442
- import { execFileSync as execFileSync8 } from "child_process";
24639
+ import { execFileSync as execFileSync9 } from "child_process";
24443
24640
 
24444
24641
  // src/shared/validateProposedContent.ts
24445
24642
  function validateProposedContent(labels, title, body) {
@@ -24500,7 +24697,7 @@ async function commentIssue(numberArg, options2) {
24500
24697
  const args = ["issue", "comment", String(number), "--body", body];
24501
24698
  if (options2.repo) args.push("--repo", options2.repo);
24502
24699
  try {
24503
- execFileSync8("gh", args, { stdio: "inherit" });
24700
+ execFileSync9("gh", args, { stdio: "inherit" });
24504
24701
  } catch {
24505
24702
  process.exit(1);
24506
24703
  }
@@ -24903,8 +25100,8 @@ function attachArgs(attachments) {
24903
25100
  }
24904
25101
 
24905
25102
  // src/commands/prs/runGhWithAttachments.ts
24906
- import { execFileSync as execFileSync9 } from "child_process";
24907
- import { basename as basename16 } from "path";
25103
+ import { execFileSync as execFileSync10 } from "child_process";
25104
+ import { basename as basename17 } from "path";
24908
25105
 
24909
25106
  // src/shared/removeStagedAttachments.ts
24910
25107
  import { rmSync as rmSync4 } from "fs";
@@ -24921,7 +25118,7 @@ function removeStagedAttachments(attachments) {
24921
25118
  var PLACED_URL = /https:\/\/\S+\/(pull|issues)\/\d+/;
24922
25119
  function reportPartialFailure(attachments, stderr) {
24923
25120
  const failed3 = attachments.filter(
24924
- ({ path: path106 }) => stderr.includes(path106) || stderr.includes(basename16(path106))
25121
+ ({ path: path106 }) => stderr.includes(path106) || stderr.includes(basename17(path106))
24925
25122
  );
24926
25123
  const named = failed3.length > 0 ? failed3 : attachments;
24927
25124
  console.error(
@@ -24931,7 +25128,7 @@ function reportPartialFailure(attachments, stderr) {
24931
25128
  function runGhWithAttachments(args, attachments) {
24932
25129
  const attaching = attachments.length > 0;
24933
25130
  try {
24934
- const stdout = execFileSync9("gh", args, {
25131
+ const stdout = execFileSync10("gh", args, {
24935
25132
  encoding: "utf8",
24936
25133
  stdio: ["inherit", "pipe", attaching ? "pipe" : "inherit"]
24937
25134
  });
@@ -25042,7 +25239,7 @@ function inWebSession() {
25042
25239
  }
25043
25240
 
25044
25241
  // src/commands/github/issue/fetchIssue.ts
25045
- import { execFileSync as execFileSync10 } from "child_process";
25242
+ import { execFileSync as execFileSync11 } from "child_process";
25046
25243
  function fetchIssue2(number, repo) {
25047
25244
  const args = [
25048
25245
  "issue",
@@ -25054,7 +25251,7 @@ function fetchIssue2(number, repo) {
25054
25251
  if (repo) args.push("--repo", repo);
25055
25252
  let raw;
25056
25253
  try {
25057
- raw = execFileSync10("gh", args, { encoding: "utf8" });
25254
+ raw = execFileSync11("gh", args, { encoding: "utf8" });
25058
25255
  } catch {
25059
25256
  console.error(`Could not fetch issue #${number} with gh issue view`);
25060
25257
  process.exit(1);
@@ -25071,14 +25268,14 @@ function fetchIssue2(number, repo) {
25071
25268
  import { existsSync as existsSync56, readFileSync as readFileSync38 } from "fs";
25072
25269
 
25073
25270
  // src/commands/github/issue/issueWorkingFile.ts
25074
- import { join as join64 } from "path";
25271
+ import { join as join66 } from "path";
25075
25272
  function issueWorkingFile(slug2, number) {
25076
25273
  const [owner = "unknown", repo = "unknown"] = slug2.split("/");
25077
- const dir = join64(getStoreDir(), "github-issues", owner, repo);
25274
+ const dir = join66(getStoreDir(), "github-issues", owner, repo);
25078
25275
  return {
25079
25276
  dir,
25080
- bodyPath: join64(dir, `${number}.md`),
25081
- metaPath: join64(dir, `${number}.json`)
25277
+ bodyPath: join66(dir, `${number}.md`),
25278
+ metaPath: join66(dir, `${number}.json`)
25082
25279
  };
25083
25280
  }
25084
25281
 
@@ -25145,12 +25342,12 @@ function prepareIssueEdit(number, repo, fresh) {
25145
25342
  }
25146
25343
 
25147
25344
  // src/commands/github/issue/pushIssueBody.ts
25148
- import { execFileSync as execFileSync11 } from "child_process";
25345
+ import { execFileSync as execFileSync12 } from "child_process";
25149
25346
  function pushIssueBody(number, repo, bodyPath) {
25150
25347
  const args = ["issue", "edit", String(number), "--body-file", bodyPath];
25151
25348
  if (repo) args.push("--repo", repo);
25152
25349
  try {
25153
- execFileSync11("gh", args, { stdio: "inherit" });
25350
+ execFileSync12("gh", args, { stdio: "inherit" });
25154
25351
  } catch {
25155
25352
  process.exit(1);
25156
25353
  }
@@ -25210,12 +25407,12 @@ function setIssueParent(number, repo, parentArg) {
25210
25407
  }
25211
25408
 
25212
25409
  // src/commands/github/issue/viewIssue.ts
25213
- import { execFileSync as execFileSync12 } from "child_process";
25410
+ import { execFileSync as execFileSync13 } from "child_process";
25214
25411
  function viewIssue(number, repo) {
25215
25412
  const args = ["issue", "view", String(number)];
25216
25413
  if (repo) args.push("--repo", repo);
25217
25414
  try {
25218
- execFileSync12("gh", args, { stdio: "inherit" });
25415
+ execFileSync13("gh", args, { stdio: "inherit" });
25219
25416
  } catch {
25220
25417
  process.exit(1);
25221
25418
  }
@@ -25271,7 +25468,7 @@ function registerEditIssue(issueCommand) {
25271
25468
  }
25272
25469
 
25273
25470
  // src/commands/github/issue/editIssueComment.ts
25274
- import { execFileSync as execFileSync13 } from "child_process";
25471
+ import { execFileSync as execFileSync14 } from "child_process";
25275
25472
  var USAGE4 = "Usage: assist github issue edit-comment <comment-id> --body <body> [-R <owner>/<repo>]";
25276
25473
  async function editIssueComment(commentIdArg, options2) {
25277
25474
  const commentId = Number.parseInt(commentIdArg, 10);
@@ -25298,7 +25495,7 @@ async function editIssueComment(commentIdArg, options2) {
25298
25495
  ];
25299
25496
  let raw;
25300
25497
  try {
25301
- raw = execFileSync13("gh", args, {
25498
+ raw = execFileSync14("gh", args, {
25302
25499
  input: JSON.stringify({ body }),
25303
25500
  encoding: "utf8"
25304
25501
  });
@@ -25679,7 +25876,7 @@ function registerRefineLaunch(program2, resumeFlag) {
25679
25876
  import { randomUUID as randomUUID12 } from "crypto";
25680
25877
 
25681
25878
  // src/commands/review/checkoutPr.ts
25682
- import { execFileSync as execFileSync15 } from "child_process";
25879
+ import { execFileSync as execFileSync16 } from "child_process";
25683
25880
  import chalk170 from "chalk";
25684
25881
 
25685
25882
  // src/commands/sessions/daemon/daemonLog.ts
@@ -25705,30 +25902,19 @@ function recentDaemonLogLines(includeRelayed = true) {
25705
25902
  return ring.filter((entry) => includeRelayed || !entry.relayed).map((entry) => entry.line);
25706
25903
  }
25707
25904
 
25708
- // src/commands/sessions/daemon/worktree/canonicalTreePath.ts
25709
- import { realpathSync as realpathSync2 } from "fs";
25710
- import { resolve as resolve16 } from "path";
25711
- function canonicalTreePath(path106) {
25712
- try {
25713
- return realpathSync2(path106);
25714
- } catch {
25715
- return resolve16(path106);
25716
- }
25717
- }
25718
-
25719
25905
  // src/commands/sessions/daemon/worktree/createWorktree.ts
25720
25906
  import { existsSync as existsSync57 } from "fs";
25721
- import { basename as basename18, dirname as dirname29 } from "path";
25907
+ import { basename as basename19, dirname as dirname29 } from "path";
25722
25908
 
25723
25909
  // src/commands/sessions/daemon/worktree/planAllocation.ts
25724
- import { basename as basename17, join as join65 } from "path";
25910
+ import { basename as basename18, join as join67 } from "path";
25725
25911
  function planAllocation(clone, boundTreeRoots2) {
25726
25912
  return boundTreeRoots2.has(clone) ? "spill" : "primary";
25727
25913
  }
25728
25914
  function nextWorktreePath(clone, base, isTaken) {
25729
- const name = basename17(clone);
25915
+ const name = basename18(clone);
25730
25916
  for (let n = 2; n < 1e3; n++) {
25731
- const candidate = join65(base, `${name}-${n}`);
25917
+ const candidate = join67(base, `${name}-${n}`);
25732
25918
  if (!isTaken(candidate)) return candidate;
25733
25919
  }
25734
25920
  throw new Error(`no free worktree suffix for ${clone}`);
@@ -25764,7 +25950,7 @@ function createWorktree(clone, strategy, boundTreeRoots2, preferredPath) {
25764
25950
  const base = strategy.root ? expandTilde2(strategy.root) : dirname29(clone);
25765
25951
  const registered = new Set(listWorktreePaths(clone));
25766
25952
  const branches = new Set(listLocalBranches(clone));
25767
- const isTaken = (candidate) => registered.has(candidate) || existsSync57(candidate) || boundTreeRoots2.has(candidate) || branches.has(basename18(candidate));
25953
+ const isTaken = (candidate) => registered.has(candidate) || existsSync57(candidate) || boundTreeRoots2.has(candidate) || branches.has(basename19(candidate));
25768
25954
  const path106 = preferredPath && !isTaken(preferredPath) ? preferredPath : nextWorktreePath(clone, base, isTaken);
25769
25955
  const start2 = worktreeStartPoint(clone, strategy.trunk);
25770
25956
  gitSync(clone, [
@@ -25772,13 +25958,13 @@ function createWorktree(clone, strategy, boundTreeRoots2, preferredPath) {
25772
25958
  "add",
25773
25959
  start2.track ? "--track" : "--no-track",
25774
25960
  "-b",
25775
- basename18(path106),
25961
+ basename19(path106),
25776
25962
  path106,
25777
25963
  start2.ref
25778
25964
  ]);
25779
25965
  recordWorktree(path106, clone, getCurrentOrigin(clone));
25780
25966
  daemonLog(
25781
- start2.track ? `worktree allocated ${path106} (branch ${basename18(path106)} tracking ${start2.ref}) for clone ${clone}` : `worktree allocated ${path106} (branch ${basename18(path106)} off ${start2.ref}, no mainline tracking) for clone ${clone}`
25967
+ start2.track ? `worktree allocated ${path106} (branch ${basename19(path106)} tracking ${start2.ref}) for clone ${clone}` : `worktree allocated ${path106} (branch ${basename19(path106)} off ${start2.ref}, no mainline tracking) for clone ${clone}`
25782
25968
  );
25783
25969
  return path106;
25784
25970
  }
@@ -26080,19 +26266,19 @@ function persistedTreeRoots() {
26080
26266
 
26081
26267
  // src/commands/sessions/daemon/worktree/seedWorktree.ts
26082
26268
  import { copyFileSync, existsSync as existsSync60, mkdirSync as mkdirSync17 } from "fs";
26083
- import { dirname as dirname30, join as join67 } from "path";
26269
+ import { dirname as dirname30, join as join69 } from "path";
26084
26270
 
26085
26271
  // src/commands/sessions/daemon/worktree/runInstall.ts
26086
26272
  import { resolve as resolve17 } from "path";
26087
26273
 
26088
26274
  // src/commands/sessions/daemon/worktree/detectInstallCommand.ts
26089
26275
  import { existsSync as existsSync59 } from "fs";
26090
- import { join as join66 } from "path";
26276
+ import { join as join68 } from "path";
26091
26277
  function detectInstallCommand(dir) {
26092
- if (!existsSync59(join66(dir, "package.json"))) return null;
26093
- if (existsSync59(join66(dir, "pnpm-lock.yaml"))) return "pnpm install";
26094
- if (existsSync59(join66(dir, "yarn.lock"))) return "yarn install";
26095
- if (existsSync59(join66(dir, "bun.lockb"))) return "bun install";
26278
+ if (!existsSync59(join68(dir, "package.json"))) return null;
26279
+ if (existsSync59(join68(dir, "pnpm-lock.yaml"))) return "pnpm install";
26280
+ if (existsSync59(join68(dir, "yarn.lock"))) return "yarn install";
26281
+ if (existsSync59(join68(dir, "bun.lockb"))) return "bun install";
26096
26282
  return "npm install";
26097
26283
  }
26098
26284
 
@@ -26254,9 +26440,9 @@ function seedWorktree(worktreePath, clone, onSeeded = () => {
26254
26440
  }
26255
26441
  function copyConfigFiles(worktreePath, clone, copy) {
26256
26442
  for (const rel of copy) {
26257
- const src = join67(clone, rel);
26443
+ const src = join69(clone, rel);
26258
26444
  if (!existsSync60(src)) continue;
26259
- const dest = join67(worktreePath, rel);
26445
+ const dest = join69(worktreePath, rel);
26260
26446
  try {
26261
26447
  mkdirSync17(dirname30(dest), { recursive: true });
26262
26448
  copyFileSync(src, dest);
@@ -26297,10 +26483,10 @@ async function moveToPrCheckoutTree() {
26297
26483
  }
26298
26484
 
26299
26485
  // src/commands/review/prHeadBranch.ts
26300
- import { execFileSync as execFileSync14 } from "child_process";
26486
+ import { execFileSync as execFileSync15 } from "child_process";
26301
26487
  function prHeadBranch(number) {
26302
26488
  try {
26303
- const out = execFileSync14(
26489
+ const out = execFileSync15(
26304
26490
  "gh",
26305
26491
  ["pr", "view", number, "--json", "headRefName", "-q", ".headRefName"],
26306
26492
  { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }
@@ -26362,7 +26548,7 @@ async function checkoutPr(number) {
26362
26548
  if (headRef && await moveToExistingCheckout(number, headRef)) return;
26363
26549
  await moveToPrCheckoutTree();
26364
26550
  try {
26365
- execFileSync15("gh", ["pr", "checkout", number], { stdio: "inherit" });
26551
+ execFileSync16("gh", ["pr", "checkout", number], { stdio: "inherit" });
26366
26552
  } catch {
26367
26553
  console.error(chalk170.red(`gh pr checkout ${number} failed; aborting.`));
26368
26554
  process.exit(1);
@@ -26566,7 +26752,7 @@ import chalk175 from "chalk";
26566
26752
 
26567
26753
  // src/commands/mermaid/exportFile.ts
26568
26754
  import { readFileSync as readFileSync39, writeFileSync as writeFileSync32 } from "fs";
26569
- import { basename as basename19, extname as extname3, resolve as resolve18 } from "path";
26755
+ import { basename as basename20, extname as extname3, resolve as resolve18 } from "path";
26570
26756
  import chalk174 from "chalk";
26571
26757
 
26572
26758
  // src/commands/mermaid/renderBlock.ts
@@ -26593,7 +26779,7 @@ async function renderBlock(krokiUrl, source) {
26593
26779
  async function exportFile(file, outDir, krokiUrl, onlyIndex) {
26594
26780
  const content = readFileSync39(file, "utf8");
26595
26781
  const blocks = extractMermaidBlocks(content);
26596
- const stem = basename19(file, extname3(file));
26782
+ const stem = basename20(file, extname3(file));
26597
26783
  if (onlyIndex !== void 0) {
26598
26784
  if (onlyIndex < 1 || onlyIndex > blocks.length) {
26599
26785
  console.error(
@@ -27290,15 +27476,15 @@ function createNetcapHandler(options2) {
27290
27476
  // src/commands/netcap/prepareExtensionForLoad.ts
27291
27477
  import { cp as cp4, readFile as readFile7, writeFile as writeFile6 } from "fs/promises";
27292
27478
  import { networkInterfaces } from "os";
27293
- import { join as join69 } from "path";
27479
+ import { join as join71 } from "path";
27294
27480
  import chalk178 from "chalk";
27295
27481
 
27296
27482
  // src/commands/netcap/netcapExtensionDir.ts
27297
- import { dirname as dirname32, join as join68 } from "path";
27483
+ import { dirname as dirname32, join as join70 } from "path";
27298
27484
  import { fileURLToPath as fileURLToPath7 } from "url";
27299
27485
  var moduleDir2 = dirname32(fileURLToPath7(import.meta.url));
27300
27486
  function netcapExtensionDir() {
27301
- return join68(moduleDir2, "commands", "netcap", "netcap-extension");
27487
+ return join70(moduleDir2, "commands", "netcap", "netcap-extension");
27302
27488
  }
27303
27489
 
27304
27490
  // src/commands/netcap/prepareExtensionForLoad.ts
@@ -27313,7 +27499,7 @@ function lanIPv4() {
27313
27499
  return void 0;
27314
27500
  }
27315
27501
  async function configureBackground(dir, host, port, filter) {
27316
- const file = join69(dir, "background.js");
27502
+ const file = join71(dir, "background.js");
27317
27503
  const source = await readFile7(file, "utf8");
27318
27504
  await writeFile6(
27319
27505
  file,
@@ -27353,20 +27539,20 @@ async function prepareExtensionForLoad(port, filter = "") {
27353
27539
  }
27354
27540
 
27355
27541
  // src/commands/netcap/resolveNetcapOutPath.ts
27356
- import { isAbsolute as isAbsolute5, join as join71, resolve as resolve21 } from "path";
27542
+ import { isAbsolute as isAbsolute5, join as join73, resolve as resolve21 } from "path";
27357
27543
 
27358
27544
  // src/commands/netcap/defaultCapturePath.ts
27359
- import { homedir as homedir22 } from "os";
27360
- import { join as join70 } from "path";
27545
+ import { homedir as homedir23 } from "os";
27546
+ import { join as join72 } from "path";
27361
27547
  function defaultCapturePath() {
27362
- return join70(homedir22(), ".assist", "netcap", "capture.jsonl");
27548
+ return join72(homedir23(), ".assist", "netcap", "capture.jsonl");
27363
27549
  }
27364
27550
 
27365
27551
  // src/commands/netcap/resolveNetcapOutPath.ts
27366
27552
  function resolveNetcapOutPath(out) {
27367
27553
  if (!out) return defaultCapturePath();
27368
27554
  const dir = isAbsolute5(out) ? out : resolve21(process.cwd(), out);
27369
- return join71(dir, "capture.jsonl");
27555
+ return join73(dir, "capture.jsonl");
27370
27556
  }
27371
27557
 
27372
27558
  // src/commands/netcap/netcap.ts
@@ -27413,7 +27599,7 @@ netcap stopped \u2014 captured ${count8} ${count8 === 1 ? "entry" : "entries"} t
27413
27599
 
27414
27600
  // src/commands/netcap/netcapExtract.ts
27415
27601
  import { writeFileSync as writeFileSync34 } from "fs";
27416
- import { join as join72 } from "path";
27602
+ import { join as join74 } from "path";
27417
27603
  import chalk180 from "chalk";
27418
27604
 
27419
27605
  // src/commands/netcap/extractPostsFromCapture.ts
@@ -27858,7 +28044,7 @@ function extractPostsFromCapture(captureFile) {
27858
28044
  function netcapExtract(file) {
27859
28045
  const captureFile = file ?? defaultCapturePath();
27860
28046
  const posts = extractPostsFromCapture(captureFile);
27861
- const outFile = join72(captureFile, "..", "posts.json");
28047
+ const outFile = join74(captureFile, "..", "posts.json");
27862
28048
  writeFileSync34(outFile, `${JSON.stringify(posts, null, 2)}
27863
28049
  `);
27864
28050
  console.log(
@@ -28221,18 +28407,18 @@ import { execSync as execSync41 } from "child_process";
28221
28407
  import { execSync as execSync40 } from "child_process";
28222
28408
  import { unlinkSync as unlinkSync14, writeFileSync as writeFileSync35 } from "fs";
28223
28409
  import { tmpdir as tmpdir6 } from "os";
28224
- import { join as join74 } from "path";
28410
+ import { join as join76 } from "path";
28225
28411
 
28226
28412
  // src/commands/prs/loadCommentsCache.ts
28227
28413
  import { existsSync as existsSync62, readFileSync as readFileSync43, unlinkSync as unlinkSync13 } from "fs";
28228
28414
  import { parse as parse3 } from "yaml";
28229
28415
 
28230
28416
  // src/commands/prs/commentsCachePath.ts
28231
- import { homedir as homedir23 } from "os";
28232
- import { join as join73 } from "path";
28417
+ import { homedir as homedir24 } from "os";
28418
+ import { join as join75 } from "path";
28233
28419
  function commentsCachePath(org, repo, prNumber) {
28234
- return join73(
28235
- homedir23(),
28420
+ return join75(
28421
+ homedir24(),
28236
28422
  ".assist",
28237
28423
  "pr-comments",
28238
28424
  org,
@@ -28280,7 +28466,7 @@ function replyToComment(org, repo, prNumber, commentId, message3) {
28280
28466
  // src/commands/prs/resolveCommentWithReply.ts
28281
28467
  function resolveThread(threadId) {
28282
28468
  const mutation = `mutation($threadId: ID!) { resolveReviewThread(input: {threadId: $threadId}) { thread { isResolved } } }`;
28283
- const queryFile = join74(tmpdir6(), `gh-mutation-${Date.now()}.graphql`);
28469
+ const queryFile = join76(tmpdir6(), `gh-mutation-${Date.now()}.graphql`);
28284
28470
  writeFileSync35(queryFile, mutation);
28285
28471
  try {
28286
28472
  execSync40(
@@ -28365,10 +28551,10 @@ function fixed(commentId, sha) {
28365
28551
  import { execSync as execSync42 } from "child_process";
28366
28552
  import { unlinkSync as unlinkSync15, writeFileSync as writeFileSync36 } from "fs";
28367
28553
  import { tmpdir as tmpdir7 } from "os";
28368
- import { join as join75 } from "path";
28554
+ import { join as join77 } from "path";
28369
28555
  var THREAD_QUERY = `query($owner: String!, $repo: String!, $prNumber: Int!) { repository(owner: $owner, name: $repo) { pullRequest(number: $prNumber) { reviewThreads(first: 100) { nodes { id isResolved comments(first: 100) { nodes { databaseId } } } } } } }`;
28370
28556
  function fetchThreadIds(org, repo, prNumber) {
28371
- const queryFile = join75(tmpdir7(), `gh-query-${Date.now()}.graphql`);
28557
+ const queryFile = join77(tmpdir7(), `gh-query-${Date.now()}.graphql`);
28372
28558
  writeFileSync36(queryFile, THREAD_QUERY);
28373
28559
  try {
28374
28560
  const result = execSync42(
@@ -28443,10 +28629,10 @@ import { stringify as stringify3 } from "yaml";
28443
28629
 
28444
28630
  // src/commands/prs/removeStaleCommentsCaches.ts
28445
28631
  import { readdirSync as readdirSync12, unlinkSync as unlinkSync16 } from "fs";
28446
- import { join as join76 } from "path";
28632
+ import { join as join78 } from "path";
28447
28633
  var STALE_PATTERN = /^pr-\d+-comments\.yaml$/;
28448
28634
  function removeStaleCommentsCaches(cwd = process.cwd()) {
28449
- const dir = join76(cwd, ".assist");
28635
+ const dir = join78(cwd, ".assist");
28450
28636
  let entries;
28451
28637
  try {
28452
28638
  entries = readdirSync12(dir);
@@ -28454,7 +28640,7 @@ function removeStaleCommentsCaches(cwd = process.cwd()) {
28454
28640
  return;
28455
28641
  }
28456
28642
  for (const entry of entries.filter((e) => STALE_PATTERN.test(e))) {
28457
- unlinkSync16(join76(dir, entry));
28643
+ unlinkSync16(join78(dir, entry));
28458
28644
  }
28459
28645
  }
28460
28646
 
@@ -29088,7 +29274,7 @@ function buildValidatedBody(options2, usage) {
29088
29274
  }
29089
29275
 
29090
29276
  // src/commands/prs/placePr.ts
29091
- import { execFileSync as execFileSync16 } from "child_process";
29277
+ import { execFileSync as execFileSync17 } from "child_process";
29092
29278
 
29093
29279
  // src/commands/prs/buildCreateArgs.ts
29094
29280
  function buildCreateArgs(title, body, options2, attachments = []) {
@@ -29174,7 +29360,7 @@ function buildEditArgs(number, title, body, attachments = []) {
29174
29360
  // src/commands/prs/placePr.ts
29175
29361
  function hasUpstream2() {
29176
29362
  try {
29177
- execFileSync16(
29363
+ execFileSync17(
29178
29364
  "git",
29179
29365
  ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"],
29180
29366
  { stdio: "pipe" }
@@ -29186,7 +29372,7 @@ function hasUpstream2() {
29186
29372
  }
29187
29373
  function ensureBranchPushed() {
29188
29374
  const args = hasUpstream2() ? ["push"] : ["push", "--set-upstream", "origin", "HEAD"];
29189
- execFileSync16("git", args, { stdio: "inherit" });
29375
+ execFileSync17("git", args, { stdio: "inherit" });
29190
29376
  }
29191
29377
  async function placePr(prNumber, title, body, options2, attachments = []) {
29192
29378
  const args = prNumber !== null ? buildEditArgs(prNumber, title, body, attachments) : buildCreateArgs(title, body, options2, attachments);
@@ -29332,10 +29518,10 @@ function warn(reason4) {
29332
29518
  }
29333
29519
 
29334
29520
  // src/commands/prs/enableAutoMerge.ts
29335
- import { execFileSync as execFileSync17 } from "child_process";
29521
+ import { execFileSync as execFileSync18 } from "child_process";
29336
29522
  function enableAutoMerge() {
29337
29523
  try {
29338
- execFileSync17(
29524
+ execFileSync18(
29339
29525
  "gh",
29340
29526
  ["pr", "merge", getCurrentBranch2(), "--auto", "--squash"],
29341
29527
  { stdio: "inherit" }
@@ -31093,7 +31279,7 @@ async function notify() {
31093
31279
  import { execSync as execSync52 } from "child_process";
31094
31280
  import { existsSync as existsSync63, mkdirSync as mkdirSync21, unlinkSync as unlinkSync17, writeFileSync as writeFileSync38 } from "fs";
31095
31281
  import { tmpdir as tmpdir8 } from "os";
31096
- import { join as join77, resolve as resolve22 } from "path";
31282
+ import { join as join79, resolve as resolve22 } from "path";
31097
31283
  import chalk205 from "chalk";
31098
31284
 
31099
31285
  // src/commands/screenshot/captureWindowPs1.ts
@@ -31230,7 +31416,7 @@ function buildOutputPath(outputDir, processName) {
31230
31416
  return resolve22(outputDir, `${processName}-${timestamp5}.png`);
31231
31417
  }
31232
31418
  function runPowerShellScript(processName, outputPath) {
31233
- const scriptPath = join77(tmpdir8(), `assist-screenshot-${Date.now()}.ps1`);
31419
+ const scriptPath = join79(tmpdir8(), `assist-screenshot-${Date.now()}.ps1`);
31234
31420
  writeFileSync38(scriptPath, captureWindowPs1, "utf8");
31235
31421
  try {
31236
31422
  execSync52(
@@ -31345,9 +31531,9 @@ function buildLimitsSegment(rateLimits) {
31345
31531
 
31346
31532
  // src/commands/readGitBranch.ts
31347
31533
  import { readFileSync as readFileSync46, statSync as statSync10 } from "fs";
31348
- import { isAbsolute as isAbsolute6, join as join78, resolve as resolve23 } from "path";
31534
+ import { isAbsolute as isAbsolute6, join as join80, resolve as resolve23 } from "path";
31349
31535
  function resolveGitDir(cwd) {
31350
- const dotGit = join78(cwd, ".git");
31536
+ const dotGit = join80(cwd, ".git");
31351
31537
  let stat5;
31352
31538
  try {
31353
31539
  stat5 = statSync10(dotGit);
@@ -31377,7 +31563,7 @@ function readGitBranch(cwd) {
31377
31563
  }
31378
31564
  let head;
31379
31565
  try {
31380
- head = readFileSync46(join78(gitDir, "HEAD"), "utf8");
31566
+ head = readFileSync46(join80(gitDir, "HEAD"), "utf8");
31381
31567
  } catch {
31382
31568
  return null;
31383
31569
  }
@@ -33798,7 +33984,7 @@ function exitWith(message3) {
33798
33984
  }
33799
33985
 
33800
33986
  // src/commands/review/highLevel/proposeHighLevelGlobs.ts
33801
- import { execFileSync as execFileSync19 } from "child_process";
33987
+ import { execFileSync as execFileSync20 } from "child_process";
33802
33988
  import chalk228 from "chalk";
33803
33989
 
33804
33990
  // src/commands/review/highLevel/buildGlobProposalPrompt.ts
@@ -33846,7 +34032,7 @@ function readGlobs(value) {
33846
34032
  }
33847
34033
 
33848
34034
  // src/commands/review/highLevel/summariseRepoTree.ts
33849
- import { execFileSync as execFileSync18 } from "child_process";
34035
+ import { execFileSync as execFileSync19 } from "child_process";
33850
34036
  import { dirname as dirname36, extname as extname4 } from "path";
33851
34037
  var MAX_DIRECTORIES = 200;
33852
34038
  var MAX_EXTENSIONS_PER_DIRECTORY = 6;
@@ -33862,7 +34048,7 @@ function summariseRepoTree(cwd = process.cwd()) {
33862
34048
  }
33863
34049
  function listTrackedFiles(cwd) {
33864
34050
  try {
33865
- return execFileSync18("git", ["ls-files"], {
34051
+ return execFileSync19("git", ["ls-files"], {
33866
34052
  cwd,
33867
34053
  encoding: "utf8",
33868
34054
  maxBuffer: 64 * 1024 * 1024
@@ -33907,7 +34093,7 @@ function proposeHighLevelGlobs(cwd = process.cwd()) {
33907
34093
  }
33908
34094
  function askClaude(prompt, cwd) {
33909
34095
  try {
33910
- return execFileSync19("claude", ["-p", "--model", "sonnet", prompt], {
34096
+ return execFileSync20("claude", ["-p", "--model", "sonnet", prompt], {
33911
34097
  cwd,
33912
34098
  encoding: "utf8",
33913
34099
  timeout: 12e4,
@@ -34000,7 +34186,7 @@ function groupFilesByDirectory(files) {
34000
34186
  }
34001
34187
 
34002
34188
  // src/commands/review/highLevel/buildHighLevelTree.ts
34003
- function basename20(path106) {
34189
+ function basename21(path106) {
34004
34190
  return path106.slice(path106.lastIndexOf("/") + 1);
34005
34191
  }
34006
34192
  function sum(nodes, key) {
@@ -34016,7 +34202,7 @@ function nodesOf(at2, prefix2) {
34016
34202
  const files = at2.files.map(
34017
34203
  (file) => ({
34018
34204
  kind: "file",
34019
- name: basename20(file.path),
34205
+ name: basename21(file.path),
34020
34206
  ...file
34021
34207
  })
34022
34208
  );
@@ -34287,7 +34473,7 @@ function evaluateHighLevelChecks(subject) {
34287
34473
 
34288
34474
  // src/commands/review/highLevel/fetchHighLevelFiles.ts
34289
34475
  import { execSync as execSync55 } from "child_process";
34290
- import { createHash as createHash3 } from "crypto";
34476
+ import { createHash as createHash4 } from "crypto";
34291
34477
  var JQ = ".[] | {filename,status,additions,deletions,patch} | @json";
34292
34478
  function toStatus(status2) {
34293
34479
  if (status2 === "added") return "added";
@@ -34295,7 +34481,7 @@ function toStatus(status2) {
34295
34481
  return "modified";
34296
34482
  }
34297
34483
  function diffUrl(repo, prNumber, path106) {
34298
- const anchor = createHash3("sha256").update(path106).digest("hex");
34484
+ const anchor = createHash4("sha256").update(path106).digest("hex");
34299
34485
  return `https://github.com/${repo}/pull/${prNumber}/files#diff-${anchor}`;
34300
34486
  }
34301
34487
  function fetchHighLevelFiles(prNumber, repo) {
@@ -34336,12 +34522,12 @@ function fetchHighLevelPr(prNumber, repo) {
34336
34522
  import { existsSync as existsSync64, readFileSync as readFileSync47 } from "fs";
34337
34523
 
34338
34524
  // src/commands/review/highLevel/highLevelReviewPath.ts
34339
- import { join as join79 } from "path";
34525
+ import { join as join81 } from "path";
34340
34526
  function slug(value) {
34341
34527
  return value.replace(/[^\w.-]+/g, "-");
34342
34528
  }
34343
34529
  function highLevelReviewPath(repo, headRef, headSha) {
34344
- return join79(
34530
+ return join81(
34345
34531
  getStoreDir(),
34346
34532
  "high-level-reviews",
34347
34533
  slug(repo),
@@ -35417,18 +35603,7 @@ import { format } from "util";
35417
35603
 
35418
35604
  // src/commands/review/createReviewLogSink.ts
35419
35605
  import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync23 } from "fs";
35420
- import { join as join80 } from "path";
35421
-
35422
- // src/shared/stripAnsi.ts
35423
- var ANSI = new RegExp(
35424
- `${String.fromCharCode(27)}\\[[0-9;?]*[ -/]*[@-~]`,
35425
- "g"
35426
- );
35427
- function stripAnsi(text17) {
35428
- return text17.replace(ANSI, "");
35429
- }
35430
-
35431
- // src/commands/review/createReviewLogSink.ts
35606
+ import { join as join82 } from "path";
35432
35607
  var LOG_FILE = "review.log";
35433
35608
  function createReviewLogSink() {
35434
35609
  let logPath2;
@@ -35445,7 +35620,7 @@ function createReviewLogSink() {
35445
35620
  },
35446
35621
  attach(reviewDir) {
35447
35622
  mkdirSync23(reviewDir, { recursive: true });
35448
- logPath2 = join80(reviewDir, LOG_FILE);
35623
+ logPath2 = join82(reviewDir, LOG_FILE);
35449
35624
  const lines2 = [
35450
35625
  "",
35451
35626
  `=== ${(/* @__PURE__ */ new Date()).toISOString()} ===`,
@@ -36526,22 +36701,22 @@ ${annotateDiffWithLineNumbers(context.diff.trimEnd())}
36526
36701
  }
36527
36702
 
36528
36703
  // src/commands/review/buildReviewPaths.ts
36529
- import { homedir as homedir24 } from "os";
36530
- import { basename as basename21, join as join81 } from "path";
36704
+ import { homedir as homedir25 } from "os";
36705
+ import { basename as basename22, join as join83 } from "path";
36531
36706
  function buildReviewPaths(repoRoot2, key) {
36532
- const reviewDir = join81(
36533
- homedir24(),
36707
+ const reviewDir = join83(
36708
+ homedir25(),
36534
36709
  ".assist",
36535
36710
  "reviews",
36536
- basename21(repoRoot2),
36711
+ basename22(repoRoot2),
36537
36712
  key
36538
36713
  );
36539
36714
  return {
36540
36715
  reviewDir,
36541
- requestPath: join81(reviewDir, "request.md"),
36542
- claudePath: join81(reviewDir, "claude.md"),
36543
- codexPath: join81(reviewDir, "codex.md"),
36544
- synthesisPath: join81(reviewDir, "synthesis.md")
36716
+ requestPath: join83(reviewDir, "request.md"),
36717
+ claudePath: join83(reviewDir, "claude.md"),
36718
+ codexPath: join83(reviewDir, "codex.md"),
36719
+ synthesisPath: join83(reviewDir, "synthesis.md")
36545
36720
  };
36546
36721
  }
36547
36722
 
@@ -36995,16 +37170,16 @@ import path87 from "path";
36995
37170
  import chalk239 from "chalk";
36996
37171
  function listRules(target, options2 = {}) {
36997
37172
  const resolved = path87.resolve(target ?? process.cwd());
36998
- const rules3 = readScopedRules(resolved);
36999
- if (rules3.length === 0) {
37173
+ const rules2 = readScopedRules(resolved);
37174
+ if (rules2.length === 0) {
37000
37175
  const label2 = path87.relative(process.cwd(), resolved) || ".";
37001
37176
  console.log(chalk239.gray(`No rules in scope for ${label2}`));
37002
37177
  return;
37003
37178
  }
37004
37179
  const base = findRepoRoot(scopeDirectory(resolved));
37005
- const width = Math.max(...rules3.map((rule) => rule.code.length));
37180
+ const width = Math.max(...rules2.map((rule) => rule.code.length));
37006
37181
  let shown;
37007
- for (const rule of rules3) {
37182
+ for (const rule of rules2) {
37008
37183
  if (rule.source !== shown) {
37009
37184
  shown = rule.source;
37010
37185
  console.log(
@@ -37522,12 +37697,12 @@ async function reviewProposedSlackMessage(target, body, workingPath) {
37522
37697
  }
37523
37698
 
37524
37699
  // src/commands/slack/slackWorkingFile.ts
37525
- import { join as join82 } from "path";
37700
+ import { join as join84 } from "path";
37526
37701
  function slackWorkingFile(channel, partIndex) {
37527
37702
  const slug2 = channel.replace(/^[#@]/, "").toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "channel";
37528
- const dir = join82(getStoreDir(), "slack");
37703
+ const dir = join84(getStoreDir(), "slack");
37529
37704
  const name = partIndex === void 0 ? slug2 : `${slug2}-${partIndex}`;
37530
- return { dir, bodyPath: join82(dir, `${name}.md`) };
37705
+ return { dir, bodyPath: join84(dir, `${name}.md`) };
37531
37706
  }
37532
37707
 
37533
37708
  // src/commands/slack/previewSlackThreadParts.ts
@@ -38707,20 +38882,20 @@ async function configure() {
38707
38882
 
38708
38883
  // src/commands/transcript/list.ts
38709
38884
  import { existsSync as existsSync76, readdirSync as readdirSync20, statSync as statSync12 } from "fs";
38710
- import { join as join94 } from "path";
38885
+ import { join as join96 } from "path";
38711
38886
  function list3() {
38712
38887
  const { vttDir } = getTranscriptConfig();
38713
38888
  if (!existsSync76(vttDir)) return;
38714
38889
  for (const entry of readdirSync20(vttDir)) {
38715
38890
  if (!entry.endsWith(".vtt")) continue;
38716
- if (statSync12(join94(vttDir, entry)).isDirectory()) continue;
38891
+ if (statSync12(join96(vttDir, entry)).isDirectory()) continue;
38717
38892
  console.log(entry);
38718
38893
  }
38719
38894
  }
38720
38895
 
38721
38896
  // src/commands/transcript/move.ts
38722
38897
  import { existsSync as existsSync77, mkdirSync as mkdirSync32, renameSync as renameSync2, writeFileSync as writeFileSync50 } from "fs";
38723
- import { basename as basename25, join as join95 } from "path";
38898
+ import { basename as basename26, join as join97 } from "path";
38724
38899
 
38725
38900
  // src/commands/transcript/convertVttToMarkdown.ts
38726
38901
  function convertVttToMarkdown(inputPath) {
@@ -38730,9 +38905,9 @@ function convertVttToMarkdown(inputPath) {
38730
38905
  // src/commands/transcript/move.ts
38731
38906
  var DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/;
38732
38907
  function archiveRawVtt(vttDir, sourcePath, filename) {
38733
- const processedDir = join95(vttDir, "processed");
38908
+ const processedDir = join97(vttDir, "processed");
38734
38909
  mkdirSync32(processedDir, { recursive: true });
38735
- renameSync2(sourcePath, join95(processedDir, filename));
38910
+ renameSync2(sourcePath, join97(processedDir, filename));
38736
38911
  }
38737
38912
  function move(file, options2) {
38738
38913
  const { date, client } = options2;
@@ -38741,27 +38916,27 @@ function move(file, options2) {
38741
38916
  process.exit(1);
38742
38917
  }
38743
38918
  const { vttDir, transcriptsDir, summaryDir } = getTranscriptConfig();
38744
- const filename = basename25(file);
38745
- const sourcePath = join95(vttDir, filename);
38919
+ const filename = basename26(file);
38920
+ const sourcePath = join97(vttDir, filename);
38746
38921
  if (!existsSync77(sourcePath)) {
38747
38922
  console.error(`Error: VTT file not found: ${sourcePath}`);
38748
38923
  process.exit(1);
38749
38924
  }
38750
- const base = basename25(filename, ".vtt").replace(/ Transcription$/, "");
38925
+ const base = basename26(filename, ".vtt").replace(/ Transcription$/, "");
38751
38926
  const outputName = `${date} ${base}.md`;
38752
- const formattedDir = join95(transcriptsDir, client);
38927
+ const formattedDir = join97(transcriptsDir, client);
38753
38928
  mkdirSync32(formattedDir, { recursive: true });
38754
- const formattedPath = join95(formattedDir, outputName);
38929
+ const formattedPath = join97(formattedDir, outputName);
38755
38930
  writeFileSync50(formattedPath, convertVttToMarkdown(sourcePath), "utf8");
38756
38931
  archiveRawVtt(vttDir, sourcePath, filename);
38757
- const summaryPath = join95(summaryDir, client, outputName);
38932
+ const summaryPath = join97(summaryDir, client, outputName);
38758
38933
  console.log(`Formatted transcript: ${formattedPath}`);
38759
38934
  console.log(`Summary target: ${summaryPath}`);
38760
38935
  }
38761
38936
 
38762
38937
  // src/commands/transcript/merge.ts
38763
38938
  import { existsSync as existsSync78, writeFileSync as writeFileSync51 } from "fs";
38764
- import { basename as basename26 } from "path";
38939
+ import { basename as basename27 } from "path";
38765
38940
 
38766
38941
  // src/commands/transcript/failTranscript.ts
38767
38942
  function failTranscript(message3) {
@@ -38907,7 +39082,7 @@ function readSource3(file) {
38907
39082
  if (!existsSync78(file)) failTranscript(`VTT file not found: ${file}`);
38908
39083
  const cues = readCleanedCues(file);
38909
39084
  if (cues.length === 0) failTranscript(`no cues found in: ${file}`);
38910
- return { path: file, name: basename26(file), cues };
39085
+ return { path: file, name: basename27(file), cues };
38911
39086
  }
38912
39087
  function wholePassages(sources) {
38913
39088
  return sources.map((source) => ({
@@ -39398,14 +39573,14 @@ function findRuleViolations(data, rule) {
39398
39573
  }
39399
39574
  return violations;
39400
39575
  }
39401
- function findForbiddenStrings(rules3, readJson) {
39402
- return rules3.flatMap((rule) => findRuleViolations(readJson(rule.file), rule));
39576
+ function findForbiddenStrings(rules2, readJson) {
39577
+ return rules2.flatMap((rule) => findRuleViolations(readJson(rule.file), rule));
39403
39578
  }
39404
39579
 
39405
39580
  // src/commands/verify/forbiddenStrings.ts
39406
39581
  function forbiddenStrings() {
39407
- const rules3 = loadConfig().forbiddenStrings ?? [];
39408
- if (rules3.length === 0) {
39582
+ const rules2 = loadConfig().forbiddenStrings ?? [];
39583
+ if (rules2.length === 0) {
39409
39584
  console.log("No forbidden-strings rules configured.");
39410
39585
  process.exit(0);
39411
39586
  }
@@ -39426,7 +39601,7 @@ function forbiddenStrings() {
39426
39601
  cache6.set(file, parsed);
39427
39602
  return parsed;
39428
39603
  };
39429
- const violations = findForbiddenStrings(rules3, readJson);
39604
+ const violations = findForbiddenStrings(rules2, readJson);
39430
39605
  if (violations.length === 0) {
39431
39606
  console.log("No forbidden strings found.");
39432
39607
  process.exit(0);
@@ -39568,7 +39743,7 @@ function listMigrationFiles(dir) {
39568
39743
 
39569
39744
  // src/commands/verify/migrations/readBaselineMigrations.ts
39570
39745
  import { execSync as execSync63 } from "child_process";
39571
- import { basename as basename27 } from "path";
39746
+ import { basename as basename28 } from "path";
39572
39747
  var MIGRATION_FILE3 = /^migration\d+[A-Za-z0-9]*\.ts$/;
39573
39748
  function readBaselineMigrations(repoRelativeDir, ref) {
39574
39749
  const baseline = /* @__PURE__ */ new Map();
@@ -39581,7 +39756,7 @@ function readBaselineMigrations(repoRelativeDir, ref) {
39581
39756
  } catch {
39582
39757
  return baseline;
39583
39758
  }
39584
- const paths = listing.split("\n").map((line) => line.trim()).filter(Boolean).filter((path106) => MIGRATION_FILE3.test(basename27(path106)));
39759
+ const paths = listing.split("\n").map((line) => line.trim()).filter(Boolean).filter((path106) => MIGRATION_FILE3.test(basename28(path106)));
39585
39760
  for (const path106 of paths) {
39586
39761
  try {
39587
39762
  const content = execSync63(`git show "${ref}:${path106}"`, {
@@ -39589,7 +39764,7 @@ function readBaselineMigrations(repoRelativeDir, ref) {
39589
39764
  maxBuffer: 16 * 1024 * 1024,
39590
39765
  stdio: ["pipe", "pipe", "pipe"]
39591
39766
  });
39592
- baseline.set(basename27(path106), content);
39767
+ baseline.set(basename28(path106), content);
39593
39768
  } catch {
39594
39769
  }
39595
39770
  }
@@ -39802,46 +39977,6 @@ function gitFailureReason(error) {
39802
39977
  return message3.trim() || "git failed without reporting a reason";
39803
39978
  }
39804
39979
 
39805
- // src/commands/watch/resolveUpstream.ts
39806
- import { execFileSync as execFileSync20 } from "child_process";
39807
- function runGit3(args, cwd) {
39808
- return execFileSync20("git", args, {
39809
- encoding: "utf8",
39810
- stdio: ["pipe", "pipe", "pipe"],
39811
- cwd
39812
- }).trim();
39813
- }
39814
- function resolveUpstream(cwd) {
39815
- try {
39816
- runGit3(["rev-parse", "--is-inside-work-tree"], cwd);
39817
- } catch {
39818
- throw new Error(
39819
- "not a git repository \u2014 run assist watch wait from inside a repo"
39820
- );
39821
- }
39822
- let branch2;
39823
- try {
39824
- branch2 = runGit3(["symbolic-ref", "--quiet", "--short", "HEAD"], cwd);
39825
- } catch {
39826
- throw new Error(
39827
- "HEAD is detached \u2014 check out a branch before waiting on its upstream"
39828
- );
39829
- }
39830
- try {
39831
- return {
39832
- branch: branch2,
39833
- upstream: runGit3(
39834
- ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"],
39835
- cwd
39836
- )
39837
- };
39838
- } catch {
39839
- throw new Error(
39840
- `branch "${branch2}" has no upstream \u2014 set one with: git push -u origin ${branch2}`
39841
- );
39842
- }
39843
- }
39844
-
39845
39980
  // src/commands/watch/simulatedDivergencePath.ts
39846
39981
  import { resolve as resolve25 } from "path";
39847
39982
  function simulatedDivergencePath(cwd) {
@@ -39909,22 +40044,6 @@ async function watchLoop() {
39909
40044
  }
39910
40045
  }
39911
40046
 
39912
- // src/commands/watch/changedPaths.ts
39913
- function changedPaths(from, cwd) {
39914
- return runGit3(["diff", "--name-only", `${from}..HEAD`], cwd).split("\n").filter((line) => line.length > 0);
39915
- }
39916
-
39917
- // src/commands/watch/readBuiltVersion.ts
39918
- import { join as join96 } from "path";
39919
- function readBuiltVersion(cwd) {
39920
- try {
39921
- const root2 = runGit3(["rev-parse", "--show-toplevel"], cwd);
39922
- return readPackageJson(join96(root2, "package.json")).version ?? "unknown";
39923
- } catch {
39924
- return "unknown";
39925
- }
39926
- }
39927
-
39928
40047
  // src/commands/watch/readRecentCommits.ts
39929
40048
  function readRecentCommits(count8 = 10, cwd) {
39930
40049
  const output = runGit3(
@@ -39972,24 +40091,12 @@ function renderWatchReport({
39972
40091
  }
39973
40092
 
39974
40093
  // src/commands/watch/restartAdvice.ts
39975
- var webUiPrefix = "src/commands/sessions/web/ui/";
39976
- var sessionsPrefix = "src/commands/sessions/";
39977
- var rules = [
39978
- {
39979
- matches: (path106) => path106.startsWith(webUiPrefix),
39980
- advice: "restart the web server, then hard-reload the browser tab"
39981
- },
39982
- {
39983
- matches: (path106) => path106.startsWith(sessionsPrefix) && !path106.startsWith(webUiPrefix),
39984
- advice: "restart the daemon"
39985
- }
39986
- ];
39987
40094
  function restartAdvice(paths) {
39988
- return rules.filter((rule) => paths.some(rule.matches)).map((rule) => rule.advice);
40095
+ return restartRules.filter((rule) => paths.some(rule.matches)).map((rule) => rule.advice);
39989
40096
  }
39990
40097
 
39991
40098
  // src/commands/watch/syncAdvice.ts
39992
- var rules2 = [
40099
+ var rules = [
39993
40100
  {
39994
40101
  matches: (path106) => path106.startsWith("claude/commands/"),
39995
40102
  reason: "claude/commands changed"
@@ -40016,7 +40123,7 @@ var rules2 = [
40016
40123
  }
40017
40124
  ];
40018
40125
  function syncAdvice(paths) {
40019
- return rules2.filter((rule) => paths.some(rule.matches)).map((rule) => rule.reason);
40126
+ return rules.filter((rule) => paths.some(rule.matches)).map((rule) => rule.reason);
40020
40127
  }
40021
40128
 
40022
40129
  // src/commands/watch/buildWatchReport.ts
@@ -40702,7 +40809,7 @@ async function auth() {
40702
40809
  // src/commands/roam/postRoamActivity.ts
40703
40810
  import { execFileSync as execFileSync22 } from "child_process";
40704
40811
  import { readdirSync as readdirSync22, readFileSync as readFileSync62, statSync as statSync13 } from "fs";
40705
- import { join as join97 } from "path";
40812
+ import { join as join98 } from "path";
40706
40813
  function findPortFile(roamDir) {
40707
40814
  let entries;
40708
40815
  try {
@@ -40711,7 +40818,7 @@ function findPortFile(roamDir) {
40711
40818
  return void 0;
40712
40819
  }
40713
40820
  const candidates = entries.filter((name) => /^roam-local-api(-[^.]+)?\.port$/.test(name)).map((name) => {
40714
- const path106 = join97(roamDir, name);
40821
+ const path106 = join98(roamDir, name);
40715
40822
  try {
40716
40823
  return { path: path106, mtimeMs: statSync13(path106).mtimeMs };
40717
40824
  } catch {
@@ -40728,7 +40835,7 @@ var PID_BY_APP = {
40728
40835
  function postRoamActivity(app, event) {
40729
40836
  const appData = process.env.APPDATA;
40730
40837
  if (!appData) return;
40731
- const portFile = findPortFile(join97(appData, "Roam"));
40838
+ const portFile = findPortFile(join98(appData, "Roam"));
40732
40839
  if (!portFile) return;
40733
40840
  let port;
40734
40841
  try {
@@ -40864,7 +40971,7 @@ async function run3(name, args) {
40864
40971
 
40865
40972
  // src/commands/run/add.ts
40866
40973
  import { mkdirSync as mkdirSync33, writeFileSync as writeFileSync53 } from "fs";
40867
- import { join as join98 } from "path";
40974
+ import { join as join99 } from "path";
40868
40975
 
40869
40976
  // src/commands/run/extractOption.ts
40870
40977
  function extractOption(args, flag) {
@@ -40925,7 +41032,7 @@ function saveNewRunConfig(name, command, args, cwd) {
40925
41032
  saveConfig(config);
40926
41033
  }
40927
41034
  function createCommandFile(name) {
40928
- const dir = join98(".claude", "commands");
41035
+ const dir = join99(".claude", "commands");
40929
41036
  mkdirSync33(dir, { recursive: true });
40930
41037
  const content = `---
40931
41038
  description: Run ${name}
@@ -40933,7 +41040,7 @@ description: Run ${name}
40933
41040
 
40934
41041
  Run \`assist run ${name} $ARGUMENTS 2>&1\`.
40935
41042
  `;
40936
- const filePath = join98(dir, `${name}.md`);
41043
+ const filePath = join99(dir, `${name}.md`);
40937
41044
  writeFileSync53(filePath, content);
40938
41045
  console.log(`Created command file: ${filePath}`);
40939
41046
  }
@@ -40990,7 +41097,7 @@ function link2() {
40990
41097
 
40991
41098
  // src/commands/run/remove.ts
40992
41099
  import { existsSync as existsSync82, unlinkSync as unlinkSync21 } from "fs";
40993
- import { join as join99 } from "path";
41100
+ import { join as join100 } from "path";
40994
41101
  function findRemoveIndex() {
40995
41102
  const idx = process.argv.indexOf("remove");
40996
41103
  if (idx === -1 || idx + 1 >= process.argv.length) return -1;
@@ -41005,7 +41112,7 @@ function parseRemoveName() {
41005
41112
  return process.argv[idx + 1];
41006
41113
  }
41007
41114
  function deleteCommandFile(name) {
41008
- const filePath = join99(".claude", "commands", `${name}.md`);
41115
+ const filePath = join100(".claude", "commands", `${name}.md`);
41009
41116
  if (existsSync82(filePath)) {
41010
41117
  unlinkSync21(filePath);
41011
41118
  console.log(`Deleted command file: ${filePath}`);
@@ -41996,7 +42103,116 @@ function createRunSession(id, { runName, runArgs, cwd, meta }) {
41996
42103
  };
41997
42104
  }
41998
42105
 
41999
- // src/commands/sessions/daemon/autoUpdate/startAutoUpdate.ts
42106
+ // src/commands/sessions/daemon/autoUpdate/autoUpdateEnabled.ts
42107
+ function autoUpdateEnabled(installDir) {
42108
+ try {
42109
+ return loadConfigFrom(installDir).autoUpdate?.enabled ?? true;
42110
+ } catch (error) {
42111
+ daemonLog(
42112
+ `auto-update: config failed to load, keeping the default (on): ${error instanceof Error ? error.message : String(error)}`
42113
+ );
42114
+ return true;
42115
+ }
42116
+ }
42117
+
42118
+ // src/commands/sessions/daemon/autoUpdate/autoUpdateState.ts
42119
+ var loop = {
42120
+ phase: "off",
42121
+ since: (/* @__PURE__ */ new Date()).toISOString(),
42122
+ reason: "not started"
42123
+ };
42124
+ var startCommit;
42125
+ var autoUpdateState = {
42126
+ loop: () => loop,
42127
+ startCommit: () => startCommit,
42128
+ setStartCommit: (commit2) => {
42129
+ startCommit = commit2;
42130
+ },
42131
+ enter: (phase, extra = {}) => {
42132
+ loop = { phase, since: (/* @__PURE__ */ new Date()).toISOString(), ...extra };
42133
+ }
42134
+ };
42135
+
42136
+ // src/commands/sessions/daemon/autoUpdate/holdForEscalation.ts
42137
+ var ESCALATION_POLL_MS = 5e3;
42138
+ async function holdForEscalation(sessionId, deps2) {
42139
+ deps2.enter("diverged", { escalationId: sessionId });
42140
+ deps2.note(`paused while session ${sessionId} reconciles the divergence`);
42141
+ while (deps2.isLive(sessionId)) await deps2.sleep(ESCALATION_POLL_MS);
42142
+ deps2.note(`session ${sessionId} ended; resuming`);
42143
+ }
42144
+
42145
+ // src/commands/sessions/daemon/autoUpdate/runLapCapturingTail.ts
42146
+ var OUTPUT_TAIL = 16 * 1024;
42147
+ async function runLapCapturingTail(deps2) {
42148
+ let tail = "";
42149
+ const end = await deps2.runLap((text17) => {
42150
+ tail = (tail + text17).slice(-OUTPUT_TAIL);
42151
+ deps2.record(text17);
42152
+ });
42153
+ return { end, tail };
42154
+ }
42155
+
42156
+ // src/commands/sessions/daemon/autoUpdate/runAutoUpdateLoop.ts
42157
+ var DIVERGED_EXIT_CODE = 3;
42158
+ var RETRY_AFTER_FAILURE_MS = 5 * 60 * 1e3;
42159
+ async function runAutoUpdateLoop(deps2, maxLaps = Number.POSITIVE_INFINITY) {
42160
+ let held = deps2.liveEscalation();
42161
+ for (let lap = 1; lap <= maxLaps; lap++) {
42162
+ if (held) await holdForEscalation(held, deps2);
42163
+ held = void 0;
42164
+ deps2.enter("waiting");
42165
+ deps2.note(
42166
+ `lap ${lap} at ${(/* @__PURE__ */ new Date()).toLocaleString()}: assist ${WATCH_LAP_ARGS.join(" ")}`
42167
+ );
42168
+ let result;
42169
+ try {
42170
+ result = await runLapCapturingTail(deps2);
42171
+ } catch (error) {
42172
+ await backOff(
42173
+ `lap ${lap} could not start (${error instanceof Error ? error.message : String(error)})`,
42174
+ deps2
42175
+ );
42176
+ continue;
42177
+ }
42178
+ const { end, tail } = result;
42179
+ const how = end.signal ? `killed by ${end.signal}` : `exited ${end.code}`;
42180
+ if (end.code === DIVERGED_EXIT_CODE) {
42181
+ held = deps2.escalate(tail);
42182
+ deps2.note(`lap ${lap} ${how} (divergence); escalated to session ${held}`);
42183
+ continue;
42184
+ }
42185
+ if (decideLap(end, false).kind === "relaunch") {
42186
+ deps2.note(`lap ${lap} ${how}; relaunching`);
42187
+ continue;
42188
+ }
42189
+ await backOff(`lap ${lap} ${how}`, deps2);
42190
+ }
42191
+ }
42192
+ async function backOff(reason4, deps2) {
42193
+ deps2.enter("retrying", { reason: reason4 });
42194
+ deps2.note(`${reason4}; retrying in ${RETRY_AFTER_FAILURE_MS / 6e4}m`);
42195
+ await deps2.sleep(RETRY_AFTER_FAILURE_MS);
42196
+ }
42197
+
42198
+ // src/commands/sessions/daemon/autoUpdate/trimWatcherLog.ts
42199
+ import { readFileSync as readFileSync65, writeFileSync as writeFileSync54 } from "fs";
42200
+ var KEEP_TAIL = 256 * 1024;
42201
+ var MAX_LOG_SIZE = 4 * 1024 * 1024;
42202
+ function trimWatcherLog(cwd) {
42203
+ const path106 = watcherLogPath(cwd);
42204
+ let log2;
42205
+ try {
42206
+ log2 = readFileSync65(path106, "utf8");
42207
+ } catch {
42208
+ return;
42209
+ }
42210
+ if (log2.length <= MAX_LOG_SIZE) return;
42211
+ writeFileSync54(path106, log2.slice(-KEEP_TAIL));
42212
+ daemonLog(`auto-update: log ${path106} trimmed to its last ${KEEP_TAIL} chars`);
42213
+ }
42214
+
42215
+ // src/commands/sessions/daemon/autoUpdate/loopDeps.ts
42000
42216
  import { setTimeout as sleep2 } from "timers/promises";
42001
42217
 
42002
42218
  // src/commands/sessions/daemon/watcherNote.ts
@@ -42009,19 +42225,6 @@ function watcherNote(text17) {
42009
42225
  // src/commands/sessions/daemon/worktree/appendWatcherLog.ts
42010
42226
  import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync34 } from "fs";
42011
42227
  import { dirname as dirname42 } from "path";
42012
-
42013
- // src/commands/sessions/daemon/worktree/watcherLogPath.ts
42014
- import { createHash as createHash4 } from "crypto";
42015
- import { homedir as homedir26 } from "os";
42016
- import { basename as basename28, join as join101 } from "path";
42017
- function watcherLogPath(cwd) {
42018
- const clone = canonicalTreePath(cwd);
42019
- const key = createHash4("sha1").update(clone).digest("hex").slice(0, 8);
42020
- const dir = process.env.ASSIST_WATCHER_LOG_DIR ?? join101(homedir26(), ".assist", "watchers");
42021
- return join101(dir, `${basename28(clone) || "root"}-${key}.log`);
42022
- }
42023
-
42024
- // src/commands/sessions/daemon/worktree/appendWatcherLog.ts
42025
42228
  function appendWatcherLog(cwd, data) {
42026
42229
  try {
42027
42230
  const path106 = watcherLogPath(cwd);
@@ -42175,18 +42378,6 @@ function escalateDivergence(ctx, cwd, output) {
42175
42378
  return id;
42176
42379
  }
42177
42380
 
42178
- // src/commands/sessions/daemon/autoUpdate/autoUpdateEnabled.ts
42179
- function autoUpdateEnabled(installDir) {
42180
- try {
42181
- return loadConfigFrom(installDir).autoUpdate?.enabled ?? true;
42182
- } catch (error) {
42183
- daemonLog(
42184
- `auto-update: config failed to load, keeping the default (on): ${error instanceof Error ? error.message : String(error)}`
42185
- );
42186
- return true;
42187
- }
42188
- }
42189
-
42190
42381
  // src/commands/sessions/daemon/autoUpdate/lineRecorder.ts
42191
42382
  function lineRecorder(onLine) {
42192
42383
  let pending = "";
@@ -42200,64 +42391,6 @@ function lineRecorder(onLine) {
42200
42391
  };
42201
42392
  }
42202
42393
 
42203
- // src/commands/sessions/daemon/autoUpdate/holdForEscalation.ts
42204
- var ESCALATION_POLL_MS = 5e3;
42205
- async function holdForEscalation(sessionId, deps2) {
42206
- deps2.note(`paused while session ${sessionId} reconciles the divergence`);
42207
- while (deps2.isLive(sessionId)) await deps2.sleep(ESCALATION_POLL_MS);
42208
- deps2.note(`session ${sessionId} ended; resuming`);
42209
- }
42210
-
42211
- // src/commands/sessions/daemon/autoUpdate/runLapCapturingTail.ts
42212
- var OUTPUT_TAIL = 16 * 1024;
42213
- async function runLapCapturingTail(deps2) {
42214
- let tail = "";
42215
- const end = await deps2.runLap((text17) => {
42216
- tail = (tail + text17).slice(-OUTPUT_TAIL);
42217
- deps2.record(text17);
42218
- });
42219
- return { end, tail };
42220
- }
42221
-
42222
- // src/commands/sessions/daemon/autoUpdate/runAutoUpdateLoop.ts
42223
- var DIVERGED_EXIT_CODE = 3;
42224
- var RETRY_AFTER_FAILURE_MS = 5 * 60 * 1e3;
42225
- async function runAutoUpdateLoop(deps2, maxLaps = Number.POSITIVE_INFINITY) {
42226
- let held = deps2.liveEscalation();
42227
- for (let lap = 1; lap <= maxLaps; lap++) {
42228
- if (held) await holdForEscalation(held, deps2);
42229
- held = void 0;
42230
- deps2.note(
42231
- `lap ${lap} at ${(/* @__PURE__ */ new Date()).toLocaleString()}: assist ${WATCH_LAP_ARGS.join(" ")}`
42232
- );
42233
- let result;
42234
- try {
42235
- result = await runLapCapturingTail(deps2);
42236
- } catch (error) {
42237
- deps2.note(
42238
- `lap ${lap} could not start (${error instanceof Error ? error.message : String(error)}); retrying in ${RETRY_AFTER_FAILURE_MS / 6e4}m`
42239
- );
42240
- await deps2.sleep(RETRY_AFTER_FAILURE_MS);
42241
- continue;
42242
- }
42243
- const { end, tail } = result;
42244
- const how = end.signal ? `killed by ${end.signal}` : `exited ${end.code}`;
42245
- if (end.code === DIVERGED_EXIT_CODE) {
42246
- held = deps2.escalate(tail);
42247
- deps2.note(`lap ${lap} ${how} (divergence); escalated to session ${held}`);
42248
- continue;
42249
- }
42250
- if (decideLap(end, false).kind === "relaunch") {
42251
- deps2.note(`lap ${lap} ${how}; relaunching`);
42252
- continue;
42253
- }
42254
- deps2.note(
42255
- `lap ${lap} ${how}; retrying in ${RETRY_AFTER_FAILURE_MS / 6e4}m`
42256
- );
42257
- await deps2.sleep(RETRY_AFTER_FAILURE_MS);
42258
- }
42259
- }
42260
-
42261
42394
  // src/commands/sessions/daemon/autoUpdate/runUpdateLap.ts
42262
42395
  import { spawn as spawn11 } from "child_process";
42263
42396
  function runUpdateLap(cwd, onOutput) {
@@ -42290,24 +42423,7 @@ function runUpdateLap(cwd, onOutput) {
42290
42423
  });
42291
42424
  }
42292
42425
 
42293
- // src/commands/sessions/daemon/autoUpdate/trimWatcherLog.ts
42294
- import { readFileSync as readFileSync65, writeFileSync as writeFileSync54 } from "fs";
42295
- var KEEP_TAIL = 256 * 1024;
42296
- var MAX_LOG_SIZE = 4 * 1024 * 1024;
42297
- function trimWatcherLog(cwd) {
42298
- const path106 = watcherLogPath(cwd);
42299
- let log2;
42300
- try {
42301
- log2 = readFileSync65(path106, "utf8");
42302
- } catch {
42303
- return;
42304
- }
42305
- if (log2.length <= MAX_LOG_SIZE) return;
42306
- writeFileSync54(path106, log2.slice(-KEEP_TAIL));
42307
- daemonLog(`auto-update: log ${path106} trimmed to its last ${KEEP_TAIL} chars`);
42308
- }
42309
-
42310
- // src/commands/sessions/daemon/autoUpdate/startAutoUpdate.ts
42426
+ // src/commands/sessions/daemon/autoUpdate/loopDeps.ts
42311
42427
  function loopDeps(installDir, ctx) {
42312
42428
  const clone = canonicalTreePath(installDir);
42313
42429
  const logLine = lineRecorder((line) => daemonLog(`auto-update: ${line}`));
@@ -42321,6 +42437,7 @@ function loopDeps(installDir, ctx) {
42321
42437
  appendWatcherLog(installDir, watcherNote(text17));
42322
42438
  daemonLog(`auto-update: ${text17}`);
42323
42439
  },
42440
+ enter: autoUpdateState.enter,
42324
42441
  liveEscalation: () => liveEscalationIn(ctx().sessions, clone)?.id,
42325
42442
  escalate: (output) => escalateDivergence(ctx(), installDir, output),
42326
42443
  isLive: (sessionId) => {
@@ -42330,14 +42447,21 @@ function loopDeps(installDir, ctx) {
42330
42447
  sleep: (ms) => sleep2(ms)
42331
42448
  };
42332
42449
  }
42450
+
42451
+ // src/commands/sessions/daemon/autoUpdate/startAutoUpdate.ts
42452
+ function stayOff(reason4) {
42453
+ autoUpdateState.enter("off", { reason: reason4 });
42454
+ daemonLog(`auto-update: off (${reason4})`);
42455
+ }
42333
42456
  function startAutoUpdate(ctx) {
42334
42457
  const installDir = getInstallDir();
42335
- if (!autoUpdateEnabled(installDir)) {
42336
- daemonLog("auto-update: off (autoUpdate.enabled is false)");
42458
+ if (!isGitRepo(installDir)) {
42459
+ stayOff(`${installDir} is not a git clone`);
42337
42460
  return;
42338
42461
  }
42339
- if (!isGitRepo(installDir)) {
42340
- daemonLog(`auto-update: off (${installDir} is not a git clone)`);
42462
+ autoUpdateState.setStartCommit(headCommit3(installDir));
42463
+ if (!autoUpdateEnabled(installDir)) {
42464
+ stayOff("autoUpdate.enabled is false");
42341
42465
  return;
42342
42466
  }
42343
42467
  trimWatcherLog(installDir);
@@ -47014,6 +47138,16 @@ function exitAfterFlush(code) {
47014
47138
  // src/commands/sessions/daemon/handleConnection.ts
47015
47139
  import { createInterface as createInterface10 } from "readline";
47016
47140
 
47141
+ // src/commands/sessions/daemon/autoUpdate/autoUpdateReply.ts
47142
+ function autoUpdateReply() {
47143
+ return {
47144
+ type: "auto-update",
47145
+ version: ASSIST_VERSION,
47146
+ startCommit: autoUpdateState.startCommit(),
47147
+ loop: autoUpdateState.loop()
47148
+ };
47149
+ }
47150
+
47017
47151
  // src/commands/sessions/daemon/handleHelloRequest.ts
47018
47152
  var handleHelloRequest = (client, m, d) => {
47019
47153
  sendTo(client, buildHello());
@@ -47539,6 +47673,7 @@ var messageHandlers = {
47539
47673
  hello: handleHelloRequest,
47540
47674
  output: handleOutputRequest,
47541
47675
  nodes: (client, m) => m.links.sendNodes(client),
47676
+ "auto-update": (client) => sendTo(client, autoUpdateReply()),
47542
47677
  "reload-links": (client, m) => {
47543
47678
  daemonLog("reload-links received");
47544
47679
  m.links.reload();