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

@shopeasy/knowledge

v0.1.0

Published

Gerencia a base de conhecimento (Markdown) do ShopEasy e monta o melhor contexto textual possível para enviar a uma IA sem RAG/vetores.

Readme

@shopeasy/knowledge

Biblioteca TypeScript responsável por gerenciar a base de conhecimento (Markdown) do ShopEasy e montar automaticamente o melhor contexto textual possível para enviar a uma IA que não possui RAG nem banco vetorial — o modelo só recebe texto puro.

A biblioteca organiza os arquivos .md, indexa tudo com busca textual (MiniSearch) e monta um contexto enxuto, relevante e dentro de um limite de caracteres.

Por que não RAG / embeddings?

O bot do ShopEasy usa um modelo que recebe apenas um bloco de texto como contexto. Não há banco vetorial nem busca semântica por embeddings — a relevância é calculada por busca textual (TF-IDF/BM25 via MiniSearch) sobre título, headings, keywords, nome do arquivo e conteúdo. Isso mantém a solução simples, previsível, sem custo de infraestrutura extra e fácil de depurar.

Instalação

npm install @shopeasy/knowledge

Estrutura esperada dos documentos

knowledge/
├── about.md
├── ai-guidelines.md   # pinned: true — instruções de comportamento, sempre incluídas
├── payments.md
├── plans.md
├── features.md
├── technology.md
└── updates.md

Cada arquivo é um único assunto, com frontmatter opcional:

---
title: Pagamentos
category: Payments
keywords: [pix, cartao, cripto]
---

# Pagamentos

O ShopEasy aceita PIX, cartão e criptomoedas.
  • title — opcional. Se ausente, usa o primeiro heading; se não houver heading, deriva do nome do arquivo.
  • id — opcional. Se ausente, o id é o caminho do arquivo relativo à pasta knowledge/, sem extensão (payments.md → payments, products/games.md → products/games).
  • keywords — opcional. Se ausente, é derivado automaticamente do título e dos headings.
  • pinned — opcional (boolean, padrão false). Documentos com pinned: true são sempre incluídos por buildContext/buildPrompt, independente da busca — ideal para instruções de comportamento da IA (veja knowledge/ai-guidelines.md).
  • Qualquer outro campo do frontmatter fica disponível em document.metadata.

Uso básico

import { Knowledge } from "@shopeasy/knowledge";

const knowledge = new Knowledge({ directory: "./knowledge" });
await knowledge.initialize();

knowledge.search("pix");
// -> [{ document: {...}, score: 4.2, terms: ["pix"] }, ...]

knowledge.getDocument("payments");
// -> KnowledgeDocument | undefined

knowledge.listDocuments();
// -> KnowledgeDocument[]

const { text, documents, characters, tokens, truncated } = knowledge.buildContext({
  query: "Como configurar pagamentos?",
  maxTokens: 3000, // ou maxCharacters: 12000
});

// `text` é o bloco pronto para ser enviado como contexto ao modelo.

const { system } = knowledge.buildPrompt({ query: "Como configurar pagamentos?", maxTokens: 3000 });
// `system` já vem com um preâmbulo de instrução + o contexto acima — pronto para
// `client.messages.create({ system, ... })` (Anthropic) ou uma system message (OpenAI).

await knowledge.close(); // encerra o watcher, se houver

API

new Knowledge(options: KnowledgeOptions)

| Opção | Tipo | Padrão | Descrição | | ----------- | ---------------- | ----------- | -------------------------------------------------------------------- | | directory | string | — | Pasta com os arquivos .md/.mdx. | | watch | boolean | true | Recarrega automaticamente (hot reload) quando um arquivo muda. | | encoding | BufferEncoding | "utf-8" | Encoding usado para ler os arquivos. | | tokenizer | (text: string) => number | estimativa interna | Contador de tokens usado por maxTokens e pelo campo tokens. Passe um tokenizer real (tiktoken, @anthropic-ai/tokenizer, gpt-tokenizer...) para contagem exata; por padrão usa uma estimativa sem dependências (estimateTokens, exportado pela lib). |

await knowledge.initialize(): Promise<void>

Carrega todos os documentos e (se watch: true) inicia o monitoramento com chokidar. Deve ser chamado antes de qualquer outro método.

knowledge.search(query: string, options?: { limit?: number }): SearchResult[]

Busca textual considerando nome do arquivo, título, headings, keywords e conteúdo — ordenada por relevância (mais relevante primeiro). limit (padrão 10) limita a quantidade de resultados.

interface SearchResult {
  document: KnowledgeDocument;
  score: number;
  terms: string[];
}

A busca é preparada para perguntas em linguagem natural, não só palavras-chave soltas:

  • Acentos são ignorados dos dois lados (indexação e consulta) — "nao" encontra "não", "comissao" encontra "comissão".
  • Stopwords em PT-BR/EN são descartadas ("o", "a", "como", "para", "com", "the", "is"...), então uma pergunta como "Como funciona o pagamento com pix?" busca efetivamente por funciona, pagamento, pix — sem o ruído das palavras funcionais diluir o ranking.
  • Prefixo e fuzzy matching estão habilitados (pagam casa com pagamento; pequenos erros de digitação ainda encontram o termo certo).

knowledge.getDocument(id: string): KnowledgeDocument | undefined

Retorna um documento pelo id (frontmatter id, ou o caminho do arquivo sem extensão).

knowledge.listDocuments(): KnowledgeDocument[]

Retorna todos os documentos carregados.

knowledge.buildContext(options: BuildContextOptions): BuiltContext

