@ridit/lens 0.3.4 → 0.3.6

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/src/tools/git.ts CHANGED
@@ -27,6 +27,7 @@ function parseArgs(body: string): GitArgs | null {
27
27
  export const gitStatusTool: Tool<GitArgs> = {
28
28
  name: "git-status",
29
29
  description: "show working tree status",
30
+ tag: "git",
30
31
  safe: true,
31
32
  permissionLabel: "git status",
32
33
  systemPromptEntry: (i) =>
@@ -45,6 +46,7 @@ export const gitStatusTool: Tool<GitArgs> = {
45
46
  export const gitLogTool: Tool<GitArgs> = {
46
47
  name: "git-log",
47
48
  description: "show commit log",
49
+ tag: "git",
48
50
  safe: true,
49
51
  permissionLabel: "git log",
50
52
  systemPromptEntry: (i) =>
@@ -60,6 +62,7 @@ export const gitLogTool: Tool<GitArgs> = {
60
62
  export const gitDiffTool: Tool<GitArgs> = {
61
63
  name: "git-diff",
62
64
  description: "show changes between commits, working tree, or staged files",
65
+ tag: "git",
63
66
  safe: true,
64
67
  permissionLabel: "git diff",
65
68
  systemPromptEntry: (i) =>
@@ -75,6 +78,7 @@ export const gitDiffTool: Tool<GitArgs> = {
75
78
  export const gitShowTool: Tool<GitArgs> = {
76
79
  name: "git-show",
77
80
  description: "show a commit's details and stat",
81
+ tag: "git",
78
82
  safe: true,
79
83
  permissionLabel: "git show",
80
84
  systemPromptEntry: (i) =>
@@ -90,6 +94,7 @@ export const gitShowTool: Tool<GitArgs> = {
90
94
  export const gitBranchTool: Tool<GitArgs> = {
91
95
  name: "git-branch",
92
96
  description: "list branches",
97
+ tag: "git",
93
98
  safe: true,
94
99
  permissionLabel: "git branch",
95
100
  systemPromptEntry: (i) =>
@@ -105,6 +110,7 @@ export const gitBranchTool: Tool<GitArgs> = {
105
110
  export const gitRemoteTool: Tool<GitArgs> = {
106
111
  name: "git-remote",
107
112
  description: "list or inspect remotes",
113
+ tag: "git",
108
114
  safe: true,
109
115
  permissionLabel: "git remote",
110
116
  systemPromptEntry: (i) =>
@@ -120,6 +126,7 @@ export const gitRemoteTool: Tool<GitArgs> = {
120
126
  export const gitTagTool: Tool<GitArgs> = {
121
127
  name: "git-tag",
122
128
  description: "list tags",
129
+ tag: "git",
123
130
  safe: true,
124
131
  permissionLabel: "git tag",
125
132
  systemPromptEntry: (i) =>
@@ -135,6 +142,7 @@ export const gitTagTool: Tool<GitArgs> = {
135
142
  export const gitBlameTool: Tool<GitArgs> = {
136
143
  name: "git-blame",
137
144
  description: "show who last modified each line of a file",
145
+ tag: "git",
138
146
  safe: true,
139
147
  permissionLabel: "git blame",
140
148
  systemPromptEntry: (i) =>
@@ -168,6 +176,7 @@ export const gitBlameTool: Tool<GitArgs> = {
168
176
  export const gitStashListTool: Tool<GitArgs> = {
169
177
  name: "git-stash-list",
170
178
  description: "list stashed changes",
179
+ tag: "git",
171
180
  safe: true,
172
181
  permissionLabel: "git stash list",
173
182
  systemPromptEntry: (i) =>
@@ -183,6 +192,7 @@ export const gitStashListTool: Tool<GitArgs> = {
183
192
  export const gitAddTool: Tool<GitArgs> = {
184
193
  name: "git-add",
185
194
  description: "stage files for commit",
195
+ tag: "git",
186
196
  safe: false,
187
197
  permissionLabel: "git add",
188
198
  systemPromptEntry: (i) =>
@@ -202,6 +212,7 @@ export const gitAddTool: Tool<GitArgs> = {
202
212
  export const gitCommitTool: Tool<GitArgs> = {
203
213
  name: "git-commit",
204
214
  description: "commit staged changes with a message",
215
+ tag: "git",
205
216
  safe: false,
206
217
  permissionLabel: "git commit",
207
218
  systemPromptEntry: (i) =>
@@ -222,6 +233,7 @@ export const gitCommitTool: Tool<GitArgs> = {
222
233
  export const gitCommitAmendTool: Tool<GitArgs> = {
223
234
  name: "git-commit-amend",
224
235
  description: "amend the last commit message",
236
+ tag: "git",
225
237
  safe: false,
226
238
  permissionLabel: "git commit --amend",
227
239
  systemPromptEntry: (i) =>
@@ -246,6 +258,7 @@ export const gitRevertTool: Tool<GitArgs> = {
246
258
  name: "git-revert",
247
259
  description:
248
260
  "revert a commit by hash (creates a new revert commit, history preserved)",
261
+ tag: "git",
249
262
  safe: false,
250
263
  permissionLabel: "git revert",
251
264
  systemPromptEntry: (i) =>
@@ -266,6 +279,7 @@ export const gitRevertTool: Tool<GitArgs> = {
266
279
  export const gitResetTool: Tool<GitArgs> = {
267
280
  name: "git-reset",
268
281
  description: "reset HEAD or unstage files",
282
+ tag: "git",
269
283
  safe: false,
270
284
  permissionLabel: "git reset",
271
285
  systemPromptEntry: (i) =>
@@ -285,6 +299,7 @@ export const gitResetTool: Tool<GitArgs> = {
285
299
  export const gitCheckoutTool: Tool<GitArgs> = {
286
300
  name: "git-checkout",
287
301
  description: "switch branches or restore files",
302
+ tag: "git",
288
303
  safe: false,
289
304
  permissionLabel: "git checkout",
290
305
  systemPromptEntry: (i) =>
@@ -305,6 +320,7 @@ export const gitCheckoutTool: Tool<GitArgs> = {
305
320
  export const gitSwitchTool: Tool<GitArgs> = {
306
321
  name: "git-switch",
307
322
  description: "switch or create branches",
323
+ tag: "git",
308
324
  safe: false,
309
325
  permissionLabel: "git switch",
310
326
  systemPromptEntry: (i) =>
@@ -325,6 +341,7 @@ export const gitSwitchTool: Tool<GitArgs> = {
325
341
  export const gitMergeTool: Tool<GitArgs> = {
326
342
  name: "git-merge",
327
343
  description: "merge a branch into the current branch",
344
+ tag: "git",
328
345
  safe: false,
329
346
  permissionLabel: "git merge",
330
347
  systemPromptEntry: (i) =>
@@ -345,6 +362,7 @@ export const gitMergeTool: Tool<GitArgs> = {
345
362
  export const gitPullTool: Tool<GitArgs> = {
346
363
  name: "git-pull",
347
364
  description: "pull from remote",
365
+ tag: "git",
348
366
  safe: false,
349
367
  permissionLabel: "git pull",
350
368
  systemPromptEntry: (i) =>
@@ -363,6 +381,7 @@ export const gitPullTool: Tool<GitArgs> = {
363
381
  export const gitPushTool: Tool<GitArgs> = {
364
382
  name: "git-push",
365
383
  description: "push commits to remote",
384
+ tag: "git",
366
385
  safe: false,
367
386
  permissionLabel: "git push",
368
387
  systemPromptEntry: (i) =>
@@ -381,6 +400,7 @@ export const gitPushTool: Tool<GitArgs> = {
381
400
  export const gitStashTool: Tool<GitArgs> = {
382
401
  name: "git-stash",
383
402
  description: "stash or apply stashed changes",
403
+ tag: "git",
384
404
  safe: false,
385
405
  permissionLabel: "git stash",
386
406
  systemPromptEntry: (i) =>
@@ -401,6 +421,7 @@ export const gitStashTool: Tool<GitArgs> = {
401
421
  export const gitBranchCreateTool: Tool<GitArgs> = {
402
422
  name: "git-branch-create",
403
423
  description: "create a new branch without switching to it",
424
+ tag: "git",
404
425
  safe: false,
405
426
  permissionLabel: "git branch (create)",
406
427
  systemPromptEntry: (i) =>
@@ -421,6 +442,7 @@ export const gitBranchCreateTool: Tool<GitArgs> = {
421
442
  export const gitBranchDeleteTool: Tool<GitArgs> = {
422
443
  name: "git-branch-delete",
423
444
  description: "delete a branch",
445
+ tag: "git",
424
446
  safe: false,
425
447
  permissionLabel: "git branch -d",
426
448
  systemPromptEntry: (i) =>
@@ -444,6 +466,7 @@ export const gitBranchDeleteTool: Tool<GitArgs> = {
444
466
  export const gitCherryPickTool: Tool<GitArgs> = {
445
467
  name: "git-cherry-pick",
446
468
  description: "apply a specific commit from another branch",
469
+ tag: "git",
447
470
  safe: false,
448
471
  permissionLabel: "git cherry-pick",
449
472
  systemPromptEntry: (i) =>
@@ -466,6 +489,7 @@ export const gitCherryPickTool: Tool<GitArgs> = {
466
489
  export const gitTagCreateTool: Tool<GitArgs> = {
467
490
  name: "git-tag-create",
468
491
  description: "create a lightweight or annotated tag",
492
+ tag: "git",
469
493
  safe: false,
470
494
  permissionLabel: "git tag (create)",
471
495
  systemPromptEntry: (i) =>
@@ -486,6 +510,7 @@ export const gitRestoreTool: Tool<GitArgs> = {
486
510
  name: "git-restore",
487
511
  description:
488
512
  "discard working directory changes for a file (cannot be undone)",
513
+ tag: "git",
489
514
  safe: false,
490
515
  permissionLabel: "git restore",
491
516
  systemPromptEntry: (i) =>
@@ -505,6 +530,7 @@ export const gitRestoreTool: Tool<GitArgs> = {
505
530
  export const gitCleanTool: Tool<GitArgs> = {
506
531
  name: "git-clean",
507
532
  description: "remove untracked files (cannot be undone)",
533
+ tag: "git",
508
534
  safe: false,
509
535
  permissionLabel: "git clean",
510
536
  systemPromptEntry: (i) =>
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Classifies user message intent to scope which tools the LLM is allowed to use.
3
+ *
4
+ * readonly → only read/search/fetch tools exposed (no write, delete, shell)
5
+ * mutating → all tools exposed
6
+ * any → all tools exposed (ambiguous / can't tell)
7
+ */
8
+ export type Intent = "readonly" | "mutating" | "any";
9
+
10
+ const READONLY_PATTERNS: RegExp[] = [
11
+ // listing / exploring
12
+ /\b(list|ls|dir|show|display|print|dump)\b/i,
13
+ /\bwhat(('?s| is| are| does)\b| files| folder)/i,
14
+ /\b(folder|directory|file) (structure|tree|layout|contents?)\b/i,
15
+ /\bexplore\b/i,
16
+
17
+ // reading / explaining
18
+ /\b(read|open|view|look at|check out|inspect|peek)\b/i,
19
+ /\b(explain|describe|summarize|summarise|tell me about|walk me through)\b/i,
20
+ /\bhow does\b/i,
21
+ /\bwhat('?s| is) (in|inside|this|that|the)\b/i,
22
+
23
+ // searching
24
+ /\b(find|search|grep|locate|where is|where are)\b/i,
25
+ /\b(look for|scan|trace)\b/i,
26
+
27
+ // understanding
28
+ /\bunderstand\b/i,
29
+ /\bshow me (how|what|where|why)\b/i,
30
+ ];
31
+
32
+ const MUTATING_PATTERNS: RegExp[] = [
33
+ // writing
34
+ /\b(write|create|make|generate|add|build|scaffold|init|initialize|setup|set up)\b/i,
35
+ /\b(new file|new folder|new component|new page|new route)\b/i,
36
+
37
+ // editing
38
+ /\b(edit|modify|update|change|refactor|rename|move|migrate)\b/i,
39
+ /\b(fix|patch|resolve|correct|debug|repair)\b/i,
40
+ /\b(implement|add .+ to|insert|inject|append|prepend)\b/i,
41
+
42
+ // deleting
43
+ /\b(delete|remove|drop|clean ?up|purge|wipe)\b/i,
44
+
45
+ // running
46
+ /\b(run|execute|install|deploy|build|test|start|launch|compile|lint|format)\b/i,
47
+ ];
48
+
49
+ export function classifyIntent(userMessage: string): Intent {
50
+ const text = userMessage.trim();
51
+
52
+ const mutatingScore = MUTATING_PATTERNS.filter((p) => p.test(text)).length;
53
+ const readonlyScore = READONLY_PATTERNS.filter((p) => p.test(text)).length;
54
+
55
+ if (mutatingScore === 0 && readonlyScore > 0) return "readonly";
56
+ if (mutatingScore > 0) return "mutating";
57
+ return "any";
58
+ }
@@ -1,4 +1,5 @@
1
1
  import type { Tool, ToolContext } from "@ridit/lens-sdk";
2
+ import { TOOL_TAGS } from "@ridit/lens-sdk";
2
3
  import {
3
4
  fetchUrl,
4
5
  searchWeb,
@@ -23,6 +24,7 @@ export const fetchTool: Tool<string> = {
23
24
  name: "fetch",
24
25
  description: "load a URL",
25
26
  safe: true,
27
+ tag: TOOL_TAGS.net,
26
28
  permissionLabel: "fetch",
27
29
  systemPromptEntry: (i) =>
28
30
  `### ${i}. fetch — load a URL\n<fetch>https://example.com</fetch>`,
@@ -45,6 +47,7 @@ export const shellTool: Tool<string> = {
45
47
  name: "shell",
46
48
  description: "run a terminal command",
47
49
  safe: false,
50
+ tag: TOOL_TAGS.shell,
48
51
  permissionLabel: "run",
49
52
  systemPromptEntry: (i) =>
50
53
  `### ${i}. shell — run a terminal command\n<shell>node -v</shell>`,
@@ -60,6 +63,7 @@ export const readFileTool: Tool<string> = {
60
63
  name: "read-file",
61
64
  description: "read a file from the repo",
62
65
  safe: true,
66
+ tag: TOOL_TAGS.read,
63
67
  permissionLabel: "read",
64
68
  systemPromptEntry: (i) =>
65
69
  `### ${i}. read-file — read a file from the repo\n<read-file>src/foo.ts</read-file>`,
@@ -74,6 +78,7 @@ export const readFileTool: Tool<string> = {
74
78
  export const readFolderTool: Tool<string> = {
75
79
  name: "read-folder",
76
80
  description: "list contents of a folder (files + subfolders, one level deep)",
81
+ tag: TOOL_TAGS.read,
77
82
  safe: true,
78
83
  permissionLabel: "folder",
79
84
  systemPromptEntry: (i) =>
@@ -94,6 +99,7 @@ interface GrepInput {
94
99
  export const grepTool: Tool<GrepInput> = {
95
100
  name: "grep",
96
101
  description: "search for a pattern across files in the repo",
102
+ tag: TOOL_TAGS.find,
97
103
  safe: true,
98
104
  permissionLabel: "grep",
99
105
  systemPromptEntry: (i) =>
@@ -124,6 +130,7 @@ interface WriteFileInput {
124
130
  export const writeFileTool: Tool<WriteFileInput> = {
125
131
  name: "write-file",
126
132
  description: "create or overwrite a file",
133
+ tag: TOOL_TAGS.write,
127
134
  safe: false,
128
135
  permissionLabel: "write",
129
136
  systemPromptEntry: (i) =>
@@ -147,6 +154,7 @@ export const writeFileTool: Tool<WriteFileInput> = {
147
154
  export const deleteFileTool: Tool<string> = {
148
155
  name: "delete-file",
149
156
  description: "permanently delete a single file",
157
+ tag: TOOL_TAGS.delete,
150
158
  safe: false,
151
159
  permissionLabel: "delete",
152
160
  systemPromptEntry: (i) =>
@@ -162,6 +170,7 @@ export const deleteFileTool: Tool<string> = {
162
170
  export const deleteFolderTool: Tool<string> = {
163
171
  name: "delete-folder",
164
172
  description: "permanently delete a folder and all its contents",
173
+ tag: TOOL_TAGS.delete,
165
174
  safe: false,
166
175
  permissionLabel: "delete folder",
167
176
  systemPromptEntry: (i) =>
@@ -177,6 +186,7 @@ export const deleteFolderTool: Tool<string> = {
177
186
  export const openUrlTool: Tool<string> = {
178
187
  name: "open-url",
179
188
  description: "open a URL in the user's default browser",
189
+ tag: TOOL_TAGS.net,
180
190
  safe: true,
181
191
  permissionLabel: "open",
182
192
  systemPromptEntry: (i) =>
@@ -194,6 +204,7 @@ interface GeneratePdfInput {
194
204
  export const generatePdfTool: Tool<GeneratePdfInput> = {
195
205
  name: "generate-pdf",
196
206
  description: "generate a PDF file from markdown-style content",
207
+ tag: TOOL_TAGS.write,
197
208
  safe: false,
198
209
  permissionLabel: "pdf",
199
210
  systemPromptEntry: (i) =>
@@ -222,6 +233,7 @@ export const generatePdfTool: Tool<GeneratePdfInput> = {
222
233
 
223
234
  export const searchTool: Tool<string> = {
224
235
  name: "search",
236
+ tag: TOOL_TAGS.net,
225
237
  description: "search the internet for anything you are unsure about",
226
238
  safe: true,
227
239
  permissionLabel: "search",
@@ -245,6 +257,7 @@ export const searchTool: Tool<string> = {
245
257
  export const cloneTool: Tool<string> = {
246
258
  name: "clone",
247
259
  description: "clone a GitHub repo so you can explore and discuss it",
260
+ tag: TOOL_TAGS.write,
248
261
  safe: false,
249
262
  permissionLabel: "clone",
250
263
  systemPromptEntry: (i) =>
@@ -265,6 +278,7 @@ export interface ChangesInput {
265
278
  export const changesTool: Tool<ChangesInput> = {
266
279
  name: "changes",
267
280
  description: "propose code edits (shown as a diff for user approval)",
281
+ tag: TOOL_TAGS.write,
268
282
  safe: false,
269
283
  permissionLabel: "changes",
270
284
  systemPromptEntry: (i) =>
@@ -290,6 +304,7 @@ interface ReadFilesInput {
290
304
  export const readFilesTool: Tool<ReadFilesInput> = {
291
305
  name: "read-files",
292
306
  description: "read multiple files from the repo at once",
307
+ tag: TOOL_TAGS.read,
293
308
  safe: true,
294
309
  permissionLabel: "read",
295
310
  systemPromptEntry: (i) =>
@@ -1,4 +1,23 @@
1
- import type { Tool } from "@ridit/lens-sdk";
1
+ import type { Tool, ToolTag } from "@ridit/lens-sdk";
2
+ import type { Intent } from "../intentClassifier";
3
+
4
+ /**
5
+ * Broad capability category for a tool.
6
+ * Used to filter the system prompt based on classified user intent.
7
+ *
8
+ * "read" — safe, purely observational (read-file, read-folder, grep, etc.)
9
+ * "net" — outbound network (fetch, search, clone, open-url)
10
+ * "write" — creates or overwrites file content (write-file, changes, generate-pdf)
11
+ * "delete" — destructive removal (delete-file, delete-folder)
12
+ * "shell" — arbitrary shell execution
13
+ */
14
+
15
+ /** Tools allowed for each intent level */
16
+ const INTENT_ALLOWED: Record<Intent, ToolTag[]> = {
17
+ readonly: ["read", "net"],
18
+ mutating: ["read", "net", "write", "delete", "shell"],
19
+ any: ["read", "net", "write", "delete", "shell"],
20
+ };
2
21
 
3
22
  class ToolRegistry {
4
23
  private tools = new Map<string, Tool<unknown>>();
@@ -27,17 +46,54 @@ class ToolRegistry {
27
46
  }
28
47
 
29
48
  /**
30
- * Build the TOOLS section of the system prompt from all registered tools.
49
+ * Returns tool names that are allowed for the given intent.
50
+ * Falls back to all names when a tool has no tag (legacy / addons).
51
+ */
52
+ namesForIntent(intent: Intent): string[] {
53
+ const allowed = new Set(INTENT_ALLOWED[intent]);
54
+ return Array.from(this.tools.values())
55
+ .filter((t) => {
56
+ const tag = (t as any).tag as ToolTag | undefined;
57
+ // No tag = addon / unknown → always allow (conservative)
58
+ if (!tag) return true;
59
+ return allowed.has(tag);
60
+ })
61
+ .map((t) => t.name);
62
+ }
63
+
64
+ /**
65
+ * Build the TOOLS section of the system prompt from all registered tools,
66
+ * optionally scoped to a specific intent.
67
+ *
68
+ * When intent is "readonly", write/delete/shell tools are omitted entirely
69
+ * so the LLM never sees them and can't hallucinate calls to them.
31
70
  */
32
- buildSystemPromptSection(): string {
71
+ buildSystemPromptSection(intent: Intent = "any"): string {
72
+ const allowed = new Set(INTENT_ALLOWED[intent]);
73
+
74
+ const visible = Array.from(this.tools.values()).filter((t) => {
75
+ const tag = (t as any).tag as ToolTag | undefined;
76
+ if (!tag) return true; // addon without tag → always show
77
+ return allowed.has(tag);
78
+ });
79
+
33
80
  const lines: string[] = ["## TOOLS\n"];
34
- lines.push(
35
- "You have exactly " +
36
- this.tools.size +
37
- " tools. To use a tool you MUST wrap it in the exact XML tags shown below — no other format will work.\n",
38
- );
81
+
82
+ if (intent === "readonly") {
83
+ lines.push(
84
+ `You have ${visible.length} tools available for this read-only request. ` +
85
+ `Do NOT attempt to write, delete, or run shell commands — ` +
86
+ `those tools are not available right now.\n`,
87
+ );
88
+ } else {
89
+ lines.push(
90
+ `You have exactly ${visible.length} tools. To use a tool you MUST wrap it ` +
91
+ `in the exact XML tags shown below — no other format will work.\n`,
92
+ );
93
+ }
94
+
39
95
  let i = 1;
40
- for (const tool of this.tools.values()) {
96
+ for (const tool of visible) {
41
97
  lines.push(tool.systemPromptEntry(i++));
42
98
  }
43
99
  return lines.join("\n");