@prestyj/cli 4.2.77 → 4.3.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/dist/cli.js +55 -71
  2. package/dist/cli.js.map +1 -1
  3. package/dist/config.d.ts +10 -0
  4. package/dist/config.d.ts.map +1 -1
  5. package/dist/config.js +33 -0
  6. package/dist/config.js.map +1 -1
  7. package/dist/core/agent-session.js +3 -3
  8. package/dist/core/agent-session.js.map +1 -1
  9. package/dist/core/auth-storage.d.ts.map +1 -1
  10. package/dist/core/auth-storage.js +4 -1
  11. package/dist/core/auth-storage.js.map +1 -1
  12. package/dist/core/compaction/compactor.d.ts +14 -1
  13. package/dist/core/compaction/compactor.d.ts.map +1 -1
  14. package/dist/core/compaction/compactor.js +35 -7
  15. package/dist/core/compaction/compactor.js.map +1 -1
  16. package/dist/core/compaction/token-estimator.d.ts.map +1 -1
  17. package/dist/core/compaction/token-estimator.js +4 -0
  18. package/dist/core/compaction/token-estimator.js.map +1 -1
  19. package/dist/core/index.d.ts +1 -1
  20. package/dist/core/index.d.ts.map +1 -1
  21. package/dist/core/index.js +1 -1
  22. package/dist/core/index.js.map +1 -1
  23. package/dist/core/mcp/client.d.ts.map +1 -1
  24. package/dist/core/mcp/client.js +15 -2
  25. package/dist/core/mcp/client.js.map +1 -1
  26. package/dist/core/model-registry.d.ts.map +1 -1
  27. package/dist/core/model-registry.js +59 -3
  28. package/dist/core/model-registry.js.map +1 -1
  29. package/dist/core/oauth/types.d.ts +1 -0
  30. package/dist/core/oauth/types.d.ts.map +1 -1
  31. package/dist/core/settings-manager.d.ts +4 -0
  32. package/dist/core/settings-manager.d.ts.map +1 -1
  33. package/dist/core/settings-manager.js +11 -1
  34. package/dist/core/settings-manager.js.map +1 -1
  35. package/dist/interactive.js +1 -1
  36. package/dist/interactive.js.map +1 -1
  37. package/dist/system-prompt.d.ts.map +1 -1
  38. package/dist/system-prompt.js +8 -58
  39. package/dist/system-prompt.js.map +1 -1
  40. package/dist/tools/bash.d.ts.map +1 -1
  41. package/dist/tools/bash.js +8 -1
  42. package/dist/tools/bash.js.map +1 -1
  43. package/dist/tools/index.d.ts +1 -0
  44. package/dist/tools/index.d.ts.map +1 -1
  45. package/dist/tools/index.js +6 -0
  46. package/dist/tools/index.js.map +1 -1
  47. package/dist/tools/overflow.js +1 -1
  48. package/dist/tools/overflow.js.map +1 -1
  49. package/dist/tools/read.d.ts.map +1 -1
  50. package/dist/tools/read.js +14 -1
  51. package/dist/tools/read.js.map +1 -1
  52. package/dist/tools/web-search.d.ts +9 -0
  53. package/dist/tools/web-search.d.ts.map +1 -0
  54. package/dist/tools/web-search.js +241 -0
  55. package/dist/tools/web-search.js.map +1 -0
  56. package/dist/tools/write.js +2 -2
  57. package/dist/tools/write.js.map +1 -1
  58. package/dist/tools/write.test.js +7 -9
  59. package/dist/tools/write.test.js.map +1 -1
  60. package/dist/ui/App.d.ts +4 -0
  61. package/dist/ui/App.d.ts.map +1 -1
  62. package/dist/ui/App.js +223 -114
  63. package/dist/ui/App.js.map +1 -1
  64. package/dist/ui/activity-phrases.d.ts.map +1 -1
  65. package/dist/ui/activity-phrases.js +74 -129
  66. package/dist/ui/activity-phrases.js.map +1 -1
  67. package/dist/ui/components/ActivityIndicator.d.ts +3 -1
  68. package/dist/ui/components/ActivityIndicator.d.ts.map +1 -1
  69. package/dist/ui/components/ActivityIndicator.js +5 -1
  70. package/dist/ui/components/ActivityIndicator.js.map +1 -1
  71. package/dist/ui/components/AssistantMessage.d.ts +1 -0
  72. package/dist/ui/components/AssistantMessage.d.ts.map +1 -1
  73. package/dist/ui/components/AssistantMessage.js +2 -2
  74. package/dist/ui/components/AssistantMessage.js.map +1 -1
  75. package/dist/ui/components/DiffFrame.d.ts +10 -0
  76. package/dist/ui/components/DiffFrame.d.ts.map +1 -0
  77. package/dist/ui/components/DiffFrame.js +13 -0
  78. package/dist/ui/components/DiffFrame.js.map +1 -0
  79. package/dist/ui/components/Footer.d.ts +2 -3
  80. package/dist/ui/components/Footer.d.ts.map +1 -1
  81. package/dist/ui/components/Footer.js +5 -4
  82. package/dist/ui/components/Footer.js.map +1 -1
  83. package/dist/ui/components/InputArea.d.ts.map +1 -1
  84. package/dist/ui/components/InputArea.js +297 -8
  85. package/dist/ui/components/InputArea.js.map +1 -1
  86. package/dist/ui/components/Markdown.d.ts +1 -1
  87. package/dist/ui/components/Markdown.d.ts.map +1 -1
  88. package/dist/ui/components/Markdown.js +25 -213
  89. package/dist/ui/components/Markdown.js.map +1 -1
  90. package/dist/ui/components/ModelSelector.d.ts.map +1 -1
  91. package/dist/ui/components/ModelSelector.js +9 -2
  92. package/dist/ui/components/ModelSelector.js.map +1 -1
  93. package/dist/ui/components/Ratchet.d.ts.map +1 -1
  94. package/dist/ui/components/Ratchet.js +10 -3
  95. package/dist/ui/components/Ratchet.js.map +1 -1
  96. package/dist/ui/components/StreamingArea.d.ts.map +1 -1
  97. package/dist/ui/components/StreamingArea.js +2 -2
  98. package/dist/ui/components/StreamingArea.js.map +1 -1
  99. package/dist/ui/components/SubAgentPanel.d.ts.map +1 -1
  100. package/dist/ui/components/SubAgentPanel.js +5 -1
  101. package/dist/ui/components/SubAgentPanel.js.map +1 -1
  102. package/dist/ui/components/ThemeSelector.d.ts +9 -0
  103. package/dist/ui/components/ThemeSelector.d.ts.map +1 -0
  104. package/dist/ui/components/ThemeSelector.js +25 -0
  105. package/dist/ui/components/ThemeSelector.js.map +1 -0
  106. package/dist/ui/components/ToolExecution.d.ts +2 -0
  107. package/dist/ui/components/ToolExecution.d.ts.map +1 -1
  108. package/dist/ui/components/ToolExecution.js +95 -30
  109. package/dist/ui/components/ToolExecution.js.map +1 -1
  110. package/dist/ui/components/ToolGroupExecution.d.ts +3 -2
  111. package/dist/ui/components/ToolGroupExecution.d.ts.map +1 -1
  112. package/dist/ui/components/ToolGroupExecution.js +68 -51
  113. package/dist/ui/components/ToolGroupExecution.js.map +1 -1
  114. package/dist/ui/hooks/useAgentLoop.d.ts +10 -3
  115. package/dist/ui/hooks/useAgentLoop.d.ts.map +1 -1
  116. package/dist/ui/hooks/useAgentLoop.js +112 -16
  117. package/dist/ui/hooks/useAgentLoop.js.map +1 -1
  118. package/dist/ui/hooks/useDoublePress.d.ts +8 -0
  119. package/dist/ui/hooks/useDoublePress.d.ts.map +1 -0
  120. package/dist/ui/hooks/useDoublePress.js +44 -0
  121. package/dist/ui/hooks/useDoublePress.js.map +1 -0
  122. package/dist/ui/hooks/useTerminalProgress.d.ts +1 -1
  123. package/dist/ui/hooks/useTerminalProgress.d.ts.map +1 -1
  124. package/dist/ui/hooks/useTerminalProgress.js +18 -15
  125. package/dist/ui/hooks/useTerminalProgress.js.map +1 -1
  126. package/dist/ui/hooks/useTerminalTitle.d.ts +3 -6
  127. package/dist/ui/hooks/useTerminalTitle.d.ts.map +1 -1
  128. package/dist/ui/hooks/useTerminalTitle.js +10 -19
  129. package/dist/ui/hooks/useTerminalTitle.js.map +1 -1
  130. package/dist/ui/live-item-flush.d.ts +4 -0
  131. package/dist/ui/live-item-flush.d.ts.map +1 -1
  132. package/dist/ui/live-item-flush.js +36 -0
  133. package/dist/ui/live-item-flush.js.map +1 -1
  134. package/dist/ui/login.d.ts.map +1 -1
  135. package/dist/ui/login.js +2 -0
  136. package/dist/ui/login.js.map +1 -1
  137. package/dist/ui/render.d.ts +3 -1
  138. package/dist/ui/render.d.ts.map +1 -1
  139. package/dist/ui/render.js +9 -3
  140. package/dist/ui/render.js.map +1 -1
  141. package/dist/ui/theme/dark-ansi.json +48 -0
  142. package/dist/ui/theme/dark-daltonized.json +48 -0
  143. package/dist/ui/theme/dark.json +22 -1
  144. package/dist/ui/theme/detect-theme.d.ts +12 -8
  145. package/dist/ui/theme/detect-theme.d.ts.map +1 -1
  146. package/dist/ui/theme/detect-theme.js +58 -39
  147. package/dist/ui/theme/detect-theme.js.map +1 -1
  148. package/dist/ui/theme/light-ansi.json +48 -0
  149. package/dist/ui/theme/light-daltonized.json +48 -0
  150. package/dist/ui/theme/light.json +44 -23
  151. package/dist/ui/theme/theme.d.ts +27 -1
  152. package/dist/ui/theme/theme.d.ts.map +1 -1
  153. package/dist/ui/theme/theme.js +24 -1
  154. package/dist/ui/theme/theme.js.map +1 -1
  155. package/dist/ui/utils/highlight.d.ts +1 -1
  156. package/dist/ui/utils/highlight.d.ts.map +1 -1
  157. package/dist/ui/utils/highlight.js +24 -4
  158. package/dist/ui/utils/highlight.js.map +1 -1
  159. package/dist/ui/utils/hyperlink.d.ts +11 -0
  160. package/dist/ui/utils/hyperlink.d.ts.map +1 -0
  161. package/dist/ui/utils/hyperlink.js +27 -0
  162. package/dist/ui/utils/hyperlink.js.map +1 -0
  163. package/dist/ui/utils/markdown-cache.d.ts +5 -6
  164. package/dist/ui/utils/markdown-cache.d.ts.map +1 -1
  165. package/dist/ui/utils/markdown-cache.js +5 -5
  166. package/dist/ui/utils/markdown-cache.js.map +1 -1
  167. package/dist/ui/utils/signal.d.ts +21 -0
  168. package/dist/ui/utils/signal.d.ts.map +1 -0
  169. package/dist/ui/utils/signal.js +30 -0
  170. package/dist/ui/utils/signal.js.map +1 -0
  171. package/dist/ui/utils/supports-hyperlinks.d.ts +6 -0
  172. package/dist/ui/utils/supports-hyperlinks.d.ts.map +1 -0
  173. package/dist/ui/utils/supports-hyperlinks.js +52 -0
  174. package/dist/ui/utils/supports-hyperlinks.js.map +1 -0
  175. package/dist/ui/utils/token-to-ansi.d.ts +9 -0
  176. package/dist/ui/utils/token-to-ansi.d.ts.map +1 -0
  177. package/dist/ui/utils/token-to-ansi.js +305 -0
  178. package/dist/ui/utils/token-to-ansi.js.map +1 -0
  179. package/dist/utils/session-title.d.ts +19 -0
  180. package/dist/utils/session-title.d.ts.map +1 -0
  181. package/dist/utils/session-title.js +82 -0
  182. package/dist/utils/session-title.js.map +1 -0
  183. package/package.json +3 -3
