@aexol/spectral 0.7.8 → 0.8.2

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 (197) hide show
  1. package/dist/agent/agents.js +4 -4
  2. package/dist/agent/index.js +8 -8
  3. package/dist/cli.js +1 -1
  4. package/dist/commands/serve.js +1 -1
  5. package/dist/extensions/kanban-bridge.js +668 -0
  6. package/dist/extensions/spectral-vision-fallback.js +84 -46
  7. package/dist/mcp/agent-dir.js +1 -1
  8. package/dist/mcp/config.js +3 -3
  9. package/dist/mcp/init.js +1 -9
  10. package/dist/mcp/sampling-handler.js +1 -1
  11. package/dist/mcp/server-manager.js +5 -1
  12. package/dist/memory/commands/status.js +1 -1
  13. package/dist/memory/compaction.js +2 -2
  14. package/dist/memory/config.js +3 -3
  15. package/dist/memory/debug-log.js +1 -1
  16. package/dist/memory/index.js +2 -0
  17. package/dist/memory/observer.js +2 -2
  18. package/dist/memory/tokens.js +1 -1
  19. package/dist/memory/tools/read-project-observations.js +2 -2
  20. package/dist/memory/tools/recall-observation.js +2 -2
  21. package/dist/memory/tools/write-project-observation.js +60 -0
  22. package/dist/relay/auto-research.js +57 -23
  23. package/dist/relay/dispatcher.js +28 -2
  24. package/dist/relay/models-fetch.js +2 -2
  25. package/dist/{pi → sdk}/ai/env-api-keys.js +9 -49
  26. package/dist/{pi → sdk}/ai/utils/oauth/anthropic.js +1 -1
  27. package/dist/{pi → sdk}/ai/utils/oauth/openai-codex.js +1 -1
  28. package/dist/{pi → sdk}/coding-agent/config.js +11 -78
  29. package/dist/{pi → sdk}/coding-agent/core/agent-session.js +2 -0
  30. package/dist/{pi → sdk}/coding-agent/core/compaction/compaction.js +161 -5
  31. package/dist/{pi → sdk}/coding-agent/core/extensions/loader.js +2 -35
  32. package/dist/{pi → sdk}/coding-agent/core/extensions/runner.js +1 -2
  33. package/dist/{pi → sdk}/coding-agent/core/model-registry.js +11 -4
  34. package/dist/sdk/coding-agent/core/model-resolver-utils.js +8 -0
  35. package/dist/{pi → sdk}/coding-agent/core/model-resolver.js +1 -1
  36. package/dist/{pi → sdk}/coding-agent/core/package-manager.js +5 -5
  37. package/dist/{pi → sdk}/coding-agent/core/resource-loader.js +1 -1
  38. package/dist/{pi → sdk}/coding-agent/core/sdk.js +1 -1
  39. package/dist/{pi → sdk}/coding-agent/core/session-manager.js +4 -4
  40. package/dist/{pi → sdk}/coding-agent/core/settings-manager.js +1 -170
  41. package/dist/{pi → sdk}/coding-agent/core/system-prompt.js +3 -1
  42. package/dist/{pi → sdk}/coding-agent/core/telemetry.js +1 -1
  43. package/dist/sdk/coding-agent/core/theme.js +202 -0
  44. package/dist/{pi → sdk}/coding-agent/core/tools/bash.js +17 -18
  45. package/dist/{pi → sdk}/coding-agent/core/tools/edit.js +7 -8
  46. package/dist/{pi → sdk}/coding-agent/core/tools/find.js +9 -13
  47. package/dist/{pi → sdk}/coding-agent/core/tools/grep.js +10 -14
  48. package/dist/{pi → sdk}/coding-agent/core/tools/ls.js +9 -10
  49. package/dist/{pi → sdk}/coding-agent/core/tools/read.js +15 -25
  50. package/dist/{pi/coding-agent/modes/interactive/components/diff.js → sdk/coding-agent/core/tools/render-diff.js} +18 -31
  51. package/dist/{pi → sdk}/coding-agent/core/tools/write.js +10 -11
  52. package/dist/{pi → sdk}/coding-agent/index.js +7 -5
  53. package/dist/{pi → sdk}/coding-agent/migrations.js +3 -3
  54. package/dist/{pi → sdk}/coding-agent/modes/index.js +0 -1
  55. package/dist/{pi → sdk}/coding-agent/modes/rpc/rpc-mode.js +2 -2
  56. package/dist/{pi → sdk}/coding-agent/utils/photon.js +2 -10
  57. package/dist/sdk/coding-agent/utils/pi-user-agent.js +3 -0
  58. package/dist/{pi → sdk}/coding-agent/utils/tools-manager.js +1 -1
  59. package/dist/{pi → sdk}/coding-agent/utils/version-check.js +2 -2
  60. package/dist/{pi → sdk}/coding-agent/utils/windows-self-update.js +1 -1
  61. package/dist/server/{pi-bridge.js → agent-bridge.js} +114 -97
  62. package/dist/server/handlers/sessions.js +21 -0
  63. package/dist/server/session-stream.js +5 -5
  64. package/package.json +6 -3
  65. package/dist/pi/coding-agent/bun/cli.js +0 -7
  66. package/dist/pi/coding-agent/bun/restore-sandbox-env.js +0 -31
  67. package/dist/pi/coding-agent/cli/args.js +0 -340
  68. package/dist/pi/coding-agent/cli/file-processor.js +0 -82
  69. package/dist/pi/coding-agent/cli/initial-message.js +0 -21
  70. package/dist/pi/coding-agent/core/footer-data-provider.js +0 -309
  71. package/dist/pi/coding-agent/modes/interactive/components/keybinding-hints.js +0 -35
  72. package/dist/pi/coding-agent/modes/interactive/components/visual-truncate.js +0 -26
  73. package/dist/pi/coding-agent/modes/interactive/interactive-mode.js +0 -3
  74. package/dist/pi/coding-agent/modes/interactive/theme/theme.js +0 -1022
  75. package/dist/pi/coding-agent/utils/pi-user-agent.js +0 -4
  76. /package/dist/{pi → sdk}/agent-core/agent-loop.js +0 -0
  77. /package/dist/{pi → sdk}/agent-core/agent.js +0 -0
  78. /package/dist/{pi → sdk}/agent-core/harness/agent-harness.js +0 -0
  79. /package/dist/{pi → sdk}/agent-core/harness/compaction/branch-summarization.js +0 -0
  80. /package/dist/{pi → sdk}/agent-core/harness/compaction/compaction.js +0 -0
  81. /package/dist/{pi → sdk}/agent-core/harness/compaction/utils.js +0 -0
  82. /package/dist/{pi → sdk}/agent-core/harness/env/nodejs.js +0 -0
  83. /package/dist/{pi → sdk}/agent-core/harness/messages.js +0 -0
  84. /package/dist/{pi → sdk}/agent-core/harness/prompt-templates.js +0 -0
  85. /package/dist/{pi → sdk}/agent-core/harness/session/jsonl-repo.js +0 -0
  86. /package/dist/{pi → sdk}/agent-core/harness/session/jsonl-storage.js +0 -0
  87. /package/dist/{pi → sdk}/agent-core/harness/session/memory-repo.js +0 -0
  88. /package/dist/{pi → sdk}/agent-core/harness/session/memory-storage.js +0 -0
  89. /package/dist/{pi → sdk}/agent-core/harness/session/repo-utils.js +0 -0
  90. /package/dist/{pi → sdk}/agent-core/harness/session/session.js +0 -0
  91. /package/dist/{pi → sdk}/agent-core/harness/session/uuid.js +0 -0
  92. /package/dist/{pi → sdk}/agent-core/harness/skills.js +0 -0
  93. /package/dist/{pi → sdk}/agent-core/harness/system-prompt.js +0 -0
  94. /package/dist/{pi → sdk}/agent-core/harness/types.js +0 -0
  95. /package/dist/{pi → sdk}/agent-core/harness/utils/shell-output.js +0 -0
  96. /package/dist/{pi → sdk}/agent-core/harness/utils/truncate.js +0 -0
  97. /package/dist/{pi → sdk}/agent-core/index.js +0 -0
  98. /package/dist/{pi → sdk}/agent-core/node.js +0 -0
  99. /package/dist/{pi → sdk}/agent-core/proxy.js +0 -0
  100. /package/dist/{pi → sdk}/agent-core/types.js +0 -0
  101. /package/dist/{pi → sdk}/ai/api-registry.js +0 -0
  102. /package/dist/{pi → sdk}/ai/cli.js +0 -0
  103. /package/dist/{pi → sdk}/ai/image-models.generated.js +0 -0
  104. /package/dist/{pi → sdk}/ai/image-models.js +0 -0
  105. /package/dist/{pi → sdk}/ai/images-api-registry.js +0 -0
  106. /package/dist/{pi → sdk}/ai/images.js +0 -0
  107. /package/dist/{pi → sdk}/ai/index.js +0 -0
  108. /package/dist/{pi → sdk}/ai/models.generated.js +0 -0
  109. /package/dist/{pi → sdk}/ai/models.js +0 -0
  110. /package/dist/{pi → sdk}/ai/oauth.js +0 -0
  111. /package/dist/{pi → sdk}/ai/providers/anthropic.js +0 -0
  112. /package/dist/{pi → sdk}/ai/providers/faux.js +0 -0
  113. /package/dist/{pi → sdk}/ai/providers/github-copilot-headers.js +0 -0
  114. /package/dist/{pi → sdk}/ai/providers/openai-completions.js +0 -0
  115. /package/dist/{pi → sdk}/ai/providers/openai-prompt-cache.js +0 -0
  116. /package/dist/{pi → sdk}/ai/providers/register-builtins.js +0 -0
  117. /package/dist/{pi → sdk}/ai/providers/simple-options.js +0 -0
  118. /package/dist/{pi → sdk}/ai/providers/transform-messages.js +0 -0
  119. /package/dist/{pi → sdk}/ai/session-resources.js +0 -0
  120. /package/dist/{pi → sdk}/ai/stream.js +0 -0
  121. /package/dist/{pi → sdk}/ai/types.js +0 -0
  122. /package/dist/{pi → sdk}/ai/utils/diagnostics.js +0 -0
  123. /package/dist/{pi → sdk}/ai/utils/event-stream.js +0 -0
  124. /package/dist/{pi → sdk}/ai/utils/hash.js +0 -0
  125. /package/dist/{pi → sdk}/ai/utils/headers.js +0 -0
  126. /package/dist/{pi → sdk}/ai/utils/json-parse.js +0 -0
  127. /package/dist/{pi → sdk}/ai/utils/node-http-proxy.js +0 -0
  128. /package/dist/{pi → sdk}/ai/utils/oauth/device-code.js +0 -0
  129. /package/dist/{pi → sdk}/ai/utils/oauth/github-copilot.js +0 -0
  130. /package/dist/{pi → sdk}/ai/utils/oauth/index.js +0 -0
  131. /package/dist/{pi → sdk}/ai/utils/oauth/oauth-page.js +0 -0
  132. /package/dist/{pi → sdk}/ai/utils/oauth/pkce.js +0 -0
  133. /package/dist/{pi → sdk}/ai/utils/oauth/types.js +0 -0
  134. /package/dist/{pi → sdk}/ai/utils/overflow.js +0 -0
  135. /package/dist/{pi → sdk}/ai/utils/sanitize-unicode.js +0 -0
  136. /package/dist/{pi → sdk}/ai/utils/typebox-helpers.js +0 -0
  137. /package/dist/{pi → sdk}/ai/utils/validation.js +0 -0
  138. /package/dist/{pi → sdk}/coding-agent/cli.js +0 -0
  139. /package/dist/{pi → sdk}/coding-agent/core/agent-session-runtime.js +0 -0
  140. /package/dist/{pi → sdk}/coding-agent/core/agent-session-services.js +0 -0
  141. /package/dist/{pi → sdk}/coding-agent/core/auth-guidance.js +0 -0
  142. /package/dist/{pi → sdk}/coding-agent/core/auth-storage.js +0 -0
  143. /package/dist/{pi → sdk}/coding-agent/core/bash-executor.js +0 -0
  144. /package/dist/{pi → sdk}/coding-agent/core/compaction/branch-summarization.js +0 -0
  145. /package/dist/{pi → sdk}/coding-agent/core/compaction/index.js +0 -0
  146. /package/dist/{pi → sdk}/coding-agent/core/compaction/utils.js +0 -0
  147. /package/dist/{pi → sdk}/coding-agent/core/defaults.js +0 -0
  148. /package/dist/{pi → sdk}/coding-agent/core/diagnostics.js +0 -0
  149. /package/dist/{pi → sdk}/coding-agent/core/event-bus.js +0 -0
  150. /package/dist/{pi → sdk}/coding-agent/core/exec.js +0 -0
  151. /package/dist/{pi → sdk}/coding-agent/core/extensions/index.js +0 -0
  152. /package/dist/{pi → sdk}/coding-agent/core/extensions/types.js +0 -0
  153. /package/dist/{pi → sdk}/coding-agent/core/extensions/wrapper.js +0 -0
  154. /package/dist/{pi → sdk}/coding-agent/core/http-dispatcher.js +0 -0
  155. /package/dist/{pi → sdk}/coding-agent/core/index.js +0 -0
  156. /package/dist/{pi → sdk}/coding-agent/core/keybindings.js +0 -0
  157. /package/dist/{pi → sdk}/coding-agent/core/messages.js +0 -0
  158. /package/dist/{pi → sdk}/coding-agent/core/output-guard.js +0 -0
  159. /package/dist/{pi → sdk}/coding-agent/core/prompt-templates.js +0 -0
  160. /package/dist/{pi → sdk}/coding-agent/core/provider-display-names.js +0 -0
  161. /package/dist/{pi → sdk}/coding-agent/core/resolve-config-value.js +0 -0
  162. /package/dist/{pi → sdk}/coding-agent/core/session-cwd.js +0 -0
  163. /package/dist/{pi → sdk}/coding-agent/core/skills.js +0 -0
  164. /package/dist/{pi → sdk}/coding-agent/core/slash-commands.js +0 -0
  165. /package/dist/{pi → sdk}/coding-agent/core/source-info.js +0 -0
  166. /package/dist/{pi → sdk}/coding-agent/core/timings.js +0 -0
  167. /package/dist/{pi → sdk}/coding-agent/core/tools/edit-diff.js +0 -0
  168. /package/dist/{pi → sdk}/coding-agent/core/tools/file-mutation-queue.js +0 -0
  169. /package/dist/{pi → sdk}/coding-agent/core/tools/index.js +0 -0
  170. /package/dist/{pi → sdk}/coding-agent/core/tools/output-accumulator.js +0 -0
  171. /package/dist/{pi → sdk}/coding-agent/core/tools/path-utils.js +0 -0
  172. /package/dist/{pi → sdk}/coding-agent/core/tools/render-utils.js +0 -0
  173. /package/dist/{pi → sdk}/coding-agent/core/tools/tool-definition-wrapper.js +0 -0
  174. /package/dist/{pi → sdk}/coding-agent/core/tools/truncate.js +0 -0
  175. /package/dist/{pi → sdk}/coding-agent/main.js +0 -0
  176. /package/dist/{pi → sdk}/coding-agent/modes/print-mode.js +0 -0
  177. /package/dist/{pi → sdk}/coding-agent/modes/rpc/jsonl.js +0 -0
  178. /package/dist/{pi → sdk}/coding-agent/modes/rpc/rpc-client.js +0 -0
  179. /package/dist/{pi → sdk}/coding-agent/modes/rpc/rpc-types.js +0 -0
  180. /package/dist/{pi → sdk}/coding-agent/utils/ansi.js +0 -0
  181. /package/dist/{pi → sdk}/coding-agent/utils/changelog.js +0 -0
  182. /package/dist/{pi → sdk}/coding-agent/utils/child-process.js +0 -0
  183. /package/dist/{pi → sdk}/coding-agent/utils/clipboard-image.js +0 -0
  184. /package/dist/{pi → sdk}/coding-agent/utils/clipboard-native.js +0 -0
  185. /package/dist/{pi → sdk}/coding-agent/utils/clipboard.js +0 -0
  186. /package/dist/{pi → sdk}/coding-agent/utils/exif-orientation.js +0 -0
  187. /package/dist/{pi → sdk}/coding-agent/utils/frontmatter.js +0 -0
  188. /package/dist/{pi → sdk}/coding-agent/utils/fs-watch.js +0 -0
  189. /package/dist/{pi → sdk}/coding-agent/utils/git.js +0 -0
  190. /package/dist/{pi → sdk}/coding-agent/utils/html.js +0 -0
  191. /package/dist/{pi → sdk}/coding-agent/utils/image-convert.js +0 -0
  192. /package/dist/{pi → sdk}/coding-agent/utils/image-resize.js +0 -0
  193. /package/dist/{pi → sdk}/coding-agent/utils/mime.js +0 -0
  194. /package/dist/{pi → sdk}/coding-agent/utils/paths.js +0 -0
  195. /package/dist/{pi → sdk}/coding-agent/utils/shell.js +0 -0
  196. /package/dist/{pi → sdk}/coding-agent/utils/sleep.js +0 -0
  197. /package/dist/{pi → sdk}/coding-agent/utils/syntax-highlight.js +0 -0
