codeam-cli 2.23.29 → 2.23.31

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 +28 -0
  2. package/dist/index.js +104 -20
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,34 @@ 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.30] — 2026-05-30
8
+
9
+ ### Chore
10
+
11
+ - **cli:** Info-level logging for AI summary/insight pipeline
12
+
13
+ ### Fixed
14
+
15
+ - **cli:** DetectInputSuggestion skips Claude TUI box borders
16
+
17
+ ## [2.23.29] — 2026-05-30
18
+
19
+ ### Fixed
20
+
21
+ - **cli:** DetectInputSuggestion skips Claude TUI box borders
22
+
23
+ ## [2.23.28] — 2026-05-30
24
+
25
+ ### Changed
26
+
27
+ - **cli:** Gate idle-suggestion seed on per-agent opt-in
28
+
29
+ ## [2.23.27] — 2026-05-30
30
+
31
+ ### Fixed
32
+
33
+ - **cli:** Seed idle-suggestion detector with the established screen
34
+
7
35
  ## [2.23.26] — 2026-05-30
8
36
 
9
37
  ### Chore
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.29",
444
+ version: "2.23.31",
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.29" : "0.0.0-dev",
5777
+ cliVersion: true ? "2.23.31" : "0.0.0-dev",
5778
5778
  nodeVersion: process.version,
5779
5779
  platform: process.platform,
5780
5780
  arch: process.arch,
