@buildautomaton/cli 0.1.45 → 0.1.47
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/bridge-mcp-server.js +161 -0
- package/dist/bridge-mcp-server.js.map +7 -0
- package/dist/cli.js +1159 -453
- package/dist/cli.js.map +4 -4
- package/dist/fork-history-mcp-server.js +132 -0
- package/dist/fork-history-mcp-server.js.map +7 -0
- package/dist/index.js +1138 -432
- package/dist/index.js.map +4 -4
- package/dist/migrations/004_bridge_runtime.sql +14 -0
- 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 path48 = __require("node:path");
|
|
977
|
+
var fs42 = __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 = path48.resolve(baseDir, baseName);
|
|
1910
|
+
if (fs42.existsSync(localBin)) return localBin;
|
|
1911
|
+
if (sourceExt.includes(path48.extname(baseName))) return void 0;
|
|
1912
1912
|
const foundExt = sourceExt.find(
|
|
1913
|
-
(ext) =>
|
|
1913
|
+
(ext) => fs42.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 = fs42.realpathSync(this._scriptPath);
|
|
1926
1926
|
} catch (err) {
|
|
1927
1927
|
resolvedScriptPath = this._scriptPath;
|
|
1928
1928
|
}
|
|
1929
|
-
executableDir =
|
|
1930
|
-
|
|
1929
|
+
executableDir = path48.resolve(
|
|
1930
|
+
path48.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 = path48.basename(
|
|
1938
1938
|
this._scriptPath,
|
|
1939
|
-
|
|
1939
|
+
path48.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(path48.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 = path48.basename(filename, path48.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(path49) {
|
|
2805
|
+
if (path49 === void 0) return this._executableDir;
|
|
2806
|
+
this._executableDir = path49;
|
|
2807
2807
|
return this;
|
|
2808
2808
|
}
|
|
2809
2809
|
/**
|
|
@@ -5233,7 +5233,7 @@ var require_websocket = __commonJS({
|
|
|
5233
5233
|
"use strict";
|
|
5234
5234
|
var EventEmitter2 = __require("events");
|
|
5235
5235
|
var https2 = __require("https");
|
|
5236
|
-
var
|
|
5236
|
+
var http2 = __require("http");
|
|
5237
5237
|
var net = __require("net");
|
|
5238
5238
|
var tls = __require("tls");
|
|
5239
5239
|
var { randomBytes: randomBytes3, createHash: createHash3 } = __require("crypto");
|
|
@@ -5767,7 +5767,7 @@ var require_websocket = __commonJS({
|
|
|
5767
5767
|
}
|
|
5768
5768
|
const defaultPort = isSecure ? 443 : 80;
|
|
5769
5769
|
const key = randomBytes3(16).toString("base64");
|
|
5770
|
-
const request = isSecure ? https2.request :
|
|
5770
|
+
const request = isSecure ? https2.request : http2.request;
|
|
5771
5771
|
const protocolSet = /* @__PURE__ */ new Set();
|
|
5772
5772
|
let perMessageDeflate;
|
|
5773
5773
|
opts.createConnection = opts.createConnection || (isSecure ? tlsConnect : netConnect);
|
|
@@ -6261,7 +6261,7 @@ var require_websocket_server = __commonJS({
|
|
|
6261
6261
|
"../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/websocket-server.js"(exports, module) {
|
|
6262
6262
|
"use strict";
|
|
6263
6263
|
var EventEmitter2 = __require("events");
|
|
6264
|
-
var
|
|
6264
|
+
var http2 = __require("http");
|
|
6265
6265
|
var { Duplex } = __require("stream");
|
|
6266
6266
|
var { createHash: createHash3 } = __require("crypto");
|
|
6267
6267
|
var extension = require_extension();
|
|
@@ -6336,8 +6336,8 @@ var require_websocket_server = __commonJS({
|
|
|
6336
6336
|
);
|
|
6337
6337
|
}
|
|
6338
6338
|
if (options.port != null) {
|
|
6339
|
-
this._server =
|
|
6340
|
-
const body =
|
|
6339
|
+
this._server = http2.createServer((req, res) => {
|
|
6340
|
+
const body = http2.STATUS_CODES[426];
|
|
6341
6341
|
res.writeHead(426, {
|
|
6342
6342
|
"Content-Length": body.length,
|
|
6343
6343
|
"Content-Type": "text/plain"
|
|
@@ -6624,7 +6624,7 @@ var require_websocket_server = __commonJS({
|
|
|
6624
6624
|
this.destroy();
|
|
6625
6625
|
}
|
|
6626
6626
|
function abortHandshake(socket, code, message, headers) {
|
|
6627
|
-
message = message ||
|
|
6627
|
+
message = message || http2.STATUS_CODES[code];
|
|
6628
6628
|
headers = {
|
|
6629
6629
|
Connection: "close",
|
|
6630
6630
|
"Content-Type": "text/html",
|
|
@@ -6633,7 +6633,7 @@ var require_websocket_server = __commonJS({
|
|
|
6633
6633
|
};
|
|
6634
6634
|
socket.once("finish", socket.destroy);
|
|
6635
6635
|
socket.end(
|
|
6636
|
-
`HTTP/1.1 ${code} ${
|
|
6636
|
+
`HTTP/1.1 ${code} ${http2.STATUS_CODES[code]}\r
|
|
6637
6637
|
` + Object.keys(headers).map((h) => `${h}: ${headers[h]}`).join("\r\n") + "\r\n\r\n" + message
|
|
6638
6638
|
);
|
|
6639
6639
|
}
|
|
@@ -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: path48, errorMaps, issueData } = params;
|
|
7065
|
+
const fullPath = [...path48, ...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, path48, key) {
|
|
7374
7374
|
this._cachedPath = [];
|
|
7375
7375
|
this.parent = parent;
|
|
7376
7376
|
this.data = value;
|
|
7377
|
-
this._path =
|
|
7377
|
+
this._path = path48;
|
|
7378
7378
|
this._key = key;
|
|
7379
7379
|
}
|
|
7380
7380
|
get path() {
|
|
@@ -11099,15 +11099,15 @@ function assignProp(target, prop, value) {
|
|
|
11099
11099
|
configurable: true
|
|
11100
11100
|
});
|
|
11101
11101
|
}
|
|
11102
|
-
function getElementAtPath(obj,
|
|
11103
|
-
if (!
|
|
11102
|
+
function getElementAtPath(obj, path48) {
|
|
11103
|
+
if (!path48)
|
|
11104
11104
|
return obj;
|
|
11105
|
-
return
|
|
11105
|
+
return path48.reduce((acc, key) => acc?.[key], obj);
|
|
11106
11106
|
}
|
|
11107
11107
|
function promiseAllObject(promisesObj) {
|
|
11108
11108
|
const keys = Object.keys(promisesObj);
|
|
11109
|
-
const
|
|
11110
|
-
return Promise.all(
|
|
11109
|
+
const promises6 = keys.map((key) => promisesObj[key]);
|
|
11110
|
+
return Promise.all(promises6).then((results) => {
|
|
11111
11111
|
const resolvedObj = {};
|
|
11112
11112
|
for (let i = 0; i < keys.length; i++) {
|
|
11113
11113
|
resolvedObj[keys[i]] = results[i];
|
|
@@ -11351,11 +11351,11 @@ function aborted(x, startIndex = 0) {
|
|
|
11351
11351
|
}
|
|
11352
11352
|
return false;
|
|
11353
11353
|
}
|
|
11354
|
-
function prefixIssues(
|
|
11354
|
+
function prefixIssues(path48, 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(path48);
|
|
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, path48 = []) => {
|
|
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 = [...path48, ...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(path48) {
|
|
11588
11588
|
const segs = [];
|
|
11589
|
-
for (const seg of
|
|
11589
|
+
for (const seg of path48) {
|
|
11590
11590
|
if (typeof seg === "number")
|
|
11591
11591
|
segs.push(`[${seg}]`);
|
|
11592
11592
|
else if (typeof seg === "symbol")
|
|
@@ -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(path48, isFile, isDirectory) {
|
|
25073
|
+
log2(`checking %s`, path48);
|
|
25074
25074
|
try {
|
|
25075
|
-
const stat3 = fs_1.statSync(
|
|
25075
|
+
const stat3 = fs_1.statSync(path48);
|
|
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(path48, type = exports.READABLE) {
|
|
25096
|
+
return check2(path48, (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.47".length > 0 ? "0.1.47" : "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 fs41 from "node:fs";
|
|
25185
|
+
import * as path47 from "node:path";
|
|
25186
25186
|
|
|
25187
25187
|
// src/cli-log-level.ts
|
|
25188
25188
|
var verbosity = "info";
|
|
@@ -27479,6 +27479,7 @@ var AcpPromptRoutingRegistry = class {
|
|
|
27479
27479
|
function createAcpManagerContext(options) {
|
|
27480
27480
|
return {
|
|
27481
27481
|
log: options.log,
|
|
27482
|
+
getBridgeAccessPort: options.getBridgeAccessPort,
|
|
27482
27483
|
reportAgentCapabilities: options.reportAgentCapabilities,
|
|
27483
27484
|
backendFallbackAgentType: null,
|
|
27484
27485
|
acpAgents: /* @__PURE__ */ new Map(),
|
|
@@ -27818,9 +27819,9 @@ function parseChangeSummaryJson(raw, allowedPaths, options) {
|
|
|
27818
27819
|
const rawPath = typeof o.path === "string" ? o.path.trim() : "";
|
|
27819
27820
|
const summary = typeof o.summary === "string" ? o.summary.trim() : "";
|
|
27820
27821
|
if (!rawPath || !summary) continue;
|
|
27821
|
-
const
|
|
27822
|
-
if (!
|
|
27823
|
-
rows.push({ path:
|
|
27822
|
+
const path48 = skip ? normalizeRepoRelativePath(rawPath) || rawPath : resolveChangeSummaryPathAgainstAllowed(rawPath, allowedPaths);
|
|
27823
|
+
if (!path48) continue;
|
|
27824
|
+
rows.push({ path: path48, summary: clampSummaryToAtMostTwoLines(summary) });
|
|
27824
27825
|
}
|
|
27825
27826
|
return rows;
|
|
27826
27827
|
}
|
|
@@ -28751,7 +28752,7 @@ function createSdkStdioSessionContext(options) {
|
|
|
28751
28752
|
return {
|
|
28752
28753
|
cwd: options.cwd,
|
|
28753
28754
|
onFileChange: options.onFileChange,
|
|
28754
|
-
mcpServers: [],
|
|
28755
|
+
mcpServers: options.mcpServers ?? [],
|
|
28755
28756
|
persistedAcpSessionId: options.persistedAcpSessionId,
|
|
28756
28757
|
agentLabel: "ACP",
|
|
28757
28758
|
suppressLoadReplay: suppressLoadReplayRef,
|
|
@@ -29194,6 +29195,7 @@ async function createSdkStdioAcpClient(options) {
|
|
|
29194
29195
|
});
|
|
29195
29196
|
const sessionCtx = createSdkStdioSessionContext({
|
|
29196
29197
|
cwd,
|
|
29198
|
+
mcpServers: options.mcpServers,
|
|
29197
29199
|
persistedAcpSessionId,
|
|
29198
29200
|
backendAgentType,
|
|
29199
29201
|
agentConfig,
|
|
@@ -29325,7 +29327,7 @@ function createCursorAcpSessionContext(options) {
|
|
|
29325
29327
|
return {
|
|
29326
29328
|
cwd: options.cwd,
|
|
29327
29329
|
onFileChange: options.onFileChange,
|
|
29328
|
-
mcpServers: [],
|
|
29330
|
+
mcpServers: options.mcpServers ?? [],
|
|
29329
29331
|
persistedAcpSessionId: options.persistedAcpSessionId,
|
|
29330
29332
|
agentLabel: "Cursor",
|
|
29331
29333
|
suppressLoadReplay: suppressLoadReplayRef,
|
|
@@ -29751,6 +29753,7 @@ async function createCursorAcpClient(options) {
|
|
|
29751
29753
|
});
|
|
29752
29754
|
const sessionCtx = createCursorAcpSessionContext({
|
|
29753
29755
|
cwd,
|
|
29756
|
+
mcpServers: options.mcpServers,
|
|
29754
29757
|
persistedAcpSessionId,
|
|
29755
29758
|
backendAgentType,
|
|
29756
29759
|
agentConfig: options.agentConfig,
|
|
@@ -30709,6 +30712,30 @@ function computeAcpAgentKey(preferredAgentType, mode, agentConfig) {
|
|
|
30709
30712
|
return `${resolved.label}::${fullCmd.join("\0")}${cacheConfig}`;
|
|
30710
30713
|
}
|
|
30711
30714
|
|
|
30715
|
+
// src/mcp/bridge-mcp-constants.ts
|
|
30716
|
+
var BRIDGE_MCP_SERVER_NAME = "buildautomaton-bridge";
|
|
30717
|
+
|
|
30718
|
+
// src/mcp/resolve-bridge-mcp-server-path.ts
|
|
30719
|
+
import { dirname as dirname4, join as join3 } from "node:path";
|
|
30720
|
+
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
30721
|
+
function resolveBridgeMcpServerScriptPath() {
|
|
30722
|
+
const cliDir = dirname4(fileURLToPath4(import.meta.url));
|
|
30723
|
+
return join3(cliDir, "bridge-mcp-server.js");
|
|
30724
|
+
}
|
|
30725
|
+
|
|
30726
|
+
// src/mcp/build-acp-mcp-servers.ts
|
|
30727
|
+
function buildAcpMcpServers(bridgeAccessPort) {
|
|
30728
|
+
if (!Number.isFinite(bridgeAccessPort) || bridgeAccessPort <= 0) return [];
|
|
30729
|
+
return [
|
|
30730
|
+
{
|
|
30731
|
+
name: BRIDGE_MCP_SERVER_NAME,
|
|
30732
|
+
command: process.execPath,
|
|
30733
|
+
args: [resolveBridgeMcpServerScriptPath(), String(bridgeAccessPort)],
|
|
30734
|
+
env: []
|
|
30735
|
+
}
|
|
30736
|
+
];
|
|
30737
|
+
}
|
|
30738
|
+
|
|
30712
30739
|
// src/agents/acp/ensure-acp-client.ts
|
|
30713
30740
|
function invalidateAcpClientState(state) {
|
|
30714
30741
|
state.clientEpoch += 1;
|
|
@@ -30740,11 +30767,13 @@ async function ensureAcpClient(options) {
|
|
|
30740
30767
|
sessionParentPath,
|
|
30741
30768
|
resolveRouting,
|
|
30742
30769
|
cloudSessionId,
|
|
30770
|
+
bridgeAccessPort,
|
|
30743
30771
|
reportAgentCapabilities,
|
|
30744
30772
|
sendSessionUpdate,
|
|
30745
30773
|
sendRequest,
|
|
30746
30774
|
log: log2
|
|
30747
30775
|
} = options;
|
|
30776
|
+
const mcpServers = buildAcpMcpServers(bridgeAccessPort ?? 0);
|
|
30748
30777
|
state.latestAgentConfigForBridgeHooks = agentConfig != null && typeof agentConfig === "object" && !Array.isArray(agentConfig) ? agentConfig : null;
|
|
30749
30778
|
const targetSessionParentPath = resolveSessionParentPathForAgentProcess(sessionParentPath);
|
|
30750
30779
|
if (state.acpStartPromise && !state.acpHandle) {
|
|
@@ -30860,7 +30889,8 @@ async function ensureAcpClient(options) {
|
|
|
30860
30889
|
state.lastAcpStartError = "Agent subprocess exited";
|
|
30861
30890
|
},
|
|
30862
30891
|
...hooks,
|
|
30863
|
-
cwd: targetSessionParentPath
|
|
30892
|
+
cwd: targetSessionParentPath,
|
|
30893
|
+
mcpServers
|
|
30864
30894
|
}).then(async (h) => {
|
|
30865
30895
|
if (spawnEpoch !== state.clientEpoch || isCliImmediateShutdownRequested()) {
|
|
30866
30896
|
try {
|
|
@@ -30951,8 +30981,8 @@ function pathspec(...paths) {
|
|
|
30951
30981
|
cache.set(key, paths);
|
|
30952
30982
|
return key;
|
|
30953
30983
|
}
|
|
30954
|
-
function isPathSpec(
|
|
30955
|
-
return
|
|
30984
|
+
function isPathSpec(path48) {
|
|
30985
|
+
return path48 instanceof String && cache.has(path48);
|
|
30956
30986
|
}
|
|
30957
30987
|
function toPaths(pathSpec) {
|
|
30958
30988
|
return cache.get(pathSpec) || [];
|
|
@@ -31041,8 +31071,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
|
|
|
31041
31071
|
function forEachLineWithContent(input, callback) {
|
|
31042
31072
|
return toLinesWithContent(input, true).map((line) => callback(line));
|
|
31043
31073
|
}
|
|
31044
|
-
function folderExists(
|
|
31045
|
-
return (0, import_file_exists.exists)(
|
|
31074
|
+
function folderExists(path48) {
|
|
31075
|
+
return (0, import_file_exists.exists)(path48, import_file_exists.FOLDER);
|
|
31046
31076
|
}
|
|
31047
31077
|
function append(target, item) {
|
|
31048
31078
|
if (Array.isArray(target)) {
|
|
@@ -31446,8 +31476,8 @@ function checkIsRepoRootTask() {
|
|
|
31446
31476
|
commands,
|
|
31447
31477
|
format: "utf-8",
|
|
31448
31478
|
onError,
|
|
31449
|
-
parser(
|
|
31450
|
-
return /^\.(git)?$/.test(
|
|
31479
|
+
parser(path48) {
|
|
31480
|
+
return /^\.(git)?$/.test(path48.trim());
|
|
31451
31481
|
}
|
|
31452
31482
|
};
|
|
31453
31483
|
}
|
|
@@ -31881,11 +31911,11 @@ function parseGrep(grep) {
|
|
|
31881
31911
|
const paths = /* @__PURE__ */ new Set();
|
|
31882
31912
|
const results = {};
|
|
31883
31913
|
forEachLineWithContent(grep, (input) => {
|
|
31884
|
-
const [
|
|
31885
|
-
paths.add(
|
|
31886
|
-
(results[
|
|
31914
|
+
const [path48, line, preview] = input.split(NULL);
|
|
31915
|
+
paths.add(path48);
|
|
31916
|
+
(results[path48] = results[path48] || []).push({
|
|
31887
31917
|
line: asNumber(line),
|
|
31888
|
-
path:
|
|
31918
|
+
path: path48,
|
|
31889
31919
|
preview
|
|
31890
31920
|
});
|
|
31891
31921
|
});
|
|
@@ -32650,14 +32680,14 @@ var init_hash_object = __esm2({
|
|
|
32650
32680
|
init_task();
|
|
32651
32681
|
}
|
|
32652
32682
|
});
|
|
32653
|
-
function parseInit(bare,
|
|
32683
|
+
function parseInit(bare, path48, text) {
|
|
32654
32684
|
const response = String(text).trim();
|
|
32655
32685
|
let result;
|
|
32656
32686
|
if (result = initResponseRegex.exec(response)) {
|
|
32657
|
-
return new InitSummary(bare,
|
|
32687
|
+
return new InitSummary(bare, path48, false, result[1]);
|
|
32658
32688
|
}
|
|
32659
32689
|
if (result = reInitResponseRegex.exec(response)) {
|
|
32660
|
-
return new InitSummary(bare,
|
|
32690
|
+
return new InitSummary(bare, path48, true, result[1]);
|
|
32661
32691
|
}
|
|
32662
32692
|
let gitDir = "";
|
|
32663
32693
|
const tokens = response.split(" ");
|
|
@@ -32668,7 +32698,7 @@ function parseInit(bare, path46, text) {
|
|
|
32668
32698
|
break;
|
|
32669
32699
|
}
|
|
32670
32700
|
}
|
|
32671
|
-
return new InitSummary(bare,
|
|
32701
|
+
return new InitSummary(bare, path48, /^re/i.test(response), gitDir);
|
|
32672
32702
|
}
|
|
32673
32703
|
var InitSummary;
|
|
32674
32704
|
var initResponseRegex;
|
|
@@ -32677,9 +32707,9 @@ var init_InitSummary = __esm2({
|
|
|
32677
32707
|
"src/lib/responses/InitSummary.ts"() {
|
|
32678
32708
|
"use strict";
|
|
32679
32709
|
InitSummary = class {
|
|
32680
|
-
constructor(bare,
|
|
32710
|
+
constructor(bare, path48, existing, gitDir) {
|
|
32681
32711
|
this.bare = bare;
|
|
32682
|
-
this.path =
|
|
32712
|
+
this.path = path48;
|
|
32683
32713
|
this.existing = existing;
|
|
32684
32714
|
this.gitDir = gitDir;
|
|
32685
32715
|
}
|
|
@@ -32691,7 +32721,7 @@ var init_InitSummary = __esm2({
|
|
|
32691
32721
|
function hasBareCommand(command) {
|
|
32692
32722
|
return command.includes(bareCommand);
|
|
32693
32723
|
}
|
|
32694
|
-
function initTask(bare = false,
|
|
32724
|
+
function initTask(bare = false, path48, customArgs) {
|
|
32695
32725
|
const commands = ["init", ...customArgs];
|
|
32696
32726
|
if (bare && !hasBareCommand(commands)) {
|
|
32697
32727
|
commands.splice(1, 0, bareCommand);
|
|
@@ -32700,7 +32730,7 @@ function initTask(bare = false, path46, customArgs) {
|
|
|
32700
32730
|
commands,
|
|
32701
32731
|
format: "utf-8",
|
|
32702
32732
|
parser(text) {
|
|
32703
|
-
return parseInit(commands.includes("--bare"),
|
|
32733
|
+
return parseInit(commands.includes("--bare"), path48, text);
|
|
32704
32734
|
}
|
|
32705
32735
|
};
|
|
32706
32736
|
}
|
|
@@ -33516,12 +33546,12 @@ var init_FileStatusSummary = __esm2({
|
|
|
33516
33546
|
"use strict";
|
|
33517
33547
|
fromPathRegex = /^(.+)\0(.+)$/;
|
|
33518
33548
|
FileStatusSummary = class {
|
|
33519
|
-
constructor(
|
|
33520
|
-
this.path =
|
|
33549
|
+
constructor(path48, index, working_dir) {
|
|
33550
|
+
this.path = path48;
|
|
33521
33551
|
this.index = index;
|
|
33522
33552
|
this.working_dir = working_dir;
|
|
33523
33553
|
if (index === "R" || working_dir === "R") {
|
|
33524
|
-
const detail = fromPathRegex.exec(
|
|
33554
|
+
const detail = fromPathRegex.exec(path48) || [null, path48, path48];
|
|
33525
33555
|
this.from = detail[2] || "";
|
|
33526
33556
|
this.path = detail[1] || "";
|
|
33527
33557
|
}
|
|
@@ -33552,14 +33582,14 @@ function splitLine(result, lineStr) {
|
|
|
33552
33582
|
default:
|
|
33553
33583
|
return;
|
|
33554
33584
|
}
|
|
33555
|
-
function data(index, workingDir,
|
|
33585
|
+
function data(index, workingDir, path48) {
|
|
33556
33586
|
const raw = `${index}${workingDir}`;
|
|
33557
33587
|
const handler = parsers6.get(raw);
|
|
33558
33588
|
if (handler) {
|
|
33559
|
-
handler(result,
|
|
33589
|
+
handler(result, path48);
|
|
33560
33590
|
}
|
|
33561
33591
|
if (raw !== "##" && raw !== "!!") {
|
|
33562
|
-
result.files.push(new FileStatusSummary(
|
|
33592
|
+
result.files.push(new FileStatusSummary(path48, index, workingDir));
|
|
33563
33593
|
}
|
|
33564
33594
|
}
|
|
33565
33595
|
}
|
|
@@ -33868,9 +33898,9 @@ var init_simple_git_api = __esm2({
|
|
|
33868
33898
|
next
|
|
33869
33899
|
);
|
|
33870
33900
|
}
|
|
33871
|
-
hashObject(
|
|
33901
|
+
hashObject(path48, write) {
|
|
33872
33902
|
return this._runTask(
|
|
33873
|
-
hashObjectTask(
|
|
33903
|
+
hashObjectTask(path48, write === true),
|
|
33874
33904
|
trailingFunctionArgument(arguments)
|
|
33875
33905
|
);
|
|
33876
33906
|
}
|
|
@@ -34223,8 +34253,8 @@ var init_branch = __esm2({
|
|
|
34223
34253
|
}
|
|
34224
34254
|
});
|
|
34225
34255
|
function toPath(input) {
|
|
34226
|
-
const
|
|
34227
|
-
return
|
|
34256
|
+
const path48 = input.trim().replace(/^["']|["']$/g, "");
|
|
34257
|
+
return path48 && normalize3(path48);
|
|
34228
34258
|
}
|
|
34229
34259
|
var parseCheckIgnore;
|
|
34230
34260
|
var init_CheckIgnore = __esm2({
|
|
@@ -34538,8 +34568,8 @@ __export2(sub_module_exports, {
|
|
|
34538
34568
|
subModuleTask: () => subModuleTask,
|
|
34539
34569
|
updateSubModuleTask: () => updateSubModuleTask
|
|
34540
34570
|
});
|
|
34541
|
-
function addSubModuleTask(repo,
|
|
34542
|
-
return subModuleTask(["add", repo,
|
|
34571
|
+
function addSubModuleTask(repo, path48) {
|
|
34572
|
+
return subModuleTask(["add", repo, path48]);
|
|
34543
34573
|
}
|
|
34544
34574
|
function initSubModuleTask(customArgs) {
|
|
34545
34575
|
return subModuleTask(["init", ...customArgs]);
|
|
@@ -34872,8 +34902,8 @@ var require_git = __commonJS2({
|
|
|
34872
34902
|
}
|
|
34873
34903
|
return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
|
|
34874
34904
|
};
|
|
34875
|
-
Git2.prototype.submoduleAdd = function(repo,
|
|
34876
|
-
return this._runTask(addSubModuleTask2(repo,
|
|
34905
|
+
Git2.prototype.submoduleAdd = function(repo, path48, then) {
|
|
34906
|
+
return this._runTask(addSubModuleTask2(repo, path48), trailingFunctionArgument2(arguments));
|
|
34877
34907
|
};
|
|
34878
34908
|
Git2.prototype.submoduleUpdate = function(args, then) {
|
|
34879
34909
|
return this._runTask(
|
|
@@ -35836,12 +35866,12 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
|
|
|
35836
35866
|
// src/agents/acp/put-summarize-change-summaries.ts
|
|
35837
35867
|
async function putEncryptedChangeSummaryRows(params) {
|
|
35838
35868
|
const base = params.apiBaseUrl.replace(/\/+$/, "");
|
|
35839
|
-
const entries = params.rows.map(({ path:
|
|
35869
|
+
const entries = params.rows.map(({ path: path48, summary }) => {
|
|
35840
35870
|
const enc = params.e2ee.encryptFields({ summary }, ["summary"]);
|
|
35841
|
-
return { path:
|
|
35871
|
+
return { path: path48, summary: JSON.stringify(enc) };
|
|
35842
35872
|
});
|
|
35843
35873
|
const res = await fetch(
|
|
35844
|
-
`${base}/
|
|
35874
|
+
`${base}/internal/sessions/${encodeURIComponent(params.sessionId)}/follow-ups/summarize-changes`,
|
|
35845
35875
|
{
|
|
35846
35876
|
method: "PUT",
|
|
35847
35877
|
headers: {
|
|
@@ -35980,8 +36010,8 @@ async function fetchSessionAttachmentPayloadsForAgent(params) {
|
|
|
35980
36010
|
for (const a of attachments) {
|
|
35981
36011
|
const id = typeof a.attachmentId === "string" ? a.attachmentId.trim() : "";
|
|
35982
36012
|
if (!id) continue;
|
|
35983
|
-
const metaUrl = `${base}/
|
|
35984
|
-
const blobUrl = `${base}/
|
|
36013
|
+
const metaUrl = `${base}/internal/sessions/${encodeURIComponent(sessionId)}/attachments/${encodeURIComponent(id)}/meta`;
|
|
36014
|
+
const blobUrl = `${base}/internal/sessions/${encodeURIComponent(sessionId)}/attachments/${encodeURIComponent(id)}/blob`;
|
|
35985
36015
|
const metaRes = await fetch(metaUrl, { headers: { Authorization: `Bearer ${token}` } });
|
|
35986
36016
|
if (!metaRes.ok) {
|
|
35987
36017
|
const t = await metaRes.text().catch(() => "");
|
|
@@ -36029,6 +36059,106 @@ async function fetchSessionAttachmentPayloadsForAgent(params) {
|
|
|
36029
36059
|
return { ok: true, images: out };
|
|
36030
36060
|
}
|
|
36031
36061
|
|
|
36062
|
+
// src/agents/acp/build-forked-session-agent-prompt.ts
|
|
36063
|
+
function buildForkedSessionAgentPrompt(userPrompt, childSessionId, parentSessionId) {
|
|
36064
|
+
const childRef = childSessionId.trim();
|
|
36065
|
+
const parentRef = parentSessionId.trim();
|
|
36066
|
+
return [
|
|
36067
|
+
"You are working in a new session that was forked from a prior session.",
|
|
36068
|
+
`This forked session id: ${childRef}`,
|
|
36069
|
+
`Parent session id: ${parentRef}`,
|
|
36070
|
+
"",
|
|
36071
|
+
"The parent session history is available through MCP tools on the buildautomaton-bridge server:",
|
|
36072
|
+
"- list_parent_session_prompts \u2014 list each user prompt / turn in the parent session",
|
|
36073
|
+
"- get_parent_session_turn_transcript \u2014 full transcript for one turn (including tool calls); requires turnId",
|
|
36074
|
+
"",
|
|
36075
|
+
"When calling these tools, always pass this forked session id as sessionId (not the parent session id).",
|
|
36076
|
+
"",
|
|
36077
|
+
"Use these tools when you need prior context instead of guessing.",
|
|
36078
|
+
"",
|
|
36079
|
+
"User request:",
|
|
36080
|
+
userPrompt.trim()
|
|
36081
|
+
].join("\n");
|
|
36082
|
+
}
|
|
36083
|
+
|
|
36084
|
+
// src/mcp/tools/session-history/fetch-parent-session-cloud.ts
|
|
36085
|
+
function internalApiBase(cloudApiBaseUrl) {
|
|
36086
|
+
return cloudApiBaseUrl.replace(/\/+$/, "");
|
|
36087
|
+
}
|
|
36088
|
+
async function fetchCloudSessionMeta(params) {
|
|
36089
|
+
const token = params.getCloudAccessToken();
|
|
36090
|
+
if (!token) return { ok: false, error: "Missing cloud access token." };
|
|
36091
|
+
const url2 = `${internalApiBase(params.cloudApiBaseUrl)}/internal/sessions/${encodeURIComponent(params.sessionId)}/meta`;
|
|
36092
|
+
const res = await fetch(url2, { headers: { Authorization: `Bearer ${token}` } });
|
|
36093
|
+
if (!res.ok) {
|
|
36094
|
+
const t = await res.text().catch(() => "");
|
|
36095
|
+
return { ok: false, error: `Session meta fetch failed (${res.status}): ${t.slice(0, 200)}` };
|
|
36096
|
+
}
|
|
36097
|
+
const data = await res.json().catch(() => null);
|
|
36098
|
+
if (!data) return { ok: false, error: "Invalid session meta response." };
|
|
36099
|
+
return { ok: true, data };
|
|
36100
|
+
}
|
|
36101
|
+
async function fetchCloudParentSessionPrompts(params) {
|
|
36102
|
+
const token = params.getCloudAccessToken();
|
|
36103
|
+
if (!token) return { ok: false, error: "Missing cloud access token." };
|
|
36104
|
+
const url2 = `${internalApiBase(params.cloudApiBaseUrl)}/internal/sessions/${encodeURIComponent(params.childSessionId)}/parent/prompts`;
|
|
36105
|
+
const res = await fetch(url2, { headers: { Authorization: `Bearer ${token}` } });
|
|
36106
|
+
if (!res.ok) {
|
|
36107
|
+
const t = await res.text().catch(() => "");
|
|
36108
|
+
return { ok: false, error: `Parent prompts fetch failed (${res.status}): ${t.slice(0, 200)}` };
|
|
36109
|
+
}
|
|
36110
|
+
const data = await res.json().catch(() => null);
|
|
36111
|
+
if (!data) return { ok: false, error: "Invalid parent prompts response." };
|
|
36112
|
+
return { ok: true, data };
|
|
36113
|
+
}
|
|
36114
|
+
async function fetchCloudParentTurnTranscript(params) {
|
|
36115
|
+
const token = params.getCloudAccessToken();
|
|
36116
|
+
if (!token) return { ok: false, error: "Missing cloud access token." };
|
|
36117
|
+
const url2 = `${internalApiBase(params.cloudApiBaseUrl)}/internal/sessions/${encodeURIComponent(params.childSessionId)}/parent/turns/${encodeURIComponent(params.turnId)}/transcript`;
|
|
36118
|
+
const res = await fetch(url2, { headers: { Authorization: `Bearer ${token}` } });
|
|
36119
|
+
if (!res.ok) {
|
|
36120
|
+
const t = await res.text().catch(() => "");
|
|
36121
|
+
return { ok: false, error: `Transcript fetch failed (${res.status}): ${t.slice(0, 200)}` };
|
|
36122
|
+
}
|
|
36123
|
+
const data = await res.json().catch(() => null);
|
|
36124
|
+
const rows = Array.isArray(data?.transcript) ? data.transcript : [];
|
|
36125
|
+
const lines = rows.map((row) => {
|
|
36126
|
+
const kind = typeof row.kind === "string" ? row.kind : "event";
|
|
36127
|
+
const payload = typeof row.payload === "string" ? row.payload : JSON.stringify(row.payload ?? "");
|
|
36128
|
+
return `[${kind}] ${payload}`;
|
|
36129
|
+
});
|
|
36130
|
+
return { ok: true, text: lines.join("\n") };
|
|
36131
|
+
}
|
|
36132
|
+
async function resolveParentSessionIdForChild(params) {
|
|
36133
|
+
const meta = await fetchCloudSessionMeta({
|
|
36134
|
+
sessionId: params.childSessionId,
|
|
36135
|
+
cloudApiBaseUrl: params.cloudApiBaseUrl,
|
|
36136
|
+
getCloudAccessToken: params.getCloudAccessToken
|
|
36137
|
+
});
|
|
36138
|
+
if (!meta.ok) return meta;
|
|
36139
|
+
const parent = meta.data.parentSessionId?.trim() ?? "";
|
|
36140
|
+
if (!parent) {
|
|
36141
|
+
return {
|
|
36142
|
+
ok: false,
|
|
36143
|
+
error: `Session ${params.childSessionId} has no parent session history. It was not forked from another session (no parent_session_id). Pass the forked child session id as sessionId, not the parent session id.`
|
|
36144
|
+
};
|
|
36145
|
+
}
|
|
36146
|
+
return { ok: true, parentSessionId: parent };
|
|
36147
|
+
}
|
|
36148
|
+
|
|
36149
|
+
// src/agents/acp/enrich-forked-session-prompt-for-agent.ts
|
|
36150
|
+
async function enrichForkedSessionPromptForAgent(params) {
|
|
36151
|
+
const meta = await fetchCloudSessionMeta({
|
|
36152
|
+
sessionId: params.sessionId,
|
|
36153
|
+
cloudApiBaseUrl: params.cloudApiBaseUrl,
|
|
36154
|
+
getCloudAccessToken: params.getCloudAccessToken
|
|
36155
|
+
});
|
|
36156
|
+
if (!meta.ok) return params.promptText;
|
|
36157
|
+
const parentSessionId = meta.data.parentSessionId?.trim() ?? "";
|
|
36158
|
+
if (!parentSessionId) return params.promptText;
|
|
36159
|
+
return buildForkedSessionAgentPrompt(params.promptText, params.sessionId, parentSessionId);
|
|
36160
|
+
}
|
|
36161
|
+
|
|
36032
36162
|
// src/agents/acp/send-prompt-to-agent.ts
|
|
36033
36163
|
async function sendPromptToAgent(options) {
|
|
36034
36164
|
const {
|
|
@@ -36050,6 +36180,15 @@ async function sendPromptToAgent(options) {
|
|
|
36050
36180
|
attachments
|
|
36051
36181
|
} = options;
|
|
36052
36182
|
try {
|
|
36183
|
+
let agentPromptText = promptText;
|
|
36184
|
+
if (sessionId && cloudApiBaseUrl && getCloudAccessToken) {
|
|
36185
|
+
agentPromptText = await enrichForkedSessionPromptForAgent({
|
|
36186
|
+
sessionId,
|
|
36187
|
+
promptText,
|
|
36188
|
+
cloudApiBaseUrl,
|
|
36189
|
+
getCloudAccessToken
|
|
36190
|
+
});
|
|
36191
|
+
}
|
|
36053
36192
|
let sendOpts = {};
|
|
36054
36193
|
if (attachments && attachments.length > 0) {
|
|
36055
36194
|
if (!sessionId || !cloudApiBaseUrl || !getCloudAccessToken) {
|
|
@@ -36088,7 +36227,7 @@ async function sendPromptToAgent(options) {
|
|
|
36088
36227
|
}
|
|
36089
36228
|
sendOpts = { images: resolved.images };
|
|
36090
36229
|
}
|
|
36091
|
-
const result = await handle.sendPrompt(
|
|
36230
|
+
const result = await handle.sendPrompt(agentPromptText, sendOpts);
|
|
36092
36231
|
if (sessionId && runId && sendSessionUpdate && agentCwd && result.success) {
|
|
36093
36232
|
await collectTurnGitDiffFromPreTurnSnapshot({
|
|
36094
36233
|
sessionId,
|
|
@@ -36212,6 +36351,7 @@ function handlePrompt(ctx, opts) {
|
|
|
36212
36351
|
sessionParentPath,
|
|
36213
36352
|
resolveRouting: () => ctx.promptRouting.resolveRouting(activeAcpSessionAgentKey),
|
|
36214
36353
|
cloudSessionId: sessionId,
|
|
36354
|
+
bridgeAccessPort: ctx.getBridgeAccessPort(),
|
|
36215
36355
|
sendSessionUpdate,
|
|
36216
36356
|
sendRequest: sendSessionUpdate,
|
|
36217
36357
|
log: ctx.log,
|
|
@@ -36298,6 +36438,7 @@ function logPromptReceivedFromBridge(ctx, opts) {
|
|
|
36298
36438
|
async function createAcpManager(options) {
|
|
36299
36439
|
const ctx = createAcpManagerContext({
|
|
36300
36440
|
log: options.log,
|
|
36441
|
+
getBridgeAccessPort: options.getBridgeAccessPort,
|
|
36301
36442
|
reportAgentCapabilities: options.reportAgentCapabilities
|
|
36302
36443
|
});
|
|
36303
36444
|
return {
|
|
@@ -36321,7 +36462,7 @@ async function createAcpManager(options) {
|
|
|
36321
36462
|
// src/git/changes/types.ts
|
|
36322
36463
|
var MAX_PATCH_CHARS = 35e4;
|
|
36323
36464
|
|
|
36324
|
-
// src/git/changes/
|
|
36465
|
+
// src/git/changes/paths/repo-format.ts
|
|
36325
36466
|
function posixJoinDirFile(dir, file2) {
|
|
36326
36467
|
const d = dir === "." || dir === "" ? "" : dir.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
36327
36468
|
const f = file2.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
@@ -36376,7 +36517,7 @@ function formatRemoteDisplayLabel(remoteUrl) {
|
|
|
36376
36517
|
}
|
|
36377
36518
|
|
|
36378
36519
|
// src/git/changes/get-working-tree-change-repo-details.ts
|
|
36379
|
-
import * as
|
|
36520
|
+
import * as path23 from "node:path";
|
|
36380
36521
|
|
|
36381
36522
|
// src/git/commits/resolve-remote-tracking.ts
|
|
36382
36523
|
async function tryConfigGet(g, key) {
|
|
@@ -36546,9 +36687,232 @@ async function listRecentCommits(repoDir, limitInput) {
|
|
|
36546
36687
|
}
|
|
36547
36688
|
}
|
|
36548
36689
|
|
|
36549
|
-
// src/git/changes/
|
|
36550
|
-
function
|
|
36551
|
-
|
|
36690
|
+
// src/git/changes/parse/normalize-git-diff-path.ts
|
|
36691
|
+
function normalizeGitDiffPath(path48) {
|
|
36692
|
+
return path48.replace(/\\/g, "/").replace(/\/ +/g, "/").trim();
|
|
36693
|
+
}
|
|
36694
|
+
|
|
36695
|
+
// src/git/changes/parse/expand-git-rename-numstat-path.ts
|
|
36696
|
+
function expandGitRenameNumstatPath(rawPath) {
|
|
36697
|
+
const open2 = rawPath.indexOf("{");
|
|
36698
|
+
const arrow = rawPath.indexOf("=>");
|
|
36699
|
+
const close = rawPath.lastIndexOf("}");
|
|
36700
|
+
if (open2 === -1 || arrow === -1 || close === -1 || open2 > arrow || arrow > close) {
|
|
36701
|
+
return null;
|
|
36702
|
+
}
|
|
36703
|
+
const prefix = rawPath.slice(0, open2);
|
|
36704
|
+
const suffix = rawPath.slice(close + 1);
|
|
36705
|
+
const oldPart = rawPath.slice(open2 + 1, arrow).trim();
|
|
36706
|
+
const newPart = rawPath.slice(arrow + 2, close).trim();
|
|
36707
|
+
return {
|
|
36708
|
+
oldPath: normalizeGitDiffPath(`${prefix}${oldPart}${suffix}`),
|
|
36709
|
+
newPath: normalizeGitDiffPath(`${prefix}${newPart}${suffix}`)
|
|
36710
|
+
};
|
|
36711
|
+
}
|
|
36712
|
+
function isGitRenameNumstatPath(rawPath) {
|
|
36713
|
+
return expandGitRenameNumstatPath(rawPath) != null;
|
|
36714
|
+
}
|
|
36715
|
+
|
|
36716
|
+
// src/git/changes/rows/build-changed-file-row.ts
|
|
36717
|
+
function buildChangedFileRow(options) {
|
|
36718
|
+
const pathInRepo = normalizeGitDiffPath(options.pathInRepo);
|
|
36719
|
+
const relLauncher = posixJoinDirFile(options.repoRelPath, pathInRepo);
|
|
36720
|
+
const additions = options.numEntry?.additions ?? 0;
|
|
36721
|
+
const deletions = options.numEntry?.deletions ?? 0;
|
|
36722
|
+
let change = options.nameEntry?.change ?? "modified";
|
|
36723
|
+
let movedFromPathRelLauncher;
|
|
36724
|
+
let movedFromPathInRepo;
|
|
36725
|
+
const oldPathInRepo = options.nameEntry?.oldPathInRepo ?? options.numEntry?.oldPathInRepo;
|
|
36726
|
+
if (change === "moved" && oldPathInRepo) {
|
|
36727
|
+
movedFromPathInRepo = normalizeGitDiffPath(oldPathInRepo);
|
|
36728
|
+
movedFromPathRelLauncher = posixJoinDirFile(options.repoRelPath, movedFromPathInRepo);
|
|
36729
|
+
} else if (oldPathInRepo && change === "modified") {
|
|
36730
|
+
change = "moved";
|
|
36731
|
+
movedFromPathInRepo = normalizeGitDiffPath(oldPathInRepo);
|
|
36732
|
+
movedFromPathRelLauncher = posixJoinDirFile(options.repoRelPath, movedFromPathInRepo);
|
|
36733
|
+
}
|
|
36734
|
+
if (options.untracked && !options.nameEntry) {
|
|
36735
|
+
change = "added";
|
|
36736
|
+
} else if (!options.nameEntry && options.numEntry && change !== "moved") {
|
|
36737
|
+
if (additions > 0 && deletions === 0) change = "added";
|
|
36738
|
+
else if (deletions > 0 && additions === 0) change = "removed";
|
|
36739
|
+
else change = "modified";
|
|
36740
|
+
}
|
|
36741
|
+
return {
|
|
36742
|
+
pathRelLauncher: relLauncher,
|
|
36743
|
+
additions,
|
|
36744
|
+
deletions,
|
|
36745
|
+
change,
|
|
36746
|
+
...movedFromPathRelLauncher ? { movedFromPathRelLauncher } : {},
|
|
36747
|
+
...movedFromPathInRepo ? { movedFromPathInRepo } : {}
|
|
36748
|
+
};
|
|
36749
|
+
}
|
|
36750
|
+
|
|
36751
|
+
// src/git/changes/listing/build-changed-file-rows.ts
|
|
36752
|
+
async function buildChangedFileRowsFromPaths(options) {
|
|
36753
|
+
const rows = [];
|
|
36754
|
+
await forEachWithGitYield([...options.paths], async (pathInRepo) => {
|
|
36755
|
+
if (isGitRenameNumstatPath(pathInRepo)) return;
|
|
36756
|
+
if (options.movedSourcePaths.has(pathInRepo)) return;
|
|
36757
|
+
const nameEntry = options.nameByPath.get(pathInRepo);
|
|
36758
|
+
const numEntry = options.numByPath.get(pathInRepo);
|
|
36759
|
+
const row = buildChangedFileRow({
|
|
36760
|
+
pathInRepo,
|
|
36761
|
+
repoRelPath: options.repoRelPath,
|
|
36762
|
+
nameEntry,
|
|
36763
|
+
numEntry,
|
|
36764
|
+
untracked: options.untrackedSet?.has(pathInRepo)
|
|
36765
|
+
});
|
|
36766
|
+
if (options.applyUntrackedStats) {
|
|
36767
|
+
await options.applyUntrackedStats(row, pathInRepo);
|
|
36768
|
+
}
|
|
36769
|
+
rows.push(row);
|
|
36770
|
+
});
|
|
36771
|
+
return rows;
|
|
36772
|
+
}
|
|
36773
|
+
|
|
36774
|
+
// src/git/changes/patch/patch-truncate.ts
|
|
36775
|
+
function truncatePatch(s) {
|
|
36776
|
+
if (s.length <= MAX_PATCH_CHARS) return s;
|
|
36777
|
+
return `${s.slice(0, MAX_PATCH_CHARS)}
|
|
36778
|
+
|
|
36779
|
+
\u2026 (diff truncated)`;
|
|
36780
|
+
}
|
|
36781
|
+
|
|
36782
|
+
// src/git/changes/patch/unified-diff-for-file.ts
|
|
36783
|
+
function patchTextFromGitDiffOutput(raw) {
|
|
36784
|
+
if (raw.trim() === "") return void 0;
|
|
36785
|
+
return normalizePatchContent(raw);
|
|
36786
|
+
}
|
|
36787
|
+
async function unifiedDiffForFile(repoCwd, pathInRepo, change, movedFromPathInRepo) {
|
|
36788
|
+
const g = cliSimpleGit(repoCwd);
|
|
36789
|
+
const renameArgs = ["-M", "--find-renames"];
|
|
36790
|
+
const args = change === "moved" && movedFromPathInRepo ? ["diff", "--no-color", ...renameArgs, "HEAD", "--", movedFromPathInRepo, pathInRepo] : change === "removed" ? ["diff", "--no-color", ...renameArgs, "HEAD", "--", pathInRepo] : ["diff", "--no-color", ...renameArgs, "HEAD", "--", pathInRepo];
|
|
36791
|
+
const raw = await g.raw([...args]).catch(() => "");
|
|
36792
|
+
const patch = patchTextFromGitDiffOutput(String(raw));
|
|
36793
|
+
return patch ? truncatePatch(patch) : void 0;
|
|
36794
|
+
}
|
|
36795
|
+
async function unifiedDiffForFileInRange(repoCwd, range, pathInRepo, change, movedFromPathInRepo) {
|
|
36796
|
+
const g = cliSimpleGit(repoCwd);
|
|
36797
|
+
const renameArgs = ["-M", "--find-renames"];
|
|
36798
|
+
const args = change === "moved" && movedFromPathInRepo ? ["diff", "--no-color", ...renameArgs, "-U20000", range, "--", movedFromPathInRepo, pathInRepo] : ["diff", "--no-color", ...renameArgs, "-U20000", range, "--", pathInRepo];
|
|
36799
|
+
const raw = await g.raw([...args]).catch(() => "");
|
|
36800
|
+
const patch = patchTextFromGitDiffOutput(String(raw));
|
|
36801
|
+
return patch ? truncatePatch(patch) : void 0;
|
|
36802
|
+
}
|
|
36803
|
+
|
|
36804
|
+
// src/git/changes/total-lines/resolve-changed-file-total-lines.ts
|
|
36805
|
+
import * as fs17 from "node:fs";
|
|
36806
|
+
import * as path20 from "node:path";
|
|
36807
|
+
|
|
36808
|
+
// src/git/changes/lines/count-lines.ts
|
|
36809
|
+
import { createReadStream } from "node:fs";
|
|
36810
|
+
import * as readline2 from "node:readline";
|
|
36811
|
+
function countLinesInText(text) {
|
|
36812
|
+
return splitTextIntoDiffLines(text).length;
|
|
36813
|
+
}
|
|
36814
|
+
async function countTextFileLines(filePath) {
|
|
36815
|
+
let bytes = 0;
|
|
36816
|
+
const maxBytes = 512e3;
|
|
36817
|
+
let lines = 0;
|
|
36818
|
+
const stream = createReadStream(filePath, { encoding: "utf8" });
|
|
36819
|
+
const rl = readline2.createInterface({ input: stream, crlfDelay: Infinity });
|
|
36820
|
+
for await (const _line of rl) {
|
|
36821
|
+
lines += 1;
|
|
36822
|
+
bytes += Buffer.byteLength(String(_line), "utf8") + 1;
|
|
36823
|
+
if (bytes > maxBytes) {
|
|
36824
|
+
rl.close();
|
|
36825
|
+
stream.destroy();
|
|
36826
|
+
return lines;
|
|
36827
|
+
}
|
|
36828
|
+
}
|
|
36829
|
+
return lines;
|
|
36830
|
+
}
|
|
36831
|
+
|
|
36832
|
+
// src/git/changes/total-lines/resolve-changed-file-total-lines.ts
|
|
36833
|
+
var MAX_BYTES = 512e3;
|
|
36834
|
+
async function countLinesInGitRef(repoGitCwd, ref, pathInRepo) {
|
|
36835
|
+
const spec = `${ref}:${pathInRepo}`;
|
|
36836
|
+
const g = cliSimpleGit(repoGitCwd);
|
|
36837
|
+
try {
|
|
36838
|
+
await g.raw(["cat-file", "-e", spec]);
|
|
36839
|
+
} catch {
|
|
36840
|
+
return null;
|
|
36841
|
+
}
|
|
36842
|
+
try {
|
|
36843
|
+
const raw = String(await g.raw(["show", spec]));
|
|
36844
|
+
const capped = raw.length > MAX_BYTES ? raw.slice(0, MAX_BYTES) : raw;
|
|
36845
|
+
return countLinesInText(capped);
|
|
36846
|
+
} catch {
|
|
36847
|
+
return null;
|
|
36848
|
+
}
|
|
36849
|
+
}
|
|
36850
|
+
async function resolveWorkingTreeFileTotalLines(options) {
|
|
36851
|
+
if (options.isBinary) return null;
|
|
36852
|
+
const filePath = path20.join(options.repoGitCwd, options.pathInRepo);
|
|
36853
|
+
if (options.change === "removed") {
|
|
36854
|
+
return countLinesInGitRef(options.repoGitCwd, "HEAD", options.pathInRepo);
|
|
36855
|
+
}
|
|
36856
|
+
if (options.change === "moved" || options.change === "modified") {
|
|
36857
|
+
try {
|
|
36858
|
+
const st = await fs17.promises.stat(filePath);
|
|
36859
|
+
if (!st.isFile()) return null;
|
|
36860
|
+
return await countTextFileLines(filePath);
|
|
36861
|
+
} catch {
|
|
36862
|
+
return null;
|
|
36863
|
+
}
|
|
36864
|
+
}
|
|
36865
|
+
return null;
|
|
36866
|
+
}
|
|
36867
|
+
async function resolveCommitFileTotalLines(options) {
|
|
36868
|
+
if (options.isBinary) return null;
|
|
36869
|
+
if (options.change === "removed") {
|
|
36870
|
+
return countLinesInGitRef(options.repoGitCwd, options.parentSha, options.pathInRepo);
|
|
36871
|
+
}
|
|
36872
|
+
if (options.change === "moved" || options.change === "modified") {
|
|
36873
|
+
return countLinesInGitRef(options.repoGitCwd, options.commitSha, options.pathInRepo);
|
|
36874
|
+
}
|
|
36875
|
+
return null;
|
|
36876
|
+
}
|
|
36877
|
+
|
|
36878
|
+
// src/git/changes/listing/path-in-repo-from-launcher.ts
|
|
36879
|
+
function pathInRepoFromLauncher(pathRelLauncher, repoRelPath) {
|
|
36880
|
+
const normRel = repoRelPath === "." || repoRelPath === "" ? "." : repoRelPath;
|
|
36881
|
+
if (normRel === ".") return pathRelLauncher;
|
|
36882
|
+
if (pathRelLauncher.startsWith(`${normRel}/`)) {
|
|
36883
|
+
return pathRelLauncher.slice(normRel.length + 1);
|
|
36884
|
+
}
|
|
36885
|
+
return pathRelLauncher;
|
|
36886
|
+
}
|
|
36887
|
+
function normalizeRepoRelPath(repoRelPath) {
|
|
36888
|
+
return repoRelPath === "." || repoRelPath === "" ? "." : repoRelPath;
|
|
36889
|
+
}
|
|
36890
|
+
|
|
36891
|
+
// src/git/changes/listing/enrich-commit-file-rows.ts
|
|
36892
|
+
async function enrichCommitFileRows(options) {
|
|
36893
|
+
await forEachWithGitYield(options.rows, async (row) => {
|
|
36894
|
+
const pathInRepo = pathInRepoFromLauncher(row.pathRelLauncher, options.repoRelPath);
|
|
36895
|
+
row.patchContent = await unifiedDiffForFileInRange(
|
|
36896
|
+
options.repoGitCwd,
|
|
36897
|
+
options.range,
|
|
36898
|
+
pathInRepo,
|
|
36899
|
+
row.change,
|
|
36900
|
+
row.movedFromPathInRepo
|
|
36901
|
+
);
|
|
36902
|
+
row.totalLines = await resolveCommitFileTotalLines({
|
|
36903
|
+
repoGitCwd: options.repoGitCwd,
|
|
36904
|
+
pathInRepo,
|
|
36905
|
+
change: row.change,
|
|
36906
|
+
commitSha: options.commitSha,
|
|
36907
|
+
parentSha: options.parentSha,
|
|
36908
|
+
isBinary: options.binaryByPath.has(pathInRepo)
|
|
36909
|
+
});
|
|
36910
|
+
});
|
|
36911
|
+
}
|
|
36912
|
+
|
|
36913
|
+
// src/git/changes/parse/parse-name-status-entries.ts
|
|
36914
|
+
function parseNameStatusEntries(lines) {
|
|
36915
|
+
const entries = [];
|
|
36552
36916
|
for (const line of lines) {
|
|
36553
36917
|
if (!line.trim()) continue;
|
|
36554
36918
|
const tabParts = line.split(" ");
|
|
@@ -36556,58 +36920,143 @@ function parseNameStatusLines(lines) {
|
|
|
36556
36920
|
const status = tabParts[0].trim();
|
|
36557
36921
|
const code = status[0];
|
|
36558
36922
|
if (code === "A") {
|
|
36559
|
-
|
|
36923
|
+
entries.push({ pathInRepo: normalizeGitDiffPath(tabParts[tabParts.length - 1]), change: "added" });
|
|
36560
36924
|
} else if (code === "D") {
|
|
36561
|
-
|
|
36925
|
+
entries.push({ pathInRepo: normalizeGitDiffPath(tabParts[tabParts.length - 1]), change: "removed" });
|
|
36562
36926
|
} else if (code === "R" || code === "C") {
|
|
36563
|
-
if (tabParts.length >= 3)
|
|
36927
|
+
if (tabParts.length >= 3) {
|
|
36928
|
+
entries.push({
|
|
36929
|
+
pathInRepo: normalizeGitDiffPath(tabParts[tabParts.length - 1]),
|
|
36930
|
+
oldPathInRepo: normalizeGitDiffPath(tabParts[tabParts.length - 2]),
|
|
36931
|
+
change: "moved"
|
|
36932
|
+
});
|
|
36933
|
+
}
|
|
36564
36934
|
} else if (code === "M" || code === "U" || code === "T") {
|
|
36565
|
-
|
|
36935
|
+
entries.push({ pathInRepo: normalizeGitDiffPath(tabParts[tabParts.length - 1]), change: "modified" });
|
|
36566
36936
|
}
|
|
36567
36937
|
}
|
|
36938
|
+
return entries;
|
|
36939
|
+
}
|
|
36940
|
+
function parseNameStatusLines(lines) {
|
|
36941
|
+
const m = /* @__PURE__ */ new Map();
|
|
36942
|
+
for (const entry of parseNameStatusEntries(lines)) {
|
|
36943
|
+
m.set(entry.pathInRepo, entry.change);
|
|
36944
|
+
}
|
|
36568
36945
|
return m;
|
|
36569
36946
|
}
|
|
36570
|
-
|
|
36947
|
+
|
|
36948
|
+
// src/git/changes/parse/parse-numstat-entries.ts
|
|
36949
|
+
function parseNumstatCounts(parts) {
|
|
36950
|
+
const [a, d] = parts;
|
|
36951
|
+
return {
|
|
36952
|
+
additions: a === "-" ? 0 : parseInt(String(a), 10) || 0,
|
|
36953
|
+
deletions: d === "-" ? 0 : parseInt(String(d), 10) || 0
|
|
36954
|
+
};
|
|
36955
|
+
}
|
|
36956
|
+
function parseNumstatLine(line) {
|
|
36571
36957
|
const parts = line.split(" ");
|
|
36572
36958
|
if (parts.length < 3) return null;
|
|
36573
|
-
const
|
|
36574
|
-
const additions
|
|
36575
|
-
const
|
|
36576
|
-
|
|
36959
|
+
const rawPath = parts[parts.length - 1];
|
|
36960
|
+
const { additions, deletions } = parseNumstatCounts(parts);
|
|
36961
|
+
const expanded = expandGitRenameNumstatPath(rawPath);
|
|
36962
|
+
const pathInRepo = normalizeGitDiffPath(expanded?.newPath ?? rawPath);
|
|
36963
|
+
return {
|
|
36964
|
+
pathInRepo,
|
|
36965
|
+
additions,
|
|
36966
|
+
deletions,
|
|
36967
|
+
...expanded ? { oldPathInRepo: expanded.oldPath } : {}
|
|
36968
|
+
};
|
|
36577
36969
|
}
|
|
36578
|
-
function
|
|
36970
|
+
function parseNumstatEntries(lines) {
|
|
36579
36971
|
const m = /* @__PURE__ */ new Map();
|
|
36580
36972
|
for (const line of lines) {
|
|
36581
36973
|
if (!line.trim()) continue;
|
|
36582
36974
|
const parts = line.split(" ");
|
|
36583
36975
|
if (parts.length < 3) continue;
|
|
36584
|
-
const [
|
|
36585
|
-
const
|
|
36586
|
-
const
|
|
36587
|
-
|
|
36976
|
+
const rawPath = parts[parts.length - 1];
|
|
36977
|
+
const expanded = expandGitRenameNumstatPath(rawPath);
|
|
36978
|
+
const entry = parseNumstatLine(line);
|
|
36979
|
+
if (!entry) continue;
|
|
36980
|
+
const existing = m.get(entry.pathInRepo);
|
|
36981
|
+
if (!existing || expanded) {
|
|
36982
|
+
m.set(entry.pathInRepo, entry);
|
|
36983
|
+
}
|
|
36588
36984
|
}
|
|
36589
36985
|
return m;
|
|
36590
36986
|
}
|
|
36987
|
+
function parseNumstat(lines) {
|
|
36988
|
+
const m = /* @__PURE__ */ new Map();
|
|
36989
|
+
for (const [path48, entry] of parseNumstatEntries(lines)) {
|
|
36990
|
+
m.set(path48, { additions: entry.additions, deletions: entry.deletions });
|
|
36991
|
+
}
|
|
36992
|
+
return m;
|
|
36993
|
+
}
|
|
36994
|
+
|
|
36995
|
+
// src/git/changes/parse/numstat-from-git-no-index.ts
|
|
36591
36996
|
async function numstatFromGitNoIndex(g, pathInRepo) {
|
|
36592
36997
|
const devNull = process.platform === "win32" ? "NUL" : "/dev/null";
|
|
36593
36998
|
try {
|
|
36594
36999
|
const out = await g.raw(["diff", "--numstat", "--no-index", "--", devNull, pathInRepo]);
|
|
36595
37000
|
const first2 = String(out).split("\n").find((l) => l.trim()) ?? "";
|
|
36596
|
-
|
|
37001
|
+
const parsed = parseNumstatLine(first2);
|
|
37002
|
+
if (!parsed) return null;
|
|
37003
|
+
return { additions: parsed.additions, deletions: parsed.deletions };
|
|
36597
37004
|
} catch {
|
|
36598
37005
|
return null;
|
|
36599
37006
|
}
|
|
36600
37007
|
}
|
|
36601
37008
|
|
|
36602
|
-
// src/git/changes/
|
|
36603
|
-
function
|
|
36604
|
-
|
|
36605
|
-
|
|
37009
|
+
// src/git/changes/rows/collect-moved-source-paths.ts
|
|
37010
|
+
function collectMovedSourcePaths(nameEntries, numByPath) {
|
|
37011
|
+
const sources = /* @__PURE__ */ new Set();
|
|
37012
|
+
for (const entry of nameEntries) {
|
|
37013
|
+
if (entry.change === "moved" && entry.oldPathInRepo) sources.add(entry.oldPathInRepo);
|
|
37014
|
+
}
|
|
37015
|
+
for (const entry of numByPath.values()) {
|
|
37016
|
+
if (entry.oldPathInRepo) sources.add(entry.oldPathInRepo);
|
|
37017
|
+
}
|
|
37018
|
+
return sources;
|
|
37019
|
+
}
|
|
36606
37020
|
|
|
36607
|
-
|
|
37021
|
+
// src/git/changes/numstat/is-binary-numstat-line.ts
|
|
37022
|
+
function isBinaryNumstatLine(line) {
|
|
37023
|
+
const parts = line.split(" ");
|
|
37024
|
+
if (parts.length < 3) return false;
|
|
37025
|
+
return parts[0] === "-" || parts[1] === "-";
|
|
37026
|
+
}
|
|
37027
|
+
|
|
37028
|
+
// src/git/changes/listing/collect-binary-paths-from-numstat.ts
|
|
37029
|
+
function collectBinaryPathsFromNumstat(numstatRaw) {
|
|
37030
|
+
const binaryByPath = /* @__PURE__ */ new Set();
|
|
37031
|
+
for (const line of numstatRaw.split("\n")) {
|
|
37032
|
+
if (!line.trim()) continue;
|
|
37033
|
+
const parts = line.split(" ");
|
|
37034
|
+
if (parts.length < 3) continue;
|
|
37035
|
+
const rawPath = parts[parts.length - 1];
|
|
37036
|
+
const expanded = expandGitRenameNumstatPath(rawPath);
|
|
37037
|
+
if (isBinaryNumstatLine(line)) binaryByPath.add(expanded?.newPath ?? rawPath);
|
|
37038
|
+
}
|
|
37039
|
+
return binaryByPath;
|
|
36608
37040
|
}
|
|
36609
37041
|
|
|
36610
|
-
// src/git/
|
|
37042
|
+
// src/git/changes/listing/parse-diff-output.ts
|
|
37043
|
+
function parseDiffOutput(nameStatusRaw, numstatRaw) {
|
|
37044
|
+
const nameEntries = parseNameStatusEntries(nameStatusRaw.split("\n"));
|
|
37045
|
+
const nameByPath = new Map(nameEntries.map((entry) => [entry.pathInRepo, entry]));
|
|
37046
|
+
const numByPath = parseNumstatEntries(numstatRaw.split("\n"));
|
|
37047
|
+
const movedSourcePaths = collectMovedSourcePaths(nameEntries, numByPath);
|
|
37048
|
+
const binaryByPath = collectBinaryPathsFromNumstat(numstatRaw);
|
|
37049
|
+
return { nameEntries, nameByPath, numByPath, movedSourcePaths, binaryByPath };
|
|
37050
|
+
}
|
|
37051
|
+
function collectChangedPaths(options) {
|
|
37052
|
+
const paths = /* @__PURE__ */ new Set();
|
|
37053
|
+
for (const entry of options.nameEntries) paths.add(entry.pathInRepo);
|
|
37054
|
+
for (const pathInRepo of options.numByPath.keys()) paths.add(pathInRepo);
|
|
37055
|
+
for (const pathInRepo of options.untracked ?? []) paths.add(pathInRepo);
|
|
37056
|
+
return paths;
|
|
37057
|
+
}
|
|
37058
|
+
|
|
37059
|
+
// src/git/changes/listing/parent-for-commit-diff.ts
|
|
36611
37060
|
var EMPTY_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
36612
37061
|
async function parentForCommitDiff(g, sha) {
|
|
36613
37062
|
try {
|
|
@@ -36620,76 +37069,112 @@ async function parentForCommitDiff(g, sha) {
|
|
|
36620
37069
|
}
|
|
36621
37070
|
}
|
|
36622
37071
|
}
|
|
37072
|
+
|
|
37073
|
+
// src/git/changes/listing/rename-diff-args.ts
|
|
37074
|
+
var RENAME_DIFF_ARGS = ["-M", "--find-renames"];
|
|
37075
|
+
|
|
37076
|
+
// src/git/changes/rows/pick-preferred-changed-file-row.ts
|
|
37077
|
+
function rowRank(row) {
|
|
37078
|
+
if (row.change === "moved") return 3;
|
|
37079
|
+
if (row.change === "modified") return 2;
|
|
37080
|
+
if (row.change === "added") return 1;
|
|
37081
|
+
return 0;
|
|
37082
|
+
}
|
|
37083
|
+
function pickPreferredChangedFileRow(a, b) {
|
|
37084
|
+
if (rowRank(a) !== rowRank(b)) return rowRank(a) > rowRank(b) ? a : b;
|
|
37085
|
+
const aHasPatch = Boolean(a.patchContent?.trim());
|
|
37086
|
+
const bHasPatch = Boolean(b.patchContent?.trim());
|
|
37087
|
+
if (aHasPatch !== bHasPatch) return aHasPatch ? a : b;
|
|
37088
|
+
const aHasMoveMeta = Boolean(a.movedFromPathInRepo || a.movedFromPathRelLauncher);
|
|
37089
|
+
const bHasMoveMeta = Boolean(b.movedFromPathInRepo || b.movedFromPathRelLauncher);
|
|
37090
|
+
if (aHasMoveMeta !== bHasMoveMeta) return aHasMoveMeta ? a : b;
|
|
37091
|
+
return a;
|
|
37092
|
+
}
|
|
37093
|
+
|
|
37094
|
+
// src/git/changes/rows/dedupe-changed-file-rows.ts
|
|
37095
|
+
function dedupeChangedFileRows(rows) {
|
|
37096
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
37097
|
+
for (const row of rows) {
|
|
37098
|
+
const key = normalizeGitDiffPath(row.pathRelLauncher);
|
|
37099
|
+
const normalizedRow = key === row.pathRelLauncher ? row : { ...row, pathRelLauncher: key };
|
|
37100
|
+
const existing = byPath.get(key);
|
|
37101
|
+
if (!existing) {
|
|
37102
|
+
byPath.set(key, normalizedRow);
|
|
37103
|
+
continue;
|
|
37104
|
+
}
|
|
37105
|
+
const preferRow = pickPreferredChangedFileRow(existing, normalizedRow);
|
|
37106
|
+
byPath.set(key, { ...preferRow, pathRelLauncher: key });
|
|
37107
|
+
}
|
|
37108
|
+
return [...byPath.values()];
|
|
37109
|
+
}
|
|
37110
|
+
|
|
37111
|
+
// src/git/changes/listing/list-changed-files-for-commit.ts
|
|
36623
37112
|
async function listChangedFilesForCommit(repoGitCwd, repoRelPath, commitSha) {
|
|
36624
37113
|
const g = cliSimpleGit(repoGitCwd);
|
|
36625
37114
|
const parent = await parentForCommitDiff(g, commitSha);
|
|
36626
37115
|
const range = `${parent}..${commitSha}`;
|
|
36627
37116
|
const [nameStatusRaw, numstatRaw] = await Promise.all([
|
|
36628
|
-
g.raw(["diff", "--name-status", range]).catch(() => ""),
|
|
36629
|
-
g.raw(["diff", "--numstat", range]).catch(() => "")
|
|
37117
|
+
g.raw(["diff", ...RENAME_DIFF_ARGS, "--name-status", range]).catch(() => ""),
|
|
37118
|
+
g.raw(["diff", ...RENAME_DIFF_ARGS, "--numstat", range]).catch(() => "")
|
|
36630
37119
|
]);
|
|
36631
|
-
const
|
|
36632
|
-
const
|
|
36633
|
-
const paths =
|
|
36634
|
-
|
|
36635
|
-
|
|
36636
|
-
await forEachWithGitYield([...paths], async (pathInRepo) => {
|
|
36637
|
-
const relLauncher = posixJoinDirFile(normRel, pathInRepo.replace(/\\/g, "/"));
|
|
36638
|
-
const nums = numByPath.get(pathInRepo);
|
|
36639
|
-
let additions = nums?.additions ?? 0;
|
|
36640
|
-
let deletions = nums?.deletions ?? 0;
|
|
36641
|
-
let change = kindByPath.get(pathInRepo) ?? "modified";
|
|
36642
|
-
if (!kindByPath.has(pathInRepo) && nums) {
|
|
36643
|
-
if (additions > 0 && deletions === 0) change = "added";
|
|
36644
|
-
else if (deletions > 0 && additions === 0) change = "removed";
|
|
36645
|
-
else change = "modified";
|
|
36646
|
-
}
|
|
36647
|
-
rows.push({ pathRelLauncher: relLauncher, additions, deletions, change });
|
|
36648
|
-
});
|
|
36649
|
-
await forEachWithGitYield(rows, async (row) => {
|
|
36650
|
-
let pathInRepo;
|
|
36651
|
-
if (normRel === ".") {
|
|
36652
|
-
pathInRepo = row.pathRelLauncher;
|
|
36653
|
-
} else if (row.pathRelLauncher.startsWith(`${normRel}/`)) {
|
|
36654
|
-
pathInRepo = row.pathRelLauncher.slice(normRel.length + 1);
|
|
36655
|
-
} else {
|
|
36656
|
-
pathInRepo = row.pathRelLauncher;
|
|
36657
|
-
}
|
|
36658
|
-
const raw = await g.raw(["diff", "-U20000", range, "--", pathInRepo]).catch(() => "");
|
|
36659
|
-
const t = String(raw).trim();
|
|
36660
|
-
row.patchContent = t ? truncatePatch(t) : void 0;
|
|
37120
|
+
const parsed = parseDiffOutput(String(nameStatusRaw), String(numstatRaw));
|
|
37121
|
+
const normRel = normalizeRepoRelPath(repoRelPath);
|
|
37122
|
+
const paths = collectChangedPaths({
|
|
37123
|
+
nameEntries: parsed.nameEntries,
|
|
37124
|
+
numByPath: parsed.numByPath
|
|
36661
37125
|
});
|
|
36662
|
-
rows
|
|
36663
|
-
|
|
36664
|
-
|
|
36665
|
-
|
|
36666
|
-
|
|
37126
|
+
let rows = await buildChangedFileRowsFromPaths({
|
|
37127
|
+
paths,
|
|
37128
|
+
repoRelPath: normRel,
|
|
37129
|
+
nameByPath: parsed.nameByPath,
|
|
37130
|
+
numByPath: parsed.numByPath,
|
|
37131
|
+
movedSourcePaths: parsed.movedSourcePaths
|
|
37132
|
+
});
|
|
37133
|
+
rows = dedupeChangedFileRows(rows);
|
|
37134
|
+
await enrichCommitFileRows({
|
|
37135
|
+
rows,
|
|
37136
|
+
repoGitCwd,
|
|
37137
|
+
repoRelPath: normRel,
|
|
37138
|
+
range,
|
|
37139
|
+
commitSha,
|
|
37140
|
+
parentSha: parent,
|
|
37141
|
+
binaryByPath: parsed.binaryByPath
|
|
37142
|
+
});
|
|
37143
|
+
const finalRows = dedupeChangedFileRows(rows);
|
|
37144
|
+
finalRows.sort((a, b) => a.pathRelLauncher.localeCompare(b.pathRelLauncher));
|
|
37145
|
+
return finalRows;
|
|
37146
|
+
}
|
|
37147
|
+
|
|
37148
|
+
// src/git/changes/listing/apply-untracked-file-stats.ts
|
|
36667
37149
|
import * as fs18 from "node:fs";
|
|
36668
|
-
import * as
|
|
36669
|
-
|
|
36670
|
-
|
|
36671
|
-
|
|
36672
|
-
|
|
36673
|
-
|
|
36674
|
-
|
|
36675
|
-
|
|
36676
|
-
|
|
36677
|
-
|
|
36678
|
-
|
|
36679
|
-
|
|
36680
|
-
lines += 1;
|
|
36681
|
-
bytes += Buffer.byteLength(String(_line), "utf8") + 1;
|
|
36682
|
-
if (bytes > maxBytes) {
|
|
36683
|
-
rl.close();
|
|
36684
|
-
stream.destroy();
|
|
36685
|
-
return lines;
|
|
37150
|
+
import * as path21 from "node:path";
|
|
37151
|
+
function createUntrackedStatsApplier(options) {
|
|
37152
|
+
return async (row, pathInRepo) => {
|
|
37153
|
+
if (!options.untrackedSet.has(pathInRepo)) return;
|
|
37154
|
+
if (options.nameByPath.has(pathInRepo)) return;
|
|
37155
|
+
if (row.change === "moved") return;
|
|
37156
|
+
const repoFilePath = path21.join(options.repoGitCwd, pathInRepo);
|
|
37157
|
+
const fromGit = await numstatFromGitNoIndex(options.g, pathInRepo);
|
|
37158
|
+
if (fromGit) {
|
|
37159
|
+
row.additions = fromGit.additions;
|
|
37160
|
+
row.deletions = fromGit.deletions;
|
|
37161
|
+
return;
|
|
36686
37162
|
}
|
|
36687
|
-
|
|
36688
|
-
|
|
37163
|
+
try {
|
|
37164
|
+
const st = await fs18.promises.stat(repoFilePath);
|
|
37165
|
+
row.additions = st.isFile() ? await countTextFileLines(repoFilePath) : 0;
|
|
37166
|
+
} catch {
|
|
37167
|
+
row.additions = 0;
|
|
37168
|
+
}
|
|
37169
|
+
row.deletions = 0;
|
|
37170
|
+
};
|
|
36689
37171
|
}
|
|
36690
37172
|
|
|
36691
|
-
// src/git/changes/
|
|
36692
|
-
import * as
|
|
37173
|
+
// src/git/changes/listing/enrich-working-tree-file-rows.ts
|
|
37174
|
+
import * as path22 from "node:path";
|
|
37175
|
+
|
|
37176
|
+
// src/git/changes/patch/hydrate-patch.ts
|
|
37177
|
+
import * as fs19 from "node:fs";
|
|
36693
37178
|
var UNIFIED_HUNK_HEADER_RE = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
|
|
36694
37179
|
var MAX_HYDRATE_LINES_PER_GAP = 8e3;
|
|
36695
37180
|
var MAX_HYDRATE_LINES_PER_FILE = 8e4;
|
|
@@ -36704,7 +37189,7 @@ async function readGitBlobLines(repoCwd, pathInRepo) {
|
|
|
36704
37189
|
}
|
|
36705
37190
|
async function readWorktreeFileLines(filePath) {
|
|
36706
37191
|
try {
|
|
36707
|
-
const raw = await
|
|
37192
|
+
const raw = await fs19.promises.readFile(filePath, "utf8");
|
|
36708
37193
|
return raw.split(/\r?\n/);
|
|
36709
37194
|
} catch {
|
|
36710
37195
|
return null;
|
|
@@ -36804,82 +37289,70 @@ async function hydrateUnifiedPatchWithFileContext(patch, filePath, repoGitCwd, p
|
|
|
36804
37289
|
return truncatePatch(out.join("\n"));
|
|
36805
37290
|
}
|
|
36806
37291
|
|
|
36807
|
-
// src/git/changes/
|
|
36808
|
-
function
|
|
36809
|
-
|
|
36810
|
-
|
|
36811
|
-
|
|
36812
|
-
|
|
36813
|
-
|
|
36814
|
-
|
|
36815
|
-
|
|
36816
|
-
|
|
36817
|
-
|
|
37292
|
+
// src/git/changes/listing/enrich-working-tree-file-rows.ts
|
|
37293
|
+
async function enrichWorkingTreeFileRows(options) {
|
|
37294
|
+
await forEachWithGitYield(options.rows, async (row) => {
|
|
37295
|
+
const pathInRepo = pathInRepoFromLauncher(row.pathRelLauncher, options.repoRelPath);
|
|
37296
|
+
const filePath = path22.join(options.repoGitCwd, pathInRepo);
|
|
37297
|
+
const hydrateKind = row.change === "moved" ? "modified" : row.change;
|
|
37298
|
+
let patch = await unifiedDiffForFile(options.repoGitCwd, pathInRepo, row.change, row.movedFromPathInRepo);
|
|
37299
|
+
if (patch) {
|
|
37300
|
+
patch = await hydrateUnifiedPatchWithFileContext(
|
|
37301
|
+
patch,
|
|
37302
|
+
filePath,
|
|
37303
|
+
options.repoGitCwd,
|
|
37304
|
+
pathInRepo,
|
|
37305
|
+
hydrateKind
|
|
37306
|
+
);
|
|
37307
|
+
}
|
|
37308
|
+
row.patchContent = patch;
|
|
37309
|
+
row.totalLines = await resolveWorkingTreeFileTotalLines({
|
|
37310
|
+
repoGitCwd: options.repoGitCwd,
|
|
37311
|
+
pathInRepo,
|
|
37312
|
+
change: row.change,
|
|
37313
|
+
isBinary: options.binaryByPath.has(pathInRepo)
|
|
37314
|
+
});
|
|
37315
|
+
});
|
|
36818
37316
|
}
|
|
36819
37317
|
|
|
36820
|
-
// src/git/changes/list-changed-files-for-repo.ts
|
|
37318
|
+
// src/git/changes/listing/list-changed-files-for-repo.ts
|
|
36821
37319
|
async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
36822
37320
|
const g = cliSimpleGit(repoGitCwd);
|
|
36823
37321
|
const [nameStatusRaw, numstatRaw, untrackedRaw] = await Promise.all([
|
|
36824
|
-
g.raw(["diff", "--name-status", "HEAD"]).catch(() => ""),
|
|
36825
|
-
g.raw(["diff", "HEAD", "--numstat"]).catch(() => ""),
|
|
37322
|
+
g.raw(["diff", ...RENAME_DIFF_ARGS, "--name-status", "HEAD"]).catch(() => ""),
|
|
37323
|
+
g.raw(["diff", ...RENAME_DIFF_ARGS, "HEAD", "--numstat"]).catch(() => ""),
|
|
36826
37324
|
g.raw(["ls-files", "--others", "--exclude-standard"]).catch(() => "")
|
|
36827
37325
|
]);
|
|
36828
|
-
const
|
|
36829
|
-
const numByPath = parseNumstat(String(numstatRaw).split("\n"));
|
|
36830
|
-
const paths = /* @__PURE__ */ new Set([...kindByPath.keys(), ...numByPath.keys()]);
|
|
37326
|
+
const parsed = parseDiffOutput(String(nameStatusRaw), String(numstatRaw));
|
|
36831
37327
|
const untracked = String(untrackedRaw).split("\n").map((s) => s.trim()).filter(Boolean);
|
|
36832
|
-
|
|
36833
|
-
const
|
|
36834
|
-
|
|
36835
|
-
|
|
36836
|
-
|
|
36837
|
-
const nums = numByPath.get(pathInRepo);
|
|
36838
|
-
let additions = nums?.additions ?? 0;
|
|
36839
|
-
let deletions = nums?.deletions ?? 0;
|
|
36840
|
-
let change = kindByPath.get(pathInRepo) ?? "modified";
|
|
36841
|
-
if (untracked.includes(pathInRepo) && !kindByPath.has(pathInRepo)) {
|
|
36842
|
-
change = "added";
|
|
36843
|
-
const fromGit = await numstatFromGitNoIndex(g, pathInRepo);
|
|
36844
|
-
if (fromGit) {
|
|
36845
|
-
additions = fromGit.additions;
|
|
36846
|
-
deletions = fromGit.deletions;
|
|
36847
|
-
} else {
|
|
36848
|
-
try {
|
|
36849
|
-
const st = await fs18.promises.stat(repoFilePath);
|
|
36850
|
-
if (st.isFile()) additions = await countTextFileLines(repoFilePath);
|
|
36851
|
-
else additions = 0;
|
|
36852
|
-
} catch {
|
|
36853
|
-
additions = 0;
|
|
36854
|
-
}
|
|
36855
|
-
deletions = 0;
|
|
36856
|
-
}
|
|
36857
|
-
}
|
|
36858
|
-
if (!kindByPath.has(pathInRepo) && nums) {
|
|
36859
|
-
if (additions > 0 && deletions === 0) change = "added";
|
|
36860
|
-
else if (deletions > 0 && additions === 0) change = "removed";
|
|
36861
|
-
else change = "modified";
|
|
36862
|
-
}
|
|
36863
|
-
rows.push({ pathRelLauncher: relLauncher, additions, deletions, change });
|
|
37328
|
+
const untrackedSet = new Set(untracked);
|
|
37329
|
+
const paths = collectChangedPaths({
|
|
37330
|
+
nameEntries: parsed.nameEntries,
|
|
37331
|
+
numByPath: parsed.numByPath,
|
|
37332
|
+
untracked
|
|
36864
37333
|
});
|
|
36865
|
-
|
|
36866
|
-
|
|
36867
|
-
|
|
36868
|
-
|
|
36869
|
-
|
|
36870
|
-
|
|
36871
|
-
|
|
36872
|
-
|
|
36873
|
-
|
|
36874
|
-
|
|
36875
|
-
|
|
36876
|
-
|
|
36877
|
-
|
|
36878
|
-
patch = await hydrateUnifiedPatchWithFileContext(patch, filePath, repoGitCwd, pathInRepo, row.change);
|
|
36879
|
-
}
|
|
36880
|
-
row.patchContent = patch;
|
|
37334
|
+
let rows = await buildChangedFileRowsFromPaths({
|
|
37335
|
+
paths,
|
|
37336
|
+
repoRelPath,
|
|
37337
|
+
nameByPath: parsed.nameByPath,
|
|
37338
|
+
numByPath: parsed.numByPath,
|
|
37339
|
+
movedSourcePaths: parsed.movedSourcePaths,
|
|
37340
|
+
untrackedSet,
|
|
37341
|
+
applyUntrackedStats: createUntrackedStatsApplier({
|
|
37342
|
+
g,
|
|
37343
|
+
repoGitCwd,
|
|
37344
|
+
untrackedSet,
|
|
37345
|
+
nameByPath: parsed.nameByPath
|
|
37346
|
+
})
|
|
36881
37347
|
});
|
|
36882
|
-
|
|
37348
|
+
rows = dedupeChangedFileRows(rows);
|
|
37349
|
+
await enrichWorkingTreeFileRows({
|
|
37350
|
+
rows,
|
|
37351
|
+
repoGitCwd,
|
|
37352
|
+
repoRelPath,
|
|
37353
|
+
binaryByPath: parsed.binaryByPath
|
|
37354
|
+
});
|
|
37355
|
+
return dedupeChangedFileRows(rows);
|
|
36883
37356
|
}
|
|
36884
37357
|
|
|
36885
37358
|
// src/git/changes/get-working-tree-change-repo-details.ts
|
|
@@ -36888,8 +37361,8 @@ function normRepoRel(p) {
|
|
|
36888
37361
|
return x === "" ? "." : x;
|
|
36889
37362
|
}
|
|
36890
37363
|
async function getWorkingTreeChangeRepoDetails(options) {
|
|
36891
|
-
const bridgeRoot =
|
|
36892
|
-
const sessionWtRoot = options.sessionWorktreeRootPath ?
|
|
37364
|
+
const bridgeRoot = path23.resolve(getBridgeRoot());
|
|
37365
|
+
const sessionWtRoot = options.sessionWorktreeRootPath ? path23.resolve(options.sessionWorktreeRootPath) : null;
|
|
36893
37366
|
const legacyNested = options.legacyRepoNestedSessionLayout === true;
|
|
36894
37367
|
const out = [];
|
|
36895
37368
|
const filter = options.repoFilterRelPath != null ? normRepoRel(options.repoFilterRelPath) : null;
|
|
@@ -36904,7 +37377,7 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
36904
37377
|
for (let i = 0; i < options.commitTargetPaths.length; i++) {
|
|
36905
37378
|
if (i > 0) await yieldToEventLoop2();
|
|
36906
37379
|
const target = options.commitTargetPaths[i];
|
|
36907
|
-
const t =
|
|
37380
|
+
const t = path23.resolve(target);
|
|
36908
37381
|
if (!await isGitRepoDirectory(t)) continue;
|
|
36909
37382
|
const g = cliSimpleGit(t);
|
|
36910
37383
|
let branch = "HEAD";
|
|
@@ -36917,8 +37390,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
36917
37390
|
const remoteDisplay = formatRemoteDisplayLabel(remoteUrl);
|
|
36918
37391
|
let repoRelPath;
|
|
36919
37392
|
if (sessionWtRoot) {
|
|
36920
|
-
const anchor = legacyNested ?
|
|
36921
|
-
const relNorm =
|
|
37393
|
+
const anchor = legacyNested ? path23.dirname(t) : t;
|
|
37394
|
+
const relNorm = path23.relative(sessionWtRoot, anchor);
|
|
36922
37395
|
repoRelPath = relNorm === "" ? "." : relNorm.replace(/\\/g, "/");
|
|
36923
37396
|
} else {
|
|
36924
37397
|
let top = t;
|
|
@@ -36927,8 +37400,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
36927
37400
|
} catch {
|
|
36928
37401
|
top = t;
|
|
36929
37402
|
}
|
|
36930
|
-
const rel =
|
|
36931
|
-
repoRelPath = rel.startsWith("..") ?
|
|
37403
|
+
const rel = path23.relative(bridgeRoot, path23.resolve(top)).replace(/\\/g, "/") || ".";
|
|
37404
|
+
repoRelPath = rel.startsWith("..") ? path23.basename(path23.resolve(top)) : rel;
|
|
36932
37405
|
}
|
|
36933
37406
|
const norm = normRepoRel(repoRelPath === "" ? "." : repoRelPath);
|
|
36934
37407
|
if (filter && norm !== filter) continue;
|
|
@@ -36960,7 +37433,7 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
36960
37433
|
return out;
|
|
36961
37434
|
}
|
|
36962
37435
|
|
|
36963
|
-
// src/git/changes/
|
|
37436
|
+
// src/git/changes/status/working-tree-changed-path-count.ts
|
|
36964
37437
|
function workingTreeChangedPathCount(nameStatusLines, numstatLines, untrackedLines) {
|
|
36965
37438
|
const kindByPath = parseNameStatusLines(nameStatusLines);
|
|
36966
37439
|
const numByPath = parseNumstat(numstatLines);
|
|
@@ -37056,23 +37529,23 @@ async function commitSessionWorktrees(options) {
|
|
|
37056
37529
|
}
|
|
37057
37530
|
|
|
37058
37531
|
// src/worktrees/remove-session-worktrees.ts
|
|
37059
|
-
import * as
|
|
37532
|
+
import * as fs22 from "node:fs";
|
|
37060
37533
|
|
|
37061
37534
|
// src/git/worktrees/worktree-remove.ts
|
|
37062
|
-
import * as
|
|
37535
|
+
import * as fs21 from "node:fs";
|
|
37063
37536
|
|
|
37064
37537
|
// src/git/worktrees/resolve-main-repo-from-git-file.ts
|
|
37065
|
-
import * as
|
|
37066
|
-
import * as
|
|
37538
|
+
import * as fs20 from "node:fs";
|
|
37539
|
+
import * as path24 from "node:path";
|
|
37067
37540
|
function resolveMainRepoFromWorktreeGitFile(wt) {
|
|
37068
|
-
const gitDirFile =
|
|
37069
|
-
if (!
|
|
37070
|
-
const first2 =
|
|
37541
|
+
const gitDirFile = path24.join(wt, ".git");
|
|
37542
|
+
if (!fs20.existsSync(gitDirFile) || !fs20.statSync(gitDirFile).isFile()) return "";
|
|
37543
|
+
const first2 = fs20.readFileSync(gitDirFile, "utf8").trim();
|
|
37071
37544
|
const m = first2.match(/^gitdir:\s*(.+)$/im);
|
|
37072
37545
|
if (!m) return "";
|
|
37073
|
-
const gitWorktreePath =
|
|
37074
|
-
const gitDir =
|
|
37075
|
-
return
|
|
37546
|
+
const gitWorktreePath = path24.resolve(wt, m[1].trim());
|
|
37547
|
+
const gitDir = path24.dirname(path24.dirname(gitWorktreePath));
|
|
37548
|
+
return path24.dirname(gitDir);
|
|
37076
37549
|
}
|
|
37077
37550
|
|
|
37078
37551
|
// src/git/worktrees/worktree-remove.ts
|
|
@@ -37081,7 +37554,7 @@ async function gitWorktreeRemoveForce(worktreePath) {
|
|
|
37081
37554
|
if (mainRepo) {
|
|
37082
37555
|
await cliSimpleGit(mainRepo).raw(["worktree", "remove", "--force", worktreePath]);
|
|
37083
37556
|
} else {
|
|
37084
|
-
|
|
37557
|
+
fs21.rmSync(worktreePath, { recursive: true, force: true });
|
|
37085
37558
|
}
|
|
37086
37559
|
}
|
|
37087
37560
|
|
|
@@ -37094,7 +37567,7 @@ async function removeSessionWorktrees(paths, log2) {
|
|
|
37094
37567
|
} catch (e) {
|
|
37095
37568
|
log2(`[worktrees] Remove failed for ${wt}: ${e instanceof Error ? e.message : String(e)}`);
|
|
37096
37569
|
try {
|
|
37097
|
-
|
|
37570
|
+
fs22.rmSync(wt, { recursive: true, force: true });
|
|
37098
37571
|
} catch {
|
|
37099
37572
|
}
|
|
37100
37573
|
}
|
|
@@ -37123,12 +37596,12 @@ async function renameSessionWorktreeBranches(paths, newBranch, log2) {
|
|
|
37123
37596
|
}
|
|
37124
37597
|
|
|
37125
37598
|
// src/worktrees/worktree-layout-file.ts
|
|
37126
|
-
import * as
|
|
37127
|
-
import * as
|
|
37599
|
+
import * as fs23 from "node:fs";
|
|
37600
|
+
import * as path25 from "node:path";
|
|
37128
37601
|
import os7 from "node:os";
|
|
37129
37602
|
var LAYOUT_FILENAME = "worktree-launcher-layout.json";
|
|
37130
37603
|
function defaultWorktreeLayoutPath() {
|
|
37131
|
-
return
|
|
37604
|
+
return path25.join(os7.homedir(), ".buildautomaton", LAYOUT_FILENAME);
|
|
37132
37605
|
}
|
|
37133
37606
|
function normalizeLoadedLayout(raw) {
|
|
37134
37607
|
if (raw && typeof raw === "object" && "launcherCwds" in raw) {
|
|
@@ -37140,8 +37613,8 @@ function normalizeLoadedLayout(raw) {
|
|
|
37140
37613
|
function loadWorktreeLayout() {
|
|
37141
37614
|
try {
|
|
37142
37615
|
const p = defaultWorktreeLayoutPath();
|
|
37143
|
-
if (!
|
|
37144
|
-
const raw = JSON.parse(
|
|
37616
|
+
if (!fs23.existsSync(p)) return { launcherCwds: [] };
|
|
37617
|
+
const raw = JSON.parse(fs23.readFileSync(p, "utf8"));
|
|
37145
37618
|
return normalizeLoadedLayout(raw);
|
|
37146
37619
|
} catch {
|
|
37147
37620
|
return { launcherCwds: [] };
|
|
@@ -37149,24 +37622,24 @@ function loadWorktreeLayout() {
|
|
|
37149
37622
|
}
|
|
37150
37623
|
function saveWorktreeLayout(layout) {
|
|
37151
37624
|
try {
|
|
37152
|
-
const dir =
|
|
37153
|
-
|
|
37154
|
-
|
|
37625
|
+
const dir = path25.dirname(defaultWorktreeLayoutPath());
|
|
37626
|
+
fs23.mkdirSync(dir, { recursive: true });
|
|
37627
|
+
fs23.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
|
|
37155
37628
|
} catch {
|
|
37156
37629
|
}
|
|
37157
37630
|
}
|
|
37158
37631
|
function baseNameSafe(pathString) {
|
|
37159
|
-
return
|
|
37632
|
+
return path25.basename(pathString).replace(/[^a-zA-Z0-9._-]+/g, "-") || "cwd";
|
|
37160
37633
|
}
|
|
37161
37634
|
function getLauncherDirNameIfPresent(layout, bridgeRootPath2) {
|
|
37162
|
-
const norm =
|
|
37163
|
-
const existing = layout.launcherCwds.find((e) =>
|
|
37635
|
+
const norm = path25.resolve(bridgeRootPath2);
|
|
37636
|
+
const existing = layout.launcherCwds.find((e) => path25.resolve(e.absolutePath) === norm);
|
|
37164
37637
|
return existing?.dirName;
|
|
37165
37638
|
}
|
|
37166
37639
|
function allocateDirNameForLauncherCwd(layout, bridgeRootPath2) {
|
|
37167
37640
|
const existing = getLauncherDirNameIfPresent(layout, bridgeRootPath2);
|
|
37168
37641
|
if (existing) return existing;
|
|
37169
|
-
const norm =
|
|
37642
|
+
const norm = path25.resolve(bridgeRootPath2);
|
|
37170
37643
|
const base = baseNameSafe(norm);
|
|
37171
37644
|
const used = new Set(layout.launcherCwds.map((e) => e.dirName));
|
|
37172
37645
|
let name = base;
|
|
@@ -37181,11 +37654,11 @@ function allocateDirNameForLauncherCwd(layout, bridgeRootPath2) {
|
|
|
37181
37654
|
}
|
|
37182
37655
|
|
|
37183
37656
|
// src/worktrees/discover-session-worktree-on-disk.ts
|
|
37184
|
-
import * as
|
|
37185
|
-
import * as
|
|
37657
|
+
import * as fs24 from "node:fs";
|
|
37658
|
+
import * as path26 from "node:path";
|
|
37186
37659
|
function isGitDir(dirPath) {
|
|
37187
37660
|
try {
|
|
37188
|
-
return
|
|
37661
|
+
return fs24.existsSync(path26.join(dirPath, ".git"));
|
|
37189
37662
|
} catch {
|
|
37190
37663
|
return false;
|
|
37191
37664
|
}
|
|
@@ -37194,23 +37667,23 @@ function collectGitRepoRootsUnderDirectory(rootPath) {
|
|
|
37194
37667
|
const out = [];
|
|
37195
37668
|
const walk = (dir) => {
|
|
37196
37669
|
if (isGitDir(dir)) {
|
|
37197
|
-
out.push(
|
|
37670
|
+
out.push(path26.resolve(dir));
|
|
37198
37671
|
return;
|
|
37199
37672
|
}
|
|
37200
37673
|
let entries;
|
|
37201
37674
|
try {
|
|
37202
|
-
entries =
|
|
37675
|
+
entries = fs24.readdirSync(dir, { withFileTypes: true });
|
|
37203
37676
|
} catch {
|
|
37204
37677
|
return;
|
|
37205
37678
|
}
|
|
37206
37679
|
for (const e of entries) {
|
|
37207
37680
|
if (e.name.startsWith(".")) continue;
|
|
37208
|
-
const full =
|
|
37681
|
+
const full = path26.join(dir, e.name);
|
|
37209
37682
|
if (!e.isDirectory()) continue;
|
|
37210
37683
|
walk(full);
|
|
37211
37684
|
}
|
|
37212
37685
|
};
|
|
37213
|
-
walk(
|
|
37686
|
+
walk(path26.resolve(rootPath));
|
|
37214
37687
|
return [...new Set(out)];
|
|
37215
37688
|
}
|
|
37216
37689
|
function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
|
|
@@ -37219,16 +37692,16 @@ function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
|
|
|
37219
37692
|
if (depth > maxDepth) return;
|
|
37220
37693
|
let entries;
|
|
37221
37694
|
try {
|
|
37222
|
-
entries =
|
|
37695
|
+
entries = fs24.readdirSync(dir, { withFileTypes: true });
|
|
37223
37696
|
} catch {
|
|
37224
37697
|
return;
|
|
37225
37698
|
}
|
|
37226
37699
|
for (const e of entries) {
|
|
37227
37700
|
if (e.name.startsWith(".")) continue;
|
|
37228
|
-
const full =
|
|
37701
|
+
const full = path26.join(dir, e.name);
|
|
37229
37702
|
if (!e.isDirectory()) continue;
|
|
37230
37703
|
if (e.name === sessionId) {
|
|
37231
|
-
if (isGitDir(full)) out.push(
|
|
37704
|
+
if (isGitDir(full)) out.push(path26.resolve(full));
|
|
37232
37705
|
} else {
|
|
37233
37706
|
walk(full, depth + 1);
|
|
37234
37707
|
}
|
|
@@ -37240,14 +37713,14 @@ function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
|
|
|
37240
37713
|
function tryBindingFromSessionDirectory(sessionDir) {
|
|
37241
37714
|
let st;
|
|
37242
37715
|
try {
|
|
37243
|
-
st =
|
|
37716
|
+
st = fs24.statSync(sessionDir);
|
|
37244
37717
|
} catch {
|
|
37245
37718
|
return null;
|
|
37246
37719
|
}
|
|
37247
37720
|
if (!st.isDirectory()) return null;
|
|
37248
37721
|
const worktreePaths = collectGitRepoRootsUnderDirectory(sessionDir);
|
|
37249
37722
|
if (worktreePaths.length === 0) return null;
|
|
37250
|
-
const abs =
|
|
37723
|
+
const abs = path26.resolve(sessionDir);
|
|
37251
37724
|
return {
|
|
37252
37725
|
sessionParentPath: abs,
|
|
37253
37726
|
workingTreeRelRoot: abs,
|
|
@@ -37257,20 +37730,20 @@ function tryBindingFromSessionDirectory(sessionDir) {
|
|
|
37257
37730
|
function discoverLegacyBindingAscendingFromCheckout(sessionId, checkoutPath) {
|
|
37258
37731
|
const sid = sessionId.trim();
|
|
37259
37732
|
if (!sid) return null;
|
|
37260
|
-
const hintR =
|
|
37733
|
+
const hintR = path26.resolve(checkoutPath);
|
|
37261
37734
|
let best = null;
|
|
37262
|
-
let cur =
|
|
37735
|
+
let cur = path26.dirname(hintR);
|
|
37263
37736
|
for (let i = 0; i < 40; i++) {
|
|
37264
37737
|
const paths = collectWorktreeRootsNamed(cur, sid, 24);
|
|
37265
|
-
if (paths.some((p) =>
|
|
37266
|
-
const isolated = resolveIsolatedSessionParentPathFromCheckouts(paths) ??
|
|
37738
|
+
if (paths.some((p) => path26.resolve(p) === hintR)) {
|
|
37739
|
+
const isolated = resolveIsolatedSessionParentPathFromCheckouts(paths) ?? path26.resolve(paths[0]);
|
|
37267
37740
|
best = {
|
|
37268
|
-
sessionParentPath:
|
|
37269
|
-
workingTreeRelRoot:
|
|
37270
|
-
repoCheckoutPaths: paths.map((p) =>
|
|
37741
|
+
sessionParentPath: path26.resolve(isolated),
|
|
37742
|
+
workingTreeRelRoot: path26.resolve(cur),
|
|
37743
|
+
repoCheckoutPaths: paths.map((p) => path26.resolve(p))
|
|
37271
37744
|
};
|
|
37272
37745
|
}
|
|
37273
|
-
const next =
|
|
37746
|
+
const next = path26.dirname(cur);
|
|
37274
37747
|
if (next === cur) break;
|
|
37275
37748
|
cur = next;
|
|
37276
37749
|
}
|
|
@@ -37278,33 +37751,33 @@ function discoverLegacyBindingAscendingFromCheckout(sessionId, checkoutPath) {
|
|
|
37278
37751
|
}
|
|
37279
37752
|
function discoverSessionWorktreeOnDisk(options) {
|
|
37280
37753
|
const { sessionId, worktreesRootPath, layout, bridgeRoot } = options;
|
|
37281
|
-
if (!sessionId.trim() || !
|
|
37754
|
+
if (!sessionId.trim() || !fs24.existsSync(worktreesRootPath)) return null;
|
|
37282
37755
|
const preferredKey = getLauncherDirNameIfPresent(layout, bridgeRoot);
|
|
37283
37756
|
const keys = [];
|
|
37284
37757
|
if (preferredKey) keys.push(preferredKey);
|
|
37285
37758
|
try {
|
|
37286
|
-
for (const name of
|
|
37759
|
+
for (const name of fs24.readdirSync(worktreesRootPath)) {
|
|
37287
37760
|
if (name.startsWith(".")) continue;
|
|
37288
|
-
const p =
|
|
37289
|
-
if (!
|
|
37761
|
+
const p = path26.join(worktreesRootPath, name);
|
|
37762
|
+
if (!fs24.statSync(p).isDirectory()) continue;
|
|
37290
37763
|
if (name !== preferredKey) keys.push(name);
|
|
37291
37764
|
}
|
|
37292
37765
|
} catch {
|
|
37293
37766
|
return null;
|
|
37294
37767
|
}
|
|
37295
37768
|
for (const key of keys) {
|
|
37296
|
-
const layoutRoot =
|
|
37297
|
-
if (!
|
|
37298
|
-
const sessionDir =
|
|
37769
|
+
const layoutRoot = path26.join(worktreesRootPath, key);
|
|
37770
|
+
if (!fs24.existsSync(layoutRoot) || !fs24.statSync(layoutRoot).isDirectory()) continue;
|
|
37771
|
+
const sessionDir = path26.join(layoutRoot, sessionId);
|
|
37299
37772
|
const nested = tryBindingFromSessionDirectory(sessionDir);
|
|
37300
37773
|
if (nested) return nested;
|
|
37301
37774
|
const legacyPaths = collectWorktreeRootsNamed(layoutRoot, sessionId, 24);
|
|
37302
37775
|
if (legacyPaths.length > 0) {
|
|
37303
|
-
const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ??
|
|
37776
|
+
const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path26.resolve(legacyPaths[0]);
|
|
37304
37777
|
return {
|
|
37305
|
-
sessionParentPath:
|
|
37306
|
-
workingTreeRelRoot:
|
|
37307
|
-
repoCheckoutPaths: legacyPaths.map((p) =>
|
|
37778
|
+
sessionParentPath: path26.resolve(isolated),
|
|
37779
|
+
workingTreeRelRoot: path26.resolve(layoutRoot),
|
|
37780
|
+
repoCheckoutPaths: legacyPaths.map((p) => path26.resolve(p))
|
|
37308
37781
|
};
|
|
37309
37782
|
}
|
|
37310
37783
|
}
|
|
@@ -37313,12 +37786,12 @@ function discoverSessionWorktreeOnDisk(options) {
|
|
|
37313
37786
|
function discoverSessionWorktreesUnderSessionWorktreeRoot(sessionWorktreeRootPathOrHint, sessionId) {
|
|
37314
37787
|
const sid = sessionId.trim();
|
|
37315
37788
|
if (!sid) return null;
|
|
37316
|
-
const hint =
|
|
37317
|
-
const underHint = tryBindingFromSessionDirectory(
|
|
37789
|
+
const hint = path26.resolve(sessionWorktreeRootPathOrHint);
|
|
37790
|
+
const underHint = tryBindingFromSessionDirectory(path26.join(hint, sid));
|
|
37318
37791
|
if (underHint) return underHint;
|
|
37319
37792
|
const direct = tryBindingFromSessionDirectory(hint);
|
|
37320
37793
|
if (direct) {
|
|
37321
|
-
if (
|
|
37794
|
+
if (path26.basename(hint) === sid && isGitDir(hint)) {
|
|
37322
37795
|
const legacyFromCheckout = discoverLegacyBindingAscendingFromCheckout(sid, hint);
|
|
37323
37796
|
if (legacyFromCheckout && legacyFromCheckout.repoCheckoutPaths.length > direct.repoCheckoutPaths.length) {
|
|
37324
37797
|
return legacyFromCheckout;
|
|
@@ -37326,24 +37799,24 @@ function discoverSessionWorktreesUnderSessionWorktreeRoot(sessionWorktreeRootPat
|
|
|
37326
37799
|
}
|
|
37327
37800
|
return direct;
|
|
37328
37801
|
}
|
|
37329
|
-
if (
|
|
37802
|
+
if (path26.basename(hint) === sid && isGitDir(hint)) {
|
|
37330
37803
|
const legacyFromCheckout = discoverLegacyBindingAscendingFromCheckout(sid, hint);
|
|
37331
37804
|
if (legacyFromCheckout) return legacyFromCheckout;
|
|
37332
37805
|
}
|
|
37333
37806
|
let st;
|
|
37334
37807
|
try {
|
|
37335
|
-
st =
|
|
37808
|
+
st = fs24.statSync(hint);
|
|
37336
37809
|
} catch {
|
|
37337
37810
|
return null;
|
|
37338
37811
|
}
|
|
37339
37812
|
if (!st.isDirectory()) return null;
|
|
37340
37813
|
const legacyPaths = collectWorktreeRootsNamed(hint, sid, 24);
|
|
37341
37814
|
if (legacyPaths.length === 0) return null;
|
|
37342
|
-
const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ??
|
|
37815
|
+
const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path26.resolve(legacyPaths[0]);
|
|
37343
37816
|
return {
|
|
37344
|
-
sessionParentPath:
|
|
37817
|
+
sessionParentPath: path26.resolve(isolated),
|
|
37345
37818
|
workingTreeRelRoot: hint,
|
|
37346
|
-
repoCheckoutPaths: legacyPaths.map((p) =>
|
|
37819
|
+
repoCheckoutPaths: legacyPaths.map((p) => path26.resolve(p))
|
|
37347
37820
|
};
|
|
37348
37821
|
}
|
|
37349
37822
|
|
|
@@ -37400,8 +37873,8 @@ function parseSessionParent(v) {
|
|
|
37400
37873
|
}
|
|
37401
37874
|
|
|
37402
37875
|
// src/worktrees/prepare-new-session-worktrees.ts
|
|
37403
|
-
import * as
|
|
37404
|
-
import * as
|
|
37876
|
+
import * as fs25 from "node:fs";
|
|
37877
|
+
import * as path27 from "node:path";
|
|
37405
37878
|
|
|
37406
37879
|
// src/git/worktrees/worktree-add.ts
|
|
37407
37880
|
async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch, baseRef = "HEAD") {
|
|
@@ -37411,7 +37884,7 @@ async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch, baseRef
|
|
|
37411
37884
|
}
|
|
37412
37885
|
|
|
37413
37886
|
// src/worktrees/prepare-new-session-worktrees.ts
|
|
37414
|
-
function
|
|
37887
|
+
function normalizeRepoRelPath2(rel) {
|
|
37415
37888
|
return rel === "" ? "." : rel.replace(/\\/g, "/");
|
|
37416
37889
|
}
|
|
37417
37890
|
function resolveBaseRefForRepo(relNorm, baseBranches) {
|
|
@@ -37423,10 +37896,10 @@ function resolveBaseRefForRepo(relNorm, baseBranches) {
|
|
|
37423
37896
|
}
|
|
37424
37897
|
async function prepareNewSessionWorktrees(options) {
|
|
37425
37898
|
const { worktreesRootPath, bridgeRoot, sessionId, layout, log: log2, worktreeBaseBranches } = options;
|
|
37426
|
-
const bridgeResolved =
|
|
37899
|
+
const bridgeResolved = path27.resolve(bridgeRoot);
|
|
37427
37900
|
const cwdKey = allocateDirNameForLauncherCwd(layout, bridgeResolved);
|
|
37428
|
-
const bridgeKeyDir =
|
|
37429
|
-
const sessionDir =
|
|
37901
|
+
const bridgeKeyDir = path27.join(worktreesRootPath, cwdKey);
|
|
37902
|
+
const sessionDir = path27.join(bridgeKeyDir, sessionId);
|
|
37430
37903
|
const repos = await discoverGitReposUnderRoot(bridgeResolved);
|
|
37431
37904
|
if (repos.length === 0) {
|
|
37432
37905
|
log2("[worktrees] No Git repositories under bridge root; skipping worktree creation.");
|
|
@@ -37434,14 +37907,14 @@ async function prepareNewSessionWorktrees(options) {
|
|
|
37434
37907
|
}
|
|
37435
37908
|
const branch = `session-${sessionId}`;
|
|
37436
37909
|
const worktreePaths = [];
|
|
37437
|
-
|
|
37910
|
+
fs25.mkdirSync(sessionDir, { recursive: true });
|
|
37438
37911
|
for (const repo of repos) {
|
|
37439
|
-
let rel =
|
|
37440
|
-
if (rel.startsWith("..") ||
|
|
37441
|
-
const relNorm =
|
|
37442
|
-
const wtPath = relNorm === "." ? sessionDir :
|
|
37912
|
+
let rel = path27.relative(bridgeResolved, repo.absolutePath);
|
|
37913
|
+
if (rel.startsWith("..") || path27.isAbsolute(rel)) continue;
|
|
37914
|
+
const relNorm = normalizeRepoRelPath2(rel === "" ? "." : rel);
|
|
37915
|
+
const wtPath = relNorm === "." ? sessionDir : path27.join(sessionDir, relNorm);
|
|
37443
37916
|
if (relNorm !== ".") {
|
|
37444
|
-
|
|
37917
|
+
fs25.mkdirSync(path27.dirname(wtPath), { recursive: true });
|
|
37445
37918
|
}
|
|
37446
37919
|
const baseRef = resolveBaseRefForRepo(relNorm, worktreeBaseBranches);
|
|
37447
37920
|
try {
|
|
@@ -37494,9 +37967,9 @@ function resolveExistingSessionParentPath(sessionId, cache2, discover) {
|
|
|
37494
37967
|
}
|
|
37495
37968
|
|
|
37496
37969
|
// src/worktrees/manager/resolve-explicit-session-parent-path.ts
|
|
37497
|
-
import * as
|
|
37970
|
+
import * as path28 from "node:path";
|
|
37498
37971
|
function resolveExplicitSessionParentPath(params) {
|
|
37499
|
-
const resolved =
|
|
37972
|
+
const resolved = path28.resolve(params.parentPathRaw);
|
|
37500
37973
|
if (parseSessionParent(params.sessionParent) !== "worktrees_root") {
|
|
37501
37974
|
return resolved;
|
|
37502
37975
|
}
|
|
@@ -37512,7 +37985,7 @@ function resolveExplicitSessionParentPath(params) {
|
|
|
37512
37985
|
for (let i = 0; i < 16; i++) {
|
|
37513
37986
|
const tryRoot = discoverSessionWorktreesUnderSessionWorktreeRoot(cur, params.sessionId);
|
|
37514
37987
|
if (tryRoot) return rememberAndReturn(tryRoot);
|
|
37515
|
-
const next =
|
|
37988
|
+
const next = path28.dirname(cur);
|
|
37516
37989
|
if (next === cur) break;
|
|
37517
37990
|
cur = next;
|
|
37518
37991
|
}
|
|
@@ -37564,16 +38037,16 @@ async function resolveSessionParentPathForPrompt(params) {
|
|
|
37564
38037
|
}
|
|
37565
38038
|
|
|
37566
38039
|
// src/worktrees/manager/session-worktree-cache.ts
|
|
37567
|
-
import * as
|
|
38040
|
+
import * as path29 from "node:path";
|
|
37568
38041
|
var SessionWorktreeCache = class {
|
|
37569
38042
|
sessionRepoCheckoutPaths = /* @__PURE__ */ new Map();
|
|
37570
38043
|
sessionParentPathBySession = /* @__PURE__ */ new Map();
|
|
37571
38044
|
sessionWorkingTreeRelRootBySession = /* @__PURE__ */ new Map();
|
|
37572
38045
|
remember(sessionId, binding) {
|
|
37573
|
-
const paths = binding.repoCheckoutPaths.map((p) =>
|
|
38046
|
+
const paths = binding.repoCheckoutPaths.map((p) => path29.resolve(p));
|
|
37574
38047
|
this.sessionRepoCheckoutPaths.set(sessionId, paths);
|
|
37575
|
-
this.sessionParentPathBySession.set(sessionId,
|
|
37576
|
-
this.sessionWorkingTreeRelRootBySession.set(sessionId,
|
|
38048
|
+
this.sessionParentPathBySession.set(sessionId, path29.resolve(binding.sessionParentPath));
|
|
38049
|
+
this.sessionWorkingTreeRelRootBySession.set(sessionId, path29.resolve(binding.workingTreeRelRoot));
|
|
37577
38050
|
}
|
|
37578
38051
|
clearSession(sessionId) {
|
|
37579
38052
|
const paths = this.sessionRepoCheckoutPaths.get(sessionId);
|
|
@@ -37603,7 +38076,7 @@ var SessionWorktreeCache = class {
|
|
|
37603
38076
|
const parent = this.sessionParentPathBySession.get(sessionId);
|
|
37604
38077
|
const relRoot = this.sessionWorkingTreeRelRootBySession.get(sessionId);
|
|
37605
38078
|
if (!parent || !relRoot) return false;
|
|
37606
|
-
return
|
|
38079
|
+
return path29.resolve(parent) !== path29.resolve(relRoot);
|
|
37607
38080
|
}
|
|
37608
38081
|
};
|
|
37609
38082
|
|
|
@@ -37707,29 +38180,29 @@ var SessionWorktreeManager = class {
|
|
|
37707
38180
|
};
|
|
37708
38181
|
|
|
37709
38182
|
// src/worktrees/manager/default-worktrees-root-path.ts
|
|
37710
|
-
import * as
|
|
38183
|
+
import * as path30 from "node:path";
|
|
37711
38184
|
import os8 from "node:os";
|
|
37712
38185
|
function defaultWorktreesRootPath() {
|
|
37713
|
-
return
|
|
38186
|
+
return path30.join(os8.homedir(), ".buildautomaton", "worktrees");
|
|
37714
38187
|
}
|
|
37715
38188
|
|
|
37716
38189
|
// src/files/watch-file-index.ts
|
|
37717
38190
|
import { watch } from "node:fs";
|
|
37718
|
-
import
|
|
38191
|
+
import path35 from "node:path";
|
|
37719
38192
|
|
|
37720
38193
|
// src/files/index/paths.ts
|
|
37721
|
-
import
|
|
38194
|
+
import path31 from "node:path";
|
|
37722
38195
|
import crypto2 from "node:crypto";
|
|
37723
38196
|
function getCwdHashForFileIndex(resolvedCwd) {
|
|
37724
|
-
return crypto2.createHash("sha256").update(
|
|
38197
|
+
return crypto2.createHash("sha256").update(path31.resolve(resolvedCwd)).digest("hex").slice(0, INDEX_HASH_LEN);
|
|
37725
38198
|
}
|
|
37726
38199
|
|
|
37727
38200
|
// src/files/index/build-file-index.ts
|
|
37728
|
-
import
|
|
38201
|
+
import path33 from "node:path";
|
|
37729
38202
|
|
|
37730
38203
|
// src/files/index/walk-workspace-tree.ts
|
|
37731
|
-
import
|
|
37732
|
-
import
|
|
38204
|
+
import fs26 from "node:fs";
|
|
38205
|
+
import path32 from "node:path";
|
|
37733
38206
|
var DEPENDENCY_INSTALL_DIR_NAMES = /* @__PURE__ */ new Set([
|
|
37734
38207
|
"node_modules",
|
|
37735
38208
|
"bower_components",
|
|
@@ -37746,7 +38219,7 @@ function shouldSkipWorkspaceWalkEntry(name) {
|
|
|
37746
38219
|
async function walkWorkspaceTreeAsync(dir, baseDir, onFile, state) {
|
|
37747
38220
|
let names;
|
|
37748
38221
|
try {
|
|
37749
|
-
names = await
|
|
38222
|
+
names = await fs26.promises.readdir(dir);
|
|
37750
38223
|
} catch {
|
|
37751
38224
|
return;
|
|
37752
38225
|
}
|
|
@@ -37758,14 +38231,14 @@ async function walkWorkspaceTreeAsync(dir, baseDir, onFile, state) {
|
|
|
37758
38231
|
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
37759
38232
|
}
|
|
37760
38233
|
state.n++;
|
|
37761
|
-
const full =
|
|
38234
|
+
const full = path32.join(dir, name);
|
|
37762
38235
|
let stat3;
|
|
37763
38236
|
try {
|
|
37764
|
-
stat3 = await
|
|
38237
|
+
stat3 = await fs26.promises.stat(full);
|
|
37765
38238
|
} catch {
|
|
37766
38239
|
continue;
|
|
37767
38240
|
}
|
|
37768
|
-
const relative6 =
|
|
38241
|
+
const relative6 = path32.relative(baseDir, full).replace(/\\/g, "/");
|
|
37769
38242
|
if (stat3.isDirectory()) {
|
|
37770
38243
|
await walkWorkspaceTreeAsync(full, baseDir, onFile, state);
|
|
37771
38244
|
} else if (stat3.isFile()) {
|
|
@@ -37778,7 +38251,7 @@ function createWalkYieldState() {
|
|
|
37778
38251
|
}
|
|
37779
38252
|
|
|
37780
38253
|
// src/files/index/file-index-sqlite-lock.ts
|
|
37781
|
-
import
|
|
38254
|
+
import fs27 from "node:fs";
|
|
37782
38255
|
function isSqliteCorruptError(e) {
|
|
37783
38256
|
const msg = e instanceof Error ? e.message : String(e);
|
|
37784
38257
|
return msg.includes("malformed") || msg.includes("database disk image is malformed") || msg.includes("corrupt");
|
|
@@ -37792,7 +38265,7 @@ function withFileIndexSqliteLock(fn) {
|
|
|
37792
38265
|
if (!isSqliteCorruptError(e)) throw e;
|
|
37793
38266
|
closeAllCliSqliteConnections();
|
|
37794
38267
|
try {
|
|
37795
|
-
|
|
38268
|
+
fs27.unlinkSync(getCliSqlitePath());
|
|
37796
38269
|
} catch {
|
|
37797
38270
|
}
|
|
37798
38271
|
chain = Promise.resolve();
|
|
@@ -37867,7 +38340,7 @@ async function collectWorkspacePathsAsync(resolved) {
|
|
|
37867
38340
|
}
|
|
37868
38341
|
async function buildFileIndexAsync(cwd) {
|
|
37869
38342
|
return withFileIndexSqliteLock(async () => {
|
|
37870
|
-
const resolved =
|
|
38343
|
+
const resolved = path33.resolve(cwd);
|
|
37871
38344
|
await yieldToEventLoop();
|
|
37872
38345
|
assertNotShutdown();
|
|
37873
38346
|
const paths = await collectWorkspacePathsAsync(resolved);
|
|
@@ -37879,7 +38352,7 @@ async function buildFileIndexAsync(cwd) {
|
|
|
37879
38352
|
}
|
|
37880
38353
|
|
|
37881
38354
|
// src/files/index/ensure-file-index.ts
|
|
37882
|
-
import
|
|
38355
|
+
import path34 from "node:path";
|
|
37883
38356
|
|
|
37884
38357
|
// src/files/index/search-file-index.ts
|
|
37885
38358
|
function escapeLikePattern(fragment) {
|
|
@@ -37931,7 +38404,7 @@ async function searchBridgeFilePathsAsync(resolvedCwd, query, limit = 100) {
|
|
|
37931
38404
|
|
|
37932
38405
|
// src/files/index/ensure-file-index.ts
|
|
37933
38406
|
async function ensureFileIndexAsync(cwd) {
|
|
37934
|
-
const resolved =
|
|
38407
|
+
const resolved = path34.resolve(cwd);
|
|
37935
38408
|
if (await bridgeFileIndexIsPopulated(resolved)) {
|
|
37936
38409
|
return { fromCache: true, pathCount: await bridgeFileIndexPathCount(resolved) };
|
|
37937
38410
|
}
|
|
@@ -37975,7 +38448,7 @@ function createFsWatcher(resolved, schedule) {
|
|
|
37975
38448
|
}
|
|
37976
38449
|
}
|
|
37977
38450
|
function startFileIndexWatcher(cwd = getBridgeRoot()) {
|
|
37978
|
-
const resolved =
|
|
38451
|
+
const resolved = path35.resolve(cwd);
|
|
37979
38452
|
void buildFileIndexAsync(resolved).catch((e) => {
|
|
37980
38453
|
if (e instanceof CliSqliteInterrupted) return;
|
|
37981
38454
|
console.error("[file-index] Initial index build failed:", e);
|
|
@@ -38005,7 +38478,7 @@ function startFileIndexWatcher(cwd = getBridgeRoot()) {
|
|
|
38005
38478
|
}
|
|
38006
38479
|
|
|
38007
38480
|
// src/connection/create-bridge-connection.ts
|
|
38008
|
-
import * as
|
|
38481
|
+
import * as path46 from "node:path";
|
|
38009
38482
|
|
|
38010
38483
|
// src/dev-servers/manager/dev-server-manager.ts
|
|
38011
38484
|
import { rm as rm2 } from "node:fs/promises";
|
|
@@ -38049,7 +38522,7 @@ function forceKillChild(proc, log2, shortId, graceMs) {
|
|
|
38049
38522
|
}
|
|
38050
38523
|
|
|
38051
38524
|
// src/dev-servers/process/wire-dev-server-child-process.ts
|
|
38052
|
-
import
|
|
38525
|
+
import fs28 from "node:fs";
|
|
38053
38526
|
|
|
38054
38527
|
// src/dev-servers/manager/forward-pipe.ts
|
|
38055
38528
|
function forwardChildPipe(childReadable, terminal, onData) {
|
|
@@ -38085,7 +38558,7 @@ function wireDevServerChildProcess(d) {
|
|
|
38085
38558
|
d.setPollInterval(void 0);
|
|
38086
38559
|
return;
|
|
38087
38560
|
}
|
|
38088
|
-
|
|
38561
|
+
fs28.readFile(d.mergedLogPath, (err, buf) => {
|
|
38089
38562
|
if (err || (d.getSpawnGeneration() ?? 0) !== d.scheduledGen) return;
|
|
38090
38563
|
if (buf.length <= d.mergedReadPos.value) return;
|
|
38091
38564
|
const chunk = Buffer.from(buf.subarray(d.mergedReadPos.value));
|
|
@@ -38123,7 +38596,7 @@ ${errTail}` : ""}`);
|
|
|
38123
38596
|
d.sendStatus(code === 0 || code == null ? "stopped" : "error", detail, tails);
|
|
38124
38597
|
};
|
|
38125
38598
|
if (mergedPath) {
|
|
38126
|
-
|
|
38599
|
+
fs28.readFile(mergedPath, (err, buf) => {
|
|
38127
38600
|
if (!err && buf.length > d.mergedReadPos.value) {
|
|
38128
38601
|
const chunk = Buffer.from(buf.subarray(d.mergedReadPos.value));
|
|
38129
38602
|
if (chunk.length > 0) {
|
|
@@ -38225,13 +38698,13 @@ function parseDevServerDefs(servers) {
|
|
|
38225
38698
|
}
|
|
38226
38699
|
|
|
38227
38700
|
// src/dev-servers/manager/shell-spawn/utils.ts
|
|
38228
|
-
import
|
|
38701
|
+
import fs29 from "node:fs";
|
|
38229
38702
|
function isSpawnEbadf(e) {
|
|
38230
38703
|
return typeof e === "object" && e !== null && "code" in e && e.code === "EBADF";
|
|
38231
38704
|
}
|
|
38232
38705
|
function rmDirQuiet(dir) {
|
|
38233
38706
|
try {
|
|
38234
|
-
|
|
38707
|
+
fs29.rmSync(dir, { recursive: true, force: true });
|
|
38235
38708
|
} catch {
|
|
38236
38709
|
}
|
|
38237
38710
|
}
|
|
@@ -38239,7 +38712,7 @@ var cachedDevNullReadFd;
|
|
|
38239
38712
|
function devNullReadFd() {
|
|
38240
38713
|
if (cachedDevNullReadFd === void 0) {
|
|
38241
38714
|
const devPath = process.platform === "win32" ? "nul" : "/dev/null";
|
|
38242
|
-
cachedDevNullReadFd =
|
|
38715
|
+
cachedDevNullReadFd = fs29.openSync(devPath, "r");
|
|
38243
38716
|
}
|
|
38244
38717
|
return cachedDevNullReadFd;
|
|
38245
38718
|
}
|
|
@@ -38313,15 +38786,15 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
|
|
|
38313
38786
|
|
|
38314
38787
|
// src/dev-servers/manager/shell-spawn/try-spawn-merged-log-file.ts
|
|
38315
38788
|
import { spawn as spawn7 } from "node:child_process";
|
|
38316
|
-
import
|
|
38789
|
+
import fs30 from "node:fs";
|
|
38317
38790
|
import { tmpdir } from "node:os";
|
|
38318
|
-
import
|
|
38791
|
+
import path36 from "node:path";
|
|
38319
38792
|
function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
38320
|
-
const tmpRoot =
|
|
38321
|
-
const logPath =
|
|
38793
|
+
const tmpRoot = fs30.mkdtempSync(path36.join(tmpdir(), "ba-devsrv-log-"));
|
|
38794
|
+
const logPath = path36.join(tmpRoot, "combined.log");
|
|
38322
38795
|
let logFd;
|
|
38323
38796
|
try {
|
|
38324
|
-
logFd =
|
|
38797
|
+
logFd = fs30.openSync(logPath, "a");
|
|
38325
38798
|
} catch {
|
|
38326
38799
|
rmDirQuiet(tmpRoot);
|
|
38327
38800
|
return null;
|
|
@@ -38340,7 +38813,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
|
38340
38813
|
} else {
|
|
38341
38814
|
proc = spawn7("/bin/sh", ["-c", command], { env, cwd, stdio, ...signal ? { signal } : {} });
|
|
38342
38815
|
}
|
|
38343
|
-
|
|
38816
|
+
fs30.closeSync(logFd);
|
|
38344
38817
|
return {
|
|
38345
38818
|
proc,
|
|
38346
38819
|
pipedStdoutStderr: true,
|
|
@@ -38349,7 +38822,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
|
38349
38822
|
};
|
|
38350
38823
|
} catch (e) {
|
|
38351
38824
|
try {
|
|
38352
|
-
|
|
38825
|
+
fs30.closeSync(logFd);
|
|
38353
38826
|
} catch {
|
|
38354
38827
|
}
|
|
38355
38828
|
rmDirQuiet(tmpRoot);
|
|
@@ -38360,22 +38833,22 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
|
38360
38833
|
|
|
38361
38834
|
// src/dev-servers/manager/shell-spawn/try-spawn-shell-script-log-redirect.ts
|
|
38362
38835
|
import { spawn as spawn8 } from "node:child_process";
|
|
38363
|
-
import
|
|
38836
|
+
import fs31 from "node:fs";
|
|
38364
38837
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
38365
|
-
import
|
|
38838
|
+
import path37 from "node:path";
|
|
38366
38839
|
function shSingleQuote(s) {
|
|
38367
38840
|
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
38368
38841
|
}
|
|
38369
38842
|
function trySpawnShellScriptLogRedirectUnix(command, env, cwd, signal) {
|
|
38370
|
-
const tmpRoot =
|
|
38371
|
-
const logPath =
|
|
38372
|
-
const innerPath =
|
|
38373
|
-
const runnerPath =
|
|
38843
|
+
const tmpRoot = fs31.mkdtempSync(path37.join(tmpdir2(), "ba-devsrv-sh-"));
|
|
38844
|
+
const logPath = path37.join(tmpRoot, "combined.log");
|
|
38845
|
+
const innerPath = path37.join(tmpRoot, "_cmd.sh");
|
|
38846
|
+
const runnerPath = path37.join(tmpRoot, "_run.sh");
|
|
38374
38847
|
try {
|
|
38375
|
-
|
|
38848
|
+
fs31.writeFileSync(innerPath, `#!/bin/sh
|
|
38376
38849
|
${command}
|
|
38377
38850
|
`);
|
|
38378
|
-
|
|
38851
|
+
fs31.writeFileSync(
|
|
38379
38852
|
runnerPath,
|
|
38380
38853
|
`#!/bin/sh
|
|
38381
38854
|
cd ${shSingleQuote(cwd)}
|
|
@@ -38401,13 +38874,13 @@ cd ${shSingleQuote(cwd)}
|
|
|
38401
38874
|
}
|
|
38402
38875
|
}
|
|
38403
38876
|
function trySpawnShellScriptLogRedirectWin(command, env, cwd, signal) {
|
|
38404
|
-
const tmpRoot =
|
|
38405
|
-
const logPath =
|
|
38406
|
-
const runnerPath =
|
|
38877
|
+
const tmpRoot = fs31.mkdtempSync(path37.join(tmpdir2(), "ba-devsrv-sh-"));
|
|
38878
|
+
const logPath = path37.join(tmpRoot, "combined.log");
|
|
38879
|
+
const runnerPath = path37.join(tmpRoot, "_run.bat");
|
|
38407
38880
|
const q = (p) => `"${p.replace(/"/g, '""')}"`;
|
|
38408
38881
|
const com = process.env.ComSpec || "cmd.exe";
|
|
38409
38882
|
try {
|
|
38410
|
-
|
|
38883
|
+
fs31.writeFileSync(
|
|
38411
38884
|
runnerPath,
|
|
38412
38885
|
`@ECHO OFF\r
|
|
38413
38886
|
CD /D ${q(cwd)}\r
|
|
@@ -39344,30 +39817,30 @@ function createOnBridgeIdentified(opts) {
|
|
|
39344
39817
|
}
|
|
39345
39818
|
|
|
39346
39819
|
// src/skills/discover-local-agent-skills.ts
|
|
39347
|
-
import
|
|
39348
|
-
import
|
|
39820
|
+
import fs32 from "node:fs";
|
|
39821
|
+
import path38 from "node:path";
|
|
39349
39822
|
var SKILL_DISCOVERY_ROOTS = [".agents/skills", ".claude/skills", ".cursor/skills", "skills"];
|
|
39350
39823
|
function discoverLocalSkills(cwd) {
|
|
39351
39824
|
const out = [];
|
|
39352
39825
|
const seenKeys = /* @__PURE__ */ new Set();
|
|
39353
39826
|
for (const rel of SKILL_DISCOVERY_ROOTS) {
|
|
39354
|
-
const base =
|
|
39355
|
-
if (!
|
|
39827
|
+
const base = path38.join(cwd, rel);
|
|
39828
|
+
if (!fs32.existsSync(base) || !fs32.statSync(base).isDirectory()) continue;
|
|
39356
39829
|
let entries = [];
|
|
39357
39830
|
try {
|
|
39358
|
-
entries =
|
|
39831
|
+
entries = fs32.readdirSync(base);
|
|
39359
39832
|
} catch {
|
|
39360
39833
|
continue;
|
|
39361
39834
|
}
|
|
39362
39835
|
for (const name of entries) {
|
|
39363
|
-
const dir =
|
|
39836
|
+
const dir = path38.join(base, name);
|
|
39364
39837
|
try {
|
|
39365
|
-
if (!
|
|
39838
|
+
if (!fs32.statSync(dir).isDirectory()) continue;
|
|
39366
39839
|
} catch {
|
|
39367
39840
|
continue;
|
|
39368
39841
|
}
|
|
39369
|
-
const skillMd =
|
|
39370
|
-
if (!
|
|
39842
|
+
const skillMd = path38.join(dir, "SKILL.md");
|
|
39843
|
+
if (!fs32.existsSync(skillMd)) continue;
|
|
39371
39844
|
const key = `${rel}/${name}`;
|
|
39372
39845
|
if (seenKeys.has(key)) continue;
|
|
39373
39846
|
seenKeys.add(key);
|
|
@@ -39379,23 +39852,23 @@ function discoverLocalSkills(cwd) {
|
|
|
39379
39852
|
function discoverSkillLayoutRoots(cwd) {
|
|
39380
39853
|
const roots = [];
|
|
39381
39854
|
for (const rel of SKILL_DISCOVERY_ROOTS) {
|
|
39382
|
-
const base =
|
|
39383
|
-
if (!
|
|
39855
|
+
const base = path38.join(cwd, rel);
|
|
39856
|
+
if (!fs32.existsSync(base) || !fs32.statSync(base).isDirectory()) continue;
|
|
39384
39857
|
let entries = [];
|
|
39385
39858
|
try {
|
|
39386
|
-
entries =
|
|
39859
|
+
entries = fs32.readdirSync(base);
|
|
39387
39860
|
} catch {
|
|
39388
39861
|
continue;
|
|
39389
39862
|
}
|
|
39390
39863
|
const skills2 = [];
|
|
39391
39864
|
for (const name of entries) {
|
|
39392
|
-
const dir =
|
|
39865
|
+
const dir = path38.join(base, name);
|
|
39393
39866
|
try {
|
|
39394
|
-
if (!
|
|
39867
|
+
if (!fs32.statSync(dir).isDirectory()) continue;
|
|
39395
39868
|
} catch {
|
|
39396
39869
|
continue;
|
|
39397
39870
|
}
|
|
39398
|
-
if (!
|
|
39871
|
+
if (!fs32.existsSync(path38.join(dir, "SKILL.md"))) continue;
|
|
39399
39872
|
const relPath = `${rel}/${name}`.replace(/\\/g, "/");
|
|
39400
39873
|
skills2.push({ name, relPath });
|
|
39401
39874
|
}
|
|
@@ -39751,7 +40224,7 @@ function syncRunningTurnQueueKeys(turns, queueKey) {
|
|
|
39751
40224
|
}
|
|
39752
40225
|
|
|
39753
40226
|
// src/prompt-turn-queue/runner/run-local-revert-before-queued-prompt.ts
|
|
39754
|
-
import
|
|
40227
|
+
import fs33 from "node:fs";
|
|
39755
40228
|
async function runLocalRevertBeforeQueuedPrompt(next, deps) {
|
|
39756
40229
|
if (next.serverState !== "requeued_with_revert") return true;
|
|
39757
40230
|
const sid = next.sessionId;
|
|
@@ -39760,7 +40233,7 @@ async function runLocalRevertBeforeQueuedPrompt(next, deps) {
|
|
|
39760
40233
|
const agentBase = deps.sessionWorktreeManager.getSessionWorktreeRootForSession(sid) ?? getBridgeRoot();
|
|
39761
40234
|
const file2 = snapshotFilePath(agentBase, tid);
|
|
39762
40235
|
try {
|
|
39763
|
-
await
|
|
40236
|
+
await fs33.promises.access(file2, fs33.constants.F_OK);
|
|
39764
40237
|
} catch {
|
|
39765
40238
|
deps.log(
|
|
39766
40239
|
`[Queue] requeued_with_revert: no pre-turn snapshot for ${tid.slice(0, 8)}\u2026; continuing without revert.`
|
|
@@ -40048,9 +40521,9 @@ function parseChangeSummarySnapshots(raw) {
|
|
|
40048
40521
|
for (const item of raw) {
|
|
40049
40522
|
if (!item || typeof item !== "object") continue;
|
|
40050
40523
|
const o = item;
|
|
40051
|
-
const
|
|
40052
|
-
if (!
|
|
40053
|
-
const row = { path:
|
|
40524
|
+
const path48 = typeof o.path === "string" && o.path.trim() !== "" ? o.path.trim() : "";
|
|
40525
|
+
if (!path48) continue;
|
|
40526
|
+
const row = { path: path48 };
|
|
40054
40527
|
if (typeof o.patchContent === "string") row.patchContent = o.patchContent;
|
|
40055
40528
|
if (typeof o.oldText === "string") row.oldText = o.oldText;
|
|
40056
40529
|
if (typeof o.newText === "string") row.newText = o.newText;
|
|
@@ -40260,8 +40733,8 @@ function randomSecret() {
|
|
|
40260
40733
|
}
|
|
40261
40734
|
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
40262
40735
|
}
|
|
40263
|
-
async function requestPreviewApi(port, secret, method,
|
|
40264
|
-
const url2 = `http://127.0.0.1:${port}${
|
|
40736
|
+
async function requestPreviewApi(port, secret, method, path48, body) {
|
|
40737
|
+
const url2 = `http://127.0.0.1:${port}${path48}`;
|
|
40265
40738
|
const headers = {
|
|
40266
40739
|
[PREVIEW_SECRET_HEADER]: secret,
|
|
40267
40740
|
"Content-Type": "application/json"
|
|
@@ -40273,7 +40746,7 @@ async function requestPreviewApi(port, secret, method, path46, body) {
|
|
|
40273
40746
|
});
|
|
40274
40747
|
const data = await res.json().catch(() => ({}));
|
|
40275
40748
|
if (!res.ok) {
|
|
40276
|
-
throw new Error(data?.error ?? `Preview API ${method} ${
|
|
40749
|
+
throw new Error(data?.error ?? `Preview API ${method} ${path48}: ${res.status}`);
|
|
40277
40750
|
}
|
|
40278
40751
|
return data;
|
|
40279
40752
|
}
|
|
@@ -40438,14 +40911,14 @@ var handleSkillCallMessage = (msg, { getWs, log: log2 }) => {
|
|
|
40438
40911
|
};
|
|
40439
40912
|
|
|
40440
40913
|
// src/files/list-dir/index.ts
|
|
40441
|
-
import
|
|
40914
|
+
import fs35 from "node:fs";
|
|
40442
40915
|
|
|
40443
40916
|
// src/files/ensure-under-cwd.ts
|
|
40444
|
-
import
|
|
40917
|
+
import path39 from "node:path";
|
|
40445
40918
|
function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
|
|
40446
|
-
const normalized =
|
|
40447
|
-
const resolved =
|
|
40448
|
-
if (!resolved.startsWith(cwd +
|
|
40919
|
+
const normalized = path39.normalize(relativePath).replace(/^(\.\/)+/, "");
|
|
40920
|
+
const resolved = path39.resolve(cwd, normalized);
|
|
40921
|
+
if (!resolved.startsWith(cwd + path39.sep) && resolved !== cwd) {
|
|
40449
40922
|
return null;
|
|
40450
40923
|
}
|
|
40451
40924
|
return resolved;
|
|
@@ -40455,15 +40928,15 @@ function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
|
|
|
40455
40928
|
var LIST_DIR_YIELD_EVERY = 256;
|
|
40456
40929
|
|
|
40457
40930
|
// src/files/list-dir/map-dir-entry.ts
|
|
40458
|
-
import
|
|
40459
|
-
import
|
|
40931
|
+
import path40 from "node:path";
|
|
40932
|
+
import fs34 from "node:fs";
|
|
40460
40933
|
async function mapDirEntry(d, relativePath, resolved) {
|
|
40461
|
-
const entryPath =
|
|
40462
|
-
const fullPath =
|
|
40934
|
+
const entryPath = path40.join(relativePath || ".", d.name).replace(/\\/g, "/");
|
|
40935
|
+
const fullPath = path40.join(resolved, d.name);
|
|
40463
40936
|
let isDir = d.isDirectory();
|
|
40464
40937
|
if (d.isSymbolicLink()) {
|
|
40465
40938
|
try {
|
|
40466
|
-
const targetStat = await
|
|
40939
|
+
const targetStat = await fs34.promises.stat(fullPath);
|
|
40467
40940
|
isDir = targetStat.isDirectory();
|
|
40468
40941
|
} catch {
|
|
40469
40942
|
isDir = false;
|
|
@@ -40493,7 +40966,7 @@ async function listDirAsync(relativePath, sessionParentPath = getBridgeRoot()) {
|
|
|
40493
40966
|
return { error: "Path is outside working directory" };
|
|
40494
40967
|
}
|
|
40495
40968
|
try {
|
|
40496
|
-
const names = await
|
|
40969
|
+
const names = await fs35.promises.readdir(resolved, { withFileTypes: true });
|
|
40497
40970
|
const entries = [];
|
|
40498
40971
|
for (let i = 0; i < names.length; i++) {
|
|
40499
40972
|
if (i > 0 && i % LIST_DIR_YIELD_EVERY === 0) {
|
|
@@ -40513,18 +40986,18 @@ var LINE_CHUNK_SIZE = 64 * 1024;
|
|
|
40513
40986
|
var READ_RANGE_YIELD_EVERY_BYTES = 256 * 1024;
|
|
40514
40987
|
|
|
40515
40988
|
// src/files/read-file/resolve-file-path.ts
|
|
40516
|
-
import
|
|
40989
|
+
import fs36 from "node:fs";
|
|
40517
40990
|
async function resolveFilePathAsync(relativePath, sessionParentPath = getBridgeRoot()) {
|
|
40518
40991
|
const resolved = ensureUnderCwd(relativePath, sessionParentPath);
|
|
40519
40992
|
if (!resolved) return { error: "Path is outside working directory" };
|
|
40520
40993
|
let real;
|
|
40521
40994
|
try {
|
|
40522
|
-
real = await
|
|
40995
|
+
real = await fs36.promises.realpath(resolved);
|
|
40523
40996
|
} catch {
|
|
40524
40997
|
real = resolved;
|
|
40525
40998
|
}
|
|
40526
40999
|
try {
|
|
40527
|
-
const stat3 = await
|
|
41000
|
+
const stat3 = await fs36.promises.stat(real);
|
|
40528
41001
|
if (!stat3.isFile()) return { error: "Not a file" };
|
|
40529
41002
|
return real;
|
|
40530
41003
|
} catch (err) {
|
|
@@ -40533,11 +41006,11 @@ async function resolveFilePathAsync(relativePath, sessionParentPath = getBridgeR
|
|
|
40533
41006
|
}
|
|
40534
41007
|
|
|
40535
41008
|
// src/files/read-file/read-file-range-async.ts
|
|
40536
|
-
import
|
|
41009
|
+
import fs37 from "node:fs";
|
|
40537
41010
|
import { StringDecoder } from "node:string_decoder";
|
|
40538
41011
|
async function readFileRangeAsync(filePath, startLine, endLine, lineOffsetIn, lineChunkSize = LINE_CHUNK_SIZE) {
|
|
40539
|
-
const fileSize = (await
|
|
40540
|
-
const fd = await
|
|
41012
|
+
const fileSize = (await fs37.promises.stat(filePath)).size;
|
|
41013
|
+
const fd = await fs37.promises.open(filePath, "r");
|
|
40541
41014
|
const bufSize = 64 * 1024;
|
|
40542
41015
|
const buf = Buffer.alloc(bufSize);
|
|
40543
41016
|
const decoder = new StringDecoder("utf8");
|
|
@@ -40699,11 +41172,11 @@ async function readFileRangeAsync(filePath, startLine, endLine, lineOffsetIn, li
|
|
|
40699
41172
|
}
|
|
40700
41173
|
|
|
40701
41174
|
// src/files/read-file/read-file-buffer-full-async.ts
|
|
40702
|
-
import
|
|
41175
|
+
import fs38 from "node:fs";
|
|
40703
41176
|
var READ_CHUNK_BYTES = 256 * 1024;
|
|
40704
41177
|
async function readFileBufferFullAsync(filePath) {
|
|
40705
|
-
const stat3 = await
|
|
40706
|
-
const fd = await
|
|
41178
|
+
const stat3 = await fs38.promises.stat(filePath);
|
|
41179
|
+
const fd = await fs38.promises.open(filePath, "r");
|
|
40707
41180
|
const chunks = [];
|
|
40708
41181
|
let position = 0;
|
|
40709
41182
|
let bytesSinceYield = 0;
|
|
@@ -40810,13 +41283,13 @@ function resolveFileBrowserSessionParent(sessionWorktreeManager, sessionId) {
|
|
|
40810
41283
|
}
|
|
40811
41284
|
|
|
40812
41285
|
// src/files/handle-file-browser-search.ts
|
|
40813
|
-
import
|
|
41286
|
+
import path41 from "node:path";
|
|
40814
41287
|
var SEARCH_LIMIT = 100;
|
|
40815
41288
|
function handleFileBrowserSearch(msg, socket, e2ee, sessionWorktreeManager) {
|
|
40816
41289
|
void (async () => {
|
|
40817
41290
|
await yieldToEventLoop();
|
|
40818
41291
|
const q = typeof msg.q === "string" ? msg.q : "";
|
|
40819
|
-
const sessionParentPath =
|
|
41292
|
+
const sessionParentPath = path41.resolve(
|
|
40820
41293
|
sessionWorktreeManager != null ? resolveFileBrowserSessionParent(sessionWorktreeManager, msg.sessionId) : getBridgeRoot()
|
|
40821
41294
|
);
|
|
40822
41295
|
if (!await bridgeFileIndexIsPopulated(sessionParentPath)) {
|
|
@@ -40935,8 +41408,8 @@ function handleSkillLayoutRequest(msg, deps) {
|
|
|
40935
41408
|
}
|
|
40936
41409
|
|
|
40937
41410
|
// src/skills/install-remote-skills.ts
|
|
40938
|
-
import
|
|
40939
|
-
import
|
|
41411
|
+
import fs39 from "node:fs";
|
|
41412
|
+
import path42 from "node:path";
|
|
40940
41413
|
function installRemoteSkills(cwd, targetDir, items) {
|
|
40941
41414
|
const installed2 = [];
|
|
40942
41415
|
if (!Array.isArray(items)) {
|
|
@@ -40947,15 +41420,15 @@ function installRemoteSkills(cwd, targetDir, items) {
|
|
|
40947
41420
|
if (typeof item.sourceId !== "string" || typeof item.skillName !== "string" || typeof item.versionHash !== "string" || !Array.isArray(item.files)) {
|
|
40948
41421
|
continue;
|
|
40949
41422
|
}
|
|
40950
|
-
const skillDir =
|
|
41423
|
+
const skillDir = path42.join(cwd, targetDir, item.skillName);
|
|
40951
41424
|
for (const f of item.files) {
|
|
40952
41425
|
if (typeof f.path !== "string" || !f.text && !f.base64) continue;
|
|
40953
|
-
const dest =
|
|
40954
|
-
|
|
41426
|
+
const dest = path42.join(skillDir, f.path);
|
|
41427
|
+
fs39.mkdirSync(path42.dirname(dest), { recursive: true });
|
|
40955
41428
|
if (f.text !== void 0) {
|
|
40956
|
-
|
|
41429
|
+
fs39.writeFileSync(dest, f.text, "utf8");
|
|
40957
41430
|
} else if (f.base64) {
|
|
40958
|
-
|
|
41431
|
+
fs39.writeFileSync(dest, Buffer.from(f.base64, "base64"));
|
|
40959
41432
|
}
|
|
40960
41433
|
}
|
|
40961
41434
|
installed2.push({
|
|
@@ -41113,7 +41586,7 @@ var handleSessionDiscardedMessage = (msg, deps) => {
|
|
|
41113
41586
|
};
|
|
41114
41587
|
|
|
41115
41588
|
// src/routing/handlers/revert-turn-snapshot.ts
|
|
41116
|
-
import * as
|
|
41589
|
+
import * as fs40 from "node:fs";
|
|
41117
41590
|
var handleRevertTurnSnapshotMessage = (msg, deps) => {
|
|
41118
41591
|
const id = typeof msg.id === "string" ? msg.id : "";
|
|
41119
41592
|
const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
|
|
@@ -41126,7 +41599,7 @@ var handleRevertTurnSnapshotMessage = (msg, deps) => {
|
|
|
41126
41599
|
const agentBase = sessionWorktreeManager.getSessionWorktreeRootForSession(sessionId) ?? getBridgeRoot();
|
|
41127
41600
|
const file2 = snapshotFilePath(agentBase, turnId);
|
|
41128
41601
|
try {
|
|
41129
|
-
await
|
|
41602
|
+
await fs40.promises.access(file2, fs40.constants.F_OK);
|
|
41130
41603
|
} catch {
|
|
41131
41604
|
sendWsMessage(s, {
|
|
41132
41605
|
type: "revert_turn_snapshot_result",
|
|
@@ -41168,7 +41641,7 @@ var handleDevServersConfig = (msg, deps) => {
|
|
|
41168
41641
|
};
|
|
41169
41642
|
|
|
41170
41643
|
// src/git/bridge-git-context.ts
|
|
41171
|
-
import * as
|
|
41644
|
+
import * as path43 from "node:path";
|
|
41172
41645
|
|
|
41173
41646
|
// src/git/branches/get-current-branch.ts
|
|
41174
41647
|
async function getCurrentBranch(repoPath) {
|
|
@@ -41218,12 +41691,12 @@ async function listRepoBranchRefs(repoPath) {
|
|
|
41218
41691
|
// src/git/bridge-git-context.ts
|
|
41219
41692
|
function folderNameForRelPath(relPath, bridgeRoot) {
|
|
41220
41693
|
if (relPath === "." || relPath === "") {
|
|
41221
|
-
return
|
|
41694
|
+
return path43.basename(path43.resolve(bridgeRoot)) || "repo";
|
|
41222
41695
|
}
|
|
41223
|
-
return
|
|
41696
|
+
return path43.basename(relPath) || relPath;
|
|
41224
41697
|
}
|
|
41225
41698
|
async function discoverGitReposForBridgeContext(bridgeRoot) {
|
|
41226
|
-
const root =
|
|
41699
|
+
const root = path43.resolve(bridgeRoot);
|
|
41227
41700
|
if (await isGitRepoDirectory(root)) {
|
|
41228
41701
|
const remoteUrl = await getRemoteOriginUrl(root);
|
|
41229
41702
|
return [{ absolutePath: root, remoteUrl }];
|
|
@@ -41231,17 +41704,17 @@ async function discoverGitReposForBridgeContext(bridgeRoot) {
|
|
|
41231
41704
|
const [deep, shallow] = await Promise.all([discoverGitReposUnderRoot(root), discoverGitRepos(root)]);
|
|
41232
41705
|
const byPath = /* @__PURE__ */ new Map();
|
|
41233
41706
|
for (const repo of [...deep, ...shallow]) {
|
|
41234
|
-
byPath.set(
|
|
41707
|
+
byPath.set(path43.resolve(repo.absolutePath), repo);
|
|
41235
41708
|
}
|
|
41236
41709
|
return [...byPath.values()];
|
|
41237
41710
|
}
|
|
41238
41711
|
async function getBridgeGitContext(bridgeRoot = getBridgeRoot()) {
|
|
41239
|
-
const bridgeResolved =
|
|
41712
|
+
const bridgeResolved = path43.resolve(bridgeRoot);
|
|
41240
41713
|
const repos = await discoverGitReposForBridgeContext(bridgeResolved);
|
|
41241
41714
|
const rows = [];
|
|
41242
41715
|
for (const repo of repos) {
|
|
41243
|
-
let rel =
|
|
41244
|
-
if (rel.startsWith("..") ||
|
|
41716
|
+
let rel = path43.relative(bridgeResolved, repo.absolutePath);
|
|
41717
|
+
if (rel.startsWith("..") || path43.isAbsolute(rel)) continue;
|
|
41245
41718
|
const relPath = rel === "" ? "." : rel.replace(/\\/g, "/");
|
|
41246
41719
|
const currentBranch = await getCurrentBranch(repo.absolutePath);
|
|
41247
41720
|
const remoteUrl = repo.remoteUrl.trim() || null;
|
|
@@ -41256,11 +41729,11 @@ async function getBridgeGitContext(bridgeRoot = getBridgeRoot()) {
|
|
|
41256
41729
|
return rows;
|
|
41257
41730
|
}
|
|
41258
41731
|
async function listRepoBranchesForBridge(repoRelPath, bridgeRoot = getBridgeRoot()) {
|
|
41259
|
-
const bridgeResolved =
|
|
41732
|
+
const bridgeResolved = path43.resolve(bridgeRoot);
|
|
41260
41733
|
const rel = repoRelPath.trim() === "." ? "" : repoRelPath.trim().replace(/\\/g, "/");
|
|
41261
|
-
const repoPath = rel === "" ? bridgeResolved :
|
|
41262
|
-
const resolved =
|
|
41263
|
-
if (!resolved.startsWith(bridgeResolved +
|
|
41734
|
+
const repoPath = rel === "" ? bridgeResolved : path43.join(bridgeResolved, rel);
|
|
41735
|
+
const resolved = path43.resolve(repoPath);
|
|
41736
|
+
if (!resolved.startsWith(bridgeResolved + path43.sep) && resolved !== bridgeResolved) {
|
|
41264
41737
|
return [];
|
|
41265
41738
|
}
|
|
41266
41739
|
return listRepoBranchRefs(resolved);
|
|
@@ -41765,10 +42238,10 @@ function listCliAgentCapabilityCacheForWorkspace(db, workspaceId) {
|
|
|
41765
42238
|
}
|
|
41766
42239
|
|
|
41767
42240
|
// src/agents/capabilities/warmup-agent-capabilities-on-connect.ts
|
|
41768
|
-
import * as
|
|
42241
|
+
import * as path45 from "node:path";
|
|
41769
42242
|
|
|
41770
42243
|
// src/agents/capabilities/probe-one-agent-type-for-capabilities.ts
|
|
41771
|
-
import * as
|
|
42244
|
+
import * as path44 from "node:path";
|
|
41772
42245
|
async function probeOneAgentTypeForCapabilities(params) {
|
|
41773
42246
|
const { agentType, cwd, workspaceId, log: log2, reportAgentCapabilities, bridgeReport = true, shouldContinue } = params;
|
|
41774
42247
|
const canContinue = () => shouldContinue?.() !== false;
|
|
@@ -41806,7 +42279,7 @@ async function probeOneAgentTypeForCapabilities(params) {
|
|
|
41806
42279
|
if (!canContinue()) return false;
|
|
41807
42280
|
handle = await resolved.createClient({
|
|
41808
42281
|
command: resolved.command,
|
|
41809
|
-
cwd:
|
|
42282
|
+
cwd: path44.resolve(cwd),
|
|
41810
42283
|
backendAgentType: agentType,
|
|
41811
42284
|
sessionMode: "agent",
|
|
41812
42285
|
persistedAcpSessionId: null,
|
|
@@ -41884,7 +42357,7 @@ async function warmupAgentCapabilitiesOnConnect(params) {
|
|
|
41884
42357
|
const { workspaceId, log: log2, getWs } = params;
|
|
41885
42358
|
const isCurrent = beginAgentCapabilityWarmupRun();
|
|
41886
42359
|
if (!isCurrent()) return;
|
|
41887
|
-
const cwd =
|
|
42360
|
+
const cwd = path45.resolve(getBridgeRoot());
|
|
41888
42361
|
async function sendBatchFromCache() {
|
|
41889
42362
|
if (!isCurrent()) return;
|
|
41890
42363
|
const socket = getWs();
|
|
@@ -41950,6 +42423,232 @@ async function warmupAgentCapabilitiesOnConnect(params) {
|
|
|
41950
42423
|
})();
|
|
41951
42424
|
}
|
|
41952
42425
|
|
|
42426
|
+
// src/mcp/tools/session-history/fork-session-mcp-tools.ts
|
|
42427
|
+
var LIST_PARENT_SESSION_PROMPTS_TOOL = "list_parent_session_prompts";
|
|
42428
|
+
var GET_PARENT_SESSION_TURN_TRANSCRIPT_TOOL = "get_parent_session_turn_transcript";
|
|
42429
|
+
var SESSION_ID_SCHEMA = {
|
|
42430
|
+
type: "string",
|
|
42431
|
+
description: "Id of this forked (child) session \u2014 not the parent session id. Required on every tool call."
|
|
42432
|
+
};
|
|
42433
|
+
var FORK_SESSION_MCP_TOOL_DEFINITIONS = [
|
|
42434
|
+
{
|
|
42435
|
+
name: LIST_PARENT_SESSION_PROMPTS_TOOL,
|
|
42436
|
+
description: "List user prompts (main turns) from the parent session a fork was created from. Requires sessionId. Returns an error if the session has no parent history (not a fork).",
|
|
42437
|
+
inputSchema: {
|
|
42438
|
+
type: "object",
|
|
42439
|
+
properties: { sessionId: SESSION_ID_SCHEMA },
|
|
42440
|
+
required: ["sessionId"],
|
|
42441
|
+
additionalProperties: false
|
|
42442
|
+
}
|
|
42443
|
+
},
|
|
42444
|
+
{
|
|
42445
|
+
name: GET_PARENT_SESSION_TURN_TRANSCRIPT_TOOL,
|
|
42446
|
+
description: "Full transcript for one parent-session turn, including tool calls and agent messages. Requires sessionId and turnId. Returns an error if the session has no parent history (not a fork).",
|
|
42447
|
+
inputSchema: {
|
|
42448
|
+
type: "object",
|
|
42449
|
+
properties: {
|
|
42450
|
+
sessionId: SESSION_ID_SCHEMA,
|
|
42451
|
+
turnId: { type: "string", description: "Turn id from list_parent_session_prompts" }
|
|
42452
|
+
},
|
|
42453
|
+
required: ["sessionId", "turnId"],
|
|
42454
|
+
additionalProperties: false
|
|
42455
|
+
}
|
|
42456
|
+
}
|
|
42457
|
+
];
|
|
42458
|
+
function textResult(text, isError = false) {
|
|
42459
|
+
return { content: [{ type: "text", text }], ...isError ? { isError: true } : {} };
|
|
42460
|
+
}
|
|
42461
|
+
function isForkSessionMcpToolName(name) {
|
|
42462
|
+
return name === LIST_PARENT_SESSION_PROMPTS_TOOL || name === GET_PARENT_SESSION_TURN_TRANSCRIPT_TOOL;
|
|
42463
|
+
}
|
|
42464
|
+
async function callForkSessionMcpTool(ctx, cloud, name, args) {
|
|
42465
|
+
const childSessionId = ctx.sessionId.trim();
|
|
42466
|
+
if (!childSessionId) {
|
|
42467
|
+
return textResult("sessionId is required.", true);
|
|
42468
|
+
}
|
|
42469
|
+
const parentResolved = await resolveParentSessionIdForChild({
|
|
42470
|
+
childSessionId,
|
|
42471
|
+
cloudApiBaseUrl: cloud.cloudApiBaseUrl,
|
|
42472
|
+
getCloudAccessToken: cloud.getCloudAccessToken
|
|
42473
|
+
});
|
|
42474
|
+
if (!parentResolved.ok) {
|
|
42475
|
+
return textResult(parentResolved.error, true);
|
|
42476
|
+
}
|
|
42477
|
+
const parentSessionId = parentResolved.parentSessionId;
|
|
42478
|
+
if (name === LIST_PARENT_SESSION_PROMPTS_TOOL) {
|
|
42479
|
+
const detail = await fetchCloudParentSessionPrompts({
|
|
42480
|
+
childSessionId,
|
|
42481
|
+
cloudApiBaseUrl: cloud.cloudApiBaseUrl,
|
|
42482
|
+
getCloudAccessToken: cloud.getCloudAccessToken
|
|
42483
|
+
});
|
|
42484
|
+
if (!detail.ok) return textResult(detail.error, true);
|
|
42485
|
+
const turns = Array.isArray(detail.data.turns) ? detail.data.turns : [];
|
|
42486
|
+
const payload = turns.map((t) => ({
|
|
42487
|
+
turnId: typeof t.turnId === "string" ? t.turnId : "",
|
|
42488
|
+
title: typeof t.title === "string" ? t.title : null,
|
|
42489
|
+
status: typeof t.status === "string" ? t.status : null,
|
|
42490
|
+
promptText: typeof t.promptText === "string" ? t.promptText : null
|
|
42491
|
+
}));
|
|
42492
|
+
return textResult(JSON.stringify({ parentSessionId, turns: payload }, null, 2));
|
|
42493
|
+
}
|
|
42494
|
+
if (name === GET_PARENT_SESSION_TURN_TRANSCRIPT_TOOL) {
|
|
42495
|
+
const turnId = typeof args.turnId === "string" ? args.turnId.trim() : "";
|
|
42496
|
+
if (!turnId) return textResult("turnId is required.", true);
|
|
42497
|
+
const transcript = await fetchCloudParentTurnTranscript({
|
|
42498
|
+
childSessionId,
|
|
42499
|
+
turnId,
|
|
42500
|
+
cloudApiBaseUrl: cloud.cloudApiBaseUrl,
|
|
42501
|
+
getCloudAccessToken: cloud.getCloudAccessToken,
|
|
42502
|
+
e2ee: ctx.e2ee
|
|
42503
|
+
});
|
|
42504
|
+
if (!transcript.ok) return textResult(transcript.error, true);
|
|
42505
|
+
return textResult(transcript.text);
|
|
42506
|
+
}
|
|
42507
|
+
return textResult(`Unknown fork session tool: ${name}`, true);
|
|
42508
|
+
}
|
|
42509
|
+
|
|
42510
|
+
// src/mcp/bridge-mcp-tools.ts
|
|
42511
|
+
function requireCloud(shared) {
|
|
42512
|
+
const cloudApiBaseUrl = shared.cloudApiBaseUrl?.trim() ?? "";
|
|
42513
|
+
const getCloudAccessToken = shared.getCloudAccessToken;
|
|
42514
|
+
if (!cloudApiBaseUrl || !getCloudAccessToken) return null;
|
|
42515
|
+
return { cloudApiBaseUrl, getCloudAccessToken };
|
|
42516
|
+
}
|
|
42517
|
+
function createBridgeMcpToolRegistry(shared) {
|
|
42518
|
+
const cloud = requireCloud(shared);
|
|
42519
|
+
return {
|
|
42520
|
+
listTools: async () => [...FORK_SESSION_MCP_TOOL_DEFINITIONS],
|
|
42521
|
+
callTool: async (name, args) => {
|
|
42522
|
+
if (isForkSessionMcpToolName(name)) {
|
|
42523
|
+
if (!cloud) {
|
|
42524
|
+
return {
|
|
42525
|
+
content: [{ type: "text", text: "Bridge MCP tools require cloud API credentials." }],
|
|
42526
|
+
isError: true
|
|
42527
|
+
};
|
|
42528
|
+
}
|
|
42529
|
+
const sessionId = typeof args.sessionId === "string" ? args.sessionId.trim() : "";
|
|
42530
|
+
if (!sessionId) {
|
|
42531
|
+
return {
|
|
42532
|
+
content: [{ type: "text", text: "sessionId is required." }],
|
|
42533
|
+
isError: true
|
|
42534
|
+
};
|
|
42535
|
+
}
|
|
42536
|
+
return callForkSessionMcpTool({ ...shared, sessionId }, cloud, name, args);
|
|
42537
|
+
}
|
|
42538
|
+
throw new Error(`Unknown bridge MCP tool: ${name}`);
|
|
42539
|
+
}
|
|
42540
|
+
};
|
|
42541
|
+
}
|
|
42542
|
+
|
|
42543
|
+
// src/mcp/bridge-access/start-server.ts
|
|
42544
|
+
import * as http from "node:http";
|
|
42545
|
+
|
|
42546
|
+
// src/mcp/bridge-access/read-json-body.ts
|
|
42547
|
+
function readJsonBody(req) {
|
|
42548
|
+
return new Promise((resolve22, reject) => {
|
|
42549
|
+
const chunks = [];
|
|
42550
|
+
req.on("data", (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
|
|
42551
|
+
req.on("end", () => {
|
|
42552
|
+
try {
|
|
42553
|
+
const raw = Buffer.concat(chunks).toString("utf8").trim();
|
|
42554
|
+
if (!raw) {
|
|
42555
|
+
resolve22({});
|
|
42556
|
+
return;
|
|
42557
|
+
}
|
|
42558
|
+
const parsed = JSON.parse(raw);
|
|
42559
|
+
resolve22(parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {});
|
|
42560
|
+
} catch (e) {
|
|
42561
|
+
reject(e);
|
|
42562
|
+
}
|
|
42563
|
+
});
|
|
42564
|
+
req.on("error", reject);
|
|
42565
|
+
});
|
|
42566
|
+
}
|
|
42567
|
+
|
|
42568
|
+
// src/mcp/bridge-access/send-json-response.ts
|
|
42569
|
+
function sendJsonResponse(res, status, body) {
|
|
42570
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
42571
|
+
res.end(JSON.stringify(body));
|
|
42572
|
+
}
|
|
42573
|
+
|
|
42574
|
+
// src/mcp/bridge-access/handle-request.ts
|
|
42575
|
+
function createBridgeAccessRequestHandler(registry2) {
|
|
42576
|
+
return (req, res) => {
|
|
42577
|
+
void (async () => {
|
|
42578
|
+
if (req.method !== "POST") {
|
|
42579
|
+
sendJsonResponse(res, 405, { ok: false, error: "Method not allowed" });
|
|
42580
|
+
return;
|
|
42581
|
+
}
|
|
42582
|
+
try {
|
|
42583
|
+
const body = await readJsonBody(req);
|
|
42584
|
+
if (req.url === "/tools/list") {
|
|
42585
|
+
const tools = await Promise.resolve(registry2.listTools());
|
|
42586
|
+
sendJsonResponse(res, 200, { ok: true, data: tools });
|
|
42587
|
+
return;
|
|
42588
|
+
}
|
|
42589
|
+
if (req.url === "/tools/call") {
|
|
42590
|
+
const sessionId = typeof body.sessionId === "string" ? body.sessionId.trim() : "";
|
|
42591
|
+
if (!sessionId) {
|
|
42592
|
+
sendJsonResponse(res, 400, { ok: false, error: "sessionId is required" });
|
|
42593
|
+
return;
|
|
42594
|
+
}
|
|
42595
|
+
const name = typeof body.name === "string" ? body.name.trim() : "";
|
|
42596
|
+
const args = body.args && typeof body.args === "object" && !Array.isArray(body.args) ? body.args : {};
|
|
42597
|
+
if (!name) {
|
|
42598
|
+
sendJsonResponse(res, 400, { ok: false, error: "name is required" });
|
|
42599
|
+
return;
|
|
42600
|
+
}
|
|
42601
|
+
const data = await registry2.callTool(name, { ...args, sessionId });
|
|
42602
|
+
sendJsonResponse(res, 200, { ok: true, data });
|
|
42603
|
+
return;
|
|
42604
|
+
}
|
|
42605
|
+
sendJsonResponse(res, 404, { ok: false, error: "Not found" });
|
|
42606
|
+
} catch (err) {
|
|
42607
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
42608
|
+
sendJsonResponse(res, 500, { ok: false, error: message });
|
|
42609
|
+
}
|
|
42610
|
+
})();
|
|
42611
|
+
};
|
|
42612
|
+
}
|
|
42613
|
+
|
|
42614
|
+
// src/mcp/bridge-access/start-server.ts
|
|
42615
|
+
function startBridgeAccessServer(registry2) {
|
|
42616
|
+
const server = http.createServer(createBridgeAccessRequestHandler(registry2));
|
|
42617
|
+
return new Promise((resolve22, reject) => {
|
|
42618
|
+
server.once("error", reject);
|
|
42619
|
+
server.listen(0, "127.0.0.1", () => {
|
|
42620
|
+
const addr = server.address();
|
|
42621
|
+
if (!addr || typeof addr === "string") {
|
|
42622
|
+
reject(new Error("Bridge access server did not bind"));
|
|
42623
|
+
return;
|
|
42624
|
+
}
|
|
42625
|
+
resolve22({
|
|
42626
|
+
port: addr.port,
|
|
42627
|
+
close: () => new Promise((closeResolve, closeReject) => {
|
|
42628
|
+
server.close((err) => err ? closeReject(err) : closeResolve());
|
|
42629
|
+
})
|
|
42630
|
+
});
|
|
42631
|
+
});
|
|
42632
|
+
});
|
|
42633
|
+
}
|
|
42634
|
+
|
|
42635
|
+
// src/mcp/bridge-access/create-state.ts
|
|
42636
|
+
async function createBridgeAccessState(options = {}) {
|
|
42637
|
+
const registry2 = createBridgeMcpToolRegistry({
|
|
42638
|
+
cloudApiBaseUrl: options.cloudApiBaseUrl,
|
|
42639
|
+
getCloudAccessToken: options.getCloudAccessToken,
|
|
42640
|
+
e2ee: options.e2ee
|
|
42641
|
+
});
|
|
42642
|
+
const server = await startBridgeAccessServer(registry2);
|
|
42643
|
+
return {
|
|
42644
|
+
getPort: () => server.port,
|
|
42645
|
+
getRegistry: () => registry2,
|
|
42646
|
+
close: async () => {
|
|
42647
|
+
await server.close();
|
|
42648
|
+
}
|
|
42649
|
+
};
|
|
42650
|
+
}
|
|
42651
|
+
|
|
41953
42652
|
// src/connection/create-bridge-connection.ts
|
|
41954
42653
|
async function createBridgeConnection(options) {
|
|
41955
42654
|
const { apiUrl, workspaceId, justAuthenticated, onAuthInvalid, persistTokens } = options;
|
|
@@ -42012,12 +42711,18 @@ async function createBridgeConnection(options) {
|
|
|
42012
42711
|
worktreesRootPath,
|
|
42013
42712
|
log: logFn
|
|
42014
42713
|
});
|
|
42714
|
+
const e2ee = options.e2eCertificate ? createCliE2eeRuntime(options.e2eCertificate) : void 0;
|
|
42715
|
+
const bridgeAccess = await createBridgeAccessState({
|
|
42716
|
+
cloudApiBaseUrl: apiUrl,
|
|
42717
|
+
getCloudAccessToken: () => tokens.accessToken,
|
|
42718
|
+
e2ee
|
|
42719
|
+
});
|
|
42015
42720
|
const acpManager = await createAcpManager({
|
|
42016
42721
|
log: logFn,
|
|
42722
|
+
getBridgeAccessPort: () => bridgeAccess.getPort(),
|
|
42017
42723
|
reportAgentCapabilities: sendAgentCapabilitiesToBridge
|
|
42018
42724
|
});
|
|
42019
42725
|
logFn("CLI running. Press Ctrl+C to exit.");
|
|
42020
|
-
const e2ee = options.e2eCertificate ? createCliE2eeRuntime(options.e2eCertificate) : void 0;
|
|
42021
42726
|
const devServerManager = new DevServerManager({ getWs, log: logFn, getBridgeRoot, e2ee });
|
|
42022
42727
|
const bridgeHeartbeat = createBridgeHeartbeatController({ getWs, log: logFn });
|
|
42023
42728
|
const baseOnBridgeIdentified = createOnBridgeIdentified({
|
|
@@ -42057,8 +42762,8 @@ async function createBridgeConnection(options) {
|
|
|
42057
42762
|
getCloudAccessToken: () => tokens.accessToken
|
|
42058
42763
|
};
|
|
42059
42764
|
const identifyReportedPaths = {
|
|
42060
|
-
bridgeRootPath:
|
|
42061
|
-
worktreesRootPath:
|
|
42765
|
+
bridgeRootPath: path46.resolve(getBridgeRoot()),
|
|
42766
|
+
worktreesRootPath: path46.resolve(worktreesRootPath)
|
|
42062
42767
|
};
|
|
42063
42768
|
const { connect } = createMainBridgeWebSocketLifecycle({
|
|
42064
42769
|
state,
|
|
@@ -42083,6 +42788,7 @@ async function createBridgeConnection(options) {
|
|
|
42083
42788
|
stopFileIndexWatcher();
|
|
42084
42789
|
bridgeHeartbeat.stop();
|
|
42085
42790
|
await closeBridgeConnection(state, acpManager, devServerManager, logFn);
|
|
42791
|
+
await bridgeAccess.close();
|
|
42086
42792
|
}
|
|
42087
42793
|
};
|
|
42088
42794
|
}
|
|
@@ -42312,9 +43018,9 @@ async function runCliAction(program2, opts) {
|
|
|
42312
43018
|
const firehoseServerUrl = opts.firehoseUrl ?? opts.proxyUrl ?? process.env.BUILDAUTOMATON_FIREHOSE_URL ?? process.env.BUILDAUTOMATON_PROXY_URL ?? DEFAULT_FIREHOSE_URL;
|
|
42313
43019
|
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);
|
|
42314
43020
|
if (bridgeRootOpt) {
|
|
42315
|
-
const resolvedBridgeRoot =
|
|
43021
|
+
const resolvedBridgeRoot = path47.resolve(process.cwd(), bridgeRootOpt);
|
|
42316
43022
|
try {
|
|
42317
|
-
const st =
|
|
43023
|
+
const st = fs41.statSync(resolvedBridgeRoot);
|
|
42318
43024
|
if (!st.isDirectory()) {
|
|
42319
43025
|
console.error(`Bridge root is not a directory: ${resolvedBridgeRoot}`);
|
|
42320
43026
|
process.exit(1);
|
|
@@ -42334,7 +43040,7 @@ async function runCliAction(program2, opts) {
|
|
|
42334
43040
|
);
|
|
42335
43041
|
let worktreesRootPath;
|
|
42336
43042
|
if (opts.worktreesRoot && opts.worktreesRoot.trim()) {
|
|
42337
|
-
worktreesRootPath =
|
|
43043
|
+
worktreesRootPath = path47.resolve(opts.worktreesRoot.trim());
|
|
42338
43044
|
}
|
|
42339
43045
|
const e2eCertificates = opts.e2eeCertificatesDir?.trim() ? await loadOrCreateE2eCertificates(opts.e2eeCertificatesDir.trim()) : void 0;
|
|
42340
43046
|
if (e2eCertificates) {
|