@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
@@ -269,9 +269,12 @@ function parseSettingsFile(path, source) {
269
269
  const prompts = {
270
270
  renameBranch: typeof promptsRaw.rename_branch === "string" ? promptsRaw.rename_branch : void 0,
271
271
  createPr: typeof promptsRaw.create_pr === "string" ? promptsRaw.create_pr : void 0,
272
- general: typeof promptsRaw.general === "string" ? promptsRaw.general : void 0
272
+ general: typeof promptsRaw.general === "string" ? promptsRaw.general : void 0,
273
+ resolveMergeConflicts: typeof promptsRaw.resolve_merge_conflicts === "string" ? promptsRaw.resolve_merge_conflicts : void 0
273
274
  };
274
- const hasPrompts = Boolean(prompts.renameBranch || prompts.createPr || prompts.general);
275
+ const hasPrompts = Boolean(
276
+ prompts.renameBranch || prompts.createPr || prompts.general || prompts.resolveMergeConflicts
277
+ );
275
278
  return {
276
279
  source,
277
280
  setup,
@@ -297,9 +300,12 @@ function mergeSettings(base, overlay) {
297
300
  const prompts = {
298
301
  renameBranch: overlay.prompts?.renameBranch ?? base.prompts?.renameBranch,
299
302
  createPr: overlay.prompts?.createPr ?? base.prompts?.createPr,
300
- general: overlay.prompts?.general ?? base.prompts?.general
303
+ general: overlay.prompts?.general ?? base.prompts?.general,
304
+ resolveMergeConflicts: overlay.prompts?.resolveMergeConflicts ?? base.prompts?.resolveMergeConflicts
301
305
  };
302
- const hasPrompts = Boolean(prompts.renameBranch || prompts.createPr || prompts.general);
306
+ const hasPrompts = Boolean(
307
+ prompts.renameBranch || prompts.createPr || prompts.general || prompts.resolveMergeConflicts
308
+ );
303
309
  return {
304
310
  source: overlay.source,
305
311
  setup: overlay.setup ?? base.setup,
@@ -3434,6 +3440,32 @@ var init_secret_vault = __esm({
3434
3440
  }
3435
3441
  });
3436
3442
 
3443
+ // src/git/branch-prefix.ts
3444
+ var branch_prefix_exports = {};
3445
+ __export(branch_prefix_exports, {
3446
+ examplePrefixedBranch: () => examplePrefixedBranch,
3447
+ resolveGitBranchPrefix: () => resolveGitBranchPrefix,
3448
+ sanitizeGitBranchPrefix: () => sanitizeGitBranchPrefix
3449
+ });
3450
+ function sanitizeGitBranchPrefix(raw) {
3451
+ const first = (raw ?? "").trim().replace(/^\/+|\/+$/g, "").split("/")[0] ?? "";
3452
+ const slug = first.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 39);
3453
+ return slug || null;
3454
+ }
3455
+ function resolveGitBranchPrefix(opts) {
3456
+ return sanitizeGitBranchPrefix(opts.setting);
3457
+ }
3458
+ function examplePrefixedBranch(prefix, ticket) {
3459
+ const head = sanitizeGitBranchPrefix(prefix);
3460
+ const slug = ticket?.trim() ? `${ticket.trim()}-eng-fix-thing` : "bb-1234-eng-fix-thing";
3461
+ return head ? `${head}/${slug}` : slug;
3462
+ }
3463
+ var init_branch_prefix = __esm({
3464
+ "src/git/branch-prefix.ts"() {
3465
+ "use strict";
3466
+ }
3467
+ });
3468
+
3437
3469
  // src/store/issue-source-labels.ts
3438
3470
  function issueSourceLabel(source) {
3439
3471
  if (source && source in ISSUE_SOURCE_LABELS) {
@@ -3502,6 +3534,7 @@ __export(app_settings_exports, {
3502
3534
  getGithubPat: () => getGithubPat,
3503
3535
  getIssueSource: () => getIssueSource,
3504
3536
  getLinearApiKey: () => getLinearApiKey,
3537
+ gitBranchPrefixSetting: () => gitBranchPrefixSetting,
3505
3538
  harnessEnvKey: () => harnessEnvKey,
3506
3539
  isAbleTimeConnected: () => isAbleTimeConnected,
3507
3540
  isIssueSourceConnected: () => isIssueSourceConnected,
@@ -3877,6 +3910,10 @@ function normalizeAdvanced(raw) {
3877
3910
  if (typeof source.autoRenameBranch === "boolean") {
3878
3911
  out.autoRenameBranch = source.autoRenameBranch;
3879
3912
  }
3913
+ if (typeof source.branchPrefix === "string") {
3914
+ const prefix = sanitizeGitBranchPrefix(source.branchPrefix);
3915
+ if (prefix) out.branchPrefix = prefix;
3916
+ }
3880
3917
  if (typeof source.autoRunAfterSetup === "boolean") {
3881
3918
  out.autoRunAfterSetup = source.autoRunAfterSetup;
3882
3919
  }
@@ -4730,6 +4767,13 @@ function updateAdvancedSettings(patch) {
4730
4767
  if (typeof patch.autoRenameBranch === "boolean") {
4731
4768
  advanced.autoRenameBranch = patch.autoRenameBranch;
4732
4769
  }
4770
+ if (patch.branchPrefix === null || patch.branchPrefix === "") {
4771
+ delete advanced.branchPrefix;
4772
+ } else if (typeof patch.branchPrefix === "string") {
4773
+ const prefix = sanitizeGitBranchPrefix(patch.branchPrefix);
4774
+ if (prefix) advanced.branchPrefix = prefix;
4775
+ else delete advanced.branchPrefix;
4776
+ }
4733
4777
  if (typeof patch.autoRunAfterSetup === "boolean") {
4734
4778
  advanced.autoRunAfterSetup = patch.autoRunAfterSetup;
4735
4779
  }
@@ -4790,6 +4834,9 @@ function updateAdvancedSettings(patch) {
4790
4834
  function autoRenameBranchEnabled(settings = loadAppSettings()) {
4791
4835
  return settings.advanced.autoRenameBranch !== false;
4792
4836
  }
4837
+ function gitBranchPrefixSetting(settings = loadAppSettings()) {
4838
+ return sanitizeGitBranchPrefix(settings.advanced.branchPrefix);
4839
+ }
4793
4840
  function autoRunAfterSetupEnabled(settings = loadAppSettings()) {
4794
4841
  return Boolean(settings.advanced.autoRunAfterSetup);
4795
4842
  }
@@ -4935,6 +4982,7 @@ var init_app_settings = __esm({
4935
4982
  init_paths();
4936
4983
  init_private_file();
4937
4984
  init_secret_vault();
4985
+ init_branch_prefix();
4938
4986
  init_issue_source_labels();
4939
4987
  init_account_profile();
4940
4988
  HARNESS_ENV_KEYS = {
@@ -7984,7 +8032,7 @@ var init_coordinator_prompt = __esm({
7984
8032
  "- get_diff \u2014 compact diff summary",
7985
8033
  "- 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.",
7986
8034
  '- 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.',
7987
- "- 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.",
8035
+ "- 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.",
7988
8036
  '- 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.',
7989
8037
  '- 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.',
7990
8038
  "Process guides:",
@@ -12431,87 +12479,6 @@ var init_spawn = __esm({
12431
12479
  }
12432
12480
  });
12433
12481
 
12434
- // src/land/pr-metadata.ts
12435
- function looksGeneric(title) {
12436
- const t = title.trim();
12437
- if (!t) return true;
12438
- if (t.length < 4) return true;
12439
- if (GENERIC_TITLE_RE.test(t)) return true;
12440
- if (!/[\s:/\-_]/.test(t) && t.length <= 24) return true;
12441
- return false;
12442
- }
12443
- function truncateTitle(title, max = 72) {
12444
- const t = title.trim().replace(/\s+/g, " ");
12445
- if (t.length <= max) return t;
12446
- return `${t.slice(0, max - 1).trimEnd()}\u2026`;
12447
- }
12448
- function titleFromPaths(paths) {
12449
- if (paths.length === 0) return null;
12450
- const top = paths.slice(0, 3).map((p) => p.replace(/\/$/, ""));
12451
- if (paths.length === 1) return truncateTitle(`Update ${top[0]}`);
12452
- if (paths.length <= 3) return truncateTitle(`Update ${top.join(", ")}`);
12453
- return truncateTitle(`Update ${top[0]} and ${paths.length - 1} more files`);
12454
- }
12455
- async function suggestPrMetadata(worktreePath, opts) {
12456
- const [{ stdout: logOut }, { stdout: nameStatus }, { stdout: statOut }] = await Promise.all([
12457
- git(["log", "--format=%s", `${opts.base}..HEAD`], worktreePath, {
12458
- reject: false
12459
- }),
12460
- git(["diff", "--name-only", `${opts.base}...HEAD`], worktreePath, {
12461
- reject: false
12462
- }),
12463
- git(["diff", "--stat", `${opts.base}...HEAD`], worktreePath, {
12464
- reject: false
12465
- })
12466
- ]);
12467
- const { stdout: dirtyNames } = await git(["diff", "--name-only", "HEAD"], worktreePath, {
12468
- reject: false
12469
- });
12470
- const { stdout: untracked } = await git(
12471
- ["ls-files", "--others", "--exclude-standard"],
12472
- worktreePath,
12473
- { reject: false }
12474
- );
12475
- const subjects = logOut.split("\n").map((s) => s.trim()).filter(Boolean);
12476
- const paths = [
12477
- ...new Set(
12478
- [...nameStatus.split("\n"), ...dirtyNames.split("\n"), ...untracked.split("\n")].map((p) => p.trim()).filter(Boolean)
12479
- )
12480
- ].sort((a, b) => a.localeCompare(b));
12481
- const meaningfulSubjects = subjects.filter((s) => !looksGeneric(s));
12482
- const fallback = opts.fallbackTitle?.trim() || null;
12483
- const fallbackOk = fallback && !looksGeneric(fallback) ? fallback : null;
12484
- const title = truncateTitle(
12485
- meaningfulSubjects[0] ?? fallbackOk ?? titleFromPaths(paths) ?? "Sideboard changes"
12486
- );
12487
- const commitMessage = meaningfulSubjects[0] ?? fallbackOk ?? titleFromPaths(paths) ?? `sideboard: ${title}`;
12488
- const summaryLines = meaningfulSubjects.length > 0 ? meaningfulSubjects.slice(0, 12).map((s) => `- ${s}`) : paths.slice(0, 20).map((p) => `- \`${p}\``);
12489
- const bodyParts = [
12490
- "## Summary",
12491
- summaryLines.length > 0 ? summaryLines.join("\n") : "- Changes landed via Sideboard.",
12492
- ""
12493
- ];
12494
- if (statOut.trim()) {
12495
- bodyParts.push("## Diff", "```", statOut.trim(), "```", "");
12496
- }
12497
- if (opts.sourceLabel) {
12498
- bodyParts.push(`_Landed via Sideboard from ${opts.sourceLabel}_`);
12499
- }
12500
- return {
12501
- title,
12502
- body: bodyParts.join("\n").trim() + "\n",
12503
- commitMessage: truncateTitle(commitMessage, 72)
12504
- };
12505
- }
12506
- var GENERIC_TITLE_RE;
12507
- var init_pr_metadata = __esm({
12508
- "src/land/pr-metadata.ts"() {
12509
- "use strict";
12510
- init_run();
12511
- GENERIC_TITLE_RE = /^(sideboard:|thread\/|wip\b|tmp\b|update\b|changes?\b|misc\b|stuff\b)/i;
12512
- }
12513
- });
12514
-
12515
12482
  // src/git/agent-git-actions.ts
12516
12483
  function agentGitPrompt(action, opts) {
12517
12484
  switch (action) {
@@ -12532,6 +12499,19 @@ function agentGitPrompt(action, opts) {
12532
12499
  return "Merge PR.";
12533
12500
  }
12534
12501
  }
12502
+ function resolveSidebarGitPrompt(action, opts) {
12503
+ if (action === "create-pr" || action === "create-draft" || action === "create-web") {
12504
+ const custom = opts?.createPr?.trim();
12505
+ if (custom) return custom;
12506
+ return agentGitPrompt(action === "create-pr" ? "create-draft" : action);
12507
+ }
12508
+ if (action === "resolve-conflicts") {
12509
+ const custom = opts?.resolveMergeConflicts?.trim();
12510
+ if (custom) return custom;
12511
+ return agentGitPrompt("resolve-conflicts", opts);
12512
+ }
12513
+ return agentGitPrompt(action, opts);
12514
+ }
12535
12515
  function gitActionExpansion(action, base) {
12536
12516
  switch (action) {
12537
12517
  case "commit-push":
@@ -17785,6 +17765,87 @@ var init_diff = __esm({
17785
17765
  }
17786
17766
  });
17787
17767
 
17768
+ // src/land/pr-metadata.ts
17769
+ function looksGeneric(title) {
17770
+ const t = title.trim();
17771
+ if (!t) return true;
17772
+ if (t.length < 4) return true;
17773
+ if (GENERIC_TITLE_RE.test(t)) return true;
17774
+ if (!/[\s:/\-_]/.test(t) && t.length <= 24) return true;
17775
+ return false;
17776
+ }
17777
+ function truncateTitle(title, max = 72) {
17778
+ const t = title.trim().replace(/\s+/g, " ");
17779
+ if (t.length <= max) return t;
17780
+ return `${t.slice(0, max - 1).trimEnd()}\u2026`;
17781
+ }
17782
+ function titleFromPaths(paths) {
17783
+ if (paths.length === 0) return null;
17784
+ const top = paths.slice(0, 3).map((p) => p.replace(/\/$/, ""));
17785
+ if (paths.length === 1) return truncateTitle(`Update ${top[0]}`);
17786
+ if (paths.length <= 3) return truncateTitle(`Update ${top.join(", ")}`);
17787
+ return truncateTitle(`Update ${top[0]} and ${paths.length - 1} more files`);
17788
+ }
17789
+ async function suggestPrMetadata(worktreePath, opts) {
17790
+ const [{ stdout: logOut }, { stdout: nameStatus }, { stdout: statOut }] = await Promise.all([
17791
+ git(["log", "--format=%s", `${opts.base}..HEAD`], worktreePath, {
17792
+ reject: false
17793
+ }),
17794
+ git(["diff", "--name-only", `${opts.base}...HEAD`], worktreePath, {
17795
+ reject: false
17796
+ }),
17797
+ git(["diff", "--stat", `${opts.base}...HEAD`], worktreePath, {
17798
+ reject: false
17799
+ })
17800
+ ]);
17801
+ const { stdout: dirtyNames } = await git(["diff", "--name-only", "HEAD"], worktreePath, {
17802
+ reject: false
17803
+ });
17804
+ const { stdout: untracked } = await git(
17805
+ ["ls-files", "--others", "--exclude-standard"],
17806
+ worktreePath,
17807
+ { reject: false }
17808
+ );
17809
+ const subjects = logOut.split("\n").map((s) => s.trim()).filter(Boolean);
17810
+ const paths = [
17811
+ ...new Set(
17812
+ [...nameStatus.split("\n"), ...dirtyNames.split("\n"), ...untracked.split("\n")].map((p) => p.trim()).filter(Boolean)
17813
+ )
17814
+ ].sort((a, b) => a.localeCompare(b));
17815
+ const meaningfulSubjects = subjects.filter((s) => !looksGeneric(s));
17816
+ const fallback = opts.fallbackTitle?.trim() || null;
17817
+ const fallbackOk = fallback && !looksGeneric(fallback) ? fallback : null;
17818
+ const title = truncateTitle(
17819
+ meaningfulSubjects[0] ?? fallbackOk ?? titleFromPaths(paths) ?? "Sideboard changes"
17820
+ );
17821
+ const commitMessage = meaningfulSubjects[0] ?? fallbackOk ?? titleFromPaths(paths) ?? `sideboard: ${title}`;
17822
+ const summaryLines = meaningfulSubjects.length > 0 ? meaningfulSubjects.slice(0, 12).map((s) => `- ${s}`) : paths.slice(0, 20).map((p) => `- \`${p}\``);
17823
+ const bodyParts = [
17824
+ "## Summary",
17825
+ summaryLines.length > 0 ? summaryLines.join("\n") : "- Changes landed via Sideboard.",
17826
+ ""
17827
+ ];
17828
+ if (statOut.trim()) {
17829
+ bodyParts.push("## Diff", "```", statOut.trim(), "```", "");
17830
+ }
17831
+ if (opts.sourceLabel) {
17832
+ bodyParts.push(`_Landed via Sideboard from ${opts.sourceLabel}_`);
17833
+ }
17834
+ return {
17835
+ title,
17836
+ body: bodyParts.join("\n").trim() + "\n",
17837
+ commitMessage: truncateTitle(commitMessage, 72)
17838
+ };
17839
+ }
17840
+ var GENERIC_TITLE_RE;
17841
+ var init_pr_metadata = __esm({
17842
+ "src/land/pr-metadata.ts"() {
17843
+ "use strict";
17844
+ init_run();
17845
+ GENERIC_TITLE_RE = /^(sideboard:|thread\/|wip\b|tmp\b|update\b|changes?\b|misc\b|stuff\b)/i;
17846
+ }
17847
+ });
17848
+
17788
17849
  // src/land/land.ts
17789
17850
  async function previewLand(thread) {
17790
17851
  if (thread.sourceIsFork) {
@@ -18315,13 +18376,14 @@ function formatRenameBranchDirective(thread, opts) {
18315
18376
  if (!isPlaceholderBranch(thread.branchName, thread.worktreePath)) return null;
18316
18377
  const dir = worktreeNameFromPath(thread.worktreePath);
18317
18378
  const ticket = thread.sourceType === "ticket" ? ticketSlugForBranch(thread.sourceRef ?? "") : null;
18318
- const example = ticket ? `\`feat/${ticket}-dark-mode\` or \`fix/${ticket}-panel-width\`` : "`fix/panel-width` or `feat/dark-mode`";
18379
+ const prefix = sanitizeGitBranchPrefix(opts?.branchPrefix);
18380
+ const example = examplePrefixedBranch(prefix, ticket);
18381
+ const shape = prefix ? `\`${prefix}/<ticket-or-task>-<short-kebab-description>\`` : "`<ticket-or-task>-<short-kebab-description>`";
18319
18382
  const lines = [
18320
18383
  "Branch naming (do this early in the turn):",
18321
18384
  `- Current branch \`${thread.branchName}\` is a temporary placeholder. The worktree folder \`${dir}\` is a stable nickname \u2014 do not rename or leave that directory.`,
18322
- `- Rename the git branch to a short kebab-case name that describes this task (what you are changing), e.g. ${example}:`,
18385
+ `- Rename the git branch to ${shape}, e.g. \`${example}\`:`,
18323
18386
  " `git branch -m <new-name>`",
18324
- "- Prefer Conventional Commits style prefixes when they fit (`fix/`, `feat/`, `chore/`, `docs/`).",
18325
18387
  ...ticket ? [
18326
18388
  `- Keep ticket \`${ticket}\` in the new branch name so the issue stays findable.`
18327
18389
  ] : [],
@@ -18505,6 +18567,7 @@ var init_instructions = __esm({
18505
18567
  import_node_fs48 = require("fs");
18506
18568
  import_node_path45 = require("path");
18507
18569
  init_git_auth_mode();
18570
+ init_branch_prefix();
18508
18571
  init_worktree_labels();
18509
18572
  init_detached_job_path();
18510
18573
  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;
@@ -19576,7 +19639,6 @@ var init_orchestrator = __esm({
19576
19639
  init_spawn();
19577
19640
  init_agents();
19578
19641
  init_worktree();
19579
- init_pr_metadata();
19580
19642
  init_stack();
19581
19643
  init_agent_git_actions();
19582
19644
  init_pr_merge_archive();
@@ -20364,7 +20426,7 @@ var init_orchestrator = __esm({
20364
20426
  }
20365
20427
  const isBrightsy = fresh.agent === "brightsy";
20366
20428
  const isOrchestration = isOrchestratorThread(fresh);
20367
- const { autoRenameBranchEnabled: autoRenameBranchEnabled2, getGithubGitAuthMode: getGithubGitAuthMode2 } = await Promise.resolve().then(() => (init_app_settings(), app_settings_exports));
20429
+ const { autoRenameBranchEnabled: autoRenameBranchEnabled2, getGithubGitAuthMode: getGithubGitAuthMode2, gitBranchPrefixSetting: gitBranchPrefixSetting2 } = await Promise.resolve().then(() => (init_app_settings(), app_settings_exports));
20368
20430
  const gitAuthMode = getGithubGitAuthMode2();
20369
20431
  const worktreeDirective = isBrightsy || isOrchestration ? null : formatWorktreeDirective(fresh, {
20370
20432
  githubSlug: await resolveGithubRepoSlug(fresh.worktreePath).catch(
@@ -20400,9 +20462,16 @@ var init_orchestrator = __esm({
20400
20462
  }
20401
20463
  }
20402
20464
  const settings = loadWorkspaceSettings(fresh.worktreePath, fresh.repoPath);
20403
- const renameBranchDirective = !isBrightsy && !isOrchestration && autoRenameBranchEnabled2() ? formatRenameBranchDirective(fresh, {
20404
- customPrompt: settings?.prompts?.renameBranch
20405
- }) : null;
20465
+ let renameBranchDirective = null;
20466
+ if (!isBrightsy && !isOrchestration && autoRenameBranchEnabled2()) {
20467
+ const { resolveGitBranchPrefix: resolveGitBranchPrefix2 } = await Promise.resolve().then(() => (init_branch_prefix(), branch_prefix_exports));
20468
+ renameBranchDirective = formatRenameBranchDirective(fresh, {
20469
+ customPrompt: settings?.prompts?.renameBranch,
20470
+ branchPrefix: resolveGitBranchPrefix2({
20471
+ setting: gitBranchPrefixSetting2()
20472
+ })
20473
+ });
20474
+ }
20406
20475
  let seed = null;
20407
20476
  if (!fresh.sessionId) {
20408
20477
  const prior = fresh.messages.slice(0, -1);
@@ -21432,9 +21501,9 @@ var init_orchestrator = __esm({
21432
21501
  return tab;
21433
21502
  }
21434
21503
  /**
21435
- * Desktop git buttons + MCP `ask_git`.
21436
- * When the worktree is clean, push / open the PR here (HTTPS via `gh` if SSH
21437
- * is missing). When dirty, steer the worktree agent to commit first.
21504
+ * Desktop git buttons + MCP `ask_git`. Always queues the worktree agent
21505
+ * with the action prompt (same path as Resolve). Repository `[prompts]`
21506
+ * overrides apply when set.
21438
21507
  */
21439
21508
  async askGit(threadRef, action) {
21440
21509
  if (!AGENT_GIT_ACTIONS.includes(action)) {
@@ -21452,17 +21521,6 @@ var init_orchestrator = __esm({
21452
21521
  "No pull request linked. Ask the worktree agent to open a draft PR first (ask_git create-draft)."
21453
21522
  );
21454
21523
  }
21455
- if ((action === "commit-push" || action === "create-draft" || action === "create-web") && thread.worktreePath?.trim() && !await isDirty(thread.worktreePath)) {
21456
- try {
21457
- await this.pushAndMaybeOpenPr(thread, action);
21458
- } catch (err) {
21459
- const message = err instanceof Error ? err.message : String(err);
21460
- updateThread(thread.id, { lastError: message });
21461
- this.emit({ type: "error", threadId: thread.id, message });
21462
- throw err;
21463
- }
21464
- return this.requireThread(threadRef);
21465
- }
21466
21524
  let prBase;
21467
21525
  if (action === "resolve-conflicts") {
21468
21526
  try {
@@ -21471,42 +21529,18 @@ var init_orchestrator = __esm({
21471
21529
  } catch {
21472
21530
  }
21473
21531
  }
21474
- return this.send(threadRef, agentGitPrompt(action, { prBase }), {
21475
- followUp: resolveOrchChildFollowUp()
21476
- });
21477
- }
21478
- /** Push origin (gh HTTPS fallback) and create/update the PR when requested. */
21479
- async pushAndMaybeOpenPr(thread, action) {
21480
- const cwd = thread.worktreePath;
21481
- const branch = await currentBranch(cwd);
21482
- await pushBranch(cwd, branch);
21483
- if (action === "commit-push") return;
21484
- const base = await resolveDefaultBranch(thread.repoPath);
21485
- const meta = await suggestPrMetadata(cwd, {
21486
- base,
21487
- fallbackTitle: thread.prTitle ?? thread.title
21488
- });
21489
- const url = await createOrUpdatePr(cwd, {
21490
- title: meta.title,
21491
- body: meta.body,
21492
- base,
21493
- head: branch,
21494
- draft: action === "create-draft",
21495
- web: action === "create-web"
21496
- });
21497
- if (!url) return;
21498
- const patch = {
21499
- prUrl: url,
21500
- prTitle: meta.title,
21501
- prIsDraft: action === "create-draft"
21502
- };
21503
- try {
21504
- const fetched = await getPrMeta(cwd, url);
21505
- if (fetched?.title) patch.prTitle = fetched.title;
21506
- if (fetched) patch.prIsDraft = Boolean(fetched.isDraft);
21507
- } catch {
21508
- }
21509
- updateThread(thread.id, patch);
21532
+ const settings = loadWorkspaceSettings(thread.worktreePath, thread.repoPath);
21533
+ return this.send(
21534
+ threadRef,
21535
+ resolveSidebarGitPrompt(action, {
21536
+ prBase,
21537
+ createPr: settings?.prompts?.createPr,
21538
+ resolveMergeConflicts: settings?.prompts?.resolveMergeConflicts
21539
+ }),
21540
+ {
21541
+ followUp: resolveOrchChildFollowUp()
21542
+ }
21543
+ );
21510
21544
  }
21511
21545
  setThreadOptions(threadRef, patch) {
21512
21546
  const thread = this.requireThread(threadRef);
@@ -26235,7 +26269,7 @@ async function startMcpServer() {
26235
26269
  );
26236
26270
  server.tool(
26237
26271
  "ask_git",
26238
- "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.",
26272
+ "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.",
26239
26273
  {
26240
26274
  ref: import_zod9.z.string().describe("Worktree thread id/ref"),
26241
26275
  action: import_zod9.z.enum(AGENT_GIT_ACTIONS).describe(
@@ -38,7 +38,8 @@ import {
38
38
  syncBoardPins,
39
39
  updateSchedule,
40
40
  waitForDetachedJob
41
- } from "../chunk-FZ3G5TXM.js";
41
+ } from "../chunk-VRPPFKAO.js";
42
+ import "../chunk-YWI3IBP2.js";
42
43
  import {
43
44
  applySystemCaEnv,
44
45
  isCursorApiKeyConfigured,
@@ -47,8 +48,13 @@ import {
47
48
  materializeSystemCaBundle,
48
49
  sideboardMcpProfile,
49
50
  trustSystemCertificates
50
- } from "../chunk-WHD4JNTN.js";
51
- import "../chunk-HVIEYTUK.js";
51
+ } from "../chunk-6H2XPPPR.js";
52
+ import {
53
+ GLOBAL_WORKSPACE_ID,
54
+ isCloudCoordinatorThread,
55
+ listWorkspaces
56
+ } from "../chunk-6YTI2HBG.js";
57
+ import "../chunk-3OHR73VK.js";
52
58
  import "../chunk-Y4MIXGPA.js";
53
59
  import {
54
60
  commentAbleTimeTask,
@@ -61,17 +67,11 @@ import {
61
67
  searchAbleTimeTasks,
62
68
  toAbleTimeIssueInfo,
63
69
  updateAbleTimeTask
64
- } from "../chunk-UBQIELFS.js";
70
+ } from "../chunk-KTHRWHA3.js";
65
71
  import {
66
72
  httpFetch
67
73
  } from "../chunk-VKQGAWHY.js";
68
74
  import "../chunk-JFOPOMK7.js";
69
- import "../chunk-OCQPTUR7.js";
70
- import {
71
- GLOBAL_WORKSPACE_ID,
72
- isCloudCoordinatorThread,
73
- listWorkspaces
74
- } from "../chunk-7NSXGL6L.js";
75
75
  import {
76
76
  canonicalizeRepoPath,
77
77
  formatGhLandError,
@@ -82,9 +82,9 @@ import {
82
82
  resolveListPrsOptions,
83
83
  resolveRepoRoot,
84
84
  warmGithubAgentAuth
85
- } from "../chunk-XBLN52RZ.js";
85
+ } from "../chunk-DP4TKAMZ.js";
86
86
  import "../chunk-D6JWYJ4P.js";
87
- import "../chunk-GOZ6T3AG.js";
87
+ import "../chunk-CJFBSQIB.js";
88
88
  import "../chunk-SYFVUF2C.js";
89
89
  import {
90
90
  gh,
@@ -103,12 +103,13 @@ import {
103
103
  resolveViewerProfileForRepo,
104
104
  saveLinearOAuth,
105
105
  writeViewerContext
106
- } from "../chunk-OKZFTOM2.js";
106
+ } from "../chunk-AH6WIBLY.js";
107
107
  import "../chunk-NSTQ6QKD.js";
108
108
  import "../chunk-KGZBYWZ3.js";
109
109
  import {
110
110
  appDataDir
111
- } from "../chunk-JTJHOYCU.js";
111
+ } from "../chunk-EQNRWDLN.js";
112
+ import "../chunk-FJZWM2ON.js";
112
113
 
113
114
  // src/mcp/server.ts
114
115
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -3877,7 +3878,7 @@ async function startMcpServer() {
3877
3878
  );
3878
3879
  }
3879
3880
  try {
3880
- const { maxConcurrentAgents } = await import("../app-settings-74HBAAH7.js");
3881
+ const { maxConcurrentAgents } = await import("../app-settings-J775CYOO.js");
3881
3882
  orch.setMaxConcurrent(maxConcurrentAgents());
3882
3883
  } catch {
3883
3884
  }
@@ -4191,8 +4192,8 @@ async function startMcpServer() {
4191
4192
  registerSlackTools(server);
4192
4193
  registerConnectedIssueVendorTools(server);
4193
4194
  registerScheduleTools(server);
4194
- const { getCaffeinateHold, setCaffeinateHold } = await import("../caffeinate-hold-DBKMRZZO.js");
4195
- const { resolveNewThreadOptions, resolveThreadDefaults } = await import("../app-settings-74HBAAH7.js");
4195
+ const { getCaffeinateHold, setCaffeinateHold } = await import("../caffeinate-hold-OK4JJ5PP.js");
4196
+ const { resolveNewThreadOptions, resolveThreadDefaults } = await import("../app-settings-J775CYOO.js");
4196
4197
  const accountDefaults = resolveThreadDefaults();
4197
4198
  const accountDefaultsHint = `Account defaults: agent=${accountDefaults.agent}, model=${accountDefaults.model?.trim() || "Auto"}, effort=${accountDefaults.effort}`;
4198
4199
  server.tool(
@@ -4535,7 +4536,7 @@ async function startMcpServer() {
4535
4536
  );
4536
4537
  server.tool(
4537
4538
  "ask_git",
4538
- "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.",
4539
+ "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.",
4539
4540
  {
4540
4541
  ref: z9.string().describe("Worktree thread id/ref"),
4541
4542
  action: z9.enum(AGENT_GIT_ACTIONS).describe(
@@ -9,23 +9,24 @@ import {
9
9
  resolveSendFollowUp,
10
10
  startOrchestration,
11
11
  waitForPidExit
12
- } from "./chunk-FZ3G5TXM.js";
13
- import "./chunk-WHD4JNTN.js";
14
- import "./chunk-HVIEYTUK.js";
12
+ } from "./chunk-VRPPFKAO.js";
13
+ import "./chunk-YWI3IBP2.js";
14
+ import "./chunk-6H2XPPPR.js";
15
+ import "./chunk-6YTI2HBG.js";
16
+ import "./chunk-3OHR73VK.js";
15
17
  import "./chunk-Y4MIXGPA.js";
16
18
  import "./chunk-VKQGAWHY.js";
17
19
  import "./chunk-JFOPOMK7.js";
18
- import "./chunk-OCQPTUR7.js";
19
- import "./chunk-7NSXGL6L.js";
20
- import "./chunk-XBLN52RZ.js";
20
+ import "./chunk-DP4TKAMZ.js";
21
21
  import "./chunk-D6JWYJ4P.js";
22
- import "./chunk-GOZ6T3AG.js";
22
+ import "./chunk-CJFBSQIB.js";
23
23
  import "./chunk-SYFVUF2C.js";
24
24
  import "./chunk-FNQNJOHY.js";
25
- import "./chunk-OKZFTOM2.js";
25
+ import "./chunk-AH6WIBLY.js";
26
26
  import "./chunk-NSTQ6QKD.js";
27
27
  import "./chunk-KGZBYWZ3.js";
28
- import "./chunk-JTJHOYCU.js";
28
+ import "./chunk-EQNRWDLN.js";
29
+ import "./chunk-FJZWM2ON.js";
29
30
  export {
30
31
  LIVE_TURN_SPAWN_GRACE_MS,
31
32
  Orchestrator,
@@ -7,25 +7,26 @@ import {
7
7
  resolveSendFollowUp,
8
8
  startOrchestration,
9
9
  waitForPidExit
10
- } from "./chunk-NF6ENGHS.js";
10
+ } from "./chunk-NY366OEZ.js";
11
11
  import "./chunk-DJU54RYW.js";
12
- import "./chunk-TWSCTJCR.js";
13
- import "./chunk-VV7UT3E2.js";
14
- import "./chunk-WXKXSZN4.js";
12
+ import "./chunk-2X6RGKLI.js";
13
+ import "./chunk-HZTHWU6A.js";
14
+ import "./chunk-S4QCBBXS.js";
15
15
  import "./chunk-6K64FJDD.js";
16
- import "./chunk-IEEAVWAP.js";
16
+ import "./chunk-QQKDGPUI.js";
17
+ import "./chunk-SALM35KD.js";
17
18
  import "./chunk-AN3THU7F.js";
18
19
  import "./chunk-N7WZ6NIN.js";
19
20
  import "./chunk-AXQ4ZWHK.js";
20
- import "./chunk-FY64F2ZQ.js";
21
- import "./chunk-J7LQVLFQ.js";
21
+ import "./chunk-NLHFAMC2.js";
22
+ import "./chunk-S5GLSRLP.js";
22
23
  import "./chunk-4TR3HZFT.js";
23
- import "./chunk-AO35MGOE.js";
24
+ import "./chunk-WDRMZKVB.js";
25
+ import "./chunk-4TYXWPAJ.js";
24
26
  import "./chunk-JT7R45JB.js";
25
- import "./chunk-TCQAFTSV.js";
27
+ import "./chunk-KJBYIIJU.js";
26
28
  import "./chunk-77WWLBCI.js";
27
- import "./chunk-EFVFB2AH.js";
28
- import "./chunk-QQKDGPUI.js";
29
+ import "./chunk-WFUDY6PC.js";
29
30
  import "./chunk-UFHNSFAS.js";
30
31
  export {
31
32
  LIVE_TURN_SPAWN_GRACE_MS,
@@ -13,7 +13,7 @@ import {
13
13
  threadLockPath,
14
14
  threadsDir,
15
15
  worktreesRoot
16
- } from "./chunk-JTJHOYCU.js";
16
+ } from "./chunk-EQNRWDLN.js";
17
17
  export {
18
18
  appDataDir,
19
19
  globalAgentCwd,
@@ -11,7 +11,7 @@ import {
11
11
  threadLockPath,
12
12
  threadsDir,
13
13
  worktreesRoot
14
- } from "./chunk-EFVFB2AH.js";
14
+ } from "./chunk-WFUDY6PC.js";
15
15
  export {
16
16
  appDataDir,
17
17
  globalAgentCwd,