@sideboard-ai/core 0.1.124 → 0.1.127

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 (39) hide show
  1. package/dist/{agents-DPR2TDNF.js → agents-7BUQY2WX.js} +6 -6
  2. package/dist/{agents-DCXXLTXF.js → agents-X2I3RNCY.js} +6 -6
  3. package/dist/{app-settings-YFWV7MOG.js → app-settings-6RPALX4J.js} +7 -1
  4. package/dist/{app-settings-IKFWZDUK.js → app-settings-E7NEQY7D.js} +7 -1
  5. package/dist/{chunk-SDCPQL27.js → chunk-DLM5LSQW.js} +484 -93
  6. package/dist/{chunk-RG737OWT.js → chunk-G6X6UFJO.js} +2 -0
  7. package/dist/{chunk-HPAWHIZ3.js → chunk-HLJUNJBF.js} +2 -0
  8. package/dist/{chunk-OHEFHIG3.js → chunk-HRSBZIXS.js} +14 -6
  9. package/dist/{chunk-KWGP6RZM.js → chunk-HULESWLI.js} +4 -4
  10. package/dist/{chunk-FXQPY2KU.js → chunk-HUYEU4GS.js} +26 -4
  11. package/dist/{chunk-2X7I6MDW.js → chunk-HZPO3SSZ.js} +11 -8
  12. package/dist/{chunk-O43IRQ2Y.js → chunk-MNL4FSKY.js} +4 -4
  13. package/dist/{chunk-7Y3AYQWT.js → chunk-NV73AO7Q.js} +4 -4
  14. package/dist/{chunk-TRTWH6C2.js → chunk-RSFCB23A.js} +11 -8
  15. package/dist/{chunk-RBVVWBVB.js → chunk-SYLTXDRF.js} +2 -2
  16. package/dist/{chunk-MZDAEI3Y.js → chunk-T7KJQJAX.js} +496 -109
  17. package/dist/{chunk-UAVZ2JSO.js → chunk-UDQI3N47.js} +14 -6
  18. package/dist/{chunk-HZLE6LJJ.js → chunk-VE22NWBD.js} +2 -2
  19. package/dist/{chunk-JNAIKICO.js → chunk-ZMUOPTKZ.js} +4 -4
  20. package/dist/{chunk-BMIRX64H.js → chunk-ZND6XV6J.js} +26 -4
  21. package/dist/{coordinator-prompt-AZ3WRDNC.js → coordinator-prompt-MEEUOEF2.js} +4 -4
  22. package/dist/{coordinator-prompt-LEU62W25.js → coordinator-prompt-SPJSGAQS.js} +4 -4
  23. package/dist/{global-workspace-U5UOVXKQ.js → global-workspace-BMWP7YZC.js} +5 -5
  24. package/dist/{global-workspace-4YNYDMLQ.js → global-workspace-KNESLWKG.js} +5 -5
  25. package/dist/index.cjs +1208 -236
  26. package/dist/index.d.cts +303 -103
  27. package/dist/index.d.ts +303 -103
  28. package/dist/index.js +671 -129
  29. package/dist/mcp/run-stdio.cjs +1054 -194
  30. package/dist/mcp/run-stdio.js +554 -114
  31. package/dist/{orchestrator-VJQ5EWZZ.js → orchestrator-3JLPPTCF.js} +8 -8
  32. package/dist/{orchestrator-JS3EHI4E.js → orchestrator-6GUCE4LY.js} +8 -8
  33. package/dist/{thread-store-JR235AWK.js → thread-store-5MLYY35S.js} +1 -1
  34. package/dist/{thread-store-PMH3BMB6.js → thread-store-F6BCARQG.js} +1 -1
  35. package/dist/{workspaces-ICK433ZV.js → workspaces-HSYRYJYV.js} +6 -6
  36. package/dist/{workspaces-POWKTH2D.js → workspaces-O4QZOASB.js} +6 -6
  37. package/dist/{worktree-CMGBTVN4.js → worktree-ERHC7Q4F.js} +5 -3
  38. package/dist/{worktree-T57RD7BQ.js → worktree-JVGSLSLJ.js} +5 -3
  39. package/package.json +1 -1
