toyon 0.4.0 → 0.4.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.
Files changed (27) hide show
  1. package/README.md +9 -12
  2. package/dist/cli.js +1 -1
  3. package/dist/daemon.js +296 -196
  4. package/dist/shell/assets/{Editor-CgKsVXHS.js → Editor-BV5mlMUd.js} +4 -4
  5. package/dist/shell/assets/{XTerm-CMRoVDAh.js → XTerm-vYHVHYnx.js} +1 -1
  6. package/dist/shell/assets/{cssMode-Dg15kh0f.js → cssMode-DmPPq0RM.js} +1 -1
  7. package/dist/shell/assets/{freemarker2-CU21n-BA.js → freemarker2-D0jw6qr7.js} +1 -1
  8. package/dist/shell/assets/{grammar-DGBFIuP2.js → grammar-SjzA6dB3.js} +1 -1
  9. package/dist/shell/assets/{handlebars-DVAbWe34.js → handlebars-DP51I8rr.js} +1 -1
  10. package/dist/shell/assets/{html-BkxU7kNX.js → html-lErft7TP.js} +1 -1
  11. package/dist/shell/assets/{htmlMode-Brl4kLPo.js → htmlMode-B8WYzHvO.js} +1 -1
  12. package/dist/shell/assets/index-2d0IGh4f.js +232 -0
  13. package/dist/shell/assets/index-DZ08PVmu.css +1 -0
  14. package/dist/shell/assets/{javascript-DoIo3Pg8.js → javascript-2o8dwNxB.js} +1 -1
  15. package/dist/shell/assets/{jsonMode-B9qq6lCy.js → jsonMode-BzyCh8f1.js} +1 -1
  16. package/dist/shell/assets/{liquid-Cn1aqWby.js → liquid-DwwOicEv.js} +1 -1
  17. package/dist/shell/assets/{lspLanguageFeatures-C5ez8E_9.js → lspLanguageFeatures-BSPiRlDO.js} +1 -1
  18. package/dist/shell/assets/{mdx-1MuVz0xd.js → mdx-Dx7gR42C.js} +1 -1
  19. package/dist/shell/assets/{python-DFhVdcGk.js → python-B09pxq08.js} +1 -1
  20. package/dist/shell/assets/{razor-BitqUXpg.js → razor-DO8UQZDP.js} +1 -1
  21. package/dist/shell/assets/{tsMode-loK_YDOL.js → tsMode-1xhdljsc.js} +1 -1
  22. package/dist/shell/assets/{xml-Cc4ljERX.js → xml-Be6HmYJA.js} +1 -1
  23. package/dist/shell/assets/{yaml-0hpVC8PU.js → yaml-D4xpEWFo.js} +1 -1
  24. package/dist/shell/index.html +2 -2
  25. package/package.json +1 -1
  26. package/dist/shell/assets/index-DUmxVabc.css +0 -1
  27. package/dist/shell/assets/index-WHwsRrlj.js +0 -232
package/dist/daemon.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // @bun
2
2
  // ../daemon/src/index.ts
3
- import { rmSync as rmSync6, writeFileSync as writeFileSync9 } from "fs";
3
+ import { rmSync as rmSync6, writeFileSync as writeFileSync10 } from "fs";
4
4
  import { dirname as dirname9 } from "path";
5
5
  import { fileURLToPath } from "url";
6
6
 
@@ -297,7 +297,7 @@ var PAIR_TTL_MS = 120000;
297
297
  var pairLink = (host, code) => `https://${host}/#pair=${code}`;
298
298
 
299
299
  // ../shared/src/protocol/limits.ts
300
- var PROTOCOL_VERSION = 58;
300
+ var PROTOCOL_VERSION = 60;
301
301
  var FILE_MAX_CHARS = 5000000;
302
302
  var IMAGE_MIME_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"];
303
303
  var IMAGE_MAX_EDGE = 2576;
