Hooks
Run your own commands when the assistant uses a tool or finishes: block risky actions, lint edits, run tests.
Hooks are your own commands, run at fixed points in the assistant's work:
- before it uses a tool, to block an action, such as committing a secret;
- after it uses one, to check the result, such as linting every edit;
- when it says the task is done, to send it back to work, for example while tests still fail.
They follow Claude Code's format, so hooks a team already has carry over.
Where to put them
Put them in .lamied/hooks.json at the root of the workspace. Without that file, Lamied uses the hooks in .claude/settings.json.
{
"hooks": {
"PreToolUse": [
{ "matcher": "Bash", "hooks": [{ "type": "command", "command": "./scripts/no-secrets.sh" }] }
],
"PostToolUse": [
{ "matcher": "Edit|Write", "hooks": [{ "type": "command", "command": "npx eslint --quiet \"$(jq -r .tool_input.file_path)\"", "timeout": 30 }] }
],
"Stop": [
{ "hooks": [{ "type": "command", "command": "pnpm test 1>&2 || exit 2", "timeout": 300 }] }
]
}
}When they run
| Event | Runs | It can |
|---|---|---|
PreToolUse | Before a tool call the matcher names | Block the call. The assistant is told why. |
PostToolUse | After such a call succeeds | Tell the assistant what it found, such as lint errors. |
Stop | When the assistant has checked that the task is done | Send it back to work with a reason. |
A matcher names the tools a hook is for. Several can be joined with |, and .* matches any text. An empty matcher, or *, means every tool. Lamied's tool names work, and so do Claude Code's:
| Claude Code | Lamied |
|---|---|
Bash | run_terminal_command |
Read | read_file |
Write | write_file |
Edit | edit_file |
Glob | find_files |
Grep | search_files |
LS | list_directory |
Other tools go by their Lamied names, such as delete_file and move_file. Tools from MCP servers are named mcp__server__tool.
What a hook is given
A hook runs in the workspace folder. The variables LAMIED_PROJECT_DIR and CLAUDE_PROJECT_DIR hold its path. The event comes as JSON on standard input:
{
"session_id": "…",
"cwd": "/home/me/shop",
"hook_event_name": "PreToolUse",
"tool_name": "edit_file",
"tool_input": { "path": "src/cart.ts", "file_path": "/home/me/shop/src/cart.ts", "old_string": "…", "new_string": "…" },
"tool_use_id": "…"
}A PostToolUse hook also gets tool_response, the tool's result. A Stop hook gets stop_hook_active, which is true when a Stop hook has already sent this task back to work.
How a hook answers
- Exit code 0: carry on.
- Exit code 2: object. What the hook printed to standard error goes to the assistant. A PreToolUse objection blocks the call; a PostToolUse one reports a problem; a Stop one sends the task back to work.
- Any other exit code, or running past its
timeout(60 seconds unless set; at most 600): a PreToolUse hook blocks the call, because a check that can't decide mustn't let the action through. Other hooks are ignored.
With exit code 0, a hook can also answer in JSON on standard output, as in Claude Code:
{"hookSpecificOutput": {"permissionDecision": "deny", "permissionDecisionReason": "…"}}blocks a PreToolUse call;{"decision": "block", "reason": "…"}objects, like exit code 2;{"hookSpecificOutput": {"additionalContext": "…"}}tells the assistant something without objecting.
You approve them first
Hooks run commands on your computer, and a repository you open may not be yours. So the first time a project's hooks would run, the chat shows their commands and asks Run this project's hooks?
- An approval is remembered for that workspace, until the commands change. Then you're asked again.
- If you decline, the hooks don't run, and you aren't asked again until Lamied restarts. Allowing commands in command permissions doesn't approve hooks.
- A Stop hook can send a task back to work three times. After that, the task ends as incomplete, with the hook's last reason.
Differences from Claude Code
- Lamied runs PreToolUse, PostToolUse and Stop hooks. Other events, such as UserPromptSubmit and SessionStart, are skipped.
- A PreToolUse hook that fails or times out blocks the call. In Claude Code, the call goes ahead.
- Help agents (research and worktree agents) run the same PreToolUse and PostToolUse hooks. Stop hooks run for the main task.