@@ -18,13 +18,13 @@ import {
18
18
  stripBrightsyNdjsonNoise,
19
19
  sumUsageList,
20
20
  toolDescription
21
- } from "./chunk-7Y3AYQWT.js";
21
+ } from "./chunk-NV73AO7Q.js";
22
22
  import {
23
23
  addWorkspace,
24
24
  ensureWorkspace,
25
25
  removeWorkspace,
26
26
  syncWorkspacesFromThreads
27
- } from "./chunk-HZLE6LJJ.js";
27
+ } from "./chunk-VE22NWBD.js";
28
28
  import {
29
29
  assertOrchestratorCapableAgent,
30
30
  coerceOrchestratorAgent,
@@ -37,14 +37,14 @@ import {
37
37
  isOrchestratorThread,
38
38
  isSlackCoordinatorThread,
39
39
  orchestratorSessionPoisonedByBuiltins
40
- } from "./chunk-KWGP6RZM.js";
40
+ } from "./chunk-HULESWLI.js";
41
41
  import {
42
42
  SLACK_REPLY_FORMATTING,
43
43
  coordinatorSystemPrompt,
44
44
  coordinatorTurnReminder,
45
45
  enrichWorkspacesWithGithub,
46
46
  ensureGlobalCoordinatorCwd
47
- } from "./chunk-2X7I6MDW.js";
47
+ } from "./chunk-HZPO3SSZ.js";
48
48
  import {
49
49
  extractPresentedPlan,
50
50
  readPlanFile,
@@ -72,6 +72,7 @@ import {
72
72
  addPrStackLayer,
73
73
  allocateTeamName,
74
74
  allocateTeamSlug,
75
+ canonicalizeRepoPath,
75
76
  commitAll,
76
77
  createExistingBranchWorktree,
77
78
  createOrUpdatePr,
@@ -109,8 +110,9 @@ import {
109
110
  takenSlugsFromThread,
110
111
  threadDisplayLabel,
111
112
  withRepoGitLock,
113
+ worktreeDisplayLabelForGroup,
112
114
  worktreeNameFromPath
113
- } from "./chunk-UAVZ2JSO.js";
115
+ } from "./chunk-UDQI3N47.js";
114
116
  import {
115
117
  ATTACHMENTS_DIR,
116
118
  LEGACY_ATTACHMENTS_DIR,
@@ -128,7 +130,7 @@ import {
128
130
  resolveVaultKey,
129
131
  updateAdvancedSettings,
130
132
  writeSecureJson
131
- } from "./chunk-FXQPY2KU.js";
133
+ } from "./chunk-HUYEU4GS.js";
132
134
  import {
133
135
  stripNestedElectronEnv
134
136
  } from "./chunk-4TR3HZFT.js";
@@ -146,7 +148,7 @@ import {
146
148
  updateThread,
147
149
  withThreadLock,
148
150
  writeThread
149
- } from "./chunk-HPAWHIZ3.js";
151
+ } from "./chunk-HLJUNJBF.js";
150
152
  import {
151
153
  appDataDir,
152
154
  findConventionSetup,
@@ -364,26 +366,9 @@ function storePath3() {
364
366
  function watchId(teamId, channelId, ts) {
365
367
  return `${teamId}:${channelId}:${ts}`;
366
368
  }
367
- function badgeId(teamId, userId) {
368
- return `${teamId}:${userId}`;
369
- }
370
369
  function slackArchiveUrl(channelId, ts) {
371
370
  return `https://slack.com/archives/${channelId}/p${ts.replace(".", "")}`;
372
371
  }
373
- function initialsFromName(name) {
374
- const parts = name.trim().split(/\s+/).filter(Boolean);
375
- if (parts.length === 0) return "?";
376
- if (parts.length === 1) {
377
- const w = parts[0];
378
- return (w.slice(0, 2) || "?").toUpperCase();
379
- }
380
- return `${parts[0][0] ?? ""}${parts[parts.length - 1][0] ?? ""}`.toUpperCase();
381
- }
382
- function hueFromId(id) {
383
- let h = 0;
384
- for (const c of id) h = h * 31 + c.charCodeAt(0) >>> 0;
385
- return h % 360;
386
- }
387
372
  function tsNewer(a, b) {
388
373
  return Number(a) > Number(b);
389
374
  }
@@ -456,7 +441,7 @@ async function continueSourceThread(threadId, prompt) {
456
441
  await continueOnReply(threadId, prompt);
457
442
  return;
458
443
  }
459
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-JS3EHI4E.js");
444
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-6GUCE4LY.js");
460
445
  await getOrchestrator2().send(threadId, prompt);
461
446
  } catch {
462
447
  }
@@ -540,7 +525,6 @@ function recordSlackOutboundWatch(input) {
540
525
  sourceThreadId: input.sourceThreadId?.trim() || void 0,
541
526
  postedAt: (/* @__PURE__ */ new Date()).toISOString(),
542
527
  lastSeenTs: ts,
543
- unread: false,
544
528
  permalink: slackArchiveUrl(channelId, ts),
545
529
  injectedReplyTs: [],
546
530
  replies: []
@@ -627,49 +611,9 @@ async function fetchMessages(token, watch, fetchImpl) {
627
611
  }
628
612
  return out;
629
613
  }
630
- function listSlackReplyBadges() {
631
- const unread = readStore3().filter((w) => w.unread && w.replyUserId && w.permalink);
632
- const byUser = /* @__PURE__ */ new Map();
633
- for (const w of unread) {
634
- const id = badgeId(w.teamId, w.replyUserId);
635
- const prev = byUser.get(id);
636
- if (!prev || tsNewer(w.replyTs || "", prev.replyTs || "")) {
637
- byUser.set(id, w);
638
- }
639
- }
640
- return [...byUser.entries()].map(([id, w]) => {
641
- const userName = w.replyUserName || w.toLabel || "Slack";
642
- return {
643
- id,
644
- userId: w.replyUserId,
645
- userName,
646
- initials: initialsFromName(userName),
647
- hue: hueFromId(w.replyUserId),
648
- permalink: w.permalink || slackArchiveUrl(w.channelId, w.replyTs || w.ts),
649
- label: w.toLabel,
650
- preview: w.replyPreview,
651
- repliedAt: w.replyTs || w.postedAt
652
- };
653
- }).sort((a, b) => b.repliedAt.localeCompare(a.repliedAt));
654
- }
655
- function dismissSlackReplyBadge(badgeKey) {
656
- const key = badgeKey.trim();
657
- const watches = readStore3().map((w) => {
658
- if (!w.unread || !w.replyUserId) return w;
659
- if (badgeId(w.teamId, w.replyUserId) !== key) return w;
660
- return { ...w, unread: false };
661
- });
662
- writeStore2(watches);
663
- return listSlackReplyBadges();
664
- }
665
- function permalinkForSlackReplyBadge(badgeKey) {
666
- return listSlackReplyBadges().find((b) => b.id === badgeKey)?.permalink ?? null;
667
- }
668
- async function refreshSlackReplyBadges(opts) {
614
+ async function pollSlackOutboundWatches(opts) {
669
615
  const now = opts?.now ?? Date.now();
670
- if (!opts?.force && now - lastPollMs < POLL_INTERVAL_MS) {
671
- return listSlackReplyBadges();
672
- }
616
+ if (!opts?.force && now - lastPollMs < POLL_INTERVAL_MS) return;
673
617
  lastPollMs = now;
674
618
  const existing = readStore3();
675
619
  let watches = pruneWatches(existing, now);
@@ -690,9 +634,7 @@ async function refreshSlackReplyBadges(opts) {
690
634
  const injected = new Set(watch.injectedReplyTs ?? []);
691
635
  const collected = [...watch.replies ?? []];
692
636
  let lastSeenTs = watch.lastSeenTs;
693
- let latestUser;
694
637
  let latestName;
695
- let latestText = "";
696
638
  let latestPermalink = watch.permalink;
697
639
  let newlyInjected = 0;
698
640
  for (const msg of replies) {
@@ -718,9 +660,7 @@ async function refreshSlackReplyBadges(opts) {
718
660
  text: msg.text ?? ""
719
661
  };
720
662
  if (!collected.some((r) => r.ts === ts)) collected.push(reply);
721
- latestUser = user;
722
663
  latestName = replyUserName;
723
- latestText = reply.text;
724
664
  latestPermalink = permalink;
725
665
  if (injected.has(ts)) {
726
666
  lastSeenTs = ts;
@@ -751,11 +691,6 @@ async function refreshSlackReplyBadges(opts) {
751
691
  watches[i] = {
752
692
  ...watch,
753
693
  lastSeenTs,
754
- unread: true,
755
- replyUserId: latestUser,
756
- replyUserName: latestName,
757
- replyTs: lastSeenTs,
758
- replyPreview: latestText.slice(0, 140),
759
694
  permalink: latestPermalink,
760
695
  injectedReplyTs: [...injected],
761
696
  replies: collected.slice(-MAX_REPLIES_PER_WATCH)
@@ -763,7 +698,6 @@ async function refreshSlackReplyBadges(opts) {
763
698
  changed = true;
764
699
  }
765
700
  if (changed) writeStore2(watches);
766
- return listSlackReplyBadges();
767
701
  }
768
702
 
769
703
  // src/orchestrator/orchestrator.ts
@@ -787,9 +721,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
787
721
  `Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
788
722
  );
789
723
  }
790
- const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-U5UOVXKQ.js");
724
+ const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-BMWP7YZC.js");
791
725
  if (isGlobalThread2(thread)) {
792
- const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-AZ3WRDNC.js");
726
+ const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-MEEUOEF2.js");
793
727
  ensureGlobalCoordinatorCwd2(
794
728
  isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
795
729
  );
@@ -1355,6 +1289,7 @@ function worktreeBindingFrom(from) {
1355
1289
  prUrl: from.prUrl,
1356
1290
  prTitle: from.prTitle,
1357
1291
  prState: from.prState,
1292
+ prIsDraft: from.prIsDraft,
1358
1293
  skipAutoArchiveOnMerge: from.skipAutoArchiveOnMerge,
1359
1294
  stackId: from.stackId,
1360
1295
  stackLayer: from.stackLayer,
@@ -2362,8 +2297,425 @@ async function requireAgent(agent, opts) {
2362
2297
  return status;
2363
2298
  }
2364
2299
 
2300
+ // src/board/home-board.ts
2301
+ var GLOBAL_WORKSPACE_ID = "__global__";
2302
+ function isHomeBoardThread(thread) {
2303
+ if (thread.sourceType === "orchestration") return false;
2304
+ if (thread.repoPath === GLOBAL_WORKSPACE_ID) return false;
2305
+ return true;
2306
+ }
2307
+ var BOARD_COLUMN_DEFS = [
2308
+ { id: "new", title: "New" },
2309
+ { id: "draft", title: "Draft" },
2310
+ { id: "review", title: "Review" },
2311
+ { id: "done", title: "Merged" }
2312
+ ];
2313
+ var HOME_BOARD_CACHE_TTL_MS = 15 * 60 * 1e3;
2314
+ function isOpenPrState(prUrl, prState) {
2315
+ if (!prUrl?.trim()) return false;
2316
+ const state = (prState ?? "OPEN").trim().toUpperCase();
2317
+ return state !== "MERGED" && state !== "CLOSED";
2318
+ }
2319
+ function isMergedPrState(prUrl, prState) {
2320
+ if (!prUrl?.trim()) return false;
2321
+ return (prState ?? "").trim().toUpperCase() === "MERGED";
2322
+ }
2323
+ function groupHomeBoardWorktrees(threads) {
2324
+ const map = /* @__PURE__ */ new Map();
2325
+ for (const t of threads) {
2326
+ const key = normalizeWorktreePath(t.worktreePath);
2327
+ const list = map.get(key) ?? [];
2328
+ list.push(t);
2329
+ map.set(key, list);
2330
+ }
2331
+ return [...map.values()].map((list) => list.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt))).sort((a, b) => (b[0]?.updatedAt ?? "").localeCompare(a[0]?.updatedAt ?? ""));
2332
+ }
2333
+ function classifyWorktreeColumn(group) {
2334
+ if (group.some((t) => isMergedPrState(t.prUrl, t.prState))) return "done";
2335
+ if (group.some((t) => isOpenPrState(t.prUrl, t.prState) && !t.prIsDraft)) {
2336
+ return "review";
2337
+ }
2338
+ if (group.some((t) => isOpenPrState(t.prUrl, t.prState) && t.prIsDraft)) {
2339
+ return "draft";
2340
+ }
2341
+ return "new";
2342
+ }
2343
+ function worktreeBoardStatus(group) {
2344
+ const order = ["running", "queued", "error", "broken"];
2345
+ for (const status of order) {
2346
+ if (group.some((t) => t.status === status)) return status;
2347
+ }
2348
+ return group[0]?.status ?? "idle";
2349
+ }
2350
+ function normalizeIssueKey(value) {
2351
+ return value.trim().toLowerCase().replace(/^#/, "");
2352
+ }
2353
+ function threadMatchesIssue(thread, issue) {
2354
+ const identRaw = issue.identifier.trim().toLowerCase();
2355
+ const identKey = normalizeIssueKey(issue.identifier);
2356
+ const title = issue.title.trim().toLowerCase();
2357
+ const refRaw = (thread.sourceRef ?? "").trim().toLowerCase();
2358
+ const refKey = normalizeIssueKey(thread.sourceRef ?? "");
2359
+ const threadTitle = (thread.title ?? "").trim().toLowerCase();
2360
+ if (thread.sourceType === "ticket" && identKey && refKey === identKey) return true;
2361
+ if (identRaw && (refRaw === identRaw || threadTitle.includes(identRaw))) return true;
2362
+ if (title && threadTitle === title) return true;
2363
+ return false;
2364
+ }
2365
+ function dedupeBoardIssues(issues) {
2366
+ const seen = /* @__PURE__ */ new Set();
2367
+ const out = [];
2368
+ for (const issue of issues) {
2369
+ const key = [
2370
+ issue.repoPath,
2371
+ issue.provider ?? "",
2372
+ issue.id || issue.identifier
2373
+ ].join(":");
2374
+ if (seen.has(key)) continue;
2375
+ seen.add(key);
2376
+ out.push(issue);
2377
+ }
2378
+ return out;
2379
+ }
2380
+ function issueNeedsWorkspacePick(provider, workspaceCount) {
2381
+ if (workspaceCount <= 1) return false;
2382
+ return provider !== "github";
2383
+ }
2384
+ function boardPrKey(pr) {
2385
+ return pr.url?.trim() || `${pr.repoPath}::${pr.number}`;
2386
+ }
2387
+ function urlsMatch(a, b) {
2388
+ const norm = (u) => u.trim().replace(/\/+$/, "").toLowerCase();
2389
+ return Boolean(a && b && norm(a) === norm(b));
2390
+ }
2391
+ function threadMatchesPr(thread, pr) {
2392
+ const num = String(pr.number);
2393
+ if (thread.sourceType === "pr" && normalizeIssueKey(thread.sourceRef ?? "") === num) {
2394
+ return true;
2395
+ }
2396
+ if (thread.prUrl && pr.url && urlsMatch(thread.prUrl, pr.url)) return true;
2397
+ const head = pr.headRefName.trim().toLowerCase();
2398
+ if (!head) return false;
2399
+ const branch = (thread.branchName ?? "").trim().toLowerCase();
2400
+ const ref = (thread.sourceRef ?? "").trim().replace(/^refs\/heads\//, "").replace(/^origin\//, "").toLowerCase();
2401
+ if (branch === head) return true;
2402
+ if (!ref || /^(main|master|develop|development|trunk|default|head)$/.test(ref)) {
2403
+ return false;
2404
+ }
2405
+ return ref === head;
2406
+ }
2407
+ function normalizeBranchRef(value) {
2408
+ return value.trim().replace(/^refs\/heads\//, "").replace(/^origin\//, "").toLowerCase();
2409
+ }
2410
+ function threadMatchesBranch(thread, pin) {
2411
+ if (pin.repoPath && thread.repoPath && pin.repoPath !== thread.repoPath) return false;
2412
+ const head = normalizeBranchRef(pin.ref === "default" ? "" : pin.ref);
2413
+ if (!head) {
2414
+ return thread.sourceType === "branch" && pin.repoPath === thread.repoPath;
2415
+ }
2416
+ const branch = normalizeBranchRef(thread.branchName ?? "");
2417
+ const ref = normalizeBranchRef(thread.sourceRef ?? "");
2418
+ return branch === head || ref === head;
2419
+ }
2420
+ function sameRepoPath(a, b) {
2421
+ return a.replace(/\/+$/, "") === b.replace(/\/+$/, "");
2422
+ }
2423
+ var DEFAULTISH_BRANCH = /^(main|master|develop|development|trunk|default|head)$/;
2424
+ function isDefaultBranchCreateRef(ref) {
2425
+ const head = normalizeBranchRef(ref);
2426
+ return !head || DEFAULTISH_BRANCH.test(head);
2427
+ }
2428
+ function findLiveThreadForCreate(input, threads) {
2429
+ const live = threads.filter(
2430
+ (t) => t.status !== "archived" && sameRepoPath(t.repoPath, input.repoPath)
2431
+ );
2432
+ if (input.cowboy) {
2433
+ return live.find((t) => t.cowboy);
2434
+ }
2435
+ if (input.sourceType === "ticket") {
2436
+ return live.find(
2437
+ (t) => threadMatchesIssue(t, {
2438
+ identifier: input.sourceRef,
2439
+ title: input.title ?? ""
2440
+ })
2441
+ );
2442
+ }
2443
+ if (input.sourceType === "pr") {
2444
+ const number = Number(normalizeIssueKey(input.sourceRef));
2445
+ return live.find(
2446
+ (t) => threadMatchesPr(t, {
2447
+ number: Number.isFinite(number) ? number : -1,
2448
+ title: input.title ?? "",
2449
+ url: "",
2450
+ headRefName: ""
2451
+ })
2452
+ );
2453
+ }
2454
+ if (input.sourceType === "branch") {
2455
+ if (isDefaultBranchCreateRef(input.sourceRef)) return void 0;
2456
+ return live.find(
2457
+ (t) => threadMatchesBranch(t, {
2458
+ ref: input.sourceRef,
2459
+ repoPath: input.repoPath
2460
+ })
2461
+ );
2462
+ }
2463
+ return void 0;
2464
+ }
2465
+ function boardPinIdentity(pin) {
2466
+ const ref = normalizeIssueKey(pin.ref);
2467
+ if (pin.kind === "ticket" && (pin.provider === "linear" || !pin.repoPath)) {
2468
+ return `ticket:account:${ref}`;
2469
+ }
2470
+ return `${pin.kind}:${pin.repoPath}:${ref}`;
2471
+ }
2472
+ function findBoardPin(pins, kind, ref, repoPath = "") {
2473
+ const key = normalizeIssueKey(ref);
2474
+ const matches = pins.filter(
2475
+ (pin) => pin.kind === kind && normalizeIssueKey(pin.ref) === key
2476
+ );
2477
+ if (repoPath) {
2478
+ const scoped = matches.filter((pin) => pin.repoPath === repoPath);
2479
+ if (scoped[0]) return scoped[0];
2480
+ }
2481
+ return matches[0];
2482
+ }
2483
+ function syncBoardPins(pins, issues, prs) {
2484
+ return pins.map((pin) => {
2485
+ if (pin.kind === "ticket") {
2486
+ const issue = findBoardIssue(issues, pin.ref, pin.repoPath);
2487
+ if (!issue) {
2488
+ return { ...pin, remoteState: pin.remoteState || "stale" };
2489
+ }
2490
+ return {
2491
+ ...pin,
2492
+ title: issue.title || pin.title,
2493
+ url: issue.url || pin.url,
2494
+ labels: issue.labels,
2495
+ provider: issue.provider ?? pin.provider,
2496
+ assignee: issue.assignee ?? pin.assignee,
2497
+ cycle: issue.cycle?.name ?? pin.cycle,
2498
+ teamKey: issue.teamKey ?? pin.teamKey,
2499
+ remoteState: "open",
2500
+ needsWorkspacePick: issue.needsWorkspacePick
2501
+ };
2502
+ }
2503
+ if (pin.kind === "pr") {
2504
+ const pr = findBoardPr(prs, pin.ref, pin.repoPath);
2505
+ if (!pr) {
2506
+ return { ...pin, remoteState: pin.remoteState === "open" ? "stale" : pin.remoteState || "stale" };
2507
+ }
2508
+ return {
2509
+ ...pin,
2510
+ title: pr.title || pin.title,
2511
+ url: pr.url || pin.url,
2512
+ headRefName: pr.headRefName || pin.headRefName,
2513
+ author: prAuthorLogin(pr) || pin.author,
2514
+ remoteState: "open"
2515
+ };
2516
+ }
2517
+ return pin;
2518
+ });
2519
+ }
2520
+ function dedupeBoardPrs(prs) {
2521
+ const seen = /* @__PURE__ */ new Set();
2522
+ const out = [];
2523
+ for (const pr of prs) {
2524
+ const key = boardPrKey(pr);
2525
+ if (seen.has(key)) continue;
2526
+ seen.add(key);
2527
+ out.push(pr);
2528
+ }
2529
+ return out.sort((a, b) => b.number - a.number);
2530
+ }
2531
+ var BOARD_PAGE_SIZE = 40;
2532
+ function tokenizeQuery(query) {
2533
+ return query.trim().toLowerCase().split(/\s+/).filter(Boolean);
2534
+ }
2535
+ function haystackMatches(haystack, tokens) {
2536
+ if (tokens.length === 0) return true;
2537
+ const h = haystack.toLowerCase();
2538
+ return tokens.every((token) => h.includes(token));
2539
+ }
2540
+ function prAuthorLogin(pr) {
2541
+ return pr.author?.login?.trim() ?? "";
2542
+ }
2543
+ function threadSearchText(thread, workspaceName = "") {
2544
+ return [
2545
+ thread.title,
2546
+ thread.sourceRef,
2547
+ thread.sourceType,
2548
+ thread.agent,
2549
+ thread.status,
2550
+ thread.branchName,
2551
+ thread.prUrl ?? "",
2552
+ workspaceName,
2553
+ thread.repoPath
2554
+ ].join(" ");
2555
+ }
2556
+ function inWorkspace(repoPath, filterRepoPath) {
2557
+ if (!filterRepoPath) return true;
2558
+ return repoPath === filterRepoPath;
2559
+ }
2560
+ function visiblePage(items, shown) {
2561
+ const n = Math.max(0, shown);
2562
+ return {
2563
+ visible: items.slice(0, n),
2564
+ hidden: Math.max(0, items.length - n)
2565
+ };
2566
+ }
2567
+ function emptyColumns() {
2568
+ return {
2569
+ backlog: [],
2570
+ queued: [],
2571
+ running: [],
2572
+ new: [],
2573
+ draft: [],
2574
+ review: [],
2575
+ done: []
2576
+ };
2577
+ }
2578
+ function emptyHidden() {
2579
+ return {
2580
+ backlog: 0,
2581
+ queued: 0,
2582
+ running: 0,
2583
+ new: 0,
2584
+ draft: 0,
2585
+ review: 0,
2586
+ done: 0
2587
+ };
2588
+ }
2589
+ function findBoardIssue(issues, ref, repoPath = "") {
2590
+ const key = normalizeIssueKey(ref);
2591
+ if (!key) return void 0;
2592
+ const matches = issues.filter((issue) => normalizeIssueKey(issue.identifier) === key);
2593
+ if (repoPath) {
2594
+ const scoped = matches.filter((issue) => issue.repoPath === repoPath);
2595
+ if (scoped[0]) return scoped[0];
2596
+ }
2597
+ return matches[0];
2598
+ }
2599
+ function findBoardPr(prs, ref, repoPath = "") {
2600
+ const num = Number(normalizeIssueKey(ref));
2601
+ if (!Number.isFinite(num)) return void 0;
2602
+ const matches = prs.filter((pr) => pr.number === num);
2603
+ if (repoPath) {
2604
+ const scoped = matches.filter((pr) => pr.repoPath === repoPath);
2605
+ if (scoped[0]) return scoped[0];
2606
+ }
2607
+ return matches[0];
2608
+ }
2609
+ function toThreadCard(group) {
2610
+ const thread = group[0];
2611
+ const withPr = group.find((t) => t.prUrl?.trim()) ?? thread;
2612
+ return {
2613
+ kind: "thread",
2614
+ id: thread.id,
2615
+ title: worktreeDisplayLabelForGroup(group),
2616
+ status: worktreeBoardStatus(group),
2617
+ agent: thread.agent,
2618
+ sourceType: thread.sourceType,
2619
+ sourceRef: thread.sourceRef,
2620
+ repoPath: thread.repoPath,
2621
+ prUrl: withPr.prUrl,
2622
+ link: `sideboard://thread/${thread.id}`,
2623
+ ...group.length > 1 ? { chatCount: group.length } : {}
2624
+ };
2625
+ }
2626
+ function threadMatchesKind(thread, kind) {
2627
+ if (kind === "all" || kind === "threads") return true;
2628
+ if (kind === "tickets") return thread.sourceType === "ticket";
2629
+ if (kind === "prs") return thread.sourceType === "pr";
2630
+ if (kind === "branches") return thread.sourceType === "branch" || thread.sourceType === "adopt";
2631
+ return true;
2632
+ }
2633
+ function assembleHomeBoard(input) {
2634
+ const tokens = tokenizeQuery(input.query ?? "");
2635
+ const repo = input.repoPath?.trim() ?? "";
2636
+ const kind = input.kind ?? "all";
2637
+ const limit = Math.max(1, input.limit ?? BOARD_PAGE_SIZE);
2638
+ const wsName = input.workspaceName ?? (() => "");
2639
+ const byUpdated = (a, b) => b.updatedAt.localeCompare(a.updatedAt);
2640
+ const live = input.threads.filter((t) => t.status !== "archived" && isHomeBoardThread(t)).sort(byUpdated);
2641
+ const columns = emptyColumns();
2642
+ const hidden = emptyHidden();
2643
+ const totals = {
2644
+ backlog: 0,
2645
+ queued: 0,
2646
+ running: 0,
2647
+ new: 0,
2648
+ draft: 0,
2649
+ review: 0,
2650
+ done: 0,
2651
+ tickets: 0,
2652
+ prs: 0,
2653
+ branches: 0,
2654
+ threads: 0
2655
+ };
2656
+ const byCol = {
2657
+ queued: [],
2658
+ running: [],
2659
+ new: [],
2660
+ draft: [],
2661
+ review: [],
2662
+ done: []
2663
+ };
2664
+ for (const group of groupHomeBoardWorktrees(live)) {
2665
+ if (!group.some((t) => threadMatchesKind(t, kind))) continue;
2666
+ const col = classifyWorktreeColumn(group);
2667
+ if (col === "backlog") continue;
2668
+ const primary = group[0];
2669
+ if (!inWorkspace(primary.repoPath, repo)) continue;
2670
+ const hay = group.map((t) => threadSearchText(t, wsName(t.repoPath))).join(" ");
2671
+ if (!haystackMatches(hay, tokens)) continue;
2672
+ byCol[col].push(group);
2673
+ }
2674
+ const flat = [...Object.values(byCol)].flatMap((groups) => groups.map((g) => g[0]));
2675
+ totals.tickets = flat.filter((t) => t.sourceType === "ticket").length;
2676
+ totals.prs = flat.filter((t) => t.sourceType === "pr").length;
2677
+ totals.branches = flat.filter((t) => t.sourceType === "branch" || t.sourceType === "adopt").length;
2678
+ totals.threads = Object.values(byCol).reduce((n, list) => n + list.length, 0);
2679
+ for (const col of Object.keys(byCol)) {
2680
+ const cards = byCol[col].map(toThreadCard);
2681
+ totals[col] = cards.length;
2682
+ const page = visiblePage(cards, limit);
2683
+ columns[col] = page.visible;
2684
+ hidden[col] = page.hidden;
2685
+ }
2686
+ if (input.column) {
2687
+ for (const col of Object.keys(columns)) {
2688
+ if (col === input.column) continue;
2689
+ columns[col] = [];
2690
+ hidden[col] = 0;
2691
+ }
2692
+ }
2693
+ return { columns, hidden, totals };
2694
+ }
2695
+ 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.";
2696
+
2365
2697
  // src/threads/create.ts
2366
2698
  async function createThread(input, _onSetupLine) {
2699
+ const repoPath = await resolveRepoRoot(input.repoPath);
2700
+ if (!existsSync7(repoPath)) {
2701
+ throw new Error(`Repo not found: ${repoPath}`);
2702
+ }
2703
+ if (input.reuseExisting !== false) {
2704
+ const existing = findLiveThreadForCreate(
2705
+ {
2706
+ sourceType: input.sourceType,
2707
+ sourceRef: input.sourceRef,
2708
+ repoPath,
2709
+ title: input.title,
2710
+ cowboy: input.cowboy
2711
+ },
2712
+ listThreads({ includeArchived: false }).map((t) => ({
2713
+ ...t,
2714
+ repoPath: canonicalizeRepoPath(t.repoPath)
2715
+ }))
2716
+ );
2717
+ if (existing) return readThread(existing.id) ?? existing;
2718
+ }
2367
2719
  const resolved = resolveNewThreadOptions({
2368
2720
  agent: input.agent,
2369
2721
  model: input.model,
@@ -2371,12 +2723,8 @@ async function createThread(input, _onSetupLine) {
2371
2723
  fast: input.fast
2372
2724
  });
2373
2725
  await requireAgent(resolved.agent);
2374
- const repoPath = await resolveRepoRoot(input.repoPath);
2375
- if (!existsSync7(repoPath)) {
2376
- throw new Error(`Repo not found: ${repoPath}`);
2377
- }
2378
2726
  if (input.cowboy) {
2379
- const { cowboyModeEnabled } = await import("./app-settings-YFWV7MOG.js");
2727
+ const { cowboyModeEnabled } = await import("./app-settings-6RPALX4J.js");
2380
2728
  if (!cowboyModeEnabled()) {
2381
2729
  throw new Error(
2382
2730
  "Cowboy mode is off. Enable it in Settings \u2192 Advanced."
@@ -2487,7 +2835,7 @@ async function createThread(input, _onSetupLine) {
2487
2835
  return readThread(thread.id) ?? thread;
2488
2836
  }
2489
2837
  async function listLinearIssues(agent, repoPath) {
2490
- const { getAdapter: getAdapter2 } = await import("./agents-DCXXLTXF.js");
2838
+ const { getAdapter: getAdapter2 } = await import("./agents-X2I3RNCY.js");
2491
2839
  await requireAgent(agent, { requireLinear: true });
2492
2840
  const adapter = getAdapter2(agent);
2493
2841
  if (!adapter.listLinearIssues) {
@@ -2673,7 +3021,8 @@ async function forkThreadWorktree(input, onSetupLine) {
2673
3021
  planMode: from.planMode,
2674
3022
  title: input.title?.trim() || void 0,
2675
3023
  parentThreadId: from.id,
2676
- attachments: [attachment]
3024
+ attachments: [attachment],
3025
+ reuseExisting: false
2677
3026
  },
2678
3027
  onSetupLine
2679
3028
  );
@@ -2753,7 +3102,7 @@ function buildQuotaHandoffAttachment(from, limitText, fallbackAgent) {
2753
3102
  "",
2754
3103
  `## Instructions`,
2755
3104
  `- Continue fleet orchestration from this handoff.`,
2756
- `- Prefer Sideboard MCP (list_threads, get_thread, send_to_thread, \u2026) for live status.`,
3105
+ `- Prefer Sideboard MCP (list_board, list_threads, get_thread, send_to_thread, \u2026) for live status.`,
2757
3106
  `- Leave model Auto unless there is a specific reason to pin one.`,
2758
3107
  `- Do not wait on the limited ${from.agent} account; keep going on ${fallbackAgent}.`
2759
3108
  ].join("\n");
@@ -2766,9 +3115,9 @@ function buildQuotaHandoffAttachment(from, limitText, fallbackAgent) {
2766
3115
  }
2767
3116
  var QUOTA_CONTINUE_PROMPT = (fromAgent, fallback) => [
2768
3117
  `${fromAgent} hit a session/usage limit. Continue this orchestration on ${fallback} using the attached handoff.`,
2769
- "Call list_threads for live fleet status, then proceed with the goal. Leave model Auto unless needed."
3118
+ "Call list_board or list_threads for live fleet status, then proceed with the goal. Leave model Auto unless needed."
2770
3119
  ].join(" ");
2771
- var QUOTA_RESUME_PROMPT = "Session/usage limit window should have reset. Continue the orchestration from where you left off. Use list_threads for fleet status.";
3120
+ var QUOTA_RESUME_PROMPT = "Session/usage limit window should have reset. Continue the orchestration from where you left off. Use list_board or list_threads for fleet status.";
2772
3121
  function createQuotaFailoverChat(from, fallbackAgent, limitText) {
2773
3122
  const handoff = buildQuotaHandoffAttachment(from, limitText, fallbackAgent);
2774
3123
  const tab = createChatTab({
@@ -2883,7 +3232,7 @@ async function adoptThread(input) {
2883
3232
  messages: input.messages ?? []
2884
3233
  });
2885
3234
  writeThread(thread);
2886
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-POWKTH2D.js");
3235
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-O4QZOASB.js");
2887
3236
  await ensureWorkspace2(repoPath);
2888
3237
  return thread;
2889
3238
  }
@@ -5156,7 +5505,7 @@ function formatScheduledPrompt(name, prompt) {
5156
5505
  ${prompt}`;
5157
5506
  }
5158
5507
  async function defaultDeps() {
5159
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-JS3EHI4E.js");
5508
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-6GUCE4LY.js");
5160
5509
  const orch = getOrchestrator2();
5161
5510
  return {
5162
5511
  findThread: (id) => findThreadByRef(id),
@@ -5310,7 +5659,7 @@ var Orchestrator = class {
5310
5659
  maxConcurrent;
5311
5660
  runningCount = 0;
5312
5661
  constructor(opts) {
5313
- this.maxConcurrent = opts?.maxConcurrent ?? 3;
5662
+ this.maxConcurrent = opts?.maxConcurrent ?? 5;
5314
5663
  }
5315
5664
  on(listener) {
5316
5665
  this.events.on("event", listener);
@@ -5376,7 +5725,7 @@ var Orchestrator = class {
5376
5725
  orphans: orphans.map((o) => ({ path: o.path, repoPath: o.repoPath }))
5377
5726
  });
5378
5727
  }
5379
- const { autoCleanupOrphansEnabled } = await import("./app-settings-YFWV7MOG.js");
5728
+ const { autoCleanupOrphansEnabled } = await import("./app-settings-6RPALX4J.js");
5380
5729
  if (autoCleanupOrphansEnabled() && shouldRunWorktreeCleanup() && orphans.length > 0) {
5381
5730
  await cleanupOrphanWorktrees({ repoPaths });
5382
5731
  }
@@ -5527,7 +5876,23 @@ var Orchestrator = class {
5527
5876
  return findThreadByRef(idOrRef) ?? readThread(idOrRef);
5528
5877
  }
5529
5878
  async createThread(input) {
5879
+ const prior = new Set(
5880
+ listThreads({ includeArchived: false }).map((t) => t.id)
5881
+ );
5530
5882
  const thread = await createThread(input);
5883
+ if (prior.has(thread.id)) {
5884
+ if (input.prompt?.trim()) {
5885
+ try {
5886
+ await this.send(thread.id, input.prompt.trim());
5887
+ } catch (err) {
5888
+ const message = err instanceof Error ? err.message : String(err);
5889
+ updateThread(thread.id, {
5890
+ lastError: `First prompt failed: ${message}`
5891
+ });
5892
+ }
5893
+ }
5894
+ return thread;
5895
+ }
5531
5896
  this.emit({ type: "status_changed", threadId: thread.id, status: thread.status });
5532
5897
  void this.finishCreateThread(thread.id, input.prompt?.trim() || void 0);
5533
5898
  return thread;
@@ -5548,7 +5913,7 @@ var Orchestrator = class {
5548
5913
  }
5549
5914
  await setup;
5550
5915
  if (skipSetup) return;
5551
- const { autoRunAfterSetupEnabled } = await import("./app-settings-YFWV7MOG.js");
5916
+ const { autoRunAfterSetupEnabled } = await import("./app-settings-6RPALX4J.js");
5552
5917
  if (autoRunAfterSetupEnabled()) {
5553
5918
  try {
5554
5919
  await this.startDev(threadId);
@@ -5833,7 +6198,7 @@ var Orchestrator = class {
5833
6198
  }
5834
6199
  const isBrightsy = fresh.agent === "brightsy";
5835
6200
  const isOrchestration = isOrchestratorThread(fresh);
5836
- const { autoRenameBranchEnabled, getGithubGitAuthMode } = await import("./app-settings-YFWV7MOG.js");
6201
+ const { autoRenameBranchEnabled, getGithubGitAuthMode } = await import("./app-settings-6RPALX4J.js");
5837
6202
  const gitAuthMode = getGithubGitAuthMode();
5838
6203
  const worktreeDirective = isBrightsy || isOrchestration ? null : formatWorktreeDirective(fresh, {
5839
6204
  githubSlug: await resolveGithubRepoSlug(fresh.worktreePath).catch(
@@ -6370,7 +6735,8 @@ var Orchestrator = class {
6370
6735
  agent,
6371
6736
  repoPath: opts.repoPath,
6372
6737
  title: opts.title ? `${opts.title} (${agent})` : `best-of-n: ${opts.prompt.slice(0, 48)} (${agent})`,
6373
- prompt: opts.prompt
6738
+ prompt: opts.prompt,
6739
+ reuseExisting: false
6374
6740
  });
6375
6741
  created.push(thread);
6376
6742
  }
@@ -6607,6 +6973,8 @@ var Orchestrator = class {
6607
6973
  if (meta.url && meta.url !== thread.prUrl) patch.prUrl = meta.url;
6608
6974
  if (meta.title && meta.title !== thread.prTitle) patch.prTitle = meta.title;
6609
6975
  if (nextState && nextState !== prevState) patch.prState = nextState;
6976
+ const nextDraft = Boolean(meta.isDraft) && nextState !== "MERGED" && nextState !== "CLOSED";
6977
+ if (nextDraft !== Boolean(thread.prIsDraft)) patch.prIsDraft = nextDraft;
6610
6978
  if (thread.skipAutoArchiveOnMerge && nextState && nextState !== "MERGED" && nextState !== "CLOSED") {
6611
6979
  patch.skipAutoArchiveOnMerge = false;
6612
6980
  }
@@ -6617,7 +6985,7 @@ var Orchestrator = class {
6617
6985
  updateThread(thread.id, { title: meta.title });
6618
6986
  }
6619
6987
  }
6620
- const { autoArchiveOnMergeEnabled } = await import("./app-settings-YFWV7MOG.js");
6988
+ const { autoArchiveOnMergeEnabled } = await import("./app-settings-6RPALX4J.js");
6621
6989
  const latest = this.requireThread(thread.id);
6622
6990
  if (!shouldAutoArchiveOnPrMerge({
6623
6991
  previousPrState: prevState || null,
@@ -6631,7 +6999,7 @@ var Orchestrator = class {
6631
6999
  }
6632
7000
  const siblings = threadsSharingWorktree(latest.worktreePath);
6633
7001
  for (const t of siblings) {
6634
- const sibPatch = { prState: "MERGED" };
7002
+ const sibPatch = { prState: "MERGED", prIsDraft: false };
6635
7003
  if (meta.url && meta.url !== t.prUrl) sibPatch.prUrl = meta.url;
6636
7004
  if (meta.title && meta.title !== t.prTitle) sibPatch.prTitle = meta.title;
6637
7005
  if (Object.keys(sibPatch).length > 0) updateThread(t.id, sibPatch);
@@ -6805,10 +7173,15 @@ var Orchestrator = class {
6805
7173
  web: action === "create-web"
6806
7174
  });
6807
7175
  if (!url) return;
6808
- const patch = { prUrl: url, prTitle: meta.title };
7176
+ const patch = {
7177
+ prUrl: url,
7178
+ prTitle: meta.title,
7179
+ prIsDraft: action === "create-draft"
7180
+ };
6809
7181
  try {
6810
7182
  const fetched = await getPrMeta(cwd, url);
6811
7183
  if (fetched?.title) patch.prTitle = fetched.title;
7184
+ if (fetched) patch.prIsDraft = Boolean(fetched.isDraft);
6812
7185
  } catch {
6813
7186
  }
6814
7187
  updateThread(thread.id, patch);
@@ -6922,7 +7295,7 @@ var Orchestrator = class {
6922
7295
  this.emit({ type: "status_changed", threadId: archived.id, status: "archived" });
6923
7296
  if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
6924
7297
  try {
6925
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-POWKTH2D.js");
7298
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-O4QZOASB.js");
6926
7299
  await ensureWorkspace2(thread.repoPath);
6927
7300
  } catch {
6928
7301
  }
@@ -6949,7 +7322,7 @@ var Orchestrator = class {
6949
7322
  await runArchiveScript(thread.repoPath, thread.worktreePath);
6950
7323
  } catch {
6951
7324
  }
6952
- const { deleteBranchOnPurgeEnabled } = await import("./app-settings-YFWV7MOG.js");
7325
+ const { deleteBranchOnPurgeEnabled } = await import("./app-settings-6RPALX4J.js");
6953
7326
  const deleteBranch = opts?.deleteBranch ?? deleteBranchOnPurgeEnabled();
6954
7327
  await removeWorktree(thread.repoPath, thread.worktreePath, {
6955
7328
  deleteBranch: deleteBranch ? thread.branchName : void 0
@@ -6976,7 +7349,7 @@ var Orchestrator = class {
6976
7349
  `Cowboy checkout missing: ${thread.worktreePath}. Re-add the project folder, then restore.`
6977
7350
  );
6978
7351
  }
6979
- const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-T57RD7BQ.js");
7352
+ const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-JVGSLSLJ.js");
6980
7353
  const slug = thread.worktreePath.split("/").pop();
6981
7354
  const dest = thread.worktreePath;
6982
7355
  await withRepoGitLock(thread.repoPath, async () => {
@@ -7084,7 +7457,7 @@ async function startOrchestration(opts) {
7084
7457
  sourceRef: "default",
7085
7458
  ...createOpts
7086
7459
  }).catch(async () => {
7087
- const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-T57RD7BQ.js");
7460
+ const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-JVGSLSLJ.js");
7088
7461
  const repo = await resolveRepoRoot2(repoPath);
7089
7462
  const def = await resolveDefaultBranch2(repo);
7090
7463
  return orch.createThread({
@@ -7094,7 +7467,7 @@ async function startOrchestration(opts) {
7094
7467
  repoPath: repo
7095
7468
  });
7096
7469
  });
7097
- const { updateThread: upd } = await import("./thread-store-JR235AWK.js");
7470
+ const { updateThread: upd } = await import("./thread-store-5MLYY35S.js");
7098
7471
  const updated = upd(thread.id, {
7099
7472
  sourceType: "orchestration",
7100
7473
  sourceRef: goal
@@ -7145,10 +7518,7 @@ export {
7145
7518
  formatSlackReplyContinuePrompt,
7146
7519
  listSlackOutboundWatches,
7147
7520
  recordSlackOutboundWatch,
7148
- listSlackReplyBadges,
7149
- dismissSlackReplyBadge,
7150
- permalinkForSlackReplyBadge,
7151
- refreshSlackReplyBadges,
7521
+ pollSlackOutboundWatches,
7152
7522
  applyPromptCacheTtlEnv,
7153
7523
  spawnAgentTurn,
7154
7524
  AGENT_GIT_ACTIONS,
@@ -7219,6 +7589,23 @@ export {
7219
7589
  cloneRepoIntoSideboard,
7220
7590
  detectAgents,
7221
7591
  requireAgent,
7592
+ isHomeBoardThread,
7593
+ BOARD_COLUMN_DEFS,
7594
+ HOME_BOARD_CACHE_TTL_MS,
7595
+ groupHomeBoardWorktrees,
7596
+ classifyWorktreeColumn,
7597
+ worktreeBoardStatus,
7598
+ dedupeBoardIssues,
7599
+ issueNeedsWorkspacePick,
7600
+ findLiveThreadForCreate,
7601
+ boardPinIdentity,
7602
+ findBoardPin,
7603
+ syncBoardPins,
7604
+ dedupeBoardPrs,
7605
+ findBoardIssue,
7606
+ findBoardPr,
7607
+ assembleHomeBoard,
7608
+ HOME_BOARD_AGENT_HINT,
7222
7609
  createThread,
7223
7610
  listLinearIssues,
7224
7611
  isCowboyThread,