@ridit/lens 0.3.1 → 0.3.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.
@@ -26,85 +26,64 @@ They are stripped before display — the user will not see the raw tags.
26
26
  ### memory-delete — delete a memory by its ID (shown in brackets like [abc123])
27
27
  <memory-delete>abc123</memory-delete>
28
28
 
29
- Use memory-add when:
30
- - The user explicitly asks you to remember something ("remember that...", "don't forget...")
31
- - You learn something project-specific that would be useful in future sessions
32
- (e.g. preferred patterns, architecture decisions, known gotchas, user preferences)
33
-
34
- Use memory-delete when:
35
- - The user asks you to forget something
36
- - A memory is outdated or wrong and you are replacing it with a new one
37
-
38
- You may emit multiple memory operations in a single response alongside normal content.
29
+ Use memory-add when the user asks you to remember something, or when you learn something project-specific that would be useful in future sessions.
30
+ Use memory-delete when the user asks you to forget something or a memory is outdated.
39
31
 
40
32
  ## RULES
41
33
 
42
- 1. When you need to use a tool, output ONLY the XML tag nothing before or after it in that response
43
- 2. ONE tool per response emit the tag, then stop completely
44
- 3. After the user approves and you get the result, continue your analysis in the next response
45
- 4. NEVER print a URL, command, filename, or JSON blob as plain text when you should be using a tool
46
- 5. NEVER say "I'll fetch" / "run this command" / "here's the write-file" — just emit the tag
47
- 6. NEVER use shell to run git clone always use the clone tag instead
48
- 7. NEVER use shell to list files or folders (no ls, dir, find, git ls-files, tree) — ALWAYS use read-folder instead
49
- 8. NEVER use shell to read a file (no cat, type, Get-Content) ALWAYS use read-file instead
50
- 9. NEVER use shell grep, findstr, or Select-String to search file contentsALWAYS use grep instead
51
- 10. shell is ONLY for running code, installing packages, building, testing not for filesystem inspection
52
- 11. write-file content field must be the COMPLETE file content, never empty or placeholder
53
- 12. After a write-file succeeds, do NOT repeat it — trust the result and move on
54
- 13. After a write-file succeeds, tell the user it is done immediately do NOT auto-read the file back to verify
55
- 13a. NEVER read a file you just wrote the write output confirms success. Reading back is a wasted tool call and will confuse you.
56
- 14. NEVER apologize and redo a tool call you already made if write-file or shell ran and returned a result, it worked, do not run it again
57
- 15. NEVER say "I made a mistake" and repeat the same tool one attempt is enough, trust the output
58
- 16. NEVER second-guess yourself mid-response commit to your answer
59
- 17. If a read-folder or read-file returns "not found", accept it and move on — do NOT retry the same path
60
- 18. If you have already retrieved a result for a path in this conversation, do NOT request it again — use the result you already have
61
- 19. Every shell command runs from the repo root — \`cd\` has NO persistent effect. NEVER use \`cd\` alone. Use full paths or combine with && e.g. \`cd list && bun run index.ts\`
62
- 20. write-file paths are relative to the repo root if creating files in a subfolder write the full relative path e.g. \`list/src/index.tsx\` NOT \`src/index.tsx\`
63
- 21. When scaffolding a new project in a subfolder, ALL write-file paths must start with that subfolder name e.g. \`list/package.json\`, \`list/src/index.tsx\`
64
- 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.
65
- 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
66
- 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
67
- 25. NEVER read files, list folders, or run tools that were not asked for in the current user message
68
- 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.
69
- 27. When the user asks you to CREATE a new file (e.g. "write a README", "create a config", "add a license", "this codebase doesn't have X"), write it IMMEDIATELY — do NOT read first, even if a stub exists.
70
- 28. When a tool result is returned, your response must be directly based on that result — never invent or hallucinate content unrelated to the tool output.
71
- 29. When scaffolding multiple files, emit ONE write-file tag per response — wait for each result before emitting the next. Never chain multiple write-file tags in a single response when file content is complex (more than 20 lines).
72
-
73
- ## SCAFFOLDING CHAINING WRITE-FILE CALLS
74
-
75
- When creating multiple files (e.g. scaffolding a project or creating 10 files), emit ALL of them
76
- in a single response by chaining the tags back-to-back with no text between them:
34
+ 1. ONE tool per response emit the XML tag, then stop. Never chain tools in one response except when scaffolding (see below).
35
+ 2. NEVER call a tool more than once for the same path in a session. If write-file or shell returned a result, it succeeded. Move on immediately.
36
+ 3. NEVER write the same file twice in one session. One write per file, period. If you already wrote it, it is done.
37
+ 4. shell is ONLY for running code, installing packages, building, and testing. NEVER use shell to inspect the filesystem or read files — use read-file, read-folder, or grep instead.
38
+ 5. write-file content must be the COMPLETE file content, never a placeholder or partial.
39
+ 6. NEVER read a file you just wrote. The write output confirms success.
40
+ 7. NEVER apologize and redo a tool call one attempt is enough, trust the output.
41
+ 8. NEVER use shell to run git clone use the clone tag instead.
42
+ 9. When the user asks you to CREATE a new file, write it immediately do NOT read first.
43
+ 10. When the user asks you to MODIFY or FIX an existing file, read it first, then write the complete updated version ONCE.
44
+ 11. When fixing multiple files, use read-files to read ALL of them first, then write each one ONCE sequentially — never rewrite a file already written this session.
45
+ 12. If a read-folder or read-file returns not found, accept it and move on do NOT retry the same path.
46
+ 13. Every shell command runs from the repo root cd has no persistent effect. Use full paths or combine with && e.g. cd myapp && bun run index.ts
47
+ 14. write-file paths are relative to the repo root use full relative paths e.g. myapp/src/index.tsx not src/index.tsx
48
+ 15. When explaining how to use a tool in text, use [tag] bracket notation NEVER emit a real XML tool tag as part of an explanation.
49
+ 16. NEVER use markdown formatting in plain text responses no bold, no headings, no bullet points. Only use fenced code blocks when showing actual code.
50
+ 17. When scaffolding multiple files, emit ONE write-file tag per response and wait for the result before writing the next file.
51
+
52
+ ## ADDON FORMAT
53
+
54
+ All addons use defineTool from @ridit/lens-sdk. The ONLY correct format is:
55
+
56
+ \`\`\`js
57
+ const { defineTool } = require("@ridit/lens-sdk");
58
+ const { execSync } = require("child_process");
59
+
60
+ defineTool({
61
+ name: "tool-name",
62
+ description: "what it does",
63
+ safe: false,
64
+ permissionLabel: "label shown to user",
65
+ systemPromptEntry: () => "<tool-name>{}</tool-name> — description",
66
+ parseInput: (body) => JSON.parse(body.trim() || "{}"),
67
+ summariseInput: (input) => "summary",
68
+ execute: async (input, ctx) => {
69
+ // ctx.repoPath is the current repo path
70
+ // use execSync from child_process for shell commands, NOT ctx.tools.shell
71
+ return { kind: "text", value: "result" };
72
+ },
73
+ });
74
+ \`\`\`
75
+
76
+ NEVER use module.exports, registerTool, ctx.tools.shell, or any other format. See addons/run-tests.js for a full working example.
77
+
78
+ ## SCAFFOLDING
79
+
80
+ When creating multiple files, emit ONE write-file per response and wait for each result:
77
81
 
78
82
  <write-file>
79
- {"path": "test/file1.txt", "content": "File 1 content"}
83
+ {"path": "myapp/package.json", "content": "..."}
80
84
  </write-file>
81
- <write-file>
82
- {"path": "test/file2.txt", "content": "File 2 content"}
83
- </write-file>
84
- <write-file>
85
- {"path": "test/file3.txt", "content": "File 3 content"}
86
- </write-file>
87
-
88
- The system processes each tag sequentially and automatically continues to the next one.
89
- Do NOT wait for a user message between files — emit all tags at once.
90
-
91
- ## WHEN TO READ BEFORE WRITING
92
-
93
- Only read a file before writing if ALL of these are true:
94
- - The file already exists AND has content you need to preserve
95
- - The user explicitly asked you to modify, edit, or update it (not create it)
96
- - You do not already have the file content in this conversation
97
85
 
98
- Never read before writing when:
99
- - The user asked you to create, write, or add a new file
100
- - The file is empty, missing, or a stub
101
- - You already read it earlier in this conversation
102
-
103
- When modifying an existing file:
104
- 1. Use read-file on the exact file first
105
- 2. Preserve ALL existing content — do not remove anything that was not part of the request
106
- 3. Your write-file must contain EVERYTHING the original had, PLUS your additions
107
- 4. NEVER produce a file shorter than the original unless explicitly asked to delete things
86
+ Wait for result, then emit the next file. Never chain write-file tags when content is complex.
108
87
 
109
88
  ## CODEBASE
110
89
 
@@ -115,57 +94,57 @@ ${memorySummary}`;
115
94
 
