codeam-cli 2.23.5 → 2.23.7

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.23.5] — 2026-05-25
8
+
9
+ ### Added
10
+
11
+ - **cli:** Report current git branch on every heartbeat (#193)
12
+
7
13
  ## [2.23.4] — 2026-05-25
8
14
 
9
15
  ### Fixed
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.23.5",
444
+ version: "2.23.7",
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",
@@ -5768,7 +5768,7 @@ function readAnonId() {
5768
5768
  }
5769
5769
  function superProperties() {
5770
5770
  return {
5771
- cliVersion: true ? "2.23.5" : "0.0.0-dev",
5771
+ cliVersion: true ? "2.23.7" : "0.0.0-dev",
5772
5772
  nodeVersion: process.version,
5773
5773
  platform: process.platform,
5774
5774
  arch: process.arch,
@@ -9633,6 +9633,38 @@ function detectListSelector(lines) {
9633
9633
  currentIndex
9634
9634
  };
9635
9635
  }
9636
+ var BANNER_ART_RE = /[█▀▄▌▐▝▘▛▜▙▟▖▗▔▕▮▯▰▱▓▒░◆◇]/;
9637
+ var BANNER_META_RE = /(?:Sonnet|Opus|Haiku|Claude)(?:\s|·|-|\(|$)/i;
9638
+ function detectStartupBanner(lines) {
9639
+ for (let i = 0; i + 2 < lines.length; i++) {
9640
+ if (!/^▐▛[█]+▜▌/.test(lines[i])) continue;
9641
+ if (!/^▝▜[█]+▛▘/.test(lines[i + 1])) continue;
9642
+ if (!lines[i + 2].includes("\u2598\u2598")) continue;
9643
+ return {
9644
+ title: lines[i].replace(/^▐▛[█]+▜▌\s*/, "").trim(),
9645
+ subtitle: lines[i + 1].replace(/^▝▜[█]+▛▘\s*/, "").trim(),
9646
+ path: lines[i + 2].replace(/.*▝▝\s*/, "").trim(),
9647
+ startIdx: i,
9648
+ endIdx: i + 2
9649
+ };
9650
+ }
9651
+ const metaIdx = lines.findIndex(
9652
+ (l) => BANNER_META_RE.test(l) && /(?:Claude|API|Console)/i.test(l) && !BANNER_ART_RE.test(l)
9653
+ );
9654
+ if (metaIdx === -1) return null;
9655
+ let artStart = metaIdx;
9656
+ while (artStart > 0 && BANNER_ART_RE.test(lines[artStart - 1])) artStart--;
9657
+ if (metaIdx - artStart < 2) return null;
9658
+ const pathLine = (lines[metaIdx + 1] ?? "").trim();
9659
+ const path40 = pathLine && !BANNER_ART_RE.test(pathLine) ? pathLine : "";
9660
+ return {
9661
+ title: "",
9662
+ subtitle: lines[metaIdx].trim(),
9663
+ path: path40,
9664
+ startIdx: artStart,
9665
+ endIdx: metaIdx + (path40 ? 1 : 0)
9666
+ };
9667
+ }
9636
9668
 
9637
9669
  // src/agents/claude/runtime.ts
9638
9670
  var ClaudeRuntimeStrategy = class {
@@ -9705,6 +9737,9 @@ var ClaudeRuntimeStrategy = class {
9705
9737
  detectReadyPrompt(lines) {
9706
9738
  return lines.some((l) => /^\?\s.*shortcut/i.test(l.trim()));
9707
9739
  }
9740
+ detectStartupBanner(lines) {
9741
+ return detectStartupBanner(lines);
9742
+ }
9708
9743
  credentialLocator() {
9709
9744
  return claudeCredentialLocator();
9710
9745
  }
@@ -11565,6 +11600,16 @@ var OutputService = class _OutputService {
11565
11600
  emitter;
11566
11601
  runtime;
11567
11602
  lastSentContent = "";
11603
+ /**
11604
+ * Per-session latch — emits the agent's startup banner as a typed
11605
+ * `agent_banner` chunk exactly once, then strips the banner lines
11606
+ * from every subsequent rendered frame so the ASCII art never
11607
+ * reaches the `text` chunk. Lives at the OutputService level (not
11608
+ * per-turn) because the banner is a session-boot artifact —
11609
+ * pair/re-pair spawns a fresh service so the latch naturally
11610
+ * resets.
11611
+ */
11612
+ bannerEmitted = false;
11568
11613
  /**
11569
11614
  * Wall-clock of the most recent tick where the rendered + filtered
11570
11615
  * content actually changed. Most agent TUIs keep redrawing a
@@ -11733,6 +11778,40 @@ var OutputService = class _OutputService {
11733
11778
  this.startTime = Date.now();
11734
11779
  this.pollTimer = setInterval(() => this.tick(), _OutputService.POLL_MS);
11735
11780
  }
11781
+ /**
11782
+ * One-shot banner emit + per-tick strip. Runs the per-agent
11783
+ * `detectStartupBanner` (when the strategy exposes one) and, on
11784
+ * first match, fires a typed `agent_banner` OutputChunk to the
11785
+ * backend. On every tick from then on it slices the banner range
11786
+ * out of the rendered line array so the ASCII / box-art body
11787
+ * doesn't leak into a downstream `text` chunk. Agents that don't
11788
+ * implement the detector pass through unchanged.
11789
+ */
11790
+ handleStartupBanner(lines) {
11791
+ const detect = this.runtime.detectStartupBanner?.bind(this.runtime);
11792
+ if (!detect) return lines;
11793
+ const banner = detect(lines);
11794
+ if (!banner) return lines;
11795
+ if (!this.bannerEmitted) {
11796
+ this.bannerEmitted = true;
11797
+ this.send(
11798
+ {
11799
+ type: "agent_banner",
11800
+ agentId: this.runtime.id,
11801
+ title: banner.title,
11802
+ subtitle: banner.subtitle,
11803
+ path: banner.path,
11804
+ done: true
11805
+ },
11806
+ { critical: true }
11807
+ ).catch(() => {
11808
+ });
11809
+ }
11810
+ return [
11811
+ ...lines.slice(0, banner.startIdx),
11812
+ ...lines.slice(banner.endIdx + 1)
11813
+ ];
11814
+ }
11736
11815
  async send(body, opts = {}) {
11737
11816
  const outcome = await this.emitter.send(body, opts);
11738
11817
  if (outcome.dead && this.pty.isActive) {
@@ -11779,7 +11858,8 @@ var OutputService = class _OutputService {
11779
11858
  return;
11780
11859
  }
11781
11860
  if (elapsed < _OutputService.WARMUP_MS) return;
11782
- const lines = this.runtime.renderToLines?.(this.pty.content) ?? renderLines(this.pty.content);
11861
+ const rendered = this.runtime.renderToLines?.(this.pty.content) ?? renderLines(this.pty.content);
11862
+ const lines = this.handleStartupBanner(rendered);
11783
11863
  const parseLine2 = this.runtime.parseTuiChrome?.bind(this.runtime) ?? (() => null);
11784
11864
  this.steps.ingest(lines, parseLine2);
11785
11865
  const stepsDelta = this.steps.consumeDelta();
@@ -11851,7 +11931,8 @@ var OutputService = class _OutputService {
11851
11931
  }
11852
11932
  }
11853
11933
  finalize() {
11854
- const lines = this.runtime.renderToLines?.(this.pty.content) ?? renderLines(this.pty.content);
11934
+ const rendered = this.runtime.renderToLines?.(this.pty.content) ?? renderLines(this.pty.content);
11935
+ const lines = this.handleStartupBanner(rendered);
11855
11936
  const parseLine2 = this.runtime.parseTuiChrome?.bind(this.runtime) ?? (() => null);
11856
11937
  this.steps.ingest(lines, parseLine2);
11857
11938
  const stepsDelta = this.steps.consumeDelta();
@@ -14235,7 +14316,7 @@ var PROJECT_IGNORE = /* @__PURE__ */ new Set([
14235
14316
  "__pycache__",
14236
14317
  ".DS_Store"
14237
14318
  ]);
14238
- var MAX_TREE_FILES = 5e3;
14319
+ var MAX_TREE_FILES = 5e4;
14239
14320
  var MAX_DIFF_BYTES = 512 * 1024;
14240
14321
  var MAX_GIT_OUTPUT = 256 * 1024;
14241
14322
  async function listProjectFiles(opts = {}) {
@@ -18401,7 +18482,7 @@ function checkChokidar() {
18401
18482
  }
18402
18483
  async function doctor(args2 = []) {
18403
18484
  const json = args2.includes("--json");
18404
- const cliVersion = true ? "2.23.5" : "0.0.0-dev";
18485
+ const cliVersion = true ? "2.23.7" : "0.0.0-dev";
18405
18486
  const apiBase = resolveApiBaseUrl();
18406
18487
  const diagnosticId = (0, import_node_crypto5.randomUUID)();
18407
18488
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -18600,7 +18681,7 @@ async function completion(args2) {
18600
18681
  // src/commands/version.ts
18601
18682
  var import_picocolors13 = __toESM(require("picocolors"));
18602
18683
  function version2() {
18603
- const v = true ? "2.23.5" : "unknown";
18684
+ const v = true ? "2.23.7" : "unknown";
18604
18685
  console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
18605
18686
  }
18606
18687
 
@@ -18828,7 +18909,7 @@ function checkForUpdates() {
18828
18909
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
18829
18910
  if (process.env.CI) return;
18830
18911
  if (!process.stdout.isTTY) return;
18831
- const current = true ? "2.23.5" : null;
18912
+ const current = true ? "2.23.7" : null;
18832
18913
  if (!current) return;
18833
18914
  const cache = readCache();
18834
18915
  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.23.5",
3
+ "version": "2.23.7",
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",