diffai-sensioerp
v0.2.1
Published
Local-AI powered git diff analyzer. Produces a structured technical summary (JSON) of what changed, with zero cloud dependencies.
Readme
diffai
Analisador de
git diffcom IA. Produz um resumo técnico estruturado (JSON) do que mudou.
diffai é um CLI que detecta o que mudou no Git, descobre via AST quais símbolos realmente foram alterados, monta chunks mínimos (nunca o arquivo inteiro) e pede a uma IA uma análise técnica por chunk. As análises são consolidadas em um único JSON e em uma documentação (docs/analysis.md).
Por padrão o diffai usa a IA da nuvem (OpenAI) quando encontra a variável OPENAI_API_KEY_CHECKLIST (definida no ambiente ou em um arquivo .env — veja .env.example). Se essa variável não existir, ele cai automaticamente para a IA local (Ollama / Qwen Coder), sem nenhuma configuração adicional e sem que código-fonte saia da máquina. Ambos os adapters implementam o mesmo port (AIProvider), então o restante do pipeline não sabe (nem precisa saber) qual dos dois está em uso.
A IA (local ou na nuvem) é apenas um Analista Técnico: ela nunca conversa com o usuário, nunca gera checklist de QA e nunca produz o Markdown final. Toda a arquitetura já está preparada para, no futuro, um pacote cloud-agent consumir somente o JSON consolidado (nunca o código-fonte).
Fluxo
git diff → git-analyzer → ast-parser → chunk-builder → cloud-ai | local-ai → summary-merger → { analysis.json, docs/analysis.md }| Etapa | Pacote | Entrada | Saída |
| ----- | -------------------------- | --------------------- | ---------------------------- |
| 1 | git-analyzer | refs/working tree | DiffResult (arquivos) |
| 2 | ast-parser | ChangedFile | FileSymbols (símbolos) |
| 3 | chunk-builder | FileSymbols | AnalysisChunk[] |
| 4 | cloud-ai (padrão) ou local-ai | AnalysisChunk | ChunkAnalysis (JSON) |
| 5 | summary-merger | ChunkAnalysis[] | ConsolidatedSummary (JSON) |
| 6 | documentation | ConsolidatedSummary | docs/analysis.md |
Regra de ouro: o diff inteiro nunca é enviado à IA. Cada chunk carrega só o trecho alterado + contexto mínimo.
Arquitetura (Clean Architecture)
Dependências apontam sempre para dentro. @diffai/shared não depende de ninguém; todos dependem dele.
┌─────────────────────────────────────────────┐
│ @diffai/shared │
│ domain (entidades) · application (ports + │
│ use-case) · schemas (Zod) · config │
└─────────────────────────────────────────────┘
▲ ▲ ▲ ▲ ▲ ▲ ▲
implementam ports │ │ │ │ │ │ │
┌───────────────┴─┬───┴─────┴──┬──┴──┬──┴─────┴───┬─┴──────────────┐
git-analyzer ast-parser chunk-builder cloud-ai local-ai summary-merger documentation
(simple-git) (ts-morph) (puro) (openai) (ollama/qwen) (puro) (puro)
└───────────────────────────────────┬───────────────────────────────────────┘
│ tudo é injetado em
┌────┴────┐
│ cli │ ← composition root (Commander + DI)
└─────────┘- domain: entidades puras do pipeline (
ChangedFile,FileSymbols,AnalysisChunk,LineRange). - application/ports: as interfaces (
GitProvider,ParserProvider,ChunkBuilder,AIProvider,PromptBuilder,SummaryMerger,SummaryWriter,DocumentationWriter,AnalysisCache,Logger). - application/use-cases:
AnalyzeChangesUseCaseorquestra o fluxo usando só ports — é a "API interna" que ocloud-agentreaproveitará. - schemas (Zod): contratos JSON que cruzam fronteiras —
ChunkAnalysisSchema(etapa 4) eConsolidatedSummarySchema(etapa 5). São a fonte da verdade; os tipos TS são inferidos deles.
Por que os contratos JSON vivem em Zod, e as entidades internas não
Entidades internas (ChangedFile, etc.) são interfaces puras, sem dependência de biblioteca — domínio limpo. Já ChunkAnalysis e ConsolidatedSummary atravessam a fronteira da IA e do futuro cloud-agent, então precisam de validação em runtime. Para esses, o schema Zod é a fonte única (type = z.infer<...>), eliminando drift entre validação e tipo.
Substituindo implementações
Todo recurso externo está atrás de um port. Trocar qualquer um é adicionar um adapter e uma linha no composition root — nada mais muda.
- IA:
AIProvider ← OpenAIAdapter (cloud-ai)ouAIProvider ← OllamaAdapter (local-ai) ← Qwen Coder. O composition root (packages/cli/src/container.ts) escolhe automaticamente:OPENAI_API_KEY_CHECKLISTpresente → nuvem; ausente → local. Pode ser forçado viaai.provider: "openai" | "ollama"nodiffai.config.json. Amanhã: LM Studio, llama.cpp, Anthropic, etc. — mesmo port, outro adapter. - Git:
GitProvider ← SimpleGitAdapter. Amanhã: GitHub API, fixtures. - Parser:
ParserProvider ← TsMorphParser. Amanhã: outros parsers por linguagem.
Performance & escala (5 → 500 arquivos)
- Paralelismo controlado:
mapWithConcurrencylimita quantos chunks vão à IA ao mesmo tempo. - Cache incremental: cada chunk tem um
id= hash estável do conteúdo.AnalysisCacheevita reanalisar código que não mudou. - Minimização de tokens: só o trecho alterado + contexto mínimo entram no prompt.
- Degradação graciosa: falha de parse em um arquivo não derruba o pipeline (
FileSymbols.parseError).
Futuro: cloud-agent (não implementado)
Um pacote futuro consumirá apenas o ConsolidatedSummary (JSON) — nunca o código. Ele fará entrevista com o dev, gerará perguntas, checklist de QA e Markdown, e anexará ao PR. Nada disso é responsabilidade da IA local. O schemaVersion no JSON permite que ele evolua de forma independente.
Requisitos
- Node.js >= 20, npm 10+
- Opção A — IA da nuvem (padrão quando configurada): uma
OPENAI_API_KEY_CHECKLISTválida, definida no ambiente ou em um.envna raiz do repo analisado (copie.env.example). - Opção B — IA local (fallback automático): Ollama com um modelo coder (padrão:
qwen2.5-coder:7b). Há umdocker/pronto para subir o Ollama.
Comandos
npm install
npm run typecheck # tsc --noEmit em todos os pacotes
npm test # vitest (IA sempre mockada)
npm run lint # eslint
npm run format # prettier --writeUso do CLI
# alterações pendentes: working tree vs HEAD
npm run diffai -- --cwd /caminho/do/repo
# TODA a branch: tudo que ela introduziu vs main (merge-base / estilo PR)
npm run diffai -- --base main
# variações úteis
npm run diffai -- --base main --two-dot # diff literal main..HEAD (sem merge-base)
npm run diffai -- --base v1.0.0 --head HEAD # intervalo entre dois refs
npm run diffai -- --staged # apenas o que está staged
npm run diffai -- --model qwen2.5-coder:7b --concurrency 8 # (flags de modelo/URL só valem para o Ollama)
npm run diffai -- health # checa se a IA (nuvem ou local) responde
# rodar sem nenhuma IA de verdade (análise offline determinística, ótimo p/ CI e testes)
DIFFAI_FAKE_AI=1 npm run diffai -- --cwd /caminho/do/repo --skip-healthEscolha automática de IA: se OPENAI_API_KEY_CHECKLIST estiver definida (no ambiente ou em um .env na pasta analisada — veja .env.example), o diffai usa a nuvem (OpenAI). Caso contrário, usa o Ollama local. Para forçar um dos dois, defina "ai": { "provider": "openai" } ou "ai": { "provider": "ollama" } no diffai.config.json.
Saídas (padrão, relativas ao --cwd): .diffai/analysis.json e docs/analysis.md.
Configuração opcional via diffai.config.json na raiz do repo (precedência: defaults < arquivo < flags).
Estrutura
packages/
shared/ # domínio, ports, schemas, config, use-case (sem deps internas)
git-analyzer/ # GitProvider (simple-git)
ast-parser/ # ParserProvider (ts-morph)
chunk-builder/ # ChunkBuilder (puro)
cloud-ai/ # AIProvider (openai) — padrão quando OPENAI_API_KEY_CHECKLIST existe
local-ai/ # AIProvider (ollama/qwen) — fallback automático
summary-merger/ # SummaryMerger (puro)
documentation/ # DocumentationWriter
cli/ # Commander + composition root (DI)Status
Pipeline completo e funcional, com o CLI executável ponta-a-ponta e IA híbrida (nuvem por padrão via OPENAI_API_KEY_CHECKLIST, local como fallback automático). Todos os pacotes implementados com testes (IA sempre mockada nos testes), typecheck e lint limpos. Próximo passo natural (fora do escopo atual): o pacote cloud-agent, que consumirá apenas o JSON consolidado.
Licença
MIT
