flightdesk 0.2.4 → 0.3.0

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 (3) hide show
  1. package/main.js +255 -90
  2. package/main.js.map +3 -3
  3. package/package.json +1 -1
package/main.js CHANGED
@@ -3216,6 +3216,8 @@ var FlightDeskAPI = class _FlightDeskAPI {
3216
3216
  status
3217
3217
  branchName
3218
3218
  prUrl
3219
+ sessionViewUrl
3220
+ sessionTeleportId
3219
3221
  }
3220
3222
  }
3221
3223
  `;
@@ -3286,7 +3288,10 @@ var FlightDeskAPI = class _FlightDeskAPI {
3286
3288
  input.projectId = options.projectId;
3287
3289
  }
3288
3290
  if (options?.status) {
3289
- input.status = [options.status];
3291
+ input.status = Array.isArray(options.status) ? options.status : [options.status];
3292
+ }
3293
+ if (options?.limit) {
3294
+ input.limit = options.limit;
3290
3295
  }
3291
3296
  const result = await this.graphql(query, { input });
3292
3297
  return result.userTasks;
@@ -3389,6 +3394,11 @@ var FlightDeskAPI = class _FlightDeskAPI {
3389
3394
  createdAt
3390
3395
  updatedAt
3391
3396
  sshConnectionString
3397
+ processUrls {
3398
+ name
3399
+ url
3400
+ primary
3401
+ }
3392
3402
  }
3393
3403
  }
3394
3404
  `;
@@ -3577,7 +3587,51 @@ var path2 = __toESM(require("node:path"));
3577
3587
  var os2 = __toESM(require("node:os"));
3578
3588
  var fs2 = __toESM(require("node:fs"));
3579
3589
  var readline2 = __toESM(require("node:readline"));
3590
+ var import_node_child_process = require("node:child_process");
3580
3591
  var playwright = null;
3592
+ var PlaywrightBrowserNotInstalledError = class extends Error {
3593
+ constructor(autoInstallError) {
3594
+ const baseMessage = "Playwright browser not installed.";
3595
+ const autoInstallInfo = autoInstallError ? `
3596
+
3597
+ Auto-install failed: ${autoInstallError}
3598
+ ` : "\n\n";
3599
+ super(
3600
+ baseMessage + autoInstallInfo + "Run one of the following commands:\n\n npx playwright install chromium # Just Chromium (recommended)\n npx playwright install # All browsers\n\nThen retry your command."
3601
+ );
3602
+ this.name = "PlaywrightBrowserNotInstalledError";
3603
+ }
3604
+ };
3605
+ function isBrowserNotInstalledError(error) {
3606
+ if (!(error instanceof Error)) return false;
3607
+ return error.message.includes("Executable doesn't exist") || error.message.includes("browserType.launch") || error.message.includes("npx playwright install");
3608
+ }
3609
+ var autoInstallAttempted = false;
3610
+ function tryAutoInstallBrowsers() {
3611
+ if (autoInstallAttempted) {
3612
+ return false;
3613
+ }
3614
+ autoInstallAttempted = true;
3615
+ console.log("");
3616
+ console.log("\u{1F4E6} Playwright browser not found. Installing automatically...");
3617
+ console.log("");
3618
+ try {
3619
+ (0, import_node_child_process.execSync)("npx playwright install chromium", {
3620
+ stdio: "inherit",
3621
+ timeout: 12e4
3622
+ // 2 minute timeout
3623
+ });
3624
+ console.log("");
3625
+ console.log("\u2705 Browser installed successfully!");
3626
+ console.log("");
3627
+ return true;
3628
+ } catch (error) {
3629
+ console.error("");
3630
+ console.error("\u274C Auto-install failed:", error instanceof Error ? error.message : String(error));
3631
+ console.error("");
3632
+ return false;
3633
+ }
3634
+ }
3581
3635
  var USER_DATA_DIR = path2.join(os2.homedir(), ".flightdesk", "chromium-profile");
3582
3636
  var STORAGE_STATE_FILE = path2.join(os2.homedir(), ".flightdesk", "auth-state.json");
