@sideboard-ai/core 0.1.46 → 0.1.50

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 (40) hide show
  1. package/dist/{agents-6L6VENDT.js → agents-3P4N6KHC.js} +1 -1
  2. package/dist/agents-LUB3Q773.js +82 -0
  3. package/dist/app-settings-7XVDQJ7F.js +80 -0
  4. package/dist/{chunk-SZAKJ4TR.js → chunk-3NAS4MWH.js} +1 -1
  5. package/dist/{chunk-SPCU3MFY.js → chunk-5WYEJ3F3.js} +3 -3
  6. package/dist/chunk-7MV3RXSC.js +277 -0
  7. package/dist/chunk-DF5VQQKA.js +191 -0
  8. package/dist/chunk-E35APBHV.js +269 -0
  9. package/dist/chunk-EOYDCKQC.js +172 -0
  10. package/dist/chunk-GZXBYHJC.js +148 -0
  11. package/dist/chunk-HBJSHRY2.js +494 -0
  12. package/dist/chunk-ISY4EGF6.js +77 -0
  13. package/dist/chunk-KGZBYWZ3.js +23 -0
  14. package/dist/chunk-RQI4TOXK.js +2232 -0
  15. package/dist/chunk-S2LL5HA4.js +33 -0
  16. package/dist/{chunk-TQK2OYPU.js → chunk-SS34TVSY.js} +1 -1
  17. package/dist/chunk-V4JRXYYU.js +122 -0
  18. package/dist/{chunk-6NAPN2N5.js → chunk-ZQCQIWIP.js} +1 -1
  19. package/dist/chunk-ZVV5EEQN.js +1892 -0
  20. package/dist/connected-teams-UBXHZGO4.js +24 -0
  21. package/dist/{coordinator-prompt-2XFYG3C5.js → coordinator-prompt-SPGDT7J5.js} +1 -1
  22. package/dist/coordinator-prompt-VG4BZ5JL.js +25 -0
  23. package/dist/cursor-recover-NNK7JPQM.js +44 -0
  24. package/dist/{global-workspace-XFOXEQCP.js → global-workspace-KSR3E63K.js} +2 -2
  25. package/dist/global-workspace-OJF4ENH4.js +40 -0
  26. package/dist/index.cjs +252 -23
  27. package/dist/index.d.cts +49 -10
  28. package/dist/index.d.ts +49 -10
  29. package/dist/index.js +5773 -192
  30. package/dist/mcp/run-stdio.cjs +212 -25
  31. package/dist/mcp/run-stdio.js +5623 -15
  32. package/dist/paths-2OFB7UJG.js +28 -0
  33. package/dist/run-HMRSRG3U.js +12 -0
  34. package/dist/thread-store-XICUWFNM.js +32 -0
  35. package/dist/title-4WAKWZRF.js +27 -0
  36. package/dist/workspaces-OZE7LRDO.js +24 -0
  37. package/dist/{workspaces-SRL2HZTB.js → workspaces-UJX7JIGH.js} +3 -3
  38. package/dist/worktree-XG5PCLZY.js +94 -0
  39. package/package.json +2 -2
  40. package/dist/chunk-7DTJFP4D.js +0 -5680
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+
3
+ import {
4
+ applyConnectedTeamToCli,
5
+ brightsyMcpServerName,
6
+ connectBrightsyTeam,
7
+ disconnectBrightsyTeam,
8
+ ensureCliTeamTracked,
9
+ ensureConnectedBrightsyTeamTokens,
10
+ getConnectedBrightsyTeamsRaw,
11
+ listConnectedBrightsyTeams
12
+ } from "./chunk-E35APBHV.js";
13
+ import "./chunk-ISY4EGF6.js";
14
+ import "./chunk-7MV3RXSC.js";
15
+ export {
16
+ applyConnectedTeamToCli,
17
+ brightsyMcpServerName,
18
+ connectBrightsyTeam,
19
+ disconnectBrightsyTeam,
20
+ ensureCliTeamTracked,
21
+ ensureConnectedBrightsyTeamTokens,
22
+ getConnectedBrightsyTeamsRaw,
23
+ listConnectedBrightsyTeams
24
+ };
@@ -6,7 +6,7 @@ import {
6
6
  enrichWorkspacesWithGithub,
7
7
  ensureGlobalCoordinatorCwd,
8
8
  formatWorkspaceInventory
9
- } from "./chunk-6NAPN2N5.js";
9
+ } from "./chunk-ZQCQIWIP.js";
10
10
  import "./chunk-FV6FN6V5.js";
