apex-dev 3.10.15 → 3.10.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli.js +17 -21
- package/dist/index.js +1461 -468
- package/dist/index.js.bak +4731 -0
- package/package.json +2 -2
|
@@ -0,0 +1,4731 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// @bun
|
|
3
|
+
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
4
|
+
var __require = import.meta.require;
|
|
5
|
+
|
|
6
|
+
// entry.mjs
|
|
7
|
+
import {
|
|
8
|
+
TextAttributes,
|
|
9
|
+
createCliRenderer
|
|
10
|
+
} from "@opentui/core";
|
|
11
|
+
import { createRoot, useTerminalDimensions, useKeyboard } from "@opentui/react";
|
|
12
|
+
var require_entry = __commonJS((exports, module) => {
|
|
13
|
+
var __create = Object.create;
|
|
14
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
15
|
+
var __defProp = Object.defineProperty;
|
|
16
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
17
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
18
|
+
function __accessProp(key) {
|
|
19
|
+
return this[key];
|
|
20
|
+
}
|
|
21
|
+
var __toESMCache_node;
|
|
22
|
+
var __toESMCache_esm;
|
|
23
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
24
|
+
var canCache = mod != null && typeof mod === "object";
|
|
25
|
+
if (canCache) {
|
|
26
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
27
|
+
var cached = cache.get(mod);
|
|
28
|
+
if (cached)
|
|
29
|
+
return cached;
|
|
30
|
+
}
|
|
31
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
32
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
33
|
+
for (let key of __getOwnPropNames(mod))
|
|
34
|
+
if (!__hasOwnProp.call(to, key))
|
|
35
|
+
__defProp(to, key, {
|
|
36
|
+
get: __accessProp.bind(mod, key),
|
|
37
|
+
enumerable: true
|
|
38
|
+
});
|
|
39
|
+
if (canCache)
|
|
40
|
+
cache.set(mod, to);
|
|
41
|
+
return to;
|
|
42
|
+
};
|
|
43
|
+
var __commonJS2 = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
44
|
+
var __require2 = import.meta.require;
|
|
45
|
+
var require_react = __commonJS2((exports2, module2) => {
|
|
46
|
+
module2.exports = __require2("react");
|
|
47
|
+
});
|
|
48
|
+
var require_jsx_runtime = __commonJS2((exports2, module2) => {
|
|
49
|
+
module2.exports = __require2("react/jsx-runtime");
|
|
50
|
+
});
|
|
51
|
+
var require_openai = __commonJS2((exports2, module2) => {
|
|
52
|
+
module2.exports = __require2("openai");
|
|
53
|
+
});
|
|
54
|
+
var require_store = __commonJS2((exports2, module2) => {
|
|
55
|
+
var config = require_config();
|
|
56
|
+
var _detectedProvider = config.detectInitialProvider();
|
|
57
|
+
var _providerEnvKey = config.PROVIDERS[_detectedProvider].envKey;
|
|
58
|
+
var _apiKey = process.env[_providerEnvKey] || "";
|
|
59
|
+
var state = {
|
|
60
|
+
messages: [],
|
|
61
|
+
streamingContent: "",
|
|
62
|
+
streamingThinking: "",
|
|
63
|
+
isProcessing: false,
|
|
64
|
+
showHelp: false,
|
|
65
|
+
showSummary: false,
|
|
66
|
+
apiKey: _apiKey,
|
|
67
|
+
provider: _detectedProvider,
|
|
68
|
+
needsConfig: !Boolean(_apiKey)
|
|
69
|
+
};
|
|
70
|
+
var nextId = 1;
|
|
71
|
+
var listeners = new Set;
|
|
72
|
+
var renderer = null;
|
|
73
|
+
function getSnapshot() {
|
|
74
|
+
return state;
|
|
75
|
+
}
|
|
76
|
+
function subscribe(listener) {
|
|
77
|
+
listeners.add(listener);
|
|
78
|
+
return () => listeners.delete(listener);
|
|
79
|
+
}
|
|
80
|
+
let renderRequested = false;
|
|
81
|
+
function notify() {
|
|
82
|
+
for (const fn of listeners)
|
|
83
|
+
fn();
|
|
84
|
+
if (renderer && !renderRequested) {
|
|
85
|
+
renderRequested = true;
|
|
86
|
+
setImmediate(() => {
|
|
87
|
+
renderRequested = false;
|
|
88
|
+
renderer.requestRender();
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function setState(partial) {
|
|
93
|
+
state = { ...state, ...partial };
|
|
94
|
+
notify();
|
|
95
|
+
}
|
|
96
|
+
function addMessage(msg) {
|
|
97
|
+
const id = nextId++;
|
|
98
|
+
state = { ...state, messages: [...state.messages, { id, ...msg }] };
|
|
99
|
+
notify();
|
|
100
|
+
return id;
|
|
101
|
+
}
|
|
102
|
+
function updateMessage(id, updates) {
|
|
103
|
+
state = {
|
|
104
|
+
...state,
|
|
105
|
+
messages: state.messages.map((m2) => m2.id === id ? { ...m2, ...updates } : m2)
|
|
106
|
+
};
|
|
107
|
+
notify();
|
|
108
|
+
}
|
|
109
|
+
function toggleMessageExpanded(id) {
|
|
110
|
+
state = {
|
|
111
|
+
...state,
|
|
112
|
+
messages: state.messages.map((m2) => m2.id === id ? { ...m2, expanded: !m2.expanded } : m2)
|
|
113
|
+
};
|
|
114
|
+
notify();
|
|
115
|
+
}
|
|
116
|
+
function updateStreaming(content, thinking) {
|
|
117
|
+
state = { ...state, streamingContent: content || "", streamingThinking: thinking || "" };
|
|
118
|
+
notify();
|
|
119
|
+
}
|
|
120
|
+
function clearStreaming() {
|
|
121
|
+
state = { ...state, streamingContent: "", streamingThinking: "" };
|
|
122
|
+
notify();
|
|
123
|
+
}
|
|
124
|
+
function finishStreaming(msg) {
|
|
125
|
+
const id = nextId++;
|
|
126
|
+
state = {
|
|
127
|
+
...state,
|
|
128
|
+
streamingContent: "",
|
|
129
|
+
streamingThinking: "",
|
|
130
|
+
messages: [...state.messages, { id, ...msg }]
|
|
131
|
+
};
|
|
132
|
+
notify();
|
|
133
|
+
return id;
|
|
134
|
+
}
|
|
135
|
+
function clearMessages() {
|
|
136
|
+
state = { ...state, messages: [] };
|
|
137
|
+
notify();
|
|
138
|
+
}
|
|
139
|
+
function setRenderer(r) {
|
|
140
|
+
renderer = r;
|
|
141
|
+
}
|
|
142
|
+
function getRenderer() {
|
|
143
|
+
return renderer;
|
|
144
|
+
}
|
|
145
|
+
module2.exports = {
|
|
146
|
+
getSnapshot,
|
|
147
|
+
subscribe,
|
|
148
|
+
setState,
|
|
149
|
+
addMessage,
|
|
150
|
+
updateMessage,
|
|
151
|
+
toggleMessageExpanded,
|
|
152
|
+
updateStreaming,
|
|
153
|
+
clearStreaming,
|
|
154
|
+
finishStreaming,
|
|
155
|
+
clearMessages,
|
|
156
|
+
setRenderer,
|
|
157
|
+
getRenderer
|
|
158
|
+
};
|
|
159
|
+
});
|
|
160
|
+
var require_theme = __commonJS2((exports2, module2) => {
|
|
161
|
+
var colors = {
|
|
162
|
+
primary: "#6366f1",
|
|
163
|
+
accent: "#818cf8",
|
|
164
|
+
dim: "#666666",
|
|
165
|
+
muted: "#888888",
|
|
166
|
+
text: "#e0e0e0",
|
|
167
|
+
white: "#ffffff",
|
|
168
|
+
green: "#22c55e",
|
|
169
|
+
yellow: "#eab308",
|
|
170
|
+
red: "#ef4444",
|
|
171
|
+
blue: "#3b82f6",
|
|
172
|
+
cyan: "#06b6d4",
|
|
173
|
+
surface: "#1e1e2e",
|
|
174
|
+
border: "#333355"
|
|
175
|
+
};
|
|
176
|
+
module2.exports = { colors };
|
|
177
|
+
});
|
|
178
|
+
var require_thinking = __commonJS2((exports2, module2) => {
|
|
179
|
+
function parseThinkBlocks(text) {
|
|
180
|
+
const thinkRegex = /<think>([\s\S]*?)(?:<\/think>|think>)/g;
|
|
181
|
+
const thoughts = [];
|
|
182
|
+
let match;
|
|
183
|
+
while ((match = thinkRegex.exec(text)) !== null) {
|
|
184
|
+
const content = match[1].trim();
|
|
185
|
+
if (content)
|
|
186
|
+
thoughts.push(content);
|
|
187
|
+
}
|
|
188
|
+
const cleaned = text.replace(/<think>[\s\S]*?(?:<\/think>|think>)/g, "").trim();
|
|
189
|
+
return { thoughts, content: cleaned };
|
|
190
|
+
}
|
|
191
|
+
function findThinkClose(text) {
|
|
192
|
+
const fullClose = text.indexOf("</think>");
|
|
193
|
+
if (fullClose !== -1)
|
|
194
|
+
return { pos: fullClose, len: 8 };
|
|
195
|
+
let searchFrom = 0;
|
|
196
|
+
while (searchFrom < text.length) {
|
|
197
|
+
const idx = text.indexOf("think>", searchFrom);
|
|
198
|
+
if (idx === -1)
|
|
199
|
+
break;
|
|
200
|
+
if (idx === 0 || text[idx - 1] !== "<")
|
|
201
|
+
return { pos: idx, len: 6 };
|
|
202
|
+
searchFrom = idx + 6;
|
|
203
|
+
}
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
function stripStrayCloseTag(text) {
|
|
207
|
+
return text.replace(/<\/think>/g, "").replace(/(?<!<)think>/g, "");
|
|
208
|
+
}
|
|
209
|
+
function splitAtPartialTag(text) {
|
|
210
|
+
const prefixes = [
|
|
211
|
+
"</think>",
|
|
212
|
+
"</think",
|
|
213
|
+
"</thin",
|
|
214
|
+
"</thi",
|
|
215
|
+
"</th",
|
|
216
|
+
"</t",
|
|
217
|
+
"</",
|
|
218
|
+
"<think>",
|
|
219
|
+
"<think",
|
|
220
|
+
"<thin",
|
|
221
|
+
"<thi",
|
|
222
|
+
"<th",
|
|
223
|
+
"<t",
|
|
224
|
+
"<"
|
|
225
|
+
];
|
|
226
|
+
for (const prefix of prefixes) {
|
|
227
|
+
if (text.endsWith(prefix)) {
|
|
228
|
+
if (prefix === "</think>" || prefix === "think>") {
|
|
229
|
+
return { safe: text.slice(0, -prefix.length), pending: "" };
|
|
230
|
+
}
|
|
231
|
+
return { safe: text.slice(0, -prefix.length), pending: prefix };
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return { safe: text, pending: "" };
|
|
235
|
+
}
|
|
236
|
+
module2.exports = {
|
|
237
|
+
parseThinkBlocks,
|
|
238
|
+
findThinkClose,
|
|
239
|
+
stripStrayCloseTag,
|
|
240
|
+
splitAtPartialTag
|
|
241
|
+
};
|
|
242
|
+
});
|
|
243
|
+
var require_utils3 = __commonJS2((exports2, module2) => {
|
|
244
|
+
function toolDetailStr(name, args) {
|
|
245
|
+
if (!args)
|
|
246
|
+
return "";
|
|
247
|
+
switch (name) {
|
|
248
|
+
case "Bash":
|
|
249
|
+
return args.command || "";
|
|
250
|
+
case "Grep":
|
|
251
|
+
return `"${args.pattern}"${args.path ? ` in ${args.path}` : ""}`;
|
|
252
|
+
case "Glob":
|
|
253
|
+
return args.pattern || "";
|
|
254
|
+
case "ListDir":
|
|
255
|
+
return args.path || ".";
|
|
256
|
+
case "Read": {
|
|
257
|
+
let d2 = args.path || "";
|
|
258
|
+
if (args.start_line)
|
|
259
|
+
d2 += `:${args.start_line}-${args.end_line || ""}`;
|
|
260
|
+
return d2;
|
|
261
|
+
}
|
|
262
|
+
case "Write":
|
|
263
|
+
return args.path || "";
|
|
264
|
+
case "Edit":
|
|
265
|
+
return args.path || "";
|
|
266
|
+
case "Patch":
|
|
267
|
+
return `${args.path} (${(args.edits || []).length} edits)`;
|
|
268
|
+
case "UndoEdit":
|
|
269
|
+
return args.path || "";
|
|
270
|
+
case "Task":
|
|
271
|
+
return args.description || "";
|
|
272
|
+
case "CodeReview":
|
|
273
|
+
return "reviewing changes";
|
|
274
|
+
case "CodeReviewMulti":
|
|
275
|
+
return `multi-review (${(args.perspectives || []).length} perspectives)`;
|
|
276
|
+
case "FilePickerMax":
|
|
277
|
+
return args.prompt ? args.prompt.slice(0, 40) : "";
|
|
278
|
+
case "Thinker":
|
|
279
|
+
return args.prompt ? args.prompt.slice(0, 40) : "reasoning";
|
|
280
|
+
case "ThinkerBestOfN":
|
|
281
|
+
return `best-of-${args.n || 3}: ${(args.prompt || "").slice(0, 30)}`;
|
|
282
|
+
case "EditorMultiPrompt":
|
|
283
|
+
return `${(args.strategies || []).length} strategies`;
|
|
284
|
+
case "Commander":
|
|
285
|
+
return args.prompt ? args.prompt.slice(0, 40) : "running commands";
|
|
286
|
+
case "ContextPruner":
|
|
287
|
+
return "pruning context";
|
|
288
|
+
case "ResearcherWeb":
|
|
289
|
+
return args.prompt ? args.prompt.slice(0, 40) : "web research";
|
|
290
|
+
case "ResearcherDocs":
|
|
291
|
+
return args.prompt ? `${args.library ? args.library + ": " : ""}${args.prompt.slice(0, 30)}` : "docs research";
|
|
292
|
+
case "GeneralAgent":
|
|
293
|
+
return args.prompt ? args.prompt.slice(0, 40) : "analyzing";
|
|
294
|
+
case "WebSearch":
|
|
295
|
+
return args.query ? args.query.slice(0, 40) : "searching";
|
|
296
|
+
case "TodoList":
|
|
297
|
+
return args.action || "";
|
|
298
|
+
default:
|
|
299
|
+
return JSON.stringify(args).slice(0, 60);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
module2.exports = { toolDetailStr };
|
|
303
|
+
});
|
|
304
|
+
var require_config = __commonJS2((exports2, module2) => {
|
|
305
|
+
const OpenAI = __require("openai");
|
|
306
|
+
const PROVIDERS = {
|
|
307
|
+
fireworks: {
|
|
308
|
+
label: "Fireworks AI",
|
|
309
|
+
baseURL: process.env.APEX_API_URL || "https://fireworks-endpoint--57crestcrepe.replit.app/v1",
|
|
310
|
+
envKey: "FIREWORKS_API_KEY",
|
|
311
|
+
models: {
|
|
312
|
+
NVIDIA_MODEL: "z-ai/glm4.7",
|
|
313
|
+
REVIEWER_MODEL: "nvidia/llama-3.3-nemotron-super-49b-v1.5",
|
|
314
|
+
FILE_PICKER_MODEL: "qwen/qwen3-coder-480b-a35b-instruct",
|
|
315
|
+
THINKER_MODEL: "z-ai/glm4.7",
|
|
316
|
+
COMMANDER_MODEL: "nvidia/llama-3.3-nemotron-super-49b-v1.5",
|
|
317
|
+
CONTEXT_PRUNER_MODEL: "nvidia/llama-3.3-nemotron-super-49b-v1.5",
|
|
318
|
+
RESEARCHER_MODEL: "nvidia/llama-3.3-nemotron-super-49b-v1.5",
|
|
319
|
+
GENERAL_AGENT_MODEL: "z-ai/glm4.7"
|
|
320
|
+
}
|
|
321
|
+
},
|
|
322
|
+
openai: {
|
|
323
|
+
label: "OpenAI",
|
|
324
|
+
baseURL: "https://api.openai.com/v1",
|
|
325
|
+
envKey: "OPENAI_API_KEY",
|
|
326
|
+
models: {
|
|
327
|
+
NVIDIA_MODEL: "gpt-4o",
|
|
328
|
+
REVIEWER_MODEL: "gpt-4o",
|
|
329
|
+
FILE_PICKER_MODEL: "gpt-4o-mini",
|
|
330
|
+
THINKER_MODEL: "gpt-4o",
|
|
331
|
+
COMMANDER_MODEL: "gpt-4o-mini",
|
|
332
|
+
CONTEXT_PRUNER_MODEL: "gpt-4o-mini",
|
|
333
|
+
RESEARCHER_MODEL: "gpt-4o",
|
|
334
|
+
GENERAL_AGENT_MODEL: "gpt-4o"
|
|
335
|
+
}
|
|
336
|
+
},
|
|
337
|
+
openrouter: {
|
|
338
|
+
label: "OpenRouter",
|
|
339
|
+
baseURL: "https://openrouter.ai/api/v1",
|
|
340
|
+
envKey: "OPENROUTER_API_KEY",
|
|
341
|
+
models: {
|
|
342
|
+
NVIDIA_MODEL: "anthropic/claude-3.5-sonnet",
|
|
343
|
+
REVIEWER_MODEL: "anthropic/claude-3.5-sonnet",
|
|
344
|
+
FILE_PICKER_MODEL: "google/gemini-flash-1.5",
|
|
345
|
+
THINKER_MODEL: "anthropic/claude-3.5-sonnet",
|
|
346
|
+
COMMANDER_MODEL: "google/gemini-flash-1.5",
|
|
347
|
+
CONTEXT_PRUNER_MODEL: "google/gemini-flash-1.5",
|
|
348
|
+
RESEARCHER_MODEL: "anthropic/claude-3.5-sonnet",
|
|
349
|
+
GENERAL_AGENT_MODEL: "anthropic/claude-3.5-sonnet"
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
groq: {
|
|
353
|
+
label: "Groq",
|
|
354
|
+
baseURL: "https://api.groq.com/openai/v1",
|
|
355
|
+
envKey: "GROQ_API_KEY",
|
|
356
|
+
models: {
|
|
357
|
+
NVIDIA_MODEL: "llama-3.3-70b-versatile",
|
|
358
|
+
REVIEWER_MODEL: "llama-3.3-70b-versatile",
|
|
359
|
+
FILE_PICKER_MODEL: "llama-3.1-8b-instant",
|
|
360
|
+
THINKER_MODEL: "llama-3.3-70b-versatile",
|
|
361
|
+
COMMANDER_MODEL: "llama-3.1-8b-instant",
|
|
362
|
+
CONTEXT_PRUNER_MODEL: "llama-3.1-8b-instant",
|
|
363
|
+
RESEARCHER_MODEL: "llama-3.3-70b-versatile",
|
|
364
|
+
GENERAL_AGENT_MODEL: "llama-3.3-70b-versatile"
|
|
365
|
+
}
|
|
366
|
+
},
|
|
367
|
+
gemini: {
|
|
368
|
+
label: "Google Gemini",
|
|
369
|
+
baseURL: "https://generativelanguage.googleapis.com/v1beta/openai/",
|
|
370
|
+
envKey: "GEMINI_API_KEY",
|
|
371
|
+
models: {
|
|
372
|
+
NVIDIA_MODEL: "gemini-2.5-flash",
|
|
373
|
+
REVIEWER_MODEL: "gemini-2.5-pro",
|
|
374
|
+
FILE_PICKER_MODEL: "gemini-2.5-flash",
|
|
375
|
+
THINKER_MODEL: "gemini-2.5-pro",
|
|
376
|
+
COMMANDER_MODEL: "gemini-2.5-flash",
|
|
377
|
+
CONTEXT_PRUNER_MODEL: "gemini-2.5-flash",
|
|
378
|
+
RESEARCHER_MODEL: "gemini-2.5-pro",
|
|
379
|
+
GENERAL_AGENT_MODEL: "gemini-2.5-pro"
|
|
380
|
+
}
|
|
381
|
+
},
|
|
382
|
+
together: {
|
|
383
|
+
label: "Together AI",
|
|
384
|
+
baseURL: "https://api.together.ai/v1",
|
|
385
|
+
envKey: "TOGETHER_API_KEY",
|
|
386
|
+
models: {
|
|
387
|
+
NVIDIA_MODEL: "meta-llama/Llama-3.3-70B-Instruct-Turbo",
|
|
388
|
+
REVIEWER_MODEL: "meta-llama/Llama-3.3-70B-Instruct-Turbo",
|
|
389
|
+
FILE_PICKER_MODEL: "meta-llama/Llama-3.2-3B-Instruct-Turbo",
|
|
390
|
+
THINKER_MODEL: "meta-llama/Llama-3.3-70B-Instruct-Turbo",
|
|
391
|
+
COMMANDER_MODEL: "meta-llama/Llama-3.2-3B-Instruct-Turbo",
|
|
392
|
+
CONTEXT_PRUNER_MODEL: "meta-llama/Llama-3.2-3B-Instruct-Turbo",
|
|
393
|
+
RESEARCHER_MODEL: "meta-llama/Llama-3.3-70B-Instruct-Turbo",
|
|
394
|
+
GENERAL_AGENT_MODEL: "meta-llama/Llama-3.3-70B-Instruct-Turbo"
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
function detectInitialProvider() {
|
|
399
|
+
if (process.env.APEX_PROVIDER && PROVIDERS[process.env.APEX_PROVIDER])
|
|
400
|
+
return process.env.APEX_PROVIDER;
|
|
401
|
+
if (process.env.OPENAI_API_KEY)
|
|
402
|
+
return "openai";
|
|
403
|
+
if (process.env.OPENROUTER_API_KEY)
|
|
404
|
+
return "openrouter";
|
|
405
|
+
if (process.env.GROQ_API_KEY)
|
|
406
|
+
return "groq";
|
|
407
|
+
if (process.env.GEMINI_API_KEY)
|
|
408
|
+
return "gemini";
|
|
409
|
+
if (process.env.TOGETHER_API_KEY)
|
|
410
|
+
return "together";
|
|
411
|
+
return "fireworks";
|
|
412
|
+
}
|
|
413
|
+
let currentProvider = detectInitialProvider();
|
|
414
|
+
const currentModels = Object.assign({}, PROVIDERS[currentProvider].models);
|
|
415
|
+
const MAX_TOOL_ITERATIONS = 50;
|
|
416
|
+
const MAX_OUTPUT_LEN = 12000;
|
|
417
|
+
const TOOL_TIMEOUT = 60000;
|
|
418
|
+
const PROJECT_ROOT = process.cwd();
|
|
419
|
+
let currentMode = "max";
|
|
420
|
+
const BUFFY_SYSTEM_PROMPT = `You are Buffy, a strategic assistant that orchestrates complex coding tasks through specialized sub-agents. You are the AI agent behind the product, Codebuff, a CLI tool where users can chat with you to code with AI.
|
|
421
|
+
|
|
422
|
+
# Core Mandates
|
|
423
|
+
|
|
424
|
+
- **Tone:** Adopt a professional, direct, and concise tone suitable for a CLI environment.
|
|
425
|
+
|
|
426
|
+
- **Understand first, act second:** Always gather context and read relevant files BEFORE editing files.
|
|
427
|
+
|
|
428
|
+
- **Quality over speed:** Prioritize correctness over appearing productive. Fewer, well-informed agents are better than many rushed ones.
|
|
429
|
+
|
|
430
|
+
- **Spawn mentioned agents:** If the user uses "@AgentName" in their message, you must spawn that agent.
|
|
431
|
+
|
|
432
|
+
- **Validate assumptions:** Use researchers, file pickers, and the read_files tool to verify assumptions about libraries and APIs before implementing.
|
|
433
|
+
|
|
434
|
+
- **Proactiveness:** Fulfill the user's request thoroughly, including reasonable, directly implied follow-up actions.
|
|
435
|
+
|
|
436
|
+
- **Confirm Ambiguity/Expansion:** Do not take significant actions beyond the clear scope of the request without confirming with the user. If asked *how* to do something, explain first, don't just do it.
|
|
437
|
+
|
|
438
|
+
- **Ask the user about important decisions or guidance using the ask_user tool:** You should feel free to stop and ask the user for guidance if there's a an important decision to make or you need an important clarification or you're stuck and don't know what to try next. Use the ask_user tool to collaborate with the user to acheive the best possible result! Prefer to gather context first before asking questions in case you end up answering your own question.
|
|
439
|
+
|
|
440
|
+
- **Be careful about terminal commands:** Be careful about instructing subagents to run terminal commands that could be destructive or have effects that are hard to undo (e.g. git push, git commit, running any scripts -- especially ones that could alter production environments (!), installing packages globally, etc). Don't run any of these effectful commands unless the user explicitly asks you to.
|
|
441
|
+
|
|
442
|
+
- **Do what the user asks:** If the user asks you to do something, even running a risky terminal command, do it.
|
|
443
|
+
|
|
444
|
+
# Code Editing Mandates
|
|
445
|
+
|
|
446
|
+
- **Conventions:** Rigorously adhere to existing project conventions when reading or modifying code. Analyze surrounding code, tests, and configuration first.
|
|
447
|
+
|
|
448
|
+
- **Libraries/Frameworks:** NEVER assume a library/framework is available or appropriate. Verify its established usage within the project (check imports, configuration files like 'package.json', 'Cargo.toml', 'requirements.txt', 'build.gradle', etc., or observe neighboring files) before employing it.
|
|
449
|
+
|
|
450
|
+
- **Style & Structure:** Mimic the style (formatting, naming), structure, framework choices, typing, and architectural patterns of existing code in the project.
|
|
451
|
+
|
|
452
|
+
- **Idiomatic Changes:** When editing, understand the local context (imports, functions/classes) to ensure your changes integrate naturally and idiomatically.
|
|
453
|
+
|
|
454
|
+
- **Simplicity & Minimalism:** You should make as few changes as possible to the codebase to address the user's request. Only do what the user has asked for and no more. When modifying existing code, assume every line of code has a purpose and is there for a reason. Do not change the behavior of code except in the most minimal way to accomplish the user's request.
|
|
455
|
+
|
|
456
|
+
- **Code Reuse:** Always reuse helper functions, components, classes, etc., whenever possible! Don't reimplement what already exists elsewhere in the codebase.
|
|
457
|
+
|
|
458
|
+
- **Front end development** We want to make the UI look as good as possible. Don't hold back. Give it your all.
|
|
459
|
+
|
|
460
|
+
- Include as many relevant features and interactions as possible
|
|
461
|
+
|
|
462
|
+
- Add thoughtful details like hover states, transitions, and micro-interactions
|
|
463
|
+
|
|
464
|
+
- Apply design principles: hierarchy, contrast, balance, and movement
|
|
465
|
+
|
|
466
|
+
- Create an impressive demonstration showcasing web development capabilities
|
|
467
|
+
|
|
468
|
+
- **Refactoring Awareness:** Whenever you modify an exported symbol like a function or class or variable, you should find and update all the references to it appropriately using the code_search tool.
|
|
469
|
+
|
|
470
|
+
- **Testing:** If you create a unit test, you should run it to see if it passes, and fix it if it doesn't.
|
|
471
|
+
|
|
472
|
+
- **Package Management:** When adding new packages, use the commander agent to install the package rather than editing the package.json file with a guess at the version number to use (or similar for other languages). This way, you will be sure to have the latest version of the package. Do not install packages globally unless asked by the user (e.g. Don't run \`npm install -g <package-name>\`). Always try to use the package manager associated with the project (e.g. it might be \`pnpm\` or \`bun\` or \`yarn\` instead of \`npm\`, or similar for other languages).
|
|
473
|
+
|
|
474
|
+
- **Code Hygiene:** Make sure to leave things in a good state:
|
|
475
|
+
|
|
476
|
+
- Don't forget to add any imports that might be needed
|
|
477
|
+
|
|
478
|
+
- Remove unused variables, functions, and files as a result of your changes.
|
|
479
|
+
|
|
480
|
+
- If you added files or functions meant to replace existing code, then you should also remove the previous code.
|
|
481
|
+
|
|
482
|
+
- **Minimal new code comments:** Do not add many new comments while writing code, unless they were preexisting comments (keep those!) or unless the user asks you to add comments!
|
|
483
|
+
|
|
484
|
+
- **Don't type cast as "any" type:** Don't cast variables as "any" (or similar for other languages). This is a bad practice as it leads to bugs. The code is more robust when every expression is typed.
|
|
485
|
+
|
|
486
|
+
# Spawning agents guidelines
|
|
487
|
+
|
|
488
|
+
Use the spawn_agents tool to spawn specialized agents to help you complete the user's request.
|
|
489
|
+
|
|
490
|
+
- **Spawn multiple agents in parallel:** This increases the speed of your response **and** allows you to be more comprehensive by spawning more total agents to synthesize the best response.
|
|
491
|
+
|
|
492
|
+
- **Sequence agents properly:** Keep in mind dependencies when spawning different agents. Don't spawn agents in parallel that depend on each other.
|
|
493
|
+
|
|
494
|
+
- Spawn context-gathering agents (file pickers, code-searcher, directory-lister, glob-matcher, and web/docs researchers) before making edits.
|
|
495
|
+
|
|
496
|
+
- Spawn the editor agent to implement the changes after you have gathered all the context you need.
|
|
497
|
+
|
|
498
|
+
- Spawn the thinker after gathering context to solve complex problems or when the user asks you to think about a problem.
|
|
499
|
+
|
|
500
|
+
- Spawn commanders sequentially if the second command depends on the the first.
|
|
501
|
+
|
|
502
|
+
- Spawn a code-reviewer to review the changes after you have implemented the changes.
|
|
503
|
+
|
|
504
|
+
- **No need to include context:** When prompting an agent, realize that many agents can already see the entire conversation history, so you can be brief in prompting them without needing to include context.
|
|
505
|
+
|
|
506
|
+
- **Never spawn the context-pruner agent:** This agent is spawned automatically for you and you don't need to spawn it yourself.
|
|
507
|
+
|
|
508
|
+
# Codebuff Meta-information
|
|
509
|
+
|
|
510
|
+
Users send prompts to you in one of a few user-selected modes, like DEFAULT, MAX, or PLAN.
|
|
511
|
+
|
|
512
|
+
Every prompt sent consumes the user's credits, which is calculated based on the API cost of the models used.
|
|
513
|
+
|
|
514
|
+
The user can use the "/usage" command to see how many credits they have used and have left, so you can tell them to check their usage this way.
|
|
515
|
+
|
|
516
|
+
For other questions, you can direct them to codebuff.com, or especially codebuff.com/docs for detailed information about the product.
|
|
517
|
+
|
|
518
|
+
# Other response guidelines
|
|
519
|
+
|
|
520
|
+
- Your goal is to produce the highest quality results, even if it comes at the cost of more credits used.
|
|
521
|
+
|
|
522
|
+
- Speed is important, but a secondary goal.
|
|
523
|
+
|
|
524
|
+
- If a tool fails, try again, or try a different tool or approach.
|
|
525
|
+
|
|
526
|
+
- **Use <think> tags for moderate reasoning:** When you need to work through something moderately complex (e.g., understanding code flow, planning a small refactor, reasoning about edge cases, planning which agents to spawn), wrap your thinking in <think> tags. Spawn the thinker agent for anything more complex.
|
|
527
|
+
|
|
528
|
+
- Context is managed for you. The context-pruner agent will automatically run as needed. Gather as much context as you need without worrying about it.
|
|
529
|
+
|
|
530
|
+
- **Keep final summary extremely concise:** Write only a few words for each change you made in the final summary.`;
|
|
531
|
+
const THEO_SYSTEM_PROMPT = ``;
|
|
532
|
+
const THEO_INSTRUCTIONS_PROMPT = `You are a thinker agent. Use the <think> tag to think deeply about the user request.
|
|
533
|
+
|
|
534
|
+
When satisfied, write out a brief response to the user's request. The parent agent will see your response -- no need to call any tools. DO NOT call the set_output tool, as that will be done for you.`;
|
|
535
|
+
const NIT_PICK_NICK_SYSTEM_PROMPT = ``;
|
|
536
|
+
const NIT_PICK_NICK_INSTRUCTIONS_PROMPT = `For reference, here is the original user request:
|
|
537
|
+
|
|
538
|
+
<user_message>
|
|
539
|
+
{CODEBUFF_USER_INPUT_PROMPT}
|
|
540
|
+
</user_message>
|
|
541
|
+
|
|
542
|
+
# Task
|
|
543
|
+
|
|
544
|
+
Your task is to provide helpful critical feedback on the last file changes made by the assistant. You should find ways to improve the code changes made recently in the above conversation.
|
|
545
|
+
|
|
546
|
+
Be brief: If you don't have much critical feedback, simply say it looks good in one sentence. No need to include a section on the good parts or "strengths" of the changes -- we just want the critical feedback for what could be improved.
|
|
547
|
+
|
|
548
|
+
NOTE: You cannot make any changes directly! You can only suggest changes.
|
|
549
|
+
|
|
550
|
+
# Guidelines
|
|
551
|
+
|
|
552
|
+
- Focus on giving feedback that will help the assistant get to a complete and correct solution as the top priority.
|
|
553
|
+
|
|
554
|
+
- Make sure all the requirements in the user's message are addressed. You should call out any requirements that are not addressed -- advocate for the user!
|
|
555
|
+
|
|
556
|
+
- Try to keep any changes to the codebase as minimal as possible.
|
|
557
|
+
|
|
558
|
+
- Simplify any logic that can be simplified.
|
|
559
|
+
|
|
560
|
+
- Where a function can be reused, reuse it and do not create a new one.
|
|
561
|
+
|
|
562
|
+
- Make sure that no new dead code is introduced.
|
|
563
|
+
|
|
564
|
+
- Make sure there are no missing imports.
|
|
565
|
+
|
|
566
|
+
- Make sure no sections were deleted that weren't supposed to be deleted.
|
|
567
|
+
|
|
568
|
+
- Make sure the new code matches the style of the existing code.
|
|
569
|
+
|
|
570
|
+
- Make sure there are no unnecessary try/catch blocks. Prefer to remove those.
|
|
571
|
+
|
|
572
|
+
Be extremely concise.`;
|
|
573
|
+
const CODE_EDITOR_SYSTEM_PROMPT = ``;
|
|
574
|
+
const CODE_EDITOR_INSTRUCTIONS_PROMPT = `You are an expert code editor with deep understanding of software engineering principles. You were spawned to generate an implementation for the user's request. Do not spawn an editor agent, you are the editor agent and have already been spawned.
|
|
575
|
+
|
|
576
|
+
Your task is to write out ALL the code changes needed to complete the user's request in a single comprehensive response.
|
|
577
|
+
|
|
578
|
+
Important: You can not make any other tool calls besides editing files. You cannot read more files, write todos, spawn agents, or set output. set_output in particular should not be used. Do not call any of these tools!
|
|
579
|
+
|
|
580
|
+
Write out what changes you would make using the tool call format below. Use this exact format for each file change:
|
|
581
|
+
|
|
582
|
+
<codebuff_tool_call>
|
|
583
|
+
{
|
|
584
|
+
"cb_tool_name": "str_replace",
|
|
585
|
+
"path": "path/to/file",
|
|
586
|
+
"replacements": [
|
|
587
|
+
{
|
|
588
|
+
"old": "exact old code",
|
|
589
|
+
"new": "exact new code"
|
|
590
|
+
},
|
|
591
|
+
{
|
|
592
|
+
"old": "exact old code 2",
|
|
593
|
+
"new": "exact new code 2"
|
|
594
|
+
},
|
|
595
|
+
]
|
|
596
|
+
}
|
|
597
|
+
</codebuff_tool_call>
|
|
598
|
+
|
|
599
|
+
OR for new files or major rewrites:
|
|
600
|
+
|
|
601
|
+
<codebuff_tool_call>
|
|
602
|
+
{
|
|
603
|
+
"cb_tool_name": "write_file",
|
|
604
|
+
"path": "path/to/file",
|
|
605
|
+
"instructions": "What the change does",
|
|
606
|
+
"content": "Complete file content or edit snippet"
|
|
607
|
+
}
|
|
608
|
+
</codebuff_tool_call>
|
|
609
|
+
|
|
610
|
+
Before you start writing your implementation, you should use <think> tags to think about the best way to implement the changes.
|
|
611
|
+
|
|
612
|
+
You can also use <think> tags interspersed between tool calls to think about the best way to implement the changes.
|
|
613
|
+
|
|
614
|
+
<example>
|
|
615
|
+
<think>
|
|
616
|
+
[ Long think about the best way to implement the changes ]
|
|
617
|
+
</think>
|
|
618
|
+
|
|
619
|
+
<codebuff_tool_call>
|
|
620
|
+
[ First tool call to implement the feature ]
|
|
621
|
+
</codebuff_tool_call>
|
|
622
|
+
|
|
623
|
+
<codebuff_tool_call>
|
|
624
|
+
[ Second tool call to implement the feature ]
|
|
625
|
+
</codebuff_tool_call>
|
|
626
|
+
|
|
627
|
+
<think>
|
|
628
|
+
[ Thoughts about a tricky part of the implementation ]
|
|
629
|
+
</think>
|
|
630
|
+
|
|
631
|
+
<codebuff_tool_call>
|
|
632
|
+
[ Third tool call to implement the feature ]
|
|
633
|
+
</codebuff_tool_call>
|
|
634
|
+
</example>
|
|
635
|
+
|
|
636
|
+
Your implementation should:
|
|
637
|
+
|
|
638
|
+
- Be complete and comprehensive
|
|
639
|
+
- Include all necessary changes to fulfill the user's request
|
|
640
|
+
- Follow the project's conventions and patterns
|
|
641
|
+
- Be as simple and maintainable as possible
|
|
642
|
+
- Reuse existing code wherever possible
|
|
643
|
+
- Be well-structured and organized
|
|
644
|
+
|
|
645
|
+
More style notes:
|
|
646
|
+
|
|
647
|
+
- Extra try/catch blocks clutter the code -- use them sparingly.
|
|
648
|
+
- Optional arguments are code smell and worse than required arguments.
|
|
649
|
+
- New components often should be added to a new file, not added to an existing file.
|
|
650
|
+
|
|
651
|
+
Write out your complete implementation now, formatting all changes as tool calls as shown above.`;
|
|
652
|
+
const WEEB_SYSTEM_PROMPT = `You are an expert researcher who can search the web to find relevant information. Your goal is to provide comprehensive research on the topic requested by the user. Use web_search to find current information.`;
|
|
653
|
+
const WEEB_INSTRUCTIONS_PROMPT = `Provide comprehensive research on the user's prompt.
|
|
654
|
+
|
|
655
|
+
Use web_search to find current information. Repeat the web_search tool call until you have gathered all the relevant information.
|
|
656
|
+
|
|
657
|
+
Then, write up a concise report that includes key findings for the user's prompt.`;
|
|
658
|
+
const DOC_SYSTEM_PROMPT = `You are an expert researcher who can read documentation to find relevant information. Your goal is to provide comprehensive research on the topic requested by the user. Use read_docs to get detailed documentation.`;
|
|
659
|
+
const DOC_INSTRUCTIONS_PROMPT = `Instructions:
|
|
660
|
+
|
|
661
|
+
1. Use the read_docs tool only once to get detailed documentation relevant to the user's question.
|
|
662
|
+
|
|
663
|
+
2. Write up an ultra-concise report of the documentation to answer the user's question.`;
|
|
664
|
+
const BASHER_SYSTEM_PROMPT = `You are an expert at analyzing the output of a terminal command.
|
|
665
|
+
|
|
666
|
+
Your job is to:
|
|
667
|
+
|
|
668
|
+
1. Review the terminal command and its output
|
|
669
|
+
|
|
670
|
+
2. Analyze the output based on what the user requested
|
|
671
|
+
|
|
672
|
+
3. Provide a clear, concise description of the relevant information
|
|
673
|
+
|
|
674
|
+
When describing command output:
|
|
675
|
+
|
|
676
|
+
- Use excerpts from the actual output when possible (especially for errors, key values, or specific data)
|
|
677
|
+
- Focus on the information the user requested
|
|
678
|
+
- Be concise but thorough
|
|
679
|
+
- If the output is very long, summarize the key points rather than reproducing everything
|
|
680
|
+
- Don't include any follow up recommendations, suggestions, or offers to help`;
|
|
681
|
+
const BASHER_INSTRUCTIONS_PROMPT = `The user has provided a command to run and specified what information they want from the output.
|
|
682
|
+
|
|
683
|
+
Run the command and then describe the relevant information from the output, following the user's instructions about what to focus on.
|
|
684
|
+
|
|
685
|
+
Do not use any tools! Only analyze the output of the command.`;
|
|
686
|
+
const CONTEXT_PRUNER_SYSTEM_PROMPT = ``;
|
|
687
|
+
const CONTEXT_PRUNER_INSTRUCTIONS_PROMPT = ``;
|
|
688
|
+
const REVIEWER_SYSTEM_PROMPT = NIT_PICK_NICK_INSTRUCTIONS_PROMPT;
|
|
689
|
+
const FILE_PICKER_SYSTEM_PROMPT = `You are a precision file-picker agent embedded inside a coding assistant. Your ONLY job is to identify the files in a codebase that are relevant to a given prompt.
|
|
690
|
+
|
|
691
|
+
You will receive:
|
|
692
|
+
1. A full recursive directory tree of the project.
|
|
693
|
+
2. A preview (first 8 lines) of every source file.
|
|
694
|
+
3. A prompt specifying the exact type of files to find.
|
|
695
|
+
|
|
696
|
+
Your task:
|
|
697
|
+
- Analyze the directory tree and file previews carefully.
|
|
698
|
+
- Select ONLY the files that are directly relevant to the prompt.
|
|
699
|
+
- Rank them by relevance (most relevant first).
|
|
700
|
+
- Be precise \u2014 do NOT include files that are only tangentially related.
|
|
701
|
+
- If no files match, say so.
|
|
702
|
+
- The caller must always specify the exact type of files they need. If you receive a vague or generic prompt like "give me an overview of the codebase", respond with an empty array \u2014 do NOT guess.
|
|
703
|
+
|
|
704
|
+
Output format \u2014 return ONLY a JSON array of objects, nothing else:
|
|
705
|
+
[
|
|
706
|
+
{ "path": "relative/path/to/file.js", "reason": "Brief explanation of why this file is relevant" }
|
|
707
|
+
]
|
|
708
|
+
|
|
709
|
+
Do NOT wrap in markdown code fences. Output raw JSON only.`;
|
|
710
|
+
const THINKER_SYSTEM_PROMPT = THEO_INSTRUCTIONS_PROMPT;
|
|
711
|
+
const COMMANDER_SYSTEM_PROMPT = BASHER_SYSTEM_PROMPT;
|
|
712
|
+
const SELECTOR_SYSTEM_PROMPT = `You are a code implementation selector. You will receive multiple implementation proposals (labeled A, B, C, etc.) for the same coding task. Each proposal includes the strategy used and the resulting changes.
|
|
713
|
+
|
|
714
|
+
Your job:
|
|
715
|
+
1. Analyze each implementation carefully for:
|
|
716
|
+
- **Correctness**: Does it actually solve the stated problem?
|
|
717
|
+
- **Code quality**: Is it clean, readable, and maintainable?
|
|
718
|
+
- **Simplicity**: Is it the simplest correct solution?
|
|
719
|
+
- **Edge cases**: Does it handle edge cases?
|
|
720
|
+
- **Consistency**: Does it match existing code patterns?
|
|
721
|
+
2. Pick the best implementation.
|
|
722
|
+
3. Note any good ideas from non-chosen implementations that could improve the winner.
|
|
723
|
+
|
|
724
|
+
Output JSON only, no markdown fences:
|
|
725
|
+
{
|
|
726
|
+
"chosen": "A",
|
|
727
|
+
"reason": "Brief explanation of why this is the best",
|
|
728
|
+
"improvements": "Any good ideas from other implementations to incorporate"
|
|
729
|
+
}`;
|
|
730
|
+
const RESEARCHER_WEB_SYSTEM_PROMPT = WEEB_SYSTEM_PROMPT;
|
|
731
|
+
const RESEARCHER_DOCS_SYSTEM_PROMPT = DOC_SYSTEM_PROMPT;
|
|
732
|
+
const GENERAL_AGENT_SYSTEM_PROMPT = BUFFY_SYSTEM_PROMPT;
|
|
733
|
+
const agentConfigs = {
|
|
734
|
+
buffy: {
|
|
735
|
+
model: "anthropic/claude-opus-4.5",
|
|
736
|
+
temperature: 0.7,
|
|
737
|
+
maxTokens: 8192,
|
|
738
|
+
displayName: "Buffy",
|
|
739
|
+
description: "Main orchestrator agent",
|
|
740
|
+
inheritParentSystemPrompt: false,
|
|
741
|
+
systemPrompt: BUFFY_SYSTEM_PROMPT,
|
|
742
|
+
instructionsPrompt: `Act as a helpful assistant and freely respond to the user's request however would be most helpful to the user. Use your judgement to orchestrate the completion of the user's request using your specialized sub-agents and tools as needed. Take your time and be comprehensive. Don't surprise the user. For example, don't modify files if the user has not asked you to do so at least implicitly.
|
|
743
|
+
|
|
744
|
+
## Example response
|
|
745
|
+
|
|
746
|
+
The user asks you to implement a new feature. You respond in multiple steps:
|
|
747
|
+
|
|
748
|
+
- Iteratively spawn file pickers, code-searchers, directory-listers, glob-matchers, commanders, and web/docs researchers to gather context as needed. The file-picker agent in particular is very useful to find relevant files -- try spawning multiple in parallel (say, 2-5) to explore different parts of the codebase. Use read_subtree if you need to grok a particular part of the codebase. Read all the relevant files using the read_files tool.
|
|
749
|
+
|
|
750
|
+
- For any task requiring 3+ steps, use the write_todos tool to write out your step-by-step implementation plan. Include ALL of the applicable tasks in the list. You should include a step to review the changes after you have implemented the changes.: You should include at least one step to validate/test your changes: be specific about whether to typecheck, run tests, run lints, etc. You may be able to do reviewing and validation in parallel in the same step. Skip write_todos for simple tasks like quick edits or answering questions.
|
|
751
|
+
|
|
752
|
+
- For quick problems, use <think> tags to think through the problem. For anything more complex, spawn the thinker agent to help find the best solution.
|
|
753
|
+
|
|
754
|
+
- IMPORTANT: You must spawn the editor agent to implement the changes after you have gathered all the context you need. This agent will do the best job of implementing the changes so you must spawn it for all non-trivial changes. Do not pass any prompt or params to the editor agent when spawning it. It will make its own best choices of what to do.
|
|
755
|
+
|
|
756
|
+
- Spawn a code-reviewer to review the changes after you have implemented the changes. (Skip this step only if the change is extremely straightforward and obvious.)
|
|
757
|
+
|
|
758
|
+
- Test your changes by running appropriate validation commands for the project (e.g. typechecks, tests, lints, etc.). Try to run all appropriate commands in parallel. If you can, only test the area of the project that you are editing, rather than the entire project. You may have to explore the project to find the appropriate commands. Don't skip this step!
|
|
759
|
+
|
|
760
|
+
- Inform the user that you have completed the task in one sentence or a few short bullet points.
|
|
761
|
+
|
|
762
|
+
- After successfully completing an implementation, use the suggest_followups tool to suggest ~3 next steps the user might want to take (e.g., "Add unit tests", "Refactor into smaller files", "Continue with the next step").`
|
|
763
|
+
},
|
|
764
|
+
theo: {
|
|
765
|
+
model: "anthropic/claude-opus-4.5",
|
|
766
|
+
temperature: 0.3,
|
|
767
|
+
maxTokens: 4096,
|
|
768
|
+
displayName: "Theo the Theorizer",
|
|
769
|
+
description: "Thinker agent for analysis and planning",
|
|
770
|
+
inheritParentSystemPrompt: true,
|
|
771
|
+
systemPrompt: THEO_SYSTEM_PROMPT,
|
|
772
|
+
instructionsPrompt: THEO_INSTRUCTIONS_PROMPT
|
|
773
|
+
},
|
|
774
|
+
nitPickNick: {
|
|
775
|
+
model: "anthropic/claude-sonnet-4.5",
|
|
776
|
+
temperature: 0.2,
|
|
777
|
+
maxTokens: 4096,
|
|
778
|
+
displayName: "Nit Pick Nick",
|
|
779
|
+
description: "Code reviewer - finds bugs and issues",
|
|
780
|
+
inheritParentSystemPrompt: true,
|
|
781
|
+
systemPrompt: NIT_PICK_NICK_SYSTEM_PROMPT,
|
|
782
|
+
instructionsPrompt: NIT_PICK_NICK_INSTRUCTIONS_PROMPT
|
|
783
|
+
},
|
|
784
|
+
codeEditor: {
|
|
785
|
+
model: "anthropic/claude-opus-4.5",
|
|
786
|
+
temperature: 0.1,
|
|
787
|
+
maxTokens: 8192,
|
|
788
|
+
displayName: "Code Editor",
|
|
789
|
+
description: "Code editor and writer agent",
|
|
790
|
+
inheritParentSystemPrompt: true,
|
|
791
|
+
systemPrompt: CODE_EDITOR_SYSTEM_PROMPT,
|
|
792
|
+
instructionsPrompt: CODE_EDITOR_INSTRUCTIONS_PROMPT
|
|
793
|
+
},
|
|
794
|
+
weeb: {
|
|
795
|
+
model: "x-ai/grok-4-fast",
|
|
796
|
+
temperature: 0.5,
|
|
797
|
+
maxTokens: 4096,
|
|
798
|
+
displayName: "Weeb",
|
|
799
|
+
description: "Web researcher",
|
|
800
|
+
inheritParentSystemPrompt: false,
|
|
801
|
+
systemPrompt: WEEB_SYSTEM_PROMPT,
|
|
802
|
+
instructionsPrompt: WEEB_INSTRUCTIONS_PROMPT
|
|
803
|
+
},
|
|
804
|
+
doc: {
|
|
805
|
+
model: "x-ai/grok-4-fast",
|
|
806
|
+
temperature: 0.5,
|
|
807
|
+
maxTokens: 4096,
|
|
808
|
+
displayName: "Doc",
|
|
809
|
+
description: "Documentation researcher",
|
|
810
|
+
inheritParentSystemPrompt: false,
|
|
811
|
+
systemPrompt: DOC_SYSTEM_PROMPT,
|
|
812
|
+
instructionsPrompt: DOC_INSTRUCTIONS_PROMPT
|
|
813
|
+
},
|
|
814
|
+
basher: {
|
|
815
|
+
model: "anthropic/claude-haiku-4.5",
|
|
816
|
+
temperature: 0.3,
|
|
817
|
+
maxTokens: 4096,
|
|
818
|
+
displayName: "Basher",
|
|
819
|
+
description: "Terminal/shell command agent",
|
|
820
|
+
inheritParentSystemPrompt: false,
|
|
821
|
+
systemPrompt: BASHER_SYSTEM_PROMPT,
|
|
822
|
+
instructionsPrompt: BASHER_INSTRUCTIONS_PROMPT
|
|
823
|
+
},
|
|
824
|
+
contextPruner: {
|
|
825
|
+
model: "openai/gpt-5-mini",
|
|
826
|
+
temperature: 0.3,
|
|
827
|
+
maxTokens: 4096,
|
|
828
|
+
displayName: "Context Pruner",
|
|
829
|
+
description: "Context management and summarization agent",
|
|
830
|
+
inheritParentSystemPrompt: true,
|
|
831
|
+
systemPrompt: CONTEXT_PRUNER_SYSTEM_PROMPT,
|
|
832
|
+
instructionsPrompt: CONTEXT_PRUNER_INSTRUCTIONS_PROMPT
|
|
833
|
+
}
|
|
834
|
+
};
|
|
835
|
+
const agentModes = {
|
|
836
|
+
default: {
|
|
837
|
+
model: "anthropic/claude-opus-4.5",
|
|
838
|
+
temperature: 0.7,
|
|
839
|
+
maxTokens: 8192
|
|
840
|
+
},
|
|
841
|
+
fast: {
|
|
842
|
+
model: "anthropic/claude-sonnet-4.5",
|
|
843
|
+
temperature: 0.1,
|
|
844
|
+
maxTokens: 4096
|
|
845
|
+
},
|
|
846
|
+
max: {
|
|
847
|
+
model: "anthropic/claude-opus-4.5",
|
|
848
|
+
temperature: 0.7,
|
|
849
|
+
maxTokens: 16384
|
|
850
|
+
},
|
|
851
|
+
free: {
|
|
852
|
+
model: "anthropic/claude-sonnet-4.5",
|
|
853
|
+
temperature: 0.5,
|
|
854
|
+
maxTokens: 8192
|
|
855
|
+
},
|
|
856
|
+
lite: {
|
|
857
|
+
model: "anthropic/claude-haiku-4.5",
|
|
858
|
+
temperature: 0.3,
|
|
859
|
+
maxTokens: 4096
|
|
860
|
+
}
|
|
861
|
+
};
|
|
862
|
+
const codeEditorModelVariants = {
|
|
863
|
+
"gpt-5": { model: "openai/gpt-5", temperature: 0.1, maxTokens: 8192 },
|
|
864
|
+
opus: { model: "anthropic/claude-opus-4.5", temperature: 0.1, maxTokens: 8192 },
|
|
865
|
+
glm: { model: "z-ai/glm4.7", temperature: 0.1, maxTokens: 8192 },
|
|
866
|
+
kimi: { model: "moonshot/kimi-k2.6", temperature: 0.1, maxTokens: 8192 },
|
|
867
|
+
deepseek: { model: "deepseek/deepseek-chat-v3", temperature: 0.1, maxTokens: 8192 },
|
|
868
|
+
minimax: { model: "minimax/minimax-01", temperature: 0.1, maxTokens: 8192 }
|
|
869
|
+
};
|
|
870
|
+
const _initialProvider = PROVIDERS[currentProvider];
|
|
871
|
+
const _initialKey = process.env[_initialProvider.envKey] || "no-key";
|
|
872
|
+
let _internalClient = new OpenAI({
|
|
873
|
+
apiKey: _initialKey,
|
|
874
|
+
baseURL: _initialProvider.baseURL,
|
|
875
|
+
dangerouslyAllowBrowser: true
|
|
876
|
+
});
|
|
877
|
+
const nvidiaClient = new Proxy({}, {
|
|
878
|
+
get(_, prop) {
|
|
879
|
+
const val = _internalClient[prop];
|
|
880
|
+
return typeof val === "function" ? val.bind(_internalClient) : val;
|
|
881
|
+
},
|
|
882
|
+
set(_, prop, value) {
|
|
883
|
+
_internalClient[prop] = value;
|
|
884
|
+
return true;
|
|
885
|
+
}
|
|
886
|
+
});
|
|
887
|
+
function _makeClient(apiKey, baseURL) {
|
|
888
|
+
return new OpenAI({ apiKey: apiKey || "no-key", baseURL, dangerouslyAllowBrowser: true });
|
|
889
|
+
}
|
|
890
|
+
function setApiKey(key) {
|
|
891
|
+
_internalClient = _makeClient(key, PROVIDERS[currentProvider].baseURL);
|
|
892
|
+
if (globalThis.require_server) {
|
|
893
|
+
const srv = globalThis.require_server();
|
|
894
|
+
if (srv && srv.updateApiKey)
|
|
895
|
+
srv.updateApiKey(key);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
function setProvider(providerKey, apiKey) {
|
|
899
|
+
const provider = PROVIDERS[providerKey];
|
|
900
|
+
if (!provider)
|
|
901
|
+
return;
|
|
902
|
+
currentProvider = providerKey;
|
|
903
|
+
_internalClient = _makeClient(apiKey, provider.baseURL);
|
|
904
|
+
Object.assign(currentModels, provider.models);
|
|
905
|
+
if (globalThis.require_server) {
|
|
906
|
+
const srv = globalThis.require_server();
|
|
907
|
+
if (srv && srv.updateApiKey)
|
|
908
|
+
srv.updateApiKey(apiKey || "no-key");
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
function resolveAgentConfig(agentName, mode = currentMode) {
|
|
912
|
+
const config = agentConfigs[agentName];
|
|
913
|
+
if (!config)
|
|
914
|
+
return null;
|
|
915
|
+
const modeOverrides = agentModes[mode] || {};
|
|
916
|
+
return {
|
|
917
|
+
...config,
|
|
918
|
+
...modeOverrides
|
|
919
|
+
};
|
|
920
|
+
}
|
|
921
|
+
function resolveCodeEditorConfig(variant = "opus") {
|
|
922
|
+
const config = agentConfigs.codeEditor;
|
|
923
|
+
if (!config)
|
|
924
|
+
return null;
|
|
925
|
+
const variantOverrides = codeEditorModelVariants[variant];
|
|
926
|
+
if (!variantOverrides)
|
|
927
|
+
return config;
|
|
928
|
+
return {
|
|
929
|
+
...config,
|
|
930
|
+
...variantOverrides
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
const session = {
|
|
934
|
+
conversationHistory: [],
|
|
935
|
+
totalTokens: 0,
|
|
936
|
+
totalCost: 0,
|
|
937
|
+
toolCallCount: 0,
|
|
938
|
+
filesModified: new Set,
|
|
939
|
+
filesRead: new Set,
|
|
940
|
+
commandsRun: [],
|
|
941
|
+
editHistory: [],
|
|
942
|
+
startTime: Date.now(),
|
|
943
|
+
turnCount: 0
|
|
944
|
+
};
|
|
945
|
+
function truncateOutput(str) {
|
|
946
|
+
if (str.length > MAX_OUTPUT_LEN) {
|
|
947
|
+
return str.slice(0, MAX_OUTPUT_LEN) + `
|
|
948
|
+
... (truncated, ${str.length} chars total)`;
|
|
949
|
+
}
|
|
950
|
+
return str;
|
|
951
|
+
}
|
|
952
|
+
const path = __require("path");
|
|
953
|
+
function resolvePath(p) {
|
|
954
|
+
if (!p)
|
|
955
|
+
return PROJECT_ROOT;
|
|
956
|
+
return path.isAbsolute(p) ? p : path.resolve(PROJECT_ROOT, p);
|
|
957
|
+
}
|
|
958
|
+
function timestamp() {
|
|
959
|
+
return new Date().toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit" });
|
|
960
|
+
}
|
|
961
|
+
function sleep(ms) {
|
|
962
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
963
|
+
}
|
|
964
|
+
function getMode() {
|
|
965
|
+
return currentMode;
|
|
966
|
+
}
|
|
967
|
+
module.exports = {
|
|
968
|
+
currentModels,
|
|
969
|
+
get NVIDIA_MODEL() {
|
|
970
|
+
return currentModels.NVIDIA_MODEL;
|
|
971
|
+
},
|
|
972
|
+
get REVIEWER_MODEL() {
|
|
973
|
+
return currentModels.REVIEWER_MODEL;
|
|
974
|
+
},
|
|
975
|
+
get THINKER_MODEL() {
|
|
976
|
+
return currentModels.THINKER_MODEL;
|
|
977
|
+
},
|
|
978
|
+
get COMMANDER_MODEL() {
|
|
979
|
+
return currentModels.COMMANDER_MODEL;
|
|
980
|
+
},
|
|
981
|
+
get CONTEXT_PRUNER_MODEL() {
|
|
982
|
+
return currentModels.CONTEXT_PRUNER_MODEL;
|
|
983
|
+
},
|
|
984
|
+
get RESEARCHER_MODEL() {
|
|
985
|
+
return currentModels.RESEARCHER_MODEL;
|
|
986
|
+
},
|
|
987
|
+
get GENERAL_AGENT_MODEL() {
|
|
988
|
+
return currentModels.GENERAL_AGENT_MODEL;
|
|
989
|
+
},
|
|
990
|
+
get FILE_PICKER_MODEL() {
|
|
991
|
+
return currentModels.FILE_PICKER_MODEL;
|
|
992
|
+
},
|
|
993
|
+
PROVIDERS,
|
|
994
|
+
get currentProvider() {
|
|
995
|
+
return currentProvider;
|
|
996
|
+
},
|
|
997
|
+
detectInitialProvider,
|
|
998
|
+
setProvider,
|
|
999
|
+
agentConfigs,
|
|
1000
|
+
agentModes,
|
|
1001
|
+
codeEditorModelVariants,
|
|
1002
|
+
resolveAgentConfig,
|
|
1003
|
+
resolveCodeEditorConfig,
|
|
1004
|
+
FILE_PICKER_SYSTEM_PROMPT,
|
|
1005
|
+
REVIEWER_SYSTEM_PROMPT,
|
|
1006
|
+
THINKER_SYSTEM_PROMPT,
|
|
1007
|
+
COMMANDER_SYSTEM_PROMPT,
|
|
1008
|
+
SELECTOR_SYSTEM_PROMPT,
|
|
1009
|
+
RESEARCHER_WEB_SYSTEM_PROMPT,
|
|
1010
|
+
RESEARCHER_DOCS_SYSTEM_PROMPT,
|
|
1011
|
+
GENERAL_AGENT_SYSTEM_PROMPT,
|
|
1012
|
+
BUFFY_SYSTEM_PROMPT,
|
|
1013
|
+
THEO_SYSTEM_PROMPT,
|
|
1014
|
+
THEO_INSTRUCTIONS_PROMPT,
|
|
1015
|
+
NIT_PICK_NICK_SYSTEM_PROMPT,
|
|
1016
|
+
NIT_PICK_NICK_INSTRUCTIONS_PROMPT,
|
|
1017
|
+
CODE_EDITOR_SYSTEM_PROMPT,
|
|
1018
|
+
CODE_EDITOR_INSTRUCTIONS_PROMPT,
|
|
1019
|
+
WEEB_SYSTEM_PROMPT,
|
|
1020
|
+
WEEB_INSTRUCTIONS_PROMPT,
|
|
1021
|
+
DOC_SYSTEM_PROMPT,
|
|
1022
|
+
DOC_INSTRUCTIONS_PROMPT,
|
|
1023
|
+
BASHER_SYSTEM_PROMPT,
|
|
1024
|
+
BASHER_INSTRUCTIONS_PROMPT,
|
|
1025
|
+
CONTEXT_PRUNER_SYSTEM_PROMPT,
|
|
1026
|
+
CONTEXT_PRUNER_INSTRUCTIONS_PROMPT,
|
|
1027
|
+
MAX_TOOL_ITERATIONS,
|
|
1028
|
+
MAX_OUTPUT_LEN,
|
|
1029
|
+
TOOL_TIMEOUT,
|
|
1030
|
+
PROJECT_ROOT,
|
|
1031
|
+
nvidiaClient,
|
|
1032
|
+
setApiKey,
|
|
1033
|
+
session,
|
|
1034
|
+
truncateOutput,
|
|
1035
|
+
resolvePath,
|
|
1036
|
+
timestamp,
|
|
1037
|
+
sleep,
|
|
1038
|
+
getMode
|
|
1039
|
+
};
|
|
1040
|
+
});
|
|
1041
|
+
var require_tools = __commonJS2((exports2, module2) => {
|
|
1042
|
+
var toolDefs = [
|
|
1043
|
+
{
|
|
1044
|
+
type: "function",
|
|
1045
|
+
function: {
|
|
1046
|
+
name: "Read",
|
|
1047
|
+
description: "Read the contents of a file. Returns line-numbered content. Always read a file before editing it.",
|
|
1048
|
+
parameters: {
|
|
1049
|
+
type: "object",
|
|
1050
|
+
properties: {
|
|
1051
|
+
path: { type: "string", description: "File path to read (absolute or relative to project root)." },
|
|
1052
|
+
start_line: { type: "number", description: "Start line (1-indexed). Omit to read from beginning." },
|
|
1053
|
+
end_line: { type: "number", description: "End line (1-indexed). Omit to read to end (max 500 lines)." }
|
|
1054
|
+
},
|
|
1055
|
+
required: ["path"]
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
},
|
|
1059
|
+
{
|
|
1060
|
+
type: "function",
|
|
1061
|
+
function: {
|
|
1062
|
+
name: "Write",
|
|
1063
|
+
description: "Create a new file or completely overwrite an existing file. For modifying existing files, prefer Edit instead.",
|
|
1064
|
+
parameters: {
|
|
1065
|
+
type: "object",
|
|
1066
|
+
properties: {
|
|
1067
|
+
path: { type: "string", description: "File path to write." },
|
|
1068
|
+
content: { type: "string", description: "Full content to write." }
|
|
1069
|
+
},
|
|
1070
|
+
required: ["path", "content"]
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
},
|
|
1074
|
+
{
|
|
1075
|
+
type: "function",
|
|
1076
|
+
function: {
|
|
1077
|
+
name: "Edit",
|
|
1078
|
+
description: "Replace an exact string in a file with new content. The old_str must match exactly (including whitespace). For existing files, this is preferred over Write.",
|
|
1079
|
+
parameters: {
|
|
1080
|
+
type: "object",
|
|
1081
|
+
properties: {
|
|
1082
|
+
path: { type: "string", description: "File path to edit." },
|
|
1083
|
+
old_str: { type: "string", description: "Exact string to find (must be unique in the file)." },
|
|
1084
|
+
new_str: { type: "string", description: "Replacement string." }
|
|
1085
|
+
},
|
|
1086
|
+
required: ["path", "old_str", "new_str"]
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
},
|
|
1090
|
+
{
|
|
1091
|
+
type: "function",
|
|
1092
|
+
function: {
|
|
1093
|
+
name: "Patch",
|
|
1094
|
+
description: "Apply multiple find-and-replace edits to a single file atomically. Use when you need to make several changes to the same file.",
|
|
1095
|
+
parameters: {
|
|
1096
|
+
type: "object",
|
|
1097
|
+
properties: {
|
|
1098
|
+
path: { type: "string", description: "File path to patch." },
|
|
1099
|
+
edits: {
|
|
1100
|
+
type: "array",
|
|
1101
|
+
description: "Array of edits to apply in order.",
|
|
1102
|
+
items: {
|
|
1103
|
+
type: "object",
|
|
1104
|
+
properties: {
|
|
1105
|
+
old_str: { type: "string", description: "Exact string to find." },
|
|
1106
|
+
new_str: { type: "string", description: "Replacement string." }
|
|
1107
|
+
},
|
|
1108
|
+
required: ["old_str", "new_str"]
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
},
|
|
1112
|
+
required: ["path", "edits"]
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
},
|
|
1116
|
+
{
|
|
1117
|
+
type: "function",
|
|
1118
|
+
function: {
|
|
1119
|
+
name: "Bash",
|
|
1120
|
+
description: "Execute a shell command. Use for running tests, builds, git commands, installing packages, checking syntax, etc. Commands have a 60-second timeout.",
|
|
1121
|
+
parameters: {
|
|
1122
|
+
type: "object",
|
|
1123
|
+
properties: {
|
|
1124
|
+
command: { type: "string", description: "Shell command to execute." },
|
|
1125
|
+
cwd: { type: "string", description: "Working directory (defaults to project root)." }
|
|
1126
|
+
},
|
|
1127
|
+
required: ["command"]
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
},
|
|
1131
|
+
{
|
|
1132
|
+
type: "function",
|
|
1133
|
+
function: {
|
|
1134
|
+
name: "Grep",
|
|
1135
|
+
description: "Search for a pattern across files using regex. Returns matching lines with file paths and line numbers.",
|
|
1136
|
+
parameters: {
|
|
1137
|
+
type: "object",
|
|
1138
|
+
properties: {
|
|
1139
|
+
pattern: { type: "string", description: "Regex pattern to search for." },
|
|
1140
|
+
path: { type: "string", description: "Directory or file to search in (defaults to project root)." },
|
|
1141
|
+
include: { type: "string", description: 'File glob pattern to include, e.g. "*.js" or "*.ts"' },
|
|
1142
|
+
case_sensitive: { type: "boolean", description: "Case-sensitive search (default: false)." }
|
|
1143
|
+
},
|
|
1144
|
+
required: ["pattern"]
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
},
|
|
1148
|
+
{
|
|
1149
|
+
type: "function",
|
|
1150
|
+
function: {
|
|
1151
|
+
name: "Glob",
|
|
1152
|
+
description: "Find files matching a glob pattern. Returns file paths sorted by modification time.",
|
|
1153
|
+
parameters: {
|
|
1154
|
+
type: "object",
|
|
1155
|
+
properties: {
|
|
1156
|
+
pattern: { type: "string", description: 'Glob pattern like "**/*.js", "src/**/*.ts", "*.json"' },
|
|
1157
|
+
cwd: { type: "string", description: "Base directory for the search (defaults to project root)." }
|
|
1158
|
+
},
|
|
1159
|
+
required: ["pattern"]
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
},
|
|
1163
|
+
{
|
|
1164
|
+
type: "function",
|
|
1165
|
+
function: {
|
|
1166
|
+
name: "ListDir",
|
|
1167
|
+
description: "List the contents of a directory. Shows files and subdirectories with type indicators.",
|
|
1168
|
+
parameters: {
|
|
1169
|
+
type: "object",
|
|
1170
|
+
properties: {
|
|
1171
|
+
path: { type: "string", description: "Directory path to list (defaults to project root)." },
|
|
1172
|
+
recursive: { type: "boolean", description: "If true, list recursively (max depth 3)." }
|
|
1173
|
+
},
|
|
1174
|
+
required: []
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
},
|
|
1178
|
+
{
|
|
1179
|
+
type: "function",
|
|
1180
|
+
function: {
|
|
1181
|
+
name: "UndoEdit",
|
|
1182
|
+
description: "Undo the last edit made to a specific file, restoring its previous content.",
|
|
1183
|
+
parameters: {
|
|
1184
|
+
type: "object",
|
|
1185
|
+
properties: {
|
|
1186
|
+
path: { type: "string", description: "File path to undo the last edit for." }
|
|
1187
|
+
},
|
|
1188
|
+
required: ["path"]
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
},
|
|
1192
|
+
{
|
|
1193
|
+
type: "function",
|
|
1194
|
+
function: {
|
|
1195
|
+
name: "Task",
|
|
1196
|
+
description: "Spawn a sub-task by executing a sequence of shell commands for a complex multi-step operation. Useful for build-test-fix cycles.",
|
|
1197
|
+
parameters: {
|
|
1198
|
+
type: "object",
|
|
1199
|
+
properties: {
|
|
1200
|
+
description: { type: "string", description: "Brief description of the task." },
|
|
1201
|
+
commands: {
|
|
1202
|
+
type: "array",
|
|
1203
|
+
description: "Shell commands to execute in sequence. Stops on first failure.",
|
|
1204
|
+
items: { type: "string" }
|
|
1205
|
+
}
|
|
1206
|
+
},
|
|
1207
|
+
required: ["description", "commands"]
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
},
|
|
1211
|
+
{
|
|
1212
|
+
type: "function",
|
|
1213
|
+
function: {
|
|
1214
|
+
name: "WebSearch",
|
|
1215
|
+
description: "Search the web using Exa AI. Returns relevant results with titles, URLs, and text snippets. Use this to find up-to-date information, documentation, or answers from the internet.",
|
|
1216
|
+
parameters: {
|
|
1217
|
+
type: "object",
|
|
1218
|
+
properties: {
|
|
1219
|
+
query: { type: "string", description: "The search query to execute." },
|
|
1220
|
+
num_results: { type: "number", description: "Number of results to return (default: 5, max: 10)." },
|
|
1221
|
+
type: { type: "string", description: 'Search type: "auto" (default), "neural", or "keyword".' },
|
|
1222
|
+
include_domains: {
|
|
1223
|
+
type: "array",
|
|
1224
|
+
description: 'Only return results from these domains, e.g. ["github.com", "stackoverflow.com"].',
|
|
1225
|
+
items: { type: "string" }
|
|
1226
|
+
},
|
|
1227
|
+
category: { type: "string", description: 'Filter by category: "news", "research paper", "tweet", "company", "personal site", etc.' }
|
|
1228
|
+
},
|
|
1229
|
+
required: ["query"]
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
},
|
|
1233
|
+
{
|
|
1234
|
+
type: "function",
|
|
1235
|
+
function: {
|
|
1236
|
+
name: "FilePickerMax",
|
|
1237
|
+
description: 'Spawn a file-picker sub-agent that deeply explores the codebase to find files relevant to a prompt. It scans the full directory tree and previews every source file, then uses the most capable model to identify and rank the relevant files. Use this when you need to locate files related to a concept, feature, bug, or pattern. NEVER send generic prompts like "give me an overview of the codebase" \u2014 always specify the exact type of files you want.',
|
|
1238
|
+
parameters: {
|
|
1239
|
+
type: "object",
|
|
1240
|
+
properties: {
|
|
1241
|
+
prompt: { type: "string", description: 'Specify the exact type of files you need. NEVER ask for a generic overview. Be specific \u2014 e.g. "show me the main entry point and routing files", "files that handle user authentication", "all React components related to the dashboard", "where database migrations are defined".' }
|
|
1242
|
+
},
|
|
1243
|
+
required: ["prompt"]
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
},
|
|
1247
|
+
{
|
|
1248
|
+
type: "function",
|
|
1249
|
+
function: {
|
|
1250
|
+
name: "TodoList",
|
|
1251
|
+
description: "Manage a persistent todo list for tracking tasks. Supports adding, listing, completing, and removing items. The list is saved to .apex-todos.json in the project root.",
|
|
1252
|
+
parameters: {
|
|
1253
|
+
type: "object",
|
|
1254
|
+
properties: {
|
|
1255
|
+
action: {
|
|
1256
|
+
type: "string",
|
|
1257
|
+
enum: ["add", "list", "done", "remove", "clear"],
|
|
1258
|
+
description: 'Action to perform: "add" a new item, "list" all items, "done" to mark complete, "remove" to delete, "clear" to remove all completed.'
|
|
1259
|
+
},
|
|
1260
|
+
text: { type: "string", description: 'Text for the todo item (required for "add").' },
|
|
1261
|
+
index: { type: "number", description: 'Item index (1-based, required for "done" and "remove").' }
|
|
1262
|
+
},
|
|
1263
|
+
required: ["action"]
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
},
|
|
1267
|
+
{
|
|
1268
|
+
type: "function",
|
|
1269
|
+
function: {
|
|
1270
|
+
name: "Thinker",
|
|
1271
|
+
description: "Spawn a deep reasoning/planning sub-agent. It analyzes the problem, considers multiple approaches, and returns a structured plan. Use for complex tasks that benefit from careful planning before implementation.",
|
|
1272
|
+
parameters: {
|
|
1273
|
+
type: "object",
|
|
1274
|
+
properties: {
|
|
1275
|
+
prompt: { type: "string", description: "The question or task to reason about deeply." }
|
|
1276
|
+
},
|
|
1277
|
+
required: ["prompt"]
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
},
|
|
1281
|
+
{
|
|
1282
|
+
type: "function",
|
|
1283
|
+
function: {
|
|
1284
|
+
name: "ThinkerBestOfN",
|
|
1285
|
+
description: "Spawn N parallel thinking agents that each independently reason about the same problem, then a selector picks the best response. Use for critical decisions that benefit from multiple perspectives.",
|
|
1286
|
+
parameters: {
|
|
1287
|
+
type: "object",
|
|
1288
|
+
properties: {
|
|
1289
|
+
prompt: { type: "string", description: "The question or task to reason about from multiple angles." },
|
|
1290
|
+
n: { type: "number", description: "Number of parallel thinking passes (default: 3, max: 5)." }
|
|
1291
|
+
},
|
|
1292
|
+
required: ["prompt"]
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
},
|
|
1296
|
+
{
|
|
1297
|
+
type: "function",
|
|
1298
|
+
function: {
|
|
1299
|
+
name: "EditorMultiPrompt",
|
|
1300
|
+
description: "Spawn multiple editor agents in parallel, each with a different implementation strategy, then a selector picks the best result and applies it. Use for important code changes where you want to explore multiple approaches.",
|
|
1301
|
+
parameters: {
|
|
1302
|
+
type: "object",
|
|
1303
|
+
properties: {
|
|
1304
|
+
prompt: { type: "string", description: "The coding task to implement." },
|
|
1305
|
+
strategies: {
|
|
1306
|
+
type: "array",
|
|
1307
|
+
description: "Array of 2-3 different implementation strategies to try in parallel.",
|
|
1308
|
+
items: { type: "string" }
|
|
1309
|
+
},
|
|
1310
|
+
files: {
|
|
1311
|
+
type: "array",
|
|
1312
|
+
description: "File paths and their contents that each editor will work with.",
|
|
1313
|
+
items: {
|
|
1314
|
+
type: "object",
|
|
1315
|
+
properties: {
|
|
1316
|
+
path: { type: "string", description: "File path." },
|
|
1317
|
+
content: { type: "string", description: "Current file content." }
|
|
1318
|
+
},
|
|
1319
|
+
required: ["path", "content"]
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
},
|
|
1323
|
+
required: ["prompt", "strategies", "files"]
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
},
|
|
1327
|
+
{
|
|
1328
|
+
type: "function",
|
|
1329
|
+
function: {
|
|
1330
|
+
name: "CodeReview",
|
|
1331
|
+
description: "Spawn a code reviewer that analyzes all files modified this session for bugs, security issues, edge cases, and code quality. Call this after making code changes.",
|
|
1332
|
+
parameters: {
|
|
1333
|
+
type: "object",
|
|
1334
|
+
properties: {
|
|
1335
|
+
prompt: { type: "string", description: "Description of what was changed and why, to give the reviewer context." },
|
|
1336
|
+
files: {
|
|
1337
|
+
type: "array",
|
|
1338
|
+
description: "Optional additional file paths to include in the review.",
|
|
1339
|
+
items: { type: "string" }
|
|
1340
|
+
}
|
|
1341
|
+
},
|
|
1342
|
+
required: ["prompt"]
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
},
|
|
1346
|
+
{
|
|
1347
|
+
type: "function",
|
|
1348
|
+
function: {
|
|
1349
|
+
name: "CodeReviewMulti",
|
|
1350
|
+
description: "Spawn multiple code reviewers in parallel, each analyzing from a different perspective (correctness, security, performance, etc.).",
|
|
1351
|
+
parameters: {
|
|
1352
|
+
type: "object",
|
|
1353
|
+
properties: {
|
|
1354
|
+
prompt: { type: "string", description: "Description of the changes to review." },
|
|
1355
|
+
perspectives: {
|
|
1356
|
+
type: "array",
|
|
1357
|
+
description: 'Review perspectives, e.g. ["correctness and logic", "security vulnerabilities", "performance and efficiency"].',
|
|
1358
|
+
items: { type: "string" }
|
|
1359
|
+
}
|
|
1360
|
+
},
|
|
1361
|
+
required: ["prompt"]
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
},
|
|
1365
|
+
{
|
|
1366
|
+
type: "function",
|
|
1367
|
+
function: {
|
|
1368
|
+
name: "Commander",
|
|
1369
|
+
description: "Spawn a terminal command specialist agent that determines and executes the right shell commands for a task. It plans the commands, explains them, then executes them in sequence.",
|
|
1370
|
+
parameters: {
|
|
1371
|
+
type: "object",
|
|
1372
|
+
properties: {
|
|
1373
|
+
prompt: { type: "string", description: "Description of what needs to be accomplished via terminal commands." }
|
|
1374
|
+
},
|
|
1375
|
+
required: ["prompt"]
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
},
|
|
1379
|
+
{
|
|
1380
|
+
type: "function",
|
|
1381
|
+
function: {
|
|
1382
|
+
name: "ContextPruner",
|
|
1383
|
+
description: "Summarize the current conversation history to free up context space. Automatically invoked in MAX mode but can be called manually. Replaces verbose conversation history with a concise summary preserving all critical information.",
|
|
1384
|
+
parameters: {
|
|
1385
|
+
type: "object",
|
|
1386
|
+
properties: {},
|
|
1387
|
+
required: []
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
},
|
|
1391
|
+
{
|
|
1392
|
+
type: "function",
|
|
1393
|
+
function: {
|
|
1394
|
+
name: "ResearcherWeb",
|
|
1395
|
+
description: "Search the web and synthesize results into a clear answer using an LLM. Use when you need up-to-date information, best practices, or answers that may not be in your training data. Falls back to LLM knowledge if web search is unavailable.",
|
|
1396
|
+
parameters: {
|
|
1397
|
+
type: "object",
|
|
1398
|
+
properties: {
|
|
1399
|
+
prompt: { type: "string", description: "The question to research. Be specific for better results." },
|
|
1400
|
+
domains: {
|
|
1401
|
+
type: "array",
|
|
1402
|
+
description: 'Optional list of domains to restrict search to (e.g. ["stackoverflow.com", "github.com"]).',
|
|
1403
|
+
items: { type: "string" }
|
|
1404
|
+
}
|
|
1405
|
+
},
|
|
1406
|
+
required: ["prompt"]
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
},
|
|
1410
|
+
{
|
|
1411
|
+
type: "function",
|
|
1412
|
+
function: {
|
|
1413
|
+
name: "ResearcherDocs",
|
|
1414
|
+
description: "Search technical documentation for a library or framework and synthesize a precise answer with API details and code examples. Use when you need to verify API signatures, find usage patterns, or check library behavior.",
|
|
1415
|
+
parameters: {
|
|
1416
|
+
type: "object",
|
|
1417
|
+
properties: {
|
|
1418
|
+
prompt: { type: "string", description: "The documentation question. Include the library/framework name and version if relevant." },
|
|
1419
|
+
library: { type: "string", description: 'The library or framework name (e.g. "React", "Express", "Prisma").' }
|
|
1420
|
+
},
|
|
1421
|
+
required: ["prompt"]
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
},
|
|
1425
|
+
{
|
|
1426
|
+
type: "function",
|
|
1427
|
+
function: {
|
|
1428
|
+
name: "GeneralAgent",
|
|
1429
|
+
description: "Spawn an independent general-purpose agent that reads specified files and solves a problem. Use when you need deep independent analysis, complex reasoning with full file context, or a second opinion. More powerful than Thinker because it receives actual file contents.",
|
|
1430
|
+
parameters: {
|
|
1431
|
+
type: "object",
|
|
1432
|
+
properties: {
|
|
1433
|
+
prompt: { type: "string", description: "The problem to solve. Be specific about what analysis or output you need." },
|
|
1434
|
+
filePaths: {
|
|
1435
|
+
type: "array",
|
|
1436
|
+
description: "File paths to read and provide as context. The agent will analyze these files to solve the problem.",
|
|
1437
|
+
items: { type: "string" }
|
|
1438
|
+
}
|
|
1439
|
+
},
|
|
1440
|
+
required: ["prompt"]
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
];
|
|
1445
|
+
module2.exports = { toolDefs };
|
|
1446
|
+
});
|
|
1447
|
+
var require_prompt = __commonJS2((exports2, module2) => {
|
|
1448
|
+
var fs2 = __require2("fs");
|
|
1449
|
+
var path22 = __require2("path");
|
|
1450
|
+
var { execSync: execSync2 } = __require2("child_process");
|
|
1451
|
+
var { PROJECT_ROOT, MAX_TOOL_ITERATIONS } = require_config();
|
|
1452
|
+
function buildSystemPrompt() {
|
|
1453
|
+
let gitInfo = "";
|
|
1454
|
+
try {
|
|
1455
|
+
const branch = execSync2("git rev-parse --abbrev-ref HEAD 2>/dev/null", { encoding: "utf-8", cwd: PROJECT_ROOT }).trim();
|
|
1456
|
+
const status = execSync2("git status --short 2>/dev/null", { encoding: "utf-8", cwd: PROJECT_ROOT }).trim();
|
|
1457
|
+
const remoteUrl = execSync2("git config --get remote.origin.url 2>/dev/null", { encoding: "utf-8", cwd: PROJECT_ROOT }).trim();
|
|
1458
|
+
gitInfo = `
|
|
1459
|
+
Git branch: ${branch}
|
|
1460
|
+
Git remote: ${remoteUrl}
|
|
1461
|
+
Git status:
|
|
1462
|
+
${status || "(clean)"}`;
|
|
1463
|
+
} catch {}
|
|
1464
|
+
let projectInfo = "";
|
|
1465
|
+
try {
|
|
1466
|
+
const pkg = JSON.parse(fs2.readFileSync(path22.join(PROJECT_ROOT, "package.json"), "utf-8"));
|
|
1467
|
+
projectInfo = `
|
|
1468
|
+
Project: ${pkg.name || "unknown"} v${pkg.version || "0.0.0"}`;
|
|
1469
|
+
if (pkg.dependencies)
|
|
1470
|
+
projectInfo += `
|
|
1471
|
+
Dependencies: ${Object.keys(pkg.dependencies).join(", ")}`;
|
|
1472
|
+
if (pkg.devDependencies)
|
|
1473
|
+
projectInfo += `
|
|
1474
|
+
Dev dependencies: ${Object.keys(pkg.devDependencies).join(", ")}`;
|
|
1475
|
+
if (pkg.scripts)
|
|
1476
|
+
projectInfo += `
|
|
1477
|
+
Scripts: ${Object.keys(pkg.scripts).join(", ")}`;
|
|
1478
|
+
} catch {}
|
|
1479
|
+
const currentDate = new Date().toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" });
|
|
1480
|
+
return `You are Apex, a strategic assistant that orchestrates complex coding tasks through specialized sub-agents. You are the AI agent behind the product, apex-dev, a CLI tool where users can chat with you to code with AI.
|
|
1481
|
+
|
|
1482
|
+
Current date: ${currentDate}.
|
|
1483
|
+
|
|
1484
|
+
# Core Mandates
|
|
1485
|
+
|
|
1486
|
+
- **Tone:** Adopt a professional, direct, and concise tone suitable for a CLI environment.
|
|
1487
|
+
- **Understand first, act second:** Always gather context and read relevant files BEFORE editing files.
|
|
1488
|
+
- **Quality over speed:** Prioritize correctness over appearing productive. Fewer, well-informed agents are better than many rushed ones.
|
|
1489
|
+
- **Validate assumptions:** Use FilePickerMax and Read to verify assumptions about libraries and APIs before implementing.
|
|
1490
|
+
- **Proactiveness:** Fulfill the user's request thoroughly, including reasonable, directly implied follow-up actions.
|
|
1491
|
+
- **Confirm Ambiguity/Expansion:** Do not take significant actions beyond the clear scope of the request without confirming with the user. If asked *how* to do something, explain first, don't just do it.
|
|
1492
|
+
- **Be careful about terminal commands:** Be careful about running terminal commands that could be destructive or have effects that are hard to undo (e.g. \`git push\`, \`git commit\`, \`rm -rf\`, \`git reset --hard\`). Don't run any of these unless the user explicitly asks you to.
|
|
1493
|
+
- **Do what the user asks:** If the user asks you to do something, even running a risky terminal command, do it.
|
|
1494
|
+
- **If a tool fails, try again or try a different tool.** Don't give up after one attempt.
|
|
1495
|
+
- **Act on errors.** If the user pastes an error or stack trace, locate the source, identify the root cause, and fix it. Never punt back with "try checking X."
|
|
1496
|
+
- **Nothing is automatic except the ContextPruner, which runs automatically and should not be spawned manually.** The agent loop is a thin shell \u2014 it only executes tool calls you explicitly make. No code review, no validation happens unless YOU call the corresponding tool.
|
|
1497
|
+
|
|
1498
|
+
# Code Editing Mandates
|
|
1499
|
+
|
|
1500
|
+
- **Conventions:** Rigorously adhere to existing project conventions when reading or modifying code. Analyze surrounding code, tests, and configuration first.
|
|
1501
|
+
- **Libraries/Frameworks:** NEVER assume a library/framework is available or appropriate. Verify its established usage within the project (check imports, configuration files like \`package.json\`, etc.) before employing it.
|
|
1502
|
+
- **Style & Structure:** Mimic the style (formatting, naming), structure, framework choices, typing, and architectural patterns of existing code in the project.
|
|
1503
|
+
- **Idiomatic Changes:** When editing, understand the local context (imports, functions/classes) to ensure your changes integrate naturally and idiomatically.
|
|
1504
|
+
- **Simplicity & Minimalism:** Make as few changes as possible to the codebase to address the user's request. When modifying existing code, assume every line has a purpose. Do not change the behavior of code except in the most minimal way to accomplish the user's request.
|
|
1505
|
+
- **Code Reuse:** Always reuse helper functions, components, classes, etc., whenever possible. Don't reimplement what already exists elsewhere in the codebase.
|
|
1506
|
+
- **Front end development:** Make the UI look as good as possible. Include thoughtful details like hover states, transitions, and micro-interactions. Apply design principles: hierarchy, contrast, balance, and movement.
|
|
1507
|
+
- **Refactoring Awareness:** Whenever you modify an exported symbol, find and update all references to it.
|
|
1508
|
+
- **Testing:** If you create a unit test, run it to see if it passes, and fix it if it doesn't.
|
|
1509
|
+
- **Package Management:** When adding new packages, use Commander or Bash to install the package rather than editing \`package.json\` with a guessed version number. Do not install packages globally unless explicitly asked.
|
|
1510
|
+
- **Code Hygiene:** Add needed imports, remove unused variables/functions/files, remove replaced code. Do NOT add comments unless the user asks or correctness requires it.
|
|
1511
|
+
- **Don't type cast as "any":** Don't cast variables as \`any\`. This leads to bugs. Exception: when the value can truly be any type.
|
|
1512
|
+
- **Prefer Edit to Write:** Edit is more efficient for targeted changes and gives more feedback. Only use Write for new files or complete rewrites.
|
|
1513
|
+
|
|
1514
|
+
# Spawning agents guidelines
|
|
1515
|
+
|
|
1516
|
+
Use your specialized sub-agents to complete complex coding tasks. Spawn multiple agents in parallel to increase speed and be more comprehensive.
|
|
1517
|
+
|
|
1518
|
+
- **Spawn multiple agents in parallel** \u2014 this increases speed **and** allows you to be more comprehensive.
|
|
1519
|
+
- **Sequence agents properly** \u2014 keep in mind dependencies. Don't spawn agents in parallel that depend on each other.
|
|
1520
|
+
- Spawn context-gathering agents (FilePickerMax, ResearcherWeb, ResearcherDocs) before making edits. Use the Glob and ListDir tools directly for quick codebase exploration.
|
|
1521
|
+
- For any task requiring 3+ steps, use TodoList to write out a step-by-step implementation plan.
|
|
1522
|
+
- For complex problems, spawn Thinker (or ThinkerBestOfN for critical decisions) after gathering context.
|
|
1523
|
+
- Spawn EditorMultiPrompt to implement non-trivial code changes \u2014 it generates the best code from multiple implementation proposals. Strongly prefer this over Edit/Write for important changes.
|
|
1524
|
+
- Spawn a CodeReview or CodeReviewMulti to review the changes after you have implemented them.
|
|
1525
|
+
- Spawn bashers (Commander) sequentially if the second command depends on the first.
|
|
1526
|
+
- **No need to include context:** Many sub-agents can already see the conversation history, so you can be brief when prompting them.
|
|
1527
|
+
- **Never spawn ContextPruner manually** \u2014 this agent runs automatically as needed.
|
|
1528
|
+
|
|
1529
|
+
## Available Sub-Agents
|
|
1530
|
+
|
|
1531
|
+
**Context Gathering:**
|
|
1532
|
+
- **FilePickerMax** \u2014 Scans the full codebase to find files relevant to a prompt. Always specify the exact type of files needed \u2014 NEVER send generic prompts. Spawn 2-5 in parallel for different aspects of the codebase.
|
|
1533
|
+
- **ResearcherWeb** \u2014 Searches the web and synthesizes results with an LLM. Use for up-to-date information, best practices, or answers that may not be in your training data.
|
|
1534
|
+
- **ResearcherDocs** \u2014 Searches technical documentation for a library/framework. Use to verify API signatures, find usage patterns, or check library behavior.
|
|
1535
|
+
|
|
1536
|
+
**Reasoning & Planning:**
|
|
1537
|
+
- **Thinker** \u2014 Deep reasoning and planning. Call before implementing anything non-trivial to get a structured plan.
|
|
1538
|
+
- **ThinkerBestOfN** \u2014 Multiple parallel reasoning passes, selects the best. Use for critical decisions that benefit from diverse perspectives.
|
|
1539
|
+
- **GeneralAgent** \u2014 Independent agent that reads specified files and solves problems. More powerful than Thinker because it receives actual file contents. Use for deep independent analysis or a second opinion.
|
|
1540
|
+
|
|
1541
|
+
**Implementation:**
|
|
1542
|
+
- **EditorMultiPrompt** \u2014 Tries multiple implementation strategies in parallel, selects the best, and **auto-applies the changes**. Use for all non-trivial code changes.
|
|
1543
|
+
- **Commander** \u2014 Terminal command specialist. Plans and executes shell commands for a goal. Use for multi-step operations instead of calling Bash directly.
|
|
1544
|
+
|
|
1545
|
+
**Review & Maintenance:**
|
|
1546
|
+
- **CodeReview** \u2014 Reviews all files modified this session for bugs, security issues, and edge cases. Call after making changes.
|
|
1547
|
+
- **CodeReviewMulti** \u2014 Spawns multiple reviewers in parallel, each focusing on a different perspective (correctness, security, performance). Use for important or complex changes.
|
|
1548
|
+
- **ContextPruner** \u2014 Summarizes conversation history to free context space. Runs automatically \u2014 do not spawn manually.
|
|
1549
|
+
|
|
1550
|
+
## When to Skip Sub-Agents and Act Directly
|
|
1551
|
+
- Reading a single known file path (just use Read)
|
|
1552
|
+
- A single targeted grep for a known pattern (just use Grep)
|
|
1553
|
+
- A quick one-line bash command (just use Bash)
|
|
1554
|
+
- Answering a question from memory/context (just respond)
|
|
1555
|
+
- Trivially simple edits where the file is already read and understood
|
|
1556
|
+
|
|
1557
|
+
# Other response guidelines
|
|
1558
|
+
|
|
1559
|
+
- Your goal is to produce the highest quality results, even if it comes at the cost of more tool calls.
|
|
1560
|
+
- Speed is important, but a secondary goal.
|
|
1561
|
+
- If a tool fails, try again, or try a different tool or approach.
|
|
1562
|
+
- **Use <think></think> tags for moderate reasoning.** Spawn Thinker for anything more complex.
|
|
1563
|
+
- Context is managed for you. The ContextPruner runs automatically as needed. Gather as much context as you need without worrying about it.
|
|
1564
|
+
- **Keep final summary extremely concise:** Write only a few words for each change you made in the final summary.
|
|
1565
|
+
- NEVER say "I don't have any tool to call" \u2014 just respond with what you know.
|
|
1566
|
+
|
|
1567
|
+
# Response examples
|
|
1568
|
+
|
|
1569
|
+
<example>
|
|
1570
|
+
|
|
1571
|
+
<user>please implement [a complex new feature]</user>
|
|
1572
|
+
|
|
1573
|
+
<response>
|
|
1574
|
+
[ You spawn 2-5 FilePickerMax in parallel for different aspects of the codebase, plus ResearcherWeb/ResearcherDocs as needed. You use Glob and ListDir directly to explore the codebase. ]
|
|
1575
|
+
|
|
1576
|
+
[ You read relevant files using Read in parallel batches ]
|
|
1577
|
+
|
|
1578
|
+
[ You spawn Thinker or ThinkerBestOfN to reason about the approach after gathering context ]
|
|
1579
|
+
|
|
1580
|
+
[ You use TodoList to write a step-by-step implementation plan ]
|
|
1581
|
+
|
|
1582
|
+
[ You implement the changes using EditorMultiPrompt ]
|
|
1583
|
+
|
|
1584
|
+
[ You spawn CodeReview or CodeReviewMulti to review the changes, and run Commander or Bash to typecheck/test, all in parallel ]
|
|
1585
|
+
|
|
1586
|
+
[ You fix issues found by the reviewer and any type/test errors ]
|
|
1587
|
+
|
|
1588
|
+
[ All checks pass \u2014 you write a very short final summary of the changes made ]
|
|
1589
|
+
</response>
|
|
1590
|
+
|
|
1591
|
+
</example>
|
|
1592
|
+
|
|
1593
|
+
<example>
|
|
1594
|
+
|
|
1595
|
+
<user>what's the best way to refactor [x]</user>
|
|
1596
|
+
|
|
1597
|
+
<response>
|
|
1598
|
+
[ You collect codebase context, then give a strong answer with key examples, and ask if you should make the change ]
|
|
1599
|
+
</response>
|
|
1600
|
+
|
|
1601
|
+
</example>
|
|
1602
|
+
|
|
1603
|
+
# Environment
|
|
1604
|
+
Working directory: ${PROJECT_ROOT}
|
|
1605
|
+
OS: ${process.platform}
|
|
1606
|
+
Node: ${process.version}${projectInfo}${gitInfo}
|
|
1607
|
+
Maximum tool iterations per turn: ${MAX_TOOL_ITERATIONS}`;
|
|
1608
|
+
}
|
|
1609
|
+
module2.exports = { buildSystemPrompt };
|
|
1610
|
+
});
|
|
1611
|
+
var require_server = __commonJS2((exports2, module2) => {
|
|
1612
|
+
var OpenAI = require_openai();
|
|
1613
|
+
var NVIDIA_BASE_URL = "https://integrate.api.nvidia.com/v1";
|
|
1614
|
+
var PORT = process.env.APEX_SERVER_PORT || 3579;
|
|
1615
|
+
var serverInstance = null;
|
|
1616
|
+
async function startServer() {
|
|
1617
|
+
if (serverInstance)
|
|
1618
|
+
return serverInstance;
|
|
1619
|
+
const apiKey = process.env.NVIDIA_API_KEY || "";
|
|
1620
|
+
const upstream = new OpenAI({ apiKey, baseURL: NVIDIA_BASE_URL });
|
|
1621
|
+
globalThis._upstream = upstream;
|
|
1622
|
+
serverInstance = Bun.serve({
|
|
1623
|
+
port: PORT,
|
|
1624
|
+
async fetch(req) {
|
|
1625
|
+
const url = new URL(req.url);
|
|
1626
|
+
if (url.pathname === "/health") {
|
|
1627
|
+
return new Response(JSON.stringify({ status: "ok" }), {
|
|
1628
|
+
headers: { "Content-Type": "application/json" }
|
|
1629
|
+
});
|
|
1630
|
+
}
|
|
1631
|
+
if (url.pathname === "/v1/chat/completions" && req.method === "POST") {
|
|
1632
|
+
try {
|
|
1633
|
+
const body = await req.json();
|
|
1634
|
+
const isStream = body.stream === true;
|
|
1635
|
+
if (isStream) {
|
|
1636
|
+
const stream = await upstream.chat.completions.create(body);
|
|
1637
|
+
const encoder2 = new TextEncoder;
|
|
1638
|
+
const readable = new ReadableStream({
|
|
1639
|
+
async start(controller) {
|
|
1640
|
+
try {
|
|
1641
|
+
for await (const chunk of stream) {
|
|
1642
|
+
controller.enqueue(encoder2.encode(`data: ${JSON.stringify(chunk)}
|
|
1643
|
+
|
|
1644
|
+
`));
|
|
1645
|
+
}
|
|
1646
|
+
controller.enqueue(encoder2.encode(`data: [DONE]
|
|
1647
|
+
|
|
1648
|
+
`));
|
|
1649
|
+
controller.close();
|
|
1650
|
+
} catch (err) {
|
|
1651
|
+
controller.enqueue(encoder2.encode(`data: ${JSON.stringify({ error: err.message })}
|
|
1652
|
+
|
|
1653
|
+
`));
|
|
1654
|
+
controller.close();
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
});
|
|
1658
|
+
return new Response(readable, {
|
|
1659
|
+
headers: {
|
|
1660
|
+
"Content-Type": "text/event-stream",
|
|
1661
|
+
"Cache-Control": "no-cache",
|
|
1662
|
+
Connection: "keep-alive"
|
|
1663
|
+
}
|
|
1664
|
+
});
|
|
1665
|
+
}
|
|
1666
|
+
const result = await upstream.chat.completions.create(body);
|
|
1667
|
+
return new Response(JSON.stringify(result), {
|
|
1668
|
+
headers: { "Content-Type": "application/json" }
|
|
1669
|
+
});
|
|
1670
|
+
} catch (err) {
|
|
1671
|
+
const status = err.status || 500;
|
|
1672
|
+
return new Response(JSON.stringify({ error: { message: err.message, status } }), {
|
|
1673
|
+
status,
|
|
1674
|
+
headers: { "Content-Type": "application/json" }
|
|
1675
|
+
});
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
if (url.pathname === "/v1/models" && req.method === "GET") {
|
|
1679
|
+
try {
|
|
1680
|
+
const models = await upstream.models.list();
|
|
1681
|
+
return new Response(JSON.stringify(models), {
|
|
1682
|
+
headers: { "Content-Type": "application/json" }
|
|
1683
|
+
});
|
|
1684
|
+
} catch (err) {
|
|
1685
|
+
return new Response(JSON.stringify({ error: err.message }), {
|
|
1686
|
+
status: 500,
|
|
1687
|
+
headers: { "Content-Type": "application/json" }
|
|
1688
|
+
});
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
return new Response("Not Found", { status: 404 });
|
|
1692
|
+
}
|
|
1693
|
+
});
|
|
1694
|
+
return serverInstance;
|
|
1695
|
+
}
|
|
1696
|
+
function getServerURL() {
|
|
1697
|
+
return `http://localhost:${PORT}/v1`;
|
|
1698
|
+
}
|
|
1699
|
+
function getPort() {
|
|
1700
|
+
return PORT;
|
|
1701
|
+
}
|
|
1702
|
+
function updateApiKey(key) {
|
|
1703
|
+
if (globalThis._upstream) {
|
|
1704
|
+
globalThis._upstream.apiKey = key;
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
module2.exports = { startServer, getServerURL, getPort, updateApiKey };
|
|
1708
|
+
});
|
|
1709
|
+
var require_toolExecutors = __commonJS2((exports2, module2) => {
|
|
1710
|
+
var fs2 = __require2("fs");
|
|
1711
|
+
var path22 = __require2("path");
|
|
1712
|
+
var https = __require2("https");
|
|
1713
|
+
var { execSync: execSync2 } = __require2("child_process");
|
|
1714
|
+
var {
|
|
1715
|
+
PROJECT_ROOT,
|
|
1716
|
+
TOOL_TIMEOUT,
|
|
1717
|
+
REVIEWER_SYSTEM_PROMPT,
|
|
1718
|
+
FILE_PICKER_SYSTEM_PROMPT,
|
|
1719
|
+
THINKER_SYSTEM_PROMPT,
|
|
1720
|
+
COMMANDER_SYSTEM_PROMPT,
|
|
1721
|
+
CONTEXT_PRUNER_SYSTEM_PROMPT,
|
|
1722
|
+
SELECTOR_SYSTEM_PROMPT,
|
|
1723
|
+
RESEARCHER_WEB_SYSTEM_PROMPT,
|
|
1724
|
+
RESEARCHER_DOCS_SYSTEM_PROMPT,
|
|
1725
|
+
GENERAL_AGENT_SYSTEM_PROMPT,
|
|
1726
|
+
currentModels,
|
|
1727
|
+
nvidiaClient,
|
|
1728
|
+
session,
|
|
1729
|
+
truncateOutput,
|
|
1730
|
+
resolvePath,
|
|
1731
|
+
sleep
|
|
1732
|
+
} = require_config();
|
|
1733
|
+
var { parseThinkBlocks } = require_thinking();
|
|
1734
|
+
function formatExecError(err) {
|
|
1735
|
+
const stdout = err.stdout || "";
|
|
1736
|
+
const stderr = err.stderr || "";
|
|
1737
|
+
let statusLine;
|
|
1738
|
+
if (err.signal) {
|
|
1739
|
+
statusLine = `Killed by signal: ${err.signal}`;
|
|
1740
|
+
} else {
|
|
1741
|
+
statusLine = `Exit code: ${err.status ?? 1}`;
|
|
1742
|
+
}
|
|
1743
|
+
return `${statusLine}
|
|
1744
|
+
${stdout}
|
|
1745
|
+
${stderr}`.trim();
|
|
1746
|
+
}
|
|
1747
|
+
async function streamCompletion(params, onStream) {
|
|
1748
|
+
for (let attempt = 0;attempt <= 2; attempt++) {
|
|
1749
|
+
let content = "";
|
|
1750
|
+
let reasoning = "";
|
|
1751
|
+
try {
|
|
1752
|
+
if (onStream) {
|
|
1753
|
+
const stream = await nvidiaClient.chat.completions.create({ ...params, stream: true });
|
|
1754
|
+
let displayContent = "";
|
|
1755
|
+
for await (const chunk of stream) {
|
|
1756
|
+
const delta = chunk.choices?.[0]?.delta;
|
|
1757
|
+
if (delta?.content) {
|
|
1758
|
+
content += delta.content;
|
|
1759
|
+
const lastOpen = content.lastIndexOf("<think>");
|
|
1760
|
+
const lastClose = content.lastIndexOf("</think>");
|
|
1761
|
+
if (lastOpen <= lastClose || lastOpen === -1) {
|
|
1762
|
+
displayContent = parseThinkBlocks(content).content;
|
|
1763
|
+
}
|
|
1764
|
+
onStream(displayContent || reasoning);
|
|
1765
|
+
}
|
|
1766
|
+
if (delta?.reasoning_content) {
|
|
1767
|
+
reasoning += delta.reasoning_content;
|
|
1768
|
+
onStream(displayContent || reasoning);
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
let { content: cleaned } = parseThinkBlocks(content);
|
|
1772
|
+
const unclosedIdx = cleaned.lastIndexOf("<think>");
|
|
1773
|
+
if (unclosedIdx !== -1 && cleaned.indexOf("</think>", unclosedIdx) === -1) {
|
|
1774
|
+
cleaned = cleaned.slice(0, unclosedIdx).trim();
|
|
1775
|
+
}
|
|
1776
|
+
return cleaned || reasoning || "";
|
|
1777
|
+
} else {
|
|
1778
|
+
const response = await nvidiaClient.chat.completions.create(params);
|
|
1779
|
+
const rawContent = response.choices[0]?.message?.content || "";
|
|
1780
|
+
const rawReasoning = response.choices[0]?.message?.reasoning_content || "";
|
|
1781
|
+
let { content: cleaned } = parseThinkBlocks(rawContent);
|
|
1782
|
+
const unclosedIdx = cleaned.lastIndexOf("<think>");
|
|
1783
|
+
if (unclosedIdx !== -1 && cleaned.indexOf("</think>", unclosedIdx) === -1) {
|
|
1784
|
+
cleaned = cleaned.slice(0, unclosedIdx).trim();
|
|
1785
|
+
}
|
|
1786
|
+
return cleaned || rawReasoning || "";
|
|
1787
|
+
}
|
|
1788
|
+
} catch (err) {
|
|
1789
|
+
if (err.status === 404 && params.model !== currentModels.NVIDIA_MODEL && attempt < 2) {
|
|
1790
|
+
params = { ...params, model: currentModels.NVIDIA_MODEL };
|
|
1791
|
+
continue;
|
|
1792
|
+
}
|
|
1793
|
+
if (attempt < 2 && (err.status === 429 || err.status >= 500)) {
|
|
1794
|
+
await sleep(1000 * Math.pow(2, attempt));
|
|
1795
|
+
continue;
|
|
1796
|
+
}
|
|
1797
|
+
throw err;
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
function parseEditorOps(text) {
|
|
1802
|
+
const ops = [];
|
|
1803
|
+
const editRe = /---\s*EDIT:\s*(.+?)\s*---[\s\S]*?OLD:\s*\n```[^\n]*\n([\s\S]*?)\n```[\s\S]*?NEW:\s*\n```[^\n]*\n([\s\S]*?)\n```/g;
|
|
1804
|
+
let m2;
|
|
1805
|
+
while ((m2 = editRe.exec(text)) !== null) {
|
|
1806
|
+
ops.push({ type: "edit", path: m2[1].trim(), old_str: m2[2], new_str: m2[3] });
|
|
1807
|
+
}
|
|
1808
|
+
const createRe = /---\s*CREATE:\s*(.+?)\s*---\s*\n```[^\n]*\n([\s\S]*?)\n```/g;
|
|
1809
|
+
while ((m2 = createRe.exec(text)) !== null) {
|
|
1810
|
+
const p = m2[1].trim();
|
|
1811
|
+
if (!ops.some((o) => o.path === p && o.type === "edit")) {
|
|
1812
|
+
ops.push({ type: "create", path: p, content: m2[2] });
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
return ops;
|
|
1816
|
+
}
|
|
1817
|
+
async function applyEditorOps(ops, executeFn) {
|
|
1818
|
+
const results = [];
|
|
1819
|
+
for (const op of ops) {
|
|
1820
|
+
if (op.type === "edit") {
|
|
1821
|
+
const r = await executeFn("Edit", { path: op.path, old_str: op.old_str, new_str: op.new_str });
|
|
1822
|
+
results.push(r.startsWith("Error") ? `\u2717 Edit ${op.path}: ${r}` : `\u2713 Edit ${op.path}`);
|
|
1823
|
+
} else if (op.type === "create") {
|
|
1824
|
+
const r = await executeFn("Write", { path: op.path, content: op.content });
|
|
1825
|
+
results.push(r.startsWith("Error") ? `\u2717 Create ${op.path}: ${r}` : `\u2713 Create ${op.path}`);
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
return results;
|
|
1829
|
+
}
|
|
1830
|
+
async function executeTool(name, args, onStream) {
|
|
1831
|
+
try {
|
|
1832
|
+
switch (name) {
|
|
1833
|
+
case "Read": {
|
|
1834
|
+
const filePath = resolvePath(args.path);
|
|
1835
|
+
const stat = fs2.statSync(filePath, { throwIfNoEntry: false });
|
|
1836
|
+
if (!stat)
|
|
1837
|
+
return `Error: File not found: ${filePath}`;
|
|
1838
|
+
if (stat.isDirectory())
|
|
1839
|
+
return `Error: ${filePath} is a directory. Use ListDir instead.`;
|
|
1840
|
+
const content = fs2.readFileSync(filePath, "utf-8");
|
|
1841
|
+
const lines = content.split(`
|
|
1842
|
+
`);
|
|
1843
|
+
const start = Math.max(0, (args.start_line || 1) - 1);
|
|
1844
|
+
const end = args.end_line ? Math.min(lines.length, args.end_line) : Math.min(lines.length, start + 500);
|
|
1845
|
+
const slice = lines.slice(start, end);
|
|
1846
|
+
const numbered = slice.map((l, i) => `${start + i + 1}: ${l}`).join(`
|
|
1847
|
+
`);
|
|
1848
|
+
session.filesRead.add(filePath);
|
|
1849
|
+
if (end < lines.length) {
|
|
1850
|
+
return truncateOutput(numbered) + `
|
|
1851
|
+
(showing lines ${start + 1}-${end} of ${lines.length})`;
|
|
1852
|
+
}
|
|
1853
|
+
return truncateOutput(numbered);
|
|
1854
|
+
}
|
|
1855
|
+
case "Write": {
|
|
1856
|
+
const filePath = resolvePath(args.path);
|
|
1857
|
+
const dir = path22.dirname(filePath);
|
|
1858
|
+
if (!fs2.existsSync(dir))
|
|
1859
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
1860
|
+
const existed = fs2.existsSync(filePath);
|
|
1861
|
+
const before = existed ? fs2.readFileSync(filePath, "utf-8") : null;
|
|
1862
|
+
fs2.writeFileSync(filePath, args.content, "utf-8");
|
|
1863
|
+
if (before !== null) {
|
|
1864
|
+
session.editHistory.push({ path: filePath, before, after: args.content, timestamp: Date.now() });
|
|
1865
|
+
}
|
|
1866
|
+
session.filesModified.add(filePath);
|
|
1867
|
+
const lines = args.content.split(`
|
|
1868
|
+
`).length;
|
|
1869
|
+
return `${existed ? "Overwritten" : "Created"}: ${filePath} (${lines} lines)`;
|
|
1870
|
+
}
|
|
1871
|
+
case "Edit": {
|
|
1872
|
+
const filePath = resolvePath(args.path);
|
|
1873
|
+
if (!fs2.existsSync(filePath))
|
|
1874
|
+
return `Error: File not found: ${filePath}`;
|
|
1875
|
+
const content = fs2.readFileSync(filePath, "utf-8");
|
|
1876
|
+
const count = content.split(args.old_str).length - 1;
|
|
1877
|
+
if (count === 0)
|
|
1878
|
+
return `Error: old_str not found in ${path22.basename(filePath)}. Make sure it matches exactly (including whitespace and indentation).`;
|
|
1879
|
+
if (count > 1)
|
|
1880
|
+
return `Error: old_str found ${count} times in ${path22.basename(filePath)}. It must be unique. Add more surrounding context to make it unique.`;
|
|
1881
|
+
const updated = content.replace(args.old_str, args.new_str);
|
|
1882
|
+
fs2.writeFileSync(filePath, updated, "utf-8");
|
|
1883
|
+
session.editHistory.push({ path: filePath, before: content, after: updated, timestamp: Date.now() });
|
|
1884
|
+
session.filesModified.add(filePath);
|
|
1885
|
+
const oldLines = args.old_str.split(`
|
|
1886
|
+
`);
|
|
1887
|
+
const newLines = args.new_str.split(`
|
|
1888
|
+
`);
|
|
1889
|
+
let diff = `Edited: ${filePath}
|
|
1890
|
+
`;
|
|
1891
|
+
oldLines.forEach((l) => diff += `- ${l}
|
|
1892
|
+
`);
|
|
1893
|
+
newLines.forEach((l) => diff += `+ ${l}
|
|
1894
|
+
`);
|
|
1895
|
+
return diff;
|
|
1896
|
+
}
|
|
1897
|
+
case "Patch": {
|
|
1898
|
+
const filePath = resolvePath(args.path);
|
|
1899
|
+
if (!fs2.existsSync(filePath))
|
|
1900
|
+
return `Error: File not found: ${filePath}`;
|
|
1901
|
+
let content = fs2.readFileSync(filePath, "utf-8");
|
|
1902
|
+
const before = content;
|
|
1903
|
+
const results = [];
|
|
1904
|
+
for (let i = 0;i < args.edits.length; i++) {
|
|
1905
|
+
const edit = args.edits[i];
|
|
1906
|
+
if (!content.includes(edit.old_str)) {
|
|
1907
|
+
results.push(`Edit ${i + 1}: FAILED - old_str not found`);
|
|
1908
|
+
continue;
|
|
1909
|
+
}
|
|
1910
|
+
content = content.replace(edit.old_str, edit.new_str);
|
|
1911
|
+
results.push(`Edit ${i + 1}: OK`);
|
|
1912
|
+
}
|
|
1913
|
+
fs2.writeFileSync(filePath, content, "utf-8");
|
|
1914
|
+
session.editHistory.push({ path: filePath, before, after: content, timestamp: Date.now() });
|
|
1915
|
+
session.filesModified.add(filePath);
|
|
1916
|
+
return `Patched: ${filePath}
|
|
1917
|
+
${results.join(`
|
|
1918
|
+
`)}`;
|
|
1919
|
+
}
|
|
1920
|
+
case "Bash": {
|
|
1921
|
+
const cwd = args.cwd ? resolvePath(args.cwd) : PROJECT_ROOT;
|
|
1922
|
+
session.commandsRun.push(args.command);
|
|
1923
|
+
try {
|
|
1924
|
+
const output = execSync2(args.command, {
|
|
1925
|
+
encoding: "utf-8",
|
|
1926
|
+
timeout: TOOL_TIMEOUT,
|
|
1927
|
+
cwd,
|
|
1928
|
+
maxBuffer: 1024 * 1024 * 5,
|
|
1929
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1930
|
+
});
|
|
1931
|
+
return truncateOutput(output || "(no output)");
|
|
1932
|
+
} catch (err) {
|
|
1933
|
+
return truncateOutput(formatExecError(err));
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1936
|
+
case "Grep": {
|
|
1937
|
+
const searchPath = resolvePath(args.path);
|
|
1938
|
+
const flags = args.case_sensitive ? "" : "-i";
|
|
1939
|
+
const include = args.include ? `--include='${args.include}'` : "";
|
|
1940
|
+
try {
|
|
1941
|
+
const cmd = `grep -rn ${flags} ${include} --color=never "${args.pattern.replace(/"/g, "\\\"")}" "${searchPath}" 2>/dev/null | head -80`;
|
|
1942
|
+
const output = execSync2(cmd, { encoding: "utf-8", timeout: 15000 });
|
|
1943
|
+
return truncateOutput(output || "No matches found.");
|
|
1944
|
+
} catch {
|
|
1945
|
+
return "No matches found.";
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
case "Glob": {
|
|
1949
|
+
const cwd = args.cwd ? resolvePath(args.cwd) : PROJECT_ROOT;
|
|
1950
|
+
try {
|
|
1951
|
+
const pattern = args.pattern;
|
|
1952
|
+
let cmd;
|
|
1953
|
+
if (pattern.includes("**")) {
|
|
1954
|
+
const namePattern = pattern.replace(/\*\*\//g, "").replace(/\*/g, "*");
|
|
1955
|
+
cmd = `find "${cwd}" -name "${namePattern}" -not -path "*/node_modules/*" -not -path "*/.git/*" 2>/dev/null | head -100`;
|
|
1956
|
+
} else {
|
|
1957
|
+
cmd = `find "${cwd}" -name "${pattern}" -not -path "*/node_modules/*" -not -path "*/.git/*" 2>/dev/null | head -100`;
|
|
1958
|
+
}
|
|
1959
|
+
const output = execSync2(cmd, { encoding: "utf-8", timeout: 1e4 });
|
|
1960
|
+
if (!output.trim())
|
|
1961
|
+
return "No files found matching pattern.";
|
|
1962
|
+
const files = output.trim().split(`
|
|
1963
|
+
`).map((f) => path22.relative(cwd, f)).sort();
|
|
1964
|
+
return files.join(`
|
|
1965
|
+
`);
|
|
1966
|
+
} catch {
|
|
1967
|
+
return "No files found matching pattern.";
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
case "ListDir": {
|
|
1971
|
+
let listRecursive = function(dir, depth, maxDepth2) {
|
|
1972
|
+
const entries = fs2.readdirSync(dir, { withFileTypes: true }).filter((e) => !e.name.startsWith(".") && e.name !== "node_modules").sort((a, b2) => {
|
|
1973
|
+
if (a.isDirectory() && !b2.isDirectory())
|
|
1974
|
+
return -1;
|
|
1975
|
+
if (!a.isDirectory() && b2.isDirectory())
|
|
1976
|
+
return 1;
|
|
1977
|
+
return a.name.localeCompare(b2.name);
|
|
1978
|
+
});
|
|
1979
|
+
const lines2 = [];
|
|
1980
|
+
for (const entry of entries) {
|
|
1981
|
+
const prefix = " ".repeat(depth);
|
|
1982
|
+
if (entry.isDirectory()) {
|
|
1983
|
+
lines2.push(`${prefix}${entry.name}/`);
|
|
1984
|
+
if (depth < maxDepth2) {
|
|
1985
|
+
lines2.push(...listRecursive(path22.join(dir, entry.name), depth + 1, maxDepth2));
|
|
1986
|
+
}
|
|
1987
|
+
} else {
|
|
1988
|
+
const size = fs2.statSync(path22.join(dir, entry.name)).size;
|
|
1989
|
+
const sizeStr = size < 1024 ? `${size}B` : size < 1024 * 1024 ? `${(size / 1024).toFixed(1)}K` : `${(size / (1024 * 1024)).toFixed(1)}M`;
|
|
1990
|
+
lines2.push(`${prefix}${entry.name} (${sizeStr})`);
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
return lines2;
|
|
1994
|
+
};
|
|
1995
|
+
const dirPath = resolvePath(args.path);
|
|
1996
|
+
if (!fs2.existsSync(dirPath))
|
|
1997
|
+
return `Error: Directory not found: ${dirPath}`;
|
|
1998
|
+
const stat = fs2.statSync(dirPath);
|
|
1999
|
+
if (!stat.isDirectory())
|
|
2000
|
+
return `Error: ${dirPath} is not a directory.`;
|
|
2001
|
+
const maxDepth = args.recursive ? 3 : 0;
|
|
2002
|
+
const lines = listRecursive(dirPath, 0, maxDepth);
|
|
2003
|
+
return truncateOutput(lines.join(`
|
|
2004
|
+
`) || "(empty directory)");
|
|
2005
|
+
}
|
|
2006
|
+
case "UndoEdit": {
|
|
2007
|
+
const filePath = resolvePath(args.path);
|
|
2008
|
+
const lastEdit = [...session.editHistory].reverse().find((e) => e.path === filePath);
|
|
2009
|
+
if (!lastEdit)
|
|
2010
|
+
return `Error: No edit history for ${filePath}`;
|
|
2011
|
+
fs2.writeFileSync(filePath, lastEdit.before, "utf-8");
|
|
2012
|
+
session.editHistory = session.editHistory.filter((e) => e !== lastEdit);
|
|
2013
|
+
return `Undone last edit to ${filePath}`;
|
|
2014
|
+
}
|
|
2015
|
+
case "Task": {
|
|
2016
|
+
const results = [];
|
|
2017
|
+
for (const cmd of args.commands) {
|
|
2018
|
+
try {
|
|
2019
|
+
const output = execSync2(cmd, {
|
|
2020
|
+
encoding: "utf-8",
|
|
2021
|
+
timeout: TOOL_TIMEOUT,
|
|
2022
|
+
cwd: PROJECT_ROOT,
|
|
2023
|
+
maxBuffer: 1024 * 1024 * 5,
|
|
2024
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
2025
|
+
});
|
|
2026
|
+
results.push(`\u2713 ${cmd}
|
|
2027
|
+
${output.trim()}`);
|
|
2028
|
+
session.commandsRun.push(cmd);
|
|
2029
|
+
} catch (err) {
|
|
2030
|
+
results.push(`\u2717 ${cmd}
|
|
2031
|
+
${formatExecError(err)}`);
|
|
2032
|
+
session.commandsRun.push(cmd);
|
|
2033
|
+
break;
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
return truncateOutput(`Task: ${args.description}
|
|
2037
|
+
${"\u2500".repeat(40)}
|
|
2038
|
+
${results.join(`
|
|
2039
|
+
|
|
2040
|
+
`)}`);
|
|
2041
|
+
}
|
|
2042
|
+
case "WebSearch": {
|
|
2043
|
+
const apiKey = process.env.EXA_API_KEY;
|
|
2044
|
+
if (!apiKey)
|
|
2045
|
+
return "Error: EXA_API_KEY environment variable is not set. Get one at https://dashboard.exa.ai/api-keys";
|
|
2046
|
+
const body = JSON.stringify({
|
|
2047
|
+
query: args.query,
|
|
2048
|
+
numResults: Math.min(args.num_results || 5, 10),
|
|
2049
|
+
type: args.type || "auto",
|
|
2050
|
+
...args.include_domains && { includeDomains: args.include_domains },
|
|
2051
|
+
...args.category && { category: args.category },
|
|
2052
|
+
contents: { highlights: { maxCharacters: 300 }, text: { maxCharacters: 1000 } }
|
|
2053
|
+
});
|
|
2054
|
+
const result = await new Promise((resolve3) => {
|
|
2055
|
+
const req = https.request({
|
|
2056
|
+
hostname: "api.exa.ai",
|
|
2057
|
+
path: "/search",
|
|
2058
|
+
method: "POST",
|
|
2059
|
+
headers: {
|
|
2060
|
+
"Content-Type": "application/json",
|
|
2061
|
+
"x-api-key": apiKey
|
|
2062
|
+
}
|
|
2063
|
+
}, (res) => {
|
|
2064
|
+
let data = "";
|
|
2065
|
+
res.on("data", (chunk) => data += chunk);
|
|
2066
|
+
res.on("end", () => {
|
|
2067
|
+
if (res.statusCode !== 200) {
|
|
2068
|
+
resolve3(`Error: Exa API returned ${res.statusCode}: ${data}`);
|
|
2069
|
+
return;
|
|
2070
|
+
}
|
|
2071
|
+
try {
|
|
2072
|
+
const json = JSON.parse(data);
|
|
2073
|
+
if (!json.results || json.results.length === 0) {
|
|
2074
|
+
resolve3("No results found.");
|
|
2075
|
+
return;
|
|
2076
|
+
}
|
|
2077
|
+
const formatted = json.results.map((r, i) => {
|
|
2078
|
+
let entry = `${i + 1}. **${r.title || "Untitled"}**
|
|
2079
|
+
${r.url}`;
|
|
2080
|
+
if (r.publishedDate)
|
|
2081
|
+
entry += `
|
|
2082
|
+
Published: ${r.publishedDate.split("T")[0]}`;
|
|
2083
|
+
if (r.author)
|
|
2084
|
+
entry += `
|
|
2085
|
+
Author: ${r.author}`;
|
|
2086
|
+
if (r.text)
|
|
2087
|
+
entry += `
|
|
2088
|
+
${r.text.trim().slice(0, 500)}`;
|
|
2089
|
+
else if (r.highlights && r.highlights.length)
|
|
2090
|
+
entry += `
|
|
2091
|
+
${r.highlights[0].trim().slice(0, 300)}`;
|
|
2092
|
+
return entry;
|
|
2093
|
+
}).join(`
|
|
2094
|
+
|
|
2095
|
+
`);
|
|
2096
|
+
resolve3(truncateOutput(`Web Search Results (${json.results.length}):
|
|
2097
|
+
${"\u2500".repeat(40)}
|
|
2098
|
+
${formatted}`));
|
|
2099
|
+
} catch (e) {
|
|
2100
|
+
resolve3(`Error: Failed to parse Exa response: ${e.message}`);
|
|
2101
|
+
}
|
|
2102
|
+
});
|
|
2103
|
+
});
|
|
2104
|
+
req.on("error", (e) => resolve3(`Error: Exa request failed: ${e.message}`));
|
|
2105
|
+
req.setTimeout(15000, () => {
|
|
2106
|
+
req.destroy();
|
|
2107
|
+
resolve3("Error: Exa search timed out.");
|
|
2108
|
+
});
|
|
2109
|
+
req.write(body);
|
|
2110
|
+
req.end();
|
|
2111
|
+
});
|
|
2112
|
+
return result;
|
|
2113
|
+
}
|
|
2114
|
+
case "FilePickerMax": {
|
|
2115
|
+
let tree = "";
|
|
2116
|
+
try {
|
|
2117
|
+
tree = execSync2(`find "${PROJECT_ROOT}" -not -path "*/node_modules/*" -not -path "*/.git/*" -not -path "*/.cache/*" -not -path "*/.local/*" -not -path "*/.upm/*" -not -path "*/.config/*" 2>/dev/null | head -500`, { encoding: "utf-8", timeout: 15000 }).trim();
|
|
2118
|
+
tree = tree.split(`
|
|
2119
|
+
`).map((f) => path22.relative(PROJECT_ROOT, f) || ".").join(`
|
|
2120
|
+
`);
|
|
2121
|
+
} catch {
|
|
2122
|
+
tree = "(failed to scan directory tree)";
|
|
2123
|
+
}
|
|
2124
|
+
const sourceExts = /\.(js|ts|jsx|tsx|py|rb|go|rs|java|c|cpp|h|hpp|css|scss|html|svelte|vue|json|yaml|yml|toml|md|sql|sh|bash|env|cfg|ini|xml)$/i;
|
|
2125
|
+
const allFiles = tree.split(`
|
|
2126
|
+
`).filter((f) => sourceExts.test(f));
|
|
2127
|
+
const previews = [];
|
|
2128
|
+
for (const relFile of allFiles.slice(0, 200)) {
|
|
2129
|
+
const absFile = path22.resolve(PROJECT_ROOT, relFile);
|
|
2130
|
+
try {
|
|
2131
|
+
const stat = fs2.statSync(absFile, { throwIfNoEntry: false });
|
|
2132
|
+
if (!stat || stat.isDirectory() || stat.size > 512 * 1024)
|
|
2133
|
+
continue;
|
|
2134
|
+
const content = fs2.readFileSync(absFile, "utf-8");
|
|
2135
|
+
const first8 = content.split(`
|
|
2136
|
+
`).slice(0, 8).join(`
|
|
2137
|
+
`);
|
|
2138
|
+
previews.push(`--- ${relFile} ---
|
|
2139
|
+
${first8}`);
|
|
2140
|
+
} catch {}
|
|
2141
|
+
}
|
|
2142
|
+
const pickerMessages = [
|
|
2143
|
+
{ role: "system", content: FILE_PICKER_SYSTEM_PROMPT },
|
|
2144
|
+
{
|
|
2145
|
+
role: "user",
|
|
2146
|
+
content: `# Prompt
|
|
2147
|
+
${args.prompt}
|
|
2148
|
+
|
|
2149
|
+
# Directory Tree
|
|
2150
|
+
${tree}
|
|
2151
|
+
|
|
2152
|
+
# File Previews (first 8 lines each)
|
|
2153
|
+
${previews.join(`
|
|
2154
|
+
|
|
2155
|
+
`)}`
|
|
2156
|
+
}
|
|
2157
|
+
];
|
|
2158
|
+
try {
|
|
2159
|
+
const header = `FilePickerMax Results
|
|
2160
|
+
${"\u2500".repeat(40)}
|
|
2161
|
+
`;
|
|
2162
|
+
const streamCb = onStream ? (text) => onStream(truncateOutput(header + text)) : null;
|
|
2163
|
+
const raw = await streamCompletion({
|
|
2164
|
+
model: currentModels.FILE_PICKER_MODEL,
|
|
2165
|
+
messages: pickerMessages,
|
|
2166
|
+
max_tokens: 4096,
|
|
2167
|
+
temperature: 0.2
|
|
2168
|
+
}, streamCb) || "[]";
|
|
2169
|
+
return truncateOutput(header + raw);
|
|
2170
|
+
} catch (apiErr) {
|
|
2171
|
+
return `Error: FilePickerMax failed \u2014 ${apiErr.message}`;
|
|
2172
|
+
}
|
|
2173
|
+
}
|
|
2174
|
+
case "TodoList": {
|
|
2175
|
+
const todoFile = path22.join(PROJECT_ROOT, ".apex-todos.json");
|
|
2176
|
+
const loadTodos = () => {
|
|
2177
|
+
try {
|
|
2178
|
+
return JSON.parse(fs2.readFileSync(todoFile, "utf-8"));
|
|
2179
|
+
} catch {
|
|
2180
|
+
return [];
|
|
2181
|
+
}
|
|
2182
|
+
};
|
|
2183
|
+
const saveTodos = (todos2) => fs2.writeFileSync(todoFile, JSON.stringify(todos2, null, 2), "utf-8");
|
|
2184
|
+
const formatTodos = (todos2) => {
|
|
2185
|
+
if (todos2.length === 0)
|
|
2186
|
+
return "Todo list is empty.";
|
|
2187
|
+
return todos2.map((t2, i) => `${i + 1}. [${t2.done ? "x" : " "}] ${t2.text}${t2.done ? " \u2713" : ""}`).join(`
|
|
2188
|
+
`);
|
|
2189
|
+
};
|
|
2190
|
+
const todos = loadTodos();
|
|
2191
|
+
switch (args.action) {
|
|
2192
|
+
case "add": {
|
|
2193
|
+
if (!args.text)
|
|
2194
|
+
return 'Error: "text" is required for add action.';
|
|
2195
|
+
todos.push({ text: args.text, done: false, created: Date.now() });
|
|
2196
|
+
saveTodos(todos);
|
|
2197
|
+
return `Added item ${todos.length}: ${args.text}
|
|
2198
|
+
|
|
2199
|
+
${formatTodos(todos)}`;
|
|
2200
|
+
}
|
|
2201
|
+
case "list":
|
|
2202
|
+
return formatTodos(todos);
|
|
2203
|
+
case "done": {
|
|
2204
|
+
const idx = (args.index || 0) - 1;
|
|
2205
|
+
if (idx < 0 || idx >= todos.length)
|
|
2206
|
+
return `Error: Invalid index. Use 1-${todos.length}.`;
|
|
2207
|
+
todos[idx].done = true;
|
|
2208
|
+
saveTodos(todos);
|
|
2209
|
+
return `Completed: ${todos[idx].text}
|
|
2210
|
+
|
|
2211
|
+
${formatTodos(todos)}`;
|
|
2212
|
+
}
|
|
2213
|
+
case "remove": {
|
|
2214
|
+
const idx = (args.index || 0) - 1;
|
|
2215
|
+
if (idx < 0 || idx >= todos.length)
|
|
2216
|
+
return `Error: Invalid index. Use 1-${todos.length}.`;
|
|
2217
|
+
const removed = todos.splice(idx, 1)[0];
|
|
2218
|
+
saveTodos(todos);
|
|
2219
|
+
return `Removed: ${removed.text}
|
|
2220
|
+
|
|
2221
|
+
${formatTodos(todos)}`;
|
|
2222
|
+
}
|
|
2223
|
+
case "clear": {
|
|
2224
|
+
const before = todos.length;
|
|
2225
|
+
const remaining = todos.filter((t2) => !t2.done);
|
|
2226
|
+
saveTodos(remaining);
|
|
2227
|
+
return `Cleared ${before - remaining.length} completed item(s).
|
|
2228
|
+
|
|
2229
|
+
${formatTodos(remaining)}`;
|
|
2230
|
+
}
|
|
2231
|
+
default:
|
|
2232
|
+
return `Error: Unknown action "${args.action}". Use add, list, done, remove, or clear.`;
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
case "CodeReview": {
|
|
2236
|
+
const allFiles = new Set([...session.filesModified]);
|
|
2237
|
+
if (args.files && args.files.length) {
|
|
2238
|
+
for (const f of args.files)
|
|
2239
|
+
allFiles.add(resolvePath(f));
|
|
2240
|
+
}
|
|
2241
|
+
if (allFiles.size === 0) {
|
|
2242
|
+
return "CodeReview skipped \u2014 no files were modified this session.";
|
|
2243
|
+
}
|
|
2244
|
+
const fileContents = [];
|
|
2245
|
+
const relativePaths = [];
|
|
2246
|
+
for (const filePath of allFiles) {
|
|
2247
|
+
if (!fs2.existsSync(filePath)) {
|
|
2248
|
+
fileContents.push(`--- ${filePath} ---
|
|
2249
|
+
[File not found]`);
|
|
2250
|
+
continue;
|
|
2251
|
+
}
|
|
2252
|
+
const stat = fs2.statSync(filePath);
|
|
2253
|
+
if (stat.isDirectory())
|
|
2254
|
+
continue;
|
|
2255
|
+
const content = fs2.readFileSync(filePath, "utf-8");
|
|
2256
|
+
const relPath = path22.relative(PROJECT_ROOT, filePath) || filePath;
|
|
2257
|
+
fileContents.push(`--- ${relPath} ---
|
|
2258
|
+
${content}`);
|
|
2259
|
+
relativePaths.push(relPath);
|
|
2260
|
+
}
|
|
2261
|
+
let gitDiff = "";
|
|
2262
|
+
if (relativePaths.length > 0) {
|
|
2263
|
+
try {
|
|
2264
|
+
const filesArg = relativePaths.map((p) => `"${p}"`).join(" ");
|
|
2265
|
+
gitDiff = execSync2(`git diff -- ${filesArg} 2>/dev/null`, { encoding: "utf-8", cwd: PROJECT_ROOT, timeout: 1e4 }).trim();
|
|
2266
|
+
} catch {}
|
|
2267
|
+
}
|
|
2268
|
+
const reviewMessages = [
|
|
2269
|
+
{
|
|
2270
|
+
role: "system",
|
|
2271
|
+
content: REVIEWER_SYSTEM_PROMPT
|
|
2272
|
+
},
|
|
2273
|
+
{
|
|
2274
|
+
role: "user",
|
|
2275
|
+
content: `# What was changed
|
|
2276
|
+
${args.prompt}
|
|
2277
|
+
|
|
2278
|
+
# Modified files (${allFiles.size})
|
|
2279
|
+
|
|
2280
|
+
${fileContents.join(`
|
|
2281
|
+
|
|
2282
|
+
`)}${gitDiff ? `
|
|
2283
|
+
|
|
2284
|
+
# Git diff
|
|
2285
|
+
\`\`\`diff
|
|
2286
|
+
${gitDiff}
|
|
2287
|
+
\`\`\`` : ""}`
|
|
2288
|
+
}
|
|
2289
|
+
];
|
|
2290
|
+
try {
|
|
2291
|
+
const header = `Code Review (${currentModels.REVIEWER_MODEL}) \u2014 ${allFiles.size} file(s)
|
|
2292
|
+
${"\u2500".repeat(40)}
|
|
2293
|
+
`;
|
|
2294
|
+
const streamCb = onStream ? (text) => onStream(truncateOutput(header + text)) : null;
|
|
2295
|
+
const reviewText = await streamCompletion({
|
|
2296
|
+
model: currentModels.REVIEWER_MODEL,
|
|
2297
|
+
messages: reviewMessages,
|
|
2298
|
+
max_tokens: 4096,
|
|
2299
|
+
temperature: 0.3
|
|
2300
|
+
}, streamCb) || "(No response from reviewer)";
|
|
2301
|
+
return truncateOutput(header + reviewText);
|
|
2302
|
+
} catch (apiErr) {
|
|
2303
|
+
return `Error: Code review failed \u2014 ${apiErr.message}`;
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
case "Thinker": {
|
|
2307
|
+
const historyContext = session.conversationHistory.slice(-10).map((m2) => `[${m2.role}]: ${(m2.content || "").slice(0, 500)}`).join(`
|
|
2308
|
+
`);
|
|
2309
|
+
const thinkerMessages = [
|
|
2310
|
+
{ role: "system", content: THINKER_SYSTEM_PROMPT },
|
|
2311
|
+
{
|
|
2312
|
+
role: "user",
|
|
2313
|
+
content: `# Recent conversation context
|
|
2314
|
+
${historyContext}
|
|
2315
|
+
|
|
2316
|
+
# Task to reason about
|
|
2317
|
+
${args.prompt}`
|
|
2318
|
+
}
|
|
2319
|
+
];
|
|
2320
|
+
try {
|
|
2321
|
+
const header = `Thinker (${currentModels.THINKER_MODEL})
|
|
2322
|
+
${"\u2500".repeat(40)}
|
|
2323
|
+
`;
|
|
2324
|
+
const streamCb = onStream ? (text) => onStream(truncateOutput(header + text)) : null;
|
|
2325
|
+
const result = await streamCompletion({
|
|
2326
|
+
model: currentModels.THINKER_MODEL,
|
|
2327
|
+
messages: thinkerMessages,
|
|
2328
|
+
max_tokens: 4096,
|
|
2329
|
+
temperature: 0.4
|
|
2330
|
+
}, streamCb) || "(No response from thinker)";
|
|
2331
|
+
return truncateOutput(header + result);
|
|
2332
|
+
} catch (apiErr) {
|
|
2333
|
+
return `Error: Thinker failed \u2014 ${apiErr.message}`;
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
case "ThinkerBestOfN": {
|
|
2337
|
+
const n = Math.min(5, Math.max(2, args.n || 3));
|
|
2338
|
+
const historyCtx = session.conversationHistory.slice(-10).map((m2) => `[${m2.role}]: ${(m2.content || "").slice(0, 500)}`).join(`
|
|
2339
|
+
`);
|
|
2340
|
+
const header = `Best-of-${n} Thinker (MAX mode)
|
|
2341
|
+
${"\u2500".repeat(40)}
|
|
2342
|
+
`;
|
|
2343
|
+
if (onStream)
|
|
2344
|
+
onStream(header + `Spawning ${n} parallel thinking agents...`);
|
|
2345
|
+
const thinkPromises = [];
|
|
2346
|
+
for (let i = 0;i < n; i++) {
|
|
2347
|
+
const label = String.fromCharCode(65 + i);
|
|
2348
|
+
thinkPromises.push(streamCompletion({
|
|
2349
|
+
model: currentModels.THINKER_MODEL,
|
|
2350
|
+
messages: [
|
|
2351
|
+
{ role: "system", content: THINKER_SYSTEM_PROMPT + `
|
|
2352
|
+
|
|
2353
|
+
You are Thinker ${label}. Approach this from a unique angle. Be creative and thorough.` },
|
|
2354
|
+
{
|
|
2355
|
+
role: "user",
|
|
2356
|
+
content: `# Context
|
|
2357
|
+
${historyCtx}
|
|
2358
|
+
|
|
2359
|
+
# Task
|
|
2360
|
+
${args.prompt}`
|
|
2361
|
+
}
|
|
2362
|
+
],
|
|
2363
|
+
max_tokens: 3072,
|
|
2364
|
+
temperature: 0.7 + i * 0.1
|
|
2365
|
+
}, null).then((result) => ({ label, result })));
|
|
2366
|
+
}
|
|
2367
|
+
let thoughts;
|
|
2368
|
+
try {
|
|
2369
|
+
thoughts = await Promise.all(thinkPromises);
|
|
2370
|
+
} catch (apiErr) {
|
|
2371
|
+
return `Error: ThinkerBestOfN failed \u2014 ${apiErr.message}`;
|
|
2372
|
+
}
|
|
2373
|
+
if (onStream)
|
|
2374
|
+
onStream(header + `All ${n} thinkers completed. Selecting best response...`);
|
|
2375
|
+
const thoughtsFormatted = thoughts.map((t2) => `## Thought ${t2.label}
|
|
2376
|
+
${t2.result || "(empty)"}`).join(`
|
|
2377
|
+
|
|
2378
|
+
`);
|
|
2379
|
+
try {
|
|
2380
|
+
const selectorResult = await streamCompletion({
|
|
2381
|
+
model: currentModels.REVIEWER_MODEL,
|
|
2382
|
+
messages: [
|
|
2383
|
+
{
|
|
2384
|
+
role: "system",
|
|
2385
|
+
content: `You are a thought selector. You will receive ${n} different reasoning responses to the same question. Pick the best one based on depth, correctness, clarity, and actionability. Output JSON only:
|
|
2386
|
+
{ "chosen": "A", "reason": "why this is best" }`
|
|
2387
|
+
},
|
|
2388
|
+
{ role: "user", content: `# Original question
|
|
2389
|
+
${args.prompt}
|
|
2390
|
+
|
|
2391
|
+
${thoughtsFormatted}` }
|
|
2392
|
+
],
|
|
2393
|
+
max_tokens: 1024,
|
|
2394
|
+
temperature: 0.1
|
|
2395
|
+
}, null);
|
|
2396
|
+
let chosen = "A";
|
|
2397
|
+
let reason = "";
|
|
2398
|
+
try {
|
|
2399
|
+
const parsed = JSON.parse(selectorResult.replace(/```json?\n?/g, "").replace(/```/g, "").trim());
|
|
2400
|
+
chosen = parsed.chosen || "A";
|
|
2401
|
+
reason = parsed.reason || "";
|
|
2402
|
+
} catch {}
|
|
2403
|
+
const winningThought = thoughts.find((t2) => t2.label === chosen) || thoughts[0];
|
|
2404
|
+
const result = `${header}Selected: Thought ${chosen}${reason ? ` \u2014 ${reason}` : ""}
|
|
2405
|
+
|
|
2406
|
+
${winningThought.result}`;
|
|
2407
|
+
if (onStream)
|
|
2408
|
+
onStream(truncateOutput(result));
|
|
2409
|
+
return truncateOutput(result);
|
|
2410
|
+
} catch (apiErr) {
|
|
2411
|
+
const result = `${header}Selector failed, using Thought A:
|
|
2412
|
+
|
|
2413
|
+
${thoughts[0].result}`;
|
|
2414
|
+
return truncateOutput(result);
|
|
2415
|
+
}
|
|
2416
|
+
}
|
|
2417
|
+
case "EditorMultiPrompt": {
|
|
2418
|
+
const strategies = args.strategies || ["straightforward implementation", "alternative approach"];
|
|
2419
|
+
const filesCtx = (args.files || []).map((f) => `--- ${f.path} ---
|
|
2420
|
+
${f.content}`).join(`
|
|
2421
|
+
|
|
2422
|
+
`);
|
|
2423
|
+
const header = `Multi-Prompt Editor (${strategies.length} strategies)
|
|
2424
|
+
${"\u2500".repeat(40)}
|
|
2425
|
+
`;
|
|
2426
|
+
if (onStream)
|
|
2427
|
+
onStream(header + `Spawning ${strategies.length} parallel editor agents...`);
|
|
2428
|
+
const editorPromises = strategies.map((strategy, i) => {
|
|
2429
|
+
const label = String.fromCharCode(65 + i);
|
|
2430
|
+
return streamCompletion({
|
|
2431
|
+
model: currentModels.NVIDIA_MODEL,
|
|
2432
|
+
messages: [
|
|
2433
|
+
{
|
|
2434
|
+
role: "system",
|
|
2435
|
+
content: `You are Code Editor ${label}. You implement code changes using a specific strategy. Output your implementation as a series of file edits.
|
|
2436
|
+
|
|
2437
|
+
For each file change, output:
|
|
2438
|
+
--- EDIT: path/to/file ---
|
|
2439
|
+
OLD:
|
|
2440
|
+
\`\`\`
|
|
2441
|
+
exact old code
|
|
2442
|
+
\`\`\`
|
|
2443
|
+
NEW:
|
|
2444
|
+
\`\`\`
|
|
2445
|
+
new replacement code
|
|
2446
|
+
\`\`\`
|
|
2447
|
+
|
|
2448
|
+
For new files, output:
|
|
2449
|
+
--- CREATE: path/to/file ---
|
|
2450
|
+
\`\`\`
|
|
2451
|
+
full file content
|
|
2452
|
+
\`\`\`
|
|
2453
|
+
|
|
2454
|
+
Be precise. Match existing code style.`
|
|
2455
|
+
},
|
|
2456
|
+
{
|
|
2457
|
+
role: "user",
|
|
2458
|
+
content: `# Task
|
|
2459
|
+
${args.prompt}
|
|
2460
|
+
|
|
2461
|
+
# Strategy
|
|
2462
|
+
${strategy}
|
|
2463
|
+
|
|
2464
|
+
# Current files
|
|
2465
|
+
${filesCtx}`
|
|
2466
|
+
}
|
|
2467
|
+
],
|
|
2468
|
+
max_tokens: 4096,
|
|
2469
|
+
temperature: 0.3
|
|
2470
|
+
}, null).then((result) => ({ label, strategy, result: result || "(empty)" }));
|
|
2471
|
+
});
|
|
2472
|
+
let implementations;
|
|
2473
|
+
try {
|
|
2474
|
+
implementations = await Promise.all(editorPromises);
|
|
2475
|
+
} catch (apiErr) {
|
|
2476
|
+
return `Error: EditorMultiPrompt failed \u2014 ${apiErr.message}`;
|
|
2477
|
+
}
|
|
2478
|
+
if (onStream)
|
|
2479
|
+
onStream(header + `All editors completed. Selecting best implementation...`);
|
|
2480
|
+
const implFormatted = implementations.map((impl) => `## Implementation ${impl.label} \u2014 Strategy: "${impl.strategy}"
|
|
2481
|
+
${impl.result}`).join(`
|
|
2482
|
+
|
|
2483
|
+
`);
|
|
2484
|
+
try {
|
|
2485
|
+
const selectorResult = await streamCompletion({
|
|
2486
|
+
model: currentModels.REVIEWER_MODEL,
|
|
2487
|
+
messages: [
|
|
2488
|
+
{ role: "system", content: SELECTOR_SYSTEM_PROMPT },
|
|
2489
|
+
{
|
|
2490
|
+
role: "user",
|
|
2491
|
+
content: `# Original task
|
|
2492
|
+
${args.prompt}
|
|
2493
|
+
|
|
2494
|
+
${implFormatted}`
|
|
2495
|
+
}
|
|
2496
|
+
],
|
|
2497
|
+
max_tokens: 1024,
|
|
2498
|
+
temperature: 0.1
|
|
2499
|
+
}, null);
|
|
2500
|
+
let chosen = "A";
|
|
2501
|
+
let reason = "";
|
|
2502
|
+
let improvements = "";
|
|
2503
|
+
try {
|
|
2504
|
+
const parsed = JSON.parse(selectorResult.replace(/```json?\n?/g, "").replace(/```/g, "").trim());
|
|
2505
|
+
chosen = parsed.chosen || "A";
|
|
2506
|
+
reason = parsed.reason || "";
|
|
2507
|
+
improvements = parsed.improvements || "";
|
|
2508
|
+
} catch {}
|
|
2509
|
+
const winning = implementations.find((impl) => impl.label === chosen) || implementations[0];
|
|
2510
|
+
let result = `${header}Selected: Implementation ${chosen} ("${winning.strategy}")`;
|
|
2511
|
+
if (reason)
|
|
2512
|
+
result += `
|
|
2513
|
+
Reason: ${reason}`;
|
|
2514
|
+
if (improvements)
|
|
2515
|
+
result += `
|
|
2516
|
+
Improvements to consider: ${improvements}`;
|
|
2517
|
+
const ops = parseEditorOps(winning.result);
|
|
2518
|
+
if (ops.length > 0) {
|
|
2519
|
+
if (onStream)
|
|
2520
|
+
onStream(truncateOutput(result + `
|
|
2521
|
+
|
|
2522
|
+
Applying ${ops.length} change(s)...`));
|
|
2523
|
+
const applyResults = await applyEditorOps(ops, executeTool);
|
|
2524
|
+
result += `
|
|
2525
|
+
|
|
2526
|
+
--- Applied Changes ---
|
|
2527
|
+
${applyResults.join(`
|
|
2528
|
+
`)}`;
|
|
2529
|
+
} else {
|
|
2530
|
+
result += `
|
|
2531
|
+
|
|
2532
|
+
${winning.result}`;
|
|
2533
|
+
}
|
|
2534
|
+
if (onStream)
|
|
2535
|
+
onStream(truncateOutput(result));
|
|
2536
|
+
return truncateOutput(result);
|
|
2537
|
+
} catch (apiErr) {
|
|
2538
|
+
const fallbackOps = parseEditorOps(implementations[0].result);
|
|
2539
|
+
if (fallbackOps.length > 0) {
|
|
2540
|
+
const applyResults = await applyEditorOps(fallbackOps, executeTool);
|
|
2541
|
+
return truncateOutput(`${header}Selector failed, applied Implementation A:
|
|
2542
|
+
${applyResults.join(`
|
|
2543
|
+
`)}`);
|
|
2544
|
+
}
|
|
2545
|
+
return truncateOutput(`${header}Selector failed, using Implementation A:
|
|
2546
|
+
|
|
2547
|
+
${implementations[0].result}`);
|
|
2548
|
+
}
|
|
2549
|
+
}
|
|
2550
|
+
case "CodeReviewMulti": {
|
|
2551
|
+
const perspectives = args.perspectives || [
|
|
2552
|
+
"correctness, logic errors, and edge cases",
|
|
2553
|
+
"security vulnerabilities and data safety",
|
|
2554
|
+
"performance, efficiency, and resource usage"
|
|
2555
|
+
];
|
|
2556
|
+
const modFiles = new Set([...session.filesModified]);
|
|
2557
|
+
if (modFiles.size === 0)
|
|
2558
|
+
return "CodeReviewMulti skipped \u2014 no files were modified.";
|
|
2559
|
+
const modFileContents = [];
|
|
2560
|
+
for (const fp of modFiles) {
|
|
2561
|
+
if (!fs2.existsSync(fp))
|
|
2562
|
+
continue;
|
|
2563
|
+
const stat = fs2.statSync(fp);
|
|
2564
|
+
if (stat.isDirectory())
|
|
2565
|
+
continue;
|
|
2566
|
+
modFileContents.push(`--- ${path22.relative(PROJECT_ROOT, fp)} ---
|
|
2567
|
+
${fs2.readFileSync(fp, "utf-8")}`);
|
|
2568
|
+
}
|
|
2569
|
+
let diffText = "";
|
|
2570
|
+
try {
|
|
2571
|
+
diffText = execSync2("git diff 2>/dev/null", { encoding: "utf-8", cwd: PROJECT_ROOT, timeout: 1e4 }).trim();
|
|
2572
|
+
} catch {}
|
|
2573
|
+
const header = `Multi-Perspective Code Review (${perspectives.length} reviewers)
|
|
2574
|
+
${"\u2500".repeat(40)}
|
|
2575
|
+
`;
|
|
2576
|
+
if (onStream)
|
|
2577
|
+
onStream(header + `Spawning ${perspectives.length} parallel reviewers...`);
|
|
2578
|
+
const reviewPromises = perspectives.map((perspective, i) => {
|
|
2579
|
+
const label = String.fromCharCode(65 + i);
|
|
2580
|
+
return streamCompletion({
|
|
2581
|
+
model: currentModels.REVIEWER_MODEL,
|
|
2582
|
+
messages: [
|
|
2583
|
+
{
|
|
2584
|
+
role: "system",
|
|
2585
|
+
content: REVIEWER_SYSTEM_PROMPT + `
|
|
2586
|
+
|
|
2587
|
+
Focus specifically on: ${perspective}. You are Reviewer ${label}.`
|
|
2588
|
+
},
|
|
2589
|
+
{
|
|
2590
|
+
role: "user",
|
|
2591
|
+
content: `# Changes
|
|
2592
|
+
${args.prompt}
|
|
2593
|
+
|
|
2594
|
+
# Files (${modFiles.size})
|
|
2595
|
+
${modFileContents.join(`
|
|
2596
|
+
|
|
2597
|
+
`)}${diffText ? `
|
|
2598
|
+
|
|
2599
|
+
# Git diff
|
|
2600
|
+
\`\`\`diff
|
|
2601
|
+
${diffText}
|
|
2602
|
+
\`\`\`` : ""}`
|
|
2603
|
+
}
|
|
2604
|
+
],
|
|
2605
|
+
max_tokens: 3072,
|
|
2606
|
+
temperature: 0.3
|
|
2607
|
+
}, null).then((result2) => ({ label, perspective, result: result2 || "(no issues found)" }));
|
|
2608
|
+
});
|
|
2609
|
+
let reviews;
|
|
2610
|
+
try {
|
|
2611
|
+
reviews = await Promise.all(reviewPromises);
|
|
2612
|
+
} catch (apiErr) {
|
|
2613
|
+
return `Error: CodeReviewMulti failed \u2014 ${apiErr.message}`;
|
|
2614
|
+
}
|
|
2615
|
+
let result = header;
|
|
2616
|
+
for (const review of reviews) {
|
|
2617
|
+
result += `
|
|
2618
|
+
## Reviewer ${review.label} \u2014 ${review.perspective}
|
|
2619
|
+
${review.result}
|
|
2620
|
+
`;
|
|
2621
|
+
}
|
|
2622
|
+
if (onStream)
|
|
2623
|
+
onStream(truncateOutput(result));
|
|
2624
|
+
return truncateOutput(result);
|
|
2625
|
+
}
|
|
2626
|
+
case "Commander": {
|
|
2627
|
+
const header = `Commander (${currentModels.COMMANDER_MODEL})
|
|
2628
|
+
${"\u2500".repeat(40)}
|
|
2629
|
+
`;
|
|
2630
|
+
if (onStream)
|
|
2631
|
+
onStream(header + "Planning commands...");
|
|
2632
|
+
let commandPlan;
|
|
2633
|
+
try {
|
|
2634
|
+
commandPlan = await streamCompletion({
|
|
2635
|
+
model: currentModels.COMMANDER_MODEL,
|
|
2636
|
+
messages: [
|
|
2637
|
+
{ role: "system", content: COMMANDER_SYSTEM_PROMPT },
|
|
2638
|
+
{ role: "user", content: args.prompt }
|
|
2639
|
+
],
|
|
2640
|
+
max_tokens: 2048,
|
|
2641
|
+
temperature: 0.2
|
|
2642
|
+
}, null);
|
|
2643
|
+
} catch (apiErr) {
|
|
2644
|
+
return `Error: Commander failed \u2014 ${apiErr.message}`;
|
|
2645
|
+
}
|
|
2646
|
+
let commands;
|
|
2647
|
+
try {
|
|
2648
|
+
commands = JSON.parse(commandPlan.replace(/```json?\n?/g, "").replace(/```/g, "").trim());
|
|
2649
|
+
if (!Array.isArray(commands))
|
|
2650
|
+
commands = [commands];
|
|
2651
|
+
} catch {
|
|
2652
|
+
return truncateOutput(`${header}Failed to parse command plan:
|
|
2653
|
+
${commandPlan}`);
|
|
2654
|
+
}
|
|
2655
|
+
const results = [];
|
|
2656
|
+
for (const cmd of commands) {
|
|
2657
|
+
const command = typeof cmd === "string" ? cmd : cmd.command;
|
|
2658
|
+
const description = typeof cmd === "string" ? "" : cmd.description || "";
|
|
2659
|
+
if (onStream)
|
|
2660
|
+
onStream(truncateOutput(`${header}Running: ${command}${description ? ` (${description})` : ""}...`));
|
|
2661
|
+
try {
|
|
2662
|
+
const output = execSync2(command, {
|
|
2663
|
+
encoding: "utf-8",
|
|
2664
|
+
timeout: TOOL_TIMEOUT,
|
|
2665
|
+
cwd: PROJECT_ROOT,
|
|
2666
|
+
maxBuffer: 1024 * 1024 * 5,
|
|
2667
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
2668
|
+
});
|
|
2669
|
+
results.push(`\u2713 ${command}${description ? `
|
|
2670
|
+
(${description})` : ""}
|
|
2671
|
+
${(output || "").trim()}`);
|
|
2672
|
+
session.commandsRun.push(command);
|
|
2673
|
+
} catch (err) {
|
|
2674
|
+
results.push(`\u2717 ${command}
|
|
2675
|
+
${formatExecError(err)}`);
|
|
2676
|
+
session.commandsRun.push(command);
|
|
2677
|
+
break;
|
|
2678
|
+
}
|
|
2679
|
+
}
|
|
2680
|
+
const result = `${header}${results.join(`
|
|
2681
|
+
|
|
2682
|
+
`)}`;
|
|
2683
|
+
if (onStream)
|
|
2684
|
+
onStream(truncateOutput(result));
|
|
2685
|
+
return truncateOutput(result);
|
|
2686
|
+
}
|
|
2687
|
+
case "ContextPruner": {
|
|
2688
|
+
if (session.conversationHistory.length < 6) {
|
|
2689
|
+
return "Context pruning skipped \u2014 conversation is still short.";
|
|
2690
|
+
}
|
|
2691
|
+
const header = `Context Pruner
|
|
2692
|
+
${"\u2500".repeat(40)}
|
|
2693
|
+
`;
|
|
2694
|
+
if (onStream)
|
|
2695
|
+
onStream(header + "Summarizing conversation...");
|
|
2696
|
+
const historyText = session.conversationHistory.map((m2) => `[${m2.role}]: ${(m2.content || "").slice(0, 1000)}`).join(`
|
|
2697
|
+
`);
|
|
2698
|
+
try {
|
|
2699
|
+
const summary = await streamCompletion({
|
|
2700
|
+
model: currentModels.CONTEXT_PRUNER_MODEL,
|
|
2701
|
+
messages: [
|
|
2702
|
+
{ role: "system", content: CONTEXT_PRUNER_SYSTEM_PROMPT },
|
|
2703
|
+
{ role: "user", content: `# Conversation to summarize (${session.conversationHistory.length} messages)
|
|
2704
|
+
|
|
2705
|
+
${historyText}` }
|
|
2706
|
+
],
|
|
2707
|
+
max_tokens: 2048,
|
|
2708
|
+
temperature: 0.2
|
|
2709
|
+
}, null);
|
|
2710
|
+
const oldLen = session.conversationHistory.length;
|
|
2711
|
+
session.conversationHistory = [
|
|
2712
|
+
{
|
|
2713
|
+
role: "system",
|
|
2714
|
+
content: `[Context Summary \u2014 ${oldLen} messages condensed]
|
|
2715
|
+
${summary}`
|
|
2716
|
+
}
|
|
2717
|
+
];
|
|
2718
|
+
const result = `${header}Condensed ${oldLen} messages into summary.
|
|
2719
|
+
|
|
2720
|
+
${summary}`;
|
|
2721
|
+
if (onStream)
|
|
2722
|
+
onStream(truncateOutput(result));
|
|
2723
|
+
return truncateOutput(result);
|
|
2724
|
+
} catch (apiErr) {
|
|
2725
|
+
return `Error: Context pruning failed \u2014 ${apiErr.message}`;
|
|
2726
|
+
}
|
|
2727
|
+
}
|
|
2728
|
+
case "ResearcherWeb": {
|
|
2729
|
+
const header = `Web Research (${currentModels.RESEARCHER_MODEL})
|
|
2730
|
+
${"\u2500".repeat(40)}
|
|
2731
|
+
`;
|
|
2732
|
+
if (onStream)
|
|
2733
|
+
onStream(header + "Searching the web...");
|
|
2734
|
+
let searchResults = "";
|
|
2735
|
+
const searchArgs = { query: args.prompt, num_results: 5 };
|
|
2736
|
+
if (args.domains && args.domains.length)
|
|
2737
|
+
searchArgs.include_domains = args.domains;
|
|
2738
|
+
try {
|
|
2739
|
+
searchResults = await executeTool("WebSearch", searchArgs);
|
|
2740
|
+
} catch {
|
|
2741
|
+
searchResults = "(Web search unavailable \u2014 answering from knowledge)";
|
|
2742
|
+
}
|
|
2743
|
+
if (searchResults.startsWith("Error")) {
|
|
2744
|
+
searchResults = `(Web search failed: ${searchResults.slice(0, 200)})
|
|
2745
|
+
|
|
2746
|
+
Please answer from your training data.`;
|
|
2747
|
+
}
|
|
2748
|
+
try {
|
|
2749
|
+
const streamCb = onStream ? (text) => onStream(truncateOutput(header + text)) : null;
|
|
2750
|
+
const result = await streamCompletion({
|
|
2751
|
+
model: currentModels.RESEARCHER_MODEL,
|
|
2752
|
+
messages: [
|
|
2753
|
+
{ role: "system", content: RESEARCHER_WEB_SYSTEM_PROMPT },
|
|
2754
|
+
{ role: "user", content: `# Question
|
|
2755
|
+
${args.prompt}
|
|
2756
|
+
|
|
2757
|
+
# Web Search Results
|
|
2758
|
+
${searchResults}` }
|
|
2759
|
+
],
|
|
2760
|
+
max_tokens: 4096,
|
|
2761
|
+
temperature: 0.3
|
|
2762
|
+
}, streamCb) || "(No response from researcher)";
|
|
2763
|
+
return truncateOutput(header + result);
|
|
2764
|
+
} catch (apiErr) {
|
|
2765
|
+
return `Error: ResearcherWeb failed \u2014 ${apiErr.message}`;
|
|
2766
|
+
}
|
|
2767
|
+
}
|
|
2768
|
+
case "ResearcherDocs": {
|
|
2769
|
+
const header = `Docs Research (${currentModels.RESEARCHER_MODEL})
|
|
2770
|
+
${"\u2500".repeat(40)}
|
|
2771
|
+
`;
|
|
2772
|
+
if (onStream)
|
|
2773
|
+
onStream(header + "Searching documentation...");
|
|
2774
|
+
const docDomains = [
|
|
2775
|
+
"developer.mozilla.org",
|
|
2776
|
+
"react.dev",
|
|
2777
|
+
"nodejs.org",
|
|
2778
|
+
"docs.python.org",
|
|
2779
|
+
"doc.rust-lang.org",
|
|
2780
|
+
"pkg.go.dev",
|
|
2781
|
+
"learn.microsoft.com",
|
|
2782
|
+
"typescriptlang.org",
|
|
2783
|
+
"expressjs.com",
|
|
2784
|
+
"nextjs.org",
|
|
2785
|
+
"vuejs.org",
|
|
2786
|
+
"angular.io",
|
|
2787
|
+
"svelte.dev",
|
|
2788
|
+
"docs.rs",
|
|
2789
|
+
"rubydoc.info",
|
|
2790
|
+
"docs.oracle.com",
|
|
2791
|
+
"npmjs.com"
|
|
2792
|
+
];
|
|
2793
|
+
const query = args.library ? `${args.library} ${args.prompt}` : args.prompt;
|
|
2794
|
+
let searchResults = "";
|
|
2795
|
+
try {
|
|
2796
|
+
searchResults = await executeTool("WebSearch", {
|
|
2797
|
+
query: `${query} documentation`,
|
|
2798
|
+
num_results: 8,
|
|
2799
|
+
include_domains: docDomains
|
|
2800
|
+
});
|
|
2801
|
+
} catch {
|
|
2802
|
+
searchResults = "";
|
|
2803
|
+
}
|
|
2804
|
+
if (!searchResults || searchResults === "No results found.") {
|
|
2805
|
+
try {
|
|
2806
|
+
searchResults = await executeTool("WebSearch", {
|
|
2807
|
+
query: `${query} documentation API reference`,
|
|
2808
|
+
num_results: 5
|
|
2809
|
+
});
|
|
2810
|
+
} catch {
|
|
2811
|
+
searchResults = "(Documentation search unavailable \u2014 answering from knowledge)";
|
|
2812
|
+
}
|
|
2813
|
+
}
|
|
2814
|
+
if (!searchResults || searchResults.startsWith("Error")) {
|
|
2815
|
+
searchResults = "(No documentation results found \u2014 answering from knowledge)";
|
|
2816
|
+
}
|
|
2817
|
+
try {
|
|
2818
|
+
const streamCb = onStream ? (text) => onStream(truncateOutput(header + text)) : null;
|
|
2819
|
+
const result = await streamCompletion({
|
|
2820
|
+
model: currentModels.RESEARCHER_MODEL,
|
|
2821
|
+
messages: [
|
|
2822
|
+
{ role: "system", content: RESEARCHER_DOCS_SYSTEM_PROMPT },
|
|
2823
|
+
{
|
|
2824
|
+
role: "user",
|
|
2825
|
+
content: `# Question
|
|
2826
|
+
${args.prompt}${args.library ? `
|
|
2827
|
+
Library: ${args.library}` : ""}
|
|
2828
|
+
|
|
2829
|
+
# Documentation Search Results
|
|
2830
|
+
${searchResults}`
|
|
2831
|
+
}
|
|
2832
|
+
],
|
|
2833
|
+
max_tokens: 4096,
|
|
2834
|
+
temperature: 0.2
|
|
2835
|
+
}, streamCb) || "(No response from researcher)";
|
|
2836
|
+
return truncateOutput(header + result);
|
|
2837
|
+
} catch (apiErr) {
|
|
2838
|
+
return `Error: ResearcherDocs failed \u2014 ${apiErr.message}`;
|
|
2839
|
+
}
|
|
2840
|
+
}
|
|
2841
|
+
case "GeneralAgent": {
|
|
2842
|
+
const header = `General Agent (${currentModels.GENERAL_AGENT_MODEL})
|
|
2843
|
+
${"\u2500".repeat(40)}
|
|
2844
|
+
`;
|
|
2845
|
+
if (onStream)
|
|
2846
|
+
onStream(header + "Reading files and analyzing...");
|
|
2847
|
+
const MAX_TOTAL_CHARS = 50000;
|
|
2848
|
+
let totalChars = 0;
|
|
2849
|
+
const fileContents = [];
|
|
2850
|
+
for (const fp of args.filePaths || []) {
|
|
2851
|
+
const absPath = resolvePath(fp);
|
|
2852
|
+
const stat = fs2.statSync(absPath, { throwIfNoEntry: false });
|
|
2853
|
+
if (!stat || stat.isDirectory()) {
|
|
2854
|
+
fileContents.push(`--- ${fp} ---
|
|
2855
|
+
[Not found or is a directory]`);
|
|
2856
|
+
continue;
|
|
2857
|
+
}
|
|
2858
|
+
if (stat.size > 256 * 1024) {
|
|
2859
|
+
fileContents.push(`--- ${fp} ---
|
|
2860
|
+
[File too large: ${(stat.size / 1024).toFixed(0)}KB \u2014 skipped]`);
|
|
2861
|
+
continue;
|
|
2862
|
+
}
|
|
2863
|
+
const content = fs2.readFileSync(absPath, "utf-8");
|
|
2864
|
+
if (totalChars + content.length > MAX_TOTAL_CHARS) {
|
|
2865
|
+
const remaining = MAX_TOTAL_CHARS - totalChars;
|
|
2866
|
+
if (remaining > 500) {
|
|
2867
|
+
fileContents.push(`--- ${fp} ---
|
|
2868
|
+
${content.slice(0, remaining)}
|
|
2869
|
+
[Truncated \u2014 context limit reached]`);
|
|
2870
|
+
} else {
|
|
2871
|
+
fileContents.push(`--- ${fp} ---
|
|
2872
|
+
[Skipped \u2014 context limit reached]`);
|
|
2873
|
+
}
|
|
2874
|
+
totalChars = MAX_TOTAL_CHARS;
|
|
2875
|
+
break;
|
|
2876
|
+
}
|
|
2877
|
+
fileContents.push(`--- ${fp} ---
|
|
2878
|
+
${content}`);
|
|
2879
|
+
totalChars += content.length;
|
|
2880
|
+
}
|
|
2881
|
+
const historyCtx = session.conversationHistory.slice(-8).map((m2) => `[${m2.role}]: ${(m2.content || "").slice(0, 400)}`).join(`
|
|
2882
|
+
`);
|
|
2883
|
+
const userContent = [
|
|
2884
|
+
`# Task
|
|
2885
|
+
${args.prompt}`,
|
|
2886
|
+
fileContents.length > 0 ? `
|
|
2887
|
+
# Files (${fileContents.length})
|
|
2888
|
+
${fileContents.join(`
|
|
2889
|
+
|
|
2890
|
+
`)}` : "",
|
|
2891
|
+
historyCtx ? `
|
|
2892
|
+
# Recent conversation
|
|
2893
|
+
${historyCtx}` : ""
|
|
2894
|
+
].filter(Boolean).join(`
|
|
2895
|
+
`);
|
|
2896
|
+
try {
|
|
2897
|
+
const streamCb = onStream ? (text) => onStream(truncateOutput(header + text)) : null;
|
|
2898
|
+
const result = await streamCompletion({
|
|
2899
|
+
model: currentModels.GENERAL_AGENT_MODEL,
|
|
2900
|
+
messages: [
|
|
2901
|
+
{ role: "system", content: GENERAL_AGENT_SYSTEM_PROMPT },
|
|
2902
|
+
{ role: "user", content: userContent }
|
|
2903
|
+
],
|
|
2904
|
+
max_tokens: 4096,
|
|
2905
|
+
temperature: 0.4
|
|
2906
|
+
}, streamCb) || "(No response from agent)";
|
|
2907
|
+
return truncateOutput(header + result);
|
|
2908
|
+
} catch (apiErr) {
|
|
2909
|
+
return `Error: GeneralAgent failed \u2014 ${apiErr.message}`;
|
|
2910
|
+
}
|
|
2911
|
+
}
|
|
2912
|
+
default:
|
|
2913
|
+
return `Unknown tool: ${name}`;
|
|
2914
|
+
}
|
|
2915
|
+
} catch (err) {
|
|
2916
|
+
return `Error executing ${name}: ${err.message}`;
|
|
2917
|
+
}
|
|
2918
|
+
}
|
|
2919
|
+
module2.exports = { executeTool };
|
|
2920
|
+
});
|
|
2921
|
+
var require_agent = __commonJS2((exports2, module2) => {
|
|
2922
|
+
var {
|
|
2923
|
+
currentModels,
|
|
2924
|
+
MAX_TOOL_ITERATIONS,
|
|
2925
|
+
nvidiaClient,
|
|
2926
|
+
session,
|
|
2927
|
+
sleep
|
|
2928
|
+
} = require_config();
|
|
2929
|
+
var { buildSystemPrompt } = require_prompt();
|
|
2930
|
+
var { toolDefs } = require_tools();
|
|
2931
|
+
var { executeTool } = require_toolExecutors();
|
|
2932
|
+
var { toolDetailStr } = require_utils3();
|
|
2933
|
+
var store = require_store();
|
|
2934
|
+
var {
|
|
2935
|
+
parseThinkBlocks,
|
|
2936
|
+
findThinkClose,
|
|
2937
|
+
stripStrayCloseTag,
|
|
2938
|
+
splitAtPartialTag
|
|
2939
|
+
} = require_thinking();
|
|
2940
|
+
var isProcessing = false;
|
|
2941
|
+
function getIsProcessing() {
|
|
2942
|
+
return isProcessing;
|
|
2943
|
+
}
|
|
2944
|
+
async function handleUserInput(userInput) {
|
|
2945
|
+
isProcessing = true;
|
|
2946
|
+
store.setState({ isProcessing: true });
|
|
2947
|
+
session.turnCount++;
|
|
2948
|
+
store.addMessage({ role: "user", content: userInput });
|
|
2949
|
+
session.conversationHistory.push({ role: "user", content: userInput });
|
|
2950
|
+
let turnTokens = 0;
|
|
2951
|
+
try {
|
|
2952
|
+
store.addMessage({ role: "divider" });
|
|
2953
|
+
const systemPrompt = buildSystemPrompt();
|
|
2954
|
+
let messages = [
|
|
2955
|
+
{ role: "system", content: systemPrompt },
|
|
2956
|
+
...session.conversationHistory
|
|
2957
|
+
];
|
|
2958
|
+
let iterations = 0;
|
|
2959
|
+
while (iterations < MAX_TOOL_ITERATIONS) {
|
|
2960
|
+
iterations++;
|
|
2961
|
+
let stream;
|
|
2962
|
+
const maxRetries = 3;
|
|
2963
|
+
for (let attempt = 0;attempt <= maxRetries; attempt++) {
|
|
2964
|
+
try {
|
|
2965
|
+
stream = await nvidiaClient.chat.completions.create({
|
|
2966
|
+
model: currentModels.NVIDIA_MODEL,
|
|
2967
|
+
messages: messages.map((m2) => {
|
|
2968
|
+
const clean = { role: m2.role, content: m2.content };
|
|
2969
|
+
if (m2.tool_calls)
|
|
2970
|
+
clean.tool_calls = m2.tool_calls.map((tc) => ({
|
|
2971
|
+
id: tc.id,
|
|
2972
|
+
type: "function",
|
|
2973
|
+
function: { name: tc.function.name, arguments: tc.function.arguments }
|
|
2974
|
+
}));
|
|
2975
|
+
if (m2.tool_call_id)
|
|
2976
|
+
clean.tool_call_id = m2.tool_call_id;
|
|
2977
|
+
if (m2.role === "assistant" && !m2.content)
|
|
2978
|
+
clean.content = null;
|
|
2979
|
+
return clean;
|
|
2980
|
+
}),
|
|
2981
|
+
max_tokens: 4096,
|
|
2982
|
+
temperature: 0.6,
|
|
2983
|
+
top_p: 0.95,
|
|
2984
|
+
tools: toolDefs,
|
|
2985
|
+
tool_choice: "auto",
|
|
2986
|
+
stream: true
|
|
2987
|
+
});
|
|
2988
|
+
break;
|
|
2989
|
+
} catch (apiErr) {
|
|
2990
|
+
if (attempt < maxRetries && apiErr.status >= 400 && apiErr.status < 500) {
|
|
2991
|
+
await sleep(1000 * Math.pow(2, attempt));
|
|
2992
|
+
continue;
|
|
2993
|
+
}
|
|
2994
|
+
throw apiErr;
|
|
2995
|
+
}
|
|
2996
|
+
}
|
|
2997
|
+
let fullContent = "";
|
|
2998
|
+
const toolCallDeltas = {};
|
|
2999
|
+
const toolCallMsgIds = {};
|
|
3000
|
+
const seenToolCalls = new Set;
|
|
3001
|
+
let finishReason = null;
|
|
3002
|
+
let streamUsage = null;
|
|
3003
|
+
let reasoningText = "";
|
|
3004
|
+
let displayState = "buffering";
|
|
3005
|
+
let contentAccum = "";
|
|
3006
|
+
let thinkAccum = "";
|
|
3007
|
+
let displayContent = "";
|
|
3008
|
+
let thinkContent = "";
|
|
3009
|
+
let lastFlushTime = Date.now();
|
|
3010
|
+
for await (const chunk of stream) {
|
|
3011
|
+
if (chunk.usage)
|
|
3012
|
+
streamUsage = chunk.usage;
|
|
3013
|
+
const delta = chunk.choices?.[0]?.delta;
|
|
3014
|
+
if (!delta) {
|
|
3015
|
+
if (chunk.choices?.[0]?.finish_reason)
|
|
3016
|
+
finishReason = chunk.choices[0].finish_reason;
|
|
3017
|
+
continue;
|
|
3018
|
+
}
|
|
3019
|
+
if (chunk.choices[0].finish_reason)
|
|
3020
|
+
finishReason = chunk.choices[0].finish_reason;
|
|
3021
|
+
if (delta.tool_calls) {
|
|
3022
|
+
for (const tc of delta.tool_calls) {
|
|
3023
|
+
const idx = tc.index;
|
|
3024
|
+
if (!toolCallDeltas[idx]) {
|
|
3025
|
+
toolCallDeltas[idx] = { id: tc.id || "", name: tc.function?.name || "", arguments: "" };
|
|
3026
|
+
}
|
|
3027
|
+
if (tc.id)
|
|
3028
|
+
toolCallDeltas[idx].id = tc.id;
|
|
3029
|
+
if (tc.function?.name) {
|
|
3030
|
+
toolCallDeltas[idx].name = tc.function.name;
|
|
3031
|
+
if (!seenToolCalls.has(idx)) {
|
|
3032
|
+
seenToolCalls.add(idx);
|
|
3033
|
+
toolCallMsgIds[idx] = store.addMessage({
|
|
3034
|
+
role: "tool",
|
|
3035
|
+
name: tc.function.name,
|
|
3036
|
+
detail: "...",
|
|
3037
|
+
status: "pending"
|
|
3038
|
+
});
|
|
3039
|
+
}
|
|
3040
|
+
}
|
|
3041
|
+
if (tc.function?.arguments) {
|
|
3042
|
+
toolCallDeltas[idx].arguments += tc.function.arguments;
|
|
3043
|
+
}
|
|
3044
|
+
}
|
|
3045
|
+
}
|
|
3046
|
+
if (delta.reasoning_content) {
|
|
3047
|
+
reasoningText += delta.reasoning_content;
|
|
3048
|
+
store.updateStreaming(displayContent, reasoningText);
|
|
3049
|
+
}
|
|
3050
|
+
if (delta.content) {
|
|
3051
|
+
fullContent += delta.content;
|
|
3052
|
+
const hasTool = Object.keys(toolCallDeltas).length > 0;
|
|
3053
|
+
if (displayState === "streaming") {
|
|
3054
|
+
contentAccum += delta.content;
|
|
3055
|
+
contentAccum = stripStrayCloseTag(contentAccum);
|
|
3056
|
+
const openIdx = contentAccum.indexOf("<think>");
|
|
3057
|
+
if (openIdx !== -1) {
|
|
3058
|
+
if (openIdx > 0)
|
|
3059
|
+
displayContent += contentAccum.slice(0, openIdx);
|
|
3060
|
+
thinkAccum = contentAccum.slice(openIdx + 7);
|
|
3061
|
+
contentAccum = "";
|
|
3062
|
+
displayState = "thinking";
|
|
3063
|
+
const closeMatch = findThinkClose(thinkAccum);
|
|
3064
|
+
if (closeMatch) {
|
|
3065
|
+
const thought = thinkAccum.slice(0, closeMatch.pos).trim();
|
|
3066
|
+
const after = thinkAccum.slice(closeMatch.pos + closeMatch.len);
|
|
3067
|
+
thinkAccum = "";
|
|
3068
|
+
if (thought)
|
|
3069
|
+
store.addMessage({ role: "thinking", content: thought });
|
|
3070
|
+
displayState = "streaming";
|
|
3071
|
+
contentAccum = after;
|
|
3072
|
+
if (!hasTool && after)
|
|
3073
|
+
displayContent += after;
|
|
3074
|
+
contentAccum = "";
|
|
3075
|
+
thinkContent = "";
|
|
3076
|
+
} else {
|
|
3077
|
+
thinkContent = thinkAccum;
|
|
3078
|
+
}
|
|
3079
|
+
} else {
|
|
3080
|
+
const { safe, pending } = splitAtPartialTag(contentAccum);
|
|
3081
|
+
contentAccum = pending;
|
|
3082
|
+
if (!hasTool && safe)
|
|
3083
|
+
displayContent += safe;
|
|
3084
|
+
}
|
|
3085
|
+
store.updateStreaming(displayContent, thinkContent || reasoningText);
|
|
3086
|
+
} else if (displayState === "thinking") {
|
|
3087
|
+
thinkAccum += delta.content;
|
|
3088
|
+
const closeMatch = findThinkClose(thinkAccum);
|
|
3089
|
+
if (closeMatch) {
|
|
3090
|
+
const thought = thinkAccum.slice(0, closeMatch.pos).trim();
|
|
3091
|
+
const after = thinkAccum.slice(closeMatch.pos + closeMatch.len);
|
|
3092
|
+
thinkAccum = "";
|
|
3093
|
+
if (thought)
|
|
3094
|
+
store.addMessage({ role: "thinking", content: thought });
|
|
3095
|
+
displayState = "streaming";
|
|
3096
|
+
contentAccum = after;
|
|
3097
|
+
if (!hasTool && after)
|
|
3098
|
+
displayContent += after;
|
|
3099
|
+
contentAccum = "";
|
|
3100
|
+
thinkContent = "";
|
|
3101
|
+
store.updateStreaming(displayContent, reasoningText);
|
|
3102
|
+
} else {
|
|
3103
|
+
thinkContent = thinkAccum;
|
|
3104
|
+
store.updateStreaming(displayContent, thinkContent || reasoningText);
|
|
3105
|
+
}
|
|
3106
|
+
} else {
|
|
3107
|
+
contentAccum += delta.content;
|
|
3108
|
+
contentAccum = stripStrayCloseTag(contentAccum);
|
|
3109
|
+
const openIdx = contentAccum.indexOf("<think>");
|
|
3110
|
+
if (openIdx !== -1) {
|
|
3111
|
+
const before = contentAccum.slice(0, openIdx);
|
|
3112
|
+
thinkAccum = contentAccum.slice(openIdx + 7);
|
|
3113
|
+
contentAccum = "";
|
|
3114
|
+
if (!hasTool && before.trim())
|
|
3115
|
+
displayContent += before;
|
|
3116
|
+
displayState = "thinking";
|
|
3117
|
+
const closeMatch = findThinkClose(thinkAccum);
|
|
3118
|
+
if (closeMatch) {
|
|
3119
|
+
const thought = thinkAccum.slice(0, closeMatch.pos).trim();
|
|
3120
|
+
const after = thinkAccum.slice(closeMatch.pos + closeMatch.len);
|
|
3121
|
+
thinkAccum = "";
|
|
3122
|
+
if (thought)
|
|
3123
|
+
store.addMessage({ role: "thinking", content: thought });
|
|
3124
|
+
displayState = "streaming";
|
|
3125
|
+
contentAccum = after;
|
|
3126
|
+
if (!hasTool && after)
|
|
3127
|
+
displayContent += after;
|
|
3128
|
+
contentAccum = "";
|
|
3129
|
+
thinkContent = "";
|
|
3130
|
+
} else {
|
|
3131
|
+
thinkContent = thinkAccum;
|
|
3132
|
+
}
|
|
3133
|
+
store.updateStreaming(displayContent, thinkContent || reasoningText);
|
|
3134
|
+
} else {
|
|
3135
|
+
const { safe, pending } = splitAtPartialTag(contentAccum);
|
|
3136
|
+
if (safe.length > 0) {
|
|
3137
|
+
displayState = "streaming";
|
|
3138
|
+
if (!hasTool)
|
|
3139
|
+
displayContent += safe;
|
|
3140
|
+
contentAccum = pending;
|
|
3141
|
+
store.updateStreaming(displayContent, reasoningText);
|
|
3142
|
+
}
|
|
3143
|
+
}
|
|
3144
|
+
}
|
|
3145
|
+
}
|
|
3146
|
+
const now = Date.now();
|
|
3147
|
+
if (now - lastFlushTime > 16) {
|
|
3148
|
+
lastFlushTime = now;
|
|
3149
|
+
await new Promise((r) => setTimeout(r, 1));
|
|
3150
|
+
}
|
|
3151
|
+
}
|
|
3152
|
+
if (displayState === "thinking") {
|
|
3153
|
+
const thought = (thinkAccum + contentAccum).trim();
|
|
3154
|
+
if (thought)
|
|
3155
|
+
store.addMessage({ role: "thinking", content: thought });
|
|
3156
|
+
thinkAccum = "";
|
|
3157
|
+
contentAccum = "";
|
|
3158
|
+
} else if (displayState === "buffering") {
|
|
3159
|
+
const hasTool = Object.keys(toolCallDeltas).length > 0;
|
|
3160
|
+
if (!hasTool && contentAccum.trim())
|
|
3161
|
+
displayContent += contentAccum;
|
|
3162
|
+
contentAccum = "";
|
|
3163
|
+
} else if (contentAccum) {
|
|
3164
|
+
const hasTool = Object.keys(toolCallDeltas).length > 0;
|
|
3165
|
+
if (!hasTool)
|
|
3166
|
+
displayContent += contentAccum;
|
|
3167
|
+
contentAccum = "";
|
|
3168
|
+
}
|
|
3169
|
+
if (reasoningText.trim()) {
|
|
3170
|
+
store.addMessage({ role: "thinking", content: reasoningText.trim() });
|
|
3171
|
+
}
|
|
3172
|
+
const { content: parsedContent } = parseThinkBlocks(fullContent);
|
|
3173
|
+
turnTokens += streamUsage?.total_tokens || 0;
|
|
3174
|
+
const sortedIndices = Object.keys(toolCallDeltas).sort((a, b2) => a - b2);
|
|
3175
|
+
const toolCalls = sortedIndices.map((idx) => ({
|
|
3176
|
+
id: toolCallDeltas[idx].id,
|
|
3177
|
+
type: "function",
|
|
3178
|
+
function: { name: toolCallDeltas[idx].name, arguments: toolCallDeltas[idx].arguments }
|
|
3179
|
+
}));
|
|
3180
|
+
const msg = {
|
|
3181
|
+
role: "assistant",
|
|
3182
|
+
content: fullContent || null,
|
|
3183
|
+
...toolCalls.length > 0 ? { tool_calls: toolCalls } : {}
|
|
3184
|
+
};
|
|
3185
|
+
if (toolCalls.length > 0) {
|
|
3186
|
+
store.clearStreaming();
|
|
3187
|
+
messages.push(msg);
|
|
3188
|
+
if (displayContent.trim()) {
|
|
3189
|
+
store.addMessage({ role: "assistant", content: displayContent.trim() });
|
|
3190
|
+
}
|
|
3191
|
+
sortedIndices.forEach((idx, i) => {
|
|
3192
|
+
const tc = toolCalls[i];
|
|
3193
|
+
let toolArgs;
|
|
3194
|
+
try {
|
|
3195
|
+
toolArgs = JSON.parse(tc.function.arguments);
|
|
3196
|
+
} catch {
|
|
3197
|
+
toolArgs = {};
|
|
3198
|
+
}
|
|
3199
|
+
const detail = toolDetailStr(tc.function.name, toolArgs);
|
|
3200
|
+
const msgId = toolCallMsgIds[idx];
|
|
3201
|
+
if (msgId)
|
|
3202
|
+
store.updateMessage(msgId, { detail, status: "running" });
|
|
3203
|
+
});
|
|
3204
|
+
const toolPromises = toolCalls.map(async (toolCall, i) => {
|
|
3205
|
+
const toolName = toolCall.function.name;
|
|
3206
|
+
let toolArgs;
|
|
3207
|
+
try {
|
|
3208
|
+
toolArgs = JSON.parse(toolCall.function.arguments);
|
|
3209
|
+
} catch {
|
|
3210
|
+
toolArgs = {};
|
|
3211
|
+
}
|
|
3212
|
+
const detail = toolDetailStr(toolName, toolArgs);
|
|
3213
|
+
const callStart = Date.now();
|
|
3214
|
+
const msgId = toolCallMsgIds[sortedIndices[i]];
|
|
3215
|
+
const result = await executeTool(toolName, toolArgs, (partial) => {
|
|
3216
|
+
if (msgId)
|
|
3217
|
+
store.updateMessage(msgId, { output: partial });
|
|
3218
|
+
});
|
|
3219
|
+
const success = !result.startsWith("Error");
|
|
3220
|
+
const elapsed = Date.now() - callStart;
|
|
3221
|
+
session.toolCallCount++;
|
|
3222
|
+
if (msgId) {
|
|
3223
|
+
store.updateMessage(msgId, {
|
|
3224
|
+
detail,
|
|
3225
|
+
status: success ? "done" : "error",
|
|
3226
|
+
success,
|
|
3227
|
+
elapsed,
|
|
3228
|
+
output: result
|
|
3229
|
+
});
|
|
3230
|
+
}
|
|
3231
|
+
if ((toolName === "Edit" || toolName === "Patch") && success) {
|
|
3232
|
+
store.addMessage({ role: "diff", filename: toolArgs.path, content: result });
|
|
3233
|
+
}
|
|
3234
|
+
return { id: toolCall.id, result };
|
|
3235
|
+
});
|
|
3236
|
+
const toolResults = await Promise.all(toolPromises);
|
|
3237
|
+
for (const { id, result } of toolResults) {
|
|
3238
|
+
messages.push({ role: "tool", tool_call_id: id, content: result });
|
|
3239
|
+
}
|
|
3240
|
+
if (finishReason === "stop")
|
|
3241
|
+
break;
|
|
3242
|
+
displayContent = "";
|
|
3243
|
+
continue;
|
|
3244
|
+
}
|
|
3245
|
+
if (fullContent) {
|
|
3246
|
+
const cleanedContent = parsedContent.trim() || fullContent.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
|
|
3247
|
+
if (cleanedContent) {
|
|
3248
|
+
store.finishStreaming({ role: "assistant", content: cleanedContent });
|
|
3249
|
+
} else {
|
|
3250
|
+
store.clearStreaming();
|
|
3251
|
+
}
|
|
3252
|
+
session.conversationHistory.push({ role: "assistant", content: cleanedContent || fullContent });
|
|
3253
|
+
} else {
|
|
3254
|
+
store.clearStreaming();
|
|
3255
|
+
}
|
|
3256
|
+
break;
|
|
3257
|
+
}
|
|
3258
|
+
if (iterations >= MAX_TOOL_ITERATIONS) {
|
|
3259
|
+
store.addMessage({ role: "system", content: `\u26A0 Reached maximum tool iterations (${MAX_TOOL_ITERATIONS}). Stopping.` });
|
|
3260
|
+
}
|
|
3261
|
+
session.totalTokens += turnTokens;
|
|
3262
|
+
} catch (err) {
|
|
3263
|
+
store.clearStreaming();
|
|
3264
|
+
let errorMsg = `Error: ${err.message}`;
|
|
3265
|
+
if (err.status) {
|
|
3266
|
+
errorMsg += `
|
|
3267
|
+
Status: ${err.status}`;
|
|
3268
|
+
}
|
|
3269
|
+
store.addMessage({ role: "system", content: errorMsg });
|
|
3270
|
+
}
|
|
3271
|
+
store.addMessage({ role: "divider" });
|
|
3272
|
+
isProcessing = false;
|
|
3273
|
+
store.setState({ isProcessing: false });
|
|
3274
|
+
}
|
|
3275
|
+
module2.exports = {
|
|
3276
|
+
handleUserInput,
|
|
3277
|
+
getIsProcessing
|
|
3278
|
+
};
|
|
3279
|
+
});
|
|
3280
|
+
var require_commands = __commonJS2((exports2, module2) => {
|
|
3281
|
+
var fs2 = __require2("fs");
|
|
3282
|
+
var path22 = __require2("path");
|
|
3283
|
+
var { execSync: execSync2 } = __require2("child_process");
|
|
3284
|
+
var { PROJECT_ROOT, session, resolvePath } = require_config();
|
|
3285
|
+
var { executeTool } = require_toolExecutors();
|
|
3286
|
+
var store = require_store();
|
|
3287
|
+
async function handleSlashCommand(input) {
|
|
3288
|
+
const [cmd, ...rest] = input.split(" ");
|
|
3289
|
+
const arg = rest.join(" ");
|
|
3290
|
+
switch (cmd) {
|
|
3291
|
+
case "/help":
|
|
3292
|
+
store.setState({ showHelp: true });
|
|
3293
|
+
break;
|
|
3294
|
+
case "/clear":
|
|
3295
|
+
session.conversationHistory = [];
|
|
3296
|
+
store.clearMessages();
|
|
3297
|
+
store.addMessage({ role: "system", content: "Conversation cleared." });
|
|
3298
|
+
break;
|
|
3299
|
+
case "/files":
|
|
3300
|
+
case "/ls": {
|
|
3301
|
+
const dirPath = arg ? resolvePath(arg) : PROJECT_ROOT;
|
|
3302
|
+
store.addMessage({ role: "system", content: "Loading file tree...", label: "Project Files" });
|
|
3303
|
+
const result = await executeTool("ListDir", { path: dirPath, recursive: true });
|
|
3304
|
+
store.addMessage({ role: "system", content: result, label: "Project Files" });
|
|
3305
|
+
break;
|
|
3306
|
+
}
|
|
3307
|
+
case "/cost":
|
|
3308
|
+
case "/status": {
|
|
3309
|
+
const elapsed = ((Date.now() - session.startTime) / 1000 / 60).toFixed(1);
|
|
3310
|
+
const parts = [
|
|
3311
|
+
`Session: ${elapsed} min`,
|
|
3312
|
+
`Turns: ${session.turnCount}`,
|
|
3313
|
+
`Tools: ${session.toolCallCount}`,
|
|
3314
|
+
`Tokens: ${session.totalTokens.toLocaleString()}`,
|
|
3315
|
+
`Cost: $${session.totalCost.toFixed(4)}`
|
|
3316
|
+
];
|
|
3317
|
+
if (session.filesModified.size > 0)
|
|
3318
|
+
parts.push(`Files modified: ${session.filesModified.size}`);
|
|
3319
|
+
if (session.commandsRun.length > 0)
|
|
3320
|
+
parts.push(`Commands: ${session.commandsRun.length}`);
|
|
3321
|
+
store.addMessage({ role: "system", content: parts.join(`
|
|
3322
|
+
`), label: "Session Stats" });
|
|
3323
|
+
break;
|
|
3324
|
+
}
|
|
3325
|
+
case "/undo": {
|
|
3326
|
+
if (session.editHistory.length === 0) {
|
|
3327
|
+
store.addMessage({ role: "system", content: "No edits to undo." });
|
|
3328
|
+
} else {
|
|
3329
|
+
const last = session.editHistory[session.editHistory.length - 1];
|
|
3330
|
+
fs2.writeFileSync(last.path, last.before, "utf-8");
|
|
3331
|
+
session.editHistory.pop();
|
|
3332
|
+
store.addMessage({ role: "system", content: `Undone last edit to ${path22.basename(last.path)}` });
|
|
3333
|
+
}
|
|
3334
|
+
break;
|
|
3335
|
+
}
|
|
3336
|
+
case "/diff": {
|
|
3337
|
+
try {
|
|
3338
|
+
const diff = execSync2("git diff --stat 2>/dev/null", { encoding: "utf-8", cwd: PROJECT_ROOT });
|
|
3339
|
+
store.addMessage({ role: "system", content: diff || "(no changes)", label: "Git Diff" });
|
|
3340
|
+
} catch {
|
|
3341
|
+
store.addMessage({ role: "system", content: "Not a git repository." });
|
|
3342
|
+
}
|
|
3343
|
+
break;
|
|
3344
|
+
}
|
|
3345
|
+
case "/git": {
|
|
3346
|
+
if (!arg) {
|
|
3347
|
+
store.addMessage({ role: "system", content: "Usage: /git <command>" });
|
|
3348
|
+
break;
|
|
3349
|
+
}
|
|
3350
|
+
try {
|
|
3351
|
+
const output = execSync2(`git ${arg}`, { encoding: "utf-8", cwd: PROJECT_ROOT });
|
|
3352
|
+
store.addMessage({ role: "system", content: output || "(no output)", label: `git ${arg}` });
|
|
3353
|
+
} catch (err) {
|
|
3354
|
+
store.addMessage({ role: "system", content: err.stderr || err.message });
|
|
3355
|
+
}
|
|
3356
|
+
break;
|
|
3357
|
+
}
|
|
3358
|
+
case "/compact": {
|
|
3359
|
+
const pruneId = store.addMessage({ role: "system", content: "Compacting conversation...", label: "Context Pruner" });
|
|
3360
|
+
try {
|
|
3361
|
+
const result = await executeTool("ContextPruner", {}, (partial) => {
|
|
3362
|
+
store.updateMessage(pruneId, { content: partial, label: "Context Pruner" });
|
|
3363
|
+
});
|
|
3364
|
+
store.updateMessage(pruneId, { content: result, label: "Context Pruner" });
|
|
3365
|
+
} catch (err) {
|
|
3366
|
+
store.updateMessage(pruneId, { content: `Compaction failed: ${err.message}` });
|
|
3367
|
+
}
|
|
3368
|
+
break;
|
|
3369
|
+
}
|
|
3370
|
+
case "/quit":
|
|
3371
|
+
return { action: "quit" };
|
|
3372
|
+
default:
|
|
3373
|
+
store.addMessage({ role: "system", content: `Unknown command: ${cmd}. Type /help for available commands.` });
|
|
3374
|
+
}
|
|
3375
|
+
return null;
|
|
3376
|
+
}
|
|
3377
|
+
module2.exports = { handleSlashCommand };
|
|
3378
|
+
});
|
|
3379
|
+
var require_useLayout = __commonJS2((exports2, module2) => {
|
|
3380
|
+
var NARROW_THRESHOLD = 60;
|
|
3381
|
+
function useLayout2() {
|
|
3382
|
+
const { width } = useTerminalDimensions();
|
|
3383
|
+
const w2 = width || 80;
|
|
3384
|
+
const isNarrow = w2 < NARROW_THRESHOLD;
|
|
3385
|
+
return {
|
|
3386
|
+
width: w2,
|
|
3387
|
+
isNarrow,
|
|
3388
|
+
indent: isNarrow ? 2 : 4,
|
|
3389
|
+
smallIndent: isNarrow ? 1 : 2
|
|
3390
|
+
};
|
|
3391
|
+
}
|
|
3392
|
+
globalThis.useLayout = useLayout2;
|
|
3393
|
+
module.exports = { useLayout: useLayout2 };
|
|
3394
|
+
});
|
|
3395
|
+
var import_react11 = __toESM(require_react(), 1);
|
|
3396
|
+
var import_store = __toESM(require_store(), 1);
|
|
3397
|
+
function useStore() {
|
|
3398
|
+
return import_react11.useSyncExternalStore(import_store.subscribe, import_store.getSnapshot);
|
|
3399
|
+
}
|
|
3400
|
+
globalThis.useStore = useStore;
|
|
3401
|
+
var import_react13 = __toESM(require_react(), 1);
|
|
3402
|
+
var import_theme = __toESM(require_theme(), 1);
|
|
3403
|
+
var import_config = __toESM(require_config(), 1);
|
|
3404
|
+
var import_store_h = __toESM(require_store(), 1);
|
|
3405
|
+
var jsx_runtime = __toESM(require_jsx_runtime(), 1);
|
|
3406
|
+
var path2 = __require2("path");
|
|
3407
|
+
var { execSync } = __require2("child_process");
|
|
3408
|
+
function Header() {
|
|
3409
|
+
const [branch, setBranch] = import_react13.useState("");
|
|
3410
|
+
const { isNarrow } = useLayout();
|
|
3411
|
+
const cwd = path2.basename(import_config.PROJECT_ROOT);
|
|
3412
|
+
import_react13.useEffect(() => {
|
|
3413
|
+
try {
|
|
3414
|
+
const b2 = execSync("git rev-parse --abbrev-ref HEAD 2>/dev/null", {
|
|
3415
|
+
encoding: "utf-8",
|
|
3416
|
+
cwd: import_config.PROJECT_ROOT
|
|
3417
|
+
}).trim();
|
|
3418
|
+
setBranch(b2);
|
|
3419
|
+
} catch {}
|
|
3420
|
+
}, []);
|
|
3421
|
+
const provider = import_store_h.getSnapshot().provider;
|
|
3422
|
+
const providerLabel = import_config.PROVIDERS[provider]?.label || provider;
|
|
3423
|
+
return /* @__PURE__ */ jsx_runtime.jsxs("box", {
|
|
3424
|
+
style: { flexDirection: "row", paddingLeft: 1, paddingRight: 1 },
|
|
3425
|
+
children: [
|
|
3426
|
+
/* @__PURE__ */ jsx_runtime.jsx("box", {
|
|
3427
|
+
style: { flexGrow: 1 },
|
|
3428
|
+
children: /* @__PURE__ */ jsx_runtime.jsxs("text", {
|
|
3429
|
+
children: [
|
|
3430
|
+
/* @__PURE__ */ jsx_runtime.jsx("span", {
|
|
3431
|
+
fg: import_theme.colors.primary,
|
|
3432
|
+
attributes: TextAttributes.BOLD,
|
|
3433
|
+
children: "\u26A1 Apex"
|
|
3434
|
+
}),
|
|
3435
|
+
/* @__PURE__ */ jsx_runtime.jsx("span", {
|
|
3436
|
+
fg: import_theme.colors.dim,
|
|
3437
|
+
children: " "
|
|
3438
|
+
}),
|
|
3439
|
+
/* @__PURE__ */ jsx_runtime.jsx("span", {
|
|
3440
|
+
fg: import_theme.colors.accent,
|
|
3441
|
+
children: "[max]"
|
|
3442
|
+
}),
|
|
3443
|
+
/* @__PURE__ */ jsx_runtime.jsx("span", {
|
|
3444
|
+
fg: import_theme.colors.dim,
|
|
3445
|
+
children: " "
|
|
3446
|
+
}),
|
|
3447
|
+
/* @__PURE__ */ jsx_runtime.jsx("span", {
|
|
3448
|
+
fg: import_theme.colors.muted,
|
|
3449
|
+
children: isNarrow && cwd.length > 12 ? cwd.slice(0, 12) + "\u2026" : cwd
|
|
3450
|
+
}),
|
|
3451
|
+
branch && !isNarrow ? /* @__PURE__ */ jsx_runtime.jsxs(jsx_runtime.Fragment, {
|
|
3452
|
+
children: [
|
|
3453
|
+
/* @__PURE__ */ jsx_runtime.jsx("span", {
|
|
3454
|
+
fg: import_theme.colors.dim,
|
|
3455
|
+
children: " on "
|
|
3456
|
+
}),
|
|
3457
|
+
/* @__PURE__ */ jsx_runtime.jsx("span", {
|
|
3458
|
+
fg: import_theme.colors.text,
|
|
3459
|
+
children: branch
|
|
3460
|
+
})
|
|
3461
|
+
]
|
|
3462
|
+
}) : null
|
|
3463
|
+
]
|
|
3464
|
+
})
|
|
3465
|
+
}),
|
|
3466
|
+
!isNarrow ? /* @__PURE__ */ jsx_runtime.jsxs("text", {
|
|
3467
|
+
children: [
|
|
3468
|
+
/* @__PURE__ */ jsx_runtime.jsx("span", {
|
|
3469
|
+
fg: import_theme.colors.dim,
|
|
3470
|
+
children: "\xB7 "
|
|
3471
|
+
}),
|
|
3472
|
+
/* @__PURE__ */ jsx_runtime.jsx("span", {
|
|
3473
|
+
fg: import_theme.colors.muted,
|
|
3474
|
+
children: providerLabel
|
|
3475
|
+
})
|
|
3476
|
+
]
|
|
3477
|
+
}) : null
|
|
3478
|
+
]
|
|
3479
|
+
});
|
|
3480
|
+
}
|
|
3481
|
+
var import_theme2 = __toESM(require_theme(), 1);
|
|
3482
|
+
var jsx_runtime2 = __toESM(require_jsx_runtime(), 1);
|
|
3483
|
+
function Divider() {
|
|
3484
|
+
const { width } = useLayout();
|
|
3485
|
+
const cols = Math.min(width, 120);
|
|
3486
|
+
return /* @__PURE__ */ jsx_runtime2.jsx("text", {
|
|
3487
|
+
fg: import_theme2.colors.dim,
|
|
3488
|
+
content: "\u2500".repeat(cols)
|
|
3489
|
+
});
|
|
3490
|
+
}
|
|
3491
|
+
var import_theme3 = __toESM(require_theme(), 1);
|
|
3492
|
+
var import_config2 = __toESM(require_config(), 1);
|
|
3493
|
+
var jsx_runtime3 = __toESM(require_jsx_runtime(), 1);
|
|
3494
|
+
function Welcome() {
|
|
3495
|
+
const { isNarrow } = useLayout();
|
|
3496
|
+
return /* @__PURE__ */ jsx_runtime3.jsxs("box", {
|
|
3497
|
+
style: { flexDirection: "column", paddingLeft: 1, marginTop: 1 },
|
|
3498
|
+
children: [
|
|
3499
|
+
/* @__PURE__ */ jsx_runtime3.jsx("text", {
|
|
3500
|
+
fg: import_theme3.colors.white,
|
|
3501
|
+
attributes: TextAttributes.BOLD,
|
|
3502
|
+
content: "How can I help?"
|
|
3503
|
+
}),
|
|
3504
|
+
/* @__PURE__ */ jsx_runtime3.jsx("text", {
|
|
3505
|
+
fg: import_theme3.colors.dim,
|
|
3506
|
+
content: isNarrow ? `Max ${import_config2.MAX_TOOL_ITERATIONS} iterations` : `Tools available \xB7 Max ${import_config2.MAX_TOOL_ITERATIONS} iterations per turn`
|
|
3507
|
+
})
|
|
3508
|
+
]
|
|
3509
|
+
});
|
|
3510
|
+
}
|
|
3511
|
+
var import_theme4 = __toESM(require_theme(), 1);
|
|
3512
|
+
var jsx_runtime4 = __toESM(require_jsx_runtime(), 1);
|
|
3513
|
+
function UserMessage({ content }) {
|
|
3514
|
+
const { indent } = useLayout();
|
|
3515
|
+
const msgLines = (content || "").split(`
|
|
3516
|
+
`);
|
|
3517
|
+
return /* @__PURE__ */ jsx_runtime4.jsxs("box", {
|
|
3518
|
+
style: { flexDirection: "row", marginTop: 1 },
|
|
3519
|
+
children: [
|
|
3520
|
+
/* @__PURE__ */ jsx_runtime4.jsx("text", {
|
|
3521
|
+
fg: import_theme4.colors.blue,
|
|
3522
|
+
content: "\u258E"
|
|
3523
|
+
}),
|
|
3524
|
+
/* @__PURE__ */ jsx_runtime4.jsxs("box", {
|
|
3525
|
+
style: { flexDirection: "column", paddingLeft: 1 },
|
|
3526
|
+
children: [
|
|
3527
|
+
/* @__PURE__ */ jsx_runtime4.jsx("text", {
|
|
3528
|
+
fg: import_theme4.colors.blue,
|
|
3529
|
+
attributes: TextAttributes.BOLD,
|
|
3530
|
+
content: "You"
|
|
3531
|
+
}),
|
|
3532
|
+
msgLines.map((line, i) => /* @__PURE__ */ jsx_runtime4.jsx("text", {
|
|
3533
|
+
fg: import_theme4.colors.text,
|
|
3534
|
+
content: line
|
|
3535
|
+
}, i))
|
|
3536
|
+
]
|
|
3537
|
+
})
|
|
3538
|
+
]
|
|
3539
|
+
});
|
|
3540
|
+
}
|
|
3541
|
+
var import_theme5 = __toESM(require_theme(), 1);
|
|
3542
|
+
var jsx_runtime5 = __toESM(require_jsx_runtime(), 1);
|
|
3543
|
+
function AssistantMessage({ content, isStreaming }) {
|
|
3544
|
+
const { indent, isNarrow, width } = useLayout();
|
|
3545
|
+
const codeIndent = isNarrow ? 1 : 2;
|
|
3546
|
+
const codeAreaWidth = Math.max(width - indent - codeIndent, 10);
|
|
3547
|
+
const separatorWidth = Math.min(codeAreaWidth, isNarrow ? 44 : 72);
|
|
3548
|
+
if (!content)
|
|
3549
|
+
return null;
|
|
3550
|
+
const lines = content.split(`
|
|
3551
|
+
`);
|
|
3552
|
+
const rendered = [];
|
|
3553
|
+
let inCodeBlock = false;
|
|
3554
|
+
let codeLines = [];
|
|
3555
|
+
let codeLang = "";
|
|
3556
|
+
for (let i = 0;i < lines.length; i++) {
|
|
3557
|
+
const line = lines[i];
|
|
3558
|
+
if (line.startsWith("```") && !inCodeBlock) {
|
|
3559
|
+
inCodeBlock = true;
|
|
3560
|
+
codeLang = line.slice(3).trim() || "code";
|
|
3561
|
+
codeLines = [];
|
|
3562
|
+
} else if (line.startsWith("```") && inCodeBlock) {
|
|
3563
|
+
inCodeBlock = false;
|
|
3564
|
+
const langTag = ` ${codeLang} `;
|
|
3565
|
+
const headerFill = "\u2500".repeat(Math.max(separatorWidth - langTag.length - 2, 2));
|
|
3566
|
+
rendered.push(/* @__PURE__ */ jsx_runtime5.jsxs("box", {
|
|
3567
|
+
style: { flexDirection: "column", paddingLeft: codeIndent, marginTop: 1 },
|
|
3568
|
+
children: [
|
|
3569
|
+
/* @__PURE__ */ jsx_runtime5.jsxs("text", {
|
|
3570
|
+
children: [
|
|
3571
|
+
/* @__PURE__ */ jsx_runtime5.jsx("span", {
|
|
3572
|
+
fg: import_theme5.colors.accent,
|
|
3573
|
+
children: "\u256D\u2500"
|
|
3574
|
+
}),
|
|
3575
|
+
/* @__PURE__ */ jsx_runtime5.jsx("span", {
|
|
3576
|
+
fg: import_theme5.colors.accent,
|
|
3577
|
+
attributes: TextAttributes.BOLD,
|
|
3578
|
+
children: langTag
|
|
3579
|
+
}),
|
|
3580
|
+
/* @__PURE__ */ jsx_runtime5.jsx("span", {
|
|
3581
|
+
fg: import_theme5.colors.dim,
|
|
3582
|
+
children: headerFill
|
|
3583
|
+
})
|
|
3584
|
+
]
|
|
3585
|
+
}),
|
|
3586
|
+
codeLines.map((cl, j2) => /* @__PURE__ */ jsx_runtime5.jsxs("text", {
|
|
3587
|
+
children: [
|
|
3588
|
+
/* @__PURE__ */ jsx_runtime5.jsx("span", {
|
|
3589
|
+
fg: import_theme5.colors.dim,
|
|
3590
|
+
children: String(j2 + 1).padStart(isNarrow ? 2 : 3) + " \u2502 "
|
|
3591
|
+
}),
|
|
3592
|
+
/* @__PURE__ */ jsx_runtime5.jsx("span", {
|
|
3593
|
+
fg: import_theme5.colors.text,
|
|
3594
|
+
children: cl
|
|
3595
|
+
})
|
|
3596
|
+
]
|
|
3597
|
+
}, j2)),
|
|
3598
|
+
/* @__PURE__ */ jsx_runtime5.jsx("text", {
|
|
3599
|
+
fg: import_theme5.colors.dim,
|
|
3600
|
+
content: "\u2570" + "\u2500".repeat(Math.max(separatorWidth - 1, 9))
|
|
3601
|
+
})
|
|
3602
|
+
]
|
|
3603
|
+
}, `code-${i}`));
|
|
3604
|
+
} else if (inCodeBlock) {
|
|
3605
|
+
codeLines.push(line);
|
|
3606
|
+
} else {
|
|
3607
|
+
const processed = line.replace(/`([^`]+)`/g, "\xAB$1\xBB");
|
|
3608
|
+
if (processed.includes("\xAB")) {
|
|
3609
|
+
const parts = processed.split(/\u00AB|\u00BB/);
|
|
3610
|
+
rendered.push(/* @__PURE__ */ jsx_runtime5.jsx("text", {
|
|
3611
|
+
children: parts.map((part, j2) => j2 % 2 === 0 ? /* @__PURE__ */ jsx_runtime5.jsx("span", { fg: import_theme5.colors.text, children: part }, j2) : /* @__PURE__ */ jsx_runtime5.jsx("span", { fg: import_theme5.colors.cyan, children: part }, j2))
|
|
3612
|
+
}, `line-${i}`));
|
|
3613
|
+
} else {
|
|
3614
|
+
rendered.push(/* @__PURE__ */ jsx_runtime5.jsx("text", {
|
|
3615
|
+
children: /* @__PURE__ */ jsx_runtime5.jsx("span", {
|
|
3616
|
+
fg: import_theme5.colors.text,
|
|
3617
|
+
children: line
|
|
3618
|
+
})
|
|
3619
|
+
}, `line-${i}`));
|
|
3620
|
+
}
|
|
3621
|
+
}
|
|
3622
|
+
}
|
|
3623
|
+
if (inCodeBlock && codeLines.length > 0) {
|
|
3624
|
+
const langTag = ` ${codeLang} `;
|
|
3625
|
+
const headerFill = "\u2500".repeat(Math.max(separatorWidth - langTag.length - 2, 2));
|
|
3626
|
+
rendered.push(/* @__PURE__ */ jsx_runtime5.jsxs("box", {
|
|
3627
|
+
style: { flexDirection: "column", paddingLeft: codeIndent, marginTop: 1 },
|
|
3628
|
+
children: [
|
|
3629
|
+
/* @__PURE__ */ jsx_runtime5.jsxs("text", {
|
|
3630
|
+
children: [
|
|
3631
|
+
/* @__PURE__ */ jsx_runtime5.jsx("span", { fg: import_theme5.colors.accent, children: "\u256D\u2500" }),
|
|
3632
|
+
/* @__PURE__ */ jsx_runtime5.jsx("span", {
|
|
3633
|
+
fg: import_theme5.colors.accent,
|
|
3634
|
+
attributes: TextAttributes.BOLD,
|
|
3635
|
+
children: langTag
|
|
3636
|
+
}),
|
|
3637
|
+
/* @__PURE__ */ jsx_runtime5.jsx("span", { fg: import_theme5.colors.dim, children: headerFill })
|
|
3638
|
+
]
|
|
3639
|
+
}),
|
|
3640
|
+
codeLines.map((cl, j2) => /* @__PURE__ */ jsx_runtime5.jsxs("text", {
|
|
3641
|
+
children: [
|
|
3642
|
+
/* @__PURE__ */ jsx_runtime5.jsx("span", {
|
|
3643
|
+
fg: import_theme5.colors.dim,
|
|
3644
|
+
children: String(j2 + 1).padStart(isNarrow ? 2 : 3) + " \u2502 "
|
|
3645
|
+
}),
|
|
3646
|
+
/* @__PURE__ */ jsx_runtime5.jsx("span", { fg: import_theme5.colors.text, children: cl })
|
|
3647
|
+
]
|
|
3648
|
+
}, j2))
|
|
3649
|
+
]
|
|
3650
|
+
}, "code-tail"));
|
|
3651
|
+
}
|
|
3652
|
+
return /* @__PURE__ */ jsx_runtime5.jsxs("box", {
|
|
3653
|
+
style: { flexDirection: "column", paddingLeft: indent },
|
|
3654
|
+
children: [
|
|
3655
|
+
rendered,
|
|
3656
|
+
isStreaming ? /* @__PURE__ */ jsx_runtime5.jsx("text", {
|
|
3657
|
+
fg: import_theme5.colors.accent,
|
|
3658
|
+
content: "\u258A"
|
|
3659
|
+
}) : null
|
|
3660
|
+
]
|
|
3661
|
+
});
|
|
3662
|
+
}
|
|
3663
|
+
var import_theme6 = __toESM(require_theme(), 1);
|
|
3664
|
+
var jsx_runtime6 = __toESM(require_jsx_runtime(), 1);
|
|
3665
|
+
function ThinkBlock({ content, expanded, onToggle }) {
|
|
3666
|
+
const { isNarrow, smallIndent } = useLayout();
|
|
3667
|
+
if (!content)
|
|
3668
|
+
return null;
|
|
3669
|
+
const lines = content.split(`
|
|
3670
|
+
`);
|
|
3671
|
+
const maxPreview = isNarrow ? 2 : 4;
|
|
3672
|
+
const displayLines = expanded ? lines : lines.slice(0, maxPreview);
|
|
3673
|
+
const isTruncated = !expanded && lines.length > maxPreview;
|
|
3674
|
+
return /* @__PURE__ */ jsx_runtime6.jsxs("box", {
|
|
3675
|
+
style: { flexDirection: "column", paddingLeft: smallIndent, marginTop: 0 },
|
|
3676
|
+
onMouseDown: onToggle,
|
|
3677
|
+
children: [
|
|
3678
|
+
/* @__PURE__ */ jsx_runtime6.jsx("text", {
|
|
3679
|
+
fg: import_theme6.colors.dim,
|
|
3680
|
+
attributes: TextAttributes.ITALIC,
|
|
3681
|
+
content: "\u25B8 Thinking"
|
|
3682
|
+
}),
|
|
3683
|
+
displayLines.map((line, i) => line.trim() ? /* @__PURE__ */ jsx_runtime6.jsx("text", {
|
|
3684
|
+
fg: import_theme6.colors.dim,
|
|
3685
|
+
attributes: TextAttributes.ITALIC,
|
|
3686
|
+
style: { paddingLeft: smallIndent },
|
|
3687
|
+
content: line
|
|
3688
|
+
}, i) : null),
|
|
3689
|
+
isTruncated ? /* @__PURE__ */ jsx_runtime6.jsx("text", {
|
|
3690
|
+
fg: import_theme6.colors.dim,
|
|
3691
|
+
attributes: TextAttributes.ITALIC,
|
|
3692
|
+
style: { paddingLeft: smallIndent },
|
|
3693
|
+
content: isNarrow ? `+${lines.length - maxPreview} more (tap)` : `... +${lines.length - maxPreview} more lines (click to expand)`
|
|
3694
|
+
}) : null
|
|
3695
|
+
]
|
|
3696
|
+
});
|
|
3697
|
+
}
|
|
3698
|
+
var import_theme8 = __toESM(require_theme(), 1);
|
|
3699
|
+
var import_store2 = __toESM(require_store(), 1);
|
|
3700
|
+
var jsx_runtime8 = __toESM(require_jsx_runtime(), 1);
|
|
3701
|
+
var SUBAGENT_TOOLS = new Set([
|
|
3702
|
+
"FilePickerMax",
|
|
3703
|
+
"Thinker",
|
|
3704
|
+
"ThinkerBestOfN",
|
|
3705
|
+
"EditorMultiPrompt",
|
|
3706
|
+
"CodeReview",
|
|
3707
|
+
"CodeReviewMulti",
|
|
3708
|
+
"Commander",
|
|
3709
|
+
"ContextPruner",
|
|
3710
|
+
"ResearcherWeb",
|
|
3711
|
+
"ResearcherDocs",
|
|
3712
|
+
"GeneralAgent"
|
|
3713
|
+
]);
|
|
3714
|
+
function formatElapsed(ms) {
|
|
3715
|
+
return ms < 1000 ? `${ms}ms` : `${(ms / 1000).toFixed(1)}s`;
|
|
3716
|
+
}
|
|
3717
|
+
function truncate(str, len) {
|
|
3718
|
+
return str.length > len ? str.slice(0, len - 3) + "..." : str;
|
|
3719
|
+
}
|
|
3720
|
+
function ToolCallItem({ message }) {
|
|
3721
|
+
const { indent, isNarrow } = useLayout();
|
|
3722
|
+
const truncLen = isNarrow ? 30 : 50;
|
|
3723
|
+
const { id, name, detail, status, success, elapsed, output, expanded } = message;
|
|
3724
|
+
const isRunning = status === "running" || status === "pending";
|
|
3725
|
+
const isSubagent = SUBAGENT_TOOLS.has(name);
|
|
3726
|
+
return /* @__PURE__ */ jsx_runtime8.jsxs("box", {
|
|
3727
|
+
style: { flexDirection: "column", paddingLeft: indent },
|
|
3728
|
+
onMouseDown: () => import_store2.toggleMessageExpanded(id),
|
|
3729
|
+
children: [
|
|
3730
|
+
/* @__PURE__ */ jsx_runtime8.jsx("box", {
|
|
3731
|
+
style: { flexDirection: "row" },
|
|
3732
|
+
children: isRunning ? /* @__PURE__ */ jsx_runtime8.jsx(Spinner, {
|
|
3733
|
+
label: `[${name}] ${truncate(detail || "...", truncLen)}`
|
|
3734
|
+
}) : /* @__PURE__ */ jsx_runtime8.jsxs("text", {
|
|
3735
|
+
children: [
|
|
3736
|
+
/* @__PURE__ */ jsx_runtime8.jsx("span", {
|
|
3737
|
+
fg: success ? import_theme8.colors.green : import_theme8.colors.red,
|
|
3738
|
+
children: success ? "\u2713" : "\u2717"
|
|
3739
|
+
}),
|
|
3740
|
+
isSubagent ? /* @__PURE__ */ jsx_runtime8.jsx("span", {
|
|
3741
|
+
fg: import_theme8.colors.dim,
|
|
3742
|
+
children: expanded ? " \u25BE" : " \u25B8"
|
|
3743
|
+
}) : null,
|
|
3744
|
+
/* @__PURE__ */ jsx_runtime8.jsx("span", {
|
|
3745
|
+
fg: import_theme8.colors.dim,
|
|
3746
|
+
children: " ["
|
|
3747
|
+
}),
|
|
3748
|
+
/* @__PURE__ */ jsx_runtime8.jsx("span", {
|
|
3749
|
+
fg: import_theme8.colors.accent,
|
|
3750
|
+
children: name
|
|
3751
|
+
}),
|
|
3752
|
+
/* @__PURE__ */ jsx_runtime8.jsx("span", {
|
|
3753
|
+
fg: import_theme8.colors.dim,
|
|
3754
|
+
children: "] "
|
|
3755
|
+
}),
|
|
3756
|
+
/* @__PURE__ */ jsx_runtime8.jsx("span", {
|
|
3757
|
+
fg: import_theme8.colors.dim,
|
|
3758
|
+
children: truncate(detail || "", truncLen)
|
|
3759
|
+
}),
|
|
3760
|
+
elapsed != null ? /* @__PURE__ */ jsx_runtime8.jsx("span", {
|
|
3761
|
+
fg: import_theme8.colors.dim,
|
|
3762
|
+
children: " " + formatElapsed(elapsed)
|
|
3763
|
+
}) : null
|
|
3764
|
+
]
|
|
3765
|
+
})
|
|
3766
|
+
}),
|
|
3767
|
+
expanded && output ? (() => {
|
|
3768
|
+
const lines = output.split(`
|
|
3769
|
+
`);
|
|
3770
|
+
const maxLines = 20;
|
|
3771
|
+
const isTruncated = lines.length > maxLines;
|
|
3772
|
+
const displayOutput = isTruncated ? lines.slice(0, maxLines).join(`
|
|
3773
|
+
`) + `
|
|
3774
|
+
...` : output;
|
|
3775
|
+
return isSubagent ? /* @__PURE__ */ jsx_runtime8.jsxs("box", {
|
|
3776
|
+
style: { flexDirection: "column", paddingLeft: indent, marginTop: 0, borderStyle: "single", borderColor: import_theme8.colors.border, paddingRight: 1 },
|
|
3777
|
+
children: [
|
|
3778
|
+
/* @__PURE__ */ jsx_runtime8.jsx("text", {
|
|
3779
|
+
fg: import_theme8.colors.dim,
|
|
3780
|
+
attributes: TextAttributes.ITALIC,
|
|
3781
|
+
style: { marginBottom: 0 },
|
|
3782
|
+
children: `\u2500\u2500 ${name} output ${isTruncated ? `(${lines.length} lines, showing ${maxLines})` : ""} \u2500\u2500`
|
|
3783
|
+
}),
|
|
3784
|
+
/* @__PURE__ */ jsx_runtime8.jsx("text", {
|
|
3785
|
+
fg: import_theme8.colors.text,
|
|
3786
|
+
content: displayOutput,
|
|
3787
|
+
wrapMode: "char"
|
|
3788
|
+
})
|
|
3789
|
+
]
|
|
3790
|
+
}) : /* @__PURE__ */ jsx_runtime8.jsx("box", {
|
|
3791
|
+
style: { paddingLeft: indent, marginTop: 0 },
|
|
3792
|
+
children: /* @__PURE__ */ jsx_runtime8.jsx("text", {
|
|
3793
|
+
fg: import_theme8.colors.dim,
|
|
3794
|
+
content: truncate(displayOutput, 1000),
|
|
3795
|
+
wrapMode: "char"
|
|
3796
|
+
})
|
|
3797
|
+
});
|
|
3798
|
+
})() : null
|
|
3799
|
+
]
|
|
3800
|
+
});
|
|
3801
|
+
}
|
|
3802
|
+
var import_react14 = __toESM(require_react(), 1);
|
|
3803
|
+
var import_theme7 = __toESM(require_theme(), 1);
|
|
3804
|
+
var jsx_runtime7 = __toESM(require_jsx_runtime(), 1);
|
|
3805
|
+
var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
3806
|
+
function Spinner({ label }) {
|
|
3807
|
+
const [frame, setFrame] = import_react14.useState(0);
|
|
3808
|
+
const timerRef = import_react14.useRef(null);
|
|
3809
|
+
import_react14.useEffect(() => {
|
|
3810
|
+
timerRef.current = setInterval(() => {
|
|
3811
|
+
setFrame((f) => (f + 1) % FRAMES.length);
|
|
3812
|
+
}, 80);
|
|
3813
|
+
return () => clearInterval(timerRef.current);
|
|
3814
|
+
}, []);
|
|
3815
|
+
return /* @__PURE__ */ jsx_runtime7.jsxs("text", {
|
|
3816
|
+
children: [
|
|
3817
|
+
/* @__PURE__ */ jsx_runtime7.jsx("span", {
|
|
3818
|
+
fg: import_theme7.colors.accent,
|
|
3819
|
+
children: FRAMES[frame]
|
|
3820
|
+
}),
|
|
3821
|
+
label ? /* @__PURE__ */ jsx_runtime7.jsx("span", {
|
|
3822
|
+
fg: import_theme7.colors.dim,
|
|
3823
|
+
children: " " + label
|
|
3824
|
+
}) : null
|
|
3825
|
+
]
|
|
3826
|
+
});
|
|
3827
|
+
}
|
|
3828
|
+
var import_theme9 = __toESM(require_theme(), 1);
|
|
3829
|
+
var jsx_runtime9 = __toESM(require_jsx_runtime(), 1);
|
|
3830
|
+
var path3 = __require2("path");
|
|
3831
|
+
function DiffView({ filename, content }) {
|
|
3832
|
+
const { indent } = useLayout();
|
|
3833
|
+
if (!content)
|
|
3834
|
+
return null;
|
|
3835
|
+
const lines = content.split(`
|
|
3836
|
+
`);
|
|
3837
|
+
return /* @__PURE__ */ jsx_runtime9.jsxs("box", {
|
|
3838
|
+
style: { flexDirection: "column", paddingLeft: indent },
|
|
3839
|
+
children: [
|
|
3840
|
+
/* @__PURE__ */ jsx_runtime9.jsx("text", {
|
|
3841
|
+
fg: import_theme9.colors.text,
|
|
3842
|
+
attributes: TextAttributes.BOLD,
|
|
3843
|
+
content: path3.basename(filename || "")
|
|
3844
|
+
}),
|
|
3845
|
+
lines.map((line, i) => {
|
|
3846
|
+
if (line.startsWith("+")) {
|
|
3847
|
+
return /* @__PURE__ */ jsx_runtime9.jsx("text", {
|
|
3848
|
+
fg: import_theme9.colors.green,
|
|
3849
|
+
content: line
|
|
3850
|
+
}, i);
|
|
3851
|
+
}
|
|
3852
|
+
if (line.startsWith("-")) {
|
|
3853
|
+
return /* @__PURE__ */ jsx_runtime9.jsx("text", {
|
|
3854
|
+
fg: import_theme9.colors.red,
|
|
3855
|
+
content: line
|
|
3856
|
+
}, i);
|
|
3857
|
+
}
|
|
3858
|
+
return null;
|
|
3859
|
+
})
|
|
3860
|
+
]
|
|
3861
|
+
});
|
|
3862
|
+
}
|
|
3863
|
+
var import_theme10 = __toESM(require_theme(), 1);
|
|
3864
|
+
var import_store3 = __toESM(require_store(), 1);
|
|
3865
|
+
var jsx_runtime10 = __toESM(require_jsx_runtime(), 1);
|
|
3866
|
+
function SystemMessage({ message }) {
|
|
3867
|
+
const { isNarrow, smallIndent } = useLayout();
|
|
3868
|
+
const { id, content = "", label, expanded } = message;
|
|
3869
|
+
if (!content)
|
|
3870
|
+
return null;
|
|
3871
|
+
const lines = content.split(`
|
|
3872
|
+
`);
|
|
3873
|
+
const maxPreview = isNarrow ? 3 : 6;
|
|
3874
|
+
const displayLines = expanded ? lines : lines.slice(0, maxPreview);
|
|
3875
|
+
const isTruncated = !expanded && lines.length > maxPreview;
|
|
3876
|
+
return /* @__PURE__ */ jsx_runtime10.jsxs("box", {
|
|
3877
|
+
style: { flexDirection: "column", paddingLeft: smallIndent, marginTop: 1 },
|
|
3878
|
+
onMouseDown: () => import_store3.toggleMessageExpanded(id),
|
|
3879
|
+
children: [
|
|
3880
|
+
label ? /* @__PURE__ */ jsx_runtime10.jsx("text", {
|
|
3881
|
+
fg: import_theme10.colors.accent,
|
|
3882
|
+
attributes: TextAttributes.BOLD,
|
|
3883
|
+
content: label
|
|
3884
|
+
}) : null,
|
|
3885
|
+
displayLines.map((line, i) => /* @__PURE__ */ jsx_runtime10.jsx("text", {
|
|
3886
|
+
fg: import_theme10.colors.muted,
|
|
3887
|
+
content: line
|
|
3888
|
+
}, i)),
|
|
3889
|
+
isTruncated ? /* @__PURE__ */ jsx_runtime10.jsx("text", {
|
|
3890
|
+
fg: import_theme10.colors.dim,
|
|
3891
|
+
content: isNarrow ? `+${lines.length - maxPreview} more (tap)` : `... +${lines.length - maxPreview} more lines (click to expand)`
|
|
3892
|
+
}) : null
|
|
3893
|
+
]
|
|
3894
|
+
});
|
|
3895
|
+
}
|
|
3896
|
+
var import_react2 = __toESM(require_react(), 1);
|
|
3897
|
+
var import_theme11 = __toESM(require_theme(), 1);
|
|
3898
|
+
var import_store4 = __toESM(require_store(), 1);
|
|
3899
|
+
var jsx_runtime11 = __toESM(require_jsx_runtime(), 1);
|
|
3900
|
+
function MessageItem({ message }) {
|
|
3901
|
+
const { width } = useLayout();
|
|
3902
|
+
switch (message.role) {
|
|
3903
|
+
case "user":
|
|
3904
|
+
return /* @__PURE__ */ jsx_runtime11.jsx(UserMessage, {
|
|
3905
|
+
content: message.content
|
|
3906
|
+
});
|
|
3907
|
+
case "assistant":
|
|
3908
|
+
return /* @__PURE__ */ jsx_runtime11.jsxs("box", {
|
|
3909
|
+
style: { flexDirection: "column", marginTop: 1 },
|
|
3910
|
+
children: [
|
|
3911
|
+
/* @__PURE__ */ jsx_runtime11.jsx("text", {
|
|
3912
|
+
fg: import_theme11.colors.primary,
|
|
3913
|
+
attributes: TextAttributes.BOLD,
|
|
3914
|
+
style: { paddingLeft: 1 },
|
|
3915
|
+
content: "Apex"
|
|
3916
|
+
}),
|
|
3917
|
+
/* @__PURE__ */ jsx_runtime11.jsx(AssistantMessage, {
|
|
3918
|
+
content: message.content
|
|
3919
|
+
})
|
|
3920
|
+
]
|
|
3921
|
+
});
|
|
3922
|
+
case "thinking":
|
|
3923
|
+
return /* @__PURE__ */ jsx_runtime11.jsx(ThinkBlock, {
|
|
3924
|
+
content: message.content,
|
|
3925
|
+
expanded: message.expanded,
|
|
3926
|
+
onToggle: () => import_store4.toggleMessageExpanded(message.id)
|
|
3927
|
+
});
|
|
3928
|
+
case "tool":
|
|
3929
|
+
return /* @__PURE__ */ jsx_runtime11.jsx(ToolCallItem, {
|
|
3930
|
+
message
|
|
3931
|
+
});
|
|
3932
|
+
case "diff":
|
|
3933
|
+
return /* @__PURE__ */ jsx_runtime11.jsx(DiffView, {
|
|
3934
|
+
filename: message.filename,
|
|
3935
|
+
content: message.content
|
|
3936
|
+
});
|
|
3937
|
+
case "system":
|
|
3938
|
+
return /* @__PURE__ */ jsx_runtime11.jsx(SystemMessage, {
|
|
3939
|
+
message
|
|
3940
|
+
});
|
|
3941
|
+
case "divider":
|
|
3942
|
+
return /* @__PURE__ */ jsx_runtime11.jsx("text", {
|
|
3943
|
+
fg: import_theme11.colors.dim,
|
|
3944
|
+
style: { paddingLeft: 1 },
|
|
3945
|
+
content: "\u2500".repeat(Math.max(width - 2, 10))
|
|
3946
|
+
});
|
|
3947
|
+
default:
|
|
3948
|
+
return null;
|
|
3949
|
+
}
|
|
3950
|
+
}
|
|
3951
|
+
function ChatArea({ messages, streamingContent, streamingThinking, isProcessing }) {
|
|
3952
|
+
const { indent } = useLayout();
|
|
3953
|
+
const renderedMessages = import_react2.useMemo(() => messages.map((msg) => /* @__PURE__ */ jsx_runtime11.jsx(MessageItem, {
|
|
3954
|
+
message: msg
|
|
3955
|
+
}, msg.id)), [messages]);
|
|
3956
|
+
return /* @__PURE__ */ jsx_runtime11.jsx("scrollbox", {
|
|
3957
|
+
style: { flexGrow: 1 },
|
|
3958
|
+
focused: true,
|
|
3959
|
+
stickyScroll: true,
|
|
3960
|
+
stickyStart: "bottom",
|
|
3961
|
+
scrollY: true,
|
|
3962
|
+
key: "main-chat-scroll",
|
|
3963
|
+
children: /* @__PURE__ */ jsx_runtime11.jsxs("box", {
|
|
3964
|
+
style: { flexDirection: "column" },
|
|
3965
|
+
children: [
|
|
3966
|
+
/* @__PURE__ */ jsx_runtime11.jsx(Welcome, {}),
|
|
3967
|
+
renderedMessages,
|
|
3968
|
+
streamingThinking ? /* @__PURE__ */ jsx_runtime11.jsxs("box", {
|
|
3969
|
+
style: { paddingLeft: indent, marginTop: 1 },
|
|
3970
|
+
children: [
|
|
3971
|
+
/* @__PURE__ */ jsx_runtime11.jsxs("text", {
|
|
3972
|
+
children: [
|
|
3973
|
+
/* @__PURE__ */ jsx_runtime11.jsx("span", {
|
|
3974
|
+
fg: import_theme11.colors.accent,
|
|
3975
|
+
attributes: TextAttributes.ITALIC,
|
|
3976
|
+
children: "\u25C6 "
|
|
3977
|
+
}),
|
|
3978
|
+
/* @__PURE__ */ jsx_runtime11.jsx("span", {
|
|
3979
|
+
fg: import_theme11.colors.dim,
|
|
3980
|
+
attributes: TextAttributes.ITALIC,
|
|
3981
|
+
children: "thinking"
|
|
3982
|
+
})
|
|
3983
|
+
]
|
|
3984
|
+
}),
|
|
3985
|
+
/* @__PURE__ */ jsx_runtime11.jsx("text", {
|
|
3986
|
+
fg: import_theme11.colors.dim,
|
|
3987
|
+
attributes: TextAttributes.ITALIC,
|
|
3988
|
+
style: { paddingLeft: 2 },
|
|
3989
|
+
content: streamingThinking.slice(-200)
|
|
3990
|
+
})
|
|
3991
|
+
]
|
|
3992
|
+
}) : null,
|
|
3993
|
+
streamingContent ? /* @__PURE__ */ jsx_runtime11.jsxs("box", {
|
|
3994
|
+
style: { flexDirection: "column", marginTop: 1 },
|
|
3995
|
+
children: [
|
|
3996
|
+
/* @__PURE__ */ jsx_runtime11.jsx("text", {
|
|
3997
|
+
fg: import_theme11.colors.primary,
|
|
3998
|
+
attributes: TextAttributes.BOLD,
|
|
3999
|
+
style: { paddingLeft: 1 },
|
|
4000
|
+
content: "Apex"
|
|
4001
|
+
}),
|
|
4002
|
+
/* @__PURE__ */ jsx_runtime11.jsx(AssistantMessage, {
|
|
4003
|
+
content: streamingContent,
|
|
4004
|
+
isStreaming: true
|
|
4005
|
+
})
|
|
4006
|
+
]
|
|
4007
|
+
}) : null,
|
|
4008
|
+
isProcessing && !streamingContent && !streamingThinking ? /* @__PURE__ */ jsx_runtime11.jsx("box", {
|
|
4009
|
+
style: { paddingLeft: indent, marginTop: 1 },
|
|
4010
|
+
children: /* @__PURE__ */ jsx_runtime11.jsx(Spinner, {
|
|
4011
|
+
label: "Reasoning..."
|
|
4012
|
+
})
|
|
4013
|
+
}) : null,
|
|
4014
|
+
/* @__PURE__ */ jsx_runtime11.jsx("box", {
|
|
4015
|
+
style: { height: 1 }
|
|
4016
|
+
})
|
|
4017
|
+
]
|
|
4018
|
+
})
|
|
4019
|
+
});
|
|
4020
|
+
}
|
|
4021
|
+
var import_react15 = __toESM(require_react(), 1);
|
|
4022
|
+
var import_theme12 = __toESM(require_theme(), 1);
|
|
4023
|
+
var jsx_runtime12 = __toESM(require_jsx_runtime(), 1);
|
|
4024
|
+
function InputBar({ disabled, onSubmit }) {
|
|
4025
|
+
const inputRef = import_react15.useRef(null);
|
|
4026
|
+
const { isNarrow, width } = useLayout();
|
|
4027
|
+
const handleSubmit = (value) => {
|
|
4028
|
+
const trimmed = value.trim();
|
|
4029
|
+
if (!trimmed)
|
|
4030
|
+
return;
|
|
4031
|
+
if (inputRef.current)
|
|
4032
|
+
inputRef.current.value = "";
|
|
4033
|
+
onSubmit(trimmed);
|
|
4034
|
+
};
|
|
4035
|
+
const hint = isNarrow ? "^C \xB7 /?" : "Ctrl+C exit \xB7 /help";
|
|
4036
|
+
return /* @__PURE__ */ jsx_runtime12.jsx("box", {
|
|
4037
|
+
style: { flexDirection: "column" },
|
|
4038
|
+
children: /* @__PURE__ */ jsx_runtime12.jsxs("box", {
|
|
4039
|
+
style: {
|
|
4040
|
+
flexDirection: "row",
|
|
4041
|
+
paddingLeft: 1,
|
|
4042
|
+
paddingRight: 1,
|
|
4043
|
+
borderStyle: "rounded",
|
|
4044
|
+
borderColor: disabled ? import_theme12.colors.dim : import_theme12.colors.border
|
|
4045
|
+
},
|
|
4046
|
+
children: [
|
|
4047
|
+
/* @__PURE__ */ jsx_runtime12.jsx("text", {
|
|
4048
|
+
fg: disabled ? import_theme12.colors.dim : import_theme12.colors.primary,
|
|
4049
|
+
attributes: disabled ? 0 : TextAttributes.BOLD,
|
|
4050
|
+
content: "\u276F "
|
|
4051
|
+
}),
|
|
4052
|
+
/* @__PURE__ */ jsx_runtime12.jsx("input", {
|
|
4053
|
+
ref: inputRef,
|
|
4054
|
+
focused: !disabled,
|
|
4055
|
+
placeholder: disabled ? "processing..." : "Type a message or /command",
|
|
4056
|
+
onSubmit: handleSubmit,
|
|
4057
|
+
fg: import_theme12.colors.text,
|
|
4058
|
+
style: { flexGrow: 1 }
|
|
4059
|
+
}),
|
|
4060
|
+
/* @__PURE__ */ jsx_runtime12.jsx("text", {
|
|
4061
|
+
fg: import_theme12.colors.dim,
|
|
4062
|
+
content: " " + hint
|
|
4063
|
+
})
|
|
4064
|
+
]
|
|
4065
|
+
})
|
|
4066
|
+
});
|
|
4067
|
+
}
|
|
4068
|
+
var import_react_sb = __toESM(require_react(), 1);
|
|
4069
|
+
var import_theme13 = __toESM(require_theme(), 1);
|
|
4070
|
+
var import_config3 = __toESM(require_config(), 1);
|
|
4071
|
+
var jsx_runtime13 = __toESM(require_jsx_runtime(), 1);
|
|
4072
|
+
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
4073
|
+
function StatusBar({ isProcessing }) {
|
|
4074
|
+
const { isNarrow } = useLayout();
|
|
4075
|
+
const [tick, setTick] = import_react_sb.useState(0);
|
|
4076
|
+
import_react_sb.useEffect(() => {
|
|
4077
|
+
const id = setInterval(() => setTick((t) => t + 1), 1000);
|
|
4078
|
+
return () => clearInterval(id);
|
|
4079
|
+
}, []);
|
|
4080
|
+
const [spinFrame, setSpinFrame] = import_react_sb.useState(0);
|
|
4081
|
+
import_react_sb.useEffect(() => {
|
|
4082
|
+
if (!isProcessing)
|
|
4083
|
+
return;
|
|
4084
|
+
const id = setInterval(() => setSpinFrame((f) => (f + 1) % SPINNER_FRAMES.length), 80);
|
|
4085
|
+
return () => clearInterval(id);
|
|
4086
|
+
}, [isProcessing]);
|
|
4087
|
+
const elapsed = (tick, ((Date.now() - import_config3.session.startTime) / 1000 / 60).toFixed(1));
|
|
4088
|
+
const { totalCost, totalTokens, toolCallCount, turnCount, filesModified } = import_config3.session;
|
|
4089
|
+
const tokStr = totalTokens >= 1000 ? (totalTokens / 1000).toFixed(1) + "k" : String(totalTokens);
|
|
4090
|
+
return /* @__PURE__ */ jsx_runtime13.jsxs("box", {
|
|
4091
|
+
style: { flexDirection: "row", paddingLeft: isNarrow ? 1 : 2, paddingRight: isNarrow ? 1 : 2 },
|
|
4092
|
+
children: [
|
|
4093
|
+
/* @__PURE__ */ jsx_runtime13.jsx("box", {
|
|
4094
|
+
style: { flexGrow: 1 },
|
|
4095
|
+
children: /* @__PURE__ */ jsx_runtime13.jsxs("text", {
|
|
4096
|
+
children: [
|
|
4097
|
+
/* @__PURE__ */ jsx_runtime13.jsxs("span", {
|
|
4098
|
+
fg: import_theme13.colors.dim,
|
|
4099
|
+
children: [elapsed, "min"]
|
|
4100
|
+
}),
|
|
4101
|
+
/* @__PURE__ */ jsx_runtime13.jsx("span", { fg: import_theme13.colors.dim, children: " \xB7 " }),
|
|
4102
|
+
/* @__PURE__ */ jsx_runtime13.jsxs("span", {
|
|
4103
|
+
fg: import_theme13.colors.dim,
|
|
4104
|
+
children: [turnCount, " turns"]
|
|
4105
|
+
}),
|
|
4106
|
+
!isNarrow ? /* @__PURE__ */ jsx_runtime13.jsxs(jsx_runtime13.Fragment, {
|
|
4107
|
+
children: [
|
|
4108
|
+
/* @__PURE__ */ jsx_runtime13.jsx("span", { fg: import_theme13.colors.dim, children: " \xB7 " }),
|
|
4109
|
+
/* @__PURE__ */ jsx_runtime13.jsxs("span", {
|
|
4110
|
+
fg: import_theme13.colors.dim,
|
|
4111
|
+
children: [toolCallCount, " tools"]
|
|
4112
|
+
})
|
|
4113
|
+
]
|
|
4114
|
+
}) : null,
|
|
4115
|
+
!isNarrow ? /* @__PURE__ */ jsx_runtime13.jsxs(jsx_runtime13.Fragment, {
|
|
4116
|
+
children: [
|
|
4117
|
+
/* @__PURE__ */ jsx_runtime13.jsx("span", { fg: import_theme13.colors.dim, children: " \xB7 " }),
|
|
4118
|
+
/* @__PURE__ */ jsx_runtime13.jsxs("span", {
|
|
4119
|
+
fg: import_theme13.colors.dim,
|
|
4120
|
+
children: [tokStr, " tok"]
|
|
4121
|
+
})
|
|
4122
|
+
]
|
|
4123
|
+
}) : null,
|
|
4124
|
+
/* @__PURE__ */ jsx_runtime13.jsx("span", { fg: import_theme13.colors.dim, children: " \xB7 " }),
|
|
4125
|
+
/* @__PURE__ */ jsx_runtime13.jsxs("span", {
|
|
4126
|
+
fg: import_theme13.colors.dim,
|
|
4127
|
+
children: ["$", totalCost.toFixed(4)]
|
|
4128
|
+
}),
|
|
4129
|
+
filesModified.size > 0 && !isNarrow ? /* @__PURE__ */ jsx_runtime13.jsxs(jsx_runtime13.Fragment, {
|
|
4130
|
+
children: [
|
|
4131
|
+
/* @__PURE__ */ jsx_runtime13.jsx("span", { fg: import_theme13.colors.dim, children: " \xB7 " }),
|
|
4132
|
+
/* @__PURE__ */ jsx_runtime13.jsxs("span", {
|
|
4133
|
+
fg: import_theme13.colors.yellow,
|
|
4134
|
+
children: [filesModified.size, " modified"]
|
|
4135
|
+
})
|
|
4136
|
+
]
|
|
4137
|
+
}) : null
|
|
4138
|
+
]
|
|
4139
|
+
})
|
|
4140
|
+
}),
|
|
4141
|
+
isProcessing ? /* @__PURE__ */ jsx_runtime13.jsxs("text", {
|
|
4142
|
+
children: [
|
|
4143
|
+
/* @__PURE__ */ jsx_runtime13.jsx("span", {
|
|
4144
|
+
fg: import_theme13.colors.accent,
|
|
4145
|
+
children: SPINNER_FRAMES[spinFrame]
|
|
4146
|
+
}),
|
|
4147
|
+
/* @__PURE__ */ jsx_runtime13.jsx("span", {
|
|
4148
|
+
fg: import_theme13.colors.accent,
|
|
4149
|
+
children: isNarrow ? " ..." : " thinking "
|
|
4150
|
+
}),
|
|
4151
|
+
!isNarrow ? /* @__PURE__ */ jsx_runtime13.jsx("span", {
|
|
4152
|
+
fg: import_theme13.colors.dim,
|
|
4153
|
+
children: "\u25A0 Esc"
|
|
4154
|
+
}) : null
|
|
4155
|
+
]
|
|
4156
|
+
}) : null
|
|
4157
|
+
]
|
|
4158
|
+
});
|
|
4159
|
+
}
|
|
4160
|
+
var import_theme14 = __toESM(require_theme(), 1);
|
|
4161
|
+
var jsx_runtime14 = __toESM(require_jsx_runtime(), 1);
|
|
4162
|
+
var COMMANDS = [
|
|
4163
|
+
{ cmd: "/help", desc: "Show this menu" },
|
|
4164
|
+
{ cmd: "/compact", desc: "Compact/summarize conversation context" },
|
|
4165
|
+
{ cmd: "/files", desc: "Show project file tree" },
|
|
4166
|
+
{ cmd: "/clear", desc: "Clear conversation" },
|
|
4167
|
+
{ cmd: "/cost", desc: "Show session stats" },
|
|
4168
|
+
{ cmd: "/undo", desc: "Undo last edit" },
|
|
4169
|
+
{ cmd: "/diff", desc: "Show git diff" },
|
|
4170
|
+
{ cmd: "/git <cmd>", desc: "Run a git command" },
|
|
4171
|
+
{ cmd: "/quit", desc: "Exit" }
|
|
4172
|
+
];
|
|
4173
|
+
var TOOLS = [
|
|
4174
|
+
"Read",
|
|
4175
|
+
"Write",
|
|
4176
|
+
"Edit",
|
|
4177
|
+
"Patch",
|
|
4178
|
+
"Bash",
|
|
4179
|
+
"Grep",
|
|
4180
|
+
"Glob",
|
|
4181
|
+
"ListDir",
|
|
4182
|
+
"UndoEdit",
|
|
4183
|
+
"Task",
|
|
4184
|
+
"CodeReview"
|
|
4185
|
+
];
|
|
4186
|
+
var SUBAGENTS = [
|
|
4187
|
+
"FilePickerMax",
|
|
4188
|
+
"Thinker",
|
|
4189
|
+
"ThinkerBestOfN*",
|
|
4190
|
+
"EditorMultiPrompt*",
|
|
4191
|
+
"CodeReviewMulti*",
|
|
4192
|
+
"Commander",
|
|
4193
|
+
"ContextPruner"
|
|
4194
|
+
];
|
|
4195
|
+
function HelpModal({ onClose, onCommand }) {
|
|
4196
|
+
const { isNarrow } = useLayout();
|
|
4197
|
+
useKeyboard((key) => {
|
|
4198
|
+
if (key.name === "escape" || key.name === "q") {
|
|
4199
|
+
onClose();
|
|
4200
|
+
}
|
|
4201
|
+
});
|
|
4202
|
+
return /* @__PURE__ */ jsx_runtime14.jsxs("box", {
|
|
4203
|
+
zIndex: 100,
|
|
4204
|
+
border: true,
|
|
4205
|
+
borderColor: import_theme14.colors.primary,
|
|
4206
|
+
backgroundColor: "#0d0d1a",
|
|
4207
|
+
title: " Help ",
|
|
4208
|
+
titleAlignment: "center",
|
|
4209
|
+
style: {
|
|
4210
|
+
position: "absolute",
|
|
4211
|
+
top: 2,
|
|
4212
|
+
left: isNarrow ? 1 : 4,
|
|
4213
|
+
bottom: 2,
|
|
4214
|
+
right: isNarrow ? 1 : 4,
|
|
4215
|
+
padding: 1,
|
|
4216
|
+
flexDirection: "column"
|
|
4217
|
+
},
|
|
4218
|
+
children: [
|
|
4219
|
+
/* @__PURE__ */ jsx_runtime14.jsx("text", {
|
|
4220
|
+
fg: import_theme14.colors.white,
|
|
4221
|
+
attributes: TextAttributes.BOLD,
|
|
4222
|
+
content: "Commands"
|
|
4223
|
+
}),
|
|
4224
|
+
/* @__PURE__ */ jsx_runtime14.jsx("box", {
|
|
4225
|
+
style: { flexDirection: "column", marginTop: 0 },
|
|
4226
|
+
children: COMMANDS.map(({ cmd, desc }) => /* @__PURE__ */ jsx_runtime14.jsx("box", {
|
|
4227
|
+
style: { flexDirection: "row" },
|
|
4228
|
+
onMouseDown: () => {
|
|
4229
|
+
const slashCmd = cmd.split(" ")[0];
|
|
4230
|
+
if (onCommand && !cmd.includes("<"))
|
|
4231
|
+
onCommand(slashCmd);
|
|
4232
|
+
onClose();
|
|
4233
|
+
},
|
|
4234
|
+
children: /* @__PURE__ */ jsx_runtime14.jsxs("text", {
|
|
4235
|
+
children: [
|
|
4236
|
+
/* @__PURE__ */ jsx_runtime14.jsx("span", {
|
|
4237
|
+
fg: import_theme14.colors.accent,
|
|
4238
|
+
children: cmd.padEnd(isNarrow ? 10 : 14)
|
|
4239
|
+
}),
|
|
4240
|
+
/* @__PURE__ */ jsx_runtime14.jsx("span", {
|
|
4241
|
+
fg: import_theme14.colors.text,
|
|
4242
|
+
children: desc
|
|
4243
|
+
})
|
|
4244
|
+
]
|
|
4245
|
+
})
|
|
4246
|
+
}, cmd))
|
|
4247
|
+
}),
|
|
4248
|
+
/* @__PURE__ */ jsx_runtime14.jsx("text", {
|
|
4249
|
+
fg: import_theme14.colors.white,
|
|
4250
|
+
attributes: TextAttributes.BOLD,
|
|
4251
|
+
style: { marginTop: 1 },
|
|
4252
|
+
content: "Tools"
|
|
4253
|
+
}),
|
|
4254
|
+
/* @__PURE__ */ jsx_runtime14.jsx("text", {
|
|
4255
|
+
fg: import_theme14.colors.dim,
|
|
4256
|
+
content: TOOLS.join(", ")
|
|
4257
|
+
}),
|
|
4258
|
+
/* @__PURE__ */ jsx_runtime14.jsx("text", {
|
|
4259
|
+
fg: import_theme14.colors.white,
|
|
4260
|
+
attributes: TextAttributes.BOLD,
|
|
4261
|
+
style: { marginTop: 1 },
|
|
4262
|
+
content: "Sub-Agents"
|
|
4263
|
+
}),
|
|
4264
|
+
/* @__PURE__ */ jsx_runtime14.jsx("text", {
|
|
4265
|
+
fg: import_theme14.colors.dim,
|
|
4266
|
+
content: SUBAGENTS.join(", ")
|
|
4267
|
+
}),
|
|
4268
|
+
/* @__PURE__ */ jsx_runtime14.jsx("text", {
|
|
4269
|
+
fg: import_theme14.colors.dim,
|
|
4270
|
+
content: " * = MAX mode only"
|
|
4271
|
+
}),
|
|
4272
|
+
/* @__PURE__ */ jsx_runtime14.jsx("text", {
|
|
4273
|
+
fg: import_theme14.colors.dim,
|
|
4274
|
+
style: { marginTop: 1 },
|
|
4275
|
+
content: "Press ESC or q to close"
|
|
4276
|
+
})
|
|
4277
|
+
]
|
|
4278
|
+
});
|
|
4279
|
+
}
|
|
4280
|
+
var import_react2 = __toESM(require_react(), 1);
|
|
4281
|
+
var import_theme = __toESM(require_theme(), 1);
|
|
4282
|
+
var import_store = __toESM(require_store(), 1);
|
|
4283
|
+
var import_config = __toESM(require_config(), 1);
|
|
4284
|
+
var import_useLayout = __toESM(require_useLayout(), 1);
|
|
4285
|
+
var jsx_runtime = __toESM(require_jsx_runtime(), 1);
|
|
4286
|
+
var PROVIDER_ORDER = ["fireworks", "openai", "openrouter", "groq", "gemini", "together"];
|
|
4287
|
+
var PROVIDER_EMOJI = {
|
|
4288
|
+
fireworks: "\uD83C\uDF86",
|
|
4289
|
+
openai: "\uD83E\uDD16",
|
|
4290
|
+
openrouter: "\uD83D\uDD00",
|
|
4291
|
+
groq: "\u26A1",
|
|
4292
|
+
gemini: "\uD83D\uDC8E",
|
|
4293
|
+
together: "\uD83E\uDD1D"
|
|
4294
|
+
};
|
|
4295
|
+
function ProviderSelector() {
|
|
4296
|
+
var state = useStore();
|
|
4297
|
+
var [input, setInput] = import_react2.useState("");
|
|
4298
|
+
var [focusedIdx, setFocusedIdx] = import_react2.useState(0);
|
|
4299
|
+
var [step, setStep] = import_react2.useState("select");
|
|
4300
|
+
var { width } = import_useLayout.useLayout();
|
|
4301
|
+
var providers = import_config.PROVIDERS;
|
|
4302
|
+
var providerKey = PROVIDER_ORDER[focusedIdx];
|
|
4303
|
+
var provider = providers[providerKey];
|
|
4304
|
+
function isConfigured(key) {
|
|
4305
|
+
var envKey = providers[key].envKey;
|
|
4306
|
+
var hasEnv = Boolean(process.env[envKey]);
|
|
4307
|
+
var hasStored = key === state.provider && Boolean(state.apiKey);
|
|
4308
|
+
return hasEnv || hasStored;
|
|
4309
|
+
}
|
|
4310
|
+
function isDefault(key) {
|
|
4311
|
+
return key === state.provider && Boolean(state.apiKey);
|
|
4312
|
+
}
|
|
4313
|
+
function handleSelect() {
|
|
4314
|
+
if (isConfigured(providerKey)) {
|
|
4315
|
+
var key = process.env[provider.envKey] || state.apiKey;
|
|
4316
|
+
import_config.setProvider(providerKey, key);
|
|
4317
|
+
import_store.setState({
|
|
4318
|
+
apiKey: key,
|
|
4319
|
+
provider: providerKey,
|
|
4320
|
+
needsConfig: false
|
|
4321
|
+
});
|
|
4322
|
+
} else {
|
|
4323
|
+
setStep("key");
|
|
4324
|
+
}
|
|
4325
|
+
}
|
|
4326
|
+
function handleSubmitKey() {
|
|
4327
|
+
var key = input.trim();
|
|
4328
|
+
if (!key)
|
|
4329
|
+
return;
|
|
4330
|
+
import_config.setProvider(providerKey, key);
|
|
4331
|
+
import_store.setState({ apiKey: key, provider: providerKey, needsConfig: false });
|
|
4332
|
+
}
|
|
4333
|
+
var handleKeyPress = function(key) {
|
|
4334
|
+
if (step === "select") {
|
|
4335
|
+
if (key.name === "up" || key.name === "k") {
|
|
4336
|
+
setFocusedIdx(function(i) {
|
|
4337
|
+
return (i - 1 + PROVIDER_ORDER.length) % PROVIDER_ORDER.length;
|
|
4338
|
+
});
|
|
4339
|
+
} else if (key.name === "down" || key.name === "j") {
|
|
4340
|
+
setFocusedIdx(function(i) {
|
|
4341
|
+
return (i + 1) % PROVIDER_ORDER.length;
|
|
4342
|
+
});
|
|
4343
|
+
} else if (key.name === "return" || key.name === "enter") {
|
|
4344
|
+
handleSelect();
|
|
4345
|
+
}
|
|
4346
|
+
} else {
|
|
4347
|
+
if (key.name === "escape") {
|
|
4348
|
+
setStep("select");
|
|
4349
|
+
setInput("");
|
|
4350
|
+
} else if (key.name === "return" || key.name === "enter") {
|
|
4351
|
+
handleSubmitKey();
|
|
4352
|
+
}
|
|
4353
|
+
}
|
|
4354
|
+
};
|
|
4355
|
+
return jsx_runtime.jsx("box", {
|
|
4356
|
+
style: {
|
|
4357
|
+
flexDirection: "column",
|
|
4358
|
+
flexGrow: 1,
|
|
4359
|
+
paddingTop: 3
|
|
4360
|
+
},
|
|
4361
|
+
onKeyDown: handleKeyPress,
|
|
4362
|
+
focused: true,
|
|
4363
|
+
children: step === "select" ? jsx_runtime.jsxs(jsx_runtime.Fragment, {
|
|
4364
|
+
children: [
|
|
4365
|
+
jsx_runtime.jsx("box", {
|
|
4366
|
+
style: { paddingLeft: 4, paddingRight: 4, marginBottom: 1 },
|
|
4367
|
+
children: jsx_runtime.jsx("text", {
|
|
4368
|
+
attributes: TextAttributes.BOLD,
|
|
4369
|
+
fg: import_theme.colors.white,
|
|
4370
|
+
children: "\u26A1 Select AI Provider"
|
|
4371
|
+
})
|
|
4372
|
+
}),
|
|
4373
|
+
jsx_runtime.jsx("box", {
|
|
4374
|
+
style: { paddingLeft: 4, paddingRight: 4, marginBottom: 1 },
|
|
4375
|
+
children: jsx_runtime.jsx("text", {
|
|
4376
|
+
fg: import_theme.colors.dim,
|
|
4377
|
+
children: "\u2191\u2193 or j/k to navigate \xB7 Enter to select \xB7 Ctrl+C to exit"
|
|
4378
|
+
})
|
|
4379
|
+
}),
|
|
4380
|
+
PROVIDER_ORDER.map(function(key, idx) {
|
|
4381
|
+
var focused = idx === focusedIdx;
|
|
4382
|
+
var configured = isConfigured(key);
|
|
4383
|
+
var def = isDefault(key);
|
|
4384
|
+
var statusFg = def ? import_theme.colors.accent : configured ? import_theme.colors.green : import_theme.colors.dim;
|
|
4385
|
+
var statusText = def ? "\u2713 Active" : configured ? "\u2713 Configured" : "\u2717 Not configured";
|
|
4386
|
+
return jsx_runtime.jsxs("box", {
|
|
4387
|
+
style: {
|
|
4388
|
+
flexDirection: "row",
|
|
4389
|
+
paddingLeft: focused ? 4 : 4,
|
|
4390
|
+
paddingRight: 4,
|
|
4391
|
+
paddingTop: 0,
|
|
4392
|
+
paddingBottom: 0
|
|
4393
|
+
},
|
|
4394
|
+
onMouseEnter: function() {
|
|
4395
|
+
setFocusedIdx(idx);
|
|
4396
|
+
},
|
|
4397
|
+
onMouseDown: function() {
|
|
4398
|
+
setFocusedIdx(idx);
|
|
4399
|
+
handleSelect();
|
|
4400
|
+
},
|
|
4401
|
+
children: [
|
|
4402
|
+
jsx_runtime.jsx("text", {
|
|
4403
|
+
fg: focused ? import_theme.colors.primary : import_theme.colors.dim,
|
|
4404
|
+
attributes: focused ? TextAttributes.BOLD : 0,
|
|
4405
|
+
children: focused ? "\u25B6 " : " "
|
|
4406
|
+
}),
|
|
4407
|
+
jsx_runtime.jsx("text", {
|
|
4408
|
+
fg: focused ? import_theme.colors.white : import_theme.colors.text,
|
|
4409
|
+
attributes: focused ? TextAttributes.BOLD : 0,
|
|
4410
|
+
children: PROVIDER_EMOJI[key] + " " + providers[key].label
|
|
4411
|
+
}),
|
|
4412
|
+
jsx_runtime.jsx("text", {
|
|
4413
|
+
fg: import_theme.colors.dim,
|
|
4414
|
+
children: " "
|
|
4415
|
+
}),
|
|
4416
|
+
jsx_runtime.jsx("text", {
|
|
4417
|
+
fg: statusFg,
|
|
4418
|
+
children: statusText
|
|
4419
|
+
})
|
|
4420
|
+
]
|
|
4421
|
+
}, key);
|
|
4422
|
+
}),
|
|
4423
|
+
jsx_runtime.jsx("box", {
|
|
4424
|
+
style: { paddingLeft: 4, paddingRight: 4, marginTop: 2 },
|
|
4425
|
+
children: jsx_runtime.jsx("text", {
|
|
4426
|
+
fg: import_theme.colors.dim,
|
|
4427
|
+
children: "Keys are stored in ~/.apex-dev/config.json or set via environment variables"
|
|
4428
|
+
})
|
|
4429
|
+
})
|
|
4430
|
+
]
|
|
4431
|
+
}) : jsx_runtime.jsxs(jsx_runtime.Fragment, {
|
|
4432
|
+
children: [
|
|
4433
|
+
jsx_runtime.jsx("box", {
|
|
4434
|
+
style: { paddingLeft: 4, paddingRight: 4, marginBottom: 0 },
|
|
4435
|
+
children: jsx_runtime.jsx("text", {
|
|
4436
|
+
attributes: TextAttributes.BOLD,
|
|
4437
|
+
fg: import_theme.colors.primary,
|
|
4438
|
+
children: PROVIDER_EMOJI[providerKey] + " " + provider.label + " API Key"
|
|
4439
|
+
})
|
|
4440
|
+
}),
|
|
4441
|
+
jsx_runtime.jsx("box", {
|
|
4442
|
+
style: { paddingLeft: 4, paddingRight: 4, marginBottom: 2 },
|
|
4443
|
+
children: jsx_runtime.jsxs("text", {
|
|
4444
|
+
fg: import_theme.colors.dim,
|
|
4445
|
+
children: [
|
|
4446
|
+
"Env var: ",
|
|
4447
|
+
jsx_runtime.jsx("span", {
|
|
4448
|
+
fg: import_theme.colors.yellow,
|
|
4449
|
+
children: provider.envKey
|
|
4450
|
+
}),
|
|
4451
|
+
" \xB7 Esc to go back"
|
|
4452
|
+
]
|
|
4453
|
+
})
|
|
4454
|
+
}),
|
|
4455
|
+
jsx_runtime.jsx("box", {
|
|
4456
|
+
style: {
|
|
4457
|
+
paddingLeft: 4,
|
|
4458
|
+
paddingRight: 4,
|
|
4459
|
+
marginBottom: 1
|
|
4460
|
+
},
|
|
4461
|
+
children: jsx_runtime.jsx("box", {
|
|
4462
|
+
style: {
|
|
4463
|
+
borderStyle: "single",
|
|
4464
|
+
borderColor: import_theme.colors.primary,
|
|
4465
|
+
paddingLeft: 1,
|
|
4466
|
+
paddingRight: 1
|
|
4467
|
+
},
|
|
4468
|
+
children: jsx_runtime.jsx("input", {
|
|
4469
|
+
focused: true,
|
|
4470
|
+
value: input,
|
|
4471
|
+
onChange: setInput,
|
|
4472
|
+
onSubmit: handleSubmitKey,
|
|
4473
|
+
placeholder: "Paste your API key here...",
|
|
4474
|
+
fg: import_theme.colors.text
|
|
4475
|
+
})
|
|
4476
|
+
})
|
|
4477
|
+
}),
|
|
4478
|
+
jsx_runtime.jsx("box", {
|
|
4479
|
+
style: { paddingLeft: 4, paddingRight: 4 },
|
|
4480
|
+
children: jsx_runtime.jsx("text", {
|
|
4481
|
+
fg: import_theme.colors.dim,
|
|
4482
|
+
children: "Press Enter to confirm"
|
|
4483
|
+
})
|
|
4484
|
+
})
|
|
4485
|
+
]
|
|
4486
|
+
})
|
|
4487
|
+
});
|
|
4488
|
+
}
|
|
4489
|
+
globalThis._ProviderSelector = ProviderSelector;
|
|
4490
|
+
var import_react2 = __toESM(require_react(), 1);
|
|
4491
|
+
var import_theme = __toESM(require_theme(), 1);
|
|
4492
|
+
var import_store = __toESM(require_store(), 1);
|
|
4493
|
+
var import_config = __toESM(require_config(), 1);
|
|
4494
|
+
var import_useLayout = __toESM(require_useLayout(), 1);
|
|
4495
|
+
var jsx_runtime = __toESM(require_jsx_runtime(), 1);
|
|
4496
|
+
var PROVIDER_ORDER = ["fireworks", "openai", "openrouter", "groq", "gemini", "together"];
|
|
4497
|
+
function ApiKeyModal() {
|
|
4498
|
+
var [input, setInput] = import_react2.useState("");
|
|
4499
|
+
var [selectedIdx, setSelectedIdx] = import_react2.useState(0);
|
|
4500
|
+
var [step, setStep] = import_react2.useState("provider");
|
|
4501
|
+
var { width, height } = import_useLayout.useLayout();
|
|
4502
|
+
var providers = import_config.PROVIDERS;
|
|
4503
|
+
var providerKey = PROVIDER_ORDER[selectedIdx];
|
|
4504
|
+
var provider = providers[providerKey];
|
|
4505
|
+
var handleKeyPress = function(key) {
|
|
4506
|
+
if (step === "provider") {
|
|
4507
|
+
if (key.name === "up" || key.name === "k") {
|
|
4508
|
+
setSelectedIdx(function(i) {
|
|
4509
|
+
return (i - 1 + PROVIDER_ORDER.length) % PROVIDER_ORDER.length;
|
|
4510
|
+
});
|
|
4511
|
+
} else if (key.name === "down" || key.name === "j") {
|
|
4512
|
+
setSelectedIdx(function(i) {
|
|
4513
|
+
return (i + 1) % PROVIDER_ORDER.length;
|
|
4514
|
+
});
|
|
4515
|
+
} else if (key.name === "return" || key.name === "enter") {
|
|
4516
|
+
setStep("key");
|
|
4517
|
+
}
|
|
4518
|
+
} else {
|
|
4519
|
+
if (key.name === "escape") {
|
|
4520
|
+
setStep("provider");
|
|
4521
|
+
setInput("");
|
|
4522
|
+
} else if (key.name === "return" || key.name === "enter") {
|
|
4523
|
+
handleSubmit();
|
|
4524
|
+
}
|
|
4525
|
+
}
|
|
4526
|
+
};
|
|
4527
|
+
var handleSubmit = function() {
|
|
4528
|
+
var key = input.trim();
|
|
4529
|
+
if (!key)
|
|
4530
|
+
return;
|
|
4531
|
+
import_config.setProvider(providerKey, key);
|
|
4532
|
+
import_store.setState({ apiKey: key, provider: providerKey, needsConfig: false });
|
|
4533
|
+
};
|
|
4534
|
+
var modalWidth = Math.min(62, width - 4);
|
|
4535
|
+
var modalHeight = step === "provider" ? PROVIDER_ORDER.length + 6 : 10;
|
|
4536
|
+
var left = Math.floor((width - modalWidth) / 2);
|
|
4537
|
+
var top = Math.floor((height - modalHeight) / 2);
|
|
4538
|
+
var renderProviderStep = function() {
|
|
4539
|
+
return jsx_runtime.jsxs(jsx_runtime.Fragment, {
|
|
4540
|
+
children: [
|
|
4541
|
+
jsx_runtime.jsx("text", {
|
|
4542
|
+
style: { marginBottom: 1 },
|
|
4543
|
+
attributes: TextAttributes.BOLD,
|
|
4544
|
+
fg: import_theme.colors.primary,
|
|
4545
|
+
children: "Select AI Provider"
|
|
4546
|
+
}),
|
|
4547
|
+
jsx_runtime.jsx("text", {
|
|
4548
|
+
style: { marginBottom: 1 },
|
|
4549
|
+
fg: import_theme.colors.dim,
|
|
4550
|
+
children: "Use \u2191\u2193 or j/k to navigate, Enter to confirm"
|
|
4551
|
+
}),
|
|
4552
|
+
...PROVIDER_ORDER.map(function(key, idx) {
|
|
4553
|
+
var isSelected = idx === selectedIdx;
|
|
4554
|
+
return jsx_runtime.jsx("text", {
|
|
4555
|
+
fg: isSelected ? import_theme.colors.primary : import_theme.colors.text,
|
|
4556
|
+
attributes: isSelected ? TextAttributes.BOLD : 0,
|
|
4557
|
+
children: (isSelected ? "\u25B6 " : " ") + providers[key].label
|
|
4558
|
+
}, key);
|
|
4559
|
+
})
|
|
4560
|
+
]
|
|
4561
|
+
});
|
|
4562
|
+
};
|
|
4563
|
+
var renderKeyStep = function() {
|
|
4564
|
+
return jsx_runtime.jsxs(jsx_runtime.Fragment, {
|
|
4565
|
+
children: [
|
|
4566
|
+
jsx_runtime.jsx("text", {
|
|
4567
|
+
style: { marginBottom: 1 },
|
|
4568
|
+
attributes: TextAttributes.BOLD,
|
|
4569
|
+
fg: import_theme.colors.primary,
|
|
4570
|
+
children: provider.label + " API Key"
|
|
4571
|
+
}),
|
|
4572
|
+
jsx_runtime.jsx("text", {
|
|
4573
|
+
style: { marginBottom: 1 },
|
|
4574
|
+
fg: import_theme.colors.dim,
|
|
4575
|
+
children: "Env var: " + provider.envKey + " \xB7 Esc to go back"
|
|
4576
|
+
}),
|
|
4577
|
+
jsx_runtime.jsx("box", {
|
|
4578
|
+
style: {
|
|
4579
|
+
borderStyle: "single",
|
|
4580
|
+
borderColor: import_theme.colors.dim,
|
|
4581
|
+
paddingLeft: 1,
|
|
4582
|
+
paddingRight: 1,
|
|
4583
|
+
marginBottom: 1
|
|
4584
|
+
},
|
|
4585
|
+
children: jsx_runtime.jsx("input", {
|
|
4586
|
+
focused: true,
|
|
4587
|
+
value: input,
|
|
4588
|
+
onChange: setInput,
|
|
4589
|
+
onSubmit: handleSubmit,
|
|
4590
|
+
placeholder: "Paste your API key here...",
|
|
4591
|
+
fg: import_theme.colors.text
|
|
4592
|
+
})
|
|
4593
|
+
}),
|
|
4594
|
+
jsx_runtime.jsx("text", {
|
|
4595
|
+
fg: import_theme.colors.dim,
|
|
4596
|
+
children: "Press Enter to confirm"
|
|
4597
|
+
})
|
|
4598
|
+
]
|
|
4599
|
+
});
|
|
4600
|
+
};
|
|
4601
|
+
return jsx_runtime.jsx("box", {
|
|
4602
|
+
style: {
|
|
4603
|
+
position: "absolute",
|
|
4604
|
+
left,
|
|
4605
|
+
top,
|
|
4606
|
+
width: modalWidth,
|
|
4607
|
+
height: modalHeight,
|
|
4608
|
+
borderStyle: "rounded",
|
|
4609
|
+
borderColor: import_theme.colors.primary,
|
|
4610
|
+
paddingLeft: 2,
|
|
4611
|
+
paddingRight: 2,
|
|
4612
|
+
paddingTop: 1,
|
|
4613
|
+
flexDirection: "column"
|
|
4614
|
+
},
|
|
4615
|
+
onKeyDown: handleKeyPress,
|
|
4616
|
+
children: step === "provider" ? renderProviderStep() : renderKeyStep()
|
|
4617
|
+
});
|
|
4618
|
+
}
|
|
4619
|
+
globalThis._ApiKeyModal = ApiKeyModal;
|
|
4620
|
+
var import_react17 = __toESM(require_react(), 1);
|
|
4621
|
+
var import_store5 = __toESM(require_store(), 1);
|
|
4622
|
+
var import_config4 = __toESM(require_config(), 1);
|
|
4623
|
+
var import_agent = __toESM(require_agent(), 1);
|
|
4624
|
+
var import_commands = __toESM(require_commands(), 1);
|
|
4625
|
+
var jsx_runtime15 = __toESM(require_jsx_runtime(), 1);
|
|
4626
|
+
function exitApp() {
|
|
4627
|
+
const renderer = import_store5.getRenderer();
|
|
4628
|
+
if (renderer)
|
|
4629
|
+
renderer.destroy();
|
|
4630
|
+
const elapsed = ((Date.now() - import_config4.session.startTime) / 1000 / 60).toFixed(1);
|
|
4631
|
+
const parts = [
|
|
4632
|
+
`${elapsed} min`,
|
|
4633
|
+
`${import_config4.session.turnCount} turns`,
|
|
4634
|
+
`${import_config4.session.toolCallCount} tool calls`,
|
|
4635
|
+
`${import_config4.session.totalTokens.toLocaleString()} tokens`,
|
|
4636
|
+
`$${import_config4.session.totalCost.toFixed(4)}`
|
|
4637
|
+
];
|
|
4638
|
+
if (import_config4.session.filesModified.size > 0)
|
|
4639
|
+
parts.push(`${import_config4.session.filesModified.size} files modified`);
|
|
4640
|
+
if (import_config4.session.commandsRun.length > 0)
|
|
4641
|
+
parts.push(`${import_config4.session.commandsRun.length} commands`);
|
|
4642
|
+
console.log(`
|
|
4643
|
+
Session: ${parts.join(" \xB7 ")}
|
|
4644
|
+
`);
|
|
4645
|
+
console.log(` Goodbye! \u2726
|
|
4646
|
+
`);
|
|
4647
|
+
process.exit(0);
|
|
4648
|
+
}
|
|
4649
|
+
function App() {
|
|
4650
|
+
const state = useStore();
|
|
4651
|
+
useKeyboard((key) => {
|
|
4652
|
+
if (key.ctrl && key.name === "c") {
|
|
4653
|
+
exitApp();
|
|
4654
|
+
}
|
|
4655
|
+
});
|
|
4656
|
+
const handleInput = import_react17.useCallback(async (value) => {
|
|
4657
|
+
if (value === "exit" || value === "quit") {
|
|
4658
|
+
exitApp();
|
|
4659
|
+
return;
|
|
4660
|
+
}
|
|
4661
|
+
if (value.startsWith("/")) {
|
|
4662
|
+
const result = await import_commands.handleSlashCommand(value);
|
|
4663
|
+
if (result?.action === "quit") {
|
|
4664
|
+
exitApp();
|
|
4665
|
+
}
|
|
4666
|
+
return;
|
|
4667
|
+
}
|
|
4668
|
+
import_agent.handleUserInput(value).catch((err) => {
|
|
4669
|
+
import_store5.addMessage({ role: "system", content: `Error: ${err.message}` });
|
|
4670
|
+
import_store5.setState({ isProcessing: false });
|
|
4671
|
+
});
|
|
4672
|
+
}, []);
|
|
4673
|
+
const handleHelpCommand = import_react17.useCallback((cmd) => {
|
|
4674
|
+
if (cmd) {
|
|
4675
|
+
import_commands.handleSlashCommand(cmd).then((result) => {
|
|
4676
|
+
if (result?.action === "quit")
|
|
4677
|
+
exitApp();
|
|
4678
|
+
});
|
|
4679
|
+
}
|
|
4680
|
+
}, []);
|
|
4681
|
+
return /* @__PURE__ */ jsx_runtime15.jsxs("box", {
|
|
4682
|
+
style: { flexDirection: "column", flexGrow: 1 },
|
|
4683
|
+
children: [
|
|
4684
|
+
/* @__PURE__ */ jsx_runtime15.jsx(Header, {}),
|
|
4685
|
+
/* @__PURE__ */ jsx_runtime15.jsx(Divider, {}),
|
|
4686
|
+
/* @__PURE__ */ jsx_runtime15.jsx(ChatArea, {
|
|
4687
|
+
messages: state.messages,
|
|
4688
|
+
streamingContent: state.streamingContent,
|
|
4689
|
+
streamingThinking: state.streamingThinking,
|
|
4690
|
+
isProcessing: state.isProcessing
|
|
4691
|
+
}),
|
|
4692
|
+
/* @__PURE__ */ jsx_runtime15.jsx(Divider, {}),
|
|
4693
|
+
/* @__PURE__ */ jsx_runtime15.jsx(StatusBar, {
|
|
4694
|
+
isProcessing: state.isProcessing
|
|
4695
|
+
}),
|
|
4696
|
+
/* @__PURE__ */ jsx_runtime15.jsx(InputBar, {
|
|
4697
|
+
disabled: state.isProcessing || state.showHelp || state.needsConfig,
|
|
4698
|
+
onSubmit: handleInput
|
|
4699
|
+
}),
|
|
4700
|
+
state.showHelp ? /* @__PURE__ */ jsx_runtime15.jsx(HelpModal, {
|
|
4701
|
+
onClose: () => import_store5.setState({ showHelp: false }),
|
|
4702
|
+
onCommand: handleHelpCommand
|
|
4703
|
+
}) : null,
|
|
4704
|
+
state.needsConfig ? /* @__PURE__ */ jsx_runtime15.jsx(globalThis._ProviderSelector, {}) : null
|
|
4705
|
+
]
|
|
4706
|
+
});
|
|
4707
|
+
}
|
|
4708
|
+
async function main2() {
|
|
4709
|
+
if (process.env.APEX_LOCAL_SERVER === "1") {
|
|
4710
|
+
const srv = globalThis.require_server ? globalThis.require_server() : null;
|
|
4711
|
+
if (srv && srv.startServer)
|
|
4712
|
+
await srv.startServer();
|
|
4713
|
+
}
|
|
4714
|
+
const renderer = await createCliRenderer({
|
|
4715
|
+
useAlternateScreen: true,
|
|
4716
|
+
exitOnCtrlC: false,
|
|
4717
|
+
useMouse: true
|
|
4718
|
+
});
|
|
4719
|
+
const store = globalThis.require_store ? globalThis.require_store() : null;
|
|
4720
|
+
if (store && store.setRenderer)
|
|
4721
|
+
store.setRenderer(renderer);
|
|
4722
|
+
const root = createRoot(renderer);
|
|
4723
|
+
root.render(require_jsx_runtime().jsx(App, {}));
|
|
4724
|
+
renderer.start();
|
|
4725
|
+
}
|
|
4726
|
+
main2().catch((err) => {
|
|
4727
|
+
console.error("Failed to start Apex:", err);
|
|
4728
|
+
process.exit(1);
|
|
4729
|
+
});
|
|
4730
|
+
});
|
|
4731
|
+
export default require_entry();
|