codeam-cli 2.20.1 → 2.20.2

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 +10 -0
  2. package/dist/index.js +223 -174
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,16 @@ 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.20.1] — 2026-05-25
8
+
9
+ ### Documentation
10
+
11
+ - Document CODEAM_TEST_MODE + CODEAM_API_URL env vars in README (#177)
12
+
13
+ ### Fixed
14
+
15
+ - **cli:** Finalize turn on content-stable + ready-prompt, not just PTY-idle (#178)
16
+
7
17
  ## [2.20.0] — 2026-05-24
8
18
 
9
19
  ### Added
package/dist/index.js CHANGED
@@ -441,7 +441,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
441
441
  // package.json
442
442
  var package_default = {
443
443
  name: "codeam-cli",
444
- version: "2.20.1",
444
+ version: "2.20.2",
445
445
  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.",
446
446
  type: "commonjs",
447
447
  main: "dist/index.js",
@@ -735,7 +735,7 @@ async function postLinkCredential(input) {
735
735
  }
736
736
  }
737
737
  async function _postJsonAuthed(url, body, pluginAuthToken) {
738
- return new Promise((resolve3, reject) => {
738
+ return new Promise((resolve4, reject) => {
739
739
  const data = JSON.stringify(body);
740
740
  const u2 = new URL(url);
741
741
  const transport = u2.protocol === "https:" ? https : http;
@@ -767,9 +767,9 @@ async function _postJsonAuthed(url, body, pluginAuthToken) {
767
767
  return;
768
768
  }
769
769
  try {
770
- resolve3(JSON.parse(responseBody));
770
+ resolve4(JSON.parse(responseBody));
771
771
  } catch {
772
- resolve3(null);
772
+ resolve4(null);
773
773
  }
774
774
  });
775
775
  }
@@ -784,7 +784,7 @@ async function _postJsonAuthed(url, body, pluginAuthToken) {
784
784
  });
785
785
  }
786
786
  async function _postJson(url, body) {
787
- return new Promise((resolve3, reject) => {
787
+ return new Promise((resolve4, reject) => {
788
788
  const data = JSON.stringify(body);
789
789
  const u2 = new URL(url);
790
790
  const transport = u2.protocol === "https:" ? https : http;
@@ -813,9 +813,9 @@ async function _postJson(url, body) {
813
813
  return;
814
814
  }
815
815
  try {
816
- resolve3(JSON.parse(body2));
816
+ resolve4(JSON.parse(body2));
817
817
  } catch {
818
- resolve3(null);
818
+ resolve4(null);
819
819
  }
820
820
  });
821
821
  }
@@ -830,7 +830,7 @@ async function _postJson(url, body) {
830
830
  });
831
831
  }
832
832
  async function _getJson(url) {
833
- return new Promise((resolve3, reject) => {
833
+ return new Promise((resolve4, reject) => {
834
834
  const u2 = new URL(url);
835
835
  const transport = u2.protocol === "https:" ? https : http;
836
836
  const req = transport.request(
@@ -854,9 +854,9 @@ async function _getJson(url) {
854
854
  return;
855
855
  }
856
856
  try {
857
- resolve3(JSON.parse(body));
857
+ resolve4(JSON.parse(body));
858
858
  } catch {
859
- resolve3(null);
859
+ resolve4(null);
860
860
  }
861
861
  });
862
862
  }
@@ -3508,14 +3508,14 @@ async function addSourceContext(frames) {
3508
3508
  return frames;
3509
3509
  }
3510
3510
  function getContextLinesFromFile(path37, ranges, output) {
3511
- return new Promise((resolve3) => {
3511
+ return new Promise((resolve4) => {
3512
3512
  const stream = (0, import_node_fs.createReadStream)(path37);
3513
3513
  const lineReaded = (0, import_node_readline.createInterface)({
3514
3514
  input: stream
3515
3515
  });
3516
3516
  function destroyStreamAndResolve() {
3517
3517
  stream.destroy();
3518
- resolve3();
3518
+ resolve4();
3519
3519
  }
3520
3520
  let lineNumber = 0;
3521
3521
  let currentRangeIndex = 0;
@@ -4802,9 +4802,9 @@ var PostHogBackendClient = class extends PostHogCoreStateless {
4802
4802
  if (!waitUntil) return;
4803
4803
  if (this.disabled || this.optedOut) return;
4804
4804
  if (!this._waitUntilCycle) {
4805
- let resolve3;
4805
+ let resolve4;
4806
4806
  const promise = new Promise((r) => {
4807
- resolve3 = r;
4807
+ resolve4 = r;
4808
4808
  });
4809
4809
  try {
4810
4810
  waitUntil(promise);
@@ -4812,7 +4812,7 @@ var PostHogBackendClient = class extends PostHogCoreStateless {
4812
4812
  return;
4813
4813
  }
4814
4814
  this._waitUntilCycle = {
4815
- resolve: resolve3,
4815
+ resolve: resolve4,
4816
4816
  startedAt: Date.now(),
4817
4817
  timer: void 0
4818
4818
  };
@@ -4836,12 +4836,12 @@ var PostHogBackendClient = class extends PostHogCoreStateless {
4836
4836
  return cycle?.resolve;
4837
4837
  }
4838
4838
  async resolveWaitUntilFlush() {
4839
- const resolve3 = this._consumeWaitUntilCycle();
4839
+ const resolve4 = this._consumeWaitUntilCycle();
4840
4840
  try {
4841
4841
  await super.flush();
4842
4842
  } catch {
4843
4843
  } finally {
4844
- resolve3?.();
4844
+ resolve4?.();
4845
4845
  }
4846
4846
  }
4847
4847
  getPersistedProperty(key) {
@@ -4933,15 +4933,15 @@ var PostHogBackendClient = class extends PostHogCoreStateless {
4933
4933
  async waitForLocalEvaluationReady(timeoutMs = THIRTY_SECONDS) {
4934
4934
  if (this.isLocalEvaluationReady()) return true;
4935
4935
  if (void 0 === this.featureFlagsPoller) return false;
4936
- return new Promise((resolve3) => {
4936
+ return new Promise((resolve4) => {
4937
4937
  const timeout = setTimeout(() => {
4938
4938
  cleanup();
4939
- resolve3(false);
4939
+ resolve4(false);
4940
4940
  }, timeoutMs);
4941
4941
  const cleanup = this._events.on("localEvaluationFlagsLoaded", (count) => {
4942
4942
  clearTimeout(timeout);
4943
4943
  cleanup();
4944
- resolve3(count > 0);
4944
+ resolve4(count > 0);
4945
4945
  });
4946
4946
  });
4947
4947
  }
@@ -5378,13 +5378,13 @@ var PostHogBackendClient = class extends PostHogCoreStateless {
5378
5378
  this.context?.enter(data, options);
5379
5379
  }
5380
5380
  async _shutdown(shutdownTimeoutMs) {
5381
- const resolve3 = this._consumeWaitUntilCycle();
5381
+ const resolve4 = this._consumeWaitUntilCycle();
5382
5382
  await this.featureFlagsPoller?.stopPoller(shutdownTimeoutMs);
5383
5383
  this.errorTracking.shutdown();
5384
5384
  try {
5385
5385
  return await super._shutdown(shutdownTimeoutMs);
5386
5386
  } finally {
5387
- resolve3?.();
5387
+ resolve4?.();
5388
5388
  }
5389
5389
  }
5390
5390
  async _requestRemoteConfigPayload(flagKey) {
@@ -5740,7 +5740,7 @@ function readAnonId() {
5740
5740
  }
5741
5741
  function superProperties() {
5742
5742
  return {
5743
- cliVersion: true ? "2.20.1" : "0.0.0-dev",
5743
+ cliVersion: true ? "2.20.2" : "0.0.0-dev",
5744
5744
  nodeVersion: process.version,
5745
5745
  platform: process.platform,
5746
5746
  arch: process.arch,
@@ -8874,15 +8874,15 @@ function runInstaller() {
8874
8874
  "-Command",
8875
8875
  "irm https://claude.ai/install.ps1 | iex"
8876
8876
  ] : ["-c", "curl -fsSL https://claude.ai/install.sh | bash"];
8877
- return new Promise((resolve3) => {
8877
+ return new Promise((resolve4) => {
8878
8878
  const proc = (0, import_child_process4.spawn)(cmd, args2, { stdio: "inherit" });
8879
8879
  proc.on("error", (err) => {
8880
8880
  console.error(`
8881
8881
  \u2717 Installer failed to launch: ${err.message}`);
8882
- resolve3(false);
8882
+ resolve4(false);
8883
8883
  });
8884
8884
  proc.on("exit", (code) => {
8885
- resolve3(code === 0);
8885
+ resolve4(code === 0);
8886
8886
  });
8887
8887
  });
8888
8888
  }
@@ -9045,17 +9045,17 @@ function parseUsageOutput(raw) {
9045
9045
  return { percent, resetAt };
9046
9046
  }
9047
9047
  async function fetchClaudeQuota() {
9048
- return new Promise((resolve3) => {
9048
+ return new Promise((resolve4) => {
9049
9049
  const claudeCmd = findInPath("claude") ? "claude" : "claude-code";
9050
9050
  if (!claudeCmd) {
9051
- resolve3(null);
9051
+ resolve4(null);
9052
9052
  return;
9053
9053
  }
9054
9054
  const helperPath = path11.join(os10.tmpdir(), "codeam-quota-helper.py");
9055
9055
  fs8.writeFileSync(helperPath, HELPER_SCRIPT, { mode: 420 });
9056
9056
  const python = findInPath("python3") ?? findInPath("python");
9057
9057
  if (!python) {
9058
- resolve3(null);
9058
+ resolve4(null);
9059
9059
  return;
9060
9060
  }
9061
9061
  const proc = (0, import_child_process5.spawn)(python, [helperPath, claudeCmd, "--tools", ""], {
@@ -9082,13 +9082,13 @@ async function fetchClaudeQuota() {
9082
9082
  fs8.unlinkSync(helperPath);
9083
9083
  } catch {
9084
9084
  }
9085
- resolve3(result);
9085
+ resolve4(result);
9086
9086
  }, 5e3);
9087
9087
  }, 8e3);
9088
9088
  setTimeout(() => {
9089
9089
  if (!resolved) {
9090
9090
  resolved = true;
9091
- resolve3(null);
9091
+ resolve4(null);
9092
9092
  }
9093
9093
  try {
9094
9094
  proc.kill();
@@ -10428,12 +10428,12 @@ function resolveNpm(os26) {
10428
10428
  return os26.id === "win32" ? "npm.cmd" : "npm";
10429
10429
  }
10430
10430
  async function installCodexViaNpm(os26) {
10431
- return new Promise((resolve3, reject) => {
10431
+ return new Promise((resolve4, reject) => {
10432
10432
  const proc = (0, import_node_child_process4.spawn)(resolveNpm(os26), ["install", "-g", "@openai/codex"], {
10433
10433
  stdio: "inherit"
10434
10434
  });
10435
10435
  proc.on("close", (code) => {
10436
- if (code === 0) resolve3();
10436
+ if (code === 0) resolve4();
10437
10437
  else reject(new Error(`npm install -g @openai/codex exited ${code}`));
10438
10438
  });
10439
10439
  proc.on("error", (err) => {
@@ -10548,12 +10548,12 @@ async function ensureCoderabbitInstalled(os26) {
10548
10548
  return false;
10549
10549
  }
10550
10550
  console.log("\n CodeRabbit CLI not found \u2014 installing via the official script\u2026\n");
10551
- const ok = await new Promise((resolve3) => {
10551
+ const ok = await new Promise((resolve4) => {
10552
10552
  const proc = (0, import_node_child_process5.spawn)("sh", ["-c", `curl -fsSL ${INSTALL_URL} | sh`], {
10553
10553
  stdio: "inherit"
10554
10554
  });
10555
- proc.on("close", (code) => resolve3(code === 0));
10556
- proc.on("error", () => resolve3(false));
10555
+ proc.on("close", (code) => resolve4(code === 0));
10556
+ proc.on("error", () => resolve4(false));
10557
10557
  });
10558
10558
  if (!ok) return false;
10559
10559
  os26.augmentPath([`${os26.homeDir()}/.local/bin`, "/opt/homebrew/bin"]);
@@ -10672,7 +10672,7 @@ var CoderabbitRuntimeStrategy = class {
10672
10672
  }
10673
10673
  async runOneShot(input) {
10674
10674
  const launch = await this.prepareInvocation(input);
10675
- return new Promise((resolve3, reject) => {
10675
+ return new Promise((resolve4, reject) => {
10676
10676
  const stdoutBuf = [];
10677
10677
  const stderrBuf = [];
10678
10678
  const proc = (0, import_node_child_process7.spawn)(launch.cmd, launch.args, {
@@ -10683,7 +10683,7 @@ var CoderabbitRuntimeStrategy = class {
10683
10683
  proc.stderr?.on("data", (b) => stderrBuf.push(b));
10684
10684
  proc.on("error", (err) => reject(err));
10685
10685
  proc.on("close", (code) => {
10686
- resolve3(
10686
+ resolve4(
10687
10687
  this.parseOutput({
10688
10688
  exitCode: code ?? 0,
10689
10689
  stdout: Buffer.concat(stdoutBuf).toString("utf8"),
@@ -11216,14 +11216,14 @@ var ChunkEmitter = class {
11216
11216
  "chunkEmitter",
11217
11217
  `send type=${body.type ?? "(clear)"} bytes=${payload.length} done=${body.done === true}`
11218
11218
  );
11219
- return new Promise((resolve3) => {
11219
+ return new Promise((resolve4) => {
11220
11220
  const attempt = (attemptsLeft) => {
11221
11221
  _transport2.post(this.url, this.headers, payload).then(({ statusCode, body: resBody }) => {
11222
11222
  const tookMs = Date.now() - t0;
11223
11223
  if (statusCode === 410 || statusCode === 404 && /SESSION_NOT_FOUND|SESSION_GONE/.test(resBody)) {
11224
11224
  process.stderr.write("[codeam] session was deleted/disconnected \u2014 stopping output stream.\n");
11225
11225
  log.info("chunkEmitter", `dead status=${statusCode} took=${tookMs}ms`);
11226
- resolve3({ dead: true });
11226
+ resolve4({ dead: true });
11227
11227
  return;
11228
11228
  }
11229
11229
  if (statusCode >= 400) {
@@ -11233,7 +11233,7 @@ var ChunkEmitter = class {
11233
11233
  } else {
11234
11234
  log.info("chunkEmitter", `ok status=${statusCode} took=${tookMs}ms`);
11235
11235
  }
11236
- resolve3({ dead: false });
11236
+ resolve4({ dead: false });
11237
11237
  }).catch((err) => {
11238
11238
  log.warn(
11239
11239
  "chunkEmitter",
@@ -11244,7 +11244,7 @@ var ChunkEmitter = class {
11244
11244
  const delay = 200 * (maxRetries - attemptsLeft + 1);
11245
11245
  setTimeout(() => attempt(attemptsLeft - 1), delay);
11246
11246
  } else {
11247
- resolve3({ dead: false });
11247
+ resolve4({ dead: false });
11248
11248
  }
11249
11249
  });
11250
11250
  };
@@ -11256,7 +11256,7 @@ var _transport2 = {
11256
11256
  post: _post
11257
11257
  };
11258
11258
  function _post(url, headers, payload) {
11259
- return new Promise((resolve3, reject) => {
11259
+ return new Promise((resolve4, reject) => {
11260
11260
  let settled = false;
11261
11261
  const u2 = new URL(url);
11262
11262
  const transport = u2.protocol === "https:" ? https3 : http3;
@@ -11280,7 +11280,7 @@ function _post(url, headers, payload) {
11280
11280
  res.on("end", () => {
11281
11281
  if (settled) return;
11282
11282
  settled = true;
11283
- resolve3({ statusCode: res.statusCode ?? 0, body: resData });
11283
+ resolve4({ statusCode: res.statusCode ?? 0, body: resData });
11284
11284
  });
11285
11285
  }
11286
11286
  );
@@ -11781,7 +11781,7 @@ function parseJsonl(filePath) {
11781
11781
  return messages;
11782
11782
  }
11783
11783
  function post(endpoint, body) {
11784
- return new Promise((resolve3) => {
11784
+ return new Promise((resolve4) => {
11785
11785
  const payload = JSON.stringify(body);
11786
11786
  const u2 = new URL(`${API_BASE4}${endpoint}`);
11787
11787
  const transport = u2.protocol === "https:" ? https4 : http4;
@@ -11802,17 +11802,17 @@ function post(endpoint, body) {
11802
11802
  res.resume();
11803
11803
  const ok = res.statusCode !== void 0 && res.statusCode >= 200 && res.statusCode < 300;
11804
11804
  if (!ok) log.warn("history:post", `${endpoint} \u2192 HTTP ${res.statusCode}`);
11805
- resolve3(ok);
11805
+ resolve4(ok);
11806
11806
  }
11807
11807
  );
11808
11808
  req.on("error", (err) => {
11809
11809
  log.warn("history:post", `${endpoint} network error`, err);
11810
- resolve3(false);
11810
+ resolve4(false);
11811
11811
  });
11812
11812
  req.on("timeout", () => {
11813
11813
  log.warn("history:post", `${endpoint} timeout after 15s`);
11814
11814
  req.destroy();
11815
- resolve3(false);
11815
+ resolve4(false);
11816
11816
  });
11817
11817
  req.write(payload);
11818
11818
  req.end();
@@ -12206,6 +12206,7 @@ var HistoryService = class _HistoryService {
12206
12206
 
12207
12207
  // src/services/file-watcher.service.ts
12208
12208
  var import_child_process7 = require("child_process");
12209
+ var fs21 = __toESM(require("fs"));
12209
12210
  var os22 = __toESM(require("os"));
12210
12211
  var path25 = __toESM(require("path"));
12211
12212
 
@@ -12295,7 +12296,7 @@ var _transport3 = {
12295
12296
  post: _post2
12296
12297
  };
12297
12298
  function _post2(url, headers, payload) {
12298
- return new Promise((resolve3, reject) => {
12299
+ return new Promise((resolve4, reject) => {
12299
12300
  let settled = false;
12300
12301
  const u2 = new URL(url);
12301
12302
  const lib = u2.protocol === "https:" ? https5 : http5;
@@ -12320,7 +12321,7 @@ function _post2(url, headers, payload) {
12320
12321
  res.on("end", () => {
12321
12322
  if (settled) return;
12322
12323
  settled = true;
12323
- resolve3({ statusCode: res.statusCode ?? 0, body });
12324
+ resolve4({ statusCode: res.statusCode ?? 0, body });
12324
12325
  });
12325
12326
  }
12326
12327
  );
@@ -12380,6 +12381,30 @@ var _chokidarSeam = {
12380
12381
  }
12381
12382
  }
12382
12383
  };
12384
+ function findGitRoot(startDir) {
12385
+ return _findGitRootSeam.resolve(startDir);
12386
+ }
12387
+ var _findGitRootSeam = {
12388
+ resolve: _defaultFindGitRoot
12389
+ };
12390
+ function _defaultFindGitRoot(startDir) {
12391
+ let dir = path25.resolve(startDir);
12392
+ const seen = /* @__PURE__ */ new Set();
12393
+ for (let i = 0; i < 256; i++) {
12394
+ if (seen.has(dir)) return null;
12395
+ seen.add(dir);
12396
+ try {
12397
+ const gitPath = path25.join(dir, ".git");
12398
+ const stat3 = fs21.statSync(gitPath, { throwIfNoEntry: false });
12399
+ if (stat3 && (stat3.isDirectory() || stat3.isFile())) return dir;
12400
+ } catch {
12401
+ }
12402
+ const parent = path25.dirname(dir);
12403
+ if (parent === dir) return null;
12404
+ dir = parent;
12405
+ }
12406
+ return null;
12407
+ }
12383
12408
  var FileWatcherService = class {
12384
12409
  constructor(opts) {
12385
12410
  this.opts = opts;
@@ -12389,6 +12414,13 @@ var FileWatcherService = class {
12389
12414
  watcher = null;
12390
12415
  pending = /* @__PURE__ */ new Map();
12391
12416
  apiBase;
12417
+ /**
12418
+ * Cache of (file directory → git root). Resolved lazily on each
12419
+ * file event so brand-new sub-repos under the workingDir light up
12420
+ * automatically; cached so a hot session with thousands of writes
12421
+ * doesn't hammer `fs.statSync` for every event.
12422
+ */
12423
+ gitRootByDir = /* @__PURE__ */ new Map();
12392
12424
  stopped = false;
12393
12425
  /**
12394
12426
  * Start watching `opts.workingDir`. Idempotent (second call is a
@@ -12531,67 +12563,82 @@ var FileWatcherService = class {
12531
12563
  }
12532
12564
  async emitForFile(absPath, changeType) {
12533
12565
  if (this.stopped) return;
12534
- const relPath = path25.relative(this.opts.workingDir, absPath);
12535
- if (!relPath || relPath.startsWith("..")) {
12566
+ const fileDir = path25.dirname(absPath);
12567
+ let gitRoot = this.gitRootByDir.get(fileDir);
12568
+ if (gitRoot === void 0) {
12569
+ gitRoot = findGitRoot(fileDir);
12570
+ this.gitRootByDir.set(fileDir, gitRoot);
12571
+ }
12572
+ if (!gitRoot) {
12573
+ log.trace(
12574
+ "fileWatcher",
12575
+ `no enclosing git repo for ${absPath} \u2014 suppressing emit`
12576
+ );
12536
12577
  return;
12537
12578
  }
12579
+ const relPathInRepo = path25.relative(gitRoot, absPath);
12580
+ if (!relPathInRepo || relPathInRepo.startsWith("..")) return;
12581
+ const repoPath = path25.relative(this.opts.workingDir, gitRoot);
12582
+ const repoName = path25.basename(gitRoot);
12538
12583
  let diffText = "";
12539
12584
  let fileStatus = "modified";
12540
12585
  if (changeType === "unlink") {
12541
- const diff = await this.gitDiff(relPath);
12586
+ const diff = await this.gitDiff(gitRoot, relPathInRepo);
12542
12587
  if (diff !== null && diff.trim().length > 0) {
12543
12588
  diffText = diff;
12544
12589
  } else {
12545
12590
  await this.postFileChanged({
12546
12591
  sessionId: this.opts.sessionId,
12547
12592
  pluginId: this.opts.pluginId,
12548
- filePath: relPath,
12593
+ filePath: relPathInRepo,
12549
12594
  fileStatus: "deleted",
12550
12595
  linesAdded: 0,
12551
12596
  linesRemoved: 0,
12552
- hunkCount: 0
12597
+ hunkCount: 0,
12598
+ repoPath,
12599
+ repoName
12553
12600
  });
12554
12601
  return;
12555
12602
  }
12556
12603
  fileStatus = "deleted";
12557
12604
  } else {
12558
- const diff = await this.gitDiff(relPath);
12605
+ const diff = await this.gitDiff(gitRoot, relPathInRepo);
12559
12606
  if (diff === null) {
12560
12607
  log.warn(
12561
12608
  "fileWatcher",
12562
- `git diff failed for ${relPath} \u2014 emitting file-changed only`
12609
+ `git diff failed for ${relPathInRepo} in ${gitRoot} \u2014 suppressing emit`
12563
12610
  );
12564
- await this.postFileChanged({
12565
- sessionId: this.opts.sessionId,
12566
- pluginId: this.opts.pluginId,
12567
- filePath: relPath,
12568
- fileStatus: changeType === "add" ? "added" : "modified",
12569
- linesAdded: 0,
12570
- linesRemoved: 0,
12571
- hunkCount: 0
12572
- });
12573
12611
  return;
12574
12612
  }
12575
12613
  diffText = diff;
12576
12614
  }
12577
12615
  const parsed = parseUnifiedDiff(diffText);
12616
+ if (changeType !== "unlink" && parsed.totalLinesAdded === 0 && parsed.totalLinesRemoved === 0 && parsed.hunks.length === 0) {
12617
+ log.trace(
12618
+ "fileWatcher",
12619
+ `no content delta for ${relPathInRepo} in ${repoName} \u2014 suppressing emit`
12620
+ );
12621
+ return;
12622
+ }
12578
12623
  const finalStatus = parsed.fileStatus !== "modified" ? parsed.fileStatus : changeType === "add" ? "added" : changeType === "unlink" ? "deleted" : fileStatus;
12579
12624
  const reviewStatus = parsed.hunks.length > 0 ? "awaiting_review" : void 0;
12580
12625
  await this.postFileChanged({
12581
12626
  sessionId: this.opts.sessionId,
12582
12627
  pluginId: this.opts.pluginId,
12583
- filePath: relPath,
12628
+ filePath: relPathInRepo,
12584
12629
  fileStatus: finalStatus,
12585
12630
  linesAdded: parsed.totalLinesAdded,
12586
12631
  linesRemoved: parsed.totalLinesRemoved,
12587
12632
  hunkCount: parsed.hunks.length,
12588
- reviewStatus
12633
+ reviewStatus,
12634
+ repoPath,
12635
+ repoName
12589
12636
  });
12590
12637
  for (const hunk of parsed.hunks) {
12591
12638
  await this.postReviewHunk({
12592
12639
  sessionId: this.opts.sessionId,
12593
12640
  pluginId: this.opts.pluginId,
12594
- filePath: relPath,
12641
+ filePath: relPathInRepo,
12595
12642
  fileStatus: finalStatus,
12596
12643
  hunkHeader: hunk.header,
12597
12644
  lines: hunk.lines,
@@ -12602,9 +12649,11 @@ var FileWatcherService = class {
12602
12649
  }
12603
12650
  /**
12604
12651
  * Compute the unified diff for a single path relative to the
12605
- * working dir. Returns `null` when git is unavailable or the cwd
12606
- * is not a repo. Returns `''` when there's no diff (a touch that
12607
- * didn't change content).
12652
+ * enclosing git repo (NOT the CLI's workingDir see
12653
+ * `emitForFile`'s walk-up). Returns `null` when git is unavailable
12654
+ * or the discovered repo root is no longer a repo (race with
12655
+ * external removal). Returns `''` when there's no diff (a touch
12656
+ * that didn't change content).
12608
12657
  *
12609
12658
  * For tracked files we use `git diff --no-color -- <path>` which
12610
12659
  * compares the worktree against HEAD's blob.
@@ -12612,16 +12661,16 @@ var FileWatcherService = class {
12612
12661
  * zero) we use `git diff --no-color --no-index /dev/null <path>`,
12613
12662
  * which produces an "added"-shaped diff against an empty source.
12614
12663
  */
12615
- async gitDiff(relPath) {
12664
+ async gitDiff(repoRoot, relPath) {
12616
12665
  const tracked = await runGit(
12617
- this.opts.workingDir,
12666
+ repoRoot,
12618
12667
  ["diff", "--no-color", "--", relPath]
12619
12668
  );
12620
12669
  if (tracked === null) return null;
12621
12670
  if (tracked.trim().length > 0) return tracked;
12622
12671
  const devNull = process.platform === "win32" ? "NUL" : "/dev/null";
12623
12672
  const untracked = await runGit(
12624
- this.opts.workingDir,
12673
+ repoRoot,
12625
12674
  ["diff", "--no-color", "--no-index", "--", devNull, relPath],
12626
12675
  { allowNonZeroExit: true }
12627
12676
  );
@@ -12689,12 +12738,12 @@ var _gitSeam = {
12689
12738
  run: _runGitImpl
12690
12739
  };
12691
12740
  async function _runGitImpl(cwd, args2, opts = {}) {
12692
- return new Promise((resolve3) => {
12741
+ return new Promise((resolve4) => {
12693
12742
  let proc;
12694
12743
  try {
12695
12744
  proc = (0, import_child_process7.spawn)("git", args2, { cwd, env: process.env });
12696
12745
  } catch {
12697
- resolve3(null);
12746
+ resolve4(null);
12698
12747
  return;
12699
12748
  }
12700
12749
  let stdout = "";
@@ -12705,13 +12754,13 @@ async function _runGitImpl(cwd, args2, opts = {}) {
12705
12754
  proc.stderr?.on("data", (c2) => {
12706
12755
  stderr += c2.toString();
12707
12756
  });
12708
- proc.on("error", () => resolve3(null));
12757
+ proc.on("error", () => resolve4(null));
12709
12758
  proc.on("close", (code) => {
12710
12759
  if (code === 0 || opts.allowNonZeroExit) {
12711
- resolve3(stdout);
12760
+ resolve4(stdout);
12712
12761
  } else {
12713
12762
  log.trace("fileWatcher", `git ${args2.join(" ")} exited ${code} stderr=${stderr.slice(0, 200)}`);
12714
- resolve3(null);
12763
+ resolve4(null);
12715
12764
  }
12716
12765
  });
12717
12766
  });
@@ -12731,7 +12780,7 @@ var _transport4 = {
12731
12780
  get: _get
12732
12781
  };
12733
12782
  function _post3(url, headers, payload) {
12734
- return new Promise((resolve3, reject) => {
12783
+ return new Promise((resolve4, reject) => {
12735
12784
  let settled = false;
12736
12785
  const u2 = new URL(url);
12737
12786
  const lib = u2.protocol === "https:" ? https6 : http6;
@@ -12756,7 +12805,7 @@ function _post3(url, headers, payload) {
12756
12805
  res.on("end", () => {
12757
12806
  if (settled) return;
12758
12807
  settled = true;
12759
- resolve3({ statusCode: res.statusCode ?? 0, body });
12808
+ resolve4({ statusCode: res.statusCode ?? 0, body });
12760
12809
  });
12761
12810
  }
12762
12811
  );
@@ -12773,7 +12822,7 @@ function _post3(url, headers, payload) {
12773
12822
  });
12774
12823
  }
12775
12824
  function _get(url, headers) {
12776
- return new Promise((resolve3, reject) => {
12825
+ return new Promise((resolve4, reject) => {
12777
12826
  let settled = false;
12778
12827
  const u2 = new URL(url);
12779
12828
  const lib = u2.protocol === "https:" ? https6 : http6;
@@ -12797,7 +12846,7 @@ function _get(url, headers) {
12797
12846
  res.on("end", () => {
12798
12847
  if (settled) return;
12799
12848
  settled = true;
12800
- resolve3({ statusCode: res.statusCode ?? 0, body });
12849
+ resolve4({ statusCode: res.statusCode ?? 0, body });
12801
12850
  });
12802
12851
  }
12803
12852
  );
@@ -13180,7 +13229,7 @@ function buildKeepAlive(ctx) {
13180
13229
  let timer = null;
13181
13230
  async function setIdleTimeout(minutes) {
13182
13231
  if (!ctx.inCodespace || !ctx.codespaceName) return;
13183
- await new Promise((resolve3) => {
13232
+ await new Promise((resolve4) => {
13184
13233
  const proc = (0, import_child_process8.spawn)(
13185
13234
  "gh",
13186
13235
  [
@@ -13194,8 +13243,8 @@ function buildKeepAlive(ctx) {
13194
13243
  { stdio: "ignore", detached: true }
13195
13244
  );
13196
13245
  proc.unref();
13197
- proc.on("exit", () => resolve3());
13198
- proc.on("error", () => resolve3());
13246
+ proc.on("exit", () => resolve4());
13247
+ proc.on("error", () => resolve4());
13199
13248
  });
13200
13249
  }
13201
13250
  return {
@@ -13218,7 +13267,7 @@ function buildKeepAlive(ctx) {
13218
13267
  }
13219
13268
 
13220
13269
  // src/commands/start/handlers.ts
13221
- var fs23 = __toESM(require("fs"));
13270
+ var fs24 = __toESM(require("fs"));
13222
13271
  var os23 = __toESM(require("os"));
13223
13272
  var path29 = __toESM(require("path"));
13224
13273
  var import_crypto4 = require("crypto");
@@ -13278,7 +13327,7 @@ function parsePayload2(schema, raw) {
13278
13327
  }
13279
13328
 
13280
13329
  // src/services/file-ops.service.ts
13281
- var fs21 = __toESM(require("fs/promises"));
13330
+ var fs22 = __toESM(require("fs/promises"));
13282
13331
  var path26 = __toESM(require("path"));
13283
13332
  var MAX_FILE_BYTES = 5 * 1024 * 1024;
13284
13333
  var MAX_WALK_DEPTH = 6;
@@ -13319,7 +13368,7 @@ function isUnder(parent, candidate) {
13319
13368
  }
13320
13369
  async function isExistingFile(absPath) {
13321
13370
  try {
13322
- const stat3 = await fs21.stat(absPath);
13371
+ const stat3 = await fs22.stat(absPath);
13323
13372
  return stat3.isFile();
13324
13373
  } catch {
13325
13374
  return false;
@@ -13332,7 +13381,7 @@ async function walkForSuffix(dir, needleVariants, depth, ctx) {
13332
13381
  ctx.visited++;
13333
13382
  let entries = [];
13334
13383
  try {
13335
- entries = await fs21.readdir(dir, { withFileTypes: true });
13384
+ entries = await fs22.readdir(dir, { withFileTypes: true });
13336
13385
  } catch {
13337
13386
  return;
13338
13387
  }
@@ -13393,11 +13442,11 @@ async function readProjectFile(rawPath) {
13393
13442
  if (!abs) {
13394
13443
  return { error: `File not found in the project tree: ${rawPath}` };
13395
13444
  }
13396
- const stat3 = await fs21.stat(abs);
13445
+ const stat3 = await fs22.stat(abs);
13397
13446
  if (stat3.size > MAX_FILE_BYTES) {
13398
13447
  return { error: `File too large (${(stat3.size / 1024 / 1024).toFixed(1)} MB > ${MAX_FILE_BYTES / 1024 / 1024} MB).` };
13399
13448
  }
13400
- const buf = await fs21.readFile(abs);
13449
+ const buf = await fs22.readFile(abs);
13401
13450
  if (looksBinary(buf)) {
13402
13451
  return { error: "Binary file \u2014 refusing to open in a code editor." };
13403
13452
  }
@@ -13416,8 +13465,8 @@ async function writeProjectFile(rawPath, content) {
13416
13465
  if (Buffer.byteLength(content, "utf-8") > MAX_FILE_BYTES) {
13417
13466
  return { error: "Content too large." };
13418
13467
  }
13419
- await fs21.mkdir(path26.dirname(abs), { recursive: true });
13420
- await fs21.writeFile(abs, content, "utf-8");
13468
+ await fs22.mkdir(path26.dirname(abs), { recursive: true });
13469
+ await fs22.writeFile(abs, content, "utf-8");
13421
13470
  return { ok: true };
13422
13471
  } catch (e) {
13423
13472
  const msg = e instanceof Error ? e.message : "Write failed";
@@ -13428,7 +13477,7 @@ async function writeProjectFile(rawPath, content) {
13428
13477
  // src/services/project-ops.service.ts
13429
13478
  var import_child_process9 = require("child_process");
13430
13479
  var import_util2 = require("util");
13431
- var fs22 = __toESM(require("fs/promises"));
13480
+ var fs23 = __toESM(require("fs/promises"));
13432
13481
  var path27 = __toESM(require("path"));
13433
13482
  var execFileP3 = (0, import_util2.promisify)(import_child_process9.execFile);
13434
13483
  var PROJECT_IGNORE = /* @__PURE__ */ new Set([
@@ -13477,7 +13526,7 @@ async function listProjectFiles(opts = {}) {
13477
13526
  }
13478
13527
  let entries = [];
13479
13528
  try {
13480
- entries = await fs22.readdir(dir, { withFileTypes: true });
13529
+ entries = await fs23.readdir(dir, { withFileTypes: true });
13481
13530
  } catch {
13482
13531
  return;
13483
13532
  }
@@ -13498,7 +13547,7 @@ async function listProjectFiles(opts = {}) {
13498
13547
  }
13499
13548
  let size = 0;
13500
13549
  try {
13501
- const st3 = await fs22.stat(full);
13550
+ const st3 = await fs23.stat(full);
13502
13551
  size = st3.size;
13503
13552
  } catch {
13504
13553
  }
@@ -13601,7 +13650,7 @@ async function gitStatus(cwd) {
13601
13650
  try {
13602
13651
  const gitDir = (await git(["rev-parse", "--git-dir"], root)).stdout.trim();
13603
13652
  const mergeHead = path27.isAbsolute(gitDir) ? path27.join(gitDir, "MERGE_HEAD") : path27.join(root, gitDir, "MERGE_HEAD");
13604
- await fs22.access(mergeHead);
13653
+ await fs23.access(mergeHead);
13605
13654
  hasMergeInProgress = true;
13606
13655
  } catch {
13607
13656
  }
@@ -13747,7 +13796,7 @@ async function jsSearchFiles(opts, cwd, cap) {
13747
13796
  }
13748
13797
  let content = "";
13749
13798
  try {
13750
- content = await fs22.readFile(path27.join(cwd, f.path), "utf8");
13799
+ content = await fs23.readFile(path27.join(cwd, f.path), "utf8");
13751
13800
  } catch {
13752
13801
  continue;
13753
13802
  }
@@ -14031,7 +14080,7 @@ var pendingAttachmentFiles = /* @__PURE__ */ new Set();
14031
14080
  function cleanupAttachmentTempFiles() {
14032
14081
  for (const p2 of pendingAttachmentFiles) {
14033
14082
  try {
14034
- fs23.unlinkSync(p2);
14083
+ fs24.unlinkSync(p2);
14035
14084
  } catch {
14036
14085
  }
14037
14086
  }
@@ -14041,7 +14090,7 @@ function saveFilesTemp(files) {
14041
14090
  return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
14042
14091
  const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
14043
14092
  const tmpPath = path29.join(os23.tmpdir(), `codeam-${(0, import_crypto4.randomUUID)()}-${safeName}`);
14044
- fs23.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
14093
+ fs24.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
14045
14094
  pendingAttachmentFiles.add(tmpPath);
14046
14095
  return tmpPath;
14047
14096
  });
@@ -14061,7 +14110,7 @@ var startTask = (ctx, _cmd, parsed) => {
14061
14110
  setTimeout(() => {
14062
14111
  for (const p2 of paths) {
14063
14112
  try {
14064
- fs23.unlinkSync(p2);
14113
+ fs24.unlinkSync(p2);
14065
14114
  } catch {
14066
14115
  }
14067
14116
  pendingAttachmentFiles.delete(p2);
@@ -14627,7 +14676,7 @@ async function pair(args2 = []) {
14627
14676
  waitSpin.message(waitMessage());
14628
14677
  }, 1e3);
14629
14678
  countdownInterval.unref?.();
14630
- await new Promise((resolve3) => {
14679
+ await new Promise((resolve4) => {
14631
14680
  let stopPolling = null;
14632
14681
  function sigintHandler() {
14633
14682
  clearInterval(countdownInterval);
@@ -14667,7 +14716,7 @@ async function pair(args2 = []) {
14667
14716
  });
14668
14717
  showSuccess(`Paired with ${info.userName} (${info.plan})`);
14669
14718
  console.log("");
14670
- resolve3();
14719
+ resolve4();
14671
14720
  },
14672
14721
  () => {
14673
14722
  clearInterval(countdownInterval);
@@ -14683,7 +14732,7 @@ async function pair(args2 = []) {
14683
14732
  }
14684
14733
 
14685
14734
  // src/commands/pair-auto.ts
14686
- var fs24 = __toESM(require("fs"));
14735
+ var fs25 = __toESM(require("fs"));
14687
14736
  var os24 = __toESM(require("os"));
14688
14737
  var import_crypto6 = require("crypto");
14689
14738
  var API_BASE7 = resolveApiBaseUrl();
@@ -14703,10 +14752,10 @@ function readTokenFromArgs(args2) {
14703
14752
  if (fileFlag) {
14704
14753
  const path37 = fileFlag.slice("--token-file=".length);
14705
14754
  try {
14706
- const content = fs24.readFileSync(path37, "utf8").trim();
14755
+ const content = fs25.readFileSync(path37, "utf8").trim();
14707
14756
  if (content.length === 0) fail(`--token-file ${path37} is empty`);
14708
14757
  try {
14709
- fs24.unlinkSync(path37);
14758
+ fs25.unlinkSync(path37);
14710
14759
  } catch {
14711
14760
  }
14712
14761
  return content;
@@ -15003,12 +15052,12 @@ var GitHubCodespacesProvider = class {
15003
15052
  }
15004
15053
  if (!isAuthed) {
15005
15054
  resetStdinForChild();
15006
- await new Promise((resolve3, reject) => {
15055
+ await new Promise((resolve4, reject) => {
15007
15056
  const proc = (0, import_child_process12.spawn)("gh", ["auth", "login", "-s", "codespace,repo,read:user"], {
15008
15057
  stdio: "inherit"
15009
15058
  });
15010
15059
  proc.on("exit", (code) => {
15011
- if (code === 0) resolve3();
15060
+ if (code === 0) resolve4();
15012
15061
  else reject(new Error("gh auth login failed."));
15013
15062
  });
15014
15063
  proc.on("error", reject);
@@ -15037,13 +15086,13 @@ var GitHubCodespacesProvider = class {
15037
15086
  }
15038
15087
  wt(noteLines.join("\n"), "One more permission needed");
15039
15088
  resetStdinForChild();
15040
- const refreshCode = await new Promise((resolve3, reject) => {
15089
+ const refreshCode = await new Promise((resolve4, reject) => {
15041
15090
  const proc = (0, import_child_process12.spawn)(
15042
15091
  "gh",
15043
15092
  ["auth", "refresh", "-h", "github.com", "-s", "codespace"],
15044
15093
  { stdio: "inherit" }
15045
15094
  );
15046
- proc.on("exit", (code) => resolve3(code ?? 1));
15095
+ proc.on("exit", (code) => resolve4(code ?? 1));
15047
15096
  proc.on("error", reject);
15048
15097
  });
15049
15098
  if (refreshCode !== 0) {
@@ -15187,10 +15236,10 @@ var GitHubCodespacesProvider = class {
15187
15236
  if (q(proceed) || !proceed) return;
15188
15237
  O2.step(`Installing gh via ${installCmd.describe}\u2026`);
15189
15238
  resetStdinForChild();
15190
- const ok = await new Promise((resolve3) => {
15239
+ const ok = await new Promise((resolve4) => {
15191
15240
  const proc = (0, import_child_process12.spawn)(installCmd.exe, installCmd.args, { stdio: "inherit" });
15192
- proc.on("exit", (code) => resolve3(code === 0));
15193
- proc.on("error", () => resolve3(false));
15241
+ proc.on("exit", (code) => resolve4(code === 0));
15242
+ proc.on("error", () => resolve4(false));
15194
15243
  });
15195
15244
  if (ok) O2.success("gh installed");
15196
15245
  else O2.error("gh install failed");
@@ -15214,14 +15263,14 @@ var GitHubCodespacesProvider = class {
15214
15263
  "Expanding GitHub scopes"
15215
15264
  );
15216
15265
  resetStdinForChild();
15217
- await new Promise((resolve3, reject) => {
15266
+ await new Promise((resolve4, reject) => {
15218
15267
  const proc = (0, import_child_process12.spawn)(
15219
15268
  "gh",
15220
15269
  ["auth", "refresh", "-h", "github.com", "-s", "repo,read:org"],
15221
15270
  { stdio: "inherit" }
15222
15271
  );
15223
15272
  proc.on("exit", (code) => {
15224
- if (code === 0) resolve3();
15273
+ if (code === 0) resolve4();
15225
15274
  else reject(new Error(
15226
15275
  "gh auth refresh failed. Re-run `gh auth refresh -h github.com -s repo,read:org` manually."
15227
15276
  ));
@@ -15392,13 +15441,13 @@ var GitHubCodespacesProvider = class {
15392
15441
  }
15393
15442
  async streamCommand(workspaceId, command2) {
15394
15443
  resetStdinForChild();
15395
- return new Promise((resolve3, reject) => {
15444
+ return new Promise((resolve4, reject) => {
15396
15445
  const proc = (0, import_child_process12.spawn)(
15397
15446
  "gh",
15398
15447
  ["codespace", "ssh", "-c", workspaceId, "--", "-tt", command2],
15399
15448
  { stdio: "inherit" }
15400
15449
  );
15401
- proc.on("exit", (code) => resolve3({ code: code ?? 0 }));
15450
+ proc.on("exit", (code) => resolve4({ code: code ?? 0 }));
15402
15451
  proc.on("error", reject);
15403
15452
  });
15404
15453
  }
@@ -15419,7 +15468,7 @@ var GitHubCodespacesProvider = class {
15419
15468
  "--",
15420
15469
  `mkdir -p ${shellQuote(remoteDir)} && tar -xzf - -C ${shellQuote(remoteDir)}`
15421
15470
  ];
15422
- await new Promise((resolve3, reject) => {
15471
+ await new Promise((resolve4, reject) => {
15423
15472
  const tar = (0, import_child_process12.spawn)("tar", tarArgs, {
15424
15473
  stdio: ["ignore", "pipe", "pipe"],
15425
15474
  env: tarEnv
@@ -15439,7 +15488,7 @@ var GitHubCodespacesProvider = class {
15439
15488
  ssh.on("error", reject);
15440
15489
  ssh.on("exit", (code) => {
15441
15490
  if (code === 0) {
15442
- resolve3();
15491
+ resolve4();
15443
15492
  } else {
15444
15493
  const reason = (sshErr || tarErr || `exit ${code}`).trim().slice(0, 500);
15445
15494
  reject(new Error(`Remote tar failed: ${reason}`));
@@ -15457,7 +15506,7 @@ var GitHubCodespacesProvider = class {
15457
15506
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote(remotePath)}`);
15458
15507
  }
15459
15508
  const cmd = parts.join(" && ");
15460
- await new Promise((resolve3, reject) => {
15509
+ await new Promise((resolve4, reject) => {
15461
15510
  const proc = (0, import_child_process12.spawn)(
15462
15511
  "gh",
15463
15512
  ["codespace", "ssh", "-c", workspaceId, "--", cmd],
@@ -15469,7 +15518,7 @@ var GitHubCodespacesProvider = class {
15469
15518
  });
15470
15519
  proc.on("error", reject);
15471
15520
  proc.on("exit", (code) => {
15472
- if (code === 0) resolve3();
15521
+ if (code === 0) resolve4();
15473
15522
  else reject(new Error(`Remote write failed: ${(stderr || `exit ${code}`).trim().slice(0, 500)}`));
15474
15523
  });
15475
15524
  proc.stdin?.write(contents);
@@ -15559,10 +15608,10 @@ var GitpodProvider = class {
15559
15608
  "Authenticating Gitpod"
15560
15609
  );
15561
15610
  resetStdinForChild2();
15562
- await new Promise((resolve3, reject) => {
15611
+ await new Promise((resolve4, reject) => {
15563
15612
  const proc = (0, import_child_process13.spawn)("gitpod", ["login"], { stdio: "inherit" });
15564
15613
  proc.on("exit", (code) => {
15565
- if (code === 0) resolve3();
15614
+ if (code === 0) resolve4();
15566
15615
  else reject(new Error("gitpod login failed."));
15567
15616
  });
15568
15617
  proc.on("error", reject);
@@ -15711,13 +15760,13 @@ var GitpodProvider = class {
15711
15760
  }
15712
15761
  async streamCommand(workspaceId, command2) {
15713
15762
  resetStdinForChild2();
15714
- return new Promise((resolve3, reject) => {
15763
+ return new Promise((resolve4, reject) => {
15715
15764
  const proc = (0, import_child_process13.spawn)(
15716
15765
  "gitpod",
15717
15766
  ["workspace", "ssh", workspaceId, "--", "-tt", command2],
15718
15767
  { stdio: "inherit" }
15719
15768
  );
15720
- proc.on("exit", (code) => resolve3({ code: code ?? 0 }));
15769
+ proc.on("exit", (code) => resolve4({ code: code ?? 0 }));
15721
15770
  proc.on("error", reject);
15722
15771
  });
15723
15772
  }
@@ -15731,7 +15780,7 @@ var GitpodProvider = class {
15731
15780
  tarArgs.push(".");
15732
15781
  const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
15733
15782
  const remoteCmd = `mkdir -p ${shellQuote2(remoteDir)} && tar -xzf - -C ${shellQuote2(remoteDir)}`;
15734
- await new Promise((resolve3, reject) => {
15783
+ await new Promise((resolve4, reject) => {
15735
15784
  const tar = (0, import_child_process13.spawn)("tar", tarArgs, {
15736
15785
  stdio: ["ignore", "pipe", "pipe"],
15737
15786
  env: tarEnv
@@ -15752,7 +15801,7 @@ var GitpodProvider = class {
15752
15801
  tar.on("error", reject);
15753
15802
  ssh.on("error", reject);
15754
15803
  ssh.on("exit", (code) => {
15755
- if (code === 0) resolve3();
15804
+ if (code === 0) resolve4();
15756
15805
  else reject(new Error(`Remote tar failed: ${(sshErr || tarErr || `exit ${code}`).trim().slice(0, 500)}`));
15757
15806
  });
15758
15807
  });
@@ -15767,7 +15816,7 @@ var GitpodProvider = class {
15767
15816
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote2(remotePath)}`);
15768
15817
  }
15769
15818
  const cmd = parts.join(" && ");
15770
- await new Promise((resolve3, reject) => {
15819
+ await new Promise((resolve4, reject) => {
15771
15820
  const proc = (0, import_child_process13.spawn)(
15772
15821
  "gitpod",
15773
15822
  ["workspace", "ssh", workspaceId, "--", cmd],
@@ -15779,7 +15828,7 @@ var GitpodProvider = class {
15779
15828
  });
15780
15829
  proc.on("error", reject);
15781
15830
  proc.on("exit", (code) => {
15782
- if (code === 0) resolve3();
15831
+ if (code === 0) resolve4();
15783
15832
  else reject(new Error(`Remote write failed: ${(stderr || `exit ${code}`).trim().slice(0, 500)}`));
15784
15833
  });
15785
15834
  proc.stdin?.write(contents);
@@ -15836,14 +15885,14 @@ var GitLabWorkspacesProvider = class {
15836
15885
  "Authenticating GitLab"
15837
15886
  );
15838
15887
  resetStdinForChild3();
15839
- await new Promise((resolve3, reject) => {
15888
+ await new Promise((resolve4, reject) => {
15840
15889
  const proc = (0, import_child_process14.spawn)(
15841
15890
  "glab",
15842
15891
  ["auth", "login", "--scopes", "api,read_user,read_repository"],
15843
15892
  { stdio: "inherit" }
15844
15893
  );
15845
15894
  proc.on("exit", (code) => {
15846
- if (code === 0) resolve3();
15895
+ if (code === 0) resolve4();
15847
15896
  else reject(new Error("glab auth login failed."));
15848
15897
  });
15849
15898
  proc.on("error", reject);
@@ -16008,13 +16057,13 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
16008
16057
  async streamCommand(workspaceId, command2) {
16009
16058
  const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
16010
16059
  resetStdinForChild3();
16011
- return new Promise((resolve3, reject) => {
16060
+ return new Promise((resolve4, reject) => {
16012
16061
  const proc = (0, import_child_process14.spawn)(
16013
16062
  "ssh",
16014
16063
  ["-tt", "-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, command2],
16015
16064
  { stdio: "inherit" }
16016
16065
  );
16017
- proc.on("exit", (code) => resolve3({ code: code ?? 0 }));
16066
+ proc.on("exit", (code) => resolve4({ code: code ?? 0 }));
16018
16067
  proc.on("error", reject);
16019
16068
  });
16020
16069
  }
@@ -16029,7 +16078,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
16029
16078
  tarArgs.push(".");
16030
16079
  const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
16031
16080
  const remoteCmd = `mkdir -p ${shellQuote3(remoteDir)} && tar -xzf - -C ${shellQuote3(remoteDir)}`;
16032
- await new Promise((resolve3, reject) => {
16081
+ await new Promise((resolve4, reject) => {
16033
16082
  const tar = (0, import_child_process14.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
16034
16083
  const ssh = (0, import_child_process14.spawn)(
16035
16084
  "ssh",
@@ -16047,7 +16096,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
16047
16096
  tar.on("error", reject);
16048
16097
  ssh.on("error", reject);
16049
16098
  ssh.on("exit", (code) => {
16050
- if (code === 0) resolve3();
16099
+ if (code === 0) resolve4();
16051
16100
  else reject(new Error(`Remote tar failed: ${(sshErr || tarErr || `exit ${code}`).trim().slice(0, 500)}`));
16052
16101
  });
16053
16102
  });
@@ -16060,7 +16109,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
16060
16109
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote3(remotePath)}`);
16061
16110
  }
16062
16111
  const cmd = parts.join(" && ");
16063
- await new Promise((resolve3, reject) => {
16112
+ await new Promise((resolve4, reject) => {
16064
16113
  const proc = (0, import_child_process14.spawn)(
16065
16114
  "ssh",
16066
16115
  ["-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, cmd],
@@ -16072,7 +16121,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
16072
16121
  });
16073
16122
  proc.on("error", reject);
16074
16123
  proc.on("exit", (code) => {
16075
- if (code === 0) resolve3();
16124
+ if (code === 0) resolve4();
16076
16125
  else reject(new Error(`Remote write failed: ${(stderr || `exit ${code}`).trim().slice(0, 500)}`));
16077
16126
  });
16078
16127
  proc.stdin?.write(contents);
@@ -16163,10 +16212,10 @@ var RailwayProvider = class {
16163
16212
  "Authenticating Railway"
16164
16213
  );
16165
16214
  resetStdinForChild4();
16166
- await new Promise((resolve3, reject) => {
16215
+ await new Promise((resolve4, reject) => {
16167
16216
  const proc = (0, import_child_process15.spawn)("railway", ["login"], { stdio: "inherit" });
16168
16217
  proc.on("exit", (code) => {
16169
- if (code === 0) resolve3();
16218
+ if (code === 0) resolve4();
16170
16219
  else reject(new Error("railway login failed."));
16171
16220
  });
16172
16221
  proc.on("error", reject);
@@ -16306,13 +16355,13 @@ var RailwayProvider = class {
16306
16355
  throw new Error("Invalid Railway workspace id (expected projectId/serviceId).");
16307
16356
  }
16308
16357
  resetStdinForChild4();
16309
- return new Promise((resolve3, reject) => {
16358
+ return new Promise((resolve4, reject) => {
16310
16359
  const proc = (0, import_child_process15.spawn)(
16311
16360
  "railway",
16312
16361
  ["shell", "--project", projectId, "--service", serviceId, "--command", command2],
16313
16362
  { stdio: "inherit" }
16314
16363
  );
16315
- proc.on("exit", (code) => resolve3({ code: code ?? 0 }));
16364
+ proc.on("exit", (code) => resolve4({ code: code ?? 0 }));
16316
16365
  proc.on("error", reject);
16317
16366
  });
16318
16367
  }
@@ -16330,7 +16379,7 @@ var RailwayProvider = class {
16330
16379
  tarArgs.push(".");
16331
16380
  const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
16332
16381
  const remoteCmd = `mkdir -p ${shellQuote4(remoteDir)} && tar -xzf - -C ${shellQuote4(remoteDir)}`;
16333
- await new Promise((resolve3, reject) => {
16382
+ await new Promise((resolve4, reject) => {
16334
16383
  const tar = (0, import_child_process15.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
16335
16384
  const sh = (0, import_child_process15.spawn)(
16336
16385
  "railway",
@@ -16348,7 +16397,7 @@ var RailwayProvider = class {
16348
16397
  tar.on("error", reject);
16349
16398
  sh.on("error", reject);
16350
16399
  sh.on("exit", (code) => {
16351
- if (code === 0) resolve3();
16400
+ if (code === 0) resolve4();
16352
16401
  else reject(new Error(`Remote tar failed: ${(shErr || tarErr || `exit ${code}`).trim().slice(0, 500)}`));
16353
16402
  });
16354
16403
  });
@@ -16364,7 +16413,7 @@ var RailwayProvider = class {
16364
16413
  parts.push(`chmod ${options.mode.toString(8)} ${shellQuote4(remotePath)}`);
16365
16414
  }
16366
16415
  const cmd = parts.join(" && ");
16367
- await new Promise((resolve3, reject) => {
16416
+ await new Promise((resolve4, reject) => {
16368
16417
  const proc = (0, import_child_process15.spawn)(
16369
16418
  "railway",
16370
16419
  ["shell", "--project", projectId, "--service", serviceId, "--command", cmd],
@@ -16376,7 +16425,7 @@ var RailwayProvider = class {
16376
16425
  });
16377
16426
  proc.on("error", reject);
16378
16427
  proc.on("exit", (code) => {
16379
- if (code === 0) resolve3();
16428
+ if (code === 0) resolve4();
16380
16429
  else reject(new Error(`Remote write failed: ${(stderr || `exit ${code}`).trim().slice(0, 500)}`));
16381
16430
  });
16382
16431
  proc.stdin?.write(contents);
@@ -16899,7 +16948,7 @@ async function stopWorkspaceFromLocal(target) {
16899
16948
 
16900
16949
  // src/commands/link.ts
16901
16950
  var import_node_crypto4 = require("crypto");
16902
- var fs25 = __toESM(require("fs"));
16951
+ var fs26 = __toESM(require("fs"));
16903
16952
  var path34 = __toESM(require("path"));
16904
16953
  var import_chokidar = __toESM(require("chokidar"));
16905
16954
  var import_picocolors11 = __toESM(require("picocolors"));
@@ -16938,7 +16987,7 @@ function parseLinkArgs(args2) {
16938
16987
  if (apiKeyFileArg) {
16939
16988
  const filePath = apiKeyFileArg.slice("--api-key-file=".length);
16940
16989
  try {
16941
- apiKey = fs25.readFileSync(path34.resolve(filePath), "utf8").trim();
16990
+ apiKey = fs26.readFileSync(path34.resolve(filePath), "utf8").trim();
16942
16991
  } catch (err) {
16943
16992
  throw new Error(`Could not read --api-key-file ${filePath}: ${err.message}`);
16944
16993
  }
@@ -16983,7 +17032,7 @@ async function link(args2 = []) {
16983
17032
  waitSpin.start(waitMsg());
16984
17033
  const countdown = setInterval(() => waitSpin.message(waitMsg()), 1e3);
16985
17034
  countdown.unref?.();
16986
- const paired = await new Promise((resolve3, reject) => {
17035
+ const paired = await new Promise((resolve4, reject) => {
16987
17036
  let stopPoll = null;
16988
17037
  const sigint = () => {
16989
17038
  clearInterval(countdown);
@@ -16996,7 +17045,7 @@ async function link(args2 = []) {
16996
17045
  process.removeListener("SIGINT", sigint);
16997
17046
  clearInterval(countdown);
16998
17047
  waitSpin.stop("Paired");
16999
- resolve3(info);
17048
+ resolve4(info);
17000
17049
  },
17001
17050
  () => {
17002
17051
  clearInterval(countdown);
@@ -17032,7 +17081,7 @@ async function link(args2 = []) {
17032
17081
  return;
17033
17082
  }
17034
17083
  if (parsed.tokenFile) {
17035
- const credential = fs25.readFileSync(path34.resolve(parsed.tokenFile), "utf8").trim();
17084
+ const credential = fs26.readFileSync(path34.resolve(parsed.tokenFile), "utf8").trim();
17036
17085
  if (!credential) {
17037
17086
  showError(`--token-file ${parsed.tokenFile} is empty.`);
17038
17087
  process.exit(1);
@@ -17102,14 +17151,14 @@ async function captureFreshCredentials(ctx) {
17102
17151
  }
17103
17152
  };
17104
17153
  try {
17105
- const token = await new Promise((resolve3, reject) => {
17154
+ const token = await new Promise((resolve4, reject) => {
17106
17155
  let settled = false;
17107
17156
  const tryExtract = async () => {
17108
17157
  if (settled) return;
17109
17158
  const t2 = await ctx.locator.extract();
17110
17159
  if (t2 && !settled) {
17111
17160
  settled = true;
17112
- resolve3(t2);
17161
+ resolve4(t2);
17113
17162
  }
17114
17163
  };
17115
17164
  watcher.on("add", () => void tryExtract());
@@ -17221,7 +17270,7 @@ async function linkDryRunPreflight(ctx) {
17221
17270
  var import_node_dns = require("dns");
17222
17271
  var import_node_util4 = require("util");
17223
17272
  var import_node_crypto5 = require("crypto");
17224
- var fs26 = __toESM(require("fs"));
17273
+ var fs27 = __toESM(require("fs"));
17225
17274
  var path35 = __toESM(require("path"));
17226
17275
  var import_picocolors12 = __toESM(require("picocolors"));
17227
17276
  var dnsResolveP = (0, import_node_util4.promisify)(import_node_dns.resolve);
@@ -17280,11 +17329,11 @@ async function checkHealth(apiBase) {
17280
17329
  function checkConfigDir() {
17281
17330
  const dir = path35.join(require("os").homedir(), ".codeam");
17282
17331
  try {
17283
- fs26.mkdirSync(dir, { recursive: true, mode: 448 });
17332
+ fs27.mkdirSync(dir, { recursive: true, mode: 448 });
17284
17333
  const probe = path35.join(dir, ".doctor-probe");
17285
- fs26.writeFileSync(probe, "ok", { mode: 384 });
17286
- const read = fs26.readFileSync(probe, "utf8");
17287
- fs26.unlinkSync(probe);
17334
+ fs27.writeFileSync(probe, "ok", { mode: 384 });
17335
+ const read = fs27.readFileSync(probe, "utf8");
17336
+ fs27.unlinkSync(probe);
17288
17337
  if (read !== "ok") throw new Error("write/read round-trip mismatch");
17289
17338
  return {
17290
17339
  id: "config-dir",
@@ -17390,7 +17439,7 @@ function checkChokidar() {
17390
17439
  }
17391
17440
  async function doctor(args2 = []) {
17392
17441
  const json = args2.includes("--json");
17393
- const cliVersion = true ? "2.20.1" : "0.0.0-dev";
17442
+ const cliVersion = true ? "2.20.2" : "0.0.0-dev";
17394
17443
  const apiBase = resolveApiBaseUrl();
17395
17444
  const diagnosticId = (0, import_node_crypto5.randomUUID)();
17396
17445
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -17589,7 +17638,7 @@ async function completion(args2) {
17589
17638
  // src/commands/version.ts
17590
17639
  var import_picocolors13 = __toESM(require("picocolors"));
17591
17640
  function version2() {
17592
- const v = true ? "2.20.1" : "unknown";
17641
+ const v = true ? "2.20.2" : "unknown";
17593
17642
  console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
17594
17643
  }
17595
17644
 
@@ -17717,7 +17766,7 @@ function tryShowSubcommandHelp(cmd, args2) {
17717
17766
  var _subcommandHelpKeys = Object.keys(HELPS);
17718
17767
 
17719
17768
  // src/lib/updateNotifier.ts
17720
- var fs27 = __toESM(require("fs"));
17769
+ var fs28 = __toESM(require("fs"));
17721
17770
  var os25 = __toESM(require("os"));
17722
17771
  var path36 = __toESM(require("path"));
17723
17772
  var https7 = __toESM(require("https"));
@@ -17732,7 +17781,7 @@ function cachePath() {
17732
17781
  }
17733
17782
  function readCache() {
17734
17783
  try {
17735
- const raw = fs27.readFileSync(cachePath(), "utf8");
17784
+ const raw = fs28.readFileSync(cachePath(), "utf8");
17736
17785
  const parsed = JSON.parse(raw);
17737
17786
  if (typeof parsed.fetchedAt !== "number" || typeof parsed.latest !== "string") return null;
17738
17787
  return parsed;
@@ -17743,10 +17792,10 @@ function readCache() {
17743
17792
  function writeCache(cache) {
17744
17793
  try {
17745
17794
  const file = cachePath();
17746
- fs27.mkdirSync(path36.dirname(file), { recursive: true });
17795
+ fs28.mkdirSync(path36.dirname(file), { recursive: true });
17747
17796
  const tmp = `${file}.${process.pid}.tmp`;
17748
- fs27.writeFileSync(tmp, JSON.stringify(cache));
17749
- fs27.renameSync(tmp, file);
17797
+ fs28.writeFileSync(tmp, JSON.stringify(cache));
17798
+ fs28.renameSync(tmp, file);
17750
17799
  } catch {
17751
17800
  }
17752
17801
  }
@@ -17764,14 +17813,14 @@ function compareSemver(a, b) {
17764
17813
  return 0;
17765
17814
  }
17766
17815
  function fetchLatest() {
17767
- return new Promise((resolve3) => {
17816
+ return new Promise((resolve4) => {
17768
17817
  const req = https7.get(
17769
17818
  REGISTRY_URL,
17770
17819
  { headers: { Accept: "application/json" }, timeout: REQUEST_TIMEOUT_MS },
17771
17820
  (res) => {
17772
17821
  if (res.statusCode !== 200) {
17773
17822
  res.resume();
17774
- resolve3(null);
17823
+ resolve4(null);
17775
17824
  return;
17776
17825
  }
17777
17826
  let buf = "";
@@ -17783,21 +17832,21 @@ function fetchLatest() {
17783
17832
  try {
17784
17833
  const json = JSON.parse(buf);
17785
17834
  if (typeof json.version === "string") {
17786
- resolve3(json.version);
17835
+ resolve4(json.version);
17787
17836
  } else {
17788
- resolve3(null);
17837
+ resolve4(null);
17789
17838
  }
17790
17839
  } catch {
17791
- resolve3(null);
17840
+ resolve4(null);
17792
17841
  }
17793
17842
  });
17794
17843
  }
17795
17844
  );
17796
17845
  req.on("timeout", () => {
17797
17846
  req.destroy();
17798
- resolve3(null);
17847
+ resolve4(null);
17799
17848
  });
17800
- req.on("error", () => resolve3(null));
17849
+ req.on("error", () => resolve4(null));
17801
17850
  });
17802
17851
  }
17803
17852
  function notifyIfStale(currentVersion, latest) {
@@ -17817,7 +17866,7 @@ function checkForUpdates() {
17817
17866
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
17818
17867
  if (process.env.CI) return;
17819
17868
  if (!process.stdout.isTTY) return;
17820
- const current = true ? "2.20.1" : null;
17869
+ const current = true ? "2.20.2" : null;
17821
17870
  if (!current) return;
17822
17871
  const cache = readCache();
17823
17872
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.20.1",
3
+ "version": "2.20.2",
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",