@mohanscodex/spectra-code 0.4.6 → 0.4.8

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 (292) hide show
  1. package/dist/package.json +1 -1
  2. package/dist/src/agents/definitions.d.ts +2 -2
  3. package/dist/src/agents/definitions.d.ts.map +1 -1
  4. package/dist/src/agents/definitions.js +18 -18
  5. package/dist/src/agents/definitions.js.map +1 -1
  6. package/dist/src/agents/registry.d.ts +1 -1
  7. package/dist/src/agents/registry.d.ts.map +1 -1
  8. package/dist/src/agents/registry.js.map +1 -1
  9. package/dist/src/cli.js +117 -115
  10. package/dist/src/cli.js.map +1 -1
  11. package/dist/src/commands/agent.d.ts +1 -1
  12. package/dist/src/commands/agent.d.ts.map +1 -1
  13. package/dist/src/commands/agent.js +14 -14
  14. package/dist/src/commands/agent.js.map +1 -1
  15. package/dist/src/commands/db.d.ts +1 -1
  16. package/dist/src/commands/db.d.ts.map +1 -1
  17. package/dist/src/commands/db.js +11 -11
  18. package/dist/src/commands/db.js.map +1 -1
  19. package/dist/src/commands/doctor.d.ts.map +1 -1
  20. package/dist/src/commands/doctor.js +33 -30
  21. package/dist/src/commands/doctor.js.map +1 -1
  22. package/dist/src/commands/mcp.d.ts +1 -1
  23. package/dist/src/commands/mcp.d.ts.map +1 -1
  24. package/dist/src/commands/mcp.js +39 -39
  25. package/dist/src/commands/mcp.js.map +1 -1
  26. package/dist/src/commands/plugin.d.ts +1 -1
  27. package/dist/src/commands/plugin.d.ts.map +1 -1
  28. package/dist/src/commands/plugin.js +13 -13
  29. package/dist/src/commands/plugin.js.map +1 -1
  30. package/dist/src/commands/session.d.ts +1 -1
  31. package/dist/src/commands/session.d.ts.map +1 -1
  32. package/dist/src/commands/session.js +12 -12
  33. package/dist/src/commands/session.js.map +1 -1
  34. package/dist/src/index.d.ts +18 -18
  35. package/dist/src/index.d.ts.map +1 -1
  36. package/dist/src/index.js +16 -16
  37. package/dist/src/index.js.map +1 -1
  38. package/dist/src/integrations/acp/index.d.ts +1 -1
  39. package/dist/src/integrations/acp/index.js +1 -1
  40. package/dist/src/integrations/acp/server.d.ts.map +1 -1
  41. package/dist/src/integrations/acp/server.js +89 -79
  42. package/dist/src/integrations/acp/server.js.map +1 -1
  43. package/dist/src/integrations/custom-tools/index.d.ts +2 -2
  44. package/dist/src/integrations/custom-tools/index.js +1 -1
  45. package/dist/src/integrations/custom-tools/loader.d.ts +2 -2
  46. package/dist/src/integrations/custom-tools/loader.d.ts.map +1 -1
  47. package/dist/src/integrations/custom-tools/loader.js +19 -18
  48. package/dist/src/integrations/custom-tools/loader.js.map +1 -1
  49. package/dist/src/integrations/mcp/client.d.ts +5 -5
  50. package/dist/src/integrations/mcp/client.d.ts.map +1 -1
  51. package/dist/src/integrations/mcp/client.js +7 -7
  52. package/dist/src/integrations/mcp/client.js.map +1 -1
  53. package/dist/src/integrations/mcp/index.d.ts +2 -2
  54. package/dist/src/integrations/mcp/index.d.ts.map +1 -1
  55. package/dist/src/integrations/mcp/index.js +1 -1
  56. package/dist/src/integrations/mcp/index.js.map +1 -1
  57. package/dist/src/security/doom-loop.d.ts +1 -1
  58. package/dist/src/security/doom-loop.d.ts.map +1 -1
  59. package/dist/src/security/doom-loop.js +4 -4
  60. package/dist/src/security/doom-loop.js.map +1 -1
  61. package/dist/src/security/index.d.ts +10 -10
  62. package/dist/src/security/index.d.ts.map +1 -1
  63. package/dist/src/security/index.js +116 -69
  64. package/dist/src/security/index.js.map +1 -1
  65. package/dist/src/security/path-safety.d.ts +1 -1
  66. package/dist/src/security/path-safety.d.ts.map +1 -1
  67. package/dist/src/security/path-safety.js +19 -19
  68. package/dist/src/security/path-safety.js.map +1 -1
  69. package/dist/src/security/permissions.d.ts +1 -1
  70. package/dist/src/security/permissions.d.ts.map +1 -1
  71. package/dist/src/security/permissions.js +12 -10
  72. package/dist/src/security/permissions.js.map +1 -1
  73. package/dist/src/security/read-tracker.d.ts +2 -2
  74. package/dist/src/security/read-tracker.d.ts.map +1 -1
  75. package/dist/src/security/read-tracker.js +15 -13
  76. package/dist/src/security/read-tracker.js.map +1 -1
  77. package/dist/src/security/ssrf-guard.d.ts +1 -1
  78. package/dist/src/security/ssrf-guard.d.ts.map +1 -1
  79. package/dist/src/security/ssrf-guard.js +11 -11
  80. package/dist/src/security/ssrf-guard.js.map +1 -1
  81. package/dist/src/security/types.d.ts +4 -4
  82. package/dist/src/security/types.d.ts.map +1 -1
  83. package/dist/src/security/wildcard.d.ts.map +1 -1
  84. package/dist/src/security/wildcard.js +14 -16
  85. package/dist/src/security/wildcard.js.map +1 -1
  86. package/dist/src/services/auth-store.d.ts +3 -3
  87. package/dist/src/services/auth-store.d.ts.map +1 -1
  88. package/dist/src/services/auth-store.js +7 -7
  89. package/dist/src/services/auth-store.js.map +1 -1
  90. package/dist/src/services/config.d.ts +3 -3
  91. package/dist/src/services/config.d.ts.map +1 -1
  92. package/dist/src/services/config.js +17 -21
  93. package/dist/src/services/config.js.map +1 -1
  94. package/dist/src/services/context.d.ts.map +1 -1
  95. package/dist/src/services/context.js +5 -8
  96. package/dist/src/services/context.js.map +1 -1
  97. package/dist/src/services/custom-providers.d.ts +2 -2
  98. package/dist/src/services/custom-providers.d.ts.map +1 -1
  99. package/dist/src/services/custom-providers.js +108 -49
  100. package/dist/src/services/custom-providers.js.map +1 -1
  101. package/dist/src/services/session-store.d.ts +1 -1
  102. package/dist/src/services/session-store.d.ts.map +1 -1
  103. package/dist/src/services/session-store.js +32 -24
  104. package/dist/src/services/session-store.js.map +1 -1
  105. package/dist/src/services/snapshot-manager.d.ts.map +1 -1
  106. package/dist/src/services/snapshot-manager.js +14 -15
  107. package/dist/src/services/snapshot-manager.js.map +1 -1
  108. package/dist/src/tools/edit.d.ts +1 -1
  109. package/dist/src/tools/edit.js +18 -16
  110. package/dist/src/tools/edit.js.map +1 -1
  111. package/dist/src/tools/glob.d.ts +1 -1
  112. package/dist/src/tools/glob.d.ts.map +1 -1
  113. package/dist/src/tools/glob.js +20 -16
  114. package/dist/src/tools/glob.js.map +1 -1
  115. package/dist/src/tools/grep.d.ts +1 -1
  116. package/dist/src/tools/grep.d.ts.map +1 -1
  117. package/dist/src/tools/grep.js +18 -18
  118. package/dist/src/tools/grep.js.map +1 -1
  119. package/dist/src/tools/index.d.ts +4 -4
  120. package/dist/src/tools/index.d.ts.map +1 -1
  121. package/dist/src/tools/index.js +40 -32
  122. package/dist/src/tools/index.js.map +1 -1
  123. package/dist/src/tools/mcp-tool.d.ts +2 -2
  124. package/dist/src/tools/mcp-tool.d.ts.map +1 -1
  125. package/dist/src/tools/mcp-tool.js +28 -25
  126. package/dist/src/tools/mcp-tool.js.map +1 -1
  127. package/dist/src/tools/read.d.ts +1 -1
  128. package/dist/src/tools/read.d.ts.map +1 -1
  129. package/dist/src/tools/read.js +15 -13
  130. package/dist/src/tools/read.js.map +1 -1
  131. package/dist/src/tools/shell.d.ts +1 -1
  132. package/dist/src/tools/shell.d.ts.map +1 -1
  133. package/dist/src/tools/shell.js +75 -66
  134. package/dist/src/tools/shell.js.map +1 -1
  135. package/dist/src/tools/task.d.ts +1 -1
  136. package/dist/src/tools/task.js +22 -22
  137. package/dist/src/tools/task.js.map +1 -1
  138. package/dist/src/tools/types.d.ts +3 -3
  139. package/dist/src/tools/types.d.ts.map +1 -1
  140. package/dist/src/tools/utils.d.ts +1 -1
  141. package/dist/src/tools/utils.js +2 -2
  142. package/dist/src/tools/utils.js.map +1 -1
  143. package/dist/src/tools/web-fetch.d.ts +1 -1
  144. package/dist/src/tools/web-fetch.js +31 -31
  145. package/dist/src/tools/web-fetch.js.map +1 -1
  146. package/dist/src/tools/write.d.ts +1 -1
  147. package/dist/src/tools/write.js +9 -9
  148. package/dist/src/tools/write.js.map +1 -1
  149. package/dist/src/tui/app-constants.d.ts.map +1 -1
  150. package/dist/src/tui/app-constants.js +11 -2
  151. package/dist/src/tui/app-constants.js.map +1 -1
  152. package/dist/src/tui/app.d.ts +1 -1
  153. package/dist/src/tui/app.d.ts.map +1 -1
  154. package/dist/src/tui/app.js +314 -121
  155. package/dist/src/tui/app.js.map +1 -1
  156. package/dist/src/tui/commands.d.ts +14 -14
  157. package/dist/src/tui/commands.d.ts.map +1 -1
  158. package/dist/src/tui/commands.js +242 -49
  159. package/dist/src/tui/commands.js.map +1 -1
  160. package/dist/src/tui/components/chat-area.d.ts +2 -2
  161. package/dist/src/tui/components/chat-area.d.ts.map +1 -1
  162. package/dist/src/tui/components/chat-area.js +5 -5
  163. package/dist/src/tui/components/chat-area.js.map +1 -1
  164. package/dist/src/tui/components/command-palette.d.ts.map +1 -1
  165. package/dist/src/tui/components/command-palette.js +7 -5
  166. package/dist/src/tui/components/command-palette.js.map +1 -1
  167. package/dist/src/tui/components/message.d.ts +2 -2
  168. package/dist/src/tui/components/message.d.ts.map +1 -1
  169. package/dist/src/tui/components/message.js +63 -52
  170. package/dist/src/tui/components/message.js.map +1 -1
  171. package/dist/src/tui/components/slash-autocomplete.d.ts +2 -2
  172. package/dist/src/tui/components/slash-autocomplete.d.ts.map +1 -1
  173. package/dist/src/tui/components/slash-autocomplete.js +6 -8
  174. package/dist/src/tui/components/slash-autocomplete.js.map +1 -1
  175. package/dist/src/tui/components/toast.d.ts +2 -2
  176. package/dist/src/tui/components/toast.d.ts.map +1 -1
  177. package/dist/src/tui/components/toast.js +11 -6
  178. package/dist/src/tui/components/toast.js.map +1 -1
  179. package/dist/src/tui/hooks/use-agent.d.ts +3 -3
  180. package/dist/src/tui/hooks/use-agent.d.ts.map +1 -1
  181. package/dist/src/tui/hooks/use-agent.js +22 -29
  182. package/dist/src/tui/hooks/use-agent.js.map +1 -1
  183. package/dist/src/tui/hooks/use-app-keyboard.d.ts +3 -3
  184. package/dist/src/tui/hooks/use-app-keyboard.d.ts.map +1 -1
  185. package/dist/src/tui/hooks/use-app-keyboard.js +28 -29
  186. package/dist/src/tui/hooks/use-app-keyboard.js.map +1 -1
  187. package/dist/src/tui/hooks/use-chat-submit.d.ts +6 -6
  188. package/dist/src/tui/hooks/use-chat-submit.d.ts.map +1 -1
  189. package/dist/src/tui/hooks/use-chat-submit.js +115 -54
  190. package/dist/src/tui/hooks/use-chat-submit.js.map +1 -1
  191. package/dist/src/tui/hooks/use-permission-queue.d.ts +2 -2
  192. package/dist/src/tui/hooks/use-permission-queue.d.ts.map +1 -1
  193. package/dist/src/tui/hooks/use-permission-queue.js +1 -1
  194. package/dist/src/tui/hooks/use-permission-queue.js.map +1 -1
  195. package/dist/src/tui/hooks/use-revert.d.ts +4 -4
  196. package/dist/src/tui/hooks/use-revert.d.ts.map +1 -1
  197. package/dist/src/tui/hooks/use-revert.js +22 -13
  198. package/dist/src/tui/hooks/use-revert.js.map +1 -1
  199. package/dist/src/tui/index.d.ts.map +1 -1
  200. package/dist/src/tui/index.js +11 -11
  201. package/dist/src/tui/index.js.map +1 -1
  202. package/dist/src/tui/prompt-bar.d.ts +1 -1
  203. package/dist/src/tui/prompt-bar.d.ts.map +1 -1
  204. package/dist/src/tui/prompt-bar.js +45 -16
  205. package/dist/src/tui/prompt-bar.js.map +1 -1
  206. package/dist/src/tui/slash-commands.d.ts +2 -2
  207. package/dist/src/tui/slash-commands.d.ts.map +1 -1
  208. package/dist/src/tui/slash-commands.js +9 -9
  209. package/dist/src/tui/slash-commands.js.map +1 -1
  210. package/dist/src/tui/theme.d.ts +1 -1
  211. package/dist/src/tui/theme.d.ts.map +1 -1
  212. package/dist/src/tui/theme.js +46 -38
  213. package/dist/src/tui/theme.js.map +1 -1
  214. package/dist/src/tui/tips.d.ts.map +1 -1
  215. package/dist/src/tui/tips.js +14 -14
  216. package/dist/src/tui/tips.js.map +1 -1
  217. package/dist/src/tui/types.d.ts +5 -5
  218. package/dist/src/tui/types.d.ts.map +1 -1
  219. package/dist/src/tui/ui/about-dialog.d.ts.map +1 -1
  220. package/dist/src/tui/ui/about-dialog.js +5 -5
  221. package/dist/src/tui/ui/about-dialog.js.map +1 -1
  222. package/dist/src/tui/ui/agent-switcher.d.ts.map +1 -1
  223. package/dist/src/tui/ui/agent-switcher.js +16 -14
  224. package/dist/src/tui/ui/agent-switcher.js.map +1 -1
  225. package/dist/src/tui/ui/debug-dialog.d.ts +1 -1
  226. package/dist/src/tui/ui/debug-dialog.d.ts.map +1 -1
  227. package/dist/src/tui/ui/debug-dialog.js +26 -26
  228. package/dist/src/tui/ui/debug-dialog.js.map +1 -1
  229. package/dist/src/tui/ui/doctor-dialog.d.ts +2 -2
  230. package/dist/src/tui/ui/doctor-dialog.d.ts.map +1 -1
  231. package/dist/src/tui/ui/doctor-dialog.js +10 -8
  232. package/dist/src/tui/ui/doctor-dialog.js.map +1 -1
  233. package/dist/src/tui/ui/manage-providers-dialog.d.ts +1 -1
  234. package/dist/src/tui/ui/manage-providers-dialog.d.ts.map +1 -1
  235. package/dist/src/tui/ui/manage-providers-dialog.js +102 -86
  236. package/dist/src/tui/ui/manage-providers-dialog.js.map +1 -1
  237. package/dist/src/tui/ui/mcp-toggle-dialog.d.ts.map +1 -1
  238. package/dist/src/tui/ui/mcp-toggle-dialog.js +17 -14
  239. package/dist/src/tui/ui/mcp-toggle-dialog.js.map +1 -1
  240. package/dist/src/tui/ui/message-controls.d.ts +1 -1
  241. package/dist/src/tui/ui/message-controls.d.ts.map +1 -1
  242. package/dist/src/tui/ui/message-controls.js +40 -28
  243. package/dist/src/tui/ui/message-controls.js.map +1 -1
  244. package/dist/src/tui/ui/model-switcher.d.ts.map +1 -1
  245. package/dist/src/tui/ui/model-switcher.js +40 -25
  246. package/dist/src/tui/ui/model-switcher.js.map +1 -1
  247. package/dist/src/tui/ui/permission-dialog.d.ts +1 -1
  248. package/dist/src/tui/ui/permission-dialog.d.ts.map +1 -1
  249. package/dist/src/tui/ui/permission-dialog.js +11 -11
  250. package/dist/src/tui/ui/permission-dialog.js.map +1 -1
  251. package/dist/src/tui/ui/provider-dialog.d.ts.map +1 -1
  252. package/dist/src/tui/ui/provider-dialog.js +75 -64
  253. package/dist/src/tui/ui/provider-dialog.js.map +1 -1
  254. package/dist/src/tui/ui/session-list.d.ts +3 -3
  255. package/dist/src/tui/ui/session-list.d.ts.map +1 -1
  256. package/dist/src/tui/ui/session-list.js +44 -32
  257. package/dist/src/tui/ui/session-list.js.map +1 -1
  258. package/dist/src/tui/ui/thinking-effort-dialog.d.ts.map +1 -1
  259. package/dist/src/tui/ui/thinking-effort-dialog.js +19 -19
  260. package/dist/src/tui/ui/thinking-effort-dialog.js.map +1 -1
  261. package/dist/src/tui/ui/update-dialog.d.ts +11 -0
  262. package/dist/src/tui/ui/update-dialog.d.ts.map +1 -0
  263. package/dist/src/tui/ui/update-dialog.js +23 -0
  264. package/dist/src/tui/ui/update-dialog.js.map +1 -0
  265. package/dist/src/tui/utils/model-config.d.ts.map +1 -1
  266. package/dist/src/tui/utils/model-config.js +13 -13
  267. package/dist/src/tui/utils/model-config.js.map +1 -1
  268. package/dist/src/tui/utils/session-messages.d.ts +2 -2
  269. package/dist/src/tui/utils/session-messages.d.ts.map +1 -1
  270. package/dist/src/tui/utils/session-messages.js +35 -22
  271. package/dist/src/tui/utils/session-messages.js.map +1 -1
  272. package/dist/src/tui/utils/update-check.d.ts +2 -0
  273. package/dist/src/tui/utils/update-check.d.ts.map +1 -0
  274. package/dist/src/tui/utils/update-check.js +65 -0
  275. package/dist/src/tui/utils/update-check.js.map +1 -0
  276. package/dist/src/tui/utils/version.d.ts.map +1 -1
  277. package/dist/src/tui/utils/version.js +7 -7
  278. package/dist/src/tui/utils/version.js.map +1 -1
  279. package/dist/src/tui/utils.d.ts +2 -2
  280. package/dist/src/tui/utils.d.ts.map +1 -1
  281. package/dist/src/tui/utils.js +7 -7
  282. package/dist/src/tui/utils.js.map +1 -1
  283. package/dist/src/tui/variant-cycle.d.ts.map +1 -1
  284. package/dist/src/tui/variant-cycle.js +23 -23
  285. package/dist/src/tui/variant-cycle.js.map +1 -1
  286. package/dist/src/utils/paths.d.ts.map +1 -1
  287. package/dist/src/utils/paths.js +25 -25
  288. package/dist/src/utils/paths.js.map +1 -1
  289. package/dist/src/utils/platform.d.ts.map +1 -1
  290. package/dist/src/utils/platform.js +15 -17
  291. package/dist/src/utils/platform.js.map +1 -1
  292. package/package.json +3 -3
