@sideboard-ai/core 0.1.125 → 0.1.130

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-GKY2GR2J.js → chunk-FMBLOOAO.js} +484 -37
  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-UAVZ2JSO.js → chunk-UDQI3N47.js} +14 -6
  17. package/dist/{chunk-HZLE6LJJ.js → chunk-VE22NWBD.js} +2 -2
  18. package/dist/{chunk-ED27HCPV.js → chunk-YALVX3UE.js} +496 -37
  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 +1233 -170
  26. package/dist/index.d.cts +233 -10
  27. package/dist/index.d.ts +233 -10
  28. package/dist/index.js +695 -131
  29. package/dist/mcp/run-stdio.cjs +1081 -149
  30. package/dist/mcp/run-stdio.js +579 -123
  31. package/dist/{orchestrator-FZYM7I42.js → orchestrator-3ZSFTG72.js} +8 -8
  32. package/dist/{orchestrator-HN3LYHFN.js → orchestrator-G6DU3AF3.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,
@@ -439,7 +441,7 @@ async function continueSourceThread(threadId, prompt) {
439
441
  await continueOnReply(threadId, prompt);
440
442
  return;
441
443
  }
442
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-HN3LYHFN.js");
444
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-G6DU3AF3.js");
443
445
  await getOrchestrator2().send(threadId, prompt);
444
446
  } catch {
445
447
  }
@@ -719,9 +721,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
719
721
  `Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
720
722
  );
721
723
  }
722
- const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-U5UOVXKQ.js");
724
+ const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-BMWP7YZC.js");
723
725
  if (isGlobalThread2(thread)) {
724
- const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-AZ3WRDNC.js");
726
+ const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-MEEUOEF2.js");
725
727
  ensureGlobalCoordinatorCwd2(
726
728
  isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
727
729
  );
@@ -1287,6 +1289,7 @@ function worktreeBindingFrom(from) {
1287
1289
  prUrl: from.prUrl,
1288
1290
  prTitle: from.prTitle,
1289
1291
  prState: from.prState,
1292
+ prIsDraft: from.prIsDraft,
1290
1293
  skipAutoArchiveOnMerge: from.skipAutoArchiveOnMerge,
1291
1294
  stackId: from.stackId,
1292
1295
  stackLayer: from.stackLayer,
@@ -2294,8 +2297,425 @@ async function requireAgent(agent, opts) {
2294
2297
  return status;
2295
2298
  }
2296
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
+
2297
2697
  // src/threads/create.ts
2298
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
+ }
2299
2719
  const resolved = resolveNewThreadOptions({
2300
2720
  agent: input.agent,
2301
2721
  model: input.model,
@@ -2303,12 +2723,8 @@ async function createThread(input, _onSetupLine) {
2303
2723
  fast: input.fast
2304
2724
  });
2305
2725
  await requireAgent(resolved.agent);
2306
- const repoPath = await resolveRepoRoot(input.repoPath);
2307
- if (!existsSync7(repoPath)) {
2308
- throw new Error(`Repo not found: ${repoPath}`);
2309
- }
2310
2726
  if (input.cowboy) {
2311
- const { cowboyModeEnabled } = await import("./app-settings-YFWV7MOG.js");
2727
+ const { cowboyModeEnabled } = await import("./app-settings-6RPALX4J.js");
2312
2728
  if (!cowboyModeEnabled()) {
2313
2729
  throw new Error(
2314
2730
  "Cowboy mode is off. Enable it in Settings \u2192 Advanced."
@@ -2419,7 +2835,7 @@ async function createThread(input, _onSetupLine) {
2419
2835
  return readThread(thread.id) ?? thread;
2420
2836
  }
2421
2837
  async function listLinearIssues(agent, repoPath) {
2422
- const { getAdapter: getAdapter2 } = await import("./agents-DCXXLTXF.js");
2838
+ const { getAdapter: getAdapter2 } = await import("./agents-X2I3RNCY.js");
2423
2839
  await requireAgent(agent, { requireLinear: true });
2424
2840
  const adapter = getAdapter2(agent);
2425
2841
  if (!adapter.listLinearIssues) {
@@ -2605,7 +3021,8 @@ async function forkThreadWorktree(input, onSetupLine) {
2605
3021
  planMode: from.planMode,
2606
3022
  title: input.title?.trim() || void 0,
2607
3023
  parentThreadId: from.id,
2608
- attachments: [attachment]
3024
+ attachments: [attachment],
3025
+ reuseExisting: false
2609
3026
  },
2610
3027
  onSetupLine
2611
3028
  );
@@ -2685,7 +3102,7 @@ function buildQuotaHandoffAttachment(from, limitText, fallbackAgent) {
2685
3102
  "",
2686
3103
  `## Instructions`,
