Hooks de Ciclo de Vida de Sessão do Claude Code
Quatro hooks de ciclo de vida de sessão do Claude Code: executa init a pedido, injeta contexto do projeto no SessionStart, faz backup de transcripts e regista limpeza no exit do SessionEnd.
Pare de configurar. Comece a construir.
Templates SaaS com orquestração de IA.
Problema: Cada nova sessão começa às cegas. Voltas a explicar o branch em que estás, a fila de tarefas, e as env vars que os teus scripts precisam. Quando a sessão termina, a limpeza que devia acontecer nunca acontece.
Solução Rápida: Cola isto no settings.json. O contexto Git aparece no chat em cada início:
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "echo '## Git' && git branch --show-current && git status --short | head -10"
}
]
}
]
}
}Cada sessão abre agora com contexto. Zero configuração manual.
Os Quatro Hooks de Ciclo de Vida de Sessão
O comportamento da sessão é conduzido por quatro tipos de hook:
| Hook | Quando Dispara | Pode Bloquear? | Caso de Uso |
|---|---|---|---|
| Setup | Com --init ou --maintenance | NÃO | Configuração única, migrações |
| SessionStart | Cada início/retoma de sessão | NÃO | Carregar contexto, definir env vars |
| PreCompact | Antes da compactação de contexto | NÃO | Backup de transcripts |
| SessionEnd | Sessão termina | NÃO | Limpeza, logging |
SessionStart: Carrega Contexto de Cada Vez
O SessionStart corre sempre que uma sessão começa ou retoma. Usa-o quando algo deve estar sempre na cabeça do Claude.
Injeção de Contexto Básica
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "echo '## Project State' && cat .claude/tasks/session-current.md 2>/dev/null || echo 'No active session'"
}
]
}
]
}
}Com Output JSON
Para injeção de contexto estruturado:
#!/usr/bin/env python3
import json
import sys
import subprocess
def get_project_context():
try:
branch = subprocess.check_output(
['git', 'rev-parse', '--abbrev-ref', 'HEAD'],
text=True, stderr=subprocess.DEVNULL
).strip()
status = subprocess.check_output(
['git', 'status', '--porcelain'],
text=True, stderr=subprocess.DEVNULL
).strip()
changes = len(status.split('\n')) if status else 0
except:
branch, changes = "unknown", 0
return f"""=== SESSION CONTEXT ===
Git Branch: {branch}
Uncommitted Changes: {changes}
=== END ===""".strip()
output = {
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": get_project_context()
}
}
print(json.dumps(output))
sys.exit(0)Matchers do SessionStart
Aponta para eventos de sessão específicos:
{
"hooks": {
"SessionStart": [
{
"matcher": "startup",
"hooks": [{ "type": "command", "command": "echo 'Fresh session'" }]
},
{
"matcher": "resume",
"hooks": [{ "type": "command", "command": "echo 'Resumed session'" }]
},
{
"matcher": "compact",
"hooks": [{ "type": "command", "command": "echo 'Post-compaction'" }]
}
]
}
}startup- Nova sessãoresume- De--resume,--continue, ou/resumeclear- Após/clearcompact- Após compactação
Persistir Variáveis de Ambiente
O SessionStart tem acesso a CLAUDE_ENV_FILE para definir variáveis de ambiente para a sessão toda:
#!/bin/bash
# Persist environment changes from nvm, pyenv, etc.
ENV_BEFORE=$(export -p | sort)
# Setup commands that modify environment
source ~/.nvm/nvm.sh
nvm use 20
if [ -n "$CLAUDE_ENV_FILE" ]; then
ENV_AFTER=$(export -p | sort)
comm -13 <(echo "$ENV_BEFORE") <(echo "$ENV_AFTER") >> "$CLAUDE_ENV_FILE"
fi
exit 0Tudo o que for escrito em CLAUDE_ENV_FILE aparece em cada comando bash que Claude correr depois disso.
Setup: Operações Únicas
Os hooks Setup só correm quando invocas explicitamente --init, --init-only, ou --maintenance. Bom para trabalho que não queres disparado em cada nova sessão.
Quando Usar Setup vs SessionStart
| Operação | Usa Setup | Usa SessionStart |
|---|---|---|
| Instalar dependências | Sim | Não |
| Correr migrações de base de dados | Sim | Não |
| Carregar estado git | Não | Sim |
| Definir variáveis de ambiente | Sim | Sim |
| Injetar contexto do projeto | Não | Sim |
| Limpar ficheiros temporários | Sim (maintenance) | Não |
Configuração do Setup
{
"hooks": {
"Setup": [
{
"matcher": "init",
"hooks": [
{
"type": "command",
"command": "npm install && npm run db:migrate"
}
]
},
{
"matcher": "maintenance",
"hooks": [
{
"type": "command",
"command": "npm prune && npm dedupe && rm -rf .cache"
}
]
}
]
}
}Invoca com:
claude --init # Runs 'init' matcher
claude --init-only # Runs 'init' matcher, then exits
claude --maintenance # Runs 'maintenance' matcherOs hooks Setup também podem escrever em CLAUDE_ENV_FILE para persistir variáveis de ambiente.
PreCompact: Antes da Perda de Contexto
O PreCompact dispara mesmo antes da compactação, quer o utilizador a tenha disparado com /compact ou ela tenha arrancado automaticamente quando a janela encheu.
Backup de Transcripts
#!/usr/bin/env python3
import json
import sys
import shutil
from pathlib import Path
from datetime import datetime
input_data = json.load(sys.stdin)
transcript_path = input_data.get('transcript_path', '')
trigger = input_data.get('trigger', 'unknown')
if transcript_path and Path(transcript_path).exists():
backup_dir = Path('.claude/backups')
backup_dir.mkdir(parents=True, exist_ok=True)
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
backup_name = f"transcript_{trigger}_{timestamp}.jsonl"
shutil.copy2(transcript_path, backup_dir / backup_name)
# Keep only last 10 backups
backups = sorted(backup_dir.glob('transcript_*.jsonl'))
for old_backup in backups[:-10]:
old_backup.unlink()
sys.exit(0)Matchers do PreCompact
{
"hooks": {
"PreCompact": [
{
"matcher": "auto",
"hooks": [{ "type": "command", "command": "echo 'Auto-compacting...'" }]
},
{
"matcher": "manual",
"hooks": [{ "type": "command", "command": "echo 'Manual /compact'" }]
}
]
}
}auto- Janela de contexto encheu, compactação automáticamanual- Utilizador correu/compact
Criar Marcadores de Recuperação
Combina PreCompact com SessionStart para reconstruir o contexto após um evento de compactação. Um padrão que funciona: um módulo backup-core partilhado, um monitor de statusline que dispara triggers baseados em threshold, e um handler PreCompact, todos a coordenar através de um ficheiro de estado para que nada se perca entre sessões. Vê o guia Context Recovery Hook para o percurso completo.
SessionEnd: Limpeza
O SessionEnd corre quando uma sessão está a terminar. Não pode bloquear o encerramento, mas pode fazer limpeza.
Registar Estatísticas da Sessão
#!/usr/bin/env python3
import json
import sys
from pathlib import Path
from datetime import datetime
input_data = json.load(sys.stdin)
session_id = input_data.get('session_id', 'unknown')
reason = input_data.get('reason', 'unknown')
log_dir = Path('.claude/logs')
log_dir.mkdir(parents=True, exist_ok=True)
log_entry = {
"session_id": session_id,
"ended_at": datetime.now().isoformat(),
"reason": reason
}
with open(log_dir / 'session-history.jsonl', 'a') as f:
f.write(json.dumps(log_entry) + '\n')
sys.exit(0)Razões do SessionEnd
O campo reason diz-te porque a sessão terminou:
clear- Utilizador correu/clearlogout- Utilizador fez logoutprompt_input_exit- Utilizador saiu enquanto o prompt estava visívelother- Outras razões de saída
Exemplo Completo de Ciclo de Vida
Uma configuração completa de ciclo de vida ligada de ponta a ponta:
{
"hooks": {
"Setup": [
{
"matcher": "init",
"hooks": [
{
"type": "command",
"command": "npm install && echo 'Dependencies installed'"
}
]
}
],
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "echo '## Context' && git status --short && echo '## Tasks' && cat .claude/tasks/session-current.md 2>/dev/null | head -20"
}
]
}
],
"PreCompact": [
{
"hooks": [
{
"type": "command",
"command": "cp \"$CLAUDE_TRANSCRIPT_PATH\" .claude/backups/last-transcript.jsonl 2>/dev/null || true"
}
]
}
],
"SessionEnd": [
{
"hooks": [
{
"type": "command",
"command": "echo \"Session ended: $(date)\" >> .claude/logs/sessions.log"
}
]
}
]
}
}Payloads de Entrada
Entrada do SessionStart
{
"session_id": "abc123",
"hook_event_name": "SessionStart",
"source": "startup",
"model": "claude-sonnet-4-20250514",
"cwd": "/path/to/project"
}Entrada do Setup
{
"session_id": "abc123",
"hook_event_name": "Setup",
"trigger": "init",
"cwd": "/path/to/project"
}Entrada do PreCompact
{
"session_id": "abc123",
"hook_event_name": "PreCompact",
"transcript_path": "~/.claude/projects/.../transcript.jsonl",
"trigger": "auto",
"custom_instructions": ""
}Entrada do SessionEnd
{
"session_id": "abc123",
"hook_event_name": "SessionEnd",
"reason": "clear",
"cwd": "/path/to/project"
}Boas Práticas
-
Mantém o SessionStart rápido - Corre em cada sessão. Empurra o trabalho pesado para o Setup.
-
Usa Setup para trabalho único - Instalação de dependências, migrações, bootstrap inicial do projeto.
-
Faz backup antes da compactação - O PreCompact é a tua última oportunidade de capturar o transcript.
-
Regista os fins de sessão - O SessionEnd é útil para analytics e depuração.
-
Usa matchers com cuidado - Comportamentos diferentes para
startupvsresumevscompactevitam surpresas.
Próximos Passos
- Configura o Guia de Hooks principal para todos os 12 hooks
- Configura Context Recovery para sobreviver à compactação
- Usa Stop Hooks para aplicação de tarefas
- Explora Skill Activation para carregamento automático de skills
Pare de configurar. Comece a construir.
Templates SaaS com orquestração de IA.