@buildautomaton/cli 0.1.46 → 0.1.48
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +979 -515
- package/dist/cli.js.map +4 -4
- package/dist/index.js +958 -494
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4175,8 +4175,8 @@ var init_parseUtil = __esm({
|
|
|
4175
4175
|
init_errors();
|
|
4176
4176
|
init_en();
|
|
4177
4177
|
makeIssue = (params) => {
|
|
4178
|
-
const { data, path:
|
|
4179
|
-
const fullPath = [...
|
|
4178
|
+
const { data, path: path46, errorMaps, issueData } = params;
|
|
4179
|
+
const fullPath = [...path46, ...issueData.path || []];
|
|
4180
4180
|
const fullIssue = {
|
|
4181
4181
|
...issueData,
|
|
4182
4182
|
path: fullPath
|
|
@@ -4484,11 +4484,11 @@ var init_types = __esm({
|
|
|
4484
4484
|
init_parseUtil();
|
|
4485
4485
|
init_util();
|
|
4486
4486
|
ParseInputLazyPath = class {
|
|
4487
|
-
constructor(parent, value,
|
|
4487
|
+
constructor(parent, value, path46, key) {
|
|
4488
4488
|
this._cachedPath = [];
|
|
4489
4489
|
this.parent = parent;
|
|
4490
4490
|
this.data = value;
|
|
4491
|
-
this._path =
|
|
4491
|
+
this._path = path46;
|
|
4492
4492
|
this._key = key;
|
|
4493
4493
|
}
|
|
4494
4494
|
get path() {
|
|
@@ -8103,15 +8103,15 @@ function assignProp(target, prop, value) {
|
|
|
8103
8103
|
configurable: true
|
|
8104
8104
|
});
|
|
8105
8105
|
}
|
|
8106
|
-
function getElementAtPath(obj,
|
|
8107
|
-
if (!
|
|
8106
|
+
function getElementAtPath(obj, path46) {
|
|
8107
|
+
if (!path46)
|
|
8108
8108
|
return obj;
|
|
8109
|
-
return
|
|
8109
|
+
return path46.reduce((acc, key) => acc?.[key], obj);
|
|
8110
8110
|
}
|
|
8111
8111
|
function promiseAllObject(promisesObj) {
|
|
8112
8112
|
const keys = Object.keys(promisesObj);
|
|
8113
|
-
const
|
|
8114
|
-
return Promise.all(
|
|
8113
|
+
const promises6 = keys.map((key) => promisesObj[key]);
|
|
8114
|
+
return Promise.all(promises6).then((results) => {
|
|
8115
8115
|
const resolvedObj = {};
|
|
8116
8116
|
for (let i = 0; i < keys.length; i++) {
|
|
8117
8117
|
resolvedObj[keys[i]] = results[i];
|
|
@@ -8355,11 +8355,11 @@ function aborted(x, startIndex = 0) {
|
|
|
8355
8355
|
}
|
|
8356
8356
|
return false;
|
|
8357
8357
|
}
|
|
8358
|
-
function prefixIssues(
|
|
8358
|
+
function prefixIssues(path46, issues) {
|
|
8359
8359
|
return issues.map((iss) => {
|
|
8360
8360
|
var _a2;
|
|
8361
8361
|
(_a2 = iss).path ?? (_a2.path = []);
|
|
8362
|
-
iss.path.unshift(
|
|
8362
|
+
iss.path.unshift(path46);
|
|
8363
8363
|
return iss;
|
|
8364
8364
|
});
|
|
8365
8365
|
}
|
|
@@ -8548,7 +8548,7 @@ function treeifyError(error40, _mapper) {
|
|
|
8548
8548
|
return issue2.message;
|
|
8549
8549
|
};
|
|
8550
8550
|
const result = { errors: [] };
|
|
8551
|
-
const processError = (error41,
|
|
8551
|
+
const processError = (error41, path46 = []) => {
|
|
8552
8552
|
var _a2, _b;
|
|
8553
8553
|
for (const issue2 of error41.issues) {
|
|
8554
8554
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
@@ -8558,7 +8558,7 @@ function treeifyError(error40, _mapper) {
|
|
|
8558
8558
|
} else if (issue2.code === "invalid_element") {
|
|
8559
8559
|
processError({ issues: issue2.issues }, issue2.path);
|
|
8560
8560
|
} else {
|
|
8561
|
-
const fullpath = [...
|
|
8561
|
+
const fullpath = [...path46, ...issue2.path];
|
|
8562
8562
|
if (fullpath.length === 0) {
|
|
8563
8563
|
result.errors.push(mapper(issue2));
|
|
8564
8564
|
continue;
|
|
@@ -8588,9 +8588,9 @@ function treeifyError(error40, _mapper) {
|
|
|
8588
8588
|
processError(error40);
|
|
8589
8589
|
return result;
|
|
8590
8590
|
}
|
|
8591
|
-
function toDotPath(
|
|
8591
|
+
function toDotPath(path46) {
|
|
8592
8592
|
const segs = [];
|
|
8593
|
-
for (const seg of
|
|
8593
|
+
for (const seg of path46) {
|
|
8594
8594
|
if (typeof seg === "number")
|
|
8595
8595
|
segs.push(`[${seg}]`);
|
|
8596
8596
|
else if (typeof seg === "symbol")
|
|
@@ -22073,10 +22073,10 @@ var require_src2 = __commonJS({
|
|
|
22073
22073
|
var fs_1 = __require("fs");
|
|
22074
22074
|
var debug_1 = __importDefault(require_src());
|
|
22075
22075
|
var log2 = debug_1.default("@kwsites/file-exists");
|
|
22076
|
-
function check2(
|
|
22077
|
-
log2(`checking %s`,
|
|
22076
|
+
function check2(path46, isFile, isDirectory) {
|
|
22077
|
+
log2(`checking %s`, path46);
|
|
22078
22078
|
try {
|
|
22079
|
-
const stat2 = fs_1.statSync(
|
|
22079
|
+
const stat2 = fs_1.statSync(path46);
|
|
22080
22080
|
if (stat2.isFile() && isFile) {
|
|
22081
22081
|
log2(`[OK] path represents a file`);
|
|
22082
22082
|
return true;
|
|
@@ -22096,8 +22096,8 @@ var require_src2 = __commonJS({
|
|
|
22096
22096
|
throw e;
|
|
22097
22097
|
}
|
|
22098
22098
|
}
|
|
22099
|
-
function exists2(
|
|
22100
|
-
return check2(
|
|
22099
|
+
function exists2(path46, type = exports.READABLE) {
|
|
22100
|
+
return check2(path46, (type & exports.FILE) > 0, (type & exports.FOLDER) > 0);
|
|
22101
22101
|
}
|
|
22102
22102
|
exports.exists = exists2;
|
|
22103
22103
|
exports.FILE = 1;
|
|
@@ -22940,9 +22940,9 @@ function parseChangeSummaryJson(raw, allowedPaths, options) {
|
|
|
22940
22940
|
const rawPath = typeof o.path === "string" ? o.path.trim() : "";
|
|
22941
22941
|
const summary = typeof o.summary === "string" ? o.summary.trim() : "";
|
|
22942
22942
|
if (!rawPath || !summary) continue;
|
|
22943
|
-
const
|
|
22944
|
-
if (!
|
|
22945
|
-
rows.push({ path:
|
|
22943
|
+
const path46 = skip ? normalizeRepoRelativePath(rawPath) || rawPath : resolveChangeSummaryPathAgainstAllowed(rawPath, allowedPaths);
|
|
22944
|
+
if (!path46) continue;
|
|
22945
|
+
rows.push({ path: path46, summary: clampSummaryToAtMostTwoLines(summary) });
|
|
22946
22946
|
}
|
|
22947
22947
|
return rows;
|
|
22948
22948
|
}
|
|
@@ -24241,8 +24241,8 @@ function randomSecret() {
|
|
|
24241
24241
|
}
|
|
24242
24242
|
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
24243
24243
|
}
|
|
24244
|
-
async function requestPreviewApi(port, secret, method,
|
|
24245
|
-
const url2 = `http://127.0.0.1:${port}${
|
|
24244
|
+
async function requestPreviewApi(port, secret, method, path46, body) {
|
|
24245
|
+
const url2 = `http://127.0.0.1:${port}${path46}`;
|
|
24246
24246
|
const headers = {
|
|
24247
24247
|
[PREVIEW_SECRET_HEADER]: secret,
|
|
24248
24248
|
"Content-Type": "application/json"
|
|
@@ -24254,7 +24254,7 @@ async function requestPreviewApi(port, secret, method, path44, body) {
|
|
|
24254
24254
|
});
|
|
24255
24255
|
const data = await res.json().catch(() => ({}));
|
|
24256
24256
|
if (!res.ok) {
|
|
24257
|
-
throw new Error(data?.error ?? `Preview API ${method} ${
|
|
24257
|
+
throw new Error(data?.error ?? `Preview API ${method} ${path46}: ${res.status}`);
|
|
24258
24258
|
}
|
|
24259
24259
|
return data;
|
|
24260
24260
|
}
|
|
@@ -24469,7 +24469,7 @@ function installBridgeProcessResilience() {
|
|
|
24469
24469
|
}
|
|
24470
24470
|
|
|
24471
24471
|
// src/cli-version.ts
|
|
24472
|
-
var CLI_VERSION = "0.1.
|
|
24472
|
+
var CLI_VERSION = "0.1.48".length > 0 ? "0.1.48" : "0.0.0-dev";
|
|
24473
24473
|
|
|
24474
24474
|
// src/connection/heartbeat/constants.ts
|
|
24475
24475
|
var BRIDGE_APP_HEARTBEAT_INTERVAL_MS = 1e4;
|
|
@@ -27995,8 +27995,8 @@ function pathspec(...paths) {
|
|
|
27995
27995
|
cache.set(key, paths);
|
|
27996
27996
|
return key;
|
|
27997
27997
|
}
|
|
27998
|
-
function isPathSpec(
|
|
27999
|
-
return
|
|
27998
|
+
function isPathSpec(path46) {
|
|
27999
|
+
return path46 instanceof String && cache.has(path46);
|
|
28000
28000
|
}
|
|
28001
28001
|
function toPaths(pathSpec) {
|
|
28002
28002
|
return cache.get(pathSpec) || [];
|
|
@@ -28085,8 +28085,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
|
|
|
28085
28085
|
function forEachLineWithContent(input, callback) {
|
|
28086
28086
|
return toLinesWithContent(input, true).map((line) => callback(line));
|
|
28087
28087
|
}
|
|
28088
|
-
function folderExists(
|
|
28089
|
-
return (0, import_file_exists.exists)(
|
|
28088
|
+
function folderExists(path46) {
|
|
28089
|
+
return (0, import_file_exists.exists)(path46, import_file_exists.FOLDER);
|
|
28090
28090
|
}
|
|
28091
28091
|
function append(target, item) {
|
|
28092
28092
|
if (Array.isArray(target)) {
|
|
@@ -28490,8 +28490,8 @@ function checkIsRepoRootTask() {
|
|
|
28490
28490
|
commands,
|
|
28491
28491
|
format: "utf-8",
|
|
28492
28492
|
onError,
|
|
28493
|
-
parser(
|
|
28494
|
-
return /^\.(git)?$/.test(
|
|
28493
|
+
parser(path46) {
|
|
28494
|
+
return /^\.(git)?$/.test(path46.trim());
|
|
28495
28495
|
}
|
|
28496
28496
|
};
|
|
28497
28497
|
}
|
|
@@ -28925,11 +28925,11 @@ function parseGrep(grep) {
|
|
|
28925
28925
|
const paths = /* @__PURE__ */ new Set();
|
|
28926
28926
|
const results = {};
|
|
28927
28927
|
forEachLineWithContent(grep, (input) => {
|
|
28928
|
-
const [
|
|
28929
|
-
paths.add(
|
|
28930
|
-
(results[
|
|
28928
|
+
const [path46, line, preview] = input.split(NULL);
|
|
28929
|
+
paths.add(path46);
|
|
28930
|
+
(results[path46] = results[path46] || []).push({
|
|
28931
28931
|
line: asNumber(line),
|
|
28932
|
-
path:
|
|
28932
|
+
path: path46,
|
|
28933
28933
|
preview
|
|
28934
28934
|
});
|
|
28935
28935
|
});
|
|
@@ -29694,14 +29694,14 @@ var init_hash_object = __esm2({
|
|
|
29694
29694
|
init_task();
|
|
29695
29695
|
}
|
|
29696
29696
|
});
|
|
29697
|
-
function parseInit(bare,
|
|
29697
|
+
function parseInit(bare, path46, text) {
|
|
29698
29698
|
const response = String(text).trim();
|
|
29699
29699
|
let result;
|
|
29700
29700
|
if (result = initResponseRegex.exec(response)) {
|
|
29701
|
-
return new InitSummary(bare,
|
|
29701
|
+
return new InitSummary(bare, path46, false, result[1]);
|
|
29702
29702
|
}
|
|
29703
29703
|
if (result = reInitResponseRegex.exec(response)) {
|
|
29704
|
-
return new InitSummary(bare,
|
|
29704
|
+
return new InitSummary(bare, path46, true, result[1]);
|
|
29705
29705
|
}
|
|
29706
29706
|
let gitDir = "";
|
|
29707
29707
|
const tokens = response.split(" ");
|
|
@@ -29712,7 +29712,7 @@ function parseInit(bare, path44, text) {
|
|
|
29712
29712
|
break;
|
|
29713
29713
|
}
|
|
29714
29714
|
}
|
|
29715
|
-
return new InitSummary(bare,
|
|
29715
|
+
return new InitSummary(bare, path46, /^re/i.test(response), gitDir);
|
|
29716
29716
|
}
|
|
29717
29717
|
var InitSummary;
|
|
29718
29718
|
var initResponseRegex;
|
|
@@ -29721,9 +29721,9 @@ var init_InitSummary = __esm2({
|
|
|
29721
29721
|
"src/lib/responses/InitSummary.ts"() {
|
|
29722
29722
|
"use strict";
|
|
29723
29723
|
InitSummary = class {
|
|
29724
|
-
constructor(bare,
|
|
29724
|
+
constructor(bare, path46, existing, gitDir) {
|
|
29725
29725
|
this.bare = bare;
|
|
29726
|
-
this.path =
|
|
29726
|
+
this.path = path46;
|
|
29727
29727
|
this.existing = existing;
|
|
29728
29728
|
this.gitDir = gitDir;
|
|
29729
29729
|
}
|
|
@@ -29735,7 +29735,7 @@ var init_InitSummary = __esm2({
|
|
|
29735
29735
|
function hasBareCommand(command) {
|
|
29736
29736
|
return command.includes(bareCommand);
|
|
29737
29737
|
}
|
|
29738
|
-
function initTask(bare = false,
|
|
29738
|
+
function initTask(bare = false, path46, customArgs) {
|
|
29739
29739
|
const commands = ["init", ...customArgs];
|
|
29740
29740
|
if (bare && !hasBareCommand(commands)) {
|
|
29741
29741
|
commands.splice(1, 0, bareCommand);
|
|
@@ -29744,7 +29744,7 @@ function initTask(bare = false, path44, customArgs) {
|
|
|
29744
29744
|
commands,
|
|
29745
29745
|
format: "utf-8",
|
|
29746
29746
|
parser(text) {
|
|
29747
|
-
return parseInit(commands.includes("--bare"),
|
|
29747
|
+
return parseInit(commands.includes("--bare"), path46, text);
|
|
29748
29748
|
}
|
|
29749
29749
|
};
|
|
29750
29750
|
}
|
|
@@ -30560,12 +30560,12 @@ var init_FileStatusSummary = __esm2({
|
|
|
30560
30560
|
"use strict";
|
|
30561
30561
|
fromPathRegex = /^(.+)\0(.+)$/;
|
|
30562
30562
|
FileStatusSummary = class {
|
|
30563
|
-
constructor(
|
|
30564
|
-
this.path =
|
|
30563
|
+
constructor(path46, index, working_dir) {
|
|
30564
|
+
this.path = path46;
|
|
30565
30565
|
this.index = index;
|
|
30566
30566
|
this.working_dir = working_dir;
|
|
30567
30567
|
if (index === "R" || working_dir === "R") {
|
|
30568
|
-
const detail = fromPathRegex.exec(
|
|
30568
|
+
const detail = fromPathRegex.exec(path46) || [null, path46, path46];
|
|
30569
30569
|
this.from = detail[2] || "";
|
|
30570
30570
|
this.path = detail[1] || "";
|
|
30571
30571
|
}
|
|
@@ -30596,14 +30596,14 @@ function splitLine(result, lineStr) {
|
|
|
30596
30596
|
default:
|
|
30597
30597
|
return;
|
|
30598
30598
|
}
|
|
30599
|
-
function data(index, workingDir,
|
|
30599
|
+
function data(index, workingDir, path46) {
|
|
30600
30600
|
const raw = `${index}${workingDir}`;
|
|
30601
30601
|
const handler = parsers6.get(raw);
|
|
30602
30602
|
if (handler) {
|
|
30603
|
-
handler(result,
|
|
30603
|
+
handler(result, path46);
|
|
30604
30604
|
}
|
|
30605
30605
|
if (raw !== "##" && raw !== "!!") {
|
|
30606
|
-
result.files.push(new FileStatusSummary(
|
|
30606
|
+
result.files.push(new FileStatusSummary(path46, index, workingDir));
|
|
30607
30607
|
}
|
|
30608
30608
|
}
|
|
30609
30609
|
}
|
|
@@ -30912,9 +30912,9 @@ var init_simple_git_api = __esm2({
|
|
|
30912
30912
|
next
|
|
30913
30913
|
);
|
|
30914
30914
|
}
|
|
30915
|
-
hashObject(
|
|
30915
|
+
hashObject(path46, write) {
|
|
30916
30916
|
return this._runTask(
|
|
30917
|
-
hashObjectTask(
|
|
30917
|
+
hashObjectTask(path46, write === true),
|
|
30918
30918
|
trailingFunctionArgument(arguments)
|
|
30919
30919
|
);
|
|
30920
30920
|
}
|
|
@@ -31267,8 +31267,8 @@ var init_branch = __esm2({
|
|
|
31267
31267
|
}
|
|
31268
31268
|
});
|
|
31269
31269
|
function toPath(input) {
|
|
31270
|
-
const
|
|
31271
|
-
return
|
|
31270
|
+
const path46 = input.trim().replace(/^["']|["']$/g, "");
|
|
31271
|
+
return path46 && normalize3(path46);
|
|
31272
31272
|
}
|
|
31273
31273
|
var parseCheckIgnore;
|
|
31274
31274
|
var init_CheckIgnore = __esm2({
|
|
@@ -31582,8 +31582,8 @@ __export2(sub_module_exports, {
|
|
|
31582
31582
|
subModuleTask: () => subModuleTask,
|
|
31583
31583
|
updateSubModuleTask: () => updateSubModuleTask
|
|
31584
31584
|
});
|
|
31585
|
-
function addSubModuleTask(repo,
|
|
31586
|
-
return subModuleTask(["add", repo,
|
|
31585
|
+
function addSubModuleTask(repo, path46) {
|
|
31586
|
+
return subModuleTask(["add", repo, path46]);
|
|
31587
31587
|
}
|
|
31588
31588
|
function initSubModuleTask(customArgs) {
|
|
31589
31589
|
return subModuleTask(["init", ...customArgs]);
|
|
@@ -31916,8 +31916,8 @@ var require_git = __commonJS2({
|
|
|
31916
31916
|
}
|
|
31917
31917
|
return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
|
|
31918
31918
|
};
|
|
31919
|
-
Git2.prototype.submoduleAdd = function(repo,
|
|
31920
|
-
return this._runTask(addSubModuleTask2(repo,
|
|
31919
|
+
Git2.prototype.submoduleAdd = function(repo, path46, then) {
|
|
31920
|
+
return this._runTask(addSubModuleTask2(repo, path46), trailingFunctionArgument2(arguments));
|
|
31921
31921
|
};
|
|
31922
31922
|
Git2.prototype.submoduleUpdate = function(args, then) {
|
|
31923
31923
|
return this._runTask(
|
|
@@ -32880,9 +32880,9 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
|
|
|
32880
32880
|
// src/agents/acp/put-summarize-change-summaries.ts
|
|
32881
32881
|
async function putEncryptedChangeSummaryRows(params) {
|
|
32882
32882
|
const base = params.apiBaseUrl.replace(/\/+$/, "");
|
|
32883
|
-
const entries = params.rows.map(({ path:
|
|
32883
|
+
const entries = params.rows.map(({ path: path46, summary }) => {
|
|
32884
32884
|
const enc = params.e2ee.encryptFields({ summary }, ["summary"]);
|
|
32885
|
-
return { path:
|
|
32885
|
+
return { path: path46, summary: JSON.stringify(enc) };
|
|
32886
32886
|
});
|
|
32887
32887
|
const res = await fetch(
|
|
32888
32888
|
`${base}/internal/sessions/${encodeURIComponent(params.sessionId)}/follow-ups/summarize-changes`,
|
|
@@ -33121,54 +33121,167 @@ function buildForkedSessionAgentPrompt(userPrompt, childSessionId, parentSession
|
|
|
33121
33121
|
].join("\n");
|
|
33122
33122
|
}
|
|
33123
33123
|
|
|
33124
|
-
// src/mcp/tools/session-history/
|
|
33124
|
+
// src/mcp/tools/session-history/cloud/internal-api.ts
|
|
33125
33125
|
function internalApiBase(cloudApiBaseUrl) {
|
|
33126
33126
|
return cloudApiBaseUrl.replace(/\/+$/, "");
|
|
33127
33127
|
}
|
|
33128
|
-
async function
|
|
33128
|
+
async function fetchInternalApiJson(params) {
|
|
33129
33129
|
const token = params.getCloudAccessToken();
|
|
33130
33130
|
if (!token) return { ok: false, error: "Missing cloud access token." };
|
|
33131
|
-
const url2 = `${internalApiBase(params.cloudApiBaseUrl)}
|
|
33131
|
+
const url2 = `${internalApiBase(params.cloudApiBaseUrl)}${params.path}`;
|
|
33132
33132
|
const res = await fetch(url2, { headers: { Authorization: `Bearer ${token}` } });
|
|
33133
33133
|
if (!res.ok) {
|
|
33134
33134
|
const t = await res.text().catch(() => "");
|
|
33135
|
-
return { ok: false, error:
|
|
33135
|
+
return { ok: false, error: `${params.errorLabel} (${res.status}): ${t.slice(0, 200)}` };
|
|
33136
33136
|
}
|
|
33137
33137
|
const data = await res.json().catch(() => null);
|
|
33138
|
-
if (!data) return { ok: false, error:
|
|
33138
|
+
if (!data) return { ok: false, error: `Invalid ${params.errorLabel.toLowerCase()} response.` };
|
|
33139
33139
|
return { ok: true, data };
|
|
33140
33140
|
}
|
|
33141
|
-
|
|
33142
|
-
|
|
33143
|
-
|
|
33144
|
-
|
|
33145
|
-
|
|
33146
|
-
|
|
33147
|
-
|
|
33148
|
-
|
|
33141
|
+
|
|
33142
|
+
// src/mcp/tools/session-history/cloud/fetch-session-meta.ts
|
|
33143
|
+
async function fetchCloudSessionMeta(params) {
|
|
33144
|
+
return fetchInternalApiJson({
|
|
33145
|
+
...params,
|
|
33146
|
+
path: `/internal/sessions/${encodeURIComponent(params.sessionId)}/meta`,
|
|
33147
|
+
errorLabel: "Session meta fetch failed"
|
|
33148
|
+
});
|
|
33149
|
+
}
|
|
33150
|
+
|
|
33151
|
+
// src/lib/e2ee/decrypt-stored-e2ee-content.ts
|
|
33152
|
+
function parseJsonObject(raw) {
|
|
33153
|
+
try {
|
|
33154
|
+
const parsed = JSON.parse(raw);
|
|
33155
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
33156
|
+
return parsed;
|
|
33157
|
+
}
|
|
33158
|
+
} catch {
|
|
33159
|
+
return null;
|
|
33149
33160
|
}
|
|
33150
|
-
|
|
33151
|
-
if (!data) return { ok: false, error: "Invalid parent prompts response." };
|
|
33152
|
-
return { ok: true, data };
|
|
33161
|
+
return null;
|
|
33153
33162
|
}
|
|
33154
|
-
|
|
33155
|
-
const
|
|
33156
|
-
|
|
33157
|
-
|
|
33158
|
-
|
|
33159
|
-
|
|
33160
|
-
|
|
33161
|
-
|
|
33163
|
+
function storedStringHasE2eeEnvelope(raw) {
|
|
33164
|
+
const record2 = parseJsonObject(raw);
|
|
33165
|
+
return record2 != null && isE2eeEnvelope(record2.ee);
|
|
33166
|
+
}
|
|
33167
|
+
function e2eeKeyIdFromStoredString(raw) {
|
|
33168
|
+
const record2 = parseJsonObject(raw);
|
|
33169
|
+
if (record2 != null && isE2eeEnvelope(record2.ee)) return record2.ee.k;
|
|
33170
|
+
return null;
|
|
33171
|
+
}
|
|
33172
|
+
function decryptStoredE2eeRecord(record2, e2ee) {
|
|
33173
|
+
if (!isE2eeEnvelope(record2.ee)) return record2;
|
|
33174
|
+
if (!e2ee) {
|
|
33175
|
+
throw new Error(`E2EE_REQUIRED:${record2.ee.k}`);
|
|
33176
|
+
}
|
|
33177
|
+
if (record2.ee.k !== e2ee.keyId) {
|
|
33178
|
+
throw new Error(`E2EE_KEY_MISMATCH:encrypted=${record2.ee.k}:cli=${e2ee.keyId}`);
|
|
33179
|
+
}
|
|
33180
|
+
return e2ee.decryptMessage(record2);
|
|
33181
|
+
}
|
|
33182
|
+
function formatCliE2eeDecryptError(error40, e2ee) {
|
|
33183
|
+
const message = error40 instanceof Error ? error40.message : String(error40);
|
|
33184
|
+
const required2 = /^E2EE_REQUIRED:(.+)$/.exec(message.trim());
|
|
33185
|
+
if (required2) {
|
|
33186
|
+
const keyId = required2[1]?.trim() || "unknown";
|
|
33187
|
+
return `Parent session content is encrypted (key ${keyId}) but this bridge was not started with E2EE certificates. Restart the CLI with your E2EE certificate to read fork parent history.`;
|
|
33188
|
+
}
|
|
33189
|
+
const mismatch = /^E2EE_KEY_MISMATCH:encrypted=(.+):cli=(.*)$/.exec(message.trim());
|
|
33190
|
+
if (mismatch) {
|
|
33191
|
+
const encryptedKeyId = mismatch[1]?.trim() || "unknown";
|
|
33192
|
+
const cliKeyId = mismatch[2]?.trim() || e2ee?.keyId || "none";
|
|
33193
|
+
return `Parent session content is encrypted with key ${encryptedKeyId}, but this bridge certificate key is ${cliKeyId}. Use the same E2EE certificate that was used for the parent session.`;
|
|
33194
|
+
}
|
|
33195
|
+
if (message.includes("E2EE key mismatch")) {
|
|
33196
|
+
return `Parent session content could not be decrypted: ${message}`;
|
|
33197
|
+
}
|
|
33198
|
+
return `Parent session content could not be decrypted: ${message}`;
|
|
33199
|
+
}
|
|
33200
|
+
function decryptCliE2eeTranscriptPayload(payload, e2ee) {
|
|
33201
|
+
const record2 = parseJsonObject(payload);
|
|
33202
|
+
if (!record2 || !isE2eeEnvelope(record2.ee)) return payload;
|
|
33203
|
+
const decrypted = decryptStoredE2eeRecord(record2, e2ee);
|
|
33204
|
+
if (typeof decrypted.payload === "string") return decrypted.payload;
|
|
33205
|
+
return JSON.stringify(decrypted.payload ?? decrypted);
|
|
33206
|
+
}
|
|
33207
|
+
function decryptCliE2eeMessageField(payload, field, e2ee) {
|
|
33208
|
+
if (payload == null) return null;
|
|
33209
|
+
const record2 = parseJsonObject(payload);
|
|
33210
|
+
if (!record2 || !isE2eeEnvelope(record2.ee)) return payload;
|
|
33211
|
+
const decrypted = decryptStoredE2eeRecord(record2, e2ee);
|
|
33212
|
+
const value = decrypted[field];
|
|
33213
|
+
if (value == null) return null;
|
|
33214
|
+
return typeof value === "string" ? value : JSON.stringify(value);
|
|
33215
|
+
}
|
|
33216
|
+
|
|
33217
|
+
// src/mcp/tools/session-history/cloud/decrypt-parent-prompt-turns.ts
|
|
33218
|
+
function assertE2eeRuntimeForEnvelope(raw, e2ee) {
|
|
33219
|
+
if (storedStringHasE2eeEnvelope(raw) && !e2ee) {
|
|
33220
|
+
throw new Error(`E2EE_REQUIRED:${e2eeKeyIdFromStoredString(raw) ?? "unknown"}`);
|
|
33221
|
+
}
|
|
33222
|
+
}
|
|
33223
|
+
function decryptParentPromptTurns(turns, e2ee) {
|
|
33224
|
+
return turns.map((turn) => {
|
|
33225
|
+
const promptRaw = typeof turn.promptText === "string" ? turn.promptText : null;
|
|
33226
|
+
const titleRaw = typeof turn.title === "string" ? turn.title : null;
|
|
33227
|
+
if (promptRaw) assertE2eeRuntimeForEnvelope(promptRaw, e2ee);
|
|
33228
|
+
if (titleRaw) assertE2eeRuntimeForEnvelope(titleRaw, e2ee);
|
|
33229
|
+
return {
|
|
33230
|
+
...turn,
|
|
33231
|
+
promptText: decryptCliE2eeMessageField(promptRaw, "prompt", e2ee),
|
|
33232
|
+
title: decryptCliE2eeMessageField(titleRaw, "title", e2ee)
|
|
33233
|
+
};
|
|
33234
|
+
});
|
|
33235
|
+
}
|
|
33236
|
+
|
|
33237
|
+
// src/mcp/tools/session-history/cloud/fetch-parent-prompts.ts
|
|
33238
|
+
async function fetchCloudParentSessionPrompts(params) {
|
|
33239
|
+
const fetched = await fetchInternalApiJson({
|
|
33240
|
+
...params,
|
|
33241
|
+
path: `/internal/sessions/${encodeURIComponent(params.childSessionId)}/parent/prompts`,
|
|
33242
|
+
errorLabel: "Parent prompts fetch failed"
|
|
33243
|
+
});
|
|
33244
|
+
if (!fetched.ok) return fetched;
|
|
33245
|
+
const turns = Array.isArray(fetched.data.turns) ? fetched.data.turns : [];
|
|
33246
|
+
try {
|
|
33247
|
+
const decryptedTurns = decryptParentPromptTurns(turns, params.e2ee);
|
|
33248
|
+
return { ok: true, data: { ...fetched.data, turns: decryptedTurns } };
|
|
33249
|
+
} catch (error40) {
|
|
33250
|
+
return { ok: false, error: formatCliE2eeDecryptError(error40, params.e2ee) };
|
|
33162
33251
|
}
|
|
33163
|
-
|
|
33164
|
-
|
|
33252
|
+
}
|
|
33253
|
+
|
|
33254
|
+
// src/mcp/tools/session-history/cloud/format-parent-transcript-text.ts
|
|
33255
|
+
function formatParentTranscriptText(rows, e2ee) {
|
|
33165
33256
|
const lines = rows.map((row) => {
|
|
33166
33257
|
const kind = typeof row.kind === "string" ? row.kind : "event";
|
|
33167
|
-
const
|
|
33258
|
+
const rawPayload = typeof row.payload === "string" ? row.payload : JSON.stringify(row.payload ?? "");
|
|
33259
|
+
if (storedStringHasE2eeEnvelope(rawPayload) && !e2ee) {
|
|
33260
|
+
throw new Error(`E2EE_REQUIRED:${e2eeKeyIdFromStoredString(rawPayload) ?? "unknown"}`);
|
|
33261
|
+
}
|
|
33262
|
+
const payload = decryptCliE2eeTranscriptPayload(rawPayload, e2ee);
|
|
33168
33263
|
return `[${kind}] ${payload}`;
|
|
33169
33264
|
});
|
|
33170
|
-
return
|
|
33265
|
+
return lines.join("\n");
|
|
33266
|
+
}
|
|
33267
|
+
|
|
33268
|
+
// src/mcp/tools/session-history/cloud/fetch-parent-transcript.ts
|
|
33269
|
+
async function fetchCloudParentTurnTranscript(params) {
|
|
33270
|
+
const fetched = await fetchInternalApiJson({
|
|
33271
|
+
...params,
|
|
33272
|
+
path: `/internal/sessions/${encodeURIComponent(params.childSessionId)}/parent/turns/${encodeURIComponent(params.turnId)}/transcript`,
|
|
33273
|
+
errorLabel: "Transcript fetch failed"
|
|
33274
|
+
});
|
|
33275
|
+
if (!fetched.ok) return fetched;
|
|
33276
|
+
const rows = Array.isArray(fetched.data.transcript) ? fetched.data.transcript : [];
|
|
33277
|
+
try {
|
|
33278
|
+
return { ok: true, text: formatParentTranscriptText(rows, params.e2ee) };
|
|
33279
|
+
} catch (error40) {
|
|
33280
|
+
return { ok: false, error: formatCliE2eeDecryptError(error40, params.e2ee) };
|
|
33281
|
+
}
|
|
33171
33282
|
}
|
|
33283
|
+
|
|
33284
|
+
// src/mcp/tools/session-history/cloud/resolve-parent-session-id.ts
|
|
33172
33285
|
async function resolveParentSessionIdForChild(params) {
|
|
33173
33286
|
const meta = await fetchCloudSessionMeta({
|
|
33174
33287
|
sessionId: params.childSessionId,
|
|
@@ -33502,7 +33615,7 @@ async function createAcpManager(options) {
|
|
|
33502
33615
|
// src/git/changes/types.ts
|
|
33503
33616
|
var MAX_PATCH_CHARS = 35e4;
|
|
33504
33617
|
|
|
33505
|
-
// src/git/changes/
|
|
33618
|
+
// src/git/changes/paths/repo-format.ts
|
|
33506
33619
|
function posixJoinDirFile(dir, file2) {
|
|
33507
33620
|
const d = dir === "." || dir === "" ? "" : dir.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
33508
33621
|
const f = file2.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
@@ -33557,7 +33670,7 @@ function formatRemoteDisplayLabel(remoteUrl) {
|
|
|
33557
33670
|
}
|
|
33558
33671
|
|
|
33559
33672
|
// src/git/changes/get-working-tree-change-repo-details.ts
|
|
33560
|
-
import * as
|
|
33673
|
+
import * as path22 from "node:path";
|
|
33561
33674
|
|
|
33562
33675
|
// src/git/commits/resolve-remote-tracking.ts
|
|
33563
33676
|
async function tryConfigGet(g, key) {
|
|
@@ -33727,9 +33840,232 @@ async function listRecentCommits(repoDir, limitInput) {
|
|
|
33727
33840
|
}
|
|
33728
33841
|
}
|
|
33729
33842
|
|
|
33730
|
-
// src/git/changes/
|
|
33731
|
-
function
|
|
33732
|
-
|
|
33843
|
+
// src/git/changes/parse/normalize-git-diff-path.ts
|
|
33844
|
+
function normalizeGitDiffPath(path46) {
|
|
33845
|
+
return path46.replace(/\\/g, "/").replace(/\/ +/g, "/").trim();
|
|
33846
|
+
}
|
|
33847
|
+
|
|
33848
|
+
// src/git/changes/parse/expand-git-rename-numstat-path.ts
|
|
33849
|
+
function expandGitRenameNumstatPath(rawPath) {
|
|
33850
|
+
const open2 = rawPath.indexOf("{");
|
|
33851
|
+
const arrow = rawPath.indexOf("=>");
|
|
33852
|
+
const close = rawPath.lastIndexOf("}");
|
|
33853
|
+
if (open2 === -1 || arrow === -1 || close === -1 || open2 > arrow || arrow > close) {
|
|
33854
|
+
return null;
|
|
33855
|
+
}
|
|
33856
|
+
const prefix = rawPath.slice(0, open2);
|
|
33857
|
+
const suffix = rawPath.slice(close + 1);
|
|
33858
|
+
const oldPart = rawPath.slice(open2 + 1, arrow).trim();
|
|
33859
|
+
const newPart = rawPath.slice(arrow + 2, close).trim();
|
|
33860
|
+
return {
|
|
33861
|
+
oldPath: normalizeGitDiffPath(`${prefix}${oldPart}${suffix}`),
|
|
33862
|
+
newPath: normalizeGitDiffPath(`${prefix}${newPart}${suffix}`)
|
|
33863
|
+
};
|
|
33864
|
+
}
|
|
33865
|
+
function isGitRenameNumstatPath(rawPath) {
|
|
33866
|
+
return expandGitRenameNumstatPath(rawPath) != null;
|
|
33867
|
+
}
|
|
33868
|
+
|
|
33869
|
+
// src/git/changes/rows/build-changed-file-row.ts
|
|
33870
|
+
function buildChangedFileRow(options) {
|
|
33871
|
+
const pathInRepo = normalizeGitDiffPath(options.pathInRepo);
|
|
33872
|
+
const relLauncher = posixJoinDirFile(options.repoRelPath, pathInRepo);
|
|
33873
|
+
const additions = options.numEntry?.additions ?? 0;
|
|
33874
|
+
const deletions = options.numEntry?.deletions ?? 0;
|
|
33875
|
+
let change = options.nameEntry?.change ?? "modified";
|
|
33876
|
+
let movedFromPathRelLauncher;
|
|
33877
|
+
let movedFromPathInRepo;
|
|
33878
|
+
const oldPathInRepo = options.nameEntry?.oldPathInRepo ?? options.numEntry?.oldPathInRepo;
|
|
33879
|
+
if (change === "moved" && oldPathInRepo) {
|
|
33880
|
+
movedFromPathInRepo = normalizeGitDiffPath(oldPathInRepo);
|
|
33881
|
+
movedFromPathRelLauncher = posixJoinDirFile(options.repoRelPath, movedFromPathInRepo);
|
|
33882
|
+
} else if (oldPathInRepo && change === "modified") {
|
|
33883
|
+
change = "moved";
|
|
33884
|
+
movedFromPathInRepo = normalizeGitDiffPath(oldPathInRepo);
|
|
33885
|
+
movedFromPathRelLauncher = posixJoinDirFile(options.repoRelPath, movedFromPathInRepo);
|
|
33886
|
+
}
|
|
33887
|
+
if (options.untracked && !options.nameEntry) {
|
|
33888
|
+
change = "added";
|
|
33889
|
+
} else if (!options.nameEntry && options.numEntry && change !== "moved") {
|
|
33890
|
+
if (additions > 0 && deletions === 0) change = "added";
|
|
33891
|
+
else if (deletions > 0 && additions === 0) change = "removed";
|
|
33892
|
+
else change = "modified";
|
|
33893
|
+
}
|
|
33894
|
+
return {
|
|
33895
|
+
pathRelLauncher: relLauncher,
|
|
33896
|
+
additions,
|
|
33897
|
+
deletions,
|
|
33898
|
+
change,
|
|
33899
|
+
...movedFromPathRelLauncher ? { movedFromPathRelLauncher } : {},
|
|
33900
|
+
...movedFromPathInRepo ? { movedFromPathInRepo } : {}
|
|
33901
|
+
};
|
|
33902
|
+
}
|
|
33903
|
+
|
|
33904
|
+
// src/git/changes/listing/build-changed-file-rows.ts
|
|
33905
|
+
async function buildChangedFileRowsFromPaths(options) {
|
|
33906
|
+
const rows = [];
|
|
33907
|
+
await forEachWithGitYield([...options.paths], async (pathInRepo) => {
|
|
33908
|
+
if (isGitRenameNumstatPath(pathInRepo)) return;
|
|
33909
|
+
if (options.movedSourcePaths.has(pathInRepo)) return;
|
|
33910
|
+
const nameEntry = options.nameByPath.get(pathInRepo);
|
|
33911
|
+
const numEntry = options.numByPath.get(pathInRepo);
|
|
33912
|
+
const row = buildChangedFileRow({
|
|
33913
|
+
pathInRepo,
|
|
33914
|
+
repoRelPath: options.repoRelPath,
|
|
33915
|
+
nameEntry,
|
|
33916
|
+
numEntry,
|
|
33917
|
+
untracked: options.untrackedSet?.has(pathInRepo)
|
|
33918
|
+
});
|
|
33919
|
+
if (options.applyUntrackedStats) {
|
|
33920
|
+
await options.applyUntrackedStats(row, pathInRepo);
|
|
33921
|
+
}
|
|
33922
|
+
rows.push(row);
|
|
33923
|
+
});
|
|
33924
|
+
return rows;
|
|
33925
|
+
}
|
|
33926
|
+
|
|
33927
|
+
// src/git/changes/patch/patch-truncate.ts
|
|
33928
|
+
function truncatePatch(s) {
|
|
33929
|
+
if (s.length <= MAX_PATCH_CHARS) return s;
|
|
33930
|
+
return `${s.slice(0, MAX_PATCH_CHARS)}
|
|
33931
|
+
|
|
33932
|
+
\u2026 (diff truncated)`;
|
|
33933
|
+
}
|
|
33934
|
+
|
|
33935
|
+
// src/git/changes/patch/unified-diff-for-file.ts
|
|
33936
|
+
function patchTextFromGitDiffOutput(raw) {
|
|
33937
|
+
if (raw.trim() === "") return void 0;
|
|
33938
|
+
return normalizePatchContent(raw);
|
|
33939
|
+
}
|
|
33940
|
+
async function unifiedDiffForFile(repoCwd, pathInRepo, change, movedFromPathInRepo) {
|
|
33941
|
+
const g = cliSimpleGit(repoCwd);
|
|
33942
|
+
const renameArgs = ["-M", "--find-renames"];
|
|
33943
|
+
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];
|
|
33944
|
+
const raw = await g.raw([...args]).catch(() => "");
|
|
33945
|
+
const patch = patchTextFromGitDiffOutput(String(raw));
|
|
33946
|
+
return patch ? truncatePatch(patch) : void 0;
|
|
33947
|
+
}
|
|
33948
|
+
async function unifiedDiffForFileInRange(repoCwd, range, pathInRepo, change, movedFromPathInRepo) {
|
|
33949
|
+
const g = cliSimpleGit(repoCwd);
|
|
33950
|
+
const renameArgs = ["-M", "--find-renames"];
|
|
33951
|
+
const args = change === "moved" && movedFromPathInRepo ? ["diff", "--no-color", ...renameArgs, "-U20000", range, "--", movedFromPathInRepo, pathInRepo] : ["diff", "--no-color", ...renameArgs, "-U20000", range, "--", pathInRepo];
|
|
33952
|
+
const raw = await g.raw([...args]).catch(() => "");
|
|
33953
|
+
const patch = patchTextFromGitDiffOutput(String(raw));
|
|
33954
|
+
return patch ? truncatePatch(patch) : void 0;
|
|
33955
|
+
}
|
|
33956
|
+
|
|
33957
|
+
// src/git/changes/total-lines/resolve-changed-file-total-lines.ts
|
|
33958
|
+
import * as fs16 from "node:fs";
|
|
33959
|
+
import * as path19 from "node:path";
|
|
33960
|
+
|
|
33961
|
+
// src/git/changes/lines/count-lines.ts
|
|
33962
|
+
import { createReadStream } from "node:fs";
|
|
33963
|
+
import * as readline2 from "node:readline";
|
|
33964
|
+
function countLinesInText(text) {
|
|
33965
|
+
return splitTextIntoDiffLines(text).length;
|
|
33966
|
+
}
|
|
33967
|
+
async function countTextFileLines(filePath) {
|
|
33968
|
+
let bytes = 0;
|
|
33969
|
+
const maxBytes = 512e3;
|
|
33970
|
+
let lines = 0;
|
|
33971
|
+
const stream = createReadStream(filePath, { encoding: "utf8" });
|
|
33972
|
+
const rl = readline2.createInterface({ input: stream, crlfDelay: Infinity });
|
|
33973
|
+
for await (const _line of rl) {
|
|
33974
|
+
lines += 1;
|
|
33975
|
+
bytes += Buffer.byteLength(String(_line), "utf8") + 1;
|
|
33976
|
+
if (bytes > maxBytes) {
|
|
33977
|
+
rl.close();
|
|
33978
|
+
stream.destroy();
|
|
33979
|
+
return lines;
|
|
33980
|
+
}
|
|
33981
|
+
}
|
|
33982
|
+
return lines;
|
|
33983
|
+
}
|
|
33984
|
+
|
|
33985
|
+
// src/git/changes/total-lines/resolve-changed-file-total-lines.ts
|
|
33986
|
+
var MAX_BYTES = 512e3;
|
|
33987
|
+
async function countLinesInGitRef(repoGitCwd, ref, pathInRepo) {
|
|
33988
|
+
const spec = `${ref}:${pathInRepo}`;
|
|
33989
|
+
const g = cliSimpleGit(repoGitCwd);
|
|
33990
|
+
try {
|
|
33991
|
+
await g.raw(["cat-file", "-e", spec]);
|
|
33992
|
+
} catch {
|
|
33993
|
+
return null;
|
|
33994
|
+
}
|
|
33995
|
+
try {
|
|
33996
|
+
const raw = String(await g.raw(["show", spec]));
|
|
33997
|
+
const capped = raw.length > MAX_BYTES ? raw.slice(0, MAX_BYTES) : raw;
|
|
33998
|
+
return countLinesInText(capped);
|
|
33999
|
+
} catch {
|
|
34000
|
+
return null;
|
|
34001
|
+
}
|
|
34002
|
+
}
|
|
34003
|
+
async function resolveWorkingTreeFileTotalLines(options) {
|
|
34004
|
+
if (options.isBinary) return null;
|
|
34005
|
+
const filePath = path19.join(options.repoGitCwd, options.pathInRepo);
|
|
34006
|
+
if (options.change === "removed") {
|
|
34007
|
+
return countLinesInGitRef(options.repoGitCwd, "HEAD", options.pathInRepo);
|
|
34008
|
+
}
|
|
34009
|
+
if (options.change === "moved" || options.change === "modified") {
|
|
34010
|
+
try {
|
|
34011
|
+
const st = await fs16.promises.stat(filePath);
|
|
34012
|
+
if (!st.isFile()) return null;
|
|
34013
|
+
return await countTextFileLines(filePath);
|
|
34014
|
+
} catch {
|
|
34015
|
+
return null;
|
|
34016
|
+
}
|
|
34017
|
+
}
|
|
34018
|
+
return null;
|
|
34019
|
+
}
|
|
34020
|
+
async function resolveCommitFileTotalLines(options) {
|
|
34021
|
+
if (options.isBinary) return null;
|
|
34022
|
+
if (options.change === "removed") {
|
|
34023
|
+
return countLinesInGitRef(options.repoGitCwd, options.parentSha, options.pathInRepo);
|
|
34024
|
+
}
|
|
34025
|
+
if (options.change === "moved" || options.change === "modified") {
|
|
34026
|
+
return countLinesInGitRef(options.repoGitCwd, options.commitSha, options.pathInRepo);
|
|
34027
|
+
}
|
|
34028
|
+
return null;
|
|
34029
|
+
}
|
|
34030
|
+
|
|
34031
|
+
// src/git/changes/listing/path-in-repo-from-launcher.ts
|
|
34032
|
+
function pathInRepoFromLauncher(pathRelLauncher, repoRelPath) {
|
|
34033
|
+
const normRel = repoRelPath === "." || repoRelPath === "" ? "." : repoRelPath;
|
|
34034
|
+
if (normRel === ".") return pathRelLauncher;
|
|
34035
|
+
if (pathRelLauncher.startsWith(`${normRel}/`)) {
|
|
34036
|
+
return pathRelLauncher.slice(normRel.length + 1);
|
|
34037
|
+
}
|
|
34038
|
+
return pathRelLauncher;
|
|
34039
|
+
}
|
|
34040
|
+
function normalizeRepoRelPath(repoRelPath) {
|
|
34041
|
+
return repoRelPath === "." || repoRelPath === "" ? "." : repoRelPath;
|
|
34042
|
+
}
|
|
34043
|
+
|
|
34044
|
+
// src/git/changes/listing/enrich-commit-file-rows.ts
|
|
34045
|
+
async function enrichCommitFileRows(options) {
|
|
34046
|
+
await forEachWithGitYield(options.rows, async (row) => {
|
|
34047
|
+
const pathInRepo = pathInRepoFromLauncher(row.pathRelLauncher, options.repoRelPath);
|
|
34048
|
+
row.patchContent = await unifiedDiffForFileInRange(
|
|
34049
|
+
options.repoGitCwd,
|
|
34050
|
+
options.range,
|
|
34051
|
+
pathInRepo,
|
|
34052
|
+
row.change,
|
|
34053
|
+
row.movedFromPathInRepo
|
|
34054
|
+
);
|
|
34055
|
+
row.totalLines = await resolveCommitFileTotalLines({
|
|
34056
|
+
repoGitCwd: options.repoGitCwd,
|
|
34057
|
+
pathInRepo,
|
|
34058
|
+
change: row.change,
|
|
34059
|
+
commitSha: options.commitSha,
|
|
34060
|
+
parentSha: options.parentSha,
|
|
34061
|
+
isBinary: options.binaryByPath.has(pathInRepo)
|
|
34062
|
+
});
|
|
34063
|
+
});
|
|
34064
|
+
}
|
|
34065
|
+
|
|
34066
|
+
// src/git/changes/parse/parse-name-status-entries.ts
|
|
34067
|
+
function parseNameStatusEntries(lines) {
|
|
34068
|
+
const entries = [];
|
|
33733
34069
|
for (const line of lines) {
|
|
33734
34070
|
if (!line.trim()) continue;
|
|
33735
34071
|
const tabParts = line.split(" ");
|
|
@@ -33737,58 +34073,143 @@ function parseNameStatusLines(lines) {
|
|
|
33737
34073
|
const status = tabParts[0].trim();
|
|
33738
34074
|
const code = status[0];
|
|
33739
34075
|
if (code === "A") {
|
|
33740
|
-
|
|
34076
|
+
entries.push({ pathInRepo: normalizeGitDiffPath(tabParts[tabParts.length - 1]), change: "added" });
|
|
33741
34077
|
} else if (code === "D") {
|
|
33742
|
-
|
|
34078
|
+
entries.push({ pathInRepo: normalizeGitDiffPath(tabParts[tabParts.length - 1]), change: "removed" });
|
|
33743
34079
|
} else if (code === "R" || code === "C") {
|
|
33744
|
-
if (tabParts.length >= 3)
|
|
34080
|
+
if (tabParts.length >= 3) {
|
|
34081
|
+
entries.push({
|
|
34082
|
+
pathInRepo: normalizeGitDiffPath(tabParts[tabParts.length - 1]),
|
|
34083
|
+
oldPathInRepo: normalizeGitDiffPath(tabParts[tabParts.length - 2]),
|
|
34084
|
+
change: "moved"
|
|
34085
|
+
});
|
|
34086
|
+
}
|
|
33745
34087
|
} else if (code === "M" || code === "U" || code === "T") {
|
|
33746
|
-
|
|
34088
|
+
entries.push({ pathInRepo: normalizeGitDiffPath(tabParts[tabParts.length - 1]), change: "modified" });
|
|
33747
34089
|
}
|
|
33748
34090
|
}
|
|
34091
|
+
return entries;
|
|
34092
|
+
}
|
|
34093
|
+
function parseNameStatusLines(lines) {
|
|
34094
|
+
const m = /* @__PURE__ */ new Map();
|
|
34095
|
+
for (const entry of parseNameStatusEntries(lines)) {
|
|
34096
|
+
m.set(entry.pathInRepo, entry.change);
|
|
34097
|
+
}
|
|
33749
34098
|
return m;
|
|
33750
34099
|
}
|
|
33751
|
-
|
|
34100
|
+
|
|
34101
|
+
// src/git/changes/parse/parse-numstat-entries.ts
|
|
34102
|
+
function parseNumstatCounts(parts) {
|
|
34103
|
+
const [a, d] = parts;
|
|
34104
|
+
return {
|
|
34105
|
+
additions: a === "-" ? 0 : parseInt(String(a), 10) || 0,
|
|
34106
|
+
deletions: d === "-" ? 0 : parseInt(String(d), 10) || 0
|
|
34107
|
+
};
|
|
34108
|
+
}
|
|
34109
|
+
function parseNumstatLine(line) {
|
|
33752
34110
|
const parts = line.split(" ");
|
|
33753
34111
|
if (parts.length < 3) return null;
|
|
33754
|
-
const
|
|
33755
|
-
const additions
|
|
33756
|
-
const
|
|
33757
|
-
|
|
34112
|
+
const rawPath = parts[parts.length - 1];
|
|
34113
|
+
const { additions, deletions } = parseNumstatCounts(parts);
|
|
34114
|
+
const expanded = expandGitRenameNumstatPath(rawPath);
|
|
34115
|
+
const pathInRepo = normalizeGitDiffPath(expanded?.newPath ?? rawPath);
|
|
34116
|
+
return {
|
|
34117
|
+
pathInRepo,
|
|
34118
|
+
additions,
|
|
34119
|
+
deletions,
|
|
34120
|
+
...expanded ? { oldPathInRepo: expanded.oldPath } : {}
|
|
34121
|
+
};
|
|
33758
34122
|
}
|
|
33759
|
-
function
|
|
34123
|
+
function parseNumstatEntries(lines) {
|
|
33760
34124
|
const m = /* @__PURE__ */ new Map();
|
|
33761
34125
|
for (const line of lines) {
|
|
33762
34126
|
if (!line.trim()) continue;
|
|
33763
34127
|
const parts = line.split(" ");
|
|
33764
34128
|
if (parts.length < 3) continue;
|
|
33765
|
-
const [
|
|
33766
|
-
const
|
|
33767
|
-
const
|
|
33768
|
-
|
|
34129
|
+
const rawPath = parts[parts.length - 1];
|
|
34130
|
+
const expanded = expandGitRenameNumstatPath(rawPath);
|
|
34131
|
+
const entry = parseNumstatLine(line);
|
|
34132
|
+
if (!entry) continue;
|
|
34133
|
+
const existing = m.get(entry.pathInRepo);
|
|
34134
|
+
if (!existing || expanded) {
|
|
34135
|
+
m.set(entry.pathInRepo, entry);
|
|
34136
|
+
}
|
|
33769
34137
|
}
|
|
33770
34138
|
return m;
|
|
33771
34139
|
}
|
|
34140
|
+
function parseNumstat(lines) {
|
|
34141
|
+
const m = /* @__PURE__ */ new Map();
|
|
34142
|
+
for (const [path46, entry] of parseNumstatEntries(lines)) {
|
|
34143
|
+
m.set(path46, { additions: entry.additions, deletions: entry.deletions });
|
|
34144
|
+
}
|
|
34145
|
+
return m;
|
|
34146
|
+
}
|
|
34147
|
+
|
|
34148
|
+
// src/git/changes/parse/numstat-from-git-no-index.ts
|
|
33772
34149
|
async function numstatFromGitNoIndex(g, pathInRepo) {
|
|
33773
34150
|
const devNull = process.platform === "win32" ? "NUL" : "/dev/null";
|
|
33774
34151
|
try {
|
|
33775
34152
|
const out = await g.raw(["diff", "--numstat", "--no-index", "--", devNull, pathInRepo]);
|
|
33776
34153
|
const first2 = String(out).split("\n").find((l) => l.trim()) ?? "";
|
|
33777
|
-
|
|
34154
|
+
const parsed = parseNumstatLine(first2);
|
|
34155
|
+
if (!parsed) return null;
|
|
34156
|
+
return { additions: parsed.additions, deletions: parsed.deletions };
|
|
33778
34157
|
} catch {
|
|
33779
34158
|
return null;
|
|
33780
34159
|
}
|
|
33781
34160
|
}
|
|
33782
34161
|
|
|
33783
|
-
// src/git/changes/
|
|
33784
|
-
function
|
|
33785
|
-
|
|
33786
|
-
|
|
34162
|
+
// src/git/changes/rows/collect-moved-source-paths.ts
|
|
34163
|
+
function collectMovedSourcePaths(nameEntries, numByPath) {
|
|
34164
|
+
const sources = /* @__PURE__ */ new Set();
|
|
34165
|
+
for (const entry of nameEntries) {
|
|
34166
|
+
if (entry.change === "moved" && entry.oldPathInRepo) sources.add(entry.oldPathInRepo);
|
|
34167
|
+
}
|
|
34168
|
+
for (const entry of numByPath.values()) {
|
|
34169
|
+
if (entry.oldPathInRepo) sources.add(entry.oldPathInRepo);
|
|
34170
|
+
}
|
|
34171
|
+
return sources;
|
|
34172
|
+
}
|
|
33787
34173
|
|
|
33788
|
-
|
|
34174
|
+
// src/git/changes/numstat/is-binary-numstat-line.ts
|
|
34175
|
+
function isBinaryNumstatLine(line) {
|
|
34176
|
+
const parts = line.split(" ");
|
|
34177
|
+
if (parts.length < 3) return false;
|
|
34178
|
+
return parts[0] === "-" || parts[1] === "-";
|
|
34179
|
+
}
|
|
34180
|
+
|
|
34181
|
+
// src/git/changes/listing/collect-binary-paths-from-numstat.ts
|
|
34182
|
+
function collectBinaryPathsFromNumstat(numstatRaw) {
|
|
34183
|
+
const binaryByPath = /* @__PURE__ */ new Set();
|
|
34184
|
+
for (const line of numstatRaw.split("\n")) {
|
|
34185
|
+
if (!line.trim()) continue;
|
|
34186
|
+
const parts = line.split(" ");
|
|
34187
|
+
if (parts.length < 3) continue;
|
|
34188
|
+
const rawPath = parts[parts.length - 1];
|
|
34189
|
+
const expanded = expandGitRenameNumstatPath(rawPath);
|
|
34190
|
+
if (isBinaryNumstatLine(line)) binaryByPath.add(expanded?.newPath ?? rawPath);
|
|
34191
|
+
}
|
|
34192
|
+
return binaryByPath;
|
|
33789
34193
|
}
|
|
33790
34194
|
|
|
33791
|
-
// src/git/
|
|
34195
|
+
// src/git/changes/listing/parse-diff-output.ts
|
|
34196
|
+
function parseDiffOutput(nameStatusRaw, numstatRaw) {
|
|
34197
|
+
const nameEntries = parseNameStatusEntries(nameStatusRaw.split("\n"));
|
|
34198
|
+
const nameByPath = new Map(nameEntries.map((entry) => [entry.pathInRepo, entry]));
|
|
34199
|
+
const numByPath = parseNumstatEntries(numstatRaw.split("\n"));
|
|
34200
|
+
const movedSourcePaths = collectMovedSourcePaths(nameEntries, numByPath);
|
|
34201
|
+
const binaryByPath = collectBinaryPathsFromNumstat(numstatRaw);
|
|
34202
|
+
return { nameEntries, nameByPath, numByPath, movedSourcePaths, binaryByPath };
|
|
34203
|
+
}
|
|
34204
|
+
function collectChangedPaths(options) {
|
|
34205
|
+
const paths = /* @__PURE__ */ new Set();
|
|
34206
|
+
for (const entry of options.nameEntries) paths.add(entry.pathInRepo);
|
|
34207
|
+
for (const pathInRepo of options.numByPath.keys()) paths.add(pathInRepo);
|
|
34208
|
+
for (const pathInRepo of options.untracked ?? []) paths.add(pathInRepo);
|
|
34209
|
+
return paths;
|
|
34210
|
+
}
|
|
34211
|
+
|
|
34212
|
+
// src/git/changes/listing/parent-for-commit-diff.ts
|
|
33792
34213
|
var EMPTY_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
33793
34214
|
async function parentForCommitDiff(g, sha) {
|
|
33794
34215
|
try {
|
|
@@ -33801,76 +34222,112 @@ async function parentForCommitDiff(g, sha) {
|
|
|
33801
34222
|
}
|
|
33802
34223
|
}
|
|
33803
34224
|
}
|
|
34225
|
+
|
|
34226
|
+
// src/git/changes/listing/rename-diff-args.ts
|
|
34227
|
+
var RENAME_DIFF_ARGS = ["-M", "--find-renames"];
|
|
34228
|
+
|
|
34229
|
+
// src/git/changes/rows/pick-preferred-changed-file-row.ts
|
|
34230
|
+
function rowRank(row) {
|
|
34231
|
+
if (row.change === "moved") return 3;
|
|
34232
|
+
if (row.change === "modified") return 2;
|
|
34233
|
+
if (row.change === "added") return 1;
|
|
34234
|
+
return 0;
|
|
34235
|
+
}
|
|
34236
|
+
function pickPreferredChangedFileRow(a, b) {
|
|
34237
|
+
if (rowRank(a) !== rowRank(b)) return rowRank(a) > rowRank(b) ? a : b;
|
|
34238
|
+
const aHasPatch = Boolean(a.patchContent?.trim());
|
|
34239
|
+
const bHasPatch = Boolean(b.patchContent?.trim());
|
|
34240
|
+
if (aHasPatch !== bHasPatch) return aHasPatch ? a : b;
|
|
34241
|
+
const aHasMoveMeta = Boolean(a.movedFromPathInRepo || a.movedFromPathRelLauncher);
|
|
34242
|
+
const bHasMoveMeta = Boolean(b.movedFromPathInRepo || b.movedFromPathRelLauncher);
|
|
34243
|
+
if (aHasMoveMeta !== bHasMoveMeta) return aHasMoveMeta ? a : b;
|
|
34244
|
+
return a;
|
|
34245
|
+
}
|
|
34246
|
+
|
|
34247
|
+
// src/git/changes/rows/dedupe-changed-file-rows.ts
|
|
34248
|
+
function dedupeChangedFileRows(rows) {
|
|
34249
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
34250
|
+
for (const row of rows) {
|
|
34251
|
+
const key = normalizeGitDiffPath(row.pathRelLauncher);
|
|
34252
|
+
const normalizedRow = key === row.pathRelLauncher ? row : { ...row, pathRelLauncher: key };
|
|
34253
|
+
const existing = byPath.get(key);
|
|
34254
|
+
if (!existing) {
|
|
34255
|
+
byPath.set(key, normalizedRow);
|
|
34256
|
+
continue;
|
|
34257
|
+
}
|
|
34258
|
+
const preferRow = pickPreferredChangedFileRow(existing, normalizedRow);
|
|
34259
|
+
byPath.set(key, { ...preferRow, pathRelLauncher: key });
|
|
34260
|
+
}
|
|
34261
|
+
return [...byPath.values()];
|
|
34262
|
+
}
|
|
34263
|
+
|
|
34264
|
+
// src/git/changes/listing/list-changed-files-for-commit.ts
|
|
33804
34265
|
async function listChangedFilesForCommit(repoGitCwd, repoRelPath, commitSha) {
|
|
33805
34266
|
const g = cliSimpleGit(repoGitCwd);
|
|
33806
34267
|
const parent = await parentForCommitDiff(g, commitSha);
|
|
33807
34268
|
const range = `${parent}..${commitSha}`;
|
|
33808
34269
|
const [nameStatusRaw, numstatRaw] = await Promise.all([
|
|
33809
|
-
g.raw(["diff", "--name-status", range]).catch(() => ""),
|
|
33810
|
-
g.raw(["diff", "--numstat", range]).catch(() => "")
|
|
34270
|
+
g.raw(["diff", ...RENAME_DIFF_ARGS, "--name-status", range]).catch(() => ""),
|
|
34271
|
+
g.raw(["diff", ...RENAME_DIFF_ARGS, "--numstat", range]).catch(() => "")
|
|
33811
34272
|
]);
|
|
33812
|
-
const
|
|
33813
|
-
const
|
|
33814
|
-
const paths =
|
|
33815
|
-
|
|
33816
|
-
|
|
33817
|
-
await forEachWithGitYield([...paths], async (pathInRepo) => {
|
|
33818
|
-
const relLauncher = posixJoinDirFile(normRel, pathInRepo.replace(/\\/g, "/"));
|
|
33819
|
-
const nums = numByPath.get(pathInRepo);
|
|
33820
|
-
let additions = nums?.additions ?? 0;
|
|
33821
|
-
let deletions = nums?.deletions ?? 0;
|
|
33822
|
-
let change = kindByPath.get(pathInRepo) ?? "modified";
|
|
33823
|
-
if (!kindByPath.has(pathInRepo) && nums) {
|
|
33824
|
-
if (additions > 0 && deletions === 0) change = "added";
|
|
33825
|
-
else if (deletions > 0 && additions === 0) change = "removed";
|
|
33826
|
-
else change = "modified";
|
|
33827
|
-
}
|
|
33828
|
-
rows.push({ pathRelLauncher: relLauncher, additions, deletions, change });
|
|
33829
|
-
});
|
|
33830
|
-
await forEachWithGitYield(rows, async (row) => {
|
|
33831
|
-
let pathInRepo;
|
|
33832
|
-
if (normRel === ".") {
|
|
33833
|
-
pathInRepo = row.pathRelLauncher;
|
|
33834
|
-
} else if (row.pathRelLauncher.startsWith(`${normRel}/`)) {
|
|
33835
|
-
pathInRepo = row.pathRelLauncher.slice(normRel.length + 1);
|
|
33836
|
-
} else {
|
|
33837
|
-
pathInRepo = row.pathRelLauncher;
|
|
33838
|
-
}
|
|
33839
|
-
const raw = await g.raw(["diff", "-U20000", range, "--", pathInRepo]).catch(() => "");
|
|
33840
|
-
const t = String(raw).trim();
|
|
33841
|
-
row.patchContent = t ? truncatePatch(t) : void 0;
|
|
34273
|
+
const parsed = parseDiffOutput(String(nameStatusRaw), String(numstatRaw));
|
|
34274
|
+
const normRel = normalizeRepoRelPath(repoRelPath);
|
|
34275
|
+
const paths = collectChangedPaths({
|
|
34276
|
+
nameEntries: parsed.nameEntries,
|
|
34277
|
+
numByPath: parsed.numByPath
|
|
33842
34278
|
});
|
|
33843
|
-
rows
|
|
33844
|
-
|
|
33845
|
-
|
|
33846
|
-
|
|
33847
|
-
|
|
34279
|
+
let rows = await buildChangedFileRowsFromPaths({
|
|
34280
|
+
paths,
|
|
34281
|
+
repoRelPath: normRel,
|
|
34282
|
+
nameByPath: parsed.nameByPath,
|
|
34283
|
+
numByPath: parsed.numByPath,
|
|
34284
|
+
movedSourcePaths: parsed.movedSourcePaths
|
|
34285
|
+
});
|
|
34286
|
+
rows = dedupeChangedFileRows(rows);
|
|
34287
|
+
await enrichCommitFileRows({
|
|
34288
|
+
rows,
|
|
34289
|
+
repoGitCwd,
|
|
34290
|
+
repoRelPath: normRel,
|
|
34291
|
+
range,
|
|
34292
|
+
commitSha,
|
|
34293
|
+
parentSha: parent,
|
|
34294
|
+
binaryByPath: parsed.binaryByPath
|
|
34295
|
+
});
|
|
34296
|
+
const finalRows = dedupeChangedFileRows(rows);
|
|
34297
|
+
finalRows.sort((a, b) => a.pathRelLauncher.localeCompare(b.pathRelLauncher));
|
|
34298
|
+
return finalRows;
|
|
34299
|
+
}
|
|
34300
|
+
|
|
34301
|
+
// src/git/changes/listing/apply-untracked-file-stats.ts
|
|
33848
34302
|
import * as fs17 from "node:fs";
|
|
33849
|
-
import * as
|
|
33850
|
-
|
|
33851
|
-
|
|
33852
|
-
|
|
33853
|
-
|
|
33854
|
-
|
|
33855
|
-
|
|
33856
|
-
|
|
33857
|
-
|
|
33858
|
-
|
|
33859
|
-
|
|
33860
|
-
|
|
33861
|
-
lines += 1;
|
|
33862
|
-
bytes += Buffer.byteLength(String(_line), "utf8") + 1;
|
|
33863
|
-
if (bytes > maxBytes) {
|
|
33864
|
-
rl.close();
|
|
33865
|
-
stream.destroy();
|
|
33866
|
-
return lines;
|
|
34303
|
+
import * as path20 from "node:path";
|
|
34304
|
+
function createUntrackedStatsApplier(options) {
|
|
34305
|
+
return async (row, pathInRepo) => {
|
|
34306
|
+
if (!options.untrackedSet.has(pathInRepo)) return;
|
|
34307
|
+
if (options.nameByPath.has(pathInRepo)) return;
|
|
34308
|
+
if (row.change === "moved") return;
|
|
34309
|
+
const repoFilePath = path20.join(options.repoGitCwd, pathInRepo);
|
|
34310
|
+
const fromGit = await numstatFromGitNoIndex(options.g, pathInRepo);
|
|
34311
|
+
if (fromGit) {
|
|
34312
|
+
row.additions = fromGit.additions;
|
|
34313
|
+
row.deletions = fromGit.deletions;
|
|
34314
|
+
return;
|
|
33867
34315
|
}
|
|
33868
|
-
|
|
33869
|
-
|
|
34316
|
+
try {
|
|
34317
|
+
const st = await fs17.promises.stat(repoFilePath);
|
|
34318
|
+
row.additions = st.isFile() ? await countTextFileLines(repoFilePath) : 0;
|
|
34319
|
+
} catch {
|
|
34320
|
+
row.additions = 0;
|
|
34321
|
+
}
|
|
34322
|
+
row.deletions = 0;
|
|
34323
|
+
};
|
|
33870
34324
|
}
|
|
33871
34325
|
|
|
33872
|
-
// src/git/changes/
|
|
33873
|
-
import * as
|
|
34326
|
+
// src/git/changes/listing/enrich-working-tree-file-rows.ts
|
|
34327
|
+
import * as path21 from "node:path";
|
|
34328
|
+
|
|
34329
|
+
// src/git/changes/patch/hydrate-patch.ts
|
|
34330
|
+
import * as fs18 from "node:fs";
|
|
33874
34331
|
var UNIFIED_HUNK_HEADER_RE = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
|
|
33875
34332
|
var MAX_HYDRATE_LINES_PER_GAP = 8e3;
|
|
33876
34333
|
var MAX_HYDRATE_LINES_PER_FILE = 8e4;
|
|
@@ -33885,7 +34342,7 @@ async function readGitBlobLines(repoCwd, pathInRepo) {
|
|
|
33885
34342
|
}
|
|
33886
34343
|
async function readWorktreeFileLines(filePath) {
|
|
33887
34344
|
try {
|
|
33888
|
-
const raw = await
|
|
34345
|
+
const raw = await fs18.promises.readFile(filePath, "utf8");
|
|
33889
34346
|
return raw.split(/\r?\n/);
|
|
33890
34347
|
} catch {
|
|
33891
34348
|
return null;
|
|
@@ -33985,82 +34442,70 @@ async function hydrateUnifiedPatchWithFileContext(patch, filePath, repoGitCwd, p
|
|
|
33985
34442
|
return truncatePatch(out.join("\n"));
|
|
33986
34443
|
}
|
|
33987
34444
|
|
|
33988
|
-
// src/git/changes/
|
|
33989
|
-
function
|
|
33990
|
-
|
|
33991
|
-
|
|
33992
|
-
|
|
33993
|
-
|
|
33994
|
-
|
|
33995
|
-
|
|
33996
|
-
|
|
33997
|
-
|
|
33998
|
-
|
|
34445
|
+
// src/git/changes/listing/enrich-working-tree-file-rows.ts
|
|
34446
|
+
async function enrichWorkingTreeFileRows(options) {
|
|
34447
|
+
await forEachWithGitYield(options.rows, async (row) => {
|
|
34448
|
+
const pathInRepo = pathInRepoFromLauncher(row.pathRelLauncher, options.repoRelPath);
|
|
34449
|
+
const filePath = path21.join(options.repoGitCwd, pathInRepo);
|
|
34450
|
+
const hydrateKind = row.change === "moved" ? "modified" : row.change;
|
|
34451
|
+
let patch = await unifiedDiffForFile(options.repoGitCwd, pathInRepo, row.change, row.movedFromPathInRepo);
|
|
34452
|
+
if (patch) {
|
|
34453
|
+
patch = await hydrateUnifiedPatchWithFileContext(
|
|
34454
|
+
patch,
|
|
34455
|
+
filePath,
|
|
34456
|
+
options.repoGitCwd,
|
|
34457
|
+
pathInRepo,
|
|
34458
|
+
hydrateKind
|
|
34459
|
+
);
|
|
34460
|
+
}
|
|
34461
|
+
row.patchContent = patch;
|
|
34462
|
+
row.totalLines = await resolveWorkingTreeFileTotalLines({
|
|
34463
|
+
repoGitCwd: options.repoGitCwd,
|
|
34464
|
+
pathInRepo,
|
|
34465
|
+
change: row.change,
|
|
34466
|
+
isBinary: options.binaryByPath.has(pathInRepo)
|
|
34467
|
+
});
|
|
34468
|
+
});
|
|
33999
34469
|
}
|
|
34000
34470
|
|
|
34001
|
-
// src/git/changes/list-changed-files-for-repo.ts
|
|
34471
|
+
// src/git/changes/listing/list-changed-files-for-repo.ts
|
|
34002
34472
|
async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
34003
34473
|
const g = cliSimpleGit(repoGitCwd);
|
|
34004
34474
|
const [nameStatusRaw, numstatRaw, untrackedRaw] = await Promise.all([
|
|
34005
|
-
g.raw(["diff", "--name-status", "HEAD"]).catch(() => ""),
|
|
34006
|
-
g.raw(["diff", "HEAD", "--numstat"]).catch(() => ""),
|
|
34475
|
+
g.raw(["diff", ...RENAME_DIFF_ARGS, "--name-status", "HEAD"]).catch(() => ""),
|
|
34476
|
+
g.raw(["diff", ...RENAME_DIFF_ARGS, "HEAD", "--numstat"]).catch(() => ""),
|
|
34007
34477
|
g.raw(["ls-files", "--others", "--exclude-standard"]).catch(() => "")
|
|
34008
34478
|
]);
|
|
34009
|
-
const
|
|
34010
|
-
const numByPath = parseNumstat(String(numstatRaw).split("\n"));
|
|
34011
|
-
const paths = /* @__PURE__ */ new Set([...kindByPath.keys(), ...numByPath.keys()]);
|
|
34479
|
+
const parsed = parseDiffOutput(String(nameStatusRaw), String(numstatRaw));
|
|
34012
34480
|
const untracked = String(untrackedRaw).split("\n").map((s) => s.trim()).filter(Boolean);
|
|
34013
|
-
|
|
34014
|
-
const
|
|
34015
|
-
|
|
34016
|
-
|
|
34017
|
-
|
|
34018
|
-
const nums = numByPath.get(pathInRepo);
|
|
34019
|
-
let additions = nums?.additions ?? 0;
|
|
34020
|
-
let deletions = nums?.deletions ?? 0;
|
|
34021
|
-
let change = kindByPath.get(pathInRepo) ?? "modified";
|
|
34022
|
-
if (untracked.includes(pathInRepo) && !kindByPath.has(pathInRepo)) {
|
|
34023
|
-
change = "added";
|
|
34024
|
-
const fromGit = await numstatFromGitNoIndex(g, pathInRepo);
|
|
34025
|
-
if (fromGit) {
|
|
34026
|
-
additions = fromGit.additions;
|
|
34027
|
-
deletions = fromGit.deletions;
|
|
34028
|
-
} else {
|
|
34029
|
-
try {
|
|
34030
|
-
const st = await fs17.promises.stat(repoFilePath);
|
|
34031
|
-
if (st.isFile()) additions = await countTextFileLines(repoFilePath);
|
|
34032
|
-
else additions = 0;
|
|
34033
|
-
} catch {
|
|
34034
|
-
additions = 0;
|
|
34035
|
-
}
|
|
34036
|
-
deletions = 0;
|
|
34037
|
-
}
|
|
34038
|
-
}
|
|
34039
|
-
if (!kindByPath.has(pathInRepo) && nums) {
|
|
34040
|
-
if (additions > 0 && deletions === 0) change = "added";
|
|
34041
|
-
else if (deletions > 0 && additions === 0) change = "removed";
|
|
34042
|
-
else change = "modified";
|
|
34043
|
-
}
|
|
34044
|
-
rows.push({ pathRelLauncher: relLauncher, additions, deletions, change });
|
|
34481
|
+
const untrackedSet = new Set(untracked);
|
|
34482
|
+
const paths = collectChangedPaths({
|
|
34483
|
+
nameEntries: parsed.nameEntries,
|
|
34484
|
+
numByPath: parsed.numByPath,
|
|
34485
|
+
untracked
|
|
34045
34486
|
});
|
|
34046
|
-
|
|
34047
|
-
|
|
34048
|
-
|
|
34049
|
-
|
|
34050
|
-
|
|
34051
|
-
|
|
34052
|
-
|
|
34053
|
-
|
|
34054
|
-
|
|
34055
|
-
|
|
34056
|
-
|
|
34057
|
-
|
|
34058
|
-
|
|
34059
|
-
patch = await hydrateUnifiedPatchWithFileContext(patch, filePath, repoGitCwd, pathInRepo, row.change);
|
|
34060
|
-
}
|
|
34061
|
-
row.patchContent = patch;
|
|
34487
|
+
let rows = await buildChangedFileRowsFromPaths({
|
|
34488
|
+
paths,
|
|
34489
|
+
repoRelPath,
|
|
34490
|
+
nameByPath: parsed.nameByPath,
|
|
34491
|
+
numByPath: parsed.numByPath,
|
|
34492
|
+
movedSourcePaths: parsed.movedSourcePaths,
|
|
34493
|
+
untrackedSet,
|
|
34494
|
+
applyUntrackedStats: createUntrackedStatsApplier({
|
|
34495
|
+
g,
|
|
34496
|
+
repoGitCwd,
|
|
34497
|
+
untrackedSet,
|
|
34498
|
+
nameByPath: parsed.nameByPath
|
|
34499
|
+
})
|
|
34062
34500
|
});
|
|
34063
|
-
|
|
34501
|
+
rows = dedupeChangedFileRows(rows);
|
|
34502
|
+
await enrichWorkingTreeFileRows({
|
|
34503
|
+
rows,
|
|
34504
|
+
repoGitCwd,
|
|
34505
|
+
repoRelPath,
|
|
34506
|
+
binaryByPath: parsed.binaryByPath
|
|
34507
|
+
});
|
|
34508
|
+
return dedupeChangedFileRows(rows);
|
|
34064
34509
|
}
|
|
34065
34510
|
|
|
34066
34511
|
// src/git/changes/get-working-tree-change-repo-details.ts
|
|
@@ -34069,8 +34514,8 @@ function normRepoRel(p) {
|
|
|
34069
34514
|
return x === "" ? "." : x;
|
|
34070
34515
|
}
|
|
34071
34516
|
async function getWorkingTreeChangeRepoDetails(options) {
|
|
34072
|
-
const bridgeRoot =
|
|
34073
|
-
const sessionWtRoot = options.sessionWorktreeRootPath ?
|
|
34517
|
+
const bridgeRoot = path22.resolve(getBridgeRoot());
|
|
34518
|
+
const sessionWtRoot = options.sessionWorktreeRootPath ? path22.resolve(options.sessionWorktreeRootPath) : null;
|
|
34074
34519
|
const legacyNested = options.legacyRepoNestedSessionLayout === true;
|
|
34075
34520
|
const out = [];
|
|
34076
34521
|
const filter = options.repoFilterRelPath != null ? normRepoRel(options.repoFilterRelPath) : null;
|
|
@@ -34085,7 +34530,7 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
34085
34530
|
for (let i = 0; i < options.commitTargetPaths.length; i++) {
|
|
34086
34531
|
if (i > 0) await yieldToEventLoop2();
|
|
34087
34532
|
const target = options.commitTargetPaths[i];
|
|
34088
|
-
const t =
|
|
34533
|
+
const t = path22.resolve(target);
|
|
34089
34534
|
if (!await isGitRepoDirectory(t)) continue;
|
|
34090
34535
|
const g = cliSimpleGit(t);
|
|
34091
34536
|
let branch = "HEAD";
|
|
@@ -34098,8 +34543,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
34098
34543
|
const remoteDisplay = formatRemoteDisplayLabel(remoteUrl);
|
|
34099
34544
|
let repoRelPath;
|
|
34100
34545
|
if (sessionWtRoot) {
|
|
34101
|
-
const anchor = legacyNested ?
|
|
34102
|
-
const relNorm =
|
|
34546
|
+
const anchor = legacyNested ? path22.dirname(t) : t;
|
|
34547
|
+
const relNorm = path22.relative(sessionWtRoot, anchor);
|
|
34103
34548
|
repoRelPath = relNorm === "" ? "." : relNorm.replace(/\\/g, "/");
|
|
34104
34549
|
} else {
|
|
34105
34550
|
let top = t;
|
|
@@ -34108,8 +34553,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
34108
34553
|
} catch {
|
|
34109
34554
|
top = t;
|
|
34110
34555
|
}
|
|
34111
|
-
const rel =
|
|
34112
|
-
repoRelPath = rel.startsWith("..") ?
|
|
34556
|
+
const rel = path22.relative(bridgeRoot, path22.resolve(top)).replace(/\\/g, "/") || ".";
|
|
34557
|
+
repoRelPath = rel.startsWith("..") ? path22.basename(path22.resolve(top)) : rel;
|
|
34113
34558
|
}
|
|
34114
34559
|
const norm = normRepoRel(repoRelPath === "" ? "." : repoRelPath);
|
|
34115
34560
|
if (filter && norm !== filter) continue;
|
|
@@ -34141,7 +34586,7 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
34141
34586
|
return out;
|
|
34142
34587
|
}
|
|
34143
34588
|
|
|
34144
|
-
// src/git/changes/
|
|
34589
|
+
// src/git/changes/status/working-tree-changed-path-count.ts
|
|
34145
34590
|
function workingTreeChangedPathCount(nameStatusLines, numstatLines, untrackedLines) {
|
|
34146
34591
|
const kindByPath = parseNameStatusLines(nameStatusLines);
|
|
34147
34592
|
const numByPath = parseNumstat(numstatLines);
|
|
@@ -34237,23 +34682,23 @@ async function commitSessionWorktrees(options) {
|
|
|
34237
34682
|
}
|
|
34238
34683
|
|
|
34239
34684
|
// src/worktrees/remove-session-worktrees.ts
|
|
34240
|
-
import * as
|
|
34685
|
+
import * as fs21 from "node:fs";
|
|
34241
34686
|
|
|
34242
34687
|
// src/git/worktrees/worktree-remove.ts
|
|
34243
|
-
import * as
|
|
34688
|
+
import * as fs20 from "node:fs";
|
|
34244
34689
|
|
|
34245
34690
|
// src/git/worktrees/resolve-main-repo-from-git-file.ts
|
|
34246
|
-
import * as
|
|
34247
|
-
import * as
|
|
34691
|
+
import * as fs19 from "node:fs";
|
|
34692
|
+
import * as path23 from "node:path";
|
|
34248
34693
|
function resolveMainRepoFromWorktreeGitFile(wt) {
|
|
34249
|
-
const gitDirFile =
|
|
34250
|
-
if (!
|
|
34251
|
-
const first2 =
|
|
34694
|
+
const gitDirFile = path23.join(wt, ".git");
|
|
34695
|
+
if (!fs19.existsSync(gitDirFile) || !fs19.statSync(gitDirFile).isFile()) return "";
|
|
34696
|
+
const first2 = fs19.readFileSync(gitDirFile, "utf8").trim();
|
|
34252
34697
|
const m = first2.match(/^gitdir:\s*(.+)$/im);
|
|
34253
34698
|
if (!m) return "";
|
|
34254
|
-
const gitWorktreePath =
|
|
34255
|
-
const gitDir =
|
|
34256
|
-
return
|
|
34699
|
+
const gitWorktreePath = path23.resolve(wt, m[1].trim());
|
|
34700
|
+
const gitDir = path23.dirname(path23.dirname(gitWorktreePath));
|
|
34701
|
+
return path23.dirname(gitDir);
|
|
34257
34702
|
}
|
|
34258
34703
|
|
|
34259
34704
|
// src/git/worktrees/worktree-remove.ts
|
|
@@ -34262,7 +34707,7 @@ async function gitWorktreeRemoveForce(worktreePath) {
|
|
|
34262
34707
|
if (mainRepo) {
|
|
34263
34708
|
await cliSimpleGit(mainRepo).raw(["worktree", "remove", "--force", worktreePath]);
|
|
34264
34709
|
} else {
|
|
34265
|
-
|
|
34710
|
+
fs20.rmSync(worktreePath, { recursive: true, force: true });
|
|
34266
34711
|
}
|
|
34267
34712
|
}
|
|
34268
34713
|
|
|
@@ -34275,7 +34720,7 @@ async function removeSessionWorktrees(paths, log2) {
|
|
|
34275
34720
|
} catch (e) {
|
|
34276
34721
|
log2(`[worktrees] Remove failed for ${wt}: ${e instanceof Error ? e.message : String(e)}`);
|
|
34277
34722
|
try {
|
|
34278
|
-
|
|
34723
|
+
fs21.rmSync(wt, { recursive: true, force: true });
|
|
34279
34724
|
} catch {
|
|
34280
34725
|
}
|
|
34281
34726
|
}
|
|
@@ -34304,12 +34749,12 @@ async function renameSessionWorktreeBranches(paths, newBranch, log2) {
|
|
|
34304
34749
|
}
|
|
34305
34750
|
|
|
34306
34751
|
// src/worktrees/worktree-layout-file.ts
|
|
34307
|
-
import * as
|
|
34308
|
-
import * as
|
|
34752
|
+
import * as fs22 from "node:fs";
|
|
34753
|
+
import * as path24 from "node:path";
|
|
34309
34754
|
import os7 from "node:os";
|
|
34310
34755
|
var LAYOUT_FILENAME = "worktree-launcher-layout.json";
|
|
34311
34756
|
function defaultWorktreeLayoutPath() {
|
|
34312
|
-
return
|
|
34757
|
+
return path24.join(os7.homedir(), ".buildautomaton", LAYOUT_FILENAME);
|
|
34313
34758
|
}
|
|
34314
34759
|
function normalizeLoadedLayout(raw) {
|
|
34315
34760
|
if (raw && typeof raw === "object" && "launcherCwds" in raw) {
|
|
@@ -34321,8 +34766,8 @@ function normalizeLoadedLayout(raw) {
|
|
|
34321
34766
|
function loadWorktreeLayout() {
|
|
34322
34767
|
try {
|
|
34323
34768
|
const p = defaultWorktreeLayoutPath();
|
|
34324
|
-
if (!
|
|
34325
|
-
const raw = JSON.parse(
|
|
34769
|
+
if (!fs22.existsSync(p)) return { launcherCwds: [] };
|
|
34770
|
+
const raw = JSON.parse(fs22.readFileSync(p, "utf8"));
|
|
34326
34771
|
return normalizeLoadedLayout(raw);
|
|
34327
34772
|
} catch {
|
|
34328
34773
|
return { launcherCwds: [] };
|
|
@@ -34330,24 +34775,24 @@ function loadWorktreeLayout() {
|
|
|
34330
34775
|
}
|
|
34331
34776
|
function saveWorktreeLayout(layout) {
|
|
34332
34777
|
try {
|
|
34333
|
-
const dir =
|
|
34334
|
-
|
|
34335
|
-
|
|
34778
|
+
const dir = path24.dirname(defaultWorktreeLayoutPath());
|
|
34779
|
+
fs22.mkdirSync(dir, { recursive: true });
|
|
34780
|
+
fs22.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
|
|
34336
34781
|
} catch {
|
|
34337
34782
|
}
|
|
34338
34783
|
}
|
|
34339
34784
|
function baseNameSafe(pathString) {
|
|
34340
|
-
return
|
|
34785
|
+
return path24.basename(pathString).replace(/[^a-zA-Z0-9._-]+/g, "-") || "cwd";
|
|
34341
34786
|
}
|
|
34342
34787
|
function getLauncherDirNameIfPresent(layout, bridgeRootPath2) {
|
|
34343
|
-
const norm =
|
|
34344
|
-
const existing = layout.launcherCwds.find((e) =>
|
|
34788
|
+
const norm = path24.resolve(bridgeRootPath2);
|
|
34789
|
+
const existing = layout.launcherCwds.find((e) => path24.resolve(e.absolutePath) === norm);
|
|
34345
34790
|
return existing?.dirName;
|
|
34346
34791
|
}
|
|
34347
34792
|
function allocateDirNameForLauncherCwd(layout, bridgeRootPath2) {
|
|
34348
34793
|
const existing = getLauncherDirNameIfPresent(layout, bridgeRootPath2);
|
|
34349
34794
|
if (existing) return existing;
|
|
34350
|
-
const norm =
|
|
34795
|
+
const norm = path24.resolve(bridgeRootPath2);
|
|
34351
34796
|
const base = baseNameSafe(norm);
|
|
34352
34797
|
const used = new Set(layout.launcherCwds.map((e) => e.dirName));
|
|
34353
34798
|
let name = base;
|
|
@@ -34362,11 +34807,11 @@ function allocateDirNameForLauncherCwd(layout, bridgeRootPath2) {
|
|
|
34362
34807
|
}
|
|
34363
34808
|
|
|
34364
34809
|
// src/worktrees/discover-session-worktree-on-disk.ts
|
|
34365
|
-
import * as
|
|
34366
|
-
import * as
|
|
34810
|
+
import * as fs23 from "node:fs";
|
|
34811
|
+
import * as path25 from "node:path";
|
|
34367
34812
|
function isGitDir(dirPath) {
|
|
34368
34813
|
try {
|
|
34369
|
-
return
|
|
34814
|
+
return fs23.existsSync(path25.join(dirPath, ".git"));
|
|
34370
34815
|
} catch {
|
|
34371
34816
|
return false;
|
|
34372
34817
|
}
|
|
@@ -34375,23 +34820,23 @@ function collectGitRepoRootsUnderDirectory(rootPath) {
|
|
|
34375
34820
|
const out = [];
|
|
34376
34821
|
const walk = (dir) => {
|
|
34377
34822
|
if (isGitDir(dir)) {
|
|
34378
|
-
out.push(
|
|
34823
|
+
out.push(path25.resolve(dir));
|
|
34379
34824
|
return;
|
|
34380
34825
|
}
|
|
34381
34826
|
let entries;
|
|
34382
34827
|
try {
|
|
34383
|
-
entries =
|
|
34828
|
+
entries = fs23.readdirSync(dir, { withFileTypes: true });
|
|
34384
34829
|
} catch {
|
|
34385
34830
|
return;
|
|
34386
34831
|
}
|
|
34387
34832
|
for (const e of entries) {
|
|
34388
34833
|
if (e.name.startsWith(".")) continue;
|
|
34389
|
-
const full =
|
|
34834
|
+
const full = path25.join(dir, e.name);
|
|
34390
34835
|
if (!e.isDirectory()) continue;
|
|
34391
34836
|
walk(full);
|
|
34392
34837
|
}
|
|
34393
34838
|
};
|
|
34394
|
-
walk(
|
|
34839
|
+
walk(path25.resolve(rootPath));
|
|
34395
34840
|
return [...new Set(out)];
|
|
34396
34841
|
}
|
|
34397
34842
|
function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
|
|
@@ -34400,16 +34845,16 @@ function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
|
|
|
34400
34845
|
if (depth > maxDepth) return;
|
|
34401
34846
|
let entries;
|
|
34402
34847
|
try {
|
|
34403
|
-
entries =
|
|
34848
|
+
entries = fs23.readdirSync(dir, { withFileTypes: true });
|
|
34404
34849
|
} catch {
|
|
34405
34850
|
return;
|
|
34406
34851
|
}
|
|
34407
34852
|
for (const e of entries) {
|
|
34408
34853
|
if (e.name.startsWith(".")) continue;
|
|
34409
|
-
const full =
|
|
34854
|
+
const full = path25.join(dir, e.name);
|
|
34410
34855
|
if (!e.isDirectory()) continue;
|
|
34411
34856
|
if (e.name === sessionId) {
|
|
34412
|
-
if (isGitDir(full)) out.push(
|
|
34857
|
+
if (isGitDir(full)) out.push(path25.resolve(full));
|
|
34413
34858
|
} else {
|
|
34414
34859
|
walk(full, depth + 1);
|
|
34415
34860
|
}
|
|
@@ -34421,14 +34866,14 @@ function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
|
|
|
34421
34866
|
function tryBindingFromSessionDirectory(sessionDir) {
|
|
34422
34867
|
let st;
|
|
34423
34868
|
try {
|
|
34424
|
-
st =
|
|
34869
|
+
st = fs23.statSync(sessionDir);
|
|
34425
34870
|
} catch {
|
|
34426
34871
|
return null;
|
|
34427
34872
|
}
|
|
34428
34873
|
if (!st.isDirectory()) return null;
|
|
34429
34874
|
const worktreePaths = collectGitRepoRootsUnderDirectory(sessionDir);
|
|
34430
34875
|
if (worktreePaths.length === 0) return null;
|
|
34431
|
-
const abs =
|
|
34876
|
+
const abs = path25.resolve(sessionDir);
|
|
34432
34877
|
return {
|
|
34433
34878
|
sessionParentPath: abs,
|
|
34434
34879
|
workingTreeRelRoot: abs,
|
|
@@ -34438,20 +34883,20 @@ function tryBindingFromSessionDirectory(sessionDir) {
|
|
|
34438
34883
|
function discoverLegacyBindingAscendingFromCheckout(sessionId, checkoutPath) {
|
|
34439
34884
|
const sid = sessionId.trim();
|
|
34440
34885
|
if (!sid) return null;
|
|
34441
|
-
const hintR =
|
|
34886
|
+
const hintR = path25.resolve(checkoutPath);
|
|
34442
34887
|
let best = null;
|
|
34443
|
-
let cur =
|
|
34888
|
+
let cur = path25.dirname(hintR);
|
|
34444
34889
|
for (let i = 0; i < 40; i++) {
|
|
34445
34890
|
const paths = collectWorktreeRootsNamed(cur, sid, 24);
|
|
34446
|
-
if (paths.some((p) =>
|
|
34447
|
-
const isolated = resolveIsolatedSessionParentPathFromCheckouts(paths) ??
|
|
34891
|
+
if (paths.some((p) => path25.resolve(p) === hintR)) {
|
|
34892
|
+
const isolated = resolveIsolatedSessionParentPathFromCheckouts(paths) ?? path25.resolve(paths[0]);
|
|
34448
34893
|
best = {
|
|
34449
|
-
sessionParentPath:
|
|
34450
|
-
workingTreeRelRoot:
|
|
34451
|
-
repoCheckoutPaths: paths.map((p) =>
|
|
34894
|
+
sessionParentPath: path25.resolve(isolated),
|
|
34895
|
+
workingTreeRelRoot: path25.resolve(cur),
|
|
34896
|
+
repoCheckoutPaths: paths.map((p) => path25.resolve(p))
|
|
34452
34897
|
};
|
|
34453
34898
|
}
|
|
34454
|
-
const next =
|
|
34899
|
+
const next = path25.dirname(cur);
|
|
34455
34900
|
if (next === cur) break;
|
|
34456
34901
|
cur = next;
|
|
34457
34902
|
}
|
|
@@ -34459,33 +34904,33 @@ function discoverLegacyBindingAscendingFromCheckout(sessionId, checkoutPath) {
|
|
|
34459
34904
|
}
|
|
34460
34905
|
function discoverSessionWorktreeOnDisk(options) {
|
|
34461
34906
|
const { sessionId, worktreesRootPath, layout, bridgeRoot } = options;
|
|
34462
|
-
if (!sessionId.trim() || !
|
|
34907
|
+
if (!sessionId.trim() || !fs23.existsSync(worktreesRootPath)) return null;
|
|
34463
34908
|
const preferredKey = getLauncherDirNameIfPresent(layout, bridgeRoot);
|
|
34464
34909
|
const keys = [];
|
|
34465
34910
|
if (preferredKey) keys.push(preferredKey);
|
|
34466
34911
|
try {
|
|
34467
|
-
for (const name of
|
|
34912
|
+
for (const name of fs23.readdirSync(worktreesRootPath)) {
|
|
34468
34913
|
if (name.startsWith(".")) continue;
|
|
34469
|
-
const p =
|
|
34470
|
-
if (!
|
|
34914
|
+
const p = path25.join(worktreesRootPath, name);
|
|
34915
|
+
if (!fs23.statSync(p).isDirectory()) continue;
|
|
34471
34916
|
if (name !== preferredKey) keys.push(name);
|
|
34472
34917
|
}
|
|
34473
34918
|
} catch {
|
|
34474
34919
|
return null;
|
|
34475
34920
|
}
|
|
34476
34921
|
for (const key of keys) {
|
|
34477
|
-
const layoutRoot =
|
|
34478
|
-
if (!
|
|
34479
|
-
const sessionDir =
|
|
34922
|
+
const layoutRoot = path25.join(worktreesRootPath, key);
|
|
34923
|
+
if (!fs23.existsSync(layoutRoot) || !fs23.statSync(layoutRoot).isDirectory()) continue;
|
|
34924
|
+
const sessionDir = path25.join(layoutRoot, sessionId);
|
|
34480
34925
|
const nested = tryBindingFromSessionDirectory(sessionDir);
|
|
34481
34926
|
if (nested) return nested;
|
|
34482
34927
|
const legacyPaths = collectWorktreeRootsNamed(layoutRoot, sessionId, 24);
|
|
34483
34928
|
if (legacyPaths.length > 0) {
|
|
34484
|
-
const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ??
|
|
34929
|
+
const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path25.resolve(legacyPaths[0]);
|
|
34485
34930
|
return {
|
|
34486
|
-
sessionParentPath:
|
|
34487
|
-
workingTreeRelRoot:
|
|
34488
|
-
repoCheckoutPaths: legacyPaths.map((p) =>
|
|
34931
|
+
sessionParentPath: path25.resolve(isolated),
|
|
34932
|
+
workingTreeRelRoot: path25.resolve(layoutRoot),
|
|
34933
|
+
repoCheckoutPaths: legacyPaths.map((p) => path25.resolve(p))
|
|
34489
34934
|
};
|
|
34490
34935
|
}
|
|
34491
34936
|
}
|
|
@@ -34494,12 +34939,12 @@ function discoverSessionWorktreeOnDisk(options) {
|
|
|
34494
34939
|
function discoverSessionWorktreesUnderSessionWorktreeRoot(sessionWorktreeRootPathOrHint, sessionId) {
|
|
34495
34940
|
const sid = sessionId.trim();
|
|
34496
34941
|
if (!sid) return null;
|
|
34497
|
-
const hint =
|
|
34498
|
-
const underHint = tryBindingFromSessionDirectory(
|
|
34942
|
+
const hint = path25.resolve(sessionWorktreeRootPathOrHint);
|
|
34943
|
+
const underHint = tryBindingFromSessionDirectory(path25.join(hint, sid));
|
|
34499
34944
|
if (underHint) return underHint;
|
|
34500
34945
|
const direct = tryBindingFromSessionDirectory(hint);
|
|
34501
34946
|
if (direct) {
|
|
34502
|
-
if (
|
|
34947
|
+
if (path25.basename(hint) === sid && isGitDir(hint)) {
|
|
34503
34948
|
const legacyFromCheckout = discoverLegacyBindingAscendingFromCheckout(sid, hint);
|
|
34504
34949
|
if (legacyFromCheckout && legacyFromCheckout.repoCheckoutPaths.length > direct.repoCheckoutPaths.length) {
|
|
34505
34950
|
return legacyFromCheckout;
|
|
@@ -34507,24 +34952,24 @@ function discoverSessionWorktreesUnderSessionWorktreeRoot(sessionWorktreeRootPat
|
|
|
34507
34952
|
}
|
|
34508
34953
|
return direct;
|
|
34509
34954
|
}
|
|
34510
|
-
if (
|
|
34955
|
+
if (path25.basename(hint) === sid && isGitDir(hint)) {
|
|
34511
34956
|
const legacyFromCheckout = discoverLegacyBindingAscendingFromCheckout(sid, hint);
|
|
34512
34957
|
if (legacyFromCheckout) return legacyFromCheckout;
|
|
34513
34958
|
}
|
|
34514
34959
|
let st;
|
|
34515
34960
|
try {
|
|
34516
|
-
st =
|
|
34961
|
+
st = fs23.statSync(hint);
|
|
34517
34962
|
} catch {
|
|
34518
34963
|
return null;
|
|
34519
34964
|
}
|
|
34520
34965
|
if (!st.isDirectory()) return null;
|
|
34521
34966
|
const legacyPaths = collectWorktreeRootsNamed(hint, sid, 24);
|
|
34522
34967
|
if (legacyPaths.length === 0) return null;
|
|
34523
|
-
const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ??
|
|
34968
|
+
const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path25.resolve(legacyPaths[0]);
|
|
34524
34969
|
return {
|
|
34525
|
-
sessionParentPath:
|
|
34970
|
+
sessionParentPath: path25.resolve(isolated),
|
|
34526
34971
|
workingTreeRelRoot: hint,
|
|
34527
|
-
repoCheckoutPaths: legacyPaths.map((p) =>
|
|
34972
|
+
repoCheckoutPaths: legacyPaths.map((p) => path25.resolve(p))
|
|
34528
34973
|
};
|
|
34529
34974
|
}
|
|
34530
34975
|
|
|
@@ -34581,8 +35026,8 @@ function parseSessionParent(v) {
|
|
|
34581
35026
|
}
|
|
34582
35027
|
|
|
34583
35028
|
// src/worktrees/prepare-new-session-worktrees.ts
|
|
34584
|
-
import * as
|
|
34585
|
-
import * as
|
|
35029
|
+
import * as fs24 from "node:fs";
|
|
35030
|
+
import * as path26 from "node:path";
|
|
34586
35031
|
|
|
34587
35032
|
// src/git/worktrees/worktree-add.ts
|
|
34588
35033
|
async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch, baseRef = "HEAD") {
|
|
@@ -34592,7 +35037,7 @@ async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch, baseRef
|
|
|
34592
35037
|
}
|
|
34593
35038
|
|
|
34594
35039
|
// src/worktrees/prepare-new-session-worktrees.ts
|
|
34595
|
-
function
|
|
35040
|
+
function normalizeRepoRelPath2(rel) {
|
|
34596
35041
|
return rel === "" ? "." : rel.replace(/\\/g, "/");
|
|
34597
35042
|
}
|
|
34598
35043
|
function resolveBaseRefForRepo(relNorm, baseBranches) {
|
|
@@ -34604,10 +35049,10 @@ function resolveBaseRefForRepo(relNorm, baseBranches) {
|
|
|
34604
35049
|
}
|
|
34605
35050
|
async function prepareNewSessionWorktrees(options) {
|
|
34606
35051
|
const { worktreesRootPath, bridgeRoot, sessionId, layout, log: log2, worktreeBaseBranches } = options;
|
|
34607
|
-
const bridgeResolved =
|
|
35052
|
+
const bridgeResolved = path26.resolve(bridgeRoot);
|
|
34608
35053
|
const cwdKey = allocateDirNameForLauncherCwd(layout, bridgeResolved);
|
|
34609
|
-
const bridgeKeyDir =
|
|
34610
|
-
const sessionDir =
|
|
35054
|
+
const bridgeKeyDir = path26.join(worktreesRootPath, cwdKey);
|
|
35055
|
+
const sessionDir = path26.join(bridgeKeyDir, sessionId);
|
|
34611
35056
|
const repos = await discoverGitReposUnderRoot(bridgeResolved);
|
|
34612
35057
|
if (repos.length === 0) {
|
|
34613
35058
|
log2("[worktrees] No Git repositories under bridge root; skipping worktree creation.");
|
|
@@ -34615,14 +35060,14 @@ async function prepareNewSessionWorktrees(options) {
|
|
|
34615
35060
|
}
|
|
34616
35061
|
const branch = `session-${sessionId}`;
|
|
34617
35062
|
const worktreePaths = [];
|
|
34618
|
-
|
|
35063
|
+
fs24.mkdirSync(sessionDir, { recursive: true });
|
|
34619
35064
|
for (const repo of repos) {
|
|
34620
|
-
let rel =
|
|
34621
|
-
if (rel.startsWith("..") ||
|
|
34622
|
-
const relNorm =
|
|
34623
|
-
const wtPath = relNorm === "." ? sessionDir :
|
|
35065
|
+
let rel = path26.relative(bridgeResolved, repo.absolutePath);
|
|
35066
|
+
if (rel.startsWith("..") || path26.isAbsolute(rel)) continue;
|
|
35067
|
+
const relNorm = normalizeRepoRelPath2(rel === "" ? "." : rel);
|
|
35068
|
+
const wtPath = relNorm === "." ? sessionDir : path26.join(sessionDir, relNorm);
|
|
34624
35069
|
if (relNorm !== ".") {
|
|
34625
|
-
|
|
35070
|
+
fs24.mkdirSync(path26.dirname(wtPath), { recursive: true });
|
|
34626
35071
|
}
|
|
34627
35072
|
const baseRef = resolveBaseRefForRepo(relNorm, worktreeBaseBranches);
|
|
34628
35073
|
try {
|
|
@@ -34675,9 +35120,9 @@ function resolveExistingSessionParentPath(sessionId, cache2, discover) {
|
|
|
34675
35120
|
}
|
|
34676
35121
|
|
|
34677
35122
|
// src/worktrees/manager/resolve-explicit-session-parent-path.ts
|
|
34678
|
-
import * as
|
|
35123
|
+
import * as path27 from "node:path";
|
|
34679
35124
|
function resolveExplicitSessionParentPath(params) {
|
|
34680
|
-
const resolved =
|
|
35125
|
+
const resolved = path27.resolve(params.parentPathRaw);
|
|
34681
35126
|
if (parseSessionParent(params.sessionParent) !== "worktrees_root") {
|
|
34682
35127
|
return resolved;
|
|
34683
35128
|
}
|
|
@@ -34693,7 +35138,7 @@ function resolveExplicitSessionParentPath(params) {
|
|
|
34693
35138
|
for (let i = 0; i < 16; i++) {
|
|
34694
35139
|
const tryRoot = discoverSessionWorktreesUnderSessionWorktreeRoot(cur, params.sessionId);
|
|
34695
35140
|
if (tryRoot) return rememberAndReturn(tryRoot);
|
|
34696
|
-
const next =
|
|
35141
|
+
const next = path27.dirname(cur);
|
|
34697
35142
|
if (next === cur) break;
|
|
34698
35143
|
cur = next;
|
|
34699
35144
|
}
|
|
@@ -34745,16 +35190,16 @@ async function resolveSessionParentPathForPrompt(params) {
|
|
|
34745
35190
|
}
|
|
34746
35191
|
|
|
34747
35192
|
// src/worktrees/manager/session-worktree-cache.ts
|
|
34748
|
-
import * as
|
|
35193
|
+
import * as path28 from "node:path";
|
|
34749
35194
|
var SessionWorktreeCache = class {
|
|
34750
35195
|
sessionRepoCheckoutPaths = /* @__PURE__ */ new Map();
|
|
34751
35196
|
sessionParentPathBySession = /* @__PURE__ */ new Map();
|
|
34752
35197
|
sessionWorkingTreeRelRootBySession = /* @__PURE__ */ new Map();
|
|
34753
35198
|
remember(sessionId, binding) {
|
|
34754
|
-
const paths = binding.repoCheckoutPaths.map((p) =>
|
|
35199
|
+
const paths = binding.repoCheckoutPaths.map((p) => path28.resolve(p));
|
|
34755
35200
|
this.sessionRepoCheckoutPaths.set(sessionId, paths);
|
|
34756
|
-
this.sessionParentPathBySession.set(sessionId,
|
|
34757
|
-
this.sessionWorkingTreeRelRootBySession.set(sessionId,
|
|
35201
|
+
this.sessionParentPathBySession.set(sessionId, path28.resolve(binding.sessionParentPath));
|
|
35202
|
+
this.sessionWorkingTreeRelRootBySession.set(sessionId, path28.resolve(binding.workingTreeRelRoot));
|
|
34758
35203
|
}
|
|
34759
35204
|
clearSession(sessionId) {
|
|
34760
35205
|
const paths = this.sessionRepoCheckoutPaths.get(sessionId);
|
|
@@ -34784,7 +35229,7 @@ var SessionWorktreeCache = class {
|
|
|
34784
35229
|
const parent = this.sessionParentPathBySession.get(sessionId);
|
|
34785
35230
|
const relRoot = this.sessionWorkingTreeRelRootBySession.get(sessionId);
|
|
34786
35231
|
if (!parent || !relRoot) return false;
|
|
34787
|
-
return
|
|
35232
|
+
return path28.resolve(parent) !== path28.resolve(relRoot);
|
|
34788
35233
|
}
|
|
34789
35234
|
};
|
|
34790
35235
|
|
|
@@ -34888,29 +35333,29 @@ var SessionWorktreeManager = class {
|
|
|
34888
35333
|
};
|
|
34889
35334
|
|
|
34890
35335
|
// src/worktrees/manager/default-worktrees-root-path.ts
|
|
34891
|
-
import * as
|
|
35336
|
+
import * as path29 from "node:path";
|
|
34892
35337
|
import os8 from "node:os";
|
|
34893
35338
|
function defaultWorktreesRootPath() {
|
|
34894
|
-
return
|
|
35339
|
+
return path29.join(os8.homedir(), ".buildautomaton", "worktrees");
|
|
34895
35340
|
}
|
|
34896
35341
|
|
|
34897
35342
|
// src/files/watch-file-index.ts
|
|
34898
35343
|
import { watch } from "node:fs";
|
|
34899
|
-
import
|
|
35344
|
+
import path34 from "node:path";
|
|
34900
35345
|
|
|
34901
35346
|
// src/files/index/paths.ts
|
|
34902
|
-
import
|
|
35347
|
+
import path30 from "node:path";
|
|
34903
35348
|
import crypto2 from "node:crypto";
|
|
34904
35349
|
function getCwdHashForFileIndex(resolvedCwd) {
|
|
34905
|
-
return crypto2.createHash("sha256").update(
|
|
35350
|
+
return crypto2.createHash("sha256").update(path30.resolve(resolvedCwd)).digest("hex").slice(0, INDEX_HASH_LEN);
|
|
34906
35351
|
}
|
|
34907
35352
|
|
|
34908
35353
|
// src/files/index/build-file-index.ts
|
|
34909
|
-
import
|
|
35354
|
+
import path32 from "node:path";
|
|
34910
35355
|
|
|
34911
35356
|
// src/files/index/walk-workspace-tree.ts
|
|
34912
|
-
import
|
|
34913
|
-
import
|
|
35357
|
+
import fs25 from "node:fs";
|
|
35358
|
+
import path31 from "node:path";
|
|
34914
35359
|
var DEPENDENCY_INSTALL_DIR_NAMES = /* @__PURE__ */ new Set([
|
|
34915
35360
|
"node_modules",
|
|
34916
35361
|
"bower_components",
|
|
@@ -34927,7 +35372,7 @@ function shouldSkipWorkspaceWalkEntry(name) {
|
|
|
34927
35372
|
async function walkWorkspaceTreeAsync(dir, baseDir, onFile, state) {
|
|
34928
35373
|
let names;
|
|
34929
35374
|
try {
|
|
34930
|
-
names = await
|
|
35375
|
+
names = await fs25.promises.readdir(dir);
|
|
34931
35376
|
} catch {
|
|
34932
35377
|
return;
|
|
34933
35378
|
}
|
|
@@ -34939,14 +35384,14 @@ async function walkWorkspaceTreeAsync(dir, baseDir, onFile, state) {
|
|
|
34939
35384
|
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
34940
35385
|
}
|
|
34941
35386
|
state.n++;
|
|
34942
|
-
const full =
|
|
35387
|
+
const full = path31.join(dir, name);
|
|
34943
35388
|
let stat2;
|
|
34944
35389
|
try {
|
|
34945
|
-
stat2 = await
|
|
35390
|
+
stat2 = await fs25.promises.stat(full);
|
|
34946
35391
|
} catch {
|
|
34947
35392
|
continue;
|
|
34948
35393
|
}
|
|
34949
|
-
const relative6 =
|
|
35394
|
+
const relative6 = path31.relative(baseDir, full).replace(/\\/g, "/");
|
|
34950
35395
|
if (stat2.isDirectory()) {
|
|
34951
35396
|
await walkWorkspaceTreeAsync(full, baseDir, onFile, state);
|
|
34952
35397
|
} else if (stat2.isFile()) {
|
|
@@ -34959,7 +35404,7 @@ function createWalkYieldState() {
|
|
|
34959
35404
|
}
|
|
34960
35405
|
|
|
34961
35406
|
// src/files/index/file-index-sqlite-lock.ts
|
|
34962
|
-
import
|
|
35407
|
+
import fs26 from "node:fs";
|
|
34963
35408
|
function isSqliteCorruptError(e) {
|
|
34964
35409
|
const msg = e instanceof Error ? e.message : String(e);
|
|
34965
35410
|
return msg.includes("malformed") || msg.includes("database disk image is malformed") || msg.includes("corrupt");
|
|
@@ -34973,7 +35418,7 @@ function withFileIndexSqliteLock(fn) {
|
|
|
34973
35418
|
if (!isSqliteCorruptError(e)) throw e;
|
|
34974
35419
|
closeAllCliSqliteConnections();
|
|
34975
35420
|
try {
|
|
34976
|
-
|
|
35421
|
+
fs26.unlinkSync(getCliSqlitePath());
|
|
34977
35422
|
} catch {
|
|
34978
35423
|
}
|
|
34979
35424
|
chain = Promise.resolve();
|
|
@@ -35048,7 +35493,7 @@ async function collectWorkspacePathsAsync(resolved) {
|
|
|
35048
35493
|
}
|
|
35049
35494
|
async function buildFileIndexAsync(cwd) {
|
|
35050
35495
|
return withFileIndexSqliteLock(async () => {
|
|
35051
|
-
const resolved =
|
|
35496
|
+
const resolved = path32.resolve(cwd);
|
|
35052
35497
|
await yieldToEventLoop();
|
|
35053
35498
|
assertNotShutdown();
|
|
35054
35499
|
const paths = await collectWorkspacePathsAsync(resolved);
|
|
@@ -35060,7 +35505,7 @@ async function buildFileIndexAsync(cwd) {
|
|
|
35060
35505
|
}
|
|
35061
35506
|
|
|
35062
35507
|
// src/files/index/ensure-file-index.ts
|
|
35063
|
-
import
|
|
35508
|
+
import path33 from "node:path";
|
|
35064
35509
|
|
|
35065
35510
|
// src/files/index/search-file-index.ts
|
|
35066
35511
|
function escapeLikePattern(fragment) {
|
|
@@ -35112,7 +35557,7 @@ async function searchBridgeFilePathsAsync(resolvedCwd, query, limit = 100) {
|
|
|
35112
35557
|
|
|
35113
35558
|
// src/files/index/ensure-file-index.ts
|
|
35114
35559
|
async function ensureFileIndexAsync(cwd) {
|
|
35115
|
-
const resolved =
|
|
35560
|
+
const resolved = path33.resolve(cwd);
|
|
35116
35561
|
if (await bridgeFileIndexIsPopulated(resolved)) {
|
|
35117
35562
|
return { fromCache: true, pathCount: await bridgeFileIndexPathCount(resolved) };
|
|
35118
35563
|
}
|
|
@@ -35156,7 +35601,7 @@ function createFsWatcher(resolved, schedule) {
|
|
|
35156
35601
|
}
|
|
35157
35602
|
}
|
|
35158
35603
|
function startFileIndexWatcher(cwd = getBridgeRoot()) {
|
|
35159
|
-
const resolved =
|
|
35604
|
+
const resolved = path34.resolve(cwd);
|
|
35160
35605
|
void buildFileIndexAsync(resolved).catch((e) => {
|
|
35161
35606
|
if (e instanceof CliSqliteInterrupted) return;
|
|
35162
35607
|
console.error("[file-index] Initial index build failed:", e);
|
|
@@ -35186,7 +35631,7 @@ function startFileIndexWatcher(cwd = getBridgeRoot()) {
|
|
|
35186
35631
|
}
|
|
35187
35632
|
|
|
35188
35633
|
// src/connection/create-bridge-connection.ts
|
|
35189
|
-
import * as
|
|
35634
|
+
import * as path45 from "node:path";
|
|
35190
35635
|
|
|
35191
35636
|
// src/dev-servers/manager/dev-server-manager.ts
|
|
35192
35637
|
import { rm as rm2 } from "node:fs/promises";
|
|
@@ -35230,7 +35675,7 @@ function forceKillChild(proc, log2, shortId, graceMs) {
|
|
|
35230
35675
|
}
|
|
35231
35676
|
|
|
35232
35677
|
// src/dev-servers/process/wire-dev-server-child-process.ts
|
|
35233
|
-
import
|
|
35678
|
+
import fs27 from "node:fs";
|
|
35234
35679
|
|
|
35235
35680
|
// src/dev-servers/manager/forward-pipe.ts
|
|
35236
35681
|
function forwardChildPipe(childReadable, terminal, onData) {
|
|
@@ -35266,7 +35711,7 @@ function wireDevServerChildProcess(d) {
|
|
|
35266
35711
|
d.setPollInterval(void 0);
|
|
35267
35712
|
return;
|
|
35268
35713
|
}
|
|
35269
|
-
|
|
35714
|
+
fs27.readFile(d.mergedLogPath, (err, buf) => {
|
|
35270
35715
|
if (err || (d.getSpawnGeneration() ?? 0) !== d.scheduledGen) return;
|
|
35271
35716
|
if (buf.length <= d.mergedReadPos.value) return;
|
|
35272
35717
|
const chunk = Buffer.from(buf.subarray(d.mergedReadPos.value));
|
|
@@ -35304,7 +35749,7 @@ ${errTail}` : ""}`);
|
|
|
35304
35749
|
d.sendStatus(code === 0 || code == null ? "stopped" : "error", detail, tails);
|
|
35305
35750
|
};
|
|
35306
35751
|
if (mergedPath) {
|
|
35307
|
-
|
|
35752
|
+
fs27.readFile(mergedPath, (err, buf) => {
|
|
35308
35753
|
if (!err && buf.length > d.mergedReadPos.value) {
|
|
35309
35754
|
const chunk = Buffer.from(buf.subarray(d.mergedReadPos.value));
|
|
35310
35755
|
if (chunk.length > 0) {
|
|
@@ -35406,13 +35851,13 @@ function parseDevServerDefs(servers) {
|
|
|
35406
35851
|
}
|
|
35407
35852
|
|
|
35408
35853
|
// src/dev-servers/manager/shell-spawn/utils.ts
|
|
35409
|
-
import
|
|
35854
|
+
import fs28 from "node:fs";
|
|
35410
35855
|
function isSpawnEbadf(e) {
|
|
35411
35856
|
return typeof e === "object" && e !== null && "code" in e && e.code === "EBADF";
|
|
35412
35857
|
}
|
|
35413
35858
|
function rmDirQuiet(dir) {
|
|
35414
35859
|
try {
|
|
35415
|
-
|
|
35860
|
+
fs28.rmSync(dir, { recursive: true, force: true });
|
|
35416
35861
|
} catch {
|
|
35417
35862
|
}
|
|
35418
35863
|
}
|
|
@@ -35420,7 +35865,7 @@ var cachedDevNullReadFd;
|
|
|
35420
35865
|
function devNullReadFd() {
|
|
35421
35866
|
if (cachedDevNullReadFd === void 0) {
|
|
35422
35867
|
const devPath = process.platform === "win32" ? "nul" : "/dev/null";
|
|
35423
|
-
cachedDevNullReadFd =
|
|
35868
|
+
cachedDevNullReadFd = fs28.openSync(devPath, "r");
|
|
35424
35869
|
}
|
|
35425
35870
|
return cachedDevNullReadFd;
|
|
35426
35871
|
}
|
|
@@ -35494,15 +35939,15 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
|
|
|
35494
35939
|
|
|
35495
35940
|
// src/dev-servers/manager/shell-spawn/try-spawn-merged-log-file.ts
|
|
35496
35941
|
import { spawn as spawn8 } from "node:child_process";
|
|
35497
|
-
import
|
|
35942
|
+
import fs29 from "node:fs";
|
|
35498
35943
|
import { tmpdir } from "node:os";
|
|
35499
|
-
import
|
|
35944
|
+
import path35 from "node:path";
|
|
35500
35945
|
function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
35501
|
-
const tmpRoot =
|
|
35502
|
-
const logPath =
|
|
35946
|
+
const tmpRoot = fs29.mkdtempSync(path35.join(tmpdir(), "ba-devsrv-log-"));
|
|
35947
|
+
const logPath = path35.join(tmpRoot, "combined.log");
|
|
35503
35948
|
let logFd;
|
|
35504
35949
|
try {
|
|
35505
|
-
logFd =
|
|
35950
|
+
logFd = fs29.openSync(logPath, "a");
|
|
35506
35951
|
} catch {
|
|
35507
35952
|
rmDirQuiet(tmpRoot);
|
|
35508
35953
|
return null;
|
|
@@ -35521,7 +35966,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
|
35521
35966
|
} else {
|
|
35522
35967
|
proc = spawn8("/bin/sh", ["-c", command], { env, cwd, stdio, ...signal ? { signal } : {} });
|
|
35523
35968
|
}
|
|
35524
|
-
|
|
35969
|
+
fs29.closeSync(logFd);
|
|
35525
35970
|
return {
|
|
35526
35971
|
proc,
|
|
35527
35972
|
pipedStdoutStderr: true,
|
|
@@ -35530,7 +35975,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
|
35530
35975
|
};
|
|
35531
35976
|
} catch (e) {
|
|
35532
35977
|
try {
|
|
35533
|
-
|
|
35978
|
+
fs29.closeSync(logFd);
|
|
35534
35979
|
} catch {
|
|
35535
35980
|
}
|
|
35536
35981
|
rmDirQuiet(tmpRoot);
|
|
@@ -35541,22 +35986,22 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
|
35541
35986
|
|
|
35542
35987
|
// src/dev-servers/manager/shell-spawn/try-spawn-shell-script-log-redirect.ts
|
|
35543
35988
|
import { spawn as spawn9 } from "node:child_process";
|
|
35544
|
-
import
|
|
35989
|
+
import fs30 from "node:fs";
|
|
35545
35990
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
35546
|
-
import
|
|
35991
|
+
import path36 from "node:path";
|
|
35547
35992
|
function shSingleQuote(s) {
|
|
35548
35993
|
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
35549
35994
|
}
|
|
35550
35995
|
function trySpawnShellScriptLogRedirectUnix(command, env, cwd, signal) {
|
|
35551
|
-
const tmpRoot =
|
|
35552
|
-
const logPath =
|
|
35553
|
-
const innerPath =
|
|
35554
|
-
const runnerPath =
|
|
35996
|
+
const tmpRoot = fs30.mkdtempSync(path36.join(tmpdir2(), "ba-devsrv-sh-"));
|
|
35997
|
+
const logPath = path36.join(tmpRoot, "combined.log");
|
|
35998
|
+
const innerPath = path36.join(tmpRoot, "_cmd.sh");
|
|
35999
|
+
const runnerPath = path36.join(tmpRoot, "_run.sh");
|
|
35555
36000
|
try {
|
|
35556
|
-
|
|
36001
|
+
fs30.writeFileSync(innerPath, `#!/bin/sh
|
|
35557
36002
|
${command}
|
|
35558
36003
|
`);
|
|
35559
|
-
|
|
36004
|
+
fs30.writeFileSync(
|
|
35560
36005
|
runnerPath,
|
|
35561
36006
|
`#!/bin/sh
|
|
35562
36007
|
cd ${shSingleQuote(cwd)}
|
|
@@ -35582,13 +36027,13 @@ cd ${shSingleQuote(cwd)}
|
|
|
35582
36027
|
}
|
|
35583
36028
|
}
|
|
35584
36029
|
function trySpawnShellScriptLogRedirectWin(command, env, cwd, signal) {
|
|
35585
|
-
const tmpRoot =
|
|
35586
|
-
const logPath =
|
|
35587
|
-
const runnerPath =
|
|
36030
|
+
const tmpRoot = fs30.mkdtempSync(path36.join(tmpdir2(), "ba-devsrv-sh-"));
|
|
36031
|
+
const logPath = path36.join(tmpRoot, "combined.log");
|
|
36032
|
+
const runnerPath = path36.join(tmpRoot, "_run.bat");
|
|
35588
36033
|
const q = (p) => `"${p.replace(/"/g, '""')}"`;
|
|
35589
36034
|
const com = process.env.ComSpec || "cmd.exe";
|
|
35590
36035
|
try {
|
|
35591
|
-
|
|
36036
|
+
fs30.writeFileSync(
|
|
35592
36037
|
runnerPath,
|
|
35593
36038
|
`@ECHO OFF\r
|
|
35594
36039
|
CD /D ${q(cwd)}\r
|
|
@@ -36357,30 +36802,30 @@ function createOnBridgeIdentified(opts) {
|
|
|
36357
36802
|
}
|
|
36358
36803
|
|
|
36359
36804
|
// src/skills/discover-local-agent-skills.ts
|
|
36360
|
-
import
|
|
36361
|
-
import
|
|
36805
|
+
import fs31 from "node:fs";
|
|
36806
|
+
import path37 from "node:path";
|
|
36362
36807
|
var SKILL_DISCOVERY_ROOTS = [".agents/skills", ".claude/skills", ".cursor/skills", "skills"];
|
|
36363
36808
|
function discoverLocalSkills(cwd) {
|
|
36364
36809
|
const out = [];
|
|
36365
36810
|
const seenKeys = /* @__PURE__ */ new Set();
|
|
36366
36811
|
for (const rel of SKILL_DISCOVERY_ROOTS) {
|
|
36367
|
-
const base =
|
|
36368
|
-
if (!
|
|
36812
|
+
const base = path37.join(cwd, rel);
|
|
36813
|
+
if (!fs31.existsSync(base) || !fs31.statSync(base).isDirectory()) continue;
|
|
36369
36814
|
let entries = [];
|
|
36370
36815
|
try {
|
|
36371
|
-
entries =
|
|
36816
|
+
entries = fs31.readdirSync(base);
|
|
36372
36817
|
} catch {
|
|
36373
36818
|
continue;
|
|
36374
36819
|
}
|
|
36375
36820
|
for (const name of entries) {
|
|
36376
|
-
const dir =
|
|
36821
|
+
const dir = path37.join(base, name);
|
|
36377
36822
|
try {
|
|
36378
|
-
if (!
|
|
36823
|
+
if (!fs31.statSync(dir).isDirectory()) continue;
|
|
36379
36824
|
} catch {
|
|
36380
36825
|
continue;
|
|
36381
36826
|
}
|
|
36382
|
-
const skillMd =
|
|
36383
|
-
if (!
|
|
36827
|
+
const skillMd = path37.join(dir, "SKILL.md");
|
|
36828
|
+
if (!fs31.existsSync(skillMd)) continue;
|
|
36384
36829
|
const key = `${rel}/${name}`;
|
|
36385
36830
|
if (seenKeys.has(key)) continue;
|
|
36386
36831
|
seenKeys.add(key);
|
|
@@ -36392,23 +36837,23 @@ function discoverLocalSkills(cwd) {
|
|
|
36392
36837
|
function discoverSkillLayoutRoots(cwd) {
|
|
36393
36838
|
const roots = [];
|
|
36394
36839
|
for (const rel of SKILL_DISCOVERY_ROOTS) {
|
|
36395
|
-
const base =
|
|
36396
|
-
if (!
|
|
36840
|
+
const base = path37.join(cwd, rel);
|
|
36841
|
+
if (!fs31.existsSync(base) || !fs31.statSync(base).isDirectory()) continue;
|
|
36397
36842
|
let entries = [];
|
|
36398
36843
|
try {
|
|
36399
|
-
entries =
|
|
36844
|
+
entries = fs31.readdirSync(base);
|
|
36400
36845
|
} catch {
|
|
36401
36846
|
continue;
|
|
36402
36847
|
}
|
|
36403
36848
|
const skills2 = [];
|
|
36404
36849
|
for (const name of entries) {
|
|
36405
|
-
const dir =
|
|
36850
|
+
const dir = path37.join(base, name);
|
|
36406
36851
|
try {
|
|
36407
|
-
if (!
|
|
36852
|
+
if (!fs31.statSync(dir).isDirectory()) continue;
|
|
36408
36853
|
} catch {
|
|
36409
36854
|
continue;
|
|
36410
36855
|
}
|
|
36411
|
-
if (!
|
|
36856
|
+
if (!fs31.existsSync(path37.join(dir, "SKILL.md"))) continue;
|
|
36412
36857
|
const relPath = `${rel}/${name}`.replace(/\\/g, "/");
|
|
36413
36858
|
skills2.push({ name, relPath });
|
|
36414
36859
|
}
|
|
@@ -36764,7 +37209,7 @@ function syncRunningTurnQueueKeys(turns, queueKey) {
|
|
|
36764
37209
|
}
|
|
36765
37210
|
|
|
36766
37211
|
// src/prompt-turn-queue/runner/run-local-revert-before-queued-prompt.ts
|
|
36767
|
-
import
|
|
37212
|
+
import fs32 from "node:fs";
|
|
36768
37213
|
async function runLocalRevertBeforeQueuedPrompt(next, deps) {
|
|
36769
37214
|
if (next.serverState !== "requeued_with_revert") return true;
|
|
36770
37215
|
const sid = next.sessionId;
|
|
@@ -36773,7 +37218,7 @@ async function runLocalRevertBeforeQueuedPrompt(next, deps) {
|
|
|
36773
37218
|
const agentBase = deps.sessionWorktreeManager.getSessionWorktreeRootForSession(sid) ?? getBridgeRoot();
|
|
36774
37219
|
const file2 = snapshotFilePath(agentBase, tid);
|
|
36775
37220
|
try {
|
|
36776
|
-
await
|
|
37221
|
+
await fs32.promises.access(file2, fs32.constants.F_OK);
|
|
36777
37222
|
} catch {
|
|
36778
37223
|
deps.log(
|
|
36779
37224
|
`[Queue] requeued_with_revert: no pre-turn snapshot for ${tid.slice(0, 8)}\u2026; continuing without revert.`
|
|
@@ -37061,9 +37506,9 @@ function parseChangeSummarySnapshots(raw) {
|
|
|
37061
37506
|
for (const item of raw) {
|
|
37062
37507
|
if (!item || typeof item !== "object") continue;
|
|
37063
37508
|
const o = item;
|
|
37064
|
-
const
|
|
37065
|
-
if (!
|
|
37066
|
-
const row = { path:
|
|
37509
|
+
const path46 = typeof o.path === "string" && o.path.trim() !== "" ? o.path.trim() : "";
|
|
37510
|
+
if (!path46) continue;
|
|
37511
|
+
const row = { path: path46 };
|
|
37067
37512
|
if (typeof o.patchContent === "string") row.patchContent = o.patchContent;
|
|
37068
37513
|
if (typeof o.oldText === "string") row.oldText = o.oldText;
|
|
37069
37514
|
if (typeof o.newText === "string") row.newText = o.newText;
|
|
@@ -37277,14 +37722,14 @@ var handleSkillCallMessage = (msg, { getWs, log: log2 }) => {
|
|
|
37277
37722
|
};
|
|
37278
37723
|
|
|
37279
37724
|
// src/files/list-dir/index.ts
|
|
37280
|
-
import
|
|
37725
|
+
import fs34 from "node:fs";
|
|
37281
37726
|
|
|
37282
37727
|
// src/files/ensure-under-cwd.ts
|
|
37283
|
-
import
|
|
37728
|
+
import path38 from "node:path";
|
|
37284
37729
|
function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
|
|
37285
|
-
const normalized =
|
|
37286
|
-
const resolved =
|
|
37287
|
-
if (!resolved.startsWith(cwd +
|
|
37730
|
+
const normalized = path38.normalize(relativePath).replace(/^(\.\/)+/, "");
|
|
37731
|
+
const resolved = path38.resolve(cwd, normalized);
|
|
37732
|
+
if (!resolved.startsWith(cwd + path38.sep) && resolved !== cwd) {
|
|
37288
37733
|
return null;
|
|
37289
37734
|
}
|
|
37290
37735
|
return resolved;
|
|
@@ -37294,15 +37739,15 @@ function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
|
|
|
37294
37739
|
var LIST_DIR_YIELD_EVERY = 256;
|
|
37295
37740
|
|
|
37296
37741
|
// src/files/list-dir/map-dir-entry.ts
|
|
37297
|
-
import
|
|
37298
|
-
import
|
|
37742
|
+
import path39 from "node:path";
|
|
37743
|
+
import fs33 from "node:fs";
|
|
37299
37744
|
async function mapDirEntry(d, relativePath, resolved) {
|
|
37300
|
-
const entryPath =
|
|
37301
|
-
const fullPath =
|
|
37745
|
+
const entryPath = path39.join(relativePath || ".", d.name).replace(/\\/g, "/");
|
|
37746
|
+
const fullPath = path39.join(resolved, d.name);
|
|
37302
37747
|
let isDir = d.isDirectory();
|
|
37303
37748
|
if (d.isSymbolicLink()) {
|
|
37304
37749
|
try {
|
|
37305
|
-
const targetStat = await
|
|
37750
|
+
const targetStat = await fs33.promises.stat(fullPath);
|
|
37306
37751
|
isDir = targetStat.isDirectory();
|
|
37307
37752
|
} catch {
|
|
37308
37753
|
isDir = false;
|
|
@@ -37332,7 +37777,7 @@ async function listDirAsync(relativePath, sessionParentPath = getBridgeRoot()) {
|
|
|
37332
37777
|
return { error: "Path is outside working directory" };
|
|
37333
37778
|
}
|
|
37334
37779
|
try {
|
|
37335
|
-
const names = await
|
|
37780
|
+
const names = await fs34.promises.readdir(resolved, { withFileTypes: true });
|
|
37336
37781
|
const entries = [];
|
|
37337
37782
|
for (let i = 0; i < names.length; i++) {
|
|
37338
37783
|
if (i > 0 && i % LIST_DIR_YIELD_EVERY === 0) {
|
|
@@ -37352,18 +37797,18 @@ var LINE_CHUNK_SIZE = 64 * 1024;
|
|
|
37352
37797
|
var READ_RANGE_YIELD_EVERY_BYTES = 256 * 1024;
|
|
37353
37798
|
|
|
37354
37799
|
// src/files/read-file/resolve-file-path.ts
|
|
37355
|
-
import
|
|
37800
|
+
import fs35 from "node:fs";
|
|
37356
37801
|
async function resolveFilePathAsync(relativePath, sessionParentPath = getBridgeRoot()) {
|
|
37357
37802
|
const resolved = ensureUnderCwd(relativePath, sessionParentPath);
|
|
37358
37803
|
if (!resolved) return { error: "Path is outside working directory" };
|
|
37359
37804
|
let real;
|
|
37360
37805
|
try {
|
|
37361
|
-
real = await
|
|
37806
|
+
real = await fs35.promises.realpath(resolved);
|
|
37362
37807
|
} catch {
|
|
37363
37808
|
real = resolved;
|
|
37364
37809
|
}
|
|
37365
37810
|
try {
|
|
37366
|
-
const stat2 = await
|
|
37811
|
+
const stat2 = await fs35.promises.stat(real);
|
|
37367
37812
|
if (!stat2.isFile()) return { error: "Not a file" };
|
|
37368
37813
|
return real;
|
|
37369
37814
|
} catch (err) {
|
|
@@ -37372,11 +37817,11 @@ async function resolveFilePathAsync(relativePath, sessionParentPath = getBridgeR
|
|
|
37372
37817
|
}
|
|
37373
37818
|
|
|
37374
37819
|
// src/files/read-file/read-file-range-async.ts
|
|
37375
|
-
import
|
|
37820
|
+
import fs36 from "node:fs";
|
|
37376
37821
|
import { StringDecoder } from "node:string_decoder";
|
|
37377
37822
|
async function readFileRangeAsync(filePath, startLine, endLine, lineOffsetIn, lineChunkSize = LINE_CHUNK_SIZE) {
|
|
37378
|
-
const fileSize = (await
|
|
37379
|
-
const fd = await
|
|
37823
|
+
const fileSize = (await fs36.promises.stat(filePath)).size;
|
|
37824
|
+
const fd = await fs36.promises.open(filePath, "r");
|
|
37380
37825
|
const bufSize = 64 * 1024;
|
|
37381
37826
|
const buf = Buffer.alloc(bufSize);
|
|
37382
37827
|
const decoder = new StringDecoder("utf8");
|
|
@@ -37538,11 +37983,11 @@ async function readFileRangeAsync(filePath, startLine, endLine, lineOffsetIn, li
|
|
|
37538
37983
|
}
|
|
37539
37984
|
|
|
37540
37985
|
// src/files/read-file/read-file-buffer-full-async.ts
|
|
37541
|
-
import
|
|
37986
|
+
import fs37 from "node:fs";
|
|
37542
37987
|
var READ_CHUNK_BYTES = 256 * 1024;
|
|
37543
37988
|
async function readFileBufferFullAsync(filePath) {
|
|
37544
|
-
const stat2 = await
|
|
37545
|
-
const fd = await
|
|
37989
|
+
const stat2 = await fs37.promises.stat(filePath);
|
|
37990
|
+
const fd = await fs37.promises.open(filePath, "r");
|
|
37546
37991
|
const chunks = [];
|
|
37547
37992
|
let position = 0;
|
|
37548
37993
|
let bytesSinceYield = 0;
|
|
@@ -37649,13 +38094,13 @@ function resolveFileBrowserSessionParent(sessionWorktreeManager, sessionId) {
|
|
|
37649
38094
|
}
|
|
37650
38095
|
|
|
37651
38096
|
// src/files/handle-file-browser-search.ts
|
|
37652
|
-
import
|
|
38097
|
+
import path40 from "node:path";
|
|
37653
38098
|
var SEARCH_LIMIT = 100;
|
|
37654
38099
|
function handleFileBrowserSearch(msg, socket, e2ee, sessionWorktreeManager) {
|
|
37655
38100
|
void (async () => {
|
|
37656
38101
|
await yieldToEventLoop();
|
|
37657
38102
|
const q = typeof msg.q === "string" ? msg.q : "";
|
|
37658
|
-
const sessionParentPath =
|
|
38103
|
+
const sessionParentPath = path40.resolve(
|
|
37659
38104
|
sessionWorktreeManager != null ? resolveFileBrowserSessionParent(sessionWorktreeManager, msg.sessionId) : getBridgeRoot()
|
|
37660
38105
|
);
|
|
37661
38106
|
if (!await bridgeFileIndexIsPopulated(sessionParentPath)) {
|
|
@@ -37774,8 +38219,8 @@ function handleSkillLayoutRequest(msg, deps) {
|
|
|
37774
38219
|
}
|
|
37775
38220
|
|
|
37776
38221
|
// src/skills/install-remote-skills.ts
|
|
37777
|
-
import
|
|
37778
|
-
import
|
|
38222
|
+
import fs38 from "node:fs";
|
|
38223
|
+
import path41 from "node:path";
|
|
37779
38224
|
function installRemoteSkills(cwd, targetDir, items) {
|
|
37780
38225
|
const installed2 = [];
|
|
37781
38226
|
if (!Array.isArray(items)) {
|
|
@@ -37786,15 +38231,15 @@ function installRemoteSkills(cwd, targetDir, items) {
|
|
|
37786
38231
|
if (typeof item.sourceId !== "string" || typeof item.skillName !== "string" || typeof item.versionHash !== "string" || !Array.isArray(item.files)) {
|
|
37787
38232
|
continue;
|
|
37788
38233
|
}
|
|
37789
|
-
const skillDir =
|
|
38234
|
+
const skillDir = path41.join(cwd, targetDir, item.skillName);
|
|
37790
38235
|
for (const f of item.files) {
|
|
37791
38236
|
if (typeof f.path !== "string" || !f.text && !f.base64) continue;
|
|
37792
|
-
const dest =
|
|
37793
|
-
|
|
38237
|
+
const dest = path41.join(skillDir, f.path);
|
|
38238
|
+
fs38.mkdirSync(path41.dirname(dest), { recursive: true });
|
|
37794
38239
|
if (f.text !== void 0) {
|
|
37795
|
-
|
|
38240
|
+
fs38.writeFileSync(dest, f.text, "utf8");
|
|
37796
38241
|
} else if (f.base64) {
|
|
37797
|
-
|
|
38242
|
+
fs38.writeFileSync(dest, Buffer.from(f.base64, "base64"));
|
|
37798
38243
|
}
|
|
37799
38244
|
}
|
|
37800
38245
|
installed2.push({
|
|
@@ -37952,7 +38397,7 @@ var handleSessionDiscardedMessage = (msg, deps) => {
|
|
|
37952
38397
|
};
|
|
37953
38398
|
|
|
37954
38399
|
// src/routing/handlers/revert-turn-snapshot.ts
|
|
37955
|
-
import * as
|
|
38400
|
+
import * as fs39 from "node:fs";
|
|
37956
38401
|
var handleRevertTurnSnapshotMessage = (msg, deps) => {
|
|
37957
38402
|
const id = typeof msg.id === "string" ? msg.id : "";
|
|
37958
38403
|
const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
|
|
@@ -37965,7 +38410,7 @@ var handleRevertTurnSnapshotMessage = (msg, deps) => {
|
|
|
37965
38410
|
const agentBase = sessionWorktreeManager.getSessionWorktreeRootForSession(sessionId) ?? getBridgeRoot();
|
|
37966
38411
|
const file2 = snapshotFilePath(agentBase, turnId);
|
|
37967
38412
|
try {
|
|
37968
|
-
await
|
|
38413
|
+
await fs39.promises.access(file2, fs39.constants.F_OK);
|
|
37969
38414
|
} catch {
|
|
37970
38415
|
sendWsMessage(s, {
|
|
37971
38416
|
type: "revert_turn_snapshot_result",
|
|
@@ -38007,7 +38452,7 @@ var handleDevServersConfig = (msg, deps) => {
|
|
|
38007
38452
|
};
|
|
38008
38453
|
|
|
38009
38454
|
// src/git/bridge-git-context.ts
|
|
38010
|
-
import * as
|
|
38455
|
+
import * as path42 from "node:path";
|
|
38011
38456
|
|
|
38012
38457
|
// src/git/branches/get-current-branch.ts
|
|
38013
38458
|
async function getCurrentBranch(repoPath) {
|
|
@@ -38057,12 +38502,12 @@ async function listRepoBranchRefs(repoPath) {
|
|
|
38057
38502
|
// src/git/bridge-git-context.ts
|
|
38058
38503
|
function folderNameForRelPath(relPath, bridgeRoot) {
|
|
38059
38504
|
if (relPath === "." || relPath === "") {
|
|
38060
|
-
return
|
|
38505
|
+
return path42.basename(path42.resolve(bridgeRoot)) || "repo";
|
|
38061
38506
|
}
|
|
38062
|
-
return
|
|
38507
|
+
return path42.basename(relPath) || relPath;
|
|
38063
38508
|
}
|
|
38064
38509
|
async function discoverGitReposForBridgeContext(bridgeRoot) {
|
|
38065
|
-
const root =
|
|
38510
|
+
const root = path42.resolve(bridgeRoot);
|
|
38066
38511
|
if (await isGitRepoDirectory(root)) {
|
|
38067
38512
|
const remoteUrl = await getRemoteOriginUrl(root);
|
|
38068
38513
|
return [{ absolutePath: root, remoteUrl }];
|
|
@@ -38070,17 +38515,17 @@ async function discoverGitReposForBridgeContext(bridgeRoot) {
|
|
|
38070
38515
|
const [deep, shallow] = await Promise.all([discoverGitReposUnderRoot(root), discoverGitRepos(root)]);
|
|
38071
38516
|
const byPath = /* @__PURE__ */ new Map();
|
|
38072
38517
|
for (const repo of [...deep, ...shallow]) {
|
|
38073
|
-
byPath.set(
|
|
38518
|
+
byPath.set(path42.resolve(repo.absolutePath), repo);
|
|
38074
38519
|
}
|
|
38075
38520
|
return [...byPath.values()];
|
|
38076
38521
|
}
|
|
38077
38522
|
async function getBridgeGitContext(bridgeRoot = getBridgeRoot()) {
|
|
38078
|
-
const bridgeResolved =
|
|
38523
|
+
const bridgeResolved = path42.resolve(bridgeRoot);
|
|
38079
38524
|
const repos = await discoverGitReposForBridgeContext(bridgeResolved);
|
|
38080
38525
|
const rows = [];
|
|
38081
38526
|
for (const repo of repos) {
|
|
38082
|
-
let rel =
|
|
38083
|
-
if (rel.startsWith("..") ||
|
|
38527
|
+
let rel = path42.relative(bridgeResolved, repo.absolutePath);
|
|
38528
|
+
if (rel.startsWith("..") || path42.isAbsolute(rel)) continue;
|
|
38084
38529
|
const relPath = rel === "" ? "." : rel.replace(/\\/g, "/");
|
|
38085
38530
|
const currentBranch = await getCurrentBranch(repo.absolutePath);
|
|
38086
38531
|
const remoteUrl = repo.remoteUrl.trim() || null;
|
|
@@ -38095,11 +38540,11 @@ async function getBridgeGitContext(bridgeRoot = getBridgeRoot()) {
|
|
|
38095
38540
|
return rows;
|
|
38096
38541
|
}
|
|
38097
38542
|
async function listRepoBranchesForBridge(repoRelPath, bridgeRoot = getBridgeRoot()) {
|
|
38098
|
-
const bridgeResolved =
|
|
38543
|
+
const bridgeResolved = path42.resolve(bridgeRoot);
|
|
38099
38544
|
const rel = repoRelPath.trim() === "." ? "" : repoRelPath.trim().replace(/\\/g, "/");
|
|
38100
|
-
const repoPath = rel === "" ? bridgeResolved :
|
|
38101
|
-
const resolved =
|
|
38102
|
-
if (!resolved.startsWith(bridgeResolved +
|
|
38545
|
+
const repoPath = rel === "" ? bridgeResolved : path42.join(bridgeResolved, rel);
|
|
38546
|
+
const resolved = path42.resolve(repoPath);
|
|
38547
|
+
if (!resolved.startsWith(bridgeResolved + path42.sep) && resolved !== bridgeResolved) {
|
|
38103
38548
|
return [];
|
|
38104
38549
|
}
|
|
38105
38550
|
return listRepoBranchRefs(resolved);
|
|
@@ -38683,10 +39128,10 @@ function listCliAgentCapabilityCacheForWorkspace(db, workspaceId) {
|
|
|
38683
39128
|
}
|
|
38684
39129
|
|
|
38685
39130
|
// src/agents/capabilities/warmup-agent-capabilities-on-connect.ts
|
|
38686
|
-
import * as
|
|
39131
|
+
import * as path44 from "node:path";
|
|
38687
39132
|
|
|
38688
39133
|
// src/agents/capabilities/probe-one-agent-type-for-capabilities.ts
|
|
38689
|
-
import * as
|
|
39134
|
+
import * as path43 from "node:path";
|
|
38690
39135
|
async function probeOneAgentTypeForCapabilities(params) {
|
|
38691
39136
|
const { agentType, cwd, workspaceId, log: log2, reportAgentCapabilities, bridgeReport = true, shouldContinue } = params;
|
|
38692
39137
|
const canContinue = () => shouldContinue?.() !== false;
|
|
@@ -38724,7 +39169,7 @@ async function probeOneAgentTypeForCapabilities(params) {
|
|
|
38724
39169
|
if (!canContinue()) return false;
|
|
38725
39170
|
handle = await resolved.createClient({
|
|
38726
39171
|
command: resolved.command,
|
|
38727
|
-
cwd:
|
|
39172
|
+
cwd: path43.resolve(cwd),
|
|
38728
39173
|
backendAgentType: agentType,
|
|
38729
39174
|
sessionMode: "agent",
|
|
38730
39175
|
persistedAcpSessionId: null,
|
|
@@ -38802,7 +39247,7 @@ async function warmupAgentCapabilitiesOnConnect(params) {
|
|
|
38802
39247
|
const { workspaceId, log: log2, getWs } = params;
|
|
38803
39248
|
const isCurrent = beginAgentCapabilityWarmupRun();
|
|
38804
39249
|
if (!isCurrent()) return;
|
|
38805
|
-
const cwd =
|
|
39250
|
+
const cwd = path44.resolve(getBridgeRoot());
|
|
38806
39251
|
async function sendBatchFromCache() {
|
|
38807
39252
|
if (!isCurrent()) return;
|
|
38808
39253
|
const socket = getWs();
|
|
@@ -38868,9 +39313,74 @@ async function warmupAgentCapabilitiesOnConnect(params) {
|
|
|
38868
39313
|
})();
|
|
38869
39314
|
}
|
|
38870
39315
|
|
|
38871
|
-
// src/mcp/tools/session-history/fork-
|
|
39316
|
+
// src/mcp/tools/session-history/fork/mcp-text-result.ts
|
|
39317
|
+
function mcpTextResult(text, isError = false) {
|
|
39318
|
+
return { content: [{ type: "text", text }], ...isError ? { isError: true } : {} };
|
|
39319
|
+
}
|
|
39320
|
+
|
|
39321
|
+
// src/mcp/tools/session-history/fork/call-get-parent-session-turn-transcript.ts
|
|
39322
|
+
async function callGetParentSessionTurnTranscriptTool(ctx, cloud, childSessionId, args) {
|
|
39323
|
+
const turnId = typeof args.turnId === "string" ? args.turnId.trim() : "";
|
|
39324
|
+
if (!turnId) return mcpTextResult("turnId is required.", true);
|
|
39325
|
+
const transcript = await fetchCloudParentTurnTranscript({
|
|
39326
|
+
childSessionId,
|
|
39327
|
+
turnId,
|
|
39328
|
+
cloudApiBaseUrl: cloud.cloudApiBaseUrl,
|
|
39329
|
+
getCloudAccessToken: cloud.getCloudAccessToken,
|
|
39330
|
+
e2ee: ctx.e2ee
|
|
39331
|
+
});
|
|
39332
|
+
if (!transcript.ok) return mcpTextResult(transcript.error, true);
|
|
39333
|
+
return mcpTextResult(transcript.text);
|
|
39334
|
+
}
|
|
39335
|
+
|
|
39336
|
+
// src/mcp/tools/session-history/fork/call-list-parent-session-prompts.ts
|
|
39337
|
+
async function callListParentSessionPromptsTool(ctx, cloud, childSessionId, parentSessionId) {
|
|
39338
|
+
const detail = await fetchCloudParentSessionPrompts({
|
|
39339
|
+
childSessionId,
|
|
39340
|
+
cloudApiBaseUrl: cloud.cloudApiBaseUrl,
|
|
39341
|
+
getCloudAccessToken: cloud.getCloudAccessToken,
|
|
39342
|
+
e2ee: ctx.e2ee
|
|
39343
|
+
});
|
|
39344
|
+
if (!detail.ok) return mcpTextResult(detail.error, true);
|
|
39345
|
+
const turns = Array.isArray(detail.data.turns) ? detail.data.turns : [];
|
|
39346
|
+
const payload = turns.map((t) => ({
|
|
39347
|
+
turnId: typeof t.turnId === "string" ? t.turnId : "",
|
|
39348
|
+
title: typeof t.title === "string" ? t.title : null,
|
|
39349
|
+
status: typeof t.status === "string" ? t.status : null,
|
|
39350
|
+
promptText: typeof t.promptText === "string" ? t.promptText : null
|
|
39351
|
+
}));
|
|
39352
|
+
return mcpTextResult(JSON.stringify({ parentSessionId, turns: payload }, null, 2));
|
|
39353
|
+
}
|
|
39354
|
+
|
|
39355
|
+
// src/mcp/tools/session-history/fork/tool-names.ts
|
|
38872
39356
|
var LIST_PARENT_SESSION_PROMPTS_TOOL = "list_parent_session_prompts";
|
|
38873
39357
|
var GET_PARENT_SESSION_TURN_TRANSCRIPT_TOOL = "get_parent_session_turn_transcript";
|
|
39358
|
+
|
|
39359
|
+
// src/mcp/tools/session-history/fork/call-fork-session-mcp-tool.ts
|
|
39360
|
+
async function callForkSessionMcpTool(ctx, cloud, name, args) {
|
|
39361
|
+
const childSessionId = ctx.sessionId.trim();
|
|
39362
|
+
if (!childSessionId) {
|
|
39363
|
+
return mcpTextResult("sessionId is required.", true);
|
|
39364
|
+
}
|
|
39365
|
+
const parentResolved = await resolveParentSessionIdForChild({
|
|
39366
|
+
childSessionId,
|
|
39367
|
+
cloudApiBaseUrl: cloud.cloudApiBaseUrl,
|
|
39368
|
+
getCloudAccessToken: cloud.getCloudAccessToken
|
|
39369
|
+
});
|
|
39370
|
+
if (!parentResolved.ok) {
|
|
39371
|
+
return mcpTextResult(parentResolved.error, true);
|
|
39372
|
+
}
|
|
39373
|
+
const parentSessionId = parentResolved.parentSessionId;
|
|
39374
|
+
if (name === LIST_PARENT_SESSION_PROMPTS_TOOL) {
|
|
39375
|
+
return callListParentSessionPromptsTool(ctx, cloud, childSessionId, parentSessionId);
|
|
39376
|
+
}
|
|
39377
|
+
if (name === GET_PARENT_SESSION_TURN_TRANSCRIPT_TOOL) {
|
|
39378
|
+
return callGetParentSessionTurnTranscriptTool(ctx, cloud, childSessionId, args);
|
|
39379
|
+
}
|
|
39380
|
+
return mcpTextResult(`Unknown fork session tool: ${name}`, true);
|
|
39381
|
+
}
|
|
39382
|
+
|
|
39383
|
+
// src/mcp/tools/session-history/fork/tool-definitions.ts
|
|
38874
39384
|
var SESSION_ID_SCHEMA = {
|
|
38875
39385
|
type: "string",
|
|
38876
39386
|
description: "Id of this forked (child) session \u2014 not the parent session id. Required on every tool call."
|
|
@@ -38900,57 +39410,11 @@ var FORK_SESSION_MCP_TOOL_DEFINITIONS = [
|
|
|
38900
39410
|
}
|
|
38901
39411
|
}
|
|
38902
39412
|
];
|
|
38903
|
-
|
|
38904
|
-
|
|
38905
|
-
}
|
|
39413
|
+
|
|
39414
|
+
// src/mcp/tools/session-history/fork/is-fork-session-mcp-tool-name.ts
|
|
38906
39415
|
function isForkSessionMcpToolName(name) {
|
|
38907
39416
|
return name === LIST_PARENT_SESSION_PROMPTS_TOOL || name === GET_PARENT_SESSION_TURN_TRANSCRIPT_TOOL;
|
|
38908
39417
|
}
|
|
38909
|
-
async function callForkSessionMcpTool(ctx, cloud, name, args) {
|
|
38910
|
-
const childSessionId = ctx.sessionId.trim();
|
|
38911
|
-
if (!childSessionId) {
|
|
38912
|
-
return textResult("sessionId is required.", true);
|
|
38913
|
-
}
|
|
38914
|
-
const parentResolved = await resolveParentSessionIdForChild({
|
|
38915
|
-
childSessionId,
|
|
38916
|
-
cloudApiBaseUrl: cloud.cloudApiBaseUrl,
|
|
38917
|
-
getCloudAccessToken: cloud.getCloudAccessToken
|
|
38918
|
-
});
|
|
38919
|
-
if (!parentResolved.ok) {
|
|
38920
|
-
return textResult(parentResolved.error, true);
|
|
38921
|
-
}
|
|
38922
|
-
const parentSessionId = parentResolved.parentSessionId;
|
|
38923
|
-
if (name === LIST_PARENT_SESSION_PROMPTS_TOOL) {
|
|
38924
|
-
const detail = await fetchCloudParentSessionPrompts({
|
|
38925
|
-
childSessionId,
|
|
38926
|
-
cloudApiBaseUrl: cloud.cloudApiBaseUrl,
|
|
38927
|
-
getCloudAccessToken: cloud.getCloudAccessToken
|
|
38928
|
-
});
|
|
38929
|
-
if (!detail.ok) return textResult(detail.error, true);
|
|
38930
|
-
const turns = Array.isArray(detail.data.turns) ? detail.data.turns : [];
|
|
38931
|
-
const payload = turns.map((t) => ({
|
|
38932
|
-
turnId: typeof t.turnId === "string" ? t.turnId : "",
|
|
38933
|
-
title: typeof t.title === "string" ? t.title : null,
|
|
38934
|
-
status: typeof t.status === "string" ? t.status : null,
|
|
38935
|
-
promptText: typeof t.promptText === "string" ? t.promptText : null
|
|
38936
|
-
}));
|
|
38937
|
-
return textResult(JSON.stringify({ parentSessionId, turns: payload }, null, 2));
|
|
38938
|
-
}
|
|
38939
|
-
if (name === GET_PARENT_SESSION_TURN_TRANSCRIPT_TOOL) {
|
|
38940
|
-
const turnId = typeof args.turnId === "string" ? args.turnId.trim() : "";
|
|
38941
|
-
if (!turnId) return textResult("turnId is required.", true);
|
|
38942
|
-
const transcript = await fetchCloudParentTurnTranscript({
|
|
38943
|
-
childSessionId,
|
|
38944
|
-
turnId,
|
|
38945
|
-
cloudApiBaseUrl: cloud.cloudApiBaseUrl,
|
|
38946
|
-
getCloudAccessToken: cloud.getCloudAccessToken,
|
|
38947
|
-
e2ee: ctx.e2ee
|
|
38948
|
-
});
|
|
38949
|
-
if (!transcript.ok) return textResult(transcript.error, true);
|
|
38950
|
-
return textResult(transcript.text);
|
|
38951
|
-
}
|
|
38952
|
-
return textResult(`Unknown fork session tool: ${name}`, true);
|
|
38953
|
-
}
|
|
38954
39418
|
|
|
38955
39419
|
// src/mcp/bridge-mcp-tools.ts
|
|
38956
39420
|
function requireCloud(shared) {
|
|
@@ -39207,8 +39671,8 @@ async function createBridgeConnection(options) {
|
|
|
39207
39671
|
getCloudAccessToken: () => tokens.accessToken
|
|
39208
39672
|
};
|
|
39209
39673
|
const identifyReportedPaths = {
|
|
39210
|
-
bridgeRootPath:
|
|
39211
|
-
worktreesRootPath:
|
|
39674
|
+
bridgeRootPath: path45.resolve(getBridgeRoot()),
|
|
39675
|
+
worktreesRootPath: path45.resolve(worktreesRootPath)
|
|
39212
39676
|
};
|
|
39213
39677
|
const { connect } = createMainBridgeWebSocketLifecycle({
|
|
39214
39678
|
state,
|