DEV Community

Guilherme Yamakawa de Oliveira
Guilherme Yamakawa de Oliveira

Posted on • Originally published at guilherme44.com

[PT-BR] Slash commands: chega de commits sem sentido

Uma das coisas que mais uso desde que comecei com o Claude Code são os slash commands. E um que acho que todo dev hoje precisa ter é o /commit. Ele acabou com um hábito que vários devs sofrem, até os mais experientes. O /commit deixa a régua explícita, e o time todo passa a escrever commits do mesmo jeito.

Sempre me importei com mensagens de commit. Mesmo dev que se importa acaba deixando um "fixes" ou um "update" genérico escapar numa sexta cansada ou no meio de um refactor gigante. É assim que um repo acaba com uma camada de ruído que ninguém decifra três meses depois.

O que é um slash command

Slash commands não são exclusivos do Claude Code. OpenCode, Codex CLI, Aider e Continue deixam você criar seus próprios comandos de algum jeito. O formato e o gatilho mudam, mas a ideia é a mesma: um atalho curto roda um prompt mais longo que você escreveu uma vez.

Vou mostrar com Claude Code, que é o que uso no dia a dia. A configuração se traduz bem pros outros.

No Claude Code, um slash command é só um arquivo markdown. Você joga ele em ~/.claude/commands/<nome>.md (global) ou .claude/commands/<nome>.md (por projeto), e pronto: /<nome> vira atalho. O frontmatter tem um description, e o corpo é o prompt que o agente lê quando você chama.

É isso. Sem DSL, sem SDK, sem manifesto de plugin. Arquivo markdown entra, slash command sai.

O problema do commit

Mesmo com um agente bom no loop, um simples "commita minhas mudanças" te entrega algo passável, mas não excelente. O agente escolhe um tipo qualquer, escreve uma mensagem que serve e empilha mudanças sem relação no mesmo commit. Melhor do que sem agente nenhum. Mas dá pra melhorar.

O que eu queria era algo que lesse minhas mudanças, agrupasse por domínio, escrevesse uma mensagem decente seguindo o Conventional Commits pra cada grupo, e pulasse os arquivos que não devem ser commitados.

/commit

Aqui vai o slash command que montei pra manter os commits limpos e consistentes:

---
description: "Analyze all changes and commit in logical groups."
---

# Commit

## Step 1: Survey changes

Run `bin/commit-survey` to get the file lists and classification.

Read diffs of key files if you need more context on the changes.

## Step 2: Group the changes

Use the `--- classified ---` output as a starting point, then refine into logical commits.

Grouping strategy:
- By domain/feature: e.g., all auth changes together
- By layer: e.g., model tests, controller tests
- By type: e.g., all config changes, all dependency updates

Files in `[skip]` should NOT be committed. If unsure about a file, skip it.

## Step 3: Commit each group

For each group, combine stage + commit in one call:

git add <specific files> && git commit -m "<message>"

Order commits from most independent to most dependent:
- Config/tooling changes first
- Then source code changes
- Then test changes
- Generated files last
Enter fullscreen mode Exit fullscreen mode

Esses três passos são o motor inteiro.

bin/commit-survey

O Step 1 chama um script. É o que faz o resto funcionar.

Eu poderia pedir pro agente rodar git status, git diff, parsear a saída e classificar os arquivos na cabeça dele. Funcionaria na maioria das vezes. Mas aí cada execução gasta tokens na mesma lógica de parsing, e a saída fica do jeito que o Claude tiver inspirado naquele dia.

Um script Ruby curto te dá:

  • Uma invocação previsível
  • Menos tokens (sem saída de git status pra digerir, só o resultado classificado)
  • Buckets que batem com o jeito que você pensa no seu código

São ~22 linhas:

SKIP_PATTERNS = %w[.env credentials master.key tasks.md notes.txt scratch .claude/]

BUCKETS = {
  "skip"   => ->(path) { SKIP_PATTERNS.any? { |pat| path.include?(pat) } },
  "test"   => ->(path) { path.start_with?("test/") },
  "db"     => ->(path) { path.start_with?("db/") },
  "config" => ->(path) { path.start_with?("config/", "Gemfile", ".rubocop", "Procfile", "Rakefile") },
  "docs"   => ->(path) { path.start_with?("docs/", "README") || path.end_with?(".md") },
  "app"    => ->(_)     { true }
}

paths = `git status --porcelain`.lines(chomp: true).map { |l| l[3..] }
grouped = BUCKETS.keys.to_h { |b| [b, []] }
paths.each { |path| grouped[BUCKETS.find { |_, matcher| matcher.call(path) }.first] << path }

BUCKETS.each_key do |bucket|
  files = grouped[bucket]
  puts "[#{bucket}] #{files.empty? ? "(none)" : files.join(", ")}"
end
Enter fullscreen mode Exit fullscreen mode

skip vem primeiro pra que segredos e notas nunca acabem staged. O resto é um buffet que você ajusta por projeto. Uma app Phoenix teria lib/, priv/repo/migrations/, assets/. Uma app Next.js teria pages/, app/, public/, prisma/. Mesma ideia, paths diferentes.

Roda num repo sujo e sai isso:

--- unstaged ---
 M config.toml
 M templates/index.html

--- untracked ---
content/blog/_index.md
content/blog/post.md

--- classified ---
[skip]   (none)
[test]   (none)
[db]     (none)
[config] config.toml
[docs]   content/blog/_index.md, content/blog/post.md
[app]    templates/index.html
Enter fullscreen mode Exit fullscreen mode

O agente olha aquilo e escreve uns três commits: chore(config): bump zola version, feat(templates): add language toggle, feat(blog): bootstrap section. Três mensagens focadas em vez de uma pra cobrir tudo.

Na prática

Eu digito /commit e o Claude:

  1. Roda bin/commit-survey e lê a saída
  2. Lê diffs dos arquivos que precisar pra ter mais contexto
  3. Stage + commit de cada grupo com mensagem Conventional Commits
  4. Pula .env e qualquer segredo silenciosamente

A coisa toda leva 30 segundos, e cada commit sai limpo e bem escopado.

Exemplo real desse próprio blog. Survey antes do run:

[skip]   (none)
[test]   (none)
[db]     (none)
[config] config.toml
[docs]   content/blog/slash-commands-no-more-bad-commits.md, content/blog/slash-commands-no-more-bad-commits.pt-br.md
[app]    sass/_predefined.scss, sass/style.scss
Enter fullscreen mode Exit fullscreen mode

O que o /commit produziu:

feat(blog): add slash commands post in EN and pt-br
chore(config): drop syntax theme, switch to monochrome code blocks
style(sass): apply osaka jade palette and bump body font
Enter fullscreen mode Exit fullscreen mode

Três commits, cada um com escopo claro.

Faça o seu

O jeito mais simples de começar é copiar esse e ir ajustando. Joga o arquivo em ~/.claude/commands/commit.md se quiser global, ou em .claude/commands/commit.md por projeto. Ajusta os patterns dos buckets pro teu stack. Pronto.

Dá pra fazer a mesma coisa pra qualquer coisa que você faz toda hora: /changelog, /release, /pr-draft, /deploy. Cada um é um arquivo markdown com os passos que você teria que digitar no chat toda vez.

Finalizando

A configuração é pequena, mas a diferença aparece em todo commit. Um slash command customizado mais um script pequeno já é suficiente pra fazer a IA pegar o trabalho rotineiro pra si.

Chega de commits "update". Cada commit reflete a régua que você escreveu no arquivo markdown.


Fontes:


Publicado originalmente em guilherme44.com.

Top comments (0)