mixdog 0.9.26 → 0.9.28

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.
Files changed (36) hide show
  1. package/package.json +7 -2
  2. package/scripts/arg-guard-test.mjs +68 -0
  3. package/scripts/compacted-placeholder-scrub-test.mjs +77 -0
  4. package/src/app.mjs +14 -0
  5. package/src/cli.mjs +40 -4
  6. package/src/rules/shared/01-tool.md +3 -0
  7. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +3 -1
  8. package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +30 -0
  9. package/src/runtime/agent/orchestrator/session/loop/transcript-repair.mjs +5 -0
  10. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +14 -1
  11. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +1 -1
  12. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +1 -1
  13. package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +1 -1
  14. package/src/runtime/memory/tool-defs.mjs +3 -3
  15. package/src/runtime/shared/staged-install-worker.mjs +14 -0
  16. package/src/runtime/shared/staged-update.mjs +530 -0
  17. package/src/runtime/shared/update-checker.mjs +1 -1
  18. package/src/session-runtime/lifecycle-api.mjs +9 -11
  19. package/src/session-runtime/runtime-core.mjs +61 -40
  20. package/src/standalone/agent-tool/tool-def.mjs +2 -2
  21. package/src/tui/App.jsx +2 -1
  22. package/src/tui/app/transcript-window.mjs +26 -6
  23. package/src/tui/components/Spinner.jsx +5 -2
  24. package/src/tui/components/StatusLine.jsx +9 -9
  25. package/src/tui/components/ToolExecution.jsx +34 -59
  26. package/src/tui/display-width.mjs +8 -4
  27. package/src/tui/dist/index.mjs +545 -425
  28. package/src/tui/engine/notification-plan.mjs +5 -1
  29. package/src/tui/hooks/useSharedTick.mjs +103 -0
  30. package/src/tui/index.jsx +2 -1
  31. package/src/tui/markdown/format-token.mjs +46 -8
  32. package/src/tui/markdown/render-ansi.mjs +24 -1
  33. package/src/tui/markdown/stream-fence.mjs +60 -0
  34. package/src/tui/markdown/streaming-markdown.mjs +22 -1
  35. package/src/workflows/default/WORKFLOW.md +12 -8
  36. package/vendor/ink/build/display-width.js +5 -4
