7. A CLI
Um ponto de entrada de 30 linhas que prova o core de IA sem nenhuma UI.
src/cli.ts é o menor ponto de entrada que o LocalLens tem. Ele lê
dois valores de argv, instancia LocalLensApp, roda uma round-trip,
faz cleanup.
O arquivo inteiro
import type { Brain } from "./domain.ts";
import { LocalLensApp } from "./locallens.ts";
const [folderPath, ...questionParts] = Bun.argv.slice(2);
const question = questionParts.join(" ").trim();
if (!folderPath || !question) {
console.log('Usage: bun run cli <folder-path> "question about the folder"');
process.exit(1);
}
const app = new LocalLensApp();
let brain: Brain | undefined;
try {
brain = await app.createBrainFromFolder({
name: folderPath.split(/[\\/]/).filter(Boolean).at(-1) ?? "cli-brain",
folderPath,
});
const result = await app.askBrain(brain.id, question);
console.log(`\n${result.answer}\n`);
if (result.citations.length > 0) {
console.log("Sources:");
for (const citation of result.citations) {
console.log(`- ${citation.relativePath}#${citation.chunkIndex}`);
}
}
} finally {
if (brain) await app.deleteBrain(brain.id).catch(() => undefined);
await app.close();
}O padrão de brain temporário
A CLI é uma ferramenta one-shot. Ela cria um brain, usa, e
apaga antes de sair. É por isso que o cleanup mora no
finally:
} finally {
if (brain) await app.deleteBrain(brain.id).catch(() => undefined);
await app.close();
}Por duas razões:
- Sem drift no JSON store. Rodar a CLI cem
vezes não deixa cem entradas em
.locallens/store.json. - Sem workspaces sobrando. O workspace QVAC fecha e apaga junto com o brain, então uso de disco fica limitado.
.catch(() => undefined) no deleteBrain é intencional. Se o
passo de criação do brain falhou, close() ainda precisa rodar.
Engolir o erro de cleanup mantém o throw original a caminho
para cima.
Nomeando o brain
name: folderPath.split(/[\\/]/).filter(Boolean).at(-1) ?? "cli-brain",A CLI deriva o nome do brain do último segmento do
caminho da pasta. examples/sample-brain vira "sample-brain".
O fallback ?? "cli-brain" cobre caminhos tipo /. Só o suficiente
para fazer a entrada JSON legível enquanto o brain existe brevemente.
Formatação de saída
A CLI imprime a resposta e um bloco Sources:. Sem renderização
Markdown, sem display de streaming, sem barra de progresso. Isso é
deliberado — a CLI é a prova mais simples de que o pipeline
funciona. Interações mais bonitas vivem no app do navegador.
LocalLens uses QWEN3_1_7B_INST_Q4 because it offers a strong balance of answer
quality and local resource use [1]. A 600M fallback is wired in for slimmer
machines [2].
Sources:
- locallens.md#0
- qvac-notes.md#1Códigos de saída
1— argumento(s) faltando. A linha de uso é impressa.- Não-zero em qualquer erro lançado por
LocalLensApp. O Bun propaga o throw por default.
AppErrors vindos do workflow surgem como stack traces normais
aqui. Sem resposta HTTP para formatar, então o campo de status fica sem uso.
Quando adicionar flags
A CLI é só-argv de propósito. Se você quer flags nomeadas
(--top-k, --model, --keep), mantenha elas neste arquivo. Não
empurra para dentro do LocalLensApp. O workflow fica o mesmo; a
CLI só aprende mais jeitos de invocar.
A seguir: o servidor Bun, a contraparte mais duradoura.