@ridit/lens 0.3.4 → 0.3.6
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/LENS.md +27 -20
- package/README.md +16 -14
- package/dist/index.mjs +946 -784
- package/package.json +2 -2
- package/src/commands/{watch.tsx → run.tsx} +3 -3
- package/src/components/chat/ChatRunner.tsx +131 -877
- package/src/components/chat/hooks/useChat.ts +540 -0
- package/src/components/chat/hooks/useChatInput.ts +79 -0
- package/src/components/chat/hooks/useCommandHandlers.ts +327 -0
- package/src/components/watch/{WatchRunner.tsx → RunRunner.tsx} +1 -2
- package/src/index.tsx +12 -17
- package/src/prompts/system.ts +141 -20
- package/src/tools/chart.ts +0 -8
- package/src/tools/git.ts +26 -0
- package/src/utils/intentClassifier.ts +58 -0
- package/src/utils/tools/builtins.ts +15 -0
- package/src/utils/tools/registry.ts +65 -9
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import type { Message } from "../../../types/chat";
|
|
2
|
+
import {
|
|
3
|
+
saveChat,
|
|
4
|
+
loadChat,
|
|
5
|
+
listChats,
|
|
6
|
+
deleteChat,
|
|
7
|
+
} from "../../../utils/chatHistory";
|
|
8
|
+
import {
|
|
9
|
+
clearRepoMemory,
|
|
10
|
+
addMemory,
|
|
11
|
+
deleteMemory,
|
|
12
|
+
listMemories,
|
|
13
|
+
} from "../../../utils/memory";
|
|
14
|
+
|
|
15
|
+
export const COMMANDS = [
|
|
16
|
+
{ cmd: "/timeline", desc: "browse commit history" },
|
|
17
|
+
{ cmd: "/clear history", desc: "wipe session memory for this repo" },
|
|
18
|
+
{ cmd: "/review", desc: "review current codebase" },
|
|
19
|
+
{ cmd: "/auto", desc: "toggle auto-approve for read/search tools" },
|
|
20
|
+
{
|
|
21
|
+
cmd: "/auto --force-all",
|
|
22
|
+
desc: "auto-approve ALL tools including shell and writes (⚠ dangerous)",
|
|
23
|
+
},
|
|
24
|
+
{ cmd: "/chat", desc: "chat history commands" },
|
|
25
|
+
{ cmd: "/chat list", desc: "list saved chats for this repo" },
|
|
26
|
+
{ cmd: "/chat load", desc: "load a saved chat by name" },
|
|
27
|
+
{ cmd: "/chat rename", desc: "rename the current chat" },
|
|
28
|
+
{ cmd: "/chat delete", desc: "delete a saved chat by name" },
|
|
29
|
+
{ cmd: "/memory", desc: "memory commands" },
|
|
30
|
+
{ cmd: "/memory list", desc: "list all memories for this repo" },
|
|
31
|
+
{ cmd: "/memory add", desc: "add a memory" },
|
|
32
|
+
{ cmd: "/memory delete", desc: "delete a memory by id" },
|
|
33
|
+
{ cmd: "/memory clear", desc: "clear all memories for this repo" },
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
type CommandContext = {
|
|
37
|
+
repoPath: string;
|
|
38
|
+
allMessages: Message[];
|
|
39
|
+
autoApprove: boolean;
|
|
40
|
+
forceApprove: boolean;
|
|
41
|
+
chatName: string | null;
|
|
42
|
+
chatNameRef: React.MutableRefObject<string | null>;
|
|
43
|
+
setShowTimeline: (v: boolean) => void;
|
|
44
|
+
setShowReview: (v: boolean) => void;
|
|
45
|
+
setShowForceWarning: (v: boolean) => void;
|
|
46
|
+
setForceApprove: (v: boolean) => void;
|
|
47
|
+
setAutoApprove: (v: boolean) => void;
|
|
48
|
+
setAllMessages: (fn: (prev: Message[]) => Message[]) => void;
|
|
49
|
+
setCommitted: (fn: (prev: Message[]) => Message[]) => void;
|
|
50
|
+
setRecentChats: (fn: (prev: string[]) => string[]) => void;
|
|
51
|
+
updateChatName: (name: string) => void;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
import React from "react";
|
|
55
|
+
|
|
56
|
+
function pushMsg(
|
|
57
|
+
msg: Message,
|
|
58
|
+
setCommitted: CommandContext["setCommitted"],
|
|
59
|
+
setAllMessages: CommandContext["setAllMessages"],
|
|
60
|
+
) {
|
|
61
|
+
setCommitted((prev) => [...prev, msg]);
|
|
62
|
+
setAllMessages((prev) => [...prev, msg]);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function makeMsg(content: string): Message {
|
|
66
|
+
return { role: "assistant", content, type: "text" };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Returns true if the command was handled, false if it should fall through
|
|
71
|
+
* to the normal message flow.
|
|
72
|
+
*/
|
|
73
|
+
export function handleCommand(text: string, ctx: CommandContext): boolean {
|
|
74
|
+
const t = text.trim().toLowerCase();
|
|
75
|
+
|
|
76
|
+
if (t === "/timeline") {
|
|
77
|
+
ctx.setShowTimeline(true);
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (t === "/review") {
|
|
82
|
+
ctx.setShowReview(true);
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (t === "/auto --force-all") {
|
|
87
|
+
if (ctx.forceApprove) {
|
|
88
|
+
ctx.setForceApprove(false);
|
|
89
|
+
ctx.setAutoApprove(false);
|
|
90
|
+
pushMsg(
|
|
91
|
+
makeMsg("Force-all mode OFF — tools will ask for permission again."),
|
|
92
|
+
ctx.setCommitted,
|
|
93
|
+
ctx.setAllMessages,
|
|
94
|
+
);
|
|
95
|
+
} else {
|
|
96
|
+
ctx.setShowForceWarning(true);
|
|
97
|
+
}
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (t === "/auto") {
|
|
102
|
+
if (ctx.forceApprove) {
|
|
103
|
+
ctx.setForceApprove(false);
|
|
104
|
+
ctx.setAutoApprove(true);
|
|
105
|
+
pushMsg(
|
|
106
|
+
makeMsg(
|
|
107
|
+
"Force-all mode OFF — switched to normal auto-approve (safe tools only).",
|
|
108
|
+
),
|
|
109
|
+
ctx.setCommitted,
|
|
110
|
+
ctx.setAllMessages,
|
|
111
|
+
);
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
const next = !ctx.autoApprove;
|
|
115
|
+
ctx.setAutoApprove(next);
|
|
116
|
+
pushMsg(
|
|
117
|
+
makeMsg(
|
|
118
|
+
next
|
|
119
|
+
? "Auto-approve ON — safe tools (read, search, fetch) will run without asking."
|
|
120
|
+
: "Auto-approve OFF — all tools will ask for permission.",
|
|
121
|
+
),
|
|
122
|
+
ctx.setCommitted,
|
|
123
|
+
ctx.setAllMessages,
|
|
124
|
+
);
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (t === "/clear history") {
|
|
129
|
+
clearRepoMemory(ctx.repoPath);
|
|
130
|
+
pushMsg(
|
|
131
|
+
makeMsg("History cleared for this repo."),
|
|
132
|
+
ctx.setCommitted,
|
|
133
|
+
ctx.setAllMessages,
|
|
134
|
+
);
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (t === "/chat") {
|
|
139
|
+
pushMsg(
|
|
140
|
+
makeMsg(
|
|
141
|
+
"Chat commands: `/chat list` · `/chat load <n>` · `/chat rename <n>` · `/chat delete <n>`",
|
|
142
|
+
),
|
|
143
|
+
ctx.setCommitted,
|
|
144
|
+
ctx.setAllMessages,
|
|
145
|
+
);
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (t.startsWith("/chat rename")) {
|
|
150
|
+
const parts = text.trim().split(/\s+/);
|
|
151
|
+
const newName = parts.slice(2).join("-");
|
|
152
|
+
if (!newName) {
|
|
153
|
+
pushMsg(
|
|
154
|
+
makeMsg("Usage: `/chat rename <new-name>`"),
|
|
155
|
+
ctx.setCommitted,
|
|
156
|
+
ctx.setAllMessages,
|
|
157
|
+
);
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
const oldName = ctx.chatNameRef.current;
|
|
161
|
+
if (oldName) deleteChat(oldName);
|
|
162
|
+
ctx.updateChatName(newName);
|
|
163
|
+
saveChat(newName, ctx.repoPath, ctx.allMessages);
|
|
164
|
+
ctx.setRecentChats((prev) =>
|
|
165
|
+
[newName, ...prev.filter((n) => n !== newName && n !== oldName)].slice(
|
|
166
|
+
0,
|
|
167
|
+
10,
|
|
168
|
+
),
|
|
169
|
+
);
|
|
170
|
+
pushMsg(
|
|
171
|
+
makeMsg(`Chat renamed to **${newName}**.`),
|
|
172
|
+
ctx.setCommitted,
|
|
173
|
+
ctx.setAllMessages,
|
|
174
|
+
);
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (t.startsWith("/chat delete")) {
|
|
179
|
+
const parts = text.trim().split(/\s+/);
|
|
180
|
+
const name = parts.slice(2).join("-");
|
|
181
|
+
if (!name) {
|
|
182
|
+
pushMsg(
|
|
183
|
+
makeMsg("Usage: `/chat delete <n>`"),
|
|
184
|
+
ctx.setCommitted,
|
|
185
|
+
ctx.setAllMessages,
|
|
186
|
+
);
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
const deleted = deleteChat(name);
|
|
190
|
+
if (!deleted) {
|
|
191
|
+
pushMsg(
|
|
192
|
+
makeMsg(`Chat **${name}** not found.`),
|
|
193
|
+
ctx.setCommitted,
|
|
194
|
+
ctx.setAllMessages,
|
|
195
|
+
);
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
if (ctx.chatNameRef.current === name) {
|
|
199
|
+
ctx.chatNameRef.current = null;
|
|
200
|
+
ctx.updateChatName("");
|
|
201
|
+
}
|
|
202
|
+
ctx.setRecentChats((prev) => prev.filter((n) => n !== name));
|
|
203
|
+
pushMsg(
|
|
204
|
+
makeMsg(`Chat **${name}** deleted.`),
|
|
205
|
+
ctx.setCommitted,
|
|
206
|
+
ctx.setAllMessages,
|
|
207
|
+
);
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (t === "/chat list") {
|
|
212
|
+
const chats = listChats(ctx.repoPath);
|
|
213
|
+
const content =
|
|
214
|
+
chats.length === 0
|
|
215
|
+
? "No saved chats for this repo yet."
|
|
216
|
+
: `Saved chats:\n\n${chats
|
|
217
|
+
.map(
|
|
218
|
+
(c) =>
|
|
219
|
+
`- **${c.name}** · ${c.userMessageCount} messages · ${new Date(c.savedAt).toLocaleString()}`,
|
|
220
|
+
)
|
|
221
|
+
.join("\n")}`;
|
|
222
|
+
pushMsg(makeMsg(content), ctx.setCommitted, ctx.setAllMessages);
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (t.startsWith("/chat load")) {
|
|
227
|
+
const parts = text.trim().split(/\s+/);
|
|
228
|
+
const name = parts.slice(2).join("-");
|
|
229
|
+
if (!name) {
|
|
230
|
+
const chats = listChats(ctx.repoPath);
|
|
231
|
+
const content =
|
|
232
|
+
chats.length === 0
|
|
233
|
+
? "No saved chats found."
|
|
234
|
+
: `Specify a chat name. Recent chats:\n\n${chats
|
|
235
|
+
.slice(0, 10)
|
|
236
|
+
.map((c) => `- **${c.name}**`)
|
|
237
|
+
.join("\n")}`;
|
|
238
|
+
pushMsg(makeMsg(content), ctx.setCommitted, ctx.setAllMessages);
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
const saved = loadChat(name);
|
|
242
|
+
if (!saved) {
|
|
243
|
+
pushMsg(
|
|
244
|
+
makeMsg(
|
|
245
|
+
`Chat **${name}** not found. Use \`/chat list\` to see saved chats.`,
|
|
246
|
+
),
|
|
247
|
+
ctx.setCommitted,
|
|
248
|
+
ctx.setAllMessages,
|
|
249
|
+
);
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
ctx.updateChatName(name);
|
|
253
|
+
|
|
254
|
+
ctx.setAllMessages(() => saved.messages);
|
|
255
|
+
ctx.setCommitted(() => saved.messages);
|
|
256
|
+
const notice = makeMsg(
|
|
257
|
+
`Loaded chat **${name}** · ${saved.userMessageCount} messages · saved ${new Date(saved.savedAt).toLocaleString()}`,
|
|
258
|
+
);
|
|
259
|
+
ctx.setCommitted((prev) => [...prev, notice]);
|
|
260
|
+
ctx.setAllMessages((prev) => [...prev, notice]);
|
|
261
|
+
return true;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (t === "/memory list" || t === "/memory") {
|
|
265
|
+
const mems = listMemories(ctx.repoPath);
|
|
266
|
+
const content =
|
|
267
|
+
mems.length === 0
|
|
268
|
+
? "No memories stored for this repo yet."
|
|
269
|
+
: `Memories for this repo:\n\n${mems.map((m) => `- [${m.id}] ${m.content}`).join("\n")}`;
|
|
270
|
+
pushMsg(makeMsg(content), ctx.setCommitted, ctx.setAllMessages);
|
|
271
|
+
return true;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (t.startsWith("/memory add")) {
|
|
275
|
+
const content = text.trim().slice("/memory add".length).trim();
|
|
276
|
+
if (!content) {
|
|
277
|
+
pushMsg(
|
|
278
|
+
makeMsg("Usage: `/memory add <content>`"),
|
|
279
|
+
ctx.setCommitted,
|
|
280
|
+
ctx.setAllMessages,
|
|
281
|
+
);
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
284
|
+
const mem = addMemory(content, ctx.repoPath);
|
|
285
|
+
pushMsg(
|
|
286
|
+
makeMsg(`Memory saved **[${mem.id}]**: ${mem.content}`),
|
|
287
|
+
ctx.setCommitted,
|
|
288
|
+
ctx.setAllMessages,
|
|
289
|
+
);
|
|
290
|
+
return true;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (t.startsWith("/memory delete")) {
|
|
294
|
+
const id = text.trim().split(/\s+/)[2];
|
|
295
|
+
if (!id) {
|
|
296
|
+
pushMsg(
|
|
297
|
+
makeMsg("Usage: `/memory delete <id>`"),
|
|
298
|
+
ctx.setCommitted,
|
|
299
|
+
ctx.setAllMessages,
|
|
300
|
+
);
|
|
301
|
+
return true;
|
|
302
|
+
}
|
|
303
|
+
const deleted = deleteMemory(id, ctx.repoPath);
|
|
304
|
+
pushMsg(
|
|
305
|
+
makeMsg(
|
|
306
|
+
deleted
|
|
307
|
+
? `Memory **[${id}]** deleted.`
|
|
308
|
+
: `Memory **[${id}]** not found.`,
|
|
309
|
+
),
|
|
310
|
+
ctx.setCommitted,
|
|
311
|
+
ctx.setAllMessages,
|
|
312
|
+
);
|
|
313
|
+
return true;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (t === "/memory clear") {
|
|
317
|
+
clearRepoMemory(ctx.repoPath);
|
|
318
|
+
pushMsg(
|
|
319
|
+
makeMsg("All memories cleared for this repo."),
|
|
320
|
+
ctx.setCommitted,
|
|
321
|
+
ctx.setAllMessages,
|
|
322
|
+
);
|
|
323
|
+
return true;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
@@ -354,7 +354,7 @@ type ActiveInvestigation = {
|
|
|
354
354
|
startTime: number;
|
|
355
355
|
};
|
|
356
356
|
|
|
357
|
-
export function
|
|
357
|
+
export function RunRunner({
|
|
358
358
|
cmd,
|
|
359
359
|
repoPath,
|
|
360
360
|
clean,
|
|
@@ -460,7 +460,6 @@ export function WatchRunner({
|
|
|
460
460
|
lensContext = `Overview: ${lensFile.overview}
|
|
461
461
|
|
|
462
462
|
Important folders: ${lensFile.importantFolders.join(", ")}
|
|
463
|
-
${lensFile.securityIssues.length > 0 ? `\nKnown security issues:\n${lensFile.securityIssues.map((s) => `- ${s}`).join("\n")}` : ""}
|
|
464
463
|
${lensFile.suggestions.length > 0 ? `\nProject suggestions:\n${lensFile.suggestions.map((s) => `- ${s}`).join("\n")}` : ""}`;
|
|
465
464
|
}
|
|
466
465
|
}
|
package/src/index.tsx
CHANGED
|
@@ -7,7 +7,7 @@ import { InitCommand } from "./commands/provider";
|
|
|
7
7
|
import { ReviewCommand } from "./commands/review";
|
|
8
8
|
import { TaskCommand } from "./commands/task";
|
|
9
9
|
import { ChatCommand } from "./commands/chat";
|
|
10
|
-
import {
|
|
10
|
+
import { RunCommand } from "./commands/run";
|
|
11
11
|
import { TimelineCommand } from "./commands/timeline";
|
|
12
12
|
import { CommitCommand } from "./commands/commit";
|
|
13
13
|
import { registerBuiltins } from "./utils/tools/builtins";
|
|
@@ -19,8 +19,7 @@ await loadAddons();
|
|
|
19
19
|
const program = new Command();
|
|
20
20
|
|
|
21
21
|
program
|
|
22
|
-
.command("
|
|
23
|
-
.alias("repo")
|
|
22
|
+
.command("repo <url>")
|
|
24
23
|
.description("Analyze a remote repository")
|
|
25
24
|
.action((url) => {
|
|
26
25
|
render(<RepoCommand url={url} />);
|
|
@@ -34,16 +33,14 @@ program
|
|
|
34
33
|
});
|
|
35
34
|
|
|
36
35
|
program
|
|
37
|
-
.command("
|
|
38
|
-
.alias("review")
|
|
36
|
+
.command("review [path]")
|
|
39
37
|
.description("Review a local codebase")
|
|
40
38
|
.action((inputPath) => {
|
|
41
39
|
render(<ReviewCommand path={inputPath ?? "."} />);
|
|
42
40
|
});
|
|
43
41
|
|
|
44
42
|
program
|
|
45
|
-
.command("
|
|
46
|
-
.alias("task")
|
|
43
|
+
.command("task <text>")
|
|
47
44
|
.description("Apply a natural language change to the codebase")
|
|
48
45
|
.option("-p, --path <path>", "Path to the repo", ".")
|
|
49
46
|
.action((text: string, opts: { path: string }) => {
|
|
@@ -51,8 +48,7 @@ program
|
|
|
51
48
|
});
|
|
52
49
|
|
|
53
50
|
program
|
|
54
|
-
.command("
|
|
55
|
-
.alias("chat")
|
|
51
|
+
.command("chat")
|
|
56
52
|
.description("Chat with your codebase — ask questions or make changes")
|
|
57
53
|
.option("-p, --path <path>", "Path to the repo", ".")
|
|
58
54
|
.action((opts: { path: string }) => {
|
|
@@ -60,8 +56,7 @@ program
|
|
|
60
56
|
});
|
|
61
57
|
|
|
62
58
|
program
|
|
63
|
-
.command("
|
|
64
|
-
.alias("timeline")
|
|
59
|
+
.command("timeline")
|
|
65
60
|
.description(
|
|
66
61
|
"Explore your code history — see commits, changes, and evolution",
|
|
67
62
|
)
|
|
@@ -71,8 +66,7 @@ program
|
|
|
71
66
|
});
|
|
72
67
|
|
|
73
68
|
program
|
|
74
|
-
.command("
|
|
75
|
-
.alias("commit")
|
|
69
|
+
.command("commit [files...]")
|
|
76
70
|
.description(
|
|
77
71
|
"Generate a smart conventional commit message from staged changes or specific files",
|
|
78
72
|
)
|
|
@@ -109,9 +103,10 @@ program
|
|
|
109
103
|
);
|
|
110
104
|
|
|
111
105
|
program
|
|
112
|
-
.command("
|
|
113
|
-
.
|
|
114
|
-
|
|
106
|
+
.command("run <cmd>")
|
|
107
|
+
.description(
|
|
108
|
+
"Run your dev server. Lens detects and fixes errors automatically",
|
|
109
|
+
)
|
|
115
110
|
.option("-p, --path <path>", "Path to the repo", ".")
|
|
116
111
|
.option("--clean", "Only show AI suggestions, hide raw logs")
|
|
117
112
|
.option("--fix-all", "Auto-apply fixes as errors are detected")
|
|
@@ -129,7 +124,7 @@ program
|
|
|
129
124
|
},
|
|
130
125
|
) => {
|
|
131
126
|
render(
|
|
132
|
-
<
|
|
127
|
+
<RunCommand
|
|
133
128
|
cmd={cmd}
|
|
134
129
|
path={opts.path}
|
|
135
130
|
clean={opts.clean ?? false}
|
package/src/prompts/system.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ImportantFile } from "../types/repo";
|
|
2
|
+
import type { Intent } from "../utils/intentClassifier";
|
|
2
3
|
|
|
3
4
|
export function buildSystemPrompt(
|
|
4
5
|
files: ImportantFile[],
|
|
@@ -12,25 +13,25 @@ export function buildSystemPrompt(
|
|
|
12
13
|
const tools = toolsSection ?? BUILTIN_TOOLS_SECTION;
|
|
13
14
|
|
|
14
15
|
return `You are an expert software engineer assistant with access to the user's codebase and tools.
|
|
15
|
-
|
|
16
|
+
|
|
16
17
|
${tools}
|
|
17
|
-
|
|
18
|
+
|
|
18
19
|
## MEMORY OPERATIONS
|
|
19
|
-
|
|
20
|
+
|
|
20
21
|
You can save and delete memories at any time by emitting these tags alongside your normal response.
|
|
21
22
|
They are stripped before display — the user will not see the raw tags.
|
|
22
|
-
|
|
23
|
+
|
|
23
24
|
### memory-add — save something important to long-term memory for this repo
|
|
24
25
|
<memory-add>User prefers TypeScript strict mode in all new files</memory-add>
|
|
25
|
-
|
|
26
|
+
|
|
26
27
|
### memory-delete — delete a memory by its ID (shown in brackets like [abc123])
|
|
27
28
|
<memory-delete>abc123</memory-delete>
|
|
28
|
-
|
|
29
|
+
|
|
29
30
|
Use memory-add when the user asks you to remember something, or when you learn something project-specific that would be useful in future sessions.
|
|
30
31
|
Use memory-delete when the user asks you to forget something or a memory is outdated.
|
|
31
|
-
|
|
32
|
+
|
|
32
33
|
## RULES
|
|
33
|
-
|
|
34
|
+
|
|
34
35
|
1. ONE tool per response — emit the XML tag, then stop. Never chain tools in one response except when scaffolding (see below).
|
|
35
36
|
2. NEVER call a tool more than once for the same path in a session. If write-file or shell returned a result, it succeeded. Move on immediately.
|
|
36
37
|
3. NEVER write the same file twice in one session. One write per file, period. If you already wrote it, it is done.
|
|
@@ -48,15 +49,15 @@ Use memory-delete when the user asks you to forget something or a memory is outd
|
|
|
48
49
|
15. When explaining how to use a tool in text, use [tag] bracket notation — NEVER emit a real XML tool tag as part of an explanation.
|
|
49
50
|
16. NEVER use markdown formatting in plain text responses — no bold, no headings, no bullet points. Only use fenced code blocks when showing actual code.
|
|
50
51
|
17. When scaffolding multiple files, emit ONE write-file tag per response and wait for the result before writing the next file.
|
|
51
|
-
|
|
52
|
+
|
|
52
53
|
## ADDON FORMAT
|
|
53
|
-
|
|
54
|
+
|
|
54
55
|
All addons use defineTool from @ridit/lens-sdk. The ONLY correct format is:
|
|
55
|
-
|
|
56
|
+
|
|
56
57
|
\`\`\`js
|
|
57
58
|
const { defineTool } = require("@ridit/lens-sdk");
|
|
58
59
|
const { execSync } = require("child_process");
|
|
59
|
-
|
|
60
|
+
|
|
60
61
|
defineTool({
|
|
61
62
|
name: "tool-name",
|
|
62
63
|
description: "what it does",
|
|
@@ -72,26 +73,146 @@ defineTool({
|
|
|
72
73
|
},
|
|
73
74
|
});
|
|
74
75
|
\`\`\`
|
|
75
|
-
|
|
76
|
+
|
|
76
77
|
NEVER use module.exports, registerTool, ctx.tools.shell, or any other format. See addons/run-tests.js for a full working example.
|
|
77
|
-
|
|
78
|
+
|
|
78
79
|
## SCAFFOLDING
|
|
79
|
-
|
|
80
|
+
|
|
80
81
|
When creating multiple files, emit ONE write-file per response and wait for each result:
|
|
81
|
-
|
|
82
|
+
|
|
82
83
|
<write-file>
|
|
83
84
|
{"path": "myapp/package.json", "content": "..."}
|
|
84
85
|
</write-file>
|
|
85
|
-
|
|
86
|
+
|
|
86
87
|
Wait for result, then emit the next file. Never chain write-file tags when content is complex.
|
|
87
|
-
|
|
88
|
+
|
|
88
89
|
## CODEBASE
|
|
89
|
-
|
|
90
|
+
|
|
90
91
|
${fileList.length > 0 ? fileList : "(no files indexed)"}
|
|
91
|
-
|
|
92
|
+
|
|
92
93
|
${memorySummary}`;
|
|
93
94
|
}
|
|
94
95
|
|
|
96
|
+
export function buildBuiltinToolsSection(intent: Intent = "any"): string {
|
|
97
|
+
const isReadonly = intent === "readonly";
|
|
98
|
+
|
|
99
|
+
const readTools = `### 1. fetch — load a URL
|
|
100
|
+
<fetch>https://example.com</fetch>
|
|
101
|
+
|
|
102
|
+
### 2. read-file — read a single file from the repo
|
|
103
|
+
<read-file>src/foo.ts</read-file>
|
|
104
|
+
|
|
105
|
+
### 3. read-files — read multiple files at once
|
|
106
|
+
<read-files>
|
|
107
|
+
["src/foo.ts", "src/bar.ts"]
|
|
108
|
+
</read-files>
|
|
109
|
+
|
|
110
|
+
### 4. read-folder — list contents of a folder (one level deep)
|
|
111
|
+
<read-folder>src/components</read-folder>
|
|
112
|
+
|
|
113
|
+
### 5. grep — search for a pattern across files
|
|
114
|
+
<grep>
|
|
115
|
+
{"pattern": "ChatRunner", "glob": "src/**/*.tsx"}
|
|
116
|
+
</grep>
|
|
117
|
+
|
|
118
|
+
### 6. search — search the internet
|
|
119
|
+
<search>how to use React useEffect cleanup</search>`;
|
|
120
|
+
|
|
121
|
+
const writeTools = `### 7. shell — run a terminal command (NOT for filesystem inspection)
|
|
122
|
+
<shell>node -v</shell>
|
|
123
|
+
|
|
124
|
+
### 8. write-file — create or overwrite a file (COMPLETE content only)
|
|
125
|
+
<write-file>
|
|
126
|
+
{"path": "data/output.csv", "content": "col1,col2\\nval1,val2"}
|
|
127
|
+
</write-file>
|
|
128
|
+
|
|
129
|
+
### 9. delete-file — permanently delete a single file
|
|
130
|
+
<delete-file>src/old-component.tsx</delete-file>
|
|
131
|
+
|
|
132
|
+
### 10. delete-folder — permanently delete a folder and all its contents
|
|
133
|
+
<delete-folder>src/legacy</delete-folder>
|
|
134
|
+
|
|
135
|
+
### 11. open-url — open a URL in the user's default browser
|
|
136
|
+
<open-url>https://github.com/owner/repo</open-url>
|
|
137
|
+
|
|
138
|
+
### 12. generate-pdf — generate a PDF from markdown-style content
|
|
139
|
+
<generate-pdf>
|
|
140
|
+
{"path": "output/report.pdf", "content": "# Title\\n\\nBody text."}
|
|
141
|
+
</generate-pdf>
|
|
142
|
+
|
|
143
|
+
### 13. clone — clone a GitHub repo
|
|
144
|
+
<clone>https://github.com/owner/repo</clone>
|
|
145
|
+
|
|
146
|
+
### 14. changes — propose code edits shown as a diff for user approval
|
|
147
|
+
<changes>
|
|
148
|
+
{"summary": "what changed and why", "patches": [{"path": "src/foo.ts", "content": "COMPLETE file content", "isNew": false}]}
|
|
149
|
+
</changes>`;
|
|
150
|
+
|
|
151
|
+
if (isReadonly) {
|
|
152
|
+
return `## TOOLS
|
|
153
|
+
|
|
154
|
+
You have 6 tools available for this read-only request. Do NOT attempt to write, delete, or run shell commands — those tools are not available right now.
|
|
155
|
+
|
|
156
|
+
${readTools}`;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return `## TOOLS
|
|
160
|
+
|
|
161
|
+
You have exactly 14 tools. To use a tool you MUST wrap it in the exact XML tags shown below — no other format will work.
|
|
162
|
+
|
|
163
|
+
### 1. fetch — load a URL
|
|
164
|
+
<fetch>https://example.com</fetch>
|
|
165
|
+
|
|
166
|
+
### 2. shell — run a terminal command (NOT for filesystem inspection)
|
|
167
|
+
<shell>node -v</shell>
|
|
168
|
+
|
|
169
|
+
### 3. read-file — read a single file from the repo
|
|
170
|
+
<read-file>src/foo.ts</read-file>
|
|
171
|
+
|
|
172
|
+
### 4. read-files — read multiple files at once
|
|
173
|
+
<read-files>
|
|
174
|
+
["src/foo.ts", "src/bar.ts"]
|
|
175
|
+
</read-files>
|
|
176
|
+
|
|
177
|
+
### 5. read-folder — list contents of a folder (one level deep)
|
|
178
|
+
<read-folder>src/components</read-folder>
|
|
179
|
+
|
|
180
|
+
### 6. grep — search for a pattern across files
|
|
181
|
+
<grep>
|
|
182
|
+
{"pattern": "ChatRunner", "glob": "src/**/*.tsx"}
|
|
183
|
+
</grep>
|
|
184
|
+
|
|
185
|
+
### 7. write-file — create or overwrite a file (COMPLETE content only)
|
|
186
|
+
<write-file>
|
|
187
|
+
{"path": "data/output.csv", "content": "col1,col2\\nval1,val2"}
|
|
188
|
+
</write-file>
|
|
189
|
+
|
|
190
|
+
### 8. delete-file — permanently delete a single file
|
|
191
|
+
<delete-file>src/old-component.tsx</delete-file>
|
|
192
|
+
|
|
193
|
+
### 9. delete-folder — permanently delete a folder and all its contents
|
|
194
|
+
<delete-folder>src/legacy</delete-folder>
|
|
195
|
+
|
|
196
|
+
### 10. open-url — open a URL in the user's default browser
|
|
197
|
+
<open-url>https://github.com/owner/repo</open-url>
|
|
198
|
+
|
|
199
|
+
### 11. generate-pdf — generate a PDF from markdown-style content
|
|
200
|
+
<generate-pdf>
|
|
201
|
+
{"path": "output/report.pdf", "content": "# Title\\n\\nBody text."}
|
|
202
|
+
</generate-pdf>
|
|
203
|
+
|
|
204
|
+
### 12. search — search the internet
|
|
205
|
+
<search>how to use React useEffect cleanup</search>
|
|
206
|
+
|
|
207
|
+
### 13. clone — clone a GitHub repo
|
|
208
|
+
<clone>https://github.com/owner/repo</clone>
|
|
209
|
+
|
|
210
|
+
### 14. changes — propose code edits shown as a diff for user approval
|
|
211
|
+
<changes>
|
|
212
|
+
{"summary": "what changed and why", "patches": [{"path": "src/foo.ts", "content": "COMPLETE file content", "isNew": false}]}
|
|
213
|
+
</changes>`;
|
|
214
|
+
}
|
|
215
|
+
|
|
95
216
|
const BUILTIN_TOOLS_SECTION = `## TOOLS
|
|
96
217
|
|
|
97
218
|
You have exactly fourteen tools. Use ONLY the XML tags shown below.
|
package/src/tools/chart.ts
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
//
|
|
6
|
-
|
|
7
1
|
import type { Tool } from "@ridit/lens-sdk";
|
|
8
2
|
|
|
9
3
|
type ChartType = "bar" | "line" | "sparkline";
|
|
@@ -13,9 +7,7 @@ interface ChartInput {
|
|
|
13
7
|
title?: string;
|
|
14
8
|
labels?: string[];
|
|
15
9
|
values: number[];
|
|
16
|
-
/** For line charts: height in rows. Default 10. */
|
|
17
10
|
height?: number;
|
|
18
|
-
/** Bar fill character. Default "█" */
|
|
19
11
|
fill?: string;
|
|
20
12
|
}
|
|
21
13
|
|