@wrongstack/tui 0.282.2 → 0.283.1

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
@@ -1,9 +1,9 @@
1
- import { hasOpenTodos, writeErr, resolveProjectDir, wstackGlobalRoot, createHqPublisherFromEnv, startFleetTelemetryBridge, startBrainTelemetryBridge, startWorktreeTelemetryBridge, startToolTelemetryBridge, startCostTelemetryBridge, GlobalMailbox, resolveWstackPaths, loadGoal, InputBuilder, DefaultSessionRewinder, writeOut, formatTodosList, buildGoalPreamble, expectDefined as expectDefined$1, InputHistoryStore, DefaultSecretScrubber, INPUT_HISTORY_DEFAULT_MAX, DefaultPromptLoader, PromptUsageStore, getDesignKitLoader, clearActiveKit, clearPersistedActiveKit, recordOverrides, setDesignOverrides, loadActiveKit, isDesignStack, materializeTokens, applyTokenOverrides, setActiveKit, projectSlug, shouldEnhance, enhanceUserPrompt, recentTextTurns, normalizedEqual, setBtwNote, listBoards, getBoard, createBoard, addTask, buildChildEnv, removeTask, moveTask, updateTask, duplicateBoard, copyTaskToBoard, transferTaskToBoard } from '@wrongstack/core';
1
+ import { hasOpenTodos, writeErr, resolveProjectDir, wstackGlobalRoot, createHqPublisherFromEnv, startFleetTelemetryBridge, startBrainTelemetryBridge, startWorktreeTelemetryBridge, startToolTelemetryBridge, startCostTelemetryBridge, GlobalMailbox, resolveWstackPaths, loadGoal, InputBuilder, DefaultSessionRewinder, writeOut, formatTodosList, buildGoalPreamble, expectDefined as expectDefined$1, InputHistoryStore, DefaultSecretScrubber, INPUT_HISTORY_DEFAULT_MAX, DefaultPromptLoader, PromptUsageStore, getDesignKitLoader, clearActiveKit, clearPersistedActiveKit, recordOverrides, setDesignOverrides, loadActiveKit, isDesignStack, materializeTokens, applyTokenOverrides, setActiveKit, projectSlug, detectContinueIntent, resolveContinuation, shouldEnhance, enhanceUserPrompt, recentTextTurns, normalizedEqual, setBtwNote, listBoards, getBoard, createBoard, addTask, buildChildEnv, removeTask, moveTask, updateTask, duplicateBoard, copyTaskToBoard, transferTaskToBoard } from '@wrongstack/core';
2
2
  export { buildGoalPreamble } from '@wrongstack/core';
3
3
  import { Box as Box$1, useStdout, Static, useInput, useStdin, measureElement, Text as Text$1, render, useApp } from 'ink';
4
4
  import { randomUUID } from 'crypto';
5
5
  import * as path9 from 'path';
6
- import React5, { forwardRef, useState, useEffect, useMemo, memo, useRef, useLayoutEffect, useCallback, useReducer, isValidElement, Fragment as Fragment$1 } from 'react';
6
+ import React7, { forwardRef, useState, useEffect, useMemo, memo, useRef, useLayoutEffect, useCallback, useReducer, isValidElement, Fragment as Fragment$1 } from 'react';
7
7
  import * as fsp from 'fs/promises';
8
8
  import { expectDefined, toErrorMessage, resolveWstackPaths as resolveWstackPaths$1, sessionScopedPath } from '@wrongstack/core/utils';
9
9
  import { routeImagesForModel } from '@wrongstack/runtime/vision';
@@ -3524,6 +3524,10 @@ function reducer(state, action) {
3524
3524
  return { ...state, confirmQueue: [...state.confirmQueue, action.info] };
3525
3525
  case "confirmClose":
3526
3526
  return { ...state, confirmQueue: state.confirmQueue.slice(1) };
3527
+ case "shellCommandWarningOpen":
3528
+ return { ...state, shellCommandWarning: action.info };
3529
+ case "shellCommandWarningClose":
3530
+ return { ...state, shellCommandWarning: null };
3527
3531
  case "enhanceOpen":
3528
3532
  return { ...state, enhance: action.info };
3529
3533
  case "enhanceClose":
@@ -3532,6 +3536,10 @@ function reducer(state, action) {
3532
3536
  return { ...state, enhanceEnabled: action.enabled };
3533
3537
  case "enhanceBusy":
3534
3538
  return { ...state, enhanceBusy: action.on };
3539
+ case "continueConfirmOpen":
3540
+ return { ...state, continueConfirm: action.info };
3541
+ case "continueConfirmClose":
3542
+ return { ...state, continueConfirm: null };
3535
3543
  case "escConfirmOpen":
3536
3544
  return { ...state, escConfirm: { snapshot: action.snapshot } };
3537
3545
  case "escConfirmClose":
@@ -6346,22 +6354,62 @@ function BrainDecisionPrompt({
6346
6354
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Press A/B/C or 1/2/3 to answer; Esc or D denies with the safe default." })
6347
6355
  ] });
6348
6356
  }
6357
+ function modeFamily(mode) {
6358
+ if (mode.tags?.includes("lite")) return "lite";
6359
+ if (mode.tags?.includes("deep")) return "deep";
6360
+ if (mode.tags?.includes("balanced") || mode.id === "default") return "balanced";
6361
+ return "custom";
6362
+ }
6349
6363
  function toModeOptions(modes, activeId) {
6350
6364
  return modes.map((m) => ({
6351
6365
  id: m.id,
6352
6366
  name: m.name,
6353
6367
  description: m.description,
6368
+ family: modeFamily(m),
6354
6369
  isActive: m.id === activeId
6355
6370
  }));
6356
6371
  }
6372
+ function familyLabel(family) {
6373
+ switch (family) {
6374
+ case "lite":
6375
+ return "lite";
6376
+ case "deep":
6377
+ return "deep";
6378
+ case "balanced":
6379
+ return "base";
6380
+ case "custom":
6381
+ return "custom";
6382
+ }
6383
+ }
6384
+ function familyColor(family) {
6385
+ switch (family) {
6386
+ case "lite":
6387
+ return "green";
6388
+ case "deep":
6389
+ return "magenta";
6390
+ case "balanced":
6391
+ return "blue";
6392
+ case "custom":
6393
+ return "yellow";
6394
+ }
6395
+ }
6357
6396
  function ModePicker({
6358
6397
  modes,
6359
6398
  selected,
6360
6399
  hint
6361
6400
  }) {
6401
+ const activeMode = modes.find((mode) => mode.isActive);
6402
+ const modeMeta = `${modes.length} mode${modes.length === 1 ? "" : "s"}`;
6362
6403
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "blue", paddingX: 1, children: [
6363
- /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\u2501\u2501 Mode Selection \u2501\u2501" }),
6364
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 navigate \xB7 Enter select \xB7 Esc cancel" }),
6404
+ /* @__PURE__ */ jsxs(Text, { color: "cyan", bold: true, children: [
6405
+ "\u2501\u2501 Mode Selection \u2501\u2501 ",
6406
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: modeMeta })
6407
+ ] }),
6408
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
6409
+ "Current: ",
6410
+ activeMode ? activeMode.name : "none",
6411
+ " \xB7 \u2191/\u2193 navigate \xB7 Enter select \xB7 Esc cancel"
6412
+ ] }),
6365
6413
  modes.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No modes available." }) : modes.map((opt, i) => /* @__PURE__ */ jsxs(
6366
6414
  Text,
6367
6415
  {
@@ -6369,9 +6417,17 @@ function ModePicker({
6369
6417
  ...i === selected ? { color: "cyan" } : {},
6370
6418
  children: [
6371
6419
  i === selected ? "\u203A " : " ",
6372
- /* @__PURE__ */ jsx(Text, { bold: true, children: opt.name.padEnd(20) }),
6373
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: opt.description }),
6374
- opt.isActive ? /* @__PURE__ */ jsx(Text, { color: "green", children: " [active]" }) : null
6420
+ /* @__PURE__ */ jsx(Text, { bold: true, children: opt.name.padEnd(18) }),
6421
+ /* @__PURE__ */ jsxs(Text, { color: familyColor(opt.family), children: [
6422
+ "[",
6423
+ familyLabel(opt.family),
6424
+ "]"
6425
+ ] }),
6426
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
6427
+ " ",
6428
+ opt.description
6429
+ ] }),
6430
+ opt.isActive ? /* @__PURE__ */ jsx(Text, { color: "green", children: " \u25CF active" }) : null
6375
6431
  ]