package/dist/ui/App.js CHANGED
@@ -2,6 +2,8 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
2
2
  import React, { useState, useRef, useCallback, useEffect, useMemo } from "react";
3
3
  import { Box, Text, Static, useStdout } from "ink";
4
4
  import { useTerminalSize } from "./hooks/useTerminalSize.js";
5
+ import { useDoublePress } from "./hooks/useDoublePress.js";
6
+ import { useTaskBarStore, useTaskBarPolling, focusTaskBar, exitTaskBar, expandTaskBar, collapseTaskBar, navigateTaskBar, killTask, } from "./stores/taskbar-store.js";
5
7
  import crypto, { createHash } from "node:crypto";
6
8
  import { readFileSync, writeFileSync } from "node:fs";
7
9
  import { homedir } from "node:os";
@@ -25,8 +27,9 @@ import { PlanOverlay } from "./components/PlanOverlay.js";
25
27
  import { ModelSelector } from "./components/ModelSelector.js";
26
28
  import { TaskOverlay } from "./components/TaskOverlay.js";
27
29
  import { SkillsOverlay } from "./components/SkillsOverlay.js";
30
+ import { ThemeSelector } from "./components/ThemeSelector.js";
28
31
  import { BackgroundTasksBar } from "./components/BackgroundTasksBar.js";
