OSS · CLI em Bash · Claude Code
hook-chain-lens
Os hooks do Claude Code disparam de forma aditiva em quatro escopos. Veja o que roda de fato.
Uma CLI de leitura pura que carrega todas as definições de hook que o Claude Code vai mesclar para o cwd atual (user, project, local e cada plugin habilitado), resolve ${CLAUDE_PLUGIN_ROOT} e imprime a cadeia mesclada agrupada por evento com escopo de origem, matcher, timeout e arquivo de origem. Bash + jq, sem escritas, sem hooks em runtime.
O que o doctor pega
Oito regras sobre a cadeia mesclada. Erros retornam exit 1, então o doctor funciona como gate de CI ou verificação de pre-commit para os arquivos de settings do Claude Code no seu repositório.
| Código | Severidade | O que detecta |
|---|---|---|
W1 | warn | O mesmo evento tem dois ou mais handlers com matcher vazio: todos disparam a cada trigger, mantendo a ordem. Muitas vezes é proposital, muitas vezes não. |
W2 | warn | O mesmo evento tem o mesmo matcher compartilhado por dois ou mais handlers: costuma ser um copy-paste de um hook de plugin que já existia. |
W3 | error | Um command aponta para um arquivo que não existe depois da expansão de ${CLAUDE_PLUGIN_ROOT}: o hook vai falhar sempre que for disparado. |
W4 | warn | O arquivo de command existe mas não está legível nem executável: permissões provavelmente derrubadas por um passo de install. |
W5 | warn | Um timeout está anormalmente baixo (menos de um segundo): quase sempre um typo do valor pretendido. |
I1 | info | Um timeout está anormalmente alto (mais de 300 segundos): vale confirmar se o hook realmente precisa desse orçamento. |
W6 | error | Um nome de evento não é reconhecido pelo Claude Code: quase sempre um typo (PreToolUsage no lugar de PreToolUse, etc.). |
W7 | warn | O campo matcher aparece em um evento que não aceita matcher: o campo é morto, o handler dispara a cada trigger. |
Instalação
Clone o repositório e crie um symlink da CLI dentro do seu PATH. Requer bash 4+, jq e coreutils. Roda em Linux e macOS.
git clone https://github.com/kenimo49/hook-chain-lens.git
ln -s "$(pwd)/hook-chain-lens/bin/hook-chain-lens" ~/.local/bin/hook-chain-lens
# requires: bash 4+, jq, coreutils Uso
Três subcomandos. Todos aceitam --cwd para ler os escopos project e local a partir de outro diretório, e list aceita --json para pipelines guiados por jq.
hook-chain-lens list # merged chain, grouped by event
hook-chain-lens list --event PreToolUse # filter to one event
hook-chain-lens list --cwd ~/other-repo # read project/local scope from elsewhere
hook-chain-lens list --json | jq '.' # machine-readable output
hook-chain-lens doctor # 8 rules; exit 1 on errors, 0 otherwise
hook-chain-lens events # every event Claude Code knows, grouped by matcher support Quatro decisões de design
As decisões interessantes são todas sobre o que a ferramenta não faz.
-
Somente leitura, sempre
O hook-chain-lens nunca edita settings.json, nunca instala um hook e nunca executa um. Ele não pode desativar em silêncio algo que quebrou, e não pode alterar o que a próxima execução do Claude Code vai fazer. A análise estática deixa a ferramenta segura para rodar no meio da sessão e segura para manter no CI.
-
Aditivo, não sobrescrito
O Claude Code mescla hooks de forma aditiva: um evento definido nos escopos user, project, local e plugin roda todos eles, nessa ordem. O list espelha isso literalmente: nenhum escopo esconde outro. É o modelo que surpreende as pessoas quando quatro plugins estão instalados e todos registram UserPromptSubmit.
-
"Habilitado" quer dizer instalado E ativado
Um plugin só contribui com hooks se o installed_plugins.json listar ele e o settings.json marcar enabledPlugins["<key>"] como true. Plugins instalados mas desativados ficam fora, o que casa com o comportamento em runtime do próprio Claude Code, então a visão mesclada é confiável.
-
O doctor sai com código diferente de zero em erros
Warnings e notas info aparecem mas não derrubam a execução. Erros (W3 arquivo ausente, W6 evento desconhecido) retornam exit 1, então hook-chain-lens doctor vira um gate de pre-commit ou de CI de verdade para os arquivos de settings no seu repositório: um caminho de hook quebrado nunca chega na main.
Ferramentas relacionadas para devs
- private-lint Git-hook gate that blocks personal and private names (family names, personal emails, client domains) before commit and push. Pure Bash; detection patterns stay machine-local, never in any repo, and a full-history audit covers the moment before you flip a repository public.
- quake-lens Earthquake statistics CLI + MCP server in pure-stdlib Python: Gutenberg-Richter b-value (Aki MLE) and Omori-Utsu aftershock decay (Ogata MLE) from public catalogs.
- rhythm-lens Measures the rhythm of Japanese, English and Portuguese Markdown (sentence-length burstiness, paragraph structure) against measured human/AI distributions from two published papers. A writing feedback instrument, not an AI detector.
- claude-shift Multi-account Claude Code manager. Switch the active login, sync the two-file auth Claude Code actually reads (~/.claude/.credentials.json + ~/.claude.json), and watch 5-hour and weekly usage across all accounts. CLI + local API + Chrome extension + browser Web UI + Tauri v2 desktop.