@sideboard-ai/core 0.1.192 → 0.1.194

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 (60) hide show
  1. package/dist/{abletime-53T7G667.js → abletime-HR6P4GJH.js} +4 -3
  2. package/dist/{abletime-JFMBCSIQ.js → abletime-JINF5KJV.js} +4 -3
  3. package/dist/agents/cursor-runner.js +3 -3
  4. package/dist/{agents-TXGQIK2E.js → agents-OXDDPH2O.js} +10 -9
  5. package/dist/{agents-T5VBB6X3.js → agents-WQL7SZY6.js} +8 -7
  6. package/dist/{app-settings-663VQWUV.js → app-settings-5KHVKJKO.js} +5 -2
  7. package/dist/{app-settings-74HBAAH7.js → app-settings-J775CYOO.js} +5 -2
  8. package/dist/branch-prefix-7DTIUJT2.js +12 -0
  9. package/dist/branch-prefix-N3KTCRCE.js +10 -0
  10. package/dist/{caffeinate-hold-SK2QISPE.js → caffeinate-hold-22VFDPSO.js} +2 -2
  11. package/dist/{caffeinate-hold-DBKMRZZO.js → caffeinate-hold-OK4JJ5PP.js} +2 -2
  12. package/dist/{chunk-TWSCTJCR.js → chunk-2X6RGKLI.js} +7 -7
  13. package/dist/{chunk-HVIEYTUK.js → chunk-3OHR73VK.js} +1 -1
  14. package/dist/{chunk-AO35MGOE.js → chunk-4TYXWPAJ.js} +1 -1
  15. package/dist/{chunk-WHD4JNTN.js → chunk-6H2XPPPR.js} +8 -8
  16. package/dist/{chunk-7NSXGL6L.js → chunk-6YTI2HBG.js} +5 -5
  17. package/dist/{chunk-WLPXZFRE.js → chunk-7J72QJOM.js} +1 -1
  18. package/dist/{chunk-OKZFTOM2.js → chunk-AH6WIBLY.js} +19 -1
  19. package/dist/{chunk-GOZ6T3AG.js → chunk-CJFBSQIB.js} +1 -1
  20. package/dist/{chunk-XBLN52RZ.js → chunk-DP4TKAMZ.js} +3 -3
  21. package/dist/{chunk-4M2AK3O5.js → chunk-E37JHCO3.js} +1 -1
  22. package/dist/{chunk-JTJHOYCU.js → chunk-EQNRWDLN.js} +10 -4
  23. package/dist/chunk-FJZWM2ON.js +23 -0
  24. package/dist/{chunk-VV7UT3E2.js → chunk-HZTHWU6A.js} +5 -5
  25. package/dist/{chunk-TCQAFTSV.js → chunk-KJBYIIJU.js} +1 -1
  26. package/dist/{chunk-UBQIELFS.js → chunk-KTHRWHA3.js} +1 -1
  27. package/dist/{chunk-FY64F2ZQ.js → chunk-NLHFAMC2.js} +1 -1
  28. package/dist/{chunk-NF6ENGHS.js → chunk-NY366OEZ.js} +151 -160
  29. package/dist/{chunk-WXKXSZN4.js → chunk-S4QCBBXS.js} +6 -6
  30. package/dist/{chunk-J7LQVLFQ.js → chunk-S5GLSRLP.js} +19 -1
  31. package/dist/{chunk-IEEAVWAP.js → chunk-SALM35KD.js} +5 -5
  32. package/dist/{chunk-FZ3G5TXM.js → chunk-VRPPFKAO.js} +152 -162
  33. package/dist/chunk-WDRMZKVB.js +20 -0
  34. package/dist/{chunk-EFVFB2AH.js → chunk-WFUDY6PC.js} +12 -5
  35. package/dist/{chunk-OCQPTUR7.js → chunk-YWI3IBP2.js} +1 -1
  36. package/dist/{connected-teams-WOYCH7FR.js → connected-teams-PU4AMFNA.js} +2 -2
  37. package/dist/{connected-teams-ON2KIKQX.js → connected-teams-WK47ATT7.js} +2 -2
  38. package/dist/{coordinator-prompt-S4HHFSLI.js → coordinator-prompt-IX66YUJ2.js} +7 -6
  39. package/dist/{coordinator-prompt-PNUDN4DW.js → coordinator-prompt-LYKS3YNG.js} +6 -5
  40. package/dist/{cursor-recover-NDMLLNYX.js → cursor-recover-DTQAUDGO.js} +2 -2
  41. package/dist/{cursor-recover-L5MD6MBO.js → cursor-recover-TXVTLAMA.js} +1 -1
  42. package/dist/{global-workspace-JTBBH2K5.js → global-workspace-4QX6UEOY.js} +7 -6
  43. package/dist/{global-workspace-2BVCJUAL.js → global-workspace-SNJ2Z6SL.js} +6 -5
  44. package/dist/index.cjs +192 -146
  45. package/dist/index.d.cts +46 -8
  46. package/dist/index.d.ts +46 -8
  47. package/dist/index.js +55 -43
  48. package/dist/mcp/run-stdio.cjs +179 -145
  49. package/dist/mcp/run-stdio.js +19 -18
  50. package/dist/{orchestrator-4G76BOHH.js → orchestrator-A5KF2335.js} +10 -9
  51. package/dist/{orchestrator-LWEZ74RK.js → orchestrator-S3M2E7O6.js} +12 -11
  52. package/dist/{paths-RRCLNSJB.js → paths-C2VERLAE.js} +1 -1
  53. package/dist/{paths-53TLA7VQ.js → paths-LOXCISLX.js} +1 -1
  54. package/dist/{thread-store-ZL6GDIDP.js → thread-store-CO77HV2K.js} +2 -2
  55. package/dist/{thread-store-RXXNSRVO.js → thread-store-M4B52FUS.js} +2 -2
  56. package/dist/{workspaces-ZYPME3YD.js → workspaces-URMUVGAQ.js} +7 -6
  57. package/dist/{workspaces-OY7D5RPU.js → workspaces-UTOYGBYB.js} +6 -5
  58. package/dist/{worktree-7LOSSD7G.js → worktree-R43JOR4V.js} +5 -4
  59. package/dist/{worktree-LPWTX2NA.js → worktree-RM6UWNGZ.js} +6 -5
  60. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -274,9 +274,12 @@ function parseSettingsFile(path2, source) {
274
274
  const prompts = {
275
275
  renameBranch: typeof promptsRaw.rename_branch === "string" ? promptsRaw.rename_branch : void 0,
276
276
  createPr: typeof promptsRaw.create_pr === "string" ? promptsRaw.create_pr : void 0,
277
- general: typeof promptsRaw.general === "string" ? promptsRaw.general : void 0
277
+ general: typeof promptsRaw.general === "string" ? promptsRaw.general : void 0,
278
+ resolveMergeConflicts: typeof promptsRaw.resolve_merge_conflicts === "string" ? promptsRaw.resolve_merge_conflicts : void 0
278
279
  };
279
- const hasPrompts = Boolean(prompts.renameBranch || prompts.createPr || prompts.general);
280
+ const hasPrompts = Boolean(
281
+ prompts.renameBranch || prompts.createPr || prompts.general || prompts.resolveMergeConflicts
282
+ );
280
283
  return {
281
284
  source,
282
285
  setup,
@@ -302,9 +305,12 @@ function mergeSettings(base, overlay) {
302
305
  const prompts = {
303
306
  renameBranch: overlay.prompts?.renameBranch ?? base.prompts?.renameBranch,
304
307
  createPr: overlay.prompts?.createPr ?? base.prompts?.createPr,
305
- general: overlay.prompts?.general ?? base.prompts?.general
308
+ general: overlay.prompts?.general ?? base.prompts?.general,
309
+ resolveMergeConflicts: overlay.prompts?.resolveMergeConflicts ?? base.prompts?.resolveMergeConflicts
306
310
  };
307
- const hasPrompts = Boolean(prompts.renameBranch || prompts.createPr || prompts.general);
311
+ const hasPrompts = Boolean(
312
+ prompts.renameBranch || prompts.createPr || prompts.general || prompts.resolveMergeConflicts
313
+ );
308
314
  return {
309
315
  source: overlay.source,
310
316
  setup: overlay.setup ?? base.setup,
@@ -428,7 +434,8 @@ function getRepoSetupInfo(worktreePath, repoPath) {
428
434
  return {
429
435
  hasConfig: hasWorkspaceHook(worktreePath, repoPath) || cursor || Boolean(convention),
430
436
  hasSetupScript,
431
- configLabel
437
+ configLabel,
438
+ ...settings?.prompts ? { prompts: settings.prompts } : {}
432
439
  };
433
440
  }
434
441
  var import_node_fs3, import_node_os, import_node_path3, import_smol_toml;
@@ -955,6 +962,32 @@ var init_secret_vault = __esm({
955
962
  }
956
963
  });
957
964
 
965
+ // src/git/branch-prefix.ts
966
+ var branch_prefix_exports = {};
967
+ __export(branch_prefix_exports, {
968
+ examplePrefixedBranch: () => examplePrefixedBranch,
969
+ resolveGitBranchPrefix: () => resolveGitBranchPrefix,
970
+ sanitizeGitBranchPrefix: () => sanitizeGitBranchPrefix
971
+ });
972
+ function sanitizeGitBranchPrefix(raw) {
973
+ const first = (raw ?? "").trim().replace(/^\/+|\/+$/g, "").split("/")[0] ?? "";
974
+ const slug = first.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 39);
975
+ return slug || null;
976
+ }
977
+ function resolveGitBranchPrefix(opts) {
978
+ return sanitizeGitBranchPrefix(opts.setting);
979
+ }
980
+ function examplePrefixedBranch(prefix, ticket) {
981
+ const head = sanitizeGitBranchPrefix(prefix);
982
+ const slug = ticket?.trim() ? `${ticket.trim()}-eng-fix-thing` : "bb-1234-eng-fix-thing";
983
+ return head ? `${head}/${slug}` : slug;
984
+ }
985
+ var init_branch_prefix = __esm({
986
+ "src/git/branch-prefix.ts"() {
987
+ "use strict";
988
+ }
989
+ });
990
+
958
991
  // src/store/issue-source-labels.ts
959
992
  function issueSourceLabel(source) {
960
993
  if (source && source in ISSUE_SOURCE_LABELS) {
@@ -1023,6 +1056,7 @@ __export(app_settings_exports, {
1023
1056
  getGithubPat: () => getGithubPat,
1024
1057
  getIssueSource: () => getIssueSource,
1025
1058
  getLinearApiKey: () => getLinearApiKey,
1059
+ gitBranchPrefixSetting: () => gitBranchPrefixSetting,
1026
1060
  harnessEnvKey: () => harnessEnvKey,
1027
1061
  isAbleTimeConnected: () => isAbleTimeConnected,
1028
1062
  isIssueSourceConnected: () => isIssueSourceConnected,
@@ -1398,6 +1432,10 @@ function normalizeAdvanced(raw) {
1398
1432
  if (typeof source.autoRenameBranch === "boolean") {
1399
1433
  out.autoRenameBranch = source.autoRenameBranch;
1400
1434
  }
1435
+ if (typeof source.branchPrefix === "string") {
1436
+ const prefix = sanitizeGitBranchPrefix(source.branchPrefix);
1437
+ if (prefix) out.branchPrefix = prefix;
1438
+ }
1401
1439
  if (typeof source.autoRunAfterSetup === "boolean") {
1402
1440
  out.autoRunAfterSetup = source.autoRunAfterSetup;
1403
1441
  }
@@ -2251,6 +2289,13 @@ function updateAdvancedSettings(patch) {
2251
2289
  if (typeof patch.autoRenameBranch === "boolean") {
2252
2290
  advanced.autoRenameBranch = patch.autoRenameBranch;
2253
2291
  }
2292
+ if (patch.branchPrefix === null || patch.branchPrefix === "") {
2293
+ delete advanced.branchPrefix;
2294
+ } else if (typeof patch.branchPrefix === "string") {
2295
+ const prefix = sanitizeGitBranchPrefix(patch.branchPrefix);
2296
+ if (prefix) advanced.branchPrefix = prefix;
2297
+ else delete advanced.branchPrefix;
2298
+ }
2254
2299
  if (typeof patch.autoRunAfterSetup === "boolean") {
2255
2300
  advanced.autoRunAfterSetup = patch.autoRunAfterSetup;
2256
2301
  }
@@ -2311,6 +2356,9 @@ function updateAdvancedSettings(patch) {
2311
2356
  function autoRenameBranchEnabled(settings = loadAppSettings()) {
2312
2357
  return settings.advanced.autoRenameBranch !== false;
2313
2358
  }
2359
+ function gitBranchPrefixSetting(settings = loadAppSettings()) {
2360
+ return sanitizeGitBranchPrefix(settings.advanced.branchPrefix);
2361
+ }
2314
2362
  function autoRunAfterSetupEnabled(settings = loadAppSettings()) {
2315
2363
  return Boolean(settings.advanced.autoRunAfterSetup);
2316
2364
  }
@@ -2456,6 +2504,7 @@ var init_app_settings = __esm({
2456
2504
  init_paths();
2457
2505
  init_private_file();
2458
2506
  init_secret_vault();
2507
+ init_branch_prefix();
2459
2508
  init_issue_source_labels();
2460
2509
  init_account_profile();
2461
2510
  HARNESS_ENV_KEYS = {
@@ -7783,7 +7832,7 @@ var init_coordinator_prompt = __esm({
7783
7832
  "- get_diff \u2014 compact diff summary",
7784
7833
  "- get_pr_checks \u2014 snapshot of a worktree thread's PR checks (null = no PR). Use this to inspect status. If the user gave a goal, the worktree agent watches with `gh pr checks --watch` \u2014 do not poll for the human.",
7785
7834
  '- request_review \u2014 open a Review chat tab on a worktree thread (attaches .claude/skills/review/SKILL.md when present, else .context/review.md copied from .sideboard/review.md / stock; sends "Review changes in this workspace."); then wait_for_turn (loop while stillRunning) / get_turn_result on the returned id. The review stays in that chat so they can read it and type next steps \u2014 do not tell the child to comment or update the PR or ticket.',
7786
- "- ask_git \u2014 commit & push, open a draft PR, mark ready for review, resolve conflicts, or merge. When the worktree is clean, Sideboard pushes / opens the PR itself. When dirty, it steers the worktree agent \u2014 then wait_for_turn (loop while stillRunning). Prefer this over paraphrasing. If ask_git / get_thread lastError says the GraphQL/PR body is too long, the branch is already pushed \u2014 send_to_thread so the worktree agent runs `gh pr create --body-file` with a short description. Do not invent SSH or auth failures from that error. If the user gave a goal (Greptile 5/5, CI green), send_to_thread that goal so the worktree agent enters the watch-fix-push loop \u2014 do not tell the human to poll.",
7835
+ "- ask_git \u2014 commit & push, open a draft PR, mark ready for review, resolve conflicts, or merge \u2014 same prompts as the desktop git buttons (Resolve, Create PR, \u2026). Always steers the worktree agent; then wait_for_turn (loop while stillRunning). Prefer this over paraphrasing. If the user gave a goal (Greptile 5/5, CI green), send_to_thread that goal so the worktree agent enters the watch-fix-push loop \u2014 do not tell the human to poll.",
7787
7836
  '- Merge (`ask_git` action=merge / send_to_thread "Merge PR.") only when the user explicitly asked to merge that PR. Do not merge because the work looks done, CI is green, or a typical flow includes it.',
7788
7837
  '- Or send_to_thread with those exact phrases: "Commit and push.", "Commit, push, and open a draft PR.", "Ready for review.", "Merge the remote branch (main) into your branch and resolve conflicts. Then, commit and push your changes.", "Merge PR." (draft PRs: `gh pr create --draft -R <origin-owner/name>` using the workspace `github:` slug \u2014 never upstream). Never run git/gh from this orchestration cwd, and never merge the PR yourself.',
7789
7838
  "Process guides:",
@@ -14176,87 +14225,6 @@ var init_spawn = __esm({
14176
14225
  }
14177
14226
  });
14178
14227
 
14179
- // src/land/pr-metadata.ts
14180
- function looksGeneric(title) {
14181
- const t = title.trim();
14182
- if (!t) return true;
14183
- if (t.length < 4) return true;
14184
- if (GENERIC_TITLE_RE.test(t)) return true;
14185
- if (!/[\s:/\-_]/.test(t) && t.length <= 24) return true;
14186
- return false;
14187
- }
14188
- function truncateTitle(title, max = 72) {
14189
- const t = title.trim().replace(/\s+/g, " ");
14190
- if (t.length <= max) return t;
14191
- return `${t.slice(0, max - 1).trimEnd()}\u2026`;
14192
- }
14193
- function titleFromPaths(paths) {
14194
- if (paths.length === 0) return null;
14195
- const top = paths.slice(0, 3).map((p) => p.replace(/\/$/, ""));
14196
- if (paths.length === 1) return truncateTitle(`Update ${top[0]}`);
14197
- if (paths.length <= 3) return truncateTitle(`Update ${top.join(", ")}`);
14198
- return truncateTitle(`Update ${top[0]} and ${paths.length - 1} more files`);
14199
- }
14200
- async function suggestPrMetadata(worktreePath, opts) {
14201
- const [{ stdout: logOut }, { stdout: nameStatus }, { stdout: statOut }] = await Promise.all([
14202
- git(["log", "--format=%s", `${opts.base}..HEAD`], worktreePath, {
14203
- reject: false
14204
- }),
14205
- git(["diff", "--name-only", `${opts.base}...HEAD`], worktreePath, {
14206
- reject: false
14207
- }),
14208
- git(["diff", "--stat", `${opts.base}...HEAD`], worktreePath, {
14209
- reject: false
14210
- })
14211
- ]);
14212
- const { stdout: dirtyNames } = await git(["diff", "--name-only", "HEAD"], worktreePath, {
14213
- reject: false
14214
- });
14215
- const { stdout: untracked } = await git(
14216
- ["ls-files", "--others", "--exclude-standard"],
14217
- worktreePath,
14218
- { reject: false }
14219
- );
14220
- const subjects = logOut.split("\n").map((s) => s.trim()).filter(Boolean);
14221
- const paths = [
14222
- ...new Set(
14223
- [...nameStatus.split("\n"), ...dirtyNames.split("\n"), ...untracked.split("\n")].map((p) => p.trim()).filter(Boolean)
14224
- )
14225
- ].sort((a, b) => a.localeCompare(b));
14226
- const meaningfulSubjects = subjects.filter((s) => !looksGeneric(s));
14227
- const fallback = opts.fallbackTitle?.trim() || null;
14228
- const fallbackOk = fallback && !looksGeneric(fallback) ? fallback : null;
14229
- const title = truncateTitle(
14230
- meaningfulSubjects[0] ?? fallbackOk ?? titleFromPaths(paths) ?? "Sideboard changes"
14231
- );
14232
- const commitMessage = meaningfulSubjects[0] ?? fallbackOk ?? titleFromPaths(paths) ?? `sideboard: ${title}`;
14233
- const summaryLines = meaningfulSubjects.length > 0 ? meaningfulSubjects.slice(0, 12).map((s) => `- ${s}`) : paths.slice(0, 20).map((p) => `- \`${p}\``);
14234
- const bodyParts = [
14235
- "## Summary",
14236
- summaryLines.length > 0 ? summaryLines.join("\n") : "- Changes landed via Sideboard.",
14237
- ""
14238
- ];
14239
- if (statOut.trim()) {
14240
- bodyParts.push("## Diff", "```", statOut.trim(), "```", "");
14241
- }
14242
- if (opts.sourceLabel) {
14243
- bodyParts.push(`_Landed via Sideboard from ${opts.sourceLabel}_`);
14244
- }
14245
- return {
14246
- title,
14247
- body: bodyParts.join("\n").trim() + "\n",
14248
- commitMessage: truncateTitle(commitMessage, 72)
14249
- };
14250
- }
14251
- var GENERIC_TITLE_RE;
14252
- var init_pr_metadata = __esm({
14253
- "src/land/pr-metadata.ts"() {
14254
- "use strict";
14255
- init_run();
14256
- GENERIC_TITLE_RE = /^(sideboard:|thread\/|wip\b|tmp\b|update\b|changes?\b|misc\b|stuff\b)/i;
14257
- }
14258
- });
14259
-
14260
14228
  // src/git/agent-git-actions.ts
14261
14229
  function agentGitPrompt(action, opts) {
14262
14230
  switch (action) {
@@ -14277,6 +14245,19 @@ function agentGitPrompt(action, opts) {
14277
14245
  return "Merge PR.";
14278
14246
  }
14279
14247
  }
14248
+ function resolveSidebarGitPrompt(action, opts) {
14249
+ if (action === "create-pr" || action === "create-draft" || action === "create-web") {
14250
+ const custom = opts?.createPr?.trim();
14251
+ if (custom) return custom;
14252
+ return agentGitPrompt(action === "create-pr" ? "create-draft" : action);
14253
+ }
14254
+ if (action === "resolve-conflicts") {
14255
+ const custom = opts?.resolveMergeConflicts?.trim();
14256
+ if (custom) return custom;
14257
+ return agentGitPrompt("resolve-conflicts", opts);
14258
+ }
14259
+ return agentGitPrompt(action, opts);
14260
+ }
14280
14261
  function gitActionExpansion(action, base) {
14281
14262
  switch (action) {
14282
14263
  case "commit-push":
@@ -19505,6 +19486,87 @@ var init_diff = __esm({
19505
19486
  }
19506
19487
  });
19507
19488
 
19489
+ // src/land/pr-metadata.ts
19490
+ function looksGeneric(title) {
19491
+ const t = title.trim();
19492
+ if (!t) return true;
19493
+ if (t.length < 4) return true;
19494
+ if (GENERIC_TITLE_RE.test(t)) return true;
19495
+ if (!/[\s:/\-_]/.test(t) && t.length <= 24) return true;
19496
+ return false;
19497
+ }
19498
+ function truncateTitle(title, max = 72) {
19499
+ const t = title.trim().replace(/\s+/g, " ");
19500
+ if (t.length <= max) return t;
19501
+ return `${t.slice(0, max - 1).trimEnd()}\u2026`;
19502
+ }
19503
+ function titleFromPaths(paths) {
19504
+ if (paths.length === 0) return null;
19505
+ const top = paths.slice(0, 3).map((p) => p.replace(/\/$/, ""));
19506
+ if (paths.length === 1) return truncateTitle(`Update ${top[0]}`);
19507
+ if (paths.length <= 3) return truncateTitle(`Update ${top.join(", ")}`);
19508
+ return truncateTitle(`Update ${top[0]} and ${paths.length - 1} more files`);
19509
+ }
19510
+ async function suggestPrMetadata(worktreePath, opts) {
19511
+ const [{ stdout: logOut }, { stdout: nameStatus }, { stdout: statOut }] = await Promise.all([
19512
+ git(["log", "--format=%s", `${opts.base}..HEAD`], worktreePath, {
19513
+ reject: false
19514
+ }),
19515
+ git(["diff", "--name-only", `${opts.base}...HEAD`], worktreePath, {
19516
+ reject: false
19517
+ }),
19518
+ git(["diff", "--stat", `${opts.base}...HEAD`], worktreePath, {
19519
+ reject: false
19520
+ })
19521
+ ]);
19522
+ const { stdout: dirtyNames } = await git(["diff", "--name-only", "HEAD"], worktreePath, {
19523
+ reject: false
19524
+ });
19525
+ const { stdout: untracked } = await git(
19526
+ ["ls-files", "--others", "--exclude-standard"],
19527
+ worktreePath,
19528
+ { reject: false }
19529
+ );
19530
+ const subjects = logOut.split("\n").map((s) => s.trim()).filter(Boolean);
19531
+ const paths = [
19532
+ ...new Set(
19533
+ [...nameStatus.split("\n"), ...dirtyNames.split("\n"), ...untracked.split("\n")].map((p) => p.trim()).filter(Boolean)
19534
+ )
19535
+ ].sort((a, b) => a.localeCompare(b));
19536
+ const meaningfulSubjects = subjects.filter((s) => !looksGeneric(s));
19537
+ const fallback = opts.fallbackTitle?.trim() || null;
19538
+ const fallbackOk = fallback && !looksGeneric(fallback) ? fallback : null;
19539
+ const title = truncateTitle(
19540
+ meaningfulSubjects[0] ?? fallbackOk ?? titleFromPaths(paths) ?? "Sideboard changes"
19541
+ );
19542
+ const commitMessage = meaningfulSubjects[0] ?? fallbackOk ?? titleFromPaths(paths) ?? `sideboard: ${title}`;
19543
+ const summaryLines = meaningfulSubjects.length > 0 ? meaningfulSubjects.slice(0, 12).map((s) => `- ${s}`) : paths.slice(0, 20).map((p) => `- \`${p}\``);
19544
+ const bodyParts = [
19545
+ "## Summary",
19546
+ summaryLines.length > 0 ? summaryLines.join("\n") : "- Changes landed via Sideboard.",
19547
+ ""
19548
+ ];
19549
+ if (statOut.trim()) {
19550
+ bodyParts.push("## Diff", "```", statOut.trim(), "```", "");
19551
+ }
19552
+ if (opts.sourceLabel) {
19553
+ bodyParts.push(`_Landed via Sideboard from ${opts.sourceLabel}_`);
19554
+ }
19555
+ return {
19556
+ title,
19557
+ body: bodyParts.join("\n").trim() + "\n",
19558
+ commitMessage: truncateTitle(commitMessage, 72)
19559
+ };
19560
+ }
19561
+ var GENERIC_TITLE_RE;
19562
+ var init_pr_metadata = __esm({
19563
+ "src/land/pr-metadata.ts"() {
19564
+ "use strict";
19565
+ init_run();
19566
+ GENERIC_TITLE_RE = /^(sideboard:|thread\/|wip\b|tmp\b|update\b|changes?\b|misc\b|stuff\b)/i;
19567
+ }
19568
+ });
19569
+
19508
19570
  // src/land/land.ts
19509
19571
  async function previewLand(thread) {
19510
19572
  if (thread.sourceIsFork) {
@@ -20035,13 +20097,14 @@ function formatRenameBranchDirective(thread, opts) {
20035
20097
  if (!isPlaceholderBranch(thread.branchName, thread.worktreePath)) return null;
20036
20098
  const dir = worktreeNameFromPath(thread.worktreePath);
20037
20099
  const ticket = thread.sourceType === "ticket" ? ticketSlugForBranch(thread.sourceRef ?? "") : null;
20038
- const example = ticket ? `\`feat/${ticket}-dark-mode\` or \`fix/${ticket}-panel-width\`` : "`fix/panel-width` or `feat/dark-mode`";
20100
+ const prefix = sanitizeGitBranchPrefix(opts?.branchPrefix);
20101
+ const example = examplePrefixedBranch(prefix, ticket);
20102
+ const shape = prefix ? `\`${prefix}/<ticket-or-task>-<short-kebab-description>\`` : "`<ticket-or-task>-<short-kebab-description>`";
20039
20103
  const lines = [
20040
20104
  "Branch naming (do this early in the turn):",
20041
20105
  `- Current branch \`${thread.branchName}\` is a temporary placeholder. The worktree folder \`${dir}\` is a stable nickname \u2014 do not rename or leave that directory.`,
20042
- `- Rename the git branch to a short kebab-case name that describes this task (what you are changing), e.g. ${example}:`,
20106
+ `- Rename the git branch to ${shape}, e.g. \`${example}\`:`,
20043
20107
  " `git branch -m <new-name>`",
20044
- "- Prefer Conventional Commits style prefixes when they fit (`fix/`, `feat/`, `chore/`, `docs/`).",
20045
20108
  ...ticket ? [
20046
20109
  `- Keep ticket \`${ticket}\` in the new branch name so the issue stays findable.`
20047
20110
  ] : [],
@@ -20247,6 +20310,7 @@ var init_instructions = __esm({
20247
20310
  import_node_fs50 = require("fs");
20248
20311
  import_node_path47 = require("path");
20249
20312
  init_git_auth_mode();
20313
+ init_branch_prefix();
20250
20314
  init_worktree_labels();
20251
20315
  init_detached_job_path();
20252
20316
  PR_GOAL_RE = /greptile|\bci\b.*\b(green|pass)|checks?\s+(pass|green)|until\s+.*\b(pass|green|merge|clean)|\bfix ci\b|watch[- ]fix[- ]push/i;
@@ -20948,7 +21012,6 @@ var init_orchestrator = __esm({
20948
21012
  init_spawn();
20949
21013
  init_agents();
20950
21014
  init_worktree();
20951
- init_pr_metadata();
20952
21015
  init_stack();
20953
21016
  init_agent_git_actions();
20954
21017
  init_pr_merge_archive();
@@ -21736,7 +21799,7 @@ var init_orchestrator = __esm({
21736
21799
  }
21737
21800
  const isBrightsy = fresh.agent === "brightsy";
21738
21801
  const isOrchestration = isOrchestratorThread(fresh);
21739
- const { autoRenameBranchEnabled: autoRenameBranchEnabled2, getGithubGitAuthMode: getGithubGitAuthMode2 } = await Promise.resolve().then(() => (init_app_settings(), app_settings_exports));
21802
+ const { autoRenameBranchEnabled: autoRenameBranchEnabled2, getGithubGitAuthMode: getGithubGitAuthMode2, gitBranchPrefixSetting: gitBranchPrefixSetting2 } = await Promise.resolve().then(() => (init_app_settings(), app_settings_exports));
21740
21803
  const gitAuthMode = getGithubGitAuthMode2();
21741
21804
  const worktreeDirective = isBrightsy || isOrchestration ? null : formatWorktreeDirective(fresh, {
21742
21805
  githubSlug: await resolveGithubRepoSlug(fresh.worktreePath).catch(
@@ -21772,9 +21835,16 @@ var init_orchestrator = __esm({
21772
21835
  }
21773
21836
  }
21774
21837
  const settings = loadWorkspaceSettings(fresh.worktreePath, fresh.repoPath);
21775
- const renameBranchDirective = !isBrightsy && !isOrchestration && autoRenameBranchEnabled2() ? formatRenameBranchDirective(fresh, {
21776
- customPrompt: settings?.prompts?.renameBranch
21777
- }) : null;
21838
+ let renameBranchDirective = null;
21839
+ if (!isBrightsy && !isOrchestration && autoRenameBranchEnabled2()) {
21840
+ const { resolveGitBranchPrefix: resolveGitBranchPrefix2 } = await Promise.resolve().then(() => (init_branch_prefix(), branch_prefix_exports));
21841
+ renameBranchDirective = formatRenameBranchDirective(fresh, {
21842
+ customPrompt: settings?.prompts?.renameBranch,
21843
+ branchPrefix: resolveGitBranchPrefix2({
21844
+ setting: gitBranchPrefixSetting2()
21845
+ })
21846
+ });
21847
+ }
21778
21848
  let seed = null;
21779
21849
  if (!fresh.sessionId) {
21780
21850
  const prior = fresh.messages.slice(0, -1);
@@ -22804,9 +22874,9 @@ var init_orchestrator = __esm({
22804
22874
  return tab;
22805
22875
  }
22806
22876
  /**
22807
- * Desktop git buttons + MCP `ask_git`.
22808
- * When the worktree is clean, push / open the PR here (HTTPS via `gh` if SSH
22809
- * is missing). When dirty, steer the worktree agent to commit first.
22877
+ * Desktop git buttons + MCP `ask_git`. Always queues the worktree agent
22878
+ * with the action prompt (same path as Resolve). Repository `[prompts]`
22879
+ * overrides apply when set.
22810
22880
  */
22811
22881
  async askGit(threadRef, action) {
22812
22882
  if (!AGENT_GIT_ACTIONS.includes(action)) {
@@ -22824,17 +22894,6 @@ var init_orchestrator = __esm({
22824
22894
  "No pull request linked. Ask the worktree agent to open a draft PR first (ask_git create-draft)."
22825
22895
  );
22826
22896
  }
22827
- if ((action === "commit-push" || action === "create-draft" || action === "create-web") && thread.worktreePath?.trim() && !await isDirty(thread.worktreePath)) {
22828
- try {
22829
- await this.pushAndMaybeOpenPr(thread, action);
22830
- } catch (err) {
22831
- const message = err instanceof Error ? err.message : String(err);
22832
- updateThread(thread.id, { lastError: message });
22833
- this.emit({ type: "error", threadId: thread.id, message });
22834
- throw err;
22835
- }
22836
- return this.requireThread(threadRef);
22837
- }
22838
22897
  let prBase;
22839
22898
  if (action === "resolve-conflicts") {
22840
22899
  try {
@@ -22843,42 +22902,18 @@ var init_orchestrator = __esm({
22843
22902
  } catch {
22844
22903
  }
22845
22904
  }
22846
- return this.send(threadRef, agentGitPrompt(action, { prBase }), {
22847
- followUp: resolveOrchChildFollowUp()
22848
- });
22849
- }
22850
- /** Push origin (gh HTTPS fallback) and create/update the PR when requested. */
22851
- async pushAndMaybeOpenPr(thread, action) {
22852
- const cwd = thread.worktreePath;
22853
- const branch = await currentBranch(cwd);
22854
- await pushBranch(cwd, branch);
22855
- if (action === "commit-push") return;
22856
- const base = await resolveDefaultBranch(thread.repoPath);
22857
- const meta = await suggestPrMetadata(cwd, {
22858
- base,
22859
- fallbackTitle: thread.prTitle ?? thread.title
22860
- });
22861
- const url = await createOrUpdatePr(cwd, {
22862
- title: meta.title,
22863
- body: meta.body,
22864
- base,
22865
- head: branch,
22866
- draft: action === "create-draft",
22867
- web: action === "create-web"
22868
- });
22869
- if (!url) return;
22870
- const patch = {
22871
- prUrl: url,
22872
- prTitle: meta.title,
22873
- prIsDraft: action === "create-draft"
22874
- };
22875
- try {
22876
- const fetched = await getPrMeta(cwd, url);
22877
- if (fetched?.title) patch.prTitle = fetched.title;
22878
- if (fetched) patch.prIsDraft = Boolean(fetched.isDraft);
22879
- } catch {
22880
- }
22881
- updateThread(thread.id, patch);
22905
+ const settings = loadWorkspaceSettings(thread.worktreePath, thread.repoPath);
22906
+ return this.send(
22907
+ threadRef,
22908
+ resolveSidebarGitPrompt(action, {
22909
+ prBase,
22910
+ createPr: settings?.prompts?.createPr,
22911
+ resolveMergeConflicts: settings?.prompts?.resolveMergeConflicts
22912
+ }),
22913
+ {
22914
+ followUp: resolveOrchChildFollowUp()
22915
+ }
22916
+ );
22882
22917
  }
22883
22918
  setThreadOptions(threadRef, patch) {
22884
22919
  const thread = this.requireThread(threadRef);
@@ -23494,6 +23529,7 @@ __export(index_exports, {
23494
23529
  estimateMessageChars: () => estimateMessageChars,
23495
23530
  estimateOccupancyTokens: () => estimateOccupancyTokens,
23496
23531
  estimateThreadChars: () => estimateThreadChars,
23532
+ examplePrefixedBranch: () => examplePrefixedBranch,
23497
23533
  expandComposerPrompt: () => expandComposerPrompt,
23498
23534
  extractBrightsyContextSummary: () => extractBrightsyContextSummary,
23499
23535
  extractGhErrorDetail: () => extractGhErrorDetail,
@@ -23606,6 +23642,7 @@ __export(index_exports, {
23606
23642
  ghHeadRef: () => ghHeadRef,
23607
23643
  ghRepoSelectArgs: () => ghRepoSelectArgs,
23608
23644
  git: () => git,
23645
+ gitBranchPrefixSetting: () => gitBranchPrefixSetting,
23609
23646
  githubAgentGitEnv: () => githubAgentGitEnv,
23610
23647
  githubHttpsInsteadOfEntries: () => githubHttpsInsteadOfEntries,
23611
23648
  githubRelationEditFlag: () => githubRelationEditFlag,
@@ -23854,6 +23891,7 @@ __export(index_exports, {
23854
23891
  resolveEffectiveIssueSource: () => resolveEffectiveIssueSource,
23855
23892
  resolveFilesToCopy: () => resolveFilesToCopy,
23856
23893
  resolveGhAuthToken: () => resolveGhAuthToken,
23894
+ resolveGitBranchPrefix: () => resolveGitBranchPrefix,
23857
23895
  resolveGitDirsForLockRecovery: () => resolveGitDirsForLockRecovery,
23858
23896
  resolveGitHubIssueRepo: () => resolveGitHubIssueRepo,
23859
23897
  resolveGithubAgentToken: () => resolveGithubAgentToken,
@@ -23876,6 +23914,7 @@ __export(index_exports, {
23876
23914
  resolveReviewGuidelines: () => resolveReviewGuidelines,
23877
23915
  resolveScheduleThreadId: () => resolveScheduleThreadId,
23878
23916
  resolveSendFollowUp: () => resolveSendFollowUp,
23917
+ resolveSidebarGitPrompt: () => resolveSidebarGitPrompt,
23879
23918
  resolveSlackListenMode: () => resolveSlackListenMode,
23880
23919
  resolveThreadDefaults: () => resolveThreadDefaults,
23881
23920
  resolveThreadEffort: () => resolveThreadEffort,
@@ -23895,6 +23934,7 @@ __export(index_exports, {
23895
23934
  runSlackRelayClient: () => runSlackRelayClient,
23896
23935
  runWorkspaceSetup: () => runWorkspaceSetup,
23897
23936
  sameWorktreePath: () => sameWorktreePath,
23937
+ sanitizeGitBranchPrefix: () => sanitizeGitBranchPrefix,
23898
23938
  sanitizeMcpServerName: () => sanitizeMcpServerName,
23899
23939
  saveAbleTimeConnection: () => saveAbleTimeConnection,
23900
23940
  saveAppSettings: () => saveAppSettings,
@@ -24040,6 +24080,7 @@ init_gh_errors();
24040
24080
  init_git_auth_mode();
24041
24081
  init_worktree();
24042
24082
  init_stack();
24083
+ init_branch_prefix();
24043
24084
  init_agent_git_actions();
24044
24085
 
24045
24086
  // src/integrations/github.ts
@@ -29183,7 +29224,7 @@ async function startMcpServer() {
29183
29224
  );
29184
29225
  server.tool(
29185
29226
  "ask_git",
29186
- "Commit & push, open a draft PR, mark ready for review, resolve conflicts, or merge \u2014 same actions as the desktop git buttons. When the worktree is clean, Sideboard pushes / opens the PR itself (HTTPS via `gh` even when origin is SSH / Settings \u2192 Git is SSH). When dirty, steers the worktree agent to commit (Settings \u2192 Follow-up, default interrupt now); 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. If this tool errors that the GraphQL/PR body is too long, the branch is already pushed \u2014 have the worktree agent retry `gh pr create --body-file` with a short description (GitHub limit 65,536 characters). Do not invent SSH/auth failures from that error.",
29227
+ "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.",
29187
29228
  {
29188
29229
  ref: import_zod9.z.string().describe("Worktree thread id/ref"),
29189
29230
  action: import_zod9.z.enum(AGENT_GIT_ACTIONS).describe(
@@ -32104,6 +32145,7 @@ init_outbound_watch();
32104
32145
  estimateMessageChars,
32105
32146
  estimateOccupancyTokens,
32106
32147
  estimateThreadChars,
32148
+ examplePrefixedBranch,
32107
32149
  expandComposerPrompt,
32108
32150
  extractBrightsyContextSummary,
32109
32151
  extractGhErrorDetail,
@@ -32216,6 +32258,7 @@ init_outbound_watch();
32216
32258
  ghHeadRef,
32217
32259
  ghRepoSelectArgs,
32218
32260
  git,
32261
+ gitBranchPrefixSetting,
32219
32262
  githubAgentGitEnv,
32220
32263
  githubHttpsInsteadOfEntries,
32221
32264
  githubRelationEditFlag,
@@ -32464,6 +32507,7 @@ init_outbound_watch();
32464
32507
  resolveEffectiveIssueSource,
32465
32508
  resolveFilesToCopy,
32466
32509
  resolveGhAuthToken,
32510
+ resolveGitBranchPrefix,
32467
32511
  resolveGitDirsForLockRecovery,
32468
32512
  resolveGitHubIssueRepo,
32469
32513
  resolveGithubAgentToken,
@@ -32486,6 +32530,7 @@ init_outbound_watch();
32486
32530
  resolveReviewGuidelines,
32487
32531
  resolveScheduleThreadId,
32488
32532
  resolveSendFollowUp,
32533
+ resolveSidebarGitPrompt,
32489
32534
  resolveSlackListenMode,
32490
32535
  resolveThreadDefaults,
32491
32536
  resolveThreadEffort,
@@ -32505,6 +32550,7 @@ init_outbound_watch();
32505
32550
  runSlackRelayClient,
32506
32551
  runWorkspaceSetup,
32507
32552
  sameWorktreePath,
32553
+ sanitizeGitBranchPrefix,
32508
32554
  sanitizeMcpServerName,
32509
32555
  saveAbleTimeConnection,
32510
32556
  saveAppSettings,