6376
6432
  },
6377
6433
  opt.id
@@ -8466,7 +8522,7 @@ function streamBoxRows(text, maxLines, contentWidth) {
8466
8522
  }
8467
8523
  return rows;
8468
8524
  }
8469
- var ToolStreamBox = React5.memo(function ToolStreamBox2({
8525
+ var ToolStreamBox = React7.memo(function ToolStreamBox2({
8470
8526
  name,
8471
8527
  text,
8472
8528
  startedAt,
@@ -9106,7 +9162,7 @@ function ConfirmPrompt({
9106
9162
  onEnableYolo,
9107
9163
  destructive
9108
9164
  }) {
9109
- React5.useEffect(() => {
9165
+ React7.useEffect(() => {
9110
9166
  writeOut("\x07");
9111
9167
  }, []);
9112
9168
  useInput((input2, _key) => {
@@ -9140,7 +9196,7 @@ function ConfirmPrompt({
9140
9196
  inputSummary ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: inputSummary }) : null,
9141
9197
  showDiff && diff ? /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginY: 1, children: renderDiff(diff, diffPath) }) : null,
9142
9198
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }),
9143
- /* @__PURE__ */ jsx(Box, { flexDirection: "row", children: /* @__PURE__ */ jsx(Text, { children: buttonLabels(suggestedPattern).map((l) => /* @__PURE__ */ jsxs(React5.Fragment, { children: [
9199
+ /* @__PURE__ */ jsx(Box, { flexDirection: "row", children: /* @__PURE__ */ jsx(Text, { children: buttonLabels(suggestedPattern).map((l) => /* @__PURE__ */ jsxs(React7.Fragment, { children: [
9144
9200
  /* @__PURE__ */ jsx(Text, { bold: true, color: BUTTON_COLOR[l.decision], children: l.bracket }),
9145
9201
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: l.rest })
9146
9202
  ] }, l.decision)) }) }),
@@ -9278,6 +9334,72 @@ function DesignPicker({ kits, selected, stack }) {
9278
9334
  ] }) }, kit.id))
9279
9335
  ] });
9280
9336
  }
9337
+ function continueAutoProceedSeconds(grounded, delayMs) {
9338
+ return grounded ? Math.max(1, Math.ceil(delayMs / 1e3)) : null;
9339
+ }
9340
+ function ContinueConfirmPanel({
9341
+ label,
9342
+ instruction,
9343
+ source,
9344
+ grounded,
9345
+ delayMs,
9346
+ onDecision
9347
+ }) {
9348
+ const autoProceedSecs = continueAutoProceedSeconds(grounded, delayMs);
9349
+ const remainingRef = React7.useRef(autoProceedSecs ?? 0);
9350
+ const decideRef = React7.useRef(onDecision);
9351
+ decideRef.current = onDecision;
9352
+ React7.useEffect(() => {
9353
+ if (!grounded) return;
9354
+ const id = setInterval(() => {
9355
+ const r = remainingRef.current - 1;
9356
+ remainingRef.current = r;
9357
+ if (r <= 0) {
9358
+ clearInterval(id);
9359
+ decideRef.current("proceed");
9360
+ }
9361
+ }, 1e3);
9362
+ return () => clearInterval(id);
9363
+ }, [grounded]);
9364
+ useInput((input, key) => {
9365
+ if (key.return) {
9366
+ onDecision("proceed");
9367
+ } else if (key.escape) {
9368
+ onDecision("cancel");
9369
+ } else if (input?.toLowerCase() === "e") {
9370
+ onDecision("edit");
9371
+ }
9372
+ });
9373
+ const accent = grounded ? "cyan" : "yellow";
9374
+ const preview = instruction.length > 320 ? `${instruction.slice(0, 317)}\u2026` : instruction;
9375
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: accent, paddingX: 1, children: [
9376
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
9377
+ /* @__PURE__ */ jsx(Text, { bold: true, color: accent, children: grounded ? "\u25B6 Continue" : "\u26A0 Continue (ambiguous)" }),
9378
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: ' \u2014 you typed a bare "continue"' })
9379
+ ] }),
9380
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
9381
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "resolves to: " }),
9382
+ /* @__PURE__ */ jsx(Text, { color: "white", children: label.replace(/^▶\s*Continue\s*→\s*/, "") })
9383
+ ] }),
9384
+ source === "open" ? /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
9385
+ /* @__PURE__ */ jsx(Text, { color: "yellow", children: 'No pending todo or suggestion \u2014 "continue" has no anchor here.' }),
9386
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "If you proceed, I'll choose the next step myself from the conversation and project state. That's a guess \u2014 it can drift from what you meant." })
9387
+ ] }) : /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginTop: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: preview }) }),
9388
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }),
9389
+ /* @__PURE__ */ jsx(Box, { flexDirection: "row", children: /* @__PURE__ */ jsxs(Text, { children: [
9390
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "green", children: "[Enter]" }),
9391
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: autoProceedSecs != null ? ` proceed (auto ${autoProceedSecs}s) \xB7 ` : " proceed anyway \xB7 " }),
9392
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "[e]" }),
9393
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
9394
+ " ",
9395
+ source === "open" ? "type what to continue" : "edit",
9396
+ " \xB7 "
9397
+ ] }),
9398
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "red", children: "[Esc]" }),
9399
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " cancel" })
9400
+ ] }) })
9401
+ ] });
9402
+ }
9281
9403
  function EnhancePanel({
9282
9404
  original,
9283
9405
  refined,
@@ -9287,12 +9409,12 @@ function EnhancePanel({
9287
9409
  onTick
9288
9410
  }) {
9289
9411
  const totalSecs = Math.max(1, Math.ceil(delayMs / 1e3));
9290
- const remainingRef = React5.useRef(totalSecs);
9291
- const decideRef = React5.useRef(onDecision);
9412
+ const remainingRef = React7.useRef(totalSecs);
9413
+ const decideRef = React7.useRef(onDecision);
9292
9414
  decideRef.current = onDecision;
9293
- const tickRef = React5.useRef(onTick);
9415
+ const tickRef = React7.useRef(onTick);
9294
9416
  tickRef.current = onTick;
9295
- React5.useEffect(() => {
9417
+ React7.useEffect(() => {
9296
9418
  const id = setInterval(() => {
9297
9419
  const r = remainingRef.current - 1;
9298
9420
  remainingRef.current = r;
@@ -9539,6 +9661,44 @@ function FilePicker({ query, matches, selected }) {
9539
9661
  function highlight(path10, _query) {
9540
9662
  return path10;
9541
9663
  }
9664
+ function truncateShellCommandWarningCommand(text, max = 80) {
9665
+ return text.length <= max ? text : `${text.slice(0, max - 1)}\u2026`;
9666
+ }
9667
+ function ShellCommandWarning({
9668
+ command,
9669
+ onDecision
9670
+ }) {
9671
+ React7.useEffect(() => {
9672
+ writeOut("\x07");
9673
+ }, []);
9674
+ useInput((input, _key) => {
9675
+ if (!input || input === "\r" || input === "\n") return;
9676
+ const ch = input.toLowerCase();
9677
+ if (ch === "y") onDecision("yes");
9678
+ if (ch === "n") onDecision("no");
9679
+ if (ch === "d") onDecision("dont-show-again");
9680
+ });
9681
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
9682
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
9683
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "yellow", children: "\u26A0 SHELL COMMAND" }),
9684
+ /* @__PURE__ */ jsx(Text, { children: " " }),
9685
+ /* @__PURE__ */ jsxs(Text, { bold: true, color: "white", children: [
9686
+ "!",
9687
+ truncateShellCommandWarningCommand(command)
9688
+ ] })
9689
+ ] }),
9690
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "This will execute in your shell via /dev. Only run commands you trust." }),
9691
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }),
9692
+ /* @__PURE__ */ jsx(Box, { flexDirection: "row", children: /* @__PURE__ */ jsxs(Text, { children: [
9693
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "green", children: "[y]" }),
9694
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "es " }),
9695
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "red", children: "[n]" }),
9696
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "o " }),
9697
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "[d]" }),
9698
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "ont show again" })
9699
+ ] }) })
9700
+ ] });
9701
+ }
9542
9702
  var METER_WIDTH = 10;
