@ridit/lens 0.3.4 → 0.3.5

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.
@@ -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 WatchRunner({
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 { WatchCommand } from "./commands/watch";
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("stalk <url>")
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("judge [path]")
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("cook <text>")
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("vibe")
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("history")
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("crimes [files...]")
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("watch <cmd>")
113
- .alias("spy")
114
- .description("Watch a dev command and get AI suggestions for errors")
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
- <WatchCommand
127
+ <RunCommand
133
128
  cmd={cmd}
134
129
  path={opts.path}
135
130
  clean={opts.clean ?? false}
@@ -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