@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
@@ -24,18 +24,19 @@ import {
24
24
  stripBrightsyNdjsonNoise,
25
25
  sumUsageList,
26
26
  toolDescription
27
- } from "./chunk-UIS7BVCX.js";
27
+ } from "./chunk-QZEZXYVV.js";
28
28
  import {
29
+ SLACK_REPLY_FORMATTING,
29
30
  addWorkspace,
30
- ensureWorkspace,
31
- removeWorkspace,
32
- syncWorkspacesFromThreads
33
- } from "./chunk-HWZWBJWL.js";
34
- import {
35
31
  assertOrchestratorCapableAgent,
36
32
  attachmentsFromWorktreePaths,
37
33
  coerceOrchestratorAgent,
34
+ coordinatorSystemPrompt,
35
+ coordinatorTurnReminder,
38
36
  createGlobalChat,
37
+ enrichWorkspacesWithGithub,
38
+ ensureGlobalCoordinatorCwd,
39
+ ensureWorkspace,
39
40
  healOrchestrationSoccerTitles,
40
41
  isCloudCoordinatorThread,
41
42
  isGlobalRepoPath,
@@ -45,16 +46,11 @@ import {
45
46
  isSlackCoordinatorThread,
46
47
  orchestratorSessionPoisonedByBuiltins,
47
48
  persistPendingFileAttachments,
49
+ removeWorkspace,
48
50
  stageAbsolutePathsAsAttachments,
49
- stageBuffersAsAttachments
50
- } from "./chunk-ADCEGG4A.js";
51
- import {
52
- SLACK_REPLY_FORMATTING,
53
- coordinatorSystemPrompt,
54
- coordinatorTurnReminder,
55
- enrichWorkspacesWithGithub,
56
- ensureGlobalCoordinatorCwd
57
- } from "./chunk-77MP647F.js";
51
+ stageBuffersAsAttachments,
52
+ syncWorkspacesFromThreads
53
+ } from "./chunk-2F63IMCL.js";
58
54
  import {
59
55
  addPrStackLayer,
60
56
  allocateTeamName,
@@ -102,7 +98,7 @@ import {
102
98
  withRepoGitLock,
103
99
  worktreeDisplayLabelForGroup,
104
100
  worktreeNameFromPath
105
- } from "./chunk-GE7M3QTE.js";
101
+ } from "./chunk-LQMFCS54.js";
106
102
  import {
107
103
  ATTACHMENTS_DIR,
108
104
  LEGACY_ATTACHMENTS_DIR
@@ -143,13 +139,13 @@ import {
143
139
  saveAppSettings,
144
140
  updateAdvancedSettings,
145
141
  writeSecureJson
146
- } from "./chunk-4XNSGDXE.js";
142
+ } from "./chunk-3BWVOUQG.js";
147
143
  import {
148
144
  stripNestedElectronEnv
149
145
  } from "./chunk-4TR3HZFT.js";
150
146
  import {
151
147
  releaseCaffeinateHoldForThread
152
- } from "./chunk-DCOZABNT.js";
148
+ } from "./chunk-AO35MGOE.js";
153
149
  import {
154
150
  writePrivateFile
155
151
  } from "./chunk-JT7R45JB.js";
@@ -164,7 +160,7 @@ import {
164
160
  updateThread,
165
161
  withThreadLock,
166
162
  writeThread
167
- } from "./chunk-TUGKX5BD.js";
163
+ } from "./chunk-7UURXPKD.js";
168
164
  import {
169
165
  appDataDir,
170
166
  findConventionSetup,
@@ -174,9 +170,10 @@ import {
174
170
  sideboardReposDir,
175
171
  sideboardWorkspacesDir,
176
172
  threadLivePath,
173
+ threadsDir,
177
174
  workspaceSettingsSourceLabel,
178
175
  worktreesRoot
179
- } from "./chunk-QZQEXME2.js";
176
+ } from "./chunk-EFVFB2AH.js";
180
177
  import {
181
178
  ensureWorktreeSideboardIgnored
182
179
  } from "./chunk-QQKDGPUI.js";
@@ -460,7 +457,7 @@ async function continueSourceThread(threadId, prompt) {
460
457
  await continueOnReply(threadId, prompt);
461
458
  return;
462
459
  }
463
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-DPTRBKEH.js");
460
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-5PSHBLP6.js");
464
461
  await getOrchestrator2().send(threadId, prompt);
465
462
  } catch {
466
463
  }
@@ -720,7 +717,7 @@ async function pollSlackOutboundWatches(opts) {
720
717
  }
721
718
 
722
719
  // src/orchestrator/orchestrator.ts
723
- import { existsSync as existsSync16 } from "fs";
720
+ import { existsSync as existsSync17 } from "fs";
724
721
 
725
722
  // src/agents/spawn.ts
726
723
  import { createInterface } from "readline";
@@ -740,9 +737,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
740
737
  `Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
741
738
  );
742
739
  }
743
- const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-IB2CCEJF.js");
740
+ const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-2BRRKSDN.js");
744
741
  if (isGlobalThread2(thread)) {
745
- const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-LWYUZAXR.js");
742
+ const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-7FHKCWXB.js");
746
743
  ensureGlobalCoordinatorCwd2(
747
744
  isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
748
745
  );
@@ -1619,7 +1616,7 @@ File: src/client/frontends/desktop/core/UserData.ts
1619
1616
 
1620
1617
  ## Growing the rules
1621
1618
 
1622
- 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.
1619
+ 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/\`.
1623
1620
  `;
1624
1621
  var REVIEW_SKILL_PATH = ".claude/skills/review/SKILL.md";
1625
1622
  var REVIEW_SKILL_NAME = "review";
@@ -1645,6 +1642,7 @@ function wrapReviewSkillMarkdown(body) {
1645
1642
  // src/review/request-review.ts
1646
1643
  var REPO_REVIEW_PATH = ".sideboard/review.md";
1647
1644
  var REPO_REVIEW_NAME = "review.md";
1645
+ var CONTEXT_REVIEW_PATH = ".context/review.md";
1648
1646
  var REVIEW_REQUEST_PATH = `${ATTACHMENTS_DIR}/Review request.md`;
1649
1647
  var LEGACY_REVIEW_REQUEST_PATH = `${LEGACY_ATTACHMENTS_DIR}/Review request.md`;
1650
1648
  var REVIEW_REQUEST_NAME = "Review request.md";
@@ -1691,41 +1689,64 @@ function skillGuidelines(content, source) {
1691
1689
  source
1692
1690
  };
1693
1691
  }
