MÓDULO 1.5

📂 O Ambiente para Construir: Estrutura e Ferramentas

📚

Tópicos

6

⏱️

Minutos

35

🎯

Nível

Iniciante

🏗️

Tipo

Prático

1

📂 Estrutura de repositório para plugins Claude Code

Uma estrutura consistente de repositório acelera onboarding, evita arquivos perdidos e facilita manutenção. O padrão abaixo é testado em projetos reais e separa claramente configuração, hooks, skills e estado.

🗂️ Estrutura recomendada

meu-projeto/
├── .claude/
│   ├── settings.json          # Hooks, comandos, permissões
│   ├── hooks/                 # Scripts de hook
│   │   ├── validate-commit.sh
│   │   ├── audit-writes.sh
│   │   └── notify-stop.sh
│   ├── skills/                # Arquivos de skill (slash commands)
│   │   ├── revisar-pr.md
│   │   ├── gerar-componente.md
│   │   └── analisar-deps.md
│   ├── state/                 # Estado persistente dos hooks
│   │   ├── .gitignore         # Ignorar estado em git (opcional)
│   │   └── hook-stats.yaml
│   └── logs/                  # Logs dos hooks (não commitar)
│       └── .gitignore         # logs/* ignorado
├── CLAUDE.md                  # Instruções do projeto
├── SPEC.md                    # Especificação do sistema de hooks
└── src/                       # Código do projeto em si

💡 .gitignore para logs e estado

Crie .claude/logs/.gitignore com * para ignorar todos os logs. Para estado, decida caso a caso: estado que precisa ser compartilhado com o time deve ser commitado; estado temporário de sessão não.

2

🧪 Como testar um hook sem quebrar o fluxo

O melhor teste de hook é passar o JSON de input manualmente via stdin, sem o Claude Code. Isso permite validar a lógica em isolamento total — sem latência de API, sem custo de tokens, 100% repetível.

🧪 Técnicas de teste

# Teste 1: Input inline via echo
echo '{"event":"PreToolUse","tool":{"name":"Bash","input":{"command":"git commit -m fix"}}}' \
  | .claude/hooks/validate-commit.sh
echo "Exit code: $?"

# Teste 2: Input de arquivo fixture
cat .claude/tests/fixtures/bash-git-commit.json \
  | .claude/hooks/validate-commit.sh

# Teste 3: Script de teste automatizado
# .claude/tests/test-validate-commit.sh
#!/bin/bash
PASS=0; FAIL=0

run_test() {
  local name="$1"
  local input="$2"
  local expected="$3"
  echo "$input" | .claude/hooks/validate-commit.sh > /dev/null 2>&1
  local actual=$?
  if [ "$actual" = "$expected" ]; then
    echo "✓ $name"
    PASS=$((PASS + 1))
  else
    echo "✗ $name (esperado $expected, obteve $actual)"
    FAIL=$((FAIL + 1))
  fi
}

run_test "mensagem curta deve bloquear" \
  '{"tool":{"name":"Bash","input":{"command":"git commit -m '\''fix'\''"}}}' \
  1

run_test "mensagem boa deve aprovar" \
  '{"tool":{"name":"Bash","input":{"command":"git commit -m '\''feat: add user authentication'\''"}}}' \
  0

echo "Resultado: $PASS passou, $FAIL falhou"
3

🪵 Logs e debugging

Hooks executam em silêncio — sem logs, você não tem visibilidade. Stdout é reservado para comunicação com o Claude; use stderr para erros e arquivo de log para rastreabilidade permanente.

🪵 Padrão de logging em hooks

#!/bin/bash
# Padrão de logging reutilizável

LOG_FILE="${CLAUDE_LOG_DIR:-.claude/logs}/validate-commit.log"
mkdir -p "$(dirname "$LOG_FILE")"

# Função de log com timestamp e nível
log() {
  local level="$1"; shift
  echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] [$level] validate-commit: $*" >> "$LOG_FILE"
}

# Redireciona stderr para log E para o fd original
exec 3>&2                          # Salva fd 2 original
exec 2> >(tee -a "$LOG_FILE" >&3) # Tee: stderr vai para log e original

log INFO "Hook iniciado — PID: $$"

# --- Lógica do hook ---
PAYLOAD=$(cat)
log DEBUG "Payload recebido: $(echo "$PAYLOAD" | head -c 200)"

if [ condição_de_bloqueio ]; then
  log WARN "Bloqueando: motivo_aqui"
  echo "Mensagem de erro para o Claude" >&2
  exit 1
fi

log INFO "Aprovado"
exit 0

💡 Monitorar logs em tempo real

Durante desenvolvimento, mantenha um terminal com tail -f .claude/logs/validate-commit.log aberto. Cada execução do hook aparece em tempo real — você vê exatamente o que está acontecendo enquanto trabalha no Claude Code.

