codeam-cli 2.23.32 → 2.23.34

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,13 @@ 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.32] — 2026-05-31
8
+
9
+ ### Added
10
+
11
+ - **shared:** Add CommitEntry + BlameLine wire types for git enrichment
12
+ - **cli:** Capture git log + blame per changed file at turn end
13
+
7
14
  ## [2.23.31] — 2026-05-30
8
15
 
9
16
  ### 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.32",
444
+ version: "2.23.34",
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",
@@ -5774,7 +5774,7 @@ function readAnonId() {
5774
5774
  }
5775
5775
  function superProperties() {
5776
5776
  return {
5777
- cliVersion: true ? "2.23.32" : "0.0.0-dev",
5777
+ cliVersion: true ? "2.23.34" : "0.0.0-dev",
5778
5778
  nodeVersion: process.version,
5779
5779
  platform: process.platform,
5780
5780
  arch: process.arch,
@@ -6305,19 +6305,40 @@ var AgentService = class _AgentService {
6305
6305
  this.quietTimer = setTimeout(tick, _AgentService.QUIET_MS);
6306
6306
  }
6307
6307
  /**
6308
- * Write one prompt to the PTY using the existing pacing (text →
6309
- * 50–300 ms → `\r` submit). Marks the agent busy so subsequent
6310
- * `sendCommand` calls queue instead of stacking pastes.
6308
+ * Write one prompt to the PTY and submit it.
6309
+ *
6310
+ * For multi-line text (or any text > 1 line), Claude Code's TUI
6311
+ * triggers bracketed-paste mode and treats the write as a paste:
6312
+ * a `[Pasted text #N]` marker lands in the input field, and any
6313
+ * `\r` we send while the paste boundary is still open is swallowed
6314
+ * as part of the paste content — never reaching the submit
6315
+ * handler. The result is the prompt sitting in the input forever
6316
+ * (the user reported stacking `[Pasted text #N]` markers with no
6317
+ * agent reply).
6318
+ *
6319
+ * Fix: explicitly bracket the paste ourselves (`ESC[200~ <text>
6320
+ * ESC[201~`) so the end marker closes the paste deterministically.
6321
+ * A short delay later we send `\r` — now OUTSIDE the bracket — so
6322
+ * Claude's input handler treats it as Submit, not paste content.
6323
+ * Single-line text doesn't trigger paste mode, so we keep the
6324
+ * legacy `text + \r` path for that case (cheaper, no markers).
6325
+ *
6326
+ * Marks the agent busy so subsequent `sendCommand` calls queue
6327
+ * instead of stacking pastes.
6311
6328
  */
6312
6329
  submitToPty(text) {
6313
6330
  if (!this.strategy) return;
6314
6331
  const s = this.strategy;
6315
6332
  this.agentBusy = true;
6316
6333
  log.trace("agent", `submit text=${text.length}B (queued=${this.pendingInputs.length})`);
6317
- s.write(text);
6318
- const lineCount = text.split("\n").length;
6319
- const delay = Math.min(300, 50 + (lineCount - 1) * 40);
6320
- setTimeout(() => s.write("\r"), delay);
6334
+ const isMultiline = text.includes("\n");
6335
+ if (isMultiline) {
6336
+ s.write(`\x1B[200~${text}\x1B[201~`);
6337
+ setTimeout(() => s.write("\r"), 80);
6338
+ } else {
6339
+ s.write(text);
6340
+ setTimeout(() => s.write("\r"), 50);
6341
+ }
6321
6342
  }
6322
6343
  drainPending() {
6323
6344
  if (!this.strategy || this.pendingInputs.length === 0) return;
@@ -12825,6 +12846,8 @@ function _post2(url, headers, payload) {
12825
12846
  // src/services/file-watcher.service.ts
12826
12847
  var API_BASE5 = resolveApiBaseUrl();
12827
12848
  var DEBOUNCE_MS = 250;
12849
+ var COALESCE_WINDOW_MS = 250;
12850
+ var COALESCE_MAX_HOLD_MS = 2e3;
12828
12851
  var MAX_RETRIES = 2;
12829
12852
  var RETRY_BACKOFF_MS = 300;
12830
12853
  var HISTORY_MAX_COMMITS = 50;
@@ -12908,6 +12931,17 @@ var FileWatcherService = class {
12908
12931
  */
12909
12932
  gitRootByDir = /* @__PURE__ */ new Map();
12910
12933
  stopped = false;
12934
+ /**
12935
+ * Cross-file coalescing buffer. Keyed by absPath so multiple
12936
+ * scheduled emits for the same file collapse to the latest
12937
+ * `changeType`. The buffer drains via `coalesceTimer` after
12938
+ * `COALESCE_WINDOW_MS` of quiescence, or forcibly after
12939
+ * `COALESCE_MAX_HOLD_MS` so the UI never starves during a long
12940
+ * continuous edit.
12941
+ */
12942
+ coalesceBuffer = /* @__PURE__ */ new Map();
12943
+ coalesceTimer = null;
12944
+ coalesceMaxHoldTimer = null;
12911
12945
  /**
12912
12946
  * Start watching `opts.workingDir`. Idempotent (second call is a
12913
12947
  * no-op). Resolves once chokidar's initial scan completes; that
@@ -13009,6 +13043,15 @@ var FileWatcherService = class {
13009
13043
  clearTimeout(entry.timer);
13010
13044
  }
13011
13045
  this.pending.clear();
13046
+ if (this.coalesceTimer) {
13047
+ clearTimeout(this.coalesceTimer);
13048
+ this.coalesceTimer = null;
13049
+ }
13050
+ if (this.coalesceMaxHoldTimer) {
13051
+ clearTimeout(this.coalesceMaxHoldTimer);
13052
+ this.coalesceMaxHoldTimer = null;
13053
+ }
13054
+ this.coalesceBuffer.clear();
13012
13055
  if (this.watcher) {
13013
13056
  try {
13014
13057
  await this.watcher.close();
@@ -13036,7 +13079,7 @@ var FileWatcherService = class {
13036
13079
  if (existing) clearTimeout(existing.timer);
13037
13080
  const timer = setTimeout(() => {
13038
13081
  this.pending.delete(absPath);
13039
- void this.emitForFile(absPath, changeType);
13082
+ this.enqueueForCoalesce(absPath, changeType);
13040
13083
  }, DEBOUNCE_MS);
13041
13084
  this.pending.set(absPath, {
13042
13085
  lastEventAt: Date.now(),
@@ -13044,6 +13087,49 @@ var FileWatcherService = class {
13044
13087
  changeType
13045
13088
  });
13046
13089
  }
13090
+ /**
13091
+ * Drop the file into the cross-file coalescing buffer. The buffer
13092
+ * flushes after `COALESCE_WINDOW_MS` of quiescence (resets on each
13093
+ * new enqueue) or after `COALESCE_MAX_HOLD_MS` regardless. Same
13094
+ * file enqueued twice in a row keeps only the latest `changeType`
13095
+ * (typically the most recent FS event wins).
13096
+ */
13097
+ enqueueForCoalesce(absPath, changeType) {
13098
+ if (this.stopped) return;
13099
+ this.coalesceBuffer.set(absPath, { absPath, changeType });
13100
+ if (this.coalesceTimer) clearTimeout(this.coalesceTimer);
13101
+ this.coalesceTimer = setTimeout(() => {
13102
+ void this.flushCoalesceBuffer();
13103
+ }, COALESCE_WINDOW_MS);
13104
+ if (!this.coalesceMaxHoldTimer) {
13105
+ this.coalesceMaxHoldTimer = setTimeout(() => {
13106
+ void this.flushCoalesceBuffer();
13107
+ }, COALESCE_MAX_HOLD_MS);
13108
+ }
13109
+ }
13110
+ /**
13111
+ * Drain the coalesce buffer. Snapshots the entries up-front so any
13112
+ * emissions that arrive mid-flush (chokidar fires again, agent
13113
+ * keeps writing) land in a fresh buffer rather than competing with
13114
+ * the in-flight one.
13115
+ */
13116
+ async flushCoalesceBuffer() {
13117
+ if (this.coalesceTimer) {
13118
+ clearTimeout(this.coalesceTimer);
13119
+ this.coalesceTimer = null;
13120
+ }
13121
+ if (this.coalesceMaxHoldTimer) {
13122
+ clearTimeout(this.coalesceMaxHoldTimer);
13123
+ this.coalesceMaxHoldTimer = null;
13124
+ }
13125
+ if (this.coalesceBuffer.size === 0) return;
13126
+ const entries = Array.from(this.coalesceBuffer.values());
13127
+ this.coalesceBuffer.clear();
13128
+ for (const entry of entries) {
13129
+ if (this.stopped) return;
13130
+ await this.emitForFile(entry.absPath, entry.changeType);
13131
+ }
13132
+ }
13047
13133
  /**
13048
13134
  * Visible for tests — lets vitest pump a synthetic file event
13049
13135
  * through the debounce + diff + emit pipeline without spinning up
@@ -19106,7 +19192,7 @@ function checkChokidar() {
19106
19192
  }
19107
19193
  async function doctor(args2 = []) {
19108
19194
  const json = args2.includes("--json");
19109
- const cliVersion = true ? "2.23.32" : "0.0.0-dev";
19195
+ const cliVersion = true ? "2.23.34" : "0.0.0-dev";
19110
19196
  const apiBase = resolveApiBaseUrl();
19111
19197
  const diagnosticId = (0, import_node_crypto6.randomUUID)();
19112
19198
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -19305,7 +19391,7 @@ async function completion(args2) {
19305
19391
  // src/commands/version.ts
19306
19392
  var import_picocolors13 = __toESM(require("picocolors"));
19307
19393
  function version2() {
19308
- const v = true ? "2.23.32" : "unknown";
19394
+ const v = true ? "2.23.34" : "unknown";
19309
19395
  console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
19310
19396
  }
19311
19397
 
@@ -19533,7 +19619,7 @@ function checkForUpdates() {
19533
19619
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
19534
19620
  if (process.env.CI) return;
19535
19621
  if (!process.stdout.isTTY) return;
19536
- const current = true ? "2.23.32" : null;
19622
+ const current = true ? "2.23.34" : null;
19537
19623
  if (!current) return;
19538
19624
  const cache = readCache();
19539
19625
  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.32",
3
+ "version": "2.23.34",
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",