mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-18 18:03:44 +00:00
feat(coding-agent): add bash spawn hook
This commit is contained in:
parent
25fa1fafde
commit
86b43c8eac
6 changed files with 81 additions and 3 deletions
|
|
@ -12,6 +12,7 @@
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Added Android/Termux support with graceful clipboard fallback ([#1164](https://github.com/badlogic/pi-mono/issues/1164))
|
- Added Android/Termux support with graceful clipboard fallback ([#1164](https://github.com/badlogic/pi-mono/issues/1164))
|
||||||
|
- Added bash tool spawn hook support for adjusting command, cwd, and env before execution ([#1160](https://github.com/badlogic/pi-mono/pull/1160) by [@mitsuhiko](https://github.com/mitsuhiko))
|
||||||
- Added typed `ToolCallEvent.input` per tool with `isToolCallEventType()` type guard for narrowing built-in tool events ([#1147](https://github.com/badlogic/pi-mono/pull/1147) by [@giuseppeg](https://github.com/giuseppeg))
|
- Added typed `ToolCallEvent.input` per tool with `isToolCallEventType()` type guard for narrowing built-in tool events ([#1147](https://github.com/badlogic/pi-mono/pull/1147) by [@giuseppeg](https://github.com/giuseppeg))
|
||||||
- Exported `discoverAndLoadExtensions` from package to enable extension testing without a local repo clone ([#1148](https://github.com/badlogic/pi-mono/issues/1148))
|
- Exported `discoverAndLoadExtensions` from package to enable extension testing without a local repo clone ([#1148](https://github.com/badlogic/pi-mono/issues/1148))
|
||||||
- Added Extension UI Protocol documentation to RPC docs covering all request/response types for extension dialogs and notifications ([#1144](https://github.com/badlogic/pi-mono/pull/1144) by [@aliou](https://github.com/aliou))
|
- Added Extension UI Protocol documentation to RPC docs covering all request/response types for extension dialogs and notifications ([#1144](https://github.com/badlogic/pi-mono/pull/1144) by [@aliou](https://github.com/aliou))
|
||||||
|
|
|
||||||
|
|
@ -1237,6 +1237,20 @@ pi.registerTool({
|
||||||
|
|
||||||
**Operations interfaces:** `ReadOperations`, `WriteOperations`, `EditOperations`, `BashOperations`, `LsOperations`, `GrepOperations`, `FindOperations`
|
**Operations interfaces:** `ReadOperations`, `WriteOperations`, `EditOperations`, `BashOperations`, `LsOperations`, `GrepOperations`, `FindOperations`
|
||||||
|
|
||||||
|
The bash tool also supports a spawn hook to adjust the command, cwd, or env before execution:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { createBashTool } from "@mariozechner/pi-coding-agent";
|
||||||
|
|
||||||
|
const bashTool = createBashTool(cwd, {
|
||||||
|
spawnHook: ({ command, cwd, env }) => ({
|
||||||
|
command: `source ~/.profile\n${command}`,
|
||||||
|
cwd: `/mnt/sandbox${cwd}`,
|
||||||
|
env: { ...env, CI: "1" },
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
See [examples/extensions/ssh.ts](../examples/extensions/ssh.ts) for a complete SSH example with `--ssh` flag.
|
See [examples/extensions/ssh.ts](../examples/extensions/ssh.ts) for a complete SSH example with `--ssh` flag.
|
||||||
|
|
||||||
### Output Truncation
|
### Output Truncation
|
||||||
|
|
@ -1777,4 +1791,5 @@ All examples in [examples/extensions/](../examples/extensions/).
|
||||||
| **Misc** |||
|
| **Misc** |||
|
||||||
| `antigravity-image-gen.ts` | Image generation tool | `registerTool`, Google Antigravity |
|
| `antigravity-image-gen.ts` | Image generation tool | `registerTool`, Google Antigravity |
|
||||||
| `inline-bash.ts` | Inline bash in tool calls | `on("tool_call")` |
|
| `inline-bash.ts` | Inline bash in tool calls | `on("tool_call")` |
|
||||||
|
| `bash-spawn-hook.ts` | Adjust bash command, cwd, and env before execution | `createBashTool`, `spawnHook` |
|
||||||
| `with-deps/` | Extension with npm dependencies | Package structure with `package.json` |
|
| `with-deps/` | Extension with npm dependencies | Package structure with `package.json` |
|
||||||
|
|
|
||||||
30
packages/coding-agent/examples/extensions/bash-spawn-hook.ts
Normal file
30
packages/coding-agent/examples/extensions/bash-spawn-hook.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
/**
|
||||||
|
* Bash Spawn Hook Example
|
||||||
|
*
|
||||||
|
* Adjusts command, cwd, and env before execution.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* pi -e ./bash-spawn-hook.ts
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
||||||
|
import { createBashTool } from "@mariozechner/pi-coding-agent";
|
||||||
|
|
||||||
|
export default function (pi: ExtensionAPI) {
|
||||||
|
const cwd = process.cwd();
|
||||||
|
|
||||||
|
const bashTool = createBashTool(cwd, {
|
||||||
|
spawnHook: ({ command, cwd, env }) => ({
|
||||||
|
command: `source ~/.profile\n${command}`,
|
||||||
|
cwd,
|
||||||
|
env: { ...env, PI_SPAWN_HOOK: "1" },
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
pi.registerTool({
|
||||||
|
...bashTool,
|
||||||
|
execute: async (id, params, onUpdate, _ctx, signal) => {
|
||||||
|
return bashTool.execute(id, params, signal, onUpdate);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -47,6 +47,7 @@ export interface BashOperations {
|
||||||
onData: (data: Buffer) => void;
|
onData: (data: Buffer) => void;
|
||||||
signal?: AbortSignal;
|
signal?: AbortSignal;
|
||||||
timeout?: number;
|
timeout?: number;
|
||||||
|
env?: NodeJS.ProcessEnv;
|
||||||
},
|
},
|
||||||
) => Promise<{ exitCode: number | null }>;
|
) => Promise<{ exitCode: number | null }>;
|
||||||
}
|
}
|
||||||
|
|
@ -55,7 +56,7 @@ export interface BashOperations {
|
||||||
* Default bash operations using local shell
|
* Default bash operations using local shell
|
||||||
*/
|
*/
|
||||||
const defaultBashOperations: BashOperations = {
|
const defaultBashOperations: BashOperations = {
|
||||||
exec: (command, cwd, { onData, signal, timeout }) => {
|
exec: (command, cwd, { onData, signal, timeout, env }) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const { shell, args } = getShellConfig();
|
const { shell, args } = getShellConfig();
|
||||||
|
|
||||||
|
|
@ -67,7 +68,7 @@ const defaultBashOperations: BashOperations = {
|
||||||
const child = spawn(shell, [...args, command], {
|
const child = spawn(shell, [...args, command], {
|
||||||
cwd,
|
cwd,
|
||||||
detached: true,
|
detached: true,
|
||||||
env: getShellEnv(),
|
env: env ?? getShellEnv(),
|
||||||
stdio: ["ignore", "pipe", "pipe"],
|
stdio: ["ignore", "pipe", "pipe"],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -135,16 +136,37 @@ const defaultBashOperations: BashOperations = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface BashSpawnContext {
|
||||||
|
command: string;
|
||||||
|
cwd: string;
|
||||||
|
env: NodeJS.ProcessEnv;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type BashSpawnHook = (context: BashSpawnContext) => BashSpawnContext;
|
||||||
|
|
||||||
|
function resolveSpawnContext(command: string, cwd: string, spawnHook?: BashSpawnHook): BashSpawnContext {
|
||||||
|
const baseContext: BashSpawnContext = {
|
||||||
|
command,
|
||||||
|
cwd,
|
||||||
|
env: { ...getShellEnv() },
|
||||||
|
};
|
||||||
|
|
||||||
|
return spawnHook ? spawnHook(baseContext) : baseContext;
|
||||||
|
}
|
||||||
|
|
||||||
export interface BashToolOptions {
|
export interface BashToolOptions {
|
||||||
/** Custom operations for command execution. Default: local shell */
|
/** Custom operations for command execution. Default: local shell */
|
||||||
operations?: BashOperations;
|
operations?: BashOperations;
|
||||||
/** Command prefix prepended to every command (e.g., "shopt -s expand_aliases" for alias support) */
|
/** Command prefix prepended to every command (e.g., "shopt -s expand_aliases" for alias support) */
|
||||||
commandPrefix?: string;
|
commandPrefix?: string;
|
||||||
|
/** Hook to adjust command, cwd, or env before execution */
|
||||||
|
spawnHook?: BashSpawnHook;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createBashTool(cwd: string, options?: BashToolOptions): AgentTool<typeof bashSchema> {
|
export function createBashTool(cwd: string, options?: BashToolOptions): AgentTool<typeof bashSchema> {
|
||||||
const ops = options?.operations ?? defaultBashOperations;
|
const ops = options?.operations ?? defaultBashOperations;
|
||||||
const commandPrefix = options?.commandPrefix;
|
const commandPrefix = options?.commandPrefix;
|
||||||
|
const spawnHook = options?.spawnHook;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: "bash",
|
name: "bash",
|
||||||
|
|
@ -159,6 +181,7 @@ export function createBashTool(cwd: string, options?: BashToolOptions): AgentToo
|
||||||
) => {
|
) => {
|
||||||
// Apply command prefix if configured (e.g., "shopt -s expand_aliases" for alias support)
|
// Apply command prefix if configured (e.g., "shopt -s expand_aliases" for alias support)
|
||||||
const resolvedCommand = commandPrefix ? `${commandPrefix}\n${command}` : command;
|
const resolvedCommand = commandPrefix ? `${commandPrefix}\n${command}` : command;
|
||||||
|
const spawnContext = resolveSpawnContext(resolvedCommand, cwd, spawnHook);
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// We'll stream to a temp file if output gets large
|
// We'll stream to a temp file if output gets large
|
||||||
|
|
@ -215,7 +238,12 @@ export function createBashTool(cwd: string, options?: BashToolOptions): AgentToo
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ops.exec(resolvedCommand, cwd, { onData: handleData, signal, timeout })
|
ops.exec(spawnContext.command, spawnContext.cwd, {
|
||||||
|
onData: handleData,
|
||||||
|
signal,
|
||||||
|
timeout,
|
||||||
|
env: spawnContext.env,
|
||||||
|
})
|
||||||
.then(({ exitCode }) => {
|
.then(({ exitCode }) => {
|
||||||
// Close temp file stream
|
// Close temp file stream
|
||||||
if (tempFileStream) {
|
if (tempFileStream) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
export {
|
export {
|
||||||
type BashOperations,
|
type BashOperations,
|
||||||
|
type BashSpawnContext,
|
||||||
|
type BashSpawnHook,
|
||||||
type BashToolDetails,
|
type BashToolDetails,
|
||||||
type BashToolInput,
|
type BashToolInput,
|
||||||
type BashToolOptions,
|
type BashToolOptions,
|
||||||
|
|
|
||||||
|
|
@ -204,6 +204,8 @@ export {
|
||||||
// Tools
|
// Tools
|
||||||
export {
|
export {
|
||||||
type BashOperations,
|
type BashOperations,
|
||||||
|
type BashSpawnContext,
|
||||||
|
type BashSpawnHook,
|
||||||
type BashToolDetails,
|
type BashToolDetails,
|
||||||
type BashToolInput,
|
type BashToolInput,
|
||||||
type BashToolOptions,
|
type BashToolOptions,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue