mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-15 20:03:05 +00:00
Refactor subagent tool, fix custom tool discovery, fix JSON mode stdout flush
Breaking changes: - Custom tools now require index.ts entry point in subdirectory (e.g., tools/mytool/index.ts instead of tools/mytool.ts) Subagent tool improvements: - Refactored to use Message[] from ai package instead of custom types - Extracted agent discovery to separate agents.ts module - Added parallel mode streaming (shows progress from all tasks) - Added turn count to usage stats footer - Removed redundant Query section from scout output Fixes: - JSON mode stdout flush: Fixed race condition where pi --mode json could exit before all output was written, causing consumers to miss final events Also: - Added signal/timeout support to pi.exec() for custom tools and hooks - Renamed pi-pods bin to avoid conflict with pi
This commit is contained in:
parent
1151975afe
commit
4fb3af93fb
15 changed files with 894 additions and 698 deletions
|
|
@ -22,7 +22,7 @@ See [examples/custom-tools/](../examples/custom-tools/) for working examples.
|
|||
|
||||
## Quick Start
|
||||
|
||||
Create a file `~/.pi/agent/tools/hello.ts`:
|
||||
Create a file `~/.pi/agent/tools/hello/index.ts`:
|
||||
|
||||
```typescript
|
||||
import { Type } from "@sinclair/typebox";
|
||||
|
|
@ -51,13 +51,26 @@ The tool is automatically discovered and available in your next pi session.
|
|||
|
||||
## Tool Locations
|
||||
|
||||
Tools must be in a subdirectory with an `index.ts` entry point:
|
||||
|
||||
| Location | Scope | Auto-discovered |
|
||||
|----------|-------|-----------------|
|
||||
| `~/.pi/agent/tools/*.ts` | Global (all projects) | Yes |
|
||||
| `.pi/tools/*.ts` | Project-local | Yes |
|
||||
| `~/.pi/agent/tools/*/index.ts` | Global (all projects) | Yes |
|
||||
| `.pi/tools/*/index.ts` | Project-local | Yes |
|
||||
| `settings.json` `customTools` array | Configured paths | Yes |
|
||||
| `--tool <path>` CLI flag | One-off/debugging | No |
|
||||
|
||||
**Example structure:**
|
||||
```
|
||||
~/.pi/agent/tools/
|
||||
├── hello/
|
||||
│ └── index.ts # Entry point (auto-discovered)
|
||||
└── complex-tool/
|
||||
├── index.ts # Entry point (auto-discovered)
|
||||
├── helpers.ts # Helper module (not loaded directly)
|
||||
└── types.ts # Type definitions (not loaded directly)
|
||||
```
|
||||
|
||||
**Priority:** Later sources win on name conflicts. CLI `--tool` takes highest priority.
|
||||
|
||||
**Reserved names:** Custom tools cannot use built-in tool names (`read`, `write`, `edit`, `bash`, `grep`, `find`, `ls`).
|
||||
|
|
@ -125,7 +138,7 @@ The factory receives a `ToolAPI` object (named `pi` by convention):
|
|||
```typescript
|
||||
interface ToolAPI {
|
||||
cwd: string; // Current working directory
|
||||
exec(command: string, args: string[]): Promise<ExecResult>;
|
||||
exec(command: string, args: string[], options?: ExecOptions): Promise<ExecResult>;
|
||||
ui: {
|
||||
select(title: string, options: string[]): Promise<string | null>;
|
||||
confirm(title: string, message: string): Promise<boolean>;
|
||||
|
|
@ -134,10 +147,36 @@ interface ToolAPI {
|
|||
};
|
||||
hasUI: boolean; // false in --print or --mode rpc
|
||||
}
|
||||
|
||||
interface ExecOptions {
|
||||
signal?: AbortSignal; // Cancel the process
|
||||
timeout?: number; // Timeout in milliseconds
|
||||
}
|
||||
|
||||
interface ExecResult {
|
||||
stdout: string;
|
||||
stderr: string;
|
||||
code: number;
|
||||
killed?: boolean; // True if process was killed by signal/timeout
|
||||
}
|
||||
```
|
||||
|
||||
Always check `pi.hasUI` before using UI methods.
|
||||
|
||||
### Cancellation Example
|
||||
|
||||
Pass the `signal` from `execute` to `pi.exec` to support cancellation:
|
||||
|
||||
```typescript
|
||||
async execute(toolCallId, params, signal) {
|
||||
const result = await pi.exec("long-running-command", ["arg"], { signal });
|
||||
if (result.killed) {
|
||||
return { content: [{ type: "text", text: "Cancelled" }] };
|
||||
}
|
||||
return { content: [{ type: "text", text: result.stdout }] };
|
||||
}
|
||||
```
|
||||
|
||||
## Session Lifecycle
|
||||
|
||||
Tools can implement `onSession` to react to session changes:
|
||||
|
|
|
|||
|
|
@ -363,15 +363,23 @@ ctx.ui.notify("Operation complete", "info");
|
|||
ctx.ui.notify("Something went wrong", "error");
|
||||
```
|
||||
|
||||
### ctx.exec(command, args)
|
||||
### ctx.exec(command, args, options?)
|
||||
|
||||
Execute a command and get the result.
|
||||
Execute a command and get the result. Supports cancellation via `AbortSignal` and timeout.
|
||||
|
||||
```typescript
|
||||
const result = await ctx.exec("git", ["status"]);
|
||||
// result.stdout: string
|
||||
// result.stderr: string
|
||||
// result.code: number
|
||||
// result.killed?: boolean // True if killed by signal/timeout
|
||||
|
||||
// With timeout (5 seconds)
|
||||
const result = await ctx.exec("slow-command", [], { timeout: 5000 });
|
||||
|
||||
// With abort signal
|
||||
const controller = new AbortController();
|
||||
const result = await ctx.exec("long-command", [], { signal: controller.signal });
|
||||
```
|
||||
|
||||
### ctx.cwd
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue