npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

checklist-qa

v0.8.2

Published

Conversational QA checklist interview engine, independent of any LLM provider.

Readme

checklist-qa

Transforma uma entrevista de QA (conduzida por qualquer LLM) numa experiência conversacional interativa, e entrega ao final um arquivo checklist.md pronto para uso — junto com o documento técnico e o changelog, tudo reunido numa única pasta anexos/.

O pacote não sabe nada sobre QA — todas as perguntas, regras de negócio e formato do checklist vivem inteiramente no prompt de entrevista (prompts/checklist-interview.md). O pacote apenas orquestra a conversa: envia o histórico ao seu provider de IA, guarda as respostas, detecta o fim da entrevista e disponibiliza o markdown resultante.

  • Independente de modelo de IA: providers embutidos para OpenAI, Anthropic, Gemini, Azure OpenAI e Ollama; qualquer outro (DeepSeek, etc.) funciona implementando a mesma interface (LLMProvider).
  • TypeScript com tipagem completa, ESM + CommonJS, Node 20+.
  • Zero perguntas hardcoded no código — tudo vem do prompt.

Instalação

npm install checklist-qa

CLI

O pacote inclui um CLI mínimo que roda a entrevista direto no terminal, usando o prompt embutido no pacote — útil para testar sem escrever código:

npx checklist-qa --provider anthropic

| Flag | Descrição | | --- | --- | | --config <arquivo> | Arquivo de configuração JSON com valores padrão para as demais opções (ver Arquivo de configuração). | | --provider <nome> | openai, anthropic, gemini, azure-openai, ollama, ou os apelidos local (= ollama), azure (= azure-openai) e nuvem (= provider de nuvem detectado automaticamente: anthropic > openai > gemini > azure-openai). Se omitido, usa a env var PROVIDER; se ela também não existir, é detectado a partir de ANTHROPIC_API_KEY > OPENAI_API_KEY_CHECKLIST > GEMINI_API_KEY > AZURE_OPENAI_API_KEY > Ollama local. | | --model <nome> | Nome do modelo (padrão depende do provider). Para azure-openai, é o nome do deployment (ou use AZURE_OPENAI_DEPLOYMENT). | | --prompt <arquivo> | Prompt de entrevista customizado (padrão: prompts/checklist-interview.md do pacote). | | --output-dir <dir> | Diretório onde a pasta anexos/ (com o(s) checklist(s), doc.md e changelog.md) será criada (padrão: diretório atual). | | --context-dir <dir> | Diretório onde procurar a pasta .diffai/ para usar como contexto adicional da entrevista (padrão: diretório atual). | | --no-extension-guess | Desliga a heurística de classificar arquivos como Backend/Frontend pela extensão (.js = Backend, .ts = Frontend) quando não há indicação explícita no contexto. Use em projetos onde essa convenção não vale (ex.: backend em TypeScript). Ativa por padrão quando a flag é omitida. | | --feature-doc-prompt <arquivo> | Prompt customizado para o documento técnico gerado em anexos/doc.md (padrão: prompts/feature-doc.md do pacote). | | --no-feature-doc | Desativa a geração do documento técnico em anexos/doc.md. | | --changelog-prompt <arquivo> | Prompt customizado para a entrada de changelog gerada em anexos/changelog.md (padrão: prompts/changelog.md do pacote). | | --no-changelog | Desativa a geração da entrada de changelog em anexos/changelog.md. | | --no-summary | Desativa a geração do resumo agregado anexos/00-resumo.md (ver Resumo agregado). | | --no-resume | Descarta uma sessão salva anteriormente (.checklist-qa-session.json) e começa uma entrevista nova (ver Retomando uma entrevista interrompida). | | --no-attach-reminder | Desliga o aviso final lembrando de anexar os arquivos gerados ao PR / Card. | | -h, --help | Mostra a ajuda. |

