@sideboard-ai/core 0.1.89 → 0.1.92

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 (30) hide show
  1. package/dist/agents/cursor-runner.cjs +129 -14
  2. package/dist/agents/cursor-runner.js +4 -1
  3. package/dist/{agents-LMUFTGKF.js → agents-JXQ5QHDU.js} +4 -4
  4. package/dist/{agents-ODBP7J6E.js → agents-TUV4NH7S.js} +5 -5
  5. package/dist/{chunk-RJLBSYUO.js → chunk-35LKGGOC.js} +45 -77
  6. package/dist/{chunk-WANQFU3S.js → chunk-6XBXVXX2.js} +2 -2
  7. package/dist/{chunk-7D27DD2X.js → chunk-CIRXAYWS.js} +260 -61
  8. package/dist/{chunk-B2KIO2SD.js → chunk-CLGO7TLO.js} +2 -2
  9. package/dist/{chunk-JE75QW2I.js → chunk-GJ2HJQIU.js} +236 -96
  10. package/dist/{chunk-CBJSPTBG.js → chunk-GXSYI7FH.js} +206 -5
  11. package/dist/{chunk-UHTNJKZX.js → chunk-NR6APJLD.js} +2 -2
  12. package/dist/{chunk-6EZRSCIT.js → chunk-QKYO6BHB.js} +2 -2
  13. package/dist/{chunk-ZXYWWSHZ.js → chunk-R7BQBSDT.js} +254 -61
  14. package/dist/{chunk-OB6IRIFV.js → chunk-XH2GS2LO.js} +2 -2
  15. package/dist/{chunk-VZ2L4AEJ.js → chunk-XUWDLRAE.js} +2 -2
  16. package/dist/{coordinator-prompt-UK5LYFN5.js → coordinator-prompt-AKEY4WSO.js} +2 -2
  17. package/dist/{coordinator-prompt-CI5SHONJ.js → coordinator-prompt-OQOOD5ET.js} +2 -2
  18. package/dist/{global-workspace-2YZ2V4I5.js → global-workspace-3GNPQCLE.js} +3 -3
  19. package/dist/{global-workspace-JQQLPJM5.js → global-workspace-M3OMVDDH.js} +3 -3
  20. package/dist/index.cjs +941 -455
  21. package/dist/index.d.cts +77 -19
  22. package/dist/index.d.ts +77 -19
  23. package/dist/index.js +166 -42
  24. package/dist/mcp/run-stdio.cjs +815 -428
  25. package/dist/mcp/run-stdio.js +77 -40
  26. package/dist/{workspaces-YWCC3WV4.js → workspaces-ERZC7ULY.js} +4 -4
  27. package/dist/{workspaces-FDO5L4NI.js → workspaces-J4WG6UFR.js} +4 -4
  28. package/dist/{worktree-7YNSJ224.js → worktree-DA4BOV7G.js} +7 -1
  29. package/dist/{worktree-4555QBQ7.js → worktree-EO5QAGJU.js} +7 -1
  30. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -3068,42 +3068,124 @@ var init_gh_errors = __esm({
3068
3068
  }
3069
3069
  });
3070
3070
 
3071
+ // src/git/github-agent-auth.ts
3072
+ function githubAgentAuthDir() {
3073
+ const override = process.env.SIDEBOARD_GIT_AUTH_DIR?.trim();
3074
+ if (override) return override;
3075
+ return (0, import_node_path10.join)((0, import_node_os4.homedir)(), ".sideboard-git-auth");
3076
+ }
3077
+ function githubCredentialStorePath() {
3078
+ return (0, import_node_path10.join)(githubAgentAuthDir(), "git-credentials");
3079
+ }
3080
+ function githubGhConfigDir() {
3081
+ return (0, import_node_path10.join)(githubAgentAuthDir(), "gh");
3082
+ }
3083
+ function writePrivateFile2(file, body) {
3084
+ (0, import_node_fs10.mkdirSync)((0, import_node_path10.dirname)(file), { recursive: true, mode: 448 });
3085
+ try {
3086
+ (0, import_node_fs10.chmodSync)((0, import_node_path10.dirname)(file), 448);
3087
+ } catch {
3088
+ }
3089
+ (0, import_node_fs10.writeFileSync)(file, body, { encoding: "utf8", mode: 384 });
3090
+ try {
3091
+ (0, import_node_fs10.chmodSync)(file, 384);
3092
+ } catch {
3093
+ }
3094
+ }
3095
+ function gitCredentialStoreContents(token) {
3096
+ return `https://x-access-token:${encodeURIComponent(token)}@github.com
3097
+ `;
3098
+ }
3099
+ function ghHostsYml(token, user = "x-access-token") {
3100
+ const u = user.replace(/[^A-Za-z0-9._-]/g, "") || "x-access-token";
3101
+ return [
3102
+ "github.com:",
3103
+ " git_protocol: https",
3104
+ ` user: ${u}`,
3105
+ ` oauth_token: ${token}`,
3106
+ " users:",
3107
+ ` ${u}:`,
3108
+ ` oauth_token: ${token}`,
3109
+ ""
3110
+ ].join("\n");
3111
+ }
3112
+ function materializeGithubAgentAuth(token, user) {
3113
+ const trimmed = token.trim();
3114
+ if (!trimmed) return;
3115
+ const root = githubAgentAuthDir();
3116
+ (0, import_node_fs10.mkdirSync)(root, { recursive: true, mode: 448 });
3117
+ try {
3118
+ (0, import_node_fs10.chmodSync)(root, 448);
3119
+ } catch {
3120
+ }
3121
+ writePrivateFile2(githubCredentialStorePath(), gitCredentialStoreContents(trimmed));
3122
+ const ghDir = githubGhConfigDir();
3123
+ (0, import_node_fs10.mkdirSync)(ghDir, { recursive: true, mode: 448 });
3124
+ writePrivateFile2((0, import_node_path10.join)(ghDir, "hosts.yml"), ghHostsYml(trimmed, user));
3125
+ writePrivateFile2((0, import_node_path10.join)(ghDir, "config.yml"), "git_protocol: https\nprompt: disabled\n");
3126
+ }
3127
+ function githubAgentAuthReady() {
3128
+ return (0, import_node_fs10.existsSync)(githubCredentialStorePath()) && (0, import_node_fs10.existsSync)((0, import_node_path10.join)(githubGhConfigDir(), "hosts.yml"));
3129
+ }
3130
+ function githubCredentialHelperGitConfig() {
3131
+ const file = githubCredentialStorePath();
3132
+ return [
3133
+ { key: "credential.helper", value: "" },
3134
+ { key: "credential.helper", value: `store --file=${file}` }
3135
+ ];
3136
+ }
3137
+ function githubGhConfigEnv() {
3138
+ return {
3139
+ GH_CONFIG_DIR: githubGhConfigDir(),
3140
+ GH_PROMPT_DISABLED: "1"
3141
+ };
3142
+ }
3143
+ var import_node_fs10, import_node_os4, import_node_path10;
3144
+ var init_github_agent_auth = __esm({
3145
+ "src/git/github-agent-auth.ts"() {
3146
+ "use strict";
3147
+ import_node_fs10 = require("fs");
3148
+ import_node_os4 = require("os");
3149
+ import_node_path10 = require("path");
3150
+ }
3151
+ });
3152
+
3071
3153
  // src/agents/path.ts
3072
3154
  function prependPathDir(env, dir) {
3073
- if (!dir || !(0, import_node_fs10.existsSync)(dir)) return;
3155
+ if (!dir || !(0, import_node_fs11.existsSync)(dir)) return;
3074
3156
  const current = env.PATH ?? "";
3075
- const parts = current.split(import_node_path10.delimiter).filter(Boolean);
3157
+ const parts = current.split(import_node_path11.delimiter).filter(Boolean);
3076
3158
  if (parts.includes(dir)) {
3077
3159
  env.PATH = current;
3078
3160
  return;
3079
3161
  }
3080
- env.PATH = [dir, ...parts].join(import_node_path10.delimiter);
3162
+ env.PATH = [dir, ...parts].join(import_node_path11.delimiter);
3081
3163
  }
3082
- function conductorBundledBinDir(home = process.env.HOME || process.env.USERPROFILE || (0, import_node_os4.homedir)()) {
3083
- return (0, import_node_path10.join)(home, "Library", "Application Support", "com.conductor.app", "bin");
3164
+ function conductorBundledBinDir(home = process.env.HOME || process.env.USERPROFILE || (0, import_node_os5.homedir)()) {
3165
+ return (0, import_node_path11.join)(home, "Library", "Application Support", "com.conductor.app", "bin");
3084
3166
  }
3085
3167
  function isConductorBundledCli(filePath) {
3086
3168
  const p = (filePath ?? "").replace(/\\/g, "/");
3087
3169
  return p.includes("/com.conductor.app/bin/") || p.endsWith("/com.conductor.app/bin");
3088
3170
  }
3089
3171
  function ensureAgentPath(env = process.env) {
3090
- const home = env.HOME || env.USERPROFILE || (0, import_node_os4.homedir)();
3172
+ const home = env.HOME || env.USERPROFILE || (0, import_node_os5.homedir)();
3091
3173
  const current = env.PATH ?? "";
3092
- const parts = current.split(import_node_path10.delimiter).filter(Boolean);
3174
+ const parts = current.split(import_node_path11.delimiter).filter(Boolean);
3093
3175
  const seen = new Set(parts);
3094
3176
  const extras = [
3095
- ...EXTRA_BIN_DIRS.map((rel) => (0, import_node_path10.join)(home, rel)),
3177
+ ...EXTRA_BIN_DIRS.map((rel) => (0, import_node_path11.join)(home, rel)),
3096
3178
  "/opt/homebrew/bin",
3097
3179
  "/usr/local/bin",
3098
3180
  // Keep after Homebrew/npm so a user-installed CLI still wins.
3099
3181
  conductorBundledBinDir(home)
3100
3182
  ];
3101
3183
  for (const dir of extras.reverse()) {
3102
- if (!dir || seen.has(dir) || !(0, import_node_fs10.existsSync)(dir)) continue;
3184
+ if (!dir || seen.has(dir) || !(0, import_node_fs11.existsSync)(dir)) continue;
3103
3185
  parts.unshift(dir);
3104
3186
  seen.add(dir);
3105
3187
  }
3106
- const next = parts.join(import_node_path10.delimiter);
3188
+ const next = parts.join(import_node_path11.delimiter);
3107
3189
  env.PATH = next;
3108
3190
  return next;
3109
3191
  }
@@ -3117,7 +3199,7 @@ function enrichPathWithNpmGlobalBin(env = process.env) {
3117
3199
  stdio: ["ignore", "pipe", "ignore"]
3118
3200
  }).trim().split(/\r?\n/).find(Boolean);
3119
3201
  if (prefix) {
3120
- const binDir = process.platform === "win32" ? prefix : (0, import_node_path10.join)(prefix, "bin");
3202
+ const binDir = process.platform === "win32" ? prefix : (0, import_node_path11.join)(prefix, "bin");
3121
3203
  prependPathDir(env, binDir);
3122
3204
  }
