@adhdev/daemon-standalone 0.9.82-rc.259 → 0.9.82-rc.260
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/index.js
CHANGED
|
@@ -32396,6 +32396,7 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
|
|
|
32396
32396
|
nodeSatisfiesRequiredTags: () => nodeSatisfiesRequiredTags,
|
|
32397
32397
|
normalizeMeshCapabilityTags: () => normalizeMeshCapabilityTags,
|
|
32398
32398
|
normalizeMeshTaskMode: () => normalizeMeshTaskMode,
|
|
32399
|
+
recordDirectDispatchTask: () => recordDirectDispatchTask,
|
|
32399
32400
|
recordMeshToolCall: () => recordMeshToolCall,
|
|
32400
32401
|
recordTaskAutoLaunch: () => recordTaskAutoLaunch,
|
|
32401
32402
|
requeueTask: () => requeueTask,
|
|
@@ -32560,6 +32561,37 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
|
|
|
32560
32561
|
return entry;
|
|
32561
32562
|
});
|
|
32562
32563
|
}
|
|
32564
|
+
function recordDirectDispatchTask(meshId, message, opts) {
|
|
32565
|
+
const missionId = typeof opts.missionId === "string" ? opts.missionId.trim() : "";
|
|
32566
|
+
if (!missionId) return null;
|
|
32567
|
+
const taskId = typeof opts.id === "string" ? opts.id.trim() : "";
|
|
32568
|
+
if (!taskId) return null;
|
|
32569
|
+
const modeValidation = validateMeshTaskModeRequest(opts.taskMode, message);
|
|
32570
|
+
if (!modeValidation.valid) {
|
|
32571
|
+
throw new Error(`live_debug_readonly_guardrail_violation: forbidden operations (${modeValidation.violations.join(", ")})`);
|
|
32572
|
+
}
|
|
32573
|
+
const now = opts.dispatchedAt && opts.dispatchedAt.trim() ? opts.dispatchedAt : (/* @__PURE__ */ new Date()).toISOString();
|
|
32574
|
+
return withQueueLock(meshId, () => {
|
|
32575
|
+
if (MeshRuntimeStore.getInstance().findQueueEntryById(meshId, taskId)) {
|
|
32576
|
+
return null;
|
|
32577
|
+
}
|
|
32578
|
+
const entry = {
|
|
32579
|
+
id: taskId,
|
|
32580
|
+
meshId,
|
|
32581
|
+
message,
|
|
32582
|
+
status: "assigned",
|
|
32583
|
+
...modeValidation.taskMode ? { taskMode: modeValidation.taskMode } : {},
|
|
32584
|
+
missionId,
|
|
32585
|
+
...opts.assignedNodeId ? { targetNodeId: opts.assignedNodeId, assignedNodeId: opts.assignedNodeId } : {},
|
|
32586
|
+
...opts.assignedSessionId ? { targetSessionId: opts.assignedSessionId, assignedSessionId: opts.assignedSessionId } : {},
|
|
32587
|
+
dispatchTimestamp: now,
|
|
32588
|
+
createdAt: now,
|
|
32589
|
+
updatedAt: now
|
|
32590
|
+
};
|
|
32591
|
+
MeshRuntimeStore.getInstance().insertQueueEntry(entry);
|
|
32592
|
+
return entry;
|
|
32593
|
+
});
|
|
32594
|
+
}
|
|
32563
32595
|
function getQueue(meshId, opts) {
|
|
32564
32596
|
return MeshRuntimeStore.getInstance().getQueueEntries(meshId, opts?.status?.length ? opts.status : void 0);
|
|
32565
32597
|
}
|
|
@@ -34148,9 +34180,50 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
|
|
|
34148
34180
|
return (Number.isFinite(bTime) ? bTime : 0) - (Number.isFinite(aTime) ? aTime : 0);
|
|
34149
34181
|
});
|
|
34150
34182
|
}
|
|
34183
|
+
function jobActivityTime(job) {
|
|
34184
|
+
const raw = job.lastUpdatedAt || job.completedAt || job.startedAt || "";
|
|
34185
|
+
const t = new Date(raw).getTime();
|
|
34186
|
+
return Number.isFinite(t) ? t : 0;
|
|
34187
|
+
}
|
|
34188
|
+
function summarizeMeshAsyncRefineJobs(jobs) {
|
|
34189
|
+
const activeJobs = [];
|
|
34190
|
+
const terminalJobs = [];
|
|
34191
|
+
for (const job of jobs) {
|
|
34192
|
+
if (TERMINAL_REFINE_STATUSES.has(job.status)) terminalJobs.push(job);
|
|
34193
|
+
else activeJobs.push(job);
|
|
34194
|
+
}
|
|
34195
|
+
let newest = 0;
|
|
34196
|
+
for (const job of jobs) newest = Math.max(newest, jobActivityTime(job));
|
|
34197
|
+
const cutoff = newest - STALE_TERMINAL_REFINE_WINDOW_MS;
|
|
34198
|
+
const terminalByRecency = [...terminalJobs].sort(
|
|
34199
|
+
(a, b) => jobActivityTime(b) - jobActivityTime(a)
|
|
34200
|
+
);
|
|
34201
|
+
const freshTerminal = terminalByRecency.filter((job) => jobActivityTime(job) >= cutoff).slice(0, RECENT_TERMINAL_REFINE_CAP);
|
|
34202
|
+
const freshTerminalIds = new Set(freshTerminal.map((job) => job.jobId));
|
|
34203
|
+
const byStatus = {};
|
|
34204
|
+
for (const job of activeJobs) {
|
|
34205
|
+
byStatus[job.status] = (byStatus[job.status] ?? 0) + 1;
|
|
34206
|
+
}
|
|
34207
|
+
for (const job of freshTerminal) {
|
|
34208
|
+
byStatus[job.status] = (byStatus[job.status] ?? 0) + 1;
|
|
34209
|
+
}
|
|
34210
|
+
const staleTerminal = terminalJobs.length - freshTerminalIds.size;
|
|
34211
|
+
return {
|
|
34212
|
+
total: activeJobs.length + freshTerminal.length,
|
|
34213
|
+
byStatus,
|
|
34214
|
+
staleTerminal,
|
|
34215
|
+
activeJobs
|
|
34216
|
+
};
|
|
34217
|
+
}
|
|
34218
|
+
var TERMINAL_REFINE_STATUSES;
|
|
34219
|
+
var STALE_TERMINAL_REFINE_WINDOW_MS;
|
|
34220
|
+
var RECENT_TERMINAL_REFINE_CAP;
|
|
34151
34221
|
var init_mesh_refine_status = __esm2({
|
|
34152
34222
|
"src/mesh/mesh-refine-status.ts"() {
|
|
34153
34223
|
"use strict";
|
|
34224
|
+
TERMINAL_REFINE_STATUSES = /* @__PURE__ */ new Set(["completed", "failed"]);
|
|
34225
|
+
STALE_TERMINAL_REFINE_WINDOW_MS = 6 * 60 * 60 * 1e3;
|
|
34226
|
+
RECENT_TERMINAL_REFINE_CAP = 8;
|
|
34154
34227
|
}
|
|
34155
34228
|
});
|
|
34156
34229
|
var mesh_review_inbox_exports = {};
|
|
@@ -40176,6 +40249,26 @@ ${cont}` : cont;
|
|
|
40176
40249
|
// ── Approval ─────────────────────────────────────
|
|
40177
40250
|
lastApprovalResolvedAt = 0;
|
|
40178
40251
|
lastResolvedModalMessage = "";
|
|
40252
|
+
/**
|
|
40253
|
+
* Monotonic counter bumped every time the FSM *enters* waiting_approval
|
|
40254
|
+
* with a freshly captured modal (see `applyWaitingApproval`). It is the
|
|
40255
|
+
* single discriminator between "the same approval re-observed across TUI
|
|
40256
|
+
* paint flaps" and "a genuinely new, distinct approval".
|
|
40257
|
+
*
|
|
40258
|
+
* The message-equality cooldown below (`lastResolvedModalMessage`) cannot
|
|
40259
|
+
* tell these apart on its own: claude-cli routinely presents consecutive
|
|
40260
|
+
* approvals whose modal message text is identical (e.g. two back-to-back
|
|
40261
|
+
* Bash-command prompts). When that second approval arrived inside
|
|
40262
|
+
* `approvalCooldown`, the message-equality guard silently swallowed the
|
|
40263
|
+
* key write and the approval stuck forever — fatal under auto-approval.
|
|
40264
|
+
*
|
|
40265
|
+
* `approvalEntrySeq` increments on every fresh entry; `lastResolvedEntrySeq`
|
|
40266
|
+
* records which entry the cooldown belongs to. We only short-circuit the
|
|
40267
|
+
* write when we are still resolving *that same* entry — a new entry (new
|
|
40268
|
+
* seq) is always a real, distinct approval and must be written.
|
|
40269
|
+
*/
|
|
40270
|
+
approvalEntrySeq = 0;
|
|
40271
|
+
lastResolvedEntrySeq = -1;
|
|
40179
40272
|
/**
|
|
40180
40273
|
* When the engine previously held a modal but the latest parse failed
|
|
40181
40274
|
* to extract one, we record the timestamp here and only drop the modal
|
|
@@ -40281,6 +40374,7 @@ ${cont}` : cont;
|
|
|
40281
40374
|
if (parsed?.status === "waiting_approval" && parsedModal) {
|
|
40282
40375
|
modal = parsedModal;
|
|
40283
40376
|
this.activeModal = parsedModal;
|
|
40377
|
+
this.approvalEntrySeq++;
|
|
40284
40378
|
if (this.currentStatus !== "waiting_approval") {
|
|
40285
40379
|
this.setStatus("waiting_approval", "resolve_modal_parse");
|
|
40286
40380
|
this.callbacks.onStatusChange();
|
|
@@ -40294,12 +40388,14 @@ ${cont}` : cont;
|
|
|
40294
40388
|
if (!modal || !buttonsValid) return;
|
|
40295
40389
|
const currentModalMessage = typeof modal?.message === "string" ? modal.message.trim() : "";
|
|
40296
40390
|
const inCooldown = !!this.lastApprovalResolvedAt && Date.now() - this.lastApprovalResolvedAt < this.timeouts.approvalCooldown;
|
|
40297
|
-
|
|
40391
|
+
const sameEntryReResolve = this.approvalEntrySeq === this.lastResolvedEntrySeq;
|
|
40392
|
+
if (inCooldown && sameEntryReResolve && currentModalMessage === this.lastResolvedModalMessage) return;
|
|
40298
40393
|
this.clearIdleFinishCandidate("resolve_modal");
|
|
40299
|
-
this.recordTrace("resolve_modal", { buttonIndex, activeModal: modal });
|
|
40394
|
+
this.recordTrace("resolve_modal", { buttonIndex, activeModal: modal, approvalEntrySeq: this.approvalEntrySeq });
|
|
40300
40395
|
this.activeModal = null;
|
|
40301
40396
|
this.lastApprovalResolvedAt = Date.now();
|
|
40302
40397
|
this.lastResolvedModalMessage = currentModalMessage;
|
|
40398
|
+
this.lastResolvedEntrySeq = this.approvalEntrySeq;
|
|
40303
40399
|
this.responseSettleIgnoreUntil = Date.now() + this.timeouts.outputSettle + 400;
|
|
40304
40400
|
if (this.approvalExitTimeout) {
|
|
40305
40401
|
clearTimeout(this.approvalExitTimeout);
|
|
@@ -40656,7 +40752,7 @@ ${cont}` : cont;
|
|
|
40656
40752
|
this.callbacks.onStatusChange();
|
|
40657
40753
|
return;
|
|
40658
40754
|
}
|
|
40659
|
-
if (!inCooldown) {
|
|
40755
|
+
if (!inCooldown || modal) {
|
|
40660
40756
|
if (!modal) {
|
|
40661
40757
|
LOG2.warn("CLI", `[${this.provider.type}] detectStatus=waiting_approval but parseApproval returned null; ignoring`);
|
|
40662
40758
|
if (this.currentStatus === "waiting_approval" && this.activeModal) {
|
|
@@ -40679,6 +40775,7 @@ ${cont}` : cont;
|
|
|
40679
40775
|
const nextBtnCount = Array.isArray(modal.buttons) ? modal.buttons.length : 0;
|
|
40680
40776
|
if (!prev || prevBtnCount !== nextBtnCount) {
|
|
40681
40777
|
this.activeModal = modal;
|
|
40778
|
+
this.approvalEntrySeq++;
|
|
40682
40779
|
this.callbacks.onStatusChange();
|
|
40683
40780
|
}
|
|
40684
40781
|
if (this.idleTimeout) clearTimeout(this.idleTimeout);
|
|
@@ -41872,6 +41969,7 @@ ${lastSnapshot}`;
|
|
|
41872
41969
|
messages: [],
|
|
41873
41970
|
workingDir: this.workingDir,
|
|
41874
41971
|
activeModal: effectiveModal,
|
|
41972
|
+
approvalEntrySeq: this.engine.approvalEntrySeq,
|
|
41875
41973
|
pendingOutboundCount: this.pendingOutboundQueue.length,
|
|
41876
41974
|
pendingOutboundMessages: this.pendingOutboundQueue.map((message) => ({
|
|
41877
41975
|
id: message.id,
|
|
@@ -43770,7 +43868,9 @@ ${lastSnapshot}`;
|
|
|
43770
43868
|
ProviderCliAdapter: () => ProviderCliAdapter,
|
|
43771
43869
|
ProviderInstanceManager: () => ProviderInstanceManager,
|
|
43772
43870
|
ProviderLoader: () => ProviderLoader,
|
|
43871
|
+
RECENT_TERMINAL_REFINE_CAP: () => RECENT_TERMINAL_REFINE_CAP,
|
|
43773
43872
|
RawTerminalAttachment: () => RawTerminalAttachment,
|
|
43873
|
+
STALE_TERMINAL_REFINE_WINDOW_MS: () => STALE_TERMINAL_REFINE_WINDOW_MS,
|
|
43774
43874
|
STANDALONE_CDP_SCAN_INTERVAL_MS: () => STANDALONE_CDP_SCAN_INTERVAL_MS2,
|
|
43775
43875
|
SessionHostPtyTransportFactory: () => SessionHostPtyTransportFactory2,
|
|
43776
43876
|
TerminalAdapter: () => TerminalAdapter,
|
|
@@ -43978,6 +44078,7 @@ ${lastSnapshot}`;
|
|
|
43978
44078
|
reconcileDirectDispatchCompletionFromTranscript: () => reconcileDirectDispatchCompletionFromTranscript,
|
|
43979
44079
|
recordCompletionConflict: () => recordCompletionConflict,
|
|
43980
44080
|
recordDebugTrace: () => recordDebugTrace,
|
|
44081
|
+
recordDirectDispatchTask: () => recordDirectDispatchTask,
|
|
43981
44082
|
recordMeshToolCall: () => recordMeshToolCall,
|
|
43982
44083
|
registerExtensionProviders: () => registerExtensionProviders,
|
|
43983
44084
|
registerMeshCoordinator: () => registerMeshCoordinator,
|
|
@@ -44014,6 +44115,7 @@ ${lastSnapshot}`;
|
|
|
44014
44115
|
startLocalIpcServer: () => startLocalIpcServer2,
|
|
44015
44116
|
suggestMeshRefineConfig: () => suggestMeshRefineConfig,
|
|
44016
44117
|
summarizeGitStatus: () => summarizeGitStatus,
|
|
44118
|
+
summarizeMeshAsyncRefineJobs: () => summarizeMeshAsyncRefineJobs,
|
|
44017
44119
|
summarizeMeshMission: () => summarizeMeshMission,
|
|
44018
44120
|
summarizeMissionTasks: () => summarizeMissionTasks,
|
|
44019
44121
|
triggerMeshQueue: () => triggerMeshQueue,
|
|
@@ -46236,7 +46338,7 @@ ${lastSnapshot}`;
|
|
|
46236
46338
|
if (!validation.valid) {
|
|
46237
46339
|
return { status: "failed", required: required2, configSource: loaded.path || loaded.source, configSourceType: "invalid", error: String(validation.rejectedCommands[0]?.reason || validation.errors.join("; ")), commandsRun: [] };
|
|
46238
46340
|
}
|
|
46239
|
-
const
|
|
46341
|
+
const execFileAsync4 = (0, import_node_util3.promisify)(import_node_child_process3.execFile);
|
|
46240
46342
|
const state = {
|
|
46241
46343
|
status: "running",
|
|
46242
46344
|
required: required2,
|
|
@@ -46262,7 +46364,7 @@ ${lastSnapshot}`;
|
|
|
46262
46364
|
const startedAt = Date.now();
|
|
46263
46365
|
state.lastCommand = command.displayCommand;
|
|
46264
46366
|
try {
|
|
46265
|
-
const result = await
|
|
46367
|
+
const result = await execFileAsync4(command.command, command.args, {
|
|
46266
46368
|
cwd,
|
|
46267
46369
|
encoding: "utf8",
|
|
46268
46370
|
timeout: command.timeoutMs || DEFAULT_TIMEOUT_MS2,
|
|
@@ -61244,7 +61346,9 @@ ${formatManifestValidationIssues2(validation.issues)}`,
|
|
|
61244
61346
|
if (buttonIndex < 0) {
|
|
61245
61347
|
return autoApproveActive;
|
|
61246
61348
|
}
|
|
61349
|
+
const approvalEntrySeq = typeof adapterStatus?.approvalEntrySeq === "number" ? adapterStatus.approvalEntrySeq : 0;
|
|
61247
61350
|
const signature = [
|
|
61351
|
+
approvalEntrySeq,
|
|
61248
61352
|
typeof modal?.message === "string" ? modal.message.trim() : "",
|
|
61249
61353
|
buttons.join("|"),
|
|
61250
61354
|
buttonIndex
|
|
@@ -66886,8 +66990,8 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
66886
66990
|
}
|
|
66887
66991
|
const https = require("https");
|
|
66888
66992
|
const { exec: exec7 } = require("child_process");
|
|
66889
|
-
const { promisify:
|
|
66890
|
-
const execAsync5 =
|
|
66993
|
+
const { promisify: promisify8 } = require("util");
|
|
66994
|
+
const execAsync5 = promisify8(exec7);
|
|
66891
66995
|
const metaPath = path30.join(this.upstreamDir, _ProviderLoader.META_FILE);
|
|
66892
66996
|
let prevEtag = "";
|
|
66893
66997
|
let prevTimestamp = 0;
|
|
@@ -68157,12 +68261,97 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
|
|
|
68157
68261
|
init_mesh_host_ownership();
|
|
68158
68262
|
init_mesh_fast_forward();
|
|
68159
68263
|
var import_node_child_process4 = require("child_process");
|
|
68264
|
+
var import_node_util4 = require("util");
|
|
68265
|
+
var execFileAsync3 = (0, import_node_util4.promisify)(import_node_child_process4.execFile);
|
|
68266
|
+
var MAX_CHANGED_FILES2 = 500;
|
|
68267
|
+
function topLevel(path39) {
|
|
68268
|
+
const slash = path39.indexOf("/");
|
|
68269
|
+
return slash === -1 ? path39 : path39.slice(0, slash);
|
|
68270
|
+
}
|
|
68271
|
+
async function analyzeMeshRefineNodeChangeArea(args) {
|
|
68272
|
+
const { nodeId, workspace, branch, baseRef, branchRef, diffCwd, submodulePaths } = args;
|
|
68273
|
+
const base = {
|
|
68274
|
+
nodeId,
|
|
68275
|
+
workspace,
|
|
68276
|
+
branch,
|
|
68277
|
+
changedTopLevelPaths: [],
|
|
68278
|
+
changedFiles: [],
|
|
68279
|
+
touchedSubmodulePaths: [],
|
|
68280
|
+
touchesSubmodule: false,
|
|
68281
|
+
aheadCount: 0
|
|
68282
|
+
};
|
|
68283
|
+
try {
|
|
68284
|
+
let mergeBase = baseRef;
|
|
68285
|
+
try {
|
|
68286
|
+
const { stdout } = await execFileAsync3("git", ["merge-base", baseRef, branchRef], { cwd: diffCwd, encoding: "utf8" });
|
|
68287
|
+
const resolved = stdout.trim();
|
|
68288
|
+
if (resolved) mergeBase = resolved;
|
|
68289
|
+
} catch {
|
|
68290
|
+
}
|
|
68291
|
+
const { stdout: countStdout } = await execFileAsync3(
|
|
68292
|
+
"git",
|
|
68293
|
+
["rev-list", "--count", `${mergeBase}..${branchRef}`],
|
|
68294
|
+
{ cwd: diffCwd, encoding: "utf8" }
|
|
68295
|
+
);
|
|
68296
|
+
base.aheadCount = Number.parseInt(countStdout.trim(), 10) || 0;
|
|
68297
|
+
const { stdout: nameStdout } = await execFileAsync3(
|
|
68298
|
+
"git",
|
|
68299
|
+
["diff", "--name-only", `${mergeBase}..${branchRef}`],
|
|
68300
|
+
{ cwd: diffCwd, encoding: "utf8" }
|
|
68301
|
+
);
|
|
68302
|
+
const files = nameStdout.split("\n").map((line) => line.trim()).filter(Boolean).slice(0, MAX_CHANGED_FILES2);
|
|
68303
|
+
base.changedFiles = files;
|
|
68304
|
+
const topSet = /* @__PURE__ */ new Set();
|
|
68305
|
+
const submoduleSet = /* @__PURE__ */ new Set();
|
|
68306
|
+
for (const file2 of files) {
|
|
68307
|
+
const top = topLevel(file2);
|
|
68308
|
+
topSet.add(top);
|
|
68309
|
+
if (submodulePaths.has(file2) || submodulePaths.has(top)) {
|
|
68310
|
+
submoduleSet.add(submodulePaths.has(file2) ? file2 : top);
|
|
68311
|
+
}
|
|
68312
|
+
}
|
|
68313
|
+
base.changedTopLevelPaths = [...topSet].sort();
|
|
68314
|
+
base.touchedSubmodulePaths = [...submoduleSet].sort();
|
|
68315
|
+
base.touchesSubmodule = submoduleSet.size > 0;
|
|
68316
|
+
return base;
|
|
68317
|
+
} catch (e) {
|
|
68318
|
+
base.error = e?.message || String(e);
|
|
68319
|
+
return base;
|
|
68320
|
+
}
|
|
68321
|
+
}
|
|
68322
|
+
function orderMeshRefineBatchNodes(changeAreas) {
|
|
68323
|
+
const areaById = {};
|
|
68324
|
+
for (const area of changeAreas) areaById[area.nodeId] = area;
|
|
68325
|
+
const ranked = [...changeAreas].sort((a, b) => {
|
|
68326
|
+
const aSub = a.touchesSubmodule ? 1 : 0;
|
|
68327
|
+
const bSub = b.touchesSubmodule ? 1 : 0;
|
|
68328
|
+
if (aSub !== bSub) return aSub - bSub;
|
|
68329
|
+
const aBreadth = a.changedTopLevelPaths.length;
|
|
68330
|
+
const bBreadth = b.changedTopLevelPaths.length;
|
|
68331
|
+
if (aBreadth !== bBreadth) return aBreadth - bBreadth;
|
|
68332
|
+
return a.nodeId.localeCompare(b.nodeId);
|
|
68333
|
+
});
|
|
68334
|
+
const rationale = [];
|
|
68335
|
+
const nonSub = ranked.filter((a) => !a.touchesSubmodule).map((a) => a.nodeId);
|
|
68336
|
+
const sub = ranked.filter((a) => a.touchesSubmodule).map((a) => a.nodeId);
|
|
68337
|
+
if (nonSub.length) {
|
|
68338
|
+
rationale.push(`Non-submodule nodes first (no submodule-main advance, conflict-free ordering): ${nonSub.join(", ")}`);
|
|
68339
|
+
}
|
|
68340
|
+
if (sub.length) {
|
|
68341
|
+
rationale.push(`Submodule-touching nodes last, serialized (each merge advances submodule main, forcing rebase of the next): ${sub.join(", ")}`);
|
|
68342
|
+
}
|
|
68343
|
+
for (const area of ranked) {
|
|
68344
|
+
if (area.error) rationale.push(`Node ${area.nodeId}: change-area analysis degraded (${area.error}); placed with neutral priority.`);
|
|
68345
|
+
}
|
|
68346
|
+
return { order: ranked.map((a) => a.nodeId), changeAreas: areaById, rationale };
|
|
68347
|
+
}
|
|
68348
|
+
var import_node_child_process5 = require("child_process");
|
|
68160
68349
|
var import_node_fs4 = require("fs");
|
|
68161
68350
|
var import_node_path2 = require("path");
|
|
68162
68351
|
var PREVIEW_DEPLOY_RECORD = ".adhdev/preview-deploy.json";
|
|
68163
68352
|
function runGit2(repoRoot, args) {
|
|
68164
68353
|
try {
|
|
68165
|
-
return (0,
|
|
68354
|
+
return (0, import_node_child_process5.execFileSync)("git", args, {
|
|
68166
68355
|
cwd: repoRoot,
|
|
68167
68356
|
encoding: "utf8",
|
|
68168
68357
|
stdio: ["ignore", "pipe", "ignore"],
|
|
@@ -68922,7 +69111,7 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
|
|
|
68922
69111
|
var import_os3 = require("os");
|
|
68923
69112
|
var import_path10 = require("path");
|
|
68924
69113
|
var fs23 = __toESM2(require("fs"));
|
|
68925
|
-
var
|
|
69114
|
+
var import_node_child_process6 = require("child_process");
|
|
68926
69115
|
var CHANNEL_NPM_TAG = { stable: "latest", preview: "next" };
|
|
68927
69116
|
var CHANNEL_SERVER_URL = {
|
|
68928
69117
|
stable: "https://api.adhf.dev",
|
|
@@ -70080,7 +70269,7 @@ ${e?.stderr || ""}`
|
|
|
70080
70269
|
}
|
|
70081
70270
|
function readChangedGitlinkPaths(repoRoot, fromRef, toRef) {
|
|
70082
70271
|
try {
|
|
70083
|
-
const output = (0,
|
|
70272
|
+
const output = (0, import_node_child_process6.execFileSync)("git", ["diff", "--raw", "--no-abbrev", fromRef, toRef], {
|
|
70084
70273
|
cwd: repoRoot,
|
|
70085
70274
|
encoding: "utf8",
|
|
70086
70275
|
maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES
|
|
@@ -70104,7 +70293,7 @@ ${e?.stderr || ""}`
|
|
|
70104
70293
|
}
|
|
70105
70294
|
function readTreeObject(repoRoot, ref, path39) {
|
|
70106
70295
|
try {
|
|
70107
|
-
const output = (0,
|
|
70296
|
+
const output = (0, import_node_child_process6.execFileSync)("git", ["ls-tree", ref, "--", path39], {
|
|
70108
70297
|
cwd: repoRoot,
|
|
70109
70298
|
encoding: "utf8",
|
|
70110
70299
|
maxBuffer: 1024 * 1024
|
|
@@ -70138,10 +70327,10 @@ ${e?.stderr || ""}`
|
|
|
70138
70327
|
}
|
|
70139
70328
|
const commandArgs = ["submodule", "update", "--init", "--recursive", "--", ...updatePaths];
|
|
70140
70329
|
try {
|
|
70141
|
-
const { execFile:
|
|
70142
|
-
const { promisify:
|
|
70143
|
-
const
|
|
70144
|
-
const result = await
|
|
70330
|
+
const { execFile: execFile5 } = await import("child_process");
|
|
70331
|
+
const { promisify: promisify8 } = await import("util");
|
|
70332
|
+
const execFileAsync4 = promisify8(execFile5);
|
|
70333
|
+
const result = await execFileAsync4("git", commandArgs, {
|
|
70145
70334
|
cwd: repoRoot,
|
|
70146
70335
|
encoding: "utf8",
|
|
70147
70336
|
maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES,
|
|
@@ -70184,11 +70373,11 @@ ${e?.stderr || ""}`
|
|
|
70184
70373
|
const startedAt = Date.now();
|
|
70185
70374
|
const entries = [];
|
|
70186
70375
|
try {
|
|
70187
|
-
const { execFile:
|
|
70188
|
-
const { promisify:
|
|
70189
|
-
const
|
|
70376
|
+
const { execFile: execFile5 } = await import("child_process");
|
|
70377
|
+
const { promisify: promisify8 } = await import("util");
|
|
70378
|
+
const execFileAsync4 = promisify8(execFile5);
|
|
70190
70379
|
const runGit3 = async (cwd, args) => {
|
|
70191
|
-
const { stdout } = await
|
|
70380
|
+
const { stdout } = await execFileAsync4("git", args, {
|
|
70192
70381
|
cwd,
|
|
70193
70382
|
encoding: "utf8",
|
|
70194
70383
|
timeout: 3e4,
|
|
@@ -70203,7 +70392,7 @@ ${e?.stderr || ""}`
|
|
|
70203
70392
|
};
|
|
70204
70393
|
const publishCommitToRemoteMain = async (submodulePath, commit, branch = "main") => {
|
|
70205
70394
|
const refspec = `${commit}:refs/heads/${branch}`;
|
|
70206
|
-
const { stdout, stderr } = await
|
|
70395
|
+
const { stdout, stderr } = await execFileAsync4("git", ["push", "origin", refspec], {
|
|
70207
70396
|
cwd: submodulePath,
|
|
70208
70397
|
encoding: "utf8",
|
|
70209
70398
|
timeout: 3e4,
|
|
@@ -70388,9 +70577,9 @@ ${e?.stderr || ""}`
|
|
|
70388
70577
|
};
|
|
70389
70578
|
}
|
|
70390
70579
|
async function runMeshRefineValidationGate(mesh, workspace, opts) {
|
|
70391
|
-
const { execFile:
|
|
70392
|
-
const { promisify:
|
|
70393
|
-
const
|
|
70580
|
+
const { execFile: execFile5 } = await import("child_process");
|
|
70581
|
+
const { promisify: promisify8 } = await import("util");
|
|
70582
|
+
const execFileAsync4 = promisify8(execFile5);
|
|
70394
70583
|
const selection = resolveMeshRefineValidationPlan(mesh, workspace);
|
|
70395
70584
|
const summary = {
|
|
70396
70585
|
status: "skipped",
|
|
@@ -70484,7 +70673,7 @@ ${e?.stderr || ""}`
|
|
|
70484
70673
|
const cwd = candidate.cwd ? (0, import_path10.resolve)(workspace, candidate.cwd) : workspace;
|
|
70485
70674
|
const timeout = candidate.timeoutMs || REFINE_VALIDATION_TIMEOUT_MS;
|
|
70486
70675
|
try {
|
|
70487
|
-
const result = await
|
|
70676
|
+
const result = await execFileAsync4(candidate.command, candidate.args, {
|
|
70488
70677
|
cwd,
|
|
70489
70678
|
encoding: "utf8",
|
|
70490
70679
|
timeout,
|
|
@@ -70526,7 +70715,7 @@ ${e?.stderr || ""}`
|
|
|
70526
70715
|
return summary;
|
|
70527
70716
|
}
|
|
70528
70717
|
try {
|
|
70529
|
-
const result = await
|
|
70718
|
+
const result = await execFileAsync4(candidate.command, candidate.args, {
|
|
70530
70719
|
cwd,
|
|
70531
70720
|
encoding: "utf8",
|
|
70532
70721
|
timeout,
|
|
@@ -71094,19 +71283,19 @@ ${e?.stderr || ""}`
|
|
|
71094
71283
|
const isSubmoduleGuard = /working trees containing submodules cannot be moved or removed/i.test(message);
|
|
71095
71284
|
const submoduleForceBlocked = isSubmoduleGuard && !forceFallbackConvergence.allow;
|
|
71096
71285
|
if (isSubmoduleGuard && forceFallbackConvergence.allow) {
|
|
71097
|
-
const { execFile:
|
|
71098
|
-
const { promisify:
|
|
71099
|
-
const
|
|
71286
|
+
const { execFile: execFile5 } = await import("child_process");
|
|
71287
|
+
const { promisify: promisify8 } = await import("util");
|
|
71288
|
+
const execFileAsync4 = promisify8(execFile5);
|
|
71100
71289
|
const GIT_TIMEOUT_CLEANUP = 3e4;
|
|
71101
71290
|
const GIT_MAX_BUFFER_CLEANUP = 4 * 1024 * 1024;
|
|
71102
71291
|
try {
|
|
71103
|
-
await
|
|
71292
|
+
await execFileAsync4("git", ["-C", workspace, "submodule", "deinit", "--all", "-f"], {
|
|
71104
71293
|
encoding: "utf8",
|
|
71105
71294
|
timeout: GIT_TIMEOUT_CLEANUP,
|
|
71106
71295
|
maxBuffer: GIT_MAX_BUFFER_CLEANUP,
|
|
71107
71296
|
windowsHide: true
|
|
71108
71297
|
});
|
|
71109
|
-
await
|
|
71298
|
+
await execFileAsync4("git", ["worktree", "remove", "--force", workspace], {
|
|
71110
71299
|
cwd: repoRoot,
|
|
71111
71300
|
encoding: "utf8",
|
|
71112
71301
|
timeout: GIT_TIMEOUT_CLEANUP,
|
|
@@ -71125,7 +71314,7 @@ ${e?.stderr || ""}`
|
|
|
71125
71314
|
} catch (deinitError) {
|
|
71126
71315
|
try {
|
|
71127
71316
|
fs23.rmSync(workspace, { recursive: true, force: true });
|
|
71128
|
-
await
|
|
71317
|
+
await execFileAsync4("git", ["worktree", "prune"], {
|
|
71129
71318
|
cwd: repoRoot,
|
|
71130
71319
|
encoding: "utf8",
|
|
71131
71320
|
timeout: GIT_TIMEOUT_CLEANUP,
|
|
@@ -71169,11 +71358,11 @@ ${e?.stderr || ""}`
|
|
|
71169
71358
|
if (refinedConvergence === "merged_pushed" || refinedConvergence === "merged_to_main") {
|
|
71170
71359
|
return { allow: true, status: refinedConvergence, source: "node_refine_state" };
|
|
71171
71360
|
}
|
|
71172
|
-
const { execFile:
|
|
71173
|
-
const { promisify:
|
|
71174
|
-
const
|
|
71361
|
+
const { execFile: execFile5 } = await import("child_process");
|
|
71362
|
+
const { promisify: promisify8 } = await import("util");
|
|
71363
|
+
const execFileAsync4 = promisify8(execFile5);
|
|
71175
71364
|
const runGit3 = async (gitArgs, cwd) => {
|
|
71176
|
-
const { stdout } = await
|
|
71365
|
+
const { stdout } = await execFileAsync4("git", gitArgs, {
|
|
71177
71366
|
cwd,
|
|
71178
71367
|
encoding: "utf8",
|
|
71179
71368
|
timeout: 3e4,
|
|
@@ -71645,30 +71834,30 @@ ${e?.stderr || ""}`
|
|
|
71645
71834
|
const sourceNode = node.clonedFromNodeId ? mesh?.nodes.find((n) => n.id === node.clonedFromNodeId || n.nodeId === node.clonedFromNodeId) : mesh?.nodes.find((n) => !n.isLocalWorktree);
|
|
71646
71835
|
const repoRoot = sourceNode?.repoRoot || sourceNode?.workspace;
|
|
71647
71836
|
if (!repoRoot) return { success: false, error: "Source node repoRoot not found", refineStages };
|
|
71648
|
-
const { execFile:
|
|
71649
|
-
const { promisify:
|
|
71650
|
-
const
|
|
71837
|
+
const { execFile: execFile5 } = await import("child_process");
|
|
71838
|
+
const { promisify: promisify8 } = await import("util");
|
|
71839
|
+
const execFileAsync4 = promisify8(execFile5);
|
|
71651
71840
|
const resolveStarted = Date.now();
|
|
71652
|
-
const { stdout: branchStdout } = await
|
|
71841
|
+
const { stdout: branchStdout } = await execFileAsync4("git", ["branch", "--show-current"], { cwd: node.workspace, encoding: "utf8" });
|
|
71653
71842
|
const branch = branchStdout.trim();
|
|
71654
71843
|
if (!branch) return { success: false, error: "Could not determine branch of the worktree node", refineStages };
|
|
71655
|
-
const { stdout: baseBranchStdout } = await
|
|
71844
|
+
const { stdout: baseBranchStdout } = await execFileAsync4("git", ["branch", "--show-current"], { cwd: repoRoot, encoding: "utf8" });
|
|
71656
71845
|
const baseBranch = baseBranchStdout.trim();
|
|
71657
71846
|
let fetchWarning;
|
|
71658
71847
|
try {
|
|
71659
|
-
await
|
|
71848
|
+
await execFileAsync4("git", ["fetch", "origin", baseBranch], { cwd: repoRoot, encoding: "utf8" });
|
|
71660
71849
|
} catch (e) {
|
|
71661
71850
|
fetchWarning = `git fetch origin ${baseBranch} failed (proceeding with local HEAD): ${e?.message}`;
|
|
71662
71851
|
}
|
|
71663
71852
|
let baseHeadRaw;
|
|
71664
71853
|
try {
|
|
71665
|
-
const { stdout } = await
|
|
71854
|
+
const { stdout } = await execFileAsync4("git", ["rev-parse", `origin/${baseBranch}`], { cwd: repoRoot, encoding: "utf8" });
|
|
71666
71855
|
baseHeadRaw = stdout.trim();
|
|
71667
71856
|
} catch {
|
|
71668
|
-
const { stdout: localHead } = await
|
|
71857
|
+
const { stdout: localHead } = await execFileAsync4("git", ["rev-parse", "HEAD"], { cwd: repoRoot, encoding: "utf8" });
|
|
71669
71858
|
baseHeadRaw = localHead.trim();
|
|
71670
71859
|
}
|
|
71671
|
-
const { stdout: branchHeadStdout } = await
|
|
71860
|
+
const { stdout: branchHeadStdout } = await execFileAsync4("git", ["rev-parse", branch], { cwd: node.workspace, encoding: "utf8" });
|
|
71672
71861
|
const baseHead = baseHeadRaw;
|
|
71673
71862
|
let branchHead = branchHeadStdout.trim();
|
|
71674
71863
|
recordMeshRefineStage(refineStages, "resolve_refs", "passed", resolveStarted, { branch, baseBranch, baseHead, branchHead, ...fetchWarning ? { fetchWarning } : {} });
|
|
@@ -71756,7 +71945,7 @@ ${tail}` : ""
|
|
|
71756
71945
|
let didAutoRebase = false;
|
|
71757
71946
|
let isBehindBase = false;
|
|
71758
71947
|
try {
|
|
71759
|
-
(0,
|
|
71948
|
+
(0, import_node_child_process6.execFileSync)("git", ["merge-base", "--is-ancestor", branchHead, baseHead], {
|
|
71760
71949
|
cwd: node.workspace,
|
|
71761
71950
|
stdio: "ignore"
|
|
71762
71951
|
});
|
|
@@ -71766,11 +71955,11 @@ ${tail}` : ""
|
|
|
71766
71955
|
if (isBehindBase) {
|
|
71767
71956
|
const autoRebaseStarted = Date.now();
|
|
71768
71957
|
try {
|
|
71769
|
-
(0,
|
|
71958
|
+
(0, import_node_child_process6.execFileSync)("git", ["rebase", baseHead], {
|
|
71770
71959
|
cwd: node.workspace,
|
|
71771
71960
|
stdio: ["ignore", "pipe", "pipe"]
|
|
71772
71961
|
});
|
|
71773
|
-
const { stdout: rebasedHeadStdout } = await
|
|
71962
|
+
const { stdout: rebasedHeadStdout } = await execFileAsync4("git", ["rev-parse", "HEAD"], { cwd: node.workspace, encoding: "utf8" });
|
|
71774
71963
|
branchHead = rebasedHeadStdout.trim();
|
|
71775
71964
|
const rebasedPatchEquivalence = await runMeshRefinePatchEquivalenceGate(repoRoot, baseHead, branchHead);
|
|
71776
71965
|
recordMeshRefineStage(refineStages, "patch_equivalence_after_auto_rebase", rebasedPatchEquivalence.status, autoRebaseStarted, {
|
|
@@ -71807,7 +71996,7 @@ ${tail}` : ""
|
|
|
71807
71996
|
}
|
|
71808
71997
|
} catch (rebaseErr) {
|
|
71809
71998
|
try {
|
|
71810
|
-
(0,
|
|
71999
|
+
(0, import_node_child_process6.execFileSync)("git", ["rebase", "--abort"], { cwd: node.workspace, stdio: "ignore" });
|
|
71811
72000
|
} catch {
|
|
71812
72001
|
}
|
|
71813
72002
|
recordMeshRefineStage(refineStages, "patch_equivalence_after_auto_rebase", "failed", autoRebaseStarted, {
|
|
@@ -72014,7 +72203,7 @@ ${tail}` : ""
|
|
|
72014
72203
|
let mergeResult;
|
|
72015
72204
|
const mergeStarted = Date.now();
|
|
72016
72205
|
try {
|
|
72017
|
-
const result = await
|
|
72206
|
+
const result = await execFileAsync4("git", ["merge", "--no-ff", branch, "-m", `Auto-merge branch '${branch}' via Refinery`], { cwd: repoRoot, encoding: "utf8" });
|
|
72018
72207
|
mergeResult = {
|
|
72019
72208
|
stdout: truncateValidationOutput(result.stdout),
|
|
72020
72209
|
stderr: truncateValidationOutput(result.stderr),
|
|
@@ -72148,7 +72337,7 @@ ${tail}` : ""
|
|
|
72148
72337
|
if (!requireApprovalForPush) {
|
|
72149
72338
|
const pushStarted = Date.now();
|
|
72150
72339
|
try {
|
|
72151
|
-
await
|
|
72340
|
+
await execFileAsync4("git", ["push", "origin", baseBranch], { cwd: repoRoot, encoding: "utf8" });
|
|
72152
72341
|
pushResult = { pushed: true, remote: "origin", branch: baseBranch, durationMs: Date.now() - pushStarted };
|
|
72153
72342
|
recordMeshRefineStage(refineStages, "push", "passed", pushStarted, pushResult);
|
|
72154
72343
|
finalBranchConvergenceState.status = "merged_pushed";
|
|
@@ -72189,6 +72378,223 @@ ${tail}` : ""
|
|
|
72189
72378
|
return { success: false, error: e.message, refineStages };
|
|
72190
72379
|
}
|
|
72191
72380
|
}
|
|
72381
|
+
/**
|
|
72382
|
+
* Batch refinery: converge multiple sibling worktree nodes onto the base branch
|
|
72383
|
+
* in one sequential pipeline, absorbing the rebase + patch-equivalence churn that
|
|
72384
|
+
* arises when several siblings touch the same submodule.
|
|
72385
|
+
*
|
|
72386
|
+
* Reuses executeMeshRefineNodeSynchronously per node — every node goes through the
|
|
72387
|
+
* exact same validation / patch-equivalence / submodule-reachability / merge / cleanup
|
|
72388
|
+
* gates, including its built-in auto-rebase onto fresh origin/<base>. Because each
|
|
72389
|
+
* node fetches origin/<base> at the start of its own refine, a node merged earlier in
|
|
72390
|
+
* the batch advances the base, and the next node's refine auto-rebases onto it before
|
|
72391
|
+
* re-running patch-equivalence. No force-push, no reset — conflicting nodes are
|
|
72392
|
+
* isolated as blocked_review while the rest of the batch proceeds.
|
|
72393
|
+
*/
|
|
72394
|
+
async batchRefineMeshNodes(meshId, requestedNodeIds, args) {
|
|
72395
|
+
const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
|
|
72396
|
+
const mesh = meshRecord?.mesh;
|
|
72397
|
+
if (!mesh) return { success: false, error: `Mesh '${meshId}' not found` };
|
|
72398
|
+
const allNodes = Array.isArray(mesh.nodes) ? mesh.nodes : [];
|
|
72399
|
+
const isConvergeable = (n) => n?.isLocalWorktree && typeof n.workspace === "string" && n.workspace;
|
|
72400
|
+
let targetNodes;
|
|
72401
|
+
if (Array.isArray(requestedNodeIds) && requestedNodeIds.length > 0) {
|
|
72402
|
+
targetNodes = [];
|
|
72403
|
+
const missing = [];
|
|
72404
|
+
const nonWorktree = [];
|
|
72405
|
+
for (const nodeId of requestedNodeIds) {
|
|
72406
|
+
const node = allNodes.find((n) => n.id === nodeId || n.nodeId === nodeId);
|
|
72407
|
+
if (!node) {
|
|
72408
|
+
missing.push(nodeId);
|
|
72409
|
+
continue;
|
|
72410
|
+
}
|
|
72411
|
+
if (!isConvergeable(node)) {
|
|
72412
|
+
nonWorktree.push(nodeId);
|
|
72413
|
+
continue;
|
|
72414
|
+
}
|
|
72415
|
+
targetNodes.push(node);
|
|
72416
|
+
}
|
|
72417
|
+
if (missing.length || nonWorktree.length) {
|
|
72418
|
+
return {
|
|
72419
|
+
success: false,
|
|
72420
|
+
error: "One or more requested nodes are not convergeable local worktree nodes.",
|
|
72421
|
+
...missing.length ? { missingNodeIds: missing } : {},
|
|
72422
|
+
...nonWorktree.length ? { nonWorktreeNodeIds: nonWorktree } : {}
|
|
72423
|
+
};
|
|
72424
|
+
}
|
|
72425
|
+
} else {
|
|
72426
|
+
targetNodes = allNodes.filter(isConvergeable);
|
|
72427
|
+
}
|
|
72428
|
+
if (targetNodes.length === 0) {
|
|
72429
|
+
return { success: true, batch: true, dryRun: args?.dryRun !== false, nodeCount: 0, order: [], results: [], note: "No convergeable local worktree nodes found." };
|
|
72430
|
+
}
|
|
72431
|
+
const { execFile: execFile5 } = await import("child_process");
|
|
72432
|
+
const { promisify: promisify8 } = await import("util");
|
|
72433
|
+
const execFileAsync4 = promisify8(execFile5);
|
|
72434
|
+
const resolveRepoRootFor = (node) => {
|
|
72435
|
+
const sourceNode = node.clonedFromNodeId ? allNodes.find((n) => n.id === node.clonedFromNodeId || n.nodeId === node.clonedFromNodeId) : allNodes.find((n) => !n.isLocalWorktree);
|
|
72436
|
+
return sourceNode?.repoRoot || sourceNode?.workspace;
|
|
72437
|
+
};
|
|
72438
|
+
const repoRootBaseRef = /* @__PURE__ */ new Map();
|
|
72439
|
+
const submodulePathsByRepoRoot = /* @__PURE__ */ new Map();
|
|
72440
|
+
const resolveBaseRef = async (repoRoot) => {
|
|
72441
|
+
const cached2 = repoRootBaseRef.get(repoRoot);
|
|
72442
|
+
if (cached2) return cached2;
|
|
72443
|
+
let baseBranch = "main";
|
|
72444
|
+
try {
|
|
72445
|
+
const { stdout } = await execFileAsync4("git", ["branch", "--show-current"], { cwd: repoRoot, encoding: "utf8" });
|
|
72446
|
+
if (stdout.trim()) baseBranch = stdout.trim();
|
|
72447
|
+
} catch {
|
|
72448
|
+
}
|
|
72449
|
+
let baseRef = "HEAD";
|
|
72450
|
+
try {
|
|
72451
|
+
await execFileAsync4("git", ["fetch", "origin", baseBranch], { cwd: repoRoot, encoding: "utf8" });
|
|
72452
|
+
} catch {
|
|
72453
|
+
}
|
|
72454
|
+
try {
|
|
72455
|
+
const { stdout } = await execFileAsync4("git", ["rev-parse", `origin/${baseBranch}`], { cwd: repoRoot, encoding: "utf8" });
|
|
72456
|
+
baseRef = stdout.trim();
|
|
72457
|
+
} catch {
|
|
72458
|
+
try {
|
|
72459
|
+
const { stdout } = await execFileAsync4("git", ["rev-parse", "HEAD"], { cwd: repoRoot, encoding: "utf8" });
|
|
72460
|
+
baseRef = stdout.trim();
|
|
72461
|
+
} catch {
|
|
72462
|
+
}
|
|
72463
|
+
}
|
|
72464
|
+
repoRootBaseRef.set(repoRoot, baseRef);
|
|
72465
|
+
return baseRef;
|
|
72466
|
+
};
|
|
72467
|
+
const changeAreas = [];
|
|
72468
|
+
for (const node of targetNodes) {
|
|
72469
|
+
const repoRoot = resolveRepoRootFor(node);
|
|
72470
|
+
let branch = typeof node.worktreeBranch === "string" ? node.worktreeBranch : "";
|
|
72471
|
+
try {
|
|
72472
|
+
const { stdout } = await execFileAsync4("git", ["branch", "--show-current"], { cwd: node.workspace, encoding: "utf8" });
|
|
72473
|
+
if (stdout.trim()) branch = stdout.trim();
|
|
72474
|
+
} catch {
|
|
72475
|
+
}
|
|
72476
|
+
if (!repoRoot || !branch) {
|
|
72477
|
+
changeAreas.push({
|
|
72478
|
+
nodeId: node.id,
|
|
72479
|
+
workspace: node.workspace,
|
|
72480
|
+
branch: branch || "(unknown)",
|
|
72481
|
+
changedTopLevelPaths: [],
|
|
72482
|
+
changedFiles: [],
|
|
72483
|
+
touchedSubmodulePaths: [],
|
|
72484
|
+
touchesSubmodule: false,
|
|
72485
|
+
aheadCount: 0,
|
|
72486
|
+
error: !repoRoot ? "source repoRoot not found" : "branch not resolved"
|
|
72487
|
+
});
|
|
72488
|
+
continue;
|
|
72489
|
+
}
|
|
72490
|
+
if (!submodulePathsByRepoRoot.has(repoRoot)) {
|
|
72491
|
+
let subPaths = /* @__PURE__ */ new Set();
|
|
72492
|
+
try {
|
|
72493
|
+
const { stdout } = await execFileAsync4("git", ["config", "--file", ".gitmodules", "--get-regexp", "path"], { cwd: repoRoot, encoding: "utf8" });
|
|
72494
|
+
for (const line of stdout.split("\n")) {
|
|
72495
|
+
const trimmed = line.trim();
|
|
72496
|
+
const spaceIdx = trimmed.indexOf(" ");
|
|
72497
|
+
if (spaceIdx === -1) continue;
|
|
72498
|
+
const value = trimmed.slice(spaceIdx + 1).trim();
|
|
72499
|
+
if (value) subPaths.add(value);
|
|
72500
|
+
}
|
|
72501
|
+
} catch {
|
|
72502
|
+
subPaths = /* @__PURE__ */ new Set();
|
|
72503
|
+
}
|
|
72504
|
+
submodulePathsByRepoRoot.set(repoRoot, subPaths);
|
|
72505
|
+
}
|
|
72506
|
+
const baseRef = await resolveBaseRef(repoRoot);
|
|
72507
|
+
let branchRef = branch;
|
|
72508
|
+
try {
|
|
72509
|
+
const { stdout } = await execFileAsync4("git", ["rev-parse", branch], { cwd: node.workspace, encoding: "utf8" });
|
|
72510
|
+
branchRef = stdout.trim() || branch;
|
|
72511
|
+
} catch {
|
|
72512
|
+
}
|
|
72513
|
+
changeAreas.push(await analyzeMeshRefineNodeChangeArea({
|
|
72514
|
+
nodeId: node.id,
|
|
72515
|
+
workspace: node.workspace,
|
|
72516
|
+
branch,
|
|
72517
|
+
baseRef,
|
|
72518
|
+
branchRef,
|
|
72519
|
+
diffCwd: node.workspace,
|
|
72520
|
+
submodulePaths: submodulePathsByRepoRoot.get(repoRoot)
|
|
72521
|
+
}));
|
|
72522
|
+
}
|
|
72523
|
+
const ordering = orderMeshRefineBatchNodes(changeAreas);
|
|
72524
|
+
const orderedNodes = ordering.order.map((nodeId) => targetNodes.find((n) => n.id === nodeId || n.nodeId === nodeId)).filter((n) => !!n);
|
|
72525
|
+
const dryRun = args?.dryRun !== false && args?.execute !== true;
|
|
72526
|
+
if (dryRun) {
|
|
72527
|
+
return {
|
|
72528
|
+
success: true,
|
|
72529
|
+
batch: true,
|
|
72530
|
+
dryRun: true,
|
|
72531
|
+
nodeCount: orderedNodes.length,
|
|
72532
|
+
order: ordering.order,
|
|
72533
|
+
orderingRationale: ordering.rationale,
|
|
72534
|
+
changeAreas: ordering.changeAreas,
|
|
72535
|
+
plan: orderedNodes.map((node) => ({
|
|
72536
|
+
nodeId: node.id,
|
|
72537
|
+
workspace: node.workspace,
|
|
72538
|
+
validationPlan: buildMeshRefineValidationPlan(mesh, node.workspace),
|
|
72539
|
+
mergeWillRun: false
|
|
72540
|
+
})),
|
|
72541
|
+
note: "Dry-run: no validation, rebase, or merge was executed. Re-run with execute=true to converge nodes in this order."
|
|
72542
|
+
};
|
|
72543
|
+
}
|
|
72544
|
+
const results = [];
|
|
72545
|
+
for (const node of orderedNodes) {
|
|
72546
|
+
let result;
|
|
72547
|
+
try {
|
|
72548
|
+
result = await this.executeMeshRefineNodeSynchronously(meshId, node.id, args);
|
|
72549
|
+
} catch (e) {
|
|
72550
|
+
result = { success: false, error: e?.message || String(e) };
|
|
72551
|
+
}
|
|
72552
|
+
const code = typeof result.code === "string" ? result.code : "";
|
|
72553
|
+
let convergence;
|
|
72554
|
+
if (code === "already_merged" && result.alreadyMergedViaOtherPath) {
|
|
72555
|
+
convergence = "skipped_patch_equivalent";
|
|
72556
|
+
} else if (result.success === true) {
|
|
72557
|
+
convergence = "merged_to_main";
|
|
72558
|
+
} else if (code === "merge_failed") {
|
|
72559
|
+
convergence = "not_mergeable";
|
|
72560
|
+
} else {
|
|
72561
|
+
convergence = "blocked_review";
|
|
72562
|
+
}
|
|
72563
|
+
const fbcs = result.finalBranchConvergenceState && typeof result.finalBranchConvergenceState === "object" ? result.finalBranchConvergenceState : void 0;
|
|
72564
|
+
const stage = Array.isArray(result.refineStages) ? result.refineStages.filter((s) => s.status === "failed").map((s) => s.stage).filter(Boolean).pop() : void 0;
|
|
72565
|
+
results.push({
|
|
72566
|
+
nodeId: node.id,
|
|
72567
|
+
workspace: node.workspace,
|
|
72568
|
+
convergence,
|
|
72569
|
+
...code ? { code } : {},
|
|
72570
|
+
...typeof result.blockedReason === "string" ? { reason: result.blockedReason } : {},
|
|
72571
|
+
...stage ? { stage } : {},
|
|
72572
|
+
...typeof result.error === "string" ? { error: result.error } : {},
|
|
72573
|
+
...fbcs ? { finalBranchConvergenceState: fbcs } : {}
|
|
72574
|
+
});
|
|
72575
|
+
}
|
|
72576
|
+
const summary = {
|
|
72577
|
+
merged: results.filter((r) => r.convergence === "merged_to_main").length,
|
|
72578
|
+
skipped: results.filter((r) => r.convergence === "skipped_patch_equivalent").length,
|
|
72579
|
+
blocked: results.filter((r) => r.convergence === "blocked_review").length,
|
|
72580
|
+
notMergeable: results.filter((r) => r.convergence === "not_mergeable").length
|
|
72581
|
+
};
|
|
72582
|
+
const allConverged = summary.blocked === 0 && summary.notMergeable === 0;
|
|
72583
|
+
return {
|
|
72584
|
+
success: true,
|
|
72585
|
+
batch: true,
|
|
72586
|
+
dryRun: false,
|
|
72587
|
+
nodeCount: orderedNodes.length,
|
|
72588
|
+
order: ordering.order,
|
|
72589
|
+
orderingRationale: ordering.rationale,
|
|
72590
|
+
summary,
|
|
72591
|
+
allConverged,
|
|
72592
|
+
results,
|
|
72593
|
+
...allConverged ? {} : {
|
|
72594
|
+
nextStep: "Resolve blocked_review / not_mergeable nodes manually (see per-node code/stage/error), then re-run mesh_refine_batch for the remaining nodes."
|
|
72595
|
+
}
|
|
72596
|
+
};
|
|
72597
|
+
}
|
|
72192
72598
|
async finishMeshRefineJob(handle, args) {
|
|
72193
72599
|
const key = this.buildRefineJobKey(handle.meshId, handle.targetNodeId);
|
|
72194
72600
|
let result;
|
|
@@ -73732,6 +74138,12 @@ ${tail}` : ""
|
|
|
73732
74138
|
if (!meshId || !nodeId) return { success: false, error: "meshId and nodeId required" };
|
|
73733
74139
|
return this.startMeshRefineJob(meshId, nodeId, args);
|
|
73734
74140
|
}
|
|
74141
|
+
case "batch_refine_mesh_nodes": {
|
|
74142
|
+
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
74143
|
+
if (!meshId) return { success: false, error: "meshId required" };
|
|
74144
|
+
const requestedNodeIds = Array.isArray(args?.nodeIds) ? args.nodeIds.filter((v) => typeof v === "string" && v.trim().length > 0).map((v) => v.trim()) : void 0;
|
|
74145
|
+
return this.batchRefineMeshNodes(meshId, requestedNodeIds, args);
|
|
74146
|
+
}
|
|
73735
74147
|
case "remove_mesh_node": {
|
|
73736
74148
|
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
73737
74149
|
const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
|