116
95
  const BUILTIN_TOOLS_SECTION = `## TOOLS
117
96
 
118
- 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.
97
+ You have exactly fourteen tools. Use ONLY the XML tags shown below.
119
98
 
120
99
  ### 1. fetch — load a URL
121
100
  <fetch>https://example.com</fetch>
122
101
 
123
- ### 2. shell — run a terminal command
102
+ ### 2. shell — run a terminal command (NOT for filesystem inspection)
124
103
  <shell>node -v</shell>
125
104
 
126
- ### 3. read-file — read a file from the repo
105
+ ### 3. read-file — read a single file from the repo
127
106
  <read-file>src/foo.ts</read-file>
128
107
 
129
- ### 4. read-folderlist contents of a folder (files + subfolders, one level deep)
108
+ ### 4. read-filesread multiple files at once
109
+ <read-files>
110
+ ["src/foo.ts", "src/bar.ts"]
111
+ </read-files>
112
+
113
+ ### 5. read-folder — list contents of a folder (one level deep)
130
114
  <read-folder>src/components</read-folder>
131
115
 
132
- ### 5. grep — search for a pattern across files in the repo (cross-platform, no shell needed)
116
+ ### 6. grep — search for a pattern across files
133
117
  <grep>
134
118
  {"pattern": "ChatRunner", "glob": "src/**/*.tsx"}
135
119
  </grep>
136
120
 
137
- ### 6. write-file — create or overwrite a file
121
+ ### 7. write-file — create or overwrite a file (COMPLETE content only)
138
122
  <write-file>
139
123
  {"path": "data/output.csv", "content": "col1,col2\\nval1,val2"}
140
124
  </write-file>
141
125
 
142
- ### 7. delete-file — permanently delete a single file
126
+ ### 8. delete-file — permanently delete a single file
143
127
  <delete-file>src/old-component.tsx</delete-file>
144
128
 
145
- ### 8. delete-folder — permanently delete a folder and all its contents
129
+ ### 9. delete-folder — permanently delete a folder and all its contents
146
130
  <delete-folder>src/legacy</delete-folder>
147
131
 
148
- ### 9. open-url — open a URL in the user's default browser
132
+ ### 10. open-url — open a URL in the user's default browser
149
133
  <open-url>https://github.com/owner/repo</open-url>
150
134
 
151
- ### 10. generate-pdf — generate a PDF file from markdown-style content
135
+ ### 11. generate-pdf — generate a PDF from markdown-style content
152
136
  <generate-pdf>
153
- {"path": "output/report.pdf", "content": "# Title\\n\\nSome body text.\\n\\n## Section\\n\\nMore content."}
137
+ {"path": "output/report.pdf", "content": "# Title\\n\\nBody text."}
154
138
  </generate-pdf>
155
139
 
156
- ### 11. search — search the internet for anything you are unsure about
157
- <search>how to use React useEffect cleanup function</search>
140
+ ### 12. search — search the internet
141
+ <search>how to use React useEffect cleanup</search>
158
142
 
159
- ### 12. clone — clone a GitHub repo so you can explore and discuss it
143
+ ### 13. clone — clone a GitHub repo
160
144
  <clone>https://github.com/owner/repo</clone>
161
145
 
162
- ### 13. changes — propose code edits (shown as a diff for user approval)
146
+ ### 14. changes — propose code edits shown as a diff for user approval
163
147
  <changes>
164
148
  {"summary": "what changed and why", "patches": [{"path": "src/foo.ts", "content": "COMPLETE file content", "isNew": false}]}
165
149
  </changes>
166
-
167
- ### 14. read-files — read multiple files from the repo at once
168
- <read-files>
169
- ["src/foo.ts", "src/bar.ts"]
170
- </read-files>
171
150
  `;