29
- import { useTheme } from "./theme/theme.js";
32
+ import { useTheme, useSetTheme } from "./theme/theme.js";
30
33
  import { useAnimationTick, useAnimationActive, deriveFrame, } from "./components/AnimationContext.js";
31
34
  import { useTerminalTitle } from "./hooks/useTerminalTitle.js";
32
35
  import { useTerminalProgress } from "./hooks/useTerminalProgress.js";
@@ -34,6 +37,7 @@ import { getGitBranch } from "../utils/git.js";
34
37
  import { getModel, getContextWindow } from "../core/model-registry.js";
35
38
  import { SessionManager } from "../core/session-manager.js";
36
39
  import { log } from "../core/logger.js";
40
+ import { generateSessionTitle } from "../utils/session-title.js";
37
41
  import { SettingsManager } from "../core/settings-manager.js";
38
42
  import { shouldCompact, compact } from "../core/compaction/compactor.js";
39
43
  import { estimateConversationTokens } from "../core/compaction/token-estimator.js";
@@ -42,7 +46,7 @@ import { loadCustomCommands } from "../core/custom-commands.js";
42
46
  import { buildSystemPrompt } from "../system-prompt.js";
43
47
  import { extractPlanSteps, findCompletedMarkers, markStepsCompleted, stripDoneMarkers, } from "../utils/plan-steps.js";
44
48
  import { getMCPServers } from "../core/mcp/index.js";
45
- import { trimFlushedItems, flushOnTurnText, flushOnTurnEnd } from "./live-item-flush.js";
49
+ import { trimFlushedItems, flushOnTurnText, flushOnTurnEnd, flushOverflow, } from "./live-item-flush.js";
46
50
  import { Buddy } from "./buddy/Buddy.js";
47
51
  // ── Provider Error Hints ──────────────────────────────────
48
52
  /** Detect provider-side errors and return a user-facing hint. */