Antes de iniciar a entrevista, o CLI consulta o npm para checar se a versão instalada é a mais recente publicada de checklist-qa e bloqueia a execução (com instruções de atualização) se não for. Se a consulta ao registro falhar (ex.: sem internet), o CLI apenas avisa e segue com a versão instalada, em vez de travar.

Contexto adicional (.diffai/)

Se o diretório de contexto (padrão: onde o CLI é executado, ou o valor de --context-dir) contiver uma pasta .diffai/, o CLI lê todos os arquivos dessa pasta recursivamente e anexa o conteúdo ao prompt de entrevista antes de iniciar a conversa. A IA analisa esse contexto antes de perguntar qualquer coisa e gera as perguntas dinamicamente a partir dele. docs/ (ex.: docs/analysis.md) não é lida: é apenas uma renderização em prosa do mesmo conteúdo de .diffai/analysis.json, então incluí-la também só duplicaria informação no prompt sem agregar nada novo.

Cada pergunta é apresentada já com uma resposta sugerida, pré-preenchida a partir do contexto e editável: no CLI, a sugestão abre no seu editor de texto ($VISUAL/$EDITOR, ou Notepad no Windows / nano nos demais), onde o desenvolvedor edita à vontade em várias linhas, salva e fecha para enviar. Quando o contexto não permite sugerir nada com segurança, o editor abre em branco. Se o contexto identificar mais de uma funcionalidade, elas são enumeradas (Funcionalidade 1, 2, ...) e tratadas separadamente na entrevista e no checklist. Ao final, o checklist.md combina o contexto (.diffai/) com as respostas confirmadas, destacando as mudanças importantes no sistema e os pontos de atenção.

Em entradas não interativas (stdin via pipe), o CLI faz fallback para uma leitura de linha única, sem abrir editor.

Se a pasta não existir, o comportamento é idêntico ao anterior: a entrevista é conduzida sem contexto extra, e nenhuma resposta é pré-preenchida.

Para usar essa mesma lógica fora do CLI, a classe ContextLoader está disponível publicamente:

import { ContextLoader } from "checklist-qa";

const context = await ContextLoader.loadFromDirectories(["./.diffai"]);

Classificação Backend/Frontend gerada por código

Antes, a IA precisava interpretar sozinha os arquivos .diffai/analysis.*.json para decidir quais testes eram de backend e quais de frontend — e frequentemente errava, misturando os dois lados. Agora o CLI faz essa separação por código, de forma determinística, com a classe DiffaiClassifier: ele lê os arquivos analysis.backend-*.json e analysis.frontend-*.json, usa o lado declarado no nome de cada análise como autoridade (conferindo com o caminho de cada arquivo) e anexa ao prompt um bloco "Classificação Backend/Frontend determinada por código (AUTORITATIVA)" com as listas prontas de testes e arquivos de cada lado. A IA é instruída a copiar essa classificação exatamente como está — em vez de reinferi-la — e a não inventar arquivos que não constem do contexto. Nomes de teste "nus" (sem caminho), como transactionModel.test.js ou new-sale.component.spec.ts, também são classificados corretamente porque o lado vem do nome da análise de origem.

A flag --no-extension-guess também vale aqui: com ela, arquivos que não tenham lado determinável pelo nome da análise nem pelo caminho ficam marcados como indeterminado em vez de serem chutados pela extensão. Para usar a classificação fora do CLI:

import { DiffaiClassifier } from "checklist-qa";

const classification = await DiffaiClassifier.classifyDirectory("./.diffai");
const block = DiffaiClassifier.buildContextBlock(classification);

