| name | add-tool |
| description | Scaffold a new agent tool package for spaceduck |
Add Tool
Scaffolds a new agent tool under packages/tools/<name>/.
Steps
- Ask for the tool name (e.g., "web-search", "shell-exec", "file-read")
- Create the directory structure:
packages/tools/<name>/
package.json # @spaceduck/tool-<name>
src/
<name>-tool.ts # main tool class
types.ts # (optional) interfaces/options
index.ts # barrel export
__tests__/
<name>.test.ts # bun:test suite
- Create
package.json:
{
"name": "@spaceduck/tool-<name>",
"version": "0.1.0",
"private": true,
"type": "module",
"main": "src/index.ts",
"types": "src/index.ts",
"scripts": { "test": "bun test" },
"dependencies": {},
"devDependencies": { "bun-types": "latest" }
}
- The tool class must follow this pattern:
export interface <Name>ToolOptions {
maxChars?: number;
}
export class <Name>Tool {
constructor(options: <Name>ToolOptions = {}) { }
async doSomething(args: ...): Promise<string> { }
}
-
Key conventions:
- Constructor accepts an options object with sensible defaults
- All public methods return
Promise<string> (tool results are always text for the LLM)
- Return error descriptions as strings -- never throw. The LLM needs to read failures
- Truncate large outputs at
maxChars (default 50,000) with [truncated] marker
- Include context headers in results (e.g.,
URL:, Title:, File:)
-
Write tests in src/__tests__/<name>.test.ts:
- Import from
.. (the barrel)
- Test happy path, error handling, and truncation
- Use
describe/it/expect from bun:test
-
Run bun install from root to link the new workspace
-
Run bun test packages/tools/<name>/ to verify
-
Export from src/index.ts:
export { <Name>Tool, type <Name>ToolOptions } from "./<name>-tool";
Reference
See existing tools for examples:
packages/tools/browser/ -- heavyweight Playwright browser automation
packages/tools/web-fetch/ -- lightweight HTTP fetch + HTML-to-text