@sideboard-ai/core 0.1.151 → 0.1.153

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 (61) hide show
  1. package/dist/{abletime-223A3LY6.js → abletime-H7LPGIP2.js} +3 -3
  2. package/dist/{abletime-CLC5UJC2.js → abletime-JSTJLXAW.js} +3 -3
  3. package/dist/agents/cursor-runner.js +2 -2
  4. package/dist/{agents-JAOMXDXN.js → agents-V24DYZI4.js} +8 -9
  5. package/dist/{agents-U2JCFXW4.js → agents-YVW75KBO.js} +8 -9
  6. package/dist/{app-settings-3CHCNIMD.js → app-settings-H3INSR67.js} +38 -4
  7. package/dist/{app-settings-J3JBCUOT.js → app-settings-VRQI3FIE.js} +38 -4
  8. package/dist/{caffeinate-hold-NBRUWYXO.js → caffeinate-hold-DBKMRZZO.js} +2 -2
  9. package/dist/{caffeinate-hold-X3JNPR42.js → caffeinate-hold-SK2QISPE.js} +2 -2
  10. package/dist/chunk-2F63IMCL.js +959 -0
  11. package/dist/{chunk-GCWAAPJ5.js → chunk-2NRNP7QP.js} +432 -239
  12. package/dist/{chunk-4XNSGDXE.js → chunk-3BWVOUQG.js} +244 -1
  13. package/dist/{chunk-LZAOX76N.js → chunk-4THE3EY7.js} +6 -6
  14. package/dist/{chunk-TUGKX5BD.js → chunk-7UURXPKD.js} +1 -1
  15. package/dist/{chunk-DCOZABNT.js → chunk-AO35MGOE.js} +1 -1
  16. package/dist/{chunk-3JBMACYX.js → chunk-CJQLPPEJ.js} +387 -15
  17. package/dist/{chunk-CZD4NRDZ.js → chunk-D7EGJ3EI.js} +244 -1
  18. package/dist/{chunk-QZQEXME2.js → chunk-EFVFB2AH.js} +1 -1
  19. package/dist/{chunk-B7XK37WZ.js → chunk-FDCFXMDG.js} +1 -1
  20. package/dist/{chunk-WLV7TRMR.js → chunk-JOK4XWBG.js} +1 -1
  21. package/dist/{chunk-BD2ICC4D.js → chunk-JTJHOYCU.js} +1 -1
  22. package/dist/{chunk-6H4TJ5TA.js → chunk-KMNODRWF.js} +481 -212
  23. package/dist/{chunk-GE7M3QTE.js → chunk-LQMFCS54.js} +387 -15
  24. package/dist/{chunk-G7SIQFBQ.js → chunk-LZ6QMEQC.js} +1 -1
  25. package/dist/{chunk-EUXOHTUK.js → chunk-LZQULFIT.js} +1 -1
  26. package/dist/{chunk-KLBOWJ74.js → chunk-OCQPTUR7.js} +1 -1
  27. package/dist/{chunk-UIS7BVCX.js → chunk-QZEZXYVV.js} +13 -13
  28. package/dist/chunk-RNC3GR6L.js +905 -0
  29. package/dist/{chunk-VHX673DI.js → chunk-WLPXZFRE.js} +1 -1
  30. package/dist/{chunk-7F4XE6UG.js → chunk-XD2BAANL.js} +5 -5
  31. package/dist/{connected-teams-XO766HB4.js → connected-teams-DBWAMP7G.js} +2 -2
  32. package/dist/{connected-teams-I2WZZZFA.js → connected-teams-WGONUQOF.js} +2 -2
  33. package/dist/{coordinator-prompt-LWYUZAXR.js → coordinator-prompt-7FHKCWXB.js} +5 -5
  34. package/dist/{coordinator-prompt-ZK5MII7W.js → coordinator-prompt-KX2FVVYT.js} +5 -5
  35. package/dist/{cursor-recover-NOQYOFRF.js → cursor-recover-L5MD6MBO.js} +1 -1
  36. package/dist/{cursor-recover-QSLWEZNA.js → cursor-recover-NDMLLNYX.js} +2 -2
  37. package/dist/{global-workspace-IB2CCEJF.js → global-workspace-2BRRKSDN.js} +5 -6
  38. package/dist/{global-workspace-T7RL3XEC.js → global-workspace-4LHCZ2DC.js} +5 -6
  39. package/dist/index.cjs +1961 -786
  40. package/dist/index.d.cts +228 -30
  41. package/dist/index.d.ts +228 -30
  42. package/dist/index.js +322 -110
  43. package/dist/mcp/run-stdio.cjs +1817 -1264
  44. package/dist/mcp/run-stdio.js +250 -82
  45. package/dist/{orchestrator-DPTRBKEH.js → orchestrator-5PSHBLP6.js} +10 -12
  46. package/dist/{orchestrator-Y5MWUU7C.js → orchestrator-ZSSU3FKW.js} +9 -11
  47. package/dist/{paths-UMEDHOS5.js → paths-53TLA7VQ.js} +1 -1
  48. package/dist/{paths-QCI3MTO5.js → paths-RRCLNSJB.js} +1 -1
  49. package/dist/{thread-store-WGASXXXR.js → thread-store-KBFBHT5G.js} +2 -2
  50. package/dist/{thread-store-57ZLHR3A.js → thread-store-MVQU6HPJ.js} +2 -2
  51. package/dist/{workspaces-ZXCUVCZC.js → workspaces-5UNDTN4G.js} +5 -7
  52. package/dist/{workspaces-VICQYI2N.js → workspaces-MQYNZXUK.js} +5 -7
  53. package/dist/{worktree-HTUM35NT.js → worktree-PEWDAX5O.js} +4 -4
  54. package/dist/{worktree-DPIREDFR.js → worktree-T4SWFSTT.js} +4 -4
  55. package/package.json +1 -1
  56. package/dist/chunk-77MP647F.js +0 -210
  57. package/dist/chunk-ADCEGG4A.js +0 -592
  58. package/dist/chunk-HWZWBJWL.js +0 -120
  59. package/dist/chunk-OXOU2FRS.js +0 -212
  60. package/dist/chunk-TSCWSAQL.js +0 -122
  61. package/dist/chunk-Y2FNDKHF.js +0 -538
@@ -33,18 +33,27 @@ import {
33
33
  summarizeTurnStderr,
34
34
  toolDescription,
35
35
  turnFailChatText
36
- } from "./chunk-LZAOX76N.js";
36
+ } from "./chunk-4THE3EY7.js";
37
37
  import {
38
- addWorkspace,
39
- ensureWorkspace,
40
- removeWorkspace,
41
- syncWorkspacesFromThreads
42
- } from "./chunk-TSCWSAQL.js";
38
+ extractPresentedPlan,
39
+ readPlanFile,
40
+ writePlanFile
41
+ } from "./chunk-2SDMENAL.js";
43
42
  import {
43
+ releaseCaffeinateHoldForThread
44
+ } from "./chunk-OCQPTUR7.js";
45
+ import {
46
+ SLACK_REPLY_FORMATTING,
47
+ addWorkspace,
44
48
  assertOrchestratorCapableAgent,
45
49
  attachmentsFromWorktreePaths,
46
50
  coerceOrchestratorAgent,
51
+ coordinatorSystemPrompt,
52
+ coordinatorTurnReminder,
47
53
  createGlobalChat,
54
+ enrichWorkspacesWithGithub,
55
+ ensureGlobalCoordinatorCwd,
56
+ ensureWorkspace,
48
57
  healOrchestrationSoccerTitles,
49
58
  isCloudCoordinatorThread,
50
59
  isGlobalRepoPath,
@@ -54,24 +63,11 @@ import {
54
63
  isSlackCoordinatorThread,
55
64
  orchestratorSessionPoisonedByBuiltins,
56
65
  persistPendingFileAttachments,
66
+ removeWorkspace,
57
67
  stageAbsolutePathsAsAttachments,
58
- stageBuffersAsAttachments
59
- } from "./chunk-Y2FNDKHF.js";
60
- import {
61
- extractPresentedPlan,
62
- readPlanFile,
63
- writePlanFile
64
- } from "./chunk-2SDMENAL.js";
65
- import {
66
- releaseCaffeinateHoldForThread
67
- } from "./chunk-KLBOWJ74.js";
68
- import {
69
- SLACK_REPLY_FORMATTING,
70
- coordinatorSystemPrompt,
71
- coordinatorTurnReminder,
72
- enrichWorkspacesWithGithub,
73
- ensureGlobalCoordinatorCwd
74
- } from "./chunk-OXOU2FRS.js";
68
+ stageBuffersAsAttachments,
69
+ syncWorkspacesFromThreads
70
+ } from "./chunk-RNC3GR6L.js";
75
71
  import {
76
72
  addPrStackLayer,
77
73
  allocateTeamName,
@@ -119,7 +115,7 @@ import {
119
115
  withRepoGitLock,
120
116
  worktreeDisplayLabelForGroup,
121
117
  worktreeNameFromPath
122
- } from "./chunk-3JBMACYX.js";
118
+ } from "./chunk-CJQLPPEJ.js";
123
119
  import {
124
120
  ATTACHMENTS_DIR,
125
121
  LEGACY_ATTACHMENTS_DIR
@@ -135,7 +131,7 @@ import {
135
131
  updateThread,
136
132
  withThreadLock,
137
133
  writeThread
138
- } from "./chunk-EUXOHTUK.js";
134
+ } from "./chunk-LZQULFIT.js";
139
135
  import {
140
136
  ensureWorktreeSideboardIgnored
141
137
  } from "./chunk-SYFVUF2C.js";
@@ -162,7 +158,7 @@ import {
162
158
  stripNestedElectronEnv,
163
159
  updateAdvancedSettings,
164
160
  writeSecureJson
165
- } from "./chunk-CZD4NRDZ.js";
161
+ } from "./chunk-D7EGJ3EI.js";
166
162
  import {
167
163
  writePrivateFile
168
164
  } from "./chunk-NSTQ6QKD.js";
@@ -175,9 +171,10 @@ import {
175
171
  sideboardReposDir,
176
172
  sideboardWorkspacesDir,
177
173
  threadLivePath,
174
+ threadsDir,
178
175
  workspaceSettingsSourceLabel,
179
176
  worktreesRoot
180
- } from "./chunk-BD2ICC4D.js";
177
+ } from "./chunk-JTJHOYCU.js";
181
178
 
182
179
  // src/orchestrator/orchestrator.ts
183
180
  import { EventEmitter } from "events";
@@ -395,7 +392,7 @@ async function continueSourceThread(threadId, prompt) {
395
392
  await continueOnReply(threadId, prompt);
396
393
  return;
397
394
  }
398
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-Y5MWUU7C.js");
395
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-ZSSU3FKW.js");
399
396
  await getOrchestrator2().send(threadId, prompt);
