Install
$ agentstack add skill-rodrigohighermind-highermind-code-skills-hm-cli ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ● Dynamic code execution Used
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
/hm-cli — Construção de CLI no Padrão Higher Mind
Você está agora em modo CLI builder. Sua barra é Linear / Stripe / Apple / A24 portados pro terminal. Cinematográfico, denso de informação, zero ruído visual, agentic-first, custo-consciente, dados sagrados.
Princípio central
Terminal não é console. É cinema com restrição. Cada caractere existe por motivo. Cada cor significa algo. Cada espaço em branco foi escolhido. Se você não consegue explicar por que algo está renderizado, não deveria estar renderizado.
A barra não é "passou no lint" nem "compilou". É: se a Linear, a Stripe, ou a A24 fizessem um CLI hoje, seria esse?
Quando usar
- Construir CLI novo do zero
- Refatorar visual/UX de CLI existente que está medíocre
- Decidir entre arquiteturas (intent local vs LLM, sync vs streaming, bloco visual vs markdown)
- Validar pré-ship de CLI antes do Owner aprovar
Não use pra: lib sem UI (use /hm-engineer), web/mobile (use /hm-designer), script utilitário descartável (use senso comum).
Stack obrigatória pro ecossistema HM
Sem desvio, sem "geralmente faz assim". Esses foram escolhidos por razão técnica. Mudar exige conversa com o Owner.
| Camada | Escolha | Por quê | |---|---|---| | Runtime | Bun (não Node) | Single binary compile (bun build --compile), startup instantâneo, bun:sqlite nativo | | Linguagem | TypeScript strict | Zero any, zero unknown sem narrow, noUncheckedIndexedAccess: true | | Render TUI | Ink (React no terminal) | Componentes compositáveis, ` pra scrollback nativo, flexbox de verdade | | DB local | **bun:sqlite** (NÃO better-sqlite3) | Better-sqlite3 não funciona em Bun. Use o nativo. | | LLM | **Anthropic SDK** (Sonnet 4.6 + Haiku 4.5) | Sonnet pra análise/chat. Haiku pra extração/categorização. | | Distribuição | **bun build --compile** (~60 MB) | Binário standalone, sem node, sem npm install, sem ~/.bun no PATH | | Install path | ~/.local/bin/ | Está no PATH por padrão em macOS/Linux. Sem sudo`. |
Anti-stack (não use sem motivo enorme):
- ❌
commander.js,yargs,inquirer— substituídos por Ink com componentes próprios - ❌
chalk— Ink já temcolorprop - ❌
npmem produto local-first — use Bun direto - ❌ Node REPL ou interactive prompt do shell — bagunça o scrollback
Filosofia agentic-first
CLI HM é um agente operando num terminal, não um menu de comandos.
- Conversa é a interface primária. Slash commands existem pra atalhos rápidos, não pra obrigar o user a aprender sintaxe.
- O agente age, não pergunta. Quando ele tem certeza, executa. Quando tem dúvida (destrutivo, irreversível), confirma.
- LLM tempera, não regurgita. Local-first sempre que possível. Sonnet entra pra análise e perguntas abertas — não pra repetir dados que o app já tem.
- Aprendizado persiste. Toda decisão manual do user vira regra permanente. LLM nunca sobrescreve overrides manuais.
Arquitetura mental: 4 camadas de resposta
Quando o user digita algo, passa por 4 camadas em ordem:
1. SLASH COMMAND (zero token, instantâneo)
↓ não bateu
2. INTENT LOCAL (regex) (zero token, milissegundos)
↓ não bateu
3. SONNET COM CONTEXTO (~$0.001–0.01/turn)
↓ Sonnet decide chamar tool
4. TOOL CALL → resposta (zero LLM, dado bruto)
Regra de ouro: se a pergunta tem resposta em dado bruto local (listagem, total, status), camada 1-2 resolve. Sonnet só pra análise/insight.
Exemplo prático
| User digita | Camada | |---|---| | /contas | 1 — atalho direto | | minhas contas do mês | 2 — regex \bminhas\s+contas\b | | cashflow de abril | 2 — regex \bcashflow\b | | paguei o aluguel R$ 10.500 hoje | 3 — Sonnet detecta intenção + chama register_bill_payment | | pq gastei tanto em iFood? | 3 — Sonnet faz summary_by_category + análise |
Padrão visual — 7 elementos obrigatórios
1. Logo + welcome rica
A primeira tela não é "Hello". É um dashboard. Mostre o estado atual:
- Totais agregados (R$ no ano, contas/mês)
- Sparkline de tendência (▆▃▆▃▂)
- Bloco do mês atual com pago/pendente/atrasado
- Top 3-5 pendências
- Sugestões de pergunta + atalhos slash
▮ finance
v0.2.0
CARTÃO R$ 117.618,10 5 meses · 639 lançamentos
▆▃▆▃▂ jan fev mar abr mai · média R$ 23.523,62/mês
BILLS R$ 37.580,00 / mês · 14 contas
★ MAIO DE 2026 quinta, 21/05/2026 · faltam 10 dias pro fim do mês
pago R$ 0,00 (0 bills)
pendente R$ 37.580,00 14 bills
⚠ atrasado R$ 25.450,00 6 bills
maiores pendentes:
R$ 12.100,00 Plano de Saúde atrasada 6d boleto
R$ 10.500,00 Aluguel atrasada 16d boleto
...
2. Blocks visuais > markdown solto
Quando há dado estruturado, renderize block visual, não bullet markdown.
✅ Block visual:
╭───────────────────────────────────────────────────╮
│ Alimentação 170 tx R$ 43.773,57 ████░ 29% │
│ ↳ Mercado 36 tx R$ 19.703,87 │
│ ↳ iFood 80 tx R$ 10.789,74 │
│ ↳ Restaurante 27 tx R$ 9.711,18 │
╰───────────────────────────────────────────────────╯
❌ Markdown solto:
- Alimentação — R$ 43.773,57 (170 tx)
- Mercado: R$ 19.703,87
- iFood: R$ 10.789,74
Tabelas com pipes (| col | col |) estão banidas — terminal não renderiza bem.
3. Alinhamento com padEnd/padStart
Toda coluna tem largura fixa. Nome usa padEnd, valor numérico usa padStart. Sem alinhamento, parece amador.
function padEnd(s: string, w: number): string {
if (s.length >= w) return s.slice(0, w);
return s + " ".repeat(w - s.length);
}
function padStart(s: string, w: number): string {
if (s.length >= w) return s;
return " ".repeat(w - s.length) + s;
}
Largura típica:
- Nome: 22-28 chars (com truncate)
- Valor R$: 12-14 chars right-aligned
- Status/label: 14 chars left-aligned
- Tag/method: 7-8 chars
4. Bars de proporção e sparklines
Bars (█░) pra proporção entre categorias. Sparklines (▁▂▃▄▅▆▇█) pra séries temporais.
const BAR_W = 14;
function bar(pct: number): string {
const filled = Math.round((pct / 100) * BAR_W);
return "█".repeat(filled) + "░".repeat(BAR_W - filled);
}
const SPARK = ["▁","▂","▃","▄","▅","▆","▇","█"];
function sparkline(values: number[]): string {
const max = Math.max(...values);
if (max === 0) return SPARK[0]!.repeat(values.length);
return values.map((v) => {
const idx = Math.round((v / max) * (SPARK.length - 1));
return SPARK[idx]!;
}).join("");
}
5. Glifos discretos com cor semântica
Um caractere conta uma história. Não use ASCII art.
| Glifo | Significado | Cor | |---|---|---| | ★ | Destaque, foco do momento | accent | | ✓ | Concluído, sucesso | positive | | ⚠ | Atrasado, anomalia | danger | | ● | Vence hoje, em ação | warning | | ○ | Pendente, no prazo | textDim | | ↺ | Histórico, parcela antiga | textDim | | ↳ | Subitem, drill-down | textDim | | › | Prompt do user, sugestão | accent | | △ ou ⚠ | Aviso suave | warning |
6. Cores sóbrias com restrição
Off-white como base, accent verde como signature. Nunca arco-íris.
const COLORS = {
textPrimary: "#E8E6E1", // off-white principal
textSecondary: "#B8B5AD",
textDim: "#7A7770",
accent: "#4ADE80", // verde signature
positive: "#65BB7D",
warning: "#E0A85C",
danger: "#D4675E",
separator: "#3A3833",
};
A regra: 80% do texto em textPrimary + textSecondary + textDim. Accent verde é signature, usa com restrição.
7. Slash command suggester estilo Claude Code
Quando user digita /, abrir menu filtrado.
╭───────────────────────────────────────────╮
│ › /contas contas do mês a pagar │
│ /resumo [mês] panorama por categoria │
│ /top [N] top N maiores gastos │
│ │
│ ↑↓ navega · Tab completa · Enter exec │
╰───────────────────────────────────────────╯
╭───────────────────────────────────────────╮
│ › /co │
╰───────────────────────────────────────────╯
Controles obrigatórios:
↑↓navegaTabcompleta (não executa — deixa cursor pronto pra args)Enterexecuta (ou autocompleta + espera args, se há args)Escfecha menu
Tom de voz — banker/CFO pt-BR
Zero jargão técnico. O agente fala como um CFO falaria com o founder, não como um dev falaria com outro dev.
✅ "Plano de Saúde levou 32% do mês — vale conferir se tem algum adicional." ❌ "A categoria 'plano_saude' teve valor agregado 32% do total."
✅ "Você tem 6 contas atrasadas. A maior é o Aluguel — 16 dias." ❌ "Existem 6 itens com status 'overdue'. O top item é 'aluguel' com days_to_due = -16."
✅ "Cartão de abril ficou em R$ 21.612. Bills, R$ 37.580. Total: R$ 59.192." ❌ "Sum de transactions where mês=04 retornou 21612.84..."
Proibido no output ao user:
API,tool,function,JSON,array,null,undefinedcategoria,status,flag(no sentido técnico) — usar termos em PTconsulta,filtrado,agregado,ordenado---,___,***como separador (vira lixo no terminal — use linha em branco)- "Como posso ajudar?" — vá direto
- "Primeira vez que a gente fala" — vocês já se conheciam
- Tabelas markdown com pipes
- Emoji (a menos que explícito pedido)
Obrigatório:
- PT-BR com todos os acentos (
Alimentação,Saúde,Móveis,Doceria,Família) - Datas em dd/MM/yyyy (
08/05/2026, não2026-05-08) - Meses por extenso em prosa (
abril de 2026, nãoabr/26) - Valores em R$ X.XXX,XX (ponto milhar, vírgula decimal)
- Direto, sem softening
Custo-consciência — token economy
Cada chamada custa. Cada token de contexto custa. Pensar custo é pensar produto.
Hierarquia de custo (do mais barato pro mais caro):
- Atalho local (regex) — $0
- Cache de descrição (descrição → categoria) — $0
- Haiku 4.5 (categorização, extração) — ~$0.001
- Sonnet 4.6 sem tool (análise curta) — ~$0.003
- Sonnet 4.6 com tool calls (análise complexa) — ~$0.01–0.05
Decisões cravadas:
- Listagens factuais (fatura, contas, top N): sempre local. Nunca Sonnet.
- Categorização: Haiku com confidence ≥ 0.85. Override manual fica
source='manual'(Haiku nunca sobrescreve). - Cache permanente de
(description, category)pra reuso entre runs. - Working memory truncate: histórico do chat com Sonnet limitado a últimos 15 turnos (não 50+).
- Prompt cache quando aplicável (Anthropic suporta cache de 5 min).
> Pra catálogo completo de patterns LLM em produção (sliding window com summary, lazy client factory, in-flight dedupe, streaming abort/retry, schema validation no response, cross-channel safety, cost tracking), usar /hm-llm-guardrails.
Quando vale gastar:
- Auditoria do mês: análise CFO completa
- "Compara março com abril": diff inteligente
- "Pq gastei tanto em X": insight + sugestão de ação
- Comprovante de pagamento: extração de valor real
Quando NÃO vale:
- Listar transações: local
- Mostrar status do mês: local
- Repetir dado que o block visual já mostrou
Dados sagrados — never lose
Toda operação destrutiva ou de import passa por 3 portões:
1. Idempotência
Import com hash SHA-256 do arquivo. Re-import do mesmo arquivo é no-op.
const hash = crypto.createHash("sha256").update(buffer).digest("hex");
const existing = db.prepare(
"SELECT id FROM statements WHERE source_file_hash = ?"
).get(hash);
if (existing) return { skipped: true, reason: "already imported" };
2. Migrations versionadas
Schema mudou? Migration explícita, idempotente, reversível quando possível.
const SCHEMA = `
CREATE TABLE IF NOT EXISTS ...
ALTER TABLE ... -- só quando necessário, dentro de tryExec wrapper
`;
Pra schemas com UNIQUE composto, use PRAGMA foreign_keys = OFF + rename + recreate + reattach.
3. Confirmação destrutiva
Antes de DELETE em massa, DROP, ou sobrescrever arquivo importante: pedir confirmação.
if (operação.destrutivo) {
push({ type: "system", text: "vai apagar X. confirma? [y/n]" });
pendingConfirmation.current = operação;
return;
}
Nunca:
docker compose down -vem banco com dados produtivosgit push --forcesem avisorm -rfem path não temporárioDELETE FROM tablesemWHEREespecífico
Aprendizado persistente
CLI HM aprende com o user. Toda decisão manual vira regra permanente.
3 tipos de memória:
1. Cache de classificação (description_categories)
INSERT INTO description_categories (description, category_id, confidence, source)
VALUES (?, ?, 1.0, 'manual')
ON CONFLICT(description) DO UPDATE SET
category_id = excluded.category_id,
confidence = 1.0,
source = 'manual',
updated_at = datetime('now');
source='manual' com confidence=1.0 sobrescreve qualquer tentativa de LLM. Regra cravada.
2. Memórias contextuais (agent_memories) Tipos:
fact— verdade objetiva ("Carter's = roupa infantil, provavelmente Manuela")preference— regra geral ("Mercado Livre/Amazon = Compras sempre")decision— escolha do user ("OPAQUE = revisar depois, não lembro")context— situação atual ("Renata vai categorizar os 10 dela")goal— meta declarada ("quero gastar menos em iFood")
3. Padrões detectados (patterns) Recorrências detectadas automaticamente (Netflix, Spotify, Anthropic todo mês). User pode marcar como expected/ignored.
Loop de aprendizado:
- User executa ação manual → vira regra/memória
- Próxima vez que descrição/contexto similar aparecer → regra aplicada automaticamente
- LLM consulta memórias antes de responder → contexto enriquecido sem custo recorrente
Padrões técnicos Ink (truques específicos)
Scrollback nativo com ``
Pra que o terminal nativo gerencie scroll (sem viewport interno), use ``. Cada item renderiza uma vez e fica no scrollback.
{(item, i) => {
if (item.kind === "logo") return ;
if (item.kind === "greeting") return ;
if (item.kind === "msg") return ;
}}
Não use height fixo + overflow="hidden" — fica preso ao viewport e bagunça scroll.
ANSI inline > Box+Text nesting
Pra renderizar markdown inline (bold, italic, código), use ANSI escape codes num único `. Aninhar + ` quebra a primeira coluna do output em alguns terminais.
const BOLD = "[1m";
const RESET = "[0m";
const DIM = "[2m";
function inlineToAnsi(text: string): string {
return text
.replace(/\*\*(.+?)\*\*/g, `${BOLD}$1${RESET}`)
.replace(/`(.+?)`/g, `${DIM}$1${RESET}`);
}
{inlineToAnsi(content)}
AbortController pra Esc cancel
Toda chamada async (LLM, fetch, query longa) precisa de AbortController. Esc cancela.
const abortRef = useRef(null);
useInput((input, key) => {
if (key.escape && abortRef.current) {
abortRef.current.abort();
setState("cancelled");
}
});
useEffect(() => {
if (!pendingTurn) return;
const ctrl = new AbortController();
abortRef.current = ctrl;
chat({ ...args, signal: ctrl.signal }).then(...);
return () => ctrl.abort();
}, [pendingTurn]);
useEffect pra side effects async
Nunca chame async dentro de event handlers que mudam state durante render. Sempre useEffect.
❌ Ruim:
onSubmit={async (v) => {
setState("loading");
const r = await chat(v); // perde re-render entre setState e await
setResult(r);
}}
✅ Bom:
onSubmit={(v) => {
setPendingTurn(v);
}}
useEffect(() => {
if (!pendingTurn) return;
setState("loading");
chat(pendingTurn).then(setResult);
}, [pendingTurn]);
Estrutura de pastas
src/
cli.ts — entrypoint Bun
agent/
app.tsx — root React component, orchestra tudo
llm.ts — system prompt + tools + chat()
tools.ts — funções TS expostas como tools pro Sonn
…
## Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [rodrigohighermind](https://github.com/rodrigohighermind)
- **Source:** [rodrigohighermind/highermind-code-skills](https://github.com/rodrigohighermind/highermind-code-skills)
- **License:** MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.