@sideboard-ai/core 0.1.176 → 0.1.178

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 (31) hide show
  1. package/dist/{agents-544IX5KA.js → agents-2UD26PUT.js} +4 -4
  2. package/dist/{agents-UENEQ6J2.js → agents-XPWZG3JU.js} +4 -4
  3. package/dist/{chunk-3ZFGZ7RT.js → chunk-7T5XKNOF.js} +2 -2
  4. package/dist/{chunk-LZQULFIT.js → chunk-GOZ6T3AG.js} +2 -0
  5. package/dist/{chunk-4ELBAYFW.js → chunk-GVC4IVF5.js} +83 -17
  6. package/dist/{chunk-NIB6BTC6.js → chunk-IVJFEB3T.js} +3 -3
  7. package/dist/{chunk-JUA7XEXR.js → chunk-MZOZ6PIZ.js} +82 -16
  8. package/dist/{chunk-7UURXPKD.js → chunk-TCQAFTSV.js} +2 -0
  9. package/dist/{chunk-W44EKPIR.js → chunk-VUFJ5MAN.js} +3 -3
  10. package/dist/{chunk-7JIYKYLL.js → chunk-WXKXSZN4.js} +10 -7
  11. package/dist/{chunk-7O53GXTA.js → chunk-XBLN52RZ.js} +10 -7
  12. package/dist/{chunk-FJWQCZ74.js → chunk-XV3EFFZ6.js} +2 -2
  13. package/dist/{coordinator-prompt-PRM5ARWG.js → coordinator-prompt-3NRUXH3F.js} +3 -3
  14. package/dist/{coordinator-prompt-BE2FERYE.js → coordinator-prompt-GKH7Q7PK.js} +3 -3
  15. package/dist/{global-workspace-3HIWNQ3B.js → global-workspace-2FQU62S2.js} +3 -3
  16. package/dist/{global-workspace-J737XNUX.js → global-workspace-C2JMJXYV.js} +3 -3
  17. package/dist/index.cjs +90 -12
  18. package/dist/index.d.cts +25 -2
  19. package/dist/index.d.ts +25 -2
  20. package/dist/index.js +15 -7
  21. package/dist/mcp/run-stdio.cjs +133 -12
  22. package/dist/mcp/run-stdio.js +59 -8
  23. package/dist/{orchestrator-GDKEVLFK.js → orchestrator-HOMNNKMT.js} +7 -5
  24. package/dist/{orchestrator-2KZD7N4X.js → orchestrator-WNFMRRQP.js} +7 -5
  25. package/dist/{thread-store-KBFBHT5G.js → thread-store-RXXNSRVO.js} +1 -1
  26. package/dist/{thread-store-MVQU6HPJ.js → thread-store-ZL6GDIDP.js} +1 -1
  27. package/dist/{workspaces-ULHYXM4Y.js → workspaces-J3M3IPR3.js} +3 -3
  28. package/dist/{workspaces-DR4UNOQC.js → workspaces-YVZYX5H2.js} +3 -3
  29. package/dist/{worktree-LC4VRWDM.js → worktree-7LOSSD7G.js} +2 -2
  30. package/dist/{worktree-UTDBQXSS.js → worktree-LPWTX2NA.js} +2 -2
  31. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -199,6 +199,7 @@ import {
199
199
  resolveFilesToCopy,
200
200
  resolveReviewGuidelines,
201
201
  resolveScheduleThreadId,
202
+ resolveSendFollowUp,
202
203
  runArchiveScript,
203
204
  runConventionSetup,
204
205
  runSetupScript,
@@ -237,7 +238,7 @@ import {
237
238
  worktreeCleanupSettings,
238
239
  wrapReviewSkillMarkdown,
239
240
  writeWorktreeFile
240
- } from "./chunk-4ELBAYFW.js";
241
+ } from "./chunk-GVC4IVF5.js";
241
242
  import {
242
243
  LEGACY_PLAN_FILE_REL,
243
244
  PLAN_FILE_NAME,
@@ -343,7 +344,7 @@ import {
343
344
  withEventParentId,
344
345
  withEventsParentId,
345
346
  writeInjectedMcpConfig
346
- } from "./chunk-NIB6BTC6.js";
347
+ } from "./chunk-IVJFEB3T.js";
347
348
  import {
348
349
  CLOUD_COORDINATOR_BUSY_REPLY,
349
350
  CLOUD_COORDINATOR_STOPPED_REPLY,
@@ -386,7 +387,7 @@ import {
386
387
  stageBuffersAsAttachments,
387
388
  syncWorkspacesFromThreads,
388
389
  takenTeamSlugsForOrchestration
389
- } from "./chunk-3ZFGZ7RT.js";
390
+ } from "./chunk-7T5XKNOF.js";
390
391
  import {
391
392
  FAMOUS_SOCCER_TEAMS,
392
393
  GITHUB_PR_BODY_MAX_CHARS,
@@ -483,7 +484,7 @@ import {
483
484
  worktreeDisplayLabelForGroup,
484
485
  worktreeNameFromPath,
485
486
  worktreeSlugForTicket
486
- } from "./chunk-7JIYKYLL.js";
487
+ } from "./chunk-WXKXSZN4.js";
487
488
  import {
488
489
  ATTACHMENTS_DIR,
489
490
  CLI_DIR,
@@ -674,7 +675,7 @@ import {
674
675
  updateThread,
675
676
  withThreadLock,
676
677
  writeThread
677
- } from "./chunk-7UURXPKD.js";
678
+ } from "./chunk-TCQAFTSV.js";
678
679
  import {
679
680
  THINKING_EFFORTS,
680
681
  isThinkingEffort,
@@ -4353,25 +4354,31 @@ async function startMcpServer() {
4353
4354
  );
4354
4355
  server.tool(
4355
4356
  "list_board",
4356
- "Home Kanban of worktrees (New, Draft, Review, Merged) \u2014 one card per checkout; sibling chat tabs nest as inner cards. Same cards as desktop Home. Path to merge: no PR \u2192 draft PR \u2192 open PR \u2192 merged. Archive removes the card to Settings \u2192 History. Queued/running are activity on the card, not columns. Orchestration chats are not on the board. Filters: query, repoPath, kind (ticket/PR/branch source), column, limit (default 40). create_thread adds a worktree (and a Home card), or returns the live one if that ticket/PR/named branch is already checked out.",
4357
+ "Home Kanban of worktrees (New, Draft, Review, Merged) \u2014 one card per checkout; sibling chat tabs nest as inner cards. Same cards as desktop Home. Path to merge: no PR \u2192 draft PR \u2192 open PR \u2192 merged. Archive removes the card to Settings \u2192 History. Queued/running are activity on the card, not columns. Orchestration chats are not on the board. Filters: query, repoPath, kind (ticket/PR/branch source), ownership (mine = your PRs / WIP; reviewing = someone else's PR), column, limit (default 40). create_thread adds a worktree (and a Home card), or returns the live one if that ticket/PR/named branch is already checked out.",
4357
4358
  {
4358
4359
  query: z8.string().optional().describe("Case-insensitive token search across title, id, labels, repo"),
4359
4360
  repoPath: z8.string().optional().describe("Limit to one workspace path from list_workspaces"),
4360
4361
  kind: z8.enum(["all", "tickets", "prs", "branches", "threads"]).optional().describe("Filter by worktree source (default all)"),
4362
+ ownership: z8.enum(["all", "mine", "reviewing"]).optional().describe(
4363
+ "Mine = PRs you authored or local WIP; reviewing = someone else's PR. Uses the gh login."
4364
+ ),
4361
4365
  column: z8.enum(["new", "draft", "review", "done", "needs_you"]).optional().describe(
4362
4366
  "Return cards for this column only (totals still include the rest). needs_you is a legacy alias for new."
4363
4367
  ),
4364
4368
  limit: z8.number().int().positive().optional().describe("Max cards per column (default 40). hidden counts the remainder.")
4365
4369
  },
4366
- async ({ query, repoPath, kind, column, limit }) => {
4370
+ async ({ query, repoPath, kind, ownership, column, limit }) => {
4367
4371
  const workspaces = orch.listWorkspaces();
4368
4372
  const all = orch.getThreads(true);
4369
4373
  const names = new Map(workspaces.map((w) => [w.path, w.name]));
4374
+ const github = await getGitHubStatus();
4370
4375
  const snap = assembleHomeBoard({
4371
4376
  threads: all.filter((t) => t.status !== "archived"),
4372
4377
  query,
4373
4378
  repoPath,
4374
4379
  kind: kind ?? "all",
4380
+ ownership: ownership ?? "all",
4381
+ viewerLogin: github.login ?? "",
4375
4382
  column: column === "needs_you" ? "new" : column,
4376
4383
  limit,
4377
4384
  workspaceName: (path2) => names.get(path2) ?? ""
@@ -8176,6 +8183,7 @@ export {
8176
8183
  resolveRepoRoot,
8177
8184
  resolveReviewGuidelines,
8178
8185
  resolveScheduleThreadId,
8186
+ resolveSendFollowUp,
8179
8187
  resolveSlackListenMode,
8180
8188
  resolveThreadDefaults,
8181
8189
  resolveThreadEffort,
@@ -866,6 +866,8 @@ function createEmptyThread(partial) {
866
866
  prTitle: partial.prTitle ?? null,
867
867
  prState: partial.prState ?? null,
868
868
  prIsDraft: Boolean(partial.prIsDraft),
869
+ prAuthorLogin: partial.prAuthorLogin?.trim() || null,
870
+ prReviewerLogins: partial.prReviewerLogins ?? [],
869
871
  skipAutoArchiveOnMerge: partial.skipAutoArchiveOnMerge ?? false,
870
872
  cowboy: Boolean(partial.cowboy),
871
873
  stackId: partial.stackId ?? null,
@@ -6166,7 +6168,7 @@ async function getPr(repoPath, number) {
6166
6168
  "view",
6167
6169
  String(number),
6168
6170
  "--json",
6169
- "number,title,headRefName,url,isCrossRepository"
6171
+ "number,title,headRefName,url,isCrossRepository,author"
6170
6172
  ];
6171
6173
  if (slug) args.push("--repo", slug);
6172
6174
  const { stdout, exitCode } = await gh(args, repoPath, { reject: false });
@@ -6212,7 +6214,7 @@ async function getPrForHeadBranch(repoPath, branch) {
6212
6214
  "view",
6213
6215
  head,
6214
6216
  "--json",
6215
- "number,title,headRefName,url,isCrossRepository"
6217
+ "number,title,headRefName,url,isCrossRepository,author"
6216
6218
  ];
6217
6219
  if (slug) viewArgs.push("--repo", slug);
6218
6220
  const viewed = await gh(viewArgs, repoPath, { reject: false });
@@ -6230,7 +6232,7 @@ async function getPrForHeadBranch(repoPath, branch) {
6230
6232
  "--head",
6231
6233
  listHead,
6232
6234
  "--json",
6233
- "number,title,headRefName,url,isCrossRepository",
6235
+ "number,title,headRefName,url,isCrossRepository,author",
6234
6236
  "--limit",
6235
6237
  "1",
6236
6238
  "--state",
@@ -6432,7 +6434,7 @@ async function getPrMeta(cwd, selector) {
6432
6434
  "view",
6433
6435
  selector,
6434
6436
  "--json",
6435
- "number,title,url,state,isDraft,reviewDecision,baseRefName,headRefName,isInMergeQueue,mergeStateStatus,mergeable"
6437
+ "number,title,url,state,isDraft,reviewDecision,baseRefName,headRefName,isInMergeQueue,mergeStateStatus,mergeable,author,reviewRequests"
6436
6438
  ];
6437
6439
  if (slug) viewArgs.push("--repo", slug);
6438
6440
  let { stdout, exitCode, stderr } = await gh(viewArgs, cwd, { reject: false });
@@ -6442,7 +6444,7 @@ async function getPrMeta(cwd, selector) {
6442
6444
  "view",
6443
6445
  selector,
6444
6446
  "--json",
6445
- "number,title,url,state,isDraft,reviewDecision,baseRefName,headRefName,mergeStateStatus,mergeable"
6447
+ "number,title,url,state,isDraft,reviewDecision,baseRefName,headRefName,mergeStateStatus,mergeable,author,reviewRequests"
6446
6448
  ];
6447
6449
  if (slug) retry.push("--repo", slug);
6448
6450
  ({ stdout, exitCode, stderr } = await gh(retry, cwd, { reject: false }));
@@ -6463,6 +6465,7 @@ async function getPrMeta(cwd, selector) {
6463
6465
  };
6464
6466
  const probed = await probeLocalMergeGate(cwd, gate);
6465
6467
  const resolved = probed.gate ?? gate;
6468
+ const author = view.author ?? {};
6466
6469
  return {
6467
6470
  number: Number(view.number),
6468
6471
  title: String(view.title ?? ""),
@@ -6474,7 +6477,9 @@ async function getPrMeta(cwd, selector) {
6474
6477
  headRefName: String(view.headRefName ?? ""),
6475
6478
  isInMergeQueue: Boolean(resolved.isInMergeQueue),
6476
6479
  mergeable: resolved.mergeable,
6477
- mergeStateStatus: resolved.mergeStateStatus
6480
+ mergeStateStatus: resolved.mergeStateStatus,
6481
+ authorLogin: author.login?.trim() || null,
6482
+ reviewerLogins: humanReviewerLogins(view.reviewRequests)
6478
6483
  };
6479
6484
  } catch {
6480
6485
  return null;
@@ -13023,6 +13028,8 @@ function worktreeBindingFrom(from) {
13023
13028
  prTitle: from.prTitle,
13024
13029
  prState: from.prState,
13025
13030
  prIsDraft: from.prIsDraft,
13031
+ prAuthorLogin: from.prAuthorLogin,
13032
+ prReviewerLogins: from.prReviewerLogins,
13026
13033
  skipAutoArchiveOnMerge: from.skipAutoArchiveOnMerge,
13027
13034
  stackId: from.stackId,
13028
13035
  stackLayer: from.stackLayer,
@@ -14659,6 +14666,35 @@ function groupHomeBoardWorktrees(threads, sort = DEFAULT_WORKTREE_SORT, labelOf
14659
14666
  }
14660
14667
  return [...map.values()].map((list) => sortThreadsInWorktree(list)).sort((a, b) => compareWorktreeGroups(a, b, sort, labelOf));
14661
14668
  }
14669
+ function normalizeViewerLogin(login) {
14670
+ return (login ?? "").trim().toLowerCase();
14671
+ }
14672
+ function loginsInclude(logins, viewerLogin) {
14673
+ const me = normalizeViewerLogin(viewerLogin);
14674
+ if (!me) return false;
14675
+ return (logins ?? []).some((login) => normalizeViewerLogin(login) === me);
14676
+ }
14677
+ function classifyWorktreeOwnership(group, viewerLogin = "") {
14678
+ const me = normalizeViewerLogin(viewerLogin);
14679
+ if (group.some((t) => {
14680
+ const author = normalizeViewerLogin(t.prAuthorLogin);
14681
+ return Boolean(author && me && author === me);
14682
+ })) {
14683
+ return "mine";
14684
+ }
14685
+ const reviewing = group.some((t) => {
14686
+ const author = normalizeViewerLogin(t.prAuthorLogin);
14687
+ if (author && me && author !== me) return true;
14688
+ if (t.sourceType === "pr" && (!author || !me || author !== me)) return true;
14689
+ if (me && loginsInclude(t.prReviewerLogins, me)) return true;
14690
+ return false;
14691
+ });
14692
+ return reviewing ? "reviewing" : "mine";
14693
+ }
14694
+ function worktreeMatchesOwnership(group, filter, viewerLogin = "") {
14695
+ if (filter === "all") return true;
14696
+ return classifyWorktreeOwnership(group, viewerLogin) === filter;
14697
+ }
14662
14698
  function classifyWorktreeColumn(group) {
14663
14699
  if (group.some((t) => isMergedPrState(t.prUrl, t.prState))) return "done";
14664
14700
  if (group.some((t) => isOpenPrState(t.prUrl, t.prState) && !t.prIsDraft)) {
@@ -14977,6 +15013,8 @@ function assembleHomeBoard(input) {
14977
15013
  const tokens = tokenizeQuery(input.query ?? "");
14978
15014
  const repo = input.repoPath?.trim() ?? "";
14979
15015
  const kind = input.kind ?? "all";
15016
+ const ownership = input.ownership ?? DEFAULT_BOARD_OWNERSHIP;
15017
+ const viewerLogin = input.viewerLogin ?? "";
14980
15018
  const limit = Math.max(1, input.limit ?? BOARD_PAGE_SIZE);
14981
15019
  const wsName = input.workspaceName ?? (() => "");
14982
15020
  const live = input.threads.filter((t) => t.status !== "archived" && isHomeBoardThread(t));
@@ -15005,6 +15043,7 @@ function assembleHomeBoard(input) {
15005
15043
  };
15006
15044
  for (const group of groupHomeBoardWorktrees(live)) {
15007
15045
  if (!group.some((t) => threadMatchesKind(t, kind))) continue;
15046
+ if (!worktreeMatchesOwnership(group, ownership, viewerLogin)) continue;
15008
15047
  const col = classifyWorktreeColumn(group);
15009
15048
  if (col === "backlog") continue;
15010
15049
  const primary = group[0];
@@ -15034,7 +15073,7 @@ function assembleHomeBoard(input) {
15034
15073
  }
15035
15074
  return { columns, hidden, totals };
15036
15075
  }
15037
- var GLOBAL_WORKSPACE_ID2, BOARD_COLUMN_DEFS, HOME_BOARD_CACHE_TTL_MS, DEFAULT_WORKTREE_SORT, DEFAULTISH_BRANCH, BOARD_PAGE_SIZE, HOME_BOARD_AGENT_HINT;
15076
+ var GLOBAL_WORKSPACE_ID2, BOARD_COLUMN_DEFS, HOME_BOARD_CACHE_TTL_MS, DEFAULT_WORKTREE_SORT, DEFAULT_BOARD_OWNERSHIP, DEFAULTISH_BRANCH, BOARD_PAGE_SIZE, HOME_BOARD_AGENT_HINT;
15038
15077
  var init_home_board = __esm({
15039
15078
  "src/board/home-board.ts"() {
15040
15079
  "use strict";
@@ -15049,9 +15088,10 @@ var init_home_board = __esm({
15049
15088
  ];
15050
15089
  HOME_BOARD_CACHE_TTL_MS = 15 * 60 * 1e3;
15051
15090
  DEFAULT_WORKTREE_SORT = "created";
15091
+ DEFAULT_BOARD_OWNERSHIP = "all";
15052
15092
  DEFAULTISH_BRANCH = /^(main|master|develop|development|trunk|default|head)$/;
15053
15093
  BOARD_PAGE_SIZE = 40;
15054
- HOME_BOARD_AGENT_HINT = "Home is a Kanban of worktrees (one card per checkout; sibling chat tabs nest as inner cards). Do not create a second worktree for a ticket, PR, or named branch that already has a live checkout \u2014 create_thread / start_board_card return that thread (alreadyStarted). Creating from the default branch still opens a new isolated worktree. 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.";
15094
+ HOME_BOARD_AGENT_HINT = "Home is a Kanban of worktrees (one card per checkout; sibling chat tabs nest as inner cards). Do not create a second worktree for a ticket, PR, or named branch that already has a live checkout \u2014 create_thread / start_board_card return that thread (alreadyStarted). Creating from the default branch still opens a new isolated worktree. Columns are the path to merge: New (no PR) \u2192 Draft (draft PR) \u2192 Review (open PR) \u2192 Merged. ownership=mine is PRs you authored (or WIP with no PR); ownership=reviewing is someone else's PR (or a review-requested checkout). Viewer login comes from gh, not a setting. Archive removes the card to Settings \u2192 History. Queued/running are activity on the card, not columns. Orchestration chats stay in the sidebar. Do not invent status.";
15055
15095
  }
15056
15096
  });
15057
15097
 
@@ -15738,6 +15778,7 @@ async function createThread(input, _onSetupLine) {
15738
15778
  }
15739
15779
  let prUrl = null;
15740
15780
  let prTitle = null;
15781
+ let prAuthorLogin2 = null;
15741
15782
  if (sourceType === "pr") {
15742
15783
  const num2 = Number(input.sourceRef.replace(/^#/, ""));
15743
15784
  if (!Number.isFinite(num2)) throw new Error(`Invalid PR number: ${input.sourceRef}`);
@@ -15753,6 +15794,7 @@ async function createThread(input, _onSetupLine) {
15753
15794
  if (reusedPr) return reusedPr;
15754
15795
  sourceIsFork = pr.isCrossRepository;
15755
15796
  prUrl = pr.url;
15797
+ prAuthorLogin2 = pr.author?.login?.trim() || null;
15756
15798
  const localFetchBranch = `sideboard-pr-${pr.number}`;
15757
15799
  await fetchPrHead(repoPath, pr.number, localFetchBranch);
15758
15800
  sourceRef = localFetchBranch;
@@ -15775,6 +15817,7 @@ async function createThread(input, _onSetupLine) {
15775
15817
  if (existing.url) {
15776
15818
  prUrl = existing.url;
15777
15819
  prTitle = existing.title;
15820
+ prAuthorLogin2 = existing.author?.login?.trim() || null;
15778
15821
  }
15779
15822
  }
15780
15823
  }
@@ -15809,7 +15852,8 @@ async function createThread(input, _onSetupLine) {
15809
15852
  parentThreadId: input.parentThreadId ?? null,
15810
15853
  status: "idle",
15811
15854
  prUrl,
15812
- prTitle
15855
+ prTitle,
15856
+ prAuthorLogin: prAuthorLogin2
15813
15857
  });
15814
15858
  writeThread(thread);
15815
15859
  await ensureWorkspace(repoPath);
@@ -19171,6 +19215,7 @@ __export(orchestrator_exports, {
19171
19215
  Orchestrator: () => Orchestrator,
19172
19216
  getOrchestrator: () => getOrchestrator,
19173
19217
  isPidAlive: () => isPidAlive,
19218
+ resolveSendFollowUp: () => resolveSendFollowUp,
19174
19219
  startOrchestration: () => startOrchestration,
19175
19220
  waitForPidExit: () => waitForPidExit
19176
19221
  });
@@ -19183,6 +19228,11 @@ function isPidAlive(pid) {
19183
19228
  return false;
19184
19229
  }
19185
19230
  }
19231
+ function sameLoginList(a, b) {
19232
+ const left = [...a ?? []].map((s) => s.trim().toLowerCase()).filter(Boolean).sort();
19233
+ const right = [...b ?? []].map((s) => s.trim().toLowerCase()).filter(Boolean).sort();
19234
+ return left.length === right.length && left.every((login, i) => login === right[i]);
19235
+ }
19186
19236
  async function waitForPidExit(pid, timeoutMs) {
19187
19237
  const start = Date.now();
19188
19238
  while (isPidAlive(pid)) {
@@ -19191,6 +19241,10 @@ async function waitForPidExit(pid, timeoutMs) {
19191
19241
  }
19192
19242
  return true;
19193
19243
  }
19244
+ function resolveSendFollowUp(thread, requested) {
19245
+ if (requested === "steer" || requested === "queue") return requested;
19246
+ return isOrchestratorThread(thread) ? followUpBehavior() : "queue";
19247
+ }
19194
19248
  function writeLiveStatus(threadId, status, lastError) {
19195
19249
  const latest = readThread(threadId);
19196
19250
  if (!latest || latest.status === "archived") return latest;
@@ -19783,13 +19837,13 @@ var init_orchestrator = __esm({
19783
19837
  if (thread.status === "archived") {
19784
19838
  throw new Error(`Thread is archived: ${thread.id}`);
19785
19839
  }
19786
- const followUp = opts?.followUp === "steer" ? "steer" : "queue";
19787
19840
  let shouldSteer = false;
19788
19841
  const sent = await withThreadLock(thread.id, async () => {
19789
19842
  const current = this.requireThread(thread.id);
19790
19843
  if (current.status === "archived") {
19791
19844
  throw new Error(`Thread is archived: ${thread.id}`);
19792
19845
  }
19846
+ const followUp = resolveSendFollowUp(current, opts?.followUp);
19793
19847
  const queue = [...current.queue, prompt];
19794
19848
  this.crashContinued.delete(thread.id);
19795
19849
  this.jobContinueCount.delete(thread.id);
@@ -20955,6 +21009,14 @@ var init_orchestrator = __esm({
20955
21009
  if (nextState && nextState !== prevState) patch.prState = nextState;
20956
21010
  const nextDraft = Boolean(meta.isDraft) && nextState !== "MERGED" && nextState !== "CLOSED";
20957
21011
  if (nextDraft !== Boolean(thread.prIsDraft)) patch.prIsDraft = nextDraft;
21012
+ const nextAuthor = meta.authorLogin?.trim() || "";
21013
+ if (nextAuthor && nextAuthor !== (thread.prAuthorLogin ?? "").trim()) {
21014
+ patch.prAuthorLogin = nextAuthor;
21015
+ }
21016
+ const nextReviewers = meta.reviewerLogins ?? [];
21017
+ if (!sameLoginList(nextReviewers, thread.prReviewerLogins)) {
21018
+ patch.prReviewerLogins = nextReviewers;
21019
+ }
20958
21020
  if (thread.skipAutoArchiveOnMerge && nextState && nextState !== "MERGED" && nextState !== "CLOSED") {
20959
21021
  patch.skipAutoArchiveOnMerge = false;
20960
21022
  }
@@ -20982,6 +21044,14 @@ var init_orchestrator = __esm({
20982
21044
  const sibPatch = { prState: "MERGED", prIsDraft: false };
20983
21045
  if (meta.url && meta.url !== t.prUrl) sibPatch.prUrl = meta.url;
20984
21046
  if (meta.title && meta.title !== t.prTitle) sibPatch.prTitle = meta.title;
21047
+ const sibAuthor = meta.authorLogin?.trim() || "";
21048
+ if (sibAuthor && sibAuthor !== (t.prAuthorLogin ?? "").trim()) {
21049
+ sibPatch.prAuthorLogin = sibAuthor;
21050
+ }
21051
+ const sibReviewers = meta.reviewerLogins ?? [];
21052
+ if (!sameLoginList(sibReviewers, t.prReviewerLogins)) {
21053
+ sibPatch.prReviewerLogins = sibReviewers;
21054
+ }
20985
21055
  if (Object.keys(sibPatch).length > 0) updateThread(t.id, sibPatch);
20986
21056
  }
20987
21057
  for (const t of siblings) {
@@ -24228,6 +24298,51 @@ async function getHomeBoardInputs(workspaces, opts) {
24228
24298
  // src/mcp/server.ts
24229
24299
  init_home_board();
24230
24300
 
24301
+ // src/integrations/github.ts
24302
+ init_run();
24303
+ async function getGitHubStatus() {
24304
+ const which = await run("which", ["gh"], { reject: false });
24305
+ if (which.exitCode !== 0 || !which.stdout.trim()) {
24306
+ return {
24307
+ connected: false,
24308
+ login: null,
24309
+ hosts: [],
24310
+ reason: "gh CLI not found on PATH"
24311
+ };
24312
+ }
24313
+ const loginResult = await run(
24314
+ "gh",
24315
+ ["api", "user", "--jq", ".login"],
24316
+ { reject: false }
24317
+ );
24318
+ if (loginResult.exitCode === 0 && loginResult.stdout.trim()) {
24319
+ return {
24320
+ connected: true,
24321
+ login: loginResult.stdout.trim(),
24322
+ hosts: ["github.com"],
24323
+ reason: null
24324
+ };
24325
+ }
24326
+ const status = await run("gh", ["auth", "status"], { reject: false });
24327
+ const text7 = `${status.stdout}
24328
+ ${status.stderr}`;
24329
+ const loginMatch = text7.match(/Logged in to ([^\s]+) account (\S+)/i) ?? text7.match(/Logged in to ([^\s]+) as (\S+)/i);
24330
+ if (loginMatch) {
24331
+ return {
24332
+ connected: true,
24333
+ login: loginMatch[2].replace(/['"]/g, ""),
24334
+ hosts: [loginMatch[1]],
24335
+ reason: null
24336
+ };
24337
+ }
24338
+ return {
24339
+ connected: false,
24340
+ login: null,
24341
+ hosts: [],
24342
+ reason: status.stderr.trim() || status.stdout.trim() || "Not logged in to GitHub (run: gh auth login)"
24343
+ };
24344
+ }
24345
+
24231
24346
  // src/mcp/create-thread-agent.ts
24232
24347
  init_cursor();
24233
24348
  function resolveOrchCreateThreadOptions(input) {
@@ -24457,25 +24572,31 @@ async function startMcpServer() {
24457
24572
  );
24458
24573
  server.tool(
24459
24574
  "list_board",
24460
- "Home Kanban of worktrees (New, Draft, Review, Merged) \u2014 one card per checkout; sibling chat tabs nest as inner cards. Same cards as desktop Home. Path to merge: no PR \u2192 draft PR \u2192 open PR \u2192 merged. Archive removes the card to Settings \u2192 History. Queued/running are activity on the card, not columns. Orchestration chats are not on the board. Filters: query, repoPath, kind (ticket/PR/branch source), column, limit (default 40). create_thread adds a worktree (and a Home card), or returns the live one if that ticket/PR/named branch is already checked out.",
24575
+ "Home Kanban of worktrees (New, Draft, Review, Merged) \u2014 one card per checkout; sibling chat tabs nest as inner cards. Same cards as desktop Home. Path to merge: no PR \u2192 draft PR \u2192 open PR \u2192 merged. Archive removes the card to Settings \u2192 History. Queued/running are activity on the card, not columns. Orchestration chats are not on the board. Filters: query, repoPath, kind (ticket/PR/branch source), ownership (mine = your PRs / WIP; reviewing = someone else's PR), column, limit (default 40). create_thread adds a worktree (and a Home card), or returns the live one if that ticket/PR/named branch is already checked out.",
24461
24576
  {
24462
24577
  query: import_zod8.z.string().optional().describe("Case-insensitive token search across title, id, labels, repo"),
24463
24578
  repoPath: import_zod8.z.string().optional().describe("Limit to one workspace path from list_workspaces"),
24464
24579
  kind: import_zod8.z.enum(["all", "tickets", "prs", "branches", "threads"]).optional().describe("Filter by worktree source (default all)"),
24580
+ ownership: import_zod8.z.enum(["all", "mine", "reviewing"]).optional().describe(
24581
+ "Mine = PRs you authored or local WIP; reviewing = someone else's PR. Uses the gh login."
24582
+ ),
24465
24583
  column: import_zod8.z.enum(["new", "draft", "review", "done", "needs_you"]).optional().describe(
24466
24584
  "Return cards for this column only (totals still include the rest). needs_you is a legacy alias for new."
24467
24585
  ),
24468
24586
  limit: import_zod8.z.number().int().positive().optional().describe("Max cards per column (default 40). hidden counts the remainder.")
24469
24587
  },
24470
- async ({ query, repoPath, kind, column, limit }) => {
24588
+ async ({ query, repoPath, kind, ownership, column, limit }) => {
24471
24589
  const workspaces = orch.listWorkspaces();
24472
24590
  const all = orch.getThreads(true);
24473
24591
  const names = new Map(workspaces.map((w) => [w.path, w.name]));
24592
+ const github = await getGitHubStatus();
24474
24593
  const snap = assembleHomeBoard({
24475
24594
  threads: all.filter((t) => t.status !== "archived"),
24476
24595
  query,
24477
24596
  repoPath,
24478
24597
  kind: kind ?? "all",
24598
+ ownership: ownership ?? "all",
24599
+ viewerLogin: github.login ?? "",
24479
24600
  column: column === "needs_you" ? "new" : column,
24480
24601
  limit,
24481
24602
  workspaceName: (path) => names.get(path) ?? ""
@@ -37,7 +37,7 @@ import {
37
37
  syncBoardPins,
38
38
  updateSchedule,
39
39
  waitForDetachedJob
40
- } from "../chunk-JUA7XEXR.js";
40
+ } from "../chunk-MZOZ6PIZ.js";
41
41
  import {
42
42
  applySystemCaEnv,
43
43
  isCursorApiKeyConfigured,
@@ -46,7 +46,7 @@ import {
46
46
  materializeSystemCaBundle,
47
47
  sideboardMcpProfile,
48
48
  trustSystemCertificates
49
- } from "../chunk-W44EKPIR.js";
49
+ } from "../chunk-VUFJ5MAN.js";
50
50
  import "../chunk-HVIEYTUK.js";
51
51
  import "../chunk-Y4MIXGPA.js";
52
52
  import {
@@ -71,7 +71,7 @@ import {
71
71
  GLOBAL_WORKSPACE_ID,
72
72
  isCloudCoordinatorThread,
73
73
  listWorkspaces
74
- } from "../chunk-FJWQCZ74.js";
74
+ } from "../chunk-XV3EFFZ6.js";
75
75
  import {
76
76
  canonicalizeRepoPath,
77
77
  formatGhLandError,
@@ -82,12 +82,13 @@ import {
82
82
  resolveListPrsOptions,
83
83
  resolveRepoRoot,
84
84
  warmGithubAgentAuth
85
- } from "../chunk-7O53GXTA.js";
85
+ } from "../chunk-XBLN52RZ.js";
86
86
  import "../chunk-D6JWYJ4P.js";
87
- import "../chunk-LZQULFIT.js";
87
+ import "../chunk-GOZ6T3AG.js";
88
88
  import "../chunk-SYFVUF2C.js";
89
89
  import {
90
- gh
90
+ gh,
91
+ run
91
92
  } from "../chunk-FNQNJOHY.js";
92
93
  import {
93
94
  disconnectLinearConnection,
@@ -2867,6 +2868,50 @@ async function getHomeBoardInputs(workspaces, opts) {
2867
2868
  }
2868
2869
  }
2869
2870
 
2871
+ // src/integrations/github.ts
2872
+ async function getGitHubStatus() {
2873
+ const which = await run("which", ["gh"], { reject: false });
2874
+ if (which.exitCode !== 0 || !which.stdout.trim()) {
2875
+ return {
2876
+ connected: false,
2877
+ login: null,
2878
+ hosts: [],
2879
+ reason: "gh CLI not found on PATH"
2880
+ };
2881
+ }
2882
+ const loginResult = await run(
2883
+ "gh",
2884
+ ["api", "user", "--jq", ".login"],
2885
+ { reject: false }
2886
+ );
2887
+ if (loginResult.exitCode === 0 && loginResult.stdout.trim()) {
2888
+ return {
2889
+ connected: true,
2890
+ login: loginResult.stdout.trim(),
2891
+ hosts: ["github.com"],
2892
+ reason: null
2893
+ };
2894
+ }
2895
+ const status = await run("gh", ["auth", "status"], { reject: false });
2896
+ const text7 = `${status.stdout}
2897
+ ${status.stderr}`;
2898
+ const loginMatch = text7.match(/Logged in to ([^\s]+) account (\S+)/i) ?? text7.match(/Logged in to ([^\s]+) as (\S+)/i);
2899
+ if (loginMatch) {
2900
+ return {
2901
+ connected: true,
2902
+ login: loginMatch[2].replace(/['"]/g, ""),
2903
+ hosts: [loginMatch[1]],
2904
+ reason: null
2905
+ };
2906
+ }
2907
+ return {
2908
+ connected: false,
2909
+ login: null,
2910
+ hosts: [],
2911
+ reason: status.stderr.trim() || status.stdout.trim() || "Not logged in to GitHub (run: gh auth login)"
2912
+ };
2913
+ }
2914
+
2870
2915
  // src/mcp/create-thread-agent.ts
2871
2916
  function resolveOrchCreateThreadOptions(input) {
2872
2917
  const { resolveNewThreadOptions } = input;
@@ -3095,25 +3140,31 @@ async function startMcpServer() {
3095
3140
  );
3096
3141
  server.tool(
3097
3142
  "list_board",
3098
- "Home Kanban of worktrees (New, Draft, Review, Merged) \u2014 one card per checkout; sibling chat tabs nest as inner cards. Same cards as desktop Home. Path to merge: no PR \u2192 draft PR \u2192 open PR \u2192 merged. Archive removes the card to Settings \u2192 History. Queued/running are activity on the card, not columns. Orchestration chats are not on the board. Filters: query, repoPath, kind (ticket/PR/branch source), column, limit (default 40). create_thread adds a worktree (and a Home card), or returns the live one if that ticket/PR/named branch is already checked out.",
3143
+ "Home Kanban of worktrees (New, Draft, Review, Merged) \u2014 one card per checkout; sibling chat tabs nest as inner cards. Same cards as desktop Home. Path to merge: no PR \u2192 draft PR \u2192 open PR \u2192 merged. Archive removes the card to Settings \u2192 History. Queued/running are activity on the card, not columns. Orchestration chats are not on the board. Filters: query, repoPath, kind (ticket/PR/branch source), ownership (mine = your PRs / WIP; reviewing = someone else's PR), column, limit (default 40). create_thread adds a worktree (and a Home card), or returns the live one if that ticket/PR/named branch is already checked out.",
3099
3144
  {
3100
3145
  query: z8.string().optional().describe("Case-insensitive token search across title, id, labels, repo"),
3101
3146
  repoPath: z8.string().optional().describe("Limit to one workspace path from list_workspaces"),
3102
3147
  kind: z8.enum(["all", "tickets", "prs", "branches", "threads"]).optional().describe("Filter by worktree source (default all)"),
3148
+ ownership: z8.enum(["all", "mine", "reviewing"]).optional().describe(
3149
+ "Mine = PRs you authored or local WIP; reviewing = someone else's PR. Uses the gh login."
3150
+ ),
3103
3151
  column: z8.enum(["new", "draft", "review", "done", "needs_you"]).optional().describe(
3104
3152
  "Return cards for this column only (totals still include the rest). needs_you is a legacy alias for new."
3105
3153
  ),
3106
3154
  limit: z8.number().int().positive().optional().describe("Max cards per column (default 40). hidden counts the remainder.")
3107
3155
  },
3108
- async ({ query, repoPath, kind, column, limit }) => {
3156
+ async ({ query, repoPath, kind, ownership, column, limit }) => {
3109
3157
  const workspaces = orch.listWorkspaces();
3110
3158
  const all = orch.getThreads(true);
3111
3159
  const names = new Map(workspaces.map((w) => [w.path, w.name]));
3160
+ const github = await getGitHubStatus();
3112
3161
  const snap = assembleHomeBoard({
3113
3162
  threads: all.filter((t) => t.status !== "archived"),
3114
3163
  query,
3115
3164
  repoPath,
3116
3165
  kind: kind ?? "all",
3166
+ ownership: ownership ?? "all",
3167
+ viewerLogin: github.login ?? "",
3117
3168
  column: column === "needs_you" ? "new" : column,
3118
3169
  limit,
3119
3170
  workspaceName: (path) => names.get(path) ?? ""
@@ -3,13 +3,14 @@ import {
3
3
  Orchestrator,
4
4
  getOrchestrator,
5
5
  isPidAlive,
6
+ resolveSendFollowUp,
6
7
  startOrchestration,
7
8
  waitForPidExit
8
- } from "./chunk-4ELBAYFW.js";
9
+ } from "./chunk-GVC4IVF5.js";
9
10
  import "./chunk-DJU54RYW.js";
10
- import "./chunk-NIB6BTC6.js";
11
- import "./chunk-3ZFGZ7RT.js";
12
- import "./chunk-7JIYKYLL.js";
11
+ import "./chunk-IVJFEB3T.js";
12
+ import "./chunk-7T5XKNOF.js";
13
+ import "./chunk-WXKXSZN4.js";
13
14
  import "./chunk-6K64FJDD.js";
14
15
  import "./chunk-IEEAVWAP.js";
15
16
  import "./chunk-AN3THU7F.js";
@@ -20,7 +21,7 @@ import "./chunk-J7LQVLFQ.js";
20
21
  import "./chunk-4TR3HZFT.js";
21
22
  import "./chunk-AO35MGOE.js";
22
23
  import "./chunk-JT7R45JB.js";
23
- import "./chunk-7UURXPKD.js";
24
+ import "./chunk-TCQAFTSV.js";
24
25
  import "./chunk-77WWLBCI.js";
25
26
  import "./chunk-EFVFB2AH.js";
26
27
  import "./chunk-QQKDGPUI.js";
@@ -30,6 +31,7 @@ export {
30
31
  Orchestrator,
31
32
  getOrchestrator,
32
33
  isPidAlive,
34
+ resolveSendFollowUp,
33
35
  startOrchestration,
34
36
  waitForPidExit
35
37
  };
@@ -5,19 +5,20 @@ import {
5
5
  Orchestrator,
6
6
  getOrchestrator,
7
7
  isPidAlive,
8
+ resolveSendFollowUp,
8
9
  startOrchestration,
9
10
  waitForPidExit
10
- } from "./chunk-JUA7XEXR.js";
11
- import "./chunk-W44EKPIR.js";
11
+ } from "./chunk-MZOZ6PIZ.js";
12
+ import "./chunk-VUFJ5MAN.js";
12
13
  import "./chunk-HVIEYTUK.js";
13
14
  import "./chunk-Y4MIXGPA.js";
14
15
  import "./chunk-VKQGAWHY.js";
15
16
  import "./chunk-JFOPOMK7.js";
16
17
  import "./chunk-OCQPTUR7.js";
17
- import "./chunk-FJWQCZ74.js";
18
- import "./chunk-7O53GXTA.js";
18
+ import "./chunk-XV3EFFZ6.js";
19
+ import "./chunk-XBLN52RZ.js";
19
20
  import "./chunk-D6JWYJ4P.js";
20
- import "./chunk-LZQULFIT.js";
21
+ import "./chunk-GOZ6T3AG.js";
21
22
  import "./chunk-SYFVUF2C.js";
22
23
  import "./chunk-FNQNJOHY.js";
23
24
  import "./chunk-OKZFTOM2.js";
@@ -29,6 +30,7 @@ export {
29
30
  Orchestrator,
30
31
  getOrchestrator,
31
32
  isPidAlive,
33
+ resolveSendFollowUp,
32
34
  startOrchestration,
33
35
  waitForPidExit
34
36
  };
@@ -13,7 +13,7 @@ import {
13
13
  updateThread,
14
14
  withThreadLock,
15
15
  writeThread
16
- } from "./chunk-7UURXPKD.js";
16
+ } from "./chunk-TCQAFTSV.js";
17
17
  import "./chunk-77WWLBCI.js";
18
18
  import "./chunk-EFVFB2AH.js";
19
19
  export {
@@ -15,7 +15,7 @@ import {
15
15
  updateThread,
16
16
  withThreadLock,
17
17
  writeThread
18
- } from "./chunk-LZQULFIT.js";
18
+ } from "./chunk-GOZ6T3AG.js";
19
19
  import "./chunk-KGZBYWZ3.js";
20
20
  import "./chunk-JTJHOYCU.js";
21
21
  export {