@ridit/lens 0.2.2 → 0.2.4
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/LENS.md +62 -19
- package/dist/index.mjs +67 -44
- package/package.json +2 -1
- package/src/index.tsx +3 -0
- package/src/utils/addons/loadAddons.ts +29 -0
- package/src/utils/chat.ts +1 -1
- package/src/utils/memory.ts +7 -12
- package/src/utils/tools/builtins.ts +1 -1
- package/src/utils/tools/registry.ts +3 -59
- package/hello.py +0 -51
package/LENS.md
CHANGED
|
@@ -1,25 +1,68 @@
|
|
|
1
|
-
# Lens
|
|
2
|
-
> Generated: 2026-03-11T17:49:46.564Z
|
|
1
|
+
# Lens - Codebase Intelligence Tool
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
This project is a CLI tool called 'lens' that analyzes a repository and provides insights. It uses React, Ink, and various other libraries to create a text-based interface. The tool has several commands, including 'repo' for analyzing a remote repository, 'init' for initializing the tool, and 'review' for reviewing a local codebase. The project is designed for developers who want to quickly understand a repository's structure and content.
|
|
3
|
+
Lens is a CLI tool that helps developers understand, navigate, and interact with codebases using AI-powered analysis.
|
|
6
4
|
|
|
7
|
-
##
|
|
8
|
-
- src/components: contains various components, including FileReviewer, FileViewer, and ProviderPicker. Each component is used to display specific information about the repository.
|
|
9
|
-
- src/commands: contains the implementation of the 'repo', 'init', and 'review' commands. Each command has its own file and exports a function that handles the command's logic.
|
|
10
|
-
- src/utils: contains utility functions for tasks such as cloning a repository, reading files, and analyzing code. These functions are used throughout the project to perform specific tasks.
|
|
5
|
+
## Core Features
|
|
11
6
|
|
|
12
|
-
|
|
13
|
-
-
|
|
7
|
+
- **Repository Analysis**: Analyze both local and remote repositories
|
|
8
|
+
- **AI-Powered Insights**: Uses LLMs to understand code structure and content
|
|
9
|
+
- **Interactive Chat**: Converse with your codebase using natural language
|
|
10
|
+
- **Code Review**: Automated code reviews with specific suggestions
|
|
11
|
+
- **Timeline Exploration**: Explore commit history and code evolution
|
|
12
|
+
- **Task Automation**: Apply natural language changes to codebases
|
|
14
13
|
|
|
15
|
-
##
|
|
16
|
-
- None detected
|
|
14
|
+
## Supported AI Providers
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
16
|
+
- Anthropic
|
|
17
|
+
- Gemini (Google AI)
|
|
18
|
+
- OpenAI
|
|
19
|
+
- Ollama (local models)
|
|
20
|
+
- Custom endpoints
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
## Technical Architecture
|
|
23
|
+
|
|
24
|
+
- Built with React components rendered in terminal via Ink
|
|
25
|
+
- TypeScript throughout for type safety
|
|
26
|
+
- Bun as build tool and runtime
|
|
27
|
+
- Commander.js for CLI structure
|
|
28
|
+
- Modular command system with separate handlers
|
|
29
|
+
|
|
30
|
+
## Commands
|
|
31
|
+
|
|
32
|
+
- `lens repo <url>` - Analyze a remote repository
|
|
33
|
+
- `lens review [path]` - Review a local codebase
|
|
34
|
+
- `lens task <text>` - Apply natural language changes
|
|
35
|
+
- `lens chat` - Interactive chat with codebase
|
|
36
|
+
- `lens timeline` - Explore commit history
|
|
37
|
+
- `lens provider` - Configure AI providers
|
|
38
|
+
|
|
39
|
+
## Key Components
|
|
40
|
+
|
|
41
|
+
- **Smart File Selection**: AI determines which files are most important
|
|
42
|
+
- **Structured Analysis**: Provides overviews, folder insights, and suggestions
|
|
43
|
+
- **Security Scanning**: Identifies potential security issues
|
|
44
|
+
- **Multi-Model Support**: Flexible AI backend configuration
|
|
45
|
+
|
|
46
|
+
## Installation
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm install -g @ridit/lens
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Usage
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Analyze a GitHub repository
|
|
56
|
+
lens repo https://github.com/user/repo
|
|
57
|
+
|
|
58
|
+
# Review local codebase
|
|
59
|
+
lens review .
|
|
60
|
+
|
|
61
|
+
# Chat with your code
|
|
62
|
+
lens chat --path .
|
|
63
|
+
|
|
64
|
+
# Make changes with natural language
|
|
65
|
+
lens task "Add TypeScript types to this component" --path .
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Lens helps developers quickly understand complex codebases through AI-assisted analysis and natural language interaction.
|
package/dist/index.mjs
CHANGED
|
@@ -35841,6 +35841,43 @@ var require_jsx_dev_runtime = __commonJS((exports, module) => {
|
|
|
35841
35841
|
}
|
|
35842
35842
|
});
|
|
35843
35843
|
|
|
35844
|
+
// src/utils/tools/registry.ts
|
|
35845
|
+
class ToolRegistry {
|
|
35846
|
+
tools = new Map;
|
|
35847
|
+
register(tool) {
|
|
35848
|
+
if (this.tools.has(tool.name)) {
|
|
35849
|
+
console.warn(`[ToolRegistry] Overwriting existing tool: "${tool.name}"`);
|
|
35850
|
+
}
|
|
35851
|
+
this.tools.set(tool.name, tool);
|
|
35852
|
+
}
|
|
35853
|
+
unregister(name) {
|
|
35854
|
+
this.tools.delete(name);
|
|
35855
|
+
}
|
|
35856
|
+
get(name) {
|
|
35857
|
+
return this.tools.get(name);
|
|
35858
|
+
}
|
|
35859
|
+
all() {
|
|
35860
|
+
return Array.from(this.tools.values());
|
|
35861
|
+
}
|
|
35862
|
+
names() {
|
|
35863
|
+
return Array.from(this.tools.keys());
|
|
35864
|
+
}
|
|
35865
|
+
buildSystemPromptSection() {
|
|
35866
|
+
const lines = [`## TOOLS
|
|
35867
|
+
`];
|
|
35868
|
+
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.
|
|
35869
|
+
`);
|
|
35870
|
+
let i = 1;
|
|
35871
|
+
for (const tool of this.tools.values()) {
|
|
35872
|
+
lines.push(tool.systemPromptEntry(i++));
|
|
35873
|
+
}
|
|
35874
|
+
return lines.join(`
|
|
35875
|
+
`);
|
|
35876
|
+
}
|
|
35877
|
+
}
|
|
35878
|
+
var registry = new ToolRegistry;
|
|
35879
|
+
globalThis.__lens_registry = registry;
|
|
35880
|
+
|
|
35844
35881
|
// node_modules/ink/build/render.js
|
|
35845
35882
|
import { Stream } from "node:stream";
|
|
35846
35883
|
import process13 from "node:process";
|
|
@@ -49805,42 +49842,6 @@ Got it — I'll always use bun for this project.`
|
|
|
49805
49842
|
Done — removed that memory.`
|
|
49806
49843
|
}
|
|
49807
49844
|
];
|
|
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
|
-
|
|
49844
49845
|
// src/utils/chat.ts
|
|
49845
49846
|
function parseResponse(text) {
|
|
49846
49847
|
const scanText = text.replace(/```[\s\S]*?```/g, (m) => " ".repeat(m.length));
|
|
@@ -52368,8 +52369,8 @@ function appendMemory(entry) {
|
|
|
52368
52369
|
}
|
|
52369
52370
|
function buildMemorySummary(repoPath) {
|
|
52370
52371
|
const m = loadMemoryFile();
|
|
52371
|
-
const relevant = m.entries.
|
|
52372
|
-
const memories = m.memories
|
|
52372
|
+
const relevant = m.entries.slice(-50);
|
|
52373
|
+
const memories = m.memories;
|
|
52373
52374
|
const parts = [];
|
|
52374
52375
|
if (memories.length > 0) {
|
|
52375
52376
|
parts.push(`## MEMORIES ABOUT THIS REPO
|
|
@@ -52395,8 +52396,8 @@ ${lines.join(`
|
|
|
52395
52396
|
}
|
|
52396
52397
|
function clearRepoMemory(repoPath) {
|
|
52397
52398
|
const m = loadMemoryFile();
|
|
52398
|
-
m.entries = m.entries
|
|
52399
|
-
m.memories = m.memories
|
|
52399
|
+
m.entries = m.entries = [];
|
|
52400
|
+
m.memories = m.memories = [];
|
|
52400
52401
|
saveMemoryFile(m);
|
|
52401
52402
|
}
|
|
52402
52403
|
function generateId() {
|
|
@@ -52407,8 +52408,7 @@ function addMemory(content, repoPath) {
|
|
|
52407
52408
|
const memory = {
|
|
52408
52409
|
id: generateId(),
|
|
52409
52410
|
content,
|
|
52410
|
-
timestamp: new Date().toISOString()
|
|
52411
|
-
repoPath
|
|
52411
|
+
timestamp: new Date().toISOString()
|
|
52412
52412
|
};
|
|
52413
52413
|
m.memories.push(memory);
|
|
52414
52414
|
saveMemoryFile(m);
|
|
@@ -52417,14 +52417,14 @@ function addMemory(content, repoPath) {
|
|
|
52417
52417
|
function deleteMemory(id, repoPath) {
|
|
52418
52418
|
const m = loadMemoryFile();
|
|
52419
52419
|
const before2 = m.memories.length;
|
|
52420
|
-
m.memories = m.memories.filter((mem) => !(mem.id === id
|
|
52420
|
+
m.memories = m.memories.filter((mem) => !(mem.id === id));
|
|
52421
52421
|
if (m.memories.length === before2)
|
|
52422
52422
|
return false;
|
|
52423
52423
|
saveMemoryFile(m);
|
|
52424
52424
|
return true;
|
|
52425
52425
|
}
|
|
52426
52426
|
function listMemories(repoPath) {
|
|
52427
|
-
return loadMemoryFile().memories
|
|
52427
|
+
return loadMemoryFile().memories;
|
|
52428
52428
|
}
|
|
52429
52429
|
|
|
52430
52430
|
// src/components/chat/ChatRunner.tsx
|
|
@@ -53678,9 +53678,32 @@ function registerBuiltins() {
|
|
|
53678
53678
|
registry.register(changesTool);
|
|
53679
53679
|
}
|
|
53680
53680
|
|
|
53681
|
+
// src/utils/addons/loadAddons.ts
|
|
53682
|
+
import path21 from "path";
|
|
53683
|
+
import os10 from "os";
|
|
53684
|
+
import { existsSync as existsSync16, readdirSync as readdirSync5 } from "fs";
|
|
53685
|
+
var ADDONS_DIR = path21.join(os10.homedir(), ".lens", "addons");
|
|
53686
|
+
async function loadAddons() {
|
|
53687
|
+
if (!existsSync16(ADDONS_DIR)) {
|
|
53688
|
+
return;
|
|
53689
|
+
}
|
|
53690
|
+
const files = readdirSync5(ADDONS_DIR).filter((f) => f.endsWith(".js") && !f.startsWith("_"));
|
|
53691
|
+
for (const file of files) {
|
|
53692
|
+
const fullPath = path21.join(ADDONS_DIR, file);
|
|
53693
|
+
try {
|
|
53694
|
+
await import(fullPath);
|
|
53695
|
+
console.log(`[addons] loaded: ${file}
|
|
53696
|
+
`);
|
|
53697
|
+
} catch (err) {
|
|
53698
|
+
console.error(`[addons] failed to load ${file}:`, err instanceof Error ? err.message : String(err));
|
|
53699
|
+
}
|
|
53700
|
+
}
|
|
53701
|
+
}
|
|
53702
|
+
|
|
53681
53703
|
// src/index.tsx
|
|
53682
53704
|
var jsx_dev_runtime25 = __toESM(require_jsx_dev_runtime(), 1);
|
|
53683
53705
|
registerBuiltins();
|
|
53706
|
+
await loadAddons();
|
|
53684
53707
|
var program2 = new Command;
|
|
53685
53708
|
program2.command("repo <url>").description("Analyze a remote repository").action((url) => {
|
|
53686
53709
|
render_default(/* @__PURE__ */ jsx_dev_runtime25.jsxDEV(RepoCommand, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ridit/lens",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "Know Your Codebase.",
|
|
5
5
|
"author": "Ridit Jangra <riditjangra09@gmail.com> (https://ridit.space)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"prepublishOnly": "npm run build"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
+
"@ridit/lens-sdk": "0.1.4",
|
|
21
22
|
"commander": "^14.0.3",
|
|
22
23
|
"figures": "^6.1.0",
|
|
23
24
|
"ink": "^6.8.0",
|
package/src/index.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import "./utils/tools/registry";
|
|
2
3
|
import { render } from "ink";
|
|
3
4
|
import { Command } from "commander";
|
|
4
5
|
import { RepoCommand } from "./commands/repo";
|
|
@@ -8,8 +9,10 @@ import { TaskCommand } from "./commands/task";
|
|
|
8
9
|
import { ChatCommand } from "./commands/chat";
|
|
9
10
|
import { TimelineCommand } from "./commands/timeline";
|
|
10
11
|
import { registerBuiltins } from "./utils/tools/builtins";
|
|
12
|
+
import { loadAddons } from "./utils/addons/loadAddons";
|
|
11
13
|
|
|
12
14
|
registerBuiltins();
|
|
15
|
+
await loadAddons();
|
|
13
16
|
|
|
14
17
|
const program = new Command();
|
|
15
18
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import os from "os";
|
|
3
|
+
import { existsSync, readdirSync } from "fs";
|
|
4
|
+
|
|
5
|
+
const ADDONS_DIR = path.join(os.homedir(), ".lens", "addons");
|
|
6
|
+
|
|
7
|
+
export async function loadAddons(): Promise<void> {
|
|
8
|
+
if (!existsSync(ADDONS_DIR)) {
|
|
9
|
+
// Silently skip — no addons directory yet
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const files = readdirSync(ADDONS_DIR).filter(
|
|
14
|
+
(f) => f.endsWith(".js") && !f.startsWith("_"),
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
for (const file of files) {
|
|
18
|
+
const fullPath = path.join(ADDONS_DIR, file);
|
|
19
|
+
try {
|
|
20
|
+
await import(fullPath);
|
|
21
|
+
console.log(`[addons] loaded: ${file}\n`);
|
|
22
|
+
} catch (err) {
|
|
23
|
+
console.error(
|
|
24
|
+
`[addons] failed to load ${file}:`,
|
|
25
|
+
err instanceof Error ? err.message : String(err),
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
package/src/utils/chat.ts
CHANGED
|
@@ -22,7 +22,7 @@ export { buildSystemPrompt, FEW_SHOT_MESSAGES } from "../prompts";
|
|
|
22
22
|
import type { Message } from "../types/chat";
|
|
23
23
|
import type { Provider } from "../types/config";
|
|
24
24
|
import { FEW_SHOT_MESSAGES } from "../prompts";
|
|
25
|
-
import { registry } from "
|
|
25
|
+
import { registry } from "../utils/tools/registry";
|
|
26
26
|
import type { FilePatch } from "../components/repo/DiffViewer";
|
|
27
27
|
|
|
28
28
|
export type ParsedResponse =
|
package/src/utils/memory.ts
CHANGED
|
@@ -15,14 +15,12 @@ export type MemoryEntry = {
|
|
|
15
15
|
detail: string;
|
|
16
16
|
summary: string;
|
|
17
17
|
timestamp: string;
|
|
18
|
-
repoPath: string;
|
|
19
18
|
};
|
|
20
19
|
|
|
21
20
|
export type Memory = {
|
|
22
21
|
id: string;
|
|
23
22
|
content: string;
|
|
24
23
|
timestamp: string;
|
|
25
|
-
repoPath: string;
|
|
26
24
|
};
|
|
27
25
|
|
|
28
26
|
export type MemoryFile = {
|
|
@@ -64,9 +62,9 @@ export function appendMemory(entry: Omit<MemoryEntry, "timestamp">): void {
|
|
|
64
62
|
|
|
65
63
|
export function buildMemorySummary(repoPath: string): string {
|
|
66
64
|
const m = loadMemoryFile();
|
|
67
|
-
const relevant = m.entries.
|
|
65
|
+
const relevant = m.entries.slice(-50);
|
|
68
66
|
|
|
69
|
-
const memories = m.memories
|
|
67
|
+
const memories = m.memories;
|
|
70
68
|
|
|
71
69
|
const parts: string[] = [];
|
|
72
70
|
|
|
@@ -92,13 +90,13 @@ export function buildMemorySummary(repoPath: string): string {
|
|
|
92
90
|
}
|
|
93
91
|
|
|
94
92
|
export function getRepoMemory(repoPath: string): MemoryEntry[] {
|
|
95
|
-
return loadMemoryFile().entries
|
|
93
|
+
return loadMemoryFile().entries;
|
|
96
94
|
}
|
|
97
95
|
|
|
98
96
|
export function clearRepoMemory(repoPath: string): void {
|
|
99
97
|
const m = loadMemoryFile();
|
|
100
|
-
m.entries = m.entries
|
|
101
|
-
m.memories = m.memories
|
|
98
|
+
m.entries = m.entries = [];
|
|
99
|
+
m.memories = m.memories = [];
|
|
102
100
|
saveMemoryFile(m);
|
|
103
101
|
}
|
|
104
102
|
|
|
@@ -114,7 +112,6 @@ export function addMemory(content: string, repoPath: string): Memory {
|
|
|
114
112
|
id: generateId(),
|
|
115
113
|
content,
|
|
116
114
|
timestamp: new Date().toISOString(),
|
|
117
|
-
repoPath,
|
|
118
115
|
};
|
|
119
116
|
m.memories.push(memory);
|
|
120
117
|
saveMemoryFile(m);
|
|
@@ -124,14 +121,12 @@ export function addMemory(content: string, repoPath: string): Memory {
|
|
|
124
121
|
export function deleteMemory(id: string, repoPath: string): boolean {
|
|
125
122
|
const m = loadMemoryFile();
|
|
126
123
|
const before = m.memories.length;
|
|
127
|
-
m.memories = m.memories.filter(
|
|
128
|
-
(mem) => !(mem.id === id && mem.repoPath === repoPath),
|
|
129
|
-
);
|
|
124
|
+
m.memories = m.memories.filter((mem) => !(mem.id === id));
|
|
130
125
|
if (m.memories.length === before) return false;
|
|
131
126
|
saveMemoryFile(m);
|
|
132
127
|
return true;
|
|
133
128
|
}
|
|
134
129
|
|
|
135
130
|
export function listMemories(repoPath: string): Memory[] {
|
|
136
|
-
return loadMemoryFile().memories
|
|
131
|
+
return loadMemoryFile().memories;
|
|
137
132
|
}
|
|
@@ -10,65 +10,7 @@
|
|
|
10
10
|
// import { registry } from "lens/tools/registry";
|
|
11
11
|
// registry.register({ name: "my-tool", ... });
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
repoPath: string;
|
|
15
|
-
/** All messages in the current conversation so far */
|
|
16
|
-
messages: unknown[];
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export type ToolResult =
|
|
20
|
-
| { kind: "text"; value: string }
|
|
21
|
-
| { kind: "error"; value: string };
|
|
22
|
-
|
|
23
|
-
export interface Tool<TInput = string> {
|
|
24
|
-
/**
|
|
25
|
-
* Tag name used in XML: <name>...</name>
|
|
26
|
-
* Must be lowercase, hyphens allowed. Must be unique.
|
|
27
|
-
*/
|
|
28
|
-
name: string;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Short description shown in system prompt and /help.
|
|
32
|
-
*/
|
|
33
|
-
description: string;
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* System prompt snippet explaining how to invoke this tool.
|
|
37
|
-
* Return the full ### N. name — description block.
|
|
38
|
-
*/
|
|
39
|
-
systemPromptEntry(index: number): string;
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Parse the raw inner text of the XML tag into a typed input.
|
|
43
|
-
* Throw or return null to signal a parse failure (tool will be skipped).
|
|
44
|
-
*/
|
|
45
|
-
parseInput(body: string): TInput | null;
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Execute the tool. May be async.
|
|
49
|
-
* Return a ToolResult — the value is fed back to the model as the tool result.
|
|
50
|
-
*/
|
|
51
|
-
execute(input: TInput, ctx: ToolContext): Promise<ToolResult> | ToolResult;
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Whether this tool is safe to auto-approve (read-only, no side effects).
|
|
55
|
-
* Defaults to false.
|
|
56
|
-
*/
|
|
57
|
-
safe?: boolean;
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Optional: permission prompt label shown to the user before execution.
|
|
61
|
-
* e.g. "run", "read", "write", "delete"
|
|
62
|
-
* Defaults to the tool name.
|
|
63
|
-
*/
|
|
64
|
-
permissionLabel?: string;
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Optional: summarise the input for display in the chat history.
|
|
68
|
-
* Defaults to showing the raw input string.
|
|
69
|
-
*/
|
|
70
|
-
summariseInput?(input: TInput): string;
|
|
71
|
-
}
|
|
13
|
+
import type { Tool } from "@ridit/lens-sdk";
|
|
72
14
|
|
|
73
15
|
// ── Registry ──────────────────────────────────────────────────────────────────
|
|
74
16
|
|
|
@@ -117,3 +59,5 @@ class ToolRegistry {
|
|
|
117
59
|
}
|
|
118
60
|
|
|
119
61
|
export const registry = new ToolRegistry();
|
|
62
|
+
|
|
63
|
+
(globalThis as any).__lens_registry = registry;
|
package/hello.py
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
# hello.py
|
|
2
|
-
# A simple script that reads a text file, makes an HTTP request,
|
|
3
|
-
# and prints the result in a friendly way.
|
|
4
|
-
|
|
5
|
-
import json
|
|
6
|
-
import pathlib
|
|
7
|
-
import sys
|
|
8
|
-
from urllib.request import urlopen
|
|
9
|
-
|
|
10
|
-
def read_local_file(path: str) -> str:
|
|
11
|
-
"""Read the contents of a file and return it as a string."""
|
|
12
|
-
file_path = pathlib.Path(path)
|
|
13
|
-
if not file_path.is_file():
|
|
14
|
-
raise FileNotFoundError(f"❌ File not found: {path}")
|
|
15
|
-
return file_path.read_text(encoding="utf-8")
|
|
16
|
-
|
|
17
|
-
def fetch_json(url: str) -> dict:
|
|
18
|
-
"""Fetch JSON from a URL and decode it into a Python dict."""
|
|
19
|
-
with urlopen(url) as response:
|
|
20
|
-
if response.status != 200:
|
|
21
|
-
raise RuntimeError(f"❌ HTTP {response.status} from {url}")
|
|
22
|
-
data = response.read()
|
|
23
|
-
return json.loads(data)
|
|
24
|
-
|
|
25
|
-
def main():
|
|
26
|
-
# 1️⃣ Read a local file (optional – you can comment this out)
|
|
27
|
-
try:
|
|
28
|
-
local_content = read_local_file("example.txt")
|
|
29
|
-
print("📄 Contents of example.txt:")
|
|
30
|
-
print(local_content)
|
|
31
|
-
except FileNotFoundError:
|
|
32
|
-
print("⚠️ example.txt not found – skipping that step.")
|
|
33
|
-
|
|
34
|
-
# 2️⃣ Fetch some JSON data from a public API
|
|
35
|
-
url = "https://api.github.com/repos/python/cpython"
|
|
36
|
-
print(f"\n🌐 Fetching data from {url} …")
|
|
37
|
-
repo_info = fetch_json(url)
|
|
38
|
-
|
|
39
|
-
# 3️⃣ Extract a couple of useful fields and display them
|
|
40
|
-
name = repo_info.get("name")
|
|
41
|
-
stars = repo_info.get("stargazers_count")
|
|
42
|
-
description = repo_info.get("description")
|
|
43
|
-
print("\n🔎 Repository info:")
|
|
44
|
-
print(f"• Name: {name}")
|
|
45
|
-
print(f"• Stars: {stars:,}") # adds commas for readability
|
|
46
|
-
print(f"• Description: {description}")
|
|
47
|
-
|
|
48
|
-
return 0
|
|
49
|
-
|
|
50
|
-
if __name__ == "__main__":
|
|
51
|
-
sys.exit(main())
|