diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index ea5a5ea5..1e3878e5 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -168,7 +168,7 @@ The `before_compact` and `before_tree` hook events allow custom compaction imple - Search by typing, page with ←/→ - Filter modes (Ctrl+O): default → no-tools → user-only → labeled-only → all - Press `l` to label entries as bookmarks -- Selecting a branch generates a summary and switches context +- Selecting a branch switches context and optionally injects a summary of the abandoned branch **Entry labels:** - Bookmark any entry via `/tree` → select → `l` diff --git a/packages/coding-agent/docs/hooks.md b/packages/coding-agent/docs/hooks.md index dba42e0b..51629704 100644 --- a/packages/coding-agent/docs/hooks.md +++ b/packages/coding-agent/docs/hooks.md @@ -510,7 +510,7 @@ Subscribe to events. See [Events](#events) for all event types. ### pi.sendMessage(message, triggerTurn?) -Inject a message into the session. Creates `CustomMessageEntry` (participates in LLM context). +Inject a message into the session. Creates a `CustomMessageEntry` that participates in the LLM context. ```typescript pi.sendMessage({ @@ -521,6 +521,20 @@ pi.sendMessage({ }, triggerTurn); // If true, triggers LLM response ``` +**Storage and timing:** +- The message is appended to the session file immediately as a `CustomMessageEntry` +- If the agent is currently streaming, the message is queued and appended after the current turn +- If `triggerTurn` is true and the agent is idle, a new agent loop starts + +**LLM context:** +- `CustomMessageEntry` is converted to a user message when building context for the LLM +- Only `content` is sent to the LLM; `details` is for rendering/state only + +**TUI display:** +- If `display: true`, the message appears in the chat with purple styling (customMessageBg, customMessageText, customMessageLabel theme colors) +- If `display: false`, the message is hidden from the TUI but still sent to the LLM +- Use `pi.registerMessageRenderer()` to customize how your messages render (see below) + ### pi.appendEntry(customType, data?) Persist hook state. Creates `CustomEntry` (does NOT participate in LLM context). @@ -558,18 +572,36 @@ To trigger LLM after command, call `pi.sendMessage(..., true)`. ### pi.registerMessageRenderer(customType, renderer) -Custom TUI rendering for `CustomMessageEntry`: +Register a custom TUI renderer for `CustomMessageEntry` messages with your `customType`. Without a custom renderer, messages display with default purple styling showing the content as-is. ```typescript import { Text } from "@mariozechner/pi-tui"; pi.registerMessageRenderer("my-hook", (message, options, theme) => { - // message.content, message.details - // options.expanded (user pressed Ctrl+O) - return new Text(theme.fg("accent", `[MY-HOOK] ${message.content}`), 0, 0); + // message.content - the message content (string or content array) + // message.details - your custom metadata + // options.expanded - true if user pressed Ctrl+O + + const prefix = theme.fg("accent", `[${message.details?.label ?? "INFO"}] `); + const text = typeof message.content === "string" + ? message.content + : message.content.map(c => c.type === "text" ? c.text : "[image]").join(""); + + return new Text(prefix + theme.fg("text", text), 0, 0); }); ``` +**Renderer signature:** +```typescript +type HookMessageRenderer = ( + message: CustomMessageEntry, + options: { expanded: boolean }, + theme: Theme +) => Component | null; +``` + +Return `null` to use default rendering. The returned component is wrapped in a styled Box by the TUI. See [tui.md](tui.md) for component details. + ### pi.exec(command, args, options?) Execute a shell command: