minipainter

Inventário local de tintas para miniaturas + correspondência de cores entre marcas para agentes de IA (Citadel, Army Painter, Vallejo, AK).

Documentação

MINIPAINTEER

A bancada de tintas, indexada.

CI npm

Site: arturskowronski.github.io/minipainter

minipainter é um registro de tintas local-first para fluxos de trabalho de pintura de miniaturas. Ele existe por um motivo prático: sugestões de tintas por IA são muito mais úteis quando entendem as tintas que você realmente possui.

O projeto oferece:

  • um catálogo local determinístico (1.607 tintas entre Citadel, Army Painter, Vallejo, AK) e inventário
  • busca de tintas priorizando as que você possui e correspondência de cores entre marcas
  • uma interface de terminal colorida (TUI) que mostra o RGB real de cada tinta como uma amostra
  • servidores MCP para Claude Desktop e ChatGPT
  • uma superfície de CLI projetada para humanos e fluxos de trabalho de agentes

A TUI colorida

A TUI do livro-razão é um aplicativo de terminal realmente colorido: um banner MINIPAINTER, molduras de seção douradas, status verde OWNED / vermelho MISSING, e uma amostra truecolor do RGB de cada tinta. A cor é ativada para um TTY e respeita NO_COLOR.

MINIPAINTER terminal UI — catalog

Por que isso existe

A maioria dos fluxos de aconselhamento sobre tintas quebra no mesmo ponto: eles recomendam tintas que você não tem em mãos.

minipainter foi construído para resolver exatamente esse problema:

  • manter um registro local do que está na sua estante de tintas
  • pesquisar rapidamente por nome, função, família e cor aproximada
  • preparar uma base de inventário estável para uma futura habilidade de IA que possa inspecionar links, fotos e imagens de modelos

O objetivo de longo prazo não é "a IA escolhe cores aleatórias para miniaturas". O objetivo é "a IA raciocina a partir do seu inventário real primeiro, e então sugere alternativas mais fortes apenas quando útil."

Destaques de recursos

  • Owned-first matching: buscas e recomendações podem priorizar tintas que você já possui.
  • Catalog in repo, inventory in your home: os registros de tintas vivem em data/catalog/; o que você possui vive em ~/.minipainting/inventory.json e acompanha você entre projetos.
  • RGB-aware search: valores RGB aproximados ajudam na correspondência de cores mais próximas.
  • Colored TUI: livro-razão de terminal com amostras RGB por tinta e status OWNED/MISSING.
  • Agent-friendly CLI: saída de comando determinística para integração com IA (Claude + ChatGPT MCP).

Capturas de tela

Tela principal

TUI em tela cheia com banner, catálogo, painel de detalhes e faixa de comandos.

See: docs/assets/hero.txt

Hero Screen

Visualização de busca

Busca filtrada para uma pesquisa semântica como bone.

See: docs/assets/search.txt

Search View

Visualização de possuídos

Apresentação apenas do inventário, focada no que já está vinculado à sua coleção.

See: docs/assets/owned.txt

Owned View

Fluxo de CLI

Uso representativo de linha de comando para busca, atualizações de propriedade e correspondência de cores.

See: docs/assets/cli.txt

CLI Flow

Executar com Docker (Postgres)

Toda a pilha — servidor MCP/HTTP mais um Postgres que armazena seu inventário — inicia com um único comando. O inventário persiste em um volume nomeado, então ele sobrevive a reinicializações de contêiner e docker compose down / recriação (apenas down -v o apaga).

docker compose up --build        # http://localhost:3000
  • GET /health — liveness
  • GET /api/inventory — tintas possuídas (do Postgres)
  • POST /mcp — MCP para Claude Desktop · POST /mcp/v3 — MCP para ChatGPT (search/fetch)

O armazenamento é selecionado por DATABASE_URL: defina-o (como docker-compose.yml faz) para Postgres, deixe-o não definido para usar um arquivo de inventário JSON local (comportamento local inalterado). Veja .env.example.

