codeam-cli 2.23.30 → 2.23.32
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 +16 -0
- package/dist/index.js +155 -8
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,22 @@ 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.31] — 2026-05-30
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **cli:** TurnFileAggregator captures pre-pair baseline silently
|
|
12
|
+
|
|
13
|
+
## [2.23.30] — 2026-05-30
|
|
14
|
+
|
|
15
|
+
### Chore
|
|
16
|
+
|
|
17
|
+
- **cli:** Info-level logging for AI summary/insight pipeline
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- **cli:** DetectInputSuggestion skips Claude TUI box borders
|
|
22
|
+
|
|
7
23
|
## [2.23.29] — 2026-05-30
|
|
8
24
|
|
|
9
25
|
### 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.
|
|
444
|
+
version: "2.23.32",
|
|
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.
|
|
5777
|
+
cliVersion: true ? "2.23.32" : "0.0.0-dev",
|
|
5778
5778
|
nodeVersion: process.version,
|
|
5779
5779
|
platform: process.platform,
|
|
5780
5780
|
arch: process.arch,
|
|
@@ -12827,6 +12827,8 @@ var API_BASE5 = resolveApiBaseUrl();
|
|
|
12827
12827
|
var DEBOUNCE_MS = 250;
|
|
12828
12828
|
var MAX_RETRIES = 2;
|
|
12829
12829
|
var RETRY_BACKOFF_MS = 300;
|
|
12830
|
+
var HISTORY_MAX_COMMITS = 50;
|
|
12831
|
+
var BLAME_MAX_LINES = 500;
|
|
12830
12832
|
var WINDOWS_LEGACY_JUNCTIONS = [
|
|
12831
12833
|
/[\\/]Application Data([\\/]|$)/i,
|
|
12832
12834
|
/[\\/]Cookies([\\/]|$)/i,
|
|
@@ -13137,6 +13139,31 @@ var FileWatcherService = class {
|
|
|
13137
13139
|
linesRemoved: hunk.linesRemoved
|
|
13138
13140
|
});
|
|
13139
13141
|
}
|
|
13142
|
+
await this.emitGitEnrichment(gitRoot, relPathInRepo, repoPath, repoName, fileStatus);
|
|
13143
|
+
}
|
|
13144
|
+
async emitGitEnrichment(gitRoot, relPathInRepo, repoPath, repoName, fileStatus) {
|
|
13145
|
+
if (this.stopped) return;
|
|
13146
|
+
const commits = await captureHistory(gitRoot, relPathInRepo, HISTORY_MAX_COMMITS);
|
|
13147
|
+
if (this.stopped) return;
|
|
13148
|
+
await this.postReviewHistory({
|
|
13149
|
+
sessionId: this.opts.sessionId,
|
|
13150
|
+
pluginId: this.opts.pluginId,
|
|
13151
|
+
filePath: relPathInRepo,
|
|
13152
|
+
repoPath,
|
|
13153
|
+
repoName,
|
|
13154
|
+
commits
|
|
13155
|
+
});
|
|
13156
|
+
if (this.stopped || fileStatus === "deleted") return;
|
|
13157
|
+
const blameLines = await captureBlame(gitRoot, relPathInRepo, BLAME_MAX_LINES);
|
|
13158
|
+
if (this.stopped) return;
|
|
13159
|
+
await this.postReviewBlame({
|
|
13160
|
+
sessionId: this.opts.sessionId,
|
|
13161
|
+
pluginId: this.opts.pluginId,
|
|
13162
|
+
filePath: relPathInRepo,
|
|
13163
|
+
repoPath,
|
|
13164
|
+
repoName,
|
|
13165
|
+
lines: blameLines
|
|
13166
|
+
});
|
|
13140
13167
|
}
|
|
13141
13168
|
/**
|
|
13142
13169
|
* Compute the unified diff for a single path relative to the
|
|
@@ -13173,6 +13200,12 @@ var FileWatcherService = class {
|
|
|
13173
13200
|
async postReviewHunk(body) {
|
|
13174
13201
|
await this.postWithRetries(`${this.apiBase}/api/review/hunks`, body);
|
|
13175
13202
|
}
|
|
13203
|
+
async postReviewHistory(body) {
|
|
13204
|
+
await this.postWithRetries(`${this.apiBase}/api/review/history`, body);
|
|
13205
|
+
}
|
|
13206
|
+
async postReviewBlame(body) {
|
|
13207
|
+
await this.postWithRetries(`${this.apiBase}/api/review/blame`, body);
|
|
13208
|
+
}
|
|
13176
13209
|
async postWithRetries(url, body) {
|
|
13177
13210
|
const payload = JSON.stringify(body);
|
|
13178
13211
|
const headers = {
|
|
@@ -13223,6 +13256,78 @@ var FileWatcherService = class {
|
|
|
13223
13256
|
function sleep(ms) {
|
|
13224
13257
|
return new Promise((r) => setTimeout(r, ms));
|
|
13225
13258
|
}
|
|
13259
|
+
async function captureHistory(repoRoot, relPath, maxCommits) {
|
|
13260
|
+
const out2 = await runGit(repoRoot, [
|
|
13261
|
+
"log",
|
|
13262
|
+
`--max-count=${maxCommits}`,
|
|
13263
|
+
"--no-color",
|
|
13264
|
+
"--format=%H%x09%an%x09%ae%x09%aI%x09%s",
|
|
13265
|
+
"--",
|
|
13266
|
+
relPath
|
|
13267
|
+
]);
|
|
13268
|
+
if (!out2) return [];
|
|
13269
|
+
const commits = [];
|
|
13270
|
+
for (const line of out2.split("\n")) {
|
|
13271
|
+
if (!line) continue;
|
|
13272
|
+
const cols = line.split(" ");
|
|
13273
|
+
if (cols.length < 5) continue;
|
|
13274
|
+
const [sha, authorName, authorEmail, committedAt, ...subjectParts] = cols;
|
|
13275
|
+
commits.push({
|
|
13276
|
+
sha,
|
|
13277
|
+
authorName: authorName ?? "",
|
|
13278
|
+
authorEmail: authorEmail ?? "",
|
|
13279
|
+
committedAt: committedAt ?? "",
|
|
13280
|
+
subject: subjectParts.join(" ")
|
|
13281
|
+
});
|
|
13282
|
+
}
|
|
13283
|
+
return commits;
|
|
13284
|
+
}
|
|
13285
|
+
async function captureBlame(repoRoot, relPath, maxLines) {
|
|
13286
|
+
const out2 = await runGit(repoRoot, [
|
|
13287
|
+
"blame",
|
|
13288
|
+
"--line-porcelain",
|
|
13289
|
+
"--no-progress",
|
|
13290
|
+
"-L",
|
|
13291
|
+
`1,${maxLines}`,
|
|
13292
|
+
"--",
|
|
13293
|
+
relPath
|
|
13294
|
+
]);
|
|
13295
|
+
if (!out2) return [];
|
|
13296
|
+
const lines = [];
|
|
13297
|
+
const blocks = out2.split(/(?=^[0-9a-f]{40} )/m);
|
|
13298
|
+
for (const block of blocks) {
|
|
13299
|
+
if (!block) continue;
|
|
13300
|
+
const blockLines = block.split("\n");
|
|
13301
|
+
const headerMatch = blockLines[0].match(/^([0-9a-f]{40}) \d+ (\d+)/);
|
|
13302
|
+
if (!headerMatch) continue;
|
|
13303
|
+
const sha = headerMatch[1];
|
|
13304
|
+
const lineNumber = parseInt(headerMatch[2], 10);
|
|
13305
|
+
let authorName = "";
|
|
13306
|
+
let authorTime = null;
|
|
13307
|
+
let text = "";
|
|
13308
|
+
for (let i = 1; i < blockLines.length; i += 1) {
|
|
13309
|
+
const bl = blockLines[i];
|
|
13310
|
+
if (bl.startsWith("author ")) {
|
|
13311
|
+
authorName = bl.slice(7);
|
|
13312
|
+
} else if (bl.startsWith("author-time ")) {
|
|
13313
|
+
const parsed = parseInt(bl.slice(12), 10);
|
|
13314
|
+
if (!Number.isNaN(parsed)) authorTime = parsed;
|
|
13315
|
+
} else if (bl.startsWith(" ")) {
|
|
13316
|
+
text = bl.slice(1);
|
|
13317
|
+
break;
|
|
13318
|
+
}
|
|
13319
|
+
}
|
|
13320
|
+
const committedAt = authorTime !== null ? new Date(authorTime * 1e3).toISOString() : "";
|
|
13321
|
+
lines.push({
|
|
13322
|
+
lineNumber,
|
|
13323
|
+
sha,
|
|
13324
|
+
authorName,
|
|
13325
|
+
committedAt,
|
|
13326
|
+
text
|
|
13327
|
+
});
|
|
13328
|
+
}
|
|
13329
|
+
return lines;
|
|
13330
|
+
}
|
|
13226
13331
|
async function runGit(cwd, args2, opts = {}) {
|
|
13227
13332
|
return _runGit(cwd, args2, opts);
|
|
13228
13333
|
}
|
|
@@ -13611,6 +13716,28 @@ var TurnFileAggregator = class {
|
|
|
13611
13716
|
repos = [];
|
|
13612
13717
|
discovering = null;
|
|
13613
13718
|
stopped = false;
|
|
13719
|
+
/**
|
|
13720
|
+
* Pre-pair worktree baseline. Captured lazily on the first
|
|
13721
|
+
* `flushTurn()` call by running the same `git status` / `git diff`
|
|
13722
|
+
* scan the regular flush uses, then stashing the result here
|
|
13723
|
+
* WITHOUT enqueuing the outbox. Every subsequent flush diff-checks
|
|
13724
|
+
* its entries against this map and only emits files that are
|
|
13725
|
+
* absent from the baseline OR whose `linesAdded` / `linesRemoved`
|
|
13726
|
+
* have moved.
|
|
13727
|
+
*
|
|
13728
|
+
* Why: the legacy first-flush behaviour shipped the entire dirty
|
|
13729
|
+
* worktree at session start, which attributed pre-existing
|
|
13730
|
+
* uncommitted edits to the brand-new sessionId (and confusingly
|
|
13731
|
+
* showed them on the new session's rail with zero hunks, because
|
|
13732
|
+
* the chokidar watcher never saw the changes — they happened
|
|
13733
|
+
* before the watcher booted). Capturing the baseline silently
|
|
13734
|
+
* keeps the rail honest: it now means "files THIS session changed".
|
|
13735
|
+
*
|
|
13736
|
+
* Key is `${repoPath}|${filePath}` so a sibling repo with the same
|
|
13737
|
+
* relative path doesn't collide with another one.
|
|
13738
|
+
*/
|
|
13739
|
+
baselineByKey = /* @__PURE__ */ new Map();
|
|
13740
|
+
baselineCaptured = false;
|
|
13614
13741
|
/** Stop the outbox scheduler. Idempotent. */
|
|
13615
13742
|
stop() {
|
|
13616
13743
|
this.stopped = true;
|
|
@@ -13642,11 +13769,28 @@ var TurnFileAggregator = class {
|
|
|
13642
13769
|
const entries = await collectRepoChangeset(repo);
|
|
13643
13770
|
if (entries) files.push(...entries);
|
|
13644
13771
|
}
|
|
13645
|
-
if (
|
|
13646
|
-
|
|
13772
|
+
if (!this.baselineCaptured) {
|
|
13773
|
+
this.baselineByKey = new Map(files.map((f) => [baselineKey(f), f]));
|
|
13774
|
+
this.baselineCaptured = true;
|
|
13775
|
+
log.info(
|
|
13776
|
+
"turnFiles",
|
|
13777
|
+
`baseline captured: ${files.length} pre-pair file(s) \u2014 suppressing initial POST`
|
|
13778
|
+
);
|
|
13779
|
+
return;
|
|
13780
|
+
}
|
|
13781
|
+
const novel = files.filter((f) => {
|
|
13782
|
+
const base = this.baselineByKey.get(baselineKey(f));
|
|
13783
|
+
if (!base) return true;
|
|
13784
|
+
return base.linesAdded !== f.linesAdded || base.linesRemoved !== f.linesRemoved || base.fileStatus !== f.fileStatus;
|
|
13785
|
+
});
|
|
13786
|
+
if (novel.length === 0) {
|
|
13787
|
+
log.trace(
|
|
13788
|
+
"turnFiles",
|
|
13789
|
+
`flush matched baseline exactly \u2014 skipping POST (scanned=${files.length})`
|
|
13790
|
+
);
|
|
13647
13791
|
return;
|
|
13648
13792
|
}
|
|
13649
|
-
const chunks = chunkArray(
|
|
13793
|
+
const chunks = chunkArray(novel, MAX_BATCH_SIZE);
|
|
13650
13794
|
for (const chunk of chunks) {
|
|
13651
13795
|
const entry = {
|
|
13652
13796
|
turnId: (0, import_crypto2.randomUUID)(),
|
|
@@ -13711,6 +13855,9 @@ function chunkArray(arr, size) {
|
|
|
13711
13855
|
}
|
|
13712
13856
|
return out2;
|
|
13713
13857
|
}
|
|
13858
|
+
function baselineKey(entry) {
|
|
13859
|
+
return `${entry.repoPath}|${entry.filePath}`;
|
|
13860
|
+
}
|
|
13714
13861
|
|
|
13715
13862
|
// src/services/turn-files/repo-dirty-tracker.ts
|
|
13716
13863
|
var RepoDirtyTracker = class {
|
|
@@ -18959,7 +19106,7 @@ function checkChokidar() {
|
|
|
18959
19106
|
}
|
|
18960
19107
|
async function doctor(args2 = []) {
|
|
18961
19108
|
const json = args2.includes("--json");
|
|
18962
|
-
const cliVersion = true ? "2.23.
|
|
19109
|
+
const cliVersion = true ? "2.23.32" : "0.0.0-dev";
|
|
18963
19110
|
const apiBase = resolveApiBaseUrl();
|
|
18964
19111
|
const diagnosticId = (0, import_node_crypto6.randomUUID)();
|
|
18965
19112
|
log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
|
|
@@ -19158,7 +19305,7 @@ async function completion(args2) {
|
|
|
19158
19305
|
// src/commands/version.ts
|
|
19159
19306
|
var import_picocolors13 = __toESM(require("picocolors"));
|
|
19160
19307
|
function version2() {
|
|
19161
|
-
const v = true ? "2.23.
|
|
19308
|
+
const v = true ? "2.23.32" : "unknown";
|
|
19162
19309
|
console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
|
|
19163
19310
|
}
|
|
19164
19311
|
|
|
@@ -19386,7 +19533,7 @@ function checkForUpdates() {
|
|
|
19386
19533
|
if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
|
|
19387
19534
|
if (process.env.CI) return;
|
|
19388
19535
|
if (!process.stdout.isTTY) return;
|
|
19389
|
-
const current = true ? "2.23.
|
|
19536
|
+
const current = true ? "2.23.32" : null;
|
|
19390
19537
|
if (!current) return;
|
|
19391
19538
|
const cache = readCache();
|
|
19392
19539
|
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.
|
|
3
|
+
"version": "2.23.32",
|
|
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",
|