@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,399 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { Box, Static, Text, useStdout } from "ink";
|
|
3
|
-
import Spinner from "ink-spinner";
|
|
4
|
-
import { TextArea } from "./TextArea";
|
|
5
|
-
import { ACCENT, GREEN, RED } from "../../colors";
|
|
6
|
-
import { DiffViewer } from "../repo/DiffViewer";
|
|
7
|
-
import { StaticMessage } from "./ChatMessage";
|
|
8
|
-
import type { DiffLine, FilePatch } from "../repo/DiffViewer";
|
|
9
|
-
import type { Message, ToolCall, ChatStage } from "../../types/chat";
|
|
10
|
-
|
|
11
|
-
function History({ committed }: { committed: Message[] }) {
|
|
12
|
-
return (
|
|
13
|
-
<Static items={committed}>
|
|
14
|
-
{(msg, i) => <StaticMessage key={i} msg={msg} />}
|
|
15
|
-
</Static>
|
|
16
|
-
);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function Hint({ text }: { text: string }) {
|
|
20
|
-
return (
|
|
21
|
-
<Text color="gray" dimColor>
|
|
22
|
-
{text}
|
|
23
|
-
</Text>
|
|
24
|
-
);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// ── PermissionPrompt ──────────────────────────────────────────────────────────
|
|
28
|
-
//
|
|
29
|
-
// Works with both the old explicit ToolCall union and the new generic
|
|
30
|
-
// { type, _label, _display } shape produced by the plugin system.
|
|
31
|
-
|
|
32
|
-
export function PermissionPrompt({
|
|
33
|
-
tool,
|
|
34
|
-
onDecide,
|
|
35
|
-
}: {
|
|
36
|
-
tool: ToolCall | { type: string; _label: string; _display: string };
|
|
37
|
-
onDecide: (approved: boolean) => void;
|
|
38
|
-
}) {
|
|
39
|
-
let icon: string;
|
|
40
|
-
let label: string;
|
|
41
|
-
let value: string;
|
|
42
|
-
|
|
43
|
-
// Generic plugin tool shape
|
|
44
|
-
if ("_label" in tool) {
|
|
45
|
-
const iconMap: Record<string, string> = {
|
|
46
|
-
run: "$",
|
|
47
|
-
fetch: "~>",
|
|
48
|
-
read: "r",
|
|
49
|
-
write: "w",
|
|
50
|
-
delete: "x",
|
|
51
|
-
"delete folder": "X",
|
|
52
|
-
open: "↗",
|
|
53
|
-
pdf: "P",
|
|
54
|
-
search: "?",
|
|
55
|
-
folder: "d",
|
|
56
|
-
grep: "/",
|
|
57
|
-
clone: "↓",
|
|
58
|
-
query: "⌗",
|
|
59
|
-
};
|
|
60
|
-
icon = iconMap[tool._label] ?? "·";
|
|
61
|
-
label = tool._label;
|
|
62
|
-
value = tool._display;
|
|
63
|
-
} else {
|
|
64
|
-
// Legacy explicit ToolCall union
|
|
65
|
-
if (tool.type === "shell") {
|
|
66
|
-
icon = "$";
|
|
67
|
-
label = "run";
|
|
68
|
-
value = tool.command;
|
|
69
|
-
} else if (tool.type === "fetch") {
|
|
70
|
-
icon = "~>";
|
|
71
|
-
label = "fetch";
|
|
72
|
-
value = tool.url;
|
|
73
|
-
} else if (tool.type === "read-file") {
|
|
74
|
-
icon = "r";
|
|
75
|
-
label = "read";
|
|
76
|
-
value = tool.filePath;
|
|
77
|
-
} else if (tool.type === "read-folder") {
|
|
78
|
-
icon = "d";
|
|
79
|
-
label = "folder";
|
|
80
|
-
value = tool.folderPath;
|
|
81
|
-
} else if (tool.type === "grep") {
|
|
82
|
-
icon = "/";
|
|
83
|
-
label = "grep";
|
|
84
|
-
value = `${tool.pattern} ${tool.glob}`;
|
|
85
|
-
} else if (tool.type === "delete-file") {
|
|
86
|
-
icon = "x";
|
|
87
|
-
label = "delete";
|
|
88
|
-
value = tool.filePath;
|
|
89
|
-
} else if (tool.type === "delete-folder") {
|
|
90
|
-
icon = "X";
|
|
91
|
-
label = "delete folder";
|
|
92
|
-
value = tool.folderPath;
|
|
93
|
-
} else if (tool.type === "open-url") {
|
|
94
|
-
icon = "↗";
|
|
95
|
-
label = "open";
|
|
96
|
-
value = tool.url;
|
|
97
|
-
} else if (tool.type === "generate-pdf") {
|
|
98
|
-
icon = "P";
|
|
99
|
-
label = "pdf";
|
|
100
|
-
value = tool.filePath;
|
|
101
|
-
} else if (tool.type === "write-file") {
|
|
102
|
-
icon = "w";
|
|
103
|
-
label = "write";
|
|
104
|
-
value = `${tool.filePath} (${tool.fileContent.length} bytes)`;
|
|
105
|
-
} else {
|
|
106
|
-
icon = "?";
|
|
107
|
-
label = "search";
|
|
108
|
-
value = (tool as any).query ?? "";
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return (
|
|
113
|
-
<Box flexDirection="column" marginY={1}>
|
|
114
|
-
<Box gap={1}>
|
|
115
|
-
<Text color={ACCENT}>{icon}</Text>
|
|
116
|
-
<Text color="gray">{label}</Text>
|
|
117
|
-
<Text color="white">{value}</Text>
|
|
118
|
-
</Box>
|
|
119
|
-
<Box gap={1} marginLeft={2}>
|
|
120
|
-
<Text color="gray">y/enter allow · n/esc deny</Text>
|
|
121
|
-
</Box>
|
|
122
|
-
</Box>
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
export function InputBox({
|
|
127
|
-
value,
|
|
128
|
-
onChange,
|
|
129
|
-
onSubmit,
|
|
130
|
-
inputKey,
|
|
131
|
-
}: {
|
|
132
|
-
value: string;
|
|
133
|
-
onChange: (v: string) => void;
|
|
134
|
-
onSubmit: (v: string) => void;
|
|
135
|
-
inputKey?: number;
|
|
136
|
-
}) {
|
|
137
|
-
const { stdout } = useStdout();
|
|
138
|
-
const cols = stdout?.columns ?? 80;
|
|
139
|
-
const rule = "─".repeat(Math.max(1, cols));
|
|
140
|
-
|
|
141
|
-
return (
|
|
142
|
-
<Box flexDirection="column" marginTop={1}>
|
|
143
|
-
<Text color="gray" dimColor>{rule}</Text>
|
|
144
|
-
<Box gap={1}>
|
|
145
|
-
<Text color={ACCENT}>{">"}</Text>
|
|
146
|
-
<TextArea
|
|
147
|
-
key={inputKey}
|
|
148
|
-
value={value}
|
|
149
|
-
onChange={onChange}
|
|
150
|
-
onSubmit={onSubmit}
|
|
151
|
-
placeholder="ask anything..."
|
|
152
|
-
/>
|
|
153
|
-
</Box>
|
|
154
|
-
<Text color="gray" dimColor>{rule}</Text>
|
|
155
|
-
</Box>
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
export function TypewriterText({
|
|
160
|
-
text,
|
|
161
|
-
color = ACCENT,
|
|
162
|
-
speed = 38,
|
|
163
|
-
}: {
|
|
164
|
-
text: string;
|
|
165
|
-
color?: string;
|
|
166
|
-
speed?: number;
|
|
167
|
-
}) {
|
|
168
|
-
const [displayed, setDisplayed] = React.useState("");
|
|
169
|
-
const [target, setTarget] = React.useState(text);
|
|
170
|
-
|
|
171
|
-
React.useEffect(() => {
|
|
172
|
-
setDisplayed("");
|
|
173
|
-
setTarget(text);
|
|
174
|
-
}, [text]);
|
|
175
|
-
|
|
176
|
-
React.useEffect(() => {
|
|
177
|
-
if (displayed.length >= target.length) return;
|
|
178
|
-
const t = setTimeout(
|
|
179
|
-
() => setDisplayed(target.slice(0, displayed.length + 1)),
|
|
180
|
-
speed,
|
|
181
|
-
);
|
|
182
|
-
return () => clearTimeout(t);
|
|
183
|
-
}, [displayed, target, speed]);
|
|
184
|
-
|
|
185
|
-
return <Text color={color}>{displayed}</Text>;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
export function ShortcutBar({
|
|
189
|
-
autoApprove,
|
|
190
|
-
forceApprove,
|
|
191
|
-
}: {
|
|
192
|
-
autoApprove?: boolean;
|
|
193
|
-
forceApprove?: boolean;
|
|
194
|
-
}) {
|
|
195
|
-
return (
|
|
196
|
-
<Box gap={3} marginTop={0}>
|
|
197
|
-
<Text color="gray" dimColor>
|
|
198
|
-
enter send · alt+enter newline · ^w del word · ^c exit
|
|
199
|
-
</Text>
|
|
200
|
-
{forceApprove ? (
|
|
201
|
-
<Text color={RED}>⚡⚡ force-all</Text>
|
|
202
|
-
) : (
|
|
203
|
-
<Text color={autoApprove ? GREEN : "gray"} dimColor={!autoApprove}>
|
|
204
|
-
{autoApprove ? "⚡ auto" : "/auto"}
|
|
205
|
-
</Text>
|
|
206
|
-
)}
|
|
207
|
-
</Box>
|
|
208
|
-
);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
export function CloneOfferView({
|
|
212
|
-
stage,
|
|
213
|
-
committed,
|
|
214
|
-
}: {
|
|
215
|
-
stage: Extract<ChatStage, { type: "clone-offer" }>;
|
|
216
|
-
committed: Message[];
|
|
217
|
-
}) {
|
|
218
|
-
return (
|
|
219
|
-
<Box flexDirection="column">
|
|
220
|
-
<History committed={committed} />
|
|
221
|
-
<Box flexDirection="column" marginY={1}>
|
|
222
|
-
<Box gap={1}>
|
|
223
|
-
<Text color={ACCENT}>*</Text>
|
|
224
|
-
<Text color="white">clone </Text>
|
|
225
|
-
<Text color={ACCENT}>{stage.repoUrl}</Text>
|
|
226
|
-
<Text color="white">?</Text>
|
|
227
|
-
</Box>
|
|
228
|
-
<Hint text=" y/enter clone · n/esc skip" />
|
|
229
|
-
</Box>
|
|
230
|
-
</Box>
|
|
231
|
-
);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
export function CloningView({
|
|
235
|
-
stage,
|
|
236
|
-
committed,
|
|
237
|
-
}: {
|
|
238
|
-
stage: Extract<ChatStage, { type: "cloning" }>;
|
|
239
|
-
committed: Message[];
|
|
240
|
-
}) {
|
|
241
|
-
return (
|
|
242
|
-
<Box flexDirection="column">
|
|
243
|
-
<History committed={committed} />
|
|
244
|
-
<Box gap={1} marginTop={1}>
|
|
245
|
-
<Text color={ACCENT}>
|
|
246
|
-
<Spinner />
|
|
247
|
-
</Text>
|
|
248
|
-
<Text color="gray">cloning </Text>
|
|
249
|
-
<Text color={ACCENT}>{stage.repoUrl}</Text>
|
|
250
|
-
<Text color="gray">...</Text>
|
|
251
|
-
</Box>
|
|
252
|
-
</Box>
|
|
253
|
-
);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
export function CloneExistsView({
|
|
257
|
-
stage,
|
|
258
|
-
committed,
|
|
259
|
-
}: {
|
|
260
|
-
stage: Extract<ChatStage, { type: "clone-exists" }>;
|
|
261
|
-
committed: Message[];
|
|
262
|
-
}) {
|
|
263
|
-
return (
|
|
264
|
-
<Box flexDirection="column">
|
|
265
|
-
<History committed={committed} />
|
|
266
|
-
<Box flexDirection="column" marginY={1}>
|
|
267
|
-
<Box gap={1}>
|
|
268
|
-
<Text color="yellow">!</Text>
|
|
269
|
-
<Text color="gray">already cloned at </Text>
|
|
270
|
-
<Text color="white">{stage.repoPath}</Text>
|
|
271
|
-
</Box>
|
|
272
|
-
<Hint text=" y re-clone · n use existing" />
|
|
273
|
-
</Box>
|
|
274
|
-
</Box>
|
|
275
|
-
);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
export function CloneDoneView({
|
|
279
|
-
stage,
|
|
280
|
-
committed,
|
|
281
|
-
}: {
|
|
282
|
-
stage: Extract<ChatStage, { type: "clone-done" }>;
|
|
283
|
-
committed: Message[];
|
|
284
|
-
}) {
|
|
285
|
-
const repoName = stage.repoUrl.split("/").pop() ?? "repo";
|
|
286
|
-
return (
|
|
287
|
-
<Box flexDirection="column">
|
|
288
|
-
<History committed={committed} />
|
|
289
|
-
<Box flexDirection="column" marginY={1}>
|
|
290
|
-
<Box gap={1}>
|
|
291
|
-
<Text color={GREEN}>✓</Text>
|
|
292
|
-
<Text color="white" bold>
|
|
293
|
-
{repoName}
|
|
294
|
-
</Text>
|
|
295
|
-
<Text color="gray">
|
|
296
|
-
cloned · {stage.fileCount} files · {stage.destPath}
|
|
297
|
-
</Text>
|
|
298
|
-
</Box>
|
|
299
|
-
<Hint text=" enter/esc continue" />
|
|
300
|
-
</Box>
|
|
301
|
-
</Box>
|
|
302
|
-
);
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
export function CloneErrorView({
|
|
306
|
-
stage,
|
|
307
|
-
committed,
|
|
308
|
-
}: {
|
|
309
|
-
stage: Extract<ChatStage, { type: "clone-error" }>;
|
|
310
|
-
committed: Message[];
|
|
311
|
-
}) {
|
|
312
|
-
return (
|
|
313
|
-
<Box flexDirection="column">
|
|
314
|
-
<History committed={committed} />
|
|
315
|
-
<Box flexDirection="column" marginY={1}>
|
|
316
|
-
<Box gap={1}>
|
|
317
|
-
<Text color={RED}>✗</Text>
|
|
318
|
-
<Text color={RED}>{stage.message}</Text>
|
|
319
|
-
</Box>
|
|
320
|
-
<Hint text=" enter/esc continue" />
|
|
321
|
-
</Box>
|
|
322
|
-
</Box>
|
|
323
|
-
);
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
export function PreviewView({
|
|
327
|
-
stage,
|
|
328
|
-
committed,
|
|
329
|
-
}: {
|
|
330
|
-
stage: Extract<ChatStage, { type: "preview" }>;
|
|
331
|
-
committed: Message[];
|
|
332
|
-
}) {
|
|
333
|
-
const { patches, diffLines, scrollOffset } = stage;
|
|
334
|
-
return (
|
|
335
|
-
<Box flexDirection="column">
|
|
336
|
-
<History committed={committed} />
|
|
337
|
-
<Box gap={1} marginTop={1}>
|
|
338
|
-
<Text color={ACCENT}>*</Text>
|
|
339
|
-
<Text color="white" bold>
|
|
340
|
-
proposed changes
|
|
341
|
-
</Text>
|
|
342
|
-
<Text color="gray">
|
|
343
|
-
({patches.length} file{patches.length !== 1 ? "s" : ""})
|
|
344
|
-
</Text>
|
|
345
|
-
</Box>
|
|
346
|
-
<Box flexDirection="column" marginLeft={2} marginTop={1}>
|
|
347
|
-
{patches.map((p) => (
|
|
348
|
-
<Box key={p.path} gap={1}>
|
|
349
|
-
<Text color={p.isNew ? GREEN : "yellow"}>
|
|
350
|
-
{p.isNew ? "+" : "~"}
|
|
351
|
-
</Text>
|
|
352
|
-
<Text color={p.isNew ? GREEN : "yellow"}>{p.path}</Text>
|
|
353
|
-
{p.isNew && (
|
|
354
|
-
<Text color="gray" dimColor>
|
|
355
|
-
new
|
|
356
|
-
</Text>
|
|
357
|
-
)}
|
|
358
|
-
</Box>
|
|
359
|
-
))}
|
|
360
|
-
</Box>
|
|
361
|
-
<DiffViewer
|
|
362
|
-
patches={patches}
|
|
363
|
-
diffs={diffLines}
|
|
364
|
-
scrollOffset={scrollOffset}
|
|
365
|
-
/>
|
|
366
|
-
<Hint text="↑↓ scroll · enter/a apply · s/esc skip" />
|
|
367
|
-
</Box>
|
|
368
|
-
);
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
export function ViewingFileView({
|
|
372
|
-
stage,
|
|
373
|
-
committed,
|
|
374
|
-
}: {
|
|
375
|
-
stage: Extract<ChatStage, { type: "viewing-file" }>;
|
|
376
|
-
committed: Message[];
|
|
377
|
-
}) {
|
|
378
|
-
const { file, diffLines, scrollOffset } = stage;
|
|
379
|
-
return (
|
|
380
|
-
<Box flexDirection="column">
|
|
381
|
-
<History committed={committed} />
|
|
382
|
-
<Box gap={1} marginTop={1}>
|
|
383
|
-
<Text color={ACCENT}>r</Text>
|
|
384
|
-
<Text color="white" bold>
|
|
385
|
-
{file.path}
|
|
386
|
-
</Text>
|
|
387
|
-
<Text color="gray" dimColor>
|
|
388
|
-
{file.isNew ? "new" : "modified"}
|
|
389
|
-
</Text>
|
|
390
|
-
</Box>
|
|
391
|
-
<DiffViewer
|
|
392
|
-
patches={[file.patch]}
|
|
393
|
-
diffs={[diffLines]}
|
|
394
|
-
scrollOffset={scrollOffset}
|
|
395
|
-
/>
|
|
396
|
-
<Hint text="↑↓ scroll · enter/esc back" />
|
|
397
|
-
</Box>
|
|
398
|
-
);
|
|
399
|
-
}
|