CLI global da plataforma ETUS. Um binário só, etus, gera os comandos de todos os apps a partir das ferramentas MCP que o hub libera para você: um app novo com ferramentas MCP vira etus <app> <comando> sem nenhuma mudança aqui.
Para agentes de código (Claude Code, Codex) é a interface mais econômica: o agente só lê --help quando precisa e usa --json para receber a resposta compacta.
Contrato completo: docs/specs/cli.md no repositório do hub (etusdigital/nexus-platforms).
curl -fsSL https://apps.etus.io/cli/install.sh | sh
etus --versionO instalador escolhe o binário da sua máquina (macOS Apple Silicon ou Intel, Linux x86_64 ou aarch64), confere o SHA256SUMS e coloca o etus no PATH.
Outras formas:
# Download direto da release (troque pelo nome da sua máquina)
base=https://github.com/evolution-foundation/etus-cli/releases/latest/download
curl -fsSLO "$base/etus-aarch64-macos" && curl -fsSLO "$base/SHA256SUMS"
shasum -a 256 -c SHA256SUMS --ignore-missing
chmod +x etus-aarch64-macos && sudo mv etus-aarch64-macos /usr/local/bin/etus
# etus-aarch64-macos | etus-x86_64-macos | etus-x86_64-linux | etus-aarch64-linux
# Com o GitHub CLI
gh release download --repo evolution-foundation/etus-cli --pattern etus-aarch64-macos --output etus
chmod +x etus && sudo mv etus /usr/local/bin/etusOu compile (Rust 1.89 ou mais novo):
cargo install --git https://github.com/evolution-foundation/etus-cli --lockedDepois, etus atualizar-cli baixa a versão mais nova sozinho, sem token: lê VERSION, o binário e o SHA256SUMS de https://github.com/evolution-foundation/etus-cli/releases/latest/download/ e só troca o binário se o sha256 conferir. ETUS_CLI_DOWNLOAD_BASE aponta para outro endereço (um espelho, por exemplo). Se os arquivos públicos não existirem, usa GH_TOKEN, GITHUB_TOKEN ou gh auth token na API do GitHub, como reserva para um repositório privado.
etus login # abre o navegador; você entra no hub e clica em Permitir
etus whoami # quem está conectado e a empresa ativa
etus apps # apps que você pode usaretus login --no-browsersó mostra o endereço. Se o navegador estiver em outra máquina, cole no terminal o endereço da página de retorno (http://127.0.0.1:<porta>/callback?...).etus login --somente-leiturapede o escopomcp:read: nenhum comando que altera dados aparece.- Precisa da permissão "Conectar agentes externos" no hub, na empresa ativa.
etus logoutrevoga a sessão no hub e apaga o token desta máquina.
No macOS e no Linux o token fica em um arquivo com permissão 0600 na pasta de configuração (tokens-<hash>.json; etus config mostra o caminho), como fazem o gh, o gcloud e o aws. Nada de token aparece em log ou saída, exceto em etus token.
- Por que não o chaveiro por padrão: o binário é assinado ad hoc, então cada versão nova tem outra identidade e o macOS volta a pedir permissão para ler o item
etus-cli. Num agente de código rodandoetussem terminal, essa janela trava o comando. - Quem vem da 0.2.0: o primeiro comando rodado num terminal lê o token do chaveiro uma vez (pode aparecer uma última janela), grava no arquivo e apaga o item do chaveiro. Sem terminal, o etus não tenta ler o chaveiro: sai com código 3 pedindo
etus login. Se a leitura falhar ou for negada, também pedeetus login. - Usar o chaveiro mesmo assim:
etus config armazenamento chaveiro(move o token para lá) ouETUS_TOKEN_STORE=keychainna sessão. Para voltar:etus config armazenamento arquivo. A variável vale mais que a configuração. - Chaveiro sem terminal: com o chaveiro escolhido no macOS, qualquer comando sem terminal (stdin ou stdout que não é terminal, como em agentes e em
$(...)) sai na hora com código 3 em vez de esperar a janela. Para scripts e agentes, use o arquivo. - Windows: continua no Gerenciador de Credenciais por padrão, que não abre janela;
etus config armazenamento arquivotroca para arquivo. - Linux: não há chaveiro suportado; o token fica sempre no arquivo.
MCP_TOKEN=$(etus token) # access token válido, renovado se estiver perto de expirar
etus token --json # {"accessToken": "...", "expiresAt": 1760000000}O token é um segredo: dá acesso à sua conta enquanto valer. Não cole em chat, log, issue nem arquivo versionado. Para não ficar visível na tela, etus token se recusa a imprimir quando a saída é um terminal (código 2); --force imprime mesmo assim. Útil para suítes de avaliação e scripts que chamam o gateway MCP direto.
etus tarefas --help # comandos do app
etus tarefas listar-tarefas --help # descrição completa e parâmetros
etus tarefas listar-tarefas --view today --today 2026-10-14
etus tarefas criar-tarefa --title "Revisar contrato" --due-date 2026-10-20 --yes
etus reunioes buscar-reunioes --q "preço" --json
etus mural --help
etus empresa listar
etus empresa usar etus # id, slug ou nome
etus skills buscar "planilha de custos"
etus skills ler <id>Nomes, pt-BR primeiro:
| App | Nome | Também aceita |
|---|---|---|
| Tarefas | tarefas |
tasks |
| Reuniões | reunioes |
records, meetings, gravador |
| Mural de Reconhecimento | mural |
muraldereconheci, mural-de-reconhecimento |
| Etus Design | design |
etus-design |
| Etus Work | work |
etus-work |
| Extensões de IA | extensoes |
extensions, ext |
| Rotinas | rotinas |
routines |
| ETUS Platforms | hub |
plataforma, platforms |
Um app novo ganha o nome do próprio app no hub, sem acento e até o primeiro "de" (Agenda de Salas vira agenda); o prefixo MCP vira alias. Os comandos seguem o título da ferramenta quando ele é em pt-BR (Criar tarefa vira criar-tarefa); senão, o nome da ferramenta (tasks__create_task vira create-task). A outra forma sempre funciona: etus tasks create-task e etus tarefas criar-tarefa chamam a mesma ferramenta. etus apps mostra os nomes e os aliases, e etus <app> <comando> --help mostra as outras formas aceitas.
Argumentos:
- flags geradas do JSON Schema:
--project-id x, números,--doneou--done false, listas repetindo a flag (--tag a --tag b) ou em JSON (--tag '["a","b"]'), objetos em JSON,nullpara apagar campos que aceitam nulo; - ou o objeto inteiro:
--json '{"title":"x"}',--input args.json,--input -(stdin). Flags somam e têm prioridade sobre o objeto; - obrigatórios e valores permitidos são conferidos antes de chamar o hub.
Ferramentas que alteram dados pedem confirmação no terminal ou --yes (sem terminal, sem --yes, o comando é recusado com código 2). --dry-run mostra a chamada sem executar.
etus setup # regras, plugins e MCP etus em cada editor do projeto (veja abaixo)
etus ativar radar # só os apps que importam neste projeto, com o guia de cada um (veja abaixo)
etus skills sincronizar # uma skill por app que você usa, na pasta do agente (veja abaixo)
etus --help # apps disponíveis (do cache)
etus <app> --help # comandos do app, os que alteram marcados com [altera]
etus <app> <comando> --help # parâmetros, exemplo e se pede --yes
etus <app> <comando> ... --json # resposta em JSON compactoCom --json, a saída é o structuredContent da ferramenta ou o texto dela já decodificado; erros vão para o stderr como {"error":{"kind","message","exitCode"}}.
| Código | Significado |
|---|---|
| 0 | ok |
| 1 | a ferramenta falhou, ou falha de rede ou do hub |
| 2 | uso errado: flag inválida, obrigatório faltando, escrita sem --yes, cancelado |
| 3 | precisa entrar: rode etus login |
| 4 | sem permissão: ferramenta não liberada, conexão só de leitura, sem "Conectar agentes externos" |
| 5 | ação necessária: a ferramenta pediu que você faça algo numa tela do hub (autorizar, conectar, entrar, pedir acesso); o link está no stderr e no JSON do stdout |
Alguns passos precisam de um clique seu por definição: autorizar um app a agir em seu nome, conectar uma conta (Google, GitHub, Slack), entrar, pedir uma permissão a um admin. Nesses casos a ferramenta não falha: devolve um resultado normal com este structuredContent (contrato da plataforma, docs/specs/action-required.md no hub):
{
"status": "action_required",
"action": {
"kind": "consent | connect | login | permission",
"title": "Conectar o Google",
"message": "Conecte sua conta Google para ler a agenda.",
"url": "https://apps.etus.io/...",
"missing": ["google"],
"expiresAt": "2026-10-14T18:00:00Z"
}
}O etus também reconhece o mesmo objeto quando ele vem só como JSON no texto da ferramenta. Ao receber:
- stdout fica igual a qualquer outra resposta: o resultado da ferramenta (com
--json, o objeto acima compacto), para o agente ler o link; - stderr mostra o título, a mensagem, o que falta e o link (
Abra: https://...); com--json, o erro traz tambémaction:{"error":{"kind":"action_required","message","exitCode":5,"action":{...}}}; - código de saída 5, para scripts e agentes detectarem sem ler o texto.
O etus nunca faz a ação por você: ele só mostra o link (e abre o navegador, se você pedir). Só abre links https (ou http em localhost).
etus <app> <comando> --open # abre o link no navegador (também --abrir)
etus <app> <comando> --wait # mostra o link, espera você concluir e repete a chamada (até 300 s)
etus <app> <comando> --wait=600 --open
etus config abrir-links sim # abre sozinho quando há terminal (nao para desligar)--openabre sempre, com ou sem terminal. Sem a flag, o link só abre sozinho cometus config abrir-links sim(open_actionsno arquivo) ouETUS_OPEN_ACTIONS=1, e ainda assim só quando stdin e stderr são um terminal; agentes rodando sem terminal nunca abrem o navegador sem--open.--wait[=SEGUNDOS](também--aguardar) repete a mesma chamada, com os mesmos argumentos e a mesma confirmação, a primeira vez depois de 2 s e depois a cada 5 s, até a resposta não ser maisaction_requiredou o tempo acabar (padrão 300 s, no máximo 1800). Concluiu: imprime o resultado final e sai com o código dele. O tempo acabou: imprime o último resultado e sai com 5. Num agente, só use--waitquando a pessoa estiver por perto para clicar.- As flags valem antes do app (
etus --open tarefas ...) ou depois do comando. Por issoopen,abrir,waiteaguardarpassam a ser nomes reservados: um parâmetro de ferramenta com esse nome vira--arg-open, como já acontecia comjsoneyes.
etus skills sincronizar (também etus skills sync) gera uma skill por app que você pode usar na empresa ativa, para o agente saber de cara o que cada app faz e quais comandos chamar, sem redescobrir com --help a cada conversa. A regra geral do etus (login, empresa, JSON, códigos de saída, confirmação) fica na skill escrita à mão etus-cli, do pacote etus-core do etus-skills.
etus login
etus skills sincronizar # Claude Code: ~/.claude/skills/etus-<app>/SKILL.md
etus skills sincronizar --agente codex # Codex: ~/.agents/skills/etus-<app>/SKILL.md
etus skills sincronizar --agente todos # os dois
etus skills sincronizar --projeto # pastas do repositório: .claude/skills e .agents/skills
etus skills sincronizar --pasta ./skills # uma pasta qualquer (vale mais que --agente e --projeto)
etus skills sincronizar --dry-run # mostra o que mudaria, sem gravar
etus skills sincronizar --json # {"pastas","criadas","atualizadas","removidas","iguais","ignoradas"}- Do seu catálogo, na hora. Renova o catálogo como
etus atualizar, então respeita a empresa ativa, o--perfile o login só leitura: só entram os apps e comandos liberados para você. - O que cada skill traz. Para que o app serve, como chamar, cada comando com aliases, resumo, se lê, altera ou é destrutivo, a permissão exigida e os parâmetros (obrigatórios com o tipo; opcionais pelo nome), as receitas e os cuidados que o app declara no manifesto (bloco
guide) e as regras de confirmação e de ação necessária. App com mais de 60 comandos lista só os obrigatórios. - Guia do app. Vem de
etus_detalharcomapp:<prefixo>, no hub. Num hub que ainda não serve o guia, a skill sai só das ferramentas, sem receitas nem permissões, e o etus avisa no stderr. - Nome e dono. A pasta é
etus-<app>, com o mesmo nome deetus apps.etus-clié reservado para a skill geral: nunca é gravada nem apagada aqui. Toda skill gerada tem a linha<!-- gerado por etus skills sincronizar: não edite, rode o comando de novo -->logo depois do frontmatter. - Só mexe no que gerou. Atualiza só o que mudou e remove só as pastas
etus-*com essa linha cujo app saiu da sua lista (perdeu acesso, app removido, outra empresa). UmSKILL.mdcom o mesmo nome feito à mão fica como está e aparece como ignorado. Pastas que são links simbólicos não são seguidas. - Idempotente. Sem data no conteúdo: rodar de novo sem mudança no hub não grava nada.
- Rode de novo depois de
etus empresa usar, de ganhar ou perder acesso a um app, ou quando um app publicar versão nova. Depois de gravar, abra uma conversa nova no agente para ele carregar as skills.
etus skills limpar-globais (também clean-global) apaga de ~/.claude/skills e ~/.agents/skills só as pastas etus-<app> com a linha de etus skills sincronizar; as feitas à mão e a etus-cli ficam. Aceita --dry-run e --json.
etus setup (também init) deixa o projeto pronto para os agentes da Etus em cada editor: regras da Etus, plugins dos perfis no Claude Code, o MCP etus e o .etus.json. Precisa de etus login. É o mesmo setup de npx github:evolution-foundation/etus-skills setup: quando a CLI está instalada, o etus-skills delega a ela.
etus setup # detecta o perfil; plataformas do .etus.json (ou todas, na primeira vez)
etus setup codigo,design --projeto ENG # soma perfis; o projeto do Plane vai para o .etus.json
etus setup --target cursor,codex # soma plataformas às do .etus.json (--substituir-alvos troca a lista)
etus setup design --com-templates # liga também os plugins opcionais do perfil
etus setup --mcp-modo completo # modo do MCP etus: quente (padrão), compacto ou completo
etus setup -i # pergunta perfil e plataformas
etus setup sincronizar # aplica de novo o .etus.json, sem login (também `sync`)
etus setup diagnosticar # confere tudo; sai com 1 se houver falha (também `doctor` e `etus doctor`)
etus doctor --maquina # confere as ferramentas desta máquina pelo perfil (ver abaixo)
etus --dry-run setup # mostra o que mudaria, sem gravarO que cada editor recebe:
Plataforma (--target) |
Regras | MCP etus |
Skills dos apps (etus ativar) |
|---|---|---|---|
Claude Code (claude-code) |
plugins do perfil em .claude/settings.json |
.mcp.json (type: http) |
.claude/skills |
Cursor (cursor) |
.cursor/rules/etus.mdc |
.cursor/mcp.json |
.agents/skills, ou .claude/skills, que o Cursor também lê |
Codex (codex) |
AGENTS.md |
.codex/config.toml (só em projeto confiável) |
.agents/skills |
Gemini CLI e Antigravity (antigravity, gemini) |
GEMINI.md |
.gemini/settings.json (httpUrl) e .agents/mcp_config.json (serverUrl) |
.agents/skills |
GitHub Copilot (copilot) |
.github/copilot-instructions.md |
.mcp.json, o mesmo do Claude Code (VS Code e Copilot CLI) |
.agents/skills, ou .claude/skills, que o Copilot também lê |
Windsurf (windsurf) |
.windsurfrules |
~/.config/devin/mcp_config.json (usuário; o legado ~/.codeium/windsurf/mcp_config.json quando é ele que existe) |
.agents/skills |
pi (pi) |
AGENTS.md (o mesmo bloco do Codex) e os packs do perfil no pacote etus-pi do settings.json global do pi (nunca no .pi/settings.json) |
.pi/mcp.json |
.agents/skills |
- Regras. Ficam entre
<!-- etus-skills:begin -->e<!-- etus-skills:end -->; o resto do arquivo fica como está. Arquivo existente sem os marcadores só com--force. AGENTS.mdcomo fonte única. Com o Codex ou o pi entre as plataformas, as regras completas ficam só noAGENTS.md; Cursor, Copilot, Windsurf e Antigravity recebem um bloco curto mandando ler oAGENTS.md(mais as diretrizes próprias de cada um), como no etus-skills. Sem os dois, cada um recebe as regras completas. Codex e pi juntos gravam um bloco só noAGENTS.md.- Plugins. Os do perfil (
.claude-plugin/profiles.jsondo etus-skills, embutido na CLI; oetus-skillspassa o dele emETUS_SKILLS_PROFILES). Só acrescenta: marketplaceetuscom origem customizada e plugin desligado (false) ficam como estão. - MCP
etus. Endereço<hub>/mcp?modo=quentepor padrão. No Claude Code que passa pelo AI Router o carregamento sob demanda de ferramentas fica desligado, e o modocompletoporia todas as ferramentas em toda sessão; oquentetraz as mais usadas mais as de catálogo (etus_buscar,etus_detalhar,etus_consultar,etus_executar). Umetusque já existe com outro endereço nunca é trocado sem--mcp-modo: o setup avisa e mantém. Os outros servidores do arquivo ficam como estão. O login é OAuth, feito no editor (o setup diz onde), e nenhum token vai para arquivo. - Claude Code:
.mcp.json, nãoclaude mcp add -s local. O arquivo é declarativo e versionável (o time tem o mesmo modo, sem segredo), funciona sem oclaudeinstalado, entra no--dry-rune nodiagnosticar, e serve também ao Copilot. Se houver umetuslocal para a pasta (~/.claude.json), que vale mais que o.mcp.json, o setup avisa como removê-lo. - pi: pacotes só no global. O pi instala pacote de projeto em
<projeto>/.pi/git/...(ou.pi/npm/...), o AI Router apaga do system prompt todo parágrafo que contém/.pi/, e o pi lista todas as skills, as do projeto e as globais, num parágrafo só (o bloco<available_skills>): um único pacote no projeto bastava para o modelo não ver nenhuma skill. Por isso o setup soma os packs do perfil ao pacotegit:github.com/evolution-foundation/etus-skills@<tag>(o manifestopidopackage.jsondo etus-skills; a tag é a da versão da CLI,pi::PACKAGE) nosettings.jsonglobal do pi, na pasta que o pi usa agora (oPI_CODING_AGENT_DIRdo ambiente ou do perfil do shell; sem ele,~/.pi/agent, e aí o setup avisa para rodaretus router setup --pi), com o filtroskillsemplugins/<pack>/skills/**; o packnexus-stackentra como o pacotegit:github.com/etusdigital/nexus-stack@v0.27.1. Só soma: packs e pacotes que já estão lá ficam, nada se repete, um pacote do etus-skills com outro ref passa para a tag da CLI, e o arquivo ganha backup quando muda. No pi, os packs do perfil valem em todos os projetos, não só neste. O pi tem um ajuste por projeto que reaproveita o pacote global sem instalar nada em.pi/(a entrada com"autoload": falseque opi config -lgrava), mas ele não está na documentação do pi e, se o pacote some do global, o pi volta a instalar em.pi/git; a CLI não usa. OAGENTS.mde o.pi/mcp.jsoncontinuam no projeto (nenhum dos dois põe/.pi/no prompt), e as skills dos apps doetus ativarficam em.agents/skills, fora do.pi/. Pelo mesmo motivo, não ponha skills em.pi/skillsdo projeto. O pi só lê.pi/mcp.jsone.agents/skillsdepois que você confia no projeto (ele pergunta na primeira vez); oAGENTS.mdvale sempre. Login do MCP:pi mcp login etus. - pi: migração do
.pi/settings.json. Os pacotes que a CLI gravava lá até a 0.12.1 (os do etus-skills e do nexus-stack, reconhecidos pela origem) vão para o global com os packs do filtro deles e saem do projeto. O arquivo antigo fica em.pi/settings.json.bak(um só; apague depois de conferir), e o.pi/settings.jsoné apagado quando fica vazio. Pacotes que você pôs à mão ficam onde estão: o setup, oetus setup diagnosticare oetus doctor --maquinaavisam que eles escondem as skills e dão o comando para mover (pi remove -l <origem> && pi install <origem>). Uma entrada"autoload": falsecujo pacote está no global não é acusada. - Não suportado. Copilot coding agent na nuvem não aceita MCP com OAuth. O Windsurf não lê MCP do projeto, por isso o
etusvai no arquivo do usuário. O Codex só lê.codex/config.tomlde projeto confiável. - Skills globais. Se houver skills de
etus skills sincronizarem~/.claude/skillsou~/.agents/skills, o setup sugereetus skills limpar-globais: no global fica só aetus-cli, e o resto vai por projeto cometus ativar.
etus doctor --maquina [PERFIL] (também etus setup diagnosticar --maquina) confere se esta máquina tem o que o perfil precisa. Só lê: nunca instala nem altera nada, e nunca mostra chave nem token. Sai com 1 se um componente obrigatório estiver bloqueado. No fim, envia o relatório da máquina ao Etus Extensões (veja "Relatório da máquina", abaixo).
etus doctor --maquina # perfil desta máquina (o que o etus router setup fixou)
etus doctor --maquina produto,design # confere outros perfis só desta vez (soma os componentes; não troca o da máquina)
etus --json doctor --maquina # inventário em JSON, sem segredos e com ${HOME} no lugar da pasta do usuário- Manifesto.
environment.json, ao lado doprofiles.jsonno etus-skills (.claude-plugin/environment.json), com uma cópia embutida na CLI.ETUS_SKILLS_ENVIRONMENTaponta para outro arquivo; comETUS_SKILLS_PROFILES, vale oenvironment.jsonda mesma pasta, se existir. Cada componente declaraid,name,required(o perfil pode deixá-lo opcional emoptional), o comando de versão com a mínima, as provaspresent,configuredeworks(um comando que precisa sair com 0, ou uma prova interna da CLI) e o comando sugerido emfix. - Quatro estados.
presente(instalado na versão mínima),configurado,funcionalebloqueado(falta, versão velha ou uma prova falhou). Quando uma prova não pôde ser conferida (sem rede, por exemplo), o componente fica no último estado alcançado, com aviso. - Perfil
codigo. git (autor configurado), gh (logado e a API aceita), Node 24, pnpm, Docker (engine respondendo), Docker Compose, Claude Code (versão mínima), RTK (hook no Claude Code ertk gain),etus(logado e igual à última release publicada), chave do AI Router (presente, no cofre e lida peloapiKeyHelper, pela mesma leitura doetus router status), MCPetus(no projeto ou no usuário, em modo quente, e o hub respondendo aowhoami), Orca (opcional) e pi (opcional: pacote etus-pi nosettings.json, nenhum pacote que o pi carregue de.pi/no.pi/settings.jsondo projeto, provideretusdo AI Router com a chave pelo comando nomodels.json, e oetusem modo quente nomcp.jsonda pasta do pi). - Relatório da máquina. No fim do
etus doctor --maquinae doetus router setup, a CLI envia ao Etus Extensões, por REST (o hub troca o seu login por um token de 5 minutos só para isso, e nenhum agente vê uma ferramenta de relatório), o mesmo inventário do--json, para o admin ver quais máquinas estão com a etus ou a receita desatualizadas e você ver as suas em "Meu perfil". Vai: um id aleatório desta máquina (gerado uma vez e guardado emmachine-report.json, na pasta de configuração do etus; nunca o nome da máquina, o usuário ou um id de hardware), sistema e arquitetura, a versão do etus, as ferramentas do manifesto com versão e status, o perfil de trabalho com a origem e se está fixado, a tag da receita e os itens da seção "Receita do perfil" com o comando que corrige. Não vão o nome da máquina, o seu usuário nem segredos: a pasta do usuário vira${HOME}, o que parece chave ou token vira[removido]e cada texto é cortado no limite do Extensões. É melhor esforço, com tempo curto: sem login, fora do ar ou recusado, o comando segue igual e o código de saída não muda. Sai uma linha no stderr dizendo que foi enviado (--quietesconde); na primeira vez, um aviso do que é enviado; quando o hub ou o Extensões não liberam o envio (no_app_accessoupermission_required: app Etus Extensões não liberado para você, falta da permissão "Enviar o relatório da máquina" ou sem empresa ativa), um aviso (uma vez só) com essas causas. Com login só de leitura (etus login --somente-leitura), não tenta enviar. Não envia com--dry-run, com--sem-relatorio(também--no-report), comETUS_NO_MACHINE_REPORT=1nem noetus doctor --maquina <PERFIL>, que confere outro perfil só daquela vez.ETUS_DEBUG=1mostra no stderr por que não foi enviado. - Checagens extras. Hook do RTK registrado mais de uma vez no
~/.claude/settings.json;nexus-stack@nexus-stackenexus-stack@etusligados juntos;ANTHROPIC_AUTH_TOKENouANTHROPIC_API_KEYexportados no shell passando na frente doapiKeyHelper; skills globais geradas sobrando (sugereetus skills limpar-globais); worktrees de repositórios em~/Projectscujo branch já está no principal ou teve o remoto apagado (só lista).
Um arquivo só, na raiz do repositório, versionável, lido e gravado pelo etus setup, pelo etus ativar e pelo etus-skills. Chaves que a CLI não conhece ficam como estão.
{
"project": "ENG",
"workspace": "etus",
"perfil": ["codigo"],
"targets": ["claude-code", "cursor", "codex"],
"mcp": { "modo": "quente" },
"active": {
"apps": ["etusradar"],
"packs": { "stalk": ["etusclone", "etuspersonas", "etusradar"] },
"agents": { "stalk": ["etusradar"] }
}
}| Chave | O que é | Quem grava |
|---|---|---|
project |
Chave do projeto no Plane (maiúsculas); sem ela, as regras usam o nome do repositório | setup --projeto |
workspace |
Workspace do Plane (padrão etus) |
setup --workspace |
perfil |
Lista de perfis do etus-skills; cada setup <perfil> soma |
setup |
targets |
Plataformas: claude-code, cursor, codex, antigravity, copilot, windsurf, pi. Ausente num arquivo antigo do etus-skills com perfil: só claude-code |
setup --target |
mcp.modo |
quente, compacto ou completo; ausente vale quente |
setup --mcp-modo |
active |
Apps, pacotes e agentes ativos neste projeto | etus ativar e etus desativar |
As skills globais entram em toda sessão, de qualquer projeto. etus ativar (também activate) põe só o que importa dentro do projeto:
etus ativar radar personas # apps, pelos nomes de `etus apps`
etus ativar arbitragem # um pacote: a categoria do app no hub
etus ativar stalk # pacote fixo radar, personas e clone (quando nenhuma categoria do hub tem esse nome)
etus ativar agente:stalk # um agente do Etus Agentes e os apps das ferramentas dele
etus ativar # gera de novo tudo o que já está ativo
etus ativar --agente todos radar # .claude/skills e .agents/skills (padrão: as plataformas do .etus.json)
etus ativos [--json] # o que está ativo e por quê (também `active`)
etus desativar arbitragem # tira e apaga os arquivos gerados (também `deactivate`)- Raiz e estado. A raiz é a do repositório git (ou a pasta atual fora de um), a mesma do
etus setup. O que está ativo fica na chaveactivedo.etus.jsonda raiz (apps,packs,agents); as outras chaves do arquivo ficam como estão. O.etus.jsoné versionável: o time ativa a mesma lista. - App. Grava
.claude/skills/etus-<app>/SKILL.md(Claude Code) e.agents/skills/etus-<app>/SKILL.md(a pasta aberta do padrão SKILL.md, lida por Codex, pi, Cursor, Copilot, Windsurf, Gemini CLI e Antigravity) conforme as plataformas do.etus.json. Cursor e Copilot também leem.claude/skills, então sem Codex, pi, Gemini, Antigravity nem Windsurf a cópia em.agents/skillsnão é feita. Semtargetsno.etus.json: Claude Code, mais.agents/skillsse o projeto temAGENTS.mdou.agents. O conteúdo é o da mesma geração deetus skills sincronizar: comandos, permissões, e o guia do app (para que serve, receitas e cuidados) vindo deetus_detalharcomapp:<prefixo>. App sem guia publicado sai só com os comandos, e oetus ativardiz quais. - Pacote. Uma categoria de apps do hub (
arbitragem,produtividade...), ou o pacote fixostalk(radar, personas e clone) quando nenhuma categoria tem esse nome. A lista de membros é gravada no.etus.json. - Agente.
agente:<slug>(ouagent:<slug>) lê o agente pelo Extensões (extensoes__catalogo_lercomagente:agentes:<slug>), grava.claude/agents/etus-<slug>.mdcom as instruções dele e ativa os apps dos prefixos das ferramentas dele. Sem acesso, o etus mostra o link da ação necessária (código 5) ou o motivo (código 4). - Arquivos gerados fora do git. Cada arquivo gerado entra no
.git/info/exclude, como oetus trabalhofaz com osettings.local.json: o conteúdo depende da empresa ativa e do acesso de cada pessoa, então cada um gera o seu a partir do.etus.json. Arquivo que o repositório já versiona fica versionado. - Só mexe no que gerou. Os arquivos levam a linha
<!-- gerado por etus ativar: não edite, rode o comando de novo -->. Uma skill deetus skills sincronizar --projetocom o mesmo nome é atualizada, mas só as deetus ativarsão apagadas; as feitas à mão aparecem como ignoradas. - Dica. Se houver skills globais de
etus skills sincronizar, o fim doetus ativarsugereetus skills limpar-globais.
etus trabalho marca em qual item do Plane você está trabalhando, para o uso de IA ser medido por empresa, projeto e item no painel do roteador. Contrato completo: docs/specs/work-context.md no repositório do hub.
etus trabalho ENG-123 # marca o item; empresa = empresa ativa do etus
etus trabalho ENG-123 --fase review # plan, impl, review ou qa
etus trabalho ENG-123 --projeto hub:<id> # projeto da Etus: hub:<id>, chat:<id> ou rotina:<id>
etus trabalho ENG-123 --empresa <id> # outra empresa (id da empresa no hub)
etus trabalho --etiqueta estudo-cache # trabalho fora do Plane
etus trabalho --fase qa # só troca a fase do que já está marcado
etus trabalho # o mesmo que `etus trabalho mostrar`
etus trabalho mostrar # estado e a linha do header que vai sair
etus trabalho cabecalho # só a linha x-etus-work, para hooks e scripts
etus trabalho limpar # apaga a marca (volta ao que o branch disser)
etus trabalho handoff # comentário de passagem no item do Plane (ver abaixo)- Persiste até mudar. Dentro de um repositório, o estado fica em
<git dir>/etus-work(um por worktree); fora, em~/.claude/etus-work(ou$CLAUDE_CONFIG_DIR/etus-work). O formato é o mesmo do utilitário do instalador do roteador (issue,phase,tag,workspace), com as chaves novascompanyeproject. - Empresa automática. Vem da empresa ativa guardada no cache do catálogo (
etus empresa usar), sem chamar a rede. Sem login nem--empresa, fica vazia. - Item pelo branch. Sem item marcado, vale a chave escrita no branch (
feat/eng-123-uploadviraENG-123), como no hook. - Versão do header.
v=2só quando há empresa ou projeto (c,pjevia); senãov=1, que todo leitor antigo entende. A linha tem no máximo 256 caracteres: se passar, cortam-se a etiqueta, o repositório e o workspace, nunca empresa, projeto ou item. --jsonemmostrartrazversion,item,planeProject,project,company,tag,repo,workspace,phase,source,headerestatePath.
etus trabalho handoff grava no item marcado (ou --item ENG-123) um comentário para a próxima pessoa, pela ferramenta plane__add_comment do app Plane:
etus trabalho handoff # no terminal, pergunta o que faltar e pede confirmação
etus trabalho handoff --objetivo "Doctor da máquina" --aceite "roda nesta máquina" \
--evidencia "cargo test: 0 falhas" --falta "release" --bloqueio "Docker parado" \
--recurso "porta 5011" --reconferir "saída do doctor no Linux" --yes
etus trabalho handoff --dry-run # mostra a chamada, sem gravarO comentário traz objetivo e critério de aceite, o commit conferido (git rev-parse HEAD, branch e quantos arquivos estão alterados sem commit), a evidência (comandos rodados e resultado), o que falta, bloqueios, recursos ocupados (a worktree atual entra sozinha) e o que reconferir. O que não foi informado aparece como "não informado". Como toda escrita da CLI, pede confirmação no terminal e, fora dele, só grava com --yes.
Os comandos que o etus router setup registra no Claude Code (etus hook em SessionStart, CwdChanged, FileChanged e UserPromptSubmit, e o etus statusline) falham abertos: com stdin vazio, null, JSON inválido, campos faltando ou o config.json do etus quebrado, saem com 0 sem escrever nada no stdout além do JSON do hook (ou da linha da statusline). O túnel da saída pela casa (etus router tunel) nunca escreve no stdout e só sai com 0 ou 2. tests/hooks_contract.rs confere tudo isso.
- O Claude Code manda em toda chamada os headers de
ANTHROPIC_CUSTOM_HEADERS(um por linha,Nome: valor), lidos doenvdos arquivos de configuração. - Com o comando
etus router setup, a CLI configura o Claude Code nativamente, registrando hooks emSessionStart,CwdChanged,FileChanged(no.git/HEAD) eUserPromptSubmit. A cada evento, o hook lê o arquivo de estado e reescreveANTHROPIC_CUSTOM_HEADERSem.claude/settings.local.jsondo projeto com a linhax-etus-work, mantendo os outros headers que você já tinha. - Além disso, comandos como
etus trabalho <item>atualizam imediatamente o.claude/settings.local.jsonda pasta atual (garantindo também que o arquivo seja ignorado no.git/info/exclude), de modo que as alterações passem a valer a partir do próximo prompt sem reiniciar o Claude Code. O roteador remove o header antes de chamar o provedor.
etus login # uma vez por computador
etus router setup # pega a chave deste computador pelo login, sem colar nada
etus router setup --model dev-continuo # define modelo principal e rápido
etus router setup <SUA_CHAVE> # plano B: informa a chave à mão (também OMNIROUTE_API_KEY)
etus router setup --pi # também configura o pi, mesmo sem ele no PATH (--sem-pi pula)
etus router setup --perfil-trabalho design # troca o perfil desta máquina e aplica a receita dele (regras, políticas, automações e modelo)
etus router setup --sem-regras # não mexe no CLAUDE.md nem nos AGENTS.md
etus router setup --sem-relatorio # não envia o relatório da máquina ao Etus Extensões no fim
etus --dry-run router setup # mostra o que mudaria, sem gravar nada e sem buscar a chave
etus router status # inspeciona a configuração atual e a origem da chave
etus router # o mesmo que etus router status
etus router chave # imprime a chave (é um segredo; quem chama é o Claude Code)
etus router eval -- <args do plugin eval> # roda o claude plugin eval pelo roteador (também avaliar)- Uma chave por computador. Sem
--chave, oetus router setuptroca a sessão doetus loginpor um token de repasse do hub (60 s, só para o approuter-panel) e pede ao AI Router uma chave para este computador, com o nome da máquina (hostname). A chave aparece no painel do roteador, em Minha chave, e pode ser revogada lá sem afetar os outros computadores. O uso, o orçamento e os limites continuam os da sua chave pessoal: o roteador troca a chave do computador pela sua. - Rodar de novo troca a chave. Cada
etus router setuppede uma chave nova e revoga a anterior deste computador (sessões abertas do Claude Code pegam a nova em até 5 minutos). - Sem texto puro. A chave fica no mesmo lugar do token do login (arquivo 0600 por padrão, ou o chaveiro com
etus config armazenamento chaveiro). O~/.claude/settings.jsonrecebe"apiKeyHelper": "etus router chave"e perde oANTHROPIC_AUTH_TOKEN(e oANTHROPIC_API_KEY). Uma chave antiga em texto puro nosettings.jsonvai para o cofre quando não há login. - Como o Claude Code lê. O Claude Code roda o
apiKeyHelperao abrir e a cada 5 minutos (padrão doCLAUDE_CODE_API_KEY_HELPER_TTL_MS) e manda o valor emAuthorization: Bearere emx-api-keypara oANTHROPIC_BASE_URL.ANTHROPIC_AUTH_TOKENouANTHROPIC_API_KEYexportados no shell têm prioridade sobre oapiKeyHelper; oetus router statusavisa quando isso acontece. Com o chaveiro no macOS, oapiKeyHelpernão roda num terminal e falha: use o arquivo. etus router chaveimprime um segredo. Recusa quando a saída é um terminal (--forceimprime mesmo assim). Não cole a chave em chat, log ou arquivo versionado.claude plugin evalpelo roteador. O avaliador de plugins roda cada caso num Claude Code isolado, com HOME e pasta de configuração próprios: ele herda as variáveisANTHROPIC_*(do shell e doenvdosettings.json), mas não oapiKeyHelper, e o caso para em "Not logged in".etus router eval -- <argumentos>lê a chave deste computador do cofre e rodaclaude plugin eval <argumentos>comANTHROPIC_AUTH_TOKEN,ANTHROPIC_BASE_URL(o roteador da chave) e os modelos doenvdosettings.json(padrãodev-continuoerapido) só no ambiente daquele processo: nada é gravado em arquivo nem impresso, e o Claude Code não repassa oANTHROPIC_AUTH_TOKENaoBashdo agente. Ponha--antes dos argumentos (o--jsondo eval não vira o--jsondo etus). Sem chave no cofre e sem oapiKeyHelperdo etus, roda oclaude plugin evalsem mudar nada; com oapiKeyHelperdo etus e sem chave, pedeetus router setup. Exemplo:etus router eval -- plugins/etus-produto --case nao-se-aplica --trust-plugin --no-publish.- pi. Com o
pino PATH (ou umsettings.jsonna pasta dele), ou com--pi, oetus router setuptambém configura o pi na pasta~/.etus/pi(ou noPI_CODING_AGENT_DIRque você já definiu no ambiente ou no perfil do shell), com backup de cada arquivo que muda: nomodels.json, o provideretus(anthropic-messages, a URL do roteador, os modelos principal com raciocínio e rápido, e"apiKey": "!etus router chave", que o pi roda a cada requisição); nosettings.json,defaultProvideretus,defaultModeligual ao--model(sem ele, oai.modeldo perfil),defaultThinkingLeveldoai.thinkingdo perfil (hojehigh),enableInstallTelemetryfalse, o pacote etus-pi com as skills doetus-core(packs que você somou ao filtro ficam), onpm:pi-subagents@0.79.1(o pi instala na primeira execução) e, emskills, as pastas de~/.claude/skillsuma a uma; nomcp.json, oetusem modo quente (umetusque já existe com outro endereço fica como está). A chave nunca vai para esses arquivos: uma chave em texto puro no provideretusé trocada pelo comando. Os outros providers, servidores e pacotes ficam como estão. Oetus router statusmostra o pi também, com a pasta que ele usa e de onde ela veio. - Por que
~/.etus/pi. O AI Router apaga do system prompt todo parágrafo que contém/.pi/, e o pi lista as skills dos pacotes (instalados em<pasta do pi>/git/...) num parágrafo só: com a pasta padrão~/.pi/agent, o modelo não vê nenhuma skill de pacote. Por isso o setup usa~/.etus/pie gravaexport PI_CODING_AGENT_DIR="$HOME/.etus/pi"num bloco marcado (# >>> etus pi >>>) no perfil do shell: o do seu$SHELL(~/.zshrc,~/.bashrcou~/.config/fish/config.fish) e os outros desses que já existem (no macOS, também o~/.bash_profile). Rodar de novo não duplica o bloco. UmPI_CODING_AGENT_DIRque você já definiu (no ambiente ou fora do bloco, no perfil) é respeitado e o perfil fica como está. No Windows, a variável vai para as variáveis do usuário comsetx. Abra um terminal novo depois do setup. - Migração do
~/.pi/agent. Na primeira vez, o setup copia para a pasta nova o que existe no~/.pi/agent:settings.json,models.json,mcp.json,auth.json,mcp-auth.json,models-store.json,keybindings.json,AGENTS.md,SYSTEM.md,APPEND_SYSTEM.mde as pastasextensions(inclusive as do Orca),gitenpm(os pacotes instalados; sem elas o pi instala de novo),skills,prompts,themes,toolsebin. O que já existe na pasta nova não é sobrescrito, e o~/.pi/agentfica como estava (serve de backup, com assessionse a instalação do próprio pi). Oextensions/etus.tsdo antigosetup-pi.shnão é copiado (a extensão do pacote etus-pi já manda a sessão) e, se estiver na pasta do pi, é apagado com backup. - Skills do Claude Code no pi. O
settings.jsonnão aponta para~/.claude/skillsinteira: o~/.claude/skills/syncedtem uma pasta por conta do app do Claude com as mesmas skills, e o pi acusa conflito. Entram as pastas próprias (comSKILL.md), uma só dosynced(a delastUpdatedmais recente nomanifest.json; se você já escolheu uma, ela fica) e nada que já esteja em~/.agents/skills, que o pi lê sozinho. Uma entrada com a pasta inteira ou com outra pasta dosyncedsai. etus doctor --maquinae o pi. Acusa falha quando a pasta que o pi usa contém/.pi/, ou quando o.pi/settings.jsondo projeto onde você roda tem pacotes que o pi carrega de.pi/(diz quais e como mover), e confere os pacotes etus-pi epi-subagents. Oetus router statusmostra o mesmo aviso (--json:projectPackagesHidden).- Sem acesso. Sem a permissão "Usar a IA" do AI Router, o comando mostra o link para pedir acesso no hub e sai com código 5; depois de aprovado, rode de novo.
etus logoutrevoga a chave deste computador no roteador e apaga a do cofre (uma chave informada à mão só é apagada). O Claude Code volta a usar o roteador depois deetus logineetus router setup. Também desliga a saída pela casa.
O etus router setup aplica, no nível do usuário, a receita do perfil de trabalho que o etus-skills publica: regras, políticas, automações e modelo (docs/convencoes.md do etus-skills, seções Regras, Políticas, Automações e Perfil v2). É ele que faz isso porque é o comando da máquina: já grava o ~/.claude/settings.json, os hooks do etus e a pasta do pi, e é o que o etus doctor --maquina manda rodar. O etus setup continua cuidando do projeto (e, no pi, dos packs do perfil no settings.json global).
-
Perfil da máquina. A receita grava arquivos que valem para a máquina toda (o bloco do
~/.claude/CLAUDE.md, opermissions.denydo Claude Code, osettings.jsondo pi), então ela segue um perfil fixo da máquina, guardado emrecipe-state.jsonna pasta de configuração do etus, e não a pasta onde você roda o comando: rodar o setup num repositório de design e depois num de código não troca a receita.- Na primeira vez, sem perfil guardado, o
etus router setuppergunta qual perfil usar (a lista vem da receita). A sugestão é, primeiro, o perfil de trabalho padrão que o hub guarda para você na empresa ativa (definido para você, para um time seu ou para um tipo de acesso, em Administração > Perfil de trabalho;GET /api/v1/mcp/work-profile, com o mesmo login do MCP), quando a receita desta versão do etus conhece esse perfil; depois, a da pasta atual: o.etus.jsondo projeto, a detecção pelo projeto oucodigo. Sem login, com o hub fora do ar (espera até 3 s) ou num hub sem esse recurso, a sugestão é a da pasta, sem aviso (ETUS_DEBUG=1diz por quê). Fora do terminal, com--jsonou com--yes, ele fixa essa sugestão e diz isso na saída (Perfil: ... fixado como perfil desta máquina; no--json,receita.fixadoAgora), com a origem:padrão do hub na primeira configuração (time Plataforma, empresa Etus)ousugestão da pasta na primeira configuração (...). - Depois, o perfil da máquina só muda com
--perfil-trabalho <perfil>(também--work-profile; vários com vírgula), que aplica a receita nova e guarda a escolha. O.etus.jsone a detecção da pasta atual não mudam mais o setup da máquina, oetus doctor --maquinanem oetus router status. - O
etus doctor --maquinae oetus router statusdizem qual perfil da máquina está valendo e de onde veio (origemDoPerfileperfilFixadono--json). Se o.etus.jsondo projeto atual pede outro perfil, eles avisam numa linhaProjeto:(notaDoProjeto), só como informação: não é diferença. Se o padrão do hub para você é outro perfil, oetus doctor --maquinaavisa numa linhaHub:(notaDoHub), também só como informação, com o comando para trocar (etus router setup --perfil-trabalho <perfil>): o padrão do hub nunca troca um perfil já fixado. Com o perfil fixado, só o doctor pergunta ao hub; oetus router statuse oetus router setupnão. Enquanto nenhum setup fixou o perfil, os dois usam a sugestão (o padrão do hub ou a pasta atual) e dizem que o perfil ainda não foi fixado. Oetus doctor --maquina <PERFIL>confere outro perfil só daquela vez, sem trocar o da máquina. - Se o perfil guardado saiu da receita (numa versão nova do etus-skills), o
etus router setupe oetus doctor --maquinaparam com código 2 e a lista de perfis válidos, pedindoetus router setup --perfil-trabalho <perfil>; oetus router statusmostra a mesma mensagem na linha da receita (receita.erro). - O
etus setupdo projeto continua usando o.etus.jsone a detecção do projeto, como antes. Não confunda--perfil-trabalhocom o--perfilglobal, que é o perfil de acesso enviado ao hub.
- Na primeira vez, sem perfil guardado, o
-
Regras. As regras do perfil, em ordem (
order, depois id) e filtradas pelostargetsde cada uma, vão para um bloco gerenciado:<!-- etus: início (a CLI atualiza este trecho) --> ...regras do perfil... <!-- etus: fim -->no
~/.claude/CLAUDE.md(Claude Code;CLAUDE_CONFIG_DIRvale), no~/.codex/AGENTS.md(Codex, quando a pasta dele existe ou ocodexestá no PATH;CODEX_HOMEvale) e noAGENTS.mdda pasta do pi (quando o pi é configurado). Só o bloco é trocado; o texto fora dele nunca muda. Arquivo sem o bloco ganha o bloco no fim; arquivo que não existe é criado. Antes de mudar, fica um backup (CLAUDE.md.bak.<data>, os 5 mais novos). Rodar de novo sem mudança na receita não grava nada. Um link (oAGENTS.mddo pi apontando para oCLAUDE.md, por exemplo) é seguido: o arquivo de verdade é gravado uma vez só, com as regras das ferramentas que o leem. Marcadores repetidos ou fora de ordem deixam o arquivo como está, com aviso.--sem-regraspula esse passo. -
Políticas. As políticas do perfil são somadas (listas unidas;
stopGuardligado em qualquer uma fica ligado). No Claude Code viramBash(...),Read(...)eEdit(...)nopermissions.denydo~/.claude/settings.json. O etus só acrescenta: o que você ou um repositório puseram fica, e o que o etus pôs fica anotado emrecipe-state.jsonna pasta de configuração do etus, para ele tirar só as dele quando a receita mudar. No pi, a extensãoetusdo pacote etus-pi lê as políticas da empresa do próprio pacote (por isso a tag do pacote acompanha a receita) e também opermissions.denydo Claude Code; comstopGuard, o setup religa a extensão se o filtroextensionsda entrada do pacote a desligou. O Codex não tem uma lista de negação que a CLI possa gravar: as políticas não valem lá, e o setup diz isso. -
Automações. Cada automação do perfil diz como cada ferramenta instala (
install.<ferramenta>.kind).settingscujosetupé do etus vira hook no~/.claude/settings.json(hoje oetus hookdox-etus-work, com a statusline;--no-work-tagsdesliga);settingsde outra ferramenta (ortk init -gdo RTK) não é instalado pelo etus, só conferido;nativenão precisa de nada;packagevira pacote nosettings.jsondo pi (etus-pi na tag da CLI epi-subagents);extensiondepende do pacote que traz a extensão. -
Modelo.
ai.modeldo perfil vira o modelo do Claude Code (ANTHROPIC_MODELe afins) e odefaultModeldo pi, a menos que você passe--model.ai.thinkingvira odefaultThinkingLeveldo pi; o etus não grava nível de raciocínio no Claude Code. -
Diferenças.
etus doctor --maquinamostra, numa seção "Receita do perfil", o que difere da receita (bloco de regras faltando ou desatualizado, regras de negação faltando, automação faltando, pacote do pi em outra versão, extensão desligada, modelo ou raciocínio diferente, componente de ambiente exigido pelo perfil bloqueado), cada uma com o comando que corrige; no--json, emreceita.itens. Oetus router statusresume numa linha. Diferença é aviso: não muda o código de saída. -
Camadas. As regras e políticas se aplicam da mais larga para a mais estreita, e cada camada só acrescenta: empresa (as políticas do
etus-core, que todo perfil lista), perfil (as regras e políticas que o perfil escolhe dos packs dele), repositório (oCLAUDE.mdouAGENTS.mddo projeto e o.claude/settings.jsondele, com seupermissions.denye seus hooksStop) e pessoa (o seu texto fora do bloco gerenciado e as suas entradas nopermissions.denydo usuário). Nenhuma camada desliga o que outra negou; no Claude Code umallowtambém não vence umdeny. -
De onde vem a receita. De uma cópia embutida na CLI (
src/setup-recipe.json, gerada de uma tag do etus-skills junto comsetup-profiles.jsonesetup-environment.json).ETUS_SKILLS_DIRaponta para um clone local do etus-skills e lê dele as regras, políticas, automações e perfis. Quando o Etus Extensões passar a entregar a receita, ela entra pelo mesmoRecipe::load(src/recipe.rs).
Para quem o admin escolheu no painel do AI Router, as chamadas à Anthropic da conta Claude dedicada a essa pessoa saem pelo IP de casa dela. O computador abre uma conexão de saída até o roteador e só repassa bytes TLS entre o roteador e a Anthropic: nunca vê o token da conta.
etus router setup --saida-casa # setup normal e liga a saída pela casa neste computador
etus router saida-casa ligar # liga num computador já configurado
etus router saida-casa status # agente, estado da conexão e IP de saída visto pela Anthropic
etus router saida-casa desligar # para o agente e apaga o arquivo dele- Só com a chave pelo login. A conexão usa a chave deste computador (
sk-etd-...). Com--chaveouOMNIROUTE_API_KEYo comando recusa: rodeetus logineetus router setup --saida-casa. Com o chaveiro no macOS também recusa, porque o agente roda sem terminal: useetus config armazenamento arquivo. - Um agente do sistema, não um hook. No macOS, um LaunchAgent do usuário (
~/Library/LaunchAgents/io.etus.saida-casa.plist); no Linux, um serviçosystemd --user(~/.config/systemd/user/etus-saida-casa.service). Ele fica de pé independente de quantas sessões do Claude Code estão abertas (uma conexão por computador), volta sozinho depois de queda, suspensão ou troca de rede e também atende o chat da Etus no navegador. O hookSessionStartsó rodaria quando uma sessão abre e precisaria de trava contra duplicatas. Windows ainda não tem suporte. - Lista fechada. Só
api.anthropic.com,console.anthropic.com,platform.claude.comeclaude.ai, na porta 443. Qualquer outro destino é recusado sem consultar o DNS. - O admin também liga. Enquanto o admin não liga a saída pela casa para você no painel, o status mostra "aguardando o admin" e o agente tenta de novo a cada 5 minutos.
- Estado e log.
saida-casa.jsonesaida-casa.log(até 256 KB, depoissaida-casa.log.1) na pasta de configuração do etus (~/Library/Application Support/etusno macOS,~/.config/etusno Linux, ouETUS_CONFIG_DIR). Nenhum dos dois guarda a chave. - Para testar.
etus router tunelroda o túnel em primeiro plano;ETUS_HOME_EGRESS_URLtroca o endereço do túnel (por exemplows://127.0.0.1:20140/etus/home-egress).ETUS_SAIDA_CASA_AGENT_DIRtroca a pasta do agente eETUS_SAIDA_CASA_NO_SERVICE=1não chamalaunchctlnemsystemctl.
Nesta CLI, etus work ... faz o dispatch para o app Etus Work; por isso o rastreamento de tarefas usa o comando canônico etus trabalho.
| O quê | Como |
|---|---|
| Hub | --hub URL, ETUS_HUB_URL ou hub_url no arquivo (padrão https://apps.etus.io) |
| Perfil | --perfil ID ou etus perfil usar ID / etus perfil limpar; vai ao gateway como ?perfil=ID |
| Cache do catálogo | 5 minutos (cache_ttl_secs no arquivo); etus atualizar renova na hora |
| Arquivo | config.json na pasta de configuração do usuário (etus config mostra o caminho) |
| Pastas | ETUS_CONFIG_DIR troca a pasta inteira |
| Perfis e ambiente | ETUS_SKILLS_PROFILES (lista de perfis) e ETUS_SKILLS_ENVIRONMENT (manifesto de ambiente); sem eles, as cópias embutidas |
| Receita do perfil | ETUS_SKILLS_DIR (clone local do etus-skills: regras, políticas, automações e perfis); sem ele, a cópia embutida |
| Links de ação | etus config abrir-links sim ou nao (open_actions no arquivo), ou ETUS_OPEN_ACTIONS=1 ou 0, que vale mais; padrão não abrir sozinho |
| Relatório da máquina | --sem-relatorio no etus doctor --maquina e no etus router setup, ou ETUS_NO_MACHINE_REPORT=1, não envia |
| Diagnóstico | ETUS_DEBUG=1 mostra no stderr o que foi pulado sem aviso (o padrão do hub, o relatório da máquina) |
| Token | etus config armazenamento arquivo ou chaveiro (token_store no arquivo), ou ETUS_TOKEN_STORE=file ou keychain, que vale mais; padrão arquivo no macOS e no Linux, Gerenciador de Credenciais no Windows |
Autocompletar: etus completions zsh > ~/.zfunc/_etus (também bash, fish, powershell, elvish).
cargo test # unitários e integração com um gateway MCP falso
cargo clippy --all-targets -- -D warnings
cargo fmt --check
cargo test keychain -- --ignored # testa o chaveiro do sistema de verdade
scripts/update-recipe.sh v2026.10.18.9 # copia receita, perfis e ambiente de uma tag do etus-skills
scripts/update-recipe.sh v2026.10.18.9 --check # só falha se as cópias embutidas estiverem velhasA tag do pacote etus-pi (pi::PACKAGE) acompanha a tag da receita: o teste package_tag_follows_the_snapshot confere, e embedded_snapshot_covers_every_embedded_profile falha quando um perfil embutido cita uma regra, política ou automação que a receita não tem. ETUS_SKILLS_GIT troca o repositório clonado pelo script (um clone local, por exemplo).
A correção certa para quem quer o chaveiro, e para distribuir o binário, é assinar com um certificado Developer ID (e notarizar). Com uma identidade de código estável entre versões, o macOS reconhece o etus atualizado como o mesmo programa e não pede permissão de novo a cada atualização. Hoje o binário é assinado ad hoc (codesign -dv mostra Signature=adhoc), por isso o arquivo é o padrão. Fica como follow up: assinar e notarizar no fluxo de release e, depois disso, reavaliar o chaveiro como padrão no macOS.
Sem CI: os binários saem de um Mac com Docker (buildx) e rustup.
-
Suba a versão no
Cargo.toml(e noCargo.lock) e escreva a seção dela noCHANGELOG.md. -
scripts/release.sh X.Y.Zgera emdist/vX.Y.Z/:etus-aarch64-macoseetus-x86_64-macos, nativos, assinados ad hoc (codesign -s -);etus-x86_64-linuxeetus-aarch64-linux, estáticos (musl), comdocker buildx build --platform linux/amd64|linux/arm64escripts/release.Dockerfile;SHA256SUMS,VERSIONe um<binário>.sha256por binário.
No fim, confere o sha256, a assinatura, que os de Linux são estáticos e que cada binário responde
etus X.Y.Za--version(os de Linux comdocker run --platform, o Intel com Rosetta). -
Depois do merge na
main,scripts/release.sh X.Y.Z --publish(com amainatualizada e limpa) gera tudo de novo e cria a releasevX.Y.Zcom esses arquivos e as notas doCHANGELOG.md. Oetus atualizar-clie o instalador procuram exatamente esses nomes.