400
397
  } catch {
401
398
  }
@@ -655,7 +652,7 @@ async function pollSlackOutboundWatches(opts) {
655
652
  }
656
653
 
657
654
  // src/orchestrator/orchestrator.ts
658
- import { existsSync as existsSync16 } from "fs";
655
+ import { existsSync as existsSync17 } from "fs";
659
656
 
660
657
  // src/agents/spawn.ts
661
658
  import { createInterface } from "readline";
@@ -736,9 +733,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
736
733
  `Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
737
734
  );
738
735
  }
739
- const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-T7RL3XEC.js");
736
+ const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-4LHCZ2DC.js");
740
737
  if (isGlobalThread2(thread)) {
741
- const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-ZK5MII7W.js");
738
+ const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-KX2FVVYT.js");
742
739
  ensureGlobalCoordinatorCwd2(
743
740
  isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
744
741
  );
@@ -1590,32 +1587,15 @@ File: src/client/frontends/desktop/core/UserData.ts
1590
1587
 
1591
1588
  ## Growing the rules
1592
1589
 
1593
- If a blocking issue is a missing or ambiguous repo rule that will recur, add one sentence to \`.claude/skills/review/SKILL.md\` (create the skill if it is missing \u2014 that is allowed and should be committed). Do not only patch this diff when the same miss will happen again. Do not write new skills under \`.sideboard/skills/\`. Do not use \`.sideboard/review.md\` for new notes.
1590
+ If a blocking issue is a missing or ambiguous repo rule that will recur, add one sentence to \`.claude/skills/review/SKILL.md\` when that skill already exists. Otherwise write it to \`.context/review.md\` (do not create a review skill). Do not only patch this diff when the same miss will happen again. Do not write new skills under \`.sideboard/skills/\`.
1594
1591
  `;
1595
1592
  var REVIEW_SKILL_PATH = ".claude/skills/review/SKILL.md";
1596
1593
  var REVIEW_SKILL_NAME = "review";
1597
- var REVIEW_SKILL_FRONTMATTER = `---
1598
- name: review
1599
- description: >-
1600
- Merge-readiness review for this repository. Use when reviewing a PR, worktree
1601
- diff, or Sideboard Review (Approve / Approve with nits / Request changes).
1602
- ---
1603
-
1604
- `;
1605
- function wrapReviewSkillMarkdown(body) {
1606
- const trimmed = body.replace(/^\uFEFF/, "").trim();
1607
- const source = trimmed || REVIEW_REQUEST_TEMPLATE.trim();
1608
- if (/^---\s*\n[\s\S]*?\n---\s*\n/.test(source)) {
1609
- return source.endsWith("\n") ? source : `${source}
1610
- `;
1611
- }
1612
- return `${REVIEW_SKILL_FRONTMATTER}${source}
1613
- `;
1614
- }
1615
1594
 
1616
1595
  // src/review/request-review.ts
1617
1596
  var REPO_REVIEW_PATH = ".sideboard/review.md";
1618
1597
  var REPO_REVIEW_NAME = "review.md";
1598
+ var CONTEXT_REVIEW_PATH = ".context/review.md";
1619
1599
  var REVIEW_REQUEST_PATH = `${ATTACHMENTS_DIR}/Review request.md`;
1620
1600
  var LEGACY_REVIEW_REQUEST_PATH = `${LEGACY_ATTACHMENTS_DIR}/Review request.md`;
1621
1601
  var REVIEW_REQUEST_NAME = "Review request.md";
@@ -1662,37 +1642,57 @@ function skillGuidelines(content, source) {
1662
1642
  source
1663
1643
  };
1664
1644
  }