@@ -7722,7 +7722,7 @@ function slug(s) {
7722
7722
  // ../daemon/package.json
7723
7723
  var package_default = {
7724
7724
  name: "@toyon/daemon",
7725
- version: "0.4.0",
7725
+ version: "0.4.1",
7726
7726
  private: true,
7727
7727
  type: "module",
7728
7728
  main: "src/index.ts",
@@ -12694,6 +12694,29 @@ async function excludeFromGit(cwd, pattern) {
12694
12694
  ` : ""}${pattern}
12695
12695
  `);
12696
12696
  }
12697
+ async function excludeBlock(cwd, name, patterns) {
12698
+ const file = await excludeFile(cwd);
12699
+ if (!file)
12700
+ return;
12701
+ const open = `# toyon ${name}`;
12702
+ const close = `# toyon end ${name}`;
12703
+ const current = existsSync3(file) ? readFileSync3(file, "utf8") : "";
12704
+ const lines = current.split(`
12705
+ `);
12706
+ const from = lines.indexOf(open);
12707
+ const to = lines.indexOf(close);
12708
+ const kept = from >= 0 && to > from ? [...lines.slice(0, from), ...lines.slice(to + 1)] : lines;
12709
+ while (kept.length > 0 && kept[kept.length - 1] === "")
12710
+ kept.pop();
12711
+ const next = [...kept, ...patterns.length > 0 ? [open, ...patterns, close] : []];
12712
+ const text = next.length > 0 ? `${next.join(`
12713
+ `)}
12714
+ ` : "";
12715
+ if (text === current)
12716
+ return;
12717
+ mkdirSync2(dirname(file), { recursive: true });
12718
+ writeFileSync(file, text);
12719
+ }
12697
12720
  async function unexcludeFromGit(cwd, pattern) {
12698
12721
  const file = await excludeFile(cwd);
12699
12722
  if (!file || !existsSync3(file))
@@ -14417,17 +14440,18 @@ class AcpSession {
14417
14440
  }
14418
14441
  return pickOption(params.options, verdict);
14419
14442
  }
14420
- askPermission(params) {
14443
+ async askPermission(params) {
14421
14444
  const plan = params.toolCall.kind === "switch_mode";
14422
- if (plan)
14423
- this.d.onPlan?.();
14445
+ const detail = permissionDetail(params);
14446
+ const planPath = plan ? await this.d.onPlan?.(detail) ?? null : null;
14424
14447
  const offered = plan ? params.options : params.options.filter((o) => o.kind !== "allow_always");
14425
14448
  const choices = offered.map((o) => ({ id: o.optionId, name: o.name, kind: o.kind }));
14426
14449
  return this.openAsk((id) => ({
14427
14450
  type: "agent-permission",
14428
14451
  id,
14429
14452
  title: params.toolCall.title ?? params.toolCall.name ?? "the agent needs a decision",
14430
- ...permissionDetail(params) ? { detail: permissionDetail(params) } : {},
14453
+ ...detail ? { detail } : {},
14454
+ ...planPath ? { plan: planPath } : {},
14431
14455
  choices,
14432
14456
  ...params.toolCall.toolCallId ? { toolId: params.toolCall.toolCallId } : {},
14433
14457
  ts: Date.now()
@@ -14438,10 +14462,17 @@ class AcpSession {
14438
14462
  return { outcome: { outcome: "cancelled" } };
14439
14463
  if (plan && (choice.kind === "allow_once" || choice.kind === "allow_always")) {
14440
14464
  this.d.setMode?.(modeAfterPlan(choice.name));
14465
+ if (planPath)
14466
+ fireAndForget(this.d.worktreeId, this.sayPlanEdited(detail, planPath), "edited plan");
14441
14467
  }
14442
14468
  return { outcome: { outcome: "selected", optionId: choice.id } };
14443
14469
  });
14444
14470
  }
14471
+ async sayPlanEdited(proposed, path) {
14472
+ if (!await this.d.planEdited?.(proposed))
14473
+ return;
14474
+ this.send(`I edited the plan before approving it. Build what is in ${path}, not the plan you proposed.`);
14475
+ }
14445
14476
  onElicit(params, signal) {
14446
14477
  const live = this.live;
14447
14478
  if (!live || !("sessionId" in params) || params.sessionId !== live.sessionId) {
@@ -14593,6 +14624,35 @@ function sameCommands(a, b) {
14593
14624
  return a.length === b.length && a.every((c, i) => c.name === b[i]?.name && c.description === b[i]?.description && c.hint === b[i]?.hint);
14594
14625
  }
14595
14626
 
14627
+ // ../daemon/src/agent/planDoc.ts
14628
+ import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
14629
+ import { join as join7 } from "path";
14630
+ var PLAN_REL = join7(".toyon", "plan.md");
14631
+ async function writePlanDoc(cwd, markdown) {
14632
+ try {
14633
+ mkdirSync5(join7(cwd, ".toyon"), { recursive: true });
14634
+ writeFileSync4(join7(cwd, PLAN_REL), markdown.endsWith(`
14635
+ `) ? markdown : `${markdown}
14636
+ `);
14637
+ } catch (e) {
14638
+ log.warn(cwd, `could not write ${PLAN_REL}; the plan stays on the card`, e);
14639
+ return null;
14640
+ }
14641
+ await excludeFromGit(cwd, PLAN_REL);
14642
+ return PLAN_REL;
14643
+ }
14644
+ function planEdited(cwd, proposed) {
14645
+ const file = join7(cwd, PLAN_REL);
14646
+ if (!existsSync8(file))
14647
+ return false;
14648
+ try {
14649
+ return readFileSync7(file, "utf8").trim() !== proposed.trim();
14650
+ } catch (e) {
14651
+ log.warn(cwd, `could not read ${PLAN_REL}`, e);
14652
+ return false;
14653
+ }
14654
+ }
14655
+
14596
14656
  // ../daemon/src/runtime/ports.ts
14597
14657
  var allocated = new Set;
14598
14658
  var range = cloud.proxyPorts;
@@ -14705,7 +14765,7 @@ function expandEnv(env, vars) {
14705
14765
 
14706
14766
  // ../daemon/src/core/remote.ts
14707
14767
  import { createHmac, timingSafeEqual } from "crypto";
14708
- import { existsSync as existsSync8, readFileSync as readFileSync7 } from "fs";
14768
+ import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
14709
14769
  function loadRemote(file, env = process.env) {
14710
14770
  if (env.TOYON_CLOUD === "1") {
14711
14771
  const host = (env.TOYON_PUBLIC_HOST ?? "").toLowerCase();
@@ -14718,9 +14778,9 @@ function loadRemote(file, env = process.env) {
14718
14778
  throw new Error(`TOYON_PREVIEWS: ${why}`);
14719
14779
  return { host, previews, front: "edge" };
14720
14780
  }
14721
- if (!existsSync8(file))
14781
+ if (!existsSync9(file))
14722
14782
  return null;
14723
- const view = parseRemote(readFileSync7(file, "utf8"));
14783
+ const view = parseRemote(readFileSync8(file, "utf8"));
14724
14784
  if (!view) {
14725
14785
  log.warn("remote", `${file} names no valid host or previews; remote access is off`);
14726
14786
  return null;
@@ -15512,13 +15572,15 @@ function defaultAgent(wt, d, preview) {
15512
15572
  d.state.save();
15513
15573
  d.hub.emit("worktreesChanged");
15514
15574
  },
15515
- onPlan: () => {
15575
+ onPlan: async (markdown) => {
15516
15576
  const w = d.state.worktree(wt.id);
15517
- if (!w || w.planned)
15518
- return;
15519
- w.planned = true;
15520
- d.state.save();
15577
+ if (w && !w.planned) {
15578
+ w.planned = true;
15579
+ d.state.save();
15580
+ }
15581
+ return markdown.trim() ? writePlanDoc(wt.path, markdown) : null;
15521
15582
  },
15583
+ planEdited: async (proposed) => planEdited(wt.path, proposed),
15522
15584
  option: (category) => d.state.requireWorktree(wt.id)[OPTION_FIELDS[category]],
15523
15585
  onOptionsLearned: (category, choices) => {
15524
15586
  const agentId = d.state.requireWorktree(wt.id).agent ?? "";
@@ -16389,20 +16451,20 @@ function makePlanner(agents) {
16389
16451
  }
16390
16452
 
16391
16453
  // ../daemon/src/core/assets.ts
16392
- import { existsSync as existsSync9, readdirSync as readdirSync2, rmSync, statSync as statSync2 } from "fs";
16393
- import { join as join7, resolve as resolve5 } from "path";
16454
+ import { existsSync as existsSync10, readdirSync as readdirSync2, rmSync, statSync as statSync2 } from "fs";
16455
+ import { join as join8, resolve as resolve5 } from "path";
16394
16456
  function locateAssets(here, env = process.env) {
16395
- const packaged = existsSync9(join7(here, "shell", "index.html"));
16457
+ const packaged = existsSync10(join8(here, "shell", "index.html"));
16396
16458
  return {
16397
- shellDist: env.TOYON_SHELL_DIST ?? (packaged ? join7(here, "shell") : join7(here, "../../shell/dist")),
16398
- bridgeJs: env.TOYON_BRIDGE_JS ?? (packaged ? join7(here, "bridge.js") : join7(here, "../../bridge/dist/bridge.js")),
16459
+ shellDist: env.TOYON_SHELL_DIST ?? (packaged ? join8(here, "shell") : join8(here, "../../shell/dist")),
16460
+ bridgeJs: env.TOYON_BRIDGE_JS ?? (packaged ? join8(here, "bridge.js") : join8(here, "../../bridge/dist/bridge.js")),
16399
16461
  sourceRoot: packaged ? null : resolve5(here, "../../.."),
16400
- packageJson: packaged ? join7(here, "..", "package.json") : null
16462
+ packageJson: packaged ? join8(here, "..", "package.json") : null
16401
16463
  };
16402
16464
  }
16403
16465
  var KEEP_MS = 3 * 24 * 60 * 60 * 1000;
16404
16466
  function pruneAssets(shellDist) {
16405
- const dir = join7(shellDist, "assets");
16467
+ const dir = join8(shellDist, "assets");
16406
16468
  let names;
16407
16469
  try {
16408
16470
  names = readdirSync2(dir);
@@ -16411,7 +16473,7 @@ function pruneAssets(shellDist) {
16411
16473
  }
16412
16474
  const found = [];
16413
16475
  for (const name of names) {
16414
- const path = join7(dir, name);
16476
+ const path = join8(dir, name);
16415
16477
  try {
16416
16478
  found.push({ path, mtime: statSync2(path).mtimeMs });
16417
16479
  } catch {}
@@ -16432,7 +16494,7 @@ function pruneAssets(shellDist) {
16432
16494
  }
16433
16495
 
16434
16496
  // ../daemon/src/core/dialog.ts
16435
- import { existsSync as existsSync10 } from "fs";
16497
+ import { existsSync as existsSync11 } from "fs";
16436
16498
  import { homedir as homedir4 } from "os";
16437
16499
  var PROMPTS = {
16438
16500
  location: "Choose where the new project goes",
@@ -16450,7 +16512,7 @@ function folderDialog() {
16450
16512
  let cancelled = false;
16451
16513
  const ask = async (start, purpose) => {
16452
16514
  const typed = start === "~" || start.startsWith("~/") ? `${homedir4()}${start.slice(1)}` : start;
16453
- const from = typed.startsWith("/") && existsSync10(typed) ? typed : homedir4();
16515
+ const from = typed.startsWith("/") && existsSync11(typed) ? typed : homedir4();
16454
16516
  cancelled = false;
16455
16517
  const p = Bun.spawn(["osascript", ...SCRIPT.flatMap((line) => ["-e", line]), PROMPTS[purpose], from], {
16456
16518
  stdout: "pipe",
@@ -16667,17 +16729,17 @@ class SelfWatch {
16667
16729
 
16668
16730
  // ../daemon/src/core/state.ts
16669
16731
  import { randomBytes } from "crypto";
16670
- import { existsSync as existsSync11, readFileSync as readFileSync8, renameSync as renameSync2, writeFileSync as writeFileSync4 } from "fs";
16732
+ import { existsSync as existsSync12, readFileSync as readFileSync9, renameSync as renameSync2, writeFileSync as writeFileSync5 } from "fs";
16671
16733
  var SEEN_WORKTREES = 50;
16672
16734
  var empty = { repos: [], worktrees: [], sessions: {} };
16673
16735
  function loadState(paths) {
16674
16736
  ensureDirs(paths);
16675
16737
  const STATE_FILE = paths.stateFile;
16676
- if (!existsSync11(STATE_FILE))
16738
+ if (!existsSync12(STATE_FILE))
16677
16739
  return structuredClone(empty);
16678
16740
  let raw;
16679
16741
  try {
16680
- raw = readFileSync8(STATE_FILE, "utf8");
16742
+ raw = readFileSync9(STATE_FILE, "utf8");
16681
16743
  } catch (e) {
16682
16744
  log.error("state", `cannot read ${STATE_FILE}; starting empty`, e);
16683
16745
  return structuredClone(empty);
@@ -16688,7 +16750,7 @@ function loadState(paths) {
16688
16750
  } catch (e) {
16689
16751
  const backup = `${STATE_FILE}.corrupt-${Date.now()}`;
16690
16752
  try {
16691
- writeFileSync4(backup, raw);
16753
+ writeFileSync5(backup, raw);
16692
16754
  } catch (be) {
16693
16755
  log.warn("state", "could not write the corrupt-state backup", be);
16694
16756
  }
@@ -16715,16 +16777,16 @@ function loadState(paths) {
16715
16777
  }
16716
16778
  function saveState(paths, state) {
16717
16779
  const tmp = `${paths.stateFile}.tmp`;
16718
- writeFileSync4(tmp, JSON.stringify(state, null, 2));
16780
+ writeFileSync5(tmp, JSON.stringify(state, null, 2));
16719
16781
  renameSync2(tmp, paths.stateFile);
16720
16782
  }
16721
16783
  function loadOrCreateToken(paths) {
16722
16784
  ensureDirs(paths);
16723
16785
  const TOKEN_FILE = paths.tokenFile;
16724
- if (existsSync11(TOKEN_FILE))
16725
- return readFileSync8(TOKEN_FILE, "utf8").trim();
16786
+ if (existsSync12(TOKEN_FILE))
16787
+ return readFileSync9(TOKEN_FILE, "utf8").trim();
16726
16788
  const token = randomBytes(32).toString("hex");
16727
- writeFileSync4(TOKEN_FILE, token, { mode: 384 });
16789
+ writeFileSync5(TOKEN_FILE, token, { mode: 384 });
16728
16790
  return token;
16729
16791
  }
16730
16792
 
@@ -16878,7 +16940,7 @@ class StateStore {
16878
16940
  }
16879
16941
 
16880
16942
  // ../daemon/src/design/service.ts
16881
- import { join as join8 } from "path";
16943
+ import { join as join9 } from "path";
16882
16944
 
16883
16945
  // ../daemon/src/design/scan.ts
16884
16946
  var COLOR = /^(#|rgba?\(|hsla?\(|oklch\(|oklab\(|lab\(|lch\(|color\(|color-mix\(|light-dark\()/i;
@@ -17328,7 +17390,7 @@ class DesignService {
17328
17390
  async function readAll(root, paths) {
17329
17391
  const out = [];
17330
17392
  for (const path of paths) {
17331
- const file = Bun.file(join8(root, path));
17393
+ const file = Bun.file(join9(root, path));
17332
17394
  if (file.size === 0 || file.size > MAX_FILE_BYTES)
17333
17395
  continue;
17334
17396
  try {
@@ -17443,14 +17505,14 @@ function typicalUse(classes) {
17443
17505
  var normal = (s) => s.replace(/[-_]/g, "").toLowerCase();
17444
17506
  async function loadProjectTypescript(root) {
17445
17507
  try {
17446
- return await import(join8(root, "node_modules", "typescript", "lib", "typescript.js"));
17508
+ return await import(join9(root, "node_modules", "typescript", "lib", "typescript.js"));
17447
17509
  } catch {
17448
17510
  return null;
17449
17511
  }
17450
17512
  }
17451
17513
 
17452
17514
  // ../daemon/src/drafts/store.ts
17453
- import { existsSync as existsSync12, readFileSync as readFileSync9, renameSync as renameSync3, writeFileSync as writeFileSync5 } from "fs";
17515
+ import { existsSync as existsSync13, readFileSync as readFileSync10, renameSync as renameSync3, writeFileSync as writeFileSync6 } from "fs";
17454
17516
  var SAVE_DELAY_MS = 1000;
17455
17517
 
17456
17518
  class DraftStore {
@@ -17459,10 +17521,10 @@ class DraftStore {
17459
17521
  saveTimer = null;
17460
17522
  constructor(d) {
17461
17523
  this.d = d;
17462
- if (!existsSync12(d.file))
17524
+ if (!existsSync13(d.file))
17463
17525
  return;
17464
17526
  try {
17465
- const raw = JSON.parse(readFileSync9(d.file, "utf8"));
17527
+ const raw = JSON.parse(readFileSync10(d.file, "utf8"));
17466
17528
  if (!raw || typeof raw !== "object")
17467
17529
  return;
17468
17530
  for (const [id, text] of Object.entries(raw)) {
@@ -17522,7 +17584,7 @@ class DraftStore {
17522
17584
  write() {
17523
17585
  const tmp = `${this.d.file}.tmp`;
17524
17586
  try {
17525
- writeFileSync5(tmp, JSON.stringify(this.drafts));
17587
+ writeFileSync6(tmp, JSON.stringify(this.drafts));
17526
17588
  renameSync3(tmp, this.d.file);
17527
17589
  } catch (e) {
17528
17590
  log.error("drafts", "could not write the drafts", e);
@@ -17687,7 +17749,7 @@ function withRepoLock(repoPath, fn) {
17687
17749
  var fileLockKey = (absPath) => `file:${absPath}`;
17688
17750
 
17689
17751
  // ../daemon/src/git/status.ts
17690
- import { join as join9 } from "path";
17752
+ import { join as join10 } from "path";
17691
17753
  async function treeFingerprint(worktreePath) {
17692
17754
  const [head, diff, untracked] = await Promise.all([
17693
17755
  git(worktreePath, "rev-parse", "HEAD"),
@@ -17697,7 +17759,7 @@ async function treeFingerprint(worktreePath) {
17697
17759
  const others = await Promise.all(untracked.out.split(`
17698
17760
  `).filter(Boolean).map(async (p) => {
17699
17761
  try {
17700
- const f = Bun.file(join9(worktreePath, p));
17762
+ const f = Bun.file(join10(worktreePath, p));
17701
17763
  const body = f.size > 2000000 ? String(f.size) : Bun.hash(await f.arrayBuffer()).toString(36);
17702
17764
  return `${p}:${body}`;
17703
17765
  } catch {
@@ -17748,7 +17810,7 @@ async function numstat(worktreePath, ...range) {
17748
17810
  }
17749
17811
  async function untrackedLines(worktreePath, file) {
17750
17812
  try {
17751
- const f = Bun.file(join9(worktreePath, file));
17813
+ const f = Bun.file(join10(worktreePath, file));
17752
17814
  if (f.size > 2000000)
17753
17815
  return {};
17754
17816
  const buf = new Uint8Array(await f.arrayBuffer());
@@ -17767,14 +17829,16 @@ async function untrackedLines(worktreePath, file) {
17767
17829
  return {};
17768
17830
  }
17769
17831
  }
17832
+ var MAX_UNTRACKED_COUNTED = 500;
17770
17833
  async function statusFilesWithCounts(worktreePath) {
17771
17834
  const files = await statusFiles(worktreePath);
17772
17835
  if (files.length === 0)
17773
17836
  return files;
17774
17837
  const counts = files.some((f) => f.xy !== "??") ? await numstat(worktreePath, "HEAD") : new Map;
17838
+ const countUntracked = files.filter((f) => f.xy === "??").length <= MAX_UNTRACKED_COUNTED;
17775
17839
  return Promise.all(files.map(async (f) => ({
17776
17840
  ...f,
17777
- ...f.xy === "??" ? await untrackedLines(worktreePath, f.path) : counts.get(f.path) ?? {}
17841
+ ...f.xy === "??" ? countUntracked ? await untrackedLines(worktreePath, f.path) : {} : counts.get(f.path) ?? {}
17778
17842
  })));
17779
17843
  }
17780
17844
  async function committedFiles(worktreePath, defaultBr) {
@@ -18504,9 +18568,9 @@ class AfterLand {
18504
18568
  }
18505
18569
 
18506
18570
  // ../daemon/src/repos/registry.ts
18507
- import { existsSync as existsSync17, mkdirSync as mkdirSync5, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "fs";
18571
+ import { existsSync as existsSync18, mkdirSync as mkdirSync6, rmSync as rmSync2, writeFileSync as writeFileSync7 } from "fs";
18508
18572
  import { homedir as homedir7 } from "os";
18509
- import { basename as basename3, dirname as dirname6, join as join14 } from "path";
18573
+ import { basename as basename3, dirname as dirname6, join as join15 } from "path";
18510
18574
 
18511
18575
  // ../daemon/src/worktrees/naming.ts
18512
18576
  import { randomBytes as randomBytes2 } from "crypto";
@@ -18532,13 +18596,13 @@ function variantLens(index) {
18532
18596
  }
18533
18597
 
18534
18598
  // ../daemon/src/repos/browse.ts
18535
- import { existsSync as existsSync13, statSync as statSync3 } from "fs";
18599
+ import { existsSync as existsSync14, statSync as statSync3 } from "fs";
18536
18600
  import { readdir } from "fs/promises";
18537
18601
  import { homedir as homedir5 } from "os";
18538
- import { dirname as dirname4, join as join10 } from "path";
18602
+ import { dirname as dirname4, join as join11 } from "path";
18539
18603
  var MAX_ENTRIES = 40;
18540
18604
  function expandTilde(raw) {
18541
- return raw === "~" || raw.startsWith("~/") ? join10(homedir5(), raw.slice(1)) : raw;
18605
+ return raw === "~" || raw.startsWith("~/") ? join11(homedir5(), raw.slice(1)) : raw;
18542
18606
  }
18543
18607
  function collapseTilde(abs) {
18544
18608
  const home = homedir5();
@@ -18549,7 +18613,7 @@ function holdsNothing(names) {
18549
18613
  }
18550
18614
  async function describeFolder(abs) {
18551
18615
  const path = collapseTilde(abs);
18552
- if (existsSync13(join10(abs, ".git")))
18616
+ if (existsSync14(join11(abs, ".git")))
18553
18617
  return { path, kind: "project" };
18554
18618
  try {
18555
18619
  return { path, kind: holdsNothing(await readdir(abs)) ? "empty" : "folder" };
@@ -18564,10 +18628,10 @@ function describe2(typed, parent) {
18564
18628
  isDir = statSync3(typed).isDirectory();
18565
18629
  } catch {}
18566
18630
  return {
18567
- exists: existsSync13(typed),
18631
+ exists: existsSync14(typed),
18568
18632
  isDir,
18569
- isRepo: isDir && existsSync13(join10(typed, ".git")),
18570
- parentExists: existsSync13(parent)
18633
+ isRepo: isDir && existsSync14(join11(typed, ".git")),
18634
+ parentExists: existsSync14(parent)
18571
18635
  };
18572
18636
  }
18573
18637
  async function browsePath(raw) {
@@ -18591,16 +18655,16 @@ async function browsePath(raw) {
18591
18655
  continue;
18592
18656
  if (!name.toLowerCase().startsWith(prefix))
18593
18657
  continue;
18594
- const abs = join10(dir, name);
18595
- entries.push({ path: collapseTilde(abs), name, isRepo: existsSync13(join10(abs, ".git")) });
18658
+ const abs = join11(dir, name);
18659
+ entries.push({ path: collapseTilde(abs), name, isRepo: existsSync14(join11(abs, ".git")) });
18596
18660
  }
18597
18661
  entries.sort((a, b) => a.isRepo !== b.isRepo ? a.isRepo ? -1 : 1 : a.name.localeCompare(b.name));
18598
18662
  return { entries: entries.slice(0, MAX_ENTRIES), target };
18599
18663
  }
18600
18664
 
18601
18665
  // ../daemon/src/repos/config.ts
18602
- import { closeSync, existsSync as existsSync14, openSync as openSync2, readdirSync as readdirSync3, readFileSync as readFileSync10, readSync } from "fs";
18603
- import { join as join11 } from "path";
18666
+ import { closeSync, existsSync as existsSync15, openSync as openSync2, readdirSync as readdirSync3, readFileSync as readFileSync11, readSync } from "fs";
18667
+ import { join as join12 } from "path";
18604
18668
 
18605
18669
  // ../../node_modules/.bun/jsonc-parser@3.3.1/node_modules/jsonc-parser/lib/esm/impl/scanner.js
18606
18670
  function createScanner(text, ignoreTrivia = false) {
@@ -19964,7 +20028,7 @@ function applyEdits(text, edits) {
19964
20028
 
19965
20029
  // ../daemon/src/repos/config.ts
19966
20030
  var PLACES = [CONFIG_FILES.folder, CONFIG_FILES.root];
19967
- var present = (repoPath, place) => [place.shared, place.local].filter((rel) => existsSync14(join11(repoPath, rel)));
20031
+ var present = (repoPath, place) => [place.shared, place.local].filter((rel) => existsSync15(join12(repoPath, rel)));
19968
20032
  var isObject2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
19969
20033
  function parseSettings(text) {
19970
20034
  const errors = [];
@@ -19989,7 +20053,7 @@ function readConfigFile(repoPath) {
19989
20053
  for (const rel of files) {
19990
20054
  let raw;
19991
20055
  try {
19992
- raw = parseSettings(readFileSync10(join11(repoPath, rel), "utf8"));
20056
+ raw = parseSettings(readFileSync11(join12(repoPath, rel), "utf8"));
19993
20057
  } catch (e) {
19994
20058
  return { ok: false, reason: `${rel} is not valid JSON: ${e instanceof Error ? e.message : String(e)}` };
19995
20059
  }
@@ -20046,12 +20110,12 @@ function configTarget(repoPath, made) {
20046
20110
  }
20047
20111
  function configBody(repoPath, rel, config) {
20048
20112
  const place = PLACES.find((p) => p.local === rel);
20049
- const abs = place && join11(repoPath, place.shared);
20050
- if (!abs || !existsSync14(abs))
20113
+ const abs = place && join12(repoPath, place.shared);
20114
+ if (!abs || !existsSync15(abs))
20051
20115
  return config;
20052
20116
  let shared;
20053
20117
  try {
20054
- shared = parseSettings(readFileSync10(abs, "utf8"));
20118
+ shared = parseSettings(readFileSync11(abs, "utf8"));
20055
20119
  } catch {
20056
20120
  return config;
20057
20121
  }
@@ -20064,7 +20128,7 @@ function configText(repoPath, rel, config) {
20064
20128
  let text;
20065
20129
  let was;
20066
20130
  try {
20067
- text = readFileSync10(join11(repoPath, rel), "utf8");
20131
+ text = readFileSync11(join12(repoPath, rel), "utf8");
20068
20132
  was = parseSettings(text);
20069
20133
  } catch {
20070
20134
  return fresh;
@@ -20094,9 +20158,9 @@ function detectConfig(repoPath) {
20094
20158
  return { config: explicit.config, needsSetup: false };
20095
20159
  if (explicit)
20096
20160
  log.warn("config", `${repoPath}: ${explicit.reason}; detecting instead`);
20097
- const pkgPath = join11(repoPath, "package.json");
20098
- if (existsSync14(pkgPath)) {
20099
- const pkg = JSON.parse(readFileSync10(pkgPath, "utf8"));
20161
+ const pkgPath = join12(repoPath, "package.json");
20162
+ if (existsSync15(pkgPath)) {
20163
+ const pkg = JSON.parse(readFileSync11(pkgPath, "utf8"));
20100
20164
  const scripts = pkg.scripts ?? {};
20101
20165
  const runner = detectRunner(repoPath, pkg.packageManager);
20102
20166
  const pages = detectPages(repoPath, scripts, runner);
@@ -20117,7 +20181,7 @@ function detectConfig(repoPath) {
20117
20181
  };
20118
20182
  }
20119
20183
  }
20120
- if (existsSync14(join11(repoPath, "start.sh"))) {
20184
+ if (existsSync15(join12(repoPath, "start.sh"))) {
20121
20185
  return { config: { run: { app: "./start.sh" } }, needsSetup: true, from: "start.sh" };
20122
20186
  }
20123
20187
  const assumed = assumeNothingToRun(repoPath);
@@ -20160,7 +20224,7 @@ function assumeNothingToRun(repoPath) {
20160
20224
  return marker && !servesPage(repoPath) ? marker : undefined;
20161
20225
  }
20162
20226
  function markerIn(repoPath) {
20163
- const named = NO_PAGE_MARKERS.find((f) => existsSync14(join11(repoPath, f)));
20227
+ const named = NO_PAGE_MARKERS.find((f) => existsSync15(join12(repoPath, f)));
20164
20228
  if (named)
20165
20229
  return named;
20166
20230
  let entries;
@@ -20172,12 +20236,12 @@ function markerIn(repoPath) {
20172
20236
  return entries.find((f) => NO_PAGE_EXTENSIONS.some((ext) => f.endsWith(ext)));
20173
20237
  }
20174
20238
  function servesPage(repoPath) {
20175
- if (WEB_FILES.some((f) => existsSync14(join11(repoPath, f))))
20239
+ if (WEB_FILES.some((f) => existsSync15(join12(repoPath, f))))
20176
20240
  return true;
20177
- if (/^web:/m.test(head(join11(repoPath, "Procfile"))))
20241
+ if (/^web:/m.test(head(join12(repoPath, "Procfile"))))
20178
20242
  return true;
20179
20243
  for (const [files, names] of WEB_DEPENDENCIES) {
20180
- const text = files.map((f) => head(join11(repoPath, f))).join(`
20244
+ const text = files.map((f) => head(join12(repoPath, f))).join(`
20181
20245
  `);
20182
20246
  if (text.trim() && names.some((n) => mentions(text, n)))
20183
20247
  return true;
@@ -20194,7 +20258,7 @@ function frontEndBeside(repoPath) {
20194
20258
  return dirs.some((d) => {
20195
20259
  if (!d.isDirectory() || d.name.startsWith(".") || NOT_A_PACKAGE.has(d.name))
20196
20260
  return false;
20197
- const pkg = head(join11(repoPath, d.name, "package.json"));
20261
+ const pkg = head(join12(repoPath, d.name, "package.json"));
20198
20262
  if (!pkg)
20199
20263
  return false;
20200
20264
  try {
@@ -20209,7 +20273,7 @@ function mentions(text, name) {
20209
20273
  return new RegExp(`(?<![\\w-])${escaped}(?![\\w-])`, "i").test(text);
20210
20274
  }
20211
20275
  function head(path) {
20212
- if (!existsSync14(path))
20276
+ if (!existsSync15(path))
20213
20277
  return "";
20214
20278
  try {
20215
20279
  const fd = openSync2(path, "r");
@@ -20237,31 +20301,31 @@ function procCommand(runner, script, body) {
20237
20301
  return `${base}${runner === "npm" ? " --" : ""} ${flag}`;
20238
20302
  }
20239
20303
  function detectPages(repoPath, scripts, runner) {
20240
- if (!scripts.build || !existsSync14(join11(repoPath, "functions")))
20304
+ if (!scripts.build || !existsSync15(join12(repoPath, "functions")))
20241
20305
  return null;
20242
- const cfg = ["wrangler.jsonc", "wrangler.json", "wrangler.toml"].map((f) => join11(repoPath, f)).find((f) => existsSync14(f));
20306
+ const cfg = ["wrangler.jsonc", "wrangler.json", "wrangler.toml"].map((f) => join12(repoPath, f)).find((f) => existsSync15(f));
20243
20307
  if (!cfg)
20244
20308
  return null;
20245
- const out = readFileSync10(cfg, "utf8").match(/pages_build_output_dir"?\s*[:=]\s*"([^"]+)"/)?.[1];
20309
+ const out = readFileSync11(cfg, "utf8").match(/pages_build_output_dir"?\s*[:=]\s*"([^"]+)"/)?.[1];
20246
20310
  if (!out)
20247
20311
  return null;
20248
20312
  return { web: `${runner} run build && npx wrangler pages dev ${out} --ip 127.0.0.1 --port $PORT` };
20249
20313
  }
20250
20314
  function detectRunner(repoPath, packageManager) {
20251
- if (existsSync14(join11(repoPath, "bun.lock")) || existsSync14(join11(repoPath, "bun.lockb")))
20315
+ if (existsSync15(join12(repoPath, "bun.lock")) || existsSync15(join12(repoPath, "bun.lockb")))
20252
20316
  return "bun";
20253
- if (existsSync14(join11(repoPath, "pnpm-lock.yaml")))
20317
+ if (existsSync15(join12(repoPath, "pnpm-lock.yaml")))
20254
20318
  return "pnpm";
20255
- if (existsSync14(join11(repoPath, "yarn.lock")) || typeof packageManager === "string" && packageManager.startsWith("yarn"))
20319
+ if (existsSync15(join12(repoPath, "yarn.lock")) || typeof packageManager === "string" && packageManager.startsWith("yarn"))
20256
20320
  return "yarn";
20257
20321
  return "npm";
20258
20322
  }
20259
20323
 
20260
20324
  // ../daemon/src/repos/create.ts
20261
- import { existsSync as existsSync15, readdirSync as readdirSync4, realpathSync as realpathSync2, statSync as statSync4 } from "fs";
20325
+ import { existsSync as existsSync16, readdirSync as readdirSync4, realpathSync as realpathSync2, statSync as statSync4 } from "fs";
20262
20326
  import { appendFile as appendFile2, mkdir as mkdir2, rm } from "fs/promises";
20263
20327
  import { homedir as homedir6 } from "os";
20264
- import { dirname as dirname5, join as join12, resolve as resolve7 } from "path";
20328
+ import { dirname as dirname5, join as join13, resolve as resolve7 } from "path";
20265
20329
  function isInside(dir, parent) {
20266
20330
  const p = parent.replace(/\/+$/, "");
20267
20331
  return dir === p || dir.startsWith(`${p}/`);
@@ -20272,27 +20336,27 @@ function planProject(opts, home = homedir6()) {
20272
20336
  throw new UserError(nameError);
20273
20337
  const first = firstProjectsFolder(opts.parent, home);
20274
20338
  const parent = first ?? resolveParent(opts.parent);
20275
- const dir = join12(parent, opts.name.trim());
20276
- if (existsSync15(dir))
20339
+ const dir = join13(parent, opts.name.trim());
20340
+ if (existsSync16(dir))
20277
20341
  throw new UserError(`${dir} already exists`);
20278
20342
  return { parent, dir, makeParent: first !== null };
20279
20343
  }
20280
20344
  function firstProjectsFolder(raw, home) {
20281
20345
  const typed = expandTilde(raw.trim()).replace(/\/+$/, "");
20282
- if (typed !== join12(home, PROJECTS_FOLDER) || existsSync15(typed) || !existsSync15(home))
20346
+ if (typed !== join13(home, PROJECTS_FOLDER) || existsSync16(typed) || !existsSync16(home))
20283
20347
  return null;
20284
- return join12(realpathSync2(home), PROJECTS_FOLDER);
20348
+ return join13(realpathSync2(home), PROJECTS_FOLDER);
20285
20349
  }
20286
20350
  function planInPlace(opts) {
20287
20351
  const name = opts.name.trim();
20288
20352
  if (!name || name === "." || name === ".." || name.includes("/")) {
20289
20353
  throw new UserError(`${opts.name} is not a folder name`);
20290
20354
  }
20291
- const typed = join12(resolveParent(opts.parent), name);
20292
- if (!existsSync15(typed) || !statSync4(typed).isDirectory())
20355
+ const typed = join13(resolveParent(opts.parent), name);
20356
+ if (!existsSync16(typed) || !statSync4(typed).isDirectory())
20293
20357
  throw new UserError(`${typed} is not a folder`);
20294
20358
  const dir = realpathSync2(typed);
20295
- if (existsSync15(join12(dir, ".git")))
20359
+ if (existsSync16(join13(dir, ".git")))
20296
20360
  throw new UserError(`${dir} is already a project; open it instead`);
20297
20361
  if (!holdsNothing(readdirSync4(dir))) {
20298
20362
  throw new UserError(`${dir} is not empty; only an empty folder can become a project where it is`);
@@ -20303,7 +20367,7 @@ function resolveParent(raw) {
20303
20367
  const typed = expandTilde(raw.trim());
20304
20368
  if (!typed.startsWith("/"))
20305
20369
  throw new UserError(`${raw} is not a folder path`);
20306
- if (!existsSync15(typed))
20370
+ if (!existsSync16(typed))
20307
20371
  throw new UserError(`${raw} does not exist; make it first`);
20308
20372
  if (!statSync4(typed).isDirectory())
20309
20373
  throw new UserError(`${raw} is not a folder`);
@@ -20383,13 +20447,13 @@ async function undoCreate(dir) {
20383
20447
  }
20384
20448
  async function undoInit(dir) {
20385
20449
  try {
20386
- await rm(join12(dir, ".git"), { recursive: true, force: true });
20450
+ await rm(join13(dir, ".git"), { recursive: true, force: true });
20387
20451
  } catch (e) {
20388
20452
  log.warn(dir, "could not take back a half-made .git", e);
20389
20453
  }
20390
20454
  }
20391
20455
  async function unmakeProject(dir, made) {
20392
- const target = made === "folder" ? dir : join12(dir, ".git");
20456
+ const target = made === "folder" ? dir : join13(dir, ".git");
20393
20457
  try {
20394
20458
  await rm(target, { recursive: true, force: true });
20395
20459
  } catch (e) {
@@ -20419,8 +20483,8 @@ async function ok(p, what) {
20419
20483
  }
20420
20484
 
20421
20485
  // ../daemon/src/repos/watcher.ts
20422
- import { existsSync as existsSync16, watch } from "fs";
20423
- import { basename as basename2, join as join13 } from "path";
20486
+ import { existsSync as existsSync17, watch } from "fs";
20487
+ import { basename as basename2, join as join14 } from "path";
20424
20488
  function watchDefaultBranch(repoPath, branch, onMove) {
20425
20489
  let last = null;
20426
20490
  fireAndForget(repoPath, git(repoPath, "rev-parse", branch).then((r) => {
@@ -20450,8 +20514,8 @@ function watchDefaultBranch(repoPath, branch, onMove) {
20450
20514
  log.debug(repoPath, `not watching ${p}`, e);
20451
20515
  }
20452
20516
  };
20453
- tryWatch(join13(repoPath, ".git"));
20454
- tryWatch(join13(repoPath, ".git", "refs", "heads"));
20517
+ tryWatch(join14(repoPath, ".git"));
20518
+ tryWatch(join14(repoPath, ".git", "refs", "heads"));
20455
20519
  return () => {
20456
20520
  for (const w of watchers)
20457
20521
  w.close();
@@ -20473,7 +20537,7 @@ function watchWorktreeDir(repoPath, onChange) {
20473
20537
  }, 300);
20474
20538
  };
20475
20539
  const arm = (commonDir) => {
20476
- const dir = join13(commonDir, "worktrees");
20540
+ const dir = join14(commonDir, "worktrees");
20477
20541
  const armInner = () => {
20478
20542
  inner?.close();
20479
20543
  inner = null;
@@ -20516,7 +20580,7 @@ function watchWorktreeDir(repoPath, onChange) {
20516
20580
  function watchConfigFile(repoPath, onChange) {
20517
20581
  const atRoot = new Set([CONFIG_FILES.root.shared, CONFIG_FILES.root.local, CONFIG_DIR]);
20518
20582
  const inDir = new Set([CONFIG_FILES.folder.shared, CONFIG_FILES.folder.local].map((rel) => basename2(rel)));
20519
- const dir = join13(repoPath, CONFIG_DIR);
20583
+ const dir = join14(repoPath, CONFIG_DIR);
20520
20584
  let timer = null;
20521
20585
  let outer = null;
20522
20586
  let inner = null;
@@ -20531,7 +20595,7 @@ function watchConfigFile(repoPath, onChange) {
20531
20595
  const armInner = () => {
20532
20596
  inner?.close();
20533
20597
  inner = null;
20534
- if (!existsSync16(dir))
20598
+ if (!existsSync17(dir))
20535
20599
  return;
20536
20600
  try {
20537
20601
  inner = watch(dir, (_event, filename) => {
@@ -20585,7 +20649,7 @@ class RepoRegistry {
20585
20649
  }
20586
20650
  async boot() {
20587
20651
  const { state } = this.d;
20588
- const gone = state.worktrees.filter((wt) => !existsSync17(wt.path) || !state.repos.some((r) => r.id === wt.repoId));
20652
+ const gone = state.worktrees.filter((wt) => !existsSync18(wt.path) || !state.repos.some((r) => r.id === wt.repoId));
20589
20653
  for (const wt of gone)
20590
20654
  await this.d.worktrees.forgetGone(wt);
20591
20655
  for (const repo of state.repos) {
@@ -20652,7 +20716,7 @@ class RepoRegistry {
20652
20716
  return `${repo.name} is set up now`;
20653
20717
  if (main.promptedAt || main.lastTurn)
20654
20718
  return `${repo.name} has a chat now`;
20655
- if (existsSync17(join14(repo.path, ".claude")))
20719
+ if (existsSync18(join15(repo.path, ".claude")))
20656
20720
  return `an agent has already run in ${repo.name}`;
20657
20721
  if ((await statusFiles(repo.path)).length > 0 || !await treeEmpty(repo.path)) {
20658
20722
  return `${repo.name} has files in it now`;
@@ -20729,7 +20793,7 @@ class RepoRegistry {
20729
20793
  }
20730
20794
  async register(rawPath, made) {
20731
20795
  const path = expandTilde(rawPath);
20732
- if (!existsSync17(path))
20796
+ if (!existsSync18(path))
20733
20797
  throw new UserError(`${path} does not exist`);
20734
20798
  if (!await isGitRepo(path))
20735
20799
  throw new UserError(`${path} is not a git repository`);
@@ -20802,10 +20866,10 @@ class RepoRegistry {
20802
20866
  throw new UserError(current.reason);
20803
20867
  const was = configTarget(repo.path, !!repo.made);
20804
20868
  const rel = configSibling(was, kind);
20805
- const moved = rel !== was && existsSync17(join14(repo.path, was));
20869
+ const moved = rel !== was && existsSync18(join15(repo.path, was));
20806
20870
  if (moved && !(kind === "local" && await isTracked(repo.path, was))) {
20807
20871
  try {
20808
- rmSync2(join14(repo.path, was));
20872
+ rmSync2(join15(repo.path, was));
20809
20873
  } catch (e) {
20810
20874
  log.warn(repoId, `could not remove ${was}`, e);
20811
20875
  }
@@ -20814,10 +20878,10 @@ class RepoRegistry {
20814
20878
  }
20815
20879
  if (isLocalConfigFile(rel))
20816
20880
  await excludeFromGit(repo.path, rel);
20817
- const file = join14(repo.path, rel);
20881
+ const file = join15(repo.path, rel);
20818
20882
  try {
20819
- mkdirSync5(dirname6(file), { recursive: true });
20820
- writeFileSync6(file, configText(repo.path, rel, config));
20883
+ mkdirSync6(dirname6(file), { recursive: true });
20884
+ writeFileSync7(file, configText(repo.path, rel, config));
20821
20885
  } catch (e) {
20822
20886
  log.warn(repoId, `could not write ${rel}`, e);
20823
20887
  }
@@ -20891,7 +20955,7 @@ class RepoRegistry {
20891
20955
  }
20892
20956
  placeConfig(repo) {
20893
20957
  const rel = configTarget(repo.path, !!repo.made);
20894
- if (isLocalConfigFile(rel) && existsSync17(join14(repo.path, rel))) {
20958
+ if (isLocalConfigFile(rel) && existsSync18(join15(repo.path, rel))) {
20895
20959
  fireAndForget(repo.id, excludeFromGit(repo.path, rel), "exclude local settings");
20896
20960
  }
20897
20961
  if (repo.configFile === rel)
@@ -20966,7 +21030,7 @@ class RepoRegistry {
20966
21030
  }
20967
21031
 
20968
21032
  // ../daemon/src/routes/service.ts
20969
- import { join as join15 } from "path";
21033
+ import { join as join16 } from "path";
20970
21034
 
20971
21035
  // ../daemon/src/routes/fileRouters.ts
20972
21036
  var SKIP = /(^|\/)(node_modules|dist|build|out|coverage|\.next|\.nuxt|\.output|\.svelte-kit|\.vercel|\.netlify)\//;
@@ -22166,7 +22230,7 @@ class RouteService {
22166
22230
  return mergeRoutes(fileRoutes(paths, roots), code);
22167
22231
  }
22168
22232
  async cachedRead(root, path, had, kept, decode) {
22169
- const abs = join15(root, path);
22233
+ const abs = join16(root, path);
22170
22234
  const stat = await this.fs.stat(abs);
22171
22235
  if (!stat)
22172
22236
  return null;
@@ -22191,7 +22255,7 @@ class RouteService {
22191
22255
  }
22192
22256
  }
22193
22257
  async hash(root, file) {
22194
- const abs = join15(root, file);
22258
+ const abs = join16(root, file);
22195
22259
  const stat = await this.fs.stat(abs);
22196
22260
  if (!stat)
22197
22261
  return null;
@@ -22235,7 +22299,7 @@ class RouteService {
22235
22299
  }
22236
22300
 
22237
22301
  // ../daemon/src/runtime/bridge-script.ts
22238
- import { existsSync as existsSync18, readFileSync as readFileSync11, statSync as statSync5 } from "fs";
22302
+ import { existsSync as existsSync19, readFileSync as readFileSync12, statSync as statSync5 } from "fs";
22239
22303
  var LEARNED_MAX = 8;
22240
22304
 
22241
22305
  class BridgeScript {
@@ -22265,7 +22329,7 @@ class BridgeScript {
22265
22329
  this.cache = null;
22266
22330
  }
22267
22331
  get() {
22268
- if (!existsSync18(this.path))
22332
+ if (!existsSync19(this.path))
22269
22333
  return "// toyon bridge not built";
22270
22334
  const s = statSync5(this.path);
22271
22335
  const stamp = `${s.mtimeMs}:${s.size}`;
@@ -22274,7 +22338,7 @@ class BridgeScript {
22274
22338
  this.stamp = stamp;
22275
22339
  const origins = [...this.origins, ...this.learned];
22276
22340
  this.cache = `window.__toyonShellOrigins=${JSON.stringify(origins)};
22277
- ${readFileSync11(this.path, "utf8")}`;
22341
+ ${readFileSync12(this.path, "utf8")}`;
22278
22342
  return this.cache;
22279
22343
  }
22280
22344
  }
@@ -22647,50 +22711,50 @@ class PairCodes {
22647
22711
  }
22648
22712
 
22649
22713
  // ../daemon/src/agent/config.ts
22650
- import { existsSync as existsSync19, readFileSync as readFileSync12 } from "fs";
22714
+ import { existsSync as existsSync20, readFileSync as readFileSync13 } from "fs";
22651
22715
  import { homedir as homedir8 } from "os";
22652
- import { join as join16 } from "path";
22716
+ import { join as join17 } from "path";
22653
22717
  function agentConfigFiles(spec, repoPath, home = homedir8()) {
22654
22718
  const file = (id, label, path) => ({
22655
22719
  id,
22656
22720
  label,
22657
22721
  path,
22658
- exists: existsSync19(path)
22722
+ exists: existsSync20(path)
22659
22723
  });
22660
22724
  if (spec.id === "claude") {
22661
22725
  return [
22662
- file("user-config", "user config (MCP servers, projects)", join16(home, ".claude.json")),
22663
- file("user-settings", "user settings (permissions, hooks)", join16(home, ".claude", "settings.json")),
22664
- ...repoPath ? [file("project-mcp", "project MCP servers", join16(repoPath, ".mcp.json"))] : [],
22665
- ...repoPath ? [file("project-settings", "project settings", join16(repoPath, ".claude", "settings.json"))] : []
22726
+ file("user-config", "user config (MCP servers, projects)", join17(home, ".claude.json")),
22727
+ file("user-settings", "user settings (permissions, hooks)", join17(home, ".claude", "settings.json")),
22728
+ ...repoPath ? [file("project-mcp", "project MCP servers", join17(repoPath, ".mcp.json"))] : [],
22729
+ ...repoPath ? [file("project-settings", "project settings", join17(repoPath, ".claude", "settings.json"))] : []
22666
22730
  ];
22667
22731
  }
22668
22732
  if (spec.id === "codex")
22669
- return [file("user-config", "config", join16(home, ".codex", "config.toml"))];
22733
+ return [file("user-config", "config", join17(home, ".codex", "config.toml"))];
22670
22734
  return [];
22671
22735
  }
22672
22736
  function describeAgentConfig(spec, repoPath, home = homedir8()) {
22673
22737
  const files = agentConfigFiles(spec, repoPath, home);
22674
22738
  const servers = [];
22675
22739
  if (spec.id === "claude") {
22676
- const user = readJson(join16(home, ".claude.json"));
22740
+ const user = readJson(join17(home, ".claude.json"));
22677
22741
  servers.push(...serversOf(user?.mcpServers, "user"));
22678
22742
  if (repoPath) {
22679
22743
  const projects = user?.projects;
22680
22744
  const mine = projects?.[repoPath];
22681
22745
  servers.push(...serversOf(mine?.mcpServers, "local"));
22682
- servers.push(...serversOf(readJson(join16(repoPath, ".mcp.json"))?.mcpServers, "project"));
22746
+ servers.push(...serversOf(readJson(join17(repoPath, ".mcp.json"))?.mcpServers, "project"));
22683
22747
  }
22684
22748
  } else if (spec.id === "codex") {
22685
- servers.push(...codexServers(join16(home, ".codex", "config.toml")));
22749
+ servers.push(...codexServers(join17(home, ".codex", "config.toml")));
22686
22750
  }
22687
22751
  return { agent: spec.id, files, servers };
22688
22752
  }
22689
22753
  function readJson(path) {
22690
- if (!existsSync19(path))
22754
+ if (!existsSync20(path))
22691
22755
  return null;
22692
22756
  try {
22693
- const v = JSON.parse(readFileSync12(path, "utf8"));
22757
+ const v = JSON.parse(readFileSync13(path, "utf8"));
22694
22758
  return v && typeof v === "object" && !Array.isArray(v) ? v : null;
22695
22759
  } catch (e) {
22696
22760
  log.warn("agent-config", `${path} is not valid JSON`, e);
@@ -22708,11 +22772,11 @@ function serversOf(raw, scope) {
22708
22772
  });
22709
22773
  }
22710
22774
  function codexServers(path) {
22711
- if (!existsSync19(path))
22775
+ if (!existsSync20(path))
22712
22776
  return [];
22713
22777
  const out = [];
22714
22778
  let current = null;
22715
- for (const raw of readFileSync12(path, "utf8").split(`
22779
+ for (const raw of readFileSync13(path, "utf8").split(`
22716
22780
  `)) {
22717
22781
  const line = raw.trim();
22718
22782
  const header = /^\[mcp_servers\.("?)([^\]"]+)\1\]$/.exec(line);
@@ -23698,8 +23762,8 @@ async function dispatch(msg, ctx, s) {
23698
23762
  }
23699
23763
 
23700
23764
  // ../daemon/src/server/http.ts
23701
- import { existsSync as existsSync20 } from "fs";
23702
- import { join as join17 } from "path";
23765
+ import { existsSync as existsSync21 } from "fs";
23766
+ import { join as join18 } from "path";
23703
23767
  var IMMUTABLE = "private, max-age=31536000, immutable";
23704
23768
  var NO_STORE = "no-store";
23705
23769
  function createFetch(opts) {
@@ -23821,7 +23885,7 @@ function createFetch(opts) {
23821
23885
  return new Response("unauthorized", { status: 401 });
23822
23886
  const [worktreeId, file, extra] = url.pathname.slice("/attachments/".length).split("/");
23823
23887
  const places = worktreeId && file && !extra ? [opts.attachments.fileFor(worktreeId, file), opts.archivedAttachment(worktreeId, file)] : [];
23824
- const path = places.find((p) => !!p && existsSync20(p));
23888
+ const path = places.find((p) => !!p && existsSync21(p));
23825
23889
  if (!path)
23826
23890
  return new Response("not found", { status: 404 });
23827
23891
  return new Response(Bun.file(path), { headers: { "cache-control": IMMUTABLE } });
@@ -23842,15 +23906,15 @@ function createFetch(opts) {
23842
23906
  });
23843
23907
  }
23844
23908
  const rel = url.pathname === "/" ? "/index.html" : url.pathname;
23845
- const file = join17(opts.shellDist, rel.replaceAll("..", ""));
23909
+ const file = join18(opts.shellDist, rel.replaceAll("..", ""));
23846
23910
  const hashed = rel.startsWith("/assets/");
23847
- if (existsSync20(file) && Bun.file(file).size > 0) {
23911
+ if (existsSync21(file) && Bun.file(file).size > 0) {
23848
23912
  return new Response(Bun.file(file), { headers: { "cache-control": hashed ? IMMUTABLE : NO_STORE } });
23849
23913
  }
23850
23914
  if (hashed)
23851
23915
  return new Response("not found", { status: 404 });
23852
- const index = join17(opts.shellDist, "index.html");
23853
- if (existsSync20(index))
23916
+ const index = join18(opts.shellDist, "index.html");
23917
+ if (existsSync21(index))
23854
23918
  return new Response(Bun.file(index), { headers: { "cache-control": NO_STORE } });
23855
23919
  return new Response("Toyon daemon running; shell not built (run: bun run build)", { status: 200 });
23856
23920
  };
@@ -24218,16 +24282,16 @@ function startServer(opts) {
24218
24282
  }
24219
24283
 
24220
24284
  // ../daemon/src/themes/store.ts
24221
- import { existsSync as existsSync21, readdirSync as readdirSync5, readFileSync as readFileSync13, statSync as statSync6, writeFileSync as writeFileSync7 } from "fs";
24285
+ import { existsSync as existsSync22, readdirSync as readdirSync5, readFileSync as readFileSync14, statSync as statSync6, writeFileSync as writeFileSync8 } from "fs";
24222
24286
  import { homedir as homedir10 } from "os";
24223
- import { basename as basename4, dirname as dirname7, join as join18, resolve as resolve8 } from "path";
24287
+ import { basename as basename4, dirname as dirname7, join as join19, resolve as resolve8 } from "path";
24224
24288
  var home = homedir10();
24225
24289
  var defaultExtensionDirs = [
24226
- join18(home, ".vscode", "extensions"),
24227
- join18(home, ".vscode-insiders", "extensions"),
24228
- join18(home, ".vscode-oss", "extensions"),
24229
- join18(home, ".cursor", "extensions"),
24230
- join18(home, ".windsurf", "extensions"),
24290
+ join19(home, ".vscode", "extensions"),
24291
+ join19(home, ".vscode-insiders", "extensions"),
24292
+ join19(home, ".vscode-oss", "extensions"),
24293
+ join19(home, ".cursor", "extensions"),
24294
+ join19(home, ".windsurf", "extensions"),
24231
24295
  "/Applications/Visual Studio Code.app/Contents/Resources/app/extensions",
24232
24296
  "/Applications/Visual Studio Code - Insiders.app/Contents/Resources/app/extensions",
24233
24297
  "/Applications/Cursor.app/Contents/Resources/app/extensions",
@@ -24311,14 +24375,14 @@ class ThemeStore {
24311
24375
  name: json?.name ?? base,
24312
24376
  source: "file"
24313
24377
  });
24314
- writeFileSync7(join18(this.themesDir, `${base}.json`), `${JSON.stringify(theme, null, 2)}
24378
+ writeFileSync8(join19(this.themesDir, `${base}.json`), `${JSON.stringify(theme, null, 2)}
24315
24379
  `);
24316
24380
  this.load();
24317
24381
  return theme;
24318
24382
  }
24319
24383
  }
24320
24384
  function readJsonc(file) {
24321
- return parse6(readFileSync13(file, "utf8"), [], { allowTrailingComma: true });
24385
+ return parse6(readFileSync14(file, "utf8"), [], { allowTrailingComma: true });
24322
24386
  }
24323
24387
  function readVscodeTheme(file, depth = 0) {
24324
24388
  const json = readJsonc(file);
@@ -24340,13 +24404,13 @@ function readVscodeTheme(file, depth = 0) {
24340
24404
  return t;
24341
24405
  }
24342
24406
  function loadDir(dir) {
24343
- if (!existsSync21(dir))
24407
+ if (!existsSync22(dir))
24344
24408
  return [];
24345
24409
  const out = [];
24346
24410
  for (const f of readdirSync5(dir)) {
24347
24411
  if (!/\.jsonc?$/i.test(f))
24348
24412
  continue;
24349
- const file = join18(dir, f);
24413
+ const file = join19(dir, f);
24350
24414
  const id = `file:${slug(f.replace(/\.(json|jsonc)$/i, ""))}`;
24351
24415
  try {
24352
24416
  const json = readJsonc(file);
@@ -24362,7 +24426,7 @@ function loadDir(dir) {
24362
24426
  return out;
24363
24427
  }
24364
24428
  function discover(dir) {
24365
- if (!existsSync21(dir))
24429
+ if (!existsSync22(dir))
24366
24430
  return [];
24367
24431
  const out = [];
24368
24432
  let entries;
@@ -24372,21 +24436,21 @@ function discover(dir) {
24372
24436
  return out;
24373
24437
  }
24374
24438
  for (const name of entries) {
24375
- const extDir = join18(dir, name);
24376
- const pkgFile = join18(extDir, "package.json");
24439
+ const extDir = join19(dir, name);
24440
+ const pkgFile = join19(extDir, "package.json");
24377
24441
  try {
24378
- if (!statSync6(extDir).isDirectory() || !existsSync21(pkgFile))
24442
+ if (!statSync6(extDir).isDirectory() || !existsSync22(pkgFile))
24379
24443
  continue;
24380
- const pkg = JSON.parse(readFileSync13(pkgFile, "utf8"));
24444
+ const pkg = JSON.parse(readFileSync14(pkgFile, "utf8"));
24381
24445
  const themes = pkg.contributes?.themes;
24382
24446
  if (!Array.isArray(themes))
24383
24447
  continue;
24384
24448
  const ext = `${pkg.publisher ?? "local"}.${pkg.name ?? name}`;
24385
24449
  let nls = {};
24386
- const nlsFile = join18(extDir, "package.nls.json");
24387
- if (existsSync21(nlsFile)) {
24450
+ const nlsFile = join19(extDir, "package.nls.json");
24451
+ if (existsSync22(nlsFile)) {
24388
24452
  try {
24389
- nls = JSON.parse(readFileSync13(nlsFile, "utf8"));
24453
+ nls = JSON.parse(readFileSync14(nlsFile, "utf8"));
24390
24454
  } catch {}
24391
24455
  }
24392
24456
  for (const c of themes) {
@@ -25210,8 +25274,8 @@ function rankRefs({ refs, merged, prs, query, defaultBranch, rowIdForPath }) {
25210
25274
  }
25211
25275
 
25212
25276
  // ../daemon/src/worktrees/service.ts
25213
- import { existsSync as existsSync23, lstatSync, readlinkSync, rmSync as rmSync5, symlinkSync, unlinkSync } from "fs";
25214
- import { basename as basename6, dirname as dirname8, join as join21 } from "path";
25277
+ import { existsSync as existsSync24, lstatSync, readFileSync as readFileSync16, readlinkSync, rmSync as rmSync5, symlinkSync, unlinkSync } from "fs";
25278
+ import { basename as basename6, dirname as dirname8, join as join22 } from "path";
25215
25279
 
25216
25280
  // ../daemon/src/git/archive.ts
25217
25281
  import { rmSync as rmSync3 } from "fs";
@@ -25220,12 +25284,16 @@ async function keepState(repoPath, wtPath, ref, indexFile) {
25220
25284
  const head = await git(wtPath, "rev-parse", "--verify", "HEAD");
25221
25285
  if (!head.ok)
25222
25286
  return null;
25223
- const status = await git(wtPath, "status", "--porcelain");
25224
- const dirty = !status.ok || status.out !== "";
25287
+ const status = await gitRaw(wtPath, "status", "--porcelain", "-uall");
25288
+ const files = status.ok ? parsePorcelain(status.out).length : null;
25289
+ const dirty = files === null || files > 0;
25225
25290
  const snapshot = dirty ? await snapshotCommit(wtPath, head.out, indexFile) : null;
25226
25291
  if (!(await git(repoPath, "update-ref", ref, snapshot ?? head.out)).ok)
25227
25292
  return null;
25228
- return { head: head.out, ...snapshot ? { snapshot } : dirty ? { lost: true } : {} };
25293
+ return {
25294
+ head: head.out,
25295
+ ...snapshot ? { snapshot, ...files ? { dirty: files } : {} } : dirty ? { lost: true } : {}
25296
+ };
25229
25297
  }
25230
25298
  async function snapshotCommit(wtPath, head, indexFile) {
25231
25299
  const env = { GIT_INDEX_FILE: indexFile };
@@ -25497,20 +25565,20 @@ function compareUrl(remoteUrl, base, branch) {
25497
25565
  }
25498
25566
 
25499
25567
  // ../daemon/src/worktrees/archive.ts
25500
- import { existsSync as existsSync22, mkdirSync as mkdirSync6, readdirSync as readdirSync6, readFileSync as readFileSync14, renameSync as renameSync4, rmSync as rmSync4, writeFileSync as writeFileSync8 } from "fs";
25501
- import { join as join19 } from "path";
25568
+ import { existsSync as existsSync23, mkdirSync as mkdirSync7, readdirSync as readdirSync6, readFileSync as readFileSync15, renameSync as renameSync4, rmSync as rmSync4, writeFileSync as writeFileSync9 } from "fs";
25569
+ import { join as join20 } from "path";
25502
25570
  class WorktreeArchive {
25503
25571
  dir;
25504
25572
  records = new Map;
25505
25573
  constructor(dir) {
25506
25574
  this.dir = dir;
25507
- mkdirSync6(dir, { recursive: true });
25575
+ mkdirSync7(dir, { recursive: true });
25508
25576
  for (const id of readdirSync6(dir)) {
25509
25577
  const file = this.filesOf(id).record;
25510
- if (!existsSync22(file))
25578
+ if (!existsSync23(file))
25511
25579
  continue;
25512
25580
  try {
25513
- this.records.set(id, JSON.parse(readFileSync14(file, "utf8")));
25581
+ this.records.set(id, JSON.parse(readFileSync15(file, "utf8")));
25514
25582
  } catch (e) {
25515
25583
  log.warn("archive", `skipped an unreadable record: ${file}`, e);
25516
25584
  }
@@ -25521,10 +25589,10 @@ class WorktreeArchive {
25521
25589
  }
25522
25590
  put(rec, from) {
25523
25591
  const at = this.filesOf(rec.worktree.id);
25524
- mkdirSync6(at.dir, { recursive: true });
25592
+ mkdirSync7(at.dir, { recursive: true });
25525
25593
  move(from.transcript, at.transcript);
25526
25594
  move(from.attachments, at.attachments);
25527
- writeFileSync8(at.record, JSON.stringify(rec, null, 2));
25595
+ writeFileSync9(at.record, JSON.stringify(rec, null, 2));
25528
25596
  this.records.set(rec.worktree.id, rec);
25529
25597
  }
25530
25598
  take(id, to) {
@@ -25551,12 +25619,12 @@ class WorktreeArchive {
25551
25619
  return { transcript: at.transcript, attachments: at.attachments };
25552
25620
  }
25553
25621
  filesOf(id) {
25554
- const dir = join19(this.dir, id);
25622
+ const dir = join20(this.dir, id);
25555
25623
  return {
25556
25624
  dir,
25557
- record: join19(dir, "record.json"),
25558
- transcript: join19(dir, "transcript.jsonl"),
25559
- attachments: join19(dir, "attachments")
25625
+ record: join20(dir, "record.json"),
25626
+ transcript: join20(dir, "transcript.jsonl"),
25627
+ attachments: join20(dir, "attachments")
25560
25628
  };
25561
25629
  }
25562
25630
  }
@@ -25571,16 +25639,16 @@ function summarize(r, repoId) {
25571
25639
  archivedAt: r.archivedAt,
25572
25640
  ...r.prompt ? { prompt: r.prompt } : {},
25573
25641
  restorable: !!r.kept,
25574
- ...r.kept?.snapshot ? { uncommitted: true } : {},
25642
+ ...r.kept?.snapshot ? { uncommitted: true, ...r.kept.dirty ? { dirty: r.kept.dirty } : {} } : {},
25575
25643
  ...r.worktree.landed ? { landed: true } : {},
25576
25644
  ...r.cost !== undefined ? { cost: r.cost } : {},
25577
25645
  ...r.auto ? { auto: r.auto } : {}
25578
25646
  };
25579
25647
  }
25580
25648
  function lastUsage(transcript) {
25581
- if (!existsSync22(transcript))
25649
+ if (!existsSync23(transcript))
25582
25650
  return null;
25583
- const lines = readFileSync14(transcript, "utf8").split(`
25651
+ const lines = readFileSync15(transcript, "utf8").split(`
25584
25652
  `);
25585
25653
  for (let i = lines.length - 1;i >= 0; i--) {
25586
25654
  if (!lines[i]?.includes('"usage"'))
@@ -25594,9 +25662,9 @@ function lastUsage(transcript) {
25594
25662
  return null;
25595
25663
  }
25596
25664
  function firstPrompt(transcript) {
25597
- if (!existsSync22(transcript))
25665
+ if (!existsSync23(transcript))
25598
25666
  return;
25599
- for (const line of readFileSync14(transcript, "utf8").split(`
25667
+ for (const line of readFileSync15(transcript, "utf8").split(`
25600
25668
  `)) {
25601
25669
  if (!line.includes('"user-message"'))
25602
25670
  continue;
@@ -25609,7 +25677,7 @@ function firstPrompt(transcript) {
25609
25677
  return;
25610
25678
  }
25611
25679
  function move(from, to) {
25612
- if (!existsSync22(from))
25680
+ if (!existsSync23(from))
25613
25681
  return;
25614
25682
  rmSync4(to, { recursive: true, force: true });
25615
25683
  renameSync4(from, to);
@@ -25673,7 +25741,7 @@ class ArchivedGit {
25673
25741
  }
25674
25742
 
25675
25743
  // ../daemon/src/worktrees/spare.ts
25676
- import { join as join20 } from "path";
25744
+ import { join as join21 } from "path";
25677
25745
  class SparePool {
25678
25746
  d;
25679
25747
  spares = new Map;
@@ -25722,7 +25790,7 @@ class SparePool {
25722
25790
  this.spares.set(repoId, entry);
25723
25791
  try {
25724
25792
  const slug = `wt-${shortId().slice(0, 4)}`;
25725
- const wtPath = join20(this.d.paths.worktreesDir, repo.name, slug);
25793
+ const wtPath = join21(this.d.paths.worktreesDir, repo.name, slug);
25726
25794
  await withRepoLock(repo.path, () => gitOrThrow(repo.path, "worktree", "add", "--detach", wtPath, repo.defaultBranch));
25727
25795
  const wt = {
25728
25796
  id: shortId(),
@@ -25953,6 +26021,7 @@ function stopOf(prev, next) {
25953
26021
 
25954
26022
  // ../daemon/src/worktrees/service.ts
25955
26023
  var LOCAL_CONFIG_FILES = [".env", ".env.local", ".env.development", ".env.development.local", ".dev.vars"];
26024
+ var BASE_IGNORE = "base .gitignore";
25956
26025
  function withoutAttachments(event) {
25957
26026
  if (event.type !== "user-message")
25958
26027
  return event;
@@ -25973,6 +26042,7 @@ class WorktreeService {
25973
26042
  spare;
25974
26043
  archive;
25975
26044
  archiving = new Map;
26045
+ going = new Set;
25976
26046
  countsCache = new Map;
25977
26047
  discoverCache = new Map;
25978
26048
  usage = new Map;
@@ -26055,7 +26125,7 @@ class WorktreeService {
26055
26125
  throw new UserError(carried.unmoved);
26056
26126
  return claimed;
26057
26127
  }
26058
- const wtPath = join21(this.d.paths.worktreesDir, repo.name, slug);
26128
+ const wtPath = join22(this.d.paths.worktreesDir, repo.name, slug);
26059
26129
  await withRepoLock(repo.path, () => gitOrThrow(repo.path, "worktree", "add", "-b", branch, wtPath, repo.defaultBranch));
26060
26130
  const wt = {
26061
26131
  id: shortId(),
@@ -26106,6 +26176,9 @@ class WorktreeService {
26106
26176
  `).indexOf(sha);
26107
26177
  return i < 0 ? null : `stash@{${i}}`;
26108
26178
  };
26179
+ const carried = await git(repo.path, "ls-tree", "-r", "--name-only", `${sha}^3`);
26180
+ const restored = (carried.ok ? carried.out.split(`
26181
+ `).filter(Boolean) : []).filter((p) => !existsSync24(join22(wt.path, p)));
26109
26182
  const applied = await git(wt.path, "stash", "apply", sha);
26110
26183
  if (applied.ok) {
26111
26184
  const ref = await entry();
@@ -26115,7 +26188,9 @@ class WorktreeService {
26115
26188
  }
26116
26189
  log.warn(wt.id, `moving ${main}'s changes failed`, applied.err);
26117
26190
  await git(wt.path, "reset", "--hard");
26118
- await git(wt.path, "clean", "-fd");
26191
+ for (let i = 0;i < restored.length; i += 100) {
26192
+ await git(wt.path, "--literal-pathspecs", "clean", "-fd", "--", ...restored.slice(i, i + 100));
26193
+ }
26119
26194
  const ref = await entry();
26120
26195
  const back = ref ? await git(repo.path, "stash", "pop", ref) : null;
26121
26196
  return back?.ok ? `the changes on ${main} did not apply in ${wt.title}, so they stayed on ${main}` : `the changes on ${main} did not apply in ${wt.title}; they are kept in git stash as "toyon: into ${wt.title}"`;
@@ -26139,9 +26214,9 @@ class WorktreeService {
26139
26214
  const branch = kind === "pr" ? `pr/${number}` : ref;
26140
26215
  const title = kind === "pr" ? `pr-${number}` : cleanTitle2(ref) || "branch";
26141
26216
  let slug = title;
26142
- if (existsSync23(join21(this.d.paths.worktreesDir, repo.name, slug)))
26217
+ if (existsSync24(join22(this.d.paths.worktreesDir, repo.name, slug)))
26143
26218
  slug = `${slug}-${shortId().slice(0, 4)}`;
26144
- const wtPath = join21(this.d.paths.worktreesDir, repo.name, slug);
26219
+ const wtPath = join22(this.d.paths.worktreesDir, repo.name, slug);
26145
26220
  await withRepoLock(repo.path, async () => {
26146
26221
  const out = (await listWorktrees(repo.path)).find((w) => w.branch === branch);
26147
26222
  if (out)
@@ -26317,6 +26392,14 @@ class WorktreeService {
26317
26392
  this.d.drafts?.drop(worktreeId);
26318
26393
  }
26319
26394
  async takeDown(wt, archive, reason) {
26395
+ this.going.add(wt.id);
26396
+ try {
26397
+ return await this.removeAndRecord(wt, archive, reason);
26398
+ } finally {
26399
+ this.going.delete(wt.id);
26400
+ }
26401
+ }
26402
+ async removeAndRecord(wt, archive, reason) {
26320
26403
  const worktreeId = wt.id;
26321
26404
  const repo = this.d.state.requireRepo(wt.repoId);
26322
26405
  await this.d.runtime.stop(worktreeId);
@@ -26348,7 +26431,7 @@ class WorktreeService {
26348
26431
  return archived;
26349
26432
  }
26350
26433
  async keep(repo, wt) {
26351
- const index = join21(this.d.paths.archiveDir, `${wt.id}.index`);
26434
+ const index = join22(this.d.paths.archiveDir, `${wt.id}.index`);
26352
26435
  const kept = await keepState(repo.path, wt.path, archiveRef(wt.id), index);
26353
26436
  if (!kept)
26354
26437
  log.warn(wt.id, "could not keep its git state");
@@ -26436,7 +26519,7 @@ class WorktreeService {
26436
26519
  }
26437
26520
  archivedAttachment(archiveId, file) {
26438
26521
  const files = this.archive.chatFiles(archiveId);
26439
- return files && isAttachmentFile(file) ? join21(files.attachments, file) : null;
26522
+ return files && isAttachmentFile(file) ? join22(files.attachments, file) : null;
26440
26523
  }
26441
26524
  archivedGit(archiveId) {
26442
26525
  const rec = this.archive.get(archiveId);
@@ -26476,7 +26559,7 @@ class WorktreeService {
26476
26559
  throw new UserError(`${old.title} was archived without its commits, so there is nothing to restore`);
26477
26560
  if (rec.auto)
26478
26561
  log.info(old.id, `restoring a worktree that archived itself (${rec.auto})`);
26479
- const path = existsSync23(old.path) ? join21(dirname8(old.path), `${basename6(old.path)}-${shortId().slice(0, 4)}`) : old.path;
26562
+ const path = existsSync24(old.path) ? join22(dirname8(old.path), `${basename6(old.path)}-${shortId().slice(0, 4)}`) : old.path;
26480
26563
  const branch = await withRepoLock(repo.path, async () => {
26481
26564
  if (!await commitOf(repo.path, archiveRef(old.id))) {
26482
26565
  throw new UserError(`${old.title}'s kept commits are gone from git`);
@@ -26536,12 +26619,12 @@ class WorktreeService {
26536
26619
  return;
26537
26620
  const repo = this.repoOf(rec);
26538
26621
  const repoPath = repo?.path ?? rec.repoPath;
26539
- if (rec.kept && existsSync23(repoPath)) {
26622
+ if (rec.kept && existsSync24(repoPath)) {
26540
26623
  const r = await git(repoPath, "update-ref", "-d", archiveRef(archiveId));
26541
26624
  if (!r.ok)
26542
26625
  log.warn(archiveId, `could not drop its archive ref: ${r.err}`);
26543
26626
  }
26544
- if (rec.worktree.lands?.length && existsSync23(repoPath))
26627
+ if (rec.worktree.lands?.length && existsSync24(repoPath))
26545
26628
  await dropLandRefs(repoPath, archiveId);
26546
26629
  this.archive.delete(archiveId);
26547
26630
  this.d.drafts?.drop(archiveId);
@@ -26579,7 +26662,7 @@ class WorktreeService {
26579
26662
  if (!hasOwnBranch(wt))
26580
26663
  return;
26581
26664
  const name = wt.branch.replace(/^toyon\//, "");
26582
- const desired = wt.kind === "worktree" ? join21(dirname8(wt.path), name) : wt.path;
26665
+ const desired = wt.kind === "worktree" ? join22(dirname8(wt.path), name) : wt.path;
26583
26666
  if (wt.linkPath && wt.linkPath !== desired)
26584
26667
  this.dropLink(wt);
26585
26668
  if (desired === wt.path)
@@ -26671,10 +26754,18 @@ class WorktreeService {
26671
26754
  this.d.runtime.markSetup(wt.id, false);
26672
26755
  }
26673
26756
  }
26757
+ async mirrorBaseIgnore(wt, repo, depsSource) {
26758
+ const src = join22(depsSource, ".gitignore");
26759
+ const mirror = existsSync24(src) && !existsSync24(join22(wt.path, ".gitignore"));
26760
+ const lines = mirror ? readFileSync16(src, "utf8").split(`
26761
+ `).filter((l) => l.trim() !== "") : [];
26762
+ await excludeBlock(repo.path, BASE_IGNORE, lines);
26763
+ }
26674
26764
  async setupThenStart(wt, repo, depsSource, { setupCommands = true }) {
26675
- const srcNm = join21(depsSource, "node_modules");
26676
- const dstNm = join21(wt.path, "node_modules");
26677
- if (existsSync23(srcNm) && !existsSync23(dstNm)) {
26765
+ await this.mirrorBaseIgnore(wt, repo, depsSource);
26766
+ const srcNm = join22(depsSource, "node_modules");
26767
+ const dstNm = join22(wt.path, "node_modules");
26768
+ if (existsSync24(srcNm) && !existsSync24(dstNm)) {
26678
26769
  const started = Date.now();
26679
26770
  const attempts = [
26680
26771
  ["clonefile", ["-Rc", srcNm, dstNm]],
@@ -26690,10 +26781,10 @@ class WorktreeService {
26690
26781
  }
26691
26782
  }
26692
26783
  for (const f of LOCAL_CONFIG_FILES) {
26693
- const src = join21(depsSource, f);
26694
- if (!existsSync23(src) || existsSync23(join21(wt.path, f)))
26784
+ const src = join22(depsSource, f);
26785
+ if (!existsSync24(src) || existsSync24(join22(wt.path, f)))
26695
26786
  continue;
26696
- const r = await run("cp", [src, join21(wt.path, f)], wt.path);
26787
+ const r = await run("cp", [src, join22(wt.path, f)], wt.path);
26697
26788
  if (r.ok)
26698
26789
  this.d.hub.emit("log", wt.id, "setup", `copied ${f}`);
26699
26790
  else
@@ -27004,9 +27095,14 @@ ${wt.landing.body}` : wt.landing.subject : undefined;
27004
27095
  const cached = this.countsCache.get(id);
27005
27096
  if (cached && Date.now() - cached.at < 1e4)
27006
27097
  return cached;
27098
+ if (this.going.has(id))
27099
+ return cached ?? {};
27007
27100
  try {
27008
27101
  const ab = countable ? await aheadBehind(path, defaultBranch) : await this.mainCounts(path);
27009
- const fresh = { ...ab, dirty: (await statusFiles(path)).length, at: Date.now() };
27102
+ const dirty = (await statusFiles(path)).length;
27103
+ if (this.going.has(id))
27104
+ return cached ?? {};
27105
+ const fresh = { ...ab, dirty, at: Date.now() };
27010
27106
  this.countsCache.set(id, fresh);
27011
27107
  return fresh;
27012
27108
  } catch {
@@ -27044,6 +27140,8 @@ ${wt.landing.body}` : wt.landing.subject : undefined;
27044
27140
  };
27045
27141
  }
27046
27142
  async gitStatus(worktreeId) {
27143
+ if (this.going.has(worktreeId))
27144
+ return null;
27047
27145
  const r = this.readable(worktreeId);
27048
27146
  if (!r)
27049
27147
  return this.archivedStatus(worktreeId);
@@ -27065,6 +27163,8 @@ ${wt.landing.body}` : wt.landing.subject : undefined;
27065
27163
  this.setLanding(r.wt.id, undefined);
27066
27164
  if (r.wt && isMain)
27067
27165
  this.setEmpty(r.wt, files.length === 0 ? await treeEmpty(r.path) : false);
27166
+ if (this.going.has(worktreeId))
27167
+ return null;
27068
27168
  this.noteCounts(worktreeId, isMain, files.length, counts);
27069
27169
  return { files, committed, head: head.ok ? head.out : undefined, ...counts };
27070
27170
  } catch (e) {
@@ -27449,7 +27549,7 @@ bridge.setShellOrigins([
27449
27549
  ],
27450
27550
  ...remote ? [`https://${remote.host}`] : []
27451
27551
  ]);
27452
- writeFileSync9(paths.pidFile, `${process.pid}
27552
+ writeFileSync10(paths.pidFile, `${process.pid}
27453
27553
  `);
27454
27554
  var stopLagSampler = startLagSampler();
27455
27555
  await repos.boot();