@buildautomaton/cli 0.1.50 → 0.1.51
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 +1653 -1287
- package/dist/cli.js.map +4 -4
- package/dist/index.js +1688 -1322
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -973,8 +973,8 @@ var require_command = __commonJS({
|
|
|
973
973
|
"../../node_modules/.pnpm/commander@12.1.0/node_modules/commander/lib/command.js"(exports) {
|
|
974
974
|
var EventEmitter2 = __require("node:events").EventEmitter;
|
|
975
975
|
var childProcess2 = __require("node:child_process");
|
|
976
|
-
var
|
|
977
|
-
var
|
|
976
|
+
var path62 = __require("node:path");
|
|
977
|
+
var fs47 = __require("node:fs");
|
|
978
978
|
var process8 = __require("node:process");
|
|
979
979
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
980
980
|
var { CommanderError: CommanderError2 } = require_error();
|
|
@@ -1906,11 +1906,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1906
1906
|
let launchWithNode = false;
|
|
1907
1907
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
1908
1908
|
function findFile(baseDir, baseName) {
|
|
1909
|
-
const localBin =
|
|
1910
|
-
if (
|
|
1911
|
-
if (sourceExt.includes(
|
|
1909
|
+
const localBin = path62.resolve(baseDir, baseName);
|
|
1910
|
+
if (fs47.existsSync(localBin)) return localBin;
|
|
1911
|
+
if (sourceExt.includes(path62.extname(baseName))) return void 0;
|
|
1912
1912
|
const foundExt = sourceExt.find(
|
|
1913
|
-
(ext) =>
|
|
1913
|
+
(ext) => fs47.existsSync(`${localBin}${ext}`)
|
|
1914
1914
|
);
|
|
1915
1915
|
if (foundExt) return `${localBin}${foundExt}`;
|
|
1916
1916
|
return void 0;
|
|
@@ -1922,21 +1922,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1922
1922
|
if (this._scriptPath) {
|
|
1923
1923
|
let resolvedScriptPath;
|
|
1924
1924
|
try {
|
|
1925
|
-
resolvedScriptPath =
|
|
1925
|
+
resolvedScriptPath = fs47.realpathSync(this._scriptPath);
|
|
1926
1926
|
} catch (err) {
|
|
1927
1927
|
resolvedScriptPath = this._scriptPath;
|
|
1928
1928
|
}
|
|
1929
|
-
executableDir =
|
|
1930
|
-
|
|
1929
|
+
executableDir = path62.resolve(
|
|
1930
|
+
path62.dirname(resolvedScriptPath),
|
|
1931
1931
|
executableDir
|
|
1932
1932
|
);
|
|
1933
1933
|
}
|
|
1934
1934
|
if (executableDir) {
|
|
1935
1935
|
let localFile = findFile(executableDir, executableFile);
|
|
1936
1936
|
if (!localFile && !subcommand._executableFile && this._scriptPath) {
|
|
1937
|
-
const legacyName =
|
|
1937
|
+
const legacyName = path62.basename(
|
|
1938
1938
|
this._scriptPath,
|
|
1939
|
-
|
|
1939
|
+
path62.extname(this._scriptPath)
|
|
1940
1940
|
);
|
|
1941
1941
|
if (legacyName !== this._name) {
|
|
1942
1942
|
localFile = findFile(
|
|
@@ -1947,7 +1947,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1947
1947
|
}
|
|
1948
1948
|
executableFile = localFile || executableFile;
|
|
1949
1949
|
}
|
|
1950
|
-
launchWithNode = sourceExt.includes(
|
|
1950
|
+
launchWithNode = sourceExt.includes(path62.extname(executableFile));
|
|
1951
1951
|
let proc;
|
|
1952
1952
|
if (process8.platform !== "win32") {
|
|
1953
1953
|
if (launchWithNode) {
|
|
@@ -2787,7 +2787,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2787
2787
|
* @return {Command}
|
|
2788
2788
|
*/
|
|
2789
2789
|
nameFromFilename(filename) {
|
|
2790
|
-
this._name =
|
|
2790
|
+
this._name = path62.basename(filename, path62.extname(filename));
|
|
2791
2791
|
return this;
|
|
2792
2792
|
}
|
|
2793
2793
|
/**
|
|
@@ -2801,9 +2801,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2801
2801
|
* @param {string} [path]
|
|
2802
2802
|
* @return {(string|null|Command)}
|
|
2803
2803
|
*/
|
|
2804
|
-
executableDir(
|
|
2805
|
-
if (
|
|
2806
|
-
this._executableDir =
|
|
2804
|
+
executableDir(path63) {
|
|
2805
|
+
if (path63 === void 0) return this._executableDir;
|
|
2806
|
+
this._executableDir = path63;
|
|
2807
2807
|
return this;
|
|
2808
2808
|
}
|
|
2809
2809
|
/**
|
|
@@ -7061,8 +7061,8 @@ var init_parseUtil = __esm({
|
|
|
7061
7061
|
init_errors();
|
|
7062
7062
|
init_en();
|
|
7063
7063
|
makeIssue = (params) => {
|
|
7064
|
-
const { data, path:
|
|
7065
|
-
const fullPath = [...
|
|
7064
|
+
const { data, path: path62, errorMaps, issueData } = params;
|
|
7065
|
+
const fullPath = [...path62, ...issueData.path || []];
|
|
7066
7066
|
const fullIssue = {
|
|
7067
7067
|
...issueData,
|
|
7068
7068
|
path: fullPath
|
|
@@ -7370,11 +7370,11 @@ var init_types = __esm({
|
|
|
7370
7370
|
init_parseUtil();
|
|
7371
7371
|
init_util();
|
|
7372
7372
|
ParseInputLazyPath = class {
|
|
7373
|
-
constructor(parent, value,
|
|
7373
|
+
constructor(parent, value, path62, key) {
|
|
7374
7374
|
this._cachedPath = [];
|
|
7375
7375
|
this.parent = parent;
|
|
7376
7376
|
this.data = value;
|
|
7377
|
-
this._path =
|
|
7377
|
+
this._path = path62;
|
|
7378
7378
|
this._key = key;
|
|
7379
7379
|
}
|
|
7380
7380
|
get path() {
|
|
@@ -11099,10 +11099,10 @@ function assignProp(target, prop, value) {
|
|
|
11099
11099
|
configurable: true
|
|
11100
11100
|
});
|
|
11101
11101
|
}
|
|
11102
|
-
function getElementAtPath(obj,
|
|
11103
|
-
if (!
|
|
11102
|
+
function getElementAtPath(obj, path62) {
|
|
11103
|
+
if (!path62)
|
|
11104
11104
|
return obj;
|
|
11105
|
-
return
|
|
11105
|
+
return path62.reduce((acc, key) => acc?.[key], obj);
|
|
11106
11106
|
}
|
|
11107
11107
|
function promiseAllObject(promisesObj) {
|
|
11108
11108
|
const keys = Object.keys(promisesObj);
|
|
@@ -11351,11 +11351,11 @@ function aborted(x, startIndex = 0) {
|
|
|
11351
11351
|
}
|
|
11352
11352
|
return false;
|
|
11353
11353
|
}
|
|
11354
|
-
function prefixIssues(
|
|
11354
|
+
function prefixIssues(path62, issues) {
|
|
11355
11355
|
return issues.map((iss) => {
|
|
11356
11356
|
var _a2;
|
|
11357
11357
|
(_a2 = iss).path ?? (_a2.path = []);
|
|
11358
|
-
iss.path.unshift(
|
|
11358
|
+
iss.path.unshift(path62);
|
|
11359
11359
|
return iss;
|
|
11360
11360
|
});
|
|
11361
11361
|
}
|
|
@@ -11544,7 +11544,7 @@ function treeifyError(error40, _mapper) {
|
|
|
11544
11544
|
return issue2.message;
|
|
11545
11545
|
};
|
|
11546
11546
|
const result = { errors: [] };
|
|
11547
|
-
const processError = (error41,
|
|
11547
|
+
const processError = (error41, path62 = []) => {
|
|
11548
11548
|
var _a2, _b;
|
|
11549
11549
|
for (const issue2 of error41.issues) {
|
|
11550
11550
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
@@ -11554,7 +11554,7 @@ function treeifyError(error40, _mapper) {
|
|
|
11554
11554
|
} else if (issue2.code === "invalid_element") {
|
|
11555
11555
|
processError({ issues: issue2.issues }, issue2.path);
|
|
11556
11556
|
} else {
|
|
11557
|
-
const fullpath = [...
|
|
11557
|
+
const fullpath = [...path62, ...issue2.path];
|
|
11558
11558
|
if (fullpath.length === 0) {
|
|
11559
11559
|
result.errors.push(mapper(issue2));
|
|
11560
11560
|
continue;
|
|
@@ -11584,9 +11584,9 @@ function treeifyError(error40, _mapper) {
|
|
|
11584
11584
|
processError(error40);
|
|
11585
11585
|
return result;
|
|
11586
11586
|
}
|
|
11587
|
-
function toDotPath(
|
|
11587
|
+
function toDotPath(path62) {
|
|
11588
11588
|
const segs = [];
|
|
11589
|
-
for (const seg of
|
|
11589
|
+
for (const seg of path62) {
|
|
11590
11590
|
if (typeof seg === "number")
|
|
11591
11591
|
segs.push(`[${seg}]`);
|
|
11592
11592
|
else if (typeof seg === "symbol")
|
|
@@ -24049,8 +24049,8 @@ var init_acp = __esm({
|
|
|
24049
24049
|
this.#requestHandler = requestHandler;
|
|
24050
24050
|
this.#notificationHandler = notificationHandler;
|
|
24051
24051
|
this.#stream = stream;
|
|
24052
|
-
this.#closedPromise = new Promise((
|
|
24053
|
-
this.#abortController.signal.addEventListener("abort", () =>
|
|
24052
|
+
this.#closedPromise = new Promise((resolve34) => {
|
|
24053
|
+
this.#abortController.signal.addEventListener("abort", () => resolve34());
|
|
24054
24054
|
});
|
|
24055
24055
|
this.#receive();
|
|
24056
24056
|
}
|
|
@@ -24199,8 +24199,8 @@ var init_acp = __esm({
|
|
|
24199
24199
|
}
|
|
24200
24200
|
async sendRequest(method, params) {
|
|
24201
24201
|
const id = this.#nextRequestId++;
|
|
24202
|
-
const responsePromise = new Promise((
|
|
24203
|
-
this.#pendingResponses.set(id, { resolve:
|
|
24202
|
+
const responsePromise = new Promise((resolve34, reject) => {
|
|
24203
|
+
this.#pendingResponses.set(id, { resolve: resolve34, reject });
|
|
24204
24204
|
});
|
|
24205
24205
|
await this.#sendMessage({ jsonrpc: "2.0", id, method, params });
|
|
24206
24206
|
return responsePromise;
|
|
@@ -25069,10 +25069,10 @@ var require_src2 = __commonJS({
|
|
|
25069
25069
|
var fs_1 = __require("fs");
|
|
25070
25070
|
var debug_1 = __importDefault(require_src());
|
|
25071
25071
|
var log2 = debug_1.default("@kwsites/file-exists");
|
|
25072
|
-
function check2(
|
|
25073
|
-
log2(`checking %s`,
|
|
25072
|
+
function check2(path62, isFile, isDirectory) {
|
|
25073
|
+
log2(`checking %s`, path62);
|
|
25074
25074
|
try {
|
|
25075
|
-
const stat3 = fs_1.statSync(
|
|
25075
|
+
const stat3 = fs_1.statSync(path62);
|
|
25076
25076
|
if (stat3.isFile() && isFile) {
|
|
25077
25077
|
log2(`[OK] path represents a file`);
|
|
25078
25078
|
return true;
|
|
@@ -25092,8 +25092,8 @@ var require_src2 = __commonJS({
|
|
|
25092
25092
|
throw e;
|
|
25093
25093
|
}
|
|
25094
25094
|
}
|
|
25095
|
-
function exists2(
|
|
25096
|
-
return check2(
|
|
25095
|
+
function exists2(path62, type = exports.READABLE) {
|
|
25096
|
+
return check2(path62, (type & exports.FILE) > 0, (type & exports.FOLDER) > 0);
|
|
25097
25097
|
}
|
|
25098
25098
|
exports.exists = exists2;
|
|
25099
25099
|
exports.FILE = 1;
|
|
@@ -25174,15 +25174,15 @@ var {
|
|
|
25174
25174
|
} = import_index.default;
|
|
25175
25175
|
|
|
25176
25176
|
// src/cli-version.ts
|
|
25177
|
-
var CLI_VERSION = "0.1.
|
|
25177
|
+
var CLI_VERSION = "0.1.51".length > 0 ? "0.1.51" : "0.0.0-dev";
|
|
25178
25178
|
|
|
25179
25179
|
// src/cli/defaults.ts
|
|
25180
25180
|
var DEFAULT_API_URL = process.env.BUILDAUTOMATON_API_URL ?? "https://api.buildautomaton.com";
|
|
25181
25181
|
var DEFAULT_FIREHOSE_URL = "https://buildautomaton-firehose.fly.dev";
|
|
25182
25182
|
|
|
25183
25183
|
// src/cli/run-cli-action.ts
|
|
25184
|
-
import * as
|
|
25185
|
-
import * as
|
|
25184
|
+
import * as fs46 from "node:fs";
|
|
25185
|
+
import * as path61 from "node:path";
|
|
25186
25186
|
|
|
25187
25187
|
// src/cli-log-level.ts
|
|
25188
25188
|
var verbosity = "info";
|
|
@@ -26297,14 +26297,14 @@ var baseOpen = async (options) => {
|
|
|
26297
26297
|
}
|
|
26298
26298
|
const subprocess = childProcess.spawn(command, cliArguments, childProcessOptions);
|
|
26299
26299
|
if (options.wait) {
|
|
26300
|
-
return new Promise((
|
|
26300
|
+
return new Promise((resolve34, reject) => {
|
|
26301
26301
|
subprocess.once("error", reject);
|
|
26302
26302
|
subprocess.once("close", (exitCode) => {
|
|
26303
26303
|
if (!options.allowNonzeroExitCode && exitCode > 0) {
|
|
26304
26304
|
reject(new Error(`Exited with code ${exitCode}`));
|
|
26305
26305
|
return;
|
|
26306
26306
|
}
|
|
26307
|
-
|
|
26307
|
+
resolve34(subprocess);
|
|
26308
26308
|
});
|
|
26309
26309
|
});
|
|
26310
26310
|
}
|
|
@@ -26848,8 +26848,8 @@ function runPendingAuth(options) {
|
|
|
26848
26848
|
let hasOpenedBrowser = false;
|
|
26849
26849
|
let resolved = false;
|
|
26850
26850
|
let resolveAuth;
|
|
26851
|
-
const authPromise = new Promise((
|
|
26852
|
-
resolveAuth =
|
|
26851
|
+
const authPromise = new Promise((resolve34) => {
|
|
26852
|
+
resolveAuth = resolve34;
|
|
26853
26853
|
});
|
|
26854
26854
|
let reconnectAttempt = 0;
|
|
26855
26855
|
const signInQuiet = createEmptyReconnectQuietSlot();
|
|
@@ -27000,7 +27000,7 @@ import sqliteWasm from "node-sqlite3-wasm";
|
|
|
27000
27000
|
|
|
27001
27001
|
// src/runtime/yield-to-event-loop.ts
|
|
27002
27002
|
function yieldToEventLoop() {
|
|
27003
|
-
return new Promise((
|
|
27003
|
+
return new Promise((resolve34) => setImmediate(resolve34));
|
|
27004
27004
|
}
|
|
27005
27005
|
|
|
27006
27006
|
// src/sqlite/cli-sqlite-paths.ts
|
|
@@ -28117,9 +28117,9 @@ function parseChangeSummaryJson(raw, allowedPaths, options) {
|
|
|
28117
28117
|
const rawPath = typeof o.path === "string" ? o.path.trim() : "";
|
|
28118
28118
|
const summary = typeof o.summary === "string" ? o.summary.trim() : "";
|
|
28119
28119
|
if (!rawPath || !summary) continue;
|
|
28120
|
-
const
|
|
28121
|
-
if (!
|
|
28122
|
-
rows.push({ path:
|
|
28120
|
+
const path62 = skip ? normalizeRepoRelativePath(rawPath) || rawPath : resolveChangeSummaryPathAgainstAllowed(rawPath, allowedPaths);
|
|
28121
|
+
if (!path62) continue;
|
|
28122
|
+
rows.push({ path: path62, summary: clampSummaryToAtMostTwoLines(summary) });
|
|
28123
28123
|
}
|
|
28124
28124
|
return rows;
|
|
28125
28125
|
}
|
|
@@ -28631,10 +28631,10 @@ async function killChildProcessTreeGracefully(child, graceMs = ACP_PROCESS_TREE_
|
|
|
28631
28631
|
}
|
|
28632
28632
|
await killProcessTree(pid, "SIGTERM");
|
|
28633
28633
|
if (graceMs <= 0) return;
|
|
28634
|
-
const exited = new Promise((
|
|
28635
|
-
child.once("exit", () =>
|
|
28634
|
+
const exited = new Promise((resolve34) => {
|
|
28635
|
+
child.once("exit", () => resolve34());
|
|
28636
28636
|
});
|
|
28637
|
-
await Promise.race([exited, new Promise((
|
|
28637
|
+
await Promise.race([exited, new Promise((resolve34) => setTimeout(resolve34, graceMs))]);
|
|
28638
28638
|
if (child.exitCode == null && child.signalCode == null) {
|
|
28639
28639
|
await killProcessTree(pid, "SIGKILL");
|
|
28640
28640
|
}
|
|
@@ -28651,8 +28651,8 @@ async function gracefulAcpSubprocessDisconnect(params) {
|
|
|
28651
28651
|
await transport.closeSession?.(sessionId);
|
|
28652
28652
|
} catch {
|
|
28653
28653
|
}
|
|
28654
|
-
await new Promise((
|
|
28655
|
-
const timer = setTimeout(
|
|
28654
|
+
await new Promise((resolve34) => {
|
|
28655
|
+
const timer = setTimeout(resolve34, ACP_GRACEFUL_SESSION_CLOSE_WAIT_MS);
|
|
28656
28656
|
timer.unref?.();
|
|
28657
28657
|
});
|
|
28658
28658
|
}
|
|
@@ -28927,11 +28927,11 @@ function createSdkStdioExtNotificationHandler(options) {
|
|
|
28927
28927
|
// src/agents/acp/clients/sdk/sdk-stdio-permission-request-handshake.ts
|
|
28928
28928
|
function awaitSdkStdioPermissionRequestHandshake(params) {
|
|
28929
28929
|
const { requestId, paramsRecord, pending, onRequest } = params;
|
|
28930
|
-
return new Promise((
|
|
28931
|
-
pending.set(requestId, { resolve:
|
|
28930
|
+
return new Promise((resolve34) => {
|
|
28931
|
+
pending.set(requestId, { resolve: resolve34, params: paramsRecord });
|
|
28932
28932
|
if (onRequest == null) {
|
|
28933
28933
|
pending.delete(requestId);
|
|
28934
|
-
|
|
28934
|
+
resolve34({ outcome: { outcome: "denied" } });
|
|
28935
28935
|
return;
|
|
28936
28936
|
}
|
|
28937
28937
|
try {
|
|
@@ -29441,10 +29441,10 @@ function createSdkStdioInitSettle(child) {
|
|
|
29441
29441
|
forceAcpSubprocessDisconnect(child);
|
|
29442
29442
|
reject(err);
|
|
29443
29443
|
},
|
|
29444
|
-
settleResolve(
|
|
29444
|
+
settleResolve(resolve34, handle) {
|
|
29445
29445
|
if (initSettled) return;
|
|
29446
29446
|
initSettled = true;
|
|
29447
|
-
|
|
29447
|
+
resolve34(handle);
|
|
29448
29448
|
}
|
|
29449
29449
|
};
|
|
29450
29450
|
}
|
|
@@ -29513,7 +29513,7 @@ async function createSdkStdioAcpClient(options) {
|
|
|
29513
29513
|
onFileChange,
|
|
29514
29514
|
stderrCapture
|
|
29515
29515
|
});
|
|
29516
|
-
return new Promise((
|
|
29516
|
+
return new Promise((resolve34, reject) => {
|
|
29517
29517
|
const init = createSdkStdioInitSettle(child);
|
|
29518
29518
|
child.on("error", (err) => {
|
|
29519
29519
|
init.settleReject(reject, new Error(formatSpawnError(err, command[0])));
|
|
@@ -29538,7 +29538,7 @@ async function createSdkStdioAcpClient(options) {
|
|
|
29538
29538
|
protocolVersion: PROTOCOL_VERSION2
|
|
29539
29539
|
});
|
|
29540
29540
|
init.settleResolve(
|
|
29541
|
-
|
|
29541
|
+
resolve34,
|
|
29542
29542
|
createSdkStdioHandle({
|
|
29543
29543
|
child,
|
|
29544
29544
|
sessionId: established.sessionId,
|
|
@@ -30071,7 +30071,7 @@ async function createCursorAcpClient(options) {
|
|
|
30071
30071
|
onFileChange,
|
|
30072
30072
|
stderrCapture
|
|
30073
30073
|
});
|
|
30074
|
-
return new Promise((
|
|
30074
|
+
return new Promise((resolve34, reject) => {
|
|
30075
30075
|
child.on("error", (err) => {
|
|
30076
30076
|
killChildProcessTree(child, "SIGKILL");
|
|
30077
30077
|
reject(new Error(formatSpawnError(err, command[0])));
|
|
@@ -30083,7 +30083,7 @@ async function createCursorAcpClient(options) {
|
|
|
30083
30083
|
sessionCtx,
|
|
30084
30084
|
incomingDeps: { dbgFs, sessionCtx, onSessionUpdate, onRequest }
|
|
30085
30085
|
});
|
|
30086
|
-
|
|
30086
|
+
resolve34(
|
|
30087
30087
|
createCursorAcpHandle({
|
|
30088
30088
|
child,
|
|
30089
30089
|
sessionId: established.sessionId,
|
|
@@ -31289,8 +31289,8 @@ function pathspec(...paths) {
|
|
|
31289
31289
|
cache.set(key, paths);
|
|
31290
31290
|
return key;
|
|
31291
31291
|
}
|
|
31292
|
-
function isPathSpec(
|
|
31293
|
-
return
|
|
31292
|
+
function isPathSpec(path62) {
|
|
31293
|
+
return path62 instanceof String && cache.has(path62);
|
|
31294
31294
|
}
|
|
31295
31295
|
function toPaths(pathSpec) {
|
|
31296
31296
|
return cache.get(pathSpec) || [];
|
|
@@ -31379,8 +31379,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
|
|
|
31379
31379
|
function forEachLineWithContent(input, callback) {
|
|
31380
31380
|
return toLinesWithContent(input, true).map((line) => callback(line));
|
|
31381
31381
|
}
|
|
31382
|
-
function folderExists(
|
|
31383
|
-
return (0, import_file_exists.exists)(
|
|
31382
|
+
function folderExists(path62) {
|
|
31383
|
+
return (0, import_file_exists.exists)(path62, import_file_exists.FOLDER);
|
|
31384
31384
|
}
|
|
31385
31385
|
function append(target, item) {
|
|
31386
31386
|
if (Array.isArray(target)) {
|
|
@@ -31784,8 +31784,8 @@ function checkIsRepoRootTask() {
|
|
|
31784
31784
|
commands,
|
|
31785
31785
|
format: "utf-8",
|
|
31786
31786
|
onError,
|
|
31787
|
-
parser(
|
|
31788
|
-
return /^\.(git)?$/.test(
|
|
31787
|
+
parser(path62) {
|
|
31788
|
+
return /^\.(git)?$/.test(path62.trim());
|
|
31789
31789
|
}
|
|
31790
31790
|
};
|
|
31791
31791
|
}
|
|
@@ -32219,11 +32219,11 @@ function parseGrep(grep) {
|
|
|
32219
32219
|
const paths = /* @__PURE__ */ new Set();
|
|
32220
32220
|
const results = {};
|
|
32221
32221
|
forEachLineWithContent(grep, (input) => {
|
|
32222
|
-
const [
|
|
32223
|
-
paths.add(
|
|
32224
|
-
(results[
|
|
32222
|
+
const [path62, line, preview] = input.split(NULL);
|
|
32223
|
+
paths.add(path62);
|
|
32224
|
+
(results[path62] = results[path62] || []).push({
|
|
32225
32225
|
line: asNumber(line),
|
|
32226
|
-
path:
|
|
32226
|
+
path: path62,
|
|
32227
32227
|
preview
|
|
32228
32228
|
});
|
|
32229
32229
|
});
|
|
@@ -32988,14 +32988,14 @@ var init_hash_object = __esm2({
|
|
|
32988
32988
|
init_task();
|
|
32989
32989
|
}
|
|
32990
32990
|
});
|
|
32991
|
-
function parseInit(bare,
|
|
32991
|
+
function parseInit(bare, path62, text) {
|
|
32992
32992
|
const response = String(text).trim();
|
|
32993
32993
|
let result;
|
|
32994
32994
|
if (result = initResponseRegex.exec(response)) {
|
|
32995
|
-
return new InitSummary(bare,
|
|
32995
|
+
return new InitSummary(bare, path62, false, result[1]);
|
|
32996
32996
|
}
|
|
32997
32997
|
if (result = reInitResponseRegex.exec(response)) {
|
|
32998
|
-
return new InitSummary(bare,
|
|
32998
|
+
return new InitSummary(bare, path62, true, result[1]);
|
|
32999
32999
|
}
|
|
33000
33000
|
let gitDir = "";
|
|
33001
33001
|
const tokens = response.split(" ");
|
|
@@ -33006,7 +33006,7 @@ function parseInit(bare, path55, text) {
|
|
|
33006
33006
|
break;
|
|
33007
33007
|
}
|
|
33008
33008
|
}
|
|
33009
|
-
return new InitSummary(bare,
|
|
33009
|
+
return new InitSummary(bare, path62, /^re/i.test(response), gitDir);
|
|
33010
33010
|
}
|
|
33011
33011
|
var InitSummary;
|
|
33012
33012
|
var initResponseRegex;
|
|
@@ -33015,9 +33015,9 @@ var init_InitSummary = __esm2({
|
|
|
33015
33015
|
"src/lib/responses/InitSummary.ts"() {
|
|
33016
33016
|
"use strict";
|
|
33017
33017
|
InitSummary = class {
|
|
33018
|
-
constructor(bare,
|
|
33018
|
+
constructor(bare, path62, existing, gitDir) {
|
|
33019
33019
|
this.bare = bare;
|
|
33020
|
-
this.path =
|
|
33020
|
+
this.path = path62;
|
|
33021
33021
|
this.existing = existing;
|
|
33022
33022
|
this.gitDir = gitDir;
|
|
33023
33023
|
}
|
|
@@ -33029,7 +33029,7 @@ var init_InitSummary = __esm2({
|
|
|
33029
33029
|
function hasBareCommand(command) {
|
|
33030
33030
|
return command.includes(bareCommand);
|
|
33031
33031
|
}
|
|
33032
|
-
function initTask(bare = false,
|
|
33032
|
+
function initTask(bare = false, path62, customArgs) {
|
|
33033
33033
|
const commands = ["init", ...customArgs];
|
|
33034
33034
|
if (bare && !hasBareCommand(commands)) {
|
|
33035
33035
|
commands.splice(1, 0, bareCommand);
|
|
@@ -33038,7 +33038,7 @@ function initTask(bare = false, path55, customArgs) {
|
|
|
33038
33038
|
commands,
|
|
33039
33039
|
format: "utf-8",
|
|
33040
33040
|
parser(text) {
|
|
33041
|
-
return parseInit(commands.includes("--bare"),
|
|
33041
|
+
return parseInit(commands.includes("--bare"), path62, text);
|
|
33042
33042
|
}
|
|
33043
33043
|
};
|
|
33044
33044
|
}
|
|
@@ -33854,12 +33854,12 @@ var init_FileStatusSummary = __esm2({
|
|
|
33854
33854
|
"use strict";
|
|
33855
33855
|
fromPathRegex = /^(.+)\0(.+)$/;
|
|
33856
33856
|
FileStatusSummary = class {
|
|
33857
|
-
constructor(
|
|
33858
|
-
this.path =
|
|
33857
|
+
constructor(path62, index, working_dir) {
|
|
33858
|
+
this.path = path62;
|
|
33859
33859
|
this.index = index;
|
|
33860
33860
|
this.working_dir = working_dir;
|
|
33861
33861
|
if (index === "R" || working_dir === "R") {
|
|
33862
|
-
const detail = fromPathRegex.exec(
|
|
33862
|
+
const detail = fromPathRegex.exec(path62) || [null, path62, path62];
|
|
33863
33863
|
this.from = detail[2] || "";
|
|
33864
33864
|
this.path = detail[1] || "";
|
|
33865
33865
|
}
|
|
@@ -33890,14 +33890,14 @@ function splitLine(result, lineStr) {
|
|
|
33890
33890
|
default:
|
|
33891
33891
|
return;
|
|
33892
33892
|
}
|
|
33893
|
-
function data(index, workingDir,
|
|
33893
|
+
function data(index, workingDir, path62) {
|
|
33894
33894
|
const raw = `${index}${workingDir}`;
|
|
33895
33895
|
const handler = parsers6.get(raw);
|
|
33896
33896
|
if (handler) {
|
|
33897
|
-
handler(result,
|
|
33897
|
+
handler(result, path62);
|
|
33898
33898
|
}
|
|
33899
33899
|
if (raw !== "##" && raw !== "!!") {
|
|
33900
|
-
result.files.push(new FileStatusSummary(
|
|
33900
|
+
result.files.push(new FileStatusSummary(path62, index, workingDir));
|
|
33901
33901
|
}
|
|
33902
33902
|
}
|
|
33903
33903
|
}
|
|
@@ -34206,9 +34206,9 @@ var init_simple_git_api = __esm2({
|
|
|
34206
34206
|
next
|
|
34207
34207
|
);
|
|
34208
34208
|
}
|
|
34209
|
-
hashObject(
|
|
34209
|
+
hashObject(path62, write) {
|
|
34210
34210
|
return this._runTask(
|
|
34211
|
-
hashObjectTask(
|
|
34211
|
+
hashObjectTask(path62, write === true),
|
|
34212
34212
|
trailingFunctionArgument(arguments)
|
|
34213
34213
|
);
|
|
34214
34214
|
}
|
|
@@ -34561,8 +34561,8 @@ var init_branch = __esm2({
|
|
|
34561
34561
|
}
|
|
34562
34562
|
});
|
|
34563
34563
|
function toPath(input) {
|
|
34564
|
-
const
|
|
34565
|
-
return
|
|
34564
|
+
const path62 = input.trim().replace(/^["']|["']$/g, "");
|
|
34565
|
+
return path62 && normalize3(path62);
|
|
34566
34566
|
}
|
|
34567
34567
|
var parseCheckIgnore;
|
|
34568
34568
|
var init_CheckIgnore = __esm2({
|
|
@@ -34876,8 +34876,8 @@ __export2(sub_module_exports, {
|
|
|
34876
34876
|
subModuleTask: () => subModuleTask,
|
|
34877
34877
|
updateSubModuleTask: () => updateSubModuleTask
|
|
34878
34878
|
});
|
|
34879
|
-
function addSubModuleTask(repo,
|
|
34880
|
-
return subModuleTask(["add", repo,
|
|
34879
|
+
function addSubModuleTask(repo, path62) {
|
|
34880
|
+
return subModuleTask(["add", repo, path62]);
|
|
34881
34881
|
}
|
|
34882
34882
|
function initSubModuleTask(customArgs) {
|
|
34883
34883
|
return subModuleTask(["init", ...customArgs]);
|
|
@@ -35210,8 +35210,8 @@ var require_git = __commonJS2({
|
|
|
35210
35210
|
}
|
|
35211
35211
|
return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
|
|
35212
35212
|
};
|
|
35213
|
-
Git2.prototype.submoduleAdd = function(repo,
|
|
35214
|
-
return this._runTask(addSubModuleTask2(repo,
|
|
35213
|
+
Git2.prototype.submoduleAdd = function(repo, path62, then) {
|
|
35214
|
+
return this._runTask(addSubModuleTask2(repo, path62), trailingFunctionArgument2(arguments));
|
|
35215
35215
|
};
|
|
35216
35216
|
Git2.prototype.submoduleUpdate = function(args, then) {
|
|
35217
35217
|
return this._runTask(
|
|
@@ -35848,8 +35848,8 @@ async function runGitTask(fn) {
|
|
|
35848
35848
|
}
|
|
35849
35849
|
async function yieldToEventLoop2() {
|
|
35850
35850
|
throwIfGitShutdownRequested();
|
|
35851
|
-
await new Promise((
|
|
35852
|
-
setImmediate(
|
|
35851
|
+
await new Promise((resolve34) => {
|
|
35852
|
+
setImmediate(resolve34);
|
|
35853
35853
|
});
|
|
35854
35854
|
throwIfGitShutdownRequested();
|
|
35855
35855
|
}
|
|
@@ -35889,11 +35889,8 @@ function cliSimpleGit(baseDir) {
|
|
|
35889
35889
|
async function getRemoteOriginUrl(gitDir) {
|
|
35890
35890
|
try {
|
|
35891
35891
|
const git = cliSimpleGit(gitDir);
|
|
35892
|
-
const
|
|
35893
|
-
|
|
35894
|
-
const origin = list.find((r) => r.name === "origin");
|
|
35895
|
-
if (!origin?.refs?.fetch && !origin?.refs?.push) return "";
|
|
35896
|
-
return (origin.refs.fetch ?? origin.refs.push ?? "").trim();
|
|
35892
|
+
const out = await git.raw(["config", "--get", "remote.origin.url"]);
|
|
35893
|
+
return String(out).trim();
|
|
35897
35894
|
} catch {
|
|
35898
35895
|
return "";
|
|
35899
35896
|
}
|
|
@@ -35909,6 +35906,12 @@ async function isGitRepoDirectory(dirPath) {
|
|
|
35909
35906
|
}
|
|
35910
35907
|
|
|
35911
35908
|
// src/git/discover-repos.ts
|
|
35909
|
+
var GIT_DISCOVER_YIELD_EVERY = 32;
|
|
35910
|
+
async function yieldGitDiscoverWork(entryCount) {
|
|
35911
|
+
if (entryCount > 0 && entryCount % GIT_DISCOVER_YIELD_EVERY === 0) {
|
|
35912
|
+
await yieldToEventLoop();
|
|
35913
|
+
}
|
|
35914
|
+
}
|
|
35912
35915
|
async function discoverGitRepos(cwd = getBridgeRoot()) {
|
|
35913
35916
|
const result = [];
|
|
35914
35917
|
const cwdResolved = path17.resolve(cwd);
|
|
@@ -35922,7 +35925,9 @@ async function discoverGitRepos(cwd = getBridgeRoot()) {
|
|
|
35922
35925
|
} catch {
|
|
35923
35926
|
return result;
|
|
35924
35927
|
}
|
|
35925
|
-
for (
|
|
35928
|
+
for (let i = 0; i < entries.length; i++) {
|
|
35929
|
+
await yieldGitDiscoverWork(i + 1);
|
|
35930
|
+
const ent = entries[i];
|
|
35926
35931
|
if (!ent.isDirectory()) continue;
|
|
35927
35932
|
const childPath = path17.join(cwdResolved, ent.name);
|
|
35928
35933
|
if (await isGitRepoDirectory(childPath)) {
|
|
@@ -35935,6 +35940,7 @@ async function discoverGitRepos(cwd = getBridgeRoot()) {
|
|
|
35935
35940
|
async function discoverGitReposUnderRoot(rootPath) {
|
|
35936
35941
|
const root = path17.resolve(rootPath);
|
|
35937
35942
|
const roots = [];
|
|
35943
|
+
let walkEntries = 0;
|
|
35938
35944
|
async function walk(dir) {
|
|
35939
35945
|
if (await isGitRepoDirectory(dir)) {
|
|
35940
35946
|
roots.push(path17.resolve(dir));
|
|
@@ -35946,7 +35952,9 @@ async function discoverGitReposUnderRoot(rootPath) {
|
|
|
35946
35952
|
} catch {
|
|
35947
35953
|
return;
|
|
35948
35954
|
}
|
|
35949
|
-
for (
|
|
35955
|
+
for (let i = 0; i < entries.length; i++) {
|
|
35956
|
+
await yieldGitDiscoverWork(++walkEntries);
|
|
35957
|
+
const ent = entries[i];
|
|
35950
35958
|
if (!ent.isDirectory() || ent.name === ".git") continue;
|
|
35951
35959
|
await walk(path17.join(dir, ent.name));
|
|
35952
35960
|
}
|
|
@@ -35954,7 +35962,9 @@ async function discoverGitReposUnderRoot(rootPath) {
|
|
|
35954
35962
|
await walk(root);
|
|
35955
35963
|
const uniq = [...new Set(roots)];
|
|
35956
35964
|
const out = [];
|
|
35957
|
-
for (
|
|
35965
|
+
for (let i = 0; i < uniq.length; i++) {
|
|
35966
|
+
await yieldGitDiscoverWork(i + 1);
|
|
35967
|
+
const p = uniq[i];
|
|
35958
35968
|
const remoteUrl = await getRemoteOriginUrl(p);
|
|
35959
35969
|
out.push({ absolutePath: p, remoteUrl });
|
|
35960
35970
|
}
|
|
@@ -36174,9 +36184,9 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
|
|
|
36174
36184
|
// src/agents/acp/put-summarize-change-summaries.ts
|
|
36175
36185
|
async function putEncryptedChangeSummaryRows(params) {
|
|
36176
36186
|
const base = params.apiBaseUrl.replace(/\/+$/, "");
|
|
36177
|
-
const entries = params.rows.map(({ path:
|
|
36187
|
+
const entries = params.rows.map(({ path: path62, summary }) => {
|
|
36178
36188
|
const enc = params.e2ee.encryptFields({ summary }, ["summary"]);
|
|
36179
|
-
return { path:
|
|
36189
|
+
return { path: path62, summary: JSON.stringify(enc) };
|
|
36180
36190
|
});
|
|
36181
36191
|
const res = await fetch(
|
|
36182
36192
|
`${base}/internal/sessions/${encodeURIComponent(params.sessionId)}/follow-ups/summarize-changes`,
|
|
@@ -36920,503 +36930,436 @@ function allocateDirNameForLauncherCwd(layout, bridgeRootPath2) {
|
|
|
36920
36930
|
return name;
|
|
36921
36931
|
}
|
|
36922
36932
|
|
|
36923
|
-
// src/worktrees/
|
|
36924
|
-
import * as
|
|
36925
|
-
|
|
36926
|
-
|
|
36927
|
-
|
|
36928
|
-
|
|
36929
|
-
|
|
36933
|
+
// src/worktrees/manager/session-worktree-cache.ts
|
|
36934
|
+
import * as path21 from "node:path";
|
|
36935
|
+
var SessionWorktreeCache = class {
|
|
36936
|
+
sessionRepoCheckoutPaths = /* @__PURE__ */ new Map();
|
|
36937
|
+
sessionParentPathBySession = /* @__PURE__ */ new Map();
|
|
36938
|
+
sessionWorkingTreeRelRootBySession = /* @__PURE__ */ new Map();
|
|
36939
|
+
remember(sessionId, binding) {
|
|
36940
|
+
const paths = binding.repoCheckoutPaths.map((p) => path21.resolve(p));
|
|
36941
|
+
this.sessionRepoCheckoutPaths.set(sessionId, paths);
|
|
36942
|
+
this.sessionParentPathBySession.set(sessionId, path21.resolve(binding.sessionParentPath));
|
|
36943
|
+
this.sessionWorkingTreeRelRootBySession.set(sessionId, path21.resolve(binding.workingTreeRelRoot));
|
|
36944
|
+
}
|
|
36945
|
+
clearSession(sessionId) {
|
|
36946
|
+
const paths = this.sessionRepoCheckoutPaths.get(sessionId);
|
|
36947
|
+
this.sessionRepoCheckoutPaths.delete(sessionId);
|
|
36948
|
+
this.sessionParentPathBySession.delete(sessionId);
|
|
36949
|
+
this.sessionWorkingTreeRelRootBySession.delete(sessionId);
|
|
36950
|
+
return paths;
|
|
36951
|
+
}
|
|
36952
|
+
getSessionParentPath(sessionId) {
|
|
36953
|
+
return this.sessionParentPathBySession.get(sessionId);
|
|
36954
|
+
}
|
|
36955
|
+
getWorkingTreeRelRoot(sessionId) {
|
|
36956
|
+
return this.sessionWorkingTreeRelRootBySession.get(sessionId) ?? null;
|
|
36957
|
+
}
|
|
36958
|
+
hasSession(sessionId) {
|
|
36959
|
+
return this.sessionParentPathBySession.has(sessionId);
|
|
36960
|
+
}
|
|
36961
|
+
getRepoCheckoutPaths(sessionId) {
|
|
36962
|
+
const paths = this.sessionRepoCheckoutPaths.get(sessionId);
|
|
36963
|
+
return paths?.length ? [...paths] : void 0;
|
|
36964
|
+
}
|
|
36965
|
+
getRepoCheckoutPathsRef(sessionId) {
|
|
36966
|
+
const paths = this.sessionRepoCheckoutPaths.get(sessionId);
|
|
36967
|
+
return paths?.length ? paths : void 0;
|
|
36968
|
+
}
|
|
36969
|
+
isLegacyNestedLayout(sessionId) {
|
|
36970
|
+
const parent = this.sessionParentPathBySession.get(sessionId);
|
|
36971
|
+
const relRoot = this.sessionWorkingTreeRelRootBySession.get(sessionId);
|
|
36972
|
+
if (!parent || !relRoot) return false;
|
|
36973
|
+
return path21.resolve(parent) !== path21.resolve(relRoot);
|
|
36974
|
+
}
|
|
36975
|
+
};
|
|
36930
36976
|
|
|
36931
|
-
// src/worktrees/
|
|
36932
|
-
|
|
36933
|
-
|
|
36934
|
-
|
|
36935
|
-
|
|
36936
|
-
|
|
36937
|
-
|
|
36938
|
-
|
|
36939
|
-
"Carthage",
|
|
36940
|
-
"DerivedData",
|
|
36941
|
-
".yarn",
|
|
36942
|
-
".pnpm-store",
|
|
36943
|
-
"dist",
|
|
36944
|
-
"build",
|
|
36945
|
-
".next",
|
|
36946
|
-
"coverage",
|
|
36947
|
-
".turbo",
|
|
36948
|
-
".cache",
|
|
36949
|
-
"target",
|
|
36950
|
-
"__pycache__"
|
|
36951
|
-
]);
|
|
36952
|
-
function shouldSkipDiskWalkEntry(name) {
|
|
36953
|
-
return name.startsWith(".") || SKIP_DISK_WALK_DIR_NAMES.has(name);
|
|
36977
|
+
// src/worktrees/manager/session-worktree-manager-context.ts
|
|
36978
|
+
function createSessionWorktreeManagerContext(options) {
|
|
36979
|
+
return {
|
|
36980
|
+
worktreesRootPath: options.worktreesRootPath,
|
|
36981
|
+
log: options.log,
|
|
36982
|
+
cache: new SessionWorktreeCache(),
|
|
36983
|
+
layout: loadWorktreeLayout()
|
|
36984
|
+
};
|
|
36954
36985
|
}
|
|
36955
36986
|
|
|
36956
|
-
// src/
|
|
36957
|
-
|
|
36958
|
-
|
|
36959
|
-
|
|
36960
|
-
|
|
36961
|
-
|
|
36962
|
-
if (
|
|
36963
|
-
|
|
36964
|
-
|
|
36965
|
-
|
|
36966
|
-
|
|
36967
|
-
|
|
36968
|
-
await
|
|
36969
|
-
|
|
36970
|
-
|
|
36971
|
-
|
|
36987
|
+
// src/git/branches/commit-and-push.ts
|
|
36988
|
+
async function gitCommitAllIfDirty(repoDir, message, options) {
|
|
36989
|
+
const g = cliSimpleGit(repoDir);
|
|
36990
|
+
const st = await g.status();
|
|
36991
|
+
if (!st.files?.length) return;
|
|
36992
|
+
const branch = options.branch.trim();
|
|
36993
|
+
if (!branch) {
|
|
36994
|
+
throw new Error("Branch name is required");
|
|
36995
|
+
}
|
|
36996
|
+
const branches = await g.branchLocal();
|
|
36997
|
+
const localNames = new Set(branches.all.map((b) => b.replace(/^\*\s*/, "").trim()));
|
|
36998
|
+
if (!localNames.has(branch)) {
|
|
36999
|
+
await g.checkoutLocalBranch(branch);
|
|
37000
|
+
} else {
|
|
37001
|
+
await g.checkout(branch);
|
|
37002
|
+
}
|
|
37003
|
+
await g.add(".");
|
|
37004
|
+
await g.commit(message);
|
|
37005
|
+
if (options.push) {
|
|
37006
|
+
await g.push(["-u", "origin", branch]);
|
|
36972
37007
|
}
|
|
36973
37008
|
}
|
|
36974
37009
|
|
|
36975
|
-
// src/worktrees/
|
|
36976
|
-
async function
|
|
36977
|
-
const
|
|
36978
|
-
|
|
36979
|
-
|
|
36980
|
-
|
|
36981
|
-
if (await isGitDir(dir)) {
|
|
36982
|
-
out.push(path22.resolve(dir));
|
|
36983
|
-
return;
|
|
36984
|
-
}
|
|
36985
|
-
let entries;
|
|
36986
|
-
try {
|
|
36987
|
-
entries = await fs19.promises.readdir(dir, { withFileTypes: true });
|
|
36988
|
-
} catch {
|
|
36989
|
-
return;
|
|
36990
|
-
}
|
|
36991
|
-
for (const e of entries) {
|
|
36992
|
-
if (shouldSkipDiskWalkEntry(e.name)) continue;
|
|
36993
|
-
const full = path22.join(dir, e.name);
|
|
36994
|
-
if (!e.isDirectory()) continue;
|
|
36995
|
-
await walk(full);
|
|
37010
|
+
// src/worktrees/commit-session-worktrees.ts
|
|
37011
|
+
async function commitSessionWorktrees(options) {
|
|
37012
|
+
const { paths, branch, message, push } = options;
|
|
37013
|
+
try {
|
|
37014
|
+
for (const wt of paths) {
|
|
37015
|
+
await gitCommitAllIfDirty(wt, message, { push, branch });
|
|
36996
37016
|
}
|
|
36997
|
-
|
|
36998
|
-
|
|
36999
|
-
|
|
37017
|
+
return { ok: true };
|
|
37018
|
+
} catch (e) {
|
|
37019
|
+
const err = e instanceof Error ? e.message : String(e);
|
|
37020
|
+
return { ok: false, error: err };
|
|
37021
|
+
}
|
|
37000
37022
|
}
|
|
37001
|
-
|
|
37002
|
-
|
|
37003
|
-
|
|
37004
|
-
const
|
|
37005
|
-
|
|
37006
|
-
|
|
37007
|
-
|
|
37008
|
-
|
|
37009
|
-
|
|
37010
|
-
|
|
37011
|
-
|
|
37012
|
-
}
|
|
37013
|
-
for (const e of entries) {
|
|
37014
|
-
if (shouldSkipDiskWalkEntry(e.name)) continue;
|
|
37015
|
-
const full = path22.join(dir, e.name);
|
|
37016
|
-
if (!e.isDirectory()) continue;
|
|
37017
|
-
if (e.name === sessionId) {
|
|
37018
|
-
if (await isGitDir(full)) out.push(path22.resolve(full));
|
|
37019
|
-
continue;
|
|
37020
|
-
}
|
|
37021
|
-
if (await isGitDir(full)) continue;
|
|
37022
|
-
await walk(full, depth + 1);
|
|
37023
|
-
}
|
|
37024
|
-
};
|
|
37025
|
-
await walk(root, 0);
|
|
37026
|
-
return { paths: out, entriesVisited: state.entries };
|
|
37023
|
+
|
|
37024
|
+
// src/worktrees/manager/git/commit-session-worktree.ts
|
|
37025
|
+
async function commitSessionWorktree(cache2, params) {
|
|
37026
|
+
const paths = cache2.getRepoCheckoutPathsRef(params.sessionId);
|
|
37027
|
+
const targets = paths?.length ? paths : [getBridgeRoot()];
|
|
37028
|
+
return commitSessionWorktrees({
|
|
37029
|
+
paths: targets,
|
|
37030
|
+
branch: params.branch,
|
|
37031
|
+
message: params.message,
|
|
37032
|
+
push: params.push
|
|
37033
|
+
});
|
|
37027
37034
|
}
|
|
37028
37035
|
|
|
37029
|
-
// src/
|
|
37030
|
-
|
|
37031
|
-
|
|
37032
|
-
const bridge = path23.resolve(bridgeRoot);
|
|
37033
|
-
const preferred = getLauncherDirNameIfPresent(layout, bridgeRoot);
|
|
37034
|
-
const relevant = layout.launcherCwds.filter((e) => {
|
|
37035
|
-
const entry = path23.resolve(e.absolutePath);
|
|
37036
|
-
return bridge === entry || bridge.startsWith(entry + path23.sep) || entry.startsWith(bridge + path23.sep);
|
|
37037
|
-
}).sort((a, b) => path23.resolve(b.absolutePath).length - path23.resolve(a.absolutePath).length).map((e) => e.dirName).filter((name) => keysOnDisk.includes(name));
|
|
37038
|
-
const ordered = [];
|
|
37039
|
-
const seen = /* @__PURE__ */ new Set();
|
|
37040
|
-
const add = (k) => {
|
|
37041
|
-
if (seen.has(k) || !keysOnDisk.includes(k)) return;
|
|
37042
|
-
seen.add(k);
|
|
37043
|
-
ordered.push(k);
|
|
37044
|
-
};
|
|
37045
|
-
if (preferred) add(preferred);
|
|
37046
|
-
for (const k of relevant) add(k);
|
|
37047
|
-
if (relevant.length > 0) {
|
|
37048
|
-
return ordered;
|
|
37049
|
-
}
|
|
37050
|
-
for (const k of keysOnDisk) add(k);
|
|
37051
|
-
return ordered;
|
|
37036
|
+
// src/git/changes/parse/normalize-git-diff-path.ts
|
|
37037
|
+
function normalizeGitDiffPath(path62) {
|
|
37038
|
+
return path62.replace(/\\/g, "/").replace(/\/ +/g, "/").trim();
|
|
37052
37039
|
}
|
|
37053
37040
|
|
|
37054
|
-
// src/
|
|
37055
|
-
|
|
37056
|
-
|
|
37057
|
-
|
|
37058
|
-
|
|
37059
|
-
|
|
37060
|
-
st = await fs20.promises.stat(sessionDir);
|
|
37061
|
-
} catch {
|
|
37041
|
+
// src/git/changes/parse/expand-git-rename-numstat-path.ts
|
|
37042
|
+
function expandGitRenameNumstatPath(rawPath) {
|
|
37043
|
+
const open2 = rawPath.indexOf("{");
|
|
37044
|
+
const arrow = rawPath.indexOf("=>");
|
|
37045
|
+
const close = rawPath.lastIndexOf("}");
|
|
37046
|
+
if (open2 === -1 || arrow === -1 || close === -1 || open2 > arrow || arrow > close) {
|
|
37062
37047
|
return null;
|
|
37063
37048
|
}
|
|
37064
|
-
|
|
37065
|
-
const
|
|
37066
|
-
|
|
37067
|
-
const
|
|
37049
|
+
const prefix = rawPath.slice(0, open2);
|
|
37050
|
+
const suffix = rawPath.slice(close + 1);
|
|
37051
|
+
const oldPart = rawPath.slice(open2 + 1, arrow).trim();
|
|
37052
|
+
const newPart = rawPath.slice(arrow + 2, close).trim();
|
|
37068
37053
|
return {
|
|
37069
|
-
|
|
37070
|
-
|
|
37071
|
-
repoCheckoutPaths: worktreePaths
|
|
37054
|
+
oldPath: normalizeGitDiffPath(`${prefix}${oldPart}${suffix}`),
|
|
37055
|
+
newPath: normalizeGitDiffPath(`${prefix}${newPart}${suffix}`)
|
|
37072
37056
|
};
|
|
37073
37057
|
}
|
|
37058
|
+
function isGitRenameNumstatPath(rawPath) {
|
|
37059
|
+
return expandGitRenameNumstatPath(rawPath) != null;
|
|
37060
|
+
}
|
|
37074
37061
|
|
|
37075
|
-
// src/
|
|
37076
|
-
|
|
37077
|
-
const
|
|
37078
|
-
const
|
|
37079
|
-
|
|
37080
|
-
|
|
37081
|
-
|
|
37082
|
-
|
|
37083
|
-
|
|
37084
|
-
|
|
37085
|
-
|
|
37086
|
-
|
|
37087
|
-
|
|
37088
|
-
|
|
37089
|
-
|
|
37090
|
-
|
|
37091
|
-
|
|
37092
|
-
|
|
37093
|
-
|
|
37094
|
-
|
|
37095
|
-
} catch {
|
|
37096
|
-
continue;
|
|
37062
|
+
// src/git/changes/parse/parse-name-status-entries.ts
|
|
37063
|
+
function parseNameStatusEntries(lines) {
|
|
37064
|
+
const entries = [];
|
|
37065
|
+
for (const line of lines) {
|
|
37066
|
+
if (!line.trim()) continue;
|
|
37067
|
+
const tabParts = line.split(" ");
|
|
37068
|
+
if (tabParts.length < 2) continue;
|
|
37069
|
+
const status = tabParts[0].trim();
|
|
37070
|
+
const code = status[0];
|
|
37071
|
+
if (code === "A") {
|
|
37072
|
+
entries.push({ pathInRepo: normalizeGitDiffPath(tabParts[tabParts.length - 1]), change: "added" });
|
|
37073
|
+
} else if (code === "D") {
|
|
37074
|
+
entries.push({ pathInRepo: normalizeGitDiffPath(tabParts[tabParts.length - 1]), change: "removed" });
|
|
37075
|
+
} else if (code === "R" || code === "C") {
|
|
37076
|
+
if (tabParts.length >= 3) {
|
|
37077
|
+
entries.push({
|
|
37078
|
+
pathInRepo: normalizeGitDiffPath(tabParts[tabParts.length - 1]),
|
|
37079
|
+
oldPathInRepo: normalizeGitDiffPath(tabParts[tabParts.length - 2]),
|
|
37080
|
+
change: "moved"
|
|
37081
|
+
});
|
|
37097
37082
|
}
|
|
37098
|
-
|
|
37099
|
-
|
|
37100
|
-
}
|
|
37101
|
-
} catch {
|
|
37102
|
-
return null;
|
|
37103
|
-
}
|
|
37104
|
-
const keys = layoutKeysForBridgeRoot(layout, bridgeRoot, keysOnDisk);
|
|
37105
|
-
for (const key of keys) {
|
|
37106
|
-
if (isCliImmediateShutdownRequested()) return null;
|
|
37107
|
-
await yieldToEventLoop();
|
|
37108
|
-
const layoutRoot = path25.join(worktreesRootPath, key);
|
|
37109
|
-
let layoutSt;
|
|
37110
|
-
try {
|
|
37111
|
-
layoutSt = await fs21.promises.stat(layoutRoot);
|
|
37112
|
-
} catch {
|
|
37113
|
-
continue;
|
|
37114
|
-
}
|
|
37115
|
-
if (!layoutSt.isDirectory()) continue;
|
|
37116
|
-
const sessionDir = path25.join(layoutRoot, sid);
|
|
37117
|
-
const nested = await tryBindingFromSessionDirectory(sessionDir);
|
|
37118
|
-
if (nested) return nested;
|
|
37119
|
-
const legacy = await collectWorktreeRootsNamed(layoutRoot, sid);
|
|
37120
|
-
if (legacy.paths.length > 0) {
|
|
37121
|
-
const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacy.paths) ?? path25.resolve(legacy.paths[0]);
|
|
37122
|
-
return {
|
|
37123
|
-
sessionParentPath: path25.resolve(isolated),
|
|
37124
|
-
workingTreeRelRoot: path25.resolve(layoutRoot),
|
|
37125
|
-
repoCheckoutPaths: legacy.paths.map((p) => path25.resolve(p))
|
|
37126
|
-
};
|
|
37083
|
+
} else if (code === "M" || code === "U" || code === "T") {
|
|
37084
|
+
entries.push({ pathInRepo: normalizeGitDiffPath(tabParts[tabParts.length - 1]), change: "modified" });
|
|
37127
37085
|
}
|
|
37128
37086
|
}
|
|
37129
|
-
return
|
|
37087
|
+
return entries;
|
|
37130
37088
|
}
|
|
37131
|
-
|
|
37132
|
-
|
|
37133
|
-
|
|
37134
|
-
|
|
37135
|
-
|
|
37136
|
-
// src/worktrees/discovery/discover-legacy-binding-ascending-from-checkout.ts
|
|
37137
|
-
import * as path26 from "node:path";
|
|
37138
|
-
async function discoverLegacyBindingAscendingFromCheckout(sessionId, checkoutPath) {
|
|
37139
|
-
const sid = sessionId.trim();
|
|
37140
|
-
if (!sid) return null;
|
|
37141
|
-
const hintR = path26.resolve(checkoutPath);
|
|
37142
|
-
let best = null;
|
|
37143
|
-
let cur = path26.dirname(hintR);
|
|
37144
|
-
for (let i = 0; i < 40; i++) {
|
|
37145
|
-
if (isCliImmediateShutdownRequested()) return best;
|
|
37146
|
-
await yieldToEventLoop();
|
|
37147
|
-
const paths = await collectWorktreeRootsNamed(cur, sid);
|
|
37148
|
-
if (paths.paths.some((p) => path26.resolve(p) === hintR)) {
|
|
37149
|
-
const isolated = resolveIsolatedSessionParentPathFromCheckouts(paths.paths) ?? path26.resolve(paths.paths[0]);
|
|
37150
|
-
best = {
|
|
37151
|
-
sessionParentPath: path26.resolve(isolated),
|
|
37152
|
-
workingTreeRelRoot: path26.resolve(cur),
|
|
37153
|
-
repoCheckoutPaths: paths.paths.map((p) => path26.resolve(p))
|
|
37154
|
-
};
|
|
37155
|
-
}
|
|
37156
|
-
const next = path26.dirname(cur);
|
|
37157
|
-
if (next === cur) break;
|
|
37158
|
-
cur = next;
|
|
37089
|
+
function parseNameStatusLines(lines) {
|
|
37090
|
+
const m = /* @__PURE__ */ new Map();
|
|
37091
|
+
for (const entry of parseNameStatusEntries(lines)) {
|
|
37092
|
+
m.set(entry.pathInRepo, entry.change);
|
|
37159
37093
|
}
|
|
37160
|
-
return
|
|
37094
|
+
return m;
|
|
37161
37095
|
}
|
|
37162
37096
|
|
|
37163
|
-
// src/
|
|
37164
|
-
|
|
37165
|
-
const
|
|
37166
|
-
|
|
37167
|
-
|
|
37168
|
-
|
|
37169
|
-
|
|
37170
|
-
|
|
37171
|
-
|
|
37172
|
-
|
|
37173
|
-
|
|
37174
|
-
|
|
37175
|
-
|
|
37176
|
-
|
|
37097
|
+
// src/git/changes/parse/parse-numstat-entries.ts
|
|
37098
|
+
function parseNumstatCounts(parts) {
|
|
37099
|
+
const [a, d] = parts;
|
|
37100
|
+
return {
|
|
37101
|
+
additions: a === "-" ? 0 : parseInt(String(a), 10) || 0,
|
|
37102
|
+
deletions: d === "-" ? 0 : parseInt(String(d), 10) || 0
|
|
37103
|
+
};
|
|
37104
|
+
}
|
|
37105
|
+
function parseNumstatLine(line) {
|
|
37106
|
+
const parts = line.split(" ");
|
|
37107
|
+
if (parts.length < 3) return null;
|
|
37108
|
+
const rawPath = parts[parts.length - 1];
|
|
37109
|
+
const { additions, deletions } = parseNumstatCounts(parts);
|
|
37110
|
+
const expanded = expandGitRenameNumstatPath(rawPath);
|
|
37111
|
+
const pathInRepo = normalizeGitDiffPath(expanded?.newPath ?? rawPath);
|
|
37112
|
+
return {
|
|
37113
|
+
pathInRepo,
|
|
37114
|
+
additions,
|
|
37115
|
+
deletions,
|
|
37116
|
+
...expanded ? { oldPathInRepo: expanded.oldPath } : {}
|
|
37117
|
+
};
|
|
37118
|
+
}
|
|
37119
|
+
function parseNumstatEntries(lines) {
|
|
37120
|
+
const m = /* @__PURE__ */ new Map();
|
|
37121
|
+
for (const line of lines) {
|
|
37122
|
+
if (!line.trim()) continue;
|
|
37123
|
+
const parts = line.split(" ");
|
|
37124
|
+
if (parts.length < 3) continue;
|
|
37125
|
+
const rawPath = parts[parts.length - 1];
|
|
37126
|
+
const expanded = expandGitRenameNumstatPath(rawPath);
|
|
37127
|
+
const entry = parseNumstatLine(line);
|
|
37128
|
+
if (!entry) continue;
|
|
37129
|
+
const existing = m.get(entry.pathInRepo);
|
|
37130
|
+
if (!existing || expanded) {
|
|
37131
|
+
m.set(entry.pathInRepo, entry);
|
|
37177
37132
|
}
|
|
37178
|
-
return direct;
|
|
37179
37133
|
}
|
|
37180
|
-
|
|
37181
|
-
|
|
37182
|
-
|
|
37134
|
+
return m;
|
|
37135
|
+
}
|
|
37136
|
+
function parseNumstat(lines) {
|
|
37137
|
+
const m = /* @__PURE__ */ new Map();
|
|
37138
|
+
for (const [path62, entry] of parseNumstatEntries(lines)) {
|
|
37139
|
+
m.set(path62, { additions: entry.additions, deletions: entry.deletions });
|
|
37183
37140
|
}
|
|
37184
|
-
|
|
37141
|
+
return m;
|
|
37142
|
+
}
|
|
37143
|
+
|
|
37144
|
+
// src/git/changes/parse/numstat-from-git-no-index.ts
|
|
37145
|
+
async function numstatFromGitNoIndex(g, pathInRepo) {
|
|
37146
|
+
const devNull = process.platform === "win32" ? "NUL" : "/dev/null";
|
|
37185
37147
|
try {
|
|
37186
|
-
|
|
37148
|
+
const out = await g.raw(["diff", "--numstat", "--no-index", "--", devNull, pathInRepo]);
|
|
37149
|
+
const first2 = String(out).split("\n").find((l) => l.trim()) ?? "";
|
|
37150
|
+
const parsed = parseNumstatLine(first2);
|
|
37151
|
+
if (!parsed) return null;
|
|
37152
|
+
return { additions: parsed.additions, deletions: parsed.deletions };
|
|
37187
37153
|
} catch {
|
|
37188
37154
|
return null;
|
|
37189
37155
|
}
|
|
37190
|
-
if (!st.isDirectory()) return null;
|
|
37191
|
-
const legacyPaths = await collectWorktreeRootsNamed(hint, sid);
|
|
37192
|
-
if (legacyPaths.paths.length === 0) return null;
|
|
37193
|
-
const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths.paths) ?? path27.resolve(legacyPaths.paths[0]);
|
|
37194
|
-
return {
|
|
37195
|
-
sessionParentPath: path27.resolve(isolated),
|
|
37196
|
-
workingTreeRelRoot: hint,
|
|
37197
|
-
repoCheckoutPaths: legacyPaths.paths.map((p) => path27.resolve(p))
|
|
37198
|
-
};
|
|
37199
37156
|
}
|
|
37200
37157
|
|
|
37201
|
-
// src/
|
|
37202
|
-
|
|
37203
|
-
|
|
37204
|
-
const
|
|
37205
|
-
|
|
37206
|
-
const
|
|
37207
|
-
|
|
37208
|
-
|
|
37209
|
-
|
|
37210
|
-
worktreesRootPath: params.worktreesRootPath,
|
|
37211
|
-
layout: params.layout,
|
|
37212
|
-
bridgeRoot: getBridgeRoot()
|
|
37213
|
-
}).finally(() => {
|
|
37214
|
-
if (discoverInflightBySessionId.get(sid) === promise2) {
|
|
37215
|
-
discoverInflightBySessionId.delete(sid);
|
|
37216
|
-
}
|
|
37217
|
-
});
|
|
37218
|
-
discoverInflightBySessionId.set(sid, promise2);
|
|
37219
|
-
return promise2;
|
|
37158
|
+
// src/git/changes/status/working-tree-changed-path-count.ts
|
|
37159
|
+
function workingTreeChangedPathCount(nameStatusLines, numstatLines, untrackedLines) {
|
|
37160
|
+
const kindByPath = parseNameStatusLines(nameStatusLines);
|
|
37161
|
+
const numByPath = parseNumstat(numstatLines);
|
|
37162
|
+
const paths = /* @__PURE__ */ new Set([...kindByPath.keys(), ...numByPath.keys()]);
|
|
37163
|
+
for (const p of untrackedLines.map((s) => s.trim()).filter(Boolean)) {
|
|
37164
|
+
paths.add(p);
|
|
37165
|
+
}
|
|
37166
|
+
return paths.size;
|
|
37220
37167
|
}
|
|
37221
37168
|
|
|
37222
|
-
// src/
|
|
37223
|
-
function
|
|
37224
|
-
|
|
37225
|
-
|
|
37226
|
-
|
|
37227
|
-
|
|
37228
|
-
|
|
37229
|
-
|
|
37230
|
-
|
|
37231
|
-
|
|
37232
|
-
|
|
37233
|
-
|
|
37234
|
-
|
|
37235
|
-
|
|
37169
|
+
// src/git/changes/count-working-tree-changed-files.ts
|
|
37170
|
+
async function countWorkingTreeChangedFilesForRepo(repoGitCwd) {
|
|
37171
|
+
return runGitTask(async () => {
|
|
37172
|
+
const g = cliSimpleGit(repoGitCwd);
|
|
37173
|
+
const [nameStatusRaw, numstatRaw, untrackedRaw] = await Promise.all([
|
|
37174
|
+
g.raw(["diff", "--name-status", "HEAD"]).catch(() => ""),
|
|
37175
|
+
g.raw(["diff", "HEAD", "--numstat"]).catch(() => ""),
|
|
37176
|
+
g.raw(["ls-files", "--others", "--exclude-standard"]).catch(() => "")
|
|
37177
|
+
]);
|
|
37178
|
+
return workingTreeChangedPathCount(
|
|
37179
|
+
String(nameStatusRaw).split("\n"),
|
|
37180
|
+
String(numstatRaw).split("\n"),
|
|
37181
|
+
String(untrackedRaw).split("\n")
|
|
37182
|
+
);
|
|
37183
|
+
});
|
|
37236
37184
|
}
|
|
37237
|
-
|
|
37238
|
-
|
|
37239
|
-
|
|
37240
|
-
|
|
37241
|
-
|
|
37242
|
-
|
|
37243
|
-
|
|
37244
|
-
|
|
37245
|
-
return [...disc.repoCheckoutPaths];
|
|
37185
|
+
|
|
37186
|
+
// src/git/commits/remote-tracking/rev-parse-safe.ts
|
|
37187
|
+
async function revParseSafe(g, ref) {
|
|
37188
|
+
try {
|
|
37189
|
+
const v = (await g.raw(["rev-parse", "--verify", ref])).trim();
|
|
37190
|
+
return v || null;
|
|
37191
|
+
} catch {
|
|
37192
|
+
return null;
|
|
37246
37193
|
}
|
|
37247
|
-
return void 0;
|
|
37248
37194
|
}
|
|
37249
37195
|
|
|
37250
|
-
// src/
|
|
37251
|
-
function
|
|
37252
|
-
|
|
37253
|
-
|
|
37254
|
-
|
|
37196
|
+
// src/git/commits/remote-tracking/git-config.ts
|
|
37197
|
+
async function tryConfigGet(g, key) {
|
|
37198
|
+
try {
|
|
37199
|
+
const out = await g.raw(["config", "--get", key]);
|
|
37200
|
+
const v = String(out).trim();
|
|
37201
|
+
return v || null;
|
|
37202
|
+
} catch {
|
|
37203
|
+
return null;
|
|
37204
|
+
}
|
|
37255
37205
|
}
|
|
37256
37206
|
|
|
37257
|
-
// src/
|
|
37258
|
-
|
|
37259
|
-
|
|
37260
|
-
|
|
37261
|
-
|
|
37262
|
-
|
|
37263
|
-
const
|
|
37264
|
-
|
|
37265
|
-
|
|
37207
|
+
// src/git/commits/remote-tracking/branch-remote-config.ts
|
|
37208
|
+
async function readBranchRemoteConfig(g) {
|
|
37209
|
+
const branch = (await g.raw(["rev-parse", "--abbrev-ref", "HEAD"])).trim();
|
|
37210
|
+
if (!branch || branch === "HEAD") {
|
|
37211
|
+
return { branch: null, remote: "origin", merge: null };
|
|
37212
|
+
}
|
|
37213
|
+
const [remote, merge2] = await Promise.all([
|
|
37214
|
+
tryConfigGet(g, `branch.${branch}.remote`),
|
|
37215
|
+
tryConfigGet(g, `branch.${branch}.merge`)
|
|
37216
|
+
]);
|
|
37217
|
+
return { branch, remote: remote ?? "origin", merge: merge2 };
|
|
37218
|
+
}
|
|
37219
|
+
function trackingRefCandidates(config2) {
|
|
37220
|
+
const candidates = ["HEAD@{upstream}", "@{u}"];
|
|
37221
|
+
if (!config2.branch) return candidates;
|
|
37222
|
+
if (config2.merge) {
|
|
37223
|
+
candidates.push(
|
|
37224
|
+
`refs/remotes/${config2.remote}/${config2.merge.replace(/^refs\/heads\//, "")}`
|
|
37225
|
+
);
|
|
37226
|
+
}
|
|
37227
|
+
candidates.push(`refs/remotes/${config2.remote}/${config2.branch}`);
|
|
37228
|
+
return candidates;
|
|
37266
37229
|
}
|
|
37267
37230
|
|
|
37268
|
-
// src/
|
|
37269
|
-
function
|
|
37270
|
-
|
|
37231
|
+
// src/git/commits/remote-tracking/first-resolved-ref.ts
|
|
37232
|
+
async function firstResolvedSha(g, candidates) {
|
|
37233
|
+
const resolved = await Promise.all(
|
|
37234
|
+
candidates.map(async (ref) => ({ ref, sha: await revParseSafe(g, ref) }))
|
|
37235
|
+
);
|
|
37236
|
+
return resolved.find((entry) => entry.sha)?.sha ?? null;
|
|
37271
37237
|
}
|
|
37272
|
-
|
|
37273
|
-
|
|
37274
|
-
|
|
37275
|
-
|
|
37276
|
-
|
|
37277
|
-
return "HEAD";
|
|
37238
|
+
|
|
37239
|
+
// src/git/commits/remote-tracking/resolve-default-remote-branch-ref.ts
|
|
37240
|
+
var DEFAULT_REMOTE_BRANCH_NAMES = ["main", "master", "trunk", "develop"];
|
|
37241
|
+
function defaultRemoteBranchNameRefs(remote) {
|
|
37242
|
+
return DEFAULT_REMOTE_BRANCH_NAMES.map((name) => `refs/remotes/${remote}/${name}`);
|
|
37278
37243
|
}
|
|
37279
|
-
async function
|
|
37280
|
-
const
|
|
37281
|
-
|
|
37282
|
-
|
|
37283
|
-
|
|
37284
|
-
|
|
37285
|
-
|
|
37286
|
-
if (repos.length === 0) {
|
|
37287
|
-
log2("[worktrees] No Git repositories under bridge root; skipping worktree creation.");
|
|
37244
|
+
async function resolveRemoteHeadSymbolicRef(g, remote) {
|
|
37245
|
+
const headSym = `refs/remotes/${remote}/HEAD`;
|
|
37246
|
+
try {
|
|
37247
|
+
const out = await g.raw(["symbolic-ref", "-q", "--verify", headSym]);
|
|
37248
|
+
const resolved = String(out).trim();
|
|
37249
|
+
return resolved.startsWith("refs/remotes/") ? resolved : null;
|
|
37250
|
+
} catch {
|
|
37288
37251
|
return null;
|
|
37289
37252
|
}
|
|
37290
|
-
|
|
37291
|
-
|
|
37292
|
-
|
|
37293
|
-
|
|
37294
|
-
|
|
37295
|
-
|
|
37296
|
-
|
|
37297
|
-
|
|
37298
|
-
|
|
37299
|
-
|
|
37300
|
-
}
|
|
37301
|
-
const baseRef = resolveBaseRefForRepo(relNorm, worktreeBaseBranches);
|
|
37302
|
-
try {
|
|
37303
|
-
await gitWorktreeAddBranch(repo.absolutePath, wtPath, branch, baseRef);
|
|
37304
|
-
log2(`[worktrees] Added worktree ${wtPath} (branch ${branch}, base ${baseRef}).`);
|
|
37305
|
-
worktreePaths.push(wtPath);
|
|
37306
|
-
} catch (e) {
|
|
37307
|
-
log2(
|
|
37308
|
-
`[worktrees] Worktree add failed for ${repo.absolutePath}: ${e instanceof Error ? e.message : String(e)}`
|
|
37309
|
-
);
|
|
37310
|
-
}
|
|
37253
|
+
}
|
|
37254
|
+
async function resolveDefaultRemoteBranchSha(g, remote) {
|
|
37255
|
+
const nameRefs = defaultRemoteBranchNameRefs(remote);
|
|
37256
|
+
const [symRef, ...nameShas] = await Promise.all([
|
|
37257
|
+
resolveRemoteHeadSymbolicRef(g, remote),
|
|
37258
|
+
...nameRefs.map((ref) => revParseSafe(g, ref))
|
|
37259
|
+
]);
|
|
37260
|
+
if (symRef) return revParseSafe(g, symRef);
|
|
37261
|
+
for (let i = 0; i < nameRefs.length; i++) {
|
|
37262
|
+
if (nameShas[i]) return nameShas[i];
|
|
37311
37263
|
}
|
|
37312
|
-
|
|
37313
|
-
return {
|
|
37314
|
-
worktreePaths,
|
|
37315
|
-
sessionParentPath: sessionDir,
|
|
37316
|
-
workingTreeRelRoot: sessionDir
|
|
37317
|
-
};
|
|
37264
|
+
return null;
|
|
37318
37265
|
}
|
|
37319
37266
|
|
|
37320
|
-
// src/
|
|
37321
|
-
|
|
37322
|
-
|
|
37323
|
-
|
|
37324
|
-
|
|
37325
|
-
|
|
37326
|
-
|
|
37327
|
-
|
|
37328
|
-
|
|
37267
|
+
// src/git/commits/remote-tracking/resolve-base-sha-for-unpushed-commits.ts
|
|
37268
|
+
import * as path22 from "node:path";
|
|
37269
|
+
var BASE_SHA_CACHE_TTL_MS = 3e4;
|
|
37270
|
+
var baseShaCache = /* @__PURE__ */ new Map();
|
|
37271
|
+
async function resolveBaseShaForUnpushedCommitsUncached(g) {
|
|
37272
|
+
const config2 = await readBranchRemoteConfig(g);
|
|
37273
|
+
const trackingSha = await firstResolvedSha(g, trackingRefCandidates(config2));
|
|
37274
|
+
if (trackingSha) return trackingSha;
|
|
37275
|
+
return resolveDefaultRemoteBranchSha(g, config2.remote);
|
|
37276
|
+
}
|
|
37277
|
+
async function resolveBaseShaForUnpushedCommits(g, repoDir) {
|
|
37278
|
+
if (repoDir) {
|
|
37279
|
+
const key = path22.resolve(repoDir);
|
|
37280
|
+
const now = Date.now();
|
|
37281
|
+
const cached2 = baseShaCache.get(key);
|
|
37282
|
+
if (cached2 && cached2.expiresAt > now) return cached2.baseSha;
|
|
37283
|
+
const baseSha = await resolveBaseShaForUnpushedCommitsUncached(g);
|
|
37284
|
+
baseShaCache.set(key, { baseSha, expiresAt: now + BASE_SHA_CACHE_TTL_MS });
|
|
37285
|
+
return baseSha;
|
|
37286
|
+
}
|
|
37287
|
+
return resolveBaseShaForUnpushedCommitsUncached(g);
|
|
37288
|
+
}
|
|
37289
|
+
|
|
37290
|
+
// src/git/commits/remote-tracking/commits-ahead-of-remote-tracking.ts
|
|
37291
|
+
async function commitsAheadOfRemoteTracking(repoDir) {
|
|
37292
|
+
const g = cliSimpleGit(repoDir);
|
|
37293
|
+
const headSha = await revParseSafe(g, "HEAD");
|
|
37294
|
+
if (!headSha) return 0;
|
|
37295
|
+
const baseSha = await resolveBaseShaForUnpushedCommits(g, repoDir);
|
|
37296
|
+
if (!baseSha || baseSha === headSha) return 0;
|
|
37297
|
+
try {
|
|
37298
|
+
const out = await g.raw(["rev-list", "--count", `${baseSha}..${headSha}`]);
|
|
37299
|
+
const n = parseInt(String(out).trim(), 10);
|
|
37300
|
+
return Number.isNaN(n) ? 0 : n;
|
|
37301
|
+
} catch {
|
|
37302
|
+
return 0;
|
|
37303
|
+
}
|
|
37304
|
+
}
|
|
37305
|
+
|
|
37306
|
+
// src/git/status/working-tree-status.ts
|
|
37307
|
+
async function getRepoWorkingTreeStatus(repoDir) {
|
|
37308
|
+
return runGitTask(async () => {
|
|
37309
|
+
const uncommittedFileCount = await countWorkingTreeChangedFilesForRepo(repoDir);
|
|
37310
|
+
const hasUncommittedChanges = uncommittedFileCount > 0;
|
|
37311
|
+
const ahead = await commitsAheadOfRemoteTracking(repoDir);
|
|
37312
|
+
return { hasUncommittedChanges, hasUnpushedCommits: ahead > 0, uncommittedFileCount };
|
|
37329
37313
|
});
|
|
37330
|
-
|
|
37331
|
-
|
|
37332
|
-
|
|
37333
|
-
|
|
37334
|
-
|
|
37314
|
+
}
|
|
37315
|
+
async function aggregateSessionPathsWorkingTreeStatus(paths) {
|
|
37316
|
+
let hasUncommittedChanges = false;
|
|
37317
|
+
let hasUnpushedCommits = false;
|
|
37318
|
+
let uncommittedFileCount = 0;
|
|
37319
|
+
await forEachWithGitYield(paths, async (p) => {
|
|
37320
|
+
const s = await getRepoWorkingTreeStatus(p);
|
|
37321
|
+
uncommittedFileCount += s.uncommittedFileCount;
|
|
37322
|
+
if (s.hasUncommittedChanges) hasUncommittedChanges = true;
|
|
37323
|
+
if (s.hasUnpushedCommits) hasUnpushedCommits = true;
|
|
37335
37324
|
});
|
|
37336
|
-
return
|
|
37325
|
+
return { hasUncommittedChanges, hasUnpushedCommits, uncommittedFileCount };
|
|
37337
37326
|
}
|
|
37338
|
-
|
|
37339
|
-
|
|
37340
|
-
|
|
37341
|
-
|
|
37342
|
-
|
|
37343
|
-
|
|
37344
|
-
|
|
37345
|
-
cache2.remember(sessionId, disc);
|
|
37346
|
-
return cache2.getSessionParentPath(sessionId);
|
|
37347
|
-
}
|
|
37348
|
-
return void 0;
|
|
37327
|
+
async function pushAheadOfUpstreamForPaths(paths) {
|
|
37328
|
+
await forEachWithGitYield(paths, async (p) => {
|
|
37329
|
+
const g = cliSimpleGit(p);
|
|
37330
|
+
const ahead = await commitsAheadOfRemoteTracking(p);
|
|
37331
|
+
if (ahead <= 0) return;
|
|
37332
|
+
await g.push();
|
|
37333
|
+
});
|
|
37349
37334
|
}
|
|
37350
37335
|
|
|
37351
|
-
// src/worktrees/manager/resolve-
|
|
37352
|
-
|
|
37353
|
-
|
|
37354
|
-
|
|
37355
|
-
|
|
37356
|
-
|
|
37357
|
-
}
|
|
37358
|
-
const rememberAndReturn = (binding) => {
|
|
37359
|
-
params.cache.remember(params.sessionId, binding);
|
|
37360
|
-
return params.cache.getSessionParentPath(params.sessionId) ?? resolved;
|
|
37336
|
+
// src/worktrees/manager/resolve-commit-targets.ts
|
|
37337
|
+
function bridgeRootBinding(bridgeRoot) {
|
|
37338
|
+
return {
|
|
37339
|
+
sessionParentPath: bridgeRoot,
|
|
37340
|
+
workingTreeRelRoot: bridgeRoot,
|
|
37341
|
+
repoCheckoutPaths: [bridgeRoot]
|
|
37361
37342
|
};
|
|
37362
|
-
const diskFirst = await params.discover();
|
|
37363
|
-
if (diskFirst) return rememberAndReturn(diskFirst);
|
|
37364
|
-
const fromRoot = await discoverSessionWorktreesUnderSessionWorktreeRoot(resolved, params.sessionId);
|
|
37365
|
-
if (fromRoot) return rememberAndReturn(fromRoot);
|
|
37366
|
-
let cur = resolved;
|
|
37367
|
-
for (let i = 0; i < 16; i++) {
|
|
37368
|
-
await yieldToEventLoop();
|
|
37369
|
-
const tryRoot = await discoverSessionWorktreesUnderSessionWorktreeRoot(cur, params.sessionId);
|
|
37370
|
-
if (tryRoot) return rememberAndReturn(tryRoot);
|
|
37371
|
-
const next = path29.dirname(cur);
|
|
37372
|
-
if (next === cur) break;
|
|
37373
|
-
cur = next;
|
|
37374
|
-
}
|
|
37375
|
-
return resolved;
|
|
37376
37343
|
}
|
|
37377
|
-
|
|
37378
|
-
// src/worktrees/manager/resolve-session-parent-path-for-prompt.ts
|
|
37379
|
-
async function resolveSessionParentPathForPrompt(params) {
|
|
37380
|
-
const { sessionId, cache: cache2, worktreesRootPath, layout, log: log2, discover, opts } = params;
|
|
37381
|
-
if (!sessionId) return void 0;
|
|
37344
|
+
async function resolveCommitTargetsAsync(sessionId, cache2, discover) {
|
|
37382
37345
|
const sid = sessionId.trim();
|
|
37383
|
-
const
|
|
37384
|
-
if (
|
|
37385
|
-
|
|
37386
|
-
|
|
37387
|
-
|
|
37388
|
-
|
|
37389
|
-
cache: cache2,
|
|
37390
|
-
discover: () => discover(sid)
|
|
37391
|
-
});
|
|
37392
|
-
}
|
|
37393
|
-
const parentKind = parseSessionParent(opts.sessionParent);
|
|
37394
|
-
if (parentKind === "bridge_root") {
|
|
37395
|
-
return void 0;
|
|
37396
|
-
}
|
|
37397
|
-
if (parentKind === "worktrees_root") {
|
|
37398
|
-
if (!opts.isNewSession) {
|
|
37399
|
-
return resolveExistingSessionParentPath(sid, cache2, () => discover(sid));
|
|
37400
|
-
}
|
|
37401
|
-
return prepareAndRememberSessionWorktrees({
|
|
37402
|
-
cache: cache2,
|
|
37403
|
-
sessionId: sid,
|
|
37404
|
-
worktreesRootPath,
|
|
37405
|
-
layout,
|
|
37406
|
-
log: log2,
|
|
37407
|
-
...opts.worktreeBaseBranches ? { worktreeBaseBranches: opts.worktreeBaseBranches } : {}
|
|
37408
|
-
});
|
|
37409
|
-
}
|
|
37410
|
-
if (!opts.isNewSession) {
|
|
37411
|
-
return resolveExistingSessionParentPath(sid, cache2, () => discover(sid));
|
|
37346
|
+
const paths = cache2.getRepoCheckoutPathsRef(sid);
|
|
37347
|
+
if (paths?.length) return paths;
|
|
37348
|
+
const disc = await discover(sid);
|
|
37349
|
+
if (disc?.repoCheckoutPaths.length) {
|
|
37350
|
+
cache2.remember(sid, disc);
|
|
37351
|
+
return disc.repoCheckoutPaths;
|
|
37412
37352
|
}
|
|
37413
|
-
|
|
37414
|
-
|
|
37415
|
-
|
|
37416
|
-
|
|
37417
|
-
|
|
37418
|
-
|
|
37419
|
-
|
|
37353
|
+
const bridgeRoot = getBridgeRoot();
|
|
37354
|
+
cache2.remember(sid, bridgeRootBinding(bridgeRoot));
|
|
37355
|
+
return [bridgeRoot];
|
|
37356
|
+
}
|
|
37357
|
+
|
|
37358
|
+
// src/worktrees/manager/git/get-session-working-tree-status.ts
|
|
37359
|
+
async function getSessionWorkingTreeStatus(cache2, sessionId, discover) {
|
|
37360
|
+
return aggregateSessionPathsWorkingTreeStatus(
|
|
37361
|
+
await resolveCommitTargetsAsync(sessionId, cache2, discover)
|
|
37362
|
+
);
|
|
37420
37363
|
}
|
|
37421
37364
|
|
|
37422
37365
|
// src/git/changes/types.ts
|
|
@@ -37477,108 +37420,13 @@ function formatRemoteDisplayLabel(remoteUrl) {
|
|
|
37477
37420
|
}
|
|
37478
37421
|
|
|
37479
37422
|
// src/git/changes/repo/get-working-tree-change-repo-details.ts
|
|
37480
|
-
import * as
|
|
37423
|
+
import * as path29 from "node:path";
|
|
37481
37424
|
|
|
37482
37425
|
// src/git/changes/repo/build-working-tree-change-repo-detail.ts
|
|
37483
|
-
import * as
|
|
37426
|
+
import * as path28 from "node:path";
|
|
37484
37427
|
|
|
37485
|
-
// src/git/commits/
|
|
37486
|
-
|
|
37487
|
-
try {
|
|
37488
|
-
const out = await g.raw(["config", "--get", key]);
|
|
37489
|
-
const v = String(out).trim();
|
|
37490
|
-
return v || null;
|
|
37491
|
-
} catch {
|
|
37492
|
-
return null;
|
|
37493
|
-
}
|
|
37494
|
-
}
|
|
37495
|
-
async function revParseSafe(g, ref) {
|
|
37496
|
-
try {
|
|
37497
|
-
const v = (await g.raw(["rev-parse", ref])).trim();
|
|
37498
|
-
return v || null;
|
|
37499
|
-
} catch {
|
|
37500
|
-
return null;
|
|
37501
|
-
}
|
|
37502
|
-
}
|
|
37503
|
-
async function resolveRemoteTrackingRefForAhead(g) {
|
|
37504
|
-
try {
|
|
37505
|
-
await g.raw(["rev-parse", "--verify", "HEAD@{upstream}"]);
|
|
37506
|
-
return "HEAD@{upstream}";
|
|
37507
|
-
} catch {
|
|
37508
|
-
try {
|
|
37509
|
-
await g.raw(["rev-parse", "--verify", "@{u}"]);
|
|
37510
|
-
return "@{u}";
|
|
37511
|
-
} catch {
|
|
37512
|
-
}
|
|
37513
|
-
}
|
|
37514
|
-
const branch = (await g.raw(["rev-parse", "--abbrev-ref", "HEAD"])).trim();
|
|
37515
|
-
if (!branch || branch === "HEAD") return null;
|
|
37516
|
-
const remote = await tryConfigGet(g, `branch.${branch}.remote`) ?? "origin";
|
|
37517
|
-
const merge2 = await tryConfigGet(g, `branch.${branch}.merge`);
|
|
37518
|
-
if (merge2) {
|
|
37519
|
-
const upstreamBranch = merge2.replace(/^refs\/heads\//, "");
|
|
37520
|
-
const ref = `refs/remotes/${remote}/${upstreamBranch}`;
|
|
37521
|
-
try {
|
|
37522
|
-
await g.raw(["rev-parse", "--verify", ref]);
|
|
37523
|
-
return ref;
|
|
37524
|
-
} catch {
|
|
37525
|
-
}
|
|
37526
|
-
}
|
|
37527
|
-
const fallbackRef = `refs/remotes/${remote}/${branch}`;
|
|
37528
|
-
try {
|
|
37529
|
-
await g.raw(["rev-parse", "--verify", fallbackRef]);
|
|
37530
|
-
return fallbackRef;
|
|
37531
|
-
} catch {
|
|
37532
|
-
return null;
|
|
37533
|
-
}
|
|
37534
|
-
}
|
|
37535
|
-
async function remoteForCurrentBranch(g) {
|
|
37536
|
-
const branch = (await g.raw(["rev-parse", "--abbrev-ref", "HEAD"])).trim();
|
|
37537
|
-
if (!branch || branch === "HEAD") return "origin";
|
|
37538
|
-
return await tryConfigGet(g, `branch.${branch}.remote`) ?? "origin";
|
|
37539
|
-
}
|
|
37540
|
-
async function resolveDefaultRemoteBranchRef(g, remote) {
|
|
37541
|
-
const headSym = `refs/remotes/${remote}/HEAD`;
|
|
37542
|
-
try {
|
|
37543
|
-
const resolved = (await g.raw(["symbolic-ref", "-q", "--verify", headSym])).trim();
|
|
37544
|
-
if (resolved.startsWith("refs/remotes/")) return resolved;
|
|
37545
|
-
} catch {
|
|
37546
|
-
}
|
|
37547
|
-
for (const name of ["main", "master", "trunk", "develop"]) {
|
|
37548
|
-
const r = `refs/remotes/${remote}/${name}`;
|
|
37549
|
-
try {
|
|
37550
|
-
await g.raw(["rev-parse", "--verify", r]);
|
|
37551
|
-
return r;
|
|
37552
|
-
} catch {
|
|
37553
|
-
}
|
|
37554
|
-
}
|
|
37555
|
-
return null;
|
|
37556
|
-
}
|
|
37557
|
-
async function resolveBaseShaForUnpushedCommits(g) {
|
|
37558
|
-
const trackingRef = await resolveRemoteTrackingRefForAhead(g);
|
|
37559
|
-
if (trackingRef) {
|
|
37560
|
-
const sha = await revParseSafe(g, trackingRef);
|
|
37561
|
-
if (sha) return sha;
|
|
37562
|
-
}
|
|
37563
|
-
const remote = await remoteForCurrentBranch(g);
|
|
37564
|
-
const defaultRef = await resolveDefaultRemoteBranchRef(g, remote);
|
|
37565
|
-
if (!defaultRef) return null;
|
|
37566
|
-
return revParseSafe(g, defaultRef);
|
|
37567
|
-
}
|
|
37568
|
-
async function commitsAheadOfRemoteTracking(repoDir) {
|
|
37569
|
-
const g = cliSimpleGit(repoDir);
|
|
37570
|
-
const headSha = await revParseSafe(g, "HEAD");
|
|
37571
|
-
if (!headSha) return 0;
|
|
37572
|
-
const baseSha = await resolveBaseShaForUnpushedCommits(g);
|
|
37573
|
-
if (!baseSha || baseSha === headSha) return 0;
|
|
37574
|
-
try {
|
|
37575
|
-
const out = await g.raw(["rev-list", "--count", `${baseSha}..${headSha}`]);
|
|
37576
|
-
const n = parseInt(String(out).trim(), 10);
|
|
37577
|
-
return Number.isNaN(n) ? 0 : n;
|
|
37578
|
-
} catch {
|
|
37579
|
-
return 0;
|
|
37580
|
-
}
|
|
37581
|
-
}
|
|
37428
|
+
// src/git/commits/list-unpushed-commits.ts
|
|
37429
|
+
import * as path23 from "node:path";
|
|
37582
37430
|
|
|
37583
37431
|
// src/git/commits/lib/parse-log-lines.ts
|
|
37584
37432
|
function parseLogShaDateSubjectLines(raw) {
|
|
@@ -37598,9 +37446,14 @@ function parseLogShaDateSubjectLines(raw) {
|
|
|
37598
37446
|
}
|
|
37599
37447
|
|
|
37600
37448
|
// src/git/commits/list-unpushed-commits.ts
|
|
37449
|
+
var UNPUSHED_CACHE_TTL_MS = 3e4;
|
|
37450
|
+
var unpushedCache = /* @__PURE__ */ new Map();
|
|
37601
37451
|
async function gitLogNotReachableFromBase(g, baseSha, headSha) {
|
|
37602
37452
|
if (baseSha === headSha) return [];
|
|
37603
37453
|
try {
|
|
37454
|
+
const countOut = await g.raw(["rev-list", "--count", `${baseSha}..${headSha}`]);
|
|
37455
|
+
const count = parseInt(String(countOut).trim(), 10);
|
|
37456
|
+
if (!Number.isFinite(count) || count <= 0) return [];
|
|
37604
37457
|
const logOut = await g.raw(["log", "--format=%H %cI %s", `${baseSha}..${headSha}`]);
|
|
37605
37458
|
return parseLogShaDateSubjectLines(logOut);
|
|
37606
37459
|
} catch {
|
|
@@ -37608,12 +37461,22 @@ async function gitLogNotReachableFromBase(g, baseSha, headSha) {
|
|
|
37608
37461
|
}
|
|
37609
37462
|
}
|
|
37610
37463
|
async function listUnpushedCommits(repoDir) {
|
|
37464
|
+
const key = path23.resolve(repoDir);
|
|
37465
|
+
const now = Date.now();
|
|
37466
|
+
const cached2 = unpushedCache.get(key);
|
|
37467
|
+
if (cached2 && cached2.expiresAt > now) return cached2.commits;
|
|
37611
37468
|
const g = cliSimpleGit(repoDir);
|
|
37612
|
-
const headSha = await
|
|
37613
|
-
|
|
37614
|
-
|
|
37615
|
-
|
|
37616
|
-
|
|
37469
|
+
const [headSha, baseSha] = await Promise.all([
|
|
37470
|
+
revParseSafe(g, "HEAD"),
|
|
37471
|
+
resolveBaseShaForUnpushedCommits(g, repoDir)
|
|
37472
|
+
]);
|
|
37473
|
+
if (!headSha || !baseSha) {
|
|
37474
|
+
unpushedCache.set(key, { commits: [], expiresAt: now + UNPUSHED_CACHE_TTL_MS });
|
|
37475
|
+
return [];
|
|
37476
|
+
}
|
|
37477
|
+
const commits = await gitLogNotReachableFromBase(g, baseSha, headSha);
|
|
37478
|
+
unpushedCache.set(key, { commits, expiresAt: now + UNPUSHED_CACHE_TTL_MS });
|
|
37479
|
+
return commits;
|
|
37617
37480
|
}
|
|
37618
37481
|
|
|
37619
37482
|
// src/git/commits/lib/sanitize-recent-commits-limit.ts
|
|
@@ -37627,72 +37490,40 @@ function sanitizeRecentCommitsLimit(value) {
|
|
|
37627
37490
|
}
|
|
37628
37491
|
|
|
37629
37492
|
// src/git/commits/list-recent-commits.ts
|
|
37630
|
-
async function
|
|
37493
|
+
async function listRecentCommitRefs(repoDir, limitInput) {
|
|
37631
37494
|
const limit = sanitizeRecentCommitsLimit(limitInput);
|
|
37632
37495
|
const g = cliSimpleGit(repoDir);
|
|
37633
|
-
const headSha = await revParseSafe(g, "HEAD");
|
|
37634
|
-
if (!headSha) return { commits: [], hasMore: false };
|
|
37635
|
-
const unpushedSet = new Set((await listUnpushedCommits(repoDir)).map((c) => c.sha));
|
|
37636
37496
|
try {
|
|
37637
|
-
const logOut = await g.raw(["log", "--format=%H %cI %s", `-n`, String(limit), "HEAD"]);
|
|
37638
|
-
const
|
|
37639
|
-
|
|
37640
|
-
|
|
37641
|
-
}));
|
|
37642
|
-
let hasMore = false;
|
|
37643
|
-
if (commits.length === limit) {
|
|
37644
|
-
const nextOut = await g.raw(["log", "-n", "1", `--skip=${limit}`, "--format=%H", "HEAD"]);
|
|
37645
|
-
hasMore = /^[0-9a-f]{7,40}$/i.test(String(nextOut).trim());
|
|
37646
|
-
}
|
|
37497
|
+
const logOut = await g.raw(["log", "--format=%H %cI %s", `-n`, String(limit + 1), "HEAD"]);
|
|
37498
|
+
const parsed = parseLogShaDateSubjectLines(logOut);
|
|
37499
|
+
const hasMore = parsed.length > limit;
|
|
37500
|
+
const commits = parsed.slice(0, limit);
|
|
37647
37501
|
return { commits, hasMore };
|
|
37648
37502
|
} catch {
|
|
37649
37503
|
return { commits: [], hasMore: false };
|
|
37650
37504
|
}
|
|
37651
37505
|
}
|
|
37652
37506
|
|
|
37653
|
-
// src/git/changes/
|
|
37654
|
-
|
|
37655
|
-
return path55.replace(/\\/g, "/").replace(/\/ +/g, "/").trim();
|
|
37656
|
-
}
|
|
37657
|
-
|
|
37658
|
-
// src/git/changes/parse/expand-git-rename-numstat-path.ts
|
|
37659
|
-
function expandGitRenameNumstatPath(rawPath) {
|
|
37660
|
-
const open2 = rawPath.indexOf("{");
|
|
37661
|
-
const arrow = rawPath.indexOf("=>");
|
|
37662
|
-
const close = rawPath.lastIndexOf("}");
|
|
37663
|
-
if (open2 === -1 || arrow === -1 || close === -1 || open2 > arrow || arrow > close) {
|
|
37664
|
-
return null;
|
|
37665
|
-
}
|
|
37666
|
-
const prefix = rawPath.slice(0, open2);
|
|
37667
|
-
const suffix = rawPath.slice(close + 1);
|
|
37668
|
-
const oldPart = rawPath.slice(open2 + 1, arrow).trim();
|
|
37669
|
-
const newPart = rawPath.slice(arrow + 2, close).trim();
|
|
37670
|
-
return {
|
|
37671
|
-
oldPath: normalizeGitDiffPath(`${prefix}${oldPart}${suffix}`),
|
|
37672
|
-
newPath: normalizeGitDiffPath(`${prefix}${newPart}${suffix}`)
|
|
37673
|
-
};
|
|
37674
|
-
}
|
|
37675
|
-
function isGitRenameNumstatPath(rawPath) {
|
|
37676
|
-
return expandGitRenameNumstatPath(rawPath) != null;
|
|
37677
|
-
}
|
|
37507
|
+
// src/git/changes/listing/list-changed-files-for-commit.ts
|
|
37508
|
+
import * as path25 from "node:path";
|
|
37678
37509
|
|
|
37679
37510
|
// src/git/changes/rows/build-changed-file-row.ts
|
|
37680
37511
|
function buildChangedFileRow(options) {
|
|
37681
37512
|
const pathInRepo = normalizeGitDiffPath(options.pathInRepo);
|
|
37682
|
-
const
|
|
37513
|
+
const workspaceRelPath = posixJoinDirFile(options.repoRelPath, pathInRepo);
|
|
37683
37514
|
const additions = options.numEntry?.additions ?? 0;
|
|
37684
37515
|
const deletions = options.numEntry?.deletions ?? 0;
|
|
37685
37516
|
let change = options.nameEntry?.change ?? "modified";
|
|
37686
|
-
let
|
|
37517
|
+
let movedFromWorkspaceRelPath;
|
|
37687
37518
|
let movedFromPathInRepo;
|
|
37688
37519
|
const oldPathInRepo = options.nameEntry?.oldPathInRepo ?? options.numEntry?.oldPathInRepo;
|
|
37689
37520
|
if (change === "moved" && oldPathInRepo) {
|
|
37690
37521
|
movedFromPathInRepo = normalizeGitDiffPath(oldPathInRepo);
|
|
37691
|
-
|
|
37522
|
+
movedFromWorkspaceRelPath = posixJoinDirFile(options.repoRelPath, movedFromPathInRepo);
|
|
37692
37523
|
} else if (oldPathInRepo && change === "modified") {
|
|
37693
37524
|
change = "moved";
|
|
37694
37525
|
movedFromPathInRepo = normalizeGitDiffPath(oldPathInRepo);
|
|
37695
|
-
|
|
37526
|
+
movedFromWorkspaceRelPath = posixJoinDirFile(options.repoRelPath, movedFromPathInRepo);
|
|
37696
37527
|
}
|
|
37697
37528
|
if (options.untracked && !options.nameEntry) {
|
|
37698
37529
|
change = "added";
|
|
@@ -37702,11 +37533,11 @@ function buildChangedFileRow(options) {
|
|
|
37702
37533
|
else change = "modified";
|
|
37703
37534
|
}
|
|
37704
37535
|
return {
|
|
37705
|
-
|
|
37536
|
+
workspaceRelPath,
|
|
37706
37537
|
additions,
|
|
37707
37538
|
deletions,
|
|
37708
37539
|
change,
|
|
37709
|
-
...
|
|
37540
|
+
...movedFromWorkspaceRelPath ? { movedFromWorkspaceRelPath } : {},
|
|
37710
37541
|
...movedFromPathInRepo ? { movedFromPathInRepo } : {}
|
|
37711
37542
|
};
|
|
37712
37543
|
}
|
|
@@ -37765,8 +37596,8 @@ async function unifiedDiffForFileInRange(repoCwd, range, pathInRepo, change, mov
|
|
|
37765
37596
|
}
|
|
37766
37597
|
|
|
37767
37598
|
// src/git/changes/total-lines/resolve-changed-file-total-lines.ts
|
|
37768
|
-
import * as
|
|
37769
|
-
import * as
|
|
37599
|
+
import * as fs18 from "node:fs";
|
|
37600
|
+
import * as path24 from "node:path";
|
|
37770
37601
|
|
|
37771
37602
|
// src/git/changes/lines/count-lines.ts
|
|
37772
37603
|
import { createReadStream } from "node:fs";
|
|
@@ -37812,13 +37643,13 @@ async function countLinesInGitRef(repoGitCwd, ref, pathInRepo) {
|
|
|
37812
37643
|
}
|
|
37813
37644
|
async function resolveWorkingTreeFileTotalLines(options) {
|
|
37814
37645
|
if (options.isBinary) return null;
|
|
37815
|
-
const filePath =
|
|
37646
|
+
const filePath = path24.join(options.repoGitCwd, options.pathInRepo);
|
|
37816
37647
|
if (options.change === "removed") {
|
|
37817
37648
|
return countLinesInGitRef(options.repoGitCwd, "HEAD", options.pathInRepo);
|
|
37818
37649
|
}
|
|
37819
37650
|
if (options.change === "moved" || options.change === "modified") {
|
|
37820
37651
|
try {
|
|
37821
|
-
const st = await
|
|
37652
|
+
const st = await fs18.promises.stat(filePath);
|
|
37822
37653
|
if (!st.isFile()) return null;
|
|
37823
37654
|
return await countTextFileLines(filePath);
|
|
37824
37655
|
} catch {
|
|
@@ -37838,23 +37669,23 @@ async function resolveCommitFileTotalLines(options) {
|
|
|
37838
37669
|
return null;
|
|
37839
37670
|
}
|
|
37840
37671
|
|
|
37841
|
-
// src/git/changes/listing/path-in-repo-from-
|
|
37842
|
-
function
|
|
37672
|
+
// src/git/changes/listing/path-in-repo-from-workspace.ts
|
|
37673
|
+
function pathInRepoFromWorkspace(workspaceRelPath, repoRelPath) {
|
|
37843
37674
|
const normRel = repoRelPath === "." || repoRelPath === "" ? "." : repoRelPath;
|
|
37844
|
-
if (normRel === ".") return
|
|
37845
|
-
if (
|
|
37846
|
-
return
|
|
37675
|
+
if (normRel === ".") return workspaceRelPath;
|
|
37676
|
+
if (workspaceRelPath.startsWith(`${normRel}/`)) {
|
|
37677
|
+
return workspaceRelPath.slice(normRel.length + 1);
|
|
37847
37678
|
}
|
|
37848
|
-
return
|
|
37679
|
+
return workspaceRelPath;
|
|
37849
37680
|
}
|
|
37850
|
-
function
|
|
37681
|
+
function normalizeRepoRelPath(repoRelPath) {
|
|
37851
37682
|
return repoRelPath === "." || repoRelPath === "" ? "." : repoRelPath;
|
|
37852
37683
|
}
|
|
37853
37684
|
|
|
37854
37685
|
// src/git/changes/listing/enrich-commit-file-rows.ts
|
|
37855
37686
|
async function enrichCommitFileRows(options) {
|
|
37856
37687
|
await forEachWithGitYield(options.rows, async (row) => {
|
|
37857
|
-
const pathInRepo =
|
|
37688
|
+
const pathInRepo = pathInRepoFromWorkspace(row.workspaceRelPath, options.repoRelPath);
|
|
37858
37689
|
row.patchContent = await unifiedDiffForFileInRange(
|
|
37859
37690
|
options.repoGitCwd,
|
|
37860
37691
|
options.range,
|
|
@@ -37873,102 +37704,6 @@ async function enrichCommitFileRows(options) {
|
|
|
37873
37704
|
});
|
|
37874
37705
|
}
|
|
37875
37706
|
|
|
37876
|
-
// src/git/changes/parse/parse-name-status-entries.ts
|
|
37877
|
-
function parseNameStatusEntries(lines) {
|
|
37878
|
-
const entries = [];
|
|
37879
|
-
for (const line of lines) {
|
|
37880
|
-
if (!line.trim()) continue;
|
|
37881
|
-
const tabParts = line.split(" ");
|
|
37882
|
-
if (tabParts.length < 2) continue;
|
|
37883
|
-
const status = tabParts[0].trim();
|
|
37884
|
-
const code = status[0];
|
|
37885
|
-
if (code === "A") {
|
|
37886
|
-
entries.push({ pathInRepo: normalizeGitDiffPath(tabParts[tabParts.length - 1]), change: "added" });
|
|
37887
|
-
} else if (code === "D") {
|
|
37888
|
-
entries.push({ pathInRepo: normalizeGitDiffPath(tabParts[tabParts.length - 1]), change: "removed" });
|
|
37889
|
-
} else if (code === "R" || code === "C") {
|
|
37890
|
-
if (tabParts.length >= 3) {
|
|
37891
|
-
entries.push({
|
|
37892
|
-
pathInRepo: normalizeGitDiffPath(tabParts[tabParts.length - 1]),
|
|
37893
|
-
oldPathInRepo: normalizeGitDiffPath(tabParts[tabParts.length - 2]),
|
|
37894
|
-
change: "moved"
|
|
37895
|
-
});
|
|
37896
|
-
}
|
|
37897
|
-
} else if (code === "M" || code === "U" || code === "T") {
|
|
37898
|
-
entries.push({ pathInRepo: normalizeGitDiffPath(tabParts[tabParts.length - 1]), change: "modified" });
|
|
37899
|
-
}
|
|
37900
|
-
}
|
|
37901
|
-
return entries;
|
|
37902
|
-
}
|
|
37903
|
-
function parseNameStatusLines(lines) {
|
|
37904
|
-
const m = /* @__PURE__ */ new Map();
|
|
37905
|
-
for (const entry of parseNameStatusEntries(lines)) {
|
|
37906
|
-
m.set(entry.pathInRepo, entry.change);
|
|
37907
|
-
}
|
|
37908
|
-
return m;
|
|
37909
|
-
}
|
|
37910
|
-
|
|
37911
|
-
// src/git/changes/parse/parse-numstat-entries.ts
|
|
37912
|
-
function parseNumstatCounts(parts) {
|
|
37913
|
-
const [a, d] = parts;
|
|
37914
|
-
return {
|
|
37915
|
-
additions: a === "-" ? 0 : parseInt(String(a), 10) || 0,
|
|
37916
|
-
deletions: d === "-" ? 0 : parseInt(String(d), 10) || 0
|
|
37917
|
-
};
|
|
37918
|
-
}
|
|
37919
|
-
function parseNumstatLine(line) {
|
|
37920
|
-
const parts = line.split(" ");
|
|
37921
|
-
if (parts.length < 3) return null;
|
|
37922
|
-
const rawPath = parts[parts.length - 1];
|
|
37923
|
-
const { additions, deletions } = parseNumstatCounts(parts);
|
|
37924
|
-
const expanded = expandGitRenameNumstatPath(rawPath);
|
|
37925
|
-
const pathInRepo = normalizeGitDiffPath(expanded?.newPath ?? rawPath);
|
|
37926
|
-
return {
|
|
37927
|
-
pathInRepo,
|
|
37928
|
-
additions,
|
|
37929
|
-
deletions,
|
|
37930
|
-
...expanded ? { oldPathInRepo: expanded.oldPath } : {}
|
|
37931
|
-
};
|
|
37932
|
-
}
|
|
37933
|
-
function parseNumstatEntries(lines) {
|
|
37934
|
-
const m = /* @__PURE__ */ new Map();
|
|
37935
|
-
for (const line of lines) {
|
|
37936
|
-
if (!line.trim()) continue;
|
|
37937
|
-
const parts = line.split(" ");
|
|
37938
|
-
if (parts.length < 3) continue;
|
|
37939
|
-
const rawPath = parts[parts.length - 1];
|
|
37940
|
-
const expanded = expandGitRenameNumstatPath(rawPath);
|
|
37941
|
-
const entry = parseNumstatLine(line);
|
|
37942
|
-
if (!entry) continue;
|
|
37943
|
-
const existing = m.get(entry.pathInRepo);
|
|
37944
|
-
if (!existing || expanded) {
|
|
37945
|
-
m.set(entry.pathInRepo, entry);
|
|
37946
|
-
}
|
|
37947
|
-
}
|
|
37948
|
-
return m;
|
|
37949
|
-
}
|
|
37950
|
-
function parseNumstat(lines) {
|
|
37951
|
-
const m = /* @__PURE__ */ new Map();
|
|
37952
|
-
for (const [path55, entry] of parseNumstatEntries(lines)) {
|
|
37953
|
-
m.set(path55, { additions: entry.additions, deletions: entry.deletions });
|
|
37954
|
-
}
|
|
37955
|
-
return m;
|
|
37956
|
-
}
|
|
37957
|
-
|
|
37958
|
-
// src/git/changes/parse/numstat-from-git-no-index.ts
|
|
37959
|
-
async function numstatFromGitNoIndex(g, pathInRepo) {
|
|
37960
|
-
const devNull = process.platform === "win32" ? "NUL" : "/dev/null";
|
|
37961
|
-
try {
|
|
37962
|
-
const out = await g.raw(["diff", "--numstat", "--no-index", "--", devNull, pathInRepo]);
|
|
37963
|
-
const first2 = String(out).split("\n").find((l) => l.trim()) ?? "";
|
|
37964
|
-
const parsed = parseNumstatLine(first2);
|
|
37965
|
-
if (!parsed) return null;
|
|
37966
|
-
return { additions: parsed.additions, deletions: parsed.deletions };
|
|
37967
|
-
} catch {
|
|
37968
|
-
return null;
|
|
37969
|
-
}
|
|
37970
|
-
}
|
|
37971
|
-
|
|
37972
37707
|
// src/git/changes/rows/collect-moved-source-paths.ts
|
|
37973
37708
|
function collectMovedSourcePaths(nameEntries, numByPath) {
|
|
37974
37709
|
const sources = /* @__PURE__ */ new Set();
|
|
@@ -38048,8 +37783,8 @@ function pickPreferredChangedFileRow(a, b) {
|
|
|
38048
37783
|
const aHasPatch = Boolean(a.patchContent?.trim());
|
|
38049
37784
|
const bHasPatch = Boolean(b.patchContent?.trim());
|
|
38050
37785
|
if (aHasPatch !== bHasPatch) return aHasPatch ? a : b;
|
|
38051
|
-
const aHasMoveMeta = Boolean(a.movedFromPathInRepo || a.
|
|
38052
|
-
const bHasMoveMeta = Boolean(b.movedFromPathInRepo || b.
|
|
37786
|
+
const aHasMoveMeta = Boolean(a.movedFromPathInRepo || a.movedFromWorkspaceRelPath);
|
|
37787
|
+
const bHasMoveMeta = Boolean(b.movedFromPathInRepo || b.movedFromWorkspaceRelPath);
|
|
38053
37788
|
if (aHasMoveMeta !== bHasMoveMeta) return aHasMoveMeta ? a : b;
|
|
38054
37789
|
return a;
|
|
38055
37790
|
}
|
|
@@ -38058,30 +37793,39 @@ function pickPreferredChangedFileRow(a, b) {
|
|
|
38058
37793
|
function dedupeChangedFileRows(rows) {
|
|
38059
37794
|
const byPath = /* @__PURE__ */ new Map();
|
|
38060
37795
|
for (const row of rows) {
|
|
38061
|
-
const key = normalizeGitDiffPath(row.
|
|
38062
|
-
const normalizedRow = key === row.
|
|
37796
|
+
const key = normalizeGitDiffPath(row.workspaceRelPath);
|
|
37797
|
+
const normalizedRow = key === row.workspaceRelPath ? row : { ...row, workspaceRelPath: key };
|
|
38063
37798
|
const existing = byPath.get(key);
|
|
38064
37799
|
if (!existing) {
|
|
38065
37800
|
byPath.set(key, normalizedRow);
|
|
38066
37801
|
continue;
|
|
38067
37802
|
}
|
|
38068
37803
|
const preferRow = pickPreferredChangedFileRow(existing, normalizedRow);
|
|
38069
|
-
byPath.set(key, { ...preferRow,
|
|
37804
|
+
byPath.set(key, { ...preferRow, workspaceRelPath: key });
|
|
38070
37805
|
}
|
|
38071
37806
|
return [...byPath.values()];
|
|
38072
37807
|
}
|
|
38073
37808
|
|
|
38074
37809
|
// src/git/changes/listing/list-changed-files-for-commit.ts
|
|
38075
|
-
|
|
37810
|
+
var COMMIT_FILES_CACHE_TTL_MS = 5 * 6e4;
|
|
37811
|
+
var commitFilesCache = /* @__PURE__ */ new Map();
|
|
37812
|
+
function commitFilesCacheKey(repoGitCwd, commitSha) {
|
|
37813
|
+
return `${path25.resolve(repoGitCwd)}:${commitSha}`;
|
|
37814
|
+
}
|
|
37815
|
+
async function listChangedFilesForCommit(repoGitCwd, repoRelPath, commitSha, options = {}) {
|
|
37816
|
+
const cacheKey = commitFilesCacheKey(repoGitCwd, commitSha);
|
|
37817
|
+
if (!options.includeFilePatches) {
|
|
37818
|
+
const now = Date.now();
|
|
37819
|
+
const cached2 = commitFilesCache.get(cacheKey);
|
|
37820
|
+
if (cached2 && cached2.expiresAt > now) return cached2.rows;
|
|
37821
|
+
}
|
|
38076
37822
|
const g = cliSimpleGit(repoGitCwd);
|
|
38077
|
-
const
|
|
38078
|
-
const range = `${parent}..${commitSha}`;
|
|
37823
|
+
const normRel = normalizeRepoRelPath(repoRelPath);
|
|
38079
37824
|
const [nameStatusRaw, numstatRaw] = await Promise.all([
|
|
38080
|
-
g.raw(["diff",
|
|
38081
|
-
g.raw(["
|
|
37825
|
+
g.raw(["diff-tree", "--no-commit-id", "--name-status", "-r", ...RENAME_DIFF_ARGS, commitSha]).catch(() => ""),
|
|
37826
|
+
g.raw(["show", "--numstat", "--format=format:", commitSha]).catch(() => "")
|
|
38082
37827
|
]);
|
|
38083
37828
|
const parsed = parseDiffOutput(String(nameStatusRaw), String(numstatRaw));
|
|
38084
|
-
const normRel = normalizeRepoRelPath2(repoRelPath);
|
|
38085
37829
|
const paths = collectChangedPaths({
|
|
38086
37830
|
nameEntries: parsed.nameEntries,
|
|
38087
37831
|
numByPath: parsed.numByPath
|
|
@@ -38094,29 +37838,39 @@ async function listChangedFilesForCommit(repoGitCwd, repoRelPath, commitSha) {
|
|
|
38094
37838
|
movedSourcePaths: parsed.movedSourcePaths
|
|
38095
37839
|
});
|
|
38096
37840
|
rows = dedupeChangedFileRows(rows);
|
|
38097
|
-
|
|
38098
|
-
|
|
38099
|
-
|
|
38100
|
-
|
|
38101
|
-
|
|
38102
|
-
|
|
38103
|
-
|
|
38104
|
-
|
|
38105
|
-
|
|
37841
|
+
if (options.includeFilePatches) {
|
|
37842
|
+
const parent = await parentForCommitDiff(g, commitSha);
|
|
37843
|
+
const range = `${parent}..${commitSha}`;
|
|
37844
|
+
await enrichCommitFileRows({
|
|
37845
|
+
rows,
|
|
37846
|
+
repoGitCwd,
|
|
37847
|
+
repoRelPath: normRel,
|
|
37848
|
+
range,
|
|
37849
|
+
commitSha,
|
|
37850
|
+
parentSha: parent,
|
|
37851
|
+
binaryByPath: parsed.binaryByPath
|
|
37852
|
+
});
|
|
37853
|
+
}
|
|
38106
37854
|
const finalRows = dedupeChangedFileRows(rows);
|
|
38107
|
-
finalRows.sort((a, b) => a.
|
|
37855
|
+
finalRows.sort((a, b) => a.workspaceRelPath.localeCompare(b.workspaceRelPath));
|
|
37856
|
+
if (!options.includeFilePatches) {
|
|
37857
|
+
commitFilesCache.set(cacheKey, {
|
|
37858
|
+
rows: finalRows,
|
|
37859
|
+
expiresAt: Date.now() + COMMIT_FILES_CACHE_TTL_MS
|
|
37860
|
+
});
|
|
37861
|
+
}
|
|
38108
37862
|
return finalRows;
|
|
38109
37863
|
}
|
|
38110
37864
|
|
|
38111
37865
|
// src/git/changes/listing/apply-untracked-file-stats.ts
|
|
38112
|
-
import * as
|
|
38113
|
-
import * as
|
|
37866
|
+
import * as fs19 from "node:fs";
|
|
37867
|
+
import * as path26 from "node:path";
|
|
38114
37868
|
function createUntrackedStatsApplier(options) {
|
|
38115
37869
|
return async (row, pathInRepo) => {
|
|
38116
37870
|
if (!options.untrackedSet.has(pathInRepo)) return;
|
|
38117
37871
|
if (options.nameByPath.has(pathInRepo)) return;
|
|
38118
37872
|
if (row.change === "moved") return;
|
|
38119
|
-
const repoFilePath =
|
|
37873
|
+
const repoFilePath = path26.join(options.repoGitCwd, pathInRepo);
|
|
38120
37874
|
const fromGit = await numstatFromGitNoIndex(options.g, pathInRepo);
|
|
38121
37875
|
if (fromGit) {
|
|
38122
37876
|
row.additions = fromGit.additions;
|
|
@@ -38124,7 +37878,7 @@ function createUntrackedStatsApplier(options) {
|
|
|
38124
37878
|
return;
|
|
38125
37879
|
}
|
|
38126
37880
|
try {
|
|
38127
|
-
const st = await
|
|
37881
|
+
const st = await fs19.promises.stat(repoFilePath);
|
|
38128
37882
|
row.additions = st.isFile() ? await countTextFileLines(repoFilePath) : 0;
|
|
38129
37883
|
} catch {
|
|
38130
37884
|
row.additions = 0;
|
|
@@ -38134,7 +37888,7 @@ function createUntrackedStatsApplier(options) {
|
|
|
38134
37888
|
}
|
|
38135
37889
|
|
|
38136
37890
|
// src/git/changes/listing/enrich-working-tree-file-rows.ts
|
|
38137
|
-
import * as
|
|
37891
|
+
import * as path27 from "node:path";
|
|
38138
37892
|
|
|
38139
37893
|
// src/git/changes/patch/hydrate/append-injected-context-lines.ts
|
|
38140
37894
|
async function appendInjectedContextLines(out, inject, maybeYield, injectedTotal) {
|
|
@@ -38214,10 +37968,10 @@ async function readGitBlobLines(repoCwd, pathInRepo) {
|
|
|
38214
37968
|
}
|
|
38215
37969
|
|
|
38216
37970
|
// src/git/changes/patch/hydrate/read-worktree-file-lines.ts
|
|
38217
|
-
import * as
|
|
37971
|
+
import * as fs20 from "node:fs";
|
|
38218
37972
|
async function readWorktreeFileLines(filePath) {
|
|
38219
37973
|
try {
|
|
38220
|
-
const raw = await
|
|
37974
|
+
const raw = await fs20.promises.readFile(filePath, "utf8");
|
|
38221
37975
|
return raw.split(/\r?\n/);
|
|
38222
37976
|
} catch {
|
|
38223
37977
|
return null;
|
|
@@ -38313,8 +38067,8 @@ async function hydrateUnifiedPatchWithFileContext(patch, filePath, repoGitCwd, p
|
|
|
38313
38067
|
// src/git/changes/listing/enrich-working-tree-file-rows.ts
|
|
38314
38068
|
async function enrichWorkingTreeFileRows(options) {
|
|
38315
38069
|
await forEachWithGitYield(options.rows, async (row) => {
|
|
38316
|
-
const pathInRepo =
|
|
38317
|
-
const filePath =
|
|
38070
|
+
const pathInRepo = pathInRepoFromWorkspace(row.workspaceRelPath, options.repoRelPath);
|
|
38071
|
+
const filePath = path27.join(options.repoGitCwd, pathInRepo);
|
|
38318
38072
|
const hydrateKind = row.change === "moved" ? "modified" : row.change;
|
|
38319
38073
|
let patch = await unifiedDiffForFile(options.repoGitCwd, pathInRepo, row.change, row.movedFromPathInRepo);
|
|
38320
38074
|
if (patch) {
|
|
@@ -38337,7 +38091,7 @@ async function enrichWorkingTreeFileRows(options) {
|
|
|
38337
38091
|
}
|
|
38338
38092
|
|
|
38339
38093
|
// src/git/changes/listing/list-changed-files-for-repo.ts
|
|
38340
|
-
async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
38094
|
+
async function listChangedFilesForRepo(repoGitCwd, repoRelPath, options = {}) {
|
|
38341
38095
|
const g = cliSimpleGit(repoGitCwd);
|
|
38342
38096
|
const [nameStatusRaw, numstatRaw, untrackedRaw] = await Promise.all([
|
|
38343
38097
|
g.raw(["diff", ...RENAME_DIFF_ARGS, "--name-status", "HEAD"]).catch(() => ""),
|
|
@@ -38367,12 +38121,14 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
|
38367
38121
|
})
|
|
38368
38122
|
});
|
|
38369
38123
|
rows = dedupeChangedFileRows(rows);
|
|
38370
|
-
|
|
38371
|
-
|
|
38372
|
-
|
|
38373
|
-
|
|
38374
|
-
|
|
38375
|
-
|
|
38124
|
+
if (options.includeFilePatches) {
|
|
38125
|
+
await enrichWorkingTreeFileRows({
|
|
38126
|
+
rows,
|
|
38127
|
+
repoGitCwd,
|
|
38128
|
+
repoRelPath,
|
|
38129
|
+
binaryByPath: parsed.binaryByPath
|
|
38130
|
+
});
|
|
38131
|
+
}
|
|
38376
38132
|
return dedupeChangedFileRows(rows);
|
|
38377
38133
|
}
|
|
38378
38134
|
|
|
@@ -38383,48 +38139,62 @@ function normRepoRel(p) {
|
|
|
38383
38139
|
}
|
|
38384
38140
|
|
|
38385
38141
|
// src/git/changes/repo/build-working-tree-change-repo-detail.ts
|
|
38142
|
+
async function loadRecentAndUnpushedCommits(repoDir, recentCommitsLimit) {
|
|
38143
|
+
const [unpushedCommits, recentCommitList] = await Promise.all([
|
|
38144
|
+
listUnpushedCommits(repoDir),
|
|
38145
|
+
listRecentCommitRefs(repoDir, recentCommitsLimit)
|
|
38146
|
+
]);
|
|
38147
|
+
const unpushedSet = new Set(unpushedCommits.map((c) => c.sha));
|
|
38148
|
+
return {
|
|
38149
|
+
unpushedCommits,
|
|
38150
|
+
recentCommits: recentCommitList.commits.map((c) => ({
|
|
38151
|
+
...c,
|
|
38152
|
+
needsPush: unpushedSet.has(c.sha)
|
|
38153
|
+
})),
|
|
38154
|
+
recentCommitsHasMore: recentCommitList.hasMore
|
|
38155
|
+
};
|
|
38156
|
+
}
|
|
38386
38157
|
async function buildWorkingTreeChangeRepoDetail(options) {
|
|
38387
|
-
const t =
|
|
38158
|
+
const t = path28.resolve(options.targetPath);
|
|
38388
38159
|
if (!await isGitRepoDirectory(t)) return null;
|
|
38389
38160
|
const g = cliSimpleGit(t);
|
|
38390
|
-
|
|
38391
|
-
|
|
38392
|
-
|
|
38393
|
-
branch = (await g.raw(["rev-parse", "--abbrev-ref", "HEAD"])).trim() || "HEAD";
|
|
38394
|
-
} catch {
|
|
38395
|
-
branch = "HEAD";
|
|
38396
|
-
}
|
|
38161
|
+
const isCommitView = options.basis.kind === "commit";
|
|
38162
|
+
const sessionWtRoot = options.sessionWorktreeRootPath ? path28.resolve(options.sessionWorktreeRootPath) : null;
|
|
38163
|
+
const legacyNested = options.legacyRepoNestedSessionLayout;
|
|
38397
38164
|
await yieldToEventLoop2();
|
|
38398
|
-
const
|
|
38165
|
+
const branchPromise = g.raw(["rev-parse", "--abbrev-ref", "HEAD"]).then((out) => out.trim() || "HEAD").catch(() => "HEAD");
|
|
38166
|
+
const remoteUrlPromise = getRemoteOriginUrl(t);
|
|
38167
|
+
const topLevelPromise = sessionWtRoot ? Promise.resolve(t) : g.raw(["rev-parse", "--show-toplevel"]).then((out) => out.trim()).catch(() => t);
|
|
38168
|
+
const [branch, remoteUrl, topOrT] = await Promise.all([
|
|
38169
|
+
branchPromise,
|
|
38170
|
+
remoteUrlPromise,
|
|
38171
|
+
topLevelPromise
|
|
38172
|
+
]);
|
|
38399
38173
|
const remoteDisplay = formatRemoteDisplayLabel(remoteUrl);
|
|
38400
|
-
const sessionWtRoot = options.sessionWorktreeRootPath ? path33.resolve(options.sessionWorktreeRootPath) : null;
|
|
38401
|
-
const legacyNested = options.legacyRepoNestedSessionLayout;
|
|
38402
38174
|
let repoRelPath;
|
|
38403
38175
|
if (sessionWtRoot) {
|
|
38404
|
-
const anchor = legacyNested ?
|
|
38405
|
-
const relNorm =
|
|
38176
|
+
const anchor = legacyNested ? path28.dirname(t) : t;
|
|
38177
|
+
const relNorm = path28.relative(sessionWtRoot, anchor);
|
|
38406
38178
|
repoRelPath = relNorm === "" ? "." : relNorm.replace(/\\/g, "/");
|
|
38407
38179
|
} else {
|
|
38408
|
-
|
|
38409
|
-
|
|
38410
|
-
|
|
38411
|
-
top = (await g.raw(["rev-parse", "--show-toplevel"])).trim();
|
|
38412
|
-
} catch {
|
|
38413
|
-
top = t;
|
|
38414
|
-
}
|
|
38415
|
-
const rel = path33.relative(options.bridgeRoot, path33.resolve(top)).replace(/\\/g, "/") || ".";
|
|
38416
|
-
repoRelPath = rel.startsWith("..") ? path33.basename(path33.resolve(top)) : rel;
|
|
38180
|
+
const top = topOrT;
|
|
38181
|
+
const rel = path28.relative(options.bridgeRoot, path28.resolve(top)).replace(/\\/g, "/") || ".";
|
|
38182
|
+
repoRelPath = rel.startsWith("..") ? path28.basename(path28.resolve(top)) : rel;
|
|
38417
38183
|
}
|
|
38418
38184
|
const norm = normRepoRel(repoRelPath === "" ? "." : repoRelPath);
|
|
38419
38185
|
if (options.repoFilterRelPath && norm !== options.repoFilterRelPath) return null;
|
|
38420
38186
|
const repoDisplayName = formatRepoShortTitle(remoteUrl, norm === "." ? "." : norm);
|
|
38421
38187
|
const relForList = norm === "." ? "." : norm;
|
|
38422
|
-
const
|
|
38423
|
-
const
|
|
38424
|
-
|
|
38425
|
-
|
|
38426
|
-
|
|
38427
|
-
|
|
38188
|
+
const commitSha = isCommitView ? options.basis.sha.trim() : null;
|
|
38189
|
+
const filesPromise = isCommitView && commitSha ? listChangedFilesForCommit(t, relForList, commitSha, {
|
|
38190
|
+
includeFilePatches: options.includeFilePatches
|
|
38191
|
+
}) : listChangedFilesForRepo(t, relForList, { includeFilePatches: options.includeFilePatches });
|
|
38192
|
+
const statusPromise = isCommitView ? Promise.resolve(false) : g.status().then((st) => (st.files?.length ?? 0) > 0);
|
|
38193
|
+
const commitsPromise = isCommitView ? Promise.resolve(null) : loadRecentAndUnpushedCommits(t, options.recentCommitsLimit);
|
|
38194
|
+
const [files, hasUncommittedChanges, commitData] = await Promise.all([
|
|
38195
|
+
filesPromise,
|
|
38196
|
+
statusPromise,
|
|
38197
|
+
commitsPromise
|
|
38428
38198
|
]);
|
|
38429
38199
|
return {
|
|
38430
38200
|
repoRelPath: norm,
|
|
@@ -38434,18 +38204,18 @@ async function buildWorkingTreeChangeRepoDetail(options) {
|
|
|
38434
38204
|
remoteDisplay,
|
|
38435
38205
|
files,
|
|
38436
38206
|
hasUncommittedChanges,
|
|
38437
|
-
unpushedCommits,
|
|
38438
|
-
recentCommits:
|
|
38439
|
-
recentCommitsHasMore:
|
|
38440
|
-
changesView:
|
|
38441
|
-
changesCommitSha:
|
|
38207
|
+
unpushedCommits: commitData?.unpushedCommits ?? [],
|
|
38208
|
+
recentCommits: commitData?.recentCommits ?? [],
|
|
38209
|
+
recentCommitsHasMore: commitData?.recentCommitsHasMore ?? false,
|
|
38210
|
+
changesView: isCommitView ? "commit" : "working",
|
|
38211
|
+
changesCommitSha: commitSha
|
|
38442
38212
|
};
|
|
38443
38213
|
}
|
|
38444
38214
|
|
|
38445
38215
|
// src/git/changes/repo/get-working-tree-change-repo-details.ts
|
|
38446
38216
|
async function getWorkingTreeChangeRepoDetails(options) {
|
|
38447
|
-
const bridgeRoot =
|
|
38448
|
-
const sessionWtRoot = options.sessionWorktreeRootPath ?
|
|
38217
|
+
const bridgeRoot = path29.resolve(getBridgeRoot());
|
|
38218
|
+
const sessionWtRoot = options.sessionWorktreeRootPath ? path29.resolve(options.sessionWorktreeRootPath) : null;
|
|
38449
38219
|
const legacyNested = options.legacyRepoNestedSessionLayout === true;
|
|
38450
38220
|
const out = [];
|
|
38451
38221
|
const filter = options.repoFilterRelPath != null ? normRepoRel(options.repoFilterRelPath) : null;
|
|
@@ -38459,14 +38229,16 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
38459
38229
|
const basis = filter == null && basisInput.kind === "commit" ? { kind: "working" } : basisInput;
|
|
38460
38230
|
for (let i = 0; i < options.commitTargetPaths.length; i++) {
|
|
38461
38231
|
await yieldToEventLoop2();
|
|
38232
|
+
const targetPath = options.commitTargetPaths[i];
|
|
38462
38233
|
const detail = await buildWorkingTreeChangeRepoDetail({
|
|
38463
|
-
targetPath
|
|
38234
|
+
targetPath,
|
|
38464
38235
|
bridgeRoot,
|
|
38465
38236
|
sessionWorktreeRootPath: sessionWtRoot,
|
|
38466
38237
|
legacyRepoNestedSessionLayout: legacyNested,
|
|
38467
38238
|
repoFilterRelPath: filter,
|
|
38468
38239
|
basis,
|
|
38469
|
-
recentCommitsLimit: options.recentCommitsLimit
|
|
38240
|
+
recentCommitsLimit: options.recentCommitsLimit,
|
|
38241
|
+
includeFilePatches: options.includeFilePatches
|
|
38470
38242
|
});
|
|
38471
38243
|
if (!detail) continue;
|
|
38472
38244
|
out.push(detail);
|
|
@@ -38475,94 +38247,148 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
38475
38247
|
return out;
|
|
38476
38248
|
}
|
|
38477
38249
|
|
|
38478
|
-
// src/git/changes/
|
|
38479
|
-
|
|
38480
|
-
|
|
38481
|
-
const
|
|
38482
|
-
|
|
38483
|
-
|
|
38484
|
-
|
|
38485
|
-
|
|
38486
|
-
|
|
38487
|
-
|
|
38488
|
-
|
|
38489
|
-
|
|
38490
|
-
|
|
38491
|
-
return runGitTask(async () => {
|
|
38250
|
+
// src/git/changes/repo/get-working-tree-change-file-patch.ts
|
|
38251
|
+
import * as path30 from "node:path";
|
|
38252
|
+
async function getWorkingTreeChangeFilePatch(options) {
|
|
38253
|
+
const repoGitCwd = path30.resolve(options.repoGitCwd);
|
|
38254
|
+
if (!await isGitRepoDirectory(repoGitCwd)) {
|
|
38255
|
+
throw new Error("Not a git repository");
|
|
38256
|
+
}
|
|
38257
|
+
const normRel = options.repoRelPath === "." || options.repoRelPath === "" ? "." : options.repoRelPath;
|
|
38258
|
+
const pathInRepo = pathInRepoFromWorkspace(options.workspaceRelPath, normRel);
|
|
38259
|
+
const movedFromPathInRepo = options.change === "moved" && options.movedFromWorkspaceRelPath ? pathInRepoFromWorkspace(options.movedFromWorkspaceRelPath, normRel) : void 0;
|
|
38260
|
+
if (options.basis.kind === "commit") {
|
|
38261
|
+
const commitSha = options.basis.sha.trim();
|
|
38262
|
+
if (!commitSha) throw new Error("commit sha is required for commit file patch");
|
|
38492
38263
|
const g = cliSimpleGit(repoGitCwd);
|
|
38493
|
-
const
|
|
38494
|
-
|
|
38495
|
-
|
|
38496
|
-
|
|
38497
|
-
|
|
38498
|
-
|
|
38499
|
-
|
|
38500
|
-
|
|
38501
|
-
String(untrackedRaw).split("\n")
|
|
38264
|
+
const parentSha = await parentForCommitDiff(g, commitSha);
|
|
38265
|
+
const range = `${parentSha}..${commitSha}`;
|
|
38266
|
+
const patchContent2 = await unifiedDiffForFileInRange(
|
|
38267
|
+
repoGitCwd,
|
|
38268
|
+
range,
|
|
38269
|
+
pathInRepo,
|
|
38270
|
+
options.change,
|
|
38271
|
+
movedFromPathInRepo
|
|
38502
38272
|
);
|
|
38273
|
+
const isBinary = Boolean(patchContent2?.includes("Binary files"));
|
|
38274
|
+
const totalLines2 = await resolveCommitFileTotalLines({
|
|
38275
|
+
repoGitCwd,
|
|
38276
|
+
pathInRepo,
|
|
38277
|
+
change: options.change,
|
|
38278
|
+
commitSha,
|
|
38279
|
+
parentSha,
|
|
38280
|
+
isBinary
|
|
38281
|
+
});
|
|
38282
|
+
return { patchContent: patchContent2, totalLines: totalLines2 };
|
|
38283
|
+
}
|
|
38284
|
+
const filePath = path30.join(repoGitCwd, pathInRepo);
|
|
38285
|
+
const hydrateKind = options.change === "moved" ? "modified" : options.change;
|
|
38286
|
+
let patchContent = await unifiedDiffForFile(
|
|
38287
|
+
repoGitCwd,
|
|
38288
|
+
pathInRepo,
|
|
38289
|
+
options.change,
|
|
38290
|
+
movedFromPathInRepo
|
|
38291
|
+
);
|
|
38292
|
+
if (patchContent) {
|
|
38293
|
+
patchContent = await hydrateUnifiedPatchWithFileContext(
|
|
38294
|
+
patchContent,
|
|
38295
|
+
filePath,
|
|
38296
|
+
repoGitCwd,
|
|
38297
|
+
pathInRepo,
|
|
38298
|
+
hydrateKind
|
|
38299
|
+
);
|
|
38300
|
+
}
|
|
38301
|
+
const totalLines = await resolveWorkingTreeFileTotalLines({
|
|
38302
|
+
repoGitCwd,
|
|
38303
|
+
pathInRepo,
|
|
38304
|
+
change: options.change,
|
|
38305
|
+
isBinary: Boolean(patchContent?.includes("Binary files"))
|
|
38503
38306
|
});
|
|
38307
|
+
return { patchContent, totalLines };
|
|
38504
38308
|
}
|
|
38505
38309
|
|
|
38506
|
-
// src/git/
|
|
38507
|
-
async function
|
|
38508
|
-
|
|
38509
|
-
|
|
38510
|
-
|
|
38511
|
-
|
|
38512
|
-
|
|
38513
|
-
|
|
38514
|
-
|
|
38515
|
-
|
|
38516
|
-
|
|
38517
|
-
let hasUnpushedCommits = false;
|
|
38518
|
-
let uncommittedFileCount = 0;
|
|
38519
|
-
await forEachWithGitYield(paths, async (p) => {
|
|
38520
|
-
const s = await getRepoWorkingTreeStatus(p);
|
|
38521
|
-
uncommittedFileCount += s.uncommittedFileCount;
|
|
38522
|
-
if (s.hasUncommittedChanges) hasUncommittedChanges = true;
|
|
38523
|
-
if (s.hasUnpushedCommits) hasUnpushedCommits = true;
|
|
38310
|
+
// src/worktrees/manager/git/get-session-working-tree-change-details.ts
|
|
38311
|
+
async function getSessionWorkingTreeChangeDetails(cache2, sessionId, discover, opts) {
|
|
38312
|
+
const targets = await resolveCommitTargetsAsync(sessionId, cache2, discover);
|
|
38313
|
+
return getWorkingTreeChangeRepoDetails({
|
|
38314
|
+
commitTargetPaths: targets,
|
|
38315
|
+
sessionWorktreeRootPath: cache2.getWorkingTreeRelRoot(sessionId),
|
|
38316
|
+
legacyRepoNestedSessionLayout: cache2.isLegacyNestedLayout(sessionId),
|
|
38317
|
+
repoFilterRelPath: opts?.repoRelPath?.trim() ? normRepoRel(opts.repoRelPath.trim()) : null,
|
|
38318
|
+
basis: opts?.basis,
|
|
38319
|
+
recentCommitsLimit: opts?.recentCommitsLimit,
|
|
38320
|
+
includeFilePatches: false
|
|
38524
38321
|
});
|
|
38525
|
-
return { hasUncommittedChanges, hasUnpushedCommits, uncommittedFileCount };
|
|
38526
38322
|
}
|
|
38527
|
-
|
|
38528
|
-
|
|
38529
|
-
|
|
38530
|
-
|
|
38531
|
-
|
|
38532
|
-
await g.push();
|
|
38533
|
-
});
|
|
38323
|
+
|
|
38324
|
+
// src/worktrees/manager/git/parse-working-tree-change-kind.ts
|
|
38325
|
+
function parseWorkingTreeChangeKind(value) {
|
|
38326
|
+
if (value === "added" || value === "removed" || value === "modified" || value === "moved") return value;
|
|
38327
|
+
return null;
|
|
38534
38328
|
}
|
|
38535
38329
|
|
|
38536
|
-
// src/git/
|
|
38537
|
-
|
|
38538
|
-
|
|
38539
|
-
|
|
38540
|
-
|
|
38541
|
-
|
|
38542
|
-
|
|
38543
|
-
|
|
38544
|
-
|
|
38545
|
-
const
|
|
38546
|
-
|
|
38547
|
-
if (
|
|
38548
|
-
|
|
38330
|
+
// src/worktrees/manager/git/get-session-working-tree-change-file-patch.ts
|
|
38331
|
+
import * as path32 from "node:path";
|
|
38332
|
+
|
|
38333
|
+
// src/git/changes/repo/resolve-checkout-repo-rel-path.ts
|
|
38334
|
+
import * as path31 from "node:path";
|
|
38335
|
+
async function resolveCheckoutRepoRelPath(options) {
|
|
38336
|
+
const t = path31.resolve(options.checkoutPath);
|
|
38337
|
+
const g = cliSimpleGit(t);
|
|
38338
|
+
const sessionWtRoot = options.sessionWorktreeRootPath ? path31.resolve(options.sessionWorktreeRootPath) : null;
|
|
38339
|
+
const legacyNested = options.legacyRepoNestedSessionLayout;
|
|
38340
|
+
let repoRelPath;
|
|
38341
|
+
if (sessionWtRoot) {
|
|
38342
|
+
const anchor = legacyNested ? path31.dirname(t) : t;
|
|
38343
|
+
const relNorm = path31.relative(sessionWtRoot, anchor);
|
|
38344
|
+
repoRelPath = relNorm === "" ? "." : relNorm.replace(/\\/g, "/");
|
|
38549
38345
|
} else {
|
|
38550
|
-
|
|
38346
|
+
let top = t;
|
|
38347
|
+
try {
|
|
38348
|
+
await yieldToEventLoop2();
|
|
38349
|
+
top = (await g.raw(["rev-parse", "--show-toplevel"])).trim();
|
|
38350
|
+
} catch {
|
|
38351
|
+
top = t;
|
|
38352
|
+
}
|
|
38353
|
+
const rel = path31.relative(options.bridgeRoot, path31.resolve(top)).replace(/\\/g, "/") || ".";
|
|
38354
|
+
repoRelPath = rel.startsWith("..") ? path31.basename(path31.resolve(top)) : rel;
|
|
38551
38355
|
}
|
|
38552
|
-
|
|
38553
|
-
|
|
38554
|
-
|
|
38555
|
-
|
|
38356
|
+
return normRepoRel(repoRelPath === "" ? "." : repoRelPath);
|
|
38357
|
+
}
|
|
38358
|
+
|
|
38359
|
+
// src/worktrees/manager/git/get-session-working-tree-change-file-patch.ts
|
|
38360
|
+
async function getSessionWorkingTreeChangeFilePatch(cache2, sessionId, discover, opts) {
|
|
38361
|
+
const targets = await resolveCommitTargetsAsync(sessionId, cache2, discover);
|
|
38362
|
+
const bridgeRoot = path32.resolve(getBridgeRoot());
|
|
38363
|
+
const sessionWtRoot = cache2.getWorkingTreeRelRoot(sessionId) ? path32.resolve(cache2.getWorkingTreeRelRoot(sessionId)) : null;
|
|
38364
|
+
const legacyNested = cache2.isLegacyNestedLayout(sessionId);
|
|
38365
|
+
const filter = normRepoRel(opts.repoRelPath.trim());
|
|
38366
|
+
for (const targetPath of targets) {
|
|
38367
|
+
const checkoutPath = path32.resolve(targetPath);
|
|
38368
|
+
if (!await isGitRepoDirectory(checkoutPath)) continue;
|
|
38369
|
+
const repoRelPath = await resolveCheckoutRepoRelPath({
|
|
38370
|
+
checkoutPath,
|
|
38371
|
+
bridgeRoot,
|
|
38372
|
+
sessionWorktreeRootPath: sessionWtRoot,
|
|
38373
|
+
legacyRepoNestedSessionLayout: legacyNested
|
|
38374
|
+
});
|
|
38375
|
+
if (repoRelPath !== filter) continue;
|
|
38376
|
+
return getWorkingTreeChangeFilePatch({
|
|
38377
|
+
repoGitCwd: checkoutPath,
|
|
38378
|
+
repoRelPath: filter,
|
|
38379
|
+
workspaceRelPath: opts.workspaceRelPath.trim(),
|
|
38380
|
+
change: opts.change,
|
|
38381
|
+
movedFromWorkspaceRelPath: opts.movedFromWorkspaceRelPath?.trim() || null,
|
|
38382
|
+
basis: opts.basis
|
|
38383
|
+
});
|
|
38556
38384
|
}
|
|
38385
|
+
throw new Error(`Repository not found: ${filter}`);
|
|
38557
38386
|
}
|
|
38558
38387
|
|
|
38559
|
-
// src/worktrees/
|
|
38560
|
-
async function
|
|
38561
|
-
const { paths, branch, message, push } = options;
|
|
38388
|
+
// src/worktrees/manager/git/push-session-upstream.ts
|
|
38389
|
+
async function pushSessionUpstream(cache2, sessionId, discover) {
|
|
38562
38390
|
try {
|
|
38563
|
-
|
|
38564
|
-
await gitCommitAllIfDirty(wt, message, { push, branch });
|
|
38565
|
-
}
|
|
38391
|
+
await pushAheadOfUpstreamForPaths(await resolveCommitTargetsAsync(sessionId, cache2, discover));
|
|
38566
38392
|
return { ok: true };
|
|
38567
38393
|
} catch (e) {
|
|
38568
38394
|
const err = e instanceof Error ? e.message : String(e);
|
|
@@ -38571,23 +38397,23 @@ async function commitSessionWorktrees(options) {
|
|
|
38571
38397
|
}
|
|
38572
38398
|
|
|
38573
38399
|
// src/worktrees/remove-session-worktrees.ts
|
|
38574
|
-
import * as
|
|
38400
|
+
import * as fs23 from "node:fs";
|
|
38575
38401
|
|
|
38576
38402
|
// src/git/worktrees/worktree-remove.ts
|
|
38577
|
-
import * as
|
|
38403
|
+
import * as fs22 from "node:fs";
|
|
38578
38404
|
|
|
38579
38405
|
// src/git/worktrees/resolve-main-repo-from-git-file.ts
|
|
38580
|
-
import * as
|
|
38581
|
-
import * as
|
|
38406
|
+
import * as fs21 from "node:fs";
|
|
38407
|
+
import * as path33 from "node:path";
|
|
38582
38408
|
function resolveMainRepoFromWorktreeGitFile(wt) {
|
|
38583
|
-
const gitDirFile =
|
|
38584
|
-
if (!
|
|
38585
|
-
const first2 =
|
|
38409
|
+
const gitDirFile = path33.join(wt, ".git");
|
|
38410
|
+
if (!fs21.existsSync(gitDirFile) || !fs21.statSync(gitDirFile).isFile()) return "";
|
|
38411
|
+
const first2 = fs21.readFileSync(gitDirFile, "utf8").trim();
|
|
38586
38412
|
const m = first2.match(/^gitdir:\s*(.+)$/im);
|
|
38587
38413
|
if (!m) return "";
|
|
38588
|
-
const gitWorktreePath =
|
|
38589
|
-
const gitDir =
|
|
38590
|
-
return
|
|
38414
|
+
const gitWorktreePath = path33.resolve(wt, m[1].trim());
|
|
38415
|
+
const gitDir = path33.dirname(path33.dirname(gitWorktreePath));
|
|
38416
|
+
return path33.dirname(gitDir);
|
|
38591
38417
|
}
|
|
38592
38418
|
|
|
38593
38419
|
// src/git/worktrees/worktree-remove.ts
|
|
@@ -38596,7 +38422,7 @@ async function gitWorktreeRemoveForce(worktreePath) {
|
|
|
38596
38422
|
if (mainRepo) {
|
|
38597
38423
|
await cliSimpleGit(mainRepo).raw(["worktree", "remove", "--force", worktreePath]);
|
|
38598
38424
|
} else {
|
|
38599
|
-
|
|
38425
|
+
fs22.rmSync(worktreePath, { recursive: true, force: true });
|
|
38600
38426
|
}
|
|
38601
38427
|
}
|
|
38602
38428
|
|
|
@@ -38609,13 +38435,20 @@ async function removeSessionWorktrees(paths, log2) {
|
|
|
38609
38435
|
} catch (e) {
|
|
38610
38436
|
log2(`[worktrees] Remove failed for ${wt}: ${e instanceof Error ? e.message : String(e)}`);
|
|
38611
38437
|
try {
|
|
38612
|
-
|
|
38438
|
+
fs23.rmSync(wt, { recursive: true, force: true });
|
|
38613
38439
|
} catch {
|
|
38614
38440
|
}
|
|
38615
38441
|
}
|
|
38616
38442
|
}
|
|
38617
38443
|
}
|
|
38618
38444
|
|
|
38445
|
+
// src/worktrees/manager/git/remove-session-worktree-checkouts.ts
|
|
38446
|
+
async function removeSessionWorktreeCheckouts(cache2, sessionId, log2) {
|
|
38447
|
+
const paths = cache2.clearSession(sessionId);
|
|
38448
|
+
if (!paths?.length) return;
|
|
38449
|
+
await removeSessionWorktrees(paths, log2);
|
|
38450
|
+
}
|
|
38451
|
+
|
|
38619
38452
|
// src/git/branches/rename-branch.ts
|
|
38620
38453
|
async function gitRenameCurrentBranch(repoDir, newName) {
|
|
38621
38454
|
const g = cliSimpleGit(repoDir);
|
|
@@ -38637,216 +38470,623 @@ async function renameSessionWorktreeBranches(paths, newBranch, log2) {
|
|
|
38637
38470
|
}
|
|
38638
38471
|
}
|
|
38639
38472
|
|
|
38640
|
-
// src/worktrees/manager/
|
|
38641
|
-
function
|
|
38642
|
-
|
|
38643
|
-
|
|
38644
|
-
|
|
38645
|
-
repoCheckoutPaths: [bridgeRoot]
|
|
38646
|
-
};
|
|
38647
|
-
}
|
|
38648
|
-
async function resolveCommitTargetsAsync(sessionId, cache2, discover) {
|
|
38649
|
-
const sid = sessionId.trim();
|
|
38650
|
-
const paths = cache2.getRepoCheckoutPathsRef(sid);
|
|
38651
|
-
if (paths?.length) return paths;
|
|
38652
|
-
const disc = await discover(sid);
|
|
38653
|
-
if (disc?.repoCheckoutPaths.length) {
|
|
38654
|
-
cache2.remember(sid, disc);
|
|
38655
|
-
return disc.repoCheckoutPaths;
|
|
38656
|
-
}
|
|
38657
|
-
const bridgeRoot = getBridgeRoot();
|
|
38658
|
-
cache2.remember(sid, bridgeRootBinding(bridgeRoot));
|
|
38659
|
-
return [bridgeRoot];
|
|
38473
|
+
// src/worktrees/manager/git/rename-session-worktree-branch.ts
|
|
38474
|
+
async function renameSessionWorktreeBranch(cache2, sessionId, newBranch, log2) {
|
|
38475
|
+
const paths = cache2.getRepoCheckoutPathsRef(sessionId);
|
|
38476
|
+
if (!paths?.length) return;
|
|
38477
|
+
await renameSessionWorktreeBranches(paths, newBranch, log2);
|
|
38660
38478
|
}
|
|
38661
38479
|
|
|
38662
|
-
// src/worktrees/
|
|
38663
|
-
|
|
38664
|
-
|
|
38665
|
-
|
|
38666
|
-
|
|
38667
|
-
|
|
38668
|
-
|
|
38669
|
-
|
|
38670
|
-
|
|
38671
|
-
|
|
38672
|
-
|
|
38673
|
-
|
|
38674
|
-
|
|
38675
|
-
|
|
38676
|
-
|
|
38480
|
+
// src/worktrees/discovery/discover-session-worktree-on-disk.ts
|
|
38481
|
+
import * as fs27 from "node:fs";
|
|
38482
|
+
import * as path38 from "node:path";
|
|
38483
|
+
|
|
38484
|
+
// src/worktrees/discovery/collect-worktree-paths.ts
|
|
38485
|
+
import * as fs25 from "node:fs";
|
|
38486
|
+
import * as path35 from "node:path";
|
|
38487
|
+
|
|
38488
|
+
// src/worktrees/discovery/disk-walk-constants.ts
|
|
38489
|
+
var DISK_WALK_YIELD_EVERY = 64;
|
|
38490
|
+
var LEGACY_WALK_MAX_DEPTH = 12;
|
|
38491
|
+
var SKIP_DISK_WALK_DIR_NAMES = /* @__PURE__ */ new Set([
|
|
38492
|
+
"node_modules",
|
|
38493
|
+
"bower_components",
|
|
38494
|
+
"vendor",
|
|
38495
|
+
"Pods",
|
|
38496
|
+
"Carthage",
|
|
38497
|
+
"DerivedData",
|
|
38498
|
+
".yarn",
|
|
38499
|
+
".pnpm-store",
|
|
38500
|
+
"dist",
|
|
38501
|
+
"build",
|
|
38502
|
+
".next",
|
|
38503
|
+
"coverage",
|
|
38504
|
+
".turbo",
|
|
38505
|
+
".cache",
|
|
38506
|
+
"target",
|
|
38507
|
+
"__pycache__"
|
|
38508
|
+
]);
|
|
38509
|
+
function shouldSkipDiskWalkEntry(name) {
|
|
38510
|
+
return name.startsWith(".") || SKIP_DISK_WALK_DIR_NAMES.has(name);
|
|
38677
38511
|
}
|
|
38678
|
-
|
|
38679
|
-
|
|
38680
|
-
|
|
38681
|
-
|
|
38682
|
-
|
|
38683
|
-
|
|
38684
|
-
|
|
38685
|
-
|
|
38686
|
-
|
|
38687
|
-
|
|
38512
|
+
|
|
38513
|
+
// src/worktrees/discovery/disk-walk-utils.ts
|
|
38514
|
+
import * as fs24 from "node:fs";
|
|
38515
|
+
import * as path34 from "node:path";
|
|
38516
|
+
async function yieldDuringDiskWalk(state) {
|
|
38517
|
+
state.entries++;
|
|
38518
|
+
if (state.entries % DISK_WALK_YIELD_EVERY !== 0) return true;
|
|
38519
|
+
if (isCliImmediateShutdownRequested()) return false;
|
|
38520
|
+
await yieldToEventLoop();
|
|
38521
|
+
return !isCliImmediateShutdownRequested();
|
|
38688
38522
|
}
|
|
38689
|
-
async function
|
|
38523
|
+
async function isGitDir(dirPath) {
|
|
38690
38524
|
try {
|
|
38691
|
-
await
|
|
38692
|
-
return
|
|
38693
|
-
} catch
|
|
38694
|
-
|
|
38695
|
-
return { ok: false, error: err };
|
|
38525
|
+
await fs24.promises.access(path34.join(dirPath, ".git"));
|
|
38526
|
+
return true;
|
|
38527
|
+
} catch {
|
|
38528
|
+
return false;
|
|
38696
38529
|
}
|
|
38697
38530
|
}
|
|
38698
|
-
|
|
38699
|
-
|
|
38700
|
-
|
|
38701
|
-
|
|
38531
|
+
|
|
38532
|
+
// src/worktrees/discovery/collect-worktree-paths.ts
|
|
38533
|
+
async function collectGitRepoRootsUnderDirectory(rootPath) {
|
|
38534
|
+
const out = [];
|
|
38535
|
+
const state = { entries: 0 };
|
|
38536
|
+
const walk = async (dir) => {
|
|
38537
|
+
if (!await yieldDuringDiskWalk(state)) return;
|
|
38538
|
+
if (await isGitDir(dir)) {
|
|
38539
|
+
out.push(path35.resolve(dir));
|
|
38540
|
+
return;
|
|
38541
|
+
}
|
|
38542
|
+
let entries;
|
|
38543
|
+
try {
|
|
38544
|
+
entries = await fs25.promises.readdir(dir, { withFileTypes: true });
|
|
38545
|
+
} catch {
|
|
38546
|
+
return;
|
|
38547
|
+
}
|
|
38548
|
+
for (const e of entries) {
|
|
38549
|
+
if (shouldSkipDiskWalkEntry(e.name)) continue;
|
|
38550
|
+
const full = path35.join(dir, e.name);
|
|
38551
|
+
if (!e.isDirectory()) continue;
|
|
38552
|
+
await walk(full);
|
|
38553
|
+
}
|
|
38554
|
+
};
|
|
38555
|
+
await walk(path35.resolve(rootPath));
|
|
38556
|
+
return { paths: [...new Set(out)], entriesVisited: state.entries };
|
|
38702
38557
|
}
|
|
38703
|
-
async function
|
|
38704
|
-
const
|
|
38705
|
-
|
|
38706
|
-
|
|
38558
|
+
async function collectWorktreeRootsNamed(root, sessionId, maxDepth = LEGACY_WALK_MAX_DEPTH) {
|
|
38559
|
+
const out = [];
|
|
38560
|
+
const state = { entries: 0 };
|
|
38561
|
+
const walk = async (dir, depth) => {
|
|
38562
|
+
if (depth > maxDepth) return;
|
|
38563
|
+
if (!await yieldDuringDiskWalk(state)) return;
|
|
38564
|
+
let entries;
|
|
38565
|
+
try {
|
|
38566
|
+
entries = await fs25.promises.readdir(dir, { withFileTypes: true });
|
|
38567
|
+
} catch {
|
|
38568
|
+
return;
|
|
38569
|
+
}
|
|
38570
|
+
for (const e of entries) {
|
|
38571
|
+
if (shouldSkipDiskWalkEntry(e.name)) continue;
|
|
38572
|
+
const full = path35.join(dir, e.name);
|
|
38573
|
+
if (!e.isDirectory()) continue;
|
|
38574
|
+
if (e.name === sessionId) {
|
|
38575
|
+
if (await isGitDir(full)) out.push(path35.resolve(full));
|
|
38576
|
+
continue;
|
|
38577
|
+
}
|
|
38578
|
+
if (await isGitDir(full)) continue;
|
|
38579
|
+
await walk(full, depth + 1);
|
|
38580
|
+
}
|
|
38581
|
+
};
|
|
38582
|
+
await walk(root, 0);
|
|
38583
|
+
return { paths: out, entriesVisited: state.entries };
|
|
38707
38584
|
}
|
|
38708
38585
|
|
|
38709
|
-
// src/worktrees/
|
|
38586
|
+
// src/worktrees/discovery/layout-keys-for-bridge-root.ts
|
|
38710
38587
|
import * as path36 from "node:path";
|
|
38711
|
-
|
|
38712
|
-
|
|
38713
|
-
|
|
38714
|
-
|
|
38715
|
-
|
|
38716
|
-
|
|
38717
|
-
|
|
38718
|
-
|
|
38719
|
-
|
|
38720
|
-
|
|
38721
|
-
|
|
38722
|
-
|
|
38723
|
-
|
|
38724
|
-
|
|
38725
|
-
|
|
38726
|
-
|
|
38727
|
-
|
|
38728
|
-
|
|
38729
|
-
return this.sessionParentPathBySession.get(sessionId);
|
|
38730
|
-
}
|
|
38731
|
-
getWorkingTreeRelRoot(sessionId) {
|
|
38732
|
-
return this.sessionWorkingTreeRelRootBySession.get(sessionId) ?? null;
|
|
38588
|
+
function layoutKeysForBridgeRoot(layout, bridgeRoot, keysOnDisk) {
|
|
38589
|
+
const bridge = path36.resolve(bridgeRoot);
|
|
38590
|
+
const preferred = getLauncherDirNameIfPresent(layout, bridgeRoot);
|
|
38591
|
+
const relevant = layout.launcherCwds.filter((e) => {
|
|
38592
|
+
const entry = path36.resolve(e.absolutePath);
|
|
38593
|
+
return bridge === entry || bridge.startsWith(entry + path36.sep) || entry.startsWith(bridge + path36.sep);
|
|
38594
|
+
}).sort((a, b) => path36.resolve(b.absolutePath).length - path36.resolve(a.absolutePath).length).map((e) => e.dirName).filter((name) => keysOnDisk.includes(name));
|
|
38595
|
+
const ordered = [];
|
|
38596
|
+
const seen = /* @__PURE__ */ new Set();
|
|
38597
|
+
const add = (k) => {
|
|
38598
|
+
if (seen.has(k) || !keysOnDisk.includes(k)) return;
|
|
38599
|
+
seen.add(k);
|
|
38600
|
+
ordered.push(k);
|
|
38601
|
+
};
|
|
38602
|
+
if (preferred) add(preferred);
|
|
38603
|
+
for (const k of relevant) add(k);
|
|
38604
|
+
if (relevant.length > 0) {
|
|
38605
|
+
return ordered;
|
|
38733
38606
|
}
|
|
38734
|
-
|
|
38735
|
-
|
|
38607
|
+
for (const k of keysOnDisk) add(k);
|
|
38608
|
+
return ordered;
|
|
38609
|
+
}
|
|
38610
|
+
|
|
38611
|
+
// src/worktrees/discovery/try-binding-from-session-directory.ts
|
|
38612
|
+
import * as fs26 from "node:fs";
|
|
38613
|
+
import * as path37 from "node:path";
|
|
38614
|
+
async function tryBindingFromSessionDirectory(sessionDir) {
|
|
38615
|
+
let st;
|
|
38616
|
+
try {
|
|
38617
|
+
st = await fs26.promises.stat(sessionDir);
|
|
38618
|
+
} catch {
|
|
38619
|
+
return null;
|
|
38736
38620
|
}
|
|
38737
|
-
|
|
38738
|
-
|
|
38739
|
-
|
|
38621
|
+
if (!st.isDirectory()) return null;
|
|
38622
|
+
const { paths: worktreePaths } = await collectGitRepoRootsUnderDirectory(sessionDir);
|
|
38623
|
+
if (worktreePaths.length === 0) return null;
|
|
38624
|
+
const abs = path37.resolve(sessionDir);
|
|
38625
|
+
return {
|
|
38626
|
+
sessionParentPath: abs,
|
|
38627
|
+
workingTreeRelRoot: abs,
|
|
38628
|
+
repoCheckoutPaths: worktreePaths
|
|
38629
|
+
};
|
|
38630
|
+
}
|
|
38631
|
+
|
|
38632
|
+
// src/worktrees/discovery/discover-session-worktree-on-disk.ts
|
|
38633
|
+
async function discoverSessionWorktreeOnDisk(options) {
|
|
38634
|
+
const { sessionId, worktreesRootPath, layout, bridgeRoot } = options;
|
|
38635
|
+
const sid = sessionId.trim();
|
|
38636
|
+
if (!sid) return null;
|
|
38637
|
+
try {
|
|
38638
|
+
await fs27.promises.access(worktreesRootPath);
|
|
38639
|
+
} catch {
|
|
38640
|
+
return null;
|
|
38740
38641
|
}
|
|
38741
|
-
|
|
38742
|
-
|
|
38743
|
-
|
|
38642
|
+
const preferredKey = getLauncherDirNameIfPresent(layout, bridgeRoot);
|
|
38643
|
+
const keysOnDisk = [];
|
|
38644
|
+
if (preferredKey) keysOnDisk.push(preferredKey);
|
|
38645
|
+
try {
|
|
38646
|
+
for (const name of await fs27.promises.readdir(worktreesRootPath)) {
|
|
38647
|
+
if (name.startsWith(".")) continue;
|
|
38648
|
+
const p = path38.join(worktreesRootPath, name);
|
|
38649
|
+
let st;
|
|
38650
|
+
try {
|
|
38651
|
+
st = await fs27.promises.stat(p);
|
|
38652
|
+
} catch {
|
|
38653
|
+
continue;
|
|
38654
|
+
}
|
|
38655
|
+
if (!st.isDirectory()) continue;
|
|
38656
|
+
if (!keysOnDisk.includes(name)) keysOnDisk.push(name);
|
|
38657
|
+
}
|
|
38658
|
+
} catch {
|
|
38659
|
+
return null;
|
|
38744
38660
|
}
|
|
38745
|
-
|
|
38746
|
-
|
|
38747
|
-
|
|
38748
|
-
|
|
38749
|
-
|
|
38661
|
+
const keys = layoutKeysForBridgeRoot(layout, bridgeRoot, keysOnDisk);
|
|
38662
|
+
for (const key of keys) {
|
|
38663
|
+
if (isCliImmediateShutdownRequested()) return null;
|
|
38664
|
+
await yieldToEventLoop();
|
|
38665
|
+
const layoutRoot = path38.join(worktreesRootPath, key);
|
|
38666
|
+
let layoutSt;
|
|
38667
|
+
try {
|
|
38668
|
+
layoutSt = await fs27.promises.stat(layoutRoot);
|
|
38669
|
+
} catch {
|
|
38670
|
+
continue;
|
|
38671
|
+
}
|
|
38672
|
+
if (!layoutSt.isDirectory()) continue;
|
|
38673
|
+
const sessionDir = path38.join(layoutRoot, sid);
|
|
38674
|
+
const nested = await tryBindingFromSessionDirectory(sessionDir);
|
|
38675
|
+
if (nested) return nested;
|
|
38676
|
+
const legacy = await collectWorktreeRootsNamed(layoutRoot, sid);
|
|
38677
|
+
if (legacy.paths.length > 0) {
|
|
38678
|
+
const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacy.paths) ?? path38.resolve(legacy.paths[0]);
|
|
38679
|
+
return {
|
|
38680
|
+
sessionParentPath: path38.resolve(isolated),
|
|
38681
|
+
workingTreeRelRoot: path38.resolve(layoutRoot),
|
|
38682
|
+
repoCheckoutPaths: legacy.paths.map((p) => path38.resolve(p))
|
|
38683
|
+
};
|
|
38684
|
+
}
|
|
38750
38685
|
}
|
|
38751
|
-
|
|
38686
|
+
return null;
|
|
38687
|
+
}
|
|
38752
38688
|
|
|
38753
|
-
// src/worktrees/
|
|
38754
|
-
|
|
38755
|
-
|
|
38756
|
-
|
|
38757
|
-
|
|
38758
|
-
|
|
38759
|
-
|
|
38760
|
-
|
|
38761
|
-
|
|
38762
|
-
|
|
38689
|
+
// src/worktrees/discovery/discover-session-worktrees-under-session-worktree-root.ts
|
|
38690
|
+
import * as fs28 from "node:fs";
|
|
38691
|
+
import * as path40 from "node:path";
|
|
38692
|
+
|
|
38693
|
+
// src/worktrees/discovery/discover-legacy-binding-ascending-from-checkout.ts
|
|
38694
|
+
import * as path39 from "node:path";
|
|
38695
|
+
async function discoverLegacyBindingAscendingFromCheckout(sessionId, checkoutPath) {
|
|
38696
|
+
const sid = sessionId.trim();
|
|
38697
|
+
if (!sid) return null;
|
|
38698
|
+
const hintR = path39.resolve(checkoutPath);
|
|
38699
|
+
let best = null;
|
|
38700
|
+
let cur = path39.dirname(hintR);
|
|
38701
|
+
for (let i = 0; i < 40; i++) {
|
|
38702
|
+
if (isCliImmediateShutdownRequested()) return best;
|
|
38703
|
+
await yieldToEventLoop();
|
|
38704
|
+
const paths = await collectWorktreeRootsNamed(cur, sid);
|
|
38705
|
+
if (paths.paths.some((p) => path39.resolve(p) === hintR)) {
|
|
38706
|
+
const isolated = resolveIsolatedSessionParentPathFromCheckouts(paths.paths) ?? path39.resolve(paths.paths[0]);
|
|
38707
|
+
best = {
|
|
38708
|
+
sessionParentPath: path39.resolve(isolated),
|
|
38709
|
+
workingTreeRelRoot: path39.resolve(cur),
|
|
38710
|
+
repoCheckoutPaths: paths.paths.map((p) => path39.resolve(p))
|
|
38711
|
+
};
|
|
38712
|
+
}
|
|
38713
|
+
const next = path39.dirname(cur);
|
|
38714
|
+
if (next === cur) break;
|
|
38715
|
+
cur = next;
|
|
38763
38716
|
}
|
|
38764
|
-
|
|
38765
|
-
|
|
38766
|
-
|
|
38767
|
-
|
|
38768
|
-
|
|
38769
|
-
|
|
38717
|
+
return best;
|
|
38718
|
+
}
|
|
38719
|
+
|
|
38720
|
+
// src/worktrees/discovery/discover-session-worktrees-under-session-worktree-root.ts
|
|
38721
|
+
async function discoverSessionWorktreesUnderSessionWorktreeRoot(sessionWorktreeRootPathOrHint, sessionId) {
|
|
38722
|
+
const sid = sessionId.trim();
|
|
38723
|
+
if (!sid) return null;
|
|
38724
|
+
const hint = path40.resolve(sessionWorktreeRootPathOrHint);
|
|
38725
|
+
const underHint = await tryBindingFromSessionDirectory(path40.join(hint, sid));
|
|
38726
|
+
if (underHint) return underHint;
|
|
38727
|
+
const direct = await tryBindingFromSessionDirectory(hint);
|
|
38728
|
+
if (direct) {
|
|
38729
|
+
if (path40.basename(hint) === sid && await isGitDir(hint)) {
|
|
38730
|
+
const legacyFromCheckout = await discoverLegacyBindingAscendingFromCheckout(sid, hint);
|
|
38731
|
+
if (legacyFromCheckout && legacyFromCheckout.repoCheckoutPaths.length > direct.repoCheckoutPaths.length) {
|
|
38732
|
+
return legacyFromCheckout;
|
|
38733
|
+
}
|
|
38734
|
+
}
|
|
38735
|
+
return direct;
|
|
38770
38736
|
}
|
|
38771
|
-
|
|
38772
|
-
|
|
38773
|
-
|
|
38774
|
-
this.cache,
|
|
38775
|
-
(sid) => this.ensureRepoCheckoutPathsForSession(sid)
|
|
38776
|
-
);
|
|
38737
|
+
if (path40.basename(hint) === sid && await isGitDir(hint)) {
|
|
38738
|
+
const legacyFromCheckout = await discoverLegacyBindingAscendingFromCheckout(sid, hint);
|
|
38739
|
+
if (legacyFromCheckout) return legacyFromCheckout;
|
|
38777
38740
|
}
|
|
38778
|
-
|
|
38779
|
-
|
|
38780
|
-
|
|
38781
|
-
|
|
38782
|
-
|
|
38783
|
-
layout: this.layout,
|
|
38784
|
-
log: this.log,
|
|
38785
|
-
discover: (sid) => this.discoverAsync(sid),
|
|
38786
|
-
opts
|
|
38787
|
-
});
|
|
38741
|
+
let st;
|
|
38742
|
+
try {
|
|
38743
|
+
st = await fs28.promises.stat(hint);
|
|
38744
|
+
} catch {
|
|
38745
|
+
return null;
|
|
38788
38746
|
}
|
|
38789
|
-
|
|
38790
|
-
|
|
38747
|
+
if (!st.isDirectory()) return null;
|
|
38748
|
+
const legacyPaths = await collectWorktreeRootsNamed(hint, sid);
|
|
38749
|
+
if (legacyPaths.paths.length === 0) return null;
|
|
38750
|
+
const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths.paths) ?? path40.resolve(legacyPaths.paths[0]);
|
|
38751
|
+
return {
|
|
38752
|
+
sessionParentPath: path40.resolve(isolated),
|
|
38753
|
+
workingTreeRelRoot: hint,
|
|
38754
|
+
repoCheckoutPaths: legacyPaths.paths.map((p) => path40.resolve(p))
|
|
38755
|
+
};
|
|
38756
|
+
}
|
|
38757
|
+
|
|
38758
|
+
// src/worktrees/manager/discover-session-binding.ts
|
|
38759
|
+
var discoverInflightBySessionId = /* @__PURE__ */ new Map();
|
|
38760
|
+
function discoverSessionBindingAsync(params) {
|
|
38761
|
+
const sid = params.sessionId.trim();
|
|
38762
|
+
if (!sid) return Promise.resolve(null);
|
|
38763
|
+
const existing = discoverInflightBySessionId.get(sid);
|
|
38764
|
+
if (existing) return existing;
|
|
38765
|
+
const promise2 = discoverSessionWorktreeOnDisk({
|
|
38766
|
+
sessionId: sid,
|
|
38767
|
+
worktreesRootPath: params.worktreesRootPath,
|
|
38768
|
+
layout: params.layout,
|
|
38769
|
+
bridgeRoot: getBridgeRoot()
|
|
38770
|
+
}).finally(() => {
|
|
38771
|
+
if (discoverInflightBySessionId.get(sid) === promise2) {
|
|
38772
|
+
discoverInflightBySessionId.delete(sid);
|
|
38773
|
+
}
|
|
38774
|
+
});
|
|
38775
|
+
discoverInflightBySessionId.set(sid, promise2);
|
|
38776
|
+
return promise2;
|
|
38777
|
+
}
|
|
38778
|
+
|
|
38779
|
+
// src/worktrees/manager/resolve-isolated-session-parent-path.ts
|
|
38780
|
+
function resolveIsolatedSessionParentPath(sessionId, cache2, ensureRepoCheckoutPaths) {
|
|
38781
|
+
if (!sessionId) return null;
|
|
38782
|
+
const sid = sessionId.trim();
|
|
38783
|
+
const cached2 = cache2.getSessionParentPath(sid);
|
|
38784
|
+
if (cached2) return cached2;
|
|
38785
|
+
const paths = ensureRepoCheckoutPaths(sid);
|
|
38786
|
+
if (!paths?.length) return null;
|
|
38787
|
+
return resolveIsolatedSessionParentPathFromCheckouts(paths);
|
|
38788
|
+
}
|
|
38789
|
+
function ensureRepoCheckoutPathsForSession(sessionId, cache2) {
|
|
38790
|
+
if (!sessionId?.trim()) return void 0;
|
|
38791
|
+
const cached2 = cache2.getRepoCheckoutPaths(sessionId.trim());
|
|
38792
|
+
return cached2?.length ? [...cached2] : void 0;
|
|
38793
|
+
}
|
|
38794
|
+
async function ensureRepoCheckoutPathsForSessionAsync(sessionId, cache2, discover) {
|
|
38795
|
+
if (!sessionId?.trim()) return void 0;
|
|
38796
|
+
const sid = sessionId.trim();
|
|
38797
|
+
const cached2 = cache2.getRepoCheckoutPaths(sid);
|
|
38798
|
+
if (cached2?.length) return cached2;
|
|
38799
|
+
const disc = await discover(sid);
|
|
38800
|
+
if (disc?.repoCheckoutPaths.length) {
|
|
38801
|
+
cache2.remember(sid, disc);
|
|
38802
|
+
return [...disc.repoCheckoutPaths];
|
|
38791
38803
|
}
|
|
38792
|
-
|
|
38793
|
-
|
|
38794
|
-
|
|
38804
|
+
return void 0;
|
|
38805
|
+
}
|
|
38806
|
+
|
|
38807
|
+
// src/worktrees/manager/parse-session-parent.ts
|
|
38808
|
+
function parseSessionParent(v) {
|
|
38809
|
+
if (v === "bridge_root" || v === "worktrees_root") return v;
|
|
38810
|
+
if (v === "session_worktrees_root") return "worktrees_root";
|
|
38811
|
+
return null;
|
|
38812
|
+
}
|
|
38813
|
+
|
|
38814
|
+
// src/worktrees/prepare-new-session-worktrees.ts
|
|
38815
|
+
import * as fs29 from "node:fs";
|
|
38816
|
+
import * as path41 from "node:path";
|
|
38817
|
+
|
|
38818
|
+
// src/git/worktrees/worktree-add.ts
|
|
38819
|
+
async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch, baseRef = "HEAD") {
|
|
38820
|
+
const mainGit = cliSimpleGit(mainRepoPath);
|
|
38821
|
+
const base = baseRef.trim() || "HEAD";
|
|
38822
|
+
await mainGit.raw(["worktree", "add", "-b", branch, worktreePath, base]);
|
|
38823
|
+
}
|
|
38824
|
+
|
|
38825
|
+
// src/worktrees/prepare-new-session-worktrees.ts
|
|
38826
|
+
function normalizeRepoRelPath2(rel) {
|
|
38827
|
+
return rel === "" ? "." : rel.replace(/\\/g, "/");
|
|
38828
|
+
}
|
|
38829
|
+
function resolveBaseRefForRepo(relNorm, baseBranches) {
|
|
38830
|
+
if (!baseBranches) return "HEAD";
|
|
38831
|
+
const direct = baseBranches[relNorm]?.trim();
|
|
38832
|
+
if (direct) return direct;
|
|
38833
|
+
if (relNorm !== "." && baseBranches["."]?.trim()) return baseBranches["."].trim();
|
|
38834
|
+
return "HEAD";
|
|
38835
|
+
}
|
|
38836
|
+
async function prepareNewSessionWorktrees(options) {
|
|
38837
|
+
const { worktreesRootPath, bridgeRoot, sessionId, layout, log: log2, worktreeBaseBranches } = options;
|
|
38838
|
+
const bridgeResolved = path41.resolve(bridgeRoot);
|
|
38839
|
+
const cwdKey = allocateDirNameForLauncherCwd(layout, bridgeResolved);
|
|
38840
|
+
const bridgeKeyDir = path41.join(worktreesRootPath, cwdKey);
|
|
38841
|
+
const sessionDir = path41.join(bridgeKeyDir, sessionId);
|
|
38842
|
+
const repos = await discoverGitReposUnderRoot(bridgeResolved);
|
|
38843
|
+
if (repos.length === 0) {
|
|
38844
|
+
log2("[worktrees] No Git repositories under bridge root; skipping worktree creation.");
|
|
38845
|
+
return null;
|
|
38795
38846
|
}
|
|
38796
|
-
|
|
38797
|
-
|
|
38798
|
-
|
|
38847
|
+
const branch = `session-${sessionId}`;
|
|
38848
|
+
const worktreePaths = [];
|
|
38849
|
+
fs29.mkdirSync(sessionDir, { recursive: true });
|
|
38850
|
+
for (const repo of repos) {
|
|
38851
|
+
let rel = path41.relative(bridgeResolved, repo.absolutePath);
|
|
38852
|
+
if (rel.startsWith("..") || path41.isAbsolute(rel)) continue;
|
|
38853
|
+
const relNorm = normalizeRepoRelPath2(rel === "" ? "." : rel);
|
|
38854
|
+
const wtPath = relNorm === "." ? sessionDir : path41.join(sessionDir, relNorm);
|
|
38855
|
+
if (relNorm !== ".") {
|
|
38856
|
+
fs29.mkdirSync(path41.dirname(wtPath), { recursive: true });
|
|
38857
|
+
}
|
|
38858
|
+
const baseRef = resolveBaseRefForRepo(relNorm, worktreeBaseBranches);
|
|
38859
|
+
try {
|
|
38860
|
+
await gitWorktreeAddBranch(repo.absolutePath, wtPath, branch, baseRef);
|
|
38861
|
+
log2(`[worktrees] Added worktree ${wtPath} (branch ${branch}, base ${baseRef}).`);
|
|
38862
|
+
worktreePaths.push(wtPath);
|
|
38863
|
+
} catch (e) {
|
|
38864
|
+
log2(
|
|
38865
|
+
`[worktrees] Worktree add failed for ${repo.absolutePath}: ${e instanceof Error ? e.message : String(e)}`
|
|
38866
|
+
);
|
|
38867
|
+
}
|
|
38799
38868
|
}
|
|
38800
|
-
|
|
38801
|
-
|
|
38869
|
+
if (worktreePaths.length === 0) return null;
|
|
38870
|
+
return {
|
|
38871
|
+
worktreePaths,
|
|
38872
|
+
sessionParentPath: sessionDir,
|
|
38873
|
+
workingTreeRelRoot: sessionDir
|
|
38874
|
+
};
|
|
38875
|
+
}
|
|
38876
|
+
|
|
38877
|
+
// src/worktrees/manager/prepare-and-remember-session-worktrees.ts
|
|
38878
|
+
async function prepareAndRememberSessionWorktrees(params) {
|
|
38879
|
+
const prep = await prepareNewSessionWorktrees({
|
|
38880
|
+
worktreesRootPath: params.worktreesRootPath,
|
|
38881
|
+
bridgeRoot: getBridgeRoot(),
|
|
38882
|
+
sessionId: params.sessionId,
|
|
38883
|
+
layout: params.layout,
|
|
38884
|
+
log: params.log,
|
|
38885
|
+
...params.worktreeBaseBranches && Object.keys(params.worktreeBaseBranches).length > 0 ? { worktreeBaseBranches: params.worktreeBaseBranches } : {}
|
|
38886
|
+
});
|
|
38887
|
+
if (!prep) return void 0;
|
|
38888
|
+
params.cache.remember(params.sessionId, {
|
|
38889
|
+
sessionParentPath: prep.sessionParentPath,
|
|
38890
|
+
workingTreeRelRoot: prep.workingTreeRelRoot,
|
|
38891
|
+
repoCheckoutPaths: prep.worktreePaths
|
|
38892
|
+
});
|
|
38893
|
+
return params.cache.getSessionParentPath(params.sessionId);
|
|
38894
|
+
}
|
|
38895
|
+
|
|
38896
|
+
// src/worktrees/manager/resolve-existing-session-parent-path.ts
|
|
38897
|
+
async function resolveExistingSessionParentPath(sessionId, cache2, discover) {
|
|
38898
|
+
const cached2 = cache2.getSessionParentPath(sessionId);
|
|
38899
|
+
if (cached2) return cached2;
|
|
38900
|
+
const disc = await discover();
|
|
38901
|
+
if (disc) {
|
|
38902
|
+
cache2.remember(sessionId, disc);
|
|
38903
|
+
return cache2.getSessionParentPath(sessionId);
|
|
38802
38904
|
}
|
|
38803
|
-
|
|
38804
|
-
|
|
38905
|
+
return void 0;
|
|
38906
|
+
}
|
|
38907
|
+
|
|
38908
|
+
// src/worktrees/manager/resolve-explicit-session-parent-path.ts
|
|
38909
|
+
import * as path42 from "node:path";
|
|
38910
|
+
async function resolveExplicitSessionParentPath(params) {
|
|
38911
|
+
const resolved = path42.resolve(params.parentPathRaw);
|
|
38912
|
+
if (parseSessionParent(params.sessionParent) !== "worktrees_root") {
|
|
38913
|
+
return resolved;
|
|
38805
38914
|
}
|
|
38806
|
-
|
|
38807
|
-
|
|
38915
|
+
const rememberAndReturn = (binding) => {
|
|
38916
|
+
params.cache.remember(params.sessionId, binding);
|
|
38917
|
+
return params.cache.getSessionParentPath(params.sessionId) ?? resolved;
|
|
38918
|
+
};
|
|
38919
|
+
const diskFirst = await params.discover();
|
|
38920
|
+
if (diskFirst) return rememberAndReturn(diskFirst);
|
|
38921
|
+
const fromRoot = await discoverSessionWorktreesUnderSessionWorktreeRoot(resolved, params.sessionId);
|
|
38922
|
+
if (fromRoot) return rememberAndReturn(fromRoot);
|
|
38923
|
+
let cur = resolved;
|
|
38924
|
+
for (let i = 0; i < 16; i++) {
|
|
38925
|
+
await yieldToEventLoop();
|
|
38926
|
+
const tryRoot = await discoverSessionWorktreesUnderSessionWorktreeRoot(cur, params.sessionId);
|
|
38927
|
+
if (tryRoot) return rememberAndReturn(tryRoot);
|
|
38928
|
+
const next = path42.dirname(cur);
|
|
38929
|
+
if (next === cur) break;
|
|
38930
|
+
cur = next;
|
|
38808
38931
|
}
|
|
38809
|
-
|
|
38810
|
-
|
|
38932
|
+
return resolved;
|
|
38933
|
+
}
|
|
38934
|
+
|
|
38935
|
+
// src/worktrees/manager/resolve-session-parent-path-for-prompt.ts
|
|
38936
|
+
async function resolveSessionParentPathForPrompt(params) {
|
|
38937
|
+
const { sessionId, cache: cache2, worktreesRootPath, layout, log: log2, discover, opts } = params;
|
|
38938
|
+
if (!sessionId) return void 0;
|
|
38939
|
+
const sid = sessionId.trim();
|
|
38940
|
+
const parentPathRaw = opts.sessionParentPath?.trim();
|
|
38941
|
+
if (parentPathRaw) {
|
|
38942
|
+
return resolveExplicitSessionParentPath({
|
|
38943
|
+
sessionId: sid,
|
|
38944
|
+
sessionParent: opts.sessionParent,
|
|
38945
|
+
parentPathRaw,
|
|
38946
|
+
cache: cache2,
|
|
38947
|
+
discover: () => discover(sid)
|
|
38948
|
+
});
|
|
38811
38949
|
}
|
|
38812
|
-
|
|
38813
|
-
|
|
38950
|
+
const parentKind = parseSessionParent(opts.sessionParent);
|
|
38951
|
+
if (parentKind === "bridge_root") {
|
|
38952
|
+
return void 0;
|
|
38814
38953
|
}
|
|
38815
|
-
|
|
38816
|
-
|
|
38954
|
+
if (parentKind === "worktrees_root") {
|
|
38955
|
+
if (!opts.isNewSession) {
|
|
38956
|
+
return resolveExistingSessionParentPath(sid, cache2, () => discover(sid));
|
|
38957
|
+
}
|
|
38958
|
+
return prepareAndRememberSessionWorktrees({
|
|
38959
|
+
cache: cache2,
|
|
38960
|
+
sessionId: sid,
|
|
38961
|
+
worktreesRootPath,
|
|
38962
|
+
layout,
|
|
38963
|
+
log: log2,
|
|
38964
|
+
...opts.worktreeBaseBranches ? { worktreeBaseBranches: opts.worktreeBaseBranches } : {}
|
|
38965
|
+
});
|
|
38817
38966
|
}
|
|
38818
|
-
|
|
38819
|
-
return
|
|
38967
|
+
if (!opts.isNewSession) {
|
|
38968
|
+
return resolveExistingSessionParentPath(sid, cache2, () => discover(sid));
|
|
38820
38969
|
}
|
|
38821
|
-
|
|
38822
|
-
|
|
38970
|
+
return prepareAndRememberSessionWorktrees({
|
|
38971
|
+
cache: cache2,
|
|
38972
|
+
sessionId: sid,
|
|
38973
|
+
worktreesRootPath,
|
|
38974
|
+
layout,
|
|
38975
|
+
log: log2
|
|
38976
|
+
});
|
|
38977
|
+
}
|
|
38978
|
+
|
|
38979
|
+
// src/worktrees/manager/session-worktree-paths.ts
|
|
38980
|
+
function createSessionWorktreePathsApi(ctx) {
|
|
38981
|
+
const discoverAsync = (sessionId) => discoverSessionBindingAsync({
|
|
38982
|
+
sessionId,
|
|
38983
|
+
worktreesRootPath: ctx.worktreesRootPath,
|
|
38984
|
+
layout: ctx.layout
|
|
38985
|
+
});
|
|
38986
|
+
return {
|
|
38987
|
+
discoverAsync,
|
|
38988
|
+
getIsolatedSessionParentPathForSession(sessionId) {
|
|
38989
|
+
return resolveIsolatedSessionParentPath(
|
|
38990
|
+
sessionId,
|
|
38991
|
+
ctx.cache,
|
|
38992
|
+
(sid) => ensureRepoCheckoutPathsForSession(sid, ctx.cache)
|
|
38993
|
+
);
|
|
38994
|
+
},
|
|
38995
|
+
resolveSessionParentPathForPrompt(sessionId, opts) {
|
|
38996
|
+
return resolveSessionParentPathForPrompt({
|
|
38997
|
+
sessionId,
|
|
38998
|
+
cache: ctx.cache,
|
|
38999
|
+
worktreesRootPath: ctx.worktreesRootPath,
|
|
39000
|
+
layout: ctx.layout,
|
|
39001
|
+
log: ctx.log,
|
|
39002
|
+
discover: (sid) => discoverAsync(sid),
|
|
39003
|
+
opts
|
|
39004
|
+
});
|
|
39005
|
+
},
|
|
39006
|
+
usesWorktreeSession(sessionId) {
|
|
39007
|
+
if (!sessionId) return false;
|
|
39008
|
+
return ctx.cache.hasSession(sessionId);
|
|
39009
|
+
},
|
|
39010
|
+
getRepoCheckoutPathsForSession(sessionId) {
|
|
39011
|
+
if (!sessionId) return void 0;
|
|
39012
|
+
return ctx.cache.getRepoCheckoutPaths(sessionId);
|
|
39013
|
+
},
|
|
39014
|
+
ensureRepoCheckoutPathsForSession(sessionId) {
|
|
39015
|
+
return ensureRepoCheckoutPathsForSession(sessionId, ctx.cache);
|
|
39016
|
+
},
|
|
39017
|
+
async ensureRepoCheckoutPathsForSessionAsync(sessionId) {
|
|
39018
|
+
return ensureRepoCheckoutPathsForSessionAsync(sessionId, ctx.cache, (sid) => discoverAsync(sid));
|
|
39019
|
+
},
|
|
39020
|
+
getSessionWorktreeRootForSession(sessionId) {
|
|
39021
|
+
return resolveIsolatedSessionParentPath(
|
|
39022
|
+
sessionId,
|
|
39023
|
+
ctx.cache,
|
|
39024
|
+
(sid) => ensureRepoCheckoutPathsForSession(sid, ctx.cache)
|
|
39025
|
+
);
|
|
39026
|
+
}
|
|
39027
|
+
};
|
|
39028
|
+
}
|
|
39029
|
+
|
|
39030
|
+
// src/worktrees/manager/session-worktree-git.ts
|
|
39031
|
+
function createSessionWorktreeGitApi(ctx) {
|
|
39032
|
+
const paths = createSessionWorktreePathsApi(ctx);
|
|
39033
|
+
return {
|
|
39034
|
+
async renameSessionBranch(sessionId, newBranch) {
|
|
39035
|
+
await renameSessionWorktreeBranch(ctx.cache, sessionId, newBranch, ctx.log);
|
|
39036
|
+
},
|
|
39037
|
+
async removeSessionWorktrees(sessionId) {
|
|
39038
|
+
await removeSessionWorktreeCheckouts(ctx.cache, sessionId, ctx.log);
|
|
39039
|
+
},
|
|
39040
|
+
async commitSession(params) {
|
|
39041
|
+
return commitSessionWorktree(ctx.cache, params);
|
|
39042
|
+
},
|
|
39043
|
+
async getSessionWorkingTreeStatus(sessionId) {
|
|
39044
|
+
return getSessionWorkingTreeStatus(ctx.cache, sessionId, (sid) => paths.discoverAsync(sid));
|
|
39045
|
+
},
|
|
39046
|
+
async getSessionWorkingTreeChangeDetails(sessionId, opts) {
|
|
39047
|
+
return getSessionWorkingTreeChangeDetails(ctx.cache, sessionId, (sid) => paths.discoverAsync(sid), opts);
|
|
39048
|
+
},
|
|
39049
|
+
async getSessionWorkingTreeChangeFilePatch(sessionId, opts) {
|
|
39050
|
+
return getSessionWorkingTreeChangeFilePatch(ctx.cache, sessionId, (sid) => paths.discoverAsync(sid), opts);
|
|
39051
|
+
},
|
|
39052
|
+
async pushSessionUpstream(sessionId) {
|
|
39053
|
+
return pushSessionUpstream(ctx.cache, sessionId, (sid) => paths.discoverAsync(sid));
|
|
39054
|
+
}
|
|
39055
|
+
};
|
|
39056
|
+
}
|
|
39057
|
+
|
|
39058
|
+
// src/worktrees/manager/session-worktree-manager.ts
|
|
39059
|
+
var SessionWorktreeManager = class {
|
|
39060
|
+
constructor(options) {
|
|
39061
|
+
const ctx = createSessionWorktreeManagerContext(options);
|
|
39062
|
+
Object.assign(this, createSessionWorktreePathsApi(ctx), createSessionWorktreeGitApi(ctx));
|
|
38823
39063
|
}
|
|
38824
39064
|
};
|
|
38825
39065
|
|
|
38826
39066
|
// src/worktrees/manager/default-worktrees-root-path.ts
|
|
38827
|
-
import * as
|
|
39067
|
+
import * as path43 from "node:path";
|
|
38828
39068
|
import os8 from "node:os";
|
|
38829
39069
|
function defaultWorktreesRootPath() {
|
|
38830
|
-
return
|
|
39070
|
+
return path43.join(os8.homedir(), ".buildautomaton", "worktrees");
|
|
38831
39071
|
}
|
|
38832
39072
|
|
|
38833
39073
|
// src/files/watch-file-index.ts
|
|
38834
39074
|
import { watch } from "node:fs";
|
|
38835
|
-
import
|
|
39075
|
+
import path48 from "node:path";
|
|
38836
39076
|
|
|
38837
39077
|
// src/files/index/paths.ts
|
|
38838
|
-
import
|
|
39078
|
+
import path44 from "node:path";
|
|
38839
39079
|
import crypto2 from "node:crypto";
|
|
38840
39080
|
function getCwdHashForFileIndex(resolvedCwd) {
|
|
38841
|
-
return crypto2.createHash("sha256").update(
|
|
39081
|
+
return crypto2.createHash("sha256").update(path44.resolve(resolvedCwd)).digest("hex").slice(0, INDEX_HASH_LEN);
|
|
38842
39082
|
}
|
|
38843
39083
|
|
|
38844
39084
|
// src/files/index/build-file-index.ts
|
|
38845
|
-
import
|
|
39085
|
+
import path46 from "node:path";
|
|
38846
39086
|
|
|
38847
39087
|
// src/files/index/walk-workspace-tree.ts
|
|
38848
39088
|
import fs30 from "node:fs";
|
|
38849
|
-
import
|
|
39089
|
+
import path45 from "node:path";
|
|
38850
39090
|
var DEPENDENCY_INSTALL_DIR_NAMES = /* @__PURE__ */ new Set([
|
|
38851
39091
|
"node_modules",
|
|
38852
39092
|
"bower_components",
|
|
@@ -38875,18 +39115,18 @@ async function walkWorkspaceTreeAsync(dir, baseDir, onFile, state) {
|
|
|
38875
39115
|
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
38876
39116
|
}
|
|
38877
39117
|
state.n++;
|
|
38878
|
-
const full =
|
|
39118
|
+
const full = path45.join(dir, name);
|
|
38879
39119
|
let stat3;
|
|
38880
39120
|
try {
|
|
38881
39121
|
stat3 = await fs30.promises.stat(full);
|
|
38882
39122
|
} catch {
|
|
38883
39123
|
continue;
|
|
38884
39124
|
}
|
|
38885
|
-
const
|
|
39125
|
+
const relative7 = path45.relative(baseDir, full).replace(/\\/g, "/");
|
|
38886
39126
|
if (stat3.isDirectory()) {
|
|
38887
39127
|
await walkWorkspaceTreeAsync(full, baseDir, onFile, state);
|
|
38888
39128
|
} else if (stat3.isFile()) {
|
|
38889
|
-
onFile(
|
|
39129
|
+
onFile(relative7);
|
|
38890
39130
|
}
|
|
38891
39131
|
}
|
|
38892
39132
|
}
|
|
@@ -38984,7 +39224,7 @@ async function collectWorkspacePathsAsync(resolved) {
|
|
|
38984
39224
|
}
|
|
38985
39225
|
async function buildFileIndexAsync(cwd) {
|
|
38986
39226
|
return withFileIndexSqliteLock(async () => {
|
|
38987
|
-
const resolved =
|
|
39227
|
+
const resolved = path46.resolve(cwd);
|
|
38988
39228
|
await yieldToEventLoop();
|
|
38989
39229
|
assertNotShutdown();
|
|
38990
39230
|
const paths = await collectWorkspacePathsAsync(resolved);
|
|
@@ -38996,7 +39236,7 @@ async function buildFileIndexAsync(cwd) {
|
|
|
38996
39236
|
}
|
|
38997
39237
|
|
|
38998
39238
|
// src/files/index/ensure-file-index.ts
|
|
38999
|
-
import
|
|
39239
|
+
import path47 from "node:path";
|
|
39000
39240
|
|
|
39001
39241
|
// src/files/index/search-file-index.ts
|
|
39002
39242
|
function escapeLikePattern(fragment) {
|
|
@@ -39048,7 +39288,7 @@ async function searchBridgeFilePathsAsync(resolvedCwd, query, limit = 100) {
|
|
|
39048
39288
|
|
|
39049
39289
|
// src/files/index/ensure-file-index.ts
|
|
39050
39290
|
async function ensureFileIndexAsync(cwd) {
|
|
39051
|
-
const resolved =
|
|
39291
|
+
const resolved = path47.resolve(cwd);
|
|
39052
39292
|
if (await bridgeFileIndexIsPopulated(resolved)) {
|
|
39053
39293
|
return { fromCache: true, pathCount: await bridgeFileIndexPathCount(resolved) };
|
|
39054
39294
|
}
|
|
@@ -39092,7 +39332,7 @@ function createFsWatcher(resolved, schedule) {
|
|
|
39092
39332
|
}
|
|
39093
39333
|
}
|
|
39094
39334
|
function startFileIndexWatcher(cwd = getBridgeRoot()) {
|
|
39095
|
-
const resolved =
|
|
39335
|
+
const resolved = path48.resolve(cwd);
|
|
39096
39336
|
void buildFileIndexAsync(resolved).catch((e) => {
|
|
39097
39337
|
if (e instanceof CliSqliteInterrupted) return;
|
|
39098
39338
|
console.error("[file-index] Initial index build failed:", e);
|
|
@@ -39122,7 +39362,7 @@ function startFileIndexWatcher(cwd = getBridgeRoot()) {
|
|
|
39122
39362
|
}
|
|
39123
39363
|
|
|
39124
39364
|
// src/connection/create-bridge-connection.ts
|
|
39125
|
-
import * as
|
|
39365
|
+
import * as path60 from "node:path";
|
|
39126
39366
|
|
|
39127
39367
|
// src/dev-servers/manager/dev-server-manager.ts
|
|
39128
39368
|
import { rm as rm2 } from "node:fs/promises";
|
|
@@ -39144,15 +39384,15 @@ function sendDevServerStatus(getWs, serverId, status, options) {
|
|
|
39144
39384
|
|
|
39145
39385
|
// src/dev-servers/process/terminate-child-process.ts
|
|
39146
39386
|
async function sigtermAndWaitForExit(proc, graceMs, log2, shortId) {
|
|
39147
|
-
const exited = new Promise((
|
|
39148
|
-
proc.once("exit", () =>
|
|
39387
|
+
const exited = new Promise((resolve34) => {
|
|
39388
|
+
proc.once("exit", () => resolve34());
|
|
39149
39389
|
});
|
|
39150
39390
|
log2(`[dev-server] Sending SIGTERM to ${shortId} (pid=${proc.pid ?? "?"}).`);
|
|
39151
39391
|
try {
|
|
39152
39392
|
proc.kill("SIGTERM");
|
|
39153
39393
|
} catch {
|
|
39154
39394
|
}
|
|
39155
|
-
await Promise.race([exited, new Promise((
|
|
39395
|
+
await Promise.race([exited, new Promise((resolve34) => setTimeout(resolve34, graceMs))]);
|
|
39156
39396
|
}
|
|
39157
39397
|
function forceKillChild(proc, log2, shortId, graceMs) {
|
|
39158
39398
|
log2(
|
|
@@ -39432,10 +39672,10 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
|
|
|
39432
39672
|
import { spawn as spawn7 } from "node:child_process";
|
|
39433
39673
|
import fs34 from "node:fs";
|
|
39434
39674
|
import { tmpdir } from "node:os";
|
|
39435
|
-
import
|
|
39675
|
+
import path49 from "node:path";
|
|
39436
39676
|
function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
39437
|
-
const tmpRoot = fs34.mkdtempSync(
|
|
39438
|
-
const logPath =
|
|
39677
|
+
const tmpRoot = fs34.mkdtempSync(path49.join(tmpdir(), "ba-devsrv-log-"));
|
|
39678
|
+
const logPath = path49.join(tmpRoot, "combined.log");
|
|
39439
39679
|
let logFd;
|
|
39440
39680
|
try {
|
|
39441
39681
|
logFd = fs34.openSync(logPath, "a");
|
|
@@ -39479,15 +39719,15 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
|
39479
39719
|
import { spawn as spawn8 } from "node:child_process";
|
|
39480
39720
|
import fs35 from "node:fs";
|
|
39481
39721
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
39482
|
-
import
|
|
39722
|
+
import path50 from "node:path";
|
|
39483
39723
|
function shSingleQuote(s) {
|
|
39484
39724
|
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
39485
39725
|
}
|
|
39486
39726
|
function trySpawnShellScriptLogRedirectUnix(command, env, cwd, signal) {
|
|
39487
|
-
const tmpRoot = fs35.mkdtempSync(
|
|
39488
|
-
const logPath =
|
|
39489
|
-
const innerPath =
|
|
39490
|
-
const runnerPath =
|
|
39727
|
+
const tmpRoot = fs35.mkdtempSync(path50.join(tmpdir2(), "ba-devsrv-sh-"));
|
|
39728
|
+
const logPath = path50.join(tmpRoot, "combined.log");
|
|
39729
|
+
const innerPath = path50.join(tmpRoot, "_cmd.sh");
|
|
39730
|
+
const runnerPath = path50.join(tmpRoot, "_run.sh");
|
|
39491
39731
|
try {
|
|
39492
39732
|
fs35.writeFileSync(innerPath, `#!/bin/sh
|
|
39493
39733
|
${command}
|
|
@@ -39518,9 +39758,9 @@ cd ${shSingleQuote(cwd)}
|
|
|
39518
39758
|
}
|
|
39519
39759
|
}
|
|
39520
39760
|
function trySpawnShellScriptLogRedirectWin(command, env, cwd, signal) {
|
|
39521
|
-
const tmpRoot = fs35.mkdtempSync(
|
|
39522
|
-
const logPath =
|
|
39523
|
-
const runnerPath =
|
|
39761
|
+
const tmpRoot = fs35.mkdtempSync(path50.join(tmpdir2(), "ba-devsrv-sh-"));
|
|
39762
|
+
const logPath = path50.join(tmpRoot, "combined.log");
|
|
39763
|
+
const runnerPath = path50.join(tmpRoot, "_run.bat");
|
|
39524
39764
|
const q = (p) => `"${p.replace(/"/g, '""')}"`;
|
|
39525
39765
|
const com = process.env.ComSpec || "cmd.exe";
|
|
39526
39766
|
try {
|
|
@@ -40033,7 +40273,7 @@ async function proxyToLocal(request) {
|
|
|
40033
40273
|
};
|
|
40034
40274
|
const maxAttempts = isIdempotentProxyMethod(request.method) ? LOCAL_PREVIEW_FETCH_RETRY_DELAYS_MS.length + 1 : 1;
|
|
40035
40275
|
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
40036
|
-
const once = await new Promise((
|
|
40276
|
+
const once = await new Promise((resolve34) => {
|
|
40037
40277
|
const req = mod.request(opts, (res) => {
|
|
40038
40278
|
const chunks = [];
|
|
40039
40279
|
res.on("data", (c) => chunks.push(c));
|
|
@@ -40044,7 +40284,7 @@ async function proxyToLocal(request) {
|
|
|
40044
40284
|
if (typeof v === "string") headers[k] = v;
|
|
40045
40285
|
else if (Array.isArray(v) && v[0]) headers[k] = v[0];
|
|
40046
40286
|
}
|
|
40047
|
-
|
|
40287
|
+
resolve34({
|
|
40048
40288
|
id: request.id,
|
|
40049
40289
|
statusCode: res.statusCode ?? 0,
|
|
40050
40290
|
headers,
|
|
@@ -40053,7 +40293,7 @@ async function proxyToLocal(request) {
|
|
|
40053
40293
|
});
|
|
40054
40294
|
});
|
|
40055
40295
|
req.on("error", (err) => {
|
|
40056
|
-
|
|
40296
|
+
resolve34({
|
|
40057
40297
|
id: request.id,
|
|
40058
40298
|
statusCode: 0,
|
|
40059
40299
|
headers: {},
|
|
@@ -40460,15 +40700,38 @@ function createOnBridgeIdentified(opts) {
|
|
|
40460
40700
|
};
|
|
40461
40701
|
}
|
|
40462
40702
|
|
|
40463
|
-
// src/skills/
|
|
40464
|
-
import fs36 from "node:fs";
|
|
40465
|
-
import path45 from "node:path";
|
|
40703
|
+
// src/skills/discovery/skill-discovery-roots.ts
|
|
40466
40704
|
var SKILL_DISCOVERY_ROOTS = [".agents/skills", ".claude/skills", ".cursor/skills", "skills"];
|
|
40467
|
-
function
|
|
40705
|
+
async function yieldSkillDiscoveryWork(entryCount) {
|
|
40706
|
+
if (entryCount > 0 && entryCount % INDEX_WORK_YIELD_EVERY === 0) {
|
|
40707
|
+
await yieldToEventLoop();
|
|
40708
|
+
}
|
|
40709
|
+
}
|
|
40710
|
+
|
|
40711
|
+
// src/skills/discovery/discover-local-skills.ts
|
|
40712
|
+
import fs36 from "node:fs";
|
|
40713
|
+
import path51 from "node:path";
|
|
40714
|
+
function collectSkillFromDir(rel, base, name, seenKeys, out) {
|
|
40715
|
+
const dir = path51.join(base, name);
|
|
40716
|
+
try {
|
|
40717
|
+
if (!fs36.statSync(dir).isDirectory()) return;
|
|
40718
|
+
} catch {
|
|
40719
|
+
return;
|
|
40720
|
+
}
|
|
40721
|
+
const skillMd = path51.join(dir, "SKILL.md");
|
|
40722
|
+
if (!fs36.existsSync(skillMd)) return;
|
|
40723
|
+
const key = `${rel}/${name}`;
|
|
40724
|
+
if (seenKeys.has(key)) return;
|
|
40725
|
+
seenKeys.add(key);
|
|
40726
|
+
out.push({ skillKey: name, path: `${rel}/${name}`.replace(/\\/g, "/") });
|
|
40727
|
+
}
|
|
40728
|
+
async function discoverLocalSkillsAsync(cwd) {
|
|
40468
40729
|
const out = [];
|
|
40469
40730
|
const seenKeys = /* @__PURE__ */ new Set();
|
|
40731
|
+
let work = 0;
|
|
40470
40732
|
for (const rel of SKILL_DISCOVERY_ROOTS) {
|
|
40471
|
-
|
|
40733
|
+
await yieldSkillDiscoveryWork(++work);
|
|
40734
|
+
const base = path51.join(cwd, rel);
|
|
40472
40735
|
if (!fs36.existsSync(base) || !fs36.statSync(base).isDirectory()) continue;
|
|
40473
40736
|
let entries = [];
|
|
40474
40737
|
try {
|
|
@@ -40476,45 +40739,45 @@ function discoverLocalSkills(cwd) {
|
|
|
40476
40739
|
} catch {
|
|
40477
40740
|
continue;
|
|
40478
40741
|
}
|
|
40479
|
-
for (
|
|
40480
|
-
|
|
40481
|
-
|
|
40482
|
-
if (!fs36.statSync(dir).isDirectory()) continue;
|
|
40483
|
-
} catch {
|
|
40484
|
-
continue;
|
|
40485
|
-
}
|
|
40486
|
-
const skillMd = path45.join(dir, "SKILL.md");
|
|
40487
|
-
if (!fs36.existsSync(skillMd)) continue;
|
|
40488
|
-
const key = `${rel}/${name}`;
|
|
40489
|
-
if (seenKeys.has(key)) continue;
|
|
40490
|
-
seenKeys.add(key);
|
|
40491
|
-
out.push({ skillKey: name, path: `${rel}/${name}`.replace(/\\/g, "/") });
|
|
40742
|
+
for (let i = 0; i < entries.length; i++) {
|
|
40743
|
+
await yieldSkillDiscoveryWork(++work);
|
|
40744
|
+
collectSkillFromDir(rel, base, entries[i], seenKeys, out);
|
|
40492
40745
|
}
|
|
40493
40746
|
}
|
|
40494
40747
|
return out;
|
|
40495
40748
|
}
|
|
40496
|
-
|
|
40749
|
+
|
|
40750
|
+
// src/skills/discovery/discover-skill-layout-roots.ts
|
|
40751
|
+
import fs37 from "node:fs";
|
|
40752
|
+
import path52 from "node:path";
|
|
40753
|
+
function collectLayoutSkill(rel, base, name, skills2) {
|
|
40754
|
+
const dir = path52.join(base, name);
|
|
40755
|
+
try {
|
|
40756
|
+
if (!fs37.statSync(dir).isDirectory()) return;
|
|
40757
|
+
} catch {
|
|
40758
|
+
return;
|
|
40759
|
+
}
|
|
40760
|
+
if (!fs37.existsSync(path52.join(dir, "SKILL.md"))) return;
|
|
40761
|
+
const relPath = `${rel}/${name}`.replace(/\\/g, "/");
|
|
40762
|
+
skills2.push({ name, relPath });
|
|
40763
|
+
}
|
|
40764
|
+
async function discoverSkillLayoutRootsAsync(cwd) {
|
|
40497
40765
|
const roots = [];
|
|
40766
|
+
let work = 0;
|
|
40498
40767
|
for (const rel of SKILL_DISCOVERY_ROOTS) {
|
|
40499
|
-
|
|
40500
|
-
|
|
40768
|
+
await yieldSkillDiscoveryWork(++work);
|
|
40769
|
+
const base = path52.join(cwd, rel);
|
|
40770
|
+
if (!fs37.existsSync(base) || !fs37.statSync(base).isDirectory()) continue;
|
|
40501
40771
|
let entries = [];
|
|
40502
40772
|
try {
|
|
40503
|
-
entries =
|
|
40773
|
+
entries = fs37.readdirSync(base);
|
|
40504
40774
|
} catch {
|
|
40505
40775
|
continue;
|
|
40506
40776
|
}
|
|
40507
40777
|
const skills2 = [];
|
|
40508
|
-
for (
|
|
40509
|
-
|
|
40510
|
-
|
|
40511
|
-
if (!fs36.statSync(dir).isDirectory()) continue;
|
|
40512
|
-
} catch {
|
|
40513
|
-
continue;
|
|
40514
|
-
}
|
|
40515
|
-
if (!fs36.existsSync(path45.join(dir, "SKILL.md"))) continue;
|
|
40516
|
-
const relPath = `${rel}/${name}`.replace(/\\/g, "/");
|
|
40517
|
-
skills2.push({ name, relPath });
|
|
40778
|
+
for (let i = 0; i < entries.length; i++) {
|
|
40779
|
+
await yieldSkillDiscoveryWork(++work);
|
|
40780
|
+
collectLayoutSkill(rel, base, entries[i], skills2);
|
|
40518
40781
|
}
|
|
40519
40782
|
if (skills2.length > 0) {
|
|
40520
40783
|
roots.push({ path: rel.replace(/\\/g, "/"), skills: skills2 });
|
|
@@ -40556,16 +40819,18 @@ async function detectLocalAgentTypes() {
|
|
|
40556
40819
|
function createSendLocalSkillsReport(getWs, logFn) {
|
|
40557
40820
|
return () => {
|
|
40558
40821
|
setImmediate(() => {
|
|
40559
|
-
|
|
40560
|
-
|
|
40561
|
-
|
|
40562
|
-
|
|
40563
|
-
|
|
40564
|
-
|
|
40565
|
-
|
|
40566
|
-
|
|
40567
|
-
|
|
40568
|
-
|
|
40822
|
+
void (async () => {
|
|
40823
|
+
try {
|
|
40824
|
+
const socket = getWs();
|
|
40825
|
+
if (!socket || socket.readyState !== wrapper_default.OPEN) return;
|
|
40826
|
+
const skills2 = await discoverLocalSkillsAsync(getBridgeRoot());
|
|
40827
|
+
sendWsMessage(socket, { type: "local_skills", skills: skills2 });
|
|
40828
|
+
} catch (e) {
|
|
40829
|
+
logFn(
|
|
40830
|
+
`[Bridge service] Local skills report failed: ${e instanceof Error ? e.message : String(e)}`
|
|
40831
|
+
);
|
|
40832
|
+
}
|
|
40833
|
+
})();
|
|
40569
40834
|
});
|
|
40570
40835
|
};
|
|
40571
40836
|
}
|
|
@@ -40868,7 +41133,7 @@ function syncRunningTurnQueueKeys(turns, queueKey) {
|
|
|
40868
41133
|
}
|
|
40869
41134
|
|
|
40870
41135
|
// src/prompt-turn-queue/runner/run-local-revert-before-queued-prompt.ts
|
|
40871
|
-
import
|
|
41136
|
+
import fs38 from "node:fs";
|
|
40872
41137
|
async function runLocalRevertBeforeQueuedPrompt(next, deps) {
|
|
40873
41138
|
if (next.serverState !== "requeued_with_revert") return true;
|
|
40874
41139
|
const sid = next.sessionId;
|
|
@@ -40877,7 +41142,7 @@ async function runLocalRevertBeforeQueuedPrompt(next, deps) {
|
|
|
40877
41142
|
const agentBase = deps.sessionWorktreeManager.getSessionWorktreeRootForSession(sid) ?? getBridgeRoot();
|
|
40878
41143
|
const file2 = snapshotFilePath(agentBase, tid);
|
|
40879
41144
|
try {
|
|
40880
|
-
await
|
|
41145
|
+
await fs38.promises.access(file2, fs38.constants.F_OK);
|
|
40881
41146
|
} catch {
|
|
40882
41147
|
deps.log(
|
|
40883
41148
|
`[Queue] requeued_with_revert: no pre-turn snapshot for ${tid.slice(0, 8)}\u2026; continuing without revert.`
|
|
@@ -41165,9 +41430,9 @@ function parseChangeSummarySnapshots(raw) {
|
|
|
41165
41430
|
for (const item of raw) {
|
|
41166
41431
|
if (!item || typeof item !== "object") continue;
|
|
41167
41432
|
const o = item;
|
|
41168
|
-
const
|
|
41169
|
-
if (!
|
|
41170
|
-
const row = { path:
|
|
41433
|
+
const path62 = typeof o.path === "string" && o.path.trim() !== "" ? o.path.trim() : "";
|
|
41434
|
+
if (!path62) continue;
|
|
41435
|
+
const row = { path: path62 };
|
|
41171
41436
|
if (typeof o.patchContent === "string") row.patchContent = o.patchContent;
|
|
41172
41437
|
if (typeof o.oldText === "string") row.oldText = o.oldText;
|
|
41173
41438
|
if (typeof o.newText === "string") row.newText = o.newText;
|
|
@@ -41377,8 +41642,8 @@ function randomSecret() {
|
|
|
41377
41642
|
}
|
|
41378
41643
|
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
41379
41644
|
}
|
|
41380
|
-
async function requestPreviewApi(port, secret, method,
|
|
41381
|
-
const url2 = `http://127.0.0.1:${port}${
|
|
41645
|
+
async function requestPreviewApi(port, secret, method, path62, body) {
|
|
41646
|
+
const url2 = `http://127.0.0.1:${port}${path62}`;
|
|
41382
41647
|
const headers = {
|
|
41383
41648
|
[PREVIEW_SECRET_HEADER]: secret,
|
|
41384
41649
|
"Content-Type": "application/json"
|
|
@@ -41390,7 +41655,7 @@ async function requestPreviewApi(port, secret, method, path55, body) {
|
|
|
41390
41655
|
});
|
|
41391
41656
|
const data = await res.json().catch(() => ({}));
|
|
41392
41657
|
if (!res.ok) {
|
|
41393
|
-
throw new Error(data?.error ?? `Preview API ${method} ${
|
|
41658
|
+
throw new Error(data?.error ?? `Preview API ${method} ${path62}: ${res.status}`);
|
|
41394
41659
|
}
|
|
41395
41660
|
return data;
|
|
41396
41661
|
}
|
|
@@ -41555,14 +41820,14 @@ var handleSkillCallMessage = (msg, { getWs, log: log2 }) => {
|
|
|
41555
41820
|
};
|
|
41556
41821
|
|
|
41557
41822
|
// src/files/list-dir/index.ts
|
|
41558
|
-
import
|
|
41823
|
+
import fs40 from "node:fs";
|
|
41559
41824
|
|
|
41560
41825
|
// src/files/ensure-under-cwd.ts
|
|
41561
|
-
import
|
|
41826
|
+
import path53 from "node:path";
|
|
41562
41827
|
function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
|
|
41563
|
-
const normalized =
|
|
41564
|
-
const resolved =
|
|
41565
|
-
if (!resolved.startsWith(cwd +
|
|
41828
|
+
const normalized = path53.normalize(relativePath).replace(/^(\.\/)+/, "");
|
|
41829
|
+
const resolved = path53.resolve(cwd, normalized);
|
|
41830
|
+
if (!resolved.startsWith(cwd + path53.sep) && resolved !== cwd) {
|
|
41566
41831
|
return null;
|
|
41567
41832
|
}
|
|
41568
41833
|
return resolved;
|
|
@@ -41572,15 +41837,15 @@ function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
|
|
|
41572
41837
|
var LIST_DIR_YIELD_EVERY = 256;
|
|
41573
41838
|
|
|
41574
41839
|
// src/files/list-dir/map-dir-entry.ts
|
|
41575
|
-
import
|
|
41576
|
-
import
|
|
41840
|
+
import path54 from "node:path";
|
|
41841
|
+
import fs39 from "node:fs";
|
|
41577
41842
|
async function mapDirEntry(d, relativePath, resolved) {
|
|
41578
|
-
const entryPath =
|
|
41579
|
-
const fullPath =
|
|
41843
|
+
const entryPath = path54.join(relativePath || ".", d.name).replace(/\\/g, "/");
|
|
41844
|
+
const fullPath = path54.join(resolved, d.name);
|
|
41580
41845
|
let isDir = d.isDirectory();
|
|
41581
41846
|
if (d.isSymbolicLink()) {
|
|
41582
41847
|
try {
|
|
41583
|
-
const targetStat = await
|
|
41848
|
+
const targetStat = await fs39.promises.stat(fullPath);
|
|
41584
41849
|
isDir = targetStat.isDirectory();
|
|
41585
41850
|
} catch {
|
|
41586
41851
|
isDir = false;
|
|
@@ -41610,7 +41875,7 @@ async function listDirAsync(relativePath, sessionParentPath = getBridgeRoot()) {
|
|
|
41610
41875
|
return { error: "Path is outside working directory" };
|
|
41611
41876
|
}
|
|
41612
41877
|
try {
|
|
41613
|
-
const names = await
|
|
41878
|
+
const names = await fs40.promises.readdir(resolved, { withFileTypes: true });
|
|
41614
41879
|
const entries = [];
|
|
41615
41880
|
for (let i = 0; i < names.length; i++) {
|
|
41616
41881
|
if (i > 0 && i % LIST_DIR_YIELD_EVERY === 0) {
|
|
@@ -41630,18 +41895,18 @@ var LINE_CHUNK_SIZE = 64 * 1024;
|
|
|
41630
41895
|
var READ_RANGE_YIELD_EVERY_BYTES = 256 * 1024;
|
|
41631
41896
|
|
|
41632
41897
|
// src/files/read-file/resolve-file-path.ts
|
|
41633
|
-
import
|
|
41898
|
+
import fs41 from "node:fs";
|
|
41634
41899
|
async function resolveFilePathAsync(relativePath, sessionParentPath = getBridgeRoot()) {
|
|
41635
41900
|
const resolved = ensureUnderCwd(relativePath, sessionParentPath);
|
|
41636
41901
|
if (!resolved) return { error: "Path is outside working directory" };
|
|
41637
41902
|
let real;
|
|
41638
41903
|
try {
|
|
41639
|
-
real = await
|
|
41904
|
+
real = await fs41.promises.realpath(resolved);
|
|
41640
41905
|
} catch {
|
|
41641
41906
|
real = resolved;
|
|
41642
41907
|
}
|
|
41643
41908
|
try {
|
|
41644
|
-
const stat3 = await
|
|
41909
|
+
const stat3 = await fs41.promises.stat(real);
|
|
41645
41910
|
if (!stat3.isFile()) return { error: "Not a file" };
|
|
41646
41911
|
return real;
|
|
41647
41912
|
} catch (err) {
|
|
@@ -41650,11 +41915,11 @@ async function resolveFilePathAsync(relativePath, sessionParentPath = getBridgeR
|
|
|
41650
41915
|
}
|
|
41651
41916
|
|
|
41652
41917
|
// src/files/read-file/read-file-range-async.ts
|
|
41653
|
-
import
|
|
41918
|
+
import fs42 from "node:fs";
|
|
41654
41919
|
import { StringDecoder } from "node:string_decoder";
|
|
41655
41920
|
async function readFileRangeAsync(filePath, startLine, endLine, lineOffsetIn, lineChunkSize = LINE_CHUNK_SIZE) {
|
|
41656
|
-
const fileSize = (await
|
|
41657
|
-
const fd = await
|
|
41921
|
+
const fileSize = (await fs42.promises.stat(filePath)).size;
|
|
41922
|
+
const fd = await fs42.promises.open(filePath, "r");
|
|
41658
41923
|
const bufSize = 64 * 1024;
|
|
41659
41924
|
const buf = Buffer.alloc(bufSize);
|
|
41660
41925
|
const decoder = new StringDecoder("utf8");
|
|
@@ -41816,11 +42081,11 @@ async function readFileRangeAsync(filePath, startLine, endLine, lineOffsetIn, li
|
|
|
41816
42081
|
}
|
|
41817
42082
|
|
|
41818
42083
|
// src/files/read-file/read-file-buffer-full-async.ts
|
|
41819
|
-
import
|
|
42084
|
+
import fs43 from "node:fs";
|
|
41820
42085
|
var READ_CHUNK_BYTES = 256 * 1024;
|
|
41821
42086
|
async function readFileBufferFullAsync(filePath) {
|
|
41822
|
-
const stat3 = await
|
|
41823
|
-
const fd = await
|
|
42087
|
+
const stat3 = await fs43.promises.stat(filePath);
|
|
42088
|
+
const fd = await fs43.promises.open(filePath, "r");
|
|
41824
42089
|
const chunks = [];
|
|
41825
42090
|
let position = 0;
|
|
41826
42091
|
let bytesSinceYield = 0;
|
|
@@ -41927,13 +42192,13 @@ async function resolveFileBrowserSessionParent(sessionWorktreeManager, sessionId
|
|
|
41927
42192
|
}
|
|
41928
42193
|
|
|
41929
42194
|
// src/files/handle-file-browser-search.ts
|
|
41930
|
-
import
|
|
42195
|
+
import path55 from "node:path";
|
|
41931
42196
|
var SEARCH_LIMIT = 100;
|
|
41932
42197
|
function handleFileBrowserSearch(msg, socket, e2ee, sessionWorktreeManager) {
|
|
41933
42198
|
void (async () => {
|
|
41934
42199
|
await yieldToEventLoop();
|
|
41935
42200
|
const q = typeof msg.q === "string" ? msg.q : "";
|
|
41936
|
-
const sessionParentPath =
|
|
42201
|
+
const sessionParentPath = path55.resolve(
|
|
41937
42202
|
sessionWorktreeManager != null ? await resolveFileBrowserSessionParent(sessionWorktreeManager, msg.sessionId) : getBridgeRoot()
|
|
41938
42203
|
);
|
|
41939
42204
|
if (!await bridgeFileIndexIsPopulated(sessionParentPath)) {
|
|
@@ -42043,37 +42308,56 @@ function handleFileBrowserSearchMessage(msg, { getWs, e2ee, sessionWorktreeManag
|
|
|
42043
42308
|
|
|
42044
42309
|
// src/routing/handlers/skill-layout-request.ts
|
|
42045
42310
|
function handleSkillLayoutRequest(msg, deps) {
|
|
42046
|
-
const socket = deps.getWs();
|
|
42047
42311
|
const id = typeof msg.id === "string" ? msg.id : "";
|
|
42048
|
-
|
|
42049
|
-
|
|
42050
|
-
|
|
42051
|
-
|
|
42312
|
+
void (async () => {
|
|
42313
|
+
const socket = deps.getWs();
|
|
42314
|
+
const roots = await discoverSkillLayoutRootsAsync(getBridgeRoot());
|
|
42315
|
+
if (socket) {
|
|
42316
|
+
sendWsMessage(socket, { type: "skill_layout_response", id, roots });
|
|
42317
|
+
}
|
|
42318
|
+
})();
|
|
42052
42319
|
}
|
|
42053
42320
|
|
|
42054
|
-
// src/skills/install-
|
|
42055
|
-
|
|
42056
|
-
|
|
42057
|
-
|
|
42321
|
+
// src/skills/install/is-valid-install-item.ts
|
|
42322
|
+
function isValidRemoteSkillInstallItem(item) {
|
|
42323
|
+
if (item === null || typeof item !== "object") return false;
|
|
42324
|
+
const o = item;
|
|
42325
|
+
return typeof o.sourceId === "string" && typeof o.skillName === "string" && typeof o.versionHash === "string" && Array.isArray(o.files);
|
|
42326
|
+
}
|
|
42327
|
+
|
|
42328
|
+
// src/skills/install/install-remote-skills-async.ts
|
|
42329
|
+
import fs44 from "node:fs";
|
|
42330
|
+
import path56 from "node:path";
|
|
42331
|
+
|
|
42332
|
+
// src/skills/install/constants.ts
|
|
42333
|
+
var INSTALL_SKILLS_YIELD_EVERY = 16;
|
|
42334
|
+
|
|
42335
|
+
// src/skills/install/install-remote-skills-async.ts
|
|
42336
|
+
async function writeInstallFileAsync(skillDir, f, filesWritten) {
|
|
42337
|
+
if (typeof f.path !== "string" || !f.text && !f.base64) return;
|
|
42338
|
+
if (++filesWritten.count % INSTALL_SKILLS_YIELD_EVERY === 0) {
|
|
42339
|
+
await yieldToEventLoop();
|
|
42340
|
+
}
|
|
42341
|
+
const dest = path56.join(skillDir, f.path);
|
|
42342
|
+
await fs44.promises.mkdir(path56.dirname(dest), { recursive: true });
|
|
42343
|
+
if (f.text !== void 0) {
|
|
42344
|
+
await fs44.promises.writeFile(dest, f.text, "utf8");
|
|
42345
|
+
} else if (f.base64) {
|
|
42346
|
+
await fs44.promises.writeFile(dest, Buffer.from(f.base64, "base64"));
|
|
42347
|
+
}
|
|
42348
|
+
}
|
|
42349
|
+
async function installRemoteSkillsAsync(cwd, targetDir, items) {
|
|
42058
42350
|
const installed2 = [];
|
|
42059
42351
|
if (!Array.isArray(items)) {
|
|
42060
42352
|
return { success: false, error: "Invalid items" };
|
|
42061
42353
|
}
|
|
42354
|
+
const filesWritten = { count: 0 };
|
|
42062
42355
|
try {
|
|
42063
42356
|
for (const item of items) {
|
|
42064
|
-
if (
|
|
42065
|
-
|
|
42066
|
-
}
|
|
42067
|
-
const skillDir = path49.join(cwd, targetDir, item.skillName);
|
|
42357
|
+
if (!isValidRemoteSkillInstallItem(item)) continue;
|
|
42358
|
+
const skillDir = path56.join(cwd, targetDir, item.skillName);
|
|
42068
42359
|
for (const f of item.files) {
|
|
42069
|
-
|
|
42070
|
-
const dest = path49.join(skillDir, f.path);
|
|
42071
|
-
fs43.mkdirSync(path49.dirname(dest), { recursive: true });
|
|
42072
|
-
if (f.text !== void 0) {
|
|
42073
|
-
fs43.writeFileSync(dest, f.text, "utf8");
|
|
42074
|
-
} else if (f.base64) {
|
|
42075
|
-
fs43.writeFileSync(dest, Buffer.from(f.base64, "base64"));
|
|
42076
|
-
}
|
|
42360
|
+
await writeInstallFileAsync(skillDir, f, filesWritten);
|
|
42077
42361
|
}
|
|
42078
42362
|
installed2.push({
|
|
42079
42363
|
sourceId: item.sourceId,
|
|
@@ -42089,28 +42373,32 @@ function installRemoteSkills(cwd, targetDir, items) {
|
|
|
42089
42373
|
|
|
42090
42374
|
// src/routing/handlers/install-skills.ts
|
|
42091
42375
|
var handleInstallSkillsMessage = (msg, deps) => {
|
|
42092
|
-
const socket = deps.getWs();
|
|
42093
42376
|
const id = typeof msg.id === "string" ? msg.id : "";
|
|
42094
42377
|
const targetDir = typeof msg.targetDir === "string" && msg.targetDir.trim() ? msg.targetDir.trim() : ".agents/skills";
|
|
42095
42378
|
const rawItems = msg.items;
|
|
42096
42379
|
const cwd = getBridgeRoot();
|
|
42097
|
-
|
|
42098
|
-
|
|
42099
|
-
const
|
|
42100
|
-
|
|
42380
|
+
void (async () => {
|
|
42381
|
+
const socket = deps.getWs();
|
|
42382
|
+
const result = await installRemoteSkillsAsync(cwd, targetDir, rawItems);
|
|
42383
|
+
if (!result.success) {
|
|
42384
|
+
const err = result.error ?? "Invalid items";
|
|
42385
|
+
deps.log(`[Bridge service] Install skills failed: ${err}`);
|
|
42386
|
+
if (socket) {
|
|
42387
|
+
sendWsMessage(socket, { type: "install_skills_result", id, success: false, error: err });
|
|
42388
|
+
}
|
|
42389
|
+
return;
|
|
42390
|
+
}
|
|
42101
42391
|
if (socket) {
|
|
42102
|
-
sendWsMessage(socket, { type: "install_skills_result", id, success:
|
|
42392
|
+
sendWsMessage(socket, { type: "install_skills_result", id, success: true, installed: result.installed });
|
|
42103
42393
|
}
|
|
42104
|
-
|
|
42105
|
-
}
|
|
42106
|
-
if (socket) {
|
|
42107
|
-
sendWsMessage(socket, { type: "install_skills_result", id, success: true, installed: result.installed });
|
|
42108
|
-
}
|
|
42394
|
+
})();
|
|
42109
42395
|
};
|
|
42110
42396
|
|
|
42111
42397
|
// src/routing/handlers/refresh-local-skills.ts
|
|
42112
42398
|
var handleRefreshLocalSkills = (_msg, deps) => {
|
|
42113
|
-
|
|
42399
|
+
setImmediate(() => {
|
|
42400
|
+
deps.sendLocalSkillsReport?.();
|
|
42401
|
+
});
|
|
42114
42402
|
};
|
|
42115
42403
|
|
|
42116
42404
|
// src/routing/handlers/git/handle-session-git-commit.ts
|
|
@@ -42141,13 +42429,78 @@ async function handleSessionGitCommitAction(deps, sessionId, msg, reply) {
|
|
|
42141
42429
|
});
|
|
42142
42430
|
}
|
|
42143
42431
|
|
|
42432
|
+
// src/routing/handlers/git/session-git-changes-params.ts
|
|
42433
|
+
function readString(value) {
|
|
42434
|
+
return typeof value === "string" ? value.trim() : "";
|
|
42435
|
+
}
|
|
42436
|
+
function readChangesObject(msg) {
|
|
42437
|
+
const changes = msg.changes;
|
|
42438
|
+
if (!changes || typeof changes !== "object" || Array.isArray(changes)) return null;
|
|
42439
|
+
return changes;
|
|
42440
|
+
}
|
|
42441
|
+
function parseSessionGitChangesParams(msg) {
|
|
42442
|
+
const nested = readChangesObject(msg);
|
|
42443
|
+
const file2 = nested?.file && typeof nested.file === "object" && !Array.isArray(nested.file) ? nested.file : null;
|
|
42444
|
+
const repoRelPath = readString(nested?.repoRelPath) || readString(msg.changesRepoRelPath);
|
|
42445
|
+
const viewRaw = nested?.view ?? msg.changesView;
|
|
42446
|
+
const view = viewRaw === "commit" ? "commit" : "working";
|
|
42447
|
+
const commitSha = readString(nested?.commitSha) || readString(msg.changesCommitSha);
|
|
42448
|
+
const recentCommitsLimit = nested?.recentCommitsLimit ?? msg.changesRecentCommitsLimit;
|
|
42449
|
+
return {
|
|
42450
|
+
repoRelPath,
|
|
42451
|
+
view,
|
|
42452
|
+
commitSha,
|
|
42453
|
+
recentCommitsLimit: typeof recentCommitsLimit === "number" || typeof recentCommitsLimit === "string" ? recentCommitsLimit : void 0,
|
|
42454
|
+
fileWorkspaceRelPath: readString(file2?.workspaceRelPath),
|
|
42455
|
+
fileChange: readString(file2?.change),
|
|
42456
|
+
fileMovedFromWorkspaceRelPath: readString(file2?.movedFromWorkspaceRelPath)
|
|
42457
|
+
};
|
|
42458
|
+
}
|
|
42459
|
+
|
|
42460
|
+
// src/routing/handlers/git/handle-session-git-get-change-file-patch.ts
|
|
42461
|
+
async function handleSessionGitGetChangeFilePatchAction(deps, msg, reply) {
|
|
42462
|
+
const changes = parseSessionGitChangesParams(msg);
|
|
42463
|
+
const { repoRelPath: repoRel, view, commitSha, fileWorkspaceRelPath, fileChange, fileMovedFromWorkspaceRelPath } = changes;
|
|
42464
|
+
const change = parseWorkingTreeChangeKind(fileChange);
|
|
42465
|
+
if (!repoRel || !fileWorkspaceRelPath || !change) {
|
|
42466
|
+
reply({
|
|
42467
|
+
ok: false,
|
|
42468
|
+
error: "changes.repoRelPath, changes.file.workspaceRelPath, and changes.file.change are required"
|
|
42469
|
+
});
|
|
42470
|
+
return;
|
|
42471
|
+
}
|
|
42472
|
+
if (view === "commit" && !commitSha) {
|
|
42473
|
+
reply({ ok: false, error: "changes.commitSha is required for commit file patch" });
|
|
42474
|
+
return;
|
|
42475
|
+
}
|
|
42476
|
+
const patch = await deps.sessionWorktreeManager.getSessionWorkingTreeChangeFilePatch(msg.sessionId, {
|
|
42477
|
+
repoRelPath: repoRel,
|
|
42478
|
+
workspaceRelPath: fileWorkspaceRelPath,
|
|
42479
|
+
change,
|
|
42480
|
+
movedFromWorkspaceRelPath: fileMovedFromWorkspaceRelPath || null,
|
|
42481
|
+
basis: view === "commit" ? { kind: "commit", sha: commitSha } : { kind: "working" }
|
|
42482
|
+
});
|
|
42483
|
+
reply(
|
|
42484
|
+
{
|
|
42485
|
+
ok: true,
|
|
42486
|
+
patchContent: patch.patchContent ?? null,
|
|
42487
|
+
totalLines: patch.totalLines
|
|
42488
|
+
},
|
|
42489
|
+
["patchContent"]
|
|
42490
|
+
);
|
|
42491
|
+
}
|
|
42492
|
+
|
|
42144
42493
|
// src/routing/handlers/git/coalesce-list-changes.ts
|
|
42145
42494
|
var listChangesInflight = /* @__PURE__ */ new Map();
|
|
42495
|
+
function normalizeListChangesRepoRel(repoRelPath) {
|
|
42496
|
+
if (!repoRelPath?.trim()) return "";
|
|
42497
|
+
return normRepoRel(repoRelPath.trim());
|
|
42498
|
+
}
|
|
42146
42499
|
function listChangesInflightKey(sessionId, opts) {
|
|
42147
42500
|
const basis = opts?.basis;
|
|
42148
42501
|
return [
|
|
42149
42502
|
sessionId,
|
|
42150
|
-
opts?.repoRelPath
|
|
42503
|
+
normalizeListChangesRepoRel(opts?.repoRelPath),
|
|
42151
42504
|
basis?.kind === "commit" ? `commit:${basis.sha}` : "working",
|
|
42152
42505
|
String(opts?.recentCommitsLimit ?? "")
|
|
42153
42506
|
].join("|");
|
|
@@ -42165,16 +42518,17 @@ function getSessionWorkingTreeChangeDetailsCoalesced(sessionWorktreeManager, ses
|
|
|
42165
42518
|
|
|
42166
42519
|
// src/routing/handlers/git/handle-session-git-list-changes.ts
|
|
42167
42520
|
async function handleSessionGitListChangesAction(deps, msg, reply) {
|
|
42168
|
-
const
|
|
42169
|
-
const
|
|
42170
|
-
const commitSha = typeof msg.changesCommitSha === "string" ? msg.changesCommitSha.trim() : "";
|
|
42521
|
+
const changes = parseSessionGitChangesParams(msg);
|
|
42522
|
+
const { repoRelPath: repoRel, view, commitSha, recentCommitsLimit } = changes;
|
|
42171
42523
|
if (view === "commit") {
|
|
42172
42524
|
if (!repoRel || !commitSha) {
|
|
42173
|
-
reply({
|
|
42525
|
+
reply({
|
|
42526
|
+
ok: false,
|
|
42527
|
+
error: "changes.repoRelPath and changes.commitSha are required for commit view"
|
|
42528
|
+
});
|
|
42174
42529
|
return;
|
|
42175
42530
|
}
|
|
42176
42531
|
}
|
|
42177
|
-
const recentCommitsLimit = typeof msg.changesRecentCommitsLimit === "number" || typeof msg.changesRecentCommitsLimit === "string" ? msg.changesRecentCommitsLimit : void 0;
|
|
42178
42532
|
const opts = repoRel && view === "commit" && commitSha ? {
|
|
42179
42533
|
repoRelPath: repoRel,
|
|
42180
42534
|
basis: { kind: "commit", sha: commitSha },
|
|
@@ -42219,7 +42573,11 @@ async function handleSessionGitStatusAction(deps, sessionId, reply) {
|
|
|
42219
42573
|
function sendSessionGitResult(ws, id, payload, e2ee, encryptedFields = []) {
|
|
42220
42574
|
if (!ws) return;
|
|
42221
42575
|
const message = { type: "session_git_result", id, ...payload };
|
|
42222
|
-
|
|
42576
|
+
let wire = message;
|
|
42577
|
+
if (e2ee && encryptedFields.length > 0) {
|
|
42578
|
+
wire = e2ee.encryptFields(message, encryptedFields);
|
|
42579
|
+
}
|
|
42580
|
+
sendWsMessage(ws, wire);
|
|
42223
42581
|
}
|
|
42224
42582
|
|
|
42225
42583
|
// src/routing/handlers/git/session-git-request.ts
|
|
@@ -42227,7 +42585,7 @@ var handleSessionGitRequestMessage = (msg, deps) => {
|
|
|
42227
42585
|
if (typeof msg.id !== "string") return;
|
|
42228
42586
|
const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
|
|
42229
42587
|
const action = msg.action;
|
|
42230
|
-
if (!sessionId || action !== "status" && action !== "push" && action !== "commit" && action !== "list_changes")
|
|
42588
|
+
if (!sessionId || action !== "status" && action !== "push" && action !== "commit" && action !== "list_changes" && action !== "file_diff")
|
|
42231
42589
|
return;
|
|
42232
42590
|
void (async () => {
|
|
42233
42591
|
const ws = deps.getWs();
|
|
@@ -42241,6 +42599,10 @@ var handleSessionGitRequestMessage = (msg, deps) => {
|
|
|
42241
42599
|
await handleSessionGitListChangesAction(deps, { ...msg, sessionId }, reply);
|
|
42242
42600
|
return;
|
|
42243
42601
|
}
|
|
42602
|
+
if (action === "file_diff") {
|
|
42603
|
+
await handleSessionGitGetChangeFilePatchAction(deps, { ...msg, sessionId }, reply);
|
|
42604
|
+
return;
|
|
42605
|
+
}
|
|
42244
42606
|
if (action === "push") {
|
|
42245
42607
|
await handleSessionGitPushAction(deps, sessionId, reply);
|
|
42246
42608
|
return;
|
|
@@ -42275,7 +42637,7 @@ var handleSessionDiscardedMessage = (msg, deps) => {
|
|
|
42275
42637
|
};
|
|
42276
42638
|
|
|
42277
42639
|
// src/routing/handlers/revert-turn-snapshot.ts
|
|
42278
|
-
import * as
|
|
42640
|
+
import * as fs45 from "node:fs";
|
|
42279
42641
|
var handleRevertTurnSnapshotMessage = (msg, deps) => {
|
|
42280
42642
|
const id = typeof msg.id === "string" ? msg.id : "";
|
|
42281
42643
|
const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
|
|
@@ -42288,7 +42650,7 @@ var handleRevertTurnSnapshotMessage = (msg, deps) => {
|
|
|
42288
42650
|
const agentBase = sessionWorktreeManager.getSessionWorktreeRootForSession(sessionId) ?? getBridgeRoot();
|
|
42289
42651
|
const file2 = snapshotFilePath(agentBase, turnId);
|
|
42290
42652
|
try {
|
|
42291
|
-
await
|
|
42653
|
+
await fs45.promises.access(file2, fs45.constants.F_OK);
|
|
42292
42654
|
} catch {
|
|
42293
42655
|
sendWsMessage(s, {
|
|
42294
42656
|
type: "revert_turn_snapshot_result",
|
|
@@ -42326,11 +42688,13 @@ var handleDevServerControl = (msg, deps) => {
|
|
|
42326
42688
|
// src/routing/handlers/dev-servers-config.ts
|
|
42327
42689
|
var handleDevServersConfig = (msg, deps) => {
|
|
42328
42690
|
const devServers = msg.devServers;
|
|
42329
|
-
|
|
42691
|
+
setImmediate(() => {
|
|
42692
|
+
deps.devServerManager?.applyConfig(devServers ?? []);
|
|
42693
|
+
});
|
|
42330
42694
|
};
|
|
42331
42695
|
|
|
42332
42696
|
// src/git/bridge-git-context.ts
|
|
42333
|
-
import * as
|
|
42697
|
+
import * as path57 from "node:path";
|
|
42334
42698
|
|
|
42335
42699
|
// src/git/branches/get-current-branch.ts
|
|
42336
42700
|
async function getCurrentBranch(repoPath) {
|
|
@@ -42380,12 +42744,12 @@ async function listRepoBranchRefs(repoPath) {
|
|
|
42380
42744
|
// src/git/bridge-git-context.ts
|
|
42381
42745
|
function folderNameForRelPath(relPath, bridgeRoot) {
|
|
42382
42746
|
if (relPath === "." || relPath === "") {
|
|
42383
|
-
return
|
|
42747
|
+
return path57.basename(path57.resolve(bridgeRoot)) || "repo";
|
|
42384
42748
|
}
|
|
42385
|
-
return
|
|
42749
|
+
return path57.basename(relPath) || relPath;
|
|
42386
42750
|
}
|
|
42387
42751
|
async function discoverGitReposForBridgeContext(bridgeRoot) {
|
|
42388
|
-
const root =
|
|
42752
|
+
const root = path57.resolve(bridgeRoot);
|
|
42389
42753
|
if (await isGitRepoDirectory(root)) {
|
|
42390
42754
|
const remoteUrl = await getRemoteOriginUrl(root);
|
|
42391
42755
|
return [{ absolutePath: root, remoteUrl }];
|
|
@@ -42393,17 +42757,19 @@ async function discoverGitReposForBridgeContext(bridgeRoot) {
|
|
|
42393
42757
|
const [deep, shallow] = await Promise.all([discoverGitReposUnderRoot(root), discoverGitRepos(root)]);
|
|
42394
42758
|
const byPath = /* @__PURE__ */ new Map();
|
|
42395
42759
|
for (const repo of [...deep, ...shallow]) {
|
|
42396
|
-
byPath.set(
|
|
42760
|
+
byPath.set(path57.resolve(repo.absolutePath), repo);
|
|
42397
42761
|
}
|
|
42398
42762
|
return [...byPath.values()];
|
|
42399
42763
|
}
|
|
42400
42764
|
async function getBridgeGitContext(bridgeRoot = getBridgeRoot()) {
|
|
42401
|
-
const bridgeResolved =
|
|
42765
|
+
const bridgeResolved = path57.resolve(bridgeRoot);
|
|
42402
42766
|
const repos = await discoverGitReposForBridgeContext(bridgeResolved);
|
|
42403
42767
|
const rows = [];
|
|
42404
|
-
for (
|
|
42405
|
-
|
|
42406
|
-
|
|
42768
|
+
for (let i = 0; i < repos.length; i++) {
|
|
42769
|
+
if (i > 0) await yieldToEventLoop();
|
|
42770
|
+
const repo = repos[i];
|
|
42771
|
+
let rel = path57.relative(bridgeResolved, repo.absolutePath);
|
|
42772
|
+
if (rel.startsWith("..") || path57.isAbsolute(rel)) continue;
|
|
42407
42773
|
const relPath = rel === "" ? "." : rel.replace(/\\/g, "/");
|
|
42408
42774
|
const currentBranch = await getCurrentBranch(repo.absolutePath);
|
|
42409
42775
|
const remoteUrl = repo.remoteUrl.trim() || null;
|
|
@@ -42418,11 +42784,11 @@ async function getBridgeGitContext(bridgeRoot = getBridgeRoot()) {
|
|
|
42418
42784
|
return rows;
|
|
42419
42785
|
}
|
|
42420
42786
|
async function listRepoBranchesForBridge(repoRelPath, bridgeRoot = getBridgeRoot()) {
|
|
42421
|
-
const bridgeResolved =
|
|
42787
|
+
const bridgeResolved = path57.resolve(bridgeRoot);
|
|
42422
42788
|
const rel = repoRelPath.trim() === "." ? "" : repoRelPath.trim().replace(/\\/g, "/");
|
|
42423
|
-
const repoPath = rel === "" ? bridgeResolved :
|
|
42424
|
-
const resolved =
|
|
42425
|
-
if (!resolved.startsWith(bridgeResolved +
|
|
42789
|
+
const repoPath = rel === "" ? bridgeResolved : path57.join(bridgeResolved, rel);
|
|
42790
|
+
const resolved = path57.resolve(repoPath);
|
|
42791
|
+
if (!resolved.startsWith(bridgeResolved + path57.sep) && resolved !== bridgeResolved) {
|
|
42426
42792
|
return [];
|
|
42427
42793
|
}
|
|
42428
42794
|
return listRepoBranchRefs(resolved);
|
|
@@ -42927,10 +43293,10 @@ function listCliAgentCapabilityCacheForWorkspace(db, workspaceId) {
|
|
|
42927
43293
|
}
|
|
42928
43294
|
|
|
42929
43295
|
// src/agents/capabilities/warmup-agent-capabilities-on-connect.ts
|
|
42930
|
-
import * as
|
|
43296
|
+
import * as path59 from "node:path";
|
|
42931
43297
|
|
|
42932
43298
|
// src/agents/capabilities/probe-one-agent-type-for-capabilities.ts
|
|
42933
|
-
import * as
|
|
43299
|
+
import * as path58 from "node:path";
|
|
42934
43300
|
async function probeOneAgentTypeForCapabilities(params) {
|
|
42935
43301
|
const { agentType, cwd, workspaceId, log: log2, reportAgentCapabilities, bridgeReport = true, shouldContinue } = params;
|
|
42936
43302
|
const canContinue = () => shouldContinue?.() !== false;
|
|
@@ -42968,7 +43334,7 @@ async function probeOneAgentTypeForCapabilities(params) {
|
|
|
42968
43334
|
if (!canContinue()) return false;
|
|
42969
43335
|
handle = await resolved.createClient({
|
|
42970
43336
|
command: resolved.command,
|
|
42971
|
-
cwd:
|
|
43337
|
+
cwd: path58.resolve(cwd),
|
|
42972
43338
|
backendAgentType: agentType,
|
|
42973
43339
|
sessionMode: "agent",
|
|
42974
43340
|
persistedAcpSessionId: null,
|
|
@@ -43046,7 +43412,7 @@ async function warmupAgentCapabilitiesOnConnect(params) {
|
|
|
43046
43412
|
const { workspaceId, log: log2, getWs } = params;
|
|
43047
43413
|
const isCurrent = beginAgentCapabilityWarmupRun();
|
|
43048
43414
|
if (!isCurrent()) return;
|
|
43049
|
-
const cwd =
|
|
43415
|
+
const cwd = path59.resolve(getBridgeRoot());
|
|
43050
43416
|
async function sendBatchFromCache() {
|
|
43051
43417
|
if (!isCurrent()) return;
|
|
43052
43418
|
const socket = getWs();
|
|
@@ -43392,18 +43758,18 @@ import * as http from "node:http";
|
|
|
43392
43758
|
|
|
43393
43759
|
// src/mcp/bridge-access/read-json-body.ts
|
|
43394
43760
|
function readJsonBody(req) {
|
|
43395
|
-
return new Promise((
|
|
43761
|
+
return new Promise((resolve34, reject) => {
|
|
43396
43762
|
const chunks = [];
|
|
43397
43763
|
req.on("data", (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
|
|
43398
43764
|
req.on("end", () => {
|
|
43399
43765
|
try {
|
|
43400
43766
|
const raw = Buffer.concat(chunks).toString("utf8").trim();
|
|
43401
43767
|
if (!raw) {
|
|
43402
|
-
|
|
43768
|
+
resolve34({});
|
|
43403
43769
|
return;
|
|
43404
43770
|
}
|
|
43405
43771
|
const parsed = JSON.parse(raw);
|
|
43406
|
-
|
|
43772
|
+
resolve34(parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {});
|
|
43407
43773
|
} catch (e) {
|
|
43408
43774
|
reject(e);
|
|
43409
43775
|
}
|
|
@@ -43461,7 +43827,7 @@ function createBridgeAccessRequestHandler(registry2) {
|
|
|
43461
43827
|
// src/mcp/bridge-access/start-server.ts
|
|
43462
43828
|
function startBridgeAccessServer(registry2) {
|
|
43463
43829
|
const server = http.createServer(createBridgeAccessRequestHandler(registry2));
|
|
43464
|
-
return new Promise((
|
|
43830
|
+
return new Promise((resolve34, reject) => {
|
|
43465
43831
|
server.once("error", reject);
|
|
43466
43832
|
server.listen(0, "127.0.0.1", () => {
|
|
43467
43833
|
const addr = server.address();
|
|
@@ -43469,7 +43835,7 @@ function startBridgeAccessServer(registry2) {
|
|
|
43469
43835
|
reject(new Error("Bridge access server did not bind"));
|
|
43470
43836
|
return;
|
|
43471
43837
|
}
|
|
43472
|
-
|
|
43838
|
+
resolve34({
|
|
43473
43839
|
port: addr.port,
|
|
43474
43840
|
close: () => new Promise((closeResolve, closeReject) => {
|
|
43475
43841
|
server.close((err) => err ? closeReject(err) : closeResolve());
|
|
@@ -43609,8 +43975,8 @@ async function createBridgeConnection(options) {
|
|
|
43609
43975
|
getCloudAccessToken: () => tokens.accessToken
|
|
43610
43976
|
};
|
|
43611
43977
|
const identifyReportedPaths = {
|
|
43612
|
-
bridgeRootPath:
|
|
43613
|
-
worktreesRootPath:
|
|
43978
|
+
bridgeRootPath: path60.resolve(getBridgeRoot()),
|
|
43979
|
+
worktreesRootPath: path60.resolve(worktreesRootPath)
|
|
43614
43980
|
};
|
|
43615
43981
|
const { connect } = createMainBridgeWebSocketLifecycle({
|
|
43616
43982
|
state,
|
|
@@ -43865,9 +44231,9 @@ async function runCliAction(program2, opts) {
|
|
|
43865
44231
|
const firehoseServerUrl = opts.firehoseUrl ?? opts.proxyUrl ?? process.env.BUILDAUTOMATON_FIREHOSE_URL ?? process.env.BUILDAUTOMATON_PROXY_URL ?? DEFAULT_FIREHOSE_URL;
|
|
43866
44232
|
const bridgeRootOpt = (opts.bridgeRoot && typeof opts.bridgeRoot === "string" && opts.bridgeRoot.trim() ? opts.bridgeRoot.trim() : null) ?? (opts.cwd && typeof opts.cwd === "string" && opts.cwd.trim() ? opts.cwd.trim() : null);
|
|
43867
44233
|
if (bridgeRootOpt) {
|
|
43868
|
-
const resolvedBridgeRoot =
|
|
44234
|
+
const resolvedBridgeRoot = path61.resolve(process.cwd(), bridgeRootOpt);
|
|
43869
44235
|
try {
|
|
43870
|
-
const st =
|
|
44236
|
+
const st = fs46.statSync(resolvedBridgeRoot);
|
|
43871
44237
|
if (!st.isDirectory()) {
|
|
43872
44238
|
console.error(`Bridge root is not a directory: ${resolvedBridgeRoot}`);
|
|
43873
44239
|
process.exit(1);
|
|
@@ -43887,7 +44253,7 @@ async function runCliAction(program2, opts) {
|
|
|
43887
44253
|
);
|
|
43888
44254
|
let worktreesRootPath;
|
|
43889
44255
|
if (opts.worktreesRoot && opts.worktreesRoot.trim()) {
|
|
43890
|
-
worktreesRootPath =
|
|
44256
|
+
worktreesRootPath = path61.resolve(opts.worktreesRoot.trim());
|
|
43891
44257
|
}
|
|
43892
44258
|
const e2eCertificates = opts.e2eeCertificatesDir?.trim() ? await loadOrCreateE2eCertificates(opts.e2eeCertificatesDir.trim()) : void 0;
|
|
43893
44259
|
if (e2eCertificates) {
|