11
11
  import "./chunk-O6W3P7V3.js";
12
12
  import "./chunk-77WWLBCI.js";
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+
3
+ import {
4
+ COORDINATOR_TOOL_PLAYBOOK,
5
+ coordinatorGreenfieldPlaybook,
6
+ coordinatorSystemPrompt,
7
+ coordinatorTurnReminder,
8
+ enrichWorkspacesWithGithub,
9
+ ensureGlobalCoordinatorCwd,
10
+ formatWorkspaceInventory
11
+ } from "./chunk-GZXBYHJC.js";
12
+ import "./chunk-ZVV5EEQN.js";
13
+ import "./chunk-EOYDCKQC.js";
14
+ import "./chunk-ISY4EGF6.js";
15
+ import "./chunk-KGZBYWZ3.js";
16
+ import "./chunk-7MV3RXSC.js";
17
+ export {
18
+ COORDINATOR_TOOL_PLAYBOOK,
19
+ coordinatorGreenfieldPlaybook,
20
+ coordinatorSystemPrompt,
21
+ coordinatorTurnReminder,
22
+ enrichWorkspacesWithGithub,
23
+ ensureGlobalCoordinatorCwd,
24
+ formatWorkspaceInventory
25
+ };
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env node
2
+
3
+ import {
4
+ appDataDir
5
+ } from "./chunk-7MV3RXSC.js";
6
+
7
+ // src/agents/cursor-recover.ts
8
+ import { existsSync, readFileSync } from "fs";
9
+ import { join } from "path";
10
+ function recoverFinishedCursorRun(opts) {
11
+ const agentId = opts.agentId.trim();
12
+ if (!agentId) return null;
13
+ const runsPath = join(appDataDir(), "cursor-sdk-store", "runs.ndjson");
14
+ if (!existsSync(runsPath)) return null;
15
+ try {
16
+ const lines = readFileSync(runsPath, "utf8").split("\n");
17
+ let best = null;
18
+ for (const line of lines) {
19
+ const trimmed = line.trim();
20
+ if (!trimmed) continue;
21
+ let row;
22
+ try {
23
+ row = JSON.parse(trimmed);
24
+ } catch {
25
+ continue;
26
+ }
27
+ if (row.agentId !== agentId) continue;
28
+ if (row.status !== "finished") continue;
29
+ if (typeof row.result !== "string" || !row.result.trim()) continue;
30
+ const endedAt = typeof row.endedAt === "number" ? row.endedAt : 0;
31
+ const createdAt = typeof row.createdAt === "number" ? row.createdAt : 0;
32
+ if (createdAt < opts.startedAfterMs && endedAt < opts.startedAfterMs) continue;
33
+ if (!best || endedAt >= best.endedAt) {
34
+ best = { runId: row.runId || "", result: row.result.trim(), endedAt };
35
+ }
36
+ }
37
+ return best;
38
+ } catch {
39
+ return null;
40
+ }
41
+ }
42
+ export {
43
+ recoverFinishedCursorRun
44
+ };
@@ -11,9 +11,9 @@ import {
11
11
  orchestrationTitleNeedsSoccerNickname,
12
12
  orchestratorSessionPoisonedByBuiltins,
13
13
  takenTeamSlugsForOrchestration
14
- } from "./chunk-SPCU3MFY.js";
15
- import "./chunk-6NAPN2N5.js";
14
+ } from "./chunk-5WYEJ3F3.js";
16
15
  import "./chunk-GI7E5733.js";
16
+ import "./chunk-ZQCQIWIP.js";
17
17
  import "./chunk-FV6FN6V5.js";
18
18
  import "./chunk-O6W3P7V3.js";
