@sideboard-ai/core 0.1.125 → 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-GKY2GR2J.js → chunk-DLM5LSQW.js} +481 -35
  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-ED27HCPV.js → chunk-T7KJQJAX.js} +493 -35
  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 +1203 -157
  26. package/dist/index.d.cts +233 -10
  27. package/dist/index.d.ts +233 -10
  28. package/dist/index.js +668 -120
  29. package/dist/mcp/run-stdio.cjs +1051 -136
  30. package/dist/mcp/run-stdio.js +552 -112
  31. package/dist/{orchestrator-FZYM7I42.js → orchestrator-3JLPPTCF.js} +8 -8
  32. package/dist/{orchestrator-HN3LYHFN.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
@@ -29,13 +29,13 @@ import {
29
29
  summarizeTurnStderr,
30
30
  toolDescription,
31
31
  turnFailChatText
32
- } from "./chunk-JNAIKICO.js";
32
+ } from "./chunk-ZMUOPTKZ.js";
33
33
  import {
34
34
  addWorkspace,
35
35
  ensureWorkspace,
36
36
  removeWorkspace,
37
37
  syncWorkspacesFromThreads
38
- } from "./chunk-RBVVWBVB.js";
38
+ } from "./chunk-SYLTXDRF.js";
39
39
  import {
40
40
  extractPresentedPlan,
41
41
  readPlanFile,
@@ -56,18 +56,19 @@ import {
56
56
  isOrchestratorThread,
57
57
  isSlackCoordinatorThread,
58
58
  orchestratorSessionPoisonedByBuiltins
59
- } from "./chunk-O43IRQ2Y.js";
59
+ } from "./chunk-MNL4FSKY.js";
60
60
  import {
61
61
  SLACK_REPLY_FORMATTING,
62
62
  coordinatorSystemPrompt,
63
63
  coordinatorTurnReminder,
64
64
  enrichWorkspacesWithGithub,
65
65
  ensureGlobalCoordinatorCwd
66
- } from "./chunk-TRTWH6C2.js";
66
+ } from "./chunk-RSFCB23A.js";
67
67
  import {
68
68
  addPrStackLayer,
69
69
  allocateTeamName,
70
70
  allocateTeamSlug,
71
+ canonicalizeRepoPath,
71
72
  commitAll,
72
73
  createExistingBranchWorktree,
73
74
  createOrUpdatePr,
@@ -105,8 +106,9 @@ import {
105
106
  takenSlugsFromThread,
106
107
  threadDisplayLabel,
107
108
  withRepoGitLock,
109
+ worktreeDisplayLabelForGroup,
108
110
  worktreeNameFromPath
109
- } from "./chunk-OHEFHIG3.js";
111
+ } from "./chunk-HRSBZIXS.js";
110
112
  import {
111
113
  ATTACHMENTS_DIR,
112
114
  LEGACY_ATTACHMENTS_DIR,
@@ -123,7 +125,7 @@ import {
123
125
  updateThread,
124
126
  withThreadLock,
125
127
  writeThread
126
- } from "./chunk-RG737OWT.js";
128
+ } from "./chunk-G6X6UFJO.js";
127
129
  import {
128
130
  clearStaleIndexLocks,
129
131
  enrichPathWithNpmGlobalBin,
@@ -145,7 +147,7 @@ import {
145
147
  stripNestedElectronEnv,
146
148
  updateAdvancedSettings,
147
149
  writeSecureJson
148
- } from "./chunk-BMIRX64H.js";
150
+ } from "./chunk-ZND6XV6J.js";
149
151
  import {
150
152
  writePrivateFile
151
153
  } from "./chunk-NSTQ6QKD.js";
@@ -378,7 +380,7 @@ async function continueSourceThread(threadId, prompt) {
378
380
  await continueOnReply(threadId, prompt);
379
381
  return;
380
382
  }
381
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-FZYM7I42.js");
383
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-3JLPPTCF.js");
382
384
  await getOrchestrator2().send(threadId, prompt);
383
385
  } catch {
384
386
  }
