co-mono/packages/coding-agent/examples/hooks/file-trigger.ts
Mario Zechner ba185b0571 Hook API: replace send() with sendMessage(), add appendEntry() and registerCommand()
Breaking changes to Hook API:
- pi.send(text, attachments?) replaced with pi.sendMessage(message, triggerTurn?)
  - Creates CustomMessageEntry instead of user messages
  - Properly handles queuing during streaming via agent loop
  - Supports optional turn triggering when idle
- New pi.appendEntry(customType, data?) for hook state persistence
- New pi.registerCommand(name, options) for custom slash commands
- Handler types renamed: SendHandler -> SendMessageHandler, new AppendEntryHandler

Implementation:
- AgentSession.sendHookMessage() handles all three cases:
  - Streaming: queues message with _hookData marker, agent loop processes it
  - Not streaming + triggerTurn: appends to state/session, calls agent.continue()
  - Not streaming + no trigger: appends to state/session only
- message_end handler routes based on _hookData presence to correct persistence
- HookRunner gains getRegisteredCommands() and getCommand() methods

New types: HookMessage<T>, RegisteredCommand, CommandContext
2025-12-30 22:42:18 +01:00

43 lines
1 KiB
TypeScript

/**
* File Trigger Hook
*
* Watches a trigger file and injects its contents into the conversation.
* Useful for external systems to send messages to the agent.
*
* Usage:
* echo "Run the tests" > /tmp/agent-trigger.txt
*/
import * as fs from "node:fs";
import type { HookAPI } from "@mariozechner/pi-coding-agent/hooks";
export default function (pi: HookAPI) {
pi.on("session", async (event, ctx) => {
if (event.reason !== "start") return;
const triggerFile = "/tmp/agent-trigger.txt";
fs.watch(triggerFile, () => {
try {
const content = fs.readFileSync(triggerFile, "utf-8").trim();
if (content) {
pi.sendMessage(
{
customType: "file-trigger",
content: `External trigger: ${content}`,
display: true,
},
true, // triggerTurn - get LLM to respond
);
fs.writeFileSync(triggerFile, ""); // Clear after reading
}
} catch {
// File might not exist yet
}
});
if (ctx.hasUI) {
ctx.ui.notify(`Watching ${triggerFile}`, "info");
}
});
}