@@ -13611,6 +13611,28 @@ var TurnFileAggregator = class {
13611
13611
  repos = [];
13612
13612
  discovering = null;
13613
13613
  stopped = false;
13614
+ /**
13615
+ * Pre-pair worktree baseline. Captured lazily on the first
13616
+ * `flushTurn()` call by running the same `git status` / `git diff`
13617
+ * scan the regular flush uses, then stashing the result here
13618
+ * WITHOUT enqueuing the outbox. Every subsequent flush diff-checks
13619
+ * its entries against this map and only emits files that are
13620
+ * absent from the baseline OR whose `linesAdded` / `linesRemoved`
13621
+ * have moved.
13622
+ *
13623
+ * Why: the legacy first-flush behaviour shipped the entire dirty
13624
+ * worktree at session start, which attributed pre-existing
13625
+ * uncommitted edits to the brand-new sessionId (and confusingly
13626
+ * showed them on the new session's rail with zero hunks, because
13627
+ * the chokidar watcher never saw the changes — they happened
13628
+ * before the watcher booted). Capturing the baseline silently
13629
+ * keeps the rail honest: it now means "files THIS session changed".
13630
+ *
13631
+ * Key is `${repoPath}|${filePath}` so a sibling repo with the same
13632
+ * relative path doesn't collide with another one.
13633
+ */
13634
+ baselineByKey = /* @__PURE__ */ new Map();
13635
+ baselineCaptured = false;
13614
13636
  /** Stop the outbox scheduler. Idempotent. */
13615
13637
  stop() {
13616
13638
  this.stopped = true;
@@ -13642,11 +13664,28 @@ var TurnFileAggregator = class {
13642
13664
  const entries = await collectRepoChangeset(repo);
13643
13665
  if (entries) files.push(...entries);
13644
13666
  }
13645
- if (files.length === 0) {
13646
- log.trace("turnFiles", "no changes detected this turn \u2014 skipping POST");
13667
+ if (!this.baselineCaptured) {
13668
+ this.baselineByKey = new Map(files.map((f) => [baselineKey(f), f]));
13669
+ this.baselineCaptured = true;
13670
+ log.info(
13671
+ "turnFiles",
13672
+ `baseline captured: ${files.length} pre-pair file(s) \u2014 suppressing initial POST`
13673
+ );
13647
13674
  return;
13648
13675
  }
13649
- const chunks = chunkArray(files, MAX_BATCH_SIZE);
13676
+ const novel = files.filter((f) => {
13677
+ const base = this.baselineByKey.get(baselineKey(f));
13678
+ if (!base) return true;
13679
+ return base.linesAdded !== f.linesAdded || base.linesRemoved !== f.linesRemoved || base.fileStatus !== f.fileStatus;
13680
+ });
13681
+ if (novel.length === 0) {
13682
+ log.trace(
13683
+ "turnFiles",
13684
+ `flush matched baseline exactly \u2014 skipping POST (scanned=${files.length})`
13685
+ );
13686
+ return;
13687
+ }
13688
+ const chunks = chunkArray(novel, MAX_BATCH_SIZE);
13650
13689
  for (const chunk of chunks) {
13651
13690
  const entry = {
13652
13691
  turnId: (0, import_crypto2.randomUUID)(),
@@ -13711,6 +13750,9 @@ function chunkArray(arr, size) {
13711
13750
  }
13712
13751
  return out2;
13713
13752
  }
13753
+ function baselineKey(entry) {
13754
+ return `${entry.repoPath}|${entry.filePath}`;
13755
+ }
13714
13756
 
13715
13757
  // src/services/turn-files/repo-dirty-tracker.ts
13716
13758
  var RepoDirtyTracker = class {
@@ -15893,10 +15935,19 @@ var requestLinkCredentialsH = (ctx, _cmd, parsed) => {
15893
15935
  })();
15894
15936
  };
15895
15937
  var requestAiSummaryH = (ctx, _cmd, parsed) => {
15896
- if (!ctx.pluginAuthToken) return;
15897
- if (typeof ctx.runtime.generateOneShot !== "function") return;
15938
+ if (!ctx.pluginAuthToken) {
15939
+ log.info("ai-summary", "no pluginAuthToken \u2014 skipping");
15940
+ return;
15941
+ }
15942
+ if (typeof ctx.runtime.generateOneShot !== "function") {
15943
+ log.info("ai-summary", `runtime ${ctx.runtime.id} has no generateOneShot \u2014 skipping`);
15944
+ return;
15945
+ }
15898
15946
  if (!parsed.prompt || !parsed.turnId || !parsed.stats) {
15899
- log.trace("ai-summary", "missing prompt/turnId/stats \u2014 skipping");
15947
+ log.info(
15948
+ "ai-summary",
15949
+ `missing fields \u2014 prompt=${!!parsed.prompt} turnId=${!!parsed.turnId} stats=${!!parsed.stats}`
15950
+ );
15900
15951
  return;
15901
15952
  }
15902
15953
  const prompt = parsed.prompt;
@@ -15904,12 +15955,19 @@ var requestAiSummaryH = (ctx, _cmd, parsed) => {
15904
15955
  const stats = parsed.stats;
15905
15956
  const pluginAuthToken = ctx.pluginAuthToken;
15906
15957
  void (async () => {
15958
+ log.info("ai-summary", `generateOneShot start turnId=${turnId} promptLen=${prompt.length}`);
15959
+ const startedAt = Date.now();
15907
15960
  const text = await ctx.runtime.generateOneShot(prompt).catch((err) => {
15908
- log.trace("ai-summary", "generateOneShot threw", err);
15961
+ log.info("ai-summary", `generateOneShot threw: ${String(err)}`);
15909
15962
  return null;
15910
15963
  });
15911
- if (!text) return;
15912
- await postAiResult({
15964
+ const tookMs = Date.now() - startedAt;
15965
+ if (!text) {
15966
+ log.info("ai-summary", `generateOneShot returned null after ${tookMs}ms \u2014 skipping POST`);
15967
+ return;
15968
+ }
15969
+ log.info("ai-summary", `generateOneShot ok turnId=${turnId} took=${tookMs}ms textLen=${text.length}`);
15970
+ const result = await postAiResult({
15913
15971
  sessionId: ctx.sessionId,
15914
15972
  pluginId: ctx.pluginId,
15915
15973
  pluginAuthToken,
@@ -15918,26 +15976,47 @@ var requestAiSummaryH = (ctx, _cmd, parsed) => {
15918
15976
  summary: text,
15919
15977
  stats
15920
15978
  });
15979
+ if (result.ok) {
15980
+ log.info("ai-summary", `postAiResult ok turnId=${turnId}`);
15981
+ } else {
15982
+ log.info("ai-summary", `postAiResult failed status=${result.status} msg=${result.message}`);
15983
+ }
15921
15984
  })();
15922
15985
  };
15923
15986
  var requestAiInsightH = (ctx, _cmd, parsed) => {
15924
- if (!ctx.pluginAuthToken) return;
15925
- if (typeof ctx.runtime.generateOneShot !== "function") return;
15987
+ if (!ctx.pluginAuthToken) {
15988
+ log.info("ai-insight", "no pluginAuthToken \u2014 skipping");
15989
+ return;
15990
+ }
15991
+ if (typeof ctx.runtime.generateOneShot !== "function") {
15992
+ log.info("ai-insight", `runtime ${ctx.runtime.id} has no generateOneShot \u2014 skipping`);
15993
+ return;
15994
+ }
15926
15995
  if (!parsed.prompt || !parsed.fileChangeId) {
15927
- log.trace("ai-insight", "missing prompt/fileChangeId \u2014 skipping");
15996
+ log.info(
15997
+ "ai-insight",
15998
+ `missing fields \u2014 prompt=${!!parsed.prompt} fileChangeId=${!!parsed.fileChangeId}`
15999
+ );
15928
16000
  return;
15929
16001
  }
15930
16002
  const prompt = parsed.prompt;
15931
16003
  const fileChangeId = parsed.fileChangeId;
15932
16004
  const pluginAuthToken = ctx.pluginAuthToken;
15933
16005
  void (async () => {
16006
+ log.info("ai-insight", `generateOneShot start fileChangeId=${fileChangeId} promptLen=${prompt.length}`);
16007
+ const startedAt = Date.now();
15934
16008
  const text = await ctx.runtime.generateOneShot(prompt).catch((err) => {
15935
- log.trace("ai-insight", "generateOneShot threw", err);
16009
+ log.info("ai-insight", `generateOneShot threw: ${String(err)}`);
15936
16010
  return null;
15937
16011
  });
15938
- if (!text) return;
16012
+ const tookMs = Date.now() - startedAt;
16013
+ if (!text) {
16014
+ log.info("ai-insight", `generateOneShot returned null after ${tookMs}ms \u2014 skipping POST`);
16015
+ return;
16016
+ }
16017
+ log.info("ai-insight", `generateOneShot ok fileChangeId=${fileChangeId} took=${tookMs}ms textLen=${text.length}`);
15939
16018
  const { summary, reasoning, securityNote } = parseInsightText(text);
15940
- await postAiResult({
16019
+ const result = await postAiResult({
15941
16020
  sessionId: ctx.sessionId,
15942
16021
  pluginId: ctx.pluginId,
15943
16022
  pluginAuthToken,
@@ -15947,6 +16026,11 @@ var requestAiInsightH = (ctx, _cmd, parsed) => {
15947
16026
  reasoning,
15948
16027
  securityNote
15949
16028
  });
16029
+ if (result.ok) {
16030
+ log.info("ai-insight", `postAiResult ok fileChangeId=${fileChangeId}`);
16031
+ } else {
16032
+ log.info("ai-insight", `postAiResult failed status=${result.status} msg=${result.message}`);
16033
+ }
15950
16034
  })();
15951
16035
  };
15952
16036
  function parseInsightText(text) {
@@ -18917,7 +19001,7 @@ function checkChokidar() {
18917
19001
  }
18918
19002
  async function doctor(args2 = []) {
18919
19003
  const json = args2.includes("--json");
18920
- const cliVersion = true ? "2.23.29" : "0.0.0-dev";
19004
+ const cliVersion = true ? "2.23.31" : "0.0.0-dev";
18921
19005
  const apiBase = resolveApiBaseUrl();
18922
19006
  const diagnosticId = (0, import_node_crypto6.randomUUID)();
18923
19007
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -19116,7 +19200,7 @@ async function completion(args2) {
19116
19200
  // src/commands/version.ts
19117
19201
  var import_picocolors13 = __toESM(require("picocolors"));
19118
19202
  function version2() {
19119
- const v = true ? "2.23.29" : "unknown";
19203
+ const v = true ? "2.23.31" : "unknown";
19120
19204
  console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
19121
19205
  }
19122
19206
 
@@ -19344,7 +19428,7 @@ function checkForUpdates() {
19344
19428
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
19345
19429
  if (process.env.CI) return;
19346
19430
  if (!process.stdout.isTTY) return;
19347
- const current = true ? "2.23.29" : null;
19431
+ const current = true ? "2.23.31" : null;
19348
19432
  if (!current) return;
19349
19433
  const cache = readCache();
19350
19434
  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.29",
3
+ "version": "2.23.31",
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",