19
19
  import "./chunk-77WWLBCI.js";
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env node
2
+
3
+ import {
4
+ GLOBAL_WORKSPACE_ID,
5
+ createGlobalChat,
6
+ ensureCloudCoordinator,
7
+ healOrchestrationSoccerTitles,
8
+ isCloudCoordinatorThread,
9
+ isGlobalRepoPath,
10
+ isGlobalThread,
11
+ isOrchestratorThread,
12
+ listGlobalThreads,
13
+ orchestrationTitleNeedsSoccerNickname,
14
+ orchestratorSessionPoisonedByBuiltins,
15
+ takenTeamSlugsForOrchestration
16
+ } from "./chunk-DF5VQQKA.js";
17
+ import "./chunk-S2LL5HA4.js";
18
+ import "./chunk-GZXBYHJC.js";
19
+ import "./chunk-ZVV5EEQN.js";
20
+ import "./chunk-EOYDCKQC.js";
21
+ import "./chunk-ISY4EGF6.js";
22
+ import "./chunk-KGZBYWZ3.js";
23
+ import {
24
+ globalAgentCwd
25
+ } from "./chunk-7MV3RXSC.js";
26
+ export {
27
+ GLOBAL_WORKSPACE_ID,
28
+ createGlobalChat,
29
+ ensureCloudCoordinator,
30
+ globalAgentCwd,
31
+ healOrchestrationSoccerTitles,
32
+ isCloudCoordinatorThread,
33
+ isGlobalRepoPath,
34
+ isGlobalThread,
35
+ isOrchestratorThread,
36
+ listGlobalThreads,
37
+ orchestrationTitleNeedsSoccerNickname,
38
+ orchestratorSessionPoisonedByBuiltins,
39
+ takenTeamSlugsForOrchestration
40
+ };
package/dist/index.cjs CHANGED
@@ -3257,7 +3257,7 @@ var init_coordinator_prompt = __esm({
3257
3257
  "- list_run_scripts / run_dev_script / stop_dev_script \u2014 start/stop named run scripts",
3258
3258
  "Inspect / review / PRs:",
3259
3259
  "- get_diff \u2014 compact diff summary",
3260
- "- request_review \u2014 open a Review chat tab on a worktree thread (merge-readiness recommendation); then wait_for_turn / get_turn_result on the returned id",
3260
+ '- request_review \u2014 open a Review chat tab on a worktree thread (attaches .sideboard/review.md when present, else local guidelines; sends "Review."); then wait_for_turn / get_turn_result on the returned id',
3261
3261
  "- Ask the worktree agent via send_to_thread to open a draft PR with `gh pr create --draft -R <origin-owner/name>` (workspace `github:` slug / that worktree's origin \u2014 never upstream). Do not open PRs from the orchestrator yourself.",
3262
3262
  "Human-only (do not attempt): merge, ready-for-review land, purge_thread.",
3263
3263
  "Thread links in replies: when mentioning a chat/thread for the user, include a markdown link `[Title](sideboard://thread/<id>)` using the full id (or the link field from create_thread / list_threads). Sideboard renders these as clickable opens.",
@@ -6385,6 +6385,8 @@ __export(index_exports, {
6385
6385
  PASTE_ATTACH_MIN_CHARS: () => PASTE_ATTACH_MIN_CHARS,
6386
6386
  PASTE_ATTACH_MIN_LINES: () => PASTE_ATTACH_MIN_LINES,
6387
6387
  PLAN_MODE_INSTRUCTION: () => PLAN_MODE_INSTRUCTION,
6388
+ REPO_REVIEW_NAME: () => REPO_REVIEW_NAME,
6389
+ REPO_REVIEW_PATH: () => REPO_REVIEW_PATH,
6388
6390
  REVIEW_REQUEST_NAME: () => REVIEW_REQUEST_NAME,
6389
6391
  REVIEW_REQUEST_PATH: () => REVIEW_REQUEST_PATH,
6390
6392
  REVIEW_REQUEST_PREFILL: () => REVIEW_REQUEST_PREFILL,
@@ -6470,6 +6472,7 @@ __export(index_exports, {
6470
6472
  ensureCloudCoordinator: () => ensureCloudCoordinator,
6471
6473
  ensureGhPreferOrigin: () => ensureGhPreferOrigin,
6472
6474
  ensureGlobalCoordinatorCwd: () => ensureGlobalCoordinatorCwd,
6475
+ ensureReviewRequestFile: () => ensureReviewRequestFile,
6473
6476
  ensureWorkspace: () => ensureWorkspace,
6474
6477
  estimateMessageChars: () => estimateMessageChars,
6475
6478
  estimateThreadChars: () => estimateThreadChars,
@@ -6633,6 +6636,7 @@ __export(index_exports, {
6633
6636
  resolvePrSelector: () => resolvePrSelector,
6634
6637
  resolveQuotaFallbackAgent: () => resolveQuotaFallbackAgent,
6635
6638
  resolveRepoRoot: () => resolveRepoRoot,
6639
+ resolveReviewGuidelines: () => resolveReviewGuidelines,
6636
6640
  resolveThreadDefaults: () => resolveThreadDefaults,
6637
6641
  resolveThreadEffort: () => resolveThreadEffort,
6638
6642
  resolveWorktreeStartPoint: () => resolveWorktreeStartPoint,
@@ -6648,6 +6652,7 @@ __export(index_exports, {
6648
6652
  settingsSourceLabel: () => settingsSourceLabel,
6649
6653
  shouldAttachPastedText: () => shouldAttachPastedText,
6650
6654
  shouldCompactContext: () => shouldCompactContext,
6655
+ shouldRefreshReviewRequestTemplate: () => shouldRefreshReviewRequestTemplate,
6651
6656
  shouldRunWorktreeCleanup: () => shouldRunWorktreeCleanup,
6652
6657
  sideboardHomeDir: () => sideboardHomeDir,
6653
6658
  sideboardReposDir: () => sideboardReposDir,
@@ -7922,6 +7927,15 @@ async function resolveMergeBase(worktreePath, base, repoPath) {
7922
7927
  return mb || baseRef;
7923
7928
  }
7924
7929
  async function countUnpushedCommits(worktreePath) {
7930
+ const head = await git(["rev-parse", "--abbrev-ref", "HEAD"], worktreePath, {
7931
+ reject: false
7932
+ });
7933
+ const branch = head.stdout.trim();
7934
+ if (branch && branch !== "HEAD") {
7935
+ await git(["fetch", "origin", branch, "--quiet"], worktreePath, {
7936
+ reject: false
7937
+ });
7938
+ }
7925
7939
  const upstream = await git(
7926
7940
  ["rev-list", "--count", "@{upstream}..HEAD"],
7927
7941
  worktreePath,
@@ -7931,10 +7945,6 @@ async function countUnpushedCommits(worktreePath) {
7931
7945
  const n2 = Number(upstream.stdout.trim());
7932
7946
  return Number.isFinite(n2) ? n2 : 0;
7933
7947
  }
7934
- const head = await git(["rev-parse", "--abbrev-ref", "HEAD"], worktreePath, {
7935
- reject: false
7936
- });
7937
- const branch = head.stdout.trim();
7938
7948
  if (!branch || branch === "HEAD") return 0;
7939
7949
  const remote = await git(
7940
7950
  ["rev-list", "--count", `origin/${branch}..HEAD`],
@@ -10122,22 +10132,149 @@ var import_node_fs24 = require("fs");
10122
10132
  var import_node_path23 = require("path");
10123
10133
  init_global_workspace();
10124
10134
  init_thread_store();
10135
+
10136
+ // src/review/review-request-template.ts
10137
+ var REVIEW_REQUEST_TEMPLATE = `# Review guidelines:
10138
+
10139
+ You are reviewing a proposed code change so a human can decide whether it is **ready to merge / land**. Findings matter, but the primary deliverable is a clear readiness recommendation \u2014 not a laundry list of style notes.
10140
+
10141
+ ## Required outcome
10142
+
10143
+ Start your reply with a **Recommendation** section using exactly one of:
10144
+
10145
+ - **Approve** \u2014 ready to merge as-is (or with only trivial nits the author can ignore).
10146
+ - **Approve with nits** \u2014 ready to merge; list only optional polish that should not block.
10147
+ - **Request changes** \u2014 not ready; blocking issues must be fixed first.
10148
+ - **Needs more information** \u2014 cannot judge readiness yet (missing context, incomplete diff, unclear intent).
10149
+
10150
+ In 1\u20133 sentences, say **why** \u2014 grounded in correctness, risk, test coverage, and scope \u2014 not vibes. If you request changes, name the blockers explicitly.
10151
+
10152
+ People running this review are asking \u201Ccan we ship this?\u201D Treat that as the question you answer first.
10153
+
10154
+ ## Findings
10155
+
10156
+ Below are guidelines for determining whether an issue is worth flagging to the original author.
10157
+
10158
+ These are not the final word. More specific guidelines elsewhere (developer message, user message, a file, etc.) override these.
10159
+
10160
+ Flag something as a bug / blocking finding only when:
10161
+
10162
+ 1. It meaningfully impacts the accuracy, performance, security, or maintainability of the code.
10163
+ 2. The bug is discrete and actionable (not a vague codebase-wide complaint or a bundle of unrelated issues).
10164
+ 3. Fixing it does not demand rigor absent from the rest of the codebase.
10165
+ 4. The issue was introduced by this change (do not flag pre-existing bugs unless they are newly exposed by this PR).
10166
+ 5. The author would likely fix it if made aware.
10167
+ 6. It does not rely on unstated assumptions about the codebase or author intent.
10168
+ 7. Speculative breakage is not enough \u2014 identify the other code that is provably affected.
10169
+ 8. It is clearly not just an intentional change by the author.
10170
+
10171
+ When flagging an issue, include a short accompanying comment:
10172
+
10173
+ 1. Clear about why it is a problem.
10174
+ 2. Severity must match reality \u2014 do not inflate.
10175
+ 3. Brief: at most one paragraph; avoid unnecessary line breaks in prose.
10176
+ 4. No code chunks longer than 3 lines; wrap code in inline ticks or a fenced block.
10177
+ 5. Call out scenarios / environments / inputs needed to hit the bug when severity depends on them.
10178
+ 6. Matter-of-fact tone \u2014 helpful assistant, not accusatory or effusive.
10179
+ 7. Skimmable on first read.
10180
+ 8. No empty flattery (\u201CGreat job\u2026\u201D, \u201CThanks for\u2026\u201D).
10181
+
10182
+ HOW MANY FINDINGS TO RETURN:
10183
+
10184
+ List every finding the author would fix if they knew about it. If nothing qualifies, say so and still give the Recommendation. Do not stop at the first finding.
10185
+
10186
+ GUIDELINES:
10187
+
10188
+ - Ignore trivial style unless it obscures meaning or violates documented standards.
10189
+ - One comment per distinct issue (or a short multi-line range if needed).
10190
+ - Use \`\`\`suggestion blocks ONLY for concrete replacement code (minimal lines; no commentary inside the block).
10191
+ - In every \`\`\`suggestion block, preserve the exact leading whitespace of the replaced lines (spaces vs tabs, number of spaces).
10192
+ - Do NOT introduce or remove outer indentation levels unless that is the actual fix.
10193
+ - Separate **blocking** findings from **nits**. Only blocking findings should drive Request changes.
10194
+
10195
+ The report appears in chat (and can become Sideboard diff comments). Avoid unnecessary location chatter in the body; keep line ranges as short as possible (prefer \u22645\u201310 lines).
10196
+
10197
+ ## Getting the diff
10198
+
10199
+ Use Sideboard's diff for this thread's worktree. Prefer the \`get_diff\` MCP tool (pass this thread's ref) for a compact summary, then read specific files with Read/Glob as needed. In the Sideboard desktop app, the Changes panel shows the same worktree diff.
10200
+
10201
+ If the user asks you to address or read line comments they added in the Changes / file diff UI, those arrive as \`diff-comment\` attachments on the next turn \u2014 follow them precisely.
10202
+
10203
+ ## Fallback: if you don't have access to the Sideboard diff tool
10204
+
10205
+ If you don't have access to \`get_diff\`, use the following git commands to get the diff:
10206
+
10207
+ \`\`\`bash
10208
+ # Get the merge base between this branch and the target
10209
+ MERGE_BASE=$(git merge-base origin/main HEAD)
10210
+
10211
+ # Get the committed diff against the merge base
10212
+ git diff $MERGE_BASE HEAD
10213
+
10214
+ # Get any uncommitted changes (staged and unstaged)
10215
+ git diff HEAD
10216
+ \`\`\`
10217
+
10218
+ Review the combination of both outputs: the first shows all committed changes on this branch relative to the target, and the second shows any uncommitted work in progress.
10219
+
10220
+ No need to mention in your report whether or not you used one of the fallback strategies; it's usually irrelevant.
10221
+
10222
+ ## Output format
10223
+
10224
+ **1. Recommendation first** (required), then **2. Findings** (may be empty).
10225
+
10226
+ Only report ONE finding per unique issue.
10227
+
10228
+ <example>
10229
+ ## Recommendation
10230
+
10231
+ **Request changes** \u2014 The empty-input crash on load will break first-run users; fix that before merge. The unused helper is a nit and can wait.
10232
+
10233
+ ## Findings
10234
+
10235
+ ### **#1 Empty input causes crash** (blocking)
10236
+
10237
+ If the input field is empty when the page loads, the app will crash.
10238
+
10239
+ File: src/client/frontends/desktop/ui/Input.tsx
10240
+
10241
+ ### **#2 Dead code** (nit)
10242
+
10243
+ The getUserData function is now unused. It should be deleted.
10244
+
10245
+ File: src/client/frontends/desktop/core/UserData.ts
10246
+ </example>
10247
+
10248
+ <example>
10249
+ ## Recommendation
10250
+
10251
+ **Approve** \u2014 Diff is scoped, behavior looks correct, and there are no blocking issues. Safe to merge.
10252
+ </example>
10253
+ `;
10254
+
10255
+ // src/review/request-review.ts
10256
+ var REPO_REVIEW_PATH = ".sideboard/review.md";
10257
+ var REPO_REVIEW_NAME = "review.md";
10125
10258
  var REVIEW_REQUEST_PATH = ".sideboard/attachments/Review request.md";
10126
10259
  var REVIEW_REQUEST_NAME = "Review request.md";
10127
- var REVIEW_REQUEST_PREFILL = `Please review the changes in this workspace and recommend whether they are ready to merge.
10128
-
10129
- Start with a **Recommendation**: Approve, Approve with nits, Request changes, or Needs more information \u2014 and say why in 1\u20133 sentences. Then list blocking findings vs nits (findings may be empty).`;
10130
- function buildReviewRequestAttachment(content) {
10131
- return {
10132
- id: (0, import_node_crypto5.randomUUID)(),
10133
- name: REVIEW_REQUEST_NAME,
10134
- kind: "file",
10135
- path: REVIEW_REQUEST_PATH,
10136
- content
10137
- };
10260
+ var REVIEW_REQUEST_PREFILL = "Review.";
10261
+ var ATTACHMENTS_GITIGNORE2 = `# Sideboard review / composer attachments (local only)
10262
+ *
10263
+ !.gitignore
10264
+ `;
10265
+ var LEGACY_REVIEW_TEMPLATE_MARKERS = [
10266
+ "You are acting as a reviewer for a proposed code change made by another engineer.",
10267
+ "HOW MANY FINDINGS TO RETURN:"
10268
+ ];
10269
+ function shouldRefreshReviewRequestTemplate(content) {
10270
+ const trimmed = content.trim();
10271
+ if (!trimmed) return true;
10272
+ if (/##\s*Recommendation|ready to merge|Approve with nits|Request changes/i.test(trimmed)) {
10273
+ return false;
10274
+ }
10275
+ return LEGACY_REVIEW_TEMPLATE_MARKERS.every((m) => trimmed.includes(m));
10138
10276
  }
10139
- function readExistingReviewRequestFile(worktreePath) {
10140
- const abs = (0, import_node_path23.join)(worktreePath, REVIEW_REQUEST_PATH);
10277
+ function readTextIfPresent(abs) {
10141
10278
  if (!(0, import_node_fs24.existsSync)(abs)) return null;
10142
10279
  try {
10143
10280
  const content = (0, import_node_fs24.readFileSync)(abs, "utf8");
@@ -10146,6 +10283,87 @@ function readExistingReviewRequestFile(worktreePath) {
10146
10283
  return null;
10147
10284
  }
10148
10285
  }
10286
+ function ensureAttachmentsGitignore(worktreePath) {
10287
+ const gitignoreAbs = (0, import_node_path23.join)(worktreePath, ".sideboard", "attachments", ".gitignore");
10288
+ if ((0, import_node_fs24.existsSync)(gitignoreAbs)) return;
10289
+ (0, import_node_fs24.mkdirSync)((0, import_node_path23.dirname)(gitignoreAbs), { recursive: true });
10290
+ (0, import_node_fs24.writeFileSync)(gitignoreAbs, ATTACHMENTS_GITIGNORE2, "utf8");
10291
+ }
10292
+ function resolveReviewGuidelines(worktreePath) {
10293
+ const repoAbs = (0, import_node_path23.join)(worktreePath, REPO_REVIEW_PATH);
10294
+ const repoContent = readTextIfPresent(repoAbs);
10295
+ if (repoContent) {
10296
+ return {
10297
+ path: REPO_REVIEW_PATH,
10298
+ name: REPO_REVIEW_NAME,
10299
+ content: repoContent,
10300
+ source: "repo"
10301
+ };
10302
+ }
10303
+ const localAbs = (0, import_node_path23.join)(worktreePath, REVIEW_REQUEST_PATH);
10304
+ const localContent = readTextIfPresent(localAbs);
10305
+ if (localContent && !shouldRefreshReviewRequestTemplate(localContent)) {
10306
+ return {
10307
+ path: REVIEW_REQUEST_PATH,
10308
+ name: REVIEW_REQUEST_NAME,
10309
+ content: localContent,
10310
+ source: "local"
10311
+ };
10312
+ }
10313
+ ensureAttachmentsGitignore(worktreePath);
10314
+ (0, import_node_fs24.mkdirSync)((0, import_node_path23.dirname)(localAbs), { recursive: true });
10315
+ (0, import_node_fs24.writeFileSync)(localAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
10316
+ return {
10317
+ path: REVIEW_REQUEST_PATH,
10318
+ name: REVIEW_REQUEST_NAME,
10319
+ content: REVIEW_REQUEST_TEMPLATE,
10320
+ source: "stock"
10321
+ };
10322
+ }
10323
+ function ensureReviewRequestFile(worktreePath) {
10324
+ const repoAbs = (0, import_node_path23.join)(worktreePath, REPO_REVIEW_PATH);
10325
+ const repoContent = readTextIfPresent(repoAbs);
10326
+ if (repoContent) {
10327
+ return {
10328
+ path: REPO_REVIEW_PATH,
10329
+ name: REPO_REVIEW_NAME,
10330
+ content: repoContent,
10331
+ source: "repo"
10332
+ };
10333
+ }
10334
+ const localAbs = (0, import_node_path23.join)(worktreePath, REVIEW_REQUEST_PATH);
10335
+ const localContent = readTextIfPresent(localAbs);
10336
+ if (localContent && !shouldRefreshReviewRequestTemplate(localContent)) {
10337
+ return {
10338
+ path: REVIEW_REQUEST_PATH,
10339
+ name: REVIEW_REQUEST_NAME,
10340
+ content: localContent,
10341
+ source: "local"
10342
+ };
10343
+ }
10344
+ (0, import_node_fs24.mkdirSync)((0, import_node_path23.dirname)(repoAbs), { recursive: true });
10345
+ (0, import_node_fs24.writeFileSync)(repoAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
10346
+ return {
10347
+ path: REPO_REVIEW_PATH,
10348
+ name: REPO_REVIEW_NAME,
10349
+ content: REVIEW_REQUEST_TEMPLATE,
10350
+ source: "repo"
10351
+ };
10352
+ }
10353
+ function buildReviewRequestAttachment(content, opts) {
10354
+ const path = opts?.path ?? REVIEW_REQUEST_PATH;
10355
+ const name = opts?.name ?? (path === REPO_REVIEW_PATH ? REPO_REVIEW_NAME : REVIEW_REQUEST_NAME);
10356
+ return {
10357
+ id: (0, import_node_crypto5.randomUUID)(),
10358
+ name,
10359
+ kind: "file",
10360
+ path,
10361
+ content
10362
+ };
10363
+ }
10364
+ function readExistingReviewRequestFile(worktreePath) {
10365
+ return readTextIfPresent((0, import_node_path23.join)(worktreePath, REPO_REVIEW_PATH)) ?? readTextIfPresent((0, import_node_path23.join)(worktreePath, REVIEW_REQUEST_PATH));
10366
+ }
10149
10367
  async function requestReview(threadRef, send) {
10150
10368
  const from = findThreadByRef(threadRef);
10151
10369
  if (!from) throw new Error(`Thread not found: ${threadRef}`);
@@ -10157,12 +10375,16 @@ async function requestReview(threadRef, send) {
10157
10375
  if (from.status === "archived") {
10158
10376
  throw new Error(`Thread is archived: ${from.id}`);
10159
10377
  }
10160
- const existing = readExistingReviewRequestFile(from.worktreePath);
10161
- const attachments = existing ? [buildReviewRequestAttachment(existing)] : [];
10378
+ const guidelines = resolveReviewGuidelines(from.worktreePath);
10162
10379
  const tab = createChatTab({
10163
10380
  fromThreadId: from.id,
10164
10381
  title: "Review",
10165
- attachments
10382
+ attachments: [
10383
+ buildReviewRequestAttachment(guidelines.content, {
10384
+ path: guidelines.path,
10385
+ name: guidelines.name
10386
+ })
10387
+ ]
10166
10388
  });
10167
10389
  const started = await send(tab.id, REVIEW_REQUEST_PREFILL);
10168
10390
  return { tab: started, from };
@@ -10742,9 +10964,11 @@ var Orchestrator = class {
10742
10964
  this.emit({ type: "error", threadId, message: `context compact failed: ${message}` });
10743
10965
  thread = this.requireThread(threadId);
10744
10966
  }
10967
+ const sentAttachments = thread.attachments.length > 0 ? [...thread.attachments] : void 0;
10745
10968
  appendMessage(threadId, {
10746
10969
  role: "user",
10747
10970
  text: prompt,
10971
+ ...sentAttachments ? { attachments: sentAttachments } : {},
10748
10972
  ts: (/* @__PURE__ */ new Date()).toISOString()
10749
10973
  });
10750
10974
  thread = this.requireThread(threadId);
@@ -10752,7 +10976,7 @@ var Orchestrator = class {
10752
10976
  thread.worktreePath,
10753
10977
  prompt,
10754
10978
  {
10755
- attachments: thread.attachments
10979
+ attachments: sentAttachments ?? thread.attachments
10756
10980
  }
10757
10981
  );
10758
10982
  const orchestrationReminder = isOrchestratorThread(thread) ? coordinatorTurnReminder({
@@ -12056,7 +12280,7 @@ async function startMcpServer() {
12056
12280
  );
12057
12281
  server.tool(
12058
12282
  "request_review",
12059
- "Start a merge-readiness Review on a worktree agent thread (same as the desktop Review button). Opens a new Review chat tab, optionally attaches .sideboard/attachments/Review request.md when present, and asks for Approve / Approve with nits / Request changes / Needs more information. Pass a worktree thread ref \u2014 not the orchestrator. Then wait_for_turn / get_turn_result on the returned review tab id.",
12283
+ 'Start a merge-readiness Review on a worktree agent thread (same as the desktop Review button). Opens a new Review chat tab, attaches .sideboard/review.md when present (else local Review request.md / stock template), and sends "Review." Expect Approve / Approve with nits / Request changes / Needs more information. Pass a worktree thread ref \u2014 not the orchestrator. Then wait_for_turn / get_turn_result on the returned review tab id.',
12060
12284
  { ref: import_zod.z.string().describe("Worktree thread id/ref to review") },
12061
12285
  async ({ ref }) => {
12062
12286
  try {
@@ -12741,6 +12965,8 @@ init_injected_mcp();
12741
12965
  PASTE_ATTACH_MIN_CHARS,
12742
12966
  PASTE_ATTACH_MIN_LINES,
12743
12967
  PLAN_MODE_INSTRUCTION,
12968
+ REPO_REVIEW_NAME,
12969
+ REPO_REVIEW_PATH,
12744
12970
  REVIEW_REQUEST_NAME,
12745
12971
  REVIEW_REQUEST_PATH,
12746
12972
  REVIEW_REQUEST_PREFILL,
@@ -12826,6 +13052,7 @@ init_injected_mcp();
12826
13052
  ensureCloudCoordinator,
12827
13053
  ensureGhPreferOrigin,
12828
13054
  ensureGlobalCoordinatorCwd,
13055
+ ensureReviewRequestFile,
12829
13056
  ensureWorkspace,
12830
13057
  estimateMessageChars,
12831
13058
  estimateThreadChars,
@@ -12989,6 +13216,7 @@ init_injected_mcp();
12989
13216
  resolvePrSelector,
12990
13217
  resolveQuotaFallbackAgent,
12991
13218
  resolveRepoRoot,
13219
+ resolveReviewGuidelines,
12992
13220
  resolveThreadDefaults,
12993
13221
  resolveThreadEffort,
12994
13222
  resolveWorktreeStartPoint,
@@ -13004,6 +13232,7 @@ init_injected_mcp();
13004
13232
  settingsSourceLabel,
13005
13233
  shouldAttachPastedText,
13006
13234
  shouldCompactContext,
13235
+ shouldRefreshReviewRequestTemplate,
13007
13236
  shouldRunWorktreeCleanup,
13008
13237
  sideboardHomeDir,
13009
13238
  sideboardReposDir,