1665
- function ensureReviewSkillFile(worktreePath) {
1666
- const abs = join4(worktreePath, REVIEW_SKILL_PATH);
1667
- const existing = readTextIfPresent(abs);
1668
- if (existing) {
1669
- return { path: REVIEW_SKILL_PATH, content: existing, wrote: false };
1670
- }
1671
- const fromRepo = readTextIfPresent(join4(worktreePath, REPO_REVIEW_PATH));
1672
- const fromLocal = readLocalGuidelines(worktreePath)?.content ?? null;
1673
- const content = wrapReviewSkillMarkdown(fromRepo ?? fromLocal ?? REVIEW_REQUEST_TEMPLATE);
1674
- mkdirSync(dirname(abs), { recursive: true });
1675
- writeFileSync(abs, content, "utf8");
1676
- return { path: REVIEW_SKILL_PATH, content, wrote: true };
1645
+ function contextGuidelines(content, source) {
1646
+ return {
1647
+ path: CONTEXT_REVIEW_PATH,
1648
+ name: REPO_REVIEW_NAME,
1649
+ content,
1650
+ source
1651
+ };
1677
1652
  }
1678
- function resolveReviewGuidelines(worktreePath) {
1653
+ function writeContextGuidelines(worktreePath, content) {
1654
+ const abs = join4(worktreePath, CONTEXT_REVIEW_PATH);
1655
+ mkdirSync(dirname(abs), { recursive: true });
1656
+ const body = content.endsWith("\n") ? content : `${content}
1657
+ `;
1658
+ writeFileSync(abs, body, "utf8");
1659
+ return body;
1660
+ }
1661
+ function readSideboardReview(worktreePath, repoPath) {
1662
+ const fromWorktree = readTextIfPresent(join4(worktreePath, REPO_REVIEW_PATH));
1663
+ if (fromWorktree) return fromWorktree;
1664
+ const repo = repoPath?.replace(/\/+$/, "");
1665
+ const wt = worktreePath.replace(/\/+$/, "");
1666
+ if (!repo || repo === wt) return null;
1667
+ return readTextIfPresent(join4(repo, REPO_REVIEW_PATH));
1668
+ }
1669
+ function ensureReviewGuidelinesFile(worktreePath, repoPath) {
1679
1670
  const skillContent = readTextIfPresent(join4(worktreePath, REVIEW_SKILL_PATH));
1680
- if (skillContent) return skillGuidelines(skillContent, "skill");
1681
- const local = readLocalGuidelines(worktreePath);
1682
- if (local) {
1683
- return {
1684
- path: local.path,
1685
- name: REVIEW_REQUEST_NAME,
1686
- content: local.content,
1687
- source: "local"
1688
- };
1671
+ if (skillContent) {
1672
+ return { ...skillGuidelines(skillContent, "skill"), wrote: false };
1673
+ }
1674
+ const contextContent = readTextIfPresent(join4(worktreePath, CONTEXT_REVIEW_PATH));
1675
+ if (contextContent && !shouldRefreshReviewRequestTemplate(contextContent)) {
1676
+ return { ...contextGuidelines(contextContent, "local"), wrote: false };
1689
1677
  }
1690
- const seeded = ensureReviewSkillFile(worktreePath);
1691
- return skillGuidelines(seeded.content, seeded.wrote ? "stock" : "skill");
1678
+ const fromSideboard = readSideboardReview(worktreePath, repoPath);
1679
+ if (fromSideboard && !shouldRefreshReviewRequestTemplate(fromSideboard)) {
1680
+ const body2 = writeContextGuidelines(worktreePath, fromSideboard);
1681
+ return { ...contextGuidelines(body2, "repo"), wrote: true };
1682
+ }
1683
+ const local = readLocalGuidelines(worktreePath);
1684
+ const body = writeContextGuidelines(worktreePath, local?.content ?? REVIEW_REQUEST_TEMPLATE);
1685
+ return {
1686
+ ...contextGuidelines(body, local ? "local" : "stock"),
1687
+ wrote: true
1688
+ };
1689
+ }
1690
+ function resolveReviewGuidelines(worktreePath, repoPath) {
1691
+ return ensureReviewGuidelinesFile(worktreePath, repoPath);
1692
1692
  }
1693
1693
  function buildReviewRequestAttachment(content, opts) {
1694
1694
  const path = opts?.path ?? REVIEW_SKILL_PATH;
1695
- const name = opts?.name ?? (path === REVIEW_SKILL_PATH ? REVIEW_SKILL_NAME : path === REPO_REVIEW_PATH ? REPO_REVIEW_NAME : REVIEW_REQUEST_NAME);
1695
+ const name = opts?.name ?? (path === REVIEW_SKILL_PATH ? REVIEW_SKILL_NAME : path === REPO_REVIEW_PATH || path === CONTEXT_REVIEW_PATH ? REPO_REVIEW_NAME : REVIEW_REQUEST_NAME);
1696
1696
  return {
1697
1697
  id: randomUUID2(),
1698
1698
  name,
@@ -1712,7 +1712,7 @@ async function requestReview(threadRef, send) {
1712
1712
  if (from.status === "archived") {
1713
1713
  throw new Error(`Thread is archived: ${from.id}`);
1714
1714
  }
1715
- const guidelines = resolveReviewGuidelines(from.worktreePath);
1715
+ const guidelines = resolveReviewGuidelines(from.worktreePath, from.repoPath);
1716
1716
  const tab = createChatTab({
1717
1717
  fromThreadId: from.id,
1718
1718
  title: "Review",
@@ -1937,6 +1937,8 @@ async function attachAbort(handle, signal) {
1937
1937
  async function runSetupScript(repoPath, worktreePath, onLine, opts) {
1938
1938
  const settings = loadWorkspaceSettings(worktreePath, repoPath);
1939
1939
  if (!settings?.setup) return { ran: false, exitCode: null, source: null };
1940
+ const source = workspaceSettingsSourceLabel(worktreePath, repoPath);
1941
+ onLine?.(`[setup] ${source ?? "settings.toml"}`);
1940
1942
  const handle = await spawnWorkspaceScript(settings.setup, {
1941
1943
  worktreePath,
1942
1944
  repoPath,
@@ -1947,7 +1949,7 @@ async function runSetupScript(repoPath, worktreePath, onLine, opts) {
1947
1949
  return {
1948
1950
  ran: true,
1949
1951
  exitCode,
1950
- source: workspaceSettingsSourceLabel(worktreePath, repoPath),
1952
+ source,
1951
1953
  kill: handle.kill
1952
1954
  };
1953
1955
  }
@@ -1971,11 +1973,9 @@ async function runConventionSetup(repoPath, worktreePath, onLine, opts) {
1971
1973
  }
1972
1974
  async function runWorkspaceSetup(repoPath, worktreePath, onLine, opts) {
1973
1975
  await ensureWorktreeSideboardIgnored(worktreePath);
1974
- const reviewSkill = ensureReviewSkillFile(worktreePath);
1975
- if (reviewSkill.wrote) {
1976
- onLine?.(
1977
- `[setup] wrote ${REVIEW_SKILL_PATH} \u2014 commit it so later worktrees inherit Review guidelines`
1978
- );
1976
+ const review = ensureReviewGuidelinesFile(worktreePath, repoPath);
1977
+ if (review.wrote) {
1978
+ onLine?.(`[setup] wrote ${review.path} \u2014 local Review guidelines`);
1979
1979
  }
1980
1980
  let setup = await runSetupScript(repoPath, worktreePath, onLine, opts);
1981
1981
  if (!setup.ran) {
@@ -2078,9 +2078,114 @@ async function startDevServer(repoPath, worktreePath, onLine, opts) {
2078
2078
  };
2079
2079
  }
2080
2080
 
2081
- // src/git/orphan-cleanup.ts
2082
- import { existsSync as existsSync5, readdirSync as readdirSync2, statSync } from "fs";
2081
+ // src/store/setup-log.ts
2082
+ import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
2083
2083
  import { join as join6 } from "path";
2084
+ var EMPTY = {
2085
+ output: "",
2086
+ running: false,
2087
+ exitCode: null,
2088
+ source: null
2089
+ };
2090
+ var memory = /* @__PURE__ */ new Map();
2091
+ var persistTimers = /* @__PURE__ */ new Map();
2092
+ function setupLogPath(threadId) {
2093
+ return join6(threadsDir(), `${threadId}.setup.log.json`);
2094
+ }
2095
+ function emptySetupLog() {
2096
+ return { ...EMPTY };
2097
+ }
2098
+ function clone(snap) {
2099
+ return { ...snap };
2100
+ }
2101
+ function persistNow(threadId) {
2102
+ const timer = persistTimers.get(threadId);
2103
+ if (timer) {
2104
+ clearTimeout(timer);
2105
+ persistTimers.delete(threadId);
2106
+ }
2107
+ const snap = memory.get(threadId);
2108
+ if (!snap) return;
2109
+ try {
2110
+ writeFileSync2(setupLogPath(threadId), `${JSON.stringify(snap)}
2111
+ `, "utf8");
2112
+ } catch {
2113
+ }
2114
+ }
2115
+ function schedulePersist(threadId) {
2116
+ if (persistTimers.has(threadId)) return;
2117
+ persistTimers.set(
2118
+ threadId,
2119
+ setTimeout(() => {
2120
+ persistTimers.delete(threadId);
2121
+ persistNow(threadId);
2122
+ }, 100)
2123
+ );
2124
+ }
2125
+ function loadFromDisk(threadId) {
2126
+ const path = setupLogPath(threadId);
2127
+ if (!existsSync5(path)) return null;
2128
+ try {
2129
+ const raw = JSON.parse(readFileSync5(path, "utf8"));
2130
+ return {
2131
+ output: typeof raw.output === "string" ? raw.output : "",
2132
+ running: Boolean(raw.running),
2133
+ exitCode: typeof raw.exitCode === "number" ? raw.exitCode : null,
2134
+ source: typeof raw.source === "string" ? raw.source : null
2135
+ };
2136
+ } catch {
2137
+ return null;
2138
+ }
2139
+ }
2140
+ function readSetupLog(threadId) {
2141
+ const cached = memory.get(threadId);
2142
+ if (cached) return clone(cached);
2143
+ const disk = loadFromDisk(threadId);
2144
+ if (disk) {
2145
+ memory.set(threadId, disk);
2146
+ return clone(disk);
2147
+ }
2148
+ return emptySetupLog();
2149
+ }
2150
+ function beginSetupLog(threadId) {
2151
+ const snap = {
2152
+ output: "",
2153
+ running: true,
2154
+ exitCode: null,
2155
+ source: null
2156
+ };
2157
+ memory.set(threadId, snap);
2158
+ persistNow(threadId);
2159
+ return clone(snap);
2160
+ }
2161
+ function appendSetupLog(threadId, line) {
2162
+ const current = memory.get(threadId) ?? readSetupLog(threadId);
2163
+ const snap = {
2164
+ ...current,
2165
+ output: current.output ? `${current.output}
2166
+ ${line}` : line,
2167
+ running: true
2168
+ };
2169
+ memory.set(threadId, snap);
2170
+ schedulePersist(threadId);
2171
+ return clone(snap);
2172
+ }
2173
+ function finishSetupLog(threadId, exitCode, source) {
2174
+ const current = memory.get(threadId) ?? readSetupLog(threadId);
2175
+ const snap = {
2176
+ ...current,
2177
+ running: false,
2178
+ exitCode,
2179
+ source: source ?? current.source
2180
+ };
2181
+ memory.set(threadId, snap);
2182
+ persistNow(threadId);
2183
+ return clone(snap);
2184
+ }
2185
+
2186
+ // src/git/orphan-cleanup.ts
2187
+ import { existsSync as existsSync6, readdirSync as readdirSync2, statSync } from "fs";
2188
+ import { join as join7 } from "path";
2084
2189
  function isSideboardWorktreePath(path) {
2085
2190
  return path.includes("/.sideboard/worktrees/") || path.includes("/sideboard/workspaces/");
2086
2191
  }
@@ -2091,7 +2196,7 @@ async function findOrphanWorktrees(repoPaths) {
2091
2196
  repoPaths?.length ? repoPaths : threads.map((t) => t.repoPath).filter(Boolean)
2092
2197
  );
2093
2198
  const homeRoot = sideboardWorkspacesDir();
2094
- if (existsSync5(homeRoot)) {
2199
+ if (existsSync6(homeRoot)) {
2095
2200
  try {
2096
2201
  for (const entry of readdirSync2(homeRoot, { withFileTypes: true })) {
2097
2202
  if (!entry.isDirectory()) continue;
@@ -2103,7 +2208,7 @@ async function findOrphanWorktrees(repoPaths) {
2103
2208
  const orphans = [];
2104
2209
  const seen = /* @__PURE__ */ new Set();
2105
2210
  for (const repoPath of repos) {
2106
- if (!repoPath || !existsSync5(repoPath)) continue;
2211
+ if (!repoPath || !existsSync6(repoPath)) continue;
2107
2212
  try {
2108
2213
  const wts = await listWorktrees(repoPath);
2109
2214
  for (const wt of wts) {
@@ -2124,12 +2229,12 @@ async function findOrphanWorktrees(repoPaths) {
2124
2229
  }
2125
2230
  try {
2126
2231
  const root = worktreesRoot(repoPath);
2127
- if (existsSync5(root)) {
2232
+ if (existsSync6(root)) {
2128
2233
  for (const entry of readdirSync2(root, { withFileTypes: true })) {
2129
2234
  if (!entry.isDirectory()) continue;
2130
- const path = join6(root, entry.name).replace(/\/$/, "");
2235
+ const path = join7(root, entry.name).replace(/\/$/, "");
2131
2236
  if (known.has(path) || seen.has(path)) continue;
2132
- if (!existsSync5(join6(path, ".git"))) continue;
2237
+ if (!existsSync6(join7(path, ".git"))) continue;
2133
2238
  seen.add(path);
2134
2239
  let mtimeMs = 0;
2135
2240
  try {
@@ -2269,8 +2374,8 @@ async function applyThreadIntoMain(thread, opts) {
2269
2374
  }
2270
2375
 
2271
2376
  // src/git/clone-repo.ts
2272
- import { existsSync as existsSync6 } from "fs";
2273
- import { basename as basename2, join as join7 } from "path";
2377
+ import { existsSync as existsSync7 } from "fs";
2378
+ import { basename as basename2, join as join8 } from "path";
2274
2379
  import { execa as execa4 } from "execa";
2275
2380
  async function cloneRepoIntoSideboard(opts) {
2276
2381
  const url = opts.url.trim();
@@ -2281,8 +2386,8 @@ async function cloneRepoIntoSideboard(opts) {
2281
2386
  name = leaf || "repo";
2282
2387
  }
2283
2388
  name = name.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "repo";
2284
- const dest = join7(sideboardReposDir(), name);
2285
- if (existsSync6(dest)) {
2389
+ const dest = join8(sideboardReposDir(), name);
2390
+ if (existsSync7(dest)) {
2286
2391
  const repoPath2 = await resolveRepoRoot(dest);
2287
2392
  const workspace2 = await ensureWorkspace(repoPath2);
2288
2393
  return { repoPath: repoPath2, workspace: workspace2 };
@@ -2299,14 +2404,14 @@ async function cloneRepoIntoSideboard(opts) {
2299
2404
  }
2300
2405
 
2301
2406
  // src/store/desktop-host.ts
2302
- import { readFileSync as readFileSync5, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
2303
- import { join as join8 } from "path";
2407
+ import { readFileSync as readFileSync6, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
2408
+ import { join as join9 } from "path";
2304
2409
  function desktopHostPidPath() {
2305
- return join8(appDataDir(), "desktop-host.pid");
2410
+ return join9(appDataDir(), "desktop-host.pid");
2306
2411
  }
2307
2412
  function readDesktopHostPid() {
2308
2413
  try {
2309
- const pid = Number.parseInt(readFileSync5(desktopHostPidPath(), "utf8").trim(), 10);
2414
+ const pid = Number.parseInt(readFileSync6(desktopHostPidPath(), "utf8").trim(), 10);
2310
2415
  if (!Number.isFinite(pid) || pid <= 0) return null;
2311
2416
  return pid;
2312
2417
  } catch {
@@ -2373,7 +2478,7 @@ function notifyParentOfChildHalt(child, status, send) {
2373
2478
  }
2374
2479
 
2375
2480
  // src/threads/create.ts
2376
- import { existsSync as existsSync7 } from "fs";
2481
+ import { existsSync as existsSync8 } from "fs";
2377
2482
 
2378
2483
  // src/detect/detect.ts
2379
2484
  var REQUIRE_AGENT_TIMEOUT_MS = 8e3;
@@ -2539,12 +2644,19 @@ function urlsMatch(a, b) {
2539
2644
  const norm = (u) => u.trim().replace(/\/+$/, "").toLowerCase();
2540
2645
  return Boolean(a && b && norm(a) === norm(b));
2541
2646
  }
2647
+ function threadPrUrlMatchesNumber(thread, number) {
2648
+ if (!Number.isFinite(number) || number < 1) return false;
2649
+ const url = (thread.prUrl ?? "").trim();
2650
+ if (!url) return false;
2651
+ return new RegExp(`/pull/${number}(?:/|$|[?#])`, "i").test(url);
2652
+ }
2542
2653
  function threadMatchesPr(thread, pr) {
2543
2654
  const num = String(pr.number);
2544
2655
  if (thread.sourceType === "pr" && normalizeIssueKey(thread.sourceRef ?? "") === num) {
2545
2656
  return true;
2546
2657
  }
2547
2658
  if (thread.prUrl && pr.url && urlsMatch(thread.prUrl, pr.url)) return true;
2659
+ if (threadPrUrlMatchesNumber(thread, pr.number)) return true;
2548
2660
  const head = pr.headRefName.trim().toLowerCase();
2549
2661
  if (!head) return false;
2550
2662
  const branch = (thread.branchName ?? "").trim().toLowerCase();
@@ -2597,19 +2709,32 @@ function findLiveThreadForCreate(input, threads) {
2597
2709
  (t) => threadMatchesPr(t, {
2598
2710
  number: Number.isFinite(number) ? number : -1,
2599
2711
  title: input.title ?? "",
2600
- url: "",
2601
- headRefName: ""
2712
+ url: input.prUrl ?? "",
2713
+ headRefName: input.headRefName ?? ""
2602
2714
  })
2603
2715
  );
2604
2716
  }
2605
2717
  if (input.sourceType === "branch") {
2606
2718
  if (isDefaultBranchCreateRef(input.sourceRef)) return void 0;
2607
- return live.find(
2719
+ const byBranch = live.find(
2608
2720
  (t) => threadMatchesBranch(t, {
2609
2721
  ref: input.sourceRef,
2610
2722
  repoPath: input.repoPath
2611
2723
  })
2612
2724
  );
2725
+ if (byBranch) return byBranch;
2726
+ if (input.prUrl || input.headRefName) {
2727
+ const number = Number(normalizeIssueKey(input.sourceRef));
2728
+ return live.find(
2729
+ (t) => threadMatchesPr(t, {
2730
+ number: Number.isFinite(number) ? number : -1,
2731
+ title: input.title ?? "",
2732
+ url: input.prUrl ?? "",
2733
+ headRefName: input.headRefName ?? input.sourceRef
2734
+ })
2735
+ );
2736
+ }
2737
+ return void 0;
2613
2738
  }
2614
2739
  return void 0;
2615
2740
  }
@@ -2835,42 +2960,53 @@ function assembleHomeBoard(input) {
2835
2960
  }
2836
2961
  return { columns, hidden, totals };
2837
2962
  }
2838
- var HOME_BOARD_AGENT_HINT = "Home is a Kanban of worktrees (one card per checkout; sibling chat tabs nest as inner cards). create_thread reuses a live worktree for the same ticket, PR, or named branch \u2014 do not recreate it. Columns are the path to merge: New (no PR) \u2192 Draft (draft PR) \u2192 Review (open PR) \u2192 Merged. Archive removes the card to Settings \u2192 History. Queued/running are activity on the card, not columns. Orchestration chats stay in the sidebar. Do not invent status.";
2963
+ var HOME_BOARD_AGENT_HINT = "Home is a Kanban of worktrees (one card per checkout; sibling chat tabs nest as inner cards). Do not create a second worktree for a ticket, PR, or named branch that already has a live checkout \u2014 create_thread / start_board_card return that thread (alreadyStarted). Creating from the default branch still opens a new isolated worktree. Columns are the path to merge: New (no PR) \u2192 Draft (draft PR) \u2192 Review (open PR) \u2192 Merged. Archive removes the card to Settings \u2192 History. Queued/running are activity on the card, not columns. Orchestration chats stay in the sidebar. Do not invent status.";
2839
2964
 
2840
2965
  // src/threads/create.ts
2841
2966
  function persistCreateAttachments(worktreePath, attachments) {
2842
2967
  return persistPendingFileAttachments(worktreePath, attachments ?? []);
2843
2968
  }
2969
+ function liveThreadsForCreate() {
2970
+ return listThreads({ includeArchived: false }).map((t) => ({
2971
+ ...t,
2972
+ repoPath: canonicalizeRepoPath(t.repoPath)
2973
+ }));
2974
+ }
2975
+ function reuseLiveThread(input, repoPath, match) {
2976
+ if (input.reuseExisting === false) return void 0;
2977
+ const existing = findLiveThreadForCreate(
2978
+ {
2979
+ sourceType: match.sourceType,
2980
+ sourceRef: match.sourceRef,
2981
+ repoPath,
2982
+ title: match.title ?? input.title,
2983
+ cowboy: input.cowboy,
2984
+ prUrl: match.prUrl,
2985
+ headRefName: match.headRefName
2986
+ },
2987
+ liveThreadsForCreate()
2988
+ );
2989
+ if (!existing) return void 0;
2990
+ const thread = readThread(existing.id) ?? existing;
2991
+ if (!input.attachments?.length) return thread;
2992
+ return updateThread(thread.id, {
2993
+ attachments: persistCreateAttachments(thread.worktreePath, [
2994
+ ...thread.attachments,
2995
+ ...input.attachments
2996
+ ])
2997
+ });
2998
+ }
2844
2999
  async function createThread(input, _onSetupLine) {
2845
3000
  const repoPath = await resolveRepoRoot(input.repoPath);
2846
- if (!existsSync7(repoPath)) {
3001
+ if (!existsSync8(repoPath)) {
2847
3002
  throw new Error(`Repo not found: ${repoPath}`);
2848
3003
  }
2849
- if (input.reuseExisting !== false) {
2850
- const existing = findLiveThreadForCreate(
2851
- {
2852
- sourceType: input.sourceType,
2853
- sourceRef: input.sourceRef,
2854
- repoPath,
2855
- title: input.title,
2856
- cowboy: input.cowboy
2857
- },
2858
- listThreads({ includeArchived: false }).map((t) => ({
2859
- ...t,
2860
- repoPath: canonicalizeRepoPath(t.repoPath)
2861
- }))
2862
- );
2863
- if (existing) {
2864
- const thread2 = readThread(existing.id) ?? existing;
2865
- if (!input.attachments?.length) return thread2;
2866
- return updateThread(thread2.id, {
2867
- attachments: persistCreateAttachments(thread2.worktreePath, [
2868
- ...thread2.attachments,
2869
- ...input.attachments
2870
- ])
2871
- });
2872
- }
2873
- }
3004
+ const reused = reuseLiveThread(input, repoPath, {
3005
+ sourceType: input.sourceType,
3006
+ sourceRef: input.sourceRef,
3007
+ title: input.title
3008
+ });
3009
+ if (reused) return reused;
2874
3010
  const resolved = resolveNewThreadOptions({
2875
3011
  agent: input.agent,
2876
3012
  model: input.model,
@@ -2879,7 +3015,7 @@ async function createThread(input, _onSetupLine) {
2879
3015
  });
2880
3016
  await requireAgent(resolved.agent);
2881
3017
  if (input.cowboy) {
2882
- const { cowboyModeEnabled } = await import("./app-settings-J3JBCUOT.js");
3018
+ const { cowboyModeEnabled } = await import("./app-settings-VRQI3FIE.js");
2883
3019
  if (!cowboyModeEnabled()) {
2884
3020
  throw new Error(
2885
3021
  "Cowboy mode is off. Enable it in Settings \u2192 Advanced."
@@ -2937,7 +3073,7 @@ async function createThread(input, _onSetupLine) {
2937
3073
  let sourceIsFork = false;
2938
3074
  if (!input.cowboy && sourceType === "branch" && (!sourceRef || sourceRef === "HEAD" || sourceRef === "default")) {
2939
3075
  if (isAbleTimeConnected() && getIssueSource() === "abletime") {
2940
- const { ensureAbleTimeTask, issueAttachmentForAbleTimeTask } = await import("./abletime-CLC5UJC2.js");
3076
+ const { ensureAbleTimeTask, issueAttachmentForAbleTimeTask } = await import("./abletime-JSTJLXAW.js");
2941
3077
  const task = await ensureAbleTimeTask({
2942
3078
  title: title?.trim() || "Untitled work",
2943
3079
  description: input.prompt?.trim() || void 0
@@ -2947,6 +3083,12 @@ async function createThread(input, _onSetupLine) {
2947
3083
  persistedSourceRef = task.identifier;
2948
3084
  title = title?.trim() || task.title;
2949
3085
  attachments = [...attachments, issueAttachmentForAbleTimeTask(task)];
3086
+ const reusedTicket = reuseLiveThread(
3087
+ { ...input, sourceType: "ticket", sourceRef, title, attachments },
3088
+ repoPath,
3089
+ { sourceType: "ticket", sourceRef, title }
3090
+ );
3091
+ if (reusedTicket) return reusedTicket;
2950
3092
  }
2951
3093
  }
2952
3094
  let prUrl = null;
@@ -2956,6 +3098,14 @@ async function createThread(input, _onSetupLine) {
2956
3098
  if (!Number.isFinite(num)) throw new Error(`Invalid PR number: ${input.sourceRef}`);
2957
3099
  const pr = await getPr(repoPath, num);
2958
3100
  if (!pr) throw new Error(`PR #${num} not found`);
3101
+ const reusedPr = reuseLiveThread(input, repoPath, {
3102
+ sourceType: "pr",
3103
+ sourceRef: String(pr.number),
3104
+ title: input.title ?? pr.title,
3105
+ prUrl: pr.url,
3106
+ headRefName: pr.headRefName
3107
+ });
3108
+ if (reusedPr) return reusedPr;
2959
3109
  sourceIsFork = pr.isCrossRepository;
2960
3110
  prUrl = pr.url;
2961
3111
  const localFetchBranch = `sideboard-pr-${pr.number}`;
@@ -2968,9 +3118,19 @@ async function createThread(input, _onSetupLine) {
2968
3118
  sourceRef = await resolveDefaultBranch(repoPath);
2969
3119
  } else {
2970
3120
  const existing = await getPrForHeadBranch(repoPath, sourceRef);
2971
- if (existing?.url) {
2972
- prUrl = existing.url;
2973
- prTitle = existing.title;
3121
+ if (existing) {
3122
+ const reusedPr = reuseLiveThread(input, repoPath, {
3123
+ sourceType: "pr",
3124
+ sourceRef: String(existing.number),
3125
+ title: input.title ?? existing.title,
3126
+ prUrl: existing.url,
3127
+ headRefName: existing.headRefName
3128
+ });
3129
+ if (reusedPr) return reusedPr;
3130
+ if (existing.url) {
3131
+ prUrl = existing.url;
3132
+ prTitle = existing.title;
3133
+ }
2974
3134
  }
2975
3135
  }
2976
3136
  } else if (sourceType === "adopt") {
@@ -3009,15 +3169,6 @@ async function createThread(input, _onSetupLine) {
3009
3169
  await ensureWorkspace(repoPath);
3010
3170
  return readThread(thread.id) ?? thread;
3011
3171
  }
3012
- async function listLinearIssues(agent, repoPath) {
3013
- const { getAdapter: getAdapter2 } = await import("./agents-JAOMXDXN.js");
3014
- await requireAgent(agent, { requireLinear: true });
3015
- const adapter = getAdapter2(agent);
3016
- if (!adapter.listLinearIssues) {
3017
- throw new Error(`${agent} does not support Linear issue listing`);
3018
- }
3019
- return adapter.listLinearIssues(repoPath);
3020
- }
3021
3172
 
3022
3173
  // src/threads/cowboy.ts
3023
3174
  function isCowboyThread(thread) {
@@ -3033,11 +3184,11 @@ function shouldRemoveWorktreeOnTeardown(thread) {
3033
3184
 
3034
3185
  // src/store/turn-live.ts
3035
3186
  import {
3036
- existsSync as existsSync8,
3187
+ existsSync as existsSync9,
3037
3188
  renameSync,
3038
3189
  unlinkSync as unlinkSync2,
3039
- writeFileSync as writeFileSync3,
3040
- readFileSync as readFileSync6
3190
+ writeFileSync as writeFileSync4,
3191
+ readFileSync as readFileSync7
3041
3192
  } from "fs";
3042
3193
  var buffers = /* @__PURE__ */ new Map();
3043
3194
  var FLUSH_MS = 800;
@@ -3114,7 +3265,7 @@ function writeTurnLive(threadId, progress) {
3114
3265
  const path = threadLivePath(threadId);
3115
3266
  const tmp = `${path}.${process.pid}.tmp`;
3116
3267
  try {
3117
- writeFileSync3(tmp, JSON.stringify(progress), "utf8");
3268
+ writeFileSync4(tmp, JSON.stringify(progress), "utf8");
3118
3269
  renameSync(tmp, path);
3119
3270
  } catch {
3120
3271
  try {
@@ -3125,9 +3276,9 @@ function writeTurnLive(threadId, progress) {
3125
3276
  }
3126
3277
  function readTurnLive(threadId) {
3127
3278
  const path = threadLivePath(threadId);
3128
- if (!existsSync8(path)) return null;
3279
+ if (!existsSync9(path)) return null;
3129
3280
  try {
3130
- const raw = JSON.parse(readFileSync6(path, "utf8"));
3281
+ const raw = JSON.parse(readFileSync7(path, "utf8"));
3131
3282
  if (!raw || typeof raw.summary !== "string") return null;
3132
3283
  return raw;
3133
3284
  } catch {
@@ -3139,7 +3290,7 @@ function clearTurnLive(threadId) {
3139
3290
  if (buf?.timer) clearTimeout(buf.timer);
3140
3291
  buffers.delete(threadId);
3141
3292
  const path = threadLivePath(threadId);
3142
- if (!existsSync8(path)) return;
3293
+ if (!existsSync9(path)) return;
3143
3294
  try {
3144
3295
  unlinkSync2(path);
3145
3296
  } catch {
@@ -3323,26 +3474,26 @@ function createQuotaFailoverChat(from, fallbackAgent, limitText) {
3323
3474
  import { execFileSync } from "child_process";
3324
3475
  import {
3325
3476
  copyFileSync as copyFileSync2,
3326
- existsSync as existsSync9,
3477
+ existsSync as existsSync10,
3327
3478
  mkdtempSync,
3328
3479
  readdirSync as readdirSync3,
3329
- readFileSync as readFileSync7,
3480
+ readFileSync as readFileSync8,
3330
3481
  rmSync
3331
3482
  } from "fs";
3332
3483
  import { tmpdir } from "os";
3333
- import { join as join9 } from "path";
3484
+ import { join as join10 } from "path";
3334
3485
  import { createRequire } from "module";
3335
- var CONDUCTOR_APP_SUPPORT = join9(
3486
+ var CONDUCTOR_APP_SUPPORT = join10(
3336
3487
  process.env.HOME ?? "",
3337
3488
  "Library",
3338
3489
  "Application Support",
3339
3490
  "com.conductor.app"
3340
3491
  );
3341
- var CONDUCTOR_DB = join9(CONDUCTOR_APP_SUPPORT, "conductor.db");
3342
- var CURSOR_SDK_STORE = join9(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
3492
+ var CONDUCTOR_DB = join10(CONDUCTOR_APP_SUPPORT, "conductor.db");
3493
+ var CURSOR_SDK_STORE = join10(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
3343
3494
  function thisModuleFile() {
3344
3495
  const cjsFile = typeof __filename !== "undefined" ? __filename : "";
3345
- return cjsFile || process.argv[1] || join9(process.cwd(), "package.json");
3496
+ return cjsFile || process.argv[1] || join10(process.cwd(), "package.json");
3346
3497
  }
3347
3498
  function openReadonlySqlite(file) {
3348
3499
  const req = createRequire(thisModuleFile());
@@ -3360,7 +3511,7 @@ function mapAgentType(raw) {
3360
3511
  return null;
3361
3512
  }
3362
3513
  function resolveConductorCursorAgentId(workspacePath) {
3363
- if (!workspacePath || !existsSync9(CURSOR_SDK_STORE)) return null;
3514
+ if (!workspacePath || !existsSync10(CURSOR_SDK_STORE)) return null;
3364
3515
  const normalized = workspacePath.replace(/\/$/, "");
3365
3516
  let best = null;
3366
3517
  let hashes;
@@ -3370,11 +3521,11 @@ function resolveConductorCursorAgentId(workspacePath) {
3370
3521
  return null;
3371
3522
  }
3372
3523
  for (const hash of hashes) {
3373
- const agentsFile = join9(CURSOR_SDK_STORE, hash, "agents.ndjson");
3374
- if (!existsSync9(agentsFile)) continue;
3524
+ const agentsFile = join10(CURSOR_SDK_STORE, hash, "agents.ndjson");
3525
+ if (!existsSync10(agentsFile)) continue;
3375
3526
  let text;
3376
3527
  try {
3377
- text = readFileSync7(agentsFile, "utf8");
3528
+ text = readFileSync8(agentsFile, "utf8");
3378
3529
  } catch {
3379
3530
  continue;
3380
3531
  }
@@ -3398,7 +3549,7 @@ function resolveConductorCursorAgentId(workspacePath) {
3398
3549
  return best?.agentId ?? null;
3399
3550
  }
3400
3551
  async function adoptThread(input) {
3401
- if (!existsSync9(input.worktreePath)) {
3552
+ if (!existsSync10(input.worktreePath)) {
3402
3553
  throw new Error(`Worktree not found: ${input.worktreePath}`);
3403
3554
  }
3404
3555
  const repoPath = await resolveRepoRoot(input.worktreePath);
@@ -3417,23 +3568,23 @@ async function adoptThread(input) {
3417
3568
  messages: input.messages ?? []
3418
3569
  });
3419
3570
  writeThread(thread);
3420
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-VICQYI2N.js");
3571
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-MQYNZXUK.js");
3421
3572
  await ensureWorkspace2(repoPath);
3422
3573
  const { ensureWorktreeSideboardIgnored: ensureWorktreeSideboardIgnored2 } = await import("./worktree-exclude-N5X5I422.js");
3423
3574
  await ensureWorktreeSideboardIgnored2(input.worktreePath);
3424
3575
  return thread;
3425
3576
  }
3426
3577
  function listConductorWorkspaces() {
3427
- if (!existsSync9(CONDUCTOR_DB)) {
3578
+ if (!existsSync10(CONDUCTOR_DB)) {
3428
3579
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
3429
3580
  }
3430
- const tmp = mkdtempSync(join9(tmpdir(), "sideboard-conductor-"));
3431
- const snapshot = join9(tmp, "conductor.db");
3581
+ const tmp = mkdtempSync(join10(tmpdir(), "sideboard-conductor-"));
3582
+ const snapshot = join10(tmp, "conductor.db");
3432
3583
  try {
3433
3584
  copyFileSync2(CONDUCTOR_DB, snapshot);
3434
3585
  for (const suffix of ["-wal", "-shm"]) {
3435
3586
  const src = `${CONDUCTOR_DB}${suffix}`;
3436
- if (existsSync9(src)) {
3587
+ if (existsSync10(src)) {
3437
3588
  try {
3438
3589
  copyFileSync2(src, `${snapshot}${suffix}`);
3439
3590
  } catch {
@@ -3515,16 +3666,16 @@ function listConductorWorkspaces() {
3515
3666
  }
3516
3667
  }
3517
3668
  function importConductorWorkspace(workspaceId) {
3518
- if (!existsSync9(CONDUCTOR_DB)) {
3669
+ if (!existsSync10(CONDUCTOR_DB)) {
3519
3670
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
3520
3671
  }
3521
- const tmp = mkdtempSync(join9(tmpdir(), "sideboard-conductor-"));
3522
- const snapshot = join9(tmp, "conductor.db");
3672
+ const tmp = mkdtempSync(join10(tmpdir(), "sideboard-conductor-"));
3673
+ const snapshot = join10(tmp, "conductor.db");
3523
3674
  try {
3524
3675
  copyFileSync2(CONDUCTOR_DB, snapshot);
3525
3676
  for (const suffix of ["-wal", "-shm"]) {
3526
3677
  const src = `${CONDUCTOR_DB}${suffix}`;
3527
- if (existsSync9(src)) {
3678
+ if (existsSync10(src)) {
3528
3679
  try {
3529
3680
  copyFileSync2(src, `${snapshot}${suffix}`);
3530
3681
  } catch {
@@ -3544,7 +3695,7 @@ function importConductorWorkspace(workspaceId) {
3544
3695
  ).get(workspaceId);
3545
3696
  if (!row) throw new Error(`Conductor workspace not found: ${workspaceId}`);
3546
3697
  const worktreePath = String(row.workspacePath);
3547
- if (!existsSync9(worktreePath)) {
3698
+ if (!existsSync10(worktreePath)) {
3548
3699
  throw new Error(`Conductor worktree missing on disk: ${worktreePath}`);
3549
3700
  }
3550
3701
  let sessionId = null;
@@ -3615,7 +3766,7 @@ async function importConductorWorkspaceAsync(workspaceId) {
3615
3766
  }
3616
3767
 
3617
3768
  // src/threads/stack-layers.ts
3618
- import { existsSync as existsSync10 } from "fs";
3769
+ import { existsSync as existsSync11 } from "fs";
3619
3770
  function stackIdFrom(stack) {
3620
3771
  if (stack.stackNumber != null) return `gh-stack-${stack.stackNumber}`;
3621
3772
  const key = stack.layers.map((l) => l.branchName).join("|");
@@ -3678,7 +3829,7 @@ async function openStackLayer(input, _onSetupLine) {
3678
3829
  let createdWorktree = false;
3679
3830
  const trees = await listWorktrees(repoPath);
3680
3831
  const checkedOut = trees.find((w) => w.branch === branchName);
3681
- if (checkedOut?.path && existsSync10(checkedOut.path)) {
3832
+ if (checkedOut?.path && existsSync11(checkedOut.path)) {
3682
3833
  if (input.reuseExistingWorktree !== false) {
3683
3834
  worktreePath = checkedOut.path;
3684
3835
  } else {
@@ -3820,7 +3971,7 @@ async function initStackFromThread(input, onSetupLine) {
3820
3971
  async function createPrStack(input, onSetupLine) {
3821
3972
  await requireAgent(input.agent);
3822
3973
  const repoPath = await resolveRepoRoot(input.repoPath);
3823
- if (!existsSync10(repoPath)) throw new Error(`Repo not found: ${repoPath}`);
3974
+ if (!existsSync11(repoPath)) throw new Error(`Repo not found: ${repoPath}`);
3824
3975
  if (!input.branches.length) throw new Error("At least one branch name required");
3825
3976
  const status = await detectGhStack(repoPath);
3826
3977
  if (!status.available) throw new Error(status.reason);
@@ -3887,7 +4038,7 @@ async function createPrStack(input, onSetupLine) {
3887
4038
  }
3888
4039
  }
3889
4040
  const claimed = new Set(threads.map((t) => t.worktreePath));
3890
- if (!claimed.has(bootstrap.worktreePath) && existsSync10(bootstrap.worktreePath)) {
4041
+ if (!claimed.has(bootstrap.worktreePath) && existsSync11(bootstrap.worktreePath)) {
3891
4042
  try {
3892
4043
  await removeWorktree(repoPath, bootstrap.worktreePath, {
3893
4044
  deleteBranch: bootstrap.branchName
@@ -3899,10 +4050,10 @@ async function createPrStack(input, onSetupLine) {
3899
4050
  }
3900
4051
 
3901
4052
  // src/diff/diff.ts
3902
- import { existsSync as existsSync11, mkdirSync as mkdirSync3, readFileSync as readFileSync8, statSync as statSync2, writeFileSync as writeFileSync4 } from "fs";
3903
- import { dirname as dirname3, join as join10 } from "path";
4053
+ import { existsSync as existsSync12, mkdirSync as mkdirSync3, readFileSync as readFileSync9, statSync as statSync2, writeFileSync as writeFileSync5 } from "fs";
4054
+ import { dirname as dirname3, join as join11 } from "path";
3904
4055
  async function inspectGitWorktree(worktreePath) {
3905
- if (!worktreePath || !existsSync11(worktreePath)) return "missing_worktree";
4056
+ if (!worktreePath || !existsSync12(worktreePath)) return "missing_worktree";
3906
4057
  const check = await git(["rev-parse", "--is-inside-work-tree"], worktreePath, {
3907
4058
  reject: false
3908
4059
  });
@@ -3910,7 +4061,7 @@ async function inspectGitWorktree(worktreePath) {
3910
4061
  return "ok";
3911
4062
  }
3912
4063
  async function initializeGitRepository(worktreePath) {
3913
- if (!worktreePath || !existsSync11(worktreePath)) {
4064
+ if (!worktreePath || !existsSync12(worktreePath)) {
3914
4065
  throw new Error("Worktree not found");
3915
4066
  }
3916
4067
  const status = await inspectGitWorktree(worktreePath);
@@ -4046,11 +4197,11 @@ new file mode 100644
4046
4197
  };
4047
4198
  }
4048
4199
  async function untrackedPatch(worktreePath, path, maxHunk) {
4049
- const abs = join10(worktreePath, path);
4200
+ const abs = join11(worktreePath, path);
4050
4201
  try {
4051
4202
  const st = statSync2(abs);
4052
4203
  if (st.isFile() && st.size > maxHunk) {
4053
- const buf = readFileSync8(abs).subarray(0, maxHunk);
4204
+ const buf = readFileSync9(abs).subarray(0, maxHunk);
4054
4205
  return syntheticAddPatch(path, buf.toString("utf8"), maxHunk);
4055
4206
  }
4056
4207
  } catch {
@@ -4551,7 +4702,7 @@ var DEFAULT_UPLOAD_MAX_BYTES = 5e7;
4551
4702
  function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
4552
4703
  assertSafeRelativePath(relativePath);
4553
4704
  const maxBytes = opts?.maxBytes ?? DEFAULT_UPLOAD_MAX_BYTES;
4554
- const abs = join10(worktreePath, relativePath);
4705
+ const abs = join11(worktreePath, relativePath);
4555
4706
  const st = statSync2(abs);
4556
4707
  if (!st.isFile()) {
4557
4708
  throw new Error(`Not a file: ${relativePath}`);
@@ -4561,7 +4712,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
4561
4712
  `File too large to upload (${st.size} bytes; max ${maxBytes})`
4562
4713
  );
4563
4714
  }
4564
- const buf = readFileSync8(abs);
4715
+ const buf = readFileSync9(abs);
4565
4716
  return {
4566
4717
  path: relativePath,
4567
4718
  contentBase64: buf.toString("base64"),
@@ -4571,12 +4722,12 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
4571
4722
  function readWorktreeFile(worktreePath, relativePath, opts) {
4572
4723
  assertSafeRelativePath(relativePath);
4573
4724
  const maxBytes = opts?.maxBytes ?? 2e5;
4574
- const abs = join10(worktreePath, relativePath);
4725
+ const abs = join11(worktreePath, relativePath);
4575
4726
  const st = statSync2(abs);
4576
4727
  if (!st.isFile()) {
4577
4728
  throw new Error(`Not a file: ${relativePath}`);
4578
4729
  }
4579
- const buf = readFileSync8(abs);
4730
+ const buf = readFileSync9(abs);
4580
4731
  if (isImageRelativePath(relativePath)) {
4581
4732
  const maxImageBytes = Math.max(maxBytes, 15e6);
4582
4733
  const truncated2 = buf.length > maxImageBytes;
@@ -4619,9 +4770,9 @@ function assertSafeRelativePath(relativePath) {
4619
4770
  }
4620
4771
  function writeWorktreeFile(worktreePath, relativePath, content) {
4621
4772
  assertSafeRelativePath(relativePath);
4622
- const abs = join10(worktreePath, relativePath);
4773
+ const abs = join11(worktreePath, relativePath);
4623
4774
  mkdirSync3(dirname3(abs), { recursive: true });
4624
- writeFileSync4(abs, content, "utf8");
4775
+ writeFileSync5(abs, content, "utf8");
4625
4776
  return { path: relativePath };
4626
4777
  }
4627
4778
  async function getDiffSummary(worktreePath, repoPath, opts) {
@@ -4746,9 +4897,9 @@ async function confirmLand(thread, opts) {
4746
4897
  }
4747
4898
 
4748
4899
  // src/skills/discover.ts
4749
- import { existsSync as existsSync12, readdirSync as readdirSync4, readFileSync as readFileSync9, statSync as statSync3 } from "fs";
4900
+ import { existsSync as existsSync13, readdirSync as readdirSync4, readFileSync as readFileSync10, statSync as statSync3 } from "fs";
4750
4901
  import { homedir } from "os";
4751
- import { join as join11 } from "path";
4902
+ import { join as join12 } from "path";
4752
4903
 
4753
4904
  // src/skills/bundled/long-running.ts
4754
4905
  var LONG_RUNNING_SKILL_COMMAND = "long-running";
@@ -4816,6 +4967,16 @@ node <detached-job.js> wait --pid-file FILE --log-file FILE [--ok-pattern TEXT]
4816
4967
  4. On \`ok\`, present once more (\`status=ok\`, last \`delta\`) and finish the task. On \`failed\`, fix from the log.
4817
4968
 
4818
4969
  Never tell the user \u201Csay status when it\u2019s done.\u201D You wait.
4970
+
4971
+ ## PR checks (only if a goal is given)
4972
+
4973
+ Do **not** watch after every push. If the user gave a goal (Greptile 5/5, CI green, until checks pass), wait the same way:
4974
+
4975
+ \`\`\`bash
4976
+ node <detached-job.js> start pr-checks -- gh pr checks --watch
4977
+ \`\`\`
4978
+
4979
+ On miss, fix, commit, push, and watch again until the goal is met or you are blocked. Do not ask the human to poll. Do not merge unless asked.
4819
4980
  `;
4820
4981
 
4821
4982
  // src/skills/discover.ts
@@ -4867,7 +5028,7 @@ function parseFrontmatter(content) {
4867
5028
  }
4868
5029
  function readSkill(skillMd, source) {
4869
5030
  try {
4870
- const content = readFileSync9(skillMd, "utf8");
5031
+ const content = readFileSync10(skillMd, "utf8");
4871
5032
  const { name: fmName, description } = parseFrontmatter(content);
4872
5033
  const dirName = skillMd.split("/").slice(-2, -1)[0] || "skill";
4873
5034
  const name = fmName || dirName;
@@ -4886,7 +5047,7 @@ function readSkill(skillMd, source) {
4886
5047
  }
4887
5048
  }
4888
5049
  function scanSkillsDir(dir, source, out) {
4889
- if (!existsSync12(dir)) return;
5050
+ if (!existsSync13(dir)) return;
4890
5051
  let entries;
4891
5052
  try {
4892
5053
  entries = readdirSync4(dir);
@@ -4895,8 +5056,8 @@ function scanSkillsDir(dir, source, out) {
4895
5056
  }
4896
5057
  for (const entry of entries) {
4897
5058
  if (entry.startsWith(".")) continue;
4898
- const skillMd = join11(dir, entry, "SKILL.md");
4899
- if (!existsSync12(skillMd)) continue;
5059
+ const skillMd = join12(dir, entry, "SKILL.md");
5060
+ if (!existsSync13(skillMd)) continue;
4900
5061
  try {
4901
5062
  if (!statSync3(skillMd).isFile()) continue;
4902
5063
  } catch {
@@ -4907,7 +5068,7 @@ function scanSkillsDir(dir, source, out) {
4907
5068
  }
4908
5069
  }
4909
5070
  function scanClaudePluginSkills(pluginsRoot, out) {
4910
- if (!existsSync12(pluginsRoot)) return;
5071
+ if (!existsSync13(pluginsRoot)) return;
4911
5072
  const walk = (dir, depth, lookingForSkillsDir) => {
4912
5073
  if (depth > 7) return;
4913
5074
  let entries;
@@ -4917,12 +5078,12 @@ function scanClaudePluginSkills(pluginsRoot, out) {
4917
5078
  return;
4918
5079
  }
4919
5080
  if (lookingForSkillsDir && entries.includes("SKILL.md")) {
4920
- const skill = readSkill(join11(dir, "SKILL.md"), "cli");
5081
+ const skill = readSkill(join12(dir, "SKILL.md"), "cli");
4921
5082
  if (skill) out.push(skill);
4922
5083
  }
4923
5084
  for (const entry of entries) {
4924
5085
  if (entry === "node_modules" || entry === ".git") continue;
4925
- const full = join11(dir, entry);
5086
+ const full = join12(dir, entry);
4926
5087
  try {
4927
5088
  if (!statSync3(full).isDirectory()) continue;
4928
5089
  } catch {
@@ -4942,17 +5103,17 @@ function discoverSkills(worktreePath) {
4942
5103
  const home = homedir();
4943
5104
  const collected = [];
4944
5105
  for (const rel of [".claude/skills", ".cursor/skills", ".sideboard/skills", ".brightsy/skills", "skills"]) {
4945
- scanSkillsDir(join11(worktreePath, rel), "workspace", collected);
5106
+ scanSkillsDir(join12(worktreePath, rel), "workspace", collected);
4946
5107
  }
4947
5108
  for (const abs of [
4948
- join11(home, ".claude/skills"),
4949
- join11(home, ".cursor/skills"),
4950
- join11(home, ".sideboard/skills"),
4951
- join11(home, ".brightsy/skills")
5109
+ join12(home, ".claude/skills"),
5110
+ join12(home, ".cursor/skills"),
5111
+ join12(home, ".sideboard/skills"),
5112
+ join12(home, ".brightsy/skills")
4952
5113
  ]) {
4953
5114
  scanSkillsDir(abs, "user", collected);
4954
5115
  }
4955
- scanClaudePluginSkills(join11(home, ".claude/plugins"), collected);
5116
+ scanClaudePluginSkills(join12(home, ".claude/plugins"), collected);
4956
5117
  collected.push(...bundledSkills());
4957
5118
  const rank = {
4958
5119
  workspace: 0,
@@ -4978,7 +5139,7 @@ function readSkillBody(skillPath, maxChars = 12e3) {
4978
5139
  \u2026(truncated)` : body;
4979
5140
  }
4980
5141
  }
4981
- const raw = readFileSync9(skillPath, "utf8");
5142
+ const raw = readFileSync10(skillPath, "utf8");
4982
5143
  if (raw.startsWith("---")) {
4983
5144
  const end = raw.indexOf("\n---", 3);
4984
5145
  if (end >= 0) {
@@ -5071,26 +5232,26 @@ function expandComposerPrompt(worktreePath, prompt, opts) {
5071
5232
  }
5072
5233
 
5073
5234
  // src/agents/instructions.ts
5074
- import { existsSync as existsSync14, readFileSync as readFileSync10, statSync as statSync4 } from "fs";
5075
- import { join as join13 } from "path";
5235
+ import { existsSync as existsSync15, readFileSync as readFileSync11, statSync as statSync4 } from "fs";
5236
+ import { join as join14 } from "path";
5076
5237
 
5077
5238
  // src/skills/detached-job-path.ts
5078
- import { existsSync as existsSync13 } from "fs";
5079
- import { dirname as dirname4, join as join12 } from "path";
5239
+ import { existsSync as existsSync14 } from "fs";
5240
+ import { dirname as dirname4, join as join13 } from "path";
5080
5241
  import { fileURLToPath } from "url";
5081
5242
  function packagedDetachedJobPath() {
5082
5243
  const dir = packagedMcpDir();
5083
5244
  if (!dir) return null;
5084
- const script = join12(dir, "scripts", "detached-job.js");
5085
- return existsSync13(script) ? script : null;
5245
+ const script = join13(dir, "scripts", "detached-job.js");
5246
+ return existsSync14(script) ? script : null;
5086
5247
  }
5087
5248
  function resolveDetachedJobScript() {
5088
5249
  const packaged = packagedDetachedJobPath();
5089
5250
  if (packaged) return packaged;
5090
5251
  let dir = dirname4(fileURLToPath(import.meta.url));
5091
5252
  for (let i = 0; i < 8; i++) {
5092
- const candidate = join12(dir, "scripts", "detached-job.js");
5093
- if (existsSync13(candidate)) return candidate;
5253
+ const candidate = join13(dir, "scripts", "detached-job.js");
5254
+ if (existsSync14(candidate)) return candidate;
5094
5255
  const parent = dirname4(dir);
5095
5256
  if (parent === dir) break;
5096
5257
  dir = parent;
@@ -5181,7 +5342,7 @@ function formatWorktreeDirective(thread, opts) {
5181
5342
  "Short git requests from the Sideboard UI are complete instructions \u2014 expand them using the rules above without asking for clarification:"
5182
5343
  );
5183
5344
  lines.push(
5184
- '- "Commit and push." \u2192 commit any uncommitted work with a purpose-stating message, then push to origin (updates an existing PR if one is linked).'
5345
+ '- "Commit and push." \u2192 commit any uncommitted work with a purpose-stating message, then push to origin (updates an existing PR if one is linked). Do not start a checks loop unless they gave a goal.'
5185
5346
  );
5186
5347
  lines.push(
5187
5348
  '- "Commit, push, and open a draft PR." \u2192 commit, push, then create a draft PR with `gh pr create --draft -R \u2026` (title/body from the change purpose).'
@@ -5190,7 +5351,7 @@ function formatWorktreeDirective(thread, opts) {
5190
5351
  '- "Commit, push, and open a PR in the browser." \u2192 commit, push, then `gh pr create --web -R \u2026`.'
5191
5352
  );
5192
5353
  lines.push(
5193
- '- "Fix CI: <name>." \u2192 investigate that failing check, fix it, commit, and push.'
5354
+ '- "Fix CI: <name>." \u2192 investigate that failing check, fix it, commit, and push. Loop only if they gave a goal to keep going until it passes.'
5194
5355
  );
5195
5356
  lines.push(
5196
5357
  '- "Merge the remote branch (<base>) into your branch and resolve conflicts. Then, commit and push your changes." \u2192 fetch the PR base, merge it into this branch, resolve conflicts carefully, commit, and push until the PR is mergeable.'
@@ -5202,11 +5363,21 @@ function formatWorktreeDirective(thread, opts) {
5202
5363
  '- "Merge PR." \u2192 merge this thread\'s open pull request on GitHub (this phrase is the explicit ask). If `gh stack view` shows a stack, use `gh stack merge`; otherwise `gh pr merge` (respect repo defaults / squash vs merge). Do not force-push main/master or merge locally into the main checkout.'
5203
5364
  );
5204
5365
  lines.push("");
5366
+ lines.push(formatPrGateDirective());
5367
+ lines.push("");
5205
5368
  lines.push(formatProcessGuideDirective());
5206
5369
  return lines.join("\n");
5207
5370
  }
5208
5371
  function formatWorktreeReminder() {
5209
- return "Sideboard worktree: stay in this cwd for all file and git work. Push and open PRs against origin, never upstream. Do not edit the main repo checkout.";
5372
+ return "Sideboard worktree: stay in this cwd for all file and git work. Push and open PRs against origin, never upstream. Do not edit the main repo checkout. If a goal is given (Greptile 5/5, CI green), watch-fix-push until it lands \u2014 do not watch after every push.";
5373
+ }
5374
+ function formatPrGateDirective() {
5375
+ return [
5376
+ "If a goal is given (not after every push):",
5377
+ "- Enter a watch-fix-push loop and stay in it until that goal is met or you are blocked (examples: \u201CGreptile 5/5\u201D, \u201CCI green\u201D, \u201Cuntil checks pass\u201D). Do not stop after one red run or one review cycle.",
5378
+ "- Wait with `/long-running`: `gh pr checks --watch` (add `-R <origin>` when needed). Present the log. Do not ask the human to poll.",
5379
+ "- Greptile 5/5: wait for the greptile check; read the latest `N/5` from the PR body or Greptile\u2019s most recently *updated* issue comment; also fetch unresolved review comments / the \u201CPrompt to fix all with AI\u201D block. Done = **5/5 and zero unresolved Greptile comments**. If Greptile is idle, comment `@greptile review`. Cap about 5 iterations, then report leftovers. Do not merge unless asked."
5380
+ ].join("\n");
5210
5381
  }
5211
5382
  function formatProcessGuideDirective() {
5212
5383
  return [
@@ -5214,7 +5385,7 @@ function formatProcessGuideDirective() {
5214
5385
  "- Long jobs (pack, test, deploy, anything that may run more than ~30s): Sideboard always provides `/long-running`. Detach and wait \u2014 do not ask the human to poll.",
5215
5386
  "- If `.claude/skills/graph-engineering/SKILL.md` exists, follow it (`/graph-engineering`) for migrations, ports, batch fixes, and other fan-out. Judge first; state on disk; grow the rulebook; do not patch around it.",
5216
5387
  "- If this same shape of work will happen again, write `.claude/skills/<kebab-name>/SKILL.md` in this worktree (Claude Code project skill). Sideboard `/name`, Claude Code, and `attach` all load that path. Do not leave the method only in chat.",
5217
- "- Merge-readiness notes: create or edit `.claude/skills/review/SKILL.md` and commit it. That file is allowed. Do not use `.sideboard/review.md` for new notes (legacy; setup copies it into the skill).",
5388
+ "- Merge-readiness notes: if `.claude/skills/review/SKILL.md` already exists, edit it. Otherwise write them to `.context/review.md` (copied from `.sideboard/review.md` when that file exists). Do not create a review skill.",
5218
5389
  "- Do not write new skills under `.sideboard/skills/` (that folder only \u2014 other `.sideboard/` files such as settings.toml are fine). Point Codex/OpenCode at the Claude skill from `AGENTS.md`. Optional: symlink `.cursor/skills/<name>` to the Claude skill.",
5219
5390
  "- Skip a guide for a one-off. If a matching skill exists, follow it. Same miss twice \u2192 edit the skill, do not patch around it."
5220
5391
  ].join("\n");
@@ -5382,8 +5553,8 @@ async function syncThreadBranchFromGit(threadId) {
5382
5553
 
5383
5554
  // src/store/schedules.ts
5384
5555
  import { randomUUID as randomUUID4 } from "crypto";
5385
- import { existsSync as existsSync15, readFileSync as readFileSync11 } from "fs";
5386
- import { join as join14 } from "path";
5556
+ import { existsSync as existsSync16, readFileSync as readFileSync12 } from "fs";
5557
+ import { join as join15 } from "path";
5387
5558
  import { Cron } from "croner";
5388
5559
  var DURATION_RE = /^(\d+)(s|m|h|d)$/i;
5389
5560
  var UNIT_MS = {
@@ -5393,7 +5564,7 @@ var UNIT_MS = {
5393
5564
  d: 864e5
5394
5565
  };
5395
5566
  function schedulesPath() {
5396
- return join14(appDataDir(), "schedules.json");
5567
+ return join15(appDataDir(), "schedules.json");
5397
5568
  }
5398
5569
  function parseDurationMs(every) {
5399
5570
  const m = every.trim().match(DURATION_RE);
@@ -5493,9 +5664,9 @@ function normalizeTask(raw) {
5493
5664
  }
5494
5665
  function readAll() {
5495
5666
  const path = schedulesPath();
5496
- if (!existsSync15(path)) return [];
5667
+ if (!existsSync16(path)) return [];
5497
5668
  try {
5498
- const parsed = JSON.parse(readFileSync11(path, "utf8"));
5669
+ const parsed = JSON.parse(readFileSync12(path, "utf8"));
5499
5670
  if (!Array.isArray(parsed)) return [];
5500
5671
  return parsed.map((row) => {
5501
5672
  try {
@@ -5618,7 +5789,7 @@ function formatScheduledPrompt(name, prompt) {
5618
5789
  ${prompt}`;
5619
5790
  }
5620
5791
  async function defaultDeps() {
5621
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-Y5MWUU7C.js");
5792
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-ZSSU3FKW.js");
5622
5793
  const orch = getOrchestrator2();
5623
5794
  return {
5624
5795
  findThread: (id) => findThreadByRef(id),
@@ -5858,7 +6029,7 @@ var Orchestrator = class {
5858
6029
  }
5859
6030
  continue;
5860
6031
  }
5861
- if (!existsSync16(thread.worktreePath)) {
6032
+ if (!existsSync17(thread.worktreePath)) {
5862
6033
  setStatus(thread.id, "broken", "Worktree missing on disk");
5863
6034
  this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
5864
6035
  continue;
@@ -5877,7 +6048,7 @@ var Orchestrator = class {
5877
6048
  orphans: orphans.map((o) => ({ path: o.path, repoPath: o.repoPath }))
5878
6049
  });
5879
6050
  }
5880
- const { autoCleanupOrphansEnabled } = await import("./app-settings-J3JBCUOT.js");
6051
+ const { autoCleanupOrphansEnabled } = await import("./app-settings-VRQI3FIE.js");
5881
6052
  if (autoCleanupOrphansEnabled() && shouldRunWorktreeCleanup() && orphans.length > 0) {
5882
6053
  await cleanupOrphanWorktrees({ repoPaths });
5883
6054
  }
@@ -6117,7 +6288,7 @@ var Orchestrator = class {
6117
6288
  }
6118
6289
  await setup;
6119
6290
  if (skipSetup) return;
6120
- const { autoRunAfterSetupEnabled } = await import("./app-settings-J3JBCUOT.js");
6291
+ const { autoRunAfterSetupEnabled } = await import("./app-settings-VRQI3FIE.js");
6121
6292
  if (autoRunAfterSetupEnabled()) {
6122
6293
  try {
6123
6294
  await this.startDev(threadId);
@@ -6425,7 +6596,7 @@ var Orchestrator = class {
6425
6596
  }
6426
6597
  const isBrightsy = fresh.agent === "brightsy";
6427
6598
  const isOrchestration = isOrchestratorThread(fresh);
6428
- const { autoRenameBranchEnabled, getGithubGitAuthMode } = await import("./app-settings-J3JBCUOT.js");
6599
+ const { autoRenameBranchEnabled, getGithubGitAuthMode } = await import("./app-settings-VRQI3FIE.js");
6429
6600
  const gitAuthMode = getGithubGitAuthMode();
6430
6601
  const worktreeDirective = isBrightsy || isOrchestration ? null : formatWorktreeDirective(fresh, {
6431
6602
  githubSlug: await resolveGithubRepoSlug(fresh.worktreePath).catch(
@@ -6531,7 +6702,7 @@ var Orchestrator = class {
6531
6702
  if (!this.stoppedTurns.has(threadId) && this.requireThread(threadId).agent === "cursor" && exitCode !== 0 && !assistantText && parts.length === 0) {
6532
6703
  const sessionId = result.sessionId || this.requireThread(threadId).sessionId || "";
6533
6704
  if (sessionId) {
6534
- const { recoverFinishedCursorRun } = await import("./cursor-recover-NOQYOFRF.js");
6705
+ const { recoverFinishedCursorRun } = await import("./cursor-recover-L5MD6MBO.js");
6535
6706
  for (let i = 0; i < 8; i++) {
6536
6707
  const recovered = recoverFinishedCursorRun({
6537
6708
  agentId: sessionId,
@@ -6921,6 +7092,15 @@ var Orchestrator = class {
6921
7092
  getActiveRuns(threadRef) {
6922
7093
  return this.requireThread(threadRef).activeRuns ?? [];
6923
7094
  }
7095
+ getSetupLog(threadRef) {
7096
+ const thread = this.getThread(threadRef);
7097
+ if (!thread) return readSetupLog(threadRef);
7098
+ const snap = readSetupLog(thread.id);
7099
+ if (snap.running && !this.processes.has(`${thread.id}:setup`)) {
7100
+ return { ...snap, running: false };
7101
+ }
7102
+ return snap;
7103
+ }
6924
7104
  async runSetup(threadRef) {
6925
7105
  const thread = this.requireThread(threadRef);
6926
7106
  this.assertNotGlobal(thread, "Setup");
@@ -6934,20 +7114,31 @@ var Orchestrator = class {
6934
7114
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
6935
7115
  kill: () => abort.abort()
6936
7116
  });
7117
+ beginSetupLog(thread.id);
6937
7118
  this.emit({ type: "setup_started", threadId: thread.id });
7119
+ let finished = false;
7120
+ const finish = (exitCode, source) => {
7121
+ if (finished) return;
7122
+ finished = true;
7123
+ finishSetupLog(thread.id, exitCode, source);
7124
+ this.emit({ type: "setup_finished", threadId: thread.id, exitCode });
7125
+ };
6938
7126
  try {
6939
7127
  const setup = await runWorkspaceSetup(
6940
7128
  thread.repoPath,
6941
7129
  thread.worktreePath,
6942
7130
  (line) => {
7131
+ appendSetupLog(thread.id, line);
6943
7132
  this.emit({ type: "setup_output", threadId: thread.id, line });
6944
7133
  },
6945
7134
  { signal: abort.signal }
6946
7135
  );
6947
7136
  if (!setup.ran) {
6948
- throw new Error(
6949
- "No setup script in .sideboard/settings.toml, .conductor/settings.toml, .cursor/worktrees.json, or script/setup (bin/setup, scripts/setup)"
6950
- );
7137
+ const line = "No setup script in .sideboard/settings.toml, .conductor/settings.toml, .cursor/worktrees.json, or script/setup (bin/setup, scripts/setup)";
7138
+ appendSetupLog(thread.id, line);
7139
+ this.emit({ type: "setup_output", threadId: thread.id, line });
7140
+ finish(null, null);
7141
+ throw new Error(line);
6951
7142
  }
6952
7143
  if (setup.exitCode !== 0 && setup.exitCode !== null) {
6953
7144
  const live = readThread(thread.id);
@@ -6960,8 +7151,11 @@ var Orchestrator = class {
6960
7151
  });
6961
7152
  }
6962
7153
  }
6963
- this.emit({ type: "setup_finished", threadId: thread.id, exitCode: setup.exitCode });
7154
+ finish(setup.exitCode, setup.source);
6964
7155
  return { exitCode: setup.exitCode, source: setup.source };
7156
+ } catch (err) {
7157
+ finish(null, null);
7158
+ throw err;
6965
7159
  } finally {
6966
7160
  this.processes.delete(key);
6967
7161
  }
@@ -7288,7 +7482,7 @@ var Orchestrator = class {
7288
7482
  updateThread(thread.id, { title: meta.title });
7289
7483
  }
7290
7484
  }
7291
- const { autoArchiveOnMergeEnabled } = await import("./app-settings-J3JBCUOT.js");
7485
+ const { autoArchiveOnMergeEnabled } = await import("./app-settings-VRQI3FIE.js");
7292
7486
  const latest = this.requireThread(thread.id);
7293
7487
  if (!shouldAutoArchiveOnPrMerge({
7294
7488
  previousPrState: prevState || null,
@@ -7605,7 +7799,7 @@ var Orchestrator = class {
7605
7799
  this.emit({ type: "status_changed", threadId: archived.id, status: "archived" });
7606
7800
  if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
7607
7801
  try {
7608
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-VICQYI2N.js");
7802
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-MQYNZXUK.js");
7609
7803
  await ensureWorkspace2(thread.repoPath);
7610
7804
  } catch {
7611
7805
  }
@@ -7632,7 +7826,7 @@ var Orchestrator = class {
7632
7826
  await runArchiveScript(thread.repoPath, thread.worktreePath);
7633
7827
  } catch {
7634
7828
  }
7635
- const { deleteBranchOnPurgeEnabled } = await import("./app-settings-J3JBCUOT.js");
7829
+ const { deleteBranchOnPurgeEnabled } = await import("./app-settings-VRQI3FIE.js");
7636
7830
  const deleteBranch = opts?.deleteBranch ?? deleteBranchOnPurgeEnabled();
7637
7831
  await removeWorktree(thread.repoPath, thread.worktreePath, {
7638
7832
  deleteBranch: deleteBranch ? thread.branchName : void 0
@@ -7647,19 +7841,19 @@ var Orchestrator = class {
7647
7841
  throw new Error("Thread is not archived");
7648
7842
  }
7649
7843
  if (isGlobalThread(thread)) {
7650
- const { globalAgentCwd } = await import("./paths-QCI3MTO5.js");
7844
+ const { globalAgentCwd } = await import("./paths-RRCLNSJB.js");
7651
7845
  updateThread(thread.id, { worktreePath: globalAgentCwd() });
7652
7846
  const restored2 = setStatus(thread.id, "idle");
7653
7847
  this.emit({ type: "status_changed", threadId: restored2.id, status: restored2.status });
7654
7848
  return restored2;
7655
7849
  }
7656
- if (!existsSync16(thread.worktreePath)) {
7850
+ if (!existsSync17(thread.worktreePath)) {
7657
7851
  if (isCowboyThread(thread) || isPrimaryCheckoutThread(thread)) {
7658
7852
  throw new Error(
7659
7853
  `Cowboy checkout missing: ${thread.worktreePath}. Re-add the project folder, then restore.`
7660
7854
  );
7661
7855
  }
7662
- const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-HTUM35NT.js");
7856
+ const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-PEWDAX5O.js");
7663
7857
  const slug = thread.worktreePath.split("/").pop();
7664
7858
  const dest = thread.worktreePath;
7665
7859
  await withRepoGitLock(thread.repoPath, async () => {
@@ -7769,7 +7963,7 @@ async function startOrchestration(opts) {
7769
7963
  sourceRef: "default",
7770
7964
  ...createOpts
7771
7965
  }).catch(async () => {
7772
- const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-HTUM35NT.js");
7966
+ const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-PEWDAX5O.js");
7773
7967
  const repo = await resolveRepoRoot2(repoPath);
7774
7968
  const def = await resolveDefaultBranch2(repo);
7775
7969
  return orch.createThread({
@@ -7779,7 +7973,7 @@ async function startOrchestration(opts) {
7779
7973
  repoPath: repo
7780
7974
  });
7781
7975
  });
7782
- const { updateThread: upd } = await import("./thread-store-57ZLHR3A.js");
7976
+ const { updateThread: upd } = await import("./thread-store-MVQU6HPJ.js");
7783
7977
  const updated = upd(thread.id, {
7784
7978
  sourceType: "orchestration",
7785
7979
  sourceRef: goal
@@ -7812,7 +8006,6 @@ export {
7812
8006
  findBoardPr,
7813
8007
  assembleHomeBoard,
7814
8008
  HOME_BOARD_AGENT_HINT,
7815
- listLinearIssues,
7816
8009
  readTurnLive,
7817
8010
  resolveScheduleThreadId,
7818
8011
  listSchedules,