toyon 0.13.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/README.md +1 -1
  2. package/dist/cli.js +30 -42
  3. package/dist/daemon.js +63 -27
  4. package/dist/shell/assets/{Editor-CUcgZuoM.js → Editor-Gzhl3Oqu.js} +3 -3
  5. package/dist/shell/assets/{XTerm-V6sBDIe-.js → XTerm-D6TL52yQ.js} +1 -1
  6. package/dist/shell/assets/{cssMode-CSyiAvIn.js → cssMode-BfNm0a39.js} +1 -1
  7. package/dist/shell/assets/{freemarker2-C4-FNK3V.js → freemarker2-DhiwR6KJ.js} +1 -1
  8. package/dist/shell/assets/{grammar-DzHb7e7O.js → grammar-thv9InXF.js} +1 -1
  9. package/dist/shell/assets/{handlebars-DcjVaOCM.js → handlebars-BbkhCZXb.js} +1 -1
  10. package/dist/shell/assets/{html-Csl8cogA.js → html-yzhYlR33.js} +1 -1
  11. package/dist/shell/assets/{htmlMode-rewe9UaO.js → htmlMode-DPICAUY8.js} +1 -1
  12. package/dist/shell/assets/index-BYBd8f7P.css +1 -0
  13. package/dist/shell/assets/{index-Bkn7zNFw.js → index-DJBEbneL.js} +111 -111
  14. package/dist/shell/assets/{javascript--pKV-E-G.js → javascript-D9eBjR4g.js} +1 -1
  15. package/dist/shell/assets/{jsonMode-zPVc0E6l.js → jsonMode-cNulMi5B.js} +1 -1
  16. package/dist/shell/assets/{liquid-jZT5xQG-.js → liquid-C_SKIx1n.js} +1 -1
  17. package/dist/shell/assets/{lspLanguageFeatures-BuiqKs90.js → lspLanguageFeatures-DNU8BRHj.js} +1 -1
  18. package/dist/shell/assets/{mdx-DwnR-ia2.js → mdx-Bt4opbQO.js} +1 -1
  19. package/dist/shell/assets/{python-DU3pl9Zu.js → python-Crb-EuXe.js} +1 -1
  20. package/dist/shell/assets/{razor-BzVUPqHs.js → razor-D3BlkbYO.js} +1 -1
  21. package/dist/shell/assets/{tsMode-ONmZhHHZ.js → tsMode-BYuAS95U.js} +1 -1
  22. package/dist/shell/assets/{xml-BXW7X3BT.js → xml-G5xafDqw.js} +1 -1
  23. package/dist/shell/assets/{yaml-DFE79ex1.js → yaml-CRp1x3nR.js} +1 -1
  24. package/dist/shell/index.html +2 -2
  25. package/package.json +1 -1
  26. package/dist/shell/assets/index-iVaPr_If.css +0 -1
package/README.md CHANGED
@@ -48,7 +48,7 @@ npm i -g toyon
48
48
  toyon ~/projects/app
49
49
  ```
50
50
 
51
- `toyon .` opens the folder you are in and `toyon --help` lists the rest. A global install keeps itself updated; `TOYON_UPDATES=off` stops it. Run outside a project, Toyon opens with none: describe a new one in a sentence, or give the project picker a git URL to clone.
51
+ `toyon .` opens the folder you are in and `toyon --help` lists the rest. The install button in the top bar makes Toyon an app of its own in Chrome, Arc, Brave or Edge, with a Dock icon and no browser bar; from then on `toyon .` opens that. A global install keeps itself updated; `TOYON_UPDATES=off` stops it. Run outside a project, Toyon opens with none: describe a new one in a sentence, or give the project picker a git URL to clone.
52
52
 
53
53
  You need:
54
54
 
package/dist/cli.js CHANGED
@@ -7,7 +7,7 @@ import { join as join7, resolve } from "path";
7
7
  // package.json
8
8
  var package_default = {
9
9
  name: "toyon",
10
- version: "0.13.0",
10
+ version: "0.14.0",
11
11
  description: "Toyon is where coding agents work on your web app while you check each change by using it. Every chat gets its own running copy with a live preview.",
12
12
  license: "MIT",
13
13
  type: "module",
@@ -5373,8 +5373,9 @@ function post(route, token, body) {
5373
5373
  body: JSON.stringify(body)
5374
5374
  });
5375
5375
  }
5376
+ var shellOrigins = ["http://toyon.localhost", `http://toyon.localhost:${port}`];
5376
5377
  function shellUrl(token, branded) {
5377
- return branded ? `http://toyon.localhost/#token=${token}` : `http://toyon.localhost:${port}/#token=${token}`;
5378
+ return `${branded ? shellOrigins[0] : shellOrigins[1]}/#token=${token}`;
5378
5379
  }
