@sideboard-ai/core 0.1.252 → 0.1.254

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 (27) hide show
  1. package/dist/{agents-B66CNEPB.js → agents-FSUI5MET.js} +3 -3
  2. package/dist/{agents-C7T4US6D.js → agents-HZP7YEKC.js} +3 -3
  3. package/dist/{chunk-AQQWP2TL.js → chunk-553IEH7O.js} +52 -25
  4. package/dist/{chunk-NYXDZCMP.js → chunk-63YSFXLT.js} +72 -2
  5. package/dist/{chunk-SJ3IUYG2.js → chunk-BYRZS2S2.js} +2 -2
  6. package/dist/{chunk-4DGYMF7H.js → chunk-F7ANS66U.js} +16 -4
  7. package/dist/{chunk-PV4KCI2S.js → chunk-HCNCZPDL.js} +72 -2
  8. package/dist/{chunk-6IP4FWMK.js → chunk-PJK24CN3.js} +2 -2
  9. package/dist/{chunk-BHPGORIY.js → chunk-R4DWUBOL.js} +22 -5
  10. package/dist/{chunk-FKGVQKRX.js → chunk-Y7CS2WGJ.js} +53 -26
  11. package/dist/{coordinator-prompt-A2JJ5X7B.js → coordinator-prompt-HZUWK3NP.js} +2 -2
  12. package/dist/{coordinator-prompt-BX2R5EMW.js → coordinator-prompt-Y6S6USNS.js} +2 -2
  13. package/dist/{global-workspace-PUJEGSOB.js → global-workspace-4JCITHB6.js} +2 -2
  14. package/dist/{global-workspace-JJRPZNTY.js → global-workspace-YA7U4M2N.js} +2 -2
  15. package/dist/index.cjs +727 -606
  16. package/dist/index.d.cts +26 -9
  17. package/dist/index.d.ts +26 -9
  18. package/dist/index.js +24 -14
  19. package/dist/mcp/run-stdio.cjs +141 -29
  20. package/dist/mcp/run-stdio.js +21 -14
  21. package/dist/{orchestrator-O3V4UH73.js → orchestrator-LWADJQTN.js} +4 -4
  22. package/dist/{orchestrator-O3TVI7SN.js → orchestrator-VHJZEVGA.js} +4 -4
  23. package/dist/{workspaces-FUOZIR3V.js → workspaces-4KHEUPEX.js} +2 -2
  24. package/dist/{workspaces-V3PZRTY5.js → workspaces-IXZD7EC4.js} +2 -2
  25. package/dist/{worktree-UK62JCYF.js → worktree-DAAF64UU.js} +3 -1
  26. package/dist/{worktree-4DNK2CK6.js → worktree-S3FOMEQ2.js} +3 -1
  27. package/package.json +1 -1
@@ -46,9 +46,9 @@ import {
46
46
  resolveLoginCommand,
47
47
  resolveQuotaFallbackAgent,
48
48
  whichOnPath
49
- } from "./chunk-BHPGORIY.js";
50
- import "./chunk-SJ3IUYG2.js";
51
- import "./chunk-NYXDZCMP.js";
49
+ } from "./chunk-R4DWUBOL.js";
50
+ import "./chunk-BYRZS2S2.js";
51
+ import "./chunk-63YSFXLT.js";
52
52
  import "./chunk-6K64FJDD.js";
53
53
  import "./chunk-2P4XKWV2.js";
54
54
  import "./chunk-5F4YFGHL.js";
@@ -55,12 +55,12 @@ import {
55
55
  resolveQuotaFallbackAgent,
56
56
  turnCostUsdFromCursorUsage,
57
57
  whichOnPath
58
- } from "./chunk-4DGYMF7H.js";
59
- import "./chunk-6IP4FWMK.js";
58
+ } from "./chunk-F7ANS66U.js";
59
+ import "./chunk-PJK24CN3.js";
60
60
  import "./chunk-XAISOBBG.js";
61
61
  import "./chunk-P2BSMNHB.js";
62
62
  import "./chunk-VKQGAWHY.js";
63
- import "./chunk-PV4KCI2S.js";
63
+ import "./chunk-HCNCZPDL.js";
64
64
  import "./chunk-D6JWYJ4P.js";
65
65
  import "./chunk-RJC2MKXN.js";
66
66
  import "./chunk-H547WSSZ.js";
