codemaxxing 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -12
- package/dist/agent.d.ts +4 -0
- package/dist/agent.js +91 -16
- package/dist/commands/git.d.ts +2 -0
- package/dist/commands/git.js +50 -0
- package/dist/commands/ollama.d.ts +27 -0
- package/dist/commands/ollama.js +171 -0
- package/dist/commands/output.d.ts +2 -0
- package/dist/commands/output.js +18 -0
- package/dist/commands/registry.d.ts +2 -0
- package/dist/commands/registry.js +8 -0
- package/dist/commands/skills.d.ts +18 -0
- package/dist/commands/skills.js +121 -0
- package/dist/commands/types.d.ts +5 -0
- package/dist/commands/types.js +1 -0
- package/dist/commands/ui.d.ts +16 -0
- package/dist/commands/ui.js +79 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.js +13 -3
- package/dist/exec.js +4 -1
- package/dist/index.js +75 -401
- package/dist/tools/files.js +58 -3
- package/dist/utils/context.js +6 -0
- package/dist/utils/mcp.d.ts +7 -2
- package/dist/utils/mcp.js +34 -6
- package/package.json +8 -5
- package/src/agent.ts +0 -894
- package/src/auth-cli.ts +0 -287
- package/src/cli.ts +0 -37
- package/src/config.ts +0 -352
- package/src/exec.ts +0 -183
- package/src/index.tsx +0 -2647
- package/src/skills/registry.ts +0 -1436
- package/src/themes.ts +0 -335
- package/src/tools/files.ts +0 -374
- package/src/utils/auth.ts +0 -606
- package/src/utils/context.ts +0 -174
- package/src/utils/git.ts +0 -117
- package/src/utils/hardware.ts +0 -131
- package/src/utils/lint.ts +0 -116
- package/src/utils/mcp.ts +0 -307
- package/src/utils/models.ts +0 -218
- package/src/utils/ollama.ts +0 -352
- package/src/utils/repomap.ts +0 -220
- package/src/utils/sessions.ts +0 -254
- package/src/utils/skills.ts +0 -241
- package/tsconfig.json +0 -16
package/src/themes.ts
DELETED
|
@@ -1,335 +0,0 @@
|
|
|
1
|
-
export interface Theme {
|
|
2
|
-
name: string;
|
|
3
|
-
description: string;
|
|
4
|
-
colors: {
|
|
5
|
-
primary: string; // Main accent (borders, highlights)
|
|
6
|
-
secondary: string; // Secondary accent (banner, headings)
|
|
7
|
-
muted: string; // Dimmed text (hints, timestamps)
|
|
8
|
-
text: string; // Normal text
|
|
9
|
-
userInput: string; // User input text
|
|
10
|
-
response: string; // AI response marker
|
|
11
|
-
tool: string; // Tool call text
|
|
12
|
-
toolResult: string; // Tool result text
|
|
13
|
-
error: string; // Error messages
|
|
14
|
-
success: string; // Success messages
|
|
15
|
-
warning: string; // Warning/approval prompts
|
|
16
|
-
spinner: string; // Spinner color
|
|
17
|
-
border: string; // Border color
|
|
18
|
-
suggestion: string; // Highlighted suggestion
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export const THEMES: Record<string, Theme> = {
|
|
23
|
-
"cyberpunk-neon": {
|
|
24
|
-
name: "Cyberpunk Neon",
|
|
25
|
-
description: "Electric cyan & magenta — Night City terminal",
|
|
26
|
-
colors: {
|
|
27
|
-
primary: "#00FFFF",
|
|
28
|
-
secondary: "#FF00FF",
|
|
29
|
-
muted: "#008B8B",
|
|
30
|
-
text: "",
|
|
31
|
-
userInput: "#008B8B",
|
|
32
|
-
response: "#00FFFF",
|
|
33
|
-
tool: "#FF00FF",
|
|
34
|
-
toolResult: "#008B8B",
|
|
35
|
-
error: "red",
|
|
36
|
-
success: "#00FF00",
|
|
37
|
-
warning: "#FF8C00",
|
|
38
|
-
spinner: "#00FFFF",
|
|
39
|
-
border: "#00FFFF",
|
|
40
|
-
suggestion: "#FF00FF",
|
|
41
|
-
},
|
|
42
|
-
},
|
|
43
|
-
dracula: {
|
|
44
|
-
name: "Dracula",
|
|
45
|
-
description: "Dark purple tones",
|
|
46
|
-
colors: {
|
|
47
|
-
primary: "#BD93F9",
|
|
48
|
-
secondary: "#FF79C6",
|
|
49
|
-
muted: "#6272A4",
|
|
50
|
-
text: "#F8F8F2",
|
|
51
|
-
userInput: "#8BE9FD",
|
|
52
|
-
response: "#BD93F9",
|
|
53
|
-
tool: "#FF79C6",
|
|
54
|
-
toolResult: "#6272A4",
|
|
55
|
-
error: "#FF5555",
|
|
56
|
-
success: "#50FA7B",
|
|
57
|
-
warning: "#FFB86C",
|
|
58
|
-
spinner: "#BD93F9",
|
|
59
|
-
border: "#BD93F9",
|
|
60
|
-
suggestion: "#FF79C6",
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
gruvbox: {
|
|
64
|
-
name: "Gruvbox",
|
|
65
|
-
description: "Warm retro tones",
|
|
66
|
-
colors: {
|
|
67
|
-
primary: "#FE8019",
|
|
68
|
-
secondary: "#FABD2F",
|
|
69
|
-
muted: "#928374",
|
|
70
|
-
text: "#EBDBB2",
|
|
71
|
-
userInput: "#83A598",
|
|
72
|
-
response: "#FE8019",
|
|
73
|
-
tool: "#FABD2F",
|
|
74
|
-
toolResult: "#928374",
|
|
75
|
-
error: "#FB4934",
|
|
76
|
-
success: "#B8BB26",
|
|
77
|
-
warning: "#FABD2F",
|
|
78
|
-
spinner: "#FE8019",
|
|
79
|
-
border: "#FE8019",
|
|
80
|
-
suggestion: "#FABD2F",
|
|
81
|
-
},
|
|
82
|
-
},
|
|
83
|
-
nord: {
|
|
84
|
-
name: "Nord",
|
|
85
|
-
description: "Cool arctic blues",
|
|
86
|
-
colors: {
|
|
87
|
-
primary: "#88C0D0",
|
|
88
|
-
secondary: "#81A1C1",
|
|
89
|
-
muted: "#4C566A",
|
|
90
|
-
text: "#ECEFF4",
|
|
91
|
-
userInput: "#88C0D0",
|
|
92
|
-
response: "#81A1C1",
|
|
93
|
-
tool: "#5E81AC",
|
|
94
|
-
toolResult: "#4C566A",
|
|
95
|
-
error: "#BF616A",
|
|
96
|
-
success: "#A3BE8C",
|
|
97
|
-
warning: "#EBCB8B",
|
|
98
|
-
spinner: "#88C0D0",
|
|
99
|
-
border: "#81A1C1",
|
|
100
|
-
suggestion: "#88C0D0",
|
|
101
|
-
},
|
|
102
|
-
},
|
|
103
|
-
mono: {
|
|
104
|
-
name: "Mono",
|
|
105
|
-
description: "Clean monochrome — easy on the eyes",
|
|
106
|
-
colors: {
|
|
107
|
-
primary: "#AAAAAA",
|
|
108
|
-
secondary: "#FFFFFF",
|
|
109
|
-
muted: "#666666",
|
|
110
|
-
text: "#CCCCCC",
|
|
111
|
-
userInput: "#AAAAAA",
|
|
112
|
-
response: "#FFFFFF",
|
|
113
|
-
tool: "#CCCCCC",
|
|
114
|
-
toolResult: "#666666",
|
|
115
|
-
error: "#FF6666",
|
|
116
|
-
success: "#66FF66",
|
|
117
|
-
warning: "#FFAA66",
|
|
118
|
-
spinner: "#AAAAAA",
|
|
119
|
-
border: "#888888",
|
|
120
|
-
suggestion: "#FFFFFF",
|
|
121
|
-
},
|
|
122
|
-
},
|
|
123
|
-
solarized: {
|
|
124
|
-
name: "Solarized",
|
|
125
|
-
description: "Solarized dark",
|
|
126
|
-
colors: {
|
|
127
|
-
primary: "#268BD2",
|
|
128
|
-
secondary: "#2AA198",
|
|
129
|
-
muted: "#586E75",
|
|
130
|
-
text: "#839496",
|
|
131
|
-
userInput: "#2AA198",
|
|
132
|
-
response: "#268BD2",
|
|
133
|
-
tool: "#B58900",
|
|
134
|
-
toolResult: "#586E75",
|
|
135
|
-
error: "#DC322F",
|
|
136
|
-
success: "#859900",
|
|
137
|
-
warning: "#CB4B16",
|
|
138
|
-
spinner: "#268BD2",
|
|
139
|
-
border: "#268BD2",
|
|
140
|
-
suggestion: "#2AA198",
|
|
141
|
-
},
|
|
142
|
-
},
|
|
143
|
-
hacker: {
|
|
144
|
-
name: "Hacker",
|
|
145
|
-
description: "Green on black — classic terminal",
|
|
146
|
-
colors: {
|
|
147
|
-
primary: "#00FF00",
|
|
148
|
-
secondary: "#00CC00",
|
|
149
|
-
muted: "#006600",
|
|
150
|
-
text: "#00DD00",
|
|
151
|
-
userInput: "#00FF00",
|
|
152
|
-
response: "#00FF00",
|
|
153
|
-
tool: "#00CC00",
|
|
154
|
-
toolResult: "#006600",
|
|
155
|
-
error: "#FF0000",
|
|
156
|
-
success: "#00FF00",
|
|
157
|
-
warning: "#FFFF00",
|
|
158
|
-
spinner: "#00FF00",
|
|
159
|
-
border: "#00FF00",
|
|
160
|
-
suggestion: "#00CC00",
|
|
161
|
-
},
|
|
162
|
-
},
|
|
163
|
-
catppuccin: {
|
|
164
|
-
name: "Catppuccin",
|
|
165
|
-
description: "Soothing pastel — Mocha flavor",
|
|
166
|
-
colors: {
|
|
167
|
-
primary: "#CBA6F7",
|
|
168
|
-
secondary: "#F5C2E7",
|
|
169
|
-
muted: "#6C7086",
|
|
170
|
-
text: "#CDD6F4",
|
|
171
|
-
userInput: "#89DCEB",
|
|
172
|
-
response: "#CBA6F7",
|
|
173
|
-
tool: "#F5C2E7",
|
|
174
|
-
toolResult: "#6C7086",
|
|
175
|
-
error: "#F38BA8",
|
|
176
|
-
success: "#A6E3A1",
|
|
177
|
-
warning: "#FAB387",
|
|
178
|
-
spinner: "#CBA6F7",
|
|
179
|
-
border: "#CBA6F7",
|
|
180
|
-
suggestion: "#F5C2E7",
|
|
181
|
-
},
|
|
182
|
-
},
|
|
183
|
-
"tokyo-night": {
|
|
184
|
-
name: "Tokyo Night",
|
|
185
|
-
description: "Rain-soaked Shibuya — neon signs in the dark",
|
|
186
|
-
colors: {
|
|
187
|
-
primary: "#FF7AC6",
|
|
188
|
-
secondary: "#7DCFFF",
|
|
189
|
-
muted: "#3B4261",
|
|
190
|
-
text: "#A9B1D6",
|
|
191
|
-
userInput: "#FF9E64",
|
|
192
|
-
response: "#FF7AC6",
|
|
193
|
-
tool: "#7DCFFF",
|
|
194
|
-
toolResult: "#3B4261",
|
|
195
|
-
error: "#F7768E",
|
|
196
|
-
success: "#73DACA",
|
|
197
|
-
warning: "#FF9E64",
|
|
198
|
-
spinner: "#FF7AC6",
|
|
199
|
-
border: "#FF7AC6",
|
|
200
|
-
suggestion: "#7DCFFF",
|
|
201
|
-
},
|
|
202
|
-
},
|
|
203
|
-
"one-dark": {
|
|
204
|
-
name: "One Dark",
|
|
205
|
-
description: "Atom editor classic",
|
|
206
|
-
colors: {
|
|
207
|
-
primary: "#61AFEF",
|
|
208
|
-
secondary: "#C678DD",
|
|
209
|
-
muted: "#5C6370",
|
|
210
|
-
text: "#ABB2BF",
|
|
211
|
-
userInput: "#56B6C2",
|
|
212
|
-
response: "#61AFEF",
|
|
213
|
-
tool: "#C678DD",
|
|
214
|
-
toolResult: "#5C6370",
|
|
215
|
-
error: "#E06C75",
|
|
216
|
-
success: "#98C379",
|
|
217
|
-
warning: "#E5C07B",
|
|
218
|
-
spinner: "#61AFEF",
|
|
219
|
-
border: "#61AFEF",
|
|
220
|
-
suggestion: "#C678DD",
|
|
221
|
-
},
|
|
222
|
-
},
|
|
223
|
-
"rose-pine": {
|
|
224
|
-
name: "Rosé Pine",
|
|
225
|
-
description: "Elegant dark florals",
|
|
226
|
-
colors: {
|
|
227
|
-
primary: "#EBBCBA",
|
|
228
|
-
secondary: "#C4A7E7",
|
|
229
|
-
muted: "#6E6A86",
|
|
230
|
-
text: "#E0DEF4",
|
|
231
|
-
userInput: "#9CCFD8",
|
|
232
|
-
response: "#EBBCBA",
|
|
233
|
-
tool: "#C4A7E7",
|
|
234
|
-
toolResult: "#6E6A86",
|
|
235
|
-
error: "#EB6F92",
|
|
236
|
-
success: "#31748F",
|
|
237
|
-
warning: "#F6C177",
|
|
238
|
-
spinner: "#EBBCBA",
|
|
239
|
-
border: "#EBBCBA",
|
|
240
|
-
suggestion: "#C4A7E7",
|
|
241
|
-
},
|
|
242
|
-
},
|
|
243
|
-
|
|
244
|
-
synthwave: {
|
|
245
|
-
name: "Synthwave",
|
|
246
|
-
description: "Retro 80s sunset — outrun aesthetics",
|
|
247
|
-
colors: {
|
|
248
|
-
primary: "#F92AAD",
|
|
249
|
-
secondary: "#E9F501",
|
|
250
|
-
muted: "#614D85",
|
|
251
|
-
text: "#F4EEFF",
|
|
252
|
-
userInput: "#36F9F6",
|
|
253
|
-
response: "#F92AAD",
|
|
254
|
-
tool: "#E9F501",
|
|
255
|
-
toolResult: "#614D85",
|
|
256
|
-
error: "#FE4450",
|
|
257
|
-
success: "#72F1B8",
|
|
258
|
-
warning: "#FF7F11",
|
|
259
|
-
spinner: "#F92AAD",
|
|
260
|
-
border: "#F92AAD",
|
|
261
|
-
suggestion: "#36F9F6",
|
|
262
|
-
},
|
|
263
|
-
},
|
|
264
|
-
"blood-moon": {
|
|
265
|
-
name: "Blood Moon",
|
|
266
|
-
description: "Dark crimson — for the night coders",
|
|
267
|
-
colors: {
|
|
268
|
-
primary: "#FF4444",
|
|
269
|
-
secondary: "#CC2936",
|
|
270
|
-
muted: "#5C2626",
|
|
271
|
-
text: "#E0C4C4",
|
|
272
|
-
userInput: "#FF6B6B",
|
|
273
|
-
response: "#FF4444",
|
|
274
|
-
tool: "#CC2936",
|
|
275
|
-
toolResult: "#5C2626",
|
|
276
|
-
error: "#FF0000",
|
|
277
|
-
success: "#4CAF50",
|
|
278
|
-
warning: "#FF8C00",
|
|
279
|
-
spinner: "#FF4444",
|
|
280
|
-
border: "#FF4444",
|
|
281
|
-
suggestion: "#CC2936",
|
|
282
|
-
},
|
|
283
|
-
},
|
|
284
|
-
|
|
285
|
-
"hot-dog": {
|
|
286
|
-
name: "Hot Dog",
|
|
287
|
-
description: "Cursed ketchup & mustard — you asked for it",
|
|
288
|
-
colors: {
|
|
289
|
-
primary: "#FF0000",
|
|
290
|
-
secondary: "#FFFF00",
|
|
291
|
-
muted: "#AA6600",
|
|
292
|
-
text: "#FFFF00",
|
|
293
|
-
userInput: "#FF0000",
|
|
294
|
-
response: "#FFFF00",
|
|
295
|
-
tool: "#FF6600",
|
|
296
|
-
toolResult: "#AA6600",
|
|
297
|
-
error: "#FF0000",
|
|
298
|
-
success: "#00FF00",
|
|
299
|
-
warning: "#FFFF00",
|
|
300
|
-
spinner: "#FF0000",
|
|
301
|
-
border: "#FF0000",
|
|
302
|
-
suggestion: "#FFFF00",
|
|
303
|
-
},
|
|
304
|
-
},
|
|
305
|
-
acid: {
|
|
306
|
-
name: "Acid",
|
|
307
|
-
description: "Every color at once — sensory overload",
|
|
308
|
-
colors: {
|
|
309
|
-
primary: "#FF00FF",
|
|
310
|
-
secondary: "#00FF88",
|
|
311
|
-
muted: "#FF6B00",
|
|
312
|
-
text: "#FFFF00",
|
|
313
|
-
userInput: "#00FFFF",
|
|
314
|
-
response: "#FF3399",
|
|
315
|
-
tool: "#33FF00",
|
|
316
|
-
toolResult: "#FF6B00",
|
|
317
|
-
error: "#FF0044",
|
|
318
|
-
success: "#00FF66",
|
|
319
|
-
warning: "#FFD700",
|
|
320
|
-
spinner: "#BF00FF",
|
|
321
|
-
border: "#FF00AA",
|
|
322
|
-
suggestion: "#00FFCC",
|
|
323
|
-
},
|
|
324
|
-
},
|
|
325
|
-
};
|
|
326
|
-
|
|
327
|
-
export const DEFAULT_THEME = "nord";
|
|
328
|
-
|
|
329
|
-
export function getTheme(name: string): Theme {
|
|
330
|
-
return THEMES[name] ?? THEMES[DEFAULT_THEME];
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
export function listThemes(): string[] {
|
|
334
|
-
return Object.keys(THEMES);
|
|
335
|
-
}
|
package/src/tools/files.ts
DELETED
|
@@ -1,374 +0,0 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, existsSync, readdirSync, statSync } from "fs";
|
|
2
|
-
import { join, relative } from "path";
|
|
3
|
-
import type { ChatCompletionTool } from "openai/resources/chat/completions";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Tool definitions for the OpenAI function calling API
|
|
7
|
-
*/
|
|
8
|
-
export const FILE_TOOLS: ChatCompletionTool[] = [
|
|
9
|
-
{
|
|
10
|
-
type: "function",
|
|
11
|
-
function: {
|
|
12
|
-
name: "read_file",
|
|
13
|
-
description: "Read the contents of a file at the given path",
|
|
14
|
-
parameters: {
|
|
15
|
-
type: "object",
|
|
16
|
-
properties: {
|
|
17
|
-
path: {
|
|
18
|
-
type: "string",
|
|
19
|
-
description: "Path to the file to read (relative to project root)",
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
required: ["path"],
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
type: "function",
|
|
28
|
-
function: {
|
|
29
|
-
name: "write_file",
|
|
30
|
-
description:
|
|
31
|
-
"Write content to a file. Creates the file if it doesn't exist, overwrites if it does.",
|
|
32
|
-
parameters: {
|
|
33
|
-
type: "object",
|
|
34
|
-
properties: {
|
|
35
|
-
path: {
|
|
36
|
-
type: "string",
|
|
37
|
-
description: "Path to the file to write (relative to project root)",
|
|
38
|
-
},
|
|
39
|
-
content: {
|
|
40
|
-
type: "string",
|
|
41
|
-
description: "Content to write to the file",
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
required: ["path", "content"],
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
type: "function",
|
|
50
|
-
function: {
|
|
51
|
-
name: "list_files",
|
|
52
|
-
description:
|
|
53
|
-
"List files and directories in the given path. Returns file names and types.",
|
|
54
|
-
parameters: {
|
|
55
|
-
type: "object",
|
|
56
|
-
properties: {
|
|
57
|
-
path: {
|
|
58
|
-
type: "string",
|
|
59
|
-
description:
|
|
60
|
-
"Directory path to list (relative to project root, defaults to '.')",
|
|
61
|
-
},
|
|
62
|
-
recursive: {
|
|
63
|
-
type: "boolean",
|
|
64
|
-
description: "Whether to list files recursively (default: false)",
|
|
65
|
-
},
|
|
66
|
-
},
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
type: "function",
|
|
72
|
-
function: {
|
|
73
|
-
name: "search_files",
|
|
74
|
-
description: "Search for a text pattern across files in the project",
|
|
75
|
-
parameters: {
|
|
76
|
-
type: "object",
|
|
77
|
-
properties: {
|
|
78
|
-
pattern: {
|
|
79
|
-
type: "string",
|
|
80
|
-
description: "Text or regex pattern to search for",
|
|
81
|
-
},
|
|
82
|
-
path: {
|
|
83
|
-
type: "string",
|
|
84
|
-
description: "Directory to search in (defaults to project root)",
|
|
85
|
-
},
|
|
86
|
-
},
|
|
87
|
-
required: ["pattern"],
|
|
88
|
-
},
|
|
89
|
-
},
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
type: "function",
|
|
93
|
-
function: {
|
|
94
|
-
name: "run_command",
|
|
95
|
-
description:
|
|
96
|
-
"Execute a shell command and return the output. Use for running tests, builds, linters, etc.",
|
|
97
|
-
parameters: {
|
|
98
|
-
type: "object",
|
|
99
|
-
properties: {
|
|
100
|
-
command: {
|
|
101
|
-
type: "string",
|
|
102
|
-
description: "Shell command to execute",
|
|
103
|
-
},
|
|
104
|
-
},
|
|
105
|
-
required: ["command"],
|
|
106
|
-
},
|
|
107
|
-
},
|
|
108
|
-
},
|
|
109
|
-
];
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Execute a tool call and return the result
|
|
113
|
-
*/
|
|
114
|
-
export async function executeTool(
|
|
115
|
-
name: string,
|
|
116
|
-
args: Record<string, unknown>,
|
|
117
|
-
cwd: string
|
|
118
|
-
): Promise<string> {
|
|
119
|
-
switch (name) {
|
|
120
|
-
case "read_file": {
|
|
121
|
-
const filePath = join(cwd, args.path as string);
|
|
122
|
-
if (!existsSync(filePath)) return `Error: File not found: ${args.path}`;
|
|
123
|
-
try {
|
|
124
|
-
return readFileSync(filePath, "utf-8");
|
|
125
|
-
} catch (e) {
|
|
126
|
-
return `Error reading file: ${e}`;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
case "write_file": {
|
|
131
|
-
const filePath = join(cwd, args.path as string);
|
|
132
|
-
try {
|
|
133
|
-
writeFileSync(filePath, args.content as string, "utf-8");
|
|
134
|
-
return `✅ Wrote ${(args.content as string).length} bytes to ${args.path}`;
|
|
135
|
-
} catch (e) {
|
|
136
|
-
return `Error writing file: ${e}`;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
case "list_files": {
|
|
141
|
-
const dirPath = join(cwd, (args.path as string) || ".");
|
|
142
|
-
if (!existsSync(dirPath)) return `Error: Directory not found: ${args.path}`;
|
|
143
|
-
try {
|
|
144
|
-
const entries = listDir(dirPath, cwd, args.recursive as boolean);
|
|
145
|
-
return entries.join("\n");
|
|
146
|
-
} catch (e) {
|
|
147
|
-
return `Error listing files: ${e}`;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
case "search_files": {
|
|
152
|
-
const searchPath = join(cwd, (args.path as string) || ".");
|
|
153
|
-
try {
|
|
154
|
-
return searchInFiles(searchPath, args.pattern as string, cwd);
|
|
155
|
-
} catch (e) {
|
|
156
|
-
return `Error searching: ${e}`;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
case "run_command": {
|
|
161
|
-
try {
|
|
162
|
-
const { execSync } = await import("child_process");
|
|
163
|
-
const output = execSync(args.command as string, {
|
|
164
|
-
cwd,
|
|
165
|
-
encoding: "utf-8",
|
|
166
|
-
timeout: 30000,
|
|
167
|
-
maxBuffer: 1024 * 1024,
|
|
168
|
-
});
|
|
169
|
-
return output || "(no output)";
|
|
170
|
-
} catch (e: any) {
|
|
171
|
-
return `Command failed: ${e.stderr || e.message}`;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
default:
|
|
176
|
-
return `Unknown tool: ${name}`;
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Generate a simple unified diff between two strings
|
|
182
|
-
*/
|
|
183
|
-
export function generateDiff(oldContent: string, newContent: string, filePath: string): string {
|
|
184
|
-
const oldLines = oldContent.split("\n");
|
|
185
|
-
const newLines = newContent.split("\n");
|
|
186
|
-
const output: string[] = [`--- a/${filePath}`, `+++ b/${filePath}`];
|
|
187
|
-
|
|
188
|
-
const lcs = computeLCS(oldLines, newLines);
|
|
189
|
-
|
|
190
|
-
let oi = 0, ni = 0, li = 0;
|
|
191
|
-
let hunkLines: string[] = [];
|
|
192
|
-
let hunkOldCount = 0;
|
|
193
|
-
let hunkNewCount = 0;
|
|
194
|
-
let hunkStartOld = 1;
|
|
195
|
-
let hunkStartNew = 1;
|
|
196
|
-
let pendingContext: string[] = [];
|
|
197
|
-
let hasHunk = false;
|
|
198
|
-
|
|
199
|
-
function flushHunk() {
|
|
200
|
-
if (hasHunk && hunkLines.length > 0) {
|
|
201
|
-
output.push(`@@ -${hunkStartOld},${hunkOldCount} +${hunkStartNew},${hunkNewCount} @@`);
|
|
202
|
-
output.push(...hunkLines);
|
|
203
|
-
}
|
|
204
|
-
hunkLines = [];
|
|
205
|
-
hunkOldCount = 0;
|
|
206
|
-
hunkNewCount = 0;
|
|
207
|
-
hasHunk = false;
|
|
208
|
-
pendingContext = [];
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
function startHunk() {
|
|
212
|
-
if (!hasHunk) {
|
|
213
|
-
hasHunk = true;
|
|
214
|
-
hunkStartOld = Math.max(1, oi + 1 - 3);
|
|
215
|
-
hunkStartNew = Math.max(1, ni + 1 - 3);
|
|
216
|
-
const contextStart = Math.max(0, oi - 3);
|
|
217
|
-
for (let c = contextStart; c < oi; c++) {
|
|
218
|
-
hunkLines.push(` ${oldLines[c]}`);
|
|
219
|
-
hunkOldCount++;
|
|
220
|
-
hunkNewCount++;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
if (pendingContext.length > 0) {
|
|
224
|
-
hunkLines.push(...pendingContext);
|
|
225
|
-
pendingContext = [];
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
while (oi < oldLines.length || ni < newLines.length) {
|
|
230
|
-
if (li < lcs.length && oi < oldLines.length && ni < newLines.length &&
|
|
231
|
-
oldLines[oi] === lcs[li] && newLines[ni] === lcs[li]) {
|
|
232
|
-
// Matching line
|
|
233
|
-
if (hasHunk) {
|
|
234
|
-
pendingContext.push(` ${oldLines[oi]}`);
|
|
235
|
-
hunkOldCount++;
|
|
236
|
-
hunkNewCount++;
|
|
237
|
-
if (pendingContext.length > 6) flushHunk();
|
|
238
|
-
}
|
|
239
|
-
oi++; ni++; li++;
|
|
240
|
-
} else if (oi < oldLines.length && (li >= lcs.length || oldLines[oi] !== lcs[li])) {
|
|
241
|
-
startHunk();
|
|
242
|
-
hunkLines.push(`-${oldLines[oi]}`);
|
|
243
|
-
hunkOldCount++;
|
|
244
|
-
oi++;
|
|
245
|
-
} else if (ni < newLines.length && (li >= lcs.length || newLines[ni] !== lcs[li])) {
|
|
246
|
-
startHunk();
|
|
247
|
-
hunkLines.push(`+${newLines[ni]}`);
|
|
248
|
-
hunkNewCount++;
|
|
249
|
-
ni++;
|
|
250
|
-
} else {
|
|
251
|
-
break;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
flushHunk();
|
|
256
|
-
|
|
257
|
-
if (output.length <= 2) return "(no changes)";
|
|
258
|
-
|
|
259
|
-
const maxDiffLines = 60;
|
|
260
|
-
if (output.length > maxDiffLines + 2) {
|
|
261
|
-
return output.slice(0, maxDiffLines + 2).join("\n") + `\n... (${output.length - maxDiffLines - 2} more lines)`;
|
|
262
|
-
}
|
|
263
|
-
return output.join("\n");
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
function computeLCS(a: string[], b: string[]): string[] {
|
|
267
|
-
if (a.length > 500 || b.length > 500) {
|
|
268
|
-
// For large files, just return common lines in order
|
|
269
|
-
const result: string[] = [];
|
|
270
|
-
let bi = 0;
|
|
271
|
-
for (const line of a) {
|
|
272
|
-
while (bi < b.length && b[bi] !== line) bi++;
|
|
273
|
-
if (bi < b.length) { result.push(line); bi++; }
|
|
274
|
-
}
|
|
275
|
-
return result;
|
|
276
|
-
}
|
|
277
|
-
const m = a.length, n = b.length;
|
|
278
|
-
const dp: number[][] = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
|
|
279
|
-
for (let i = 1; i <= m; i++) {
|
|
280
|
-
for (let j = 1; j <= n; j++) {
|
|
281
|
-
dp[i][j] = a[i - 1] === b[j - 1] ? dp[i - 1][j - 1] + 1 : Math.max(dp[i - 1][j], dp[i][j - 1]);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
const result: string[] = [];
|
|
285
|
-
let i = m, j = n;
|
|
286
|
-
while (i > 0 && j > 0) {
|
|
287
|
-
if (a[i - 1] === b[j - 1]) { result.unshift(a[i - 1]); i--; j--; }
|
|
288
|
-
else if (dp[i - 1][j] > dp[i][j - 1]) { i--; }
|
|
289
|
-
else { j--; }
|
|
290
|
-
}
|
|
291
|
-
return result;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
/**
|
|
295
|
-
* Get existing file content for diff preview (returns null if file doesn't exist)
|
|
296
|
-
*/
|
|
297
|
-
export function getExistingContent(filePath: string, cwd: string): string | null {
|
|
298
|
-
const fullPath = join(cwd, filePath);
|
|
299
|
-
if (!existsSync(fullPath)) return null;
|
|
300
|
-
try {
|
|
301
|
-
return readFileSync(fullPath, "utf-8");
|
|
302
|
-
} catch {
|
|
303
|
-
return null;
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
function listDir(
|
|
308
|
-
dirPath: string,
|
|
309
|
-
cwd: string,
|
|
310
|
-
recursive: boolean = false,
|
|
311
|
-
depth: number = 0
|
|
312
|
-
): string[] {
|
|
313
|
-
const entries: string[] = [];
|
|
314
|
-
const IGNORE = ["node_modules", ".git", "dist", ".next", "__pycache__"];
|
|
315
|
-
|
|
316
|
-
for (const entry of readdirSync(dirPath)) {
|
|
317
|
-
if (IGNORE.includes(entry)) continue;
|
|
318
|
-
const fullPath = join(dirPath, entry);
|
|
319
|
-
const rel = relative(cwd, fullPath);
|
|
320
|
-
const stat = statSync(fullPath);
|
|
321
|
-
const prefix = " ".repeat(depth);
|
|
322
|
-
|
|
323
|
-
if (stat.isDirectory()) {
|
|
324
|
-
entries.push(`${prefix}📁 ${rel}/`);
|
|
325
|
-
if (recursive && depth < 3) {
|
|
326
|
-
entries.push(...listDir(fullPath, cwd, true, depth + 1));
|
|
327
|
-
}
|
|
328
|
-
} else {
|
|
329
|
-
const size = stat.size > 1024 ? `${(stat.size / 1024).toFixed(1)}KB` : `${stat.size}B`;
|
|
330
|
-
entries.push(`${prefix}📄 ${rel} (${size})`);
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
return entries;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
function searchInFiles(
|
|
337
|
-
dirPath: string,
|
|
338
|
-
pattern: string,
|
|
339
|
-
cwd: string
|
|
340
|
-
): string {
|
|
341
|
-
const results: string[] = [];
|
|
342
|
-
const IGNORE = ["node_modules", ".git", "dist", ".next", "__pycache__"];
|
|
343
|
-
const regex = new RegExp(pattern, "gi");
|
|
344
|
-
|
|
345
|
-
function search(dir: string) {
|
|
346
|
-
for (const entry of readdirSync(dir)) {
|
|
347
|
-
if (IGNORE.includes(entry)) continue;
|
|
348
|
-
const fullPath = join(dir, entry);
|
|
349
|
-
const stat = statSync(fullPath);
|
|
350
|
-
|
|
351
|
-
if (stat.isDirectory()) {
|
|
352
|
-
search(fullPath);
|
|
353
|
-
} else if (stat.size < 100000) {
|
|
354
|
-
try {
|
|
355
|
-
const content = readFileSync(fullPath, "utf-8");
|
|
356
|
-
const lines = content.split("\n");
|
|
357
|
-
for (let i = 0; i < lines.length; i++) {
|
|
358
|
-
if (regex.test(lines[i])) {
|
|
359
|
-
results.push(`${relative(cwd, fullPath)}:${i + 1}: ${lines[i].trim()}`);
|
|
360
|
-
}
|
|
361
|
-
regex.lastIndex = 0;
|
|
362
|
-
}
|
|
363
|
-
} catch {
|
|
364
|
-
// skip binary files
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
search(dirPath);
|
|
371
|
-
return results.length > 0
|
|
372
|
-
? results.slice(0, 50).join("\n")
|
|
373
|
-
: "No matches found.";
|
|
374
|
-
}
|