Docs menuHooks

DocsAI assistant

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.

.lamied/hooks.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

EventRunsIt can
PreToolUseBefore a tool call the matcher namesBlock the call. The assistant is told why.
PostToolUseAfter such a call succeedsTell the assistant what it found, such as lint errors.
StopWhen the assistant has checked that the task is doneSend 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 CodeLamied
Bashrun_terminal_command
Readread_file
Writewrite_file
Editedit_file
Globfind_files
Grepsearch_files
LSlist_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:

Standard input, PreToolUse
{
  "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.