@sideboard-ai/core 0.1.298 → 0.1.300

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 (29) hide show
  1. package/dist/{agents-GHZFY65E.js → agents-IVKPCK56.js} +3 -3
  2. package/dist/{agents-XL67HSZH.js → agents-JCCJJMA7.js} +2 -2
  3. package/dist/{chunk-RQMLJZ2V.js → chunk-EB3F5AWV.js} +2 -2
  4. package/dist/{chunk-IL77XODJ.js → chunk-EB7B4D2Z.js} +3 -3
  5. package/dist/{chunk-JM5P36PM.js → chunk-F3JYJ4UB.js} +2 -2
  6. package/dist/{chunk-BIMHIHX3.js → chunk-G52HLBQR.js} +21 -1
  7. package/dist/{chunk-KRGQWQME.js → chunk-GNX6YNNU.js} +164 -107
  8. package/dist/{chunk-4FWMUMXG.js → chunk-MMIDAD6J.js} +3 -3
  9. package/dist/{chunk-KMYCNSSI.js → chunk-QB4UQA5L.js} +3 -3
  10. package/dist/{chunk-5FMQU2RU.js → chunk-UMALFEDS.js} +2 -2
  11. package/dist/{chunk-2POLTCSC.js → chunk-YAILSKW3.js} +61 -47
  12. package/dist/{conductor-NGIEDMAB.js → conductor-JLHXMMKR.js} +2 -2
  13. package/dist/{conductor-HUITLA2M.js → conductor-UQDDDW35.js} +3 -3
  14. package/dist/{coordinator-prompt-JUN3IITF.js → coordinator-prompt-HKLVS26O.js} +2 -2
  15. package/dist/{coordinator-prompt-52T5A4SU.js → coordinator-prompt-SSUYZSJO.js} +1 -1
  16. package/dist/{global-workspace-VVUIFQ72.js → global-workspace-I32EWKX3.js} +2 -2
  17. package/dist/{global-workspace-Z5YRL67Y.js → global-workspace-MHE6FCUZ.js} +1 -1
  18. package/dist/index.cjs +317 -222
  19. package/dist/index.d.cts +40 -26
  20. package/dist/index.d.ts +40 -26
  21. package/dist/index.js +11 -11
  22. package/dist/mcp/run-stdio.cjs +81 -43
  23. package/dist/mcp/run-stdio.js +7 -7
  24. package/dist/{orchestrator-S7TKI6DR.js → orchestrator-FVVDOQVD.js} +5 -5
  25. package/dist/{orchestrator-JY4ZETFX.js → orchestrator-SX4SGZHF.js} +4 -4
  26. package/dist/{workspaces-OSF56FHS.js → workspaces-ATMIKXLK.js} +2 -2
  27. package/dist/{workspaces-7FVNGUBV.js → workspaces-O3L32CDO.js} +1 -1
  28. package/dist/{worktree-LFSBIUCN.js → worktree-RMYXBTJG.js} +1 -1
  29. package/package.json +1 -1
@@ -37,7 +37,7 @@ import {
37
37
  planModeTurnInstruction,
38
38
  resolveCursorModelId,
39
39
  resolveQuotaFallbackAgent
40
- } from "./chunk-IL77XODJ.js";
40
+ } from "./chunk-EB7B4D2Z.js";
41
41
  import "./chunk-VGNLMGBK.js";
42
42
  import "./chunk-F36QRDM4.js";
43
43
  import {
@@ -52,8 +52,8 @@ import {
52
52
  resolveLoginCommand,
53
53
  whichOnPath
54
54
  } from "./chunk-B42H6GJR.js";
55
- import "./chunk-KMYCNSSI.js";
56
- import "./chunk-BIMHIHX3.js";
55
+ import "./chunk-QB4UQA5L.js";
56
+ import "./chunk-G52HLBQR.js";
57
57
  import "./chunk-K5EACGG2.js";
58
58
  import "./chunk-2P4XKWV2.js";
59
59
  import "./chunk-33KDPEK4.js";
@@ -46,7 +46,7 @@ import {
46
46
  resolveCursorSdkRoot,
47
47
  resolveQuotaFallbackAgent,
48
48
  turnCostUsdFromCursorUsage
49
- } from "./chunk-RQMLJZ2V.js";
49
+ } from "./chunk-EB3F5AWV.js";
50
50
  import {
51
51
  getAgentSetupInfo,
52
52
  installAgent,
@@ -63,7 +63,7 @@ import "./chunk-7IAMHN2N.js";
63
63
  import "./chunk-PXMLMBMI.js";
64
64
  import "./chunk-2GPQZZLY.js";
65
65
  import "./chunk-5XQ43YZU.js";
66
- import "./chunk-5FMQU2RU.js";
66
+ import "./chunk-UMALFEDS.js";
67
67
  import "./chunk-3IXGI2FP.js";
68
68
  import "./chunk-N2WW62LF.js";
69
69
  import "./chunk-DD67JWTN.js";
@@ -24,7 +24,7 @@ import {
24
24
  } from "./chunk-5XQ43YZU.js";
25
25
  import {
26
26
  isOrchestratorThread
27
- } from "./chunk-5FMQU2RU.js";
27
+ } from "./chunk-UMALFEDS.js";
28
28
  import {
29
29
  codexUnattendedGitConfigArgs,
30
30
  mergeAgentGitAuthEnv,
@@ -2020,7 +2020,7 @@ var claudeAdapter = {
2020
2020
  );
2021
2021
  }
2022
2022
  const mode = permissionMode(thread);
2023
- const { isOrchestratorThread: isOrchestratorThread2 } = await import("./global-workspace-Z5YRL67Y.js");
2023
+ const { isOrchestratorThread: isOrchestratorThread2 } = await import("./global-workspace-MHE6FCUZ.js");
2024
2024
  const isOrchestrator = isOrchestratorThread2(thread);
