@ridit/lens 0.3.7 → 0.3.9
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.mjs +105368 -274002
- package/package.json +13 -19
- package/src/colors.ts +15 -15
- package/src/commands/chat.tsx +32 -23
- package/src/commands/provider.tsx +11 -238
- package/src/commands/repo.tsx +66 -120
- package/src/commands/timeline.tsx +11 -22
- package/src/components/ChatView.tsx +238 -0
- package/src/components/Message.tsx +46 -0
- package/src/components/ToolCall.tsx +67 -0
- package/src/components/chat/ChatView.tsx +550 -0
- package/src/components/chat/Message.tsx +152 -0
- package/src/components/chat/StatusBar.tsx +214 -0
- package/src/components/chat/TextArea.tsx +173 -176
- package/src/components/provider/ApiKeyStep.tsx +207 -199
- package/src/components/provider/ModelStep.tsx +90 -88
- package/src/components/provider/ProviderSetup.tsx +331 -0
- package/src/components/provider/ProviderTypeStep.tsx +53 -61
- package/src/components/repo/StepRow.tsx +68 -69
- package/src/components/timeline/TimelineView.tsx +840 -0
- package/src/components/toolcall-utils.ts +103 -0
- package/src/components/watch/RunView.tsx +497 -0
- package/src/hooks/useChatInput.ts +49 -0
- package/src/hooks/useCommandHandler.ts +117 -0
- package/src/index.tsx +386 -139
- package/src/utils/git.ts +149 -155
- package/src/utils/repo.ts +62 -69
- package/src/utils/thinking.tsx +64 -0
- package/src/utils/watch.ts +165 -307
- package/tests/message.test.ts +38 -0
- package/tests/toolcall-utils.test.ts +111 -0
- package/tsconfig.json +8 -24
- package/CLAUDE.md +0 -50
- package/LENS.md +0 -48
- package/LICENSE +0 -21
- package/README.md +0 -93
- package/addons/README.md +0 -55
- package/addons/clean-cache.js +0 -48
- package/addons/generate-readme.js +0 -67
- package/addons/git-stats.js +0 -29
- package/addons/run-tests.js +0 -127
- package/src/commands/commit.tsx +0 -668
- package/src/commands/review.tsx +0 -294
- package/src/commands/run.tsx +0 -56
- package/src/commands/task.tsx +0 -36
- package/src/components/chat/ChatMessage.tsx +0 -195
- package/src/components/chat/ChatOverlays.tsx +0 -399
- package/src/components/chat/ChatRunner.tsx +0 -517
- package/src/components/chat/hooks/useChat.ts +0 -631
- package/src/components/chat/hooks/useChatInput.ts +0 -79
- package/src/components/chat/hooks/useCommandHandlers.ts +0 -327
- package/src/components/provider/ProviderPicker.tsx +0 -76
- package/src/components/provider/RemoveProviderStep.tsx +0 -82
- package/src/components/repo/DiffViewer.tsx +0 -175
- package/src/components/repo/FileReviewer.tsx +0 -70
- package/src/components/repo/FileViewer.tsx +0 -60
- package/src/components/repo/IssueFixer.tsx +0 -666
- package/src/components/repo/LensFileMenu.tsx +0 -115
- package/src/components/repo/NoProviderPrompt.tsx +0 -28
- package/src/components/repo/PreviewRunner.tsx +0 -217
- package/src/components/repo/RepoAnalysis.tsx +0 -534
- package/src/components/task/TaskRunner.tsx +0 -396
- package/src/components/timeline/CommitDetail.tsx +0 -272
- package/src/components/timeline/CommitList.tsx +0 -162
- package/src/components/timeline/TimelineChat.tsx +0 -166
- package/src/components/timeline/TimelineRunner.tsx +0 -1285
- package/src/components/watch/RunRunner.tsx +0 -929
- package/src/prompts/fewshot.ts +0 -252
- package/src/prompts/index.ts +0 -2
- package/src/prompts/system.ts +0 -285
- package/src/tools/chart.ts +0 -202
- package/src/tools/convert-image.ts +0 -312
- package/src/tools/files.ts +0 -253
- package/src/tools/git.ts +0 -603
- package/src/tools/index.ts +0 -17
- package/src/tools/pdf.ts +0 -164
- package/src/tools/shell.ts +0 -96
- package/src/tools/view-image.ts +0 -335
- package/src/tools/web.ts +0 -212
- package/src/types/chat.ts +0 -86
- package/src/types/config.ts +0 -20
- package/src/types/repo.ts +0 -54
- package/src/utils/addons/loadAddons.ts +0 -34
- package/src/utils/ai.ts +0 -321
- package/src/utils/chat.ts +0 -326
- package/src/utils/chatHistory.ts +0 -121
- package/src/utils/config.ts +0 -61
- package/src/utils/files.ts +0 -105
- package/src/utils/intentClassifier.ts +0 -58
- package/src/utils/lensfile.ts +0 -142
- package/src/utils/llm.ts +0 -81
- package/src/utils/memory.ts +0 -209
- package/src/utils/preview.ts +0 -119
- package/src/utils/stats.ts +0 -174
- package/src/utils/tools/builtins.ts +0 -377
- package/src/utils/tools/registry.ts +0 -105
|
@@ -1,377 +0,0 @@
|
|
|
1
|
-
import type { Tool, ToolContext } from "@ridit/lens-sdk";
|
|
2
|
-
import { TOOL_TAGS } from "@ridit/lens-sdk";
|
|
3
|
-
import {
|
|
4
|
-
fetchUrl,
|
|
5
|
-
searchWeb,
|
|
6
|
-
runShell,
|
|
7
|
-
openUrl,
|
|
8
|
-
readFile,
|
|
9
|
-
readFolder,
|
|
10
|
-
grepFiles,
|
|
11
|
-
writeFile,
|
|
12
|
-
deleteFile,
|
|
13
|
-
deleteFolder,
|
|
14
|
-
generatePdf,
|
|
15
|
-
viewImageTool,
|
|
16
|
-
registerGitTools,
|
|
17
|
-
chartDataTool,
|
|
18
|
-
convertImageTool,
|
|
19
|
-
} from "../../tools";
|
|
20
|
-
|
|
21
|
-
const cleanBody = (body: string) => body.trim().replace(/\\/g, "/");
|
|
22
|
-
|
|
23
|
-
export const fetchTool: Tool<string> = {
|
|
24
|
-
name: "fetch",
|
|
25
|
-
description: "load a URL",
|
|
26
|
-
safe: true,
|
|
27
|
-
tag: TOOL_TAGS.net,
|
|
28
|
-
permissionLabel: "fetch",
|
|
29
|
-
systemPromptEntry: (i) =>
|
|
30
|
-
`### ${i}. fetch — load a URL\n<fetch>https://example.com</fetch>`,
|
|
31
|
-
parseInput: (body) => body.replace(/^<|>$/g, "").trim() || null,
|
|
32
|
-
summariseInput: (url) => url,
|
|
33
|
-
execute: async (url) => {
|
|
34
|
-
try {
|
|
35
|
-
const value = await fetchUrl(url);
|
|
36
|
-
return { kind: "text", value };
|
|
37
|
-
} catch (err) {
|
|
38
|
-
return {
|
|
39
|
-
kind: "error",
|
|
40
|
-
value: `Fetch failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
},
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
export const shellTool: Tool<string> = {
|
|
47
|
-
name: "shell",
|
|
48
|
-
description: "run a terminal command",
|
|
49
|
-
safe: false,
|
|
50
|
-
tag: TOOL_TAGS.shell,
|
|
51
|
-
permissionLabel: "run",
|
|
52
|
-
systemPromptEntry: (i) =>
|
|
53
|
-
`### ${i}. shell — run a terminal command\n<shell>node -v</shell>`,
|
|
54
|
-
parseInput: (body) => body || null,
|
|
55
|
-
summariseInput: (cmd) => cmd,
|
|
56
|
-
execute: async (cmd, ctx) => {
|
|
57
|
-
const value = await runShell(cmd, ctx.repoPath);
|
|
58
|
-
return { kind: "text", value };
|
|
59
|
-
},
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
export const readFileTool: Tool<string> = {
|
|
63
|
-
name: "read-file",
|
|
64
|
-
description: "read a file from the repo",
|
|
65
|
-
safe: true,
|
|
66
|
-
tag: TOOL_TAGS.read,
|
|
67
|
-
permissionLabel: "read",
|
|
68
|
-
systemPromptEntry: (i) =>
|
|
69
|
-
`### ${i}. read-file — read a file from the repo\n<read-file>src/foo.ts</read-file>`,
|
|
70
|
-
parseInput: (body) => cleanBody(body) || null,
|
|
71
|
-
summariseInput: (p) => p,
|
|
72
|
-
execute: (filePath, ctx) => ({
|
|
73
|
-
kind: "text",
|
|
74
|
-
value: readFile(filePath, ctx.repoPath),
|
|
75
|
-
}),
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
export const readFolderTool: Tool<string> = {
|
|
79
|
-
name: "read-folder",
|
|
80
|
-
description: "list contents of a folder (files + subfolders, one level deep)",
|
|
81
|
-
tag: TOOL_TAGS.read,
|
|
82
|
-
safe: true,
|
|
83
|
-
permissionLabel: "folder",
|
|
84
|
-
systemPromptEntry: (i) =>
|
|
85
|
-
`### ${i}. read-folder — list contents of a folder (files + subfolders, one level deep)\n<read-folder>src/components</read-folder>`,
|
|
86
|
-
parseInput: (body) => cleanBody(body) || null,
|
|
87
|
-
summariseInput: (p) => p,
|
|
88
|
-
execute: (folderPath, ctx) => ({
|
|
89
|
-
kind: "text",
|
|
90
|
-
value: readFolder(folderPath, ctx.repoPath),
|
|
91
|
-
}),
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
interface GrepInput {
|
|
95
|
-
pattern: string;
|
|
96
|
-
glob: string;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export const grepTool: Tool<GrepInput> = {
|
|
100
|
-
name: "grep",
|
|
101
|
-
description: "search for a pattern across files in the repo",
|
|
102
|
-
tag: TOOL_TAGS.find,
|
|
103
|
-
safe: true,
|
|
104
|
-
permissionLabel: "grep",
|
|
105
|
-
systemPromptEntry: (i) =>
|
|
106
|
-
`### ${i}. grep — search for a pattern across files in the repo (cross-platform, no shell needed)\n<grep>\n{"pattern": "ChatRunner", "glob": "src/**/*.tsx"}\n</grep>`,
|
|
107
|
-
parseInput: (body) => {
|
|
108
|
-
try {
|
|
109
|
-
const parsed = JSON.parse(cleanBody(body)) as {
|
|
110
|
-
pattern: string;
|
|
111
|
-
glob?: string;
|
|
112
|
-
};
|
|
113
|
-
return { pattern: parsed.pattern, glob: parsed.glob ?? "**/*" };
|
|
114
|
-
} catch {
|
|
115
|
-
return { pattern: body, glob: "**/*" };
|
|
116
|
-
}
|
|
117
|
-
},
|
|
118
|
-
summariseInput: ({ pattern, glob }) => `${pattern} — ${glob}`,
|
|
119
|
-
execute: ({ pattern, glob }, ctx) => ({
|
|
120
|
-
kind: "text",
|
|
121
|
-
value: grepFiles(pattern, glob, ctx.repoPath),
|
|
122
|
-
}),
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
interface WriteFileInput {
|
|
126
|
-
path: string;
|
|
127
|
-
content: string;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export const writeFileTool: Tool<WriteFileInput> = {
|
|
131
|
-
name: "write-file",
|
|
132
|
-
description: "create or overwrite a file",
|
|
133
|
-
tag: TOOL_TAGS.write,
|
|
134
|
-
safe: false,
|
|
135
|
-
permissionLabel: "write",
|
|
136
|
-
systemPromptEntry: (i) =>
|
|
137
|
-
`### ${i}. write-file — create or overwrite a file\n<write-file>\n{"path": "data/output.csv", "content": "col1,col2\\nval1,val2"}\n</write-file>`,
|
|
138
|
-
parseInput: (body) => {
|
|
139
|
-
const tryParse = (s: string) => {
|
|
140
|
-
try {
|
|
141
|
-
const parsed = JSON.parse(s) as { path: string; content: string };
|
|
142
|
-
if (!parsed.path || parsed.content === undefined) return null;
|
|
143
|
-
return { ...parsed, path: parsed.path.replace(/\\/g, "/") };
|
|
144
|
-
} catch {
|
|
145
|
-
return null;
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
const first = tryParse(body.trim());
|
|
150
|
-
if (first) return first;
|
|
151
|
-
|
|
152
|
-
try {
|
|
153
|
-
const sanitized = body
|
|
154
|
-
.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f]/g, "")
|
|
155
|
-
.replace(/\n/g, "\\n")
|
|
156
|
-
.replace(/\r/g, "\\r")
|
|
157
|
-
.replace(/\t/g, "\\t");
|
|
158
|
-
const second = tryParse(sanitized);
|
|
159
|
-
if (second) return second;
|
|
160
|
-
} catch {}
|
|
161
|
-
|
|
162
|
-
const pathMatch = body.match(/"path"\s*:\s*"([^"]+)"/);
|
|
163
|
-
const contentMatch = body.match(/"content"\s*:\s*"([\s\S]*)"\s*}?\s*$/);
|
|
164
|
-
if (pathMatch && contentMatch && contentMatch[1] !== undefined) {
|
|
165
|
-
return {
|
|
166
|
-
path: pathMatch[1]!.replace(/\\/g, "/"),
|
|
167
|
-
content: contentMatch[1]!.replace(/\\n/g, "\n").replace(/\\t/g, "\t"),
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
return null;
|
|
172
|
-
},
|
|
173
|
-
summariseInput: ({ path, content }) => `${path} (${content.length} bytes)`,
|
|
174
|
-
execute: ({ path: filePath, content }, ctx) => ({
|
|
175
|
-
kind: "text",
|
|
176
|
-
value: writeFile(filePath, content, ctx.repoPath),
|
|
177
|
-
}),
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
export const deleteFileTool: Tool<string> = {
|
|
181
|
-
name: "delete-file",
|
|
182
|
-
description: "permanently delete a single file",
|
|
183
|
-
tag: TOOL_TAGS.delete,
|
|
184
|
-
safe: false,
|
|
185
|
-
permissionLabel: "delete",
|
|
186
|
-
systemPromptEntry: (i) =>
|
|
187
|
-
`### ${i}. delete-file — permanently delete a single file\n<delete-file>src/old-component.tsx</delete-file>`,
|
|
188
|
-
parseInput: (body) => cleanBody(body) || null,
|
|
189
|
-
summariseInput: (p) => p,
|
|
190
|
-
execute: (filePath, ctx) => ({
|
|
191
|
-
kind: "text",
|
|
192
|
-
value: deleteFile(filePath, ctx.repoPath),
|
|
193
|
-
}),
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
export const deleteFolderTool: Tool<string> = {
|
|
197
|
-
name: "delete-folder",
|
|
198
|
-
description: "permanently delete a folder and all its contents",
|
|
199
|
-
tag: TOOL_TAGS.delete,
|
|
200
|
-
safe: false,
|
|
201
|
-
permissionLabel: "delete folder",
|
|
202
|
-
systemPromptEntry: (i) =>
|
|
203
|
-
`### ${i}. delete-folder — permanently delete a folder and all its contents\n<delete-folder>src/legacy</delete-folder>`,
|
|
204
|
-
parseInput: (body) => cleanBody(body) || null,
|
|
205
|
-
summariseInput: (p) => p,
|
|
206
|
-
execute: (folderPath, ctx) => ({
|
|
207
|
-
kind: "text",
|
|
208
|
-
value: deleteFolder(folderPath, ctx.repoPath),
|
|
209
|
-
}),
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
export const openUrlTool: Tool<string> = {
|
|
213
|
-
name: "open-url",
|
|
214
|
-
description: "open a URL in the user's default browser",
|
|
215
|
-
tag: TOOL_TAGS.net,
|
|
216
|
-
safe: true,
|
|
217
|
-
permissionLabel: "open",
|
|
218
|
-
systemPromptEntry: (i) =>
|
|
219
|
-
`### ${i}. open-url — open a URL in the user's default browser\n<open-url>https://github.com/owner/repo</open-url>`,
|
|
220
|
-
parseInput: (body) => body.replace(/^<|>$/g, "").trim() || null,
|
|
221
|
-
summariseInput: (url) => url,
|
|
222
|
-
execute: (url) => ({ kind: "text", value: openUrl(url) }),
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
interface GeneratePdfInput {
|
|
226
|
-
filePath: string;
|
|
227
|
-
content: string;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
export const generatePdfTool: Tool<GeneratePdfInput> = {
|
|
231
|
-
name: "generate-pdf",
|
|
232
|
-
description: "generate a PDF file from markdown-style content",
|
|
233
|
-
tag: TOOL_TAGS.write,
|
|
234
|
-
safe: false,
|
|
235
|
-
permissionLabel: "pdf",
|
|
236
|
-
systemPromptEntry: (i) =>
|
|
237
|
-
`### ${i}. generate-pdf — generate a PDF file from markdown-style content\n<generate-pdf>\n{"path": "output/report.pdf", "content": "# Title\\n\\nSome body text."}\n</generate-pdf>`,
|
|
238
|
-
parseInput: (body) => {
|
|
239
|
-
try {
|
|
240
|
-
const parsed = JSON.parse(cleanBody(body)) as {
|
|
241
|
-
path?: string;
|
|
242
|
-
filePath?: string;
|
|
243
|
-
content?: string;
|
|
244
|
-
};
|
|
245
|
-
return {
|
|
246
|
-
filePath: parsed.path ?? parsed.filePath ?? "output.pdf",
|
|
247
|
-
content: parsed.content ?? "",
|
|
248
|
-
};
|
|
249
|
-
} catch {
|
|
250
|
-
return null;
|
|
251
|
-
}
|
|
252
|
-
},
|
|
253
|
-
summariseInput: ({ filePath }) => filePath,
|
|
254
|
-
execute: async ({ filePath, content }, ctx) => ({
|
|
255
|
-
kind: "text",
|
|
256
|
-
value: await generatePdf(filePath, content, ctx.repoPath),
|
|
257
|
-
}),
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
export const searchTool: Tool<string> = {
|
|
261
|
-
name: "search",
|
|
262
|
-
tag: TOOL_TAGS.net,
|
|
263
|
-
description: "search the internet for anything you are unsure about",
|
|
264
|
-
safe: true,
|
|
265
|
-
permissionLabel: "search",
|
|
266
|
-
systemPromptEntry: (i) =>
|
|
267
|
-
`### ${i}. search — search the internet for anything you are unsure about\n<search>how to use React useEffect cleanup function</search>`,
|
|
268
|
-
parseInput: (body) => body || null,
|
|
269
|
-
summariseInput: (q) => `"${q}"`,
|
|
270
|
-
execute: async (query) => {
|
|
271
|
-
try {
|
|
272
|
-
const value = await searchWeb(query);
|
|
273
|
-
return { kind: "text", value };
|
|
274
|
-
} catch (err) {
|
|
275
|
-
return {
|
|
276
|
-
kind: "error",
|
|
277
|
-
value: `Search failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
},
|
|
281
|
-
};
|
|
282
|
-
|
|
283
|
-
export const cloneTool: Tool<string> = {
|
|
284
|
-
name: "clone",
|
|
285
|
-
description: "clone a GitHub repo so you can explore and discuss it",
|
|
286
|
-
tag: TOOL_TAGS.write,
|
|
287
|
-
safe: false,
|
|
288
|
-
permissionLabel: "clone",
|
|
289
|
-
systemPromptEntry: (i) =>
|
|
290
|
-
`### ${i}. clone — clone a GitHub repo so you can explore and discuss it\n<clone>https://github.com/owner/repo</clone>`,
|
|
291
|
-
parseInput: (body) => body.replace(/^<|>$/g, "").trim() || null,
|
|
292
|
-
summariseInput: (url) => url,
|
|
293
|
-
execute: (repoUrl) => ({
|
|
294
|
-
kind: "text",
|
|
295
|
-
value: `Clone of ${repoUrl} was handled by the UI.`,
|
|
296
|
-
}),
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
export interface ChangesInput {
|
|
300
|
-
summary: string;
|
|
301
|
-
patches: { path: string; content: string; isNew: boolean }[];
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
export const changesTool: Tool<ChangesInput> = {
|
|
305
|
-
name: "changes",
|
|
306
|
-
description: "propose code edits (shown as a diff for user approval)",
|
|
307
|
-
tag: TOOL_TAGS.write,
|
|
308
|
-
safe: false,
|
|
309
|
-
permissionLabel: "changes",
|
|
310
|
-
systemPromptEntry: (i) =>
|
|
311
|
-
`### ${i}. changes — propose code edits (shown as a diff for user approval)\n<changes>\n{"summary": "what changed and why", "patches": [{"path": "src/foo.ts", "content": "COMPLETE file content", "isNew": false}]}\n</changes>`,
|
|
312
|
-
parseInput: (body) => {
|
|
313
|
-
try {
|
|
314
|
-
return JSON.parse(cleanBody(body)) as ChangesInput;
|
|
315
|
-
} catch {
|
|
316
|
-
return null;
|
|
317
|
-
}
|
|
318
|
-
},
|
|
319
|
-
summariseInput: ({ summary }) => summary,
|
|
320
|
-
execute: ({ summary }) => ({
|
|
321
|
-
kind: "text",
|
|
322
|
-
value: `Changes proposed: ${summary}`,
|
|
323
|
-
}),
|
|
324
|
-
};
|
|
325
|
-
|
|
326
|
-
interface ReadFilesInput {
|
|
327
|
-
paths: string[];
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
export const readFilesTool: Tool<ReadFilesInput> = {
|
|
331
|
-
name: "read-files",
|
|
332
|
-
description: "read multiple files from the repo at once",
|
|
333
|
-
tag: TOOL_TAGS.read,
|
|
334
|
-
safe: true,
|
|
335
|
-
permissionLabel: "read",
|
|
336
|
-
systemPromptEntry: (i) =>
|
|
337
|
-
`### ${i}. read-files — read multiple files from the repo at once\n<read-files>\n["src/foo.ts", "src/bar.ts"]\n</read-files>`,
|
|
338
|
-
parseInput: (body) => {
|
|
339
|
-
try {
|
|
340
|
-
const parsed = JSON.parse(cleanBody(body)) as string[];
|
|
341
|
-
if (!Array.isArray(parsed) || parsed.length === 0) return null;
|
|
342
|
-
return { paths: parsed };
|
|
343
|
-
} catch {
|
|
344
|
-
return null;
|
|
345
|
-
}
|
|
346
|
-
},
|
|
347
|
-
summariseInput: ({ paths }) => paths.join(", "),
|
|
348
|
-
execute: ({ paths }, ctx) => ({
|
|
349
|
-
kind: "text",
|
|
350
|
-
value: paths
|
|
351
|
-
.map((p) => `=== ${p} ===\n${readFile(p, ctx.repoPath)}`)
|
|
352
|
-
.join("\n\n"),
|
|
353
|
-
}),
|
|
354
|
-
};
|
|
355
|
-
|
|
356
|
-
import { registry } from "./registry";
|
|
357
|
-
|
|
358
|
-
export function registerBuiltins(): void {
|
|
359
|
-
registry.register(fetchTool);
|
|
360
|
-
registry.register(shellTool);
|
|
361
|
-
registry.register(readFileTool);
|
|
362
|
-
registry.register(readFolderTool);
|
|
363
|
-
registry.register(grepTool);
|
|
364
|
-
registry.register(writeFileTool);
|
|
365
|
-
registry.register(deleteFileTool);
|
|
366
|
-
registry.register(deleteFolderTool);
|
|
367
|
-
registry.register(openUrlTool);
|
|
368
|
-
registry.register(generatePdfTool);
|
|
369
|
-
registry.register(searchTool);
|
|
370
|
-
registry.register(cloneTool);
|
|
371
|
-
registry.register(changesTool);
|
|
372
|
-
registry.register(viewImageTool);
|
|
373
|
-
registry.register(chartDataTool);
|
|
374
|
-
registry.register(convertImageTool);
|
|
375
|
-
registry.register(readFilesTool);
|
|
376
|
-
registerGitTools();
|
|
377
|
-
}
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import type { Tool, ToolTag } from "@ridit/lens-sdk";
|
|
2
|
-
import type { Intent } from "../intentClassifier";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Broad capability category for a tool.
|
|
6
|
-
* Used to filter the system prompt based on classified user intent.
|
|
7
|
-
*
|
|
8
|
-
* "read" — safe, purely observational (read-file, read-folder, grep, etc.)
|
|
9
|
-
* "net" — outbound network (fetch, search, clone, open-url)
|
|
10
|
-
* "write" — creates or overwrites file content (write-file, changes, generate-pdf)
|
|
11
|
-
* "delete" — destructive removal (delete-file, delete-folder)
|
|
12
|
-
* "shell" — arbitrary shell execution
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
/** Tools allowed for each intent level */
|
|
16
|
-
const INTENT_ALLOWED: Record<Intent, ToolTag[]> = {
|
|
17
|
-
readonly: ["read", "net"],
|
|
18
|
-
mutating: ["read", "net", "write", "delete", "shell"],
|
|
19
|
-
any: ["read", "net", "write", "delete", "shell"],
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
class ToolRegistry {
|
|
23
|
-
private tools = new Map<string, Tool<unknown>>();
|
|
24
|
-
|
|
25
|
-
register<T>(tool: Tool<T>): void {
|
|
26
|
-
if (this.tools.has(tool.name)) {
|
|
27
|
-
console.warn(`[ToolRegistry] Overwriting existing tool: "${tool.name}"`);
|
|
28
|
-
}
|
|
29
|
-
this.tools.set(tool.name, tool as Tool<unknown>);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
unregister(name: string): void {
|
|
33
|
-
this.tools.delete(name);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
get(name: string): Tool<unknown> | undefined {
|
|
37
|
-
return this.tools.get(name);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
all(): Tool<unknown>[] {
|
|
41
|
-
return Array.from(this.tools.values());
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
names(): string[] {
|
|
45
|
-
return Array.from(this.tools.keys());
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Returns tool names that are allowed for the given intent.
|
|
50
|
-
* Falls back to all names when a tool has no tag (legacy / addons).
|
|
51
|
-
*/
|
|
52
|
-
namesForIntent(intent: Intent): string[] {
|
|
53
|
-
const allowed = new Set(INTENT_ALLOWED[intent]);
|
|
54
|
-
return Array.from(this.tools.values())
|
|
55
|
-
.filter((t) => {
|
|
56
|
-
const tag = (t as any).tag as ToolTag | undefined;
|
|
57
|
-
// No tag = addon / unknown → always allow (conservative)
|
|
58
|
-
if (!tag) return true;
|
|
59
|
-
return allowed.has(tag);
|
|
60
|
-
})
|
|
61
|
-
.map((t) => t.name);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Build the TOOLS section of the system prompt from all registered tools,
|
|
66
|
-
* optionally scoped to a specific intent.
|
|
67
|
-
*
|
|
68
|
-
* When intent is "readonly", write/delete/shell tools are omitted entirely
|
|
69
|
-
* so the LLM never sees them and can't hallucinate calls to them.
|
|
70
|
-
*/
|
|
71
|
-
buildSystemPromptSection(intent: Intent = "any"): string {
|
|
72
|
-
const allowed = new Set(INTENT_ALLOWED[intent]);
|
|
73
|
-
|
|
74
|
-
const visible = Array.from(this.tools.values()).filter((t) => {
|
|
75
|
-
const tag = (t as any).tag as ToolTag | undefined;
|
|
76
|
-
if (!tag) return true; // addon without tag → always show
|
|
77
|
-
return allowed.has(tag);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
const lines: string[] = ["## TOOLS\n"];
|
|
81
|
-
|
|
82
|
-
if (intent === "readonly") {
|
|
83
|
-
lines.push(
|
|
84
|
-
`You have ${visible.length} tools available for this read-only request. ` +
|
|
85
|
-
`Do NOT attempt to write, delete, or run shell commands — ` +
|
|
86
|
-
`those tools are not available right now.\n`,
|
|
87
|
-
);
|
|
88
|
-
} else {
|
|
89
|
-
lines.push(
|
|
90
|
-
`You have exactly ${visible.length} tools. To use a tool you MUST wrap it ` +
|
|
91
|
-
`in the exact XML tags shown below — no other format will work.\n`,
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
let i = 1;
|
|
96
|
-
for (const tool of visible) {
|
|
97
|
-
lines.push(tool.systemPromptEntry(i++));
|
|
98
|
-
}
|
|
99
|
-
return lines.join("\n");
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export const registry = new ToolRegistry();
|
|
104
|
-
|
|
105
|
-
(globalThis as any).__lens_registry = registry;
|