@tryhamster/gerbil 1.0.0-rc.0
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/LICENSE +23 -0
- package/README.md +253 -0
- package/bin/cli.js +2 -0
- package/dist/auto-update-BbNHbSU1.mjs +3 -0
- package/dist/browser/index.d.mts +262 -0
- package/dist/browser/index.d.mts.map +1 -0
- package/dist/browser/index.mjs +755 -0
- package/dist/browser/index.mjs.map +1 -0
- package/dist/chrome-backend-C5Un08O4.mjs +771 -0
- package/dist/chrome-backend-C5Un08O4.mjs.map +1 -0
- package/dist/chrome-backend-CtwPENIW.mjs +3 -0
- package/dist/chunk-Ct1HF2bE.mjs +7 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +7078 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/frameworks/express.d.mts +22 -0
- package/dist/frameworks/express.d.mts.map +1 -0
- package/dist/frameworks/express.mjs +123 -0
- package/dist/frameworks/express.mjs.map +1 -0
- package/dist/frameworks/fastify.d.mts +11 -0
- package/dist/frameworks/fastify.d.mts.map +1 -0
- package/dist/frameworks/fastify.mjs +73 -0
- package/dist/frameworks/fastify.mjs.map +1 -0
- package/dist/frameworks/hono.d.mts +14 -0
- package/dist/frameworks/hono.d.mts.map +1 -0
- package/dist/frameworks/hono.mjs +82 -0
- package/dist/frameworks/hono.mjs.map +1 -0
- package/dist/frameworks/next.d.mts +31 -0
- package/dist/frameworks/next.d.mts.map +1 -0
- package/dist/frameworks/next.mjs +116 -0
- package/dist/frameworks/next.mjs.map +1 -0
- package/dist/frameworks/react.d.mts +56 -0
- package/dist/frameworks/react.d.mts.map +1 -0
- package/dist/frameworks/react.mjs +172 -0
- package/dist/frameworks/react.mjs.map +1 -0
- package/dist/frameworks/trpc.d.mts +12 -0
- package/dist/frameworks/trpc.d.mts.map +1 -0
- package/dist/frameworks/trpc.mjs +80 -0
- package/dist/frameworks/trpc.mjs.map +1 -0
- package/dist/gerbil-BfnsFWRE.mjs +644 -0
- package/dist/gerbil-BfnsFWRE.mjs.map +1 -0
- package/dist/gerbil-BjW-z7Fq.mjs +5 -0
- package/dist/gerbil-DZ1k3ChC.d.mts +138 -0
- package/dist/gerbil-DZ1k3ChC.d.mts.map +1 -0
- package/dist/index.d.mts +223 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +13 -0
- package/dist/index.mjs.map +1 -0
- package/dist/integrations/ai-sdk.d.mts +78 -0
- package/dist/integrations/ai-sdk.d.mts.map +1 -0
- package/dist/integrations/ai-sdk.mjs +199 -0
- package/dist/integrations/ai-sdk.mjs.map +1 -0
- package/dist/integrations/langchain.d.mts +41 -0
- package/dist/integrations/langchain.d.mts.map +1 -0
- package/dist/integrations/langchain.mjs +93 -0
- package/dist/integrations/langchain.mjs.map +1 -0
- package/dist/integrations/llamaindex.d.mts +45 -0
- package/dist/integrations/llamaindex.d.mts.map +1 -0
- package/dist/integrations/llamaindex.mjs +86 -0
- package/dist/integrations/llamaindex.mjs.map +1 -0
- package/dist/integrations/mcp-client.d.mts +206 -0
- package/dist/integrations/mcp-client.d.mts.map +1 -0
- package/dist/integrations/mcp-client.mjs +507 -0
- package/dist/integrations/mcp-client.mjs.map +1 -0
- package/dist/integrations/mcp.d.mts +177 -0
- package/dist/integrations/mcp.d.mts.map +1 -0
- package/dist/integrations/mcp.mjs +8 -0
- package/dist/mcp-R8kRLIKb.mjs +348 -0
- package/dist/mcp-R8kRLIKb.mjs.map +1 -0
- package/dist/models-DKULvhOr.mjs +136 -0
- package/dist/models-DKULvhOr.mjs.map +1 -0
- package/dist/models-De2-_GmQ.d.mts +22 -0
- package/dist/models-De2-_GmQ.d.mts.map +1 -0
- package/dist/one-liner-BUQR0nqq.mjs +98 -0
- package/dist/one-liner-BUQR0nqq.mjs.map +1 -0
- package/dist/skills/index.d.mts +390 -0
- package/dist/skills/index.d.mts.map +1 -0
- package/dist/skills/index.mjs +7 -0
- package/dist/skills-D3CEpgDc.mjs +630 -0
- package/dist/skills-D3CEpgDc.mjs.map +1 -0
- package/dist/tools-BsiEE6f2.mjs +567 -0
- package/dist/tools-BsiEE6f2.mjs.map +1 -0
- package/dist/types-BS1N92Jt.d.mts +183 -0
- package/dist/types-BS1N92Jt.d.mts.map +1 -0
- package/dist/utils-7vXqtq2Q.mjs +63 -0
- package/dist/utils-7vXqtq2Q.mjs.map +1 -0
- package/docs/ai-sdk.md +80 -0
- package/docs/architecture/README.md +84 -0
- package/docs/architecture/caching.md +227 -0
- package/docs/architecture/inference.md +176 -0
- package/docs/architecture/overview.md +179 -0
- package/docs/architecture/streaming.md +261 -0
- package/docs/architecture/webgpu.md +213 -0
- package/docs/browser.md +328 -0
- package/docs/cli.md +155 -0
- package/docs/frameworks.md +90 -0
- package/docs/mcp-client.md +224 -0
- package/docs/mcp.md +109 -0
- package/docs/memory.md +229 -0
- package/docs/repl.md +473 -0
- package/docs/skills.md +261 -0
- package/docs/tools.md +304 -0
- package/package.json +207 -0
package/docs/skills.md
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
# Gerbil Skills
|
|
2
|
+
|
|
3
|
+
Skills are reusable AI tasks with Zod-validated inputs and outputs.
|
|
4
|
+
|
|
5
|
+
## Built-in Skills
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import {
|
|
9
|
+
commit, summarize, explain, review,
|
|
10
|
+
test, translate, extract, title
|
|
11
|
+
} from "@tryhamster/gerbil/skills";
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
### commit
|
|
15
|
+
|
|
16
|
+
Generate git commit messages from staged changes.
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
const msg = await commit({
|
|
20
|
+
type: "conventional", // "conventional" | "simple" | "detailed"
|
|
21
|
+
maxLength: 72
|
|
22
|
+
});
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### summarize
|
|
26
|
+
|
|
27
|
+
Summarize content.
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
const summary = await summarize({
|
|
31
|
+
content: document,
|
|
32
|
+
length: "short", // "short" | "medium" | "long"
|
|
33
|
+
format: "bullets", // "paragraph" | "bullets"
|
|
34
|
+
focus: ["key points"]
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### explain
|
|
39
|
+
|
|
40
|
+
Explain code or concepts.
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
const explanation = await explain({
|
|
44
|
+
content: code,
|
|
45
|
+
level: "beginner", // "beginner" | "intermediate" | "expert"
|
|
46
|
+
language: "typescript"
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### review
|
|
51
|
+
|
|
52
|
+
Code review.
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
const feedback = await review({
|
|
56
|
+
code,
|
|
57
|
+
focus: ["security", "performance"], // "security" | "performance" | "style" | "bugs" | "all"
|
|
58
|
+
format: "detailed" // "inline" | "summary" | "detailed"
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### test
|
|
63
|
+
|
|
64
|
+
Generate tests.
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
const tests = await test({
|
|
68
|
+
code,
|
|
69
|
+
framework: "vitest", // "jest" | "vitest" | "mocha" | "playwright"
|
|
70
|
+
style: "unit" // "unit" | "integration" | "e2e"
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### translate
|
|
75
|
+
|
|
76
|
+
Translate text.
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
const spanish = await translate({
|
|
80
|
+
text,
|
|
81
|
+
to: "es",
|
|
82
|
+
from: "en", // optional, auto-detected
|
|
83
|
+
preserveFormatting: true
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### extract
|
|
88
|
+
|
|
89
|
+
Extract structured data.
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
const data = await extract({
|
|
93
|
+
content: "John is 32 and lives in NYC",
|
|
94
|
+
schema: z.object({ name: z.string(), age: z.number(), city: z.string() }),
|
|
95
|
+
context: "Extract person info"
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### title
|
|
100
|
+
|
|
101
|
+
Generate titles.
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
const heading = await title({
|
|
105
|
+
content: article,
|
|
106
|
+
style: "seo", // "professional" | "clickbait" | "seo" | "simple"
|
|
107
|
+
maxLength: 60
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Custom Skills
|
|
112
|
+
|
|
113
|
+
### defineSkill
|
|
114
|
+
|
|
115
|
+
Create a custom skill:
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
import { defineSkill } from "@tryhamster/gerbil/skills";
|
|
119
|
+
import { z } from "zod";
|
|
120
|
+
|
|
121
|
+
export const sentiment = defineSkill({
|
|
122
|
+
// Required
|
|
123
|
+
name: "sentiment", // kebab-case
|
|
124
|
+
description: "Analyze text sentiment",
|
|
125
|
+
|
|
126
|
+
// Input/Output validation
|
|
127
|
+
input: z.object({
|
|
128
|
+
text: z.string(),
|
|
129
|
+
detailed: z.boolean().default(false)
|
|
130
|
+
}),
|
|
131
|
+
output: z.object({
|
|
132
|
+
sentiment: z.enum(["positive", "negative", "neutral"]),
|
|
133
|
+
confidence: z.number(),
|
|
134
|
+
keywords: z.array(z.string()).optional()
|
|
135
|
+
}),
|
|
136
|
+
|
|
137
|
+
// Defaults
|
|
138
|
+
temperature: 0.3,
|
|
139
|
+
maxTokens: 100,
|
|
140
|
+
thinking: false,
|
|
141
|
+
|
|
142
|
+
// Model (skill loads this automatically if different from current)
|
|
143
|
+
model: "qwen2.5-coder-0.5b",
|
|
144
|
+
|
|
145
|
+
// Metadata
|
|
146
|
+
version: "1.0.0",
|
|
147
|
+
author: "your-name",
|
|
148
|
+
|
|
149
|
+
// Implementation - receives (input, gerbil)
|
|
150
|
+
async run(input, gerbil) {
|
|
151
|
+
const prompt = input.detailed
|
|
152
|
+
? `Analyze sentiment with keywords: ${input.text}`
|
|
153
|
+
: `What is the sentiment: ${input.text}`;
|
|
154
|
+
|
|
155
|
+
return gerbil.json(prompt, { schema: this.output });
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### File Convention
|
|
161
|
+
|
|
162
|
+
Skills can be loaded from files matching `*.skill.ts` or `*.skill.js`:
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
// skills/sentiment.skill.ts
|
|
166
|
+
import { defineSkill } from "@tryhamster/gerbil/skills";
|
|
167
|
+
|
|
168
|
+
export default defineSkill({
|
|
169
|
+
name: "sentiment",
|
|
170
|
+
// ...
|
|
171
|
+
});
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Loading Skills
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
import { loadSkills, loadSkillPackage, useSkill } from "@tryhamster/gerbil/skills";
|
|
178
|
+
|
|
179
|
+
// Load from directory
|
|
180
|
+
await loadSkills("./skills"); // loads *.skill.ts files
|
|
181
|
+
|
|
182
|
+
// Load from npm package
|
|
183
|
+
await loadSkillPackage("gerbil-skills-extra");
|
|
184
|
+
|
|
185
|
+
// Use by name
|
|
186
|
+
const sentiment = useSkill("sentiment");
|
|
187
|
+
const result = await sentiment({ text: "I love this!" });
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Skill-Specific Models
|
|
191
|
+
|
|
192
|
+
Skills can specify which model they prefer:
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
export const codeReview = defineSkill({
|
|
196
|
+
name: "code-review",
|
|
197
|
+
description: "Review code for issues",
|
|
198
|
+
model: "qwen2.5-coder-0.5b", // Uses coder model
|
|
199
|
+
|
|
200
|
+
async run(input, gerbil) {
|
|
201
|
+
// gerbil is already loaded with qwen2.5-coder-0.5b
|
|
202
|
+
return gerbil.generate(`Review this code: ${input.code}`);
|
|
203
|
+
},
|
|
204
|
+
});
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
When a skill specifies a different model than the currently loaded one, Gerbil automatically switches models. To avoid this overhead, you can pre-load the skill's model or pass your own Gerbil instance:
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
// Option 1: Let skill switch automatically (may incur loading time)
|
|
211
|
+
const result = await codeReview({ code: myCode });
|
|
212
|
+
|
|
213
|
+
// Option 2: Pass pre-loaded Gerbil instance
|
|
214
|
+
const g = new Gerbil();
|
|
215
|
+
await g.loadModel("qwen2.5-coder-0.5b");
|
|
216
|
+
const result = await codeReview.run({ code: myCode }, g);
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Registry API
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
import {
|
|
223
|
+
listSkills, // Get all skill names
|
|
224
|
+
getSkillInfo, // Get skill metadata
|
|
225
|
+
hasSkill, // Check if skill exists
|
|
226
|
+
removeSkill, // Unregister a skill
|
|
227
|
+
clearSkills // Clear all skills
|
|
228
|
+
} from "@tryhamster/gerbil/skills";
|
|
229
|
+
|
|
230
|
+
console.log(listSkills());
|
|
231
|
+
// ["commit", "summarize", "explain", "review", ...]
|
|
232
|
+
|
|
233
|
+
const info = getSkillInfo("commit");
|
|
234
|
+
// { name, description, version, author, builtin: true }
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## REPL Skills View
|
|
238
|
+
|
|
239
|
+
In the Gerbil REPL, press `2` or navigate to **Skills** to access all available skills.
|
|
240
|
+
|
|
241
|
+
**Controls:**
|
|
242
|
+
- **Up/Down** — Navigate skill list
|
|
243
|
+
- **Enter** — Select and run a skill
|
|
244
|
+
- **c** — Create a new skill (opens wizard)
|
|
245
|
+
- **Esc** — Back to menu
|
|
246
|
+
|
|
247
|
+
The first option **"+ Create new skill"** opens a guided wizard that helps you build a custom skill step-by-step.
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## MCP Integration
|
|
252
|
+
|
|
253
|
+
Skills are automatically exposed as MCP tools:
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
gerbil serve --mcp
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Each skill becomes a tool: `gerbil_commit`, `gerbil_summarize`, etc.
|
|
260
|
+
|
|
261
|
+
Custom skills loaded via `loadSkills()` are also exposed.
|
package/docs/tools.md
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
# Gerbil Tools & Agents
|
|
2
|
+
|
|
3
|
+
Gerbil supports tool calling with Qwen3 models, enabling agentic workflows where the LLM can call functions to accomplish tasks.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
### Simple Tool (Recommended)
|
|
8
|
+
|
|
9
|
+
Create a file in `.gerbil/tools/` with the `.tool.ts` extension:
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
// .gerbil/tools/get_weather.tool.ts
|
|
13
|
+
|
|
14
|
+
export default {
|
|
15
|
+
name: "get_weather",
|
|
16
|
+
description: "Get current weather for a city",
|
|
17
|
+
parameters: {
|
|
18
|
+
city: { type: "string", description: "City name" },
|
|
19
|
+
},
|
|
20
|
+
execute: async (params: { city: string }) => {
|
|
21
|
+
return `Weather in ${params.city}: 72°F, sunny`;
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
That's it! Gerbil automatically loads tools from `.gerbil/tools/` when the REPL starts.
|
|
27
|
+
|
|
28
|
+
### Advanced Tool (with Zod)
|
|
29
|
+
|
|
30
|
+
For more complex validation, use `defineTool` with Zod schemas:
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import { defineTool } from "@tryhamster/gerbil";
|
|
34
|
+
import { z } from "zod";
|
|
35
|
+
|
|
36
|
+
export const weatherTool = defineTool({
|
|
37
|
+
name: "get_weather",
|
|
38
|
+
description: "Get current weather for a city",
|
|
39
|
+
parameters: z.object({
|
|
40
|
+
city: z.string().describe("City name"),
|
|
41
|
+
units: z.enum(["celsius", "fahrenheit"]).optional().default("fahrenheit"),
|
|
42
|
+
}),
|
|
43
|
+
execute: async ({ city, units }) => {
|
|
44
|
+
return `Weather in ${city}: ${units === "celsius" ? "22°C" : "72°F"}, sunny`;
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Project Tools
|
|
50
|
+
|
|
51
|
+
Store your custom tools in `.gerbil/tools/`:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
.gerbil/
|
|
55
|
+
└── tools/
|
|
56
|
+
├── get_weather.tool.ts
|
|
57
|
+
├── search_docs.tool.ts
|
|
58
|
+
└── calculator.tool.ts
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Tool File Format
|
|
62
|
+
|
|
63
|
+
Each `.tool.ts` file exports a config object:
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
// .gerbil/tools/my_tool.tool.ts
|
|
67
|
+
|
|
68
|
+
export default {
|
|
69
|
+
name: "my_tool", // Tool name (snake_case)
|
|
70
|
+
description: "What the tool does", // LLM uses this to decide when to call
|
|
71
|
+
parameters: { // Optional input parameters
|
|
72
|
+
param1: { type: "string", description: "First param" },
|
|
73
|
+
param2: { type: "number", optional: true },
|
|
74
|
+
},
|
|
75
|
+
execute: async (params) => {
|
|
76
|
+
// Your implementation
|
|
77
|
+
return "result string";
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Managing Tools in REPL
|
|
83
|
+
|
|
84
|
+
Press `3` or select **Tools** from the menu to:
|
|
85
|
+
- View all registered tools (builtin + project)
|
|
86
|
+
- Create new tools with the guided wizard
|
|
87
|
+
- Execute tools directly with `x`
|
|
88
|
+
- Open tool files in VS Code with `o`
|
|
89
|
+
|
|
90
|
+
## Built-in Tools
|
|
91
|
+
|
|
92
|
+
### `gerbil_docs`
|
|
93
|
+
|
|
94
|
+
Search Gerbil documentation for any topic.
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
import { docsTool } from "@tryhamster/gerbil";
|
|
98
|
+
|
|
99
|
+
const result = await docsTool({ query: "streaming" });
|
|
100
|
+
// Returns documentation about streaming responses
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Available topics:**
|
|
104
|
+
- `quickstart` — Getting started
|
|
105
|
+
- `generate` — Text generation options
|
|
106
|
+
- `stream` — Streaming responses
|
|
107
|
+
- `json` — Structured JSON output
|
|
108
|
+
- `thinking` — Chain-of-thought reasoning
|
|
109
|
+
- `embed` — Embeddings
|
|
110
|
+
- `models` — Available models
|
|
111
|
+
- `ai-sdk` — Vercel AI SDK integration
|
|
112
|
+
- `next`, `express`, `react`, `hono` — Framework integrations
|
|
113
|
+
- `skills` — Skills system
|
|
114
|
+
- `cli` — CLI commands
|
|
115
|
+
- `tools` — Tool calling
|
|
116
|
+
|
|
117
|
+
## Tool Examples
|
|
118
|
+
|
|
119
|
+
### Calculator
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
// .gerbil/tools/calculator.tool.ts
|
|
123
|
+
|
|
124
|
+
export default {
|
|
125
|
+
name: "calculator",
|
|
126
|
+
description: "Perform basic math calculations",
|
|
127
|
+
parameters: {
|
|
128
|
+
expression: { type: "string", description: "Math expression like '2 + 2'" },
|
|
129
|
+
},
|
|
130
|
+
execute: async (params: { expression: string }) => {
|
|
131
|
+
try {
|
|
132
|
+
// Simple evaluator (use a proper math lib in production)
|
|
133
|
+
const result = Function(`return ${params.expression}`)();
|
|
134
|
+
return `${params.expression} = ${result}`;
|
|
135
|
+
} catch {
|
|
136
|
+
return `Error: Invalid expression "${params.expression}"`;
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### File Reader
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
// .gerbil/tools/read_file.tool.ts
|
|
146
|
+
|
|
147
|
+
import fs from "fs/promises";
|
|
148
|
+
|
|
149
|
+
export default {
|
|
150
|
+
name: "read_file",
|
|
151
|
+
description: "Read contents of a file",
|
|
152
|
+
parameters: {
|
|
153
|
+
path: { type: "string", description: "File path to read" },
|
|
154
|
+
},
|
|
155
|
+
execute: async (params: { path: string }) => {
|
|
156
|
+
try {
|
|
157
|
+
const content = await fs.readFile(params.path, "utf-8");
|
|
158
|
+
return content.slice(0, 2000); // Limit output size
|
|
159
|
+
} catch (e) {
|
|
160
|
+
return `Error reading file: ${e}`;
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### API Caller
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
// .gerbil/tools/fetch_url.tool.ts
|
|
170
|
+
|
|
171
|
+
export default {
|
|
172
|
+
name: "fetch_url",
|
|
173
|
+
description: "Fetch content from a URL",
|
|
174
|
+
parameters: {
|
|
175
|
+
url: { type: "string", description: "URL to fetch" },
|
|
176
|
+
},
|
|
177
|
+
execute: async (params: { url: string }) => {
|
|
178
|
+
try {
|
|
179
|
+
const res = await fetch(params.url);
|
|
180
|
+
const text = await res.text();
|
|
181
|
+
return text.slice(0, 2000);
|
|
182
|
+
} catch (e) {
|
|
183
|
+
return `Error fetching URL: ${e}`;
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Joke Generator
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
// .gerbil/tools/generate_joke.tool.ts
|
|
193
|
+
|
|
194
|
+
export default {
|
|
195
|
+
name: "generate_joke",
|
|
196
|
+
description: "Generates a joke based on a theme",
|
|
197
|
+
parameters: {
|
|
198
|
+
theme: { type: "string", description: "The theme (optional)", optional: true },
|
|
199
|
+
},
|
|
200
|
+
execute: async (params: { theme?: string }) => {
|
|
201
|
+
const theme = params?.theme || "programming";
|
|
202
|
+
const jokes: Record<string, string> = {
|
|
203
|
+
programming: "Why do programmers prefer dark mode? Because light attracts bugs!",
|
|
204
|
+
coffee: "Why do Java developers wear glasses? Because they can't C#!",
|
|
205
|
+
ai: "Why did the neural network break up with the algorithm? Too many trust issues!",
|
|
206
|
+
};
|
|
207
|
+
return jokes[theme.toLowerCase()] || `Why did the ${theme} cross the road? To optimize the other side!`;
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Tool Registry API
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
import {
|
|
216
|
+
defineTool,
|
|
217
|
+
getTool,
|
|
218
|
+
listTools,
|
|
219
|
+
getToolDefinitions,
|
|
220
|
+
loadProjectTools
|
|
221
|
+
} from "@tryhamster/gerbil";
|
|
222
|
+
|
|
223
|
+
// Load tools from .gerbil/tools/
|
|
224
|
+
await loadProjectTools();
|
|
225
|
+
|
|
226
|
+
// Get tool by name
|
|
227
|
+
const tool = getTool("get_weather");
|
|
228
|
+
await tool({ city: "NYC" });
|
|
229
|
+
|
|
230
|
+
// List all tool names
|
|
231
|
+
console.log(listTools());
|
|
232
|
+
// ["gerbil_docs", "get_weather", "calculator"]
|
|
233
|
+
|
|
234
|
+
// Get all definitions (for prompt injection)
|
|
235
|
+
const definitions = getToolDefinitions();
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## Using Tools in Chat
|
|
239
|
+
|
|
240
|
+
### REPL Agent Mode
|
|
241
|
+
|
|
242
|
+
The easiest way to use tools is in the REPL's Agent mode:
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
gerbil repl
|
|
246
|
+
# Press ⌘A (Cmd+A) to toggle agent mode
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
**Toggle shortcuts:**
|
|
250
|
+
- **⌘A (Cmd+A)**: Toggle agent mode on/off (works anywhere)
|
|
251
|
+
- **⌘T (Cmd+T)**: Toggle thinking mode on/off
|
|
252
|
+
|
|
253
|
+
When agent mode is enabled:
|
|
254
|
+
- Shows `+tools` badge in the header
|
|
255
|
+
- Tool calls display in cyan boxes with results
|
|
256
|
+
- Works with any chat persona
|
|
257
|
+
|
|
258
|
+
In Agent mode, the model can:
|
|
259
|
+
1. See available tools in its system prompt
|
|
260
|
+
2. Call tools using the `<tool_call>` format
|
|
261
|
+
3. Receive tool results and synthesize a response
|
|
262
|
+
|
|
263
|
+
### Programmatic Usage
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
import {
|
|
267
|
+
formatToolsForPrompt,
|
|
268
|
+
parseToolCall,
|
|
269
|
+
executeToolCall,
|
|
270
|
+
getToolDefinitions
|
|
271
|
+
} from "@tryhamster/gerbil";
|
|
272
|
+
|
|
273
|
+
// Add tools to system prompt
|
|
274
|
+
const tools = getToolDefinitions();
|
|
275
|
+
const systemPrompt = `You are a helpful assistant.\n\n${formatToolsForPrompt(tools)}`;
|
|
276
|
+
|
|
277
|
+
// Generate response
|
|
278
|
+
const response = await gerbil.generate(userQuery, { system: systemPrompt });
|
|
279
|
+
|
|
280
|
+
// Check for tool calls
|
|
281
|
+
const toolCall = parseToolCall(response.text);
|
|
282
|
+
if (toolCall) {
|
|
283
|
+
const result = await executeToolCall(toolCall.tool, toolCall.params);
|
|
284
|
+
// Continue conversation with tool result
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Best Practices
|
|
289
|
+
|
|
290
|
+
1. **Clear descriptions** — The model uses descriptions to decide when to call tools
|
|
291
|
+
2. **Handle missing params** — Use defaults for optional parameters
|
|
292
|
+
3. **Limit output size** — Long tool outputs consume context (aim for <2000 chars)
|
|
293
|
+
4. **Return strings** — Execute function must return a string
|
|
294
|
+
5. **Graceful errors** — Catch exceptions and return helpful error messages
|
|
295
|
+
6. **Snake_case names** — Use `get_weather` not `getWeather`
|
|
296
|
+
|
|
297
|
+
## Model Compatibility
|
|
298
|
+
|
|
299
|
+
Tool calling works best with:
|
|
300
|
+
- **Qwen3-0.6B** — Excellent tool calling support, fast
|
|
301
|
+
- **Qwen3-1.7B** — Better reasoning, still fast
|
|
302
|
+
- **Qwen2.5 models** — Good support
|
|
303
|
+
|
|
304
|
+
Smaller models (SmolLM2) may struggle with consistent tool call formatting.
|