@@ -264,22 +268,23 @@ function markTaskInProgress(cwd, taskId) {
264
268
  // ── App Component ──────────────────────────────────────────
265
269
  export function App(props) {
266
270
  const theme = useTheme();
271
+ const switchTheme = useSetTheme();
267
272
  const { stdout } = useStdout();
268
273
  const { columns, resizeKey } = useTerminalSize();
269
274
  // Hoisted before terminal title hook so it can reference them
270
275
  const [lastUserMessage, setLastUserMessage] = useState("");
276
+ const [exitPending, setExitPending] = useState(false);
271
277
  const [planMode, setPlanMode] = useState(false);
278
+ const planModeLocalRef = useRef(false);
279
+ planModeLocalRef.current = planMode;
272
280
  // Terminal title — updated later after agentLoop is created
273
281
  // (hoisted here so the hook is always called in the same order)
274
- const [titlePhase, setTitlePhase] = useState("idle");
275
282
  const [titleRunning, setTitleRunning] = useState(false);
276
- const [titleToolNames, setTitleToolNames] = useState([]);
283
+ const [sessionTitle, setSessionTitle] = useState(undefined);
284
+ const sessionTitleGeneratedRef = useRef(false);
277
285
  useTerminalTitle({
278
- phase: titlePhase,
279
286
  isRunning: titleRunning,
280
- userMessage: lastUserMessage,
281
- activeToolNames: titleToolNames,
282
- planMode,
287
+ sessionTitle,
283
288
  });
284
289
  // Items scrolled into Static (history). For restored sessions, skip the
285
290
  // banner and add restored items via useEffect so Ink's <Static> treats them
@@ -328,10 +333,19 @@ export function App(props) {
328
333
  // Two-phase flush: items waiting to be moved to Static history after the
329
334
  // live area has been cleared and Ink has committed the smaller output.
330
335
  const pendingFlushRef = useRef([]);
336
+ const [flushGeneration, setFlushGeneration] = useState(0);
337
+ /** Queue items for two-phase flush and signal the drain effect. */
338
+ const queueFlush = useCallback((items) => {
339
+ if (items.length === 0)
340
+ return;
341
+ pendingFlushRef.current = [...pendingFlushRef.current, ...items];
342
+ setFlushGeneration((g) => g + 1);
343
+ }, []);
331
344
  // Derive credentials for the current provider
332
345
  const currentCreds = props.credentialsByProvider?.[currentProvider];
333
346
  const activeApiKey = currentCreds?.accessToken ?? props.apiKey;
334
347
  const activeAccountId = currentCreds?.accountId ?? props.accountId;
348
+ const activeBaseUrl = currentCreds?.baseUrl ?? props.baseUrl;
335
349
  // Load git branch
336
350
  useEffect(() => {
337
351
  getGitBranch(props.cwd).then(setGitBranch);
@@ -392,7 +406,11 @@ export function App(props) {
392
406
  stdout?.write("\x1b[2J\x1b[3J\x1b[H");
393
407
  setPlanAutoExpand(true);
394
408
  setOverlay("plan");
395
- planOverlayPendingRef.current = false;
409
+ // Don't clear planOverlayPendingRef here — keep it true until
410
+ // the user actually approves/rejects the plan. Clearing it on a
411
+ // timer causes a race where agent_done fires after the 300ms
412
+ // timeout but before the user interacts, triggering a premature
413
+ // completion sound.
396
414
  }, 300);
397
415
  return ("Plan submitted. Exiting plan mode.\n" +
398
416
  "The plan pane is opening for user review.\n" +
@@ -523,63 +541,23 @@ export function App(props) {
523
541
  }
524
542
  return messages;
525
543
  }, [currentModel, compactConversation]);
526
- // ── Background task bar state ───────────────────────────
527
- const [bgTasks, setBgTasks] = useState([]);
528
- const [taskBarFocused, setTaskBarFocused] = useState(false);
529
- const [taskBarExpanded, setTaskBarExpanded] = useState(false);
530
- const [selectedTaskIndex, setSelectedTaskIndex] = useState(0);
531
- // Poll ProcessManager every 2s for running tasks
532
- useEffect(() => {
533
- if (!props.processManager)
534
- return;
535
- const pm = props.processManager;
536
- const poll = () => {
537
- const running = pm.list().filter((p) => p.exitCode === null);
538
- setBgTasks(running);
539
- };
540
- poll();
541
- const interval = setInterval(poll, 2000);
542
- return () => clearInterval(interval);
543
- }, [props.processManager]);
544
- // Auto-exit task panel when all tasks gone
545
- useEffect(() => {
546
- if (bgTasks.length === 0) {
547
- setTaskBarFocused(false);
548
- setTaskBarExpanded(false);
549
- }
550
- // Clamp selected index
551
- const maxIdx = Math.min(bgTasks.length, 5) - 1;
552
- if (selectedTaskIndex > maxIdx && maxIdx >= 0) {
553
- setSelectedTaskIndex(maxIdx);
554
- }
555
- }, [bgTasks.length, selectedTaskIndex]);
556
- const handleFocusTaskBar = useCallback(() => {
557
- if (bgTasks.length > 0) {
558
- setTaskBarFocused(true);
559
- }
560
- }, [bgTasks.length]);
561
- const handleTaskBarExit = useCallback(() => {
562
- setTaskBarFocused(false);
563
- setTaskBarExpanded(false);
564
- }, []);
565
- const handleTaskBarExpand = useCallback(() => {
566
- setTaskBarExpanded(true);
567
- setSelectedTaskIndex(0);
568
- }, []);
569
- const handleTaskBarCollapse = useCallback(() => {
570
- setTaskBarExpanded(false);
571
- }, []);
544
+ // ── Background task bar state (external store) ──────────
545
+ const { bgTasks, focused: taskBarFocused, expanded: taskBarExpanded, selectedIndex: selectedTaskIndex, } = useTaskBarStore();
546
+ useTaskBarPolling(props.processManager);
547
+ const handleFocusTaskBar = useCallback(() => focusTaskBar(), []);
548
+ const handleTaskBarExit = useCallback(() => exitTaskBar(), []);
549
+ const handleTaskBarExpand = useCallback(() => expandTaskBar(), []);
550
+ const handleTaskBarCollapse = useCallback(() => collapseTaskBar(), []);
572
551
  const handleTaskKill = useCallback((id) => {
573
- props.processManager?.stop(id);
552
+ if (props.processManager)
553
+ killTask(props.processManager, id);
574
554
  }, [props.processManager]);
575
- const handleTaskNavigate = useCallback((index) => {
576
- setSelectedTaskIndex(index);
577
- }, []);
555
+ const handleTaskNavigate = useCallback((index) => navigateTaskBar(index), []);
578
556
  // Resolve fresh OAuth credentials before each agent loop run.
579
557
  // Falls back to the static props when authStorage is not available.
580
- const resolveCredentials = useCallback(async () => {
558
+ const resolveCredentials = useCallback(async (opts) => {
581
559
  if (props.authStorage) {
582
- const creds = await props.authStorage.resolveCredentials(currentProvider);
560
+ const creds = await props.authStorage.resolveCredentials(currentProvider, opts);
583
561
  return { apiKey: creds.accessToken, accountId: creds.accountId };
584
562
  }
585
563
  return { apiKey: activeApiKey, accountId: activeAccountId };
@@ -592,7 +570,7 @@ export function App(props) {
592
570
  maxTokens: props.maxTokens,
593
571
  thinking: thinkingEnabled ? (props.thinking ?? "medium") : undefined,
594
572
  apiKey: activeApiKey,
595
- baseUrl: props.baseUrl,
573
+ baseUrl: activeBaseUrl,
596
574
  accountId: activeAccountId,
597
575
  resolveCredentials,
598
576
  transformContext,
@@ -613,7 +591,57 @@ export function App(props) {
613
591
  }
614
592
  })();
615
593
  }
616
- }, [persistNewMessages, planMode, props.cwd, props.skills]),
594
+ // Generate session title after the first turn (background, best-effort)
595
+ if (!sessionTitleGeneratedRef.current) {
596
+ sessionTitleGeneratedRef.current = true;
597
+ const msgs = messagesRef.current;
598
+ // Find the first user message and first assistant text
599
+ const userMsg = msgs.find((m) => m.role === "user");
600
+ const assistantMsg = msgs.find((m) => m.role === "assistant");
601
+ const userText = typeof userMsg?.content === "string"
602
+ ? userMsg.content
603
+ : Array.isArray(userMsg?.content)
604
+ ? userMsg.content
605
+ .filter((c) => c.type === "text")
606
+ .map((c) => c.text)
607
+ .join(" ")
608
+ : "";
609
+ const assistantText = typeof assistantMsg?.content === "string"
610
+ ? assistantMsg.content
611
+ : Array.isArray(assistantMsg?.content)
612
+ ? assistantMsg.content
613
+ .filter((c) => c.type === "text")
614
+ .map((c) => c.text)
615
+ .join(" ")
616
+ : "";
617
+ if (userText) {
618
+ generateSessionTitle({
619
+ provider: currentProvider,
620
+ userMessage: userText,
621
+ assistantPreview: assistantText.slice(0, 200),
622
+ apiKey: activeApiKey,
623
+ baseUrl: activeBaseUrl,
624
+ accountId: activeAccountId,
625
+ resolveCredentials,
626
+ }).then((title) => {
627
+ setSessionTitle(title);
628
+ log("INFO", "title", `Session title generated: ${title}`);
629
+ }, () => {
630
+ // Best-effort — silently ignore failures
631
+ });
632
+ }
633
+ }
634
+ }, [
635
+ persistNewMessages,
636
+ planMode,
637
+ props.cwd,
638
+ props.skills,
639
+ currentProvider,
640
+ activeApiKey,
641
+ activeAccountId,
642
+ activeBaseUrl,
643
+ resolveCredentials,
644
+ ]),
617
645
  onTurnText: useCallback((text, thinking, thinkingMs) => {
618
646
  // Track [DONE:n] markers for plan step progress
619
647
  if (planStepsRef.current.length > 0) {
@@ -629,13 +657,28 @@ export function App(props) {
629
657
  // Flush all completed items from the previous turn to Static history.
630
658
  // This keeps liveItems bounded per-turn, preventing Ink's live area from
631
659
  // growing unbounded, which makes Ink's live-area re-renders expensive.
660
+ //
661
+ // Items are queued in pendingFlushRef (not sent to setHistory directly)
662
+ // so the Static write happens in a SEPARATE render cycle from the
663
+ // live-area change — avoiding both Ink cursor-math clipping and the
664
+ // brief duplicate that occurred when setHistory was nested inside the
665
+ // setLiveItems updater.
632
666
  setLiveItems((prev) => {
633
667
  const flushed = flushOnTurnText(prev);
634
668
  if (flushed.length > 0) {
635
- setHistory((h) => compactHistory([...h, ...trimFlushedItems(flushed)]));
669
+ queueFlush(flushed);
636
670
  }
637
671
  const displayText = planStepsRef.current.length > 0 ? stripDoneMarkers(text) : text;
638
- return [{ kind: "assistant", text: displayText, thinking, thinkingMs, id: getId() }];
672
+ return [
673
+ {
674
+ kind: "assistant",
675
+ text: displayText,
676
+ thinking,
677
+ thinkingMs,
678
+ planMode: planModeLocalRef.current,
679
+ id: getId(),
680
+ },
681
+ ];
639
682
  });
640
683
  }, []),
641
684
  onToolStart: useCallback((toolCallId, name, args) => {
@@ -646,7 +689,7 @@ export function App(props) {
646
689
  setLiveItems((prev) => {
647
690
  const { flushed, remaining } = partitionCompleted(prev);
648
691
  if (flushed.length > 0) {
649
- setHistory((h) => compactHistory([...h, ...trimFlushedItems(flushed)]));
692
+ queueFlush(flushed);
650
693
  }
651
694
  return remaining;
652
695
  });
@@ -707,6 +750,24 @@ export function App(props) {
707
750
  }
708
751
  }, []),
709
752
  onToolUpdate: useCallback((toolCallId, update) => {
753
+ const u = update;
754
+ // Bash progress streaming — append output to tool_start item
755
+ if (u.type === "bash_progress") {
756
+ setLiveItems((prev) => {
757
+ const idx = prev.findIndex((item) => item.kind === "tool_start" && item.toolCallId === toolCallId);
758
+ if (idx === -1)
759
+ return prev;
760
+ const item = prev[idx];
761
+ const next = [...prev];
762
+ next[idx] = {
763
+ ...item,
764
+ progressOutput: (item.progressOutput ?? "") + String(u.output ?? ""),
765
+ };
766
+ return next;
767
+ });
768
+ return;
769
+ }
770
+ // Subagent updates
710
771
  setLiveItems((prev) => {
711
772
  const groupIdx = prev.findIndex((item) => item.kind === "subagent_group");
712
773
  if (groupIdx === -1)
@@ -759,7 +820,7 @@ export function App(props) {
759
820
  // Flush completed items to Static to keep the live area small
760
821
  const { flushed, remaining } = partitionCompleted(next);
761
822
  if (flushed.length > 0) {
762
- setHistory((h) => compactHistory([...h, ...trimFlushedItems(flushed)]));
823
+ queueFlush(flushed);
763
824
  }
764
825
  return remaining;
765
826
  });
@@ -818,7 +879,14 @@ export function App(props) {
818
879
  // Flush completed items to Static to keep the live area small
819
880
  const { flushed, remaining } = partitionCompleted(updated);
820
881
  if (flushed.length > 0) {
821
- setHistory((h) => compactHistory([...h, ...trimFlushedItems(flushed)]));
882
+ queueFlush(flushed);
883
+ return remaining;
884
+ }
885
+ // Overflow flush: if live area is still large, flush aggressively
886
+ const overflow = flushOverflow(updated);
887
+ if (overflow.flushed.length > 0) {
888
+ queueFlush(overflow.flushed);
889
+ return overflow.remaining;
822
890
  }
823
891
  return remaining;
824
892
  });
@@ -831,7 +899,7 @@ export function App(props) {
831
899
  setLiveItems((prev) => {
832
900
  const { flushed, remaining } = partitionCompleted(prev);
833
901
  if (flushed.length > 0) {
834
- setHistory((h) => compactHistory([...h, ...trimFlushedItems(flushed)]));
902
+ queueFlush(flushed);
835
903
  }
836
904
  return [
837
905
  ...remaining,
@@ -882,7 +950,7 @@ export function App(props) {
882
950
  // Flush completed items to Static
883
951
  const { flushed, remaining } = partitionCompleted(updated);
884
952
  if (flushed.length > 0) {
885
- setHistory((h) => compactHistory([...h, ...trimFlushedItems(flushed)]));
953
+ queueFlush(flushed);
886
954
  }
887
955
  return remaining;
888
956
  });
@@ -895,15 +963,18 @@ export function App(props) {
895
963
  ...(usage.cacheRead != null && { cacheRead: String(usage.cacheRead) }),
896
964
  ...(usage.cacheWrite != null && { cacheWrite: String(usage.cacheWrite) }),
897
965
  });
898
- // Track actual token count for compaction decisions
966
+ // Track actual token count for compaction decisions.
967
+ // Anthropic has separate input/output limits — only count input.
968
+ // All other providers share the context window — count both.
969
+ const inputContext = usage.inputTokens + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0);
899
970
  lastActualTokensRef.current =
900
- usage.inputTokens + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0);
971
+ currentProvider === "anthropic" ? inputContext : inputContext + usage.outputTokens;
901
972
  // For tool-only turns (no text), flush completed items to Static so
902
973
  // liveItems doesn't grow unbounded across consecutive tool-only turns.
903
974
  setLiveItems((prev) => {
904
975
  const { flushed, remaining } = flushOnTurnEnd(prev, stopReason);
905
976
  if (flushed.length > 0) {
906
- setHistory((h) => compactHistory([...h, ...trimFlushedItems(flushed)]));
977
+ queueFlush(flushed);
907
978
  }
908
979
  return remaining;
909
980
  });
@@ -926,9 +997,8 @@ export function App(props) {
926
997
  // separate render cycle so the Static write never coincides with
927
998
  // a live-area height change in the same frame.
928
999
  setLiveItems((prev) => {
929
- if (prev.length > 0) {
930
- pendingFlushRef.current = prev;
931
- }
1000
+ if (prev.length > 0)
1001
+ queueFlush(prev);
932
1002
  return [];
933
1003
  });
934
1004
  // Run-all: auto-start next pending task after a short delay
@@ -1007,9 +1077,8 @@ export function App(props) {
1007
1077
  ? undefined
1008
1078
  : content.filter((c) => c.type === "image").length || undefined;
1009
1079
  setLiveItems((prev) => {
1010
- if (prev.length > 0) {
1011
- setHistory((h) => compactHistory([...h, ...trimFlushedItems(prev)]));
1012
- }
1080
+ if (prev.length > 0)
1081
+ queueFlush(prev);
1013
1082
  return [];
1014
1083
  });
1015
1084
  const userItem = {
@@ -1036,14 +1105,11 @@ export function App(props) {
1036
1105
  pendingFlushRef.current = [];
1037
1106
  setHistory((h) => compactHistory([...h, ...trimFlushedItems(items)]));
1038
1107
  }
1039
- });
1108
+ }, [flushGeneration]);
1040
1109
  // Sync terminal title with agent loop state
1041
- const activeToolNamesKey = agentLoop.activeToolCalls.map((tc) => tc.name).join(",");
1042
1110
  useEffect(() => {
1043
- setTitlePhase(agentLoop.activityPhase);
1044
1111
  setTitleRunning(agentLoop.isRunning);
1045
- setTitleToolNames(agentLoop.activeToolCalls.map((tc) => tc.name));
1046
- }, [agentLoop.activityPhase, agentLoop.isRunning, activeToolNamesKey]);
1112
+ }, [agentLoop.isRunning]);
1047
1113
  // Terminal progress bar (OSC 9;4) — pulsing bar in supported terminals
1048
1114
  useTerminalProgress(agentLoop.isRunning, agentLoop.activeToolCalls.length > 0);
1049
1115
  // Animated thinking border — derived from global animation tick
@@ -1096,9 +1162,16 @@ export function App(props) {
1096
1162
  messagesRef.current = [{ role: "system", content: newPrompt }];
1097
1163
  })();
1098
1164
  agentLoop.reset();
1165
+ setSessionTitle(undefined);
1166
+ sessionTitleGeneratedRef.current = false;
1099
1167
  setLiveItems([{ kind: "info", text: "Session cleared.", id: getId() }]);
1100
1168
  return;
1101
1169
  }
1170
+ // Handle /theme — open theme selector overlay
1171
+ if (trimmed === "/theme" || trimmed === "/t") {
1172
+ setOverlay("theme");
1173
+ return;
1174
+ }
1102
1175
  // Handle /plan — toggle plan mode
1103
1176
  if (trimmed === "/plan" || trimmed === "/plan on") {
1104
1177
  setPlanMode(true);
@@ -1175,7 +1248,7 @@ export function App(props) {
1175
1248
  // Move live items into history before starting
1176
1249
  setLiveItems((prev) => {
1177
1250
  if (prev.length > 0) {
1178
- setHistory((h) => compactHistory([...h, ...trimFlushedItems(prev)]));
1251
+ pendingFlushRef.current = [...pendingFlushRef.current, ...prev];
1179
1252
  }
1180
1253
  return [];
1181
1254
  });
@@ -1281,7 +1354,7 @@ export function App(props) {
1281
1354
  // Move any remaining live items into history (Static) before starting new turn
1282
1355
  setLiveItems((prev) => {
1283
1356
  if (prev.length > 0) {
1284
- setHistory((h) => compactHistory([...h, ...trimFlushedItems(prev)]));
1357
+ pendingFlushRef.current = [...pendingFlushRef.current, ...prev];
1285
1358
  }
1286
1359
  return [];
1287
1360
  });
@@ -1323,15 +1396,16 @@ export function App(props) {
1323
1396
  ]);
1324
1397
  }
1325
1398
  }, [agentLoop, props.onSlashCommand, compactConversation]);