Implantação

Qualquer host Docker + Postgres funciona (Fly.io, Railway, um VPS…). Para um servidor MCP remoto com um clique e banco de dados gerenciado, o repositório inclui um Render Blueprint (render.yaml) que provisiona o serviço web e o Postgres juntos e configura DATABASE_URL automaticamente:

Deploy to Render

A implantação de referência warpaint-mcp.fly.dev roda no Fly.io com Fly Managed Postgres — veja docs/deploy-fly.md para os passos de fly mpg attach + migração.

Instalação

A maneira mais rápida — execute direto do npm com npx, sem clonar, sem instalar:

npx minipainter paint search bone
npx minipainter match color "#d2c29b"
npx minipainter tui

Ou instale globalmente para obter o comando curto mpaint em qualquer lugar:

npm install -g minipainter
mpaint paint search bone
mpaint match color "#d2c29b"

O catálogo é incluído, então busca e correspondência funcionam na primeira execução sem nada para configurar. Seu inventário fica em ~/.minipainting/inventory.json, criado automaticamente na primeira vez que você marca uma tinta como possuída (o legado ~/.warpaint/ é migrado automaticamente).

Requisitos:

  • Node.js 18 ou mais recente
  • shell POSIX-ish (Linux, macOS, WSL)

A partir do código-fonte

Para hackear, clone e execute contra a árvore de trabalho:

git clone https://github.com/ArturSkowronski/minipainter.git
cd minipainter
npm install
node src/cli.mjs paint search bone

Depois disso, você tem quatro modos de uso:

  • CLI / TUI — veja o Quickstart abaixo
  • Servidor HTTP auto-hospedado — um runtime único amigável ao Docker com armazenamento JSON e endpoints de API
  • MCP local para Claude Desktop — veja Configuração do MCP para Claude Desktop
  • MCP remoto para Claude mobile/web — veja MCP remoto

Quickstart

Inicialize o inventário local em ~/.minipainting/inventory.json:

node src/cli.mjs catalog sync

Pesquise tintas:

node src/cli.mjs paint search black
node src/cli.mjs paint search bone --json

Inspecione uma tinta:

node src/cli.mjs paint show "Abaddon Black" --json

Marque tintas como possuídas ou ausentes:

node src/cli.mjs inventory own "Abaddon Black"
node src/cli.mjs inventory unown "Abaddon Black"
node src/cli.mjs inventory list

Execute correspondência semântica ou de cores:

node src/cli.mjs match describe bone
node src/cli.mjs match color "#d2c29b"

Inicie a TUI:

node src/cli.mjs tui

Execute o servidor MCP localmente:

node src/mcp-server.mjs

Execute o servidor HTTP auto-hospedado localmente:

DATA_DIR=.minipainting-data node src/mcp-http-server.mjs

Fluxo de trabalho da TUI

A TUI é centrada em três áreas de apresentação:

  • FORGE CATALOG: tintas visíveis no escopo atual
  • SELECTED PIGMENT: a tinta atualmente destacada com fornecedor, famílias, uso e RGB
  • RITUAL COMMANDS: a legenda de comandos para a sessão ativa

Comandos atuais da TUI:

  • search <text>
  • owned
  • catalog
  • toggle
  • quit

Uso recomendado:

  1. comece com catalog
  2. restrinja com search bone, search black ou consultas semelhantes
  3. inspecione o painel de pigmento selecionado
  4. alterne a propriedade conforme sua coleção muda

Direção do projeto

Implementado agora:

  • registro JSON local
  • catálogos iniciais de fornecedores para Citadel e Army Painter
  • rastreamento de inventário possuído / ausente
  • busca determinística e correspondência de cores
  • apresentação de terminal colorida com amostras por tinta
  • servidor MCP local para Claude Desktop

Planejado depois:

  • uma habilidade separada para analisar links de conjuntos de tintas
  • preenchimento de inventário orientado por imagens a partir de fotos de frascos de tinta
  • análise de fotos de modelos que recomenda tintas possuídas primeiro
  • equivalentes entre fornecedores mais fortes e dicas de correspondência

