@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/mcp/run-stdio.cjs
CHANGED
|
@@ -1649,7 +1649,7 @@ async function git(args, cwd, opts) {
|
|
|
1649
1649
|
prefix.push("-c", `${key}=${value}`);
|
|
1650
1650
|
}
|
|
1651
1651
|
}
|
|
1652
|
-
return run("git", [...prefix, ...args], {
|
|
1652
|
+
return run("git", ["--no-pager", ...prefix, ...args], {
|
|
1653
1653
|
cwd,
|
|
1654
1654
|
reject: opts?.reject,
|
|
1655
1655
|
timeoutMs: opts?.timeoutMs,
|
|
@@ -2124,7 +2124,7 @@ async function originGhRepoEnv(cwd) {
|
|
|
2124
2124
|
const slug = await resolveGithubRepoSlug(cwd);
|
|
2125
2125
|
return slug ? { GH_REPO: slug } : {};
|
|
2126
2126
|
}
|
|
2127
|
-
async function resolveDefaultBranch(repoPath) {
|
|
2127
|
+
async function resolveDefaultBranch(repoPath, opts) {
|
|
2128
2128
|
const viaOrigin = await git(
|
|
2129
2129
|
["symbolic-ref", "refs/remotes/origin/HEAD"],
|
|
2130
2130
|
repoPath,
|
|
@@ -2133,22 +2133,24 @@ async function resolveDefaultBranch(repoPath) {
|
|
|
2133
2133
|
if (viaOrigin.exitCode === 0 && viaOrigin.stdout.trim()) {
|
|
2134
2134
|
return viaOrigin.stdout.trim().replace(/^refs\/remotes\/origin\//, "");
|
|
2135
2135
|
}
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2136
|
+
if (opts?.network !== false) {
|
|
2137
|
+
const slug = await resolveGithubRepoSlug(repoPath);
|
|
2138
|
+
const viaGh = await gh(
|
|
2139
|
+
[
|
|
2140
|
+
"repo",
|
|
2141
|
+
"view",
|
|
2142
|
+
...slug ? ["--repo", slug] : [],
|
|
2143
|
+
"--json",
|
|
2144
|
+
"defaultBranchRef",
|
|
2145
|
+
"--jq",
|
|
2146
|
+
".defaultBranchRef.name"
|
|
2147
|
+
],
|
|
2148
|
+
repoPath,
|
|
2149
|
+
{ reject: false, timeoutMs: 8e3 }
|
|
2150
|
+
);
|
|
2151
|
+
if (viaGh.exitCode === 0 && viaGh.stdout.trim()) {
|
|
2152
|
+
return viaGh.stdout.trim();
|
|
2153
|
+
}
|
|
2152
2154
|
}
|
|
2153
2155
|
for (const candidate of ["main", "master"]) {
|
|
2154
2156
|
const check = await git(
|
|
@@ -2800,11 +2802,11 @@ async function listWorktrees(repoPath) {
|
|
|
2800
2802
|
return entries;
|
|
2801
2803
|
}
|
|
2802
2804
|
async function isDirty(worktreePath) {
|
|
2803
|
-
const { stdout } = await git(["status", "--porcelain"
|
|
2805
|
+
const { stdout } = await git(["status", "--porcelain"], worktreePath);
|
|
2804
2806
|
for (const line of stdout.split("\n")) {
|
|
2805
2807
|
if (!line.trim()) continue;
|
|
2806
2808
|
const rel = porcelainStatusPath(line);
|
|
2807
|
-
if (!rel || isSideboardScratchPath(rel)) continue;
|
|
2809
|
+
if (!rel || isSideboardScratchPath(rel) || rel === ".sideboard") continue;
|
|
2808
2810
|
return true;
|
|
2809
2811
|
}
|
|
2810
2812
|
return false;
|
|
@@ -9744,24 +9746,25 @@ function capPatch(patch, maxHunk) {
|
|
|
9744
9746
|
|
|
9745
9747
|
\u2026 truncated (${patch.length - maxHunk} more chars)`;
|
|
9746
9748
|
}
|
|
9749
|
+
var mergeBaseCache = /* @__PURE__ */ new Map();
|
|
9750
|
+
var MERGE_BASE_TTL_MS = 45e3;
|
|
9747
9751
|
async function resolveMergeBase(worktreePath, base, repoPath) {
|
|
9748
9752
|
const baseRef = await resolveDiffBaseRef(worktreePath, base, repoPath);
|
|
9753
|
+
const cacheKey = `${worktreePath}\0${baseRef}`;
|
|
9754
|
+
const hit = mergeBaseCache.get(cacheKey);
|
|
9755
|
+
if (hit && Date.now() - hit.at < MERGE_BASE_TTL_MS) return hit.value;
|
|
9749
9756
|
const { stdout } = await git(["merge-base", baseRef, "HEAD"], worktreePath, {
|
|
9750
9757
|
reject: false
|
|
9751
9758
|
});
|
|
9752
|
-
const mb = stdout.trim();
|
|
9753
|
-
|
|
9759
|
+
const mb = stdout.trim() || baseRef;
|
|
9760
|
+
mergeBaseCache.set(cacheKey, { at: Date.now(), value: mb });
|
|
9761
|
+
return mb;
|
|
9754
9762
|
}
|
|
9755
9763
|
async function countUnpushedCommits(worktreePath) {
|
|
9756
9764
|
const head = await git(["rev-parse", "--abbrev-ref", "HEAD"], worktreePath, {
|
|
9757
9765
|
reject: false
|
|
9758
9766
|
});
|
|
9759
9767
|
const branch = head.stdout.trim();
|
|
9760
|
-
if (branch && branch !== "HEAD") {
|
|
9761
|
-
await git(["fetch", "origin", branch, "--quiet"], worktreePath, {
|
|
9762
|
-
reject: false
|
|
9763
|
-
});
|
|
9764
|
-
}
|
|
9765
9768
|
const upstream = await git(
|
|
9766
9769
|
["rev-list", "--count", "@{upstream}..HEAD"],
|
|
9767
9770
|
worktreePath,
|
|
@@ -9794,11 +9797,37 @@ function splitCombinedDiff(stdout) {
|
|
|
9794
9797
|
}
|
|
9795
9798
|
return map;
|
|
9796
9799
|
}
|
|
9800
|
+
function syntheticAddPatch(path, content, maxHunk) {
|
|
9801
|
+
const lines = content.split("\n");
|
|
9802
|
+
const body = lines.map((l) => `+${l}`).join("\n");
|
|
9803
|
+
const header = `diff --git a/${path} b/${path}
|
|
9804
|
+
new file mode 100644
|
|
9805
|
+
--- /dev/null
|
|
9806
|
+
+++ b/${path}
|
|
9807
|
+
@@ -0,0 +1,${lines.length} @@
|
|
9808
|
+
`;
|
|
9809
|
+
return {
|
|
9810
|
+
path,
|
|
9811
|
+
status: "A",
|
|
9812
|
+
patch: capPatch(`${header}${body}`, maxHunk),
|
|
9813
|
+
additions: lines.length,
|
|
9814
|
+
deletions: 0
|
|
9815
|
+
};
|
|
9816
|
+
}
|
|
9797
9817
|
async function untrackedPatch(worktreePath, path, maxHunk) {
|
|
9818
|
+
const abs = (0, import_node_path20.join)(worktreePath, path);
|
|
9819
|
+
try {
|
|
9820
|
+
const st = (0, import_node_fs25.statSync)(abs);
|
|
9821
|
+
if (st.isFile() && st.size > maxHunk) {
|
|
9822
|
+
const buf = (0, import_node_fs25.readFileSync)(abs).subarray(0, maxHunk);
|
|
9823
|
+
return syntheticAddPatch(path, buf.toString("utf8"), maxHunk);
|
|
9824
|
+
}
|
|
9825
|
+
} catch {
|
|
9826
|
+
}
|
|
9798
9827
|
const { stdout: patch } = await git(
|
|
9799
|
-
["diff", "--no-index", "--", "/dev/null", path],
|
|
9828
|
+
["diff", "--no-ext-diff", "--no-color", "--no-index", "--", "/dev/null", path],
|
|
9800
9829
|
worktreePath,
|
|
9801
|
-
{ reject: false }
|
|
9830
|
+
{ reject: false, timeoutMs: 4e3 }
|
|
9802
9831
|
);
|
|
9803
9832
|
const normalized = patch.replace(/^diff --git a\/.+? b\/.+?$/m, `diff --git a/${path} b/${path}`).replace(/^--- .*$/m, "--- /dev/null").replace(/^\+\+\+ .*$/m, `+++ b/${path}`);
|
|
9804
9833
|
const addLines = normalized.split("\n").filter((l) => l.startsWith("+") && !l.startsWith("+++")).length;
|
|
@@ -9810,22 +9839,97 @@ async function untrackedPatch(worktreePath, path, maxHunk) {
|
|
|
9810
9839
|
deletions: 0
|
|
9811
9840
|
};
|
|
9812
9841
|
}
|
|
9813
|
-
async function listUntrackedPaths(worktreePath) {
|
|
9814
|
-
const
|
|
9815
|
-
|
|
9816
|
-
|
|
9817
|
-
{ reject: false }
|
|
9818
|
-
);
|
|
9842
|
+
async function listUntrackedPaths(worktreePath, path) {
|
|
9843
|
+
const args = ["ls-files", "-z", "--others", "--exclude-standard"];
|
|
9844
|
+
if (path) args.push("--", path);
|
|
9845
|
+
const { stdout } = await git(args, worktreePath, { reject: false });
|
|
9819
9846
|
return stdout.split("\0").filter(Boolean).filter((p) => !isSideboardScratchPath(p));
|
|
9820
9847
|
}
|
|
9821
|
-
async function listUntrackedFiles(worktreePath, maxHunk) {
|
|
9822
|
-
const
|
|
9823
|
-
if (
|
|
9824
|
-
return Promise.all(
|
|
9848
|
+
async function listUntrackedFiles(worktreePath, maxHunk, paths) {
|
|
9849
|
+
const list = paths ?? await listUntrackedPaths(worktreePath);
|
|
9850
|
+
if (list.length === 0) return [];
|
|
9851
|
+
return Promise.all(list.map((path) => untrackedPatch(worktreePath, path, maxHunk)));
|
|
9852
|
+
}
|
|
9853
|
+
function untrackedStubs(paths) {
|
|
9854
|
+
return paths.map((path) => ({
|
|
9855
|
+
path,
|
|
9856
|
+
status: "A",
|
|
9857
|
+
patch: "",
|
|
9858
|
+
additions: 0,
|
|
9859
|
+
deletions: 0
|
|
9860
|
+
}));
|
|
9861
|
+
}
|
|
9862
|
+
function pathspec(path) {
|
|
9863
|
+
return path ? ["--", path] : [];
|
|
9864
|
+
}
|
|
9865
|
+
function gitDiff(args, cwd, opts) {
|
|
9866
|
+
return git(["diff", "--no-ext-diff", "--no-color", ...args], cwd, {
|
|
9867
|
+
reject: false,
|
|
9868
|
+
timeoutMs: opts?.timeoutMs
|
|
9869
|
+
});
|
|
9870
|
+
}
|
|
9871
|
+
function fileFromPatch(path, patch, maxHunk) {
|
|
9872
|
+
let status = "M";
|
|
9873
|
+
if (/^new file mode /m.test(patch) || /^--- \/dev\/null/m.test(patch)) {
|
|
9874
|
+
status = "A";
|
|
9875
|
+
} else if (/^deleted file mode /m.test(patch) || /^\+\+\+ \/dev\/null/m.test(patch)) {
|
|
9876
|
+
status = "D";
|
|
9877
|
+
} else if (/^rename from /m.test(patch)) {
|
|
9878
|
+
status = "R";
|
|
9879
|
+
}
|
|
9880
|
+
const additions = patch.split("\n").filter((l) => l.startsWith("+") && !l.startsWith("+++")).length;
|
|
9881
|
+
const deletions = patch.split("\n").filter((l) => l.startsWith("-") && !l.startsWith("---")).length;
|
|
9882
|
+
return {
|
|
9883
|
+
path,
|
|
9884
|
+
status,
|
|
9885
|
+
patch: capPatch(patch, maxHunk),
|
|
9886
|
+
additions,
|
|
9887
|
+
deletions
|
|
9888
|
+
};
|
|
9889
|
+
}
|
|
9890
|
+
async function gitDiffParts(worktreePath, diffArgs, includePatches, path) {
|
|
9891
|
+
const extra = pathspec(path);
|
|
9892
|
+
if (includePatches) {
|
|
9893
|
+
const diff = await gitDiff([...diffArgs, ...extra], worktreePath, {
|
|
9894
|
+
timeoutMs: 4e3
|
|
9895
|
+
});
|
|
9896
|
+
return { nameStatus: "", numstat: "", combinedDiff: diff.stdout };
|
|
9897
|
+
}
|
|
9898
|
+
const [ns, num2] = await Promise.all([
|
|
9899
|
+
gitDiff(["--name-status", ...diffArgs, ...extra], worktreePath),
|
|
9900
|
+
gitDiff(["--numstat", ...diffArgs, ...extra], worktreePath)
|
|
9901
|
+
]);
|
|
9902
|
+
return {
|
|
9903
|
+
nameStatus: ns.stdout,
|
|
9904
|
+
numstat: num2.stdout,
|
|
9905
|
+
combinedDiff: ""
|
|
9906
|
+
};
|
|
9907
|
+
}
|
|
9908
|
+
function formatStat(files) {
|
|
9909
|
+
if (files.length === 0) return "(no changes)";
|
|
9910
|
+
const additions = files.reduce((n2, f) => n2 + (f.additions ?? 0), 0);
|
|
9911
|
+
const deletions = files.reduce((n2, f) => n2 + (f.deletions ?? 0), 0);
|
|
9912
|
+
const n = files.length;
|
|
9913
|
+
return `${n} file${n === 1 ? "" : "s"} changed, ${additions} insertions(+), ${deletions} deletions(-)`;
|
|
9914
|
+
}
|
|
9915
|
+
function emptyScopeStats() {
|
|
9916
|
+
const z2 = emptyStat();
|
|
9917
|
+
return {
|
|
9918
|
+
commits: z2,
|
|
9919
|
+
uncommitted: z2,
|
|
9920
|
+
staged: z2,
|
|
9921
|
+
unstaged: z2,
|
|
9922
|
+
last_turn: z2
|
|
9923
|
+
};
|
|
9825
9924
|
}
|
|
9826
9925
|
function filesFromDiff(nameStatus, numstat, combinedDiff, maxHunk) {
|
|
9827
|
-
const counts = parseNumstat(numstat);
|
|
9828
9926
|
const patches = splitCombinedDiff(combinedDiff);
|
|
9927
|
+
if (!nameStatus.trim() && patches.size > 0) {
|
|
9928
|
+
return [...patches.entries()].map(
|
|
9929
|
+
([path, patch]) => fileFromPatch(path, patch, maxHunk)
|
|
9930
|
+
);
|
|
9931
|
+
}
|
|
9932
|
+
const counts = parseNumstat(numstat);
|
|
9829
9933
|
const files = [];
|
|
9830
9934
|
for (const { status, path } of parseNameStatus(nameStatus)) {
|
|
9831
9935
|
const n = counts.get(path);
|
|
@@ -9847,11 +9951,11 @@ async function computeScopeStats(worktreePath, mergeBase, lastTurnBase, untracke
|
|
|
9847
9951
|
{ stdout: unstagedNum },
|
|
9848
9952
|
lastTurnNum
|
|
9849
9953
|
] = await Promise.all([
|
|
9850
|
-
mergeBase ?
|
|
9851
|
-
|
|
9852
|
-
|
|
9853
|
-
|
|
9854
|
-
lastTurnBase ?
|
|
9954
|
+
mergeBase ? gitDiff(["--numstat", mergeBase], worktreePath) : Promise.resolve({ stdout: "" }),
|
|
9955
|
+
gitDiff(["--numstat", "HEAD"], worktreePath),
|
|
9956
|
+
gitDiff(["--numstat", "--cached"], worktreePath),
|
|
9957
|
+
gitDiff(["--numstat"], worktreePath),
|
|
9958
|
+
lastTurnBase ? gitDiff(["--numstat", lastTurnBase], worktreePath) : Promise.resolve({ stdout: "" })
|
|
9855
9959
|
]);
|
|
9856
9960
|
return {
|
|
9857
9961
|
commits: mergeBase ? statFromNumstat(commitsNum, untrackedCount) : emptyStat(),
|
|
@@ -9870,7 +9974,7 @@ async function listBranchCommits(worktreePath, repoPath, opts) {
|
|
|
9870
9974
|
if (head.exitCode !== 0) return [];
|
|
9871
9975
|
let base = opts?.base;
|
|
9872
9976
|
try {
|
|
9873
|
-
base = base ?? await resolveDefaultBranch(repoPath);
|
|
9977
|
+
base = base ?? await resolveDefaultBranch(repoPath, { network: false });
|
|
9874
9978
|
} catch {
|
|
9875
9979
|
base = "HEAD";
|
|
9876
9980
|
}
|
|
@@ -9900,7 +10004,121 @@ async function listBranchCommits(worktreePath, repoPath, opts) {
|
|
|
9900
10004
|
}
|
|
9901
10005
|
return commits;
|
|
9902
10006
|
}
|
|
10007
|
+
var SHA_RE = /^[0-9a-f]{7,40}$/i;
|
|
10008
|
+
function emptyPathDiff(scope, base, hasLastTurnBase, commitSha = null) {
|
|
10009
|
+
return {
|
|
10010
|
+
scope,
|
|
10011
|
+
commitSha: scope === "commits" ? commitSha : null,
|
|
10012
|
+
base,
|
|
10013
|
+
files: [],
|
|
10014
|
+
stat: formatStat([]),
|
|
10015
|
+
dirty: false,
|
|
10016
|
+
unpushed: 0,
|
|
10017
|
+
hasLastTurnBase,
|
|
10018
|
+
commits: [],
|
|
10019
|
+
scopeStats: emptyScopeStats()
|
|
10020
|
+
};
|
|
10021
|
+
}
|
|
10022
|
+
async function getPathDiff(worktreePath, repoPath, opts) {
|
|
10023
|
+
const { path, scope, commitSha, lastTurnBase, hasLastTurnBase, includePatches, maxHunk } = opts;
|
|
10024
|
+
if (scope === "last_turn" && !lastTurnBase) {
|
|
10025
|
+
return emptyPathDiff(scope, "last turn", false);
|
|
10026
|
+
}
|
|
10027
|
+
let diffArgs;
|
|
10028
|
+
let labelBase;
|
|
10029
|
+
if (scope === "staged") {
|
|
10030
|
+
diffArgs = ["--cached"];
|
|
10031
|
+
labelBase = "staged";
|
|
10032
|
+
} else if (scope === "unstaged") {
|
|
10033
|
+
diffArgs = [];
|
|
10034
|
+
labelBase = "unstaged";
|
|
10035
|
+
} else if (scope === "uncommitted") {
|
|
10036
|
+
diffArgs = ["HEAD"];
|
|
10037
|
+
labelBase = "HEAD";
|
|
10038
|
+
} else if (scope === "last_turn") {
|
|
10039
|
+
diffArgs = [lastTurnBase];
|
|
10040
|
+
labelBase = "last turn";
|
|
10041
|
+
} else if (scope === "commits" && commitSha) {
|
|
10042
|
+
diffArgs = [`${commitSha}^!`];
|
|
10043
|
+
labelBase = commitSha.slice(0, 7);
|
|
10044
|
+
} else if (scope === "commits" && opts.base && SHA_RE.test(opts.base)) {
|
|
10045
|
+
diffArgs = [opts.base];
|
|
10046
|
+
labelBase = opts.base.slice(0, 7);
|
|
10047
|
+
} else if (scope === "commits") {
|
|
10048
|
+
let base = opts.base;
|
|
10049
|
+
try {
|
|
10050
|
+
base = base ?? await resolveDefaultBranch(repoPath, { network: false });
|
|
10051
|
+
} catch {
|
|
10052
|
+
base = "HEAD";
|
|
10053
|
+
}
|
|
10054
|
+
const mergeBase = await resolveMergeBase(worktreePath, base, repoPath);
|
|
10055
|
+
diffArgs = [mergeBase];
|
|
10056
|
+
labelBase = mergeBase;
|
|
10057
|
+
} else {
|
|
10058
|
+
diffArgs = ["HEAD"];
|
|
10059
|
+
labelBase = "HEAD";
|
|
10060
|
+
}
|
|
10061
|
+
const diff = await gitDiff(
|
|
10062
|
+
includePatches ? [...diffArgs, "--", path] : ["--name-status", ...diffArgs, "--", path],
|
|
10063
|
+
worktreePath,
|
|
10064
|
+
{ timeoutMs: 4e3 }
|
|
10065
|
+
);
|
|
10066
|
+
const filesMap = /* @__PURE__ */ new Map();
|
|
10067
|
+
if (includePatches) {
|
|
10068
|
+
const parsed = filesFromDiff("", "", diff.stdout, maxHunk);
|
|
10069
|
+
for (const file of parsed) filesMap.set(file.path, file);
|
|
10070
|
+
if (diff.stdout.trim() && !filesMap.has(path)) {
|
|
10071
|
+
filesMap.set(path, fileFromPatch(path, diff.stdout, maxHunk));
|
|
10072
|
+
}
|
|
10073
|
+
} else {
|
|
10074
|
+
for (const file of filesFromDiff(diff.stdout, "", "", maxHunk)) {
|
|
10075
|
+
filesMap.set(file.path, file);
|
|
10076
|
+
}
|
|
10077
|
+
}
|
|
10078
|
+
if (filesMap.size === 0) {
|
|
10079
|
+
const maybe = await listUntrackedPaths(worktreePath, path);
|
|
10080
|
+
if (maybe.length > 0) {
|
|
10081
|
+
const extra = includePatches ? await listUntrackedFiles(worktreePath, maxHunk, maybe) : untrackedStubs(maybe);
|
|
10082
|
+
for (const file of extra) filesMap.set(file.path, file);
|
|
10083
|
+
}
|
|
10084
|
+
}
|
|
10085
|
+
const files = [...filesMap.values()].sort((a, b) => a.path.localeCompare(b.path));
|
|
10086
|
+
return {
|
|
10087
|
+
scope,
|
|
10088
|
+
commitSha: scope === "commits" ? commitSha : null,
|
|
10089
|
+
base: labelBase,
|
|
10090
|
+
files,
|
|
10091
|
+
stat: formatStat(files),
|
|
10092
|
+
dirty: files.length > 0,
|
|
10093
|
+
unpushed: 0,
|
|
10094
|
+
hasLastTurnBase,
|
|
10095
|
+
commits: [],
|
|
10096
|
+
scopeStats: emptyScopeStats(),
|
|
10097
|
+
mergeBase: SHA_RE.test(diffArgs[0] ?? "") ? diffArgs[0] : null
|
|
10098
|
+
};
|
|
10099
|
+
}
|
|
9903
10100
|
async function getDiff(worktreePath, repoPath, opts) {
|
|
10101
|
+
const maxHunk = opts?.maxHunkChars ?? 8e3;
|
|
10102
|
+
const scope = opts?.scope ?? "commits";
|
|
10103
|
+
const lastTurnBase = opts?.lastTurnBase?.trim() || null;
|
|
10104
|
+
const hasLastTurnBase = Boolean(lastTurnBase);
|
|
10105
|
+
const commitSha = opts?.commitSha?.trim() || null;
|
|
10106
|
+
const includePatches = opts?.includePatches ?? true;
|
|
10107
|
+
const pathFilter = opts?.path?.trim() || null;
|
|
10108
|
+
const fileOnly = Boolean(pathFilter);
|
|
10109
|
+
const includeMeta = (opts?.includeMeta ?? true) && !fileOnly;
|
|
10110
|
+
if (fileOnly && pathFilter && !includeMeta) {
|
|
10111
|
+
return getPathDiff(worktreePath, repoPath, {
|
|
10112
|
+
path: pathFilter,
|
|
10113
|
+
scope,
|
|
10114
|
+
commitSha,
|
|
10115
|
+
lastTurnBase,
|
|
10116
|
+
hasLastTurnBase,
|
|
10117
|
+
base: opts?.base,
|
|
10118
|
+
includePatches,
|
|
10119
|
+
maxHunk
|
|
10120
|
+
});
|
|
10121
|
+
}
|
|
9904
10122
|
const status = await inspectGitWorktree(worktreePath);
|
|
9905
10123
|
if (status === "missing_worktree") {
|
|
9906
10124
|
throw new Error("Worktree not found");
|
|
@@ -9908,18 +10126,19 @@ async function getDiff(worktreePath, repoPath, opts) {
|
|
|
9908
10126
|
if (status === "not_git") {
|
|
9909
10127
|
throw new Error("Not a Git repository");
|
|
9910
10128
|
}
|
|
9911
|
-
const
|
|
9912
|
-
const
|
|
9913
|
-
const
|
|
9914
|
-
const hasLastTurnBase = Boolean(lastTurnBase);
|
|
9915
|
-
const commitSha = opts?.commitSha?.trim() || null;
|
|
9916
|
-
const head = await git(["rev-parse", "--verify", "HEAD"], worktreePath, {
|
|
10129
|
+
const wantUntracked = opts?.includeUntracked !== void 0 ? opts.includeUntracked : !fileOnly && (scope === "commits" && !commitSha || scope === "uncommitted" || scope === "unstaged" || scope === "last_turn");
|
|
10130
|
+
const needsMergeBase = scope === "commits" && !commitSha || includeMeta;
|
|
10131
|
+
const headP = git(["rev-parse", "--verify", "HEAD"], worktreePath, {
|
|
9917
10132
|
reject: false
|
|
9918
10133
|
});
|
|
10134
|
+
const untrackedP = wantUntracked ? listUntrackedPaths(worktreePath, pathFilter) : Promise.resolve([]);
|
|
10135
|
+
const head = await headP;
|
|
9919
10136
|
const hasHead = head.exitCode === 0;
|
|
9920
|
-
const untrackedPaths = await listUntrackedPaths(worktreePath);
|
|
9921
10137
|
if (!hasHead) {
|
|
9922
|
-
const
|
|
10138
|
+
const untrackedPaths2 = await untrackedP;
|
|
10139
|
+
const files2 = scope === "staged" ? [] : includePatches ? (await listUntrackedFiles(worktreePath, maxHunk, untrackedPaths2)).sort(
|
|
10140
|
+
(a, b) => a.path.localeCompare(b.path)
|
|
10141
|
+
) : untrackedStubs(untrackedPaths2).sort(
|
|
9923
10142
|
(a, b) => a.path.localeCompare(b.path)
|
|
9924
10143
|
);
|
|
9925
10144
|
const untrackedStat = {
|
|
@@ -9933,7 +10152,7 @@ async function getDiff(worktreePath, repoPath, opts) {
|
|
|
9933
10152
|
commitSha: null,
|
|
9934
10153
|
base: "(no commits)",
|
|
9935
10154
|
files: files2,
|
|
9936
|
-
stat: files2
|
|
10155
|
+
stat: formatStat(files2),
|
|
9937
10156
|
dirty: files2.length > 0,
|
|
9938
10157
|
unpushed: 0,
|
|
9939
10158
|
hasLastTurnBase,
|
|
@@ -9947,137 +10166,122 @@ async function getDiff(worktreePath, repoPath, opts) {
|
|
|
9947
10166
|
}
|
|
9948
10167
|
};
|
|
9949
10168
|
}
|
|
9950
|
-
let
|
|
9951
|
-
|
|
9952
|
-
|
|
9953
|
-
|
|
9954
|
-
|
|
10169
|
+
let mergeBase = "HEAD";
|
|
10170
|
+
let baseLabel = "HEAD";
|
|
10171
|
+
if (needsMergeBase) {
|
|
10172
|
+
let base = opts?.base;
|
|
10173
|
+
try {
|
|
10174
|
+
base = base ?? await resolveDefaultBranch(repoPath, { network: false });
|
|
10175
|
+
} catch {
|
|
10176
|
+
base = "HEAD";
|
|
10177
|
+
}
|
|
10178
|
+
mergeBase = await resolveMergeBase(worktreePath, base, repoPath);
|
|
10179
|
+
baseLabel = await resolveDiffBaseRef(worktreePath, base, repoPath);
|
|
9955
10180
|
}
|
|
9956
|
-
|
|
9957
|
-
|
|
9958
|
-
|
|
9959
|
-
|
|
9960
|
-
|
|
9961
|
-
|
|
9962
|
-
|
|
10181
|
+
const includeUntracked = wantUntracked;
|
|
10182
|
+
const metaPromise = includeMeta ? Promise.all([
|
|
10183
|
+
computeScopeStats(
|
|
10184
|
+
worktreePath,
|
|
10185
|
+
mergeBase,
|
|
10186
|
+
lastTurnBase,
|
|
10187
|
+
// Cheap: don't wait on the untracked walk just to pad the badge.
|
|
10188
|
+
0
|
|
10189
|
+
),
|
|
10190
|
+
listBranchCommits(worktreePath, repoPath, { base: baseLabel }),
|
|
10191
|
+
isDirty(worktreePath),
|
|
10192
|
+
countUnpushedCommits(worktreePath)
|
|
10193
|
+
]).then(([scopeStats, commits, dirty, unpushed]) => ({
|
|
10194
|
+
scopeStats,
|
|
10195
|
+
commits,
|
|
10196
|
+
dirty,
|
|
10197
|
+
unpushed
|
|
10198
|
+
})) : Promise.resolve({
|
|
10199
|
+
scopeStats: emptyScopeStats(),
|
|
10200
|
+
commits: [],
|
|
10201
|
+
dirty: false,
|
|
10202
|
+
unpushed: 0
|
|
10203
|
+
});
|
|
10204
|
+
if (scope === "last_turn" && !lastTurnBase) {
|
|
10205
|
+
const meta2 = await metaPromise;
|
|
10206
|
+
return {
|
|
10207
|
+
scope,
|
|
10208
|
+
commitSha: null,
|
|
10209
|
+
base: "last turn",
|
|
10210
|
+
files: [],
|
|
10211
|
+
stat: "(no last agent turn)",
|
|
10212
|
+
dirty: meta2.dirty,
|
|
10213
|
+
unpushed: meta2.unpushed,
|
|
10214
|
+
hasLastTurnBase: false,
|
|
10215
|
+
commits: meta2.commits,
|
|
10216
|
+
scopeStats: meta2.scopeStats
|
|
10217
|
+
};
|
|
9963
10218
|
}
|
|
9964
|
-
|
|
9965
|
-
|
|
9966
|
-
worktreePath,
|
|
9967
|
-
mergeBase,
|
|
9968
|
-
lastTurnBase,
|
|
9969
|
-
untrackedPaths.length
|
|
9970
|
-
);
|
|
9971
|
-
const includeUntracked = scope === "commits" && !commitSha || scope === "uncommitted" || scope === "unstaged" || scope === "last_turn";
|
|
9972
|
-
let nameStatus = "";
|
|
9973
|
-
let numstat = "";
|
|
9974
|
-
let combinedDiff = "";
|
|
9975
|
-
let labelBase = base;
|
|
10219
|
+
let diffArgs;
|
|
10220
|
+
let labelBase = baseLabel;
|
|
9976
10221
|
if (scope === "staged") {
|
|
9977
|
-
|
|
9978
|
-
git(["diff", "--name-status", "--cached"], worktreePath, { reject: false }),
|
|
9979
|
-
git(["diff", "--numstat", "--cached"], worktreePath, { reject: false }),
|
|
9980
|
-
git(["diff", "--cached"], worktreePath, { reject: false })
|
|
9981
|
-
]);
|
|
9982
|
-
nameStatus = ns.stdout;
|
|
9983
|
-
numstat = num2.stdout;
|
|
9984
|
-
combinedDiff = diff.stdout;
|
|
10222
|
+
diffArgs = ["--cached"];
|
|
9985
10223
|
labelBase = "staged";
|
|
9986
10224
|
} else if (scope === "unstaged") {
|
|
9987
|
-
|
|
9988
|
-
git(["diff", "--name-status"], worktreePath, { reject: false }),
|
|
9989
|
-
git(["diff", "--numstat"], worktreePath, { reject: false }),
|
|
9990
|
-
git(["diff"], worktreePath, { reject: false })
|
|
9991
|
-
]);
|
|
9992
|
-
nameStatus = ns.stdout;
|
|
9993
|
-
numstat = num2.stdout;
|
|
9994
|
-
combinedDiff = diff.stdout;
|
|
10225
|
+
diffArgs = [];
|
|
9995
10226
|
labelBase = "unstaged";
|
|
9996
10227
|
} else if (scope === "uncommitted") {
|
|
9997
|
-
|
|
9998
|
-
git(["diff", "--name-status", "HEAD"], worktreePath, { reject: false }),
|
|
9999
|
-
git(["diff", "--numstat", "HEAD"], worktreePath, { reject: false }),
|
|
10000
|
-
git(["diff", "HEAD"], worktreePath, { reject: false })
|
|
10001
|
-
]);
|
|
10002
|
-
nameStatus = ns.stdout;
|
|
10003
|
-
numstat = num2.stdout;
|
|
10004
|
-
combinedDiff = diff.stdout;
|
|
10228
|
+
diffArgs = ["HEAD"];
|
|
10005
10229
|
labelBase = "HEAD";
|
|
10006
10230
|
} else if (scope === "last_turn") {
|
|
10007
|
-
|
|
10008
|
-
const commits2 = await listBranchCommits(worktreePath, repoPath, { base });
|
|
10009
|
-
return {
|
|
10010
|
-
scope,
|
|
10011
|
-
commitSha: null,
|
|
10012
|
-
base: "last turn",
|
|
10013
|
-
files: [],
|
|
10014
|
-
stat: "(no last agent turn)",
|
|
10015
|
-
dirty: await isDirty(worktreePath),
|
|
10016
|
-
unpushed: await countUnpushedCommits(worktreePath),
|
|
10017
|
-
hasLastTurnBase: false,
|
|
10018
|
-
commits: commits2,
|
|
10019
|
-
scopeStats
|
|
10020
|
-
};
|
|
10021
|
-
}
|
|
10022
|
-
const [ns, num2, diff] = await Promise.all([
|
|
10023
|
-
git(["diff", "--name-status", lastTurnBase], worktreePath, { reject: false }),
|
|
10024
|
-
git(["diff", "--numstat", lastTurnBase], worktreePath, { reject: false }),
|
|
10025
|
-
git(["diff", lastTurnBase], worktreePath, { reject: false })
|
|
10026
|
-
]);
|
|
10027
|
-
nameStatus = ns.stdout;
|
|
10028
|
-
numstat = num2.stdout;
|
|
10029
|
-
combinedDiff = diff.stdout;
|
|
10231
|
+
diffArgs = [lastTurnBase];
|
|
10030
10232
|
labelBase = "last turn";
|
|
10031
10233
|
} else if (scope === "commits" && commitSha) {
|
|
10032
|
-
|
|
10033
|
-
const [ns, num2, diff] = await Promise.all([
|
|
10034
|
-
git(["diff", "--name-status", range], worktreePath, { reject: false }),
|
|
10035
|
-
git(["diff", "--numstat", range], worktreePath, { reject: false }),
|
|
10036
|
-
git(["diff", range], worktreePath, { reject: false })
|
|
10037
|
-
]);
|
|
10038
|
-
nameStatus = ns.stdout;
|
|
10039
|
-
numstat = num2.stdout;
|
|
10040
|
-
combinedDiff = diff.stdout;
|
|
10234
|
+
diffArgs = [`${commitSha}^!`];
|
|
10041
10235
|
labelBase = commitSha.slice(0, 7);
|
|
10042
10236
|
} else {
|
|
10043
|
-
|
|
10044
|
-
|
|
10045
|
-
git(["diff", "--numstat", mergeBase], worktreePath, { reject: false }),
|
|
10046
|
-
git(["diff", mergeBase], worktreePath, { reject: false })
|
|
10047
|
-
]);
|
|
10048
|
-
nameStatus = ns.stdout;
|
|
10049
|
-
numstat = num2.stdout;
|
|
10050
|
-
combinedDiff = diff.stdout;
|
|
10051
|
-
labelBase = base;
|
|
10237
|
+
diffArgs = [mergeBase];
|
|
10238
|
+
labelBase = baseLabel;
|
|
10052
10239
|
}
|
|
10053
|
-
const
|
|
10240
|
+
const partsPromise = gitDiffParts(
|
|
10241
|
+
worktreePath,
|
|
10242
|
+
diffArgs,
|
|
10243
|
+
includePatches,
|
|
10244
|
+
pathFilter
|
|
10245
|
+
);
|
|
10246
|
+
const [parts, meta, untrackedPaths] = await Promise.all([
|
|
10247
|
+
partsPromise,
|
|
10248
|
+
metaPromise,
|
|
10249
|
+
untrackedP
|
|
10250
|
+
]);
|
|
10054
10251
|
const filesMap = /* @__PURE__ */ new Map();
|
|
10055
|
-
for (const file of filesFromDiff(
|
|
10252
|
+
for (const file of filesFromDiff(
|
|
10253
|
+
parts.nameStatus,
|
|
10254
|
+
parts.numstat,
|
|
10255
|
+
parts.combinedDiff,
|
|
10256
|
+
maxHunk
|
|
10257
|
+
)) {
|
|
10056
10258
|
filesMap.set(file.path, file);
|
|
10057
10259
|
}
|
|
10058
10260
|
if (includeUntracked) {
|
|
10059
|
-
const
|
|
10060
|
-
for (const file of
|
|
10261
|
+
const extra = includePatches ? await listUntrackedFiles(worktreePath, maxHunk, untrackedPaths) : untrackedStubs(untrackedPaths);
|
|
10262
|
+
for (const file of extra) {
|
|
10061
10263
|
if (!filesMap.has(file.path)) filesMap.set(file.path, file);
|
|
10062
10264
|
}
|
|
10265
|
+
} else if (fileOnly && pathFilter && !filesMap.has(pathFilter)) {
|
|
10266
|
+
const maybe = await listUntrackedPaths(worktreePath, pathFilter);
|
|
10267
|
+
if (maybe.length > 0) {
|
|
10268
|
+
const extra = includePatches ? await listUntrackedFiles(worktreePath, maxHunk, maybe) : untrackedStubs(maybe);
|
|
10269
|
+
for (const file of extra) filesMap.set(file.path, file);
|
|
10270
|
+
}
|
|
10063
10271
|
}
|
|
10064
10272
|
const files = [...filesMap.values()].sort((a, b) => a.path.localeCompare(b.path));
|
|
10065
|
-
const { stdout: statOut } = await git(
|
|
10066
|
-
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],
|
|
10067
|
-
worktreePath,
|
|
10068
|
-
{ reject: false }
|
|
10069
|
-
);
|
|
10070
10273
|
return {
|
|
10071
10274
|
scope,
|
|
10072
10275
|
commitSha: scope === "commits" ? commitSha : null,
|
|
10073
10276
|
base: labelBase,
|
|
10074
10277
|
files,
|
|
10075
|
-
stat:
|
|
10076
|
-
dirty:
|
|
10077
|
-
unpushed:
|
|
10278
|
+
stat: formatStat(files),
|
|
10279
|
+
dirty: fileOnly || !includeMeta ? files.length > 0 : meta.dirty,
|
|
10280
|
+
unpushed: meta.unpushed,
|
|
10078
10281
|
hasLastTurnBase,
|
|
10079
|
-
commits,
|
|
10080
|
-
scopeStats
|
|
10282
|
+
commits: meta.commits,
|
|
10283
|
+
scopeStats: meta.scopeStats,
|
|
10284
|
+
mergeBase: needsMergeBase ? mergeBase : null
|
|
10081
10285
|
};
|
|
10082
10286
|
}
|
|
10083
10287
|
async function listWorktreeFiles(worktreePath, opts) {
|
|
@@ -12031,6 +12235,11 @@ var Orchestrator = class {
|
|
|
12031
12235
|
return getDiff(thread.worktreePath, thread.repoPath, {
|
|
12032
12236
|
scope: opts?.scope,
|
|
12033
12237
|
commitSha: opts?.commitSha,
|
|
12238
|
+
base: opts?.base,
|
|
12239
|
+
includePatches: opts?.includePatches,
|
|
12240
|
+
includeMeta: opts?.includeMeta,
|
|
12241
|
+
includeUntracked: opts?.includeUntracked,
|
|
12242
|
+
path: opts?.path,
|
|
12034
12243
|
lastTurnBase: this.turnBaselines.get(thread.id) ?? null
|
|
12035
12244
|
});
|
|
12036
12245
|
}
|