@@ -44,7 +44,7 @@ import {
44
44
  summarizeTurnStderr,
45
45
  toolDescription,
46
46
  turnFailChatText
47
- } from "./chunk-4DGYMF7H.js";
47
+ } from "./chunk-F7ANS66U.js";
48
48
  import {
49
49
  SLACK_REPLY_FORMATTING,
50
50
  addWorkspace,
@@ -67,7 +67,7 @@ import {
67
67
  stageAbsolutePathsAsAttachments,
68
68
  stageBuffersAsAttachments,
69
69
  syncWorkspacesFromThreads
70
- } from "./chunk-6IP4FWMK.js";
70
+ } from "./chunk-PJK24CN3.js";
71
71
  import {
72
72
  addPrStackLayer,
73
73
  allocateTeamName,
@@ -110,6 +110,7 @@ import {
110
110
  resolveGithubRepoSlug,
111
111
  resolvePrSelector,
112
112
  resolveRepoRoot,
113
+ switchWorktreeBranch,
113
114
  takenSlugsFromThread,
114
115
  threadDisplayLabel,
115
116
  ticketSlugForBranch,
@@ -117,7 +118,7 @@ import {
117
118
  worktreeDisplayLabelForGroup,
118
119
  worktreeNameFromPath,
119
120
  worktreeSlugForTicket
120
- } from "./chunk-PV4KCI2S.js";
121
+ } from "./chunk-HCNCZPDL.js";
121
122
  import {
122
123
  ATTACHMENTS_DIR,
123
124
  DETACHED_JOBS_DIR,
@@ -152,6 +153,7 @@ import {
152
153
  applyTurnUsage,
153
154
  assertOrchestratorCapableAgent,
154
155
  autoCleanupHistoryEnabled,
156
+ caffeinateWhileSchedulesEnabled,
155
157
  childEnvWithAppSettings,
156
158
  coerceOrchestratorAgent,
157
159
  contextTokens,
@@ -491,9 +493,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
491
493
  `Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
492
494
  );
493
495
  }
494
- const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-JJRPZNTY.js");
496
+ const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-YA7U4M2N.js");
495
497
  if (isGlobalThread2(thread)) {
496
- const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-A2JJ5X7B.js");
498
+ const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-HZUWK3NP.js");
497
499
  ensureGlobalCoordinatorCwd2(
498
500
  isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
499
501
  );
@@ -4599,7 +4601,7 @@ async function adoptThread(input) {
4599
4601
  messages: input.messages ?? []
4600
4602
  });
4601
4603
  writeThread(thread);
4602
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-V3PZRTY5.js");
4604
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-IXZD7EC4.js");
4603
4605
  await ensureWorkspace2(repoPath);
4604
4606
  const { ensureWorktreeSideboardIgnored: ensureWorktreeSideboardIgnored2 } = await import("./worktree-exclude-5N2KTYIT.js");
4605
4607
  await ensureWorktreeSideboardIgnored2(input.worktreePath);
@@ -6585,11 +6587,15 @@ function formatWorktreeDirective(thread, opts) {
6585
6587
  lines.push("");
6586
6588
  lines.push(formatDevAccessHint(opts?.activeRuns));
6587
6589
  lines.push("");
6590
+ lines.push(
6591
+ "Schedules: `create_schedule` / `list_schedules` (`at` / `every` / `cron`). `threadId=self` continues this chat; omit threadId for a new Global chat. Fires only while Sideboard.app is running. A future job turns on Settings \u2192 Advanced \u2192 Caffeinate while schedules are enabled."
6592
+ );
6593
+ lines.push("");
6588
6594
  lines.push(formatProcessGuideDirective({ worktreePath: thread.worktreePath }));
6589
6595
  return lines.join("\n");
6590
6596
  }
6591
6597
  function formatWorktreeReminder() {
6592
- return "Sideboard worktree: stay in this cwd for all file and git work. Push and open PRs against origin, never upstream. Do not edit the main repo checkout. If a goal is given (Greptile 5/5, CI green), watch-fix-push until it lands \u2014 do not watch after every push.";
6598
+ return "Sideboard worktree: stay in this cwd for all file and git work. Push and open PRs against origin, never upstream. Do not edit the main repo checkout. If a goal is given (Greptile 5/5, CI green), watch-fix-push until it lands \u2014 do not watch after every push. Schedules: create_schedule (at / every / cron; threadId=self for this chat).";
6593
6599
  }
6594
6600
  var PR_GOAL_RE = /greptile|\bci\b.*\b(green|pass)|checks?\s+(pass|green)|until\s+.*\b(pass|green|merge|clean)|\bfix ci\b|watch[- ]fix[- ]push/i;
6595
6601
  function mentionsPrGoal(prompt) {
@@ -6899,7 +6905,7 @@ function resolveScheduleThreadId(raw, env = process.env) {
6899
6905
  const value = raw?.trim() || "";
6900
6906
  if (!value) return null;
6901
6907
  if (value.toLowerCase() === "self") {
6902
- return env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || null;
6908
+ return env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || env.SIDEBOARD_THREAD_ID?.trim() || null;
6903
6909
  }
6904
6910
  return value;
6905
6911
  }
@@ -7027,6 +7033,17 @@ function buildTask(input, now) {
7027
7033
  function listSchedules() {
7028
7034
  return readAll().sort((a, b) => a.nextRunAt.localeCompare(b.nextRunAt));
7029
7035
  }
7036
+ function scheduleNeedsCaffeinate(task, now = /* @__PURE__ */ new Date()) {
7037
+ if (!task.enabled) return false;
7038
+ const at = new Date(task.nextRunAt);
7039
+ if (Number.isNaN(at.getTime())) return false;
7040
+ return at.getTime() > now.getTime();
7041
+ }
7042
+ function ensureCaffeinateForFutureSchedule(task) {
7043
+ if (!scheduleNeedsCaffeinate(task)) return;
7044
+ if (caffeinateWhileSchedulesEnabled()) return;
7045
+ updateAdvancedSettings({ caffeinateWhileSchedules: true });
7046
+ }
7030
7047
  function getSchedule(id) {
7031
7048
  const key = id.trim();
7032
7049
  if (!key) return null;
@@ -7039,6 +7056,7 @@ function createSchedule(input) {
7039
7056
  const rows = readAll();
7040
7057
  rows.push(task);
7041
7058
  writeAll(rows);
7059
+ ensureCaffeinateForFutureSchedule(task);
7042
7060
  return task;
7043
7061
  }
7044
7062
  function updateSchedule(id, patch) {
@@ -7065,6 +7083,7 @@ function updateSchedule(id, patch) {
7065
7083
  };
7066
7084
  rows[idx] = next;
7067
7085
  writeAll(rows);
7086
+ ensureCaffeinateForFutureSchedule(next);
7068
7087
  return next;
7069
7088
  }
7070
7089
  function deleteSchedule(id) {
@@ -7105,7 +7124,7 @@ function formatScheduledPrompt(name, prompt) {
7105
7124
  ${prompt}`;
7106
7125
  }
7107
7126
  async function defaultDeps() {
7108
- const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-O3TVI7SN.js");
7127
+ const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-VHJZEVGA.js");
7109
7128
  const orch = getOrchestrator2();
7110
7129
  return {
7111
7130
  findThread: (id) => findThreadByRef(id),
@@ -7125,19 +7144,9 @@ async function fireSchedule(id) {
7125
7144
  try {
7126
7145
  const deps = fireHooks ?? await defaultDeps();
7127
7146
  const prompt = formatScheduledPrompt(schedule.name, schedule.prompt);
7128
- if (schedule.threadId) {
7129
- const thread = deps.findThread(schedule.threadId);
7130
- if (!thread || thread.status === "archived") {
7131
- return recordScheduleRun(schedule.id, {
7132
- lastError: thread ? `Orchestration chat ${schedule.threadId} is archived` : `Orchestration chat not found: ${schedule.threadId}`
7133
- });
7134
- }
7135
- if (!isOrchestratorThread(thread)) {
7136
- return recordScheduleRun(schedule.id, {
7137
- lastError: `Thread ${schedule.threadId} is not an orchestration chat`
7138
- });
7139
- }
7140
- const sent = await deps.send(thread.id, prompt);
7147
+ const target = schedule.threadId ? deps.findThread(schedule.threadId) : null;
7148
+ if (target && target.status !== "archived") {
7149
+ const sent = await deps.send(target.id, prompt);
7141
7150
  return recordScheduleRun(schedule.id, {
7142
7151
  lastThreadId: sent.id,
7143
7152
  lastError: null
@@ -7148,6 +7157,9 @@ async function fireSchedule(id) {
7148
7157
  agent: pickAgent(schedule),
7149
7158
  model: schedule.model
7150
7159
  });
7160
+ if (schedule.threadId && schedule.when.kind !== "once") {
7161
+ updateSchedule(schedule.id, { threadId: created.id });
7162
+ }
7151
7163
  return recordScheduleRun(schedule.id, {
7152
7164
  lastThreadId: created.id,
7153
7165
  lastError: null
@@ -9670,6 +9682,20 @@ var Orchestrator = class {
9670
9682
  if (!next) throw new Error("Title cannot be empty");
9671
9683
  return updateThread(thread.id, { title: next, userSetTitle: true });
9672
9684
  }
9685
+ async switchThreadBranch(threadRef, branchName) {
9686
+ const thread = this.requireThread(threadRef);
9687
+ this.assertNotGlobal(thread, "Switch branch");
9688
+ if (thread.status === "running") {
9689
+ throw new Error("Stop the agent before switching branches");
9690
+ }
9691
+ await switchWorktreeBranch({
9692
+ repoPath: thread.repoPath,
9693
+ worktreePath: thread.worktreePath,
9694
+ branchName
9695
+ });
9696
+ await syncThreadBranchFromGit(thread.id);
9697
+ return this.requireThread(thread.id);
9698
+ }
9673
9699
  setAttachments(threadRef, attachments) {
9674
9700
  return updateThread(this.requireThread(threadRef).id, { attachments });
9675
9701
  }
@@ -9738,7 +9764,7 @@ var Orchestrator = class {
9738
9764
  this.enforceHistoryRetention();
9739
9765
  if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
9740
9766
  try {
9741
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-V3PZRTY5.js");
9767
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-IXZD7EC4.js");
9742
9768
  await ensureWorkspace2(thread.repoPath);
9743
9769
  } catch {
9744
9770
  }
@@ -9792,7 +9818,7 @@ var Orchestrator = class {
9792
9818
  `Cowboy checkout missing: ${thread.worktreePath}. Re-add the project folder, then restore.`
9793
9819
  );
9794
9820
  }
9795
- const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-UK62JCYF.js");
9821
+ const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-DAAF64UU.js");
9796
9822
  const slug = thread.worktreePath.split("/").pop();
9797
9823
  const dest = thread.worktreePath;
9798
9824
  await withRepoGitLock(thread.repoPath, async () => {
@@ -9902,7 +9928,7 @@ async function startOrchestration(opts) {
9902
9928
  sourceRef: "default",
9903
9929
  ...createOpts
9904
9930
  }).catch(async () => {
9905
- const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-UK62JCYF.js");
9931
+ const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-DAAF64UU.js");
9906
9932
  const repo = await resolveRepoRoot2(repoPath);
9907
9933
  const def = await resolveDefaultBranch2(repo);
9908
9934
  return orch.createThread({
@@ -9953,6 +9979,7 @@ export {
9953
9979
  localhostPreviewUrl,
9954
9980
  resolveScheduleThreadId,
9955
9981
  listSchedules,
9982
+ scheduleNeedsCaffeinate,
9956
9983
  getSchedule,
9957
9984
  createSchedule,
9958
9985
  updateSchedule,
@@ -2517,6 +2517,7 @@ var PR_DETAILS_JSON_FIELDS = [
2517
2517
  "additions",
2518
2518
  "deletions",
2519
2519
  "changedFiles",
2520
+ "commits",
2520
2521
  "comments",
2521
2522
  "reviews",
2522
2523
  "assignees",
@@ -2527,6 +2528,7 @@ function parsePrDetailsView(view) {
2527
2528
  const author = view.author ?? {};
2528
2529
  const comments = Array.isArray(view.comments) ? view.comments : [];
2529
2530
  const reviews = Array.isArray(view.reviews) ? view.reviews : [];
2531
+ const commits = Array.isArray(view.commits) ? view.commits : [];
2530
2532
  const requested = {
2531
2533
  ids: reviewerIdentities(view.reviewRequests),
2532
2534
  users: humanReviewerLogins(view.reviewRequests),
@@ -2547,8 +2549,22 @@ function parsePrDetailsView(view) {
2547
2549
  additions: Number(view.additions ?? 0),
2548
2550
  deletions: Number(view.deletions ?? 0),
2549
2551
  changedFiles: Number(view.changedFiles ?? 0),
2550
- // Commits live in Changes; omit from GraphQL to save rate-limit points.
2551
- commits: [],
2552
+ commits: commits.map((c) => {
2553
+ const row = c;
2554
+ const authors = Array.isArray(row.authors) ? row.authors.map((a) => {
2555
+ const actor = a;
2556
+ return {
2557
+ login: actor.login?.trim() || actor.name?.trim() || "unknown",
2558
+ name: actor.name ?? null
2559
+ };
2560
+ }) : [];
2561
+ return {
2562
+ oid: String(row.oid ?? ""),
2563
+ messageHeadline: String(row.messageHeadline ?? ""),
2564
+ committedDate: String(row.committedDate ?? ""),
2565
+ authors
2566
+ };
2567
+ }),
2552
2568
  comments: comments.map((c) => {
2553
2569
  const row = c;
2554
2570
  const a = row.author ?? {};
@@ -2927,6 +2943,59 @@ async function createExistingBranchWorktree(opts) {
2927
2943
  await ensureWorktreeSideboardIgnored(worktreePath);
2928
2944
  return { branchName, worktreePath };
2929
2945
  }
2946
+ async function switchWorktreeBranch(opts) {
2947
+ const branch = opts.branchName.trim().replace(/^origin\//, "");
2948
+ if (!branch) throw new Error("branch name required");
2949
+ const head = await git(["rev-parse", "--abbrev-ref", "HEAD"], opts.worktreePath, {
2950
+ reject: false
2951
+ });
2952
+ if (head.stdout.trim() === branch) return branch;
2953
+ const here = normalizeWorktreePath(opts.worktreePath);
2954
+ const trees = await listWorktrees(opts.repoPath);
2955
+ const taken = trees.find(
2956
+ (w) => w.branch === branch && normalizeWorktreePath(w.path) !== here
2957
+ );
2958
+ if (taken?.path) {
2959
+ throw new Error(`Branch ${branch} is already checked out at ${taken.path}`);
2960
+ }
2961
+ await withRepoGitLock(opts.repoPath, async () => {
2962
+ const local = await git(
2963
+ ["show-ref", "--verify", "--quiet", `refs/heads/${branch}`],
2964
+ opts.worktreePath,
2965
+ { reject: false }
2966
+ );
2967
+ if (local.exitCode === 0) {
2968
+ const sw = await git(["switch", branch], opts.worktreePath, {
2969
+ reject: false
2970
+ });
2971
+ if (sw.exitCode !== 0) {
2972
+ throw new Error(
2973
+ sw.stderr.trim() || sw.stdout.trim() || `Failed to switch to ${branch}`
2974
+ );
2975
+ }
2976
+ return;
2977
+ }
2978
+ const remote = `origin/${branch}`;
2979
+ const remoteOk = await git(["rev-parse", "--verify", "--quiet", remote], opts.repoPath, {
2980
+ reject: false
2981
+ });
2982
+ if (remoteOk.exitCode === 0) {
2983
+ const sw = await git(
2984
+ ["switch", "-c", branch, "--track", remote],
2985
+ opts.worktreePath,
2986
+ { reject: false }
2987
+ );
2988
+ if (sw.exitCode !== 0) {
2989
+ throw new Error(
2990
+ sw.stderr.trim() || sw.stdout.trim() || `Failed to switch to ${branch}`
2991
+ );
2992
+ }
2993
+ return;
2994
+ }
2995
+ throw new Error(`Unknown branch ${branch}`);
2996
+ });
2997
+ return branch;
2998
+ }
2930
2999
  async function removeWorktree(repoPath, worktreePath, opts) {
2931
3000
  await withRepoGitLock(repoPath, async () => {
2932
3001
  await git(["worktree", "remove", "--force", worktreePath], repoPath, {
@@ -3406,6 +3475,7 @@ export {
3406
3475
  fastForwardMainCheckoutIfSafe,
3407
3476
  createThreadWorktree,
3408
3477
  createExistingBranchWorktree,
3478
+ switchWorktreeBranch,
3409
3479
  removeWorktree,
3410
3480
  listWorktrees,
3411
3481
  countUnpushedVsOrigin,
@@ -6,7 +6,7 @@ import {
6
6
  resolveRepoRoot,
7
7
  takenSlugsFromThread,
8
8
  teamSlugFromName
9
- } from "./chunk-NYXDZCMP.js";
9
+ } from "./chunk-63YSFXLT.js";
10
10
  import {
11
11
  ATTACHMENTS_DIR,
12
12
  attachmentsGitignoreBody
@@ -398,7 +398,7 @@ var COORDINATOR_TOOL_PLAYBOOK = [
398
398
  "- ask_user \u2014 composer multiple-choice only when blocked on a concrete choice (approach fork, which API, Save this context / Do not save). Never for hellos, check-ins, invented \u201Cwhat should we do?\u201D menus, or after a review \u2014 reply in chat. Explain options first, description on every option, then wait.",
399
399
  "- get_viewer_context / update_viewer_context \u2014 read or replace Settings \u2192 Agents (account) and Settings \u2192 Projects (per-repo) context. You can update a project: pass scope=project and repoPath from list_workspaces (this cwd is not a project). update_viewer_context requires confirmed=true after ask_user. Never write context unprompted.",
400
400
  "- set_caffeinate \u2014 keep this Mac awake across turns (macOS caffeinate). Turn on for Slack / away-from-keyboard work, overnight schedules, or when the user will be away. Turn OFF when they say they are done, wrapping up, going to sleep, or no longer need the machine awake. Closing this chat also releases it.",
401
- "- list_schedules / create_schedule / update_schedule / delete_schedule / run_schedule \u2014 local jobs that send a prompt to an orchestration chat (threadId or self) or start a new Global chat (omit threadId). One-shot `at`, interval `every` (15m/1h/6h/1d), or 5-field `cron`. Recurring jobs without threadId open a new chat each run. Jobs fire only while Sideboard.app is running; sleep skips until wake. Overnight/unattended runs: ask the user to enable Settings \u2192 Advanced \u2192 Caffeinate while schedules are enabled, or call set_caffeinate.",
401
+ "- list_schedules / create_schedule / update_schedule / delete_schedule / run_schedule \u2014 local jobs that send a prompt to a chat (threadId or self) or start a new Global chat (omit threadId). One-shot `at`, interval `every` (15m/1h/6h/1d), or 5-field `cron`. Recurring jobs without threadId open a new chat each run. Worktree agents have the same tools (`threadId=self` continues that coding chat). Jobs fire only while Sideboard.app is running; sleep skips until wake. Creating or enabling a future job turns on Settings \u2192 Advanced \u2192 Caffeinate while schedules are enabled (or call set_caffeinate).",
402
402
  "Workspaces:",
403
403
  "- add_workspace / remove_workspace \u2014 register or unregister a git repo",
404
404
  "Worktree threads (chats):",
@@ -2,7 +2,7 @@
2
2
 
3
3
  import {
4
4
  isOrchestratorThread
5
- } from "./chunk-6IP4FWMK.js";
5
+ } from "./chunk-PJK24CN3.js";
6
6
  import {
7
7
  applyConnectedTeamToCli,
8
8
  brightsyMcpServerName,
@@ -20,7 +20,7 @@ import {
20
20
  mergeAgentGitAuthEnv,
21
21
  resolveAgentGitAuthEnv,
22
22
  resolveCodexGitWritableRoots
23
- } from "./chunk-PV4KCI2S.js";
23
+ } from "./chunk-HCNCZPDL.js";
24
24
  import {
25
25
  enrichPathWithNpmGlobalBin,
26
26
  isConductorBundledCli,
@@ -1240,7 +1240,12 @@ var SIDEBOARD_ARTIFACT_MCP_ALLOWED_TOOLS = [
1240
1240
  "mcp__sideboard__get_run_log",
1241
1241
  "mcp__sideboard__notify_orchestrator",
1242
1242
  "mcp__sideboard__get_viewer_context",
1243
- "mcp__sideboard__update_viewer_context"
1243
+ "mcp__sideboard__update_viewer_context",
1244
+ "mcp__sideboard__list_schedules",
1245
+ "mcp__sideboard__create_schedule",
1246
+ "mcp__sideboard__update_schedule",
1247
+ "mcp__sideboard__delete_schedule",
1248
+ "mcp__sideboard__run_schedule"
1244
1249
  ];
1245
1250
  var SIDEBOARD_GITHUB_MCP_ALLOWED_TOOLS = ["mcp__sideboard__github_*"];
1246
1251
  var SIDEBOARD_LINEAR_MCP_ALLOWED_TOOLS = ["mcp__sideboard__linear_*"];
@@ -1830,6 +1835,13 @@ function toolDescription(name, input) {
1830
1835
  return str(input?.name) ? `Read ${str(input?.name)} log` : "Read Dev log";
1831
1836
  }
1832
1837
  if (/notify_orchestrator$/i.test(name)) return "Notify orchestrator";
1838
+ if (/list_schedules$/i.test(name)) return "List schedules";
1839
+ if (/create_schedule$/i.test(name)) {
1840
+ return str(input?.name) ? `Schedule ${str(input?.name)}` : "Create schedule";
1841
+ }
1842
+ if (/update_schedule$/i.test(name)) return "Update schedule";
1843
+ if (/delete_schedule$/i.test(name)) return "Delete schedule";
1844
+ if (/run_schedule$/i.test(name)) return "Run schedule";
1833
1845
  if (isSkillToolName(name)) {
1834
1846
  const cmd = skillCommandFromInput(input);
1835
1847
  return cmd ? `Using /${cmd}` : "Using skill";
@@ -2958,7 +2970,7 @@ var claudeAdapter = {
2958
2970
  );
2959
2971
  }
2960
2972
  const mode = permissionMode(thread);
2961
- const { isOrchestratorThread: isOrchestratorThread2 } = await import("./global-workspace-JJRPZNTY.js");
2973
+ const { isOrchestratorThread: isOrchestratorThread2 } = await import("./global-workspace-YA7U4M2N.js");
2962
2974
  const isOrchestrator = isOrchestratorThread2(thread);
2963
2975
  const injectedServers = await buildInjectedMcpServers({
2964
2976
  includeSideboard: true,
@@ -2480,6 +2480,7 @@ var PR_DETAILS_JSON_FIELDS = [
2480
2480
  "additions",
2481
2481
  "deletions",
2482
2482
  "changedFiles",
2483
+ "commits",
2483
2484
  "comments",
2484
2485
  "reviews",
2485
2486
  "assignees",
@@ -2490,6 +2491,7 @@ function parsePrDetailsView(view) {
2490
2491
  const author = view.author ?? {};
2491
2492
  const comments = Array.isArray(view.comments) ? view.comments : [];
2492
2493
  const reviews = Array.isArray(view.reviews) ? view.reviews : [];
2494
+ const commits = Array.isArray(view.commits) ? view.commits : [];
2493
2495
  const requested = {
2494
2496
  ids: reviewerIdentities(view.reviewRequests),
2495
2497
  users: humanReviewerLogins(view.reviewRequests),
@@ -2510,8 +2512,22 @@ function parsePrDetailsView(view) {
2510
2512
  additions: Number(view.additions ?? 0),
2511
2513
  deletions: Number(view.deletions ?? 0),
2512
2514
  changedFiles: Number(view.changedFiles ?? 0),
2513
- // Commits live in Changes; omit from GraphQL to save rate-limit points.
2514
- commits: [],
2515
+ commits: commits.map((c) => {
2516
+ const row = c;
2517
+ const authors = Array.isArray(row.authors) ? row.authors.map((a) => {
2518
+ const actor = a;
2519
+ return {
2520
+ login: actor.login?.trim() || actor.name?.trim() || "unknown",
2521
+ name: actor.name ?? null
2522
+ };
2523
+ }) : [];
2524
+ return {
2525
+ oid: String(row.oid ?? ""),
2526
+ messageHeadline: String(row.messageHeadline ?? ""),
2527
+ committedDate: String(row.committedDate ?? ""),
2528
+ authors
2529
+ };
2530
+ }),
2515
2531
  comments: comments.map((c) => {
2516
2532
  const row = c;
2517
2533
  const a = row.author ?? {};
@@ -2890,6 +2906,59 @@ async function createExistingBranchWorktree(opts) {
2890
2906
  await ensureWorktreeSideboardIgnored(worktreePath);
2891
2907
  return { branchName, worktreePath };
2892
2908
  }
2909
+ async function switchWorktreeBranch(opts) {
2910
+ const branch = opts.branchName.trim().replace(/^origin\//, "");
2911
+ if (!branch) throw new Error("branch name required");
2912
+ const head = await git(["rev-parse", "--abbrev-ref", "HEAD"], opts.worktreePath, {
2913
+ reject: false
2914
+ });
2915
+ if (head.stdout.trim() === branch) return branch;
2916
+ const here = normalizeWorktreePath(opts.worktreePath);
2917
+ const trees = await listWorktrees(opts.repoPath);
2918
+ const taken = trees.find(
2919
+ (w) => w.branch === branch && normalizeWorktreePath(w.path) !== here
2920
+ );
2921
+ if (taken?.path) {
2922
+ throw new Error(`Branch ${branch} is already checked out at ${taken.path}`);
2923
+ }
2924
+ await withRepoGitLock(opts.repoPath, async () => {
2925
+ const local = await git(
2926
+ ["show-ref", "--verify", "--quiet", `refs/heads/${branch}`],
2927
+ opts.worktreePath,
2928
+ { reject: false }
2929
+ );
2930
+ if (local.exitCode === 0) {
2931
+ const sw = await git(["switch", branch], opts.worktreePath, {
2932
+ reject: false
2933
+ });
2934
+ if (sw.exitCode !== 0) {
2935
+ throw new Error(
2936
+ sw.stderr.trim() || sw.stdout.trim() || `Failed to switch to ${branch}`
2937
+ );
2938
+ }
2939
+ return;
2940
+ }
2941
+ const remote = `origin/${branch}`;
2942
+ const remoteOk = await git(["rev-parse", "--verify", "--quiet", remote], opts.repoPath, {
2943
+ reject: false
2944
+ });
2945
+ if (remoteOk.exitCode === 0) {
2946
+ const sw = await git(
2947
+ ["switch", "-c", branch, "--track", remote],
2948
+ opts.worktreePath,
2949
+ { reject: false }
2950
+ );
2951
+ if (sw.exitCode !== 0) {
2952
+ throw new Error(
2953
+ sw.stderr.trim() || sw.stdout.trim() || `Failed to switch to ${branch}`
2954
+ );
2955
+ }
2956
+ return;
2957
+ }
2958
+ throw new Error(`Unknown branch ${branch}`);
2959
+ });
2960
+ return branch;
2961
+ }
2893
2962
  async function removeWorktree(repoPath, worktreePath, opts) {
2894
2963
  await withRepoGitLock(repoPath, async () => {
2895
2964
  await git(["worktree", "remove", "--force", worktreePath], repoPath, {
@@ -3344,6 +3413,7 @@ export {
3344
3413
  fastForwardMainCheckoutIfSafe,
3345
3414
  createThreadWorktree,
3346
3415
  createExistingBranchWorktree,
3416
+ switchWorktreeBranch,
3347
3417
  removeWorktree,
3348
3418
  listWorktrees,
3349
3419
  countUnpushedVsOrigin,
@@ -8,7 +8,7 @@ import {
8
8
  resolveRepoRoot,
9
9
  takenSlugsFromThread,
10
10
  teamSlugFromName
11
- } from "./chunk-PV4KCI2S.js";
11
+ } from "./chunk-HCNCZPDL.js";
12
12
  import {
13
13
  ATTACHMENTS_DIR,
14
14
  attachmentsGitignoreBody
@@ -353,7 +353,7 @@ var COORDINATOR_TOOL_PLAYBOOK = [
353
353
  "- ask_user \u2014 composer multiple-choice only when blocked on a concrete choice (approach fork, which API, Save this context / Do not save). Never for hellos, check-ins, invented \u201Cwhat should we do?\u201D menus, or after a review \u2014 reply in chat. Explain options first, description on every option, then wait.",
354
354
  "- get_viewer_context / update_viewer_context \u2014 read or replace Settings \u2192 Agents (account) and Settings \u2192 Projects (per-repo) context. You can update a project: pass scope=project and repoPath from list_workspaces (this cwd is not a project). update_viewer_context requires confirmed=true after ask_user. Never write context unprompted.",
355
355
  "- set_caffeinate \u2014 keep this Mac awake across turns (macOS caffeinate). Turn on for Slack / away-from-keyboard work, overnight schedules, or when the user will be away. Turn OFF when they say they are done, wrapping up, going to sleep, or no longer need the machine awake. Closing this chat also releases it.",
356
- "- list_schedules / create_schedule / update_schedule / delete_schedule / run_schedule \u2014 local jobs that send a prompt to an orchestration chat (threadId or self) or start a new Global chat (omit threadId). One-shot `at`, interval `every` (15m/1h/6h/1d), or 5-field `cron`. Recurring jobs without threadId open a new chat each run. Jobs fire only while Sideboard.app is running; sleep skips until wake. Overnight/unattended runs: ask the user to enable Settings \u2192 Advanced \u2192 Caffeinate while schedules are enabled, or call set_caffeinate.",
356
+ "- list_schedules / create_schedule / update_schedule / delete_schedule / run_schedule \u2014 local jobs that send a prompt to a chat (threadId or self) or start a new Global chat (omit threadId). One-shot `at`, interval `every` (15m/1h/6h/1d), or 5-field `cron`. Recurring jobs without threadId open a new chat each run. Worktree agents have the same tools (`threadId=self` continues that coding chat). Jobs fire only while Sideboard.app is running; sleep skips until wake. Creating or enabling a future job turns on Settings \u2192 Advanced \u2192 Caffeinate while schedules are enabled (or call set_caffeinate).",
357
357
  "Workspaces:",
358
358
  "- add_workspace / remove_workspace \u2014 register or unregister a git repo",
359
359
  "Worktree threads (chats):",
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  isOrchestratorThread
3
- } from "./chunk-SJ3IUYG2.js";
3
+ } from "./chunk-BYRZS2S2.js";
4
4
  import {
5
5
  codexUnattendedGitConfigArgs,
6
6
  mergeAgentGitAuthEnv,
7
7
  resolveAgentGitAuthEnv,
8
8
  resolveCodexGitWritableRoots
9
- } from "./chunk-NYXDZCMP.js";
9
+ } from "./chunk-63YSFXLT.js";
10
10
  import {
11
11
  applyConnectedTeamToCli,
12
12
  brightsyMcpServerName,
@@ -572,7 +572,12 @@ var WORKTREE_MCP_TOOLS = [
572
572
  "get_run_log",
573
573
  "notify_orchestrator",
574
574
  "get_viewer_context",
575
- "update_viewer_context"
575
+ "update_viewer_context",
576
+ "list_schedules",
577
+ "create_schedule",
578
+ "update_schedule",
579
+ "delete_schedule",
580
+ "run_schedule"
576
581
  ];
577
582
  var WORKTREE_GITHUB_MCP_TOOLS = [
578
583
  "github_search_issues",
@@ -623,7 +628,12 @@ var SIDEBOARD_ARTIFACT_MCP_ALLOWED_TOOLS = [
623
628
  "mcp__sideboard__get_run_log",
624
629
  "mcp__sideboard__notify_orchestrator",
625
630
  "mcp__sideboard__get_viewer_context",
626
- "mcp__sideboard__update_viewer_context"
631
+ "mcp__sideboard__update_viewer_context",
632
+ "mcp__sideboard__list_schedules",
633
+ "mcp__sideboard__create_schedule",
634
+ "mcp__sideboard__update_schedule",
635
+ "mcp__sideboard__delete_schedule",
636
+ "mcp__sideboard__run_schedule"
627
637
  ];
628
638
  var SIDEBOARD_GITHUB_MCP_ALLOWED_TOOLS = ["mcp__sideboard__github_*"];
629
639
  var SIDEBOARD_LINEAR_MCP_ALLOWED_TOOLS = ["mcp__sideboard__linear_*"];
@@ -1320,6 +1330,13 @@ function toolDescription(name, input) {
1320
1330
  return str(input?.name) ? `Read ${str(input?.name)} log` : "Read Dev log";
1321
1331
  }
1322
1332
  if (/notify_orchestrator$/i.test(name)) return "Notify orchestrator";
1333
+ if (/list_schedules$/i.test(name)) return "List schedules";
1334
+ if (/create_schedule$/i.test(name)) {
1335
+ return str(input?.name) ? `Schedule ${str(input?.name)}` : "Create schedule";
1336
+ }
1337
+ if (/update_schedule$/i.test(name)) return "Update schedule";
1338
+ if (/delete_schedule$/i.test(name)) return "Delete schedule";
1339
+ if (/run_schedule$/i.test(name)) return "Run schedule";
1323
1340
  if (isSkillToolName(name)) {
1324
1341
  const cmd = skillCommandFromInput(input);
1325
1342
  return cmd ? `Using /${cmd}` : "Using skill";
@@ -2544,7 +2561,7 @@ var claudeAdapter = {
2544
2561
  );
2545
2562
  }
2546
2563
  const mode = permissionMode(thread);
2547
- const { isOrchestratorThread: isOrchestratorThread2 } = await import("./global-workspace-PUJEGSOB.js");
2564
+ const { isOrchestratorThread: isOrchestratorThread2 } = await import("./global-workspace-4JCITHB6.js");
2548
2565
  const isOrchestrator = isOrchestratorThread2(thread);
2549
2566
  const injectedServers = await buildInjectedMcpServers({
2550
2567
  includeSideboard: true,