3583
3637
  var PersistentBrowser = class {
@@ -3605,9 +3659,29 @@ var PersistentBrowser = class {
3605
3659
  throw new Error("Playwright not available");
3606
3660
  }
3607
3661
  ensureUserDataDir();
3608
- this.browser = await playwright.chromium.launch({
3609
- headless: this.headless
3610
- });
3662
+ try {
3663
+ this.browser = await playwright.chromium.launch({
3664
+ headless: this.headless
3665
+ });
3666
+ } catch (error) {
3667
+ if (isBrowserNotInstalledError(error)) {
3668
+ if (tryAutoInstallBrowsers()) {
3669
+ try {
3670
+ this.browser = await playwright.chromium.launch({
3671
+ headless: this.headless
3672
+ });
3673
+ } catch (retryError) {
3674
+ throw new PlaywrightBrowserNotInstalledError(
3675
+ retryError instanceof Error ? retryError.message : String(retryError)
3676
+ );
3677
+ }
3678
+ } else {
3679
+ throw new PlaywrightBrowserNotInstalledError();
3680
+ }
3681
+ } else {
3682
+ throw error;
3683
+ }
3684
+ }
3611
3685
  const hasAuthState = fs2.existsSync(STORAGE_STATE_FILE);
3612
3686
  const contextOptions = {
3613
3687
  viewport: { width: 1280, height: 720 },
@@ -3702,7 +3776,26 @@ async function launchBrowser(headless) {
3702
3776
  throw new Error("Playwright not available");
3703
3777
  }
3704
3778
  ensureUserDataDir();
3705
- const browser = await playwright.chromium.launch({ headless });
3779
+ let browser;
3780
+ try {
3781
+ browser = await playwright.chromium.launch({ headless });
3782
+ } catch (error) {
3783
+ if (isBrowserNotInstalledError(error)) {
3784
+ if (tryAutoInstallBrowsers()) {
3785
+ try {
3786
+ browser = await playwright.chromium.launch({ headless });
3787
+ } catch (retryError) {
3788
+ throw new PlaywrightBrowserNotInstalledError(
3789
+ retryError instanceof Error ? retryError.message : String(retryError)
3790
+ );
3791
+ }
3792
+ } else {
3793
+ throw new PlaywrightBrowserNotInstalledError();
3794
+ }
3795
+ } else {
3796
+ throw error;
3797
+ }
3798
+ }
3706
3799
  const hasAuthState = fs2.existsSync(STORAGE_STATE_FILE);
3707
3800
  const contextOptions = {
3708
3801
  viewport: { width: 1280, height: 720 },
@@ -3781,44 +3874,63 @@ function waitForEnter() {
3781
3874
  });
3782
3875
  });
3783
3876
  }
