@sideboard-ai/core 0.1.88 → 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 (31) hide show
  1. package/dist/agents/cursor-runner.cjs +176 -15
  2. package/dist/agents/cursor-runner.js +11 -2
  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-MMI4RJ5I.js +46 -0
  12. package/dist/{chunk-UHTNJKZX.js → chunk-NR6APJLD.js} +2 -2
  13. package/dist/{chunk-6EZRSCIT.js → chunk-QKYO6BHB.js} +2 -2
  14. package/dist/{chunk-ZXYWWSHZ.js → chunk-R7BQBSDT.js} +254 -61
  15. package/dist/{chunk-OB6IRIFV.js → chunk-XH2GS2LO.js} +2 -2
  16. package/dist/{chunk-VZ2L4AEJ.js → chunk-XUWDLRAE.js} +2 -2
  17. package/dist/{coordinator-prompt-UK5LYFN5.js → coordinator-prompt-AKEY4WSO.js} +2 -2
  18. package/dist/{coordinator-prompt-CI5SHONJ.js → coordinator-prompt-OQOOD5ET.js} +2 -2
  19. package/dist/{global-workspace-2YZ2V4I5.js → global-workspace-3GNPQCLE.js} +3 -3
  20. package/dist/{global-workspace-JQQLPJM5.js → global-workspace-M3OMVDDH.js} +3 -3
  21. package/dist/index.cjs +998 -463
  22. package/dist/index.d.cts +77 -19
  23. package/dist/index.d.ts +77 -19
  24. package/dist/index.js +181 -50
  25. package/dist/mcp/run-stdio.cjs +872 -436
  26. package/dist/mcp/run-stdio.js +132 -48
  27. package/dist/{workspaces-YWCC3WV4.js → workspaces-ERZC7ULY.js} +4 -4
  28. package/dist/{workspaces-FDO5L4NI.js → workspaces-J4WG6UFR.js} +4 -4
  29. package/dist/{worktree-7YNSJ224.js → worktree-DA4BOV7G.js} +7 -1
  30. package/dist/{worktree-4555QBQ7.js → worktree-EO5QAGJU.js} +7 -1
  31. 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,
