codeam-cli 2.20.0 → 2.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,12 @@ 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.0] — 2026-05-24
8
+
9
+ ### Added
10
+
11
+ - **shared:** CODEAM_TEST_MODE env var to route clients at dev preview (#176)
12
+
7
13
  ## [2.19.0] — 2026-05-24
8
14
 
9
15
  ### 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.0",
444
+ version: "2.20.1",
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",
@@ -5740,7 +5740,7 @@ function readAnonId() {
5740
5740
  }
5741
5741
  function superProperties() {
5742
5742
  return {
5743
- cliVersion: true ? "2.20.0" : "0.0.0-dev",
5743
+ cliVersion: true ? "2.20.1" : "0.0.0-dev",
5744
5744
  nodeVersion: process.version,
5745
5745
  platform: process.platform,
5746
5746
  arch: process.arch,
@@ -9531,6 +9531,9 @@ var ClaudeRuntimeStrategy = class {
9531
9531
  detectInteractivePrompt(lines) {
9532
9532
  return detectSelector(lines) ?? detectListSelector(lines);
9533
9533
  }
9534
+ detectReadyPrompt(lines) {
9535
+ return lines.some((l) => /^\?\s.*shortcut/i.test(l.trim()));
9536
+ }
9534
9537
  credentialLocator() {
9535
9538
  return claudeCredentialLocator();
9536
9539
  }
@@ -10411,6 +10414,9 @@ var CodexRuntimeStrategy = class {
10411
10414
  detectInteractivePrompt(lines) {
10412
10415
  return detectCodexSelector(lines);
10413
10416
  }
10417
+ detectReadyPrompt(lines) {
10418
+ return lines.some((l) => /[│┃]\s*›/u.test(l));
10419
+ }
10414
10420
  credentialLocator() {
10415
10421
  return codexCredentialLocator();
10416
10422
  }
@@ -10858,6 +10864,9 @@ var CursorRuntimeStrategy = class {
10858
10864
  detectInteractivePrompt(lines) {
10859
10865
  return detectCursorSelector(lines);
10860
10866
  }
10867
+ detectReadyPrompt(lines) {
10868
+ return lines.some((l) => /[│┃]\s*[›>]\s/u.test(l));
10869
+ }
10861
10870
  credentialLocator() {
10862
10871
  return cursorCredentialLocator();
10863
10872
  }
@@ -11065,6 +11074,12 @@ var AiderRuntimeStrategy = class {
11065
11074
  detectInteractivePrompt(lines) {
11066
11075
  return detectAiderSelector(lines);
11067
11076
  }
11077
+ detectReadyPrompt(lines) {
11078
+ return lines.some((l) => {
11079
+ const t2 = l.replace(/\x1B\[[^@-~]*[@-~]/g, "").trim();
11080
+ return t2 === ">" || /^>\s*$/.test(t2) || /^\.\.\.\s*>\s*$/.test(t2);
11081
+ });
11082
+ }
11068
11083
  credentialLocator() {
11069
11084
  return aiderCredentialLocator();
11070
11085
  }
@@ -11362,6 +11377,20 @@ var OutputService = class _OutputService {
11362
11377
  emitter;
11363
11378
  runtime;
11364
11379
  lastSentContent = "";
11380
+ /**
11381
+ * Wall-clock of the most recent tick where the rendered + filtered
11382
+ * content actually changed. Most agent TUIs keep redrawing a
11383
+ * spinner + input prompt after the response is settled (Claude:
11384
+ * `? for shortcuts`; Codex: ratatui input bar), which keeps
11385
+ * `pty.lastPushTime` moving and prevents the PTY-idle heuristic
11386
+ * from ever crossing the IDLE_MS threshold — so the turn never
11387
+ * finalises and `done: true` never reaches the webapp's
11388
+ * canonical-refresh path. Tracking content-stability separately
11389
+ * closes that hole: PTY can churn all it wants, but once the
11390
+ * filtered TUI output stops changing for a beat we know the
11391
+ * agent is done.
11392
+ */
11393
+ lastContentChangeAt = 0;
11365
11394
  pollTimer = null;
11366
11395
  startTime = 0;
11367
11396
  terminalTurnPending = false;
@@ -11375,6 +11404,22 @@ var OutputService = class _OutputService {
11375
11404
  static IDLE_MS = 3e3;
11376
11405
  /** Same threshold but tighter for selectors (UI is ready to interact immediately). */
11377
11406
  static SELECTOR_IDLE_MS = 1500;
11407
+ /**
11408
+ * Content-stable threshold. When the rendered + filtered content
11409
+ * hasn't changed for this long AND we can see Claude's "? for
11410
+ * shortcuts" prompt re-drawn at the bottom (= back to input
11411
+ * state), we know the response is settled even though the PTY
11412
+ * itself is still pushing spinner / status redraws. Tighter than
11413
+ * IDLE_MS because the ready-prompt is a strong signal on its own.
11414
+ */
11415
+ static READY_STABLE_MS = 800;
11416
+ /**
11417
+ * Hard content-stable fallback. If the filtered content has been
11418
+ * unchanged for this long, finalize regardless of the ready
11419
+ * prompt — covers cases where Claude's TUI doesn't redraw the
11420
+ * shortcuts line (older versions, headless runs).
11421
+ */
11422
+ static CONTENT_STABLE_MS = 8e3;
11378
11423
  /**
11379
11424
  * Grace period before tick processes anything — Claude needs ~100-
11380
11425
  * 200 ms after `\r` to clear the input echo and re-render the TUI.
@@ -11496,6 +11541,7 @@ var OutputService = class _OutputService {
11496
11541
  this.pty.activate();
11497
11542
  this.steps.reset();
11498
11543
  this.lastSentContent = "";
11544
+ this.lastContentChangeAt = 0;
11499
11545
  this.startTime = Date.now();
11500
11546
  this.pollTimer = setInterval(() => this.tick(), _OutputService.POLL_MS);
11501
11547
  }
@@ -11588,18 +11634,32 @@ var OutputService = class _OutputService {
11588
11634
  return;
11589
11635
  }
11590
11636
  const idleMs = this.pty.lastPushTime > 0 ? now - this.pty.lastPushTime : elapsed;
11637
+ if (content !== this.lastSentContent) {
11638
+ this.lastContentChangeAt = now;
11639
+ this.lastSentContent = content;
11640
+ this.send({ type: "text", content, done: false }).catch(() => {
11641
+ });
11642
+ }
11643
+ const contentStableMs = this.lastContentChangeAt > 0 ? now - this.lastContentChangeAt : 0;
11644
+ const readyPrompt = this.runtime.detectReadyPrompt?.(lines) ?? false;
11591
11645
  log.trace(
11592
11646
  "outputSvc",
11593
- `tick content (raw=${this.pty.size}B lines=${lines.length} content=${content.length} idleMs=${idleMs})`
11647
+ `tick content (raw=${this.pty.size}B lines=${lines.length} content=${content.length} idleMs=${idleMs} stableMs=${contentStableMs} ready=${readyPrompt})`
11594
11648
  );
11595
11649
  if (idleMs >= _OutputService.IDLE_MS) {
11650
+ log.trace("outputSvc", `finalize: idleMs=${idleMs}`);
11596
11651
  this.finalize();
11597
11652
  return;
11598
11653
  }
11599
- if (content !== this.lastSentContent) {
11600
- this.lastSentContent = content;
11601
- this.send({ type: "text", content, done: false }).catch(() => {
11602
- });
11654
+ if (readyPrompt && contentStableMs >= _OutputService.READY_STABLE_MS) {
11655
+ log.trace("outputSvc", `finalize: readyPrompt + stableMs=${contentStableMs}`);
11656
+ this.finalize();
11657
+ return;
11658
+ }
11659
+ if (contentStableMs >= _OutputService.CONTENT_STABLE_MS) {
11660
+ log.trace("outputSvc", `finalize: stableMs=${contentStableMs} (fallback)`);
11661
+ this.finalize();
11662
+ return;
11603
11663
  }
11604
11664
  }
11605
11665
  finalize() {
@@ -17330,7 +17390,7 @@ function checkChokidar() {
17330
17390
  }
17331
17391
  async function doctor(args2 = []) {
17332
17392
  const json = args2.includes("--json");
17333
- const cliVersion = true ? "2.20.0" : "0.0.0-dev";
17393
+ const cliVersion = true ? "2.20.1" : "0.0.0-dev";
17334
17394
  const apiBase = resolveApiBaseUrl();
17335
17395
  const diagnosticId = (0, import_node_crypto5.randomUUID)();
17336
17396
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -17529,7 +17589,7 @@ async function completion(args2) {
17529
17589
  // src/commands/version.ts
17530
17590
  var import_picocolors13 = __toESM(require("picocolors"));
17531
17591
  function version2() {
17532
- const v = true ? "2.20.0" : "unknown";
17592
+ const v = true ? "2.20.1" : "unknown";
17533
17593
  console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
17534
17594
  }
17535
17595
 
@@ -17757,7 +17817,7 @@ function checkForUpdates() {
17757
17817
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
17758
17818
  if (process.env.CI) return;
17759
17819
  if (!process.stdout.isTTY) return;
17760
- const current = true ? "2.20.0" : null;
17820
+ const current = true ? "2.20.1" : null;
17761
17821
  if (!current) return;
17762
17822
  const cache = readCache();
17763
17823
  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.0",
3
+ "version": "2.20.1",
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",