codeam-cli 2.16.1 → 2.17.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/CHANGELOG.md +21 -0
  2. package/dist/index.js +215 -27
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,27 @@ All notable changes to `codeam-cli` are documented here.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [2.16.2] — 2026-05-22
8
+
9
+ ### Chore
10
+
11
+ - **meta:** Expand issue templates with question + documentation forms
12
+
13
+ ### Documentation
14
+
15
+ - **shared:** Correct prod URL in api-url history comment
16
+
17
+ ### Fixed
18
+
19
+ - **cli:** Codeam link — auto-install, multi-probe creds, file-watcher login
20
+ - **cli:** Windows EPERM crash from chokidar watching user-profile junctions (#43)
21
+
22
+ ## [2.16.1] — 2026-05-21
23
+
24
+ ### Fixed
25
+
26
+ - **cli:** Codeam link — auto-install, multi-probe creds, file-watcher login (#42)
27
+
7
28
  ## [2.16.0] — 2026-05-21
8
29
 
9
30
  ### Added
package/dist/index.js CHANGED
@@ -424,7 +424,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
424
424
  // package.json
425
425
  var package_default = {
426
426
  name: "codeam-cli",
427
- version: "2.16.1",
427
+ version: "2.17.0",
428
428
  description: "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device \u2014 async. The terminal companion for CodeAgent Mobile.",
429
429
  type: "commonjs",
430
430
  main: "dist/index.js",
@@ -6262,6 +6262,7 @@ var HistoryService = class _HistoryService {
6262
6262
 
6263
6263
  // src/services/file-watcher.service.ts
6264
6264
  var import_child_process7 = require("child_process");
6265
+ var os13 = __toESM(require("os"));
6265
6266
  var path15 = __toESM(require("path"));
6266
6267
 
6267
6268
  // src/services/file-watcher/diff-parser.ts
@@ -6397,6 +6398,44 @@ var API_BASE5 = process.env.CODEAM_API_URL ?? DEFAULT_API_BASE_URL;
6397
6398
  var DEBOUNCE_MS = 250;
6398
6399
  var MAX_RETRIES = 2;
6399
6400
  var RETRY_BACKOFF_MS = 300;
6401
+ var WINDOWS_LEGACY_JUNCTIONS = [
6402
+ /[\\/]Application Data([\\/]|$)/i,
6403
+ /[\\/]Cookies([\\/]|$)/i,
6404
+ /[\\/]Local Settings([\\/]|$)/i,
6405
+ /[\\/]My Documents([\\/]|$)/i,
6406
+ /[\\/]NetHood([\\/]|$)/i,
6407
+ /[\\/]PrintHood([\\/]|$)/i,
6408
+ /[\\/]Recent([\\/]|$)/i,
6409
+ /[\\/]SendTo([\\/]|$)/i,
6410
+ /[\\/]Start Menu([\\/]|$)/i,
6411
+ /[\\/]Templates([\\/]|$)/i
6412
+ ];
6413
+ function isUnsafeWindowsWatchRoot(dir, homedir12) {
6414
+ const norm = (p2) => p2.replace(/\//g, "\\").replace(/\\+$/, "").toLowerCase();
6415
+ const cwd = norm(dir);
6416
+ const home = norm(homedir12);
6417
+ if (cwd === home) return true;
6418
+ if (/^[a-z]:$/.test(cwd)) return true;
6419
+ const sysRoots = [
6420
+ "c:\\windows",
6421
+ "c:\\program files",
6422
+ "c:\\program files (x86)",
6423
+ "c:\\programdata"
6424
+ ];
6425
+ for (const root of sysRoots) {
6426
+ if (cwd === root || cwd.startsWith(root + "\\")) return true;
6427
+ }
6428
+ return false;
6429
+ }
6430
+ var _chokidarSeam = {
6431
+ load: () => {
6432
+ try {
6433
+ return require("chokidar");
6434
+ } catch {
6435
+ return null;
6436
+ }
6437
+ }
6438
+ };
6400
6439
  var FileWatcherService = class {
6401
6440
  constructor(opts) {
6402
6441
  this.opts = opts;
@@ -6419,14 +6458,19 @@ var FileWatcherService = class {
6419
6458
  if (this.stopped) {
6420
6459
  throw new Error("FileWatcherService has already been stopped \u2014 re-instantiate to restart.");
6421
6460
  }
6422
- let chokidar2;
6423
- try {
6424
- chokidar2 = require("chokidar");
6425
- } catch (err) {
6461
+ const isWin = process.platform === "win32";
6462
+ if (isWin && isUnsafeWindowsWatchRoot(this.opts.workingDir, os13.homedir())) {
6463
+ log.warn(
6464
+ "fileWatcher",
6465
+ `refusing to watch ${this.opts.workingDir} \u2014 looks like a Windows user-profile or system path. Run codeam from your project folder to enable file change emission.`
6466
+ );
6467
+ return;
6468
+ }
6469
+ const chokidar2 = _chokidarSeam.load();
6470
+ if (!chokidar2) {
6426
6471
  log.warn(
6427
6472
  "fileWatcher",
6428
- `chokidar unavailable \u2014 file change emission disabled`,
6429
- err
6473
+ `chokidar unavailable \u2014 file change emission disabled`
6430
6474
  );
6431
6475
  return;
6432
6476
  }
@@ -6444,11 +6488,22 @@ var FileWatcherService = class {
6444
6488
  /\.parcel-cache/,
6445
6489
  // Build outputs that aren't a typical "dist" target
6446
6490
  /target\//,
6447
- /__pycache__/
6491
+ /__pycache__/,
6492
+ // Windows-only: skip legacy user-profile junctions whose ACLs
6493
+ // throw EPERM during chokidar's recursive traversal.
6494
+ ...isWin ? WINDOWS_LEGACY_JUNCTIONS : []
6448
6495
  ],
6449
6496
  ignoreInitial: true,
6450
6497
  // we only care about post-start changes
6451
6498
  persistent: true,
6499
+ // Windows-only safety net: don't follow reparse points, and let
6500
+ // chokidar swallow EPERM/EACCES on unreadable paths instead of
6501
+ // bubbling them up as fatal errors. Both are no-ops on macOS
6502
+ // (fsevents traversal doesn't fail on permission errors).
6503
+ ...isWin ? {
6504
+ followSymlinks: false,
6505
+ ignorePermissionErrors: true
6506
+ } : {},
6452
6507
  awaitWriteFinish: {
6453
6508
  // Coalesces rapid sequential writes (npm install spam, build
6454
6509
  // tools emitting bursts). Lower than chokidar's default so
@@ -6461,6 +6516,13 @@ var FileWatcherService = class {
6461
6516
  watcher.on("add", (filePath) => this.schedule(filePath, "add"));
6462
6517
  watcher.on("change", (filePath) => this.schedule(filePath, "change"));
6463
6518
  watcher.on("unlink", (filePath) => this.schedule(filePath, "unlink"));
6519
+ watcher.on("error", (err) => {
6520
+ const code = err?.code ?? "unknown";
6521
+ log.warn(
6522
+ "fileWatcher",
6523
+ `chokidar error (code=${code}) \u2014 watcher continues: ${err}`
6524
+ );
6525
+ });
6464
6526
  this.watcher = watcher;
6465
6527
  log.info(
6466
6528
  "fileWatcher",
@@ -7199,7 +7261,7 @@ function buildKeepAlive(ctx) {
7199
7261
 
7200
7262
  // src/commands/start/handlers.ts
7201
7263
  var fs14 = __toESM(require("fs"));
7202
- var os13 = __toESM(require("os"));
7264
+ var os14 = __toESM(require("os"));
7203
7265
  var path19 = __toESM(require("path"));
7204
7266
  var import_crypto3 = require("crypto");
7205
7267
  var import_child_process11 = require("child_process");
@@ -8007,7 +8069,7 @@ function closeTerminal(sessionId) {
8007
8069
  function saveFilesTemp(files) {
8008
8070
  return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
8009
8071
  const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
8010
- const tmpPath = path19.join(os13.tmpdir(), `codeam-${(0, import_crypto3.randomUUID)()}-${safeName}`);
8072
+ const tmpPath = path19.join(os14.tmpdir(), `codeam-${(0, import_crypto3.randomUUID)()}-${safeName}`);
8011
8073
  fs14.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
8012
8074
  return tmpPath;
8013
8075
  });
@@ -8530,8 +8592,9 @@ async function promptForAgent(initialValue) {
8530
8592
  // src/commands/pair.ts
8531
8593
  async function pair(args2 = []) {
8532
8594
  const config = loadCliConfig();
8595
+ const dryRun = args2.includes("--dry-run");
8533
8596
  const flagAgent = parseAgentFlag(args2);
8534
- const agentId = flagAgent ?? await promptForAgent(config.preferredAgent ?? "claude");
8597
+ const agentId = dryRun ? flagAgent ?? config.preferredAgent ?? "claude" : flagAgent ?? await promptForAgent(config.preferredAgent ?? "claude");
8535
8598
  showIntro();
8536
8599
  const pluginId = (0, import_crypto4.randomUUID)();
8537
8600
  const spin = dist_exports.spinner();
@@ -8543,6 +8606,20 @@ async function pair(args2 = []) {
8543
8606
  process.exit(1);
8544
8607
  }
8545
8608
  spin.stop("Got pairing code");
8609
+ if (dryRun) {
8610
+ const codeOk = typeof result.code === "string" && result.code.trim().length > 0;
8611
+ const expiresOk = typeof result.expiresAt === "number" && result.expiresAt > 0;
8612
+ if (!codeOk || !expiresOk) {
8613
+ showError(
8614
+ `Pair dry-run: backend response shape unexpected (codeType=${typeof result.code}, codeEmpty=${!codeOk}, expiresType=${typeof result.expiresAt}, expiresPositive=${expiresOk}).`
8615
+ );
8616
+ process.exit(1);
8617
+ }
8618
+ showSuccess(
8619
+ `Pair dry-run OK \u2014 backend reachable, response shape valid (codeLength=${result.code.length}, expiresAt=${result.expiresAt}, agent=${agentId}).`
8620
+ );
8621
+ process.exit(0);
8622
+ }
8546
8623
  showPairingCode(result.code);
8547
8624
  console.log(import_picocolors3.default.dim(" Scan the QR code or enter the code in CodeAgent Mobile."));
8548
8625
  console.log("");
@@ -8596,7 +8673,7 @@ async function pair(args2 = []) {
8596
8673
 
8597
8674
  // src/commands/pair-auto.ts
8598
8675
  var fs15 = __toESM(require("fs"));
8599
- var os14 = __toESM(require("os"));
8676
+ var os15 = __toESM(require("os"));
8600
8677
  var import_crypto5 = require("crypto");
8601
8678
  var API_BASE7 = process.env.CODEAM_API_URL ?? DEFAULT_API_BASE_URL;
8602
8679
  function fail(msg) {
@@ -8636,7 +8713,7 @@ async function claim(token, pluginId) {
8636
8713
  pluginId,
8637
8714
  ideName: "codeam-cli (codespace)",
8638
8715
  ideVersion: process.env.npm_package_version ?? "unknown",
8639
- hostname: os14.hostname(),
8716
+ hostname: os15.hostname(),
8640
8717
  codespaceName: process.env.CODESPACE_NAME ?? "",
8641
8718
  // Current git branch of the codespace's working directory, so the
8642
8719
  // backend can populate `PairedSession.branch` for the codespace pair.
@@ -10741,7 +10818,7 @@ var import_picocolors11 = __toESM(require("picocolors"));
10741
10818
  // src/agents/claude/local-token.ts
10742
10819
  var import_node_child_process2 = require("child_process");
10743
10820
  var fs16 = __toESM(require("fs"));
10744
- var os15 = __toESM(require("os"));
10821
+ var os16 = __toESM(require("os"));
10745
10822
  var path24 = __toESM(require("path"));
10746
10823
  var import_node_util3 = require("util");
10747
10824
  var execFileP7 = (0, import_node_util3.promisify)(import_node_child_process2.execFile);
@@ -10752,7 +10829,7 @@ var KEYCHAIN_SERVICE_NAMES = [
10752
10829
  "Anthropic Claude"
10753
10830
  ];
10754
10831
  function claudeCredentialsPaths() {
10755
- const home = os15.homedir();
10832
+ const home = os16.homedir();
10756
10833
  return [
10757
10834
  path24.join(home, ".claude", ".credentials.json"),
10758
10835
  path24.join(home, ".config", "claude", ".credentials.json")
@@ -10787,10 +10864,10 @@ async function extractLocalClaudeToken() {
10787
10864
 
10788
10865
  // src/agents/codex/local-token.ts
10789
10866
  var fs17 = __toESM(require("fs"));
10790
- var os16 = __toESM(require("os"));
10867
+ var os17 = __toESM(require("os"));
10791
10868
  var path25 = __toESM(require("path"));
10792
10869
  function codexCredentialsPath() {
10793
- return path25.join(os16.homedir(), ".codex", "auth.json");
10870
+ return path25.join(os17.homedir(), ".codex", "auth.json");
10794
10871
  }
10795
10872
  async function extractLocalCodexToken() {
10796
10873
  const file = codexCredentialsPath();
@@ -10858,11 +10935,12 @@ function parseLinkArgs(args2) {
10858
10935
  );
10859
10936
  }
10860
10937
  const reuseExisting = args2.includes("--reuse-existing");
10938
+ const dryRun = args2.includes("--dry-run");
10861
10939
  const apiKeyArg = args2.find((a) => a.startsWith("--api-key="));
10862
10940
  const apiKey = apiKeyArg ? apiKeyArg.slice("--api-key=".length) : null;
10863
10941
  const tokenFileArg = args2.find((a) => a.startsWith("--token-file="));
10864
10942
  const tokenFile = tokenFileArg ? tokenFileArg.slice("--token-file=".length) : null;
10865
- return { agent: normalised, reuseExisting, apiKey, tokenFile };
10943
+ return { agent: normalised, reuseExisting, apiKey, tokenFile, dryRun };
10866
10944
  }
10867
10945
  async function link(args2 = []) {
10868
10946
  const parsed = parseLinkArgs(args2);
@@ -10872,6 +10950,10 @@ async function link(args2 = []) {
10872
10950
  import_picocolors11.default.bold(` Link ${meta.displayName}`) + import_picocolors11.default.dim(` \xB7 ${meta.vendor}`)
10873
10951
  );
10874
10952
  console.log("");
10953
+ if (parsed.dryRun) {
10954
+ await linkDryRunPreflight(meta);
10955
+ return;
10956
+ }
10875
10957
  const pluginId = (0, import_node_crypto.randomUUID)();
10876
10958
  const spin = dist_exports.spinner();
10877
10959
  spin.start("Requesting pairing code...");
@@ -10981,10 +11063,20 @@ async function link(args2 = []) {
10981
11063
  await uploadAndSucceed(meta, paired, pluginId, captured);
10982
11064
  }
10983
11065
  async function captureFreshCredentials(meta) {
11066
+ const isWin = process.platform === "win32";
10984
11067
  const watcher = import_chokidar.default.watch(meta.watchPaths(), {
10985
11068
  persistent: true,
10986
11069
  ignoreInitial: false,
10987
- awaitWriteFinish: { stabilityThreshold: 500, pollInterval: 100 }
11070
+ awaitWriteFinish: { stabilityThreshold: 500, pollInterval: 100 },
11071
+ // Windows-only: when a credential file's ancestor is missing,
11072
+ // chokidar walks up to the closest existing parent and starts
11073
+ // traversing it. On a default Windows shell that ancestor is
11074
+ // `C:\Users\<u>`, which contains legacy junctions whose ACL makes
11075
+ // `fs.watch` throw EPERM (#43). These flags are no-ops on macOS,
11076
+ // where the user has read access to their entire home.
11077
+ ...isWin ? { followSymlinks: false, ignorePermissionErrors: true } : {}
11078
+ });
11079
+ watcher.on("error", () => {
10988
11080
  });
10989
11081
  let child = null;
10990
11082
  let keychainPoll = null;
@@ -11081,11 +11173,42 @@ async function uploadAndSucceed(meta, paired, pluginId, token) {
11081
11173
  );
11082
11174
  console.log("");
11083
11175
  }
11176
+ async function linkDryRunPreflight(meta) {
11177
+ const spin = dist_exports.spinner();
11178
+ spin.start(`Probing ${meta.publicId} link endpoint...`);
11179
+ const result = await postLinkCredential({
11180
+ agentId: meta.publicId,
11181
+ sessionId: "dryrun-session",
11182
+ pluginId: "dryrun-plugin",
11183
+ pluginAuthToken: "dryrun-token",
11184
+ method: "oauth",
11185
+ credential: "dryrun-credential"
11186
+ });
11187
+ if (result.ok) {
11188
+ spin.stop("Unexpected 2xx");
11189
+ showError(
11190
+ "Link dry-run: backend accepted a stub credential (2xx). PluginAuthGuard appears to be disabled \u2014 investigate api-v2."
11191
+ );
11192
+ process.exit(1);
11193
+ }
11194
+ if (result.status === 401) {
11195
+ spin.stop("Endpoint OK");
11196
+ showSuccess(
11197
+ `Link dry-run OK \u2014 /api/plugin/agents/${meta.publicId}/link reachable and auth-gated (401 as expected).`
11198
+ );
11199
+ process.exit(0);
11200
+ }
11201
+ spin.stop("Failed");
11202
+ showError(
11203
+ `Link dry-run: unexpected response from /api/plugin/agents/${meta.publicId}/link (status=${result.status}, message=${result.message}). Expected 401.`
11204
+ );
11205
+ process.exit(1);
11206
+ }
11084
11207
 
11085
11208
  // src/commands/version.ts
11086
11209
  var import_picocolors12 = __toESM(require("picocolors"));
11087
11210
  function version() {
11088
- const v = true ? "2.16.1" : "unknown";
11211
+ const v = true ? "2.17.0" : "unknown";
11089
11212
  console.log(`${import_picocolors12.default.bold("codeam-cli")} ${import_picocolors12.default.cyan(v)}`);
11090
11213
  }
11091
11214
 
@@ -11126,18 +11249,80 @@ function help() {
11126
11249
  process.stdout.write(lines.join("\n") + "\n");
11127
11250
  }
11128
11251
 
11252
+ // src/commands/subcommand-help.ts
11253
+ var import_picocolors14 = __toESM(require("picocolors"));
11254
+ var HELPS = {
11255
+ pair: () => print([
11256
+ ` ${import_picocolors14.default.bold("codeam pair")} ${import_picocolors14.default.dim("\u2014 pair a mobile device with this CLI")}`,
11257
+ "",
11258
+ ` ${import_picocolors14.default.cyan("codeam pair")} ${import_picocolors14.default.dim("interactive pairing (prompts for the agent)")}`,
11259
+ ` ${import_picocolors14.default.cyan("codeam pair --agent <id>")} ${import_picocolors14.default.dim("pair non-interactively (agent: claude | codex)")}`,
11260
+ ` ${import_picocolors14.default.cyan("codeam pair --dry-run")} ${import_picocolors14.default.dim("request a pairing code, validate the response, exit")}`
11261
+ ]),
11262
+ "pair-auto": () => print([
11263
+ ` ${import_picocolors14.default.bold("codeam pair-auto")} ${import_picocolors14.default.dim("\u2014 non-interactive variant of pair for scripted setups")}`,
11264
+ "",
11265
+ ` ${import_picocolors14.default.cyan("codeam pair-auto --agent <id>")} ${import_picocolors14.default.dim("pair using the supplied agent id; exit on success or timeout")}`
11266
+ ]),
11267
+ link: () => print([
11268
+ ` ${import_picocolors14.default.bold("codeam link <agent>")} ${import_picocolors14.default.dim("\u2014 upload a local agent token (Claude or Codex) to your vault")}`,
11269
+ "",
11270
+ ` ${import_picocolors14.default.cyan("codeam link claude")}`,
11271
+ ` ${import_picocolors14.default.cyan("codeam link codex")}`,
11272
+ "",
11273
+ ` ${import_picocolors14.default.white("--api-key=<key>")} ${import_picocolors14.default.dim("paste an API key directly (skip the local auth flow)")}`,
11274
+ ` ${import_picocolors14.default.white("--reuse-existing")} ${import_picocolors14.default.dim("upload existing creds without re-launching the agent login")}`,
11275
+ ` ${import_picocolors14.default.white("--token-file=<path>")} ${import_picocolors14.default.dim("manual credential blob path for unusual vendor locations")}`,
11276
+ ` ${import_picocolors14.default.white("--dry-run")} ${import_picocolors14.default.dim("probe the /api/plugin/agents/<agent>/link endpoint and exit")}`
11277
+ ]),
11278
+ sessions: () => print([
11279
+ ` ${import_picocolors14.default.bold("codeam sessions")} ${import_picocolors14.default.dim("\u2014 list, switch, or delete paired mobile sessions")}`,
11280
+ "",
11281
+ ` ${import_picocolors14.default.cyan("codeam sessions")} ${import_picocolors14.default.dim("list all paired sessions on this machine")}`,
11282
+ ` ${import_picocolors14.default.cyan("codeam sessions switch")} ${import_picocolors14.default.dim("interactively switch the active session")}`,
11283
+ ` ${import_picocolors14.default.cyan("codeam sessions delete <id>")} ${import_picocolors14.default.dim("remove a specific paired session")}`
11284
+ ]),
11285
+ deploy: () => print([
11286
+ ` ${import_picocolors14.default.bold("codeam deploy")} ${import_picocolors14.default.dim("\u2014 provision a cloud workspace (GitHub Codespaces) and pair it")}`,
11287
+ "",
11288
+ ` ${import_picocolors14.default.cyan("codeam deploy")} ${import_picocolors14.default.dim("start a new deploy (prompts for repo + agent)")}`,
11289
+ ` ${import_picocolors14.default.cyan("codeam deploy ls | list")} ${import_picocolors14.default.dim("list deployed cloud workspaces")}`,
11290
+ ` ${import_picocolors14.default.cyan("codeam deploy stop | remove")} ${import_picocolors14.default.dim("pick a workspace and stop its codeam-pair session")}`
11291
+ ]),
11292
+ status: () => print([
11293
+ ` ${import_picocolors14.default.bold("codeam status")} ${import_picocolors14.default.dim("\u2014 show the active session, agent, and connection info")}`
11294
+ ]),
11295
+ logout: () => print([
11296
+ ` ${import_picocolors14.default.bold("codeam logout")} ${import_picocolors14.default.dim("\u2014 remove every paired session from this machine")}`
11297
+ ])
11298
+ };
11299
+ function print(lines) {
11300
+ process.stdout.write(["", ...lines, ""].join("\n") + "\n");
11301
+ }
11302
+ function isHelpFlag(arg) {
11303
+ return arg === "--help" || arg === "-h";
11304
+ }
11305
+ function tryShowSubcommandHelp(cmd, args2) {
11306
+ if (!isHelpFlag(args2[0])) return false;
11307
+ const renderer = HELPS[cmd];
11308
+ if (!renderer) return false;
11309
+ renderer();
11310
+ return true;
11311
+ }
11312
+ var _subcommandHelpKeys = Object.keys(HELPS);
11313
+
11129
11314
  // src/lib/updateNotifier.ts
11130
11315
  var fs19 = __toESM(require("fs"));
11131
- var os17 = __toESM(require("os"));
11316
+ var os18 = __toESM(require("os"));
11132
11317
  var path27 = __toESM(require("path"));
11133
11318
  var https7 = __toESM(require("https"));
11134
- var import_picocolors14 = __toESM(require("picocolors"));
11319
+ var import_picocolors15 = __toESM(require("picocolors"));
11135
11320
  var PKG_NAME = "codeam-cli";
11136
11321
  var REGISTRY_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
11137
11322
  var TTL_MS = 24 * 60 * 60 * 1e3;
11138
11323
  var REQUEST_TIMEOUT_MS = 1500;
11139
11324
  function cachePath() {
11140
- const dir = path27.join(os17.homedir(), ".codeam");
11325
+ const dir = path27.join(os18.homedir(), ".codeam");
11141
11326
  return path27.join(dir, "update-check.json");
11142
11327
  }
11143
11328
  function readCache() {
@@ -11210,11 +11395,11 @@ function fetchLatest() {
11210
11395
  }
11211
11396
  function notifyIfStale(currentVersion, latest) {
11212
11397
  if (compareSemver(latest, currentVersion) <= 0) return;
11213
- const arrow = import_picocolors14.default.dim("\u2192");
11214
- const cmd = import_picocolors14.default.cyan("npm install -g codeam-cli");
11398
+ const arrow = import_picocolors15.default.dim("\u2192");
11399
+ const cmd = import_picocolors15.default.cyan("npm install -g codeam-cli");
11215
11400
  const lines = [
11216
11401
  "",
11217
- ` ${import_picocolors14.default.yellow("\u25CF")} ${import_picocolors14.default.bold("Update available")} ${import_picocolors14.default.dim(currentVersion)} ${arrow} ${import_picocolors14.default.green(latest)}`,
11402
+ ` ${import_picocolors15.default.yellow("\u25CF")} ${import_picocolors15.default.bold("Update available")} ${import_picocolors15.default.dim(currentVersion)} ${arrow} ${import_picocolors15.default.green(latest)}`,
11218
11403
  ` Run ${cmd} to upgrade.`,
11219
11404
  ""
11220
11405
  ];
@@ -11225,7 +11410,7 @@ function checkForUpdates() {
11225
11410
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
11226
11411
  if (process.env.CI) return;
11227
11412
  if (!process.stdout.isTTY) return;
11228
- const current = true ? "2.16.1" : null;
11413
+ const current = true ? "2.17.0" : null;
11229
11414
  if (!current) return;
11230
11415
  const cache = readCache();
11231
11416
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -11244,6 +11429,9 @@ var [, , command, ...args] = process.argv;
11244
11429
  async function main() {
11245
11430
  const isMetaCommand = command === "--version" || command === "-v" || command === "version" || command === "--help" || command === "-h" || command === "help";
11246
11431
  if (!isMetaCommand) checkForUpdates();
11432
+ if (typeof command === "string" && tryShowSubcommandHelp(command, args)) {
11433
+ return;
11434
+ }
11247
11435
  switch (command) {
11248
11436
  case "--version":
11249
11437
  case "-v":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.16.1",
3
+ "version": "2.17.0",
4
4
  "description": "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device — async. The terminal companion for CodeAgent Mobile.",
5
5
  "type": "commonjs",
6
6
  "main": "dist/index.js",