interface BuildContextOptions {
  query: string;
  maxCharacters?: number;  // pelo menos um dos dois é obrigatório
  maxTokens?: number;      // preferido: modelos de IA têm limite de contexto em tokens, não em caracteres
  maxDocuments?: number;   // opcional, sem limite por padrão
}

interface BuiltContext {
  text: string;                    // texto final pronto para a IA
  documents: KnowledgeDocument[];  // documentos efetivamente incluídos, na ordem em que aparecem
  characters: number;              // tamanho de `text` em caracteres
  tokens: number;                  // tamanho de `text` estimado (ou exato, com tokenizer customizado) em tokens
  truncated: boolean;              // true se algum documento foi cortado ou deixado de fora
}

Pesquisa os documentos relevantes para query, inclui os documentos pinned primeiro (independente da busca), remove duplicados, ordena por relevância e monta um único texto respeitando maxCharacters ou maxTokens:

# Documento: Instruções para IA

...

---

# Documento: Pagamentos

...

---

# Documento: Cupons

...

---

Se o conteúdo de um documento não couber inteiro no espaço restante, ele é truncado em um limite de palavra (nunca corta uma palavra ao meio) e recebe o marcador [conteúdo truncado]. Documentos que não cabem de forma alguma são simplesmente deixados de fora — nunca excede o orçamento pedido.

// Por caracteres:
knowledge.buildContext({ query: "pix", maxCharacters: 4000 });

// Por tokens (recomendado ao montar o prompt de um modelo de IA):
knowledge.buildContext({ query: "pix", maxTokens: 1000 });

knowledge.buildPrompt(options: BuildPromptOptions): AIPrompt

Açúcar sintático sobre buildContext: devolve tudo que buildContext devolve, mais um campo system — o texto pronto para virar o system prompt de uma chamada de IA (Anthropic, OpenAI ou qualquer SDK que aceite uma string de instrução + contexto).

interface BuildPromptOptions extends BuildContextOptions {
  preamble?: string; // sobrescreve a instrução padrão do ShopEasy
}

interface AIPrompt extends BuiltContext {
  system: string; // preamble + contexto, pronto para a chamada ao modelo
}
const { system } = knowledge.buildPrompt({
  query: userMessage,
  maxTokens: 3000,
});

const response = await anthropic.messages.create({
  model: "claude-sonnet-5",
  system,
  messages: [{ role: "user", content: userMessage }],
});

Documentos com pinned: true (como knowledge/ai-guidelines.md) sempre entram no system, então instruções de comportamento não dependem de a pergunta do usuário "bater" com elas na busca.

await knowledge.close(): Promise<void>

Encerra o watcher (se estiver ativo). Seguro de chamar mesmo com watch: false.

Hot reload

Com watch: true (padrão), a biblioteca usa chokidar para monitorar a pasta knowledge/:

  • arquivo alterado → apenas aquele documento é reprocessado (frontmatter, headings, keywords, conteúdo) e reindexado;
  • arquivo criado → é carregado e indexado imediatamente;
  • arquivo removido → é removido do índice e do cache.

Nada disso reinicia a aplicação nem recarrega os outros documentos.

Arquitetura

Cada responsabilidade vive em seu próprio módulo (princípio da responsabilidade única):

src/
├── loader/markdown-loader.ts     # varre o diretório e lê os arquivos .md/.mdx (fs)
├── parser/document-parser.ts     # gray-matter + unified/remark-parse -> KnowledgeDocument
├── store/document-store.ts       # cache em memória (Map) por id e por arquivo
├── search/search-index.ts        # MiniSearch: normalização (acentos/stopwords) e busca por relevância
├── context/context-builder.ts    # monta o texto final respeitando maxCharacters/maxTokens + pinned
├── tokenizer.ts                  # estimateTokens (padrão) + tipo TokenCounter plugável
├── watcher/knowledge-watcher.ts  # chokidar: notifica add/change/unlink por arquivo
├── knowledge.ts                  # orquestra tudo e expõe a API pública (buildContext/buildPrompt)
└── index.ts                      # exports públicos

Knowledge (src/knowledge.ts) é a única classe que conhece todas as peças; cada peça individual (MarkdownLoader, DocumentParser, DocumentStore, SearchIndex, ContextBuilder, KnowledgeWatcher) é testável isoladamente e não depende das demais.

Escalabilidade

  • O carregamento inicial lê os arquivos em paralelo e o índice do MiniSearch foi desenhado para milhares de documentos.
  • O hot reload reprocessa apenas o arquivo alterado, não a base inteira.
  • buildContext/buildPrompt nunca materializam mais texto do que o necessário: param de percorrer os resultados assim que o orçamento (caracteres ou tokens) acaba.

Tecnologias utilizadas

Não é usado LangChain, LlamaIndex, banco vetorial ou embeddings.

Desenvolvimento

npm install
npm run build       # gera dist/ (ESM + CJS + .d.ts) via tsup
npm test            # roda a suíte de testes (Vitest)
npm run typecheck   # apenas checa tipos
npm run example     # roda examples/basic-usage.ts com tsx

Testes

A suíte cobre cada módulo isoladamente e um fluxo de integração ponta a ponta (incluindo hot reload real em um diretório temporário):

  • test/parser.test.ts — frontmatter, título/keywords/id/pinned derivados, headings.
  • test/loader.test.ts — leitura recursiva de diretórios.
  • test/search.test.ts — ranqueamento, normalização de acentos/stopwords, update()/remove().
  • test/context-builder.test.ts — formatação, deduplicação, truncamento, limite de caracteres/tokens/documentos.
  • test/tokenizer.test.ts — estimativa de tokens.
  • test/knowledge.test.ts — API pública (buildContext, buildPrompt, documentos pinned) e hot reload (add/change/unlink).