@staff0rd/assist 0.774.1 → 0.775.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { Command } from "commander";
6
6
  // package.json
7
7
  var package_default = {
8
8
  name: "@staff0rd/assist",
9
- version: "0.774.1",
9
+ version: "0.775.1",
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`
@@ -16406,6 +16242,23 @@ function findTailscalePeer(status2, hostname2) {
16406
16242
  );
16407
16243
  }
16408
16244
 
16245
+ // src/commands/sessions/nodes/tailscaleCliFailure.ts
16246
+ function tailscaleCliFailure(error, cli) {
16247
+ const detail = describePeerError(error);
16248
+ const owner = /Tailscale already in use by (\S+?),/.exec(detail)?.[1];
16249
+ if (owner)
16250
+ return failed(
16251
+ "tailscale",
16252
+ `Tailscale on this node is signed in by Windows user ${owner}, so it is disconnected while another user is active`,
16253
+ `sign in to Windows as ${owner} and run \`${cli} up --unattended\` (or tick "Run unattended" in Tailscale's menu) so it stays connected for every user; otherwise sign ${owner} out and run \`${cli} up\` as this user`
16254
+ );
16255
+ return failed(
16256
+ "tailscale",
16257
+ detail,
16258
+ `\`${cli} status --json\` failed \u2014 install Tailscale on this node and sign in with \`${cli} up\``
16259
+ );
16260
+ }
16261
+
16409
16262
  // src/commands/sessions/nodes/tailscaleHop.ts