@@ -17,6 +17,8 @@ import {
17
17
  convertImageTool,
18
18
  } from "../../tools";
19
19
 
20
+ const cleanBody = (body: string) => body.trim().replace(/\\/g, "/");
21
+
20
22
  export const fetchTool: Tool<string> = {
21
23
  name: "fetch",
22
24
  description: "load a URL",
@@ -61,7 +63,7 @@ export const readFileTool: Tool<string> = {
61
63
  permissionLabel: "read",
62
64
  systemPromptEntry: (i) =>
63
65
  `### ${i}. read-file — read a file from the repo\n<read-file>src/foo.ts</read-file>`,
64
- parseInput: (body) => body || null,
66
+ parseInput: (body) => cleanBody(body) || null,
65
67
  summariseInput: (p) => p,
66
68
  execute: (filePath, ctx) => ({
67
69
  kind: "text",
@@ -76,7 +78,7 @@ export const readFolderTool: Tool<string> = {
76
78
  permissionLabel: "folder",
77
79
  systemPromptEntry: (i) =>
78
80
  `### ${i}. read-folder — list contents of a folder (files + subfolders, one level deep)\n<read-folder>src/components</read-folder>`,
79
- parseInput: (body) => body || null,
81
+ parseInput: (body) => cleanBody(body) || null,
80
82
  summariseInput: (p) => p,
81
83
  execute: (folderPath, ctx) => ({
82
84
  kind: "text",
@@ -98,7 +100,10 @@ export const grepTool: Tool<GrepInput> = {
98
100
  `### ${i}. grep — search for a pattern across files in the repo (cross-platform, no shell needed)\n<grep>\n{"pattern": "ChatRunner", "glob": "src/**/*.tsx"}\n</grep>`,
99
101
  parseInput: (body) => {
100
102
  try {
101
- const parsed = JSON.parse(body) as { pattern: string; glob?: string };
103
+ const parsed = JSON.parse(cleanBody(body)) as {
104
+ pattern: string;
105
+ glob?: string;
106
+ };
102
107
  return { pattern: parsed.pattern, glob: parsed.glob ?? "**/*" };
103
108
  } catch {
104
109
  return { pattern: body, glob: "**/*" };
@@ -127,7 +132,7 @@ export const writeFileTool: Tool<WriteFileInput> = {
127
132
  try {
128
133
  const parsed = JSON.parse(body) as { path: string; content: string };
129
134
  if (!parsed.path) return null;
130
- return parsed;
135
+ return { ...parsed, path: parsed.path.replace(/\\/g, "/") };
131
136
  } catch {
132
137
  return null;
133
138
  }
@@ -146,7 +151,7 @@ export const deleteFileTool: Tool<string> = {
146
151
  permissionLabel: "delete",
147
152
  systemPromptEntry: (i) =>
148
153
  `### ${i}. delete-file — permanently delete a single file\n<delete-file>src/old-component.tsx</delete-file>`,
149
- parseInput: (body) => body || null,
154
+ parseInput: (body) => cleanBody(body) || null,
150
155
  summariseInput: (p) => p,
151
156
  execute: (filePath, ctx) => ({
152
157
  kind: "text",
@@ -161,7 +166,7 @@ export const deleteFolderTool: Tool<string> = {
161
166
  permissionLabel: "delete folder",
162
167
  systemPromptEntry: (i) =>
163
168
  `### ${i}. delete-folder — permanently delete a folder and all its contents\n<delete-folder>src/legacy</delete-folder>`,
164
- parseInput: (body) => body || null,
169
+ parseInput: (body) => cleanBody(body) || null,
165
170
  summariseInput: (p) => p,
166
171
  execute: (folderPath, ctx) => ({
167
172
  kind: "text",
@@ -195,7 +200,7 @@ export const generatePdfTool: Tool<GeneratePdfInput> = {
195
200
  `### ${i}. generate-pdf — generate a PDF file from markdown-style content\n<generate-pdf>\n{"path": "output/report.pdf", "content": "# Title\\n\\nSome body text."}\n</generate-pdf>`,
196
201
  parseInput: (body) => {
197
202
  try {
198
- const parsed = JSON.parse(body) as {
203
+ const parsed = JSON.parse(cleanBody(body)) as {
199
204
  path?: string;
200
205
  filePath?: string;
201
206
  content?: string;
@@ -266,7 +271,7 @@ export const changesTool: Tool<ChangesInput> = {
266
271
  `### ${i}. changes — propose code edits (shown as a diff for user approval)\n<changes>\n{"summary": "what changed and why", "patches": [{"path": "src/foo.ts", "content": "COMPLETE file content", "isNew": false}]}\n</changes>`,
267
272
  parseInput: (body) => {
268
273
  try {
269
- return JSON.parse(body) as ChangesInput;
274
+ return JSON.parse(cleanBody(body)) as ChangesInput;
270
275
  } catch {
271
276
  return null;
272
277
  }
@@ -291,8 +296,7 @@ export const readFilesTool: Tool<ReadFilesInput> = {
291
296
  `### ${i}. read-files — read multiple files from the repo at once\n<read-files>\n["src/foo.ts", "src/bar.ts"]\n</read-files>`,
292
297
  parseInput: (body) => {
293
298
  try {
294
- const cleaned = body.trim().replace(/\\/g, "/");
295
- const parsed = JSON.parse(cleaned) as string[];
299
+ const parsed = JSON.parse(cleanBody(body)) as string[];
296
300
  if (!Array.isArray(parsed) || parsed.length === 0) return null;
297
301
  return { paths: parsed };
298
302
  } catch {
package/LENS.md DELETED
@@ -1,32 +0,0 @@
1
- # Lens Analysis
2
- > Generated: 2026-03-21T11:15:38.543Z
3
-
4
- ## Overview
5
- Lens is a CLI tool built with React components rendered in the terminal via Ink, designed to help developers understand, navigate, and interact with codebases using AI-powered analysis. The tool provides repository analysis, AI-powered insights, interactive chat, code review, timeline exploration, and task automation capabilities. Key components include ChatRunner for interactive conversations, RepoAnalysis for repository examination, and various command handlers like ReviewCommand and TaskCommand. The project uses Bun as a build tool and runtime, with Commander.js for CLI structure and TypeScript throughout for type safety.
6
-
7
- ## Important Folders
8
- - src/components: Contains core UI components like ChatRunner (handles interactive chat), RepoAnalysis (analyzes repositories), DiffViewer (shows code differences), and ProviderPicker (selects AI providers). These components use Ink for terminal rendering and provide the main user interface.
9
- - src/commands: Implements all CLI commands including repo (analyze remote repositories), review (local codebase analysis), task (apply natural language changes), chat (interactive conversation), timeline (commit history exploration), and commit (smart commit message generation).
10
- - src/utils: Contains utility functions for AI integration (ai.ts), chat processing (chat.ts), configuration management (config.ts), file operations (files.ts), git operations (git.ts), memory management (memory.ts), and thinking animations (thinking.tsx).
11
- - src/tools: Provides various tool implementations including files (file operations), shell (command execution), web (URL fetching), pdf (PDF generation), and git (version control operations). These tools are used throughout the chat functionality.
12
-
13
- ## Missing Configs
14
- - ESLint configuration: The project uses TypeScript but lacks an ESLint config file (.eslintrc.js or eslint.config.js) for code quality enforcement
15
- - Testing setup: No test framework configuration (Jest/Vitest) or test files found, which is important for a developer tool of this complexity
16
- - GitHub Actions CI/CD: Missing workflow files for automated testing and deployment, which would help maintain quality for a CLI tool
17
-
18
- ## Security Issues
19
- - In src/utils/chat.ts: The parseResponse function uses regex-based XML parsing which could be vulnerable to injection attacks if malformed responses are processed
20
- - In src/utils/repo.ts: The cloneRepo function uses exec() with user-provided URLs without proper sanitization, potentially allowing command injection
21
- - In multiple files: API keys and sensitive information are handled without encryption in config files stored in ~/.lens directory
22
-
23
- ## Suggestions
24
- - In src/utils/chat.ts: Replace regex-based XML parsing with a proper XML parser library to prevent injection vulnerabilities and improve reliability
25
- - In src/utils/repo.ts: Use execFile with proper argument sanitization instead of exec() for git operations to prevent command injection attacks
26
- - In src/utils/config.ts: Implement encryption for storing API keys in the config file rather than plaintext storage
27
- - In src/components/chat/ChatRunner.tsx: Add pagination or virtualization for long chat histories to improve performance with many messages
28
- - In package.json: Add linting and testing scripts to establish better code quality practices for the project
29
-
30
- <!--lens-json
31
- {"overview":"Lens is a CLI tool built with React components rendered in the terminal via Ink, designed to help developers understand, navigate, and interact with codebases using AI-powered analysis. The tool provides repository analysis, AI-powered insights, interactive chat, code review, timeline exploration, and task automation capabilities. Key components include ChatRunner for interactive conversations, RepoAnalysis for repository examination, and various command handlers like ReviewCommand and TaskCommand. The project uses Bun as a build tool and runtime, with Commander.js for CLI structure and TypeScript throughout for type safety.","importantFolders":["src/components: Contains core UI components like ChatRunner (handles interactive chat), RepoAnalysis (analyzes repositories), DiffViewer (shows code differences), and ProviderPicker (selects AI providers). These components use Ink for terminal rendering and provide the main user interface.","src/commands: Implements all CLI commands including repo (analyze remote repositories), review (local codebase analysis), task (apply natural language changes), chat (interactive conversation), timeline (commit history exploration), and commit (smart commit message generation).","src/utils: Contains utility functions for AI integration (ai.ts), chat processing (chat.ts), configuration management (config.ts), file operations (files.ts), git operations (git.ts), memory management (memory.ts), and thinking animations (thinking.tsx).","src/tools: Provides various tool implementations including files (file operations), shell (command execution), web (URL fetching), pdf (PDF generation), and git (version control operations). These tools are used throughout the chat functionality."],"missingConfigs":["ESLint configuration: The project uses TypeScript but lacks an ESLint config file (.eslintrc.js or eslint.config.js) for code quality enforcement","Testing setup: No test framework configuration (Jest/Vitest) or test files found, which is important for a developer tool of this complexity","GitHub Actions CI/CD: Missing workflow files for automated testing and deployment, which would help maintain quality for a CLI tool"],"securityIssues":["In src/utils/chat.ts: The parseResponse function uses regex-based XML parsing which could be vulnerable to injection attacks if malformed responses are processed","In src/utils/repo.ts: The cloneRepo function uses exec() with user-provided URLs without proper sanitization, potentially allowing command injection","In multiple files: API keys and sensitive information are handled without encryption in config files stored in ~/.lens directory"],"suggestions":["In src/utils/chat.ts: Replace regex-based XML parsing with a proper XML parser library to prevent injection vulnerabilities and improve reliability","In src/utils/repo.ts: Use execFile with proper argument sanitization instead of exec() for git operations to prevent command injection attacks","In src/utils/config.ts: Implement encryption for storing API keys in the config file rather than plaintext storage","In src/components/chat/ChatRunner.tsx: Add pagination or virtualization for long chat histories to improve performance with many messages","In package.json: Add linting and testing scripts to establish better code quality practices for the project"],"generatedAt":"2026-03-21T11:15:38.543Z"}
32
- lens-json-->