@sideboard-ai/core 0.1.67 → 0.1.68
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/{agents-TS3EI2YI.js → agents-JBD7IAGK.js} +6 -6
- package/dist/{agents-OTBZVTZO.js → agents-PE3RHQS5.js} +6 -6
- package/dist/{chunk-K7LM4CLD.js → chunk-47RBLBRK.js} +4 -4
- package/dist/{chunk-B4PRRKMA.js → chunk-6Q7H6SMX.js} +4 -4
- package/dist/{chunk-FGDJOJ2L.js → chunk-AE5VBOFE.js} +1 -1
- package/dist/{chunk-DB3I75FI.js → chunk-BTL7EMGT.js} +4 -4
- package/dist/{chunk-4UIYN56C.js → chunk-D4DPQ552.js} +1 -1
- package/dist/{chunk-WAFIF22N.js → chunk-DPDGMMGI.js} +2 -2
- package/dist/{chunk-4BUCGNZS.js → chunk-EEBDVLDK.js} +22 -20
- package/dist/{chunk-57WWNMFP.js → chunk-N5LMEP65.js} +1 -1
- package/dist/{chunk-JWZNK3CQ.js → chunk-OICHV4DH.js} +2 -2
- package/dist/{chunk-5HTTCYWA.js → chunk-QQC2D4MY.js} +2 -2
- package/dist/{chunk-LW5CMQGZ.js → chunk-REKGFVUX.js} +22 -20
- package/dist/{chunk-7ZOQY4A7.js → chunk-ROXEENBT.js} +2 -2
- package/dist/{chunk-M5V4QIWF.js → chunk-VROPG6QF.js} +1 -1
- package/dist/{chunk-ZRFAZVGL.js → chunk-VX6ACNXS.js} +1 -1
- package/dist/{connected-teams-RUNMOCG5.js → connected-teams-35EIRJCP.js} +2 -2
- package/dist/{connected-teams-OMDHTL6A.js → connected-teams-ZRS53OAT.js} +2 -2
- package/dist/{coordinator-prompt-3BSMKZL4.js → coordinator-prompt-HD6IMCKS.js} +3 -3
- package/dist/{coordinator-prompt-6HMTOX4G.js → coordinator-prompt-YZYZ5P6P.js} +3 -3
- package/dist/{global-workspace-QS7A23CB.js → global-workspace-7P5NG3JZ.js} +4 -4
- package/dist/{global-workspace-YZ2NJVLL.js → global-workspace-Z6GF7D2K.js} +4 -4
- package/dist/index.cjs +367 -158
- package/dist/index.d.cts +27 -1
- package/dist/index.d.ts +27 -1
- package/dist/index.js +364 -157
- package/dist/mcp/run-stdio.cjs +367 -158
- package/dist/mcp/run-stdio.js +363 -156
- package/dist/{run-Y2MI3EVV.js → run-A4RHY7HH.js} +1 -1
- package/dist/{run-JLD2Y52J.js → run-V2WZUMJA.js} +1 -1
- package/dist/{workspaces-EBFW7I7S.js → workspaces-BWJNGEE6.js} +5 -5
- package/dist/{workspaces-AIUYUXB7.js → workspaces-O6EZGKQK.js} +5 -5
- package/dist/{worktree-WMDJW2BL.js → worktree-VDXLUOWR.js} +2 -2
- package/dist/{worktree-7NBPIVFZ.js → worktree-ZPSKEZFE.js} +2 -2
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -2295,7 +2295,7 @@ async function git(args, cwd, opts) {
|
|
|
2295
2295
|
prefix.push("-c", `${key}=${value}`);
|
|
2296
2296
|
}
|
|
2297
2297
|
}
|
|
2298
|
-
return run("git", [...prefix, ...args], {
|
|
2298
|
+
return run("git", ["--no-pager", ...prefix, ...args], {
|
|
2299
2299
|
cwd,
|
|
2300
2300
|
reject: opts?.reject,
|
|
2301
2301
|
timeoutMs: opts?.timeoutMs,
|
|
@@ -2798,7 +2798,7 @@ async function originGhRepoEnv(cwd) {
|
|
|
2798
2798
|
const slug = await resolveGithubRepoSlug(cwd);
|
|
2799
2799
|
return slug ? { GH_REPO: slug } : {};
|
|
2800
2800
|
}
|
|
2801
|
-
async function resolveDefaultBranch(repoPath) {
|
|
2801
|
+
async function resolveDefaultBranch(repoPath, opts) {
|
|
2802
2802
|
const viaOrigin = await git(
|
|
2803
2803
|
["symbolic-ref", "refs/remotes/origin/HEAD"],
|
|
2804
2804
|
repoPath,
|
|
@@ -2807,22 +2807,24 @@ async function resolveDefaultBranch(repoPath) {
|
|
|
2807
2807
|
if (viaOrigin.exitCode === 0 && viaOrigin.stdout.trim()) {
|
|
2808
2808
|
return viaOrigin.stdout.trim().replace(/^refs\/remotes\/origin\//, "");
|
|
2809
2809
|
}
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2810
|
+
if (opts?.network !== false) {
|
|
2811
|
+
const slug = await resolveGithubRepoSlug(repoPath);
|
|
2812
|
+
const viaGh = await gh(
|
|
2813
|
+
[
|
|
2814
|
+
"repo",
|
|
2815
|
+
"view",
|
|
2816
|
+
...slug ? ["--repo", slug] : [],
|
|
2817
|
+
"--json",
|
|
2818
|
+
"defaultBranchRef",
|
|
2819
|
+
"--jq",
|
|
2820
|
+
".defaultBranchRef.name"
|
|
2821
|
+
],
|
|
2822
|
+
repoPath,
|
|
2823
|
+
{ reject: false, timeoutMs: 8e3 }
|
|
2824
|
+
);
|
|
2825
|
+
if (viaGh.exitCode === 0 && viaGh.stdout.trim()) {
|
|
2826
|
+
return viaGh.stdout.trim();
|
|
2827
|
+
}
|
|
2826
2828
|
}
|
|
2827
2829
|
for (const candidate of ["main", "master"]) {
|
|
2828
2830
|
const check = await git(
|
|
@@ -3474,11 +3476,11 @@ async function listWorktrees(repoPath) {
|
|
|
3474
3476
|
return entries;
|
|
3475
3477
|
}
|
|
3476
3478
|
async function isDirty(worktreePath) {
|
|
3477
|
-
const { stdout } = await git(["status", "--porcelain"
|
|
3479
|
+
const { stdout } = await git(["status", "--porcelain"], worktreePath);
|
|
3478
3480
|
for (const line of stdout.split("\n")) {
|
|
3479
3481
|
if (!line.trim()) continue;
|
|
3480
3482
|
const rel = porcelainStatusPath(line);
|
|
3481
|
-
if (!rel || isSideboardScratchPath(rel)) continue;
|
|
3483
|
+
if (!rel || isSideboardScratchPath(rel) || rel === ".sideboard") continue;
|
|
3482
3484
|
return true;
|
|
3483
3485
|
}
|
|
3484
3486
|
return false;
|
|
@@ -9181,24 +9183,25 @@ function capPatch(patch, maxHunk) {
|
|
|
9181
9183
|
|
|
9182
9184
|
\u2026 truncated (${patch.length - maxHunk} more chars)`;
|
|
9183
9185
|
}
|
|
9186
|
+
var mergeBaseCache = /* @__PURE__ */ new Map();
|
|
9187
|
+
var MERGE_BASE_TTL_MS = 45e3;
|
|
9184
9188
|
async function resolveMergeBase(worktreePath, base, repoPath) {
|
|
9185
9189
|
const baseRef = await resolveDiffBaseRef(worktreePath, base, repoPath);
|
|
9190
|
+
const cacheKey = `${worktreePath}\0${baseRef}`;
|
|
9191
|
+
const hit = mergeBaseCache.get(cacheKey);
|
|
9192
|
+
if (hit && Date.now() - hit.at < MERGE_BASE_TTL_MS) return hit.value;
|
|
9186
9193
|
const { stdout } = await git(["merge-base", baseRef, "HEAD"], worktreePath, {
|
|
9187
9194
|
reject: false
|
|
9188
9195
|
});
|
|
9189
|
-
const mb = stdout.trim();
|
|
9190
|
-
|
|
9196
|
+
const mb = stdout.trim() || baseRef;
|
|
9197
|
+
mergeBaseCache.set(cacheKey, { at: Date.now(), value: mb });
|
|
9198
|
+
return mb;
|
|
9191
9199
|
}
|
|
9192
9200
|
async function countUnpushedCommits(worktreePath) {
|
|
9193
9201
|
const head = await git(["rev-parse", "--abbrev-ref", "HEAD"], worktreePath, {
|
|
9194
9202
|
reject: false
|
|
9195
9203
|
});
|
|
9196
9204
|
const branch = head.stdout.trim();
|
|
9197
|
-
if (branch && branch !== "HEAD") {
|
|
9198
|
-
await git(["fetch", "origin", branch, "--quiet"], worktreePath, {
|
|
9199
|
-
reject: false
|
|
9200
|
-
});
|
|
9201
|
-
}
|
|
9202
9205
|
const upstream = await git(
|
|
9203
9206
|
["rev-list", "--count", "@{upstream}..HEAD"],
|
|
9204
9207
|
worktreePath,
|
|
@@ -9231,11 +9234,37 @@ function splitCombinedDiff(stdout) {
|
|
|
9231
9234
|
}
|
|
9232
9235
|
return map;
|
|
9233
9236
|
}
|
|
9237
|
+
function syntheticAddPatch(path, content, maxHunk) {
|
|
9238
|
+
const lines = content.split("\n");
|
|
9239
|
+
const body = lines.map((l) => `+${l}`).join("\n");
|
|
9240
|
+
const header = `diff --git a/${path} b/${path}
|
|
9241
|
+
new file mode 100644
|
|
9242
|
+
--- /dev/null
|
|
9243
|
+
+++ b/${path}
|
|
9244
|
+
@@ -0,0 +1,${lines.length} @@
|
|
9245
|
+
`;
|
|
9246
|
+
return {
|
|
9247
|
+
path,
|
|
9248
|
+
status: "A",
|
|
9249
|
+
patch: capPatch(`${header}${body}`, maxHunk),
|
|
9250
|
+
additions: lines.length,
|
|
9251
|
+
deletions: 0
|
|
9252
|
+
};
|
|
9253
|
+
}
|
|
9234
9254
|
async function untrackedPatch(worktreePath, path, maxHunk) {
|
|
9255
|
+
const abs = (0, import_node_path17.join)(worktreePath, path);
|
|
9256
|
+
try {
|
|
9257
|
+
const st = (0, import_node_fs20.statSync)(abs);
|
|
9258
|
+
if (st.isFile() && st.size > maxHunk) {
|
|
9259
|
+
const buf = (0, import_node_fs20.readFileSync)(abs).subarray(0, maxHunk);
|
|
9260
|
+
return syntheticAddPatch(path, buf.toString("utf8"), maxHunk);
|
|
9261
|
+
}
|
|
9262
|
+
} catch {
|
|
9263
|
+
}
|
|
9235
9264
|
const { stdout: patch } = await git(
|
|
9236
|
-
["diff", "--no-index", "--", "/dev/null", path],
|
|
9265
|
+
["diff", "--no-ext-diff", "--no-color", "--no-index", "--", "/dev/null", path],
|
|
9237
9266
|
worktreePath,
|
|
9238
|
-
{ reject: false }
|
|
9267
|
+
{ reject: false, timeoutMs: 4e3 }
|
|
9239
9268
|
);
|
|
9240
9269
|
const normalized = patch.replace(/^diff --git a\/.+? b\/.+?$/m, `diff --git a/${path} b/${path}`).replace(/^--- .*$/m, "--- /dev/null").replace(/^\+\+\+ .*$/m, `+++ b/${path}`);
|
|
9241
9270
|
const addLines = normalized.split("\n").filter((l) => l.startsWith("+") && !l.startsWith("+++")).length;
|
|
@@ -9247,22 +9276,97 @@ async function untrackedPatch(worktreePath, path, maxHunk) {
|
|
|
9247
9276
|
deletions: 0
|
|
9248
9277
|
};
|
|
9249
9278
|
}
|
|
9250
|
-
async function listUntrackedPaths(worktreePath) {
|
|
9251
|
-
const
|
|
9252
|
-
|
|
9253
|
-
|
|
9254
|
-
{ reject: false }
|
|
9255
|
-
);
|
|
9279
|
+
async function listUntrackedPaths(worktreePath, path) {
|
|
9280
|
+
const args = ["ls-files", "-z", "--others", "--exclude-standard"];
|
|
9281
|
+
if (path) args.push("--", path);
|
|
9282
|
+
const { stdout } = await git(args, worktreePath, { reject: false });
|
|
9256
9283
|
return stdout.split("\0").filter(Boolean).filter((p) => !isSideboardScratchPath(p));
|
|
9257
9284
|
}
|
|
9258
|
-
async function listUntrackedFiles(worktreePath, maxHunk) {
|
|
9259
|
-
const
|
|
9260
|
-
if (
|
|
9261
|
-
return Promise.all(
|
|
9285
|
+
async function listUntrackedFiles(worktreePath, maxHunk, paths) {
|
|
9286
|
+
const list = paths ?? await listUntrackedPaths(worktreePath);
|
|
9287
|
+
if (list.length === 0) return [];
|
|
9288
|
+
return Promise.all(list.map((path) => untrackedPatch(worktreePath, path, maxHunk)));
|
|
9289
|
+
}
|
|
9290
|
+
function untrackedStubs(paths) {
|
|
9291
|
+
return paths.map((path) => ({
|
|
9292
|
+
path,
|
|
9293
|
+
status: "A",
|
|
9294
|
+
patch: "",
|
|
9295
|
+
additions: 0,
|
|
9296
|
+
deletions: 0
|
|
9297
|
+
}));
|
|
9298
|
+
}
|
|
9299
|
+
function pathspec(path) {
|
|
9300
|
+
return path ? ["--", path] : [];
|
|
9301
|
+
}
|
|
9302
|
+
function gitDiff(args, cwd, opts) {
|
|
9303
|
+
return git(["diff", "--no-ext-diff", "--no-color", ...args], cwd, {
|
|
9304
|
+
reject: false,
|
|
9305
|
+
timeoutMs: opts?.timeoutMs
|
|
9306
|
+
});
|
|
9307
|
+
}
|
|
9308
|
+
function fileFromPatch(path, patch, maxHunk) {
|
|
9309
|
+
let status = "M";
|
|
9310
|
+
if (/^new file mode /m.test(patch) || /^--- \/dev\/null/m.test(patch)) {
|
|
9311
|
+
status = "A";
|
|
9312
|
+
} else if (/^deleted file mode /m.test(patch) || /^\+\+\+ \/dev\/null/m.test(patch)) {
|
|
9313
|
+
status = "D";
|
|
9314
|
+
} else if (/^rename from /m.test(patch)) {
|
|
9315
|
+
status = "R";
|
|
9316
|
+
}
|
|
9317
|
+
const additions = patch.split("\n").filter((l) => l.startsWith("+") && !l.startsWith("+++")).length;
|
|
9318
|
+
const deletions = patch.split("\n").filter((l) => l.startsWith("-") && !l.startsWith("---")).length;
|
|
9319
|
+
return {
|
|
9320
|
+
path,
|
|
9321
|
+
status,
|
|
9322
|
+
patch: capPatch(patch, maxHunk),
|
|
9323
|
+
additions,
|
|
9324
|
+
deletions
|
|
9325
|
+
};
|
|
9326
|
+
}
|
|
9327
|
+
async function gitDiffParts(worktreePath, diffArgs, includePatches, path) {
|
|
9328
|
+
const extra = pathspec(path);
|
|
9329
|
+
if (includePatches) {
|
|
9330
|
+
const diff = await gitDiff([...diffArgs, ...extra], worktreePath, {
|
|
9331
|
+
timeoutMs: 4e3
|
|
9332
|
+
});
|
|
9333
|
+
return { nameStatus: "", numstat: "", combinedDiff: diff.stdout };
|
|
9334
|
+
}
|
|
9335
|
+
const [ns, num2] = await Promise.all([
|
|
9336
|
+
gitDiff(["--name-status", ...diffArgs, ...extra], worktreePath),
|
|
9337
|
+
gitDiff(["--numstat", ...diffArgs, ...extra], worktreePath)
|
|
9338
|
+
]);
|
|
9339
|
+
return {
|
|
9340
|
+
nameStatus: ns.stdout,
|
|
9341
|
+
numstat: num2.stdout,
|
|
9342
|
+
combinedDiff: ""
|
|
9343
|
+
};
|
|
9344
|
+
}
|
|
9345
|
+
function formatStat(files) {
|
|
9346
|
+
if (files.length === 0) return "(no changes)";
|
|
9347
|
+
const additions = files.reduce((n2, f) => n2 + (f.additions ?? 0), 0);
|
|
9348
|
+
const deletions = files.reduce((n2, f) => n2 + (f.deletions ?? 0), 0);
|
|
9349
|
+
const n = files.length;
|
|
9350
|
+
return `${n} file${n === 1 ? "" : "s"} changed, ${additions} insertions(+), ${deletions} deletions(-)`;
|
|
9351
|
+
}
|
|
9352
|
+
function emptyScopeStats() {
|
|
9353
|
+
const z2 = emptyStat();
|
|
9354
|
+
return {
|
|
9355
|
+
commits: z2,
|
|
9356
|
+
uncommitted: z2,
|
|
9357
|
+
staged: z2,
|
|
9358
|
+
unstaged: z2,
|
|
9359
|
+
last_turn: z2
|
|
9360
|
+
};
|
|
9262
9361
|
}
|
|
9263
9362
|
function filesFromDiff(nameStatus, numstat, combinedDiff, maxHunk) {
|
|
9264
|
-
const counts = parseNumstat(numstat);
|
|
9265
9363
|
const patches = splitCombinedDiff(combinedDiff);
|
|
9364
|
+
if (!nameStatus.trim() && patches.size > 0) {
|
|
9365
|
+
return [...patches.entries()].map(
|
|
9366
|
+
([path, patch]) => fileFromPatch(path, patch, maxHunk)
|
|
9367
|
+
);
|
|
9368
|
+
}
|
|
9369
|
+
const counts = parseNumstat(numstat);
|
|
9266
9370
|
const files = [];
|
|
9267
9371
|
for (const { status, path } of parseNameStatus(nameStatus)) {
|
|
9268
9372
|
const n = counts.get(path);
|
|
@@ -9284,11 +9388,11 @@ async function computeScopeStats(worktreePath, mergeBase, lastTurnBase, untracke
|
|
|
9284
9388
|
{ stdout: unstagedNum },
|
|
9285
9389
|
lastTurnNum
|
|
9286
9390
|
] = await Promise.all([
|
|
9287
|
-
mergeBase ?
|
|
9288
|
-
|
|
9289
|
-
|
|
9290
|
-
|
|
9291
|
-
lastTurnBase ?
|
|
9391
|
+
mergeBase ? gitDiff(["--numstat", mergeBase], worktreePath) : Promise.resolve({ stdout: "" }),
|
|
9392
|
+
gitDiff(["--numstat", "HEAD"], worktreePath),
|
|
9393
|
+
gitDiff(["--numstat", "--cached"], worktreePath),
|
|
9394
|
+
gitDiff(["--numstat"], worktreePath),
|
|
9395
|
+
lastTurnBase ? gitDiff(["--numstat", lastTurnBase], worktreePath) : Promise.resolve({ stdout: "" })
|
|
9292
9396
|
]);
|
|
9293
9397
|
return {
|
|
9294
9398
|
commits: mergeBase ? statFromNumstat(commitsNum, untrackedCount) : emptyStat(),
|
|
@@ -9307,7 +9411,7 @@ async function listBranchCommits(worktreePath, repoPath, opts) {
|
|
|
9307
9411
|
if (head.exitCode !== 0) return [];
|
|
9308
9412
|
let base = opts?.base;
|
|
9309
9413
|
try {
|
|
9310
|
-
base = base ?? await resolveDefaultBranch(repoPath);
|
|
9414
|
+
base = base ?? await resolveDefaultBranch(repoPath, { network: false });
|
|
9311
9415
|
} catch {
|
|
9312
9416
|
base = "HEAD";
|
|
9313
9417
|
}
|
|
@@ -9337,7 +9441,121 @@ async function listBranchCommits(worktreePath, repoPath, opts) {
|
|
|
9337
9441
|
}
|
|
9338
9442
|
return commits;
|
|
9339
9443
|
}
|
|
9444
|
+
var SHA_RE = /^[0-9a-f]{7,40}$/i;
|
|
9445
|
+
function emptyPathDiff(scope, base, hasLastTurnBase, commitSha = null) {
|
|
9446
|
+
return {
|
|
9447
|
+
scope,
|
|
9448
|
+
commitSha: scope === "commits" ? commitSha : null,
|
|
9449
|
+
base,
|
|
9450
|
+
files: [],
|
|
9451
|
+
stat: formatStat([]),
|
|
9452
|
+
dirty: false,
|
|
9453
|
+
unpushed: 0,
|
|
9454
|
+
hasLastTurnBase,
|
|
9455
|
+
commits: [],
|
|
9456
|
+
scopeStats: emptyScopeStats()
|
|
9457
|
+
};
|
|
9458
|
+
}
|
|
9459
|
+
async function getPathDiff(worktreePath, repoPath, opts) {
|
|
9460
|
+
const { path, scope, commitSha, lastTurnBase, hasLastTurnBase, includePatches, maxHunk } = opts;
|
|
9461
|
+
if (scope === "last_turn" && !lastTurnBase) {
|
|
9462
|
+
return emptyPathDiff(scope, "last turn", false);
|
|
9463
|
+
}
|
|
9464
|
+
let diffArgs;
|
|
9465
|
+
let labelBase;
|
|
9466
|
+
if (scope === "staged") {
|
|
9467
|
+
diffArgs = ["--cached"];
|
|
9468
|
+
labelBase = "staged";
|
|
9469
|
+
} else if (scope === "unstaged") {
|
|
9470
|
+
diffArgs = [];
|
|
9471
|
+
labelBase = "unstaged";
|
|
9472
|
+
} else if (scope === "uncommitted") {
|
|
9473
|
+
diffArgs = ["HEAD"];
|
|
9474
|
+
labelBase = "HEAD";
|
|
9475
|
+
} else if (scope === "last_turn") {
|
|
9476
|
+
diffArgs = [lastTurnBase];
|
|
9477
|
+
labelBase = "last turn";
|
|
9478
|
+
} else if (scope === "commits" && commitSha) {
|
|
9479
|
+
diffArgs = [`${commitSha}^!`];
|
|
9480
|
+
labelBase = commitSha.slice(0, 7);
|
|
9481
|
+
} else if (scope === "commits" && opts.base && SHA_RE.test(opts.base)) {
|
|
9482
|
+
diffArgs = [opts.base];
|
|
9483
|
+
labelBase = opts.base.slice(0, 7);
|
|
9484
|
+
} else if (scope === "commits") {
|
|
9485
|
+
let base = opts.base;
|
|
9486
|
+
try {
|
|
9487
|
+
base = base ?? await resolveDefaultBranch(repoPath, { network: false });
|
|
9488
|
+
} catch {
|
|
9489
|
+
base = "HEAD";
|
|
9490
|
+
}
|
|
9491
|
+
const mergeBase = await resolveMergeBase(worktreePath, base, repoPath);
|
|
9492
|
+
diffArgs = [mergeBase];
|
|
9493
|
+
labelBase = mergeBase;
|
|
9494
|
+
} else {
|
|
9495
|
+
diffArgs = ["HEAD"];
|
|
9496
|
+
labelBase = "HEAD";
|
|
9497
|
+
}
|
|
9498
|
+
const diff = await gitDiff(
|
|
9499
|
+
includePatches ? [...diffArgs, "--", path] : ["--name-status", ...diffArgs, "--", path],
|
|
9500
|
+
worktreePath,
|
|
9501
|
+
{ timeoutMs: 4e3 }
|
|
9502
|
+
);
|
|
9503
|
+
const filesMap = /* @__PURE__ */ new Map();
|
|
9504
|
+
if (includePatches) {
|
|
9505
|
+
const parsed = filesFromDiff("", "", diff.stdout, maxHunk);
|
|
9506
|
+
for (const file of parsed) filesMap.set(file.path, file);
|
|
9507
|
+
if (diff.stdout.trim() && !filesMap.has(path)) {
|
|
9508
|
+
filesMap.set(path, fileFromPatch(path, diff.stdout, maxHunk));
|
|
9509
|
+
}
|
|
9510
|
+
} else {
|
|
9511
|
+
for (const file of filesFromDiff(diff.stdout, "", "", maxHunk)) {
|
|
9512
|
+
filesMap.set(file.path, file);
|
|
9513
|
+
}
|
|
9514
|
+
}
|
|
9515
|
+
if (filesMap.size === 0) {
|
|
9516
|
+
const maybe = await listUntrackedPaths(worktreePath, path);
|
|
9517
|
+
if (maybe.length > 0) {
|
|
9518
|
+
const extra = includePatches ? await listUntrackedFiles(worktreePath, maxHunk, maybe) : untrackedStubs(maybe);
|
|
9519
|
+
for (const file of extra) filesMap.set(file.path, file);
|
|
9520
|
+
}
|
|
9521
|
+
}
|
|
9522
|
+
const files = [...filesMap.values()].sort((a, b) => a.path.localeCompare(b.path));
|
|
9523
|
+
return {
|
|
9524
|
+
scope,
|
|
9525
|
+
commitSha: scope === "commits" ? commitSha : null,
|
|
9526
|
+
base: labelBase,
|
|
9527
|
+
files,
|
|
9528
|
+
stat: formatStat(files),
|
|
9529
|
+
dirty: files.length > 0,
|
|
9530
|
+
unpushed: 0,
|
|
9531
|
+
hasLastTurnBase,
|
|
9532
|
+
commits: [],
|
|
9533
|
+
scopeStats: emptyScopeStats(),
|
|
9534
|
+
mergeBase: SHA_RE.test(diffArgs[0] ?? "") ? diffArgs[0] : null
|
|
9535
|
+
};
|
|
9536
|
+
}
|
|
9340
9537
|
async function getDiff(worktreePath, repoPath, opts) {
|
|
9538
|
+
const maxHunk = opts?.maxHunkChars ?? 8e3;
|
|
9539
|
+
const scope = opts?.scope ?? "commits";
|
|
9540
|
+
const lastTurnBase = opts?.lastTurnBase?.trim() || null;
|
|
9541
|
+
const hasLastTurnBase = Boolean(lastTurnBase);
|
|
9542
|
+
const commitSha = opts?.commitSha?.trim() || null;
|
|
9543
|
+
const includePatches = opts?.includePatches ?? true;
|
|
9544
|
+
const pathFilter = opts?.path?.trim() || null;
|
|
9545
|
+
const fileOnly = Boolean(pathFilter);
|
|
9546
|
+
const includeMeta = (opts?.includeMeta ?? true) && !fileOnly;
|
|
9547
|
+
if (fileOnly && pathFilter && !includeMeta) {
|
|
9548
|
+
return getPathDiff(worktreePath, repoPath, {
|
|
9549
|
+
path: pathFilter,
|
|
9550
|
+
scope,
|
|
9551
|
+
commitSha,
|
|
9552
|
+
lastTurnBase,
|
|
9553
|
+
hasLastTurnBase,
|
|
9554
|
+
base: opts?.base,
|
|
9555
|
+
includePatches,
|
|
9556
|
+
maxHunk
|
|
9557
|
+
});
|
|
9558
|
+
}
|
|
9341
9559
|
const status = await inspectGitWorktree(worktreePath);
|
|
9342
9560
|
if (status === "missing_worktree") {
|
|
9343
9561
|
throw new Error("Worktree not found");
|
|
@@ -9345,18 +9563,19 @@ async function getDiff(worktreePath, repoPath, opts) {
|
|
|
9345
9563
|
if (status === "not_git") {
|
|
9346
9564
|
throw new Error("Not a Git repository");
|
|
9347
9565
|
}
|
|
9348
|
-
const
|
|
9349
|
-
const
|
|
9350
|
-
const
|
|
9351
|
-
const hasLastTurnBase = Boolean(lastTurnBase);
|
|
9352
|
-
const commitSha = opts?.commitSha?.trim() || null;
|
|
9353
|
-
const head = await git(["rev-parse", "--verify", "HEAD"], worktreePath, {
|
|
9566
|
+
const wantUntracked = opts?.includeUntracked !== void 0 ? opts.includeUntracked : !fileOnly && (scope === "commits" && !commitSha || scope === "uncommitted" || scope === "unstaged" || scope === "last_turn");
|
|
9567
|
+
const needsMergeBase = scope === "commits" && !commitSha || includeMeta;
|
|
9568
|
+
const headP = git(["rev-parse", "--verify", "HEAD"], worktreePath, {
|
|
9354
9569
|
reject: false
|
|
9355
9570
|
});
|
|
9571
|
+
const untrackedP = wantUntracked ? listUntrackedPaths(worktreePath, pathFilter) : Promise.resolve([]);
|
|
9572
|
+
const head = await headP;
|
|
9356
9573
|
const hasHead = head.exitCode === 0;
|
|
9357
|
-
const untrackedPaths = await listUntrackedPaths(worktreePath);
|
|
9358
9574
|
if (!hasHead) {
|
|
9359
|
-
const
|
|
9575
|
+
const untrackedPaths2 = await untrackedP;
|
|
9576
|
+
const files2 = scope === "staged" ? [] : includePatches ? (await listUntrackedFiles(worktreePath, maxHunk, untrackedPaths2)).sort(
|
|
9577
|
+
(a, b) => a.path.localeCompare(b.path)
|
|
9578
|
+
) : untrackedStubs(untrackedPaths2).sort(
|
|
9360
9579
|
(a, b) => a.path.localeCompare(b.path)
|
|
9361
9580
|
);
|
|
9362
9581
|
const untrackedStat = {
|
|
@@ -9370,7 +9589,7 @@ async function getDiff(worktreePath, repoPath, opts) {
|
|
|
9370
9589
|
commitSha: null,
|
|
9371
9590
|
base: "(no commits)",
|
|
9372
9591
|
files: files2,
|
|
9373
|
-
stat: files2
|
|
9592
|
+
stat: formatStat(files2),
|
|
9374
9593
|
dirty: files2.length > 0,
|
|
9375
9594
|
unpushed: 0,
|
|
9376
9595
|
hasLastTurnBase,
|
|
@@ -9384,137 +9603,122 @@ async function getDiff(worktreePath, repoPath, opts) {
|
|
|
9384
9603
|
}
|
|
9385
9604
|
};
|
|
9386
9605
|
}
|
|
9387
|
-
let
|
|
9388
|
-
|
|
9389
|
-
|
|
9390
|
-
|
|
9391
|
-
|
|
9606
|
+
let mergeBase = "HEAD";
|
|
9607
|
+
let baseLabel = "HEAD";
|
|
9608
|
+
if (needsMergeBase) {
|
|
9609
|
+
let base = opts?.base;
|
|
9610
|
+
try {
|
|
9611
|
+
base = base ?? await resolveDefaultBranch(repoPath, { network: false });
|
|
9612
|
+
} catch {
|
|
9613
|
+
base = "HEAD";
|
|
9614
|
+
}
|
|
9615
|
+
mergeBase = await resolveMergeBase(worktreePath, base, repoPath);
|
|
9616
|
+
baseLabel = await resolveDiffBaseRef(worktreePath, base, repoPath);
|
|
9392
9617
|
}
|
|
9393
|
-
|
|
9394
|
-
|
|
9395
|
-
|
|
9396
|
-
|
|
9397
|
-
|
|
9398
|
-
|
|
9399
|
-
|
|
9618
|
+
const includeUntracked = wantUntracked;
|
|
9619
|
+
const metaPromise = includeMeta ? Promise.all([
|
|
9620
|
+
computeScopeStats(
|
|
9621
|
+
worktreePath,
|
|
9622
|
+
mergeBase,
|
|
9623
|
+
lastTurnBase,
|
|
9624
|
+
// Cheap: don't wait on the untracked walk just to pad the badge.
|
|
9625
|
+
0
|
|
9626
|
+
),
|
|
9627
|
+
listBranchCommits(worktreePath, repoPath, { base: baseLabel }),
|
|
9628
|
+
isDirty(worktreePath),
|
|
9629
|
+
countUnpushedCommits(worktreePath)
|
|
9630
|
+
]).then(([scopeStats, commits, dirty, unpushed]) => ({
|
|
9631
|
+
scopeStats,
|
|
9632
|
+
commits,
|
|
9633
|
+
dirty,
|
|
9634
|
+
unpushed
|
|
9635
|
+
})) : Promise.resolve({
|
|
9636
|
+
scopeStats: emptyScopeStats(),
|
|
9637
|
+
commits: [],
|
|
9638
|
+
dirty: false,
|
|
9639
|
+
unpushed: 0
|
|
9640
|
+
});
|
|
9641
|
+
if (scope === "last_turn" && !lastTurnBase) {
|
|
9642
|
+
const meta2 = await metaPromise;
|
|
9643
|
+
return {
|
|
9644
|
+
scope,
|
|
9645
|
+
commitSha: null,
|
|
9646
|
+
base: "last turn",
|
|
9647
|
+
files: [],
|
|
9648
|
+
stat: "(no last agent turn)",
|
|
9649
|
+
dirty: meta2.dirty,
|
|
9650
|
+
unpushed: meta2.unpushed,
|
|
9651
|
+
hasLastTurnBase: false,
|
|
9652
|
+
commits: meta2.commits,
|
|
9653
|
+
scopeStats: meta2.scopeStats
|
|
9654
|
+
};
|
|
9400
9655
|
}
|
|
9401
|
-
|
|
9402
|
-
|
|
9403
|
-
worktreePath,
|
|
9404
|
-
mergeBase,
|
|
9405
|
-
lastTurnBase,
|
|
9406
|
-
untrackedPaths.length
|
|
9407
|
-
);
|
|
9408
|
-
const includeUntracked = scope === "commits" && !commitSha || scope === "uncommitted" || scope === "unstaged" || scope === "last_turn";
|
|
9409
|
-
let nameStatus = "";
|
|
9410
|
-
let numstat = "";
|
|
9411
|
-
let combinedDiff = "";
|
|
9412
|
-
let labelBase = base;
|
|
9656
|
+
let diffArgs;
|
|
9657
|
+
let labelBase = baseLabel;
|
|
9413
9658
|
if (scope === "staged") {
|
|
9414
|
-
|
|
9415
|
-
git(["diff", "--name-status", "--cached"], worktreePath, { reject: false }),
|
|
9416
|
-
git(["diff", "--numstat", "--cached"], worktreePath, { reject: false }),
|
|
9417
|
-
git(["diff", "--cached"], worktreePath, { reject: false })
|
|
9418
|
-
]);
|
|
9419
|
-
nameStatus = ns.stdout;
|
|
9420
|
-
numstat = num2.stdout;
|
|
9421
|
-
combinedDiff = diff.stdout;
|
|
9659
|
+
diffArgs = ["--cached"];
|
|
9422
9660
|
labelBase = "staged";
|
|
9423
9661
|
} else if (scope === "unstaged") {
|
|
9424
|
-
|
|
9425
|
-
git(["diff", "--name-status"], worktreePath, { reject: false }),
|
|
9426
|
-
git(["diff", "--numstat"], worktreePath, { reject: false }),
|
|
9427
|
-
git(["diff"], worktreePath, { reject: false })
|
|
9428
|
-
]);
|
|
9429
|
-
nameStatus = ns.stdout;
|
|
9430
|
-
numstat = num2.stdout;
|
|
9431
|
-
combinedDiff = diff.stdout;
|
|
9662
|
+
diffArgs = [];
|
|
9432
9663
|
labelBase = "unstaged";
|
|
9433
9664
|
} else if (scope === "uncommitted") {
|
|
9434
|
-
|
|
9435
|
-
git(["diff", "--name-status", "HEAD"], worktreePath, { reject: false }),
|
|
9436
|
-
git(["diff", "--numstat", "HEAD"], worktreePath, { reject: false }),
|
|
9437
|
-
git(["diff", "HEAD"], worktreePath, { reject: false })
|
|
9438
|
-
]);
|
|
9439
|
-
nameStatus = ns.stdout;
|
|
9440
|
-
numstat = num2.stdout;
|
|
9441
|
-
combinedDiff = diff.stdout;
|
|
9665
|
+
diffArgs = ["HEAD"];
|
|
9442
9666
|
labelBase = "HEAD";
|
|
9443
9667
|
} else if (scope === "last_turn") {
|
|
9444
|
-
|
|
9445
|
-
const commits2 = await listBranchCommits(worktreePath, repoPath, { base });
|
|
9446
|
-
return {
|
|
9447
|
-
scope,
|
|
9448
|
-
commitSha: null,
|
|
9449
|
-
base: "last turn",
|
|
9450
|
-
files: [],
|
|
9451
|
-
stat: "(no last agent turn)",
|
|
9452
|
-
dirty: await isDirty(worktreePath),
|
|
9453
|
-
unpushed: await countUnpushedCommits(worktreePath),
|
|
9454
|
-
hasLastTurnBase: false,
|
|
9455
|
-
commits: commits2,
|
|
9456
|
-
scopeStats
|
|
9457
|
-
};
|
|
9458
|
-
}
|
|
9459
|
-
const [ns, num2, diff] = await Promise.all([
|
|
9460
|
-
git(["diff", "--name-status", lastTurnBase], worktreePath, { reject: false }),
|
|
9461
|
-
git(["diff", "--numstat", lastTurnBase], worktreePath, { reject: false }),
|
|
9462
|
-
git(["diff", lastTurnBase], worktreePath, { reject: false })
|
|
9463
|
-
]);
|
|
9464
|
-
nameStatus = ns.stdout;
|
|
9465
|
-
numstat = num2.stdout;
|
|
9466
|
-
combinedDiff = diff.stdout;
|
|
9668
|
+
diffArgs = [lastTurnBase];
|
|
9467
9669
|
labelBase = "last turn";
|
|
9468
9670
|
} else if (scope === "commits" && commitSha) {
|
|
9469
|
-
|
|
9470
|
-
const [ns, num2, diff] = await Promise.all([
|
|
9471
|
-
git(["diff", "--name-status", range], worktreePath, { reject: false }),
|
|
9472
|
-
git(["diff", "--numstat", range], worktreePath, { reject: false }),
|
|
9473
|
-
git(["diff", range], worktreePath, { reject: false })
|
|
9474
|
-
]);
|
|
9475
|
-
nameStatus = ns.stdout;
|
|
9476
|
-
numstat = num2.stdout;
|
|
9477
|
-
combinedDiff = diff.stdout;
|
|
9671
|
+
diffArgs = [`${commitSha}^!`];
|
|
9478
9672
|
labelBase = commitSha.slice(0, 7);
|
|
9479
9673
|
} else {
|
|
9480
|
-
|
|
9481
|
-
|
|
9482
|
-
git(["diff", "--numstat", mergeBase], worktreePath, { reject: false }),
|
|
9483
|
-
git(["diff", mergeBase], worktreePath, { reject: false })
|
|
9484
|
-
]);
|
|
9485
|
-
nameStatus = ns.stdout;
|
|
9486
|
-
numstat = num2.stdout;
|
|
9487
|
-
combinedDiff = diff.stdout;
|
|
9488
|
-
labelBase = base;
|
|
9674
|
+
diffArgs = [mergeBase];
|
|
9675
|
+
labelBase = baseLabel;
|
|
9489
9676
|
}
|
|
9490
|
-
const
|
|
9677
|
+
const partsPromise = gitDiffParts(
|
|
9678
|
+
worktreePath,
|
|
9679
|
+
diffArgs,
|
|
9680
|
+
includePatches,
|
|
9681
|
+
pathFilter
|
|
9682
|
+
);
|
|
9683
|
+
const [parts, meta, untrackedPaths] = await Promise.all([
|
|
9684
|
+
partsPromise,
|
|
9685
|
+
metaPromise,
|
|
9686
|
+
untrackedP
|
|
9687
|
+
]);
|
|
9491
9688
|
const filesMap = /* @__PURE__ */ new Map();
|
|
9492
|
-
for (const file of filesFromDiff(
|
|
9689
|
+
for (const file of filesFromDiff(
|
|
9690
|
+
parts.nameStatus,
|
|
9691
|
+
parts.numstat,
|
|
9692
|
+
parts.combinedDiff,
|
|
9693
|
+
maxHunk
|
|
9694
|
+
)) {
|
|
9493
9695
|
filesMap.set(file.path, file);
|
|
9494
9696
|
}
|
|
9495
9697
|
if (includeUntracked) {
|
|
9496
|
-
const
|
|
9497
|
-
for (const file of
|
|
9698
|
+
const extra = includePatches ? await listUntrackedFiles(worktreePath, maxHunk, untrackedPaths) : untrackedStubs(untrackedPaths);
|
|
9699
|
+
for (const file of extra) {
|
|
9498
9700
|
if (!filesMap.has(file.path)) filesMap.set(file.path, file);
|
|
9499
9701
|
}
|
|
9702
|
+
} else if (fileOnly && pathFilter && !filesMap.has(pathFilter)) {
|
|
9703
|
+
const maybe = await listUntrackedPaths(worktreePath, pathFilter);
|
|
9704
|
+
if (maybe.length > 0) {
|
|
9705
|
+
const extra = includePatches ? await listUntrackedFiles(worktreePath, maxHunk, maybe) : untrackedStubs(maybe);
|
|
9706
|
+
for (const file of extra) filesMap.set(file.path, file);
|
|
9707
|
+
}
|
|
9500
9708
|
}
|
|
9501
9709
|
const files = [...filesMap.values()].sort((a, b) => a.path.localeCompare(b.path));
|
|
9502
|
-
const { stdout: statOut } = await git(
|
|
9503
|
-
scope === "staged" ? ["diff", "--stat", "--cached"] : scope === "unstaged" ? ["diff", "--stat"] : scope === "uncommitted" ? ["diff", "--stat", "HEAD"] : scope === "last_turn" && lastTurnBase ? ["diff", "--stat", lastTurnBase] : scope === "commits" && commitSha ? ["diff", "--stat", `${commitSha}^!`] : ["diff", "--stat", mergeBase],
|
|
9504
|
-
worktreePath,
|
|
9505
|
-
{ reject: false }
|
|
9506
|
-
);
|
|
9507
9710
|
return {
|
|
9508
9711
|
scope,
|
|
9509
9712
|
commitSha: scope === "commits" ? commitSha : null,
|
|
9510
9713
|
base: labelBase,
|
|
9511
9714
|
files,
|
|
9512
|
-
stat:
|
|
9513
|
-
dirty:
|
|
9514
|
-
unpushed:
|
|
9715
|
+
stat: formatStat(files),
|
|
9716
|
+
dirty: fileOnly || !includeMeta ? files.length > 0 : meta.dirty,
|
|
9717
|
+
unpushed: meta.unpushed,
|
|
9515
9718
|
hasLastTurnBase,
|
|
9516
|
-
commits,
|
|
9517
|
-
scopeStats
|
|
9719
|
+
commits: meta.commits,
|
|
9720
|
+
scopeStats: meta.scopeStats,
|
|
9721
|
+
mergeBase: needsMergeBase ? mergeBase : null
|
|
9518
9722
|
};
|
|
9519
9723
|
}
|
|
9520
9724
|
async function listWorktreeFiles(worktreePath, opts) {
|
|
@@ -13210,6 +13414,11 @@ var Orchestrator = class {
|
|
|
13210
13414
|
return getDiff(thread.worktreePath, thread.repoPath, {
|
|
13211
13415
|
scope: opts?.scope,
|
|
13212
13416
|
commitSha: opts?.commitSha,
|
|
13417
|
+
base: opts?.base,
|
|
13418
|
+
includePatches: opts?.includePatches,
|
|
13419
|
+
includeMeta: opts?.includeMeta,
|
|
13420
|
+
includeUntracked: opts?.includeUntracked,
|
|
13421
|
+
path: opts?.path,
|
|
13213
13422
|
lastTurnBase: this.turnBaselines.get(thread.id) ?? null
|
|
13214
13423
|
});
|
|
13215
13424
|
}
|