Variáveis de ambiente usadas pelos providers embutidos:

  • PROVIDER — seleciona o provider sem passar --provider: local (= ollama) ou nuvem (= provider de nuvem detectado automaticamente: anthropic > openai > gemini > azure-openai). Também aceita anthropic | openai | gemini | azure-openai | ollama. A flag --provider tem prioridade sobre esta variável.
  • ANTHROPIC_API_KEY — necessária para o provider de nuvem anthropic.
  • OPENAI_API_KEY_CHECKLIST — necessária para o provider de nuvem openai.
  • GEMINI_API_KEY — necessária para o provider de nuvem gemini.
  • AZURE_OPENAI_API_KEY / AZURE_OPENAI_ENDPOINT — necessárias para o provider de nuvem azure-openai (AZURE_OPENAI_ENDPOINT no formato https://SEU-RECURSO.openai.azure.com).
  • AZURE_OPENAI_DEPLOYMENT — nome do deployment no Azure, usado quando --model/model não é passado.
  • AZURE_OPENAI_API_VERSION — opcional para o provider de nuvem azure-openai (padrão 2024-06-01).
  • BASE_URL — opcional para o provider local (ollama): URL base do servidor. Tem prioridade sobre OLLAMA_BASE_URL.
  • OLLAMA_BASE_URL — opcional para o provider local (ollama), padrão http://localhost:11434.

São os mesmos providers mínimos descritos em examples/providers — o CLI é só um ponto de entrada de terminal para o mesmo ChecklistQA. Use VISUAL ou EDITOR para escolher o editor em que as respostas são editadas (padrão: Notepad no Windows, nano nos demais).

O CLI carrega automaticamente um arquivo .env do diretório onde for executado (não do diretório do pacote), sem sobrescrever variáveis já exportadas no shell.

Arquivo de configuração (checklist-qa.config.json)

Para não repetir as mesmas flags a cada execução, o CLI aceita um arquivo checklist-qa.config.json no diretório atual (opcional — a ausência dele não é erro) com valores padrão para qualquer flag documentada acima:

{
  "provider": "anthropic",
  "contextDir": ".",
  "noChangelog": true,
  "noSummary": false
}

Cada chave espelha exatamente uma flag (provider, model, prompt, outputDir, contextDir, noExtensionGuess, featureDocPrompt, noFeatureDoc, changelogPrompt, noChangelog, noSummary, noResume, noAttachReminder) — todas opcionais. Precedência: flag passada explicitamente na linha de comando > arquivo de configuração > padrão embutido.

Use --config <arquivo> para apontar para um arquivo com outro nome/local; nesse caso, o arquivo precisa existir (um --config explícito para um caminho inexistente é erro). Já o arquivo padrão (checklist-qa.config.json no diretório atual) é totalmente opcional — só falha alto se existir e estiver malformado (JSON inválido, não for um objeto, tiver uma chave não reconhecida ou uma chave com o tipo errado): um arquivo de configuração quebrado nunca é ignorado silenciosamente. Só JSON é aceito (sem .js/.ts de configuração), para não introduzir execução de código arbitrário.

Assim como as classes de providers inline do CLI, ConfigLoader é um detalhe de implementação do próprio CLI (src/ConfigLoader.ts) — não faz parte da API pública da biblioteca (ChecklistQA, que é consumida programaticamente, não lê arquivo de configuração nenhum).

Validação em CI (checklist-qa check)

npx checklist-qa check

Diferente do comando padrão, check não chama nenhuma IA — ele só valida que um checklist já foi gerado (localmente, antes de abrir a PR) e tem a estrutura que o prompt garante, e falha o build quando não estiver:

  • Existe a pasta anexos/ com ao menos um arquivo .md de funcionalidade (o 00-resumo.md agregado e os arquivos doc.md/changelog.md, se existirem, não entram nessa validação — nenhum deles tem "Risco Geral" próprio).
  • Cada arquivo de funcionalidade tem a linha **Risco Geral: <Baixo|Médio|Alto>**, a seção ## Checklist de QA, a seção ## Resumo Final e a seção ### 7. Testes Identificados no Contexto com as subseções #### Backend e #### Frontend.

| Flag | Descrição | | --- | --- | | --output-dir <dir> | Diretório onde procurar a pasta anexos/ (padrão: diretório atual). | | -h, --help | Mostra a ajuda do subcomando. |

Termina com código de saída 0 (tudo OK) ou 1 (faltou o checklist ou ele está incompleto) — pensado para um step de CI (ex.: Bitbucket Pipelines) que bloqueia o merge:

- step:
    name: Verifica checklist de QA
    script:
      - npx checklist-qa check

Para usar a mesma validação de fora do CLI, a classe ChecklistValidator está disponível publicamente:

import { ChecklistValidator } from "checklist-qa";

const report = await ChecklistValidator.checkDirectory("./");

Uso rápido

import { ChecklistQA, PromptLoader } from "checklist-qa";
import { myProvider } from "./my-provider.js";

const prompt = await PromptLoader.loadFromFile("./node_modules/checklist-qa/prompts/checklist-interview.md");

const qa = new ChecklistQA({
  provider: myProvider,
  prompt,
});

let question = await qa.start();

while (!qa.isFinished()) {
  console.log(question);
  // Resposta sugerida a partir do contexto (.diffai): apresente-a já preenchida e editável.
  const answer = await getAnswerFromUser(qa.suggestedAnswer()); // sua UI, CLI, etc.
  question = await qa.answer(answer);
}

console.log(qa.getMarkdown()); // conteúdo final do checklist.md
await qa.download(); // grava anexos/checklist.md em disco (Node) ou dispara o download (browser)

Você também pode copiar prompts/checklist-interview.md para o seu próprio projeto e ajustá-lo, desde que mantenha o marcador <QA_CHECKLIST_READY> (veja Como funciona a entrevista).

Configuração

interface ChecklistQAOptions {
  provider: LLMProvider;       // obrigatório — seu adaptador de IA
  prompt: string;              // obrigatório — texto do prompt de entrevista
  outputFileName?: string;     // padrão: "checklist.md"
  outputDirName?: string;      // padrão: "anexos" — pasta única que reúne checklist(s), doc.md e changelog.md
  autoDownload?: boolean;      // padrão: false — baixa/gera o arquivo automaticamente ao concluir
  marker?: string;             // padrão: "<QA_CHECKLIST_READY>"
  suggestionMarker?: string;   // padrão: "<RESPOSTA_SUGERIDA>" — separa a pergunta da resposta pré-preenchida
  progressMarker?: string;     // padrão: "<PROGRESSO>" — precede a estimativa "pergunta atual/total"
  sessionId?: string;          // padrão: gerado automaticamente (crypto.randomUUID)
}

API

| Método | Descrição | | --- | --- | | qa.start() | Envia o prompt ao provider e retorna a primeira pergunta. | | qa.answer(texto) | Envia a resposta do usuário e retorna a próxima pergunta ("" quando a entrevista termina). | | qa.question() | Última pergunta feita pela IA. | | qa.suggestedAnswer() | Resposta sugerida (pré-preenchida a partir do contexto) para a última pergunta ("" quando não há). | | qa.progress() | { current, total } autorreportado pela IA para a última pergunta (undefined quando não informado). total é uma estimativa que pode mudar entre perguntas. | | qa.isFinished() | true quando o marcador de conclusão foi detectado. | | qa.getMarkdown() | Conteúdo do checklist.md (undefined até a entrevista terminar). | | qa.getHistory() | Histórico completo da conversa (nunca perde mensagens). | | qa.download(outputDir?) | Grava o arquivo em disco (Node) ou dispara o download (browser). |

Criando um Provider

A única coisa que o pacote exige de um provider é:

interface LLMProvider {
  chat(messages: Message[]): Promise<Message>;
}

type Role = "system" | "user" | "assistant";
interface Message {
  role: Role;
  content: string;
}

Toda vez que uma pergunta precisa ser feita, o InterviewEngine chama provider.chat(historicoCompleto) e espera de volta uma única mensagem assistant. Você decide como essa chamada conversa com a IA por trás.

Exemplos completos ficam em examples/providers: OpenAI, Anthropic, Gemini, Azure OpenAI e Ollama. Um provider mínimo para qualquer serviço com API compatível:

import type { LLMProvider, Message } from "checklist-qa";

export class MyProvider implements LLMProvider {
  async chat(messages: Message[]): Promise<Message> {
    const content = await callMyLLM(messages);
    return { role: "assistant", content };
  }
}

Como funciona a entrevista

  1. qa.start() envia o prompt de entrevista (mensagem system) para o LLMProvider e recebe a primeira pergunta.

  2. A cada qa.answer(resposta), a resposta do usuário é adicionada ao histórico e o histórico completo é reenviado ao provider — o pacote nunca trunca ou reescreve mensagens anteriores.

  3. O pacote nunca mostra mais de uma pergunta por vez — isso é responsabilidade do prompt, que já instrui a IA a perguntar um item por mensagem.

  4. Cada pergunta pode vir acompanhada de uma resposta sugerida (derivada do contexto), separada da pergunta pelo marcador <RESPOSTA_SUGERIDA>. O InterviewEngine separa a pergunta (antes do marcador) da sugestão (depois dele) e expõe a sugestão via qa.suggestedAnswer() — apresente-a já preenchida e editável na sua UI. Se a pergunta não tiver marcador, qa.suggestedAnswer() retorna "". 4b. Cada pergunta também pode vir com uma estimativa de progresso, no formato <PROGRESSO>\npergunta_atual/total_estimado. Como a entrevista é dinâmica (sem lista fixa de perguntas), o total é a própria IA quem estima e pode revisar a cada pergunta. O InterviewEngine expõe isso via qa.progress() (undefined quando a pergunta não trouxe a estimativa).

  5. Quando a IA decide que a entrevista terminou, ela responde exatamente com o marcador:

    <QA_CHECKLIST_READY>

    seguido, na mesma mensagem, do conteúdo completo do checklist.md em Markdown.

  6. O InterviewEngine detecta o marcador, separa qualquer texto antes dele (última "pergunta", normalmente vazia) do conteúdo após ele (o Markdown), marca a sessão como finalizada e armazena o Markdown.

  7. A partir daí, qa.isFinished() retorna true e qa.getMarkdown() / qa.download() ficam disponíveis.

Use as opções marker, suggestionMarker e progressMarker em ChecklistQAOptions caso seu prompt use marcadores diferentes.

Retomando uma entrevista interrompida

No CLI, o progresso é salvo automaticamente a cada resposta em .checklist-qa-session.json (no diretório atual) e retomado sozinho na próxima execução, caso o processo seja interrompido (fechar o terminal, crash, Ctrl+C) no meio de uma entrevista longa — sem precisar de nenhuma flag. Ao terminar a entrevista com sucesso, esse arquivo é apagado, então uma execução concluída nunca é confundida com uma sessão retomável.

Se a versão do checklist-qa ou o prompt de entrevista mudou desde que a sessão foi salva, o CLI avisa (nunca recusa nem apaga silenciosamente) e continua retomando mesmo assim — um bloqueio deixaria você sem saída a não ser apagar o arquivo manualmente. Use --no-resume para descartar uma sessão salva e começar do zero quando isso acontecer, ou sempre que preferir recomeçar.

Só é possível ter uma entrevista em andamento por diretório (o nome do arquivo é fixo). Se você usa controle de versão, adicione .checklist-qa-session.json ao seu .gitignore — é um arquivo de rascunho local, não um artefato do checklist.

Para implementar a mesma persistência na sua própria UI (fora do CLI), use qa.serialize() / ChecklistQA.restore():

import { ChecklistQA } from "checklist-qa";

// Salvar (a qualquer momento, tipicamente após start()/answer()):
const snapshot = qa.serialize();
await salvarEmAlgumLugar(JSON.stringify(snapshot));

// Retomar depois, com um provider (e demais opções) fornecidos de novo:
const snapshot2 = JSON.parse(await lerDeAlgumLugar());
const qa2 = ChecklistQA.restore(snapshot2, { provider, prompt });

options.provider/prompt/featureDocPrompt/changelogPrompt nunca fazem parte do snapshot (não são serializáveis) — forneça-os de novo em restore(). options.sessionId é ignorado: o id real vem do próprio snapshot.

Geração e download do Markdown

Todo arquivo gerado — checklist(s), resumo agregado, documento técnico e changelog — é sempre gravado dentro de uma única pasta compartilhada, outputDirName (padrão anexos/), nunca solto na raiz de outputDir.

  • qa.getMarkdown() retorna a string do Markdown assim que a entrevista termina.
  • qa.download(outputDir?):
    • Uma única funcionalidade: grava checklist.md (ou o outputFileName configurado) dentro de anexos/ via fs/promises (Node), opcionalmente dentro de outputDir, e retorna o caminho gravado. No browser, cria um Blob e dispara o download (sem depender de nenhum framework), com o nome da pasta prefixado ao arquivo (anexos-checklist.md).
    • Mais de uma funcionalidade: grava um arquivo por funcionalidade (1-<slug>.md, 2-<slug>.md, ...) dentro dessa mesma pasta anexos/ (ou o outputDirName configurado), e retorna a lista de caminhos gravados. No browser, cada arquivo é baixado separadamente com o nome da pasta prefixado (anexos-1-<slug>.md).
  • qa.downloadFeatureDocs(outputDir?) e qa.downloadChangelogs(outputDir?) gravam, respectivamente, doc.md e changelog.md dentro dessa mesma pasta anexos/ — cada um sobrescreve apenas o seu próprio arquivo fixo, sem afetar o(s) checklist(s) nem o outro documento já gravados ali.
  • Configure autoDownload: true para que o download aconteça automaticamente assim que qa.isFinished() se tornar true, sem precisar chamar qa.download() manualmente.

Resumo agregado (multi-funcionalidade)

Quando a entrevista cobre mais de uma funcionalidade, download() também grava anexos/00-resumo.md — um arquivo puramente derivado por código (sem chamada extra à IA) que consolida, numa tabela, o **Risco Geral: <Baixo|Médio|Alto>** de cada funcionalidade (linha que o prompt sempre gera, logo abaixo do título) e calcula um risco geral do PR (o mais alto entre as funcionalidades). O nome 00- garante que esse arquivo sempre apareça primeiro na pasta, antes dos arquivos 1-..., 2-... de cada funcionalidade. Quando a entrevista cobre só uma funcionalidade, não há nada para agregar e esse arquivo não é gerado.

Se alguma funcionalidade não tiver uma linha de risco reconhecível (formato inesperado), ela aparece na tabela como "Risco não determinado" em vez de ser omitida ou de herdar um risco arbitrário — e fica de fora do cálculo do risco geral do PR.

Use noSummary: true em ChecklistQAOptions (ou --no-summary no CLI) para desativar essa geração.

Para usar a mesma lógica de fora do ChecklistQA, a classe ChecklistSummary está disponível publicamente:

import { ChecklistSummary } from "checklist-qa";

const { risk, justification } = ChecklistSummary.parseRisk(markdown);
const overall = ChecklistSummary.aggregateRisk([risk1, risk2]);

FAQ

Preciso reescrever as perguntas da entrevista no código? Não. Todas as perguntas, regras e o formato do checklist final vivem em prompts/checklist-interview.md. O pacote só orquestra o fluxo.

Posso usar qualquer LLM? Sim. Implemente LLMProvider (um único método, chat) para o serviço que preferir. O pacote nunca importa um SDK de IA específico.

O que acontece se a IA esquecer o marcador <QA_CHECKLIST_READY>? A entrevista nunca é marcada como concluída e qa.getMarkdown()/qa.download() continuam indisponíveis — ajuste o prompt ou o marker configurado se isso acontecer com frequência.

Funciona em CommonJS? Sim, o pacote publica builds ESM e CommonJS com tipos para ambos.

Como troco de provider no meio do desenvolvimento? Basta passar uma instância diferente em provider ao criar o ChecklistQA — nada mais no pacote precisa mudar.