1399
+ const handleDoubleExit = useDoublePress(setExitPending, () => process.exit(0));
1326
1400
  const handleAbort = useCallback(() => {
1327
1401
  if (agentLoop.isRunning) {
1328
1402
  agentLoop.clearQueue();
1329
1403
  agentLoop.abort();
1330
1404
  }
1331
1405
  else {
1332
- process.exit(0);
1406
+ handleDoubleExit();
1333
1407
  }
1334
- }, [agentLoop]);
1408
+ }, [agentLoop, handleDoubleExit]);
1335
1409
  const handleToggleThinking = useCallback(() => {
1336
1410
  setThinkingEnabled((prev) => {
1337
1411
  const next = !prev;
@@ -1408,12 +1482,28 @@ export function App(props) {
1408
1482
  });
1409
1483
  }
1410
1484
  }, [props.settingsFile, props.mcpManager, props.credentialsByProvider, props.authStorage]);
1485
+ const handleThemeSelect = useCallback((name) => {
1486
+ setOverlay(null);
1487
+ if (switchTheme) {
1488
+ switchTheme(name);
1489
+ }
1490
+ // Persist to settings
1491
+ if (props.settingsFile) {
1492
+ const sm = new SettingsManager(props.settingsFile);
1493
+ sm.load().then(() => sm.set("theme", name));
1494
+ }
1495
+ setLiveItems((prev) => [
1496
+ ...prev,
1497
+ { kind: "info", text: `Theme switched to: ${name}`, id: getId() },
1498
+ ]);
1499
+ }, [switchTheme, props.settingsFile]);
1411
1500
  // All available slash commands for the command palette
