@wrongstack/tui 0.306.2 → 0.306.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -291,9 +291,9 @@ var init_connections_health = __esm({
291
291
  });
292
292
 
293
293
  // src/app.tsx
294
- import * as path7 from "node:path";
294
+ import * as path9 from "node:path";
295
295
  import { applyRewindToConversation, DefaultSessionRewinder } from "@wrongstack/core/storage";
296
- import React9, { useCallback as useCallback25, useEffect as useEffect67, useRef as useRef39 } from "react";
296
+ import React9, { useCallback as useCallback26, useEffect as useEffect68, useRef as useRef39 } from "react";
297
297
 
298
298
  // src/hooks/use-terminal-size.ts
299
299
  import { onResize } from "@wrongstack/core/utils";
@@ -1553,6 +1553,7 @@ function RightSidebar({ width, maxHeight, focused = false, children }) {
1553
1553
 
1554
1554
  // src/ui-contracts.ts
1555
1555
  var SIDEBAR_MISSION_ROWS = 8;
1556
+ var SETTINGS_PICKER_MAX_HEIGHT = 24;
1556
1557
  var PANEL_POSITION_FIELD_START = 46;
1557
1558
  var SIDEBAR_PANEL_LIMIT = 6;
1558
1559
  var SIDEBAR_TWIN_MAX_WRAP_LINES = 10;
@@ -8063,10 +8064,10 @@ function MailboxPanel({
8063
8064
  messages,
8064
8065
  agents,
8065
8066
  unreadCount,
8066
- open
8067
+ open: open2
8067
8068
  }) {
8068
8069
  const { columns: termWidth } = useTerminalSize({ fallbackColumns: 90 });
8069
- if (!open) return null;
8070
+ if (!open2) return null;
8070
8071
  const showMessages = messages.slice(0, 6);
8071
8072
  const showAgents = agents.slice(0, 8);
8072
8073
  const maxSubjectLen = Math.max(15, Math.min(30, termWidth - 55));
@@ -8412,10 +8413,10 @@ function PlanPanel({
8412
8413
  setSelected((value) => Math.min(Math.max(0, items.length - 1), value + 1));
8413
8414
  }
8414
8415
  });
8415
- const open = items.filter((i) => i.status === "open");
8416
+ const open2 = items.filter((i) => i.status === "open");
8416
8417
  const inProgress = items.filter((i) => i.status === "in_progress");
8417
8418
  const done = items.filter((i) => i.status === "done");
8418
- const ordered = useMemo5(() => [...inProgress, ...open, ...done], [inProgress, open, done]);
8419
+ const ordered = useMemo5(() => [...inProgress, ...open2, ...done], [inProgress, open2, done]);
8419
8420
  useEffect11(() => {
8420
8421
  setSelected((value) => Math.min(Math.max(0, ordered.length - 1), value));
8421
8422
  }, [ordered.length]);
@@ -8468,7 +8469,7 @@ function PlanPanel({
8468
8469
  ] }),
8469
8470
  /* @__PURE__ */ jsxs19(Text, { color: theme.textMuted, children: [
8470
8471
  " \u25CB ",
8471
- open.length
8472
+ open2.length
8472
8473
  ] }),
