@adhdev/daemon-core 0.9.82-rc.386 → 0.9.82-rc.387
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/git/git-worktree.d.ts +22 -0
- package/dist/index.js +158 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +158 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/commands/med-family/mesh-crud.ts +11 -0
- package/src/commands/router.ts +49 -2
- package/src/git/git-worktree.ts +68 -0
- package/src/mesh/mesh-work-queue.ts +110 -2
|
@@ -75,3 +75,25 @@ export declare function listWorktrees(repoRoot: string): Promise<WorktreeEntry[]
|
|
|
75
75
|
* Parse `git worktree list --porcelain` output into structured entries.
|
|
76
76
|
*/
|
|
77
77
|
export declare function parseWorktreeListOutput(output: string): WorktreeEntry[];
|
|
78
|
+
export interface BranchRefDeleteResult {
|
|
79
|
+
/** True if the branch ref no longer exists after this call. */
|
|
80
|
+
deleted: boolean;
|
|
81
|
+
/** Why it was (not) deleted, for surfacing in the cleanup result. */
|
|
82
|
+
reason: string;
|
|
83
|
+
/** True when a forced delete (`-D`) was needed (e.g. squash/patch-equivalent merge). */
|
|
84
|
+
forced?: boolean;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Delete a local branch ref after its worktree was removed.
|
|
88
|
+
*
|
|
89
|
+
* SAFETY: this is only meant to be called once the caller has independently
|
|
90
|
+
* verified that the branch is fully merged / its content is contained in the
|
|
91
|
+
* default ref (no work loss). It first tries the safe `git branch -d`, which
|
|
92
|
+
* refuses to delete a branch git itself does not consider merged. If
|
|
93
|
+
* `safeDeleteOnly` is false (the caller proved containment by patch-equivalence,
|
|
94
|
+
* which `-d` cannot see), it falls back to `git branch -D`. When the branch is
|
|
95
|
+
* already gone, this reports `deleted: true` idempotently.
|
|
96
|
+
*/
|
|
97
|
+
export declare function deleteBranchRef(repoRoot: string, branch: string, opts?: {
|
|
98
|
+
safeDeleteOnly?: boolean;
|
|
99
|
+
}): Promise<BranchRefDeleteResult>;
|
package/dist/index.js
CHANGED
|
@@ -383,10 +383,10 @@ function readInjected(value) {
|
|
|
383
383
|
}
|
|
384
384
|
function getDaemonBuildInfo() {
|
|
385
385
|
if (cached) return cached;
|
|
386
|
-
const commit = readInjected(true ? "
|
|
387
|
-
const commitShort = readInjected(true ? "
|
|
388
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
389
|
-
const builtAt = readInjected(true ? "2026-06-
|
|
386
|
+
const commit = readInjected(true ? "057d5def5d55af124dfe910ee9accdf244576c14" : void 0) ?? "unknown";
|
|
387
|
+
const commitShort = readInjected(true ? "057d5def" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
388
|
+
const version = readInjected(true ? "0.9.82-rc.387" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
389
|
+
const builtAt = readInjected(true ? "2026-06-26T02:21:27.575Z" : void 0);
|
|
390
390
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
391
391
|
return cached;
|
|
392
392
|
}
|
|
@@ -1413,6 +1413,7 @@ var init_git_diff = __esm({
|
|
|
1413
1413
|
var git_worktree_exports = {};
|
|
1414
1414
|
__export(git_worktree_exports, {
|
|
1415
1415
|
createWorktree: () => createWorktree,
|
|
1416
|
+
deleteBranchRef: () => deleteBranchRef,
|
|
1416
1417
|
listWorktrees: () => listWorktrees,
|
|
1417
1418
|
parseWorktreeListOutput: () => parseWorktreeListOutput,
|
|
1418
1419
|
removeWorktree: () => removeWorktree,
|
|
@@ -1555,6 +1556,53 @@ function parseWorktreeListOutput(output) {
|
|
|
1555
1556
|
}
|
|
1556
1557
|
return entries;
|
|
1557
1558
|
}
|
|
1559
|
+
async function deleteBranchRef(repoRoot, branch, opts = {}) {
|
|
1560
|
+
const name = (branch || "").trim();
|
|
1561
|
+
if (!name) return { deleted: false, reason: "empty_branch_name" };
|
|
1562
|
+
try {
|
|
1563
|
+
await execFileAsync2("git", ["rev-parse", "--verify", "--quiet", `refs/heads/${name}`], {
|
|
1564
|
+
cwd: repoRoot,
|
|
1565
|
+
encoding: "utf8",
|
|
1566
|
+
timeout: GIT_TIMEOUT_MS,
|
|
1567
|
+
maxBuffer: GIT_MAX_BUFFER,
|
|
1568
|
+
windowsHide: true
|
|
1569
|
+
});
|
|
1570
|
+
} catch {
|
|
1571
|
+
return { deleted: true, reason: "branch_ref_absent" };
|
|
1572
|
+
}
|
|
1573
|
+
try {
|
|
1574
|
+
await execFileAsync2("git", ["branch", "-d", name], {
|
|
1575
|
+
cwd: repoRoot,
|
|
1576
|
+
encoding: "utf8",
|
|
1577
|
+
timeout: GIT_TIMEOUT_MS,
|
|
1578
|
+
maxBuffer: GIT_MAX_BUFFER,
|
|
1579
|
+
windowsHide: true
|
|
1580
|
+
});
|
|
1581
|
+
return { deleted: true, reason: "safe_deleted_merged_branch" };
|
|
1582
|
+
} catch (error) {
|
|
1583
|
+
const stderr = typeof error?.stderr === "string" ? error.stderr : "";
|
|
1584
|
+
const notMerged = /not fully merged/i.test(stderr) || /not fully merged/i.test(String(error?.message || ""));
|
|
1585
|
+
if (!notMerged) {
|
|
1586
|
+
return { deleted: false, reason: `branch_delete_failed: ${stderr.trim() || error?.message || "unknown error"}` };
|
|
1587
|
+
}
|
|
1588
|
+
if (opts.safeDeleteOnly) {
|
|
1589
|
+
return { deleted: false, reason: "branch_not_merged_per_git_safe_delete_only" };
|
|
1590
|
+
}
|
|
1591
|
+
try {
|
|
1592
|
+
await execFileAsync2("git", ["branch", "-D", name], {
|
|
1593
|
+
cwd: repoRoot,
|
|
1594
|
+
encoding: "utf8",
|
|
1595
|
+
timeout: GIT_TIMEOUT_MS,
|
|
1596
|
+
maxBuffer: GIT_MAX_BUFFER,
|
|
1597
|
+
windowsHide: true
|
|
1598
|
+
});
|
|
1599
|
+
return { deleted: true, reason: "force_deleted_patch_equivalent_branch", forced: true };
|
|
1600
|
+
} catch (forceError) {
|
|
1601
|
+
const fErr = typeof forceError?.stderr === "string" ? forceError.stderr : forceError?.message;
|
|
1602
|
+
return { deleted: false, reason: `branch_force_delete_failed: ${String(fErr || "unknown error").trim()}` };
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1558
1606
|
async function pruneWorktrees(repoRoot) {
|
|
1559
1607
|
try {
|
|
1560
1608
|
await execFileAsync2("git", ["worktree", "prune"], {
|
|
@@ -4421,6 +4469,12 @@ function isMutationKeywordInCommandContext(text, matchStart, matchEnd) {
|
|
|
4421
4469
|
const linePrefix = text.slice(lineStart, matchStart);
|
|
4422
4470
|
if (/^\s*[$>]\s/.test(text.slice(lineStart))) return true;
|
|
4423
4471
|
if (/^\s*$/.test(linePrefix)) return true;
|
|
4472
|
+
let tokStart = matchStart;
|
|
4473
|
+
while (tokStart > lineStart && !/\s/.test(text[tokStart - 1])) tokStart--;
|
|
4474
|
+
const beforeToken = text.slice(lineStart, tokStart);
|
|
4475
|
+
const tokenLead = text.slice(tokStart, matchStart);
|
|
4476
|
+
const atCmdPos = /^\s*$/.test(beforeToken) || /(?:&&|\|\||\||;)\s*$/.test(beforeToken);
|
|
4477
|
+
if (atCmdPos && /^(?:\.\/|\.\.\/|~\/|\/)/.test(tokenLead)) return true;
|
|
4424
4478
|
if (/(?:&&|\|\||\||;)\s*$/.test(linePrefix)) return true;
|
|
4425
4479
|
if (/(?:^|[\s,])(?:then|run|first)\s+$/i.test(linePrefix)) return true;
|
|
4426
4480
|
if (/,\s*$/.test(linePrefix)) return true;
|
|
@@ -4447,9 +4501,65 @@ function isInsideBackticksOrFence(text, matchStart, matchEnd) {
|
|
|
4447
4501
|
}
|
|
4448
4502
|
return inlineCount % 2 === 1;
|
|
4449
4503
|
}
|
|
4504
|
+
function isInsidePathSegment(text, matchStart, matchEnd) {
|
|
4505
|
+
const prev = matchStart > 0 ? text[matchStart - 1] : "";
|
|
4506
|
+
const next = matchEnd < text.length ? text[matchEnd] : "";
|
|
4507
|
+
const isSep = (c) => c === "/" || c === "\\";
|
|
4508
|
+
const segChar = (c) => /[A-Za-z0-9._~-]/.test(c);
|
|
4509
|
+
const inPath = isSep(prev) && (next === "" || isSep(next) || segChar(next) || /\s/.test(next)) || isSep(next) && (prev === "" || isSep(prev) || segChar(prev) || /\s/.test(prev));
|
|
4510
|
+
if (!inPath) return false;
|
|
4511
|
+
const lineStart = text.lastIndexOf("\n", matchStart - 1) + 1;
|
|
4512
|
+
let tokStart = matchStart;
|
|
4513
|
+
while (tokStart > lineStart && !/\s/.test(text[tokStart - 1])) tokStart--;
|
|
4514
|
+
const linePrefixBeforeToken = text.slice(lineStart, tokStart);
|
|
4515
|
+
const tokenPrefix = text.slice(tokStart, matchStart);
|
|
4516
|
+
const atLineStart = /^\s*$/.test(linePrefixBeforeToken);
|
|
4517
|
+
const afterConnective = /(?:&&|\|\||\||;)\s*$/.test(linePrefixBeforeToken);
|
|
4518
|
+
const isRunPrefixPath = /^(?:\.\/|\.\.\/|~\/|\/)/.test(tokenPrefix);
|
|
4519
|
+
if ((atLineStart || afterConnective) && isRunPrefixPath) return false;
|
|
4520
|
+
return true;
|
|
4521
|
+
}
|
|
4522
|
+
function isInsideQuotedSpan(text, matchStart, matchEnd) {
|
|
4523
|
+
const pairs = [
|
|
4524
|
+
["\u201C", "\u201D"],
|
|
4525
|
+
["\u2018", "\u2019"],
|
|
4526
|
+
["\u300C", "\u300D"],
|
|
4527
|
+
["\u300E", "\u300F"],
|
|
4528
|
+
["\u300A", "\u300B"]
|
|
4529
|
+
];
|
|
4530
|
+
for (const [open, close] of pairs) {
|
|
4531
|
+
const openIdx = text.lastIndexOf(open, matchStart - 1);
|
|
4532
|
+
if (openIdx < 0) continue;
|
|
4533
|
+
if (text.indexOf(close, openIdx + open.length) >= matchEnd) return true;
|
|
4534
|
+
}
|
|
4535
|
+
const lineStart = text.lastIndexOf("\n", matchStart - 1) + 1;
|
|
4536
|
+
const lineEnd = (() => {
|
|
4537
|
+
const i = text.indexOf("\n", matchEnd);
|
|
4538
|
+
return i < 0 ? text.length : i;
|
|
4539
|
+
})();
|
|
4540
|
+
for (const q of ['"', "'"]) {
|
|
4541
|
+
let count = 0;
|
|
4542
|
+
for (let i = lineStart; i < matchStart; i++) if (text[i] === q) count++;
|
|
4543
|
+
if (count % 2 === 1 && text.indexOf(q, matchEnd) >= 0 && text.indexOf(q, matchEnd) < lineEnd) {
|
|
4544
|
+
if (q === '"') return true;
|
|
4545
|
+
let openPos = -1, c = 0;
|
|
4546
|
+
for (let i = lineStart; i < matchStart; i++) {
|
|
4547
|
+
if (text[i] === q) {
|
|
4548
|
+
c++;
|
|
4549
|
+
if (c % 2 === 1) openPos = i;
|
|
4550
|
+
}
|
|
4551
|
+
}
|
|
4552
|
+
const beforeOpen = openPos > lineStart ? text[openPos - 1] : " ";
|
|
4553
|
+
if (/[\s(\[{>]/.test(beforeOpen) || openPos === lineStart) return true;
|
|
4554
|
+
}
|
|
4555
|
+
}
|
|
4556
|
+
return false;
|
|
4557
|
+
}
|
|
4450
4558
|
function isRealMutationMatch(text, matchStart, matchEnd) {
|
|
4451
4559
|
if (hasNegationBefore(text, matchStart)) return false;
|
|
4452
4560
|
if (hasTrailingNegation(text, matchEnd)) return false;
|
|
4561
|
+
if (isInsidePathSegment(text, matchStart, matchEnd)) return false;
|
|
4562
|
+
if (isInsideQuotedSpan(text, matchStart, matchEnd)) return false;
|
|
4453
4563
|
return isMutationKeywordInCommandContext(text, matchStart, matchEnd);
|
|
4454
4564
|
}
|
|
4455
4565
|
function patternHasRealMutation(pattern, text) {
|
|
@@ -47597,13 +47707,16 @@ var meshCrudHandlers = {
|
|
|
47597
47707
|
worktreeBranch: typeof node?.worktreeBranch === "string" ? node.worktreeBranch : void 0,
|
|
47598
47708
|
worktreeCleanupFallback: typeof worktreeCleanup?.fallback === "string" ? worktreeCleanup.fallback : void 0,
|
|
47599
47709
|
forced: worktreeCleanup?.forced === true ? true : void 0,
|
|
47600
|
-
forceFallbackReason: typeof worktreeCleanup?.reason === "string" ? worktreeCleanup.reason : void 0
|
|
47710
|
+
forceFallbackReason: typeof worktreeCleanup?.reason === "string" ? worktreeCleanup.reason : void 0,
|
|
47711
|
+
branchRefDeleted: typeof worktreeCleanup?.branchRefDeleted === "boolean" ? worktreeCleanup.branchRefDeleted : void 0,
|
|
47712
|
+
branchRefReason: typeof worktreeCleanup?.branchRefReason === "string" ? worktreeCleanup.branchRefReason : void 0
|
|
47601
47713
|
}
|
|
47602
47714
|
});
|
|
47603
47715
|
} catch {
|
|
47604
47716
|
}
|
|
47605
47717
|
}
|
|
47606
47718
|
const residueWarning = worktreeCleanup?.residue === true && typeof worktreeCleanup?.residueWarning === "string" ? worktreeCleanup.residueWarning : void 0;
|
|
47719
|
+
const branchRefWarning = typeof worktreeCleanup?.branchRefWarning === "string" ? worktreeCleanup.branchRefWarning : void 0;
|
|
47607
47720
|
const skippedLiveSessionIds = Array.isArray(sessionCleanup?.skippedLiveSessionIds) ? sessionCleanup.skippedLiveSessionIds.filter((v) => typeof v === "string") : [];
|
|
47608
47721
|
const orphanedSessionsRemaining = skippedLiveSessionIds.length > 0;
|
|
47609
47722
|
const orphanNextAction = orphanedSessionsRemaining ? `Live session(s) [${skippedLiveSessionIds.join(", ")}] were skipped and still survive this node removal. Run mesh_cleanup_sessions with mode:'stop_and_delete' and sessionIds:[${skippedLiveSessionIds.map((id) => `'${id}'`).join(", ")}] to release them.` : void 0;
|
|
@@ -47611,6 +47724,7 @@ var meshCrudHandlers = {
|
|
|
47611
47724
|
success: true,
|
|
47612
47725
|
removed,
|
|
47613
47726
|
...residueWarning ? { residueWarning } : {},
|
|
47727
|
+
...branchRefWarning ? { branchRefWarning } : {},
|
|
47614
47728
|
...sessionCleanup ? { sessionCleanup } : {},
|
|
47615
47729
|
...worktreeCleanup ? { worktreeCleanup } : {},
|
|
47616
47730
|
...orphanedSessionsRemaining ? { orphanedSessionsRemaining: true, nextAction: orphanNextAction } : {}
|
|
@@ -52880,16 +52994,50 @@ var DaemonCommandRouter = class {
|
|
|
52880
52994
|
recoveryHint: "Inspect the worktree branch and mesh metadata before retrying cleanup."
|
|
52881
52995
|
};
|
|
52882
52996
|
}
|
|
52883
|
-
const
|
|
52997
|
+
const mergeConvergence = await this.getWorktreeForceCleanupConvergence({ repoRoot, workspace, node: args.node });
|
|
52998
|
+
const forceFallbackConvergence = args.force ? { allow: true, status: "force_override", source: "caller_force_flag" } : mergeConvergence;
|
|
52999
|
+
const deleteBranchIfMerged = async () => {
|
|
53000
|
+
const branch = String(args.node.worktreeBranch).trim();
|
|
53001
|
+
const status = mergeConvergence.allow ? mergeConvergence.status || "" : "";
|
|
53002
|
+
const MERGED_STATUSES = /* @__PURE__ */ new Set([
|
|
53003
|
+
"merged_to_main",
|
|
53004
|
+
"merged_pushed",
|
|
53005
|
+
"merged_to_default_ref",
|
|
53006
|
+
"cleanup_candidate"
|
|
53007
|
+
]);
|
|
53008
|
+
const PATCH_EQUIV_STATUS = "patch_equivalent_to_default_ref";
|
|
53009
|
+
if (!branch) {
|
|
53010
|
+
return { branchRefDeleted: false, branchRefReason: "empty_branch_name" };
|
|
53011
|
+
}
|
|
53012
|
+
if (!mergeConvergence.allow || !MERGED_STATUSES.has(status) && status !== PATCH_EQUIV_STATUS) {
|
|
53013
|
+
return {
|
|
53014
|
+
branchRefDeleted: false,
|
|
53015
|
+
branchRefReason: `branch_not_merged_preserved: ${mergeConvergence.error || mergeConvergence.status || "convergence_unverified"}`,
|
|
53016
|
+
branchRefWarning: `Branch ref '${branch}' was preserved (not deleted) because it is not confirmed merged into the default ref \u2014 no work was lost. Merge it (or pass a verified branchConvergence final state) and re-run cleanup, or delete it manually after confirming.`
|
|
53017
|
+
};
|
|
53018
|
+
}
|
|
53019
|
+
const { deleteBranchRef: deleteBranchRef2 } = await Promise.resolve().then(() => (init_git_worktree(), git_worktree_exports));
|
|
53020
|
+
const res = await deleteBranchRef2(repoRoot, branch, { safeDeleteOnly: status !== PATCH_EQUIV_STATUS });
|
|
53021
|
+
return {
|
|
53022
|
+
branchRefDeleted: res.deleted,
|
|
53023
|
+
branchRefReason: res.reason,
|
|
53024
|
+
...res.forced ? { branchRefForced: true } : {},
|
|
53025
|
+
...res.deleted ? {} : {
|
|
53026
|
+
branchRefWarning: `Branch ref '${branch}' could not be deleted (${res.reason}); it was preserved so no work is lost.`
|
|
53027
|
+
}
|
|
53028
|
+
};
|
|
53029
|
+
};
|
|
52884
53030
|
try {
|
|
52885
53031
|
const result = await removeWorktree2(repoRoot, workspace, {
|
|
52886
53032
|
requireClean: !args.force,
|
|
52887
53033
|
allowSubmoduleForceFallback: forceFallbackConvergence.allow
|
|
52888
53034
|
});
|
|
53035
|
+
const branchOutcome = await deleteBranchIfMerged();
|
|
52889
53036
|
return {
|
|
52890
53037
|
success: true,
|
|
52891
53038
|
removedPath: result.removedPath,
|
|
52892
53039
|
repoRoot,
|
|
53040
|
+
...branchOutcome,
|
|
52893
53041
|
...result.fallback ? {
|
|
52894
53042
|
fallback: result.fallback,
|
|
52895
53043
|
forced: result.forced,
|
|
@@ -52922,10 +53070,12 @@ var DaemonCommandRouter = class {
|
|
|
52922
53070
|
maxBuffer: GIT_MAX_BUFFER_CLEANUP,
|
|
52923
53071
|
windowsHide: true
|
|
52924
53072
|
});
|
|
53073
|
+
const branchOutcome = await deleteBranchIfMerged();
|
|
52925
53074
|
return {
|
|
52926
53075
|
success: true,
|
|
52927
53076
|
removedPath: workspace,
|
|
52928
53077
|
repoRoot,
|
|
53078
|
+
...branchOutcome,
|
|
52929
53079
|
fallback: "git_worktree_remove_submodule_deinit",
|
|
52930
53080
|
forced: true,
|
|
52931
53081
|
reason: "working_trees_containing_submodules",
|
|
@@ -52943,10 +53093,12 @@ var DaemonCommandRouter = class {
|
|
|
52943
53093
|
});
|
|
52944
53094
|
} catch {
|
|
52945
53095
|
}
|
|
53096
|
+
const branchOutcome = await deleteBranchIfMerged();
|
|
52946
53097
|
return {
|
|
52947
53098
|
success: true,
|
|
52948
53099
|
removedPath: workspace,
|
|
52949
53100
|
repoRoot,
|
|
53101
|
+
...branchOutcome,
|
|
52950
53102
|
fallback: "fs_rm_worktree_prune",
|
|
52951
53103
|
forced: true,
|
|
52952
53104
|
reason: "working_trees_containing_submodules",
|