@@ -10441,6 +10809,51 @@ function normalizeParseResult(parsed) {
10441
10809
  // src/agents/spawn.ts
10442
10810
  init_path();
10443
10811
  init_usage();
10812
+
10813
+ // src/agents/cursor-stream-coalesce.ts
10814
+ function isTextEvent(event) {
10815
+ return event.type === "stdout" || event.type === "thinking";
10816
+ }
10817
+ function createAgentStreamCoalescer(emit, opts) {
10818
+ const intervalMs = opts?.intervalMs ?? 32;
10819
+ let pending = null;
10820
+ let timer = null;
10821
+ const flush = () => {
10822
+ if (timer) {
10823
+ clearTimeout(timer);
10824
+ timer = null;
10825
+ }
10826
+ if (!pending) return;
10827
+ const event = pending;
10828
+ pending = null;
10829
+ emit({ type: event.type, data: event.data });
10830
+ };
10831
+ const schedule = () => {
10832
+ if (timer) return;
10833
+ timer = setTimeout(flush, intervalMs);
10834
+ timer.unref?.();
10835
+ };
10836
+ return {
10837
+ push(event) {
10838
+ if (!isTextEvent(event)) {
10839
+ flush();
10840
+ emit(event);
10841
+ return;
10842
+ }
10843
+ if (!event.data) return;
10844
+ if (pending && pending.type === event.type) {
10845
+ pending.data += event.data;
10846
+ } else {
10847
+ flush();
10848
+ pending = { type: event.type, data: event.data };
10849
+ }
10850
+ schedule();
10851
+ },
10852
+ flush
10853
+ };
10854
+ }
10855
+
10856
+ // src/agents/spawn.ts
10444
10857
  async function spawnAgentTurn(thread, input, onEvent) {
10445
10858
  ensureAgentPath();
10446
10859
  if (!thread.worktreePath?.trim()) {
@@ -10468,10 +10881,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
10468
10881
  const env = childEnvWithAppSettings(cmd.env);
10469
10882
  try {
10470
10883
  if (isOrchestratorThread(thread)) {
10471
- Object.assign(env, await resolveAgentGitAuthEnv(env));
10884
+ mergeAgentGitAuthEnv(env, await resolveAgentGitAuthEnv(env));
10472
10885
  } else {
10473
- const originEnv = await originGhRepoEnv(thread.worktreePath, { env });
10474
- Object.assign(env, originEnv);
10886
+ mergeAgentGitAuthEnv(env, await originGhRepoEnv(thread.worktreePath, { env }));
10475
10887
  }
10476
10888
  } catch (err) {
10477
10889
  const detail = err instanceof Error ? err.message : String(err);
@@ -10495,12 +10907,13 @@ async function spawnAgentTurn(thread, input, onEvent) {
10495
10907
  let assistantText = "";
10496
10908
  let parts = [];
10497
10909
  let usage = null;
10910
+ const outbound = createAgentStreamCoalescer(onEvent);
10498
10911
  const consume = (stream, kind) => {
10499
10912
  if (!stream) return;
10500
10913
  const rl = (0, import_node_readline2.createInterface)({ input: stream, crlfDelay: Infinity });
10501
10914
  rl.on("line", (line) => {
10502
10915
  if (kind === "stderr") {
10503
- onEvent({ type: "stderr", data: line });
10916
+ outbound.push({ type: "stderr", data: line });
10504
10917
  return;
10505
10918
  }
10506
10919
  let events = normalizeParseResult(adapter.parseEvent(line));
@@ -10515,12 +10928,12 @@ async function spawnAgentTurn(thread, input, onEvent) {
10515
10928
  for (const parsed of events) {
10516
10929
  if (parsed.type === "session_id") {
10517
10930
  sessionId = parsed.data;
10518
- onEvent(parsed);
10931
+ outbound.push(parsed);
10519
10932
  continue;
10520
10933
  }
10521
10934
  if (parsed.type === "usage") {
10522
10935
  usage = applyTurnUsage(usage, parsed.data, parsed.scope ?? "request");
10523
- onEvent(parsed);
10936
+ outbound.push(parsed);
10524
10937
  continue;
10525
10938
  }
10526
10939
  if (parsed.type === "stdout") {
@@ -10533,13 +10946,14 @@ async function spawnAgentTurn(thread, input, onEvent) {
10533
10946
  assistantText += parsed.data;
10534
10947
  }
10535
10948
  parts = applyAgentEvent(parts, parsed);
10536
- onEvent(parsed);
10949
+ outbound.push(parsed);
10537
10950
  }
10538
10951
  });
10539
10952
  };
10540
10953
  consume(child.stdout, "stdout");
10541
10954
  consume(child.stderr, "stderr");
10542
10955
  const done = child.then((result) => {
10956
+ outbound.flush();
10543
10957
  const exitCode = result.exitCode ?? null;
10544
10958
  onEvent({ type: "exit", data: exitCode });
10545
10959
  const finalized = finalizeParts(parts);
@@ -10564,8 +10978,8 @@ init_usage();
10564
10978
  init_claude_mcp();
10565
10979
 
10566
10980
  // src/agents/instructions.ts
10567
- var import_node_fs22 = require("fs");
10568
- var import_node_path19 = require("path");
10981
+ var import_node_fs26 = require("fs");
10982
+ var import_node_path24 = require("path");
10569
10983
  init_git_auth_mode();
10570
10984
  init_worktree_labels();
10571
10985
  function normPath3(p) {
@@ -10698,9 +11112,10 @@ function formatArtifactDirective() {
10698
11112
  "Files / media browser (CMS file manager column):",
10699
11113
  "4) Call Sideboard MCP `present_files` with optional title, path, and datasource (brightsy|memory).",
10700
11114
  " Opens the Files column for browse/upload/pick. Do NOT say a file manager UI is missing.",
10701
- "Multiple-choice questions (any mode \u2014 not only Plan):",
10702
- "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.",
10703
- "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."
10704
11119
  ].join("\n");
10705
11120
  }
10706
11121
  function formatUiReminder() {
@@ -10711,7 +11126,7 @@ function formatUiReminder() {
10711
11126
  "CMS forms/tables: MCP present_schema (Brightsy resource_id or inline schema+schemaUi).",
10712
11127
  "Files column: MCP present_files (brightsy account storage or memory).",
10713
11128
  "Do not say artifacts/CMS UI are unavailable.",
10714
- "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."
10715
11130
  ].join(" ");
10716
11131
  }
10717
11132
  var FILES_BY_AGENT = {
@@ -10735,11 +11150,11 @@ function loadAgentInstructions(worktreePath, agent) {
10735
11150
  const out = [];
10736
11151
  for (const rel of candidates) {
10737
11152
  if (seenPaths.has(rel)) continue;
10738
- const abs = (0, import_node_path19.join)(worktreePath, rel);
10739
- 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;
10740
11155
  try {
10741
- if (!(0, import_node_fs22.statSync)(abs).isFile()) continue;
10742
- 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");
10743
11158
  if (!content.trim()) continue;
10744
11159
  if (content.length > MAX_CHARS_PER_FILE) {
10745
11160
  content = `${content.slice(0, MAX_CHARS_PER_FILE)}
@@ -10821,9 +11236,9 @@ async function requireAgent(agent, opts) {
10821
11236
  init_settings();
10822
11237
 
10823
11238
  // src/hook/conductor.ts
10824
- var import_node_fs23 = require("fs");
11239
+ var import_node_fs27 = require("fs");
10825
11240
  var import_node_net = require("net");
10826
- var import_node_path20 = require("path");
11241
+ var import_node_path25 = require("path");
10827
11242
  var import_execa4 = require("execa");
10828
11243
  var import_node_readline3 = require("readline");
10829
11244
  init_settings();
@@ -10838,9 +11253,9 @@ function matchSimpleGlob(pattern, name) {
10838
11253
  return new RegExp(`^${escaped}$`).test(name);
10839
11254
  }
10840
11255
  function readWorktreeInclude(repoPath) {
10841
- const path2 = (0, import_node_path20.join)(repoPath, ".worktreeinclude");
10842
- if (!(0, import_node_fs23.existsSync)(path2)) return [];
10843
- 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("#"));
10844
11259
  }
10845
11260
  function resolveFilesToCopy(repoPath) {
10846
11261
  const fromInclude = readWorktreeInclude(repoPath);
@@ -10850,10 +11265,10 @@ function resolveFilesToCopy(repoPath) {
10850
11265
  if (settings?.fileIncludeGlobs?.length) {
10851
11266
  const matched = [];
10852
11267
  try {
10853
- 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 })) {
10854
11269
  if (!entry.isFile()) continue;
10855
11270
  for (const glob of settings.fileIncludeGlobs) {
10856
- 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)) {
10857
11272
  matched.push(entry.name);
10858
11273
  break;
10859
11274
  }
@@ -10865,7 +11280,7 @@ function resolveFilesToCopy(repoPath) {
10865
11280
  }
10866
11281
  const defaults = [];
10867
11282
  try {
10868
- 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 })) {
10869
11284
  if (entry.isFile() && entry.name.startsWith(".env")) {
10870
11285
  defaults.push(entry.name);
10871
11286
  }
@@ -10879,11 +11294,11 @@ function copyConfiguredFiles(repoPath, worktreePath) {
10879
11294
  const patterns = resolveFilesToCopy(repoPath);
10880
11295
  const copied = [];
10881
11296
  for (const rel of patterns) {
10882
- const src = (0, import_node_path20.join)(repoPath, rel);
10883
- if (!(0, import_node_fs23.existsSync)(src)) continue;
10884
- const dest = (0, import_node_path20.join)(worktreePath, rel);
10885
- (0, import_node_fs23.mkdirSync)((0, import_node_path20.dirname)(dest), { recursive: true });
10886
- (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);
10887
11302
  copied.push(rel);
10888
11303
  }
10889
11304
  return copied;
@@ -10919,7 +11334,7 @@ function buildWorkspaceScriptEnv(opts, baseEnv) {
10919
11334
  const env = stripNestedElectronEnv({
10920
11335
  ...baseEnv ?? process.env
10921
11336
  });
10922
- const name = opts.workspaceName ?? (0, import_node_path20.basename)(opts.worktreePath);
11337
+ const name = opts.workspaceName ?? (0, import_node_path25.basename)(opts.worktreePath);
10923
11338
  const ports = opts.ports ?? [];
10924
11339
  const primary = ports[0];
10925
11340
  env.SIDEBOARD_WORKSPACE_NAME = name;
@@ -11005,7 +11420,10 @@ async function spawnWorkspaceScript(command, opts) {
11005
11420
  loginEnv
11006
11421
  );
11007
11422
  try {
11008
- Object.assign(env, await resolveAgentGitAuthEnv(env, { cwd: opts.worktreePath }));
11423
+ mergeAgentGitAuthEnv(
11424
+ env,
11425
+ await resolveAgentGitAuthEnv(env, { cwd: opts.worktreePath })
11426
+ );
11009
11427
  } catch {
11010
11428
  }
11011
11429
  const shell = process.platform === "darwin" ? "zsh" : "bash";
@@ -11177,12 +11595,12 @@ init_convention_setup();
11177
11595
  init_cursor_worktrees();
11178
11596
 
11179
11597
  // src/diff/diff.ts
11180
- var import_node_fs24 = require("fs");
11181
- var import_node_path21 = require("path");
11598
+ var import_node_fs28 = require("fs");
11599
+ var import_node_path26 = require("path");
11182
11600
  init_run();
11183
11601
  init_worktree();
11184
11602
  async function inspectGitWorktree(worktreePath) {
11185
- if (!worktreePath || !(0, import_node_fs24.existsSync)(worktreePath)) return "missing_worktree";
11603
+ if (!worktreePath || !(0, import_node_fs28.existsSync)(worktreePath)) return "missing_worktree";
11186
11604
  const check = await git(["rev-parse", "--is-inside-work-tree"], worktreePath, {
11187
11605
  reject: false
11188
11606
  });
@@ -11190,7 +11608,7 @@ async function inspectGitWorktree(worktreePath) {
11190
11608
  return "ok";
11191
11609
  }
11192
11610
  async function initializeGitRepository(worktreePath) {
11193
- if (!worktreePath || !(0, import_node_fs24.existsSync)(worktreePath)) {
11611
+ if (!worktreePath || !(0, import_node_fs28.existsSync)(worktreePath)) {
11194
11612
  throw new Error("Worktree not found");
11195
11613
  }
11196
11614
  const status = await inspectGitWorktree(worktreePath);
@@ -11325,11 +11743,11 @@ new file mode 100644
11325
11743
  };
11326
11744
  }
11327
11745
  async function untrackedPatch(worktreePath, path2, maxHunk) {
11328
- const abs = (0, import_node_path21.join)(worktreePath, path2);
11746
+ const abs = (0, import_node_path26.join)(worktreePath, path2);
11329
11747
  try {
11330
- const st = (0, import_node_fs24.statSync)(abs);
11748
+ const st = (0, import_node_fs28.statSync)(abs);
11331
11749
  if (st.isFile() && st.size > maxHunk) {
11332
- const buf = (0, import_node_fs24.readFileSync)(abs).subarray(0, maxHunk);
11750
+ const buf = (0, import_node_fs28.readFileSync)(abs).subarray(0, maxHunk);
11333
11751
  return syntheticAddPatch(path2, buf.toString("utf8"), maxHunk);
11334
11752
  }
11335
11753
  } catch {
@@ -11830,8 +12248,8 @@ var DEFAULT_UPLOAD_MAX_BYTES = 5e7;
11830
12248
  function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
11831
12249
  assertSafeRelativePath(relativePath);
11832
12250
  const maxBytes = opts?.maxBytes ?? DEFAULT_UPLOAD_MAX_BYTES;
11833
- const abs = (0, import_node_path21.join)(worktreePath, relativePath);
11834
- 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);
11835
12253
  if (!st.isFile()) {
11836
12254
  throw new Error(`Not a file: ${relativePath}`);
11837
12255
  }
@@ -11840,7 +12258,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
11840
12258
  `File too large to upload (${st.size} bytes; max ${maxBytes})`
11841
12259
  );
11842
12260
  }
11843
- const buf = (0, import_node_fs24.readFileSync)(abs);
12261
+ const buf = (0, import_node_fs28.readFileSync)(abs);
11844
12262
  return {
11845
12263
  path: relativePath,
11846
12264
  contentBase64: buf.toString("base64"),
@@ -11850,12 +12268,12 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
11850
12268
  function readWorktreeFile(worktreePath, relativePath, opts) {
11851
12269
  assertSafeRelativePath(relativePath);
11852
12270
  const maxBytes = opts?.maxBytes ?? 2e5;
11853
- const abs = (0, import_node_path21.join)(worktreePath, relativePath);
11854
- 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);
11855
12273
  if (!st.isFile()) {
11856
12274
  throw new Error(`Not a file: ${relativePath}`);
11857
12275
  }
11858
- const buf = (0, import_node_fs24.readFileSync)(abs);
12276
+ const buf = (0, import_node_fs28.readFileSync)(abs);
11859
12277
  if (isImageRelativePath(relativePath)) {
11860
12278
  const maxImageBytes = Math.max(maxBytes, 15e6);
11861
12279
  const truncated2 = buf.length > maxImageBytes;
@@ -11898,9 +12316,9 @@ function assertSafeRelativePath(relativePath) {
11898
12316
  }
11899
12317
  function writeWorktreeFile(worktreePath, relativePath, content) {
11900
12318
  assertSafeRelativePath(relativePath);
11901
- const abs = (0, import_node_path21.join)(worktreePath, relativePath);
11902
- (0, import_node_fs24.mkdirSync)((0, import_node_path21.dirname)(abs), { recursive: true });
11903
- (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");
11904
12322
  return { path: relativePath };
11905
12323
  }
11906
12324
  async function getDiffSummary(worktreePath, repoPath, opts) {
@@ -11919,9 +12337,9 @@ async function getDiffSummary(worktreePath, repoPath, opts) {
11919
12337
  }
11920
12338
 
11921
12339
  // src/skills/discover.ts
11922
- var import_node_fs25 = require("fs");
11923
- var import_node_os8 = require("os");
11924
- 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");
11925
12343
  function toCommand(name) {
11926
12344
  return name.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
11927
12345
  }
@@ -11953,7 +12371,7 @@ function parseFrontmatter(content) {
11953
12371
  }
11954
12372
  function readSkill(skillMd, source) {
11955
12373
  try {
11956
- const content = (0, import_node_fs25.readFileSync)(skillMd, "utf8");
12374
+ const content = (0, import_node_fs29.readFileSync)(skillMd, "utf8");
11957
12375
  const { name: fmName, description } = parseFrontmatter(content);
11958
12376
  const dirName = skillMd.split("/").slice(-2, -1)[0] || "skill";
11959
12377
  const name = fmName || dirName;
@@ -11972,19 +12390,19 @@ function readSkill(skillMd, source) {
11972
12390
  }
11973
12391
  }
11974
12392
  function scanSkillsDir(dir, source, out) {
11975
- if (!(0, import_node_fs25.existsSync)(dir)) return;
12393
+ if (!(0, import_node_fs29.existsSync)(dir)) return;
11976
12394
  let entries;
11977
12395
  try {
11978
- entries = (0, import_node_fs25.readdirSync)(dir);
12396
+ entries = (0, import_node_fs29.readdirSync)(dir);
11979
12397
  } catch {
11980
12398
  return;
11981
12399
  }
11982
12400
  for (const entry of entries) {
11983
12401
  if (entry.startsWith(".")) continue;
11984
- const skillMd = (0, import_node_path22.join)(dir, entry, "SKILL.md");
11985
- 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;
11986
12404
  try {
11987
- if (!(0, import_node_fs25.statSync)(skillMd).isFile()) continue;
12405
+ if (!(0, import_node_fs29.statSync)(skillMd).isFile()) continue;
11988
12406
  } catch {
11989
12407
  continue;
11990
12408
  }
@@ -11993,24 +12411,24 @@ function scanSkillsDir(dir, source, out) {
11993
12411
  }
11994
12412
  }
11995
12413
  function scanClaudePluginSkills(pluginsRoot, out) {
11996
- if (!(0, import_node_fs25.existsSync)(pluginsRoot)) return;
12414
+ if (!(0, import_node_fs29.existsSync)(pluginsRoot)) return;
11997
12415
  const walk = (dir, depth, lookingForSkillsDir) => {
11998
12416
  if (depth > 7) return;
11999
12417
  let entries;
12000
12418
  try {
12001
- entries = (0, import_node_fs25.readdirSync)(dir);
12419
+ entries = (0, import_node_fs29.readdirSync)(dir);
12002
12420
  } catch {
12003
12421
  return;
12004
12422
  }
12005
12423
  if (lookingForSkillsDir && entries.includes("SKILL.md")) {
12006
- 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");
12007
12425
  if (skill) out.push(skill);
12008
12426
  }
12009
12427
  for (const entry of entries) {
12010
12428
  if (entry === "node_modules" || entry === ".git") continue;
12011
- const full = (0, import_node_path22.join)(dir, entry);
12429
+ const full = (0, import_node_path27.join)(dir, entry);
12012
12430
  try {
12013
- if (!(0, import_node_fs25.statSync)(full).isDirectory()) continue;
12431
+ if (!(0, import_node_fs29.statSync)(full).isDirectory()) continue;
12014
12432
  } catch {
12015
12433
  continue;
12016
12434
  }
@@ -12025,20 +12443,20 @@ function scanClaudePluginSkills(pluginsRoot, out) {
12025
12443
  walk(pluginsRoot, 0, false);
12026
12444
  }
12027
12445
  function discoverSkills(worktreePath) {
12028
- const home = (0, import_node_os8.homedir)();
12446
+ const home = (0, import_node_os10.homedir)();
12029
12447
  const collected = [];
12030
12448
  for (const rel of [".claude/skills", ".cursor/skills", ".sideboard/skills", ".brightsy/skills", "skills"]) {
12031
- scanSkillsDir((0, import_node_path22.join)(worktreePath, rel), "workspace", collected);
12449
+ scanSkillsDir((0, import_node_path27.join)(worktreePath, rel), "workspace", collected);
12032
12450
  }
12033
12451
  for (const abs of [
12034
- (0, import_node_path22.join)(home, ".claude/skills"),
12035
- (0, import_node_path22.join)(home, ".cursor/skills"),
12036
- (0, import_node_path22.join)(home, ".sideboard/skills"),
12037
- (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")
12038
12456
  ]) {
12039
12457
  scanSkillsDir(abs, "user", collected);
12040
12458
  }
12041
- scanClaudePluginSkills((0, import_node_path22.join)(home, ".claude/plugins"), collected);
12459
+ scanClaudePluginSkills((0, import_node_path27.join)(home, ".claude/plugins"), collected);
12042
12460
  const rank = { workspace: 0, user: 1, cli: 2 };
12043
12461
  const byCommand = /* @__PURE__ */ new Map();
12044
12462
  for (const skill of collected) {
@@ -12050,7 +12468,7 @@ function discoverSkills(worktreePath) {
12050
12468
  return [...byCommand.values()].sort((a, b) => a.command.localeCompare(b.command));
12051
12469
  }
12052
12470
  function readSkillBody(skillPath, maxChars = 12e3) {
12053
- const raw = (0, import_node_fs25.readFileSync)(skillPath, "utf8");
12471
+ const raw = (0, import_node_fs29.readFileSync)(skillPath, "utf8");
12054
12472
  if (raw.startsWith("---")) {
12055
12473
  const end = raw.indexOf("\n---", 3);
12056
12474
  if (end >= 0) {
@@ -12191,8 +12609,8 @@ function buildDiffCommentAttachment(input) {
12191
12609
  }
12192
12610
 
12193
12611
  // src/composer/stage-files.ts
12194
- var import_node_fs26 = require("fs");
12195
- var import_node_path23 = require("path");
12612
+ var import_node_fs30 = require("fs");
12613
+ var import_node_path28 = require("path");
12196
12614
  var import_node_crypto5 = require("crypto");
12197
12615
  init_workspace_scratch();
12198
12616
  var IMAGE_EXTENSIONS2 = /* @__PURE__ */ new Set([
@@ -12218,7 +12636,7 @@ var IMAGE_MIME_BY_EXT = {
12218
12636
  var MAX_INLINE_BYTES = 4e5;
12219
12637
  var MAX_PREVIEW_BYTES = 5e6;
12220
12638
  function fileExtension(filePath) {
12221
- const base = (0, import_node_path23.basename)(filePath).toLowerCase();
12639
+ const base = (0, import_node_path28.basename)(filePath).toLowerCase();
12222
12640
  return base.includes(".") ? base.split(".").pop() || "" : "";
12223
12641
  }
12224
12642
  function isImageFilePath(filePath) {
@@ -12228,22 +12646,22 @@ function imageMimeType(filePath) {
12228
12646
  return IMAGE_MIME_BY_EXT[fileExtension(filePath)] || "image/png";
12229
12647
  }
12230
12648
  function ensureAttachmentsDir(worktreePath) {
12231
- const dir = (0, import_node_path23.join)(worktreePath, ATTACHMENTS_DIR);
12232
- (0, import_node_fs26.mkdirSync)(dir, { recursive: true });
12233
- const gi = (0, import_node_path23.join)(dir, ".gitignore");
12234
- if (!(0, import_node_fs26.existsSync)(gi)) {
12235
- (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");
12236
12654
  }
12237
12655
  return dir;
12238
12656
  }
12239
12657
  function uniqueAttachmentName(dir, originalName) {
12240
12658
  const safe = originalName.replace(/[/\\]/g, "_") || "file";
12241
- if (!(0, import_node_fs26.existsSync)((0, import_node_path23.join)(dir, safe))) return safe;
12242
- 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);
12243
12661
  const stem = ext ? safe.slice(0, -ext.length) : safe;
12244
12662
  for (let i = 1; i < 1e4; i++) {
12245
12663
  const candidate = `${stem}-${i}${ext}`;
12246
- 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;
12247
12665
  }
12248
12666
  return `${stem}-${(0, import_node_crypto5.randomUUID)()}${ext}`;
12249
12667
  }
@@ -12295,9 +12713,9 @@ function attachmentFromBuffer(name, buf, opts) {
12295
12713
  };
12296
12714
  }
12297
12715
  function attachmentFromAbsolutePath(absolutePath) {
12298
- const name = (0, import_node_path23.basename)(absolutePath);
12716
+ const name = (0, import_node_path28.basename)(absolutePath);
12299
12717
  try {
12300
- const st = (0, import_node_fs26.statSync)(absolutePath);
12718
+ const st = (0, import_node_fs30.statSync)(absolutePath);
12301
12719
  if (!st.isFile()) {
12302
12720
  return {
12303
12721
  id: (0, import_node_crypto5.randomUUID)(),
@@ -12306,7 +12724,7 @@ function attachmentFromAbsolutePath(absolutePath) {
12306
12724
  content: `(not a file: ${absolutePath})`
12307
12725
  };
12308
12726
  }
12309
- const buf = (0, import_node_fs26.readFileSync)(absolutePath);
12727
+ const buf = (0, import_node_fs30.readFileSync)(absolutePath);
12310
12728
  return attachmentFromBuffer(name, buf, { sourceLabel: absolutePath });
12311
12729
  } catch (err) {
12312
12730
  return {
@@ -12322,15 +12740,15 @@ function stageAbsolutePathsAsAttachments(worktreePath, absolutePaths) {
12322
12740
  const dir = ensureAttachmentsDir(worktreePath);
12323
12741
  const out = [];
12324
12742
  for (const abs of absolutePaths) {
12325
- const originalName = (0, import_node_path23.basename)(abs);
12743
+ const originalName = (0, import_node_path28.basename)(abs);
12326
12744
  try {
12327
- const st = (0, import_node_fs26.statSync)(abs);
12745
+ const st = (0, import_node_fs30.statSync)(abs);
12328
12746
  if (!st.isFile()) continue;
12329
12747
  const name = uniqueAttachmentName(dir, originalName);
12330
- const destAbs = (0, import_node_path23.join)(dir, name);
12331
- (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);
12332
12750
  const rel = `${ATTACHMENTS_DIR}/${name}`;
12333
- const buf = (0, import_node_fs26.readFileSync)(destAbs);
12751
+ const buf = (0, import_node_fs30.readFileSync)(destAbs);
12334
12752
  out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
12335
12753
  } catch (err) {
12336
12754
  out.push({
@@ -12352,8 +12770,8 @@ function stageBuffersAsAttachments(worktreePath, buffers) {
12352
12770
  try {
12353
12771
  const buf = Buffer.from(item.dataBase64, "base64");
12354
12772
  const name = uniqueAttachmentName(dir, originalName);
12355
- const destAbs = (0, import_node_path23.join)(dir, name);
12356
- (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);
12357
12775
  const rel = `${ATTACHMENTS_DIR}/${name}`;
12358
12776
  out.push(attachmentFromBuffer(name, buf, { path: rel }));
12359
12777
  } catch (err) {
@@ -12389,18 +12807,18 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
12389
12807
  if (!rel || rel.includes("..") || rel.startsWith("/")) {
12390
12808
  out.push({
12391
12809
  id: (0, import_node_crypto5.randomUUID)(),
12392
- name: (0, import_node_path23.basename)(rel) || "file",
12810
+ name: (0, import_node_path28.basename)(rel) || "file",
12393
12811
  kind: "file",
12394
12812
  content: `(invalid path: ${rel})`
12395
12813
  });
12396
12814
  continue;
12397
12815
  }
12398
- const name = (0, import_node_path23.basename)(rel);
12816
+ const name = (0, import_node_path28.basename)(rel);
12399
12817
  try {
12400
- const abs = (0, import_node_path23.join)(worktreePath, rel);
12401
- 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);
12402
12820
  if (!st.isFile()) continue;
12403
- const buf = (0, import_node_fs26.readFileSync)(abs);
12821
+ const buf = (0, import_node_fs30.readFileSync)(abs);
12404
12822
  out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
12405
12823
  } catch (err) {
12406
12824
  out.push({
@@ -12888,7 +13306,7 @@ async function confirmLand(thread, opts) {
12888
13306
  }
12889
13307
 
12890
13308
  // src/threads/create.ts
12891
- var import_node_fs27 = require("fs");
13309
+ var import_node_fs31 = require("fs");
12892
13310
  init_worktree();
12893
13311
  init_app_settings();
12894
13312
  init_thread_store();
@@ -12902,12 +13320,13 @@ async function createThread(input, _onSetupLine) {
12902
13320
  });
12903
13321
  await requireAgent(resolved.agent);
12904
13322
  const repoPath = await resolveRepoRoot(input.repoPath);
12905
- if (!(0, import_node_fs27.existsSync)(repoPath)) {
13323
+ if (!(0, import_node_fs31.existsSync)(repoPath)) {
12906
13324
  throw new Error(`Repo not found: ${repoPath}`);
12907
13325
  }
12908
13326
  let sourceRef = input.sourceRef;
12909
13327
  let sourceIsFork = false;
12910
13328
  let prUrl = null;
13329
+ let prTitle = null;
12911
13330
  if (input.sourceType === "pr") {
12912
13331
  const num2 = Number(input.sourceRef.replace(/^#/, ""));
12913
13332
  if (!Number.isFinite(num2)) throw new Error(`Invalid PR number: ${input.sourceRef}`);
@@ -12923,6 +13342,12 @@ async function createThread(input, _onSetupLine) {
12923
13342
  } else if (input.sourceType === "branch") {
12924
13343
  if (!sourceRef || sourceRef === "HEAD" || sourceRef === "default") {
12925
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
+ }
12926
13351
  }
12927
13352
  } else if (input.sourceType === "adopt") {
12928
13353
  throw new Error("Use adoptThread() for adopt sources");
@@ -12953,7 +13378,8 @@ async function createThread(input, _onSetupLine) {
12953
13378
  sourceIsFork,
12954
13379
  parentThreadId: input.parentThreadId ?? null,
12955
13380
  status: "idle",
12956
- prUrl
13381
+ prUrl,
13382
+ prTitle
12957
13383
  });
12958
13384
  writeThread(thread);
12959
13385
  await ensureWorkspace(repoPath);
@@ -13129,7 +13555,7 @@ async function forkThreadWorktree(input, onSetupLine) {
13129
13555
  }
13130
13556
 
13131
13557
  // src/threads/stack-layers.ts
13132
- var import_node_fs28 = require("fs");
13558
+ var import_node_fs32 = require("fs");
13133
13559
  init_run();
13134
13560
  init_stack();
13135
13561
  init_worktree();
@@ -13197,7 +13623,7 @@ async function openStackLayer(input, _onSetupLine) {
13197
13623
  let createdWorktree = false;
13198
13624
  const trees = await listWorktrees(repoPath);
13199
13625
  const checkedOut = trees.find((w) => w.branch === branchName);
13200
- if (checkedOut?.path && (0, import_node_fs28.existsSync)(checkedOut.path)) {
13626
+ if (checkedOut?.path && (0, import_node_fs32.existsSync)(checkedOut.path)) {
13201
13627
  if (input.reuseExistingWorktree !== false) {
13202
13628
  worktreePath = checkedOut.path;
13203
13629
  } else {
@@ -13339,7 +13765,7 @@ async function initStackFromThread(input, onSetupLine) {
13339
13765
  async function createPrStack(input, onSetupLine) {
13340
13766
  await requireAgent(input.agent);
13341
13767
  const repoPath = await resolveRepoRoot(input.repoPath);
13342
- 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}`);
13343
13769
  if (!input.branches.length) throw new Error("At least one branch name required");
13344
13770
  const status = await detectGhStack(repoPath);
13345
13771
  if (!status.available) throw new Error(status.reason);
@@ -13406,7 +13832,7 @@ async function createPrStack(input, onSetupLine) {
13406
13832
  }
13407
13833
  }
13408
13834
  const claimed = new Set(threads.map((t) => t.worktreePath));
13409
- 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)) {
13410
13836
  try {
13411
13837
  await removeWorktree(repoPath, bootstrap.worktreePath, {
13412
13838
  deleteBranch: bootstrap.branchName
@@ -13429,20 +13855,26 @@ function stackAgentDefaultsFrom(input) {
13429
13855
 
13430
13856
  // src/threads/adopt.ts
13431
13857
  var import_node_child_process4 = require("child_process");
13432
- var import_node_fs29 = require("fs");
13433
- var import_node_os9 = require("os");
13434
- var import_node_path24 = require("path");
13435
- 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");
13436
13862
  init_worktree();
13437
13863
  init_thread_store();
13438
- var CONDUCTOR_APP_SUPPORT = (0, import_node_path24.join)(
13864
+ var import_meta4 = {};
13865
+ var CONDUCTOR_APP_SUPPORT = (0, import_node_path29.join)(
13439
13866
  process.env.HOME ?? "",
13440
13867
  "Library",
13441
13868
  "Application Support",
13442
13869
  "com.conductor.app"
13443
13870
  );
13444
- var CONDUCTOR_DB = (0, import_node_path24.join)(CONDUCTOR_APP_SUPPORT, "conductor.db");
13445
- 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
+ }
13446
13878
  function mapAgentType(raw) {
13447
13879
  if (!raw) return null;
13448
13880
  const v = raw.toLowerCase();
@@ -13454,21 +13886,21 @@ function mapAgentType(raw) {
13454
13886
  return null;
13455
13887
  }
13456
13888
  function resolveConductorCursorAgentId(workspacePath) {
13457
- 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;
13458
13890
  const normalized = workspacePath.replace(/\/$/, "");
13459
13891
  let best = null;
13460
13892
  let hashes;
13461
13893
  try {
13462
- hashes = (0, import_node_fs29.readdirSync)(CURSOR_SDK_STORE);
13894
+ hashes = (0, import_node_fs33.readdirSync)(CURSOR_SDK_STORE);
13463
13895
  } catch {
13464
13896
  return null;
13465
13897
  }
13466
13898
  for (const hash of hashes) {
13467
- const agentsFile = (0, import_node_path24.join)(CURSOR_SDK_STORE, hash, "agents.ndjson");
13468
- 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;
13469
13901
  let text3;
13470
13902
  try {
13471
- text3 = (0, import_node_fs29.readFileSync)(agentsFile, "utf8");
13903
+ text3 = (0, import_node_fs33.readFileSync)(agentsFile, "utf8");
13472
13904
  } catch {
13473
13905
  continue;
13474
13906
  }
@@ -13492,7 +13924,7 @@ function resolveConductorCursorAgentId(workspacePath) {
13492
13924
  return best?.agentId ?? null;
13493
13925
  }
13494
13926
  async function adoptThread(input) {
13495
- if (!(0, import_node_fs29.existsSync)(input.worktreePath)) {
13927
+ if (!(0, import_node_fs33.existsSync)(input.worktreePath)) {
13496
13928
  throw new Error(`Worktree not found: ${input.worktreePath}`);
13497
13929
  }
13498
13930
  const repoPath = await resolveRepoRoot(input.worktreePath);
@@ -13519,23 +13951,23 @@ function conductorDbPath() {
13519
13951
  return CONDUCTOR_DB;
13520
13952
  }
13521
13953
  function listConductorWorkspaces() {
13522
- if (!(0, import_node_fs29.existsSync)(CONDUCTOR_DB)) {
13954
+ if (!(0, import_node_fs33.existsSync)(CONDUCTOR_DB)) {
13523
13955
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
13524
13956
  }
13525
- const tmp = (0, import_node_fs29.mkdtempSync)((0, import_node_path24.join)((0, import_node_os9.tmpdir)(), "sideboard-conductor-"));
13526
- 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");
13527
13959
  try {
13528
- (0, import_node_fs29.copyFileSync)(CONDUCTOR_DB, snapshot);
13960
+ (0, import_node_fs33.copyFileSync)(CONDUCTOR_DB, snapshot);
13529
13961
  for (const suffix of ["-wal", "-shm"]) {
13530
13962
  const src = `${CONDUCTOR_DB}${suffix}`;
13531
- if ((0, import_node_fs29.existsSync)(src)) {
13963
+ if ((0, import_node_fs33.existsSync)(src)) {
13532
13964
  try {
13533
- (0, import_node_fs29.copyFileSync)(src, `${snapshot}${suffix}`);
13965
+ (0, import_node_fs33.copyFileSync)(src, `${snapshot}${suffix}`);
13534
13966
  } catch {
13535
13967
  }
13536
13968
  }
13537
13969
  }
13538
- const db = new import_better_sqlite3.default(snapshot, { readonly: true, fileMustExist: true });
13970
+ const db = openReadonlySqlite(snapshot);
13539
13971
  try {
13540
13972
  const tables = db.prepare(`SELECT name FROM sqlite_master WHERE type='table'`).all();
13541
13973
  const names = new Set(tables.map((t) => t.name));
@@ -13606,27 +14038,27 @@ function listConductorWorkspaces() {
13606
14038
  db.close();
13607
14039
  }
13608
14040
  } finally {
13609
- (0, import_node_fs29.rmSync)(tmp, { recursive: true, force: true });
14041
+ (0, import_node_fs33.rmSync)(tmp, { recursive: true, force: true });
13610
14042
  }
13611
14043
  }
13612
14044
  function importConductorWorkspace(workspaceId) {
13613
- if (!(0, import_node_fs29.existsSync)(CONDUCTOR_DB)) {
14045
+ if (!(0, import_node_fs33.existsSync)(CONDUCTOR_DB)) {
13614
14046
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
13615
14047
  }
13616
- const tmp = (0, import_node_fs29.mkdtempSync)((0, import_node_path24.join)((0, import_node_os9.tmpdir)(), "sideboard-conductor-"));
13617
- 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");
13618
14050
  try {
13619
- (0, import_node_fs29.copyFileSync)(CONDUCTOR_DB, snapshot);
14051
+ (0, import_node_fs33.copyFileSync)(CONDUCTOR_DB, snapshot);
13620
14052
  for (const suffix of ["-wal", "-shm"]) {
13621
14053
  const src = `${CONDUCTOR_DB}${suffix}`;
13622
- if ((0, import_node_fs29.existsSync)(src)) {
14054
+ if ((0, import_node_fs33.existsSync)(src)) {
13623
14055
  try {
13624
- (0, import_node_fs29.copyFileSync)(src, `${snapshot}${suffix}`);
14056
+ (0, import_node_fs33.copyFileSync)(src, `${snapshot}${suffix}`);
13625
14057
  } catch {
13626
14058
  }
13627
14059
  }
13628
14060
  }
13629
- const db = new import_better_sqlite3.default(snapshot, { readonly: true, fileMustExist: true });
14061
+ const db = openReadonlySqlite(snapshot);
13630
14062
  try {
13631
14063
  const row = db.prepare(
13632
14064
  `SELECT
@@ -13639,7 +14071,7 @@ function importConductorWorkspace(workspaceId) {
13639
14071
  ).get(workspaceId);
13640
14072
  if (!row) throw new Error(`Conductor workspace not found: ${workspaceId}`);
13641
14073
  const worktreePath = String(row.workspacePath);
13642
- if (!(0, import_node_fs29.existsSync)(worktreePath)) {
14074
+ if (!(0, import_node_fs33.existsSync)(worktreePath)) {
13643
14075
  throw new Error(`Conductor worktree missing on disk: ${worktreePath}`);
13644
14076
  }
13645
14077
  let sessionId = null;
@@ -13702,7 +14134,7 @@ function importConductorWorkspace(workspaceId) {
13702
14134
  db.close();
13703
14135
  }
13704
14136
  } finally {
13705
- (0, import_node_fs29.rmSync)(tmp, { recursive: true, force: true });
14137
+ (0, import_node_fs33.rmSync)(tmp, { recursive: true, force: true });
13706
14138
  }
13707
14139
  }
13708
14140
  async function importConductorWorkspaceAsync(workspaceId) {
@@ -13713,8 +14145,8 @@ async function importConductorWorkspaceAsync(workspaceId) {
13713
14145
  var import_node_events = require("events");
13714
14146
 
13715
14147
  // src/slack/outbound-watch.ts
13716
- var import_node_fs31 = require("fs");
13717
- var import_node_path27 = require("path");
14148
+ var import_node_fs35 = require("fs");
14149
+ var import_node_path32 = require("path");
13718
14150
  init_paths();
13719
14151
  init_private_file();
13720
14152
  init_secure_file();
@@ -13760,19 +14192,19 @@ async function slackAuthTest(token) {
13760
14192
  }
13761
14193
 
13762
14194
  // src/slack/reply-target.ts
13763
- var import_node_fs30 = require("fs");
13764
- var import_node_path25 = require("path");
14195
+ var import_node_fs34 = require("fs");
14196
+ var import_node_path30 = require("path");
13765
14197
  init_paths();
13766
14198
  init_private_file();
13767
14199
  init_secure_file();
13768
14200
  function storePath2() {
13769
- return (0, import_node_path25.join)(appDataDir(), "slack-reply-to.json");
14201
+ return (0, import_node_path30.join)(appDataDir(), "slack-reply-to.json");
13770
14202
  }
13771
14203
  function readStore2() {
13772
14204
  const path2 = storePath2();
13773
- if (!(0, import_node_fs30.existsSync)(path2)) return {};
14205
+ if (!(0, import_node_fs34.existsSync)(path2)) return {};
13774
14206
  try {
13775
- 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"));
13776
14208
  return parsed?.targets && typeof parsed.targets === "object" ? parsed.targets : {};
13777
14209
  } catch {
13778
14210
  return {};
@@ -13789,11 +14221,11 @@ function getSlackReplyTarget(threadId) {
13789
14221
  }
13790
14222
 
13791
14223
  // src/slack/workspaces.ts
13792
- var import_node_path26 = require("path");
14224
+ var import_node_path31 = require("path");
13793
14225
  init_paths();
13794
14226
  init_secure_file();
13795
14227
  function storePath3() {
13796
- return (0, import_node_path26.join)(appDataDir(), "slack-workspaces.json");
14228
+ return (0, import_node_path31.join)(appDataDir(), "slack-workspaces.json");
13797
14229
  }
13798
14230
  function readStore3() {
13799
14231
  try {
@@ -13909,7 +14341,7 @@ var POLL_INTERVAL_MS = 12e3;
13909
14341
  var lastPollMs = 0;
13910
14342
  var nameCache = /* @__PURE__ */ new Map();
13911
14343
  function storePath4() {
13912
- return (0, import_node_path27.join)(appDataDir(), "slack-outbound-watch.json");
14344
+ return (0, import_node_path32.join)(appDataDir(), "slack-outbound-watch.json");
13913
14345
  }
13914
14346
  function watchId(teamId, channelId, ts) {
13915
14347
  return `${teamId}:${channelId}:${ts}`;
@@ -13939,9 +14371,9 @@ function tsNewer(a, b) {
13939
14371
  }
13940
14372
  function readStore4() {
13941
14373
  const path2 = storePath4();
13942
- if (!(0, import_node_fs31.existsSync)(path2)) return [];
14374
+ if (!(0, import_node_fs35.existsSync)(path2)) return [];
13943
14375
  try {
13944
- 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"));
13945
14377
  return Array.isArray(parsed?.watches) ? parsed.watches : [];
13946
14378
  } catch {
13947
14379
  return [];
@@ -14286,7 +14718,7 @@ async function refreshSlackReplyBadges(opts) {
14286
14718
  }
14287
14719
 
14288
14720
  // src/orchestrator/orchestrator.ts
14289
- var import_node_fs37 = require("fs");
14721
+ var import_node_fs41 = require("fs");
14290
14722
  init_error_detail();
14291
14723
  init_agents();
14292
14724
  init_worktree();
@@ -14307,8 +14739,8 @@ function shouldAutoArchiveOnPrMerge(opts) {
14307
14739
  }
14308
14740
 
14309
14741
  // src/git/orphan-cleanup.ts
14310
- var import_node_fs32 = require("fs");
14311
- var import_node_path28 = require("path");
14742
+ var import_node_fs36 = require("fs");
14743
+ var import_node_path33 = require("path");
14312
14744
  init_worktree();
14313
14745
  init_thread_store();
14314
14746
  init_paths();
@@ -14323,9 +14755,9 @@ async function findOrphanWorktrees(repoPaths) {
14323
14755
  repoPaths?.length ? repoPaths : threads.map((t) => t.repoPath).filter(Boolean)
14324
14756
  );
14325
14757
  const homeRoot = sideboardWorkspacesDir();
14326
- if ((0, import_node_fs32.existsSync)(homeRoot)) {
14758
+ if ((0, import_node_fs36.existsSync)(homeRoot)) {
14327
14759
  try {
14328
- 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 })) {
14329
14761
  if (!entry.isDirectory()) continue;
14330
14762
  void entry;
14331
14763
  }
@@ -14335,7 +14767,7 @@ async function findOrphanWorktrees(repoPaths) {
14335
14767
  const orphans = [];
14336
14768
  const seen = /* @__PURE__ */ new Set();
14337
14769
  for (const repoPath of repos) {
14338
- if (!repoPath || !(0, import_node_fs32.existsSync)(repoPath)) continue;
14770
+ if (!repoPath || !(0, import_node_fs36.existsSync)(repoPath)) continue;
14339
14771
  try {
14340
14772
  const wts = await listWorktrees(repoPath);
14341
14773
  for (const wt of wts) {
@@ -14346,7 +14778,7 @@ async function findOrphanWorktrees(repoPaths) {
14346
14778
  seen.add(path2);
14347
14779
  let mtimeMs = 0;
14348
14780
  try {
14349
- mtimeMs = (0, import_node_fs32.statSync)(path2).mtimeMs;
14781
+ mtimeMs = (0, import_node_fs36.statSync)(path2).mtimeMs;
14350
14782
  } catch {
14351
14783
  mtimeMs = 0;
14352
14784
  }
@@ -14356,16 +14788,16 @@ async function findOrphanWorktrees(repoPaths) {
14356
14788
  }
14357
14789
  try {
14358
14790
  const root = worktreesRoot(repoPath);
14359
- if ((0, import_node_fs32.existsSync)(root)) {
14360
- 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 })) {
14361
14793
  if (!entry.isDirectory()) continue;
14362
- const path2 = (0, import_node_path28.join)(root, entry.name).replace(/\/$/, "");
14794
+ const path2 = (0, import_node_path33.join)(root, entry.name).replace(/\/$/, "");
14363
14795
  if (known.has(path2) || seen.has(path2)) continue;
14364
- 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;
14365
14797
  seen.add(path2);
14366
14798
  let mtimeMs = 0;
14367
14799
  try {
14368
- mtimeMs = (0, import_node_fs32.statSync)(path2).mtimeMs;
14800
+ mtimeMs = (0, import_node_fs36.statSync)(path2).mtimeMs;
14369
14801
  } catch {
14370
14802
  mtimeMs = Date.now();
14371
14803
  }
@@ -14511,8 +14943,8 @@ async function applyThreadIntoMain(thread, opts) {
14511
14943
  }
14512
14944
 
14513
14945
  // src/git/clone-repo.ts
14514
- var import_node_fs33 = require("fs");
14515
- var import_node_path29 = require("path");
14946
+ var import_node_fs37 = require("fs");
14947
+ var import_node_path34 = require("path");
14516
14948
  var import_execa6 = require("execa");
14517
14949
  init_paths();
14518
14950
  init_workspaces();
@@ -14522,12 +14954,12 @@ async function cloneRepoIntoSideboard(opts) {
14522
14954
  if (!url) throw new Error("Clone URL is required");
14523
14955
  let name = opts.name?.trim();
14524
14956
  if (!name) {
14525
- const leaf = (0, import_node_path29.basename)(url.replace(/\/$/, "").replace(/\.git$/, ""));
14957
+ const leaf = (0, import_node_path34.basename)(url.replace(/\/$/, "").replace(/\.git$/, ""));
14526
14958
  name = leaf || "repo";
14527
14959
  }
14528
14960
  name = name.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "repo";
14529
- const dest = (0, import_node_path29.join)(sideboardReposDir(), name);
14530
- 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)) {
14531
14963
  const repoPath2 = await resolveRepoRoot(dest);
14532
14964
  const workspace2 = await ensureWorkspace(repoPath2);
14533
14965
  return { repoPath: repoPath2, workspace: workspace2 };
@@ -14549,8 +14981,8 @@ init_orchestrator_capable();
14549
14981
 
14550
14982
  // src/review/request-review.ts
14551
14983
  var import_node_crypto8 = require("crypto");
14552
- var import_node_fs34 = require("fs");
14553
- var import_node_path30 = require("path");
14984
+ var import_node_fs38 = require("fs");
14985
+ var import_node_path35 = require("path");
14554
14986
  init_global_workspace();
14555
14987
  init_thread_store();
14556
14988
 
@@ -14698,22 +15130,22 @@ function shouldRefreshReviewRequestTemplate(content) {
14698
15130
  return LEGACY_REVIEW_TEMPLATE_MARKERS.every((m) => trimmed.includes(m));
14699
15131
  }
14700
15132
  function readTextIfPresent(abs) {
14701
- if (!(0, import_node_fs34.existsSync)(abs)) return null;
15133
+ if (!(0, import_node_fs38.existsSync)(abs)) return null;
14702
15134
  try {
14703
- const content = (0, import_node_fs34.readFileSync)(abs, "utf8");
15135
+ const content = (0, import_node_fs38.readFileSync)(abs, "utf8");
14704
15136
  return content.trim() ? content : null;
14705
15137
  } catch {
14706
15138
  return null;
14707
15139
  }
14708
15140
  }
14709
15141
  function ensureAttachmentsGitignore(worktreePath) {
14710
- const gitignoreAbs = (0, import_node_path30.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
14711
- if ((0, import_node_fs34.existsSync)(gitignoreAbs)) return;
14712
- (0, import_node_fs34.mkdirSync)((0, import_node_path30.dirname)(gitignoreAbs), { recursive: true });
14713
- (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");
14714
15146
  }
14715
15147
  function resolveReviewGuidelines(worktreePath) {
14716
- const repoAbs = (0, import_node_path30.join)(worktreePath, REPO_REVIEW_PATH);
15148
+ const repoAbs = (0, import_node_path35.join)(worktreePath, REPO_REVIEW_PATH);
14717
15149
  const repoContent = readTextIfPresent(repoAbs);
14718
15150
  if (repoContent) {
14719
15151
  return {
@@ -14723,7 +15155,7 @@ function resolveReviewGuidelines(worktreePath) {
14723
15155
  source: "repo"
14724
15156
  };
14725
15157
  }
14726
- const localAbs = (0, import_node_path30.join)(worktreePath, REVIEW_REQUEST_PATH);
15158
+ const localAbs = (0, import_node_path35.join)(worktreePath, REVIEW_REQUEST_PATH);
14727
15159
  const localContent = readTextIfPresent(localAbs);
14728
15160
  if (localContent && !shouldRefreshReviewRequestTemplate(localContent)) {
14729
15161
  return {
@@ -14733,7 +15165,7 @@ function resolveReviewGuidelines(worktreePath) {
14733
15165
  source: "local"
14734
15166
  };
14735
15167
  }
14736
- 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);
14737
15169
  const legacyContent = readTextIfPresent(legacyAbs);
14738
15170
  if (legacyContent && !shouldRefreshReviewRequestTemplate(legacyContent)) {
14739
15171
  return {
@@ -14744,8 +15176,8 @@ function resolveReviewGuidelines(worktreePath) {
14744
15176
  };
14745
15177
  }
14746
15178
  ensureAttachmentsGitignore(worktreePath);
14747
- (0, import_node_fs34.mkdirSync)((0, import_node_path30.dirname)(localAbs), { recursive: true });
14748
- (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");
14749
15181
  return {
14750
15182
  path: REVIEW_REQUEST_PATH,
14751
15183
  name: REVIEW_REQUEST_NAME,
@@ -14754,7 +15186,7 @@ function resolveReviewGuidelines(worktreePath) {
14754
15186
  };
14755
15187
  }
14756
15188
  function ensureReviewRequestFile(worktreePath) {
14757
- const repoAbs = (0, import_node_path30.join)(worktreePath, REPO_REVIEW_PATH);
15189
+ const repoAbs = (0, import_node_path35.join)(worktreePath, REPO_REVIEW_PATH);
14758
15190
  const repoContent = readTextIfPresent(repoAbs);
14759
15191
  if (repoContent) {
14760
15192
  return {
@@ -14764,10 +15196,10 @@ function ensureReviewRequestFile(worktreePath) {
14764
15196
  source: "repo"
14765
15197
  };
14766
15198
  }
14767
- const localAbs = (0, import_node_path30.join)(worktreePath, REVIEW_REQUEST_PATH);
14768
- 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));
14769
15201
  if (localContent && !shouldRefreshReviewRequestTemplate(localContent)) {
14770
- 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;
14771
15203
  return {
14772
15204
  path: path2,
14773
15205
  name: REVIEW_REQUEST_NAME,
@@ -14775,8 +15207,8 @@ function ensureReviewRequestFile(worktreePath) {
14775
15207
  source: "local"
14776
15208
  };
14777
15209
  }
14778
- (0, import_node_fs34.mkdirSync)((0, import_node_path30.dirname)(repoAbs), { recursive: true });
14779
- (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");
14780
15212
  return {
14781
15213
  path: REPO_REVIEW_PATH,
14782
15214
  name: REPO_REVIEW_NAME,
@@ -14796,7 +15228,7 @@ function buildReviewRequestAttachment(content, opts) {
14796
15228
  };
14797
15229
  }
14798
15230
  function readExistingReviewRequestFile(worktreePath) {
14799
- 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));
14800
15232
  }
14801
15233
  async function requestReview(threadRef, send2) {
14802
15234
  const from = findThreadByRef(threadRef);
@@ -15057,7 +15489,7 @@ var Orchestrator = class {
15057
15489
  }
15058
15490
  continue;
15059
15491
  }
15060
- if (!(0, import_node_fs37.existsSync)(thread.worktreePath)) {
15492
+ if (!(0, import_node_fs41.existsSync)(thread.worktreePath)) {
15061
15493
  setStatus(thread.id, "broken", "Worktree missing on disk");
15062
15494
  this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
15063
15495
  continue;
@@ -15560,10 +15992,12 @@ var Orchestrator = class {
15560
15992
  if (event.type === "stderr" && typeof event.data === "string") {
15561
15993
  pushTurnStderr(stderrTail, event.data);
15562
15994
  }
15563
- const live = readThread(threadId);
15564
- if (live?.lastError?.includes("reconciled on startup") && (this.activeTurns.has(threadId) || this.startingTurns.has(threadId))) {
15565
- setStatus(threadId, "running");
15566
- this.emit({ type: "status_changed", threadId, status: "running" });
15995
+ if (event.type !== "stdout" && event.type !== "thinking") {
15996
+ const live = readThread(threadId);
15997
+ if (live?.lastError?.includes("reconciled on startup") && (this.activeTurns.has(threadId) || this.startingTurns.has(threadId))) {
15998
+ setStatus(threadId, "running");
15999
+ this.emit({ type: "status_changed", threadId, status: "running" });
16000
+ }
15567
16001
  }
15568
16002
  }
15569
16003
  );
@@ -16147,8 +16581,9 @@ var Orchestrator = class {
16147
16581
  return result;
16148
16582
  }
16149
16583
  async mergePr(threadRef) {
16150
- const { thread, selector, cwd } = await this.withPrSelector(threadRef);
16584
+ const { thread, selectors, cwd } = await this.withPrSelector(threadRef);
16151
16585
  this.assertNotGlobal(thread, "Merge PR");
16586
+ const selector = selectors[0];
16152
16587
  if (!selector) throw new Error("No pull request linked to this thread");
16153
16588
  const result = await mergePr(cwd, selector);
16154
16589
  const state = normalizePrState(result.state) || "MERGED";
@@ -16168,29 +16603,34 @@ var Orchestrator = class {
16168
16603
  await this.persistPrMetaAndMaybeArchive(thread, metaLike);
16169
16604
  return { url: metaLike.url, state };
16170
16605
  }
16171
- /** Resolve PR selector and optionally persist `prUrl` when found. */
16606
+ /** Resolve PR selectors and optionally persist `prUrl` when found. */
16172
16607
  async withPrSelector(threadRef) {
16173
16608
  const thread = this.requireThread(threadRef);
16174
- const selector = resolvePrSelector(thread);
16609
+ const selectors = resolvePrSelectors(thread);
16175
16610
  const cwd = thread.worktreePath;
16176
16611
  if (!cwd?.trim()) {
16177
16612
  throw new Error(`Thread ${threadRef} has no worktreePath`);
16178
16613
  }
16179
- return { thread, selector, cwd };
16614
+ return { thread, selectors, cwd };
16180
16615
  }
16181
16616
  async getPrChecks(threadRef) {
16182
- const { selector, cwd } = await this.withPrSelector(threadRef);
16183
- if (!selector) return null;
16184
- 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;
16185
16623
  }
16186
16624
  async getPrMeta(threadRef) {
16187
- const { thread, selector, cwd } = await this.withPrSelector(threadRef);
16188
- if (!selector) return null;
16189
- const meta = await getPrMeta(cwd, selector);
16190
- if (meta) {
16191
- 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
+ }
16192
16632
  }
16193
- return meta;
16633
+ return null;
16194
16634
  }
16195
16635
  /**
16196
16636
  * Persist PR URL/title/state and Conductor-style auto-archive when the PR
@@ -16314,9 +16754,12 @@ var Orchestrator = class {
16314
16754
  return { stack: result.stack, threads: result.threads };
16315
16755
  }
16316
16756
  async getPrDetails(threadRef) {
16317
- const { thread, selector, cwd } = await this.withPrSelector(threadRef);
16318
- if (!selector) return null;
16319
- 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
+ }
16320
16763
  if (details) {
16321
16764
  const patch = {};
16322
16765
  if (details.url && details.url !== thread.prUrl) patch.prUrl = details.url;
@@ -16543,7 +16986,7 @@ var Orchestrator = class {
16543
16986
  this.emit({ type: "status_changed", threadId: restored2.id, status: restored2.status });
16544
16987
  return restored2;
16545
16988
  }
16546
- if (!(0, import_node_fs37.existsSync)(thread.worktreePath)) {
16989
+ if (!(0, import_node_fs41.existsSync)(thread.worktreePath)) {
16547
16990
  const { createThreadWorktree: createThreadWorktree2 } = await Promise.resolve().then(() => (init_worktree(), worktree_exports));
16548
16991
  const { execa: execa7 } = await import("execa");
16549
16992
  const slug = thread.worktreePath.split("/").pop();
@@ -16784,7 +17227,7 @@ init_coordinator_prompt();
16784
17227
  var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
16785
17228
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
16786
17229
  var import_zod3 = require("zod");
16787
- var import_node_path33 = require("path");
17230
+ var import_node_path38 = require("path");
16788
17231
  init_worktree();
16789
17232
  init_global_workspace();
16790
17233
  init_list_models();
@@ -17398,6 +17841,7 @@ function registerLinearTools(server) {
17398
17841
  }
17399
17842
 
17400
17843
  // src/mcp/server.ts
17844
+ init_git_auth_mode();
17401
17845
  var MAX_ORCH_THREADS = 5;
17402
17846
  var CREATE_THREAD_TIMEOUT_MS = 9e4;
17403
17847
  function withTimeout(promise, ms, label) {
@@ -17419,6 +17863,14 @@ function withTimeout(promise, ms, label) {
17419
17863
  }
17420
17864
  async function startMcpServer() {
17421
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
+ }
17422
17874
  try {
17423
17875
  const { maxConcurrentAgents: maxConcurrentAgents2 } = await Promise.resolve().then(() => (init_app_settings(), app_settings_exports));
17424
17876
  orch.setMaxConcurrent(maxConcurrentAgents2());
@@ -17462,7 +17914,7 @@ async function startMcpServer() {
17462
17914
  async () => {
17463
17915
  const threads = orch.getThreads(true);
17464
17916
  const lines = threads.map((t) => {
17465
- 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;
17466
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}` : ""}`;
17467
17919
  });
17468
17920
  return {
@@ -17525,7 +17977,7 @@ async function startMcpServer() {
17525
17977
  );
17526
17978
  server.tool(
17527
17979
  "ask_user",
17528
- "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.",
17529
17981
  {
17530
17982
  questions: import_zod3.z.array(
17531
17983
  import_zod3.z.object({
@@ -18770,6 +19222,76 @@ init_accounts();
18770
19222
  init_connected_teams();
18771
19223
  init_injected_mcp();
18772
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
+
18773
19295
  // src/slack/oauth.ts
18774
19296
  var import_node_crypto10 = require("crypto");
18775
19297
  init_app_settings();
@@ -20076,9 +20598,9 @@ var import_node_http3 = require("http");
20076
20598
  var import_ws3 = require("ws");
20077
20599
 
20078
20600
  // src/slack/relay-static.ts
20079
- var import_node_fs38 = require("fs");
20601
+ var import_node_fs43 = require("fs");
20080
20602
  var import_promises = require("fs/promises");
20081
- var import_node_path34 = __toESM(require("path"), 1);
20603
+ var import_node_path40 = __toESM(require("path"), 1);
20082
20604
  var TYPES = {
20083
20605
  ".css": "text/css; charset=utf-8",
20084
20606
  ".html": "text/html; charset=utf-8",
@@ -20109,9 +20631,9 @@ function resolveStaticPath(root, requestUrl) {
20109
20631
  return null;
20110
20632
  }
20111
20633
  if (!pathname.startsWith("/") || pathname.includes("\0")) return null;
20112
- const rootResolved = import_node_path34.default.resolve(root);
20113
- const candidate = import_node_path34.default.resolve(rootResolved, `.${pathname}`);
20114
- 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)) {
20115
20637
  return null;
20116
20638
  }
20117
20639
  return candidate;
@@ -20125,7 +20647,7 @@ async function fileSize(file) {
20125
20647
  }
20126
20648
  }
20127
20649
  function sendFile(req, res, file, size) {
20128
- const ext = import_node_path34.default.extname(file).toLowerCase();
20650
+ const ext = import_node_path40.default.extname(file).toLowerCase();
20129
20651
  res.writeHead(200, {
20130
20652
  "Content-Type": TYPES[ext] ?? "application/octet-stream",
20131
20653
  "Content-Length": size,
@@ -20135,7 +20657,7 @@ function sendFile(req, res, file, size) {
20135
20657
  res.end();
20136
20658
  return true;
20137
20659
  }
20138
- (0, import_node_fs38.createReadStream)(file).pipe(res);
20660
+ (0, import_node_fs43.createReadStream)(file).pipe(res);
20139
20661
  return true;
20140
20662
  }
20141
20663
  async function tryServeStatic(req, res, root) {
@@ -20144,7 +20666,7 @@ async function tryServeStatic(req, res, root) {
20144
20666
  if (!candidate) return false;
20145
20667
  const direct = await fileSize(candidate);
20146
20668
  if (direct != null) return sendFile(req, res, candidate, direct);
20147
- const asIndex = import_node_path34.default.join(candidate, "index.html");
20669
+ const asIndex = import_node_path40.default.join(candidate, "index.html");
20148
20670
  const indexSize = await fileSize(asIndex);
20149
20671
  if (indexSize != null) return sendFile(req, res, asIndex, indexSize);
20150
20672
  return false;
@@ -20477,6 +20999,7 @@ async function startSlackRelayServer(opts) {
20477
20999
  cleanupOrphanWorktrees,
20478
21000
  cloneRepoIntoSideboard,
20479
21001
  codexAdapter,
21002
+ codexSandboxWritableRootsArgs,
20480
21003
  codexUnattendedGitConfigArgs,
20481
21004
  coerceOrchestratorAgent,
20482
21005
  collectTakenTeamSlugs,
@@ -20593,6 +21116,7 @@ async function startSlackRelayServer(opts) {
20593
21116
  getPr,
20594
21117
  getPrChecks,
20595
21118
  getPrDetails,
21119
+ getPrForHeadBranch,
20596
21120
  getPrMeta,
20597
21121
  getPrStack,
20598
21122
  getRepoSetupInfo,
@@ -20629,6 +21153,7 @@ async function startSlackRelayServer(opts) {
20629
21153
  isCloudCoordinatorThread,
20630
21154
  isConductorBundledCli,
20631
21155
  isCursorAutoModel,
21156
+ isDefaultishSourceRef,
20632
21157
  isDirty,
20633
21158
  isGhRateLimitError,
20634
21159
  isGlobalRepoPath,
@@ -20695,8 +21220,10 @@ async function startSlackRelayServer(opts) {
20695
21220
  maybeCompactContext,
20696
21221
  mcpAllowTools,
20697
21222
  mcpAuthWarnings,
21223
+ mergeAgentGitAuthEnv,
20698
21224
  mergePr,
20699
21225
  mergePrStack,
21226
+ mergeSideboardIntoMcpServersJson,
20700
21227
  mergeUsage,
20701
21228
  nextPastedTextName,
20702
21229
  nextThinkingEffort,
@@ -20747,6 +21274,7 @@ async function startSlackRelayServer(opts) {
20747
21274
  recordSlackOutboundWatch,
20748
21275
  refreshGitHubAuth,
20749
21276
  refreshSlackReplyBadges,
21277
+ registerPackagedUserMcpClients,
20750
21278
  releaseCaffeinateHoldForThread,
20751
21279
  removeWorkspace,
20752
21280
  removeWorktree,
@@ -20755,9 +21283,11 @@ async function startSlackRelayServer(opts) {
20755
21283
  requestReview,
20756
21284
  requireAgent,
20757
21285
  resetGhStackDetectCache,
21286
+ resetGithubAgentTokenMemo,
20758
21287
  resolveAgentExecutable,
20759
21288
  resolveAgentGitAuthEnv,
20760
21289
  resolveClaudeExecutable,
21290
+ resolveCodexGitWritableRoots,
20761
21291
  resolveCommandBinarySync,
20762
21292
  resolveConductorCursorAgentId,
20763
21293
  resolveCursorModelId,
@@ -20774,6 +21304,7 @@ async function startSlackRelayServer(opts) {
20774
21304
  resolveNewThreadOptions,
20775
21305
  resolvePlanMarkdown,
20776
21306
  resolvePrSelector,
21307
+ resolvePrSelectors,
20777
21308
  resolveQuotaFallbackAgent,
20778
21309
  resolveRepoRoot,
20779
21310
  resolveReviewGuidelines,
@@ -20796,6 +21327,7 @@ async function startSlackRelayServer(opts) {
20796
21327
  sanitizeMcpServerName,
20797
21328
  saveAppSettings,
20798
21329
  saveLinearOAuth,
21330
+ scrubGithubTokensFromChildEnv,
20799
21331
  secureFileUnlocksWith,
20800
21332
  setCaffeinateHold,
20801
21333
  setHttpFetchImpl,
@@ -20866,7 +21398,10 @@ async function startSlackRelayServer(opts) {
20866
21398
  updateLinearIssue,
20867
21399
  updateOpencodeSettings,
20868
21400
  updateThread,
21401
+ userClaudeMcpConfigPath,
21402
+ userCursorMcpConfigPath,
20869
21403
  validateLinearApiKey,
21404
+ warmGithubAgentAuth,
20870
21405
  withAgentInstructions,
20871
21406
  withExportedPath,
20872
21407
  withThreadLock,