@@ -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-4YNYDMLQ.js");
724
+ const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-KNESLWKG.js");
723
725
  if (isGlobalThread2(thread)) {
724
- const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-LEU62W25.js");
726
+ const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-SPJSGAQS.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,
@@ -2262,8 +2265,418 @@ async function requireAgent(agent, opts) {
2262
2265
  return status;
2263
2266
  }
2264
2267
 
2268
+ // src/board/home-board.ts
2269
+ var GLOBAL_WORKSPACE_ID = "__global__";
2270
+ function isHomeBoardThread(thread) {
2271
+ if (thread.sourceType === "orchestration") return false;
2272
+ if (thread.repoPath === GLOBAL_WORKSPACE_ID) return false;
2273
+ return true;
2274
+ }
2275
+ var BOARD_COLUMN_DEFS = [
2276
+ { id: "new", title: "New" },
2277
+ { id: "draft", title: "Draft" },
2278
+ { id: "review", title: "Review" },
2279
+ { id: "done", title: "Merged" }
2280
+ ];
2281
+ var HOME_BOARD_CACHE_TTL_MS = 15 * 60 * 1e3;
2282
+ function isOpenPrState(prUrl, prState) {
2283
+ if (!prUrl?.trim()) return false;
2284
+ const state = (prState ?? "OPEN").trim().toUpperCase();
2285
+ return state !== "MERGED" && state !== "CLOSED";
2286
+ }
2287
+ function isMergedPrState(prUrl, prState) {
2288
+ if (!prUrl?.trim()) return false;
2289
+ return (prState ?? "").trim().toUpperCase() === "MERGED";
2290
+ }
2291
+ function groupHomeBoardWorktrees(threads) {
2292
+ const map = /* @__PURE__ */ new Map();
2293
+ for (const t of threads) {
2294
+ const key = normalizeWorktreePath(t.worktreePath);
2295
+ const list = map.get(key) ?? [];
2296
+ list.push(t);
2297
+ map.set(key, list);
2298
+ }
2299
+ return [...map.values()].map((list) => list.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt))).sort((a, b) => (b[0]?.updatedAt ?? "").localeCompare(a[0]?.updatedAt ?? ""));
2300
+ }
2301
+ function classifyWorktreeColumn(group) {
2302
+ if (group.some((t) => isMergedPrState(t.prUrl, t.prState))) return "done";
2303
+ if (group.some((t) => isOpenPrState(t.prUrl, t.prState) && !t.prIsDraft)) {
2304
+ return "review";
2305
+ }
2306
+ if (group.some((t) => isOpenPrState(t.prUrl, t.prState) && t.prIsDraft)) {
2307
+ return "draft";
2308
+ }
2309
+ return "new";
2310
+ }
2311
+ function worktreeBoardStatus(group) {
2312
+ const order = ["running", "queued", "error", "broken"];
2313
+ for (const status of order) {
2314
+ if (group.some((t) => t.status === status)) return status;
2315
+ }
2316
+ return group[0]?.status ?? "idle";
2317
+ }
2318
+ function normalizeIssueKey(value) {
2319
+ return value.trim().toLowerCase().replace(/^#/, "");
2320
+ }
2321
+ function threadMatchesIssue(thread, issue) {
2322
+ const identRaw = issue.identifier.trim().toLowerCase();
2323
+ const identKey = normalizeIssueKey(issue.identifier);
2324
+ const title = issue.title.trim().toLowerCase();
2325
+ const refRaw = (thread.sourceRef ?? "").trim().toLowerCase();
2326
+ const refKey = normalizeIssueKey(thread.sourceRef ?? "");
2327
+ const threadTitle = (thread.title ?? "").trim().toLowerCase();
2328
+ if (thread.sourceType === "ticket" && identKey && refKey === identKey) return true;
2329
+ if (identRaw && (refRaw === identRaw || threadTitle.includes(identRaw))) return true;
2330
+ if (title && threadTitle === title) return true;
2331
+ return false;
2332
+ }
2333
+ function dedupeBoardIssues(issues) {
2334
+ const seen = /* @__PURE__ */ new Set();
2335
+ const out = [];
2336
+ for (const issue of issues) {
2337
+ const key = [
2338
+ issue.repoPath,
2339
+ issue.provider ?? "",
2340
+ issue.id || issue.identifier
2341
+ ].join(":");
2342
+ if (seen.has(key)) continue;
2343
+ seen.add(key);
2344
+ out.push(issue);
2345
+ }
2346
+ return out;
2347
+ }
2348
+ function issueNeedsWorkspacePick(provider, workspaceCount) {
2349
+ if (workspaceCount <= 1) return false;
2350
+ return provider !== "github";
2351
+ }
2352
+ function boardPrKey(pr) {
2353
+ return pr.url?.trim() || `${pr.repoPath}::${pr.number}`;
2354
+ }
2355
+ function urlsMatch(a, b) {
2356
+ const norm = (u) => u.trim().replace(/\/+$/, "").toLowerCase();
2357
+ return Boolean(a && b && norm(a) === norm(b));
2358
+ }
2359
+ function threadMatchesPr(thread, pr) {
2360
+ const num = String(pr.number);
2361
+ if (thread.sourceType === "pr" && normalizeIssueKey(thread.sourceRef ?? "") === num) {
2362
+ return true;
2363
+ }
2364
+ if (thread.prUrl && pr.url && urlsMatch(thread.prUrl, pr.url)) return true;
2365
+ const head = pr.headRefName.trim().toLowerCase();
2366
+ if (!head) return false;
2367
+ const branch = (thread.branchName ?? "").trim().toLowerCase();
2368
+ const ref = (thread.sourceRef ?? "").trim().replace(/^refs\/heads\//, "").replace(/^origin\//, "").toLowerCase();
2369
+ if (branch === head) return true;
2370
+ if (!ref || /^(main|master|develop|development|trunk|default|head)$/.test(ref)) {
2371
+ return false;
2372
+ }
2373
+ return ref === head;
2374
+ }
2375
+ function normalizeBranchRef(value) {
2376
+ return value.trim().replace(/^refs\/heads\//, "").replace(/^origin\//, "").toLowerCase();
2377
+ }
2378
+ function threadMatchesBranch(thread, pin) {
2379
+ if (pin.repoPath && thread.repoPath && pin.repoPath !== thread.repoPath) return false;
2380
+ const head = normalizeBranchRef(pin.ref === "default" ? "" : pin.ref);
2381
+ if (!head) {
2382
+ return thread.sourceType === "branch" && pin.repoPath === thread.repoPath;
2383
+ }
2384
+ const branch = normalizeBranchRef(thread.branchName ?? "");
2385
+ const ref = normalizeBranchRef(thread.sourceRef ?? "");
2386
+ return branch === head || ref === head;
2387
+ }
2388
+ function sameRepoPath(a, b) {
2389
+ return a.replace(/\/+$/, "") === b.replace(/\/+$/, "");
2390
+ }
2391
+ var DEFAULTISH_BRANCH = /^(main|master|develop|development|trunk|default|head)$/;
2392
+ function isDefaultBranchCreateRef(ref) {
2393
+ const head = normalizeBranchRef(ref);
2394
+ return !head || DEFAULTISH_BRANCH.test(head);
2395
+ }
2396
+ function findLiveThreadForCreate(input, threads) {
2397
+ const live = threads.filter(
2398
+ (t) => t.status !== "archived" && sameRepoPath(t.repoPath, input.repoPath)
2399
+ );
2400
+ if (input.cowboy) {
2401
+ return live.find((t) => t.cowboy);
2402
+ }
2403
+ if (input.sourceType === "ticket") {
2404
+ return live.find(
2405
+ (t) => threadMatchesIssue(t, {
2406
+ identifier: input.sourceRef,
2407
+ title: input.title ?? ""
2408
+ })
2409
+ );
2410
+ }
2411
+ if (input.sourceType === "pr") {
2412
+ const number = Number(normalizeIssueKey(input.sourceRef));
2413
+ return live.find(
2414
+ (t) => threadMatchesPr(t, {
2415
+ number: Number.isFinite(number) ? number : -1,
2416
+ title: input.title ?? "",
2417
+ url: "",
2418
+ headRefName: ""
2419
+ })
2420
+ );
2421
+ }
2422
+ if (input.sourceType === "branch") {
2423
+ if (isDefaultBranchCreateRef(input.sourceRef)) return void 0;
2424
+ return live.find(
2425
+ (t) => threadMatchesBranch(t, {
2426
+ ref: input.sourceRef,
2427
+ repoPath: input.repoPath
2428
+ })
2429
+ );
2430
+ }
2431
+ return void 0;
2432
+ }
2433
+ function findBoardPin(pins, kind, ref, repoPath = "") {
2434
+ const key = normalizeIssueKey(ref);
2435
+ const matches = pins.filter(
2436
+ (pin) => pin.kind === kind && normalizeIssueKey(pin.ref) === key
2437
+ );
2438
+ if (repoPath) {
2439
+ const scoped = matches.filter((pin) => pin.repoPath === repoPath);
2440
+ if (scoped[0]) return scoped[0];
2441
+ }
2442
+ return matches[0];
2443
+ }
2444
+ function syncBoardPins(pins, issues, prs) {
2445
+ return pins.map((pin) => {
2446
+ if (pin.kind === "ticket") {
2447
+ const issue = findBoardIssue(issues, pin.ref, pin.repoPath);
2448
+ if (!issue) {
2449
+ return { ...pin, remoteState: pin.remoteState || "stale" };
2450
+ }
2451
+ return {
2452
+ ...pin,
2453
+ title: issue.title || pin.title,
2454
+ url: issue.url || pin.url,
2455
+ labels: issue.labels,
2456
+ provider: issue.provider ?? pin.provider,
2457
+ assignee: issue.assignee ?? pin.assignee,
2458
+ cycle: issue.cycle?.name ?? pin.cycle,
2459
+ teamKey: issue.teamKey ?? pin.teamKey,
2460
+ remoteState: "open",
2461
+ needsWorkspacePick: issue.needsWorkspacePick
2462
+ };
2463
+ }
2464
+ if (pin.kind === "pr") {
2465
+ const pr = findBoardPr(prs, pin.ref, pin.repoPath);
2466
+ if (!pr) {
2467
+ return { ...pin, remoteState: pin.remoteState === "open" ? "stale" : pin.remoteState || "stale" };
2468
+ }
2469
+ return {
2470
+ ...pin,
2471
+ title: pr.title || pin.title,
2472
+ url: pr.url || pin.url,
2473
+ headRefName: pr.headRefName || pin.headRefName,
2474
+ author: prAuthorLogin(pr) || pin.author,
2475
+ remoteState: "open"
2476
+ };
2477
+ }
2478
+ return pin;
2479
+ });
2480
+ }
2481
+ function dedupeBoardPrs(prs) {
2482
+ const seen = /* @__PURE__ */ new Set();
2483
+ const out = [];
2484
+ for (const pr of prs) {
2485
+ const key = boardPrKey(pr);
2486
+ if (seen.has(key)) continue;
2487
+ seen.add(key);
2488
+ out.push(pr);
2489
+ }
2490
+ return out.sort((a, b) => b.number - a.number);
2491
+ }
2492
+ var BOARD_PAGE_SIZE = 40;
2493
+ function tokenizeQuery(query) {
2494
+ return query.trim().toLowerCase().split(/\s+/).filter(Boolean);
2495
+ }
2496
+ function haystackMatches(haystack, tokens) {
2497
+ if (tokens.length === 0) return true;
2498
+ const h = haystack.toLowerCase();
2499
+ return tokens.every((token) => h.includes(token));
2500
+ }
2501
+ function prAuthorLogin(pr) {
2502
+ return pr.author?.login?.trim() ?? "";
2503
+ }
2504
+ function threadSearchText(thread, workspaceName = "") {
2505
+ return [
2506
+ thread.title,
2507
+ thread.sourceRef,
2508
+ thread.sourceType,
2509
+ thread.agent,
2510
+ thread.status,
2511
+ thread.branchName,
2512
+ thread.prUrl ?? "",
2513
+ workspaceName,
2514
+ thread.repoPath
2515
+ ].join(" ");
2516
+ }
2517
+ function inWorkspace(repoPath, filterRepoPath) {
2518
+ if (!filterRepoPath) return true;
2519
+ return repoPath === filterRepoPath;
2520
+ }
2521
+ function visiblePage(items, shown) {
2522
+ const n = Math.max(0, shown);
2523
+ return {
2524
+ visible: items.slice(0, n),
2525
+ hidden: Math.max(0, items.length - n)
2526
+ };
2527
+ }
2528
+ function emptyColumns() {
2529
+ return {
2530
+ backlog: [],
2531
+ queued: [],
2532
+ running: [],
2533
+ new: [],
2534
+ draft: [],
2535
+ review: [],
2536
+ done: []
2537
+ };
2538
+ }
2539
+ function emptyHidden() {
2540
+ return {
2541
+ backlog: 0,
2542
+ queued: 0,
2543
+ running: 0,
2544
+ new: 0,
2545
+ draft: 0,
2546
+ review: 0,
2547
+ done: 0
2548
+ };
2549
+ }
2550
+ function findBoardIssue(issues, ref, repoPath = "") {
2551
+ const key = normalizeIssueKey(ref);
2552
+ if (!key) return void 0;
2553
+ const matches = issues.filter((issue) => normalizeIssueKey(issue.identifier) === key);
2554
+ if (repoPath) {
2555
+ const scoped = matches.filter((issue) => issue.repoPath === repoPath);
2556
+ if (scoped[0]) return scoped[0];
2557
+ }
2558
+ return matches[0];
2559
+ }
2560
+ function findBoardPr(prs, ref, repoPath = "") {
2561
+ const num = Number(normalizeIssueKey(ref));
2562
+ if (!Number.isFinite(num)) return void 0;
2563
+ const matches = prs.filter((pr) => pr.number === num);
2564
+ if (repoPath) {
2565
+ const scoped = matches.filter((pr) => pr.repoPath === repoPath);
2566
+ if (scoped[0]) return scoped[0];
2567
+ }
2568
+ return matches[0];
2569
+ }
2570
+ function toThreadCard(group) {
2571
+ const thread = group[0];
2572
+ const withPr = group.find((t) => t.prUrl?.trim()) ?? thread;
2573
+ return {
2574
+ kind: "thread",
2575
+ id: thread.id,
2576
+ title: worktreeDisplayLabelForGroup(group),
2577
+ status: worktreeBoardStatus(group),
2578
+ agent: thread.agent,
2579
+ sourceType: thread.sourceType,
2580
+ sourceRef: thread.sourceRef,
2581
+ repoPath: thread.repoPath,
2582
+ prUrl: withPr.prUrl,
2583
+ link: `sideboard://thread/${thread.id}`,
2584
+ ...group.length > 1 ? { chatCount: group.length } : {}
2585
+ };
2586
+ }
2587
+ function threadMatchesKind(thread, kind) {
2588
+ if (kind === "all" || kind === "threads") return true;
2589
+ if (kind === "tickets") return thread.sourceType === "ticket";
2590
+ if (kind === "prs") return thread.sourceType === "pr";
2591
+ if (kind === "branches") return thread.sourceType === "branch" || thread.sourceType === "adopt";
2592
+ return true;
2593
+ }
2594
+ function assembleHomeBoard(input) {
2595
+ const tokens = tokenizeQuery(input.query ?? "");
2596
+ const repo = input.repoPath?.trim() ?? "";
2597
+ const kind = input.kind ?? "all";
2598
+ const limit = Math.max(1, input.limit ?? BOARD_PAGE_SIZE);
2599
+ const wsName = input.workspaceName ?? (() => "");
2600
+ const byUpdated = (a, b) => b.updatedAt.localeCompare(a.updatedAt);
2601
+ const live = input.threads.filter((t) => t.status !== "archived" && isHomeBoardThread(t)).sort(byUpdated);
2602
+ const columns = emptyColumns();
2603
+ const hidden = emptyHidden();
2604
+ const totals = {
2605
+ backlog: 0,
2606
+ queued: 0,
2607
+ running: 0,
2608
+ new: 0,
2609
+ draft: 0,
2610
+ review: 0,
2611
+ done: 0,
2612
+ tickets: 0,
2613
+ prs: 0,
2614
+ branches: 0,
2615
+ threads: 0
2616
+ };
2617
+ const byCol = {
2618
+ queued: [],
2619
+ running: [],
2620
+ new: [],
2621
+ draft: [],
2622
+ review: [],
2623
+ done: []
2624
+ };
2625
+ for (const group of groupHomeBoardWorktrees(live)) {
2626
+ if (!group.some((t) => threadMatchesKind(t, kind))) continue;
2627
+ const col = classifyWorktreeColumn(group);
2628
+ if (col === "backlog") continue;
2629
+ const primary = group[0];
2630
+ if (!inWorkspace(primary.repoPath, repo)) continue;
2631
+ const hay = group.map((t) => threadSearchText(t, wsName(t.repoPath))).join(" ");
2632
+ if (!haystackMatches(hay, tokens)) continue;
2633
+ byCol[col].push(group);
2634
+ }
2635
+ const flat = [...Object.values(byCol)].flatMap((groups) => groups.map((g) => g[0]));
2636
+ totals.tickets = flat.filter((t) => t.sourceType === "ticket").length;
2637
+ totals.prs = flat.filter((t) => t.sourceType === "pr").length;
2638
+ totals.branches = flat.filter((t) => t.sourceType === "branch" || t.sourceType === "adopt").length;
2639
+ totals.threads = Object.values(byCol).reduce((n, list) => n + list.length, 0);
2640
+ for (const col of Object.keys(byCol)) {
2641
+ const cards = byCol[col].map(toThreadCard);
2642
+ totals[col] = cards.length;
2643
+ const page = visiblePage(cards, limit);
2644
+ columns[col] = page.visible;
2645
+ hidden[col] = page.hidden;
2646
+ }
2647
+ if (input.column) {
2648
+ for (const col of Object.keys(columns)) {
2649
+ if (col === input.column) continue;
2650
+ columns[col] = [];
2651
+ hidden[col] = 0;
2652
+ }
2653
+ }
2654
+ return { columns, hidden, totals };
2655
+ }
2656
+ 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.";
2657
+
2265
2658
  // src/threads/create.ts
2266
2659
  async function createThread(input, _onSetupLine) {
2660
+ const repoPath = await resolveRepoRoot(input.repoPath);
2661
+ if (!existsSync7(repoPath)) {
2662
+ throw new Error(`Repo not found: ${repoPath}`);
2663
+ }
2664
+ if (input.reuseExisting !== false) {
2665
+ const existing = findLiveThreadForCreate(
2666
+ {
2667
+ sourceType: input.sourceType,
2668
+ sourceRef: input.sourceRef,
2669
+ repoPath,
2670
+ title: input.title,
2671
+ cowboy: input.cowboy
2672
+ },
2673
+ listThreads({ includeArchived: false }).map((t) => ({
2674
+ ...t,
2675
+ repoPath: canonicalizeRepoPath(t.repoPath)
2676
+ }))
2677
+ );
2678
+ if (existing) return readThread(existing.id) ?? existing;
2679
+ }
2267
2680
  const resolved = resolveNewThreadOptions({
2268
2681
  agent: input.agent,
2269
2682
  model: input.model,
@@ -2271,12 +2684,8 @@ async function createThread(input, _onSetupLine) {
2271
2684
  fast: input.fast
2272
2685
  });
2273
2686
  await requireAgent(resolved.agent);
2274
- const repoPath = await resolveRepoRoot(input.repoPath);
2275
- if (!existsSync7(repoPath)) {
2276
- throw new Error(`Repo not found: ${repoPath}`);
2277
- }
2278
2687
  if (input.cowboy) {
2279
- const { cowboyModeEnabled } = await import("./app-settings-IKFWZDUK.js");
2688
+ const { cowboyModeEnabled } = await import("./app-settings-E7NEQY7D.js");
2280
2689
  if (!cowboyModeEnabled()) {
2281
2690
  throw new Error(
2282
2691
  "Cowboy mode is off. Enable it in Settings \u2192 Advanced."
@@ -2387,7 +2796,7 @@ async function createThread(input, _onSetupLine) {
2387
2796
  return readThread(thread.id) ?? thread;
2388
2797
  }
2389
2798
  async function listLinearIssues(agent, repoPath) {
2390
- const { getAdapter: getAdapter2 } = await import("./agents-DPR2TDNF.js");
2799
+ const { getAdapter: getAdapter2 } = await import("./agents-7BUQY2WX.js");
2391
2800
  await requireAgent(agent, { requireLinear: true });
2392
2801
  const adapter = getAdapter2(agent);
2393
2802
  if (!adapter.listLinearIssues) {
@@ -2573,7 +2982,8 @@ async function forkThreadWorktree(input, onSetupLine) {
2573
2982
  planMode: from.planMode,
2574
2983
  title: input.title?.trim() || void 0,
2575
2984
  parentThreadId: from.id,
2576
- attachments: [attachment]
2985
+ attachments: [attachment],
2986
+ reuseExisting: false
2577
2987
  },
2578
2988
  onSetupLine
2579
2989
  );
@@ -2653,7 +3063,7 @@ function buildQuotaHandoffAttachment(from, limitText, fallbackAgent) {
2653
3063
  "",
2654
3064
  `## Instructions`,
2655
3065
  `- Continue fleet orchestration from this handoff.`,
2656
- `- Prefer Sideboard MCP (list_threads, get_thread, send_to_thread, \u2026) for live status.`,
3066
+ `- Prefer Sideboard MCP (list_board, list_threads, get_thread, send_to_thread, \u2026) for live status.`,
2657
3067
  `- Leave model Auto unless there is a specific reason to pin one.`,
2658
3068
  `- Do not wait on the limited ${from.agent} account; keep going on ${fallbackAgent}.`
2659
3069
  ].join("\n");
@@ -2666,9 +3076,9 @@ function buildQuotaHandoffAttachment(from, limitText, fallbackAgent) {
2666
3076
  }
2667
3077
  var QUOTA_CONTINUE_PROMPT = (fromAgent, fallback) => [
2668
3078
  `${fromAgent} hit a session/usage limit. Continue this orchestration on ${fallback} using the attached handoff.`,
2669
- "Call list_threads for live fleet status, then proceed with the goal. Leave model Auto unless needed."
3079
+ "Call list_board or list_threads for live fleet status, then proceed with the goal. Leave model Auto unless needed."
2670
3080
  ].join(" ");
2671
- 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.";
3081
+ 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.";
2672
3082
  function createQuotaFailoverChat(from, fallbackAgent, limitText) {
2673
3083
  const handoff = buildQuotaHandoffAttachment(from, limitText, fallbackAgent);
2674
3084
  const tab = createChatTab({
@@ -2783,7 +3193,7 @@ async function adoptThread(input) {
2783
3193
  messages: input.messages ?? []
2784
3194
  });
2785
3195
  writeThread(thread);
2786
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-ICK433ZV.js");
3196
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-HSYRYJYV.js");
2787
3197
  await ensureWorkspace2(repoPath);
2788
3198
  return thread;
2789
3199
  }
@@ -4930,7 +5340,7 @@ function formatScheduledPrompt(name, prompt) {
4930
5340
  ${prompt}`;
4931
5341
  }
4932
5342
  async function defaultDeps() {
4933
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-FZYM7I42.js");
5343
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-3JLPPTCF.js");
4934
5344
  const orch = getOrchestrator2();
4935
5345
  return {
4936
5346
  findThread: (id) => findThreadByRef(id),
@@ -5084,7 +5494,7 @@ var Orchestrator = class {
5084
5494
  maxConcurrent;
5085
5495
  runningCount = 0;
5086
5496
  constructor(opts) {
5087
- this.maxConcurrent = opts?.maxConcurrent ?? 3;
5497
+ this.maxConcurrent = opts?.maxConcurrent ?? 5;
5088
5498
  }
5089
5499
  on(listener) {
5090
5500
  this.events.on("event", listener);
@@ -5150,7 +5560,7 @@ var Orchestrator = class {
5150
5560
  orphans: orphans.map((o) => ({ path: o.path, repoPath: o.repoPath }))
5151
5561
  });
5152
5562
  }
5153
- const { autoCleanupOrphansEnabled } = await import("./app-settings-IKFWZDUK.js");
5563
+ const { autoCleanupOrphansEnabled } = await import("./app-settings-E7NEQY7D.js");
5154
5564
  if (autoCleanupOrphansEnabled() && shouldRunWorktreeCleanup() && orphans.length > 0) {
5155
5565
  await cleanupOrphanWorktrees({ repoPaths });
5156
5566
  }
@@ -5301,7 +5711,23 @@ var Orchestrator = class {
5301
5711
  return findThreadByRef(idOrRef) ?? readThread(idOrRef);
5302
5712
  }
5303
5713
  async createThread(input) {
5714
+ const prior = new Set(
5715
+ listThreads({ includeArchived: false }).map((t) => t.id)
5716
+ );
5304
5717
  const thread = await createThread(input);
5718
+ if (prior.has(thread.id)) {
5719
+ if (input.prompt?.trim()) {
5720
+ try {
5721
+ await this.send(thread.id, input.prompt.trim());
5722
+ } catch (err) {
5723
+ const message = err instanceof Error ? err.message : String(err);
5724
+ updateThread(thread.id, {
5725
+ lastError: `First prompt failed: ${message}`
5726
+ });
5727
+ }
5728
+ }
5729
+ return thread;
5730
+ }
5305
5731
  this.emit({ type: "status_changed", threadId: thread.id, status: thread.status });
5306
5732
  void this.finishCreateThread(thread.id, input.prompt?.trim() || void 0);
5307
5733
  return thread;
@@ -5322,7 +5748,7 @@ var Orchestrator = class {
5322
5748
  }
5323
5749
  await setup;
5324
5750
  if (skipSetup) return;
5325
- const { autoRunAfterSetupEnabled } = await import("./app-settings-IKFWZDUK.js");
5751
+ const { autoRunAfterSetupEnabled } = await import("./app-settings-E7NEQY7D.js");
5326
5752
  if (autoRunAfterSetupEnabled()) {
5327
5753
  try {
5328
5754
  await this.startDev(threadId);
@@ -5607,7 +6033,7 @@ var Orchestrator = class {
5607
6033
  }
5608
6034
  const isBrightsy = fresh.agent === "brightsy";
5609
6035
  const isOrchestration = isOrchestratorThread(fresh);
5610
- const { autoRenameBranchEnabled, getGithubGitAuthMode } = await import("./app-settings-IKFWZDUK.js");
6036
+ const { autoRenameBranchEnabled, getGithubGitAuthMode } = await import("./app-settings-E7NEQY7D.js");
5611
6037
  const gitAuthMode = getGithubGitAuthMode();
5612
6038
  const worktreeDirective = isBrightsy || isOrchestration ? null : formatWorktreeDirective(fresh, {
5613
6039
  githubSlug: await resolveGithubRepoSlug(fresh.worktreePath).catch(
@@ -6144,7 +6570,8 @@ var Orchestrator = class {
6144
6570
  agent,
6145
6571
  repoPath: opts.repoPath,
6146
6572
  title: opts.title ? `${opts.title} (${agent})` : `best-of-n: ${opts.prompt.slice(0, 48)} (${agent})`,
6147
- prompt: opts.prompt
6573
+ prompt: opts.prompt,
6574
+ reuseExisting: false
6148
6575
  });
6149
6576
  created.push(thread);
6150
6577
  }
@@ -6381,6 +6808,8 @@ var Orchestrator = class {
6381
6808
  if (meta.url && meta.url !== thread.prUrl) patch.prUrl = meta.url;
6382
6809
  if (meta.title && meta.title !== thread.prTitle) patch.prTitle = meta.title;
6383
6810
  if (nextState && nextState !== prevState) patch.prState = nextState;
6811
+ const nextDraft = Boolean(meta.isDraft) && nextState !== "MERGED" && nextState !== "CLOSED";
6812
+ if (nextDraft !== Boolean(thread.prIsDraft)) patch.prIsDraft = nextDraft;
6384
6813
  if (thread.skipAutoArchiveOnMerge && nextState && nextState !== "MERGED" && nextState !== "CLOSED") {
6385
6814
  patch.skipAutoArchiveOnMerge = false;
6386
6815
  }
@@ -6391,7 +6820,7 @@ var Orchestrator = class {
6391
6820
  updateThread(thread.id, { title: meta.title });
6392
6821
  }
6393
6822
  }
6394
- const { autoArchiveOnMergeEnabled } = await import("./app-settings-IKFWZDUK.js");
6823
+ const { autoArchiveOnMergeEnabled } = await import("./app-settings-E7NEQY7D.js");
6395
6824
  const latest = this.requireThread(thread.id);
6396
6825
  if (!shouldAutoArchiveOnPrMerge({
6397
6826
  previousPrState: prevState || null,
@@ -6405,7 +6834,7 @@ var Orchestrator = class {
6405
6834
  }
6406
6835
  const siblings = threadsSharingWorktree(latest.worktreePath);
6407
6836
  for (const t of siblings) {
6408
- const sibPatch = { prState: "MERGED" };
6837
+ const sibPatch = { prState: "MERGED", prIsDraft: false };
6409
6838
  if (meta.url && meta.url !== t.prUrl) sibPatch.prUrl = meta.url;
6410
6839
  if (meta.title && meta.title !== t.prTitle) sibPatch.prTitle = meta.title;
6411
6840
  if (Object.keys(sibPatch).length > 0) updateThread(t.id, sibPatch);
@@ -6579,10 +7008,15 @@ var Orchestrator = class {
6579
7008
  web: action === "create-web"
6580
7009
  });
6581
7010
  if (!url) return;
6582
- const patch = { prUrl: url, prTitle: meta.title };
7011
+ const patch = {
7012
+ prUrl: url,
7013
+ prTitle: meta.title,
7014
+ prIsDraft: action === "create-draft"
7015
+ };
6583
7016
  try {
6584
7017
  const fetched = await getPrMeta(cwd, url);
6585
7018
  if (fetched?.title) patch.prTitle = fetched.title;
7019
+ if (fetched) patch.prIsDraft = Boolean(fetched.isDraft);
6586
7020
  } catch {
6587
7021
  }
6588
7022
  updateThread(thread.id, patch);
@@ -6696,7 +7130,7 @@ var Orchestrator = class {
6696
7130
  this.emit({ type: "status_changed", threadId: archived.id, status: "archived" });
6697
7131
  if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
6698
7132
  try {
6699
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-ICK433ZV.js");
7133
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-HSYRYJYV.js");
6700
7134
  await ensureWorkspace2(thread.repoPath);
6701
7135
  } catch {
6702
7136
  }
@@ -6723,7 +7157,7 @@ var Orchestrator = class {
6723
7157
  await runArchiveScript(thread.repoPath, thread.worktreePath);
6724
7158
  } catch {
6725
7159
  }
6726
- const { deleteBranchOnPurgeEnabled } = await import("./app-settings-IKFWZDUK.js");
7160
+ const { deleteBranchOnPurgeEnabled } = await import("./app-settings-E7NEQY7D.js");
6727
7161
  const deleteBranch = opts?.deleteBranch ?? deleteBranchOnPurgeEnabled();
6728
7162
  await removeWorktree(thread.repoPath, thread.worktreePath, {
6729
7163
  deleteBranch: deleteBranch ? thread.branchName : void 0
@@ -6750,7 +7184,7 @@ var Orchestrator = class {
6750
7184
  `Cowboy checkout missing: ${thread.worktreePath}. Re-add the project folder, then restore.`
6751
7185
  );
6752
7186
  }
6753
- const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-CMGBTVN4.js");
7187
+ const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-ERHC7Q4F.js");
6754
7188
  const slug = thread.worktreePath.split("/").pop();
6755
7189
  const dest = thread.worktreePath;
6756
7190
  await withRepoGitLock(thread.repoPath, async () => {
@@ -6858,7 +7292,7 @@ async function startOrchestration(opts) {
6858
7292
  sourceRef: "default",
6859
7293
  ...createOpts
6860
7294
  }).catch(async () => {
6861
- const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-CMGBTVN4.js");
7295
+ const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-ERHC7Q4F.js");
6862
7296
  const repo = await resolveRepoRoot2(repoPath);
6863
7297
  const def = await resolveDefaultBranch2(repo);
6864
7298
  return orch.createThread({
@@ -6868,7 +7302,7 @@ async function startOrchestration(opts) {
6868
7302
  repoPath: repo
6869
7303
  });
6870
7304
  });
6871
- const { updateThread: upd } = await import("./thread-store-PMH3BMB6.js");
7305
+ const { updateThread: upd } = await import("./thread-store-F6BCARQG.js");
6872
7306
  const updated = upd(thread.id, {
6873
7307
  sourceType: "orchestration",
6874
7308
  sourceRef: goal
@@ -6888,6 +7322,18 @@ export {
6888
7322
  recordSlackOutboundWatch,
6889
7323
  pollSlackOutboundWatches,
6890
7324
  AGENT_GIT_ACTIONS,
7325
+ BOARD_COLUMN_DEFS,
7326
+ HOME_BOARD_CACHE_TTL_MS,
7327
+ dedupeBoardIssues,
7328
+ issueNeedsWorkspacePick,
7329
+ findLiveThreadForCreate,
7330
+ findBoardPin,
7331
+ syncBoardPins,
7332
+ dedupeBoardPrs,
7333
+ findBoardIssue,
7334
+ findBoardPr,
7335
+ assembleHomeBoard,
7336
+ HOME_BOARD_AGENT_HINT,
6891
7337
  listLinearIssues,
6892
7338
  readTurnLive,
6893
7339
  resolveScheduleThreadId,