@aexol/spectral 0.7.7 → 0.8.0

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 (221) hide show
  1. package/dist/agent/agents.js +4 -4
  2. package/dist/agent/index.js +24 -148
  3. package/dist/cli.js +25 -220
  4. package/dist/commands/serve.js +1 -1
  5. package/dist/extensions/spectral-vision-fallback.js +225 -0
  6. package/dist/mcp/agent-dir.js +1 -1
  7. package/dist/mcp/config.js +3 -3
  8. package/dist/mcp/sampling-handler.js +1 -1
  9. package/dist/mcp/server-manager.js +5 -1
  10. package/dist/memory/commands/status.js +6 -6
  11. package/dist/memory/commands/view.js +16 -14
  12. package/dist/memory/compaction.js +33 -5
  13. package/dist/memory/config.js +3 -3
  14. package/dist/memory/debug-log.js +1 -1
  15. package/dist/memory/observer.js +2 -2
  16. package/dist/memory/prompts.js +5 -5
  17. package/dist/memory/tokens.js +1 -1
  18. package/dist/memory/tools/read-project-observations.js +2 -2
  19. package/dist/memory/tools/recall-observation.js +4 -4
  20. package/dist/relay/auto-research.js +23 -23
  21. package/dist/relay/dispatcher.js +28 -2
  22. package/dist/relay/models-fetch.js +15 -3
  23. package/dist/{pi → sdk}/coding-agent/cli/args.js +4 -4
  24. package/dist/{pi → sdk}/coding-agent/config.js +9 -20
  25. package/dist/{pi → sdk}/coding-agent/core/agent-session.js +5 -17
  26. package/dist/{pi → sdk}/coding-agent/core/compaction/compaction.js +161 -5
  27. package/dist/{pi → sdk}/coding-agent/core/extensions/loader.js +0 -6
  28. package/dist/{pi → sdk}/coding-agent/core/extensions/runner.js +7 -1
  29. package/dist/{pi → sdk}/coding-agent/core/keybindings.js +129 -2
  30. package/dist/{pi → sdk}/coding-agent/core/model-registry.js +11 -4
  31. package/dist/{pi → sdk}/coding-agent/core/package-manager.js +5 -5
  32. package/dist/{pi → sdk}/coding-agent/core/sdk.js +1 -1
  33. package/dist/{pi → sdk}/coding-agent/core/session-manager.js +4 -4
  34. package/dist/{pi → sdk}/coding-agent/core/settings-manager.js +20 -0
  35. package/dist/{pi → sdk}/coding-agent/core/telemetry.js +1 -1
  36. package/dist/{pi → sdk}/coding-agent/core/tools/bash.js +17 -63
  37. package/dist/{pi → sdk}/coding-agent/core/tools/edit.js +4 -141
  38. package/dist/{pi → sdk}/coding-agent/core/tools/find.js +0 -11
  39. package/dist/{pi → sdk}/coding-agent/core/tools/grep.js +0 -11
  40. package/dist/{pi → sdk}/coding-agent/core/tools/ls.js +0 -11
  41. package/dist/{pi → sdk}/coding-agent/core/tools/read.js +0 -12
  42. package/dist/{pi → sdk}/coding-agent/core/tools/render-utils.js +1 -14
  43. package/dist/{pi → sdk}/coding-agent/core/tools/write.js +2 -97
  44. package/dist/{pi → sdk}/coding-agent/migrations.js +3 -3
  45. package/dist/{pi → sdk}/coding-agent/modes/interactive/components/keybinding-hints.js +1 -1
  46. package/dist/sdk/coding-agent/modes/interactive/components/visual-truncate.js +26 -0
  47. package/dist/{pi → sdk}/coding-agent/modes/interactive/theme/theme.js +1 -2
  48. package/dist/{pi → sdk}/coding-agent/utils/tools-manager.js +1 -1
  49. package/dist/{pi → sdk}/coding-agent/utils/version-check.js +2 -2
  50. package/dist/{pi → sdk}/coding-agent/utils/windows-self-update.js +1 -1
  51. package/dist/server/{pi-bridge.js → agent-bridge.js} +158 -89
  52. package/dist/server/handlers/sessions.js +21 -0
  53. package/dist/server/session-stream.js +12 -6
  54. package/package.json +6 -3
  55. package/dist/pi/coding-agent/core/export-html/ansi-to-html.js +0 -248
  56. package/dist/pi/coding-agent/core/export-html/index.js +0 -225
  57. package/dist/pi/coding-agent/core/export-html/tool-renderer.js +0 -107
  58. package/dist/pi/coding-agent/modes/interactive/components/visual-truncate.js +0 -32
  59. package/dist/pi/tui/autocomplete.js +0 -631
  60. package/dist/pi/tui/components/box.js +0 -103
  61. package/dist/pi/tui/components/cancellable-loader.js +0 -34
  62. package/dist/pi/tui/components/editor.js +0 -1915
  63. package/dist/pi/tui/components/image.js +0 -88
  64. package/dist/pi/tui/components/input.js +0 -425
  65. package/dist/pi/tui/components/loader.js +0 -68
  66. package/dist/pi/tui/components/markdown.js +0 -633
  67. package/dist/pi/tui/components/select-list.js +0 -158
  68. package/dist/pi/tui/components/settings-list.js +0 -184
  69. package/dist/pi/tui/components/spacer.js +0 -22
  70. package/dist/pi/tui/components/text.js +0 -88
  71. package/dist/pi/tui/components/truncated-text.js +0 -50
  72. package/dist/pi/tui/editor-component.js +0 -1
  73. package/dist/pi/tui/fuzzy.js +0 -109
  74. package/dist/pi/tui/index.js +0 -31
  75. package/dist/pi/tui/keybindings.js +0 -173
  76. package/dist/pi/tui/keys.js +0 -1172
  77. package/dist/pi/tui/kill-ring.js +0 -43
  78. package/dist/pi/tui/stdin-buffer.js +0 -360
  79. package/dist/pi/tui/terminal-image.js +0 -335
  80. package/dist/pi/tui/terminal.js +0 -324
  81. package/dist/pi/tui/tui.js +0 -1076
  82. package/dist/pi/tui/undo-stack.js +0 -24
  83. package/dist/pi/tui/utils.js +0 -1016
  84. /package/dist/{pi → sdk}/agent-core/agent-loop.js +0 -0
  85. /package/dist/{pi → sdk}/agent-core/agent.js +0 -0
  86. /package/dist/{pi → sdk}/agent-core/harness/agent-harness.js +0 -0
  87. /package/dist/{pi → sdk}/agent-core/harness/compaction/branch-summarization.js +0 -0
  88. /package/dist/{pi → sdk}/agent-core/harness/compaction/compaction.js +0 -0
  89. /package/dist/{pi → sdk}/agent-core/harness/compaction/utils.js +0 -0
  90. /package/dist/{pi → sdk}/agent-core/harness/env/nodejs.js +0 -0
  91. /package/dist/{pi → sdk}/agent-core/harness/messages.js +0 -0
  92. /package/dist/{pi → sdk}/agent-core/harness/prompt-templates.js +0 -0
  93. /package/dist/{pi → sdk}/agent-core/harness/session/jsonl-repo.js +0 -0
  94. /package/dist/{pi → sdk}/agent-core/harness/session/jsonl-storage.js +0 -0
  95. /package/dist/{pi → sdk}/agent-core/harness/session/memory-repo.js +0 -0
  96. /package/dist/{pi → sdk}/agent-core/harness/session/memory-storage.js +0 -0
  97. /package/dist/{pi → sdk}/agent-core/harness/session/repo-utils.js +0 -0
  98. /package/dist/{pi → sdk}/agent-core/harness/session/session.js +0 -0
  99. /package/dist/{pi → sdk}/agent-core/harness/session/uuid.js +0 -0
  100. /package/dist/{pi → sdk}/agent-core/harness/skills.js +0 -0
  101. /package/dist/{pi → sdk}/agent-core/harness/system-prompt.js +0 -0
  102. /package/dist/{pi → sdk}/agent-core/harness/types.js +0 -0
  103. /package/dist/{pi → sdk}/agent-core/harness/utils/shell-output.js +0 -0
  104. /package/dist/{pi → sdk}/agent-core/harness/utils/truncate.js +0 -0
  105. /package/dist/{pi → sdk}/agent-core/index.js +0 -0
  106. /package/dist/{pi → sdk}/agent-core/node.js +0 -0
  107. /package/dist/{pi → sdk}/agent-core/proxy.js +0 -0
  108. /package/dist/{pi → sdk}/agent-core/types.js +0 -0
  109. /package/dist/{pi → sdk}/ai/api-registry.js +0 -0
  110. /package/dist/{pi → sdk}/ai/cli.js +0 -0
  111. /package/dist/{pi → sdk}/ai/env-api-keys.js +0 -0
  112. /package/dist/{pi → sdk}/ai/image-models.generated.js +0 -0
  113. /package/dist/{pi → sdk}/ai/image-models.js +0 -0
  114. /package/dist/{pi → sdk}/ai/images-api-registry.js +0 -0
  115. /package/dist/{pi → sdk}/ai/images.js +0 -0
  116. /package/dist/{pi → sdk}/ai/index.js +0 -0
  117. /package/dist/{pi → sdk}/ai/models.generated.js +0 -0
  118. /package/dist/{pi → sdk}/ai/models.js +0 -0
  119. /package/dist/{pi → sdk}/ai/oauth.js +0 -0
  120. /package/dist/{pi → sdk}/ai/providers/anthropic.js +0 -0
  121. /package/dist/{pi → sdk}/ai/providers/faux.js +0 -0
  122. /package/dist/{pi → sdk}/ai/providers/github-copilot-headers.js +0 -0
  123. /package/dist/{pi → sdk}/ai/providers/openai-completions.js +0 -0
  124. /package/dist/{pi → sdk}/ai/providers/openai-prompt-cache.js +0 -0
  125. /package/dist/{pi → sdk}/ai/providers/register-builtins.js +0 -0
  126. /package/dist/{pi → sdk}/ai/providers/simple-options.js +0 -0
  127. /package/dist/{pi → sdk}/ai/providers/transform-messages.js +0 -0
  128. /package/dist/{pi → sdk}/ai/session-resources.js +0 -0
  129. /package/dist/{pi → sdk}/ai/stream.js +0 -0
  130. /package/dist/{pi → sdk}/ai/types.js +0 -0
  131. /package/dist/{pi → sdk}/ai/utils/diagnostics.js +0 -0
  132. /package/dist/{pi → sdk}/ai/utils/event-stream.js +0 -0
  133. /package/dist/{pi → sdk}/ai/utils/hash.js +0 -0
  134. /package/dist/{pi → sdk}/ai/utils/headers.js +0 -0
  135. /package/dist/{pi → sdk}/ai/utils/json-parse.js +0 -0
  136. /package/dist/{pi → sdk}/ai/utils/node-http-proxy.js +0 -0
  137. /package/dist/{pi → sdk}/ai/utils/oauth/anthropic.js +0 -0
  138. /package/dist/{pi → sdk}/ai/utils/oauth/device-code.js +0 -0
  139. /package/dist/{pi → sdk}/ai/utils/oauth/github-copilot.js +0 -0
  140. /package/dist/{pi → sdk}/ai/utils/oauth/index.js +0 -0
  141. /package/dist/{pi → sdk}/ai/utils/oauth/oauth-page.js +0 -0
  142. /package/dist/{pi → sdk}/ai/utils/oauth/openai-codex.js +0 -0
  143. /package/dist/{pi → sdk}/ai/utils/oauth/pkce.js +0 -0
  144. /package/dist/{pi → sdk}/ai/utils/oauth/types.js +0 -0
  145. /package/dist/{pi → sdk}/ai/utils/overflow.js +0 -0
  146. /package/dist/{pi → sdk}/ai/utils/sanitize-unicode.js +0 -0
  147. /package/dist/{pi → sdk}/ai/utils/typebox-helpers.js +0 -0
  148. /package/dist/{pi → sdk}/ai/utils/validation.js +0 -0
  149. /package/dist/{pi → sdk}/coding-agent/bun/cli.js +0 -0
  150. /package/dist/{pi → sdk}/coding-agent/bun/restore-sandbox-env.js +0 -0
  151. /package/dist/{pi → sdk}/coding-agent/cli/file-processor.js +0 -0
  152. /package/dist/{pi → sdk}/coding-agent/cli/initial-message.js +0 -0
  153. /package/dist/{pi → sdk}/coding-agent/cli.js +0 -0
  154. /package/dist/{pi → sdk}/coding-agent/core/agent-session-runtime.js +0 -0
  155. /package/dist/{pi → sdk}/coding-agent/core/agent-session-services.js +0 -0
  156. /package/dist/{pi → sdk}/coding-agent/core/auth-guidance.js +0 -0
  157. /package/dist/{pi → sdk}/coding-agent/core/auth-storage.js +0 -0
  158. /package/dist/{pi → sdk}/coding-agent/core/bash-executor.js +0 -0
  159. /package/dist/{pi → sdk}/coding-agent/core/compaction/branch-summarization.js +0 -0
  160. /package/dist/{pi → sdk}/coding-agent/core/compaction/index.js +0 -0
  161. /package/dist/{pi → sdk}/coding-agent/core/compaction/utils.js +0 -0
  162. /package/dist/{pi → sdk}/coding-agent/core/defaults.js +0 -0
  163. /package/dist/{pi → sdk}/coding-agent/core/diagnostics.js +0 -0
  164. /package/dist/{pi → sdk}/coding-agent/core/event-bus.js +0 -0
  165. /package/dist/{pi → sdk}/coding-agent/core/exec.js +0 -0
  166. /package/dist/{pi → sdk}/coding-agent/core/extensions/index.js +0 -0
  167. /package/dist/{pi → sdk}/coding-agent/core/extensions/types.js +0 -0
  168. /package/dist/{pi → sdk}/coding-agent/core/extensions/wrapper.js +0 -0
  169. /package/dist/{pi → sdk}/coding-agent/core/footer-data-provider.js +0 -0
  170. /package/dist/{pi → sdk}/coding-agent/core/http-dispatcher.js +0 -0
  171. /package/dist/{pi → sdk}/coding-agent/core/index.js +0 -0
  172. /package/dist/{pi → sdk}/coding-agent/core/messages.js +0 -0
  173. /package/dist/{pi → sdk}/coding-agent/core/model-resolver.js +0 -0
  174. /package/dist/{pi → sdk}/coding-agent/core/output-guard.js +0 -0
  175. /package/dist/{pi → sdk}/coding-agent/core/prompt-templates.js +0 -0
  176. /package/dist/{pi → sdk}/coding-agent/core/provider-display-names.js +0 -0
  177. /package/dist/{pi → sdk}/coding-agent/core/resolve-config-value.js +0 -0
  178. /package/dist/{pi → sdk}/coding-agent/core/resource-loader.js +0 -0
  179. /package/dist/{pi → sdk}/coding-agent/core/session-cwd.js +0 -0
  180. /package/dist/{pi → sdk}/coding-agent/core/skills.js +0 -0
  181. /package/dist/{pi → sdk}/coding-agent/core/slash-commands.js +0 -0
  182. /package/dist/{pi → sdk}/coding-agent/core/source-info.js +0 -0
  183. /package/dist/{pi → sdk}/coding-agent/core/system-prompt.js +0 -0
  184. /package/dist/{pi → sdk}/coding-agent/core/timings.js +0 -0
  185. /package/dist/{pi → sdk}/coding-agent/core/tools/edit-diff.js +0 -0
  186. /package/dist/{pi → sdk}/coding-agent/core/tools/file-mutation-queue.js +0 -0
  187. /package/dist/{pi → sdk}/coding-agent/core/tools/index.js +0 -0
  188. /package/dist/{pi → sdk}/coding-agent/core/tools/output-accumulator.js +0 -0
  189. /package/dist/{pi → sdk}/coding-agent/core/tools/path-utils.js +0 -0
  190. /package/dist/{pi → sdk}/coding-agent/core/tools/tool-definition-wrapper.js +0 -0
  191. /package/dist/{pi → sdk}/coding-agent/core/tools/truncate.js +0 -0
  192. /package/dist/{pi → sdk}/coding-agent/index.js +0 -0
  193. /package/dist/{pi → sdk}/coding-agent/main.js +0 -0
  194. /package/dist/{pi → sdk}/coding-agent/modes/index.js +0 -0
  195. /package/dist/{pi → sdk}/coding-agent/modes/interactive/components/diff.js +0 -0
  196. /package/dist/{pi → sdk}/coding-agent/modes/interactive/interactive-mode.js +0 -0
  197. /package/dist/{pi → sdk}/coding-agent/modes/print-mode.js +0 -0
  198. /package/dist/{pi → sdk}/coding-agent/modes/rpc/jsonl.js +0 -0
  199. /package/dist/{pi → sdk}/coding-agent/modes/rpc/rpc-client.js +0 -0
  200. /package/dist/{pi → sdk}/coding-agent/modes/rpc/rpc-mode.js +0 -0
  201. /package/dist/{pi → sdk}/coding-agent/modes/rpc/rpc-types.js +0 -0
  202. /package/dist/{pi → sdk}/coding-agent/utils/ansi.js +0 -0
  203. /package/dist/{pi → sdk}/coding-agent/utils/changelog.js +0 -0
  204. /package/dist/{pi → sdk}/coding-agent/utils/child-process.js +0 -0
  205. /package/dist/{pi → sdk}/coding-agent/utils/clipboard-image.js +0 -0
  206. /package/dist/{pi → sdk}/coding-agent/utils/clipboard-native.js +0 -0
  207. /package/dist/{pi → sdk}/coding-agent/utils/clipboard.js +0 -0
  208. /package/dist/{pi → sdk}/coding-agent/utils/exif-orientation.js +0 -0
  209. /package/dist/{pi → sdk}/coding-agent/utils/frontmatter.js +0 -0
  210. /package/dist/{pi → sdk}/coding-agent/utils/fs-watch.js +0 -0
  211. /package/dist/{pi → sdk}/coding-agent/utils/git.js +0 -0
  212. /package/dist/{pi → sdk}/coding-agent/utils/html.js +0 -0
  213. /package/dist/{pi → sdk}/coding-agent/utils/image-convert.js +0 -0
  214. /package/dist/{pi → sdk}/coding-agent/utils/image-resize.js +0 -0
  215. /package/dist/{pi → sdk}/coding-agent/utils/mime.js +0 -0
  216. /package/dist/{pi → sdk}/coding-agent/utils/paths.js +0 -0
  217. /package/dist/{pi → sdk}/coding-agent/utils/photon.js +0 -0
  218. /package/dist/{pi → sdk}/coding-agent/utils/pi-user-agent.js +0 -0
  219. /package/dist/{pi → sdk}/coding-agent/utils/shell.js +0 -0
  220. /package/dist/{pi → sdk}/coding-agent/utils/sleep.js +0 -0
  221. /package/dist/{pi → sdk}/coding-agent/utils/syntax-highlight.js +0 -0
