@buildautomaton/cli 0.1.33 → 0.1.35
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/dist/cli.js +407 -249
- package/dist/cli.js.map +4 -4
- package/dist/index.js +403 -245
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -8000,8 +8000,8 @@ function getElementAtPath(obj, path41) {
|
|
|
8000
8000
|
}
|
|
8001
8001
|
function promiseAllObject(promisesObj) {
|
|
8002
8002
|
const keys = Object.keys(promisesObj);
|
|
8003
|
-
const
|
|
8004
|
-
return Promise.all(
|
|
8003
|
+
const promises5 = keys.map((key) => promisesObj[key]);
|
|
8004
|
+
return Promise.all(promises5).then((results) => {
|
|
8005
8005
|
const resolvedObj = {};
|
|
8006
8006
|
for (let i = 0; i < keys.length; i++) {
|
|
8007
8007
|
resolvedObj[keys[i]] = results[i];
|
|
@@ -22158,20 +22158,22 @@ function createWsBridge(options) {
|
|
|
22158
22158
|
onOpen?.();
|
|
22159
22159
|
});
|
|
22160
22160
|
ws.on("message", (raw) => {
|
|
22161
|
-
|
|
22162
|
-
|
|
22163
|
-
|
|
22164
|
-
|
|
22165
|
-
|
|
22166
|
-
|
|
22167
|
-
|
|
22168
|
-
|
|
22169
|
-
|
|
22161
|
+
setImmediate(() => {
|
|
22162
|
+
try {
|
|
22163
|
+
let data;
|
|
22164
|
+
if (typeof raw === "string") {
|
|
22165
|
+
data = JSON.parse(raw);
|
|
22166
|
+
} else if (Buffer.isBuffer(raw) || raw instanceof ArrayBuffer) {
|
|
22167
|
+
const str = Buffer.isBuffer(raw) ? raw.toString("utf8") : Buffer.from(raw).toString("utf8");
|
|
22168
|
+
data = JSON.parse(str);
|
|
22169
|
+
} else {
|
|
22170
|
+
data = raw;
|
|
22171
|
+
}
|
|
22172
|
+
onMessage?.(data);
|
|
22173
|
+
} catch {
|
|
22174
|
+
onMessage?.(raw);
|
|
22170
22175
|
}
|
|
22171
|
-
|
|
22172
|
-
} catch {
|
|
22173
|
-
onMessage?.(raw);
|
|
22174
|
-
}
|
|
22176
|
+
});
|
|
22175
22177
|
});
|
|
22176
22178
|
ws.on("close", (code, reason) => {
|
|
22177
22179
|
disposeClientPing();
|
|
@@ -23962,7 +23964,7 @@ function installBridgeProcessResilience() {
|
|
|
23962
23964
|
}
|
|
23963
23965
|
|
|
23964
23966
|
// src/cli-version.ts
|
|
23965
|
-
var CLI_VERSION = "0.1.
|
|
23967
|
+
var CLI_VERSION = "0.1.35".length > 0 ? "0.1.35" : "0.0.0-dev";
|
|
23966
23968
|
|
|
23967
23969
|
// src/connection/heartbeat/constants.ts
|
|
23968
23970
|
var BRIDGE_APP_HEARTBEAT_INTERVAL_MS = 1e4;
|
|
@@ -25550,17 +25552,13 @@ function resolveSessionParentPathForAgentProcess(resolvedSessionParentPath) {
|
|
|
25550
25552
|
return getBridgeRoot();
|
|
25551
25553
|
}
|
|
25552
25554
|
|
|
25553
|
-
// src/git/session-git-queue.ts
|
|
25554
|
-
import { execFile as execFile7 } from "node:child_process";
|
|
25555
|
+
// src/git/snapshot/session-git-queue.ts
|
|
25555
25556
|
import { readFile, stat } from "node:fs/promises";
|
|
25556
|
-
import { promisify as promisify7 } from "node:util";
|
|
25557
25557
|
import * as path15 from "node:path";
|
|
25558
25558
|
|
|
25559
|
-
// src/git/pre-turn-snapshot.ts
|
|
25559
|
+
// src/git/snapshot/pre-turn-snapshot.ts
|
|
25560
25560
|
import * as fs14 from "node:fs";
|
|
25561
25561
|
import * as path14 from "node:path";
|
|
25562
|
-
import { execFile as execFile6 } from "node:child_process";
|
|
25563
|
-
import { promisify as promisify6 } from "node:util";
|
|
25564
25562
|
|
|
25565
25563
|
// src/git/discover-repos.ts
|
|
25566
25564
|
import * as fs13 from "node:fs";
|
|
@@ -30125,12 +30123,68 @@ function gitInstanceFactory(baseDir, options) {
|
|
|
30125
30123
|
init_git_response_error();
|
|
30126
30124
|
var simpleGit = gitInstanceFactory;
|
|
30127
30125
|
|
|
30126
|
+
// src/git/git-runtime.ts
|
|
30127
|
+
var activeGitChildProcesses = /* @__PURE__ */ new Set();
|
|
30128
|
+
function abortActiveGitChildProcesses() {
|
|
30129
|
+
for (const child of activeGitChildProcesses) {
|
|
30130
|
+
try {
|
|
30131
|
+
child.kill("SIGTERM");
|
|
30132
|
+
} catch {
|
|
30133
|
+
}
|
|
30134
|
+
}
|
|
30135
|
+
}
|
|
30136
|
+
var GitOperationAbortedError = class extends Error {
|
|
30137
|
+
constructor(message = "Git operation aborted") {
|
|
30138
|
+
super(message);
|
|
30139
|
+
this.name = "GitOperationAbortedError";
|
|
30140
|
+
}
|
|
30141
|
+
};
|
|
30142
|
+
function throwIfGitShutdownRequested() {
|
|
30143
|
+
if (isCliImmediateShutdownRequested()) {
|
|
30144
|
+
abortActiveGitChildProcesses();
|
|
30145
|
+
throw new GitOperationAbortedError();
|
|
30146
|
+
}
|
|
30147
|
+
}
|
|
30148
|
+
async function runGitTask(fn) {
|
|
30149
|
+
throwIfGitShutdownRequested();
|
|
30150
|
+
try {
|
|
30151
|
+
const result = await fn();
|
|
30152
|
+
throwIfGitShutdownRequested();
|
|
30153
|
+
return result;
|
|
30154
|
+
} catch (e) {
|
|
30155
|
+
if (isCliImmediateShutdownRequested()) {
|
|
30156
|
+
throw new GitOperationAbortedError();
|
|
30157
|
+
}
|
|
30158
|
+
throw e;
|
|
30159
|
+
}
|
|
30160
|
+
}
|
|
30161
|
+
async function yieldToEventLoop2() {
|
|
30162
|
+
throwIfGitShutdownRequested();
|
|
30163
|
+
await new Promise((resolve18) => {
|
|
30164
|
+
setImmediate(resolve18);
|
|
30165
|
+
});
|
|
30166
|
+
throwIfGitShutdownRequested();
|
|
30167
|
+
}
|
|
30168
|
+
async function forEachWithGitYield(items, fn) {
|
|
30169
|
+
for (let i = 0; i < items.length; i++) {
|
|
30170
|
+
if (i > 0 || items.length > 1) await yieldToEventLoop2();
|
|
30171
|
+
await fn(items[i], i);
|
|
30172
|
+
}
|
|
30173
|
+
}
|
|
30174
|
+
|
|
30128
30175
|
// src/git/cli-simple-git.ts
|
|
30129
30176
|
function cliSimpleGit(baseDir) {
|
|
30130
|
-
const git = simpleGit({
|
|
30177
|
+
const git = simpleGit({
|
|
30178
|
+
baseDir,
|
|
30179
|
+
trimmed: true,
|
|
30180
|
+
spawnOptions: {}
|
|
30181
|
+
});
|
|
30131
30182
|
git.outputHandler((command, stdout, stderr) => {
|
|
30132
30183
|
const trace = isCliTrace();
|
|
30133
30184
|
const onChunk = (label) => (chunk) => {
|
|
30185
|
+
if (isCliImmediateShutdownRequested()) {
|
|
30186
|
+
abortActiveGitChildProcesses();
|
|
30187
|
+
}
|
|
30134
30188
|
const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
30135
30189
|
const line = text.replace(/\s+$/, "");
|
|
30136
30190
|
if (trace && line) {
|
|
@@ -30219,17 +30273,37 @@ async function discoverGitReposUnderRoot(rootPath) {
|
|
|
30219
30273
|
return out;
|
|
30220
30274
|
}
|
|
30221
30275
|
|
|
30222
|
-
// src/git/
|
|
30276
|
+
// src/git/git-exec.ts
|
|
30277
|
+
import { execFile as execFile6, spawn as spawn4 } from "node:child_process";
|
|
30278
|
+
import { promisify as promisify6 } from "node:util";
|
|
30223
30279
|
var execFileAsync5 = promisify6(execFile6);
|
|
30280
|
+
async function execGitFile(args, options) {
|
|
30281
|
+
throwIfGitShutdownRequested();
|
|
30282
|
+
try {
|
|
30283
|
+
const result = await execFileAsync5("git", args, {
|
|
30284
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
30285
|
+
...options
|
|
30286
|
+
});
|
|
30287
|
+
throwIfGitShutdownRequested();
|
|
30288
|
+
return {
|
|
30289
|
+
stdout: String(result.stdout ?? ""),
|
|
30290
|
+
stderr: String(result.stderr ?? "")
|
|
30291
|
+
};
|
|
30292
|
+
} catch (e) {
|
|
30293
|
+
if (isCliImmediateShutdownRequested()) {
|
|
30294
|
+
throw new GitOperationAbortedError();
|
|
30295
|
+
}
|
|
30296
|
+
throw e;
|
|
30297
|
+
}
|
|
30298
|
+
}
|
|
30299
|
+
|
|
30300
|
+
// src/git/snapshot/pre-turn-snapshot.ts
|
|
30224
30301
|
function snapshotsDirForCwd(agentCwd) {
|
|
30225
30302
|
return path14.join(agentCwd, ".buildautomaton", "snapshots");
|
|
30226
30303
|
}
|
|
30227
30304
|
async function gitStashCreate(repoRoot, log2) {
|
|
30228
30305
|
try {
|
|
30229
|
-
const { stdout } = await
|
|
30230
|
-
cwd: repoRoot,
|
|
30231
|
-
maxBuffer: 10 * 1024 * 1024
|
|
30232
|
-
});
|
|
30306
|
+
const { stdout } = await execGitFile(["stash", "create"], { cwd: repoRoot });
|
|
30233
30307
|
return stdout.trim();
|
|
30234
30308
|
} catch (e) {
|
|
30235
30309
|
log2(
|
|
@@ -30240,7 +30314,7 @@ async function gitStashCreate(repoRoot, log2) {
|
|
|
30240
30314
|
}
|
|
30241
30315
|
async function gitRun(repoRoot, args, log2, label) {
|
|
30242
30316
|
try {
|
|
30243
|
-
await
|
|
30317
|
+
await execGitFile(args, { cwd: repoRoot });
|
|
30244
30318
|
return { ok: true };
|
|
30245
30319
|
} catch (e) {
|
|
30246
30320
|
const msg = e instanceof Error ? e.message : String(e);
|
|
@@ -30274,10 +30348,10 @@ async function capturePreTurnSnapshot(options) {
|
|
|
30274
30348
|
return { ok: false, error: "No git repos to snapshot" };
|
|
30275
30349
|
}
|
|
30276
30350
|
const repos = [];
|
|
30277
|
-
|
|
30351
|
+
await forEachWithGitYield(repoRoots, async (root) => {
|
|
30278
30352
|
const stashSha = await gitStashCreate(root, log2);
|
|
30279
30353
|
repos.push({ path: root, stashSha });
|
|
30280
|
-
}
|
|
30354
|
+
});
|
|
30281
30355
|
const dir = snapshotsDirForCwd(agentCwd);
|
|
30282
30356
|
try {
|
|
30283
30357
|
fs14.mkdirSync(dir, { recursive: true });
|
|
@@ -30312,17 +30386,25 @@ async function applyPreTurnSnapshot(filePath, log2) {
|
|
|
30312
30386
|
if (!Array.isArray(data.repos)) {
|
|
30313
30387
|
return { ok: false, error: "Invalid snapshot file" };
|
|
30314
30388
|
}
|
|
30315
|
-
|
|
30316
|
-
|
|
30389
|
+
let applyError = null;
|
|
30390
|
+
await forEachWithGitYield(data.repos, async (r) => {
|
|
30391
|
+
if (applyError || !r.path) return;
|
|
30317
30392
|
const reset = await gitRun(r.path, ["reset", "--hard", "HEAD"], log2, "reset --hard");
|
|
30318
|
-
if (!reset.ok)
|
|
30393
|
+
if (!reset.ok) {
|
|
30394
|
+
applyError = reset;
|
|
30395
|
+
return;
|
|
30396
|
+
}
|
|
30319
30397
|
const clean = await gitRun(r.path, ["clean", "-fd"], log2, "clean -fd");
|
|
30320
|
-
if (!clean.ok)
|
|
30398
|
+
if (!clean.ok) {
|
|
30399
|
+
applyError = clean;
|
|
30400
|
+
return;
|
|
30401
|
+
}
|
|
30321
30402
|
if (r.stashSha) {
|
|
30322
30403
|
const ap = await gitRun(r.path, ["stash", "apply", r.stashSha], log2, "stash apply");
|
|
30323
|
-
if (!ap.ok)
|
|
30404
|
+
if (!ap.ok) applyError = ap;
|
|
30324
30405
|
}
|
|
30325
|
-
}
|
|
30406
|
+
});
|
|
30407
|
+
if (applyError?.ok === false) return applyError;
|
|
30326
30408
|
log2(`[snapshot] Restored pre-turn state for ${data.runId.slice(0, 8)}\u2026`);
|
|
30327
30409
|
return { ok: true };
|
|
30328
30410
|
}
|
|
@@ -30330,8 +30412,7 @@ function snapshotFilePath(agentCwd, runId) {
|
|
|
30330
30412
|
return path14.join(snapshotsDirForCwd(agentCwd), `${runId}.json`);
|
|
30331
30413
|
}
|
|
30332
30414
|
|
|
30333
|
-
// src/git/session-git-queue.ts
|
|
30334
|
-
var execFileAsync6 = promisify7(execFile7);
|
|
30415
|
+
// src/git/snapshot/session-git-queue.ts
|
|
30335
30416
|
var MAX_FULL_FILE_TEXT_BYTES = 512 * 1024;
|
|
30336
30417
|
async function readWorkspaceFileAsUtf8(absPath) {
|
|
30337
30418
|
try {
|
|
@@ -30360,35 +30441,28 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
|
|
|
30360
30441
|
return;
|
|
30361
30442
|
}
|
|
30362
30443
|
const multiRepo = data.repos.length > 1;
|
|
30363
|
-
|
|
30364
|
-
if (!repo.stashSha)
|
|
30444
|
+
await forEachWithGitYield(data.repos, async (repo) => {
|
|
30445
|
+
if (!repo.stashSha) return;
|
|
30365
30446
|
let namesRaw;
|
|
30366
30447
|
try {
|
|
30367
|
-
const { stdout } = await
|
|
30368
|
-
cwd: repo.path,
|
|
30369
|
-
maxBuffer: 10 * 1024 * 1024
|
|
30370
|
-
});
|
|
30448
|
+
const { stdout } = await execGitFile(["diff", "--name-only", repo.stashSha], { cwd: repo.path });
|
|
30371
30449
|
namesRaw = stdout;
|
|
30372
30450
|
} catch (e) {
|
|
30373
30451
|
log2(
|
|
30374
30452
|
`[session-git-queue] Git diff --name-only failed in ${repo.path}: ${e instanceof Error ? e.message : String(e)}`
|
|
30375
30453
|
);
|
|
30376
|
-
|
|
30454
|
+
return;
|
|
30377
30455
|
}
|
|
30378
30456
|
const lines = namesRaw.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
30379
30457
|
const slug = path15.basename(repo.path).replace(/[^\w.-]+/g, "_") || "repo";
|
|
30380
|
-
|
|
30381
|
-
if (rel.includes(".."))
|
|
30458
|
+
await forEachWithGitYield(lines, async (rel) => {
|
|
30459
|
+
if (rel.includes("..")) return;
|
|
30382
30460
|
try {
|
|
30383
|
-
const { stdout: patchContent } = await
|
|
30384
|
-
"git",
|
|
30461
|
+
const { stdout: patchContent } = await execGitFile(
|
|
30385
30462
|
["diff", "--no-color", repo.stashSha, "--", rel],
|
|
30386
|
-
{
|
|
30387
|
-
cwd: repo.path,
|
|
30388
|
-
maxBuffer: 50 * 1024 * 1024
|
|
30389
|
-
}
|
|
30463
|
+
{ cwd: repo.path }
|
|
30390
30464
|
);
|
|
30391
|
-
if (!patchContent.trim())
|
|
30465
|
+
if (!patchContent.trim()) return;
|
|
30392
30466
|
const displayPath = multiRepo ? `${slug}/${rel}` : rel;
|
|
30393
30467
|
const workspaceFilePath = path15.join(repo.path, rel);
|
|
30394
30468
|
const newText = await readWorkspaceFileAsUtf8(workspaceFilePath);
|
|
@@ -30405,8 +30479,8 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
|
|
|
30405
30479
|
`[session-git-queue] Git diff failed for ${rel}: ${e instanceof Error ? e.message : String(e)}`
|
|
30406
30480
|
);
|
|
30407
30481
|
}
|
|
30408
|
-
}
|
|
30409
|
-
}
|
|
30482
|
+
});
|
|
30483
|
+
});
|
|
30410
30484
|
}
|
|
30411
30485
|
|
|
30412
30486
|
// src/agents/acp/put-summarize-change-summaries.ts
|
|
@@ -30774,9 +30848,9 @@ __export(claude_code_acp_client_exports, {
|
|
|
30774
30848
|
});
|
|
30775
30849
|
|
|
30776
30850
|
// src/agents/acp/clients/detect-command-on-path.ts
|
|
30777
|
-
import { execFile as
|
|
30778
|
-
import { promisify as
|
|
30779
|
-
var
|
|
30851
|
+
import { execFile as execFile7 } from "node:child_process";
|
|
30852
|
+
import { promisify as promisify7 } from "node:util";
|
|
30853
|
+
var execFileAsync6 = promisify7(execFile7);
|
|
30780
30854
|
var COMMAND_ON_PATH_PROBE_TIMEOUT_MS = 750;
|
|
30781
30855
|
async function execFileShutdownAware(file2, args, timeoutMs) {
|
|
30782
30856
|
if (isCliImmediateShutdownRequested()) throw new Error("shutdown");
|
|
@@ -30786,7 +30860,7 @@ async function execFileShutdownAware(file2, args, timeoutMs) {
|
|
|
30786
30860
|
}, 50);
|
|
30787
30861
|
shutdownPoll.unref?.();
|
|
30788
30862
|
try {
|
|
30789
|
-
await
|
|
30863
|
+
await execFileAsync6(file2, args, { timeout: timeoutMs, signal: ac.signal });
|
|
30790
30864
|
} finally {
|
|
30791
30865
|
clearInterval(shutdownPoll);
|
|
30792
30866
|
}
|
|
@@ -30866,7 +30940,7 @@ __export(cursor_acp_client_exports, {
|
|
|
30866
30940
|
createCursorAcpClient: () => createCursorAcpClient,
|
|
30867
30941
|
detectLocalAgentPresence: () => detectLocalAgentPresence3
|
|
30868
30942
|
});
|
|
30869
|
-
import { spawn as
|
|
30943
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
30870
30944
|
import * as readline from "node:readline";
|
|
30871
30945
|
|
|
30872
30946
|
// src/agents/acp/format-session-update-kind-for-log.ts
|
|
@@ -30936,7 +31010,7 @@ async function createCursorAcpClient(options) {
|
|
|
30936
31010
|
} = options;
|
|
30937
31011
|
const dbgFs = process.env.BUILDAUTOMATON_DEBUG_ACP_FS === "1";
|
|
30938
31012
|
const isWindows = process.platform === "win32";
|
|
30939
|
-
const child =
|
|
31013
|
+
const child = spawn5(command[0], command.slice(1), {
|
|
30940
31014
|
cwd,
|
|
30941
31015
|
stdio: ["pipe", "pipe", "pipe"],
|
|
30942
31016
|
env: process.env,
|
|
@@ -32114,7 +32188,7 @@ async function ensureAcpClient(options) {
|
|
|
32114
32188
|
if (!state.acpStartPromise) {
|
|
32115
32189
|
let statOk = false;
|
|
32116
32190
|
try {
|
|
32117
|
-
const st = fs15.
|
|
32191
|
+
const st = await fs15.promises.stat(targetSessionParentPath);
|
|
32118
32192
|
statOk = st.isDirectory();
|
|
32119
32193
|
if (!statOk) {
|
|
32120
32194
|
state.lastAcpStartError = `Agent cwd is not a directory: ${targetSessionParentPath}`;
|
|
@@ -32382,7 +32456,7 @@ import os8 from "node:os";
|
|
|
32382
32456
|
import * as fs17 from "node:fs";
|
|
32383
32457
|
import * as path20 from "node:path";
|
|
32384
32458
|
|
|
32385
|
-
// src/git/worktree-add.ts
|
|
32459
|
+
// src/git/worktrees/worktree-add.ts
|
|
32386
32460
|
async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
|
|
32387
32461
|
const mainGit = cliSimpleGit(mainRepoPath);
|
|
32388
32462
|
await mainGit.raw(["worktree", "add", "-b", branch, worktreePath, "HEAD"]);
|
|
@@ -32487,7 +32561,7 @@ async function prepareNewSessionWorktrees(options) {
|
|
|
32487
32561
|
};
|
|
32488
32562
|
}
|
|
32489
32563
|
|
|
32490
|
-
// src/git/rename-branch.ts
|
|
32564
|
+
// src/git/branches/rename-branch.ts
|
|
32491
32565
|
async function gitRenameCurrentBranch(repoDir, newName) {
|
|
32492
32566
|
const g = cliSimpleGit(repoDir);
|
|
32493
32567
|
await g.raw(["branch", "-m", newName]);
|
|
@@ -32511,10 +32585,10 @@ async function renameSessionWorktreeBranches(paths, newBranch, log2) {
|
|
|
32511
32585
|
// src/worktrees/remove-session-worktrees.ts
|
|
32512
32586
|
import * as fs20 from "node:fs";
|
|
32513
32587
|
|
|
32514
|
-
// src/git/worktree-remove.ts
|
|
32588
|
+
// src/git/worktrees/worktree-remove.ts
|
|
32515
32589
|
import * as fs19 from "node:fs";
|
|
32516
32590
|
|
|
32517
|
-
// src/git/resolve-main-repo-from-git-file.ts
|
|
32591
|
+
// src/git/worktrees/resolve-main-repo-from-git-file.ts
|
|
32518
32592
|
import * as fs18 from "node:fs";
|
|
32519
32593
|
import * as path21 from "node:path";
|
|
32520
32594
|
function resolveMainRepoFromWorktreeGitFile(wt) {
|
|
@@ -32528,7 +32602,7 @@ function resolveMainRepoFromWorktreeGitFile(wt) {
|
|
|
32528
32602
|
return path21.dirname(gitDir);
|
|
32529
32603
|
}
|
|
32530
32604
|
|
|
32531
|
-
// src/git/worktree-remove.ts
|
|
32605
|
+
// src/git/worktrees/worktree-remove.ts
|
|
32532
32606
|
async function gitWorktreeRemoveForce(worktreePath) {
|
|
32533
32607
|
const mainRepo = resolveMainRepoFromWorktreeGitFile(worktreePath);
|
|
32534
32608
|
if (mainRepo) {
|
|
@@ -32554,7 +32628,88 @@ async function removeSessionWorktrees(paths, log2) {
|
|
|
32554
32628
|
}
|
|
32555
32629
|
}
|
|
32556
32630
|
|
|
32557
|
-
// src/git/
|
|
32631
|
+
// src/git/changes/lib/parse-git-status.ts
|
|
32632
|
+
function parseNameStatusLines(lines) {
|
|
32633
|
+
const m = /* @__PURE__ */ new Map();
|
|
32634
|
+
for (const line of lines) {
|
|
32635
|
+
if (!line.trim()) continue;
|
|
32636
|
+
const tabParts = line.split(" ");
|
|
32637
|
+
if (tabParts.length < 2) continue;
|
|
32638
|
+
const status = tabParts[0].trim();
|
|
32639
|
+
const code = status[0];
|
|
32640
|
+
if (code === "A") {
|
|
32641
|
+
m.set(tabParts[tabParts.length - 1], "added");
|
|
32642
|
+
} else if (code === "D") {
|
|
32643
|
+
m.set(tabParts[tabParts.length - 1], "removed");
|
|
32644
|
+
} else if (code === "R" || code === "C") {
|
|
32645
|
+
if (tabParts.length >= 3) m.set(tabParts[tabParts.length - 1], "modified");
|
|
32646
|
+
} else if (code === "M" || code === "U" || code === "T") {
|
|
32647
|
+
m.set(tabParts[tabParts.length - 1], "modified");
|
|
32648
|
+
}
|
|
32649
|
+
}
|
|
32650
|
+
return m;
|
|
32651
|
+
}
|
|
32652
|
+
function parseNumstatFirstLine(line) {
|
|
32653
|
+
const parts = line.split(" ");
|
|
32654
|
+
if (parts.length < 3) return null;
|
|
32655
|
+
const [a, d] = parts;
|
|
32656
|
+
const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
|
|
32657
|
+
const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
|
|
32658
|
+
return { additions, deletions };
|
|
32659
|
+
}
|
|
32660
|
+
function parseNumstat(lines) {
|
|
32661
|
+
const m = /* @__PURE__ */ new Map();
|
|
32662
|
+
for (const line of lines) {
|
|
32663
|
+
if (!line.trim()) continue;
|
|
32664
|
+
const parts = line.split(" ");
|
|
32665
|
+
if (parts.length < 3) continue;
|
|
32666
|
+
const [a, d, p] = parts;
|
|
32667
|
+
const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
|
|
32668
|
+
const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
|
|
32669
|
+
m.set(p, { additions, deletions });
|
|
32670
|
+
}
|
|
32671
|
+
return m;
|
|
32672
|
+
}
|
|
32673
|
+
async function numstatFromGitNoIndex(g, pathInRepo) {
|
|
32674
|
+
const devNull = process.platform === "win32" ? "NUL" : "/dev/null";
|
|
32675
|
+
try {
|
|
32676
|
+
const out = await g.raw(["diff", "--numstat", "--no-index", "--", devNull, pathInRepo]);
|
|
32677
|
+
const first2 = String(out).split("\n").find((l) => l.trim()) ?? "";
|
|
32678
|
+
return parseNumstatFirstLine(first2);
|
|
32679
|
+
} catch {
|
|
32680
|
+
return null;
|
|
32681
|
+
}
|
|
32682
|
+
}
|
|
32683
|
+
|
|
32684
|
+
// src/git/changes/lib/working-tree-changed-path-count.ts
|
|
32685
|
+
function workingTreeChangedPathCount(nameStatusLines, numstatLines, untrackedLines) {
|
|
32686
|
+
const kindByPath = parseNameStatusLines(nameStatusLines);
|
|
32687
|
+
const numByPath = parseNumstat(numstatLines);
|
|
32688
|
+
const paths = /* @__PURE__ */ new Set([...kindByPath.keys(), ...numByPath.keys()]);
|
|
32689
|
+
for (const p of untrackedLines.map((s) => s.trim()).filter(Boolean)) {
|
|
32690
|
+
paths.add(p);
|
|
32691
|
+
}
|
|
32692
|
+
return paths.size;
|
|
32693
|
+
}
|
|
32694
|
+
|
|
32695
|
+
// src/git/changes/count-working-tree-changed-files.ts
|
|
32696
|
+
async function countWorkingTreeChangedFilesForRepo(repoGitCwd) {
|
|
32697
|
+
return runGitTask(async () => {
|
|
32698
|
+
const g = cliSimpleGit(repoGitCwd);
|
|
32699
|
+
const [nameStatusRaw, numstatRaw, untrackedRaw] = await Promise.all([
|
|
32700
|
+
g.raw(["diff", "--name-status", "HEAD"]).catch(() => ""),
|
|
32701
|
+
g.raw(["diff", "HEAD", "--numstat"]).catch(() => ""),
|
|
32702
|
+
g.raw(["ls-files", "--others", "--exclude-standard"]).catch(() => "")
|
|
32703
|
+
]);
|
|
32704
|
+
return workingTreeChangedPathCount(
|
|
32705
|
+
String(nameStatusRaw).split("\n"),
|
|
32706
|
+
String(numstatRaw).split("\n"),
|
|
32707
|
+
String(untrackedRaw).split("\n")
|
|
32708
|
+
);
|
|
32709
|
+
});
|
|
32710
|
+
}
|
|
32711
|
+
|
|
32712
|
+
// src/git/commits/resolve-remote-tracking.ts
|
|
32558
32713
|
async function tryConfigGet(g, key) {
|
|
32559
32714
|
try {
|
|
32560
32715
|
const out = await g.raw(["config", "--get", key]);
|
|
@@ -32637,30 +32792,6 @@ async function resolveBaseShaForUnpushedCommits(g) {
|
|
|
32637
32792
|
if (!defaultRef) return null;
|
|
32638
32793
|
return revParseSafe(g, defaultRef);
|
|
32639
32794
|
}
|
|
32640
|
-
function parseLogShaDateSubjectLines(raw) {
|
|
32641
|
-
const out = [];
|
|
32642
|
-
for (const line of String(raw).split("\n")) {
|
|
32643
|
-
const l = line.trimEnd();
|
|
32644
|
-
if (!l.trim()) continue;
|
|
32645
|
-
const parts = l.split(" ");
|
|
32646
|
-
if (parts.length < 3) continue;
|
|
32647
|
-
const sha = parts[0].trim();
|
|
32648
|
-
const committedAt = parts[1].trim();
|
|
32649
|
-
const subject = parts.slice(2).join(" ").trim();
|
|
32650
|
-
if (!/^[0-9a-f]{7,40}$/i.test(sha)) continue;
|
|
32651
|
-
out.push({ sha, shortSha: sha.slice(0, 7), subject, committedAt });
|
|
32652
|
-
}
|
|
32653
|
-
return out;
|
|
32654
|
-
}
|
|
32655
|
-
async function gitLogNotReachableFromBase(g, baseSha, headSha) {
|
|
32656
|
-
if (baseSha === headSha) return [];
|
|
32657
|
-
try {
|
|
32658
|
-
const logOut = await g.raw(["log", "--format=%H %cI %s", `${baseSha}..${headSha}`]);
|
|
32659
|
-
return parseLogShaDateSubjectLines(logOut);
|
|
32660
|
-
} catch {
|
|
32661
|
-
return [];
|
|
32662
|
-
}
|
|
32663
|
-
}
|
|
32664
32795
|
async function commitsAheadOfRemoteTracking(repoDir) {
|
|
32665
32796
|
const g = cliSimpleGit(repoDir);
|
|
32666
32797
|
const headSha = await revParseSafe(g, "HEAD");
|
|
@@ -32675,44 +32806,41 @@ async function commitsAheadOfRemoteTracking(repoDir) {
|
|
|
32675
32806
|
return 0;
|
|
32676
32807
|
}
|
|
32677
32808
|
}
|
|
32809
|
+
|
|
32810
|
+
// src/git/status/working-tree-status.ts
|
|
32678
32811
|
async function getRepoWorkingTreeStatus(repoDir) {
|
|
32679
|
-
|
|
32680
|
-
|
|
32681
|
-
|
|
32682
|
-
|
|
32683
|
-
|
|
32684
|
-
}
|
|
32685
|
-
async function listUnpushedCommits(repoDir) {
|
|
32686
|
-
const g = cliSimpleGit(repoDir);
|
|
32687
|
-
const headSha = await revParseSafe(g, "HEAD");
|
|
32688
|
-
if (!headSha) return [];
|
|
32689
|
-
const baseSha = await resolveBaseShaForUnpushedCommits(g);
|
|
32690
|
-
if (!baseSha) return [];
|
|
32691
|
-
return gitLogNotReachableFromBase(g, baseSha, headSha);
|
|
32812
|
+
return runGitTask(async () => {
|
|
32813
|
+
const uncommittedFileCount = await countWorkingTreeChangedFilesForRepo(repoDir);
|
|
32814
|
+
const hasUncommittedChanges = uncommittedFileCount > 0;
|
|
32815
|
+
const ahead = await commitsAheadOfRemoteTracking(repoDir);
|
|
32816
|
+
return { hasUncommittedChanges, hasUnpushedCommits: ahead > 0, uncommittedFileCount };
|
|
32817
|
+
});
|
|
32692
32818
|
}
|
|
32693
32819
|
async function aggregateSessionPathsWorkingTreeStatus(paths) {
|
|
32694
32820
|
let hasUncommittedChanges = false;
|
|
32695
32821
|
let hasUnpushedCommits = false;
|
|
32696
|
-
|
|
32822
|
+
let uncommittedFileCount = 0;
|
|
32823
|
+
await forEachWithGitYield(paths, async (p) => {
|
|
32697
32824
|
const s = await getRepoWorkingTreeStatus(p);
|
|
32825
|
+
uncommittedFileCount += s.uncommittedFileCount;
|
|
32698
32826
|
if (s.hasUncommittedChanges) hasUncommittedChanges = true;
|
|
32699
32827
|
if (s.hasUnpushedCommits) hasUnpushedCommits = true;
|
|
32700
|
-
}
|
|
32701
|
-
return { hasUncommittedChanges, hasUnpushedCommits };
|
|
32828
|
+
});
|
|
32829
|
+
return { hasUncommittedChanges, hasUnpushedCommits, uncommittedFileCount };
|
|
32702
32830
|
}
|
|
32703
32831
|
async function pushAheadOfUpstreamForPaths(paths) {
|
|
32704
|
-
|
|
32832
|
+
await forEachWithGitYield(paths, async (p) => {
|
|
32705
32833
|
const g = cliSimpleGit(p);
|
|
32706
32834
|
const ahead = await commitsAheadOfRemoteTracking(p);
|
|
32707
|
-
if (ahead <= 0)
|
|
32835
|
+
if (ahead <= 0) return;
|
|
32708
32836
|
await g.push();
|
|
32709
|
-
}
|
|
32837
|
+
});
|
|
32710
32838
|
}
|
|
32711
32839
|
|
|
32712
|
-
// src/git/
|
|
32840
|
+
// src/git/changes/types.ts
|
|
32713
32841
|
var MAX_PATCH_CHARS = 35e4;
|
|
32714
32842
|
|
|
32715
|
-
// src/git/
|
|
32843
|
+
// src/git/changes/lib/repo-format.ts
|
|
32716
32844
|
function posixJoinDirFile(dir, file2) {
|
|
32717
32845
|
const d = dir === "." || dir === "" ? "" : dir.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
32718
32846
|
const f = file2.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
@@ -32766,63 +32894,80 @@ function formatRemoteDisplayLabel(remoteUrl) {
|
|
|
32766
32894
|
return `origin \xB7 ${hostPath}`;
|
|
32767
32895
|
}
|
|
32768
32896
|
|
|
32769
|
-
// src/git/
|
|
32897
|
+
// src/git/changes/get-working-tree-change-repo-details.ts
|
|
32770
32898
|
import * as path23 from "node:path";
|
|
32771
32899
|
|
|
32772
|
-
// src/git/
|
|
32773
|
-
function
|
|
32774
|
-
const
|
|
32775
|
-
for (const line of
|
|
32776
|
-
|
|
32777
|
-
|
|
32778
|
-
|
|
32779
|
-
|
|
32780
|
-
const
|
|
32781
|
-
|
|
32782
|
-
|
|
32783
|
-
|
|
32784
|
-
|
|
32785
|
-
} else if (code === "R" || code === "C") {
|
|
32786
|
-
if (tabParts.length >= 3) m.set(tabParts[tabParts.length - 1], "modified");
|
|
32787
|
-
} else if (code === "M" || code === "U" || code === "T") {
|
|
32788
|
-
m.set(tabParts[tabParts.length - 1], "modified");
|
|
32789
|
-
}
|
|
32900
|
+
// src/git/commits/lib/parse-log-lines.ts
|
|
32901
|
+
function parseLogShaDateSubjectLines(raw) {
|
|
32902
|
+
const out = [];
|
|
32903
|
+
for (const line of String(raw).split("\n")) {
|
|
32904
|
+
const l = line.trimEnd();
|
|
32905
|
+
if (!l.trim()) continue;
|
|
32906
|
+
const parts = l.split(" ");
|
|
32907
|
+
if (parts.length < 3) continue;
|
|
32908
|
+
const sha = parts[0].trim();
|
|
32909
|
+
const committedAt = parts[1].trim();
|
|
32910
|
+
const subject = parts.slice(2).join(" ").trim();
|
|
32911
|
+
if (!/^[0-9a-f]{7,40}$/i.test(sha)) continue;
|
|
32912
|
+
out.push({ sha, shortSha: sha.slice(0, 7), subject, committedAt });
|
|
32790
32913
|
}
|
|
32791
|
-
return
|
|
32792
|
-
}
|
|
32793
|
-
function parseNumstatFirstLine(line) {
|
|
32794
|
-
const parts = line.split(" ");
|
|
32795
|
-
if (parts.length < 3) return null;
|
|
32796
|
-
const [a, d] = parts;
|
|
32797
|
-
const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
|
|
32798
|
-
const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
|
|
32799
|
-
return { additions, deletions };
|
|
32914
|
+
return out;
|
|
32800
32915
|
}
|
|
32801
|
-
|
|
32802
|
-
|
|
32803
|
-
|
|
32804
|
-
|
|
32805
|
-
|
|
32806
|
-
|
|
32807
|
-
|
|
32808
|
-
|
|
32809
|
-
|
|
32810
|
-
m.set(p, { additions, deletions });
|
|
32916
|
+
|
|
32917
|
+
// src/git/commits/list-unpushed-commits.ts
|
|
32918
|
+
async function gitLogNotReachableFromBase(g, baseSha, headSha) {
|
|
32919
|
+
if (baseSha === headSha) return [];
|
|
32920
|
+
try {
|
|
32921
|
+
const logOut = await g.raw(["log", "--format=%H %cI %s", `${baseSha}..${headSha}`]);
|
|
32922
|
+
return parseLogShaDateSubjectLines(logOut);
|
|
32923
|
+
} catch {
|
|
32924
|
+
return [];
|
|
32811
32925
|
}
|
|
32812
|
-
return m;
|
|
32813
32926
|
}
|
|
32814
|
-
async function
|
|
32815
|
-
const
|
|
32927
|
+
async function listUnpushedCommits(repoDir) {
|
|
32928
|
+
const g = cliSimpleGit(repoDir);
|
|
32929
|
+
const headSha = await revParseSafe(g, "HEAD");
|
|
32930
|
+
if (!headSha) return [];
|
|
32931
|
+
const baseSha = await resolveBaseShaForUnpushedCommits(g);
|
|
32932
|
+
if (!baseSha) return [];
|
|
32933
|
+
return gitLogNotReachableFromBase(g, baseSha, headSha);
|
|
32934
|
+
}
|
|
32935
|
+
|
|
32936
|
+
// src/git/commits/lib/sanitize-recent-commits-limit.ts
|
|
32937
|
+
var RECENT_COMMITS_LIMIT_MIN = 1;
|
|
32938
|
+
var RECENT_COMMITS_LIMIT_MAX = 50;
|
|
32939
|
+
var RECENT_COMMITS_LIMIT_DEFAULT = 2;
|
|
32940
|
+
function sanitizeRecentCommitsLimit(value) {
|
|
32941
|
+
const n = typeof value === "number" ? value : typeof value === "string" ? Number.parseInt(value.trim(), 10) : Number.NaN;
|
|
32942
|
+
if (!Number.isFinite(n)) return RECENT_COMMITS_LIMIT_DEFAULT;
|
|
32943
|
+
return Math.min(RECENT_COMMITS_LIMIT_MAX, Math.max(RECENT_COMMITS_LIMIT_MIN, Math.trunc(n)));
|
|
32944
|
+
}
|
|
32945
|
+
|
|
32946
|
+
// src/git/commits/list-recent-commits.ts
|
|
32947
|
+
async function listRecentCommits(repoDir, limitInput) {
|
|
32948
|
+
const limit = sanitizeRecentCommitsLimit(limitInput);
|
|
32949
|
+
const g = cliSimpleGit(repoDir);
|
|
32950
|
+
const headSha = await revParseSafe(g, "HEAD");
|
|
32951
|
+
if (!headSha) return { commits: [], hasMore: false };
|
|
32952
|
+
const unpushedSet = new Set((await listUnpushedCommits(repoDir)).map((c) => c.sha));
|
|
32816
32953
|
try {
|
|
32817
|
-
const
|
|
32818
|
-
const
|
|
32819
|
-
|
|
32954
|
+
const logOut = await g.raw(["log", "--format=%H %cI %s", `-n`, String(limit), "HEAD"]);
|
|
32955
|
+
const commits = parseLogShaDateSubjectLines(logOut).map((c) => ({
|
|
32956
|
+
...c,
|
|
32957
|
+
needsPush: unpushedSet.has(c.sha)
|
|
32958
|
+
}));
|
|
32959
|
+
let hasMore = false;
|
|
32960
|
+
if (commits.length === limit) {
|
|
32961
|
+
const nextOut = await g.raw(["log", "-n", "1", `--skip=${limit}`, "--format=%H", "HEAD"]);
|
|
32962
|
+
hasMore = /^[0-9a-f]{7,40}$/i.test(String(nextOut).trim());
|
|
32963
|
+
}
|
|
32964
|
+
return { commits, hasMore };
|
|
32820
32965
|
} catch {
|
|
32821
|
-
return
|
|
32966
|
+
return { commits: [], hasMore: false };
|
|
32822
32967
|
}
|
|
32823
32968
|
}
|
|
32824
32969
|
|
|
32825
|
-
// src/git/
|
|
32970
|
+
// src/git/changes/lib/patch-truncate.ts
|
|
32826
32971
|
function truncatePatch(s) {
|
|
32827
32972
|
if (s.length <= MAX_PATCH_CHARS) return s;
|
|
32828
32973
|
return `${s.slice(0, MAX_PATCH_CHARS)}
|
|
@@ -32830,7 +32975,7 @@ function truncatePatch(s) {
|
|
|
32830
32975
|
\u2026 (diff truncated)`;
|
|
32831
32976
|
}
|
|
32832
32977
|
|
|
32833
|
-
// src/git/
|
|
32978
|
+
// src/git/commits/list-changed-files-for-commit.ts
|
|
32834
32979
|
var EMPTY_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
32835
32980
|
async function parentForCommitDiff(g, sha) {
|
|
32836
32981
|
try {
|
|
@@ -32856,7 +33001,7 @@ async function listChangedFilesForCommit(repoGitCwd, repoRelPath, commitSha) {
|
|
|
32856
33001
|
const paths = new Set([...kindByPath.keys(), ...numByPath.keys()].filter(Boolean));
|
|
32857
33002
|
const rows = [];
|
|
32858
33003
|
const normRel = repoRelPath === "." || repoRelPath === "" ? "." : repoRelPath;
|
|
32859
|
-
|
|
33004
|
+
await forEachWithGitYield([...paths], async (pathInRepo) => {
|
|
32860
33005
|
const relLauncher = posixJoinDirFile(normRel, pathInRepo.replace(/\\/g, "/"));
|
|
32861
33006
|
const nums = numByPath.get(pathInRepo);
|
|
32862
33007
|
let additions = nums?.additions ?? 0;
|
|
@@ -32868,8 +33013,8 @@ async function listChangedFilesForCommit(repoGitCwd, repoRelPath, commitSha) {
|
|
|
32868
33013
|
else change = "modified";
|
|
32869
33014
|
}
|
|
32870
33015
|
rows.push({ pathRelLauncher: relLauncher, additions, deletions, change });
|
|
32871
|
-
}
|
|
32872
|
-
|
|
33016
|
+
});
|
|
33017
|
+
await forEachWithGitYield(rows, async (row) => {
|
|
32873
33018
|
let pathInRepo;
|
|
32874
33019
|
if (normRel === ".") {
|
|
32875
33020
|
pathInRepo = row.pathRelLauncher;
|
|
@@ -32881,16 +33026,16 @@ async function listChangedFilesForCommit(repoGitCwd, repoRelPath, commitSha) {
|
|
|
32881
33026
|
const raw = await g.raw(["diff", "-U20000", range, "--", pathInRepo]).catch(() => "");
|
|
32882
33027
|
const t = String(raw).trim();
|
|
32883
33028
|
row.patchContent = t ? truncatePatch(t) : void 0;
|
|
32884
|
-
}
|
|
33029
|
+
});
|
|
32885
33030
|
rows.sort((a, b) => a.pathRelLauncher.localeCompare(b.pathRelLauncher));
|
|
32886
33031
|
return rows;
|
|
32887
33032
|
}
|
|
32888
33033
|
|
|
32889
|
-
// src/git/
|
|
33034
|
+
// src/git/changes/list-changed-files-for-repo.ts
|
|
32890
33035
|
import * as fs22 from "node:fs";
|
|
32891
33036
|
import * as path22 from "node:path";
|
|
32892
33037
|
|
|
32893
|
-
// src/git/
|
|
33038
|
+
// src/git/changes/lib/count-lines.ts
|
|
32894
33039
|
import { createReadStream } from "node:fs";
|
|
32895
33040
|
import * as readline2 from "node:readline";
|
|
32896
33041
|
async function countTextFileLines(filePath) {
|
|
@@ -32911,7 +33056,7 @@ async function countTextFileLines(filePath) {
|
|
|
32911
33056
|
return lines;
|
|
32912
33057
|
}
|
|
32913
33058
|
|
|
32914
|
-
// src/git/
|
|
33059
|
+
// src/git/changes/hydrate-patch.ts
|
|
32915
33060
|
import * as fs21 from "node:fs";
|
|
32916
33061
|
var UNIFIED_HUNK_HEADER_RE = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
|
|
32917
33062
|
var MAX_HYDRATE_LINES_PER_GAP = 8e3;
|
|
@@ -33027,26 +33172,16 @@ async function hydrateUnifiedPatchWithFileContext(patch, filePath, repoGitCwd, p
|
|
|
33027
33172
|
return truncatePatch(out.join("\n"));
|
|
33028
33173
|
}
|
|
33029
33174
|
|
|
33030
|
-
// src/git/
|
|
33175
|
+
// src/git/changes/unified-diff-for-file.ts
|
|
33031
33176
|
async function unifiedDiffForFile(repoCwd, pathInRepo, change) {
|
|
33032
33177
|
const g = cliSimpleGit(repoCwd);
|
|
33033
|
-
|
|
33034
|
-
|
|
33035
|
-
|
|
33036
|
-
|
|
33037
|
-
raw = await g.raw(["diff", "--no-index", "--", devNull, pathInRepo]);
|
|
33038
|
-
} else {
|
|
33039
|
-
raw = await g.raw(["diff", "HEAD", "--", pathInRepo]);
|
|
33040
|
-
}
|
|
33041
|
-
const t = String(raw).trim();
|
|
33042
|
-
if (!t) return void 0;
|
|
33043
|
-
return truncatePatch(t);
|
|
33044
|
-
} catch {
|
|
33045
|
-
return void 0;
|
|
33046
|
-
}
|
|
33178
|
+
const args = change === "added" ? ["diff", "--no-color", "HEAD", "--", pathInRepo] : change === "removed" ? ["diff", "--no-color", "HEAD", "--", pathInRepo] : ["diff", "--no-color", "HEAD", "--", pathInRepo];
|
|
33179
|
+
const raw = await g.raw([...args]).catch(() => "");
|
|
33180
|
+
const t = String(raw).trim();
|
|
33181
|
+
return t ? truncatePatch(t) : void 0;
|
|
33047
33182
|
}
|
|
33048
33183
|
|
|
33049
|
-
// src/git/
|
|
33184
|
+
// src/git/changes/list-changed-files-for-repo.ts
|
|
33050
33185
|
async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
33051
33186
|
const g = cliSimpleGit(repoGitCwd);
|
|
33052
33187
|
const [nameStatusRaw, numstatRaw, untrackedRaw] = await Promise.all([
|
|
@@ -33060,7 +33195,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
|
33060
33195
|
const untracked = String(untrackedRaw).split("\n").map((s) => s.trim()).filter(Boolean);
|
|
33061
33196
|
for (const p of untracked) paths.add(p);
|
|
33062
33197
|
const rows = [];
|
|
33063
|
-
|
|
33198
|
+
await forEachWithGitYield([...paths], async (pathInRepo) => {
|
|
33064
33199
|
const relLauncher = posixJoinDirFile(repoRelPath, pathInRepo.replace(/\\/g, "/"));
|
|
33065
33200
|
const repoFilePath = path22.join(repoGitCwd, pathInRepo);
|
|
33066
33201
|
const nums = numByPath.get(pathInRepo);
|
|
@@ -33090,9 +33225,9 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
|
33090
33225
|
else change = "modified";
|
|
33091
33226
|
}
|
|
33092
33227
|
rows.push({ pathRelLauncher: relLauncher, additions, deletions, change });
|
|
33093
|
-
}
|
|
33228
|
+
});
|
|
33094
33229
|
const normRel = repoRelPath === "." || repoRelPath === "" ? "." : repoRelPath;
|
|
33095
|
-
|
|
33230
|
+
await forEachWithGitYield(rows, async (row) => {
|
|
33096
33231
|
let pathInRepo;
|
|
33097
33232
|
if (normRel === ".") {
|
|
33098
33233
|
pathInRepo = row.pathRelLauncher;
|
|
@@ -33107,11 +33242,11 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
|
33107
33242
|
patch = await hydrateUnifiedPatchWithFileContext(patch, filePath, repoGitCwd, pathInRepo, row.change);
|
|
33108
33243
|
}
|
|
33109
33244
|
row.patchContent = patch;
|
|
33110
|
-
}
|
|
33245
|
+
});
|
|
33111
33246
|
return rows;
|
|
33112
33247
|
}
|
|
33113
33248
|
|
|
33114
|
-
// src/git/
|
|
33249
|
+
// src/git/changes/get-working-tree-change-repo-details.ts
|
|
33115
33250
|
function normRepoRel(p) {
|
|
33116
33251
|
const x = p.replace(/\\/g, "/").trim();
|
|
33117
33252
|
return x === "" ? "." : x;
|
|
@@ -33130,7 +33265,9 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
33130
33265
|
throw new Error("commit sha is required for commit changes");
|
|
33131
33266
|
}
|
|
33132
33267
|
const basis = filter == null && basisInput.kind === "commit" ? { kind: "working" } : basisInput;
|
|
33133
|
-
for (
|
|
33268
|
+
for (let i = 0; i < options.commitTargetPaths.length; i++) {
|
|
33269
|
+
if (i > 0) await yieldToEventLoop2();
|
|
33270
|
+
const target = options.commitTargetPaths[i];
|
|
33134
33271
|
const t = path23.resolve(target);
|
|
33135
33272
|
if (!await isGitRepoDirectory(t)) continue;
|
|
33136
33273
|
const g = cliSimpleGit(t);
|
|
@@ -33164,7 +33301,10 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
33164
33301
|
const files = basis.kind === "commit" ? await listChangedFilesForCommit(t, relForList, basis.sha.trim()) : await listChangedFilesForRepo(t, relForList);
|
|
33165
33302
|
const st = await g.status();
|
|
33166
33303
|
const hasUncommittedChanges = (st.files?.length ?? 0) > 0;
|
|
33167
|
-
const unpushedCommits = await
|
|
33304
|
+
const [unpushedCommits, recentCommitList] = await Promise.all([
|
|
33305
|
+
listUnpushedCommits(t),
|
|
33306
|
+
listRecentCommits(t, options.recentCommitsLimit)
|
|
33307
|
+
]);
|
|
33168
33308
|
out.push({
|
|
33169
33309
|
repoRelPath: norm,
|
|
33170
33310
|
repoDisplayName,
|
|
@@ -33174,6 +33314,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
33174
33314
|
files,
|
|
33175
33315
|
hasUncommittedChanges,
|
|
33176
33316
|
unpushedCommits,
|
|
33317
|
+
recentCommits: recentCommitList.commits,
|
|
33318
|
+
recentCommitsHasMore: recentCommitList.hasMore,
|
|
33177
33319
|
changesView: basis.kind === "commit" ? "commit" : "working",
|
|
33178
33320
|
changesCommitSha: basis.kind === "commit" ? basis.sha.trim() : null
|
|
33179
33321
|
});
|
|
@@ -33182,7 +33324,7 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
33182
33324
|
return out;
|
|
33183
33325
|
}
|
|
33184
33326
|
|
|
33185
|
-
// src/git/commit-and-push.ts
|
|
33327
|
+
// src/git/branches/commit-and-push.ts
|
|
33186
33328
|
async function gitCommitAllIfDirty(repoDir, message, options) {
|
|
33187
33329
|
const g = cliSimpleGit(repoDir);
|
|
33188
33330
|
const st = await g.status();
|
|
@@ -33606,7 +33748,8 @@ var SessionWorktreeManager = class {
|
|
|
33606
33748
|
sessionWorktreeRootPath: sessionWorkingTreeRelRoot,
|
|
33607
33749
|
legacyRepoNestedSessionLayout: legacyNested,
|
|
33608
33750
|
repoFilterRelPath: opts?.repoRelPath?.trim() ? opts.repoRelPath.trim() : null,
|
|
33609
|
-
basis: opts?.basis
|
|
33751
|
+
basis: opts?.basis,
|
|
33752
|
+
recentCommitsLimit: opts?.recentCommitsLimit
|
|
33610
33753
|
});
|
|
33611
33754
|
}
|
|
33612
33755
|
async pushSessionUpstream(sessionId) {
|
|
@@ -34136,7 +34279,7 @@ function pipedStdoutStderrFor(attemptStdio) {
|
|
|
34136
34279
|
}
|
|
34137
34280
|
|
|
34138
34281
|
// src/dev-servers/manager/shell-spawn/try-spawn-piped-via-sh.ts
|
|
34139
|
-
import { spawn as
|
|
34282
|
+
import { spawn as spawn6 } from "node:child_process";
|
|
34140
34283
|
function trySpawnPipedViaSh(command, env, cwd, signal) {
|
|
34141
34284
|
const attempts = [
|
|
34142
34285
|
{ stdio: [devNullReadFd(), "pipe", "pipe"], endStdin: false },
|
|
@@ -34157,9 +34300,9 @@ function trySpawnPipedViaSh(command, env, cwd, signal) {
|
|
|
34157
34300
|
if (process.platform === "win32") {
|
|
34158
34301
|
opts.windowsHide = true;
|
|
34159
34302
|
const com = process.env.ComSpec || "cmd.exe";
|
|
34160
|
-
proc =
|
|
34303
|
+
proc = spawn6(com, ["/d", "/s", "/c", command], opts);
|
|
34161
34304
|
} else {
|
|
34162
|
-
proc =
|
|
34305
|
+
proc = spawn6("/bin/sh", ["-c", command], opts);
|
|
34163
34306
|
}
|
|
34164
34307
|
if (attempt.endStdin) {
|
|
34165
34308
|
proc.stdin?.end();
|
|
@@ -34179,7 +34322,7 @@ function trySpawnPipedViaSh(command, env, cwd, signal) {
|
|
|
34179
34322
|
}
|
|
34180
34323
|
|
|
34181
34324
|
// src/dev-servers/manager/shell-spawn/try-spawn-shell-true-piped.ts
|
|
34182
|
-
import { spawn as
|
|
34325
|
+
import { spawn as spawn7 } from "node:child_process";
|
|
34183
34326
|
function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
|
|
34184
34327
|
try {
|
|
34185
34328
|
const opts = {
|
|
@@ -34192,7 +34335,7 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
|
|
|
34192
34335
|
if (process.platform === "win32") {
|
|
34193
34336
|
opts.windowsHide = true;
|
|
34194
34337
|
}
|
|
34195
|
-
return
|
|
34338
|
+
return spawn7(command, opts);
|
|
34196
34339
|
} catch (e) {
|
|
34197
34340
|
if (isSpawnEbadf(e)) return null;
|
|
34198
34341
|
throw e;
|
|
@@ -34200,7 +34343,7 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
|
|
|
34200
34343
|
}
|
|
34201
34344
|
|
|
34202
34345
|
// src/dev-servers/manager/shell-spawn/try-spawn-merged-log-file.ts
|
|
34203
|
-
import { spawn as
|
|
34346
|
+
import { spawn as spawn8 } from "node:child_process";
|
|
34204
34347
|
import fs28 from "node:fs";
|
|
34205
34348
|
import { tmpdir } from "node:os";
|
|
34206
34349
|
import path31 from "node:path";
|
|
@@ -34218,7 +34361,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
|
34218
34361
|
try {
|
|
34219
34362
|
let proc;
|
|
34220
34363
|
if (process.platform === "win32") {
|
|
34221
|
-
proc =
|
|
34364
|
+
proc = spawn8(process.env.ComSpec || "cmd.exe", ["/d", "/s", "/c", command], {
|
|
34222
34365
|
env,
|
|
34223
34366
|
cwd,
|
|
34224
34367
|
stdio,
|
|
@@ -34226,7 +34369,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
|
34226
34369
|
...signal ? { signal } : {}
|
|
34227
34370
|
});
|
|
34228
34371
|
} else {
|
|
34229
|
-
proc =
|
|
34372
|
+
proc = spawn8("/bin/sh", ["-c", command], { env, cwd, stdio, ...signal ? { signal } : {} });
|
|
34230
34373
|
}
|
|
34231
34374
|
fs28.closeSync(logFd);
|
|
34232
34375
|
return {
|
|
@@ -34247,7 +34390,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
|
34247
34390
|
}
|
|
34248
34391
|
|
|
34249
34392
|
// src/dev-servers/manager/shell-spawn/try-spawn-shell-script-log-redirect.ts
|
|
34250
|
-
import { spawn as
|
|
34393
|
+
import { spawn as spawn9 } from "node:child_process";
|
|
34251
34394
|
import fs29 from "node:fs";
|
|
34252
34395
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
34253
34396
|
import path32 from "node:path";
|
|
@@ -34270,7 +34413,7 @@ cd ${shSingleQuote(cwd)}
|
|
|
34270
34413
|
/bin/sh ${shSingleQuote(innerPath)} >>${shSingleQuote(logPath)} 2>&1
|
|
34271
34414
|
`
|
|
34272
34415
|
);
|
|
34273
|
-
const proc =
|
|
34416
|
+
const proc = spawn9("/bin/sh", [runnerPath], {
|
|
34274
34417
|
env,
|
|
34275
34418
|
cwd: tmpRoot,
|
|
34276
34419
|
stdio: "ignore",
|
|
@@ -34302,7 +34445,7 @@ CD /D ${q(cwd)}\r
|
|
|
34302
34445
|
${command} >> ${q(logPath)} 2>&1\r
|
|
34303
34446
|
`
|
|
34304
34447
|
);
|
|
34305
|
-
const proc =
|
|
34448
|
+
const proc = spawn9(com, ["/d", "/s", "/c", q(runnerPath)], {
|
|
34306
34449
|
env,
|
|
34307
34450
|
cwd: tmpRoot,
|
|
34308
34451
|
stdio: "ignore",
|
|
@@ -34323,7 +34466,7 @@ ${command} >> ${q(logPath)} 2>&1\r
|
|
|
34323
34466
|
}
|
|
34324
34467
|
|
|
34325
34468
|
// src/dev-servers/manager/shell-spawn/try-spawn-inherit.ts
|
|
34326
|
-
import { spawn as
|
|
34469
|
+
import { spawn as spawn10 } from "node:child_process";
|
|
34327
34470
|
function trySpawnInheritStdio(command, env, cwd, signal) {
|
|
34328
34471
|
const opts = {
|
|
34329
34472
|
env,
|
|
@@ -34335,9 +34478,9 @@ function trySpawnInheritStdio(command, env, cwd, signal) {
|
|
|
34335
34478
|
if (process.platform === "win32") {
|
|
34336
34479
|
opts.windowsHide = true;
|
|
34337
34480
|
const com = process.env.ComSpec || "cmd.exe";
|
|
34338
|
-
proc =
|
|
34481
|
+
proc = spawn10(com, ["/d", "/s", "/c", command], opts);
|
|
34339
34482
|
} else {
|
|
34340
|
-
proc =
|
|
34483
|
+
proc = spawn10("/bin/sh", ["-c", command], opts);
|
|
34341
34484
|
}
|
|
34342
34485
|
return { proc, pipedStdoutStderr: false };
|
|
34343
34486
|
}
|
|
@@ -34899,11 +35042,13 @@ function connectFirehose(options) {
|
|
|
34899
35042
|
if (Buffer.isBuffer(raw) && tryConsumeBinaryProxyBody(raw, deps)) {
|
|
34900
35043
|
return;
|
|
34901
35044
|
}
|
|
34902
|
-
|
|
34903
|
-
|
|
34904
|
-
|
|
34905
|
-
|
|
34906
|
-
|
|
35045
|
+
setImmediate(() => {
|
|
35046
|
+
try {
|
|
35047
|
+
const text = Buffer.isBuffer(raw) ? raw.toString("utf8") : String(raw);
|
|
35048
|
+
dispatchFirehoseJsonMessage(JSON.parse(text), deps);
|
|
35049
|
+
} catch {
|
|
35050
|
+
}
|
|
35051
|
+
});
|
|
34907
35052
|
});
|
|
34908
35053
|
ws.on("close", (code, reason) => {
|
|
34909
35054
|
disposeClientPing();
|
|
@@ -35419,7 +35564,9 @@ async function runLocalRevertBeforeQueuedPrompt(next, deps) {
|
|
|
35419
35564
|
const tid = typeof pl.snapshotRevertTurnId === "string" && pl.snapshotRevertTurnId.trim() !== "" ? pl.snapshotRevertTurnId.trim() : next.turnId;
|
|
35420
35565
|
const agentBase = deps.sessionWorktreeManager.getSessionWorktreeRootForSession(sid) ?? getBridgeRoot();
|
|
35421
35566
|
const file2 = snapshotFilePath(agentBase, tid);
|
|
35422
|
-
|
|
35567
|
+
try {
|
|
35568
|
+
await fs31.promises.access(file2, fs31.constants.F_OK);
|
|
35569
|
+
} catch {
|
|
35423
35570
|
deps.log(
|
|
35424
35571
|
`[Queue] requeued_with_revert: no pre-turn snapshot for ${tid.slice(0, 8)}\u2026; continuing without revert.`
|
|
35425
35572
|
);
|
|
@@ -35593,12 +35740,12 @@ function createBridgePromptSenders(deps, getWs) {
|
|
|
35593
35740
|
}
|
|
35594
35741
|
|
|
35595
35742
|
// src/agents/acp/from-bridge/bridge-prompt-preamble.ts
|
|
35596
|
-
import { execFile as
|
|
35597
|
-
import { promisify as
|
|
35598
|
-
var
|
|
35743
|
+
import { execFile as execFile8 } from "node:child_process";
|
|
35744
|
+
import { promisify as promisify8 } from "node:util";
|
|
35745
|
+
var execFileAsync7 = promisify8(execFile8);
|
|
35599
35746
|
async function readGitBranch(cwd) {
|
|
35600
35747
|
try {
|
|
35601
|
-
const { stdout } = await
|
|
35748
|
+
const { stdout } = await execFileAsync7("git", ["branch", "--show-current"], { cwd, maxBuffer: 64 * 1024 });
|
|
35602
35749
|
const b = stdout.trim();
|
|
35603
35750
|
return b || null;
|
|
35604
35751
|
} catch {
|
|
@@ -36308,7 +36455,8 @@ var handleSessionGitRequestMessage = (msg, deps) => {
|
|
|
36308
36455
|
reply({
|
|
36309
36456
|
ok: true,
|
|
36310
36457
|
hasUncommittedChanges: r.hasUncommittedChanges,
|
|
36311
|
-
hasUnpushedCommits: r.hasUnpushedCommits
|
|
36458
|
+
hasUnpushedCommits: r.hasUnpushedCommits,
|
|
36459
|
+
uncommittedFileCount: r.uncommittedFileCount
|
|
36312
36460
|
});
|
|
36313
36461
|
return;
|
|
36314
36462
|
}
|
|
@@ -36322,7 +36470,12 @@ var handleSessionGitRequestMessage = (msg, deps) => {
|
|
|
36322
36470
|
return;
|
|
36323
36471
|
}
|
|
36324
36472
|
}
|
|
36325
|
-
const
|
|
36473
|
+
const recentCommitsLimit = typeof msg.changesRecentCommitsLimit === "number" || typeof msg.changesRecentCommitsLimit === "string" ? msg.changesRecentCommitsLimit : void 0;
|
|
36474
|
+
const opts = repoRel && view === "commit" && commitSha ? {
|
|
36475
|
+
repoRelPath: repoRel,
|
|
36476
|
+
basis: { kind: "commit", sha: commitSha },
|
|
36477
|
+
recentCommitsLimit
|
|
36478
|
+
} : repoRel ? { repoRelPath: repoRel, basis: { kind: "working" }, recentCommitsLimit } : recentCommitsLimit !== void 0 ? { recentCommitsLimit } : void 0;
|
|
36326
36479
|
const repos = await deps.sessionWorktreeManager.getSessionWorkingTreeChangeDetails(sessionId, opts);
|
|
36327
36480
|
reply({
|
|
36328
36481
|
ok: true,
|
|
@@ -36340,7 +36493,8 @@ var handleSessionGitRequestMessage = (msg, deps) => {
|
|
|
36340
36493
|
reply({
|
|
36341
36494
|
ok: true,
|
|
36342
36495
|
hasUncommittedChanges: st2.hasUncommittedChanges,
|
|
36343
|
-
hasUnpushedCommits: st2.hasUnpushedCommits
|
|
36496
|
+
hasUnpushedCommits: st2.hasUnpushedCommits,
|
|
36497
|
+
uncommittedFileCount: st2.uncommittedFileCount
|
|
36344
36498
|
});
|
|
36345
36499
|
return;
|
|
36346
36500
|
}
|
|
@@ -36365,7 +36519,8 @@ var handleSessionGitRequestMessage = (msg, deps) => {
|
|
|
36365
36519
|
reply({
|
|
36366
36520
|
ok: true,
|
|
36367
36521
|
hasUncommittedChanges: st.hasUncommittedChanges,
|
|
36368
|
-
hasUnpushedCommits: st.hasUnpushedCommits
|
|
36522
|
+
hasUnpushedCommits: st.hasUnpushedCommits,
|
|
36523
|
+
uncommittedFileCount: st.uncommittedFileCount
|
|
36369
36524
|
});
|
|
36370
36525
|
} catch (e) {
|
|
36371
36526
|
reply({ ok: false, error: e instanceof Error ? e.message : String(e) });
|
|
@@ -36408,7 +36563,9 @@ var handleRevertTurnSnapshotMessage = (msg, deps) => {
|
|
|
36408
36563
|
if (!s) return;
|
|
36409
36564
|
const agentBase = sessionWorktreeManager.getSessionWorktreeRootForSession(sessionId) ?? getBridgeRoot();
|
|
36410
36565
|
const file2 = snapshotFilePath(agentBase, turnId);
|
|
36411
|
-
|
|
36566
|
+
try {
|
|
36567
|
+
await fs35.promises.access(file2, fs35.constants.F_OK);
|
|
36568
|
+
} catch {
|
|
36412
36569
|
sendWsMessage(s, {
|
|
36413
36570
|
type: "revert_turn_snapshot_result",
|
|
36414
36571
|
id,
|
|
@@ -36527,9 +36684,7 @@ function handleBridgeMessage(data, deps) {
|
|
|
36527
36684
|
if (!deps.getWs()) return;
|
|
36528
36685
|
const msg = parseApiToBridgeMessage(normalizeInboundBridgeWebSocketJson(data), deps.log);
|
|
36529
36686
|
if (!msg) return;
|
|
36530
|
-
|
|
36531
|
-
dispatchBridgeMessage(msg, deps);
|
|
36532
|
-
});
|
|
36687
|
+
dispatchBridgeMessage(msg, deps);
|
|
36533
36688
|
}
|
|
36534
36689
|
|
|
36535
36690
|
// src/auth/refresh-bridge-tokens.ts
|
|
@@ -37174,25 +37329,27 @@ async function createBridgeConnection(options) {
|
|
|
37174
37329
|
}
|
|
37175
37330
|
function sendAgentCapabilitiesToBridge(info) {
|
|
37176
37331
|
if (!Array.isArray(info.configOptions) || info.configOptions.length === 0) return;
|
|
37177
|
-
|
|
37178
|
-
|
|
37179
|
-
|
|
37180
|
-
|
|
37181
|
-
|
|
37182
|
-
|
|
37183
|
-
|
|
37184
|
-
|
|
37185
|
-
|
|
37186
|
-
|
|
37187
|
-
|
|
37188
|
-
|
|
37189
|
-
|
|
37190
|
-
|
|
37191
|
-
|
|
37192
|
-
|
|
37193
|
-
|
|
37194
|
-
|
|
37195
|
-
|
|
37332
|
+
setImmediate(() => {
|
|
37333
|
+
let changed = false;
|
|
37334
|
+
try {
|
|
37335
|
+
changed = withCliSqliteSync(
|
|
37336
|
+
(db) => upsertCliAgentCapabilityCache(db, {
|
|
37337
|
+
workspaceId,
|
|
37338
|
+
agentType: info.agentType,
|
|
37339
|
+
configOptions: info.configOptions
|
|
37340
|
+
})
|
|
37341
|
+
);
|
|
37342
|
+
} catch (e) {
|
|
37343
|
+
if (e instanceof CliSqliteInterrupted) return;
|
|
37344
|
+
}
|
|
37345
|
+
if (!changed) return;
|
|
37346
|
+
const socket = getWs();
|
|
37347
|
+
if (!socket || socket.readyState !== wrapper_default.OPEN) return;
|
|
37348
|
+
sendWsMessage(socket, {
|
|
37349
|
+
type: "agent_capabilities",
|
|
37350
|
+
agentType: info.agentType,
|
|
37351
|
+
configOptions: info.configOptions
|
|
37352
|
+
});
|
|
37196
37353
|
});
|
|
37197
37354
|
}
|
|
37198
37355
|
const worktreesRootPath = options.worktreesRootPath ?? defaultWorktreesRootPath();
|
|
@@ -37344,6 +37501,7 @@ async function runConnectedBridge(options, restartWithoutAuth) {
|
|
|
37344
37501
|
let bridgeClose = null;
|
|
37345
37502
|
const onSignal = (kind) => {
|
|
37346
37503
|
requestCliImmediateShutdown();
|
|
37504
|
+
abortActiveGitChildProcesses();
|
|
37347
37505
|
cleanupKeyCommand?.();
|
|
37348
37506
|
logImmediate(
|
|
37349
37507
|
kind === "interrupt" ? "Keyboard interrupt (Ctrl+C) \u2014 stopping\u2026" : "Stop requested \u2014 shutting down\u2026"
|