1694
- function ensureReviewSkillFile(worktreePath) {
1695
- const abs = join4(worktreePath, REVIEW_SKILL_PATH);
1696
- const existing = readTextIfPresent(abs);
1697
- if (existing) {
1698
- return { path: REVIEW_SKILL_PATH, content: existing, wrote: false };
1699
- }
1700
- const fromRepo = readTextIfPresent(join4(worktreePath, REPO_REVIEW_PATH));
1701
- const fromLocal = readLocalGuidelines(worktreePath)?.content ?? null;
1702
- const content = wrapReviewSkillMarkdown(fromRepo ?? fromLocal ?? REVIEW_REQUEST_TEMPLATE);
1692
+ function contextGuidelines(content, source) {
1693
+ return {
1694
+ path: CONTEXT_REVIEW_PATH,
1695
+ name: REPO_REVIEW_NAME,
1696
+ content,
1697
+ source
1698
+ };
1699
+ }
1700
+ function writeContextGuidelines(worktreePath, content) {
1701
+ const abs = join4(worktreePath, CONTEXT_REVIEW_PATH);
1703
1702
  mkdirSync(dirname(abs), { recursive: true });
1704
- writeFileSync(abs, content, "utf8");
1705
- return { path: REVIEW_SKILL_PATH, content, wrote: true };
1703
+ const body = content.endsWith("\n") ? content : `${content}
1704
+ `;
1705
+ writeFileSync(abs, body, "utf8");
1706
+ return body;
1706
1707
  }
1707
- function resolveReviewGuidelines(worktreePath) {
1708
+ function readSideboardReview(worktreePath, repoPath) {
1709
+ const fromWorktree = readTextIfPresent(join4(worktreePath, REPO_REVIEW_PATH));
1710
+ if (fromWorktree) return fromWorktree;
1711
+ const repo = repoPath?.replace(/\/+$/, "");
1712
+ const wt = worktreePath.replace(/\/+$/, "");
1713
+ if (!repo || repo === wt) return null;
1714
+ return readTextIfPresent(join4(repo, REPO_REVIEW_PATH));
1715
+ }
1716
+ function ensureReviewGuidelinesFile(worktreePath, repoPath) {
1708
1717
  const skillContent = readTextIfPresent(join4(worktreePath, REVIEW_SKILL_PATH));
1709
- if (skillContent) return skillGuidelines(skillContent, "skill");
1710
- const local = readLocalGuidelines(worktreePath);
1711
- if (local) {
1712
- return {
1713
- path: local.path,
1714
- name: REVIEW_REQUEST_NAME,
1715
- content: local.content,
1716
- source: "local"
1717
- };
1718
+ if (skillContent) {
1719
+ return { ...skillGuidelines(skillContent, "skill"), wrote: false };
1720
+ }
1721
+ const contextContent = readTextIfPresent(join4(worktreePath, CONTEXT_REVIEW_PATH));
1722
+ if (contextContent && !shouldRefreshReviewRequestTemplate(contextContent)) {
1723
+ return { ...contextGuidelines(contextContent, "local"), wrote: false };
1718
1724
  }
1719
- const seeded = ensureReviewSkillFile(worktreePath);
1720
- return skillGuidelines(seeded.content, seeded.wrote ? "stock" : "skill");
1725
+ const fromSideboard = readSideboardReview(worktreePath, repoPath);
1726
+ if (fromSideboard && !shouldRefreshReviewRequestTemplate(fromSideboard)) {
1727
+ const body2 = writeContextGuidelines(worktreePath, fromSideboard);
1728
+ return { ...contextGuidelines(body2, "repo"), wrote: true };
1729
+ }
1730
+ const local = readLocalGuidelines(worktreePath);
1731
+ const body = writeContextGuidelines(worktreePath, local?.content ?? REVIEW_REQUEST_TEMPLATE);
1732
+ return {
1733
+ ...contextGuidelines(body, local ? "local" : "stock"),
1734
+ wrote: true
1735
+ };
1721
1736
  }
1722
- function ensureReviewRequestFile(worktreePath) {
1723
- const seeded = ensureReviewSkillFile(worktreePath);
1724
- return skillGuidelines(seeded.content, "skill");
1737
+ function ensureReviewSkillFile(worktreePath, repoPath) {
1738
+ const g = ensureReviewGuidelinesFile(worktreePath, repoPath);
1739
+ return { path: g.path, content: g.content, wrote: g.wrote };
1740
+ }
1741
+ function resolveReviewGuidelines(worktreePath, repoPath) {
1742
+ return ensureReviewGuidelinesFile(worktreePath, repoPath);
1743
+ }
1744
+ function ensureReviewRequestFile(worktreePath, repoPath) {
1745
+ return ensureReviewGuidelinesFile(worktreePath, repoPath);
1725
1746
  }
1726
1747
  function buildReviewRequestAttachment(content, opts) {
1727
1748
  const path = opts?.path ?? REVIEW_SKILL_PATH;
1728
- const name = opts?.name ?? (path === REVIEW_SKILL_PATH ? REVIEW_SKILL_NAME : path === REPO_REVIEW_PATH ? REPO_REVIEW_NAME : REVIEW_REQUEST_NAME);
1749
+ 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);
1729
1750
  return {
1730
1751
  id: randomUUID2(),
1731
1752
  name,
@@ -1735,7 +1756,7 @@ function buildReviewRequestAttachment(content, opts) {
1735
1756
  };
1736
1757
  }
1737
1758
  function readExistingReviewRequestFile(worktreePath) {
1738
- return readTextIfPresent(join4(worktreePath, REVIEW_SKILL_PATH)) ?? readTextIfPresent(join4(worktreePath, REPO_REVIEW_PATH)) ?? readTextIfPresent(join4(worktreePath, REVIEW_REQUEST_PATH)) ?? readTextIfPresent(join4(worktreePath, LEGACY_REVIEW_REQUEST_PATH));
1759
+ return readTextIfPresent(join4(worktreePath, REVIEW_SKILL_PATH)) ?? readTextIfPresent(join4(worktreePath, CONTEXT_REVIEW_PATH)) ?? readTextIfPresent(join4(worktreePath, REPO_REVIEW_PATH)) ?? readTextIfPresent(join4(worktreePath, REVIEW_REQUEST_PATH)) ?? readTextIfPresent(join4(worktreePath, LEGACY_REVIEW_REQUEST_PATH));
1739
1760
  }
1740
1761
  async function requestReview(threadRef, send) {
1741
1762
  const from = findThreadByRef(threadRef);
@@ -1748,7 +1769,7 @@ async function requestReview(threadRef, send) {
1748
1769
  if (from.status === "archived") {
1749
1770
  throw new Error(`Thread is archived: ${from.id}`);
1750
1771
  }
1751
- const guidelines = resolveReviewGuidelines(from.worktreePath);
1772
+ const guidelines = resolveReviewGuidelines(from.worktreePath, from.repoPath);
1752
1773
  const tab = createChatTab({
1753
1774
  fromThreadId: from.id,
1754
1775
  title: "Review",
@@ -1973,6 +1994,8 @@ async function attachAbort(handle, signal) {
1973
1994
  async function runSetupScript(repoPath, worktreePath, onLine, opts) {
1974
1995
  const settings = loadWorkspaceSettings(worktreePath, repoPath);
1975
1996
  if (!settings?.setup) return { ran: false, exitCode: null, source: null };
1997
+ const source = workspaceSettingsSourceLabel(worktreePath, repoPath);
1998
+ onLine?.(`[setup] ${source ?? "settings.toml"}`);
1976
1999
  const handle = await spawnWorkspaceScript(settings.setup, {
1977
2000
  worktreePath,
1978
2001
  repoPath,
@@ -1983,7 +2006,7 @@ async function runSetupScript(repoPath, worktreePath, onLine, opts) {
1983
2006
  return {
1984
2007
  ran: true,
1985
2008
  exitCode,
1986
- source: workspaceSettingsSourceLabel(worktreePath, repoPath),
2009
+ source,
1987
2010
  kill: handle.kill
1988
2011
  };
1989
2012
  }
@@ -2007,11 +2030,9 @@ async function runConventionSetup(repoPath, worktreePath, onLine, opts) {
2007
2030
  }
2008
2031
  async function runWorkspaceSetup(repoPath, worktreePath, onLine, opts) {
2009
2032
  await ensureWorktreeSideboardIgnored(worktreePath);
2010
- const reviewSkill = ensureReviewSkillFile(worktreePath);
2011
- if (reviewSkill.wrote) {
2012
- onLine?.(
2013
- `[setup] wrote ${REVIEW_SKILL_PATH} \u2014 commit it so later worktrees inherit Review guidelines`
2014
- );
2033
+ const review = ensureReviewGuidelinesFile(worktreePath, repoPath);
2034
+ if (review.wrote) {
2035
+ onLine?.(`[setup] wrote ${review.path} \u2014 local Review guidelines`);
2015
2036
  }
2016
2037
  let setup = await runSetupScript(repoPath, worktreePath, onLine, opts);
2017
2038
  if (!setup.ran) {
@@ -2114,9 +2135,122 @@ async function startDevServer(repoPath, worktreePath, onLine, opts) {
2114
2135
  };
2115
2136
  }
2116
2137
 
2117
- // src/git/orphan-cleanup.ts
2118
- import { existsSync as existsSync5, readdirSync as readdirSync2, statSync } from "fs";
2138
+ // src/store/setup-log.ts
2139
+ import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
2119
2140
  import { join as join6 } from "path";
2141
+ var EMPTY = {
2142
+ output: "",
2143
+ running: false,
2144
+ exitCode: null,
2145
+ source: null
2146
+ };
2147
+ var memory = /* @__PURE__ */ new Map();
2148
+ var persistTimers = /* @__PURE__ */ new Map();
2149
+ function setupLogPath(threadId) {
2150
+ return join6(threadsDir(), `${threadId}.setup.log.json`);
2151
+ }
2152
+ function emptySetupLog() {
2153
+ return { ...EMPTY };
2154
+ }
2155
+ function mergeSetupOutput(prev, incoming) {
2156
+ if (!prev) return incoming;
2157
+ if (!incoming) return prev;
2158
+ if (prev === incoming) return prev;
2159
+ if (prev.startsWith(incoming) || prev.endsWith(incoming)) return prev;
2160
+ if (incoming.startsWith(prev) || incoming.endsWith(prev)) return incoming;
2161
+ return incoming.length >= prev.length ? incoming : prev;
2162
+ }
2163
+ function clone(snap) {
2164
+ return { ...snap };
2165
+ }
2166
+ function persistNow(threadId) {
2167
+ const timer = persistTimers.get(threadId);
2168
+ if (timer) {
2169
+ clearTimeout(timer);
2170
+ persistTimers.delete(threadId);
2171
+ }
2172
+ const snap = memory.get(threadId);
2173
+ if (!snap) return;
2174
+ try {
2175
+ writeFileSync2(setupLogPath(threadId), `${JSON.stringify(snap)}
2176
+ `, "utf8");
2177
+ } catch {
2178
+ }
2179
+ }
2180
+ function schedulePersist(threadId) {
2181
+ if (persistTimers.has(threadId)) return;
2182
+ persistTimers.set(
2183
+ threadId,
2184
+ setTimeout(() => {
2185
+ persistTimers.delete(threadId);
2186
+ persistNow(threadId);
2187
+ }, 100)
2188
+ );
2189
+ }
2190
+ function loadFromDisk(threadId) {
2191
+ const path = setupLogPath(threadId);
2192
+ if (!existsSync5(path)) return null;
2193
+ try {
2194
+ const raw = JSON.parse(readFileSync5(path, "utf8"));
2195
+ return {
2196
+ output: typeof raw.output === "string" ? raw.output : "",
2197
+ running: Boolean(raw.running),
2198
+ exitCode: typeof raw.exitCode === "number" ? raw.exitCode : null,
2199
+ source: typeof raw.source === "string" ? raw.source : null
2200
+ };
2201
+ } catch {
2202
+ return null;
2203
+ }
2204
+ }
2205
+ function readSetupLog(threadId) {
2206
+ const cached = memory.get(threadId);
2207
+ if (cached) return clone(cached);
2208
+ const disk = loadFromDisk(threadId);
2209
+ if (disk) {
2210
+ memory.set(threadId, disk);
2211
+ return clone(disk);
2212
+ }
2213
+ return emptySetupLog();
2214
+ }
2215
+ function beginSetupLog(threadId) {
2216
+ const snap = {
2217
+ output: "",
2218
+ running: true,
2219
+ exitCode: null,
2220
+ source: null
2221
+ };
2222
+ memory.set(threadId, snap);
2223
+ persistNow(threadId);
2224
+ return clone(snap);
2225
+ }
2226
+ function appendSetupLog(threadId, line) {
2227
+ const current = memory.get(threadId) ?? readSetupLog(threadId);
2228
+ const snap = {
2229
+ ...current,
2230
+ output: current.output ? `${current.output}
2231
+ ${line}` : line,
2232
+ running: true
2233
+ };
2234
+ memory.set(threadId, snap);
2235
+ schedulePersist(threadId);
2236
+ return clone(snap);
2237
+ }
2238
+ function finishSetupLog(threadId, exitCode, source) {
2239
+ const current = memory.get(threadId) ?? readSetupLog(threadId);
2240
+ const snap = {
2241
+ ...current,
2242
+ running: false,
2243
+ exitCode,
2244
+ source: source ?? current.source
2245
+ };
2246
+ memory.set(threadId, snap);
2247
+ persistNow(threadId);
2248
+ return clone(snap);
2249
+ }
2250
+
2251
+ // src/git/orphan-cleanup.ts
2252
+ import { existsSync as existsSync6, readdirSync as readdirSync2, statSync } from "fs";
2253
+ import { join as join7 } from "path";
2120
2254
  function isSideboardWorktreePath(path) {
2121
2255
  return path.includes("/.sideboard/worktrees/") || path.includes("/sideboard/workspaces/");
2122
2256
  }
@@ -2127,7 +2261,7 @@ async function findOrphanWorktrees(repoPaths) {
2127
2261
  repoPaths?.length ? repoPaths : threads.map((t) => t.repoPath).filter(Boolean)
2128
2262
  );
2129
2263
  const homeRoot = sideboardWorkspacesDir();
2130
- if (existsSync5(homeRoot)) {
2264
+ if (existsSync6(homeRoot)) {
2131
2265
  try {
2132
2266
  for (const entry of readdirSync2(homeRoot, { withFileTypes: true })) {
2133
2267
  if (!entry.isDirectory()) continue;
@@ -2139,7 +2273,7 @@ async function findOrphanWorktrees(repoPaths) {
2139
2273
  const orphans = [];
2140
2274
  const seen = /* @__PURE__ */ new Set();
2141
2275
  for (const repoPath of repos) {
2142
- if (!repoPath || !existsSync5(repoPath)) continue;
2276
+ if (!repoPath || !existsSync6(repoPath)) continue;
2143
2277
  try {
2144
2278
  const wts = await listWorktrees(repoPath);
2145
2279
  for (const wt of wts) {
@@ -2160,12 +2294,12 @@ async function findOrphanWorktrees(repoPaths) {
2160
2294
  }
2161
2295
  try {
2162
2296
  const root = worktreesRoot(repoPath);
2163
- if (existsSync5(root)) {
2297
+ if (existsSync6(root)) {
2164
2298
  for (const entry of readdirSync2(root, { withFileTypes: true })) {
2165
2299
  if (!entry.isDirectory()) continue;
2166
- const path = join6(root, entry.name).replace(/\/$/, "");
2300
+ const path = join7(root, entry.name).replace(/\/$/, "");
2167
2301
  if (known.has(path) || seen.has(path)) continue;
2168
- if (!existsSync5(join6(path, ".git"))) continue;
2302
+ if (!existsSync6(join7(path, ".git"))) continue;
2169
2303
  seen.add(path);
2170
2304
  let mtimeMs = 0;
2171
2305
  try {
@@ -2314,8 +2448,8 @@ async function applyThreadIntoMain(thread, opts) {
2314
2448
  }
2315
2449
 
2316
2450
  // src/git/clone-repo.ts
2317
- import { existsSync as existsSync6 } from "fs";
2318
- import { basename as basename2, join as join7 } from "path";
2451
+ import { existsSync as existsSync7 } from "fs";
2452
+ import { basename as basename2, join as join8 } from "path";
2319
2453
  import { execa as execa4 } from "execa";
2320
2454
  async function cloneRepoIntoSideboard(opts) {
2321
2455
  const url = opts.url.trim();
@@ -2326,8 +2460,8 @@ async function cloneRepoIntoSideboard(opts) {
2326
2460
  name = leaf || "repo";
2327
2461
  }
2328
2462
  name = name.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "repo";
2329
- const dest = join7(sideboardReposDir(), name);
2330
- if (existsSync6(dest)) {
2463
+ const dest = join8(sideboardReposDir(), name);
2464
+ if (existsSync7(dest)) {
2331
2465
  const repoPath2 = await resolveRepoRoot(dest);
2332
2466
  const workspace2 = await ensureWorkspace(repoPath2);
2333
2467
  return { repoPath: repoPath2, workspace: workspace2 };
@@ -2344,13 +2478,13 @@ async function cloneRepoIntoSideboard(opts) {
2344
2478
  }
2345
2479
 
2346
2480
  // src/store/desktop-host.ts
2347
- import { readFileSync as readFileSync5, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
2348
- import { join as join8 } from "path";
2481
+ import { readFileSync as readFileSync6, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
2482
+ import { join as join9 } from "path";
2349
2483
  function desktopHostPidPath() {
2350
- return join8(appDataDir(), "desktop-host.pid");
2484
+ return join9(appDataDir(), "desktop-host.pid");
2351
2485
  }
2352
2486
  function claimDesktopHost(pid = process.pid) {
2353
- writeFileSync2(desktopHostPidPath(), `${pid}
2487
+ writeFileSync3(desktopHostPidPath(), `${pid}
2354
2488
  `, "utf8");
2355
2489
  }
2356
2490
  function releaseDesktopHost(pid = process.pid) {
@@ -2362,7 +2496,7 @@ function releaseDesktopHost(pid = process.pid) {
2362
2496
  }
2363
2497
  function readDesktopHostPid() {
2364
2498
  try {
2365
- const pid = Number.parseInt(readFileSync5(desktopHostPidPath(), "utf8").trim(), 10);
2499
+ const pid = Number.parseInt(readFileSync6(desktopHostPidPath(), "utf8").trim(), 10);
2366
2500
  if (!Number.isFinite(pid) || pid <= 0) return null;
2367
2501
  return pid;
2368
2502
  } catch {
@@ -2429,7 +2563,7 @@ function notifyParentOfChildHalt(child, status, send) {
2429
2563
  }
2430
2564
 
2431
2565
  // src/threads/create.ts
2432
- import { existsSync as existsSync7 } from "fs";
2566
+ import { existsSync as existsSync8 } from "fs";
2433
2567
 
2434
2568
  // src/detect/detect.ts
2435
2569
  async function detectAgents() {
@@ -2600,12 +2734,19 @@ function urlsMatch(a, b) {
2600
2734
  const norm = (u) => u.trim().replace(/\/+$/, "").toLowerCase();
2601
2735
  return Boolean(a && b && norm(a) === norm(b));
2602
2736
  }
2737
+ function threadPrUrlMatchesNumber(thread, number) {
2738
+ if (!Number.isFinite(number) || number < 1) return false;
2739
+ const url = (thread.prUrl ?? "").trim();
2740
+ if (!url) return false;
2741
+ return new RegExp(`/pull/${number}(?:/|$|[?#])`, "i").test(url);
2742
+ }
2603
2743
  function threadMatchesPr(thread, pr) {
2604
2744
  const num = String(pr.number);
2605
2745
  if (thread.sourceType === "pr" && normalizeIssueKey(thread.sourceRef ?? "") === num) {
2606
2746
  return true;
2607
2747
  }
2608
2748
  if (thread.prUrl && pr.url && urlsMatch(thread.prUrl, pr.url)) return true;
2749
+ if (threadPrUrlMatchesNumber(thread, pr.number)) return true;
2609
2750
  const head = pr.headRefName.trim().toLowerCase();
2610
2751
  if (!head) return false;
2611
2752
  const branch = (thread.branchName ?? "").trim().toLowerCase();
@@ -2658,22 +2799,65 @@ function findLiveThreadForCreate(input, threads) {
2658
2799
  (t) => threadMatchesPr(t, {
2659
2800
  number: Number.isFinite(number) ? number : -1,
2660
2801
  title: input.title ?? "",
2661
- url: "",
2662
- headRefName: ""
2802
+ url: input.prUrl ?? "",
2803
+ headRefName: input.headRefName ?? ""
2663
2804
  })
2664
2805
  );
2665
2806
  }
2666
2807
  if (input.sourceType === "branch") {
2667
2808
  if (isDefaultBranchCreateRef(input.sourceRef)) return void 0;
2668
- return live.find(
2809
+ const byBranch = live.find(
2669
2810
  (t) => threadMatchesBranch(t, {
2670
2811
  ref: input.sourceRef,
2671
2812
  repoPath: input.repoPath
2672
2813
  })
2673
2814
  );
2815
+ if (byBranch) return byBranch;
2816
+ if (input.prUrl || input.headRefName) {
2817
+ const number = Number(normalizeIssueKey(input.sourceRef));
2818
+ return live.find(
2819
+ (t) => threadMatchesPr(t, {
2820
+ number: Number.isFinite(number) ? number : -1,
2821
+ title: input.title ?? "",
2822
+ url: input.prUrl ?? "",
2823
+ headRefName: input.headRefName ?? input.sourceRef
2824
+ })
2825
+ );
2826
+ }
2827
+ return void 0;
2674
2828
  }
2675
2829
  return void 0;
2676
2830
  }
2831
+ function findLiveThreadForCreateSource(source, threads, prs) {
2832
+ const found = findLiveThreadForCreate(
2833
+ {
2834
+ sourceType: source.kind,
2835
+ sourceRef: source.ref,
2836
+ repoPath: source.repoPath,
2837
+ title: source.title,
2838
+ prUrl: source.url,
2839
+ headRefName: source.headRefName
2840
+ },
2841
+ threads
2842
+ );
2843
+ if (found) return found;
2844
+ if (source.kind !== "branch" || !prs?.length) return void 0;
2845
+ if (isDefaultBranchCreateRef(source.ref)) return void 0;
2846
+ const head = normalizeBranchRef(source.ref);
2847
+ const pr = prs.find((p) => normalizeBranchRef(p.headRefName) === head);
2848
+ if (!pr) return void 0;
2849
+ return findLiveThreadForCreate(
2850
+ {
2851
+ sourceType: "pr",
2852
+ sourceRef: String(pr.number),
2853
+ repoPath: source.repoPath,
2854
+ title: pr.title,
2855
+ prUrl: pr.url,
2856
+ headRefName: pr.headRefName
2857
+ },
2858
+ threads
2859
+ );
2860
+ }
2677
2861
  function boardPinIdentity(pin) {
2678
2862
  const ref = normalizeIssueKey(pin.ref);
2679
2863
  if (pin.kind === "ticket" && (pin.provider === "linear" || !pin.repoPath)) {
@@ -2903,42 +3087,53 @@ function assembleHomeBoard(input) {
2903
3087
  }
2904
3088
  return { columns, hidden, totals };
2905
3089
  }
2906
- 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.";
3090
+ 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.";
2907
3091
 
2908
3092
  // src/threads/create.ts
2909
3093
  function persistCreateAttachments(worktreePath, attachments) {
2910
3094
  return persistPendingFileAttachments(worktreePath, attachments ?? []);
2911
3095
  }
3096
+ function liveThreadsForCreate() {
3097
+ return listThreads({ includeArchived: false }).map((t) => ({
3098
+ ...t,
3099
+ repoPath: canonicalizeRepoPath(t.repoPath)
3100
+ }));
3101
+ }
3102
+ function reuseLiveThread(input, repoPath, match) {
3103
+ if (input.reuseExisting === false) return void 0;
3104
+ const existing = findLiveThreadForCreate(
3105
+ {
3106
+ sourceType: match.sourceType,
3107
+ sourceRef: match.sourceRef,
3108
+ repoPath,
3109
+ title: match.title ?? input.title,
3110
+ cowboy: input.cowboy,
3111
+ prUrl: match.prUrl,
3112
+ headRefName: match.headRefName
3113
+ },
3114
+ liveThreadsForCreate()
3115
+ );
3116
+ if (!existing) return void 0;
3117
+ const thread = readThread(existing.id) ?? existing;
3118
+ if (!input.attachments?.length) return thread;
3119
+ return updateThread(thread.id, {
3120
+ attachments: persistCreateAttachments(thread.worktreePath, [
3121
+ ...thread.attachments,
3122
+ ...input.attachments
3123
+ ])
3124
+ });
3125
+ }
2912
3126
  async function createThread(input, _onSetupLine) {
2913
3127
  const repoPath = await resolveRepoRoot(input.repoPath);
2914
- if (!existsSync7(repoPath)) {
3128
+ if (!existsSync8(repoPath)) {
2915
3129
  throw new Error(`Repo not found: ${repoPath}`);
2916
3130
  }
2917
- if (input.reuseExisting !== false) {
2918
- const existing = findLiveThreadForCreate(
2919
- {
2920
- sourceType: input.sourceType,
2921
- sourceRef: input.sourceRef,
2922
- repoPath,
2923
- title: input.title,
2924
- cowboy: input.cowboy
2925
- },
2926
- listThreads({ includeArchived: false }).map((t) => ({
2927
- ...t,
2928
- repoPath: canonicalizeRepoPath(t.repoPath)
2929
- }))
2930
- );
2931
- if (existing) {
2932
- const thread2 = readThread(existing.id) ?? existing;
2933
- if (!input.attachments?.length) return thread2;
2934
- return updateThread(thread2.id, {
2935
- attachments: persistCreateAttachments(thread2.worktreePath, [
2936
- ...thread2.attachments,
2937
- ...input.attachments
2938
- ])
2939
- });
2940
- }
2941
- }
3131
+ const reused = reuseLiveThread(input, repoPath, {
3132
+ sourceType: input.sourceType,
3133
+ sourceRef: input.sourceRef,
3134
+ title: input.title
3135
+ });
3136
+ if (reused) return reused;
2942
3137
  const resolved = resolveNewThreadOptions({
2943
3138
  agent: input.agent,
2944
3139
  model: input.model,
@@ -2947,7 +3142,7 @@ async function createThread(input, _onSetupLine) {
2947
3142
  });
2948
3143
  await requireAgent(resolved.agent);
2949
3144
  if (input.cowboy) {
2950
- const { cowboyModeEnabled } = await import("./app-settings-3CHCNIMD.js");
3145
+ const { cowboyModeEnabled } = await import("./app-settings-H3INSR67.js");
2951
3146
  if (!cowboyModeEnabled()) {
2952
3147
  throw new Error(
2953
3148
  "Cowboy mode is off. Enable it in Settings \u2192 Advanced."
@@ -3005,7 +3200,7 @@ async function createThread(input, _onSetupLine) {
3005
3200
  let sourceIsFork = false;
3006
3201
  if (!input.cowboy && sourceType === "branch" && (!sourceRef || sourceRef === "HEAD" || sourceRef === "default")) {
3007
3202
  if (isAbleTimeConnected() && getIssueSource() === "abletime") {
3008
- const { ensureAbleTimeTask, issueAttachmentForAbleTimeTask } = await import("./abletime-223A3LY6.js");
3203
+ const { ensureAbleTimeTask, issueAttachmentForAbleTimeTask } = await import("./abletime-H7LPGIP2.js");
3009
3204
  const task = await ensureAbleTimeTask({
3010
3205
  title: title?.trim() || "Untitled work",
3011
3206
  description: input.prompt?.trim() || void 0
@@ -3015,6 +3210,12 @@ async function createThread(input, _onSetupLine) {
3015
3210
  persistedSourceRef = task.identifier;
3016
3211
  title = title?.trim() || task.title;
3017
3212
  attachments = [...attachments, issueAttachmentForAbleTimeTask(task)];
3213
+ const reusedTicket = reuseLiveThread(
3214
+ { ...input, sourceType: "ticket", sourceRef, title, attachments },
3215
+ repoPath,
3216
+ { sourceType: "ticket", sourceRef, title }
3217
+ );
3218
+ if (reusedTicket) return reusedTicket;
3018
3219
  }
3019
3220
  }
3020
3221
  let prUrl = null;
@@ -3024,6 +3225,14 @@ async function createThread(input, _onSetupLine) {
3024
3225
  if (!Number.isFinite(num)) throw new Error(`Invalid PR number: ${input.sourceRef}`);
3025
3226
  const pr = await getPr(repoPath, num);
3026
3227
  if (!pr) throw new Error(`PR #${num} not found`);
3228
+ const reusedPr = reuseLiveThread(input, repoPath, {
3229
+ sourceType: "pr",
3230
+ sourceRef: String(pr.number),
3231
+ title: input.title ?? pr.title,
3232
+ prUrl: pr.url,
3233
+ headRefName: pr.headRefName
3234
+ });
3235
+ if (reusedPr) return reusedPr;
3027
3236
  sourceIsFork = pr.isCrossRepository;
3028
3237
  prUrl = pr.url;
3029
3238
  const localFetchBranch = `sideboard-pr-${pr.number}`;
@@ -3036,9 +3245,19 @@ async function createThread(input, _onSetupLine) {
3036
3245
  sourceRef = await resolveDefaultBranch(repoPath);
3037
3246
  } else {
3038
3247
  const existing = await getPrForHeadBranch(repoPath, sourceRef);
3039
- if (existing?.url) {
3040
- prUrl = existing.url;
3041
- prTitle = existing.title;
3248
+ if (existing) {
3249
+ const reusedPr = reuseLiveThread(input, repoPath, {
3250
+ sourceType: "pr",
3251
+ sourceRef: String(existing.number),
3252
+ title: input.title ?? existing.title,
3253
+ prUrl: existing.url,
3254
+ headRefName: existing.headRefName
3255
+ });
3256
+ if (reusedPr) return reusedPr;
3257
+ if (existing.url) {
3258
+ prUrl = existing.url;
3259
+ prTitle = existing.title;
3260
+ }
3042
3261
  }
3043
3262
  }
3044
3263
  } else if (sourceType === "adopt") {
@@ -3078,7 +3297,7 @@ async function createThread(input, _onSetupLine) {
3078
3297
  return readThread(thread.id) ?? thread;
3079
3298
  }
3080
3299
  async function listLinearIssues(agent, repoPath) {
3081
- const { getAdapter: getAdapter2 } = await import("./agents-U2JCFXW4.js");
3300
+ const { getAdapter: getAdapter2 } = await import("./agents-YVW75KBO.js");
3082
3301
  await requireAgent(agent, { requireLinear: true });
3083
3302
  const adapter = getAdapter2(agent);
3084
3303
  if (!adapter.listLinearIssues) {
@@ -3101,11 +3320,11 @@ function shouldRemoveWorktreeOnTeardown(thread) {
3101
3320
 
3102
3321
  // src/store/turn-live.ts
3103
3322
  import {
3104
- existsSync as existsSync8,
3323
+ existsSync as existsSync9,
3105
3324
  renameSync,
3106
3325
  unlinkSync as unlinkSync2,
3107
- writeFileSync as writeFileSync3,
3108
- readFileSync as readFileSync6
3326
+ writeFileSync as writeFileSync4,
3327
+ readFileSync as readFileSync7
3109
3328
  } from "fs";
3110
3329
  var buffers = /* @__PURE__ */ new Map();
3111
3330
  var FLUSH_MS = 800;
@@ -3182,7 +3401,7 @@ function writeTurnLive(threadId, progress) {
3182
3401
  const path = threadLivePath(threadId);
3183
3402
  const tmp = `${path}.${process.pid}.tmp`;
3184
3403
  try {
3185
- writeFileSync3(tmp, JSON.stringify(progress), "utf8");
3404
+ writeFileSync4(tmp, JSON.stringify(progress), "utf8");
3186
3405
  renameSync(tmp, path);
3187
3406
  } catch {
3188
3407
  try {
@@ -3193,9 +3412,9 @@ function writeTurnLive(threadId, progress) {
3193
3412
  }
3194
3413
  function readTurnLive(threadId) {
3195
3414
  const path = threadLivePath(threadId);
3196
- if (!existsSync8(path)) return null;
3415
+ if (!existsSync9(path)) return null;
3197
3416
  try {
3198
- const raw = JSON.parse(readFileSync6(path, "utf8"));
3417
+ const raw = JSON.parse(readFileSync7(path, "utf8"));
3199
3418
  if (!raw || typeof raw.summary !== "string") return null;
3200
3419
  return raw;
3201
3420
  } catch {
@@ -3207,7 +3426,7 @@ function clearTurnLive(threadId) {
3207
3426
  if (buf?.timer) clearTimeout(buf.timer);
3208
3427
  buffers.delete(threadId);
3209
3428
  const path = threadLivePath(threadId);
3210
- if (!existsSync8(path)) return;
3429
+ if (!existsSync9(path)) return;
3211
3430
  try {
3212
3431
  unlinkSync2(path);
3213
3432
  } catch {
@@ -3391,26 +3610,26 @@ function createQuotaFailoverChat(from, fallbackAgent, limitText) {
3391
3610
  import { execFileSync } from "child_process";
3392
3611
  import {
3393
3612
  copyFileSync as copyFileSync2,
3394
- existsSync as existsSync9,
3613
+ existsSync as existsSync10,
3395
3614
  mkdtempSync,
3396
3615
  readdirSync as readdirSync3,
3397
- readFileSync as readFileSync7,
3616
+ readFileSync as readFileSync8,
3398
3617
  rmSync
3399
3618
  } from "fs";
3400
3619
  import { tmpdir } from "os";
3401
- import { join as join9 } from "path";
3620
+ import { join as join10 } from "path";
3402
3621
  import { createRequire } from "module";
3403
- var CONDUCTOR_APP_SUPPORT = join9(
3622
+ var CONDUCTOR_APP_SUPPORT = join10(
3404
3623
  process.env.HOME ?? "",
3405
3624
  "Library",
3406
3625
  "Application Support",
3407
3626
  "com.conductor.app"
3408
3627
  );
3409
- var CONDUCTOR_DB = join9(CONDUCTOR_APP_SUPPORT, "conductor.db");
3410
- var CURSOR_SDK_STORE = join9(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
3628
+ var CONDUCTOR_DB = join10(CONDUCTOR_APP_SUPPORT, "conductor.db");
3629
+ var CURSOR_SDK_STORE = join10(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
3411
3630
  function thisModuleFile() {
3412
3631
  const cjsFile = typeof __filename !== "undefined" ? __filename : "";
3413
- return cjsFile || process.argv[1] || join9(process.cwd(), "package.json");
3632
+ return cjsFile || process.argv[1] || join10(process.cwd(), "package.json");
3414
3633
  }
3415
3634
  function openReadonlySqlite(file) {
3416
3635
  const req = createRequire(thisModuleFile());
@@ -3428,7 +3647,7 @@ function mapAgentType(raw) {
3428
3647
  return null;
3429
3648
  }
3430
3649
  function resolveConductorCursorAgentId(workspacePath) {
3431
- if (!workspacePath || !existsSync9(CURSOR_SDK_STORE)) return null;
3650
+ if (!workspacePath || !existsSync10(CURSOR_SDK_STORE)) return null;
3432
3651
  const normalized = workspacePath.replace(/\/$/, "");
3433
3652
  let best = null;
3434
3653
  let hashes;
@@ -3438,11 +3657,11 @@ function resolveConductorCursorAgentId(workspacePath) {
3438
3657
  return null;
3439
3658
  }
3440
3659
  for (const hash of hashes) {
3441
- const agentsFile = join9(CURSOR_SDK_STORE, hash, "agents.ndjson");
3442
- if (!existsSync9(agentsFile)) continue;
3660
+ const agentsFile = join10(CURSOR_SDK_STORE, hash, "agents.ndjson");
3661
+ if (!existsSync10(agentsFile)) continue;
3443
3662
  let text;
3444
3663
  try {
3445
- text = readFileSync7(agentsFile, "utf8");
3664
+ text = readFileSync8(agentsFile, "utf8");
3446
3665
  } catch {
3447
3666
  continue;
3448
3667
  }
@@ -3466,7 +3685,7 @@ function resolveConductorCursorAgentId(workspacePath) {
3466
3685
  return best?.agentId ?? null;
3467
3686
  }
3468
3687
  async function adoptThread(input) {
3469
- if (!existsSync9(input.worktreePath)) {
3688
+ if (!existsSync10(input.worktreePath)) {
3470
3689
  throw new Error(`Worktree not found: ${input.worktreePath}`);
3471
3690
  }
3472
3691
  const repoPath = await resolveRepoRoot(input.worktreePath);
@@ -3485,7 +3704,7 @@ async function adoptThread(input) {
3485
3704
  messages: input.messages ?? []
3486
3705
  });
3487
3706
  writeThread(thread);
3488
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-ZXCUVCZC.js");
3707
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-5UNDTN4G.js");
3489
3708
  await ensureWorkspace2(repoPath);
3490
3709
  const { ensureWorktreeSideboardIgnored: ensureWorktreeSideboardIgnored2 } = await import("./worktree-exclude-X54WSOXM.js");
3491
3710
  await ensureWorktreeSideboardIgnored2(input.worktreePath);
@@ -3495,16 +3714,16 @@ function conductorDbPath() {
3495
3714
  return CONDUCTOR_DB;
3496
3715
  }
3497
3716
  function listConductorWorkspaces() {
3498
- if (!existsSync9(CONDUCTOR_DB)) {
3717
+ if (!existsSync10(CONDUCTOR_DB)) {
3499
3718
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
3500
3719
  }
3501
- const tmp = mkdtempSync(join9(tmpdir(), "sideboard-conductor-"));
3502
- const snapshot = join9(tmp, "conductor.db");
3720
+ const tmp = mkdtempSync(join10(tmpdir(), "sideboard-conductor-"));
3721
+ const snapshot = join10(tmp, "conductor.db");
3503
3722
  try {
3504
3723
  copyFileSync2(CONDUCTOR_DB, snapshot);
3505
3724
  for (const suffix of ["-wal", "-shm"]) {
3506
3725
  const src = `${CONDUCTOR_DB}${suffix}`;
3507
- if (existsSync9(src)) {
3726
+ if (existsSync10(src)) {
3508
3727
  try {
3509
3728
  copyFileSync2(src, `${snapshot}${suffix}`);
3510
3729
  } catch {
@@ -3586,16 +3805,16 @@ function listConductorWorkspaces() {
3586
3805
  }
3587
3806
  }
3588
3807
  function importConductorWorkspace(workspaceId) {
3589
- if (!existsSync9(CONDUCTOR_DB)) {
3808
+ if (!existsSync10(CONDUCTOR_DB)) {
3590
3809
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
3591
3810
  }
3592
- const tmp = mkdtempSync(join9(tmpdir(), "sideboard-conductor-"));
3593
- const snapshot = join9(tmp, "conductor.db");
3811
+ const tmp = mkdtempSync(join10(tmpdir(), "sideboard-conductor-"));
3812
+ const snapshot = join10(tmp, "conductor.db");
3594
3813
  try {
3595
3814
  copyFileSync2(CONDUCTOR_DB, snapshot);
3596
3815
  for (const suffix of ["-wal", "-shm"]) {
3597
3816
  const src = `${CONDUCTOR_DB}${suffix}`;
3598
- if (existsSync9(src)) {
3817
+ if (existsSync10(src)) {
3599
3818
  try {
3600
3819
  copyFileSync2(src, `${snapshot}${suffix}`);
3601
3820
  } catch {
@@ -3615,7 +3834,7 @@ function importConductorWorkspace(workspaceId) {
3615
3834
  ).get(workspaceId);
3616
3835
  if (!row) throw new Error(`Conductor workspace not found: ${workspaceId}`);
3617
3836
  const worktreePath = String(row.workspacePath);
3618
- if (!existsSync9(worktreePath)) {
3837
+ if (!existsSync10(worktreePath)) {
3619
3838
  throw new Error(`Conductor worktree missing on disk: ${worktreePath}`);
3620
3839
  }
3621
3840
  let sessionId = null;
@@ -3686,7 +3905,7 @@ async function importConductorWorkspaceAsync(workspaceId) {
3686
3905
  }
3687
3906
 
3688
3907
  // src/threads/stack-layers.ts
3689
- import { existsSync as existsSync10 } from "fs";
3908
+ import { existsSync as existsSync11 } from "fs";
3690
3909
  function stackIdFrom(stack) {
3691
3910
  if (stack.stackNumber != null) return `gh-stack-${stack.stackNumber}`;
3692
3911
  const key = stack.layers.map((l) => l.branchName).join("|");
@@ -3749,7 +3968,7 @@ async function openStackLayer(input, _onSetupLine) {
3749
3968
  let createdWorktree = false;
3750
3969
  const trees = await listWorktrees(repoPath);
3751
3970
  const checkedOut = trees.find((w) => w.branch === branchName);
3752
- if (checkedOut?.path && existsSync10(checkedOut.path)) {
3971
+ if (checkedOut?.path && existsSync11(checkedOut.path)) {
3753
3972
  if (input.reuseExistingWorktree !== false) {
3754
3973
  worktreePath = checkedOut.path;
3755
3974
  } else {
@@ -3891,7 +4110,7 @@ async function initStackFromThread(input, onSetupLine) {
3891
4110
  async function createPrStack(input, onSetupLine) {
3892
4111
  await requireAgent(input.agent);
3893
4112
  const repoPath = await resolveRepoRoot(input.repoPath);
3894
- if (!existsSync10(repoPath)) throw new Error(`Repo not found: ${repoPath}`);
4113
+ if (!existsSync11(repoPath)) throw new Error(`Repo not found: ${repoPath}`);
3895
4114
  if (!input.branches.length) throw new Error("At least one branch name required");
3896
4115
  const status = await detectGhStack(repoPath);
3897
4116
  if (!status.available) throw new Error(status.reason);
@@ -3958,7 +4177,7 @@ async function createPrStack(input, onSetupLine) {
3958
4177
  }
3959
4178
  }
3960
4179
  const claimed = new Set(threads.map((t) => t.worktreePath));
3961
- if (!claimed.has(bootstrap.worktreePath) && existsSync10(bootstrap.worktreePath)) {
4180
+ if (!claimed.has(bootstrap.worktreePath) && existsSync11(bootstrap.worktreePath)) {
3962
4181
  try {
3963
4182
  await removeWorktree(repoPath, bootstrap.worktreePath, {
3964
4183
  deleteBranch: bootstrap.branchName
@@ -3980,10 +4199,10 @@ function stackAgentDefaultsFrom(input) {
3980
4199
  }
3981
4200
 
3982
4201
  // src/diff/diff.ts
3983
- import { existsSync as existsSync11, mkdirSync as mkdirSync3, readFileSync as readFileSync8, statSync as statSync2, writeFileSync as writeFileSync4 } from "fs";
3984
- import { dirname as dirname3, join as join10 } from "path";
4202
+ import { existsSync as existsSync12, mkdirSync as mkdirSync3, readFileSync as readFileSync9, statSync as statSync2, writeFileSync as writeFileSync5 } from "fs";
4203
+ import { dirname as dirname3, join as join11 } from "path";
3985
4204
  async function inspectGitWorktree(worktreePath) {
3986
- if (!worktreePath || !existsSync11(worktreePath)) return "missing_worktree";
4205
+ if (!worktreePath || !existsSync12(worktreePath)) return "missing_worktree";
3987
4206
  const check = await git(["rev-parse", "--is-inside-work-tree"], worktreePath, {
3988
4207
  reject: false
3989
4208
  });
@@ -3991,7 +4210,7 @@ async function inspectGitWorktree(worktreePath) {
3991
4210
  return "ok";
3992
4211
  }
3993
4212
  async function initializeGitRepository(worktreePath) {
3994
- if (!worktreePath || !existsSync11(worktreePath)) {
4213
+ if (!worktreePath || !existsSync12(worktreePath)) {
3995
4214
  throw new Error("Worktree not found");
3996
4215
  }
3997
4216
  const status = await inspectGitWorktree(worktreePath);
@@ -4127,11 +4346,11 @@ new file mode 100644
4127
4346
  };
4128
4347
  }
4129
4348
  async function untrackedPatch(worktreePath, path, maxHunk) {
4130
- const abs = join10(worktreePath, path);
4349
+ const abs = join11(worktreePath, path);
4131
4350
  try {
4132
4351
  const st = statSync2(abs);
4133
4352
  if (st.isFile() && st.size > maxHunk) {
4134
- const buf = readFileSync8(abs).subarray(0, maxHunk);
4353
+ const buf = readFileSync9(abs).subarray(0, maxHunk);
4135
4354
  return syntheticAddPatch(path, buf.toString("utf8"), maxHunk);
4136
4355
  }
4137
4356
  } catch {
@@ -4632,7 +4851,7 @@ var DEFAULT_UPLOAD_MAX_BYTES = 5e7;
4632
4851
  function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
4633
4852
  assertSafeRelativePath(relativePath);
4634
4853
  const maxBytes = opts?.maxBytes ?? DEFAULT_UPLOAD_MAX_BYTES;
4635
- const abs = join10(worktreePath, relativePath);
4854
+ const abs = join11(worktreePath, relativePath);
4636
4855
  const st = statSync2(abs);
4637
4856
  if (!st.isFile()) {
4638
4857
  throw new Error(`Not a file: ${relativePath}`);
@@ -4642,7 +4861,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
4642
4861
  `File too large to upload (${st.size} bytes; max ${maxBytes})`
4643
4862
  );
4644
4863
  }
4645
- const buf = readFileSync8(abs);
4864
+ const buf = readFileSync9(abs);
4646
4865
  return {
4647
4866
  path: relativePath,
4648
4867
  contentBase64: buf.toString("base64"),
@@ -4652,12 +4871,12 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
4652
4871
  function readWorktreeFile(worktreePath, relativePath, opts) {
4653
4872
  assertSafeRelativePath(relativePath);
4654
4873
  const maxBytes = opts?.maxBytes ?? 2e5;
4655
- const abs = join10(worktreePath, relativePath);
4874
+ const abs = join11(worktreePath, relativePath);
4656
4875
  const st = statSync2(abs);
4657
4876
  if (!st.isFile()) {
4658
4877
  throw new Error(`Not a file: ${relativePath}`);
4659
4878
  }
4660
- const buf = readFileSync8(abs);
4879
+ const buf = readFileSync9(abs);
4661
4880
  if (isImageRelativePath(relativePath)) {
4662
4881
  const maxImageBytes = Math.max(maxBytes, 15e6);
4663
4882
  const truncated2 = buf.length > maxImageBytes;
@@ -4700,9 +4919,9 @@ function assertSafeRelativePath(relativePath) {
4700
4919
  }
4701
4920
  function writeWorktreeFile(worktreePath, relativePath, content) {
4702
4921
  assertSafeRelativePath(relativePath);
4703
- const abs = join10(worktreePath, relativePath);
4922
+ const abs = join11(worktreePath, relativePath);
4704
4923
  mkdirSync3(dirname3(abs), { recursive: true });
4705
- writeFileSync4(abs, content, "utf8");
4924
+ writeFileSync5(abs, content, "utf8");
4706
4925
  return { path: relativePath };
4707
4926
  }
4708
4927
  async function getDiffSummary(worktreePath, repoPath, opts) {
@@ -4827,9 +5046,9 @@ async function confirmLand(thread, opts) {
4827
5046
  }
4828
5047
 
4829
5048
  // src/skills/discover.ts
4830
- import { existsSync as existsSync12, readdirSync as readdirSync4, readFileSync as readFileSync9, statSync as statSync3 } from "fs";
5049
+ import { existsSync as existsSync13, readdirSync as readdirSync4, readFileSync as readFileSync10, statSync as statSync3 } from "fs";
4831
5050
  import { homedir } from "os";
4832
- import { join as join11 } from "path";
5051
+ import { join as join12 } from "path";
4833
5052
 
4834
5053
  // src/skills/bundled/long-running.ts
4835
5054
  var LONG_RUNNING_SKILL_COMMAND = "long-running";
@@ -4897,6 +5116,16 @@ node <detached-job.js> wait --pid-file FILE --log-file FILE [--ok-pattern TEXT]
4897
5116
  4. On \`ok\`, present once more (\`status=ok\`, last \`delta\`) and finish the task. On \`failed\`, fix from the log.
4898
5117
 
4899
5118
  Never tell the user \u201Csay status when it\u2019s done.\u201D You wait.
5119
+
5120
+ ## PR checks (only if a goal is given)
5121
+
5122
+ Do **not** watch after every push. If the user gave a goal (Greptile 5/5, CI green, until checks pass), wait the same way:
5123
+
5124
+ \`\`\`bash
5125
+ node <detached-job.js> start pr-checks -- gh pr checks --watch
5126
+ \`\`\`
5127
+
5128
+ 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.
4900
5129
  `;
4901
5130
 
4902
5131
  // src/skills/discover.ts
@@ -4948,7 +5177,7 @@ function parseFrontmatter(content) {
4948
5177
  }
4949
5178
  function readSkill(skillMd, source) {
4950
5179
  try {
4951
- const content = readFileSync9(skillMd, "utf8");
5180
+ const content = readFileSync10(skillMd, "utf8");
4952
5181
  const { name: fmName, description } = parseFrontmatter(content);
4953
5182
  const dirName = skillMd.split("/").slice(-2, -1)[0] || "skill";
4954
5183
  const name = fmName || dirName;
@@ -4967,7 +5196,7 @@ function readSkill(skillMd, source) {
4967
5196
  }
4968
5197
  }
4969
5198
  function scanSkillsDir(dir, source, out) {
4970
- if (!existsSync12(dir)) return;
5199
+ if (!existsSync13(dir)) return;
4971
5200
  let entries;
4972
5201
  try {
4973
5202
  entries = readdirSync4(dir);
@@ -4976,8 +5205,8 @@ function scanSkillsDir(dir, source, out) {
4976
5205
  }
4977
5206
  for (const entry of entries) {
4978
5207
  if (entry.startsWith(".")) continue;
4979
- const skillMd = join11(dir, entry, "SKILL.md");
4980
- if (!existsSync12(skillMd)) continue;
5208
+ const skillMd = join12(dir, entry, "SKILL.md");
5209
+ if (!existsSync13(skillMd)) continue;
4981
5210
  try {
4982
5211
  if (!statSync3(skillMd).isFile()) continue;
4983
5212
  } catch {
@@ -4988,7 +5217,7 @@ function scanSkillsDir(dir, source, out) {
4988
5217
  }
4989
5218
  }
4990
5219
  function scanClaudePluginSkills(pluginsRoot, out) {
4991
- if (!existsSync12(pluginsRoot)) return;
5220
+ if (!existsSync13(pluginsRoot)) return;
4992
5221
  const walk = (dir, depth, lookingForSkillsDir) => {
4993
5222
  if (depth > 7) return;
4994
5223
  let entries;
@@ -4998,12 +5227,12 @@ function scanClaudePluginSkills(pluginsRoot, out) {
4998
5227
  return;
4999
5228
  }
5000
5229
  if (lookingForSkillsDir && entries.includes("SKILL.md")) {
5001
- const skill = readSkill(join11(dir, "SKILL.md"), "cli");
5230
+ const skill = readSkill(join12(dir, "SKILL.md"), "cli");
5002
5231
  if (skill) out.push(skill);
5003
5232
  }
5004
5233
  for (const entry of entries) {
5005
5234
  if (entry === "node_modules" || entry === ".git") continue;
5006
- const full = join11(dir, entry);
5235
+ const full = join12(dir, entry);
5007
5236
  try {
5008
5237
  if (!statSync3(full).isDirectory()) continue;
5009
5238
  } catch {
@@ -5023,17 +5252,17 @@ function discoverSkills(worktreePath) {
5023
5252
  const home = homedir();
5024
5253
  const collected = [];
5025
5254
  for (const rel of [".claude/skills", ".cursor/skills", ".sideboard/skills", ".brightsy/skills", "skills"]) {
5026
- scanSkillsDir(join11(worktreePath, rel), "workspace", collected);
5255
+ scanSkillsDir(join12(worktreePath, rel), "workspace", collected);
5027
5256
  }
5028
5257
  for (const abs of [
5029
- join11(home, ".claude/skills"),
5030
- join11(home, ".cursor/skills"),
5031
- join11(home, ".sideboard/skills"),
5032
- join11(home, ".brightsy/skills")
5258
+ join12(home, ".claude/skills"),
5259
+ join12(home, ".cursor/skills"),
5260
+ join12(home, ".sideboard/skills"),
5261
+ join12(home, ".brightsy/skills")
5033
5262
  ]) {
5034
5263
  scanSkillsDir(abs, "user", collected);
5035
5264
  }
5036
- scanClaudePluginSkills(join11(home, ".claude/plugins"), collected);
5265
+ scanClaudePluginSkills(join12(home, ".claude/plugins"), collected);
5037
5266
  collected.push(...bundledSkills());
5038
5267
  const rank = {
5039
5268
  workspace: 0,
@@ -5059,7 +5288,7 @@ function readSkillBody(skillPath, maxChars = 12e3) {
5059
5288
  \u2026(truncated)` : body;
5060
5289
  }
5061
5290
  }
5062
- const raw = readFileSync9(skillPath, "utf8");
5291
+ const raw = readFileSync10(skillPath, "utf8");
5063
5292
  if (raw.startsWith("---")) {
5064
5293
  const end = raw.indexOf("\n---", 3);
5065
5294
  if (end >= 0) {
@@ -5152,26 +5381,26 @@ function expandComposerPrompt(worktreePath, prompt, opts) {
5152
5381
  }
5153
5382
 
5154
5383
  // src/agents/instructions.ts
5155
- import { existsSync as existsSync14, readFileSync as readFileSync10, statSync as statSync4 } from "fs";
5156
- import { join as join13 } from "path";
5384
+ import { existsSync as existsSync15, readFileSync as readFileSync11, statSync as statSync4 } from "fs";
5385
+ import { join as join14 } from "path";
5157
5386
 
5158
5387
  // src/skills/detached-job-path.ts
5159
- import { existsSync as existsSync13 } from "fs";
5160
- import { dirname as dirname4, join as join12 } from "path";
5388
+ import { existsSync as existsSync14 } from "fs";
5389
+ import { dirname as dirname4, join as join13 } from "path";
5161
5390
  import { fileURLToPath } from "url";
5162
5391
  function packagedDetachedJobPath() {
5163
5392
  const dir = packagedMcpDir();
5164
5393
  if (!dir) return null;
5165
- const script = join12(dir, "scripts", "detached-job.js");
5166
- return existsSync13(script) ? script : null;
5394
+ const script = join13(dir, "scripts", "detached-job.js");
5395
+ return existsSync14(script) ? script : null;
5167
5396
  }
5168
5397
  function resolveDetachedJobScript() {
5169
5398
  const packaged = packagedDetachedJobPath();
5170
5399
  if (packaged) return packaged;
5171
5400
  let dir = dirname4(fileURLToPath(import.meta.url));
5172
5401
  for (let i = 0; i < 8; i++) {
5173
- const candidate = join12(dir, "scripts", "detached-job.js");
5174
- if (existsSync13(candidate)) return candidate;
5402
+ const candidate = join13(dir, "scripts", "detached-job.js");
5403
+ if (existsSync14(candidate)) return candidate;
5175
5404
  const parent = dirname4(dir);
5176
5405
  if (parent === dir) break;
5177
5406
  dir = parent;
@@ -5262,7 +5491,7 @@ function formatWorktreeDirective(thread, opts) {
5262
5491
  "Short git requests from the Sideboard UI are complete instructions \u2014 expand them using the rules above without asking for clarification:"
5263
5492
  );
5264
5493
  lines.push(
5265
- '- "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).'
5494
+ '- "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.'
5266
5495
  );
5267
5496
  lines.push(
5268
5497
  '- "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).'
@@ -5271,7 +5500,7 @@ function formatWorktreeDirective(thread, opts) {
5271
5500
  '- "Commit, push, and open a PR in the browser." \u2192 commit, push, then `gh pr create --web -R \u2026`.'
5272
5501
  );
5273
5502
  lines.push(
5274
- '- "Fix CI: <name>." \u2192 investigate that failing check, fix it, commit, and push.'
5503
+ '- "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.'
5275
5504
  );
5276
5505
  lines.push(
5277
5506
  '- "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.'
@@ -5283,11 +5512,21 @@ function formatWorktreeDirective(thread, opts) {
5283
5512
  '- "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.'
5284
5513
  );
5285
5514
  lines.push("");
5515
+ lines.push(formatPrGateDirective());
5516
+ lines.push("");
5286
5517
  lines.push(formatProcessGuideDirective());
5287
5518
  return lines.join("\n");
5288
5519
  }
5289
5520
  function formatWorktreeReminder() {
5290
- 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.";
5521
+ 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.";
5522
+ }
5523
+ function formatPrGateDirective() {
5524
+ return [
5525
+ "If a goal is given (not after every push):",
5526
+ "- 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.",
5527
+ "- Wait with `/long-running`: `gh pr checks --watch` (add `-R <origin>` when needed). Present the log. Do not ask the human to poll.",
5528
+ "- 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."
5529
+ ].join("\n");
5291
5530
  }
5292
5531
  function formatProcessGuideDirective() {
5293
5532
  return [
@@ -5295,7 +5534,7 @@ function formatProcessGuideDirective() {
5295
5534
  "- 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.",
5296
5535
  "- 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.",
5297
5536
  "- 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.",
5298
- "- 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).",
5537
+ "- 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.",
5299
5538
  "- 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.",
5300
5539
  "- 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."
5301
5540
  ].join("\n");
@@ -5366,11 +5605,11 @@ function loadAgentInstructions(worktreePath, agent) {
5366
5605
  const out = [];
5367
5606
  for (const rel of candidates) {
5368
5607
  if (seenPaths.has(rel)) continue;
5369
- const abs = join13(worktreePath, rel);
5370
- if (!existsSync14(abs)) continue;
5608
+ const abs = join14(worktreePath, rel);
5609
+ if (!existsSync15(abs)) continue;
5371
5610
  try {
5372
5611
  if (!statSync4(abs).isFile()) continue;
5373
- let content = readFileSync10(abs, "utf8");
5612
+ let content = readFileSync11(abs, "utf8");
5374
5613
  if (!content.trim()) continue;
5375
5614
  if (content.length > MAX_CHARS_PER_FILE) {
5376
5615
  content = `${content.slice(0, MAX_CHARS_PER_FILE)}
@@ -5690,8 +5929,8 @@ async function syncThreadBranchFromGit(threadId) {
5690
5929
 
5691
5930
  // src/store/schedules.ts
5692
5931
  import { randomUUID as randomUUID4 } from "crypto";
5693
- import { existsSync as existsSync15, readFileSync as readFileSync11 } from "fs";
5694
- import { join as join14 } from "path";
5932
+ import { existsSync as existsSync16, readFileSync as readFileSync12 } from "fs";
5933
+ import { join as join15 } from "path";
5695
5934
  import { Cron } from "croner";
5696
5935
  var DURATION_RE = /^(\d+)(s|m|h|d)$/i;
5697
5936
  var UNIT_MS = {
@@ -5701,7 +5940,7 @@ var UNIT_MS = {
5701
5940
  d: 864e5
5702
5941
  };
5703
5942
  function schedulesPath() {
5704
- return join14(appDataDir(), "schedules.json");
5943
+ return join15(appDataDir(), "schedules.json");
5705
5944
  }
5706
5945
  function parseDurationMs(every) {
5707
5946
  const m = every.trim().match(DURATION_RE);
@@ -5807,9 +6046,9 @@ function normalizeTask(raw) {
5807
6046
  }
5808
6047
  function readAll() {
5809
6048
  const path = schedulesPath();
5810
- if (!existsSync15(path)) return [];
6049
+ if (!existsSync16(path)) return [];
5811
6050
  try {
5812
- const parsed = JSON.parse(readFileSync11(path, "utf8"));
6051
+ const parsed = JSON.parse(readFileSync12(path, "utf8"));
5813
6052
  if (!Array.isArray(parsed)) return [];
5814
6053
  return parsed.map((row) => {
5815
6054
  try {
@@ -5935,7 +6174,7 @@ function formatScheduledPrompt(name, prompt) {
5935
6174
  ${prompt}`;
5936
6175
  }
5937
6176
  async function defaultDeps() {
5938
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-DPTRBKEH.js");
6177
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-5PSHBLP6.js");
5939
6178
  const orch = getOrchestrator2();
5940
6179
  return {
5941
6180
  findThread: (id) => findThreadByRef(id),
@@ -6175,7 +6414,7 @@ var Orchestrator = class {
6175
6414
  }
6176
6415
  continue;
6177
6416
  }
6178
- if (!existsSync16(thread.worktreePath)) {
6417
+ if (!existsSync17(thread.worktreePath)) {
6179
6418
  setStatus(thread.id, "broken", "Worktree missing on disk");
6180
6419
  this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
6181
6420
  continue;
@@ -6194,7 +6433,7 @@ var Orchestrator = class {
6194
6433
  orphans: orphans.map((o) => ({ path: o.path, repoPath: o.repoPath }))
6195
6434
  });
6196
6435
  }
6197
- const { autoCleanupOrphansEnabled } = await import("./app-settings-3CHCNIMD.js");
6436
+ const { autoCleanupOrphansEnabled } = await import("./app-settings-H3INSR67.js");
6198
6437
  if (autoCleanupOrphansEnabled() && shouldRunWorktreeCleanup() && orphans.length > 0) {
6199
6438
  await cleanupOrphanWorktrees({ repoPaths });
6200
6439
  }
@@ -6434,7 +6673,7 @@ var Orchestrator = class {
6434
6673
  }
6435
6674
  await setup;
6436
6675
  if (skipSetup) return;
6437
- const { autoRunAfterSetupEnabled } = await import("./app-settings-3CHCNIMD.js");
6676
+ const { autoRunAfterSetupEnabled } = await import("./app-settings-H3INSR67.js");
6438
6677
  if (autoRunAfterSetupEnabled()) {
6439
6678
  try {
6440
6679
  await this.startDev(threadId);
@@ -6742,7 +6981,7 @@ var Orchestrator = class {
6742
6981
  }
6743
6982
  const isBrightsy = fresh.agent === "brightsy";
6744
6983
  const isOrchestration = isOrchestratorThread(fresh);
6745
- const { autoRenameBranchEnabled, getGithubGitAuthMode } = await import("./app-settings-3CHCNIMD.js");
6984
+ const { autoRenameBranchEnabled, getGithubGitAuthMode } = await import("./app-settings-H3INSR67.js");
6746
6985
  const gitAuthMode = getGithubGitAuthMode();
6747
6986
  const worktreeDirective = isBrightsy || isOrchestration ? null : formatWorktreeDirective(fresh, {
6748
6987
  githubSlug: await resolveGithubRepoSlug(fresh.worktreePath).catch(
@@ -6848,7 +7087,7 @@ var Orchestrator = class {
6848
7087
  if (!this.stoppedTurns.has(threadId) && this.requireThread(threadId).agent === "cursor" && exitCode !== 0 && !assistantText && parts.length === 0) {
6849
7088
  const sessionId = result.sessionId || this.requireThread(threadId).sessionId || "";
6850
7089
  if (sessionId) {
6851
- const { recoverFinishedCursorRun } = await import("./cursor-recover-QSLWEZNA.js");
7090
+ const { recoverFinishedCursorRun } = await import("./cursor-recover-NDMLLNYX.js");
6852
7091
  for (let i = 0; i < 8; i++) {
6853
7092
  const recovered = recoverFinishedCursorRun({
6854
7093
  agentId: sessionId,
@@ -7238,6 +7477,15 @@ var Orchestrator = class {
7238
7477
  getActiveRuns(threadRef) {
7239
7478
  return this.requireThread(threadRef).activeRuns ?? [];
7240
7479
  }
7480
+ getSetupLog(threadRef) {
7481
+ const thread = this.getThread(threadRef);
7482
+ if (!thread) return readSetupLog(threadRef);
7483
+ const snap = readSetupLog(thread.id);
7484
+ if (snap.running && !this.processes.has(`${thread.id}:setup`)) {
7485
+ return { ...snap, running: false };
7486
+ }
7487
+ return snap;
7488
+ }
7241
7489
  async runSetup(threadRef) {
7242
7490
  const thread = this.requireThread(threadRef);
7243
7491
  this.assertNotGlobal(thread, "Setup");
@@ -7251,20 +7499,31 @@ var Orchestrator = class {
7251
7499
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
7252
7500
  kill: () => abort.abort()
7253
7501
  });
7502
+ beginSetupLog(thread.id);
7254
7503
  this.emit({ type: "setup_started", threadId: thread.id });
7504
+ let finished = false;
7505
+ const finish = (exitCode, source) => {
7506
+ if (finished) return;
7507
+ finished = true;
7508
+ finishSetupLog(thread.id, exitCode, source);
7509
+ this.emit({ type: "setup_finished", threadId: thread.id, exitCode });
7510
+ };
7255
7511
  try {
7256
7512
  const setup = await runWorkspaceSetup(
7257
7513
  thread.repoPath,
7258
7514
  thread.worktreePath,
7259
7515
  (line) => {
7516
+ appendSetupLog(thread.id, line);
7260
7517
  this.emit({ type: "setup_output", threadId: thread.id, line });
7261
7518
  },
7262
7519
  { signal: abort.signal }
7263
7520
  );
7264
7521
  if (!setup.ran) {
7265
- throw new Error(
7266
- "No setup script in .sideboard/settings.toml, .conductor/settings.toml, .cursor/worktrees.json, or script/setup (bin/setup, scripts/setup)"
7267
- );
7522
+ const line = "No setup script in .sideboard/settings.toml, .conductor/settings.toml, .cursor/worktrees.json, or script/setup (bin/setup, scripts/setup)";
7523
+ appendSetupLog(thread.id, line);
7524
+ this.emit({ type: "setup_output", threadId: thread.id, line });
7525
+ finish(null, null);
7526
+ throw new Error(line);
7268
7527
  }
7269
7528
  if (setup.exitCode !== 0 && setup.exitCode !== null) {
7270
7529
  const live = readThread(thread.id);
@@ -7277,8 +7536,11 @@ var Orchestrator = class {
7277
7536
  });
7278
7537
  }
7279
7538
  }
7280
- this.emit({ type: "setup_finished", threadId: thread.id, exitCode: setup.exitCode });
7539
+ finish(setup.exitCode, setup.source);
7281
7540
  return { exitCode: setup.exitCode, source: setup.source };
7541
+ } catch (err) {
7542
+ finish(null, null);
7543
+ throw err;
7282
7544
  } finally {
7283
7545
  this.processes.delete(key);
7284
7546
  }
@@ -7605,7 +7867,7 @@ var Orchestrator = class {
7605
7867
  updateThread(thread.id, { title: meta.title });
7606
7868
  }
7607
7869
  }
7608
- const { autoArchiveOnMergeEnabled } = await import("./app-settings-3CHCNIMD.js");
7870
+ const { autoArchiveOnMergeEnabled } = await import("./app-settings-H3INSR67.js");
7609
7871
  const latest = this.requireThread(thread.id);
7610
7872
  if (!shouldAutoArchiveOnPrMerge({
7611
7873
  previousPrState: prevState || null,
@@ -7922,7 +8184,7 @@ var Orchestrator = class {
7922
8184
  this.emit({ type: "status_changed", threadId: archived.id, status: "archived" });
7923
8185
  if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
7924
8186
  try {
7925
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-ZXCUVCZC.js");
8187
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-5UNDTN4G.js");
7926
8188
  await ensureWorkspace2(thread.repoPath);
7927
8189
  } catch {
7928
8190
  }
@@ -7949,7 +8211,7 @@ var Orchestrator = class {
7949
8211
  await runArchiveScript(thread.repoPath, thread.worktreePath);
7950
8212
  } catch {
7951
8213
  }
7952
- const { deleteBranchOnPurgeEnabled } = await import("./app-settings-3CHCNIMD.js");
8214
+ const { deleteBranchOnPurgeEnabled } = await import("./app-settings-H3INSR67.js");
7953
8215
  const deleteBranch = opts?.deleteBranch ?? deleteBranchOnPurgeEnabled();
7954
8216
  await removeWorktree(thread.repoPath, thread.worktreePath, {
7955
8217
  deleteBranch: deleteBranch ? thread.branchName : void 0
@@ -7964,19 +8226,19 @@ var Orchestrator = class {
7964
8226
  throw new Error("Thread is not archived");
7965
8227
  }
7966
8228
  if (isGlobalThread(thread)) {
7967
- const { globalAgentCwd } = await import("./paths-UMEDHOS5.js");
8229
+ const { globalAgentCwd } = await import("./paths-53TLA7VQ.js");
7968
8230
  updateThread(thread.id, { worktreePath: globalAgentCwd() });
7969
8231
  const restored2 = setStatus(thread.id, "idle");
7970
8232
  this.emit({ type: "status_changed", threadId: restored2.id, status: restored2.status });
7971
8233
  return restored2;
7972
8234
  }
7973
- if (!existsSync16(thread.worktreePath)) {
8235
+ if (!existsSync17(thread.worktreePath)) {
7974
8236
  if (isCowboyThread(thread) || isPrimaryCheckoutThread(thread)) {
7975
8237
  throw new Error(
7976
8238
  `Cowboy checkout missing: ${thread.worktreePath}. Re-add the project folder, then restore.`
7977
8239
  );
7978
8240
  }
7979
- const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-DPIREDFR.js");
8241
+ const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-T4SWFSTT.js");
7980
8242
  const slug = thread.worktreePath.split("/").pop();
7981
8243
  const dest = thread.worktreePath;
7982
8244
  await withRepoGitLock(thread.repoPath, async () => {
@@ -8086,7 +8348,7 @@ async function startOrchestration(opts) {
8086
8348
  sourceRef: "default",
8087
8349
  ...createOpts
8088
8350
  }).catch(async () => {
8089
- const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-DPIREDFR.js");
8351
+ const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-T4SWFSTT.js");
8090
8352
  const repo = await resolveRepoRoot2(repoPath);
8091
8353
  const def = await resolveDefaultBranch2(repo);
8092
8354
  return orch.createThread({
@@ -8096,7 +8358,7 @@ async function startOrchestration(opts) {
8096
8358
  repoPath: repo
8097
8359
  });
8098
8360
  });
8099
- const { updateThread: upd } = await import("./thread-store-WGASXXXR.js");
8361
+ const { updateThread: upd } = await import("./thread-store-KBFBHT5G.js");
8100
8362
  const updated = upd(thread.id, {
8101
8363
  sourceType: "orchestration",
8102
8364
  sourceRef: goal
@@ -8108,6 +8370,9 @@ async function startOrchestration(opts) {
8108
8370
  }
8109
8371
 
8110
8372
  export {
8373
+ emptySetupLog,
8374
+ mergeSetupOutput,
8375
+ readSetupLog,
8111
8376
  schedulesPath,
8112
8377
  parseDurationMs,
8113
8378
  defaultScheduleName,
@@ -8194,11 +8459,13 @@ export {
8194
8459
  wrapReviewSkillMarkdown,
8195
8460
  REPO_REVIEW_PATH,
8196
8461
  REPO_REVIEW_NAME,
8462
+ CONTEXT_REVIEW_PATH,
8197
8463
  REVIEW_REQUEST_PATH,
8198
8464
  LEGACY_REVIEW_REQUEST_PATH,
8199
8465
  REVIEW_REQUEST_NAME,
8200
8466
  REVIEW_REQUEST_PREFILL,
8201
8467
  shouldRefreshReviewRequestTemplate,
8468
+ ensureReviewGuidelinesFile,
8202
8469
  ensureReviewSkillFile,
8203
8470
  resolveReviewGuidelines,
8204
8471
  ensureReviewRequestFile,
@@ -8240,6 +8507,7 @@ export {
8240
8507
  issueNeedsWorkspacePick,
8241
8508
  isAccountWideIssueSource,
8242
8509
  findLiveThreadForCreate,
8510
+ findLiveThreadForCreateSource,
8243
8511
  boardPinIdentity,
8244
8512
  findBoardPin,
8245
8513
  syncBoardPins,
@@ -8293,6 +8561,7 @@ export {
8293
8561
  formatRenameBranchDirective,
8294
8562
  formatWorktreeDirective,
8295
8563
  formatWorktreeReminder,
8564
+ formatPrGateDirective,
8296
8565
  formatProcessGuideDirective,
8297
8566
  formatLongRunningDirective,
8298
8567
  formatLongRunningReminder,