Notas técnicas

  • Os dados do catálogo embutido vivem em data/catalog/ (Citadel e Army Painter, mantidos no controle de versão)
  • Arquivo de inventário: ~/.minipainting/inventory.json — armazena apenas IDs de tintas possuídas na forma { "version": 1, "owned": ["citadel/abaddon-black", ...] }
  • Diretório de dados do servidor auto-hospedado: DATA_DIR (padrão /data no Docker); o inventário fica em <DATA_DIR>/inventory.json
  • O catálogo e o inventário são compostos em tempo de execução; salvar nunca reescreve o catálogo
  • IDs são estáveis por convenção (fornecedor + slug do nome); ao carregar, IDs possuídos ausentes do catálogo são relatados como avisos em vez de serem descartados silenciosamente
  • Um .minipainting/registry.json pré-existente local ao projeto, ao lado do caminho do inventário, é migrado automaticamente na primeira execução
  • Diretórios de dados legados .warpaint/ são renomeados automaticamente para .minipainting/ na primeira execução (variantes de home e local ao projeto)
  • Substitua a localização do inventário na superfície da API com { inventoryPath } ou { cwd } (o último resolve para <cwd>/.minipainting/inventory.json, que é o que a suíte de testes usa para isolamento)
  • Valores RGB são cores de referência aproximadas para correspondência, não uma garantia da aparência final pintada
  • Ponto de entrada do MCP: node src/mcp-server.mjs
  • Script auxiliar do MCP: npm run mcp
  • Script auxiliar do servidor HTTP: npm run server
  • As capturas de demonstração do README são reproduzíveis via:
npm run generate:demo

Configuração do MCP para Claude Desktop

minipainter agora inclui um servidor MCP local para que o Claude Desktop possa usar seu registro de tintas diretamente.

Exemplo de configuração local do MCP:

{
  "mcpServers": {
    "minipainter": {
      "command": "node",
      "args": ["/absolute/path/to/minipainter/src/mcp-server.mjs"]
    }
  }
}

Após adicionar o servidor, o Claude Desktop pode chamar ferramentas como:

  • paint_search
  • paint_show
  • inventory_list
  • inventory_mark_owned
  • inventory_mark_unowned
  • match_color
  • match_describe

Fluxo local sugerido:

  1. inicialize seu registro uma vez com node src/cli.mjs catalog sync
  2. adicione o servidor MCP ao Claude Desktop
  3. peça ao Claude para pesquisar tintas ou atualizar a propriedade através das ferramentas expostas

Habilidade de agente

Para Claude Code, busque a habilidade direto do site, sem clonar. Ela vem com as proteções certas embutidas: leituras somente JSON, regras product_format, tratamento de falhas.

# project-scoped
mkdir -p .claude/skills/minipainter
curl -fsSL https://arturskowronski.github.io/minipainter/SKILL.md \
  -o .claude/skills/minipainter/SKILL.md

Ou salve-a em ~/.claude/skills/minipainter/SKILL.md para usá-la em qualquer lugar.

Docker auto-hospedado

A imagem Docker executa um runtime de servidor HTTP único projetado para uso auto-hospedado. Construa-a a partir do repositório (nenhuma imagem é publicada em um registro ainda):

docker build -t minipainter .
docker run -p 3000:3000 -v minipainting-data:/data minipainter

Ou suba o servidor junto com Postgres em um único passo com docker compose up -d.

O servidor expõe:

  • GET /health
  • GET /api/paints
  • GET /api/paints/:paint
  • GET /api/inventory
  • PUT /api/inventory/:paint
  • DELETE /api/inventory/:paint
  • POST /api/match/color
  • POST /api/match/describe
  • POST /mcp