4

📋 O SPEC.md como bússola

O SPEC.md é o documento que captura decisões de design antes do código. Para hooks, ele define o que o hook deve fazer, quais invariantes mantém, quais são os edge cases e por que certas escolhas foram feitas.

📋 Template de SPEC.md para hook

# SPEC: validate-commit hook

## Objetivo
Bloquear commits do Claude Code com mensagens que não seguem
Conventional Commits ou são muito genéricas.

## Responsabilidade
- INTERCEPTA: PreToolUse Bash quando o comando contém "git commit"
- BLOQUEIA se: mensagem tem menos de 20 chars OU é genérica
- APROVA se: mensagem segue o padrão ou tem 20+ chars descritivos

## Invariantes
1. Falha silenciosa = aprovação (fail-open)
2. Nunca bloqueia commits de merge automático
3. Mensagem de erro é acionável (diz o que fazer, não só o que está errado)

## Casos de borda
- git commit --amend: APROVADO (não valida re-commit)
- git commit -m 'WIP: ...' : APROVADO (WIP é exceção explícita)
- jq não instalado: APROVADO (fail-open, log warning)

## Dependências
- jq (obrigatório, com fallback para grep)
- bash 4.0+

## Decisões de design
- Por que não usar regex mais complexo: manutenibilidade > precisão
- Por que não checar branch: escopo pequeno, fácil de expandir depois
5

🌿 Versionamento de prompts

Skills e CLAUDE.md são código — devem ser versionados no git com commits descritivos. Cada iteração de um prompt é um commit: você consegue ver o que mudou, por que mudou e fazer rollback quando algo quebra.

✓ Boas mensagens de commit de prompt

skill(revisar-pr): adiciona verificação de tipos TypeScript
claude.md: instrui leitura de MEMORY.md ao iniciar sessão
skill(gerar-componente): corrige template de stories

✗ Mensagens ruins

update prompts
fix skill
ajustes no CLAUDE.md

📊 Por que commitar prompts incrementalmente

  • • Permite rollback quando uma nova instrução causa comportamento inesperado
  • • Facilita code review: outra pessoa pode revisar mudanças no prompt
  • • Cria histórico de "por que este prompt existe" nos logs do git
  • • Permite comparar versões com git diff HEAD~1 HEAD CLAUDE.md
6

🔧 Ferramentas de apoio: jq, yq, shellcheck

Três ferramentas eliminam as classes de erro mais comuns em desenvolvimento de hooks. jq, yq e shellcheck são o kit mínimo que todo desenvolvedor de hooks deve ter instalado.

jq

Processa JSON no terminal. Essencial para parsear o payload de stdin dos hooks.

# Instalação
brew install jq   # macOS
apt install jq    # Ubuntu/Debian

# Uso em hook
COMMAND=$(echo "$PAYLOAD" | jq -r '.tool.input.command // empty')
TOOL=$(echo "$PAYLOAD" | jq -r '.tool.name // empty')

# Query condicional
echo "$PAYLOAD" | jq 'if .tool.name == "Bash" then .tool.input.command else "" end'
yq

Processa YAML com a mesma sintaxe do jq. Indispensável para ler arquivos de estado em YAML.

# Instalação
brew install yq   # macOS
snap install yq   # Linux

# Ler campo simples
VERSION=$(yq '.version' state.yaml)

# Ler com default
RUNS=$(yq '.stats.runs // 0' state.yaml)

# Atualizar in-place
yq -i '.version += 1' state.yaml
shellcheck

Analisa scripts shell e detecta bugs comuns antes de você rodar em produção. Integre no CI.

# Instalação
brew install shellcheck   # macOS
apt install shellcheck    # Ubuntu/Debian

# Verificar um hook
shellcheck -S error .claude/hooks/validate-commit.sh

# Erros comuns que shellcheck detecta:
# - Variáveis não quotadas (word splitting)
# - Comparações sem quotes ($VAR vs "$VAR")
# - Uso de [ vs [[ inconsistente
# - Pipelines que mascaram erros (sem set -o pipefail)

Resumo do Módulo 1.5

Estrutura .claude/ — hooks/, skills/, state/, logs/ organizados e separados
Teste com echo | ./hook.sh — isolado, rápido e sem custo de API
Log em arquivo, stderr para Claude — stdout é reservado para comunicação com o sistema
SPEC.md antes de qualquer código — invariantes e edge cases documentados upfront
Skills versionadas como código — commit descritivo para cada iteração de prompt
jq + yq + shellcheck — kit mínimo para desenvolvimento de hooks profissional

Próximo Módulo:

1.6 — Seu Primeiro Hook de Validação — projeto prático completo aplicando tudo da Trilha 1