docs: update docs
This commit is contained in:
+58
-49
@@ -25,14 +25,17 @@ So this file documents the hook subsystem implementation itself (types/loader/ru
|
||||
A hook module must default-export a factory:
|
||||
|
||||
```ts
|
||||
import type { HookAPI } from "@oh-my-pi/pi-coding-agent/hooks";
|
||||
import type { HookAPI } from "@oh-my-pi/pi-coding-agent/extensibility/hooks";
|
||||
|
||||
export default function hook(pi: HookAPI): void {
|
||||
pi.on("tool_call", async (event, ctx) => {
|
||||
if (event.toolName === "bash" && String(event.input.command ?? "").includes("rm -rf")) {
|
||||
return { block: true, reason: "blocked by policy" };
|
||||
}
|
||||
});
|
||||
pi.on("tool_call", async (event, ctx) => {
|
||||
if (
|
||||
event.toolName === "bash" &&
|
||||
String(event.input.command ?? "").includes("rm -rf")
|
||||
) {
|
||||
return { block: true, reason: "blocked by policy" };
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
@@ -126,7 +129,6 @@ tool_call handlers
|
||||
└─ error ──> emit tool_result(isError=true) then rethrow original error
|
||||
```
|
||||
|
||||
|
||||
## Execution model and mutation semantics
|
||||
|
||||
### 1) Pre-execution: `tool_call`
|
||||
@@ -252,85 +254,92 @@ Hook status text set via `ctx.ui.setStatus(key, text)` is:
|
||||
### Block unsafe bash commands
|
||||
|
||||
```ts
|
||||
import type { HookAPI } from "@oh-my-pi/pi-coding-agent/hooks";
|
||||
import type { HookAPI } from "@oh-my-pi/pi-coding-agent/extensibility/hooks";
|
||||
|
||||
export default function (pi: HookAPI): void {
|
||||
pi.on("tool_call", async (event, ctx) => {
|
||||
if (event.toolName !== "bash") return;
|
||||
const cmd = String(event.input.command ?? "");
|
||||
if (!cmd.includes("rm -rf")) return;
|
||||
pi.on("tool_call", async (event, ctx) => {
|
||||
if (event.toolName !== "bash") return;
|
||||
const cmd = String(event.input.command ?? "");
|
||||
if (!cmd.includes("rm -rf")) return;
|
||||
|
||||
if (!ctx.hasUI) return { block: true, reason: "rm -rf blocked (no UI)" };
|
||||
const ok = await ctx.ui.confirm("Dangerous command", `Allow: ${cmd}`);
|
||||
if (!ok) return { block: true, reason: "user denied command" };
|
||||
});
|
||||
if (!ctx.hasUI) return { block: true, reason: "rm -rf blocked (no UI)" };
|
||||
const ok = await ctx.ui.confirm("Dangerous command", `Allow: ${cmd}`);
|
||||
if (!ok) return { block: true, reason: "user denied command" };
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Redact tool output on post-execution
|
||||
|
||||
```ts
|
||||
import type { HookAPI } from "@oh-my-pi/pi-coding-agent/hooks";
|
||||
import type { HookAPI } from "@oh-my-pi/pi-coding-agent/extensibility/hooks";
|
||||
|
||||
export default function (pi: HookAPI): void {
|
||||
pi.on("tool_result", async event => {
|
||||
if (event.toolName !== "read" || event.isError) return;
|
||||
pi.on("tool_result", async (event) => {
|
||||
if (event.toolName !== "read" || event.isError) return;
|
||||
|
||||
const redacted = event.content.map(chunk => {
|
||||
if (chunk.type !== "text") return chunk;
|
||||
return { ...chunk, text: chunk.text.replaceAll(/API_KEY=\S+/g, "API_KEY=[REDACTED]") };
|
||||
});
|
||||
const redacted = event.content.map((chunk) => {
|
||||
if (chunk.type !== "text") return chunk;
|
||||
return {
|
||||
...chunk,
|
||||
text: chunk.text.replaceAll(/API_KEY=\S+/g, "API_KEY=[REDACTED]"),
|
||||
};
|
||||
});
|
||||
|
||||
return { content: redacted };
|
||||
});
|
||||
return { content: redacted };
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Modify model context per LLM call
|
||||
|
||||
```ts
|
||||
import type { HookAPI } from "@oh-my-pi/pi-coding-agent/hooks";
|
||||
import type { HookAPI } from "@oh-my-pi/pi-coding-agent/extensibility/hooks";
|
||||
|
||||
export default function (pi: HookAPI): void {
|
||||
pi.on("context", async event => {
|
||||
const filtered = event.messages.filter(msg => !(msg.role === "custom" && msg.customType === "debug-only"));
|
||||
return { messages: filtered };
|
||||
});
|
||||
pi.on("context", async (event) => {
|
||||
const filtered = event.messages.filter(
|
||||
(msg) => !(msg.role === "custom" && msg.customType === "debug-only"),
|
||||
);
|
||||
return { messages: filtered };
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Register slash command with command-safe context methods
|
||||
|
||||
```ts
|
||||
import type { HookAPI } from "@oh-my-pi/pi-coding-agent/hooks";
|
||||
import type { HookAPI } from "@oh-my-pi/pi-coding-agent/extensibility/hooks";
|
||||
|
||||
export default function (pi: HookAPI): void {
|
||||
pi.registerCommand("handoff", {
|
||||
description: "Create a new session with setup message",
|
||||
handler: async (_args, ctx) => {
|
||||
await ctx.waitForIdle();
|
||||
await ctx.newSession({
|
||||
parentSession: ctx.sessionManager.getSessionFile(),
|
||||
setup: async sm => {
|
||||
sm.appendMessage({
|
||||
role: "user",
|
||||
content: [{ type: "text", text: "Continue from prior session summary." }],
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
pi.registerCommand("handoff", {
|
||||
description: "Create a new session with setup message",
|
||||
handler: async (_args, ctx) => {
|
||||
await ctx.waitForIdle();
|
||||
await ctx.newSession({
|
||||
parentSession: ctx.sessionManager.getSessionFile(),
|
||||
setup: async (sm) => {
|
||||
sm.appendMessage({
|
||||
role: "user",
|
||||
content: [
|
||||
{ type: "text", text: "Continue from prior session summary." },
|
||||
],
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## Export surface
|
||||
|
||||
`src/extensibility/hooks/index.ts` exports:
|
||||
`src/extensibility/hooks/index.ts` and the package subpath `@oh-my-pi/pi-coding-agent/extensibility/hooks` export:
|
||||
|
||||
- loading APIs (`discoverAndLoadHooks`, `loadHooks`)
|
||||
- runner and wrapper (`HookRunner`, `HookToolWrapper`)
|
||||
- all hook types
|
||||
- `execCommand` re-export
|
||||
|
||||
And package root (`src/index.ts`) re-exports hook **types** as a legacy compatibility surface.
|
||||
The package root (`@oh-my-pi/pi-coding-agent`) does not re-export `HookAPI`; import legacy hook types from the hooks subpath.
|
||||
|
||||
Reference in New Issue
Block a user