Configuração opcional em tempo de execução:

  • PORT — porta de escuta, padrão 3000
  • DATA_DIR — diretório de estado persistente, padrão /data no Docker
  • AUTH_TOKEN — protege /api/* e /mcp com Authorization: Bearer ...
  • INVENTORY_SYNC_TOKEN — protege o endpoint de sincronização legado /inventory

MCP remoto (Claude Mobile)

Para Claude mobile ou web, o servidor MCP stdio acima não é alcançável. Execute minipainter-mcp-http em vez disso — um transporte MCP HTTP Streamable expondo as mesmas ferramentas, além de GET/POST /inventory para sincronizar o inventário local.

Teste de fumaça local

export INVENTORY_SYNC_TOKEN=$(openssl rand -hex 32)
export PORT=3000
export INVENTORY_PATH=$HOME/.minipainting/inventory.json
npm run mcp:http

Então, em outro shell:

curl -s http://localhost:3000/health
curl -s -X POST http://localhost:3000/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

# Sync endpoint (bearer-token protected)
curl -s -H "Authorization: Bearer $INVENTORY_SYNC_TOKEN" \
     http://localhost:3000/inventory

Implantar no Fly.io

O repositório inclui um Dockerfile e fly.toml. Receita completa em docs/deploy-fly.md. Versão curta:

fly launch --no-deploy --copy-config --name <your-app-name>
fly volumes create inventory_data --region <your-region> --size 1
fly secrets set INVENTORY_SYNC_TOKEN="$(openssl rand -hex 32)"
fly deploy

Conectar Claude

No Claude (mobile ou web), adicione um conector personalizado:

  • URL: https://<your-app-name>.fly.dev/mcp

O endpoint /mcp atualmente não tem autenticação — qualquer pessoa com a URL pode chamar ferramentas. Use a obscuridade da URL mais os controles de rede do Fly por enquanto; adicione autenticação por usuário antes de compartilhar a URL.

Variáveis de ambiente

VariávelObrigatórioPropósito
INVENTORY_SYNC_TOKENpara /inventoryToken Bearer protegendo GET/POST /inventory; quando não definido, a sincronização retorna 503
INVENTORY_PATHnãoCaminho para inventory.json; padrão ~/.minipainting/inventory.json localmente, /data/inventory.json na imagem Docker
INVENTORY_JSONnãoJSON de semente único; usado apenas quando INVENTORY_PATH está ausente no primeiro boot
WARPAINT_INVENTORY_JSONnãoAlias legado de INVENTORY_JSON
MCP_SERVER_NAMEnãoNome do servidor no handshake do MCP + log de inicialização; padrão paint-inventory
PORTnão (padrão 3000)Porta TCP para escutar

Limitações conhecidas

  • /mcp ainda não tem autenticação. O token Bearer protege apenas /inventory.
  • Transporte sem estado: sem streams de ferramentas SSE de longa duração (as ferramentas são rápidas, então isso é aceitável).

Auto-hospedando seu próprio MCP

O servidor MCP é genérico — apenas a CLI (mpaint) é marcada. Para executar sua própria instância:

  1. Faça um fork ou clone do repositório.

  2. (Opcional) renomeie seu app Fly em fly.toml.

  3. Crie um volume Fly e defina segredos:

    fly volumes create inventory_data --size 1 --region <your-region>
    fly secrets set INVENTORY_SYNC_TOKEN=$(openssl rand -hex 24)
    # Optional one-time seed:
    fly secrets set INVENTORY_JSON="$(cat ~/.minipainting/inventory.json)"
    
  4. (Opcional) nomeie seu servidor MCP (mostrado no handshake do MCP e nos logs de inicialização):

    fly secrets set MCP_SERVER_NAME=my-paints
    
  5. Implante:

    fly deploy
    
  6. Registre o remoto em sua CLI local e sincronize:

    mpaint sync add default \
      --url https://my-app.fly.dev \
      --token <token-from-step-3>
    mpaint sync push
    

Depois disso, seu inventário local e o MCP implantado permanecem sincronizados via mpaint sync push (upload local → remoto) e mpaint sync pull --force (sobrescrever local a partir do remoto).

Licença

MIT © Artur Skowronski