5379
5380
 
5380
5381
  // src/openUrl.ts
@@ -5404,14 +5405,15 @@ function profilesOf(browser) {
5404
5405
  function pwaStillInstalled(browser, id) {
5405
5406
  return profilesOf(browser).some((p) => existsSync3(join3(p, "Web Applications", "Manifest Resources", id)));
5406
5407
  }
5407
- function findPwaId(browser, manifestId) {
5408
+ function findPwaId(browser) {
5408
5409
  const cache = join3(pwaCacheDir, `${browser.replace(/\W+/g, "-")}.id`);
5409
5410
  if (existsSync3(cache)) {
5410
5411
  const id = readFileSync2(cache, "utf8").trim();
5411
5412
  if (/^[a-p]{32}$/.test(id) && pwaStillInstalled(browser, id))
5412
5413
  return id;
5413
5414
  }
5414
- const re = new RegExp(`web_apps-dt-([a-p]{32})[\\s\\S]{0,16}?${manifestId.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&")}`);
5415
+ const ids = shellOrigins.map((o) => `${o}/`.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&")).join("|");
5416
+ const re = new RegExp(`web_apps-dt-([a-p]{32})[\\s\\S]{0,16}?(?:${ids})`);
5415
5417
  for (const p of profilesOf(browser)) {
5416
5418
  const dir = join3(p, "Sync Data", "LevelDB");
5417
5419
  if (!existsSync3(dir))
@@ -5429,29 +5431,28 @@ function findPwaId(browser, manifestId) {
5429
5431
  }
5430
5432
  return null;
5431
5433
  }
5432
- function showAppWindow(daemon) {
5433
- const appUrl = shellUrl(daemon.token, false);
5434
- console.log(`toyon: app window (${appUrl.split("#")[0]})`);
5435
- if (openAppWindow(appUrl))
5434
+ function openShell(daemon) {
5435
+ console.log(`toyon: ${daemon.url}`);
5436
+ const found = process.platform === "darwin" ? launchInstalledApp() : "none";
5437
+ if (found === "launched")
5436
5438
  return;
5437
- console.log("no Chromium browser found; opening in default browser");
5438
5439
  openUrl(daemon.url);
5440
+ if (found === "uninstalled")
5441
+ console.log("tip: install Toyon as an app from the top bar; toyon then opens in its own window");
5439
5442
  }
5440
- function openAppWindow(appUrl) {
5441
- const manifestId = new URL("/", appUrl).href;
5443
+ function launchInstalledApp() {
5444
+ let seen = false;
5442
5445
  for (const app of CHROMIUMS) {
5443
- if (spawnSync("open", ["-Ra", app]).status === 0) {
5444
- const id = findPwaId(app, manifestId);
5445
- const flag = id ? `--app-id=${id}` : `--app=${appUrl}`;
5446
- spawn3("open", ["-na", app, "--args", flag], { stdio: "ignore" }).unref();
5447
- if (!id) {
5448
- console.log(`tip: install Toyon as an app (browser menu, then Install, or the install button in the top bar when opened in a tab)`);
5449
- console.log(` once installed, it gets a native-style title bar; \`toyon --app\` then launches the installed app`);
5450
- }
5451
- return true;
5452
- }
5446
+ if (spawnSync("open", ["-Ra", app]).status !== 0)
5447
+ continue;
5448
+ seen = true;
5449
+ const id = findPwaId(app);
5450
+ if (!id)
5451
+ continue;
5452
+ spawn3("open", ["-na", app, "--args", `--app-id=${id}`], { stdio: "ignore" }).unref();
5453
+ return "launched";
5453
5454
  }
5454
- return false;
5455
+ return seen ? "uninstalled" : "none";
5455
5456
  }
5456
5457
 
5457
5458
  // src/args.ts
@@ -5532,18 +5533,14 @@ function parseArgs(argv) {
5532
5533
  }
5533
5534
  }
5534
5535
  let path = null;
5535
- let app = false;
5536
5536
  for (const a of argv) {
5537
- if (a === "--app" || a === "--pwa")
5538
- app = true;
5539
- else if (a.startsWith("-"))
5537
+ if (a.startsWith("-"))
5540
5538
  return { kind: "error", message: `unknown option ${a}` };
5541
- else if (path !== null)
5539
+ if (path !== null)
5542
5540
  return { kind: "error", message: "Toyon opens one path at a time" };
5543
- else
5544
- path = a;
5541
+ path = a;
5545
5542
  }
5546
- return { kind: "open", path, app };
5543
+ return { kind: "open", path };
5547
5544
  }
5548
5545
  function parseLogs(rest) {
5549
5546
  let follow = false;
@@ -5641,9 +5638,6 @@ usage
5641
5638
  toyon deploy fly destroy <name> [--yes]
5642
5639
  delete that app and its volume
5643
5640
 
5644
- options for toyon [path]
5645
- --app open a Chromium app window (the installed Toyon app when there is one)
5646
-
5647
5641
  environment
5648
5642
  TOYON_HOME where state lives (default ~/.toyon)
5649
5643
  TOYON_PORT the daemon's port (default 4141)
@@ -6218,7 +6212,7 @@ async function helper(args) {
6218
6212
  if (!res.ok)
6219
6213
  console.error(`could not open ${path}: ${await res.text()}`);
6220
6214
  }
6221
- showAppWindow(daemon);
6215
+ openShell(daemon);
6222
6216
  return 0;
6223
6217
  }
6224
6218
 
@@ -6849,16 +6843,10 @@ async function open2(cmd) {
6849
6843
  const res = await post("/register", daemon.token, { path: target });
6850
6844
  if (!res.ok) {
6851
6845
  console.error(`could not open ${target}: ${await res.text()}`);
6852
- if (explicit || !cmd.app)
6853
- return 1;
6846
+ return 1;
6854
6847
  }
6855
6848
  }
6856
- if (cmd.app)
6857
- showAppWindow(daemon);
6858
- else {
6859
- console.log(`toyon: ${daemon.url}`);
6860
- openUrl(daemon.url);
6861
- }
6849
+ openShell(daemon);
6862
6850
  return 0;
6863
6851
  }
6864
6852
  async function start() {
package/dist/daemon.js CHANGED
@@ -6602,7 +6602,7 @@ ${text.slice(text.length - (CONTEXT_CHARS - HEAD_CHARS))}`;
6602
6602
  }
6603
6603
 
6604
6604
  // ../shared/src/protocol/limits.ts
6605
- var PROTOCOL_VERSION = 105;
6605
+ var PROTOCOL_VERSION = 107;
6606
6606
  var PAGE_ERROR_MAX_CHARS = 2000;
6607
6607
  var PAGE_TITLE_MAX_CHARS = 1000;
6608
6608
  var RENDER_ERRORS_MAX = 10;
@@ -8528,7 +8528,7 @@ async function turnRemoteOff(ts, daemonPort, file, was) {
8528
8528
  // ../daemon/package.json
8529
8529
  var package_default = {
8530
8530
  name: "@toyon/daemon",
8531
- version: "0.13.0",
8531
+ version: "0.14.0",
8532
8532
  private: true,
8533
8533
  type: "module",
8534
8534
  main: "src/index.ts",
@@ -15275,7 +15275,7 @@ function mapUpdate(update, memos, tag, sink, background) {
15275
15275
  out.push({ type: "tool-update", toolId: update.toolCallId, ...refined });
15276
15276
  }
15277
15277
  if (update.content)
15278
- memo.content = [...memo.content, ...update.content];
15278
+ memo.content = update.content;
15279
15279
  if (update.rawOutput !== undefined)
15280
15280
  memo.rawOutput = update.rawOutput;
15281
15281
  if ((update.status === "completed" || update.status === "failed") && !memo.ended) {
@@ -16182,15 +16182,30 @@ class AcpSession {
16182
16182
  this.emit({ type: "tool-end", toolId, output: output.join(`
16183
16183
  `), isError: true });
16184
16184
  }
16185
- for (const { event } of this.log.entries)
16185
+ const going = new Set;
16186
+ for (const { event } of this.log.entries) {
16186
16187
  this.trackHandoff(event);
16188
+ if (event.type === "handoff-go")
16189
+ going.add(event.id);
16190
+ else if (event.type === "handoff" || event.type === "handoff-declined")
16191
+ going.delete(event.id);
16192
+ }
16193
+ for (const id of going) {
16194
+ this.emit({
16195
+ type: "handoff-declined",
16196
+ id,
16197
+ outcome: "failed",
16198
+ reason: "Toyon stopped while the worktree was being made",
16199
+ ts: Date.now()
16200
+ });
16201
+ }
16187
16202
  if (this.openHandoffs.size > 0)
16188
16203
  this.syncStatus();
16189
16204
  }
16190
16205
  trackHandoff(event) {
16191
16206
  if (event.type === "handoff-proposed")
16192
16207
  return this.openHandoffs.add(event.id).size > 0;
16193
- if (event.type === "handoff" || event.type === "handoff-declined")
16208
+ if (event.type === "handoff-go" || event.type === "handoff" || event.type === "handoff-declined")
16194
16209
  return this.openHandoffs.delete(event.id);
16195
16210
  return false;
16196
16211
  }
@@ -18641,6 +18656,8 @@ class RuntimeRegistry {
18641
18656
  }
18642
18657
  if (this.settingUp.has(id))
18643
18658
  return;
18659
+ if (wt.kind === "spare" && wt.phase !== undefined && wt.phase !== "ready")
18660
+ return;
18644
18661
  const repo = this.deps.state.repos.find((r) => r.id === wt.repoId);
18645
18662
  if (repo)
18646
18663
  await this.start(wt, repo);
@@ -19435,7 +19452,7 @@ function openAskOf(entries) {
19435
19452
  open.delete(e.id);
19436
19453
  else if (e.type === "handoff-proposed")
19437
19454
  open.set(e.id, `Continue in ${e.repo.name}?`);
19438
- else if (e.type === "handoff" || e.type === "handoff-declined")
19455
+ else if (e.type === "handoff-go" || e.type === "handoff" || e.type === "handoff-declined")
19439
19456
  open.delete(e.id);
19440
19457
  }
19441
19458
  const last = [...open.values()].at(-1)?.trim();
@@ -22738,9 +22755,14 @@ async function behindUpstream(worktreePath) {
22738
22755
  const r = await git(worktreePath, "rev-list", "--count", "HEAD..@{upstream}");
22739
22756
  return r.ok ? Number(r.out) || 0 : null;
22740
22757
  }
22741
- async function aheadUpstream(worktreePath) {
22758
+ async function aheadUpstream(worktreePath, branch) {
22742
22759
  const r = await git(worktreePath, "rev-list", "--count", "@{upstream}..HEAD");
22743
- return r.ok ? Number(r.out) || 0 : null;
22760
+ if (r.ok)
22761
+ return Number(r.out) || 0;
22762
+ if (!branch)
22763
+ return null;
22764
+ const o = await git(worktreePath, "rev-list", "--count", `origin/${branch}..HEAD`);
22765
+ return o.ok ? Number(o.out) || 0 : null;
22744
22766
  }
22745
22767
  async function aheadBehind(worktreePath, defaultBr) {
22746
22768
  const [a, b] = await Promise.all([
@@ -28964,10 +28986,12 @@ var handlers = {
28964
28986
  const followUp = msg.attachments?.length ? { attachments: msg.attachments } : undefined;
28965
28987
  s.runtime.agentFor(msg.worktreeId)?.answer(msg.askId, { kind: "answers", answers: msg.answers }, followUp);
28966
28988
  });
28989
+ s.worktrees.markPrompted(msg.worktreeId);
28967
28990
  },
28968
28991
  "agent-decide"(msg, _ctx, s) {
28969
28992
  requireRun(s, msg.worktreeId);
28970
28993
  s.runtime.agentFor(msg.worktreeId)?.answer(msg.askId, { kind: "choice", choiceId: msg.choiceId });
28994
+ s.worktrees.markPrompted(msg.worktreeId);
28971
28995
  },
28972
28996
  async "handoff-answer"(msg, _ctx, s) {
28973
28997
  requireRun(s, msg.worktreeId);
@@ -29061,8 +29085,8 @@ var handlers = {
29061
29085
  ctx.reply({ t: "refs", repoId: msg.repoId, query: msg.query, refs });
29062
29086
  },
29063
29087
  async "search-chats"(msg, ctx, s) {
29064
- const { hits, truncated } = await s.chats.search(msg.repoId, msg.query);
29065
- ctx.reply({ t: "chat-hits", repoId: msg.repoId, query: msg.query, hits, truncated });
29088
+ const { hits, truncated, asks } = await s.chats.search(msg.repoId, msg.query);
29089
+ ctx.reply({ t: "chat-hits", repoId: msg.repoId, query: msg.query, hits, truncated, asks });
29066
29090
  },
29067
29091
  async "open-ref"(msg, _ctx, s) {
29068
29092
  await s.worktrees.openRef(msg.repoId, msg.kind, msg.ref, { createdBy: msg.clientId, pr: msg.pr });
@@ -30815,6 +30839,7 @@ var CHAT_HITS_MAX = 200;
30815
30839
  var MIN_QUERY = 2;
30816
30840
  var SNIPPET = 160;
30817
30841
  var BEFORE = 50;
30842
+ var ASK = 500;
30818
30843
  var bunFs2 = {
30819
30844
  async stat(path) {
30820
30845
  try {
@@ -30886,6 +30911,9 @@ function searchRows(rows, terms, chat, limit = CHAT_HITS_PER_WORKTREE) {
30886
30911
  }
30887
30912
  return { hits, more: false };
30888
30913
  }
30914
+ function firstAsk(rows) {
30915
+ return rows.find((r) => r.role === "user")?.text.slice(0, ASK);
30916
+ }
30889
30917
  function termRanges(lower, terms) {
30890
30918
  const ranges = [];
30891
30919
  for (const term of terms) {
@@ -30907,7 +30935,7 @@ class ChatSearch {
30907
30935
  this.d.state.requireRepo(repoId);
30908
30936
  const terms = needleTerms(query);
30909
30937
  if (terms.length === 0)
30910
- return { hits: [], truncated: false };
30938
+ return { hits: [], truncated: false, asks: {} };
30911
30939
  const chats = [
30912
30940
  ...this.d.state.worktrees.filter((w) => w.repoId === repoId && w.kind === "worktree").map((w) => ({ id: w.id, archived: false, path: this.d.transcriptPath(w.id) })),
30913
30941
  ...this.d.archivedChats(repoId).map((a) => ({ id: a.id, archived: true, path: a.transcript }))
@@ -30915,13 +30943,23 @@ class ChatSearch {
30915
30943
  const read = new Set;
30916
30944
  const found = await Promise.all(chats.map(async (c) => {
30917
30945
  const rows = await this.rowsOf(c.id, c.archived, c.path, read);
30918
- return searchRows(rows, terms, { worktreeId: c.id, archived: c.archived });
30946
+ return {
30947
+ id: c.id,
30948
+ ...searchRows(rows, terms, { worktreeId: c.id, archived: c.archived }),
30949
+ ask: firstAsk(rows)
30950
+ };
30919
30951
  }));
30920
30952
  for (const key of this.said.keys())
30921
30953
  if (!read.has(key))
30922
30954
  this.said.delete(key);
30923
30955
  const all = found.filter((f) => f.hits.length > 0).sort((a, b) => b.hits[0].ts - a.hits[0].ts).flatMap((f) => f.hits);
30924
- return { hits: all.slice(0, CHAT_HITS_MAX), truncated: all.length > CHAT_HITS_MAX || found.some((f) => f.more) };
30956
+ const hits = all.slice(0, CHAT_HITS_MAX);
30957
+ const kept = new Set(hits.map((h) => h.worktreeId));
30958
+ const asks = {};
30959
+ for (const f of found)
30960
+ if (f.ask && kept.has(f.id))
30961
+ asks[f.id] = f.ask;
30962
+ return { hits, truncated: all.length > CHAT_HITS_MAX || found.some((f) => f.more), asks };
30925
30963
  }
30926
30964
  async rowsOf(id, archived, path, read) {
30927
30965
  const live = archived ? undefined : this.d.live(id);
@@ -31107,7 +31145,7 @@ function openProposals(entries) {
31107
31145
  for (const { event } of entries) {
31108
31146
  if (event.type === "handoff-proposed")
31109
31147
  open.set(event.id, event);
31110
- else if (event.type === "handoff" || event.type === "handoff-declined")
31148
+ else if (event.type === "handoff-go" || event.type === "handoff" || event.type === "handoff-declined")
31111
31149
  open.delete(event.id);
31112
31150
  }
31113
31151
  return [...open.values()];
@@ -31126,7 +31164,6 @@ function proposedThisTurn(entries) {
31126
31164
 
31127
31165
  class HandoffService {
31128
31166
  d;
31129
- settling = new Set;
31130
31167
  constructor(d) {
31131
31168
  this.d = d;
31132
31169
  d.hub.on("landed", (worktreeId) => this.landed(worktreeId));
@@ -31239,13 +31276,11 @@ class HandoffService {
31239
31276
  if (!openProposals(entries).some((p) => p.id === id)) {
31240
31277
  return log.debug(worktreeId, `answer for a handoff that already closed (${id})`);
31241
31278
  }
31242
- if (this.settling.has(id))
31243
- return;
31244
31279
  if (!go) {
31245
31280
  agent.note({ type: "handoff-declined", id, ts: Date.now() });
31246
31281
  return;
31247
31282
  }
31248
- this.settling.add(id);
31283
+ agent.note({ type: "handoff-go", id, ts: Date.now() });
31249
31284
  try {
31250
31285
  const repo = this.d.state.repo(proposed.repo.id);
31251
31286
  if (!repo)
@@ -31271,8 +31306,6 @@ class HandoffService {
31271
31306
  const reason = e instanceof Error ? e.message : String(e);
31272
31307
  agent.note({ type: "handoff-declined", id, outcome: "failed", reason, ts: Date.now() });
31273
31308
  throw e;
31274
- } finally {
31275
- this.settling.delete(id);
31276
31309
  }
31277
31310
  }
31278
31311
  async ask(worktreeId, repoId, text) {
@@ -33107,6 +33140,7 @@ class SparePool {
33107
33140
  await withRepoLock(repo.path, () => gitOrThrow(repo.path, "worktree", "add", "--detach", wt.path, baseOf(repo)));
33108
33141
  })();
33109
33142
  const done = (async () => {
33143
+ this.d.runtime.markSetup(wt.id, true);
33110
33144
  try {
33111
33145
  await added;
33112
33146
  if (after) {
@@ -33136,6 +33170,7 @@ class SparePool {
33136
33170
  } catch (e) {
33137
33171
  await this.rollBack(wt, e);
33138
33172
  } finally {
33173
+ this.d.runtime.markSetup(wt.id, false);
33139
33174
  if (this.inflight.get(repoId)?.added === added)
33140
33175
  this.inflight.delete(repoId);
33141
33176
  }
@@ -33600,7 +33635,7 @@ class WorktreeService {
33600
33635
  claimed.effort = opts.effort;
33601
33636
  if (profile !== undefined && profile !== resolveRun(repo, claimed).profile) {
33602
33637
  claimed.profile = profile;
33603
- this.restartProcs(claimed, repo);
33638
+ this.restartProcs(claimed);
33604
33639
  }
33605
33640
  const carried = await this.carryMain(repo, claimed, !!opts.carry);
33606
33641
  this.d.state.save();
@@ -33852,8 +33887,8 @@ class WorktreeService {
33852
33887
  throw new UserError(`no profile "${name}" in ${repo.configFile}`);
33853
33888
  return name;
33854
33889
  }
33855
- restartProcs(wt, repo) {
33856
- fireAndForget(wt.id, this.d.runtime.stopProcs(wt.id).then(() => this.d.runtime.start(wt, repo)), "profile restart");
33890
+ restartProcs(wt) {
33891
+ fireAndForget(wt.id, this.d.runtime.stopProcs(wt.id).then(() => this.d.runtime.wake(wt.id)), "profile restart");
33857
33892
  }
33858
33893
  setProfile(worktreeId, name) {
33859
33894
  const { wt, repo } = this.d.state.requireWorktreeWithRepo(worktreeId);
@@ -33865,7 +33900,7 @@ class WorktreeService {
33865
33900
  wt.profile = profile;
33866
33901
  this.d.state.save();
33867
33902
  this.d.hub.emit("worktreesChanged");
33868
- this.restartProcs(wt, repo);
33903
+ this.restartProcs(wt);
33869
33904
  }
33870
33905
  setMode(worktreeId, mode) {
33871
33906
  if (!this.d.state.setMode(worktreeId, mode))
@@ -34725,7 +34760,7 @@ ${wt.landing.body}` : wt.landing.subject : undefined;
34725
34760
  return { result: fetched };
34726
34761
  if (wt.pr?.state === "open") {
34727
34762
  const dirty = (await statusFiles(wt.path)).length > 0;
34728
- const missing = dirty || (await aheadUpstream(wt.path) ?? 0) > 0;
34763
+ const missing = dirty || (await aheadUpstream(wt.path, wt.branch) ?? 0) > 0;
34729
34764
  if (!missing)
34730
34765
  return { result: await mergePr(wt.path, wt.pr.number, policy.merge, w) };
34731
34766
  const committed = await this.commitIfDirty(wt, message);
@@ -35157,9 +35192,10 @@ ${wt.landing.body}` : wt.landing.subject : undefined;
35157
35192
  return { ...ab, ...unpushed === undefined ? {} : { unpushed }, dirty };
35158
35193
  }
35159
35194
  async unpushed(id, path) {
35160
- if (this.d.state.worktree(id)?.pr?.state !== "open")
35195
+ const wt = this.d.state.worktree(id);
35196
+ if (wt?.pr?.state !== "open")
35161
35197
  return;
35162
- return await aheadUpstream(path) ?? undefined;
35198
+ return await aheadUpstream(path, wt.branch) ?? undefined;
35163
35199
  }
35164
35200
  async freshCounts(worktreeId) {
35165
35201
  if (this.going.has(worktreeId))