@sideboard-ai/core 0.1.287 → 0.1.288

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 (61) hide show
  1. package/dist/{abletime-I4RODUKT.js → abletime-4WJUVE64.js} +4 -4
  2. package/dist/{abletime-4CH2IOX5.js → abletime-VFKYNSJT.js} +4 -4
  3. package/dist/{abletime-oauth-7OZZ3ABX.js → abletime-oauth-FW24YHYX.js} +3 -3
  4. package/dist/{abletime-oauth-C2HN2Q6F.js → abletime-oauth-ICMB2XV2.js} +3 -3
  5. package/dist/{abletime-rest-E4LBRFLM.js → abletime-rest-HFMRTUDT.js} +3 -3
  6. package/dist/{abletime-rest-WFI2HWBU.js → abletime-rest-OGWC7BC6.js} +3 -3
  7. package/dist/agents/cursor-runner.js +3 -3
  8. package/dist/{agents-D3PU4UCP.js → agents-GZLTMGSH.js} +19 -17
  9. package/dist/{agents-KU4QPB3Y.js → agents-UQMHFZSM.js} +17 -15
  10. package/dist/{app-settings-EQQZQNYD.js → app-settings-EHMXHNG5.js} +1 -1
  11. package/dist/{app-settings-NI4X5HW3.js → app-settings-MR2U6AXN.js} +1 -1
  12. package/dist/{chunk-UFJLNCOF.js → chunk-2N77CLRQ.js} +8 -298
  13. package/dist/{chunk-YNTXWFXT.js → chunk-36J3IZBS.js} +12 -12
  14. package/dist/{chunk-IJMUYHPT.js → chunk-3E4NOIXN.js} +3 -3
  15. package/dist/{chunk-5MGLNVNK.js → chunk-3W5SL5EW.js} +1 -1
  16. package/dist/{chunk-QPBR5SAL.js → chunk-442O6OXM.js} +25 -127
  17. package/dist/chunk-4BXRRHDC.js +139 -0
  18. package/dist/{chunk-RFPMTOV2.js → chunk-4NM72HJZ.js} +5 -5
  19. package/dist/{chunk-Z3EE3PUZ.js → chunk-6NVQNTJO.js} +10 -300
  20. package/dist/{chunk-VOIWX5HA.js → chunk-7UUILVMV.js} +1 -1
  21. package/dist/chunk-AF2DMY3K.js +295 -0
  22. package/dist/{chunk-5QOLFOAX.js → chunk-AFLNFGGB.js} +1 -1
  23. package/dist/{chunk-5T4YWFLD.js → chunk-AHH5USEA.js} +2 -2
  24. package/dist/{chunk-6CVVNAIU.js → chunk-B3BPLPKQ.js} +2 -2
  25. package/dist/{chunk-ZGAMETNF.js → chunk-DBOFDEQ7.js} +3 -3
  26. package/dist/{chunk-VQEARG5H.js → chunk-GU6DT6NO.js} +5 -5
  27. package/dist/{chunk-K5XSTHHP.js → chunk-HNB2HTCV.js} +1 -1
  28. package/dist/{chunk-KLAQ4G34.js → chunk-IA4MPG7F.js} +3 -3
  29. package/dist/{chunk-S2OOM7AU.js → chunk-K6IOJI3L.js} +1 -1
  30. package/dist/{chunk-U6QDXT4K.js → chunk-MIVX7QKI.js} +1 -1
  31. package/dist/{chunk-WRIMAEF2.js → chunk-MVZO7QZ2.js} +12 -12
  32. package/dist/{chunk-LS4FFCPL.js → chunk-MYJYINTS.js} +3 -3
  33. package/dist/{chunk-DEJALWKM.js → chunk-NFPWLDCH.js} +5 -5
  34. package/dist/chunk-NXXQFLNQ.js +300 -0
  35. package/dist/chunk-OH4EN323.js +89 -0
  36. package/dist/chunk-W2KZTDET.js +302 -0
  37. package/dist/{chunk-L65YT2SC.js → chunk-WQSIUZEB.js} +6 -6
  38. package/dist/{chunk-LKVP7JCB.js → chunk-WW5XGEMY.js} +33 -313
  39. package/dist/{chunk-TMM4FEZ5.js → chunk-Y67TBJD5.js} +3 -3
  40. package/dist/{chunk-XSDCLM3F.js → chunk-YSLCWCMJ.js} +1 -1
  41. package/dist/{conductor-FOKYVMUE.js → conductor-MCU63Z5U.js} +5 -5
  42. package/dist/{conductor-KDUL6AMD.js → conductor-ZLYZVIAV.js} +6 -6
  43. package/dist/{coordinator-prompt-GU5PGLG6.js → coordinator-prompt-KHCXXDRO.js} +3 -3
  44. package/dist/{coordinator-prompt-YARYX53V.js → coordinator-prompt-UV6THLMD.js} +3 -3
  45. package/dist/{global-workspace-C5HMC66E.js → global-workspace-AXTSYVAE.js} +3 -3
  46. package/dist/{global-workspace-SMCPOWE5.js → global-workspace-GDHZSFP6.js} +3 -3
  47. package/dist/index.cjs +469 -274
  48. package/dist/index.d.cts +24 -8
  49. package/dist/index.d.ts +24 -8
  50. package/dist/index.js +369 -256
  51. package/dist/mcp/run-stdio.cjs +602 -225
  52. package/dist/mcp/run-stdio.js +333 -187
  53. package/dist/optional-cli-6SW4OOX3.js +23 -0
  54. package/dist/optional-cli-FRUZSZ3U.js +98 -0
  55. package/dist/{orchestrator-NWKE56BW.js → orchestrator-5ZBHR6BJ.js} +9 -7
  56. package/dist/{orchestrator-2UAVOXTN.js → orchestrator-SDH6W6ZL.js} +12 -10
  57. package/dist/{workspaces-DQMDCRB2.js → workspaces-L2UR4KC2.js} +3 -3
  58. package/dist/{workspaces-KHQ5ETSQ.js → workspaces-Q2R2XGO3.js} +3 -3
  59. package/dist/{worktree-NKLNVNXX.js → worktree-2GOK4VYT.js} +2 -2
  60. package/dist/{worktree-YTZC3UQN.js → worktree-YEUTJ7MZ.js} +2 -2
  61. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -12,8 +12,6 @@ import {
12
12
  LIVE_TURN_SPAWN_GRACE_MS,
13
13
  LONG_RUNNING_SKILL_COMMAND,
14
14
  MAX_RUN_LOG_CHARS,
15
- OPTIONAL_SERVICES,
16
- OPTIONAL_SERVICE_IDS,
17
15
  Orchestrator,
18
16
  RUN_SCRIPT_DESKTOP_ADOPT_MS,
19
17
  addStackLayerFromThread,
@@ -41,8 +39,6 @@ import {
41
39
  computeNextRunAt,
42
40
  conductorDbPath,
43
41
  confirmLand,
44
- connectOptionalService,
45
- connectedOptionalServices,
46
42
  consumeComposerAttachments,
47
43
  createPrStack,
48
44
  createSchedule,
@@ -53,7 +49,6 @@ import {
53
49
  deleteSchedule,
54
50
  desktopHostPidPath,
55
51
  detectAgents,
56
- disconnectOptionalService,
57
52
  discoverSkills,
58
53
  emptyRunLog,
59
54
  emptySetupLog,
@@ -81,8 +76,6 @@ import {
81
76
  formatLongRunningDirective,
82
77
  formatLongRunningReminder,
83
78
  formatNotifyOrchestratorPrompt,
84
- formatOptionalServicesDirective,
85
- formatOptionalServicesReminder,
86
79
  formatPrGateDirective,
87
80
  formatProcessGuideDirective,
88
81
  formatRenameBranchDirective,
@@ -109,7 +102,6 @@ import {
109
102
  isDesktopHostAlive,
110
103
  isHomeBoardThread,
111
104
  isInjectedNoticeText,
112
- isOptionalServiceId,
113
105
  isPidAlive,
114
106
  isPrimaryCheckoutThread,
115
107
  isSetupLastError,
@@ -135,12 +127,9 @@ import {
135
127
  mentionsPrGoal,
136
128
  mergeSetupOutput,
137
129
  moveQueuedItemToFront,
138
- normalizeServiceOrigin,
139
130
  notifyOrchestrator,
140
131
  openPrStackLayers,
141
132
  openStackLayer,
142
- optionalServiceConnected,
143
- optionalServiceSpec,
144
133
  packagedDetachedJobPath,
145
134
  parseDurationMs,
146
135
  parseIssueSince,
@@ -190,7 +179,6 @@ import {
190
179
  takePendingTurnAttachments,
191
180
  thisProcessShouldDrainAgentQueues,
192
181
  updateSchedule,
193
- verifyOptionalService,
194
182
  visibleAgentBlock,
195
183
  waitForPidExit,
196
184
  workspaceTagTargetRef,
@@ -198,7 +186,22 @@ import {
198
186
  worktreeBoardStatus,
199
187
  worktreeCleanupSettings,
200
188
  writeWorktreeFile
201
- } from "./chunk-LKVP7JCB.js";
189
+ } from "./chunk-WW5XGEMY.js";
190
+ import {
191
+ LEGACY_PLAN_FILE_REL,
192
+ PLAN_FILE_NAME,
193
+ PLAN_FILE_REL,
194
+ PRESENT_PLAN_REQUIRES_PLAN_MODE,
195
+ PRESENT_PLAN_TOOL_DESCRIPTION,
196
+ extractPresentedPlan,
197
+ isPresentPlanToolName,
198
+ planFileAbs,
199
+ readPlanFile,
200
+ resolvePlanMarkdown,
201
+ shouldRegisterPresentPlan,
202
+ threadMayPresentPlan,
203
+ writePlanFile
204
+ } from "./chunk-PGPSIEMW.js";
202
205
  import {
203
206
  BASE_ALLOWED_TOOLS,
204
207
  BRIGHTSY_MCP_ALLOWED_TOOLS,
@@ -239,42 +242,33 @@ import {
239
242
  formatClaudeUsageResetClock,
240
243
  formatClaudeUsageTooltip,
241
244
  getAdapter,
242
- getAgentSetupInfo,
243
245
  getClaudePlanUsage,
244
246
  hottestClaudeWindow,
245
- installAgent,
246
- installNpmGlobalPackage,
247
247
  isBrightsyConnected,
248
248
  isCursorApiKeyConfigured,
249
249
  isCursorAutoModel,
250
250
  isInjectedOrchMcpName,
251
251
  isSessionQuotaLimit,
252
- listAgentSetupInfo,
253
252
  listBrightsyChatTargets,
254
253
  listCodexModels,
255
254
  listCursorModels,
256
255
  listModelsForAgent,
257
256
  listOpencodeModels,
258
- loginAgent,
259
- loginManagedAccount,
260
257
  mcpAllowTools,
261
258
  mcpAllowToolsFromNames,
262
259
  mcpAuthWarnings,
263
260
  mcpStatusThinkingFromClaudeInit,
264
261
  mergeSideboardIntoMcpServersJson,
265
262
  normalizeTurnInput,
266
- openInSystemTerminal,
267
263
  opencodeAdapter,
268
264
  parseClaudeUsagePayload,
269
265
  parseMcpList,
270
266
  parseSessionQuotaResetAt,
271
267
  permissionMode,
272
268
  planModeTurnInstruction,
273
- prepareTerminalCommand,
274
269
  promptMentionsBrightsy,
275
270
  registerPackagedUserMcpClients,
276
271
  resolveCursorModelId,
277
- resolveLoginCommand,
278
272
  resolveQuotaFallbackAgent,
279
273
  sanitizeMcpServerName,
280
274
  shouldInjectBrightsyMcp,
@@ -285,10 +279,9 @@ import {
285
279
  userClaudeMcpConfigPath,
286
280
  userCursorMcpConfigPath,
287
281
  userMcpNamesToDisable,
288
- whichOnPath,
289
282
  worktreeMcpToolNames,
290
283
  writeInjectedMcpConfig
291
- } from "./chunk-Z3EE3PUZ.js";
284
+ } from "./chunk-6NVQNTJO.js";
292
285
  import {
293
286
  brightsyMcpServerName,
294
287
  connectBrightsyTeam,
@@ -415,7 +408,7 @@ import {
415
408
  workspaceAgentChatSummaries,
416
409
  worktreeRunPortsPath,
417
410
  wrapReviewSkillMarkdown
418
- } from "./chunk-L65YT2SC.js";
411
+ } from "./chunk-WQSIUZEB.js";
419
412
  import {
420
413
  STDERR_EVENT_MAX_CHARS,
421
414
  STDOUT_EVENT_MAX_CHARS,
@@ -457,7 +450,7 @@ import {
457
450
  visibleToolRowDetail,
458
451
  withEventParentId,
459
452
  withEventsParentId
460
- } from "./chunk-XSDCLM3F.js";
453
+ } from "./chunk-YSLCWCMJ.js";
461
454
  import "./chunk-7H655NEW.js";
462
455
  import {
463
456
  ABLETIME_OAUTH_CALLBACK_PATH,
@@ -481,7 +474,7 @@ import {
481
474
  parseAbleTimeOAuthCallbackUrl,
482
475
  resolveAbleTimeOAuthHost,
483
476
  startAbleTimeOAuth
484
- } from "./chunk-IJMUYHPT.js";
477
+ } from "./chunk-3E4NOIXN.js";
485
478
  import {
486
479
  ableTimeDependencyTarget,
487
480
  commentAbleTimeTask,
@@ -506,8 +499,8 @@ import {
506
499
  toAbleTimeIssueInfo,
507
500
  updateAbleTimeTask,
508
501
  verifyAbleTimeConnection
509
- } from "./chunk-LS4FFCPL.js";
510
- import "./chunk-6CVVNAIU.js";
502
+ } from "./chunk-MYJYINTS.js";
503
+ import "./chunk-B3BPLPKQ.js";
511
504
  import {
512
505
  ABLETIME_MCP_PATH,
513
506
  ABLETIME_MCP_PM_PATH,
@@ -528,22 +521,41 @@ import {
528
521
  normalizeAbleTimeCredential,
529
522
  normalizeAbleTimeHost,
530
523
  rewriteAbleTimeError
531
- } from "./chunk-DEJALWKM.js";
524
+ } from "./chunk-NFPWLDCH.js";
532
525
  import {
533
- LEGACY_PLAN_FILE_REL,
534
- PLAN_FILE_NAME,
535
- PLAN_FILE_REL,
536
- PRESENT_PLAN_REQUIRES_PLAN_MODE,
537
- PRESENT_PLAN_TOOL_DESCRIPTION,
538
- extractPresentedPlan,
539
- isPresentPlanToolName,
540
- planFileAbs,
541
- readPlanFile,
542
- resolvePlanMarkdown,
543
- shouldRegisterPresentPlan,
544
- threadMayPresentPlan,
545
- writePlanFile
546
- } from "./chunk-PGPSIEMW.js";
526
+ clearOptionalCliPresenceCache,
527
+ detectOptionalServiceClis,
528
+ installOptionalServiceCli,
529
+ optionalCliPresence
530
+ } from "./chunk-OH4EN323.js";
531
+ import {
532
+ OPTIONAL_SERVICES,
533
+ OPTIONAL_SERVICE_IDS,
534
+ connectOptionalService,
535
+ connectedOptionalServices,
536
+ disconnectOptionalService,
537
+ formatOptionalServicesDirective,
538
+ formatOptionalServicesDirectiveForTurn,
539
+ formatOptionalServicesReminder,
540
+ formatOptionalServicesReminderForTurn,
541
+ isOptionalServiceId,
542
+ normalizeServiceOrigin,
543
+ optionalServiceConnected,
544
+ optionalServiceSpec,
545
+ verifyOptionalService
546
+ } from "./chunk-AF2DMY3K.js";
547
+ import {
548
+ getAgentSetupInfo,
549
+ installAgent,
550
+ installNpmGlobalPackage,
551
+ listAgentSetupInfo,
552
+ loginAgent,
553
+ loginManagedAccount,
554
+ openInSystemTerminal,
555
+ prepareTerminalCommand,
556
+ resolveLoginCommand,
557
+ whichOnPath
558
+ } from "./chunk-NXXQFLNQ.js";
547
559
  import {
548
560
  CLOUD_COORDINATOR_BUSY_REPLY,
549
561
  CLOUD_COORDINATOR_CANCELED_REPLY,
@@ -596,7 +608,7 @@ import {
596
608
  stageBuffersAsAttachments,
597
609
  syncWorkspacesFromThreads,
598
610
  takenTeamSlugsForOrchestration
599
- } from "./chunk-ZGAMETNF.js";
611
+ } from "./chunk-DBOFDEQ7.js";
600
612
  import {
601
613
  GITHUB_PR_BODY_MAX_CHARS,
602
614
  GITHUB_PR_BODY_SAFE_CHARS,
@@ -684,7 +696,7 @@ import {
684
696
  suggestSlug,
685
697
  switchWorktreeBranch,
686
698
  warmGithubAgentAuth
687
- } from "./chunk-K5XSTHHP.js";
699
+ } from "./chunk-HNB2HTCV.js";
688
700
  import {
689
701
  FAMOUS_SOCCER_TEAMS,
690
702
  allocateTeamName,
@@ -739,7 +751,7 @@ import {
739
751
  } from "./chunk-JAT3JFOS.js";
740
752
  import {
741
753
  clipMcpToolResult
742
- } from "./chunk-S2OOM7AU.js";
754
+ } from "./chunk-K6IOJI3L.js";
743
755
  import "./chunk-AXQ4ZWHK.js";
744
756
  import {
745
757
  AGENT_RUNNER_MAX_OLD_SPACE_MB,
@@ -755,8 +767,8 @@ import {
755
767
  trustSystemCertificates,
756
768
  turnCostUsdFromCursorUsage,
757
769
  withMaxOldSpaceSize
758
- } from "./chunk-VOIWX5HA.js";
759
- import "./chunk-U6QDXT4K.js";
770
+ } from "./chunk-7UUILVMV.js";
771
+ import "./chunk-MIVX7QKI.js";
760
772
  import {
761
773
  ATTACHMENTS_DIR,
762
774
  CLI_DIR,
@@ -952,7 +964,7 @@ import {
952
964
  withTurnScopedCost,
953
965
  writeSecureJson,
954
966
  writeViewerContext
955
- } from "./chunk-YNTXWFXT.js";
967
+ } from "./chunk-36J3IZBS.js";
956
968
  import {
957
969
  THINKING_EFFORTS,
958
970
  isThinkingEffort,
@@ -3351,51 +3363,6 @@ function commentsFromAbleTimeIssues(tasks, issues, sinceIso) {
3351
3363
  return out;
3352
3364
  }
3353
3365
 
3354
- // src/integrations/optional-cli.ts
3355
- async function detectOptionalServiceClis() {
3356
- enrichPathWithNpmGlobalBin();
3357
- return Promise.all(
3358
- OPTIONAL_SERVICES.map(async (spec) => {
3359
- if (!spec.cli) {
3360
- return { id: spec.id, cli: null, installed: false, path: null };
3361
- }
3362
- const path2 = await whichOnPath(spec.cli);
3363
- return {
3364
- id: spec.id,
3365
- cli: spec.cli,
3366
- installed: Boolean(path2),
3367
- path: path2
3368
- };
3369
- })
3370
- );
3371
- }
3372
- async function installOptionalServiceCli(id) {
3373
- if (!isOptionalServiceId(id)) {
3374
- return { ok: false, message: "Unknown optional service" };
3375
- }
3376
- const spec = optionalServiceSpec(id);
3377
- if (!spec.cli || !spec.npmPackage) {
3378
- return {
3379
- ok: false,
3380
- message: `${spec.label} has no CLI to install. Use the HTTP API with the stored token.`
3381
- };
3382
- }
3383
- enrichPathWithNpmGlobalBin();
3384
- const existing = await whichOnPath(spec.cli);
3385
- if (existing) {
3386
- return {
3387
- ok: true,
3388
- command: existing,
3389
- message: `Already on PATH: ${existing}`
3390
- };
3391
- }
3392
- return installNpmGlobalPackage({
3393
- npmPackage: spec.npmPackage,
3394
- installCommand: `npm install -g ${spec.npmPackage}`,
3395
- cliBin: spec.cli
3396
- });
3397
- }
3398
-
3399
3366
  // src/composer/diff-comment.ts
3400
3367
  function lineRangeLabel(lines) {
3401
3368
  const nos = lines.map((l) => l.lineNo);
@@ -3657,7 +3624,7 @@ function createClippedMcpServer() {
3657
3624
  }
3658
3625
 
3659
3626
  // src/mcp/server.ts
3660
- import { z as z6 } from "zod";
3627
+ import { z as z7 } from "zod";
3661
3628
  import { randomUUID as randomUUID5 } from "crypto";
3662
3629
  import { basename as basename2 } from "path";
3663
3630
 
@@ -4719,6 +4686,147 @@ function registerConnectedIssueVendorTools(server, settings = loadAppSettings())
4719
4686
  if (isAbleTimeConnected(settings)) registerAbleTimeTools(server);
4720
4687
  }
4721
4688
 
4689
+ // src/mcp/posthog-tools.ts
4690
+ import { z as z5 } from "zod";
4691
+ var POSTHOG_METHODS = ["GET", "POST", "PATCH"];
4692
+ var POSTHOG_RESPONSE_MAX_CHARS = 256e3;
4693
+ var POSTHOG_TIMEOUT_MS = 2e4;
4694
+ var POSTHOG_READ_QUERY = /^\/api\/(?:projects|environments)\/[^/]+\/query$/;
4695
+ function isPosthogReadQuery(method, path2) {
4696
+ if (method !== "POST") return false;
4697
+ return POSTHOG_READ_QUERY.test(path2.trim().replace(/\/+$/, ""));
4698
+ }
4699
+ function withPosthogSlash(method, path2) {
4700
+ const raw = path2.trim();
4701
+ if ((method === "POST" || method === "PATCH") && raw.startsWith("/api/") && !raw.endsWith("/")) {
4702
+ return `${raw}/`;
4703
+ }
4704
+ return raw;
4705
+ }
4706
+ function posthogApiUrl(host, path2, query) {
4707
+ const raw = path2.trim();
4708
+ if (!raw.startsWith("/api/")) {
4709
+ throw new Error("path must start with /api/");
4710
+ }
4711
+ if (raw.includes("://") || raw.startsWith("//") || /[?#\s\\]/.test(raw) || raw.includes("..")) {
4712
+ throw new Error("path must be a PostHog API path, not a URL");
4713
+ }
4714
+ const origin = normalizeServiceOrigin(host, "https://us.posthog.com");
4715
+ const url = new URL(raw, origin);
4716
+ if (url.origin !== new URL(origin).origin || !url.pathname.startsWith("/api/")) {
4717
+ throw new Error("path must stay on the connected PostHog host");
4718
+ }
4719
+ if (query) {
4720
+ for (const [key, value] of Object.entries(query)) {
4721
+ if (!key.trim()) throw new Error("query keys must be non-empty");
4722
+ url.searchParams.set(key, value);
4723
+ }
4724
+ }
4725
+ return url.toString();
4726
+ }
4727
+ async function runPosthogApi(input) {
4728
+ const method = input.method;
4729
+ if (!POSTHOG_METHODS.includes(method)) {
4730
+ return mcpJson({ ok: false, error: "method must be GET, POST, or PATCH" }, true);
4731
+ }
4732
+ if (method !== "GET" && input.write !== true && !isPosthogReadQuery(method, input.path)) {
4733
+ return mcpJson(
4734
+ {
4735
+ ok: false,
4736
+ error: "POST and PATCH require write=true, and only when the user asked to change PostHog data. POST /api/projects/:id/query/ and POST /api/environments/:id/query/ are reads and do not need write=true."
4737
+ },
4738
+ true
4739
+ );
4740
+ }
4741
+ if (method === "GET" && input.body !== void 0) {
4742
+ return mcpJson({ ok: false, error: "GET does not take a body" }, true);
4743
+ }
4744
+ const integrations = loadAppSettings().integrations;
4745
+ const token = integrations.posthogPersonalApiKey?.trim();
4746
+ if (!token) {
4747
+ return mcpJson(
4748
+ { ok: false, error: "PostHog is not connected (Settings \u2192 Connectors)." },
4749
+ true
4750
+ );
4751
+ }
4752
+ let url;
4753
+ try {
4754
+ url = posthogApiUrl(
4755
+ integrations.posthogHost || "https://us.posthog.com",
4756
+ withPosthogSlash(method, input.path),
4757
+ input.query
4758
+ );
4759
+ } catch (err) {
4760
+ return mcpJson(
4761
+ { ok: false, error: err instanceof Error ? err.message : String(err) },
4762
+ true
4763
+ );
4764
+ }
4765
+ const headers = {
4766
+ Authorization: `Bearer ${token}`,
4767
+ Accept: "application/json"
4768
+ };
4769
+ let body;
4770
+ if (input.body !== void 0) {
4771
+ headers["Content-Type"] = "application/json";
4772
+ body = typeof input.body === "string" ? input.body : JSON.stringify(input.body);
4773
+ }
4774
+ try {
4775
+ const res = await httpFetch(url, {
4776
+ method,
4777
+ headers,
4778
+ body,
4779
+ signal: AbortSignal.timeout(POSTHOG_TIMEOUT_MS)
4780
+ });
4781
+ const text5 = await res.text();
4782
+ const truncated = text5.length > POSTHOG_RESPONSE_MAX_CHARS;
4783
+ const slice = truncated ? text5.slice(0, POSTHOG_RESPONSE_MAX_CHARS) : text5;
4784
+ let parsed = slice;
4785
+ if (slice.trim()) {
4786
+ try {
4787
+ parsed = JSON.parse(slice);
4788
+ } catch {
4789
+ parsed = slice;
4790
+ }
4791
+ } else {
4792
+ parsed = null;
4793
+ }
4794
+ return mcpJson(
4795
+ {
4796
+ ok: res.ok,
4797
+ status: res.status,
4798
+ ...truncated ? { truncated: true } : {},
4799
+ body: parsed
4800
+ },
4801
+ !res.ok
4802
+ );
4803
+ } catch (err) {
4804
+ return mcpJson(
4805
+ { ok: false, error: err instanceof Error ? err.message : String(err) },
4806
+ true
4807
+ );
4808
+ }
4809
+ }
4810
+ var POSTHOG_API_DESCRIPTION = "Call the connected PostHog project (Settings \u2192 Connectors). path must start with /api/ and is joined to the stored host. GET is the default. POST /api/projects/:id/query/ and POST /api/environments/:id/query/ are reads (HogQL and insights) and do not need write=true. Other POST and PATCH require write=true, and only when the user asked to change PostHog data. The API key is not in the environment. Do not sign in to a PostHog MCP.";
4811
+ function registerPosthogTools(server) {
4812
+ server.tool(
4813
+ "posthog_api",
4814
+ POSTHOG_API_DESCRIPTION,
4815
+ {
4816
+ method: z5.enum(POSTHOG_METHODS).describe(
4817
+ "GET, POST /api/projects|environments/:id/query/ for a read, or POST/PATCH when the user asked for a change"
4818
+ ),
4819
+ path: z5.string().describe("PostHog path starting with /api/, not a full URL"),
4820
+ query: z5.record(z5.string(), z5.string()).optional().describe("Query string parameters"),
4821
+ body: z5.unknown().optional().describe("JSON body for POST or PATCH"),
4822
+ write: z5.boolean().optional().describe(
4823
+ "Required true for POST and PATCH that change data. Omit for GET and for POST .../query/."
4824
+ )
4825
+ },
4826
+ async (input) => runPosthogApi(input)
4827
+ );
4828
+ }
4829
+
4722
4830
  // src/mcp/pr-list.ts
4723
4831
  function compactPrRow(pr) {
4724
4832
  return {
@@ -4754,7 +4862,7 @@ function formatMcpPrList(input) {
4754
4862
  }
4755
4863
 
4756
4864
  // src/mcp/schedule-tools.ts
4757
- import { z as z5 } from "zod";
4865
+ import { z as z6 } from "zod";
4758
4866
  function text4(payload, isError = false) {
4759
4867
  return mcpJson(payload, isError);
4760
4868
  }
@@ -4794,18 +4902,18 @@ function registerScheduleTools(server) {
4794
4902
  "create_schedule",
4795
4903
  "Create a local schedule that, when due, sends a prompt to an existing chat (chatId) or starts a new Global orchestration chat (omit chatId). Pass chatId=self to continue this chat. If that chat is missing or archived when the job fires, Sideboard starts a new Global chat (recurring jobs then continue that chat). Exactly one of at (ISO datetime), every (15m/1h/6h/1d), or cron (5-field). Recurring jobs without chatId open a new Global chat each run. Creating or enabling a future job turns on Settings \u2192 Advanced \u2192 Caffeinate while schedules are enabled so the Mac stays awake until due. Sideboard.app must be running for the job to fire.",
4796
4904
  {
4797
- prompt: z5.string().describe("User message / goal queued when the schedule fires"),
4798
- name: z5.string().optional(),
4799
- at: z5.string().optional().describe("ISO datetime for a one-shot"),
4800
- every: z5.string().optional().describe("Interval such as 15m, 1h, 6h, 1d"),
4801
- cron: z5.string().optional().describe("5-field cron expression"),
4802
- tz: z5.string().optional().describe("IANA timezone for cron (default: system)"),
4803
- chatId: z5.string().optional().describe(
4905
+ prompt: z6.string().describe("User message / goal queued when the schedule fires"),
4906
+ name: z6.string().optional(),
4907
+ at: z6.string().optional().describe("ISO datetime for a one-shot"),
4908
+ every: z6.string().optional().describe("Interval such as 15m, 1h, 6h, 1d"),
4909
+ cron: z6.string().optional().describe("5-field cron expression"),
4910
+ tz: z6.string().optional().describe("IANA timezone for cron (default: system)"),
4911
+ chatId: z6.string().optional().describe(
4804
4912
  'Existing chat id (orchestration or workspace), or "self" for this chat. Omit to create a new Global chat on fire.'
4805
4913
  ),
4806
- threadId: z5.string().optional().describe("Legacy alias for chatId."),
4807
- agent: z5.enum(["claude", "cursor", "codex", "opencode"]).optional().describe("Agent for a new Global chat (omit for Account default)"),
4808
- model: z5.string().optional()
4914
+ threadId: z6.string().optional().describe("Legacy alias for chatId."),
4915
+ agent: z6.enum(["claude", "cursor", "codex", "opencode"]).optional().describe("Agent for a new Global chat (omit for Account default)"),
4916
+ model: z6.string().optional()
4809
4917
  },
4810
4918
  async (args) => {
4811
4919
  try {
@@ -4841,20 +4949,20 @@ function registerScheduleTools(server) {
4841
4949
  "update_schedule",
4842
4950
  "Update a local schedule (prompt, cadence, target chat, enabled). Pass id from list_schedules.",
4843
4951
  {
4844
- id: z5.string(),
4845
- prompt: z5.string().optional(),
4846
- name: z5.string().optional(),
4847
- at: z5.string().optional(),
4848
- every: z5.string().optional(),
4849
- cron: z5.string().optional(),
4850
- tz: z5.string().optional(),
4851
- chatId: z5.string().optional().describe(
4952
+ id: z6.string(),
4953
+ prompt: z6.string().optional(),
4954
+ name: z6.string().optional(),
4955
+ at: z6.string().optional(),
4956
+ every: z6.string().optional(),
4957
+ cron: z6.string().optional(),
4958
+ tz: z6.string().optional(),
4959
+ chatId: z6.string().optional().describe(
4852
4960
  'Existing chat (orchestration or workspace), "self", or empty string to create a new Global chat each run'
4853
4961
  ),
4854
- threadId: z5.string().optional().describe("Legacy alias for chatId."),
4855
- agent: z5.enum(["claude", "cursor", "codex", "opencode"]).optional(),
4856
- model: z5.string().optional(),
4857
- enabled: z5.boolean().optional()
4962
+ threadId: z6.string().optional().describe("Legacy alias for chatId."),
4963
+ agent: z6.enum(["claude", "cursor", "codex", "opencode"]).optional(),
4964
+ model: z6.string().optional(),
4965
+ enabled: z6.boolean().optional()
4858
4966
  },
4859
4967
  async (args) => {
4860
4968
  try {
@@ -4895,7 +5003,7 @@ function registerScheduleTools(server) {
4895
5003
  server.tool(
4896
5004
  "delete_schedule",
4897
5005
  "Delete a local schedule. Pass id from list_schedules.",
4898
- { id: z5.string() },
5006
+ { id: z6.string() },
4899
5007
  async ({ id }) => {
4900
5008
  try {
4901
5009
  deleteSchedule(id);
@@ -4908,7 +5016,7 @@ function registerScheduleTools(server) {
4908
5016
  server.tool(
4909
5017
  "run_schedule",
4910
5018
  "Fire a schedule now (does not wait for nextRunAt). Queues the prompt on the target chat or starts a new Global chat. If Sideboard.app is running, the desktop drains the turn.",
4911
- { id: z5.string() },
5019
+ { id: z6.string() },
4912
5020
  async ({ id }) => {
4913
5021
  try {
4914
5022
  if (!getSchedule(id)) {
@@ -5018,7 +5126,7 @@ async function handleJobStop(params, _ctx) {
5018
5126
  return stopDetachedJob(jobCwd(rec), id, reason ? { reason } : void 0);
5019
5127
  }
5020
5128
  async function defaultTurnWait(ref, timeoutMs) {
5021
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-2UAVOXTN.js");
5129
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-SDH6W6ZL.js");
5022
5130
  const orch = getOrchestrator2();
5023
5131
  const thread = await orch.waitForTurn(ref, timeoutMs, {
5024
5132
  resolveIfStillRunning: true
@@ -5370,7 +5478,7 @@ async function startMcpServer() {
5370
5478
  );
5371
5479
  }
5372
5480
  try {
5373
- const { maxConcurrentAgents: maxConcurrentAgents2 } = await import("./app-settings-NI4X5HW3.js");
5481
+ const { maxConcurrentAgents: maxConcurrentAgents2 } = await import("./app-settings-MR2U6AXN.js");
5374
5482
  orch.setMaxConcurrent(maxConcurrentAgents2());
5375
5483
  } catch {
5376
5484
  }
@@ -5398,6 +5506,7 @@ async function startMcpServer() {
5398
5506
  }
5399
5507
  };
5400
5508
  registerViewerContextTools(server);
5509
+ registerPosthogTools(server);
5401
5510
  if (worktreeProfile) {
5402
5511
  registerConnectedIssueVendorTools(server);
5403
5512
  }
@@ -5453,16 +5562,16 @@ async function startMcpServer() {
5453
5562
  "list_board",
5454
5563
  "Home Kanban of workspaces (New, Draft, Review, Merged) \u2014 one card per checkout; sibling chats on that card are separate agents. 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_workspace adds a workspace (and a Home card), or returns the live checkout if that ticket/PR/named branch is already checked out. That reuse is one checkout, not one agent \u2014 fork_chat to add another.",
5455
5564
  {
5456
- query: z6.string().optional().describe("Case-insensitive token search across title, id, labels, workspace tags, repo"),
5457
- repoPath: z6.string().optional().describe("Limit to one project path from list_projects"),
5458
- kind: z6.enum(["all", "tickets", "prs", "branches", "threads"]).optional().describe("Filter by worktree source (default all)"),
5459
- ownership: z6.enum(["all", "mine", "reviewing"]).optional().describe(
5565
+ query: z7.string().optional().describe("Case-insensitive token search across title, id, labels, workspace tags, repo"),
5566
+ repoPath: z7.string().optional().describe("Limit to one project path from list_projects"),
5567
+ kind: z7.enum(["all", "tickets", "prs", "branches", "threads"]).optional().describe("Filter by worktree source (default all)"),
5568
+ ownership: z7.enum(["all", "mine", "reviewing"]).optional().describe(
5460
5569
  "Mine = PRs you authored or local WIP; reviewing = someone else's PR. Uses the gh login."
5461
5570
  ),
5462
- column: z6.enum(["new", "draft", "review", "done", "needs_you"]).optional().describe(
5571
+ column: z7.enum(["new", "draft", "review", "done", "needs_you"]).optional().describe(
5463
5572
  "Return cards for this column only (totals still include the rest). needs_you is a legacy alias for new."
5464
5573
  ),
5465
- limit: z6.number().int().positive().optional().describe("Max cards per column (default 40). hidden counts the remainder.")
5574
+ limit: z7.number().int().positive().optional().describe("Max cards per column (default 40). hidden counts the remainder.")
5466
5575
  },
5467
5576
  async ({ query, repoPath, kind, ownership, column, limit }) => {
5468
5577
  const workspaces = orch.listWorkspaces();
@@ -5492,7 +5601,7 @@ async function startMcpServer() {
5492
5601
  server.tool(
5493
5602
  "get_chat",
5494
5603
  "Get a compact chat summary by id/ref. Includes last message preview, parentChatId, and child workspace chats (status + lastText). While running, includes progress (last tool/thinking) and lastActivityAt. Includes usage (billed token + costUsd totals when providers reported cost) and lastTurnUsage. blockedReason is why that agent is waiting on a person (question, plan approval, or a reported block).",
5495
- { ref: z6.string() },
5604
+ { ref: z7.string() },
5496
5605
  async ({ ref }) => {
5497
5606
  const t = orch.getThread(ref);
5498
5607
  if (!t) {
@@ -5539,17 +5648,17 @@ async function startMcpServer() {
5539
5648
  "present_artifact",
5540
5649
  "Show a document or live log in Sideboard\u2019s side column. For html/svg/markdown/react, pass the FULL document and do not also fence that same body in chat. For type=log, pass only NEW lines (same artifact_id appends). Prefer type=log for long-running job output \u2014 do not resend HTML. type=react is a single default-export component (JSX/TSX); only react/react-dom imports.",
5541
5650
  {
5542
- title: z6.string().describe("Short title shown in the artifact pane header"),
5543
- type: z6.enum(["html", "svg", "markdown", "react", "log"]).describe(
5651
+ title: z7.string().describe("Short title shown in the artifact pane header"),
5652
+ type: z7.enum(["html", "svg", "markdown", "react", "log"]).describe(
5544
5653
  "html/svg/markdown/react replace the pane. log appends content to the same artifact_id (new lines only)."
5545
5654
  ),
5546
- content: z6.string().describe(
5655
+ content: z7.string().describe(
5547
5656
  "html/svg/markdown/react: full document. log: only the new lines since the last call (empty is ok for a status-only update)."
5548
5657
  ),
5549
- artifact_id: z6.string().optional().describe("Stable id. Required for type=log so later calls append to the same pane."),
5550
- status: z6.enum(["running", "ok", "failed", "idle"]).optional().describe("Log header pill: running (working), ok (done), failed, idle"),
5551
- phase: z6.string().optional().describe("Log subtitle (Signing, Notarizing, \u2026)"),
5552
- mode: z6.enum(["append", "replace"]).optional().describe("log only: append (default) or replace the buffer")
5658
+ artifact_id: z7.string().optional().describe("Stable id. Required for type=log so later calls append to the same pane."),
5659
+ status: z7.enum(["running", "ok", "failed", "idle"]).optional().describe("Log header pill: running (working), ok (done), failed, idle"),
5660
+ phase: z7.string().optional().describe("Log subtitle (Signing, Notarizing, \u2026)"),
5661
+ mode: z7.enum(["append", "replace"]).optional().describe("log only: append (default) or replace the buffer")
5553
5662
  },
5554
5663
  async ({ title, type, artifact_id, status, phase, mode }) => {
5555
5664
  return mcpJson(
@@ -5562,15 +5671,15 @@ async function startMcpServer() {
5562
5671
  "ask_user",
5563
5672
  "Ask the user a clarifying multiple-choice question in Sideboard\u2019s composer. Call only when work is blocked on choosing among a few concrete options (approach fork, which API, auth vs cookies). Do not call for greetings, check-ins, \u201Chello\u201D, open-ended how-can-I-help, or to invent a menu of possible next tasks \u2014 reply in chat instead. If one option is the obvious default, proceed without asking. Before calling, write a short chat message explaining the decision and what each option means. Include a description on every option. After calling, stop and wait for answers. Not for \u201Cis the plan ready?\u201D.",
5564
5673
  {
5565
- questions: z6.array(
5566
- z6.object({
5567
- question: z6.string().describe("Full question text ending with ?"),
5568
- header: z6.string().max(24).optional().describe("Short label shown above the question"),
5569
- multiSelect: z6.boolean().optional().describe("Allow selecting multiple options"),
5570
- options: z6.array(
5571
- z6.object({
5572
- label: z6.string(),
5573
- description: z6.string().optional().describe("What this option means / when to choose it (strongly preferred)")
5674
+ questions: z7.array(
5675
+ z7.object({
5676
+ question: z7.string().describe("Full question text ending with ?"),
5677
+ header: z7.string().max(24).optional().describe("Short label shown above the question"),
5678
+ multiSelect: z7.boolean().optional().describe("Allow selecting multiple options"),
5679
+ options: z7.array(
5680
+ z7.object({
5681
+ label: z7.string(),
5682
+ description: z7.string().optional().describe("What this option means / when to choose it (strongly preferred)")
5574
5683
  })
5575
5684
  ).min(2).max(6).describe("2\u20136 choices (Sideboard also offers Other)")
5576
5685
  })
@@ -5596,9 +5705,9 @@ async function startMcpServer() {
5596
5705
  "present_plan",
5597
5706
  PRESENT_PLAN_TOOL_DESCRIPTION,
5598
5707
  {
5599
- title: z6.string().optional().describe("Short plan title (defaults to Plan)"),
5600
- content: z6.string().min(1).describe("Full plan markdown (headings, steps, risks, open questions)"),
5601
- thread_id: z6.string().optional().describe("Sideboard thread id when cwd is not the worktree")
5708
+ title: z7.string().optional().describe("Short plan title (defaults to Plan)"),
5709
+ content: z7.string().min(1).describe("Full plan markdown (headings, steps, risks, open questions)"),
5710
+ thread_id: z7.string().optional().describe("Sideboard thread id when cwd is not the worktree")
5602
5711
  },
5603
5712
  async ({ title, content, thread_id }) => {
5604
5713
  const { writePlanFile: writePlanFile2 } = await import("./plan-file-7MMIPV7Y.js");
@@ -5627,15 +5736,15 @@ async function startMcpServer() {
5627
5736
  "present_schema",
5628
5737
  "Open Sideboard\u2019s schema-driven side column (filterable table and/or form) when the user needs to filter, edit, publish, or persist records. Do not call this just to re-display rows you already wrote as a markdown table. If the user asks for an editable / interactive table, call this even if chat already showed those rows. Pass JSON Schema + optional schemaUi. Prefer datasource=inline with embedded resource/records. Use datasource=brightsy with resource_id only when the user is logged into Brightsy.",
5629
5738
  {
5630
- title: z6.string().describe("Pane title"),
5631
- mode: z6.enum(["table", "form"]).optional().describe("table = list/filter records; form = edit one record"),
5632
- datasource: z6.enum(["brightsy", "inline"]).optional().describe("brightsy resolves via login; inline uses embedded resource/records"),
5633
- resource_id: z6.string().optional().describe("Brightsy record type UUID (or generic resource id)"),
5634
- record_id: z6.string().optional().describe("Record id when opening form mode"),
5635
- resource: z6.record(z6.unknown()).optional().describe("Inline { id, title, schema, schemaUi?, slug? }"),
5636
- record: z6.record(z6.unknown()).optional().describe("Inline record { id, data, published_at? }"),
5637
- records: z6.array(z6.record(z6.unknown())).optional().describe("Inline records for table mode"),
5638
- pane_id: z6.string().optional().describe("Stable pane id across updates")
5739
+ title: z7.string().describe("Pane title"),
5740
+ mode: z7.enum(["table", "form"]).optional().describe("table = list/filter records; form = edit one record"),
5741
+ datasource: z7.enum(["brightsy", "inline"]).optional().describe("brightsy resolves via login; inline uses embedded resource/records"),
5742
+ resource_id: z7.string().optional().describe("Brightsy record type UUID (or generic resource id)"),
5743
+ record_id: z7.string().optional().describe("Record id when opening form mode"),
5744
+ resource: z7.record(z7.unknown()).optional().describe("Inline { id, title, schema, schemaUi?, slug? }"),
5745
+ record: z7.record(z7.unknown()).optional().describe("Inline record { id, data, published_at? }"),
5746
+ records: z7.array(z7.record(z7.unknown())).optional().describe("Inline records for table mode"),
5747
+ pane_id: z7.string().optional().describe("Stable pane id across updates")
5639
5748
  },
5640
5749
  async (args) => {
5641
5750
  const id = args.pane_id?.trim() || `schema_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
@@ -5656,10 +5765,10 @@ async function startMcpServer() {
5656
5765
  "present_files",
5657
5766
  "Open Sideboard\u2019s Files column (CMS-style file manager: browse, upload, pick). Use datasource=brightsy when the user is logged into Brightsy account storage; datasource=memory for a session-local demo store. Prefer this over claiming a file manager UI is unavailable. Pair with present_schema when editing records that need media.",
5658
5767
  {
5659
- title: z6.string().optional().describe("Pane title (default: Files)"),
5660
- datasource: z6.enum(["brightsy", "memory"]).optional().describe("brightsy = account storage via login; memory = session demo store"),
5661
- path: z6.string().optional().describe("Initial folder path (e.g. public)"),
5662
- pane_id: z6.string().optional().describe("Stable pane id across updates")
5768
+ title: z7.string().optional().describe("Pane title (default: Files)"),
5769
+ datasource: z7.enum(["brightsy", "memory"]).optional().describe("brightsy = account storage via login; memory = session demo store"),
5770
+ path: z7.string().optional().describe("Initial folder path (e.g. public)"),
5771
+ pane_id: z7.string().optional().describe("Stable pane id across updates")
5663
5772
  },
5664
5773
  async (args) => {
5665
5774
  const id = args.pane_id?.trim() || `files_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
@@ -5679,8 +5788,8 @@ async function startMcpServer() {
5679
5788
  "wait_for_job",
5680
5789
  "Wait on a detached long job (tests, pack, deploy, connector CLI) started with detached-job.cjs. MCP clients kill tools around 60s, so this returns within 45s. Sideboard opens a type=log pane from this result (artifact_id = job id, content = delta). stillRunning is the source of truth \u2014 if true, call wait_for_job again. If the job is hanging, producing no useful output, or doing the wrong thing, call stop_job instead of looping forever. Do not end the turn or tell the user you will let them know later. If false, ok/failed is the result.",
5681
5790
  {
5682
- id: z6.string().describe("Detached job id (same kebab-case id passed to detached-job.cjs start)"),
5683
- timeoutMs: z6.number().optional()
5791
+ id: z7.string().describe("Detached job id (same kebab-case id passed to detached-job.cjs start)"),
5792
+ timeoutMs: z7.number().optional()
5684
5793
  },
5685
5794
  async ({ id, timeoutMs }) => {
5686
5795
  const result = await waitForDetachedJob(process.cwd(), id, {
@@ -5693,8 +5802,8 @@ async function startMcpServer() {
5693
5802
  "stop_job",
5694
5803
  "Stop a detached job you started with detached-job.cjs when it is hanging, producing no useful output, buffering forever, or doing the wrong thing (wrong project, infinite watch, huge dump). Do not stop a pack/test/deploy that is clearly making progress. The type=log pane updates to status=failed with the last delta. Then decide the next step.",
5695
5804
  {
5696
- id: z6.string().describe("Detached job id (same kebab-case id passed to detached-job.cjs start)"),
5697
- reason: z6.string().optional().describe("Why you are stopping (hanging, no progress, wrong output, already have the answer)")
5805
+ id: z7.string().describe("Detached job id (same kebab-case id passed to detached-job.cjs start)"),
5806
+ reason: z7.string().optional().describe("Why you are stopping (hanging, no progress, wrong output, already have the answer)")
5698
5807
  },
5699
5808
  async ({ id, reason }) => {
5700
5809
  const result = await stopDetachedJob(process.cwd(), id, { reason });
@@ -5706,7 +5815,7 @@ async function startMcpServer() {
5706
5815
  "list_run_scripts",
5707
5816
  "List .sideboard/.conductor run scripts for this thread (same menu as the desktop Dev button) and which are active. Each script and active run includes preview: url (open http://localhost:<SIDEBOARD_PORT>) or window (Electron: native window is the app; url is renderer/HMR only). Never guess :3000. Worktree turns may omit ref (uses cwd).",
5708
5817
  {
5709
- ref: z6.string().optional().describe("Thread id. Omit on a worktree turn (uses cwd).")
5818
+ ref: z7.string().optional().describe("Thread id. Omit on a worktree turn (uses cwd).")
5710
5819
  },
5711
5820
  async ({ ref }) => {
5712
5821
  try {
@@ -5736,8 +5845,8 @@ async function startMcpServer() {
5736
5845
  "run_dev_script",
5737
5846
  "Start a .sideboard/.conductor run script (same as the desktop Dev / play button). Several named scripts can run at once; each gets its own port, and starting one does not stop the others. Logs and Stop appear in the thread UI \u2014 do not detached-job or shell-spawn the same command. Returns {port, ports, url, preview}. preview=url \u2192 open url (browser, curl, Playwright). preview=window \u2192 Electron: the native window is the app; url is renderer/HMR only (no preload/IPC in a browser). The port is allocated SIDEBOARD_PORT, not 3000. Worktree turns may omit ref (uses cwd). Omit name for the default script.",
5738
5847
  {
5739
- ref: z6.string().optional().describe("Thread id. Omit on a worktree turn (uses cwd)."),
5740
- name: z6.string().optional().describe("Script name from list_run_scripts (default script if omitted).")
5848
+ ref: z7.string().optional().describe("Thread id. Omit on a worktree turn (uses cwd)."),
5849
+ name: z7.string().optional().describe("Script name from list_run_scripts (default script if omitted).")
5741
5850
  },
5742
5851
  async ({ ref, name }) => {
5743
5852
  try {
@@ -5765,8 +5874,8 @@ async function startMcpServer() {
5765
5874
  "stop_dev_script",
5766
5875
  "Stop a run script started with run_dev_script or the desktop Dev button (same as UI Stop). Omit name to stop all scripts on this worktree. Worktree turns may omit ref (uses cwd).",
5767
5876
  {
5768
- ref: z6.string().optional().describe("Thread id. Omit on a worktree turn (uses cwd)."),
5769
- name: z6.string().optional().describe("Script name to stop; omit to stop all active scripts.")
5877
+ ref: z7.string().optional().describe("Thread id. Omit on a worktree turn (uses cwd)."),
5878
+ name: z7.string().optional().describe("Script name to stop; omit to stop all active scripts.")
5770
5879
  },
5771
5880
  async ({ ref, name }) => {
5772
5881
  try {
@@ -5783,9 +5892,9 @@ async function startMcpServer() {
5783
5892
  "get_run_log",
5784
5893
  "Read the Run-tab terminal for a worktree script (same stdout/stderr as the desktop Dev pane). Default is a tail \u2014 raise `tail` only if the error is missing. Worktree turns may omit ref (uses cwd). Omit name for the default script.",
5785
5894
  {
5786
- ref: z6.string().optional().describe("Thread id. Omit on a worktree turn (uses cwd)."),
5787
- name: z6.string().optional().describe("Script name from list_run_scripts (default script if omitted)."),
5788
- tail: z6.number().int().min(1).max(MAX_RUN_LOG_CHARS).optional().describe(
5895
+ ref: z7.string().optional().describe("Thread id. Omit on a worktree turn (uses cwd)."),
5896
+ name: z7.string().optional().describe("Script name from list_run_scripts (default script if omitted)."),
5897
+ tail: z7.number().int().min(1).max(MAX_RUN_LOG_CHARS).optional().describe(
5789
5898
  `Max characters to return from the end (default ${DEFAULT_RUN_LOG_TAIL_CHARS}). Do not request the full buffer unless the tail is truncated and you still need more.`
5790
5899
  )
5791
5900
  },
@@ -5823,8 +5932,8 @@ async function startMcpServer() {
5823
5932
  "notify_orchestrator",
5824
5933
  "Wake the parent Global orchestrator with a short status so it does not have to poll wait_for_turn. Use when you are blocked and the parent may have moved on (missing access, waiting on something the coordinator must handle). The message is shown on the sidebar and board until the next user message in this chat. ask_user already notifies for input-required \u2014 do not call this for the same question. Information only: never a git command. Do not use this to steer other worktrees.",
5825
5934
  {
5826
- message: z6.string().min(1).max(1500).describe("Why you are blocked / what the parent should know (short)."),
5827
- reason: z6.enum(["input-required", "blocked"]).optional().describe(
5935
+ message: z7.string().min(1).max(1500).describe("Why you are blocked / what the parent should know (short)."),
5936
+ reason: z7.enum(["input-required", "blocked"]).optional().describe(
5828
5937
  "input-required = waiting on the user in this chat; blocked = need the coordinator (default)."
5829
5938
  )
5830
5939
  },
@@ -5861,11 +5970,11 @@ async function startMcpServer() {
5861
5970
  "set_workspace_tags",
5862
5971
  worktreeProfile ? "Update tags on this workspace (the checkout you are in). Omit ref to use this chat. mode=replace (default), add, or remove. Short labels \u2014 the same ones the orchestrator used for this task. You cannot retag a different checkout." : "Label a project workspace so related checkouts stay recognizable in the sidebar and on the phone. Pass any chat id on that workspace. Use the same tags on every workspace from one task. mode=replace is the default; add and remove change the set. create_workspace tags= sets them at create time. Not for orchestration chats.",
5863
5972
  {
5864
- ref: z6.string().optional().describe(
5973
+ ref: z7.string().optional().describe(
5865
5974
  worktreeProfile ? "Omit for this chat. Another chat id only if it is on this same checkout." : "Chat id on the workspace to label."
5866
5975
  ),
5867
- tags: z6.union([z6.string(), z6.array(z6.string())]).describe("Labels to set. A string may be comma-separated."),
5868
- mode: z6.enum(["replace", "add", "remove"]).optional()
5976
+ tags: z7.union([z7.string(), z7.array(z7.string())]).describe("Labels to set. A string may be comma-separated."),
5977
+ mode: z7.enum(["replace", "add", "remove"]).optional()
5869
5978
  },
5870
5979
  async ({ ref, tags, mode }) => {
5871
5980
  try {
@@ -5892,14 +6001,14 @@ async function startMcpServer() {
5892
6001
  if (!worktreeProfile) {
5893
6002
  registerConnectedIssueVendorTools(server);
5894
6003
  const { getCaffeinateHold: getCaffeinateHold2, setCaffeinateHold: setCaffeinateHold2 } = await import("./caffeinate-hold-DYJGWK6Y.js");
5895
- const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults: resolveThreadDefaults2 } = await import("./app-settings-NI4X5HW3.js");
6004
+ const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults: resolveThreadDefaults2 } = await import("./app-settings-MR2U6AXN.js");
5896
6005
  const accountDefaults = resolveThreadDefaults2();
5897
6006
  const accountDefaultsHint = `Account defaults: agent=${accountDefaults.agent}, model=${accountDefaults.model?.trim() || "Auto"}, effort=${accountDefaults.effort}`;
5898
6007
  server.tool(
5899
6008
  "set_caffeinate",
5900
6009
  "Keep this Mac awake with caffeinate (like Claude Code) across turns \u2014 independent of Settings toggles. Turn ON when the user will be away from the keyboard, is driving work from Slack, or asks you to keep the machine awake. Turn OFF when they say they are done, wrapping up, going to sleep, or no longer need the Mac awake. Closing or archiving this orchestration chat also releases the hold. macOS only.",
5901
6010
  {
5902
- enabled: z6.boolean().describe("true = hold caffeinate on; false = release and let the Mac sleep")
6011
+ enabled: z7.boolean().describe("true = hold caffeinate on; false = release and let the Mac sleep")
5903
6012
  },
5904
6013
  async ({ enabled }) => {
5905
6014
  const threadId = process.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || null;
@@ -5925,24 +6034,24 @@ async function startMcpServer() {
5925
6034
  "create_workspace",
5926
6035
  `Create a workspace (isolated git checkout + first chat) from branch, pr, or ticket. A ticket, PR, or named branch has one checkout \u2014 if one already matches, returns it (alreadyStarted=true) instead of a second checkout. That is not one agent: chats lists every agent on that checkout. Add another with fork_chat; send_to_chat only the chat that should do this job. Creating from the default branch still opens a new isolated checkout. Pass repoPath from list_projects. cowboy=true uses the project folder on the default branch (no isolated worktree; land is commit+push). From an orchestration chat, omit parentChatId (Sideboard binds the child to this chat) or pass the exact id from the turn reminder \u2014 never invent a uuid. Omit agent/model \u2014 Sideboard applies ${accountDefaultsHint}. Do not pass your own agent or agent=cursor; those are ignored. Setup (settings.toml, .cursor/worktrees.json, or script/setup) runs in the background in parallel with the first turn (skipped for cowboy). Pass the same tags on every workspace you open for one task (short labels such as "phone-sync") so they still match in the sidebar the next day. Reusing a checkout adds those tags.`,
5927
6036
  {
5928
- sourceType: z6.enum(["branch", "pr", "ticket"]),
5929
- sourceRef: z6.string(),
5930
- agent: z6.enum(["claude", "codex", "opencode", "brightsy", "cursor"]).optional().describe(
6037
+ sourceType: z7.enum(["branch", "pr", "ticket"]),
6038
+ sourceRef: z7.string(),
6039
+ agent: z7.enum(["claude", "codex", "opencode", "brightsy", "cursor"]).optional().describe(
5931
6040
  `Usually omit. Sideboard uses Account default agent (${accountDefaults.agent}). Passing your own agent or cursor is ignored.`
5932
6041
  ),
5933
- model: z6.string().nullable().optional().describe(
6042
+ model: z7.string().nullable().optional().describe(
5934
6043
  `Usually omit to use Account default model (${accountDefaults.model?.trim() || "Auto"}). Pass null only to force Auto / agent-default.`
5935
6044
  ),
5936
- repoPath: z6.string(),
5937
- title: z6.string().optional(),
5938
- cowboy: z6.boolean().optional().describe(
6045
+ repoPath: z7.string(),
6046
+ title: z7.string().optional(),
6047
+ cowboy: z7.boolean().optional().describe(
5939
6048
  "If true, work in the project folder on the default branch (no thread/* worktree). Requires Settings \u2192 Advanced \u2192 Cowboy mode. Land is commit+push to that branch. Archive does not delete the folder."
5940
6049
  ),
5941
- parentChatId: z6.string().optional().describe(
6050
+ parentChatId: z7.string().optional().describe(
5942
6051
  "Orchestration: omit (preferred) or pass YOUR chat id from the turn reminder / AGENTS.md. Do not invent uuids."
5943
6052
  ),
5944
- parentThreadId: z6.string().optional().describe("Legacy alias for parentChatId."),
5945
- tags: z6.union([z6.string(), z6.array(z6.string())]).optional().describe(
6053
+ parentThreadId: z7.string().optional().describe("Legacy alias for parentChatId."),
6054
+ tags: z7.union([z7.string(), z7.array(z7.string())]).optional().describe(
5946
6055
  "Short labels for this workspace (or a comma-separated string). Use the same tags on every checkout for one task. Added when the checkout already exists."
5947
6056
  )
5948
6057
  },
@@ -5958,11 +6067,11 @@ async function startMcpServer() {
5958
6067
  "start_board_card",
5959
6068
  "Same as create_workspace for a ticket, PR, or named branch (attaches issue text when Sideboard can resolve it). Does not create a second checkout when one already matches \u2014 returns that workspace (alreadyStarted) and its chats. A workspace can have many agents. fork_chat to add one; send_to_chat only the chat that should do this job.",
5960
6069
  {
5961
- kind: z6.enum(["ticket", "pr", "branch"]),
5962
- ref: z6.string().describe("Ticket identifier (ENG-12), PR number (44), or branch name from list_board"),
5963
- repoPath: z6.string().describe("Project path from list_projects / list_board"),
5964
- title: z6.string().optional(),
5965
- tags: z6.union([z6.string(), z6.array(z6.string())]).optional().describe(
6070
+ kind: z7.enum(["ticket", "pr", "branch"]),
6071
+ ref: z7.string().describe("Ticket identifier (ENG-12), PR number (44), or branch name from list_board"),
6072
+ repoPath: z7.string().describe("Project path from list_projects / list_board"),
6073
+ title: z7.string().optional(),
6074
+ tags: z7.union([z7.string(), z7.array(z7.string())]).optional().describe(
5966
6075
  "Short labels for this workspace. Same tags on every checkout for one task. Added when the checkout already exists."
5967
6076
  )
5968
6077
  },
@@ -6067,9 +6176,9 @@ async function startMcpServer() {
6067
6176
  "send_to_chat",
6068
6177
  'Steer a prompt on a workspace chat (Settings \u2192 Follow-up, default steer: interrupt the in-flight turn and start now, same as the workspace composer). Use after create_workspace to start or continue a conversation. For commit/push/PR, prefer ask_git (canonical desktop-button phrases). Send "Merge PR." / ask_git merge only when the user explicitly asked to merge. force_stop=true kills the in-flight turn and clears the queue before this prompt \u2014 only when the current request is wrong and must be replaced. Do not send_to_chat to check in, resume after a halt notice, or because wait_for_turn returned stillRunning; that interrupts the child mid-thought. Call wait_for_turn again instead.',
6069
6178
  {
6070
- ref: z6.string(),
6071
- prompt: z6.string(),
6072
- force_stop: z6.boolean().optional()
6179
+ ref: z7.string(),
6180
+ prompt: z7.string(),
6181
+ force_stop: z7.boolean().optional()
6073
6182
  },
6074
6183
  async ({ ref, prompt, force_stop }) => {
6075
6184
  if (force_stop) {
@@ -6094,8 +6203,8 @@ async function startMcpServer() {
6094
6203
  "wait_for_turn",
6095
6204
  "Wait until the chat finishes its current/queued turn, or return early with a live progress snapshot. MCP clients often kill tools around 60s, so this returns within 45s even while the child is still working. taskState is the A2A-style lifecycle: submitted (queued, not started), working, input-required (ask_user), completed, failed, canceled. stillRunning is true only for submitted/working. blockedReason is why the child is waiting on a person (the question, plan approval, or a reported block) \u2014 read it and do not send_to_chat a check-in. If stillRunning, text and usage are empty (they would be the previous turn) \u2014 read progress and call wait_for_turn again. Do not send_to_chat a check-in (that steers / interrupts). On failed, lastError/text is the failure. On canceled, the child did not finish \u2014 resume with send_to_chat or tell the user. On input-required, wait for the user in that chat. When finished, text is this turn\u2019s assistant reply and usage is that turn\u2019s tokens + costUsd (this turn, not the Claude session total; sessionCostUsd is the provider session total when present).",
6096
6205
  {
6097
- ref: z6.string(),
6098
- timeoutMs: z6.number().optional()
6206
+ ref: z7.string(),
6207
+ timeoutMs: z7.number().optional()
6099
6208
  },
6100
6209
  async ({ ref, timeoutMs }) => {
6101
6210
  const thread = await orch.waitForTurn(ref, mcpWaitForTurnTimeoutMs(timeoutMs), {
@@ -6123,7 +6232,7 @@ async function startMcpServer() {
6123
6232
  server.tool(
6124
6233
  "get_turn_result",
6125
6234
  "Assistant message and last-turn usage when the turn finished. While stillRunning (submitted/working), text and usage are empty so a previous reply cannot be mistaken for this turn \u2014 use progress for tools/thinking. Not the full transcript. Includes taskState (A2A lifecycle) and blockedReason (why the agent is waiting on a person).",
6126
- { ref: z6.string() },
6235
+ { ref: z7.string() },
6127
6236
  async ({ ref }) => {
6128
6237
  const result = orch.getTurnResult(ref);
6129
6238
  const hint = mcpWaitTaskHint(result.taskState, result.status, result.blockedReason);
@@ -6139,9 +6248,9 @@ async function startMcpServer() {
6139
6248
  "stop_chat",
6140
6249
  "Force-stop a thread: kill any in-flight agent turn AND clear queued prompts so drainQueue cannot continue. Does not archive the worktree. Optional force defaults to true. Pass reason \u2014 a short note on why this chat is stopping. It is stored on the thread and kept when the turn unwinds. If you omit it, Sideboard still records that the queue was cleared.",
6141
6250
  {
6142
- ref: z6.string(),
6143
- force: z6.boolean().optional(),
6144
- reason: z6.string().optional().describe("Why this chat is stopping. Stored on the thread for the next session.")
6251
+ ref: z7.string(),
6252
+ force: z7.boolean().optional(),
6253
+ reason: z7.string().optional().describe("Why this chat is stopping. Stored on the thread for the next session.")
6145
6254
  },
6146
6255
  async ({ ref, force, reason }) => {
6147
6256
  const t = orch.getThread(ref);
@@ -6165,7 +6274,7 @@ async function startMcpServer() {
6165
6274
  server.tool(
6166
6275
  "archive_chat",
6167
6276
  "Archive a thread (stops agent/dev, runs archive script, removes worktree when last chat tab). Coordinators commit, push, and open PRs by asking the worktree agent (ask_git). Merge only when the user explicitly asked.",
6168
- { ref: z6.string() },
6277
+ { ref: z7.string() },
6169
6278
  async ({ ref }) => {
6170
6279
  const t = orch.getThread(ref);
6171
6280
  if (!t) {
@@ -6191,7 +6300,7 @@ async function startMcpServer() {
6191
6300
  server.tool(
6192
6301
  "restore_chat",
6193
6302
  "Restore an archived thread (recreates worktree from branch when needed)",
6194
- { ref: z6.string() },
6303
+ { ref: z7.string() },
6195
6304
  async ({ ref }) => {
6196
6305
  try {
6197
6306
  const restored = await orch.restore(ref);
@@ -6210,8 +6319,8 @@ async function startMcpServer() {
6210
6319
  "get_diff",
6211
6320
  "Compact diff summary (capped hunks, paginated)",
6212
6321
  {
6213
- ref: z6.string(),
6214
- maxFiles: z6.number().optional()
6322
+ ref: z7.string(),
6323
+ maxFiles: z7.number().optional()
6215
6324
  },
6216
6325
  async ({ ref, maxFiles }) => {
6217
6326
  const summary = await orch.diffSummary(ref);
@@ -6224,7 +6333,7 @@ async function startMcpServer() {
6224
6333
  server.tool(
6225
6334
  "get_pr_checks",
6226
6335
  "Snapshot of GitHub PR checks for a worktree thread (`gh pr checks` plus merge/review gates). null = no PR. If the user gave a goal (Greptile 5/5, CI green), the worktree agent watches with `gh pr checks --watch` via /long-running \u2014 this tool is a snapshot, not a waiter. Coordinators: do not run gh from the orchestration cwd.",
6227
- { ref: z6.string().describe("Worktree thread id/ref") },
6336
+ { ref: z7.string().describe("Worktree thread id/ref") },
6228
6337
  async ({ ref }) => {
6229
6338
  try {
6230
6339
  const checks = await orch.getPrChecks(ref);
@@ -6238,7 +6347,7 @@ async function startMcpServer() {
6238
6347
  server.tool(
6239
6348
  "request_review",
6240
6349
  'Start a merge-readiness Review on a worktree agent thread (same as the desktop Review button). If that worktree has a single unused idle agent (no messages), runs the review there; otherwise opens a new Review chat tab. Attaches .claude/skills/review/SKILL.md when present (else copies .sideboard/review.md into .context/review.md, or seeds that file from the stock template), and sends "Review changes in this workspace." Expect Approve / Approve with nits / Request changes / Needs more information in that chat. The review stays there so the user can read it and type next steps \u2014 do not comment on or update the PR or ticket, and do not ask_user after the review, until they ask. Pass a worktree thread ref \u2014 not the orchestrator. Then wait_for_turn (loop while stillRunning) / get_turn_result on the returned review thread id.',
6241
- { ref: z6.string().describe("Worktree thread id/ref to review") },
6350
+ { ref: z7.string().describe("Worktree thread id/ref to review") },
6242
6351
  async ({ ref }) => {
6243
6352
  try {
6244
6353
  const tab = await orch.requestReview(ref);
@@ -6260,8 +6369,8 @@ async function startMcpServer() {
6260
6369
  "ask_git",
6261
6370
  "Commit & push, open a draft PR, mark ready for review, resolve conflicts, or merge \u2014 same prompts as the desktop git buttons (including Resolve). Always steers the worktree agent; does not push or open the PR locally. Then wait_for_turn (loop while stillRunning). Do not start a checks loop on a plain push \u2014 only if the user gave a goal (Greptile 5/5, CI green). Pass a worktree thread ref (not the orchestrator). action=ready-for-review and action=merge only when the user explicitly asked (or clicked the desktop button). Do not run git or gh from the orchestration cwd.",
6262
6371
  {
6263
- ref: z6.string().describe("Worktree thread id/ref"),
6264
- action: z6.enum(AGENT_GIT_ACTIONS).describe(
6372
+ ref: z7.string().describe("Worktree thread id/ref"),
6373
+ action: z7.enum(AGENT_GIT_ACTIONS).describe(
6265
6374
  "commit-push | create-draft | create-web | resolve-conflicts | ready-for-review | merge"
6266
6375
  )
6267
6376
  },
@@ -6290,7 +6399,7 @@ async function startMcpServer() {
6290
6399
  }
6291
6400
  }
6292
6401
  );
6293
- const agentEnum = z6.enum(["claude", "codex", "opencode", "brightsy", "cursor"]);
6402
+ const agentEnum = z7.enum(["claude", "codex", "opencode", "brightsy", "cursor"]);
6294
6403
  server.tool(
6295
6404
  "list_models",
6296
6405
  "List models for an agent. Prefer Auto: do not call this unless you have a reason to pin a specific model (user request, cost/latency, capability). Omit agent to list all.",
@@ -6311,11 +6420,11 @@ async function startMcpServer() {
6311
6420
  "fork_workspace",
6312
6421
  "Fork a worktree agent chat into a NEW git worktree + chat (desktop \u201CFork to new workspace\u201D). Seeds a transcript (through through_index, default all). Optional agent override. Leave model unset for Auto (default) \u2014 only pass model when you have a reason. Not for the orchestrator. Then send_to_chat / wait_for_turn (loop while stillRunning) on the returned id.",
6313
6422
  {
6314
- ref: z6.string().describe("Worktree thread id/ref to fork"),
6315
- through_index: z6.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
6423
+ ref: z7.string().describe("Worktree thread id/ref to fork"),
6424
+ through_index: z7.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
6316
6425
  agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
6317
- model: z6.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
6318
- title: z6.string().optional()
6426
+ model: z7.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
6427
+ title: z7.string().optional()
6319
6428
  },
6320
6429
  async ({ ref, through_index, agent, model, title }) => {
6321
6430
  try {
@@ -6349,11 +6458,11 @@ async function startMcpServer() {
6349
6458
  "fork_chat",
6350
6459
  "Add another agent chat on the SAME workspace (same files, Run, and git). Also forks a Global orchestration chat into a new orchestration tab. Pass agent when the new chat should use a different harness. Leave model unset for Auto unless you have a reason. Orchestration forks require an MCP-capable agent (claude, cursor, codex, opencode \u2014 not brightsy). Use this for a second agent on a checkout (review, another harness, parallel work on the same branch). Do not create_workspace again for that \u2014 it returns the existing checkout. Then send_to_chat / wait_for_turn (loop while stillRunning) on the returned id. Use fork_workspace only when the work needs its own git checkout.",
6351
6460
  {
6352
- ref: z6.string().describe("Thread id/ref to fork (worktree agent or orchestration chat)"),
6353
- through_index: z6.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
6461
+ ref: z7.string().describe("Thread id/ref to fork (worktree agent or orchestration chat)"),
6462
+ through_index: z7.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
6354
6463
  agent: agentEnum.optional().describe("Agent for the forked chat (default: same as source)"),
6355
- model: z6.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
6356
- title: z6.string().optional()
6464
+ model: z7.string().nullable().optional().describe("Usually omit (Auto). Only set from list_models when you need a specific model"),
6465
+ title: z7.string().optional()
6357
6466
  },
6358
6467
  async ({ ref, through_index, agent, model, title }) => {
6359
6468
  try {
@@ -6391,7 +6500,7 @@ async function startMcpServer() {
6391
6500
  server.tool(
6392
6501
  "run_setup",
6393
6502
  "Re-run workspace setup (Sideboard/Conductor settings, .cursor/worktrees.json, or script/setup). New worktrees already run this automatically.",
6394
- { ref: z6.string() },
6503
+ { ref: z7.string() },
6395
6504
  async ({ ref }) => {
6396
6505
  const result = await orch.runSetup(ref);
6397
6506
  return mcpJson(result);
@@ -6400,7 +6509,7 @@ async function startMcpServer() {
6400
6509
  server.tool(
6401
6510
  "add_project",
6402
6511
  "Register a git repo as a Sideboard workspace",
6403
- { repoPath: z6.string() },
6512
+ { repoPath: z7.string() },
6404
6513
  async ({ repoPath }) => {
6405
6514
  const ws = await orch.addWorkspace(repoPath);
6406
6515
  return mcpJson(ws);
@@ -6409,7 +6518,7 @@ async function startMcpServer() {
6409
6518
  server.tool(
6410
6519
  "remove_project",
6411
6520
  "Unregister a Sideboard workspace and archive its chats",
6412
- { repoPath: z6.string() },
6521
+ { repoPath: z7.string() },
6413
6522
  async ({ repoPath }) => {
6414
6523
  await orch.removeWorkspace(repoPath);
6415
6524
  return { content: [{ type: "text", text: "ok" }] };
@@ -6419,12 +6528,12 @@ async function startMcpServer() {
6419
6528
  "fanout",
6420
6529
  "Best-of-n: create one thread per agent with the same prompt (parallel attempts)",
6421
6530
  {
6422
- prompt: z6.string(),
6423
- agents: z6.array(z6.enum(["claude", "codex", "opencode", "brightsy", "cursor"])),
6424
- repoPath: z6.string(),
6425
- sourceType: z6.enum(["branch", "pr", "ticket"]).optional(),
6426
- sourceRef: z6.string().optional(),
6427
- title: z6.string().optional()
6531
+ prompt: z7.string(),
6532
+ agents: z7.array(z7.enum(["claude", "codex", "opencode", "brightsy", "cursor"])),
6533
+ repoPath: z7.string(),
6534
+ sourceType: z7.enum(["branch", "pr", "ticket"]).optional(),
6535
+ sourceRef: z7.string().optional(),
6536
+ title: z7.string().optional()
6428
6537
  },
6429
6538
  async (args) => {
6430
6539
  const threads = await orch.bestOfN(args);
@@ -6442,8 +6551,8 @@ async function startMcpServer() {
6442
6551
  "list_branches",
6443
6552
  "List git branches in a registered workspace. Pass repoPath from list_projects (unmerged into the default branch by default \u2014 for create_workspace sourceType=branch).",
6444
6553
  {
6445
- repoPath: z6.string(),
6446
- unmergedOnly: z6.boolean().optional()
6554
+ repoPath: z7.string(),
6555
+ unmergedOnly: z7.boolean().optional()
6447
6556
  },
6448
6557
  async ({ repoPath, unmergedOnly }) => {
6449
6558
  const root = await resolveRepoRoot(repoPath);
@@ -6464,19 +6573,19 @@ async function startMcpServer() {
6464
6573
  "list_prs",
6465
6574
  `List GitHub PRs for a registered workspace (the review surface for assigned ticket work \u2014 not the tickets). Pass repoPath from list_projects. "Get me N tickets to review" \u2192 queue=review and limit=N: open non-draft PRs labeled with this project's ready-for-review GitHub label (Settings \u2192 Projects; default eng-review) with no individual user reviewer. Prefer teams that match Settings \u2192 Agents / Projects roles for this repo. A team like engineering-team is not a claim (you are on that team). Also: state (open|closed|merged|all|review), label, reviewer (me|unassigned|login), query, limit (default 40, max 250). Then create_workspace with sourceType=pr.`,
6466
6575
  {
6467
- repoPath: z6.string(),
6468
- query: z6.string().optional().describe("GitHub search tokens (title, draft:true, \u2026)"),
6469
- queue: z6.enum(["review", "mine", "approved", "changes"]).optional().describe(
6576
+ repoPath: z7.string(),
6577
+ query: z7.string().optional().describe("GitHub search tokens (title, draft:true, \u2026)"),
6578
+ queue: z7.enum(["review", "mine", "approved", "changes"]).optional().describe(
6470
6579
  "review = unclaimed ready-for-review inbox (Settings \u2192 Projects label, default eng-review). mine = review-requested:@me. approved / changes = eng-approved / eng-requested-changes."
6471
6580
  ),
6472
- state: z6.enum(["open", "closed", "merged", "all", "review"]).optional().describe("GitHub PR state (default open). review is an alias for queue=review."),
6473
- label: z6.string().optional().describe(
6581
+ state: z7.enum(["open", "closed", "merged", "all", "review"]).optional().describe("GitHub PR state (default open). review is an alias for queue=review."),
6582
+ label: z7.string().optional().describe(
6474
6583
  "GitHub label / workflow tag. Comma-separated AND. Examples: eng-review, eng-approved, eng-requested-changes"
6475
6584
  ),
6476
- reviewer: z6.string().optional().describe(
6585
+ reviewer: z7.string().optional().describe(
6477
6586
  "me (review requested of you), unassigned (no individual reviewer; team queues like engineering-team still count), all, or a GitHub login"
6478
6587
  ),
6479
- limit: z6.number().int().positive().max(250).optional().describe("Page size (default 40, max 250). Raise when truncated is true.")
6588
+ limit: z7.number().int().positive().max(250).optional().describe("Page size (default 40, max 250). Raise when truncated is true.")
6480
6589
  },
6481
6590
  async ({ repoPath, query, queue, state, label, reviewer, limit }) => {
6482
6591
  const root = await resolveRepoRoot(repoPath);
@@ -6509,7 +6618,7 @@ async function startMcpServer() {
6509
6618
  server.tool(
6510
6619
  "get_pr_stack",
6511
6620
  "Load the GitHub PR stack for a thread worktree (`gh stack view --json`). Returns null JSON when the branch is not stacked. Prefer this before ask_git merge on stacked PRs (and only merge when the user explicitly asked).",
6512
- { ref: z6.string() },
6621
+ { ref: z7.string() },
6513
6622
  async ({ ref }) => {
6514
6623
  const stack = await orch.getPrStack(ref);
6515
6624
  return mcpJson(stack);
@@ -6519,8 +6628,8 @@ async function startMcpServer() {
6519
6628
  "open_pr_stack_layers",
6520
6629
  "Materialize one worktree+thread per stack layer (or a single 1-based layer). Pass a thread ref already on the stack.",
6521
6630
  {
6522
- ref: z6.string(),
6523
- layer: z6.number().int().positive().optional()
6631
+ ref: z7.string(),
6632
+ layer: z7.number().int().positive().optional()
6524
6633
  },
6525
6634
  async ({ ref, layer }) => {
6526
6635
  const result = await orch.openPrStackLayers(ref, { layer });
@@ -6543,9 +6652,9 @@ async function startMcpServer() {
6543
6652
  "add_stack_layer",
6544
6653
  "Add a branch on top of the current stack (`gh stack add`) and open a worktree+thread for it.",
6545
6654
  {
6546
- ref: z6.string(),
6547
- branchName: z6.string(),
6548
- title: z6.string().optional()
6655
+ ref: z7.string(),
6656
+ branchName: z7.string(),
6657
+ title: z7.string().optional()
6549
6658
  },
6550
6659
  async ({ ref, branchName, title }) => {
6551
6660
  const result = await orch.addStackLayer(ref, branchName, { title });
@@ -6563,11 +6672,11 @@ async function startMcpServer() {
6563
6672
  "create_pr_stack",
6564
6673
  "Create a new GitHub PR stack with one Sideboard worktree per layer (bottom\u2192top branch names). Requires `gh extension install github/gh-stack`.",
6565
6674
  {
6566
- repoPath: z6.string(),
6567
- branches: z6.array(z6.string()).min(1),
6568
- agent: z6.enum(["claude", "codex", "opencode", "brightsy", "cursor"]),
6569
- base: z6.string().optional(),
6570
- title: z6.string().optional()
6675
+ repoPath: z7.string(),
6676
+ branches: z7.array(z7.string()).min(1),
6677
+ agent: z7.enum(["claude", "codex", "opencode", "brightsy", "cursor"]),
6678
+ base: z7.string().optional(),
6679
+ title: z7.string().optional()
6571
6680
  },
6572
6681
  async (args) => {
6573
6682
  const result = await orch.createPrStack({
@@ -6595,10 +6704,10 @@ async function startMcpServer() {
6595
6704
  "list_issues",
6596
6705
  "List or search issues (Linear, AbleTime, or GitHub; falls back to GitHub). Use Settings \u2192 Agents / Projects notes and roles to pick tickets relevant to the viewer (query + assignee=me or unassigned as the notes say). Default 40; pass query and/or limit (max 250) when truncated. assignee: me (Linear default), unassigned, all, or a user id. Pass updatedSince for new/updated tickets and new comments (\u201Cany updates since yesterday?\u201D). Then create_workspace with sourceType=ticket.",
6597
6706
  {
6598
- repoPath: z6.string(),
6599
- query: z6.string().optional().describe("Search title, identifier, or description"),
6600
- assignee: z6.string().optional().describe("me (Linear default), unassigned, all, a user id, or a GitHub login"),
6601
- limit: z6.number().int().positive().max(250).optional().describe("Page size (default 40, max 250). Raise when truncated is true."),
6707
+ repoPath: z7.string(),
6708
+ query: z7.string().optional().describe("Search title, identifier, or description"),
6709
+ assignee: z7.string().optional().describe("me (Linear default), unassigned, all, a user id, or a GitHub login"),
6710
+ limit: z7.number().int().positive().max(250).optional().describe("Page size (default 40, max 250). Raise when truncated is true."),
6602
6711
  updatedSince: mcpIssueUpdatedSinceSchema
6603
6712
  },
6604
6713
  async ({ repoPath, query, assignee, limit, updatedSince }) => {
@@ -12488,7 +12597,7 @@ async function continueSourceThread(threadId, prompt) {
12488
12597
  await continueOnReply(threadId, prompt);
12489
12598
  return;
12490
12599
  }
12491
- const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-2UAVOXTN.js");
12600
+ const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-SDH6W6ZL.js");
12492
12601
  await getOrchestrator2().send(threadId, prompt);
12493
12602
  } catch {
12494
12603
  }
@@ -13014,6 +13123,7 @@ export {
13014
13123
  clearAgentDoneCustomSoundFile,
13015
13124
  clearBoardPins,
13016
13125
  clearHomeBoardCache,
13126
+ clearOptionalCliPresenceCache,
13017
13127
  clipAgentEventForPaint,
13018
13128
  clipThinkingForStore,
13019
13129
  cloneRepoIntoSideboard,
@@ -13180,7 +13290,9 @@ export {
13180
13290
  formatMessagesAsTranscript,
13181
13291
  formatNotifyOrchestratorPrompt,
13182
13292
  formatOptionalServicesDirective,
13293
+ formatOptionalServicesDirectiveForTurn,
13183
13294
  formatOptionalServicesReminder,
13295
+ formatOptionalServicesReminderForTurn,
13184
13296
  formatPlanQuestionAnswers,
13185
13297
  formatPlanQuestionsForChat,
13186
13298
  formatPrGateDirective,
@@ -13489,6 +13601,7 @@ export {
13489
13601
  openPrStackLayers,
13490
13602
  openStackLayer,
13491
13603
  opencodeAdapter,
13604
+ optionalCliPresence,
13492
13605
  optionalServiceConnected,
13493
13606
  optionalServiceSpec,
13494
13607
  orchestrationQuotaFallbackAgent,