mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-15 16:04:03 +00:00
- /todos command displays all todos on current branch with custom UI - Update hooks.md to clarify components must not be wrapped in Box/Container - Cross-reference tool and hook in example READMEs
114 lines
3 KiB
Markdown
114 lines
3 KiB
Markdown
# Custom Tools Examples
|
|
|
|
Example custom tools for pi-coding-agent.
|
|
|
|
## Examples
|
|
|
|
Each example uses the `subdirectory/index.ts` structure required for tool discovery.
|
|
|
|
### hello/
|
|
Minimal example showing the basic structure of a custom tool.
|
|
|
|
### question/
|
|
Demonstrates `pi.ui.select()` for asking the user questions with options.
|
|
|
|
### todo/
|
|
Full-featured example demonstrating:
|
|
- `onSession` for state reconstruction from session history
|
|
- Custom `renderCall` and `renderResult`
|
|
- Proper branching support via details storage
|
|
- State management without external files
|
|
|
|
**Companion hook:** [hooks/todo/](../hooks/todo/) adds a `/todos` command for users to view the todo list.
|
|
|
|
### subagent/
|
|
Delegate tasks to specialized subagents with isolated context windows. Includes:
|
|
- `index.ts` - The custom tool (single, parallel, and chain modes)
|
|
- `agents.ts` - Agent discovery helper
|
|
- `agents/` - Sample agent definitions (scout, planner, reviewer, worker)
|
|
- `commands/` - Workflow presets (/implement, /scout-and-plan, /implement-and-review)
|
|
|
|
See [subagent/README.md](subagent/README.md) for full documentation.
|
|
|
|
## Usage
|
|
|
|
```bash
|
|
# Test directly (can point to any .ts file)
|
|
pi --tool examples/custom-tools/todo/index.ts
|
|
|
|
# Or copy entire folder to tools directory for persistent use
|
|
cp -r todo ~/.pi/agent/tools/
|
|
```
|
|
|
|
Then in pi:
|
|
```
|
|
> add a todo "test custom tools"
|
|
> list todos
|
|
> toggle todo #1
|
|
> clear todos
|
|
```
|
|
|
|
## Writing Custom Tools
|
|
|
|
See [docs/custom-tools.md](../../docs/custom-tools.md) for full documentation.
|
|
|
|
### Key Points
|
|
|
|
**Factory pattern:**
|
|
```typescript
|
|
import { Type } from "@sinclair/typebox";
|
|
import { StringEnum } from "@mariozechner/pi-ai";
|
|
import { Text } from "@mariozechner/pi-tui";
|
|
import type { CustomToolFactory } from "@mariozechner/pi-coding-agent";
|
|
|
|
const factory: CustomToolFactory = (pi) => ({
|
|
name: "my_tool",
|
|
label: "My Tool",
|
|
description: "Tool description for LLM",
|
|
parameters: Type.Object({
|
|
action: StringEnum(["list", "add"] as const),
|
|
}),
|
|
|
|
// Called on session start/switch/branch/clear
|
|
onSession(event) {
|
|
// Reconstruct state from event.entries
|
|
},
|
|
|
|
async execute(toolCallId, params) {
|
|
return {
|
|
content: [{ type: "text", text: "Result" }],
|
|
details: { /* for rendering and state reconstruction */ },
|
|
};
|
|
},
|
|
});
|
|
|
|
export default factory;
|
|
```
|
|
|
|
**Custom rendering:**
|
|
```typescript
|
|
renderCall(args, theme) {
|
|
return new Text(
|
|
theme.fg("toolTitle", theme.bold("my_tool ")) + args.action,
|
|
0, 0 // No padding - Box handles it
|
|
);
|
|
},
|
|
|
|
renderResult(result, { expanded, isPartial }, theme) {
|
|
if (isPartial) {
|
|
return new Text(theme.fg("warning", "Working..."), 0, 0);
|
|
}
|
|
return new Text(theme.fg("success", "✓ Done"), 0, 0);
|
|
},
|
|
```
|
|
|
|
**Use StringEnum for string parameters** (required for Google API compatibility):
|
|
```typescript
|
|
import { StringEnum } from "@mariozechner/pi-ai";
|
|
|
|
// Good
|
|
action: StringEnum(["list", "add"] as const)
|
|
|
|
// Bad - doesn't work with Google
|
|
action: Type.Union([Type.Literal("list"), Type.Literal("add")])
|
|
```
|