@@ -0,0 +1,202 @@
1
+ /**
2
+ * Minimal shared theme for syntax highlighting and extension API compatibility.
3
+ *
4
+ * This replaces the interactive/mode TUI theme system with a stripped-down
5
+ * version that supports the core utilities used by the serve/relay path:
6
+ * - getLanguageFromPath(): maps file extensions to highlight.js language IDs
7
+ * - highlightCode(): syntax-highlights code blocks
8
+ * - loadThemeFromPath(): loads a theme JSON file from disk
9
+ * - Theme class: used by resource-loader and extension types
10
+ *
11
+ * All ANSI color formatting (theme.fg(), theme.bold(), etc.) is removed —
12
+ * tool output is plain text for LLM consumption in serve/relay mode.
13
+ */
14
+ import * as fs from "node:fs";
15
+ import { highlight, supportsLanguage } from "../utils/syntax-highlight.js";
16
+ // ============================================================================
17
+ // Language detection
18
+ // ============================================================================
19
+ const extToLang = {
20
+ ts: "typescript",
21
+ tsx: "typescript",
22
+ js: "javascript",
23
+ jsx: "javascript",
24
+ mjs: "javascript",
25
+ cjs: "javascript",
26
+ py: "python",
27
+ rb: "ruby",
28
+ rs: "rust",
29
+ go: "go",
30
+ java: "java",
31
+ kt: "kotlin",
32
+ swift: "swift",
33
+ c: "c",
34
+ h: "c",
35
+ cpp: "cpp",
36
+ cc: "cpp",
37
+ cxx: "cpp",
38
+ hpp: "cpp",
39
+ cs: "csharp",
40
+ php: "php",
41
+ sh: "bash",
42
+ bash: "bash",
43
+ zsh: "bash",
44
+ fish: "fish",
45
+ ps1: "powershell",
46
+ sql: "sql",
47
+ html: "html",
48
+ htm: "html",
49
+ css: "css",
50
+ scss: "scss",
51
+ sass: "sass",
52
+ less: "less",
53
+ json: "json",
54
+ yaml: "yaml",
55
+ yml: "yaml",
56
+ toml: "toml",
57
+ xml: "xml",
58
+ md: "markdown",
59
+ markdown: "markdown",
60
+ dockerfile: "dockerfile",
61
+ makefile: "makefile",
62
+ cmake: "cmake",
63
+ lua: "lua",
64
+ perl: "perl",
65
+ r: "r",
66
+ scala: "scala",
67
+ clj: "clojure",
68
+ ex: "elixir",
69
+ exs: "elixir",
70
+ erl: "erlang",
71
+ hs: "haskell",
72
+ ml: "ocaml",
73
+ vim: "vim",
74
+ graphql: "graphql",
75
+ proto: "protobuf",
76
+ tf: "hcl",
77
+ hcl: "hcl",
78
+ };
79
+ export function getLanguageFromPath(filePath) {
80
+ const ext = filePath.split(".").pop()?.toLowerCase();
81
+ if (!ext)
82
+ return undefined;
83
+ return extToLang[ext];
84
+ }
85
+ // ============================================================================
86
+ // Syntax highlighting
87
+ // ============================================================================
88
+ /** Identity formatter — returns text as-is (no ANSI styling in serve mode). */
89
+ function identity(text) {
90
+ return text;
91
+ }
92
+ const neutralHighlightTheme = {
93
+ keyword: identity,
94
+ built_in: identity,
95
+ literal: identity,
96
+ number: identity,
97
+ string: identity,
98
+ comment: identity,
99
+ function: identity,
100
+ title: identity,
101
+ class: identity,
102
+ type: identity,
103
+ attr: identity,
104
+ variable: identity,
105
+ params: identity,
106
+ operator: identity,
107
+ punctuation: identity,
108
+ };
109
+ /**
110
+ * Highlight code with syntax coloring based on file extension or language.
111
+ * In serve mode, returns plain text (no ANSI coloring for LLM consumption).
112
+ */
113
+ export function highlightCode(code, lang) {
114
+ const validLang = lang && supportsLanguage(lang) ? lang : undefined;
115
+ if (!validLang) {
116
+ return code.split("\n");
117
+ }
118
+ try {
119
+ return highlight(code, {
120
+ language: validLang,
121
+ ignoreIllegals: true,
122
+ theme: neutralHighlightTheme,
123
+ }).split("\n");
124
+ }
125
+ catch {
126
+ return code.split("\n");
127
+ }
128
+ }
129
+ // ============================================================================
130
+ // Theme loading (for resource-loader)
131
+ // ============================================================================
132
+ /** Resolve built-in theme directory using config path helpers. */
133
+ function getThemesDir() {
134
+ try {
135
+ // This function is exported from config.ts and uses the actual package directory
136
+ const { getThemesDir: fn } = require("../../config.js");
137
+ return fn();
138
+ }
139
+ catch {
140
+ // Fallback: themes were in modes/interactive/theme/, try to find them
141
+ try {
142
+ const { join } = require("node:path");
143
+ const { existsSync } = require("node:fs");
144
+ const packageDir = join(__dirname, "../../..");
145
+ for (const base of ["src", "dist"]) {
146
+ const candidates = [
147
+ join(packageDir, base, "modes", "interactive", "theme"),
148
+ ];
149
+ for (const dir of candidates) {
150
+ if (existsSync(dir))
151
+ return dir;
152
+ }
153
+ }
154
+ }
155
+ catch {
156
+ // ignore
157
+ }
158
+ return __dirname;
159
+ }
160
+ }
161
+ function getBuiltinThemes() {
162
+ const themesDir = getThemesDir();
163
+ if (!themesDir)
164
+ return {};
165
+ try {
166
+ const { join } = require("node:path");
167
+ const { readFileSync } = require("node:fs");
168
+ return {
169
+ dark: JSON.parse(readFileSync(join(themesDir, "dark.json"), "utf-8")),
170
+ light: JSON.parse(readFileSync(join(themesDir, "light.json"), "utf-8")),
171
+ };
172
+ }
173
+ catch {
174
+ return {};
175
+ }
176
+ }
177
+ export function loadThemeFromPath(themePath) {
178
+ const content = fs.readFileSync(themePath, "utf-8");
179
+ const json = JSON.parse(content);
180
+ return {
181
+ name: json.name,
182
+ sourcePath: themePath,
183
+ };
184
+ }
185
+ // ============================================================================
186
+ // Registered themes (for extension API compatibility)
187
+ // ============================================================================
188
+ const registeredThemes = new Map();
189
+ export function setRegisteredThemes(themes) {
190
+ registeredThemes.clear();
191
+ for (const t of themes) {
192
+ if (t.name) {
193
+ registeredThemes.set(t.name, t);
194
+ }
195
+ }
196
+ }
197
+ export function getAvailableThemes() {
198
+ return Array.from(new Set([
199
+ ...Object.keys(getBuiltinThemes()),
200
+ ...registeredThemes.keys(),
201
+ ])).sort();
202
+ }
@@ -1,12 +1,10 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { spawn } from "child_process";
3
3
  import { Type } from "typebox";