2025
2025
  const rpc = worktreeAgentRpc(isOrchestrator);
2026
2026
  const injectedServers = await buildInjectedMcpServers({
@@ -12,13 +12,13 @@ import {
12
12
  } from "./chunk-F36QRDM4.js";
13
13
  import {
14
14
  isOrchestratorThread
15
- } from "./chunk-KMYCNSSI.js";
15
+ } from "./chunk-QB4UQA5L.js";
16
16
  import {
17
17
  codexUnattendedGitConfigArgs,
18
18
  mergeAgentGitAuthEnv,
19
19
  resolveAgentGitAuthEnv,
20
20
  resolveCodexGitWritableRoots
21
- } from "./chunk-BIMHIHX3.js";
21
+ } from "./chunk-G52HLBQR.js";
22
22
  import {
23
23
  loadBrightsyConfig
24
24
  } from "./chunk-33KDPEK4.js";
@@ -1757,7 +1757,7 @@ var claudeAdapter = {
1757
1757
  );
1758
1758
  }
1759
1759
  const mode = permissionMode(thread);
1760
- const { isOrchestratorThread: isOrchestratorThread2 } = await import("./global-workspace-VVUIFQ72.js");
1760
+ const { isOrchestratorThread: isOrchestratorThread2 } = await import("./global-workspace-I32EWKX3.js");
1761
1761
  const isOrchestrator = isOrchestratorThread2(thread);
1762
1762
  const rpc = worktreeAgentRpc(isOrchestrator);