@@ -1,43 +1,46 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
2
- import { useRef, useCallback, useEffect, useMemo, useState } from "react";
3
- import { useTerminalDimensions } from "@opentui/react";
4
- import { execSync } from "child_process";
5
- import { c, SPINNER } from "./theme.js";
6
- import { ChatArea } from "./components/chat-area.js";
7
- import { CommandPalette } from "./components/command-palette.js";
8
- import { PromptBar } from "./prompt-bar.js";
9
- import { Tips } from "./tips.js";
10
- import { titlecase } from "./utils.js";
11
- import { SessionStore } from "../services/session-store.js";
12
- import { SnapshotManager } from "../services/snapshot-manager.js";
13
- import { ProviderDialog } from "./ui/provider-dialog.js";
14
- import { SessionList } from "./ui/session-list.js";
15
- import { ModelSwitcher } from "./ui/model-switcher.js";
16
- import { ManageProvidersDialog } from "./ui/manage-providers-dialog.js";
17
- import { DoctorDialog } from "./ui/doctor-dialog.js";
18
- import { AboutDialog } from "./ui/about-dialog.js";
19
- import { AgentSwitcher } from "./ui/agent-switcher.js";
20
- import { ThinkingEffortDialog } from "./ui/thinking-effort-dialog.js";
21
- import { McpToggleDialog } from "./ui/mcp-toggle-dialog.js";
22
- import { DebugDialog } from "./ui/debug-dialog.js";
23
- import { MessageControls } from "./ui/message-controls.js";
24
- import { ToastContainer, showToast } from "./components/toast.js";
25
- import clipboard from "clipboardy";
26
- import { buildCmdItems, collectSlashNames } from "./commands.js";
27
- import { slashHead } from "./slash-commands.js";
28
- import { SlashAutocomplete } from "./components/slash-autocomplete.js";
29
- import { loadConfig } from "../services/config.js";
30
- import { registerAllCustomProviders } from "../services/custom-providers.js";
31
- import { PermissionDialog } from "./ui/permission-dialog.js";
32
- import { PLACEHOLDERS } from "./app-constants.js";
33
- import { loadSavedConfig, saveModelConfig, fmtCtx, lookupContextWindow } from "./utils/model-config.js";
34
- import { sdkMessagesToChatMessages } from "./utils/session-messages.js";
35
- import { usePermissionQueue } from "./hooks/use-permission-queue.js";
36
- import { useRevert } from "./hooks/use-revert.js";
37
- import { useAgent } from "./hooks/use-agent.js";
38
- import { useChatSubmit } from "./hooks/use-chat-submit.js";
39
- import { useAppKeyboard } from "./hooks/use-app-keyboard.js";
40
- import { cycleEffort } from "./variant-cycle.js";
2
+ import { useRef, useCallback, useEffect, useMemo, useState } from 'react';
3
+ import { useTerminalDimensions } from '@opentui/react';
4
+ import { execFileSync } from 'child_process';
5
+ import { c, SPINNER } from './theme.js';
6
+ import { ChatArea } from './components/chat-area.js';
7
+ import { CommandPalette } from './components/command-palette.js';
8
+ import { PromptBar } from './prompt-bar.js';
9
+ import { Tips } from './tips.js';
10
+ import { titlecase } from './utils.js';
11
+ import { SessionStore } from '../services/session-store.js';
12
+ import { SnapshotManager } from '../services/snapshot-manager.js';
13
+ import { ProviderDialog } from './ui/provider-dialog.js';
14
+ import { SessionList } from './ui/session-list.js';
15
+ import { ModelSwitcher } from './ui/model-switcher.js';
16
+ import { ManageProvidersDialog } from './ui/manage-providers-dialog.js';
17
+ import { DoctorDialog } from './ui/doctor-dialog.js';
18
+ import { AboutDialog } from './ui/about-dialog.js';
19
+ import { AgentSwitcher } from './ui/agent-switcher.js';
20
+ import { ThinkingEffortDialog } from './ui/thinking-effort-dialog.js';
21
+ import { McpToggleDialog } from './ui/mcp-toggle-dialog.js';
22
+ import { DebugDialog } from './ui/debug-dialog.js';
23
+ import { UpdateDialog } from './ui/update-dialog.js';
24
+ import { MessageControls } from './ui/message-controls.js';
25
+ import { ToastContainer, showToast } from './components/toast.js';
26
+ import clipboard from 'clipboardy';
27
+ import { buildCmdItems, collectSlashNames } from './commands.js';
28
+ import { slashHead } from './slash-commands.js';
29
+ import { SlashAutocomplete } from './components/slash-autocomplete.js';
30
+ import { checkForUpdate } from './utils/update-check.js';
31
+ import { VERSION } from './utils/version.js';
32
+ import { loadConfig } from '../services/config.js';
33
+ import { registerAllCustomProviders } from '../services/custom-providers.js';
34
+ import { PermissionDialog } from './ui/permission-dialog.js';
35
+ import { PLACEHOLDERS } from './app-constants.js';
36
+ import { loadSavedConfig, saveModelConfig, fmtCtx, lookupContextWindow } from './utils/model-config.js';
37
+ import { sdkMessagesToChatMessages } from './utils/session-messages.js';
38
+ import { usePermissionQueue } from './hooks/use-permission-queue.js';
39
+ import { useRevert } from './hooks/use-revert.js';
40
+ import { useAgent } from './hooks/use-agent.js';
41
+ import { useChatSubmit } from './hooks/use-chat-submit.js';
42
+ import { useAppKeyboard } from './hooks/use-app-keyboard.js';
43
+ import { cycleEffort } from './variant-cycle.js';
41
44
  export function App({ renderer }) {
42
45
  const { width: termWidth, height: termHeight } = useTerminalDimensions();
43
46
  // --- State ---
@@ -51,13 +54,13 @@ export function App({ renderer }) {
51
54
  const [selectedModel, setSelectedModel] = useState(savedConfig.model);
52
55
  const [selectedProvider, setSelectedProvider] = useState(savedConfig.provider);
53
56
  const [thinkingEffort, setThinkingEffort] = useState(undefined);
54
- const [route, setRoute] = useState("home");
57
+ const [route, setRoute] = useState('home');
55
58
  const [messages, setMessages] = useState([]);
56
59
  const [isLoading, setIsLoading] = useState(false);
57
- const [status, setStatus] = useState("Ready");
60
+ const [status, setStatus] = useState('Ready');
58
61
  const [spinnerFrame, setSpinnerFrame] = useState(0);
59
62
  const [showCmd, setShowCmd] = useState(false);
60
- const [cmdFilter, setCmdFilter] = useState("");
63
+ const [cmdFilter, setCmdFilter] = useState('');
61
64
  const [cmdSelected, setCmdSelected] = useState(0);
62
65
  const [elapsedMs, setElapsedMs] = useState(null);
63
66
  const [tokPerSec, setTokPerSec] = useState(null);
@@ -65,9 +68,10 @@ export function App({ renderer }) {
65
68
  const [showThinking, setShowThinking] = useState(true);
66
69
  const [showToolCalls, setShowToolCalls] = useState(true);
67
70
  const [copiedMsg, setCopiedMsg] = useState(false);
68
- const [selectedAgent, setSelectedAgent] = useState("build");
71
+ const [selectedAgent, setSelectedAgent] = useState('build');
69
72
  const [submitKey, setSubmitKey] = useState(0);
70
73
  const [dialogStep, setDialogStep] = useState(null);
74
+ const [updateVersion, setUpdateVersion] = useState(null);
71
75
  const [placeholderIdx, setPlaceholderIdx] = useState(0);
72
76
  const [promptHistory, setPromptHistory] = useState([]);
73
77
  const [historyIdx, setHistoryIdx] = useState(-1);
@@ -76,7 +80,7 @@ export function App({ renderer }) {
76
80
  const [interruptKey, setInterruptKey] = useState(0);
77
81
  const [msgControls, setMsgControls] = useState(null);
78
82
  const [revertPoint, setRevertPoint] = useState(null);
79
- const [draftText, setDraftText] = useState("");
83
+ const [draftText, setDraftText] = useState('');
80
84
  const [slashSelected, setSlashSelected] = useState(0);
81
85
  const [promptPosition, setPromptPosition] = useState({ top: 0, left: 0, width: 0 });
82
86
  // --- Refs ---
@@ -98,10 +102,14 @@ export function App({ renderer }) {
98
102
  const mcpCount = 0;
99
103
  const customProviderCount = Object.keys(customProviders).length;
100
104
  const cwdLabel = useMemo(() => {
101
- const home = process.env.HOME || process.env.USERPROFILE || "";
102
- const dir = process.cwd().replace(home, "~");
105
+ const home = process.env.HOME || process.env.USERPROFILE || '';
106
+ const dir = process.cwd().replace(home, '~');
103
107
  try {
104
- const branch = execSync("git rev-parse --abbrev-ref HEAD 2>/dev/null", { encoding: "utf-8", timeout: 2000 }).toString().trim();
108
+ const branch = execFileSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {
109
+ encoding: 'utf-8',
110
+ timeout: 2000,
111
+ stdio: ['pipe', 'pipe', 'ignore'],
112
+ }).trim();
105
113
  if (branch)
106
114
  return `${dir}:${branch}`;
107
115
  }
@@ -113,12 +121,21 @@ export function App({ renderer }) {
113
121
  const id = setInterval(() => setPlaceholderIdx((p) => (p + 1) % PLACEHOLDERS.length), 4000);
114
122
  return () => clearInterval(id);
115
123
  }, []);
124
+ useEffect(() => {
125
+ checkForUpdate().then((version) => {
126
+ if (version)
127
+ setUpdateVersion(version);
128
+ });
129
+ }, []);
116
130
  useEffect(() => {
117
131
  if (!isLoading)
118
132
  return;
119
133
  const id = setInterval(() => setSpinnerFrame((f) => (f + 1) % SPINNER.length), 80);
120
134
  renderer.requestLive();
121
- return () => { clearInterval(id); renderer.dropLive(); };
135
+ return () => {
136
+ clearInterval(id);
137
+ renderer.dropLive();
138
+ };
122
139
  }, [isLoading, renderer]);
123
140
  useEffect(() => {
124
141
  const handler = (selection) => {
@@ -134,8 +151,10 @@ export function App({ renderer }) {
134
151
  catch { }
135
152
  }, 2000);
136
153
  };
137
- renderer.on("selection", handler);
138
- return () => { renderer.off?.("selection", handler); };
154
+ renderer.on('selection', handler);
155
+ return () => {
156
+ renderer.off?.('selection', handler);
157
+ };
139
158
  }, [renderer]);
140
159
  // --- Stable callbacks ---
141
160
  const addMessage = useCallback((msg) => setMessages((p) => [...p, msg]), []);
@@ -143,30 +162,59 @@ export function App({ renderer }) {
143
162
  // --- Hooks (order matters for ref lifecycle) ---
144
163
  const securityRef = useRef(null);
145
164
  const { permissionRequest, enqueuePermission, resolvePermission } = usePermissionQueue(securityRef);
146
- const { agentRef, loadedSessionMessages, getOrCreateAgent, resetAgentForModelSwitch, } = useAgent({ securityRef, securityConfig, enqueuePermission });
147
- const { revertedMessagesRef, revertDraftRef, runRevert, runRedo, runRollbackFiles, discardRevert, } = useRevert({
148
- sessionStore, sessionId, agentRef, loadedSessionMessages,
149
- setMessages, setRevertPoint, setHistoryIdx, setNavKey, snapshotManager,
165
+ const { agentRef, loadedSessionMessages, getOrCreateAgent, resetAgentForModelSwitch } = useAgent({
166
+ securityRef,
167
+ securityConfig,
168
+ enqueuePermission,
169
+ });
170
+ const { revertedMessagesRef, revertDraftRef, runRevert, runRedo, runRollbackFiles, discardRevert } = useRevert({
171
+ sessionStore,
172
+ sessionId,
173
+ agentRef,
174
+ loadedSessionMessages,
175
+ setMessages,
176
+ setRevertPoint,
177
+ setHistoryIdx,
178
+ setNavKey,
179
+ snapshotManager,
150
180
  });
151
181
  const handleCycleVariant = useCallback(() => {
152
182
  if (!provider) {
153
- showToast("No provider configured", "warn");
183
+ showToast('No provider configured', 'warn');
154
184
  return;
155
185
  }
156
186
  const nextEffort = cycleEffort(provider, thinkingEffort);
157
187
  if (!nextEffort) {
158
- showToast("No variants available", "info");
188
+ showToast('No variants available', 'info');
159
189
  return;
160
190
  }
161
191
  setThinkingEffort(nextEffort);
162
192
  agentRef.current = null;
163
- showToast(nextEffort === "none" ? "Thinking: off" : `Thinking: ${nextEffort}`, "info");
193
+ showToast(nextEffort === 'none' ? 'Thinking: off' : `Thinking: ${nextEffort}`, 'info');
164
194
  }, [provider, thinkingEffort, agentRef]);
165
195
  const cmdItems = useMemo(() => buildCmdItems({
166
- renderer, sessionStore: sessionStore.current, sessionIdRef: sessionId,
167
- hasModel, selectedModel, provider, mcpCount, customProviderCount, messagesLength: messages.length,
168
- showThinking, showToolCalls,
169
- setRoute, setMessages, setStatus, setElapsedMs, setTokPerSec, setTokenUsage, setShowThinking, setShowToolCalls, setHomeKey, setNavKey, setDialogStep,
196
+ renderer,
197
+ sessionStore: sessionStore.current,
198
+ sessionIdRef: sessionId,
199
+ hasModel,
200
+ selectedModel,
201
+ provider,
202
+ mcpCount,
203
+ customProviderCount,
204
+ messagesLength: messages.length,
205
+ showThinking,
206
+ showToolCalls,
207
+ setRoute,
208
+ setMessages,
209
+ setStatus,
210
+ setElapsedMs,
211
+ setTokPerSec,
212
+ setTokenUsage,
213
+ setShowThinking,
214
+ setShowToolCalls,
215
+ setHomeKey,
216
+ setNavKey,
217
+ setDialogStep,
170
218
  onCycleVariant: handleCycleVariant,
171
219
  currentEffort: thinkingEffort,
172
220
  selectedAgent,
@@ -174,23 +222,81 @@ export function App({ renderer }) {
174
222
  securityRef.current?.getReadTracker().reset();
175
223
  securityRef.current?.getDoomLoop().reset();
176
224
  },
177
- }), [renderer, hasModel, selectedModel, provider, mcpCount, customProviderCount, messages.length, showThinking, showToolCalls, handleCycleVariant, thinkingEffort, selectedAgent, sessionStore, sessionId, setRoute, setMessages, setStatus, setElapsedMs, setTokPerSec, setTokenUsage, setShowThinking, setShowToolCalls, setHomeKey, setNavKey, setDialogStep, securityRef]);
225
+ }), [
226
+ renderer,
227
+ hasModel,
228
+ selectedModel,
229
+ provider,
230
+ mcpCount,
231
+ customProviderCount,
232
+ messages.length,
233
+ showThinking,
234
+ showToolCalls,
235
+ handleCycleVariant,
236
+ thinkingEffort,
237
+ selectedAgent,
238
+ sessionStore,
239
+ sessionId,
240
+ setRoute,
241
+ setMessages,
242
+ setStatus,
243
+ setElapsedMs,
244
+ setTokPerSec,
245
+ setTokenUsage,
246
+ setShowThinking,
247
+ setShowToolCalls,
248
+ setHomeKey,
249
+ setNavKey,
250
+ setDialogStep,
251
+ securityRef,
252
+ ]);
178
253
  const slashNames = useMemo(() => collectSlashNames(cmdItems), [cmdItems]);
179
- const { handleSubmit, updateLastAssistantMeta, } = useChatSubmit({
180
- sessionStore, sessionId, agentRef, securityRef, loadedSessionMessages,
181
- snapshotManager, lastAgentRef: useRef(null), isStreamingRef, currentTurnStartRef,
182
- currentTurnMsgIdRef, revertPoint, getOrCreateAgent,
183
- selectedModel, provider, selectedAgent, customProviders, thinkingEffort,
184
- cmdItems, slashNames, addMessage, updateMessage, setMessages,
185
- setIsLoading, setStatus, setRoute, setElapsedMs, setTokPerSec,
186
- setTokenUsage, setDraftText, setSlashSelected, setSubmitKey,
187
- setPromptHistory, setHistoryIdx, setInterruptKey, setRevertPoint,
254
+ const { handleSubmit, updateLastAssistantMeta } = useChatSubmit({
255
+ sessionStore,
256
+ sessionId,
257
+ agentRef,
258
+ securityRef,
259
+ loadedSessionMessages,
260
+ snapshotManager,
261
+ lastAgentRef: useRef(null),
262
+ isStreamingRef,
263
+ currentTurnStartRef,
264
+ currentTurnMsgIdRef,
265
+ revertPoint,
266
+ getOrCreateAgent,
267
+ selectedModel,
268
+ provider,
269
+ selectedAgent,
270
+ customProviders,
271
+ thinkingEffort,
272
+ cmdItems,
273
+ slashNames,
274
+ addMessage,
275
+ updateMessage,
276
+ setMessages,
277
+ setIsLoading,
278
+ setStatus,
279
+ setRoute,
280
+ setElapsedMs,
281
+ setTokPerSec,
282
+ setTokenUsage,
283
+ setDraftText,
284
+ setSlashSelected,
285
+ setSubmitKey,
286
+ setPromptHistory,
287
+ setHistoryIdx,
288
+ setInterruptKey,
289
+ setRevertPoint,
188
290
  discardRevert,
189
291
  });
190
292
  // --- cmdFiltered + slash ---
191
293
  const cmdFiltered = useMemo(() => {
192
294
  const q = cmdFilter.toLowerCase();
193
- return !q ? cmdItems : cmdItems.filter((i) => i.label.toLowerCase().includes(q) || i.desc.toLowerCase().includes(q) || (i.cat && i.cat.toLowerCase().includes(q)));
295
+ return !q
296
+ ? cmdItems
297
+ : cmdItems.filter((i) => i.label.toLowerCase().includes(q) ||
298
+ i.desc.toLowerCase().includes(q) ||
299
+ (i.cat && i.cat.toLowerCase().includes(q)));
194
300
  }, [cmdItems, cmdFilter]);
195
301
  const slashFiltered = useMemo(() => {
196
302
  const head = slashHead(draftText);
@@ -208,54 +314,98 @@ export function App({ renderer }) {
208
314
  });
209
315
  }, [cmdItems, draftText]);
210
316
  const slashActive = useMemo(() => slashHead(draftText) !== undefined, [draftText]);
211
- useEffect(() => { setSlashSelected(0); }, [draftText]);
212
- useEffect(() => { if (cmdSelected >= cmdFiltered.length && cmdFiltered.length > 0)
213
- setCmdSelected(cmdFiltered.length - 1); }, [cmdSelected, cmdFiltered.length]);
214
- const execCmd = useCallback((item) => { item.action(); setShowCmd(false); }, [setShowCmd]);
317
+ useEffect(() => {
318
+ setSlashSelected(0);
319
+ }, [draftText]);
320
+ useEffect(() => {
321
+ if (cmdSelected >= cmdFiltered.length && cmdFiltered.length > 0)
322
+ setCmdSelected(cmdFiltered.length - 1);
323
+ }, [cmdSelected, cmdFiltered.length]);
324
+ const execCmd = useCallback((item) => {
325
+ item.action();
326
+ setShowCmd(false);
327
+ }, [setShowCmd]);
215
328
  // --- Keyboard ---
216
329
  useAppKeyboard({
217
- renderer, isStreamingRef, currentTurnStartRef, currentTurnMsgIdRef,
218
- revertPoint, revertedMessagesRef, runRedo, runRollbackFiles,
219
- dialogStep, msgControls, permissionRequest, dialogKeyHandler,
220
- showCmd, cmdFilter, cmdSelected, cmdFiltered,
221
- draftText, slashActive, slashFiltered, slashSelected,
222
- promptHistory, historyIdx, interruptKey,
223
- selectedAgent, thinkingEffort, provider,
224
- agentRef, securityRef, promptTextareaRef,
225
- setShowCmd, setCmdFilter, setCmdSelected,
226
- setDraftText, setSlashSelected, setHistoryIdx, setNavKey,
227
- setInterruptKey, setSelectedAgent, setMessages, setStatus,
228
- setThinkingEffort, updateMessage, updateLastAssistantMeta,
229
- execCmd, handleCycleVariant,
330
+ renderer,
331
+ isStreamingRef,
332
+ currentTurnStartRef,
333
+ currentTurnMsgIdRef,
334
+ revertPoint,
335
+ revertedMessagesRef,
336
+ runRedo,
337
+ runRollbackFiles,
338
+ dialogStep,
339
+ msgControls,
340
+ permissionRequest,
341
+ dialogKeyHandler,
342
+ showCmd,
343
+ cmdFilter,
344
+ cmdSelected,
345
+ cmdFiltered,
346
+ draftText,
347
+ slashActive,
348
+ slashFiltered,
349
+ slashSelected,
350
+ promptHistory,
351
+ historyIdx,
352
+ interruptKey,
353
+ selectedAgent,
354
+ thinkingEffort,
355
+ provider,
356
+ agentRef,
357
+ securityRef,
358
+ promptTextareaRef,
359
+ setShowCmd,
360
+ setCmdFilter,
361
+ setCmdSelected,
362
+ setDraftText,
363
+ setSlashSelected,
364
+ setHistoryIdx,
365
+ setNavKey,
366
+ setInterruptKey,
367
+ setSelectedAgent,
368
+ setMessages,
369
+ setStatus,
370
+ setThinkingEffort,
371
+ updateMessage,
372
+ updateLastAssistantMeta,
373
+ execCmd,
374
+ handleCycleVariant,
230
375
  });
231
376
  // --- JSX ---
232
- return (_jsxs("box", { flexDirection: "column", height: termHeight, backgroundColor: c.bg, children: [route === "home" ? (_jsxs("box", { flexDirection: "column", flexGrow: 1, children: [_jsx("box", { flexGrow: 1 }), _jsxs("box", { flexDirection: "column", alignItems: "center", flexShrink: 0, children: [_jsx("ascii-font", { text: "SPECTRA", font: "block", color: c.accent }), _jsx("box", { height: 1 }), _jsx(PromptBar, { isLoading: isLoading, spinnerFrame: spinnerFrame, inputKey: `h-${submitKey}-${navKey}`, placeholder: `Ask anything... "${PLACEHOLDERS[placeholderIdx]}"`, onSubmit: handleSubmit, hasModel: hasModel, agent: selectedAgent, model: selectedModel || "", provider: provider || "", thinkingEffort: thinkingEffort, initialValue: revertDraftRef.current || (historyIdx >= 0 ? promptHistory[historyIdx] : ""), width: Math.min(68, termWidth - 8), focused: !dialogStep && !showCmd && !msgControls && !permissionRequest, onTextChange: (t) => setDraftText(t), onGetTextarea: (r) => { promptTextareaRef.current = r; }, onPositionChange: setPromptPosition }), _jsx("box", { height: 1 }), _jsx("box", { flexDirection: "row", justifyContent: "flex-end", width: Math.min(68, termWidth - 8), children: _jsxs("box", { flexDirection: "row", gap: 2, children: [_jsxs("box", { flexDirection: "row", children: [_jsx("text", { fg: c.text, children: "tab" }), _jsx("text", { fg: c.dim, children: " agent" })] }), _jsxs("box", { flexDirection: "row", children: [_jsx("text", { fg: c.text, children: "ctrl+t" }), _jsx("text", { fg: c.dim, children: " effort" })] }), _jsxs("box", { flexDirection: "row", children: [_jsx("text", { fg: c.text, children: "ctrl+p" }), _jsx("text", { fg: c.dim, children: " commands" })] })] }) }), _jsx("box", { height: 1 }), _jsx("box", { flexDirection: "row", gap: 4, alignItems: "center", children: [
233
- { icon: "◈", label: `${sessionStore.current.list().length} sessions` },
234
- { icon: "", label: "3 agents" },
235
- { icon: "◆", label: "7 tools" },
236
- { icon: "⬢", label: `${mcpCount} MCP` },
237
- ].map((s) => (_jsxs("box", { flexDirection: "row", gap: 1, alignItems: "center", children: [_jsx("text", { fg: c.accent, children: s.icon }), _jsx("text", { fg: c.dim, children: s.label })] }, s.label))) }), _jsx(Tips, {})] }), _jsx("box", { flexGrow: 1 }), _jsxs("box", { flexDirection: "row", justifyContent: "space-between", paddingLeft: 2, paddingRight: 2, height: 1, marginBottom: 1, children: [_jsx("box", { flexDirection: "row", gap: 4, children: _jsx("text", { fg: c.dim, overflow: "hidden", wrapMode: "none", children: cwdLabel }) }), _jsx("text", { fg: c.dim, flexShrink: 0, children: "Spectra Code" })] })] }, `home-${homeKey}`)) : (_jsxs("box", { flexDirection: "column", height: termHeight, paddingLeft: 2, paddingRight: 2, children: [revertPoint && (_jsxs("box", { flexDirection: "column", alignItems: "center", paddingY: 1, children: [_jsxs("box", { flexDirection: "row", children: [_jsx("text", { fg: c.warn, children: "Messages reverted. " }), _jsx("text", { fg: c.accent, children: "Ctrl+Y" }), _jsx("text", { fg: c.dim, children: " to restore" })] }), _jsxs("box", { flexDirection: "row", children: [_jsx("text", { fg: c.dim, children: "Files unchanged. " }), _jsx("text", { fg: c.accent, children: "Ctrl+Shift+Y" }), _jsx("text", { fg: c.dim, children: " to rollback files" })] })] })), _jsx("box", { flexDirection: "column", flexGrow: 1, paddingBottom: 1, children: _jsx(ChatArea, { messages: messages, showThinking: showThinking, showToolCalls: showToolCalls, revertPoint: revertPoint, onMessageClick: (msg) => setMsgControls(msg) }) }), _jsxs("box", { flexShrink: 0, children: [_jsx(PromptBar, { isLoading: isLoading, spinnerFrame: spinnerFrame, inputKey: `c-${submitKey}-${navKey}`, placeholder: "Reply...", onSubmit: handleSubmit, hasModel: hasModel, agent: selectedAgent, model: selectedModel || "", provider: provider || "", thinkingEffort: thinkingEffort, initialValue: revertDraftRef.current || (historyIdx >= 0 ? promptHistory[historyIdx] : ""), elapsedMs: elapsedMs, tokenUsage: tokenUsage, width: termWidth - 4, focused: !dialogStep && !showCmd && !msgControls && !permissionRequest, onTextChange: (t) => setDraftText(t), onGetTextarea: (r) => { promptTextareaRef.current = r; }, onPositionChange: setPromptPosition }), _jsx("box", { height: 1 }), _jsxs("box", { flexDirection: "row", justifyContent: "space-between", alignItems: "center", height: 1, paddingLeft: 3, paddingRight: 1, children: [_jsxs("box", { flexDirection: "row", gap: 2, alignItems: "center", overflow: "hidden", children: [isLoading ? (_jsxs("box", { flexDirection: "row", gap: 2, alignItems: "center", children: [_jsxs("box", { flexDirection: "row", gap: 1, children: [_jsx("text", { fg: c.warn, children: SPINNER[spinnerFrame] }), _jsx("text", { fg: c.dim, children: "Streaming..." })] }), _jsxs("box", { flexDirection: "row", gap: 1, children: [_jsx("text", { fg: c.accent, children: "esc" }), _jsx("text", { fg: c.dim, children: "interrupt" })] })] })) : (_jsx("text", { fg: c.dim, children: "Ready" })), (tokenUsage.input + tokenUsage.output) > 0 && (() => {
238
- const used = tokenUsage.input + tokenUsage.output;
239
- const cw = lookupContextWindow(selectedModel || "", provider);
240
- const pct = cw ? Math.round((used / cw) * 100) : null;
241
- return (_jsxs("box", { flexDirection: "row", gap: 1, children: [_jsx("text", { fg: c.subtext, children: fmtCtx(used) }), pct !== null && _jsxs("text", { fg: pct > 80 ? c.warn : c.dim, children: ["(", pct, "%)"] })] }));
242
- })()] }), _jsxs("box", { flexDirection: "row", gap: 2, alignItems: "center", children: [_jsxs("box", { flexDirection: "row", children: [_jsx("text", { fg: c.text, children: "tab" }), _jsx("text", { fg: c.dim, children: " agent" })] }), _jsxs("box", { flexDirection: "row", children: [_jsx("text", { fg: c.text, children: "ctrl+t" }), _jsx("text", { fg: c.dim, children: " effort" })] }), _jsxs("box", { flexDirection: "row", children: [_jsx("text", { fg: c.text, children: "ctrl+p" }), _jsx("text", { fg: c.dim, children: " commands" })] })] })] })] })] })), showCmd && _jsx(CommandPalette, { filter: cmdFilter, selected: cmdSelected, items: cmdFiltered, termWidth: termWidth, termHeight: termHeight }), slashActive && slashFiltered.length > 0 && (_jsx(SlashAutocomplete, { query: slashHead(draftText)?.name || "", selected: slashSelected, items: slashFiltered, termWidth: termWidth, termHeight: termHeight, route: route, promptTop: promptPosition.top, promptLeft: promptPosition.left, promptWidth: promptPosition.width })), dialogStep?.type === "provider" && (_jsx(ProviderDialog, { termWidth: termWidth, termHeight: termHeight, keyHandlerRef: dialogKeyHandler, onModelSelected: (modelId, providerId) => {
377
+ return (_jsxs("box", { flexDirection: "column", height: termHeight, backgroundColor: c.bg, children: [route === 'home' ? (_jsxs("box", { flexDirection: "column", flexGrow: 1, children: [_jsx("box", { flexGrow: 1 }), _jsxs("box", { flexDirection: "column", alignItems: "center", flexShrink: 0, children: [_jsx("ascii-font", { text: "SPECTRA", font: "block", color: c.accent }), _jsx("box", { height: 1 }), _jsx(PromptBar, { isLoading: isLoading, spinnerFrame: spinnerFrame, inputKey: `h-${submitKey}-${navKey}`, placeholder: `Ask anything... "${PLACEHOLDERS[placeholderIdx]}"`, onSubmit: handleSubmit, hasModel: hasModel, agent: selectedAgent, model: selectedModel || '', provider: provider || '', thinkingEffort: thinkingEffort, initialValue: revertDraftRef.current || (historyIdx >= 0 ? promptHistory[historyIdx] : ''), width: Math.min(68, termWidth - 8), focused: !dialogStep && !showCmd && !msgControls && !permissionRequest, onTextChange: (t) => setDraftText(t), onGetTextarea: (r) => {
378
+ promptTextareaRef.current = r;
379
+ }, onPositionChange: setPromptPosition }), _jsx("box", { height: 1 }), _jsx("box", { flexDirection: "row", justifyContent: "flex-end", width: Math.min(68, termWidth - 8), children: _jsxs("box", { flexDirection: "row", gap: 2, children: [_jsxs("box", { flexDirection: "row", children: [_jsx("text", { fg: c.text, children: "tab" }), _jsx("text", { fg: c.dim, children: " agent" })] }), _jsxs("box", { flexDirection: "row", children: [_jsx("text", { fg: c.text, children: "ctrl+t" }), _jsx("text", { fg: c.dim, children: " effort" })] }), _jsxs("box", { flexDirection: "row", children: [_jsx("text", { fg: c.text, children: "ctrl+p" }), _jsx("text", { fg: c.dim, children: " commands" })] })] }) }), _jsx("box", { height: 1 }), _jsx("box", { flexDirection: "row", gap: 4, alignItems: "center", children: [
380
+ { icon: '◈', label: `${sessionStore.current.list().length} sessions` },
381
+ { icon: '◉', label: '3 agents' },
382
+ { icon: '◆', label: '7 tools' },
383
+ { icon: '⬢', label: `${mcpCount} MCP` },
384
+ ].map((s) => (_jsxs("box", { flexDirection: "row", gap: 1, alignItems: "center", children: [_jsx("text", { fg: c.accent, children: s.icon }), _jsx("text", { fg: c.dim, children: s.label })] }, s.label))) }), _jsx(Tips, {})] }), _jsx("box", { flexGrow: 1 }), _jsxs("box", { flexDirection: "row", justifyContent: "space-between", paddingLeft: 2, paddingRight: 2, height: 1, marginBottom: 1, children: [_jsx("box", { flexDirection: "row", gap: 4, children: _jsx("text", { fg: c.dim, overflow: "hidden", wrapMode: "none", children: cwdLabel }) }), _jsx("text", { fg: c.dim, flexShrink: 0, children: "Spectra Code" })] })] }, `home-${homeKey}`)) : (_jsxs("box", { flexDirection: "column", height: termHeight, paddingLeft: 2, paddingRight: 2, children: [revertPoint && (_jsxs("box", { flexDirection: "column", alignItems: "center", paddingY: 1, children: [_jsxs("box", { flexDirection: "row", children: [_jsx("text", { fg: c.warn, children: "Messages reverted. " }), _jsx("text", { fg: c.accent, children: "Ctrl+Y" }), _jsx("text", { fg: c.dim, children: " to restore" })] }), _jsxs("box", { flexDirection: "row", children: [_jsx("text", { fg: c.dim, children: "Files unchanged. " }), _jsx("text", { fg: c.accent, children: "Ctrl+Shift+Y" }), _jsx("text", { fg: c.dim, children: " to rollback files" })] })] })), _jsx("box", { flexDirection: "column", flexGrow: 1, paddingBottom: 1, children: _jsx(ChatArea, { messages: messages, showThinking: showThinking, showToolCalls: showToolCalls, revertPoint: revertPoint, onMessageClick: (msg) => setMsgControls(msg) }) }), _jsxs("box", { flexShrink: 0, children: [_jsx(PromptBar, { isLoading: isLoading, spinnerFrame: spinnerFrame, inputKey: `c-${submitKey}-${navKey}`, placeholder: "Reply...", onSubmit: handleSubmit, hasModel: hasModel, agent: selectedAgent, model: selectedModel || '', provider: provider || '', thinkingEffort: thinkingEffort, initialValue: revertDraftRef.current || (historyIdx >= 0 ? promptHistory[historyIdx] : ''), elapsedMs: elapsedMs, tokenUsage: tokenUsage, width: termWidth - 4, focused: !dialogStep && !showCmd && !msgControls && !permissionRequest, onTextChange: (t) => setDraftText(t), onGetTextarea: (r) => {
385
+ promptTextareaRef.current = r;
386
+ }, onPositionChange: setPromptPosition }), _jsx("box", { height: 1 }), _jsxs("box", { flexDirection: "row", justifyContent: "space-between", alignItems: "center", height: 1, paddingLeft: 3, paddingRight: 1, children: [_jsxs("box", { flexDirection: "row", gap: 2, alignItems: "center", overflow: "hidden", children: [isLoading ? (_jsxs("box", { flexDirection: "row", gap: 2, alignItems: "center", children: [_jsxs("box", { flexDirection: "row", gap: 1, children: [_jsx("text", { fg: c.warn, children: SPINNER[spinnerFrame] }), _jsx("text", { fg: c.dim, children: "Streaming..." })] }), _jsxs("box", { flexDirection: "row", gap: 1, children: [_jsx("text", { fg: c.accent, children: "esc" }), _jsx("text", { fg: c.dim, children: "interrupt" })] })] })) : (_jsx("text", { fg: c.dim, children: "Ready" })), tokenUsage.input + tokenUsage.output > 0 &&
387
+ (() => {
388
+ const used = tokenUsage.input + tokenUsage.output;
389
+ const cw = lookupContextWindow(selectedModel || '', provider);
390
+ const pct = cw ? Math.round((used / cw) * 100) : null;
391
+ return (_jsxs("box", { flexDirection: "row", gap: 1, children: [_jsx("text", { fg: c.subtext, children: fmtCtx(used) }), pct !== null && _jsxs("text", { fg: pct > 80 ? c.warn : c.dim, children: ["(", pct, "%)"] })] }));
392
+ })()] }), _jsxs("box", { flexDirection: "row", gap: 2, alignItems: "center", children: [_jsxs("box", { flexDirection: "row", children: [_jsx("text", { fg: c.text, children: "tab" }), _jsx("text", { fg: c.dim, children: " agent" })] }), _jsxs("box", { flexDirection: "row", children: [_jsx("text", { fg: c.text, children: "ctrl+t" }), _jsx("text", { fg: c.dim, children: " effort" })] }), _jsxs("box", { flexDirection: "row", children: [_jsx("text", { fg: c.text, children: "ctrl+p" }), _jsx("text", { fg: c.dim, children: " commands" })] })] })] })] })] })), showCmd && (_jsx(CommandPalette, { filter: cmdFilter, selected: cmdSelected, items: cmdFiltered, termWidth: termWidth, termHeight: termHeight })), slashActive && slashFiltered.length > 0 && (_jsx(SlashAutocomplete, { query: slashHead(draftText)?.name || '', selected: slashSelected, items: slashFiltered, termWidth: termWidth, termHeight: termHeight, route: route, promptTop: promptPosition.top, promptLeft: promptPosition.left, promptWidth: promptPosition.width })), dialogStep?.type === 'provider' && (_jsx(ProviderDialog, { termWidth: termWidth, termHeight: termHeight, keyHandlerRef: dialogKeyHandler, onModelSelected: (modelId, providerId) => {
243
393
  resetAgentForModelSwitch();
244
394
  setSelectedModel(modelId);
245
395
  setSelectedProvider(providerId);
246
396
  setDialogStep(null);
247
397
  saveModelConfig(modelId, providerId);
248
- showToast(`Model set`, "success");
249
- }, onClose: () => setDialogStep(null) })), dialogStep?.type === "session-list" && (_jsx(SessionList, { store: sessionStore.current, termWidth: termWidth, termHeight: termHeight, mode: dialogStep.mode || "load", onLoad: (data) => {
398
+ showToast(`Model set`, 'success');
399
+ }, onClose: () => setDialogStep(null) })), dialogStep?.type === 'session-list' && (_jsx(SessionList, { store: sessionStore.current, termWidth: termWidth, termHeight: termHeight, mode: dialogStep.mode || 'load', onLoad: (data) => {
250
400
  const { messages: loadedMsgs, tokenUsage: tu } = sdkMessagesToChatMessages(data);
251
401
  setTokenUsage(tu);
252
402
  setMessages(() => loadedMsgs);
253
403
  sessionId.current = data.id;
254
404
  setSelectedModel(data.model);
255
- setSelectedProvider(data.provider || data.model.split("/")[0]);
405
+ setSelectedProvider(data.provider || data.model.split('/')[0]);
256
406
  setSelectedAgent(data.agent);
257
407
  setThinkingEffort(data.thinkingEffort || undefined);
258
- setRoute("chat");
408
+ setRoute('chat');
259
409
  setDialogStep(null);
260
410
  loadedSessionMessages.current = data.messages;
261
411
  if (agentRef.current) {
@@ -264,47 +414,80 @@ export function App({ renderer }) {
264
414
  }
265
415
  securityRef.current?.getReadTracker().reset();
266
416
  securityRef.current?.getDoomLoop().reset();
267
- showToast(`Loaded: ${data.title.slice(0, 40)}`, "info");
417
+ showToast(`Loaded: ${data.title.slice(0, 40)}`, 'info');
268
418
  }, onDelete: (id) => {
269
419
  sessionStore.current.delete(id);
270
420
  if (sessionId.current === id) {
271
421
  sessionId.current = null;
272
422
  setMessages([]);
273
- setRoute("home");
423
+ setRoute('home');
274
424
  setHomeKey((k) => k + 1);
275
425
  }
276
- showToast("Session deleted", "success");
426
+ showToast('Session deleted', 'success');
277
427
  }, onRename: (id, title) => {
278
428
  sessionStore.current.rename(id, title);
279
- showToast("Session renamed", "success");
280
- }, onClose: () => setDialogStep(null), registerHandler: (fn) => { dialogKeyHandler.current = fn; } })), dialogStep?.type === "switch-model" && (_jsx(ModelSwitcher, { providerId: provider || "", termWidth: termWidth, termHeight: termHeight, onModelSelected: (modelId, providerId) => {
429
+ showToast('Session renamed', 'success');
430
+ }, onClose: () => setDialogStep(null), registerHandler: (fn) => {
431
+ dialogKeyHandler.current = fn;
432
+ } })), dialogStep?.type === 'switch-model' && (_jsx(ModelSwitcher, { providerId: provider || '', termWidth: termWidth, termHeight: termHeight, onModelSelected: (modelId, providerId) => {
281
433
  resetAgentForModelSwitch();
282
434
  setSelectedModel(modelId);
283
435
  setSelectedProvider(providerId);
284
436
  setDialogStep(null);
285
437
  saveModelConfig(modelId, providerId);
286
- showToast(`Switched model`, "info");
287
- }, onClose: () => setDialogStep(null), registerHandler: (fn) => { dialogKeyHandler.current = fn; } })), dialogStep?.type === "manage-providers" && (_jsx(ManageProvidersDialog, { termWidth: termWidth, termHeight: termHeight, providers: customProviders, onProvidersChange: (updated) => {
438
+ showToast(`Switched model`, 'info');
439
+ }, onClose: () => setDialogStep(null), registerHandler: (fn) => {
440
+ dialogKeyHandler.current = fn;
441
+ } })), dialogStep?.type === 'manage-providers' && (_jsx(ManageProvidersDialog, { termWidth: termWidth, termHeight: termHeight, providers: customProviders, onProvidersChange: (updated) => {
288
442
  setCustomProviders(updated);
289
443
  resetAgentForModelSwitch();
290
- showToast("Providers updated", "success");
291
- }, onClose: () => setDialogStep(null), registerHandler: (fn) => { dialogKeyHandler.current = fn; } })), dialogStep?.type === "doctor" && dialogStep.result && (_jsx(DoctorDialog, { result: dialogStep.result, termWidth: termWidth, termHeight: termHeight, onClose: () => setDialogStep(null), registerHandler: (fn) => { dialogKeyHandler.current = fn; } })), dialogStep?.type === "about" && (_jsx(AboutDialog, { termWidth: termWidth, termHeight: termHeight, onClose: () => setDialogStep(null), registerHandler: (fn) => { dialogKeyHandler.current = fn; } })), dialogStep?.type === "switch-agent" && (_jsx(AgentSwitcher, { currentAgent: selectedAgent, termWidth: termWidth, termHeight: termHeight, onAgentSelected: (agent) => {
444
+ showToast('Providers updated', 'success');
445
+ }, onClose: () => setDialogStep(null), registerHandler: (fn) => {
446
+ dialogKeyHandler.current = fn;
447
+ } })), dialogStep?.type === 'doctor' && dialogStep.result && (_jsx(DoctorDialog, { result: dialogStep.result, termWidth: termWidth, termHeight: termHeight, onClose: () => setDialogStep(null), registerHandler: (fn) => {
448
+ dialogKeyHandler.current = fn;
449
+ } })), dialogStep?.type === 'about' && (_jsx(AboutDialog, { termWidth: termWidth, termHeight: termHeight, onClose: () => setDialogStep(null), registerHandler: (fn) => {
450
+ dialogKeyHandler.current = fn;
451
+ } })), updateVersion && (_jsx(UpdateDialog, { newVersion: updateVersion, currentVersion: VERSION, termWidth: termWidth, termHeight: termHeight, onClose: () => setUpdateVersion(null), onInstall: () => {
452
+ try {
453
+ clipboard.writeSync('bun update -g @mohanscodex/spectra-code');
454
+ showToast('Command copied to clipboard', 'success');
455
+ }
456
+ catch { }
457
+ setUpdateVersion(null);
458
+ }, registerHandler: (fn) => {
459
+ dialogKeyHandler.current = fn;
460
+ } })), dialogStep?.type === 'switch-agent' && (_jsx(AgentSwitcher, { currentAgent: selectedAgent, termWidth: termWidth, termHeight: termHeight, onAgentSelected: (agent) => {
292
461
  setSelectedAgent(agent);
293
462
  resetAgentForModelSwitch();
294
463
  setDialogStep(null);
295
- showToast(`Switched to ${titlecase(agent)} agent`, "info");
296
- }, onClose: () => setDialogStep(null), registerHandler: (fn) => { dialogKeyHandler.current = fn; } })), dialogStep?.type === "thinking-effort" && (_jsx(ThinkingEffortDialog, { provider: provider, currentEffort: thinkingEffort, termWidth: termWidth, termHeight: termHeight, onEffortSelected: (effort) => {
464
+ showToast(`Switched to ${titlecase(agent)} agent`, 'info');
465
+ }, onClose: () => setDialogStep(null), registerHandler: (fn) => {
466
+ dialogKeyHandler.current = fn;
467
+ } })), dialogStep?.type === 'thinking-effort' && (_jsx(ThinkingEffortDialog, { provider: provider, currentEffort: thinkingEffort, termWidth: termWidth, termHeight: termHeight, onEffortSelected: (effort) => {
297
468
  setThinkingEffort(effort);
298
469
  resetAgentForModelSwitch();
299
470
  setDialogStep(null);
300
- showToast(effort === "none" ? "Thinking: off" : `Thinking: ${effort}`, "info");
301
- }, onClose: () => setDialogStep(null), registerHandler: (fn) => { dialogKeyHandler.current = fn; } })), dialogStep?.type === "toggle-mcp" && (_jsx(McpToggleDialog, { termWidth: termWidth, termHeight: termHeight, onClose: () => setDialogStep(null), registerHandler: (fn) => { dialogKeyHandler.current = fn; } })), dialogStep?.type === "debug" && (_jsx(DebugDialog, { termWidth: termWidth, termHeight: termHeight, selectedModel: selectedModel, provider: provider, selectedAgent: selectedAgent, thinkingEffort: thinkingEffort, sessionStore: sessionStore.current, mcpCount: mcpCount, onClose: () => setDialogStep(null), registerHandler: (fn) => { dialogKeyHandler.current = fn; } })), msgControls && sessionId.current && (_jsx(MessageControls, { message: msgControls, sessionId: sessionId.current, messages: messages, termWidth: termWidth, termHeight: termHeight, revertPoint: revertPoint, onRevert: (msgId) => { runRevert(messages, msgId); setMsgControls(null); }, onRedo: () => { runRedo(); setMsgControls(null); }, onFork: (msgId) => {
471
+ showToast(effort === 'none' ? 'Thinking: off' : `Thinking: ${effort}`, 'info');
472
+ }, onClose: () => setDialogStep(null), registerHandler: (fn) => {
473
+ dialogKeyHandler.current = fn;
474
+ } })), dialogStep?.type === 'toggle-mcp' && (_jsx(McpToggleDialog, { termWidth: termWidth, termHeight: termHeight, onClose: () => setDialogStep(null), registerHandler: (fn) => {
475
+ dialogKeyHandler.current = fn;
476
+ } })), dialogStep?.type === 'debug' && (_jsx(DebugDialog, { termWidth: termWidth, termHeight: termHeight, selectedModel: selectedModel, provider: provider, selectedAgent: selectedAgent, thinkingEffort: thinkingEffort, sessionStore: sessionStore.current, mcpCount: mcpCount, onClose: () => setDialogStep(null), registerHandler: (fn) => {
477
+ dialogKeyHandler.current = fn;
478
+ } })), msgControls && sessionId.current && (_jsx(MessageControls, { message: msgControls, sessionId: sessionId.current, messages: messages, termWidth: termWidth, termHeight: termHeight, revertPoint: revertPoint, onRevert: (msgId) => {
479
+ runRevert(messages, msgId);
480
+ setMsgControls(null);
481
+ }, onRedo: () => {
482
+ runRedo();
483
+ setMsgControls(null);
484
+ }, onFork: (msgId) => {
302
485
  const forked = sessionStore.current.fork(sessionId.current);
303
486
  if (forked) {
304
- const msgIdx = messages.findIndex(m => m.id === msgId);
487
+ const msgIdx = messages.findIndex((m) => m.id === msgId);
305
488
  if (msgIdx >= 0) {
306
489
  forked.messages = forked.messages.slice(0, msgIdx + 1);
307
- forked.title = `${forked.title.split(" (fork)")[0]} (fork)`;
490
+ forked.title = `${forked.title.split(' (fork)')[0]} (fork)`;
308
491
  sessionStore.current.save(forked);
309
492
  }
310
493
  const data = sessionStore.current.get(forked.id);
@@ -313,10 +496,20 @@ export function App({ renderer }) {
313
496
  setMessages(loadedMsgs);
314
497
  sessionId.current = forked.id;
315
498
  loadedSessionMessages.current = data.messages;
316
- showToast("Session forked", "success");
499
+ showToast('Session forked', 'success');
317
500
  }
318
501
  }
319
502
  setMsgControls(null);
320
- }, onClose: () => setMsgControls(null), registerHandler: (fn) => { dialogKeyHandler.current = fn; } })), permissionRequest && (_jsx(PermissionDialog, { request: permissionRequest, termWidth: termWidth, termHeight: termHeight, onAllow: (id) => { resolvePermission(id, { action: "once" }); }, onAllowAlways: (id) => { resolvePermission(id, { action: "always" }); }, onDeny: (id) => { resolvePermission(id, { action: "deny" }); }, onClose: () => { resolvePermission(permissionRequest.id, { action: "deny" }); } })), _jsx(ToastContainer, {})] }));
503
+ }, onClose: () => setMsgControls(null), registerHandler: (fn) => {
504
+ dialogKeyHandler.current = fn;
505
+ } })), permissionRequest && (_jsx(PermissionDialog, { request: permissionRequest, termWidth: termWidth, termHeight: termHeight, onAllow: (id) => {
506
+ resolvePermission(id, { action: 'once' });
507
+ }, onAllowAlways: (id) => {
508
+ resolvePermission(id, { action: 'always' });
509
+ }, onDeny: (id) => {
510
+ resolvePermission(id, { action: 'deny' });
511
+ }, onClose: () => {
512
+ resolvePermission(permissionRequest.id, { action: 'deny' });
513
+ } })), _jsx(ToastContainer, {})] }));
321
514
  }
322
515
  //# sourceMappingURL=app.js.map