@buildautomaton/cli 0.1.33 → 0.1.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/dist/cli.js +354 -204
- package/dist/cli.js.map +4 -4
- package/dist/index.js +350 -200
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -25064,7 +25064,7 @@ var {
|
|
|
25064
25064
|
} = import_index.default;
|
|
25065
25065
|
|
|
25066
25066
|
// src/cli-version.ts
|
|
25067
|
-
var CLI_VERSION = "0.1.
|
|
25067
|
+
var CLI_VERSION = "0.1.34".length > 0 ? "0.1.34" : "0.0.0-dev";
|
|
25068
25068
|
|
|
25069
25069
|
// src/cli/defaults.ts
|
|
25070
25070
|
var DEFAULT_API_URL = process.env.BUILDAUTOMATON_API_URL ?? "https://api.buildautomaton.com";
|
|
@@ -27783,17 +27783,13 @@ function getAgentModelFromAgentConfig(config2) {
|
|
|
27783
27783
|
return t !== "" ? t : null;
|
|
27784
27784
|
}
|
|
27785
27785
|
|
|
27786
|
-
// src/git/session-git-queue.ts
|
|
27787
|
-
import { execFile as execFile7 } from "node:child_process";
|
|
27786
|
+
// src/git/snapshot/session-git-queue.ts
|
|
27788
27787
|
import { readFile as readFile2, stat as stat2 } from "node:fs/promises";
|
|
27789
|
-
import { promisify as promisify7 } from "node:util";
|
|
27790
27788
|
import * as path15 from "node:path";
|
|
27791
27789
|
|
|
27792
|
-
// src/git/pre-turn-snapshot.ts
|
|
27790
|
+
// src/git/snapshot/pre-turn-snapshot.ts
|
|
27793
27791
|
import * as fs15 from "node:fs";
|
|
27794
27792
|
import * as path14 from "node:path";
|
|
27795
|
-
import { execFile as execFile6 } from "node:child_process";
|
|
27796
|
-
import { promisify as promisify6 } from "node:util";
|
|
27797
27793
|
|
|
27798
27794
|
// src/git/discover-repos.ts
|
|
27799
27795
|
import * as fs14 from "node:fs";
|
|
@@ -32358,12 +32354,68 @@ function gitInstanceFactory(baseDir, options) {
|
|
|
32358
32354
|
init_git_response_error();
|
|
32359
32355
|
var simpleGit = gitInstanceFactory;
|
|
32360
32356
|
|
|
32357
|
+
// src/git/git-runtime.ts
|
|
32358
|
+
var activeGitChildProcesses = /* @__PURE__ */ new Set();
|
|
32359
|
+
function abortActiveGitChildProcesses() {
|
|
32360
|
+
for (const child of activeGitChildProcesses) {
|
|
32361
|
+
try {
|
|
32362
|
+
child.kill("SIGTERM");
|
|
32363
|
+
} catch {
|
|
32364
|
+
}
|
|
32365
|
+
}
|
|
32366
|
+
}
|
|
32367
|
+
var GitOperationAbortedError = class extends Error {
|
|
32368
|
+
constructor(message = "Git operation aborted") {
|
|
32369
|
+
super(message);
|
|
32370
|
+
this.name = "GitOperationAbortedError";
|
|
32371
|
+
}
|
|
32372
|
+
};
|
|
32373
|
+
function throwIfGitShutdownRequested() {
|
|
32374
|
+
if (isCliImmediateShutdownRequested()) {
|
|
32375
|
+
abortActiveGitChildProcesses();
|
|
32376
|
+
throw new GitOperationAbortedError();
|
|
32377
|
+
}
|
|
32378
|
+
}
|
|
32379
|
+
async function runGitTask(fn) {
|
|
32380
|
+
throwIfGitShutdownRequested();
|
|
32381
|
+
try {
|
|
32382
|
+
const result = await fn();
|
|
32383
|
+
throwIfGitShutdownRequested();
|
|
32384
|
+
return result;
|
|
32385
|
+
} catch (e) {
|
|
32386
|
+
if (isCliImmediateShutdownRequested()) {
|
|
32387
|
+
throw new GitOperationAbortedError();
|
|
32388
|
+
}
|
|
32389
|
+
throw e;
|
|
32390
|
+
}
|
|
32391
|
+
}
|
|
32392
|
+
async function yieldToEventLoop2() {
|
|
32393
|
+
throwIfGitShutdownRequested();
|
|
32394
|
+
await new Promise((resolve20) => {
|
|
32395
|
+
setImmediate(resolve20);
|
|
32396
|
+
});
|
|
32397
|
+
throwIfGitShutdownRequested();
|
|
32398
|
+
}
|
|
32399
|
+
async function forEachWithGitYield(items, fn) {
|
|
32400
|
+
for (let i = 0; i < items.length; i++) {
|
|
32401
|
+
if (i > 0 || items.length > 1) await yieldToEventLoop2();
|
|
32402
|
+
await fn(items[i], i);
|
|
32403
|
+
}
|
|
32404
|
+
}
|
|
32405
|
+
|
|
32361
32406
|
// src/git/cli-simple-git.ts
|
|
32362
32407
|
function cliSimpleGit(baseDir) {
|
|
32363
|
-
const git = simpleGit({
|
|
32408
|
+
const git = simpleGit({
|
|
32409
|
+
baseDir,
|
|
32410
|
+
trimmed: true,
|
|
32411
|
+
spawnOptions: {}
|
|
32412
|
+
});
|
|
32364
32413
|
git.outputHandler((command, stdout, stderr) => {
|
|
32365
32414
|
const trace = isCliTrace();
|
|
32366
32415
|
const onChunk = (label) => (chunk) => {
|
|
32416
|
+
if (isCliImmediateShutdownRequested()) {
|
|
32417
|
+
abortActiveGitChildProcesses();
|
|
32418
|
+
}
|
|
32367
32419
|
const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
32368
32420
|
const line = text.replace(/\s+$/, "");
|
|
32369
32421
|
if (trace && line) {
|
|
@@ -32452,17 +32504,37 @@ async function discoverGitReposUnderRoot(rootPath) {
|
|
|
32452
32504
|
return out;
|
|
32453
32505
|
}
|
|
32454
32506
|
|
|
32455
|
-
// src/git/
|
|
32507
|
+
// src/git/git-exec.ts
|
|
32508
|
+
import { execFile as execFile6, spawn as spawn2 } from "node:child_process";
|
|
32509
|
+
import { promisify as promisify6 } from "node:util";
|
|
32456
32510
|
var execFileAsync5 = promisify6(execFile6);
|
|
32511
|
+
async function execGitFile(args, options) {
|
|
32512
|
+
throwIfGitShutdownRequested();
|
|
32513
|
+
try {
|
|
32514
|
+
const result = await execFileAsync5("git", args, {
|
|
32515
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
32516
|
+
...options
|
|
32517
|
+
});
|
|
32518
|
+
throwIfGitShutdownRequested();
|
|
32519
|
+
return {
|
|
32520
|
+
stdout: String(result.stdout ?? ""),
|
|
32521
|
+
stderr: String(result.stderr ?? "")
|
|
32522
|
+
};
|
|
32523
|
+
} catch (e) {
|
|
32524
|
+
if (isCliImmediateShutdownRequested()) {
|
|
32525
|
+
throw new GitOperationAbortedError();
|
|
32526
|
+
}
|
|
32527
|
+
throw e;
|
|
32528
|
+
}
|
|
32529
|
+
}
|
|
32530
|
+
|
|
32531
|
+
// src/git/snapshot/pre-turn-snapshot.ts
|
|
32457
32532
|
function snapshotsDirForCwd(agentCwd) {
|
|
32458
32533
|
return path14.join(agentCwd, ".buildautomaton", "snapshots");
|
|
32459
32534
|
}
|
|
32460
32535
|
async function gitStashCreate(repoRoot, log2) {
|
|
32461
32536
|
try {
|
|
32462
|
-
const { stdout } = await
|
|
32463
|
-
cwd: repoRoot,
|
|
32464
|
-
maxBuffer: 10 * 1024 * 1024
|
|
32465
|
-
});
|
|
32537
|
+
const { stdout } = await execGitFile(["stash", "create"], { cwd: repoRoot });
|
|
32466
32538
|
return stdout.trim();
|
|
32467
32539
|
} catch (e) {
|
|
32468
32540
|
log2(
|
|
@@ -32473,7 +32545,7 @@ async function gitStashCreate(repoRoot, log2) {
|
|
|
32473
32545
|
}
|
|
32474
32546
|
async function gitRun(repoRoot, args, log2, label) {
|
|
32475
32547
|
try {
|
|
32476
|
-
await
|
|
32548
|
+
await execGitFile(args, { cwd: repoRoot });
|
|
32477
32549
|
return { ok: true };
|
|
32478
32550
|
} catch (e) {
|
|
32479
32551
|
const msg = e instanceof Error ? e.message : String(e);
|
|
@@ -32507,10 +32579,10 @@ async function capturePreTurnSnapshot(options) {
|
|
|
32507
32579
|
return { ok: false, error: "No git repos to snapshot" };
|
|
32508
32580
|
}
|
|
32509
32581
|
const repos = [];
|
|
32510
|
-
|
|
32582
|
+
await forEachWithGitYield(repoRoots, async (root) => {
|
|
32511
32583
|
const stashSha = await gitStashCreate(root, log2);
|
|
32512
32584
|
repos.push({ path: root, stashSha });
|
|
32513
|
-
}
|
|
32585
|
+
});
|
|
32514
32586
|
const dir = snapshotsDirForCwd(agentCwd);
|
|
32515
32587
|
try {
|
|
32516
32588
|
fs15.mkdirSync(dir, { recursive: true });
|
|
@@ -32545,17 +32617,25 @@ async function applyPreTurnSnapshot(filePath, log2) {
|
|
|
32545
32617
|
if (!Array.isArray(data.repos)) {
|
|
32546
32618
|
return { ok: false, error: "Invalid snapshot file" };
|
|
32547
32619
|
}
|
|
32548
|
-
|
|
32549
|
-
|
|
32620
|
+
let applyError = null;
|
|
32621
|
+
await forEachWithGitYield(data.repos, async (r) => {
|
|
32622
|
+
if (applyError || !r.path) return;
|
|
32550
32623
|
const reset = await gitRun(r.path, ["reset", "--hard", "HEAD"], log2, "reset --hard");
|
|
32551
|
-
if (!reset.ok)
|
|
32624
|
+
if (!reset.ok) {
|
|
32625
|
+
applyError = reset;
|
|
32626
|
+
return;
|
|
32627
|
+
}
|
|
32552
32628
|
const clean = await gitRun(r.path, ["clean", "-fd"], log2, "clean -fd");
|
|
32553
|
-
if (!clean.ok)
|
|
32629
|
+
if (!clean.ok) {
|
|
32630
|
+
applyError = clean;
|
|
32631
|
+
return;
|
|
32632
|
+
}
|
|
32554
32633
|
if (r.stashSha) {
|
|
32555
32634
|
const ap = await gitRun(r.path, ["stash", "apply", r.stashSha], log2, "stash apply");
|
|
32556
|
-
if (!ap.ok)
|
|
32635
|
+
if (!ap.ok) applyError = ap;
|
|
32557
32636
|
}
|
|
32558
|
-
}
|
|
32637
|
+
});
|
|
32638
|
+
if (applyError?.ok === false) return applyError;
|
|
32559
32639
|
log2(`[snapshot] Restored pre-turn state for ${data.runId.slice(0, 8)}\u2026`);
|
|
32560
32640
|
return { ok: true };
|
|
32561
32641
|
}
|
|
@@ -32563,8 +32643,7 @@ function snapshotFilePath(agentCwd, runId) {
|
|
|
32563
32643
|
return path14.join(snapshotsDirForCwd(agentCwd), `${runId}.json`);
|
|
32564
32644
|
}
|
|
32565
32645
|
|
|
32566
|
-
// src/git/session-git-queue.ts
|
|
32567
|
-
var execFileAsync6 = promisify7(execFile7);
|
|
32646
|
+
// src/git/snapshot/session-git-queue.ts
|
|
32568
32647
|
var MAX_FULL_FILE_TEXT_BYTES = 512 * 1024;
|
|
32569
32648
|
async function readWorkspaceFileAsUtf8(absPath) {
|
|
32570
32649
|
try {
|
|
@@ -32593,35 +32672,28 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
|
|
|
32593
32672
|
return;
|
|
32594
32673
|
}
|
|
32595
32674
|
const multiRepo = data.repos.length > 1;
|
|
32596
|
-
|
|
32597
|
-
if (!repo.stashSha)
|
|
32675
|
+
await forEachWithGitYield(data.repos, async (repo) => {
|
|
32676
|
+
if (!repo.stashSha) return;
|
|
32598
32677
|
let namesRaw;
|
|
32599
32678
|
try {
|
|
32600
|
-
const { stdout } = await
|
|
32601
|
-
cwd: repo.path,
|
|
32602
|
-
maxBuffer: 10 * 1024 * 1024
|
|
32603
|
-
});
|
|
32679
|
+
const { stdout } = await execGitFile(["diff", "--name-only", repo.stashSha], { cwd: repo.path });
|
|
32604
32680
|
namesRaw = stdout;
|
|
32605
32681
|
} catch (e) {
|
|
32606
32682
|
log2(
|
|
32607
32683
|
`[session-git-queue] Git diff --name-only failed in ${repo.path}: ${e instanceof Error ? e.message : String(e)}`
|
|
32608
32684
|
);
|
|
32609
|
-
|
|
32685
|
+
return;
|
|
32610
32686
|
}
|
|
32611
32687
|
const lines = namesRaw.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
32612
32688
|
const slug = path15.basename(repo.path).replace(/[^\w.-]+/g, "_") || "repo";
|
|
32613
|
-
|
|
32614
|
-
if (rel.includes(".."))
|
|
32689
|
+
await forEachWithGitYield(lines, async (rel) => {
|
|
32690
|
+
if (rel.includes("..")) return;
|
|
32615
32691
|
try {
|
|
32616
|
-
const { stdout: patchContent } = await
|
|
32617
|
-
"git",
|
|
32692
|
+
const { stdout: patchContent } = await execGitFile(
|
|
32618
32693
|
["diff", "--no-color", repo.stashSha, "--", rel],
|
|
32619
|
-
{
|
|
32620
|
-
cwd: repo.path,
|
|
32621
|
-
maxBuffer: 50 * 1024 * 1024
|
|
32622
|
-
}
|
|
32694
|
+
{ cwd: repo.path }
|
|
32623
32695
|
);
|
|
32624
|
-
if (!patchContent.trim())
|
|
32696
|
+
if (!patchContent.trim()) return;
|
|
32625
32697
|
const displayPath = multiRepo ? `${slug}/${rel}` : rel;
|
|
32626
32698
|
const workspaceFilePath = path15.join(repo.path, rel);
|
|
32627
32699
|
const newText = await readWorkspaceFileAsUtf8(workspaceFilePath);
|
|
@@ -32638,8 +32710,8 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
|
|
|
32638
32710
|
`[session-git-queue] Git diff failed for ${rel}: ${e instanceof Error ? e.message : String(e)}`
|
|
32639
32711
|
);
|
|
32640
32712
|
}
|
|
32641
|
-
}
|
|
32642
|
-
}
|
|
32713
|
+
});
|
|
32714
|
+
});
|
|
32643
32715
|
}
|
|
32644
32716
|
|
|
32645
32717
|
// src/agents/acp/put-summarize-change-summaries.ts
|
|
@@ -32981,9 +33053,9 @@ __export(claude_code_acp_client_exports, {
|
|
|
32981
33053
|
});
|
|
32982
33054
|
|
|
32983
33055
|
// src/agents/acp/clients/detect-command-on-path.ts
|
|
32984
|
-
import { execFile as
|
|
32985
|
-
import { promisify as
|
|
32986
|
-
var
|
|
33056
|
+
import { execFile as execFile7 } from "node:child_process";
|
|
33057
|
+
import { promisify as promisify7 } from "node:util";
|
|
33058
|
+
var execFileAsync6 = promisify7(execFile7);
|
|
32987
33059
|
var COMMAND_ON_PATH_PROBE_TIMEOUT_MS = 750;
|
|
32988
33060
|
async function execFileShutdownAware(file2, args, timeoutMs) {
|
|
32989
33061
|
if (isCliImmediateShutdownRequested()) throw new Error("shutdown");
|
|
@@ -32993,7 +33065,7 @@ async function execFileShutdownAware(file2, args, timeoutMs) {
|
|
|
32993
33065
|
}, 50);
|
|
32994
33066
|
shutdownPoll.unref?.();
|
|
32995
33067
|
try {
|
|
32996
|
-
await
|
|
33068
|
+
await execFileAsync6(file2, args, { timeout: timeoutMs, signal: ac.signal });
|
|
32997
33069
|
} finally {
|
|
32998
33070
|
clearInterval(shutdownPoll);
|
|
32999
33071
|
}
|
|
@@ -33018,7 +33090,7 @@ async function execProbeShutdownAware(file2, args, timeoutMs) {
|
|
|
33018
33090
|
}
|
|
33019
33091
|
|
|
33020
33092
|
// src/agents/acp/clients/sdk/sdk-stdio-acp-client.ts
|
|
33021
|
-
import { spawn as
|
|
33093
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
33022
33094
|
import { Readable, Writable } from "node:stream";
|
|
33023
33095
|
|
|
33024
33096
|
// src/agents/acp/clients/agent-stderr-capture.ts
|
|
@@ -33613,7 +33685,7 @@ async function createSdkStdioAcpClient(options) {
|
|
|
33613
33685
|
getActiveConfigOptions
|
|
33614
33686
|
} = options;
|
|
33615
33687
|
const isWindows = process.platform === "win32";
|
|
33616
|
-
const child =
|
|
33688
|
+
const child = spawn3(command[0], command.slice(1), {
|
|
33617
33689
|
cwd,
|
|
33618
33690
|
stdio: ["pipe", "pipe", "pipe"],
|
|
33619
33691
|
env: process.env,
|
|
@@ -33826,7 +33898,7 @@ __export(cursor_acp_client_exports, {
|
|
|
33826
33898
|
createCursorAcpClient: () => createCursorAcpClient,
|
|
33827
33899
|
detectLocalAgentPresence: () => detectLocalAgentPresence3
|
|
33828
33900
|
});
|
|
33829
|
-
import { spawn as
|
|
33901
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
33830
33902
|
import * as readline from "node:readline";
|
|
33831
33903
|
|
|
33832
33904
|
// src/agents/acp/format-session-update-kind-for-log.ts
|
|
@@ -33896,7 +33968,7 @@ async function createCursorAcpClient(options) {
|
|
|
33896
33968
|
} = options;
|
|
33897
33969
|
const dbgFs = process.env.BUILDAUTOMATON_DEBUG_ACP_FS === "1";
|
|
33898
33970
|
const isWindows = process.platform === "win32";
|
|
33899
|
-
const child =
|
|
33971
|
+
const child = spawn4(command[0], command.slice(1), {
|
|
33900
33972
|
cwd,
|
|
33901
33973
|
stdio: ["pipe", "pipe", "pipe"],
|
|
33902
33974
|
env: process.env,
|
|
@@ -35342,7 +35414,7 @@ import os8 from "node:os";
|
|
|
35342
35414
|
import * as fs18 from "node:fs";
|
|
35343
35415
|
import * as path21 from "node:path";
|
|
35344
35416
|
|
|
35345
|
-
// src/git/worktree-add.ts
|
|
35417
|
+
// src/git/worktrees/worktree-add.ts
|
|
35346
35418
|
async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
|
|
35347
35419
|
const mainGit = cliSimpleGit(mainRepoPath);
|
|
35348
35420
|
await mainGit.raw(["worktree", "add", "-b", branch, worktreePath, "HEAD"]);
|
|
@@ -35447,7 +35519,7 @@ async function prepareNewSessionWorktrees(options) {
|
|
|
35447
35519
|
};
|
|
35448
35520
|
}
|
|
35449
35521
|
|
|
35450
|
-
// src/git/rename-branch.ts
|
|
35522
|
+
// src/git/branches/rename-branch.ts
|
|
35451
35523
|
async function gitRenameCurrentBranch(repoDir, newName) {
|
|
35452
35524
|
const g = cliSimpleGit(repoDir);
|
|
35453
35525
|
await g.raw(["branch", "-m", newName]);
|
|
@@ -35471,10 +35543,10 @@ async function renameSessionWorktreeBranches(paths, newBranch, log2) {
|
|
|
35471
35543
|
// src/worktrees/remove-session-worktrees.ts
|
|
35472
35544
|
import * as fs21 from "node:fs";
|
|
35473
35545
|
|
|
35474
|
-
// src/git/worktree-remove.ts
|
|
35546
|
+
// src/git/worktrees/worktree-remove.ts
|
|
35475
35547
|
import * as fs20 from "node:fs";
|
|
35476
35548
|
|
|
35477
|
-
// src/git/resolve-main-repo-from-git-file.ts
|
|
35549
|
+
// src/git/worktrees/resolve-main-repo-from-git-file.ts
|
|
35478
35550
|
import * as fs19 from "node:fs";
|
|
35479
35551
|
import * as path22 from "node:path";
|
|
35480
35552
|
function resolveMainRepoFromWorktreeGitFile(wt) {
|
|
@@ -35488,7 +35560,7 @@ function resolveMainRepoFromWorktreeGitFile(wt) {
|
|
|
35488
35560
|
return path22.dirname(gitDir);
|
|
35489
35561
|
}
|
|
35490
35562
|
|
|
35491
|
-
// src/git/worktree-remove.ts
|
|
35563
|
+
// src/git/worktrees/worktree-remove.ts
|
|
35492
35564
|
async function gitWorktreeRemoveForce(worktreePath) {
|
|
35493
35565
|
const mainRepo = resolveMainRepoFromWorktreeGitFile(worktreePath);
|
|
35494
35566
|
if (mainRepo) {
|
|
@@ -35514,7 +35586,88 @@ async function removeSessionWorktrees(paths, log2) {
|
|
|
35514
35586
|
}
|
|
35515
35587
|
}
|
|
35516
35588
|
|
|
35517
|
-
// src/git/
|
|
35589
|
+
// src/git/changes/lib/parse-git-status.ts
|
|
35590
|
+
function parseNameStatusLines(lines) {
|
|
35591
|
+
const m = /* @__PURE__ */ new Map();
|
|
35592
|
+
for (const line of lines) {
|
|
35593
|
+
if (!line.trim()) continue;
|
|
35594
|
+
const tabParts = line.split(" ");
|
|
35595
|
+
if (tabParts.length < 2) continue;
|
|
35596
|
+
const status = tabParts[0].trim();
|
|
35597
|
+
const code = status[0];
|
|
35598
|
+
if (code === "A") {
|
|
35599
|
+
m.set(tabParts[tabParts.length - 1], "added");
|
|
35600
|
+
} else if (code === "D") {
|
|
35601
|
+
m.set(tabParts[tabParts.length - 1], "removed");
|
|
35602
|
+
} else if (code === "R" || code === "C") {
|
|
35603
|
+
if (tabParts.length >= 3) m.set(tabParts[tabParts.length - 1], "modified");
|
|
35604
|
+
} else if (code === "M" || code === "U" || code === "T") {
|
|
35605
|
+
m.set(tabParts[tabParts.length - 1], "modified");
|
|
35606
|
+
}
|
|
35607
|
+
}
|
|
35608
|
+
return m;
|
|
35609
|
+
}
|
|
35610
|
+
function parseNumstatFirstLine(line) {
|
|
35611
|
+
const parts = line.split(" ");
|
|
35612
|
+
if (parts.length < 3) return null;
|
|
35613
|
+
const [a, d] = parts;
|
|
35614
|
+
const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
|
|
35615
|
+
const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
|
|
35616
|
+
return { additions, deletions };
|
|
35617
|
+
}
|
|
35618
|
+
function parseNumstat(lines) {
|
|
35619
|
+
const m = /* @__PURE__ */ new Map();
|
|
35620
|
+
for (const line of lines) {
|
|
35621
|
+
if (!line.trim()) continue;
|
|
35622
|
+
const parts = line.split(" ");
|
|
35623
|
+
if (parts.length < 3) continue;
|
|
35624
|
+
const [a, d, p] = parts;
|
|
35625
|
+
const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
|
|
35626
|
+
const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
|
|
35627
|
+
m.set(p, { additions, deletions });
|
|
35628
|
+
}
|
|
35629
|
+
return m;
|
|
35630
|
+
}
|
|
35631
|
+
async function numstatFromGitNoIndex(g, pathInRepo) {
|
|
35632
|
+
const devNull = process.platform === "win32" ? "NUL" : "/dev/null";
|
|
35633
|
+
try {
|
|
35634
|
+
const out = await g.raw(["diff", "--numstat", "--no-index", "--", devNull, pathInRepo]);
|
|
35635
|
+
const first2 = String(out).split("\n").find((l) => l.trim()) ?? "";
|
|
35636
|
+
return parseNumstatFirstLine(first2);
|
|
35637
|
+
} catch {
|
|
35638
|
+
return null;
|
|
35639
|
+
}
|
|
35640
|
+
}
|
|
35641
|
+
|
|
35642
|
+
// src/git/changes/lib/working-tree-changed-path-count.ts
|
|
35643
|
+
function workingTreeChangedPathCount(nameStatusLines, numstatLines, untrackedLines) {
|
|
35644
|
+
const kindByPath = parseNameStatusLines(nameStatusLines);
|
|
35645
|
+
const numByPath = parseNumstat(numstatLines);
|
|
35646
|
+
const paths = /* @__PURE__ */ new Set([...kindByPath.keys(), ...numByPath.keys()]);
|
|
35647
|
+
for (const p of untrackedLines.map((s) => s.trim()).filter(Boolean)) {
|
|
35648
|
+
paths.add(p);
|
|
35649
|
+
}
|
|
35650
|
+
return paths.size;
|
|
35651
|
+
}
|
|
35652
|
+
|
|
35653
|
+
// src/git/changes/count-working-tree-changed-files.ts
|
|
35654
|
+
async function countWorkingTreeChangedFilesForRepo(repoGitCwd) {
|
|
35655
|
+
return runGitTask(async () => {
|
|
35656
|
+
const g = cliSimpleGit(repoGitCwd);
|
|
35657
|
+
const [nameStatusRaw, numstatRaw, untrackedRaw] = await Promise.all([
|
|
35658
|
+
g.raw(["diff", "--name-status", "HEAD"]).catch(() => ""),
|
|
35659
|
+
g.raw(["diff", "HEAD", "--numstat"]).catch(() => ""),
|
|
35660
|
+
g.raw(["ls-files", "--others", "--exclude-standard"]).catch(() => "")
|
|
35661
|
+
]);
|
|
35662
|
+
return workingTreeChangedPathCount(
|
|
35663
|
+
String(nameStatusRaw).split("\n"),
|
|
35664
|
+
String(numstatRaw).split("\n"),
|
|
35665
|
+
String(untrackedRaw).split("\n")
|
|
35666
|
+
);
|
|
35667
|
+
});
|
|
35668
|
+
}
|
|
35669
|
+
|
|
35670
|
+
// src/git/commits/resolve-remote-tracking.ts
|
|
35518
35671
|
async function tryConfigGet(g, key) {
|
|
35519
35672
|
try {
|
|
35520
35673
|
const out = await g.raw(["config", "--get", key]);
|
|
@@ -35597,30 +35750,6 @@ async function resolveBaseShaForUnpushedCommits(g) {
|
|
|
35597
35750
|
if (!defaultRef) return null;
|
|
35598
35751
|
return revParseSafe(g, defaultRef);
|
|
35599
35752
|
}
|
|
35600
|
-
function parseLogShaDateSubjectLines(raw) {
|
|
35601
|
-
const out = [];
|
|
35602
|
-
for (const line of String(raw).split("\n")) {
|
|
35603
|
-
const l = line.trimEnd();
|
|
35604
|
-
if (!l.trim()) continue;
|
|
35605
|
-
const parts = l.split(" ");
|
|
35606
|
-
if (parts.length < 3) continue;
|
|
35607
|
-
const sha = parts[0].trim();
|
|
35608
|
-
const committedAt = parts[1].trim();
|
|
35609
|
-
const subject = parts.slice(2).join(" ").trim();
|
|
35610
|
-
if (!/^[0-9a-f]{7,40}$/i.test(sha)) continue;
|
|
35611
|
-
out.push({ sha, shortSha: sha.slice(0, 7), subject, committedAt });
|
|
35612
|
-
}
|
|
35613
|
-
return out;
|
|
35614
|
-
}
|
|
35615
|
-
async function gitLogNotReachableFromBase(g, baseSha, headSha) {
|
|
35616
|
-
if (baseSha === headSha) return [];
|
|
35617
|
-
try {
|
|
35618
|
-
const logOut = await g.raw(["log", "--format=%H %cI %s", `${baseSha}..${headSha}`]);
|
|
35619
|
-
return parseLogShaDateSubjectLines(logOut);
|
|
35620
|
-
} catch {
|
|
35621
|
-
return [];
|
|
35622
|
-
}
|
|
35623
|
-
}
|
|
35624
35753
|
async function commitsAheadOfRemoteTracking(repoDir) {
|
|
35625
35754
|
const g = cliSimpleGit(repoDir);
|
|
35626
35755
|
const headSha = await revParseSafe(g, "HEAD");
|
|
@@ -35635,44 +35764,41 @@ async function commitsAheadOfRemoteTracking(repoDir) {
|
|
|
35635
35764
|
return 0;
|
|
35636
35765
|
}
|
|
35637
35766
|
}
|
|
35767
|
+
|
|
35768
|
+
// src/git/status/working-tree-status.ts
|
|
35638
35769
|
async function getRepoWorkingTreeStatus(repoDir) {
|
|
35639
|
-
|
|
35640
|
-
|
|
35641
|
-
|
|
35642
|
-
|
|
35643
|
-
|
|
35644
|
-
}
|
|
35645
|
-
async function listUnpushedCommits(repoDir) {
|
|
35646
|
-
const g = cliSimpleGit(repoDir);
|
|
35647
|
-
const headSha = await revParseSafe(g, "HEAD");
|
|
35648
|
-
if (!headSha) return [];
|
|
35649
|
-
const baseSha = await resolveBaseShaForUnpushedCommits(g);
|
|
35650
|
-
if (!baseSha) return [];
|
|
35651
|
-
return gitLogNotReachableFromBase(g, baseSha, headSha);
|
|
35770
|
+
return runGitTask(async () => {
|
|
35771
|
+
const uncommittedFileCount = await countWorkingTreeChangedFilesForRepo(repoDir);
|
|
35772
|
+
const hasUncommittedChanges = uncommittedFileCount > 0;
|
|
35773
|
+
const ahead = await commitsAheadOfRemoteTracking(repoDir);
|
|
35774
|
+
return { hasUncommittedChanges, hasUnpushedCommits: ahead > 0, uncommittedFileCount };
|
|
35775
|
+
});
|
|
35652
35776
|
}
|
|
35653
35777
|
async function aggregateSessionPathsWorkingTreeStatus(paths) {
|
|
35654
35778
|
let hasUncommittedChanges = false;
|
|
35655
35779
|
let hasUnpushedCommits = false;
|
|
35656
|
-
|
|
35780
|
+
let uncommittedFileCount = 0;
|
|
35781
|
+
await forEachWithGitYield(paths, async (p) => {
|
|
35657
35782
|
const s = await getRepoWorkingTreeStatus(p);
|
|
35783
|
+
uncommittedFileCount += s.uncommittedFileCount;
|
|
35658
35784
|
if (s.hasUncommittedChanges) hasUncommittedChanges = true;
|
|
35659
35785
|
if (s.hasUnpushedCommits) hasUnpushedCommits = true;
|
|
35660
|
-
}
|
|
35661
|
-
return { hasUncommittedChanges, hasUnpushedCommits };
|
|
35786
|
+
});
|
|
35787
|
+
return { hasUncommittedChanges, hasUnpushedCommits, uncommittedFileCount };
|
|
35662
35788
|
}
|
|
35663
35789
|
async function pushAheadOfUpstreamForPaths(paths) {
|
|
35664
|
-
|
|
35790
|
+
await forEachWithGitYield(paths, async (p) => {
|
|
35665
35791
|
const g = cliSimpleGit(p);
|
|
35666
35792
|
const ahead = await commitsAheadOfRemoteTracking(p);
|
|
35667
|
-
if (ahead <= 0)
|
|
35793
|
+
if (ahead <= 0) return;
|
|
35668
35794
|
await g.push();
|
|
35669
|
-
}
|
|
35795
|
+
});
|
|
35670
35796
|
}
|
|
35671
35797
|
|
|
35672
|
-
// src/git/
|
|
35798
|
+
// src/git/changes/types.ts
|
|
35673
35799
|
var MAX_PATCH_CHARS = 35e4;
|
|
35674
35800
|
|
|
35675
|
-
// src/git/
|
|
35801
|
+
// src/git/changes/lib/repo-format.ts
|
|
35676
35802
|
function posixJoinDirFile(dir, file2) {
|
|
35677
35803
|
const d = dir === "." || dir === "" ? "" : dir.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
35678
35804
|
const f = file2.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
@@ -35726,63 +35852,80 @@ function formatRemoteDisplayLabel(remoteUrl) {
|
|
|
35726
35852
|
return `origin \xB7 ${hostPath}`;
|
|
35727
35853
|
}
|
|
35728
35854
|
|
|
35729
|
-
// src/git/
|
|
35855
|
+
// src/git/changes/get-working-tree-change-repo-details.ts
|
|
35730
35856
|
import * as path24 from "node:path";
|
|
35731
35857
|
|
|
35732
|
-
// src/git/
|
|
35733
|
-
function
|
|
35734
|
-
const
|
|
35735
|
-
for (const line of
|
|
35736
|
-
|
|
35737
|
-
|
|
35738
|
-
|
|
35739
|
-
|
|
35740
|
-
const
|
|
35741
|
-
|
|
35742
|
-
|
|
35743
|
-
|
|
35744
|
-
|
|
35745
|
-
} else if (code === "R" || code === "C") {
|
|
35746
|
-
if (tabParts.length >= 3) m.set(tabParts[tabParts.length - 1], "modified");
|
|
35747
|
-
} else if (code === "M" || code === "U" || code === "T") {
|
|
35748
|
-
m.set(tabParts[tabParts.length - 1], "modified");
|
|
35749
|
-
}
|
|
35858
|
+
// src/git/commits/lib/parse-log-lines.ts
|
|
35859
|
+
function parseLogShaDateSubjectLines(raw) {
|
|
35860
|
+
const out = [];
|
|
35861
|
+
for (const line of String(raw).split("\n")) {
|
|
35862
|
+
const l = line.trimEnd();
|
|
35863
|
+
if (!l.trim()) continue;
|
|
35864
|
+
const parts = l.split(" ");
|
|
35865
|
+
if (parts.length < 3) continue;
|
|
35866
|
+
const sha = parts[0].trim();
|
|
35867
|
+
const committedAt = parts[1].trim();
|
|
35868
|
+
const subject = parts.slice(2).join(" ").trim();
|
|
35869
|
+
if (!/^[0-9a-f]{7,40}$/i.test(sha)) continue;
|
|
35870
|
+
out.push({ sha, shortSha: sha.slice(0, 7), subject, committedAt });
|
|
35750
35871
|
}
|
|
35751
|
-
return
|
|
35752
|
-
}
|
|
35753
|
-
function parseNumstatFirstLine(line) {
|
|
35754
|
-
const parts = line.split(" ");
|
|
35755
|
-
if (parts.length < 3) return null;
|
|
35756
|
-
const [a, d] = parts;
|
|
35757
|
-
const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
|
|
35758
|
-
const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
|
|
35759
|
-
return { additions, deletions };
|
|
35872
|
+
return out;
|
|
35760
35873
|
}
|
|
35761
|
-
|
|
35762
|
-
|
|
35763
|
-
|
|
35764
|
-
|
|
35765
|
-
|
|
35766
|
-
|
|
35767
|
-
|
|
35768
|
-
|
|
35769
|
-
|
|
35770
|
-
m.set(p, { additions, deletions });
|
|
35874
|
+
|
|
35875
|
+
// src/git/commits/list-unpushed-commits.ts
|
|
35876
|
+
async function gitLogNotReachableFromBase(g, baseSha, headSha) {
|
|
35877
|
+
if (baseSha === headSha) return [];
|
|
35878
|
+
try {
|
|
35879
|
+
const logOut = await g.raw(["log", "--format=%H %cI %s", `${baseSha}..${headSha}`]);
|
|
35880
|
+
return parseLogShaDateSubjectLines(logOut);
|
|
35881
|
+
} catch {
|
|
35882
|
+
return [];
|
|
35771
35883
|
}
|
|
35772
|
-
return m;
|
|
35773
35884
|
}
|
|
35774
|
-
async function
|
|
35775
|
-
const
|
|
35885
|
+
async function listUnpushedCommits(repoDir) {
|
|
35886
|
+
const g = cliSimpleGit(repoDir);
|
|
35887
|
+
const headSha = await revParseSafe(g, "HEAD");
|
|
35888
|
+
if (!headSha) return [];
|
|
35889
|
+
const baseSha = await resolveBaseShaForUnpushedCommits(g);
|
|
35890
|
+
if (!baseSha) return [];
|
|
35891
|
+
return gitLogNotReachableFromBase(g, baseSha, headSha);
|
|
35892
|
+
}
|
|
35893
|
+
|
|
35894
|
+
// src/git/commits/lib/sanitize-recent-commits-limit.ts
|
|
35895
|
+
var RECENT_COMMITS_LIMIT_MIN = 1;
|
|
35896
|
+
var RECENT_COMMITS_LIMIT_MAX = 50;
|
|
35897
|
+
var RECENT_COMMITS_LIMIT_DEFAULT = 2;
|
|
35898
|
+
function sanitizeRecentCommitsLimit(value) {
|
|
35899
|
+
const n = typeof value === "number" ? value : typeof value === "string" ? Number.parseInt(value.trim(), 10) : Number.NaN;
|
|
35900
|
+
if (!Number.isFinite(n)) return RECENT_COMMITS_LIMIT_DEFAULT;
|
|
35901
|
+
return Math.min(RECENT_COMMITS_LIMIT_MAX, Math.max(RECENT_COMMITS_LIMIT_MIN, Math.trunc(n)));
|
|
35902
|
+
}
|
|
35903
|
+
|
|
35904
|
+
// src/git/commits/list-recent-commits.ts
|
|
35905
|
+
async function listRecentCommits(repoDir, limitInput) {
|
|
35906
|
+
const limit = sanitizeRecentCommitsLimit(limitInput);
|
|
35907
|
+
const g = cliSimpleGit(repoDir);
|
|
35908
|
+
const headSha = await revParseSafe(g, "HEAD");
|
|
35909
|
+
if (!headSha) return { commits: [], hasMore: false };
|
|
35910
|
+
const unpushedSet = new Set((await listUnpushedCommits(repoDir)).map((c) => c.sha));
|
|
35776
35911
|
try {
|
|
35777
|
-
const
|
|
35778
|
-
const
|
|
35779
|
-
|
|
35912
|
+
const logOut = await g.raw(["log", "--format=%H %cI %s", `-n`, String(limit), "HEAD"]);
|
|
35913
|
+
const commits = parseLogShaDateSubjectLines(logOut).map((c) => ({
|
|
35914
|
+
...c,
|
|
35915
|
+
needsPush: unpushedSet.has(c.sha)
|
|
35916
|
+
}));
|
|
35917
|
+
let hasMore = false;
|
|
35918
|
+
if (commits.length === limit) {
|
|
35919
|
+
const nextOut = await g.raw(["log", "-n", "1", `--skip=${limit}`, "--format=%H", "HEAD"]);
|
|
35920
|
+
hasMore = /^[0-9a-f]{7,40}$/i.test(String(nextOut).trim());
|
|
35921
|
+
}
|
|
35922
|
+
return { commits, hasMore };
|
|
35780
35923
|
} catch {
|
|
35781
|
-
return
|
|
35924
|
+
return { commits: [], hasMore: false };
|
|
35782
35925
|
}
|
|
35783
35926
|
}
|
|
35784
35927
|
|
|
35785
|
-
// src/git/
|
|
35928
|
+
// src/git/changes/lib/patch-truncate.ts
|
|
35786
35929
|
function truncatePatch(s) {
|
|
35787
35930
|
if (s.length <= MAX_PATCH_CHARS) return s;
|
|
35788
35931
|
return `${s.slice(0, MAX_PATCH_CHARS)}
|
|
@@ -35790,7 +35933,7 @@ function truncatePatch(s) {
|
|
|
35790
35933
|
\u2026 (diff truncated)`;
|
|
35791
35934
|
}
|
|
35792
35935
|
|
|
35793
|
-
// src/git/
|
|
35936
|
+
// src/git/commits/list-changed-files-for-commit.ts
|
|
35794
35937
|
var EMPTY_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
35795
35938
|
async function parentForCommitDiff(g, sha) {
|
|
35796
35939
|
try {
|
|
@@ -35816,7 +35959,7 @@ async function listChangedFilesForCommit(repoGitCwd, repoRelPath, commitSha) {
|
|
|
35816
35959
|
const paths = new Set([...kindByPath.keys(), ...numByPath.keys()].filter(Boolean));
|
|
35817
35960
|
const rows = [];
|
|
35818
35961
|
const normRel = repoRelPath === "." || repoRelPath === "" ? "." : repoRelPath;
|
|
35819
|
-
|
|
35962
|
+
await forEachWithGitYield([...paths], async (pathInRepo) => {
|
|
35820
35963
|
const relLauncher = posixJoinDirFile(normRel, pathInRepo.replace(/\\/g, "/"));
|
|
35821
35964
|
const nums = numByPath.get(pathInRepo);
|
|
35822
35965
|
let additions = nums?.additions ?? 0;
|
|
@@ -35828,8 +35971,8 @@ async function listChangedFilesForCommit(repoGitCwd, repoRelPath, commitSha) {
|
|
|
35828
35971
|
else change = "modified";
|
|
35829
35972
|
}
|
|
35830
35973
|
rows.push({ pathRelLauncher: relLauncher, additions, deletions, change });
|
|
35831
|
-
}
|
|
35832
|
-
|
|
35974
|
+
});
|
|
35975
|
+
await forEachWithGitYield(rows, async (row) => {
|
|
35833
35976
|
let pathInRepo;
|
|
35834
35977
|
if (normRel === ".") {
|
|
35835
35978
|
pathInRepo = row.pathRelLauncher;
|
|
@@ -35841,16 +35984,16 @@ async function listChangedFilesForCommit(repoGitCwd, repoRelPath, commitSha) {
|
|
|
35841
35984
|
const raw = await g.raw(["diff", "-U20000", range, "--", pathInRepo]).catch(() => "");
|
|
35842
35985
|
const t = String(raw).trim();
|
|
35843
35986
|
row.patchContent = t ? truncatePatch(t) : void 0;
|
|
35844
|
-
}
|
|
35987
|
+
});
|
|
35845
35988
|
rows.sort((a, b) => a.pathRelLauncher.localeCompare(b.pathRelLauncher));
|
|
35846
35989
|
return rows;
|
|
35847
35990
|
}
|
|
35848
35991
|
|
|
35849
|
-
// src/git/
|
|
35992
|
+
// src/git/changes/list-changed-files-for-repo.ts
|
|
35850
35993
|
import * as fs23 from "node:fs";
|
|
35851
35994
|
import * as path23 from "node:path";
|
|
35852
35995
|
|
|
35853
|
-
// src/git/
|
|
35996
|
+
// src/git/changes/lib/count-lines.ts
|
|
35854
35997
|
import { createReadStream } from "node:fs";
|
|
35855
35998
|
import * as readline2 from "node:readline";
|
|
35856
35999
|
async function countTextFileLines(filePath) {
|
|
@@ -35871,7 +36014,7 @@ async function countTextFileLines(filePath) {
|
|
|
35871
36014
|
return lines;
|
|
35872
36015
|
}
|
|
35873
36016
|
|
|
35874
|
-
// src/git/
|
|
36017
|
+
// src/git/changes/hydrate-patch.ts
|
|
35875
36018
|
import * as fs22 from "node:fs";
|
|
35876
36019
|
var UNIFIED_HUNK_HEADER_RE = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
|
|
35877
36020
|
var MAX_HYDRATE_LINES_PER_GAP = 8e3;
|
|
@@ -35987,26 +36130,16 @@ async function hydrateUnifiedPatchWithFileContext(patch, filePath, repoGitCwd, p
|
|
|
35987
36130
|
return truncatePatch(out.join("\n"));
|
|
35988
36131
|
}
|
|
35989
36132
|
|
|
35990
|
-
// src/git/
|
|
36133
|
+
// src/git/changes/unified-diff-for-file.ts
|
|
35991
36134
|
async function unifiedDiffForFile(repoCwd, pathInRepo, change) {
|
|
35992
36135
|
const g = cliSimpleGit(repoCwd);
|
|
35993
|
-
|
|
35994
|
-
|
|
35995
|
-
|
|
35996
|
-
|
|
35997
|
-
raw = await g.raw(["diff", "--no-index", "--", devNull, pathInRepo]);
|
|
35998
|
-
} else {
|
|
35999
|
-
raw = await g.raw(["diff", "HEAD", "--", pathInRepo]);
|
|
36000
|
-
}
|
|
36001
|
-
const t = String(raw).trim();
|
|
36002
|
-
if (!t) return void 0;
|
|
36003
|
-
return truncatePatch(t);
|
|
36004
|
-
} catch {
|
|
36005
|
-
return void 0;
|
|
36006
|
-
}
|
|
36136
|
+
const args = change === "added" ? ["diff", "--no-color", "HEAD", "--", pathInRepo] : change === "removed" ? ["diff", "--no-color", "HEAD", "--", pathInRepo] : ["diff", "--no-color", "HEAD", "--", pathInRepo];
|
|
36137
|
+
const raw = await g.raw([...args]).catch(() => "");
|
|
36138
|
+
const t = String(raw).trim();
|
|
36139
|
+
return t ? truncatePatch(t) : void 0;
|
|
36007
36140
|
}
|
|
36008
36141
|
|
|
36009
|
-
// src/git/
|
|
36142
|
+
// src/git/changes/list-changed-files-for-repo.ts
|
|
36010
36143
|
async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
36011
36144
|
const g = cliSimpleGit(repoGitCwd);
|
|
36012
36145
|
const [nameStatusRaw, numstatRaw, untrackedRaw] = await Promise.all([
|
|
@@ -36020,7 +36153,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
|
36020
36153
|
const untracked = String(untrackedRaw).split("\n").map((s) => s.trim()).filter(Boolean);
|
|
36021
36154
|
for (const p of untracked) paths.add(p);
|
|
36022
36155
|
const rows = [];
|
|
36023
|
-
|
|
36156
|
+
await forEachWithGitYield([...paths], async (pathInRepo) => {
|
|
36024
36157
|
const relLauncher = posixJoinDirFile(repoRelPath, pathInRepo.replace(/\\/g, "/"));
|
|
36025
36158
|
const repoFilePath = path23.join(repoGitCwd, pathInRepo);
|
|
36026
36159
|
const nums = numByPath.get(pathInRepo);
|
|
@@ -36050,9 +36183,9 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
|
36050
36183
|
else change = "modified";
|
|
36051
36184
|
}
|
|
36052
36185
|
rows.push({ pathRelLauncher: relLauncher, additions, deletions, change });
|
|
36053
|
-
}
|
|
36186
|
+
});
|
|
36054
36187
|
const normRel = repoRelPath === "." || repoRelPath === "" ? "." : repoRelPath;
|
|
36055
|
-
|
|
36188
|
+
await forEachWithGitYield(rows, async (row) => {
|
|
36056
36189
|
let pathInRepo;
|
|
36057
36190
|
if (normRel === ".") {
|
|
36058
36191
|
pathInRepo = row.pathRelLauncher;
|
|
@@ -36067,11 +36200,11 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
|
36067
36200
|
patch = await hydrateUnifiedPatchWithFileContext(patch, filePath, repoGitCwd, pathInRepo, row.change);
|
|
36068
36201
|
}
|
|
36069
36202
|
row.patchContent = patch;
|
|
36070
|
-
}
|
|
36203
|
+
});
|
|
36071
36204
|
return rows;
|
|
36072
36205
|
}
|
|
36073
36206
|
|
|
36074
|
-
// src/git/
|
|
36207
|
+
// src/git/changes/get-working-tree-change-repo-details.ts
|
|
36075
36208
|
function normRepoRel(p) {
|
|
36076
36209
|
const x = p.replace(/\\/g, "/").trim();
|
|
36077
36210
|
return x === "" ? "." : x;
|
|
@@ -36090,7 +36223,9 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
36090
36223
|
throw new Error("commit sha is required for commit changes");
|
|
36091
36224
|
}
|
|
36092
36225
|
const basis = filter == null && basisInput.kind === "commit" ? { kind: "working" } : basisInput;
|
|
36093
|
-
for (
|
|
36226
|
+
for (let i = 0; i < options.commitTargetPaths.length; i++) {
|
|
36227
|
+
if (i > 0) await yieldToEventLoop2();
|
|
36228
|
+
const target = options.commitTargetPaths[i];
|
|
36094
36229
|
const t = path24.resolve(target);
|
|
36095
36230
|
if (!await isGitRepoDirectory(t)) continue;
|
|
36096
36231
|
const g = cliSimpleGit(t);
|
|
@@ -36124,7 +36259,10 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
36124
36259
|
const files = basis.kind === "commit" ? await listChangedFilesForCommit(t, relForList, basis.sha.trim()) : await listChangedFilesForRepo(t, relForList);
|
|
36125
36260
|
const st = await g.status();
|
|
36126
36261
|
const hasUncommittedChanges = (st.files?.length ?? 0) > 0;
|
|
36127
|
-
const unpushedCommits = await
|
|
36262
|
+
const [unpushedCommits, recentCommitList] = await Promise.all([
|
|
36263
|
+
listUnpushedCommits(t),
|
|
36264
|
+
listRecentCommits(t, options.recentCommitsLimit)
|
|
36265
|
+
]);
|
|
36128
36266
|
out.push({
|
|
36129
36267
|
repoRelPath: norm,
|
|
36130
36268
|
repoDisplayName,
|
|
@@ -36134,6 +36272,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
36134
36272
|
files,
|
|
36135
36273
|
hasUncommittedChanges,
|
|
36136
36274
|
unpushedCommits,
|
|
36275
|
+
recentCommits: recentCommitList.commits,
|
|
36276
|
+
recentCommitsHasMore: recentCommitList.hasMore,
|
|
36137
36277
|
changesView: basis.kind === "commit" ? "commit" : "working",
|
|
36138
36278
|
changesCommitSha: basis.kind === "commit" ? basis.sha.trim() : null
|
|
36139
36279
|
});
|
|
@@ -36142,7 +36282,7 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
36142
36282
|
return out;
|
|
36143
36283
|
}
|
|
36144
36284
|
|
|
36145
|
-
// src/git/commit-and-push.ts
|
|
36285
|
+
// src/git/branches/commit-and-push.ts
|
|
36146
36286
|
async function gitCommitAllIfDirty(repoDir, message, options) {
|
|
36147
36287
|
const g = cliSimpleGit(repoDir);
|
|
36148
36288
|
const st = await g.status();
|
|
@@ -36566,7 +36706,8 @@ var SessionWorktreeManager = class {
|
|
|
36566
36706
|
sessionWorktreeRootPath: sessionWorkingTreeRelRoot,
|
|
36567
36707
|
legacyRepoNestedSessionLayout: legacyNested,
|
|
36568
36708
|
repoFilterRelPath: opts?.repoRelPath?.trim() ? opts.repoRelPath.trim() : null,
|
|
36569
|
-
basis: opts?.basis
|
|
36709
|
+
basis: opts?.basis,
|
|
36710
|
+
recentCommitsLimit: opts?.recentCommitsLimit
|
|
36570
36711
|
});
|
|
36571
36712
|
}
|
|
36572
36713
|
async pushSessionUpstream(sessionId) {
|
|
@@ -37096,7 +37237,7 @@ function pipedStdoutStderrFor(attemptStdio) {
|
|
|
37096
37237
|
}
|
|
37097
37238
|
|
|
37098
37239
|
// src/dev-servers/manager/shell-spawn/try-spawn-piped-via-sh.ts
|
|
37099
|
-
import { spawn as
|
|
37240
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
37100
37241
|
function trySpawnPipedViaSh(command, env, cwd, signal) {
|
|
37101
37242
|
const attempts = [
|
|
37102
37243
|
{ stdio: [devNullReadFd(), "pipe", "pipe"], endStdin: false },
|
|
@@ -37117,9 +37258,9 @@ function trySpawnPipedViaSh(command, env, cwd, signal) {
|
|
|
37117
37258
|
if (process.platform === "win32") {
|
|
37118
37259
|
opts.windowsHide = true;
|
|
37119
37260
|
const com = process.env.ComSpec || "cmd.exe";
|
|
37120
|
-
proc =
|
|
37261
|
+
proc = spawn5(com, ["/d", "/s", "/c", command], opts);
|
|
37121
37262
|
} else {
|
|
37122
|
-
proc =
|
|
37263
|
+
proc = spawn5("/bin/sh", ["-c", command], opts);
|
|
37123
37264
|
}
|
|
37124
37265
|
if (attempt.endStdin) {
|
|
37125
37266
|
proc.stdin?.end();
|
|
@@ -37139,7 +37280,7 @@ function trySpawnPipedViaSh(command, env, cwd, signal) {
|
|
|
37139
37280
|
}
|
|
37140
37281
|
|
|
37141
37282
|
// src/dev-servers/manager/shell-spawn/try-spawn-shell-true-piped.ts
|
|
37142
|
-
import { spawn as
|
|
37283
|
+
import { spawn as spawn6 } from "node:child_process";
|
|
37143
37284
|
function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
|
|
37144
37285
|
try {
|
|
37145
37286
|
const opts = {
|
|
@@ -37152,7 +37293,7 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
|
|
|
37152
37293
|
if (process.platform === "win32") {
|
|
37153
37294
|
opts.windowsHide = true;
|
|
37154
37295
|
}
|
|
37155
|
-
return
|
|
37296
|
+
return spawn6(command, opts);
|
|
37156
37297
|
} catch (e) {
|
|
37157
37298
|
if (isSpawnEbadf(e)) return null;
|
|
37158
37299
|
throw e;
|
|
@@ -37160,7 +37301,7 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
|
|
|
37160
37301
|
}
|
|
37161
37302
|
|
|
37162
37303
|
// src/dev-servers/manager/shell-spawn/try-spawn-merged-log-file.ts
|
|
37163
|
-
import { spawn as
|
|
37304
|
+
import { spawn as spawn7 } from "node:child_process";
|
|
37164
37305
|
import fs29 from "node:fs";
|
|
37165
37306
|
import { tmpdir } from "node:os";
|
|
37166
37307
|
import path32 from "node:path";
|
|
@@ -37178,7 +37319,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
|
37178
37319
|
try {
|
|
37179
37320
|
let proc;
|
|
37180
37321
|
if (process.platform === "win32") {
|
|
37181
|
-
proc =
|
|
37322
|
+
proc = spawn7(process.env.ComSpec || "cmd.exe", ["/d", "/s", "/c", command], {
|
|
37182
37323
|
env,
|
|
37183
37324
|
cwd,
|
|
37184
37325
|
stdio,
|
|
@@ -37186,7 +37327,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
|
37186
37327
|
...signal ? { signal } : {}
|
|
37187
37328
|
});
|
|
37188
37329
|
} else {
|
|
37189
|
-
proc =
|
|
37330
|
+
proc = spawn7("/bin/sh", ["-c", command], { env, cwd, stdio, ...signal ? { signal } : {} });
|
|
37190
37331
|
}
|
|
37191
37332
|
fs29.closeSync(logFd);
|
|
37192
37333
|
return {
|
|
@@ -37207,7 +37348,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
|
37207
37348
|
}
|
|
37208
37349
|
|
|
37209
37350
|
// src/dev-servers/manager/shell-spawn/try-spawn-shell-script-log-redirect.ts
|
|
37210
|
-
import { spawn as
|
|
37351
|
+
import { spawn as spawn8 } from "node:child_process";
|
|
37211
37352
|
import fs30 from "node:fs";
|
|
37212
37353
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
37213
37354
|
import path33 from "node:path";
|
|
@@ -37230,7 +37371,7 @@ cd ${shSingleQuote(cwd)}
|
|
|
37230
37371
|
/bin/sh ${shSingleQuote(innerPath)} >>${shSingleQuote(logPath)} 2>&1
|
|
37231
37372
|
`
|
|
37232
37373
|
);
|
|
37233
|
-
const proc =
|
|
37374
|
+
const proc = spawn8("/bin/sh", [runnerPath], {
|
|
37234
37375
|
env,
|
|
37235
37376
|
cwd: tmpRoot,
|
|
37236
37377
|
stdio: "ignore",
|
|
@@ -37262,7 +37403,7 @@ CD /D ${q(cwd)}\r
|
|
|
37262
37403
|
${command} >> ${q(logPath)} 2>&1\r
|
|
37263
37404
|
`
|
|
37264
37405
|
);
|
|
37265
|
-
const proc =
|
|
37406
|
+
const proc = spawn8(com, ["/d", "/s", "/c", q(runnerPath)], {
|
|
37266
37407
|
env,
|
|
37267
37408
|
cwd: tmpRoot,
|
|
37268
37409
|
stdio: "ignore",
|
|
@@ -37283,7 +37424,7 @@ ${command} >> ${q(logPath)} 2>&1\r
|
|
|
37283
37424
|
}
|
|
37284
37425
|
|
|
37285
37426
|
// src/dev-servers/manager/shell-spawn/try-spawn-inherit.ts
|
|
37286
|
-
import { spawn as
|
|
37427
|
+
import { spawn as spawn9 } from "node:child_process";
|
|
37287
37428
|
function trySpawnInheritStdio(command, env, cwd, signal) {
|
|
37288
37429
|
const opts = {
|
|
37289
37430
|
env,
|
|
@@ -37295,9 +37436,9 @@ function trySpawnInheritStdio(command, env, cwd, signal) {
|
|
|
37295
37436
|
if (process.platform === "win32") {
|
|
37296
37437
|
opts.windowsHide = true;
|
|
37297
37438
|
const com = process.env.ComSpec || "cmd.exe";
|
|
37298
|
-
proc =
|
|
37439
|
+
proc = spawn9(com, ["/d", "/s", "/c", command], opts);
|
|
37299
37440
|
} else {
|
|
37300
|
-
proc =
|
|
37441
|
+
proc = spawn9("/bin/sh", ["-c", command], opts);
|
|
37301
37442
|
}
|
|
37302
37443
|
return { proc, pipedStdoutStderr: false };
|
|
37303
37444
|
}
|
|
@@ -38721,12 +38862,12 @@ function createBridgePromptSenders(deps, getWs) {
|
|
|
38721
38862
|
}
|
|
38722
38863
|
|
|
38723
38864
|
// src/agents/acp/from-bridge/bridge-prompt-preamble.ts
|
|
38724
|
-
import { execFile as
|
|
38725
|
-
import { promisify as
|
|
38726
|
-
var
|
|
38865
|
+
import { execFile as execFile8 } from "node:child_process";
|
|
38866
|
+
import { promisify as promisify8 } from "node:util";
|
|
38867
|
+
var execFileAsync7 = promisify8(execFile8);
|
|
38727
38868
|
async function readGitBranch(cwd) {
|
|
38728
38869
|
try {
|
|
38729
|
-
const { stdout } = await
|
|
38870
|
+
const { stdout } = await execFileAsync7("git", ["branch", "--show-current"], { cwd, maxBuffer: 64 * 1024 });
|
|
38730
38871
|
const b = stdout.trim();
|
|
38731
38872
|
return b || null;
|
|
38732
38873
|
} catch {
|
|
@@ -38974,7 +39115,7 @@ var handleSessionRequestResponseMessage = (msg, deps) => {
|
|
|
38974
39115
|
};
|
|
38975
39116
|
|
|
38976
39117
|
// src/skills/preview.ts
|
|
38977
|
-
import { spawn as
|
|
39118
|
+
import { spawn as spawn10 } from "node:child_process";
|
|
38978
39119
|
var PREVIEW_API_BASE_PATH = "/__preview";
|
|
38979
39120
|
var PREVIEW_SECRET_HEADER = "X-Preview-Secret";
|
|
38980
39121
|
var DEFAULT_PORT = 3e3;
|
|
@@ -39053,7 +39194,7 @@ var previewSkill = {
|
|
|
39053
39194
|
const parts = command.split(/\s+/);
|
|
39054
39195
|
const exe = parts[0];
|
|
39055
39196
|
const args = parts.slice(1);
|
|
39056
|
-
previewProcess =
|
|
39197
|
+
previewProcess = spawn10(isWindows && exe === "npm" ? "npm.cmd" : exe, args, {
|
|
39057
39198
|
cwd: getBridgeRoot(),
|
|
39058
39199
|
stdio: ["ignore", "pipe", "pipe"],
|
|
39059
39200
|
env: {
|
|
@@ -39610,7 +39751,8 @@ var handleSessionGitRequestMessage = (msg, deps) => {
|
|
|
39610
39751
|
reply({
|
|
39611
39752
|
ok: true,
|
|
39612
39753
|
hasUncommittedChanges: r.hasUncommittedChanges,
|
|
39613
|
-
hasUnpushedCommits: r.hasUnpushedCommits
|
|
39754
|
+
hasUnpushedCommits: r.hasUnpushedCommits,
|
|
39755
|
+
uncommittedFileCount: r.uncommittedFileCount
|
|
39614
39756
|
});
|
|
39615
39757
|
return;
|
|
39616
39758
|
}
|
|
@@ -39624,7 +39766,12 @@ var handleSessionGitRequestMessage = (msg, deps) => {
|
|
|
39624
39766
|
return;
|
|
39625
39767
|
}
|
|
39626
39768
|
}
|
|
39627
|
-
const
|
|
39769
|
+
const recentCommitsLimit = typeof msg.changesRecentCommitsLimit === "number" || typeof msg.changesRecentCommitsLimit === "string" ? msg.changesRecentCommitsLimit : void 0;
|
|
39770
|
+
const opts = repoRel && view === "commit" && commitSha ? {
|
|
39771
|
+
repoRelPath: repoRel,
|
|
39772
|
+
basis: { kind: "commit", sha: commitSha },
|
|
39773
|
+
recentCommitsLimit
|
|
39774
|
+
} : repoRel ? { repoRelPath: repoRel, basis: { kind: "working" }, recentCommitsLimit } : recentCommitsLimit !== void 0 ? { recentCommitsLimit } : void 0;
|
|
39628
39775
|
const repos = await deps.sessionWorktreeManager.getSessionWorkingTreeChangeDetails(sessionId, opts);
|
|
39629
39776
|
reply({
|
|
39630
39777
|
ok: true,
|
|
@@ -39642,7 +39789,8 @@ var handleSessionGitRequestMessage = (msg, deps) => {
|
|
|
39642
39789
|
reply({
|
|
39643
39790
|
ok: true,
|
|
39644
39791
|
hasUncommittedChanges: st2.hasUncommittedChanges,
|
|
39645
|
-
hasUnpushedCommits: st2.hasUnpushedCommits
|
|
39792
|
+
hasUnpushedCommits: st2.hasUnpushedCommits,
|
|
39793
|
+
uncommittedFileCount: st2.uncommittedFileCount
|
|
39646
39794
|
});
|
|
39647
39795
|
return;
|
|
39648
39796
|
}
|
|
@@ -39667,7 +39815,8 @@ var handleSessionGitRequestMessage = (msg, deps) => {
|
|
|
39667
39815
|
reply({
|
|
39668
39816
|
ok: true,
|
|
39669
39817
|
hasUncommittedChanges: st.hasUncommittedChanges,
|
|
39670
|
-
hasUnpushedCommits: st.hasUnpushedCommits
|
|
39818
|
+
hasUnpushedCommits: st.hasUnpushedCommits,
|
|
39819
|
+
uncommittedFileCount: st.uncommittedFileCount
|
|
39671
39820
|
});
|
|
39672
39821
|
} catch (e) {
|
|
39673
39822
|
reply({ ok: false, error: e instanceof Error ? e.message : String(e) });
|
|
@@ -40567,6 +40716,7 @@ async function runConnectedBridge(options, restartWithoutAuth) {
|
|
|
40567
40716
|
let bridgeClose = null;
|
|
40568
40717
|
const onSignal = (kind) => {
|
|
40569
40718
|
requestCliImmediateShutdown();
|
|
40719
|
+
abortActiveGitChildProcesses();
|
|
40570
40720
|
cleanupKeyCommand?.();
|
|
40571
40721
|
logImmediate(
|
|
40572
40722
|
kind === "interrupt" ? "Keyboard interrupt (Ctrl+C) \u2014 stopping\u2026" : "Stop requested \u2014 shutting down\u2026"
|