TL;DR: Claude Desktop não suporta múltiplas contas nativamente. A solução é copiar o
.app, modificar o wrapper de lançamento para apontar para umuser-data-dirdiferente, e re-assinar ad-hoc. O CLI continua único em~/.claude/. Dois gotchas críticos: nunca altereCFBundleNamenoInfo.plist(o Electron crasha) e removaCFBundleIconNamese quiser ícone customizado (o macOS prioriza o Asset Catalog sobre o.icns).
Sabe quando você tem dois chips no celular pra separar zap do trabalho do zap da família? É exatamente isso que precisei resolver no Claude Desktop. A conta pessoal tem conectores que a corporativa não oferece, e ficar deslogando e relogando virou aquela tortura chinesa moderna.
Spoiler: deu certo. Mas levou uma tarde, dois crashes do Electron e uma armadilha de cache de ícone que me fez questionar minhas escolhas de vida. Bora ao que importa, com o setup completo, os dois ícones distintos no Dock, e os gotchas que custaram caro pra eu descobrir.
Nota: Os scripts completos (
rebuild.sheinvert.py) estão no repositório rflpazini/articles no GitHub. Os trechos aqui no artigo são exatamente o que está lá, copy-paste direto.
A ideia central, antes de descer no detalhe: o Claude Desktop é tipo o Notion ou o Slack, ele guarda todo o estado num diretório de dados (~/Library/Application Support/Claude). Se eu fizer o app apontar pra um diretório diferente, ele se comporta como uma instalação completamente nova. É o mesmo truque dos perfis do Chrome, só que o Claude não expõe isso na UI. Vamos forçar.
A decisão de escopo: só o Desktop
Antes de entrar na mecânica, vale explicar o que não foi separado.
A CLI continua única em ~/.claude/, com todos os skills, agents, memória de projeto e regras globais. É a CLI que importa pro trabalho real de desenvolvimento, e as duas contas compartilham esse contexto sem dor de cabeça.
O que não compartilham é a sessão do Desktop: preferências de UI, histórico de conversas, configurações de integrações, conectores. Cada instância tem seu próprio diretório de dados. Esse foi o escopo da separação, e funciona bem porque a "identidade" no Desktop é de uso, não de configuração técnica.
O layout final
/Applications/Claude.app <- conta da empresa (original, intocado)
/Applications/Claude Personal.app <- conta pessoal (cópia modificada)
~/Library/Application Support/Claude <- dados da empresa
~/Library/Application Support/Claude-Personal <- dados pessoais
Bundle IDs:
- Empresa:
com.anthropic.claudefordesktop(assinatura Anthropic Developer ID, notarized) - Pessoal:
com.anthropic.claudefordesktop.personal(assinatura ad-hoc, sem notarização)
Ícone da conta pessoal: asterisco laranja em fundo cream (invertido). Permite distinguir as duas instâncias no Dock e no Cmd+Tab num piscar de olhos. Sem isso, vira loteria toda vez que você muda de janela.
Resultado final no Dock: branco em laranja (empresa) à esquerda, laranja em cream (pessoal) à direita. Bate o olho e sabe qual é qual.
Como construir o Claude Personal.app
1. Copiar o app original
cp -R /Applications/Claude.app /Applications/"Claude Personal.app"
Tranquilo, é só cp -R. A magia vem nos próximos passos.
2. Criar o wrapper de lançamento
O binário original do Electron precisa ser renomeado pra que o script wrapper ocupe o lugar dele:
cd "/Applications/Claude Personal.app/Contents/MacOS"
mv Claude Claude-bin
Crie o wrapper Claude no lugar:
cat > "/Applications/Claude Personal.app/Contents/MacOS/Claude" << 'EOF'
#!/bin/bash
exec "$(dirname "$0")/Claude-bin" \
--user-data-dir="$HOME/Library/Application Support/Claude-Personal" \
"$@"
EOF
chmod +x "/Applications/Claude Personal.app/Contents/MacOS/Claude"
O flag --user-data-dir é tudo que diferencia as duas instâncias. O Electron usa esse diretório pra sessão, preferências e cache. Dois diretórios diferentes, dois contextos completamente isolados. É o mesmo princípio que o --profile do Chrome usa, só que aqui a gente injeta na mão.
3. Alterar o bundle identifier no Info.plist
/usr/libexec/PlistBuddy \
-c "Set :CFBundleIdentifier com.anthropic.claudefordesktop.personal" \
"/Applications/Claude Personal.app/Contents/Info.plist"
Gotcha: Esse aqui custou um crash inteiro pra eu descobrir. Altere somente
CFBundleIdentifiereCFBundleDisplayName. Nunca toque emCFBundleName. O Electron resolve os processos auxiliares (Claude Helper.app,Claude Helper (GPU).app) pelo valor deCFBundleName. Se o nome não bater com o que está emContents/Frameworks/, o app crasha comUnable to find helper appemelectron_main_delegate_mac.mm:65antes de mostrar qualquer janela. Você fica olhando pro Console do macOS sem entender.CFBundleDisplayNameé o que aparece no Finder e no Dock, e é seguro alterar.
Pro display name (opcional, mas recomendado pra clareza):
/usr/libexec/PlistBuddy \
-c "Set :CFBundleDisplayName 'Claude Personal'" \
"/Applications/Claude Personal.app/Contents/Info.plist"
4. Re-assinar o app
Modificar o binário e o Info.plist invalida a assinatura original. O macOS bloqueia o launch de apps com assinatura inválida (e com razão, é uma feature de segurança). Re-assine com ad-hoc:
# Remove atributos de quarentena antes de assinar
xattr -cr "/Applications/Claude Personal.app"
# Re-assina ad-hoc (sem certificado de desenvolvedor)
codesign --force --deep --sign - \
"/Applications/Claude Personal.app"
Gotcha: O
xattr -crantes docodesigné obrigatório, não decoração. O macOS adiciona o atributocom.apple.quarantineem qualquer app copiado ou baixado. Sem remover esse atributo, ocodesignàs vezes falha silenciosamente ou a Gatekeeper rejeita o app no primeiro launch. Você roda o app, ele não abre, e fica achando que o problema é outro.
O --sign - usa assinatura ad-hoc. O app não vai passar pelo processo de notarização da Apple, mas roda normalmente em Macs sem restrições de MDM que bloqueiem apps não notarizados. Pro seu Mac pessoal, suficiente.
O ícone invertido (e a armadilha do Asset Catalog)
Pra distinguir as instâncias visualmente, o ícone da conta pessoal tem as cores trocadas: asterisco laranja em fundo cream (ao invés do asterisco branco em fundo laranja original). É sutil, mas no Dock você bate o olho e sabe qual é qual.
A geração usa um script Python que percorre cada pixel e interpola entre as duas cores alvo proporcionalmente à distância de cada uma no espaço RGB. Esse approach preserva a textura granulada do ícone original (não chapa as cores) e mantém o alpha intacto:
Esquerda: ícone original do Claude (asterisco branco em fundo laranja). Direita: a versão invertida que o invert.py gera. Mesma textura granulada, cores trocadas com gradiente proporcional.
# invert.py
import sys
import math
from PIL import Image
ORANGE = (216, 117, 85)
CREAM = (254, 252, 251)
def color_distance(a, b):
return math.sqrt(sum((x - y) ** 2 for x, y in zip(a, b)))
def swap_pixel(rgba):
r, g, b, a = rgba
if a == 0:
return rgba
d_orange = color_distance((r, g, b), ORANGE)
d_cream = color_distance((r, g, b), CREAM)
total = d_orange + d_cream
if total == 0:
return rgba
t = d_orange / total
new_r = round((1 - t) * CREAM[0] + t * ORANGE[0])
new_g = round((1 - t) * CREAM[1] + t * ORANGE[1])
new_b = round((1 - t) * CREAM[2] + t * ORANGE[2])
return (new_r, new_g, new_b, a)
def process(path_in, path_out):
img = Image.open(path_in).convert("RGBA")
pixels = list(img.getdata())
swapped = [swap_pixel(p) for p in pixels]
out = Image.new("RGBA", img.size)
out.putdata(swapped)
out.save(path_out, "PNG")
if __name__ == "__main__":
process(sys.argv[1], sys.argv[2])
A lógica do swap, em português claro: pra cada pixel, calcule a distância até ORANGE e até CREAM. Quanto mais próximo de ORANGE, mais peso a nova cor recebe de ORANGE (e o pixel "vira" laranja). O t = d_orange / total faz o blend contínuo: pixel exatamente igual a ORANGE vira CREAM (t=0, peso total no cream), pixel exatamente igual a CREAM vira ORANGE (t=1, peso total no orange), e tons intermediários ficam num gradiente proporcional. É inversão simétrica sem threshold, o que evita bandeamento (aquele efeito feio de "escadinha" entre cores).
Pra regenerar o .icns completo se a Anthropic mudar a paleta de cores um dia:
mkdir -p /tmp/ci
iconutil -c iconset /Applications/Claude.app/Contents/Resources/electron.icns \
-o /tmp/ci/orig.iconset
mkdir -p /tmp/ci/new.iconset
for f in /tmp/ci/orig.iconset/*.png; do
python3 ~/.claude/scripts/claude-personal-app/invert.py \
"$f" \
"/tmp/ci/new.iconset/$(basename $f)"
done
iconutil -c icns /tmp/ci/new.iconset \
-o ~/.claude/scripts/claude-personal-app/personal.icns
Depois de gerar o .icns, copie pro app:
cp ~/.claude/scripts/claude-personal-app/personal.icns \
"/Applications/Claude Personal.app/Contents/Resources/electron.icns"
E aqui chega o ponto que me fez perder umas 2 horas e quase mandar o MacBook pela janela.
Depois de trocar o .icns, re-assinar, limpar o cache de ícone do macOS, reiniciar o Dock, o Finder, matar o iconservicesd, sacrificar uma cabra ao deus da Apple... o Dock continuou mostrando os dois ícones idênticos. O arquivo .icns estava visivelmente diferente quando extraído com iconutil, mas o macOS insistia em renderizar o ícone original. Na cara dura.
A causa, depois de muito stack overflow e algumas blasfêmias: versões recentes do Claude Desktop (e de muitos apps Electron modernos) embutem o ícone num Assets.car (Asset Catalog do macOS), referenciado no Info.plist via CFBundleIconName. Quando essa chave existe, o macOS Big Sur+ prioriza o Asset Catalog sobre o CFBundleIconFile, e o .icns é completamente ignorado. Silenciosamente. Sem warning, sem nada.
Verifique se o seu app tem essa chave:
defaults read "/Applications/Claude Personal.app/Contents/Info.plist" CFBundleIconName
# Se retornar "Claude" ou similar, achamos o vilão
Solução: remover CFBundleIconName força o fallback pro electron.icns.
/usr/libexec/PlistBuddy \
-c "Delete :CFBundleIconName" \
"/Applications/Claude Personal.app/Contents/Info.plist"
# Re-assinar depois de mexer no Info.plist
xattr -cr "/Applications/Claude Personal.app"
codesign --force --deep --sign - "/Applications/Claude Personal.app"
# Forçar o macOS a re-ler o ícone
touch "/Applications/Claude Personal.app"
/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister \
-f "/Applications/Claude Personal.app"
killall Dock Finder
Gotcha: Se você mexer no ícone e nada mudar no Dock mesmo depois de limpar todos os caches conhecidos (
com.apple.iconservices.store,iconservicesd, restart do Dock), procure porCFBundleIconNamenoInfo.plist. Em apps Electron modernos é quase certo que essa chave está apontando pra um Asset Catalog que sobrescreve o.icns. Foi essa a sacada que me destravou, e eu não vi documentado em lugar nenhum. Anota aí.
Login inicial: o problema do deep link OAuth
Tem um detalhe que complica o primeiro login na conta pessoal e vale destacar antes que você quebre a cabeça.
O Claude usa OAuth com o esquema claude:// pro callback de autenticação. O macOS roteia esse deep link pra primeira instância registrada como handler desse esquema. Se as duas instâncias estiverem abertas, o callback vai pra conta da empresa (que registrou primeiro), e o login da conta pessoal nunca completa. Trava num loop.
O fluxo correto pro primeiro login:
1. Feche completamente o Claude.app (conta da empresa): Cmd+Q
2. Abra o Claude Personal.app
3. Complete o fluxo de login OAuth
4. Após logar, reabra o Claude.app (conta da empresa)
Gotcha: Isso vale somente pro primeiro login. Depois que a sessão fica persistida no diretório
Claude-Personal/, você pode abrir as duas instâncias ao mesmo tempo sem problema. O OAuth só é necessário uma vez por conta por diretório de dados. Se um dia a sessão expirar, o ritual do Cmd+Q precisa ser repetido.
Manutenção pós-update
O Claude Desktop tem auto-update via Squirrel. Quando sai uma versão nova, o updater substitui os arquivos dentro de /Applications/Claude.app/, preservando o app intocado.
O Claude Personal.app não recebe esse update automaticamente. Precisa ser recriado a partir da versão atualizada. É a parte chata, mas resolvível com script.
Pra isso, o rebuild.sh faz todo o processo de uma vez, incluindo a remoção do CFBundleIconName e o cache bust no final:
#!/bin/bash
# Rebuild Claude Personal.app from current Claude.app.
# Run this after Anthropic releases a new Claude version so Personal stays in sync.
set -euo pipefail
SRC="/Applications/Claude.app"
DST="/Applications/Claude Personal.app"
ICON="$(cd "$(dirname "$0")" && pwd)/personal.icns"
DATA_DIR_NAME="Claude-Personal"
NEW_BUNDLE_ID="com.anthropic.claudefordesktop.personal"
NEW_NAME="Claude Personal"
if [[ ! -d "$SRC" ]]; then
echo "ERROR: $SRC not found." >&2
exit 1
fi
if [[ ! -f "$ICON" ]]; then
echo "ERROR: $ICON not found. Regenerate via invert.py." >&2
exit 1
fi
if pgrep -f "Claude Personal.app" >/dev/null; then
echo "Quit Claude Personal.app before rebuilding." >&2
exit 1
fi
echo "Removing old $DST..."
rm -rf "$DST"
echo "Copying $SRC -> $DST..."
cp -R "$SRC" "$DST"
echo "Replacing icon..."
cp "$ICON" "$DST/Contents/Resources/electron.icns"
echo "Updating Info.plist..."
# IMPORTANT: do NOT change CFBundleName. Electron resolves helper apps by
# CFBundleName ("Claude Helper.app" etc). Changing it causes a FATAL crash:
# "Unable to find helper app". Use CFBundleDisplayName for the user-facing name.
/usr/libexec/PlistBuddy -c "Set :CFBundleDisplayName '$NEW_NAME'" "$DST/Contents/Info.plist"
/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier '$NEW_BUNDLE_ID'" "$DST/Contents/Info.plist"
# Drop CFBundleIconName so macOS falls back to electron.icns (the inverted one).
# Modern Electron apps embed an Asset Catalog (Assets.car) referenced by
# CFBundleIconName, and Big Sur+ prioritizes that over CFBundleIconFile.
# Without this delete, the .icns swap is silently ignored.
/usr/libexec/PlistBuddy -c "Delete :CFBundleIconName" "$DST/Contents/Info.plist" 2>/dev/null || true
echo "Installing wrapper binary..."
mv "$DST/Contents/MacOS/Claude" "$DST/Contents/MacOS/Claude-bin"
cat > "$DST/Contents/MacOS/Claude" <<EOF
#!/bin/bash
DIR="\$(cd "\$(dirname "\$0")" && pwd)"
exec "\$DIR/Claude-bin" --user-data-dir="\$HOME/Library/Application Support/$DATA_DIR_NAME" "\$@"
EOF
chmod +x "$DST/Contents/MacOS/Claude"
echo "Stripping xattrs and re-signing ad-hoc..."
xattr -cr "$DST"
codesign --force --deep --sign - "$DST"
echo "Refreshing Launch Services..."
/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister -f "$DST"
touch "$DST"
killall Dock Finder 2>/dev/null || true
mkdir -p "$HOME/Library/Application Support/$DATA_DIR_NAME"
echo
echo "Done. Open Claude Personal.app from Spotlight."
echo "Data dir: ~/Library/Application Support/$DATA_DIR_NAME"
O rebuild apaga o .app atual e recria do zero. Sessão e configurações ficam intactas porque vivem no diretório de dados (~/Library/Application Support/Claude-Personal/), não dentro do bundle. Esse é o ponto crítico: o app é descartável, o estado é persistente.
A linha 2>/dev/null || true no Delete :CFBundleIconName é defensiva: se uma versão futura do Claude parar de embutir essa chave, o script continua funcionando sem alteração. Custa zero, salva o futuro.
Separar a CLI também (opcional)
Isso não foi necessário no meu caso, mas vale registrar pra quem quiser ir além.
Se um dia fizer sentido usar CLIs com configurações completamente diferentes por conta, basta apontar pra um diretório de configuração distinto:
# Em ~/.zshrc ou ~/.bashrc
alias claude-work='CLAUDE_CONFIG_DIR=$HOME/.claude-work claude'
O ~/.claude/ continua sendo o diretório pessoal por padrão. O alias claude-work usa um diretório separado com suas próprias skills, agents e memória. Cada CLI roda completamente isolada da outra. É a mesma lógica do Desktop, mas com variável de ambiente em vez de wrapper script.
O que ficou no final
Dois ícones distintos no Dock: branco em laranja (empresa) e laranja em cream (pessoal). Acabou a loteria do Cmd+Tab.
As duas instâncias rodam ao mesmo tempo, sem conflito, com sessões totalmente isoladas. CLI compartilhada porque é lá que mora o trabalho real de desenvolvimento, com todos os skills, agents e contexto de projeto centralizado.
Quando sair update novo? Atualiza o Claude.app normalmente, roda rebuild.sh, 30 segundos, voltou pro ar. Workflow resolvido.
Os dois pontos de atenção, gravados a sangue depois dessa tarde:
-
Nunca toque em
CFBundleName. O Electron crasha antes de carregar a janela. -
Delete
CFBundleIconNamese quiser ícone customizado. O Asset Catalog ganha do.icnsno Big Sur+, e ninguém te avisa.
O resto funciona sem segredo.
Se você usa duas contas do Claude (ou tá pensando em separar pessoal e trabalho em qualquer Electron app, porque o truque do --user-data-dir é genérico, vale pra Slack, Notion, VS Code), vale a hora de configuração. Me conta nos comentários se você usou em outro app, quero ver até onde dá pra adaptar o rebuild.sh. Bora trocar figurinha.



Top comments (0)