@beel_es/cli 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +11 -0
  2. package/dist/index.js +136 -1
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -40,6 +40,17 @@ npx @beel_es/cli --live invoices list # production
40
40
 
41
41
  Discover everything with `--help` at any level: `beel --help`, `beel invoices --help`, `beel invoices list --help` (flags, enums and defaults come from the API spec).
42
42
 
43
+ ### Docs search (no API key needed)
44
+
45
+ Search [docs.beel.es](https://docs.beel.es) locally — fetches `llms-full.txt` once (cached 15 min) and prints only the matching sections. An agent gets the relevant ~2KB instead of the full 660KB docs:
46
+
47
+ ```bash
48
+ npx @beel_es/cli docs list # all pages (JSON)
49
+ npx @beel_es/cli docs search idempotency key # top matching sections (markdown)
50
+ npx @beel_es/cli docs search rate limit --limit 5
51
+ npx @beel_es/cli docs get glossary # one full page
52
+ ```
53
+
43
54
  ### Generic escape hatch
44
55
 
45
56
  Any endpoint, even ones this CLI version doesn't know yet:
package/dist/index.js CHANGED
@@ -10797,7 +10797,7 @@ var require_package = __commonJS({
10797
10797
  "package.json"(exports, module) {
10798
10798
  module.exports = {
10799
10799
  name: "@beel_es/cli",
10800
- version: "0.1.0",
10800
+ version: "0.1.1",
10801
10801
  description: "Agent-first CLI for the BeeL invoicing API. Commands are derived at startup from the embedded OpenAPI spec. Run with npx @beel_es/cli \u2014 no install needed.",
10802
10802
  license: "MIT",
10803
10803
  type: "module",
@@ -11372,6 +11372,140 @@ function registerRequest(program3) {
11372
11372
  });
11373
11373
  }
11374
11374
 
11375
+ // src/docs/fetch.ts
11376
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync4, statSync, mkdirSync as mkdirSync2, existsSync as existsSync2 } from "fs";
11377
+ import { tmpdir } from "os";
11378
+ import { join as join2 } from "path";
11379
+ var CACHE_TTL_MS = 15 * 60 * 1e3;
11380
+ function docsBaseUrl(env = process.env) {
11381
+ return env.BEEL_DOCS_URL ?? "https://docs.beel.es";
11382
+ }
11383
+ async function fetchDocsFile(file, env = process.env) {
11384
+ const cacheDir = join2(tmpdir(), "beel-cli-docs-cache");
11385
+ const cachePath = join2(cacheDir, file);
11386
+ if (existsSync2(cachePath) && Date.now() - statSync(cachePath).mtimeMs < CACHE_TTL_MS) {
11387
+ return readFileSync3(cachePath, "utf8");
11388
+ }
11389
+ const url = `${docsBaseUrl(env)}/${file}`;
11390
+ const response = await fetch(url);
11391
+ if (!response.ok) {
11392
+ throw new UsageError(`Cannot fetch docs from ${url} (HTTP ${response.status})`);
11393
+ }
11394
+ const text = await response.text();
11395
+ mkdirSync2(cacheDir, { recursive: true });
11396
+ writeFileSync4(cachePath, text);
11397
+ return text;
11398
+ }
11399
+
11400
+ // src/docs/parse.ts
11401
+ function splitChunks(fullText) {
11402
+ const chunks = [];
11403
+ let page = "";
11404
+ let heading = null;
11405
+ let buffer = [];
11406
+ let inFence = false;
11407
+ const flush = () => {
11408
+ const content = buffer.join("\n").trim();
11409
+ if (page && content) chunks.push({ page, heading, content });
11410
+ buffer = [];
11411
+ };
11412
+ for (const line of fullText.split("\n")) {
11413
+ if (line.trimStart().startsWith("```")) {
11414
+ inFence = !inFence;
11415
+ buffer.push(line);
11416
+ continue;
11417
+ }
11418
+ if (!inFence && line.startsWith("# ")) {
11419
+ flush();
11420
+ page = line.slice(2).trim();
11421
+ heading = null;
11422
+ continue;
11423
+ }
11424
+ if (!inFence && line.startsWith("## ")) {
11425
+ flush();
11426
+ heading = line.slice(3).trim();
11427
+ continue;
11428
+ }
11429
+ buffer.push(line);
11430
+ }
11431
+ flush();
11432
+ return chunks;
11433
+ }
11434
+ function parseIndex(indexText) {
11435
+ const pages = [];
11436
+ for (const line of indexText.split("\n")) {
11437
+ const match = line.match(/^-\s*\[([^\]]+)\]\((\S+)\)\s*-?\s*(.*)$/);
11438
+ if (match) {
11439
+ pages.push({ title: match[1], url: match[2], description: match[3].trim() });
11440
+ }
11441
+ }
11442
+ return pages;
11443
+ }
11444
+ function scoreChunk(chunk, terms) {
11445
+ const haystack = chunk.content.toLowerCase();
11446
+ const titleHaystack = `${chunk.page} ${chunk.heading ?? ""}`.toLowerCase();
11447
+ let score = 0;
11448
+ let termsHit = 0;
11449
+ for (const term of terms) {
11450
+ const t = term.toLowerCase();
11451
+ const contentHits = haystack.split(t).length - 1;
11452
+ const titleHits = titleHaystack.split(t).length - 1;
11453
+ if (contentHits + titleHits > 0) termsHit++;
11454
+ score += contentHits + titleHits * 5;
11455
+ }
11456
+ if (termsHit === terms.length && terms.length > 1) score *= 2;
11457
+ return termsHit === 0 ? 0 : score;
11458
+ }
11459
+ function searchChunks(chunks, terms, limit) {
11460
+ return chunks.map((chunk) => ({ chunk, score: scoreChunk(chunk, terms) })).filter((s) => s.score > 0).sort((a, b) => b.score - a.score).slice(0, limit).map((s) => s.chunk);
11461
+ }
11462
+ function findPage(chunks, titleQuery) {
11463
+ const q = titleQuery.toLowerCase();
11464
+ const titles = [...new Set(chunks.map((c) => c.page))];
11465
+ const exact = titles.find((t) => t.toLowerCase() === q);
11466
+ const partial = titles.filter((t) => t.toLowerCase().includes(q));
11467
+ const title = exact ?? (partial.length === 1 ? partial[0] : void 0);
11468
+ if (!title) return [];
11469
+ return chunks.filter((c) => c.page === title);
11470
+ }
11471
+
11472
+ // src/commands/docs.ts
11473
+ function registerDocs(program3) {
11474
+ const docs = program3.command("docs").description(
11475
+ "Search the BeeL API docs. Downloads docs.beel.es/llms-full.txt once (cached 15 min in tmpdir), then filters locally \u2014 search terms never leave your machine. No API key needed."
11476
+ );
11477
+ docs.command("list").description("List all documentation pages").action(async () => {
11478
+ const index = await fetchDocsFile("llms.txt");
11479
+ printJson(parseIndex(index));
11480
+ });
11481
+ docs.command("search").description("Search the docs; prints only the matching sections as markdown").argument("<terms...>", "Search terms").option("--limit <n>", "Max sections to print", Number, 3).action(async (terms, opts) => {
11482
+ const full = await fetchDocsFile("llms-full.txt");
11483
+ const results = searchChunks(splitChunks(full), terms, opts.limit);
11484
+ if (results.length === 0) {
11485
+ throw new UsageError(`No docs sections match: ${terms.join(" ")}. Try 'beel docs list' to see available pages.`);
11486
+ }
11487
+ printChunks(results);
11488
+ });
11489
+ docs.command("get").description("Print a full documentation page as markdown").argument("<title...>", 'Page title (or unambiguous part of it), e.g. "idempotency"').action(async (titleParts) => {
11490
+ const full = await fetchDocsFile("llms-full.txt");
11491
+ const chunks = findPage(splitChunks(full), titleParts.join(" "));
11492
+ if (chunks.length === 0) {
11493
+ throw new UsageError(`No page matches '${titleParts.join(" ")}' unambiguously. Run 'beel docs list' to see titles.`);
11494
+ }
11495
+ printChunks(chunks);
11496
+ });
11497
+ }
11498
+ function printChunks(chunks) {
11499
+ for (const chunk of chunks) {
11500
+ const location = chunk.heading ? `${chunk.page} \u203A ${chunk.heading}` : chunk.page;
11501
+ process.stdout.write(`
11502
+ \u2501\u2501\u2501 ${location} \u2501\u2501\u2501
11503
+
11504
+ ${chunk.content}
11505
+ `);
11506
+ }
11507
+ }
11508
+
11375
11509
  // src/index.ts
11376
11510
  var { version } = require_package();
11377
11511
  var program2 = new Command("beel").description(
@@ -11380,6 +11514,7 @@ var program2 = new Command("beel").description(
11380
11514
  registerLogin(program2);
11381
11515
  registerConfig(program2);
11382
11516
  registerRequest(program2);
11517
+ registerDocs(program2);
11383
11518
  try {
11384
11519
  registerSpecCommands(program2, (0, import_yaml.parse)(public_api_default));
11385
11520
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@beel_es/cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Agent-first CLI for the BeeL invoicing API. Commands are derived at startup from the embedded OpenAPI spec. Run with npx @beel_es/cli — no install needed.",
5
5
  "license": "MIT",
6
6
  "type": "module",