@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
@@ -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-3ZSFTG72.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
  }
@@ -4644,7 +5054,8 @@ function formatArtifactDirective() {
4644
5054
  "```html",
4645
5055
  "<!DOCTYPE html><html><head><title>Demo</title></head><body><h1>Hi</h1></body></html>",
4646
5056
  "```",
4647
- "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.",
5057
+ "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.",
5058
+ " 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.",
4648
5059
  "CMS / JSON Schema forms & tables (Brightsy or any schema+schemaUi source):",
4649
5060
  "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:",
4650
5061
  " - datasource=brightsy + resource_id (record type UUID) after fetching types via Brightsy MCP, or",
@@ -4659,7 +5070,7 @@ function formatArtifactDirective() {
4659
5070
  ].join("\n");
4660
5071
  }
4661
5072
  function formatUiReminder() {
4662
- 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.";
5073
+ 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.";
4663
5074
  }
4664
5075
 
4665
5076
  // src/threads/sync-branch.ts
@@ -4930,7 +5341,7 @@ function formatScheduledPrompt(name, prompt) {
4930
5341
  ${prompt}`;
4931
5342
  }
4932
5343
  async function defaultDeps() {
4933
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-FZYM7I42.js");
5344
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-3ZSFTG72.js");
4934
5345
  const orch = getOrchestrator2();
4935
5346
  return {
4936
5347
  findThread: (id) => findThreadByRef(id),
@@ -5084,7 +5495,7 @@ var Orchestrator = class {
5084
5495
  maxConcurrent;
5085
5496
  runningCount = 0;
5086
5497
  constructor(opts) {
5087
- this.maxConcurrent = opts?.maxConcurrent ?? 3;
5498
+ this.maxConcurrent = opts?.maxConcurrent ?? 5;
5088
5499
  }
5089
5500
  on(listener) {
5090
5501
  this.events.on("event", listener);
@@ -5150,7 +5561,7 @@ var Orchestrator = class {
5150
5561
  orphans: orphans.map((o) => ({ path: o.path, repoPath: o.repoPath }))
5151
5562
  });
5152
5563
  }
5153
- const { autoCleanupOrphansEnabled } = await import("./app-settings-IKFWZDUK.js");
5564
+ const { autoCleanupOrphansEnabled } = await import("./app-settings-E7NEQY7D.js");
5154
5565
  if (autoCleanupOrphansEnabled() && shouldRunWorktreeCleanup() && orphans.length > 0) {
5155
5566
  await cleanupOrphanWorktrees({ repoPaths });
5156
5567
  }
@@ -5301,7 +5712,23 @@ var Orchestrator = class {
5301
5712
  return findThreadByRef(idOrRef) ?? readThread(idOrRef);
5302
5713
  }
5303
5714
  async createThread(input) {
5715
+ const prior = new Set(
5716
+ listThreads({ includeArchived: false }).map((t) => t.id)
5717
+ );
5304
5718
  const thread = await createThread(input);
5719
+ if (prior.has(thread.id)) {
5720
+ if (input.prompt?.trim()) {
5721
+ try {
5722
+ await this.send(thread.id, input.prompt.trim());
5723
+ } catch (err) {
5724
+ const message = err instanceof Error ? err.message : String(err);
5725
+ updateThread(thread.id, {
5726
+ lastError: `First prompt failed: ${message}`
5727
+ });
5728
+ }
5729
+ }
5730
+ return thread;
5731
+ }
5305
5732
  this.emit({ type: "status_changed", threadId: thread.id, status: thread.status });
5306
5733
  void this.finishCreateThread(thread.id, input.prompt?.trim() || void 0);
5307
5734
  return thread;
@@ -5322,7 +5749,7 @@ var Orchestrator = class {
5322
5749
  }
5323
5750
  await setup;
5324
5751
  if (skipSetup) return;
5325
- const { autoRunAfterSetupEnabled } = await import("./app-settings-IKFWZDUK.js");
5752
+ const { autoRunAfterSetupEnabled } = await import("./app-settings-E7NEQY7D.js");
5326
5753
  if (autoRunAfterSetupEnabled()) {
5327
5754
  try {
5328
5755
  await this.startDev(threadId);
@@ -5607,7 +6034,7 @@ var Orchestrator = class {
5607
6034
  }
5608
6035
  const isBrightsy = fresh.agent === "brightsy";
5609
6036
  const isOrchestration = isOrchestratorThread(fresh);
5610
- const { autoRenameBranchEnabled, getGithubGitAuthMode } = await import("./app-settings-IKFWZDUK.js");
6037
+ const { autoRenameBranchEnabled, getGithubGitAuthMode } = await import("./app-settings-E7NEQY7D.js");
5611
6038
  const gitAuthMode = getGithubGitAuthMode();
5612
6039
  const worktreeDirective = isBrightsy || isOrchestration ? null : formatWorktreeDirective(fresh, {
5613
6040
  githubSlug: await resolveGithubRepoSlug(fresh.worktreePath).catch(
@@ -6144,7 +6571,8 @@ var Orchestrator = class {
6144
6571
  agent,
6145
6572
  repoPath: opts.repoPath,
6146
6573
  title: opts.title ? `${opts.title} (${agent})` : `best-of-n: ${opts.prompt.slice(0, 48)} (${agent})`,
6147
- prompt: opts.prompt
6574
+ prompt: opts.prompt,
6575
+ reuseExisting: false
6148
6576
  });
6149
6577
  created.push(thread);
6150
6578
  }
@@ -6381,6 +6809,8 @@ var Orchestrator = class {
6381
6809
  if (meta.url && meta.url !== thread.prUrl) patch.prUrl = meta.url;
6382
6810
  if (meta.title && meta.title !== thread.prTitle) patch.prTitle = meta.title;
6383
6811
  if (nextState && nextState !== prevState) patch.prState = nextState;
6812
+ const nextDraft = Boolean(meta.isDraft) && nextState !== "MERGED" && nextState !== "CLOSED";
6813
+ if (nextDraft !== Boolean(thread.prIsDraft)) patch.prIsDraft = nextDraft;
6384
6814
  if (thread.skipAutoArchiveOnMerge && nextState && nextState !== "MERGED" && nextState !== "CLOSED") {
6385
6815
  patch.skipAutoArchiveOnMerge = false;
6386
6816
  }
@@ -6391,7 +6821,7 @@ var Orchestrator = class {
6391
6821
  updateThread(thread.id, { title: meta.title });
6392
6822
  }
6393
6823
  }
6394
- const { autoArchiveOnMergeEnabled } = await import("./app-settings-IKFWZDUK.js");
6824
+ const { autoArchiveOnMergeEnabled } = await import("./app-settings-E7NEQY7D.js");
6395
6825
  const latest = this.requireThread(thread.id);
6396
6826
  if (!shouldAutoArchiveOnPrMerge({
6397
6827
  previousPrState: prevState || null,
@@ -6405,7 +6835,7 @@ var Orchestrator = class {
6405
6835
  }
6406
6836
  const siblings = threadsSharingWorktree(latest.worktreePath);
6407
6837
  for (const t of siblings) {
6408
- const sibPatch = { prState: "MERGED" };
6838
+ const sibPatch = { prState: "MERGED", prIsDraft: false };
6409
6839
  if (meta.url && meta.url !== t.prUrl) sibPatch.prUrl = meta.url;
6410
6840
  if (meta.title && meta.title !== t.prTitle) sibPatch.prTitle = meta.title;
6411
6841
  if (Object.keys(sibPatch).length > 0) updateThread(t.id, sibPatch);
@@ -6579,10 +7009,15 @@ var Orchestrator = class {
6579
7009
  web: action === "create-web"
6580
7010
  });
6581
7011
  if (!url) return;
6582
- const patch = { prUrl: url, prTitle: meta.title };
7012
+ const patch = {
7013
+ prUrl: url,
7014
+ prTitle: meta.title,
7015
+ prIsDraft: action === "create-draft"
7016
+ };
6583
7017
  try {
6584
7018
  const fetched = await getPrMeta(cwd, url);
6585
7019
  if (fetched?.title) patch.prTitle = fetched.title;
7020
+ if (fetched) patch.prIsDraft = Boolean(fetched.isDraft);
6586
7021
  } catch {
6587
7022
  }
6588
7023
  updateThread(thread.id, patch);
@@ -6696,7 +7131,7 @@ var Orchestrator = class {
6696
7131
  this.emit({ type: "status_changed", threadId: archived.id, status: "archived" });
6697
7132
  if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
6698
7133
  try {
6699
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-ICK433ZV.js");
7134
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-HSYRYJYV.js");
6700
7135
  await ensureWorkspace2(thread.repoPath);
6701
7136
  } catch {
6702
7137
  }
@@ -6723,7 +7158,7 @@ var Orchestrator = class {
6723
7158
  await runArchiveScript(thread.repoPath, thread.worktreePath);
6724
7159
  } catch {
6725
7160
  }
6726
- const { deleteBranchOnPurgeEnabled } = await import("./app-settings-IKFWZDUK.js");
7161
+ const { deleteBranchOnPurgeEnabled } = await import("./app-settings-E7NEQY7D.js");
6727
7162
  const deleteBranch = opts?.deleteBranch ?? deleteBranchOnPurgeEnabled();
6728
7163
  await removeWorktree(thread.repoPath, thread.worktreePath, {
6729
7164
  deleteBranch: deleteBranch ? thread.branchName : void 0
@@ -6750,7 +7185,7 @@ var Orchestrator = class {
6750
7185
  `Cowboy checkout missing: ${thread.worktreePath}. Re-add the project folder, then restore.`
6751
7186
  );
6752
7187
  }
6753
- const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-CMGBTVN4.js");
7188
+ const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-ERHC7Q4F.js");
6754
7189
  const slug = thread.worktreePath.split("/").pop();
6755
7190
  const dest = thread.worktreePath;
6756
7191
  await withRepoGitLock(thread.repoPath, async () => {
@@ -6858,7 +7293,7 @@ async function startOrchestration(opts) {
6858
7293
  sourceRef: "default",
6859
7294
  ...createOpts
6860
7295
  }).catch(async () => {
6861
- const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-CMGBTVN4.js");
7296
+ const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-ERHC7Q4F.js");
6862
7297
  const repo = await resolveRepoRoot2(repoPath);
6863
7298
  const def = await resolveDefaultBranch2(repo);
6864
7299
  return orch.createThread({
@@ -6868,7 +7303,7 @@ async function startOrchestration(opts) {
6868
7303
  repoPath: repo
6869
7304
  });
6870
7305
  });
6871
- const { updateThread: upd } = await import("./thread-store-PMH3BMB6.js");
7306
+ const { updateThread: upd } = await import("./thread-store-F6BCARQG.js");
6872
7307
  const updated = upd(thread.id, {
6873
7308
  sourceType: "orchestration",
6874
7309
  sourceRef: goal
@@ -6888,6 +7323,18 @@ export {
6888
7323
  recordSlackOutboundWatch,
6889
7324
  pollSlackOutboundWatches,
6890
7325
  AGENT_GIT_ACTIONS,
7326
+ BOARD_COLUMN_DEFS,
7327
+ HOME_BOARD_CACHE_TTL_MS,
7328
+ dedupeBoardIssues,
7329
+ issueNeedsWorkspacePick,
7330
+ findLiveThreadForCreate,
7331
+ findBoardPin,
7332
+ syncBoardPins,
7333
+ dedupeBoardPrs,
7334
+ findBoardIssue,
7335
+ findBoardPr,
7336
+ assembleHomeBoard,
7337
+ HOME_BOARD_AGENT_HINT,
6891
7338
  listLinearIssues,
6892
7339
  readTurnLive,
6893
7340
  resolveScheduleThreadId,