Motivação
A maioria dos “orquestradores de agentes de IA” por aí é, na prática, um punhado de containers Docker que só faz sentido rodando na nuvem. O Stormwind nasceu de uma pergunta diferente: dá pra ter um app desktop de verdade — instalável, sem infraestrutura externa, sem servidor — que orquestra vários agentes de IA (o Claude Code local, um modelo rodando na sua máquina, uma API remota) através de um pipeline com etapas reais de planejamento, desenvolvimento e revisão?
O objetivo não era só “colocar uma UI Electron na frente de um backend Docker” — isso é um wrapper, não
um app desktop. A meta explícita foi: todo o sistema roda nativo. Sem docker compose up como
pré-requisito, sem Postgres/Redis/MinIO no caminho crítico, com o backend (BFF + orquestração) embutido
como binários standalone dentro do próprio instalador.
O que foi feito
- Pipeline multiagente real com LangGraph: quatro nós — Analista (levanta requisitos e monta um blueprint estruturado), Gestor (quebra em tarefas com dependências e paralelismo), Dev (implementa) e Reviewer (revisa) — com roteamento condicional por complexidade: tarefas de alta complexidade passam por um nó extra de revisão de segurança antes do Reviewer; tarefas simples vão direto.
- Cliente MCP de verdade, não uma simulação: fala JSON-RPC sobre stdio com o SDK oficial do
Model Context Protocol, verificado ao vivo contra o
@modelcontextprotocol/server-filesystem(14 ferramentas reais) e omcp-server-fetch(fazendo requisições HTTP reais). No caminho, apareceu um bug real de descompasso de versão do SDK — corrigido fixandomcp<2.0. - Sistema de Skills no estilo
SKILL.md(YAML + Markdown) — executáveis ou só instruções — plugado de verdade no nó Reviewer, com rotasGET /api/capabilities/{skills,mcp-servers}pra descoberta. - Registro de agentes locais e externos: três backends reais —
local_process(verificado invocando o próprio CLIclaudeda máquina),local_model(modelo local via base URL) eremote_api(provedor remoto via Portkey Gateway) — escolhidos por papel de agente. - Migração completa pra desktop-nativo: Postgres → SQLite (via Prisma), MinIO → sistema de
arquivos local, Redis/pub-sub → um event bus em processo. O checkpointer de estado do LangGraph
também virou SQLite embutido (
aiosqlite+AsyncSqliteSaver). - BFF e orquestração empacotados como binários standalone, não processos de desenvolvimento: o
BFF (Node/Fastify) vira um
.exevia Node Single Executable Applications (--experimental-sea), e o serviço de orquestração (Python/FastAPI) vira um.exevia PyInstaller — os dois entram no instalador Tauri como sidecars de verdade (bundle.externalBin), sem exigir Node ou Python instalados na máquina do usuário. - Runner de migration em runtime: como o executável empacotado não carrega a CLI do Prisma, o próprio BFF aplica as migrations SQLite na primeira inicialização (idempotente, com tabela de controle própria) — testado do zero, sem banco pré-existente, e num restart sobre um banco já migrado.
Como funciona
Arquitetura desktop-nativa
flowchart TB
User["🖥️ Usuário"]
subgraph Tauri["App Tauri v2 (Rust)"]
Shell["Shell / Control Panel\nspawn_managed_sidecar()"]
WebUI["WebView\nNext.js (dev mode nesta fase)"]
end
subgraph Sidecars["Sidecars nativos — binários standalone"]
BFF["bff.exe\nNode SEA — Fastify"]
Orch["orchestration.exe\nPyInstaller — FastAPI + LangGraph"]
end
SQLite[("SQLite\nProjetos · Runs · Settings\n(Prisma)")]
Checkpoint[("SQLite\nLangGraph checkpoints")]
FS[("Sistema de arquivos local\n%APPDATA%/Stormwind")]
subgraph AgentSources["Fontes de agentes"]
LocalCLI["claude CLI (local_process)"]
LocalModel["modelo local (local_model)"]
RemoteAPI["Portkey Gateway (remote_api)"]
end
MCP["Cliente MCP\nJSON-RPC / stdio"]
Skills["Sistema de Skills\nSKILL.md"]
User --> WebUI
Shell -->|"spawn via app.shell().sidecar()"| BFF
Shell -->|"spawn via app.shell().sidecar()"| Orch
WebUI -->|HTTP :3001| BFF
WebUI -->|SSE / HTTP :8000| Orch
BFF --> SQLite
BFF --> FS
Orch --> Checkpoint
Orch --> MCP
Orch --> Skills
Orch --> AgentSources
Orch -->|eventos de progresso| BFF
Pipeline de execução de uma run
sequenceDiagram
actor U as Usuário
participant BFF as bff (sidecar)
participant Graph as LangGraph (orchestration)
participant An as Analista
participant Ge as Gestor
participant Dev as Dev
participant Gate as Gate de complexidade
participant Sec as Security Reviewer
participant Rev as Reviewer
participant MCP as Cliente MCP
U->>BFF: cria run (briefing do projeto)
BFF->>Graph: dispara grafo (thread_id = run_id)
Graph->>An: gera blueprint estruturado
An-->>Graph: blueprint (tarefas, dependências, complexidade)
Graph->>Ge: quebra em tarefas paralelas/sequenciais
Ge-->>Graph: plano de execução
Graph->>Dev: implementa cada tarefa
Dev-->>Graph: diffs / arquivos
Graph->>Gate: complexidade da tarefa?
alt alta complexidade
Gate->>Sec: roteia pro nó de segurança
Sec->>MCP: chama ferramentas (fetch/filesystem) se preciso
Sec-->>Graph: achados de segurança
Graph->>Rev: revisão final (com achados)
else baixa complexidade
Gate->>Rev: vai direto pro Reviewer
end
Rev->>Rev: aplica Skills (ex: review_checklist)
alt aprovado
Rev-->>Graph: aprovado
Graph-->>BFF: run concluída
else rejeitado
Rev-->>Dev: volta pro Dev com feedback
end
BFF-->>U: status em tempo real (SSE)
Migration em runtime no sidecar empacotado
sequenceDiagram
participant Tauri as Tauri (lib.rs)
participant BFF as bff.exe
participant Prisma as Prisma Client
participant DB as SQLite (stormwind.db)
Tauri->>BFF: spawn com DATABASE_URL=file:...\nPRISMA_QUERY_ENGINE_LIBRARY=...
BFF->>Prisma: $connect()
BFF->>Prisma: applyMigrations() — sem CLI do prisma disponível no bundle
Prisma->>DB: CREATE TABLE IF NOT EXISTS _stormwind_migrations
Prisma->>DB: SELECT name FROM _stormwind_migrations
loop cada migration não aplicada
Prisma->>DB: executa migration.sql (statement a statement)
Prisma->>DB: INSERT INTO _stormwind_migrations
end
BFF-->>Tauri: pronto — Fastify escutando em :3001
Stack
- ⌂ DESKTOP: Tauri v2 (Rust) — shell nativo,
tauri_plugin_shellpra gerenciar sidecars, sistema de eventos (emit/listen) no lugar do preload-bridge do Electron. - ◆ BACKEND: Fastify (BFF, Node) empacotado via Node SEA; FastAPI + LangGraph (orquestração,
Python) empacotado via PyInstaller — os dois como sidecars
externalBin. - ⛁ DADOS: SQLite via Prisma (dados de app) + SQLite via
aiosqlite/AsyncSqliteSaver(checkpoints do LangGraph) + sistema de arquivos local (armazenamento de briefings). - ⚙ ORQUESTRAÇÃO DE AGENTES: LangGraph com roteamento condicional por complexidade, cliente MCP
real (SDK oficial, JSON-RPC/stdio), sistema de Skills (
SKILL.md), registro de agentes locais e externos (local_process,local_model,remote_api). - ◈ FRONTEND: Next.js, carregado dentro do WebView do Tauri.
Como rodar
# Pré-requisitos: Node 20+, Python 3.12+ (via uv), Rust/Cargo (via rustup)
# 1. Instalar dependências
cd bff && npm install && npx prisma generate
cd ../orchestration && uv sync
cd ../web && npm install
# 2. Empacotar os sidecars (gera os binários standalone e injeta no Tauri)
node bff/scripts/build-sidecar.mjs
python orchestration/build_sidecar.py
# 3. Rodar o app desktop
cd src-tauri && cargo tauri dev
Detalhes de arquitetura, decisões e um guia dedicado de Tauri (pra quem nunca mexeu) estão em
TAURI-GUIDE.md e DESKTOP-MIGRATION-PLAN.md no repositório.
# Código aberto
O Stormwind é open source: uma ferramenta de verdade pra orquestrar agentes de IA, não só um wrapper em cima de containers.