mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-15 12:03:49 +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 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))
|
||||
- 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))
|
||||
|
|
|
|||
|
|
@ -1237,6 +1237,20 @@ pi.registerTool({
|
|||
|
||||
**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.
|
||||
|
||||
### Output Truncation
|
||||
|
|
@ -1777,4 +1791,5 @@ All examples in [examples/extensions/](../examples/extensions/).
|
|||
| **Misc** |||
|
||||
| `antigravity-image-gen.ts` | Image generation tool | `registerTool`, Google Antigravity |
|
||||
| `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` |
|
||||
|
|
|
|||
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;
|
||||
signal?: AbortSignal;
|
||||
timeout?: number;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
},
|
||||
) => Promise<{ exitCode: number | null }>;
|
||||
}
|
||||
|
|
@ -55,7 +56,7 @@ export interface BashOperations {
|
|||
* Default bash operations using local shell
|
||||
*/
|
||||
const defaultBashOperations: BashOperations = {
|
||||
exec: (command, cwd, { onData, signal, timeout }) => {
|
||||
exec: (command, cwd, { onData, signal, timeout, env }) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const { shell, args } = getShellConfig();
|
||||
|
||||
|
|
@ -67,7 +68,7 @@ const defaultBashOperations: BashOperations = {
|
|||
const child = spawn(shell, [...args, command], {
|
||||
cwd,
|
||||
detached: true,
|
||||
env: getShellEnv(),
|
||||
env: env ?? getShellEnv(),
|
||||
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 {
|
||||
/** Custom operations for command execution. Default: local shell */
|
||||
operations?: BashOperations;
|
||||
/** Command prefix prepended to every command (e.g., "shopt -s expand_aliases" for alias support) */
|
||||
commandPrefix?: string;
|
||||
/** Hook to adjust command, cwd, or env before execution */
|
||||
spawnHook?: BashSpawnHook;
|
||||
}
|
||||
|
||||
export function createBashTool(cwd: string, options?: BashToolOptions): AgentTool<typeof bashSchema> {
|
||||
const ops = options?.operations ?? defaultBashOperations;
|
||||
const commandPrefix = options?.commandPrefix;
|
||||
const spawnHook = options?.spawnHook;
|
||||
|
||||
return {
|
||||
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)
|
||||
const resolvedCommand = commandPrefix ? `${commandPrefix}\n${command}` : command;
|
||||
const spawnContext = resolveSpawnContext(resolvedCommand, cwd, spawnHook);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
// 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 }) => {
|
||||
// Close temp file stream
|
||||
if (tempFileStream) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
export {
|
||||
type BashOperations,
|
||||
type BashSpawnContext,
|
||||
type BashSpawnHook,
|
||||
type BashToolDetails,
|
||||
type BashToolInput,
|
||||
type BashToolOptions,
|
||||
|
|
|
|||
|
|
@ -204,6 +204,8 @@ export {
|
|||
// Tools
|
||||
export {
|
||||
type BashOperations,
|
||||
type BashSpawnContext,
|
||||
type BashSpawnHook,
|
||||
type BashToolDetails,
|
||||
type BashToolInput,
|
||||
type BashToolOptions,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue