@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
package/src/tools/files.ts
DELETED
|
@@ -1,253 +0,0 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
import {
|
|
3
|
-
existsSync,
|
|
4
|
-
mkdirSync,
|
|
5
|
-
readdirSync,
|
|
6
|
-
readFileSync,
|
|
7
|
-
statSync,
|
|
8
|
-
writeFileSync,
|
|
9
|
-
} from "fs";
|
|
10
|
-
import type { FilePatch } from "../components/repo/DiffViewer";
|
|
11
|
-
|
|
12
|
-
const SKIP_DIRS = new Set([
|
|
13
|
-
"node_modules",
|
|
14
|
-
".git",
|
|
15
|
-
"dist",
|
|
16
|
-
"build",
|
|
17
|
-
".next",
|
|
18
|
-
"out",
|
|
19
|
-
"coverage",
|
|
20
|
-
"__pycache__",
|
|
21
|
-
".venv",
|
|
22
|
-
"venv",
|
|
23
|
-
]);
|
|
24
|
-
|
|
25
|
-
export function walkDir(
|
|
26
|
-
dir: string,
|
|
27
|
-
base = dir,
|
|
28
|
-
results: string[] = [],
|
|
29
|
-
): string[] {
|
|
30
|
-
let entries: string[];
|
|
31
|
-
try {
|
|
32
|
-
entries = readdirSync(dir, { encoding: "utf-8" });
|
|
33
|
-
} catch {
|
|
34
|
-
return results;
|
|
35
|
-
}
|
|
36
|
-
for (const entry of entries) {
|
|
37
|
-
if (results.length >= 100) return results;
|
|
38
|
-
if (SKIP_DIRS.has(entry)) continue;
|
|
39
|
-
const full = path.join(dir, entry);
|
|
40
|
-
const rel = path.relative(base, full).replace(/\\/g, "/");
|
|
41
|
-
let isDir = false;
|
|
42
|
-
try {
|
|
43
|
-
isDir = statSync(full).isDirectory();
|
|
44
|
-
} catch {
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
if (isDir) walkDir(full, base, results);
|
|
48
|
-
else results.push(rel);
|
|
49
|
-
}
|
|
50
|
-
return results;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function applyPatches(repoPath: string, patches: FilePatch[]): void {
|
|
54
|
-
for (const patch of patches) {
|
|
55
|
-
const fullPath = path.join(repoPath, patch.path);
|
|
56
|
-
const dir = path.dirname(fullPath);
|
|
57
|
-
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
58
|
-
writeFileSync(fullPath, patch.content, "utf-8");
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export function readFile(filePath: string, repoPath: string): string {
|
|
63
|
-
const candidates = path.isAbsolute(filePath)
|
|
64
|
-
? [filePath]
|
|
65
|
-
: [filePath, path.join(repoPath, filePath)];
|
|
66
|
-
for (const candidate of candidates) {
|
|
67
|
-
if (existsSync(candidate)) {
|
|
68
|
-
try {
|
|
69
|
-
const content = readFileSync(candidate, "utf-8");
|
|
70
|
-
const lines = content.split("\n").length;
|
|
71
|
-
return `File: ${candidate} (${lines} lines)\n\n${content.slice(0, 8000)}${
|
|
72
|
-
content.length > 8000 ? "\n\n… (truncated)" : ""
|
|
73
|
-
}`;
|
|
74
|
-
} catch (err) {
|
|
75
|
-
return `Error reading file: ${err instanceof Error ? err.message : String(err)}`;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
return `File not found: ${filePath}. If reading from a cloned repo, use the full absolute path e.g. C:\\Users\\...\\repo\\file.ts`;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export function readFolder(folderPath: string, repoPath: string): string {
|
|
83
|
-
const sanitized = folderPath
|
|
84
|
-
.replace(/^(ls|dir|find|tree|cat|read|ls -la?|ls -al?)\s+/i, "")
|
|
85
|
-
.trim();
|
|
86
|
-
|
|
87
|
-
const candidates = path.isAbsolute(sanitized)
|
|
88
|
-
? [sanitized]
|
|
89
|
-
: [sanitized, path.join(repoPath, sanitized)];
|
|
90
|
-
|
|
91
|
-
for (const candidate of candidates) {
|
|
92
|
-
if (!existsSync(candidate)) continue;
|
|
93
|
-
let stat: ReturnType<typeof statSync>;
|
|
94
|
-
try {
|
|
95
|
-
stat = statSync(candidate);
|
|
96
|
-
} catch {
|
|
97
|
-
continue;
|
|
98
|
-
}
|
|
99
|
-
if (!stat.isDirectory()) {
|
|
100
|
-
return `Not a directory: ${candidate}. Use read-file to read a file.`;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
let entries: string[];
|
|
104
|
-
try {
|
|
105
|
-
entries = readdirSync(candidate, { encoding: "utf-8" });
|
|
106
|
-
} catch (err) {
|
|
107
|
-
return `Error reading folder: ${err instanceof Error ? err.message : String(err)}`;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const files: string[] = [];
|
|
111
|
-
const subfolders: string[] = [];
|
|
112
|
-
|
|
113
|
-
for (const entry of entries) {
|
|
114
|
-
if (entry.startsWith(".") && entry !== ".env") continue;
|
|
115
|
-
const full = path.join(candidate, entry);
|
|
116
|
-
try {
|
|
117
|
-
if (statSync(full).isDirectory()) subfolders.push(`${entry}/`);
|
|
118
|
-
else files.push(entry);
|
|
119
|
-
} catch {}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const total = files.length + subfolders.length;
|
|
123
|
-
const lines: string[] = [`Folder: ${candidate} (${total} entries)`, ""];
|
|
124
|
-
if (files.length > 0) {
|
|
125
|
-
lines.push("Files:");
|
|
126
|
-
files.forEach((f) => lines.push(` ${f}`));
|
|
127
|
-
}
|
|
128
|
-
if (subfolders.length > 0) {
|
|
129
|
-
if (files.length > 0) lines.push("");
|
|
130
|
-
lines.push("Subfolders:");
|
|
131
|
-
subfolders.forEach((d) => lines.push(` ${d}`));
|
|
132
|
-
}
|
|
133
|
-
if (total === 0) lines.push("(empty folder)");
|
|
134
|
-
return lines.join("\n");
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return `Folder not found: ${sanitized}`;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
export function grepFiles(
|
|
141
|
-
pattern: string,
|
|
142
|
-
glob: string,
|
|
143
|
-
repoPath: string,
|
|
144
|
-
): string {
|
|
145
|
-
let regex: RegExp;
|
|
146
|
-
try {
|
|
147
|
-
regex = new RegExp(pattern, "i");
|
|
148
|
-
} catch {
|
|
149
|
-
regex = new RegExp(pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i");
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const globToFilter = (g: string): ((rel: string) => boolean) => {
|
|
153
|
-
const cleaned = g.replace(/^\*\*\//, "");
|
|
154
|
-
const parts = cleaned.split("/");
|
|
155
|
-
const ext = parts[parts.length - 1];
|
|
156
|
-
const prefix = parts.slice(0, -1).join("/");
|
|
157
|
-
return (rel: string) => {
|
|
158
|
-
if (ext?.startsWith("*.")) {
|
|
159
|
-
if (!rel.endsWith(ext.slice(1))) return false;
|
|
160
|
-
} else if (ext && !ext.includes("*")) {
|
|
161
|
-
if (!rel.endsWith(ext)) return false;
|
|
162
|
-
}
|
|
163
|
-
if (prefix && !prefix.includes("*")) {
|
|
164
|
-
if (!rel.startsWith(prefix)) return false;
|
|
165
|
-
}
|
|
166
|
-
return true;
|
|
167
|
-
};
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
const filter = globToFilter(glob);
|
|
171
|
-
const allFiles = walkDir(repoPath);
|
|
172
|
-
const matchedFiles = allFiles.filter(filter);
|
|
173
|
-
if (matchedFiles.length === 0) return `No files matched glob: ${glob}`;
|
|
174
|
-
|
|
175
|
-
const results: string[] = [];
|
|
176
|
-
let totalMatches = 0;
|
|
177
|
-
|
|
178
|
-
for (const relPath of matchedFiles) {
|
|
179
|
-
const fullPath = path.join(repoPath, relPath);
|
|
180
|
-
let content: string;
|
|
181
|
-
try {
|
|
182
|
-
content = readFileSync(fullPath, "utf-8");
|
|
183
|
-
} catch {
|
|
184
|
-
continue;
|
|
185
|
-
}
|
|
186
|
-
const fileLines = content.split("\n");
|
|
187
|
-
const fileMatches: string[] = [];
|
|
188
|
-
fileLines.forEach((line, i) => {
|
|
189
|
-
if (regex.test(line)) {
|
|
190
|
-
fileMatches.push(` ${i + 1}: ${line.trimEnd()}`);
|
|
191
|
-
totalMatches++;
|
|
192
|
-
}
|
|
193
|
-
});
|
|
194
|
-
if (fileMatches.length > 0)
|
|
195
|
-
results.push(`${relPath}\n${fileMatches.join("\n")}`);
|
|
196
|
-
if (totalMatches >= 200) {
|
|
197
|
-
results.push("(truncated — too many matches)");
|
|
198
|
-
break;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
if (results.length === 0)
|
|
203
|
-
return `No matches for /${pattern}/ in ${matchedFiles.length} file(s) matching ${glob}`;
|
|
204
|
-
|
|
205
|
-
return `grep /${pattern}/ ${glob} — ${totalMatches} match(es) in ${results.length} file(s)\n\n${results.join("\n\n")}`;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
export function writeFile(
|
|
209
|
-
filePath: string,
|
|
210
|
-
content: string,
|
|
211
|
-
repoPath: string,
|
|
212
|
-
): string {
|
|
213
|
-
const fullPath = path.isAbsolute(filePath)
|
|
214
|
-
? filePath
|
|
215
|
-
: path.join(repoPath, filePath);
|
|
216
|
-
try {
|
|
217
|
-
const dir = path.dirname(fullPath);
|
|
218
|
-
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
219
|
-
writeFileSync(fullPath, content, "utf-8");
|
|
220
|
-
const lines = content.split("\n").length;
|
|
221
|
-
return `Written: ${fullPath} (${lines} lines, ${content.length} bytes)`;
|
|
222
|
-
} catch (err) {
|
|
223
|
-
return `Error writing file: ${err instanceof Error ? err.message : String(err)}`;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
export function deleteFile(filePath: string, repoPath: string): string {
|
|
228
|
-
const fullPath = path.isAbsolute(filePath)
|
|
229
|
-
? filePath
|
|
230
|
-
: path.join(repoPath, filePath);
|
|
231
|
-
try {
|
|
232
|
-
if (!existsSync(fullPath)) return `File not found: ${fullPath}`;
|
|
233
|
-
const { unlinkSync } = require("fs") as typeof import("fs");
|
|
234
|
-
unlinkSync(fullPath);
|
|
235
|
-
return `Deleted: ${fullPath}`;
|
|
236
|
-
} catch (err) {
|
|
237
|
-
return `Error deleting file: ${err instanceof Error ? err.message : String(err)}`;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
export function deleteFolder(folderPath: string, repoPath: string): string {
|
|
242
|
-
const fullPath = path.isAbsolute(folderPath)
|
|
243
|
-
? folderPath
|
|
244
|
-
: path.join(repoPath, folderPath);
|
|
245
|
-
try {
|
|
246
|
-
if (!existsSync(fullPath)) return `Folder not found: ${fullPath}`;
|
|
247
|
-
const { rmSync } = require("fs") as typeof import("fs");
|
|
248
|
-
rmSync(fullPath, { recursive: true, force: true });
|
|
249
|
-
return `Deleted folder: ${fullPath}`;
|
|
250
|
-
} catch (err) {
|
|
251
|
-
return `Error deleting folder: ${err instanceof Error ? err.message : String(err)}`;
|
|
252
|
-
}
|
|
253
|
-
}
|