3123
3205
  } catch {
@@ -3145,14 +3227,14 @@ function withExportedPath(command, pathValue) {
3145
3227
  if (/^(export\s+PATH=|PATH=)/.test(trimmed)) return trimmed;
3146
3228
  return `export PATH=${posixShellSingleQuote(pathValue)} && ${trimmed}`;
3147
3229
  }
3148
- var import_node_fs10, import_node_child_process3, import_node_os4, import_node_path10, EXTRA_BIN_DIRS;
3230
+ var import_node_fs11, import_node_child_process3, import_node_os5, import_node_path11, EXTRA_BIN_DIRS;
3149
3231
  var init_path = __esm({
3150
3232
  "src/agents/path.ts"() {
3151
3233
  "use strict";
3152
- import_node_fs10 = require("fs");
3234
+ import_node_fs11 = require("fs");
3153
3235
  import_node_child_process3 = require("child_process");
3154
- import_node_os4 = require("os");
3155
- import_node_path10 = require("path");
3236
+ import_node_os5 = require("os");
3237
+ import_node_path11 = require("path");
3156
3238
  EXTRA_BIN_DIRS = [
3157
3239
  ".local/bin",
3158
3240
  ".cargo/bin",
@@ -3277,42 +3359,46 @@ function appendIndexedGitConfig(existing, entries) {
3277
3359
  return out;
3278
3360
  }
3279
3361
  function githubAgentGitEnv(existing) {
3280
- return appendIndexedGitConfig(existing, HTTPS_REWRITE);
3281
- }
3282
- function githubHttpsBearerEnv(existing, token) {
3283
- const rewrite = githubAgentGitEnv(existing);
3284
- const header = appendIndexedGitConfig(
3285
- { ...existing, ...rewrite },
3286
- [
3287
- {
3288
- key: "http.https://github.com/.extraHeader",
3289
- value: `AUTHORIZATION: bearer ${token}`
3290
- }
3291
- ]
3292
- );
3293
- return { ...rewrite, ...header };
3362
+ const helpers = githubAgentAuthReady() ? githubCredentialHelperGitConfig() : [{ key: "credential.helper", value: "" }];
3363
+ return appendIndexedGitConfig(existing, [...HTTPS_REWRITE, ...helpers]);
3294
3364
  }
3295
3365
  function applyGithubGitAuthEnv(existing, opts) {
3296
3366
  const out = { ...nonInteractiveGitProcessEnv() };
3367
+ const token = opts.token?.trim() || existing?.GH_TOKEN?.trim() || "" || "";
3368
+ if (token) materializeGithubAgentAuth(token);
3369
+ Object.assign(out, githubGhConfigEnv());
3297
3370
  if (opts.mode === "ssh") return out;
3298
- const existingToken = existing?.GH_TOKEN?.trim() || "";
3299
- const provided = existingToken ? "" : opts.token?.trim() || "";
3300
- const token = existingToken || provided;
3301
- Object.assign(
3302
- out,
3303
- token ? githubHttpsBearerEnv(existing, token) : githubAgentGitEnv(existing)
3304
- );
3305
- if (provided) out.GH_TOKEN = provided;
3371
+ Object.assign(out, githubAgentGitEnv(existing));
3306
3372
  return out;
3307
3373
  }
3374
+ function scrubGithubTokensFromChildEnv(env) {
3375
+ for (const key of GITHUB_CHILD_TOKEN_KEYS) {
3376
+ delete env[key];
3377
+ }
3378
+ }
3379
+ function mergeAgentGitAuthEnv(env, gitEnv) {
3380
+ Object.assign(env, gitEnv);
3381
+ scrubGithubTokensFromChildEnv(env);
3382
+ }
3308
3383
  async function resolveGithubAgentToken(mode, cwd) {
3309
- if (mode === "ssh") return null;
3310
- if (mode === "token") return getGithubPat();
3311
- try {
3312
- return await resolveGhAuthToken(cwd);
3313
- } catch {
3314
- return null;
3384
+ if (tokenMemo && tokenMemo.mode === mode && Date.now() - tokenMemo.at < TOKEN_TTL_MS) {
3385
+ return tokenMemo.value;
3386
+ }
3387
+ let value = null;
3388
+ if (mode === "token") {
3389
+ value = getGithubPat();
3390
+ } else {
3391
+ try {
3392
+ value = await resolveGhAuthToken(cwd);
3393
+ } catch {
3394
+ value = null;
3395
+ }
3315
3396
  }
3397
+ tokenMemo = { mode, value, at: Date.now() };
3398
+ return value;
3399
+ }
3400
+ function resetGithubAgentTokenMemo() {
3401
+ tokenMemo = null;
3316
3402
  }
3317
3403
  async function resolveAgentGitAuthEnv(existing, opts) {
3318
3404
  let mode = "auto";
@@ -3334,7 +3420,40 @@ async function resolveAgentGitAuthEnv(existing, opts) {
3334
3420
  }
3335
3421
  return applyGithubGitAuthEnv(existing, { mode, token });
3336
3422
  }
3337
- function codexUnattendedGitConfigArgs(sandbox) {
3423
+ async function warmGithubAgentAuth(opts) {
3424
+ if (!opts?.force && githubAgentAuthReady()) return;
3425
+ await resolveAgentGitAuthEnv(void 0, opts);
3426
+ }
3427
+ function normalizeWritableRoot(raw) {
3428
+ const trimmed = raw.trim().replace(/\/+$/, "");
3429
+ return trimmed && (0, import_node_path12.isAbsolute)(trimmed) ? trimmed : null;
3430
+ }
3431
+ async function resolveCodexGitWritableRoots(cwd) {
3432
+ const roots = /* @__PURE__ */ new Set();
3433
+ const authDir = normalizeWritableRoot(githubAgentAuthDir());
3434
+ if (authDir) roots.add(authDir);
3435
+ try {
3436
+ const [gitDir, commonDir] = await Promise.all([
3437
+ git(["rev-parse", "--absolute-git-dir"], cwd, { reject: false, timeoutMs: 5e3 }),
3438
+ git(["rev-parse", "--path-format=absolute", "--git-common-dir"], cwd, {
3439
+ reject: false,
3440
+ timeoutMs: 5e3
3441
+ })
3442
+ ]);
3443
+ const gitDirPath = gitDir.exitCode === 0 ? normalizeWritableRoot(gitDir.stdout) : null;
3444
+ const commonPath = commonDir.exitCode === 0 ? normalizeWritableRoot(commonDir.stdout) : null;
3445
+ if (gitDirPath) roots.add(gitDirPath);
3446
+ if (commonPath) roots.add(commonPath);
3447
+ } catch {
3448
+ }
3449
+ return [...roots];
3450
+ }
3451
+ function codexSandboxWritableRootsArgs(roots) {
3452
+ const abs = [...new Set(roots.map(normalizeWritableRoot).filter(Boolean))];
3453
+ if (abs.length === 0) return [];
3454
+ return ["-c", `sandbox_workspace_write.writable_roots=${JSON.stringify(abs)}`];
3455
+ }
3456
+ function codexUnattendedGitConfigArgs(sandbox, opts) {
3338
3457
  const args = [
3339
3458
  "-c",
3340
3459
  'shell_environment_policy.inherit="all"',
@@ -3343,54 +3462,65 @@ function codexUnattendedGitConfigArgs(sandbox) {
3343
3462
  ];
3344
3463
  if (sandbox === "workspace-write") {
3345
3464
  args.push("-c", "sandbox_workspace_write.network_access=true");
3465
+ args.push(...codexSandboxWritableRootsArgs(opts?.writableRoots ?? []));
3346
3466
  }
3347
3467
  return args;
3348
3468
  }
3349
3469
  function formatGitAuthModeDirective(mode) {
3470
+ const shared = [
3471
+ "- `git` and `gh` already authenticate in this process. Do not look for tokens in the environment, paste credentials into commands, or switch remotes to SSH.",
3472
+ "- Do not set GitHub token environment variables, pass `--with-token`, or run `gh auth login` from this turn.",
3473
+ "- Push with `git push -u origin HEAD`. Create/update PRs with `gh` as below.",
3474
+ "- If git/gh fail with auth errors, tell the user to run `gh auth login` on this Mac (or set a PAT in Account \u2192 GitHub). Do not wait for a Keychain dialog."
3475
+ ];
3350
3476
  switch (mode) {
3351
3477
  case "gh":
3352
3478
  return [
3353
3479
  "Git authentication (Account \u2192 GitHub mode: gh CLI):",
3354
3480
  "- This process rewrites `git@github.com:` and `ssh://git@github.com/` to HTTPS.",
3355
- "- `GH_TOKEN` is injected from `gh auth token` (no macOS Keychain prompts). Do not switch remotes to SSH.",
3356
- "- Push with `git push -u origin HEAD`. Create/update PRs with `gh` as below."
3481
+ ...shared
3357
3482
  ].join("\n");
3358
3483
  case "ssh":
3359
3484
  return [
3360
3485
  "Git authentication (Account \u2192 GitHub mode: SSH):",
3361
3486
  "- Keep SSH remotes (`git@github.com:\u2026`). Do not rewrite them to HTTPS.",
3362
3487
  "- SSH is batch-mode: it will not prompt for a Keychain password. If push fails with `Permission denied (publickey)`, tell the user to unlock ssh-agent or switch Account \u2192 GitHub to Auto / gh CLI \u2014 do not rewrite remotes yourself.",
3363
- "- Push with `git push -u origin HEAD`. Create/update PRs with `gh` as below (API still uses `gh`)."
3488
+ "- `gh` already authenticates for PRs/API (no token in the environment). Do not set GitHub token environment variables or run `gh auth login` from this turn.",
3489
+ "- Push with `git push -u origin HEAD`. Create/update PRs with `gh` as below."
3364
3490
  ].join("\n");
3365
3491
  case "token":
3366
3492
  return [
3367
3493
  "Git authentication (Account \u2192 GitHub mode: personal access token):",
3368
3494
  "- This process rewrites GitHub SSH remotes to HTTPS.",
3369
- "- `GH_TOKEN` is set in the environment. Use HTTPS git and `gh`; do not paste the token into commands or chat.",
3370
- "- Push with `git push -u origin HEAD`. Create/update PRs with `gh` as below."
3495
+ ...shared
3371
3496
  ].join("\n");
3372
3497
  case "auto":
3373
3498
  default:
3374
3499
  return [
3375
3500
  "Git authentication (Account \u2192 GitHub mode: auto):",
3376
- "- This process rewrites GitHub SSH remotes to HTTPS and authenticates with `GH_TOKEN` from `gh` so git/ssh never prompt the macOS Keychain (required for unattended orchestrator turns).",
3377
- "- Do not switch remotes to SSH. Push with `git push -u origin HEAD`.",
3378
- "- If HTTPS auth fails, tell the user to run `gh auth login` on this Mac or set Account \u2192 GitHub to a PAT \u2014 do not wait for a Keychain dialog."
3501
+ "- This process rewrites GitHub SSH remotes to HTTPS so git/ssh never prompt the macOS Keychain (required for unattended orchestrator turns).",
3502
+ ...shared
3379
3503
  ].join("\n");
3380
3504
  }
3381
3505
  }
3382
- var HTTPS_REWRITE;
3506
+ var import_node_path12, HTTPS_REWRITE, TOKEN_TTL_MS, tokenMemo, GITHUB_CHILD_TOKEN_KEYS;
3383
3507
  var init_git_auth_mode = __esm({
3384
3508
  "src/git/git-auth-mode.ts"() {
3385
3509
  "use strict";
3510
+ import_node_path12 = require("path");
3386
3511
  init_app_settings();
3512
+ init_github_agent_auth();
3387
3513
  init_run();
3388
3514
  HTTPS_REWRITE = [
3389
3515
  { key: "url.https://github.com/.insteadOf", value: "git@github.com:" },
3390
- { key: "url.https://github.com/.insteadOf", value: "ssh://git@github.com/" },
3391
- // Empty helper disables ~/.gitconfig osxkeychain so GUI agents cannot pop
3392
- // "git-credential-osxkeychain wants to use the keychain".
3393
- { key: "credential.helper", value: "" }
3516
+ { key: "url.https://github.com/.insteadOf", value: "ssh://git@github.com/" }
3517
+ ];
3518
+ TOKEN_TTL_MS = 12 * 60 * 60 * 1e3;
3519
+ tokenMemo = null;
3520
+ GITHUB_CHILD_TOKEN_KEYS = [
3521
+ "GH_TOKEN",
3522
+ "GITHUB_TOKEN",
3523
+ "GH_ENTERPRISE_TOKEN"
3394
3524
  ];
3395
3525
  }
3396
3526
  });
@@ -3751,10 +3881,12 @@ __export(worktree_exports, {
3751
3881
  getPr: () => getPr,
3752
3882
  getPrChecks: () => getPrChecks,
3753
3883
  getPrDetails: () => getPrDetails,
3884
+ getPrForHeadBranch: () => getPrForHeadBranch,
3754
3885
  getPrMeta: () => getPrMeta,
3755
3886
  ghHeadRef: () => ghHeadRef,
3756
3887
  ghRepoSelectArgs: () => ghRepoSelectArgs,
3757
3888
  githubAgentGitEnv: () => githubAgentGitEnv,
3889
+ isDefaultishSourceRef: () => isDefaultishSourceRef,
3758
3890
  isDirty: () => isDirty,
3759
3891
  isPlaceholderBranch: () => isPlaceholderBranch,
3760
3892
  isSideboardScratchPath: () => isSideboardScratchPath,
@@ -3772,6 +3904,7 @@ __export(worktree_exports, {
3772
3904
  resolveDiffBaseRef: () => resolveDiffBaseRef,
3773
3905
  resolveGithubRepoSlug: () => resolveGithubRepoSlug,
3774
3906
  resolvePrSelector: () => resolvePrSelector,
3907
+ resolvePrSelectors: () => resolvePrSelectors,
3775
3908
  resolveRepoRoot: () => resolveRepoRoot,
3776
3909
  resolveWorktreeStartPoint: () => resolveWorktreeStartPoint,
3777
3910
  slugify: () => slugify,
@@ -3880,7 +4013,7 @@ async function originGhRepoEnv(cwd, opts) {
3880
4013
  await ensureGhPreferOrigin(cwd);
3881
4014
  const slug = await resolveGithubRepoSlug(cwd);
3882
4015
  const mode = opts?.mode ?? getGithubGitAuthMode();
3883
- const token = mode === "token" ? getGithubPat() : mode === "ssh" ? null : await resolveGhAuthToken(cwd);
4016
+ const token = await resolveGithubAgentToken(mode, cwd);
3884
4017
  return {
3885
4018
  ...applyGithubGitAuthEnv(opts?.env, { mode, token }),
3886
4019
  ...slug ? { GH_REPO: slug } : {}
@@ -4020,13 +4153,78 @@ async function getPr(repoPath, number) {
4020
4153
  if (exitCode !== 0 || !stdout.trim()) return null;
4021
4154
  return JSON.parse(stdout);
4022
4155
  }
4023
- function resolvePrSelector(thread) {
4024
- if (thread.prUrl?.trim()) return thread.prUrl.trim();
4156
+ function normalizeGitBranchName(ref) {
4157
+ return ref.trim().replace(/^refs\/heads\//, "").replace(/^origin\//, "");
4158
+ }
4159
+ function isDefaultishSourceRef(ref) {
4160
+ const n = normalizeGitBranchName(ref ?? "").toLowerCase();
4161
+ return !n || n === "head" || n === "default" || n === "main" || n === "master" || n === "develop" || n === "trunk";
4162
+ }
4163
+ function resolvePrSelectors(thread) {
4164
+ const out = [];
4165
+ const push = (value) => {
4166
+ const v = value?.trim();
4167
+ if (!v || out.includes(v)) return;
4168
+ out.push(v);
4169
+ };
4170
+ push(thread.prUrl);
4025
4171
  if (thread.sourceType === "pr" && thread.sourceRef?.trim()) {
4026
- return thread.sourceRef.replace(/^#/, "").trim();
4172
+ push(thread.sourceRef.replace(/^#/, "").trim());
4173
+ }
4174
+ push(thread.branchName);
4175
+ if (thread.sourceType === "branch") {
4176
+ const source = normalizeGitBranchName(thread.sourceRef ?? "");
4177
+ if (source && !isDefaultishSourceRef(source) && source !== thread.branchName?.trim()) {
4178
+ push(source);
4179
+ }
4180
+ }
4181
+ return out;
4182
+ }
4183
+ function resolvePrSelector(thread) {
4184
+ return resolvePrSelectors(thread)[0] ?? null;
4185
+ }
4186
+ async function getPrForHeadBranch(repoPath, branch) {
4187
+ const head = normalizeGitBranchName(branch);
4188
+ if (!head || isDefaultishSourceRef(head)) return null;
4189
+ const slug = await resolveGithubRepoSlug(repoPath);
4190
+ const viewArgs = [
4191
+ "pr",
4192
+ "view",
4193
+ head,
4194
+ "--json",
4195
+ "number,title,headRefName,url,isCrossRepository"
4196
+ ];
4197
+ if (slug) viewArgs.push("--repo", slug);
4198
+ const viewed = await gh(viewArgs, repoPath, { reject: false });
4199
+ if (viewed.exitCode === 0 && viewed.stdout.trim()) {
4200
+ try {
4201
+ return JSON.parse(viewed.stdout);
4202
+ } catch {
4203
+ return null;
4204
+ }
4205
+ }
4206
+ const listHead = slug ? ghHeadRef(slug, head) : head;
4207
+ const listArgs = [
4208
+ "pr",
4209
+ "list",
4210
+ "--head",
4211
+ listHead,
4212
+ "--json",
4213
+ "number,title,headRefName,url,isCrossRepository",
4214
+ "--limit",
4215
+ "1",
4216
+ "--state",
4217
+ "open"
4218
+ ];
4219
+ if (slug) listArgs.push("--repo", slug);
4220
+ const listed = await gh(listArgs, repoPath, { reject: false });
4221
+ if (listed.exitCode !== 0 || !listed.stdout.trim()) return null;
4222
+ try {
4223
+ const rows = JSON.parse(listed.stdout);
4224
+ return rows[0] ?? null;
4225
+ } catch {
4226
+ return null;
4027
4227
  }
4028
- if (thread.branchName?.trim()) return thread.branchName.trim();
4029
- return null;
4030
4228
  }
4031
4229
  function normalizeGhTime(value) {
4032
4230
  if (typeof value !== "string" || !value.trim()) return null;
@@ -4352,7 +4550,7 @@ async function fetchPrHead(repoPath, number, localBranch) {
4352
4550
  const label = opts?.ghAuth ? `${remote} (gh auth)` : remote;
4353
4551
  const gitOpts = { reject: false };
4354
4552
  if (opts?.ghAuth) {
4355
- const token = await resolveGhAuthToken(repoPath);
4553
+ const token = await resolveGithubAgentToken(getGithubGitAuthMode(), repoPath);
4356
4554
  if (!token) {
4357
4555
  errors.push(`${label}: gh auth token unavailable`);
4358
4556
  return false;
@@ -4403,7 +4601,7 @@ async function fetchPrHead(repoPath, number, localBranch) {
4403
4601
  const ensureOid = async (remote, opts) => {
4404
4602
  const gitOpts = { reject: false };
4405
4603
  if (opts?.ghAuth) {
4406
- const token = await resolveGhAuthToken(repoPath);
4604
+ const token = await resolveGithubAgentToken(getGithubGitAuthMode(), repoPath);
4407
4605
  if (!token) return false;
4408
4606
  gitOpts.env = { GIT_TERMINAL_PROMPT: "0" };
4409
4607
  gitOpts.config = {
@@ -4471,8 +4669,8 @@ function isLocalPrFetchBranch(ref) {
4471
4669
  }
4472
4670
  async function createThreadWorktree(opts) {
4473
4671
  let branchName = `thread/${opts.slug}`;
4474
- const worktreePath = (0, import_node_path11.join)(worktreesRoot(opts.repoPath), opts.slug);
4475
- if ((0, import_node_fs11.existsSync)(worktreePath)) {
4672
+ const worktreePath = (0, import_node_path13.join)(worktreesRoot(opts.repoPath), opts.slug);
4673
+ if ((0, import_node_fs12.existsSync)(worktreePath)) {
4476
4674
  throw new Error(`Worktree already exists at ${worktreePath}`);
4477
4675
  }
4478
4676
  await ensureGhPreferOrigin(opts.repoPath);
@@ -4541,8 +4739,8 @@ ${add.stdout}`;
4541
4739
  async function createExistingBranchWorktree(opts) {
4542
4740
  const branchName = opts.branchName.trim();
4543
4741
  if (!branchName) throw new Error("branch name required");
4544
- const worktreePath = (0, import_node_path11.join)(worktreesRoot(opts.repoPath), opts.slug);
4545
- if ((0, import_node_fs11.existsSync)(worktreePath)) {
4742
+ const worktreePath = (0, import_node_path13.join)(worktreesRoot(opts.repoPath), opts.slug);
4743
+ if ((0, import_node_fs12.existsSync)(worktreePath)) {
4546
4744
  throw new Error(`Worktree already exists at ${worktreePath}`);
4547
4745
  }
4548
4746
  await ensureGhPreferOrigin(opts.repoPath);
@@ -4653,7 +4851,7 @@ async function pushBranch(worktreePath, branchName) {
4653
4851
  if (ssh.exitCode === 0) return;
4654
4852
  const sshErr = (ssh.stderr || ssh.stdout).trim();
4655
4853
  const slug = await resolveGithubRepoSlug(worktreePath);
4656
- const token = await resolveGhAuthToken(worktreePath);
4854
+ const token = await resolveGithubAgentToken(getGithubGitAuthMode(), worktreePath);
4657
4855
  if (!slug || !token) {
4658
4856
  throw new Error(
4659
4857
  sshErr || `git push origin ${branchName} failed` + (!token ? " (no SSH agent and gh auth token unavailable \u2014 run: gh auth login)" : "")
@@ -4830,10 +5028,10 @@ function sameRepoPath(a, b) {
4830
5028
  return normalizeWorktreePath(a) === normalizeWorktreePath(b);
4831
5029
  }
4832
5030
  function listLocalThreadBranchSlugs(repoPath) {
4833
- const refsDir = (0, import_node_path11.join)(repoPath, ".git", "refs", "heads", "thread");
4834
- if (!(0, import_node_fs11.existsSync)(refsDir)) return [];
5031
+ const refsDir = (0, import_node_path13.join)(repoPath, ".git", "refs", "heads", "thread");
5032
+ if (!(0, import_node_fs12.existsSync)(refsDir)) return [];
4835
5033
  try {
4836
- return (0, import_node_fs11.readdirSync)(refsDir).filter((name) => !name.startsWith(".")).map((name) => normalizeTakenSlug(name));
5034
+ return (0, import_node_fs12.readdirSync)(refsDir).filter((name) => !name.startsWith(".")).map((name) => normalizeTakenSlug(name));
4837
5035
  } catch {
4838
5036
  return [];
4839
5037
  }
@@ -4841,8 +5039,8 @@ function listLocalThreadBranchSlugs(repoPath) {
4841
5039
  function collectTakenTeamSlugs(repoPath) {
4842
5040
  const taken = /* @__PURE__ */ new Set();
4843
5041
  const root = worktreesRoot(repoPath);
4844
- if ((0, import_node_fs11.existsSync)(root)) {
4845
- for (const entry of (0, import_node_fs11.readdirSync)(root, { withFileTypes: true })) {
5042
+ if ((0, import_node_fs12.existsSync)(root)) {
5043
+ for (const entry of (0, import_node_fs12.readdirSync)(root, { withFileTypes: true })) {
4846
5044
  if (entry.isDirectory() && entry.name !== ".DS_Store") {
4847
5045
  taken.add(normalizeTakenSlug(entry.name));
4848
5046
  }
@@ -4863,18 +5061,18 @@ function allocateTeamSlug(repoPath) {
4863
5061
  const taken = collectTakenTeamSlugs(repoPath);
4864
5062
  for (let attempt = 0; attempt < 32; attempt++) {
4865
5063
  const team = allocateTeamName(taken);
4866
- const path2 = (0, import_node_path11.join)(worktreesRoot(repoPath), team.slug);
4867
- if (!(0, import_node_fs11.existsSync)(path2)) return team;
5064
+ const path2 = (0, import_node_path13.join)(worktreesRoot(repoPath), team.slug);
5065
+ if (!(0, import_node_fs12.existsSync)(path2)) return team;
4868
5066
  taken.add(team.slug);
4869
5067
  }
4870
5068
  throw new Error("No available soccer team worktree directories left");
4871
5069
  }
4872
- var import_node_fs11, import_node_path11;
5070
+ var import_node_fs12, import_node_path13;
4873
5071
  var init_worktree = __esm({
4874
5072
  "src/git/worktree.ts"() {
4875
5073
  "use strict";
4876
- import_node_fs11 = require("fs");
4877
- import_node_path11 = require("path");
5074
+ import_node_fs12 = require("fs");
5075
+ import_node_path13 = require("path");
4878
5076
  init_paths();
4879
5077
  init_thread_store();
4880
5078
  init_teams();
@@ -4958,7 +5156,7 @@ function coordinatorTurnReminder(opts) {
4958
5156
  function ensureGlobalCoordinatorCwd(opts) {
4959
5157
  const dir = globalAgentCwd();
4960
5158
  try {
4961
- (0, import_node_fs12.mkdirSync)(dir, { recursive: true });
5159
+ (0, import_node_fs13.mkdirSync)(dir, { recursive: true });
4962
5160
  } catch {
4963
5161
  return dir;
4964
5162
  }
@@ -4966,7 +5164,7 @@ function ensureGlobalCoordinatorCwd(opts) {
4966
5164
  let orchId = opts?.orchestratorThreadId?.trim() || "";
4967
5165
  if (!orchId) {
4968
5166
  try {
4969
- const existing = (0, import_node_fs12.readFileSync)((0, import_node_path12.join)(dir, "AGENTS.md"), "utf8");
5167
+ const existing = (0, import_node_fs13.readFileSync)((0, import_node_path14.join)(dir, "AGENTS.md"), "utf8");
4970
5168
  const m = existing.match(
4971
5169
  /YOUR orchestration thread id is `([0-9a-f-]{36})`/i
4972
5170
  );
@@ -5008,9 +5206,9 @@ function ensureGlobalCoordinatorCwd(opts) {
5008
5206
  "Always ask worktree agents to commit, push, and open draft PRs (`ask_git` / `send_to_thread`). Tell them to merge only when the user explicitly asked. The worktree agent runs git/gh; never merge from this orchestration cwd."
5009
5207
  ].join("\n");
5010
5208
  try {
5011
- (0, import_node_fs12.writeFileSync)((0, import_node_path12.join)(dir, "CLAUDE.md"), `${body}
5209
+ (0, import_node_fs13.writeFileSync)((0, import_node_path14.join)(dir, "CLAUDE.md"), `${body}
5012
5210
  `, "utf8");
5013
- (0, import_node_fs12.writeFileSync)((0, import_node_path12.join)(dir, "AGENTS.md"), `${body}
5211
+ (0, import_node_fs13.writeFileSync)((0, import_node_path14.join)(dir, "AGENTS.md"), `${body}
5014
5212
  `, "utf8");
5015
5213
  } catch {
5016
5214
  }
@@ -5042,12 +5240,12 @@ function coordinatorSystemPrompt(opts) {
5042
5240
  formatWorkspaceInventory(opts.workspaces)
5043
5241
  ].join("\n");
5044
5242
  }
5045
- var import_node_fs12, import_node_path12, COORDINATOR_TOOL_PLAYBOOK, SLACK_REPLY_FORMATTING;
5243
+ var import_node_fs13, import_node_path14, COORDINATOR_TOOL_PLAYBOOK, SLACK_REPLY_FORMATTING;
5046
5244
  var init_coordinator_prompt = __esm({
5047
5245
  "src/orchestrator/coordinator-prompt.ts"() {
5048
5246
  "use strict";
5049
- import_node_fs12 = require("fs");
5050
- import_node_path12 = require("path");
5247
+ import_node_fs13 = require("fs");
5248
+ import_node_path14 = require("path");
5051
5249
  init_worktree();
5052
5250
  init_app_settings();
5053
5251
  init_paths();
@@ -5063,7 +5261,7 @@ var init_coordinator_prompt = __esm({
5063
5261
  "- get_pr_stack / open_pr_stack_layers / add_stack_layer / create_pr_stack \u2014 GitHub stacked PRs (`gh stack`); one worktree per layer",
5064
5262
  "- list_models \u2014 only when you need a specific model (rare); otherwise omit model so Account defaults apply",
5065
5263
  "- list_threads / get_thread \u2014 fleet status (what is going on)",
5066
- "- ask_user \u2014 multiple-choice questions in the composer (any mode, not only Plan). Explain options in chat first, include a description on every option, then wait for answers.",
5264
+ "- ask_user \u2014 composer multiple-choice only when blocked on a concrete choice (approach fork, which API). Never for hellos, check-ins, or invented \u201Cwhat should we do?\u201D menus \u2014 reply in chat. Explain options first, description on every option, then wait.",
5067
5265
  "- set_caffeinate \u2014 keep this Mac awake across turns (macOS caffeinate). Turn on for Slack / away-from-keyboard work. Turn OFF when the user says they are done, wrapping up, going to sleep, or no longer need the machine awake. Closing this chat also releases it.",
5068
5266
  "Workspaces:",
5069
5267
  "- add_workspace / remove_workspace \u2014 register or unregister a git repo",
@@ -5353,38 +5551,38 @@ __export(workspaces_exports, {
5353
5551
  syncWorkspacesFromThreads: () => syncWorkspacesFromThreads
5354
5552
  });
5355
5553
  function workspacesFile() {
5356
- return (0, import_node_path13.join)(appDataDir(), "workspaces.json");
5554
+ return (0, import_node_path15.join)(appDataDir(), "workspaces.json");
5357
5555
  }
5358
5556
  function removedWorkspacesFile() {
5359
- return (0, import_node_path13.join)(appDataDir(), "removed-workspaces.json");
5557
+ return (0, import_node_path15.join)(appDataDir(), "removed-workspaces.json");
5360
5558
  }
5361
5559
  function readAll() {
5362
5560
  const path2 = workspacesFile();
5363
- if (!(0, import_node_fs13.existsSync)(path2)) return [];
5561
+ if (!(0, import_node_fs14.existsSync)(path2)) return [];
5364
5562
  try {
5365
- const raw = JSON.parse((0, import_node_fs13.readFileSync)(path2, "utf8"));
5563
+ const raw = JSON.parse((0, import_node_fs14.readFileSync)(path2, "utf8"));
5366
5564
  return Array.isArray(raw) ? raw : [];
5367
5565
  } catch {
5368
5566
  return [];
5369
5567
  }
5370
5568
  }
5371
5569
  function writeAll(list) {
5372
- (0, import_node_fs13.mkdirSync)(appDataDir(), { recursive: true });
5373
- (0, import_node_fs13.writeFileSync)(workspacesFile(), JSON.stringify(list, null, 2), "utf8");
5570
+ (0, import_node_fs14.mkdirSync)(appDataDir(), { recursive: true });
5571
+ (0, import_node_fs14.writeFileSync)(workspacesFile(), JSON.stringify(list, null, 2), "utf8");
5374
5572
  }
5375
5573
  function readRemoved() {
5376
5574
  const path2 = removedWorkspacesFile();
5377
- if (!(0, import_node_fs13.existsSync)(path2)) return /* @__PURE__ */ new Set();
5575
+ if (!(0, import_node_fs14.existsSync)(path2)) return /* @__PURE__ */ new Set();
5378
5576
  try {
5379
- const raw = JSON.parse((0, import_node_fs13.readFileSync)(path2, "utf8"));
5577
+ const raw = JSON.parse((0, import_node_fs14.readFileSync)(path2, "utf8"));
5380
5578
  return new Set(Array.isArray(raw) ? raw.filter((p) => typeof p === "string") : []);
5381
5579
  } catch {
5382
5580
  return /* @__PURE__ */ new Set();
5383
5581
  }
5384
5582
  }
5385
5583
  function writeRemoved(paths) {
5386
- (0, import_node_fs13.mkdirSync)(appDataDir(), { recursive: true });
5387
- (0, import_node_fs13.writeFileSync)(removedWorkspacesFile(), JSON.stringify([...paths].sort(), null, 2), "utf8");
5584
+ (0, import_node_fs14.mkdirSync)(appDataDir(), { recursive: true });
5585
+ (0, import_node_fs14.writeFileSync)(removedWorkspacesFile(), JSON.stringify([...paths].sort(), null, 2), "utf8");
5388
5586
  }
5389
5587
  function rememberRemoved(repoPath) {
5390
5588
  const next = readRemoved();
@@ -5407,7 +5605,7 @@ function listWorkspaces() {
5407
5605
  async function addWorkspace(repoPath) {
5408
5606
  const root = await resolveRepoRoot(repoPath);
5409
5607
  if (!root || root === "/") throw new Error(`Invalid repo path: ${repoPath}`);
5410
- if (!(0, import_node_fs13.existsSync)(root)) throw new Error(`Repo not found: ${root}`);
5608
+ if (!(0, import_node_fs14.existsSync)(root)) throw new Error(`Repo not found: ${root}`);
5411
5609
  forgetRemoved(root);
5412
5610
  await ensureGhPreferOrigin(root);
5413
5611
  const current = readAll();
@@ -5415,7 +5613,7 @@ async function addWorkspace(repoPath) {
5415
5613
  if (existing) return existing;
5416
5614
  const next = {
5417
5615
  path: root,
5418
- name: (0, import_node_path13.basename)(root),
5616
+ name: (0, import_node_path15.basename)(root),
5419
5617
  addedAt: (/* @__PURE__ */ new Date()).toISOString()
5420
5618
  };
5421
5619
  writeAll([...current, next]);
@@ -5437,10 +5635,10 @@ function syncWorkspacesFromThreads(repoPaths) {
5437
5635
  if (!path2 || path2 === "/" || isGlobalRepoPath(path2) || byPath.has(path2) || removed.has(path2)) {
5438
5636
  continue;
5439
5637
  }
5440
- if (!(0, import_node_fs13.existsSync)(path2)) continue;
5638
+ if (!(0, import_node_fs14.existsSync)(path2)) continue;
5441
5639
  const ws = {
5442
5640
  path: path2,
5443
- name: (0, import_node_path13.basename)(path2),
5641
+ name: (0, import_node_path15.basename)(path2),
5444
5642
  addedAt: (/* @__PURE__ */ new Date()).toISOString()
5445
5643
  };
5446
5644
  byPath.set(path2, ws);
@@ -5450,12 +5648,12 @@ function syncWorkspacesFromThreads(repoPaths) {
5450
5648
  if (dirty) writeAll(next);
5451
5649
  return next.sort((a, b) => a.name.localeCompare(b.name));
5452
5650
  }
5453
- var import_node_fs13, import_node_path13;
5651
+ var import_node_fs14, import_node_path15;
5454
5652
  var init_workspaces = __esm({
5455
5653
  "src/store/workspaces.ts"() {
5456
5654
  "use strict";
5457
- import_node_fs13 = require("fs");
5458
- import_node_path13 = require("path");
5655
+ import_node_fs14 = require("fs");
5656
+ import_node_path15 = require("path");
5459
5657
  init_paths();
5460
5658
  init_global_workspace();
5461
5659
  init_worktree();
@@ -5464,32 +5662,32 @@ var init_workspaces = __esm({
5464
5662
 
5465
5663
  // src/brightsy/config.ts
5466
5664
  function brightsyConfigPath() {
5467
- return (0, import_node_path14.join)((0, import_node_os5.homedir)(), ".brightsy", "config.json");
5665
+ return (0, import_node_path16.join)((0, import_node_os6.homedir)(), ".brightsy", "config.json");
5468
5666
  }
5469
5667
  function loadBrightsyConfig() {
5470
5668
  const path2 = brightsyConfigPath();
5471
- if (!(0, import_node_fs14.existsSync)(path2)) {
5669
+ if (!(0, import_node_fs15.existsSync)(path2)) {
5472
5670
  throw new Error("Brightsy not logged in \u2014 run `brightsy login` first");
5473
5671
  }
5474
- const raw = JSON.parse((0, import_node_fs14.readFileSync)(path2, "utf8"));
5672
+ const raw = JSON.parse((0, import_node_fs15.readFileSync)(path2, "utf8"));
5475
5673
  if (!raw.access_token || !raw.account_id) {
5476
5674
  throw new Error("Brightsy config incomplete \u2014 run `brightsy login`");
5477
5675
  }
5478
5676
  return raw;
5479
5677
  }
5480
5678
  function saveBrightsyConfig(cfg) {
5481
- (0, import_node_fs14.writeFileSync)(brightsyConfigPath(), `${JSON.stringify(cfg, null, 2)}
5679
+ (0, import_node_fs15.writeFileSync)(brightsyConfigPath(), `${JSON.stringify(cfg, null, 2)}
5482
5680
  `, {
5483
5681
  mode: 384
5484
5682
  });
5485
5683
  }
5486
- var import_node_fs14, import_node_os5, import_node_path14;
5684
+ var import_node_fs15, import_node_os6, import_node_path16;
5487
5685
  var init_config = __esm({
5488
5686
  "src/brightsy/config.ts"() {
5489
5687
  "use strict";
5490
- import_node_fs14 = require("fs");
5491
- import_node_os5 = require("os");
5492
- import_node_path14 = require("path");
5688
+ import_node_fs15 = require("fs");
5689
+ import_node_os6 = require("os");
5690
+ import_node_path16 = require("path");
5493
5691
  }
5494
5692
  });
5495
5693
 
@@ -5592,22 +5790,22 @@ __export(connected_teams_exports, {
5592
5790
  listConnectedBrightsyTeams: () => listConnectedBrightsyTeams
5593
5791
  });
5594
5792
  function storePath() {
5595
- return (0, import_node_path15.join)(appDataDir(), "brightsy-teams.json");
5793
+ return (0, import_node_path17.join)(appDataDir(), "brightsy-teams.json");
5596
5794
  }
5597
5795
  function readStore() {
5598
5796
  const path2 = storePath();
5599
- if (!(0, import_node_fs15.existsSync)(path2)) return [];
5797
+ if (!(0, import_node_fs16.existsSync)(path2)) return [];
5600
5798
  try {
5601
- const parsed = JSON.parse((0, import_node_fs15.readFileSync)(path2, "utf8"));
5799
+ const parsed = JSON.parse((0, import_node_fs16.readFileSync)(path2, "utf8"));
5602
5800
  return Array.isArray(parsed.teams) ? parsed.teams : [];
5603
5801
  } catch {
5604
5802
  return [];
5605
5803
  }
5606
5804
  }
5607
5805
  function writeStore(teams) {
5608
- (0, import_node_fs15.mkdirSync)(appDataDir(), { recursive: true });
5806
+ (0, import_node_fs16.mkdirSync)(appDataDir(), { recursive: true });
5609
5807
  const path2 = storePath();
5610
- (0, import_node_fs15.writeFileSync)(path2, `${JSON.stringify({ teams }, null, 2)}
5808
+ (0, import_node_fs16.writeFileSync)(path2, `${JSON.stringify({ teams }, null, 2)}
5611
5809
  `, {
5612
5810
  mode: 384
5613
5811
  });
@@ -5769,12 +5967,12 @@ function brightsyMcpServerName(slug) {
5769
5967
  const cleaned = slug.replace(/[^A-Za-z0-9_-]/g, "_").replace(/^_+|_+$/g, "");
5770
5968
  return `brightsy_${cleaned || "team"}`;
5771
5969
  }
5772
- var import_node_fs15, import_node_path15;
5970
+ var import_node_fs16, import_node_path17;
5773
5971
  var init_connected_teams = __esm({
5774
5972
  "src/brightsy/connected-teams.ts"() {
5775
5973
  "use strict";
5776
- import_node_fs15 = require("fs");
5777
- import_node_path15 = require("path");
5974
+ import_node_fs16 = require("fs");
5975
+ import_node_path17 = require("path");
5778
5976
  init_paths();
5779
5977
  init_accounts();
5780
5978
  init_config();
@@ -5911,17 +6109,27 @@ function extractJsonErrorMessage(obj) {
5911
6109
  }
5912
6110
  return null;
5913
6111
  }
6112
+ function isPinnedStderrLine(line) {
6113
+ if (/^\s*at\s/.test(line)) return false;
6114
+ return /cannot find (?:package|module)|ERR_MODULE_NOT_FOUND|cursor startup failed:/i.test(
6115
+ line
6116
+ );
6117
+ }
5914
6118
  function pushTurnStderr(tail, line, maxLines = 12) {
5915
6119
  const trimmed = line.trim();
5916
6120
  if (!trimmed) return;
5917
6121
  if (NODE_VERSION_FOOTER.test(trimmed)) return;
5918
6122
  if (/^reconnecting\.\.\./i.test(trimmed)) return;
5919
6123
  tail.push(trimmed);
5920
- while (tail.length > maxLines) tail.shift();
6124
+ while (tail.length > maxLines) {
6125
+ const dropIdx = tail.findIndex((l) => !isPinnedStderrLine(l));
6126
+ if (dropIdx === -1) tail.shift();
6127
+ else tail.splice(dropIdx, 1);
6128
+ }
5921
6129
  }
5922
6130
  function looksLikeMinifiedJsDump(line) {
5923
6131
  if (line.length < 200) return false;
5924
- return /yield Promise\.all/.test(line) || /\(0,[A-Za-z$]\.\w+\)/.test(line) || /CURSOR_RIPGREP_PATH/.test(line);
6132
+ return /yield Promise\.all/.test(line) || /\(0,[A-Za-z$]\.\w+\)/.test(line) || /CURSOR_RIPGREP_PATH/.test(line) || /findFilesWithRipgrep/.test(line) || /@cursor\/sdk\/dist\//.test(line);
5925
6133
  }
5926
6134
  function looksLikeNestedElectronCrash(line) {
5927
6135
  return /HasCustomHostObject|ElectronInitializeICUandStartNode/i.test(line);
@@ -5936,7 +6144,15 @@ function summarizeTurnStderr(tail, maxChars = 500) {
5936
6144
  const cursorStartup = [...tail].reverse().find((line) => /cursor startup failed:/i.test(line));
5937
6145
  if (cursorStartup) return clipStderr(cursorStartup, maxChars);
5938
6146
  if (tail.some(looksLikeNestedElectronCrash)) return NESTED_ELECTRON_SUMMARY;
5939
- const moduleMissing = [...tail].reverse().find((line) => /cannot find module/i.test(line));
6147
+ if (tail.some(
6148
+ (line) => /\[resource_exhausted\]|resource_exhausted/i.test(line) || /findFilesWithRipgrep/.test(line)
6149
+ )) {
6150
+ return clipStderr(
6151
+ "Cursor local file search failed (ripgrep / resource_exhausted). Wait a minute and retry; if it keeps happening, check Cursor usage.",
6152
+ maxChars
6153
+ );
6154
+ }
6155
+ const moduleMissing = [...tail].reverse().find((line) => /cannot find (?:package|module)/i.test(line));
5940
6156
  if (moduleMissing) return clipStderr(moduleMissing, maxChars);
5941
6157
  const useful = tail.filter((line) => !looksLikeMinifiedJsDump(line));
5942
6158
  if (useful.length === 0 && tail.some(looksLikeMinifiedJsDump)) {
@@ -5955,7 +6171,7 @@ function looksLikeAgentFailureMessage(text3) {
5955
6171
  if (!lower) return false;
5956
6172
  return /you've hit your|hit your (session|weekly|opus) limit|usage limit/.test(lower) || /credit balance is too low|out of credits|insufficient.?quota|quota.?exceeded/.test(lower) || /invalid user api key|invalid api key|not logged in|not authenticated|unauthorized/.test(
5957
6173
  lower
5958
- ) || /\b429\b|too many requests|rate.?limit/.test(lower) || /prompt is too long|context.*(too long|exceed)|conversation too long/.test(lower);
6174
+ ) || /\b429\b|too many requests|rate.?limit/.test(lower) || /prompt is too long|context.*(too long|exceed)|conversation too long/.test(lower) || /\[resource_exhausted\]|resource_exhausted/.test(lower) || /findFilesWithRipgrep/.test(text3);
5959
6175
  }
5960
6176
  function fallbackTurnFailDetail(assistantText) {
5961
6177
  const t = assistantText.trim();
@@ -5977,6 +6193,9 @@ function humanizeAgentFailDetail(detail) {
5977
6193
  if (/\b429\b|rate.?limit|too many requests/.test(lower)) {
5978
6194
  return `${raw} \u2014 wait a moment and retry.`;
5979
6195
  }
6196
+ if (/\[resource_exhausted\]|resource_exhausted|findfileswithripgrep/.test(lower)) {
6197
+ return "Cursor local file search failed (ripgrep / resource_exhausted). Wait a minute and retry; if it keeps happening, check Cursor usage.";
6198
+ }
5980
6199
  if (/invalid user api key|invalid api key|not logged in|not authenticated|unauthorized|authentication|please run.*login|codex login|claude auth|cursor api/.test(
5981
6200
  lower
5982
6201
  )) {
@@ -6303,11 +6522,11 @@ async function syncCliForTarget(accountId) {
6303
6522
  }
6304
6523
  applyConnectedTeamToCli(team);
6305
6524
  }
6306
- var import_node_fs16, brightsyAdapter;
6525
+ var import_node_fs17, brightsyAdapter;
6307
6526
  var init_brightsy = __esm({
6308
6527
  "src/agents/brightsy.ts"() {
6309
6528
  "use strict";
6310
- import_node_fs16 = require("fs");
6529
+ import_node_fs17 = require("fs");
6311
6530
  init_run();
6312
6531
  init_connected_teams();
6313
6532
  init_config();
@@ -6322,7 +6541,7 @@ var init_brightsy = __esm({
6322
6541
  async detect() {
6323
6542
  const brightsy = resolveAgentExecutable("brightsy");
6324
6543
  if (brightsy !== "brightsy") {
6325
- if (!(0, import_node_fs16.existsSync)(brightsy)) {
6544
+ if (!(0, import_node_fs17.existsSync)(brightsy)) {
6326
6545
  return {
6327
6546
  agent: "brightsy",
6328
6547
  installed: false,
@@ -6457,13 +6676,38 @@ var init_profile = __esm({
6457
6676
 
6458
6677
  // src/agents/node-launch.ts
6459
6678
  function isAsarPath(filePath) {
6679
+ if (/\.asar\.unpacked([/\\]|$)/.test(filePath)) return false;
6460
6680
  return /\.asar([/\\]|$)/.test(filePath);
6461
6681
  }
6682
+ function unpackedAsarPath(filePath) {
6683
+ if (!isAsarPath(filePath)) return null;
6684
+ const unpacked = filePath.replace(/\.asar(?=[/\\])/, ".asar.unpacked");
6685
+ if (unpacked === filePath) return null;
6686
+ return (0, import_node_fs18.existsSync)(unpacked) ? unpacked : null;
6687
+ }
6688
+ function nodeReadableScriptPath(scriptPath) {
6689
+ return unpackedAsarPath(scriptPath) ?? scriptPath;
6690
+ }
6691
+ async function findSystemNode() {
6692
+ const whichNode = await run("which", ["node"], { reject: false });
6693
+ const fromWhich = whichNode.exitCode === 0 && whichNode.stdout.trim() ? whichNode.stdout.trim() : "";
6694
+ if (fromWhich && !isElectronLikeCommand(fromWhich)) return fromWhich;
6695
+ const fallbacks = [
6696
+ ...WELL_KNOWN_NODE_BINS,
6697
+ (0, import_node_path18.join)((0, import_node_os7.homedir)(), ".local/share/fnm/aliases/default/bin/node"),
6698
+ (0, import_node_path18.join)((0, import_node_os7.homedir)(), ".nvm/current/bin/node")
6699
+ ];
6700
+ for (const bin of fallbacks) {
6701
+ if ((0, import_node_fs18.existsSync)(bin) && !isElectronLikeCommand(bin)) return bin;
6702
+ }
6703
+ return null;
6704
+ }
6462
6705
  function applyNodeLaunch(launch, args) {
6706
+ const readableArgs = args.map(nodeReadableScriptPath);
6463
6707
  if (!launch.env.ELECTRON_RUN_AS_NODE) {
6464
- return { file: launch.file, args, env: launch.env };
6708
+ return { file: launch.file, args: readableArgs, env: launch.env };
6465
6709
  }
6466
- const wrapped = wrapElectronAsNodeLaunch(launch.file, args);
6710
+ const wrapped = wrapElectronAsNodeLaunch(launch.file, readableArgs);
6467
6711
  if (process.platform === "win32") {
6468
6712
  return { file: wrapped.file, args: wrapped.args, env: launch.env };
6469
6713
  }
@@ -6472,27 +6716,73 @@ function applyNodeLaunch(launch, args) {
6472
6716
  return { file: wrapped.file, args: wrapped.args, env };
6473
6717
  }
6474
6718
  async function resolveNodeLaunch(scriptPath) {
6475
- if (isAsarPath(scriptPath)) {
6476
- return {
6477
- file: process.execPath,
6478
- env: { ELECTRON_RUN_AS_NODE: "1" }
6479
- };
6480
- }
6481
- const whichNode = await run("which", ["node"], { reject: false });
6482
- const nodeBin = whichNode.exitCode === 0 && whichNode.stdout.trim() ? whichNode.stdout.trim() : null;
6483
- if (nodeBin) {
6484
- return { file: nodeBin, env: {} };
6719
+ const script = nodeReadableScriptPath(scriptPath);
6720
+ if (!isAsarPath(script)) {
6721
+ const nodeBin = await findSystemNode();
6722
+ if (nodeBin) {
6723
+ return { file: nodeBin, env: {} };
6724
+ }
6485
6725
  }
6486
6726
  return {
6487
6727
  file: process.execPath,
6488
6728
  env: { ELECTRON_RUN_AS_NODE: "1" }
6489
6729
  };
6490
6730
  }
6731
+ var import_node_fs18, import_node_os7, import_node_path18, WELL_KNOWN_NODE_BINS;
6491
6732
  var init_node_launch = __esm({
6492
6733
  "src/agents/node-launch.ts"() {
6493
6734
  "use strict";
6735
+ import_node_fs18 = require("fs");
6736
+ import_node_os7 = require("os");
6737
+ import_node_path18 = require("path");
6494
6738
  init_nested_electron_env();
6495
6739
  init_run();
6740
+ WELL_KNOWN_NODE_BINS = [
6741
+ "/opt/homebrew/bin/node",
6742
+ "/usr/local/bin/node"
6743
+ ];
6744
+ }
6745
+ });
6746
+
6747
+ // src/agents/packaged-runtime.ts
6748
+ function electronResourcesPath() {
6749
+ const resources = process.resourcesPath;
6750
+ if (typeof resources !== "string" || !resources) return null;
6751
+ return resources;
6752
+ }
6753
+ function packagedCursorRuntimeDir() {
6754
+ const resources = electronResourcesPath();
6755
+ if (!resources) return null;
6756
+ const dir = (0, import_node_path19.join)(resources, "cursor-runtime");
6757
+ if (!(0, import_node_fs19.existsSync)((0, import_node_path19.join)(dir, "core-dist", "agents", "cursor-runner.js"))) return null;
6758
+ return dir;
6759
+ }
6760
+ function packagedCursorRunnerPath() {
6761
+ const dir = packagedCursorRuntimeDir();
6762
+ return dir ? (0, import_node_path19.join)(dir, "core-dist", "agents", "cursor-runner.js") : null;
6763
+ }
6764
+ function packagedMcpDir() {
6765
+ const resources = electronResourcesPath();
6766
+ if (!resources) return null;
6767
+ const dir = (0, import_node_path19.join)(resources, "sideboard-mcp");
6768
+ if (!(0, import_node_fs19.existsSync)((0, import_node_path19.join)(dir, "core-dist", "mcp", "run-stdio.js"))) return null;
6769
+ return dir;
6770
+ }
6771
+ function packagedMcpStdioPath() {
6772
+ const dir = packagedMcpDir();
6773
+ return dir ? (0, import_node_path19.join)(dir, "core-dist", "mcp", "run-stdio.js") : null;
6774
+ }
6775
+ function packagedCursorRipgrepCandidate(platformPkg, binName) {
6776
+ const dir = packagedCursorRuntimeDir();
6777
+ if (!dir) return null;
6778
+ return (0, import_node_path19.join)(dir, "node_modules", platformPkg, "bin", binName);
6779
+ }
6780
+ var import_node_fs19, import_node_path19;
6781
+ var init_packaged_runtime = __esm({
6782
+ "src/agents/packaged-runtime.ts"() {
6783
+ "use strict";
6784
+ import_node_fs19 = require("fs");
6785
+ import_node_path19 = require("path");
6496
6786
  }
6497
6787
  });
6498
6788
 
@@ -6579,35 +6869,37 @@ function corePackageDir() {
6579
6869
  try {
6580
6870
  const url = import_meta.url;
6581
6871
  if (typeof url === "string" && url.length > 0) {
6582
- return (0, import_node_path16.dirname)((0, import_node_url.fileURLToPath)(url));
6872
+ return (0, import_node_path20.dirname)((0, import_node_url.fileURLToPath)(url));
6583
6873
  }
6584
6874
  } catch {
6585
6875
  }
6586
6876
  try {
6587
- const req = (0, import_node_module.createRequire)((0, import_node_path16.join)(process.cwd(), "package.json"));
6588
- return (0, import_node_path16.dirname)(req.resolve("@sideboard-ai/core"));
6877
+ const req = (0, import_node_module.createRequire)((0, import_node_path20.join)(process.cwd(), "package.json"));
6878
+ return (0, import_node_path20.dirname)(req.resolve("@sideboard-ai/core"));
6589
6879
  } catch {
6590
6880
  return process.cwd();
6591
6881
  }
6592
6882
  }
6593
6883
  function findSideboardMcpJsEntry() {
6594
6884
  const override = process.env.SIDEBOARD_MCP_ENTRY?.trim() || process.env.SIDEBOARD_CLI?.trim();
6595
- if (override && (0, import_node_fs17.existsSync)(override)) return override;
6885
+ if (override && (0, import_node_fs20.existsSync)(override)) return override;
6886
+ const packaged = packagedMcpStdioPath();
6887
+ if (packaged) return packaged;
6596
6888
  let dir = corePackageDir();
6597
6889
  for (let i = 0; i < 10; i++) {
6598
6890
  const candidates = [
6599
- (0, import_node_path16.join)(dir, "mcp/run-stdio.js"),
6600
- (0, import_node_path16.join)(dir, "mcp/run-stdio.cjs"),
6601
- (0, import_node_path16.join)(dir, "dist/mcp/run-stdio.js"),
6602
- (0, import_node_path16.join)(dir, "dist/mcp/run-stdio.cjs"),
6603
- (0, import_node_path16.join)(dir, "packages/core/dist/mcp/run-stdio.js"),
6604
- (0, import_node_path16.join)(dir, "packages/cli/dist/index.js"),
6605
- (0, import_node_path16.join)(dir, "cli/dist/index.js")
6891
+ (0, import_node_path20.join)(dir, "mcp/run-stdio.js"),
6892
+ (0, import_node_path20.join)(dir, "mcp/run-stdio.cjs"),
6893
+ (0, import_node_path20.join)(dir, "dist/mcp/run-stdio.js"),
6894
+ (0, import_node_path20.join)(dir, "dist/mcp/run-stdio.cjs"),
6895
+ (0, import_node_path20.join)(dir, "packages/core/dist/mcp/run-stdio.js"),
6896
+ (0, import_node_path20.join)(dir, "packages/cli/dist/index.js"),
6897
+ (0, import_node_path20.join)(dir, "cli/dist/index.js")
6606
6898
  ];
6607
6899
  for (const p of candidates) {
6608
- if ((0, import_node_fs17.existsSync)(p)) return p;
6900
+ if ((0, import_node_fs20.existsSync)(p) && !isAsarPath(p)) return p;
6609
6901
  }
6610
- const parent = (0, import_node_path16.dirname)(dir);
6902
+ const parent = (0, import_node_path20.dirname)(dir);
6611
6903
  if (parent === dir) break;
6612
6904
  dir = parent;
6613
6905
  }
@@ -6619,12 +6911,14 @@ async function resolveSideboardMcpServer() {
6619
6911
  const isCli = /[/\\]cli[/\\]dist[/\\]index\.js$/.test(entry);
6620
6912
  const scriptArgs = isCli ? [entry, "mcp"] : [entry];
6621
6913
  const launch = applyNodeLaunch(await resolveNodeLaunch(entry), scriptArgs);
6622
- return {
6623
- name: "sideboard",
6624
- command: launch.file,
6625
- args: launch.args,
6626
- ...Object.keys(launch.env).length > 0 ? { env: launch.env } : {}
6627
- };
6914
+ if (launch.file !== "/bin/sh" && !isElectronLikeCommand(launch.file)) {
6915
+ return {
6916
+ name: "sideboard",
6917
+ command: launch.file,
6918
+ args: launch.args,
6919
+ ...Object.keys(launch.env).length > 0 ? { env: launch.env } : {}
6920
+ };
6921
+ }
6628
6922
  }
6629
6923
  const which = await run("which", ["sideboard"], { reject: false });
6630
6924
  if (which.exitCode === 0 && which.stdout.trim()) {
@@ -6647,7 +6941,10 @@ async function buildInjectedMcpServers(opts) {
6647
6941
  sideboard.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID = orchId;
6648
6942
  }
6649
6943
  try {
6650
- Object.assign(sideboard.env, await resolveAgentGitAuthEnv(sideboard.env));
6944
+ mergeAgentGitAuthEnv(
6945
+ sideboard.env,
6946
+ await resolveAgentGitAuthEnv(sideboard.env)
6947
+ );
6651
6948
  } catch {
6652
6949
  }
6653
6950
  servers.push(sideboard);
@@ -6669,9 +6966,6 @@ async function buildInjectedMcpServers(opts) {
6669
6966
  }
6670
6967
  return servers;
6671
6968
  }
6672
- function shSingleQuote(value) {
6673
- return `'${value.replace(/'/g, `'\\''`)}'`;
6674
- }
6675
6969
  function cursorSafeMcpLaunch(command, args) {
6676
6970
  if (process.platform === "win32") {
6677
6971
  return args && args.length > 0 ? { command, args } : { command };
@@ -6679,31 +6973,13 @@ function cursorSafeMcpLaunch(command, args) {
6679
6973
  const unwrapped = unwrapStrippedElectronLaunch(command, args);
6680
6974
  const file = unwrapped?.file ?? command;
6681
6975
  const fileArgs = unwrapped?.args ?? args ?? [];
6682
- if (!isElectronLikeCommand(file)) {
6683
- return fileArgs.length > 0 ? { command: file, args: fileArgs } : { command: file };
6684
- }
6685
- const dir = (0, import_node_path16.join)(appDataDir(), "mcp-launch");
6686
- (0, import_node_fs17.mkdirSync)(dir, { recursive: true });
6687
- const wrap = (0, import_node_path16.join)(dir, "cursor-electron-as-node.sh");
6688
- const execLine = [file, ...fileArgs].map(shSingleQuote).join(" ");
6689
- (0, import_node_fs17.writeFileSync)(
6690
- wrap,
6691
- [
6692
- "#!/bin/sh",
6693
- "vars=`printenv | awk -F= '/^(ELECTRON_|CHROME_)/{print $1}'`",
6694
- '[ -n "$vars" ] && unset $vars',
6695
- "export ELECTRON_RUN_AS_NODE=1",
6696
- `exec ${execLine} "$@"`,
6697
- ""
6698
- ].join("\n"),
6699
- { mode: 493 }
6700
- );
6701
- return { command: wrap };
6976
+ return fileArgs.length > 0 ? { command: file, args: fileArgs } : { command: file };
6702
6977
  }
6703
6978
  function mcpSpawnEnv(env) {
6704
6979
  if (!env) return void 0;
6705
6980
  const out = { ...env };
6706
6981
  delete out.ELECTRON_RUN_AS_NODE;
6982
+ delete out.ELECTRON_RUN_AS_NODE;
6707
6983
  return Object.keys(out).length > 0 ? out : void 0;
6708
6984
  }
6709
6985
  function toCursorMcpServers(servers) {
@@ -6763,22 +7039,22 @@ function writeMcpServersConfig(servers) {
6763
7039
  ...env ? { env } : {}
6764
7040
  };
6765
7041
  }
6766
- const dir = (0, import_node_fs17.mkdtempSync)((0, import_node_path16.join)((0, import_node_os6.tmpdir)(), "sideboard-mcp-"));
6767
- const cfgPath = (0, import_node_path16.join)(dir, "mcp.json");
6768
- (0, import_node_fs17.writeFileSync)(cfgPath, JSON.stringify({ mcpServers }, null, 2));
7042
+ const dir = (0, import_node_fs20.mkdtempSync)((0, import_node_path20.join)((0, import_node_os8.tmpdir)(), "sideboard-mcp-"));
7043
+ const cfgPath = (0, import_node_path20.join)(dir, "mcp.json");
7044
+ (0, import_node_fs20.writeFileSync)(cfgPath, JSON.stringify({ mcpServers }, null, 2));
6769
7045
  return cfgPath;
6770
7046
  }
6771
7047
  async function writeInjectedMcpConfig(opts) {
6772
7048
  return writeMcpServersConfig(await buildInjectedMcpServers(opts));
6773
7049
  }
6774
- var import_node_fs17, import_node_module, import_node_os6, import_node_path16, import_node_url, import_meta, SIDEBOARD_MCP_ALLOWED_TOOLS, SIDEBOARD_ARTIFACT_MCP_ALLOWED_TOOLS, BRIGHTSY_MCP_ALLOWED_TOOLS, brightsyMcpCommandCache, BRIGHTSY_WORD;
7050
+ var import_node_fs20, import_node_module, import_node_os8, import_node_path20, import_node_url, import_meta, SIDEBOARD_MCP_ALLOWED_TOOLS, SIDEBOARD_ARTIFACT_MCP_ALLOWED_TOOLS, BRIGHTSY_MCP_ALLOWED_TOOLS, brightsyMcpCommandCache, BRIGHTSY_WORD;
6775
7051
  var init_injected_mcp = __esm({
6776
7052
  "src/agents/injected-mcp.ts"() {
6777
7053
  "use strict";
6778
- import_node_fs17 = require("fs");
7054
+ import_node_fs20 = require("fs");
6779
7055
  import_node_module = require("module");
6780
- import_node_os6 = require("os");
6781
- import_node_path16 = require("path");
7056
+ import_node_os8 = require("os");
7057
+ import_node_path20 = require("path");
6782
7058
  import_node_url = require("url");
6783
7059
  init_run();
6784
7060
  init_config();
@@ -6787,6 +7063,7 @@ var init_injected_mcp = __esm({
6787
7063
  init_app_settings();
6788
7064
  init_paths();
6789
7065
  init_node_launch();
7066
+ init_packaged_runtime();
6790
7067
  init_nested_electron_env();
6791
7068
  init_git_auth_mode();
6792
7069
  import_meta = {};
@@ -6860,7 +7137,7 @@ var PLAN_MODE_INSTRUCTION;
6860
7137
  var init_types = __esm({
6861
7138
  "src/agents/types.ts"() {
6862
7139
  "use strict";
6863
- PLAN_MODE_INSTRUCTION = "Plan mode is active and must remain active until the user turns Plan mode off in the UI (or Approves / Hands off the plan). Analyze the codebase, search and read files as needed, and produce or refine a clear implementation plan. Do not modify, create, or delete any project files except via Sideboard MCP present_plan (writes .context/attachments/plan.md). When you need a clarifying decision (approach forks, auth choice, scope): (1) first write a short chat message that explains the decision and what each option means (tradeoffs, when to pick it) \u2014 do not leave the user staring at bare labels; (2) then call Sideboard MCP ask_user with the same options, including a description on every option. Sideboard shows questions in the composer and mirrors them in chat. After ask_user, wait for the user's next message with their answers before finalizing the plan. When the plan is ready for approval: (1) call present_plan with the full markdown plan (title + content) so Sideboard saves .context/attachments/plan.md and shows it in chat for Approve / Hand off / Copy; (2) Claude should also call ExitPlanMode after present_plan. Do not skip present_plan \u2014 the plan must be a markdown file, not only chat prose.";
7140
+ PLAN_MODE_INSTRUCTION = "Plan mode is active and must remain active until the user turns Plan mode off in the UI (or Approves / Hands off the plan). Analyze the codebase, search and read files as needed, and produce or refine a clear implementation plan. Do not modify, create, or delete any project files except via Sideboard MCP present_plan (writes .context/attachments/plan.md). When you need a clarifying decision (approach forks, auth choice, scope) \u2014 not greetings, check-ins, or an invented task menu: (1) first write a short chat message that explains the decision and what each option means (tradeoffs, when to pick it) \u2014 do not leave the user staring at bare labels; (2) then call Sideboard MCP ask_user with the same options, including a description on every option. If one option is the obvious default, proceed without asking. Sideboard shows questions in the composer and mirrors them in chat. After ask_user, wait for the user's next message with their answers before finalizing the plan. When the plan is ready for approval: (1) call present_plan with the full markdown plan (title + content) so Sideboard saves .context/attachments/plan.md and shows it in chat for Approve / Hand off / Copy; (2) Claude should also call ExitPlanMode after present_plan. Do not skip present_plan \u2014 the plan must be a markdown file, not only chat prose.";
6864
7141
  }
6865
7142
  });
6866
7143
 
@@ -6970,11 +7247,11 @@ function parseIssuesJson(raw) {
6970
7247
  }
6971
7248
  return [];
6972
7249
  }
6973
- var import_node_fs18, BASE_ALLOWED_TOOLS, CLAUDE_CHROME_ALLOWED_TOOLS, CLAUDE_PROMPT_ARG_MAX, claudeAdapter;
7250
+ var import_node_fs21, BASE_ALLOWED_TOOLS, CLAUDE_CHROME_ALLOWED_TOOLS, CLAUDE_PROMPT_ARG_MAX, claudeAdapter;
6974
7251
  var init_claude = __esm({
6975
7252
  "src/agents/claude.ts"() {
6976
7253
  "use strict";
6977
- import_node_fs18 = require("fs");
7254
+ import_node_fs21 = require("fs");
6978
7255
  init_run();
6979
7256
  init_app_settings();
6980
7257
  init_claude_mcp();
@@ -7003,7 +7280,7 @@ var init_claude = __esm({
7003
7280
  async detect() {
7004
7281
  const claude = resolveClaudeExecutable();
7005
7282
  if (claude !== "claude") {
7006
- if (!(0, import_node_fs18.existsSync)(claude)) {
7283
+ if (!(0, import_node_fs21.existsSync)(claude)) {
7007
7284
  return {
7008
7285
  agent: "claude",
7009
7286
  installed: false,
@@ -7240,7 +7517,7 @@ async function listCodexModels() {
7240
7517
  if (codex === "codex") {
7241
7518
  const which = await run("which", ["codex"], { reject: false });
7242
7519
  if (which.exitCode !== 0) return FALLBACK_CODEX_MODELS;
7243
- } else if (!(0, import_node_fs19.existsSync)(codex)) {
7520
+ } else if (!(0, import_node_fs22.existsSync)(codex)) {
7244
7521
  return FALLBACK_CODEX_MODELS;
7245
7522
  }
7246
7523
  const listed = await run(codex, ["debug", "models"], { reject: false });
@@ -7275,12 +7552,12 @@ function usageFromCodex(usage) {
7275
7552
  }
7276
7553
  function codexConfigHasNetworkAccess() {
7277
7554
  const candidates = [
7278
- (0, import_node_path17.join)((0, import_node_os7.homedir)(), ".codex", "config.toml"),
7279
- (0, import_node_path17.join)((0, import_node_os7.homedir)(), ".config", "codex", "config.toml")
7555
+ (0, import_node_path21.join)((0, import_node_os9.homedir)(), ".codex", "config.toml"),
7556
+ (0, import_node_path21.join)((0, import_node_os9.homedir)(), ".config", "codex", "config.toml")
7280
7557
  ];
7281
7558
  for (const path2 of candidates) {
7282
- if (!(0, import_node_fs19.existsSync)(path2)) continue;
7283
- const text3 = (0, import_node_fs19.readFileSync)(path2, "utf8");
7559
+ if (!(0, import_node_fs22.existsSync)(path2)) continue;
7560
+ const text3 = (0, import_node_fs22.readFileSync)(path2, "utf8");
7284
7561
  if (/network_access\s*=\s*true/.test(text3)) return true;
7285
7562
  }
7286
7563
  return false;
@@ -7312,21 +7589,21 @@ function asRecord(value) {
7312
7589
  return void 0;
7313
7590
  }
7314
7591
  function codexLooksAuthenticated() {
7315
- const authPath = (0, import_node_path17.join)((0, import_node_os7.homedir)(), ".codex", "auth.json");
7316
- if (!(0, import_node_fs19.existsSync)(authPath)) return false;
7592
+ const authPath = (0, import_node_path21.join)((0, import_node_os9.homedir)(), ".codex", "auth.json");
7593
+ if (!(0, import_node_fs22.existsSync)(authPath)) return false;
7317
7594
  try {
7318
- return (0, import_node_fs19.statSync)(authPath).size > 2;
7595
+ return (0, import_node_fs22.statSync)(authPath).size > 2;
7319
7596
  } catch {
7320
7597
  return false;
7321
7598
  }
7322
7599
  }
7323
- var import_node_fs19, import_node_os7, import_node_path17, CODEX_PROMPT_ARG_MAX, FALLBACK_CODEX_MODELS, cachedCodexModels, CODEX_MODEL_CACHE_MS, codexAdapter;
7600
+ var import_node_fs22, import_node_os9, import_node_path21, CODEX_PROMPT_ARG_MAX, FALLBACK_CODEX_MODELS, cachedCodexModels, CODEX_MODEL_CACHE_MS, codexAdapter;
7324
7601
  var init_codex = __esm({
7325
7602
  "src/agents/codex.ts"() {
7326
7603
  "use strict";
7327
- import_node_fs19 = require("fs");
7328
- import_node_os7 = require("os");
7329
- import_node_path17 = require("path");
7604
+ import_node_fs22 = require("fs");
7605
+ import_node_os9 = require("os");
7606
+ import_node_path21 = require("path");
7330
7607
  init_run();
7331
7608
  init_app_settings();
7332
7609
  init_global_workspace();
@@ -7351,7 +7628,7 @@ var init_codex = __esm({
7351
7628
  async detect() {
7352
7629
  const codex = resolveAgentExecutable("codex");
7353
7630
  if (codex !== "codex") {
7354
- if (!(0, import_node_fs19.existsSync)(codex)) {
7631
+ if (!(0, import_node_fs22.existsSync)(codex)) {
7355
7632
  return {
7356
7633
  agent: "codex",
7357
7634
  installed: false,
@@ -7424,8 +7701,13 @@ var init_codex = __esm({
7424
7701
  // `codex exec` rejects `--ask-for-approval` (global-only on newer CLIs).
7425
7702
  "-c",
7426
7703
  'approval_policy="never"',
7427
- // Seatbelt cannot use the login Keychain; default policy also strips GH_TOKEN.
7428
- ...codexUnattendedGitConfigArgs(mode.codexSandbox),
7704
+ // Seatbelt cannot use the login Keychain; inherit GH_CONFIG_DIR / GIT_CONFIG_*.
7705
+ // Default policy also strips *TOKEN*. Linked worktrees need the main
7706
+ // repo `.git` (+ `.git/worktrees/<name>`) as writable_roots so git commit
7707
+ // can create index.lock.
7708
+ ...codexUnattendedGitConfigArgs(mode.codexSandbox, {
7709
+ writableRoots: mode.codexSandbox === "workspace-write" ? await resolveCodexGitWritableRoots(thread.worktreePath) : []
7710
+ }),
7429
7711
  ...model ? ["--model", model] : [],
7430
7712
  ...mcpOverrides
7431
7713
  ];
@@ -7746,6 +8028,74 @@ var init_cursor_events = __esm({
7746
8028
  }
7747
8029
  });
7748
8030
 
8031
+ // src/agents/cursor-ripgrep.ts
8032
+ function rgBinaryName() {
8033
+ return process.platform === "win32" ? "rg.exe" : "rg";
8034
+ }
8035
+ function platformRipgrepPackage() {
8036
+ return `@cursor/sdk-${process.platform}-${process.arch}`;
8037
+ }
8038
+ function usableRipgrepPath(candidate) {
8039
+ const raw = candidate?.trim();
8040
+ if (!raw || !(0, import_node_path22.isAbsolute)(raw)) return null;
8041
+ const readable = nodeReadableScriptPath(raw);
8042
+ if (!(0, import_node_fs23.existsSync)(readable) || isAsarPath(readable)) return null;
8043
+ return readable;
8044
+ }
8045
+ function walkForBundledRipgrep(startFile) {
8046
+ if (!startFile) return null;
8047
+ const pkg = platformRipgrepPackage();
8048
+ const name = rgBinaryName();
8049
+ let dir = (0, import_node_path22.dirname)((0, import_node_path22.resolve)(startFile));
8050
+ const root = (0, import_node_path22.parse)(dir).root;
8051
+ while (dir !== root) {
8052
+ const hit = usableRipgrepPath((0, import_node_path22.join)(dir, "node_modules", pkg, "bin", name));
8053
+ if (hit) return hit;
8054
+ const next = (0, import_node_path22.dirname)(dir);
8055
+ if (next === dir) break;
8056
+ dir = next;
8057
+ }
8058
+ return null;
8059
+ }
8060
+ function requireResolveBundledRipgrep(fromFile) {
8061
+ try {
8062
+ const req = (0, import_node_module2.createRequire)(fromFile);
8063
+ const pkgJson = req.resolve(`${platformRipgrepPackage()}/package.json`);
8064
+ return usableRipgrepPath((0, import_node_path22.join)((0, import_node_path22.dirname)(pkgJson), "bin", rgBinaryName()));
8065
+ } catch {
8066
+ return null;
8067
+ }
8068
+ }
8069
+ function resolveCursorRipgrepPath(opts) {
8070
+ const env = opts?.env ?? process.env;
8071
+ const fromEnv = usableRipgrepPath(env[RIPGREP_ENV]);
8072
+ if (fromEnv) return fromEnv;
8073
+ const fromPackaged = usableRipgrepPath(
8074
+ packagedCursorRipgrepCandidate(platformRipgrepPackage(), rgBinaryName())
8075
+ );
8076
+ if (fromPackaged) return fromPackaged;
8077
+ const start = opts?.startFile?.trim() || process.argv[1] || (0, import_node_url2.fileURLToPath)(import_meta2.url);
8078
+ return walkForBundledRipgrep(start) ?? requireResolveBundledRipgrep(start);
8079
+ }
8080
+ function cursorRipgrepEnv(opts) {
8081
+ const path2 = resolveCursorRipgrepPath(opts);
8082
+ return path2 ? { [RIPGREP_ENV]: path2 } : {};
8083
+ }
8084
+ var import_node_fs23, import_node_module2, import_node_path22, import_node_url2, import_meta2, RIPGREP_ENV;
8085
+ var init_cursor_ripgrep = __esm({
8086
+ "src/agents/cursor-ripgrep.ts"() {
8087
+ "use strict";
8088
+ import_node_fs23 = require("fs");
8089
+ import_node_module2 = require("module");
8090
+ import_node_path22 = require("path");
8091
+ import_node_url2 = require("url");
8092
+ init_node_launch();
8093
+ init_packaged_runtime();
8094
+ import_meta2 = {};
8095
+ RIPGREP_ENV = "CURSOR_RIPGREP_PATH";
8096
+ }
8097
+ });
8098
+
7749
8099
  // src/agents/cursor.ts
7750
8100
  function resolveCursorApiKey() {
7751
8101
  const fromEnv = (process.env.CURSOR_API_KEY || "").trim();
@@ -7785,51 +8135,55 @@ function entryDir() {
7785
8135
  const cjsDir = typeof __dirname !== "undefined" ? __dirname : "";
7786
8136
  if (cjsDir) return cjsDir;
7787
8137
  try {
7788
- return (0, import_node_path18.dirname)((0, import_node_url2.fileURLToPath)(import_meta2.url));
8138
+ return (0, import_node_path23.dirname)((0, import_node_url3.fileURLToPath)(import_meta3.url));
7789
8139
  } catch {
7790
8140
  try {
7791
- const req = (0, import_node_module2.createRequire)(process.cwd() + "/");
7792
- return (0, import_node_path18.dirname)(req.resolve("@sideboard-ai/core"));
8141
+ const req = (0, import_node_module3.createRequire)(process.cwd() + "/");
8142
+ return (0, import_node_path23.dirname)(req.resolve("@sideboard-ai/core"));
7793
8143
  } catch {
7794
8144
  return process.cwd();
7795
8145
  }
7796
8146
  }
7797
8147
  }
7798
8148
  function cursorRunnerPath() {
8149
+ const packaged = packagedCursorRunnerPath();
8150
+ if (packaged) return packaged;
7799
8151
  const root = entryDir();
7800
8152
  const candidates = [
7801
- (0, import_node_path18.join)(root, "agents", "cursor-runner.js"),
7802
- (0, import_node_path18.join)(root, "agents", "cursor-runner.cjs"),
8153
+ (0, import_node_path23.join)(root, "agents", "cursor-runner.js"),
8154
+ (0, import_node_path23.join)(root, "agents", "cursor-runner.cjs"),
7803
8155
  // If somehow resolved from package root instead of dist/
7804
- (0, import_node_path18.join)(root, "dist", "agents", "cursor-runner.js"),
7805
- (0, import_node_path18.join)(root, "dist", "agents", "cursor-runner.cjs"),
8156
+ (0, import_node_path23.join)(root, "dist", "agents", "cursor-runner.js"),
8157
+ (0, import_node_path23.join)(root, "dist", "agents", "cursor-runner.cjs"),
7806
8158
  // Source tree (dev): packages/core/src/agents/cursor-runner.ts
7807
- (0, import_node_path18.join)(root, "cursor-runner.ts"),
7808
- (0, import_node_path18.join)(root, "src", "agents", "cursor-runner.ts")
8159
+ (0, import_node_path23.join)(root, "cursor-runner.ts"),
8160
+ (0, import_node_path23.join)(root, "src", "agents", "cursor-runner.ts")
7809
8161
  ];
7810
8162
  for (const candidate of candidates) {
7811
- if ((0, import_node_fs20.existsSync)(candidate)) return candidate;
8163
+ if ((0, import_node_fs24.existsSync)(candidate)) return candidate;
7812
8164
  }
7813
8165
  return candidates[0];
7814
8166
  }
7815
- var import_node_fs20, import_node_module2, import_node_path18, import_node_url2, import_sdk, import_meta2, FALLBACK_CURSOR_MODELS, cachedModels, MODEL_CACHE_MS, cursorAdapter;
8167
+ var import_node_fs24, import_node_module3, import_node_path23, import_node_url3, import_sdk, import_meta3, FALLBACK_CURSOR_MODELS, cachedModels, MODEL_CACHE_MS, cursorAdapter;
7816
8168
  var init_cursor = __esm({
7817
8169
  "src/agents/cursor.ts"() {
7818
8170
  "use strict";
7819
- import_node_fs20 = require("fs");
7820
- import_node_module2 = require("module");
7821
- import_node_path18 = require("path");
7822
- import_node_url2 = require("url");
8171
+ import_node_fs24 = require("fs");
8172
+ import_node_module3 = require("module");
8173
+ import_node_path23 = require("path");
8174
+ import_node_url3 = require("url");
7823
8175
  import_sdk = require("@cursor/sdk");
7824
8176
  init_run();
7825
8177
  init_app_settings();
7826
8178
  init_global_workspace();
7827
8179
  init_cursor_events();
7828
8180
  init_injected_mcp();
8181
+ init_cursor_ripgrep();
7829
8182
  init_node_launch();
8183
+ init_packaged_runtime();
7830
8184
  init_turn_input();
7831
8185
  init_cursor_events();
7832
- import_meta2 = {};
8186
+ import_meta3 = {};
7833
8187
  FALLBACK_CURSOR_MODELS = [
7834
8188
  { id: "default", displayName: "Auto" },
7835
8189
  { id: "composer-2.5", displayName: "Composer 2.5" },
@@ -7896,6 +8250,7 @@ var init_cursor = __esm({
7896
8250
  stdin: JSON.stringify(req),
7897
8251
  env: {
7898
8252
  ...launch.env,
8253
+ ...cursorRipgrepEnv({ startFile: runner }),
7899
8254
  ...apiKey ? { CURSOR_API_KEY: apiKey } : {}
7900
8255
  }
7901
8256
  };
@@ -7947,7 +8302,7 @@ async function listOpencodeModels() {
7947
8302
  if (opencode === "opencode") {
7948
8303
  const which = await run("which", ["opencode"], { reject: false });
7949
8304
  if (which.exitCode !== 0) return FALLBACK_OPENCODE_MODELS;
7950
- } else if (!(0, import_node_fs21.existsSync)(opencode)) {
8305
+ } else if (!(0, import_node_fs25.existsSync)(opencode)) {
7951
8306
  return FALLBACK_OPENCODE_MODELS;
7952
8307
  }
7953
8308
  const listed = await run(opencode, ["models"], { reject: false });
@@ -7977,11 +8332,11 @@ function usageFromOpencode(tokens) {
7977
8332
  cacheWriteTokens: tokens.cache?.write ? Number(tokens.cache.write) : void 0
7978
8333
  };
7979
8334
  }
7980
- var import_node_fs21, FALLBACK_OPENCODE_MODELS, cachedOpencodeModels, OPENCODE_MODEL_CACHE_MS, opencodeAdapter;
8335
+ var import_node_fs25, FALLBACK_OPENCODE_MODELS, cachedOpencodeModels, OPENCODE_MODEL_CACHE_MS, opencodeAdapter;
7981
8336
  var init_opencode = __esm({
7982
8337
  "src/agents/opencode.ts"() {
7983
8338
  "use strict";
7984
- import_node_fs21 = require("fs");
8339
+ import_node_fs25 = require("fs");
7985
8340
  init_run();
7986
8341
  init_app_settings();
7987
8342
  init_global_workspace();
@@ -8007,7 +8362,7 @@ var init_opencode = __esm({
8007
8362
  async detect() {
8008
8363
  const opencode = resolveAgentExecutable("opencode");
8009
8364
  if (opencode !== "opencode") {
8010
- if (!(0, import_node_fs21.existsSync)(opencode)) {
8365
+ if (!(0, import_node_fs25.existsSync)(opencode)) {
8011
8366
  return {
8012
8367
  agent: "opencode",
8013
8368
  installed: false,
@@ -8804,40 +9159,40 @@ __export(plan_file_exports, {
8804
9159
  writePlanFile: () => writePlanFile
8805
9160
  });
8806
9161
  function ensureAttachmentsGitignore2(worktreePath) {
8807
- const gitignoreAbs = (0, import_node_path31.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
8808
- if ((0, import_node_fs35.existsSync)(gitignoreAbs)) return;
8809
- (0, import_node_fs35.mkdirSync)((0, import_node_path31.dirname)(gitignoreAbs), { recursive: true });
8810
- (0, import_node_fs35.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
9162
+ const gitignoreAbs = (0, import_node_path36.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
9163
+ if ((0, import_node_fs39.existsSync)(gitignoreAbs)) return;
9164
+ (0, import_node_fs39.mkdirSync)((0, import_node_path36.dirname)(gitignoreAbs), { recursive: true });
9165
+ (0, import_node_fs39.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
8811
9166
  }
8812
9167
  function planFileAbs(worktreePath) {
8813
- return (0, import_node_path31.join)(worktreePath, PLAN_FILE_REL);
9168
+ return (0, import_node_path36.join)(worktreePath, PLAN_FILE_REL);
8814
9169
  }
8815
9170
  function readTextIfPresent2(abs) {
8816
- if (!(0, import_node_fs35.existsSync)(abs)) return null;
9171
+ if (!(0, import_node_fs39.existsSync)(abs)) return null;
8817
9172
  try {
8818
- const content = (0, import_node_fs35.readFileSync)(abs, "utf8");
9173
+ const content = (0, import_node_fs39.readFileSync)(abs, "utf8");
8819
9174
  return content.trim() ? content : null;
8820
9175
  } catch {
8821
9176
  return null;
8822
9177
  }
8823
9178
  }
8824
9179
  function readPlanFile(worktreePath) {
8825
- return readTextIfPresent2(planFileAbs(worktreePath)) ?? readTextIfPresent2((0, import_node_path31.join)(worktreePath, `${LEGACY_ATTACHMENTS_DIR}/plan.md`)) ?? readTextIfPresent2((0, import_node_path31.join)(worktreePath, LEGACY_PLAN_FILE_REL));
9180
+ return readTextIfPresent2(planFileAbs(worktreePath)) ?? readTextIfPresent2((0, import_node_path36.join)(worktreePath, `${LEGACY_ATTACHMENTS_DIR}/plan.md`)) ?? readTextIfPresent2((0, import_node_path36.join)(worktreePath, LEGACY_PLAN_FILE_REL));
8826
9181
  }
8827
9182
  function writePlanFile(worktreePath, content) {
8828
9183
  ensureAttachmentsGitignore2(worktreePath);
8829
9184
  const abs = planFileAbs(worktreePath);
8830
- (0, import_node_fs35.mkdirSync)((0, import_node_path31.dirname)(abs), { recursive: true });
9185
+ (0, import_node_fs39.mkdirSync)((0, import_node_path36.dirname)(abs), { recursive: true });
8831
9186
  const body = content.trimEnd() + (content.endsWith("\n") ? "" : "\n");
8832
- (0, import_node_fs35.writeFileSync)(abs, body, "utf8");
9187
+ (0, import_node_fs39.writeFileSync)(abs, body, "utf8");
8833
9188
  return PLAN_FILE_REL;
8834
9189
  }
8835
- var import_node_fs35, import_node_path31;
9190
+ var import_node_fs39, import_node_path36;
8836
9191
  var init_plan_file = __esm({
8837
9192
  "src/plan/plan-file.ts"() {
8838
9193
  "use strict";
8839
- import_node_fs35 = require("fs");
8840
- import_node_path31 = require("path");
9194
+ import_node_fs39 = require("fs");
9195
+ import_node_path36 = require("path");
8841
9196
  init_workspace_scratch();
8842
9197
  init_plan_present();
8843
9198
  init_plan_present();
@@ -8852,10 +9207,10 @@ __export(cursor_recover_exports, {
8852
9207
  function recoverFinishedCursorRun(opts) {
8853
9208
  const agentId = opts.agentId.trim();
8854
9209
  if (!agentId) return null;
8855
- const runsPath = (0, import_node_path32.join)(appDataDir(), "cursor-sdk-store", "runs.ndjson");
8856
- if (!(0, import_node_fs36.existsSync)(runsPath)) return null;
9210
+ const runsPath = (0, import_node_path37.join)(appDataDir(), "cursor-sdk-store", "runs.ndjson");
9211
+ if (!(0, import_node_fs40.existsSync)(runsPath)) return null;
8857
9212
  try {
8858
- const lines = (0, import_node_fs36.readFileSync)(runsPath, "utf8").split("\n");
9213
+ const lines = (0, import_node_fs40.readFileSync)(runsPath, "utf8").split("\n");
8859
9214
  let best = null;
8860
9215
  for (const line of lines) {
8861
9216
  const trimmed = line.trim();
@@ -8881,12 +9236,12 @@ function recoverFinishedCursorRun(opts) {
8881
9236
  return null;
8882
9237
  }
8883
9238
  }
8884
- var import_node_fs36, import_node_path32;
9239
+ var import_node_fs40, import_node_path37;
8885
9240
  var init_cursor_recover = __esm({
8886
9241
  "src/agents/cursor-recover.ts"() {
8887
9242
  "use strict";
8888
- import_node_fs36 = require("fs");
8889
- import_node_path32 = require("path");
9243
+ import_node_fs40 = require("fs");
9244
+ import_node_path37 = require("path");
8890
9245
  init_paths();
8891
9246
  }
8892
9247
  });
@@ -9040,6 +9395,7 @@ __export(index_exports, {
9040
9395
  cleanupOrphanWorktrees: () => cleanupOrphanWorktrees,
9041
9396
  cloneRepoIntoSideboard: () => cloneRepoIntoSideboard,
9042
9397
  codexAdapter: () => codexAdapter,
9398
+ codexSandboxWritableRootsArgs: () => codexSandboxWritableRootsArgs,
9043
9399
  codexUnattendedGitConfigArgs: () => codexUnattendedGitConfigArgs,
9044
9400
  coerceOrchestratorAgent: () => coerceOrchestratorAgent,
9045
9401
  collectTakenTeamSlugs: () => collectTakenTeamSlugs,
@@ -9156,6 +9512,7 @@ __export(index_exports, {
9156
9512
  getPr: () => getPr,
9157
9513
  getPrChecks: () => getPrChecks,
9158
9514
  getPrDetails: () => getPrDetails,
9515
+ getPrForHeadBranch: () => getPrForHeadBranch,
9159
9516
  getPrMeta: () => getPrMeta,
9160
9517
  getPrStack: () => getPrStack,
9161
9518
  getRepoSetupInfo: () => getRepoSetupInfo,
@@ -9192,6 +9549,7 @@ __export(index_exports, {
9192
9549
  isCloudCoordinatorThread: () => isCloudCoordinatorThread,
9193
9550
  isConductorBundledCli: () => isConductorBundledCli,
9194
9551
  isCursorAutoModel: () => isCursorAutoModel,
9552
+ isDefaultishSourceRef: () => isDefaultishSourceRef,
9195
9553
  isDirty: () => isDirty,
9196
9554
  isGhRateLimitError: () => isGhRateLimitError,
9197
9555
  isGlobalRepoPath: () => isGlobalRepoPath,
@@ -9258,8 +9616,10 @@ __export(index_exports, {
9258
9616
  maybeCompactContext: () => maybeCompactContext,
9259
9617
  mcpAllowTools: () => mcpAllowTools,
9260
9618
  mcpAuthWarnings: () => mcpAuthWarnings,
9619
+ mergeAgentGitAuthEnv: () => mergeAgentGitAuthEnv,
9261
9620
  mergePr: () => mergePr,
9262
9621
  mergePrStack: () => mergePrStack,
9622
+ mergeSideboardIntoMcpServersJson: () => mergeSideboardIntoMcpServersJson,
9263
9623
  mergeUsage: () => mergeUsage,
9264
9624
  nextPastedTextName: () => nextPastedTextName,
9265
9625
  nextThinkingEffort: () => nextThinkingEffort,
@@ -9310,6 +9670,7 @@ __export(index_exports, {
9310
9670
  recordSlackOutboundWatch: () => recordSlackOutboundWatch,
9311
9671
  refreshGitHubAuth: () => refreshGitHubAuth,
9312
9672
  refreshSlackReplyBadges: () => refreshSlackReplyBadges,
9673
+ registerPackagedUserMcpClients: () => registerPackagedUserMcpClients,
9313
9674
  releaseCaffeinateHoldForThread: () => releaseCaffeinateHoldForThread,
9314
9675
  removeWorkspace: () => removeWorkspace,
9315
9676
  removeWorktree: () => removeWorktree,
@@ -9318,9 +9679,11 @@ __export(index_exports, {
9318
9679
  requestReview: () => requestReview,
9319
9680
  requireAgent: () => requireAgent,
9320
9681
  resetGhStackDetectCache: () => resetGhStackDetectCache,
9682
+ resetGithubAgentTokenMemo: () => resetGithubAgentTokenMemo,
9321
9683
  resolveAgentExecutable: () => resolveAgentExecutable,
9322
9684
  resolveAgentGitAuthEnv: () => resolveAgentGitAuthEnv,
9323
9685
  resolveClaudeExecutable: () => resolveClaudeExecutable,
9686
+ resolveCodexGitWritableRoots: () => resolveCodexGitWritableRoots,
9324
9687
  resolveCommandBinarySync: () => resolveCommandBinarySync,
9325
9688
  resolveConductorCursorAgentId: () => resolveConductorCursorAgentId,
9326
9689
  resolveCursorModelId: () => resolveCursorModelId,
@@ -9337,6 +9700,7 @@ __export(index_exports, {
9337
9700
  resolveNewThreadOptions: () => resolveNewThreadOptions,
9338
9701
  resolvePlanMarkdown: () => resolvePlanMarkdown,
9339
9702
  resolvePrSelector: () => resolvePrSelector,
9703
+ resolvePrSelectors: () => resolvePrSelectors,
9340
9704
  resolveQuotaFallbackAgent: () => resolveQuotaFallbackAgent,
9341
9705
  resolveRepoRoot: () => resolveRepoRoot,
9342
9706
  resolveReviewGuidelines: () => resolveReviewGuidelines,
@@ -9359,6 +9723,7 @@ __export(index_exports, {
9359
9723
  sanitizeMcpServerName: () => sanitizeMcpServerName,
9360
9724
  saveAppSettings: () => saveAppSettings,
9361
9725
  saveLinearOAuth: () => saveLinearOAuth,
9726
+ scrubGithubTokensFromChildEnv: () => scrubGithubTokensFromChildEnv,
9362
9727
  secureFileUnlocksWith: () => secureFileUnlocksWith,
9363
9728
  setCaffeinateHold: () => setCaffeinateHold,
9364
9729
  setHttpFetchImpl: () => setHttpFetchImpl,
@@ -9429,7 +9794,10 @@ __export(index_exports, {
9429
9794
  updateLinearIssue: () => updateLinearIssue,
9430
9795
  updateOpencodeSettings: () => updateOpencodeSettings,
9431
9796
  updateThread: () => updateThread,
9797
+ userClaudeMcpConfigPath: () => userClaudeMcpConfigPath,
9798
+ userCursorMcpConfigPath: () => userCursorMcpConfigPath,
9432
9799
  validateLinearApiKey: () => validateLinearApiKey,
9800
+ warmGithubAgentAuth: () => warmGithubAgentAuth,
9433
9801
  withAgentInstructions: () => withAgentInstructions,
9434
9802
  withExportedPath: () => withExportedPath,
9435
9803
  withThreadLock: () => withThreadLock,
@@ -10513,10 +10881,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
10513
10881
  const env = childEnvWithAppSettings(cmd.env);
10514
10882
  try {
10515
10883
  if (isOrchestratorThread(thread)) {
10516
- Object.assign(env, await resolveAgentGitAuthEnv(env));
10884
+ mergeAgentGitAuthEnv(env, await resolveAgentGitAuthEnv(env));
10517
10885
  } else {
10518
- const originEnv = await originGhRepoEnv(thread.worktreePath, { env });
10519
- Object.assign(env, originEnv);
10886
+ mergeAgentGitAuthEnv(env, await originGhRepoEnv(thread.worktreePath, { env }));
10520
10887
  }
10521
10888
  } catch (err) {
10522
10889
  const detail = err instanceof Error ? err.message : String(err);
@@ -10611,8 +10978,8 @@ init_usage();
10611
10978
  init_claude_mcp();
10612
10979
 
10613
10980
  // src/agents/instructions.ts
10614
- var import_node_fs22 = require("fs");
10615
- var import_node_path19 = require("path");
10981
+ var import_node_fs26 = require("fs");
10982
+ var import_node_path24 = require("path");
10616
10983
  init_git_auth_mode();
10617
10984
  init_worktree_labels();
10618
10985
  function normPath3(p) {
@@ -10745,9 +11112,10 @@ function formatArtifactDirective() {
10745
11112
  "Files / media browser (CMS file manager column):",
10746
11113
  "4) Call Sideboard MCP `present_files` with optional title, path, and datasource (brightsy|memory).",
10747
11114
  " Opens the Files column for browse/upload/pick. Do NOT say a file manager UI is missing.",
10748
- "Multiple-choice questions (any mode \u2014 not only Plan):",
10749
- "5) Call Sideboard MCP `ask_user` when the user should pick from predefined options (approach forks, requirements, which API). First write a short chat message that explains the decision and what each option means (tradeoffs, when to pick it). Include a description on every option. After calling, stop and wait for their next message with answers. Do not ask multiple-choice questions as plain chat bullets \u2014 use ask_user so Sideboard shows the composer picker.",
10750
- "Never say artifacts, CMS UI, or the Files column are unavailable. Prefer present_schema for list/edit/publish; present_files for storage UI; html fences for standalone pages; ask_user for predefined-option questions."
11115
+ "Multiple-choice questions:",
11116
+ "5) Call Sideboard MCP `ask_user` only when work is blocked on choosing among a few concrete options (approach forks, which API, auth vs cookies). First write a short chat message that explains the decision and what each option means (tradeoffs, when to pick it). Include a description on every option. After calling, stop and wait for their next message with answers. If you are asking a real multiple-choice, use ask_user rather than chat bullets so Sideboard shows the composer picker.",
11117
+ "Do not call ask_user for greetings, check-ins, \u201Chello\u201D, open-ended how-can-I-help, or to invent a menu of possible next tasks \u2014 reply in chat. If one option is the obvious default, proceed without asking.",
11118
+ "Never say artifacts, CMS UI, or the Files column are unavailable. Prefer present_schema for list/edit/publish; present_files for storage UI; html fences for standalone pages; ask_user only for those blocked predefined-option questions."
10751
11119
  ].join("\n");
10752
11120
  }
10753
11121
  function formatUiReminder() {
@@ -10758,7 +11126,7 @@ function formatUiReminder() {
10758
11126
  "CMS forms/tables: MCP present_schema (Brightsy resource_id or inline schema+schemaUi).",
10759
11127
  "Files column: MCP present_files (brightsy account storage or memory).",
10760
11128
  "Do not say artifacts/CMS UI are unavailable.",
10761
- "Multiple-choice questions: MCP ask_user (composer picker, any mode). Explain options in chat first, then wait for answers."
11129
+ "ask_user: only when blocked on a real multiple-choice (approach fork, which API). Never for hellos, check-ins, or \u201Cwhat next?\u201D menus \u2014 reply in chat."
10762
11130
  ].join(" ");
10763
11131
  }
10764
11132
  var FILES_BY_AGENT = {
@@ -10782,11 +11150,11 @@ function loadAgentInstructions(worktreePath, agent) {
10782
11150
  const out = [];
10783
11151
  for (const rel of candidates) {
10784
11152
  if (seenPaths.has(rel)) continue;
10785
- const abs = (0, import_node_path19.join)(worktreePath, rel);
10786
- if (!(0, import_node_fs22.existsSync)(abs)) continue;
11153
+ const abs = (0, import_node_path24.join)(worktreePath, rel);
11154
+ if (!(0, import_node_fs26.existsSync)(abs)) continue;
10787
11155
  try {
10788
- if (!(0, import_node_fs22.statSync)(abs).isFile()) continue;
10789
- let content = (0, import_node_fs22.readFileSync)(abs, "utf8");
11156
+ if (!(0, import_node_fs26.statSync)(abs).isFile()) continue;
11157
+ let content = (0, import_node_fs26.readFileSync)(abs, "utf8");
10790
11158
  if (!content.trim()) continue;
10791
11159
  if (content.length > MAX_CHARS_PER_FILE) {
10792
11160
  content = `${content.slice(0, MAX_CHARS_PER_FILE)}
@@ -10868,9 +11236,9 @@ async function requireAgent(agent, opts) {
10868
11236
  init_settings();
10869
11237
 
10870
11238
  // src/hook/conductor.ts
10871
- var import_node_fs23 = require("fs");
11239
+ var import_node_fs27 = require("fs");
10872
11240
  var import_node_net = require("net");
10873
- var import_node_path20 = require("path");
11241
+ var import_node_path25 = require("path");
10874
11242
  var import_execa4 = require("execa");
10875
11243
  var import_node_readline3 = require("readline");
10876
11244
  init_settings();
@@ -10885,9 +11253,9 @@ function matchSimpleGlob(pattern, name) {
10885
11253
  return new RegExp(`^${escaped}$`).test(name);
10886
11254
  }
10887
11255
  function readWorktreeInclude(repoPath) {
10888
- const path2 = (0, import_node_path20.join)(repoPath, ".worktreeinclude");
10889
- if (!(0, import_node_fs23.existsSync)(path2)) return [];
10890
- return (0, import_node_fs23.readFileSync)(path2, "utf8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
11256
+ const path2 = (0, import_node_path25.join)(repoPath, ".worktreeinclude");
11257
+ if (!(0, import_node_fs27.existsSync)(path2)) return [];
11258
+ return (0, import_node_fs27.readFileSync)(path2, "utf8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
10891
11259
  }
10892
11260
  function resolveFilesToCopy(repoPath) {
10893
11261
  const fromInclude = readWorktreeInclude(repoPath);
@@ -10897,10 +11265,10 @@ function resolveFilesToCopy(repoPath) {
10897
11265
  if (settings?.fileIncludeGlobs?.length) {
10898
11266
  const matched = [];
10899
11267
  try {
10900
- for (const entry of (0, import_node_fs23.readdirSync)(repoPath, { withFileTypes: true })) {
11268
+ for (const entry of (0, import_node_fs27.readdirSync)(repoPath, { withFileTypes: true })) {
10901
11269
  if (!entry.isFile()) continue;
10902
11270
  for (const glob of settings.fileIncludeGlobs) {
10903
- if (matchSimpleGlob(glob, entry.name) || matchSimpleGlob((0, import_node_path20.basename)(glob), entry.name)) {
11271
+ if (matchSimpleGlob(glob, entry.name) || matchSimpleGlob((0, import_node_path25.basename)(glob), entry.name)) {
10904
11272
  matched.push(entry.name);
10905
11273
  break;
10906
11274
  }
@@ -10912,7 +11280,7 @@ function resolveFilesToCopy(repoPath) {
10912
11280
  }
10913
11281
  const defaults = [];
10914
11282
  try {
10915
- for (const entry of (0, import_node_fs23.readdirSync)(repoPath, { withFileTypes: true })) {
11283
+ for (const entry of (0, import_node_fs27.readdirSync)(repoPath, { withFileTypes: true })) {
10916
11284
  if (entry.isFile() && entry.name.startsWith(".env")) {
10917
11285
  defaults.push(entry.name);
10918
11286
  }
@@ -10926,11 +11294,11 @@ function copyConfiguredFiles(repoPath, worktreePath) {
10926
11294
  const patterns = resolveFilesToCopy(repoPath);
10927
11295
  const copied = [];
10928
11296
  for (const rel of patterns) {
10929
- const src = (0, import_node_path20.join)(repoPath, rel);
10930
- if (!(0, import_node_fs23.existsSync)(src)) continue;
10931
- const dest = (0, import_node_path20.join)(worktreePath, rel);
10932
- (0, import_node_fs23.mkdirSync)((0, import_node_path20.dirname)(dest), { recursive: true });
10933
- (0, import_node_fs23.copyFileSync)(src, dest);
11297
+ const src = (0, import_node_path25.join)(repoPath, rel);
11298
+ if (!(0, import_node_fs27.existsSync)(src)) continue;
11299
+ const dest = (0, import_node_path25.join)(worktreePath, rel);
11300
+ (0, import_node_fs27.mkdirSync)((0, import_node_path25.dirname)(dest), { recursive: true });
11301
+ (0, import_node_fs27.copyFileSync)(src, dest);
10934
11302
  copied.push(rel);
10935
11303
  }
10936
11304
  return copied;
@@ -10966,7 +11334,7 @@ function buildWorkspaceScriptEnv(opts, baseEnv) {
10966
11334
  const env = stripNestedElectronEnv({
10967
11335
  ...baseEnv ?? process.env
10968
11336
  });
10969
- const name = opts.workspaceName ?? (0, import_node_path20.basename)(opts.worktreePath);
11337
+ const name = opts.workspaceName ?? (0, import_node_path25.basename)(opts.worktreePath);
10970
11338
  const ports = opts.ports ?? [];
10971
11339
  const primary = ports[0];
10972
11340
  env.SIDEBOARD_WORKSPACE_NAME = name;
@@ -11052,7 +11420,10 @@ async function spawnWorkspaceScript(command, opts) {
11052
11420
  loginEnv
11053
11421
  );
11054
11422
  try {
11055
- Object.assign(env, await resolveAgentGitAuthEnv(env, { cwd: opts.worktreePath }));
11423
+ mergeAgentGitAuthEnv(
11424
+ env,
11425
+ await resolveAgentGitAuthEnv(env, { cwd: opts.worktreePath })
11426
+ );
11056
11427
  } catch {
11057
11428
  }
11058
11429
  const shell = process.platform === "darwin" ? "zsh" : "bash";
@@ -11224,12 +11595,12 @@ init_convention_setup();
11224
11595
  init_cursor_worktrees();
11225
11596
 
11226
11597
  // src/diff/diff.ts
11227
- var import_node_fs24 = require("fs");
11228
- var import_node_path21 = require("path");
11598
+ var import_node_fs28 = require("fs");
11599
+ var import_node_path26 = require("path");
11229
11600
  init_run();
11230
11601
  init_worktree();
11231
11602
  async function inspectGitWorktree(worktreePath) {
11232
- if (!worktreePath || !(0, import_node_fs24.existsSync)(worktreePath)) return "missing_worktree";
11603
+ if (!worktreePath || !(0, import_node_fs28.existsSync)(worktreePath)) return "missing_worktree";
11233
11604
  const check = await git(["rev-parse", "--is-inside-work-tree"], worktreePath, {
11234
11605
  reject: false
11235
11606
  });
@@ -11237,7 +11608,7 @@ async function inspectGitWorktree(worktreePath) {
11237
11608
  return "ok";
11238
11609
  }
11239
11610
  async function initializeGitRepository(worktreePath) {
11240
- if (!worktreePath || !(0, import_node_fs24.existsSync)(worktreePath)) {
11611
+ if (!worktreePath || !(0, import_node_fs28.existsSync)(worktreePath)) {
11241
11612
  throw new Error("Worktree not found");
11242
11613
  }
11243
11614
  const status = await inspectGitWorktree(worktreePath);
@@ -11372,11 +11743,11 @@ new file mode 100644
11372
11743
  };
11373
11744
  }
11374
11745
  async function untrackedPatch(worktreePath, path2, maxHunk) {
11375
- const abs = (0, import_node_path21.join)(worktreePath, path2);
11746
+ const abs = (0, import_node_path26.join)(worktreePath, path2);
11376
11747
  try {
11377
- const st = (0, import_node_fs24.statSync)(abs);
11748
+ const st = (0, import_node_fs28.statSync)(abs);
11378
11749
  if (st.isFile() && st.size > maxHunk) {
11379
- const buf = (0, import_node_fs24.readFileSync)(abs).subarray(0, maxHunk);
11750
+ const buf = (0, import_node_fs28.readFileSync)(abs).subarray(0, maxHunk);
11380
11751
  return syntheticAddPatch(path2, buf.toString("utf8"), maxHunk);
11381
11752
  }
11382
11753
  } catch {
@@ -11877,8 +12248,8 @@ var DEFAULT_UPLOAD_MAX_BYTES = 5e7;
11877
12248
  function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
11878
12249
  assertSafeRelativePath(relativePath);
11879
12250
  const maxBytes = opts?.maxBytes ?? DEFAULT_UPLOAD_MAX_BYTES;
11880
- const abs = (0, import_node_path21.join)(worktreePath, relativePath);
11881
- const st = (0, import_node_fs24.statSync)(abs);
12251
+ const abs = (0, import_node_path26.join)(worktreePath, relativePath);
12252
+ const st = (0, import_node_fs28.statSync)(abs);
11882
12253
  if (!st.isFile()) {
11883
12254
  throw new Error(`Not a file: ${relativePath}`);
11884
12255
  }
@@ -11887,7 +12258,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
11887
12258
  `File too large to upload (${st.size} bytes; max ${maxBytes})`
11888
12259
  );
11889
12260
  }
11890
- const buf = (0, import_node_fs24.readFileSync)(abs);
12261
+ const buf = (0, import_node_fs28.readFileSync)(abs);
11891
12262
  return {
11892
12263
  path: relativePath,
11893
12264
  contentBase64: buf.toString("base64"),
@@ -11897,12 +12268,12 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
11897
12268
  function readWorktreeFile(worktreePath, relativePath, opts) {
11898
12269
  assertSafeRelativePath(relativePath);
11899
12270
  const maxBytes = opts?.maxBytes ?? 2e5;
11900
- const abs = (0, import_node_path21.join)(worktreePath, relativePath);
11901
- const st = (0, import_node_fs24.statSync)(abs);
12271
+ const abs = (0, import_node_path26.join)(worktreePath, relativePath);
12272
+ const st = (0, import_node_fs28.statSync)(abs);
11902
12273
  if (!st.isFile()) {
11903
12274
  throw new Error(`Not a file: ${relativePath}`);
11904
12275
  }
11905
- const buf = (0, import_node_fs24.readFileSync)(abs);
12276
+ const buf = (0, import_node_fs28.readFileSync)(abs);
11906
12277
  if (isImageRelativePath(relativePath)) {
11907
12278
  const maxImageBytes = Math.max(maxBytes, 15e6);
11908
12279
  const truncated2 = buf.length > maxImageBytes;
@@ -11945,9 +12316,9 @@ function assertSafeRelativePath(relativePath) {
11945
12316
  }
11946
12317
  function writeWorktreeFile(worktreePath, relativePath, content) {
11947
12318
  assertSafeRelativePath(relativePath);
11948
- const abs = (0, import_node_path21.join)(worktreePath, relativePath);
11949
- (0, import_node_fs24.mkdirSync)((0, import_node_path21.dirname)(abs), { recursive: true });
11950
- (0, import_node_fs24.writeFileSync)(abs, content, "utf8");
12319
+ const abs = (0, import_node_path26.join)(worktreePath, relativePath);
12320
+ (0, import_node_fs28.mkdirSync)((0, import_node_path26.dirname)(abs), { recursive: true });
12321
+ (0, import_node_fs28.writeFileSync)(abs, content, "utf8");
11951
12322
  return { path: relativePath };
11952
12323
  }
11953
12324
  async function getDiffSummary(worktreePath, repoPath, opts) {
@@ -11966,9 +12337,9 @@ async function getDiffSummary(worktreePath, repoPath, opts) {
11966
12337
  }
11967
12338
 
11968
12339
  // src/skills/discover.ts
11969
- var import_node_fs25 = require("fs");
11970
- var import_node_os8 = require("os");
11971
- var import_node_path22 = require("path");
12340
+ var import_node_fs29 = require("fs");
12341
+ var import_node_os10 = require("os");
12342
+ var import_node_path27 = require("path");
11972
12343
  function toCommand(name) {
11973
12344
  return name.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
11974
12345
  }
@@ -12000,7 +12371,7 @@ function parseFrontmatter(content) {
12000
12371
  }
12001
12372
  function readSkill(skillMd, source) {
12002
12373
  try {
12003
- const content = (0, import_node_fs25.readFileSync)(skillMd, "utf8");
12374
+ const content = (0, import_node_fs29.readFileSync)(skillMd, "utf8");
12004
12375
  const { name: fmName, description } = parseFrontmatter(content);
12005
12376
  const dirName = skillMd.split("/").slice(-2, -1)[0] || "skill";
12006
12377
  const name = fmName || dirName;
@@ -12019,19 +12390,19 @@ function readSkill(skillMd, source) {
12019
12390
  }
12020
12391
  }
12021
12392
  function scanSkillsDir(dir, source, out) {
12022
- if (!(0, import_node_fs25.existsSync)(dir)) return;
12393
+ if (!(0, import_node_fs29.existsSync)(dir)) return;
12023
12394
  let entries;
12024
12395
  try {
12025
- entries = (0, import_node_fs25.readdirSync)(dir);
12396
+ entries = (0, import_node_fs29.readdirSync)(dir);
12026
12397
  } catch {
12027
12398
  return;
12028
12399
  }
12029
12400
  for (const entry of entries) {
12030
12401
  if (entry.startsWith(".")) continue;
12031
- const skillMd = (0, import_node_path22.join)(dir, entry, "SKILL.md");
12032
- if (!(0, import_node_fs25.existsSync)(skillMd)) continue;
12402
+ const skillMd = (0, import_node_path27.join)(dir, entry, "SKILL.md");
12403
+ if (!(0, import_node_fs29.existsSync)(skillMd)) continue;
12033
12404
  try {
12034
- if (!(0, import_node_fs25.statSync)(skillMd).isFile()) continue;
12405
+ if (!(0, import_node_fs29.statSync)(skillMd).isFile()) continue;
12035
12406
  } catch {
12036
12407
  continue;
12037
12408
  }
@@ -12040,24 +12411,24 @@ function scanSkillsDir(dir, source, out) {
12040
12411
  }
12041
12412
  }
12042
12413
  function scanClaudePluginSkills(pluginsRoot, out) {
12043
- if (!(0, import_node_fs25.existsSync)(pluginsRoot)) return;
12414
+ if (!(0, import_node_fs29.existsSync)(pluginsRoot)) return;
12044
12415
  const walk = (dir, depth, lookingForSkillsDir) => {
12045
12416
  if (depth > 7) return;
12046
12417
  let entries;
12047
12418
  try {
12048
- entries = (0, import_node_fs25.readdirSync)(dir);
12419
+ entries = (0, import_node_fs29.readdirSync)(dir);
12049
12420
  } catch {
12050
12421
  return;
12051
12422
  }
12052
12423
  if (lookingForSkillsDir && entries.includes("SKILL.md")) {
12053
- const skill = readSkill((0, import_node_path22.join)(dir, "SKILL.md"), "cli");
12424
+ const skill = readSkill((0, import_node_path27.join)(dir, "SKILL.md"), "cli");
12054
12425
  if (skill) out.push(skill);
12055
12426
  }
12056
12427
  for (const entry of entries) {
12057
12428
  if (entry === "node_modules" || entry === ".git") continue;
12058
- const full = (0, import_node_path22.join)(dir, entry);
12429
+ const full = (0, import_node_path27.join)(dir, entry);
12059
12430
  try {
12060
- if (!(0, import_node_fs25.statSync)(full).isDirectory()) continue;
12431
+ if (!(0, import_node_fs29.statSync)(full).isDirectory()) continue;
12061
12432
  } catch {
12062
12433
  continue;
12063
12434
  }
@@ -12072,20 +12443,20 @@ function scanClaudePluginSkills(pluginsRoot, out) {
12072
12443
  walk(pluginsRoot, 0, false);
12073
12444
  }
12074
12445
  function discoverSkills(worktreePath) {
12075
- const home = (0, import_node_os8.homedir)();
12446
+ const home = (0, import_node_os10.homedir)();
12076
12447
  const collected = [];
12077
12448
  for (const rel of [".claude/skills", ".cursor/skills", ".sideboard/skills", ".brightsy/skills", "skills"]) {
12078
- scanSkillsDir((0, import_node_path22.join)(worktreePath, rel), "workspace", collected);
12449
+ scanSkillsDir((0, import_node_path27.join)(worktreePath, rel), "workspace", collected);
12079
12450
  }
12080
12451
  for (const abs of [
12081
- (0, import_node_path22.join)(home, ".claude/skills"),
12082
- (0, import_node_path22.join)(home, ".cursor/skills"),
12083
- (0, import_node_path22.join)(home, ".sideboard/skills"),
12084
- (0, import_node_path22.join)(home, ".brightsy/skills")
12452
+ (0, import_node_path27.join)(home, ".claude/skills"),
12453
+ (0, import_node_path27.join)(home, ".cursor/skills"),
12454
+ (0, import_node_path27.join)(home, ".sideboard/skills"),
12455
+ (0, import_node_path27.join)(home, ".brightsy/skills")
12085
12456
  ]) {
12086
12457
  scanSkillsDir(abs, "user", collected);
12087
12458
  }
12088
- scanClaudePluginSkills((0, import_node_path22.join)(home, ".claude/plugins"), collected);
12459
+ scanClaudePluginSkills((0, import_node_path27.join)(home, ".claude/plugins"), collected);
12089
12460
  const rank = { workspace: 0, user: 1, cli: 2 };
12090
12461
  const byCommand = /* @__PURE__ */ new Map();
12091
12462
  for (const skill of collected) {
@@ -12097,7 +12468,7 @@ function discoverSkills(worktreePath) {
12097
12468
  return [...byCommand.values()].sort((a, b) => a.command.localeCompare(b.command));
12098
12469
  }
12099
12470
  function readSkillBody(skillPath, maxChars = 12e3) {
12100
- const raw = (0, import_node_fs25.readFileSync)(skillPath, "utf8");
12471
+ const raw = (0, import_node_fs29.readFileSync)(skillPath, "utf8");
12101
12472
  if (raw.startsWith("---")) {
12102
12473
  const end = raw.indexOf("\n---", 3);
12103
12474
  if (end >= 0) {
@@ -12238,8 +12609,8 @@ function buildDiffCommentAttachment(input) {
12238
12609
  }
12239
12610
 
12240
12611
  // src/composer/stage-files.ts
12241
- var import_node_fs26 = require("fs");
12242
- var import_node_path23 = require("path");
12612
+ var import_node_fs30 = require("fs");
12613
+ var import_node_path28 = require("path");
12243
12614
  var import_node_crypto5 = require("crypto");
12244
12615
  init_workspace_scratch();
12245
12616
  var IMAGE_EXTENSIONS2 = /* @__PURE__ */ new Set([
@@ -12265,7 +12636,7 @@ var IMAGE_MIME_BY_EXT = {
12265
12636
  var MAX_INLINE_BYTES = 4e5;
12266
12637
  var MAX_PREVIEW_BYTES = 5e6;
12267
12638
  function fileExtension(filePath) {
12268
- const base = (0, import_node_path23.basename)(filePath).toLowerCase();
12639
+ const base = (0, import_node_path28.basename)(filePath).toLowerCase();
12269
12640
  return base.includes(".") ? base.split(".").pop() || "" : "";
12270
12641
  }
12271
12642
  function isImageFilePath(filePath) {
@@ -12275,22 +12646,22 @@ function imageMimeType(filePath) {
12275
12646
  return IMAGE_MIME_BY_EXT[fileExtension(filePath)] || "image/png";
12276
12647
  }
12277
12648
  function ensureAttachmentsDir(worktreePath) {
12278
- const dir = (0, import_node_path23.join)(worktreePath, ATTACHMENTS_DIR);
12279
- (0, import_node_fs26.mkdirSync)(dir, { recursive: true });
12280
- const gi = (0, import_node_path23.join)(dir, ".gitignore");
12281
- if (!(0, import_node_fs26.existsSync)(gi)) {
12282
- (0, import_node_fs26.writeFileSync)(gi, attachmentsGitignoreBody(), "utf8");
12649
+ const dir = (0, import_node_path28.join)(worktreePath, ATTACHMENTS_DIR);
12650
+ (0, import_node_fs30.mkdirSync)(dir, { recursive: true });
12651
+ const gi = (0, import_node_path28.join)(dir, ".gitignore");
12652
+ if (!(0, import_node_fs30.existsSync)(gi)) {
12653
+ (0, import_node_fs30.writeFileSync)(gi, attachmentsGitignoreBody(), "utf8");
12283
12654
  }
12284
12655
  return dir;
12285
12656
  }
12286
12657
  function uniqueAttachmentName(dir, originalName) {
12287
12658
  const safe = originalName.replace(/[/\\]/g, "_") || "file";
12288
- if (!(0, import_node_fs26.existsSync)((0, import_node_path23.join)(dir, safe))) return safe;
12289
- const ext = (0, import_node_path23.extname)(safe);
12659
+ if (!(0, import_node_fs30.existsSync)((0, import_node_path28.join)(dir, safe))) return safe;
12660
+ const ext = (0, import_node_path28.extname)(safe);
12290
12661
  const stem = ext ? safe.slice(0, -ext.length) : safe;
12291
12662
  for (let i = 1; i < 1e4; i++) {
12292
12663
  const candidate = `${stem}-${i}${ext}`;
12293
- if (!(0, import_node_fs26.existsSync)((0, import_node_path23.join)(dir, candidate))) return candidate;
12664
+ if (!(0, import_node_fs30.existsSync)((0, import_node_path28.join)(dir, candidate))) return candidate;
12294
12665
  }
12295
12666
  return `${stem}-${(0, import_node_crypto5.randomUUID)()}${ext}`;
12296
12667
  }
@@ -12342,9 +12713,9 @@ function attachmentFromBuffer(name, buf, opts) {
12342
12713
  };
12343
12714
  }
12344
12715
  function attachmentFromAbsolutePath(absolutePath) {
12345
- const name = (0, import_node_path23.basename)(absolutePath);
12716
+ const name = (0, import_node_path28.basename)(absolutePath);
12346
12717
  try {
12347
- const st = (0, import_node_fs26.statSync)(absolutePath);
12718
+ const st = (0, import_node_fs30.statSync)(absolutePath);
12348
12719
  if (!st.isFile()) {
12349
12720
  return {
12350
12721
  id: (0, import_node_crypto5.randomUUID)(),
@@ -12353,7 +12724,7 @@ function attachmentFromAbsolutePath(absolutePath) {
12353
12724
  content: `(not a file: ${absolutePath})`
12354
12725
  };
12355
12726
  }
12356
- const buf = (0, import_node_fs26.readFileSync)(absolutePath);
12727
+ const buf = (0, import_node_fs30.readFileSync)(absolutePath);
12357
12728
  return attachmentFromBuffer(name, buf, { sourceLabel: absolutePath });
12358
12729
  } catch (err) {
12359
12730
  return {
@@ -12369,15 +12740,15 @@ function stageAbsolutePathsAsAttachments(worktreePath, absolutePaths) {
12369
12740
  const dir = ensureAttachmentsDir(worktreePath);
12370
12741
  const out = [];
12371
12742
  for (const abs of absolutePaths) {
12372
- const originalName = (0, import_node_path23.basename)(abs);
12743
+ const originalName = (0, import_node_path28.basename)(abs);
12373
12744
  try {
12374
- const st = (0, import_node_fs26.statSync)(abs);
12745
+ const st = (0, import_node_fs30.statSync)(abs);
12375
12746
  if (!st.isFile()) continue;
12376
12747
  const name = uniqueAttachmentName(dir, originalName);
12377
- const destAbs = (0, import_node_path23.join)(dir, name);
12378
- (0, import_node_fs26.copyFileSync)(abs, destAbs);
12748
+ const destAbs = (0, import_node_path28.join)(dir, name);
12749
+ (0, import_node_fs30.copyFileSync)(abs, destAbs);
12379
12750
  const rel = `${ATTACHMENTS_DIR}/${name}`;
12380
- const buf = (0, import_node_fs26.readFileSync)(destAbs);
12751
+ const buf = (0, import_node_fs30.readFileSync)(destAbs);
12381
12752
  out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
12382
12753
  } catch (err) {
12383
12754
  out.push({
@@ -12399,8 +12770,8 @@ function stageBuffersAsAttachments(worktreePath, buffers) {
12399
12770
  try {
12400
12771
  const buf = Buffer.from(item.dataBase64, "base64");
12401
12772
  const name = uniqueAttachmentName(dir, originalName);
12402
- const destAbs = (0, import_node_path23.join)(dir, name);
12403
- (0, import_node_fs26.writeFileSync)(destAbs, buf);
12773
+ const destAbs = (0, import_node_path28.join)(dir, name);
12774
+ (0, import_node_fs30.writeFileSync)(destAbs, buf);
12404
12775
  const rel = `${ATTACHMENTS_DIR}/${name}`;
12405
12776
  out.push(attachmentFromBuffer(name, buf, { path: rel }));
12406
12777
  } catch (err) {
@@ -12436,18 +12807,18 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
12436
12807
  if (!rel || rel.includes("..") || rel.startsWith("/")) {
12437
12808
  out.push({
12438
12809
  id: (0, import_node_crypto5.randomUUID)(),
12439
- name: (0, import_node_path23.basename)(rel) || "file",
12810
+ name: (0, import_node_path28.basename)(rel) || "file",
12440
12811
  kind: "file",
12441
12812
  content: `(invalid path: ${rel})`
12442
12813
  });
12443
12814
  continue;
12444
12815
  }
12445
- const name = (0, import_node_path23.basename)(rel);
12816
+ const name = (0, import_node_path28.basename)(rel);
12446
12817
  try {
12447
- const abs = (0, import_node_path23.join)(worktreePath, rel);
12448
- const st = (0, import_node_fs26.statSync)(abs);
12818
+ const abs = (0, import_node_path28.join)(worktreePath, rel);
12819
+ const st = (0, import_node_fs30.statSync)(abs);
12449
12820
  if (!st.isFile()) continue;
12450
- const buf = (0, import_node_fs26.readFileSync)(abs);
12821
+ const buf = (0, import_node_fs30.readFileSync)(abs);
12451
12822
  out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
12452
12823
  } catch (err) {
12453
12824
  out.push({
@@ -12935,7 +13306,7 @@ async function confirmLand(thread, opts) {
12935
13306
  }
12936
13307
 
12937
13308
  // src/threads/create.ts
12938
- var import_node_fs27 = require("fs");
13309
+ var import_node_fs31 = require("fs");
12939
13310
  init_worktree();
12940
13311
  init_app_settings();
12941
13312
  init_thread_store();
@@ -12949,12 +13320,13 @@ async function createThread(input, _onSetupLine) {
12949
13320
  });
12950
13321
  await requireAgent(resolved.agent);
12951
13322
  const repoPath = await resolveRepoRoot(input.repoPath);
12952
- if (!(0, import_node_fs27.existsSync)(repoPath)) {
13323
+ if (!(0, import_node_fs31.existsSync)(repoPath)) {
12953
13324
  throw new Error(`Repo not found: ${repoPath}`);
12954
13325
  }
12955
13326
  let sourceRef = input.sourceRef;
12956
13327
  let sourceIsFork = false;
12957
13328
  let prUrl = null;
13329
+ let prTitle = null;
12958
13330
  if (input.sourceType === "pr") {
12959
13331
  const num2 = Number(input.sourceRef.replace(/^#/, ""));
12960
13332
  if (!Number.isFinite(num2)) throw new Error(`Invalid PR number: ${input.sourceRef}`);
@@ -12970,6 +13342,12 @@ async function createThread(input, _onSetupLine) {
12970
13342
  } else if (input.sourceType === "branch") {
12971
13343
  if (!sourceRef || sourceRef === "HEAD" || sourceRef === "default") {
12972
13344
  sourceRef = await resolveDefaultBranch(repoPath);
13345
+ } else {
13346
+ const existing = await getPrForHeadBranch(repoPath, sourceRef);
13347
+ if (existing?.url) {
13348
+ prUrl = existing.url;
13349
+ prTitle = existing.title;
13350
+ }
12973
13351
  }
12974
13352
  } else if (input.sourceType === "adopt") {
12975
13353
  throw new Error("Use adoptThread() for adopt sources");
@@ -13000,7 +13378,8 @@ async function createThread(input, _onSetupLine) {
13000
13378
  sourceIsFork,
13001
13379
  parentThreadId: input.parentThreadId ?? null,
13002
13380
  status: "idle",
13003
- prUrl
13381
+ prUrl,
13382
+ prTitle
13004
13383
  });
13005
13384
  writeThread(thread);
13006
13385
  await ensureWorkspace(repoPath);
@@ -13176,7 +13555,7 @@ async function forkThreadWorktree(input, onSetupLine) {
13176
13555
  }
13177
13556
 
13178
13557
  // src/threads/stack-layers.ts
13179
- var import_node_fs28 = require("fs");
13558
+ var import_node_fs32 = require("fs");
13180
13559
  init_run();
13181
13560
  init_stack();
13182
13561
  init_worktree();
@@ -13244,7 +13623,7 @@ async function openStackLayer(input, _onSetupLine) {
13244
13623
  let createdWorktree = false;
13245
13624
  const trees = await listWorktrees(repoPath);
13246
13625
  const checkedOut = trees.find((w) => w.branch === branchName);
13247
- if (checkedOut?.path && (0, import_node_fs28.existsSync)(checkedOut.path)) {
13626
+ if (checkedOut?.path && (0, import_node_fs32.existsSync)(checkedOut.path)) {
13248
13627
  if (input.reuseExistingWorktree !== false) {
13249
13628
  worktreePath = checkedOut.path;
13250
13629
  } else {
@@ -13386,7 +13765,7 @@ async function initStackFromThread(input, onSetupLine) {
13386
13765
  async function createPrStack(input, onSetupLine) {
13387
13766
  await requireAgent(input.agent);
13388
13767
  const repoPath = await resolveRepoRoot(input.repoPath);
13389
- if (!(0, import_node_fs28.existsSync)(repoPath)) throw new Error(`Repo not found: ${repoPath}`);
13768
+ if (!(0, import_node_fs32.existsSync)(repoPath)) throw new Error(`Repo not found: ${repoPath}`);
13390
13769
  if (!input.branches.length) throw new Error("At least one branch name required");
13391
13770
  const status = await detectGhStack(repoPath);
13392
13771
  if (!status.available) throw new Error(status.reason);
@@ -13453,7 +13832,7 @@ async function createPrStack(input, onSetupLine) {
13453
13832
  }
13454
13833
  }
13455
13834
  const claimed = new Set(threads.map((t) => t.worktreePath));
13456
- if (!claimed.has(bootstrap.worktreePath) && (0, import_node_fs28.existsSync)(bootstrap.worktreePath)) {
13835
+ if (!claimed.has(bootstrap.worktreePath) && (0, import_node_fs32.existsSync)(bootstrap.worktreePath)) {
13457
13836
  try {
13458
13837
  await removeWorktree(repoPath, bootstrap.worktreePath, {
13459
13838
  deleteBranch: bootstrap.branchName
@@ -13476,20 +13855,26 @@ function stackAgentDefaultsFrom(input) {
13476
13855
 
13477
13856
  // src/threads/adopt.ts
13478
13857
  var import_node_child_process4 = require("child_process");
13479
- var import_node_fs29 = require("fs");
13480
- var import_node_os9 = require("os");
13481
- var import_node_path24 = require("path");
13482
- var import_better_sqlite3 = __toESM(require("better-sqlite3"), 1);
13858
+ var import_node_fs33 = require("fs");
13859
+ var import_node_os11 = require("os");
13860
+ var import_node_path29 = require("path");
13861
+ var import_node_module4 = require("module");
13483
13862
  init_worktree();
13484
13863
  init_thread_store();
13485
- var CONDUCTOR_APP_SUPPORT = (0, import_node_path24.join)(
13864
+ var import_meta4 = {};
13865
+ var CONDUCTOR_APP_SUPPORT = (0, import_node_path29.join)(
13486
13866
  process.env.HOME ?? "",
13487
13867
  "Library",
13488
13868
  "Application Support",
13489
13869
  "com.conductor.app"
13490
13870
  );
13491
- var CONDUCTOR_DB = (0, import_node_path24.join)(CONDUCTOR_APP_SUPPORT, "conductor.db");
13492
- var CURSOR_SDK_STORE = (0, import_node_path24.join)(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
13871
+ var CONDUCTOR_DB = (0, import_node_path29.join)(CONDUCTOR_APP_SUPPORT, "conductor.db");
13872
+ var CURSOR_SDK_STORE = (0, import_node_path29.join)(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
13873
+ function openReadonlySqlite(file) {
13874
+ const req = (0, import_node_module4.createRequire)(import_meta4.url);
13875
+ const Database = req("better-sqlite3");
13876
+ return new Database(file, { readonly: true, fileMustExist: true });
13877
+ }
13493
13878
  function mapAgentType(raw) {
13494
13879
  if (!raw) return null;
13495
13880
  const v = raw.toLowerCase();
@@ -13501,21 +13886,21 @@ function mapAgentType(raw) {
13501
13886
  return null;
13502
13887
  }
13503
13888
  function resolveConductorCursorAgentId(workspacePath) {
13504
- if (!workspacePath || !(0, import_node_fs29.existsSync)(CURSOR_SDK_STORE)) return null;
13889
+ if (!workspacePath || !(0, import_node_fs33.existsSync)(CURSOR_SDK_STORE)) return null;
13505
13890
  const normalized = workspacePath.replace(/\/$/, "");
13506
13891
  let best = null;
13507
13892
  let hashes;
13508
13893
  try {
13509
- hashes = (0, import_node_fs29.readdirSync)(CURSOR_SDK_STORE);
13894
+ hashes = (0, import_node_fs33.readdirSync)(CURSOR_SDK_STORE);
13510
13895
  } catch {
13511
13896
  return null;
13512
13897
  }
13513
13898
  for (const hash of hashes) {
13514
- const agentsFile = (0, import_node_path24.join)(CURSOR_SDK_STORE, hash, "agents.ndjson");
13515
- if (!(0, import_node_fs29.existsSync)(agentsFile)) continue;
13899
+ const agentsFile = (0, import_node_path29.join)(CURSOR_SDK_STORE, hash, "agents.ndjson");
13900
+ if (!(0, import_node_fs33.existsSync)(agentsFile)) continue;
13516
13901
  let text3;
13517
13902
  try {
13518
- text3 = (0, import_node_fs29.readFileSync)(agentsFile, "utf8");
13903
+ text3 = (0, import_node_fs33.readFileSync)(agentsFile, "utf8");
13519
13904
  } catch {
13520
13905
  continue;
13521
13906
  }
@@ -13539,7 +13924,7 @@ function resolveConductorCursorAgentId(workspacePath) {
13539
13924
  return best?.agentId ?? null;
13540
13925
  }
13541
13926
  async function adoptThread(input) {
13542
- if (!(0, import_node_fs29.existsSync)(input.worktreePath)) {
13927
+ if (!(0, import_node_fs33.existsSync)(input.worktreePath)) {
13543
13928
  throw new Error(`Worktree not found: ${input.worktreePath}`);
13544
13929
  }
13545
13930
  const repoPath = await resolveRepoRoot(input.worktreePath);
@@ -13566,23 +13951,23 @@ function conductorDbPath() {
13566
13951
  return CONDUCTOR_DB;
13567
13952
  }
13568
13953
  function listConductorWorkspaces() {
13569
- if (!(0, import_node_fs29.existsSync)(CONDUCTOR_DB)) {
13954
+ if (!(0, import_node_fs33.existsSync)(CONDUCTOR_DB)) {
13570
13955
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
13571
13956
  }
13572
- const tmp = (0, import_node_fs29.mkdtempSync)((0, import_node_path24.join)((0, import_node_os9.tmpdir)(), "sideboard-conductor-"));
13573
- const snapshot = (0, import_node_path24.join)(tmp, "conductor.db");
13957
+ const tmp = (0, import_node_fs33.mkdtempSync)((0, import_node_path29.join)((0, import_node_os11.tmpdir)(), "sideboard-conductor-"));
13958
+ const snapshot = (0, import_node_path29.join)(tmp, "conductor.db");
13574
13959
  try {
13575
- (0, import_node_fs29.copyFileSync)(CONDUCTOR_DB, snapshot);
13960
+ (0, import_node_fs33.copyFileSync)(CONDUCTOR_DB, snapshot);
13576
13961
  for (const suffix of ["-wal", "-shm"]) {
13577
13962
  const src = `${CONDUCTOR_DB}${suffix}`;
13578
- if ((0, import_node_fs29.existsSync)(src)) {
13963
+ if ((0, import_node_fs33.existsSync)(src)) {
13579
13964
  try {
13580
- (0, import_node_fs29.copyFileSync)(src, `${snapshot}${suffix}`);
13965
+ (0, import_node_fs33.copyFileSync)(src, `${snapshot}${suffix}`);
13581
13966
  } catch {
13582
13967
  }
13583
13968
  }
13584
13969
  }
13585
- const db = new import_better_sqlite3.default(snapshot, { readonly: true, fileMustExist: true });
13970
+ const db = openReadonlySqlite(snapshot);
13586
13971
  try {
13587
13972
  const tables = db.prepare(`SELECT name FROM sqlite_master WHERE type='table'`).all();
13588
13973
  const names = new Set(tables.map((t) => t.name));
@@ -13653,27 +14038,27 @@ function listConductorWorkspaces() {
13653
14038
  db.close();
13654
14039
  }
13655
14040
  } finally {
13656
- (0, import_node_fs29.rmSync)(tmp, { recursive: true, force: true });
14041
+ (0, import_node_fs33.rmSync)(tmp, { recursive: true, force: true });
13657
14042
  }
13658
14043
  }
13659
14044
  function importConductorWorkspace(workspaceId) {
13660
- if (!(0, import_node_fs29.existsSync)(CONDUCTOR_DB)) {
14045
+ if (!(0, import_node_fs33.existsSync)(CONDUCTOR_DB)) {
13661
14046
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
13662
14047
  }
13663
- const tmp = (0, import_node_fs29.mkdtempSync)((0, import_node_path24.join)((0, import_node_os9.tmpdir)(), "sideboard-conductor-"));
13664
- const snapshot = (0, import_node_path24.join)(tmp, "conductor.db");
14048
+ const tmp = (0, import_node_fs33.mkdtempSync)((0, import_node_path29.join)((0, import_node_os11.tmpdir)(), "sideboard-conductor-"));
14049
+ const snapshot = (0, import_node_path29.join)(tmp, "conductor.db");
13665
14050
  try {
13666
- (0, import_node_fs29.copyFileSync)(CONDUCTOR_DB, snapshot);
14051
+ (0, import_node_fs33.copyFileSync)(CONDUCTOR_DB, snapshot);
13667
14052
  for (const suffix of ["-wal", "-shm"]) {
13668
14053
  const src = `${CONDUCTOR_DB}${suffix}`;
13669
- if ((0, import_node_fs29.existsSync)(src)) {
14054
+ if ((0, import_node_fs33.existsSync)(src)) {
13670
14055
  try {
13671
- (0, import_node_fs29.copyFileSync)(src, `${snapshot}${suffix}`);
14056
+ (0, import_node_fs33.copyFileSync)(src, `${snapshot}${suffix}`);
13672
14057
  } catch {
13673
14058
  }
13674
14059
  }
13675
14060
  }
13676
- const db = new import_better_sqlite3.default(snapshot, { readonly: true, fileMustExist: true });
14061
+ const db = openReadonlySqlite(snapshot);
13677
14062
  try {
13678
14063
  const row = db.prepare(
13679
14064
  `SELECT
@@ -13686,7 +14071,7 @@ function importConductorWorkspace(workspaceId) {
13686
14071
  ).get(workspaceId);
13687
14072
  if (!row) throw new Error(`Conductor workspace not found: ${workspaceId}`);
13688
14073
  const worktreePath = String(row.workspacePath);
13689
- if (!(0, import_node_fs29.existsSync)(worktreePath)) {
14074
+ if (!(0, import_node_fs33.existsSync)(worktreePath)) {
13690
14075
  throw new Error(`Conductor worktree missing on disk: ${worktreePath}`);
13691
14076
  }
13692
14077
  let sessionId = null;
@@ -13749,7 +14134,7 @@ function importConductorWorkspace(workspaceId) {
13749
14134
  db.close();
13750
14135
  }
13751
14136
  } finally {
13752
- (0, import_node_fs29.rmSync)(tmp, { recursive: true, force: true });
14137
+ (0, import_node_fs33.rmSync)(tmp, { recursive: true, force: true });
13753
14138
  }
13754
14139
  }
13755
14140
  async function importConductorWorkspaceAsync(workspaceId) {
@@ -13760,8 +14145,8 @@ async function importConductorWorkspaceAsync(workspaceId) {
13760
14145
  var import_node_events = require("events");
13761
14146
 
13762
14147
  // src/slack/outbound-watch.ts
13763
- var import_node_fs31 = require("fs");
13764
- var import_node_path27 = require("path");
14148
+ var import_node_fs35 = require("fs");
14149
+ var import_node_path32 = require("path");
13765
14150
  init_paths();
13766
14151
  init_private_file();
13767
14152
  init_secure_file();
@@ -13807,19 +14192,19 @@ async function slackAuthTest(token) {
13807
14192
  }
13808
14193
 
13809
14194
  // src/slack/reply-target.ts
13810
- var import_node_fs30 = require("fs");
13811
- var import_node_path25 = require("path");
14195
+ var import_node_fs34 = require("fs");
14196
+ var import_node_path30 = require("path");
13812
14197
  init_paths();
13813
14198
  init_private_file();
13814
14199
  init_secure_file();
13815
14200
  function storePath2() {
13816
- return (0, import_node_path25.join)(appDataDir(), "slack-reply-to.json");
14201
+ return (0, import_node_path30.join)(appDataDir(), "slack-reply-to.json");
13817
14202
  }
13818
14203
  function readStore2() {
13819
14204
  const path2 = storePath2();
13820
- if (!(0, import_node_fs30.existsSync)(path2)) return {};
14205
+ if (!(0, import_node_fs34.existsSync)(path2)) return {};
13821
14206
  try {
13822
- const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse((0, import_node_fs30.readFileSync)(path2, "utf8"));
14207
+ const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse((0, import_node_fs34.readFileSync)(path2, "utf8"));
13823
14208
  return parsed?.targets && typeof parsed.targets === "object" ? parsed.targets : {};
13824
14209
  } catch {
13825
14210
  return {};
@@ -13836,11 +14221,11 @@ function getSlackReplyTarget(threadId) {
13836
14221
  }
13837
14222
 
13838
14223
  // src/slack/workspaces.ts
13839
- var import_node_path26 = require("path");
14224
+ var import_node_path31 = require("path");
13840
14225
  init_paths();
13841
14226
  init_secure_file();
13842
14227
  function storePath3() {
13843
- return (0, import_node_path26.join)(appDataDir(), "slack-workspaces.json");
14228
+ return (0, import_node_path31.join)(appDataDir(), "slack-workspaces.json");
13844
14229
  }
13845
14230
  function readStore3() {
13846
14231
  try {
@@ -13956,7 +14341,7 @@ var POLL_INTERVAL_MS = 12e3;
13956
14341
  var lastPollMs = 0;
13957
14342
  var nameCache = /* @__PURE__ */ new Map();
13958
14343
  function storePath4() {
13959
- return (0, import_node_path27.join)(appDataDir(), "slack-outbound-watch.json");
14344
+ return (0, import_node_path32.join)(appDataDir(), "slack-outbound-watch.json");
13960
14345
  }
13961
14346
  function watchId(teamId, channelId, ts) {
13962
14347
  return `${teamId}:${channelId}:${ts}`;
@@ -13986,9 +14371,9 @@ function tsNewer(a, b) {
13986
14371
  }
13987
14372
  function readStore4() {
13988
14373
  const path2 = storePath4();
13989
- if (!(0, import_node_fs31.existsSync)(path2)) return [];
14374
+ if (!(0, import_node_fs35.existsSync)(path2)) return [];
13990
14375
  try {
13991
- const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse((0, import_node_fs31.readFileSync)(path2, "utf8"));
14376
+ const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse((0, import_node_fs35.readFileSync)(path2, "utf8"));
13992
14377
  return Array.isArray(parsed?.watches) ? parsed.watches : [];
13993
14378
  } catch {
13994
14379
  return [];
@@ -14333,7 +14718,7 @@ async function refreshSlackReplyBadges(opts) {
14333
14718
  }
14334
14719
 
14335
14720
  // src/orchestrator/orchestrator.ts
14336
- var import_node_fs37 = require("fs");
14721
+ var import_node_fs41 = require("fs");
14337
14722
  init_error_detail();
14338
14723
  init_agents();
14339
14724
  init_worktree();
@@ -14354,8 +14739,8 @@ function shouldAutoArchiveOnPrMerge(opts) {
14354
14739
  }
14355
14740
 
14356
14741
  // src/git/orphan-cleanup.ts
14357
- var import_node_fs32 = require("fs");
14358
- var import_node_path28 = require("path");
14742
+ var import_node_fs36 = require("fs");
14743
+ var import_node_path33 = require("path");
14359
14744
  init_worktree();
14360
14745
  init_thread_store();
14361
14746
  init_paths();
@@ -14370,9 +14755,9 @@ async function findOrphanWorktrees(repoPaths) {
14370
14755
  repoPaths?.length ? repoPaths : threads.map((t) => t.repoPath).filter(Boolean)
14371
14756
  );
14372
14757
  const homeRoot = sideboardWorkspacesDir();
14373
- if ((0, import_node_fs32.existsSync)(homeRoot)) {
14758
+ if ((0, import_node_fs36.existsSync)(homeRoot)) {
14374
14759
  try {
14375
- for (const entry of (0, import_node_fs32.readdirSync)(homeRoot, { withFileTypes: true })) {
14760
+ for (const entry of (0, import_node_fs36.readdirSync)(homeRoot, { withFileTypes: true })) {
14376
14761
  if (!entry.isDirectory()) continue;
14377
14762
  void entry;
14378
14763
  }
@@ -14382,7 +14767,7 @@ async function findOrphanWorktrees(repoPaths) {
14382
14767
  const orphans = [];
14383
14768
  const seen = /* @__PURE__ */ new Set();
14384
14769
  for (const repoPath of repos) {
14385
- if (!repoPath || !(0, import_node_fs32.existsSync)(repoPath)) continue;
14770
+ if (!repoPath || !(0, import_node_fs36.existsSync)(repoPath)) continue;
14386
14771
  try {
14387
14772
  const wts = await listWorktrees(repoPath);
14388
14773
  for (const wt of wts) {
@@ -14393,7 +14778,7 @@ async function findOrphanWorktrees(repoPaths) {
14393
14778
  seen.add(path2);
14394
14779
  let mtimeMs = 0;
14395
14780
  try {
14396
- mtimeMs = (0, import_node_fs32.statSync)(path2).mtimeMs;
14781
+ mtimeMs = (0, import_node_fs36.statSync)(path2).mtimeMs;
14397
14782
  } catch {
14398
14783
  mtimeMs = 0;
14399
14784
  }
@@ -14403,16 +14788,16 @@ async function findOrphanWorktrees(repoPaths) {
14403
14788
  }
14404
14789
  try {
14405
14790
  const root = worktreesRoot(repoPath);
14406
- if ((0, import_node_fs32.existsSync)(root)) {
14407
- for (const entry of (0, import_node_fs32.readdirSync)(root, { withFileTypes: true })) {
14791
+ if ((0, import_node_fs36.existsSync)(root)) {
14792
+ for (const entry of (0, import_node_fs36.readdirSync)(root, { withFileTypes: true })) {
14408
14793
  if (!entry.isDirectory()) continue;
14409
- const path2 = (0, import_node_path28.join)(root, entry.name).replace(/\/$/, "");
14794
+ const path2 = (0, import_node_path33.join)(root, entry.name).replace(/\/$/, "");
14410
14795
  if (known.has(path2) || seen.has(path2)) continue;
14411
- if (!(0, import_node_fs32.existsSync)((0, import_node_path28.join)(path2, ".git"))) continue;
14796
+ if (!(0, import_node_fs36.existsSync)((0, import_node_path33.join)(path2, ".git"))) continue;
14412
14797
  seen.add(path2);
14413
14798
  let mtimeMs = 0;
14414
14799
  try {
14415
- mtimeMs = (0, import_node_fs32.statSync)(path2).mtimeMs;
14800
+ mtimeMs = (0, import_node_fs36.statSync)(path2).mtimeMs;
14416
14801
  } catch {
14417
14802
  mtimeMs = Date.now();
14418
14803
  }
@@ -14558,8 +14943,8 @@ async function applyThreadIntoMain(thread, opts) {
14558
14943
  }
14559
14944
 
14560
14945
  // src/git/clone-repo.ts
14561
- var import_node_fs33 = require("fs");
14562
- var import_node_path29 = require("path");
14946
+ var import_node_fs37 = require("fs");
14947
+ var import_node_path34 = require("path");
14563
14948
  var import_execa6 = require("execa");
14564
14949
  init_paths();
14565
14950
  init_workspaces();
@@ -14569,12 +14954,12 @@ async function cloneRepoIntoSideboard(opts) {
14569
14954
  if (!url) throw new Error("Clone URL is required");
14570
14955
  let name = opts.name?.trim();
14571
14956
  if (!name) {
14572
- const leaf = (0, import_node_path29.basename)(url.replace(/\/$/, "").replace(/\.git$/, ""));
14957
+ const leaf = (0, import_node_path34.basename)(url.replace(/\/$/, "").replace(/\.git$/, ""));
14573
14958
  name = leaf || "repo";
14574
14959
  }
14575
14960
  name = name.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "repo";
14576
- const dest = (0, import_node_path29.join)(sideboardReposDir(), name);
14577
- if ((0, import_node_fs33.existsSync)(dest)) {
14961
+ const dest = (0, import_node_path34.join)(sideboardReposDir(), name);
14962
+ if ((0, import_node_fs37.existsSync)(dest)) {
14578
14963
  const repoPath2 = await resolveRepoRoot(dest);
14579
14964
  const workspace2 = await ensureWorkspace(repoPath2);
14580
14965
  return { repoPath: repoPath2, workspace: workspace2 };
@@ -14596,8 +14981,8 @@ init_orchestrator_capable();
14596
14981
 
14597
14982
  // src/review/request-review.ts
14598
14983
  var import_node_crypto8 = require("crypto");
14599
- var import_node_fs34 = require("fs");
14600
- var import_node_path30 = require("path");
14984
+ var import_node_fs38 = require("fs");
14985
+ var import_node_path35 = require("path");
14601
14986
  init_global_workspace();
14602
14987
  init_thread_store();
14603
14988
 
@@ -14745,22 +15130,22 @@ function shouldRefreshReviewRequestTemplate(content) {
14745
15130
  return LEGACY_REVIEW_TEMPLATE_MARKERS.every((m) => trimmed.includes(m));
14746
15131
  }
14747
15132
  function readTextIfPresent(abs) {
14748
- if (!(0, import_node_fs34.existsSync)(abs)) return null;
15133
+ if (!(0, import_node_fs38.existsSync)(abs)) return null;
14749
15134
  try {
14750
- const content = (0, import_node_fs34.readFileSync)(abs, "utf8");
15135
+ const content = (0, import_node_fs38.readFileSync)(abs, "utf8");
14751
15136
  return content.trim() ? content : null;
14752
15137
  } catch {
14753
15138
  return null;
14754
15139
  }
14755
15140
  }
14756
15141
  function ensureAttachmentsGitignore(worktreePath) {
14757
- const gitignoreAbs = (0, import_node_path30.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
14758
- if ((0, import_node_fs34.existsSync)(gitignoreAbs)) return;
14759
- (0, import_node_fs34.mkdirSync)((0, import_node_path30.dirname)(gitignoreAbs), { recursive: true });
14760
- (0, import_node_fs34.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
15142
+ const gitignoreAbs = (0, import_node_path35.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
15143
+ if ((0, import_node_fs38.existsSync)(gitignoreAbs)) return;
15144
+ (0, import_node_fs38.mkdirSync)((0, import_node_path35.dirname)(gitignoreAbs), { recursive: true });
15145
+ (0, import_node_fs38.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
14761
15146
  }
14762
15147
  function resolveReviewGuidelines(worktreePath) {
14763
- const repoAbs = (0, import_node_path30.join)(worktreePath, REPO_REVIEW_PATH);
15148
+ const repoAbs = (0, import_node_path35.join)(worktreePath, REPO_REVIEW_PATH);
14764
15149
  const repoContent = readTextIfPresent(repoAbs);
14765
15150
  if (repoContent) {
14766
15151
  return {
@@ -14770,7 +15155,7 @@ function resolveReviewGuidelines(worktreePath) {
14770
15155
  source: "repo"
14771
15156
  };
14772
15157
  }
14773
- const localAbs = (0, import_node_path30.join)(worktreePath, REVIEW_REQUEST_PATH);
15158
+ const localAbs = (0, import_node_path35.join)(worktreePath, REVIEW_REQUEST_PATH);
14774
15159
  const localContent = readTextIfPresent(localAbs);
14775
15160
  if (localContent && !shouldRefreshReviewRequestTemplate(localContent)) {
14776
15161
  return {
@@ -14780,7 +15165,7 @@ function resolveReviewGuidelines(worktreePath) {
14780
15165
  source: "local"
14781
15166
  };
14782
15167
  }
14783
- const legacyAbs = (0, import_node_path30.join)(worktreePath, LEGACY_REVIEW_REQUEST_PATH);
15168
+ const legacyAbs = (0, import_node_path35.join)(worktreePath, LEGACY_REVIEW_REQUEST_PATH);
14784
15169
  const legacyContent = readTextIfPresent(legacyAbs);
14785
15170
  if (legacyContent && !shouldRefreshReviewRequestTemplate(legacyContent)) {
14786
15171
  return {
@@ -14791,8 +15176,8 @@ function resolveReviewGuidelines(worktreePath) {
14791
15176
  };
14792
15177
  }
14793
15178
  ensureAttachmentsGitignore(worktreePath);
14794
- (0, import_node_fs34.mkdirSync)((0, import_node_path30.dirname)(localAbs), { recursive: true });
14795
- (0, import_node_fs34.writeFileSync)(localAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
15179
+ (0, import_node_fs38.mkdirSync)((0, import_node_path35.dirname)(localAbs), { recursive: true });
15180
+ (0, import_node_fs38.writeFileSync)(localAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
14796
15181
  return {
14797
15182
  path: REVIEW_REQUEST_PATH,
14798
15183
  name: REVIEW_REQUEST_NAME,
@@ -14801,7 +15186,7 @@ function resolveReviewGuidelines(worktreePath) {
14801
15186
  };
14802
15187
  }
14803
15188
  function ensureReviewRequestFile(worktreePath) {
14804
- const repoAbs = (0, import_node_path30.join)(worktreePath, REPO_REVIEW_PATH);
15189
+ const repoAbs = (0, import_node_path35.join)(worktreePath, REPO_REVIEW_PATH);
14805
15190
  const repoContent = readTextIfPresent(repoAbs);
14806
15191
  if (repoContent) {
14807
15192
  return {
@@ -14811,10 +15196,10 @@ function ensureReviewRequestFile(worktreePath) {
14811
15196
  source: "repo"
14812
15197
  };
14813
15198
  }
14814
- const localAbs = (0, import_node_path30.join)(worktreePath, REVIEW_REQUEST_PATH);
14815
- const localContent = readTextIfPresent(localAbs) ?? readTextIfPresent((0, import_node_path30.join)(worktreePath, LEGACY_REVIEW_REQUEST_PATH));
15199
+ const localAbs = (0, import_node_path35.join)(worktreePath, REVIEW_REQUEST_PATH);
15200
+ const localContent = readTextIfPresent(localAbs) ?? readTextIfPresent((0, import_node_path35.join)(worktreePath, LEGACY_REVIEW_REQUEST_PATH));
14816
15201
  if (localContent && !shouldRefreshReviewRequestTemplate(localContent)) {
14817
- const path2 = (0, import_node_fs34.existsSync)(localAbs) ? REVIEW_REQUEST_PATH : LEGACY_REVIEW_REQUEST_PATH;
15202
+ const path2 = (0, import_node_fs38.existsSync)(localAbs) ? REVIEW_REQUEST_PATH : LEGACY_REVIEW_REQUEST_PATH;
14818
15203
  return {
14819
15204
  path: path2,
14820
15205
  name: REVIEW_REQUEST_NAME,
@@ -14822,8 +15207,8 @@ function ensureReviewRequestFile(worktreePath) {
14822
15207
  source: "local"
14823
15208
  };
14824
15209
  }
14825
- (0, import_node_fs34.mkdirSync)((0, import_node_path30.dirname)(repoAbs), { recursive: true });
14826
- (0, import_node_fs34.writeFileSync)(repoAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
15210
+ (0, import_node_fs38.mkdirSync)((0, import_node_path35.dirname)(repoAbs), { recursive: true });
15211
+ (0, import_node_fs38.writeFileSync)(repoAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
14827
15212
  return {
14828
15213
  path: REPO_REVIEW_PATH,
14829
15214
  name: REPO_REVIEW_NAME,
@@ -14843,7 +15228,7 @@ function buildReviewRequestAttachment(content, opts) {
14843
15228
  };
14844
15229
  }
14845
15230
  function readExistingReviewRequestFile(worktreePath) {
14846
- return readTextIfPresent((0, import_node_path30.join)(worktreePath, REPO_REVIEW_PATH)) ?? readTextIfPresent((0, import_node_path30.join)(worktreePath, REVIEW_REQUEST_PATH)) ?? readTextIfPresent((0, import_node_path30.join)(worktreePath, LEGACY_REVIEW_REQUEST_PATH));
15231
+ return readTextIfPresent((0, import_node_path35.join)(worktreePath, REPO_REVIEW_PATH)) ?? readTextIfPresent((0, import_node_path35.join)(worktreePath, REVIEW_REQUEST_PATH)) ?? readTextIfPresent((0, import_node_path35.join)(worktreePath, LEGACY_REVIEW_REQUEST_PATH));
14847
15232
  }
14848
15233
  async function requestReview(threadRef, send2) {
14849
15234
  const from = findThreadByRef(threadRef);
@@ -15104,7 +15489,7 @@ var Orchestrator = class {
15104
15489
  }
15105
15490
  continue;
15106
15491
  }
15107
- if (!(0, import_node_fs37.existsSync)(thread.worktreePath)) {
15492
+ if (!(0, import_node_fs41.existsSync)(thread.worktreePath)) {
15108
15493
  setStatus(thread.id, "broken", "Worktree missing on disk");
15109
15494
  this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
15110
15495
  continue;
@@ -16196,8 +16581,9 @@ var Orchestrator = class {
16196
16581
  return result;
16197
16582
  }
16198
16583
  async mergePr(threadRef) {
16199
- const { thread, selector, cwd } = await this.withPrSelector(threadRef);
16584
+ const { thread, selectors, cwd } = await this.withPrSelector(threadRef);
16200
16585
  this.assertNotGlobal(thread, "Merge PR");
16586
+ const selector = selectors[0];
16201
16587
  if (!selector) throw new Error("No pull request linked to this thread");
16202
16588
  const result = await mergePr(cwd, selector);
16203
16589
  const state = normalizePrState(result.state) || "MERGED";
@@ -16217,29 +16603,34 @@ var Orchestrator = class {
16217
16603
  await this.persistPrMetaAndMaybeArchive(thread, metaLike);
16218
16604
  return { url: metaLike.url, state };
16219
16605
  }
16220
- /** Resolve PR selector and optionally persist `prUrl` when found. */
16606
+ /** Resolve PR selectors and optionally persist `prUrl` when found. */
16221
16607
  async withPrSelector(threadRef) {
16222
16608
  const thread = this.requireThread(threadRef);
16223
- const selector = resolvePrSelector(thread);
16609
+ const selectors = resolvePrSelectors(thread);
16224
16610
  const cwd = thread.worktreePath;
16225
16611
  if (!cwd?.trim()) {
16226
16612
  throw new Error(`Thread ${threadRef} has no worktreePath`);
16227
16613
  }
16228
- return { thread, selector, cwd };
16614
+ return { thread, selectors, cwd };
16229
16615
  }
16230
16616
  async getPrChecks(threadRef) {
16231
- const { selector, cwd } = await this.withPrSelector(threadRef);
16232
- if (!selector) return null;
16233
- return getPrChecks(cwd, selector);
16617
+ const { selectors, cwd } = await this.withPrSelector(threadRef);
16618
+ for (const selector of selectors) {
16619
+ const checks = await getPrChecks(cwd, selector);
16620
+ if (checks) return checks;
16621
+ }
16622
+ return null;
16234
16623
  }
16235
16624
  async getPrMeta(threadRef) {
16236
- const { thread, selector, cwd } = await this.withPrSelector(threadRef);
16237
- if (!selector) return null;
16238
- const meta = await getPrMeta(cwd, selector);
16239
- if (meta) {
16240
- await this.persistPrMetaAndMaybeArchive(thread, meta);
16625
+ const { thread, selectors, cwd } = await this.withPrSelector(threadRef);
16626
+ for (const selector of selectors) {
16627
+ const meta = await getPrMeta(cwd, selector);
16628
+ if (meta) {
16629
+ await this.persistPrMetaAndMaybeArchive(thread, meta);
16630
+ return meta;
16631
+ }
16241
16632
  }
16242
- return meta;
16633
+ return null;
16243
16634
  }
16244
16635
  /**
16245
16636
  * Persist PR URL/title/state and Conductor-style auto-archive when the PR
@@ -16363,9 +16754,12 @@ var Orchestrator = class {
16363
16754
  return { stack: result.stack, threads: result.threads };
16364
16755
  }
16365
16756
  async getPrDetails(threadRef) {
16366
- const { thread, selector, cwd } = await this.withPrSelector(threadRef);
16367
- if (!selector) return null;
16368
- const details = await getPrDetails(cwd, selector);
16757
+ const { thread, selectors, cwd } = await this.withPrSelector(threadRef);
16758
+ let details = null;
16759
+ for (const selector of selectors) {
16760
+ details = await getPrDetails(cwd, selector);
16761
+ if (details) break;
16762
+ }
16369
16763
  if (details) {
16370
16764
  const patch = {};
16371
16765
  if (details.url && details.url !== thread.prUrl) patch.prUrl = details.url;
@@ -16592,7 +16986,7 @@ var Orchestrator = class {
16592
16986
  this.emit({ type: "status_changed", threadId: restored2.id, status: restored2.status });
16593
16987
  return restored2;
16594
16988
  }
16595
- if (!(0, import_node_fs37.existsSync)(thread.worktreePath)) {
16989
+ if (!(0, import_node_fs41.existsSync)(thread.worktreePath)) {
16596
16990
  const { createThreadWorktree: createThreadWorktree2 } = await Promise.resolve().then(() => (init_worktree(), worktree_exports));
16597
16991
  const { execa: execa7 } = await import("execa");
16598
16992
  const slug = thread.worktreePath.split("/").pop();
@@ -16833,7 +17227,7 @@ init_coordinator_prompt();
16833
17227
  var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
16834
17228
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
16835
17229
  var import_zod3 = require("zod");
16836
- var import_node_path33 = require("path");
17230
+ var import_node_path38 = require("path");
16837
17231
  init_worktree();
16838
17232
  init_global_workspace();
16839
17233
  init_list_models();
@@ -17447,6 +17841,7 @@ function registerLinearTools(server) {
17447
17841
  }
17448
17842
 
17449
17843
  // src/mcp/server.ts
17844
+ init_git_auth_mode();
17450
17845
  var MAX_ORCH_THREADS = 5;
17451
17846
  var CREATE_THREAD_TIMEOUT_MS = 9e4;
17452
17847
  function withTimeout(promise, ms, label) {
@@ -17468,6 +17863,14 @@ function withTimeout(promise, ms, label) {
17468
17863
  }
17469
17864
  async function startMcpServer() {
17470
17865
  const orch = getOrchestrator();
17866
+ try {
17867
+ await warmGithubAgentAuth();
17868
+ } catch (err) {
17869
+ console.error(
17870
+ "[sideboard-mcp] GitHub agent auth warm skipped:",
17871
+ err instanceof Error ? err.message : err
17872
+ );
17873
+ }
17471
17874
  try {
17472
17875
  const { maxConcurrentAgents: maxConcurrentAgents2 } = await Promise.resolve().then(() => (init_app_settings(), app_settings_exports));
17473
17876
  orch.setMaxConcurrent(maxConcurrentAgents2());
@@ -17511,7 +17914,7 @@ async function startMcpServer() {
17511
17914
  async () => {
17512
17915
  const threads = orch.getThreads(true);
17513
17916
  const lines = threads.map((t) => {
17514
- const repo = t.repoPath === GLOBAL_WORKSPACE_ID ? "Orchestration" : (0, import_node_path33.basename)(t.repoPath) || t.repoPath;
17917
+ const repo = t.repoPath === GLOBAL_WORKSPACE_ID ? "Orchestration" : (0, import_node_path38.basename)(t.repoPath) || t.repoPath;
17515
17918
  return `${t.id.slice(0, 8)} ${t.status.padEnd(9)} ${t.agent.padEnd(8)} ${repo} ${t.sourceType}:${t.sourceRef} ${t.title} sideboard://thread/${t.id}${t.devPort ? ` http://localhost:${t.devPort}` : ""}`;
17516
17919
  });
17517
17920
  return {
@@ -17574,7 +17977,7 @@ async function startMcpServer() {
17574
17977
  );
17575
17978
  server.tool(
17576
17979
  "ask_user",
17577
- "Ask the user clarifying multiple-choice questions in Sideboard\u2019s composer (any mode \u2014 not only Plan). Before calling, write a short chat message explaining the decision and what each option means. Include a description on every option. Use when the user should pick from predefined options (approach forks, requirements, which API) \u2014 not for \u201Cis the plan ready?\u201D. After calling, stop and wait for their next message with answers.",
17980
+ "Ask the user a clarifying multiple-choice question in Sideboard\u2019s composer. Call only when work is blocked on choosing among a few concrete options (approach fork, which API, auth vs cookies). Do not call for greetings, check-ins, \u201Chello\u201D, open-ended how-can-I-help, or to invent a menu of possible next tasks \u2014 reply in chat instead. If one option is the obvious default, proceed without asking. Before calling, write a short chat message explaining the decision and what each option means. Include a description on every option. After calling, stop and wait for answers. Not for \u201Cis the plan ready?\u201D.",
17578
17981
  {
17579
17982
  questions: import_zod3.z.array(
17580
17983
  import_zod3.z.object({
@@ -18819,6 +19222,76 @@ init_accounts();
18819
19222
  init_connected_teams();
18820
19223
  init_injected_mcp();
18821
19224
 
19225
+ // src/agents/user-mcp-config.ts
19226
+ var import_node_fs42 = require("fs");
19227
+ var import_node_os12 = require("os");
19228
+ var import_node_path39 = require("path");
19229
+ init_paths();
19230
+ init_injected_mcp();
19231
+ function userCursorMcpConfigPath() {
19232
+ return (0, import_node_path39.join)((0, import_node_os12.homedir)(), ".cursor", "mcp.json");
19233
+ }
19234
+ function userClaudeMcpConfigPath() {
19235
+ return (0, import_node_path39.join)((0, import_node_os12.homedir)(), ".claude.json");
19236
+ }
19237
+ function asObject(value) {
19238
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
19239
+ return { ...value };
19240
+ }
19241
+ function stripElectronSpawnEnv(env) {
19242
+ if (!env) return void 0;
19243
+ const next = { ...env };
19244
+ delete next.ELECTRON_RUN_AS_NODE;
19245
+ delete next.ELECTRON_RUN_AS_NODE;
19246
+ return Object.keys(next).length > 0 ? next : void 0;
19247
+ }
19248
+ function mergeSideboardIntoMcpServersJson(existing, sideboard) {
19249
+ const root = asObject(existing);
19250
+ const servers = asObject(root.mcpServers);
19251
+ const env = stripElectronSpawnEnv(sideboard.env);
19252
+ servers.sideboard = {
19253
+ command: sideboard.command,
19254
+ ...sideboard.args && sideboard.args.length > 0 ? { args: sideboard.args } : {},
19255
+ ...env ? { env } : {}
19256
+ };
19257
+ return { ...root, mcpServers: servers };
19258
+ }
19259
+ function writeMergedMcpServersJson(configPath, sideboard) {
19260
+ let existing = {};
19261
+ if ((0, import_node_fs42.existsSync)(configPath)) {
19262
+ try {
19263
+ existing = JSON.parse((0, import_node_fs42.readFileSync)(configPath, "utf8"));
19264
+ } catch {
19265
+ existing = {};
19266
+ }
19267
+ }
19268
+ const next = mergeSideboardIntoMcpServersJson(existing, sideboard);
19269
+ (0, import_node_fs42.mkdirSync)((0, import_node_path39.dirname)(configPath), { recursive: true });
19270
+ (0, import_node_fs42.writeFileSync)(configPath, `${JSON.stringify(next, null, 2)}
19271
+ `);
19272
+ }
19273
+ function launchFromResolved(server) {
19274
+ return {
19275
+ command: server.command,
19276
+ args: server.args,
19277
+ env: {
19278
+ ...server.env ?? {},
19279
+ SIDEBOARD_APP_DATA: appDataDir()
19280
+ }
19281
+ };
19282
+ }
19283
+ async function registerPackagedUserMcpClients() {
19284
+ const launch = launchFromResolved(await resolveSideboardMcpServer());
19285
+ const cursor = userCursorMcpConfigPath();
19286
+ writeMergedMcpServersJson(cursor, launch);
19287
+ const claude = userClaudeMcpConfigPath();
19288
+ if ((0, import_node_fs42.existsSync)(claude)) {
19289
+ writeMergedMcpServersJson(claude, launch);
19290
+ return { cursor, claude };
19291
+ }
19292
+ return { cursor };
19293
+ }
19294
+
18822
19295
  // src/slack/oauth.ts
18823
19296
  var import_node_crypto10 = require("crypto");
18824
19297
  init_app_settings();
@@ -20125,9 +20598,9 @@ var import_node_http3 = require("http");
20125
20598
  var import_ws3 = require("ws");
20126
20599
 
20127
20600
  // src/slack/relay-static.ts
20128
- var import_node_fs38 = require("fs");
20601
+ var import_node_fs43 = require("fs");
20129
20602
  var import_promises = require("fs/promises");
20130
- var import_node_path34 = __toESM(require("path"), 1);
20603
+ var import_node_path40 = __toESM(require("path"), 1);
20131
20604
  var TYPES = {
20132
20605
  ".css": "text/css; charset=utf-8",
20133
20606
  ".html": "text/html; charset=utf-8",
@@ -20158,9 +20631,9 @@ function resolveStaticPath(root, requestUrl) {
20158
20631
  return null;
20159
20632
  }
20160
20633
  if (!pathname.startsWith("/") || pathname.includes("\0")) return null;
20161
- const rootResolved = import_node_path34.default.resolve(root);
20162
- const candidate = import_node_path34.default.resolve(rootResolved, `.${pathname}`);
20163
- if (candidate !== rootResolved && !candidate.startsWith(rootResolved + import_node_path34.default.sep)) {
20634
+ const rootResolved = import_node_path40.default.resolve(root);
20635
+ const candidate = import_node_path40.default.resolve(rootResolved, `.${pathname}`);
20636
+ if (candidate !== rootResolved && !candidate.startsWith(rootResolved + import_node_path40.default.sep)) {
20164
20637
  return null;
20165
20638
  }
20166
20639
  return candidate;
@@ -20174,7 +20647,7 @@ async function fileSize(file) {
20174
20647
  }
20175
20648
  }
20176
20649
  function sendFile(req, res, file, size) {
20177
- const ext = import_node_path34.default.extname(file).toLowerCase();
20650
+ const ext = import_node_path40.default.extname(file).toLowerCase();
20178
20651
  res.writeHead(200, {
20179
20652
  "Content-Type": TYPES[ext] ?? "application/octet-stream",
20180
20653
  "Content-Length": size,
@@ -20184,7 +20657,7 @@ function sendFile(req, res, file, size) {
20184
20657
  res.end();
20185
20658
  return true;
20186
20659
  }
20187
- (0, import_node_fs38.createReadStream)(file).pipe(res);
20660
+ (0, import_node_fs43.createReadStream)(file).pipe(res);
20188
20661
  return true;
20189
20662
  }
20190
20663
  async function tryServeStatic(req, res, root) {
@@ -20193,7 +20666,7 @@ async function tryServeStatic(req, res, root) {
20193
20666
  if (!candidate) return false;
20194
20667
  const direct = await fileSize(candidate);
20195
20668
  if (direct != null) return sendFile(req, res, candidate, direct);
20196
- const asIndex = import_node_path34.default.join(candidate, "index.html");
20669
+ const asIndex = import_node_path40.default.join(candidate, "index.html");
20197
20670
  const indexSize = await fileSize(asIndex);
20198
20671
  if (indexSize != null) return sendFile(req, res, asIndex, indexSize);
20199
20672
  return false;
@@ -20526,6 +20999,7 @@ async function startSlackRelayServer(opts) {
20526
20999
  cleanupOrphanWorktrees,
20527
21000
  cloneRepoIntoSideboard,
20528
21001
  codexAdapter,
21002
+ codexSandboxWritableRootsArgs,
20529
21003
  codexUnattendedGitConfigArgs,
20530
21004
  coerceOrchestratorAgent,
20531
21005
  collectTakenTeamSlugs,
@@ -20642,6 +21116,7 @@ async function startSlackRelayServer(opts) {
20642
21116
  getPr,
20643
21117
  getPrChecks,
20644
21118
  getPrDetails,
21119
+ getPrForHeadBranch,
20645
21120
  getPrMeta,
20646
21121
  getPrStack,
20647
21122
  getRepoSetupInfo,
@@ -20678,6 +21153,7 @@ async function startSlackRelayServer(opts) {
20678
21153
  isCloudCoordinatorThread,
20679
21154
  isConductorBundledCli,
20680
21155
  isCursorAutoModel,
21156
+ isDefaultishSourceRef,
20681
21157
  isDirty,
20682
21158
  isGhRateLimitError,
20683
21159
  isGlobalRepoPath,
@@ -20744,8 +21220,10 @@ async function startSlackRelayServer(opts) {
20744
21220
  maybeCompactContext,
20745
21221
  mcpAllowTools,
20746
21222
  mcpAuthWarnings,
21223
+ mergeAgentGitAuthEnv,
20747
21224
  mergePr,
20748
21225
  mergePrStack,
21226
+ mergeSideboardIntoMcpServersJson,
20749
21227
  mergeUsage,
20750
21228
  nextPastedTextName,
20751
21229
  nextThinkingEffort,
@@ -20796,6 +21274,7 @@ async function startSlackRelayServer(opts) {
20796
21274
  recordSlackOutboundWatch,
20797
21275
  refreshGitHubAuth,
20798
21276
  refreshSlackReplyBadges,
21277
+ registerPackagedUserMcpClients,
20799
21278
  releaseCaffeinateHoldForThread,
20800
21279
  removeWorkspace,
20801
21280
  removeWorktree,
@@ -20804,9 +21283,11 @@ async function startSlackRelayServer(opts) {
20804
21283
  requestReview,
20805
21284
  requireAgent,
20806
21285
  resetGhStackDetectCache,
21286
+ resetGithubAgentTokenMemo,
20807
21287
  resolveAgentExecutable,
20808
21288
  resolveAgentGitAuthEnv,
20809
21289
  resolveClaudeExecutable,
21290
+ resolveCodexGitWritableRoots,
20810
21291
  resolveCommandBinarySync,
20811
21292
  resolveConductorCursorAgentId,
20812
21293
  resolveCursorModelId,
@@ -20823,6 +21304,7 @@ async function startSlackRelayServer(opts) {
20823
21304
  resolveNewThreadOptions,
20824
21305
  resolvePlanMarkdown,
20825
21306
  resolvePrSelector,
21307
+ resolvePrSelectors,
20826
21308
  resolveQuotaFallbackAgent,
20827
21309
  resolveRepoRoot,
20828
21310
  resolveReviewGuidelines,
@@ -20845,6 +21327,7 @@ async function startSlackRelayServer(opts) {
20845
21327
  sanitizeMcpServerName,
20846
21328
  saveAppSettings,
20847
21329
  saveLinearOAuth,
21330
+ scrubGithubTokensFromChildEnv,
20848
21331
  secureFileUnlocksWith,
20849
21332
  setCaffeinateHold,
20850
21333
  setHttpFetchImpl,
@@ -20915,7 +21398,10 @@ async function startSlackRelayServer(opts) {
20915
21398
  updateLinearIssue,
20916
21399
  updateOpencodeSettings,
20917
21400
  updateThread,
21401
+ userClaudeMcpConfigPath,
21402
+ userCursorMcpConfigPath,
20918
21403
  validateLinearApiKey,
21404
+ warmGithubAgentAuth,
20919
21405
  withAgentInstructions,
20920
21406
  withExportedPath,
20921
21407
  withThreadLock,