1763
1763
  const injectedServers = await buildInjectedMcpServers({
@@ -11,7 +11,7 @@ import {
11
11
  isGlobalThread,
12
12
  isOrchestratorThread,
13
13
  isSlackCoordinatorThread
14
- } from "./chunk-5FMQU2RU.js";
14
+ } from "./chunk-UMALFEDS.js";
15
15
  import {
16
16
  mergeAgentGitAuthEnv,
17
17
  resolveAgentGitAuthEnv
@@ -1674,7 +1674,7 @@ var PORT_RANGE_SIZE = 10;
1674
1674
  var RECLAIM_RETRY_DELAY_MS = 40;
1675
1675
  var RECLAIM_TRIES = 8;
1676
1676
  async function killReclaimable(ports, worktreePath, signal) {
1677
- const { killListenersOnPorts: killListenersOnPorts2, killStaleWorktreeListeners: killStaleWorktreeListeners2 } = await import("./conductor-NGIEDMAB.js");
1677
+ const { killListenersOnPorts: killListenersOnPorts2, killStaleWorktreeListeners: killStaleWorktreeListeners2 } = await import("./conductor-JLHXMMKR.js");
1678
1678
  if (worktreePath) killStaleWorktreeListeners2(ports, worktreePath, signal);
1679
1679
  else killListenersOnPorts2(ports);
1680
1680
  }
@@ -750,6 +750,24 @@ function applyCleanLocalMergeTree(gate) {
750
750
  mergeStateStatus: mergeState === "BEHIND" ? "BEHIND" : mergeState === "UNKNOWN" || !mergeState ? "CLEAN" : gate.mergeStateStatus
751
751
  };
752
752
  }
753
+ function classifyPrMergeIssue(gate) {
754
+ if (!gate) return null;
755
+ if (prIsInMergeQueue(gate)) return null;
756
+ const mergeable = (gate.mergeable ?? "").toUpperCase();
757
+ const mergeState = (gate.mergeStateStatus ?? "").toUpperCase();
758
+ if (mergeable === "CONFLICTING" || mergeState === "DIRTY") return "conflicts";
759
+ if (mergeState === "BEHIND") return "behind";
760
+ return null;
761
+ }
762
+ function mergeStateSuggestsCheckFailure(gate) {
763
+ if (!gate) return false;
764
+ const mergeState = (gate.mergeStateStatus ?? "").toUpperCase();
765
+ if (mergeState === "UNSTABLE") return true;
766
+ if (mergeState === "BLOCKED") {
767
+ return (gate.reviewDecision ?? "").toUpperCase() === "APPROVED";
768
+ }
769
+ return false;
770
+ }
753
771
  function ciChecksFailed(checks) {
754
772
  if (!checks?.length) return false;
755
773
  return checks.some(
@@ -2584,6 +2602,9 @@ function suggestSlug(source) {
2584
2602
  }
2585
2603
 
2586
2604
  export {
2605
+ classifyPrMergeIssue,
2606
+ mergeStateSuggestsCheckFailure,
2607
+ ciChecksFailed,
2587
2608
  GITHUB_PR_BODY_MAX_CHARS,
2588
2609
  GITHUB_PR_BODY_SAFE_CHARS,
2589
2610
  isGhRateLimitError,
@@ -2613,7 +2634,6 @@ export {
2613
2634
  resolveListPrsOptions,
2614
2635
  enqueueByKey,
2615
2636
  withRepoGitLock,
2616
- ciChecksFailed,
2617
2637
  detectGhStack,
2618
2638
  resetGhStackDetectCache,
2619
2639
  parseGhStackViewJson,
@@ -14,7 +14,7 @@ import {
14
14
  parseSessionQuotaResetAt,
15
15
  planModeTurnInstruction,
16
16
  resolveQuotaFallbackAgent
17
- } from "./chunk-IL77XODJ.js";
17
+ } from "./chunk-EB7B4D2Z.js";
18
18
  import {
19
19
  REVIEW_TAB_TITLE,
20
20
  applyWorktreePkgCacheEnv,
@@ -53,7 +53,7 @@ import {
53
53
  worktreeDevProcessKey,
54
54
  worktreeRunProcessKey,
55
55
  worktreeSetupProcessKey
56
- } from "./chunk-4FWMUMXG.js";
56
+ } from "./chunk-MMIDAD6J.js";
57
57
  import {
58
58
  applyAgentEvent,
59
59
  clipAgentEventForPaint,
@@ -100,12 +100,13 @@ import {
100
100
  stageAbsolutePathsAsAttachments,
101
101
  stageBuffersAsAttachments,
102
102
  syncWorkspacesFromThreads
103
- } from "./chunk-KMYCNSSI.js";
103
+ } from "./chunk-QB4UQA5L.js";
104
104
  import {
105
105
  addPrStackLayer,
106
106
  allocateTeamSlug,
107
107
  canonicalizeRepoPath,
108
108
  ciChecksFailed,
109
+ classifyPrMergeIssue,
109
110
  commitAll,
110
111
  connectedPrSelectors,
111
112
  createExistingBranchWorktree,
@@ -132,6 +133,7 @@ import {
132
133
  markPrReady,
133
134
  mergeAgentGitAuthEnv,
134
135
  mergePr,
136
+ mergeStateSuggestsCheckFailure,
135
137
  pushBranch,
136
138
  removeWorktree,
137
139
  resolveAgentGitAuthEnv,
@@ -142,7 +144,7 @@ import {
142
144
  resolveRepoRoot,
143
145
  switchWorktreeBranch,
144
146
  withRepoGitLock
145
- } from "./chunk-BIMHIHX3.js";
147
+ } from "./chunk-G52HLBQR.js";
146
148
  import {
147
149
  agentNicknameFromWorktreePath,
148
150
  appendMessage,
@@ -451,9 +453,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
451
453
  `Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
452
454
  );
453
455
  }
454
- const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-VVUIFQ72.js");
456
+ const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-I32EWKX3.js");
455
457
  if (isGlobalThread2(thread)) {
456
- const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-JUN3IITF.js");
458
+ const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-HKLVS26O.js");
457
459
  ensureGlobalCoordinatorCwd2(
458
460
  isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
459
461
  );
@@ -2001,20 +2003,7 @@ function threadCardBlockFields(group) {
2001
2003
  return block ? { blockedReason: block.reason, blockedSource: block.source } : null;
2002
2004
  }
2003
2005
 
2004
- // src/board/home-board.ts
2005
- var GLOBAL_WORKSPACE_ID = "__global__";
2006
- function isHomeBoardThread(thread) {
2007
- if (thread.sourceType === "orchestration") return false;
2008
- if (thread.repoPath === GLOBAL_WORKSPACE_ID) return false;
2009
- return true;
2010
- }
2011
- var BOARD_COLUMN_DEFS = [
2012
- { id: "new", title: "New" },
2013
- { id: "draft", title: "Draft" },
2014
- { id: "review", title: "Review" },
2015
- { id: "done", title: "Merged" }
2016
- ];
2017
- var HOME_BOARD_CACHE_TTL_MS = 15 * 60 * 1e3;
2006
+ // src/board/pr-state.ts
2018
2007
  function isOpenPrState(prUrl, prState) {
2019
2008
  if (!prUrl?.trim()) return false;
2020
2009
  const state = (prState ?? "OPEN").trim().toUpperCase();
@@ -2024,58 +2013,12 @@ function isMergedPrState(prUrl, prState) {
2024
2013
  if (!prUrl?.trim()) return false;
2025
2014
  return (prState ?? "").trim().toUpperCase() === "MERGED";
2026
2015
  }
2027
- var DEFAULT_WORKTREE_SORT = "created";
2028
- function groupCreatedAt(group) {
2029
- let min = group[0]?.createdAt ?? "";
2030
- for (const t of group) {
2031
- if (t.createdAt && t.createdAt < min) min = t.createdAt;
2032
- }
2033
- return min;
2034
- }
2035
- function groupUpdatedAt(group) {
2036
- let max = group[0]?.updatedAt ?? "";
2037
- for (const t of group) {
2038
- if (t.updatedAt && t.updatedAt > max) max = t.updatedAt;
2039
- }
2040
- return max;
2041
- }
2042
- function sortThreadsInWorktree(list) {
2043
- return [...list].sort((a, b) => {
2044
- const created = a.createdAt.localeCompare(b.createdAt);
2045
- if (created !== 0) return created;
2046
- return a.id.localeCompare(b.id);
2047
- });
2048
- }
2049
- function compareWorktreeGroups(a, b, mode, labelOf) {
2050
- if (mode === "name") {
2051
- const named = (labelOf?.(a) ?? a[0]?.id ?? "").localeCompare(
2052
- labelOf?.(b) ?? b[0]?.id ?? "",
2053
- void 0,
2054
- { sensitivity: "base" }
2055
- );
2056
- if (named !== 0) return named;
2057
- } else if (mode === "activity") {
2058
- const activity = groupUpdatedAt(b).localeCompare(groupUpdatedAt(a));
2059
- if (activity !== 0) return activity;
2060
- } else {
2061
- const created = groupCreatedAt(b).localeCompare(groupCreatedAt(a));
2062
- if (created !== 0) return created;
2063
- }
2064
- return (a[0]?.id ?? "").localeCompare(b[0]?.id ?? "");
2065
- }
2066
- function defaultWorktreeGroupLabel(group) {
2067
- return worktreeDisplayLabelForGroup(group);
2068
- }
2069
- function groupHomeBoardWorktrees(threads, sort = DEFAULT_WORKTREE_SORT, labelOf = defaultWorktreeGroupLabel) {
2070
- const map = /* @__PURE__ */ new Map();
2071
- for (const t of threads) {
2072
- const key = normalizeWorktreePath(t.worktreePath);
2073
- const list = map.get(key) ?? [];
2074
- list.push(t);
2075
- map.set(key, list);
2076
- }
2077
- return [...map.values()].map((list) => sortThreadsInWorktree(list)).sort((a, b) => compareWorktreeGroups(a, b, sort, labelOf));
2016
+ function isApprovedOpenPr(thread) {
2017
+ if (!isOpenPrState(thread.prUrl, thread.prState) || thread.prIsDraft) return false;
2018
+ return (thread.prReviewDecision ?? "").trim().toUpperCase() === "APPROVED";
2078
2019
  }
2020
+
2021
+ // src/board/worktree-ownership.ts
2079
2022
  var DEFAULT_BOARD_OWNERSHIP = "all";
2080
2023
  function normalizeViewerLogin(login) {
2081
2024
  return (login ?? "").trim().toLowerCase();
@@ -2106,16 +2049,8 @@ function worktreeMatchesOwnership(group, filter, viewerLogin = "") {
2106
2049
  if (filter === "all") return true;
2107
2050
  return classifyWorktreeOwnership(group, viewerLogin) === filter;
2108
2051
  }
2109
- function classifyWorktreeColumn(group) {
2110
- if (group.some((t) => isMergedPrState(t.prUrl, t.prState))) return "done";
2111
- if (group.some((t) => isOpenPrState(t.prUrl, t.prState) && !t.prIsDraft)) {
2112
- return "review";
2113
- }
2114
- if (group.some((t) => isOpenPrState(t.prUrl, t.prState) && t.prIsDraft)) {
2115
- return "draft";
2116
- }
2117
- return "new";
2118
- }
2052
+
2053
+ // src/board/worktree-list-badges.ts
2119
2054
  function labelsInclude(labels, name) {
2120
2055
  const want = name.trim().toLowerCase();
2121
2056
  if (!want) return false;
@@ -2154,34 +2089,152 @@ function groupPrLabels(group) {
2154
2089
  }
2155
2090
  return out;
2156
2091
  }
2092
+ function isClosedPrState(prUrl, prState) {
2093
+ if (!prUrl?.trim()) return false;
2094
+ return (prState ?? "").trim().toUpperCase() === "CLOSED";
2095
+ }
2096
+ function listBadge(id, title, note) {
2097
+ return {
2098
+ id,
2099
+ label: id,
2100
+ title: note ? `${title} \xB7 ${note}` : title,
2101
+ mod: `is-${id}`,
2102
+ ...note ? { conflict: true } : {}
2103
+ };
2104
+ }
2105
+ function hasMergeConflict(thread) {
2106
+ if (!isOpenPrState(thread.prUrl, thread.prState)) return false;
2107
+ return classifyPrMergeIssue({
2108
+ mergeable: thread.prMergeable ?? null,
2109
+ mergeStateStatus: thread.prMergeStateStatus ?? null,
2110
+ isInMergeQueue: thread.prIsInMergeQueue
2111
+ }) === "conflicts";
2112
+ }
2113
+ function blockerNote(group) {
2114
+ const open = group.filter(
2115
+ (thread) => isOpenPrState(thread.prUrl, thread.prState) && !thread.prIsInMergeQueue
2116
+ );
2117
+ const parts = [];
2118
+ if (open.some(
2119
+ (thread) => thread.prChecksFailed || mergeStateSuggestsCheckFailure({
2120
+ mergeStateStatus: thread.prMergeStateStatus ?? null,
2121
+ reviewDecision: thread.prReviewDecision ?? null
2122
+ })
2123
+ )) {
2124
+ parts.push("checks failing");
2125
+ } else if (open.some(
2126
+ (thread) => !thread.prIsDraft && (thread.prReviewDecision ?? "").trim().toUpperCase() === "CHANGES_REQUESTED"
2127
+ )) {
2128
+ parts.push("changes requested");
2129
+ }
2130
+ if (open.some(hasMergeConflict)) parts.push("merge conflicts");
2131
+ return parts.length > 0 ? parts.join(" \xB7 ") : void 0;
2132
+ }
2157
2133
  function classifyWorktreeListBadges(group, viewerLogin = "", reviewLabel = "") {
2158
2134
  if (group.length === 0 || group.some((t) => t.cowboy)) return [];
2135
+ if (group.some((t) => isMergedPrState(t.prUrl, t.prState))) {
2136
+ return [listBadge("merged", "Merged")];
2137
+ }
2138
+ const note = blockerNote(group);
2139
+ if (group.some((t) => isApprovedOpenPr(t))) {
2140
+ return [listBadge("approved", "Approved, not merged", note)];
2141
+ }
2159
2142
  const open = group.filter((t) => isOpenPrState(t.prUrl, t.prState));
2160
- if (open.length === 0) return [];
2143
+ if (open.length === 0) {
2144
+ if (group.some((t) => isClosedPrState(t.prUrl, t.prState))) return [];
2145
+ return [listBadge("new", "No pull request")];
2146
+ }
2161
2147
  const ownership = classifyWorktreeOwnership(group, viewerLogin);
2162
2148
  if (ownership === "reviewing") {
2163
- return [
2164
- {
2165
- id: "review",
2166
- label: "review",
2167
- title: "Someone else's PR",
2168
- mod: "is-review"
2169
- }
2170
- ];
2149
+ return [listBadge("review", "Someone else's PR", note)];
2171
2150
  }
2172
2151
  if (!normalizeViewerLogin(viewerLogin)) return [];
2173
- const draft = open.some((t) => t.prIsDraft);
2152
+ if (!open.some((t) => !t.prIsDraft)) {
2153
+ return [listBadge("draft", "Draft pull request", note)];
2154
+ }
2174
2155
  const readyLabel = reviewLabel.trim();
2175
- const inReview = readyLabel ? labelsInclude(groupPrLabels(group), readyLabel) : !draft;
2156
+ const inReview = readyLabel ? labelsInclude(groupPrLabels(group), readyLabel) : true;
2176
2157
  if (!inReview) return [];
2177
- return [
2178
- {
2179
- id: "pending",
2180
- label: "pending",
2181
- title: "Waiting for review",
2182
- mod: "is-pending"
2183
- }
2184
- ];
2158
+ return [listBadge("pending", "Waiting for review", note)];
2159
+ }
2160
+
2161
+ // src/board/home-board.ts
2162
+ var GLOBAL_WORKSPACE_ID = "__global__";
2163
+ function isHomeBoardThread(thread) {
2164
+ if (thread.sourceType === "orchestration") return false;
2165
+ if (thread.repoPath === GLOBAL_WORKSPACE_ID) return false;
2166
+ return true;
2167
+ }
2168
+ var BOARD_COLUMN_DEFS = [
2169
+ { id: "new", title: "New" },
2170
+ { id: "draft", title: "Draft" },
2171
+ { id: "review", title: "Review" },
2172
+ { id: "approved", title: "Approved" },
2173
+ { id: "done", title: "Merged" }
2174
+ ];
2175
+ var HOME_BOARD_CACHE_TTL_MS = 15 * 60 * 1e3;
2176
+ var DEFAULT_WORKTREE_SORT = "created";
2177
+ function groupCreatedAt(group) {
2178
+ let min = group[0]?.createdAt ?? "";
2179
+ for (const t of group) {
2180
+ if (t.createdAt && t.createdAt < min) min = t.createdAt;
2181
+ }
2182
+ return min;
2183
+ }
2184
+ function groupUpdatedAt(group) {
2185
+ let max = group[0]?.updatedAt ?? "";
2186
+ for (const t of group) {
2187
+ if (t.updatedAt && t.updatedAt > max) max = t.updatedAt;
2188
+ }
2189
+ return max;
2190
+ }
2191
+ function sortThreadsInWorktree(list) {
2192
+ return [...list].sort((a, b) => {
2193
+ const created = a.createdAt.localeCompare(b.createdAt);
2194
+ if (created !== 0) return created;
2195
+ return a.id.localeCompare(b.id);
2196
+ });
2197
+ }
2198
+ function compareWorktreeGroups(a, b, mode, labelOf) {
2199
+ if (mode === "name") {
2200
+ const named = (labelOf?.(a) ?? a[0]?.id ?? "").localeCompare(
2201
+ labelOf?.(b) ?? b[0]?.id ?? "",
2202
+ void 0,
2203
+ { sensitivity: "base" }
2204
+ );
2205
+ if (named !== 0) return named;
2206
+ } else if (mode === "activity") {
2207
+ const activity = groupUpdatedAt(b).localeCompare(groupUpdatedAt(a));
2208
+ if (activity !== 0) return activity;
2209
+ } else {
2210
+ const created = groupCreatedAt(b).localeCompare(groupCreatedAt(a));
2211
+ if (created !== 0) return created;
2212
+ }
2213
+ return (a[0]?.id ?? "").localeCompare(b[0]?.id ?? "");
2214
+ }
2215
+ function defaultWorktreeGroupLabel(group) {
2216
+ return worktreeDisplayLabelForGroup(group);
2217
+ }
2218
+ function groupHomeBoardWorktrees(threads, sort = DEFAULT_WORKTREE_SORT, labelOf = defaultWorktreeGroupLabel) {
2219
+ const map = /* @__PURE__ */ new Map();
2220
+ for (const t of threads) {
2221
+ const key = normalizeWorktreePath(t.worktreePath);
2222
+ const list = map.get(key) ?? [];
2223
+ list.push(t);
2224
+ map.set(key, list);
2225
+ }
2226
+ return [...map.values()].map((list) => sortThreadsInWorktree(list)).sort((a, b) => compareWorktreeGroups(a, b, sort, labelOf));
2227
+ }
2228
+ function classifyWorktreeColumn(group) {
2229
+ if (group.some((t) => isMergedPrState(t.prUrl, t.prState))) return "done";
2230
+ if (group.some((t) => isApprovedOpenPr(t))) return "approved";
2231
+ if (group.some((t) => isOpenPrState(t.prUrl, t.prState) && !t.prIsDraft)) {
2232
+ return "review";
2233
+ }
2234
+ if (group.some((t) => isOpenPrState(t.prUrl, t.prState) && t.prIsDraft)) {
2235
+ return "draft";
2236
+ }
2237
+ return "new";
2185
2238
  }
2186
2239
  function normalizeIssueKey(value) {
2187
2240
  return value.trim().toLowerCase().replace(/^#/, "");
@@ -2491,6 +2544,7 @@ function emptyColumns() {
2491
2544
  new: [],
2492
2545
  draft: [],
2493
2546
  review: [],
2547
+ approved: [],
2494
2548
  done: []
2495
2549
  };
2496
2550
  }
@@ -2502,6 +2556,7 @@ function emptyHidden() {
2502
2556
  new: 0,
2503
2557
  draft: 0,
2504
2558
  review: 0,
2559
+ approved: 0,
2505
2560
  done: 0
2506
2561
  };
2507
2562
  }
@@ -2570,6 +2625,7 @@ function assembleHomeBoard(input) {
2570
2625
  new: 0,
2571
2626
  draft: 0,
2572
2627
  review: 0,
2628
+ approved: 0,
2573
2629
  done: 0,
2574
2630
  tickets: 0,
2575
2631
  prs: 0,
@@ -2582,6 +2638,7 @@ function assembleHomeBoard(input) {
2582
2638
  new: [],
2583
2639
  draft: [],
2584
2640
  review: [],
2641
+ approved: [],
2585
2642
  done: []
2586
2643
  };
2587
2644
  for (const group of groupHomeBoardWorktrees(live)) {
@@ -2616,7 +2673,7 @@ function assembleHomeBoard(input) {
2616
2673
  }
2617
2674
  return { columns, hidden, totals };
2618
2675
  }
2619
- var HOME_BOARD_AGENT_HINT = "Home is a Kanban of workspaces (one card per checkout; sibling chats on a card are separate agents). Do not create a second workspace for a ticket, PR, or named branch that already has a live checkout \u2014 create_workspace / start_board_card return that checkout (alreadyStarted) and its chats. Add another agent with fork_chat. Creating from the default branch still opens a new isolated workspace. Columns are the path to merge: New (no PR) \u2192 Draft (draft PR) \u2192 Review (open PR) \u2192 Merged. ownership=mine is PRs you authored (or WIP with no PR); ownership=reviewing is someone else's PR (or a review-requested checkout). Viewer login comes from gh, not a setting. Archive removes the card to Settings \u2192 History (list_archive to browse, restore_workspace to bring it back). Queued/running are activity on the card, not columns. blockedReason means an agent on that card is waiting on a person (the question, plan approval, or a reported block) \u2014 read it and do not send_to_chat a check-in. Orchestration chats stay in the sidebar. Do not invent status.";
2676
+ var HOME_BOARD_AGENT_HINT = "Home is a Kanban of workspaces (one card per checkout; sibling chats on a card are separate agents). Do not create a second workspace for a ticket, PR, or named branch that already has a live checkout \u2014 create_workspace / start_board_card return that checkout (alreadyStarted) and its chats. Add another agent with fork_chat. Creating from the default branch still opens a new isolated workspace. Columns are the path to merge: New (no PR) \u2192 Draft (draft PR) \u2192 Review (open PR) \u2192 Approved (approved, not merged) \u2192 Merged. ownership=mine is PRs you authored (or WIP with no PR); ownership=reviewing is someone else's PR (or a review-requested checkout). Viewer login comes from gh, not a setting. Archive removes the card to Settings \u2192 History (list_archive to browse, restore_workspace to bring it back). Queued/running are activity on the card, not columns. blockedReason means an agent on that card is waiting on a person (the question, plan approval, or a reported block) \u2014 read it and do not send_to_chat a check-in. Orchestration chats stay in the sidebar. Do not invent status.";
2620
2677
 
2621
2678
  // src/threads/workspace-tags.ts
2622
2679
  function workspaceTagTargetRef(opts) {
@@ -2892,7 +2949,7 @@ async function createThread(input, _onSetupLine) {
2892
2949
  return readThread(thread.id) ?? thread;
2893
2950
  }
2894
2951
  async function listLinearIssues(agent, repoPath) {
2895
- const { getAdapter: getAdapter2 } = await import("./agents-GHZFY65E.js");
2952
+ const { getAdapter: getAdapter2 } = await import("./agents-IVKPCK56.js");
2896
2953
  await requireAgent(agent, { requireLinear: true });
2897
2954
  const adapter = getAdapter2(agent);
2898
2955
  if (!adapter.listLinearIssues) {
@@ -3668,7 +3725,7 @@ async function adoptThread(input) {
3668
3725
  messages: input.messages ?? []
3669
3726
  });
3670
3727
  writeThread(thread);
3671
- const { addWorkspace: addWorkspace2 } = await import("./workspaces-OSF56FHS.js");
3728
+ const { addWorkspace: addWorkspace2 } = await import("./workspaces-ATMIKXLK.js");
3672
3729
  await addWorkspace2(repoPath);
3673
3730
  const { ensureWorktreeSideboardIgnored } = await import("./worktree-exclude-RXZPTCNH.js");
3674
3731
  await ensureWorktreeSideboardIgnored(input.worktreePath);
@@ -6597,7 +6654,7 @@ function formatScheduledPrompt(name, prompt) {
6597
6654
  ${prompt}`;
6598
6655
  }
6599
6656
  async function defaultDeps() {
6600
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-S7TKI6DR.js");
6657
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-FVVDOQVD.js");
6601
6658
  const orch = getOrchestrator2();
6602
6659
  return {
6603
6660
  findThread: (id) => findThreadByRef(id),
@@ -9472,7 +9529,7 @@ async function startOrchestration(opts) {
9472
9529
  sourceRef: "default",
9473
9530
  ...createOpts
9474
9531
  }).catch(async () => {
9475
- const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-LFSBIUCN.js");
9532
+ const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-RMYXBTJG.js");
9476
9533
  const repo = await resolveRepoRoot2(repoPath);
9477
9534
  const def = await resolveDefaultBranch2(repo);
9478
9535
  return orch.createThread({
@@ -9502,14 +9559,14 @@ export {
9502
9559
  worktreeBoardStatus,
9503
9560
  visibleAgentBlock,
9504
9561
  worktreeAgentBlock,
9562
+ reviewLabelForRepo,
9563
+ classifyWorktreeListBadges,
9505
9564
  isHomeBoardThread,
9506
9565
  BOARD_COLUMN_DEFS,
9507
9566
  HOME_BOARD_CACHE_TTL_MS,
9508
9567
  DEFAULT_WORKTREE_SORT,
9509
9568
  groupHomeBoardWorktrees,
9510
9569
  classifyWorktreeColumn,
9511
- reviewLabelForRepo,
9512
- classifyWorktreeListBadges,
9513
9570
  dedupeBoardIssues,
9514
9571
  issueNeedsWorkspacePick,
9515
9572
  isAccountWideIssueSource,
@@ -8,11 +8,11 @@ import {
8
8
  isGlobalThread,
9
9
  isOrchestratorThread,
10
10
  isSlackCoordinatorThread
11
- } from "./chunk-KMYCNSSI.js";
11
+ } from "./chunk-QB4UQA5L.js";
12
12
  import {
13
13
  mergeAgentGitAuthEnv,
14
14
  resolveAgentGitAuthEnv
15
- } from "./chunk-BIMHIHX3.js";
15
+ } from "./chunk-G52HLBQR.js";
16
16
  import {
17
17
  allocateTeamName,
18
18
  createEmptyThread,
@@ -1776,7 +1776,7 @@ var PORT_RANGE_SIZE = 10;
1776
1776
  var RECLAIM_RETRY_DELAY_MS = 40;
1777
1777
  var RECLAIM_TRIES = 8;
1778
1778
  async function killReclaimable(ports, worktreePath, signal) {
1779
- const { killListenersOnPorts: killListenersOnPorts2, killStaleWorktreeListeners: killStaleWorktreeListeners2 } = await import("./conductor-HUITLA2M.js");
1779
+ const { killListenersOnPorts: killListenersOnPorts2, killStaleWorktreeListeners: killStaleWorktreeListeners2 } = await import("./conductor-UQDDDW35.js");
1780
1780
  if (worktreePath) killStaleWorktreeListeners2(ports, worktreePath, signal);
1781
1781
  else killListenersOnPorts2(ports);
1782
1782
  }
@@ -3,7 +3,7 @@ import {
3
3
  ensureGhPreferOrigin,
4
4
  resolveGithubRepoSlug,
5
5
  resolveRepoRoot
6
- } from "./chunk-BIMHIHX3.js";
6
+ } from "./chunk-G52HLBQR.js";
7
7
  import {
8
8
  allocateTeamName,
9
9
  createEmptyThread,
@@ -384,7 +384,7 @@ var COORDINATOR_TOOL_PLAYBOOK = [
384
384
  "Sideboard MCP (fleet control \u2014 prefer these for status and orchestration):",
385
385
  "Discover:",
386
386
  "- list_projects \u2014 registered repos (path + github slug + project context when set). Use account / project context to pick the right repo for tickets or reviews.",
387
- "- list_board \u2014 Home Kanban of workspaces (New / Draft / Review / Merged; one card per checkout). Sibling chats on a card are separate agents, not one agent. Path to merge: no PR \u2192 draft PR \u2192 open PR \u2192 merged. Archive removes the card to Settings \u2192 History (list_archive to browse, restore_workspace to bring it back). Queued/running are activity on the card, not columns. Orchestration chats are not on the board. Filters: query, repoPath, kind, column, limit.",
387
+ "- list_board \u2014 Home Kanban of workspaces (New / Draft / Review / Approved / Merged; one card per checkout). Sibling chats on a card are separate agents, not one agent. Path to merge: no PR \u2192 draft PR \u2192 open PR \u2192 approved \u2192 merged. Archive removes the card to Settings \u2192 History (list_archive to browse, restore_workspace to bring it back). Queued/running are activity on the card, not columns. Orchestration chats are not on the board. Filters: query, repoPath, kind, column, limit.",
388
388
  `- list_branches / list_prs / list_issues \u2014 pass repoPath from list_projects. Review is PRs (the surface for assigned ticket work), not the tickets: "Get me N tickets to review" \u2192 list_prs(queue=review, limit=N) then create_workspace sourceType=pr. That is open non-draft PRs labeled with this project's ready-for-review GitHub label (Settings \u2192 Projects; default eng-review) with no individual user reviewer yet. A team request (engineering-team) is not a claim \u2014 the viewer is on that team and can pick it up; claimed means an individual account is the reviewer. Bots ignored. Use Settings \u2192 Agents / Projects context (roles, teams, labels \u2014 freeform text; project adds to account) to prefer the right queues. queue=mine is review-requested:@me; queue=approved|changes uses those labels. Also: state, label, reviewer=me|unassigned|login, query, limit default 40 max 250; raise limit or tighten when truncated. list_issues (query, assignee=me|unassigned|all|user, limit default 40 max 250) lists Linear, AbleTime, or GitHub tickets \u2014 do not use it for that review-inbox ask. When they ask for tickets to work on, follow their context (assignee=me or unassigned as it says).`,
389
389
  "- Find work: when they ask to find work, pick up tickets, or list reviews, use Settings \u2192 Agents (account) plus Settings \u2192 Projects (per-repo) context. Tickets \u2192 Sideboard list_issues / linear_* (Account Linear). Reviews \u2192 list_prs(queue=review). Do not call Claude Linear MCP or any other vendor Linear MCP \u2014 those HTTP connectors hang or flap on the first turn. If a vendor MCP is down or reconnecting, ignore it and keep going with Sideboard tools. Show the options \u2014 do not create_workspace or start unless they also asked to start (e.g. \u201Cfind me work and start it\u201D). Do not start this unprompted. To change that context, show the proposed text, ask_user (Save this context / Do not save), wait, then update_viewer_context with confirmed=true. Never write it without confirmation.",
390
390
  "- Ticket updates: list_issues / linear_search_issues / github_search_issues with updatedSince (yesterday, 2d, or ISO). One call returns new assignments, ticket edits, and new comments \u2014 do not get_issue every ticket just to check for comments. kind=created vs updated distinguishes new tickets from existing ones.",
@@ -501,7 +501,7 @@ function coordinatorTurnReminder(opts) {
501
501
  goal ? `- Goal / title: ${goal}` : null,
502
502
  accountDefaultsPlaybookLine(),
503
503
  accountContextReminderLine() || null,
504
- "- Status: list_board (worktree Kanban: New \u2192 Draft \u2192 Review \u2192 Merged) or list_chats. Link chats as `[Title](sideboard://chat/<id>)`. Merge only if the user asked. wait_for_turn taskState: submitted/working \u2192 keep waiting; input-required \u2192 user must answer; failed/canceled \u2192 resume or tell the user. blockedReason is why that agent is waiting \u2014 read it; do not send_to_chat a check-in."
504
+ "- Status: list_board (worktree Kanban: New \u2192 Draft \u2192 Review \u2192 Approved \u2192 Merged) or list_chats. Link chats as `[Title](sideboard://chat/<id>)`. Merge only if the user asked. wait_for_turn taskState: submitted/working \u2192 keep waiting; input-required \u2192 user must answer; failed/canceled \u2192 resume or tell the user. blockedReason is why that agent is waiting \u2014 read it; do not send_to_chat a check-in."
505
505
  ].filter(Boolean).join("\n");
506
506
  }
507
507
  function ensureGlobalCoordinatorCwd(opts) {
@@ -339,7 +339,7 @@ var COORDINATOR_TOOL_PLAYBOOK = [
339
339
  "Sideboard MCP (fleet control \u2014 prefer these for status and orchestration):",
340
340
  "Discover:",
341
341
  "- list_projects \u2014 registered repos (path + github slug + project context when set). Use account / project context to pick the right repo for tickets or reviews.",
342
- "- list_board \u2014 Home Kanban of workspaces (New / Draft / Review / Merged; one card per checkout). Sibling chats on a card are separate agents, not one agent. Path to merge: no PR \u2192 draft PR \u2192 open PR \u2192 merged. Archive removes the card to Settings \u2192 History (list_archive to browse, restore_workspace to bring it back). Queued/running are activity on the card, not columns. Orchestration chats are not on the board. Filters: query, repoPath, kind, column, limit.",
342
+ "- list_board \u2014 Home Kanban of workspaces (New / Draft / Review / Approved / Merged; one card per checkout). Sibling chats on a card are separate agents, not one agent. Path to merge: no PR \u2192 draft PR \u2192 open PR \u2192 approved \u2192 merged. Archive removes the card to Settings \u2192 History (list_archive to browse, restore_workspace to bring it back). Queued/running are activity on the card, not columns. Orchestration chats are not on the board. Filters: query, repoPath, kind, column, limit.",
343
343
  `- list_branches / list_prs / list_issues \u2014 pass repoPath from list_projects. Review is PRs (the surface for assigned ticket work), not the tickets: "Get me N tickets to review" \u2192 list_prs(queue=review, limit=N) then create_workspace sourceType=pr. That is open non-draft PRs labeled with this project's ready-for-review GitHub label (Settings \u2192 Projects; default eng-review) with no individual user reviewer yet. A team request (engineering-team) is not a claim \u2014 the viewer is on that team and can pick it up; claimed means an individual account is the reviewer. Bots ignored. Use Settings \u2192 Agents / Projects context (roles, teams, labels \u2014 freeform text; project adds to account) to prefer the right queues. queue=mine is review-requested:@me; queue=approved|changes uses those labels. Also: state, label, reviewer=me|unassigned|login, query, limit default 40 max 250; raise limit or tighten when truncated. list_issues (query, assignee=me|unassigned|all|user, limit default 40 max 250) lists Linear, AbleTime, or GitHub tickets \u2014 do not use it for that review-inbox ask. When they ask for tickets to work on, follow their context (assignee=me or unassigned as it says).`,
344
344
  "- Find work: when they ask to find work, pick up tickets, or list reviews, use Settings \u2192 Agents (account) plus Settings \u2192 Projects (per-repo) context. Tickets \u2192 Sideboard list_issues / linear_* (Account Linear). Reviews \u2192 list_prs(queue=review). Do not call Claude Linear MCP or any other vendor Linear MCP \u2014 those HTTP connectors hang or flap on the first turn. If a vendor MCP is down or reconnecting, ignore it and keep going with Sideboard tools. Show the options \u2014 do not create_workspace or start unless they also asked to start (e.g. \u201Cfind me work and start it\u201D). Do not start this unprompted. To change that context, show the proposed text, ask_user (Save this context / Do not save), wait, then update_viewer_context with confirmed=true. Never write it without confirmation.",
345
345
  "- Ticket updates: list_issues / linear_search_issues / github_search_issues with updatedSince (yesterday, 2d, or ISO). One call returns new assignments, ticket edits, and new comments \u2014 do not get_issue every ticket just to check for comments. kind=created vs updated distinguishes new tickets from existing ones.",
@@ -456,7 +456,7 @@ function coordinatorTurnReminder(opts) {
456
456
  goal ? `- Goal / title: ${goal}` : null,
457
457
  accountDefaultsPlaybookLine(),
458
458
  accountContextReminderLine() || null,
459
- "- Status: list_board (worktree Kanban: New \u2192 Draft \u2192 Review \u2192 Merged) or list_chats. Link chats as `[Title](sideboard://chat/<id>)`. Merge only if the user asked. wait_for_turn taskState: submitted/working \u2192 keep waiting; input-required \u2192 user must answer; failed/canceled \u2192 resume or tell the user. blockedReason is why that agent is waiting \u2014 read it; do not send_to_chat a check-in."
459
+ "- Status: list_board (worktree Kanban: New \u2192 Draft \u2192 Review \u2192 Approved \u2192 Merged) or list_chats. Link chats as `[Title](sideboard://chat/<id>)`. Merge only if the user asked. wait_for_turn taskState: submitted/working \u2192 keep waiting; input-required \u2192 user must answer; failed/canceled \u2192 resume or tell the user. blockedReason is why that agent is waiting \u2014 read it; do not send_to_chat a check-in."
460
460
  ].filter(Boolean).join("\n");
461
461
  }
462
462
  function ensureGlobalCoordinatorCwd(opts) {