@adhdev/daemon-core 0.9.82-rc.385 → 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.
@@ -26,6 +26,18 @@ export interface CliAdapterStatus {
26
26
  providerSessionId?: string;
27
27
  errorMessage?: string;
28
28
  errorReason?: string;
29
+ /**
30
+ * FSM-spec adapters only: true once the driver has observed its first
31
+ * non-initial idle state (the prompt is genuinely drawn — see
32
+ * FsmDriver.maybeMarkReady / readySeenOnce). Used by CliProviderInstance to
33
+ * re-arm the queue-claim `agent:ready` event on the first genuine ready,
34
+ * independent of the boot-time starting→idle one-shot. That one-shot is
35
+ * consumed too early for providers whose INITIAL FSM state already reports
36
+ * status 'idle' (e.g. antigravity-cli), so without this re-arm the worker
37
+ * never claims its queued task and the coordinator relaunch-loops. Absent
38
+ * (undefined) for non-FSM adapters — they keep the boot one-shot behavior.
39
+ */
40
+ fsmReadySeen?: boolean;
29
41
  }
30
42
  export interface AcpAdapterHandle {
31
43
  onEvent(event: string, data?: unknown): void;
@@ -58,6 +58,17 @@ export interface CliSessionStatus {
58
58
  errorMessage?: string;
59
59
  errorReason?: string;
60
60
  providerSessionId?: string;
61
+ /**
62
+ * Spec/FSM adapters only (SpecCliAdapter): true once the driver has observed
63
+ * its first non-initial idle state — the prompt is genuinely drawn. The
64
+ * legacy ProviderCliAdapter never sets it (undefined). CliProviderInstance
65
+ * uses it to re-arm the queue-claim agent:ready on the first genuine ready,
66
+ * independent of the boot-time starting→idle one-shot (which is consumed too
67
+ * early for specs whose initial state already reports status 'idle', e.g.
68
+ * antigravity-cli — without the re-arm the worker never claims its queued
69
+ * task and the coordinator relaunch-loops).
70
+ */
71
+ fsmReadySeen?: boolean;
61
72
  /**
62
73
  * Timestamp (ms) of the most recent raw PTY output chunk. Advances on every
63
74
  * byte the process emits, including tool/build output that produces no
@@ -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 ? "3aa3e12839017da30b44a64ca6c20f0c9730fae2" : void 0) ?? "unknown";
387
- const commitShort = readInjected(true ? "3aa3e128" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
388
- const version = readInjected(true ? "0.9.82-rc.385" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
389
- const builtAt = readInjected(true ? "2026-06-25T16:15:39.207Z" : void 0);
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) {
@@ -36214,6 +36324,17 @@ var FsmDriver = class {
36214
36324
  hasIdleHoldPending() {
36215
36325
  return (this.lastFsmEval?.transitions ?? []).some((t) => !t.holdSatisfied && t.condResult);
36216
36326
  }
36327
+ /**
36328
+ * True once the machine has reached its first non-initial idle state (the
36329
+ * prompt is genuinely drawn — see maybeMarkReady). The cli-adapter surfaces
36330
+ * this on its idle status so CliProviderInstance can re-arm the queue-claim
36331
+ * agent:ready on the first genuine ready, independent of the boot-time
36332
+ * starting→idle one-shot (which is consumed too early for specs whose
36333
+ * initial state already reports idle).
36334
+ */
36335
+ hasSeenReady() {
36336
+ return this.readySeenOnce;
36337
+ }
36217
36338
  getCompletionIdleDebounceState() {
36218
36339
  const out = outgoingTransitions(this.spec, this.currentStateId);
36219
36340
  const toReady = this.lastFsmEval?.transitions.find((t, i) => {
@@ -37880,7 +38001,7 @@ var SpecCliAdapter = class _SpecCliAdapter {
37880
38001
  if (state.status === "generating") {
37881
38002
  return { status: "generating", messages: [], activeModal: null, activeInteractivePrompt: this.activeInteractivePrompt, ...sessionFields };
37882
38003
  }
37883
- return { status: "idle", messages: [], activeModal: null, activeInteractivePrompt: this.activeInteractivePrompt, ...sessionFields };
38004
+ return { status: "idle", messages: [], activeModal: null, activeInteractivePrompt: this.activeInteractivePrompt, fsmReadySeen: this.driver.hasSeenReady?.() ?? false, ...sessionFields };
37884
38005
  }
37885
38006
  maybeRefreshNativeHistory() {
37886
38007
  }
@@ -38925,6 +39046,14 @@ var CliProviderInstance = class _CliProviderInstance {
38925
39046
  context = null;
38926
39047
  events = [];
38927
39048
  lastStatus = "starting";
39049
+ // Idempotency guard for the queue-claim agent:ready event. agent:ready is the
39050
+ // sole signal the mesh coordinator's tryAssignQueueTask waits on to hand a
39051
+ // queued task to this worker. It is emitted in two places: the boot-time
39052
+ // starting→idle one-shot, and the readySeen re-arm below. This flag makes the
39053
+ // event fire AT MOST ONCE per session so a worker is never claimed twice and a
39054
+ // queued task is never double-dispatched/double-injected. Whichever path fires
39055
+ // first sets it; the other becomes a no-op.
39056
+ agentReadyEmitted = false;
38928
39057
  generatingStartedAt = 0;
38929
39058
  settings = {};
38930
39059
  monitor;
@@ -40005,6 +40134,17 @@ var CliProviderInstance = class _CliProviderInstance {
40005
40134
  } catch {
40006
40135
  }
40007
40136
  }
40137
+ /**
40138
+ * Emit the queue-claim agent:ready event at most once per session. Both the
40139
+ * boot-time starting→idle one-shot and the fsmReadySeen re-arm call this; the
40140
+ * agentReadyEmitted guard ensures the second caller is a no-op so a worker is
40141
+ * never claimed twice and a queued task is never double-dispatched.
40142
+ */
40143
+ emitAgentReadyOnce(chatTitle, now) {
40144
+ if (this.agentReadyEmitted) return;
40145
+ this.agentReadyEmitted = true;
40146
+ this.pushEvent({ event: "agent:ready", chatTitle, timestamp: now });
40147
+ }
40008
40148
  detectStatusTransition() {
40009
40149
  const now = Date.now();
40010
40150
  const adapterStatus = this.adapter.getStatus({ allowParse: false });
@@ -40157,7 +40297,7 @@ var CliProviderInstance = class _CliProviderInstance {
40157
40297
  this.scheduleCompletedDebounceFlush(flushDelay);
40158
40298
  }
40159
40299
  } else if (newStatus === "idle" && this.lastStatus === "starting") {
40160
- this.pushEvent({ event: "agent:ready", chatTitle, timestamp: now });
40300
+ this.emitAgentReadyOnce(chatTitle, now);
40161
40301
  } else if (newStatus === "error") {
40162
40302
  if (this.generatingDebounceTimer) {
40163
40303
  clearTimeout(this.generatingDebounceTimer);
@@ -40196,6 +40336,9 @@ var CliProviderInstance = class _CliProviderInstance {
40196
40336
  }
40197
40337
  this.lastStatus = newStatus;
40198
40338
  }
40339
+ if (newStatus === "idle" && adapterStatus.fsmReadySeen === true && !this.agentReadyEmitted) {
40340
+ this.emitAgentReadyOnce(chatTitle, now);
40341
+ }
40199
40342
  this.applyProviderResponse(parsedStatus, {
40200
40343
  phase: newStatus === "idle" && (previousStatus === "generating" || previousStatus === "waiting_approval") ? "turn_completed" : "immediate"
40201
40344
  });
@@ -47564,13 +47707,16 @@ var meshCrudHandlers = {
47564
47707
  worktreeBranch: typeof node?.worktreeBranch === "string" ? node.worktreeBranch : void 0,
47565
47708
  worktreeCleanupFallback: typeof worktreeCleanup?.fallback === "string" ? worktreeCleanup.fallback : void 0,
47566
47709
  forced: worktreeCleanup?.forced === true ? true : void 0,
47567
- 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
47568
47713
  }
47569
47714
  });
47570
47715
  } catch {
47571
47716
  }
47572
47717
  }
47573
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;
47574
47720
  const skippedLiveSessionIds = Array.isArray(sessionCleanup?.skippedLiveSessionIds) ? sessionCleanup.skippedLiveSessionIds.filter((v) => typeof v === "string") : [];
47575
47721
  const orphanedSessionsRemaining = skippedLiveSessionIds.length > 0;
47576
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;
@@ -47578,6 +47724,7 @@ var meshCrudHandlers = {
47578
47724
  success: true,
47579
47725
  removed,
47580
47726
  ...residueWarning ? { residueWarning } : {},
47727
+ ...branchRefWarning ? { branchRefWarning } : {},
47581
47728
  ...sessionCleanup ? { sessionCleanup } : {},
47582
47729
  ...worktreeCleanup ? { worktreeCleanup } : {},
47583
47730
  ...orphanedSessionsRemaining ? { orphanedSessionsRemaining: true, nextAction: orphanNextAction } : {}
@@ -52847,16 +52994,50 @@ var DaemonCommandRouter = class {
52847
52994
  recoveryHint: "Inspect the worktree branch and mesh metadata before retrying cleanup."
52848
52995
  };
52849
52996
  }
52850
- const forceFallbackConvergence = args.force ? { allow: true, status: "force_override", source: "caller_force_flag" } : await this.getWorktreeForceCleanupConvergence({ repoRoot, workspace, node: args.node });
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
+ };
52851
53030
  try {
52852
53031
  const result = await removeWorktree2(repoRoot, workspace, {
52853
53032
  requireClean: !args.force,
52854
53033
  allowSubmoduleForceFallback: forceFallbackConvergence.allow
52855
53034
  });
53035
+ const branchOutcome = await deleteBranchIfMerged();
52856
53036
  return {
52857
53037
  success: true,
52858
53038
  removedPath: result.removedPath,
52859
53039
  repoRoot,
53040
+ ...branchOutcome,
52860
53041
  ...result.fallback ? {
52861
53042
  fallback: result.fallback,
52862
53043
  forced: result.forced,
@@ -52889,10 +53070,12 @@ var DaemonCommandRouter = class {
52889
53070
  maxBuffer: GIT_MAX_BUFFER_CLEANUP,
52890
53071
  windowsHide: true
52891
53072
  });
53073
+ const branchOutcome = await deleteBranchIfMerged();
52892
53074
  return {
52893
53075
  success: true,
52894
53076
  removedPath: workspace,
52895
53077
  repoRoot,
53078
+ ...branchOutcome,
52896
53079
  fallback: "git_worktree_remove_submodule_deinit",
52897
53080
  forced: true,
52898
53081
  reason: "working_trees_containing_submodules",
@@ -52910,10 +53093,12 @@ var DaemonCommandRouter = class {
52910
53093
  });
52911
53094
  } catch {
52912
53095
  }
53096
+ const branchOutcome = await deleteBranchIfMerged();
52913
53097
  return {
52914
53098
  success: true,
52915
53099
  removedPath: workspace,
52916
53100
  repoRoot,
53101
+ ...branchOutcome,
52917
53102
  fallback: "fs_rm_worktree_prune",
52918
53103
  forced: true,
52919
53104
  reason: "working_trees_containing_submodules",