LocalLens
Walkthrough

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

src/cli.ts
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:

  1. Sem drift no JSON store. Rodar a CLI cem vezes não deixa cem entradas em .locallens/store.json.
  2. 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#1

Có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.

On this page