@noclaf/cli-core
v1.11.0
Published
Core whitelabel de MCP server + CLI para gestão de projetos a partir de assistentes de IA: módulos por domínio, marca injetada, pacote de cliente por cima.
Readme
@noclaf/cli-core
Core whitelabel de MCP server + CLI para gestão de projetos a partir de assistentes de IA (Claude Code, Codex CLI, Copilot, Cowork). O core não tem marca nem binário: cada cliente é um pacote pequeno que escolhe os módulos, passa a marca e publica o próprio CLI.
Montar um cliente
// src/app.ts
import { fileURLToPath } from 'node:url';
import { coreModules } from '@noclaf/cli-core/modules';
import type { ClientOptions } from '@noclaf/cli-core/cli';
import { brand } from './brand.js';
export const options = {
brand,
modules: coreModules,
packageRoot: fileURLToPath(new URL('..', import.meta.url)),
supabase: { url: 'https://<projeto>.supabase.co', anonKey: '<anon key>' },
} satisfies ClientOptions;// src/cli.ts
#!/usr/bin/env node
import { runCli } from '@noclaf/cli-core/cli';
import { options } from './app.js';
await runCli(options);src/mcp-server.ts é igual, com runMcpServer de @noclaf/cli-core/mcp.
O package.json do cliente declara os bin apontando pra dist/cli.js e dist/mcp-server.js e depende do core por
major (^1.0.0). packageRoot é a raiz do pacote do cliente: dele saem a versão, o caminho do binário MCP gravado nos
configs e o conteúdo da extensão .mcpb. A tabela de tools e comandos do README do cliente sai de
updateReadme(options, caminhoDoReadme), entre <!-- TOOLS:START/END --> e <!-- COMMANDS:START/END -->.
Referência viva: nos-cli (todos os módulos) e qas-cli (sem time).
Exports
| Subpath | O que tem |
| --- | --- |
| . | createApp, defineModule, defineTool, projectIdArg, z, NotFoundError, LOCALES, tipos (Brand, Module, ports, entidades) |
| ./modules | coreModules e cada módulo: projects, tasks, time, delivery, exec, sprints, releases, calendar, homeOffice |
| ./modules/<nome> | um módulo só |
| ./adapters/supabase | gateway, client autenticado, helpers de query |
| ./cli | runCli, buildProgram, updateReadme, ClientOptions |
| ./mcp | runMcpServer |
Use o z reexportado pelo core nos módulos de cliente: o inputSchema das tools sai do zod do core, e o zod é pinado
em versão exata porque a saída do JSON Schema muda entre minors.
Marca
Brand (em .) junta nome do binário, prefixos das tools (toolPrefix pras tools de produto, cliToolPrefix pras
que espelham o CLI; os dois ^[a-z][a-z0-9]*_$), prefixo de env, pasta em ~, arquivo de config do repo, PAT, repo
e pacote de skills e o locale. createApp registra a marca; textos, env e caminhos do core leem dela. Um processo
roda uma marca por vez.
Módulos
Cada domínio é um módulo em src/modules/<nome>/: tools/, i18n/<locale>.ts, port.ts (o que ele exige do
gateway) e supabase*.ts (o adapter desse port). createApp({ brand, modules }) monta o registry MCP e os comandos do
CLI dos módulos ligados; requires declara dependência entre módulos e falha cedo quando falta. O locale vem da marca:
um módulo que não traz o locale da marca não compila.
- Entidade do core é fechada. Campo a mais em task, sprint ou release não entra no módulo do core: vira um módulo
do cliente, com tabela própria (
task_id), tool própria e i18n própria. Exemplo completo emexamples/module-extra/(migration, port, adapter, tool, i18n e o cliente ligando tudo); o adapter entra porsupabase.gatewaynas opções do cliente.bun run check:examplescompila o exemplo contra o build do pacote. - Módulo não conhece a marca. Tools recebem os textos já resolvidos e o prefixo entra na montagem.
- Backend só no
supabase*.tsdo módulo. O resto fala com oport.ts.
Contrato de backend
Cada módulo declara em supabase-contract.ts as tabelas que espera: colunas, tipo, nulidade, referências e as regras
de acesso (estas em i18n/<locale>-backend.ts). As colunas são checadas pelo compilador contra database.types.ts, e
um teste garante que toda tabela e coluna que o adapter lê está no contrato do módulo ou das suas dependências. O
núcleo (perfis e a edge function que troca o PAT) fica em adapters/supabase/contract.ts.
No cliente:
<bin> backend contract -o BACKEND.mdgera o contrato em Markdown só para os módulos ligados. É o documento para quem implementa o backend.<bin> backend checkvalida o Supabase configurado: tabelas, colunas, tipos, valores de enum, a função de PAT e se o anon lê alguma linha. Logado, também confere o acesso de um usuário real. Sai com código 1 se houver erro;--jsonpara automação.
Módulo do cliente com tabela própria declara backend(t) devolvendo um BackendContract (o helper tipado tables()
só cobre as tabelas do core).
Documentação
<bin> docs gera a documentação do cliente num HTML único e abre no navegador: guias (instalação, autenticação,
backend), comandos gerais e uma página por módulo ligado, com os comandos de CLI, as tools MCP e o contrato de backend.
-o <arquivo> grava onde quiser, --no-open não abre o navegador e --json emite só o modelo. O visual vem de
@noclaf/cli-docs.
O conteúdo sai do próprio app montado, sem arquivo de documentação à parte. Cada módulo só declara docs(t) com título,
resumo e, se quiser, uma visão geral em Markdown (os textos ficam no i18n do módulo). O logo vem de brand.icon (URL ou
data URI).
Histórico de versões da documentação
Se o pacote do cliente traz um docs-history.json na raiz, o docs mostra o seletor de versões, a comparação e o
changelog. O arquivo é gerado no release, a partir do histórico publicado na versão anterior (sem retroativo: o primeiro
release com a etapa cria um histórico de uma versão). Histórico de outro pacote, malformado ou de outro formato é
ignorado.
No workflow de publicação do cliente (checkout com fetch-depth: 0), depois do build:
- name: Docs history
env: { NO_UPDATE_NOTIFIER: '1' }
run: |
node dist/cli.js docs --json > "$RUNNER_TEMP/model.json"
npx cli-core-docs-history --model "$RUNNER_TEMP/model.json" --out docs-history.jsonInclua docs-history.json em files e no .gitignore. Flags: --model <arquivo|->, --repo ., --out, --dry-run,
--tmp <dir>. O tamanho é checado (aviso acima de 64 KB, erro acima de 128 KB). As notas vêm dos commits feat:/fix:
desde a tag anterior, do corpo do commit de release e do CHANGELOG do core na faixa de versões.
Versionamento
Semver com changesets. Todo PR que muda o pacote traz um bunx changeset.
| Mudança | Versão |
| --- | --- |
| Nome ou schema de tool (fixture tool-schemas), contrato de módulo, port, ClientOptions, migration destrutiva | major |
| Tool nova, módulo novo, export novo, migration aditiva | minor |
| Texto, correção | patch |
Publicar
bun run release # aplica os changesets: versão + CHANGELOG
git commit -am "release v$(node -p "require('./package.json').version")"
git tag -a "v$(node -p "require('./package.json').version")" -m release && git push --follow-tagsA tag v* dispara .github/workflows/publish.yml (npm público, trusted publishing via OIDC). O CI de PR roda lint,
testes, build e, com a variable CLIENT_REPO definida, instala o cliente contra o tarball do PR (canary).
O mesmo workflow gera o site geral da documentação (todos os módulos, marca neutra) e anexa como artefato
docs-site-<tag> do run (fica 90 dias). Ele não é publicado; localmente, bun run docs:site grava em site/index.html.
Convenções
- Idioma: textos em pt-BR (por módulo, em
i18n/). Código em inglês. - Backups: escrita em arquivo de config existente gera
.bak.<timestamp>ao lado. - stdout reservado pro JSON-RPC no MCP server; logs vão pra stderr.
- Testes rodam com a marca fixture de
src/__tests__/brand.ts, carregada pelobunfig.toml.
