@ridit/lens 0.1.5 → 0.1.7
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/dist/{index.cjs → index.mjs} +917 -66
- package/package.json +5 -6
- package/skills.json +7 -0
- package/src/components/chat/ChatMessage.tsx +6 -0
- package/src/components/chat/ChatOverlays.tsx +28 -1
- package/src/components/chat/ChatRunner.tsx +211 -66
- package/src/index.tsx +60 -60
- package/src/types/chat.ts +18 -1
- package/src/utils/chat.ts +737 -24
- package/src/utils/thinking.tsx +20 -1
- package/tsconfig.json +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ridit/lens",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "Know Your Codebase.",
|
|
5
5
|
"author": "Ridit Jangra <riditjangra09@gmail.com> (https://ridit.space)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -8,17 +8,16 @@
|
|
|
8
8
|
"type": "git",
|
|
9
9
|
"url": "https://github.com/ridit-jangra/Lens"
|
|
10
10
|
},
|
|
11
|
-
"main": "dist/index.
|
|
11
|
+
"main": "dist/index.mjs",
|
|
12
12
|
"bin": {
|
|
13
|
-
"lens": "./dist/index.
|
|
13
|
+
"lens": "./dist/index.mjs"
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
|
-
"build": "bun build src/index.tsx --target node --outfile dist/index.
|
|
17
|
-
"postbuild": "node -e \"const fs=require('fs');const f='dist/index.
|
|
16
|
+
"build": "bun build src/index.tsx --target node --outfile dist/index.mjs",
|
|
17
|
+
"postbuild": "node -e \"const fs=require('fs');const f='dist/index.mjs';fs.writeFileSync(f,'#!/usr/bin/env node\\n'+fs.readFileSync(f,'utf8'))\"",
|
|
18
18
|
"prepublishOnly": "npm run build"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@ridit/lens": "^0.1.4",
|
|
22
21
|
"chalk": "^5.6.2",
|
|
23
22
|
"commander": "^14.0.3",
|
|
24
23
|
"figures": "^6.1.0",
|
package/skills.json
ADDED
|
@@ -116,6 +116,12 @@ export function StaticMessage({ msg }: { msg: Message }) {
|
|
|
116
116
|
shell: "$",
|
|
117
117
|
fetch: "~>",
|
|
118
118
|
"read-file": "r",
|
|
119
|
+
"read-folder": "d",
|
|
120
|
+
grep: "/",
|
|
121
|
+
"delete-file": "x",
|
|
122
|
+
"delete-folder": "X",
|
|
123
|
+
"open-url": "↗",
|
|
124
|
+
"generate-pdf": "P",
|
|
119
125
|
"write-file": "w",
|
|
120
126
|
search: "?",
|
|
121
127
|
};
|
|
@@ -48,6 +48,30 @@ export function PermissionPrompt({
|
|
|
48
48
|
icon = "r";
|
|
49
49
|
label = "read";
|
|
50
50
|
value = tool.filePath;
|
|
51
|
+
} else if (tool.type === "read-folder") {
|
|
52
|
+
icon = "d";
|
|
53
|
+
label = "folder";
|
|
54
|
+
value = tool.folderPath;
|
|
55
|
+
} else if (tool.type === "grep") {
|
|
56
|
+
icon = "/";
|
|
57
|
+
label = "grep";
|
|
58
|
+
value = `${tool.pattern} ${tool.glob}`;
|
|
59
|
+
} else if (tool.type === "delete-file") {
|
|
60
|
+
icon = "x";
|
|
61
|
+
label = "delete";
|
|
62
|
+
value = tool.filePath;
|
|
63
|
+
} else if (tool.type === "delete-folder") {
|
|
64
|
+
icon = "X";
|
|
65
|
+
label = "delete folder";
|
|
66
|
+
value = tool.folderPath;
|
|
67
|
+
} else if (tool.type === "open-url") {
|
|
68
|
+
icon = "↗";
|
|
69
|
+
label = "open";
|
|
70
|
+
value = tool.url;
|
|
71
|
+
} else if (tool.type === "generate-pdf") {
|
|
72
|
+
icon = "P";
|
|
73
|
+
label = "pdf";
|
|
74
|
+
value = tool.filePath;
|
|
51
75
|
} else if (tool.type === "write-file") {
|
|
52
76
|
icon = "w";
|
|
53
77
|
label = "write";
|
|
@@ -128,12 +152,15 @@ export function TypewriterText({
|
|
|
128
152
|
return <Text color={color}>{displayed}</Text>;
|
|
129
153
|
}
|
|
130
154
|
|
|
131
|
-
export function ShortcutBar() {
|
|
155
|
+
export function ShortcutBar({ autoApprove }: { autoApprove?: boolean }) {
|
|
132
156
|
return (
|
|
133
157
|
<Box gap={3} marginTop={0}>
|
|
134
158
|
<Text color="gray" dimColor>
|
|
135
159
|
enter send · ^v paste · ^c exit
|
|
136
160
|
</Text>
|
|
161
|
+
<Text color={autoApprove ? "green" : "gray"} dimColor={!autoApprove}>
|
|
162
|
+
{autoApprove ? "⚡ auto" : "/auto"}
|
|
163
|
+
</Text>
|
|
137
164
|
</Box>
|
|
138
165
|
);
|
|
139
166
|
}
|
|
@@ -20,6 +20,12 @@ import {
|
|
|
20
20
|
runShell,
|
|
21
21
|
fetchUrl,
|
|
22
22
|
readFile,
|
|
23
|
+
readFolder,
|
|
24
|
+
grepFiles,
|
|
25
|
+
deleteFile,
|
|
26
|
+
deleteFolder,
|
|
27
|
+
openUrl,
|
|
28
|
+
generatePdf,
|
|
23
29
|
writeFile,
|
|
24
30
|
buildSystemPrompt,
|
|
25
31
|
parseResponse,
|
|
@@ -55,6 +61,7 @@ const COMMANDS = [
|
|
|
55
61
|
{ cmd: "/timeline", desc: "browse commit history" },
|
|
56
62
|
{ cmd: "/clear history", desc: "wipe session memory for this repo" },
|
|
57
63
|
{ cmd: "/review", desc: "review current codebsae" },
|
|
64
|
+
{ cmd: "/auto", desc: "toggle auto-approve for read/search tools" },
|
|
58
65
|
];
|
|
59
66
|
|
|
60
67
|
function CommandPalette({
|
|
@@ -98,6 +105,11 @@ export const ChatRunner = ({ repoPath }: { repoPath: string }) => {
|
|
|
98
105
|
const [clonedUrls, setClonedUrls] = useState<Set<string>>(new Set());
|
|
99
106
|
const [showTimeline, setShowTimeline] = useState(false);
|
|
100
107
|
const [showReview, setShowReview] = useState(false);
|
|
108
|
+
const [autoApprove, setAutoApprove] = useState(false);
|
|
109
|
+
|
|
110
|
+
// Cache of tool results within a single conversation turn to prevent
|
|
111
|
+
// the model from re-calling tools it already ran with the same args
|
|
112
|
+
const toolResultCache = useRef<Map<string, string>>(new Map());
|
|
101
113
|
|
|
102
114
|
const inputBuffer = useRef("");
|
|
103
115
|
const flushTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
@@ -169,7 +181,13 @@ export const ChatRunner = ({ repoPath }: { repoPath: string }) => {
|
|
|
169
181
|
parsed.kind === "shell" ||
|
|
170
182
|
parsed.kind === "fetch" ||
|
|
171
183
|
parsed.kind === "read-file" ||
|
|
184
|
+
parsed.kind === "read-folder" ||
|
|
185
|
+
parsed.kind === "grep" ||
|
|
172
186
|
parsed.kind === "write-file" ||
|
|
187
|
+
parsed.kind === "delete-file" ||
|
|
188
|
+
parsed.kind === "delete-folder" ||
|
|
189
|
+
parsed.kind === "open-url" ||
|
|
190
|
+
parsed.kind === "generate-pdf" ||
|
|
173
191
|
parsed.kind === "search"
|
|
174
192
|
) {
|
|
175
193
|
let tool: Parameters<typeof PermissionPrompt>[0]["tool"];
|
|
@@ -179,6 +197,22 @@ export const ChatRunner = ({ repoPath }: { repoPath: string }) => {
|
|
|
179
197
|
tool = { type: "fetch", url: parsed.url };
|
|
180
198
|
} else if (parsed.kind === "read-file") {
|
|
181
199
|
tool = { type: "read-file", filePath: parsed.filePath };
|
|
200
|
+
} else if (parsed.kind === "read-folder") {
|
|
201
|
+
tool = { type: "read-folder", folderPath: parsed.folderPath };
|
|
202
|
+
} else if (parsed.kind === "grep") {
|
|
203
|
+
tool = { type: "grep", pattern: parsed.pattern, glob: parsed.glob };
|
|
204
|
+
} else if (parsed.kind === "delete-file") {
|
|
205
|
+
tool = { type: "delete-file", filePath: parsed.filePath };
|
|
206
|
+
} else if (parsed.kind === "delete-folder") {
|
|
207
|
+
tool = { type: "delete-folder", folderPath: parsed.folderPath };
|
|
208
|
+
} else if (parsed.kind === "open-url") {
|
|
209
|
+
tool = { type: "open-url", url: parsed.url };
|
|
210
|
+
} else if (parsed.kind === "generate-pdf") {
|
|
211
|
+
tool = {
|
|
212
|
+
type: "generate-pdf",
|
|
213
|
+
filePath: parsed.filePath,
|
|
214
|
+
content: parsed.pdfContent,
|
|
215
|
+
};
|
|
182
216
|
} else if (parsed.kind === "search") {
|
|
183
217
|
tool = { type: "search", query: parsed.query };
|
|
184
218
|
} else {
|
|
@@ -199,13 +233,34 @@ export const ChatRunner = ({ repoPath }: { repoPath: string }) => {
|
|
|
199
233
|
setCommitted((prev) => [...prev, preambleMsg]);
|
|
200
234
|
}
|
|
201
235
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
236
|
+
// Safe tools that can be auto-approved (no side effects)
|
|
237
|
+
const isSafeTool =
|
|
238
|
+
parsed.kind === "read-file" ||
|
|
239
|
+
parsed.kind === "read-folder" ||
|
|
240
|
+
parsed.kind === "grep" ||
|
|
241
|
+
parsed.kind === "fetch" ||
|
|
242
|
+
parsed.kind === "open-url" ||
|
|
243
|
+
parsed.kind === "search";
|
|
244
|
+
|
|
245
|
+
const executeAndContinue = async (approved: boolean) => {
|
|
246
|
+
let result = "(denied by user)";
|
|
247
|
+
if (approved) {
|
|
248
|
+
// Build a cache key for idempotent read-only tools
|
|
249
|
+
const cacheKey =
|
|
250
|
+
parsed.kind === "read-file"
|
|
251
|
+
? `read-file:${parsed.filePath}`
|
|
252
|
+
: parsed.kind === "read-folder"
|
|
253
|
+
? `read-folder:${parsed.folderPath}`
|
|
254
|
+
: parsed.kind === "grep"
|
|
255
|
+
? `grep:${parsed.pattern}:${parsed.glob}`
|
|
256
|
+
: null;
|
|
257
|
+
|
|
258
|
+
if (cacheKey && toolResultCache.current.has(cacheKey)) {
|
|
259
|
+
// Return cached result with a note so the model stops retrying
|
|
260
|
+
result =
|
|
261
|
+
toolResultCache.current.get(cacheKey)! +
|
|
262
|
+
"\n\n[NOTE: This result was already retrieved earlier. Do not request it again.]";
|
|
263
|
+
} else {
|
|
209
264
|
try {
|
|
210
265
|
setStage({ type: "thinking" });
|
|
211
266
|
if (parsed.kind === "shell") {
|
|
@@ -214,6 +269,22 @@ export const ChatRunner = ({ repoPath }: { repoPath: string }) => {
|
|
|
214
269
|
result = await fetchUrl(parsed.url);
|
|
215
270
|
} else if (parsed.kind === "read-file") {
|
|
216
271
|
result = readFile(parsed.filePath, repoPath);
|
|
272
|
+
} else if (parsed.kind === "read-folder") {
|
|
273
|
+
result = readFolder(parsed.folderPath, repoPath);
|
|
274
|
+
} else if (parsed.kind === "grep") {
|
|
275
|
+
result = grepFiles(parsed.pattern, parsed.glob, repoPath);
|
|
276
|
+
} else if (parsed.kind === "delete-file") {
|
|
277
|
+
result = deleteFile(parsed.filePath, repoPath);
|
|
278
|
+
} else if (parsed.kind === "delete-folder") {
|
|
279
|
+
result = deleteFolder(parsed.folderPath, repoPath);
|
|
280
|
+
} else if (parsed.kind === "open-url") {
|
|
281
|
+
result = openUrl(parsed.url);
|
|
282
|
+
} else if (parsed.kind === "generate-pdf") {
|
|
283
|
+
result = generatePdf(
|
|
284
|
+
parsed.filePath,
|
|
285
|
+
parsed.pdfContent,
|
|
286
|
+
repoPath,
|
|
287
|
+
);
|
|
217
288
|
} else if (parsed.kind === "write-file") {
|
|
218
289
|
result = writeFile(
|
|
219
290
|
parsed.filePath,
|
|
@@ -223,72 +294,130 @@ export const ChatRunner = ({ repoPath }: { repoPath: string }) => {
|
|
|
223
294
|
} else if (parsed.kind === "search") {
|
|
224
295
|
result = await searchWeb(parsed.query);
|
|
225
296
|
}
|
|
297
|
+
// Store result in cache for cacheable tools
|
|
298
|
+
if (cacheKey) {
|
|
299
|
+
toolResultCache.current.set(cacheKey, result);
|
|
300
|
+
}
|
|
226
301
|
} catch (err: unknown) {
|
|
227
302
|
result = `Error: ${err instanceof Error ? err.message : "failed"}`;
|
|
228
303
|
}
|
|
229
304
|
}
|
|
305
|
+
}
|
|
230
306
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
const toolName =
|
|
255
|
-
parsed.kind === "shell"
|
|
256
|
-
? "shell"
|
|
257
|
-
: parsed.kind === "fetch"
|
|
258
|
-
? "fetch"
|
|
259
|
-
: parsed.kind === "read-file"
|
|
260
|
-
? "read-file"
|
|
307
|
+
if (approved && !result.startsWith("Error:")) {
|
|
308
|
+
const kindMap = {
|
|
309
|
+
shell: "shell-run",
|
|
310
|
+
fetch: "url-fetched",
|
|
311
|
+
"read-file": "file-read",
|
|
312
|
+
"read-folder": "file-read",
|
|
313
|
+
grep: "file-read",
|
|
314
|
+
"delete-file": "file-written",
|
|
315
|
+
"delete-folder": "file-written",
|
|
316
|
+
"open-url": "url-fetched",
|
|
317
|
+
"generate-pdf": "file-written",
|
|
318
|
+
"write-file": "file-written",
|
|
319
|
+
search: "url-fetched",
|
|
320
|
+
} as const;
|
|
321
|
+
appendHistory({
|
|
322
|
+
kind: kindMap[parsed.kind as keyof typeof kindMap] ?? "shell-run",
|
|
323
|
+
detail:
|
|
324
|
+
parsed.kind === "shell"
|
|
325
|
+
? parsed.command
|
|
326
|
+
: parsed.kind === "fetch"
|
|
327
|
+
? parsed.url
|
|
261
328
|
: parsed.kind === "search"
|
|
262
|
-
?
|
|
263
|
-
: "
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
approved,
|
|
281
|
-
};
|
|
329
|
+
? parsed.query
|
|
330
|
+
: parsed.kind === "read-folder"
|
|
331
|
+
? parsed.folderPath
|
|
332
|
+
: parsed.kind === "grep"
|
|
333
|
+
? `${parsed.pattern} ${parsed.glob}`
|
|
334
|
+
: parsed.kind === "delete-file"
|
|
335
|
+
? parsed.filePath
|
|
336
|
+
: parsed.kind === "delete-folder"
|
|
337
|
+
? parsed.folderPath
|
|
338
|
+
: parsed.kind === "open-url"
|
|
339
|
+
? parsed.url
|
|
340
|
+
: parsed.kind === "generate-pdf"
|
|
341
|
+
? parsed.filePath
|
|
342
|
+
: parsed.filePath,
|
|
343
|
+
summary: result.split("\n")[0]?.slice(0, 120) ?? "",
|
|
344
|
+
repoPath,
|
|
345
|
+
});
|
|
346
|
+
}
|
|
282
347
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
348
|
+
const toolName =
|
|
349
|
+
parsed.kind === "shell"
|
|
350
|
+
? "shell"
|
|
351
|
+
: parsed.kind === "fetch"
|
|
352
|
+
? "fetch"
|
|
353
|
+
: parsed.kind === "read-file"
|
|
354
|
+
? "read-file"
|
|
355
|
+
: parsed.kind === "read-folder"
|
|
356
|
+
? "read-folder"
|
|
357
|
+
: parsed.kind === "grep"
|
|
358
|
+
? "grep"
|
|
359
|
+
: parsed.kind === "delete-file"
|
|
360
|
+
? "delete-file"
|
|
361
|
+
: parsed.kind === "delete-folder"
|
|
362
|
+
? "delete-folder"
|
|
363
|
+
: parsed.kind === "open-url"
|
|
364
|
+
? "open-url"
|
|
365
|
+
: parsed.kind === "generate-pdf"
|
|
366
|
+
? "generate-pdf"
|
|
367
|
+
: parsed.kind === "search"
|
|
368
|
+
? "search"
|
|
369
|
+
: "write-file";
|
|
370
|
+
|
|
371
|
+
const toolContent =
|
|
372
|
+
parsed.kind === "shell"
|
|
373
|
+
? parsed.command
|
|
374
|
+
: parsed.kind === "fetch"
|
|
375
|
+
? parsed.url
|
|
376
|
+
: parsed.kind === "search"
|
|
377
|
+
? parsed.query
|
|
378
|
+
: parsed.kind === "read-folder"
|
|
379
|
+
? parsed.folderPath
|
|
380
|
+
: parsed.kind === "grep"
|
|
381
|
+
? `${parsed.pattern} — ${parsed.glob}`
|
|
382
|
+
: parsed.kind === "delete-file"
|
|
383
|
+
? parsed.filePath
|
|
384
|
+
: parsed.kind === "delete-folder"
|
|
385
|
+
? parsed.folderPath
|
|
386
|
+
: parsed.kind === "open-url"
|
|
387
|
+
? parsed.url
|
|
388
|
+
: parsed.kind === "generate-pdf"
|
|
389
|
+
? parsed.filePath
|
|
390
|
+
: parsed.filePath;
|
|
391
|
+
|
|
392
|
+
const toolMsg: Message = {
|
|
393
|
+
role: "assistant",
|
|
394
|
+
type: "tool",
|
|
395
|
+
toolName,
|
|
396
|
+
content: toolContent,
|
|
397
|
+
result,
|
|
398
|
+
approved,
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
const withTool = [...currentAll, toolMsg];
|
|
402
|
+
setAllMessages(withTool);
|
|
403
|
+
setCommitted((prev) => [...prev, toolMsg]);
|
|
404
|
+
|
|
405
|
+
setStage({ type: "thinking" });
|
|
406
|
+
callChat(provider!, systemPrompt, withTool)
|
|
407
|
+
.then((r: string) => processResponse(r, withTool))
|
|
408
|
+
.catch(handleError(withTool));
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
if (autoApprove && isSafeTool) {
|
|
412
|
+
executeAndContinue(true);
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
286
415
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
416
|
+
setStage({
|
|
417
|
+
type: "permission",
|
|
418
|
+
tool,
|
|
419
|
+
pendingMessages: currentAll,
|
|
420
|
+
resolve: executeAndContinue,
|
|
292
421
|
});
|
|
293
422
|
return;
|
|
294
423
|
}
|
|
@@ -349,6 +478,21 @@ export const ChatRunner = ({ repoPath }: { repoPath: string }) => {
|
|
|
349
478
|
return;
|
|
350
479
|
}
|
|
351
480
|
|
|
481
|
+
if (text.trim().toLowerCase() === "/auto") {
|
|
482
|
+
const next = !autoApprove;
|
|
483
|
+
setAutoApprove(next);
|
|
484
|
+
const msg: Message = {
|
|
485
|
+
role: "assistant",
|
|
486
|
+
content: next
|
|
487
|
+
? "Auto-approve ON — read, search, grep and folder tools will run without asking. Write and code changes still require approval."
|
|
488
|
+
: "Auto-approve OFF — all tools will ask for permission.",
|
|
489
|
+
type: "text",
|
|
490
|
+
};
|
|
491
|
+
setCommitted((prev) => [...prev, msg]);
|
|
492
|
+
setAllMessages((prev) => [...prev, msg]);
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
|
|
352
496
|
if (text.trim().toLowerCase() === "/clear history") {
|
|
353
497
|
clearRepoHistory(repoPath);
|
|
354
498
|
const clearedMsg: Message = {
|
|
@@ -365,6 +509,7 @@ export const ChatRunner = ({ repoPath }: { repoPath: string }) => {
|
|
|
365
509
|
const nextAll = [...allMessages, userMsg];
|
|
366
510
|
setCommitted((prev) => [...prev, userMsg]);
|
|
367
511
|
setAllMessages(nextAll);
|
|
512
|
+
toolResultCache.current.clear();
|
|
368
513
|
setStage({ type: "thinking" });
|
|
369
514
|
callChat(provider, systemPrompt, nextAll)
|
|
370
515
|
.then((raw: string) => processResponse(raw, nextAll))
|
|
@@ -724,7 +869,7 @@ Suggestions: ${lensFile.suggestions.slice(0, 3).join("; ")}`
|
|
|
724
869
|
setInputValue("");
|
|
725
870
|
}}
|
|
726
871
|
/>
|
|
727
|
-
<ShortcutBar />
|
|
872
|
+
<ShortcutBar autoApprove={autoApprove} />
|
|
728
873
|
</Box>
|
|
729
874
|
)}
|
|
730
875
|
</Box>
|
package/src/index.tsx
CHANGED
|
@@ -1,60 +1,60 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { render } from "ink";
|
|
3
|
-
import { Command } from "commander";
|
|
4
|
-
import { RepoCommand } from "./commands/repo";
|
|
5
|
-
import { InitCommand } from "./commands/provider";
|
|
6
|
-
import { ReviewCommand } from "./commands/review";
|
|
7
|
-
import { TaskCommand } from "./commands/task";
|
|
8
|
-
import { ChatCommand } from "./commands/chat";
|
|
9
|
-
import { TimelineCommand } from "./commands/timeline";
|
|
10
|
-
|
|
11
|
-
const program = new Command();
|
|
12
|
-
|
|
13
|
-
program
|
|
14
|
-
.command("repo <url>")
|
|
15
|
-
.description("Analyze a remote repository")
|
|
16
|
-
.action((url) => {
|
|
17
|
-
render(<RepoCommand url={url} />);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
program
|
|
21
|
-
.command("provider")
|
|
22
|
-
.description("Configure AI providers")
|
|
23
|
-
.action(() => {
|
|
24
|
-
render(<InitCommand />);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
program
|
|
28
|
-
.command("review [path]")
|
|
29
|
-
.description("Review a local codebase")
|
|
30
|
-
.action((inputPath) => {
|
|
31
|
-
render(<ReviewCommand path={inputPath ?? "."} />);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
program
|
|
35
|
-
.command("task <text>")
|
|
36
|
-
.description("Apply a natural language change to the codebase")
|
|
37
|
-
.option("-p, --path <path>", "Path to the repo", ".")
|
|
38
|
-
.action((text: string, opts: { path: string }) => {
|
|
39
|
-
render(<TaskCommand prompt={text} path={opts.path} />);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
program
|
|
43
|
-
.command("chat")
|
|
44
|
-
.description("Chat with your codebase — ask questions or make changes")
|
|
45
|
-
.option("-p, --path <path>", "Path to the repo", ".")
|
|
46
|
-
.action((opts: { path: string }) => {
|
|
47
|
-
render(<ChatCommand path={opts.path} />);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
program
|
|
51
|
-
.command("timeline")
|
|
52
|
-
.description(
|
|
53
|
-
"Explore your code history — see commits, changes, and evolution",
|
|
54
|
-
)
|
|
55
|
-
.option("-p, --path <path>", "Path to the repo", ".")
|
|
56
|
-
.action((opts: { path: string }) => {
|
|
57
|
-
render(<TimelineCommand path={opts.path} />);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
program.parse(process.argv);
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render } from "ink";
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
import { RepoCommand } from "./commands/repo";
|
|
5
|
+
import { InitCommand } from "./commands/provider";
|
|
6
|
+
import { ReviewCommand } from "./commands/review";
|
|
7
|
+
import { TaskCommand } from "./commands/task";
|
|
8
|
+
import { ChatCommand } from "./commands/chat";
|
|
9
|
+
import { TimelineCommand } from "./commands/timeline";
|
|
10
|
+
|
|
11
|
+
const program = new Command();
|
|
12
|
+
|
|
13
|
+
program
|
|
14
|
+
.command("repo <url>")
|
|
15
|
+
.description("Analyze a remote repository")
|
|
16
|
+
.action((url) => {
|
|
17
|
+
render(<RepoCommand url={url} />);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
program
|
|
21
|
+
.command("provider")
|
|
22
|
+
.description("Configure AI providers")
|
|
23
|
+
.action(() => {
|
|
24
|
+
render(<InitCommand />);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
program
|
|
28
|
+
.command("review [path]")
|
|
29
|
+
.description("Review a local codebase")
|
|
30
|
+
.action((inputPath) => {
|
|
31
|
+
render(<ReviewCommand path={inputPath ?? "."} />);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
program
|
|
35
|
+
.command("task <text>")
|
|
36
|
+
.description("Apply a natural language change to the codebase")
|
|
37
|
+
.option("-p, --path <path>", "Path to the repo", ".")
|
|
38
|
+
.action((text: string, opts: { path: string }) => {
|
|
39
|
+
render(<TaskCommand prompt={text} path={opts.path} />);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
program
|
|
43
|
+
.command("chat")
|
|
44
|
+
.description("Chat with your codebase — ask questions or make changes")
|
|
45
|
+
.option("-p, --path <path>", "Path to the repo", ".")
|
|
46
|
+
.action((opts: { path: string }) => {
|
|
47
|
+
render(<ChatCommand path={opts.path} />);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
program
|
|
51
|
+
.command("timeline")
|
|
52
|
+
.description(
|
|
53
|
+
"Explore your code history — see commits, changes, and evolution",
|
|
54
|
+
)
|
|
55
|
+
.option("-p, --path <path>", "Path to the repo", ".")
|
|
56
|
+
.action((opts: { path: string }) => {
|
|
57
|
+
render(<TimelineCommand path={opts.path} />);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
program.parse(process.argv);
|
package/src/types/chat.ts
CHANGED
|
@@ -6,7 +6,13 @@ export type ToolCall =
|
|
|
6
6
|
| { type: "shell"; command: string }
|
|
7
7
|
| { type: "fetch"; url: string }
|
|
8
8
|
| { type: "read-file"; filePath: string }
|
|
9
|
+
| { type: "read-folder"; folderPath: string }
|
|
10
|
+
| { type: "grep"; pattern: string; glob: string }
|
|
9
11
|
| { type: "write-file"; filePath: string; fileContent: string }
|
|
12
|
+
| { type: "delete-file"; filePath: string }
|
|
13
|
+
| { type: "delete-folder"; folderPath: string }
|
|
14
|
+
| { type: "open-url"; url: string }
|
|
15
|
+
| { type: "generate-pdf"; filePath: string; content: string }
|
|
10
16
|
| { type: "search"; query: string };
|
|
11
17
|
|
|
12
18
|
// ── Messages ──────────────────────────────────────────────────────────────────
|
|
@@ -16,7 +22,18 @@ export type Message =
|
|
|
16
22
|
| {
|
|
17
23
|
role: "assistant";
|
|
18
24
|
type: "tool";
|
|
19
|
-
toolName:
|
|
25
|
+
toolName:
|
|
26
|
+
| "shell"
|
|
27
|
+
| "fetch"
|
|
28
|
+
| "read-file"
|
|
29
|
+
| "read-folder"
|
|
30
|
+
| "grep"
|
|
31
|
+
| "write-file"
|
|
32
|
+
| "delete-file"
|
|
33
|
+
| "delete-folder"
|
|
34
|
+
| "open-url"
|
|
35
|
+
| "generate-pdf"
|
|
36
|
+
| "search";
|
|
20
37
|
content: string;
|
|
21
38
|
result: string;
|
|
22
39
|
approved: boolean;
|