@ridit/lens 0.2.1 → 0.2.2
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 +629 -461
- package/package.json +1 -1
- package/src/components/chat/ChatOverlays.tsx +73 -46
- package/src/components/chat/ChatRunner.tsx +203 -369
- package/src/index.tsx +3 -0
- package/src/prompts/system.ts +79 -102
- package/src/utils/chat.ts +95 -176
- package/src/utils/tools/builtins.ts +324 -0
- package/src/utils/tools/registry.ts +119 -0
package/dist/index.mjs
CHANGED
|
@@ -49206,72 +49206,17 @@ print("OK")
|
|
|
49206
49206
|
}
|
|
49207
49207
|
}
|
|
49208
49208
|
// src/prompts/system.ts
|
|
49209
|
-
function buildSystemPrompt(files, memorySummary = "") {
|
|
49209
|
+
function buildSystemPrompt(files, memorySummary = "", toolsSection) {
|
|
49210
49210
|
const fileList = files.map((f) => `### ${f.path}
|
|
49211
49211
|
\`\`\`
|
|
49212
49212
|
${f.content.slice(0, 2000)}
|
|
49213
49213
|
\`\`\``).join(`
|
|
49214
49214
|
|
|
49215
49215
|
`);
|
|
49216
|
+
const tools = toolsSection ?? BUILTIN_TOOLS_SECTION;
|
|
49216
49217
|
return `You are an expert software engineer assistant with access to the user's codebase and tools.
|
|
49217
49218
|
|
|
49218
|
-
|
|
49219
|
-
|
|
49220
|
-
You have exactly thirteen tools. To use a tool you MUST wrap it in the exact XML tags shown below — no other format will work.
|
|
49221
|
-
|
|
49222
|
-
### 1. fetch — load a URL
|
|
49223
|
-
<fetch>https://example.com</fetch>
|
|
49224
|
-
|
|
49225
|
-
### 2. shell — run a terminal command
|
|
49226
|
-
<shell>node -v</shell>
|
|
49227
|
-
|
|
49228
|
-
### 3. read-file — read a file from the repo
|
|
49229
|
-
<read-file>src/foo.ts</read-file>
|
|
49230
|
-
|
|
49231
|
-
### 4. read-folder — list contents of a folder (files + subfolders, one level deep)
|
|
49232
|
-
<read-folder>src/components</read-folder>
|
|
49233
|
-
|
|
49234
|
-
### 5. grep — search for a pattern across files in the repo (cross-platform, no shell needed)
|
|
49235
|
-
<grep>
|
|
49236
|
-
{"pattern": "ChatRunner", "glob": "src/**/*.tsx"}
|
|
49237
|
-
</grep>
|
|
49238
|
-
|
|
49239
|
-
### 6. write-file — create or overwrite a file
|
|
49240
|
-
<write-file>
|
|
49241
|
-
{"path": "data/output.csv", "content": "col1,col2
|
|
49242
|
-
val1,val2"}
|
|
49243
|
-
</write-file>
|
|
49244
|
-
|
|
49245
|
-
### 7. delete-file — permanently delete a single file
|
|
49246
|
-
<delete-file>src/old-component.tsx</delete-file>
|
|
49247
|
-
|
|
49248
|
-
### 8. delete-folder — permanently delete a folder and all its contents
|
|
49249
|
-
<delete-folder>src/legacy</delete-folder>
|
|
49250
|
-
|
|
49251
|
-
### 9. open-url — open a URL in the user's default browser
|
|
49252
|
-
<open-url>https://github.com/owner/repo</open-url>
|
|
49253
|
-
|
|
49254
|
-
### 10. generate-pdf — generate a PDF file from markdown-style content
|
|
49255
|
-
<generate-pdf>
|
|
49256
|
-
{"path": "output/report.pdf", "content": "# Title
|
|
49257
|
-
|
|
49258
|
-
Some body text.
|
|
49259
|
-
|
|
49260
|
-
## Section
|
|
49261
|
-
|
|
49262
|
-
More content."}
|
|
49263
|
-
</generate-pdf>
|
|
49264
|
-
|
|
49265
|
-
### 11. search — search the internet for anything you are unsure about
|
|
49266
|
-
<search>how to use React useEffect cleanup function</search>
|
|
49267
|
-
|
|
49268
|
-
### 12. clone — clone a GitHub repo so you can explore and discuss it
|
|
49269
|
-
<clone>https://github.com/owner/repo</clone>
|
|
49270
|
-
|
|
49271
|
-
### 13. changes — propose code edits (shown as a diff for user approval)
|
|
49272
|
-
<changes>
|
|
49273
|
-
{"summary": "what changed and why", "patches": [{"path": "src/foo.ts", "content": "COMPLETE file content", "isNew": false}]}
|
|
49274
|
-
</changes>
|
|
49219
|
+
${tools}
|
|
49275
49220
|
|
|
49276
49221
|
## MEMORY OPERATIONS
|
|
49277
49222
|
|
|
@@ -49321,9 +49266,26 @@ You may emit multiple memory operations in a single response alongside normal co
|
|
|
49321
49266
|
22. When scaffolding a multi-file project, after each write-file succeeds, immediately proceed to writing the NEXT file — NEVER rewrite a file you already wrote in this session. Each file is written ONCE and ONLY ONCE.
|
|
49322
49267
|
23. For JSX/TSX files always use \`.tsx\` extension and include \`/** @jsxImportSource react */\` or ensure tsconfig has jsx set — bun needs this to parse JSX
|
|
49323
49268
|
24. When explaining how to use a tool in text, use [tag] bracket notation or a fenced code block — NEVER emit a real XML tool tag as part of an explanation or example
|
|
49324
|
-
25. NEVER
|
|
49325
|
-
26. NEVER
|
|
49326
|
-
|
|
49269
|
+
25. NEVER read files, list folders, or run tools that were not asked for in the current user message
|
|
49270
|
+
26. NEVER use markdown formatting in plain text responses — no **bold**, no *italics*, no # headings, no bullet points with -, *, or +, no numbered lists, no backtick inline code. Write in plain prose. Only use fenced \`\`\` code blocks when showing actual code.
|
|
49271
|
+
|
|
49272
|
+
## SCAFFOLDING — CHAINING WRITE-FILE CALLS
|
|
49273
|
+
|
|
49274
|
+
When creating multiple files (e.g. scaffolding a project or creating 10 files), emit ALL of them
|
|
49275
|
+
in a single response by chaining the tags back-to-back with no text between them:
|
|
49276
|
+
|
|
49277
|
+
<write-file>
|
|
49278
|
+
{"path": "test/file1.txt", "content": "File 1 content"}
|
|
49279
|
+
</write-file>
|
|
49280
|
+
<write-file>
|
|
49281
|
+
{"path": "test/file2.txt", "content": "File 2 content"}
|
|
49282
|
+
</write-file>
|
|
49283
|
+
<write-file>
|
|
49284
|
+
{"path": "test/file3.txt", "content": "File 3 content"}
|
|
49285
|
+
</write-file>
|
|
49286
|
+
|
|
49287
|
+
The system processes each tag sequentially and automatically continues to the next one.
|
|
49288
|
+
Do NOT wait for a user message between files — emit all tags at once.
|
|
49327
49289
|
|
|
49328
49290
|
## CRITICAL: READ BEFORE YOU WRITE
|
|
49329
49291
|
|
|
@@ -49345,61 +49307,62 @@ These rules are mandatory whenever you plan to edit or create a file:
|
|
|
49345
49307
|
- NEVER produce a file that is shorter than the original unless you are explicitly asked to delete things
|
|
49346
49308
|
- If you catch yourself rewriting a file from scratch, STOP — go back and read the original first
|
|
49347
49309
|
|
|
49348
|
-
## WHEN TO USE read-folder:
|
|
49349
|
-
- Before editing files in an unfamiliar directory — list it first to understand the structure
|
|
49350
|
-
- When a feature spans multiple files and you are not sure what exists
|
|
49351
|
-
- When the user asks you to explore or explain a part of the codebase
|
|
49352
|
-
|
|
49353
|
-
## SCAFFOLDING A NEW PROJECT (follow this exactly)
|
|
49354
|
-
|
|
49355
|
-
When the user asks to create a new CLI/app in a subfolder (e.g. "make a todo app called list"):
|
|
49356
|
-
1. Create all files first using write-file with paths like \`list/package.json\`, \`list/src/index.tsx\`
|
|
49357
|
-
2. Then run \`cd list && bun install\` (or npm/pnpm) in one shell command
|
|
49358
|
-
3. Then run the project with \`cd list && bun run index.ts\` or whatever the entry point is
|
|
49359
|
-
4. NEVER run \`bun init\` — it is interactive and will hang. Create package.json manually with write-file instead
|
|
49360
|
-
5. TSX files need either tsconfig.json with \`"jsx": "react-jsx"\` or \`/** @jsxImportSource react */\` at the top
|
|
49361
|
-
|
|
49362
|
-
## FETCH → WRITE FLOW (follow this exactly when saving fetched data)
|
|
49363
|
-
|
|
49364
|
-
1. fetch the URL
|
|
49365
|
-
2. Analyze the result — count the rows, identify columns, check completeness
|
|
49366
|
-
3. Tell the user what you found: "Found X rows with columns: A, B, C. Writing now."
|
|
49367
|
-
4. emit write-file with correctly structured, complete content
|
|
49368
|
-
5. After write-file confirms success, emit read-file to verify
|
|
49369
|
-
6. Only after read-file confirms content is correct, tell the user it is done
|
|
49370
|
-
|
|
49371
|
-
## WHEN TO USE TOOLS
|
|
49372
|
-
|
|
49373
|
-
- User shares any URL → fetch it immediately
|
|
49374
|
-
- User asks to run anything → shell it immediately
|
|
49375
|
-
- User asks to open a link, open a URL, or visit a website → open-url it immediately, do NOT use fetch
|
|
49376
|
-
- User asks to delete a file → delete-file it immediately (requires approval)
|
|
49377
|
-
- User asks to delete a folder or directory → delete-folder it immediately (requires approval)
|
|
49378
|
-
- User asks to search for a pattern in files, find usages, find where something is defined → grep it immediately, NEVER use shell grep/findstr/Select-String
|
|
49379
|
-
- User asks to read a file → read-file it immediately, NEVER use shell cat/type
|
|
49380
|
-
- User asks what files are in a folder, or to explore/list a directory → read-folder it immediately, NEVER use shell ls/dir/find/git ls-files
|
|
49381
|
-
- User asks to explore a folder or directory → read-folder it immediately
|
|
49382
|
-
- User asks to save/create/write a file → write-file it immediately, then read-file to verify
|
|
49383
|
-
- User asks to modify/edit/add to an existing file → read-file it FIRST, then emit changes
|
|
49384
|
-
- User shares a GitHub URL and wants to clone/explore/discuss it → use clone immediately, NEVER use shell git clone
|
|
49385
|
-
- After clone succeeds, you will see context about the clone in the conversation. Wait for the user to ask a specific question before using any tools. Do NOT auto-read files, do NOT emit any tool tags until the user asks.
|
|
49386
|
-
- You are unsure about an API, library, error, concept, or piece of code → search it immediately
|
|
49387
|
-
- User asks about something recent or that you might not know → search it immediately
|
|
49388
|
-
- You are about to say "I'm not sure" or "I don't know" → search instead of guessing
|
|
49389
|
-
|
|
49390
|
-
## shell IS ONLY FOR:
|
|
49391
|
-
- Running code: \`node script.js\`, \`bun run dev\`, \`python main.py\`
|
|
49392
|
-
- Installing packages: \`npm install\`, \`pip install\`
|
|
49393
|
-
- Building/testing: \`npm run build\`, \`bun test\`
|
|
49394
|
-
- Git operations other than clone: \`git status\`, \`git log\`, \`git diff\`
|
|
49395
|
-
- Anything that EXECUTES — not reads or lists
|
|
49396
|
-
|
|
49397
49310
|
## CODEBASE
|
|
49398
49311
|
|
|
49399
49312
|
${fileList.length > 0 ? fileList : "(no files indexed)"}
|
|
49400
49313
|
|
|
49401
49314
|
${memorySummary}`;
|
|
49402
49315
|
}
|
|
49316
|
+
var BUILTIN_TOOLS_SECTION = `## TOOLS
|
|
49317
|
+
|
|
49318
|
+
You have exactly thirteen tools. To use a tool you MUST wrap it in the exact XML tags shown below — no other format will work.
|
|
49319
|
+
|
|
49320
|
+
### 1. fetch — load a URL
|
|
49321
|
+
<fetch>https://example.com</fetch>
|
|
49322
|
+
|
|
49323
|
+
### 2. shell — run a terminal command
|
|
49324
|
+
<shell>node -v</shell>
|
|
49325
|
+
|
|
49326
|
+
### 3. read-file — read a file from the repo
|
|
49327
|
+
<read-file>src/foo.ts</read-file>
|
|
49328
|
+
|
|
49329
|
+
### 4. read-folder — list contents of a folder (files + subfolders, one level deep)
|
|
49330
|
+
<read-folder>src/components</read-folder>
|
|
49331
|
+
|
|
49332
|
+
### 5. grep — search for a pattern across files in the repo (cross-platform, no shell needed)
|
|
49333
|
+
<grep>
|
|
49334
|
+
{"pattern": "ChatRunner", "glob": "src/**/*.tsx"}
|
|
49335
|
+
</grep>
|
|
49336
|
+
|
|
49337
|
+
### 6. write-file — create or overwrite a file
|
|
49338
|
+
<write-file>
|
|
49339
|
+
{"path": "data/output.csv", "content": "col1,col2\\nval1,val2"}
|
|
49340
|
+
</write-file>
|
|
49341
|
+
|
|
49342
|
+
### 7. delete-file — permanently delete a single file
|
|
49343
|
+
<delete-file>src/old-component.tsx</delete-file>
|
|
49344
|
+
|
|
49345
|
+
### 8. delete-folder — permanently delete a folder and all its contents
|
|
49346
|
+
<delete-folder>src/legacy</delete-folder>
|
|
49347
|
+
|
|
49348
|
+
### 9. open-url — open a URL in the user's default browser
|
|
49349
|
+
<open-url>https://github.com/owner/repo</open-url>
|
|
49350
|
+
|
|
49351
|
+
### 10. generate-pdf — generate a PDF file from markdown-style content
|
|
49352
|
+
<generate-pdf>
|
|
49353
|
+
{"path": "output/report.pdf", "content": "# Title\\n\\nSome body text.\\n\\n## Section\\n\\nMore content."}
|
|
49354
|
+
</generate-pdf>
|
|
49355
|
+
|
|
49356
|
+
### 11. search — search the internet for anything you are unsure about
|
|
49357
|
+
<search>how to use React useEffect cleanup function</search>
|
|
49358
|
+
|
|
49359
|
+
### 12. clone — clone a GitHub repo so you can explore and discuss it
|
|
49360
|
+
<clone>https://github.com/owner/repo</clone>
|
|
49361
|
+
|
|
49362
|
+
### 13. changes — propose code edits (shown as a diff for user approval)
|
|
49363
|
+
<changes>
|
|
49364
|
+
{"summary": "what changed and why", "patches": [{"path": "src/foo.ts", "content": "COMPLETE file content", "isNew": false}]}
|
|
49365
|
+
</changes>`;
|
|
49403
49366
|
// src/prompts/fewshot.ts
|
|
49404
49367
|
var FEW_SHOT_MESSAGES = [
|
|
49405
49368
|
{
|
|
@@ -49842,133 +49805,129 @@ Got it — I'll always use bun for this project.`
|
|
|
49842
49805
|
Done — removed that memory.`
|
|
49843
49806
|
}
|
|
49844
49807
|
];
|
|
49808
|
+
// src/utils/tools/registry.ts
|
|
49809
|
+
class ToolRegistry {
|
|
49810
|
+
tools = new Map;
|
|
49811
|
+
register(tool) {
|
|
49812
|
+
if (this.tools.has(tool.name)) {
|
|
49813
|
+
console.warn(`[ToolRegistry] Overwriting existing tool: "${tool.name}"`);
|
|
49814
|
+
}
|
|
49815
|
+
this.tools.set(tool.name, tool);
|
|
49816
|
+
}
|
|
49817
|
+
unregister(name) {
|
|
49818
|
+
this.tools.delete(name);
|
|
49819
|
+
}
|
|
49820
|
+
get(name) {
|
|
49821
|
+
return this.tools.get(name);
|
|
49822
|
+
}
|
|
49823
|
+
all() {
|
|
49824
|
+
return Array.from(this.tools.values());
|
|
49825
|
+
}
|
|
49826
|
+
names() {
|
|
49827
|
+
return Array.from(this.tools.keys());
|
|
49828
|
+
}
|
|
49829
|
+
buildSystemPromptSection() {
|
|
49830
|
+
const lines = [`## TOOLS
|
|
49831
|
+
`];
|
|
49832
|
+
lines.push("You have exactly " + this.tools.size + ` tools. To use a tool you MUST wrap it in the exact XML tags shown below — no other format will work.
|
|
49833
|
+
`);
|
|
49834
|
+
let i = 1;
|
|
49835
|
+
for (const tool of this.tools.values()) {
|
|
49836
|
+
lines.push(tool.systemPromptEntry(i++));
|
|
49837
|
+
}
|
|
49838
|
+
return lines.join(`
|
|
49839
|
+
`);
|
|
49840
|
+
}
|
|
49841
|
+
}
|
|
49842
|
+
var registry = new ToolRegistry;
|
|
49843
|
+
|
|
49845
49844
|
// src/utils/chat.ts
|
|
49846
49845
|
function parseResponse(text) {
|
|
49847
49846
|
const scanText = text.replace(/```[\s\S]*?```/g, (m) => " ".repeat(m.length));
|
|
49848
49847
|
const candidates = [];
|
|
49849
|
-
const
|
|
49850
|
-
|
|
49851
|
-
|
|
49852
|
-
|
|
49853
|
-
|
|
49854
|
-
|
|
49855
|
-
|
|
49856
|
-
|
|
49857
|
-
|
|
49858
|
-
|
|
49859
|
-
|
|
49860
|
-
|
|
49861
|
-
|
|
49862
|
-
|
|
49863
|
-
|
|
49864
|
-
|
|
49865
|
-
|
|
49866
|
-
|
|
49867
|
-
|
|
49868
|
-
|
|
49869
|
-
|
|
49870
|
-
|
|
49871
|
-
|
|
49872
|
-
|
|
49873
|
-
|
|
49874
|
-
|
|
49875
|
-
|
|
49876
|
-
|
|
49877
|
-
|
|
49878
|
-
|
|
49879
|
-
|
|
49880
|
-
|
|
49881
|
-
|
|
49882
|
-
|
|
49883
|
-
originalMatch[1]
|
|
49884
|
-
], { index: m.index, input: text, groups: undefined });
|
|
49885
|
-
candidates.push({ index: m.index, kind: kind2, match: fakeMatch });
|
|
49848
|
+
for (const toolName2 of registry.names()) {
|
|
49849
|
+
const escaped = toolName2.replace(/[-]/g, "\\-");
|
|
49850
|
+
const xmlRe = new RegExp(`<${escaped}>([\\s\\S]*?)<\\/${escaped}>`, "g");
|
|
49851
|
+
xmlRe.lastIndex = 0;
|
|
49852
|
+
const xmlM = xmlRe.exec(scanText);
|
|
49853
|
+
if (xmlM) {
|
|
49854
|
+
const orig = new RegExp(xmlRe.source);
|
|
49855
|
+
const origM = orig.exec(text.slice(xmlM.index));
|
|
49856
|
+
if (origM) {
|
|
49857
|
+
candidates.push({
|
|
49858
|
+
index: xmlM.index,
|
|
49859
|
+
toolName: toolName2,
|
|
49860
|
+
match: Object.assign([
|
|
49861
|
+
text.slice(xmlM.index, xmlM.index + origM[0].length),
|
|
49862
|
+
origM[1]
|
|
49863
|
+
], { index: xmlM.index, input: text, groups: undefined })
|
|
49864
|
+
});
|
|
49865
|
+
}
|
|
49866
|
+
}
|
|
49867
|
+
const fencedRe = new RegExp(`\`\`\`${escaped}\\r?\\n([\\s\\S]*?)\\r?\\n\`\`\``, "g");
|
|
49868
|
+
fencedRe.lastIndex = 0;
|
|
49869
|
+
const fencedM = fencedRe.exec(scanText);
|
|
49870
|
+
if (fencedM) {
|
|
49871
|
+
const orig = new RegExp(fencedRe.source);
|
|
49872
|
+
const origM = orig.exec(text.slice(fencedM.index));
|
|
49873
|
+
if (origM) {
|
|
49874
|
+
candidates.push({
|
|
49875
|
+
index: fencedM.index,
|
|
49876
|
+
toolName: toolName2,
|
|
49877
|
+
match: Object.assign([
|
|
49878
|
+
text.slice(fencedM.index, fencedM.index + origM[0].length),
|
|
49879
|
+
origM[1]
|
|
49880
|
+
], { index: fencedM.index, input: text, groups: undefined })
|
|
49881
|
+
});
|
|
49886
49882
|
}
|
|
49887
49883
|
}
|
|
49888
49884
|
}
|
|
49889
49885
|
if (candidates.length === 0)
|
|
49890
49886
|
return { kind: "text", content: text.trim() };
|
|
49891
49887
|
candidates.sort((a, b) => a.index - b.index);
|
|
49892
|
-
const {
|
|
49893
|
-
const before2 = text.slice(0, match.index).
|
|
49888
|
+
const { toolName, match } = candidates[0];
|
|
49889
|
+
const before2 = text.slice(0, match.index).trim();
|
|
49894
49890
|
const body = (match[1] ?? "").trim();
|
|
49895
|
-
|
|
49891
|
+
const afterMatch = text.slice(match.index + match[0].length).trim();
|
|
49892
|
+
const remainder = afterMatch.length > 0 ? afterMatch : undefined;
|
|
49893
|
+
if (toolName === "changes") {
|
|
49896
49894
|
try {
|
|
49897
49895
|
const parsed = JSON.parse(body);
|
|
49898
49896
|
const display = [before2, parsed.summary].filter(Boolean).join(`
|
|
49899
49897
|
|
|
49900
49898
|
`);
|
|
49901
|
-
return { kind: "changes", content: display, patches: parsed.patches };
|
|
49902
|
-
} catch {}
|
|
49903
|
-
}
|
|
49904
|
-
if (kind === "shell")
|
|
49905
|
-
return { kind: "shell", content: before2, command: body };
|
|
49906
|
-
if (kind === "fetch")
|
|
49907
|
-
return {
|
|
49908
|
-
kind: "fetch",
|
|
49909
|
-
content: before2,
|
|
49910
|
-
url: body.replace(/^<|>$/g, "").trim()
|
|
49911
|
-
};
|
|
49912
|
-
if (kind === "read-file")
|
|
49913
|
-
return { kind: "read-file", content: before2, filePath: body };
|
|
49914
|
-
if (kind === "read-folder")
|
|
49915
|
-
return { kind: "read-folder", content: before2, folderPath: body };
|
|
49916
|
-
if (kind === "delete-file")
|
|
49917
|
-
return { kind: "delete-file", content: before2, filePath: body };
|
|
49918
|
-
if (kind === "delete-folder")
|
|
49919
|
-
return { kind: "delete-folder", content: before2, folderPath: body };
|
|
49920
|
-
if (kind === "open-url")
|
|
49921
|
-
return {
|
|
49922
|
-
kind: "open-url",
|
|
49923
|
-
content: before2,
|
|
49924
|
-
url: body.replace(/^<|>$/g, "").trim()
|
|
49925
|
-
};
|
|
49926
|
-
if (kind === "search")
|
|
49927
|
-
return { kind: "search", content: before2, query: body };
|
|
49928
|
-
if (kind === "clone")
|
|
49929
|
-
return {
|
|
49930
|
-
kind: "clone",
|
|
49931
|
-
content: before2,
|
|
49932
|
-
repoUrl: body.replace(/^<|>$/g, "").trim()
|
|
49933
|
-
};
|
|
49934
|
-
if (kind === "generate-pdf") {
|
|
49935
|
-
try {
|
|
49936
|
-
const parsed = JSON.parse(body);
|
|
49937
|
-
return {
|
|
49938
|
-
kind: "generate-pdf",
|
|
49939
|
-
content: before2,
|
|
49940
|
-
filePath: parsed.path ?? parsed.filePath ?? "output.pdf",
|
|
49941
|
-
pdfContent: parsed.content ?? ""
|
|
49942
|
-
};
|
|
49943
|
-
} catch {
|
|
49944
|
-
return { kind: "text", content: text };
|
|
49945
|
-
}
|
|
49946
|
-
}
|
|
49947
|
-
if (kind === "grep") {
|
|
49948
|
-
try {
|
|
49949
|
-
const parsed = JSON.parse(body);
|
|
49950
49899
|
return {
|
|
49951
|
-
kind: "
|
|
49952
|
-
content:
|
|
49953
|
-
|
|
49954
|
-
|
|
49900
|
+
kind: "changes",
|
|
49901
|
+
content: display,
|
|
49902
|
+
patches: parsed.patches,
|
|
49903
|
+
remainder
|
|
49955
49904
|
};
|
|
49956
49905
|
} catch {
|
|
49957
|
-
return { kind: "
|
|
49906
|
+
return { kind: "text", content: text.trim() };
|
|
49958
49907
|
}
|
|
49959
49908
|
}
|
|
49960
|
-
if (
|
|
49961
|
-
|
|
49962
|
-
|
|
49963
|
-
|
|
49964
|
-
|
|
49965
|
-
|
|
49966
|
-
|
|
49967
|
-
fileContent: parsed.content
|
|
49968
|
-
};
|
|
49969
|
-
} catch {}
|
|
49909
|
+
if (toolName === "clone") {
|
|
49910
|
+
return {
|
|
49911
|
+
kind: "clone",
|
|
49912
|
+
content: before2,
|
|
49913
|
+
repoUrl: body.replace(/^<|>$/g, "").trim(),
|
|
49914
|
+
remainder
|
|
49915
|
+
};
|
|
49970
49916
|
}
|
|
49971
|
-
|
|
49917
|
+
const tool = registry.get(toolName);
|
|
49918
|
+
if (!tool)
|
|
49919
|
+
return { kind: "text", content: text.trim() };
|
|
49920
|
+
const input = tool.parseInput(body);
|
|
49921
|
+
if (input === null)
|
|
49922
|
+
return { kind: "text", content: text.trim() };
|
|
49923
|
+
return {
|
|
49924
|
+
kind: "tool",
|
|
49925
|
+
toolName,
|
|
49926
|
+
input,
|
|
49927
|
+
rawInput: body,
|
|
49928
|
+
content: before2,
|
|
49929
|
+
remainder
|
|
49930
|
+
};
|
|
49972
49931
|
}
|
|
49973
49932
|
function extractGithubUrl(text) {
|
|
49974
49933
|
const match = text.match(/https?:\/\/github\.com\/[\w.-]+\/[\w.-]+/);
|
|
@@ -49987,10 +49946,9 @@ function buildApiMessages(messages) {
|
|
|
49987
49946
|
content: "The tool call was denied by the user. Please respond without using that tool."
|
|
49988
49947
|
};
|
|
49989
49948
|
}
|
|
49990
|
-
const label = m.toolName === "shell" ? `shell command \`${m.content}\`` : m.toolName === "fetch" ? `fetch of ${m.content}` : m.toolName === "read-file" ? `read-file of ${m.content}` : m.toolName === "read-folder" ? `read-folder of ${m.content}` : m.toolName === "grep" ? `grep for "${m.content}"` : m.toolName === "delete-file" ? `delete-file of ${m.content}` : m.toolName === "delete-folder" ? `delete-folder of ${m.content}` : m.toolName === "open-url" ? `open-url ${m.content}` : m.toolName === "generate-pdf" ? `generate-pdf to ${m.content}` : m.toolName === "search" ? `web search for "${m.content}"` : `write-file to ${m.content}`;
|
|
49991
49949
|
return {
|
|
49992
49950
|
role: "user",
|
|
49993
|
-
content: `Here is the output from the ${
|
|
49951
|
+
content: `Here is the output from the ${m.toolName} of ${m.content}:
|
|
49994
49952
|
|
|
49995
49953
|
${m.result}
|
|
49996
49954
|
|
|
@@ -50489,50 +50447,71 @@ function PermissionPrompt({
|
|
|
50489
50447
|
let icon;
|
|
50490
50448
|
let label;
|
|
50491
50449
|
let value;
|
|
50492
|
-
if (
|
|
50493
|
-
|
|
50494
|
-
|
|
50495
|
-
|
|
50496
|
-
|
|
50497
|
-
|
|
50498
|
-
|
|
50499
|
-
|
|
50500
|
-
|
|
50501
|
-
|
|
50502
|
-
|
|
50503
|
-
|
|
50504
|
-
|
|
50505
|
-
|
|
50506
|
-
|
|
50507
|
-
|
|
50508
|
-
|
|
50509
|
-
|
|
50510
|
-
|
|
50511
|
-
value = `${tool.pattern} ${tool.glob}`;
|
|
50512
|
-
} else if (tool.type === "delete-file") {
|
|
50513
|
-
icon = "x";
|
|
50514
|
-
label = "delete";
|
|
50515
|
-
value = tool.filePath;
|
|
50516
|
-
} else if (tool.type === "delete-folder") {
|
|
50517
|
-
icon = "X";
|
|
50518
|
-
label = "delete folder";
|
|
50519
|
-
value = tool.folderPath;
|
|
50520
|
-
} else if (tool.type === "open-url") {
|
|
50521
|
-
icon = "↗";
|
|
50522
|
-
label = "open";
|
|
50523
|
-
value = tool.url;
|
|
50524
|
-
} else if (tool.type === "generate-pdf") {
|
|
50525
|
-
icon = "P";
|
|
50526
|
-
label = "pdf";
|
|
50527
|
-
value = tool.filePath;
|
|
50528
|
-
} else if (tool.type === "write-file") {
|
|
50529
|
-
icon = "w";
|
|
50530
|
-
label = "write";
|
|
50531
|
-
value = `${tool.filePath} (${tool.fileContent.length} bytes)`;
|
|
50450
|
+
if ("_label" in tool) {
|
|
50451
|
+
const iconMap = {
|
|
50452
|
+
run: "$",
|
|
50453
|
+
fetch: "~>",
|
|
50454
|
+
read: "r",
|
|
50455
|
+
write: "w",
|
|
50456
|
+
delete: "x",
|
|
50457
|
+
"delete folder": "X",
|
|
50458
|
+
open: "↗",
|
|
50459
|
+
pdf: "P",
|
|
50460
|
+
search: "?",
|
|
50461
|
+
folder: "d",
|
|
50462
|
+
grep: "/",
|
|
50463
|
+
clone: "↓",
|
|
50464
|
+
query: "⌗"
|
|
50465
|
+
};
|
|
50466
|
+
icon = iconMap[tool._label] ?? "·";
|
|
50467
|
+
label = tool._label;
|
|
50468
|
+
value = tool._display;
|
|
50532
50469
|
} else {
|
|
50533
|
-
|
|
50534
|
-
|
|
50535
|
-
|
|
50470
|
+
if (tool.type === "shell") {
|
|
50471
|
+
icon = "$";
|
|
50472
|
+
label = "run";
|
|
50473
|
+
value = tool.command;
|
|
50474
|
+
} else if (tool.type === "fetch") {
|
|
50475
|
+
icon = "~>";
|
|
50476
|
+
label = "fetch";
|
|
50477
|
+
value = tool.url;
|
|
50478
|
+
} else if (tool.type === "read-file") {
|
|
50479
|
+
icon = "r";
|
|
50480
|
+
label = "read";
|
|
50481
|
+
value = tool.filePath;
|
|
50482
|
+
} else if (tool.type === "read-folder") {
|
|
50483
|
+
icon = "d";
|
|
50484
|
+
label = "folder";
|
|
50485
|
+
value = tool.folderPath;
|
|
50486
|
+
} else if (tool.type === "grep") {
|
|
50487
|
+
icon = "/";
|
|
50488
|
+
label = "grep";
|
|
50489
|
+
value = `${tool.pattern} ${tool.glob}`;
|
|
50490
|
+
} else if (tool.type === "delete-file") {
|
|
50491
|
+
icon = "x";
|
|
50492
|
+
label = "delete";
|
|
50493
|
+
value = tool.filePath;
|
|
50494
|
+
} else if (tool.type === "delete-folder") {
|
|
50495
|
+
icon = "X";
|
|
50496
|
+
label = "delete folder";
|
|
50497
|
+
value = tool.folderPath;
|
|
50498
|
+
} else if (tool.type === "open-url") {
|
|
50499
|
+
icon = "↗";
|
|
50500
|
+
label = "open";
|
|
50501
|
+
value = tool.url;
|
|
50502
|
+
} else if (tool.type === "generate-pdf") {
|
|
50503
|
+
icon = "P";
|
|
50504
|
+
label = "pdf";
|
|
50505
|
+
value = tool.filePath;
|
|
50506
|
+
} else if (tool.type === "write-file") {
|
|
50507
|
+
icon = "w";
|
|
50508
|
+
label = "write";
|
|
50509
|
+
value = `${tool.filePath} (${tool.fileContent.length} bytes)`;
|
|
50510
|
+
} else {
|
|
50511
|
+
icon = "?";
|
|
50512
|
+
label = "search";
|
|
50513
|
+
value = tool.query ?? "";
|
|
50514
|
+
}
|
|
50536
50515
|
}
|
|
50537
50516
|
return /* @__PURE__ */ jsx_dev_runtime20.jsxDEV(Box_default, {
|
|
50538
50517
|
flexDirection: "column",
|
|
@@ -52555,8 +52534,7 @@ var ChatRunner = ({ repoPath }) => {
|
|
|
52555
52534
|
};
|
|
52556
52535
|
const abortControllerRef = import_react49.useRef(null);
|
|
52557
52536
|
const toolResultCache = import_react49.useRef(new Map);
|
|
52558
|
-
const
|
|
52559
|
-
const flushTimer = import_react49.useRef(null);
|
|
52537
|
+
const batchApprovedRef = import_react49.useRef(false);
|
|
52560
52538
|
const thinkingPhrase = useThinkingPhrase(stage.type === "thinking");
|
|
52561
52539
|
import_react48.default.useEffect(() => {
|
|
52562
52540
|
const chats = listChats(repoPath);
|
|
@@ -52567,22 +52545,8 @@ var ChatRunner = ({ repoPath }) => {
|
|
|
52567
52545
|
saveChat(chatNameRef.current, repoPath, allMessages);
|
|
52568
52546
|
}
|
|
52569
52547
|
}, [allMessages]);
|
|
52570
|
-
const flushBuffer = () => {
|
|
52571
|
-
const buf = inputBuffer.current;
|
|
52572
|
-
if (!buf)
|
|
52573
|
-
return;
|
|
52574
|
-
inputBuffer.current = "";
|
|
52575
|
-
setInputValue((v) => v + buf);
|
|
52576
|
-
};
|
|
52577
|
-
const scheduleFlush = () => {
|
|
52578
|
-
if (flushTimer.current !== null)
|
|
52579
|
-
return;
|
|
52580
|
-
flushTimer.current = setTimeout(() => {
|
|
52581
|
-
flushTimer.current = null;
|
|
52582
|
-
flushBuffer();
|
|
52583
|
-
}, 16);
|
|
52584
|
-
};
|
|
52585
52548
|
const handleError = (currentAll) => (err) => {
|
|
52549
|
+
batchApprovedRef.current = false;
|
|
52586
52550
|
if (err instanceof Error && err.name === "AbortError") {
|
|
52587
52551
|
setStage({ type: "idle" });
|
|
52588
52552
|
return;
|
|
@@ -52598,6 +52562,7 @@ var ChatRunner = ({ repoPath }) => {
|
|
|
52598
52562
|
};
|
|
52599
52563
|
const processResponse = (raw, currentAll, signal) => {
|
|
52600
52564
|
if (signal.aborted) {
|
|
52565
|
+
batchApprovedRef.current = false;
|
|
52601
52566
|
setStage({ type: "idle" });
|
|
52602
52567
|
return;
|
|
52603
52568
|
}
|
|
@@ -52620,14 +52585,15 @@ var ChatRunner = ({ repoPath }) => {
|
|
|
52620
52585
|
const cleanRaw = raw.replace(/<memory-add>[\s\S]*?<\/memory-add>/g, "").replace(/<memory-delete>[\s\S]*?<\/memory-delete>/g, "").trim();
|
|
52621
52586
|
const parsed = parseResponse(cleanRaw);
|
|
52622
52587
|
if (parsed.kind === "changes") {
|
|
52588
|
+
batchApprovedRef.current = false;
|
|
52623
52589
|
if (parsed.patches.length === 0) {
|
|
52624
|
-
const
|
|
52590
|
+
const msg = {
|
|
52625
52591
|
role: "assistant",
|
|
52626
52592
|
content: parsed.content,
|
|
52627
52593
|
type: "text"
|
|
52628
52594
|
};
|
|
52629
|
-
setAllMessages([...currentAll,
|
|
52630
|
-
setCommitted((prev) => [...prev,
|
|
52595
|
+
setAllMessages([...currentAll, msg]);
|
|
52596
|
+
setCommitted((prev) => [...prev, msg]);
|
|
52631
52597
|
setStage({ type: "idle" });
|
|
52632
52598
|
return;
|
|
52633
52599
|
}
|
|
@@ -52651,144 +52617,8 @@ var ChatRunner = ({ repoPath }) => {
|
|
|
52651
52617
|
});
|
|
52652
52618
|
return;
|
|
52653
52619
|
}
|
|
52654
|
-
if (parsed.kind === "shell" || parsed.kind === "fetch" || parsed.kind === "read-file" || parsed.kind === "read-folder" || parsed.kind === "grep" || parsed.kind === "write-file" || parsed.kind === "delete-file" || parsed.kind === "delete-folder" || parsed.kind === "open-url" || parsed.kind === "generate-pdf" || parsed.kind === "search") {
|
|
52655
|
-
let tool;
|
|
52656
|
-
if (parsed.kind === "shell") {
|
|
52657
|
-
tool = { type: "shell", command: parsed.command };
|
|
52658
|
-
} else if (parsed.kind === "fetch") {
|
|
52659
|
-
tool = { type: "fetch", url: parsed.url };
|
|
52660
|
-
} else if (parsed.kind === "read-file") {
|
|
52661
|
-
tool = { type: "read-file", filePath: parsed.filePath };
|
|
52662
|
-
} else if (parsed.kind === "read-folder") {
|
|
52663
|
-
tool = { type: "read-folder", folderPath: parsed.folderPath };
|
|
52664
|
-
} else if (parsed.kind === "grep") {
|
|
52665
|
-
tool = { type: "grep", pattern: parsed.pattern, glob: parsed.glob };
|
|
52666
|
-
} else if (parsed.kind === "delete-file") {
|
|
52667
|
-
tool = { type: "delete-file", filePath: parsed.filePath };
|
|
52668
|
-
} else if (parsed.kind === "delete-folder") {
|
|
52669
|
-
tool = { type: "delete-folder", folderPath: parsed.folderPath };
|
|
52670
|
-
} else if (parsed.kind === "open-url") {
|
|
52671
|
-
tool = { type: "open-url", url: parsed.url };
|
|
52672
|
-
} else if (parsed.kind === "generate-pdf") {
|
|
52673
|
-
tool = {
|
|
52674
|
-
type: "generate-pdf",
|
|
52675
|
-
filePath: parsed.filePath,
|
|
52676
|
-
content: parsed.pdfContent
|
|
52677
|
-
};
|
|
52678
|
-
} else if (parsed.kind === "search") {
|
|
52679
|
-
tool = { type: "search", query: parsed.query };
|
|
52680
|
-
} else {
|
|
52681
|
-
tool = {
|
|
52682
|
-
type: "write-file",
|
|
52683
|
-
filePath: parsed.filePath,
|
|
52684
|
-
fileContent: parsed.fileContent
|
|
52685
|
-
};
|
|
52686
|
-
}
|
|
52687
|
-
if (parsed.content) {
|
|
52688
|
-
const preambleMsg = {
|
|
52689
|
-
role: "assistant",
|
|
52690
|
-
content: parsed.content,
|
|
52691
|
-
type: "text"
|
|
52692
|
-
};
|
|
52693
|
-
setAllMessages([...currentAll, preambleMsg]);
|
|
52694
|
-
setCommitted((prev) => [...prev, preambleMsg]);
|
|
52695
|
-
}
|
|
52696
|
-
const isSafeTool = parsed.kind === "read-file" || parsed.kind === "read-folder" || parsed.kind === "grep" || parsed.kind === "fetch" || parsed.kind === "open-url" || parsed.kind === "search";
|
|
52697
|
-
const executeAndContinue = async (approved) => {
|
|
52698
|
-
let result2 = "(denied by user)";
|
|
52699
|
-
if (approved) {
|
|
52700
|
-
const cacheKey = parsed.kind === "read-file" ? `read-file:${parsed.filePath}` : parsed.kind === "read-folder" ? `read-folder:${parsed.folderPath}` : parsed.kind === "grep" ? `grep:${parsed.pattern}:${parsed.glob}` : null;
|
|
52701
|
-
if (cacheKey && toolResultCache.current.has(cacheKey)) {
|
|
52702
|
-
result2 = toolResultCache.current.get(cacheKey) + `
|
|
52703
|
-
|
|
52704
|
-
[NOTE: This result was already retrieved earlier. Do not request it again.]`;
|
|
52705
|
-
} else {
|
|
52706
|
-
try {
|
|
52707
|
-
setStage({ type: "thinking" });
|
|
52708
|
-
if (parsed.kind === "shell") {
|
|
52709
|
-
result2 = await runShell(parsed.command, repoPath);
|
|
52710
|
-
} else if (parsed.kind === "fetch") {
|
|
52711
|
-
result2 = await fetchUrl(parsed.url);
|
|
52712
|
-
} else if (parsed.kind === "read-file") {
|
|
52713
|
-
result2 = readFile(parsed.filePath, repoPath);
|
|
52714
|
-
} else if (parsed.kind === "read-folder") {
|
|
52715
|
-
result2 = readFolder(parsed.folderPath, repoPath);
|
|
52716
|
-
} else if (parsed.kind === "grep") {
|
|
52717
|
-
result2 = grepFiles(parsed.pattern, parsed.glob, repoPath);
|
|
52718
|
-
} else if (parsed.kind === "delete-file") {
|
|
52719
|
-
result2 = deleteFile(parsed.filePath, repoPath);
|
|
52720
|
-
} else if (parsed.kind === "delete-folder") {
|
|
52721
|
-
result2 = deleteFolder(parsed.folderPath, repoPath);
|
|
52722
|
-
} else if (parsed.kind === "open-url") {
|
|
52723
|
-
result2 = openUrl(parsed.url);
|
|
52724
|
-
} else if (parsed.kind === "generate-pdf") {
|
|
52725
|
-
result2 = generatePdf(parsed.filePath, parsed.pdfContent, repoPath);
|
|
52726
|
-
} else if (parsed.kind === "write-file") {
|
|
52727
|
-
result2 = writeFile(parsed.filePath, parsed.fileContent, repoPath);
|
|
52728
|
-
} else if (parsed.kind === "search") {
|
|
52729
|
-
result2 = await searchWeb(parsed.query);
|
|
52730
|
-
}
|
|
52731
|
-
if (cacheKey) {
|
|
52732
|
-
toolResultCache.current.set(cacheKey, result2);
|
|
52733
|
-
}
|
|
52734
|
-
} catch (err) {
|
|
52735
|
-
result2 = `Error: ${err instanceof Error ? err.message : "failed"}`;
|
|
52736
|
-
}
|
|
52737
|
-
}
|
|
52738
|
-
}
|
|
52739
|
-
if (approved && !result2.startsWith("Error:")) {
|
|
52740
|
-
const kindMap2 = {
|
|
52741
|
-
shell: "shell-run",
|
|
52742
|
-
fetch: "url-fetched",
|
|
52743
|
-
"read-file": "file-read",
|
|
52744
|
-
"read-folder": "file-read",
|
|
52745
|
-
grep: "file-read",
|
|
52746
|
-
"delete-file": "file-written",
|
|
52747
|
-
"delete-folder": "file-written",
|
|
52748
|
-
"open-url": "url-fetched",
|
|
52749
|
-
"generate-pdf": "file-written",
|
|
52750
|
-
"write-file": "file-written",
|
|
52751
|
-
search: "url-fetched"
|
|
52752
|
-
};
|
|
52753
|
-
appendMemory({
|
|
52754
|
-
kind: kindMap2[parsed.kind] ?? "shell-run",
|
|
52755
|
-
detail: parsed.kind === "shell" ? parsed.command : parsed.kind === "fetch" ? parsed.url : parsed.kind === "search" ? parsed.query : parsed.kind === "read-folder" ? parsed.folderPath : parsed.kind === "grep" ? `${parsed.pattern} ${parsed.glob}` : parsed.kind === "delete-file" ? parsed.filePath : parsed.kind === "delete-folder" ? parsed.folderPath : parsed.kind === "open-url" ? parsed.url : parsed.kind === "generate-pdf" ? parsed.filePath : parsed.filePath,
|
|
52756
|
-
summary: result2.split(`
|
|
52757
|
-
`)[0]?.slice(0, 120) ?? "",
|
|
52758
|
-
repoPath
|
|
52759
|
-
});
|
|
52760
|
-
}
|
|
52761
|
-
const toolName = parsed.kind === "shell" ? "shell" : parsed.kind === "fetch" ? "fetch" : parsed.kind === "read-file" ? "read-file" : parsed.kind === "read-folder" ? "read-folder" : parsed.kind === "grep" ? "grep" : parsed.kind === "delete-file" ? "delete-file" : parsed.kind === "delete-folder" ? "delete-folder" : parsed.kind === "open-url" ? "open-url" : parsed.kind === "generate-pdf" ? "generate-pdf" : parsed.kind === "search" ? "search" : "write-file";
|
|
52762
|
-
const toolContent = parsed.kind === "shell" ? parsed.command : parsed.kind === "fetch" ? parsed.url : parsed.kind === "search" ? parsed.query : parsed.kind === "read-folder" ? parsed.folderPath : parsed.kind === "grep" ? `${parsed.pattern} — ${parsed.glob}` : parsed.kind === "delete-file" ? parsed.filePath : parsed.kind === "delete-folder" ? parsed.folderPath : parsed.kind === "open-url" ? parsed.url : parsed.kind === "generate-pdf" ? parsed.filePath : parsed.filePath;
|
|
52763
|
-
const toolMsg = {
|
|
52764
|
-
role: "assistant",
|
|
52765
|
-
type: "tool",
|
|
52766
|
-
toolName,
|
|
52767
|
-
content: toolContent,
|
|
52768
|
-
result: result2,
|
|
52769
|
-
approved
|
|
52770
|
-
};
|
|
52771
|
-
const withTool = [...currentAll, toolMsg];
|
|
52772
|
-
setAllMessages(withTool);
|
|
52773
|
-
setCommitted((prev) => [...prev, toolMsg]);
|
|
52774
|
-
const nextAbort = new AbortController;
|
|
52775
|
-
abortControllerRef.current = nextAbort;
|
|
52776
|
-
setStage({ type: "thinking" });
|
|
52777
|
-
callChat(provider, systemPrompt, withTool, nextAbort.signal).then((r) => processResponse(r, withTool, nextAbort.signal)).catch(handleError(withTool));
|
|
52778
|
-
};
|
|
52779
|
-
if (autoApprove && isSafeTool) {
|
|
52780
|
-
executeAndContinue(true);
|
|
52781
|
-
return;
|
|
52782
|
-
}
|
|
52783
|
-
setStage({
|
|
52784
|
-
type: "permission",
|
|
52785
|
-
tool,
|
|
52786
|
-
pendingMessages: currentAll,
|
|
52787
|
-
resolve: executeAndContinue
|
|
52788
|
-
});
|
|
52789
|
-
return;
|
|
52790
|
-
}
|
|
52791
52620
|
if (parsed.kind === "clone") {
|
|
52621
|
+
batchApprovedRef.current = false;
|
|
52792
52622
|
if (parsed.content) {
|
|
52793
52623
|
const preambleMsg = {
|
|
52794
52624
|
role: "assistant",
|
|
@@ -52805,23 +52635,116 @@ var ChatRunner = ({ repoPath }) => {
|
|
|
52805
52635
|
});
|
|
52806
52636
|
return;
|
|
52807
52637
|
}
|
|
52808
|
-
|
|
52809
|
-
|
|
52810
|
-
|
|
52811
|
-
|
|
52812
|
-
|
|
52813
|
-
|
|
52814
|
-
|
|
52815
|
-
|
|
52816
|
-
|
|
52817
|
-
|
|
52818
|
-
|
|
52819
|
-
|
|
52820
|
-
|
|
52821
|
-
|
|
52822
|
-
|
|
52638
|
+
if (parsed.kind === "text") {
|
|
52639
|
+
batchApprovedRef.current = false;
|
|
52640
|
+
const msg = {
|
|
52641
|
+
role: "assistant",
|
|
52642
|
+
content: parsed.content,
|
|
52643
|
+
type: "text"
|
|
52644
|
+
};
|
|
52645
|
+
const withMsg = [...currentAll, msg];
|
|
52646
|
+
setAllMessages(withMsg);
|
|
52647
|
+
setCommitted((prev) => [...prev, msg]);
|
|
52648
|
+
const lastUserMsg = [...currentAll].reverse().find((m) => m.role === "user");
|
|
52649
|
+
const githubUrl = lastUserMsg ? extractGithubUrl(lastUserMsg.content) : null;
|
|
52650
|
+
if (githubUrl && !clonedUrls.has(githubUrl)) {
|
|
52651
|
+
setTimeout(() => setStage({ type: "clone-offer", repoUrl: githubUrl }), 80);
|
|
52652
|
+
} else {
|
|
52653
|
+
setStage({ type: "idle" });
|
|
52654
|
+
}
|
|
52655
|
+
return;
|
|
52656
|
+
}
|
|
52657
|
+
const tool = registry.get(parsed.toolName);
|
|
52658
|
+
if (!tool) {
|
|
52659
|
+
batchApprovedRef.current = false;
|
|
52823
52660
|
setStage({ type: "idle" });
|
|
52661
|
+
return;
|
|
52662
|
+
}
|
|
52663
|
+
if (parsed.content) {
|
|
52664
|
+
const preambleMsg = {
|
|
52665
|
+
role: "assistant",
|
|
52666
|
+
content: parsed.content,
|
|
52667
|
+
type: "text"
|
|
52668
|
+
};
|
|
52669
|
+
setAllMessages([...currentAll, preambleMsg]);
|
|
52670
|
+
setCommitted((prev) => [...prev, preambleMsg]);
|
|
52671
|
+
}
|
|
52672
|
+
const remainder = parsed.remainder;
|
|
52673
|
+
const isSafe = tool.safe ?? false;
|
|
52674
|
+
const executeAndContinue = async (approved) => {
|
|
52675
|
+
if (approved && remainder) {
|
|
52676
|
+
batchApprovedRef.current = true;
|
|
52677
|
+
}
|
|
52678
|
+
let result2 = "(denied by user)";
|
|
52679
|
+
if (approved) {
|
|
52680
|
+
const cacheKey = isSafe ? `${parsed.toolName}:${parsed.rawInput}` : null;
|
|
52681
|
+
if (cacheKey && toolResultCache.current.has(cacheKey)) {
|
|
52682
|
+
result2 = toolResultCache.current.get(cacheKey) + `
|
|
52683
|
+
|
|
52684
|
+
[NOTE: This result was already retrieved earlier. Do not request it again.]`;
|
|
52685
|
+
} else {
|
|
52686
|
+
try {
|
|
52687
|
+
setStage({ type: "thinking" });
|
|
52688
|
+
const toolResult = await tool.execute(parsed.input, {
|
|
52689
|
+
repoPath,
|
|
52690
|
+
messages: currentAll
|
|
52691
|
+
});
|
|
52692
|
+
result2 = toolResult.value;
|
|
52693
|
+
if (cacheKey && toolResult.kind === "text") {
|
|
52694
|
+
toolResultCache.current.set(cacheKey, result2);
|
|
52695
|
+
}
|
|
52696
|
+
} catch (err) {
|
|
52697
|
+
result2 = `Error: ${err instanceof Error ? err.message : "failed"}`;
|
|
52698
|
+
}
|
|
52699
|
+
}
|
|
52700
|
+
}
|
|
52701
|
+
if (approved && !result2.startsWith("Error:")) {
|
|
52702
|
+
appendMemory({
|
|
52703
|
+
kind: "shell-run",
|
|
52704
|
+
detail: tool.summariseInput ? String(tool.summariseInput(parsed.input)) : parsed.rawInput,
|
|
52705
|
+
summary: result2.split(`
|
|
52706
|
+
`)[0]?.slice(0, 120) ?? "",
|
|
52707
|
+
repoPath
|
|
52708
|
+
});
|
|
52709
|
+
}
|
|
52710
|
+
const displayContent = tool.summariseInput ? String(tool.summariseInput(parsed.input)) : parsed.rawInput;
|
|
52711
|
+
const toolMsg = {
|
|
52712
|
+
role: "assistant",
|
|
52713
|
+
type: "tool",
|
|
52714
|
+
toolName: parsed.toolName,
|
|
52715
|
+
content: displayContent,
|
|
52716
|
+
result: result2,
|
|
52717
|
+
approved
|
|
52718
|
+
};
|
|
52719
|
+
const withTool = [...currentAll, toolMsg];
|
|
52720
|
+
setAllMessages(withTool);
|
|
52721
|
+
setCommitted((prev) => [...prev, toolMsg]);
|
|
52722
|
+
if (approved && remainder && remainder.length > 0) {
|
|
52723
|
+
processResponse(remainder, withTool, signal);
|
|
52724
|
+
return;
|
|
52725
|
+
}
|
|
52726
|
+
batchApprovedRef.current = false;
|
|
52727
|
+
const nextAbort = new AbortController;
|
|
52728
|
+
abortControllerRef.current = nextAbort;
|
|
52729
|
+
setStage({ type: "thinking" });
|
|
52730
|
+
callChat(provider, systemPrompt, withTool, nextAbort.signal).then((r) => processResponse(r, withTool, nextAbort.signal)).catch(handleError(withTool));
|
|
52731
|
+
};
|
|
52732
|
+
if (autoApprove && isSafe || batchApprovedRef.current) {
|
|
52733
|
+
executeAndContinue(true);
|
|
52734
|
+
return;
|
|
52824
52735
|
}
|
|
52736
|
+
const permLabel = tool.permissionLabel ?? tool.name;
|
|
52737
|
+
const permValue = tool.summariseInput ? String(tool.summariseInput(parsed.input)) : parsed.rawInput;
|
|
52738
|
+
setStage({
|
|
52739
|
+
type: "permission",
|
|
52740
|
+
tool: {
|
|
52741
|
+
type: parsed.toolName,
|
|
52742
|
+
_display: permValue,
|
|
52743
|
+
_label: permLabel
|
|
52744
|
+
},
|
|
52745
|
+
pendingMessages: currentAll,
|
|
52746
|
+
resolve: executeAndContinue
|
|
52747
|
+
});
|
|
52825
52748
|
};
|
|
52826
52749
|
const sendMessage = (text) => {
|
|
52827
52750
|
if (!provider)
|
|
@@ -52839,7 +52762,7 @@ var ChatRunner = ({ repoPath }) => {
|
|
|
52839
52762
|
setAutoApprove(next);
|
|
52840
52763
|
const msg = {
|
|
52841
52764
|
role: "assistant",
|
|
52842
|
-
content: next ? "Auto-approve ON — read, search,
|
|
52765
|
+
content: next ? "Auto-approve ON — safe tools (read, search, fetch) will run without asking." : "Auto-approve OFF — all tools will ask for permission.",
|
|
52843
52766
|
type: "text"
|
|
52844
52767
|
};
|
|
52845
52768
|
setCommitted((prev) => [...prev, msg]);
|
|
@@ -52848,13 +52771,13 @@ var ChatRunner = ({ repoPath }) => {
|
|
|
52848
52771
|
}
|
|
52849
52772
|
if (text.trim().toLowerCase() === "/clear history") {
|
|
52850
52773
|
clearRepoMemory(repoPath);
|
|
52851
|
-
const
|
|
52774
|
+
const msg = {
|
|
52852
52775
|
role: "assistant",
|
|
52853
52776
|
content: "History cleared for this repo.",
|
|
52854
52777
|
type: "text"
|
|
52855
52778
|
};
|
|
52856
|
-
setCommitted((prev) => [...prev,
|
|
52857
|
-
setAllMessages((prev) => [...prev,
|
|
52779
|
+
setCommitted((prev) => [...prev, msg]);
|
|
52780
|
+
setAllMessages((prev) => [...prev, msg]);
|
|
52858
52781
|
return;
|
|
52859
52782
|
}
|
|
52860
52783
|
if (text.trim().toLowerCase() === "/chat") {
|
|
@@ -52901,7 +52824,7 @@ var ChatRunner = ({ repoPath }) => {
|
|
|
52901
52824
|
if (!name) {
|
|
52902
52825
|
const msg2 = {
|
|
52903
52826
|
role: "assistant",
|
|
52904
|
-
content: "Usage: `/chat delete <
|
|
52827
|
+
content: "Usage: `/chat delete <n>`",
|
|
52905
52828
|
type: "text"
|
|
52906
52829
|
};
|
|
52907
52830
|
setCommitted((prev) => [...prev, msg2]);
|
|
@@ -53052,6 +52975,7 @@ ${mems.map((m) => `- [${m.id}] ${m.content}`).join(`
|
|
|
53052
52975
|
setCommitted((prev) => [...prev, userMsg]);
|
|
53053
52976
|
setAllMessages(nextAll);
|
|
53054
52977
|
toolResultCache.current.clear();
|
|
52978
|
+
batchApprovedRef.current = false;
|
|
53055
52979
|
inputHistoryRef.current = [
|
|
53056
52980
|
text,
|
|
53057
52981
|
...inputHistoryRef.current.filter((m) => m !== text)
|
|
@@ -53074,6 +52998,7 @@ ${mems.map((m) => `- [${m.id}] ${m.content}`).join(`
|
|
|
53074
52998
|
if (stage.type === "thinking" && key.escape) {
|
|
53075
52999
|
abortControllerRef.current?.abort();
|
|
53076
53000
|
abortControllerRef.current = null;
|
|
53001
|
+
batchApprovedRef.current = false;
|
|
53077
53002
|
setStage({ type: "idle" });
|
|
53078
53003
|
return;
|
|
53079
53004
|
}
|
|
@@ -53092,8 +53017,7 @@ ${mems.map((m) => `- [${m.id}] ${m.content}`).join(`
|
|
|
53092
53017
|
if (key.downArrow) {
|
|
53093
53018
|
const next = historyIndexRef.current - 1;
|
|
53094
53019
|
historyIndexRef.current = next;
|
|
53095
|
-
|
|
53096
|
-
setInputValue(val);
|
|
53020
|
+
setInputValue(next < 0 ? "" : inputHistoryRef.current[next]);
|
|
53097
53021
|
setInputKey((k) => k + 1);
|
|
53098
53022
|
return;
|
|
53099
53023
|
}
|
|
@@ -53153,16 +53077,14 @@ ${mems.map((m) => `- [${m.id}] ${m.content}`).join(`
|
|
|
53153
53077
|
if (stage.type === "clone-exists") {
|
|
53154
53078
|
if (input === "y" || input === "Y") {
|
|
53155
53079
|
const { repoUrl, repoPath: existingPath } = stage;
|
|
53156
|
-
const cloneUrl = toCloneUrl(repoUrl);
|
|
53157
53080
|
setStage({ type: "cloning", repoUrl });
|
|
53158
|
-
startCloneRepo(
|
|
53081
|
+
startCloneRepo(toCloneUrl(repoUrl), { forceReclone: true }).then((result2) => {
|
|
53159
53082
|
if (result2.done) {
|
|
53160
|
-
const fileCount = walkDir3(existingPath).length;
|
|
53161
53083
|
setStage({
|
|
53162
53084
|
type: "clone-done",
|
|
53163
53085
|
repoUrl,
|
|
53164
53086
|
destPath: existingPath,
|
|
53165
|
-
fileCount
|
|
53087
|
+
fileCount: walkDir3(existingPath).length
|
|
53166
53088
|
});
|
|
53167
53089
|
} else {
|
|
53168
53090
|
setStage({
|
|
@@ -53175,12 +53097,11 @@ ${mems.map((m) => `- [${m.id}] ${m.content}`).join(`
|
|
|
53175
53097
|
}
|
|
53176
53098
|
if (input === "n" || input === "N") {
|
|
53177
53099
|
const { repoUrl, repoPath: existingPath } = stage;
|
|
53178
|
-
const fileCount = walkDir3(existingPath).length;
|
|
53179
53100
|
setStage({
|
|
53180
53101
|
type: "clone-done",
|
|
53181
53102
|
repoUrl,
|
|
53182
53103
|
destPath: existingPath,
|
|
53183
|
-
fileCount
|
|
53104
|
+
fileCount: walkDir3(existingPath).length
|
|
53184
53105
|
});
|
|
53185
53106
|
return;
|
|
53186
53107
|
}
|
|
@@ -53202,7 +53123,7 @@ Ask me anything about it — I can read files, explain how it works, or suggest
|
|
|
53202
53123
|
type: "tool",
|
|
53203
53124
|
toolName: "fetch",
|
|
53204
53125
|
content: stage.repoUrl,
|
|
53205
|
-
result: `Clone complete. Repo: ${repoName}. Local path: ${stage.destPath}. ${stage.fileCount} files
|
|
53126
|
+
result: `Clone complete. Repo: ${repoName}. Local path: ${stage.destPath}. ${stage.fileCount} files.`,
|
|
53206
53127
|
approved: true
|
|
53207
53128
|
};
|
|
53208
53129
|
const withClone = [...allMessages, contextMsg, summaryMsg];
|
|
@@ -53223,6 +53144,7 @@ Ask me anything about it — I can read files, explain how it works, or suggest
|
|
|
53223
53144
|
return;
|
|
53224
53145
|
}
|
|
53225
53146
|
if (input === "n" || input === "N" || key.escape) {
|
|
53147
|
+
batchApprovedRef.current = false;
|
|
53226
53148
|
stage.resolve(false);
|
|
53227
53149
|
return;
|
|
53228
53150
|
}
|
|
@@ -53312,17 +53234,16 @@ ${lensFile.overview}
|
|
|
53312
53234
|
|
|
53313
53235
|
Important folders: ${lensFile.importantFolders.join(", ")}
|
|
53314
53236
|
Suggestions: ${lensFile.suggestions.slice(0, 3).join("; ")}` : "";
|
|
53315
|
-
|
|
53316
|
-
|
|
53317
|
-
|
|
53318
|
-
I have memory of previous actions in this repo.` : "";
|
|
53319
|
-
const lensGreetNote = lensFile ? `
|
|
53320
|
-
|
|
53321
|
-
Found LENS.md — I have context from a previous analysis of this repo.` : "";
|
|
53237
|
+
const toolsSection = registry.buildSystemPromptSection();
|
|
53238
|
+
setSystemPrompt(buildSystemPrompt(importantFiles, historySummary, toolsSection) + lensContext);
|
|
53322
53239
|
const greeting = {
|
|
53323
53240
|
role: "assistant",
|
|
53324
|
-
content: `Welcome to Lens
|
|
53325
|
-
Codebase loaded — ${importantFiles.length} files indexed.${
|
|
53241
|
+
content: `Welcome to Lens
|
|
53242
|
+
Codebase loaded — ${importantFiles.length} files indexed.${historySummary ? `
|
|
53243
|
+
|
|
53244
|
+
I have memory of previous actions in this repo.` : ""}${lensFile ? `
|
|
53245
|
+
|
|
53246
|
+
Found LENS.md — I have context from a previous analysis of this repo.` : ""}
|
|
53326
53247
|
Ask me anything, tell me what to build, share a URL, or ask me to read/write files.
|
|
53327
53248
|
|
|
53328
53249
|
Tip: type /timeline to browse commit history.`,
|
|
@@ -53337,7 +53258,7 @@ Tip: type /timeline to browse commit history.`,
|
|
|
53337
53258
|
return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(ProviderPicker, {
|
|
53338
53259
|
onDone: handleProviderDone
|
|
53339
53260
|
}, undefined, false, undefined, this);
|
|
53340
|
-
if (stage.type === "loading")
|
|
53261
|
+
if (stage.type === "loading")
|
|
53341
53262
|
return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
|
|
53342
53263
|
gap: 1,
|
|
53343
53264
|
marginTop: 1,
|
|
@@ -53357,19 +53278,16 @@ Tip: type /timeline to browse commit history.`,
|
|
|
53357
53278
|
}, undefined, false, undefined, this)
|
|
53358
53279
|
]
|
|
53359
53280
|
}, undefined, true, undefined, this);
|
|
53360
|
-
|
|
53361
|
-
if (showTimeline) {
|
|
53281
|
+
if (showTimeline)
|
|
53362
53282
|
return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(TimelineRunner, {
|
|
53363
53283
|
repoPath,
|
|
53364
53284
|
onExit: () => setShowTimeline(false)
|
|
53365
53285
|
}, undefined, false, undefined, this);
|
|
53366
|
-
|
|
53367
|
-
if (showReview) {
|
|
53286
|
+
if (showReview)
|
|
53368
53287
|
return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(ReviewCommand, {
|
|
53369
53288
|
path: repoPath,
|
|
53370
53289
|
onExit: () => setShowReview(false)
|
|
53371
53290
|
}, undefined, false, undefined, this);
|
|
53372
|
-
}
|
|
53373
53291
|
if (stage.type === "clone-offer")
|
|
53374
53292
|
return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(CloneOfferView, {
|
|
53375
53293
|
stage,
|
|
@@ -53510,9 +53428,259 @@ var TimelineCommand = ({ path: inputPath }) => {
|
|
|
53510
53428
|
repoPath: resolvedPath
|
|
53511
53429
|
}, undefined, false, undefined, this);
|
|
53512
53430
|
};
|
|
53431
|
+
// src/utils/tools/builtins.ts
|
|
53432
|
+
var fetchTool = {
|
|
53433
|
+
name: "fetch",
|
|
53434
|
+
description: "load a URL",
|
|
53435
|
+
safe: true,
|
|
53436
|
+
permissionLabel: "fetch",
|
|
53437
|
+
systemPromptEntry: (i) => `### ${i}. fetch — load a URL
|
|
53438
|
+
<fetch>https://example.com</fetch>`,
|
|
53439
|
+
parseInput: (body) => body.replace(/^<|>$/g, "").trim() || null,
|
|
53440
|
+
summariseInput: (url) => url,
|
|
53441
|
+
execute: async (url) => {
|
|
53442
|
+
try {
|
|
53443
|
+
const value = await fetchUrl(url);
|
|
53444
|
+
return { kind: "text", value };
|
|
53445
|
+
} catch (err) {
|
|
53446
|
+
return {
|
|
53447
|
+
kind: "error",
|
|
53448
|
+
value: `Fetch failed: ${err instanceof Error ? err.message : String(err)}`
|
|
53449
|
+
};
|
|
53450
|
+
}
|
|
53451
|
+
}
|
|
53452
|
+
};
|
|
53453
|
+
var shellTool = {
|
|
53454
|
+
name: "shell",
|
|
53455
|
+
description: "run a terminal command",
|
|
53456
|
+
safe: false,
|
|
53457
|
+
permissionLabel: "run",
|
|
53458
|
+
systemPromptEntry: (i) => `### ${i}. shell — run a terminal command
|
|
53459
|
+
<shell>node -v</shell>`,
|
|
53460
|
+
parseInput: (body) => body || null,
|
|
53461
|
+
summariseInput: (cmd) => cmd,
|
|
53462
|
+
execute: async (cmd, ctx) => {
|
|
53463
|
+
const value = await runShell(cmd, ctx.repoPath);
|
|
53464
|
+
return { kind: "text", value };
|
|
53465
|
+
}
|
|
53466
|
+
};
|
|
53467
|
+
var readFileTool = {
|
|
53468
|
+
name: "read-file",
|
|
53469
|
+
description: "read a file from the repo",
|
|
53470
|
+
safe: true,
|
|
53471
|
+
permissionLabel: "read",
|
|
53472
|
+
systemPromptEntry: (i) => `### ${i}. read-file — read a file from the repo
|
|
53473
|
+
<read-file>src/foo.ts</read-file>`,
|
|
53474
|
+
parseInput: (body) => body || null,
|
|
53475
|
+
summariseInput: (p) => p,
|
|
53476
|
+
execute: (filePath, ctx) => ({
|
|
53477
|
+
kind: "text",
|
|
53478
|
+
value: readFile(filePath, ctx.repoPath)
|
|
53479
|
+
})
|
|
53480
|
+
};
|
|
53481
|
+
var readFolderTool = {
|
|
53482
|
+
name: "read-folder",
|
|
53483
|
+
description: "list contents of a folder (files + subfolders, one level deep)",
|
|
53484
|
+
safe: true,
|
|
53485
|
+
permissionLabel: "folder",
|
|
53486
|
+
systemPromptEntry: (i) => `### ${i}. read-folder — list contents of a folder (files + subfolders, one level deep)
|
|
53487
|
+
<read-folder>src/components</read-folder>`,
|
|
53488
|
+
parseInput: (body) => body || null,
|
|
53489
|
+
summariseInput: (p) => p,
|
|
53490
|
+
execute: (folderPath, ctx) => ({
|
|
53491
|
+
kind: "text",
|
|
53492
|
+
value: readFolder(folderPath, ctx.repoPath)
|
|
53493
|
+
})
|
|
53494
|
+
};
|
|
53495
|
+
var grepTool = {
|
|
53496
|
+
name: "grep",
|
|
53497
|
+
description: "search for a pattern across files in the repo",
|
|
53498
|
+
safe: true,
|
|
53499
|
+
permissionLabel: "grep",
|
|
53500
|
+
systemPromptEntry: (i) => `### ${i}. grep — search for a pattern across files in the repo (cross-platform, no shell needed)
|
|
53501
|
+
<grep>
|
|
53502
|
+
{"pattern": "ChatRunner", "glob": "src/**/*.tsx"}
|
|
53503
|
+
</grep>`,
|
|
53504
|
+
parseInput: (body) => {
|
|
53505
|
+
try {
|
|
53506
|
+
const parsed = JSON.parse(body);
|
|
53507
|
+
return { pattern: parsed.pattern, glob: parsed.glob ?? "**/*" };
|
|
53508
|
+
} catch {
|
|
53509
|
+
return { pattern: body, glob: "**/*" };
|
|
53510
|
+
}
|
|
53511
|
+
},
|
|
53512
|
+
summariseInput: ({ pattern, glob }) => `${pattern} — ${glob}`,
|
|
53513
|
+
execute: ({ pattern, glob }, ctx) => ({
|
|
53514
|
+
kind: "text",
|
|
53515
|
+
value: grepFiles(pattern, glob, ctx.repoPath)
|
|
53516
|
+
})
|
|
53517
|
+
};
|
|
53518
|
+
var writeFileTool = {
|
|
53519
|
+
name: "write-file",
|
|
53520
|
+
description: "create or overwrite a file",
|
|
53521
|
+
safe: false,
|
|
53522
|
+
permissionLabel: "write",
|
|
53523
|
+
systemPromptEntry: (i) => `### ${i}. write-file — create or overwrite a file
|
|
53524
|
+
<write-file>
|
|
53525
|
+
{"path": "data/output.csv", "content": "col1,col2\\nval1,val2"}
|
|
53526
|
+
</write-file>`,
|
|
53527
|
+
parseInput: (body) => {
|
|
53528
|
+
try {
|
|
53529
|
+
const parsed = JSON.parse(body);
|
|
53530
|
+
if (!parsed.path)
|
|
53531
|
+
return null;
|
|
53532
|
+
return parsed;
|
|
53533
|
+
} catch {
|
|
53534
|
+
return null;
|
|
53535
|
+
}
|
|
53536
|
+
},
|
|
53537
|
+
summariseInput: ({ path: path21, content }) => `${path21} (${content.length} bytes)`,
|
|
53538
|
+
execute: ({ path: filePath, content }, ctx) => ({
|
|
53539
|
+
kind: "text",
|
|
53540
|
+
value: writeFile(filePath, content, ctx.repoPath)
|
|
53541
|
+
})
|
|
53542
|
+
};
|
|
53543
|
+
var deleteFileTool = {
|
|
53544
|
+
name: "delete-file",
|
|
53545
|
+
description: "permanently delete a single file",
|
|
53546
|
+
safe: false,
|
|
53547
|
+
permissionLabel: "delete",
|
|
53548
|
+
systemPromptEntry: (i) => `### ${i}. delete-file — permanently delete a single file
|
|
53549
|
+
<delete-file>src/old-component.tsx</delete-file>`,
|
|
53550
|
+
parseInput: (body) => body || null,
|
|
53551
|
+
summariseInput: (p) => p,
|
|
53552
|
+
execute: (filePath, ctx) => ({
|
|
53553
|
+
kind: "text",
|
|
53554
|
+
value: deleteFile(filePath, ctx.repoPath)
|
|
53555
|
+
})
|
|
53556
|
+
};
|
|
53557
|
+
var deleteFolderTool = {
|
|
53558
|
+
name: "delete-folder",
|
|
53559
|
+
description: "permanently delete a folder and all its contents",
|
|
53560
|
+
safe: false,
|
|
53561
|
+
permissionLabel: "delete folder",
|
|
53562
|
+
systemPromptEntry: (i) => `### ${i}. delete-folder — permanently delete a folder and all its contents
|
|
53563
|
+
<delete-folder>src/legacy</delete-folder>`,
|
|
53564
|
+
parseInput: (body) => body || null,
|
|
53565
|
+
summariseInput: (p) => p,
|
|
53566
|
+
execute: (folderPath, ctx) => ({
|
|
53567
|
+
kind: "text",
|
|
53568
|
+
value: deleteFolder(folderPath, ctx.repoPath)
|
|
53569
|
+
})
|
|
53570
|
+
};
|
|
53571
|
+
var openUrlTool = {
|
|
53572
|
+
name: "open-url",
|
|
53573
|
+
description: "open a URL in the user's default browser",
|
|
53574
|
+
safe: true,
|
|
53575
|
+
permissionLabel: "open",
|
|
53576
|
+
systemPromptEntry: (i) => `### ${i}. open-url — open a URL in the user's default browser
|
|
53577
|
+
<open-url>https://github.com/owner/repo</open-url>`,
|
|
53578
|
+
parseInput: (body) => body.replace(/^<|>$/g, "").trim() || null,
|
|
53579
|
+
summariseInput: (url) => url,
|
|
53580
|
+
execute: (url) => ({ kind: "text", value: openUrl(url) })
|
|
53581
|
+
};
|
|
53582
|
+
var generatePdfTool = {
|
|
53583
|
+
name: "generate-pdf",
|
|
53584
|
+
description: "generate a PDF file from markdown-style content",
|
|
53585
|
+
safe: false,
|
|
53586
|
+
permissionLabel: "pdf",
|
|
53587
|
+
systemPromptEntry: (i) => `### ${i}. generate-pdf — generate a PDF file from markdown-style content
|
|
53588
|
+
<generate-pdf>
|
|
53589
|
+
{"path": "output/report.pdf", "content": "# Title\\n\\nSome body text."}
|
|
53590
|
+
</generate-pdf>`,
|
|
53591
|
+
parseInput: (body) => {
|
|
53592
|
+
try {
|
|
53593
|
+
const parsed = JSON.parse(body);
|
|
53594
|
+
return {
|
|
53595
|
+
filePath: parsed.path ?? parsed.filePath ?? "output.pdf",
|
|
53596
|
+
content: parsed.content ?? ""
|
|
53597
|
+
};
|
|
53598
|
+
} catch {
|
|
53599
|
+
return null;
|
|
53600
|
+
}
|
|
53601
|
+
},
|
|
53602
|
+
summariseInput: ({ filePath }) => filePath,
|
|
53603
|
+
execute: ({ filePath, content }, ctx) => ({
|
|
53604
|
+
kind: "text",
|
|
53605
|
+
value: generatePdf(filePath, content, ctx.repoPath)
|
|
53606
|
+
})
|
|
53607
|
+
};
|
|
53608
|
+
var searchTool = {
|
|
53609
|
+
name: "search",
|
|
53610
|
+
description: "search the internet for anything you are unsure about",
|
|
53611
|
+
safe: true,
|
|
53612
|
+
permissionLabel: "search",
|
|
53613
|
+
systemPromptEntry: (i) => `### ${i}. search — search the internet for anything you are unsure about
|
|
53614
|
+
<search>how to use React useEffect cleanup function</search>`,
|
|
53615
|
+
parseInput: (body) => body || null,
|
|
53616
|
+
summariseInput: (q) => `"${q}"`,
|
|
53617
|
+
execute: async (query) => {
|
|
53618
|
+
try {
|
|
53619
|
+
const value = await searchWeb(query);
|
|
53620
|
+
return { kind: "text", value };
|
|
53621
|
+
} catch (err) {
|
|
53622
|
+
return {
|
|
53623
|
+
kind: "error",
|
|
53624
|
+
value: `Search failed: ${err instanceof Error ? err.message : String(err)}`
|
|
53625
|
+
};
|
|
53626
|
+
}
|
|
53627
|
+
}
|
|
53628
|
+
};
|
|
53629
|
+
var cloneTool = {
|
|
53630
|
+
name: "clone",
|
|
53631
|
+
description: "clone a GitHub repo so you can explore and discuss it",
|
|
53632
|
+
safe: false,
|
|
53633
|
+
permissionLabel: "clone",
|
|
53634
|
+
systemPromptEntry: (i) => `### ${i}. clone — clone a GitHub repo so you can explore and discuss it
|
|
53635
|
+
<clone>https://github.com/owner/repo</clone>`,
|
|
53636
|
+
parseInput: (body) => body.replace(/^<|>$/g, "").trim() || null,
|
|
53637
|
+
summariseInput: (url) => url,
|
|
53638
|
+
execute: (repoUrl) => ({
|
|
53639
|
+
kind: "text",
|
|
53640
|
+
value: `Clone of ${repoUrl} was handled by the UI.`
|
|
53641
|
+
})
|
|
53642
|
+
};
|
|
53643
|
+
var changesTool = {
|
|
53644
|
+
name: "changes",
|
|
53645
|
+
description: "propose code edits (shown as a diff for user approval)",
|
|
53646
|
+
safe: false,
|
|
53647
|
+
permissionLabel: "changes",
|
|
53648
|
+
systemPromptEntry: (i) => `### ${i}. changes — propose code edits (shown as a diff for user approval)
|
|
53649
|
+
<changes>
|
|
53650
|
+
{"summary": "what changed and why", "patches": [{"path": "src/foo.ts", "content": "COMPLETE file content", "isNew": false}]}
|
|
53651
|
+
</changes>`,
|
|
53652
|
+
parseInput: (body) => {
|
|
53653
|
+
try {
|
|
53654
|
+
return JSON.parse(body);
|
|
53655
|
+
} catch {
|
|
53656
|
+
return null;
|
|
53657
|
+
}
|
|
53658
|
+
},
|
|
53659
|
+
summariseInput: ({ summary }) => summary,
|
|
53660
|
+
execute: ({ summary }) => ({
|
|
53661
|
+
kind: "text",
|
|
53662
|
+
value: `Changes proposed: ${summary}`
|
|
53663
|
+
})
|
|
53664
|
+
};
|
|
53665
|
+
function registerBuiltins() {
|
|
53666
|
+
registry.register(fetchTool);
|
|
53667
|
+
registry.register(shellTool);
|
|
53668
|
+
registry.register(readFileTool);
|
|
53669
|
+
registry.register(readFolderTool);
|
|
53670
|
+
registry.register(grepTool);
|
|
53671
|
+
registry.register(writeFileTool);
|
|
53672
|
+
registry.register(deleteFileTool);
|
|
53673
|
+
registry.register(deleteFolderTool);
|
|
53674
|
+
registry.register(openUrlTool);
|
|
53675
|
+
registry.register(generatePdfTool);
|
|
53676
|
+
registry.register(searchTool);
|
|
53677
|
+
registry.register(cloneTool);
|
|
53678
|
+
registry.register(changesTool);
|
|
53679
|
+
}
|
|
53513
53680
|
|
|
53514
53681
|
// src/index.tsx
|
|
53515
53682
|
var jsx_dev_runtime25 = __toESM(require_jsx_dev_runtime(), 1);
|
|
53683
|
+
registerBuiltins();
|
|
53516
53684
|
var program2 = new Command;
|
|
53517
53685
|
program2.command("repo <url>").description("Analyze a remote repository").action((url) => {
|
|
53518
53686
|
render_default(/* @__PURE__ */ jsx_dev_runtime25.jsxDEV(RepoCommand, {
|