4
- import { keyHint } from "../../modes/interactive/components/keybinding-hints.js";
5
- import { theme } from "../../modes/interactive/theme/theme.js";
6
4
  import { waitForChildProcess } from "../../utils/child-process.js";
7
5
  import { getShellConfig, getShellEnv, killProcessTree, trackDetachedChildPid, untrackDetachedChildPid, } from "../../utils/shell.js";
8
6
  import { OutputAccumulator } from "./output-accumulator.js";
9
- import { getTextOutput, invalidArgText, str } from "./render-utils.js";
7
+ import { getTextOutput, str } from "./render-utils.js";
10
8
  import { wrapToolDefinition } from "./tool-definition-wrapper.js";
11
9
  import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize } from "./truncate.js";
12
10
  const bashSchema = Type.Object({
@@ -103,12 +101,17 @@ const BASH_UPDATE_THROTTLE_MS = 100;
103
101
  function formatDuration(ms) {
104
102
  return `${(ms / 1000).toFixed(1)}s`;
105
103
  }
104
+ function plainText(s) { return s; }
105
+ const noopTheme = {
106
+ fg: (_name, text) => text,
107
+ bold: plainText,
108
+ };
106
109
  function formatBashCall(args) {
107
110
  const command = str(args?.command);
108
111
  const timeout = args?.timeout;
109
- const timeoutSuffix = timeout ? theme.fg("muted", ` (timeout ${timeout}s)`) : "";
110
- const commandDisplay = command === null ? invalidArgText(theme) : command ? command : theme.fg("toolOutput", "...");
111
- return theme.fg("toolTitle", theme.bold(`$ ${commandDisplay}`)) + timeoutSuffix;
112
+ const timeoutSuffix = timeout ? ` (timeout ${timeout}s)` : "";
113
+ const commandDisplay = command === null ? "[invalid]" : command || "...";
114
+ return `$ ${commandDisplay}${timeoutSuffix}`;
112
115
  }
113
116
  function formatBashResult(result, options, showImages, startedAt, endedAt) {
114
117
  let output = getTextOutput(result, showImages).trim();
@@ -122,24 +125,20 @@ function formatBashResult(result, options, showImages, startedAt, endedAt) {
122
125
  }
123
126
  const parts = [];
124
127
  if (output) {
125
- const styledOutput = output
126
- .split("\n")
127
- .map((line) => theme.fg("toolOutput", line))
128
- .join("\n");
129
128
  if (options.expanded) {
130
- parts.push(`\n${styledOutput}`);
129
+ parts.push(`\n${output}`);
131
130
  }
132
131
  else {
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")})`);
132
+ const lines = output.split("\n");
133
+ if (lines.length > BASH_PREVIEW_LINES) {
134
+ const hint = `... (${lines.length - BASH_PREVIEW_LINES} earlier lines)`;
136
135
  parts.push("");
137
136
  parts.push(hint);
138
- parts.push(...styledLines.slice(-BASH_PREVIEW_LINES));
137
+ parts.push(...lines.slice(-BASH_PREVIEW_LINES));
139
138
  }
140
139
  else {
141
140
  parts.push("");
142
- parts.push(...styledLines);
141
+ parts.push(...lines);
143
142
  }
144
143
  }
145
144
  }
@@ -156,12 +155,12 @@ function formatBashResult(result, options, showImages, startedAt, endedAt) {
156
155
  warnings.push(`Truncated: ${truncation.outputLines} lines shown (${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit)`);
157
156
  }
158
157
  }
159
- parts.push(`\n${theme.fg("warning", `[${warnings.join(". ")}]`)}`);
158
+ parts.push(`\n[${warnings.join(". ")}]`);
160
159
  }
161
160
  if (startedAt !== undefined) {
162
161
  const label = options.isPartial ? "Elapsed" : "Took";
163
162
  const endTime = endedAt ?? Date.now();
164
- parts.push(`\n${theme.fg("muted", `${label} ${formatDuration(endTime - startedAt)}`)}`);
163
+ parts.push(`\n${label} ${formatDuration(endTime - startedAt)}`);
165
164
  }
166
165
  return parts.join("");
167
166
  }