2687
3104
  `- Continue fleet orchestration from this handoff.`,
2688
- `- 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.`,
2689
3106
  `- Leave model Auto unless there is a specific reason to pin one.`,
2690
3107
  `- Do not wait on the limited ${from.agent} account; keep going on ${fallbackAgent}.`
2691
3108
  ].join("\n");
@@ -2698,9 +3115,9 @@ function buildQuotaHandoffAttachment(from, limitText, fallbackAgent) {
2698
3115
  }
2699
3116
  var QUOTA_CONTINUE_PROMPT = (fromAgent, fallback) => [
2700
3117
  `${fromAgent} hit a session/usage limit. Continue this orchestration on ${fallback} using the attached handoff.`,
2701
- "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."
2702
3119
  ].join(" ");
2703
- 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.";
2704
3121
  function createQuotaFailoverChat(from, fallbackAgent, limitText) {
2705
3122
  const handoff = buildQuotaHandoffAttachment(from, limitText, fallbackAgent);
2706
3123
  const tab = createChatTab({
@@ -2815,7 +3232,7 @@ async function adoptThread(input) {
2815
3232
  messages: input.messages ?? []
2816
3233
  });
2817
3234
  writeThread(thread);
2818
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-POWKTH2D.js");
3235
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-O4QZOASB.js");
2819
3236
  await ensureWorkspace2(repoPath);
2820
3237
  return thread;
2821
3238
  }
@@ -4728,7 +5145,8 @@ function formatArtifactDirective() {
4728
5145
  "```html",
4729
5146
  "<!DOCTYPE html><html><head><title>Demo</title></head><body><h1>Hi</h1></body></html>",
4730
5147
  "```",
4731
- "2) Or call Sideboard MCP `present_artifact` with title, type (html|svg|markdown), and content \u2014 not both a fence and this tool for the same body.",
5148
+ "2) Or call Sideboard MCP `present_artifact` with title, type (html|svg|markdown|react|log), and content \u2014 not both a fence and this tool for the same body.",
5149
+ " type=log is append-only: same `artifact_id`, `content` = new lines only (plus optional status/phase). Do not resend the full log or wrap it in HTML.",
4732
5150
  "CMS / JSON Schema forms & tables (Brightsy or any schema+schemaUi source):",
4733
5151
  "3) Call Sideboard MCP `present_schema` when the user needs to filter, edit, publish, or persist rows \u2014 including after you already showed a markdown table, if they then ask for an editable table. If they only need to read the data, a markdown table is enough. When you do call it, pass title, mode (table|form), and either:",
4734
5152
  " - datasource=brightsy + resource_id (record type UUID) after fetching types via Brightsy MCP, or",
@@ -4743,7 +5161,7 @@ function formatArtifactDirective() {
4743
5161
  ].join("\n");
4744
5162
  }
4745
5163
  function formatUiReminder() {
4746
- return "Sideboard UI: markdown table is enough to read data; present_schema if they ask to edit/filter (even after markdown); present_files for the file manager. html fence or present_artifact, not both for the same document. ask_user only for a real multiple-choice (not hellos or \u201Cwhat next?\u201D) \u2014 reply in chat. Do not say artifacts/CMS UI are unavailable.";
5164
+ return "Sideboard UI: markdown table is enough to read data; present_schema if they ask to edit/filter (even after markdown); present_files for the file manager. html fence or present_artifact, not both for the same document. type=log appends (same artifact_id, new lines only). ask_user only for a real multiple-choice (not hellos or \u201Cwhat next?\u201D) \u2014 reply in chat. Do not say artifacts/CMS UI are unavailable.";
4747
5165
  }
4748
5166
  var FILES_BY_AGENT = {
4749
5167
  claude: [
@@ -5088,7 +5506,7 @@ function formatScheduledPrompt(name, prompt) {
5088
5506
  ${prompt}`;
5089
5507
  }
5090
5508
  async function defaultDeps() {
5091
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-HN3LYHFN.js");
5509
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-G6DU3AF3.js");
5092
5510
  const orch = getOrchestrator2();
5093
5511
  return {
5094
5512
  findThread: (id) => findThreadByRef(id),
@@ -5242,7 +5660,7 @@ var Orchestrator = class {
5242
5660
  maxConcurrent;
5243
5661
  runningCount = 0;
5244
5662
  constructor(opts) {
5245
- this.maxConcurrent = opts?.maxConcurrent ?? 3;
5663
+ this.maxConcurrent = opts?.maxConcurrent ?? 5;
5246
5664
  }
5247
5665
  on(listener) {
5248
5666
  this.events.on("event", listener);
@@ -5308,7 +5726,7 @@ var Orchestrator = class {
5308
5726
  orphans: orphans.map((o) => ({ path: o.path, repoPath: o.repoPath }))
5309
5727
  });
5310
5728
  }
5311
- const { autoCleanupOrphansEnabled } = await import("./app-settings-YFWV7MOG.js");
5729
+ const { autoCleanupOrphansEnabled } = await import("./app-settings-6RPALX4J.js");
5312
5730
  if (autoCleanupOrphansEnabled() && shouldRunWorktreeCleanup() && orphans.length > 0) {
5313
5731
  await cleanupOrphanWorktrees({ repoPaths });
5314
5732
  }
@@ -5459,7 +5877,23 @@ var Orchestrator = class {
5459
5877
  return findThreadByRef(idOrRef) ?? readThread(idOrRef);
5460
5878
  }
5461
5879
  async createThread(input) {
5880
+ const prior = new Set(
5881
+ listThreads({ includeArchived: false }).map((t) => t.id)
5882
+ );
5462
5883
  const thread = await createThread(input);
5884
+ if (prior.has(thread.id)) {
5885
+ if (input.prompt?.trim()) {
5886
+ try {
5887
+ await this.send(thread.id, input.prompt.trim());
5888
+ } catch (err) {
5889
+ const message = err instanceof Error ? err.message : String(err);
5890
+ updateThread(thread.id, {
5891
+ lastError: `First prompt failed: ${message}`
5892
+ });
5893
+ }
5894
+ }
5895
+ return thread;
5896
+ }
5463
5897
  this.emit({ type: "status_changed", threadId: thread.id, status: thread.status });
5464
5898
  void this.finishCreateThread(thread.id, input.prompt?.trim() || void 0);
5465
5899
  return thread;
@@ -5480,7 +5914,7 @@ var Orchestrator = class {
5480
5914
  }
5481
5915
  await setup;
5482
5916
  if (skipSetup) return;
5483
- const { autoRunAfterSetupEnabled } = await import("./app-settings-YFWV7MOG.js");
5917
+ const { autoRunAfterSetupEnabled } = await import("./app-settings-6RPALX4J.js");
5484
5918
  if (autoRunAfterSetupEnabled()) {
5485
5919
  try {
5486
5920
  await this.startDev(threadId);
@@ -5765,7 +6199,7 @@ var Orchestrator = class {
5765
6199
  }
5766
6200
  const isBrightsy = fresh.agent === "brightsy";
5767
6201
  const isOrchestration = isOrchestratorThread(fresh);
5768
- const { autoRenameBranchEnabled, getGithubGitAuthMode } = await import("./app-settings-YFWV7MOG.js");
6202
+ const { autoRenameBranchEnabled, getGithubGitAuthMode } = await import("./app-settings-6RPALX4J.js");
5769
6203
  const gitAuthMode = getGithubGitAuthMode();
5770
6204
  const worktreeDirective = isBrightsy || isOrchestration ? null : formatWorktreeDirective(fresh, {
5771
6205
  githubSlug: await resolveGithubRepoSlug(fresh.worktreePath).catch(
@@ -6302,7 +6736,8 @@ var Orchestrator = class {
6302
6736
  agent,
6303
6737
  repoPath: opts.repoPath,
6304
6738
  title: opts.title ? `${opts.title} (${agent})` : `best-of-n: ${opts.prompt.slice(0, 48)} (${agent})`,
6305
- prompt: opts.prompt
6739
+ prompt: opts.prompt,
6740
+ reuseExisting: false
6306
6741
  });
6307
6742
  created.push(thread);
6308
6743
  }
@@ -6539,6 +6974,8 @@ var Orchestrator = class {
6539
6974
  if (meta.url && meta.url !== thread.prUrl) patch.prUrl = meta.url;
6540
6975
  if (meta.title && meta.title !== thread.prTitle) patch.prTitle = meta.title;
6541
6976
  if (nextState && nextState !== prevState) patch.prState = nextState;
6977
+ const nextDraft = Boolean(meta.isDraft) && nextState !== "MERGED" && nextState !== "CLOSED";
6978
+ if (nextDraft !== Boolean(thread.prIsDraft)) patch.prIsDraft = nextDraft;
6542
6979
  if (thread.skipAutoArchiveOnMerge && nextState && nextState !== "MERGED" && nextState !== "CLOSED") {
6543
6980
  patch.skipAutoArchiveOnMerge = false;
6544
6981
  }
@@ -6549,7 +6986,7 @@ var Orchestrator = class {
6549
6986
  updateThread(thread.id, { title: meta.title });
6550
6987
  }
6551
6988
  }
6552
- const { autoArchiveOnMergeEnabled } = await import("./app-settings-YFWV7MOG.js");
6989
+ const { autoArchiveOnMergeEnabled } = await import("./app-settings-6RPALX4J.js");
6553
6990
  const latest = this.requireThread(thread.id);
6554
6991
  if (!shouldAutoArchiveOnPrMerge({
6555
6992
  previousPrState: prevState || null,
@@ -6563,7 +7000,7 @@ var Orchestrator = class {
6563
7000
  }
6564
7001
  const siblings = threadsSharingWorktree(latest.worktreePath);
6565
7002
  for (const t of siblings) {
6566
- const sibPatch = { prState: "MERGED" };
7003
+ const sibPatch = { prState: "MERGED", prIsDraft: false };
6567
7004
  if (meta.url && meta.url !== t.prUrl) sibPatch.prUrl = meta.url;
6568
7005
  if (meta.title && meta.title !== t.prTitle) sibPatch.prTitle = meta.title;
6569
7006
  if (Object.keys(sibPatch).length > 0) updateThread(t.id, sibPatch);
@@ -6737,10 +7174,15 @@ var Orchestrator = class {
6737
7174
  web: action === "create-web"
6738
7175
  });
6739
7176
  if (!url) return;
6740
- const patch = { prUrl: url, prTitle: meta.title };
7177
+ const patch = {
7178
+ prUrl: url,
7179
+ prTitle: meta.title,
7180
+ prIsDraft: action === "create-draft"
7181
+ };
6741
7182
  try {
6742
7183
  const fetched = await getPrMeta(cwd, url);
6743
7184
  if (fetched?.title) patch.prTitle = fetched.title;
7185
+ if (fetched) patch.prIsDraft = Boolean(fetched.isDraft);
6744
7186
  } catch {
6745
7187
  }
6746
7188
  updateThread(thread.id, patch);
@@ -6854,7 +7296,7 @@ var Orchestrator = class {
6854
7296
  this.emit({ type: "status_changed", threadId: archived.id, status: "archived" });
6855
7297
  if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
6856
7298
  try {
6857
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-POWKTH2D.js");
7299
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-O4QZOASB.js");
6858
7300
  await ensureWorkspace2(thread.repoPath);
6859
7301
  } catch {
6860
7302
  }
@@ -6881,7 +7323,7 @@ var Orchestrator = class {
6881
7323
  await runArchiveScript(thread.repoPath, thread.worktreePath);
6882
7324
  } catch {
6883
7325
  }
6884
- const { deleteBranchOnPurgeEnabled } = await import("./app-settings-YFWV7MOG.js");
7326
+ const { deleteBranchOnPurgeEnabled } = await import("./app-settings-6RPALX4J.js");
6885
7327
  const deleteBranch = opts?.deleteBranch ?? deleteBranchOnPurgeEnabled();
6886
7328
  await removeWorktree(thread.repoPath, thread.worktreePath, {
6887
7329
  deleteBranch: deleteBranch ? thread.branchName : void 0
@@ -6908,7 +7350,7 @@ var Orchestrator = class {
6908
7350
  `Cowboy checkout missing: ${thread.worktreePath}. Re-add the project folder, then restore.`
6909
7351
  );
6910
7352
  }
6911
- const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-T57RD7BQ.js");
7353
+ const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-JVGSLSLJ.js");
6912
7354
  const slug = thread.worktreePath.split("/").pop();
6913
7355
  const dest = thread.worktreePath;
6914
7356
  await withRepoGitLock(thread.repoPath, async () => {
@@ -7016,7 +7458,7 @@ async function startOrchestration(opts) {
7016
7458
  sourceRef: "default",
7017
7459
  ...createOpts
7018
7460
  }).catch(async () => {
7019
- const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-T57RD7BQ.js");
7461
+ const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-JVGSLSLJ.js");
7020
7462
  const repo = await resolveRepoRoot2(repoPath);
7021
7463
  const def = await resolveDefaultBranch2(repo);
7022
7464
  return orch.createThread({
@@ -7026,7 +7468,7 @@ async function startOrchestration(opts) {
7026
7468
  repoPath: repo
7027
7469
  });
7028
7470
  });
7029
- const { updateThread: upd } = await import("./thread-store-JR235AWK.js");
7471
+ const { updateThread: upd } = await import("./thread-store-5MLYY35S.js");
7030
7472
  const updated = upd(thread.id, {
7031
7473
  sourceType: "orchestration",
7032
7474
  sourceRef: goal
@@ -7148,6 +7590,23 @@ export {
7148
7590
  cloneRepoIntoSideboard,
7149
7591
  detectAgents,
7150
7592
  requireAgent,
7593
+ isHomeBoardThread,
7594
+ BOARD_COLUMN_DEFS,
7595
+ HOME_BOARD_CACHE_TTL_MS,
7596
+ groupHomeBoardWorktrees,
7597
+ classifyWorktreeColumn,
7598
+ worktreeBoardStatus,
7599
+ dedupeBoardIssues,
7600
+ issueNeedsWorkspacePick,
7601
+ findLiveThreadForCreate,
7602
+ boardPinIdentity,
7603
+ findBoardPin,
7604
+ syncBoardPins,
7605
+ dedupeBoardPrs,
7606
+ findBoardIssue,
7607
+ findBoardPr,
7608
+ assembleHomeBoard,
7609
+ HOME_BOARD_AGENT_HINT,
7151
7610
  createThread,
7152
7611
  listLinearIssues,
7153
7612
  isCowboyThread,