@@ -156,14 +156,14 @@ var require_mixdog_debug = __commonJS({
156
156
  // src/tui/index.jsx
157
157
  import React21 from "react";
158
158
  import { render } from "../../../vendor/ink/build/index.js";
159
- import { closeSync as closeSync2, constants as fsConstants, createWriteStream, mkdirSync as mkdirSync6, openSync as openSync2, readSync } from "node:fs";
159
+ import { closeSync as closeSync2, constants as fsConstants, createWriteStream, mkdirSync as mkdirSync7, openSync as openSync2, readSync } from "node:fs";
160
160
  import { tmpdir as tmpdir2 } from "node:os";
161
161
  import { dirname as dirname9, join as join10 } from "node:path";
162
162
  import { performance as performance3 } from "node:perf_hooks";
163
163
  import { format } from "node:util";
164
164
 
165
165
  // src/tui/App.jsx
166
- import React20, { useState as useState8, useCallback as useCallback6, useEffect as useEffect10, useLayoutEffect as useLayoutEffect4, useMemo as useMemo2, useRef as useRef9 } from "react";
166
+ import React20, { useState as useState8, useCallback as useCallback6, useEffect as useEffect10, useLayoutEffect as useLayoutEffect4, useMemo as useMemo2, useRef as useRef10 } from "react";
167
167
  import { Box as Box18, Text as Text20, useApp, useInput as useInput5, useStdin as useStdin3, useStdout as useStdout2 } from "../../../vendor/ink/build/index.js";
168
168
 
169
169
  // src/tui/themes/base.mjs
@@ -3099,9 +3099,136 @@ function formatAggregateDetail(summaries) {
3099
3099
  return order.map((item) => item.type === "metric" ? metrics.get(item.key)?.render(metrics.get(item.key)) : item.text).filter(Boolean).join(", ");
3100
3100
  }
3101
3101
 
3102
+ // src/runtime/shared/update-checker.mjs
3103
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3104
+ import { dirname as dirname2, join as join2 } from "node:path";
3105
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
3106
+
3107
+ // src/runtime/shared/plugin-paths.mjs
3108
+ import { homedir } from "os";
3109
+ import { dirname, join } from "path";
3110
+ import { fileURLToPath } from "url";
3111
+ var DEFAULT_ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
3112
+ function mixdogHome() {
3113
+ return process.env.MIXDOG_HOME || join(homedir(), ".mixdog");
3114
+ }
3115
+ function resolvePluginData() {
3116
+ return process.env.MIXDOG_DATA_DIR || join(mixdogHome(), "data");
3117
+ }
3118
+
3119
+ // src/runtime/shared/spawn-flags.mjs
3120
+ var isWin = process.platform === "win32";
3121
+ var detachedSpawnOpts = Object.freeze({
3122
+ windowsHide: true,
3123
+ ...isWin ? {} : { detached: true }
3124
+ });
3125
+ var hiddenSpawnOpts = Object.freeze({
3126
+ windowsHide: true
3127
+ });
3128
+
3129
+ // src/runtime/shared/update-checker.mjs
3130
+ var PACKAGE_NAME = "mixdog";
3131
+ var REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
3132
+ var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
3133
+ var _MODULE_DIR = dirname2(fileURLToPath2(import.meta.url));
3134
+ var _PACKAGE_JSON_PATH = join2(_MODULE_DIR, "..", "..", "..", "package.json");
3135
+ var _PACKAGE_ROOT = dirname2(_PACKAGE_JSON_PATH);
3136
+ var _localVersionCache = null;
3137
+ function localPackageVersion() {
3138
+ if (_localVersionCache) return _localVersionCache;
3139
+ try {
3140
+ const raw = JSON.parse(readFileSync(_PACKAGE_JSON_PATH, "utf8"));
3141
+ _localVersionCache = String(raw?.version || "0.0.0");
3142
+ } catch {
3143
+ _localVersionCache = "0.0.0";
3144
+ }
3145
+ return _localVersionCache;
3146
+ }
3147
+ function parseSemver(value) {
3148
+ const text = String(value || "").trim().replace(/^v/i, "");
3149
+ const [core, ...preParts] = text.split("-");
3150
+ const parts = core.split(".").map((n) => {
3151
+ const num2 = Number.parseInt(n, 10);
3152
+ return Number.isFinite(num2) ? num2 : 0;
3153
+ });
3154
+ while (parts.length < 3) parts.push(0);
3155
+ return { parts, prerelease: preParts.join("-") };
3156
+ }
3157
+ function compareSemver(a, b) {
3158
+ const pa = parseSemver(a);
3159
+ const pb = parseSemver(b);
3160
+ for (let i = 0; i < 3; i++) {
3161
+ const diff = (pa.parts[i] || 0) - (pb.parts[i] || 0);
3162
+ if (diff !== 0) return diff;
3163
+ }
3164
+ if (pa.prerelease && !pb.prerelease) return -1;
3165
+ if (!pa.prerelease && pb.prerelease) return 1;
3166
+ if (pa.prerelease !== pb.prerelease) return pa.prerelease < pb.prerelease ? -1 : 1;
3167
+ return 0;
3168
+ }
3169
+
3102
3170
  // src/tui/components/Spinner.jsx
3103
- import React, { useRef } from "react";
3104
- import { Box, Text, useAnimation } from "../../../vendor/ink/build/index.js";
3171
+ import React, { useRef as useRef2 } from "react";
3172
+ import { Box, Text } from "../../../vendor/ink/build/index.js";
3173
+
3174
+ // src/tui/hooks/useSharedTick.mjs
3175
+ import { useEffect, useRef, useState } from "react";
3176
+ var TICK_SLOP_MS = 16;
3177
+ var subscribers = /* @__PURE__ */ new Set();
3178
+ var timer = null;
3179
+ function nextDelayMs(now) {
3180
+ let soonest = Infinity;
3181
+ for (const sub of subscribers) {
3182
+ const due = sub.lastFire + sub.interval - now;
3183
+ if (due < soonest) soonest = due;
3184
+ }
3185
+ return Number.isFinite(soonest) ? Math.max(1, soonest) : null;
3186
+ }
3187
+ function reconcileTimer() {
3188
+ if (timer) {
3189
+ clearTimeout(timer);
3190
+ timer = null;
3191
+ }
3192
+ const delay = nextDelayMs(Date.now());
3193
+ if (delay == null) return;
3194
+ timer = setTimeout(onTick, delay);
3195
+ timer.unref?.();
3196
+ }
3197
+ function onTick() {
3198
+ timer = null;
3199
+ const now = Date.now();
3200
+ for (const sub of Array.from(subscribers)) {
3201
+ if (!subscribers.has(sub)) continue;
3202
+ if (now - sub.lastFire >= sub.interval - TICK_SLOP_MS) {
3203
+ sub.lastFire = now;
3204
+ sub.notify(now);
3205
+ }
3206
+ }
3207
+ reconcileTimer();
3208
+ }
3209
+ function useSharedTick(intervalMs, isActive = true, onTick2 = null) {
3210
+ const [, setTick] = useState(0);
3211
+ const cbRef = useRef(onTick2);
3212
+ cbRef.current = onTick2;
3213
+ useEffect(() => {
3214
+ if (!isActive) return void 0;
3215
+ const interval = Math.max(1, Number(intervalMs) || 0);
3216
+ const sub = {
3217
+ interval,
3218
+ lastFire: Date.now(),
3219
+ notify: (now) => {
3220
+ if (cbRef.current) cbRef.current(now);
3221
+ else setTick((t) => (t + 1) % 1e6);
3222
+ }
3223
+ };
3224
+ subscribers.add(sub);
3225
+ reconcileTimer();
3226
+ return () => {
3227
+ subscribers.delete(sub);
3228
+ reconcileTimer();
3229
+ };
3230
+ }, [intervalMs, isActive]);
3231
+ }
3105
3232
 
3106
3233
  // src/tui/spinner-verbs.mjs
3107
3234
  var SPINNER_VERBS = [
@@ -3438,19 +3565,19 @@ function tokenModeGlyph(mode) {
3438
3565
  }
3439
3566
  }
3440
3567
  function Spinner({ verb = "Working", startedAt, outputTokens = 0, tokens = 0, thinking = false, thinkingActiveSince = 0, mode = "responding", columns = 80, marginTop = 1 }) {
3441
- useAnimation({ interval: FRAME_MS });
3568
+ useSharedTick(FRAME_MS);
3442
3569
  const { TEXT_RGB, SHIMMER_RGB, SPINNER_GLYPH_RGB, THINKING_INACTIVE, THINKING_SHIMMER, STALL_RGB } = spinnerRgb();
3443
3570
  const now = Date.now();
3444
3571
  const elapsedMs = startedAt ? Math.max(0, now - startedAt) : 0;
3445
3572
  const frame = Math.floor(elapsedMs / FRAME_MS);
3446
- const lastGrowRef = useRef(now);
3447
- const lastTokensRef = useRef(0);
3448
- const displayedOutputRef = useRef(0);
3449
- const displayVerbRef = useRef("");
3450
- const displayVerbModeRef = useRef("");
3451
- const nextVerbCheckRef = useRef(0);
3452
- const stallSmoothRef = useRef(0);
3453
- const lastStallTickRef = useRef(0);
3573
+ const lastGrowRef = useRef2(now);
3574
+ const lastTokensRef = useRef2(0);
3575
+ const displayedOutputRef = useRef2(0);
3576
+ const displayVerbRef = useRef2("");
3577
+ const displayVerbModeRef = useRef2("");
3578
+ const nextVerbCheckRef = useRef2(0);
3579
+ const stallSmoothRef = useRef2(0);
3580
+ const lastStallTickRef = useRef2(0);
3454
3581
  const targetOutputTokens = Math.max(0, Number(outputTokens || tokens || 0));
3455
3582
  if (targetOutputTokens > lastTokensRef.current) {
3456
3583
  lastTokensRef.current = targetOutputTokens;
@@ -3559,7 +3686,7 @@ function Spinner({ verb = "Working", startedAt, outputTokens = 0, tokens = 0, th
3559
3686
  }
3560
3687
 
3561
3688
  // src/tui/components/StatusLine.jsx
3562
- import React2, { useEffect, useRef as useRef2, useState } from "react";
3689
+ import React2, { useEffect as useEffect2, useRef as useRef3, useState as useState2 } from "react";
3563
3690
  import { Box as Box2, Text as Text2 } from "../../../vendor/ink/build/index.js";
3564
3691
 
3565
3692
  // src/tui/statusline-ansi-bridge.mjs
@@ -3693,11 +3820,11 @@ var statuslineModulePrewarmScheduled = false;
3693
3820
  function scheduleStatuslineModulePrewarm() {
3694
3821
  if (statuslineModulePrewarmScheduled) return;
3695
3822
  statuslineModulePrewarmScheduled = true;
3696
- const timer = setTimeout(() => {
3823
+ const timer2 = setTimeout(() => {
3697
3824
  loadStatuslineModule().catch(() => {
3698
3825
  });
3699
3826
  }, STATUSLINE_MODULE_PREWARM_DELAY_MS);
3700
- timer.unref?.();
3827
+ timer2.unref?.();
3701
3828
  }
3702
3829
  var STATUSLINE_BOOT_FULL_DELAY_MS = 1e3;
3703
3830
  var STATUSLINE_BOOT_FULL_DELAY_ACTIVE_MS = 2200;
@@ -4006,7 +4133,7 @@ function workflowModeLabel(workflow = {}, remoteEnabled = false) {
4006
4133
  return remoteEnabled === true ? `Remote / ${base}` : base;
4007
4134
  }
4008
4135
  function StatusLineView({ sessionId, clientHostPid, provider, model, effort, fast, cwd, stats, contextWindow, displayContextWindow = 0, compactBoundaryTokens = 0, autoCompactTokenLimit = 0, rawContextWindow, resizeEpoch, agentRevision = "", agentWorkers = [], agentJobs = [], activeTools = null, initialLine = "", workflow = null, remoteEnabled = false, themeEpoch = 0 }) {
4009
- const [line, setLine] = useState(() => normalizeStatusLine(initialLine || localBootStatusLine({
4136
+ const [line, setLine] = useState2(() => normalizeStatusLine(initialLine || localBootStatusLine({
4010
4137
  provider,
4011
4138
  model,
4012
4139
  effort,
@@ -4021,19 +4148,19 @@ function StatusLineView({ sessionId, clientHostPid, provider, model, effort, fas
4021
4148
  agentJobs,
4022
4149
  activeTools
4023
4150
  })));
4024
- const [refreshTick, setRefreshTick] = useState(0);
4025
- const statuslineArgsRef = useRef2(null);
4026
- const bootFullDoneRef = useRef2(false);
4027
- const mountAtRef = useRef2(Date.now());
4028
- const lineRef = useRef2("");
4029
- const bootFullNextAttemptAtRef = useRef2(0);
4030
- const bootFullRetryBackoffMsRef = useRef2(STATUSLINE_BOOT_FULL_RETRY_MS);
4031
- const renderEffectIdRef = useRef2(0);
4032
- const lastImmediateArgsRef = useRef2(null);
4033
- const themeEpochRef = useRef2(themeEpoch);
4034
- const lastRawFullLineRef = useRef2("");
4035
- const lastRawFullLineCacheKeyRef = useRef2("");
4036
- const lastRawFullLineAtRef = useRef2(0);
4151
+ const [refreshTick, setRefreshTick] = useState2(0);
4152
+ const statuslineArgsRef = useRef3(null);
4153
+ const bootFullDoneRef = useRef3(false);
4154
+ const mountAtRef = useRef3(Date.now());
4155
+ const lineRef = useRef3("");
4156
+ const bootFullNextAttemptAtRef = useRef3(0);
4157
+ const bootFullRetryBackoffMsRef = useRef3(STATUSLINE_BOOT_FULL_RETRY_MS);
4158
+ const renderEffectIdRef = useRef3(0);
4159
+ const lastImmediateArgsRef = useRef3(null);
4160
+ const themeEpochRef = useRef3(themeEpoch);
4161
+ const lastRawFullLineRef = useRef3("");
4162
+ const lastRawFullLineCacheKeyRef = useRef3("");
4163
+ const lastRawFullLineAtRef = useRef3(0);
4037
4164
  const statuslineArgs = {
4038
4165
  sessionId,
4039
4166
  clientHostPid,
@@ -4074,17 +4201,13 @@ function StatusLineView({ sessionId, clientHostPid, provider, model, effort, fas
4074
4201
  String(statsForSignature.currentContextSource || "")
4075
4202
  ].join("|");
4076
4203
  const refreshMs = hasActiveStatuslineWork(line, agentWorkers, agentJobs, activeTools) ? STATUSLINE_ACTIVE_REFRESH_MS : STATUSLINE_REFRESH_MS;
4077
- useEffect(() => {
4204
+ useEffect2(() => {
4078
4205
  scheduleStatuslineModulePrewarm();
4079
4206
  }, []);
4080
- useEffect(() => {
4081
- const timer = setInterval(() => {
4082
- setRefreshTick((tick) => (tick + 1) % 1e6);
4083
- }, refreshMs);
4084
- timer.unref?.();
4085
- return () => clearInterval(timer);
4086
- }, [refreshMs]);
4087
- useEffect(() => {
4207
+ useSharedTick(refreshMs, true, () => {
4208
+ setRefreshTick((tick) => (tick + 1) % 1e6);
4209
+ });
4210
+ useEffect2(() => {
4088
4211
  let alive = true;
4089
4212
  let bootRetryTimer = null;
4090
4213
  const effectId = renderEffectIdRef.current + 1;
@@ -4146,7 +4269,7 @@ ${grafted}` : cachedL1;
4146
4269
  autoCompactTokenLimit: args.autoCompactTokenLimit,
4147
4270
  stats: args.stats
4148
4271
  };
4149
- const timer = setTimeout(() => {
4272
+ const timer2 = setTimeout(() => {
4150
4273
  if (bootFullDoneRef.current !== true) {
4151
4274
  if (!bootFullRenderEligible(mountAtRef.current, lineRef.current, args.agentWorkers, args.agentJobs, args.activeTools)) {
4152
4275
  const active = hasActiveStatuslineWork(lineRef.current, args.agentWorkers, args.agentJobs, args.activeTools);
@@ -4192,10 +4315,10 @@ ${grafted}` : cachedL1;
4192
4315
  resetStatuslineModuleLoad();
4193
4316
  });
4194
4317
  }, STATUSLINE_RENDER_DEBOUNCE_MS);
4195
- timer.unref?.();
4318
+ timer2.unref?.();
4196
4319
  return () => {
4197
4320
  alive = false;
4198
- clearTimeout(timer);
4321
+ clearTimeout(timer2);
4199
4322
  clearTimeout(bootRetryTimer);
4200
4323
  };
4201
4324
  }, [sessionId, clientHostPid, provider, model, effort, fast, cwd, statsSignature, contextWindow, displayContextWindow, compactBoundaryTokens, autoCompactTokenLimit, rawContextWindow, resizeEpoch, agentRevision, agentWorkersSignature, agentJobsSignature, activeToolsSignature, refreshTick, themeEpoch]);
@@ -4203,25 +4326,25 @@ ${grafted}` : cachedL1;
4203
4326
  const workflowLabel = workflowModeLabel(workflow, remoteEnabled);
4204
4327
  return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: "100%", height: 3, overflow: "hidden", justifyContent: "flex-start", paddingLeft: 2, backgroundColor: surfaceBackground(), children: [
4205
4328
  /* @__PURE__ */ jsxs2(Box2, { flexDirection: "row", width: "100%", overflow: "hidden", children: [
4206
- /* @__PURE__ */ jsx2(Box2, { flexGrow: 1, flexShrink: 1, overflow: "hidden", children: /* @__PURE__ */ jsx2(Text2, { wrap: "truncate", children: lines[0] || " " }) }),
4329
+ /* @__PURE__ */ jsx2(Box2, { flexGrow: 1, flexShrink: 1, flexBasis: 0, overflow: "hidden", children: /* @__PURE__ */ jsx2(Text2, { wrap: "truncate", children: lines[0] || " " }) }),
4207
4330
  /* @__PURE__ */ jsx2(Box2, { flexShrink: 0, marginLeft: 1, marginRight: 1, children: /* @__PURE__ */ jsx2(Text2, { color: theme.statusText, wrap: "truncate", children: workflowLabel }) })
4208
4331
  ] }),
4209
- /* @__PURE__ */ jsx2(Box2, { flexDirection: "row", width: "100%", overflow: "hidden", children: /* @__PURE__ */ jsx2(Box2, { flexGrow: 1, flexShrink: 1, overflow: "hidden", children: /* @__PURE__ */ jsx2(Text2, { wrap: "truncate", children: lines[1] || " " }) }) })
4332
+ /* @__PURE__ */ jsx2(Box2, { flexDirection: "row", width: "100%", overflow: "hidden", children: /* @__PURE__ */ jsx2(Box2, { flexGrow: 1, flexShrink: 1, flexBasis: 0, overflow: "hidden", children: /* @__PURE__ */ jsx2(Text2, { wrap: "truncate", children: lines[1] || " " }) }) })
4210
4333
  ] });
4211
4334
  }
4212
4335
  var StatusLine = React2.memo(StatusLineView);
4213
4336
 
4214
4337
  // src/tui/components/PromptInput.jsx
4215
- import React3, { useEffect as useEffect2, useLayoutEffect, useState as useState2, useRef as useRef3 } from "react";
4338
+ import React3, { useEffect as useEffect3, useLayoutEffect, useState as useState3, useRef as useRef4 } from "react";
4216
4339
  import { Box as Box3, Text as Text3, useInput, usePaste, useStdin } from "../../../vendor/ink/build/index.js";
4217
4340
 
4218
4341
  // src/tui/display-width.mjs
4219
4342
  import stringWidth from "string-width";
4220
4343
  var graphemeSegmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
4221
4344
  function isProblemCodePoint(cp) {
4222
- return cp >= 9312 && cp <= 9471 || cp >= 8592 && cp <= 8703;
4345
+ return cp >= 9312 && cp <= 9471 || cp >= 8596 && cp <= 8703;
4223
4346
  }
4224
- var PROBLEM_RE = /[\u2190-\u21ff\u2460-\u24ff]/;
4347
+ var PROBLEM_RE = /[\u2194-\u21ff\u2460-\u24ff]/;
4225
4348
  function resolveAmbiguousWidePolicy(env = process.env, platform = process.platform) {
4226
4349
  const override = env?.MIXDOG_TUI_AMBIGUOUS_WIDE;
4227
4350
  if (override === "1") return true;
@@ -4577,29 +4700,29 @@ function PromptInput({
4577
4700
  mouseSelectionRef,
4578
4701
  suppressShiftNavRef
4579
4702
  }) {
4580
- const [draft, setDraft] = useState2(() => {
4703
+ const [draft, setDraft] = useState3(() => {
4581
4704
  const value2 = String(initialValue || "");
4582
4705
  return { value: value2, cursor: value2.length, selectionAnchor: null };
4583
4706
  });
4584
- const [, bumpCursorAnchorEpoch] = useState2(0);
4585
- const draftRef = useRef3(draft);
4586
- const lastReportedValueRef = useRef3(draft.value);
4587
- const pasteGenerationRef = useRef3(0);
4707
+ const [, bumpCursorAnchorEpoch] = useState3(0);
4708
+ const draftRef = useRef4(draft);
4709
+ const lastReportedValueRef = useRef4(draft.value);
4710
+ const pasteGenerationRef = useRef4(0);
4588
4711
  if (valueRef) valueRef.current = draftRef.current.value;
4589
4712
  const { isRawModeSupported } = useStdin();
4590
- const boxRef = useRef3(null);
4591
- const cursorEnabledRef = useRef3(false);
4592
- const contentWidthRef = useRef3(80);
4593
- const preferredColumnRef = useRef3(null);
4594
- const mouseExtendCoalesceRef = useRef3({ pendingNext: null, timer: null, t: 0 });
4595
- const undoRef = useRef3({ past: [], future: [], lastPushAt: 0, lastValue: null });
4713
+ const boxRef = useRef4(null);
4714
+ const cursorEnabledRef = useRef4(false);
4715
+ const contentWidthRef = useRef4(80);
4716
+ const preferredColumnRef = useRef4(null);
4717
+ const mouseExtendCoalesceRef = useRef4({ pendingNext: null, timer: null, t: 0 });
4718
+ const undoRef = useRef4({ past: [], future: [], lastPushAt: 0, lastValue: null });
4596
4719
  const { value, cursor } = draft;
4597
4720
  draftRef.current = draft;
4598
4721
  if (selectionRef) {
4599
4722
  const range = selectionRange(draft);
4600
4723
  selectionRef.current = range ? { range, text: mask ? "" : draft.value.slice(range.start, range.end) } : null;
4601
4724
  }
4602
- const flushThrottleRef = useRef3({ lastAt: 0, timer: null });
4725
+ const flushThrottleRef = useRef4({ lastAt: 0, timer: null });
4603
4726
  const flushImmediate = () => {
4604
4727
  let node = boxRef.current;
4605
4728
  for (let i = 0; node && i < 64; i++) {
@@ -4816,7 +4939,7 @@ function PromptInput({
4816
4939
  }
4817
4940
  };
4818
4941
  }
4819
- useEffect2(() => () => {
4942
+ useEffect3(() => () => {
4820
4943
  const state = mouseExtendCoalesceRef.current;
4821
4944
  if (state.timer) clearTimeout(state.timer);
4822
4945
  state.timer = null;
@@ -4879,13 +5002,13 @@ function PromptInput({
4879
5002
  apply(handled);
4880
5003
  }
4881
5004
  };
4882
- useEffect2(() => {
5005
+ useEffect3(() => {
4883
5006
  if (!draftOverride || typeof draftOverride.value !== "string") return;
4884
5007
  pasteGenerationRef.current += 1;
4885
5008
  commitDraft({ value: draftOverride.value, cursor: draftOverride.value.length, selectionAnchor: null }, { skipHistory: true });
4886
5009
  resetUndo();
4887
5010
  }, [draftOverride?.id]);
4888
- useEffect2(() => () => {
5011
+ useEffect3(() => () => {
4889
5012
  pasteGenerationRef.current += 1;
4890
5013
  if (selectionRef) selectionRef.current = null;
4891
5014
  }, [selectionRef]);
@@ -5270,7 +5393,7 @@ function QueuedCommands({ queued, columns, compact = false }) {
5270
5393
  }
5271
5394
 
5272
5395
  // src/tui/components/Picker.jsx
5273
- import React6, { useState as useState3, useCallback, useEffect as useEffect3, useRef as useRef4 } from "react";
5396
+ import React6, { useState as useState4, useCallback, useEffect as useEffect4, useRef as useRef5 } from "react";
5274
5397
  import { Box as Box6, Text as Text6, useInput as useInput2 } from "../../../vendor/ink/build/index.js";
5275
5398
  import stringWidth2 from "string-width";
5276
5399
 
@@ -5388,20 +5511,20 @@ function Picker({
5388
5511
  themeEpoch = 0
5389
5512
  }) {
5390
5513
  const visibleLimit = Math.max(1, Math.floor(Number(visibleCount) || MAX_VISIBLE));
5391
- const [selectedIndex, setSelectedIndex] = useState3(() => Math.max(0, Math.min(Number(initialIndex) || 0, Math.max(0, items.length - 1))));
5514
+ const [selectedIndex, setSelectedIndex] = useState4(() => Math.max(0, Math.min(Number(initialIndex) || 0, Math.max(0, items.length - 1))));
5392
5515
  const confirmButtons = Array.isArray(confirmBar?.buttons) ? confirmBar.buttons.filter(Boolean) : [];
5393
5516
  const hasConfirm = confirmButtons.length > 0;
5394
- const [confirmFocus, setConfirmFocus] = useState3(-1);
5395
- const lastTabAtRef = useRef4(0);
5396
- useEffect3(() => {
5517
+ const [confirmFocus, setConfirmFocus] = useState4(-1);
5518
+ const lastTabAtRef = useRef5(0);
5519
+ useEffect4(() => {
5397
5520
  setConfirmFocus(-1);
5398
5521
  }, [confirmButtons.length, confirmBar]);
5399
- const [selectionMemo] = useState3(() => ({ value: null, initialIndex }));
5400
- useEffect3(() => {
5522
+ const [selectionMemo] = useState4(() => ({ value: null, initialIndex }));
5523
+ useEffect4(() => {
5401
5524
  const item = items[selectedIndex];
5402
5525
  if (item && item.value != null) selectionMemo.value = item.value;
5403
5526
  }, [items, selectedIndex, selectionMemo]);
5404
- useEffect3(() => {
5527
+ useEffect4(() => {
5405
5528
  setSelectedIndex((i) => {
5406
5529
  if (selectionMemo.value != null) {
5407
5530
  const found = items.findIndex((entry) => entry && entry.value === selectionMemo.value);
@@ -5412,7 +5535,7 @@ function Picker({
5412
5535
  return Math.min(Math.max(0, i), Math.max(0, items.length - 1));
5413
5536
  });
5414
5537
  }, [items, selectionMemo, initialIndex]);
5415
- useEffect3(() => {
5538
+ useEffect4(() => {
5416
5539
  if (initialIndex == null) {
5417
5540
  selectionMemo.initialIndex = null;
5418
5541
  return;
@@ -5421,7 +5544,7 @@ function Picker({
5421
5544
  selectionMemo.initialIndex = initialIndex;
5422
5545
  setSelectedIndex(Math.max(0, Math.min(Number(initialIndex) || 0, Math.max(0, items.length - 1))));
5423
5546
  }, [initialIndex, items.length, selectionMemo]);
5424
- useEffect3(() => {
5547
+ useEffect4(() => {
5425
5548
  if (typeof onHighlight !== "function") return;
5426
5549
  const item = items[selectedIndex];
5427
5550
  if (item) onHighlight(item.value, item, selectedIndex);
@@ -6048,7 +6171,7 @@ function ContextPanel({ rows, title = "Context Usage", columns = 80, fillHeight
6048
6171
  }
6049
6172
 
6050
6173
  // src/tui/components/UsagePanel.jsx
6051
- import React9, { useEffect as useEffect4, useState as useState4 } from "react";
6174
+ import React9, { useEffect as useEffect5, useState as useState5 } from "react";
6052
6175
  import { Box as Box9, Text as Text9, useInput as useInput3 } from "../../../vendor/ink/build/index.js";
6053
6176
  import stringWidth5 from "string-width";
6054
6177
  import { Fragment as Fragment4, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
@@ -6265,11 +6388,11 @@ function UsagePanel({ dashboard, loading = false, columns = 80, fillHeight = fal
6265
6388
  const hasMeasuredRows = Number(panelRows) > 0;
6266
6389
  const maxProviderRows = hasMeasuredRows ? Math.max(1, Math.floor(panelRows) - 6) : rows.length;
6267
6390
  const maxScrollOffset = Math.max(0, rows.length - maxProviderRows);
6268
- const [scrollOffset, setScrollOffset] = useState4(0);
6391
+ const [scrollOffset, setScrollOffset] = useState5(0);
6269
6392
  const visibleRows = hasMeasuredRows ? rows.slice(scrollOffset, scrollOffset + maxProviderRows) : rows;
6270
6393
  const scrollable = rows.length > maxProviderRows;
6271
6394
  const helpText = `${scrollable || isLoading || isChecking ? "\u2191/\u2193 Scroll \xB7 PgUp/PgDn Page \xB7 " : ""}Esc Back \xB7 /usage refresh`;
6272
- useEffect4(() => {
6395
+ useEffect5(() => {
6273
6396
  setScrollOffset((offset) => Math.min(Math.max(0, offset), maxScrollOffset));
6274
6397
  }, [maxScrollOffset]);
6275
6398
  useInput3((input, key) => {
@@ -6342,7 +6465,7 @@ function UsagePanel({ dashboard, loading = false, columns = 80, fillHeight = fal
6342
6465
  }
6343
6466
 
6344
6467
  // src/tui/components/TextEntryPanel.jsx
6345
- import React10, { useEffect as useEffect5, useLayoutEffect as useLayoutEffect2, useRef as useRef5, useState as useState5 } from "react";
6468
+ import React10, { useEffect as useEffect6, useLayoutEffect as useLayoutEffect2, useRef as useRef6, useState as useState6 } from "react";
6346
6469
  import { Box as Box10, Text as Text10, useInput as useInput4, usePaste as usePaste2, useStdin as useStdin2 } from "../../../vendor/ink/build/index.js";
6347
6470
  import stringWidth6 from "string-width";
6348
6471
 
@@ -6555,13 +6678,13 @@ function TextEntryPanel({
6555
6678
  onSubmit,
6556
6679
  onCancel
6557
6680
  }) {
6558
- const [draft, setDraft] = useState5(() => ({ value: String(initialValue || ""), cursor: String(initialValue || "").length, selectionAnchor: null }));
6559
- const [, bumpCursorAnchorEpoch] = useState5(0);
6560
- const draftRef = useRef5(draft);
6561
- const boxRef = useRef5(null);
6562
- const cursorEnabledRef = useRef5(false);
6563
- const contentWidthRef = useRef5(80);
6564
- const preferredColumnRef = useRef5(null);
6681
+ const [draft, setDraft] = useState6(() => ({ value: String(initialValue || ""), cursor: String(initialValue || "").length, selectionAnchor: null }));
6682
+ const [, bumpCursorAnchorEpoch] = useState6(0);
6683
+ const draftRef = useRef6(draft);
6684
+ const boxRef = useRef6(null);
6685
+ const cursorEnabledRef = useRef6(false);
6686
+ const contentWidthRef = useRef6(80);
6687
+ const preferredColumnRef = useRef6(null);
6565
6688
  const { isRawModeSupported } = useStdin2();
6566
6689
  draftRef.current = draft;
6567
6690
  const flushImmediate = () => {
@@ -6597,13 +6720,13 @@ function TextEntryPanel({
6597
6720
  commitDraft(moveCursor(current, moved.cursor, { extend }), { keepPreferredColumn: true });
6598
6721
  return true;
6599
6722
  };
6600
- useEffect5(() => {
6723
+ useEffect6(() => {
6601
6724
  const value = String(initialValue || "");
6602
6725
  commitDraft({ value, cursor: value.length, selectionAnchor: null });
6603
6726
  }, [title, initialValue]);
6604
6727
  const labelCells = stringWidth6(String(promptLabel || ""));
6605
6728
  const contentCells = Math.max(1, columns - 4 - labelCells);
6606
- useEffect5(() => {
6729
+ useEffect6(() => {
6607
6730
  if (!multiline) {
6608
6731
  onContentRowsChange?.(1);
6609
6732
  return;
@@ -6876,7 +6999,7 @@ function TextEntryPanel({
6876
6999
  import { execFile } from "node:child_process";
6877
7000
  import { Buffer as Buffer2 } from "node:buffer";
6878
7001
  import { randomBytes } from "node:crypto";
6879
- import { existsSync, unlinkSync } from "node:fs";
7002
+ import { existsSync as existsSync2, unlinkSync } from "node:fs";
6880
7003
  import { readFile as readFileAsync, stat as statAsync } from "node:fs/promises";
6881
7004
  import { tmpdir } from "node:os";
6882
7005
  import { basename, extname, isAbsolute, resolve } from "node:path";
@@ -7158,7 +7281,7 @@ async function readClipboardImageToTempFile() {
7158
7281
  const r = await execFileBuffer("powershell.exe", ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", ps], {
7159
7282
  env: { ...process.env, MIXDOG_CLIPBOARD_IMAGE_PATH: out }
7160
7283
  });
7161
- return r.ok && existsSync(out) ? out : null;
7284
+ return r.ok && existsSync2(out) ? out : null;
7162
7285
  }
7163
7286
  if (process.platform === "darwin") {
7164
7287
  const script = `set png_data to (the clipboard as \xABclass PNGf\xBB)
@@ -7166,7 +7289,7 @@ set fp to open for access POSIX file "${out.replace(/"/g, '\\"')}" with write pe
7166
7289
  write png_data to fp
7167
7290
  close access fp`;
7168
7291
  const r = await execFileBuffer("osascript", ["-e", script]);
7169
- return r.ok && existsSync(out) ? out : null;
7292
+ return r.ok && existsSync2(out) ? out : null;
7170
7293
  }
7171
7294
  return null;
7172
7295
  }
@@ -7216,11 +7339,11 @@ async function readClipboardText() {
7216
7339
  }
7217
7340
 
7218
7341
  // src/standalone/projects.mjs
7219
- import { homedir } from "node:os";
7220
- import { existsSync as existsSync2, mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from "node:fs";
7221
- import { basename as basename2, dirname, isAbsolute as isAbsolute2, join, resolve as resolve2 } from "node:path";
7222
- var MIXDOG_HOME = process.env.MIXDOG_HOME || join(homedir(), ".mixdog");
7223
- var PROJECTS_FILE = join(MIXDOG_HOME, "projects.json");
7342
+ import { homedir as homedir2 } from "node:os";
7343
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync, statSync, writeFileSync as writeFileSync2 } from "node:fs";
7344
+ import { basename as basename2, dirname as dirname3, isAbsolute as isAbsolute2, join as join3, resolve as resolve2 } from "node:path";
7345
+ var MIXDOG_HOME = process.env.MIXDOG_HOME || join3(homedir2(), ".mixdog");
7346
+ var PROJECTS_FILE = join3(MIXDOG_HOME, "projects.json");
7224
7347
  function toAbsolute(rawPath) {
7225
7348
  const text = String(rawPath || "").trim();
7226
7349
  if (!text) return "";
@@ -7232,8 +7355,8 @@ function normalizeKey(absPath) {
7232
7355
  }
7233
7356
  function readStore() {
7234
7357
  try {
7235
- if (!existsSync2(PROJECTS_FILE)) return { projects: [] };
7236
- const raw = readFileSync(PROJECTS_FILE, "utf8");
7358
+ if (!existsSync3(PROJECTS_FILE)) return { projects: [] };
7359
+ const raw = readFileSync2(PROJECTS_FILE, "utf8");
7237
7360
  const parsed = JSON.parse(raw);
7238
7361
  const projects = Array.isArray(parsed?.projects) ? parsed.projects : [];
7239
7362
  return {
@@ -7250,9 +7373,9 @@ function readStore() {
7250
7373
  }
7251
7374
  function writeStore(store) {
7252
7375
  try {
7253
- mkdirSync(dirname(PROJECTS_FILE), { recursive: true });
7376
+ mkdirSync2(dirname3(PROJECTS_FILE), { recursive: true });
7254
7377
  const tmp = `${PROJECTS_FILE}.${process.pid}.tmp`;
7255
- writeFileSync(tmp, `${JSON.stringify(store, null, 2)}
7378
+ writeFileSync2(tmp, `${JSON.stringify(store, null, 2)}
7256
7379
  `, "utf8");
7257
7380
  renameSync(tmp, PROJECTS_FILE);
7258
7381
  } catch {
@@ -7266,14 +7389,14 @@ function ensureProjectIdMarker(absPath, name) {
7266
7389
  } catch {
7267
7390
  return;
7268
7391
  }
7269
- const markerPath = join(absPath, ".mixdog", "project.id");
7270
- if (existsSync2(markerPath)) return;
7392
+ const markerPath = join3(absPath, ".mixdog", "project.id");
7393
+ if (existsSync3(markerPath)) return;
7271
7394
  const value = String(name || basename2(absPath) || "").trim();
7272
7395
  if (!value || value.toLowerCase() === "common") return;
7273
- const markerDir = join(absPath, ".mixdog");
7274
- mkdirSync(markerDir, { recursive: true });
7396
+ const markerDir = join3(absPath, ".mixdog");
7397
+ mkdirSync2(markerDir, { recursive: true });
7275
7398
  const tmp = `${markerPath}.${process.pid}.tmp`;
7276
- writeFileSync(tmp, `${value}
7399
+ writeFileSync2(tmp, `${value}
7277
7400
  `, "utf8");
7278
7401
  renameSync(tmp, markerPath);
7279
7402
  } catch {
@@ -7341,7 +7464,7 @@ function renameProject(rawPath, nextName) {
7341
7464
  }
7342
7465
  function pathExists(rawPath) {
7343
7466
  const absPath = toAbsolute(rawPath);
7344
- return !!absPath && existsSync2(absPath);
7467
+ return !!absPath && existsSync3(absPath);
7345
7468
  }
7346
7469
  function isDirectory(rawPath) {
7347
7470
  const absPath = toAbsolute(rawPath);
@@ -7356,7 +7479,7 @@ function ensureDir(rawPath) {
7356
7479
  const absPath = toAbsolute(rawPath);
7357
7480
  if (!absPath) return "";
7358
7481
  try {
7359
- mkdirSync(absPath, { recursive: true });
7482
+ mkdirSync2(absPath, { recursive: true });
7360
7483
  return absPath;
7361
7484
  } catch {
7362
7485
  return "";
@@ -7381,7 +7504,7 @@ function runCapture(cmd, args, { timeoutMs = DIALOG_TIMEOUT_MS } = {}) {
7381
7504
  }
7382
7505
  let stdout = "";
7383
7506
  let settled = false;
7384
- const timer = setTimeout(() => {
7507
+ const timer2 = setTimeout(() => {
7385
7508
  if (settled) return;
7386
7509
  settled = true;
7387
7510
  try {
@@ -7390,20 +7513,20 @@ function runCapture(cmd, args, { timeoutMs = DIALOG_TIMEOUT_MS } = {}) {
7390
7513
  }
7391
7514
  resolve5({ ok: false, code: -1, stdout: "", error: new Error("dialog timed out") });
7392
7515
  }, timeoutMs);
7393
- timer.unref?.();
7516
+ timer2.unref?.();
7394
7517
  child.stdout.on("data", (chunk) => {
7395
7518
  stdout += chunk.toString("utf8");
7396
7519
  });
7397
7520
  child.on("error", (error) => {
7398
7521
  if (settled) return;
7399
7522
  settled = true;
7400
- clearTimeout(timer);
7523
+ clearTimeout(timer2);
7401
7524
  resolve5({ ok: false, code: -1, stdout: "", error });
7402
7525
  });
7403
7526
  child.on("close", (code) => {
7404
7527
  if (settled) return;
7405
7528
  settled = true;
7406
- clearTimeout(timer);
7529
+ clearTimeout(timer2);
7407
7530
  resolve5({ ok: code === 0, code, stdout });
7408
7531
  });
7409
7532
  });
@@ -7989,7 +8112,7 @@ function copyToClipboard(text) {
7989
8112
  import stringWidth11 from "string-width";
7990
8113
 
7991
8114
  // src/tui/app/use-mouse-input.mjs
7992
- import { useCallback as useCallback2, useEffect as useEffect6, useRef as useRef6 } from "react";
8115
+ import { useCallback as useCallback2, useEffect as useEffect7, useRef as useRef7 } from "react";
7993
8116
 
7994
8117
  // src/tui/markdown/measure-rendered-rows.mjs
7995
8118
  import stripAnsi4 from "strip-ansi";
@@ -8454,6 +8577,25 @@ function collectFlatBodyLines(text, codeBlock, bandWidth) {
8454
8577
  }
8455
8578
  return lines;
8456
8579
  }
8580
+ function normalizeCodePlain(text) {
8581
+ return String(text ?? "").replace(/\r\n?/g, "\n").replace(/\t/g, " ").replace(/[\u0000-\u0009\u000b-\u001f\u007f]/g, " ");
8582
+ }
8583
+ function collectPlainBodyLines(text, codeBlock, bandWidth) {
8584
+ const max = Math.max(1, bandWidth);
8585
+ const lines = [];
8586
+ for (const line of String(text ?? "").split(EOL)) {
8587
+ if (!line) {
8588
+ lines.push("");
8589
+ continue;
8590
+ }
8591
+ if (line.length <= max && displayWidth(line) <= max) {
8592
+ lines.push(codeBlock(line));
8593
+ } else {
8594
+ for (const seg of wrapCodeLine(codeBlock(line), max)) lines.push(seg);
8595
+ }
8596
+ }
8597
+ return lines;
8598
+ }
8457
8599
  function collectHighlightedBodyLines(text, hljsLang, bandWidth) {
8458
8600
  const highlighted = highlightCodeText(text, hljsLang);
8459
8601
  const { codeBlock } = colorizers();
@@ -8469,19 +8611,24 @@ function renderCodeBlock(token, width = 0) {
8469
8611
  const { codeBlock } = colorizers();
8470
8612
  const c = extraColorizers();
8471
8613
  const lang = normalizeLang(token.lang);
8472
- const text = normalizeCodeText(decodeEntities(token.text ?? ""));
8473
8614
  const renderWidth = Math.max(8, Number(width) || 80);
8474
8615
  const contentWidth = Math.max(1, renderWidth - CODE_GUTTER.length);
8475
8616
  let bodyLines;
8476
- if (DIFF_LANGS.has(lang) || !lang && looksLikeUnifiedDiff(text)) {
8477
- bodyLines = collectDiffBodyLines(text, c, contentWidth);
8617
+ if (token.plain) {
8618
+ const text = normalizeCodePlain(decodeEntities(token.text ?? ""));
8619
+ bodyLines = collectPlainBodyLines(text, codeBlock, contentWidth);
8478
8620
  } else {
8479
- const hljsLang = resolveHljsLanguage(lang);
8480
- const family = LANG_FAMILY[lang];
8481
- if (hljsLang && family !== "md") {
8482
- bodyLines = collectHighlightedBodyLines(text, hljsLang, contentWidth);
8621
+ const text = normalizeCodeText(decodeEntities(token.text ?? ""));
8622
+ if (DIFF_LANGS.has(lang) || !lang && looksLikeUnifiedDiff(text)) {
8623
+ bodyLines = collectDiffBodyLines(text, c, contentWidth);
8483
8624
  } else {
8484
- bodyLines = collectFlatBodyLines(text, codeBlock, contentWidth);
8625
+ const hljsLang = resolveHljsLanguage(lang);
8626
+ const family = LANG_FAMILY[lang];
8627
+ if (hljsLang && family !== "md") {
8628
+ bodyLines = collectHighlightedBodyLines(text, hljsLang, contentWidth);
8629
+ } else {
8630
+ bodyLines = collectFlatBodyLines(text, codeBlock, contentWidth);
8631
+ }
8485
8632
  }
8486
8633
  }
8487
8634
  const indentedBody = bodyLines.map((line) => `${CODE_GUTTER}${line ?? ""}`);
@@ -8903,6 +9050,38 @@ function trimPartialClosingFences(tokens) {
8903
9050
  if (lastLine.length >= marker.length || lastLine !== marker[0].repeat(lastLine.length)) return;
8904
9051
  token.text = String(token.text ?? "").slice(0, -lastLine.length).replace(/\n$/, "");
8905
9052
  }
9053
+ var OPEN_FENCE_RE = /^( {0,3})(`{3,}|~{3,})(.*)$/;
9054
+ var CLOSE_FENCE_RE = /^ {0,3}([`~]+)[ \t]*$/;
9055
+ function isClosingFence(line, char, openLen) {
9056
+ const m = CLOSE_FENCE_RE.exec(line);
9057
+ if (!m) return false;
9058
+ const run = m[1];
9059
+ return run.startsWith(char.repeat(openLen));
9060
+ }
9061
+ function findOpenFenceStart(text) {
9062
+ const value = String(text ?? "");
9063
+ let open = null;
9064
+ let start = 0;
9065
+ for (let i = 0; i <= value.length; i++) {
9066
+ if (i !== value.length && value[i] !== "\n") continue;
9067
+ const line = value.slice(start, i);
9068
+ if (!open) {
9069
+ const m = OPEN_FENCE_RE.exec(line);
9070
+ if (m) {
9071
+ const char = m[2][0];
9072
+ const info = m[3];
9073
+ if (!(char === "`" && info.indexOf("`") !== -1)) {
9074
+ open = { index: start, indent: m[1].length, char, len: m[2].length, lang: info.trim() };
9075
+ }
9076
+ }
9077
+ } else if (isClosingFence(line, open.char, open.len)) {
9078
+ open = null;
9079
+ }
9080
+ start = i + 1;
9081
+ }
9082
+ if (!open || open.indent !== 0) return null;
9083
+ return { index: open.index, lang: open.lang };
9084
+ }
8906
9085
 
8907
9086
  // src/tui/markdown/render-ansi.mjs
8908
9087
  var TOKEN_CACHE_MAX = 500;
@@ -8932,6 +9111,23 @@ function lexMarkdown(content, { trimPartialFences = false } = {}) {
8932
9111
  }];
8933
9112
  }
8934
9113
  if (trimPartialFences) {
9114
+ const open = findOpenFenceStart(text);
9115
+ if (open) {
9116
+ const post = text.slice(open.index);
9117
+ const nl = post.indexOf("\n");
9118
+ const codeToken = {
9119
+ type: "code",
9120
+ raw: post,
9121
+ lang: open.lang,
9122
+ text: nl === -1 ? "" : post.slice(nl + 1),
9123
+ plain: true
9124
+ };
9125
+ const pre = text.slice(0, open.index);
9126
+ const tokens3 = pre ? marked.lexer(pre) : [];
9127
+ tokens3.push(codeToken);
9128
+ trimPartialClosingFences(tokens3);
9129
+ return tokens3;
9130
+ }
8935
9131
  const tokens2 = marked.lexer(text);
8936
9132
  trimPartialClosingFences(tokens2);
8937
9133
  return tokens2;
@@ -9077,6 +9273,21 @@ function resolveStreamingMarkdownParts(text, streamKey) {
9077
9273
  if (!t.startsWith(stablePrefix)) {
9078
9274
  stablePrefix = "";
9079
9275
  }
9276
+ const open = findOpenFenceStart(t);
9277
+ if (open) {
9278
+ let openPrefix = t.substring(0, open.index);
9279
+ if (isWhitespaceOnlyText(openPrefix)) openPrefix = "";
9280
+ if (key && openPrefix) touchStablePrefixKey(key, openPrefix);
9281
+ else if (key) stablePrefixByStreamKey.delete(key);
9282
+ const unstableSuffix2 = t.substring(openPrefix.length);
9283
+ return {
9284
+ plain: false,
9285
+ openFence: true,
9286
+ stablePrefix: openPrefix,
9287
+ unstableSuffix: unstableSuffix2,
9288
+ unstableForRender: unstableSuffix2
9289
+ };
9290
+ }
9080
9291
  try {
9081
9292
  configureMarked();
9082
9293
  const boundary = stablePrefix.length;
@@ -9831,6 +10042,8 @@ function positiveIntEnv(name, fallback) {
9831
10042
  var TRANSCRIPT_WINDOW_MIN_ITEMS = positiveIntEnv("MIXDOG_TUI_TRANSCRIPT_WINDOW_MIN_ITEMS", 12);
9832
10043
  var TRANSCRIPT_WINDOW_OVERSCAN_ROWS = positiveIntEnv("MIXDOG_TUI_TRANSCRIPT_OVERSCAN_ROWS", 16);
9833
10044
  var TRANSCRIPT_WINDOW_MAX_ITEMS = positiveIntEnv("MIXDOG_TUI_TRANSCRIPT_WINDOW_ITEMS", 80);
10045
+ var TRANSCRIPT_WINDOW_TAIL_OVERSCAN_ROWS = positiveIntEnv("MIXDOG_TUI_TRANSCRIPT_TAIL_OVERSCAN_ROWS", 4);
10046
+ var TRANSCRIPT_WINDOW_TAIL_MAX_ITEMS = positiveIntEnv("MIXDOG_TUI_TRANSCRIPT_WINDOW_TAIL_ITEMS", 20);
9834
10047
  var SELECTION_PAINT_INTERVAL_MS = positiveIntEnv("MIXDOG_TUI_SELECTION_PAINT_MS", 24);
9835
10048
  var SCROLL_COALESCE_MS = positiveIntEnv("MIXDOG_TUI_SCROLL_COALESCE_MS", 16);
9836
10049
  var PROMPT_HISTORY_LIMIT = 50;
@@ -10449,12 +10662,14 @@ function transcriptRenderWindow(items, { scrollOffset = 0, viewportHeight = 24,
10449
10662
  return { startIndex: 0, endIndex: itemCount, items: allItems, bottomSpacerRows: 0, totalRows, maxScrollRows, effectiveScrollOffset };
10450
10663
  }
10451
10664
  const minItems = Math.min(TRANSCRIPT_WINDOW_MIN_ITEMS, itemCount);
10452
- const maxItems = Math.max(minItems, TRANSCRIPT_WINDOW_MAX_ITEMS);
10665
+ const atTail = effectiveScrollOffset === 0;
10666
+ const overscanRows = atTail ? TRANSCRIPT_WINDOW_TAIL_OVERSCAN_ROWS : TRANSCRIPT_WINDOW_OVERSCAN_ROWS;
10667
+ const maxItems = Math.max(minItems, atTail ? TRANSCRIPT_WINDOW_TAIL_MAX_ITEMS : TRANSCRIPT_WINDOW_MAX_ITEMS);
10453
10668
  const prefixRows = fallbackIndex.prefixRows;
10454
10669
  const visibleTop = Math.max(0, totalRows - effectiveScrollOffset - viewRows);
10455
10670
  const visibleBottom = Math.min(totalRows, totalRows - effectiveScrollOffset);
10456
- const desiredTop = Math.max(0, visibleTop - TRANSCRIPT_WINDOW_OVERSCAN_ROWS);
10457
- const desiredBottom = Math.min(totalRows, visibleBottom + TRANSCRIPT_WINDOW_OVERSCAN_ROWS);
10671
+ const desiredTop = Math.max(0, visibleTop - overscanRows);
10672
+ const desiredBottom = Math.min(totalRows, visibleBottom + overscanRows);
10458
10673
  let startIndex = Math.max(0, upperBound(prefixRows, desiredTop) - 1);
10459
10674
  let endIndex = Math.min(itemCount, Math.max(startIndex + 1, lowerBound(prefixRows, Math.max(desiredBottom, desiredTop + 1))));
10460
10675
  while (endIndex - startIndex < minItems && startIndex > 0) startIndex--;
@@ -10462,9 +10677,10 @@ function transcriptRenderWindow(items, { scrollOffset = 0, viewportHeight = 24,
10462
10677
  if (endIndex - startIndex > maxItems) {
10463
10678
  const visibleStartIndex = Math.max(0, upperBound(prefixRows, visibleTop) - 1);
10464
10679
  const visibleEndIndex = Math.min(itemCount, Math.max(visibleStartIndex + 1, lowerBound(prefixRows, Math.max(visibleBottom, visibleTop + 1))));
10465
- startIndex = Math.max(0, Math.min(visibleStartIndex, itemCount - maxItems));
10466
- endIndex = Math.min(itemCount, Math.max(visibleEndIndex, startIndex + maxItems));
10467
- if (endIndex - startIndex > maxItems) startIndex = Math.max(0, endIndex - maxItems);
10680
+ const effectiveMaxItems = Math.max(maxItems, visibleEndIndex - visibleStartIndex);
10681
+ startIndex = Math.max(0, Math.min(visibleStartIndex, itemCount - effectiveMaxItems));
10682
+ endIndex = Math.min(itemCount, Math.max(visibleEndIndex, startIndex + effectiveMaxItems));
10683
+ if (endIndex - startIndex > effectiveMaxItems) startIndex = Math.max(0, endIndex - effectiveMaxItems);
10468
10684
  }
10469
10685
  const bottomSpacerRows = Math.max(0, totalRows - (prefixRows[endIndex] || totalRows));
10470
10686
  return {
@@ -10513,8 +10729,8 @@ function useMouseInput({
10513
10729
  setMeasuredRowsVersion,
10514
10730
  clearStitchBuffer
10515
10731
  }) {
10516
- const zoomTimerRef = useRef6(null);
10517
- const edgeAutoscrollRef = useRef6({ dir: 0, timer: null, noMove: 0 });
10732
+ const zoomTimerRef = useRef7(null);
10733
+ const edgeAutoscrollRef = useRef7({ dir: 0, timer: null, noMove: 0 });
10518
10734
  const passthroughCtrlWheelZoom = useCallback2(() => {
10519
10735
  if (!stdout?.write) return;
10520
10736
  try {
@@ -10537,10 +10753,10 @@ function useMouseInput({
10537
10753
  zoomTimerRef.current = setTimeout(() => restore(0), 700);
10538
10754
  zoomTimerRef.current.unref?.();
10539
10755
  }, [stdout, zoomTimerRef]);
10540
- useEffect6(() => () => {
10756
+ useEffect7(() => () => {
10541
10757
  if (zoomTimerRef.current) clearTimeout(zoomTimerRef.current);
10542
10758
  }, [zoomTimerRef]);
10543
- useEffect6(() => {
10759
+ useEffect7(() => {
10544
10760
  if (!inkInput || !isRawModeSupported) return void 0;
10545
10761
  const WHEEL_SGR = /\x1b\[<(\d+);/;
10546
10762
  const linearSelection = (a, b) => {
@@ -10917,7 +11133,7 @@ function useMouseInput({
10917
11133
  }
10918
11134
 
10919
11135
  // src/tui/app/use-transcript-scroll.mjs
10920
- import { useCallback as useCallback3, useEffect as useEffect7, useRef as useRef7 } from "react";
11136
+ import { useCallback as useCallback3, useEffect as useEffect8, useRef as useRef8 } from "react";
10921
11137
  function useTranscriptScroll({
10922
11138
  store,
10923
11139
  frameColumns,
@@ -10937,13 +11153,13 @@ function useTranscriptScroll({
10937
11153
  selectionLayoutRef,
10938
11154
  selectionTextRef
10939
11155
  }) {
10940
- const scrollAnimationRef = useRef7(null);
10941
- const selectionPaintRef = useRef7({ t: 0, rect: null, pending: null, timer: null });
10942
- const scrollCoalesceRef = useRef7({ pendingRows: 0, timer: null });
10943
- const selectionTextTimerRef = useRef7(null);
10944
- const stitchBufferRef = useRef7(/* @__PURE__ */ new Map());
10945
- const stitchHarvestTimerRef = useRef7(null);
10946
- const stitchHarvestScrollRef = useRef7(0);
11156
+ const scrollAnimationRef = useRef8(null);
11157
+ const selectionPaintRef = useRef8({ t: 0, rect: null, pending: null, timer: null });
11158
+ const scrollCoalesceRef = useRef8({ pendingRows: 0, timer: null });
11159
+ const selectionTextTimerRef = useRef8(null);
11160
+ const stitchBufferRef = useRef8(/* @__PURE__ */ new Map());
11161
+ const stitchHarvestTimerRef = useRef8(null);
11162
+ const stitchHarvestScrollRef = useRef8(0);
10947
11163
  const clearStitchBuffer = useCallback3(() => {
10948
11164
  stitchBufferRef.current.clear();
10949
11165
  if (stitchHarvestTimerRef.current) {
@@ -11210,14 +11426,14 @@ function useTranscriptScroll({
11210
11426
  if (lr != null && Number.isFinite(lr.x2)) return Math.max(0, lr.x2);
11211
11427
  return Math.max(0, frameColumns - 1);
11212
11428
  }, [store, frameColumns]);
11213
- const gridSelectionActiveRef = useRef7(() => {
11429
+ const gridSelectionActiveRef = useRef8(() => {
11214
11430
  const drag = dragRef.current;
11215
11431
  if (!drag || drag.active) return false;
11216
11432
  if (drag.region !== "transcript" && drag.region !== "status") return false;
11217
11433
  const rect = drag.rect;
11218
11434
  return Boolean(rect) && !(rect.x1 === rect.x2 && rect.y1 === rect.y2);
11219
11435
  });
11220
- useEffect7(() => () => {
11436
+ useEffect8(() => () => {
11221
11437
  const paintState = selectionPaintRef.current;
11222
11438
  if (paintState.timer) clearTimeout(paintState.timer);
11223
11439
  paintState.timer = null;
@@ -11421,7 +11637,7 @@ function useTranscriptScroll({
11421
11637
  }
11422
11638
 
11423
11639
  // src/tui/app/use-transcript-window.mjs
11424
- import { useCallback as useCallback4, useEffect as useEffect8, useLayoutEffect as useLayoutEffect3, useMemo, useRef as useRef8, useState as useState6 } from "react";
11640
+ import { useCallback as useCallback4, useEffect as useEffect9, useLayoutEffect as useLayoutEffect3, useMemo, useRef as useRef9, useState as useState7 } from "react";
11425
11641
  function useTranscriptWindow({
11426
11642
  items,
11427
11643
  themeEpoch,
@@ -11450,14 +11666,14 @@ function useTranscriptWindow({
11450
11666
  measuredRowsVersion,
11451
11667
  setMeasuredRowsVersion
11452
11668
  }) {
11453
- const transcriptTotalRowsRef = useRef8(0);
11454
- const preservedScrollDeltaRef = useRef8(0);
11455
- const committedMaxScrollRowsRef = useRef8(0);
11456
- const incrementalRowIndexCacheRef = useRef8(null);
11457
- const prevViewportGeomRef = useRef8({ contentHeight: 0, floatingPanelRows: 0 });
11458
- const transcriptItemElsRef = useRef8(/* @__PURE__ */ new Map());
11459
- const transcriptMeasureRefCache = useRef8(/* @__PURE__ */ new Map());
11460
- const transcriptMeasureItemsRef = useRef8(/* @__PURE__ */ new Map());
11669
+ const transcriptTotalRowsRef = useRef9(0);
11670
+ const preservedScrollDeltaRef = useRef9(0);
11671
+ const committedMaxScrollRowsRef = useRef9(0);
11672
+ const incrementalRowIndexCacheRef = useRef9(null);
11673
+ const prevViewportGeomRef = useRef9({ contentHeight: 0, floatingPanelRows: 0 });
11674
+ const transcriptItemElsRef = useRef9(/* @__PURE__ */ new Map());
11675
+ const transcriptMeasureRefCache = useRef9(/* @__PURE__ */ new Map());
11676
+ const transcriptMeasureItemsRef = useRef9(/* @__PURE__ */ new Map());
11461
11677
  const transcriptMeasureRef = useCallback4((item) => {
11462
11678
  if (!TRANSCRIPT_MEASURED_ROWS || !item || item.id == null) return void 0;
11463
11679
  if (shouldSuppressFullyFailedToolItem(item)) {
@@ -11855,13 +12071,13 @@ function useTranscriptWindow({
11855
12071
  dragRef.current = { ...dragRef.current, rect: clippedRect };
11856
12072
  paintSelectionRect(clippedRect, { rememberText: false, immediate: true });
11857
12073
  }, [transcriptContentHeight, transcriptWindow.totalRows, transcriptWindow.effectiveScrollOffset, withSelectionClip, paintSelectionRect]);
11858
- useEffect8(() => {
12074
+ useEffect9(() => {
11859
12075
  if (!dragRef.current.rect) return;
11860
12076
  const clippedRect = withSelectionClip(dragRef.current.rect);
11861
12077
  dragRef.current = { ...dragRef.current, rect: clippedRect };
11862
12078
  paintSelectionRect(clippedRect, { rememberText: false, immediate: true });
11863
12079
  }, [themeEpoch, withSelectionClip, paintSelectionRect]);
11864
- useEffect8(() => {
12080
+ useEffect9(() => {
11865
12081
  const maxRows = Math.max(0, Number(transcriptWindow.maxScrollRows) || 0);
11866
12082
  if (scrollTargetRef.current <= maxRows && scrollPositionRef.current <= maxRows && scrollOffset <= maxRows) return;
11867
12083
  stopSmoothScroll();
@@ -14014,37 +14230,25 @@ function createOnboardingSteps({
14014
14230
  }
14015
14231
 
14016
14232
  // src/runtime/shared/config.mjs
14017
- import { readFileSync as readFileSync4, mkdirSync as mkdirSync4, existsSync as existsSync5 } from "fs";
14018
- import { join as join5, dirname as dirname5 } from "path";
14233
+ import { readFileSync as readFileSync5, mkdirSync as mkdirSync5, existsSync as existsSync6 } from "fs";
14234
+ import { join as join6, dirname as dirname6 } from "path";
14019
14235
  import { createRequire as createRequire2 } from "module";
14020
14236
 
14021
- // src/runtime/shared/plugin-paths.mjs
14022
- import { homedir as homedir2 } from "os";
14023
- import { dirname as dirname2, join as join2 } from "path";
14024
- import { fileURLToPath } from "url";
14025
- var DEFAULT_ROOT = join2(dirname2(fileURLToPath(import.meta.url)), "..", "..");
14026
- function mixdogHome() {
14027
- return process.env.MIXDOG_HOME || join2(homedir2(), ".mixdog");
14028
- }
14029
- function resolvePluginData() {
14030
- return process.env.MIXDOG_DATA_DIR || join2(mixdogHome(), "data");
14031
- }
14032
-
14033
14237
  // src/runtime/shared/atomic-file.mjs
14034
14238
  import {
14035
14239
  closeSync,
14036
- existsSync as existsSync3,
14240
+ existsSync as existsSync4,
14037
14241
  fsyncSync,
14038
14242
  linkSync,
14039
- mkdirSync as mkdirSync2,
14243
+ mkdirSync as mkdirSync3,
14040
14244
  openSync,
14041
- readFileSync as readFileSync2,
14245
+ readFileSync as readFileSync3,
14042
14246
  renameSync as renameSync2,
14043
14247
  statSync as statSync2,
14044
14248
  unlinkSync as unlinkSync2,
14045
- writeFileSync as writeFileSync2
14249
+ writeFileSync as writeFileSync3
14046
14250
  } from "fs";
14047
- import { dirname as dirname3, basename as basename3, join as join3 } from "path";
14251
+ import { dirname as dirname4, basename as basename3, join as join4 } from "path";
14048
14252
  import { randomBytes as randomBytes2 } from "crypto";
14049
14253
  import { execFile as execFile2, execFileSync } from "child_process";
14050
14254
  import { promisify } from "util";
@@ -14096,7 +14300,7 @@ function withFileLockSync(lockPath, fn, opts = {}) {
14096
14300
  const timeoutMs = Number.isFinite(opts.timeoutMs) ? opts.timeoutMs : DEFAULT_LOCK_TIMEOUT_MS;
14097
14301
  const staleMs = Number.isFinite(opts.staleMs) ? opts.staleMs : 3e4;
14098
14302
  const deadline = Date.now() + timeoutMs;
14099
- mkdirSync2(dirname3(lockPath), { recursive: true });
14303
+ mkdirSync3(dirname4(lockPath), { recursive: true });
14100
14304
  let attempt = 0;
14101
14305
  let lastErr = null;
14102
14306
  while (true) {
@@ -14124,7 +14328,7 @@ function withFileLockSync(lockPath, fn, opts = {}) {
14124
14328
  continue;
14125
14329
  }
14126
14330
  try {
14127
- writeFileSync2(fd, `${process.pid} ${Date.now()} ${_OWNER_TOKEN}
14331
+ writeFileSync3(fd, `${process.pid} ${Date.now()} ${_OWNER_TOKEN}
14128
14332
  `, "utf8");
14129
14333
  } catch {
14130
14334
  }
@@ -14149,7 +14353,7 @@ function withFileLockSync(lockPath, fn, opts = {}) {
14149
14353
  }
14150
14354
  function _readLockOwnerPid(lockPath) {
14151
14355
  try {
14152
- const raw = readFileSync2(lockPath, "utf8");
14356
+ const raw = readFileSync3(lockPath, "utf8");
14153
14357
  const tok = String(raw).trim().split(/\s+/)[0];
14154
14358
  const pid = Number.parseInt(tok, 10);
14155
14359
  return Number.isFinite(pid) && pid > 0 ? pid : null;
@@ -14169,7 +14373,7 @@ function _pidIsDead(pid) {
14169
14373
  }
14170
14374
  function _readLockOwner(lockPath) {
14171
14375
  try {
14172
- const raw = readFileSync2(lockPath, "utf8");
14376
+ const raw = readFileSync3(lockPath, "utf8");
14173
14377
  const parts = String(raw).trim().split(/\s+/);
14174
14378
  const pid = Number.parseInt(parts[0], 10);
14175
14379
  return {
@@ -14250,14 +14454,14 @@ function _reclaimGuardIsUnreadableAndStale(guardPath, staleMs) {
14250
14454
  }
14251
14455
  function _reclaimGuardMatchesToken(guardPath, token) {
14252
14456
  try {
14253
- return readFileSync2(guardPath, "utf8") === token;
14457
+ return readFileSync3(guardPath, "utf8") === token;
14254
14458
  } catch {
14255
14459
  return false;
14256
14460
  }
14257
14461
  }
14258
14462
  function _readGuardContent(guardPath) {
14259
14463
  try {
14260
- return readFileSync2(guardPath, "utf8");
14464
+ return readFileSync3(guardPath, "utf8");
14261
14465
  } catch {
14262
14466
  return null;
14263
14467
  }
@@ -14309,7 +14513,7 @@ function _tryAcquireReclaimGuard(lockPath, staleMs) {
14309
14513
  const token = `${process.pid} ${Date.now()} ${randomBytes2(8).toString("hex")}
14310
14514
  `;
14311
14515
  try {
14312
- writeFileSync2(fd, token, "utf8");
14516
+ writeFileSync3(fd, token, "utf8");
14313
14517
  } catch {
14314
14518
  }
14315
14519
  return { fd, guardPath, token };
@@ -14342,7 +14546,7 @@ async function withFileLock(lockPath, fn, opts = {}) {
14342
14546
  const timeoutMs = Number.isFinite(opts.timeoutMs) ? opts.timeoutMs : DEFAULT_LOCK_TIMEOUT_MS;
14343
14547
  const staleMs = Number.isFinite(opts.staleMs) ? opts.staleMs : 3e4;
14344
14548
  const deadline = Date.now() + timeoutMs;
14345
- mkdirSync2(dirname3(lockPath), { recursive: true });
14549
+ mkdirSync3(dirname4(lockPath), { recursive: true });
14346
14550
  let attempt = 0;
14347
14551
  let lastErr = null;
14348
14552
  while (true) {
@@ -14370,7 +14574,7 @@ async function withFileLock(lockPath, fn, opts = {}) {
14370
14574
  continue;
14371
14575
  }
14372
14576
  try {
14373
- writeFileSync2(fd, `${process.pid} ${Date.now()} ${_OWNER_TOKEN}
14577
+ writeFileSync3(fd, `${process.pid} ${Date.now()} ${_OWNER_TOKEN}
14374
14578
  `, "utf8");
14375
14579
  } catch {
14376
14580
  }
@@ -14397,8 +14601,8 @@ var _cachedUserSid = null;
14397
14601
  function _resolveCurrentUserPrincipal() {
14398
14602
  const systemRoot = process.env.SystemRoot || process.env.windir;
14399
14603
  if (systemRoot) {
14400
- const whoami = join3(systemRoot, "System32", "whoami.exe");
14401
- if (existsSync3(whoami)) {
14604
+ const whoami = join4(systemRoot, "System32", "whoami.exe");
14605
+ if (existsSync4(whoami)) {
14402
14606
  try {
14403
14607
  const out = execFileSync(whoami, ["/user", "/fo", "csv", "/nh"], {
14404
14608
  encoding: "utf8",
@@ -14426,8 +14630,8 @@ function _enforceOwnerOnlyAclWin32(targetPath) {
14426
14630
  err.code = "EACLNOROOT";
14427
14631
  throw err;
14428
14632
  }
14429
- const icacls = join3(systemRoot, "System32", "icacls.exe");
14430
- if (!existsSync3(icacls)) {
14633
+ const icacls = join4(systemRoot, "System32", "icacls.exe");
14634
+ if (!existsSync4(icacls)) {
14431
14635
  const err = new Error(`icacls.exe not found at ${icacls}; refusing to leave secret world-readable`);
14432
14636
  err.code = "EACLNOICACLS";
14433
14637
  throw err;
@@ -14453,8 +14657,8 @@ function _enforceOwnerOnlyAclWin32(targetPath) {
14453
14657
  async function _resolveCurrentUserPrincipalAsync() {
14454
14658
  const systemRoot = process.env.SystemRoot || process.env.windir;
14455
14659
  if (systemRoot) {
14456
- const whoami = join3(systemRoot, "System32", "whoami.exe");
14457
- if (existsSync3(whoami)) {
14660
+ const whoami = join4(systemRoot, "System32", "whoami.exe");
14661
+ if (existsSync4(whoami)) {
14458
14662
  try {
14459
14663
  const { stdout } = await _execFileAsync(whoami, ["/user", "/fo", "csv", "/nh"], {
14460
14664
  encoding: "utf8",
@@ -14478,8 +14682,8 @@ async function _enforceOwnerOnlyAclWin32Async(targetPath) {
14478
14682
  err.code = "EACLNOROOT";
14479
14683
  throw err;
14480
14684
  }
14481
- const icacls = join3(systemRoot, "System32", "icacls.exe");
14482
- if (!existsSync3(icacls)) {
14685
+ const icacls = join4(systemRoot, "System32", "icacls.exe");
14686
+ if (!existsSync4(icacls)) {
14483
14687
  const err = new Error(`icacls.exe not found at ${icacls}; refusing to leave secret world-readable`);
14484
14688
  err.code = "EACLNOICACLS";
14485
14689
  throw err;
@@ -14498,12 +14702,12 @@ async function _enforceOwnerOnlyAclWin32Async(targetPath) {
14498
14702
  }
14499
14703
  function writeFileAtomicSync(filePath, data, opts = {}) {
14500
14704
  const run = () => {
14501
- const dir = dirname3(filePath);
14502
- mkdirSync2(dir, { recursive: true });
14503
- const tmp = join3(dir, `.${basename3(filePath)}.${randomBytes2(12).toString("hex")}.tmp`);
14705
+ const dir = dirname4(filePath);
14706
+ mkdirSync3(dir, { recursive: true });
14707
+ const tmp = join4(dir, `.${basename3(filePath)}.${randomBytes2(12).toString("hex")}.tmp`);
14504
14708
  try {
14505
14709
  const writeOpts = { encoding: opts.encoding || "utf8", flag: "wx", mode: opts.mode !== void 0 ? opts.mode : 384 };
14506
- writeFileSync2(tmp, data, writeOpts);
14710
+ writeFileSync3(tmp, data, writeOpts);
14507
14711
  if (opts.secret === true) _enforceOwnerOnlyAclWin32(tmp);
14508
14712
  if (opts.fsync !== false) {
14509
14713
  let fd = null;
@@ -14539,8 +14743,8 @@ function writeFileAtomicSync(filePath, data, opts = {}) {
14539
14743
  renameWithRetrySync(tmp, filePath, opts);
14540
14744
  } catch (err) {
14541
14745
  if (opts.renameFallback === "truncate" && opts.secret !== true && process.platform === "win32" && RETRY_CODES.has(err?.code)) {
14542
- const data2 = readFileSync2(tmp);
14543
- writeFileSync2(filePath, data2, { mode: opts.mode !== void 0 ? opts.mode : 384 });
14746
+ const data2 = readFileSync3(tmp);
14747
+ writeFileSync3(filePath, data2, { mode: opts.mode !== void 0 ? opts.mode : 384 });
14544
14748
  try {
14545
14749
  unlinkSync2(tmp);
14546
14750
  } catch {
@@ -14570,7 +14774,7 @@ function writeFileAtomicSync(filePath, data, opts = {}) {
14570
14774
  return true;
14571
14775
  } catch (err) {
14572
14776
  try {
14573
- if (existsSync3(tmp)) unlinkSync2(tmp);
14777
+ if (existsSync4(tmp)) unlinkSync2(tmp);
14574
14778
  } catch {
14575
14779
  }
14576
14780
  throw err;
@@ -14589,7 +14793,7 @@ async function updateJsonAtomic(filePath, mutator, opts = {}) {
14589
14793
  return withFileLock(`${filePath}.lock`, () => {
14590
14794
  let cur = null;
14591
14795
  try {
14592
- cur = JSON.parse(readFileSync2(filePath, "utf8"));
14796
+ cur = JSON.parse(readFileSync3(filePath, "utf8"));
14593
14797
  } catch {
14594
14798
  cur = null;
14595
14799
  }
@@ -14603,22 +14807,22 @@ async function updateJsonAtomic(filePath, mutator, opts = {}) {
14603
14807
  // src/runtime/shared/user-data-guard.mjs
14604
14808
  import {
14605
14809
  copyFileSync,
14606
- existsSync as existsSync4,
14607
- mkdirSync as mkdirSync3,
14810
+ existsSync as existsSync5,
14811
+ mkdirSync as mkdirSync4,
14608
14812
  readdirSync,
14609
- readFileSync as readFileSync3,
14813
+ readFileSync as readFileSync4,
14610
14814
  rmSync,
14611
14815
  statSync as statSync3,
14612
- writeFileSync as writeFileSync3
14816
+ writeFileSync as writeFileSync4
14613
14817
  } from "fs";
14614
- import { dirname as dirname4, join as join4, resolve as resolve3 } from "path";
14818
+ import { dirname as dirname5, join as join5, resolve as resolve3 } from "path";
14615
14819
  import { homedir as homedir3 } from "os";
14616
14820
  import { createHash } from "crypto";
14617
14821
  function mixdogConfigBaseDir() {
14618
- return process.env.MIXDOG_CONFIG_DIR || join4(homedir3(), ".mixdog");
14822
+ return process.env.MIXDOG_CONFIG_DIR || join5(homedir3(), ".mixdog");
14619
14823
  }
14620
14824
  function getBackupRoot() {
14621
- return process.env.MIXDOG_USER_DATA_BACKUP_ROOT || join4(mixdogConfigBaseDir(), "backups", "user-data");
14825
+ return process.env.MIXDOG_USER_DATA_BACKUP_ROOT || join5(mixdogConfigBaseDir(), "backups", "user-data");
14622
14826
  }
14623
14827
  var USER_DATA_FILES = [
14624
14828
  "mixdog-config.json",
@@ -14639,13 +14843,13 @@ function safeReason(reason) {
14639
14843
  }
14640
14844
  function initMarkerPath(dataDir) {
14641
14845
  const id = createHash("sha256").update(String(dataDir || "unknown")).digest("hex").slice(0, 16);
14642
- return join4(getBackupRoot(), `.initialized-${id}.json`);
14846
+ return join5(getBackupRoot(), `.initialized-${id}.json`);
14643
14847
  }
14644
14848
  function isPlainObject(value) {
14645
14849
  return !!value && typeof value === "object" && !Array.isArray(value);
14646
14850
  }
14647
14851
  function hasUserDataInitMarker(dataDir) {
14648
- return existsSync4(initMarkerPath(dataDir));
14852
+ return existsSync5(initMarkerPath(dataDir));
14649
14853
  }
14650
14854
  function isStructurallyCompleteMixdogConfigBackup(parsed) {
14651
14855
  if (!isPlainObject(parsed)) return false;
@@ -14662,10 +14866,10 @@ function loadLatestMixdogConfigFromBackup(_dataDir) {
14662
14866
  return null;
14663
14867
  }
14664
14868
  for (const name of entries) {
14665
- const cfgPath = join4(root, name, "mixdog-config.json");
14666
- if (!existsSync4(cfgPath)) continue;
14869
+ const cfgPath = join5(root, name, "mixdog-config.json");
14870
+ if (!existsSync5(cfgPath)) continue;
14667
14871
  try {
14668
- const parsed = JSON.parse(readFileSync3(cfgPath, "utf8"));
14872
+ const parsed = JSON.parse(readFileSync4(cfgPath, "utf8"));
14669
14873
  if (isStructurallyCompleteMixdogConfigBackup(parsed)) return parsed;
14670
14874
  } catch {
14671
14875
  continue;
@@ -14677,12 +14881,12 @@ function copyTree(src, dst, copied) {
14677
14881
  const st = statSync3(src);
14678
14882
  if (st.isDirectory()) {
14679
14883
  for (const name of readdirSync(src)) {
14680
- copyTree(join4(src, name), join4(dst, name), copied);
14884
+ copyTree(join5(src, name), join5(dst, name), copied);
14681
14885
  }
14682
14886
  return;
14683
14887
  }
14684
14888
  if (!st.isFile()) return;
14685
- mkdirSync3(dirname4(dst), { recursive: true });
14889
+ mkdirSync4(dirname5(dst), { recursive: true });
14686
14890
  copyFileSync(src, dst);
14687
14891
  copied.push(dst);
14688
14892
  }
@@ -14695,15 +14899,15 @@ function pruneBackups(keep = 40) {
14695
14899
  }
14696
14900
  for (const name of entries.slice(keep)) {
14697
14901
  try {
14698
- rmSync(join4(getBackupRoot(), name), { recursive: true, force: true });
14902
+ rmSync(join5(getBackupRoot(), name), { recursive: true, force: true });
14699
14903
  } catch {
14700
14904
  }
14701
14905
  }
14702
14906
  }
14703
14907
  function markUserDataInitialized(dataDir) {
14704
14908
  try {
14705
- mkdirSync3(getBackupRoot(), { recursive: true });
14706
- writeFileSync3(initMarkerPath(dataDir), JSON.stringify({
14909
+ mkdirSync4(getBackupRoot(), { recursive: true });
14910
+ writeFileSync4(initMarkerPath(dataDir), JSON.stringify({
14707
14911
  dataDir,
14708
14912
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
14709
14913
  }, null, 2) + "\n", "utf8");
@@ -14714,16 +14918,16 @@ function backupUserData(dataDir, reason = "snapshot") {
14714
14918
  if (process.env.MIXDOG_SKIP_USER_DATA_BACKUP === "1" || process.env.MIXDOG_SKIP_USER_DATA_BACKUP === "true") {
14715
14919
  return { dir: null, copied: [] };
14716
14920
  }
14717
- if (!dataDir || !existsSync4(dataDir)) return { dir: null, copied: [] };
14718
- const backupDir = join4(getBackupRoot(), `${stamp()}-${safeReason(reason)}`);
14921
+ if (!dataDir || !existsSync5(dataDir)) return { dir: null, copied: [] };
14922
+ const backupDir = join5(getBackupRoot(), `${stamp()}-${safeReason(reason)}`);
14719
14923
  const copied = [];
14720
14924
  for (const rel of USER_DATA_FILES) {
14721
- const src = join4(dataDir, rel);
14722
- if (existsSync4(src)) copyTree(src, join4(backupDir, rel), copied);
14925
+ const src = join5(dataDir, rel);
14926
+ if (existsSync5(src)) copyTree(src, join5(backupDir, rel), copied);
14723
14927
  }
14724
14928
  for (const rel of USER_DATA_DIRS) {
14725
- const src = join4(dataDir, rel);
14726
- if (existsSync4(src)) copyTree(src, join4(backupDir, rel), copied);
14929
+ const src = join5(dataDir, rel);
14930
+ if (existsSync5(src)) copyTree(src, join5(backupDir, rel), copied);
14727
14931
  }
14728
14932
  if (copied.length > 0) {
14729
14933
  markUserDataInitialized(dataDir);
@@ -14741,7 +14945,7 @@ var CASE_INSENSITIVE = process.platform === "win32";
14741
14945
  var _require = createRequire2(import.meta.url);
14742
14946
  var { getSecret: _getSecret, setSecret: _setSecret, deleteSecret: _deleteSecret, hasSecret: _hasSecret } = _require("../../lib/keychain-cjs.cjs");
14743
14947
  var DATA_DIR = resolvePluginData();
14744
- var CONFIG_PATH = join5(DATA_DIR, "mixdog-config.json");
14948
+ var CONFIG_PATH = join6(DATA_DIR, "mixdog-config.json");
14745
14949
  var GENERATED_KEY = "_generated";
14746
14950
  function isPlainObject2(value) {
14747
14951
  return !!value && typeof value === "object" && !Array.isArray(value);
@@ -14754,7 +14958,7 @@ function stripGeneratedMarker(data) {
14754
14958
  function readJsonFile(path2) {
14755
14959
  let raw;
14756
14960
  try {
14757
- raw = readFileSync4(path2, "utf8");
14961
+ raw = readFileSync5(path2, "utf8");
14758
14962
  } catch (err) {
14759
14963
  if (err.code === "ENOENT") return null;
14760
14964
  process.stderr.write(`[config] readJsonFile: unexpected read error for ${path2}: ${err.message}
@@ -14777,7 +14981,7 @@ function readJsonFile(path2) {
14777
14981
  }
14778
14982
  }
14779
14983
  function writeJsonFile(path2, data) {
14780
- mkdirSync4(dirname5(path2), { recursive: true, mode: 448 });
14984
+ mkdirSync5(dirname6(path2), { recursive: true, mode: 448 });
14781
14985
  if (path2 === CONFIG_PATH) {
14782
14986
  try {
14783
14987
  backupUserData(DATA_DIR, "pre-config-write");
@@ -14811,7 +15015,7 @@ function readAll() {
14811
15015
  function quarantineMalformedConfig(parseErr) {
14812
15016
  const corrupt = `${CONFIG_PATH}.corrupt-${Date.now()}`;
14813
15017
  try {
14814
- if (existsSync5(CONFIG_PATH)) renameWithRetrySync(CONFIG_PATH, corrupt);
15018
+ if (existsSync6(CONFIG_PATH)) renameWithRetrySync(CONFIG_PATH, corrupt);
14815
15019
  } catch {
14816
15020
  }
14817
15021
  process.stderr.write(
@@ -14833,7 +15037,7 @@ function restoreAllForRmWOrThrow(reason) {
14833
15037
  function readAllForRmW() {
14834
15038
  let raw;
14835
15039
  try {
14836
- raw = readFileSync4(CONFIG_PATH, "utf8");
15040
+ raw = readFileSync5(CONFIG_PATH, "utf8");
14837
15041
  } catch (err) {
14838
15042
  if (err.code === "ENOENT") {
14839
15043
  if (hasUserDataInitMarker(DATA_DIR)) {
@@ -17545,9 +17749,9 @@ import { useCallback as useCallback5 } from "react";
17545
17749
 
17546
17750
  // src/tui/prompt-history-store.mjs
17547
17751
  import { createHash as createHash2 } from "node:crypto";
17548
- import { chmodSync, existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync5, renameSync as renameSync3, writeFileSync as writeFileSync4 } from "node:fs";
17752
+ import { chmodSync, existsSync as existsSync7, mkdirSync as mkdirSync6, readFileSync as readFileSync6, renameSync as renameSync3, writeFileSync as writeFileSync5 } from "node:fs";
17549
17753
  import { chmod, mkdir, rename, writeFile } from "node:fs/promises";
17550
- import { dirname as dirname6, join as join6, resolve as resolve4 } from "node:path";
17754
+ import { dirname as dirname7, join as join7, resolve as resolve4 } from "node:path";
17551
17755
  var PROMPT_HISTORY_LIMIT2 = 50;
17552
17756
  function promptHistoryKey2(value) {
17553
17757
  return String(value || "").trim().replace(/\s+/g, " ");
@@ -17562,12 +17766,12 @@ function historyFilePath(cwd) {
17562
17766
  const key = promptHistoryCwdKey(cwd);
17563
17767
  if (!key) return "";
17564
17768
  const digest = createHash2("sha256").update(key, "utf8").digest("hex").slice(0, 32);
17565
- return join6(resolvePluginData(), "tui-prompt-history", `${digest}.json`);
17769
+ return join7(resolvePluginData(), "tui-prompt-history", `${digest}.json`);
17566
17770
  }
17567
17771
  function readEntries(filePath) {
17568
- if (!filePath || !existsSync6(filePath)) return [];
17772
+ if (!filePath || !existsSync7(filePath)) return [];
17569
17773
  try {
17570
- const parsed = JSON.parse(readFileSync5(filePath, "utf8"));
17774
+ const parsed = JSON.parse(readFileSync6(filePath, "utf8"));
17571
17775
  const entries = Array.isArray(parsed?.entries) ? parsed.entries : [];
17572
17776
  return entries.map((entry) => String(entry || "").trim()).filter((entry) => promptHistoryKey2(entry));
17573
17777
  } catch {
@@ -17581,9 +17785,9 @@ function buildPayload(entries) {
17581
17785
  function writeEntriesSync(filePath, entries) {
17582
17786
  if (!filePath) return false;
17583
17787
  try {
17584
- mkdirSync5(dirname6(filePath), { recursive: true, mode: 448 });
17788
+ mkdirSync6(dirname7(filePath), { recursive: true, mode: 448 });
17585
17789
  const tmp = `${filePath}.${process.pid}.tmp`;
17586
- writeFileSync4(tmp, buildPayload(entries), { encoding: "utf8", mode: 384 });
17790
+ writeFileSync5(tmp, buildPayload(entries), { encoding: "utf8", mode: 384 });
17587
17791
  renameSync3(tmp, filePath);
17588
17792
  try {
17589
17793
  chmodSync(filePath, 384);
@@ -17597,7 +17801,7 @@ function writeEntriesSync(filePath, entries) {
17597
17801
  async function writeEntriesAsync(filePath, entries) {
17598
17802
  if (!filePath) return false;
17599
17803
  try {
17600
- await mkdir(dirname6(filePath), { recursive: true, mode: 448 });
17804
+ await mkdir(dirname7(filePath), { recursive: true, mode: 448 });
17601
17805
  const tmp = `${filePath}.${process.pid}.tmp`;
17602
17806
  await writeFile(tmp, buildPayload(entries), { encoding: "utf8", mode: 384 });
17603
17807
  await rename(tmp, filePath);
@@ -17635,16 +17839,16 @@ function reconcileWithDisk(filePath, pend) {
17635
17839
  function scheduleWriteBehind(filePath) {
17636
17840
  if (!filePath) return;
17637
17841
  if (pendingTimers.has(filePath)) clearTimeout(pendingTimers.get(filePath));
17638
- const timer = setTimeout(() => {
17842
+ const timer2 = setTimeout(() => {
17639
17843
  void writeBehindFlush(filePath);
17640
17844
  }, WRITE_BEHIND_MS);
17641
- if (typeof timer.unref === "function") timer.unref();
17642
- pendingTimers.set(filePath, timer);
17845
+ if (typeof timer2.unref === "function") timer2.unref();
17846
+ pendingTimers.set(filePath, timer2);
17643
17847
  }
17644
17848
  async function writeBehindFlush(filePath) {
17645
- const timer = pendingTimers.get(filePath);
17646
- if (timer) {
17647
- clearTimeout(timer);
17849
+ const timer2 = pendingTimers.get(filePath);
17850
+ if (timer2) {
17851
+ clearTimeout(timer2);
17648
17852
  pendingTimers.delete(filePath);
17649
17853
  }
17650
17854
  const pend = pendingAppends.get(filePath);
@@ -17660,7 +17864,7 @@ async function writeBehindFlush(filePath) {
17660
17864
  }
17661
17865
  }
17662
17866
  function flushPromptHistory() {
17663
- for (const timer of pendingTimers.values()) clearTimeout(timer);
17867
+ for (const timer2 of pendingTimers.values()) clearTimeout(timer2);
17664
17868
  pendingTimers.clear();
17665
17869
  for (const [filePath, pend] of pendingAppends) {
17666
17870
  if (!pend.length) continue;
@@ -18170,7 +18374,7 @@ function NoticeMessage({ text, tone, columns = 80 }) {
18170
18374
  }
18171
18375
 
18172
18376
  // src/tui/components/ToolExecution.jsx
18173
- import React16, { useEffect as useEffect9, useState as useState7 } from "react";
18377
+ import React16 from "react";
18174
18378
  import { Box as Box14, Text as Text16 } from "../../../vendor/ink/build/index.js";
18175
18379
  import stringWidth9 from "string-width";
18176
18380
 
@@ -18641,15 +18845,12 @@ import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
18641
18845
  var TOOL_BLINK_MS = 500;
18642
18846
  var TOOL_BLINK_LIMIT_MS = 3e3;
18643
18847
  var TOOL_PENDING_SHOW_DELAY_MS = 1e3;
18848
+ var TOOL_ANIM_TICK_MS = TOOL_BLINK_MS;
18644
18849
  function statusCopy(name, label, count, doneCount, pending, isError, args = {}) {
18645
18850
  return formatToolActionHeader(name, args, { pending, count });
18646
18851
  }
18647
18852
  function ToolExecution({ name, args, result, rawResult, isError, errorCount, expanded, columns = 80, attached = false, count = 1, completedCount = 0, startedAt = 0, completedAt = 0, aggregate = false, categories = {}, doneCategories = null, headerFinalized = true, deferredDisplayReady = false }) {
18648
18853
  const rowWidth = Math.max(1, Number(columns || 80));
18649
- const [blinkOn, setBlinkOn] = useState7(true);
18650
- const [blinkExpired, setBlinkExpired] = useState7(false);
18651
- const [pendingDelayElapsed, setPendingDelayElapsed] = useState7(false);
18652
- const [, setElapsedTick] = useState7(0);
18653
18854
  const groupCount = Math.max(1, Number(count || 1));
18654
18855
  const doneCount = Math.max(0, Math.min(groupCount, Number(completedCount || (result == null ? 0 : groupCount))));
18655
18856
  const rt = result == null ? null : String(result).replace(/\s+$/, "");
@@ -18657,13 +18858,19 @@ function ToolExecution({ name, args, result, rawResult, isError, errorCount, exp
18657
18858
  const pending = doneCount < groupCount;
18658
18859
  const startedAtMs = Number(startedAt || 0);
18659
18860
  const completedAtMs = Number(completedAt || 0);
18660
- const pendingAgeMs = pending && startedAtMs ? Math.max(0, Date.now() - startedAtMs) : 0;
18861
+ const nowMs = Date.now();
18862
+ useSharedTick(TOOL_ANIM_TICK_MS, pending);
18863
+ const pendingAgeMs = pending && startedAtMs ? Math.max(0, nowMs - startedAtMs) : 0;
18864
+ const pendingDelayElapsed = pending ? !startedAtMs || pendingAgeMs >= TOOL_PENDING_SHOW_DELAY_MS : false;
18661
18865
  const hasVisibleProgress = doneCount > 0 || Boolean(String(rt || "").trim());
18662
18866
  const pendingDisplayReady = !pending || !startedAtMs || pendingDelayElapsed || pendingAgeMs >= TOOL_PENDING_SHOW_DELAY_MS || hasVisibleProgress || deferredDisplayReady;
18867
+ const blinkActive = pending && pendingDisplayReady;
18868
+ const blinkExpired = blinkActive && startedAtMs > 0 && nowMs - startedAtMs >= TOOL_BLINK_LIMIT_MS;
18869
+ const blinkOn = !blinkActive || blinkExpired ? true : Math.floor(nowMs / TOOL_BLINK_MS) % 2 === 0;
18663
18870
  const headerPending = pending || headerFinalized === false;
18664
18871
  const hasResult = result != null && Boolean(String(rt || "").trim());
18665
18872
  const hasRawResult = rawResult != null && Boolean(String(rawRt || "").trim());
18666
- const elapsedMs = startedAtMs ? Math.max(0, (pending ? Date.now() : completedAtMs || Date.now()) - startedAtMs) : 0;
18873
+ const elapsedMs = startedAtMs ? Math.max(0, (pending ? nowMs : completedAtMs || nowMs) - startedAtMs) : 0;
18667
18874
  const elapsed = elapsedMs >= 1e3 ? formatElapsed(elapsedMs) : "";
18668
18875
  const failedCount = clampFailureCount(errorCount, groupCount, isError);
18669
18876
  const displayGroupCount = groupCount;
@@ -18673,53 +18880,6 @@ function ToolExecution({ name, args, result, rawResult, isError, errorCount, exp
18673
18880
  (v) => (v && typeof v === "object" ? Number(v.count || 0) : Number(v || 0)) > 0
18674
18881
  );
18675
18882
  const displayDoneCategories = hasDoneCounts ? normalizedDoneCategories : displayCategories;
18676
- useEffect9(() => {
18677
- if (!pending) {
18678
- setPendingDelayElapsed(false);
18679
- return void 0;
18680
- }
18681
- const started = Number(startedAt || 0);
18682
- if (!started) {
18683
- setPendingDelayElapsed(true);
18684
- return void 0;
18685
- }
18686
- const remaining = TOOL_PENDING_SHOW_DELAY_MS - Math.max(0, Date.now() - started);
18687
- if (remaining <= 0) {
18688
- setPendingDelayElapsed(true);
18689
- return void 0;
18690
- }
18691
- setPendingDelayElapsed(false);
18692
- const timer = setTimeout(() => setPendingDelayElapsed(true), remaining);
18693
- return () => clearTimeout(timer);
18694
- }, [pending, startedAt]);
18695
- useEffect9(() => {
18696
- if (!pending || !pendingDisplayReady || blinkExpired) {
18697
- setBlinkOn(true);
18698
- return void 0;
18699
- }
18700
- const timer = setInterval(() => setBlinkOn((on) => !on), TOOL_BLINK_MS);
18701
- return () => clearInterval(timer);
18702
- }, [pending, pendingDisplayReady, blinkExpired]);
18703
- useEffect9(() => {
18704
- if (!pending || !pendingDisplayReady) {
18705
- setBlinkExpired(false);
18706
- return void 0;
18707
- }
18708
- const started = Number(startedAt || 0);
18709
- const remaining = TOOL_BLINK_LIMIT_MS - (started ? Math.max(0, Date.now() - started) : 0);
18710
- if (remaining <= 0) {
18711
- setBlinkExpired(true);
18712
- return void 0;
18713
- }
18714
- setBlinkExpired(false);
18715
- const timer = setTimeout(() => setBlinkExpired(true), remaining);
18716
- return () => clearTimeout(timer);
18717
- }, [pending, pendingDisplayReady, startedAt]);
18718
- useEffect9(() => {
18719
- if (!pending || !pendingDisplayReady || !startedAtMs) return void 0;
18720
- const timer = setInterval(() => setElapsedTick((tick) => (tick + 1) % 1e6), 1e3);
18721
- return () => clearInterval(timer);
18722
- }, [pending, pendingDisplayReady, startedAtMs]);
18723
18883
  if (pending && !pendingDisplayReady) {
18724
18884
  const placeholderNormalizedName = String(formatToolSurface(name, args)?.normalizedName || "").toLowerCase();
18725
18885
  const placeholderSingleRow = !aggregate && (SKILL_SURFACE_NAMES2.has(placeholderNormalizedName) || isAgentTool(placeholderNormalizedName));
@@ -18841,7 +19001,9 @@ function ToolExecution({ name, args, result, rawResult, isError, errorCount, exp
18841
19001
  const finalStatusColor = toolStatusColor({ pending, groupCount, failedCount, terminalStatus });
18842
19002
  const dotColor = finalStatusColor;
18843
19003
  const markerGlyph = isAgentResponse ? AGENT_RESPONSE_MARKER : isAgentSurfaceCard ? AGENT_CALL_MARKER : TURN_MARKER;
18844
- const dotText = pending && !blinkExpired && !blinkOn ? " " : markerGlyph;
19004
+ const isDirectionalMarker = isAgentResponse || isAgentSurfaceCard;
19005
+ const markerText = isDirectionalMarker ? `${markerGlyph} ` : markerGlyph;
19006
+ const dotText = pending && !blinkExpired && !blinkOn ? " " : markerText;
18845
19007
  let labelText;
18846
19008
  if (isAgentResponse) labelText = agentResponseTitle(parsedArgs);
18847
19009
  else if (isBackgroundResponse) labelText = backgroundTaskResultTitle(normalizedName, backgroundMeta || parsedArgs);
@@ -19128,7 +19290,7 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
19128
19290
  const { stdout } = useStdout2();
19129
19291
  const [exiting, setExiting] = useState8(false);
19130
19292
  const [tuiReady, setTuiReady] = useState8(false);
19131
- const exitRequestedRef = useRef9(false);
19293
+ const exitRequestedRef = useRef10(false);
19132
19294
  const [resizeState, setResizeState] = useState8(() => ({ ...terminalSize(stdout), epoch: 0 }));
19133
19295
  const [panelTransitionEpoch, setPanelTransitionEpoch] = useState8(0);
19134
19296
  const [panelInkMaskEpoch, setPanelInkMaskEpoch] = useState8(0);
@@ -19136,22 +19298,22 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
19136
19298
  const rightSafetyColumns = windowsLikeTerminal ? 1 : 0;
19137
19299
  const frameColumns = Math.max(1, resizeState.columns - rightSafetyColumns);
19138
19300
  const [scrollOffset, setScrollOffset] = useState8(0);
19139
- const scrollPositionRef = useRef9(0);
19140
- const scrollTargetRef = useRef9(0);
19141
- const maxScrollRowsRef = useRef9(0);
19142
- const transcriptBottomSlackRowsRef = useRef9(0);
19143
- const transcriptAnchorRef = useRef9(null);
19144
- const transcriptAnchorDirtyRef = useRef9(false);
19145
- const transcriptGeomRef = useRef9({ prefixRows: null, totalRows: 0, viewRows: 1 });
19301
+ const scrollPositionRef = useRef10(0);
19302
+ const scrollTargetRef = useRef10(0);
19303
+ const maxScrollRowsRef = useRef10(0);
19304
+ const transcriptBottomSlackRowsRef = useRef10(0);
19305
+ const transcriptAnchorRef = useRef10(null);
19306
+ const transcriptAnchorDirtyRef = useRef10(false);
19307
+ const transcriptGeomRef = useRef10({ prefixRows: null, totalRows: 0, viewRows: 1 });
19146
19308
  const [measuredRowsVersion, setMeasuredRowsVersion] = useState8(0);
19147
- const followingRef = useRef9(false);
19148
- const lastItemsCountRef = useRef9(0);
19149
- const pickerOpenedFromEnterRef = useRef9(false);
19150
- const pickerOpenedFromEnterTimerRef = useRef9(null);
19151
- const projectPickerRef = useRef9(null);
19309
+ const followingRef = useRef10(false);
19310
+ const lastItemsCountRef = useRef10(0);
19311
+ const pickerOpenedFromEnterRef = useRef10(false);
19312
+ const pickerOpenedFromEnterTimerRef = useRef10(null);
19313
+ const projectPickerRef = useRef10(null);
19152
19314
  const buildProjectPickerState = (opts) => projectPickerRef.current.buildProjectPickerState(opts);
19153
19315
  const [picker, setPickerState] = useState8(null);
19154
- const livePickerRef = useRef9(null);
19316
+ const livePickerRef = useRef10(null);
19155
19317
  const setPicker = useCallback6((next) => {
19156
19318
  livePickerRef.current = typeof next === "function" ? next(livePickerRef.current) : next;
19157
19319
  setPickerState((prev) => {
@@ -19173,14 +19335,14 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
19173
19335
  livePickerRef.current = picker;
19174
19336
  const [contextPanel, setContextPanel] = useState8(null);
19175
19337
  const [usagePanel, setUsagePanel] = useState8(null);
19176
- const usageRequestRef = useRef9(0);
19177
- const settingsHeavyCacheRef = useRef9(null);
19338
+ const usageRequestRef = useRef10(0);
19339
+ const settingsHeavyCacheRef = useRef10(null);
19178
19340
  const closeUsagePanel = useCallback6(() => {
19179
19341
  usageRequestRef.current += 1;
19180
19342
  setUsagePanel(null);
19181
19343
  }, []);
19182
19344
  const [providerPrompt, setProviderPrompt] = useState8(null);
19183
- const oauthSubmitRef = useRef9(false);
19345
+ const oauthSubmitRef = useRef10(false);
19184
19346
  const [channelPrompt, setChannelPrompt] = useState8(null);
19185
19347
  const [hookPrompt, setHookPrompt] = useState8(null);
19186
19348
  const [settingsPrompt, setSettingsPrompt] = useState8(null);
@@ -19202,7 +19364,7 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
19202
19364
  pickFolder
19203
19365
  });
19204
19366
  projectPickerRef.current = projectPicker;
19205
- const initialPickerBuiltRef = useRef9(false);
19367
+ const initialPickerBuiltRef = useRef10(false);
19206
19368
  if (!initialPickerBuiltRef.current) {
19207
19369
  initialPickerBuiltRef.current = true;
19208
19370
  let onboardingOwnsScreen = false;
@@ -19310,30 +19472,30 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
19310
19472
  const toolApproval = state.toolApproval || null;
19311
19473
  const [promptDraft, setPromptDraft] = useState8("");
19312
19474
  const [promptDraftOverride, setPromptDraftOverride] = useState8(null);
19313
- const promptLayoutValueRef = useRef9("");
19475
+ const promptLayoutValueRef = useRef10("");
19314
19476
  const [, setPromptLayoutRows] = useState8(1);
19315
19477
  const [textEntryLayoutRows, setTextEntryLayoutRows] = useState8(1);
19316
19478
  const [, setPastedImages] = useState8({});
19317
- const pastedImagesRef = useRef9({});
19318
- const nextPastedImageIdRef = useRef9(1);
19479
+ const pastedImagesRef = useRef10({});
19480
+ const nextPastedImageIdRef = useRef10(1);
19319
19481
  const [, setPastedTexts] = useState8({});
19320
- const pastedTextsRef = useRef9({});
19321
- const nextPastedTextIdRef = useRef9(1);
19322
- const promptValueRef = useRef9("");
19323
- const promptSelectionRef = useRef9(null);
19324
- const promptBoxRectRef = useRef9(null);
19325
- const promptMouseSelectionRef = useRef9(null);
19326
- const promptHistoryNavRef = useRef9({ active: false, index: -1, seed: "", lastValue: "" });
19327
- const promptHistoryDraftChangeRef = useRef9(false);
19482
+ const pastedTextsRef = useRef10({});
19483
+ const nextPastedTextIdRef = useRef10(1);
19484
+ const promptValueRef = useRef10("");
19485
+ const promptSelectionRef = useRef10(null);
19486
+ const promptBoxRectRef = useRef10(null);
19487
+ const promptMouseSelectionRef = useRef10(null);
19488
+ const promptHistoryNavRef = useRef10({ active: false, index: -1, seed: "", lastValue: "" });
19489
+ const promptHistoryDraftChangeRef = useRef10(false);
19328
19490
  const [promptHint, setPromptHint] = useState8("");
19329
19491
  const [promptHintTone, setPromptHintTone] = useState8("info");
19330
19492
  const [welcomePromptHintDismissed, setWelcomePromptHintDismissed] = useState8(false);
19331
19493
  const [conditionalWelcomePromptHint, setConditionalWelcomePromptHint] = useState8("");
19332
- const welcomePromptHintRef = useRef9(null);
19494
+ const welcomePromptHintRef = useRef10(null);
19333
19495
  if (welcomePromptHintRef.current === null) {
19334
19496
  welcomePromptHintRef.current = randomWelcomePromptHint();
19335
19497
  }
19336
- const welcomePromptHintVisibleRef = useRef9(false);
19498
+ const welcomePromptHintVisibleRef = useRef10(false);
19337
19499
  const dismissWelcomePromptHint = useCallback6(() => {
19338
19500
  if (!welcomePromptHintVisibleRef.current) return;
19339
19501
  setWelcomePromptHintDismissed((dismissed) => dismissed || true);
@@ -19341,15 +19503,15 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
19341
19503
  const toastErrorSignature = useMemo2(() => (state.toasts || []).filter((toast) => toast?.tone === "error").map((toast) => `${toast.id || ""}:${toast.text || ""}`).join("|"), [state.toasts]);
19342
19504
  const [slashIndex, setSlashIndex] = useState8(0);
19343
19505
  const [slashDismissedFor, setSlashDismissedFor] = useState8("");
19344
- const slashPaletteRef = useRef9({ open: false, count: 0 });
19345
- const workflowTabCycleRef = useRef9({ pending: false, lastAt: 0 });
19346
- const scrollFocusRef = useRef9({});
19347
- const onboardingStartedRef = useRef9(false);
19348
- const onboardingRef = useRef9({ defaultRoute: null, searchRoute: null, agentRoutes: {}, agents: [], providerModels: [] });
19349
- const providerModelsCacheRef = useRef9({ models: null, at: 0 });
19350
- const searchModelsCacheRef = useRef9({ models: null, at: 0 });
19351
- const modelPickerRequestRef = useRef9(0);
19352
- const onboardingPrefetchSeqRef = useRef9(0);
19506
+ const slashPaletteRef = useRef10({ open: false, count: 0 });
19507
+ const workflowTabCycleRef = useRef10({ pending: false, lastAt: 0 });
19508
+ const scrollFocusRef = useRef10({});
19509
+ const onboardingStartedRef = useRef10(false);
19510
+ const onboardingRef = useRef10({ defaultRoute: null, searchRoute: null, agentRoutes: {}, agents: [], providerModels: [] });
19511
+ const providerModelsCacheRef = useRef10({ models: null, at: 0 });
19512
+ const searchModelsCacheRef = useRef10({ models: null, at: 0 });
19513
+ const modelPickerRequestRef = useRef10(0);
19514
+ const onboardingPrefetchSeqRef = useRef10(0);
19353
19515
  const clearModelCaches = useCallback6((scope = "all") => {
19354
19516
  if (scope === "all" || scope === "provider") {
19355
19517
  providerModelsCacheRef.current = { models: null, at: 0 };
@@ -19514,14 +19676,14 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
19514
19676
  runDoctor: (...a) => store.runDoctor?.(...a),
19515
19677
  requestExit: (...a) => requestExit(...a)
19516
19678
  });
19517
- const promptHintTimerRef = useRef9(null);
19518
- const promptHintActiveRef = useRef9(false);
19519
- const dragRef = useRef9({ anchor: null, anchorScroll: 0, last: null, active: false, rect: null, region: null, anchorSpan: null });
19520
- const transcriptViewportRef = useRef9({ top: 0, bottom: 0 });
19521
- const panelTransitionRef = useRef9({ signature: "", reserve: 0, clearRows: 0, guardRows: 0, epoch: 0 });
19522
- const panelCloseInkMaskRowsRef = useRef9(0);
19523
- const projectBootInputLatchRef = useRef9(false);
19524
- const frameRowsRef = useRef9(24);
19679
+ const promptHintTimerRef = useRef10(null);
19680
+ const promptHintActiveRef = useRef10(false);
19681
+ const dragRef = useRef10({ anchor: null, anchorScroll: 0, last: null, active: false, rect: null, region: null, anchorSpan: null });
19682
+ const transcriptViewportRef = useRef10({ top: 0, bottom: 0 });
19683
+ const panelTransitionRef = useRef10({ signature: "", reserve: 0, clearRows: 0, guardRows: 0, epoch: 0 });
19684
+ const panelCloseInkMaskRowsRef = useRef10(0);
19685
+ const projectBootInputLatchRef = useRef10(false);
19686
+ const frameRowsRef = useRef10(24);
19525
19687
  const STATUSLINE_BAND_ROWS = 3;
19526
19688
  const promptContentColumns = Math.max(1, frameColumns - 4);
19527
19689
  const syncPromptLayoutRows = useCallback6((value) => {
@@ -19598,9 +19760,9 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
19598
19760
  alive = false;
19599
19761
  };
19600
19762
  }, [store, state.provider, state.model, state.workflow?.id, toastErrorSignature]);
19601
- const selectionLayoutRef = useRef9(null);
19602
- const selectionTextRef = useRef9("");
19603
- const lastClickRef = useRef9({ x: -1, y: -1, t: 0, count: 0 });
19763
+ const selectionLayoutRef = useRef10(null);
19764
+ const selectionTextRef = useRef10("");
19765
+ const lastClickRef = useRef10({ x: -1, y: -1, t: 0, count: 0 });
19604
19766
  const showSelectionCopyHint = useCallback6((text, tone = "plain") => {
19605
19767
  if (promptHintTimerRef.current) clearTimeout(promptHintTimerRef.current);
19606
19768
  promptHintActiveRef.current = true;
@@ -19614,8 +19776,8 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
19614
19776
  }, 2200);
19615
19777
  }, []);
19616
19778
  useEffect10(() => {
19617
- const timer = setTimeout(() => setTuiReady(true), 0);
19618
- return () => clearTimeout(timer);
19779
+ const timer2 = setTimeout(() => setTuiReady(true), 0);
19780
+ return () => clearTimeout(timer2);
19619
19781
  }, []);
19620
19782
  useEffect10(() => {
19621
19783
  if (!stdout) return void 0;
@@ -19868,14 +20030,14 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
19868
20030
  }, 2e3);
19869
20031
  hardExitTimer.unref?.();
19870
20032
  setTimeout(() => {
19871
- let timer = null;
20033
+ let timer2 = null;
19872
20034
  Promise.race([
19873
20035
  Promise.resolve(store.dispose?.("cli-react-exit", { detach: true })),
19874
20036
  new Promise((resolve5) => {
19875
- timer = setTimeout(resolve5, 350);
20037
+ timer2 = setTimeout(resolve5, 350);
19876
20038
  })
19877
20039
  ]).finally(() => {
19878
- if (timer) clearTimeout(timer);
20040
+ if (timer2) clearTimeout(timer2);
19879
20041
  exit();
19880
20042
  });
19881
20043
  }, 60);
@@ -21106,8 +21268,8 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
21106
21268
  return void 0;
21107
21269
  }
21108
21270
  if (!hadTransitionClearance) return void 0;
21109
- const timer = setTimeout(() => setPanelTransitionEpoch((epoch) => epoch + 1), 0);
21110
- return () => clearTimeout(timer);
21271
+ const timer2 = setTimeout(() => setPanelTransitionEpoch((epoch) => epoch + 1), 0);
21272
+ return () => clearTimeout(timer2);
21111
21273
  }, [panelLayoutSignature, bottomClusterRows, panelTransitionClearRows, panelTransitionGuardRows]);
21112
21274
  useEffect10(() => {
21113
21275
  panelTransitionRef.current.tailId = latestTranscriptItem?.id ?? null;
@@ -21244,7 +21406,7 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
21244
21406
  /* @__PURE__ */ jsx20(Text20, { color: theme.logo ?? theme.claude, bold: true, children: centerLine("\u2588\u2588\u2551\u255A\u2588\u2588\u2554\u255D\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551", frameColumns) }),
21245
21407
  /* @__PURE__ */ jsx20(Text20, { color: theme.logo ?? theme.claude, bold: true, children: centerLine("\u2588\u2588\u2551 \u255A\u2550\u255D \u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2554\u255D \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D", frameColumns) }),
21246
21408
  /* @__PURE__ */ jsx20(Box18, { height: 1, flexShrink: 0 }),
21247
- /* @__PURE__ */ jsx20(Text20, { color: theme.inactive, children: centerLine(`mixdog coding agent \xB7 ${state.cwd}`, frameColumns, 4) })
21409
+ /* @__PURE__ */ jsx20(Text20, { color: theme.inactive, children: centerLine(`mixdog coding agent \xB7 v${localPackageVersion()} \xB7 ${state.cwd}`, frameColumns, 4) })
21248
21410
  ] }) : null,
21249
21411
  /* @__PURE__ */ jsxs16(
21250
21412
  Box18,
@@ -22470,7 +22632,9 @@ function resolveTuiRuntimeNotificationDelivery(event, text) {
22470
22632
  const parsed = parseAgentJob(trimmed);
22471
22633
  const meta = event?.meta && typeof event.meta === "object" ? event.meta : {};
22472
22634
  if (meta.kind === "update-notice") {
22473
- return { action: "notice", displayText: trimmed, tone: meta.tone === "warn" ? "warn" : "info" };
22635
+ const ver = String(meta.version || "").trim();
22636
+ const displayText = ver ? `mixdog v${ver} ready \u2014 restart to apply.` : trimmed;
22637
+ return { action: "notice", displayText, tone: meta.tone === "warn" ? "warn" : "info" };
22474
22638
  }
22475
22639
  if (!isExecutionNotification(event, trimmed, parsed)) {
22476
22640
  return { action: "enqueue", displayText: trimmed, modelContent: trimmed };
@@ -23011,7 +23175,7 @@ function createAgentJobFeed({
23011
23175
 
23012
23176
  // src/tui/engine/tui-steering-persist.mjs
23013
23177
  import { randomBytes as randomBytes3 } from "crypto";
23014
- import { join as join7 } from "path";
23178
+ import { join as join8 } from "path";
23015
23179
  var PENDING_MESSAGES_FILE = "session-pending-messages.json";
23016
23180
  var PENDING_MESSAGES_MODE = 384;
23017
23181
  var _persistChain = Promise.resolve();
@@ -23022,7 +23186,7 @@ function _serialize(task) {
23022
23186
  return run;
23023
23187
  }
23024
23188
  function pendingMessagesPath() {
23025
- return join7(resolvePluginData(), PENDING_MESSAGES_FILE);
23189
+ return join8(resolvePluginData(), PENDING_MESSAGES_FILE);
23026
23190
  }
23027
23191
  function tuiSteeringSessionKey(leadSessionId) {
23028
23192
  if (typeof leadSessionId !== "string" || !/^[A-Za-z0-9_-]+$/.test(leadSessionId)) return null;
@@ -23581,9 +23745,9 @@ function createSessionFlow(bag) {
23581
23745
  }
23582
23746
  if (compactType) {
23583
23747
  const clearPromise = runtime.clear({ compactType, requireCompactSuccess: true });
23584
- let timer = null;
23748
+ let timer2 = null;
23585
23749
  const timeout = new Promise((_, reject) => {
23586
- timer = setTimeout(
23750
+ timer2 = setTimeout(
23587
23751
  () => reject(new Error(`compaction timed out after ${AUTO_CLEAR_COMPACT_TIMEOUT_MS}ms; auto-clear deferred to next idle`)),
23588
23752
  AUTO_CLEAR_COMPACT_TIMEOUT_MS
23589
23753
  );
@@ -23608,7 +23772,7 @@ function createSessionFlow(bag) {
23608
23772
  });
23609
23773
  throw raceError;
23610
23774
  } finally {
23611
- if (timer) clearTimeout(timer);
23775
+ if (timer2) clearTimeout(timer2);
23612
23776
  }
23613
23777
  } else {
23614
23778
  await runtime.clear({});
@@ -25052,52 +25216,8 @@ function createEngineApiB(bag) {
25052
25216
  };
25053
25217
  }
25054
25218
 
25055
- // src/runtime/shared/update-checker.mjs
25056
- import { dirname as dirname7, join as join8 } from "node:path";
25057
- import { fileURLToPath as fileURLToPath2 } from "node:url";
25058
-
25059
- // src/runtime/shared/spawn-flags.mjs
25060
- var isWin = process.platform === "win32";
25061
- var detachedSpawnOpts = Object.freeze({
25062
- windowsHide: true,
25063
- ...isWin ? {} : { detached: true }
25064
- });
25065
- var hiddenSpawnOpts = Object.freeze({
25066
- windowsHide: true
25067
- });
25068
-
25069
- // src/runtime/shared/update-checker.mjs
25070
- var PACKAGE_NAME = "mixdog";
25071
- var REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
25072
- var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
25073
- var _MODULE_DIR = dirname7(fileURLToPath2(import.meta.url));
25074
- var _PACKAGE_JSON_PATH = join8(_MODULE_DIR, "..", "..", "..", "package.json");
25075
- var _PACKAGE_ROOT = dirname7(_PACKAGE_JSON_PATH);
25076
- function parseSemver(value) {
25077
- const text = String(value || "").trim().replace(/^v/i, "");
25078
- const [core, ...preParts] = text.split("-");
25079
- const parts = core.split(".").map((n) => {
25080
- const num2 = Number.parseInt(n, 10);
25081
- return Number.isFinite(num2) ? num2 : 0;
25082
- });
25083
- while (parts.length < 3) parts.push(0);
25084
- return { parts, prerelease: preParts.join("-") };
25085
- }
25086
- function compareSemver(a, b) {
25087
- const pa = parseSemver(a);
25088
- const pb = parseSemver(b);
25089
- for (let i = 0; i < 3; i++) {
25090
- const diff = (pa.parts[i] || 0) - (pb.parts[i] || 0);
25091
- if (diff !== 0) return diff;
25092
- }
25093
- if (pa.prerelease && !pb.prerelease) return -1;
25094
- if (!pa.prerelease && pb.prerelease) return 1;
25095
- if (pa.prerelease !== pb.prerelease) return pa.prerelease < pb.prerelease ? -1 : 1;
25096
- return 0;
25097
- }
25098
-
25099
25219
  // src/tui/app/doctor.mjs
25100
- import { readFileSync as readFileSync6 } from "node:fs";
25220
+ import { readFileSync as readFileSync7 } from "node:fs";
25101
25221
  import { dirname as dirname8, join as join9 } from "node:path";
25102
25222
  import { fileURLToPath as fileURLToPath3 } from "node:url";
25103
25223
  var GLYPH = { ok: "\u2713", warn: "\u26A0", fail: "\u2717" };
@@ -25124,7 +25244,7 @@ function median(nums) {
25124
25244
  function readPackageJson() {
25125
25245
  try {
25126
25246
  const dir = dirname8(fileURLToPath3(import.meta.url));
25127
- const raw = JSON.parse(readFileSync6(join9(dir, "..", "..", "..", "package.json"), "utf8"));
25247
+ const raw = JSON.parse(readFileSync7(join9(dir, "..", "..", "..", "package.json"), "utf8"));
25128
25248
  return raw && typeof raw === "object" ? raw : null;
25129
25249
  } catch {
25130
25250
  return null;
@@ -25260,7 +25380,7 @@ async function buildDoctorReport(runtime = {}, getState = () => ({})) {
25260
25380
  const pgdata = join9(dataDir, "pgdata");
25261
25381
  let writes;
25262
25382
  try {
25263
- writes = parseCheckpointWriteSeconds(readFileSync6(join9(dataDir, "pg.log"), "utf8"));
25383
+ writes = parseCheckpointWriteSeconds(readFileSync7(join9(dataDir, "pg.log"), "utf8"));
25264
25384
  } catch {
25265
25385
  row("ok", "pg.log unavailable \xB7 check skipped");
25266
25386
  return;
@@ -26062,13 +26182,13 @@ async function createEngineSession({
26062
26182
  const value = String(text ?? "").trim();
26063
26183
  if (!value) return null;
26064
26184
  set({ toasts: [...state.toasts.filter((toast) => toast.id !== id), { id, text: value, tone }] });
26065
- const timer = setTimeout(() => {
26066
- toastTimers.delete(timer);
26185
+ const timer2 = setTimeout(() => {
26186
+ toastTimers.delete(timer2);
26067
26187
  if (flags.disposed) return;
26068
26188
  set({ toasts: state.toasts.filter((toast) => toast.id !== id) });
26069
26189
  }, ttlMs);
26070
- toastTimers.add(timer);
26071
- timer.unref?.();
26190
+ toastTimers.add(timer2);
26191
+ timer2.unref?.();
26072
26192
  return id;
26073
26193
  };
26074
26194
  const pushNotice = (text, tone = "info", options = {}) => {
@@ -26137,8 +26257,8 @@ async function createEngineSession({
26137
26257
  }, 2e3);
26138
26258
  lifecycle.runtimePulseTimer.unref?.();
26139
26259
  function clearToastTimers() {
26140
- for (const timer of toastTimers) {
26141
- clearTimeout(timer);
26260
+ for (const timer2 of toastTimers) {
26261
+ clearTimeout(timer2);
26142
26262
  }
26143
26263
  toastTimers.clear();
26144
26264
  }
@@ -26255,21 +26375,21 @@ function waitWithTimeout(promise, timeoutMs, label = "cleanup") {
26255
26375
  const ms = Math.max(1, Math.floor(Number(timeoutMs) || 1));
26256
26376
  return new Promise((resolve5, reject) => {
26257
26377
  let settled = false;
26258
- const timer = setTimeout(() => {
26378
+ const timer2 = setTimeout(() => {
26259
26379
  if (settled) return;
26260
26380
  settled = true;
26261
26381
  reject(new Error(`${label} timed out after ${ms}ms`));
26262
26382
  }, ms);
26263
- if (typeof timer.unref === "function") timer.unref();
26383
+ if (typeof timer2.unref === "function") timer2.unref();
26264
26384
  Promise.resolve(promise).then((value) => {
26265
26385
  if (settled) return;
26266
26386
  settled = true;
26267
- clearTimeout(timer);
26387
+ clearTimeout(timer2);
26268
26388
  resolve5(value);
26269
26389
  }).catch((error) => {
26270
26390
  if (settled) return;
26271
26391
  settled = true;
26272
- clearTimeout(timer);
26392
+ clearTimeout(timer2);
26273
26393
  reject(error);
26274
26394
  });
26275
26395
  });
@@ -26406,7 +26526,7 @@ function installTuiLoopProbe() {
26406
26526
  if (!LOOP_PROBE_ENABLED) return () => {
26407
26527
  };
26408
26528
  let last = performance3.now();
26409
- const timer = setInterval(() => {
26529
+ const timer2 = setInterval(() => {
26410
26530
  const now = performance3.now();
26411
26531
  const drift = now - last - LOOP_PROBE_INTERVAL_MS;
26412
26532
  last = now;
@@ -26418,10 +26538,10 @@ function installTuiLoopProbe() {
26418
26538
  }
26419
26539
  }
26420
26540
  }, LOOP_PROBE_INTERVAL_MS);
26421
- timer.unref?.();
26541
+ timer2.unref?.();
26422
26542
  return () => {
26423
26543
  try {
26424
- clearInterval(timer);
26544
+ clearInterval(timer2);
26425
26545
  } catch {
26426
26546
  }
26427
26547
  };
@@ -26454,7 +26574,7 @@ function installTuiPerfProbe() {
26454
26574
  stallMs: PERF_STALL_THRESHOLD_MS,
26455
26575
  renderGapMs: PERF_RENDER_GAP_MS
26456
26576
  });
26457
- const timer = setInterval(() => {
26577
+ const timer2 = setInterval(() => {
26458
26578
  const now = performance3.now();
26459
26579
  const elapsed = now - last;
26460
26580
  const lagMs = elapsed - PERF_STALL_INTERVAL_MS;
@@ -26472,10 +26592,10 @@ function installTuiPerfProbe() {
26472
26592
  heapMb: (mem.heapUsed / 1048576).toFixed(1)
26473
26593
  });
26474
26594
  }, PERF_STALL_INTERVAL_MS);
26475
- timer.unref?.();
26595
+ timer2.unref?.();
26476
26596
  return () => {
26477
26597
  try {
26478
- clearInterval(timer);
26598
+ clearInterval(timer2);
26479
26599
  } catch {
26480
26600
  }
26481
26601
  };
@@ -26573,19 +26693,19 @@ function dumpActiveHandles(label) {
26573
26693
  }
26574
26694
  }
26575
26695
  function waitWithTimeout2(promise, ms) {
26576
- let timer = null;
26696
+ let timer2 = null;
26577
26697
  return Promise.race([
26578
26698
  Promise.resolve(promise).then(() => true),
26579
26699
  new Promise((resolve5) => {
26580
- timer = setTimeout(() => resolve5(false), ms);
26700
+ timer2 = setTimeout(() => resolve5(false), ms);
26581
26701
  })
26582
26702
  ]).finally(() => {
26583
- if (timer) clearTimeout(timer);
26703
+ if (timer2) clearTimeout(timer2);
26584
26704
  });
26585
26705
  }
26586
26706
  function scheduleHardExit(code = 0) {
26587
26707
  if (!EXIT_HARD_ENABLED) return;
26588
- const timer = setTimeout(() => {
26708
+ const timer2 = setTimeout(() => {
26589
26709
  dumpActiveHandles("hard-exit");
26590
26710
  try {
26591
26711
  process.stdout.write(`${TERMINAL_MODE_RESET}${TERMINAL_OSC_RESET_BG}`);
@@ -26593,7 +26713,7 @@ function scheduleHardExit(code = 0) {
26593
26713
  }
26594
26714
  process.exit(code);
26595
26715
  }, EXIT_HARD_DELAY_MS);
26596
- timer.unref?.();
26716
+ timer2.unref?.();
26597
26717
  }
26598
26718
  function resolveTuiStderrLogPath() {
26599
26719
  return process.env.MIXDOG_TUI_STDERR_LOG || join10(process.env.MIXDOG_RUNTIME_ROOT || join10(tmpdir2(), "mixdog"), "mixdog-tui.stderr.log");
@@ -26628,7 +26748,7 @@ function paintBootSplash() {
26628
26748
  `;
26629
26749
  }
26630
26750
  out += "\r\n";
26631
- out += `${subtleFg}${center(`mixdog coding agent \xB7 ${process.cwd()}`)}${reset}`;
26751
+ out += `${subtleFg}${center(`mixdog coding agent \xB7 v${localPackageVersion()} \xB7 ${process.cwd()}`)}${reset}`;
26632
26752
  out += "\x1B[H";
26633
26753
  process.stdout.write(out);
26634
26754
  return { stop: () => {
@@ -26678,7 +26798,7 @@ function installTuiStderrGuard() {
26678
26798
  const originalWrite = process.stderr.write.bind(process.stderr);
26679
26799
  const logPath = resolveTuiStderrLogPath();
26680
26800
  try {
26681
- mkdirSync6(dirname9(logPath), { recursive: true });
26801
+ mkdirSync7(dirname9(logPath), { recursive: true });
26682
26802
  } catch {
26683
26803
  }
26684
26804
  (0, import_mixdog_debug.rotateBoundedLog)(logPath, import_mixdog_debug.PLUGIN_LOG_MAX_BYTES, import_mixdog_debug.PLUGIN_LOG_KEEP_BYTES);