8473
8474
  updatedAt && size.columns >= 105 ? /* @__PURE__ */ jsxs19(Text, { color: theme.textMuted, children: [
8474
8475
  " updated ",
@@ -11787,8 +11788,8 @@ function detectLang(fenceInfo) {
11787
11788
  const tag = fenceInfo.trim().toLowerCase().split(/\s+/)[0] ?? "";
11788
11789
  return LANG_ALIASES[tag] ?? "plain";
11789
11790
  }
11790
- function langFromPath(path10) {
11791
- const m = /\.([A-Za-z0-9]+)$/.exec(path10.trim());
11791
+ function langFromPath(path12) {
11792
+ const m = /\.([A-Za-z0-9]+)$/.exec(path12.trim());
11792
11793
  const ext = m?.[1]?.toLowerCase() ?? "";
11793
11794
  return LANG_ALIASES[ext] ?? "plain";
11794
11795
  }
@@ -12239,13 +12240,13 @@ function visualWorkBoard(toolName, text, ok) {
12239
12240
  function boardSummaryRows(toolName, fields, ok) {
12240
12241
  const success = fields["ok"] !== "false" && ok;
12241
12242
  const count = numberFromParsedField(fields, "count");
12242
- const open = numberFromParsedField(fields, "open");
12243
+ const open2 = numberFromParsedField(fields, "open");
12243
12244
  const completed = numberFromParsedField(fields, "completed");
12244
12245
  const inProgress = numberFromParsedField(fields, "inProgress") ?? numberFromParsedField(fields, "in_progress");
12245
12246
  const parts = [
12246
12247
  toolName,
12247
12248
  count !== void 0 ? `${count} item${count === 1 ? "" : "s"}` : void 0,
12248
- open !== void 0 ? `${open} open` : void 0,
12249
+ open2 !== void 0 ? `${open2} open` : void 0,
12249
12250
  completed !== void 0 ? `${completed} done` : void 0,
12250
12251
  inProgress !== void 0 && inProgress > 0 ? `${inProgress} in progress` : void 0
12251
12252
  ].filter(Boolean);
@@ -13079,19 +13080,19 @@ function formatToolArgs(toolName, input) {
13079
13080
  case "codebase-stats":
13080
13081
  return "index health";
13081
13082
  case "lsp_diagnostics": {
13082
- const path10 = stringOf(obj["path"]);
13083
+ const path12 = stringOf(obj["path"]);
13083
13084
  const severity = stringArrayOf(obj["severity"]) ?? stringArrayOf(obj["severities"]);
13084
- return [path10 ? shortenPath(path10, 40) : "workspace", severity?.join("+")].filter(Boolean).join(" \xB7 ");
13085
+ return [path12 ? shortenPath(path12, 40) : "workspace", severity?.join("+")].filter(Boolean).join(" \xB7 ");
13085
13086
  }
13086
13087
  case "lsp_definition":
13087
13088
  case "lsp_completion":
13088
13089
  case "lsp_rename": {
13089
- const path10 = stringOf(obj["path"]) ?? stringOf(obj["file"]);
13090
+ const path12 = stringOf(obj["path"]) ?? stringOf(obj["file"]);
13090
13091
  const line = numOf(obj["line"]);
13091
13092
  const character = numOf(obj["character"]);
13092
13093
  const position = line !== void 0 ? `L${line}:${character ?? 1}` : "";
13093
13094
  const rename = toolName === "lsp_rename" ? stringOf(obj["new_name"]) : void 0;
13094
- return [path10 ? shortenPath(path10, 38) : "", position, rename ? `\u2192 ${rename}` : ""].filter(Boolean).join(" \xB7 ");
13095
+ return [path12 ? shortenPath(path12, 38) : "", position, rename ? `\u2192 ${rename}` : ""].filter(Boolean).join(" \xB7 ");
13095
13096
  }
13096
13097
  case "set_working_dir": {
13097
13098
  const p = stringOf(obj["path"]);
@@ -13534,19 +13535,19 @@ function visualEdit(toolName, text, ok) {
13534
13535
  const parsed = tryParseJson(text);
13535
13536
  if (parsed && typeof parsed === "object") {
13536
13537
  const obj = parsed;
13537
- const path10 = typeof obj["path"] === "string" ? obj["path"] : void 0;
13538
+ const path12 = typeof obj["path"] === "string" ? obj["path"] : void 0;
13538
13539
  const replacements = typeof obj["replacements"] === "number" ? obj["replacements"] : void 0;
13539
13540
  const bytes = typeof obj["bytes"] === "number" ? obj["bytes"] : void 0;
13540
13541
  const created = obj["created"] === true;
13541
13542
  const files = Array.isArray(obj["files"]) ? obj["files"] : void 0;
13542
13543
  const results = Array.isArray(obj["results"]) ? obj["results"] : void 0;
13543
- if (toolName === "edit" && path10 !== void 0) {
13544
+ if (toolName === "edit" && path12 !== void 0) {
13544
13545
  const repText = replacements !== void 0 ? `${replacements} replacement${replacements === 1 ? "" : "s"}` : void 0;
13545
- rows.push({ kind: "ok", text: "", marker: "edit ", path: path10 });
13546
+ rows.push({ kind: "ok", text: "", marker: "edit ", path: path12 });
13546
13547
  if (repText) rows.push({ kind: "meta", text: repText });
13547
- } else if (toolName === "write" && path10 !== void 0) {
13548
+ } else if (toolName === "write" && path12 !== void 0) {
13548
13549
  const sizeText = bytes !== void 0 ? `${bytes} bytes` : created ? "new file" : "updated";
13549
- rows.push({ kind: "ok", text: "", marker: "write ", path: path10 });
13550
+ rows.push({ kind: "ok", text: "", marker: "write ", path: path12 });
13550
13551
  rows.push({ kind: "meta", text: sizeText });
13551
13552
  } else if ((toolName === "diff" || toolName === "patch") && files && files.length > 0) {
13552
13553
  rows.push({
@@ -13569,8 +13570,8 @@ function visualEdit(toolName, text, ok) {
13569
13570
  marker: "replace ",
13570
13571
  text: `${results.length} replacement${results.length === 1 ? "" : "s"} across ${fileCount} file${fileCount === 1 ? "" : "s"}`
13571
13572
  });
13572
- } else if (path10 !== void 0) {
13573
- rows.push({ kind: "ok", text: "", marker: `${toolName} `, path: path10 });
13573
+ } else if (path12 !== void 0) {
13574
+ rows.push({ kind: "ok", text: "", marker: `${toolName} `, path: path12 });
13574
13575
  } else {
13575
13576
  return void 0;
13576
13577
  }
@@ -13664,12 +13665,12 @@ function parseMatchHit(hit) {
13664
13665
  }
13665
13666
  if (hit && typeof hit === "object") {
13666
13667
  const o = hit;
13667
- const path10 = stringOf(o["file"]) ?? stringOf(o["path"]) ?? stringOf(o["url"]);
13668
+ const path12 = stringOf(o["file"]) ?? stringOf(o["path"]) ?? stringOf(o["url"]);
13668
13669
  const line = numOf(o["line"]) ?? numOf(o["lineNumber"]);
13669
13670
  const title = stringOf(o["title"]);
13670
13671
  const snippet2 = stringOf(o["snippet"]);
13671
13672
  const text = stringOf(o["text"]) ?? stringOf(o["match"]) ?? stringOf(o["preview"]) ?? [title, snippet2].filter(Boolean).join(" \u2014 ");
13672
- return { path: path10, line: line === void 0 ? void 0 : String(line), text };
13673
+ return { path: path12, line: line === void 0 ? void 0 : String(line), text };
13673
13674
  }
13674
13675
  return void 0;
13675
13676
  }
@@ -13767,8 +13768,8 @@ function splitReplaceDiffs(obj) {
13767
13768
  const record = result;
13768
13769
  const diff = stringOf(record["diff"]);
13769
13770
  if (!diff?.trim()) continue;
13770
- const path10 = stringOf(record["path"]);
13771
- items.push(path10 ? { path: path10, diff } : { diff });
13771
+ const path12 = stringOf(record["path"]);
13772
+ items.push(path12 ? { path: path12, diff } : { diff });
13772
13773
  }
13773
13774
  return items;
13774
13775
  }
@@ -13843,8 +13844,8 @@ function collectMultiFileDiffItems(toolName, output, _input) {
13843
13844
  if (!diffText?.trim()) return void 0;
13844
13845
  const blocks = splitGitStyleDiff(diffText);
13845
13846
  if (blocks.length === 0) {
13846
- const path10 = explicitFiles[0] ?? extractPatchHeaderPath(diffText);
13847
- return [{ path: path10, diff: diffText }];
13847
+ const path12 = explicitFiles[0] ?? extractPatchHeaderPath(diffText);
13848
+ return [{ path: path12, diff: diffText }];
13848
13849
  }
13849
13850
  return blocks.map((block, idx) => ({
13850
13851
  path: explicitFiles[idx] ?? block.path,
@@ -13878,9 +13879,9 @@ function newFileDiffFromWriteInput(output, input) {
13878
13879
  const obj = input;
13879
13880
  const content = stringOf(obj["content"]);
13880
13881
  if (content === void 0) return void 0;
13881
- const path10 = stringOf(output["path"]) ?? stringOf(obj["path"]) ?? "new file";
13882
+ const path12 = stringOf(output["path"]) ?? stringOf(obj["path"]) ?? "new file";
13882
13883
  const lines = content === "" ? [] : content.replace(/\n$/, "").split("\n");
13883
- const header = [`+++ ${path10}`, `@@ -0,0 +1,${lines.length} @@`];
13884
+ const header = [`+++ ${path12}`, `@@ -0,0 +1,${lines.length} @@`];
13884
13885
  return [...header, ...lines.map((line) => `+${line}`)].join("\n");
13885
13886
  }
13886
13887
  function splitGitStyleDiff(diff) {
@@ -13920,8 +13921,8 @@ function splitGitStyleDiff(diff) {
13920
13921
  }
13921
13922
  flush();
13922
13923
  if (blocks.length === 1 && !blocks[0].path) {
13923
- const path10 = extractPatchHeaderPath(blocks[0].diff);
13924
- if (path10) blocks[0] = { path: path10, diff: blocks[0].diff };
13924
+ const path12 = extractPatchHeaderPath(blocks[0].diff);
13925
+ if (path12) blocks[0] = { path: path12, diff: blocks[0].diff };
13925
13926
  }
13926
13927
  return blocks;
13927
13928
  }
@@ -13942,8 +13943,8 @@ function parseDiffGitPath(line) {
13942
13943
  function extractPatchHeaderPath(diffText) {
13943
13944
  for (const line of diffText.split("\n")) {
13944
13945
  if (line.startsWith("+++ ")) {
13945
- const path10 = line.slice(4).trim();
13946
- const cleaned = path10.replace(/^b\//, "").split(" ")[0].trim();
13946
+ const path12 = line.slice(4).trim();
13947
+ const cleaned = path12.replace(/^b\//, "").split(" ")[0].trim();
13947
13948
  return cleaned || void 0;
13948
13949
  }
13949
13950
  }
@@ -14075,13 +14076,13 @@ function formatMultiDiffSummary(summary, threshold = MULTI_DIFF_SUMMARY_THRESHOL
14075
14076
  return parts.join(" \xB7 ");
14076
14077
  }
14077
14078
  function DiffFileBlock({
14078
- path: path10,
14079
+ path: path12,
14079
14080
  preview,
14080
14081
  useColor = true,
14081
14082
  contentWidth
14082
14083
  }) {
14083
14084
  return /* @__PURE__ */ jsxs37(Box, { flexDirection: "column", marginTop: 1, children: [
14084
- /* @__PURE__ */ jsx38(Text, { dimColor: true, italic: true, children: path10 }),
14085
+ /* @__PURE__ */ jsx38(Text, { dimColor: true, italic: true, children: path12 }),
14085
14086
  /* @__PURE__ */ jsx38(
14086
14087
  DiffBlock,
14087
14088
  {
@@ -14092,7 +14093,7 @@ function DiffFileBlock({
14092
14093
  hiddenAdded: preview.hiddenAdded,
14093
14094
  hiddenRemoved: preview.hiddenRemoved,
14094
14095
  useColor,
14095
- lang: langFromPath(path10),
14096
+ lang: langFromPath(path12),
14096
14097
  contentWidth
14097
14098
  }
14098
14099
  )
@@ -14367,7 +14368,7 @@ function renderDiffLine(line) {
14367
14368
  "\n"
14368
14369
  ] }, line);
14369
14370
  }
14370
- function renderDiff(diff, path10) {
14371
+ function renderDiff(diff, path12) {
14371
14372
  const preview = parseUnifiedDiff(diff, CONFIRM_DIFF_MAX_LINES);
14372
14373
  if (preview.rows.length > 0) {
14373
14374
  return /* @__PURE__ */ jsx39(
@@ -14380,7 +14381,7 @@ function renderDiff(diff, path10) {
14380
14381
  hiddenAdded: preview.hiddenAdded,
14381
14382
  hiddenRemoved: preview.hiddenRemoved,
14382
14383
  useColor: theme.supportsBackground,
14383
- lang: langFromPath(path10 ?? "")
14384
+ lang: langFromPath(path12 ?? "")
14384
14385
  }
14385
14386
  );
14386
14387
  }
@@ -15501,8 +15502,8 @@ function FilePicker({ query, matches, selected }) {
15501
15502
  ] }, m))
15502
15503
  ] });
15503
15504
  }
15504
- function highlight(path10, _query) {
15505
- return path10;
15505
+ function highlight(path12, _query) {
15506
+ return path12;
15506
15507
  }
15507
15508
 
15508
15509
  // src/components/help-panel.tsx
@@ -19774,15 +19775,15 @@ var Entry = React4.memo(function Entry2({
19774
19775
  const outObj = outJson && typeof outJson === "object" ? outJson : void 0;
19775
19776
  const created = name === "write" && (outObj?.["created"] === true || /^[^\n]*\bcreated=true\b/.test(outputForFormatting));
19776
19777
  const verb = created ? "Write" : "Update";
19777
- const path10 = stringOf(inputObj?.["path"]) ?? stringOf(outObj?.["path"]);
19778
+ const path12 = stringOf(inputObj?.["path"]) ?? stringOf(outObj?.["path"]);
19778
19779
  const agg = multiDiffs2 ? summarizeMultiFileDiffs(multiDiffs2) : void 0;
19779
- const target = multiDiffs2 ? agg?.fileCount === 1 ? multiDiffs2[0].path : `${agg?.fileCount ?? multiDiffs2.length} files` : path10 ?? "file";
19780
+ const target = multiDiffs2 ? agg?.fileCount === 1 ? multiDiffs2[0].path : `${agg?.fileCount ?? multiDiffs2.length} files` : path12 ?? "file";
19780
19781
  return {
19781
19782
  verb,
19782
19783
  target,
19783
19784
  added: agg ? agg.added : diff2?.added ?? 0,
19784
19785
  removed: agg ? agg.removed : diff2?.removed ?? 0,
19785
- lang: langFromPath(multiDiffs2 ? "" : path10 ?? "")
19786
+ lang: langFromPath(multiDiffs2 ? "" : path12 ?? "")
19786
19787
  };
19787
19788
  })();
19788
19789
  return {
@@ -22479,7 +22480,10 @@ function SettingsPicker({
22479
22480
  const bottomChromeRows = statuslineMode === "detailed" ? 5 : 2;
22480
22481
  const BORDER_ROWS = 2;
22481
22482
  const BASE_CHROME_ROWS = contentWidth < 64 ? 4 : 3;
22482
- const pickerHeight = Math.max(8, termRows - INPUT_ROWS - bottomChromeRows);
22483
+ const pickerHeight = Math.min(
22484
+ SETTINGS_PICKER_MAX_HEIGHT,
22485
+ Math.max(BORDER_ROWS, termRows - INPUT_ROWS - bottomChromeRows)
22486
+ );
22483
22487
  const innerHeight = pickerHeight - BORDER_ROWS;
22484
22488
  const totalFields = fieldRowIndex.length;
22485
22489
  const sectionFieldStarts = deriveSettingsSectionFieldStarts(fieldRowIndex);
@@ -32094,7 +32098,7 @@ var LayoutStore = class {
32094
32098
  const filePath = path2.join(this.sessionDataDir, LAYOUT_FILENAME);
32095
32099
  let raw;
32096
32100
  try {
32097
- raw = await import("node:fs/promises").then((fs5) => fs5.readFile(filePath, "utf8"));
32101
+ raw = await import("node:fs/promises").then((fs6) => fs6.readFile(filePath, "utf8"));
32098
32102
  } catch {
32099
32103
  return 0;
32100
32104
  }
@@ -32133,12 +32137,12 @@ var LayoutStore = class {
32133
32137
  };
32134
32138
  const filePath = path2.join(this.sessionDataDir, LAYOUT_FILENAME);
32135
32139
  try {
32136
- const fs5 = await import("node:fs/promises");
32140
+ const fs6 = await import("node:fs/promises");
32137
32141
  const dir = path2.dirname(filePath);
32138
- await fs5.mkdir(dir, { recursive: true });
32142
+ await fs6.mkdir(dir, { recursive: true });
32139
32143
  const tmp = path2.join(dir, `.${path2.basename(filePath)}.${Date.now().toString(36)}.tmp`);
32140
- await fs5.writeFile(tmp, JSON.stringify(snapshot, null, 2), "utf8");
32141
- await fs5.rename(tmp, filePath);
32144
+ await fs6.writeFile(tmp, JSON.stringify(snapshot, null, 2), "utf8");
32145
+ await fs6.rename(tmp, filePath);
32142
32146
  } catch {
32143
32147
  }
32144
32148
  }
@@ -32228,7 +32232,7 @@ var OAUTH_TITLE = {
32228
32232
  copilot: "Sign in with GitHub Copilot"
32229
32233
  };
32230
32234
  function useAuthPanel(opts) {
32231
- const { authHost, stateRef, dispatch, open } = opts;
32235
+ const { authHost, stateRef, dispatch, open: open2 } = opts;
32232
32236
  const flowAbortRef = useRef11(null);
32233
32237
  const standaloneSecretRef = useRef11(false);
32234
32238
  const promptRef = useRef11(null);
@@ -32249,8 +32253,8 @@ function useAuthPanel(opts) {
32249
32253
  };
32250
32254
  }, [abortLiveFlow]);
32251
32255
  useEffect28(() => {
32252
- if (!open) abortLiveFlow();
32253
- }, [open, abortLiveFlow]);
32256
+ if (!open2) abortLiveFlow();
32257
+ }, [open2, abortLiveFlow]);
32254
32258
  const reloadProviders = useCallback7(async () => {
32255
32259
  if (!authHost || !mountedRef.current) return;
32256
32260
  try {
@@ -34952,6 +34956,45 @@ function renderList2() {
34952
34956
  return lines.join("\n");
34953
34957
  }
34954
34958
 
34959
+ // src/slash-command-lifecycle.ts
34960
+ function registerSlashCommandLifecycle(registry, command, options = {}) {
34961
+ const owner = options.owner ?? "core";
34962
+ const displaced = /* @__PURE__ */ new Map();
34963
+ for (const key of [command.name, ...command.aliases ?? []]) {
34964
+ const previous = registry.get(key);
34965
+ const previousOwner = registry.ownerOf(key);
34966
+ if (previous && previousOwner && previous !== command && !displaced.has(previous)) {
34967
+ displaced.set(previous, { command: previous, owner: previousOwner });
34968
+ }
34969
+ }
34970
+ registry.register(command, owner, { official: options.official });
34971
+ const namespacedName = owner === "core" ? null : `${owner}:${command.name}`;
34972
+ let cleaned = false;
34973
+ return () => {
34974
+ if (cleaned) return;
34975
+ cleaned = true;
34976
+ const ownsBareName = registry.get(command.name) === command;
34977
+ const ownsNamespace = namespacedName !== null && registry.get(namespacedName) === command;
34978
+ if (ownsBareName) registry.unregister(command.name);
34979
+ else if (ownsNamespace && namespacedName) registry.unregister(namespacedName);
34980
+ else return;
34981
+ for (const registration of displaced.values()) {
34982
+ const keys = [registration.command.name, ...registration.command.aliases ?? []];
34983
+ if (keys.some((key) => {
34984
+ const current = registry.get(key);
34985
+ return current !== void 0 && current !== registration.command;
34986
+ })) {
34987
+ continue;
34988
+ }
34989
+ registry.register(
34990
+ registration.command,
34991
+ registration.owner,
34992
+ registration.owner === "core" ? void 0 : { official: true }
34993
+ );
34994
+ }
34995
+ };
34996
+ }
34997
+
34955
34998
  // src/steering-preamble.ts
34956
34999
  function buildSteeringPreamble(snapshot, newDirection) {
34957
35000
  const lines = ["[STEERING \u2014 I pressed Esc to interrupt you mid-task on purpose.", ""];
@@ -35012,67 +35055,69 @@ function useCoreTuiCommands({
35012
35055
  const stdout = { columns: terminalWidth };
35013
35056
  const getCronJobs = useMemo10(() => createCronJobsGetter(agent), [agent]);
35014
35057
  useEffect33(() => {
35015
- slashRegistry.register(createKillSlashCommand());
35016
- slashRegistry.register(createPsSlashCommand());
35017
- slashRegistry.register(createCronSlashCommand({ getCronJobs }));
35058
+ const cleanups = [];
35059
+ cleanups.push(registerSlashCommandLifecycle(slashRegistry, createKillSlashCommand()));
35060
+ cleanups.push(registerSlashCommandLifecycle(slashRegistry, createPsSlashCommand()));
35061
+ cleanups.push(
35062
+ registerSlashCommandLifecycle(slashRegistry, createCronSlashCommand({ getCronJobs }))
35063
+ );
35018
35064
  if (memoryStore) {
35019
- slashRegistry.register(createMemorySlashCommand({ memoryStore }));
35065
+ cleanups.push(
35066
+ registerSlashCommandLifecycle(
35067
+ slashRegistry,
35068
+ createMemorySlashCommand({ memoryStore })
35069
+ )
35070
+ );
35020
35071
  }
35021
- {
35022
- const existingContext = slashRegistry.get("context");
35023
- if (!existingContext?.__panelWrapper) {
35024
- if (existingContext) slashRegistry.unregister("context");
35025
- const contextPanelCmd = createContextSlashCommand({
35026
- onPanelOpen,
35027
- fallback: existingContext,
35028
- getSummary: () => {
35029
- const leader = stateRef.current.leader;
35030
- const memories = Object.values(memoryContextMonitorRef.current.memories);
35031
- return {
35032
- contextPct: leader.ctxPct,
35033
- contextTokens: leader.ctxTokens,
35034
- contextMaxTokens: leader.ctxMaxTokens,
35035
- memoryTotal: memoryRecordTotalRef.current,
35036
- memoryCtx: memories.filter((memory) => memory.state === "active").length,
35037
- memoryPending: memories.filter((memory) => memory.state === "injected").length,
35038
- memoryLeft: memories.filter((memory) => memory.state === "exited").length
35039
- };
35040
- }
35041
- });
35042
- contextPanelCmd.__panelWrapper = true;
35043
- slashRegistry.register(contextPanelCmd);
35072
+ const existingContext = slashRegistry.get("context");
35073
+ const contextPanelCmd = createContextSlashCommand({
35074
+ onPanelOpen,
35075
+ fallback: existingContext,
35076
+ getSummary: () => {
35077
+ const leader = stateRef.current.leader;
35078
+ const memories = Object.values(memoryContextMonitorRef.current.memories);
35079
+ return {
35080
+ contextPct: leader.ctxPct,
35081
+ contextTokens: leader.ctxTokens,
35082
+ contextMaxTokens: leader.ctxMaxTokens,
35083
+ memoryTotal: memoryRecordTotalRef.current,
35084
+ memoryCtx: memories.filter((memory) => memory.state === "active").length,
35085
+ memoryPending: memories.filter((memory) => memory.state === "injected").length,
35086
+ memoryLeft: memories.filter((memory) => memory.state === "exited").length
35087
+ };
35044
35088
  }
35045
- }
35046
- slashRegistry.register(
35047
- createConnectionsSlashCommand({ onPanelOpen })
35048
- );
35049
- slashRegistry.register(
35050
- createKanbanSlashCommand({
35051
- projectRoot: agent.ctx.projectRoot,
35052
- sessionId: agent.ctx.session?.id ?? null,
35053
- onPanelOpen,
35054
- // Goal → Kanban bridge & `/kanban use <boardId>` both arrive here.
35055
- // We capture a `focusedBoardId` and pass it down as `initialBoardId`
35056
- // so the panel lands on the requested board rather than defaulting
35057
- // to the session-tag fallback.
35058
- onBoardFocus: boardFocusRef.current ? boardFocusRef.current : {
35059
- current: (boardId) => {
35060
- setFocusedBoardId(boardId);
35061
- return true;
35062
- }
35063
- },
35064
- terminalWidth: stdout.columns ?? 80
35089
+ });
35090
+ cleanups.push(
35091
+ registerSlashCommandLifecycle(slashRegistry, contextPanelCmd, {
35092
+ owner: "tui",
35093
+ official: true
35065
35094
  })
35066
35095
  );
35096
+ cleanups.push(
35097
+ registerSlashCommandLifecycle(
35098
+ slashRegistry,
35099
+ createConnectionsSlashCommand({ onPanelOpen })
35100
+ )
35101
+ );
35102
+ cleanups.push(
35103
+ registerSlashCommandLifecycle(
35104
+ slashRegistry,
35105
+ createKanbanSlashCommand({
35106
+ projectRoot: agent.ctx.projectRoot,
35107
+ sessionId: agent.ctx.session?.id ?? null,
35108
+ onPanelOpen,
35109
+ onBoardFocus: boardFocusRef.current ? boardFocusRef.current : {
35110
+ current: (boardId) => {
35111
+ setFocusedBoardId(boardId);
35112
+ return true;
35113
+ }
35114
+ },
35115
+ terminalWidth: stdout.columns ?? 80
35116
+ })
35117
+ )
35118
+ );
35067
35119
  return () => {
35068
- slashRegistry.unregister("kill");
35069
- slashRegistry.unregister("ps");
35070
- slashRegistry.unregister("cron");
35071
- if (memoryStore) {
35072
- slashRegistry.unregister("memory");
35073
- }
35074
- slashRegistry.unregister("context");
35075
- slashRegistry.unregister("kanban");
35120
+ for (const cleanup of cleanups.reverse()) cleanup();
35076
35121
  };
35077
35122
  }, [
35078
35123
  slashRegistry,
@@ -35143,10 +35188,7 @@ function useCoreTuiCommands({
35143
35188
  };
35144
35189
  }
35145
35190
  };
35146
- slashRegistry.register(cmd);
35147
- return () => {
35148
- slashRegistry.unregister("steer");
35149
- };
35191
+ return registerSlashCommandLifecycle(slashRegistry, cmd);
35150
35192
  }, [slashRegistry, director, runSteerSequence]);
35151
35193
  useEffect33(() => {
35152
35194
  const cmd = {
@@ -35180,29 +35222,29 @@ function useCoreTuiCommands({
35180
35222
  return {};
35181
35223
  }
35182
35224
  };
35183
- slashRegistry.register(cmd);
35184
- return () => {
35185
- slashRegistry.unregister("rewind");
35186
- };
35225
+ return registerSlashCommandLifecycle(slashRegistry, cmd);
35187
35226
  }, [slashRegistry, handleRewindTo]);
35188
35227
  useEffect33(() => {
35228
+ const original = slashRegistry.get("agents");
35189
35229
  const cmd = {
35190
35230
  name: "agents",
35191
- description: "Toggle the agents monitor overlay.",
35192
- async run(args) {
35231
+ description: original?.description ?? "Toggle the agents monitor overlay.",
35232
+ argsHint: original?.argsHint,
35233
+ help: original?.help,
35234
+ async run(args, ctx) {
35193
35235
  const arg = args.trim().toLowerCase();
35194
35236
  if (!arg || arg === "monitor") {
35195
35237
  dispatch({ type: "toggleAgentsMonitor" });
35196
35238
  return { message: void 0 };
35197
35239
  }
35198
- return { message: void 0 };
35240
+ return original?.run(args, ctx) ?? { message: "/agents is unavailable." };
35199
35241
  }
35200
35242
  };
35201
- slashRegistry.register(cmd);
35202
- return () => {
35203
- slashRegistry.unregister("agents");
35204
- };
35205
- }, [slashRegistry]);
35243
+ return registerSlashCommandLifecycle(slashRegistry, cmd, {
35244
+ owner: "tui",
35245
+ official: true
35246
+ });
35247
+ }, [slashRegistry, dispatch]);
35206
35248
  return { getCronJobs, runSteerSequence };
35207
35249
  }
35208
35250
 
@@ -35983,6 +36025,7 @@ async function searchFiles(root, query, limit = 8) {
35983
36025
  }
35984
36026
 
35985
36027
  // src/hooks/use-file-search.ts
36028
+ var FILE_SEARCH_DEBOUNCE_MS = 150;
35986
36029
  function detectAtToken(buffer, cursor) {
35987
36030
  let i = cursor - 1;
35988
36031
  while (i >= 0) {
@@ -36006,22 +36049,34 @@ function useFileSearch(options) {
36006
36049
  if (state.picker.open) dispatch({ type: "pickerClose" });
36007
36050
  return;
36008
36051
  }
36009
- if (!state.picker.open || state.picker.query !== detected.query) {
36052
+ const justOpened = !state.picker.open;
36053
+ if (justOpened || state.picker.query !== detected.query) {
36010
36054
  dispatch({ type: "pickerOpen", query: detected.query });
36011
36055
  }
36012
36056
  let cancelled = false;
36013
- searchFiles(projectRoot, detected.query, 8).then((matches) => {
36014
- if (!cancelled) {
36015
- dispatch({ type: "pickerSetMatches", query: detected.query, matches });
36016
- }
36017
- }).catch(() => void 0);
36057
+ const run = () => {
36058
+ searchFiles(projectRoot, detected.query, 8).then((matches) => {
36059
+ if (!cancelled) {
36060
+ dispatch({ type: "pickerSetMatches", query: detected.query, matches });
36061
+ }
36062
+ }).catch(() => void 0);
36063
+ };
36064
+ if (justOpened) {
36065
+ run();
36066
+ return () => {
36067
+ cancelled = true;
36068
+ };
36069
+ }
36070
+ const timer = setTimeout(run, FILE_SEARCH_DEBOUNCE_MS);
36071
+ timer.unref?.();
36018
36072
  return () => {
36019
36073
  cancelled = true;
36074
+ clearTimeout(timer);
36020
36075
  };
36021
36076
  }, [state.buffer, state.cursor, projectRoot]);
36022
36077
  const acceptPickerSelection = async () => {
36023
- const { open, matches, selected } = state.picker;
36024
- if (!open || matches.length === 0) return;
36078
+ const { open: open2, matches, selected } = state.picker;
36079
+ if (!open2 || matches.length === 0) return;
36025
36080
  const picked = matches[selected];
36026
36081
  if (!picked) return;
36027
36082
  const builder = builderRef.current;
@@ -36034,10 +36089,10 @@ function useFileSearch(options) {
36034
36089
  }
36035
36090
  const absPath = path4.isAbsolute(picked) ? picked : path4.join(projectRoot, picked);
36036
36091
  try {
36037
- const stat3 = await fs3.stat(absPath);
36092
+ const stat4 = await fs3.stat(absPath);
36038
36093
  let data;
36039
- if (stat3.size > TEXT_FILE_ATTACHMENT_INLINE_MAX_BYTES) {
36040
- data = fileReadContract(picked, stat3.size);
36094
+ if (stat4.size > TEXT_FILE_ATTACHMENT_INLINE_MAX_BYTES) {
36095
+ data = fileReadContract(picked, stat4.size);
36041
36096
  } else {
36042
36097
  const inline = await fs3.readFile(absPath, "utf8");
36043
36098
  const bytes = Buffer.byteLength(inline, "utf8");
@@ -36255,11 +36310,252 @@ function useHelpPanel(dispatch, slashRegistry) {
36255
36310
  return { openHelpPanel };
36256
36311
  }
36257
36312
 
36313
+ // src/hooks/use-history-archive.ts
36314
+ import * as fsp2 from "node:fs/promises";
36315
+ import * as path6 from "node:path";
36316
+ import { useCallback as useCallback14, useEffect as useEffect39, useRef as useRef20, useState as useState27 } from "react";
36317
+
36318
+ // src/history-archive.ts
36319
+ import * as fsp from "node:fs/promises";
36320
+ import * as path5 from "node:path";
36321
+ var ARCHIVE_PAGE_SIZE = 100;
36322
+ var HistoryArchive = class {
36323
+ filePath;
36324
+ handle = null;
36325
+ closed = false;
36326
+ /** Byte-offset index: index → { offset, length }. Built on first load. */
36327
+ index = null;
36328
+ /** Total entries written so far (monotonic counter). */
36329
+ // ── Write serialisation ──────────────────────────────────────────────
36330
+ writeChain = Promise.resolve();
36331
+ drainToken = null;
36332
+ constructor(sessionDir) {
36333
+ this.filePath = path5.join(sessionDir, "history-archive.jsonl");
36334
+ }
36335
+ /**
36336
+ * Append an entry to the archive. Fire-and-forget: the write is queued
36337
+ * on an internal promise chain so callers never block. Returns immediately.
36338
+ */
36339
+ append(entry) {
36340
+ if (this.closed) return;
36341
+ const line = JSON.stringify(entry) + "\n";
36342
+ this.enqueueWrite(line);
36343
+ }
36344
+ /**
36345
+ * Load a range of entries by index. Indices are 0-based and sequential
36346
+ * across the entire file (index 0 = oldest archived entry).
36347
+ *
36348
+ * Returns the loaded entries in chronological order (oldest first).
36349
+ * When the range exceeds the available data, returns whatever fits.
36350
+ */
36351
+ async loadRange(startIndex, count) {
36352
+ if (this.closed || count <= 0) return [];
36353
+ await this.buildIndex();
36354
+ if (!this.index) return [];
36355
+ const clampedStart = Math.max(0, startIndex);
36356
+ const clampedEnd = Math.min(clampedStart + count, this.index.length);
36357
+ if (clampedStart >= clampedEnd) return [];
36358
+ const handle = await this.ensureOpen();
36359
+ const slice = this.index.slice(clampedStart, clampedEnd);
36360
+ const entries = [];
36361
+ for (const slot of slice) {
36362
+ const buf = Buffer.allocUnsafe(slot.length);
36363
+ await handle.read(buf, 0, slot.length, slot.offset);
36364
+ const text = buf.toString("utf8").trim();
36365
+ if (!text) continue;
36366
+ try {
36367
+ const parsed = JSON.parse(text);
36368
+ entries.push(parsed);
36369
+ } catch {
36370
+ }
36371
+ }
36372
+ return entries;
36373
+ }
36374
+ /** How many entries are archived on disk. */
36375
+ get archivedCount() {
36376
+ return this.index?.length ?? 0;
36377
+ }
36378
+ /**
36379
+ * Close the file handle and release resources. Idempotent.
36380
+ * The write chain is drained before closing so no in-flight write
36381
+ * is lost.
36382
+ */
36383
+ async close() {
36384
+ if (this.closed) return;
36385
+ this.closed = true;
36386
+ await this.writeChain;
36387
+ if (this.handle) {
36388
+ try {
36389
+ await this.handle.close();
36390
+ } catch {
36391
+ }
36392
+ this.handle = null;
36393
+ }
36394
+ }
36395
+ // ── Internals ────────────────────────────────────────────────────────
36396
+ async ensureOpen() {
36397
+ if (this.handle) return this.handle;
36398
+ await fsp.mkdir(path5.dirname(this.filePath), { recursive: true });
36399
+ this.handle = await fsp.open(this.filePath, "a+", 384);
36400
+ return this.handle;
36401
+ }
36402
+ /**
36403
+ * Build the byte-offset index by scanning the archive file.
36404
+ * Rebuilds when `this.index` is null (first use or after rotation).
36405
+ * The index is cached for the lifetime of the archive object.
36406
+ */
36407
+ async buildIndex() {
36408
+ if (this.index !== null) return;
36409
+ const handle = await this.ensureOpen();
36410
+ const stat4 = await handle.stat();
36411
+ if (stat4.size === 0) {
36412
+ this.index = [];
36413
+ return;
36414
+ }
36415
+ const buf = Buffer.allocUnsafe(stat4.size);
36416
+ await handle.read(buf, 0, stat4.size, 0);
36417
+ const text = buf.toString("utf8");
36418
+ const lines = text.split("\n");
36419
+ const idx = [];
36420
+ let offset = 0;
36421
+ for (let i = 0; i < lines.length; i++) {
36422
+ const line = lines[i];
36423
+ const lineBytes = Buffer.byteLength(line, "utf8") + 1;
36424
+ if (line.trim().length === 0) {
36425
+ offset += lineBytes;
36426
+ continue;
36427
+ }
36428
+ idx.push({ index: idx.length, offset, length: lineBytes });
36429
+ offset += lineBytes;
36430
+ }
36431
+ this.index = idx;
36432
+ }
36433
+ /** Serialise writes through a FIFO promise chain with fresh-token coalescing. */
36434
+ enqueueWrite(line) {
36435
+ if (this.drainToken) {
36436
+ this.drainToken.buffer.push(line);
36437
+ return;
36438
+ }
36439
+ let resolve;
36440
+ const promise = new Promise((r) => {
36441
+ resolve = r;
36442
+ });
36443
+ this.drainToken = { buffer: [line], promise, resolve };
36444
+ const doWrite = async () => {
36445
+ const token = this.drainToken;
36446
+ this.drainToken = null;
36447
+ try {
36448
+ const batch = token.buffer.join("");
36449
+ const handle = await this.ensureOpen();
36450
+ await handle.appendFile(batch, "utf8");
36451
+ this.index = null;
36452
+ } catch {
36453
+ } finally {
36454
+ token.resolve();
36455
+ }
36456
+ };
36457
+ this.writeChain = this.writeChain.then(doWrite, doWrite);
36458
+ }
36459
+ };
36460
+ function historyArchivePath(sessionDir) {
36461
+ return path5.join(sessionDir, "history-archive.jsonl");
36462
+ }
36463
+
36464
+ // src/hooks/use-history-archive.ts
36465
+ function useHistoryArchive({
36466
+ entries,
36467
+ dispatch,
36468
+ sessionsDir,
36469
+ sessionId
36470
+ }) {
36471
+ const archiveRef = useRef20(null);
36472
+ const lastArchivedIdRef = useRef20(-1);
36473
+ const appendedCountRef = useRef20(0);
36474
+ const nextLoadEndRef = useRef20(null);
36475
+ const loadingRef = useRef20(false);
36476
+ const entriesRef = useRef20(entries);
36477
+ entriesRef.current = entries;
36478
+ const [archiveGeneration, setArchiveGeneration] = useState27(0);
36479
+ useEffect39(() => {
36480
+ if (!sessionsDir || !sessionId) return;
36481
+ let disposed = false;
36482
+ let created = null;
36483
+ const directory = path6.join(sessionsDir, sessionId);
36484
+ void (async () => {
36485
+ try {
36486
+ await fsp2.rm(historyArchivePath(directory), { force: true });
36487
+ } catch {
36488
+ return;
36489
+ }
36490
+ if (disposed) return;
36491
+ created = new HistoryArchive(directory);
36492
+ archiveRef.current = created;
36493
+ setArchiveGeneration((generation) => generation + 1);
36494
+ })();
36495
+ return () => {
36496
+ disposed = true;
36497
+ archiveRef.current = null;
36498
+ lastArchivedIdRef.current = -1;
36499
+ appendedCountRef.current = 0;
36500
+ nextLoadEndRef.current = null;
36501
+ void created?.close();
36502
+ };
36503
+ }, [sessionsDir, sessionId]);
36504
+ useEffect39(() => {
36505
+ const archive = archiveRef.current;
36506
+ if (!archive) return;
36507
+ const since = lastArchivedIdRef.current;
36508
+ let highest = since;
36509
+ let appended = 0;
36510
+ for (const entry of entries) {
36511
+ if (entry.id <= since) continue;
36512
+ archive.append(entry);
36513
+ appended++;
36514
+ if (entry.id > highest) highest = entry.id;
36515
+ }
36516
+ if (appended === 0) return;
36517
+ lastArchivedIdRef.current = highest;
36518
+ appendedCountRef.current += appended;
36519
+ }, [entries, archiveGeneration]);
36520
+ const onRequestOlderEntries = useCallback14(() => {
36521
+ if (loadingRef.current) return;
36522
+ const settleEmpty = () => {
36523
+ dispatch({ type: "startArchiveLoad" });
36524
+ dispatch({ type: "archiveLoaded", entries: [] });
36525
+ };
36526
+ const archive = archiveRef.current;
36527
+ if (!archive) {
36528
+ settleEmpty();
36529
+ return;
36530
+ }
36531
+ if (nextLoadEndRef.current === null) {
36532
+ nextLoadEndRef.current = Math.max(0, appendedCountRef.current - entriesRef.current.length);
36533
+ }
36534
+ const end = nextLoadEndRef.current;
36535
+ if (end <= 0) {
36536
+ settleEmpty();
36537
+ return;
36538
+ }
36539
+ const start = Math.max(0, end - ARCHIVE_PAGE_SIZE);
36540
+ loadingRef.current = true;
36541
+ dispatch({ type: "startArchiveLoad" });
36542
+ void archive.loadRange(start, end - start).then((loaded) => {
36543
+ nextLoadEndRef.current = start;
36544
+ dispatch({ type: "archiveLoaded", entries: loaded });
36545
+ }).catch(() => {
36546
+ dispatch({ type: "archiveLoaded", entries: [] });
36547
+ }).finally(() => {
36548
+ loadingRef.current = false;
36549
+ });
36550
+ }, [dispatch]);
36551
+ return { onRequestOlderEntries };
36552
+ }
36553
+
36258
36554
  // src/hooks/use-history-copy-notice.ts
36259
- import { useCallback as useCallback14, useEffect as useEffect39, useRef as useRef20 } from "react";
36555
+ import { useCallback as useCallback15, useEffect as useEffect40, useRef as useRef21 } from "react";
36260
36556
  function useHistoryCopyNotice(dispatch) {
36261
- const copiedHintTimerRef = useRef20(null);
36262
- const onHistoryCopy = useCallback14(
36557
+ const copiedHintTimerRef = useRef21(null);
36558
+ const onHistoryCopy = useCallback15(
36263
36559
  (entryId) => {
36264
36560
  dispatch({ type: "copiedNotice", text: "\u2713 Copied", entryId });
36265
36561
  if (copiedHintTimerRef.current) clearTimeout(copiedHintTimerRef.current);
@@ -36270,7 +36566,7 @@ function useHistoryCopyNotice(dispatch) {
36270
36566
  },
36271
36567
  [dispatch]
36272
36568
  );
36273
- useEffect39(
36569
+ useEffect40(
36274
36570
  () => () => {
36275
36571
  if (copiedHintTimerRef.current) clearTimeout(copiedHintTimerRef.current);
36276
36572
  },
@@ -36280,15 +36576,15 @@ function useHistoryCopyNotice(dispatch) {
36280
36576
  }
36281
36577
 
36282
36578
  // src/hooks/use-history-viewport-sync.ts
36283
- import { useEffect as useEffect40, useLayoutEffect as useLayoutEffect2, useRef as useRef21, useState as useState27 } from "react";
36579
+ import { useEffect as useEffect41, useLayoutEffect as useLayoutEffect2, useRef as useRef22, useState as useState28 } from "react";
36284
36580
  function useHistoryViewportSync(input) {
36285
36581
  const { stdoutRows, viewportRows, setViewportRows } = input;
36286
- const bottomRegionRef = useRef21(null);
36287
- const statusBarWrapRef = useRef21(null);
36288
- const belowStatusBarRef = useRef21(null);
36289
- const [termRows, setTermRows] = useState27(stdoutRows ?? 24);
36290
- const [statusBarRows, setStatusBarRows] = useState27(2);
36291
- useEffect40(() => {
36582
+ const bottomRegionRef = useRef22(null);
36583
+ const statusBarWrapRef = useRef22(null);
36584
+ const belowStatusBarRef = useRef22(null);
36585
+ const [termRows, setTermRows] = useState28(stdoutRows ?? 24);
36586
+ const [statusBarRows, setStatusBarRows] = useState28(2);
36587
+ useEffect41(() => {
36292
36588
  const handleResize = () => setTermRows(process.stdout.rows ?? 24);
36293
36589
  process.stdout.prependListener("resize", handleResize);
36294
36590
  return () => {
@@ -36316,7 +36612,7 @@ function useHistoryViewportSync(input) {
36316
36612
 
36317
36613
  // src/hooks/use-initial-prompt.ts
36318
36614
  import { buildGoalPreamble } from "@wrongstack/core/execution";
36319
- import { useEffect as useEffect41, useRef as useRef22 } from "react";
36615
+ import { useEffect as useEffect42, useRef as useRef23 } from "react";
36320
36616
  function useInitialPrompt({
36321
36617
  initialGoal,
36322
36618
  initialAsk,
@@ -36324,8 +36620,8 @@ function useInitialPrompt({
36324
36620
  runBlocksRef,
36325
36621
  dispatch
36326
36622
  }) {
36327
- const bootInjectedRef = useRef22(false);
36328
- useEffect41(() => {
36623
+ const bootInjectedRef = useRef23(false);
36624
+ useEffect42(() => {
36329
36625
  if (bootInjectedRef.current) return;
36330
36626
  const goal = initialGoal?.trim();
36331
36627
  const ask = initialAsk?.trim();
@@ -36372,7 +36668,7 @@ function useInitialPrompt({
36372
36668
  import { DefaultSecretScrubber } from "@wrongstack/core/security";
36373
36669
  import { INPUT_HISTORY_DEFAULT_MAX, InputHistoryStore } from "@wrongstack/core/storage";
36374
36670
  import { resolveWstackPaths as resolveWstackPaths3 } from "@wrongstack/core/utils";
36375
- import { useEffect as useEffect42, useMemo as useMemo12, useRef as useRef23 } from "react";
36671
+ import { useEffect as useEffect43, useMemo as useMemo12, useRef as useRef24 } from "react";
36376
36672
  function useInputHistoryPersistence({
36377
36673
  projectRoot,
36378
36674
  inputHistory,
@@ -36383,10 +36679,10 @@ function useInputHistoryPersistence({
36383
36679
  const file = resolveWstackPaths3({ projectRoot }).projectInputHistory;
36384
36680
  return new InputHistoryStore(file, new DefaultSecretScrubber(), INPUT_HISTORY_DEFAULT_MAX);
36385
36681
  }, [projectRoot]);
36386
- const inputHistoryLoadedRef = useRef23(false);
36387
- const inputHistorySaveTimerRef = useRef23(void 0);
36388
- const inputHistoryLastSavedRef = useRef23("");
36389
- useEffect42(() => {
36682
+ const inputHistoryLoadedRef = useRef24(false);
36683
+ const inputHistorySaveTimerRef = useRef24(void 0);
36684
+ const inputHistoryLastSavedRef = useRef24("");
36685
+ useEffect43(() => {
36390
36686
  if (!inputHistoryStore) return;
36391
36687
  let cancelled = false;
36392
36688
  inputHistoryStore.load().then((entries) => {
@@ -36402,7 +36698,7 @@ function useInputHistoryPersistence({
36402
36698
  cancelled = true;
36403
36699
  };
36404
36700
  }, [inputHistoryStore, dispatch]);
36405
- useEffect42(() => {
36701
+ useEffect43(() => {
36406
36702
  if (!inputHistoryStore || !inputHistoryLoadedRef.current) return;
36407
36703
  const snapshot = JSON.stringify(inputHistory);
36408
36704
  if (snapshot === inputHistoryLastSavedRef.current) return;
@@ -36421,9 +36717,9 @@ function useInputHistoryPersistence({
36421
36717
  // src/hooks/use-interrupt-ladder.ts
36422
36718
  import { getProcessRegistry as getProcessRegistry8 } from "@wrongstack/tools";
36423
36719
  import {
36424
- useCallback as useCallback15,
36425
- useEffect as useEffect43,
36426
- useRef as useRef24
36720
+ useCallback as useCallback16,
36721
+ useEffect as useEffect44,
36722
+ useRef as useRef25
36427
36723
  } from "react";
36428
36724
  var INTERRUPT_PRESS_WINDOW_MS = 2e3;
36429
36725
  function useInterruptLadder({
@@ -36444,9 +36740,9 @@ function useInterruptLadder({
36444
36740
  getSddRun,
36445
36741
  confirmExitRef
36446
36742
  }) {
36447
- const interruptsSyncRef = useRef24(0);
36448
- const lastPressAtRef = useRef24(0);
36449
- const runInterruptLadder = useCallback15(() => {
36743
+ const interruptsSyncRef = useRef25(0);
36744
+ const lastPressAtRef = useRef25(0);
36745
+ const runInterruptLadder = useCallback16(() => {
36450
36746
  const current = stateRef.current;
36451
36747
  const now = Date.now();
36452
36748
  if (interruptsSyncRef.current > 0 && current.status === "idle" && now - lastPressAtRef.current > INTERRUPT_PRESS_WINDOW_MS) {
@@ -36621,7 +36917,7 @@ function useInterruptLadder({
36621
36917
  onExit,
36622
36918
  exit
36623
36919
  ]);
36624
- useEffect43(() => {
36920
+ useEffect44(() => {
36625
36921
  process.on("SIGINT", runInterruptLadder);
36626
36922
  return () => {
36627
36923
  process.off("SIGINT", runInterruptLadder);
@@ -36631,10 +36927,10 @@ function useInterruptLadder({
36631
36927
  }
36632
36928
 
36633
36929
  // src/hooks/use-kanban-board-focus.ts
36634
- import { useRef as useRef25, useState as useState28 } from "react";
36930
+ import { useRef as useRef26, useState as useState29 } from "react";
36635
36931
  function useKanbanBoardFocus() {
36636
- const [focusedBoardId, setFocusedBoardId] = useState28(null);
36637
- const boardFocusRef = useRef25(null);
36932
+ const [focusedBoardId, setFocusedBoardId] = useState29(null);
36933
+ const boardFocusRef = useRef26(null);
36638
36934
  if (!boardFocusRef.current) {
36639
36935
  boardFocusRef.current = {
36640
36936
  current: (boardId) => {
@@ -36647,7 +36943,7 @@ function useKanbanBoardFocus() {
36647
36943
  }
36648
36944
 
36649
36945
  // src/hooks/use-live-settings-state.ts
36650
- import { useEffect as useEffect44, useRef as useRef26 } from "react";
36946
+ import { useEffect as useEffect45, useRef as useRef27 } from "react";
36651
36947
  function useLiveSettingsState({
36652
36948
  getSettings,
36653
36949
  titleController,
@@ -36658,12 +36954,12 @@ function useLiveSettingsState({
36658
36954
  const liveStatuslineMode = liveSettings?.statuslineMode ?? DEFAULT_STATUSLINE_MODE;
36659
36955
  const liveAnimationStyle = liveSettings?.animationStyle ?? "rainbow";
36660
36956
  const liveThinkingWord = liveSettings?.thinkingWord ?? "thinking";
36661
- const chimeRef = useRef26(chime);
36957
+ const chimeRef = useRef27(chime);
36662
36958
  chimeRef.current = liveSettings?.chime ?? chime;
36663
- const confirmExitRef = useRef26(confirmExit);
36959
+ const confirmExitRef = useRef27(confirmExit);
36664
36960
  confirmExitRef.current = liveSettings?.confirmExit ?? confirmExit;
36665
36961
  const liveTitleAnimation = liveSettings?.titleAnimation;
36666
- useEffect44(() => {
36962
+ useEffect45(() => {
36667
36963
  if (!titleController) return;
36668
36964
  titleController.setEnabled(liveTitleAnimation !== false);
36669
36965
  }, [titleController, liveTitleAnimation]);
@@ -36678,10 +36974,10 @@ function useLiveSettingsState({
36678
36974
  }
36679
36975
 
36680
36976
  // src/hooks/use-live-todos.ts
36681
- import { useEffect as useEffect45, useState as useState29 } from "react";
36977
+ import { useEffect as useEffect46, useState as useState30 } from "react";
36682
36978
  function useLiveTodos(context) {
36683
- const [todos, setTodos] = useState29(() => [...context.todos]);
36684
- useEffect45(() => {
36979
+ const [todos, setTodos] = useState30(() => [...context.todos]);
36980
+ useEffect46(() => {
36685
36981
  setTodos([...context.todos]);
36686
36982
  return context.state.onChange((change) => {
36687
36983
  if (change.kind === "todos_replaced") setTodos([...change.todos]);
@@ -36691,17 +36987,17 @@ function useLiveTodos(context) {
36691
36987
  }
36692
36988
 
36693
36989
  // src/hooks/use-mailbox-view-model.ts
36694
- import { useEffect as useEffect46, useState as useState30 } from "react";
36990
+ import { useEffect as useEffect47, useState as useState31 } from "react";
36695
36991
  function useMailboxViewModel(events) {
36696
- const [mailboxStatus, setMailboxStatus] = useState30({
36992
+ const [mailboxStatus, setMailboxStatus] = useState31({
36697
36993
  unread: 0,
36698
36994
  onlineAgents: 0,
36699
36995
  onlineClients: { tui: 0, webui: 0, repl: 0 }
36700
36996
  });
36701
- const [mailboxPanelOpen, setMailboxPanelOpen] = useState30(false);
36702
- const [mailboxMessages, setMailboxMessages] = useState30([]);
36703
- const [mailboxAgents, setMailboxAgents] = useState30([]);
36704
- useEffect46(() => {
36997
+ const [mailboxPanelOpen, setMailboxPanelOpen] = useState31(false);
36998
+ const [mailboxMessages, setMailboxMessages] = useState31([]);
36999
+ const [mailboxAgents, setMailboxAgents] = useState31([]);
37000
+ useEffect47(() => {
36705
37001
  const AGENT_ONLINE_TTL_MS = 12e4;
36706
37002
  const lastSeen = /* @__PURE__ */ new Map();
36707
37003
  const unsubUnread = events.onPattern("mailbox.unread_count", (_event, payload) => {
@@ -36763,7 +37059,7 @@ function useMailboxViewModel(events) {
36763
37059
  unsubSnapshot();
36764
37060
  };
36765
37061
  }, [events]);
36766
- useEffect46(() => {
37062
+ useEffect47(() => {
36767
37063
  const unsubMessage = events.onPattern("mailbox.received", (_event, payload) => {
36768
37064
  const data = payload;
36769
37065
  if (!data?.messageId) return;
@@ -36864,7 +37160,7 @@ function useMailboxViewModel(events) {
36864
37160
  }
36865
37161
 
36866
37162
  // src/hooks/use-mode-picker.ts
36867
- import { useCallback as useCallback16 } from "react";
37163
+ import { useCallback as useCallback17 } from "react";
36868
37164
  function buildModePickerOptions(result) {
36869
37165
  return toModeOptions(result.modes, result.activeId);
36870
37166
  }
@@ -36872,7 +37168,7 @@ function useModePicker({
36872
37168
  dispatch,
36873
37169
  getModes
36874
37170
  }) {
36875
- const openModePicker = useCallback16(async () => {
37171
+ const openModePicker = useCallback17(async () => {
36876
37172
  if (!getModes) return;
36877
37173
  const result = await getModes();
36878
37174
  const options = buildModePickerOptions(result);
@@ -36914,11 +37210,11 @@ function useModelPickRequest(opts) {
36914
37210
  }
36915
37211
 
36916
37212
  // src/hooks/use-mouse-tracking.ts
36917
- import { useEffect as useEffect47, useRef as useRef27, useState as useState31 } from "react";
37213
+ import { useEffect as useEffect48, useRef as useRef28, useState as useState32 } from "react";
36918
37214
  function useMouseTracking(input) {
36919
37215
  const { initialMouseMode, initialNativeMouse, overlayOpen, protocol, stdout } = input;
36920
- const [mouseMode, setMouseMode] = useState31(initialMouseMode);
36921
- const [nativeMouse, setNativeMouse] = useState31(initialNativeMouse ?? false);
37216
+ const [mouseMode, setMouseMode] = useState32(initialMouseMode);
37217
+ const [nativeMouse, setNativeMouse] = useState32(initialNativeMouse ?? false);
36922
37218
  const mouseTrackingOn = shouldEnableMouseTracking({
36923
37219
  fullMode: mouseMode,
36924
37220
  overlayOpen,
@@ -36927,8 +37223,8 @@ function useMouseTracking(input) {
36927
37223
  protocol
36928
37224
  });
36929
37225
  const mouseTrackingSequence = mouseTrackingOn ? mouseMode ? MOUSE_DRAG_ON : MOUSE_CLICK_ON : MOUSE_OFF;
36930
- const mouseWrittenRef = useRef27(null);
36931
- useEffect47(() => {
37226
+ const mouseWrittenRef = useRef28(null);
37227
+ useEffect48(() => {
36932
37228
  if (mouseWrittenRef.current === mouseTrackingSequence) return;
36933
37229
  mouseWrittenRef.current = mouseTrackingSequence;
36934
37230
  try {
@@ -36936,7 +37232,7 @@ function useMouseTracking(input) {
36936
37232
  } catch {
36937
37233
  }
36938
37234
  }, [mouseTrackingSequence, stdout]);
36939
- useEffect47(
37235
+ useEffect48(
36940
37236
  () => () => {
36941
37237
  const wasTracking = mouseWrittenRef.current !== null && mouseWrittenRef.current !== MOUSE_OFF;
36942
37238
  mouseWrittenRef.current = null;
@@ -36963,10 +37259,10 @@ import {
36963
37259
  } from "@wrongstack/tools/auto-proceed-loop-guard";
36964
37260
  import { todoTool } from "@wrongstack/tools/todo";
36965
37261
  import {
36966
- useCallback as useCallback17,
36967
- useEffect as useEffect48,
36968
- useRef as useRef28,
36969
- useState as useState32
37262
+ useCallback as useCallback18,
37263
+ useEffect as useEffect49,
37264
+ useRef as useRef29,
37265
+ useState as useState33
36970
37266
  } from "react";
36971
37267
  async function commitTodoTransition(agent, todos) {
36972
37268
  if (!agent?.ctx?.state) return null;
@@ -37011,27 +37307,27 @@ function useNextStepsAutoSubmit({
37011
37307
  clearDraft,
37012
37308
  runBlocksRef
37013
37309
  }) {
37014
- const [nextStepsAutoSubmitCountdown, setNextStepsAutoSubmitCountdown] = useState32(
37310
+ const [nextStepsAutoSubmitCountdown, setNextStepsAutoSubmitCountdown] = useState33(
37015
37311
  null
37016
37312
  );
37017
- const [nextStepsAutoSubmitLabel, setNextStepsAutoSubmitLabel] = useState32(null);
37018
- const nextStepsAutoSubmitSuggestionRef = useRef28(null);
37019
- const nextStepsAutoSubmitSourceRef = useRef28(null);
37020
- const nextStepsAutoSubmitTodoIdRef = useRef28(null);
37021
- const nextStepsAutoSubmitTodosRef = useRef28([]);
37022
- const nextStepsAutoSubmitTimerRef = useRef28(void 0);
37023
- const autoSubmitStreakRef = useRef28(0);
37024
- const autoSubmitCapWarnedRef = useRef28(false);
37025
- const autoSubmitLoopGuardRef = useRef28(createAutoProceedLoopGuard());
37026
- const continuationAttemptTrackerRef = useRef28(createContinuationAttemptTracker());
37027
- const [nextStepsRecheck, setNextStepsRecheck] = useState32(0);
37028
- useEffect48(() => {
37313
+ const [nextStepsAutoSubmitLabel, setNextStepsAutoSubmitLabel] = useState33(null);
37314
+ const nextStepsAutoSubmitSuggestionRef = useRef29(null);
37315
+ const nextStepsAutoSubmitSourceRef = useRef29(null);
37316
+ const nextStepsAutoSubmitTodoIdRef = useRef29(null);
37317
+ const nextStepsAutoSubmitTodosRef = useRef29([]);
37318
+ const nextStepsAutoSubmitTimerRef = useRef29(void 0);
37319
+ const autoSubmitStreakRef = useRef29(0);
37320
+ const autoSubmitCapWarnedRef = useRef29(false);
37321
+ const autoSubmitLoopGuardRef = useRef29(createAutoProceedLoopGuard());
37322
+ const continuationAttemptTrackerRef = useRef29(createContinuationAttemptTracker());
37323
+ const [nextStepsRecheck, setNextStepsRecheck] = useState33(0);
37324
+ useEffect49(() => {
37029
37325
  autoSubmitStreakRef.current = 0;
37030
37326
  autoSubmitCapWarnedRef.current = false;
37031
37327
  autoSubmitLoopGuardRef.current.reset();
37032
37328
  continuationAttemptTrackerRef.current.reset();
37033
37329
  }, [autonomyLive]);
37034
- useEffect48(() => {
37330
+ useEffect49(() => {
37035
37331
  const liveAutonomy = getAutonomy?.() ?? autonomyLive;
37036
37332
  if (state.status !== "idle" || liveAutonomy !== "auto") {
37037
37333
  clearInterval(nextStepsAutoSubmitTimerRef.current);
@@ -37265,7 +37561,7 @@ ${GROUNDED_NO_PROGRESS_STEER}` : suggestion;
37265
37561
  autonomyNextPrompt,
37266
37562
  dispatch
37267
37563
  ]);
37268
- const cancelNextStepsCountdown = useCallback17(() => {
37564
+ const cancelNextStepsCountdown = useCallback18(() => {
37269
37565
  if (nextStepsAutoSubmitTimerRef.current == null) return;
37270
37566
  clearInterval(nextStepsAutoSubmitTimerRef.current);
37271
37567
  nextStepsAutoSubmitTimerRef.current = void 0;
@@ -37291,7 +37587,7 @@ ${GROUNDED_NO_PROGRESS_STEER}` : suggestion;
37291
37587
 
37292
37588
  // src/hooks/use-panel-controllers.ts
37293
37589
  import { toErrorMessage as toErrorMessage6 } from "@wrongstack/core/utils";
37294
- import React8, { useEffect as useEffect49 } from "react";
37590
+ import React8, { useEffect as useEffect50 } from "react";
37295
37591
 
37296
37592
  // src/on-panel-open.ts
37297
37593
  function createPanelOpenDispatcher(deps) {
@@ -37425,7 +37721,7 @@ function usePanelControllers({
37425
37721
  dispatch({ type: "sessionsPanelBusy", on: false });
37426
37722
  }
37427
37723
  }, [getLiveSessions]);
37428
- useEffect49(() => {
37724
+ useEffect50(() => {
37429
37725
  if (!onPanelOpen) return;
37430
37726
  const dispatcher = createPanelOpenDispatcher({
37431
37727
  dispatch,
@@ -37452,7 +37748,7 @@ function usePanelControllers({
37452
37748
  openShadowPanel,
37453
37749
  openHelpPanel
37454
37750
  ]);
37455
- useEffect49(() => {
37751
+ useEffect50(() => {
37456
37752
  if (!state.sessionsPanelOpen || !getLiveSessions) return void 0;
37457
37753
  const t = setInterval(() => {
37458
37754
  void loadLiveSessions();
@@ -37532,7 +37828,7 @@ function usePanelControllers({
37532
37828
  if (!getPluginItems) return;
37533
37829
  dispatch({ type: "pluginPickerSetItems", items: getPluginItems() });
37534
37830
  }, [getPluginItems]);
37535
- useEffect49(() => {
37831
+ useEffect50(() => {
37536
37832
  if (!state.pluginPicker.open) return;
37537
37833
  refreshPluginPicker();
37538
37834
  }, [state.pluginPicker.open, refreshPluginPicker]);
@@ -37564,7 +37860,7 @@ function usePanelControllers({
37564
37860
  if (!getMcpServers) return;
37565
37861
  dispatch({ type: "mcpPickerSetItems", items: getMcpServers() });
37566
37862
  }, [getMcpServers]);
37567
- useEffect49(() => {
37863
+ useEffect50(() => {
37568
37864
  if (!state.mcpPicker.open) return;
37569
37865
  refreshMcpPicker();
37570
37866
  }, [state.mcpPicker.open, refreshMcpPicker]);
@@ -37608,7 +37904,7 @@ function usePanelControllers({
37608
37904
  dispatch({ type: "toolsPickerSetItems", items });
37609
37905
  setLiveToolCount(items.filter((item) => item.enabled).length);
37610
37906
  }, [getToolsItems]);
37611
- useEffect49(() => {
37907
+ useEffect50(() => {
37612
37908
  if (!state.toolsPicker.open) return;
37613
37909
  refreshToolsPicker();
37614
37910
  }, [state.toolsPicker.open, refreshToolsPicker]);
@@ -37644,7 +37940,7 @@ function usePanelControllers({
37644
37940
  }
37645
37941
 
37646
37942
  // src/hooks/use-paste-handling.ts
37647
- import { useRef as useRef29 } from "react";
37943
+ import { useRef as useRef30 } from "react";
37648
37944
  import { toErrorMessage as toErrorMessage7 } from "@wrongstack/core/utils";
37649
37945
 
37650
37946
  // src/input-validation/limits.ts
@@ -37689,8 +37985,8 @@ function usePasteHandling({
37689
37985
  setDraft,
37690
37986
  tokenPreviewsRef
37691
37987
  }) {
37692
- const pasteAccumRef = useRef29(null);
37693
- const pasteFlushTimerRef = useRef29(null);
37988
+ const pasteAccumRef = useRef30(null);
37989
+ const pasteFlushTimerRef = useRef30(null);
37694
37990
  const commitPaste = async (full) => {
37695
37991
  const builder = builderRef.current;
37696
37992
  if (!builder || !full) return;
@@ -37781,11 +38077,11 @@ function usePasteHandling({
37781
38077
 
37782
38078
  // src/hooks/use-prompt-picker.ts
37783
38079
  import { createRequire } from "node:module";
37784
- import * as path5 from "node:path";
38080
+ import * as path7 from "node:path";
37785
38081
  import { DefaultPromptLoader } from "@wrongstack/core/execution";
37786
38082
  import { PromptUsageStore } from "@wrongstack/core/storage";
37787
38083
  import { resolveWstackPaths as resolveWstackPaths4 } from "@wrongstack/core/utils";
37788
- import { useCallback as useCallback18, useRef as useRef30 } from "react";
38084
+ import { useCallback as useCallback19, useRef as useRef31 } from "react";
37789
38085
  function buildPromptPickerPayload(all, recentSlugs) {
37790
38086
  const entries = all.map((e) => ({
37791
38087
  slug: e.slug,
@@ -37806,15 +38102,15 @@ function buildPromptPickerPayload(all, recentSlugs) {
37806
38102
  return { all: entries, categories, recentSlugs };
37807
38103
  }
37808
38104
  function usePromptPicker({ projectRoot, dispatch }) {
37809
- const promptLoaderRef = useRef30(null);
37810
- const promptUsageRef = useRef30(null);
37811
- const getPromptLoader = useCallback18(() => {
38105
+ const promptLoaderRef = useRef31(null);
38106
+ const promptUsageRef = useRef31(null);
38107
+ const getPromptLoader = useCallback19(() => {
37812
38108
  if (!promptLoaderRef.current && projectRoot) {
37813
38109
  let bundledDir;
37814
38110
  try {
37815
38111
  const req = createRequire(import.meta.url);
37816
- bundledDir = path5.join(
37817
- path5.dirname(req.resolve("@wrongstack/core/package.json")),
38112
+ bundledDir = path7.join(
38113
+ path7.dirname(req.resolve("@wrongstack/core/package.json")),
37818
38114
  "data",
37819
38115
  "prompts"
37820
38116
  );
@@ -37827,7 +38123,7 @@ function usePromptPicker({ projectRoot, dispatch }) {
37827
38123
  }
37828
38124
  return promptLoaderRef.current;
37829
38125
  }, [projectRoot]);
37830
- const openPromptPicker = useCallback18(async () => {
38126
+ const openPromptPicker = useCallback19(async () => {
37831
38127
  const loader = getPromptLoader();
37832
38128
  if (!loader) return;
37833
38129
  loader.invalidateCache();
@@ -37836,7 +38132,7 @@ function usePromptPicker({ projectRoot, dispatch }) {
37836
38132
  const payload = buildPromptPickerPayload(all, recentSlugs);
37837
38133
  dispatch({ type: "promptPickerOpen", ...payload });
37838
38134
  }, [dispatch, getPromptLoader]);
37839
- const setPromptFavorite = useCallback18(
38135
+ const setPromptFavorite = useCallback19(
37840
38136
  async (slug, favorite) => {
37841
38137
  const loader = getPromptLoader();
37842
38138
  if (!loader) return;
@@ -37852,7 +38148,7 @@ function usePromptPicker({ projectRoot, dispatch }) {
37852
38148
  import { tuiStreamFlushMs } from "@wrongstack/core/utils";
37853
38149
  import { DEFAULT_MIN_IMPORTANCE as DEFAULT_MIN_IMPORTANCE2, DEFAULT_MIN_SCORE as DEFAULT_MIN_SCORE2, MIN_RELATION_STRENGTH as MIN_RELATION_STRENGTH2 } from "@wrongstack/sage";
37854
38150
  import { isFinalTurnStopReason as isFinalTurnStopReason2 } from "@wrongstack/tools/next-steps";
37855
- import { useEffect as useEffect50 } from "react";
38151
+ import { useEffect as useEffect51 } from "react";
37856
38152
 
37857
38153
  // src/memory-lifecycle-entry.ts
37858
38154
  function memoryLifecycleEntry(event, payload) {
@@ -38052,7 +38348,7 @@ function useProviderEventBridge({
38052
38348
  assistantCommittedThisRunRef,
38053
38349
  setMemoryContextMonitor
38054
38350
  }) {
38055
- useEffect50(() => {
38351
+ useEffect51(() => {
38056
38352
  const FLUSH_MS2 = tuiStreamFlushMs();
38057
38353
  let pendingToolStream = null;
38058
38354
  const pendingSageStats = /* @__PURE__ */ new Map();
@@ -38426,7 +38722,7 @@ function useProviderEventBridge({
38426
38722
  }
38427
38723
 
38428
38724
  // src/hooks/use-queue-manager.ts
38429
- import { useEffect as useEffect51, useRef as useRef31 } from "react";
38725
+ import { useEffect as useEffect52, useRef as useRef32 } from "react";
38430
38726
 
38431
38727
  // src/queue-slash.ts
38432
38728
  var USAGE5 = "Usage:\n /queue \u2014 list pending messages\n /queue list \u2014 same as /queue\n /queue clear \u2014 drop all pending messages\n /queue delete N M\u2026 \u2014 drop messages at 1-based positions\n /queue picker on|off \u2014 toggle the mid-run send-mode picker (queue/btw/steer)\n /queue picker \u2014 show whether the picker is on";
@@ -38533,8 +38829,8 @@ function useQueueManager({
38533
38829
  saveSettings,
38534
38830
  midRunSendPickerRef
38535
38831
  }) {
38536
- const persistState = useRef31({ running: false, pending: null });
38537
- useEffect51(() => {
38832
+ const persistState = useRef32({ running: false, pending: null });
38833
+ useEffect52(() => {
38538
38834
  if (!queueStore) return;
38539
38835
  let cancelled = false;
38540
38836
  queueStore.read().then((items) => {
@@ -38561,7 +38857,7 @@ function useQueueManager({
38561
38857
  cancelled = true;
38562
38858
  };
38563
38859
  }, [queueStore]);
38564
- useEffect51(() => {
38860
+ useEffect52(() => {
38565
38861
  if (!queueStore) return;
38566
38862
  const raw = stateRef.current.queue.map(
38567
38863
  ({
@@ -38590,10 +38886,10 @@ function useQueueManager({
38590
38886
  }
38591
38887
  })();
38592
38888
  }, [stateRef.current.queue, queueStore, stateRef]);
38593
- useEffect51(() => {
38889
+ useEffect52(() => {
38594
38890
  onQueueChange?.(stateRef.current.queue.map((q) => q.displayText));
38595
38891
  }, [stateRef.current.queue, onQueueChange, stateRef]);
38596
- useEffect51(() => {
38892
+ useEffect52(() => {
38597
38893
  const cmd = createQueueSlashCommand({
38598
38894
  getQueue: () => stateRef.current.queue,
38599
38895
  clear: () => dispatch({ type: "queueClear" }),
@@ -38616,7 +38912,7 @@ function useQueueManager({
38616
38912
  }
38617
38913
 
38618
38914
  // src/hooks/use-session-interrupt-controller.ts
38619
- import { useEffect as useEffect52 } from "react";
38915
+ import { useEffect as useEffect53 } from "react";
38620
38916
  function useSessionInterruptController({
38621
38917
  interruptController,
38622
38918
  dispatch,
@@ -38638,7 +38934,7 @@ function useSessionInterruptController({
38638
38934
  getSddRun,
38639
38935
  switchAutonomy
38640
38936
  }) {
38641
- useEffect52(() => {
38937
+ useEffect53(() => {
38642
38938
  if (!interruptController) return;
38643
38939
  let pendingClearResolve = null;
38644
38940
  let pendingSlashResolve = null;
@@ -38765,15 +39061,15 @@ function useSessionInterruptController({
38765
39061
  }
38766
39062
 
38767
39063
  // src/hooks/use-settings-auto-save.ts
38768
- import { useEffect as useEffect53, useRef as useRef32 } from "react";
39064
+ import { useEffect as useEffect54, useRef as useRef33 } from "react";
38769
39065
  function useSettingsAutoSave(state, saveSettings, dispatch) {
38770
- const settingsAutoSaveGateRef = useRef32(true);
38771
- useEffect53(() => {
39066
+ const settingsAutoSaveGateRef = useRef33(true);
39067
+ useEffect54(() => {
38772
39068
  if (state.settingsPicker.open) {
38773
39069
  settingsAutoSaveGateRef.current = true;
38774
39070
  }
38775
39071
  }, [state.settingsPicker.open]);
38776
- useEffect53(() => {
39072
+ useEffect54(() => {
38777
39073
  const sp = state.settingsPicker;
38778
39074
  const save = saveSettings;
38779
39075
  if (!sp.open || !save) return;
@@ -38889,15 +39185,15 @@ function useSettingsAutoSave(state, saveSettings, dispatch) {
38889
39185
  }
38890
39186
 
38891
39187
  // src/hooks/use-shadow-panel.ts
38892
- import { useCallback as useCallback19 } from "react";
39188
+ import { useCallback as useCallback20 } from "react";
38893
39189
  import { toErrorMessage as toErrorMessage8 } from "@wrongstack/core/utils";
38894
39190
  function useShadowPanel(dispatch, host) {
38895
39191
  const { getShadowData, onShadowStart, onShadowStop } = host;
38896
- const openShadowPanel = useCallback19(() => {
39192
+ const openShadowPanel = useCallback20(() => {
38897
39193
  if (!getShadowData) return;
38898
39194
  dispatch({ type: "shadowOpen", shadow: getShadowData() });
38899
39195
  }, [dispatch, getShadowData]);
38900
- const handleShadowStart = useCallback19(async () => {
39196
+ const handleShadowStart = useCallback20(async () => {
38901
39197
  if (!onShadowStart) return;
38902
39198
  dispatch({ type: "shadowHint", text: "Starting Shadow Agent\u2026" });
38903
39199
  try {
@@ -38911,7 +39207,7 @@ function useShadowPanel(dispatch, host) {
38911
39207
  dispatch({ type: "shadowHint", text: `Failed to start: ${toErrorMessage8(error)}` });
38912
39208
  }
38913
39209
  }, [dispatch, getShadowData, onShadowStart]);
38914
- const handleShadowStop = useCallback19(async () => {
39210
+ const handleShadowStop = useCallback20(async () => {
38915
39211
  if (!onShadowStop) return;
38916
39212
  dispatch({ type: "shadowHint", text: "Stopping Shadow Agent\u2026" });
38917
39213
  try {
@@ -38929,14 +39225,14 @@ function useShadowPanel(dispatch, host) {
38929
39225
  }
38930
39226
 
38931
39227
  // src/hooks/use-slash-picker.ts
38932
- import { useCallback as useCallback20, useEffect as useEffect54 } from "react";
39228
+ import { useCallback as useCallback21, useEffect as useEffect55 } from "react";
38933
39229
  function useSlashPicker({
38934
39230
  state,
38935
39231
  slashRegistry,
38936
39232
  dispatch,
38937
39233
  setDraft
38938
39234
  }) {
38939
- useEffect54(() => {
39235
+ useEffect55(() => {
38940
39236
  const trimmed = state.buffer.trimStart();
38941
39237
  if (!trimmed.startsWith("/") || /\s/.test(trimmed)) {
38942
39238
  if (state.slashPicker.open) dispatch({ type: "slashPickerClose" });
@@ -38948,9 +39244,9 @@ function useSlashPicker({
38948
39244
  dispatch({ type: "slashPickerOpen", query, matches });
38949
39245
  }
38950
39246
  }, [state.buffer, state.slashPicker.open, state.slashPicker.query, slashRegistry, dispatch]);
38951
- return useCallback20(() => {
38952
- const { open, matches, selected } = state.slashPicker;
38953
- if (!open || matches.length === 0) return;
39247
+ return useCallback21(() => {
39248
+ const { open: open2, matches, selected } = state.slashPicker;
39249
+ if (!open2 || matches.length === 0) return;
38954
39250
  const picked = matches[selected];
38955
39251
  if (!picked) return;
38956
39252
  const command = picked.argsHint !== void 0 ? `/${picked.name} ` : `/${picked.name}`;
@@ -38960,11 +39256,11 @@ function useSlashPicker({
38960
39256
  }
38961
39257
 
38962
39258
  // src/hooks/use-stable-key-handler.ts
38963
- import { useCallback as useCallback21, useRef as useRef33 } from "react";
39259
+ import { useCallback as useCallback22, useRef as useRef34 } from "react";
38964
39260
  function useStableKeyHandler(handleKey) {
38965
- const handleKeyRef = useRef33(null);
39261
+ const handleKeyRef = useRef34(null);
38966
39262
  handleKeyRef.current = handleKey;
38967
- return useCallback21((input, key) => {
39263
+ return useCallback22((input, key) => {
38968
39264
  handleKeyRef.current?.(input, key)?.catch(() => {
38969
39265
  });
38970
39266
  return Promise.resolve();
@@ -38974,7 +39270,7 @@ function useStableKeyHandler(handleKey) {
38974
39270
  // src/hooks/use-statusbar-view-model.ts
38975
39271
  import * as fs4 from "node:fs/promises";
38976
39272
  import { getContextBreakdown } from "@wrongstack/core/utils";
38977
- import { useEffect as useEffect55, useMemo as useMemo13, useState as useState33 } from "react";
39273
+ import { useEffect as useEffect56, useMemo as useMemo13, useState as useState34 } from "react";
38978
39274
 
38979
39275
  // src/context-fill.ts
38980
39276
  function resolveContextFill(inputs) {
@@ -39107,8 +39403,8 @@ function useStatusbarViewModel({
39107
39403
  const planHidden = hiddenItems.includes("plan");
39108
39404
  const taskHidden = hiddenItems.includes("tasks");
39109
39405
  const planPath = agent.ctx.meta["plan.path"];
39110
- const [planCounts, setPlanCounts] = useState33(null);
39111
- useEffect55(() => {
39406
+ const [planCounts, setPlanCounts] = useState34(null);
39407
+ useEffect56(() => {
39112
39408
  if (planHidden) return;
39113
39409
  if (typeof planPath !== "string" || !planPath) {
39114
39410
  setPlanCounts(null);
@@ -39118,8 +39414,8 @@ function useStatusbarViewModel({
39118
39414
  let lastFingerprint = "";
39119
39415
  const poll = async () => {
39120
39416
  try {
39121
- const stat3 = await fs4.stat(planPath);
39122
- const fingerprint = `${stat3.mtimeMs}:${stat3.size}`;
39417
+ const stat4 = await fs4.stat(planPath);
39418
+ const fingerprint = `${stat4.mtimeMs}:${stat4.size}`;
39123
39419
  if (fingerprint === lastFingerprint) return;
39124
39420
  const data = await fs4.readFile(planPath, "utf8");
39125
39421
  const parsed = JSON.parse(data);
@@ -39129,15 +39425,15 @@ function useStatusbarViewModel({
39129
39425
  setPlanCounts((previous) => previous === null ? previous : null);
39130
39426
  return;
39131
39427
  }
39132
- let open = 0;
39428
+ let open2 = 0;
39133
39429
  let inProgress = 0;
39134
39430
  let done = 0;
39135
39431
  for (const item of parsed.items) {
39136
39432
  if (item?.status === "done") done++;
39137
39433
  else if (item?.status === "in_progress") inProgress++;
39138
- else open++;
39434
+ else open2++;
39139
39435
  }
39140
- const next = open + inProgress + done > 0 ? { open, inProgress, done } : null;
39436
+ const next = open2 + inProgress + done > 0 ? { open: open2, inProgress, done } : null;
39141
39437
  setPlanCounts((previous) => samePlanCounts(previous, next) ? previous : next);
39142
39438
  } catch {
39143
39439
  if (!cancelled) {
@@ -39154,8 +39450,8 @@ function useStatusbarViewModel({
39154
39450
  };
39155
39451
  }, [planPath, planHidden]);
39156
39452
  const taskPath = agent.ctx.meta["task.path"];
39157
- const [taskCounts, setTaskCounts] = useState33(null);
39158
- useEffect55(() => {
39453
+ const [taskCounts, setTaskCounts] = useState34(null);
39454
+ useEffect56(() => {
39159
39455
  if (taskHidden) return;
39160
39456
  if (typeof taskPath !== "string" || !taskPath) {
39161
39457
  setTaskCounts(null);
@@ -39165,8 +39461,8 @@ function useStatusbarViewModel({
39165
39461
  let lastFingerprint = "";
39166
39462
  const poll = async () => {
39167
39463
  try {
39168
- const stat3 = await fs4.stat(taskPath);
39169
- const fingerprint = `${stat3.mtimeMs}:${stat3.size}`;
39464
+ const stat4 = await fs4.stat(taskPath);
39465
+ const fingerprint = `${stat4.mtimeMs}:${stat4.size}`;
39170
39466
  if (fingerprint === lastFingerprint) return;
39171
39467
  const data = await fs4.readFile(taskPath, "utf8");
39172
39468
  const parsed = JSON.parse(data);
@@ -39225,7 +39521,7 @@ function useStatusbarViewModel({
39225
39521
  }
39226
39522
 
39227
39523
  // src/hooks/use-statusline-hidden-sync.ts
39228
- import { useEffect as useEffect56 } from "react";
39524
+ import { useEffect as useEffect57 } from "react";
39229
39525
  function statuslineHiddenDiffers(hookHidden, pickerHidden) {
39230
39526
  const currentHidden = new Set(hookHidden);
39231
39527
  const pickerHiddenSet = new Set(pickerHidden);
@@ -39237,7 +39533,7 @@ function useStatuslineHiddenSync({
39237
39533
  hiddenItems,
39238
39534
  setHiddenItems
39239
39535
  }) {
39240
- useEffect56(() => {
39536
+ useEffect57(() => {
39241
39537
  if (!pickerOpen) return;
39242
39538
  if (statuslineHiddenDiffers(hiddenItems, pickerHidden)) {
39243
39539
  setHiddenItems([...pickerHidden]);
@@ -39246,7 +39542,7 @@ function useStatuslineHiddenSync({
39246
39542
  }
39247
39543
 
39248
39544
  // src/hooks/use-stream-chip-expiration.ts
39249
- import { useEffect as useEffect57, useRef as useRef34 } from "react";
39545
+ import { useEffect as useEffect58, useRef as useRef35 } from "react";
39250
39546
  function computeStreamChipActions(input) {
39251
39547
  const actions = [];
39252
39548
  const visible = new Set(input.visibleChipKeys);
@@ -39271,9 +39567,9 @@ function useStreamChipExpiration({
39271
39567
  visibleChips,
39272
39568
  dispatch
39273
39569
  }) {
39274
- const prevBrainPromptRef = useRef34(brainPrompt);
39275
- const prevEnhanceRef = useRef34(enhance);
39276
- useEffect57(() => {
39570
+ const prevBrainPromptRef = useRef35(brainPrompt);
39571
+ const prevEnhanceRef = useRef35(enhance);
39572
+ useEffect58(() => {
39277
39573
  const actions = computeStreamChipActions({
39278
39574
  brainPromptNow: !!brainPrompt,
39279
39575
  brainPromptPrev: !!prevBrainPromptRef.current,
@@ -39285,7 +39581,7 @@ function useStreamChipExpiration({
39285
39581
  prevBrainPromptRef.current = brainPrompt;
39286
39582
  prevEnhanceRef.current = enhance;
39287
39583
  }, [brainPrompt, enhance, visibleChips, dispatch]);
39288
- useEffect57(() => {
39584
+ useEffect58(() => {
39289
39585
  const interval = setInterval(() => {
39290
39586
  const expired = computeExpiredChipKeys(visibleChips, Date.now());
39291
39587
  for (const key of expired) {
@@ -39297,9 +39593,9 @@ function useStreamChipExpiration({
39297
39593
  }
39298
39594
 
39299
39595
  // src/hooks/use-theme-state.ts
39300
- import { useEffect as useEffect58 } from "react";
39596
+ import { useEffect as useEffect59 } from "react";
39301
39597
  function useThemeState({ configStore }) {
39302
- useEffect58(() => {
39598
+ useEffect59(() => {
39303
39599
  if (!configStore) return;
39304
39600
  const initial = configStore.get().themePreset;
39305
39601
  if (initial && initial !== getActiveThemeName()) {
@@ -39318,10 +39614,11 @@ function useThemeState({ configStore }) {
39318
39614
  }
39319
39615
 
39320
39616
  // src/hooks/use-tui-activity.ts
39617
+ import * as fs5 from "node:fs/promises";
39321
39618
  import * as os from "node:os";
39322
39619
  import { loadGoal } from "@wrongstack/core/storage";
39323
39620
  import { resolveWstackPaths as resolveWstackPaths5 } from "@wrongstack/core/utils";
39324
- import { useCallback as useCallback22, useEffect as useEffect59, useMemo as useMemo14, useRef as useRef35, useState as useState34 } from "react";
39621
+ import { useCallback as useCallback23, useEffect as useEffect60, useMemo as useMemo14, useRef as useRef36, useState as useState35 } from "react";
39325
39622
 
39326
39623
  // src/heap-watchdog.ts
39327
39624
  import {
@@ -39332,6 +39629,7 @@ import {
39332
39629
  } from "@wrongstack/core/utils";
39333
39630
 
39334
39631
  // src/hooks/use-tui-activity.ts
39632
+ var GOAL_FILE_MISSING = "missing";
39335
39633
  function attachmentsSize(store) {
39336
39634
  const items = store.items;
39337
39635
  return items?.size ?? 0;
@@ -39365,10 +39663,10 @@ function useTuiActivity({
39365
39663
  builderRef
39366
39664
  }) {
39367
39665
  recordTuiAppRender();
39368
- const [rolledThinkingWord, setRolledThinkingWord] = useState34(() => pickRandomTuiThinkingWord());
39666
+ const [rolledThinkingWord, setRolledThinkingWord] = useState35(() => pickRandomTuiThinkingWord());
39369
39667
  const thinkingWorking = status === "running" || status === "streaming";
39370
- const prevThinkingWorkingRef = useRef35(false);
39371
- useEffect59(() => {
39668
+ const prevThinkingWorkingRef = useRef36(false);
39669
+ useEffect60(() => {
39372
39670
  if (thinkingWorking && !prevThinkingWorkingRef.current) {
39373
39671
  setRolledThinkingWord((previous) => pickRandomTuiThinkingWord(previous));
39374
39672
  }
@@ -39379,16 +39677,16 @@ function useTuiActivity({
39379
39677
  () => Object.values(fleet).filter((entry) => entry.status === "running").length,
39380
39678
  [fleet]
39381
39679
  );
39382
- const startedAtRef = useRef35(Date.now());
39383
- const [nowTick, setNowTick] = useState34(Date.now());
39384
- useEffect59(() => {
39680
+ const startedAtRef = useRef36(Date.now());
39681
+ const [nowTick, setNowTick] = useState35(Date.now());
39682
+ useEffect60(() => {
39385
39683
  const timer = setInterval(() => setNowTick(Date.now()), 1e4);
39386
39684
  return () => clearInterval(timer);
39387
39685
  }, []);
39388
- const [workingTimeBase, setWorkingTimeBase] = useState34(0);
39389
- const workingStartRef = useRef35(null);
39390
- const prevStatusRef = useRef35(status);
39391
- useEffect59(() => {
39686
+ const [workingTimeBase, setWorkingTimeBase] = useState35(0);
39687
+ const workingStartRef = useRef36(null);
39688
+ const prevStatusRef = useRef36(status);
39689
+ useEffect60(() => {
39392
39690
  if (prevStatusRef.current === status) return;
39393
39691
  const wasWorking = prevStatusRef.current === "running" || prevStatusRef.current === "streaming";
39394
39692
  const isWorking = status === "running" || status === "streaming";
@@ -39401,10 +39699,10 @@ function useTuiActivity({
39401
39699
  }
39402
39700
  prevStatusRef.current = status;
39403
39701
  }, [status]);
39404
- const [fleetWorkingBase, setFleetWorkingBase] = useState34(0);
39405
- const fleetWorkingStartRef = useRef35(null);
39406
- const prevFleetRunningRef = useRef35(0);
39407
- useEffect59(() => {
39702
+ const [fleetWorkingBase, setFleetWorkingBase] = useState35(0);
39703
+ const fleetWorkingStartRef = useRef36(null);
39704
+ const prevFleetRunningRef = useRef36(0);
39705
+ useEffect60(() => {
39408
39706
  const running = fleetRunningCount;
39409
39707
  if (prevFleetRunningRef.current === running) return;
39410
39708
  const wasRunning = prevFleetRunningRef.current > 0;
@@ -39427,16 +39725,16 @@ function useTuiActivity({
39427
39725
  const timingNow = Date.now();
39428
39726
  const workingTimeMs = workingStartRef.current === null ? workingTimeBase : workingTimeBase + (timingNow - workingStartRef.current);
39429
39727
  const fleetWorkingTimeMs = fleetWorkingStartRef.current === null ? fleetWorkingBase : fleetWorkingBase + (timingNow - fleetWorkingStartRef.current);
39430
- const rssHistoryRef = useRef35([]);
39431
- const heapHistoryRef = useRef35([]);
39728
+ const rssHistoryRef = useRef36([]);
39729
+ const heapHistoryRef = useRef36([]);
39432
39730
  const processMemory = useMemo14(() => {
39433
39731
  const sample = takeHeapSample();
39434
39732
  pushMetricHistory(rssHistoryRef.current, Math.min(1, sample.rss / TOTAL_MEM));
39435
39733
  pushMetricHistory(heapHistoryRef.current, Math.min(1, sample.load));
39436
39734
  return sample;
39437
39735
  }, [nowTick]);
39438
- const cpuPrevRef = useRef35(null);
39439
- const cpuHistoryRef = useRef35([]);
39736
+ const cpuPrevRef = useRef36(null);
39737
+ const cpuHistoryRef = useRef36([]);
39440
39738
  const cpuPercent = useMemo14(() => {
39441
39739
  const now = process.hrtime.bigint();
39442
39740
  const cpuNow = process.cpuUsage();
@@ -39450,7 +39748,7 @@ function useTuiActivity({
39450
39748
  pushMetricHistory(cpuHistoryRef.current, pct / 100);
39451
39749
  return pct;
39452
39750
  }, [nowTick]);
39453
- useEffect59(() => {
39751
+ useEffect60(() => {
39454
39752
  const stopHeapWatchdog = startSharedHeapWatchdog({
39455
39753
  collectStats: () => {
39456
39754
  const currentState = stateRef.current;
@@ -39494,13 +39792,27 @@ function useTuiActivity({
39494
39792
  void stopHeapWatchdog();
39495
39793
  };
39496
39794
  }, [agentContext, dispatch, stateRef, attachments, builderRef]);
39497
- const goalSummaryFingerprintRef = useRef35(void 0);
39498
- const goalSummaryGenerationRef = useRef35(0);
39499
- const refreshGoalSummary = useCallback22(() => {
39795
+ const goalSummaryFingerprintRef = useRef36(void 0);
39796
+ const goalSummaryGenerationRef = useRef36(0);
39797
+ const goalStatFingerprintRef = useRef36(void 0);
39798
+ const refreshGoalSummary = useCallback23(() => {
39500
39799
  const generation = ++goalSummaryGenerationRef.current;
39501
39800
  if (!projectRoot) return;
39502
39801
  const goalPath = resolveWstackPaths5({ projectRoot }).projectGoal;
39503
- loadGoal(goalPath).then((goal) => {
39802
+ fs5.stat(goalPath).then(
39803
+ (stat4) => {
39804
+ const statFingerprint = `${stat4.mtimeMs}:${stat4.size}`;
39805
+ if (statFingerprint === goalStatFingerprintRef.current) return void 0;
39806
+ goalStatFingerprintRef.current = statFingerprint;
39807
+ return loadGoal(goalPath);
39808
+ },
39809
+ () => {
39810
+ if (goalStatFingerprintRef.current === GOAL_FILE_MISSING) return void 0;
39811
+ goalStatFingerprintRef.current = GOAL_FILE_MISSING;
39812
+ return null;
39813
+ }
39814
+ ).then((goal) => {
39815
+ if (goal === void 0) return;
39504
39816
  if (generation !== goalSummaryGenerationRef.current) return;
39505
39817
  if (!goal) {
39506
39818
  if (goalSummaryFingerprintRef.current === "null") return;
@@ -39531,7 +39843,7 @@ function useTuiActivity({
39531
39843
  }).catch(() => {
39532
39844
  });
39533
39845
  }, [dispatch, projectRoot]);
39534
- useEffect59(() => {
39846
+ useEffect60(() => {
39535
39847
  refreshGoalSummary();
39536
39848
  }, [nowTick, refreshGoalSummary]);
39537
39849
  const enhanceDots = enhanceBusy ? timingFrame % 36 : 0;
@@ -39554,7 +39866,7 @@ function useTuiActivity({
39554
39866
  }
39555
39867
 
39556
39868
  // src/hooks/use-tui-controllers.ts
39557
- import { useEffect as useEffect60 } from "react";
39869
+ import { useEffect as useEffect61 } from "react";
39558
39870
  function useTuiControllers({
39559
39871
  dispatch,
39560
39872
  fleetChat,
@@ -39564,7 +39876,7 @@ function useTuiControllers({
39564
39876
  enhanceController,
39565
39877
  agentsMonitorController
39566
39878
  }) {
39567
- useEffect60(() => {
39879
+ useEffect61(() => {
39568
39880
  if (!fleetStreamController) return;
39569
39881
  fleetStreamController.mode = fleetChat;
39570
39882
  fleetStreamController.setMode = (mode) => {
@@ -39576,12 +39888,12 @@ function useTuiControllers({
39576
39888
  };
39577
39889
  };
39578
39890
  }, [dispatch, fleetStreamController, fleetChat]);
39579
- useEffect60(() => {
39891
+ useEffect61(() => {
39580
39892
  if (fleetStreamController) {
39581
39893
  fleetStreamController.mode = fleetChat;
39582
39894
  }
39583
39895
  }, [fleetStreamController, fleetChat]);
39584
- useEffect60(() => {
39896
+ useEffect61(() => {
39585
39897
  if (!enhanceController) return;
39586
39898
  enhanceController.enabled = enhanceEnabled;
39587
39899
  enhanceController.setEnabled = (enabled) => {
@@ -39593,7 +39905,7 @@ function useTuiControllers({
39593
39905
  };
39594
39906
  };
39595
39907
  }, [dispatch, enhanceController, enhanceEnabled]);
39596
- useEffect60(() => {
39908
+ useEffect61(() => {
39597
39909
  if (!agentsMonitorController) return;
39598
39910
  agentsMonitorController.visible = agentsMonitorOpen;
39599
39911
  agentsMonitorController.setVisible = (visible) => {
@@ -39607,28 +39919,28 @@ function useTuiControllers({
39607
39919
  };
39608
39920
  };
39609
39921
  }, [agentsMonitorController, agentsMonitorOpen, dispatch]);
39610
- useEffect60(() => {
39922
+ useEffect61(() => {
39611
39923
  if (agentsMonitorController) agentsMonitorController.visible = agentsMonitorOpen;
39612
39924
  }, [agentsMonitorController, agentsMonitorOpen]);
39613
39925
  }
39614
39926
 
39615
39927
  // src/hooks/use-tui-environment-state.ts
39616
39928
  import { getIndexState as getIndexState2, getProcessRegistry as getProcessRegistry9, onIndexStateChange } from "@wrongstack/tools";
39617
- import { useEffect as useEffect61, useRef as useRef36, useState as useState36 } from "react";
39929
+ import { useEffect as useEffect62, useRef as useRef37, useState as useState37 } from "react";
39618
39930
 
39619
39931
  // src/hooks/use-statusline-state.ts
39620
- import { useState as useState35 } from "react";
39932
+ import { useState as useState36 } from "react";
39621
39933
  function useStatuslineState(opts) {
39622
- const [liveModel, setLiveModel] = useState35(opts.model);
39623
- const [liveProvider, setLiveProvider] = useState35(opts.provider ?? "agent");
39624
- const [activeMaxContext, setActiveMaxContext] = useState35(opts.effectiveMaxContext);
39625
- const [yoloLive, setYoloLive] = useState35(opts.yolo);
39626
- const [autonomyLive, setAutonomyLive] = useState35(opts.getAutonomy?.() ?? "off");
39627
- const [liveModeLabel, setLiveModeLabel] = useState35(opts.modeLabel ?? "");
39628
- const [hiddenItems, setHiddenItems] = useState35(
39934
+ const [liveModel, setLiveModel] = useState36(opts.model);
39935
+ const [liveProvider, setLiveProvider] = useState36(opts.provider ?? "agent");
39936
+ const [activeMaxContext, setActiveMaxContext] = useState36(opts.effectiveMaxContext);
39937
+ const [yoloLive, setYoloLive] = useState36(opts.yolo);
39938
+ const [autonomyLive, setAutonomyLive] = useState36(opts.getAutonomy?.() ?? "off");
39939
+ const [liveModeLabel, setLiveModeLabel] = useState36(opts.modeLabel ?? "");
39940
+ const [hiddenItems, setHiddenItems] = useState36(
39629
39941
  Array.isArray(opts.statuslineHiddenItems) ? [...opts.statuslineHiddenItems] : [...opts.statuslineHiddenItems]
39630
39942
  );
39631
- const [sessionCount, setSessionCount] = useState35(0);
39943
+ const [sessionCount, setSessionCount] = useState36(0);
39632
39944
  return {
39633
39945
  liveModel,
39634
39946
  setLiveModel,
@@ -39665,14 +39977,14 @@ function useTuiEnvironmentState({
39665
39977
  setStatuslineHiddenItems,
39666
39978
  saveStatuslineHiddenItems
39667
39979
  }) {
39668
- const [memoryContextMonitor, setMemoryContextMonitor] = useState36(emptyMemoryContextMonitor);
39669
- const [memoryRecordTotal, setMemoryRecordTotal] = useState36();
39670
- const memoryContextMonitorRef = useRef36(memoryContextMonitor);
39980
+ const [memoryContextMonitor, setMemoryContextMonitor] = useState37(emptyMemoryContextMonitor);
39981
+ const [memoryRecordTotal, setMemoryRecordTotal] = useState37();
39982
+ const memoryContextMonitorRef = useRef37(memoryContextMonitor);
39671
39983
  memoryContextMonitorRef.current = memoryContextMonitor;
39672
- const memoryRecordTotalRef = useRef36(memoryRecordTotal);
39984
+ const memoryRecordTotalRef = useRef37(memoryRecordTotal);
39673
39985
  memoryRecordTotalRef.current = memoryRecordTotal;
39674
39986
  const activeMemoryInContext = activeMemoryContextCount(memoryContextMonitor);
39675
- useEffect61(() => {
39987
+ useEffect62(() => {
39676
39988
  let cancelled = false;
39677
39989
  const refreshTotal = async () => {
39678
39990
  try {
@@ -39698,19 +40010,19 @@ function useTuiEnvironmentState({
39698
40010
  modeLabel,
39699
40011
  statuslineHiddenItems
39700
40012
  });
39701
- const hiddenItemsRef = useRef36(statusline.hiddenItems);
40013
+ const hiddenItemsRef = useRef37(statusline.hiddenItems);
39702
40014
  hiddenItemsRef.current = statusline.hiddenItems;
39703
- const [liveToolCount, setLiveToolCount] = useState36(toolCount);
39704
- useEffect61(() => setLiveToolCount(toolCount), [toolCount]);
39705
- const [indexState, setIndexState] = useState36(() => getIndexState2());
39706
- useEffect61(() => {
40015
+ const [liveToolCount, setLiveToolCount] = useState37(toolCount);
40016
+ useEffect62(() => setLiveToolCount(toolCount), [toolCount]);
40017
+ const [indexState, setIndexState] = useState37(() => getIndexState2());
40018
+ useEffect62(() => {
39707
40019
  setIndexState(getIndexState2());
39708
40020
  return onIndexStateChange(setIndexState);
39709
40021
  }, []);
39710
- const [breakerCountdown, setBreakerCountdown] = useState36(
40022
+ const [breakerCountdown, setBreakerCountdown] = useState37(
39711
40023
  () => getProcessRegistry9().getBreakerCountdown()
39712
40024
  );
39713
- useEffect61(() => {
40025
+ useEffect62(() => {
39714
40026
  const settings = getSettings?.();
39715
40027
  if (settings) {
39716
40028
  getProcessRegistry9().setBreakerConfig({
@@ -39721,7 +40033,7 @@ function useTuiEnvironmentState({
39721
40033
  return getProcessRegistry9().onBreakerCountdownChange(setBreakerCountdown);
39722
40034
  }, [getSettings]);
39723
40035
  const breakerArmed = breakerCountdown !== null;
39724
- useEffect61(() => {
40036
+ useEffect62(() => {
39725
40037
  if (!breakerArmed) return;
39726
40038
  const timer = setInterval(
39727
40039
  () => setBreakerCountdown(getProcessRegistry9().getBreakerCountdown()),
@@ -39729,10 +40041,10 @@ function useTuiEnvironmentState({
39729
40041
  );
39730
40042
  return () => clearInterval(timer);
39731
40043
  }, [breakerArmed]);
39732
- useEffect61(() => {
40044
+ useEffect62(() => {
39733
40045
  statusline.setHiddenItems([...statuslineHiddenItems]);
39734
40046
  }, [statuslineHiddenItems, statusline.setHiddenItems]);
39735
- useEffect61(() => {
40047
+ useEffect62(() => {
39736
40048
  setStatuslineHiddenItems(statusline.hiddenItems);
39737
40049
  saveStatuslineHiddenItems(statusline.hiddenItems).catch?.((error) => {
39738
40050
  console.error(
@@ -39762,13 +40074,13 @@ function useTuiEnvironmentState({
39762
40074
  }
39763
40075
 
39764
40076
  // src/hooks/use-tui-event-bridge.ts
39765
- import { useCallback as useCallback24, useEffect as useEffect64 } from "react";
40077
+ import { useCallback as useCallback25, useEffect as useEffect65 } from "react";
39766
40078
 
39767
40079
  // src/hooks/use-brain-events.ts
39768
- import { useEffect as useEffect62 } from "react";
40080
+ import { useEffect as useEffect63 } from "react";
39769
40081
  var MONITOR_INTERVENTION_GRACE_MS = 5e3;
39770
40082
  function useBrainEvents(events, dispatch, getSessionId) {
39771
- useEffect62(() => {
40083
+ useEffect63(() => {
39772
40084
  const isCurrentSession2 = (sessionId) => {
39773
40085
  const current = getSessionId?.();
39774
40086
  return !sessionId || !current || sessionId === current;
@@ -39938,7 +40250,7 @@ function useBrainEvents(events, dispatch, getSessionId) {
39938
40250
 
39939
40251
  // src/hooks/use-subagent-events.ts
39940
40252
  import { expectDefined as expectDefined5 } from "@wrongstack/core/utils";
39941
- import { useCallback as useCallback23, useEffect as useEffect63, useRef as useRef37 } from "react";
40253
+ import { useCallback as useCallback24, useEffect as useEffect64, useRef as useRef38 } from "react";
39942
40254
  var STREAM_COLORS2 = ["cyan", "magenta", "yellow", "green", "blue"];
39943
40255
  function providerContextLimitHistoryEntry(event) {
39944
40256
  if (event.source !== "provider" || event.decreased !== true || typeof event.previousMaxContext !== "number" || event.previousMaxContext <= event.maxContext) {
@@ -39981,21 +40293,21 @@ function labelFor2(labelsRef, id, name) {
39981
40293
  return v;
39982
40294
  }
39983
40295
  function useSubagentEvents(events, dispatch, setActiveMaxContext, getSessionId, getChatMode, sessionGenerationRef) {
39984
- const labelsRef = useRef37(/* @__PURE__ */ new Map());
39985
- const ctxDispatchRef = useRef37(
40296
+ const labelsRef = useRef38(/* @__PURE__ */ new Map());
40297
+ const ctxDispatchRef = useRef38(
39986
40298
  /* @__PURE__ */ new Map()
39987
40299
  );
39988
- const leaderCtxDispatchRef = useRef37(void 0);
39989
- const leaderCtxFlushRef = useRef37(void 0);
39990
- const latestLeaderCtxPctRef = useRef37(void 0);
39991
- const ctxFlushRef = useRef37(/* @__PURE__ */ new Map());
40300
+ const leaderCtxDispatchRef = useRef38(void 0);
40301
+ const leaderCtxFlushRef = useRef38(void 0);
40302
+ const latestLeaderCtxPctRef = useRef38(void 0);
40303
+ const ctxFlushRef = useRef38(/* @__PURE__ */ new Map());
39992
40304
  const gate = useFleetGenerationGate(sessionGenerationRef);
39993
- const lbl = useCallback23(
40305
+ const lbl = useCallback24(
39994
40306
  (id, name) => labelFor2(labelsRef, id, name),
39995
40307
  []
39996
40308
  // labelsRef is a stable ref
39997
40309
  );
39998
- useEffect63(() => {
40310
+ useEffect64(() => {
39999
40311
  const seen = /* @__PURE__ */ new Set();
40000
40312
  const isCurrentSession2 = (sessionId) => {
40001
40313
  const current = getSessionId?.();
@@ -40333,14 +40645,14 @@ function useTuiEventBridge({
40333
40645
  onClearHistory,
40334
40646
  sessionGenerationRef
40335
40647
  }) {
40336
- const getChatMode = useCallback24(() => stateRef.current.fleetChat, [stateRef]);
40648
+ const getChatMode = useCallback25(() => stateRef.current.fleetChat, [stateRef]);
40337
40649
  useSubagentEvents(events, dispatch, setActiveMaxContext, getSessionId, getChatMode, sessionGenerationRef);
40338
40650
  useSessionEvents(events, dispatch, onClearHistory, getSessionId);
40339
40651
  useBrainEvents(events, dispatch, getSessionId);
40340
40652
  useGoalEvents(subscribeGoal, dispatch, stateRef, getSessionId);
40341
40653
  }
40342
40654
  function useSessionEvents(events, dispatch, onClearHistory, getSessionId) {
40343
- useEffect64(() => {
40655
+ useEffect65(() => {
40344
40656
  const isCurrentSession2 = (sessionId) => {
40345
40657
  const current = getSessionId?.();
40346
40658
  return !sessionId || !current || sessionId === current;
@@ -40371,7 +40683,7 @@ function useSessionEvents(events, dispatch, onClearHistory, getSessionId) {
40371
40683
  }, [events, dispatch, onClearHistory, getSessionId]);
40372
40684
  }
40373
40685
  function useGoalEvents(subscribeGoal, dispatch, stateRef, getSessionId) {
40374
- useEffect64(() => {
40686
+ useEffect65(() => {
40375
40687
  if (!subscribeGoal) return;
40376
40688
  const isCurrentSession2 = (sessionId) => {
40377
40689
  const current = getSessionId?.();
@@ -40594,10 +40906,7 @@ import {
40594
40906
  } from "@wrongstack/core/design";
40595
40907
  import { SKILL_LIMITS, stripFrontmatter } from "@wrongstack/core/skills";
40596
40908
  import { toErrorMessage as toErrorMessage9 } from "@wrongstack/core/utils";
40597
- import {
40598
- useEffect as useEffect65,
40599
- useRef as useRef38
40600
- } from "react";
40909
+ import { useEffect as useEffect66 } from "react";
40601
40910
  function useTuiSlashCommands({
40602
40911
  slashRegistry,
40603
40912
  skillLoader,
@@ -40621,17 +40930,7 @@ function useTuiSlashCommands({
40621
40930
  listSessions,
40622
40931
  openPromptPicker
40623
40932
  }) {
40624
- const resourceOriginalsRef = useRef38(
40625
- new Map(
40626
- ["fallback", "profile", "provider-status", "memory", "worktree", "git"].map(
40627
- (name) => [name, slashRegistry.get(name)]
40628
- )
40629
- )
40630
- );
40631
- const panelOriginalsRef = useRef38(
40632
- new Map(["cron", "prompts"].map((name) => [name, slashRegistry.get(name)]))
40633
- );
40634
- useEffect65(() => {
40933
+ useEffect66(() => {
40635
40934
  if (!getPickableProviders || !switchProviderAndModel) return;
40636
40935
  const cmd = {
40637
40936
  name: "model",
@@ -40642,12 +40941,12 @@ function useTuiSlashCommands({
40642
40941
  return { message: void 0 };
40643
40942
  }
40644
40943
  };
40645
- slashRegistry.register(cmd, "tui", { official: true });
40646
- return () => {
40647
- slashRegistry.unregister("model");
40648
- };
40944
+ return registerSlashCommandLifecycle(slashRegistry, cmd, {
40945
+ owner: "tui",
40946
+ official: true
40947
+ });
40649
40948
  }, [slashRegistry, getPickableProviders, switchProviderAndModel, openModelPicker]);
40650
- useEffect65(() => {
40949
+ useEffect66(() => {
40651
40950
  const cmd = {
40652
40951
  name: "f",
40653
40952
  description: "Open F-key panel picker. Arrow keys to navigate, Enter to open, Esc to close.",
@@ -40656,12 +40955,12 @@ function useTuiSlashCommands({
40656
40955
  return { message: void 0 };
40657
40956
  }
40658
40957
  };
40659
- slashRegistry.register(cmd, "tui", { official: true });
40660
- return () => {
40661
- slashRegistry.unregister("f");
40662
- };
40958
+ return registerSlashCommandLifecycle(slashRegistry, cmd, {
40959
+ owner: "tui",
40960
+ official: true
40961
+ });
40663
40962
  }, [slashRegistry, openFKeyPicker]);
40664
- useEffect65(() => {
40963
+ useEffect66(() => {
40665
40964
  const cmd = {
40666
40965
  name: "design",
40667
40966
  description: "Design Studio: /design (picker) | <kit> [stack] | off | foundations | set <k=v> | tune <k=v> | swap <kit> | materialize [stack] [path] | verify.",
@@ -40758,7 +41057,7 @@ ${menu}` };
40758
41057
  kitId: active.kit,
40759
41058
  outPath
40760
41059
  });
40761
- const fsp = await import("node:fs/promises");
41060
+ const fsp3 = await import("node:fs/promises");
40762
41061
  const nodePath = await import("node:path");
40763
41062
  const { resolveMaterializeTarget } = await import("@wrongstack/core/design");
40764
41063
  let abs;
@@ -40768,8 +41067,8 @@ ${menu}` };
40768
41067
  return { message: e.message };
40769
41068
  }
40770
41069
  try {
40771
- await fsp.mkdir(nodePath.dirname(abs), { recursive: true });
40772
- await fsp.writeFile(abs, result.content);
41070
+ await fsp3.mkdir(nodePath.dirname(abs), { recursive: true });
41071
+ await fsp3.writeFile(abs, result.content);
40773
41072
  } catch (e) {
40774
41073
  return { message: `Failed to write ${result.path}: ${e.message}` };
40775
41074
  }
@@ -40788,12 +41087,12 @@ ${menu}` };
40788
41087
  return { runText: `design use ${kit.id}${stack ? ` --stack ${stack}` : ""}` };
40789
41088
  }
40790
41089
  };
40791
- slashRegistry.register(cmd, "tui", { official: true });
40792
- return () => {
40793
- slashRegistry.unregister("design");
40794
- };
41090
+ return registerSlashCommandLifecycle(slashRegistry, cmd, {
41091
+ owner: "tui",
41092
+ official: true
41093
+ });
40795
41094
  }, [slashRegistry, projectRoot, agent]);
40796
- useEffect65(() => {
41095
+ useEffect66(() => {
40797
41096
  if (!getSettings || !saveSettings) return;
40798
41097
  const cmd = {
40799
41098
  name: "settings",
@@ -40895,12 +41194,12 @@ Available chords:
40895
41194
  return { message: void 0 };
40896
41195
  }
40897
41196
  };
40898
- slashRegistry.register(cmd, "tui", { official: true });
40899
- return () => {
40900
- slashRegistry.unregister("settings");
40901
- };
41197
+ return registerSlashCommandLifecycle(slashRegistry, cmd, {
41198
+ owner: "tui",
41199
+ official: true
41200
+ });
40902
41201
  }, [slashRegistry, getSettings, saveSettings, openSettings, dispatch]);
40903
- useEffect65(() => {
41202
+ useEffect66(() => {
40904
41203
  const cmd = {
40905
41204
  name: "settings-get",
40906
41205
  aliases: ["config-get", "get"],
@@ -40927,12 +41226,12 @@ Available chords:
40927
41226
  return { message: `${result.label}: ${result.displayValue}` };
40928
41227
  }
40929
41228
  };
40930
- slashRegistry.register(cmd, "tui", { official: true });
40931
- return () => {
40932
- slashRegistry.unregister("settings-get");
40933
- };
41229
+ return registerSlashCommandLifecycle(slashRegistry, cmd, {
41230
+ owner: "tui",
41231
+ official: true
41232
+ });
40934
41233
  }, [slashRegistry, state.settingsPicker]);
40935
- useEffect65(() => {
41234
+ useEffect66(() => {
40936
41235
  const cmd = {
40937
41236
  name: "statusline",
40938
41237
  aliases: ["sl"],
@@ -40980,12 +41279,12 @@ Available chords:
40980
41279
  return { message: void 0 };
40981
41280
  }
40982
41281
  };
40983
- slashRegistry.register(cmd, "tui", { official: true });
40984
- return () => {
40985
- slashRegistry.unregister("statusline");
40986
- };
41282
+ return registerSlashCommandLifecycle(slashRegistry, cmd, {
41283
+ owner: "tui",
41284
+ official: true
41285
+ });
40987
41286
  }, [slashRegistry, openStatuslinePicker, setHiddenItems]);
40988
- useEffect65(() => {
41287
+ useEffect66(() => {
40989
41288
  const cmd = {
40990
41289
  name: "mailbox",
40991
41290
  aliases: ["inbox", "mail"],
@@ -40995,12 +41294,12 @@ Available chords:
40995
41294
  return { message: void 0 };
40996
41295
  }
40997
41296
  };
40998
- slashRegistry.register(cmd, "tui", { official: true });
40999
- return () => {
41000
- slashRegistry.unregister("mailbox");
41001
- };
41297
+ return registerSlashCommandLifecycle(slashRegistry, cmd, {
41298
+ owner: "tui",
41299
+ official: true
41300
+ });
41002
41301
  }, [slashRegistry]);
41003
- useEffect65(() => {
41302
+ useEffect66(() => {
41004
41303
  if (!switchAutonomy) return;
41005
41304
  const cmd = {
41006
41305
  name: "autonomy",
@@ -41011,12 +41310,12 @@ Available chords:
41011
41310
  return { message: void 0 };
41012
41311
  }
41013
41312
  };
41014
- slashRegistry.register(cmd, "tui", { official: true });
41015
- return () => {
41016
- slashRegistry.unregister("autonomy");
41017
- };
41313
+ return registerSlashCommandLifecycle(slashRegistry, cmd, {
41314
+ owner: "tui",
41315
+ official: true
41316
+ });
41018
41317
  }, [slashRegistry, switchAutonomy]);
41019
- useEffect65(() => {
41318
+ useEffect66(() => {
41020
41319
  if (!skillLoader) return;
41021
41320
  const cmd = {
41022
41321
  name: "skill",
@@ -41045,72 +41344,78 @@ Available chords:
41045
41344
  }
41046
41345
  }
41047
41346
  };
41048
- slashRegistry.register(cmd, "tui", { official: true });
41049
- return () => {
41050
- slashRegistry.unregister("skill");
41051
- };
41347
+ return registerSlashCommandLifecycle(slashRegistry, cmd, {
41348
+ owner: "tui",
41349
+ official: true
41350
+ });
41052
41351
  }, [slashRegistry, skillLoader, dispatch]);
41053
- useEffect65(() => {
41352
+ useEffect66(() => {
41054
41353
  if (!getResourceMenu) return;
41055
41354
  const names = ["fallback", "profile", "provider-status", "memory", "worktree", "git"];
41355
+ const cleanups = [];
41056
41356
  for (const name of names) {
41057
- const original = resourceOriginalsRef.current.get(name);
41058
- slashRegistry.register(
41059
- {
41060
- name,
41061
- description: original?.description ?? `Browse ${name} state interactively.`,
41062
- argsHint: original?.argsHint,
41063
- help: original?.help,
41064
- async run(args, ctx) {
41065
- if (args.trim())
41066
- return original?.run(args, ctx) ?? { message: `/${name} is unavailable.` };
41067
- try {
41068
- const snapshot = await getResourceMenu(name);
41069
- dispatch({ type: "resourceMenuOpen", snapshot });
41070
- return { message: void 0 };
41071
- } catch (err) {
41072
- return { message: `Could not load ${name}: ${toErrorMessage9(err)}` };
41357
+ const original = slashRegistry.get(name);
41358
+ cleanups.push(
41359
+ registerSlashCommandLifecycle(
41360
+ slashRegistry,
41361
+ {
41362
+ name,
41363
+ description: original?.description ?? `Browse ${name} state interactively.`,
41364
+ argsHint: original?.argsHint,
41365
+ help: original?.help,
41366
+ async run(args, ctx) {
41367
+ if (args.trim())
41368
+ return original?.run(args, ctx) ?? { message: `/${name} is unavailable.` };
41369
+ try {
41370
+ const snapshot = await getResourceMenu(name);
41371
+ dispatch({ type: "resourceMenuOpen", snapshot });
41372
+ return { message: void 0 };
41373
+ } catch (err) {
41374
+ return { message: `Could not load ${name}: ${toErrorMessage9(err)}` };
41375
+ }
41073
41376
  }
41074
- }
41075
- },
41076
- "tui",
41077
- { official: true }
41377
+ },
41378
+ { owner: "tui", official: true }
41379
+ )
41078
41380
  );
41079
41381
  }
41080
41382
  return () => {
41081
- for (const name of names) slashRegistry.unregister(name);
41383
+ for (const cleanup of [...cleanups].reverse()) cleanup();
41082
41384
  };
41083
41385
  }, [slashRegistry, getResourceMenu, dispatch]);
41084
- useEffect65(() => {
41386
+ useEffect66(() => {
41085
41387
  const definitions = [
41086
41388
  { name: "cron", open: () => dispatch({ type: "toggleCronMonitor" }) },
41087
41389
  { name: "prompts", open: () => void openPromptPicker() }
41088
41390
  ];
41391
+ const cleanups = [];
41089
41392
  for (const definition of definitions) {
41090
- const original = panelOriginalsRef.current.get(definition.name);
41091
- slashRegistry.register(
41092
- {
41093
- name: definition.name,
41094
- description: original?.description ?? `Open the ${definition.name} browser.`,
41095
- argsHint: original?.argsHint,
41096
- help: original?.help,
41097
- async run(args, ctx) {
41098
- if (args.trim()) {
41099
- return original?.run(args, ctx) ?? { message: `/${definition.name} is unavailable.` };
41393
+ const original = slashRegistry.get(definition.name);
41394
+ cleanups.push(
41395
+ registerSlashCommandLifecycle(
41396
+ slashRegistry,
41397
+ {
41398
+ name: definition.name,
41399
+ description: original?.description ?? `Open the ${definition.name} browser.`,
41400
+ argsHint: original?.argsHint,
41401
+ help: original?.help,
41402
+ async run(args, ctx) {
41403
+ if (args.trim()) {
41404
+ return original?.run(args, ctx) ?? { message: `/${definition.name} is unavailable.` };
41405
+ }
41406
+ definition.open();
41407
+ return { message: void 0 };
41100
41408
  }
41101
- definition.open();
41102
- return { message: void 0 };
41103
- }
41104
- },
41105
- "tui",
41106
- { official: true }
41409
+ },
41410
+ { owner: "tui", official: true }
41411
+ )
41107
41412
  );
41108
41413
  }
41109
41414
  return () => {
41110
- for (const { name } of definitions) slashRegistry.unregister(name);
41415
+ for (const cleanup of [...cleanups].reverse()) cleanup();
41111
41416
  };
41112
41417
  }, [slashRegistry, dispatch, openPromptPicker]);
41113
- useEffect65(() => {
41418
+ useEffect66(() => {
41114
41419
  const cmd = {
41115
41420
  name: "theme",
41116
41421
  description: "Pick a TUI color theme preset interactively (picker).",
@@ -41137,12 +41442,12 @@ Available chords:
41137
41442
  return { message: void 0 };
41138
41443
  }
41139
41444
  };
41140
- slashRegistry.register(cmd, "tui", { official: true });
41141
- return () => {
41142
- slashRegistry.unregister("theme");
41143
- };
41445
+ return registerSlashCommandLifecycle(slashRegistry, cmd, {
41446
+ owner: "tui",
41447
+ official: true
41448
+ });
41144
41449
  }, [slashRegistry, dispatch]);
41145
- useEffect65(() => {
41450
+ useEffect66(() => {
41146
41451
  const cmd = {
41147
41452
  name: "resume",
41148
41453
  aliases: ["load"],
@@ -41165,28 +41470,28 @@ Available chords:
41165
41470
  return { message: void 0 };
41166
41471
  }
41167
41472
  };
41168
- slashRegistry.register(cmd, "tui", { official: true });
41169
- return () => {
41170
- slashRegistry.unregister("resume");
41171
- };
41473
+ return registerSlashCommandLifecycle(slashRegistry, cmd, {
41474
+ owner: "tui",
41475
+ official: true
41476
+ });
41172
41477
  }, [slashRegistry, listSessions]);
41173
41478
  }
41174
41479
 
41175
41480
  // src/hooks/use-working-dir-chip.ts
41176
- import * as path6 from "node:path";
41177
- import { useEffect as useEffect66, useState as useState37 } from "react";
41481
+ import * as path8 from "node:path";
41482
+ import { useEffect as useEffect67, useState as useState38 } from "react";
41178
41483
  function formatWorkingDirChip(workingDir, projectRoot) {
41179
41484
  if (!workingDir || workingDir === projectRoot) return void 0;
41180
- const impl = workingDir.includes("\\") || projectRoot.includes("\\") ? path6.win32 : path6;
41485
+ const impl = workingDir.includes("\\") || projectRoot.includes("\\") ? path8.win32 : path8;
41181
41486
  const rel = impl.relative(projectRoot, workingDir) || ".";
41182
41487
  if (rel === ".") return void 0;
41183
41488
  return rel.replaceAll(impl.sep, "/");
41184
41489
  }
41185
41490
  function useWorkingDirChip(ctx, projectRoot) {
41186
- const [workingDirChip, setWorkingDirChip] = useState37(
41491
+ const [workingDirChip, setWorkingDirChip] = useState38(
41187
41492
  () => formatWorkingDirChip(ctx.workingDir, projectRoot)
41188
41493
  );
41189
- useEffect66(() => {
41494
+ useEffect67(() => {
41190
41495
  setWorkingDirChip(formatWorkingDirChip(ctx.workingDir, projectRoot));
41191
41496
  return ctx.onWorkingDirChanged((newDir) => {
41192
41497
  setWorkingDirChip(formatWorkingDirChip(newDir, projectRoot));
@@ -43518,14 +43823,16 @@ function App(props) {
43518
43823
  sessionsDir
43519
43824
  });
43520
43825
  const historyScrollRef = useRef39(null);
43521
- const onScrollInfo = useCallback25(
43826
+ const onScrollInfo = useCallback26(
43522
43827
  (info) => dispatch({ type: "setHistoryScrolled", scrolled: info.scrolled }),
43523
43828
  [dispatch]
43524
43829
  );
43525
- const onRequestOlderEntries = useCallback25(() => {
43526
- dispatch({ type: "startArchiveLoad" });
43527
- setTimeout(() => dispatch({ type: "archiveLoaded", entries: [] }), 0);
43528
- }, [dispatch]);
43830
+ const { onRequestOlderEntries } = useHistoryArchive({
43831
+ entries: state.entries,
43832
+ dispatch,
43833
+ sessionsDir,
43834
+ sessionId: agent.ctx.session?.id
43835
+ });
43529
43836
  const onHistoryCopy = useHistoryCopyNotice(dispatch);
43530
43837
  const { focusedBoardId, setFocusedBoardId, boardFocusRef } = useKanbanBoardFocus();
43531
43838
  useInputHistoryPersistence({
@@ -43593,8 +43900,8 @@ function App(props) {
43593
43900
  submitRef
43594
43901
  } = useAppRuntimeRefs(attachments, state);
43595
43902
  const projectName = React9.useMemo(() => {
43596
- const base = path7.basename(projectRoot);
43597
- return base && base !== path7.sep ? base : void 0;
43903
+ const base = path9.basename(projectRoot);
43904
+ return base && base !== path9.sep ? base : void 0;
43598
43905
  }, [projectRoot]);
43599
43906
  const workingDirChip = useWorkingDirChip(agent.ctx, projectRoot);
43600
43907
  const {
@@ -43613,7 +43920,7 @@ function App(props) {
43613
43920
  liveSettings,
43614
43921
  mailbox.mailboxPanelOpen
43615
43922
  );
43616
- useEffect67(() => {
43923
+ useEffect68(() => {
43617
43924
  titleController?.setModel(liveModel);
43618
43925
  }, [titleController, liveModel]);
43619
43926
  const activity = useTuiActivity({
@@ -43629,11 +43936,11 @@ function App(props) {
43629
43936
  builderRef
43630
43937
  });
43631
43938
  const { displayThinkingWord, refreshGoalSummary } = activity;
43632
- const liveDirector = useCallback25(
43939
+ const liveDirector = useCallback26(
43633
43940
  () => getDirector?.() ?? director,
43634
43941
  [getDirector, director]
43635
43942
  );
43636
- const clearPendingConfirms = useCallback25(() => {
43943
+ const clearPendingConfirms = useCallback26(() => {
43637
43944
  const queue = stateRef.current.confirmQueue;
43638
43945
  if (queue.length === 0) return;
43639
43946
  for (const c of queue) {
@@ -43650,7 +43957,7 @@ function App(props) {
43650
43957
  dispatch,
43651
43958
  open: state.authPanel.open
43652
43959
  });
43653
- useEffect67(() => {
43960
+ useEffect68(() => {
43654
43961
  if (!secretInputController) return;
43655
43962
  const previousSecret = secretInputController.readSecret;
43656
43963
  const previousText = secretInputController.readText;
@@ -43662,13 +43969,13 @@ function App(props) {
43662
43969
  else delete secretInputController.readText;
43663
43970
  };
43664
43971
  }, [secretInputController, authPanelController.readSecret, authPanelController.readText]);
43665
- const statuslineHiddenForPicker = useCallback25(() => {
43972
+ const statuslineHiddenForPicker = useCallback26(() => {
43666
43973
  return mergeStatuslineHiddenItems(
43667
43974
  hiddenItemsRef.current,
43668
43975
  stateRef.current.statuslinePicker.hiddenItems
43669
43976
  );
43670
43977
  }, []);
43671
- const openStatuslinePicker = useCallback25(
43978
+ const openStatuslinePicker = useCallback26(
43672
43979
  (field) => {
43673
43980
  if (field !== void 0) {
43674
43981
  dispatch({ type: "statuslineFieldSet", field });
@@ -43746,7 +44053,7 @@ function App(props) {
43746
44053
  dispatch({ type: "clearInput" });
43747
44054
  };
43748
44055
  const staleGuardRef = useRef39(JSON.stringify({ a: "", y: false, m: "", model: "", provider: "" }));
43749
- useEffect67(() => {
44056
+ useEffect68(() => {
43750
44057
  const poll = () => {
43751
44058
  const a = getAutonomy?.() ?? "off";
43752
44059
  const y = getYolo?.() ?? false;
@@ -43962,8 +44269,8 @@ function App(props) {
43962
44269
  saveSettings,
43963
44270
  midRunSendPickerRef
43964
44271
  });
43965
- const getActiveSessionId = useCallback25(() => agent.ctx.session.id, [agent]);
43966
- const getLeaderTranscript = useCallback25(
44272
+ const getActiveSessionId = useCallback26(() => agent.ctx.session.id, [agent]);
44273
+ const getLeaderTranscript = useCallback26(
43967
44274
  () => leaderTimelineFromEntries(stateRef.current.entries),
43968
44275
  [stateRef]
43969
44276
  );
@@ -44032,7 +44339,7 @@ function App(props) {
44032
44339
  setDraft,
44033
44340
  tokenPreviewsRef
44034
44341
  });
44035
- const onThemePickerEnter = useCallback25(() => {
44342
+ const onThemePickerEnter = useCallback26(() => {
44036
44343
  const preset = THEME_OPTIONS[Math.max(0, state.themePicker.selected)];
44037
44344
  const presetName = preset?.id;
44038
44345
  if (!presetName) return;
@@ -44373,7 +44680,7 @@ import {
44373
44680
  // src/run-tui.ts
44374
44681
  import { createWriteStream } from "node:fs";
44375
44682
  import { tmpdir } from "node:os";
44376
- import { join as join6 } from "node:path";
44683
+ import { join as join8 } from "node:path";
44377
44684
  import { detectTerminal, TerminalLifecycle, writeErr } from "@wrongstack/core/utils";
44378
44685
  import { getProcessRegistry as getProcessRegistry10 } from "@wrongstack/tools";
44379
44686
  import { render } from "ink";
@@ -44381,7 +44688,7 @@ import React10 from "react";
44381
44688
 
44382
44689
  // src/run-tui-client-registration.ts
44383
44690
  import { randomUUID } from "node:crypto";
44384
- import * as path8 from "node:path";
44691
+ import * as path10 from "node:path";
44385
44692
  import { RemoteMailbox, resolveProjectDir } from "@wrongstack/core/coordination";
44386
44693
  import {
44387
44694
  createHqPublisherFromEnv,
@@ -44480,7 +44787,7 @@ function createRunTuiClientRegistration(opts) {
44480
44787
  await mailbox.registerClient({
44481
44788
  clientId,
44482
44789
  sessionId: opts.getSessionId?.() ?? opts.projectRoot,
44483
- name: `TUI [${path8.basename(opts.projectRoot)}]`,
44790
+ name: `TUI [${path10.basename(opts.projectRoot)}]`,
44484
44791
  source: "tui",
44485
44792
  pid: process.pid
44486
44793
  });
@@ -44498,7 +44805,7 @@ function createRunTuiClientRegistration(opts) {
44498
44805
  tuiHqPublisher = createHqPublisherFromEnv({
44499
44806
  clientKind: "tui",
44500
44807
  projectRoot: opts.projectRoot,
44501
- projectName: path8.basename(opts.projectRoot),
44808
+ projectName: path10.basename(opts.projectRoot),
44502
44809
  appConfig: opts.appConfig
44503
44810
  });
44504
44811
  tuiHqPublisher?.connect();
@@ -44511,7 +44818,7 @@ function createRunTuiClientRegistration(opts) {
44511
44818
  events: opts.events,
44512
44819
  sessionId: tuiSessionId,
44513
44820
  projectRoot: opts.projectRoot,
44514
- projectName: path8.basename(opts.projectRoot)
44821
+ projectName: path10.basename(opts.projectRoot)
44515
44822
  })
44516
44823
  );
44517
44824
  } catch {
@@ -44640,7 +44947,7 @@ function createRunTuiClientRegistration(opts) {
44640
44947
  }
44641
44948
 
44642
44949
  // src/run-tui-title-controller.ts
44643
- import * as path9 from "node:path";
44950
+ import * as path11 from "node:path";
44644
44951
 
44645
44952
  // src/terminal-title.ts
44646
44953
  var SPINNER = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
@@ -44720,7 +45027,7 @@ function createRunTuiTitleController(opts) {
44720
45027
  stdout: opts.stdout,
44721
45028
  events: opts.events,
44722
45029
  model: currentModel,
44723
- appName: opts.projectRoot ? path9.basename(opts.projectRoot) : void 0
45030
+ appName: opts.projectRoot ? path11.basename(opts.projectRoot) : void 0
44724
45031
  });
44725
45032
  };
44726
45033
  const stop = () => {
@@ -44810,7 +45117,7 @@ async function runTui(opts) {
44810
45117
  const traceEnv = process.env["WRONGSTACK_TUI_TRACE"];
44811
45118
  let restoreTrace = null;
44812
45119
  if (traceEnv && traceEnv !== "0") {
44813
- const tracePath = traceEnv === "1" ? join6(tmpdir(), `wstack-tui-trace-${process.pid}.bin`) : traceEnv;
45120
+ const tracePath = traceEnv === "1" ? join8(tmpdir(), `wstack-tui-trace-${process.pid}.bin`) : traceEnv;
44814
45121
  try {
44815
45122
  const stream = createWriteStream(tracePath);
44816
45123
  const realWrite = stdout.write.bind(stdout);