3877
+ async function navigateAndPrepare(page, sessionUrl, timeout, debug) {
3878
+ if (debug) console.log(` [DEBUG] Navigating to: ${sessionUrl}`);
3879
+ await page.goto(sessionUrl, { waitUntil: "domcontentloaded", timeout });
3880
+ const dismissBtn = await page.$(`button:has-text("Don't ask me again"), button:has-text("Got it")`);
3881
+ if (dismissBtn) {
3882
+ if (debug) console.log(" [DEBUG] Dismissing notification modal");
3883
+ await dismissBtn.click();
3884
+ await page.waitForTimeout(500);
3885
+ }
3886
+ const matchedElement = await page.waitForSelector('[data-testid="conversation-turn"], .cursor-pointer.bg-bg-300, button:has-text("Create PR")', {
3887
+ timeout: 1e4
3888
+ }).catch(() => null);
3889
+ if (debug) {
3890
+ if (matchedElement) {
3891
+ const tagName = await matchedElement.evaluate((el) => `${el.tagName}.${el.className}`);
3892
+ console.log(` [DEBUG] waitForSelector matched: ${tagName}`);
3893
+ } else {
3894
+ console.log(" [DEBUG] waitForSelector timed out - no expected elements found");
3895
+ }
3896
+ }
3897
+ }
3898
+ async function captureDebugInfo(page) {
3899
+ console.log(` [DEBUG] Current URL after navigation: ${page.url()}`);
3900
+ const screenshotDir = path2.join(os2.homedir(), ".flightdesk", "debug-screenshots");
3901
+ if (!fs2.existsSync(screenshotDir)) fs2.mkdirSync(screenshotDir, { recursive: true });
3902
+ const screenshotPath = path2.join(screenshotDir, `session-${Date.now()}.png`);
3903
+ await page.screenshot({ path: screenshotPath, fullPage: true });
3904
+ console.log(` [DEBUG] Screenshot saved: ${screenshotPath}`);
3905
+ }
3906
+ async function autoCreatePr(page, createPrButton) {
3907
+ console.log(' Clicking "Create PR" button...');
3908
+ await createPrButton.click();
3909
+ await page.waitForTimeout(5e3);
3910
+ return await extractPrUrl(page) ?? void 0;
3911
+ }
3784
3912
  async function scrapeSession(page, sessionUrl, options = {}) {
3785
3913
  const { timeout = 3e4, autoPr = false } = options;
3786
3914
  try {
3787
3915
  page.setDefaultTimeout(timeout);
3788
- await page.goto(sessionUrl, { waitUntil: "domcontentloaded", timeout });
3789
- await page.waitForSelector('[data-testid="conversation-turn"], .code-spinner-animate, button:has-text("Create PR")', {
3790
- timeout: 1e4
3791
- }).catch(() => {
3792
- });
3793
- const result = { status: "active" };
3916
+ const debug = process.env.FLIGHTDESK_DEBUG === "1";
3917
+ await navigateAndPrepare(page, sessionUrl, timeout, debug);
3918
+ if (debug) await captureDebugInfo(page);
3794
3919
  const archiveIndicator = await page.$("text=This session has been archived");
3795
- if (archiveIndicator) {
3796
- return { status: "archived" };
3797
- }
3920
+ if (archiveIndicator) return { status: "archived" };
3798
3921
  const url = page.url();
3799
3922
  if (url.includes("/login") || url.includes("/oauth")) {
3800
- return {
3801
- status: "error",
3802
- error: "Not logged in to Claude. Run: flightdesk auth"
3803
- };
3923
+ return { status: "error", error: "Not logged in to Claude. Run: flightdesk auth" };
3804
3924
  }
3805
- const isActive = await detectActiveSpinner(page);
3806
- result.isActive = isActive;
3925
+ const result = { status: "active" };
3926
+ result.isActive = await detectActiveSpinner(page, debug);
3807
3927
  const branchName = await extractBranchName(page);
3808
- if (branchName) {
3809
- result.branchName = branchName;
3810
- }
3928
+ if (branchName) result.branchName = branchName;
3811
3929
  const createPrButton = await page.$('button:has-text("Create PR"):not([aria-haspopup])');
3812
3930
  const viewPrButton = await page.$('button:has-text("View PR")');
3813
3931
  result.hasPrButton = !!createPrButton && !viewPrButton;
3814
3932
  if (autoPr && createPrButton && !viewPrButton) {
3815
- console.log(' Clicking "Create PR" button...');
3816
- await createPrButton.click();
3817
- await page.waitForTimeout(5e3);
3818
- const prUrl = await extractPrUrl(page);
3819
- if (prUrl) {
3820
- result.prUrl = prUrl;
3821
- }
3933
+ result.prUrl = await autoCreatePr(page, createPrButton);
3822
3934
  }
3823
3935
  return result;
3824
3936
  } catch (error) {
@@ -3911,33 +4023,27 @@ async function extractPrUrl(page) {
3911
4023
  }
3912
4024
  return null;
3913
4025
  }
3914
- async function detectActiveSpinner(page) {
4026
+ async function detectActiveSpinner(page, debug = false) {
3915
4027
  try {
3916
- const spinner = await page.$(".code-spinner-animate");
3917
- if (spinner) {
4028
+ const selectedRowSpinner = await page.$(".cursor-pointer.bg-bg-300 .code-spinner-animate");
4029
+ if (debug) {
4030
+ const globalSpinner = await page.$(".code-spinner-animate");
4031
+ console.log(` [DEBUG] .code-spinner-animate global: ${!!globalSpinner}, in selected row: ${!!selectedRowSpinner}`);
4032
+ }
4033
+ if (selectedRowSpinner) {
4034
+ if (debug) console.log(" [DEBUG] Selected sidebar row has spinner: ACTIVE");
3918
4035
  return true;
3919
4036
  }
3920
- const spinnerByContent = await page.$('span:has-text("\u273D")');
3921
- if (spinnerByContent) {
3922
- const hasAnimation = await spinnerByContent.evaluate((el) => {
3923
- let current = el;
3924
- while (current) {
3925
- const classList = current.classList;
3926
- if (classList?.contains("code-spinner-animate")) {
3927
- return true;
3928
- }
3929
- const style = globalThis.getComputedStyle(current);
3930
- if (style.animationName && style.animationName !== "none") {
3931
- return true;
3932
- }
3933
- current = current.parentElement;
3934
- }
3935
- return false;
3936
- });
3937
- return hasAnimation;
4037
+ const stopButton = await page.$('button:has-text("Stop")');
4038
+ if (debug) console.log(` [DEBUG] Stop button found: ${!!stopButton}`);
4039
+ if (stopButton) {
4040
+ if (debug) console.log(" [DEBUG] Stop button detected: ACTIVE");
4041
+ return true;
3938
4042
  }
4043
+ if (debug) console.log(" [DEBUG] No activity indicators: IDLE");
3939
4044
  return false;
3940
- } catch {
4045
+ } catch (error) {
4046
+ if (debug) console.log(` [DEBUG] Spinner detection error: ${error}`);
3941
4047
  return false;
3942
4048
  }
3943
4049
  }
@@ -3954,30 +4060,48 @@ async function authCommand() {
3954
4060
  console.log(`Profile directory: ${USER_DATA_DIR}
3955
4061
  `);
3956
4062
  console.log("Checking current authentication status...");
3957
- const isAuthenticated = await checkAuth();
3958
- if (isAuthenticated) {
3959
- console.log("\u2705 Already logged in to Claude!");
3960
- console.log("\nThe watch daemon will be able to monitor your sessions.");
3961
- return;
4063
+ try {
4064
+ const isAuthenticated = await checkAuth();
4065
+ if (isAuthenticated) {
4066
+ console.log("\u2705 Already logged in to Claude!");
4067
+ console.log("\nThe watch daemon will be able to monitor your sessions.");
4068
+ return;
4069
+ }
4070
+ } catch (error) {
4071
+ if (error instanceof PlaywrightBrowserNotInstalledError) {
4072
+ console.error("");
4073
+ console.error("\u274C " + error.message);
4074
+ process.exit(1);
4075
+ }
4076
+ throw error;
3962
4077
  }
3963
4078
  console.log("\u274C Not logged in to Claude.\n");
3964
4079
  console.log("Opening browser for login...");
3965
4080
  console.log("Please log in to your Claude account.\n");
3966
- const loginSuccessful = await openForLogin();
3967
- if (loginSuccessful) {
3968
- console.log("\n\u2705 Successfully logged in!");
3969
- console.log("The watch daemon can now monitor your Claude Code sessions.");
3970
- } else {
3971
- console.log("\n\u274C Login was not detected.");
3972
- console.log("Please try again with: flightdesk auth");
4081
+ try {
4082
+ const loginSuccessful = await openForLogin();
4083
+ if (loginSuccessful) {
4084
+ console.log("\n\u2705 Successfully logged in!");
4085
+ console.log("The watch daemon can now monitor your Claude Code sessions.");
4086
+ } else {
4087
+ console.log("\n\u274C Login was not detected.");
4088
+ console.log("Please try again with: flightdesk auth");
4089
+ }
4090
+ } catch (error) {
4091
+ if (error instanceof PlaywrightBrowserNotInstalledError) {
4092
+ console.error("");
4093
+ console.error("\u274C " + error.message);
4094
+ process.exit(1);
4095
+ }
4096
+ throw error;
3973
4097
  }
3974
4098
  }
3975
4099
 
3976
4100
  // apps/cli/src/lib/git.ts
3977
- var import_node_child_process = require("node:child_process");
4101
+ var import_node_child_process2 = require("node:child_process");
3978
4102
  function detectGitRepo() {
3979
4103
  try {
3980
- const remoteUrl = (0, import_node_child_process.execSync)("git remote get-url origin", {
4104
+ const remoteUrl = (0, import_node_child_process2.execSync)("git remote get-url origin", {
3981
4105
  encoding: "utf-8",
3982
4106
  stdio: ["pipe", "pipe", "pipe"]
3983
4107
  }).trim();
@@ -3985,7 +4109,7 @@ function detectGitRepo() {
3985
4109
  if (!repoFullName) {
3986
4110
  return null;
3987
4111
  }
3988
- const branch = (0, import_node_child_process.execSync)("git rev-parse --abbrev-ref HEAD", {
4112
+ const branch = (0, import_node_child_process2.execSync)("git rev-parse --abbrev-ref HEAD", {
3989
4113
  encoding: "utf-8",
3990
4114
  stdio: ["pipe", "pipe", "pipe"]
3991
4115
  }).trim();
@@ -4263,16 +4387,25 @@ async function watchCommand(options) {
4263
4387
  } else {
4264
4388
  browser = new PersistentBrowser(options.headless !== false);
4265
4389
  console.log("Checking Claude authentication...");
4266
- const isAuthenticated = await browser.checkAuth();
4267
- if (!isAuthenticated) {
4268
- console.log("\u26A0\uFE0F Not logged into Claude. Run: flightdesk auth");
4269
- console.log("");
4270
- await browser.close();
4271
- browser = null;
4272
- } else {
4273
- console.log("\u2705 Playwright ready, Claude authenticated");
4274
- console.log(" (Browser session kept alive for monitoring)");
4275
- console.log("");
4390
+ try {
4391
+ const isAuthenticated = await browser.checkAuth();
4392
+ if (!isAuthenticated) {
4393
+ console.log("\u26A0\uFE0F Not logged into Claude. Run: flightdesk auth");
4394
+ console.log("");
4395
+ await browser.close();
4396
+ browser = null;
4397
+ } else {
4398
+ console.log("\u2705 Playwright ready, Claude authenticated");
4399
+ console.log(" (Browser session kept alive for monitoring)");
4400
+ console.log("");
4401
+ }
4402
+ } catch (error) {
4403
+ if (error instanceof PlaywrightBrowserNotInstalledError) {
4404
+ console.error("");
4405
+ console.error("\u274C " + error.message);
4406
+ process.exit(1);
4407
+ }
4408
+ throw error;
4276
4409
  }
4277
4410
  }
4278
4411
  const api = FlightDeskAPI.fromConfig(config, org2);
@@ -4280,7 +4413,11 @@ async function watchCommand(options) {
4280
4413
  const timestamp = (/* @__PURE__ */ new Date()).toLocaleTimeString();
4281
4414
  console.log(`[${timestamp}] Checking tasks...`);
4282
4415
  try {
4283
- const tasks = await api.listTasks();
4416
+ const ACTIVE_STATUSES = ["PENDING", "DISPATCHED", "IN_PROGRESS", "BRANCH_CREATED"];
4417
+ const tasks = await api.listTasks({
4418
+ status: ACTIVE_STATUSES,
4419
+ limit: 200
4420
+ });
4284
4421
  const activeTasks = tasks.filter(
4285
4422
  (t) => ["DISPATCHED", "IN_PROGRESS", "BRANCH_CREATED"].includes(t.status) || t.status === "PENDING" && t.sessionViewUrl
4286
4423
  );
@@ -4528,15 +4665,34 @@ ${task2.description}`);
4528
4665
  Created: ${new Date(task2.createdAt).toLocaleString()}`);
4529
4666
  console.log(`Updated: ${new Date(task2.updatedAt).toLocaleString()}`);
4530
4667
  }
4668
+ function parseSessionId(value) {
4669
+ const urlPrefix = "https://claude.ai/code/";
4670
+ if (value.startsWith(urlPrefix)) {
4671
+ return value.slice(urlPrefix.length);
4672
+ }
4673
+ return value;
4674
+ }
4531
4675
  async function handleUpdate(api, options) {
4532
4676
  const input = {};
4533
4677
  if (options.status) input.status = options.status;
4534
4678
  if (options.branch) input.branchName = options.branch;
4535
4679
  if (options.prUrl) input.prUrl = options.prUrl;
4680
+ if (options.session) {
4681
+ const sessionId = parseSessionId(options.session);
4682
+ input.sessionViewUrl = `https://claude.ai/code/${sessionId}`;
4683
+ input.sessionTeleportId = sessionId;
4684
+ }
4536
4685
  if (Object.keys(input).length === 0) {
4537
- console.error("No updates specified. Use --status, --branch, or --pr-url");
4686
+ console.error("No updates specified. Use --status, --branch, --pr-url, or --session");
4538
4687
  process.exit(1);
4539
4688
  }
4689
+ if (options.branch && !options.status) {
4690
+ const current = await api.getTask(options.taskId);
4691
+ if (current?.status === "DISPATCHED") {
4692
+ input.status = "IN_PROGRESS";
4693
+ console.log(` Auto-transitioning DISPATCHED \u2192 IN_PROGRESS`);
4694
+ }
4695
+ }
4540
4696
  console.log(`Updating task ${options.taskId}...`);
4541
4697
  const task2 = await api.updateTask(options.taskId, input);
4542
4698
  console.log(`
@@ -4544,6 +4700,7 @@ async function handleUpdate(api, options) {
4544
4700
  console.log(` Status: ${task2.status}`);
4545
4701
  if (task2.branchName) console.log(` Branch: ${task2.branchName}`);
4546
4702
  if (task2.prUrl) console.log(` PR: ${task2.prUrl}`);
4703
+ if (task2.sessionViewUrl) console.log(` Session: ${task2.sessionViewUrl}`);
4547
4704
  }
4548
4705
  function getStatusEmoji2(status) {
4549
4706
  switch (status) {
@@ -5254,7 +5411,7 @@ ${projects.length} project(s)`);
5254
5411
  }
5255
5412
 
5256
5413
  // apps/cli/src/commands/preview.ts
5257
- var import_node_child_process2 = require("node:child_process");
5414
+ var import_node_child_process3 = require("node:child_process");
5258
5415
  var path3 = __toESM(require("node:path"));
5259
5416
  var os3 = __toESM(require("node:os"));
5260
5417
  var fs4 = __toESM(require("node:fs"));
@@ -5331,7 +5488,15 @@ async function handleStatus2(api, options) {
5331
5488
  console.log("\u2500".repeat(50));
5332
5489
  console.log(`${emoji} Status: ${instance.status}`);
5333
5490
  console.log(` ID: ${instance.id.substring(0, 8)}`);
5334
- console.log(` Preview URL: ${instance.previewUrl}`);
5491
+ if (instance.processUrls && instance.processUrls.length > 0) {
5492
+ console.log(" URLs:");
5493
+ for (const pu of instance.processUrls) {
5494
+ const label = pu.primary ? `${pu.name} (primary)` : pu.name;
5495
+ console.log(` ${label}: ${pu.url}`);
5496
+ }
5497
+ } else {
5498
+ console.log(` Preview URL: ${instance.previewUrl}`);
5499
+ }
5335
5500
  console.log(` SSH: ${instance.sshConnectionString}`);
5336
5501
  console.log(` Container: ${instance.containerId.substring(0, 12)}`);
5337
5502
  if (instance.lastActivityAt) {
@@ -5360,7 +5525,7 @@ async function handleLogs(api, options) {
5360
5525
  `);
5361
5526
  validateSSHParams(instance);
5362
5527
  const sshCommand = `docker logs -f ${instance.containerId}`;
5363
- const ssh = (0, import_node_child_process2.spawn)("ssh", [
5528
+ const ssh = (0, import_node_child_process3.spawn)("ssh", [
5364
5529
  "-o",
5365
5530
  "StrictHostKeyChecking=no",
5366
5531
  "-o",
@@ -5400,13 +5565,13 @@ async function handleMount(api, options) {
5400
5565
  await new Promise((resolve) => setTimeout(resolve, 3e3));
5401
5566
  }
5402
5567
  try {
5403
- (0, import_node_child_process2.execSync)("which sshfs", { stdio: "ignore" });
5568
+ (0, import_node_child_process3.execSync)("which sshfs", { stdio: "ignore" });
5404
5569
  } catch {
5405
5570
  console.error("\u274C sshfs is not installed.");
5406
5571
  console.error("");
5407
5572
  console.error("Install it with:");
5408
5573
  if (process.platform === "darwin") {
5409
- console.error(" brew install macfuse sshfs");
5574
+ console.error(" brew install macfuse gromgit/fuse/sshfs-mac");
5410
5575
  } else if (process.platform === "linux") {
5411
5576
  console.error(" sudo apt install sshfs");
5412
5577
  } else {
@@ -5421,7 +5586,7 @@ async function handleMount(api, options) {
5421
5586
  fs4.mkdirSync(mountDir, { recursive: true });
5422
5587
  }
5423
5588
  try {
5424
- const mounted = (0, import_node_child_process2.execSync)("mount", { encoding: "utf8" });
5589
+ const mounted = (0, import_node_child_process3.execSync)("mount", { encoding: "utf8" });
5425
5590
  if (mounted.includes(mountDir)) {
5426
5591
  console.log(`\u{1F4C1} Already mounted at ${mountDir}`);
5427
5592
  return;
@@ -5447,7 +5612,7 @@ async function handleMount(api, options) {
5447
5612
  mountDir
5448
5613
  ];
5449
5614
  try {
5450
- const result = (0, import_node_child_process2.spawnSync)("sshfs", sshfsArgs, { stdio: "inherit" });
5615
+ const result = (0, import_node_child_process3.spawnSync)("sshfs", sshfsArgs, { stdio: "inherit" });
5451
5616
  if (result.status !== 0) {
5452
5617
  throw new Error(`sshfs exited with code ${result.status}`);
5453
5618
  }
@@ -5481,9 +5646,9 @@ async function handleUnmount(_api, options) {
5481
5646
  try {
5482
5647
  let result;
5483
5648
  if (process.platform === "darwin") {
5484
- result = (0, import_node_child_process2.spawnSync)("umount", [mountDir], { stdio: "inherit" });
5649
+ result = (0, import_node_child_process3.spawnSync)("umount", [mountDir], { stdio: "inherit" });
5485
5650
  } else {
5486
- result = (0, import_node_child_process2.spawnSync)("fusermount", ["-u", mountDir], { stdio: "inherit" });
5651
+ result = (0, import_node_child_process3.spawnSync)("fusermount", ["-u", mountDir], { stdio: "inherit" });
5487
5652
  }
5488
5653
  if (result.status !== 0) {
5489
5654
  throw new Error(`Unmount exited with code ${result.status}`);
@@ -5523,7 +5688,7 @@ async function handleTeardown(api, options) {
5523
5688
 
5524
5689
  // apps/cli/src/main.ts
5525
5690
  var program2 = new Command();
5526
- program2.name("flightdesk").description("FlightDesk CLI - AI task management for Claude Code sessions").version("0.2.4").option("--dev", "Use local development API (localhost:3000)").option("--api <url>", "Use custom API URL");
5691
+ program2.name("flightdesk").description("FlightDesk CLI - AI task management for Claude Code sessions").version("0.3.0").option("--dev", "Use local development API (localhost:3000)").option("--api <url>", "Use custom API URL");
5527
5692
  program2.hook("preAction", () => {
5528
5693
  const opts = program2.opts();
5529
5694
  if (opts.api) {
@@ -5544,7 +5709,7 @@ var task = program2.command("task").description("Task management commands");
5544
5709
  task.command("create").description("Create a new task").requiredOption("-p, --project <id>", "Project ID").requiredOption("-t, --title <title>", "Task title").option("-d, --description <description>", "Task description").action((options) => taskCommand("create", options));
5545
5710
  task.command("list").description("List tasks").option("-p, --project <id>", "Filter by project ID").option("--status <status>", "Filter by status").action((options) => taskCommand("list", options));
5546
5711
  task.command("status <task-id>").description("Get task status").action((taskId) => taskCommand("status", { taskId }));
5547
- task.command("update <task-id>").description("Update task").option("-s, --status <status>", "New status").option("--branch <branch>", "Branch name").option("--pr-url <url>", "Pull request URL").action((taskId, options) => taskCommand("update", { taskId, ...options }));
5712
+ task.command("update <task-id>").description("Update task").option("-s, --status <status>", "New status").option("--branch <branch>", "Branch name").option("--pr-url <url>", "Pull request URL").option("--session <session>", "Claude Code session (URL or session ID)").action((taskId, options) => taskCommand("update", { taskId, ...options }));
5548
5713
  program2.command("watch").description("Start the Playwright daemon to monitor Claude Code sessions").option("--interval <minutes>", "Check interval in minutes", "5").option("--once", "Run once and exit").option("--auto-pr", 'Automatically click "Create PR" button when found').option("--no-headless", "Run browser in visible mode (for debugging)").action(watchCommand);
5549
5714
  program2.command("import").description("Scan Claude.ai for existing sessions and import them as tasks").option("--dry-run", "Show what would be imported without making changes").option("--limit <n>", "Maximum number of sessions to scan", "50").option("-p, --project <id>", "Import all sessions into a specific project").option("--no-headless", "Run browser in visible mode (for debugging)").option("-v, --verbose", "Show detailed progress").action(importCommand);
5550
5715
  program2.command("status").description("Show status of all active tasks").option("-p, --project <id>", "Filter by project").action(statusCommand);