@@ -113,14 +113,16 @@ export class ExtensionRunner {
113
113
  shutdownHandler = () => { };
114
114
  shortcutDiagnostics = [];
115
115
  commandDiagnostics = [];
116
+ settingsManager;
116
117
  staleMessage;
117
- constructor(extensions, runtime, cwd, sessionManager, modelRegistry) {
118
+ constructor(extensions, runtime, cwd, sessionManager, modelRegistry, settingsManager) {
118
119
  this.extensions = extensions;
119
120
  this.runtime = runtime;
120
121
  this.uiContext = noOpUIContext;
121
122
  this.cwd = cwd;
122
123
  this.sessionManager = sessionManager;
123
124
  this.modelRegistry = modelRegistry;
125
+ this.settingsManager = settingsManager;
124
126
  }
125
127
  bindCore(actions, contextActions, providerActions) {
126
128
  // Copy actions into the shared runtime (all extension APIs reference this)
@@ -399,6 +401,10 @@ export class ExtensionRunner {
399
401
  runner.assertActive();
400
402
  return runner.modelRegistry;
401
403
  },
404
+ get settingsManager() {
405
+ runner.assertActive();
406
+ return runner.settingsManager;
407
+ },
402
408
  get model() {
403
409
  runner.assertActive();
404
410
  return getModel();
@@ -1,7 +1,134 @@
1
- import { TUI_KEYBINDINGS, KeybindingsManager as TuiKeybindingsManager, } from "../../tui/index.js";
2
1
  import { existsSync, readFileSync } from "fs";
3
2
  import { join } from "path";
4
3
  import { getAgentDir } from "../config.js";
4
+ /** Placeholder — no terminal key input in headless mode. */
5
+ export function matchesKey(_data, _keyId) {
6
+ return false;
7
+ }
8
+ /** Placeholder. */
9
+ export function parseKey(_data) {
10
+ return "";
11
+ }
12
+ /** Placeholder. */
13
+ export function isKeyRelease(_data) {
14
+ return false;
15
+ }
16
+ /** Placeholder. */
17
+ export function isKeyRepeat(_data) {
18
+ return false;
19
+ }
20
+ /** Placeholder. */
21
+ export function isKittyProtocolActive() {
22
+ return false;
23
+ }
24
+ /** Placeholder. */
25
+ export function setKittyProtocolActive(_active) { }
26
+ /** Placeholder. */
27
+ export function decodeKittyPrintable(_data) {
28
+ return _data;
29
+ }
30
+ /** Key identifier helper. */
31
+ export class Key {
32
+ static c(key) { return `ctrl+${key}`; }
33
+ static m(key) { return `alt+${key}`; }
34
+ static cm(key) { return `ctrl+alt+${key}`; }
35
+ static backspace = "backspace";
36
+ static enter = "enter";
37
+ static escape = "escape";
38
+ static tab = "tab";
39
+ static up = "up";
40
+ static down = "down";
41
+ static left = "left";
42
+ static right = "right";
43
+ static delete = "delete";
44
+ static home = "home";
45
+ static end = "end";
46
+ static pageUp = "pageUp";
47
+ static pageDown = "pageDown";
48
+ static f1 = "f1";
49
+ static f2 = "f2";
50
+ static f3 = "f3";
51
+ static f4 = "f4";
52
+ static f5 = "f5";
53
+ static f6 = "f6";
54
+ static f7 = "f7";
55
+ static f8 = "f8";
56
+ static f9 = "f9";
57
+ static f10 = "f10";
58
+ static f11 = "f11";
59
+ static f12 = "f12";
60
+ static space = "space";
61
+ }
62
+ /** Built-in keybinding defaults. */
63
+ export const TUI_KEYBINDINGS = {
64
+ "tui.editor.cursorUp": { defaultKeys: "up" },
65
+ "tui.editor.cursorDown": { defaultKeys: "down" },
66
+ "tui.editor.cursorLeft": { defaultKeys: "left" },
67
+ "tui.editor.cursorRight": { defaultKeys: "right" },
68
+ "tui.editor.cursorWordLeft": { defaultKeys: ["ctrl+left", "alt+left"] },
69
+ "tui.editor.cursorWordRight": { defaultKeys: ["ctrl+right", "alt+right"] },
70
+ "tui.editor.cursorLineStart": { defaultKeys: "home" },
71
+ "tui.editor.cursorLineEnd": { defaultKeys: "end" },
72
+ "tui.editor.jumpForward": { defaultKeys: [] },
73
+ "tui.editor.jumpBackward": { defaultKeys: [] },
74
+ "tui.editor.pageUp": { defaultKeys: "pageUp" },
75
+ "tui.editor.pageDown": { defaultKeys: "pageDown" },
76
+ "tui.editor.deleteCharBackward": { defaultKeys: "backspace" },
77
+ "tui.editor.deleteCharForward": { defaultKeys: "delete" },
78
+ "tui.editor.deleteWordBackward": { defaultKeys: ["ctrl+backspace", "alt+backspace"] },
79
+ "tui.editor.deleteWordForward": { defaultKeys: ["ctrl+delete", "alt+delete"] },
80
+ "tui.editor.deleteToLineStart": { defaultKeys: "ctrl+u" },
81
+ "tui.editor.deleteToLineEnd": { defaultKeys: "ctrl+k" },
82
+ "tui.editor.yank": { defaultKeys: "ctrl+y" },
83
+ "tui.editor.yankPop": { defaultKeys: "alt+y" },
84
+ "tui.editor.undo": { defaultKeys: "ctrl+_" },
85
+ "tui.input.newLine": { defaultKeys: "enter" },
86
+ "tui.input.submit": { defaultKeys: [] },
87
+ "tui.input.tab": { defaultKeys: "tab" },
88
+ "tui.input.copy": { defaultKeys: [] },
89
+ "tui.select.up": { defaultKeys: "up" },
90
+ "tui.select.down": { defaultKeys: "down" },
91
+ "tui.select.pageUp": { defaultKeys: "pageUp" },
92
+ "tui.select.pageDown": { defaultKeys: "pageDown" },
93
+ "tui.select.confirm": { defaultKeys: "enter" },
94
+ "tui.select.cancel": { defaultKeys: "escape" },
95
+ };
96
+ // ============================================================================
97
+ // Base KeybindingsManager
98
+ // ============================================================================
99
+ export class BaseKeybindingsManager {
100
+ definitions;
101
+ userBindings;
102
+ constructor(definitions = {}, userBindings = {}) {
103
+ this.definitions = definitions;
104
+ this.userBindings = userBindings;
105
+ }
106
+ getKeys(binding) {
107
+ const key = binding;
108
+ const def = this.definitions[key];
109
+ if (!def)
110
+ return [];
111
+ return Array.isArray(def.defaultKeys) ? def.defaultKeys : [def.defaultKeys];
112
+ }
113
+ setUserBindings(bindings) {
114
+ this.userBindings = bindings;
115
+ }
116
+ getResolvedBindings() {
117
+ return { ...this.userBindings };
118
+ }
119
+ reconfigure(_config) { }
120
+ }
121
+ // ============================================================================
122
+ // Singleton accessor
123
+ // ============================================================================
124
+ let _globalKeybindings = null;
125
+ export function getKeybindings() {
126
+ if (!_globalKeybindings) {
127
+ _globalKeybindings = KeybindingsManager.create();
128
+ }
129
+ return _globalKeybindings;
130
+ }
131
+ export function setKeybindings(_config) { }
5
132
  export const KEYBINDINGS = {
6
133
  ...TUI_KEYBINDINGS,
7
134
  "app.interrupt": { defaultKeys: "escape", description: "Cancel or abort" },
@@ -266,7 +393,7 @@ function loadRawConfig(path) {
266
393
  return undefined;
267
394
  }
268
395
  }
269
- export class KeybindingsManager extends TuiKeybindingsManager {
396
+ export class KeybindingsManager extends BaseKeybindingsManager {
270
397
  configPath;
271
398
  constructor(userBindings = {}, configPath) {
272
399
  super(KEYBINDINGS, userBindings);
@@ -241,16 +241,23 @@ export class ModelRegistry {
241
241
  loadError = undefined;
242
242
  authStorage;
243
243
  modelsJsonPath;
244
- constructor(authStorage, modelsJsonPath) {
244
+ constructor(authStorage, modelsJsonPath, loadBuiltins) {
245
245
  this.authStorage = authStorage;
246
246
  this.modelsJsonPath = modelsJsonPath ? normalizePath(modelsJsonPath) : undefined;
247
- this.loadModels();
247
+ if (loadBuiltins) {
248
+ this.loadModels();
249
+ }
250
+ // When loadBuiltins is false, we start with an empty model list
251
+ // — the caller is responsible for populating it via registerProvider().
252
+ // This is used in relay/serve mode where all models come from the
253
+ // backend's synthetic proxy providers, and loading the 16K-line
254
+ // models.generated.ts is pure startup waste.
248
255
  }
249
256
  static create(authStorage, modelsJsonPath = join(getAgentDir(), "models.json")) {
250
- return new ModelRegistry(authStorage, modelsJsonPath);
257
+ return new ModelRegistry(authStorage, modelsJsonPath, true);
251
258
  }
252
259
  static inMemory(authStorage) {
253
- return new ModelRegistry(authStorage, undefined);
260
+ return new ModelRegistry(authStorage, undefined, false);
254
261
  }
255
262
  /**
256
263
  * Reload models from disk (built-in + custom from models.json).
@@ -33,7 +33,7 @@ const NETWORK_TIMEOUT_MS = 10000;
33
33
  const UPDATE_CHECK_CONCURRENCY = 4;
34
34
  const GIT_UPDATE_CONCURRENCY = 4;
35
35
  function isOfflineModeEnabled() {
36
- const value = process.env.PI_OFFLINE;
36
+ const value = process.env.SPECTRAL_OFFLINE;
37
37
  if (!value)
38
38
  return false;
39
39
  return value === "1" || value.toLowerCase() === "true" || value.toLowerCase() === "yes";
@@ -1833,9 +1833,9 @@ export class DefaultPackageManager {
1833
1833
  this.addResource(target, path, metadata, enabled);
1834
1834
  }
1835
1835
  };
1836
- // Project extensions from .pi/
1836
+ // Project extensions from .spectral/
1837
1837
  addResources("extensions", collectAutoExtensionEntries(projectDirs.extensions), projectMetadata, projectOverrides.extensions, projectBaseDir);
1838
- // Project skills from .pi/
1838
+ // Project skills from .spectral/
1839
1839
  addResources("skills", collectAutoSkillEntries(projectDirs.skills, "pi"), projectMetadata, projectOverrides.skills, projectBaseDir);
1840
1840
  // Project skills from .agents/ (each with its own baseDir)
1841
1841
  for (const agentsSkillsDir of projectAgentsSkillDirs) {
@@ -1848,9 +1848,9 @@ export class DefaultPackageManager {
1848
1848
  }
1849
1849
  addResources("prompts", collectAutoPromptEntries(projectDirs.prompts), projectMetadata, projectOverrides.prompts, projectBaseDir);
1850
1850
  addResources("themes", collectAutoThemeEntries(projectDirs.themes), projectMetadata, projectOverrides.themes, projectBaseDir);
1851
- // User extensions from ~/.pi/agent/
1851
+ // User extensions from ~/.spectral/agent/
1852
1852
  addResources("extensions", collectAutoExtensionEntries(userDirs.extensions), userMetadata, userOverrides.extensions, globalBaseDir);
1853
- // User skills from ~/.pi/agent/
1853
+ // User skills from ~/.spectral/agent/
1854
1854
  addResources("skills", collectAutoSkillEntries(userDirs.skills, "pi"), userMetadata, userOverrides.skills, globalBaseDir);
1855
1855
  // User skills from ~/.agents/ (with its own baseDir)
1856
1856
  const userAgentsBaseDir = dirname(userAgentsSkillsDir);
@@ -31,7 +31,7 @@ function getAttributionHeaders(model, settingsManager) {
31
31
  }
32
32
  if (model.provider === "openrouter" || model.baseUrl.includes("openrouter.ai")) {
33
33
  return {
34
- "HTTP-Referer": "https://pi.dev",
34
+ "HTTP-Referer": "https://spectral.dev",
35
35
  "X-OpenRouter-Title": "pi",
36
36
  "X-OpenRouter-Categories": "cli-agent",
37
37
  };
@@ -208,7 +208,7 @@ export function buildSessionContext(entries, leafId, byId) {
208
208
  }
209
209
  /**
210
210
  * Compute the default session directory for a cwd.
211
- * Encodes cwd into a safe directory name under ~/.pi/agent/sessions/.
211
+ * Encodes cwd into a safe directory name under ~/.spectral/agent/sessions/.
212
212
  */
213
213
  export function getDefaultSessionDir(cwd, agentDir = getDefaultAgentDir()) {
214
214
  const resolvedCwd = resolvePath(cwd);
@@ -1002,7 +1002,7 @@ export class SessionManager {
1002
1002
  /**
1003
1003
  * Create a new session.
1004
1004
  * @param cwd Working directory (stored in session header)
1005
- * @param sessionDir Optional session directory. If omitted, uses default (~/.pi/agent/sessions/<encoded-cwd>/).
1005
+ * @param sessionDir Optional session directory. If omitted, uses default (~/.spectral/agent/sessions/<encoded-cwd>/).
1006
1006
  */
1007
1007
  static create(cwd, sessionDir) {
1008
1008
  const dir = sessionDir ? normalizePath(sessionDir) : getDefaultSessionDir(cwd);
@@ -1027,7 +1027,7 @@ export class SessionManager {
1027
1027
  /**
1028
1028
  * Continue the most recent session, or create new if none.
1029
1029
  * @param cwd Working directory
1030
- * @param sessionDir Optional session directory. If omitted, uses default (~/.pi/agent/sessions/<encoded-cwd>/).
1030
+ * @param sessionDir Optional session directory. If omitted, uses default (~/.spectral/agent/sessions/<encoded-cwd>/).
1031
1031
  */
1032
1032
  static continueRecent(cwd, sessionDir) {
1033
1033
  const dir = sessionDir ? normalizePath(sessionDir) : getDefaultSessionDir(cwd);
@@ -1089,7 +1089,7 @@ export class SessionManager {
1089
1089
  /**
1090
1090
  * List all sessions for a directory.
1091
1091
  * @param cwd Working directory (used to compute default session directory)
1092
- * @param sessionDir Optional session directory. If omitted, uses default (~/.pi/agent/sessions/<encoded-cwd>/).
1092
+ * @param sessionDir Optional session directory. If omitted, uses default (~/.spectral/agent/sessions/<encoded-cwd>/).
1093
1093
  * @param onProgress Optional callback for progress updates (loaded, total)
1094
1094
  */
1095
1095
  static async list(cwd, sessionDir, onProgress) {
@@ -385,6 +385,26 @@ export class SettingsManager {
385
385
  getDefaultModel() {
386
386
  return this.settings.defaultModel;
387
387
  }
388
+ getDefaultVisionProvider() {
389
+ return this.settings.defaultVisionProvider;
390
+ }
391
+ getDefaultVisionModel() {
392
+ return this.settings.defaultVisionModel;
393
+ }
394
+ setDefaultVisionProvider(provider) {
395
+ this.globalSettings.defaultVisionProvider = provider;
396
+ this.markModified("defaultVisionProvider");
397
+ }
398
+ setDefaultVisionModel(modelId) {
399
+ this.globalSettings.defaultVisionModel = modelId;
400
+ this.markModified("defaultVisionModel");
401
+ }
402
+ setDefaultVisionModelAndProvider(provider, modelId) {
403
+ this.globalSettings.defaultVisionProvider = provider;
404
+ this.globalSettings.defaultVisionModel = modelId;
405
+ this.markModified("defaultVisionProvider");
406
+ this.markModified("defaultVisionModel");
407
+ }
388
408
  setDefaultProvider(provider) {
389
409
  this.globalSettings.defaultProvider = provider;
390
410
  this.markModified("defaultProvider");
@@ -3,6 +3,6 @@ function isTruthyEnvFlag(value) {
3
3
  return false;
4
4
  return value === "1" || value.toLowerCase() === "true" || value.toLowerCase() === "yes";
5
5
  }
6
- export function isInstallTelemetryEnabled(settingsManager, telemetryEnv = process.env.PI_TELEMETRY) {
6
+ export function isInstallTelemetryEnabled(settingsManager, telemetryEnv = process.env.SPECTRAL_TELEMETRY) {
7
7
  return telemetryEnv !== undefined ? isTruthyEnvFlag(telemetryEnv) : settingsManager.getEnableInstallTelemetry();
8
8
  }
@@ -1,9 +1,7 @@
1
1
  import { existsSync } from "node:fs";
2
- import { Container, Text, truncateToWidth } from "../../../tui/index.js";
3
2
  import { spawn } from "child_process";
4
3
  import { Type } from "typebox";
5
4
  import { keyHint } from "../../modes/interactive/components/keybinding-hints.js";
6
- import { truncateToVisualLines } from "../../modes/interactive/components/visual-truncate.js";
7
5
  import { theme } from "../../modes/interactive/theme/theme.js";
8
6
  import { waitForChildProcess } from "../../utils/child-process.js";
9
7
  import { getShellConfig, getShellEnv, killProcessTree, trackDetachedChildPid, untrackDetachedChildPid, } from "../../utils/shell.js";
@@ -102,13 +100,6 @@ function resolveSpawnContext(command, cwd, spawnHook) {
102
100
  }
103
101
  const BASH_PREVIEW_LINES = 5;
104
102
  const BASH_UPDATE_THROTTLE_MS = 100;
105
- class BashResultRenderComponent extends Container {
106
- state = {
107
- cachedWidth: undefined,
108
- cachedLines: undefined,
109
- cachedSkipped: undefined,
110
- };
111
- }
112
103
  function formatDuration(ms) {
113
104
  return `${(ms / 1000).toFixed(1)}s`;
114
105
  }
@@ -119,9 +110,7 @@ function formatBashCall(args) {
119
110
  const commandDisplay = command === null ? invalidArgText(theme) : command ? command : theme.fg("toolOutput", "...");
120
111
  return theme.fg("toolTitle", theme.bold(`$ ${commandDisplay}`)) + timeoutSuffix;
121
112
  }
122
- function rebuildBashResultRenderComponent(component, result, options, showImages, startedAt, endedAt) {
123
- const state = component.state;
124
- component.clear();
113
+ function formatBashResult(result, options, showImages, startedAt, endedAt) {
125
114
  let output = getTextOutput(result, showImages).trim();
126
115
  const truncation = result.details?.truncation;
127
116
  const fullOutputPath = result.details?.fullOutputPath;
@@ -131,36 +120,27 @@ function rebuildBashResultRenderComponent(component, result, options, showImages
131
120
  output = output.slice(0, footerStart).trimEnd();
132
121
  }
133
122
  }
123
+ const parts = [];
134
124
  if (output) {
135
125
  const styledOutput = output
136
126
  .split("\n")
137
127
  .map((line) => theme.fg("toolOutput", line))
138
128
  .join("\n");
139
129
  if (options.expanded) {
140
- component.addChild(new Text(`\n${styledOutput}`, 0, 0));
130
+ parts.push(`\n${styledOutput}`);
141
131
  }
142
132
  else {
143
- component.addChild({
144
- render: (width) => {
145
- if (state.cachedLines === undefined || state.cachedWidth !== width) {
146
- const preview = truncateToVisualLines(styledOutput, BASH_PREVIEW_LINES, width);
147
- state.cachedLines = preview.visualLines;
148
- state.cachedSkipped = preview.skippedCount;
149
- state.cachedWidth = width;
150
- }
151
- if (state.cachedSkipped && state.cachedSkipped > 0) {
152
- const hint = theme.fg("muted", `... (${state.cachedSkipped} earlier lines,`) +
153
- ` ${keyHint("app.tools.expand", "to expand")})`;
154
- return ["", truncateToWidth(hint, width, "..."), ...(state.cachedLines ?? [])];
155
- }
156
- return ["", ...(state.cachedLines ?? [])];
157
- },
158
- invalidate: () => {
159
- state.cachedWidth = undefined;
160
- state.cachedLines = undefined;
161
- state.cachedSkipped = undefined;
162
- },
163
- });
133
+ const styledLines = styledOutput.split("\n");
134
+ if (styledLines.length > BASH_PREVIEW_LINES) {
135
+ const hint = theme.fg("muted", `... (${styledLines.length - BASH_PREVIEW_LINES} earlier lines, ${keyHint("app.tools.expand", "to expand")})`);
136
+ parts.push("");
137
+ parts.push(hint);
138
+ parts.push(...styledLines.slice(-BASH_PREVIEW_LINES));
139
+ }
140
+ else {
141
+ parts.push("");
142
+ parts.push(...styledLines);
143
+ }
164
144
  }
165
145
  }
166
146
  if (truncation?.truncated || fullOutputPath) {
@@ -176,13 +156,14 @@ function rebuildBashResultRenderComponent(component, result, options, showImages
176
156
  warnings.push(`Truncated: ${truncation.outputLines} lines shown (${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit)`);
177
157
  }
178
158
  }
179
- component.addChild(new Text(`\n${theme.fg("warning", `[${warnings.join(". ")}]`)}`, 0, 0));
159
+ parts.push(`\n${theme.fg("warning", `[${warnings.join(". ")}]`)}`);
180
160
  }
181
161
  if (startedAt !== undefined) {
182
162
  const label = options.isPartial ? "Elapsed" : "Took";
183
163
  const endTime = endedAt ?? Date.now();
184
- component.addChild(new Text(`\n${theme.fg("muted", `${label} ${formatDuration(endTime - startedAt)}`)}`, 0, 0));
164
+ parts.push(`\n${theme.fg("muted", `${label} ${formatDuration(endTime - startedAt)}`)}`);
185
165
  }
166
+ return parts.join("");
186
167
  }
187
168
  export function createBashToolDefinition(cwd, options) {
188
169
  const ops = options?.operations ?? createLocalBashOperations({ shellPath: options?.shellPath });
@@ -307,33 +288,6 @@ export function createBashToolDefinition(cwd, options) {
307
288
  clearUpdateTimer();
308
289
  }
309
290
  },
310
- renderCall(args, _theme, context) {
311
- const state = context.state;
312
- if (context.executionStarted && state.startedAt === undefined) {
313
- state.startedAt = Date.now();
314
- state.endedAt = undefined;
315
- }
316
- const text = context.lastComponent ?? new Text("", 0, 0);
317
- text.setText(formatBashCall(args));
318
- return text;
319
- },
320
- renderResult(result, options, _theme, context) {
321
- const state = context.state;
322
- if (state.startedAt !== undefined && options.isPartial && !state.interval) {
323
- state.interval = setInterval(() => context.invalidate(), 1000);
324
- }
325
- if (!options.isPartial || context.isError) {
326
- state.endedAt ??= Date.now();
327
- if (state.interval) {
328
- clearInterval(state.interval);
329
- state.interval = undefined;
330
- }
331
- }
332
- const component = context.lastComponent ?? new BashResultRenderComponent();
333
- rebuildBashResultRenderComponent(component, result, options, context.showImages, state.startedAt, state.endedAt);
334
- component.invalidate();
335
- return component;
336
- },
337
291
  };
338
292
  }
339
293
  export function createBashTool(cwd, options) {
@@ -1,9 +1,8 @@
1
- import { Box, Container, Spacer, Text } from "../../../tui/index.js";
2
1
  import { constants } from "fs";
3
2
  import { access as fsAccess, readFile as fsReadFile, writeFile as fsWriteFile } from "fs/promises";
4
3
  import { Type } from "typebox";
5
4
  import { renderDiff } from "../../modes/interactive/components/diff.js";
6
- import { applyEditsToNormalizedContent, computeEditsDiff, detectLineEnding, generateDiffString, generateUnifiedPatch, normalizeToLF, restoreLineEndings, stripBom, } from "./edit-diff.js";
5
+ import { applyEditsToNormalizedContent, detectLineEnding, generateDiffString, generateUnifiedPatch, normalizeToLF, restoreLineEndings, stripBom, } from "./edit-diff.js";
7
6
  import { withFileMutationQueue } from "./file-mutation-queue.js";
8
7
  import { resolveToCwd } from "./path-utils.js";
9
8
  import { invalidArgText, shortenPath, str } from "./render-utils.js";
@@ -54,45 +53,6 @@ function validateEditInput(input) {
54
53
  }
55
54
  return { path: input.path, edits: input.edits };
56
55
  }
57
- function createEditCallRenderComponent() {
58
- return Object.assign(new Box(1, 1, (text) => text), {
59
- preview: undefined,
60
- previewArgsKey: undefined,
61
- previewPending: false,
62
- settledError: false,
63
- });
64
- }
65
- function getEditCallRenderComponent(state, lastComponent) {
66
- if (lastComponent instanceof Box) {
67
- const component = lastComponent;
68
- state.callComponent = component;
69
- return component;
70
- }
71
- if (state.callComponent) {
72
- return state.callComponent;
73
- }
74
- const component = createEditCallRenderComponent();
75
- state.callComponent = component;
76
- return component;
77
- }
78
- function getRenderablePreviewInput(args) {
79
- if (!args) {
80
- return null;
81
- }
82
- const path = typeof args.path === "string" ? args.path : typeof args.file_path === "string" ? args.file_path : null;
83
- if (!path) {
84
- return null;
85
- }
86
- if (Array.isArray(args.edits) &&
87
- args.edits.length > 0 &&
88
- args.edits.every((edit) => typeof edit?.oldText === "string" && typeof edit?.newText === "string")) {
89
- return { path, edits: args.edits };
90
- }
91
- if (typeof args.oldText === "string" && typeof args.newText === "string") {
92
- return { path, edits: [{ oldText: args.oldText, newText: args.newText }] };
93
- }
94
- return null;
95
- }
96
56
  function formatEditCall(args, theme) {
97
57
  const invalidArg = invalidArgText(theme);
98
58
  const rawPath = str(args?.file_path ?? args?.path);
@@ -100,64 +60,24 @@ function formatEditCall(args, theme) {
100
60
  const pathDisplay = path === null ? invalidArg : path ? theme.fg("accent", path) : theme.fg("toolOutput", "...");
101
61
  return `${theme.fg("toolTitle", theme.bold("edit"))} ${pathDisplay}`;
102
62
  }
103
- function formatEditResult(args, preview, result, theme, isError) {
63
+ function formatEditResult(args, result, theme, isError) {
104
64
  const rawPath = str(args?.file_path ?? args?.path);
105
- const previewDiff = preview && !("error" in preview) ? preview.diff : undefined;
106
- const previewError = preview && "error" in preview ? preview.error : undefined;
107
65
  if (isError) {
108
66
  const errorText = result.content
109
67
  .filter((c) => c.type === "text")
110
68
  .map((c) => c.text || "")
111
69
  .join("\n");
112
- if (!errorText || errorText === previewError) {
70
+ if (!errorText) {
113
71
  return undefined;
114
72
  }
115
73
  return theme.fg("error", errorText);
116
74
  }
117
75
  const resultDiff = result.details?.diff;
118
- if (resultDiff && resultDiff !== previewDiff) {
76
+ if (resultDiff) {
119
77
  return renderDiff(resultDiff, { filePath: rawPath ?? undefined });
120
78
  }
121
79
  return undefined;
122
80
  }
123
- function getEditHeaderBg(preview, settledError, theme) {
124
- if (preview) {
125
- if ("error" in preview) {
126
- return (text) => theme.bg("toolErrorBg", text);
127
- }
128
- return (text) => theme.bg("toolSuccessBg", text);
129
- }
130
- if (settledError) {
131
- return (text) => theme.bg("toolErrorBg", text);
132
- }
133
- return (text) => theme.bg("toolPendingBg", text);
134
- }
135
- function buildEditCallComponent(component, args, theme) {
136
- component.setBgFn(getEditHeaderBg(component.preview, component.settledError, theme));
137
- component.clear();
138
- component.addChild(new Text(formatEditCall(args, theme), 0, 0));
139
- if (!component.preview) {
140
- return component;
141
- }
142
- const body = "error" in component.preview ? theme.fg("error", component.preview.error) : renderDiff(component.preview.diff);
143
- component.addChild(new Spacer(1));
144
- component.addChild(new Text(body, 0, 0));
145
- return component;
146
- }
147
- function setEditPreview(component, preview, argsKey) {
148
- const current = component.preview;
149
- const changed = current === undefined ||
150
- ("error" in current && "error" in preview
151
- ? current.error !== preview.error
152
- : "error" in current !== "error" in preview) ||
153
- (!("error" in current) &&
154
- !("error" in preview) &&
155
- (current.diff !== preview.diff || current.firstChangedLine !== preview.firstChangedLine));
156
- component.preview = preview;
157
- component.previewArgsKey = argsKey;
158
- component.previewPending = false;
159
- return changed;
160
- }
161
81
  export function createEditToolDefinition(cwd, options) {
162
82
  const ops = options?.operations ?? defaultEditOperations;
163
83
  return {
@@ -172,7 +92,6 @@ export function createEditToolDefinition(cwd, options) {
172
92
  "Keep edits[].oldText as small as possible while still being unique in the file. Do not pad with large unchanged regions.",
173
93
  ],
174
94
  parameters: editSchema,
175
- renderShell: "self",
176
95
  prepareArguments: prepareEditArguments,
177
96
  async execute(_toolCallId, input, signal, _onUpdate, _ctx) {
178
97
  const { path, edits } = validateEditInput(input);
@@ -261,62 +180,6 @@ export function createEditToolDefinition(cwd, options) {
261
180
  })();
262
181
  }));
263
182
  },
264
- renderCall(args, theme, context) {
265
- const component = getEditCallRenderComponent(context.state, context.lastComponent);
266
- const previewInput = getRenderablePreviewInput(args);
267
- const argsKey = previewInput
268
- ? JSON.stringify({ path: previewInput.path, edits: previewInput.edits })
269
- : undefined;
270
- if (component.previewArgsKey !== argsKey) {
271
- component.preview = undefined;
272
- component.previewArgsKey = argsKey;
273
- component.previewPending = false;
274
- component.settledError = false;
275
- }
276
- if (context.argsComplete && previewInput && !component.preview && !component.previewPending) {
277
- component.previewPending = true;
278
- const requestKey = argsKey;
279
- void computeEditsDiff(previewInput.path, previewInput.edits, context.cwd).then((preview) => {
280
- if (component.previewArgsKey === requestKey) {
281
- setEditPreview(component, preview, requestKey);
282
- context.invalidate();
283
- }
284
- });
285
- }
286
- return buildEditCallComponent(component, args, theme);
287
- },
288
- renderResult(result, _options, theme, context) {
289
- const callComponent = context.state.callComponent;
290
- const previewInput = getRenderablePreviewInput(context.args);
291
- const argsKey = previewInput
292
- ? JSON.stringify({ path: previewInput.path, edits: previewInput.edits })
293
- : undefined;
294
- const typedResult = result;
295
- const resultDiff = !context.isError ? typedResult.details?.diff : undefined;
296
- let changed = false;
297
- if (callComponent) {
298
- if (typeof resultDiff === "string") {
299
- changed =
300
- setEditPreview(callComponent, { diff: resultDiff, firstChangedLine: typedResult.details?.firstChangedLine }, argsKey) || changed;
301
- }
302
- if (callComponent.settledError !== context.isError) {
303
- callComponent.settledError = context.isError;
304
- changed = true;
305
- }
306
- if (changed) {
307
- buildEditCallComponent(callComponent, context.args, theme);
308
- }
309
- }
310
- const output = formatEditResult(context.args, callComponent?.preview, typedResult, theme, context.isError);
311
- const component = context.lastComponent ?? new Container();
312
- component.clear();
313
- if (!output) {
314
- return component;
315
- }
316
- component.addChild(new Spacer(1));
317
- component.addChild(new Text(output, 1, 0));
318
- return component;
319
- },
320
183
  };
321
184
  }
322
185
  export function createEditTool(cwd, options) {