framework/devtools/skills/flowai-engineer-hook/SKILL.md
Creation and configuration of event hooks/plugins to manage agent behavior, command filtering, auditing, and automation. Works across IDEs (Cursor, Claude Code, OpenCode). Use when you need to: (1) Create a new hook (e.g., for formatting or security checks), (2) Configure hooks/plugins, (3) Implement logic for blocking or modifying agent actions via scripts.
npx skillsauth add korchasa/flow flowai-engineer-hookInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill helps design and implement event hooks (or plugins). Hooks allow intercepting agent actions (command execution, file read/write, tool usage) and applying rules: allow, block (with explanation), request confirmation, or modify input data.
| IDE | User Hooks | Project Hooks | Format |
|-----|-----------|--------------|--------|
| Cursor | ~/.cursor/hooks.json | .cursor/hooks.json | JSON config + shell scripts |
| Claude Code | ~/.claude/settings.json | .claude/settings.json<br>.claude/settings.local.json | JSON config (4 hook types) |
| OpenCode | ~/.config/opencode/plugins/*.{js,ts} | .opencode/plugins/*.{js,ts}<br>opencode.json plugin (npm) | JS/TS modules (event-based) |
.cursor/ directory → Cursor.claude/ directory → Claude Code.opencode/ directory or opencode.json → OpenCodeIMPORTANT: After detecting the IDE, read ONLY the corresponding reference file. Do not load all references.
| Type | Cursor | Claude Code | OpenCode | |------|--------|-------------|----------| | Command (shell script) | Yes | Yes | No (use plugin code) | | Prompt (LLM-evaluated) | Yes | Yes (8 events) | No | | HTTP (POST to URL) | No | Yes | No | | Agent (multi-turn subagent) | No | Yes (8 events) | No | | Programmatic (JS/TS code) | No | No | Yes |
| Cursor Event | Claude Code Event | OpenCode Equivalent |
|:---|:---|:---|
| beforeShellExecution | PreToolUse (matcher: "Bash") | tool.execute.before |
| afterShellExecution | PostToolUse (matcher: "Bash") | tool.execute.after |
| preToolUse | PreToolUse | tool.execute.before |
| postToolUse | PostToolUse | tool.execute.after |
| postToolUseFailure | PostToolUseFailure | event → tool.execute.after |
| sessionStart | SessionStart | event → session.created |
| sessionEnd | SessionEnd | event → session.deleted |
| subagentStart | SubagentStart | — |
| subagentStop | SubagentStop | — |
| stop | Stop | event → session.idle |
| preCompact | PreCompact | experimental.session.compacting |
| afterFileEdit | PostToolUse (matcher: "Edit\|Write") | event → file.edited |
| beforeSubmitPrompt | UserPromptSubmit | chat.message |
| beforeMCPExecution | PreToolUse (matcher: "mcp__.*") | tool.execute.before |
| afterMCPExecution | PostToolUse (matcher: "mcp__.*") | tool.execute.after |
| beforeReadFile | PreToolUse (matcher: "Read") | tool.execute.before |
| — | PermissionRequest | permission.ask |
| — | Notification | event → various |
| — | TeammateIdle | — |
| — | TaskCompleted | event → todo.updated |
| — | ConfigChange | — |
| — | InstructionsLoaded | — |
| — | WorktreeCreate / WorktreeRemove | — |
JSON config + shell scripts. Two types: command and prompt.
In .cursor/hooks.json:
{
"version": 1,
"hooks": {
"afterFileEdit": [{ "command": ".cursor/hooks/format.sh" }],
"beforeShellExecution": [
{ "command": ".cursor/hooks/guard.sh", "matcher": "rm " }
]
}
}
{ "type": "prompt", "prompt": "Is this command safe?", "timeout": 10 }
For events list, input/output schemas, env vars, matcher values: see hooks_api.md.
guard.sh:
#!/bin/bash
input=$(cat)
command=$(echo "$input" | jq -r '.command')
if [[ "$command" == *"rm -rf"* ]]; then
echo '{"permission": "ask", "user_message": "Are you sure?", "agent_message": "rm -rf requires confirmation."}'
else
echo '{"permission": "allow"}'
fi
JSON config in settings.json. Four types: command, http, prompt, agent.
In .claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/guard.sh",
"timeout": 30
}
]
}
]
}
}
Note the nested structure: each event maps to an array of matcher groups, each containing a hooks array.
$ARGUMENTS placeholder.Prompt/agent types only available for 8 events: PermissionRequest, PostToolUse, PostToolUseFailure, PreToolUse, Stop, SubagentStop, TaskCompleted, UserPromptSubmit.
For all 18 events, input/output schemas, env vars, exit code behavior per event: see claude_code_hooks_api.md.
guard.sh (same script works for both Cursor and Claude Code):
#!/bin/bash
input=$(cat)
tool_name=$(echo "$input" | jq -r '.tool_name // empty')
tool_input=$(echo "$input" | jq -r '.tool_input.command // empty')
if [[ "$tool_input" == *"rm -rf"* ]]; then
echo "Blocked: rm -rf requires manual confirmation" >&2
exit 2
fi
exit 0
JS/TS modules returning a Hooks object. No JSON config — all logic is code.
import type { Plugin } from "@opencode-ai/plugin"
export default (async ({ project, client, $, directory, worktree }) => {
return {
"tool.execute.before": async (input) => {
// inspect/modify tool arguments
return input
},
event: async (event) => {
// handle any system event
},
}
}) satisfies Plugin
.opencode/plugins/*.tsopencode.json → "plugin": ["package-name"]For all hooks, events, tool() helper, examples: see opencode_plugins_api.md.
import type { Plugin } from "@opencode-ai/plugin"
export default (async ({ client }) => ({
"tool.execute.before": async (input) => {
if (input.tool === "bash" && input.args?.command?.includes("rm -rf")) {
client.app.log({ body: { level: "warn", message: "Blocked rm -rf" } })
return { ...input, args: { command: "echo 'Blocked: rm -rf not allowed'" } }
}
return input
},
})) satisfies Plugin
assets/hook_template.sh — Bash script template for command-type hooksmatcher so hooks only fire for relevant commandsfailClosed: true for security-critical hooksclaude --debug or Ctrl+O for verbose hook logging/hooks menu available for managing hooksstop_hook_active in Stop hooks to prevent infinite loopsPermissionRequest hooks don't fire in non-interactive mode (-p).opencode/package.json (Bun auto-installs)development
Use when the user asks to add TypeScript strict-mode code-style rules to AGENTS.md for a TypeScript project using strict mode. Do NOT trigger for Deno projects (use setup-agent-code-style-deno) or non-strict TS configurations.
development
Use when the user asks to add Deno/TypeScript code-style rules to AGENTS.md, or during initial Deno project setup when code-style guidelines need to be established. Do NOT trigger for non-Deno TypeScript projects (use setup-agent-code-style-strict), or for runtime-agnostic style advice.
testing
Use when the user provides a source (URL, file path, or free text) to save into the project's memex — a long-term knowledge bank for AI agents. Stores the raw source, extracts entities into cross-linked pages, runs a backlink audit, and updates the index and activity log. Do NOT trigger on casual reads; only when the intent is to persist a source into the memex.
development
Use when the user asks to audit a memex (long-term knowledge bank for AI agents) for orphans, dead SALP REFs, missing sections, contradictions, or index drift. Runs a deterministic structural check, layers LLM-judgement findings, optionally auto-fixes trivial issues with `--fix`. Do NOT trigger on general code linting.