From 80dac4a6c9fef3d37149cd127e159738c2e04a15 Mon Sep 17 00:00:00 2001 From: can1357 Date: Thu, 23 Apr 2026 23:03:16 +0200 Subject: [PATCH] fix(docs): align skill examples with extension APIs --- docs/skills/authoring-extensions.md | 25 ++++++++++++----- docs/skills/authoring-hooks.md | 27 +++++++------------ .../skills/examples/hello-extension/README.md | 6 ++--- docs/skills/examples/hello-extension/index.ts | 18 +++++++++---- .../{ => .claude-plugin}/marketplace.json | 0 .../examples/mini-marketplace/README.md | 7 ++--- docs/skills/examples/safety-hook/README.md | 2 +- 7 files changed, 48 insertions(+), 37 deletions(-) rename docs/skills/examples/mini-marketplace/{ => .claude-plugin}/marketplace.json (100%) diff --git a/docs/skills/authoring-extensions.md b/docs/skills/authoring-extensions.md index dfb35c052..63f19d3a1 100644 --- a/docs/skills/authoring-extensions.md +++ b/docs/skills/authoring-extensions.md @@ -37,11 +37,19 @@ export default function myExtension(pi: ExtensionAPI) { }); // Slash command: /greet - pi.commands.register("greet", { + pi.registerCommand("greet", { description: "Send a greeting into the conversation", handler: async (args, ctx) => { const name = args.trim() || "world"; - await pi.sendMessage(`Hello, ${name}!`, { triggerTurn: false }); + pi.sendMessage( + { + customType: "greeting", + content: `Hello, ${name}!`, + display: true, + attribution: "user", + }, + { triggerTurn: false } + ); ctx.ui.notify(`Greeted ${name}`, "info"); }, }); @@ -71,8 +79,8 @@ omp discovers extension modules in this order: 1. **Project-scoped auto-discovery** — `/.omp/extensions/` 2. **User-scoped auto-discovery** — `~/.omp/agent/extensions/` -3. **Marketplace-installed plugins** — `~/.claude/plugins/cache/` (extensions shipped inside marketplace plugin packages) -4. **CLI flag** — `omp --extension-path ./my-ext.ts` (also `-e`; `--hook` is treated as an alias) +3. **Marketplace-installed plugins** — `~/.omp/plugins/node_modules/` (extensions shipped inside installed plugin packages) +4. **CLI flag** — `omp --extension ./my-ext.ts` (also `-e`; `--hook` is treated as an alias) 5. **Settings `extensions` array** — paths listed in `~/.omp/agent/config.yml` or `/.omp/settings.json` Within each scope, de-duplication is by resolved absolute path — first seen wins. @@ -120,7 +128,7 @@ Multiple entry points are supported: ## Registering commands ```ts -pi.commands.register("my-cmd", { +pi.registerCommand("my-cmd", { description: "What the command does", handler: async (args, ctx) => { // args: everything the user typed after /my-cmd @@ -178,13 +186,16 @@ pi.registerTool({ ```ts pi.on("tool_call", async (event, ctx) => { // event.toolName, event.input, event.toolCallId - if (event.toolName === "bash" && event.input.command?.includes("rm -rf /")) { + if (event.toolName !== "bash") return; + + const command = String((event.input as { command?: unknown }).command ?? ""); + if (command.includes("rm -rf /")) { return { block: true, reason: "Blocked by safety policy" }; } }); pi.on("turn_end", async (_event, ctx) => { - ctx.ui.setStatus("tokens", `~${ctx.getContextUsage()?.total ?? "?"} tokens`); + ctx.ui.setStatus("tokens", `~${ctx.getContextUsage()?.tokens ?? "?"} tokens`); }); ``` diff --git a/docs/skills/authoring-hooks.md b/docs/skills/authoring-hooks.md index f48aab6a6..d99a99242 100644 --- a/docs/skills/authoring-hooks.md +++ b/docs/skills/authoring-hooks.md @@ -7,7 +7,7 @@ description: Use when creating a new omp hook. Covers HookAPI, event catalog, bl Hooks are event-driven interceptors that run alongside the agent loop. They are best used for cross-cutting concerns: safety policy, secret redaction, context pruning, audit logging. A hook module registers handlers via `pi.on(event, handler)` and can block tool execution, override tool output, or rewrite the message context before each LLM call. -> **Relationship to extensions:** The hook subsystem (`HookAPI`) is the legacy API. The extension runner now handles everything hooks can do plus more. Both share the same event model. Use `ExtensionAPI` for new work; use `HookAPI` only if you are maintaining an existing hook module. +> **Relationship to extensions:** The hook subsystem (`HookAPI`) is the legacy API. The extension runner now handles everything hooks can do plus more. `ExtensionAPI` supports the hook event model plus extension-only events. Use `ExtensionAPI` for new work; use `HookAPI` only if you are maintaining an existing hook module. ## Factory signature @@ -41,9 +41,6 @@ export default function myExtension(pi: ExtensionAPI): void { |---|---|---| | `tool_call` | Before every tool execution | `{ block?: boolean; reason?: string }` | | `tool_result` | After every tool execution | `{ content?; details?; isError?: boolean }` | -| `tool_execution_start` | When tool starts (observability) | — | -| `tool_execution_update` | Streaming tool update | — | -| `tool_execution_end` | When tool finishes | — | ### Session lifecycle @@ -65,13 +62,12 @@ export default function myExtension(pi: ExtensionAPI): void { | Event | Fires | Can return | |---|---|---| -| `before_agent_start` | Before agent starts a turn | `{ message?: { customType; content; display; details }; systemPrompt?: string }` | +| `before_agent_start` | Before agent starts a turn | `{ message?: { customType; content; display; details; attribution? } }` | | `agent_start` | Agent streaming starts | — | | `agent_end` | Agent streaming ends | — | | `turn_start` | Start of a user→agent turn | — | | `turn_end` | End of a user→agent turn | — | | `context` | Before each LLM API call | `{ messages?: Message[] }` | -| `input` | Before user input is submitted | `{ value: string }` | | `auto_compaction_start` | Auto-compaction begins | — | | `auto_compaction_end` | Auto-compaction ends | — | | `auto_retry_start` | Auto-retry begins | — | @@ -79,12 +75,7 @@ export default function myExtension(pi: ExtensionAPI): void { | `ttsr_triggered` | TTSR (too-short response) triggered | — | | `todo_reminder` | Todo reminder fires | — | -### User shell/notebook interception - -| Event | Fires | Can return | -|---|---|---| -| `user_bash` | Before user-initiated bash | `{ result?: BashResult }` | -| `user_python` | Before user-initiated python | `{ result?: PythonResult }` | +Extension-only events such as `tool_execution_start`, `tool_execution_update`, `tool_execution_end`, `input`, `user_bash`, and `user_python` require `ExtensionAPI`. ## Pre-tool blocking contract @@ -129,7 +120,7 @@ omp.on("tool_result", async (event, ctx) => { Contract: -- Handlers run in registration order; each sees the **accumulated** result so far (middleware chain). +- Handlers run in registration order. For `HookAPI`, each handler receives the original tool result event, and the last returned override wins. - `content` replaces the full content array for the LLM. - `details` replaces the structured details object. - `isError` overrides the error flag (typed, but note: `HookToolWrapper` behavior for error path rethrows regardless). @@ -251,13 +242,13 @@ export default function contextFilter(omp: HookAPI): void { |---|---| | `notify(message, type?)` | Show an in-app notification | | `setStatus(key, text)` | Set footer status text (keyed, sorted by key) | -| `select(title, options, opts?)` | Show a selection dialog | -| `confirm(title, message, opts?)` | Show a yes/no dialog | -| `input(title, placeholder?, opts?)` | Show a text input dialog | -| `editor(title, prefill?, opts?)` | Show a multi-line editor | +| `select(title, options)` | Show a selection dialog | +| `confirm(title, message)` | Show a yes/no dialog | +| `input(title, placeholder?)` | Show a text input dialog | +| `editor(title, prefill?, { signal }?)` | Show a multi-line editor | | `setEditorText(text)` | Set the input editor content | | `getEditorText()` | Get current input editor content | -| `custom(factory, opts?)` | Render a custom TUI component | +| `custom(factory)` | Render a custom TUI component | | `theme` | Current theme object | `ctx.hasUI` is `false` in headless/print/subagent mode — always guard interactive calls. diff --git a/docs/skills/examples/hello-extension/README.md b/docs/skills/examples/hello-extension/README.md index 982dbcc15..8c1991c86 100644 --- a/docs/skills/examples/hello-extension/README.md +++ b/docs/skills/examples/hello-extension/README.md @@ -1,6 +1,6 @@ # hello-extension -A minimal `oh-my-pi` extension that demonstrates the two most common authoring patterns: subscribing to `session_start` to log a greeting on load, and registering a `/hello` slash command that sends a notification to the user. It is intentionally small — use it as a copy-paste starting point for your own extension. +A minimal `oh-my-pi` extension that demonstrates the two most common authoring patterns: subscribing to `session_start` to notify on load, and registering a `/hello` slash command that sends a greeting into the conversation. It is intentionally small — use it as a copy-paste starting point for your own extension. ## Install @@ -23,7 +23,7 @@ extensions: **Option C — load once via CLI flag:** ``` -omp --extension-path ./hello-extension +omp --extension ./hello-extension ``` ## Usage @@ -34,6 +34,6 @@ After loading, type `/hello` in the omp prompt to trigger the notification. - Default export factory receiving `ExtensionAPI` - `pi.on("session_start", ...)` — session lifecycle hook -- `pi.commands.register(...)` — slash command registration +- `pi.registerCommand(...)` — slash command registration - `ctx.ui.notify(...)` — user-facing notification - `package.json` with `omp.extensions` manifest field diff --git a/docs/skills/examples/hello-extension/index.ts b/docs/skills/examples/hello-extension/index.ts index 22c6cce13..db80b0721 100644 --- a/docs/skills/examples/hello-extension/index.ts +++ b/docs/skills/examples/hello-extension/index.ts @@ -2,17 +2,25 @@ import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent"; export default function helloExtension(pi: ExtensionAPI) { - // Log a greeting to the console whenever a session starts. + // Show a greeting whenever a session starts. pi.on("session_start", async (_event, ctx) => { - console.log("[hello-extension] session started in", ctx.cwd); ctx.ui.notify("Hello from hello-extension!", "info"); }); // Register a /hello slash command that sends a greeting into the conversation. - pi.commands.register("hello", { + pi.registerCommand("hello", { description: "Send a greeting into the conversation", - handler: async (_args, ctx) => { - await pi.sendMessage("Hello from my extension!", { triggerTurn: false }); + handler: async (args, ctx) => { + const name = args.trim() || "there"; + pi.sendMessage( + { + customType: "hello-extension", + content: `Hello, ${name}!`, + display: true, + attribution: "user", + }, + { triggerTurn: false } + ); ctx.ui.notify("Message sent!", "info"); }, }); diff --git a/docs/skills/examples/mini-marketplace/marketplace.json b/docs/skills/examples/mini-marketplace/.claude-plugin/marketplace.json similarity index 100% rename from docs/skills/examples/mini-marketplace/marketplace.json rename to docs/skills/examples/mini-marketplace/.claude-plugin/marketplace.json diff --git a/docs/skills/examples/mini-marketplace/README.md b/docs/skills/examples/mini-marketplace/README.md index 4b58c0162..147e9f8a9 100644 --- a/docs/skills/examples/mini-marketplace/README.md +++ b/docs/skills/examples/mini-marketplace/README.md @@ -1,6 +1,6 @@ # mini-marketplace -A minimal `oh-my-pi` marketplace catalog that demonstrates the `marketplace.json` format. It lists one plugin (`hello-extension`) using a relative path source. +A minimal `oh-my-pi` marketplace catalog that demonstrates the `marketplace.json` format. It lists one plugin (`my-plugin`) using a relative path source. ## Install command @@ -26,11 +26,12 @@ omp plugin install my-plugin@example-marketplace ``` mini-marketplace/ - marketplace.json ← catalog (normally at .claude-plugin/marketplace.json in a real repo) + .claude-plugin/ + marketplace.json ← catalog README.md my-plugin/ package.json ← omp.extensions manifest index.ts ← extension entry point ``` -In a real published marketplace, `marketplace.json` lives at `.claude-plugin/marketplace.json` inside the Git repository root. For this local example it is at the directory root so you can point `/marketplace add` directly at this folder. +Published and local marketplaces use the same catalog location: `.claude-plugin/marketplace.json` inside the marketplace root. Point `/marketplace add` at this folder to load the example. diff --git a/docs/skills/examples/safety-hook/README.md b/docs/skills/examples/safety-hook/README.md index bd3a1aa9f..29cd70480 100644 --- a/docs/skills/examples/safety-hook/README.md +++ b/docs/skills/examples/safety-hook/README.md @@ -19,7 +19,7 @@ Restart `omp`. The hook is active for all sessions. Or load once: ``` -omp --extension-path ./safety-hook +omp --extension ./safety-hook ``` ## How it works