Provedores de LLM
O planejador é agnóstico de provedor. Google Gemini e OpenAI são suportados; configure vários ao mesmo tempo e escolha um por execução:
// windup.config.ts
llm: {
provider: "google", // default for runs without --llm
model: "gemini-3.1-flash-lite",
// apiKeyEnv: "GEMINI_API_KEY", // já tem a chave com outro nome? aponte para ela
providers: {
openai: { model: "gpt-5-mini" }, // default model when --llm openai is used
// openai: { apiKeyEnv: "MY_OPENAI_KEY", baseUrl: "https://my-proxy/v1" },
},
},
npx windup run checkout # config default (google)
npx windup run checkout --llm openai # provider default model (gpt-5-mini)
npx windup run checkout --llm openai:gpt-5-nano # explicit provider:model
WINDUP_LLM=openai:gpt-5-mini npx windup run --all # same thing via env (CI)
--llmfunciona emrun,bench(compara provedores no mesmo cenário) escan(camada de LLM-assist).- Chaves de API:
GOOGLE_GENERATIVE_AI_API_KEY/OPENAI_API_KEYpor padrão. Para reaproveitar uma chave que seu projeto já guarda com outro nome, aponte para ela comapiKeyEnv— seja no nívelllm(llm.apiKeyEnv: "GEMINI_API_KEY", que vale para o provedor que não tiver sobrescrita) ou por provedor (llm.providers.openai.apiKeyEnv, que tem prioridade). Não precisa duplicar o segredo. Owindup doctorinforma exatamente qual variável ele espera. - Um nome de modelo errado é tratado como erro de configuração, não como falha de teste: o 404 do provedor vira uma mensagem acionável listando os modelos conhecidos, a execução falha com
kind: config(nunca repetida por--retries) e owindup doctoravisa de antemão quando o modelo configurado não está na tabela de modelos conhecidos. baseUrl(apenas OpenAI) aponta para qualquer endpoint compatível com OpenAI — Azure, um proxy ou um servidor de modelo local.- Trocar de provedor nunca invalida o cache de planos: planos são dados, replays são livres de LLM independentemente de quem planejou.
windup costsdetalha o gasto por provedor e por modelo, então alternar entre LLMs mantém o gasto por fornecedor visível.
Planejar com sua assinatura do Claude (--llm claude-code)
Se você já paga um plano Claude (Pro/Max), pode planejar com ele em vez de comprar tokens de API — o Windup dirige o claude CLI que você já tem, sem chave de API, sem servidor extra.
Opt-in, nunca default. Usar uma assinatura para planejar programaticamente é uma zona cinzenta não endossada pela Anthropic, e o Windup não a opera. Para trabalho sensível a confiabilidade (CI, suítes compartilhadas), prefira
--llm googleou--llm openai. Replays em cache nunca chamam nenhum LLM, então um plano feito assim continua replayando a $0 sem nada rodando.
Configuração — um comando
npx windup claude login # instala o claude CLI se faltar, depois loga na sua assinatura
npx windup claude status # a qualquer momento: "claude CLI: ready — voce@exemplo.com (max plan)"
O windup claude login instala o Claude Code CLI (com a sua confirmação — nunca um install global calado, nunca em CI) e abre o próprio login de navegador da Anthropic; você clica autorizar na sua conta. O app desktop e o CLI logam separado, então ter o app desktop não basta. Na mão, se preferir: npm install -g @anthropic-ai/claude-code, depois claude → /login (escolha “assinatura”, não uma chave de API).
É só isso — sem wrapper, sem Python, sem servidor local. O Windup faz spawn do claude em modo não-interativo a cada plano (a partir de um diretório temporário isolado, então nunca pega o CLAUDE.md de um projeto).
Várias contas — uma por projeto (--profile)
O login do CLI é global: um token, num único diretório de config, então todo projeto planeja na conta que logou por último. Se você tem um plano pessoal mais um por cliente, isso significa que o trabalho do cliente consome silenciosamente o seu plano. Amarre cada projeto à sua própria conta, uma vez:
cd ~/work/acme
npx windup claude login --profile acme # config dir próprio + amarra este projeto + faz login
npx windup claude status # → confirma qual conta este projeto consome
O --profile acme dá àquela conta o seu próprio diretório de config (~/.claude-acme — uma sessão independente), amarra o projeto a ele exportando CLAUDE_CONFIG_DIR no .envrc, roda direnv allow, e só então abre o login. A partir daí, dar cd no projeto faz daquela conta a que planeja — incluindo o processo claude que o Windup faz spawn, que herda o ambiente. Repita por projeto com outro nome; seu ~/.claude padrão fica intocado como o perfil sem nome.
Seu .envrc nunca é sobrescrito por acidente: um arquivo existente recebe um append (os outros exports intactos), rodar de novo não faz nada, e uma amarração a um perfil diferente interrompe e te diz para rodar de novo com --force — que religa aquela linha e imprime o que substituiu. Sem direnv? O comando imprime o export para você pôr no shell.
npx windup claude status # qual conta está ativa aqui (email + plano) — não gasta tokens
npx windup claude status --profile acme # checa um perfil nomeado sem trocar para ele
npx windup claude login --profile acme --force # aponta este projeto para outro perfil (religa o .envrc)
npx windup claude login --force # troca a conta ATIVA (desloga primeiro, dizendo de quem)
npx windup claude logout --profile acme # desloga aquele perfil (os outros ficam intactos)
npx windup claude logout --profile acme --remove # …e apaga o config dir dele: perfil aposentado
Encerrando um cliente? npx windup claude logout --profile <nome> --remove limpa a credencial daquele perfil e apaga o config dir dele — todos os outros perfis ficam intactos, e o ~/.claude padrão nunca é removido. Se o status algum dia disser account email not reported, é porque o config dir tem um token válido sem metadado de conta: deslogue e logue aquele perfil de novo para registrar qual conta é.
Duas coisas que vale saber: o .claude/settings.json de um projeto não consegue trocar a conta (o diretório de config é resolvido antes daquelas settings carregarem) — é por isso que a amarração vive no .envrc; e replays cacheados não chamam LLM nenhum, então com o .windup/cache/ commitado a suíte roda a $0 sem tocar em conta alguma.
npx windup run checkout --llm claude-code # modelo padrão: claude-sonnet-4-6
npx windup run checkout --llm claude-code:claude-opus-4-6
WINDUP_LLM=claude-code npx windup run --all # via variável de ambiente
Opcionalmente fixe no config para que windup run puro já use:
// windup.config.ts
llm: { provider: "claude-code", model: "claude-sonnet-4-6" },
- O custo aparece como $0 no
windup costs— os tokens são reais e ficam no ledger, mas são cobertos pela sua assinatura, então o Windup não inventa um preço por token para eles. - Se o
claudenão estiver instalado ou logado, a execução falha na hora com uma mensagem acionável (instalar //login), não um stack trace. - Mais lento para planejar que uma API hospedada (cada plano sobe o agente do CLI — ~8–12s vs ~2–4s), mas o planejamento acontece uma vez e é cacheado; replays são $0 e instantâneos de qualquer forma.
- Por baixo dos panos: não há JSON mode, então o Windup carrega o schema do plano no prompt e desembrulha a resposta mecanicamente (o Ajv ainda valida todo plano);
temperature/seednão têm equivalente no CLI e não são enviados.
Alternativa: rotear pelo claude-code-openai-wrapper (HTTP)
Em vez do CLI, você pode apontar o Windup para o claude-code-openai-wrapper — um proxy local de terceiros, mantido pela comunidade, que expõe um endpoint compatível com OpenAI sobre a sua sessão do Claude Code. Útil se você já o roda, quer uma fronteira HTTP, ou alcança o Claude via Bedrock/Vertex por trás dele. O Windup usa o wrapper (em vez de fazer spawn do CLI) sempre que uma URL estiver configurada:
# suba o wrapper (precisa de Python 3.11+ e Poetry), depois:
WINDUP_CLAUDE_CODE_URL=http://localhost:8000/v1 npx windup run checkout --llm claude-code
// windup.config.ts — mesmo efeito, persistido
llm: { provider: "claude-code", providers: { "claude-code": { baseUrl: "http://localhost:8000/v1" } } },
A auth de cliente dele vem desligada; defina CLAUDE_CODE_API_KEY só se você a habilitou. Mesmo custo $0, mesmo un-fence. Um wrapper fora do ar falha na hora com uma mensagem nomeando a URL.