16410
16263
  async function tailscaleHop(spec, probes) {
16411
16264
  const cli = tailscaleCli();
@@ -16413,11 +16266,7 @@ async function tailscaleHop(spec, probes) {
16413
16266
  try {
16414
16267
  status2 = await probes.tailscale();
16415
16268
  } catch (error) {
16416
- return failed(
16417
- "tailscale",
16418
- describePeerError(error),
16419
- `\`${cli} status --json\` failed \u2014 install Tailscale on this node and sign in with \`${cli} up\``
16420
- );
16269
+ return tailscaleCliFailure(error, cli);
16421
16270
  }
16422
16271
  if (status2.BackendState !== "Running")
16423
16272
  return failed(
@@ -16437,7 +16286,7 @@ async function tailscaleHop(spec, probes) {
16437
16286
  return failed(
16438
16287
  "tailscale",
16439
16288
  `${hostname2} is offline in the tailnet`,
16440
- `is ${spec.name}'s machine awake with Tailscale running and signed in?`
16289
+ `is ${spec.name}'s machine awake with Tailscale running and signed in? On Windows, Tailscale disconnects while a user other than the one who signed it in is active unless it runs unattended \u2014 run \`assist sessions nodes doctor\` on ${spec.name}`
16441
16290
  );
16442
16291
  return passed(
16443
16292
  "tailscale",
@@ -17164,6 +17013,194 @@ function unsetConfig(req, res) {
17164
17013
  });
17165
17014
  }
17166
17015
 
17016
+ // src/commands/watch/resolveUpstream.ts
17017
+ import { execFileSync as execFileSync4 } from "child_process";
17018
+ function runGit3(args, cwd) {
17019
+ return execFileSync4("git", args, {
17020
+ encoding: "utf8",
17021
+ stdio: ["pipe", "pipe", "pipe"],
17022
+ cwd
17023
+ }).trim();
17024
+ }
17025
+ function resolveUpstream(cwd) {
17026
+ try {
17027
+ runGit3(["rev-parse", "--is-inside-work-tree"], cwd);
17028
+ } catch {
17029
+ throw new Error(
17030
+ "not a git repository \u2014 run assist watch wait from inside a repo"
17031
+ );
17032
+ }
17033
+ let branch2;
17034
+ try {
17035
+ branch2 = runGit3(["symbolic-ref", "--quiet", "--short", "HEAD"], cwd);
17036
+ } catch {
17037
+ throw new Error(
17038
+ "HEAD is detached \u2014 check out a branch before waiting on its upstream"
17039
+ );
17040
+ }
17041
+ try {
17042
+ return {
17043
+ branch: branch2,
17044
+ upstream: runGit3(
17045
+ ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"],
17046
+ cwd
17047
+ )
17048
+ };
17049
+ } catch {
17050
+ throw new Error(
17051
+ `branch "${branch2}" has no upstream \u2014 set one with: git push -u origin ${branch2}`
17052
+ );
17053
+ }
17054
+ }
17055
+
17056
+ // src/commands/watch/changedPaths.ts
17057
+ function changedPaths(from, cwd) {
17058
+ return runGit3(["diff", "--name-only", `${from}..HEAD`], cwd).split("\n").filter((line) => line.length > 0);
17059
+ }
17060
+
17061
+ // src/commands/watch/readBuiltVersion.ts
17062
+ import { join as join41 } from "path";
17063
+ function readBuiltVersion(cwd) {
17064
+ try {
17065
+ const root2 = runGit3(["rev-parse", "--show-toplevel"], cwd);
17066
+ return readPackageJson(join41(root2, "package.json")).version ?? "unknown";
17067
+ } catch {
17068
+ return "unknown";
17069
+ }
17070
+ }
17071
+
17072
+ // src/commands/watch/restartRules.ts
17073
+ var webUiPrefix = "src/commands/sessions/web/ui/";
17074
+ var sessionsPrefix = "src/commands/sessions/";
17075
+ var restartRules = [
17076
+ {
17077
+ target: "webserver",
17078
+ matches: (path106) => path106.startsWith(webUiPrefix),
17079
+ advice: "restart the web server, then hard-reload the browser tab"
17080
+ },
17081
+ {
17082
+ target: "daemon",
17083
+ matches: (path106) => path106.startsWith(sessionsPrefix) && !path106.startsWith(webUiPrefix),
17084
+ advice: "restart the daemon"
17085
+ }
17086
+ ];
17087
+
17088
+ // src/commands/watch/restartTargets.ts
17089
+ function restartTargets(paths) {
17090
+ return restartRules.filter((rule) => paths.some(rule.matches)).map((rule) => rule.target);
17091
+ }
17092
+
17093
+ // src/commands/sessions/web/updates/pendingRestarts.ts
17094
+ function targetsSince(commit2, target, changedSince2) {
17095
+ return commit2 !== void 0 && restartTargets(changedSince2(commit2)).includes(target);
17096
+ }
17097
+ function pendingRestarts(input) {
17098
+ const { daemonVersion, built, changedSince: changedSince2 } = input;
17099
+ const daemon = daemonVersion !== void 0 && (built !== "unknown" && daemonVersion !== built || targetsSince(input.daemonStartCommit, "daemon", changedSince2));
17100
+ const web3 = targetsSince(input.webStartCommit, "webserver", changedSince2);
17101
+ return [
17102
+ ...daemon ? ["daemon"] : [],
17103
+ ...web3 ? ["webserver"] : []
17104
+ ];
17105
+ }
17106
+
17107
+ // src/shared/stripAnsi.ts
17108
+ var ANSI = new RegExp(
17109
+ `${String.fromCharCode(27)}\\[[0-9;?]*[ -/]*[@-~]`,
17110
+ "g"
17111
+ );
17112
+ function stripAnsi(text17) {
17113
+ return text17.replace(ANSI, "");
17114
+ }
17115
+
17116
+ // src/commands/sessions/daemon/worktree/watcherLogPath.ts
17117
+ import { createHash as createHash2 } from "crypto";
17118
+ import { homedir as homedir16 } from "os";
17119
+ import { basename as basename12, join as join42 } from "path";
17120
+
17121
+ // src/commands/sessions/daemon/worktree/canonicalTreePath.ts
17122
+ import { realpathSync as realpathSync2 } from "fs";
17123
+ import { resolve as resolve14 } from "path";
17124
+ function canonicalTreePath(path106) {
17125
+ try {
17126
+ return realpathSync2(path106);
17127
+ } catch {
17128
+ return resolve14(path106);
17129
+ }
17130
+ }
17131
+
17132
+ // src/commands/sessions/daemon/worktree/watcherLogPath.ts
17133
+ function watcherLogPath(cwd) {
17134
+ const clone = canonicalTreePath(cwd);
17135
+ const key = createHash2("sha1").update(clone).digest("hex").slice(0, 8);
17136
+ const dir = process.env.ASSIST_WATCHER_LOG_DIR ?? join42(homedir16(), ".assist", "watchers");
17137
+ return join42(dir, `${basename12(clone) || "root"}-${key}.log`);
17138
+ }
17139
+
17140
+ // src/commands/sessions/web/updates/readUpdateHistory.ts
17141
+ var HISTORY_LINES = 200;
17142
+ function readUpdateHistory(installDir) {
17143
+ 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);
17144
+ }
17145
+
17146
+ // src/commands/watch/headCommit.ts
17147
+ function headCommit3(cwd) {
17148
+ try {
17149
+ return runGit3(["rev-parse", "HEAD"], cwd);
17150
+ } catch {
17151
+ return void 0;
17152
+ }
17153
+ }
17154
+
17155
+ // src/commands/sessions/web/updates/webStartCommit.ts
17156
+ var captured;
17157
+ var webStartCommit = {
17158
+ capture: () => {
17159
+ captured = headCommit3(getInstallDir());
17160
+ },
17161
+ value: () => captured
17162
+ };
17163
+
17164
+ // src/commands/sessions/web/updates/updates.ts
17165
+ function changedSince(installDir) {
17166
+ return (commit2) => {
17167
+ try {
17168
+ return changedPaths(commit2, installDir);
17169
+ } catch {
17170
+ return [];
17171
+ }
17172
+ };
17173
+ }
17174
+ async function updates(_req, res) {
17175
+ const installDir = getInstallDir();
17176
+ const reply2 = await requestDaemonReply(
17177
+ { type: "auto-update" },
17178
+ "auto-update"
17179
+ );
17180
+ const built = readBuiltVersion(installDir);
17181
+ const status2 = {
17182
+ nodeName: resolveNodeName(),
17183
+ installDir,
17184
+ running: reply2?.version ?? ASSIST_VERSION,
17185
+ built,
17186
+ restart: pendingRestarts({
17187
+ daemonVersion: reply2?.version,
17188
+ daemonStartCommit: reply2?.startCommit,
17189
+ webStartCommit: webStartCommit.value(),
17190
+ built,
17191
+ changedSince: changedSince(installDir)
17192
+ }),
17193
+ daemonReachable: reply2 !== void 0,
17194
+ loop: reply2?.loop ?? {
17195
+ phase: "off",
17196
+ since: (/* @__PURE__ */ new Date()).toISOString(),
17197
+ reason: "daemon not reachable"
17198
+ },
17199
+ history: readUpdateHistory(installDir)
17200
+ };
17201
+ respondJson(res, 200, status2);
17202
+ }
17203
+
17167
17204
  // src/commands/sessions/web/readRequestBuffer.ts
17168
17205
  async function readRequestBuffer(req, limit) {
17169
17206
  const chunks = [];
@@ -17185,7 +17222,7 @@ async function readRequestBuffer(req, limit) {
17185
17222
 
17186
17223
  // src/commands/sessions/web/stageAttachment.ts
17187
17224
  import { mkdir as mkdir4, mkdtemp, writeFile as writeFile2 } from "fs/promises";
17188
- import { extname as extname2, join as join41 } from "path";
17225
+ import { extname as extname2, join as join43 } from "path";
17189
17226
  function extensionFromMime(contentType) {
17190
17227
  const mime = contentType.split(";")[0].trim().toLowerCase();
17191
17228
  const known = attachmentMimeTypes[mime];
@@ -17213,9 +17250,9 @@ function safeBaseName(name) {
17213
17250
  async function stageAttachment(name, contentType, body) {
17214
17251
  const ext = pickExtension(name, contentType);
17215
17252
  await mkdir4(stagedAttachmentsDir, { recursive: true });
17216
- const dir = await mkdtemp(join41(stagedAttachmentsDir, "upload-"));
17253
+ const dir = await mkdtemp(join43(stagedAttachmentsDir, "upload-"));
17217
17254
  const alt = safeBaseName(name);
17218
- const filePath = join41(dir, `${alt}.${ext}`);
17255
+ const filePath = join43(dir, `${alt}.${ext}`);
17219
17256
  await writeFile2(filePath, body);
17220
17257
  return { dir, filePath, alt };
17221
17258
  }
@@ -17263,17 +17300,17 @@ async function uploadPrImage(req, res) {
17263
17300
  import { readFile as readFile3, stat as stat4, writeFile as writeFile3 } from "fs/promises";
17264
17301
 
17265
17302
  // 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";
17303
+ import { execFile as execFile9 } from "child_process";
17304
+ import { existsSync as existsSync39 } from "fs";
17305
+ import { dirname as dirname24, join as join44 } from "path";
17269
17306
  import { promisify as promisify10 } from "util";
17270
- var execFileAsync9 = promisify10(execFile10);
17307
+ var execFileAsync9 = promisify10(execFile9);
17271
17308
  var TIMEOUT_MS = 15e3;
17272
17309
  function findOxfmtScript(root2) {
17273
17310
  let dir = root2;
17274
17311
  for (; ; ) {
17275
- const candidate = join42(dir, "node_modules", "oxfmt", "bin", "oxfmt");
17276
- if (existsSync40(candidate)) return candidate;
17312
+ const candidate = join44(dir, "node_modules", "oxfmt", "bin", "oxfmt");
17313
+ if (existsSync39(candidate)) return candidate;
17277
17314
  const parent = dirname24(dir);
17278
17315
  if (parent === dir) return void 0;
17279
17316
  dir = parent;
@@ -17343,7 +17380,7 @@ async function writeFileContent(req, res) {
17343
17380
  }
17344
17381
 
17345
17382
  // src/commands/sessions/web/createCssHandler.ts
17346
- import { createHash as createHash2 } from "crypto";
17383
+ import { createHash as createHash3 } from "crypto";
17347
17384
  import { readFileSync as readFileSync24 } from "fs";
17348
17385
  import { createRequire } from "module";
17349
17386
  var require2 = createRequire(import.meta.url);
@@ -17353,7 +17390,7 @@ function createCssHandler(packageEntry) {
17353
17390
  if (!cache6) {
17354
17391
  const resolved = require2.resolve(packageEntry);
17355
17392
  const body = readFileSync24(resolved, "utf8");
17356
- const etag = `"${createHash2("sha256").update(body).digest("hex").slice(0, 16)}"`;
17393
+ const etag = `"${createHash3("sha256").update(body).digest("hex").slice(0, 16)}"`;
17357
17394
  cache6 = { body, etag };
17358
17395
  }
17359
17396
  const headers = { ETag: cache6.etag, "Cache-Control": "no-cache" };
@@ -17436,7 +17473,8 @@ var routes2 = {
17436
17473
  "GET /api/config": getConfig,
17437
17474
  "POST /api/config/set": setConfig,
17438
17475
  "POST /api/config/unset": unsetConfig,
17439
- "GET /api/review/synthesis": getReviewSynthesis
17476
+ "GET /api/review/synthesis": getReviewSynthesis,
17477
+ "GET /api/updates": updates
17440
17478
  };
17441
17479
  var handleRequest = createFallbackHandler(
17442
17480
  routes2,
@@ -17705,11 +17743,183 @@ function scheduleReconnect() {
17705
17743
  setTimeout(() => void connect2(), RECONNECT_MS).unref();
17706
17744
  }
17707
17745
 
17708
- // src/commands/sessions/web.ts
17746
+ // src/commands/sessions/daemon/originForCwd.ts
17747
+ var cache4 = /* @__PURE__ */ new Map();
17748
+ function originResolutionForCwd(cwd) {
17749
+ if (!cwd) return void 0;
17750
+ const cached = cache4.get(cwd);
17751
+ if (cached !== void 0) return { origin: cached, stable: true };
17752
+ const resolved = resolveCurrentOrigin(cwd);
17753
+ if (resolved.stable) cache4.set(cwd, resolved.origin);
17754
+ return resolved;
17755
+ }
17756
+ function originForCwd(cwd) {
17757
+ return originResolutionForCwd(cwd)?.origin;
17758
+ }
17759
+
17760
+ // src/commands/sessions/daemon/repoDirExists.ts
17761
+ import { existsSync as existsSync40 } from "fs";
17762
+ function repoDirExists(cwd) {
17763
+ return existsSync40(cwd);
17764
+ }
17765
+
17766
+ // src/commands/sessions/daemon/worktree/git.ts
17767
+ import { execFile as execFile10, execFileSync as execFileSync5 } from "child_process";
17768
+ function isWindowsPath(cwd) {
17769
+ return /^[A-Za-z]:[\\/]/.test(cwd);
17770
+ }
17771
+ function gitInvocation(cwd, args) {
17772
+ const windows = isWindowsPath(cwd);
17773
+ return {
17774
+ file: windows ? "git.exe" : "git",
17775
+ argv: windows ? ["-C", cwd, ...args] : args,
17776
+ options: {
17777
+ encoding: "utf8",
17778
+ windowsHide: true,
17779
+ ...windows ? {} : { cwd }
17780
+ }
17781
+ };
17782
+ }
17783
+ function git(cwd, args) {
17784
+ const { file, argv, options: options2 } = gitInvocation(cwd, args);
17785
+ return new Promise((resolve28, reject) => {
17786
+ execFile10(file, argv, options2, (error, stdout) => {
17787
+ if (error) reject(error);
17788
+ else resolve28(stdout.toString());
17789
+ });
17790
+ });
17791
+ }
17792
+ async function gitOrNull(cwd, args) {
17793
+ try {
17794
+ return (await git(cwd, args)).trim() || null;
17795
+ } catch {
17796
+ return null;
17797
+ }
17798
+ }
17799
+ async function gitResult(cwd, args) {
17800
+ try {
17801
+ return { ok: true, out: (await git(cwd, args)).trim() };
17802
+ } catch (error) {
17803
+ return {
17804
+ ok: false,
17805
+ error: error instanceof Error ? error.message : String(error)
17806
+ };
17807
+ }
17808
+ }
17809
+ function gitSyncResult(cwd, args) {
17810
+ try {
17811
+ return { ok: true, out: gitSync(cwd, args).trim() };
17812
+ } catch (error) {
17813
+ return {
17814
+ ok: false,
17815
+ error: error instanceof Error ? error.message : String(error)
17816
+ };
17817
+ }
17818
+ }
17819
+ function gitSync(cwd, args) {
17820
+ const { file, argv, options: options2 } = gitInvocation(cwd, args);
17821
+ return execFileSync5(file, argv, {
17822
+ ...options2,
17823
+ stdio: ["pipe", "pipe", "pipe"]
17824
+ }).toString();
17825
+ }
17826
+ function gitSyncOrNull(cwd, args) {
17827
+ try {
17828
+ return gitSync(cwd, args).trim() || null;
17829
+ } catch {
17830
+ return null;
17831
+ }
17832
+ }
17833
+
17834
+ // src/commands/sessions/daemon/worktree/listWorktreePaths.ts
17835
+ function listWorktreePaths(cwd) {
17836
+ const out = gitSyncOrNull(cwd, ["worktree", "list", "--porcelain"]);
17837
+ if (!out) return [];
17838
+ return out.split("\n").filter((line) => line.startsWith("worktree ")).map((line) => line.slice("worktree ".length).trim());
17839
+ }
17840
+ function mainWorktree(cwd) {
17841
+ return listWorktreePaths(cwd)[0] ?? null;
17842
+ }
17843
+ function listLocalBranches(cwd) {
17844
+ const out = gitSyncOrNull(cwd, [
17845
+ "for-each-ref",
17846
+ "--format=%(refname:short)",
17847
+ "refs/heads"
17848
+ ]);
17849
+ return out ? out.split("\n").map((line) => line.trim()) : [];
17850
+ }
17851
+ function gitCommonDir(cwd) {
17852
+ return gitSyncOrNull(cwd, [
17853
+ "rev-parse",
17854
+ "--path-format=absolute",
17855
+ "--git-common-dir"
17856
+ ]);
17857
+ }
17858
+
17859
+ // src/commands/sessions/daemon/worktree/readWorktreeRegistry.ts
17860
+ var REGISTRY_FILE = "worktrees.json";
17861
+ function readWorktreeRegistry() {
17862
+ const data = loadJson(REGISTRY_FILE);
17863
+ return Object.entries(data).filter(([, record]) => !record.reaped).map(([path106, record]) => ({ path: path106, ...record }));
17864
+ }
17865
+ function worktreeAttributionIncludingReaped(path106) {
17866
+ const record = loadJson(REGISTRY_FILE)[path106];
17867
+ return record ? { clone: record.clone, origin: record.origin } : void 0;
17868
+ }
17869
+ function recordWorktree(path106, clone, origin) {
17870
+ const data = loadJson(REGISTRY_FILE);
17871
+ data[path106] = { clone, origin };
17872
+ saveJson(REGISTRY_FILE, data);
17873
+ }
17874
+ function forgetWorktree(path106) {
17875
+ const data = loadJson(REGISTRY_FILE);
17876
+ const record = data[path106];
17877
+ if (!record || record.reaped) return;
17878
+ data[path106] = { ...record, reaped: true };
17879
+ saveJson(REGISTRY_FILE, data);
17880
+ }
17881
+
17882
+ // src/commands/sessions/daemon/repoGroupForCwd.ts
17883
+ var cache5 = /* @__PURE__ */ new Map();
17884
+ function repoGroupForCwd(cwd) {
17885
+ if (!cwd) return void 0;
17886
+ if (cache5.has(cwd)) return cache5.get(cwd);
17887
+ const { group, stable } = resolveRepoGroup(cwd);
17888
+ if (stable) cache5.set(cwd, group);
17889
+ return group;
17890
+ }
17891
+ function resolveRepoGroup(cwd) {
17892
+ const clone = mainWorktree(cwd);
17893
+ const origin = clone ? originResolutionForCwd(clone) : void 0;
17894
+ if (clone && origin)
17895
+ return {
17896
+ group: hostedGroup(origin.origin, clone),
17897
+ stable: origin.stable
17898
+ };
17899
+ const reaped = worktreeAttributionIncludingReaped(cwd);
17900
+ if (!reaped) return { group: void 0, stable: repoDirExists(cwd) };
17901
+ const current = currentOriginOfClone(reaped.clone);
17902
+ return {
17903
+ group: hostedGroup(current?.origin ?? reaped.origin, reaped.clone),
17904
+ stable: current?.stable === true
17905
+ };
17906
+ }
17907
+ function currentOriginOfClone(clone) {
17908
+ const main = mainWorktree(clone);
17909
+ return main ? originResolutionForCwd(main) : void 0;
17910
+ }
17911
+ function hostedGroup(origin, clone) {
17912
+ return { origin, clone };
17913
+ }
17914
+
17915
+ // src/commands/sessions/repoEntryCwd.ts
17709
17916
  function repoEntryCwd(serverCwd) {
17710
17917
  return repoGroupForCwd(serverCwd)?.clone ?? serverCwd;
17711
17918
  }
17919
+
17920
+ // src/commands/sessions/web.ts
17712
17921
  async function web(options2) {
17922
+ webStartCommit.capture();
17713
17923
  const port = Number.parseInt(options2.port, 10);
17714
17924
  const server = startWebServer(
17715
17925
  "Assist",
@@ -18115,7 +18325,7 @@ function registerBranch(program2) {
18115
18325
  }
18116
18326
 
18117
18327
  // src/commands/cliHook.ts
18118
- import { basename as basename12 } from "path";
18328
+ import { basename as basename13 } from "path";
18119
18329
 
18120
18330
  // src/shared/splitCompound.ts
18121
18331
  import { parse } from "shell-quote";
@@ -18691,7 +18901,7 @@ function findTruncatedReadDenyRaw(rawCommand) {
18691
18901
  }
18692
18902
 
18693
18903
  // src/shared/isApprovedRead.ts
18694
- import { resolve as resolve15, sep as sep2 } from "path";
18904
+ import { resolve as resolve16, sep as sep2 } from "path";
18695
18905
 
18696
18906
  // src/shared/appliesFixStructure.ts
18697
18907
  var APPLY_RE = /^assist\s+github\s+issue\s+fix-structure\b.*\s--apply\b/;
@@ -18745,7 +18955,7 @@ function extractGraphqlQuery(args) {
18745
18955
 
18746
18956
  // src/shared/loadCliReads.ts
18747
18957
  import { existsSync as existsSync41, readFileSync as readFileSync26, writeFileSync as writeFileSync22 } from "fs";
18748
- import { dirname as dirname25, resolve as resolve14 } from "path";
18958
+ import { dirname as dirname25, resolve as resolve15 } from "path";
18749
18959
  import { fileURLToPath as fileURLToPath5 } from "url";
18750
18960
  var __filename3 = fileURLToPath5(import.meta.url);
18751
18961
  var __dirname4 = dirname25(__filename3);
@@ -18760,13 +18970,13 @@ var cachedReads;
18760
18970
  var cachedWrites;
18761
18971
  function getCliReadsLines() {
18762
18972
  if (!cachedReads) {
18763
- cachedReads = readLines(resolve14(packageRoot(), "allowed.cli-reads"));
18973
+ cachedReads = readLines(resolve15(packageRoot(), "allowed.cli-reads"));
18764
18974
  }
18765
18975
  return cachedReads;
18766
18976
  }
18767
18977
  function getCliWritesLines() {
18768
18978
  if (!cachedWrites) {
18769
- cachedWrites = readLines(resolve14(packageRoot(), "allowed.cli-writes"));
18979
+ cachedWrites = readLines(resolve15(packageRoot(), "allowed.cli-writes"));
18770
18980
  }
18771
18981
  return cachedWrites;
18772
18982
  }
@@ -18775,7 +18985,7 @@ function loadCliReads() {
18775
18985
  }
18776
18986
  function saveCliReads(commands) {
18777
18987
  writeFileSync22(
18778
- resolve14(packageRoot(), "allowed.cli-reads"),
18988
+ resolve15(packageRoot(), "allowed.cli-reads"),
18779
18989
  `${commands.join("\n")}
18780
18990
  `
18781
18991
  );
@@ -18801,13 +19011,13 @@ function findCliWrite(command) {
18801
19011
 
18802
19012
  // src/shared/readSettingsPerms.ts
18803
19013
  import { existsSync as existsSync42, readFileSync as readFileSync27 } from "fs";
18804
- import { homedir as homedir16 } from "os";
18805
- import { join as join43 } from "path";
19014
+ import { homedir as homedir17 } from "os";
19015
+ import { join as join45 } from "path";
18806
19016
  function readSettingsPerms(key) {
18807
19017
  const paths = [
18808
- join43(homedir16(), ".claude", "settings.json"),
18809
- join43(process.cwd(), ".claude", "settings.json"),
18810
- join43(process.cwd(), ".claude", "settings.local.json")
19018
+ join45(homedir17(), ".claude", "settings.json"),
19019
+ join45(process.cwd(), ".claude", "settings.json"),
19020
+ join45(process.cwd(), ".claude", "settings.local.json")
18811
19021
  ];
18812
19022
  const entries = [];
18813
19023
  for (const p of paths) {
@@ -18913,19 +19123,19 @@ function isCdToCwd(command) {
18913
19123
  const parts = command.split(/\s+/);
18914
19124
  if (parts[0] !== "cd" || parts.length > 2) return false;
18915
19125
  if (parts.length === 1) return false;
18916
- const resolved = resolve15(normalizeMsysPath(parts[1]));
18917
- return resolved === resolve15(process.cwd());
19126
+ const resolved = resolve16(normalizeMsysPath(parts[1]));
19127
+ return resolved === resolve16(process.cwd());
18918
19128
  }
18919
19129
  function isCdToReadAllowedDir(command) {
18920
19130
  const parts = command.split(/\s+/);
18921
19131
  if (parts[0] !== "cd" || parts.length !== 2) return void 0;
18922
- const target = resolve15(normalizeMsysPath(parts[1]));
19132
+ const target = resolve16(normalizeMsysPath(parts[1]));
18923
19133
  for (const entry of readSettingsPerms("allow")) {
18924
19134
  const m = entry.match(READ_RE);
18925
19135
  if (!m) continue;
18926
19136
  const base = globBaseDir(m[1]);
18927
19137
  if (!base) continue;
18928
- const resolved = resolve15(normalizeMsysPath(base));
19138
+ const resolved = resolve16(normalizeMsysPath(base));
18929
19139
  if (target === resolved || target.startsWith(resolved + sep2)) {
18930
19140
  return `cd to Read-allowed directory: ${entry}`;
18931
19141
  }
@@ -19030,12 +19240,12 @@ function decideCommand(toolName, rawCommand) {
19030
19240
 
19031
19241
  // src/commands/cliHook/logDeniedToolCall.ts
19032
19242
  import { mkdirSync as mkdirSync12 } from "fs";
19033
- import { homedir as homedir17 } from "os";
19034
- import { join as join44 } from "path";
19243
+ import { homedir as homedir18 } from "os";
19244
+ import { join as join46 } from "path";
19035
19245
  import Database from "better-sqlite3";
19036
19246
  var _db;
19037
19247
  function getDbDir() {
19038
- return join44(homedir17(), ".assist");
19248
+ return join46(homedir18(), ".assist");
19039
19249
  }
19040
19250
  function initSchema(db) {
19041
19251
  db.exec(`
@@ -19054,7 +19264,7 @@ function openPromptsDb(dir) {
19054
19264
  if (_db) return _db;
19055
19265
  const dbDir = dir ?? getDbDir();
19056
19266
  mkdirSync12(dbDir, { recursive: true });
19057
- const db = new Database(join44(dbDir, "assist.db"));
19267
+ const db = new Database(join46(dbDir, "assist.db"));
19058
19268
  db.pragma("journal_mode = WAL");
19059
19269
  initSchema(db);
19060
19270
  _db = db;
@@ -19125,7 +19335,7 @@ async function cliHook() {
19125
19335
  logDeniedToolCall({
19126
19336
  tool: input.toolName,
19127
19337
  command: input.command,
19128
- repo: basename12(process.cwd()),
19338
+ repo: basename13(process.cwd()),
19129
19339
  sessionId: process.env.CLAUDE_SESSION_ID,
19130
19340
  denyReason: decision.permissionDecisionReason
19131
19341
  });
@@ -19174,8 +19384,8 @@ ${reasons.join("\n")}`);
19174
19384
 
19175
19385
  // src/commands/permitCliReads.ts
19176
19386
  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";
19387
+ import { homedir as homedir19 } from "os";
19388
+ import { join as join47 } from "path";
19179
19389
 
19180
19390
  // src/commands/permitCliReads/assertCliExists.ts
19181
19391
  function assertCliExists(cli) {
@@ -19438,7 +19648,7 @@ function updateSettings(cli, commands) {
19438
19648
  // src/commands/permitCliReads.ts
19439
19649
  function logPath(cli) {
19440
19650
  const safeName = cli.replace(/\s+/g, "-");
19441
- return join45(homedir18(), ".assist", `cli-discover-${safeName}.log`);
19651
+ return join47(homedir19(), ".assist", `cli-discover-${safeName}.log`);
19442
19652
  }
19443
19653
  function readCache(cli) {
19444
19654
  const path106 = logPath(cli);
@@ -19446,7 +19656,7 @@ function readCache(cli) {
19446
19656
  return readFileSync28(path106, "utf8");
19447
19657
  }
19448
19658
  function writeCache(cli, output) {
19449
- const dir = join45(homedir18(), ".assist");
19659
+ const dir = join47(homedir19(), ".assist");
19450
19660
  mkdirSync13(dir, { recursive: true });
19451
19661
  writeFileSync23(logPath(cli), output);
19452
19662
  }
@@ -19610,18 +19820,18 @@ function isHashCommentFile(filePath) {
19610
19820
  }
19611
19821
 
19612
19822
  // src/commands/codeComment/getRestrictedDir.ts
19613
- import { homedir as homedir19 } from "os";
19614
- import { join as join46 } from "path";
19823
+ import { homedir as homedir20 } from "os";
19824
+ import { join as join48 } from "path";
19615
19825
  function getRestrictedDir() {
19616
- return join46(homedir19(), ".assist", "restricted");
19826
+ return join48(homedir20(), ".assist", "restricted");
19617
19827
  }
19618
19828
  function getPinStatePath(pin2) {
19619
- return join46(getRestrictedDir(), `code-comment-${pin2}.json`);
19829
+ return join48(getRestrictedDir(), `code-comment-${pin2}.json`);
19620
19830
  }
19621
19831
 
19622
19832
  // src/commands/codeComment/sweepRestrictedDir.ts
19623
19833
  import { readdirSync as readdirSync6, statSync as statSync8, unlinkSync as unlinkSync7 } from "fs";
19624
- import { join as join47 } from "path";
19834
+ import { join as join49 } from "path";
19625
19835
  var STALE_AFTER_MS = 30 * 60 * 1e3;
19626
19836
  function sweepRestrictedDir(dir = getRestrictedDir()) {
19627
19837
  let entries;
@@ -19632,7 +19842,7 @@ function sweepRestrictedDir(dir = getRestrictedDir()) {
19632
19842
  }
19633
19843
  const cutoff = Date.now() - STALE_AFTER_MS;
19634
19844
  for (const entry of entries) {
19635
- const path106 = join47(dir, entry);
19845
+ const path106 = join49(dir, entry);
19636
19846
  try {
19637
19847
  if (statSync8(path106).mtimeMs < cutoff) unlinkSync7(path106);
19638
19848
  } catch {
@@ -21286,18 +21496,18 @@ import chalk137 from "chalk";
21286
21496
 
21287
21497
  // src/commands/criteriaExtension/criteriaExtensionDir.ts
21288
21498
  import { existsSync as existsSync46 } from "fs";
21289
- import { dirname as dirname26, join as join48 } from "path";
21499
+ import { dirname as dirname26, join as join50 } from "path";
21290
21500
  import { fileURLToPath as fileURLToPath6 } from "url";
21291
21501
  var moduleDir = dirname26(fileURLToPath6(import.meta.url));
21292
21502
  function criteriaExtensionDir() {
21293
- const bundled = join48(
21503
+ const bundled = join50(
21294
21504
  moduleDir,
21295
21505
  "commands",
21296
21506
  "criteriaExtension",
21297
21507
  "criteria-extension"
21298
21508
  );
21299
21509
  if (existsSync46(bundled)) return bundled;
21300
- return join48(moduleDir, "..", "..", "..", "criteria-extension");
21510
+ return join50(moduleDir, "..", "..", "..", "criteria-extension");
21301
21511
  }
21302
21512
 
21303
21513
  // src/commands/criteriaExtension/criteriaExtension.ts
@@ -21325,24 +21535,24 @@ async function criteriaExtension() {
21325
21535
 
21326
21536
  // src/commands/criteriaExtension/printCriteriaExtensionUrl.ts
21327
21537
  import { readFile as readFile4 } from "fs/promises";
21328
- import { join as join49 } from "path";
21538
+ import { join as join51 } from "path";
21329
21539
  import chalk138 from "chalk";
21330
21540
 
21331
21541
  // src/commands/criteriaExtension/latestUpdate.ts
21332
- function latestUpdate(manifest, updates) {
21542
+ function latestUpdate(manifest, updates2) {
21333
21543
  const { id } = JSON.parse(manifest).browser_specific_settings.gecko;
21334
- return JSON.parse(updates).addons[id]?.updates.at(-1);
21544
+ return JSON.parse(updates2).addons[id]?.updates.at(-1);
21335
21545
  }
21336
21546
 
21337
21547
  // src/commands/criteriaExtension/latestUpdateLink.ts
21338
- function latestUpdateLink(manifest, updates) {
21339
- return latestUpdate(manifest, updates)?.update_link;
21548
+ function latestUpdateLink(manifest, updates2) {
21549
+ return latestUpdate(manifest, updates2)?.update_link;
21340
21550
  }
21341
21551
 
21342
21552
  // src/commands/criteriaExtension/printCriteriaExtensionUrl.ts
21343
21553
  async function printCriteriaExtensionUrl() {
21344
21554
  const manifest = await readFile4(
21345
- join49(criteriaExtensionDir(), "manifest.json"),
21555
+ join51(criteriaExtensionDir(), "manifest.json"),
21346
21556
  "utf8"
21347
21557
  );
21348
21558
  const { update_url } = JSON.parse(manifest).browser_specific_settings.gecko;
@@ -21358,16 +21568,16 @@ async function printCriteriaExtensionUrl() {
21358
21568
 
21359
21569
  // src/commands/criteriaExtension/signCriteriaExtension.ts
21360
21570
  import { rm as rm2 } from "fs/promises";
21361
- import { homedir as homedir20, tmpdir as tmpdir3 } from "os";
21362
- import { join as join55 } from "path";
21571
+ import { homedir as homedir21, tmpdir as tmpdir3 } from "os";
21572
+ import { join as join57 } from "path";
21363
21573
  import chalk140 from "chalk";
21364
21574
 
21365
21575
  // src/commands/criteriaExtension/copySignedXpi.ts
21366
21576
  import { copyFile } from "fs/promises";
21367
- import { join as join50 } from "path";
21577
+ import { join as join52 } from "path";
21368
21578
  var SIGNED_XPI_NAME = "criteria-extension.xpi";
21369
21579
  async function copySignedXpi(xpi, dir) {
21370
- const fixed2 = join50(dir, SIGNED_XPI_NAME);
21580
+ const fixed2 = join52(dir, SIGNED_XPI_NAME);
21371
21581
  if (fixed2 === xpi) return fixed2;
21372
21582
  await copyFile(xpi, fixed2);
21373
21583
  return fixed2;
@@ -21376,11 +21586,11 @@ async function copySignedXpi(xpi, dir) {
21376
21586
  // src/commands/criteriaExtension/findSignedXpi.ts
21377
21587
  import { existsSync as existsSync47 } from "fs";
21378
21588
  import { readdir } from "fs/promises";
21379
- import { join as join51 } from "path";
21589
+ import { join as join53 } from "path";
21380
21590
  async function findSignedXpi(dir) {
21381
21591
  if (!existsSync47(dir)) return null;
21382
21592
  const name = (await readdir(dir)).find((entry) => entry.endsWith(".xpi"));
21383
- return name ? join51(dir, name) : null;
21593
+ return name ? join53(dir, name) : null;
21384
21594
  }
21385
21595
 
21386
21596
  // src/commands/criteriaExtension/signedAddonInstallPath.ts
@@ -21403,10 +21613,10 @@ async function signedAddonInstallPath(xpi) {
21403
21613
 
21404
21614
  // src/commands/criteriaExtension/signPreflightProblem.ts
21405
21615
  import { existsSync as existsSync48 } from "fs";
21406
- import { join as join52 } from "path";
21616
+ import { join as join54 } from "path";
21407
21617
  var KEY_URL = "https://addons.mozilla.org/en-US/developers/addon/api/key/";
21408
21618
  function signPreflightProblem(source) {
21409
- if (!existsSync48(join52(source, "content.js")))
21619
+ if (!existsSync48(join54(source, "content.js")))
21410
21620
  return {
21411
21621
  message: `no content.js in ${source}`,
21412
21622
  hint: "run npm run build to bundle the content script"
@@ -21421,7 +21631,7 @@ function signPreflightProblem(source) {
21421
21631
 
21422
21632
  // src/commands/criteriaExtension/stageCriteriaExtension.ts
21423
21633
  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";
21634
+ import { basename as basename14, join as join55 } from "path";
21425
21635
 
21426
21636
  // src/commands/criteriaExtension/stampManifestVersion.ts
21427
21637
  function stampManifestVersion(manifest, version2) {
@@ -21436,9 +21646,9 @@ async function stageCriteriaExtension(source, staging, version2) {
21436
21646
  await mkdir5(staging, { recursive: true });
21437
21647
  await cp3(source, staging, {
21438
21648
  recursive: true,
21439
- filter: (path106) => basename13(path106) !== "updates.json"
21649
+ filter: (path106) => basename14(path106) !== "updates.json"
21440
21650
  });
21441
- const manifest = join53(staging, "manifest.json");
21651
+ const manifest = join55(staging, "manifest.json");
21442
21652
  const stamped = stampManifestVersion(
21443
21653
  await readFile5(manifest, "utf8"),
21444
21654
  version2
@@ -21448,13 +21658,13 @@ async function stageCriteriaExtension(source, staging, version2) {
21448
21658
 
21449
21659
  // src/commands/criteriaExtension/writeUpdateManifest.ts
21450
21660
  import { readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
21451
- import { join as join54 } from "path";
21661
+ import { join as join56 } from "path";
21452
21662
 
21453
21663
  // src/commands/criteriaExtension/buildUpdateManifest.ts
21454
21664
  var RELEASE_DOWNLOAD_URL = "https://github.com/staff0rd/assist/releases/download";
21455
21665
  function buildUpdateManifest(manifest, version2) {
21456
21666
  const { id } = JSON.parse(manifest).browser_specific_settings.gecko;
21457
- const updates = {
21667
+ const updates2 = {
21458
21668
  addons: {
21459
21669
  [id]: {
21460
21670
  updates: [
@@ -21466,24 +21676,24 @@ function buildUpdateManifest(manifest, version2) {
21466
21676
  }
21467
21677
  }
21468
21678
  };
21469
- return `${JSON.stringify(updates, null, " ")}
21679
+ return `${JSON.stringify(updates2, null, " ")}
21470
21680
  `;
21471
21681
  }
21472
21682
 
21473
21683
  // src/commands/criteriaExtension/writeUpdateManifest.ts
21474
21684
  async function writeUpdateManifest(source, dir, version2) {
21475
21685
  await writeFile5(
21476
- join54(dir, "updates.json"),
21686
+ join56(dir, "updates.json"),
21477
21687
  buildUpdateManifest(
21478
- await readFile6(join54(source, "manifest.json"), "utf8"),
21688
+ await readFile6(join56(source, "manifest.json"), "utf8"),
21479
21689
  version2
21480
21690
  )
21481
21691
  );
21482
21692
  }
21483
21693
 
21484
21694
  // src/commands/criteriaExtension/signCriteriaExtension.ts
21485
- var ARTIFACTS_DIR = join55(homedir20(), ".assist", "criteria-extension");
21486
- var STAGING_DIR = join55(tmpdir3(), "assist-criteria-extension");
21695
+ var ARTIFACTS_DIR = join57(homedir21(), ".assist", "criteria-extension");
21696
+ var STAGING_DIR = join57(tmpdir3(), "assist-criteria-extension");
21487
21697
  async function signCriteriaExtension() {
21488
21698
  const source = criteriaExtensionDir();
21489
21699
  const problem = signPreflightProblem(source);
@@ -21671,12 +21881,12 @@ import { unlinkSync as unlinkSync9, writeFileSync as writeFileSync26 } from "fs"
21671
21881
  import chalk143 from "chalk";
21672
21882
 
21673
21883
  // src/commands/dbMigration/getMigrationPinPath.ts
21674
- import { join as join56 } from "path";
21884
+ import { join as join58 } from "path";
21675
21885
  function getMigrationPinPath(pin2) {
21676
- return join56(getRestrictedDir(), `db-migration-pin-${pin2}.json`);
21886
+ return join58(getRestrictedDir(), `db-migration-pin-${pin2}.json`);
21677
21887
  }
21678
21888
  function getMigrationApprovalPath(migrationId) {
21679
- return join56(getRestrictedDir(), `db-migration-approval-${migrationId}.json`);
21889
+ return join58(getRestrictedDir(), `db-migration-approval-${migrationId}.json`);
21680
21890
  }
21681
21891
 
21682
21892
  // src/commands/dbMigration/readMigrationPinState.ts
@@ -21809,15 +22019,15 @@ function registerDeploy(program2) {
21809
22019
  }
21810
22020
 
21811
22021
  // src/commands/devlog/list.ts
21812
- import { execFileSync as execFileSync5 } from "child_process";
21813
- import { basename as basename15 } from "path";
22022
+ import { execFileSync as execFileSync6 } from "child_process";
22023
+ import { basename as basename16 } from "path";
21814
22024
 
21815
22025
  // 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");
22026
+ import { homedir as homedir22 } from "os";
22027
+ import { join as join59 } from "path";
22028
+ var BLOG_REPO_ROOT = join59(homedir22(), "git/blog");
21819
22029
  function loadBlogSkipDays(repoName) {
21820
- const config = loadRawYaml(join57(BLOG_REPO_ROOT, "assist.yml"));
22030
+ const config = loadRawYaml(join59(BLOG_REPO_ROOT, "assist.yml"));
21821
22031
  const devlog = config.devlog;
21822
22032
  const skip2 = devlog?.skip;
21823
22033
  return new Set(skip2?.[repoName]);
@@ -21829,13 +22039,13 @@ import chalk146 from "chalk";
21829
22039
 
21830
22040
  // src/shared/getRepoName.ts
21831
22041
  import { existsSync as existsSync51, readFileSync as readFileSync33 } from "fs";
21832
- import { basename as basename14, join as join58 } from "path";
22042
+ import { basename as basename15, join as join60 } from "path";
21833
22043
  function getRepoName() {
21834
22044
  const config = loadConfig();
21835
22045
  if (config.devlog?.name) {
21836
22046
  return config.devlog.name;
21837
22047
  }
21838
- const packageJsonPath = join58(process.cwd(), "package.json");
22048
+ const packageJsonPath = join60(process.cwd(), "package.json");
21839
22049
  if (existsSync51(packageJsonPath)) {
21840
22050
  try {
21841
22051
  const content = readFileSync33(packageJsonPath, "utf8");
@@ -21846,13 +22056,13 @@ function getRepoName() {
21846
22056
  } catch {
21847
22057
  }
21848
22058
  }
21849
- return basename14(process.cwd());
22059
+ return basename15(process.cwd());
21850
22060
  }
21851
22061
 
21852
22062
  // src/commands/devlog/loadDevlogEntries.ts
21853
22063
  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");
22064
+ import { join as join61 } from "path";
22065
+ var DEVLOG_DIR = join61(BLOG_REPO_ROOT, "src/content/devlog");
21856
22066
  function extractFrontmatter(content) {
21857
22067
  const fm = content.match(/^---\n([\s\S]*?)\n---/);
21858
22068
  return fm?.[1] ?? null;
@@ -21880,7 +22090,7 @@ function readDevlogFiles(callback) {
21880
22090
  try {
21881
22091
  const files = readdirSync7(DEVLOG_DIR).filter((f) => f.endsWith(".md"));
21882
22092
  for (const file of files) {
21883
- const content = readFileSync34(join59(DEVLOG_DIR, file), "utf8");
22093
+ const content = readFileSync34(join61(DEVLOG_DIR, file), "utf8");
21884
22094
  const parsed = parseFrontmatter(content, file);
21885
22095
  if (parsed) callback(parsed);
21886
22096
  }
@@ -21982,14 +22192,14 @@ function list2(options2) {
21982
22192
  const config = loadConfig();
21983
22193
  const days = options2.days ?? 30;
21984
22194
  const ignore3 = options2.ignore ?? config.devlog?.ignore ?? [];
21985
- const repoName = basename15(process.cwd());
22195
+ const repoName = basename16(process.cwd());
21986
22196
  const skipDays = loadBlogSkipDays(repoName);
21987
22197
  const devlogEntries = loadDevlogEntries(repoName);
21988
22198
  const args = ["log"];
21989
22199
  if (options2.reverse) args.push("--reverse");
21990
22200
  else args.push("-n", "500");
21991
22201
  args.push("--pretty=format:%ad|%h|%s", "--date=short");
21992
- const output = execFileSync5("git", args, { encoding: "utf8" });
22202
+ const output = execFileSync6("git", args, { encoding: "utf8" });
21993
22203
  const commitsByDate = parseGitLogCommits(output, ignore3);
21994
22204
  let dateCount = 0;
21995
22205
  let isFirst = true;
@@ -22012,7 +22222,7 @@ function list2(options2) {
22012
22222
  }
22013
22223
 
22014
22224
  // src/commands/devlog/getLastVersionInfo.ts
22015
- import { execFileSync as execFileSync6, execSync as execSync32 } from "child_process";
22225
+ import { execFileSync as execFileSync7, execSync as execSync32 } from "child_process";
22016
22226
  import semver from "semver";
22017
22227
  function getVersionAtCommit(hash) {
22018
22228
  try {
@@ -22031,7 +22241,7 @@ function stripToMinor(version2) {
22031
22241
  }
22032
22242
  function getLastVersionInfoFromGit() {
22033
22243
  try {
22034
- const output = execFileSync6(
22244
+ const output = execFileSync7(
22035
22245
  "git",
22036
22246
  ["log", "-1", "--pretty=format:%ad|%h", "--date=short"],
22037
22247
  {
@@ -22076,7 +22286,7 @@ function bumpVersion(version2, type) {
22076
22286
  }
22077
22287
 
22078
22288
  // src/commands/devlog/next/displayNextEntry.ts
22079
- import { execFileSync as execFileSync7 } from "child_process";
22289
+ import { execFileSync as execFileSync8 } from "child_process";
22080
22290
  import chalk149 from "chalk";
22081
22291
 
22082
22292
  // src/commands/devlog/next/displayNextEntry/displayVersion.ts
@@ -22110,7 +22320,7 @@ function findTargetDate(commitsByDate, skipDays) {
22110
22320
  return Array.from(commitsByDate.keys()).filter((d) => !skipDays.has(d)).sort()[0];
22111
22321
  }
22112
22322
  function fetchCommitsByDate(ignore3, lastDate) {
22113
- const output = execFileSync7(
22323
+ const output = execFileSync8(
22114
22324
  "git",
22115
22325
  ["log", "--pretty=format:%ad|%h|%s", "--date=short", "-n", "500"],
22116
22326
  { encoding: "utf8" }
@@ -22268,11 +22478,11 @@ function repos(options2) {
22268
22478
 
22269
22479
  // src/commands/devlog/skip.ts
22270
22480
  import { writeFileSync as writeFileSync29 } from "fs";
22271
- import { join as join60 } from "path";
22481
+ import { join as join62 } from "path";
22272
22482
  import chalk151 from "chalk";
22273
22483
  import { stringify as stringifyYaml3 } from "yaml";
22274
22484
  function getBlogConfigPath() {
22275
- return join60(BLOG_REPO_ROOT, "assist.yml");
22485
+ return join62(BLOG_REPO_ROOT, "assist.yml");
22276
22486
  }
22277
22487
  function skip(date) {
22278
22488
  if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) {
@@ -22334,7 +22544,7 @@ function registerDevlog(program2) {
22334
22544
 
22335
22545
  // src/commands/dotnet/checkBuildLocks.ts
22336
22546
  import { closeSync as closeSync4, openSync as openSync5, readdirSync as readdirSync8 } from "fs";
22337
- import { join as join61 } from "path";
22547
+ import { join as join63 } from "path";
22338
22548
  import chalk153 from "chalk";
22339
22549
  var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "packages"]);
22340
22550
  function isLockedDll(debugDir) {
@@ -22346,7 +22556,7 @@ function isLockedDll(debugDir) {
22346
22556
  }
22347
22557
  for (const file of files) {
22348
22558
  if (!file.toLowerCase().endsWith(".dll")) continue;
22349
- const dllPath = join61(debugDir, file);
22559
+ const dllPath = join63(debugDir, file);
22350
22560
  try {
22351
22561
  const fd = openSync5(dllPath, "r+");
22352
22562
  closeSync4(fd);
@@ -22364,13 +22574,13 @@ function findFirstLockedDll(dir) {
22364
22574
  return null;
22365
22575
  }
22366
22576
  if (entries.includes("bin")) {
22367
- const locked = isLockedDll(join61(dir, "bin", "Debug"));
22577
+ const locked = isLockedDll(join63(dir, "bin", "Debug"));
22368
22578
  if (locked) return locked;
22369
22579
  }
22370
22580
  for (const entry of entries) {
22371
22581
  if (SKIP_DIRS.has(entry) || entry === "bin" || entry.startsWith("."))
22372
22582
  continue;
22373
- const found = findFirstLockedDll(join61(dir, entry));
22583
+ const found = findFirstLockedDll(join63(dir, entry));
22374
22584
  if (found) return found;
22375
22585
  }
22376
22586
  return null;
@@ -22717,11 +22927,11 @@ import chalk159 from "chalk";
22717
22927
 
22718
22928
  // src/commands/dotnet/findSolution.ts
22719
22929
  import { readdirSync as readdirSync10 } from "fs";
22720
- import { dirname as dirname27, join as join62 } from "path";
22930
+ import { dirname as dirname27, join as join64 } from "path";
22721
22931
  import chalk158 from "chalk";
22722
22932
  function findSlnInDir(dir) {
22723
22933
  try {
22724
- return readdirSync10(dir).filter((f) => f.endsWith(".sln")).map((f) => join62(dir, f));
22934
+ return readdirSync10(dir).filter((f) => f.endsWith(".sln")).map((f) => join64(dir, f));
22725
22935
  } catch {
22726
22936
  return [];
22727
22937
  }
@@ -23716,7 +23926,7 @@ function aggregateCommitters(authorLists) {
23716
23926
  import { spawnSync as spawnSync6 } from "child_process";
23717
23927
  import { unlinkSync as unlinkSync12, writeFileSync as writeFileSync30 } from "fs";
23718
23928
  import { tmpdir as tmpdir5 } from "os";
23719
- import { join as join63 } from "path";
23929
+ import { join as join65 } from "path";
23720
23930
  function buildArgs2(queryFile, vars) {
23721
23931
  const args = ["api", "graphql", "-F", `query=@${queryFile}`];
23722
23932
  for (const [key, value] of Object.entries(vars)) {
@@ -23726,7 +23936,7 @@ function buildArgs2(queryFile, vars) {
23726
23936
  return args;
23727
23937
  }
23728
23938
  function runGhGraphql(mutation, vars) {
23729
- const queryFile = join63(tmpdir5(), `gh-query-${Date.now()}.graphql`);
23939
+ const queryFile = join65(tmpdir5(), `gh-query-${Date.now()}.graphql`);
23730
23940
  writeFileSync30(queryFile, mutation);
23731
23941
  try {
23732
23942
  const result = spawnSync6("gh", buildArgs2(queryFile, vars), {
@@ -24439,7 +24649,7 @@ Anything nested below the leaf level fails the run before a single write, naming
24439
24649
  }
24440
24650
 
24441
24651
  // src/commands/github/issue/commentIssue.ts
24442
- import { execFileSync as execFileSync8 } from "child_process";
24652
+ import { execFileSync as execFileSync9 } from "child_process";
24443
24653
 
24444
24654
  // src/shared/validateProposedContent.ts
24445
24655
  function validateProposedContent(labels, title, body) {
@@ -24500,7 +24710,7 @@ async function commentIssue(numberArg, options2) {
24500
24710
  const args = ["issue", "comment", String(number), "--body", body];
24501
24711
  if (options2.repo) args.push("--repo", options2.repo);
24502
24712
  try {
24503
- execFileSync8("gh", args, { stdio: "inherit" });
24713
+ execFileSync9("gh", args, { stdio: "inherit" });
24504
24714
  } catch {
24505
24715
  process.exit(1);
24506
24716
  }
@@ -24903,8 +25113,8 @@ function attachArgs(attachments) {
24903
25113
  }
24904
25114
 
24905
25115
  // src/commands/prs/runGhWithAttachments.ts
24906
- import { execFileSync as execFileSync9 } from "child_process";
24907
- import { basename as basename16 } from "path";
25116
+ import { execFileSync as execFileSync10 } from "child_process";
25117
+ import { basename as basename17 } from "path";
24908
25118
 
24909
25119
  // src/shared/removeStagedAttachments.ts
24910
25120
  import { rmSync as rmSync4 } from "fs";
@@ -24921,7 +25131,7 @@ function removeStagedAttachments(attachments) {
24921
25131
  var PLACED_URL = /https:\/\/\S+\/(pull|issues)\/\d+/;
24922
25132
  function reportPartialFailure(attachments, stderr) {
24923
25133
  const failed3 = attachments.filter(
24924
- ({ path: path106 }) => stderr.includes(path106) || stderr.includes(basename16(path106))
25134
+ ({ path: path106 }) => stderr.includes(path106) || stderr.includes(basename17(path106))
24925
25135
  );
24926
25136
  const named = failed3.length > 0 ? failed3 : attachments;
24927
25137
  console.error(
@@ -24931,7 +25141,7 @@ function reportPartialFailure(attachments, stderr) {
24931
25141
  function runGhWithAttachments(args, attachments) {
24932
25142
  const attaching = attachments.length > 0;
24933
25143
  try {
24934
- const stdout = execFileSync9("gh", args, {
25144
+ const stdout = execFileSync10("gh", args, {
24935
25145
  encoding: "utf8",
24936
25146
  stdio: ["inherit", "pipe", attaching ? "pipe" : "inherit"]
24937
25147
  });
@@ -25042,7 +25252,7 @@ function inWebSession() {
25042
25252
  }
25043
25253
 
25044
25254
  // src/commands/github/issue/fetchIssue.ts
25045
- import { execFileSync as execFileSync10 } from "child_process";
25255
+ import { execFileSync as execFileSync11 } from "child_process";
25046
25256
  function fetchIssue2(number, repo) {
25047
25257
  const args = [
25048
25258
  "issue",
@@ -25054,7 +25264,7 @@ function fetchIssue2(number, repo) {
25054
25264
  if (repo) args.push("--repo", repo);
25055
25265
  let raw;
25056
25266
  try {
25057
- raw = execFileSync10("gh", args, { encoding: "utf8" });
25267
+ raw = execFileSync11("gh", args, { encoding: "utf8" });
25058
25268
  } catch {
25059
25269
  console.error(`Could not fetch issue #${number} with gh issue view`);
25060
25270
  process.exit(1);
@@ -25071,14 +25281,14 @@ function fetchIssue2(number, repo) {
25071
25281
  import { existsSync as existsSync56, readFileSync as readFileSync38 } from "fs";
25072
25282
 
25073
25283
  // src/commands/github/issue/issueWorkingFile.ts
25074
- import { join as join64 } from "path";
25284
+ import { join as join66 } from "path";
25075
25285
  function issueWorkingFile(slug2, number) {
25076
25286
  const [owner = "unknown", repo = "unknown"] = slug2.split("/");
25077
- const dir = join64(getStoreDir(), "github-issues", owner, repo);
25287
+ const dir = join66(getStoreDir(), "github-issues", owner, repo);
25078
25288
  return {
25079
25289
  dir,
25080
- bodyPath: join64(dir, `${number}.md`),
25081
- metaPath: join64(dir, `${number}.json`)
25290
+ bodyPath: join66(dir, `${number}.md`),
25291
+ metaPath: join66(dir, `${number}.json`)
25082
25292
  };
25083
25293
  }
25084
25294
 
@@ -25145,12 +25355,12 @@ function prepareIssueEdit(number, repo, fresh) {
25145
25355
  }
25146
25356
 
25147
25357
  // src/commands/github/issue/pushIssueBody.ts
25148
- import { execFileSync as execFileSync11 } from "child_process";
25358
+ import { execFileSync as execFileSync12 } from "child_process";
25149
25359
  function pushIssueBody(number, repo, bodyPath) {
25150
25360
  const args = ["issue", "edit", String(number), "--body-file", bodyPath];
25151
25361
  if (repo) args.push("--repo", repo);
25152
25362
  try {
25153
- execFileSync11("gh", args, { stdio: "inherit" });
25363
+ execFileSync12("gh", args, { stdio: "inherit" });
25154
25364
  } catch {
25155
25365
  process.exit(1);
25156
25366
  }
@@ -25210,12 +25420,12 @@ function setIssueParent(number, repo, parentArg) {
25210
25420
  }
25211
25421
 
25212
25422
  // src/commands/github/issue/viewIssue.ts
25213
- import { execFileSync as execFileSync12 } from "child_process";
25423
+ import { execFileSync as execFileSync13 } from "child_process";
25214
25424
  function viewIssue(number, repo) {
25215
25425
  const args = ["issue", "view", String(number)];
25216
25426
  if (repo) args.push("--repo", repo);
25217
25427
  try {
25218
- execFileSync12("gh", args, { stdio: "inherit" });
25428
+ execFileSync13("gh", args, { stdio: "inherit" });
25219
25429
  } catch {
25220
25430
  process.exit(1);
25221
25431
  }
@@ -25271,7 +25481,7 @@ function registerEditIssue(issueCommand) {
25271
25481
  }
25272
25482
 
25273
25483
  // src/commands/github/issue/editIssueComment.ts
25274
- import { execFileSync as execFileSync13 } from "child_process";
25484
+ import { execFileSync as execFileSync14 } from "child_process";
25275
25485
  var USAGE4 = "Usage: assist github issue edit-comment <comment-id> --body <body> [-R <owner>/<repo>]";
25276
25486
  async function editIssueComment(commentIdArg, options2) {
25277
25487
  const commentId = Number.parseInt(commentIdArg, 10);
@@ -25298,7 +25508,7 @@ async function editIssueComment(commentIdArg, options2) {
25298
25508
  ];
25299
25509
  let raw;
25300
25510
  try {
25301
- raw = execFileSync13("gh", args, {
25511
+ raw = execFileSync14("gh", args, {
25302
25512
  input: JSON.stringify({ body }),
25303
25513
  encoding: "utf8"
25304
25514
  });
@@ -25679,7 +25889,7 @@ function registerRefineLaunch(program2, resumeFlag) {
25679
25889
  import { randomUUID as randomUUID12 } from "crypto";
25680
25890
 
25681
25891
  // src/commands/review/checkoutPr.ts
25682
- import { execFileSync as execFileSync15 } from "child_process";
25892
+ import { execFileSync as execFileSync16 } from "child_process";
25683
25893
  import chalk170 from "chalk";
25684
25894
 
25685
25895
  // src/commands/sessions/daemon/daemonLog.ts
@@ -25705,30 +25915,19 @@ function recentDaemonLogLines(includeRelayed = true) {
25705
25915
  return ring.filter((entry) => includeRelayed || !entry.relayed).map((entry) => entry.line);
25706
25916
  }
25707
25917
 
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
25918
  // src/commands/sessions/daemon/worktree/createWorktree.ts
25720
25919
  import { existsSync as existsSync57 } from "fs";
25721
- import { basename as basename18, dirname as dirname29 } from "path";
25920
+ import { basename as basename19, dirname as dirname29 } from "path";
25722
25921
 
25723
25922
  // src/commands/sessions/daemon/worktree/planAllocation.ts
25724
- import { basename as basename17, join as join65 } from "path";
25923
+ import { basename as basename18, join as join67 } from "path";
25725
25924
  function planAllocation(clone, boundTreeRoots2) {
25726
25925
  return boundTreeRoots2.has(clone) ? "spill" : "primary";
25727
25926
  }
25728
25927
  function nextWorktreePath(clone, base, isTaken) {
25729
- const name = basename17(clone);
25928
+ const name = basename18(clone);
25730
25929
  for (let n = 2; n < 1e3; n++) {
25731
- const candidate = join65(base, `${name}-${n}`);
25930
+ const candidate = join67(base, `${name}-${n}`);
25732
25931
  if (!isTaken(candidate)) return candidate;
25733
25932
  }
25734
25933
  throw new Error(`no free worktree suffix for ${clone}`);
@@ -25764,7 +25963,7 @@ function createWorktree(clone, strategy, boundTreeRoots2, preferredPath) {
25764
25963
  const base = strategy.root ? expandTilde2(strategy.root) : dirname29(clone);
25765
25964
  const registered = new Set(listWorktreePaths(clone));
25766
25965
  const branches = new Set(listLocalBranches(clone));
25767
- const isTaken = (candidate) => registered.has(candidate) || existsSync57(candidate) || boundTreeRoots2.has(candidate) || branches.has(basename18(candidate));
25966
+ const isTaken = (candidate) => registered.has(candidate) || existsSync57(candidate) || boundTreeRoots2.has(candidate) || branches.has(basename19(candidate));
25768
25967
  const path106 = preferredPath && !isTaken(preferredPath) ? preferredPath : nextWorktreePath(clone, base, isTaken);
25769
25968
  const start2 = worktreeStartPoint(clone, strategy.trunk);
25770
25969
  gitSync(clone, [
@@ -25772,13 +25971,13 @@ function createWorktree(clone, strategy, boundTreeRoots2, preferredPath) {
25772
25971
  "add",
25773
25972
  start2.track ? "--track" : "--no-track",
25774
25973
  "-b",
25775
- basename18(path106),
25974
+ basename19(path106),
25776
25975
  path106,
25777
25976
  start2.ref
25778
25977
  ]);
25779
25978
  recordWorktree(path106, clone, getCurrentOrigin(clone));
25780
25979
  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}`
25980
+ 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
25981
  );
25783
25982
  return path106;
25784
25983
  }
@@ -26080,19 +26279,19 @@ function persistedTreeRoots() {
26080
26279
 
26081
26280
  // src/commands/sessions/daemon/worktree/seedWorktree.ts
26082
26281
  import { copyFileSync, existsSync as existsSync60, mkdirSync as mkdirSync17 } from "fs";
26083
- import { dirname as dirname30, join as join67 } from "path";
26282
+ import { dirname as dirname30, join as join69 } from "path";
26084
26283
 
26085
26284
  // src/commands/sessions/daemon/worktree/runInstall.ts
26086
26285
  import { resolve as resolve17 } from "path";
26087
26286
 
26088
26287
  // src/commands/sessions/daemon/worktree/detectInstallCommand.ts
26089
26288
  import { existsSync as existsSync59 } from "fs";
26090
- import { join as join66 } from "path";
26289
+ import { join as join68 } from "path";
26091
26290
  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";
26291
+ if (!existsSync59(join68(dir, "package.json"))) return null;
26292
+ if (existsSync59(join68(dir, "pnpm-lock.yaml"))) return "pnpm install";
26293
+ if (existsSync59(join68(dir, "yarn.lock"))) return "yarn install";
26294
+ if (existsSync59(join68(dir, "bun.lockb"))) return "bun install";
26096
26295
  return "npm install";
26097
26296
  }
26098
26297
 
@@ -26254,9 +26453,9 @@ function seedWorktree(worktreePath, clone, onSeeded = () => {
26254
26453
  }
26255
26454
  function copyConfigFiles(worktreePath, clone, copy) {
26256
26455
  for (const rel of copy) {
26257
- const src = join67(clone, rel);
26456
+ const src = join69(clone, rel);
26258
26457
  if (!existsSync60(src)) continue;
26259
- const dest = join67(worktreePath, rel);
26458
+ const dest = join69(worktreePath, rel);
26260
26459
  try {
26261
26460
  mkdirSync17(dirname30(dest), { recursive: true });
26262
26461
  copyFileSync(src, dest);
@@ -26297,10 +26496,10 @@ async function moveToPrCheckoutTree() {
26297
26496
  }
26298
26497
 
26299
26498
  // src/commands/review/prHeadBranch.ts
26300
- import { execFileSync as execFileSync14 } from "child_process";
26499
+ import { execFileSync as execFileSync15 } from "child_process";
26301
26500
  function prHeadBranch(number) {
26302
26501
  try {
26303
- const out = execFileSync14(
26502
+ const out = execFileSync15(
26304
26503
  "gh",
26305
26504
  ["pr", "view", number, "--json", "headRefName", "-q", ".headRefName"],
26306
26505
  { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }
@@ -26362,7 +26561,7 @@ async function checkoutPr(number) {
26362
26561
  if (headRef && await moveToExistingCheckout(number, headRef)) return;
26363
26562
  await moveToPrCheckoutTree();
26364
26563
  try {
26365
- execFileSync15("gh", ["pr", "checkout", number], { stdio: "inherit" });
26564
+ execFileSync16("gh", ["pr", "checkout", number], { stdio: "inherit" });
26366
26565
  } catch {
26367
26566
  console.error(chalk170.red(`gh pr checkout ${number} failed; aborting.`));
26368
26567
  process.exit(1);
@@ -26566,7 +26765,7 @@ import chalk175 from "chalk";
26566
26765
 
26567
26766
  // src/commands/mermaid/exportFile.ts
26568
26767
  import { readFileSync as readFileSync39, writeFileSync as writeFileSync32 } from "fs";
26569
- import { basename as basename19, extname as extname3, resolve as resolve18 } from "path";
26768
+ import { basename as basename20, extname as extname3, resolve as resolve18 } from "path";
26570
26769
  import chalk174 from "chalk";
26571
26770
 
26572
26771
  // src/commands/mermaid/renderBlock.ts
@@ -26593,7 +26792,7 @@ async function renderBlock(krokiUrl, source) {
26593
26792
  async function exportFile(file, outDir, krokiUrl, onlyIndex) {
26594
26793
  const content = readFileSync39(file, "utf8");
26595
26794
  const blocks = extractMermaidBlocks(content);
26596
- const stem = basename19(file, extname3(file));
26795
+ const stem = basename20(file, extname3(file));
26597
26796
  if (onlyIndex !== void 0) {
26598
26797
  if (onlyIndex < 1 || onlyIndex > blocks.length) {
26599
26798
  console.error(
@@ -27290,15 +27489,15 @@ function createNetcapHandler(options2) {
27290
27489
  // src/commands/netcap/prepareExtensionForLoad.ts
27291
27490
  import { cp as cp4, readFile as readFile7, writeFile as writeFile6 } from "fs/promises";
27292
27491
  import { networkInterfaces } from "os";
27293
- import { join as join69 } from "path";
27492
+ import { join as join71 } from "path";
27294
27493
  import chalk178 from "chalk";
27295
27494
 
27296
27495
  // src/commands/netcap/netcapExtensionDir.ts
27297
- import { dirname as dirname32, join as join68 } from "path";
27496
+ import { dirname as dirname32, join as join70 } from "path";
27298
27497
  import { fileURLToPath as fileURLToPath7 } from "url";
27299
27498
  var moduleDir2 = dirname32(fileURLToPath7(import.meta.url));
27300
27499
  function netcapExtensionDir() {
27301
- return join68(moduleDir2, "commands", "netcap", "netcap-extension");
27500
+ return join70(moduleDir2, "commands", "netcap", "netcap-extension");
27302
27501
  }
27303
27502
 
27304
27503
  // src/commands/netcap/prepareExtensionForLoad.ts
@@ -27313,7 +27512,7 @@ function lanIPv4() {
27313
27512
  return void 0;
27314
27513
  }
27315
27514
  async function configureBackground(dir, host, port, filter) {
27316
- const file = join69(dir, "background.js");
27515
+ const file = join71(dir, "background.js");
27317
27516
  const source = await readFile7(file, "utf8");
27318
27517
  await writeFile6(
27319
27518
  file,
@@ -27353,20 +27552,20 @@ async function prepareExtensionForLoad(port, filter = "") {
27353
27552
  }
27354
27553
 
27355
27554
  // src/commands/netcap/resolveNetcapOutPath.ts
27356
- import { isAbsolute as isAbsolute5, join as join71, resolve as resolve21 } from "path";
27555
+ import { isAbsolute as isAbsolute5, join as join73, resolve as resolve21 } from "path";
27357
27556
 
27358
27557
  // src/commands/netcap/defaultCapturePath.ts
27359
- import { homedir as homedir22 } from "os";
27360
- import { join as join70 } from "path";
27558
+ import { homedir as homedir23 } from "os";
27559
+ import { join as join72 } from "path";
27361
27560
  function defaultCapturePath() {
27362
- return join70(homedir22(), ".assist", "netcap", "capture.jsonl");
27561
+ return join72(homedir23(), ".assist", "netcap", "capture.jsonl");
27363
27562
  }
27364
27563
 
27365
27564
  // src/commands/netcap/resolveNetcapOutPath.ts
27366
27565
  function resolveNetcapOutPath(out) {
27367
27566
  if (!out) return defaultCapturePath();
27368
27567
  const dir = isAbsolute5(out) ? out : resolve21(process.cwd(), out);
27369
- return join71(dir, "capture.jsonl");
27568
+ return join73(dir, "capture.jsonl");
27370
27569
  }
27371
27570
 
27372
27571
  // src/commands/netcap/netcap.ts
@@ -27413,7 +27612,7 @@ netcap stopped \u2014 captured ${count8} ${count8 === 1 ? "entry" : "entries"} t
27413
27612
 
27414
27613
  // src/commands/netcap/netcapExtract.ts
27415
27614
  import { writeFileSync as writeFileSync34 } from "fs";
27416
- import { join as join72 } from "path";
27615
+ import { join as join74 } from "path";
27417
27616
  import chalk180 from "chalk";
27418
27617
 
27419
27618
  // src/commands/netcap/extractPostsFromCapture.ts
@@ -27858,7 +28057,7 @@ function extractPostsFromCapture(captureFile) {
27858
28057
  function netcapExtract(file) {
27859
28058
  const captureFile = file ?? defaultCapturePath();
27860
28059
  const posts = extractPostsFromCapture(captureFile);
27861
- const outFile = join72(captureFile, "..", "posts.json");
28060
+ const outFile = join74(captureFile, "..", "posts.json");
27862
28061
  writeFileSync34(outFile, `${JSON.stringify(posts, null, 2)}
27863
28062
  `);
27864
28063
  console.log(
@@ -28221,18 +28420,18 @@ import { execSync as execSync41 } from "child_process";
28221
28420
  import { execSync as execSync40 } from "child_process";
28222
28421
  import { unlinkSync as unlinkSync14, writeFileSync as writeFileSync35 } from "fs";
28223
28422
  import { tmpdir as tmpdir6 } from "os";
28224
- import { join as join74 } from "path";
28423
+ import { join as join76 } from "path";
28225
28424
 
28226
28425
  // src/commands/prs/loadCommentsCache.ts
28227
28426
  import { existsSync as existsSync62, readFileSync as readFileSync43, unlinkSync as unlinkSync13 } from "fs";
28228
28427
  import { parse as parse3 } from "yaml";
28229
28428
 
28230
28429
  // src/commands/prs/commentsCachePath.ts
28231
- import { homedir as homedir23 } from "os";
28232
- import { join as join73 } from "path";
28430
+ import { homedir as homedir24 } from "os";
28431
+ import { join as join75 } from "path";
28233
28432
  function commentsCachePath(org, repo, prNumber) {
28234
- return join73(
28235
- homedir23(),
28433
+ return join75(
28434
+ homedir24(),
28236
28435
  ".assist",
28237
28436
  "pr-comments",
28238
28437
  org,
@@ -28280,7 +28479,7 @@ function replyToComment(org, repo, prNumber, commentId, message3) {
28280
28479
  // src/commands/prs/resolveCommentWithReply.ts
28281
28480
  function resolveThread(threadId) {
28282
28481
  const mutation = `mutation($threadId: ID!) { resolveReviewThread(input: {threadId: $threadId}) { thread { isResolved } } }`;
28283
- const queryFile = join74(tmpdir6(), `gh-mutation-${Date.now()}.graphql`);
28482
+ const queryFile = join76(tmpdir6(), `gh-mutation-${Date.now()}.graphql`);
28284
28483
  writeFileSync35(queryFile, mutation);
28285
28484
  try {
28286
28485
  execSync40(
@@ -28365,10 +28564,10 @@ function fixed(commentId, sha) {
28365
28564
  import { execSync as execSync42 } from "child_process";
28366
28565
  import { unlinkSync as unlinkSync15, writeFileSync as writeFileSync36 } from "fs";
28367
28566
  import { tmpdir as tmpdir7 } from "os";
28368
- import { join as join75 } from "path";
28567
+ import { join as join77 } from "path";
28369
28568
  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
28569
  function fetchThreadIds(org, repo, prNumber) {
28371
- const queryFile = join75(tmpdir7(), `gh-query-${Date.now()}.graphql`);
28570
+ const queryFile = join77(tmpdir7(), `gh-query-${Date.now()}.graphql`);
28372
28571
  writeFileSync36(queryFile, THREAD_QUERY);
28373
28572
  try {
28374
28573
  const result = execSync42(
@@ -28443,10 +28642,10 @@ import { stringify as stringify3 } from "yaml";
28443
28642
 
28444
28643
  // src/commands/prs/removeStaleCommentsCaches.ts
28445
28644
  import { readdirSync as readdirSync12, unlinkSync as unlinkSync16 } from "fs";
28446
- import { join as join76 } from "path";
28645
+ import { join as join78 } from "path";
28447
28646
  var STALE_PATTERN = /^pr-\d+-comments\.yaml$/;
28448
28647
  function removeStaleCommentsCaches(cwd = process.cwd()) {
28449
- const dir = join76(cwd, ".assist");
28648
+ const dir = join78(cwd, ".assist");
28450
28649
  let entries;
28451
28650
  try {
28452
28651
  entries = readdirSync12(dir);
@@ -28454,7 +28653,7 @@ function removeStaleCommentsCaches(cwd = process.cwd()) {
28454
28653
  return;
28455
28654
  }
28456
28655
  for (const entry of entries.filter((e) => STALE_PATTERN.test(e))) {
28457
- unlinkSync16(join76(dir, entry));
28656
+ unlinkSync16(join78(dir, entry));
28458
28657
  }
28459
28658
  }
28460
28659
 
@@ -29088,7 +29287,7 @@ function buildValidatedBody(options2, usage) {
29088
29287
  }
29089
29288
 
29090
29289
  // src/commands/prs/placePr.ts
29091
- import { execFileSync as execFileSync16 } from "child_process";
29290
+ import { execFileSync as execFileSync17 } from "child_process";
29092
29291
 
29093
29292
  // src/commands/prs/buildCreateArgs.ts
29094
29293
  function buildCreateArgs(title, body, options2, attachments = []) {
@@ -29174,7 +29373,7 @@ function buildEditArgs(number, title, body, attachments = []) {
29174
29373
  // src/commands/prs/placePr.ts
29175
29374
  function hasUpstream2() {
29176
29375
  try {
29177
- execFileSync16(
29376
+ execFileSync17(
29178
29377
  "git",
29179
29378
  ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"],
29180
29379
  { stdio: "pipe" }
@@ -29186,7 +29385,7 @@ function hasUpstream2() {
29186
29385
  }
29187
29386
  function ensureBranchPushed() {
29188
29387
  const args = hasUpstream2() ? ["push"] : ["push", "--set-upstream", "origin", "HEAD"];
29189
- execFileSync16("git", args, { stdio: "inherit" });
29388
+ execFileSync17("git", args, { stdio: "inherit" });
29190
29389
  }
29191
29390
  async function placePr(prNumber, title, body, options2, attachments = []) {
29192
29391
  const args = prNumber !== null ? buildEditArgs(prNumber, title, body, attachments) : buildCreateArgs(title, body, options2, attachments);
@@ -29332,10 +29531,10 @@ function warn(reason4) {
29332
29531
  }
29333
29532
 
29334
29533
  // src/commands/prs/enableAutoMerge.ts
29335
- import { execFileSync as execFileSync17 } from "child_process";
29534
+ import { execFileSync as execFileSync18 } from "child_process";
29336
29535
  function enableAutoMerge() {
29337
29536
  try {
29338
- execFileSync17(
29537
+ execFileSync18(
29339
29538
  "gh",
29340
29539
  ["pr", "merge", getCurrentBranch2(), "--auto", "--squash"],
29341
29540
  { stdio: "inherit" }
@@ -31093,7 +31292,7 @@ async function notify() {
31093
31292
  import { execSync as execSync52 } from "child_process";
31094
31293
  import { existsSync as existsSync63, mkdirSync as mkdirSync21, unlinkSync as unlinkSync17, writeFileSync as writeFileSync38 } from "fs";
31095
31294
  import { tmpdir as tmpdir8 } from "os";
31096
- import { join as join77, resolve as resolve22 } from "path";
31295
+ import { join as join79, resolve as resolve22 } from "path";
31097
31296
  import chalk205 from "chalk";
31098
31297
 
31099
31298
  // src/commands/screenshot/captureWindowPs1.ts
@@ -31230,7 +31429,7 @@ function buildOutputPath(outputDir, processName) {
31230
31429
  return resolve22(outputDir, `${processName}-${timestamp5}.png`);
31231
31430
  }
31232
31431
  function runPowerShellScript(processName, outputPath) {
31233
- const scriptPath = join77(tmpdir8(), `assist-screenshot-${Date.now()}.ps1`);
31432
+ const scriptPath = join79(tmpdir8(), `assist-screenshot-${Date.now()}.ps1`);
31234
31433
  writeFileSync38(scriptPath, captureWindowPs1, "utf8");
31235
31434
  try {
31236
31435
  execSync52(
@@ -31345,9 +31544,9 @@ function buildLimitsSegment(rateLimits) {
31345
31544
 
31346
31545
  // src/commands/readGitBranch.ts
31347
31546
  import { readFileSync as readFileSync46, statSync as statSync10 } from "fs";
31348
- import { isAbsolute as isAbsolute6, join as join78, resolve as resolve23 } from "path";
31547
+ import { isAbsolute as isAbsolute6, join as join80, resolve as resolve23 } from "path";
31349
31548
  function resolveGitDir(cwd) {
31350
- const dotGit = join78(cwd, ".git");
31549
+ const dotGit = join80(cwd, ".git");
31351
31550
  let stat5;
31352
31551
  try {
31353
31552
  stat5 = statSync10(dotGit);
@@ -31377,7 +31576,7 @@ function readGitBranch(cwd) {
31377
31576
  }
31378
31577
  let head;
31379
31578
  try {
31380
- head = readFileSync46(join78(gitDir, "HEAD"), "utf8");
31579
+ head = readFileSync46(join80(gitDir, "HEAD"), "utf8");
31381
31580
  } catch {
31382
31581
  return null;
31383
31582
  }
@@ -33798,7 +33997,7 @@ function exitWith(message3) {
33798
33997
  }
33799
33998
 
33800
33999
  // src/commands/review/highLevel/proposeHighLevelGlobs.ts
33801
- import { execFileSync as execFileSync19 } from "child_process";
34000
+ import { execFileSync as execFileSync20 } from "child_process";
33802
34001
  import chalk228 from "chalk";
33803
34002
 
33804
34003
  // src/commands/review/highLevel/buildGlobProposalPrompt.ts
@@ -33846,7 +34045,7 @@ function readGlobs(value) {
33846
34045
  }
33847
34046
 
33848
34047
  // src/commands/review/highLevel/summariseRepoTree.ts
33849
- import { execFileSync as execFileSync18 } from "child_process";
34048
+ import { execFileSync as execFileSync19 } from "child_process";
33850
34049
  import { dirname as dirname36, extname as extname4 } from "path";
33851
34050
  var MAX_DIRECTORIES = 200;
33852
34051
  var MAX_EXTENSIONS_PER_DIRECTORY = 6;
@@ -33862,7 +34061,7 @@ function summariseRepoTree(cwd = process.cwd()) {
33862
34061
  }
33863
34062
  function listTrackedFiles(cwd) {
33864
34063
  try {
33865
- return execFileSync18("git", ["ls-files"], {
34064
+ return execFileSync19("git", ["ls-files"], {
33866
34065
  cwd,
33867
34066
  encoding: "utf8",
33868
34067
  maxBuffer: 64 * 1024 * 1024
@@ -33907,7 +34106,7 @@ function proposeHighLevelGlobs(cwd = process.cwd()) {
33907
34106
  }
33908
34107
  function askClaude(prompt, cwd) {
33909
34108
  try {
33910
- return execFileSync19("claude", ["-p", "--model", "sonnet", prompt], {
34109
+ return execFileSync20("claude", ["-p", "--model", "sonnet", prompt], {
33911
34110
  cwd,
33912
34111
  encoding: "utf8",
33913
34112
  timeout: 12e4,
@@ -34000,7 +34199,7 @@ function groupFilesByDirectory(files) {
34000
34199
  }
34001
34200
 
34002
34201
  // src/commands/review/highLevel/buildHighLevelTree.ts
34003
- function basename20(path106) {
34202
+ function basename21(path106) {
34004
34203
  return path106.slice(path106.lastIndexOf("/") + 1);
34005
34204
  }
34006
34205
  function sum(nodes, key) {
@@ -34016,7 +34215,7 @@ function nodesOf(at2, prefix2) {
34016
34215
  const files = at2.files.map(
34017
34216
  (file) => ({
34018
34217
  kind: "file",
34019
- name: basename20(file.path),
34218
+ name: basename21(file.path),
34020
34219
  ...file
34021
34220
  })
34022
34221
  );
@@ -34287,7 +34486,7 @@ function evaluateHighLevelChecks(subject) {
34287
34486
 
34288
34487
  // src/commands/review/highLevel/fetchHighLevelFiles.ts
34289
34488
  import { execSync as execSync55 } from "child_process";
34290
- import { createHash as createHash3 } from "crypto";
34489
+ import { createHash as createHash4 } from "crypto";
34291
34490
  var JQ = ".[] | {filename,status,additions,deletions,patch} | @json";
34292
34491
  function toStatus(status2) {
34293
34492
  if (status2 === "added") return "added";
@@ -34295,7 +34494,7 @@ function toStatus(status2) {
34295
34494
  return "modified";
34296
34495
  }
34297
34496
  function diffUrl(repo, prNumber, path106) {
34298
- const anchor = createHash3("sha256").update(path106).digest("hex");
34497
+ const anchor = createHash4("sha256").update(path106).digest("hex");
34299
34498
  return `https://github.com/${repo}/pull/${prNumber}/files#diff-${anchor}`;
34300
34499
  }
34301
34500
  function fetchHighLevelFiles(prNumber, repo) {
@@ -34336,12 +34535,12 @@ function fetchHighLevelPr(prNumber, repo) {
34336
34535
  import { existsSync as existsSync64, readFileSync as readFileSync47 } from "fs";
34337
34536
 
34338
34537
  // src/commands/review/highLevel/highLevelReviewPath.ts
34339
- import { join as join79 } from "path";
34538
+ import { join as join81 } from "path";
34340
34539
  function slug(value) {
34341
34540
  return value.replace(/[^\w.-]+/g, "-");
34342
34541
  }
34343
34542
  function highLevelReviewPath(repo, headRef, headSha) {
34344
- return join79(
34543
+ return join81(
34345
34544
  getStoreDir(),
34346
34545
  "high-level-reviews",
34347
34546
  slug(repo),
@@ -35417,18 +35616,7 @@ import { format } from "util";
35417
35616
 
35418
35617
  // src/commands/review/createReviewLogSink.ts
35419
35618
  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
35619
+ import { join as join82 } from "path";
35432
35620
  var LOG_FILE = "review.log";
35433
35621
  function createReviewLogSink() {
35434
35622
  let logPath2;
@@ -35445,7 +35633,7 @@ function createReviewLogSink() {
35445
35633
  },
35446
35634
  attach(reviewDir) {
35447
35635
  mkdirSync23(reviewDir, { recursive: true });
35448
- logPath2 = join80(reviewDir, LOG_FILE);
35636
+ logPath2 = join82(reviewDir, LOG_FILE);
35449
35637
  const lines2 = [
35450
35638
  "",
35451
35639
  `=== ${(/* @__PURE__ */ new Date()).toISOString()} ===`,
@@ -36526,22 +36714,22 @@ ${annotateDiffWithLineNumbers(context.diff.trimEnd())}
36526
36714
  }
36527
36715
 
36528
36716
  // src/commands/review/buildReviewPaths.ts
36529
- import { homedir as homedir24 } from "os";
36530
- import { basename as basename21, join as join81 } from "path";
36717
+ import { homedir as homedir25 } from "os";
36718
+ import { basename as basename22, join as join83 } from "path";
36531
36719
  function buildReviewPaths(repoRoot2, key) {
36532
- const reviewDir = join81(
36533
- homedir24(),
36720
+ const reviewDir = join83(
36721
+ homedir25(),
36534
36722
  ".assist",
36535
36723
  "reviews",
36536
- basename21(repoRoot2),
36724
+ basename22(repoRoot2),
36537
36725
  key
36538
36726
  );
36539
36727
  return {
36540
36728
  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")
36729
+ requestPath: join83(reviewDir, "request.md"),
36730
+ claudePath: join83(reviewDir, "claude.md"),
36731
+ codexPath: join83(reviewDir, "codex.md"),
36732
+ synthesisPath: join83(reviewDir, "synthesis.md")
36545
36733
  };
36546
36734
  }
36547
36735
 
@@ -36995,16 +37183,16 @@ import path87 from "path";
36995
37183
  import chalk239 from "chalk";
36996
37184
  function listRules(target, options2 = {}) {
36997
37185
  const resolved = path87.resolve(target ?? process.cwd());
36998
- const rules3 = readScopedRules(resolved);
36999
- if (rules3.length === 0) {
37186
+ const rules2 = readScopedRules(resolved);
37187
+ if (rules2.length === 0) {
37000
37188
  const label2 = path87.relative(process.cwd(), resolved) || ".";
37001
37189
  console.log(chalk239.gray(`No rules in scope for ${label2}`));
37002
37190
  return;
37003
37191
  }
37004
37192
  const base = findRepoRoot(scopeDirectory(resolved));
37005
- const width = Math.max(...rules3.map((rule) => rule.code.length));
37193
+ const width = Math.max(...rules2.map((rule) => rule.code.length));
37006
37194
  let shown;
37007
- for (const rule of rules3) {
37195
+ for (const rule of rules2) {
37008
37196
  if (rule.source !== shown) {
37009
37197
  shown = rule.source;
37010
37198
  console.log(
@@ -37522,12 +37710,12 @@ async function reviewProposedSlackMessage(target, body, workingPath) {
37522
37710
  }
37523
37711
 
37524
37712
  // src/commands/slack/slackWorkingFile.ts
37525
- import { join as join82 } from "path";
37713
+ import { join as join84 } from "path";
37526
37714
  function slackWorkingFile(channel, partIndex) {
37527
37715
  const slug2 = channel.replace(/^[#@]/, "").toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "channel";
37528
- const dir = join82(getStoreDir(), "slack");
37716
+ const dir = join84(getStoreDir(), "slack");
37529
37717
  const name = partIndex === void 0 ? slug2 : `${slug2}-${partIndex}`;
37530
- return { dir, bodyPath: join82(dir, `${name}.md`) };
37718
+ return { dir, bodyPath: join84(dir, `${name}.md`) };
37531
37719
  }
37532
37720
 
37533
37721
  // src/commands/slack/previewSlackThreadParts.ts
@@ -38707,20 +38895,20 @@ async function configure() {
38707
38895
 
38708
38896
  // src/commands/transcript/list.ts
38709
38897
  import { existsSync as existsSync76, readdirSync as readdirSync20, statSync as statSync12 } from "fs";
38710
- import { join as join94 } from "path";
38898
+ import { join as join96 } from "path";
38711
38899
  function list3() {
38712
38900
  const { vttDir } = getTranscriptConfig();
38713
38901
  if (!existsSync76(vttDir)) return;
38714
38902
  for (const entry of readdirSync20(vttDir)) {
38715
38903
  if (!entry.endsWith(".vtt")) continue;
38716
- if (statSync12(join94(vttDir, entry)).isDirectory()) continue;
38904
+ if (statSync12(join96(vttDir, entry)).isDirectory()) continue;
38717
38905
  console.log(entry);
38718
38906
  }
38719
38907
  }
38720
38908
 
38721
38909
  // src/commands/transcript/move.ts
38722
38910
  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";
38911
+ import { basename as basename26, join as join97 } from "path";
38724
38912
 
38725
38913
  // src/commands/transcript/convertVttToMarkdown.ts
38726
38914
  function convertVttToMarkdown(inputPath) {
@@ -38730,9 +38918,9 @@ function convertVttToMarkdown(inputPath) {
38730
38918
  // src/commands/transcript/move.ts
38731
38919
  var DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/;
38732
38920
  function archiveRawVtt(vttDir, sourcePath, filename) {
38733
- const processedDir = join95(vttDir, "processed");
38921
+ const processedDir = join97(vttDir, "processed");
38734
38922
  mkdirSync32(processedDir, { recursive: true });
38735
- renameSync2(sourcePath, join95(processedDir, filename));
38923
+ renameSync2(sourcePath, join97(processedDir, filename));
38736
38924
  }
38737
38925
  function move(file, options2) {
38738
38926
  const { date, client } = options2;
@@ -38741,27 +38929,27 @@ function move(file, options2) {
38741
38929
  process.exit(1);
38742
38930
  }
38743
38931
  const { vttDir, transcriptsDir, summaryDir } = getTranscriptConfig();
38744
- const filename = basename25(file);
38745
- const sourcePath = join95(vttDir, filename);
38932
+ const filename = basename26(file);
38933
+ const sourcePath = join97(vttDir, filename);
38746
38934
  if (!existsSync77(sourcePath)) {
38747
38935
  console.error(`Error: VTT file not found: ${sourcePath}`);
38748
38936
  process.exit(1);
38749
38937
  }
38750
- const base = basename25(filename, ".vtt").replace(/ Transcription$/, "");
38938
+ const base = basename26(filename, ".vtt").replace(/ Transcription$/, "");
38751
38939
  const outputName = `${date} ${base}.md`;
38752
- const formattedDir = join95(transcriptsDir, client);
38940
+ const formattedDir = join97(transcriptsDir, client);
38753
38941
  mkdirSync32(formattedDir, { recursive: true });
38754
- const formattedPath = join95(formattedDir, outputName);
38942
+ const formattedPath = join97(formattedDir, outputName);
38755
38943
  writeFileSync50(formattedPath, convertVttToMarkdown(sourcePath), "utf8");
38756
38944
  archiveRawVtt(vttDir, sourcePath, filename);
38757
- const summaryPath = join95(summaryDir, client, outputName);
38945
+ const summaryPath = join97(summaryDir, client, outputName);
38758
38946
  console.log(`Formatted transcript: ${formattedPath}`);
38759
38947
  console.log(`Summary target: ${summaryPath}`);
38760
38948
  }
38761
38949
 
38762
38950
  // src/commands/transcript/merge.ts
38763
38951
  import { existsSync as existsSync78, writeFileSync as writeFileSync51 } from "fs";
38764
- import { basename as basename26 } from "path";
38952
+ import { basename as basename27 } from "path";
38765
38953
 
38766
38954
  // src/commands/transcript/failTranscript.ts
38767
38955
  function failTranscript(message3) {
@@ -38907,7 +39095,7 @@ function readSource3(file) {
38907
39095
  if (!existsSync78(file)) failTranscript(`VTT file not found: ${file}`);
38908
39096
  const cues = readCleanedCues(file);
38909
39097
  if (cues.length === 0) failTranscript(`no cues found in: ${file}`);
38910
- return { path: file, name: basename26(file), cues };
39098
+ return { path: file, name: basename27(file), cues };
38911
39099
  }
38912
39100
  function wholePassages(sources) {
38913
39101
  return sources.map((source) => ({
@@ -39398,14 +39586,14 @@ function findRuleViolations(data, rule) {
39398
39586
  }
39399
39587
  return violations;
39400
39588
  }
39401
- function findForbiddenStrings(rules3, readJson) {
39402
- return rules3.flatMap((rule) => findRuleViolations(readJson(rule.file), rule));
39589
+ function findForbiddenStrings(rules2, readJson) {
39590
+ return rules2.flatMap((rule) => findRuleViolations(readJson(rule.file), rule));
39403
39591
  }
39404
39592
 
39405
39593
  // src/commands/verify/forbiddenStrings.ts
39406
39594
  function forbiddenStrings() {
39407
- const rules3 = loadConfig().forbiddenStrings ?? [];
39408
- if (rules3.length === 0) {
39595
+ const rules2 = loadConfig().forbiddenStrings ?? [];
39596
+ if (rules2.length === 0) {
39409
39597
  console.log("No forbidden-strings rules configured.");
39410
39598
  process.exit(0);
39411
39599
  }
@@ -39426,7 +39614,7 @@ function forbiddenStrings() {
39426
39614
  cache6.set(file, parsed);
39427
39615
  return parsed;
39428
39616
  };
39429
- const violations = findForbiddenStrings(rules3, readJson);
39617
+ const violations = findForbiddenStrings(rules2, readJson);
39430
39618
  if (violations.length === 0) {
39431
39619
  console.log("No forbidden strings found.");
39432
39620
  process.exit(0);
@@ -39568,7 +39756,7 @@ function listMigrationFiles(dir) {
39568
39756
 
39569
39757
  // src/commands/verify/migrations/readBaselineMigrations.ts
39570
39758
  import { execSync as execSync63 } from "child_process";
39571
- import { basename as basename27 } from "path";
39759
+ import { basename as basename28 } from "path";
39572
39760
  var MIGRATION_FILE3 = /^migration\d+[A-Za-z0-9]*\.ts$/;
39573
39761
  function readBaselineMigrations(repoRelativeDir, ref) {
39574
39762
  const baseline = /* @__PURE__ */ new Map();
@@ -39581,7 +39769,7 @@ function readBaselineMigrations(repoRelativeDir, ref) {
39581
39769
  } catch {
39582
39770
  return baseline;
39583
39771
  }
39584
- const paths = listing.split("\n").map((line) => line.trim()).filter(Boolean).filter((path106) => MIGRATION_FILE3.test(basename27(path106)));
39772
+ const paths = listing.split("\n").map((line) => line.trim()).filter(Boolean).filter((path106) => MIGRATION_FILE3.test(basename28(path106)));
39585
39773
  for (const path106 of paths) {
39586
39774
  try {
39587
39775
  const content = execSync63(`git show "${ref}:${path106}"`, {
@@ -39589,7 +39777,7 @@ function readBaselineMigrations(repoRelativeDir, ref) {
39589
39777
  maxBuffer: 16 * 1024 * 1024,
39590
39778
  stdio: ["pipe", "pipe", "pipe"]
39591
39779
  });
39592
- baseline.set(basename27(path106), content);
39780
+ baseline.set(basename28(path106), content);
39593
39781
  } catch {
39594
39782
  }
39595
39783
  }
@@ -39802,46 +39990,6 @@ function gitFailureReason(error) {
39802
39990
  return message3.trim() || "git failed without reporting a reason";
39803
39991
  }
39804
39992
 
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
39993
  // src/commands/watch/simulatedDivergencePath.ts
39846
39994
  import { resolve as resolve25 } from "path";
39847
39995
  function simulatedDivergencePath(cwd) {
@@ -39909,22 +40057,6 @@ async function watchLoop() {
39909
40057
  }
39910
40058
  }
39911
40059
 
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
40060
  // src/commands/watch/readRecentCommits.ts
39929
40061
  function readRecentCommits(count8 = 10, cwd) {
39930
40062
  const output = runGit3(
@@ -39972,24 +40104,12 @@ function renderWatchReport({
39972
40104
  }
39973
40105
 
39974
40106
  // 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
40107
  function restartAdvice(paths) {
39988
- return rules.filter((rule) => paths.some(rule.matches)).map((rule) => rule.advice);
40108
+ return restartRules.filter((rule) => paths.some(rule.matches)).map((rule) => rule.advice);
39989
40109
  }
39990
40110
 
39991
40111
  // src/commands/watch/syncAdvice.ts
39992
- var rules2 = [
40112
+ var rules = [
39993
40113
  {
39994
40114
  matches: (path106) => path106.startsWith("claude/commands/"),
39995
40115
  reason: "claude/commands changed"
@@ -40016,7 +40136,7 @@ var rules2 = [
40016
40136
  }
40017
40137
  ];
40018
40138
  function syncAdvice(paths) {
40019
- return rules2.filter((rule) => paths.some(rule.matches)).map((rule) => rule.reason);
40139
+ return rules.filter((rule) => paths.some(rule.matches)).map((rule) => rule.reason);
40020
40140
  }
40021
40141
 
40022
40142
  // src/commands/watch/buildWatchReport.ts
@@ -40702,7 +40822,7 @@ async function auth() {
40702
40822
  // src/commands/roam/postRoamActivity.ts
40703
40823
  import { execFileSync as execFileSync22 } from "child_process";
40704
40824
  import { readdirSync as readdirSync22, readFileSync as readFileSync62, statSync as statSync13 } from "fs";
40705
- import { join as join97 } from "path";
40825
+ import { join as join98 } from "path";
40706
40826
  function findPortFile(roamDir) {
40707
40827
  let entries;
40708
40828
  try {
@@ -40711,7 +40831,7 @@ function findPortFile(roamDir) {
40711
40831
  return void 0;
40712
40832
  }
40713
40833
  const candidates = entries.filter((name) => /^roam-local-api(-[^.]+)?\.port$/.test(name)).map((name) => {
40714
- const path106 = join97(roamDir, name);
40834
+ const path106 = join98(roamDir, name);
40715
40835
  try {
40716
40836
  return { path: path106, mtimeMs: statSync13(path106).mtimeMs };
40717
40837
  } catch {
@@ -40728,7 +40848,7 @@ var PID_BY_APP = {
40728
40848
  function postRoamActivity(app, event) {
40729
40849
  const appData = process.env.APPDATA;
40730
40850
  if (!appData) return;
40731
- const portFile = findPortFile(join97(appData, "Roam"));
40851
+ const portFile = findPortFile(join98(appData, "Roam"));
40732
40852
  if (!portFile) return;
40733
40853
  let port;
40734
40854
  try {
@@ -40864,7 +40984,7 @@ async function run3(name, args) {
40864
40984
 
40865
40985
  // src/commands/run/add.ts
40866
40986
  import { mkdirSync as mkdirSync33, writeFileSync as writeFileSync53 } from "fs";
40867
- import { join as join98 } from "path";
40987
+ import { join as join99 } from "path";
40868
40988
 
40869
40989
  // src/commands/run/extractOption.ts
40870
40990
  function extractOption(args, flag) {
@@ -40925,7 +41045,7 @@ function saveNewRunConfig(name, command, args, cwd) {
40925
41045
  saveConfig(config);
40926
41046
  }
40927
41047
  function createCommandFile(name) {
40928
- const dir = join98(".claude", "commands");
41048
+ const dir = join99(".claude", "commands");
40929
41049
  mkdirSync33(dir, { recursive: true });
40930
41050
  const content = `---
40931
41051
  description: Run ${name}
@@ -40933,7 +41053,7 @@ description: Run ${name}
40933
41053
 
40934
41054
  Run \`assist run ${name} $ARGUMENTS 2>&1\`.
40935
41055
  `;
40936
- const filePath = join98(dir, `${name}.md`);
41056
+ const filePath = join99(dir, `${name}.md`);
40937
41057
  writeFileSync53(filePath, content);
40938
41058
  console.log(`Created command file: ${filePath}`);
40939
41059
  }
@@ -40990,7 +41110,7 @@ function link2() {
40990
41110
 
40991
41111
  // src/commands/run/remove.ts
40992
41112
  import { existsSync as existsSync82, unlinkSync as unlinkSync21 } from "fs";
40993
- import { join as join99 } from "path";
41113
+ import { join as join100 } from "path";
40994
41114
  function findRemoveIndex() {
40995
41115
  const idx = process.argv.indexOf("remove");
40996
41116
  if (idx === -1 || idx + 1 >= process.argv.length) return -1;
@@ -41005,7 +41125,7 @@ function parseRemoveName() {
41005
41125
  return process.argv[idx + 1];
41006
41126
  }
41007
41127
  function deleteCommandFile(name) {
41008
- const filePath = join99(".claude", "commands", `${name}.md`);
41128
+ const filePath = join100(".claude", "commands", `${name}.md`);
41009
41129
  if (existsSync82(filePath)) {
41010
41130
  unlinkSync21(filePath);
41011
41131
  console.log(`Deleted command file: ${filePath}`);
@@ -41996,7 +42116,116 @@ function createRunSession(id, { runName, runArgs, cwd, meta }) {
41996
42116
  };
41997
42117
  }
41998
42118
 
41999
- // src/commands/sessions/daemon/autoUpdate/startAutoUpdate.ts
42119
+ // src/commands/sessions/daemon/autoUpdate/autoUpdateEnabled.ts
42120
+ function autoUpdateEnabled(installDir) {
42121
+ try {
42122
+ return loadConfigFrom(installDir).autoUpdate?.enabled ?? true;
42123
+ } catch (error) {
42124
+ daemonLog(
42125
+ `auto-update: config failed to load, keeping the default (on): ${error instanceof Error ? error.message : String(error)}`
42126
+ );
42127
+ return true;
42128
+ }
42129
+ }
42130
+
42131
+ // src/commands/sessions/daemon/autoUpdate/autoUpdateState.ts
42132
+ var loop = {
42133
+ phase: "off",
42134
+ since: (/* @__PURE__ */ new Date()).toISOString(),
42135
+ reason: "not started"
42136
+ };
42137
+ var startCommit;
42138
+ var autoUpdateState = {
42139
+ loop: () => loop,
42140
+ startCommit: () => startCommit,
42141
+ setStartCommit: (commit2) => {
42142
+ startCommit = commit2;
42143
+ },
42144
+ enter: (phase, extra = {}) => {
42145
+ loop = { phase, since: (/* @__PURE__ */ new Date()).toISOString(), ...extra };
42146
+ }
42147
+ };
42148
+
42149
+ // src/commands/sessions/daemon/autoUpdate/holdForEscalation.ts
42150
+ var ESCALATION_POLL_MS = 5e3;
42151
+ async function holdForEscalation(sessionId, deps2) {
42152
+ deps2.enter("diverged", { escalationId: sessionId });
42153
+ deps2.note(`paused while session ${sessionId} reconciles the divergence`);
42154
+ while (deps2.isLive(sessionId)) await deps2.sleep(ESCALATION_POLL_MS);
42155
+ deps2.note(`session ${sessionId} ended; resuming`);
42156
+ }
42157
+
42158
+ // src/commands/sessions/daemon/autoUpdate/runLapCapturingTail.ts
42159
+ var OUTPUT_TAIL = 16 * 1024;
42160
+ async function runLapCapturingTail(deps2) {
42161
+ let tail = "";
42162
+ const end = await deps2.runLap((text17) => {
42163
+ tail = (tail + text17).slice(-OUTPUT_TAIL);
42164
+ deps2.record(text17);
42165
+ });
42166
+ return { end, tail };
42167
+ }
42168
+
42169
+ // src/commands/sessions/daemon/autoUpdate/runAutoUpdateLoop.ts
42170
+ var DIVERGED_EXIT_CODE = 3;
42171
+ var RETRY_AFTER_FAILURE_MS = 5 * 60 * 1e3;
42172
+ async function runAutoUpdateLoop(deps2, maxLaps = Number.POSITIVE_INFINITY) {
42173
+ let held = deps2.liveEscalation();
42174
+ for (let lap = 1; lap <= maxLaps; lap++) {
42175
+ if (held) await holdForEscalation(held, deps2);
42176
+ held = void 0;
42177
+ deps2.enter("waiting");
42178
+ deps2.note(
42179
+ `lap ${lap} at ${(/* @__PURE__ */ new Date()).toLocaleString()}: assist ${WATCH_LAP_ARGS.join(" ")}`
42180
+ );
42181
+ let result;
42182
+ try {
42183
+ result = await runLapCapturingTail(deps2);
42184
+ } catch (error) {
42185
+ await backOff(
42186
+ `lap ${lap} could not start (${error instanceof Error ? error.message : String(error)})`,
42187
+ deps2
42188
+ );
42189
+ continue;
42190
+ }
42191
+ const { end, tail } = result;
42192
+ const how = end.signal ? `killed by ${end.signal}` : `exited ${end.code}`;
42193
+ if (end.code === DIVERGED_EXIT_CODE) {
42194
+ held = deps2.escalate(tail);
42195
+ deps2.note(`lap ${lap} ${how} (divergence); escalated to session ${held}`);
42196
+ continue;
42197
+ }
42198
+ if (decideLap(end, false).kind === "relaunch") {
42199
+ deps2.note(`lap ${lap} ${how}; relaunching`);
42200
+ continue;
42201
+ }
42202
+ await backOff(`lap ${lap} ${how}`, deps2);
42203
+ }
42204
+ }
42205
+ async function backOff(reason4, deps2) {
42206
+ deps2.enter("retrying", { reason: reason4 });
42207
+ deps2.note(`${reason4}; retrying in ${RETRY_AFTER_FAILURE_MS / 6e4}m`);
42208
+ await deps2.sleep(RETRY_AFTER_FAILURE_MS);
42209
+ }
42210
+
42211
+ // src/commands/sessions/daemon/autoUpdate/trimWatcherLog.ts
42212
+ import { readFileSync as readFileSync65, writeFileSync as writeFileSync54 } from "fs";
42213
+ var KEEP_TAIL = 256 * 1024;
42214
+ var MAX_LOG_SIZE = 4 * 1024 * 1024;
42215
+ function trimWatcherLog(cwd) {
42216
+ const path106 = watcherLogPath(cwd);
42217
+ let log2;
42218
+ try {
42219
+ log2 = readFileSync65(path106, "utf8");
42220
+ } catch {
42221
+ return;
42222
+ }
42223
+ if (log2.length <= MAX_LOG_SIZE) return;
42224
+ writeFileSync54(path106, log2.slice(-KEEP_TAIL));
42225
+ daemonLog(`auto-update: log ${path106} trimmed to its last ${KEEP_TAIL} chars`);
42226
+ }
42227
+
42228
+ // src/commands/sessions/daemon/autoUpdate/loopDeps.ts
42000
42229
  import { setTimeout as sleep2 } from "timers/promises";
42001
42230
 
42002
42231
  // src/commands/sessions/daemon/watcherNote.ts
@@ -42009,19 +42238,6 @@ function watcherNote(text17) {
42009
42238
  // src/commands/sessions/daemon/worktree/appendWatcherLog.ts
42010
42239
  import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync34 } from "fs";
42011
42240
  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
42241
  function appendWatcherLog(cwd, data) {
42026
42242
  try {
42027
42243
  const path106 = watcherLogPath(cwd);
@@ -42175,18 +42391,6 @@ function escalateDivergence(ctx, cwd, output) {
42175
42391
  return id;
42176
42392
  }
42177
42393
 
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
42394
  // src/commands/sessions/daemon/autoUpdate/lineRecorder.ts
42191
42395
  function lineRecorder(onLine) {
42192
42396
  let pending = "";
@@ -42200,64 +42404,6 @@ function lineRecorder(onLine) {
42200
42404
  };
42201
42405
  }
42202
42406
 
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
42407
  // src/commands/sessions/daemon/autoUpdate/runUpdateLap.ts
42262
42408
  import { spawn as spawn11 } from "child_process";
42263
42409
  function runUpdateLap(cwd, onOutput) {
@@ -42290,24 +42436,7 @@ function runUpdateLap(cwd, onOutput) {
42290
42436
  });
42291
42437
  }
42292
42438
 
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
42439
+ // src/commands/sessions/daemon/autoUpdate/loopDeps.ts
42311
42440
  function loopDeps(installDir, ctx) {
42312
42441
  const clone = canonicalTreePath(installDir);
42313
42442
  const logLine = lineRecorder((line) => daemonLog(`auto-update: ${line}`));
@@ -42321,6 +42450,7 @@ function loopDeps(installDir, ctx) {
42321
42450
  appendWatcherLog(installDir, watcherNote(text17));
42322
42451
  daemonLog(`auto-update: ${text17}`);
42323
42452
  },
42453
+ enter: autoUpdateState.enter,
42324
42454
  liveEscalation: () => liveEscalationIn(ctx().sessions, clone)?.id,
42325
42455
  escalate: (output) => escalateDivergence(ctx(), installDir, output),
42326
42456
  isLive: (sessionId) => {
@@ -42330,14 +42460,21 @@ function loopDeps(installDir, ctx) {
42330
42460
  sleep: (ms) => sleep2(ms)
42331
42461
  };
42332
42462
  }
42463
+
42464
+ // src/commands/sessions/daemon/autoUpdate/startAutoUpdate.ts
42465
+ function stayOff(reason4) {
42466
+ autoUpdateState.enter("off", { reason: reason4 });
42467
+ daemonLog(`auto-update: off (${reason4})`);
42468
+ }
42333
42469
  function startAutoUpdate(ctx) {
42334
42470
  const installDir = getInstallDir();
42335
- if (!autoUpdateEnabled(installDir)) {
42336
- daemonLog("auto-update: off (autoUpdate.enabled is false)");
42471
+ if (!isGitRepo(installDir)) {
42472
+ stayOff(`${installDir} is not a git clone`);
42337
42473
  return;
42338
42474
  }
42339
- if (!isGitRepo(installDir)) {
42340
- daemonLog(`auto-update: off (${installDir} is not a git clone)`);
42475
+ autoUpdateState.setStartCommit(headCommit3(installDir));
42476
+ if (!autoUpdateEnabled(installDir)) {
42477
+ stayOff("autoUpdate.enabled is false");
42341
42478
  return;
42342
42479
  }
42343
42480
  trimWatcherLog(installDir);
@@ -47014,6 +47151,16 @@ function exitAfterFlush(code) {
47014
47151
  // src/commands/sessions/daemon/handleConnection.ts
47015
47152
  import { createInterface as createInterface10 } from "readline";
47016
47153
 
47154
+ // src/commands/sessions/daemon/autoUpdate/autoUpdateReply.ts
47155
+ function autoUpdateReply() {
47156
+ return {
47157
+ type: "auto-update",
47158
+ version: ASSIST_VERSION,
47159
+ startCommit: autoUpdateState.startCommit(),
47160
+ loop: autoUpdateState.loop()
47161
+ };
47162
+ }
47163
+
47017
47164
  // src/commands/sessions/daemon/handleHelloRequest.ts
47018
47165
  var handleHelloRequest = (client, m, d) => {
47019
47166
  sendTo(client, buildHello());
@@ -47539,6 +47686,7 @@ var messageHandlers = {
47539
47686
  hello: handleHelloRequest,
47540
47687
  output: handleOutputRequest,
47541
47688
  nodes: (client, m) => m.links.sendNodes(client),
47689
+ "auto-update": (client) => sendTo(client, autoUpdateReply()),
47542
47690
  "reload-links": (client, m) => {
47543
47691
  daemonLog("reload-links received");
47544
47692
  m.links.reload();