Create container plugin agent hooks
Skill yukihirop/nagi/.claude/skills/create-container-plugin-agent-hooks
Scaffold a new agent-hooks plugin for nagi containers. Generates index.mjs with hook factories and deploy/templates/container/claude-code/entry.template.ts registration. Triggers on "create agent hooks plugin", "new agent hooks", "scaffold agent hooks".From its SKILL.md
npx -y skills add yukihirop/nagi --skill create-container-plugin-agent-hooksAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
6.8 KB, ~1.6k tokens by cl100k_base, as published. Nobody here has run it
Create Agent-Hooks Plugin
Step 0: Language selection
Before proceeding with any other steps in this skill, ask the user which language to continue in using AskUserQuestion. Keep this initial prompt in English because the preferred language is not yet known.
- Question:
Which language should I continue in? - Options:
English,日本語 (Japanese)
Use the selected language for all subsequent user-facing messages and for every further AskUserQuestion prompt in this skill. Do not translate code, file paths, shell commands, or file contents.
Scaffold a new agent-hooks plugin that runs inside agent containers and sends notifications to chat channels via IPC, following the established pattern (agent-hooks).
UX Note: Use AskUserQuestion for all user-facing questions.
Step 1: Gather information
AskUserQuestion:
- Plugin name (lowercase — e.g., "open-code", "cursor", "windsurf")
- One-line description (e.g., "Tool execution and session notifications for Open Code")
- Which hook types to support? (PostToolUse, SessionStart, or both)
The full plugin name will be agent-hooks-{name}.
Step 2: Choose target agent
AskUserQuestion: Which agent should this plugin be created for?
- Claude Code —
container/claude-code/plugins/agent-hooks-{name}/ - Open Code —
container/open-code/plugins/agent-hooks-{name}/ - Both — Create in both
Step 3: Generate plugin
Create the plugin in the selected directory with a single file:
index.mjs
Agent-hooks plugins are pure JavaScript ES Modules — no TypeScript, no build step, no package.json.
Generate from this template:
/**
* Agent Hooks: {Name}
* {description}
*/
import fs from "node:fs";
import path from "node:path";
const MESSAGES_DIR = "/workspace/ipc/messages";
function writeIpcMessage(chatJid, groupFolder, text) {
fs.mkdirSync(MESSAGES_DIR, { recursive: true });
const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}.json`;
const filepath = path.join(MESSAGES_DIR, filename);
const tempPath = `${filepath}.tmp`;
fs.writeFileSync(tempPath, JSON.stringify({
type: "message",
chatJid,
text,
groupFolder,
timestamp: new Date().toISOString(),
}));
fs.renameSync(tempPath, filepath);
}
If PostToolUse is supported, add:
const DEFAULT_SKIP_TOOLS = ["mcp__nagi__send_message", "mcp__nagi__list_tasks"];
export function createPostToolUseHook(chatJid, groupFolder, extraSkipTools, log) {
const skipTools = new Set([...DEFAULT_SKIP_TOOLS, ...(extraSkipTools ?? [])]);
return async (input) => {
try {
const name = input.tool_name;
log(`[hook:PostToolUse] tool=${name} chatJid=${chatJid}`);
if (!name || !chatJid || skipTools.has(name)) return {};
// TODO: Customize tool display format for {name}
const text = `\u{2699}\uFE0F \`${name}\``;
writeIpcMessage(chatJid, groupFolder, text);
log(`[hook:PostToolUse] sent: ${text}`);
} catch (err) {
log(`[hook:PostToolUse] error: ${err}`);
}
return {};
};
}
If SessionStart is supported, add:
export function createSessionStartHook(chatJid, groupFolder, log) {
return async (input) => {
try {
log(`[hook:SessionStart] chatJid=${chatJid} source=${input?.source}`);
if (!chatJid) return {};
writeIpcMessage(chatJid, groupFolder, "\u{1F4AD} Thinking...");
log("[hook:SessionStart] sent thinking message");
} catch (err) {
log(`[hook:SessionStart] error: ${err}`);
}
return {};
};
}
Replace {name}, {Name}, {description} placeholders.
Step 4: Add to container entry.template.ts
AskUserQuestion: Which agent's entry.template.ts should register this plugin?
- Claude Code —
deploy/templates/container/claude-code/entry.template.ts - Open Code —
deploy/templates/container/open-code/entry.template.ts(create if missing) - Both — Add to both
Add a new try/catch block after any existing agent-hooks blocks:
try {
const pluginPath = "/app/agent-plugins/agent-hooks-{name}/index.mjs";
const agentHooks = await import(/* webpackIgnore: true */ pluginPath);
plugins.push({
name: "agent-hooks-{name}",
createHooks: (
chatJid: string,
groupFolder: string,
hooksConfig: { postToolUse?: boolean; sessionStart?: boolean; skipTools?: string[] } | undefined,
log: (msg: string) => void,
) => ({
// Include PostToolUse if supported:
...(hooksConfig?.postToolUse !== false ? {
PostToolUse: [{ hooks: [agentHooks.createPostToolUseHook(chatJid, groupFolder, hooksConfig?.skipTools, log)] }],
} : {}),
// Include SessionStart if supported:
...(hooksConfig?.sessionStart !== false ? {
SessionStart: [{ hooks: [agentHooks.createSessionStartHook(chatJid, groupFolder, log)] }],
} : {}),
}),
});
} catch {
// Plugin not available, skip
}
Remove hook type entries that were not selected in Step 1.
Step 5: Verify
No build step needed — agent-hooks plugins are pure .mjs files loaded at runtime.
The plugin directory is automatically mounted into containers at /app/plugins/ by the orchestrator (via container/plugins/ → /app/plugins/ bind mount).
Verify TypeScript still compiles:
pnpm exec tsc --noEmit
Step 6: Next steps
Tell the user:
- Implement hooks — Edit
container/plugins/agent-hooks-{name}/index.mjsto customize notification format and behavior - Sync container entry — Run
/deployto add the plugin to your local entry.ts (select the agent you chose) - Rebuild Docker image —
./container/claude-code/build.shand/or./container/open-code/build.sh - Restart nagi — Run
/nagi-restart - Test — Send a message in Slack/Discord that triggers tool use
Key design rules
- Pure JavaScript — No TypeScript, no build step. Single
index.mjsfile - IPC messaging — Write JSON files to
/workspace/ipc/messages/for the host to consume - Atomic writes — Always write to
.tmpthen rename (prevents partial reads) - Never throw — Wrap hook logic in try/catch, log errors and return
{} - Skip internal tools — Always skip
mcp__nagi__send_messageto avoid notification loops - Export factory functions —
createPostToolUseHook()and/orcreateSessionStartHook()that return async hook callbacks
Reference
Existing agent-hooks plugins to study:
container/claude-code/plugins/agent-hooks/— PostToolUse (tool icons, summary) + SessionStart ("Thinking...")
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.