@episoda/cli 0.2.158 → 0.2.160

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.
package/dist/index.js CHANGED
@@ -1653,10 +1653,6 @@ var require_git_executor = __commonJS({
1653
1653
  };
1654
1654
  } catch {
1655
1655
  }
1656
- try {
1657
- await this.runGitCommand(["fetch", "--all", "--prune"], cwd, options);
1658
- } catch {
1659
- }
1660
1656
  const args = ["worktree", "add"];
1661
1657
  if (command.detach) {
1662
1658
  args.push("--detach", command.path);
@@ -3203,13 +3199,14 @@ async function getDevServerStatus() {
3203
3199
  }
3204
3200
 
3205
3201
  // src/commands/daemon.ts
3206
- var import_child_process4 = require("child_process");
3202
+ var import_child_process5 = require("child_process");
3207
3203
  var path6 = __toESM(require("path"));
3208
3204
  var fs5 = __toESM(require("fs"));
3209
3205
 
3210
3206
  // src/daemon/worktree-manager.ts
3211
3207
  var fs2 = __toESM(require("fs"));
3212
3208
  var path3 = __toESM(require("path"));
3209
+ var import_child_process2 = require("child_process");
3213
3210
  var import_core3 = __toESM(require_dist());
3214
3211
  function validateModuleUid(moduleUid) {
3215
3212
  if (!moduleUid || typeof moduleUid !== "string" || !moduleUid.trim()) {
@@ -3226,6 +3223,38 @@ function validateModuleUid(moduleUid) {
3226
3223
  }
3227
3224
  return true;
3228
3225
  }
3226
+ function isValidDefaultBranchName(name) {
3227
+ if (!name || typeof name !== "string") return false;
3228
+ const trimmed = name.trim();
3229
+ if (trimmed !== name) return false;
3230
+ if (name.length > 255) return false;
3231
+ if (name.startsWith("-")) return false;
3232
+ if (name.startsWith("/")) return false;
3233
+ if (name.endsWith("/")) return false;
3234
+ if (name.endsWith(".")) return false;
3235
+ if (name.endsWith(".lock")) return false;
3236
+ if (name.includes("..")) return false;
3237
+ if (name.includes("@{")) return false;
3238
+ if (name.includes("//")) return false;
3239
+ if (name.includes("\\")) return false;
3240
+ if (/[ ~^:?*\[\]\s]/.test(name)) return false;
3241
+ if (!/^[A-Za-z0-9][A-Za-z0-9._/-]*$/.test(name)) return false;
3242
+ return true;
3243
+ }
3244
+ function runGit(args, cwd, timeoutMs) {
3245
+ const res = (0, import_child_process2.spawnSync)("git", args, {
3246
+ cwd,
3247
+ encoding: "utf-8",
3248
+ timeout: timeoutMs,
3249
+ windowsHide: true,
3250
+ shell: false
3251
+ });
3252
+ return {
3253
+ success: res.status === 0,
3254
+ stdout: (res.stdout || "").toString(),
3255
+ stderr: (res.stderr || "").toString()
3256
+ };
3257
+ }
3229
3258
  var WorktreeManager = class _WorktreeManager {
3230
3259
  constructor(projectRoot) {
3231
3260
  // ============================================================
@@ -3283,22 +3312,24 @@ var WorktreeManager = class _WorktreeManager {
3283
3312
  */
3284
3313
  async ensureFetchRefspecConfigured() {
3285
3314
  try {
3286
- const { execSync: execSync10 } = require("child_process");
3287
3315
  let fetchRefspec = null;
3288
3316
  try {
3289
- fetchRefspec = execSync10("git config --get remote.origin.fetch", {
3290
- cwd: this.bareRepoPath,
3291
- encoding: "utf-8",
3292
- timeout: 5e3
3293
- }).trim();
3317
+ const res = runGit(["config", "--get", "remote.origin.fetch"], this.bareRepoPath, 5e3);
3318
+ if (res.success) {
3319
+ fetchRefspec = res.stdout.trim();
3320
+ }
3294
3321
  } catch {
3295
3322
  }
3296
3323
  if (!fetchRefspec) {
3297
3324
  console.log("[WorktreeManager] EP1014: Configuring missing fetch refspec for bare repo");
3298
- execSync10('git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"', {
3299
- cwd: this.bareRepoPath,
3300
- timeout: 5e3
3301
- });
3325
+ const setRes = runGit(
3326
+ ["config", "remote.origin.fetch", "+refs/heads/*:refs/remotes/origin/*"],
3327
+ this.bareRepoPath,
3328
+ 5e3
3329
+ );
3330
+ if (!setRes.success) {
3331
+ throw new Error(setRes.stderr || "Failed to configure fetch refspec");
3332
+ }
3302
3333
  console.log("[WorktreeManager] EP1014: Fetch refspec configured successfully");
3303
3334
  }
3304
3335
  } catch (error) {
@@ -3331,11 +3362,44 @@ var WorktreeManager = class _WorktreeManager {
3331
3362
  manager.writeConfig(config);
3332
3363
  return manager;
3333
3364
  }
3365
+ /**
3366
+ * EP1373: Resolve the default branch name using a 3-level fallback:
3367
+ * 1. Explicit defaultBranch parameter (from project GitHub config)
3368
+ * 2. Git-derived: refs/remotes/origin/HEAD symbolic-ref target
3369
+ * 3. Final fallback: 'main' (with warning)
3370
+ */
3371
+ async resolveDefaultBranch(explicitDefault) {
3372
+ if (explicitDefault) {
3373
+ if (isValidDefaultBranchName(explicitDefault)) {
3374
+ return explicitDefault;
3375
+ }
3376
+ console.warn(`[WorktreeManager] EP1373: Invalid defaultBranch provided ("${explicitDefault}"), ignoring`);
3377
+ }
3378
+ try {
3379
+ const res = runGit(["symbolic-ref", "refs/remotes/origin/HEAD"], this.bareRepoPath, 5e3);
3380
+ if (!res.success) {
3381
+ throw new Error(res.stderr || "symbolic-ref failed");
3382
+ }
3383
+ const symref = res.stdout.trim();
3384
+ const match = symref.match(/^refs\/remotes\/origin\/(.+)$/);
3385
+ if (match && match[1]) {
3386
+ const derived = match[1];
3387
+ if (isValidDefaultBranchName(derived)) {
3388
+ return derived;
3389
+ }
3390
+ console.warn(`[WorktreeManager] EP1373: Derived default branch from origin/HEAD is invalid ("${derived}"), ignoring`);
3391
+ }
3392
+ } catch {
3393
+ }
3394
+ console.warn('[WorktreeManager] EP1373: Could not resolve default branch, falling back to "main"');
3395
+ return "main";
3396
+ }
3334
3397
  /**
3335
3398
  * Create a worktree for a module
3336
3399
  * The entire operation is locked to prevent race conditions
3400
+ * EP1373: Added defaultBranch parameter for explicit default branch resolution
3337
3401
  */
3338
- async createWorktree(moduleUid, branchName, createBranch = false) {
3402
+ async createWorktree(moduleUid, branchName, createBranch = false, defaultBranch, correlationId) {
3339
3403
  if (!validateModuleUid(moduleUid)) {
3340
3404
  return {
3341
3405
  success: false,
@@ -3401,25 +3465,63 @@ var WorktreeManager = class _WorktreeManager {
3401
3465
  console.warn("[WorktreeManager] EP1265: Failed to prune worktrees (continuing):", pruneResult.output);
3402
3466
  }
3403
3467
  }
3404
- const fetchResult = await this.gitExecutor.execute({
3405
- action: "fetch",
3406
- remote: "origin",
3407
- branch: "+refs/heads/main:refs/remotes/origin/main"
3408
- }, { cwd: this.bareRepoPath });
3409
- if (!fetchResult.success && createBranch) {
3410
- console.error("[worktree-manager] Failed to fetch from origin:", fetchResult.output);
3411
- return {
3412
- success: false,
3413
- error: "Failed to fetch latest refs from origin. Cannot create worktree with stale refs."
3414
- };
3468
+ const resolvedDefault = await this.resolveDefaultBranch(defaultBranch);
3469
+ let shouldCreateBranch = createBranch;
3470
+ let startPoint;
3471
+ if (createBranch) {
3472
+ const managerFetchStartMs = Date.now();
3473
+ const fetchResult = await this.gitExecutor.execute({
3474
+ action: "fetch",
3475
+ remote: "origin",
3476
+ branch: `+refs/heads/${resolvedDefault}:refs/remotes/origin/${resolvedDefault}`
3477
+ }, { cwd: this.bareRepoPath });
3478
+ console.log(`[WorktreeManager] EP1373: manager_fetch phase=manager_fetch durationMs=${Date.now() - managerFetchStartMs} moduleUid=${moduleUid} correlationId=${correlationId || "none"} defaultBranch=${resolvedDefault} success=${fetchResult.success}`);
3479
+ if (!fetchResult.success) {
3480
+ console.error("[worktree-manager] Failed to fetch from origin:", fetchResult.output);
3481
+ return {
3482
+ success: false,
3483
+ error: "Failed to fetch latest refs from origin. Cannot create worktree with stale refs."
3484
+ };
3485
+ }
3486
+ startPoint = `origin/${resolvedDefault}`;
3487
+ } else {
3488
+ const existingBranchFetchStartMs = Date.now();
3489
+ const branchFetchResult = await this.gitExecutor.execute({
3490
+ action: "fetch",
3491
+ remote: "origin",
3492
+ branch: `+refs/heads/${branchName}:refs/remotes/origin/${branchName}`
3493
+ }, { cwd: this.bareRepoPath });
3494
+ console.log(`[WorktreeManager] EP1373: existing_branch_fetch phase=existing_branch_fetch durationMs=${Date.now() - existingBranchFetchStartMs} moduleUid=${moduleUid} correlationId=${correlationId || "none"} branch=${branchName} success=${branchFetchResult.success}`);
3495
+ const localBranchExists = runGit(
3496
+ ["show-ref", "--verify", `refs/heads/${branchName}`],
3497
+ this.bareRepoPath,
3498
+ 5e3
3499
+ ).success;
3500
+ const remoteBranchExists = runGit(
3501
+ ["show-ref", "--verify", `refs/remotes/origin/${branchName}`],
3502
+ this.bareRepoPath,
3503
+ 5e3
3504
+ ).success;
3505
+ if (!localBranchExists && remoteBranchExists) {
3506
+ shouldCreateBranch = true;
3507
+ startPoint = `origin/${branchName}`;
3508
+ console.log(`[WorktreeManager] EP1373: remote_only_branch phase=remote_only_branch moduleUid=${moduleUid} correlationId=${correlationId || "none"} branch=${branchName}`);
3509
+ } else if (!localBranchExists && !remoteBranchExists) {
3510
+ return {
3511
+ success: false,
3512
+ error: `Branch "${branchName}" not found locally or on origin`
3513
+ };
3514
+ }
3415
3515
  }
3516
+ const gitWorktreeAddStartMs = Date.now();
3416
3517
  const result = await this.gitExecutor.execute({
3417
3518
  action: "worktree_add",
3418
3519
  path: worktreePath,
3419
3520
  branch: branchName,
3420
- create: createBranch,
3421
- startPoint: createBranch ? "origin/main" : void 0
3521
+ create: shouldCreateBranch,
3522
+ startPoint
3422
3523
  }, { cwd: this.bareRepoPath });
3524
+ console.log(`[WorktreeManager] EP1373: git_worktree_add phase=git_worktree_add durationMs=${Date.now() - gitWorktreeAddStartMs} moduleUid=${moduleUid} correlationId=${correlationId || "none"} success=${result.success}`);
3423
3525
  if (!result.success) {
3424
3526
  return {
3425
3527
  success: false,
@@ -3915,8 +4017,8 @@ var WorktreeManager = class _WorktreeManager {
3915
4017
  console.log(`[WorktreeManager] EP959: Timeout: ${TIMEOUT_MINUTES} minutes`);
3916
4018
  console.log(`[WorktreeManager] EP959: Script: ${scriptPreview}`);
3917
4019
  try {
3918
- const { execSync: execSync10 } = require("child_process");
3919
- execSync10(script, {
4020
+ const { execSync: execSync11 } = require("child_process");
4021
+ execSync11(script, {
3920
4022
  cwd: worktree.worktreePath,
3921
4023
  stdio: "inherit",
3922
4024
  timeout: TIMEOUT_MINUTES * 60 * 1e3,
@@ -3950,8 +4052,8 @@ var WorktreeManager = class _WorktreeManager {
3950
4052
  console.log(`[WorktreeManager] EP959: Timeout: ${TIMEOUT_MINUTES} minutes`);
3951
4053
  console.log(`[WorktreeManager] EP959: Script: ${scriptPreview}`);
3952
4054
  try {
3953
- const { execSync: execSync10 } = require("child_process");
3954
- execSync10(script, {
4055
+ const { execSync: execSync11 } = require("child_process");
4056
+ execSync11(script, {
3955
4057
  cwd: worktree.worktreePath,
3956
4058
  stdio: "inherit",
3957
4059
  timeout: TIMEOUT_MINUTES * 60 * 1e3,
@@ -4090,7 +4192,7 @@ async function syncProjectPath(config, projectId, path18) {
4090
4192
  // src/utils/bootstrap.ts
4091
4193
  var fs3 = __toESM(require("fs"));
4092
4194
  var path4 = __toESM(require("path"));
4093
- var import_child_process2 = require("child_process");
4195
+ var import_child_process3 = require("child_process");
4094
4196
  async function extractBootstrapScripts(bareRepoPath, projectPath) {
4095
4197
  const scriptsDir = path4.join(projectPath, ".episoda", "scripts");
4096
4198
  const scriptPath = path4.join(scriptsDir, "api-helper.sh");
@@ -4106,7 +4208,7 @@ async function extractBootstrapScripts(bareRepoPath, projectPath) {
4106
4208
  ];
4107
4209
  for (const srcPath of scriptPaths) {
4108
4210
  try {
4109
- const scriptContent = (0, import_child_process2.execSync)(
4211
+ const scriptContent = (0, import_child_process3.execSync)(
4110
4212
  `git --git-dir="${bareRepoPath}" show main:${srcPath}`,
4111
4213
  { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
4112
4214
  );
@@ -4124,7 +4226,7 @@ async function extractBootstrapScripts(bareRepoPath, projectPath) {
4124
4226
  var fs4 = __toESM(require("fs"));
4125
4227
  var os = __toESM(require("os"));
4126
4228
  var path5 = __toESM(require("path"));
4127
- var import_child_process3 = require("child_process");
4229
+ var import_child_process4 = require("child_process");
4128
4230
  var PROTOCOL_SCHEME = "episoda";
4129
4231
  var APP_NAME = "Episoda";
4130
4232
  function parseProtocolUrl(rawUrl) {
@@ -4151,11 +4253,11 @@ function getLinuxDesktopPath() {
4151
4253
  function resolveBinaryFromPath(name) {
4152
4254
  try {
4153
4255
  if (process.platform === "win32") {
4154
- const output2 = (0, import_child_process3.execSync)(`where ${name}`, { stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
4256
+ const output2 = (0, import_child_process4.execSync)(`where ${name}`, { stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
4155
4257
  const first = output2.split(/\r?\n/)[0];
4156
4258
  return first || null;
4157
4259
  }
4158
- const output = (0, import_child_process3.execSync)(`command -v ${name}`, { stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
4260
+ const output = (0, import_child_process4.execSync)(`command -v ${name}`, { stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
4159
4261
  return output || null;
4160
4262
  } catch {
4161
4263
  return null;
@@ -4264,7 +4366,7 @@ exit 0
4264
4366
  fs4.writeFileSync(handlerPath, handlerScript, { mode: 493 });
4265
4367
  fs4.writeFileSync(infoPlistPath, infoPlist);
4266
4368
  const lsregister = "/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister";
4267
- (0, import_child_process3.execSync)(`"${lsregister}" -f "${appPath}"`);
4369
+ (0, import_child_process4.execSync)(`"${lsregister}" -f "${appPath}"`);
4268
4370
  return { success: true, message: "Protocol handler registered for macOS." };
4269
4371
  }
4270
4372
  if (process.platform === "linux") {
@@ -4286,7 +4388,7 @@ Terminal=false
4286
4388
  MimeType=x-scheme-handler/${PROTOCOL_SCHEME};
4287
4389
  `;
4288
4390
  fs4.writeFileSync(desktopPath, desktopEntry);
4289
- (0, import_child_process3.execSync)(`xdg-mime default ${path5.basename(desktopPath)} x-scheme-handler/${PROTOCOL_SCHEME}`);
4391
+ (0, import_child_process4.execSync)(`xdg-mime default ${path5.basename(desktopPath)} x-scheme-handler/${PROTOCOL_SCHEME}`);
4290
4392
  return { success: true, message: "Protocol handler registered for Linux." };
4291
4393
  }
4292
4394
  if (process.platform === "win32") {
@@ -4301,9 +4403,9 @@ shell.Run cmd, 0, False
4301
4403
  `;
4302
4404
  fs4.writeFileSync(vbsPath, vbsScript);
4303
4405
  const handlerCommand = `wscript.exe "${vbsPath}" "%1"`;
4304
- (0, import_child_process3.execSync)(`reg add HKCU\\Software\\Classes\\${PROTOCOL_SCHEME} /ve /d "URL:Episoda Protocol" /f`);
4305
- (0, import_child_process3.execSync)(`reg add HKCU\\Software\\Classes\\${PROTOCOL_SCHEME} /v "URL Protocol" /d "" /f`);
4306
- (0, import_child_process3.execSync)(`reg add HKCU\\Software\\Classes\\${PROTOCOL_SCHEME}\\shell\\open\\command /ve /d "${handlerCommand}" /f`);
4406
+ (0, import_child_process4.execSync)(`reg add HKCU\\Software\\Classes\\${PROTOCOL_SCHEME} /ve /d "URL:Episoda Protocol" /f`);
4407
+ (0, import_child_process4.execSync)(`reg add HKCU\\Software\\Classes\\${PROTOCOL_SCHEME} /v "URL Protocol" /d "" /f`);
4408
+ (0, import_child_process4.execSync)(`reg add HKCU\\Software\\Classes\\${PROTOCOL_SCHEME}\\shell\\open\\command /ve /d "${handlerCommand}" /f`);
4307
4409
  fs4.writeFileSync(marker, JSON.stringify({ registeredAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2));
4308
4410
  return { success: true, message: "Protocol handler registered for Windows." };
4309
4411
  }
@@ -4355,7 +4457,7 @@ async function maybeRegisterProtocolHandler() {
4355
4457
  }
4356
4458
  function findGitRoot(startDir) {
4357
4459
  try {
4358
- const result = (0, import_child_process4.execSync)("git rev-parse --show-toplevel", {
4460
+ const result = (0, import_child_process5.execSync)("git rev-parse --show-toplevel", {
4359
4461
  cwd: startDir,
4360
4462
  encoding: "utf-8",
4361
4463
  stdio: ["pipe", "pipe", "pipe"]
@@ -4559,14 +4661,14 @@ Received ${signal}. Daemon continues running in background.`);
4559
4661
  var os2 = __toESM(require("os"));
4560
4662
  var fs7 = __toESM(require("fs"));
4561
4663
  var path8 = __toESM(require("path"));
4562
- var import_child_process6 = require("child_process");
4664
+ var import_child_process7 = require("child_process");
4563
4665
  var import_core6 = __toESM(require_dist());
4564
4666
 
4565
4667
  // src/daemon/machine-id.ts
4566
4668
  var fs6 = __toESM(require("fs"));
4567
4669
  var path7 = __toESM(require("path"));
4568
4670
  var crypto2 = __toESM(require("crypto"));
4569
- var import_child_process5 = require("child_process");
4671
+ var import_child_process6 = require("child_process");
4570
4672
  var import_core5 = __toESM(require_dist());
4571
4673
  function isValidUUID(str) {
4572
4674
  const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -4609,7 +4711,7 @@ async function getMachineId() {
4609
4711
  function getHardwareUUID() {
4610
4712
  try {
4611
4713
  if (process.platform === "darwin") {
4612
- const output = (0, import_child_process5.execSync)(
4714
+ const output = (0, import_child_process6.execSync)(
4613
4715
  `ioreg -d2 -c IOPlatformExpertDevice | awk -F\\" '/IOPlatformUUID/{print $(NF-1)}'`,
4614
4716
  { encoding: "utf-8", timeout: 5e3 }
4615
4717
  ).trim();
@@ -4630,7 +4732,7 @@ function getHardwareUUID() {
4630
4732
  }
4631
4733
  }
4632
4734
  } else if (process.platform === "win32") {
4633
- const output = (0, import_child_process5.execSync)("wmic csproduct get uuid", {
4735
+ const output = (0, import_child_process6.execSync)("wmic csproduct get uuid", {
4634
4736
  encoding: "utf-8",
4635
4737
  timeout: 5e3
4636
4738
  });
@@ -5069,7 +5171,7 @@ async function monitorAuthorization(apiUrl, deviceCode, expiresIn) {
5069
5171
  resolve5(false);
5070
5172
  }, expiresIn * 1e3);
5071
5173
  const url = `${apiUrl}/api/oauth/authorize-stream?device_code=${deviceCode}`;
5072
- const curlProcess = (0, import_child_process6.spawn)("curl", ["-N", url]);
5174
+ const curlProcess = (0, import_child_process7.spawn)("curl", ["-N", url]);
5073
5175
  let buffer = "";
5074
5176
  curlProcess.stdout.on("data", (chunk) => {
5075
5177
  buffer += chunk.toString();
@@ -5173,7 +5275,7 @@ function openBrowser(url) {
5173
5275
  break;
5174
5276
  }
5175
5277
  try {
5176
- (0, import_child_process6.spawn)(command, args, {
5278
+ (0, import_child_process7.spawn)(command, args, {
5177
5279
  detached: true,
5178
5280
  stdio: "ignore"
5179
5281
  }).unref();
@@ -5194,7 +5296,7 @@ async function installGitCredentialHelper(apiUrl) {
5194
5296
  } catch {
5195
5297
  }
5196
5298
  try {
5197
- const allHelpers = (0, import_child_process6.execSync)("git config --global --get-all credential.helper", {
5299
+ const allHelpers = (0, import_child_process7.execSync)("git config --global --get-all credential.helper", {
5198
5300
  encoding: "utf8",
5199
5301
  stdio: ["pipe", "pipe", "pipe"]
5200
5302
  }).trim().split("\n");
@@ -5203,7 +5305,7 @@ async function installGitCredentialHelper(apiUrl) {
5203
5305
  }
5204
5306
  } catch {
5205
5307
  }
5206
- (0, import_child_process6.execSync)(`git config --global --add credential.helper "${helperPath}"`, {
5308
+ (0, import_child_process7.execSync)(`git config --global --add credential.helper "${helperPath}"`, {
5207
5309
  encoding: "utf8",
5208
5310
  stdio: ["pipe", "pipe", "pipe"]
5209
5311
  });
@@ -5247,7 +5349,7 @@ ${exportLine}
5247
5349
  var os3 = __toESM(require("os"));
5248
5350
  var fs8 = __toESM(require("fs"));
5249
5351
  var path9 = __toESM(require("path"));
5250
- var import_child_process7 = require("child_process");
5352
+ var import_child_process8 = require("child_process");
5251
5353
  var import_core7 = __toESM(require_dist());
5252
5354
  async function connectCommand(options) {
5253
5355
  const { code } = options;
@@ -5327,7 +5429,7 @@ async function installGitCredentialHelper2(apiUrl) {
5327
5429
  const scriptContent = generateCredentialHelperScript(apiUrl);
5328
5430
  fs8.writeFileSync(helperPath, scriptContent, { mode: 493 });
5329
5431
  try {
5330
- const allHelpers = (0, import_child_process7.execSync)("git config --global --get-all credential.helper", {
5432
+ const allHelpers = (0, import_child_process8.execSync)("git config --global --get-all credential.helper", {
5331
5433
  encoding: "utf8",
5332
5434
  stdio: ["pipe", "pipe", "pipe"]
5333
5435
  }).trim().split("\n");
@@ -5336,7 +5438,7 @@ async function installGitCredentialHelper2(apiUrl) {
5336
5438
  }
5337
5439
  } catch {
5338
5440
  }
5339
- (0, import_child_process7.execSync)(`git config --global --add credential.helper "${helperPath}"`, {
5441
+ (0, import_child_process8.execSync)(`git config --global --add credential.helper "${helperPath}"`, {
5340
5442
  encoding: "utf8",
5341
5443
  stdio: ["pipe", "pipe", "pipe"]
5342
5444
  });
@@ -5377,16 +5479,17 @@ ${exportLine}
5377
5479
  }
5378
5480
 
5379
5481
  // src/commands/status.ts
5482
+ var import_child_process10 = require("child_process");
5380
5483
  var import_core8 = __toESM(require_dist());
5381
5484
 
5382
5485
  // src/utils/update-checker.ts
5383
- var import_child_process8 = require("child_process");
5486
+ var import_child_process9 = require("child_process");
5384
5487
  var semver = __toESM(require("semver"));
5385
5488
  var PACKAGE_NAME = "@episoda/cli";
5386
5489
  var NPM_REGISTRY = "https://registry.npmjs.org";
5387
5490
  function isFileLinkedInstall() {
5388
5491
  try {
5389
- const output = (0, import_child_process8.execSync)(`npm list -g ${PACKAGE_NAME} --json`, {
5492
+ const output = (0, import_child_process9.execSync)(`npm list -g ${PACKAGE_NAME} --json`, {
5390
5493
  stdio: ["pipe", "pipe", "pipe"],
5391
5494
  timeout: 1e4
5392
5495
  }).toString();
@@ -5430,7 +5533,7 @@ async function checkForUpdates(currentVersion) {
5430
5533
  }
5431
5534
  function performSyncUpdate(targetVersion) {
5432
5535
  try {
5433
- (0, import_child_process8.execSync)(`npm install -g ${PACKAGE_NAME}@${targetVersion}`, {
5536
+ (0, import_child_process9.execSync)(`npm install -g ${PACKAGE_NAME}@${targetVersion}`, {
5434
5537
  stdio: ["pipe", "pipe", "pipe"],
5435
5538
  timeout: 12e4
5436
5539
  // 2 minute timeout
@@ -5445,12 +5548,12 @@ function performSyncUpdate(targetVersion) {
5445
5548
  }
5446
5549
  async function restartDaemon() {
5447
5550
  try {
5448
- (0, import_child_process8.execSync)("episoda stop", {
5551
+ (0, import_child_process9.execSync)("episoda stop", {
5449
5552
  stdio: ["pipe", "pipe", "pipe"],
5450
5553
  timeout: 1e4
5451
5554
  });
5452
5555
  await new Promise((resolve5) => setTimeout(resolve5, 1e3));
5453
- const child = (0, import_child_process8.spawn)("episoda", ["dev"], {
5556
+ const child = (0, import_child_process9.spawn)("episoda", ["dev"], {
5454
5557
  detached: true,
5455
5558
  stdio: "ignore",
5456
5559
  shell: true
@@ -5506,7 +5609,7 @@ async function updateAndRestartDaemon(targetVersion) {
5506
5609
  }
5507
5610
  function getInstalledVersion() {
5508
5611
  try {
5509
- const output = (0, import_child_process8.execSync)(`npm list -g ${PACKAGE_NAME} --json`, {
5612
+ const output = (0, import_child_process9.execSync)(`npm list -g ${PACKAGE_NAME} --json`, {
5510
5613
  stdio: ["pipe", "pipe", "pipe"],
5511
5614
  timeout: 1e4
5512
5615
  }).toString();
@@ -5517,6 +5620,22 @@ function getInstalledVersion() {
5517
5620
  }
5518
5621
  }
5519
5622
 
5623
+ // src/utils/github-token.ts
5624
+ var INVALID_GITHUB_TOKEN_LITERALS = /* @__PURE__ */ new Set([
5625
+ "${GITHUB_PERSONAL_ACCESS_TOKEN}",
5626
+ "undefined",
5627
+ "null",
5628
+ '""',
5629
+ "''"
5630
+ ]);
5631
+ function sanitizeGithubToken(token) {
5632
+ if (token === void 0) return "";
5633
+ const trimmed = token.trim();
5634
+ if (!trimmed) return "";
5635
+ if (INVALID_GITHUB_TOKEN_LITERALS.has(trimmed)) return "";
5636
+ return trimmed;
5637
+ }
5638
+
5520
5639
  // src/commands/status.ts
5521
5640
  async function statusCommand(options = {}) {
5522
5641
  status.info("Checking CLI status...");
@@ -5648,12 +5767,25 @@ async function statusCommand(options = {}) {
5648
5767
  }
5649
5768
  status.info("");
5650
5769
  status.info("MCP Configuration:");
5651
- if (process.env.GITHUB_PERSONAL_ACCESS_TOKEN) {
5770
+ const envGithubToken = sanitizeGithubToken(process.env.GITHUB_PERSONAL_ACCESS_TOKEN);
5771
+ if (envGithubToken) {
5652
5772
  status.success(" \u2713 GitHub MCP token available (GITHUB_PERSONAL_ACCESS_TOKEN is set)");
5653
5773
  } else {
5654
- status.warning(" \u26A0 GitHub MCP token missing (GITHUB_PERSONAL_ACCESS_TOKEN not set)");
5655
- status.info(' GitHub MCP tools will fail with "Requires authentication".');
5656
- status.info(" Fix: export GITHUB_PERSONAL_ACCESS_TOKEN=$(gh auth token)");
5774
+ let ghAuthAvailable = false;
5775
+ try {
5776
+ const ghToken = (0, import_child_process10.execSync)("gh auth token", { encoding: "utf8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
5777
+ ghAuthAvailable = ghToken.length > 0;
5778
+ } catch {
5779
+ }
5780
+ if (ghAuthAvailable) {
5781
+ status.success(" \u2713 GitHub MCP token available (via gh auth)");
5782
+ status.info(' The portable launcher (scripts/github-mcp-server.sh) will use "gh auth token" automatically.');
5783
+ } else {
5784
+ status.warning(" \u26A0 GitHub MCP token missing");
5785
+ status.info(' GitHub MCP tools will fail with "Requires authentication".');
5786
+ status.info(" Fix: gh auth login # Authenticate GitHub CLI (recommended)");
5787
+ status.info(" Alt: export GITHUB_PERSONAL_ACCESS_TOKEN=ghp_...");
5788
+ }
5657
5789
  }
5658
5790
  if (options.verify) {
5659
5791
  status.info("");
@@ -6524,7 +6656,7 @@ async function listWorktrees(options) {
6524
6656
  }
6525
6657
 
6526
6658
  // src/commands/update.ts
6527
- var import_child_process9 = require("child_process");
6659
+ var import_child_process11 = require("child_process");
6528
6660
  var import_core14 = __toESM(require_dist());
6529
6661
  async function isDaemonRunning2() {
6530
6662
  try {
@@ -6536,7 +6668,7 @@ async function isDaemonRunning2() {
6536
6668
  }
6537
6669
  function stopDaemon2() {
6538
6670
  try {
6539
- (0, import_child_process9.execSync)("episoda stop", { stdio: "pipe" });
6671
+ (0, import_child_process11.execSync)("episoda stop", { stdio: "pipe" });
6540
6672
  return true;
6541
6673
  } catch {
6542
6674
  return false;
@@ -6544,7 +6676,7 @@ function stopDaemon2() {
6544
6676
  }
6545
6677
  function startDaemon2() {
6546
6678
  try {
6547
- const child = (0, import_child_process9.spawn)("episoda", ["dev"], {
6679
+ const child = (0, import_child_process11.spawn)("episoda", ["dev"], {
6548
6680
  detached: true,
6549
6681
  stdio: "ignore",
6550
6682
  shell: process.platform === "win32"
@@ -6557,7 +6689,7 @@ function startDaemon2() {
6557
6689
  }
6558
6690
  function performUpdate(targetVersion) {
6559
6691
  try {
6560
- (0, import_child_process9.execSync)(`npm install -g ${PACKAGE_NAME}@${targetVersion}`, {
6692
+ (0, import_child_process11.execSync)(`npm install -g ${PACKAGE_NAME}@${targetVersion}`, {
6561
6693
  stdio: "pipe",
6562
6694
  timeout: 12e4
6563
6695
  // 2 minute timeout
@@ -6685,7 +6817,7 @@ async function setupCommand(options = {}) {
6685
6817
  }
6686
6818
 
6687
6819
  // src/commands/run.ts
6688
- var import_child_process10 = require("child_process");
6820
+ var import_child_process12 = require("child_process");
6689
6821
  var import_core15 = __toESM(require_dist());
6690
6822
 
6691
6823
  // src/utils/env-cache.ts
@@ -6843,7 +6975,7 @@ async function runCommand(args, options = {}) {
6843
6975
  status.info(`Running: ${command} ${commandArgs.join(" ")}`);
6844
6976
  status.info("");
6845
6977
  }
6846
- const child = (0, import_child_process10.spawn)(command, commandArgs, {
6978
+ const child = (0, import_child_process12.spawn)(command, commandArgs, {
6847
6979
  stdio: "inherit",
6848
6980
  env: mergedEnv,
6849
6981
  shell: process.platform === "win32"
@@ -6894,7 +7026,7 @@ function signalToNumber(signal) {
6894
7026
  }
6895
7027
 
6896
7028
  // src/commands/shell.ts
6897
- var import_child_process11 = require("child_process");
7029
+ var import_child_process13 = require("child_process");
6898
7030
  var import_core16 = __toESM(require_dist());
6899
7031
  async function shellCommand(shell, options = {}) {
6900
7032
  const config = await (0, import_core16.loadConfig)();
@@ -6938,7 +7070,7 @@ async function shellCommand(shell, options = {}) {
6938
7070
  // Set a marker so users know they're in an episoda shell
6939
7071
  EPISODA_SHELL: "1"
6940
7072
  };
6941
- const child = (0, import_child_process11.spawn)(targetShell, [], {
7073
+ const child = (0, import_child_process13.spawn)(targetShell, [], {
6942
7074
  stdio: "inherit",
6943
7075
  env: mergedEnv
6944
7076
  });
@@ -7211,19 +7343,19 @@ async function promptForValue(key) {
7211
7343
  }
7212
7344
 
7213
7345
  // src/commands/migrations.ts
7214
- var import_child_process12 = require("child_process");
7346
+ var import_child_process14 = require("child_process");
7215
7347
  var import_util = require("util");
7216
7348
  var import_path = __toESM(require("path"));
7217
- var exec = (0, import_util.promisify)(import_child_process12.exec);
7349
+ var exec = (0, import_util.promisify)(import_child_process14.exec);
7218
7350
  function getDefaultMigrationDir() {
7219
7351
  return process.env.EPISODA_MIGRATION_DIR || "supabase/migrations";
7220
7352
  }
7221
- async function runGit(command, cwd) {
7353
+ async function runGit2(command, cwd) {
7222
7354
  const { stdout } = await exec(command, { cwd, maxBuffer: 1024 * 1024 * 10 });
7223
7355
  return stdout.trim();
7224
7356
  }
7225
7357
  async function getRepoRoot() {
7226
- return runGit("git rev-parse --show-toplevel", process.cwd());
7358
+ return runGit2("git rev-parse --show-toplevel", process.cwd());
7227
7359
  }
7228
7360
  function parseBaseRef(baseRef) {
7229
7361
  const parts = baseRef.split("/");
@@ -7261,17 +7393,17 @@ async function getMigrationConflicts(options) {
7261
7393
  if (options.fetch) {
7262
7394
  const { remote, ref } = parseBaseRef(options.baseRef);
7263
7395
  if (remote && ref) {
7264
- await runGit(`git fetch ${remote} ${ref}`, repoRoot);
7396
+ await runGit2(`git fetch ${remote} ${ref}`, repoRoot);
7265
7397
  } else if (ref) {
7266
- await runGit(`git fetch origin ${ref}`, repoRoot);
7398
+ await runGit2(`git fetch origin ${ref}`, repoRoot);
7267
7399
  }
7268
7400
  }
7269
- const mainFilesOutput = await runGit(
7401
+ const mainFilesOutput = await runGit2(
7270
7402
  `git ls-tree -r --name-only ${options.baseRef} ${options.migrationDir}`,
7271
7403
  repoRoot
7272
7404
  );
7273
7405
  const mainFiles = mainFilesOutput.split("\n").filter(Boolean);
7274
- const diffOutput = await runGit(
7406
+ const diffOutput = await runGit2(
7275
7407
  `git diff --name-status ${options.baseRef}...HEAD -- ${options.migrationDir}`,
7276
7408
  repoRoot
7277
7409
  );
@@ -7409,15 +7541,15 @@ async function migrationsFixCommand(options) {
7409
7541
  return;
7410
7542
  }
7411
7543
  for (const entry of renamePlan) {
7412
- await runGit(`git mv "${entry.from}" "${entry.to}"`, repoRoot);
7544
+ await runGit2(`git mv "${entry.from}" "${entry.to}"`, repoRoot);
7413
7545
  }
7414
7546
  if (options.commit || options.push) {
7415
7547
  const message = options.message || "Fix migration ordering conflicts";
7416
- await runGit("git add --all", repoRoot);
7417
- await runGit(`git commit -m "${message}"`, repoRoot);
7548
+ await runGit2("git add --all", repoRoot);
7549
+ await runGit2(`git commit -m "${message}"`, repoRoot);
7418
7550
  }
7419
7551
  if (options.push) {
7420
- await runGit("git push", repoRoot);
7552
+ await runGit2("git push", repoRoot);
7421
7553
  }
7422
7554
  status.success("Migration conflicts fixed.");
7423
7555
  }