1412
1501
  const allCommands = useMemo(() => [
1413
1502
  { name: "model", aliases: ["m"], description: "Switch model" },
1414
1503
  { name: "compact", aliases: ["c"], description: "Compact conversation" },
1415
1504
  { name: "clear", aliases: [], description: "Clear session and terminal" },
1416
1505
  { name: "quit", aliases: ["q", "exit"], description: "Exit the agent" },
1506
+ { name: "theme", aliases: ["t"], description: "Switch theme" },
1417
1507
  { name: "plan", aliases: [], description: "Toggle plan mode (on/off)" },
1418
1508
  { name: "plans", aliases: [], description: "Open plans pane" },
1419
1509
  ...PROMPT_COMMANDS.map((cmd) => ({
@@ -1432,15 +1522,15 @@ export function App(props) {
1432
1522
  case "tombstone":
1433
1523
  return null;
1434
1524
  case "banner":
1435
- return (_jsx(Banner, { version: props.version, model: props.model, provider: props.provider, cwd: props.cwd, taskCount: taskCount }, item.id));
1525
+ return (_jsx(Banner, { version: props.version, model: currentModel, provider: currentProvider, cwd: props.cwd, taskCount: taskCount }, item.id));
1436
1526
  case "user":
1437
1527
  return (_jsx(UserMessage, { text: item.text, imageCount: item.imageCount, pasteInfo: item.pasteInfo }, item.id));
1438
1528
  case "task":
1439
1529
  return (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { wrap: "wrap", children: [_jsx(Text, { color: theme.success, bold: true, children: "▶ " }), _jsx(Text, { color: theme.textDim, children: "Task: " }), _jsx(Text, { color: theme.success, children: item.title })] }) }, item.id));
1440
1530
  case "assistant":
1441
- return (_jsx(AssistantMessage, { text: item.text, thinking: item.thinking, thinkingMs: item.thinkingMs, showThinking: props.showThinking }, item.id));
1531
+ return (_jsx(AssistantMessage, { text: item.text, thinking: item.thinking, thinkingMs: item.thinkingMs, showThinking: props.showThinking, planMode: item.planMode }, item.id));
1442
1532
  case "tool_start":
1443
- return _jsx(ToolExecution, { status: "running", name: item.name, args: item.args }, item.id);
1533
+ return (_jsx(ToolExecution, { status: "running", name: item.name, args: item.args, progressOutput: item.progressOutput }, item.id));
1444
1534
  case "tool_done":
1445
1535
  return (_jsx(ToolExecution, { status: "done", name: item.name, args: item.args, result: item.result, isError: item.isError, details: item.details }, item.id));
1446
1536
  case "tool_group":
@@ -1456,7 +1546,7 @@ export function App(props) {
1456
1546
  case "info":
1457
1547
  return (_jsx(Box, { marginTop: 1, flexShrink: 1, children: _jsx(Text, { color: theme.textDim, wrap: "wrap", children: item.text }) }, item.id));
1458
1548
  case "plan_transition":
1459
- return (_jsx(Box, { marginTop: 1, flexShrink: 1, children: _jsxs(Text, { color: theme.planPrimary, bold: true, wrap: "wrap", children: [item.active ? " " : " ", item.text] }) }, item.id));
1549
+ return (_jsx(Box, { marginTop: 1, flexShrink: 1, children: _jsxs(Text, { color: theme.planPrimary, bold: true, wrap: "wrap", children: [item.active ? " " : " ", item.text] }) }, item.id));
1460
1550
  case "queued":
1461
1551
  return (_jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: theme.accent, bold: true, children: "⏳ Queued: " }), _jsxs(Text, { color: theme.text, wrap: "wrap", children: [item.text, item.imageCount
1462
1552
  ? ` (+${item.imageCount} image${item.imageCount > 1 ? "s" : ""})`
@@ -1547,11 +1637,17 @@ export function App(props) {
1547
1637
  setStaticKey((k) => k + 1);
1548
1638
  setOverlay(null);
1549
1639
  } })) : isPlanView ? (_jsx(PlanOverlay, { cwd: props.cwd, autoExpandNewest: planAutoExpand, onClose: () => {
1640
+ planOverlayPendingRef.current = false;
1550
1641
  stdout?.write("\x1b[2J\x1b[3J\x1b[H");
1551
1642
  setStaticKey((k) => k + 1);
1552
1643
  setPlanAutoExpand(false);
1553
1644
  setOverlay(null);
1554
1645
  }, onApprove: (planPath) => {
1646
+ log("INFO", "plan", "Plan approved — transitioning to implementation", {
1647
+ planPath,
1648
+ });
1649
+ // Plan overlay dismissed — allow future onDone to fire normally
1650
+ planOverlayPendingRef.current = false;
1555
1651
  // Store approved plan path — will be injected into the new system prompt
1556
1652
  approvedPlanPathRef.current = planPath;
1557
1653
  // Extract plan steps for progress tracking
@@ -1569,28 +1665,36 @@ export function App(props) {
1569
1665
  setOverlay(null);
1570
1666
  // Rebuild system prompt with the approved plan, then reset the session
1571
1667
  void (async () => {
1572
- const newPrompt = await buildSystemPrompt(props.cwd, props.skills, false, planPath);
1573
- messagesRef.current = [{ role: "system", content: newPrompt }];
1574
- agentLoop.reset();
1575
- persistedIndexRef.current = messagesRef.current.length;
1576
- // Create a new session file
1577
- const sm = sessionManagerRef.current;
1578
- if (sm) {
1579
- const s = await sm.create(props.cwd, currentProvider, currentModel);
1580
- sessionPathRef.current = s.path;
1668
+ try {
1669
+ const newPrompt = await buildSystemPrompt(props.cwd, props.skills, false, planPath);
1670
+ messagesRef.current = [{ role: "system", content: newPrompt }];
1671
+ agentLoop.reset();
1672
+ persistedIndexRef.current = messagesRef.current.length;
1673
+ // Create a new session file
1674
+ const sm = sessionManagerRef.current;
1675
+ if (sm) {
1676
+ const s = await sm.create(props.cwd, currentProvider, currentModel);
1677
+ sessionPathRef.current = s.path;
1678
+ }
1679
+ // Start implementation with a clean context
1680
+ setLiveItems([
1681
+ {
1682
+ kind: "info",
1683
+ text: "Plan approved — starting fresh session for implementation",
1684
+ id: getId(),
1685
+ },
1686
+ ]);
1687
+ setDoneStatus(null);
1688
+ await agentLoop.run("The plan has been approved. Implement it now, following each step in order.");
1689
+ }
1690
+ catch (err) {
1691
+ const errMsg = err instanceof Error ? err.message : String(err);
1692
+ log("ERROR", "error", errMsg);
1693
+ setLiveItems((prev) => [...prev, { kind: "error", message: errMsg, id: getId() }]);
1581
1694
  }
1582
- // Start implementation with a clean context
1583
- setLiveItems([
1584
- {
1585
- kind: "info",
1586
- text: "Plan approved — starting fresh session for implementation",
1587
- id: getId(),
1588
- },
1589
- ]);
1590
- setDoneStatus(null);
1591
- await agentLoop.run("The plan has been approved. Implement it now, following each step in order.");
1592
1695
  })();
1593
1696
  }, onReject: (planPath, feedback) => {
1697
+ planOverlayPendingRef.current = false;
1594
1698
  stdout?.write("\x1b[2J\x1b[3J\x1b[H");
1595
1699
  setStaticKey((k) => k + 1);
1596
1700
  setPlanAutoExpand(false);
@@ -1603,10 +1707,15 @@ export function App(props) {
1603
1707
  ...prev,
1604
1708
  { kind: "info", text: `Plan rejected — "${feedback}"`, id: getId() },
1605
1709
  ]);
1606
- void agentLoop.run(msg);
1710
+ void agentLoop.run(msg).catch((err) => {
1711
+ const errMsg = err instanceof Error ? err.message : String(err);
1712
+ log("ERROR", "error", errMsg);
1713
+ setLiveItems((prev) => [...prev, { kind: "error", message: errMsg, id: getId() }]);
1714
+ });
1607
1715
  } })) : (_jsxs(_Fragment, { children: [_jsxs(Box, { flexDirection: "column", flexGrow: 1, paddingRight: 1, children: [liveItems.map((item) => renderItem(item)), _jsx(StreamingArea, { isRunning: agentLoop.isRunning, streamingText: agentLoop.streamingText, streamingThinking: agentLoop.streamingThinking, showThinking: props.showThinking, thinkingMs: agentLoop.thinkingMs, planMode: planMode })] }), agentLoop.isRunning && agentLoop.activityPhase !== "idle" ? (_jsx(Box, { marginTop: 1, borderStyle: "round", borderColor: agentLoop.activityPhase === "thinking"
1608
1716
  ? THINKING_BORDER_COLORS[thinkingBorderFrame]
1609
- : "transparent", paddingLeft: 1, paddingRight: 1, width: columns, children: _jsx(ActivityIndicator, { phase: agentLoop.activityPhase, elapsedMs: agentLoop.elapsedMs, thinkingMs: agentLoop.thinkingMs, isThinking: agentLoop.isThinking, tokenEstimate: agentLoop.streamedTokenEstimate, charCountRef: agentLoop.charCountRef, realTokensAccumRef: agentLoop.realTokensAccumRef, userMessage: lastUserMessage, activeToolNames: agentLoop.activeToolCalls.map((tc) => tc.name), planMode: planMode, retryInfo: agentLoop.retryInfo, planDone: planSteps.filter((s) => s.completed).length, planTotal: planSteps.length }) })) : (doneStatus && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: theme.success, children: [" ", doneStatus.verb, " ", formatDuration(doneStatus.durationMs)] }) }))), agentLoop.queuedCount > 0 && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: theme.accent, children: [" ", agentLoop.queuedCount, " message", agentLoop.queuedCount > 1 ? "s" : "", " queued"] }) })), _jsx(InputArea, { onSubmit: handleSubmit, onAbort: handleAbort, disabled: agentLoop.isRunning, isActive: !taskBarFocused && !overlay, onDownAtEnd: handleFocusTaskBar, onShiftTab: handleToggleThinking, onToggleTasks: () => {
1717
+ : "transparent", paddingLeft: 1, paddingRight: 1, width: columns, children: _jsx(ActivityIndicator, { phase: agentLoop.activityPhase, elapsedMs: agentLoop.elapsedMs, runStartRef: agentLoop.runStartRef, thinkingMs: agentLoop.thinkingMs, isThinking: agentLoop.isThinking, tokenEstimate: agentLoop.streamedTokenEstimate, charCountRef: agentLoop.charCountRef, realTokensAccumRef: agentLoop.realTokensAccumRef, userMessage: lastUserMessage, activeToolNames: agentLoop.activeToolCalls.map((tc) => tc.name), planMode: planMode, retryInfo: agentLoop.retryInfo, planDone: planSteps.filter((s) => s.completed).length, planTotal: planSteps.length }) })) : agentLoop.stallError ? (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: theme.warning, children: " API provider stream interrupted — retries exhausted." }), _jsx(Text, { color: theme.textDim, children: " Your conversation is preserved. Send a message to continue." })] })) : (doneStatus &&
1718
+ !agentLoop.isRunning && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: theme.success, children: ["✻ ", doneStatus.verb, " ", formatDuration(doneStatus.durationMs)] }) }))), agentLoop.queuedCount > 0 && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: theme.accent, children: ["⏳ ", agentLoop.queuedCount, " message", agentLoop.queuedCount > 1 ? "s" : "", " queued"] }) })), _jsx(InputArea, { onSubmit: handleSubmit, onAbort: handleAbort, disabled: agentLoop.isRunning, isActive: !taskBarFocused && !overlay, onDownAtEnd: handleFocusTaskBar, onShiftTab: handleToggleThinking, onToggleTasks: () => {
1610
1719
  stdout?.write("\x1b[2J\x1b[3J\x1b[H");
1611
1720
  setOverlay("tasks");
1612
1721
  }, onToggleSkills: () => {
@@ -1625,6 +1734,6 @@ export function App(props) {
1625
1734
  id: getId(),
1626
1735
  },
1627
1736
  ]);
1628
- }, cwd: props.cwd, commands: allCommands }), overlay === "model" ? (_jsx(ModelSelector, { onSelect: handleModelSelect, onCancel: () => setOverlay(null), loggedInProviders: props.loggedInProviders ?? [currentProvider], currentModel: currentModel, currentProvider: currentProvider })) : (_jsx(Footer, { model: currentModel, tokensIn: agentLoop.contextUsed, linesAdded: agentLoop.linesChanged.added, linesRemoved: agentLoop.linesChanged.removed, cwd: props.cwd, gitBranch: gitBranch, thinkingEnabled: thinkingEnabled, planMode: planMode })), buddyEnabled && _jsx(Buddy, { phase: agentLoop.activityPhase }), bgTasks.length > 0 && (_jsx(BackgroundTasksBar, { tasks: bgTasks, focused: taskBarFocused, expanded: taskBarExpanded, selectedIndex: selectedTaskIndex, onExpand: handleTaskBarExpand, onCollapse: handleTaskBarCollapse, onKill: handleTaskKill, onExit: handleTaskBarExit, onNavigate: handleTaskNavigate }))] }))] }));
1737
+ }, cwd: props.cwd, commands: allCommands }), overlay === "model" ? (_jsx(ModelSelector, { onSelect: handleModelSelect, onCancel: () => setOverlay(null), loggedInProviders: props.loggedInProviders ?? [currentProvider], currentModel: currentModel, currentProvider: currentProvider })) : overlay === "theme" ? (_jsx(ThemeSelector, { onSelect: handleThemeSelect, onCancel: () => setOverlay(null), currentTheme: theme.name })) : (_jsx(Footer, { model: currentModel, tokensIn: agentLoop.contextUsed, cwd: props.cwd, gitBranch: gitBranch, thinkingEnabled: thinkingEnabled, planMode: planMode, exitPending: exitPending })), buddyEnabled && _jsx(Buddy, { phase: agentLoop.activityPhase }), bgTasks.length > 0 && (_jsx(BackgroundTasksBar, { tasks: bgTasks, focused: taskBarFocused, expanded: taskBarExpanded, selectedIndex: selectedTaskIndex, onExpand: handleTaskBarExpand, onCollapse: handleTaskBarCollapse, onKill: handleTaskKill, onExit: handleTaskBarExit, onNavigate: handleTaskNavigate }))] }))] }));
1629
1738
  }
1630
1739
  //# sourceMappingURL=App.js.map