9543
9703
  function isLeaderEntry2(entry) {
9544
9704
  return entry.id === "leader" || entry.name === "LEADER";
@@ -10918,7 +11078,7 @@ function brainRiskColor(risk) {
10918
11078
  return "red";
10919
11079
  }
10920
11080
  }
10921
- var Entry = React5.memo(function Entry2({
11081
+ var Entry = React7.memo(function Entry2({
10922
11082
  entry,
10923
11083
  termWidth,
10924
11084
  setSuggestions,
@@ -12963,14 +13123,11 @@ function PluginPicker({
12963
13123
  ] });
12964
13124
  }
12965
13125
  var CHROME_ROWS5 = 15;
12966
- function formatCount(noun, count) {
12967
- return `${count} ${noun}${count === 1 ? "" : "s"}`;
12968
- }
12969
13126
  function toolActionLabel(enabled) {
12970
- return enabled ? "Enter disables this tool" : "Enter re-enables this tool";
13127
+ return enabled ? "Enter makes this tool passive" : "Enter re-enables this tool";
12971
13128
  }
12972
13129
  function statusBadge2(enabled) {
12973
- return enabled ? /* @__PURE__ */ jsx(Text, { color: "green", children: "\u25CF active" }) : /* @__PURE__ */ jsx(Text, { color: "red", children: "\u25CB disabled" });
13130
+ return enabled ? /* @__PURE__ */ jsx(Text, { color: "green", children: "\u25CF active" }) : /* @__PURE__ */ jsx(Text, { color: "red", children: "\u25CB passive" });
12974
13131
  }
12975
13132
  function rwBadge(mutating) {
12976
13133
  return mutating ? /* @__PURE__ */ jsx(Text, { color: "yellow", children: "mut" }) : /* @__PURE__ */ jsx(Text, { color: "cyan", children: "ro" });
@@ -12979,23 +13136,52 @@ function displayCategory(cat) {
12979
13136
  if (!cat || cat.trim() === "") return "Other";
12980
13137
  return cat.charAt(0).toUpperCase() + cat.slice(1);
12981
13138
  }
12982
- function buildRows(items) {
12983
- const rows = [];
12984
- let lastCat = "";
12985
- for (let i = 0; i < items.length; i++) {
12986
- const item = items[i];
12987
- const cat = displayCategory(item.category);
12988
- if (cat !== lastCat) {
12989
- lastCat = cat;
12990
- rows.push({ type: "header", category: cat });
12991
- }
12992
- rows.push({ type: "item", item, index: i });
13139
+ var DEFAULT_COLUMN_WIDTHS = {
13140
+ name: 28,
13141
+ category: 16,
13142
+ owner: 31,
13143
+ permission: 8
13144
+ };
13145
+ var MIN_COLUMN_WIDTHS = {
13146
+ name: 22,
13147
+ category: 13,
13148
+ owner: 20,
13149
+ permission: 7
13150
+ };
13151
+ function shrinkColumn(widths, column, overflow) {
13152
+ const room = widths[column] - MIN_COLUMN_WIDTHS[column];
13153
+ const shrinkBy = Math.min(room, overflow);
13154
+ widths[column] -= shrinkBy;
13155
+ return overflow - shrinkBy;
13156
+ }
13157
+ function columnWidthsFor(columns) {
13158
+ const widths = { ...DEFAULT_COLUMN_WIDTHS };
13159
+ const rowChromeWidth = 30;
13160
+ const availableVariableWidth = Math.max(
13161
+ Object.values(MIN_COLUMN_WIDTHS).reduce((sum, width) => sum + width, 0),
13162
+ columns - rowChromeWidth
13163
+ );
13164
+ const desiredVariableWidth = Object.values(widths).reduce((sum, width) => sum + width, 0);
13165
+ let overflow = desiredVariableWidth - availableVariableWidth;
13166
+ for (const column of ["owner", "category", "name", "permission"]) {
13167
+ if (overflow <= 0) break;
13168
+ overflow = shrinkColumn(widths, column, overflow);
12993
13169
  }
12994
- return rows;
13170
+ return widths;
13171
+ }
13172
+ function fitCell(value, width) {
13173
+ if (width <= 0) return "";
13174
+ if (value.length > width) {
13175
+ return `${value.slice(0, Math.max(0, width - 1))}\u2026`;
13176
+ }
13177
+ return value.padEnd(width);
13178
+ }
13179
+ function buildRows(items) {
13180
+ return items.map((item, index) => ({ item, index }));
12995
13181
  }
12996
13182
  function windowRows(rows, focus, max) {
12997
13183
  if (rows.length <= max) {
12998
- return { rows, start: 0, end: rows.length, contextHeader: null };
13184
+ return { rows, start: 0, end: rows.length };
12999
13185
  }
13000
13186
  let start = focus - Math.floor(max / 2);
13001
13187
  if (start < 0) start = 0;
@@ -13004,20 +13190,7 @@ function windowRows(rows, focus, max) {
13004
13190
  end = rows.length;
13005
13191
  start = end - max;
13006
13192
  }
13007
- let contextHeader = null;
13008
- if (start > 0) {
13009
- const first = rows[start];
13010
- if (first && first.type === "item") {
13011
- for (let i = start - 1; i >= 0; i--) {
13012
- const r = rows[i];
13013
- if (r && r.type === "header") {
13014
- contextHeader = r.category;
13015
- break;
13016
- }
13017
- }
13018
- }
13019
- }
13020
- return { rows: rows.slice(start, end), start, end, contextHeader };
13193
+ return { rows: rows.slice(start, end), start, end };
13021
13194
  }
13022
13195
  function ToolsPicker({
13023
13196
  items,
@@ -13028,6 +13201,8 @@ function ToolsPicker({
13028
13201
  }) {
13029
13202
  const { stdout } = useStdout();
13030
13203
  const termRows = stdout?.rows ?? 24;
13204
+ const termColumns = stdout?.columns ?? 100;
13205
+ const columnWidths = columnWidthsFor(termColumns);
13031
13206
  const maxVisible = Math.max(6, termRows - CHROME_ROWS5);
13032
13207
  const total = items.length;
13033
13208
  let visibleItems;
@@ -13047,51 +13222,31 @@ function ToolsPicker({
13047
13222
  }
13048
13223
  const rows = buildRows(visibleItems);
13049
13224
  const enabledCount = items.filter((item) => item.enabled).length;
13050
- const disabledCount = total - enabledCount;
13225
+ const passiveCount = total - enabledCount;
13051
13226
  const selectedItem = visibleItems[visibleSelected];
13052
13227
  let displayRows;
13053
13228
  let hiddenAbove = 0;
13054
13229
  let hiddenBelow = 0;
13055
- let contextHeader = null;
13056
13230
  if (filterActive) {
13057
13231
  displayRows = rows;
13058
13232
  } else {
13059
- const selectedRowIdx = rows.findIndex(
13060
- (r) => r.type === "item" && r.index === visibleSelected
13061
- );
13062
- const win = windowRows(
13063
- rows,
13064
- selectedRowIdx < 0 ? 0 : selectedRowIdx,
13065
- maxVisible
13066
- );
13233
+ const win = windowRows(rows, visibleSelected, maxVisible);
13067
13234
  displayRows = win.rows;
13068
13235
  hiddenAbove = win.start;
13069
13236
  hiddenBelow = rows.length - win.end;
13070
- contextHeader = win.contextHeader;
13071
13237
  }
13072
13238
  const hasFilter = filterActive;
13073
13239
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 1, children: [
13074
13240
  /* @__PURE__ */ jsx(Text, { bold: true, color: "green", children: "\u2501\u2501 Tools \u2501\u2501" }),
13075
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: hasFilter ? `Filter: ${filter} (${visibleItems.length} match${visibleItems.length === 1 ? "" : "es"}) \xB7 Backspace edit \xB7 Esc clear` : `\u2191/\u2193 select \xB7 type to filter \xB7 Enter toggles \xB7 Esc close \xB7 ${formatCount("active", enabledCount)} / ${formatCount("disabled", disabledCount)}` }),
13076
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Disabled tools stay listed here so you can select them and re-enable them later." }),
13241
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: hasFilter ? `Filter: ${filter} (${visibleItems.length} match${visibleItems.length === 1 ? "" : "es"}) \xB7 Backspace edit \xB7 Esc clear` : `\u2191/\u2193 select \xB7 type to filter \xB7 Enter toggles \xB7 Esc close \xB7 ${enabledCount} active / ${passiveCount} passive` }),
13242
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Passive tools stay listed for this session so you can select them and re-enable them later." }),
13077
13243
  /* @__PURE__ */ jsx(Box, { marginTop: 1, flexDirection: "column", children: total === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: busy ? "Loading tools\u2026" : "No tools registered." }) : visibleItems.length === 0 ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
13078
13244
  'No tools match "',
13079
13245
  filter,
13080
13246
  '".'
13081
13247
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
13082
13248
  hiddenAbove > 0 && !hasFilter ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \u2191 ${hiddenAbove} more` }) : null,
13083
- contextHeader && !hasFilter ? /* @__PURE__ */ jsxs(Text, { bold: true, color: "yellow", dimColor: true, children: [
13084
- " ",
13085
- contextHeader
13086
- ] }) : null,
13087
- displayRows.map((row) => {
13088
- if (row.type === "header") {
13089
- return /* @__PURE__ */ jsxs(Text, { bold: true, color: "yellow", dimColor: true, children: [
13090
- " ",
13091
- row.category
13092
- ] }, `cat-${row.category}`);
13093
- }
13094
- const { item, index } = row;
13249
+ displayRows.map(({ item, index }) => {
13095
13250
  const focused = index === visibleSelected;
13096
13251
  return /* @__PURE__ */ jsxs(
13097
13252
  Text,
@@ -13103,14 +13258,17 @@ function ToolsPicker({
13103
13258
  focused ? "\u203A " : " ",
13104
13259
  statusBadge2(item.enabled),
13105
13260
  " ",
13106
- /* @__PURE__ */ jsx(Text, { bold: true, children: item.name.padEnd(20) }),
13261
+ /* @__PURE__ */ jsx(Text, { bold: true, children: fitCell(item.name, columnWidths.name) }),
13262
+ " ",
13107
13263
  /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
13108
- `[${item.owner}]`.padEnd(22),
13109
- rwBadge(item.mutating),
13264
+ fitCell(displayCategory(item.category), columnWidths.category),
13110
13265
  " ",
13266
+ fitCell(`[${item.owner}]`, columnWidths.owner),
13267
+ " ",
13268
+ rwBadge(item.mutating),
13111
13269
  " ",
13112
- item.permission.padEnd(6),
13113
13270
  " ",
13271
+ fitCell(item.permission, columnWidths.permission),
13114
13272
  " ",
13115
13273
  /* @__PURE__ */ jsxs(Text, { color: item.descMode === "simple" ? "yellow" : void 0, children: [
13116
13274
  "desc:",
@@ -13961,6 +14119,7 @@ function SessionsPanel({
13961
14119
  var MAX_VISIBLE_ITEMS = 8;
13962
14120
  function SlashMenu({ query, matches, selected }) {
13963
14121
  const placeholder = query ? `/${query}` : "/";
14122
+ const resultMeta = matches.length > 0 ? `${selected + 1}/${matches.length}` : "no matches";
13964
14123
  const rows = [];
13965
14124
  let lastCategory = "";
13966
14125
  for (let i = 0; i < matches.length; i++) {
@@ -13975,12 +14134,18 @@ function SlashMenu({ query, matches, selected }) {
13975
14134
  const visible = windowRows3(rows, selectedRowIdx < 0 ? 0 : selectedRowIdx, MAX_VISIBLE_ITEMS);
13976
14135
  const hiddenAbove = visible.start;
13977
14136
  const hiddenBelow = rows.length - visible.end;
13978
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
13979
- /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
13980
- placeholder,
14137
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
14138
+ /* @__PURE__ */ jsxs(Text, { bold: true, color: "cyan", children: [
14139
+ "\u2501\u2501 Command palette \u2501\u2501 ",
14140
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: placeholder }),
13981
14141
  " ",
13982
- matches.length > 0 ? `(${selected + 1}/${matches.length})` : ""
14142
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
14143
+ "(",
14144
+ resultMeta,
14145
+ ")"
14146
+ ] })
13983
14147
  ] }),
14148
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Type to filter commands by name, alias, or description." }),
13984
14149
  hiddenAbove > 0 && /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
13985
14150
  " \u2191 ",
13986
14151
  hiddenAbove,
@@ -14418,6 +14583,79 @@ function runGit(cwd, args) {
14418
14583
  });
14419
14584
  }
14420
14585
 
14586
+ // src/rehydrate-history.ts
14587
+ function contentBlocksText(content) {
14588
+ if (typeof content === "string") return content;
14589
+ if (!Array.isArray(content)) return "";
14590
+ return content.filter(
14591
+ (block) => !!block && typeof block === "object" && block.type === "text" && typeof block.text === "string"
14592
+ ).map((block) => block.text).join("");
14593
+ }
14594
+ function rehydrateHistory(messages, startId, toolCalls) {
14595
+ const entries = [];
14596
+ const toolCallsById = /* @__PURE__ */ new Map();
14597
+ if (toolCalls) {
14598
+ for (const tc of toolCalls) {
14599
+ if (!toolCallsById.has(tc.id)) toolCallsById.set(tc.id, tc);
14600
+ }
14601
+ }
14602
+ const consumed = /* @__PURE__ */ new Set();
14603
+ const fallback = [];
14604
+ let nextId = startId;
14605
+ const textOf = (msg) => contentBlocksText(msg.content);
14606
+ const toolEntryFor = (tc) => ({
14607
+ id: nextId++,
14608
+ kind: "tool",
14609
+ name: tc.name,
14610
+ durationMs: tc.durationMs,
14611
+ ok: tc.ok,
14612
+ outputBytes: tc.outputBytes,
14613
+ outputTokens: tc.outputTokens,
14614
+ outputLines: tc.outputLines
14615
+ });
14616
+ const pushAssistantText = (parts) => {
14617
+ const text = parts.join("").trim();
14618
+ if (text) entries.push({ id: nextId++, kind: "assistant", text });
14619
+ parts.length = 0;
14620
+ };
14621
+ for (const msg of messages) {
14622
+ if (msg.role === "system") continue;
14623
+ if (msg.role === "user") {
14624
+ const text = textOf(msg).trim();
14625
+ if (text) entries.push({ id: nextId++, kind: "user", text });
14626
+ continue;
14627
+ }
14628
+ if (msg.role === "assistant") {
14629
+ if (!Array.isArray(msg.content)) {
14630
+ const text = textOf(msg).trim();
14631
+ if (text) entries.push({ id: nextId++, kind: "assistant", text });
14632
+ continue;
14633
+ }
14634
+ const textParts = [];
14635
+ for (const block of msg.content) {
14636
+ if (block.type === "text") {
14637
+ textParts.push(block.text);
14638
+ continue;
14639
+ }
14640
+ if (block.type !== "tool_use") continue;
14641
+ pushAssistantText(textParts);
14642
+ const tc = toolCallsById.get(block.id);
14643
+ if (!tc) continue;
14644
+ entries.push(toolEntryFor(tc));
14645
+ consumed.add(block.id);
14646
+ }
14647
+ pushAssistantText(textParts);
14648
+ }
14649
+ }
14650
+ if (toolCalls) {
14651
+ for (const tc of toolCalls) {
14652
+ if (!consumed.has(tc.id)) fallback.push(toolEntryFor(tc));
14653
+ }
14654
+ }
14655
+ entries.push(...fallback);
14656
+ return entries;
14657
+ }
14658
+
14421
14659
  // src/app-initial-state.ts
14422
14660
  function buildRestoredEntries(messages, restoredToolCalls) {
14423
14661
  if (!messages || messages.length === 0) return [];
@@ -14573,9 +14811,11 @@ function createInitialState(options) {
14573
14811
  },
14574
14812
  fKeyPicker: { open: false, selected: 0 },
14575
14813
  confirmQueue: [],
14814
+ shellCommandWarning: null,
14576
14815
  enhance: null,
14577
14816
  enhanceEnabled,
14578
14817
  enhanceBusy: false,
14818
+ continueConfirm: null,
14579
14819
  escConfirm: null,
14580
14820
  sendModePicker: null,
14581
14821
  contextChipVersion: 0,
@@ -15670,6 +15910,7 @@ function usePasteHandling({
15670
15910
  };
15671
15911
  }
15672
15912
  var ENTER_DOUBLE_TAP_MS = 50;
15913
+ var formatTokens = (tokens) => tokens.toLocaleString("en-US");
15673
15914
  function debouncedEnter(host) {
15674
15915
  const now = Date.now();
15675
15916
  if (now - host.lastEnterAtRef.current < ENTER_DOUBLE_TAP_MS) return true;
@@ -15828,12 +16069,16 @@ function usePickerKeys(host) {
15828
16069
  dispatch({ type: "modelPickerHint", text: err });
15829
16070
  return;
15830
16071
  }
16072
+ const previousMaxContext = host.activeMaxContext;
16073
+ const nextMaxContext = host.getAgentCtxMaxContext();
15831
16074
  host.setLiveProvider?.(providerId);
15832
16075
  host.setLiveModel?.(modelId);
15833
- host.setActiveMaxContext?.(host.agentCtxMaxContext);
16076
+ host.setActiveMaxContext?.(nextMaxContext);
16077
+ const contextWarning = previousMaxContext && nextMaxContext > 0 && nextMaxContext < previousMaxContext ? `
16078
+ \u26A0 smaller context window: ${formatTokens(previousMaxContext)} \u2192 ${formatTokens(nextMaxContext)} tokens${host.currentContextTokens > 0 ? `; current request \u2248 ${formatTokens(host.currentContextTokens)} tokens (${Math.round(host.currentContextTokens / nextMaxContext * 100)}% of new window)` : ""}` : "";
15834
16079
  dispatch({
15835
16080
  type: "addEntry",
15836
- entry: { kind: "info", text: `Switched to ${providerId} / ${modelId}.` }
16081
+ entry: { kind: "info", text: `Switched to ${providerId} / ${modelId}.${contextWarning}` }
15837
16082
  });
15838
16083
  dispatch({ type: "modelPickerClose" });
15839
16084
  };
@@ -17744,78 +17989,8 @@ function selectedSlashCommandLine(picker) {
17744
17989
  const picked = picker.matches[picker.selected];
17745
17990
  return picked ? `/${picked.name}` : null;
17746
17991
  }
17747
- function contentBlocksText(content) {
17748
- if (typeof content === "string") return content;
17749
- if (!Array.isArray(content)) return "";
17750
- return content.filter(
17751
- (block) => !!block && typeof block === "object" && block.type === "text" && typeof block.text === "string"
17752
- ).map((block) => block.text).join("");
17753
- }
17754
- function rehydrateHistory(messages, startId, toolCalls) {
17755
- const entries = [];
17756
- const toolCallsById = /* @__PURE__ */ new Map();
17757
- if (toolCalls) {
17758
- for (const tc of toolCalls) {
17759
- if (!toolCallsById.has(tc.id)) toolCallsById.set(tc.id, tc);
17760
- }
17761
- }
17762
- const consumed = /* @__PURE__ */ new Set();
17763
- const fallback = [];
17764
- let nextId = startId;
17765
- const textOf = (msg) => contentBlocksText(msg.content);
17766
- const toolEntryFor = (tc) => ({
17767
- id: nextId++,
17768
- kind: "tool",
17769
- name: tc.name,
17770
- durationMs: tc.durationMs,
17771
- ok: tc.ok,
17772
- outputBytes: tc.outputBytes,
17773
- outputTokens: tc.outputTokens,
17774
- outputLines: tc.outputLines
17775
- });
17776
- const pushAssistantText = (parts) => {
17777
- const text = parts.join("").trim();
17778
- if (text) entries.push({ id: nextId++, kind: "assistant", text });
17779
- parts.length = 0;
17780
- };
17781
- for (const msg of messages) {
17782
- if (msg.role === "system") continue;
17783
- if (msg.role === "user") {
17784
- const text = textOf(msg).trim();
17785
- if (text) entries.push({ id: nextId++, kind: "user", text });
17786
- continue;
17787
- }
17788
- if (msg.role === "assistant") {
17789
- if (!Array.isArray(msg.content)) {
17790
- const text = textOf(msg).trim();
17791
- if (text) entries.push({ id: nextId++, kind: "assistant", text });
17792
- continue;
17793
- }
17794
- const textParts = [];
17795
- for (const block of msg.content) {
17796
- if (block.type === "text") {
17797
- textParts.push(block.text);
17798
- continue;
17799
- }
17800
- if (block.type !== "tool_use") continue;
17801
- pushAssistantText(textParts);
17802
- const tc = toolCallsById.get(block.id);
17803
- if (!tc) continue;
17804
- entries.push(toolEntryFor(tc));
17805
- consumed.add(block.id);
17806
- }
17807
- pushAssistantText(textParts);
17808
- }
17809
- }
17810
- if (toolCalls) {
17811
- for (const tc of toolCalls) {
17812
- if (!consumed.has(tc.id)) fallback.push(toolEntryFor(tc));
17813
- }
17814
- }
17815
- entries.push(...fallback);
17816
- return entries;
17817
- }
17818
17992
  var PASTE_THRESHOLD_CHARS = 200;
17993
+ var CONTINUE_CONFIRM_DELAY_MS = 4e3;
17819
17994
  var SB_PADX2 = 2;
17820
17995
  function sddLifecycleEntry(op, r) {
17821
17996
  if (typeof r === "number") {
@@ -17991,6 +18166,10 @@ function App({
17991
18166
  });
17992
18167
  const hiddenItemsRef = useRef(hiddenItems);
17993
18168
  hiddenItemsRef.current = hiddenItems;
18169
+ const [liveToolCount, setLiveToolCount] = useState(toolCount);
18170
+ useEffect(() => {
18171
+ setLiveToolCount(toolCount);
18172
+ }, [toolCount]);
17994
18173
  const prevBranchRef = useRef(null);
17995
18174
  const [indexState, setIndexState] = useState(() => getIndexState());
17996
18175
  useEffect(() => {
@@ -18090,19 +18269,19 @@ function App({
18090
18269
  });
18091
18270
  const { openPromptPicker } = usePromptPicker({ projectRoot, dispatch });
18092
18271
  const { openModePicker } = useModePicker({ dispatch, getModes });
18093
- const openBrainPanel = React5.useCallback(() => {
18272
+ const openBrainPanel = React7.useCallback(() => {
18094
18273
  if (!getBrainData) return;
18095
18274
  const data = getBrainData();
18096
18275
  dispatch({ type: "brainOpen", riskLevel: data.riskLevel, log: data.log });
18097
18276
  }, [getBrainData]);
18098
- const changeBrainRisk = React5.useCallback(
18277
+ const changeBrainRisk = React7.useCallback(
18099
18278
  (delta) => {
18100
18279
  dispatch({ type: "brainRiskChange", delta });
18101
18280
  },
18102
18281
  []
18103
18282
  );
18104
- const prevRiskRef = React5.useRef(state.brainPanel.riskLevel);
18105
- React5.useEffect(() => {
18283
+ const prevRiskRef = React7.useRef(state.brainPanel.riskLevel);
18284
+ React7.useEffect(() => {
18106
18285
  if (!state.brainPanel.open) return;
18107
18286
  const current = state.brainPanel.riskLevel;
18108
18287
  if (current !== prevRiskRef.current && onBrainRiskLevel) {
@@ -18112,12 +18291,12 @@ function App({
18112
18291
  else dispatch({ type: "brainHint", text: `Risk ceiling \u2192 ${current.toUpperCase()}` });
18113
18292
  }
18114
18293
  }, [state.brainPanel.riskLevel, state.brainPanel.open, onBrainRiskLevel]);
18115
- const openShadowPanel = React5.useCallback(() => {
18294
+ const openShadowPanel = React7.useCallback(() => {
18116
18295
  if (!getShadowData) return;
18117
18296
  const data = getShadowData();
18118
18297
  dispatch({ type: "shadowOpen", shadow: data });
18119
18298
  }, [getShadowData]);
18120
- const handleShadowStart = React5.useCallback(async () => {
18299
+ const handleShadowStart = React7.useCallback(async () => {
18121
18300
  if (!onShadowStart) return;
18122
18301
  dispatch({ type: "shadowHint", text: "Starting Shadow Agent\u2026" });
18123
18302
  try {
@@ -18131,7 +18310,7 @@ function App({
18131
18310
  dispatch({ type: "shadowHint", text: `Failed to start: ${toErrorMessage(e)}` });
18132
18311
  }
18133
18312
  }, [onShadowStart, getShadowData]);
18134
- const handleShadowStop = React5.useCallback(async () => {
18313
+ const handleShadowStop = React7.useCallback(async () => {
18135
18314
  if (!onShadowStop) return;
18136
18315
  dispatch({ type: "shadowHint", text: "Stopping Shadow Agent\u2026" });
18137
18316
  try {
@@ -18145,7 +18324,7 @@ function App({
18145
18324
  dispatch({ type: "shadowHint", text: `Failed to stop: ${toErrorMessage(e)}` });
18146
18325
  }
18147
18326
  }, [onShadowStop, getShadowData]);
18148
- const openHelpPanel = React5.useCallback(() => {
18327
+ const openHelpPanel = React7.useCallback(() => {
18149
18328
  const entries = Array.from(slashRegistry.listWithOwner()).map(({ cmd, owner }) => ({
18150
18329
  name: owner === "core" ? cmd.name : `${owner}:${cmd.name}`,
18151
18330
  description: cmd.description,
@@ -18177,7 +18356,7 @@ function App({
18177
18356
  const inputGateRef = useRef(false);
18178
18357
  const lastEnterAtRef = useRef(0);
18179
18358
  const tokenPreviewsRef = useRef(/* @__PURE__ */ new Map());
18180
- const projectName = React5.useMemo(() => {
18359
+ const projectName = React7.useMemo(() => {
18181
18360
  const base = path9.basename(projectRoot);
18182
18361
  return base && base !== path9.sep ? base : void 0;
18183
18362
  }, [projectRoot]);
@@ -18282,7 +18461,7 @@ function App({
18282
18461
  }
18283
18462
  });
18284
18463
  const handleKeyRef = useRef(null);
18285
- const handleRewindTo = React5.useCallback(
18464
+ const handleRewindTo = React7.useCallback(
18286
18465
  async (checkpointIndex) => {
18287
18466
  const sessionId = agent.ctx.session.id;
18288
18467
  if (!sessionId) return;
@@ -18304,7 +18483,7 @@ function App({
18304
18483
  dispatch({ type: "clearInput" });
18305
18484
  };
18306
18485
  const startedAtRef = useRef(Date.now());
18307
- const [nowTick, setNowTick] = React5.useState(Date.now());
18486
+ const [nowTick, setNowTick] = React7.useState(Date.now());
18308
18487
  useEffect(() => {
18309
18488
  const t = setInterval(() => setNowTick(Date.now()), 1e4);
18310
18489
  return () => clearInterval(t);
@@ -18390,7 +18569,7 @@ function App({
18390
18569
  agent.ctx.provider,
18391
18570
  agent.ctx.todos
18392
18571
  ]);
18393
- const [gitInfo, setGitInfo] = React5.useState(null);
18572
+ const [gitInfo, setGitInfo] = React7.useState(null);
18394
18573
  useEffect(() => {
18395
18574
  let cancelled = false;
18396
18575
  const refresh = () => {
@@ -18718,8 +18897,8 @@ function App({
18718
18897
  } catch {
18719
18898
  }
18720
18899
  }, []);
18721
- React5.useLayoutEffect(() => {
18722
- const anyOpenNow = state.picker.open || state.slashPicker.open || state.modelPicker.open || state.autonomyPicker.open || state.designPicker.open || state.resumePicker.open || state.settingsPicker.open || state.enhanceBusy || state.enhance != null || state.coordinator.monitorOpen || state.escConfirm != null || state.sendModePicker != null || state.confirmQueue.length > 0;
18900
+ React7.useLayoutEffect(() => {
18901
+ const anyOpenNow = state.picker.open || state.slashPicker.open || state.modelPicker.open || state.autonomyPicker.open || state.designPicker.open || state.resumePicker.open || state.settingsPicker.open || state.enhanceBusy || state.enhance != null || state.continueConfirm != null || state.coordinator.monitorOpen || state.escConfirm != null || state.sendModePicker != null || state.confirmQueue.length > 0;
18723
18902
  const overlayClosed = prevAnyOverlayOpen.current && !anyOpenNow;
18724
18903
  const newEntryCommitted = state.entries.length > prevEntriesCount.current;
18725
18904
  const curToolStreamLen = state.toolStream?.text.length ?? 0;
@@ -18739,6 +18918,7 @@ function App({
18739
18918
  state.settingsPicker.open,
18740
18919
  state.enhanceBusy,
18741
18920
  state.enhance,
18921
+ state.continueConfirm,
18742
18922
  state.coordinator.monitorOpen,
18743
18923
  state.escConfirm,
18744
18924
  state.sendModePicker,
@@ -18871,8 +19051,9 @@ function App({
18871
19051
  process.stdout.off("resize", handleResize);
18872
19052
  };
18873
19053
  }, [eraseLiveRegion]);
18874
- React5.useLayoutEffect(() => {
18875
- if (state.enhanceBusy || state.enhance != null) eraseLiveRegion();
19054
+ React7.useLayoutEffect(() => {
19055
+ if (state.enhanceBusy || state.enhance != null || state.continueConfirm != null)
19056
+ eraseLiveRegion();
18876
19057
  });
18877
19058
  useEffect(() => {
18878
19059
  const trimmed = state.buffer.trimStart();
@@ -19022,20 +19203,20 @@ function App({
19022
19203
  slashRegistry.unregister("agents");
19023
19204
  };
19024
19205
  }, [slashRegistry]);
19025
- const openModelPicker = React5.useCallback(async () => {
19206
+ const openModelPicker = React7.useCallback(async () => {
19026
19207
  if (!getPickableProviders) return;
19027
19208
  const providers = await getPickableProviders();
19028
19209
  dispatch({ type: "modelPickerOpen", providers });
19029
19210
  }, [getPickableProviders]);
19030
- const openProjectPicker = React5.useCallback(async () => {
19211
+ const openProjectPicker = React7.useCallback(async () => {
19031
19212
  if (!getProjectPickerItems) return;
19032
19213
  const items = await getProjectPickerItems();
19033
19214
  dispatch({ type: "projectPickerOpen", items });
19034
19215
  }, [getProjectPickerItems]);
19035
- const openFKeyPicker = React5.useCallback(() => {
19216
+ const openFKeyPicker = React7.useCallback(() => {
19036
19217
  dispatch({ type: "fKeyPickerOpen" });
19037
19218
  }, [dispatch]);
19038
- const loadLiveSessions = React5.useCallback(async () => {
19219
+ const loadLiveSessions = React7.useCallback(async () => {
19039
19220
  if (!getLiveSessions) {
19040
19221
  dispatch({ type: "sessionsPanelSet", sessions: [] });
19041
19222
  return;
@@ -19082,7 +19263,7 @@ function App({
19082
19263
  }, 5e3);
19083
19264
  return () => clearInterval(t);
19084
19265
  }, [state.sessionsPanelOpen, getLiveSessions, loadLiveSessions]);
19085
- const openSettings = React5.useCallback(() => {
19266
+ const openSettings = React7.useCallback(() => {
19086
19267
  if (!getSettings) return;
19087
19268
  const s2 = getSettings();
19088
19269
  dispatch({
@@ -19127,7 +19308,7 @@ function App({
19127
19308
  animationStyle: s2.animationStyle ?? "rainbow"
19128
19309
  });
19129
19310
  }, [getSettings]);
19130
- const refreshPluginPicker = React5.useCallback(() => {
19311
+ const refreshPluginPicker = React7.useCallback(() => {
19131
19312
  if (!getPluginItems) return;
19132
19313
  dispatch({ type: "pluginPickerSetItems", items: getPluginItems() });
19133
19314
  }, [getPluginItems]);
@@ -19135,7 +19316,7 @@ function App({
19135
19316
  if (!state.pluginPicker.open) return;
19136
19317
  refreshPluginPicker();
19137
19318
  }, [state.pluginPicker.open, refreshPluginPicker]);
19138
- const toggleSelectedPlugin = React5.useCallback(async () => {
19319
+ const toggleSelectedPlugin = React7.useCallback(async () => {
19139
19320
  if (!onPluginToggle) return;
19140
19321
  const selected = stateRef.current.pluginPicker.items[stateRef.current.pluginPicker.selected];
19141
19322
  if (!selected) return;
@@ -19159,7 +19340,7 @@ function App({
19159
19340
  });
19160
19341
  }
19161
19342
  }, [onPluginToggle]);
19162
- const refreshMcpPicker = React5.useCallback(() => {
19343
+ const refreshMcpPicker = React7.useCallback(() => {
19163
19344
  if (!getMcpServers) return;
19164
19345
  dispatch({ type: "mcpPickerSetItems", items: getMcpServers() });
19165
19346
  }, [getMcpServers]);
@@ -19167,7 +19348,7 @@ function App({
19167
19348
  if (!state.mcpPicker.open) return;
19168
19349
  refreshMcpPicker();
19169
19350
  }, [state.mcpPicker.open, refreshMcpPicker]);
19170
- const toggleSelectedMcpServer = React5.useCallback(async () => {
19351
+ const toggleSelectedMcpServer = React7.useCallback(async () => {
19171
19352
  if (!onMcpToggle) return;
19172
19353
  const selected = stateRef.current.mcpPicker.items[stateRef.current.mcpPicker.selected];
19173
19354
  if (!selected) return;
@@ -19184,7 +19365,7 @@ function App({
19184
19365
  });
19185
19366
  }
19186
19367
  }, [onMcpToggle]);
19187
- const restartSelectedMcpServer = React5.useCallback(async () => {
19368
+ const restartSelectedMcpServer = React7.useCallback(async () => {
19188
19369
  if (!onMcpRestart) return;
19189
19370
  const selected = stateRef.current.mcpPicker.items[stateRef.current.mcpPicker.selected];
19190
19371
  if (!selected) return;
@@ -19201,15 +19382,17 @@ function App({
19201
19382
  });
19202
19383
  }
19203
19384
  }, [onMcpRestart]);
19204
- const refreshToolsPicker = React5.useCallback(() => {
19385
+ const refreshToolsPicker = React7.useCallback(() => {
19205
19386
  if (!getToolsItems) return;
19206
- dispatch({ type: "toolsPickerSetItems", items: getToolsItems() });
19387
+ const items = getToolsItems();
19388
+ dispatch({ type: "toolsPickerSetItems", items });
19389
+ setLiveToolCount(items.filter((item) => item.enabled).length);
19207
19390
  }, [getToolsItems]);
19208
19391
  useEffect(() => {
19209
19392
  if (!state.toolsPicker.open) return;
19210
19393
  refreshToolsPicker();
19211
19394
  }, [state.toolsPicker.open, refreshToolsPicker]);
19212
- const toggleSelectedTool = React5.useCallback(async () => {
19395
+ const toggleSelectedTool = React7.useCallback(async () => {
19213
19396
  if (!onToolToggle) return;
19214
19397
  const selected = stateRef.current.toolsPicker.items[stateRef.current.toolsPicker.selected];
19215
19398
  if (!selected) return;
@@ -19217,6 +19400,7 @@ function App({
19217
19400
  try {
19218
19401
  const result = await onToolToggle(selected.name);
19219
19402
  dispatch({ type: "toolsPickerSetItems", items: result.items });
19403
+ setLiveToolCount(result.items.filter((item) => item.enabled).length);
19220
19404
  dispatch({ type: "toolsPickerHint", text: result.error ?? result.message });
19221
19405
  } catch (err) {
19222
19406
  dispatch({ type: "toolsPickerBusy", busy: false });
@@ -19241,7 +19425,7 @@ function App({
19241
19425
  nextStepsAutoSubmitSuggestionRef.current = null;
19242
19426
  return;
19243
19427
  }
19244
- if (state.enhance != null || state.enhanceBusy) {
19428
+ if (state.enhance != null || state.enhanceBusy || state.continueConfirm != null) {
19245
19429
  return;
19246
19430
  }
19247
19431
  if (nextStepsAutoSubmitTimerRef.current != null) {
@@ -19322,6 +19506,7 @@ function App({
19322
19506
  autonomyLive,
19323
19507
  state.enhance,
19324
19508
  state.enhanceBusy,
19509
+ state.continueConfirm,
19325
19510
  nextStepsRecheck,
19326
19511
  getSettings,
19327
19512
  getSuggestions,
@@ -19879,11 +20064,14 @@ Available chords:
19879
20064
  });
19880
20065
  });
19881
20066
  const offFallback = events.on("provider.fallback", (e) => {
20067
+ const fallbackEvent = e;
20068
+ const contextWarning = fallbackEvent.contextWindowWarning ? `
20069
+ \u26A0 smaller context window: ${fallbackEvent.contextWindowWarning.fromMaxContext.toLocaleString("en-US")} \u2192 ${fallbackEvent.contextWindowWarning.toMaxContext.toLocaleString("en-US")} tokens${fallbackEvent.contextWindowWarning.currentTokens ? `; current request \u2248 ${fallbackEvent.contextWindowWarning.currentTokens.toLocaleString("en-US")} tokens (${Math.round(fallbackEvent.contextWindowWarning.currentTokens / fallbackEvent.contextWindowWarning.toMaxContext * 100)}% of new window)` : ""}` : "";
19882
20070
  dispatch({
19883
20071
  type: "addEntry",
19884
20072
  entry: {
19885
20073
  kind: "warn",
19886
- text: `\u21BB rate-limited (${e.status}) \u2014 switched to ${e.to.providerId}/${e.to.model}`
20074
+ text: `\u21BB rate-limited (${e.status}) \u2014 switched to ${e.to.providerId}/${e.to.model}${contextWarning}`
19887
20075
  }
19888
20076
  });
19889
20077
  });
@@ -20153,7 +20341,9 @@ Available chords:
20153
20341
  setLiveProvider,
20154
20342
  setLiveModel,
20155
20343
  setActiveMaxContext,
20156
- agentCtxMaxContext: agent.ctx.provider.capabilities.maxContext,
20344
+ getAgentCtxMaxContext: () => agent.ctx.provider.capabilities.maxContext,
20345
+ activeMaxContext,
20346
+ currentContextTokens,
20157
20347
  switchAutonomy,
20158
20348
  submit: (text) => submitRef.current(text),
20159
20349
  onPromptPickerEnter: () => {
@@ -20487,11 +20677,13 @@ Available chords:
20487
20677
  const handleKey = async (input, key) => {
20488
20678
  if (state.status === "aborting" && !state.steeringPending && state.interrupts === 0) return;
20489
20679
  if (state.confirmQueue.length > 0) return;
20680
+ if (state.shellCommandWarning) return;
20490
20681
  if (state.enhanceBusy) {
20491
20682
  if (key.escape) enhanceAbortRef.current?.abort();
20492
20683
  return;
20493
20684
  }
20494
20685
  if (state.enhance) return;
20686
+ if (state.continueConfirm) return;
20495
20687
  if (state.escConfirm) return;
20496
20688
  if (state.sendModePicker) return;
20497
20689
  if (state.helpOpen) {
@@ -21531,6 +21723,33 @@ Available chords:
21531
21723
  await pasteClipboardImage();
21532
21724
  return;
21533
21725
  }
21726
+ if (trimmed.startsWith("!")) {
21727
+ const command = trimmed.slice(1).trim();
21728
+ if (!command) {
21729
+ dispatch({
21730
+ type: "addEntry",
21731
+ entry: { kind: "info", text: "Usage: !<shell command>" }
21732
+ });
21733
+ return;
21734
+ }
21735
+ const settings = getSettings?.();
21736
+ if (settings?.shellBangWarningDontShowAgain !== true) {
21737
+ const decision = await new Promise((resolve) => {
21738
+ dispatch({ type: "shellCommandWarningOpen", info: { command, resolve } });
21739
+ });
21740
+ dispatch({ type: "shellCommandWarningClose" });
21741
+ if (decision === "no") return;
21742
+ if (decision === "dont-show-again") {
21743
+ if (settings && saveSettings) {
21744
+ const err = await saveSettings({ ...settings, shellBangWarningDontShowAgain: true });
21745
+ if (err) {
21746
+ dispatch({ type: "addEntry", entry: { kind: "warn", text: err } });
21747
+ }
21748
+ }
21749
+ }
21750
+ }
21751
+ return submit(`/dev ${command}`);
21752
+ }
21534
21753
  if (trimmed === "/prompt" || trimmed === "/prompts-browse") {
21535
21754
  pushSubmittedHistory();
21536
21755
  clearDraft();
@@ -21613,6 +21832,9 @@ ${content}
21613
21832
  const currentMode = getModeLabel();
21614
21833
  if (currentMode !== liveModeLabel) setLiveModeLabel(currentMode);
21615
21834
  }
21835
+ if (getToolsItems) {
21836
+ setLiveToolCount(getToolsItems().filter((item) => item.enabled).length);
21837
+ }
21616
21838
  if (res?.exit) {
21617
21839
  exit();
21618
21840
  onExit(0);
@@ -21650,6 +21872,10 @@ ${content}
21650
21872
  const builder = builderRef.current;
21651
21873
  if (!builder) return;
21652
21874
  const steering = state.steeringPending;
21875
+ const continueResolved = !steering && state.status === "idle" && detectContinueIntent(trimmed) ? resolveContinuation({
21876
+ todos: agent.ctx.todos,
21877
+ suggestions: getSuggestions?.() ?? []
21878
+ }) : null;
21653
21879
  let effectiveText = trimmed;
21654
21880
  const chips = [];
21655
21881
  let cleanText = trimmed;
@@ -21665,7 +21891,7 @@ ${content}
21665
21891
  chips.length = 0;
21666
21892
  }
21667
21893
  }
21668
- if (enhanceEnabledRef.current && state.status === "idle" && !steering && shouldEnhance(cleanText)) {
21894
+ if (enhanceEnabledRef.current && state.status === "idle" && !steering && !continueResolved && shouldEnhance(cleanText)) {
21669
21895
  dispatch({ type: "enhanceBusy", on: true });
21670
21896
  const ac = new AbortController();
21671
21897
  enhanceAbortRef.current = ac;
@@ -21728,6 +21954,31 @@ ${content}
21728
21954
  }
21729
21955
  }
21730
21956
  }
21957
+ if (continueResolved) {
21958
+ const decision = await new Promise((resolve) => {
21959
+ dispatch({
21960
+ type: "continueConfirmOpen",
21961
+ info: {
21962
+ label: continueResolved.label,
21963
+ instruction: continueResolved.text,
21964
+ source: continueResolved.source,
21965
+ grounded: continueResolved.source !== "open",
21966
+ resolve
21967
+ }
21968
+ });
21969
+ });
21970
+ dispatch({ type: "continueConfirmClose" });
21971
+ if (decision === "cancel") {
21972
+ clearDraft();
21973
+ return;
21974
+ }
21975
+ if (decision === "edit") {
21976
+ const seed = continueResolved.source === "open" ? "" : continueResolved.text;
21977
+ setDraft(seed, seed.length);
21978
+ return;
21979
+ }
21980
+ effectiveText = continueResolved.text;
21981
+ }
21731
21982
  const sddContext = await getSDDContext?.();
21732
21983
  if (sddContext && trimmed) {
21733
21984
  builder.appendText(`[SDD SESSION ACTIVE]
@@ -21742,7 +21993,7 @@ User message:
21742
21993
  builder.appendText(toAppend);
21743
21994
  }
21744
21995
  if (steering) dispatch({ type: "steerConsume" });
21745
- const displayText = steering ? `\u21AF ${effectiveText}` : effectiveText;
21996
+ const displayText = continueResolved ? continueResolved.label : steering ? `\u21AF ${effectiveText}` : effectiveText;
21746
21997
  const pasteParts = [];
21747
21998
  for (const m of trimmed.matchAll(new RegExp(INLINE_TOKEN_SRC, "g"))) {
21748
21999
  const token = m[0];
@@ -21873,7 +22124,7 @@ ${content}
21873
22124
  stdout?.columns ?? 80
21874
22125
  );
21875
22126
  const inputHeight = Math.max(1, inputCellRows.length);
21876
- const hideInput = enhanceActive || state.helpOpen || state.processListOpen || state.authPanel.open;
22127
+ const hideInput = enhanceActive || state.continueConfirm != null || state.helpOpen || state.processListOpen || state.authPanel.open;
21877
22128
  const lowerFunctionPanelOpen = state.monitorOpen || state.agentsMonitorOpen || (state.autoPhase?.monitorOpen ?? false) || state.worktreeMonitorOpen || state.planPanelOpen || state.kanbanPanelOpen || state.todosMonitorOpen || state.queuePanelOpen || state.processListOpen || state.goalPanelOpen;
21878
22129
  return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexGrow: 1, flexShrink: 0, children: [
21879
22130
  mouseMode ? /* @__PURE__ */ jsx(
@@ -22166,6 +22417,17 @@ ${content}
22166
22417
  }
22167
22418
  }
22168
22419
  ) }) : null,
22420
+ state.shellCommandWarning ? (() => {
22421
+ const info = state.shellCommandWarning;
22422
+ let resolved = false;
22423
+ const onDecision = (decision) => {
22424
+ if (resolved) return;
22425
+ resolved = true;
22426
+ info.resolve(decision);
22427
+ dispatch({ type: "shellCommandWarningClose" });
22428
+ };
22429
+ return /* @__PURE__ */ jsx(ShellCommandWarning, { command: info.command, onDecision });
22430
+ })() : null,
22169
22431
  state.confirmQueue.length > 0 && (() => {
22170
22432
  const head = expectDefined$1(state.confirmQueue[0]);
22171
22433
  let resolved = false;
@@ -22287,6 +22549,26 @@ ${content}
22287
22549
  }
22288
22550
  );
22289
22551
  })() : null,
22552
+ state.continueConfirm ? (() => {
22553
+ const info = state.continueConfirm;
22554
+ let resolved = false;
22555
+ const onDecision = (decision) => {
22556
+ if (resolved) return;
22557
+ resolved = true;
22558
+ info.resolve(decision);
22559
+ };
22560
+ return /* @__PURE__ */ jsx(
22561
+ ContinueConfirmPanel,
22562
+ {
22563
+ label: info.label,
22564
+ instruction: info.instruction,
22565
+ source: info.source,
22566
+ grounded: info.grounded,
22567
+ delayMs: CONTINUE_CONFIRM_DELAY_MS,
22568
+ onDecision
22569
+ }
22570
+ );
22571
+ })() : null,
22290
22572
  /* @__PURE__ */ jsx(Box, { ref: statusBarWrapRef, flexDirection: "column", flexShrink: 0, children: /* @__PURE__ */ jsx(
22291
22573
  StatusBar,
22292
22574
  {
@@ -22337,7 +22619,7 @@ ${content}
22337
22619
  sessionCount,
22338
22620
  mailbox: mailboxStatus,
22339
22621
  tokenSavingMode: liveSettings?.featureTokenSaving ?? tokenSavingMode,
22340
- toolCount,
22622
+ toolCount: liveToolCount,
22341
22623
  sideEffectCount: agent.ctx.sideEffects?.length ?? 0
22342
22624
  }
22343
22625
  ) }),
@@ -22823,7 +23105,7 @@ async function runTui(opts) {
22823
23105
  };
22824
23106
  try {
22825
23107
  instance = render(
22826
- React5.createElement(App, {
23108
+ React7.createElement(App, {
22827
23109
  agent: opts.agent,
22828
23110
  slashRegistry: opts.slashRegistry,
22829
23111
  attachments: opts.attachments,