@@ -1,11 +1,11 @@
1
1
  import { constants } from "fs";
2
2
  import { access as fsAccess, readFile as fsReadFile, writeFile as fsWriteFile } from "fs/promises";
3
3
  import { Type } from "typebox";
4
- import { renderDiff } from "../../modes/interactive/components/diff.js";
4
+ import { renderDiff } from "./render-diff.js";
5
5
  import { applyEditsToNormalizedContent, detectLineEnding, generateDiffString, generateUnifiedPatch, normalizeToLF, restoreLineEndings, stripBom, } from "./edit-diff.js";
6
6
  import { withFileMutationQueue } from "./file-mutation-queue.js";
7
7
  import { resolveToCwd } from "./path-utils.js";
8
- import { invalidArgText, shortenPath, str } from "./render-utils.js";
8
+ import { shortenPath, str } from "./render-utils.js";
9
9
  import { wrapToolDefinition } from "./tool-definition-wrapper.js";
10
10
  const replaceEditSchema = Type.Object({
11
11
  oldText: Type.String({
@@ -53,14 +53,13 @@ function validateEditInput(input) {
53
53
  }
54
54
  return { path: input.path, edits: input.edits };
55
55
  }
56
- function formatEditCall(args, theme) {
57
- const invalidArg = invalidArgText(theme);
56
+ function formatEditCall(args, _theme) {
58
57
  const rawPath = str(args?.file_path ?? args?.path);
59
58
  const path = rawPath !== null ? shortenPath(rawPath) : null;
60
- const pathDisplay = path === null ? invalidArg : path ? theme.fg("accent", path) : theme.fg("toolOutput", "...");
61
- return `${theme.fg("toolTitle", theme.bold("edit"))} ${pathDisplay}`;
59
+ const pathDisplay = path === null ? "[invalid]" : path || "...";
60
+ return `edit ${pathDisplay}`;
62
61
  }
63
- function formatEditResult(args, result, theme, isError) {
62
+ function formatEditResult(args, result, _theme, isError) {
64
63
  const rawPath = str(args?.file_path ?? args?.path);
65
64
  if (isError) {
66
65
  const errorText = result.content
@@ -70,7 +69,7 @@ function formatEditResult(args, result, theme, isError) {
70
69
  if (!errorText) {
71
70
  return undefined;
72
71
  }
73
- return theme.fg("error", errorText);
72
+ return errorText;
74
73
  }
75
74
  const resultDiff = result.details?.diff;
76
75
  if (resultDiff) {
@@ -3,10 +3,9 @@ import { spawn } from "child_process";
3
3
  import { existsSync } from "fs";
4
4
  import path from "path";
5
5
  import { Type } from "typebox";
6
- import { keyHint } from "../../modes/interactive/components/keybinding-hints.js";
7
6
  import { ensureTool } from "../../utils/tools-manager.js";
8
7
  import { resolveToCwd } from "./path-utils.js";
9
- import { getTextOutput, invalidArgText, shortenPath, str } from "./render-utils.js";
8
+ import { getTextOutput, shortenPath, str } from "./render-utils.js";
10
9
  import { wrapToolDefinition } from "./tool-definition-wrapper.js";
11
10
  import { DEFAULT_MAX_BYTES, formatSize, truncateHead } from "./truncate.js";
12
11
  function toPosixPath(value) {
@@ -25,22 +24,19 @@ const defaultFindOperations = {
25
24
  // This is a placeholder. Actual fd execution happens in execute() when no custom glob is provided.
26
25
  glob: () => [],
27
26
  };
28
- function formatFindCall(args, theme) {
27
+ function formatFindCall(args, _theme) {
29
28
  const pattern = str(args?.pattern);
30
29
  const rawPath = str(args?.path);
31
30
  const path = rawPath !== null ? shortenPath(rawPath || ".") : null;
32
31
  const limit = args?.limit;
33
- const invalidArg = invalidArgText(theme);
34
- let text = theme.fg("toolTitle", theme.bold("find")) +
35
- " " +
36
- (pattern === null ? invalidArg : theme.fg("accent", pattern || "")) +
37
- theme.fg("toolOutput", ` in ${path === null ? invalidArg : path}`);
32
+ const invalidArg = "[invalid]";
33
+ let text = `find ${pattern === null ? invalidArg : pattern || ""} in ${path === null ? invalidArg : path}`;
38
34
  if (limit !== undefined) {
39
- text += theme.fg("toolOutput", ` (limit ${limit})`);
35
+ text += ` (limit ${limit})`;
40
36
  }
41
37
  return text;
42
38
  }
43
- function formatFindResult(result, options, theme, showImages) {
39
+ function formatFindResult(result, options, _theme, showImages) {
44
40
  const output = getTextOutput(result, showImages).trim();
45
41
  let text = "";
46
42
  if (output) {
@@ -48,9 +44,9 @@ function formatFindResult(result, options, theme, showImages) {
48
44
  const maxLines = options.expanded ? lines.length : 20;
49
45
  const displayLines = lines.slice(0, maxLines);
50
46
  const remaining = lines.length - maxLines;
51
- text += `\n${displayLines.map((line) => theme.fg("toolOutput", line)).join("\n")}`;
47
+ text += `\n${displayLines.join("\n")}`;
52
48
  if (remaining > 0) {
53
- text += `${theme.fg("muted", `\n... (${remaining} more lines,`)} ${keyHint("app.tools.expand", "to expand")})`;
49
+ text += `\n... (${remaining} more lines)`;
54
50
  }
55
51
  }
56
52
  const resultLimit = result.details?.resultLimitReached;
@@ -61,7 +57,7 @@ function formatFindResult(result, options, theme, showImages) {
61
57
  warnings.push(`${resultLimit} results limit`);
62
58
  if (truncation?.truncated)
63
59
  warnings.push(`${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit`);
64
- text += `\n${theme.fg("warning", `[Truncated: ${warnings.join(", ")}]`)}`;
60
+ text += `\n[Truncated: ${warnings.join(", ")}]`;
65
61
  }
66
62
  return text;
67
63
  }
@@ -3,10 +3,9 @@ import { spawn } from "child_process";
3
3
  import { readFileSync, statSync } from "fs";
4
4
  import path from "path";
5
5
  import { Type } from "typebox";
6
- import { keyHint } from "../../modes/interactive/components/keybinding-hints.js";
7
6
  import { ensureTool } from "../../utils/tools-manager.js";
8
7
  import { resolveToCwd } from "./path-utils.js";
9
- import { getTextOutput, invalidArgText, shortenPath, str } from "./render-utils.js";
8
+ import { getTextOutput, shortenPath, str } from "./render-utils.js";
10
9
  import { wrapToolDefinition } from "./tool-definition-wrapper.js";
11
10
  import { DEFAULT_MAX_BYTES, formatSize, GREP_MAX_LINE_LENGTH, truncateHead, truncateLine, } from "./truncate.js";
12
11
  const grepSchema = Type.Object({
@@ -23,24 +22,21 @@ const defaultGrepOperations = {
23
22
  isDirectory: (p) => statSync(p).isDirectory(),
24
23
  readFile: (p) => readFileSync(p, "utf-8"),
25
24
  };
26
- function formatGrepCall(args, theme) {
25
+ function formatGrepCall(args, _theme) {
27
26
  const pattern = str(args?.pattern);
28
27
  const rawPath = str(args?.path);
29
28
  const path = rawPath !== null ? shortenPath(rawPath || ".") : null;
30
29
  const glob = str(args?.glob);
31
30
  const limit = args?.limit;
32
- const invalidArg = invalidArgText(theme);
33
- let text = theme.fg("toolTitle", theme.bold("grep")) +
34
- " " +
35
- (pattern === null ? invalidArg : theme.fg("accent", `/${pattern || ""}/`)) +
36
- theme.fg("toolOutput", ` in ${path === null ? invalidArg : path}`);
31
+ const invalidArg = "[invalid]";
32
+ let text = `grep ${pattern === null ? invalidArg : `/${pattern || ""}/`} in ${path === null ? invalidArg : path}`;
37
33
  if (glob)
38
- text += theme.fg("toolOutput", ` (${glob})`);
34
+ text += ` (${glob})`;
39
35
  if (limit !== undefined)
40
- text += theme.fg("toolOutput", ` limit ${limit}`);
36
+ text += ` limit ${limit}`;
41
37
  return text;
42
38
  }
43
- function formatGrepResult(result, options, theme, showImages) {
39
+ function formatGrepResult(result, options, _theme, showImages) {
44
40
  const output = getTextOutput(result, showImages).trim();
45
41
  let text = "";
46
42
  if (output) {
@@ -48,9 +44,9 @@ function formatGrepResult(result, options, theme, showImages) {
48
44
  const maxLines = options.expanded ? lines.length : 15;
49
45
  const displayLines = lines.slice(0, maxLines);
50
46
  const remaining = lines.length - maxLines;
51
- text += `\n${displayLines.map((line) => theme.fg("toolOutput", line)).join("\n")}`;
47
+ text += `\n${displayLines.join("\n")}`;
52
48
  if (remaining > 0) {
53
- text += `${theme.fg("muted", `\n... (${remaining} more lines,`)} ${keyHint("app.tools.expand", "to expand")})`;
49
+ text += `\n... (${remaining} more lines)`;
54
50
  }
55
51
  }
56
52
  const matchLimit = result.details?.matchLimitReached;
@@ -64,7 +60,7 @@ function formatGrepResult(result, options, theme, showImages) {
64
60
  warnings.push(`${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit`);
65
61
  if (linesTruncated)
66
62
  warnings.push("some lines truncated");
67
- text += `\n${theme.fg("warning", `[Truncated: ${warnings.join(", ")}]`)}`;
63
+ text += `\n[Truncated: ${warnings.join(", ")}]`;
68
64
  }
69
65
  return text;
70
66
  }
@@ -1,9 +1,8 @@
1
1
  import { existsSync, readdirSync, statSync } from "fs";
2
2
  import nodePath from "path";
3
3
  import { Type } from "typebox";
4
- import { keyHint } from "../../modes/interactive/components/keybinding-hints.js";
5
4
  import { resolveToCwd } from "./path-utils.js";
6
- import { getTextOutput, invalidArgText, shortenPath, str } from "./render-utils.js";
5
+ import { getTextOutput, shortenPath, str } from "./render-utils.js";
7
6
  import { wrapToolDefinition } from "./tool-definition-wrapper.js";
8
7
  import { DEFAULT_MAX_BYTES, formatSize, truncateHead } from "./truncate.js";
9
8
  const lsSchema = Type.Object({
@@ -16,18 +15,18 @@ const defaultLsOperations = {
16
15
  stat: statSync,
17
16
  readdir: readdirSync,
18
17
  };
19
- function formatLsCall(args, theme) {
18
+ function formatLsCall(args, _theme) {
20
19
  const rawPath = str(args?.path);
21
20
  const path = rawPath !== null ? shortenPath(rawPath || ".") : null;
22
21
  const limit = args?.limit;
23
- const invalidArg = invalidArgText(theme);
24
- let text = `${theme.fg("toolTitle", theme.bold("ls"))} ${path === null ? invalidArg : theme.fg("accent", path)}`;
22
+ const invalidArg = "[invalid]";
23
+ let text = `ls ${path === null ? invalidArg : path}`;
25
24
  if (limit !== undefined) {
26
- text += theme.fg("toolOutput", ` (limit ${limit})`);
25
+ text += ` (limit ${limit})`;
27
26
  }
28
27
  return text;
29
28
  }
30
- function formatLsResult(result, options, theme, showImages) {
29
+ function formatLsResult(result, options, _theme, showImages) {
31
30
  const output = getTextOutput(result, showImages).trim();
32
31
  let text = "";
33
32
  if (output) {
@@ -35,9 +34,9 @@ function formatLsResult(result, options, theme, showImages) {
35
34
  const maxLines = options.expanded ? lines.length : 20;
36
35
  const displayLines = lines.slice(0, maxLines);
37
36
  const remaining = lines.length - maxLines;
38
- text += `\n${displayLines.map((line) => theme.fg("toolOutput", line)).join("\n")}`;
37
+ text += `\n${displayLines.join("\n")}`;
39
38
  if (remaining > 0) {
40
- text += `${theme.fg("muted", `\n... (${remaining} more lines,`)} ${keyHint("app.tools.expand", "to expand")})`;
39
+ text += `\n... (${remaining} more lines)`;
41
40
  }
42
41
  }
43
42
  const entryLimit = result.details?.entryLimitReached;
@@ -48,7 +47,7 @@ function formatLsResult(result, options, theme, showImages) {
48
47
  warnings.push(`${entryLimit} entries limit`);
49
48
  if (truncation?.truncated)
50
49
  warnings.push(`${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit`);
51
- text += `\n${theme.fg("warning", `[Truncated: ${warnings.join(", ")}]`)}`;
50
+ text += `\n[Truncated: ${warnings.join(", ")}]`;
52
51
  }
53
52
  return text;
54
53
  }
@@ -3,13 +3,12 @@ import { constants } from "fs";
3
3
  import { access as fsAccess, readFile as fsReadFile } from "fs/promises";
4
4
  import { Type } from "typebox";
5
5
  import { getReadmePath } from "../../config.js";
6
- import { keyHint, keyText } from "../../modes/interactive/components/keybinding-hints.js";
7
- import { getLanguageFromPath, highlightCode } from "../../modes/interactive/theme/theme.js";
6
+ import { getLanguageFromPath, highlightCode } from "../theme.js";
8
7
  import { formatDimensionNote, resizeImage } from "../../utils/image-resize.js";
9
8
  import { detectSupportedImageMimeTypeFromFile } from "../../utils/mime.js";
10
9
  import { formatPathRelativeToCwdOrAbsolute } from "../../utils/paths.js";
11
10
  import { resolveReadPath } from "./path-utils.js";
12
- import { getTextOutput, invalidArgText, replaceTabs, shortenPath, str } from "./render-utils.js";
11
+ import { getTextOutput, replaceTabs, shortenPath, str } from "./render-utils.js";
13
12
  import { wrapToolDefinition } from "./tool-definition-wrapper.js";
14
13
  import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, truncateHead } from "./truncate.js";
15
14
  const readSchema = Type.Object({
@@ -23,19 +22,18 @@ const defaultReadOperations = {
23
22
  access: (path) => fsAccess(path, constants.R_OK),
24
23
  detectImageMimeType: detectSupportedImageMimeTypeFromFile,
25
24
  };
26
- function formatReadLineRange(args, theme) {
25
+ function formatReadLineRange(args, _theme) {
27
26
  if (args?.offset === undefined && args?.limit === undefined)
28
27
  return "";
29
28
  const startLine = args.offset ?? 1;
30
29
  const endLine = args.limit !== undefined ? startLine + args.limit - 1 : "";
31
- return theme.fg("warning", `:${startLine}${endLine ? `-${endLine}` : ""}`);
30
+ return `:${startLine}${endLine ? `-${endLine}` : ""}`;
32
31
  }
33
- function formatReadCall(args, theme) {
32
+ function formatReadCall(args, _theme) {
34
33
  const rawPath = str(args?.file_path ?? args?.path);
35
34
  const path = rawPath !== null ? shortenPath(rawPath) : null;
36
- const invalidArg = invalidArgText(theme);
37
- const pathDisplay = path === null ? invalidArg : path ? theme.fg("accent", path) : theme.fg("toolOutput", "...");
38
- return `${theme.fg("toolTitle", theme.bold("read"))} ${pathDisplay}${formatReadLineRange(args, theme)}`;
35
+ const pathDisplay = path === null ? "[invalid]" : path || "...";
36
+ return `read ${pathDisplay}${formatReadLineRange(args)}`;
39
37
  }
40
38
  function trimTrailingEmptyLines(lines) {
41
39
  let end = lines.length;
@@ -85,19 +83,11 @@ function getCompactReadClassification(args, cwd) {
85
83
  }
86
84
  return undefined;
87
85
  }
88
- function formatCompactReadCall(classification, args, theme) {
89
- const expandHint = theme.fg("dim", ` (${keyText("app.tools.expand")} to expand)`);
86
+ function formatCompactReadCall(classification, args, _theme) {
90
87
  if (classification.kind === "skill") {
91
- return (theme.fg("customMessageLabel", `\x1b[1m[skill]\x1b[22m `) +
92
- theme.fg("customMessageText", classification.label) +
93
- formatReadLineRange(args, theme) +
94
- expandHint);
88
+ return `[skill] ${classification.label}${formatReadLineRange(args)}`;
95
89
  }
96
- return (theme.fg("toolTitle", theme.bold(`read ${classification.kind}`)) +
97
- " " +
98
- theme.fg("accent", classification.label) +
99
- formatReadLineRange(args, theme) +
100
- expandHint);
90
+ return `read ${classification.kind} ${classification.label}${formatReadLineRange(args)}`;
101
91
  }
102
92
  function formatReadResult(args, result, options, theme, showImages, cwd, isError) {
103
93
  if (!options.expanded && !isError && getCompactReadClassification(args, cwd)) {
@@ -111,20 +101,20 @@ function formatReadResult(args, result, options, theme, showImages, cwd, isError
111
101
  const maxLines = options.expanded ? lines.length : 10;
112
102
  const displayLines = lines.slice(0, maxLines);
113
103
  const remaining = lines.length - maxLines;
114
- let text = `\n${displayLines.map((line) => (lang ? replaceTabs(line) : theme.fg("toolOutput", replaceTabs(line)))).join("\n")}`;
104
+ let text = `\n${displayLines.map((line) => replaceTabs(line)).join("\n")}`;
115
105
  if (remaining > 0) {
116
- text += `${theme.fg("muted", `\n... (${remaining} more lines,`)} ${keyHint("app.tools.expand", "to expand")})`;
106
+ text += `\n... (${remaining} more lines)`;
117
107
  }
118
108
  const truncation = result.details?.truncation;
119
109
  if (truncation?.truncated) {
120
110
  if (truncation.firstLineExceedsLimit) {
121
- text += `\n${theme.fg("warning", `[First line exceeds ${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit]`)}`;
111
+ text += `\n[First line exceeds ${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit]`;
122
112
  }
123
113
  else if (truncation.truncatedBy === "lines") {
124
- text += `\n${theme.fg("warning", `[Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines (${truncation.maxLines ?? DEFAULT_MAX_LINES} line limit)]`)}`;
114
+ text += `\n[Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines (${truncation.maxLines ?? DEFAULT_MAX_LINES} line limit)]`;
125
115
  }
126
116
  else {
127
- text += `\n${theme.fg("warning", `[Truncated: ${truncation.outputLines} lines shown (${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit)]`)}`;
117
+ text += `\n[Truncated: ${truncation.outputLines} lines shown (${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit)]`;
128
118
  }
129
119
  }
130
120
  return text;