@buildautomaton/cli 0.1.37 → 0.1.39
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 +1201 -868
- package/dist/cli.js.map +4 -4
- package/dist/index.js +1186 -853
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4065,8 +4065,8 @@ var init_parseUtil = __esm({
|
|
|
4065
4065
|
init_errors();
|
|
4066
4066
|
init_en();
|
|
4067
4067
|
makeIssue = (params) => {
|
|
4068
|
-
const { data, path:
|
|
4069
|
-
const fullPath = [...
|
|
4068
|
+
const { data, path: path44, errorMaps, issueData } = params;
|
|
4069
|
+
const fullPath = [...path44, ...issueData.path || []];
|
|
4070
4070
|
const fullIssue = {
|
|
4071
4071
|
...issueData,
|
|
4072
4072
|
path: fullPath
|
|
@@ -4374,11 +4374,11 @@ var init_types = __esm({
|
|
|
4374
4374
|
init_parseUtil();
|
|
4375
4375
|
init_util();
|
|
4376
4376
|
ParseInputLazyPath = class {
|
|
4377
|
-
constructor(parent, value,
|
|
4377
|
+
constructor(parent, value, path44, key) {
|
|
4378
4378
|
this._cachedPath = [];
|
|
4379
4379
|
this.parent = parent;
|
|
4380
4380
|
this.data = value;
|
|
4381
|
-
this._path =
|
|
4381
|
+
this._path = path44;
|
|
4382
4382
|
this._key = key;
|
|
4383
4383
|
}
|
|
4384
4384
|
get path() {
|
|
@@ -7993,10 +7993,10 @@ function assignProp(target, prop, value) {
|
|
|
7993
7993
|
configurable: true
|
|
7994
7994
|
});
|
|
7995
7995
|
}
|
|
7996
|
-
function getElementAtPath(obj,
|
|
7997
|
-
if (!
|
|
7996
|
+
function getElementAtPath(obj, path44) {
|
|
7997
|
+
if (!path44)
|
|
7998
7998
|
return obj;
|
|
7999
|
-
return
|
|
7999
|
+
return path44.reduce((acc, key) => acc?.[key], obj);
|
|
8000
8000
|
}
|
|
8001
8001
|
function promiseAllObject(promisesObj) {
|
|
8002
8002
|
const keys = Object.keys(promisesObj);
|
|
@@ -8245,11 +8245,11 @@ function aborted(x, startIndex = 0) {
|
|
|
8245
8245
|
}
|
|
8246
8246
|
return false;
|
|
8247
8247
|
}
|
|
8248
|
-
function prefixIssues(
|
|
8248
|
+
function prefixIssues(path44, issues) {
|
|
8249
8249
|
return issues.map((iss) => {
|
|
8250
8250
|
var _a2;
|
|
8251
8251
|
(_a2 = iss).path ?? (_a2.path = []);
|
|
8252
|
-
iss.path.unshift(
|
|
8252
|
+
iss.path.unshift(path44);
|
|
8253
8253
|
return iss;
|
|
8254
8254
|
});
|
|
8255
8255
|
}
|
|
@@ -8438,7 +8438,7 @@ function treeifyError(error40, _mapper) {
|
|
|
8438
8438
|
return issue2.message;
|
|
8439
8439
|
};
|
|
8440
8440
|
const result = { errors: [] };
|
|
8441
|
-
const processError = (error41,
|
|
8441
|
+
const processError = (error41, path44 = []) => {
|
|
8442
8442
|
var _a2, _b;
|
|
8443
8443
|
for (const issue2 of error41.issues) {
|
|
8444
8444
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
@@ -8448,7 +8448,7 @@ function treeifyError(error40, _mapper) {
|
|
|
8448
8448
|
} else if (issue2.code === "invalid_element") {
|
|
8449
8449
|
processError({ issues: issue2.issues }, issue2.path);
|
|
8450
8450
|
} else {
|
|
8451
|
-
const fullpath = [...
|
|
8451
|
+
const fullpath = [...path44, ...issue2.path];
|
|
8452
8452
|
if (fullpath.length === 0) {
|
|
8453
8453
|
result.errors.push(mapper(issue2));
|
|
8454
8454
|
continue;
|
|
@@ -8478,9 +8478,9 @@ function treeifyError(error40, _mapper) {
|
|
|
8478
8478
|
processError(error40);
|
|
8479
8479
|
return result;
|
|
8480
8480
|
}
|
|
8481
|
-
function toDotPath(
|
|
8481
|
+
function toDotPath(path44) {
|
|
8482
8482
|
const segs = [];
|
|
8483
|
-
for (const seg of
|
|
8483
|
+
for (const seg of path44) {
|
|
8484
8484
|
if (typeof seg === "number")
|
|
8485
8485
|
segs.push(`[${seg}]`);
|
|
8486
8486
|
else if (typeof seg === "symbol")
|
|
@@ -20943,8 +20943,8 @@ var init_acp = __esm({
|
|
|
20943
20943
|
this.#requestHandler = requestHandler;
|
|
20944
20944
|
this.#notificationHandler = notificationHandler;
|
|
20945
20945
|
this.#stream = stream;
|
|
20946
|
-
this.#closedPromise = new Promise((
|
|
20947
|
-
this.#abortController.signal.addEventListener("abort", () =>
|
|
20946
|
+
this.#closedPromise = new Promise((resolve20) => {
|
|
20947
|
+
this.#abortController.signal.addEventListener("abort", () => resolve20());
|
|
20948
20948
|
});
|
|
20949
20949
|
this.#receive();
|
|
20950
20950
|
}
|
|
@@ -21093,8 +21093,8 @@ var init_acp = __esm({
|
|
|
21093
21093
|
}
|
|
21094
21094
|
async sendRequest(method, params) {
|
|
21095
21095
|
const id = this.#nextRequestId++;
|
|
21096
|
-
const responsePromise = new Promise((
|
|
21097
|
-
this.#pendingResponses.set(id, { resolve:
|
|
21096
|
+
const responsePromise = new Promise((resolve20, reject) => {
|
|
21097
|
+
this.#pendingResponses.set(id, { resolve: resolve20, reject });
|
|
21098
21098
|
});
|
|
21099
21099
|
await this.#sendMessage({ jsonrpc: "2.0", id, method, params });
|
|
21100
21100
|
return responsePromise;
|
|
@@ -21963,10 +21963,10 @@ var require_src2 = __commonJS({
|
|
|
21963
21963
|
var fs_1 = __require("fs");
|
|
21964
21964
|
var debug_1 = __importDefault(require_src());
|
|
21965
21965
|
var log2 = debug_1.default("@kwsites/file-exists");
|
|
21966
|
-
function check2(
|
|
21967
|
-
log2(`checking %s`,
|
|
21966
|
+
function check2(path44, isFile, isDirectory) {
|
|
21967
|
+
log2(`checking %s`, path44);
|
|
21968
21968
|
try {
|
|
21969
|
-
const stat2 = fs_1.statSync(
|
|
21969
|
+
const stat2 = fs_1.statSync(path44);
|
|
21970
21970
|
if (stat2.isFile() && isFile) {
|
|
21971
21971
|
log2(`[OK] path represents a file`);
|
|
21972
21972
|
return true;
|
|
@@ -21986,8 +21986,8 @@ var require_src2 = __commonJS({
|
|
|
21986
21986
|
throw e;
|
|
21987
21987
|
}
|
|
21988
21988
|
}
|
|
21989
|
-
function exists2(
|
|
21990
|
-
return check2(
|
|
21989
|
+
function exists2(path44, type = exports.READABLE) {
|
|
21990
|
+
return check2(path44, (type & exports.FILE) > 0, (type & exports.FOLDER) > 0);
|
|
21991
21991
|
}
|
|
21992
21992
|
exports.exists = exists2;
|
|
21993
21993
|
exports.FILE = 1;
|
|
@@ -22794,9 +22794,9 @@ function parseChangeSummaryJson(raw, allowedPaths, options) {
|
|
|
22794
22794
|
const rawPath = typeof o.path === "string" ? o.path.trim() : "";
|
|
22795
22795
|
const summary = typeof o.summary === "string" ? o.summary.trim() : "";
|
|
22796
22796
|
if (!rawPath || !summary) continue;
|
|
22797
|
-
const
|
|
22798
|
-
if (!
|
|
22799
|
-
rows.push({ path:
|
|
22797
|
+
const path44 = skip ? normalizeRepoRelativePath(rawPath) || rawPath : resolveChangeSummaryPathAgainstAllowed(rawPath, allowedPaths);
|
|
22798
|
+
if (!path44) continue;
|
|
22799
|
+
rows.push({ path: path44, summary: clampSummaryToAtMostTwoLines(summary) });
|
|
22800
22800
|
}
|
|
22801
22801
|
return rows;
|
|
22802
22802
|
}
|
|
@@ -23442,11 +23442,11 @@ async function sendAcpPromptViaTransport(transport, ctx, sessionId, promptText,
|
|
|
23442
23442
|
// src/agents/acp/clients/sdk/sdk-stdio-permission-request-handshake.ts
|
|
23443
23443
|
function awaitSdkStdioPermissionRequestHandshake(params) {
|
|
23444
23444
|
const { requestId, paramsRecord, pending, onRequest } = params;
|
|
23445
|
-
return new Promise((
|
|
23446
|
-
pending.set(requestId, { resolve:
|
|
23445
|
+
return new Promise((resolve20) => {
|
|
23446
|
+
pending.set(requestId, { resolve: resolve20, params: paramsRecord });
|
|
23447
23447
|
if (onRequest == null) {
|
|
23448
23448
|
pending.delete(requestId);
|
|
23449
|
-
|
|
23449
|
+
resolve20({ outcome: { outcome: "denied" } });
|
|
23450
23450
|
return;
|
|
23451
23451
|
}
|
|
23452
23452
|
try {
|
|
@@ -23512,7 +23512,7 @@ async function createSdkStdioAcpClient(options) {
|
|
|
23512
23512
|
child.once("close", (code, signal) => {
|
|
23513
23513
|
onAgentSubprocessExit?.({ code, signal });
|
|
23514
23514
|
});
|
|
23515
|
-
return new Promise((
|
|
23515
|
+
return new Promise((resolve20, reject) => {
|
|
23516
23516
|
let initSettled = false;
|
|
23517
23517
|
const settleReject = (err) => {
|
|
23518
23518
|
if (initSettled) return;
|
|
@@ -23526,7 +23526,7 @@ async function createSdkStdioAcpClient(options) {
|
|
|
23526
23526
|
const settleResolve = (handle) => {
|
|
23527
23527
|
if (initSettled) return;
|
|
23528
23528
|
initSettled = true;
|
|
23529
|
-
|
|
23529
|
+
resolve20(handle);
|
|
23530
23530
|
};
|
|
23531
23531
|
child.on("error", (err) => {
|
|
23532
23532
|
settleReject(new Error(formatSpawnError(err, command[0])));
|
|
@@ -23746,7 +23746,7 @@ async function proxyToLocal(request) {
|
|
|
23746
23746
|
};
|
|
23747
23747
|
const maxAttempts = isIdempotentProxyMethod(request.method) ? LOCAL_PREVIEW_FETCH_RETRY_DELAYS_MS.length + 1 : 1;
|
|
23748
23748
|
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
23749
|
-
const once = await new Promise((
|
|
23749
|
+
const once = await new Promise((resolve20) => {
|
|
23750
23750
|
const req = mod.request(opts, (res) => {
|
|
23751
23751
|
const chunks = [];
|
|
23752
23752
|
res.on("data", (c) => chunks.push(c));
|
|
@@ -23757,7 +23757,7 @@ async function proxyToLocal(request) {
|
|
|
23757
23757
|
if (typeof v === "string") headers[k] = v;
|
|
23758
23758
|
else if (Array.isArray(v) && v[0]) headers[k] = v[0];
|
|
23759
23759
|
}
|
|
23760
|
-
|
|
23760
|
+
resolve20({
|
|
23761
23761
|
id: request.id,
|
|
23762
23762
|
statusCode: res.statusCode ?? 0,
|
|
23763
23763
|
headers,
|
|
@@ -23766,7 +23766,7 @@ async function proxyToLocal(request) {
|
|
|
23766
23766
|
});
|
|
23767
23767
|
});
|
|
23768
23768
|
req.on("error", (err) => {
|
|
23769
|
-
|
|
23769
|
+
resolve20({
|
|
23770
23770
|
id: request.id,
|
|
23771
23771
|
statusCode: 0,
|
|
23772
23772
|
headers: {},
|
|
@@ -23848,8 +23848,8 @@ function randomSecret() {
|
|
|
23848
23848
|
}
|
|
23849
23849
|
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
23850
23850
|
}
|
|
23851
|
-
async function requestPreviewApi(port, secret, method,
|
|
23852
|
-
const url2 = `http://127.0.0.1:${port}${
|
|
23851
|
+
async function requestPreviewApi(port, secret, method, path44, body) {
|
|
23852
|
+
const url2 = `http://127.0.0.1:${port}${path44}`;
|
|
23853
23853
|
const headers = {
|
|
23854
23854
|
[PREVIEW_SECRET_HEADER]: secret,
|
|
23855
23855
|
"Content-Type": "application/json"
|
|
@@ -23861,7 +23861,7 @@ async function requestPreviewApi(port, secret, method, path41, body) {
|
|
|
23861
23861
|
});
|
|
23862
23862
|
const data = await res.json().catch(() => ({}));
|
|
23863
23863
|
if (!res.ok) {
|
|
23864
|
-
throw new Error(data?.error ?? `Preview API ${method} ${
|
|
23864
|
+
throw new Error(data?.error ?? `Preview API ${method} ${path44}: ${res.status}`);
|
|
23865
23865
|
}
|
|
23866
23866
|
return data;
|
|
23867
23867
|
}
|
|
@@ -24076,7 +24076,7 @@ function installBridgeProcessResilience() {
|
|
|
24076
24076
|
}
|
|
24077
24077
|
|
|
24078
24078
|
// src/cli-version.ts
|
|
24079
|
-
var CLI_VERSION = "0.1.
|
|
24079
|
+
var CLI_VERSION = "0.1.39".length > 0 ? "0.1.39" : "0.0.0-dev";
|
|
24080
24080
|
|
|
24081
24081
|
// src/connection/heartbeat/constants.ts
|
|
24082
24082
|
var BRIDGE_APP_HEARTBEAT_INTERVAL_MS = 1e4;
|
|
@@ -24517,14 +24517,14 @@ var baseOpen = async (options) => {
|
|
|
24517
24517
|
}
|
|
24518
24518
|
const subprocess = childProcess.spawn(command, cliArguments, childProcessOptions);
|
|
24519
24519
|
if (options.wait) {
|
|
24520
|
-
return new Promise((
|
|
24520
|
+
return new Promise((resolve20, reject) => {
|
|
24521
24521
|
subprocess.once("error", reject);
|
|
24522
24522
|
subprocess.once("close", (exitCode) => {
|
|
24523
24523
|
if (!options.allowNonzeroExitCode && exitCode > 0) {
|
|
24524
24524
|
reject(new Error(`Exited with code ${exitCode}`));
|
|
24525
24525
|
return;
|
|
24526
24526
|
}
|
|
24527
|
-
|
|
24527
|
+
resolve20(subprocess);
|
|
24528
24528
|
});
|
|
24529
24529
|
});
|
|
24530
24530
|
}
|
|
@@ -25013,8 +25013,8 @@ function runPendingAuth(options) {
|
|
|
25013
25013
|
let hasOpenedBrowser = false;
|
|
25014
25014
|
let resolved = false;
|
|
25015
25015
|
let resolveAuth;
|
|
25016
|
-
const authPromise = new Promise((
|
|
25017
|
-
resolveAuth =
|
|
25016
|
+
const authPromise = new Promise((resolve20) => {
|
|
25017
|
+
resolveAuth = resolve20;
|
|
25018
25018
|
});
|
|
25019
25019
|
let reconnectAttempt = 0;
|
|
25020
25020
|
const signInQuiet = createEmptyReconnectQuietSlot();
|
|
@@ -25165,7 +25165,7 @@ import sqliteWasm from "node-sqlite3-wasm";
|
|
|
25165
25165
|
|
|
25166
25166
|
// src/runtime/yield-to-event-loop.ts
|
|
25167
25167
|
function yieldToEventLoop() {
|
|
25168
|
-
return new Promise((
|
|
25168
|
+
return new Promise((resolve20) => setImmediate(resolve20));
|
|
25169
25169
|
}
|
|
25170
25170
|
|
|
25171
25171
|
// src/sqlite/cli-sqlite-paths.ts
|
|
@@ -25730,8 +25730,8 @@ function pathspec(...paths) {
|
|
|
25730
25730
|
cache.set(key, paths);
|
|
25731
25731
|
return key;
|
|
25732
25732
|
}
|
|
25733
|
-
function isPathSpec(
|
|
25734
|
-
return
|
|
25733
|
+
function isPathSpec(path44) {
|
|
25734
|
+
return path44 instanceof String && cache.has(path44);
|
|
25735
25735
|
}
|
|
25736
25736
|
function toPaths(pathSpec) {
|
|
25737
25737
|
return cache.get(pathSpec) || [];
|
|
@@ -25820,8 +25820,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
|
|
|
25820
25820
|
function forEachLineWithContent(input, callback) {
|
|
25821
25821
|
return toLinesWithContent(input, true).map((line) => callback(line));
|
|
25822
25822
|
}
|
|
25823
|
-
function folderExists(
|
|
25824
|
-
return (0, import_file_exists.exists)(
|
|
25823
|
+
function folderExists(path44) {
|
|
25824
|
+
return (0, import_file_exists.exists)(path44, import_file_exists.FOLDER);
|
|
25825
25825
|
}
|
|
25826
25826
|
function append(target, item) {
|
|
25827
25827
|
if (Array.isArray(target)) {
|
|
@@ -26225,8 +26225,8 @@ function checkIsRepoRootTask() {
|
|
|
26225
26225
|
commands,
|
|
26226
26226
|
format: "utf-8",
|
|
26227
26227
|
onError,
|
|
26228
|
-
parser(
|
|
26229
|
-
return /^\.(git)?$/.test(
|
|
26228
|
+
parser(path44) {
|
|
26229
|
+
return /^\.(git)?$/.test(path44.trim());
|
|
26230
26230
|
}
|
|
26231
26231
|
};
|
|
26232
26232
|
}
|
|
@@ -26660,11 +26660,11 @@ function parseGrep(grep) {
|
|
|
26660
26660
|
const paths = /* @__PURE__ */ new Set();
|
|
26661
26661
|
const results = {};
|
|
26662
26662
|
forEachLineWithContent(grep, (input) => {
|
|
26663
|
-
const [
|
|
26664
|
-
paths.add(
|
|
26665
|
-
(results[
|
|
26663
|
+
const [path44, line, preview] = input.split(NULL);
|
|
26664
|
+
paths.add(path44);
|
|
26665
|
+
(results[path44] = results[path44] || []).push({
|
|
26666
26666
|
line: asNumber(line),
|
|
26667
|
-
path:
|
|
26667
|
+
path: path44,
|
|
26668
26668
|
preview
|
|
26669
26669
|
});
|
|
26670
26670
|
});
|
|
@@ -27429,14 +27429,14 @@ var init_hash_object = __esm2({
|
|
|
27429
27429
|
init_task();
|
|
27430
27430
|
}
|
|
27431
27431
|
});
|
|
27432
|
-
function parseInit(bare,
|
|
27432
|
+
function parseInit(bare, path44, text) {
|
|
27433
27433
|
const response = String(text).trim();
|
|
27434
27434
|
let result;
|
|
27435
27435
|
if (result = initResponseRegex.exec(response)) {
|
|
27436
|
-
return new InitSummary(bare,
|
|
27436
|
+
return new InitSummary(bare, path44, false, result[1]);
|
|
27437
27437
|
}
|
|
27438
27438
|
if (result = reInitResponseRegex.exec(response)) {
|
|
27439
|
-
return new InitSummary(bare,
|
|
27439
|
+
return new InitSummary(bare, path44, true, result[1]);
|
|
27440
27440
|
}
|
|
27441
27441
|
let gitDir = "";
|
|
27442
27442
|
const tokens = response.split(" ");
|
|
@@ -27447,7 +27447,7 @@ function parseInit(bare, path41, text) {
|
|
|
27447
27447
|
break;
|
|
27448
27448
|
}
|
|
27449
27449
|
}
|
|
27450
|
-
return new InitSummary(bare,
|
|
27450
|
+
return new InitSummary(bare, path44, /^re/i.test(response), gitDir);
|
|
27451
27451
|
}
|
|
27452
27452
|
var InitSummary;
|
|
27453
27453
|
var initResponseRegex;
|
|
@@ -27456,9 +27456,9 @@ var init_InitSummary = __esm2({
|
|
|
27456
27456
|
"src/lib/responses/InitSummary.ts"() {
|
|
27457
27457
|
"use strict";
|
|
27458
27458
|
InitSummary = class {
|
|
27459
|
-
constructor(bare,
|
|
27459
|
+
constructor(bare, path44, existing, gitDir) {
|
|
27460
27460
|
this.bare = bare;
|
|
27461
|
-
this.path =
|
|
27461
|
+
this.path = path44;
|
|
27462
27462
|
this.existing = existing;
|
|
27463
27463
|
this.gitDir = gitDir;
|
|
27464
27464
|
}
|
|
@@ -27470,7 +27470,7 @@ var init_InitSummary = __esm2({
|
|
|
27470
27470
|
function hasBareCommand(command) {
|
|
27471
27471
|
return command.includes(bareCommand);
|
|
27472
27472
|
}
|
|
27473
|
-
function initTask(bare = false,
|
|
27473
|
+
function initTask(bare = false, path44, customArgs) {
|
|
27474
27474
|
const commands = ["init", ...customArgs];
|
|
27475
27475
|
if (bare && !hasBareCommand(commands)) {
|
|
27476
27476
|
commands.splice(1, 0, bareCommand);
|
|
@@ -27479,7 +27479,7 @@ function initTask(bare = false, path41, customArgs) {
|
|
|
27479
27479
|
commands,
|
|
27480
27480
|
format: "utf-8",
|
|
27481
27481
|
parser(text) {
|
|
27482
|
-
return parseInit(commands.includes("--bare"),
|
|
27482
|
+
return parseInit(commands.includes("--bare"), path44, text);
|
|
27483
27483
|
}
|
|
27484
27484
|
};
|
|
27485
27485
|
}
|
|
@@ -28295,12 +28295,12 @@ var init_FileStatusSummary = __esm2({
|
|
|
28295
28295
|
"use strict";
|
|
28296
28296
|
fromPathRegex = /^(.+)\0(.+)$/;
|
|
28297
28297
|
FileStatusSummary = class {
|
|
28298
|
-
constructor(
|
|
28299
|
-
this.path =
|
|
28298
|
+
constructor(path44, index, working_dir) {
|
|
28299
|
+
this.path = path44;
|
|
28300
28300
|
this.index = index;
|
|
28301
28301
|
this.working_dir = working_dir;
|
|
28302
28302
|
if (index === "R" || working_dir === "R") {
|
|
28303
|
-
const detail = fromPathRegex.exec(
|
|
28303
|
+
const detail = fromPathRegex.exec(path44) || [null, path44, path44];
|
|
28304
28304
|
this.from = detail[2] || "";
|
|
28305
28305
|
this.path = detail[1] || "";
|
|
28306
28306
|
}
|
|
@@ -28331,14 +28331,14 @@ function splitLine(result, lineStr) {
|
|
|
28331
28331
|
default:
|
|
28332
28332
|
return;
|
|
28333
28333
|
}
|
|
28334
|
-
function data(index, workingDir,
|
|
28334
|
+
function data(index, workingDir, path44) {
|
|
28335
28335
|
const raw = `${index}${workingDir}`;
|
|
28336
28336
|
const handler = parsers6.get(raw);
|
|
28337
28337
|
if (handler) {
|
|
28338
|
-
handler(result,
|
|
28338
|
+
handler(result, path44);
|
|
28339
28339
|
}
|
|
28340
28340
|
if (raw !== "##" && raw !== "!!") {
|
|
28341
|
-
result.files.push(new FileStatusSummary(
|
|
28341
|
+
result.files.push(new FileStatusSummary(path44, index, workingDir));
|
|
28342
28342
|
}
|
|
28343
28343
|
}
|
|
28344
28344
|
}
|
|
@@ -28647,9 +28647,9 @@ var init_simple_git_api = __esm2({
|
|
|
28647
28647
|
next
|
|
28648
28648
|
);
|
|
28649
28649
|
}
|
|
28650
|
-
hashObject(
|
|
28650
|
+
hashObject(path44, write) {
|
|
28651
28651
|
return this._runTask(
|
|
28652
|
-
hashObjectTask(
|
|
28652
|
+
hashObjectTask(path44, write === true),
|
|
28653
28653
|
trailingFunctionArgument(arguments)
|
|
28654
28654
|
);
|
|
28655
28655
|
}
|
|
@@ -29002,8 +29002,8 @@ var init_branch = __esm2({
|
|
|
29002
29002
|
}
|
|
29003
29003
|
});
|
|
29004
29004
|
function toPath(input) {
|
|
29005
|
-
const
|
|
29006
|
-
return
|
|
29005
|
+
const path44 = input.trim().replace(/^["']|["']$/g, "");
|
|
29006
|
+
return path44 && normalize2(path44);
|
|
29007
29007
|
}
|
|
29008
29008
|
var parseCheckIgnore;
|
|
29009
29009
|
var init_CheckIgnore = __esm2({
|
|
@@ -29317,8 +29317,8 @@ __export2(sub_module_exports, {
|
|
|
29317
29317
|
subModuleTask: () => subModuleTask,
|
|
29318
29318
|
updateSubModuleTask: () => updateSubModuleTask
|
|
29319
29319
|
});
|
|
29320
|
-
function addSubModuleTask(repo,
|
|
29321
|
-
return subModuleTask(["add", repo,
|
|
29320
|
+
function addSubModuleTask(repo, path44) {
|
|
29321
|
+
return subModuleTask(["add", repo, path44]);
|
|
29322
29322
|
}
|
|
29323
29323
|
function initSubModuleTask(customArgs) {
|
|
29324
29324
|
return subModuleTask(["init", ...customArgs]);
|
|
@@ -29651,8 +29651,8 @@ var require_git = __commonJS2({
|
|
|
29651
29651
|
}
|
|
29652
29652
|
return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
|
|
29653
29653
|
};
|
|
29654
|
-
Git2.prototype.submoduleAdd = function(repo,
|
|
29655
|
-
return this._runTask(addSubModuleTask2(repo,
|
|
29654
|
+
Git2.prototype.submoduleAdd = function(repo, path44, then) {
|
|
29655
|
+
return this._runTask(addSubModuleTask2(repo, path44), trailingFunctionArgument2(arguments));
|
|
29656
29656
|
};
|
|
29657
29657
|
Git2.prototype.submoduleUpdate = function(args, then) {
|
|
29658
29658
|
return this._runTask(
|
|
@@ -30289,8 +30289,8 @@ async function runGitTask(fn) {
|
|
|
30289
30289
|
}
|
|
30290
30290
|
async function yieldToEventLoop2() {
|
|
30291
30291
|
throwIfGitShutdownRequested();
|
|
30292
|
-
await new Promise((
|
|
30293
|
-
setImmediate(
|
|
30292
|
+
await new Promise((resolve20) => {
|
|
30293
|
+
setImmediate(resolve20);
|
|
30294
30294
|
});
|
|
30295
30295
|
throwIfGitShutdownRequested();
|
|
30296
30296
|
}
|
|
@@ -30615,9 +30615,9 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
|
|
|
30615
30615
|
// src/agents/acp/put-summarize-change-summaries.ts
|
|
30616
30616
|
async function putEncryptedChangeSummaryRows(params) {
|
|
30617
30617
|
const base = params.apiBaseUrl.replace(/\/+$/, "");
|
|
30618
|
-
const entries = params.rows.map(({ path:
|
|
30618
|
+
const entries = params.rows.map(({ path: path44, summary }) => {
|
|
30619
30619
|
const enc = params.e2ee.encryptFields({ summary }, ["summary"]);
|
|
30620
|
-
return { path:
|
|
30620
|
+
return { path: path44, summary: JSON.stringify(enc) };
|
|
30621
30621
|
});
|
|
30622
30622
|
const res = await fetch(
|
|
30623
30623
|
`${base}/api/sessions/${encodeURIComponent(params.sessionId)}/follow-ups/summarize-changes`,
|
|
@@ -31171,7 +31171,7 @@ async function createCursorAcpClient(options) {
|
|
|
31171
31171
|
logDebug,
|
|
31172
31172
|
getStderrText: () => stderrCapture.getText()
|
|
31173
31173
|
};
|
|
31174
|
-
return new Promise((
|
|
31174
|
+
return new Promise((resolve20, reject) => {
|
|
31175
31175
|
child.on("error", (err) => {
|
|
31176
31176
|
child.kill();
|
|
31177
31177
|
reject(new Error(formatSpawnError2(err, command[0])));
|
|
@@ -31347,7 +31347,7 @@ async function createCursorAcpClient(options) {
|
|
|
31347
31347
|
clientInfo: { name: "buildautomaton-cli", version: "0.1.0" }
|
|
31348
31348
|
});
|
|
31349
31349
|
const sessionId = established.sessionId;
|
|
31350
|
-
|
|
31350
|
+
resolve20({
|
|
31351
31351
|
sessionId,
|
|
31352
31352
|
async sendPrompt(prompt, options2) {
|
|
31353
31353
|
const imgs = options2?.images?.map((im) => ({ type: "image", mimeType: im.mimeType, data: im.dataBase64 }));
|
|
@@ -31639,7 +31639,7 @@ function createBridgeOnFileChange(opts) {
|
|
|
31639
31639
|
|
|
31640
31640
|
// src/agents/acp/hooks/bridge-on-request.ts
|
|
31641
31641
|
function createBridgeOnRequest(opts) {
|
|
31642
|
-
const { routing, getSendRequest, log: log2, getAutoApproveAcpPermissions, resolveAcpPermissionRequest } = opts;
|
|
31642
|
+
const { routing, getSendRequest, log: log2, getAutoApproveAcpPermissions, resolveAcpPermissionRequest, nextTranscriptStreamSeq } = opts;
|
|
31643
31643
|
return (request) => {
|
|
31644
31644
|
const runId = routing.runId;
|
|
31645
31645
|
const sessionId = routing.sessionId;
|
|
@@ -31668,6 +31668,7 @@ function createBridgeOnRequest(opts) {
|
|
|
31668
31668
|
);
|
|
31669
31669
|
}
|
|
31670
31670
|
try {
|
|
31671
|
+
const transcriptStreamSeq = nextTranscriptStreamSeq?.(runId);
|
|
31671
31672
|
sendReq({
|
|
31672
31673
|
type: "session_update",
|
|
31673
31674
|
...sessionId ? { sessionId } : {},
|
|
@@ -31678,7 +31679,8 @@ function createBridgeOnRequest(opts) {
|
|
|
31678
31679
|
requestId: request.requestId,
|
|
31679
31680
|
method: request.method,
|
|
31680
31681
|
params: request.params
|
|
31681
|
-
}
|
|
31682
|
+
},
|
|
31683
|
+
...transcriptStreamSeq != null ? { transcriptStreamSeq } : {}
|
|
31682
31684
|
});
|
|
31683
31685
|
} catch (err) {
|
|
31684
31686
|
log2(
|
|
@@ -32079,7 +32081,7 @@ function sendSessionInfoTitleUpdate(params) {
|
|
|
32079
32081
|
|
|
32080
32082
|
// src/agents/acp/hooks/bridge-on-session-update/create-bridge-on-session-update.ts
|
|
32081
32083
|
function createBridgeOnSessionUpdate(opts) {
|
|
32082
|
-
const { routing, getSendSessionUpdate, log: log2, sessionParentPath } = opts;
|
|
32084
|
+
const { routing, getSendSessionUpdate, log: log2, sessionParentPath, nextTranscriptStreamSeq } = opts;
|
|
32083
32085
|
const pathTracker = new PathSnapshotTracker();
|
|
32084
32086
|
return (params) => {
|
|
32085
32087
|
const runId = routing.runId;
|
|
@@ -32157,12 +32159,14 @@ function createBridgeOnSessionUpdate(opts) {
|
|
|
32157
32159
|
return;
|
|
32158
32160
|
}
|
|
32159
32161
|
try {
|
|
32162
|
+
const transcriptStreamSeq = nextTranscriptStreamSeq?.(runId);
|
|
32160
32163
|
send({
|
|
32161
32164
|
type: "session_update",
|
|
32162
32165
|
...sessionId ? { sessionId } : {},
|
|
32163
32166
|
runId,
|
|
32164
32167
|
kind: updateKind,
|
|
32165
|
-
payload: params
|
|
32168
|
+
payload: params,
|
|
32169
|
+
...transcriptStreamSeq != null ? { transcriptStreamSeq } : {}
|
|
32166
32170
|
});
|
|
32167
32171
|
} catch (err) {
|
|
32168
32172
|
log2(
|
|
@@ -32175,10 +32179,17 @@ function createBridgeOnSessionUpdate(opts) {
|
|
|
32175
32179
|
|
|
32176
32180
|
// src/agents/acp/hooks/build-acp-session-bridge-hooks.ts
|
|
32177
32181
|
function buildAcpSessionBridgeHooks(opts) {
|
|
32182
|
+
const transcriptStreamSeqByRunId = /* @__PURE__ */ new Map();
|
|
32183
|
+
const nextTranscriptStreamSeq = (runId) => {
|
|
32184
|
+
const n = (transcriptStreamSeqByRunId.get(runId) ?? 0) + 1;
|
|
32185
|
+
transcriptStreamSeqByRunId.set(runId, n);
|
|
32186
|
+
return n;
|
|
32187
|
+
};
|
|
32188
|
+
const optsWithSeq = { ...opts, nextTranscriptStreamSeq };
|
|
32178
32189
|
return {
|
|
32179
|
-
onFileChange: createBridgeOnFileChange(
|
|
32180
|
-
onSessionUpdate: createBridgeOnSessionUpdate(
|
|
32181
|
-
onRequest: createBridgeOnRequest(
|
|
32190
|
+
onFileChange: createBridgeOnFileChange(optsWithSeq),
|
|
32191
|
+
onSessionUpdate: createBridgeOnSessionUpdate(optsWithSeq),
|
|
32192
|
+
onRequest: createBridgeOnRequest(optsWithSeq)
|
|
32182
32193
|
};
|
|
32183
32194
|
}
|
|
32184
32195
|
|
|
@@ -32582,266 +32593,65 @@ async function createAcpManager(options) {
|
|
|
32582
32593
|
};
|
|
32583
32594
|
}
|
|
32584
32595
|
|
|
32585
|
-
// src/
|
|
32586
|
-
|
|
32587
|
-
import os8 from "node:os";
|
|
32588
|
-
|
|
32589
|
-
// src/worktrees/prepare-new-session-worktrees.ts
|
|
32590
|
-
import * as fs17 from "node:fs";
|
|
32591
|
-
import * as path20 from "node:path";
|
|
32592
|
-
|
|
32593
|
-
// src/git/worktrees/worktree-add.ts
|
|
32594
|
-
async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
|
|
32595
|
-
const mainGit = cliSimpleGit(mainRepoPath);
|
|
32596
|
-
await mainGit.raw(["worktree", "add", "-b", branch, worktreePath, "HEAD"]);
|
|
32597
|
-
}
|
|
32598
|
-
|
|
32599
|
-
// src/worktrees/worktree-layout-file.ts
|
|
32600
|
-
import * as fs16 from "node:fs";
|
|
32601
|
-
import * as path19 from "node:path";
|
|
32602
|
-
import os7 from "node:os";
|
|
32603
|
-
var LAYOUT_FILENAME = "worktree-launcher-layout.json";
|
|
32604
|
-
function defaultWorktreeLayoutPath() {
|
|
32605
|
-
return path19.join(os7.homedir(), ".buildautomaton", LAYOUT_FILENAME);
|
|
32606
|
-
}
|
|
32607
|
-
function normalizeLoadedLayout(raw) {
|
|
32608
|
-
if (raw && typeof raw === "object" && "launcherCwds" in raw) {
|
|
32609
|
-
const j = raw;
|
|
32610
|
-
if (Array.isArray(j.launcherCwds)) return { launcherCwds: j.launcherCwds };
|
|
32611
|
-
}
|
|
32612
|
-
return { launcherCwds: [] };
|
|
32613
|
-
}
|
|
32614
|
-
function loadWorktreeLayout() {
|
|
32615
|
-
try {
|
|
32616
|
-
const p = defaultWorktreeLayoutPath();
|
|
32617
|
-
if (!fs16.existsSync(p)) return { launcherCwds: [] };
|
|
32618
|
-
const raw = JSON.parse(fs16.readFileSync(p, "utf8"));
|
|
32619
|
-
return normalizeLoadedLayout(raw);
|
|
32620
|
-
} catch {
|
|
32621
|
-
return { launcherCwds: [] };
|
|
32622
|
-
}
|
|
32623
|
-
}
|
|
32624
|
-
function saveWorktreeLayout(layout) {
|
|
32625
|
-
try {
|
|
32626
|
-
const dir = path19.dirname(defaultWorktreeLayoutPath());
|
|
32627
|
-
fs16.mkdirSync(dir, { recursive: true });
|
|
32628
|
-
fs16.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
|
|
32629
|
-
} catch {
|
|
32630
|
-
}
|
|
32631
|
-
}
|
|
32632
|
-
function baseNameSafe(pathString) {
|
|
32633
|
-
return path19.basename(pathString).replace(/[^a-zA-Z0-9._-]+/g, "-") || "cwd";
|
|
32634
|
-
}
|
|
32635
|
-
function getLauncherDirNameIfPresent(layout, bridgeRootPath2) {
|
|
32636
|
-
const norm = path19.resolve(bridgeRootPath2);
|
|
32637
|
-
const existing = layout.launcherCwds.find((e) => path19.resolve(e.absolutePath) === norm);
|
|
32638
|
-
return existing?.dirName;
|
|
32639
|
-
}
|
|
32640
|
-
function allocateDirNameForLauncherCwd(layout, bridgeRootPath2) {
|
|
32641
|
-
const existing = getLauncherDirNameIfPresent(layout, bridgeRootPath2);
|
|
32642
|
-
if (existing) return existing;
|
|
32643
|
-
const norm = path19.resolve(bridgeRootPath2);
|
|
32644
|
-
const base = baseNameSafe(norm);
|
|
32645
|
-
const used = new Set(layout.launcherCwds.map((e) => e.dirName));
|
|
32646
|
-
let name = base;
|
|
32647
|
-
let n = 2;
|
|
32648
|
-
while (used.has(name)) {
|
|
32649
|
-
name = `${base}-${n}`;
|
|
32650
|
-
n += 1;
|
|
32651
|
-
}
|
|
32652
|
-
layout.launcherCwds.push({ absolutePath: norm, dirName: name });
|
|
32653
|
-
saveWorktreeLayout(layout);
|
|
32654
|
-
return name;
|
|
32655
|
-
}
|
|
32656
|
-
|
|
32657
|
-
// src/worktrees/prepare-new-session-worktrees.ts
|
|
32658
|
-
async function prepareNewSessionWorktrees(options) {
|
|
32659
|
-
const { worktreesRootPath, bridgeRoot, sessionId, layout, log: log2 } = options;
|
|
32660
|
-
const bridgeResolved = path20.resolve(bridgeRoot);
|
|
32661
|
-
const cwdKey = allocateDirNameForLauncherCwd(layout, bridgeResolved);
|
|
32662
|
-
const bridgeKeyDir = path20.join(worktreesRootPath, cwdKey);
|
|
32663
|
-
const sessionDir = path20.join(bridgeKeyDir, sessionId);
|
|
32664
|
-
const repos = await discoverGitReposUnderRoot(bridgeResolved);
|
|
32665
|
-
if (repos.length === 0) {
|
|
32666
|
-
log2("[worktrees] No Git repositories under bridge root; skipping worktree creation.");
|
|
32667
|
-
return null;
|
|
32668
|
-
}
|
|
32669
|
-
const branch = `session-${sessionId}`;
|
|
32670
|
-
const worktreePaths = [];
|
|
32671
|
-
fs17.mkdirSync(sessionDir, { recursive: true });
|
|
32672
|
-
for (const repo of repos) {
|
|
32673
|
-
let rel = path20.relative(bridgeResolved, repo.absolutePath);
|
|
32674
|
-
if (rel.startsWith("..") || path20.isAbsolute(rel)) continue;
|
|
32675
|
-
const relNorm = rel === "" ? "." : rel;
|
|
32676
|
-
const wtPath = relNorm === "." ? sessionDir : path20.join(sessionDir, relNorm);
|
|
32677
|
-
if (relNorm !== ".") {
|
|
32678
|
-
fs17.mkdirSync(path20.dirname(wtPath), { recursive: true });
|
|
32679
|
-
}
|
|
32680
|
-
try {
|
|
32681
|
-
await gitWorktreeAddBranch(repo.absolutePath, wtPath, branch);
|
|
32682
|
-
log2(`[worktrees] Added worktree ${wtPath} (branch ${branch}).`);
|
|
32683
|
-
worktreePaths.push(wtPath);
|
|
32684
|
-
} catch (e) {
|
|
32685
|
-
log2(
|
|
32686
|
-
`[worktrees] Worktree add failed for ${repo.absolutePath}: ${e instanceof Error ? e.message : String(e)}`
|
|
32687
|
-
);
|
|
32688
|
-
}
|
|
32689
|
-
}
|
|
32690
|
-
if (worktreePaths.length === 0) return null;
|
|
32691
|
-
return {
|
|
32692
|
-
worktreePaths,
|
|
32693
|
-
sessionParentPath: sessionDir,
|
|
32694
|
-
workingTreeRelRoot: sessionDir
|
|
32695
|
-
};
|
|
32696
|
-
}
|
|
32697
|
-
|
|
32698
|
-
// src/git/branches/rename-branch.ts
|
|
32699
|
-
async function gitRenameCurrentBranch(repoDir, newName) {
|
|
32700
|
-
const g = cliSimpleGit(repoDir);
|
|
32701
|
-
await g.raw(["branch", "-m", newName]);
|
|
32702
|
-
}
|
|
32703
|
-
|
|
32704
|
-
// src/worktrees/rename-session-worktree-branches.ts
|
|
32705
|
-
async function renameSessionWorktreeBranches(paths, newBranch, log2) {
|
|
32706
|
-
const safe = newBranch.replace(/[^a-zA-Z0-9/_-]+/g, "-").slice(0, 80) || "session-branch";
|
|
32707
|
-
for (const wt of paths) {
|
|
32708
|
-
try {
|
|
32709
|
-
await gitRenameCurrentBranch(wt, safe);
|
|
32710
|
-
log2(`[worktrees] Renamed branch in ${wt} \u2192 ${safe}`);
|
|
32711
|
-
} catch (e) {
|
|
32712
|
-
log2(
|
|
32713
|
-
`[worktrees] Branch rename failed in ${wt}: ${e instanceof Error ? e.message : String(e)}`
|
|
32714
|
-
);
|
|
32715
|
-
}
|
|
32716
|
-
}
|
|
32717
|
-
}
|
|
32718
|
-
|
|
32719
|
-
// src/worktrees/remove-session-worktrees.ts
|
|
32720
|
-
import * as fs20 from "node:fs";
|
|
32721
|
-
|
|
32722
|
-
// src/git/worktrees/worktree-remove.ts
|
|
32723
|
-
import * as fs19 from "node:fs";
|
|
32724
|
-
|
|
32725
|
-
// src/git/worktrees/resolve-main-repo-from-git-file.ts
|
|
32726
|
-
import * as fs18 from "node:fs";
|
|
32727
|
-
import * as path21 from "node:path";
|
|
32728
|
-
function resolveMainRepoFromWorktreeGitFile(wt) {
|
|
32729
|
-
const gitDirFile = path21.join(wt, ".git");
|
|
32730
|
-
if (!fs18.existsSync(gitDirFile) || !fs18.statSync(gitDirFile).isFile()) return "";
|
|
32731
|
-
const first2 = fs18.readFileSync(gitDirFile, "utf8").trim();
|
|
32732
|
-
const m = first2.match(/^gitdir:\s*(.+)$/im);
|
|
32733
|
-
if (!m) return "";
|
|
32734
|
-
const gitWorktreePath = path21.resolve(wt, m[1].trim());
|
|
32735
|
-
const gitDir = path21.dirname(path21.dirname(gitWorktreePath));
|
|
32736
|
-
return path21.dirname(gitDir);
|
|
32737
|
-
}
|
|
32596
|
+
// src/git/changes/types.ts
|
|
32597
|
+
var MAX_PATCH_CHARS = 35e4;
|
|
32738
32598
|
|
|
32739
|
-
// src/git/
|
|
32740
|
-
|
|
32741
|
-
const
|
|
32742
|
-
|
|
32743
|
-
|
|
32744
|
-
} else {
|
|
32745
|
-
fs19.rmSync(worktreePath, { recursive: true, force: true });
|
|
32746
|
-
}
|
|
32599
|
+
// src/git/changes/lib/repo-format.ts
|
|
32600
|
+
function posixJoinDirFile(dir, file2) {
|
|
32601
|
+
const d = dir === "." || dir === "" ? "" : dir.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
32602
|
+
const f = file2.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
32603
|
+
return d ? `${d}/${f}` : f;
|
|
32747
32604
|
}
|
|
32748
|
-
|
|
32749
|
-
|
|
32750
|
-
|
|
32751
|
-
for (const wt of paths) {
|
|
32605
|
+
function formatRepoShortTitle(remoteUrl, repoRelPath) {
|
|
32606
|
+
const u = remoteUrl.trim();
|
|
32607
|
+
if (u) {
|
|
32752
32608
|
try {
|
|
32753
|
-
|
|
32754
|
-
|
|
32755
|
-
|
|
32756
|
-
|
|
32757
|
-
|
|
32758
|
-
|
|
32759
|
-
}
|
|
32609
|
+
if (u.startsWith("git@")) {
|
|
32610
|
+
const colon = u.indexOf(":");
|
|
32611
|
+
if (colon > 0) {
|
|
32612
|
+
const pathPart = u.slice(colon + 1).replace(/\.git$/i, "").replace(/\/+$/, "");
|
|
32613
|
+
if (pathPart.includes("/")) return pathPart;
|
|
32614
|
+
}
|
|
32615
|
+
} else {
|
|
32616
|
+
const parsed = new URL(u);
|
|
32617
|
+
const p = parsed.pathname.replace(/^\//, "").replace(/\.git$/i, "");
|
|
32618
|
+
const parts = p.split("/").filter(Boolean);
|
|
32619
|
+
if (parts.length >= 2) {
|
|
32620
|
+
return `${parts[parts.length - 2]}/${parts[parts.length - 1]}`;
|
|
32621
|
+
}
|
|
32622
|
+
if (parts.length === 1) return parts[0];
|
|
32760
32623
|
}
|
|
32624
|
+
} catch {
|
|
32761
32625
|
}
|
|
32762
32626
|
}
|
|
32763
|
-
|
|
32764
|
-
|
|
32765
|
-
|
|
32766
|
-
|
|
32767
|
-
const m = /* @__PURE__ */ new Map();
|
|
32768
|
-
for (const line of lines) {
|
|
32769
|
-
if (!line.trim()) continue;
|
|
32770
|
-
const tabParts = line.split(" ");
|
|
32771
|
-
if (tabParts.length < 2) continue;
|
|
32772
|
-
const status = tabParts[0].trim();
|
|
32773
|
-
const code = status[0];
|
|
32774
|
-
if (code === "A") {
|
|
32775
|
-
m.set(tabParts[tabParts.length - 1], "added");
|
|
32776
|
-
} else if (code === "D") {
|
|
32777
|
-
m.set(tabParts[tabParts.length - 1], "removed");
|
|
32778
|
-
} else if (code === "R" || code === "C") {
|
|
32779
|
-
if (tabParts.length >= 3) m.set(tabParts[tabParts.length - 1], "modified");
|
|
32780
|
-
} else if (code === "M" || code === "U" || code === "T") {
|
|
32781
|
-
m.set(tabParts[tabParts.length - 1], "modified");
|
|
32782
|
-
}
|
|
32783
|
-
}
|
|
32784
|
-
return m;
|
|
32785
|
-
}
|
|
32786
|
-
function parseNumstatFirstLine(line) {
|
|
32787
|
-
const parts = line.split(" ");
|
|
32788
|
-
if (parts.length < 3) return null;
|
|
32789
|
-
const [a, d] = parts;
|
|
32790
|
-
const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
|
|
32791
|
-
const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
|
|
32792
|
-
return { additions, deletions };
|
|
32793
|
-
}
|
|
32794
|
-
function parseNumstat(lines) {
|
|
32795
|
-
const m = /* @__PURE__ */ new Map();
|
|
32796
|
-
for (const line of lines) {
|
|
32797
|
-
if (!line.trim()) continue;
|
|
32798
|
-
const parts = line.split(" ");
|
|
32799
|
-
if (parts.length < 3) continue;
|
|
32800
|
-
const [a, d, p] = parts;
|
|
32801
|
-
const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
|
|
32802
|
-
const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
|
|
32803
|
-
m.set(p, { additions, deletions });
|
|
32627
|
+
if (repoRelPath && repoRelPath !== ".") {
|
|
32628
|
+
const segments = repoRelPath.split("/").filter(Boolean);
|
|
32629
|
+
const last2 = segments[segments.length - 1];
|
|
32630
|
+
if (last2) return last2;
|
|
32804
32631
|
}
|
|
32805
|
-
return
|
|
32632
|
+
return "Repository";
|
|
32806
32633
|
}
|
|
32807
|
-
|
|
32808
|
-
const
|
|
32634
|
+
function formatRemoteDisplayLabel(remoteUrl) {
|
|
32635
|
+
const u = remoteUrl.trim();
|
|
32636
|
+
if (!u) return "";
|
|
32637
|
+
let hostPath = u;
|
|
32809
32638
|
try {
|
|
32810
|
-
|
|
32811
|
-
|
|
32812
|
-
|
|
32639
|
+
if (u.startsWith("git@")) {
|
|
32640
|
+
const rest = u.slice("git@".length);
|
|
32641
|
+
const slash = rest.indexOf(":");
|
|
32642
|
+
if (slash > 0) hostPath = `${rest.slice(0, slash)}/${rest.slice(slash + 1)}`;
|
|
32643
|
+
} else {
|
|
32644
|
+
const parsed = new URL(u);
|
|
32645
|
+
hostPath = `${parsed.hostname}${parsed.pathname}`.replace(/\/\.git$/i, "").replace(/\.git$/i, "");
|
|
32646
|
+
}
|
|
32813
32647
|
} catch {
|
|
32814
|
-
|
|
32815
|
-
}
|
|
32816
|
-
}
|
|
32817
|
-
|
|
32818
|
-
// src/git/changes/lib/working-tree-changed-path-count.ts
|
|
32819
|
-
function workingTreeChangedPathCount(nameStatusLines, numstatLines, untrackedLines) {
|
|
32820
|
-
const kindByPath = parseNameStatusLines(nameStatusLines);
|
|
32821
|
-
const numByPath = parseNumstat(numstatLines);
|
|
32822
|
-
const paths = /* @__PURE__ */ new Set([...kindByPath.keys(), ...numByPath.keys()]);
|
|
32823
|
-
for (const p of untrackedLines.map((s) => s.trim()).filter(Boolean)) {
|
|
32824
|
-
paths.add(p);
|
|
32648
|
+
hostPath = u.replace(/^https?:\/\//i, "").replace(/\.git$/i, "");
|
|
32825
32649
|
}
|
|
32826
|
-
return
|
|
32650
|
+
return `origin \xB7 ${hostPath}`;
|
|
32827
32651
|
}
|
|
32828
32652
|
|
|
32829
|
-
// src/git/changes/
|
|
32830
|
-
|
|
32831
|
-
return runGitTask(async () => {
|
|
32832
|
-
const g = cliSimpleGit(repoGitCwd);
|
|
32833
|
-
const [nameStatusRaw, numstatRaw, untrackedRaw] = await Promise.all([
|
|
32834
|
-
g.raw(["diff", "--name-status", "HEAD"]).catch(() => ""),
|
|
32835
|
-
g.raw(["diff", "HEAD", "--numstat"]).catch(() => ""),
|
|
32836
|
-
g.raw(["ls-files", "--others", "--exclude-standard"]).catch(() => "")
|
|
32837
|
-
]);
|
|
32838
|
-
return workingTreeChangedPathCount(
|
|
32839
|
-
String(nameStatusRaw).split("\n"),
|
|
32840
|
-
String(numstatRaw).split("\n"),
|
|
32841
|
-
String(untrackedRaw).split("\n")
|
|
32842
|
-
);
|
|
32843
|
-
});
|
|
32844
|
-
}
|
|
32653
|
+
// src/git/changes/get-working-tree-change-repo-details.ts
|
|
32654
|
+
import * as path20 from "node:path";
|
|
32845
32655
|
|
|
32846
32656
|
// src/git/commits/resolve-remote-tracking.ts
|
|
32847
32657
|
async function tryConfigGet(g, key) {
|
|
@@ -32941,96 +32751,6 @@ async function commitsAheadOfRemoteTracking(repoDir) {
|
|
|
32941
32751
|
}
|
|
32942
32752
|
}
|
|
32943
32753
|
|
|
32944
|
-
// src/git/status/working-tree-status.ts
|
|
32945
|
-
async function getRepoWorkingTreeStatus(repoDir) {
|
|
32946
|
-
return runGitTask(async () => {
|
|
32947
|
-
const uncommittedFileCount = await countWorkingTreeChangedFilesForRepo(repoDir);
|
|
32948
|
-
const hasUncommittedChanges = uncommittedFileCount > 0;
|
|
32949
|
-
const ahead = await commitsAheadOfRemoteTracking(repoDir);
|
|
32950
|
-
return { hasUncommittedChanges, hasUnpushedCommits: ahead > 0, uncommittedFileCount };
|
|
32951
|
-
});
|
|
32952
|
-
}
|
|
32953
|
-
async function aggregateSessionPathsWorkingTreeStatus(paths) {
|
|
32954
|
-
let hasUncommittedChanges = false;
|
|
32955
|
-
let hasUnpushedCommits = false;
|
|
32956
|
-
let uncommittedFileCount = 0;
|
|
32957
|
-
await forEachWithGitYield(paths, async (p) => {
|
|
32958
|
-
const s = await getRepoWorkingTreeStatus(p);
|
|
32959
|
-
uncommittedFileCount += s.uncommittedFileCount;
|
|
32960
|
-
if (s.hasUncommittedChanges) hasUncommittedChanges = true;
|
|
32961
|
-
if (s.hasUnpushedCommits) hasUnpushedCommits = true;
|
|
32962
|
-
});
|
|
32963
|
-
return { hasUncommittedChanges, hasUnpushedCommits, uncommittedFileCount };
|
|
32964
|
-
}
|
|
32965
|
-
async function pushAheadOfUpstreamForPaths(paths) {
|
|
32966
|
-
await forEachWithGitYield(paths, async (p) => {
|
|
32967
|
-
const g = cliSimpleGit(p);
|
|
32968
|
-
const ahead = await commitsAheadOfRemoteTracking(p);
|
|
32969
|
-
if (ahead <= 0) return;
|
|
32970
|
-
await g.push();
|
|
32971
|
-
});
|
|
32972
|
-
}
|
|
32973
|
-
|
|
32974
|
-
// src/git/changes/types.ts
|
|
32975
|
-
var MAX_PATCH_CHARS = 35e4;
|
|
32976
|
-
|
|
32977
|
-
// src/git/changes/lib/repo-format.ts
|
|
32978
|
-
function posixJoinDirFile(dir, file2) {
|
|
32979
|
-
const d = dir === "." || dir === "" ? "" : dir.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
32980
|
-
const f = file2.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
32981
|
-
return d ? `${d}/${f}` : f;
|
|
32982
|
-
}
|
|
32983
|
-
function formatRepoShortTitle(remoteUrl, repoRelPath) {
|
|
32984
|
-
const u = remoteUrl.trim();
|
|
32985
|
-
if (u) {
|
|
32986
|
-
try {
|
|
32987
|
-
if (u.startsWith("git@")) {
|
|
32988
|
-
const colon = u.indexOf(":");
|
|
32989
|
-
if (colon > 0) {
|
|
32990
|
-
const pathPart = u.slice(colon + 1).replace(/\.git$/i, "").replace(/\/+$/, "");
|
|
32991
|
-
if (pathPart.includes("/")) return pathPart;
|
|
32992
|
-
}
|
|
32993
|
-
} else {
|
|
32994
|
-
const parsed = new URL(u);
|
|
32995
|
-
const p = parsed.pathname.replace(/^\//, "").replace(/\.git$/i, "");
|
|
32996
|
-
const parts = p.split("/").filter(Boolean);
|
|
32997
|
-
if (parts.length >= 2) {
|
|
32998
|
-
return `${parts[parts.length - 2]}/${parts[parts.length - 1]}`;
|
|
32999
|
-
}
|
|
33000
|
-
if (parts.length === 1) return parts[0];
|
|
33001
|
-
}
|
|
33002
|
-
} catch {
|
|
33003
|
-
}
|
|
33004
|
-
}
|
|
33005
|
-
if (repoRelPath && repoRelPath !== ".") {
|
|
33006
|
-
const segments = repoRelPath.split("/").filter(Boolean);
|
|
33007
|
-
const last2 = segments[segments.length - 1];
|
|
33008
|
-
if (last2) return last2;
|
|
33009
|
-
}
|
|
33010
|
-
return "Repository";
|
|
33011
|
-
}
|
|
33012
|
-
function formatRemoteDisplayLabel(remoteUrl) {
|
|
33013
|
-
const u = remoteUrl.trim();
|
|
33014
|
-
if (!u) return "";
|
|
33015
|
-
let hostPath = u;
|
|
33016
|
-
try {
|
|
33017
|
-
if (u.startsWith("git@")) {
|
|
33018
|
-
const rest = u.slice("git@".length);
|
|
33019
|
-
const slash = rest.indexOf(":");
|
|
33020
|
-
if (slash > 0) hostPath = `${rest.slice(0, slash)}/${rest.slice(slash + 1)}`;
|
|
33021
|
-
} else {
|
|
33022
|
-
const parsed = new URL(u);
|
|
33023
|
-
hostPath = `${parsed.hostname}${parsed.pathname}`.replace(/\/\.git$/i, "").replace(/\.git$/i, "");
|
|
33024
|
-
}
|
|
33025
|
-
} catch {
|
|
33026
|
-
hostPath = u.replace(/^https?:\/\//i, "").replace(/\.git$/i, "");
|
|
33027
|
-
}
|
|
33028
|
-
return `origin \xB7 ${hostPath}`;
|
|
33029
|
-
}
|
|
33030
|
-
|
|
33031
|
-
// src/git/changes/get-working-tree-change-repo-details.ts
|
|
33032
|
-
import * as path23 from "node:path";
|
|
33033
|
-
|
|
33034
32754
|
// src/git/commits/lib/parse-log-lines.ts
|
|
33035
32755
|
function parseLogShaDateSubjectLines(raw) {
|
|
33036
32756
|
const out = [];
|
|
@@ -33101,6 +32821,59 @@ async function listRecentCommits(repoDir, limitInput) {
|
|
|
33101
32821
|
}
|
|
33102
32822
|
}
|
|
33103
32823
|
|
|
32824
|
+
// src/git/changes/lib/parse-git-status.ts
|
|
32825
|
+
function parseNameStatusLines(lines) {
|
|
32826
|
+
const m = /* @__PURE__ */ new Map();
|
|
32827
|
+
for (const line of lines) {
|
|
32828
|
+
if (!line.trim()) continue;
|
|
32829
|
+
const tabParts = line.split(" ");
|
|
32830
|
+
if (tabParts.length < 2) continue;
|
|
32831
|
+
const status = tabParts[0].trim();
|
|
32832
|
+
const code = status[0];
|
|
32833
|
+
if (code === "A") {
|
|
32834
|
+
m.set(tabParts[tabParts.length - 1], "added");
|
|
32835
|
+
} else if (code === "D") {
|
|
32836
|
+
m.set(tabParts[tabParts.length - 1], "removed");
|
|
32837
|
+
} else if (code === "R" || code === "C") {
|
|
32838
|
+
if (tabParts.length >= 3) m.set(tabParts[tabParts.length - 1], "modified");
|
|
32839
|
+
} else if (code === "M" || code === "U" || code === "T") {
|
|
32840
|
+
m.set(tabParts[tabParts.length - 1], "modified");
|
|
32841
|
+
}
|
|
32842
|
+
}
|
|
32843
|
+
return m;
|
|
32844
|
+
}
|
|
32845
|
+
function parseNumstatFirstLine(line) {
|
|
32846
|
+
const parts = line.split(" ");
|
|
32847
|
+
if (parts.length < 3) return null;
|
|
32848
|
+
const [a, d] = parts;
|
|
32849
|
+
const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
|
|
32850
|
+
const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
|
|
32851
|
+
return { additions, deletions };
|
|
32852
|
+
}
|
|
32853
|
+
function parseNumstat(lines) {
|
|
32854
|
+
const m = /* @__PURE__ */ new Map();
|
|
32855
|
+
for (const line of lines) {
|
|
32856
|
+
if (!line.trim()) continue;
|
|
32857
|
+
const parts = line.split(" ");
|
|
32858
|
+
if (parts.length < 3) continue;
|
|
32859
|
+
const [a, d, p] = parts;
|
|
32860
|
+
const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
|
|
32861
|
+
const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
|
|
32862
|
+
m.set(p, { additions, deletions });
|
|
32863
|
+
}
|
|
32864
|
+
return m;
|
|
32865
|
+
}
|
|
32866
|
+
async function numstatFromGitNoIndex(g, pathInRepo) {
|
|
32867
|
+
const devNull = process.platform === "win32" ? "NUL" : "/dev/null";
|
|
32868
|
+
try {
|
|
32869
|
+
const out = await g.raw(["diff", "--numstat", "--no-index", "--", devNull, pathInRepo]);
|
|
32870
|
+
const first2 = String(out).split("\n").find((l) => l.trim()) ?? "";
|
|
32871
|
+
return parseNumstatFirstLine(first2);
|
|
32872
|
+
} catch {
|
|
32873
|
+
return null;
|
|
32874
|
+
}
|
|
32875
|
+
}
|
|
32876
|
+
|
|
33104
32877
|
// src/git/changes/lib/patch-truncate.ts
|
|
33105
32878
|
function truncatePatch(s) {
|
|
33106
32879
|
if (s.length <= MAX_PATCH_CHARS) return s;
|
|
@@ -33166,8 +32939,8 @@ async function listChangedFilesForCommit(repoGitCwd, repoRelPath, commitSha) {
|
|
|
33166
32939
|
}
|
|
33167
32940
|
|
|
33168
32941
|
// src/git/changes/list-changed-files-for-repo.ts
|
|
33169
|
-
import * as
|
|
33170
|
-
import * as
|
|
32942
|
+
import * as fs17 from "node:fs";
|
|
32943
|
+
import * as path19 from "node:path";
|
|
33171
32944
|
|
|
33172
32945
|
// src/git/changes/lib/count-lines.ts
|
|
33173
32946
|
import { createReadStream } from "node:fs";
|
|
@@ -33191,7 +32964,7 @@ async function countTextFileLines(filePath) {
|
|
|
33191
32964
|
}
|
|
33192
32965
|
|
|
33193
32966
|
// src/git/changes/hydrate-patch.ts
|
|
33194
|
-
import * as
|
|
32967
|
+
import * as fs16 from "node:fs";
|
|
33195
32968
|
var UNIFIED_HUNK_HEADER_RE = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
|
|
33196
32969
|
var MAX_HYDRATE_LINES_PER_GAP = 8e3;
|
|
33197
32970
|
var MAX_HYDRATE_LINES_PER_FILE = 8e4;
|
|
@@ -33206,7 +32979,7 @@ async function readGitBlobLines(repoCwd, pathInRepo) {
|
|
|
33206
32979
|
}
|
|
33207
32980
|
async function readWorktreeFileLines(filePath) {
|
|
33208
32981
|
try {
|
|
33209
|
-
const raw = await
|
|
32982
|
+
const raw = await fs16.promises.readFile(filePath, "utf8");
|
|
33210
32983
|
return raw.split(/\r?\n/);
|
|
33211
32984
|
} catch {
|
|
33212
32985
|
return null;
|
|
@@ -33331,7 +33104,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
|
33331
33104
|
const rows = [];
|
|
33332
33105
|
await forEachWithGitYield([...paths], async (pathInRepo) => {
|
|
33333
33106
|
const relLauncher = posixJoinDirFile(repoRelPath, pathInRepo.replace(/\\/g, "/"));
|
|
33334
|
-
const repoFilePath =
|
|
33107
|
+
const repoFilePath = path19.join(repoGitCwd, pathInRepo);
|
|
33335
33108
|
const nums = numByPath.get(pathInRepo);
|
|
33336
33109
|
let additions = nums?.additions ?? 0;
|
|
33337
33110
|
let deletions = nums?.deletions ?? 0;
|
|
@@ -33344,7 +33117,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
|
33344
33117
|
deletions = fromGit.deletions;
|
|
33345
33118
|
} else {
|
|
33346
33119
|
try {
|
|
33347
|
-
const st = await
|
|
33120
|
+
const st = await fs17.promises.stat(repoFilePath);
|
|
33348
33121
|
if (st.isFile()) additions = await countTextFileLines(repoFilePath);
|
|
33349
33122
|
else additions = 0;
|
|
33350
33123
|
} catch {
|
|
@@ -33370,7 +33143,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
|
|
|
33370
33143
|
} else {
|
|
33371
33144
|
pathInRepo = row.pathRelLauncher;
|
|
33372
33145
|
}
|
|
33373
|
-
const filePath =
|
|
33146
|
+
const filePath = path19.join(repoGitCwd, pathInRepo);
|
|
33374
33147
|
let patch = await unifiedDiffForFile(repoGitCwd, pathInRepo, row.change);
|
|
33375
33148
|
if (patch) {
|
|
33376
33149
|
patch = await hydrateUnifiedPatchWithFileContext(patch, filePath, repoGitCwd, pathInRepo, row.change);
|
|
@@ -33386,8 +33159,8 @@ function normRepoRel(p) {
|
|
|
33386
33159
|
return x === "" ? "." : x;
|
|
33387
33160
|
}
|
|
33388
33161
|
async function getWorkingTreeChangeRepoDetails(options) {
|
|
33389
|
-
const bridgeRoot =
|
|
33390
|
-
const sessionWtRoot = options.sessionWorktreeRootPath ?
|
|
33162
|
+
const bridgeRoot = path20.resolve(getBridgeRoot());
|
|
33163
|
+
const sessionWtRoot = options.sessionWorktreeRootPath ? path20.resolve(options.sessionWorktreeRootPath) : null;
|
|
33391
33164
|
const legacyNested = options.legacyRepoNestedSessionLayout === true;
|
|
33392
33165
|
const out = [];
|
|
33393
33166
|
const filter = options.repoFilterRelPath != null ? normRepoRel(options.repoFilterRelPath) : null;
|
|
@@ -33402,7 +33175,7 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
33402
33175
|
for (let i = 0; i < options.commitTargetPaths.length; i++) {
|
|
33403
33176
|
if (i > 0) await yieldToEventLoop2();
|
|
33404
33177
|
const target = options.commitTargetPaths[i];
|
|
33405
|
-
const t =
|
|
33178
|
+
const t = path20.resolve(target);
|
|
33406
33179
|
if (!await isGitRepoDirectory(t)) continue;
|
|
33407
33180
|
const g = cliSimpleGit(t);
|
|
33408
33181
|
let branch = "HEAD";
|
|
@@ -33415,8 +33188,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
33415
33188
|
const remoteDisplay = formatRemoteDisplayLabel(remoteUrl);
|
|
33416
33189
|
let repoRelPath;
|
|
33417
33190
|
if (sessionWtRoot) {
|
|
33418
|
-
const anchor = legacyNested ?
|
|
33419
|
-
const relNorm =
|
|
33191
|
+
const anchor = legacyNested ? path20.dirname(t) : t;
|
|
33192
|
+
const relNorm = path20.relative(sessionWtRoot, anchor);
|
|
33420
33193
|
repoRelPath = relNorm === "" ? "." : relNorm.replace(/\\/g, "/");
|
|
33421
33194
|
} else {
|
|
33422
33195
|
let top = t;
|
|
@@ -33425,8 +33198,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
33425
33198
|
} catch {
|
|
33426
33199
|
top = t;
|
|
33427
33200
|
}
|
|
33428
|
-
const rel =
|
|
33429
|
-
repoRelPath = rel.startsWith("..") ?
|
|
33201
|
+
const rel = path20.relative(bridgeRoot, path20.resolve(top)).replace(/\\/g, "/") || ".";
|
|
33202
|
+
repoRelPath = rel.startsWith("..") ? path20.basename(path20.resolve(top)) : rel;
|
|
33430
33203
|
}
|
|
33431
33204
|
const norm = normRepoRel(repoRelPath === "" ? "." : repoRelPath);
|
|
33432
33205
|
if (filter && norm !== filter) continue;
|
|
@@ -33458,6 +33231,64 @@ async function getWorkingTreeChangeRepoDetails(options) {
|
|
|
33458
33231
|
return out;
|
|
33459
33232
|
}
|
|
33460
33233
|
|
|
33234
|
+
// src/git/changes/lib/working-tree-changed-path-count.ts
|
|
33235
|
+
function workingTreeChangedPathCount(nameStatusLines, numstatLines, untrackedLines) {
|
|
33236
|
+
const kindByPath = parseNameStatusLines(nameStatusLines);
|
|
33237
|
+
const numByPath = parseNumstat(numstatLines);
|
|
33238
|
+
const paths = /* @__PURE__ */ new Set([...kindByPath.keys(), ...numByPath.keys()]);
|
|
33239
|
+
for (const p of untrackedLines.map((s) => s.trim()).filter(Boolean)) {
|
|
33240
|
+
paths.add(p);
|
|
33241
|
+
}
|
|
33242
|
+
return paths.size;
|
|
33243
|
+
}
|
|
33244
|
+
|
|
33245
|
+
// src/git/changes/count-working-tree-changed-files.ts
|
|
33246
|
+
async function countWorkingTreeChangedFilesForRepo(repoGitCwd) {
|
|
33247
|
+
return runGitTask(async () => {
|
|
33248
|
+
const g = cliSimpleGit(repoGitCwd);
|
|
33249
|
+
const [nameStatusRaw, numstatRaw, untrackedRaw] = await Promise.all([
|
|
33250
|
+
g.raw(["diff", "--name-status", "HEAD"]).catch(() => ""),
|
|
33251
|
+
g.raw(["diff", "HEAD", "--numstat"]).catch(() => ""),
|
|
33252
|
+
g.raw(["ls-files", "--others", "--exclude-standard"]).catch(() => "")
|
|
33253
|
+
]);
|
|
33254
|
+
return workingTreeChangedPathCount(
|
|
33255
|
+
String(nameStatusRaw).split("\n"),
|
|
33256
|
+
String(numstatRaw).split("\n"),
|
|
33257
|
+
String(untrackedRaw).split("\n")
|
|
33258
|
+
);
|
|
33259
|
+
});
|
|
33260
|
+
}
|
|
33261
|
+
|
|
33262
|
+
// src/git/status/working-tree-status.ts
|
|
33263
|
+
async function getRepoWorkingTreeStatus(repoDir) {
|
|
33264
|
+
return runGitTask(async () => {
|
|
33265
|
+
const uncommittedFileCount = await countWorkingTreeChangedFilesForRepo(repoDir);
|
|
33266
|
+
const hasUncommittedChanges = uncommittedFileCount > 0;
|
|
33267
|
+
const ahead = await commitsAheadOfRemoteTracking(repoDir);
|
|
33268
|
+
return { hasUncommittedChanges, hasUnpushedCommits: ahead > 0, uncommittedFileCount };
|
|
33269
|
+
});
|
|
33270
|
+
}
|
|
33271
|
+
async function aggregateSessionPathsWorkingTreeStatus(paths) {
|
|
33272
|
+
let hasUncommittedChanges = false;
|
|
33273
|
+
let hasUnpushedCommits = false;
|
|
33274
|
+
let uncommittedFileCount = 0;
|
|
33275
|
+
await forEachWithGitYield(paths, async (p) => {
|
|
33276
|
+
const s = await getRepoWorkingTreeStatus(p);
|
|
33277
|
+
uncommittedFileCount += s.uncommittedFileCount;
|
|
33278
|
+
if (s.hasUncommittedChanges) hasUncommittedChanges = true;
|
|
33279
|
+
if (s.hasUnpushedCommits) hasUnpushedCommits = true;
|
|
33280
|
+
});
|
|
33281
|
+
return { hasUncommittedChanges, hasUnpushedCommits, uncommittedFileCount };
|
|
33282
|
+
}
|
|
33283
|
+
async function pushAheadOfUpstreamForPaths(paths) {
|
|
33284
|
+
await forEachWithGitYield(paths, async (p) => {
|
|
33285
|
+
const g = cliSimpleGit(p);
|
|
33286
|
+
const ahead = await commitsAheadOfRemoteTracking(p);
|
|
33287
|
+
if (ahead <= 0) return;
|
|
33288
|
+
await g.push();
|
|
33289
|
+
});
|
|
33290
|
+
}
|
|
33291
|
+
|
|
33461
33292
|
// src/git/branches/commit-and-push.ts
|
|
33462
33293
|
async function gitCommitAllIfDirty(repoDir, message, options) {
|
|
33463
33294
|
const g = cliSimpleGit(repoDir);
|
|
@@ -33495,12 +33326,137 @@ async function commitSessionWorktrees(options) {
|
|
|
33495
33326
|
}
|
|
33496
33327
|
}
|
|
33497
33328
|
|
|
33329
|
+
// src/worktrees/remove-session-worktrees.ts
|
|
33330
|
+
import * as fs20 from "node:fs";
|
|
33331
|
+
|
|
33332
|
+
// src/git/worktrees/worktree-remove.ts
|
|
33333
|
+
import * as fs19 from "node:fs";
|
|
33334
|
+
|
|
33335
|
+
// src/git/worktrees/resolve-main-repo-from-git-file.ts
|
|
33336
|
+
import * as fs18 from "node:fs";
|
|
33337
|
+
import * as path21 from "node:path";
|
|
33338
|
+
function resolveMainRepoFromWorktreeGitFile(wt) {
|
|
33339
|
+
const gitDirFile = path21.join(wt, ".git");
|
|
33340
|
+
if (!fs18.existsSync(gitDirFile) || !fs18.statSync(gitDirFile).isFile()) return "";
|
|
33341
|
+
const first2 = fs18.readFileSync(gitDirFile, "utf8").trim();
|
|
33342
|
+
const m = first2.match(/^gitdir:\s*(.+)$/im);
|
|
33343
|
+
if (!m) return "";
|
|
33344
|
+
const gitWorktreePath = path21.resolve(wt, m[1].trim());
|
|
33345
|
+
const gitDir = path21.dirname(path21.dirname(gitWorktreePath));
|
|
33346
|
+
return path21.dirname(gitDir);
|
|
33347
|
+
}
|
|
33348
|
+
|
|
33349
|
+
// src/git/worktrees/worktree-remove.ts
|
|
33350
|
+
async function gitWorktreeRemoveForce(worktreePath) {
|
|
33351
|
+
const mainRepo = resolveMainRepoFromWorktreeGitFile(worktreePath);
|
|
33352
|
+
if (mainRepo) {
|
|
33353
|
+
await cliSimpleGit(mainRepo).raw(["worktree", "remove", "--force", worktreePath]);
|
|
33354
|
+
} else {
|
|
33355
|
+
fs19.rmSync(worktreePath, { recursive: true, force: true });
|
|
33356
|
+
}
|
|
33357
|
+
}
|
|
33358
|
+
|
|
33359
|
+
// src/worktrees/remove-session-worktrees.ts
|
|
33360
|
+
async function removeSessionWorktrees(paths, log2) {
|
|
33361
|
+
for (const wt of paths) {
|
|
33362
|
+
try {
|
|
33363
|
+
await gitWorktreeRemoveForce(wt);
|
|
33364
|
+
log2(`[worktrees] Removed worktree ${wt}`);
|
|
33365
|
+
} catch (e) {
|
|
33366
|
+
log2(`[worktrees] Remove failed for ${wt}: ${e instanceof Error ? e.message : String(e)}`);
|
|
33367
|
+
try {
|
|
33368
|
+
fs20.rmSync(wt, { recursive: true, force: true });
|
|
33369
|
+
} catch {
|
|
33370
|
+
}
|
|
33371
|
+
}
|
|
33372
|
+
}
|
|
33373
|
+
}
|
|
33374
|
+
|
|
33375
|
+
// src/git/branches/rename-branch.ts
|
|
33376
|
+
async function gitRenameCurrentBranch(repoDir, newName) {
|
|
33377
|
+
const g = cliSimpleGit(repoDir);
|
|
33378
|
+
await g.raw(["branch", "-m", newName]);
|
|
33379
|
+
}
|
|
33380
|
+
|
|
33381
|
+
// src/worktrees/rename-session-worktree-branches.ts
|
|
33382
|
+
async function renameSessionWorktreeBranches(paths, newBranch, log2) {
|
|
33383
|
+
const safe = newBranch.replace(/[^a-zA-Z0-9/_-]+/g, "-").slice(0, 80) || "session-branch";
|
|
33384
|
+
for (const wt of paths) {
|
|
33385
|
+
try {
|
|
33386
|
+
await gitRenameCurrentBranch(wt, safe);
|
|
33387
|
+
log2(`[worktrees] Renamed branch in ${wt} \u2192 ${safe}`);
|
|
33388
|
+
} catch (e) {
|
|
33389
|
+
log2(
|
|
33390
|
+
`[worktrees] Branch rename failed in ${wt}: ${e instanceof Error ? e.message : String(e)}`
|
|
33391
|
+
);
|
|
33392
|
+
}
|
|
33393
|
+
}
|
|
33394
|
+
}
|
|
33395
|
+
|
|
33396
|
+
// src/worktrees/worktree-layout-file.ts
|
|
33397
|
+
import * as fs21 from "node:fs";
|
|
33398
|
+
import * as path22 from "node:path";
|
|
33399
|
+
import os7 from "node:os";
|
|
33400
|
+
var LAYOUT_FILENAME = "worktree-launcher-layout.json";
|
|
33401
|
+
function defaultWorktreeLayoutPath() {
|
|
33402
|
+
return path22.join(os7.homedir(), ".buildautomaton", LAYOUT_FILENAME);
|
|
33403
|
+
}
|
|
33404
|
+
function normalizeLoadedLayout(raw) {
|
|
33405
|
+
if (raw && typeof raw === "object" && "launcherCwds" in raw) {
|
|
33406
|
+
const j = raw;
|
|
33407
|
+
if (Array.isArray(j.launcherCwds)) return { launcherCwds: j.launcherCwds };
|
|
33408
|
+
}
|
|
33409
|
+
return { launcherCwds: [] };
|
|
33410
|
+
}
|
|
33411
|
+
function loadWorktreeLayout() {
|
|
33412
|
+
try {
|
|
33413
|
+
const p = defaultWorktreeLayoutPath();
|
|
33414
|
+
if (!fs21.existsSync(p)) return { launcherCwds: [] };
|
|
33415
|
+
const raw = JSON.parse(fs21.readFileSync(p, "utf8"));
|
|
33416
|
+
return normalizeLoadedLayout(raw);
|
|
33417
|
+
} catch {
|
|
33418
|
+
return { launcherCwds: [] };
|
|
33419
|
+
}
|
|
33420
|
+
}
|
|
33421
|
+
function saveWorktreeLayout(layout) {
|
|
33422
|
+
try {
|
|
33423
|
+
const dir = path22.dirname(defaultWorktreeLayoutPath());
|
|
33424
|
+
fs21.mkdirSync(dir, { recursive: true });
|
|
33425
|
+
fs21.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
|
|
33426
|
+
} catch {
|
|
33427
|
+
}
|
|
33428
|
+
}
|
|
33429
|
+
function baseNameSafe(pathString) {
|
|
33430
|
+
return path22.basename(pathString).replace(/[^a-zA-Z0-9._-]+/g, "-") || "cwd";
|
|
33431
|
+
}
|
|
33432
|
+
function getLauncherDirNameIfPresent(layout, bridgeRootPath2) {
|
|
33433
|
+
const norm = path22.resolve(bridgeRootPath2);
|
|
33434
|
+
const existing = layout.launcherCwds.find((e) => path22.resolve(e.absolutePath) === norm);
|
|
33435
|
+
return existing?.dirName;
|
|
33436
|
+
}
|
|
33437
|
+
function allocateDirNameForLauncherCwd(layout, bridgeRootPath2) {
|
|
33438
|
+
const existing = getLauncherDirNameIfPresent(layout, bridgeRootPath2);
|
|
33439
|
+
if (existing) return existing;
|
|
33440
|
+
const norm = path22.resolve(bridgeRootPath2);
|
|
33441
|
+
const base = baseNameSafe(norm);
|
|
33442
|
+
const used = new Set(layout.launcherCwds.map((e) => e.dirName));
|
|
33443
|
+
let name = base;
|
|
33444
|
+
let n = 2;
|
|
33445
|
+
while (used.has(name)) {
|
|
33446
|
+
name = `${base}-${n}`;
|
|
33447
|
+
n += 1;
|
|
33448
|
+
}
|
|
33449
|
+
layout.launcherCwds.push({ absolutePath: norm, dirName: name });
|
|
33450
|
+
saveWorktreeLayout(layout);
|
|
33451
|
+
return name;
|
|
33452
|
+
}
|
|
33453
|
+
|
|
33498
33454
|
// src/worktrees/discover-session-worktree-on-disk.ts
|
|
33499
|
-
import * as
|
|
33500
|
-
import * as
|
|
33455
|
+
import * as fs22 from "node:fs";
|
|
33456
|
+
import * as path23 from "node:path";
|
|
33501
33457
|
function isGitDir(dirPath) {
|
|
33502
33458
|
try {
|
|
33503
|
-
return
|
|
33459
|
+
return fs22.existsSync(path23.join(dirPath, ".git"));
|
|
33504
33460
|
} catch {
|
|
33505
33461
|
return false;
|
|
33506
33462
|
}
|
|
@@ -33509,23 +33465,23 @@ function collectGitRepoRootsUnderDirectory(rootPath) {
|
|
|
33509
33465
|
const out = [];
|
|
33510
33466
|
const walk = (dir) => {
|
|
33511
33467
|
if (isGitDir(dir)) {
|
|
33512
|
-
out.push(
|
|
33468
|
+
out.push(path23.resolve(dir));
|
|
33513
33469
|
return;
|
|
33514
33470
|
}
|
|
33515
33471
|
let entries;
|
|
33516
33472
|
try {
|
|
33517
|
-
entries =
|
|
33473
|
+
entries = fs22.readdirSync(dir, { withFileTypes: true });
|
|
33518
33474
|
} catch {
|
|
33519
33475
|
return;
|
|
33520
33476
|
}
|
|
33521
33477
|
for (const e of entries) {
|
|
33522
33478
|
if (e.name.startsWith(".")) continue;
|
|
33523
|
-
const full =
|
|
33479
|
+
const full = path23.join(dir, e.name);
|
|
33524
33480
|
if (!e.isDirectory()) continue;
|
|
33525
33481
|
walk(full);
|
|
33526
33482
|
}
|
|
33527
33483
|
};
|
|
33528
|
-
walk(
|
|
33484
|
+
walk(path23.resolve(rootPath));
|
|
33529
33485
|
return [...new Set(out)];
|
|
33530
33486
|
}
|
|
33531
33487
|
function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
|
|
@@ -33534,16 +33490,16 @@ function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
|
|
|
33534
33490
|
if (depth > maxDepth) return;
|
|
33535
33491
|
let entries;
|
|
33536
33492
|
try {
|
|
33537
|
-
entries =
|
|
33493
|
+
entries = fs22.readdirSync(dir, { withFileTypes: true });
|
|
33538
33494
|
} catch {
|
|
33539
33495
|
return;
|
|
33540
33496
|
}
|
|
33541
33497
|
for (const e of entries) {
|
|
33542
33498
|
if (e.name.startsWith(".")) continue;
|
|
33543
|
-
const full =
|
|
33499
|
+
const full = path23.join(dir, e.name);
|
|
33544
33500
|
if (!e.isDirectory()) continue;
|
|
33545
33501
|
if (e.name === sessionId) {
|
|
33546
|
-
if (isGitDir(full)) out.push(
|
|
33502
|
+
if (isGitDir(full)) out.push(path23.resolve(full));
|
|
33547
33503
|
} else {
|
|
33548
33504
|
walk(full, depth + 1);
|
|
33549
33505
|
}
|
|
@@ -33555,14 +33511,14 @@ function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
|
|
|
33555
33511
|
function tryBindingFromSessionDirectory(sessionDir) {
|
|
33556
33512
|
let st;
|
|
33557
33513
|
try {
|
|
33558
|
-
st =
|
|
33514
|
+
st = fs22.statSync(sessionDir);
|
|
33559
33515
|
} catch {
|
|
33560
33516
|
return null;
|
|
33561
33517
|
}
|
|
33562
33518
|
if (!st.isDirectory()) return null;
|
|
33563
33519
|
const worktreePaths = collectGitRepoRootsUnderDirectory(sessionDir);
|
|
33564
33520
|
if (worktreePaths.length === 0) return null;
|
|
33565
|
-
const abs =
|
|
33521
|
+
const abs = path23.resolve(sessionDir);
|
|
33566
33522
|
return {
|
|
33567
33523
|
sessionParentPath: abs,
|
|
33568
33524
|
workingTreeRelRoot: abs,
|
|
@@ -33572,20 +33528,20 @@ function tryBindingFromSessionDirectory(sessionDir) {
|
|
|
33572
33528
|
function discoverLegacyBindingAscendingFromCheckout(sessionId, checkoutPath) {
|
|
33573
33529
|
const sid = sessionId.trim();
|
|
33574
33530
|
if (!sid) return null;
|
|
33575
|
-
const hintR =
|
|
33531
|
+
const hintR = path23.resolve(checkoutPath);
|
|
33576
33532
|
let best = null;
|
|
33577
|
-
let cur =
|
|
33533
|
+
let cur = path23.dirname(hintR);
|
|
33578
33534
|
for (let i = 0; i < 40; i++) {
|
|
33579
33535
|
const paths = collectWorktreeRootsNamed(cur, sid, 24);
|
|
33580
|
-
if (paths.some((p) =>
|
|
33581
|
-
const isolated = resolveIsolatedSessionParentPathFromCheckouts(paths) ??
|
|
33536
|
+
if (paths.some((p) => path23.resolve(p) === hintR)) {
|
|
33537
|
+
const isolated = resolveIsolatedSessionParentPathFromCheckouts(paths) ?? path23.resolve(paths[0]);
|
|
33582
33538
|
best = {
|
|
33583
|
-
sessionParentPath:
|
|
33584
|
-
workingTreeRelRoot:
|
|
33585
|
-
repoCheckoutPaths: paths.map((p) =>
|
|
33539
|
+
sessionParentPath: path23.resolve(isolated),
|
|
33540
|
+
workingTreeRelRoot: path23.resolve(cur),
|
|
33541
|
+
repoCheckoutPaths: paths.map((p) => path23.resolve(p))
|
|
33586
33542
|
};
|
|
33587
33543
|
}
|
|
33588
|
-
const next =
|
|
33544
|
+
const next = path23.dirname(cur);
|
|
33589
33545
|
if (next === cur) break;
|
|
33590
33546
|
cur = next;
|
|
33591
33547
|
}
|
|
@@ -33593,33 +33549,33 @@ function discoverLegacyBindingAscendingFromCheckout(sessionId, checkoutPath) {
|
|
|
33593
33549
|
}
|
|
33594
33550
|
function discoverSessionWorktreeOnDisk(options) {
|
|
33595
33551
|
const { sessionId, worktreesRootPath, layout, bridgeRoot } = options;
|
|
33596
|
-
if (!sessionId.trim() || !
|
|
33552
|
+
if (!sessionId.trim() || !fs22.existsSync(worktreesRootPath)) return null;
|
|
33597
33553
|
const preferredKey = getLauncherDirNameIfPresent(layout, bridgeRoot);
|
|
33598
33554
|
const keys = [];
|
|
33599
33555
|
if (preferredKey) keys.push(preferredKey);
|
|
33600
33556
|
try {
|
|
33601
|
-
for (const name of
|
|
33557
|
+
for (const name of fs22.readdirSync(worktreesRootPath)) {
|
|
33602
33558
|
if (name.startsWith(".")) continue;
|
|
33603
|
-
const p =
|
|
33604
|
-
if (!
|
|
33559
|
+
const p = path23.join(worktreesRootPath, name);
|
|
33560
|
+
if (!fs22.statSync(p).isDirectory()) continue;
|
|
33605
33561
|
if (name !== preferredKey) keys.push(name);
|
|
33606
33562
|
}
|
|
33607
33563
|
} catch {
|
|
33608
33564
|
return null;
|
|
33609
33565
|
}
|
|
33610
33566
|
for (const key of keys) {
|
|
33611
|
-
const layoutRoot =
|
|
33612
|
-
if (!
|
|
33613
|
-
const sessionDir =
|
|
33567
|
+
const layoutRoot = path23.join(worktreesRootPath, key);
|
|
33568
|
+
if (!fs22.existsSync(layoutRoot) || !fs22.statSync(layoutRoot).isDirectory()) continue;
|
|
33569
|
+
const sessionDir = path23.join(layoutRoot, sessionId);
|
|
33614
33570
|
const nested = tryBindingFromSessionDirectory(sessionDir);
|
|
33615
33571
|
if (nested) return nested;
|
|
33616
33572
|
const legacyPaths = collectWorktreeRootsNamed(layoutRoot, sessionId, 24);
|
|
33617
33573
|
if (legacyPaths.length > 0) {
|
|
33618
|
-
const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ??
|
|
33574
|
+
const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path23.resolve(legacyPaths[0]);
|
|
33619
33575
|
return {
|
|
33620
|
-
sessionParentPath:
|
|
33621
|
-
workingTreeRelRoot:
|
|
33622
|
-
repoCheckoutPaths: legacyPaths.map((p) =>
|
|
33576
|
+
sessionParentPath: path23.resolve(isolated),
|
|
33577
|
+
workingTreeRelRoot: path23.resolve(layoutRoot),
|
|
33578
|
+
repoCheckoutPaths: legacyPaths.map((p) => path23.resolve(p))
|
|
33623
33579
|
};
|
|
33624
33580
|
}
|
|
33625
33581
|
}
|
|
@@ -33628,12 +33584,12 @@ function discoverSessionWorktreeOnDisk(options) {
|
|
|
33628
33584
|
function discoverSessionWorktreesUnderSessionWorktreeRoot(sessionWorktreeRootPathOrHint, sessionId) {
|
|
33629
33585
|
const sid = sessionId.trim();
|
|
33630
33586
|
if (!sid) return null;
|
|
33631
|
-
const hint =
|
|
33632
|
-
const underHint = tryBindingFromSessionDirectory(
|
|
33587
|
+
const hint = path23.resolve(sessionWorktreeRootPathOrHint);
|
|
33588
|
+
const underHint = tryBindingFromSessionDirectory(path23.join(hint, sid));
|
|
33633
33589
|
if (underHint) return underHint;
|
|
33634
33590
|
const direct = tryBindingFromSessionDirectory(hint);
|
|
33635
33591
|
if (direct) {
|
|
33636
|
-
if (
|
|
33592
|
+
if (path23.basename(hint) === sid && isGitDir(hint)) {
|
|
33637
33593
|
const legacyFromCheckout = discoverLegacyBindingAscendingFromCheckout(sid, hint);
|
|
33638
33594
|
if (legacyFromCheckout && legacyFromCheckout.repoCheckoutPaths.length > direct.repoCheckoutPaths.length) {
|
|
33639
33595
|
return legacyFromCheckout;
|
|
@@ -33641,216 +33597,349 @@ function discoverSessionWorktreesUnderSessionWorktreeRoot(sessionWorktreeRootPat
|
|
|
33641
33597
|
}
|
|
33642
33598
|
return direct;
|
|
33643
33599
|
}
|
|
33644
|
-
if (
|
|
33600
|
+
if (path23.basename(hint) === sid && isGitDir(hint)) {
|
|
33645
33601
|
const legacyFromCheckout = discoverLegacyBindingAscendingFromCheckout(sid, hint);
|
|
33646
33602
|
if (legacyFromCheckout) return legacyFromCheckout;
|
|
33647
33603
|
}
|
|
33648
33604
|
let st;
|
|
33649
33605
|
try {
|
|
33650
|
-
st =
|
|
33606
|
+
st = fs22.statSync(hint);
|
|
33651
33607
|
} catch {
|
|
33652
33608
|
return null;
|
|
33653
33609
|
}
|
|
33654
33610
|
if (!st.isDirectory()) return null;
|
|
33655
33611
|
const legacyPaths = collectWorktreeRootsNamed(hint, sid, 24);
|
|
33656
33612
|
if (legacyPaths.length === 0) return null;
|
|
33657
|
-
const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ??
|
|
33613
|
+
const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path23.resolve(legacyPaths[0]);
|
|
33658
33614
|
return {
|
|
33659
|
-
sessionParentPath:
|
|
33615
|
+
sessionParentPath: path23.resolve(isolated),
|
|
33660
33616
|
workingTreeRelRoot: hint,
|
|
33661
|
-
repoCheckoutPaths: legacyPaths.map((p) =>
|
|
33617
|
+
repoCheckoutPaths: legacyPaths.map((p) => path23.resolve(p))
|
|
33662
33618
|
};
|
|
33663
33619
|
}
|
|
33664
33620
|
|
|
33665
|
-
// src/worktrees/session-
|
|
33621
|
+
// src/worktrees/manager/discover-session-binding.ts
|
|
33622
|
+
function discoverSessionBinding(params) {
|
|
33623
|
+
return discoverSessionWorktreeOnDisk({
|
|
33624
|
+
sessionId: params.sessionId,
|
|
33625
|
+
worktreesRootPath: params.worktreesRootPath,
|
|
33626
|
+
layout: params.layout,
|
|
33627
|
+
bridgeRoot: getBridgeRoot()
|
|
33628
|
+
});
|
|
33629
|
+
}
|
|
33630
|
+
|
|
33631
|
+
// src/worktrees/manager/resolve-isolated-session-parent-path.ts
|
|
33632
|
+
function resolveIsolatedSessionParentPath(sessionId, cache2, ensureRepoCheckoutPaths) {
|
|
33633
|
+
if (!sessionId) return null;
|
|
33634
|
+
const sid = sessionId.trim();
|
|
33635
|
+
const cached2 = cache2.getSessionParentPath(sid);
|
|
33636
|
+
if (cached2) return cached2;
|
|
33637
|
+
const paths = ensureRepoCheckoutPaths(sid);
|
|
33638
|
+
if (!paths?.length) return null;
|
|
33639
|
+
return resolveIsolatedSessionParentPathFromCheckouts(paths);
|
|
33640
|
+
}
|
|
33641
|
+
function ensureRepoCheckoutPathsForSession(sessionId, cache2, discover) {
|
|
33642
|
+
if (!sessionId?.trim()) return void 0;
|
|
33643
|
+
const sid = sessionId.trim();
|
|
33644
|
+
const cached2 = cache2.getRepoCheckoutPaths(sid);
|
|
33645
|
+
if (cached2?.length) return cached2;
|
|
33646
|
+
const disc = discover(sid);
|
|
33647
|
+
if (disc?.repoCheckoutPaths.length) {
|
|
33648
|
+
cache2.remember(sid, disc);
|
|
33649
|
+
return [...disc.repoCheckoutPaths];
|
|
33650
|
+
}
|
|
33651
|
+
return void 0;
|
|
33652
|
+
}
|
|
33653
|
+
|
|
33654
|
+
// src/worktrees/manager/resolve-commit-targets.ts
|
|
33655
|
+
function resolveCommitTargets(sessionId, cache2, discover) {
|
|
33656
|
+
const paths = cache2.getRepoCheckoutPathsRef(sessionId);
|
|
33657
|
+
if (paths?.length) return paths;
|
|
33658
|
+
const disc = discover(sessionId);
|
|
33659
|
+
if (disc?.repoCheckoutPaths.length) {
|
|
33660
|
+
cache2.remember(sessionId, disc);
|
|
33661
|
+
return disc.repoCheckoutPaths;
|
|
33662
|
+
}
|
|
33663
|
+
return [getBridgeRoot()];
|
|
33664
|
+
}
|
|
33665
|
+
|
|
33666
|
+
// src/worktrees/manager/parse-session-parent.ts
|
|
33666
33667
|
function parseSessionParent(v) {
|
|
33667
33668
|
if (v === "bridge_root" || v === "worktrees_root") return v;
|
|
33668
33669
|
if (v === "session_worktrees_root") return "worktrees_root";
|
|
33669
33670
|
return null;
|
|
33670
33671
|
}
|
|
33671
|
-
|
|
33672
|
-
|
|
33673
|
-
|
|
33672
|
+
|
|
33673
|
+
// src/worktrees/prepare-new-session-worktrees.ts
|
|
33674
|
+
import * as fs23 from "node:fs";
|
|
33675
|
+
import * as path24 from "node:path";
|
|
33676
|
+
|
|
33677
|
+
// src/git/worktrees/worktree-add.ts
|
|
33678
|
+
async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch, baseRef = "HEAD") {
|
|
33679
|
+
const mainGit = cliSimpleGit(mainRepoPath);
|
|
33680
|
+
const base = baseRef.trim() || "HEAD";
|
|
33681
|
+
await mainGit.raw(["worktree", "add", "-b", branch, worktreePath, base]);
|
|
33682
|
+
}
|
|
33683
|
+
|
|
33684
|
+
// src/worktrees/prepare-new-session-worktrees.ts
|
|
33685
|
+
function normalizeRepoRelPath(rel) {
|
|
33686
|
+
return rel === "" ? "." : rel.replace(/\\/g, "/");
|
|
33687
|
+
}
|
|
33688
|
+
function resolveBaseRefForRepo(relNorm, baseBranches) {
|
|
33689
|
+
if (!baseBranches) return "HEAD";
|
|
33690
|
+
const direct = baseBranches[relNorm]?.trim();
|
|
33691
|
+
if (direct) return direct;
|
|
33692
|
+
if (relNorm !== "." && baseBranches["."]?.trim()) return baseBranches["."].trim();
|
|
33693
|
+
return "HEAD";
|
|
33694
|
+
}
|
|
33695
|
+
async function prepareNewSessionWorktrees(options) {
|
|
33696
|
+
const { worktreesRootPath, bridgeRoot, sessionId, layout, log: log2, worktreeBaseBranches } = options;
|
|
33697
|
+
const bridgeResolved = path24.resolve(bridgeRoot);
|
|
33698
|
+
const cwdKey = allocateDirNameForLauncherCwd(layout, bridgeResolved);
|
|
33699
|
+
const bridgeKeyDir = path24.join(worktreesRootPath, cwdKey);
|
|
33700
|
+
const sessionDir = path24.join(bridgeKeyDir, sessionId);
|
|
33701
|
+
const repos = await discoverGitReposUnderRoot(bridgeResolved);
|
|
33702
|
+
if (repos.length === 0) {
|
|
33703
|
+
log2("[worktrees] No Git repositories under bridge root; skipping worktree creation.");
|
|
33704
|
+
return null;
|
|
33705
|
+
}
|
|
33706
|
+
const branch = `session-${sessionId}`;
|
|
33707
|
+
const worktreePaths = [];
|
|
33708
|
+
fs23.mkdirSync(sessionDir, { recursive: true });
|
|
33709
|
+
for (const repo of repos) {
|
|
33710
|
+
let rel = path24.relative(bridgeResolved, repo.absolutePath);
|
|
33711
|
+
if (rel.startsWith("..") || path24.isAbsolute(rel)) continue;
|
|
33712
|
+
const relNorm = normalizeRepoRelPath(rel === "" ? "." : rel);
|
|
33713
|
+
const wtPath = relNorm === "." ? sessionDir : path24.join(sessionDir, relNorm);
|
|
33714
|
+
if (relNorm !== ".") {
|
|
33715
|
+
fs23.mkdirSync(path24.dirname(wtPath), { recursive: true });
|
|
33716
|
+
}
|
|
33717
|
+
const baseRef = resolveBaseRefForRepo(relNorm, worktreeBaseBranches);
|
|
33718
|
+
try {
|
|
33719
|
+
await gitWorktreeAddBranch(repo.absolutePath, wtPath, branch, baseRef);
|
|
33720
|
+
log2(`[worktrees] Added worktree ${wtPath} (branch ${branch}, base ${baseRef}).`);
|
|
33721
|
+
worktreePaths.push(wtPath);
|
|
33722
|
+
} catch (e) {
|
|
33723
|
+
log2(
|
|
33724
|
+
`[worktrees] Worktree add failed for ${repo.absolutePath}: ${e instanceof Error ? e.message : String(e)}`
|
|
33725
|
+
);
|
|
33726
|
+
}
|
|
33727
|
+
}
|
|
33728
|
+
if (worktreePaths.length === 0) return null;
|
|
33729
|
+
return {
|
|
33730
|
+
worktreePaths,
|
|
33731
|
+
sessionParentPath: sessionDir,
|
|
33732
|
+
workingTreeRelRoot: sessionDir
|
|
33733
|
+
};
|
|
33734
|
+
}
|
|
33735
|
+
|
|
33736
|
+
// src/worktrees/manager/prepare-and-remember-session-worktrees.ts
|
|
33737
|
+
async function prepareAndRememberSessionWorktrees(params) {
|
|
33738
|
+
const prep = await prepareNewSessionWorktrees({
|
|
33739
|
+
worktreesRootPath: params.worktreesRootPath,
|
|
33740
|
+
bridgeRoot: getBridgeRoot(),
|
|
33741
|
+
sessionId: params.sessionId,
|
|
33742
|
+
layout: params.layout,
|
|
33743
|
+
log: params.log,
|
|
33744
|
+
...params.worktreeBaseBranches && Object.keys(params.worktreeBaseBranches).length > 0 ? { worktreeBaseBranches: params.worktreeBaseBranches } : {}
|
|
33745
|
+
});
|
|
33746
|
+
if (!prep) return void 0;
|
|
33747
|
+
params.cache.remember(params.sessionId, {
|
|
33748
|
+
sessionParentPath: prep.sessionParentPath,
|
|
33749
|
+
workingTreeRelRoot: prep.workingTreeRelRoot,
|
|
33750
|
+
repoCheckoutPaths: prep.worktreePaths
|
|
33751
|
+
});
|
|
33752
|
+
return params.cache.getSessionParentPath(params.sessionId);
|
|
33753
|
+
}
|
|
33754
|
+
|
|
33755
|
+
// src/worktrees/manager/resolve-existing-session-parent-path.ts
|
|
33756
|
+
function resolveExistingSessionParentPath(sessionId, cache2, discover) {
|
|
33757
|
+
const cached2 = cache2.getSessionParentPath(sessionId);
|
|
33758
|
+
if (cached2) return cached2;
|
|
33759
|
+
const disc = discover();
|
|
33760
|
+
if (disc) {
|
|
33761
|
+
cache2.remember(sessionId, disc);
|
|
33762
|
+
return cache2.getSessionParentPath(sessionId);
|
|
33763
|
+
}
|
|
33764
|
+
return void 0;
|
|
33765
|
+
}
|
|
33766
|
+
|
|
33767
|
+
// src/worktrees/manager/resolve-explicit-session-parent-path.ts
|
|
33768
|
+
import * as path25 from "node:path";
|
|
33769
|
+
function resolveExplicitSessionParentPath(params) {
|
|
33770
|
+
const resolved = path25.resolve(params.parentPathRaw);
|
|
33771
|
+
if (parseSessionParent(params.sessionParent) !== "worktrees_root") {
|
|
33772
|
+
return resolved;
|
|
33773
|
+
}
|
|
33774
|
+
const rememberAndReturn = (binding) => {
|
|
33775
|
+
params.cache.remember(params.sessionId, binding);
|
|
33776
|
+
return params.cache.getSessionParentPath(params.sessionId) ?? resolved;
|
|
33777
|
+
};
|
|
33778
|
+
const diskFirst = params.discover();
|
|
33779
|
+
if (diskFirst) return rememberAndReturn(diskFirst);
|
|
33780
|
+
const fromRoot = discoverSessionWorktreesUnderSessionWorktreeRoot(resolved, params.sessionId);
|
|
33781
|
+
if (fromRoot) return rememberAndReturn(fromRoot);
|
|
33782
|
+
let cur = resolved;
|
|
33783
|
+
for (let i = 0; i < 16; i++) {
|
|
33784
|
+
const tryRoot = discoverSessionWorktreesUnderSessionWorktreeRoot(cur, params.sessionId);
|
|
33785
|
+
if (tryRoot) return rememberAndReturn(tryRoot);
|
|
33786
|
+
const next = path25.dirname(cur);
|
|
33787
|
+
if (next === cur) break;
|
|
33788
|
+
cur = next;
|
|
33789
|
+
}
|
|
33790
|
+
return resolved;
|
|
33791
|
+
}
|
|
33792
|
+
|
|
33793
|
+
// src/worktrees/manager/resolve-session-parent-path-for-prompt.ts
|
|
33794
|
+
async function resolveSessionParentPathForPrompt(params) {
|
|
33795
|
+
const { sessionId, cache: cache2, worktreesRootPath, layout, log: log2, discover, opts } = params;
|
|
33796
|
+
if (!sessionId) return void 0;
|
|
33797
|
+
const sid = sessionId.trim();
|
|
33798
|
+
const parentPathRaw = opts.sessionParentPath?.trim();
|
|
33799
|
+
if (parentPathRaw) {
|
|
33800
|
+
return resolveExplicitSessionParentPath({
|
|
33801
|
+
sessionId: sid,
|
|
33802
|
+
sessionParent: opts.sessionParent,
|
|
33803
|
+
parentPathRaw,
|
|
33804
|
+
cache: cache2,
|
|
33805
|
+
discover: () => discover(sid)
|
|
33806
|
+
});
|
|
33807
|
+
}
|
|
33808
|
+
const parentKind = parseSessionParent(opts.sessionParent);
|
|
33809
|
+
if (parentKind === "bridge_root") {
|
|
33810
|
+
return void 0;
|
|
33811
|
+
}
|
|
33812
|
+
if (parentKind === "worktrees_root") {
|
|
33813
|
+
if (!opts.isNewSession) {
|
|
33814
|
+
return resolveExistingSessionParentPath(sid, cache2, () => discover(sid));
|
|
33815
|
+
}
|
|
33816
|
+
return prepareAndRememberSessionWorktrees({
|
|
33817
|
+
cache: cache2,
|
|
33818
|
+
sessionId: sid,
|
|
33819
|
+
worktreesRootPath,
|
|
33820
|
+
layout,
|
|
33821
|
+
log: log2,
|
|
33822
|
+
...opts.worktreeBaseBranches ? { worktreeBaseBranches: opts.worktreeBaseBranches } : {}
|
|
33823
|
+
});
|
|
33824
|
+
}
|
|
33825
|
+
if (!opts.isNewSession) {
|
|
33826
|
+
return resolveExistingSessionParentPath(sid, cache2, () => discover(sid));
|
|
33827
|
+
}
|
|
33828
|
+
return prepareAndRememberSessionWorktrees({
|
|
33829
|
+
cache: cache2,
|
|
33830
|
+
sessionId: sid,
|
|
33831
|
+
worktreesRootPath,
|
|
33832
|
+
layout,
|
|
33833
|
+
log: log2
|
|
33834
|
+
});
|
|
33835
|
+
}
|
|
33836
|
+
|
|
33837
|
+
// src/worktrees/manager/session-worktree-cache.ts
|
|
33838
|
+
import * as path26 from "node:path";
|
|
33839
|
+
var SessionWorktreeCache = class {
|
|
33674
33840
|
sessionRepoCheckoutPaths = /* @__PURE__ */ new Map();
|
|
33675
33841
|
sessionParentPathBySession = /* @__PURE__ */ new Map();
|
|
33676
33842
|
sessionWorkingTreeRelRootBySession = /* @__PURE__ */ new Map();
|
|
33677
|
-
|
|
33678
|
-
|
|
33679
|
-
this.worktreesRootPath = options.worktreesRootPath;
|
|
33680
|
-
this.log = options.log;
|
|
33681
|
-
this.layout = loadWorktreeLayout();
|
|
33682
|
-
}
|
|
33683
|
-
rememberSessionWorktrees(sessionId, binding) {
|
|
33684
|
-
const paths = binding.repoCheckoutPaths.map((p) => path25.resolve(p));
|
|
33843
|
+
remember(sessionId, binding) {
|
|
33844
|
+
const paths = binding.repoCheckoutPaths.map((p) => path26.resolve(p));
|
|
33685
33845
|
this.sessionRepoCheckoutPaths.set(sessionId, paths);
|
|
33686
|
-
this.sessionParentPathBySession.set(sessionId,
|
|
33687
|
-
this.sessionWorkingTreeRelRootBySession.set(sessionId,
|
|
33846
|
+
this.sessionParentPathBySession.set(sessionId, path26.resolve(binding.sessionParentPath));
|
|
33847
|
+
this.sessionWorkingTreeRelRootBySession.set(sessionId, path26.resolve(binding.workingTreeRelRoot));
|
|
33848
|
+
}
|
|
33849
|
+
clearSession(sessionId) {
|
|
33850
|
+
const paths = this.sessionRepoCheckoutPaths.get(sessionId);
|
|
33851
|
+
this.sessionRepoCheckoutPaths.delete(sessionId);
|
|
33852
|
+
this.sessionParentPathBySession.delete(sessionId);
|
|
33853
|
+
this.sessionWorkingTreeRelRootBySession.delete(sessionId);
|
|
33854
|
+
return paths;
|
|
33688
33855
|
}
|
|
33689
|
-
|
|
33856
|
+
getSessionParentPath(sessionId) {
|
|
33690
33857
|
return this.sessionParentPathBySession.get(sessionId);
|
|
33691
33858
|
}
|
|
33692
|
-
|
|
33693
|
-
return
|
|
33694
|
-
|
|
33695
|
-
|
|
33696
|
-
|
|
33697
|
-
|
|
33698
|
-
|
|
33859
|
+
getWorkingTreeRelRoot(sessionId) {
|
|
33860
|
+
return this.sessionWorkingTreeRelRootBySession.get(sessionId) ?? null;
|
|
33861
|
+
}
|
|
33862
|
+
hasSession(sessionId) {
|
|
33863
|
+
return this.sessionParentPathBySession.has(sessionId);
|
|
33864
|
+
}
|
|
33865
|
+
getRepoCheckoutPaths(sessionId) {
|
|
33866
|
+
const paths = this.sessionRepoCheckoutPaths.get(sessionId);
|
|
33867
|
+
return paths?.length ? [...paths] : void 0;
|
|
33868
|
+
}
|
|
33869
|
+
getRepoCheckoutPathsRef(sessionId) {
|
|
33870
|
+
const paths = this.sessionRepoCheckoutPaths.get(sessionId);
|
|
33871
|
+
return paths?.length ? paths : void 0;
|
|
33699
33872
|
}
|
|
33700
33873
|
isLegacyNestedLayout(sessionId) {
|
|
33701
33874
|
const parent = this.sessionParentPathBySession.get(sessionId);
|
|
33702
33875
|
const relRoot = this.sessionWorkingTreeRelRootBySession.get(sessionId);
|
|
33703
33876
|
if (!parent || !relRoot) return false;
|
|
33704
|
-
return
|
|
33877
|
+
return path26.resolve(parent) !== path26.resolve(relRoot);
|
|
33878
|
+
}
|
|
33879
|
+
};
|
|
33880
|
+
|
|
33881
|
+
// src/worktrees/manager/manager.ts
|
|
33882
|
+
var SessionWorktreeManager = class {
|
|
33883
|
+
worktreesRootPath;
|
|
33884
|
+
log;
|
|
33885
|
+
cache = new SessionWorktreeCache();
|
|
33886
|
+
layout;
|
|
33887
|
+
constructor(options) {
|
|
33888
|
+
this.worktreesRootPath = options.worktreesRootPath;
|
|
33889
|
+
this.log = options.log;
|
|
33890
|
+
this.layout = loadWorktreeLayout();
|
|
33891
|
+
}
|
|
33892
|
+
discover(sessionId) {
|
|
33893
|
+
return discoverSessionBinding({
|
|
33894
|
+
sessionId,
|
|
33895
|
+
worktreesRootPath: this.worktreesRootPath,
|
|
33896
|
+
layout: this.layout
|
|
33897
|
+
});
|
|
33705
33898
|
}
|
|
33706
|
-
/**
|
|
33707
|
-
* Session parent path for `worktrees_root`: the per-session directory (new layout) or primary checkout (legacy).
|
|
33708
|
-
*/
|
|
33709
33899
|
getIsolatedSessionParentPathForSession(sessionId) {
|
|
33710
|
-
|
|
33711
|
-
|
|
33712
|
-
|
|
33713
|
-
|
|
33714
|
-
|
|
33715
|
-
|
|
33716
|
-
|
|
33717
|
-
|
|
33718
|
-
|
|
33719
|
-
|
|
33720
|
-
* or `undefined` meaning use {@link getBridgeRoot}.
|
|
33721
|
-
*/
|
|
33722
|
-
async resolveSessionParentPathForPrompt(sessionId, opts) {
|
|
33723
|
-
if (!sessionId) return void 0;
|
|
33724
|
-
const sid = sessionId.trim();
|
|
33725
|
-
const parentPathRaw = opts.sessionParentPath?.trim();
|
|
33726
|
-
if (parentPathRaw) {
|
|
33727
|
-
const resolved = path25.resolve(parentPathRaw);
|
|
33728
|
-
if (sid && parseSessionParent(opts.sessionParent) === "worktrees_root") {
|
|
33729
|
-
const diskFirst = this.tryDiscoverFromDisk(sid);
|
|
33730
|
-
if (diskFirst) {
|
|
33731
|
-
this.rememberSessionWorktrees(sid, diskFirst);
|
|
33732
|
-
return this.sessionParentPathAfterRemember(sid);
|
|
33733
|
-
}
|
|
33734
|
-
const fromRoot = discoverSessionWorktreesUnderSessionWorktreeRoot(resolved, sid);
|
|
33735
|
-
if (fromRoot) {
|
|
33736
|
-
this.rememberSessionWorktrees(sid, fromRoot);
|
|
33737
|
-
return this.sessionParentPathAfterRemember(sid);
|
|
33738
|
-
}
|
|
33739
|
-
let cur = resolved;
|
|
33740
|
-
for (let i = 0; i < 16; i++) {
|
|
33741
|
-
const tryRoot = discoverSessionWorktreesUnderSessionWorktreeRoot(cur, sid);
|
|
33742
|
-
if (tryRoot) {
|
|
33743
|
-
this.rememberSessionWorktrees(sid, tryRoot);
|
|
33744
|
-
return this.sessionParentPathAfterRemember(sid);
|
|
33745
|
-
}
|
|
33746
|
-
const next = path25.dirname(cur);
|
|
33747
|
-
if (next === cur) break;
|
|
33748
|
-
cur = next;
|
|
33749
|
-
}
|
|
33750
|
-
}
|
|
33751
|
-
return resolved;
|
|
33752
|
-
}
|
|
33753
|
-
const parentKind = parseSessionParent(opts.sessionParent);
|
|
33754
|
-
if (parentKind === "bridge_root") {
|
|
33755
|
-
return void 0;
|
|
33756
|
-
}
|
|
33757
|
-
if (parentKind === "worktrees_root") {
|
|
33758
|
-
if (!opts.isNewSession) {
|
|
33759
|
-
const cached2 = this.sessionParentPathAfterRemember(sid);
|
|
33760
|
-
if (cached2) return cached2;
|
|
33761
|
-
const disc = this.tryDiscoverFromDisk(sid);
|
|
33762
|
-
if (disc) {
|
|
33763
|
-
this.rememberSessionWorktrees(sid, disc);
|
|
33764
|
-
return this.sessionParentPathAfterRemember(sid);
|
|
33765
|
-
}
|
|
33766
|
-
return void 0;
|
|
33767
|
-
}
|
|
33768
|
-
const prep2 = await prepareNewSessionWorktrees({
|
|
33769
|
-
worktreesRootPath: this.worktreesRootPath,
|
|
33770
|
-
bridgeRoot: getBridgeRoot(),
|
|
33771
|
-
sessionId: sid,
|
|
33772
|
-
layout: this.layout,
|
|
33773
|
-
log: this.log
|
|
33774
|
-
});
|
|
33775
|
-
if (!prep2) return void 0;
|
|
33776
|
-
this.rememberSessionWorktrees(sid, {
|
|
33777
|
-
sessionParentPath: prep2.sessionParentPath,
|
|
33778
|
-
workingTreeRelRoot: prep2.workingTreeRelRoot,
|
|
33779
|
-
repoCheckoutPaths: prep2.worktreePaths
|
|
33780
|
-
});
|
|
33781
|
-
return this.sessionParentPathAfterRemember(sid);
|
|
33782
|
-
}
|
|
33783
|
-
if (!opts.isNewSession) {
|
|
33784
|
-
const cached2 = this.sessionParentPathAfterRemember(sid);
|
|
33785
|
-
if (cached2) return cached2;
|
|
33786
|
-
const disc = this.tryDiscoverFromDisk(sid);
|
|
33787
|
-
if (disc) {
|
|
33788
|
-
this.rememberSessionWorktrees(sid, disc);
|
|
33789
|
-
return this.sessionParentPathAfterRemember(sid);
|
|
33790
|
-
}
|
|
33791
|
-
return void 0;
|
|
33792
|
-
}
|
|
33793
|
-
const prep = await prepareNewSessionWorktrees({
|
|
33900
|
+
return resolveIsolatedSessionParentPath(
|
|
33901
|
+
sessionId,
|
|
33902
|
+
this.cache,
|
|
33903
|
+
(sid) => this.ensureRepoCheckoutPathsForSession(sid)
|
|
33904
|
+
);
|
|
33905
|
+
}
|
|
33906
|
+
resolveSessionParentPathForPrompt(sessionId, opts) {
|
|
33907
|
+
return resolveSessionParentPathForPrompt({
|
|
33908
|
+
sessionId,
|
|
33909
|
+
cache: this.cache,
|
|
33794
33910
|
worktreesRootPath: this.worktreesRootPath,
|
|
33795
|
-
bridgeRoot: getBridgeRoot(),
|
|
33796
|
-
sessionId: sid,
|
|
33797
33911
|
layout: this.layout,
|
|
33798
|
-
log: this.log
|
|
33799
|
-
|
|
33800
|
-
|
|
33801
|
-
this.rememberSessionWorktrees(sid, {
|
|
33802
|
-
sessionParentPath: prep.sessionParentPath,
|
|
33803
|
-
workingTreeRelRoot: prep.workingTreeRelRoot,
|
|
33804
|
-
repoCheckoutPaths: prep.worktreePaths
|
|
33912
|
+
log: this.log,
|
|
33913
|
+
discover: (sid) => this.discover(sid),
|
|
33914
|
+
opts
|
|
33805
33915
|
});
|
|
33806
|
-
return this.sessionParentPathAfterRemember(sid);
|
|
33807
33916
|
}
|
|
33808
33917
|
async renameSessionBranch(sessionId, newBranch) {
|
|
33809
|
-
const paths = this.
|
|
33918
|
+
const paths = this.cache.getRepoCheckoutPathsRef(sessionId);
|
|
33810
33919
|
if (!paths?.length) return;
|
|
33811
33920
|
await renameSessionWorktreeBranches(paths, newBranch, this.log);
|
|
33812
33921
|
}
|
|
33813
|
-
/** True when this session uses an isolated worktree layout (not the bridge root). */
|
|
33814
33922
|
usesWorktreeSession(sessionId) {
|
|
33815
33923
|
if (!sessionId) return false;
|
|
33816
|
-
return this.
|
|
33924
|
+
return this.cache.hasSession(sessionId);
|
|
33817
33925
|
}
|
|
33818
|
-
/** Per-repo git checkout directories for this session (for snapshots, commits, change lists). */
|
|
33819
33926
|
getRepoCheckoutPathsForSession(sessionId) {
|
|
33820
33927
|
if (!sessionId) return void 0;
|
|
33821
|
-
|
|
33822
|
-
return paths?.length ? [...paths] : void 0;
|
|
33928
|
+
return this.cache.getRepoCheckoutPaths(sessionId);
|
|
33823
33929
|
}
|
|
33824
|
-
/**
|
|
33825
|
-
* Same paths as {@link getRepoCheckoutPathsForSession}, but loads from disk into memory when the CLI
|
|
33826
|
-
* restarted or maps were not yet populated (avoids discovering every repo under the worktrees root).
|
|
33827
|
-
*/
|
|
33828
33930
|
ensureRepoCheckoutPathsForSession(sessionId) {
|
|
33829
|
-
|
|
33830
|
-
const sid = sessionId.trim();
|
|
33831
|
-
const cached2 = this.sessionRepoCheckoutPaths.get(sid);
|
|
33832
|
-
if (cached2?.length) return [...cached2];
|
|
33833
|
-
const disc = this.tryDiscoverFromDisk(sid);
|
|
33834
|
-
if (disc?.repoCheckoutPaths.length) {
|
|
33835
|
-
this.rememberSessionWorktrees(sid, disc);
|
|
33836
|
-
return [...disc.repoCheckoutPaths];
|
|
33837
|
-
}
|
|
33838
|
-
return void 0;
|
|
33931
|
+
return ensureRepoCheckoutPathsForSession(sessionId, this.cache, (sid) => this.discover(sid));
|
|
33839
33932
|
}
|
|
33840
|
-
/** Session parent directory when in worktrees mode; null otherwise (same as {@link getIsolatedSessionParentPathForSession} path). */
|
|
33841
33933
|
getSessionWorktreeRootForSession(sessionId) {
|
|
33842
33934
|
return this.getIsolatedSessionParentPathForSession(sessionId);
|
|
33843
33935
|
}
|
|
33844
33936
|
async removeSessionWorktrees(sessionId) {
|
|
33845
|
-
const paths = this.
|
|
33846
|
-
this.sessionRepoCheckoutPaths.delete(sessionId);
|
|
33847
|
-
this.sessionParentPathBySession.delete(sessionId);
|
|
33848
|
-
this.sessionWorkingTreeRelRootBySession.delete(sessionId);
|
|
33937
|
+
const paths = this.cache.clearSession(sessionId);
|
|
33849
33938
|
if (!paths?.length) return;
|
|
33850
33939
|
await removeSessionWorktrees(paths, this.log);
|
|
33851
33940
|
}
|
|
33852
33941
|
async commitSession(params) {
|
|
33853
|
-
const paths = this.
|
|
33942
|
+
const paths = this.cache.getRepoCheckoutPathsRef(params.sessionId);
|
|
33854
33943
|
const targets = paths?.length ? paths : [getBridgeRoot()];
|
|
33855
33944
|
return commitSessionWorktrees({
|
|
33856
33945
|
paths: targets,
|
|
@@ -33859,28 +33948,17 @@ var SessionWorktreeManager = class {
|
|
|
33859
33948
|
push: params.push
|
|
33860
33949
|
});
|
|
33861
33950
|
}
|
|
33862
|
-
resolveCommitTargets(sessionId) {
|
|
33863
|
-
const paths = this.sessionRepoCheckoutPaths.get(sessionId);
|
|
33864
|
-
if (paths?.length) return paths;
|
|
33865
|
-
const disc = this.tryDiscoverFromDisk(sessionId);
|
|
33866
|
-
if (disc?.repoCheckoutPaths.length) {
|
|
33867
|
-
this.rememberSessionWorktrees(sessionId, disc);
|
|
33868
|
-
return disc.repoCheckoutPaths;
|
|
33869
|
-
}
|
|
33870
|
-
return [getBridgeRoot()];
|
|
33871
|
-
}
|
|
33872
33951
|
async getSessionWorkingTreeStatus(sessionId) {
|
|
33873
|
-
return aggregateSessionPathsWorkingTreeStatus(
|
|
33952
|
+
return aggregateSessionPathsWorkingTreeStatus(
|
|
33953
|
+
resolveCommitTargets(sessionId, this.cache, (sid) => this.discover(sid))
|
|
33954
|
+
);
|
|
33874
33955
|
}
|
|
33875
|
-
/** Per-repo changed files vs HEAD (or a single commit vs parent) for the same git roots used for commit/push. */
|
|
33876
33956
|
async getSessionWorkingTreeChangeDetails(sessionId, opts) {
|
|
33877
|
-
const targets = this.
|
|
33878
|
-
const sessionWorkingTreeRelRoot = this.sessionWorkingTreeRelRootBySession.get(sessionId) ?? null;
|
|
33879
|
-
const legacyNested = this.isLegacyNestedLayout(sessionId);
|
|
33957
|
+
const targets = resolveCommitTargets(sessionId, this.cache, (sid) => this.discover(sid));
|
|
33880
33958
|
return getWorkingTreeChangeRepoDetails({
|
|
33881
33959
|
commitTargetPaths: targets,
|
|
33882
|
-
sessionWorktreeRootPath:
|
|
33883
|
-
legacyRepoNestedSessionLayout:
|
|
33960
|
+
sessionWorktreeRootPath: this.cache.getWorkingTreeRelRoot(sessionId),
|
|
33961
|
+
legacyRepoNestedSessionLayout: this.cache.isLegacyNestedLayout(sessionId),
|
|
33884
33962
|
repoFilterRelPath: opts?.repoRelPath?.trim() ? opts.repoRelPath.trim() : null,
|
|
33885
33963
|
basis: opts?.basis,
|
|
33886
33964
|
recentCommitsLimit: opts?.recentCommitsLimit
|
|
@@ -33888,7 +33966,9 @@ var SessionWorktreeManager = class {
|
|
|
33888
33966
|
}
|
|
33889
33967
|
async pushSessionUpstream(sessionId) {
|
|
33890
33968
|
try {
|
|
33891
|
-
await pushAheadOfUpstreamForPaths(
|
|
33969
|
+
await pushAheadOfUpstreamForPaths(
|
|
33970
|
+
resolveCommitTargets(sessionId, this.cache, (sid) => this.discover(sid))
|
|
33971
|
+
);
|
|
33892
33972
|
return { ok: true };
|
|
33893
33973
|
} catch (e) {
|
|
33894
33974
|
const err = e instanceof Error ? e.message : String(e);
|
|
@@ -33896,27 +33976,31 @@ var SessionWorktreeManager = class {
|
|
|
33896
33976
|
}
|
|
33897
33977
|
}
|
|
33898
33978
|
};
|
|
33979
|
+
|
|
33980
|
+
// src/worktrees/manager/default-worktrees-root-path.ts
|
|
33981
|
+
import * as path27 from "node:path";
|
|
33982
|
+
import os8 from "node:os";
|
|
33899
33983
|
function defaultWorktreesRootPath() {
|
|
33900
|
-
return
|
|
33984
|
+
return path27.join(os8.homedir(), ".buildautomaton", "worktrees");
|
|
33901
33985
|
}
|
|
33902
33986
|
|
|
33903
33987
|
// src/files/watch-file-index.ts
|
|
33904
33988
|
import { watch } from "node:fs";
|
|
33905
|
-
import
|
|
33989
|
+
import path32 from "node:path";
|
|
33906
33990
|
|
|
33907
33991
|
// src/files/index/paths.ts
|
|
33908
|
-
import
|
|
33992
|
+
import path28 from "node:path";
|
|
33909
33993
|
import crypto2 from "node:crypto";
|
|
33910
33994
|
function getCwdHashForFileIndex(resolvedCwd) {
|
|
33911
|
-
return crypto2.createHash("sha256").update(
|
|
33995
|
+
return crypto2.createHash("sha256").update(path28.resolve(resolvedCwd)).digest("hex").slice(0, INDEX_HASH_LEN);
|
|
33912
33996
|
}
|
|
33913
33997
|
|
|
33914
33998
|
// src/files/index/build-file-index.ts
|
|
33915
|
-
import
|
|
33999
|
+
import path30 from "node:path";
|
|
33916
34000
|
|
|
33917
34001
|
// src/files/index/walk-workspace-tree.ts
|
|
33918
34002
|
import fs24 from "node:fs";
|
|
33919
|
-
import
|
|
34003
|
+
import path29 from "node:path";
|
|
33920
34004
|
var DEPENDENCY_INSTALL_DIR_NAMES = /* @__PURE__ */ new Set([
|
|
33921
34005
|
"node_modules",
|
|
33922
34006
|
"bower_components",
|
|
@@ -33945,18 +34029,18 @@ async function walkWorkspaceTreeAsync(dir, baseDir, onFile, state) {
|
|
|
33945
34029
|
if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
|
|
33946
34030
|
}
|
|
33947
34031
|
state.n++;
|
|
33948
|
-
const full =
|
|
34032
|
+
const full = path29.join(dir, name);
|
|
33949
34033
|
let stat2;
|
|
33950
34034
|
try {
|
|
33951
34035
|
stat2 = await fs24.promises.stat(full);
|
|
33952
34036
|
} catch {
|
|
33953
34037
|
continue;
|
|
33954
34038
|
}
|
|
33955
|
-
const
|
|
34039
|
+
const relative6 = path29.relative(baseDir, full).replace(/\\/g, "/");
|
|
33956
34040
|
if (stat2.isDirectory()) {
|
|
33957
34041
|
await walkWorkspaceTreeAsync(full, baseDir, onFile, state);
|
|
33958
34042
|
} else if (stat2.isFile()) {
|
|
33959
|
-
onFile(
|
|
34043
|
+
onFile(relative6);
|
|
33960
34044
|
}
|
|
33961
34045
|
}
|
|
33962
34046
|
}
|
|
@@ -34054,7 +34138,7 @@ async function collectWorkspacePathsAsync(resolved) {
|
|
|
34054
34138
|
}
|
|
34055
34139
|
async function buildFileIndexAsync(cwd) {
|
|
34056
34140
|
return withFileIndexSqliteLock(async () => {
|
|
34057
|
-
const resolved =
|
|
34141
|
+
const resolved = path30.resolve(cwd);
|
|
34058
34142
|
await yieldToEventLoop();
|
|
34059
34143
|
assertNotShutdown();
|
|
34060
34144
|
const paths = await collectWorkspacePathsAsync(resolved);
|
|
@@ -34066,7 +34150,7 @@ async function buildFileIndexAsync(cwd) {
|
|
|
34066
34150
|
}
|
|
34067
34151
|
|
|
34068
34152
|
// src/files/index/ensure-file-index.ts
|
|
34069
|
-
import
|
|
34153
|
+
import path31 from "node:path";
|
|
34070
34154
|
|
|
34071
34155
|
// src/files/index/search-file-index.ts
|
|
34072
34156
|
function escapeLikePattern(fragment) {
|
|
@@ -34118,7 +34202,7 @@ async function searchBridgeFilePathsAsync(resolvedCwd, query, limit = 100) {
|
|
|
34118
34202
|
|
|
34119
34203
|
// src/files/index/ensure-file-index.ts
|
|
34120
34204
|
async function ensureFileIndexAsync(cwd) {
|
|
34121
|
-
const resolved =
|
|
34205
|
+
const resolved = path31.resolve(cwd);
|
|
34122
34206
|
if (await bridgeFileIndexIsPopulated(resolved)) {
|
|
34123
34207
|
return { fromCache: true, pathCount: await bridgeFileIndexPathCount(resolved) };
|
|
34124
34208
|
}
|
|
@@ -34162,7 +34246,7 @@ function createFsWatcher(resolved, schedule) {
|
|
|
34162
34246
|
}
|
|
34163
34247
|
}
|
|
34164
34248
|
function startFileIndexWatcher(cwd = getBridgeRoot()) {
|
|
34165
|
-
const resolved =
|
|
34249
|
+
const resolved = path32.resolve(cwd);
|
|
34166
34250
|
void buildFileIndexAsync(resolved).catch((e) => {
|
|
34167
34251
|
if (e instanceof CliSqliteInterrupted) return;
|
|
34168
34252
|
console.error("[file-index] Initial index build failed:", e);
|
|
@@ -34192,7 +34276,7 @@ function startFileIndexWatcher(cwd = getBridgeRoot()) {
|
|
|
34192
34276
|
}
|
|
34193
34277
|
|
|
34194
34278
|
// src/connection/create-bridge-connection.ts
|
|
34195
|
-
import * as
|
|
34279
|
+
import * as path43 from "node:path";
|
|
34196
34280
|
|
|
34197
34281
|
// src/dev-servers/manager/dev-server-manager.ts
|
|
34198
34282
|
import { rm as rm2 } from "node:fs/promises";
|
|
@@ -34214,15 +34298,15 @@ function sendDevServerStatus(getWs, serverId, status, options) {
|
|
|
34214
34298
|
|
|
34215
34299
|
// src/dev-servers/process/terminate-child-process.ts
|
|
34216
34300
|
async function sigtermAndWaitForExit(proc, graceMs, log2, shortId) {
|
|
34217
|
-
const exited = new Promise((
|
|
34218
|
-
proc.once("exit", () =>
|
|
34301
|
+
const exited = new Promise((resolve20) => {
|
|
34302
|
+
proc.once("exit", () => resolve20());
|
|
34219
34303
|
});
|
|
34220
34304
|
log2(`[dev-server] Sending SIGTERM to ${shortId} (pid=${proc.pid ?? "?"}).`);
|
|
34221
34305
|
try {
|
|
34222
34306
|
proc.kill("SIGTERM");
|
|
34223
34307
|
} catch {
|
|
34224
34308
|
}
|
|
34225
|
-
await Promise.race([exited, new Promise((
|
|
34309
|
+
await Promise.race([exited, new Promise((resolve20) => setTimeout(resolve20, graceMs))]);
|
|
34226
34310
|
}
|
|
34227
34311
|
function forceKillChild(proc, log2, shortId, graceMs) {
|
|
34228
34312
|
log2(
|
|
@@ -34502,10 +34586,10 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
|
|
|
34502
34586
|
import { spawn as spawn8 } from "node:child_process";
|
|
34503
34587
|
import fs28 from "node:fs";
|
|
34504
34588
|
import { tmpdir } from "node:os";
|
|
34505
|
-
import
|
|
34589
|
+
import path33 from "node:path";
|
|
34506
34590
|
function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
34507
|
-
const tmpRoot = fs28.mkdtempSync(
|
|
34508
|
-
const logPath =
|
|
34591
|
+
const tmpRoot = fs28.mkdtempSync(path33.join(tmpdir(), "ba-devsrv-log-"));
|
|
34592
|
+
const logPath = path33.join(tmpRoot, "combined.log");
|
|
34509
34593
|
let logFd;
|
|
34510
34594
|
try {
|
|
34511
34595
|
logFd = fs28.openSync(logPath, "a");
|
|
@@ -34549,15 +34633,15 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
|
|
|
34549
34633
|
import { spawn as spawn9 } from "node:child_process";
|
|
34550
34634
|
import fs29 from "node:fs";
|
|
34551
34635
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
34552
|
-
import
|
|
34636
|
+
import path34 from "node:path";
|
|
34553
34637
|
function shSingleQuote(s) {
|
|
34554
34638
|
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
34555
34639
|
}
|
|
34556
34640
|
function trySpawnShellScriptLogRedirectUnix(command, env, cwd, signal) {
|
|
34557
|
-
const tmpRoot = fs29.mkdtempSync(
|
|
34558
|
-
const logPath =
|
|
34559
|
-
const innerPath =
|
|
34560
|
-
const runnerPath =
|
|
34641
|
+
const tmpRoot = fs29.mkdtempSync(path34.join(tmpdir2(), "ba-devsrv-sh-"));
|
|
34642
|
+
const logPath = path34.join(tmpRoot, "combined.log");
|
|
34643
|
+
const innerPath = path34.join(tmpRoot, "_cmd.sh");
|
|
34644
|
+
const runnerPath = path34.join(tmpRoot, "_run.sh");
|
|
34561
34645
|
try {
|
|
34562
34646
|
fs29.writeFileSync(innerPath, `#!/bin/sh
|
|
34563
34647
|
${command}
|
|
@@ -34588,9 +34672,9 @@ cd ${shSingleQuote(cwd)}
|
|
|
34588
34672
|
}
|
|
34589
34673
|
}
|
|
34590
34674
|
function trySpawnShellScriptLogRedirectWin(command, env, cwd, signal) {
|
|
34591
|
-
const tmpRoot = fs29.mkdtempSync(
|
|
34592
|
-
const logPath =
|
|
34593
|
-
const runnerPath =
|
|
34675
|
+
const tmpRoot = fs29.mkdtempSync(path34.join(tmpdir2(), "ba-devsrv-sh-"));
|
|
34676
|
+
const logPath = path34.join(tmpRoot, "combined.log");
|
|
34677
|
+
const runnerPath = path34.join(tmpRoot, "_run.bat");
|
|
34594
34678
|
const q = (p) => `"${p.replace(/"/g, '""')}"`;
|
|
34595
34679
|
const com = process.env.ComSpec || "cmd.exe";
|
|
34596
34680
|
try {
|
|
@@ -35364,13 +35448,13 @@ function createOnBridgeIdentified(opts) {
|
|
|
35364
35448
|
|
|
35365
35449
|
// src/skills/discover-local-agent-skills.ts
|
|
35366
35450
|
import fs30 from "node:fs";
|
|
35367
|
-
import
|
|
35451
|
+
import path35 from "node:path";
|
|
35368
35452
|
var SKILL_DISCOVERY_ROOTS = [".agents/skills", ".claude/skills", ".cursor/skills", "skills"];
|
|
35369
35453
|
function discoverLocalSkills(cwd) {
|
|
35370
35454
|
const out = [];
|
|
35371
35455
|
const seenKeys = /* @__PURE__ */ new Set();
|
|
35372
35456
|
for (const rel of SKILL_DISCOVERY_ROOTS) {
|
|
35373
|
-
const base =
|
|
35457
|
+
const base = path35.join(cwd, rel);
|
|
35374
35458
|
if (!fs30.existsSync(base) || !fs30.statSync(base).isDirectory()) continue;
|
|
35375
35459
|
let entries = [];
|
|
35376
35460
|
try {
|
|
@@ -35379,13 +35463,13 @@ function discoverLocalSkills(cwd) {
|
|
|
35379
35463
|
continue;
|
|
35380
35464
|
}
|
|
35381
35465
|
for (const name of entries) {
|
|
35382
|
-
const dir =
|
|
35466
|
+
const dir = path35.join(base, name);
|
|
35383
35467
|
try {
|
|
35384
35468
|
if (!fs30.statSync(dir).isDirectory()) continue;
|
|
35385
35469
|
} catch {
|
|
35386
35470
|
continue;
|
|
35387
35471
|
}
|
|
35388
|
-
const skillMd =
|
|
35472
|
+
const skillMd = path35.join(dir, "SKILL.md");
|
|
35389
35473
|
if (!fs30.existsSync(skillMd)) continue;
|
|
35390
35474
|
const key = `${rel}/${name}`;
|
|
35391
35475
|
if (seenKeys.has(key)) continue;
|
|
@@ -35398,7 +35482,7 @@ function discoverLocalSkills(cwd) {
|
|
|
35398
35482
|
function discoverSkillLayoutRoots(cwd) {
|
|
35399
35483
|
const roots = [];
|
|
35400
35484
|
for (const rel of SKILL_DISCOVERY_ROOTS) {
|
|
35401
|
-
const base =
|
|
35485
|
+
const base = path35.join(cwd, rel);
|
|
35402
35486
|
if (!fs30.existsSync(base) || !fs30.statSync(base).isDirectory()) continue;
|
|
35403
35487
|
let entries = [];
|
|
35404
35488
|
try {
|
|
@@ -35408,13 +35492,13 @@ function discoverSkillLayoutRoots(cwd) {
|
|
|
35408
35492
|
}
|
|
35409
35493
|
const skills2 = [];
|
|
35410
35494
|
for (const name of entries) {
|
|
35411
|
-
const dir =
|
|
35495
|
+
const dir = path35.join(base, name);
|
|
35412
35496
|
try {
|
|
35413
35497
|
if (!fs30.statSync(dir).isDirectory()) continue;
|
|
35414
35498
|
} catch {
|
|
35415
35499
|
continue;
|
|
35416
35500
|
}
|
|
35417
|
-
if (!fs30.existsSync(
|
|
35501
|
+
if (!fs30.existsSync(path35.join(dir, "SKILL.md"))) continue;
|
|
35418
35502
|
const relPath = `${rel}/${name}`.replace(/\\/g, "/");
|
|
35419
35503
|
skills2.push({ name, relPath });
|
|
35420
35504
|
}
|
|
@@ -35537,7 +35621,9 @@ var API_TO_BRIDGE_MESSAGE_TYPES = [
|
|
|
35537
35621
|
"file_browser_search",
|
|
35538
35622
|
"skill_layout_request",
|
|
35539
35623
|
"install_skills",
|
|
35540
|
-
"refresh_local_skills"
|
|
35624
|
+
"refresh_local_skills",
|
|
35625
|
+
"bridge_git_context_request",
|
|
35626
|
+
"list_repo_branches_request"
|
|
35541
35627
|
];
|
|
35542
35628
|
var API_TO_BRIDGE_TYPE_SET = new Set(API_TO_BRIDGE_MESSAGE_TYPES);
|
|
35543
35629
|
function parseApiToBridgeMessage(data, log2) {
|
|
@@ -35621,9 +35707,6 @@ var handleAgentConfigMessage = (msg, deps) => {
|
|
|
35621
35707
|
handleBridgeAgentConfig(msg, deps);
|
|
35622
35708
|
};
|
|
35623
35709
|
|
|
35624
|
-
// src/prompt-turn-queue/runner.ts
|
|
35625
|
-
import fs31 from "node:fs";
|
|
35626
|
-
|
|
35627
35710
|
// src/prompt-turn-queue/client-report.ts
|
|
35628
35711
|
function sendPromptQueueClientReport(ws, queues) {
|
|
35629
35712
|
if (!ws) return false;
|
|
@@ -35693,8 +35776,36 @@ async function mergeServerQueueSnapshot(queueKey, serverTurns) {
|
|
|
35693
35776
|
return { queueKey, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), turns };
|
|
35694
35777
|
}
|
|
35695
35778
|
|
|
35696
|
-
// src/prompt-turn-queue/runner.ts
|
|
35697
|
-
|
|
35779
|
+
// src/prompt-turn-queue/runner/dispatch-local-prompt.ts
|
|
35780
|
+
function dispatchLocalPrompt(next, deps) {
|
|
35781
|
+
const pl = next.payload;
|
|
35782
|
+
const rawParent = pl["sessionParent"];
|
|
35783
|
+
const sessionParent = rawParent === "bridge_root" || rawParent === "worktrees_root" ? rawParent : void 0;
|
|
35784
|
+
const rawParentPath = pl["sessionParentPath"];
|
|
35785
|
+
const sessionParentPath = typeof rawParentPath === "string" && rawParentPath.trim() !== "" ? rawParentPath.trim() : void 0;
|
|
35786
|
+
const rawBaseBranches = pl["worktreeBaseBranches"];
|
|
35787
|
+
const worktreeBaseBranches = rawBaseBranches != null && typeof rawBaseBranches === "object" && !Array.isArray(rawBaseBranches) ? rawBaseBranches : void 0;
|
|
35788
|
+
const msg = {
|
|
35789
|
+
type: "prompt",
|
|
35790
|
+
sessionId: next.sessionId,
|
|
35791
|
+
runId: next.turnId,
|
|
35792
|
+
prompt: pl.prompt,
|
|
35793
|
+
mode: typeof pl.mode === "string" ? pl.mode : "agent",
|
|
35794
|
+
isNewSession: pl.isNewSession === true,
|
|
35795
|
+
...sessionParent ? { sessionParent } : {},
|
|
35796
|
+
...sessionParentPath ? { sessionParentPath } : {},
|
|
35797
|
+
...worktreeBaseBranches && Object.keys(worktreeBaseBranches).length > 0 ? { worktreeBaseBranches } : {},
|
|
35798
|
+
...typeof pl.followUpCatalogPromptId === "string" ? { followUpCatalogPromptId: pl.followUpCatalogPromptId } : {},
|
|
35799
|
+
...Array.isArray(pl.sessionChangeSummaryFilePaths) ? { sessionChangeSummaryFilePaths: pl.sessionChangeSummaryFilePaths } : {},
|
|
35800
|
+
...Array.isArray(pl.sessionChangeSummaryFileSnapshots) ? { sessionChangeSummaryFileSnapshots: pl.sessionChangeSummaryFileSnapshots } : {},
|
|
35801
|
+
...typeof pl.agentType === "string" && pl.agentType.trim() ? { agentType: pl.agentType.trim() } : {},
|
|
35802
|
+
...pl.agentConfig != null && typeof pl.agentConfig === "object" && !Array.isArray(pl.agentConfig) && Object.keys(pl.agentConfig).length > 0 ? { agentConfig: pl.agentConfig } : {},
|
|
35803
|
+
...Array.isArray(pl.attachments) && pl.attachments.length > 0 ? { attachments: pl.attachments } : {}
|
|
35804
|
+
};
|
|
35805
|
+
handleBridgePrompt(msg, deps);
|
|
35806
|
+
}
|
|
35807
|
+
|
|
35808
|
+
// src/prompt-turn-queue/runner/queue-selection.ts
|
|
35698
35809
|
function isRunnableServerState(s) {
|
|
35699
35810
|
return s === "queued" || s === "requeued" || s === "requeued_with_revert";
|
|
35700
35811
|
}
|
|
@@ -35713,6 +35824,28 @@ function pickNextRunnableTurn(turns) {
|
|
|
35713
35824
|
function hasRunningTurn(turns) {
|
|
35714
35825
|
return turns.some((t) => t.lastClientState === "running");
|
|
35715
35826
|
}
|
|
35827
|
+
|
|
35828
|
+
// src/prompt-turn-queue/runner/run-id-queue-key-map.ts
|
|
35829
|
+
var runIdToQueueKey = /* @__PURE__ */ new Map();
|
|
35830
|
+
function getRunIdQueueKey(runId) {
|
|
35831
|
+
return runIdToQueueKey.get(runId);
|
|
35832
|
+
}
|
|
35833
|
+
function setRunIdQueueKey(runId, queueKey) {
|
|
35834
|
+
runIdToQueueKey.set(runId, queueKey);
|
|
35835
|
+
}
|
|
35836
|
+
function deleteRunIdQueueKey(runId) {
|
|
35837
|
+
const queueKey = runIdToQueueKey.get(runId);
|
|
35838
|
+
runIdToQueueKey.delete(runId);
|
|
35839
|
+
return queueKey;
|
|
35840
|
+
}
|
|
35841
|
+
function syncRunningTurnQueueKeys(turns, queueKey) {
|
|
35842
|
+
for (const running of turns.filter((t) => t.lastClientState === "running")) {
|
|
35843
|
+
runIdToQueueKey.set(running.turnId, queueKey);
|
|
35844
|
+
}
|
|
35845
|
+
}
|
|
35846
|
+
|
|
35847
|
+
// src/prompt-turn-queue/runner/run-local-revert-before-queued-prompt.ts
|
|
35848
|
+
import fs31 from "node:fs";
|
|
35716
35849
|
async function runLocalRevertBeforeQueuedPrompt(next, deps) {
|
|
35717
35850
|
if (next.serverState !== "requeued_with_revert") return true;
|
|
35718
35851
|
const sid = next.sessionId;
|
|
@@ -35734,30 +35867,23 @@ async function runLocalRevertBeforeQueuedPrompt(next, deps) {
|
|
|
35734
35867
|
}
|
|
35735
35868
|
return res.ok;
|
|
35736
35869
|
}
|
|
35737
|
-
|
|
35738
|
-
|
|
35739
|
-
|
|
35740
|
-
|
|
35741
|
-
const
|
|
35742
|
-
|
|
35743
|
-
const
|
|
35744
|
-
|
|
35745
|
-
|
|
35746
|
-
|
|
35747
|
-
|
|
35748
|
-
|
|
35749
|
-
|
|
35750
|
-
|
|
35751
|
-
...sessionParentPath ? { sessionParentPath } : {},
|
|
35752
|
-
...typeof pl.followUpCatalogPromptId === "string" ? { followUpCatalogPromptId: pl.followUpCatalogPromptId } : {},
|
|
35753
|
-
...Array.isArray(pl.sessionChangeSummaryFilePaths) ? { sessionChangeSummaryFilePaths: pl.sessionChangeSummaryFilePaths } : {},
|
|
35754
|
-
...Array.isArray(pl.sessionChangeSummaryFileSnapshots) ? { sessionChangeSummaryFileSnapshots: pl.sessionChangeSummaryFileSnapshots } : {},
|
|
35755
|
-
...typeof pl.agentType === "string" && pl.agentType.trim() ? { agentType: pl.agentType.trim() } : {},
|
|
35756
|
-
...pl.agentConfig != null && typeof pl.agentConfig === "object" && !Array.isArray(pl.agentConfig) && Object.keys(pl.agentConfig).length > 0 ? { agentConfig: pl.agentConfig } : {},
|
|
35757
|
-
...Array.isArray(pl.attachments) && pl.attachments.length > 0 ? { attachments: pl.attachments } : {}
|
|
35758
|
-
};
|
|
35759
|
-
handleBridgePrompt(msg, deps);
|
|
35870
|
+
|
|
35871
|
+
// src/prompt-turn-queue/runner/finalize-prompt-turn-on-bridge.ts
|
|
35872
|
+
async function finalizePromptTurnOnBridge(getWs, runId, success2, opts) {
|
|
35873
|
+
if (!runId) return false;
|
|
35874
|
+
const queueKey = deleteRunIdQueueKey(runId);
|
|
35875
|
+
if (!queueKey) return false;
|
|
35876
|
+
const f = await readPersistedQueue(queueKey);
|
|
35877
|
+
if (!f) return false;
|
|
35878
|
+
const t = f.turns.find((x) => x.turnId === runId);
|
|
35879
|
+
if (!t) return false;
|
|
35880
|
+
t.lastClientState = opts?.terminalClientState ?? (success2 ? "stopped" : "failed");
|
|
35881
|
+
await writePersistedQueue(f);
|
|
35882
|
+
sendPromptQueueClientReport(getWs(), { [queueKey]: [{ turnId: runId, clientState: t.lastClientState }] });
|
|
35883
|
+
return true;
|
|
35760
35884
|
}
|
|
35885
|
+
|
|
35886
|
+
// src/prompt-turn-queue/runner/apply-prompt-queue-state-from-server.ts
|
|
35761
35887
|
async function applyPromptQueueStateFromServer(msg, deps) {
|
|
35762
35888
|
const raw = msg.queues;
|
|
35763
35889
|
if (!raw || typeof raw !== "object") return;
|
|
@@ -35771,9 +35897,7 @@ async function applyPromptQueueStateFromServer(msg, deps) {
|
|
|
35771
35897
|
if (!Array.isArray(serverTurns)) continue;
|
|
35772
35898
|
const file2 = await readPersistedQueue(queueKey);
|
|
35773
35899
|
if (!file2) continue;
|
|
35774
|
-
|
|
35775
|
-
runIdToQueueKey.set(running.turnId, queueKey);
|
|
35776
|
-
}
|
|
35900
|
+
syncRunningTurnQueueKeys(file2.turns, queueKey);
|
|
35777
35901
|
}
|
|
35778
35902
|
for (const [queueKey, serverTurns] of Object.entries(raw)) {
|
|
35779
35903
|
if (!Array.isArray(serverTurns)) continue;
|
|
@@ -35818,7 +35942,7 @@ async function applyPromptQueueStateFromServer(msg, deps) {
|
|
|
35818
35942
|
}
|
|
35819
35943
|
next.lastClientState = "running";
|
|
35820
35944
|
await writePersistedQueue(file2);
|
|
35821
|
-
|
|
35945
|
+
setRunIdQueueKey(next.turnId, queueKey);
|
|
35822
35946
|
startedThisTick.add(next.turnId);
|
|
35823
35947
|
report[queueKey] = [{ turnId: next.turnId, clientState: "running" }];
|
|
35824
35948
|
}
|
|
@@ -35831,24 +35955,10 @@ async function applyPromptQueueStateFromServer(msg, deps) {
|
|
|
35831
35955
|
if (!file2) continue;
|
|
35832
35956
|
const running = file2.turns.find((t) => t.lastClientState === "running");
|
|
35833
35957
|
if (!running || !startedThisTick.has(running.turnId)) continue;
|
|
35834
|
-
if (
|
|
35958
|
+
if (getRunIdQueueKey(running.turnId) !== queueKey) continue;
|
|
35835
35959
|
dispatchLocalPrompt(running, deps);
|
|
35836
35960
|
}
|
|
35837
35961
|
}
|
|
35838
|
-
async function finalizePromptTurnOnBridge(getWs, runId, success2, opts) {
|
|
35839
|
-
if (!runId) return false;
|
|
35840
|
-
const queueKey = runIdToQueueKey.get(runId);
|
|
35841
|
-
runIdToQueueKey.delete(runId);
|
|
35842
|
-
if (!queueKey) return false;
|
|
35843
|
-
const f = await readPersistedQueue(queueKey);
|
|
35844
|
-
if (!f) return false;
|
|
35845
|
-
const t = f.turns.find((x) => x.turnId === runId);
|
|
35846
|
-
if (!t) return false;
|
|
35847
|
-
t.lastClientState = opts?.terminalClientState ?? (success2 ? "stopped" : "failed");
|
|
35848
|
-
await writePersistedQueue(f);
|
|
35849
|
-
sendPromptQueueClientReport(getWs(), { [queueKey]: [{ turnId: runId, clientState: t.lastClientState }] });
|
|
35850
|
-
return true;
|
|
35851
|
-
}
|
|
35852
35962
|
|
|
35853
35963
|
// src/agents/acp/from-bridge/bridge-prompt-wiring.ts
|
|
35854
35964
|
function createBridgePromptSenders(deps, getWs) {
|
|
@@ -35895,6 +36005,91 @@ function createBridgePromptSenders(deps, getWs) {
|
|
|
35895
36005
|
return { sendBridgeMessage, sendResult: sendResult2, sendSessionUpdate };
|
|
35896
36006
|
}
|
|
35897
36007
|
|
|
36008
|
+
// src/agents/acp/from-bridge/handle-bridge-prompt/parse-bridge-attachments.ts
|
|
36009
|
+
function parseBridgeAttachments(msg) {
|
|
36010
|
+
const raw = msg.attachments;
|
|
36011
|
+
if (!Array.isArray(raw)) return [];
|
|
36012
|
+
const out = [];
|
|
36013
|
+
for (const x of raw) {
|
|
36014
|
+
if (x === null || typeof x !== "object" || Array.isArray(x)) continue;
|
|
36015
|
+
const o = x;
|
|
36016
|
+
const id = typeof o.attachmentId === "string" ? o.attachmentId.trim() : "";
|
|
36017
|
+
if (!id) continue;
|
|
36018
|
+
const mt = typeof o.mimeType === "string" && o.mimeType.trim() ? o.mimeType.trim() : "application/octet-stream";
|
|
36019
|
+
out.push({ attachmentId: id, mimeType: mt });
|
|
36020
|
+
}
|
|
36021
|
+
return out;
|
|
36022
|
+
}
|
|
36023
|
+
|
|
36024
|
+
// src/agents/acp/from-bridge/handle-bridge-prompt/parse-worktree-base-branches.ts
|
|
36025
|
+
function parseWorktreeBaseBranches(msg) {
|
|
36026
|
+
const raw = msg.worktreeBaseBranches;
|
|
36027
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
36028
|
+
const out = {};
|
|
36029
|
+
for (const [k, v] of Object.entries(raw)) {
|
|
36030
|
+
if (typeof k !== "string" || typeof v !== "string") continue;
|
|
36031
|
+
const rel = k.trim();
|
|
36032
|
+
const branch = v.trim();
|
|
36033
|
+
if (rel && branch) out[rel] = branch;
|
|
36034
|
+
}
|
|
36035
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
36036
|
+
}
|
|
36037
|
+
|
|
36038
|
+
// src/agents/acp/change-summary/decrypt-change-summary-file-input.ts
|
|
36039
|
+
function decryptChangeSummaryFileInput(row, e2ee) {
|
|
36040
|
+
if (!e2ee) return row;
|
|
36041
|
+
for (const field of ["path", "patchContent", "oldText", "newText"]) {
|
|
36042
|
+
const raw = row[field];
|
|
36043
|
+
if (typeof raw !== "string" || raw.trim() === "") continue;
|
|
36044
|
+
let o;
|
|
36045
|
+
try {
|
|
36046
|
+
o = JSON.parse(raw);
|
|
36047
|
+
} catch {
|
|
36048
|
+
continue;
|
|
36049
|
+
}
|
|
36050
|
+
if (!isE2eeEnvelope(o.ee)) continue;
|
|
36051
|
+
try {
|
|
36052
|
+
const d = e2ee.decryptMessage(o);
|
|
36053
|
+
const out = {
|
|
36054
|
+
path: typeof d.path === "string" ? d.path : row.path
|
|
36055
|
+
};
|
|
36056
|
+
if (d.directoryRemoved === true) out.directoryRemoved = true;
|
|
36057
|
+
else if (row.directoryRemoved === true) out.directoryRemoved = true;
|
|
36058
|
+
if (typeof d.patchContent === "string") out.patchContent = d.patchContent;
|
|
36059
|
+
else if (typeof row.patchContent === "string" && row.patchContent !== raw) out.patchContent = row.patchContent;
|
|
36060
|
+
if (typeof d.oldText === "string") out.oldText = d.oldText;
|
|
36061
|
+
else if (typeof row.oldText === "string") out.oldText = row.oldText;
|
|
36062
|
+
if (typeof d.newText === "string") out.newText = d.newText;
|
|
36063
|
+
else if (typeof row.newText === "string") out.newText = row.newText;
|
|
36064
|
+
return out;
|
|
36065
|
+
} catch {
|
|
36066
|
+
return row;
|
|
36067
|
+
}
|
|
36068
|
+
}
|
|
36069
|
+
return row;
|
|
36070
|
+
}
|
|
36071
|
+
|
|
36072
|
+
// src/agents/acp/change-summary/resolve-change-summary-prompt-for-agent.ts
|
|
36073
|
+
function hasSummarizePayload(f) {
|
|
36074
|
+
return f.directoryRemoved === true || f.patchContent != null && f.patchContent.trim() !== "" || f.oldText != null && f.oldText.trim() !== "" || f.newText != null && f.newText.trim() !== "";
|
|
36075
|
+
}
|
|
36076
|
+
function resolveChangeSummaryPromptForAgent(params) {
|
|
36077
|
+
const isBuiltin = params.followUpCatalogPromptId === BUILTIN_SESSION_CHANGE_SUMMARY_FOLLOW_UP_CATALOG_PROMPT_ID;
|
|
36078
|
+
const snaps = params.sessionChangeSummaryFileSnapshots;
|
|
36079
|
+
if (!isBuiltin || !snaps || snaps.length === 0) {
|
|
36080
|
+
return { promptText: params.bridgePromptText, sessionChangeSummaryFilePaths: void 0 };
|
|
36081
|
+
}
|
|
36082
|
+
const decrypted = dedupeSessionFileChangesByPath(snaps.map((row) => decryptChangeSummaryFileInput(row, params.e2ee)));
|
|
36083
|
+
const withPayload = decrypted.filter(hasSummarizePayload);
|
|
36084
|
+
if (withPayload.length === 0) {
|
|
36085
|
+
return { promptText: params.bridgePromptText, sessionChangeSummaryFilePaths: void 0 };
|
|
36086
|
+
}
|
|
36087
|
+
return {
|
|
36088
|
+
promptText: buildSessionChangeSummaryPrompt(withPayload),
|
|
36089
|
+
sessionChangeSummaryFilePaths: withPayload.map((f) => f.path)
|
|
36090
|
+
};
|
|
36091
|
+
}
|
|
36092
|
+
|
|
35898
36093
|
// src/agents/acp/from-bridge/bridge-prompt-preamble.ts
|
|
35899
36094
|
import { execFile as execFile8 } from "node:child_process";
|
|
35900
36095
|
import { promisify as promisify8 } from "node:util";
|
|
@@ -35947,9 +36142,9 @@ function parseChangeSummarySnapshots(raw) {
|
|
|
35947
36142
|
for (const item of raw) {
|
|
35948
36143
|
if (!item || typeof item !== "object") continue;
|
|
35949
36144
|
const o = item;
|
|
35950
|
-
const
|
|
35951
|
-
if (!
|
|
35952
|
-
const row = { path:
|
|
36145
|
+
const path44 = typeof o.path === "string" && o.path.trim() !== "" ? o.path.trim() : "";
|
|
36146
|
+
if (!path44) continue;
|
|
36147
|
+
const row = { path: path44 };
|
|
35953
36148
|
if (typeof o.patchContent === "string") row.patchContent = o.patchContent;
|
|
35954
36149
|
if (typeof o.oldText === "string") row.oldText = o.oldText;
|
|
35955
36150
|
if (typeof o.newText === "string") row.newText = o.newText;
|
|
@@ -35966,76 +36161,73 @@ function parseFollowUpFieldsFromPromptMessage(msg) {
|
|
|
35966
36161
|
return { followUpCatalogPromptId, sessionChangeSummaryFilePaths, sessionChangeSummaryFileSnapshots };
|
|
35967
36162
|
}
|
|
35968
36163
|
|
|
35969
|
-
// src/agents/acp/
|
|
35970
|
-
function
|
|
35971
|
-
|
|
35972
|
-
|
|
35973
|
-
|
|
35974
|
-
|
|
35975
|
-
|
|
35976
|
-
|
|
35977
|
-
|
|
35978
|
-
|
|
35979
|
-
|
|
35980
|
-
|
|
35981
|
-
|
|
35982
|
-
|
|
35983
|
-
|
|
35984
|
-
|
|
35985
|
-
|
|
35986
|
-
|
|
35987
|
-
|
|
35988
|
-
|
|
35989
|
-
|
|
35990
|
-
|
|
35991
|
-
|
|
35992
|
-
|
|
35993
|
-
|
|
35994
|
-
|
|
35995
|
-
|
|
35996
|
-
|
|
35997
|
-
|
|
35998
|
-
|
|
35999
|
-
|
|
36000
|
-
|
|
36001
|
-
}
|
|
36002
|
-
|
|
36003
|
-
|
|
36004
|
-
|
|
36005
|
-
|
|
36006
|
-
|
|
36007
|
-
|
|
36008
|
-
|
|
36009
|
-
|
|
36010
|
-
|
|
36011
|
-
|
|
36012
|
-
}
|
|
36013
|
-
const decrypted = dedupeSessionFileChangesByPath(snaps.map((row) => decryptChangeSummaryFileInput(row, params.e2ee)));
|
|
36014
|
-
const withPayload = decrypted.filter(hasSummarizePayload);
|
|
36015
|
-
if (withPayload.length === 0) {
|
|
36016
|
-
return { promptText: params.bridgePromptText, sessionChangeSummaryFilePaths: void 0 };
|
|
36164
|
+
// src/agents/acp/from-bridge/handle-bridge-prompt/run-preamble-and-prompt.ts
|
|
36165
|
+
async function runPreambleAndPrompt(params) {
|
|
36166
|
+
const {
|
|
36167
|
+
deps,
|
|
36168
|
+
msg,
|
|
36169
|
+
getWs,
|
|
36170
|
+
log: log2,
|
|
36171
|
+
sessionWorktreeManager,
|
|
36172
|
+
sessionId,
|
|
36173
|
+
runId,
|
|
36174
|
+
promptText,
|
|
36175
|
+
attachments,
|
|
36176
|
+
mode,
|
|
36177
|
+
agentType,
|
|
36178
|
+
agentId,
|
|
36179
|
+
agentConfig,
|
|
36180
|
+
resolvedCwd,
|
|
36181
|
+
senders: { sendResult: sendResult2, sendSessionUpdate }
|
|
36182
|
+
} = params;
|
|
36183
|
+
const effectiveCwd = resolveSessionParentPathForAgentProcess(resolvedCwd);
|
|
36184
|
+
await runBridgePromptPreamble({
|
|
36185
|
+
getWs,
|
|
36186
|
+
log: log2,
|
|
36187
|
+
sessionWorktreeManager,
|
|
36188
|
+
sessionId,
|
|
36189
|
+
runId,
|
|
36190
|
+
effectiveCwd
|
|
36191
|
+
});
|
|
36192
|
+
const {
|
|
36193
|
+
followUpCatalogPromptId,
|
|
36194
|
+
sessionChangeSummaryFilePaths: pathsFromBridge,
|
|
36195
|
+
sessionChangeSummaryFileSnapshots
|
|
36196
|
+
} = parseFollowUpFieldsFromPromptMessage(msg);
|
|
36197
|
+
const { promptText: resolvedPromptText, sessionChangeSummaryFilePaths } = resolveChangeSummaryPromptForAgent({
|
|
36198
|
+
followUpCatalogPromptId,
|
|
36199
|
+
sessionChangeSummaryFileSnapshots,
|
|
36200
|
+
bridgePromptText: promptText,
|
|
36201
|
+
e2ee: deps.e2ee
|
|
36202
|
+
});
|
|
36203
|
+
if (sessionChangeSummaryFileSnapshots && sessionChangeSummaryFileSnapshots.length > 0 && resolvedPromptText === promptText) {
|
|
36204
|
+
deps.log(
|
|
36205
|
+
"[Agent] Change-summary snapshots were present but the prompt was not rebuilt (decrypt failed or empty payloads); sending the bridge prompt as-is."
|
|
36206
|
+
);
|
|
36017
36207
|
}
|
|
36018
|
-
|
|
36019
|
-
|
|
36020
|
-
|
|
36021
|
-
|
|
36208
|
+
const pathsForUpload = sessionChangeSummaryFilePaths ?? pathsFromBridge;
|
|
36209
|
+
deps.acpManager.handlePrompt({
|
|
36210
|
+
promptText: resolvedPromptText,
|
|
36211
|
+
promptId: msg.id,
|
|
36212
|
+
sessionId,
|
|
36213
|
+
runId,
|
|
36214
|
+
mode,
|
|
36215
|
+
agentType,
|
|
36216
|
+
agentId,
|
|
36217
|
+
agentConfig,
|
|
36218
|
+
sessionParentPath: effectiveCwd,
|
|
36219
|
+
sendResult: sendResult2,
|
|
36220
|
+
sendSessionUpdate,
|
|
36221
|
+
followUpCatalogPromptId,
|
|
36222
|
+
sessionChangeSummaryFilePaths: pathsForUpload,
|
|
36223
|
+
cloudApiBaseUrl: deps.cloudApiBaseUrl,
|
|
36224
|
+
getCloudAccessToken: deps.getCloudAccessToken,
|
|
36225
|
+
e2ee: deps.e2ee,
|
|
36226
|
+
...attachments.length > 0 ? { attachments } : {}
|
|
36227
|
+
});
|
|
36022
36228
|
}
|
|
36023
36229
|
|
|
36024
|
-
// src/agents/acp/from-bridge/handle-bridge-prompt.ts
|
|
36025
|
-
function parseBridgeAttachments(msg) {
|
|
36026
|
-
const raw = msg.attachments;
|
|
36027
|
-
if (!Array.isArray(raw)) return [];
|
|
36028
|
-
const out = [];
|
|
36029
|
-
for (const x of raw) {
|
|
36030
|
-
if (x === null || typeof x !== "object" || Array.isArray(x)) continue;
|
|
36031
|
-
const o = x;
|
|
36032
|
-
const id = typeof o.attachmentId === "string" ? o.attachmentId.trim() : "";
|
|
36033
|
-
if (!id) continue;
|
|
36034
|
-
const mt = typeof o.mimeType === "string" && o.mimeType.trim() ? o.mimeType.trim() : "application/octet-stream";
|
|
36035
|
-
out.push({ attachmentId: id, mimeType: mt });
|
|
36036
|
-
}
|
|
36037
|
-
return out;
|
|
36038
|
-
}
|
|
36230
|
+
// src/agents/acp/from-bridge/handle-bridge-prompt/handle-bridge-prompt.ts
|
|
36039
36231
|
function handleBridgePrompt(msg, deps) {
|
|
36040
36232
|
const { getWs, log: log2, acpManager, sessionWorktreeManager } = deps;
|
|
36041
36233
|
const rawPrompt = msg.prompt;
|
|
@@ -36044,12 +36236,12 @@ function handleBridgePrompt(msg, deps) {
|
|
|
36044
36236
|
const sessionId = msg.sessionId;
|
|
36045
36237
|
const runId = typeof msg.runId === "string" ? msg.runId : void 0;
|
|
36046
36238
|
const promptId = typeof msg.id === "string" ? msg.id : void 0;
|
|
36047
|
-
const
|
|
36239
|
+
const senders = createBridgePromptSenders(deps, getWs);
|
|
36048
36240
|
if (!promptText.trim() && attachments.length === 0) {
|
|
36049
36241
|
log2(
|
|
36050
36242
|
`[Bridge service] Prompt ignored: empty or missing prompt text (session ${typeof msg.sessionId === "string" ? msg.sessionId.slice(0, 8) : "\u2014"}\u2026, run ${typeof msg.runId === "string" ? msg.runId.slice(0, 8) : "\u2014"}\u2026).`
|
|
36051
36243
|
);
|
|
36052
|
-
sendBridgeMessage(
|
|
36244
|
+
senders.sendBridgeMessage(
|
|
36053
36245
|
{
|
|
36054
36246
|
type: "prompt_result",
|
|
36055
36247
|
...promptId ? { id: promptId } : {},
|
|
@@ -36071,57 +36263,50 @@ function handleBridgePrompt(msg, deps) {
|
|
|
36071
36263
|
const agentId = typeof rawAgentId === "string" && rawAgentId.trim() !== "" ? rawAgentId.trim() : null;
|
|
36072
36264
|
const mode = typeof msg.mode === "string" && msg.mode.trim() ? msg.mode.trim() : void 0;
|
|
36073
36265
|
const agentConfig = msg.agentConfig != null && typeof msg.agentConfig === "object" && !Array.isArray(msg.agentConfig) ? msg.agentConfig : void 0;
|
|
36266
|
+
const worktreeBaseBranches = parseWorktreeBaseBranches(msg);
|
|
36074
36267
|
acpManager.logPromptReceivedFromBridge({ agentType, mode });
|
|
36075
|
-
|
|
36076
|
-
|
|
36077
|
-
|
|
36268
|
+
void sessionWorktreeManager.resolveSessionParentPathForPrompt(sessionId, {
|
|
36269
|
+
isNewSession,
|
|
36270
|
+
sessionParent,
|
|
36271
|
+
sessionParentPath,
|
|
36272
|
+
...worktreeBaseBranches ? { worktreeBaseBranches } : {}
|
|
36273
|
+
}).then(
|
|
36274
|
+
(cwd) => runPreambleAndPrompt({
|
|
36275
|
+
deps,
|
|
36276
|
+
msg,
|
|
36078
36277
|
getWs,
|
|
36079
36278
|
log: log2,
|
|
36080
36279
|
sessionWorktreeManager,
|
|
36081
36280
|
sessionId,
|
|
36082
36281
|
runId,
|
|
36083
|
-
|
|
36084
|
-
|
|
36085
|
-
|
|
36086
|
-
|
|
36087
|
-
|
|
36088
|
-
|
|
36089
|
-
|
|
36090
|
-
|
|
36091
|
-
|
|
36092
|
-
|
|
36093
|
-
|
|
36094
|
-
|
|
36095
|
-
|
|
36096
|
-
|
|
36097
|
-
|
|
36098
|
-
|
|
36099
|
-
|
|
36100
|
-
}
|
|
36101
|
-
const pathsForUpload = sessionChangeSummaryFilePaths ?? pathsFromBridge;
|
|
36102
|
-
acpManager.handlePrompt({
|
|
36103
|
-
promptText: resolvedPromptText,
|
|
36104
|
-
promptId: msg.id,
|
|
36282
|
+
promptText,
|
|
36283
|
+
attachments,
|
|
36284
|
+
mode,
|
|
36285
|
+
agentType,
|
|
36286
|
+
agentId,
|
|
36287
|
+
agentConfig,
|
|
36288
|
+
resolvedCwd: cwd,
|
|
36289
|
+
senders
|
|
36290
|
+
})
|
|
36291
|
+
).catch((err) => {
|
|
36292
|
+
log2(`[Agent] Session parent path resolve failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
36293
|
+
void runPreambleAndPrompt({
|
|
36294
|
+
deps,
|
|
36295
|
+
msg,
|
|
36296
|
+
getWs,
|
|
36297
|
+
log: log2,
|
|
36298
|
+
sessionWorktreeManager,
|
|
36105
36299
|
sessionId,
|
|
36106
36300
|
runId,
|
|
36301
|
+
promptText,
|
|
36302
|
+
attachments,
|
|
36107
36303
|
mode,
|
|
36108
36304
|
agentType,
|
|
36109
36305
|
agentId,
|
|
36110
36306
|
agentConfig,
|
|
36111
|
-
|
|
36112
|
-
|
|
36113
|
-
sendSessionUpdate,
|
|
36114
|
-
followUpCatalogPromptId,
|
|
36115
|
-
sessionChangeSummaryFilePaths: pathsForUpload,
|
|
36116
|
-
cloudApiBaseUrl: deps.cloudApiBaseUrl,
|
|
36117
|
-
getCloudAccessToken: deps.getCloudAccessToken,
|
|
36118
|
-
e2ee: deps.e2ee,
|
|
36119
|
-
...attachments.length > 0 ? { attachments } : {}
|
|
36307
|
+
resolvedCwd: void 0,
|
|
36308
|
+
senders
|
|
36120
36309
|
});
|
|
36121
|
-
}
|
|
36122
|
-
void sessionWorktreeManager.resolveSessionParentPathForPrompt(sessionId, { isNewSession, sessionParent, sessionParentPath }).then((cwd) => preambleAndPrompt(cwd)).catch((err) => {
|
|
36123
|
-
log2(`[Agent] Session parent path resolve failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
36124
|
-
void preambleAndPrompt(void 0);
|
|
36125
36310
|
});
|
|
36126
36311
|
}
|
|
36127
36312
|
|
|
@@ -36176,11 +36361,11 @@ var handleSkillCallMessage = (msg, { getWs, log: log2 }) => {
|
|
|
36176
36361
|
import fs33 from "node:fs";
|
|
36177
36362
|
|
|
36178
36363
|
// src/files/ensure-under-cwd.ts
|
|
36179
|
-
import
|
|
36364
|
+
import path36 from "node:path";
|
|
36180
36365
|
function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
|
|
36181
|
-
const normalized =
|
|
36182
|
-
const resolved =
|
|
36183
|
-
if (!resolved.startsWith(cwd +
|
|
36366
|
+
const normalized = path36.normalize(relativePath).replace(/^(\.\/)+/, "");
|
|
36367
|
+
const resolved = path36.resolve(cwd, normalized);
|
|
36368
|
+
if (!resolved.startsWith(cwd + path36.sep) && resolved !== cwd) {
|
|
36184
36369
|
return null;
|
|
36185
36370
|
}
|
|
36186
36371
|
return resolved;
|
|
@@ -36190,11 +36375,11 @@ function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
|
|
|
36190
36375
|
var LIST_DIR_YIELD_EVERY = 256;
|
|
36191
36376
|
|
|
36192
36377
|
// src/files/list-dir/map-dir-entry.ts
|
|
36193
|
-
import
|
|
36378
|
+
import path37 from "node:path";
|
|
36194
36379
|
import fs32 from "node:fs";
|
|
36195
36380
|
async function mapDirEntry(d, relativePath, resolved) {
|
|
36196
|
-
const entryPath =
|
|
36197
|
-
const fullPath =
|
|
36381
|
+
const entryPath = path37.join(relativePath || ".", d.name).replace(/\\/g, "/");
|
|
36382
|
+
const fullPath = path37.join(resolved, d.name);
|
|
36198
36383
|
let isDir = d.isDirectory();
|
|
36199
36384
|
if (d.isSymbolicLink()) {
|
|
36200
36385
|
try {
|
|
@@ -36545,13 +36730,13 @@ function resolveFileBrowserSessionParent(sessionWorktreeManager, sessionId) {
|
|
|
36545
36730
|
}
|
|
36546
36731
|
|
|
36547
36732
|
// src/files/handle-file-browser-search.ts
|
|
36548
|
-
import
|
|
36733
|
+
import path38 from "node:path";
|
|
36549
36734
|
var SEARCH_LIMIT = 100;
|
|
36550
36735
|
function handleFileBrowserSearch(msg, socket, e2ee, sessionWorktreeManager) {
|
|
36551
36736
|
void (async () => {
|
|
36552
36737
|
await yieldToEventLoop();
|
|
36553
36738
|
const q = typeof msg.q === "string" ? msg.q : "";
|
|
36554
|
-
const sessionParentPath =
|
|
36739
|
+
const sessionParentPath = path38.resolve(
|
|
36555
36740
|
sessionWorktreeManager != null ? resolveFileBrowserSessionParent(sessionWorktreeManager, msg.sessionId) : getBridgeRoot()
|
|
36556
36741
|
);
|
|
36557
36742
|
if (!await bridgeFileIndexIsPopulated(sessionParentPath)) {
|
|
@@ -36671,7 +36856,7 @@ function handleSkillLayoutRequest(msg, deps) {
|
|
|
36671
36856
|
|
|
36672
36857
|
// src/skills/install-remote-skills.ts
|
|
36673
36858
|
import fs37 from "node:fs";
|
|
36674
|
-
import
|
|
36859
|
+
import path39 from "node:path";
|
|
36675
36860
|
function installRemoteSkills(cwd, targetDir, items) {
|
|
36676
36861
|
const installed2 = [];
|
|
36677
36862
|
if (!Array.isArray(items)) {
|
|
@@ -36682,11 +36867,11 @@ function installRemoteSkills(cwd, targetDir, items) {
|
|
|
36682
36867
|
if (typeof item.sourceId !== "string" || typeof item.skillName !== "string" || typeof item.versionHash !== "string" || !Array.isArray(item.files)) {
|
|
36683
36868
|
continue;
|
|
36684
36869
|
}
|
|
36685
|
-
const skillDir =
|
|
36870
|
+
const skillDir = path39.join(cwd, targetDir, item.skillName);
|
|
36686
36871
|
for (const f of item.files) {
|
|
36687
36872
|
if (typeof f.path !== "string" || !f.text && !f.base64) continue;
|
|
36688
|
-
const dest =
|
|
36689
|
-
fs37.mkdirSync(
|
|
36873
|
+
const dest = path39.join(skillDir, f.path);
|
|
36874
|
+
fs37.mkdirSync(path39.dirname(dest), { recursive: true });
|
|
36690
36875
|
if (f.text !== void 0) {
|
|
36691
36876
|
fs37.writeFileSync(dest, f.text, "utf8");
|
|
36692
36877
|
} else if (f.base64) {
|
|
@@ -36902,6 +37087,148 @@ var handleDevServersConfig = (msg, deps) => {
|
|
|
36902
37087
|
deps.devServerManager?.applyConfig(devServers ?? []);
|
|
36903
37088
|
};
|
|
36904
37089
|
|
|
37090
|
+
// src/git/bridge-git-context.ts
|
|
37091
|
+
import * as path40 from "node:path";
|
|
37092
|
+
|
|
37093
|
+
// src/git/branches/get-current-branch.ts
|
|
37094
|
+
async function getCurrentBranch(repoPath) {
|
|
37095
|
+
try {
|
|
37096
|
+
const git = cliSimpleGit(repoPath);
|
|
37097
|
+
const branch = await git.revparse(["--abbrev-ref", "HEAD"]);
|
|
37098
|
+
const trimmed2 = typeof branch === "string" ? branch.trim() : "";
|
|
37099
|
+
if (!trimmed2 || trimmed2 === "HEAD") return null;
|
|
37100
|
+
return trimmed2;
|
|
37101
|
+
} catch {
|
|
37102
|
+
return null;
|
|
37103
|
+
}
|
|
37104
|
+
}
|
|
37105
|
+
|
|
37106
|
+
// src/git/branches/list-repo-branch-refs.ts
|
|
37107
|
+
function normalizeBranchRef(raw) {
|
|
37108
|
+
const trimmed2 = raw.trim();
|
|
37109
|
+
if (!trimmed2 || trimmed2 === "HEAD") return null;
|
|
37110
|
+
if (trimmed2.startsWith("refs/heads/")) return trimmed2.slice("refs/heads/".length);
|
|
37111
|
+
if (trimmed2.startsWith("refs/remotes/")) return trimmed2.slice("refs/remotes/".length);
|
|
37112
|
+
return trimmed2;
|
|
37113
|
+
}
|
|
37114
|
+
async function listRepoBranchRefs(repoPath) {
|
|
37115
|
+
try {
|
|
37116
|
+
const git = cliSimpleGit(repoPath);
|
|
37117
|
+
const out = await git.raw([
|
|
37118
|
+
"for-each-ref",
|
|
37119
|
+
"--sort=-committerdate",
|
|
37120
|
+
"--format=%(refname:short)",
|
|
37121
|
+
"refs/heads",
|
|
37122
|
+
"refs/remotes"
|
|
37123
|
+
]);
|
|
37124
|
+
const lines = out.split("\n").map((line) => normalizeBranchRef(line)).filter((line) => Boolean(line));
|
|
37125
|
+
const seen = /* @__PURE__ */ new Set();
|
|
37126
|
+
const ordered = [];
|
|
37127
|
+
for (const name of lines) {
|
|
37128
|
+
if (seen.has(name)) continue;
|
|
37129
|
+
seen.add(name);
|
|
37130
|
+
ordered.push(name);
|
|
37131
|
+
}
|
|
37132
|
+
return ordered;
|
|
37133
|
+
} catch {
|
|
37134
|
+
return [];
|
|
37135
|
+
}
|
|
37136
|
+
}
|
|
37137
|
+
|
|
37138
|
+
// src/git/bridge-git-context.ts
|
|
37139
|
+
function folderNameForRelPath(relPath, bridgeRoot) {
|
|
37140
|
+
if (relPath === "." || relPath === "") {
|
|
37141
|
+
return path40.basename(path40.resolve(bridgeRoot)) || "repo";
|
|
37142
|
+
}
|
|
37143
|
+
return path40.basename(relPath) || relPath;
|
|
37144
|
+
}
|
|
37145
|
+
async function discoverGitReposForBridgeContext(bridgeRoot) {
|
|
37146
|
+
const root = path40.resolve(bridgeRoot);
|
|
37147
|
+
if (await isGitRepoDirectory(root)) {
|
|
37148
|
+
const remoteUrl = await getRemoteOriginUrl(root);
|
|
37149
|
+
return [{ absolutePath: root, remoteUrl }];
|
|
37150
|
+
}
|
|
37151
|
+
const [deep, shallow] = await Promise.all([discoverGitReposUnderRoot(root), discoverGitRepos(root)]);
|
|
37152
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
37153
|
+
for (const repo of [...deep, ...shallow]) {
|
|
37154
|
+
byPath.set(path40.resolve(repo.absolutePath), repo);
|
|
37155
|
+
}
|
|
37156
|
+
return [...byPath.values()];
|
|
37157
|
+
}
|
|
37158
|
+
async function getBridgeGitContext(bridgeRoot = getBridgeRoot()) {
|
|
37159
|
+
const bridgeResolved = path40.resolve(bridgeRoot);
|
|
37160
|
+
const repos = await discoverGitReposForBridgeContext(bridgeResolved);
|
|
37161
|
+
const rows = [];
|
|
37162
|
+
for (const repo of repos) {
|
|
37163
|
+
let rel = path40.relative(bridgeResolved, repo.absolutePath);
|
|
37164
|
+
if (rel.startsWith("..") || path40.isAbsolute(rel)) continue;
|
|
37165
|
+
const relPath = rel === "" ? "." : rel.replace(/\\/g, "/");
|
|
37166
|
+
const currentBranch = await getCurrentBranch(repo.absolutePath);
|
|
37167
|
+
const remoteUrl = repo.remoteUrl.trim() || null;
|
|
37168
|
+
rows.push({
|
|
37169
|
+
relPath,
|
|
37170
|
+
folderName: folderNameForRelPath(relPath, bridgeResolved),
|
|
37171
|
+
currentBranch,
|
|
37172
|
+
remoteUrl
|
|
37173
|
+
});
|
|
37174
|
+
}
|
|
37175
|
+
rows.sort((a, b) => a.relPath.localeCompare(b.relPath));
|
|
37176
|
+
return rows;
|
|
37177
|
+
}
|
|
37178
|
+
async function listRepoBranchesForBridge(repoRelPath, bridgeRoot = getBridgeRoot()) {
|
|
37179
|
+
const bridgeResolved = path40.resolve(bridgeRoot);
|
|
37180
|
+
const rel = repoRelPath.trim() === "." ? "" : repoRelPath.trim().replace(/\\/g, "/");
|
|
37181
|
+
const repoPath = rel === "" ? bridgeResolved : path40.join(bridgeResolved, rel);
|
|
37182
|
+
const resolved = path40.resolve(repoPath);
|
|
37183
|
+
if (!resolved.startsWith(bridgeResolved + path40.sep) && resolved !== bridgeResolved) {
|
|
37184
|
+
return [];
|
|
37185
|
+
}
|
|
37186
|
+
return listRepoBranchRefs(resolved);
|
|
37187
|
+
}
|
|
37188
|
+
|
|
37189
|
+
// src/routing/handlers/bridge-git-context-messages.ts
|
|
37190
|
+
function handleBridgeGitContextRequestMessage(msg, getWs) {
|
|
37191
|
+
void (async () => {
|
|
37192
|
+
const socket = getWs();
|
|
37193
|
+
if (!socket) return;
|
|
37194
|
+
try {
|
|
37195
|
+
const repos = await getBridgeGitContext();
|
|
37196
|
+
sendWsMessage(socket, { type: "bridge_git_context_response", id: msg.id, repos });
|
|
37197
|
+
} catch (e) {
|
|
37198
|
+
sendWsMessage(socket, {
|
|
37199
|
+
type: "bridge_git_context_response",
|
|
37200
|
+
id: msg.id,
|
|
37201
|
+
error: e instanceof Error ? e.message : String(e)
|
|
37202
|
+
});
|
|
37203
|
+
}
|
|
37204
|
+
})();
|
|
37205
|
+
}
|
|
37206
|
+
function handleListRepoBranchesRequestMessage(msg, getWs) {
|
|
37207
|
+
void (async () => {
|
|
37208
|
+
const socket = getWs();
|
|
37209
|
+
if (!socket) return;
|
|
37210
|
+
const repoRelPath = typeof msg.repoRelPath === "string" ? msg.repoRelPath.trim() : "";
|
|
37211
|
+
if (!repoRelPath) {
|
|
37212
|
+
sendWsMessage(socket, {
|
|
37213
|
+
type: "list_repo_branches_response",
|
|
37214
|
+
id: msg.id,
|
|
37215
|
+
error: "repoRelPath required"
|
|
37216
|
+
});
|
|
37217
|
+
return;
|
|
37218
|
+
}
|
|
37219
|
+
try {
|
|
37220
|
+
const branches = await listRepoBranchesForBridge(repoRelPath);
|
|
37221
|
+
sendWsMessage(socket, { type: "list_repo_branches_response", id: msg.id, branches });
|
|
37222
|
+
} catch (e) {
|
|
37223
|
+
sendWsMessage(socket, {
|
|
37224
|
+
type: "list_repo_branches_response",
|
|
37225
|
+
id: msg.id,
|
|
37226
|
+
error: e instanceof Error ? e.message : String(e)
|
|
37227
|
+
});
|
|
37228
|
+
}
|
|
37229
|
+
})();
|
|
37230
|
+
}
|
|
37231
|
+
|
|
36905
37232
|
// src/routing/dispatch-bridge-message.ts
|
|
36906
37233
|
function dispatchBridgeMessage(msg, deps) {
|
|
36907
37234
|
switch (msg.type) {
|
|
@@ -36965,6 +37292,12 @@ function dispatchBridgeMessage(msg, deps) {
|
|
|
36965
37292
|
case "refresh_local_skills":
|
|
36966
37293
|
handleRefreshLocalSkills(msg, deps);
|
|
36967
37294
|
break;
|
|
37295
|
+
case "bridge_git_context_request":
|
|
37296
|
+
handleBridgeGitContextRequestMessage(msg, deps.getWs);
|
|
37297
|
+
break;
|
|
37298
|
+
case "list_repo_branches_request":
|
|
37299
|
+
handleListRepoBranchesRequestMessage(msg, deps.getWs);
|
|
37300
|
+
break;
|
|
36968
37301
|
}
|
|
36969
37302
|
}
|
|
36970
37303
|
|
|
@@ -37415,10 +37748,10 @@ function listCliAgentCapabilityCacheForWorkspace(db, workspaceId) {
|
|
|
37415
37748
|
}
|
|
37416
37749
|
|
|
37417
37750
|
// src/agents/capabilities/warmup-agent-capabilities-on-connect.ts
|
|
37418
|
-
import * as
|
|
37751
|
+
import * as path42 from "node:path";
|
|
37419
37752
|
|
|
37420
37753
|
// src/agents/capabilities/probe-one-agent-type-for-capabilities.ts
|
|
37421
|
-
import * as
|
|
37754
|
+
import * as path41 from "node:path";
|
|
37422
37755
|
async function probeOneAgentTypeForCapabilities(params) {
|
|
37423
37756
|
const { agentType, cwd, workspaceId, log: log2, reportAgentCapabilities, bridgeReport = true } = params;
|
|
37424
37757
|
if (isCliImmediateShutdownRequested()) return false;
|
|
@@ -37458,7 +37791,7 @@ async function probeOneAgentTypeForCapabilities(params) {
|
|
|
37458
37791
|
if (isCliImmediateShutdownRequested()) return false;
|
|
37459
37792
|
handle = await resolved.createClient({
|
|
37460
37793
|
command: resolved.command,
|
|
37461
|
-
cwd:
|
|
37794
|
+
cwd: path41.resolve(cwd),
|
|
37462
37795
|
backendAgentType: agentType,
|
|
37463
37796
|
sessionMode: "agent",
|
|
37464
37797
|
persistedAcpSessionId: null,
|
|
@@ -37532,7 +37865,7 @@ async function probeAgentCapabilitiesForDetectedTypes(params) {
|
|
|
37532
37865
|
async function warmupAgentCapabilitiesOnConnect(params) {
|
|
37533
37866
|
const { workspaceId, log: log2, getWs } = params;
|
|
37534
37867
|
if (isCliImmediateShutdownRequested()) return;
|
|
37535
|
-
const cwd =
|
|
37868
|
+
const cwd = path42.resolve(getBridgeRoot());
|
|
37536
37869
|
async function sendBatchFromCache() {
|
|
37537
37870
|
const socket = getWs();
|
|
37538
37871
|
if (!socket || socket.readyState !== wrapper_default.OPEN) return;
|
|
@@ -37699,8 +38032,8 @@ async function createBridgeConnection(options) {
|
|
|
37699
38032
|
getCloudAccessToken: () => tokens.accessToken
|
|
37700
38033
|
};
|
|
37701
38034
|
const identifyReportedPaths = {
|
|
37702
|
-
bridgeRootPath:
|
|
37703
|
-
worktreesRootPath:
|
|
38035
|
+
bridgeRootPath: path43.resolve(getBridgeRoot()),
|
|
38036
|
+
worktreesRootPath: path43.resolve(worktreesRootPath)
|
|
37704
38037
|
};
|
|
37705
38038
|
const { connect } = createMainBridgeWebSocketLifecycle({
|
|
37706
38039
|
state,
|