Uma ideia vira artigo, carrossel e Reel — pela mesma esteira.
Com gates que impedem a peça de afirmar o que a apuração não sustenta.
Não é um gerador de post. É um capataz de 13 etapas que manda no Claude Code, guarda estado em arquivo, e para quando alguma coisa não fecha.
A diferença está no que ele recusa a publicar.
flowchart LR
A[pauta] --> B[apuração]
B --> C[ganchos]
C --> D[roteiro]
D --> E[foto]
E --> F{validação}
F --> G{gate factual}
G --> H{editor}
H --> I[montagem]
I --> J[artigo]
J --> K[promoção]
K --> L[Reel]
style F fill:#B4651C,color:#fff
style G fill:#B0392C,color:#fff
style H fill:#B4651C,color:#fff
Os três losangos param a peça. O vermelho é código puro e não pode ser convencido.
_saida/<conta>/<AAAA-MM-DD>/<peça>/
├── carrossel/
│ ├── slide-01.png … slide-10.png prontos pro Instagram
│ ├── legenda.md
│ ├── reel.mp4 reel-web.mp4 com legenda palavra a palavra
│ └── revisao.html página pra revisar antes de postar
└── artigo/
└── artigo.md o post do blog
Uma apuração, três formatos, o mesmo fato. O artigo e o carrossel falam da mesma coisa no mesmo dia porque saem da mesma ficha — não porque alguém lembrou de sincronizar.
Cada regra deste repositório nasceu de uma peça que saiu errada. Os comentários no código contam qual — eles são o mais valioso que tem aqui.
ficha diz: "entre 33% e 46% já usavam a ferramenta"
peça diz: "Até 46% já usavam" ← REPROVADO
Citar só o teto transforma o intervalo de uma estimativa em fato. O gate compara valor
canônico, não grafia: a ficha escreve 500.000, a peça escreve "500 mil", e ele sabe que
é o mesmo número.
Ele existe porque um editor por IA reprovou uma peça apontando dois erros factuais e, no dia seguinte, aprovou o mesmo arquivo byte por byte, rebaixando os dois a "ressalva menor". Julgamento de agente varia. Factualidade não pode variar.
Por isso ele roda antes do editor: julgamento editorial pode reprovar, nunca liberar o que a factualidade reprovou.
| camada | quem é | o que faz |
|---|---|---|
| autor | hook-editor |
escreve 5 ganchos, cada um citando quais fatos da ficha o sustentam |
| fact-check | agente separado | julga cada gancho contra a ficha: SUPPORTED / UNSUPPORTED |
| juiz | agente separado | escolhe só entre os SUPPORTED |
O juiz existe porque quem escreve tende a preferir a frase segura: o autor produziu uma opção boa e elegeu a mais morna. Separar escrita de escolha custou um incidente para ser aprendido.
E fato classificado como NEGADO na ficha não pode virar afirmação em slide nenhum — a esteira rastreia por índice qual fato sustenta qual frase.
editor rejeita → veredito é gravado com o HASH do roteiro
texto muda → vale pedir de novo
texto igual → a porta continua fechada
Apagar o arquivo de aprovação não reabre nada. Sem isso, "rodar de novo" era re-sortear o editor até ele ceder — o que é contornar o gate, não corrigir a peça.
O validador mecânico tem 48 regras, e todas são de coisa física (cabe no quadro, tem foto, o destaque renderiza) ou operacional (tem fonte, tem legenda, o Instagram aceita).
"Está fraco" não está lá. Quatro regras de gosto já entraram como erro neste arquivo, e as
quatro foram derrubadas ao ver a peça que elas barravam. Julgamento mora nos agentes, em
metodo/, onde pode ser discutido — não numa expressão regular.
| pra quê | obrigatório | |
|---|---|---|
| Node ≥ 18 e Python ≥ 3.10 | o capataz e a fábrica de vídeo | sim |
| Claude Code, logado | é quem faz o trabalho — ~10 sessões por peça, no seu plano | sim |
| ffmpeg + ffprobe | vídeo | pro Reel |
| edge-tts | narração, grátis | pro Reel |
| faster-whisper | legenda palavra a palavra | pro Reel |
| Playwright | vira o slide HTML em PNG | pro carrossel |
| ComfyUI + GPU | gerar imagem | não — você pode pôr as suas |
git clone https://github.com/thallisribeiro/contenthub && cd contenthub
npm install
pip install -r requirements.txt
npm testOs testes não chamam agente nenhum. Se passam, o motor está inteiro; se quebram, quebraram antes de custar um token.
cp -r perfis/exemplo perfis/minha-contaEdite perfis/minha-conta/perfil.json — nicho, público, territórios, paleta, CTA, estilo —
e os cinco documentos em data/:
tone-of-voice.md como esta conta fala (frases de exemplo, não adjetivos)
avatar.md uma pessoa, não um segmento
content-pillars.md os assuntos, e o que cada um entrega
anti-patterns.md erros já cometidos, pra não repetir
visual-identity.md como as peças parecem
Isso é o onboarding. Não há wizard: é um JSON e cinco markdowns, comentados campo a
campo. Todo campo _alguma_coisa explica o campo acima dele.
node core/pipeline-editorial.js --perfil minha-conta --parar-em triagemPara depois de escolher a pauta, antes de gastar o resto. Olhe:
_trabalho/<id>/
├── 01-candidatos.json o que a busca achou
├── 02-triagem.json por que descartou cada um
└── 02-escolhido.json a pauta
Gostou? Continue:
node core/pipeline-editorial.js --perfil minha-conta --id <id>Rodar de novo é seguro. Etapa cujo artefato já existe é reaproveitada — queda no meio não recobra o que já foi pago.
node core/pipeline-editorial.js --perfil minha-conta --link https://veiculo.com/materiaO link substitui a busca de pauta e não pula a apuração. Sem ficha, o gate factual não teria contra o que comparar, e a peça poderia afirmar qualquer coisa. O link entra na frente do corredor, nunca no meio.
editorial |
foto sangrando no quadro, manchete grande. Notícia com imagem que se sustenta |
post-social |
cartão claro com avatar e @. Tese, opinião, frase que se cita |
{ "estilo": "post-social" }Fazer um novo: copie um diretório, mude o CSS, mantenha as âncoras e as classes. O contrato
está em estilos/LEIA.md, e a carga confere — template incompleto
falha ali, não no olho de quem revisa.
A primeira versão do
post-socialinventou nomes de classe próprios e renderizou a manchete despedaçada, sem erro nenhum. A conferência existe por causa disso.
As regras de texto são listas de palavras em idiomas/<lang>.json. Acompanham pt-BR e
en-US.
ESTEIRA_IDIOMA=en-US node core/pipeline-editorial.js --perfil minha-contaSem a lista do seu idioma, essas regras não reprovam — elas deixam de existir. Falha aberta é pior que falha fechada, porque quem opera acha que está protegido. Por isso o validador avisa alto quando não acha a lista, em vez de seguir mudo.
Ao traduzir, traduza a função de cada lista — descrita em idiomas/en-US.json — não as
palavras. E use radical: cust* pega custa, custou, custando. Listar verbo forma a forma em
língua flexionada é garantia de falso positivo.
| variável | pra quê |
|---|---|
ESTEIRA_TETO_CHAMADAS |
teto de sessões de agente por execução. Ligue. Uma peça ruim passa de 20 |
ESTEIRA_IDIOMA |
qual idiomas/<lang>.json usar. Padrão pt-BR |
FABRICA_REELS |
onde está a fábrica de vídeo, se você a moveu |
BLOG_REPO |
repositório do blog, se você publica artigo |
_estado/execucoes.jsonl uma linha por etapa: ms, sessões de agente, resultado
É o que responde onde a esteira queima retrabalho — antes de você cortar etapa por intuição. O relatório também agrega por estilo, território e formato de capa, para que a escolha de aparência seja medida e não decorada.
npm test # 7 suítes, nenhum agente chamado
npm run test:pipeline # o orquestrador inteiro, com os agentes bloqueados| suíte | casos |
|---|---|
| orquestrador | 60 |
| validador de carrossel | 48 |
| ponte peça → Reel | 29 |
| gate factual | 12 |
| publicador do Instagram | 10 |
| teto de gasto | 5 |
| portão editorial do Reel | 4 |
| gate factual do artigo | ✓ |
O self-test do orquestrador roda o pipeline como processo filho com os agentes bloqueados
por trava, não por convenção: tentar chamar um agente ali é erro. Já foi convenção, e um
caso escapou e ficou pendurado num claude -p de verdade dentro do que devia ser teste
seco.
core/
├── pipeline-editorial.js o capataz: 13 etapas, estado em arquivo
├── adaptadores/
│ ├── carrossel/ validador, gate factual, montagem
│ ├── artigo/ gate factual do artigo
│ └── reel/ a ponte peça → brief
├── reel/fabrica/ o motor de vídeo (TTS, legenda, montagem)
├── publicador/verde/ Instagram e YouTube
├── fonte/ de onde vem a pauta (adapters plugáveis)
└── feedback/ métricas voltando pra decisão de pauta
metodo/
├── EDITORIAL.md a direção editorial
├── SWIPE.md o que já funcionou
└── agents/ 16 agentes, um arquivo cada
estilos/ idiomas/ perfis/
MIT. Use, modifique, venda — só mantenha o aviso de copyright.
Os comentários no código contam por que cada regra existe, quase sempre com a peça que a
originou.
Cada um custou um erro real. É a parte que não dá pra reescrever do zero.