markdown-lsp 0.2.2 → 1.1.0

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.
package/README.md CHANGED
@@ -6,87 +6,238 @@
6
6
  [![license](https://img.shields.io/npm/l/markdown-lsp.svg?style=flat-square)](https://github.com/Docsbook-io/markdown-lsp/blob/main/LICENSE)
7
7
  [![node](https://img.shields.io/node/v/markdown-lsp.svg?style=flat-square)](https://www.npmjs.com/package/markdown-lsp)
8
8
 
9
- Language Server Protocol implementation for Markdown documentation. Optional AI-powered semantic layer on top.
9
+ CLI and library for querying Markdown documentation graphs. Point it at a folder of `.md` files and get instant full-text search, outline, link analysis, and symbol lookup — all as JSON.
10
10
 
11
- **Status: M1 complete, M2 (AI layer) opt-in.**
11
+ **Status: v1.1.0. CLI is the default interface. LSP stdio mode available as a subcommand.**
12
12
 
13
- ## Two layers
13
+ ---
14
14
 
15
- ### Structural (default, no AI)
15
+ ## Quick Start
16
16
 
17
- Out of the box — like Marksman, but persisted in Postgres and addressable from a service.
17
+ ```bash
18
+ # List all pages
19
+ npx markdown-lsp workspace-outline ./docs
20
+
21
+ # Full-text search (natural-language, ranked)
22
+ npx markdown-lsp search-text ./docs "getting started"
23
+
24
+ # Fuzzy heading search
25
+ npx markdown-lsp search-symbols ./docs "auth" --limit 10
26
+ ```
27
+
28
+ ---
29
+
30
+ ## Installation
31
+
32
+ ```bash
33
+ npm install -g markdown-lsp
34
+ # or per-project
35
+ npm install markdown-lsp
36
+ ```
37
+
38
+ Node.js >= 20 required.
18
39
 
19
- - `textDocument/documentSymbol` — heading outline
20
- - `workspace/symbol` — fuzzy subsequence search across all headings (e.g. `oaf` matches `OAuth flow`)
21
- - `textDocument/definition` — jump from a link to its target document
22
- - `textDocument/references` — find every page linking to the current document
23
- - `textDocument/completion` — wiki-link completion `[[...]]`
24
- - `textDocument/publishDiagnostics` — warnings for unresolved link targets
25
- - `workspace.executeCommand("markdownLsp/reindex")` — force re-index of the workspace
26
- - Incremental indexing via content-hash diff; watched-files cleanup
40
+ ---
27
41
 
28
- This layer is fully deterministic, free, and runs offline against your Postgres.
42
+ ## Subcommands
29
43
 
30
- ### Semantic (optional, AI-powered)
44
+ All subcommands accept a **`--pretty`** flag for indented JSON output (compact by default).
31
45
 
32
- Off by default. When enabled, an extract pass identifies canonical concepts per section so that
33
- references survive synonym variation (`auth` ≡ `authentication` ≡ `OAuth` ≡ `login`).
46
+ | Subcommand | Arguments | Description |
47
+ |---|---|---|
48
+ | `workspace-outline` | `<docs-dir> [--prefix p] [--limit n]` | List all pages with metadata |
49
+ | `outline` | `<docs-dir> <page>` | Heading outline of a single page |
50
+ | `search-text` | `<docs-dir> <query> [--mode ranked\|verbatim] [--regex] [--case-sensitive] [--prefix p] [--limit n] [--context n]` | Full-text search |
51
+ | `search-symbols` | `<docs-dir> <query> [--limit n]` | Fuzzy subsequence search across headings |
52
+ | `search-paths` | `<docs-dir> <glob>` | List pages matching a glob pattern |
53
+ | `links-to` | `<docs-dir> <page>` | All pages that link to `<page>` |
54
+ | `links-from` | `<docs-dir> <page>` | All links originating from `<page>` |
55
+ | `resolve-link` | `<docs-dir> <from-page> <link-text>` | Resolve a specific link text from a page |
56
+ | `get-section` | `<docs-dir> <page> <anchor>` | Get a section by anchor slug |
57
+ | `lsp` / `serve` | `[--stdio]` | Start the LSP stdio server |
58
+ | `graph` | `<docs-dir> [--format json\|dot\|mermaid\|html] [--out file]` | Export the doc link graph |
59
+ | `semantic-search` | `<docs-dir> <query> [--limit n] [--model model]` | AI semantic search via embeddings |
34
60
 
35
- Enable with:
61
+ ### search-text modes
62
+
63
+ - **ranked** (default) — tokenizes query, drops stop words, ranks by coverage/heading/proximity. Best for natural-language questions.
64
+ - **verbatim** (`--mode verbatim`) — literal substring match. Use `--regex` for regex.
65
+
66
+ ### Output format
67
+
68
+ All subcommands print JSON to stdout. Use `--pretty` for human-readable output:
36
69
 
37
70
  ```bash
38
- export MARKDOWN_LSP_AI_ENABLED=1
39
- export AI_GATEWAY_API_KEY=... # Vercel AI Gateway
71
+ markdown-lsp search-text ./docs "authentication" --limit 5 --pretty
40
72
  ```
41
73
 
42
- If the flag is not set, no AI calls are ever made. The server starts and behaves as a
43
- pure-structural LSP — no key required.
74
+ ---
44
75
 
45
- ## Architecture
76
+ ## Examples
77
+
78
+ ```bash
79
+ # Workspace overview
80
+ markdown-lsp workspace-outline ./docs --limit 20 --pretty
81
+
82
+ # Find pages about authentication
83
+ markdown-lsp search-text ./docs "authentication flow" --pretty
46
84
 
47
- - LSP over stdio (`vscode-languageserver/node`) — works in any editor
48
- - pgvector (Neon serverless) for cosine search on canonical-term embeddings (only when AI layer is enabled)
49
- - Drizzle ORM; all tables prefixed `mdlsp_`
50
- - Vercel AI Gateway (`text-embedding-3-small` for embeddings, `gpt-4o-mini` for extraction) — when AI on
51
- - An optional MCP HTTP facade (M3) over the same handlers — for AI agents like Claude Code
85
+ # What links to README.md?
86
+ markdown-lsp links-to ./docs README.md
52
87
 
53
- ## Setup
88
+ # Glob search: all files under api/
89
+ markdown-lsp search-paths ./docs "api/**"
90
+
91
+ # Outline of a specific page
92
+ markdown-lsp outline ./docs quick-start.md --pretty
93
+
94
+ # Find headings containing "auth"
95
+ markdown-lsp search-symbols ./docs "auth" --limit 10
96
+
97
+ # Resolve a link from a page
98
+ markdown-lsp resolve-link ./docs README.md "Getting Started"
99
+
100
+ # Get a specific section by anchor
101
+ markdown-lsp get-section ./docs overview.md quick-links --pretty
102
+ ```
103
+
104
+ ---
105
+
106
+ ## Graph export
107
+
108
+ Export the full page link graph — nodes are pages, edges are markdown links.
54
109
 
55
110
  ```bash
56
- pnpm install
57
- cp .env.example .env.local # fill DATABASE_URL; AI_GATEWAY_API_KEY only if you want the AI layer
58
- pnpm migrate # runs scripts/apply-migration.ts against DATABASE_URL
59
- pnpm build
111
+ # JSON (nodes + edges — machine-readable)
112
+ markdown-lsp graph ./docs --format json --pretty
113
+
114
+ # Graphviz DOT
115
+ markdown-lsp graph ./docs --format dot > graph.dot
116
+
117
+ # Mermaid flowchart (embed in markdown)
118
+ markdown-lsp graph ./docs --format mermaid
119
+
120
+ # Self-contained interactive HTML with D3 force-directed graph
121
+ # (drag, zoom, hover highlights neighbours, click to inspect)
122
+ markdown-lsp graph ./docs --format html --out graph.html
60
123
  ```
61
124
 
62
- ## Run
125
+ JSON output shape:
126
+ ```json
127
+ {
128
+ "nodes": [{"id": "README.md", "title": "Docsbook", "charCount": 2634, "sectionsCount": 10}],
129
+ "edges": [{"source": "README.md", "target": "quick-start.md", "kind": "inline", "label": "Get started"}],
130
+ "unresolvedCount": 3
131
+ }
132
+ ```
133
+
134
+ ---
63
135
 
64
- LSP via stdio (for editor integration):
136
+ ## Semantic search
137
+
138
+ AI-powered semantic search using text embeddings — finds conceptually related pages even if they
139
+ don't contain the exact query words.
65
140
 
66
141
  ```bash
67
- node dist/server.js --stdio
142
+ # Requires OPENROUTER_API_KEY (OpenRouter) or AI_GATEWAY_API_KEY (Vercel AI Gateway)
143
+ OPENROUTER_API_KEY=sk-or-... markdown-lsp semantic-search ./docs "how to configure webhooks" --limit 5
144
+
145
+ # Override embedding model
146
+ markdown-lsp semantic-search ./docs "authentication" --model openai/text-embedding-3-small --limit 3
68
147
  ```
69
148
 
70
- `bin/markdown-lsp` wraps the same entry point as a CLI.
149
+ - Default embedding model: `openai/text-embedding-3-small` (via OpenRouter model prefix required)
150
+ - Results cached in `.markdown-lsp-cache/embeddings/` — second run is instant, no API call
151
+ - Returns `[{ pagePath, pageTitle, score, snippet }]` sorted by cosine similarity
71
152
 
72
- ## Use from Docsbook
153
+ **Environment variables:**
73
154
 
74
- The structural layer is what Docsbook's "Source of Truth" feature wants. Wire it in like this:
155
+ | Variable | Purpose |
156
+ |---|---|
157
+ | `OPENROUTER_API_KEY` | OpenRouter API key (takes priority if set) |
158
+ | `AI_GATEWAY_API_KEY` | Vercel AI Gateway key (fallback) |
159
+ | `EMBEDDING_MODEL` | Override default embedding model |
160
+
161
+ ---
162
+
163
+ ## LSP mode (editor integration)
164
+
165
+ `markdown-lsp` also works as a Language Server Protocol server for editors (VS Code, Zed, Neovim, etc.).
166
+
167
+ ```bash
168
+ # Recommended (v1.0.0+)
169
+ npx markdown-lsp lsp --stdio
170
+
171
+ # Back-compat — old LSP flag style still works so existing editor configs don't break
172
+ npx markdown-lsp --stdio
173
+ ```
174
+
175
+ The LSP server speaks the standard protocol over stdio. It requires a Postgres database for the structural index (see Setup below).
176
+
177
+ ### Editor configuration example (VS Code)
178
+
179
+ In your `settings.json`:
180
+
181
+ ```json
182
+ {
183
+ "markdown-lsp.serverPath": "markdown-lsp",
184
+ "markdown-lsp.args": ["lsp", "--stdio"]
185
+ }
186
+ ```
187
+
188
+ ---
189
+
190
+ ## Use as a library
75
191
 
76
192
  ```ts
77
- import { ensureWorkspace, indexWorkspace } from "@docsbook/markdown-lsp/indexer"
78
- import { getDocumentSymbols, getWorkspaceSymbols } from "@docsbook/markdown-lsp/core"
193
+ import { buildGraph, loadDocsAsFiles } from "markdown-lsp/graph"
194
+ import { searchTextRanked, searchSymbols, listPages } from "markdown-lsp/bridge"
195
+
196
+ const graph = buildGraph("./docs")
197
+ const hits = searchTextRanked(graph, "authentication flow")
198
+ const pages = listPages(graph, { limit: 50 })
199
+ ```
200
+
201
+ Available entry points:
202
+ - `markdown-lsp/bridge` — search functions + `RichDocGraph`, `buildInMemoryGraph`, types
203
+ - `markdown-lsp/graph` — `buildGraph(docsRoot)`, `loadDocsAsFiles(docsRoot)`
204
+ - `markdown-lsp/indexer` — SQLite/Postgres workspace indexer (for LSP use)
205
+ - `markdown-lsp/core` — document symbols and references (for LSP use)
206
+ - `markdown-lsp/parser` — raw Markdown parser
207
+
208
+ ---
209
+
210
+ ## LSP Setup (for editor / structural indexer use)
79
211
 
80
- // after cloning a workspace repo into ./tmp/<workspace-id>/
81
- const ws = await ensureWorkspace("./tmp/42")
82
- await indexWorkspace(ws)
212
+ The CLI subcommands work **without any database** — they build an in-memory graph on the fly.
83
213
 
84
- // MCP tools then call:
85
- await getWorkspaceSymbols(ws, "auth")
86
- await findReferencesToDocument(ws, authDocId)
214
+ The LSP server requires Postgres (for the incremental index):
215
+
216
+ ```bash
217
+ pnpm install
218
+ cp .env.example .env.local # fill DATABASE_URL; AI_GATEWAY_API_KEY only if you want the AI layer
219
+ pnpm migrate # runs scripts/apply-migration.ts against DATABASE_URL
220
+ pnpm build
221
+ ```
222
+
223
+ Optional AI layer (semantic synonym resolution):
224
+
225
+ ```bash
226
+ export MARKDOWN_LSP_AI_ENABLED=1
227
+ export AI_GATEWAY_API_KEY=... # Vercel AI Gateway
87
228
  ```
88
229
 
89
- No AI required.
230
+ ---
231
+
232
+ ## Architecture
233
+
234
+ - **CLI** — `node:util parseArgs`, zero extra deps, reads `.md` files into an in-memory graph
235
+ - **Graph** — pure TypeScript, no DB needed; `buildGraph(docsRoot)` walks the directory tree
236
+ - **LSP** — `vscode-languageserver/node` over stdio; requires Postgres (Drizzle ORM, `mdlsp_` prefix)
237
+ - **AI layer** (opt-in) — pgvector cosine search on canonical-term embeddings; `text-embedding-3-small` via Vercel AI Gateway
238
+ - **Bridge** — pure in-memory search (searchText, searchTextRanked, searchSymbols, searchPaths, listPages)
239
+
240
+ ---
90
241
 
91
242
  ## Tests
92
243
 
@@ -94,17 +245,21 @@ No AI required.
94
245
  pnpm test
95
246
  ```
96
247
 
97
- 27 tests cover the parser, indexer, and core handlers (plus a small suite for the AI feature flag).
248
+ 27 tests cover the parser, indexer, core handlers, and bridge search functions.
249
+
250
+ ---
98
251
 
99
252
  ## Milestones
100
253
 
101
254
  - **M0 — Scaffold** ✅
102
255
  - **M1 — Structural layer** ✅
103
- - M2 — Semantic extract (opt-in, code present, awaiting live AI Gateway credit)
104
- - M3 — MCP HTTP facade
256
+ - **M2 — Semantic extract** (opt-in, code present, awaiting live AI Gateway credit)
257
+ - **M3 — CLI-first interface** ✅ (v1.0.0)
105
258
  - M4 — User overrides for the glossary (merge / split / rename / add_synonym)
106
259
  - M5 — Docsbook integration
107
260
 
261
+ ---
262
+
108
263
  ## License
109
264
 
110
265
  MIT
package/bin/markdown-lsp CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import("../dist/server.js")
2
+ import("../dist/cli.js")
@@ -0,0 +1,3 @@
1
+ export declare function cachedEmbedding(text: string, model: string): number[] | null;
2
+ export declare function saveEmbedding(text: string, model: string, vec: number[]): void;
3
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/ai/cache.ts"],"names":[],"mappings":"AAUA,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAW5E;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,CAK9E"}
@@ -0,0 +1,27 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ const CACHE_DIR = ".markdown-lsp-cache/embeddings";
5
+ function cacheKey(text, model) {
6
+ return createHash("sha256").update(model + "\x00" + text).digest("hex");
7
+ }
8
+ export function cachedEmbedding(text, model) {
9
+ const key = cacheKey(text, model);
10
+ const filePath = join(CACHE_DIR, `${key}.json`);
11
+ if (existsSync(filePath)) {
12
+ try {
13
+ return JSON.parse(readFileSync(filePath, "utf8"));
14
+ }
15
+ catch {
16
+ return null;
17
+ }
18
+ }
19
+ return null;
20
+ }
21
+ export function saveEmbedding(text, model, vec) {
22
+ mkdirSync(CACHE_DIR, { recursive: true });
23
+ const key = cacheKey(text, model);
24
+ const filePath = join(CACHE_DIR, `${key}.json`);
25
+ writeFileSync(filePath, JSON.stringify(vec), "utf8");
26
+ }
27
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/ai/cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAC5E,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEhC,MAAM,SAAS,GAAG,gCAAgC,CAAA;AAElD,SAAS,QAAQ,CAAC,IAAY,EAAE,KAAa;IAC3C,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,GAAG,MAAM,GAAG,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AACzE,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,KAAa;IACzD,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IACjC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,CAAC,CAAA;IAC/C,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAa,CAAA;QAC/D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAA;QACb,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,KAAa,EAAE,GAAa;IACtE,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IACzC,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IACjC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,CAAC,CAAA;IAC/C,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAA;AACtD,CAAC"}
@@ -3,7 +3,13 @@ export interface AiConfig {
3
3
  embeddingModel: string;
4
4
  extractModel: string;
5
5
  hasGatewayKey: boolean;
6
+ hasOpenRouterKey: boolean;
6
7
  }
7
8
  export declare function getAiConfig(): AiConfig;
8
9
  export declare function assertAiEnabled(): void;
10
+ /**
11
+ * Lighter check for CLI commands (semantic-search) that don't need MARKDOWN_LSP_AI_ENABLED.
12
+ * Only checks that at least one API key is present. Exits with a clear message on failure.
13
+ */
14
+ export declare function assertApiKey(): void;
9
15
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/ai/config.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,OAAO,CAAA;IAChB,cAAc,EAAE,MAAM,CAAA;IACtB,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,OAAO,CAAA;CACvB;AAED,wBAAgB,WAAW,IAAI,QAAQ,CAQtC;AAED,wBAAgB,eAAe,IAAI,IAAI,CAYtC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/ai/config.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,OAAO,CAAA;IAChB,cAAc,EAAE,MAAM,CAAA;IACtB,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,OAAO,CAAA;IACtB,gBAAgB,EAAE,OAAO,CAAA;CAC1B;AAED,wBAAgB,WAAW,IAAI,QAAQ,CAStC;AAED,wBAAgB,eAAe,IAAI,IAAI,CAYtC;AAED;;;GAGG;AACH,wBAAgB,YAAY,IAAI,IAAI,CAQnC"}
package/dist/ai/config.js CHANGED
@@ -1,10 +1,12 @@
1
+ import { DEFAULT_EMBEDDING_MODEL } from "./gateway.js";
1
2
  export function getAiConfig() {
2
3
  const enabled = process.env.MARKDOWN_LSP_AI_ENABLED === "1" || process.env.MARKDOWN_LSP_AI_ENABLED === "true";
3
4
  return {
4
5
  enabled,
5
- embeddingModel: process.env.EMBEDDING_MODEL ?? "text-embedding-3-small",
6
+ embeddingModel: process.env.EMBEDDING_MODEL ?? DEFAULT_EMBEDDING_MODEL,
6
7
  extractModel: process.env.EXTRACT_MODEL ?? "gpt-4o-mini",
7
8
  hasGatewayKey: Boolean(process.env.AI_GATEWAY_API_KEY),
9
+ hasOpenRouterKey: Boolean(process.env.OPENROUTER_API_KEY),
8
10
  };
9
11
  }
10
12
  export function assertAiEnabled() {
@@ -12,8 +14,19 @@ export function assertAiEnabled() {
12
14
  if (!cfg.enabled) {
13
15
  throw new Error("AI features are disabled. Set MARKDOWN_LSP_AI_ENABLED=1 to enable embeddings + semantic extraction.");
14
16
  }
15
- if (!cfg.hasGatewayKey) {
16
- throw new Error("AI features require AI_GATEWAY_API_KEY (Vercel AI Gateway) — or fork the gateway module to use a different provider.");
17
+ if (!cfg.hasGatewayKey && !cfg.hasOpenRouterKey) {
18
+ throw new Error("AI features require OPENROUTER_API_KEY (OpenRouter) or AI_GATEWAY_API_KEY (Vercel AI Gateway).");
19
+ }
20
+ }
21
+ /**
22
+ * Lighter check for CLI commands (semantic-search) that don't need MARKDOWN_LSP_AI_ENABLED.
23
+ * Only checks that at least one API key is present. Exits with a clear message on failure.
24
+ */
25
+ export function assertApiKey() {
26
+ const cfg = getAiConfig();
27
+ if (!cfg.hasGatewayKey && !cfg.hasOpenRouterKey) {
28
+ process.stderr.write("Error: set OPENROUTER_API_KEY (OpenRouter) or AI_GATEWAY_API_KEY (Vercel AI Gateway) to use semantic-search.\n");
29
+ process.exit(1);
17
30
  }
18
31
  }
19
32
  //# sourceMappingURL=config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/ai/config.ts"],"names":[],"mappings":"AAOA,MAAM,UAAU,WAAW;IACzB,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,uBAAuB,KAAK,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,uBAAuB,KAAK,MAAM,CAAA;IAC7G,OAAO;QACL,OAAO;QACP,cAAc,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,wBAAwB;QACvE,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,aAAa;QACxD,aAAa,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;KACvD,CAAA;AACH,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,MAAM,GAAG,GAAG,WAAW,EAAE,CAAA;IACzB,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CACb,qGAAqG,CACtG,CAAA;IACH,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,sHAAsH,CACvH,CAAA;IACH,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/ai/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AAUtD,MAAM,UAAU,WAAW;IACzB,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,uBAAuB,KAAK,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,uBAAuB,KAAK,MAAM,CAAA;IAC7G,OAAO;QACL,OAAO;QACP,cAAc,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,uBAAuB;QACtE,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,aAAa;QACxD,aAAa,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;QACtD,gBAAgB,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;KAC1D,CAAA;AACH,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,MAAM,GAAG,GAAG,WAAW,EAAE,CAAA;IACzB,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CACb,qGAAqG,CACtG,CAAA;IACH,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,aAAa,IAAI,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CACb,gGAAgG,CACjG,CAAA;IACH,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY;IAC1B,MAAM,GAAG,GAAG,WAAW,EAAE,CAAA;IACzB,IAAI,CAAC,GAAG,CAAC,aAAa,IAAI,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QAChD,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,gHAAgH,CACjH,CAAA;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;AACH,CAAC"}
@@ -2,6 +2,13 @@ export interface EmbeddingResult {
2
2
  vectors: number[][];
3
3
  tokensUsed: number;
4
4
  }
5
- export declare function embedTexts(texts: string[]): Promise<EmbeddingResult>;
6
- export declare function embedOne(text: string): Promise<number[]>;
5
+ /**
6
+ * Embed multiple texts with disk-cache support.
7
+ * Checks cache per-text before hitting the API; saves results after.
8
+ * @param texts - texts to embed
9
+ * @param modelOverride - override the embedding model (default: EMBEDDING_MODEL env or openai/text-embedding-3-small)
10
+ * @param useCache - whether to use disk cache (default: true)
11
+ */
12
+ export declare function embedTexts(texts: string[], modelOverride?: string, useCache?: boolean): Promise<EmbeddingResult>;
13
+ export declare function embedOne(text: string, modelOverride?: string): Promise<number[]>;
7
14
  //# sourceMappingURL=embeddings.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"embeddings.d.ts","sourceRoot":"","sources":["../../src/ai/embeddings.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,EAAE,EAAE,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,wBAAsB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,eAAe,CAAC,CAiB1E;AAED,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAG9D"}
1
+ {"version":3,"file":"embeddings.d.ts","sourceRoot":"","sources":["../../src/ai/embeddings.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,EAAE,EAAE,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAC9B,KAAK,EAAE,MAAM,EAAE,EACf,aAAa,CAAC,EAAE,MAAM,EACtB,QAAQ,UAAO,GACd,OAAO,CAAC,eAAe,CAAC,CAgD1B;AAED,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAGtF"}
@@ -1,27 +1,65 @@
1
1
  import { embedMany } from "ai";
2
2
  import { getGateway, EMBEDDING_MODEL } from "./gateway.js";
3
3
  import { assertAiEnabled } from "./config.js";
4
+ import { cachedEmbedding, saveEmbedding } from "./cache.js";
4
5
  const BATCH_SIZE = 96;
5
6
  const MAX_INPUT_CHARS = 6000;
6
- export async function embedTexts(texts) {
7
+ /**
8
+ * Embed multiple texts with disk-cache support.
9
+ * Checks cache per-text before hitting the API; saves results after.
10
+ * @param texts - texts to embed
11
+ * @param modelOverride - override the embedding model (default: EMBEDDING_MODEL env or openai/text-embedding-3-small)
12
+ * @param useCache - whether to use disk cache (default: true)
13
+ */
14
+ export async function embedTexts(texts, modelOverride, useCache = true) {
7
15
  if (texts.length === 0)
8
16
  return { vectors: [], tokensUsed: 0 };
9
17
  assertAiEnabled();
10
18
  const gw = getGateway();
11
- const model = gw.embedding(EMBEDDING_MODEL);
12
- const out = [];
19
+ const modelName = modelOverride ?? EMBEDDING_MODEL;
20
+ const model = gw.embedding(modelName);
21
+ const out = new Array(texts.length).fill(null);
13
22
  let tokensUsed = 0;
14
- for (let i = 0; i < texts.length; i += BATCH_SIZE) {
15
- const batch = texts.slice(i, i + BATCH_SIZE).map((t) => t.slice(0, MAX_INPUT_CHARS));
16
- const res = await embedMany({ model, values: batch });
17
- for (const v of res.embeddings)
18
- out.push(v);
23
+ // Check cache first
24
+ const uncachedIndices = [];
25
+ const uncachedTexts = [];
26
+ if (useCache) {
27
+ for (let i = 0; i < texts.length; i++) {
28
+ const cached = cachedEmbedding(texts[i], modelName);
29
+ if (cached !== null) {
30
+ out[i] = cached;
31
+ }
32
+ else {
33
+ uncachedIndices.push(i);
34
+ uncachedTexts.push(texts[i]);
35
+ }
36
+ }
37
+ }
38
+ else {
39
+ for (let i = 0; i < texts.length; i++) {
40
+ uncachedIndices.push(i);
41
+ uncachedTexts.push(texts[i]);
42
+ }
43
+ }
44
+ // Embed uncached texts in batches
45
+ for (let b = 0; b < uncachedTexts.length; b += BATCH_SIZE) {
46
+ const batchTexts = uncachedTexts.slice(b, b + BATCH_SIZE).map((t) => t.slice(0, MAX_INPUT_CHARS));
47
+ const batchIndices = uncachedIndices.slice(b, b + BATCH_SIZE);
48
+ const res = await embedMany({ model, values: batchTexts });
49
+ for (let j = 0; j < res.embeddings.length; j++) {
50
+ const vec = res.embeddings[j];
51
+ const originalIndex = batchIndices[j];
52
+ out[originalIndex] = vec;
53
+ if (useCache) {
54
+ saveEmbedding(uncachedTexts[b + j], modelName, vec);
55
+ }
56
+ }
19
57
  tokensUsed += res.usage?.tokens ?? 0;
20
58
  }
21
59
  return { vectors: out, tokensUsed };
22
60
  }
23
- export async function embedOne(text) {
24
- const { vectors } = await embedTexts([text]);
61
+ export async function embedOne(text, modelOverride) {
62
+ const { vectors } = await embedTexts([text], modelOverride);
25
63
  return vectors[0];
26
64
  }
27
65
  //# sourceMappingURL=embeddings.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"embeddings.js","sourceRoot":"","sources":["../../src/ai/embeddings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,CAAA;AAC9B,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAE7C,MAAM,UAAU,GAAG,EAAE,CAAA;AACrB,MAAM,eAAe,GAAG,IAAI,CAAA;AAO5B,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,KAAe;IAC9C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,CAAA;IAC7D,eAAe,EAAE,CAAA;IACjB,MAAM,EAAE,GAAG,UAAU,EAAE,CAAA;IACvB,MAAM,KAAK,GAAG,EAAE,CAAC,SAAS,CAAC,eAAe,CAAC,CAAA;IAE3C,MAAM,GAAG,GAAe,EAAE,CAAA;IAC1B,IAAI,UAAU,GAAG,CAAC,CAAA;IAElB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,UAAU,EAAE,CAAC;QAClD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC,CAAA;QACpF,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAA;QACrD,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,UAAU;YAAE,GAAG,CAAC,IAAI,CAAC,CAAa,CAAC,CAAA;QACvD,UAAU,IAAI,GAAG,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC,CAAA;IACtC,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,CAAA;AACrC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAY;IACzC,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA;IAC5C,OAAO,OAAO,CAAC,CAAC,CAAE,CAAA;AACpB,CAAC"}
1
+ {"version":3,"file":"embeddings.js","sourceRoot":"","sources":["../../src/ai/embeddings.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,8 @@
1
+ export declare const DEFAULT_EMBEDDING_MODEL = "openai/text-embedding-3-small";
1
2
  export declare const EMBEDDING_MODEL: string;
2
3
  export declare const EMBEDDING_DIM = 1536;
3
4
  export declare const EXTRACT_MODEL: string;
4
5
  export declare function getGateway(): import("@ai-sdk/openai").OpenAIProvider;
6
+ /** Reset the cached gateway (useful for tests or when env changes mid-process) */
7
+ export declare function resetGateway(): void;
5
8
  //# sourceMappingURL=gateway.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../../src/ai/gateway.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,eAAe,QAA0D,CAAA;AACtF,eAAO,MAAM,aAAa,OAAO,CAAA;AACjC,eAAO,MAAM,aAAa,QAA6C,CAAA;AAIvE,wBAAgB,UAAU,4CASzB"}
1
+ {"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../../src/ai/gateway.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,uBAAuB,kCAAkC,CAAA;AACtE,eAAO,MAAM,eAAe,QAAyD,CAAA;AACrF,eAAO,MAAM,aAAa,OAAO,CAAA;AACjC,eAAO,MAAM,aAAa,QAA6C,CAAA;AAIvE,wBAAgB,UAAU,4CAoBzB;AAED,kFAAkF;AAClF,wBAAgB,YAAY,IAAI,IAAI,CAEnC"}
@@ -1,18 +1,27 @@
1
1
  import { createOpenAI } from "@ai-sdk/openai";
2
- export const EMBEDDING_MODEL = process.env.EMBEDDING_MODEL ?? "text-embedding-3-small";
2
+ export const DEFAULT_EMBEDDING_MODEL = "openai/text-embedding-3-small";
3
+ export const EMBEDDING_MODEL = process.env.EMBEDDING_MODEL ?? DEFAULT_EMBEDDING_MODEL;
3
4
  export const EMBEDDING_DIM = 1536;
4
5
  export const EXTRACT_MODEL = process.env.EXTRACT_MODEL ?? "gpt-4o-mini";
5
6
  let _gw = null;
6
7
  export function getGateway() {
7
8
  if (_gw)
8
9
  return _gw;
9
- const apiKey = process.env.AI_GATEWAY_API_KEY;
10
- if (!apiKey)
11
- throw new Error("AI_GATEWAY_API_KEY is not set");
12
- _gw = createOpenAI({
13
- apiKey,
14
- baseURL: process.env.AI_GATEWAY_BASE_URL ?? "https://ai-gateway.vercel.sh/v1",
15
- });
10
+ // OpenRouter takes priority if OPENROUTER_API_KEY is set
11
+ const openrouterKey = process.env.OPENROUTER_API_KEY;
12
+ const gatewayKey = process.env.AI_GATEWAY_API_KEY;
13
+ const apiKey = openrouterKey ?? gatewayKey;
14
+ if (!apiKey) {
15
+ throw new Error("Set OPENROUTER_API_KEY (OpenRouter) or AI_GATEWAY_API_KEY (Vercel AI Gateway) to use AI features.");
16
+ }
17
+ const baseURL = openrouterKey
18
+ ? (process.env.AI_GATEWAY_BASE_URL ?? "https://openrouter.ai/api/v1")
19
+ : (process.env.AI_GATEWAY_BASE_URL ?? "https://ai-gateway.vercel.sh/v1");
20
+ _gw = createOpenAI({ apiKey, baseURL });
16
21
  return _gw;
17
22
  }
23
+ /** Reset the cached gateway (useful for tests or when env changes mid-process) */
24
+ export function resetGateway() {
25
+ _gw = null;
26
+ }
18
27
  //# sourceMappingURL=gateway.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"gateway.js","sourceRoot":"","sources":["../../src/ai/gateway.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAE7C,MAAM,CAAC,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,wBAAwB,CAAA;AACtF,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,CAAA;AACjC,MAAM,CAAC,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,aAAa,CAAA;AAEvE,IAAI,GAAG,GAA2C,IAAI,CAAA;AAEtD,MAAM,UAAU,UAAU;IACxB,IAAI,GAAG;QAAE,OAAO,GAAG,CAAA;IACnB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAA;IAC7C,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAA;IAC7D,GAAG,GAAG,YAAY,CAAC;QACjB,MAAM;QACN,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,iCAAiC;KAC9E,CAAC,CAAA;IACF,OAAO,GAAG,CAAA;AACZ,CAAC"}
1
+ {"version":3,"file":"gateway.js","sourceRoot":"","sources":["../../src/ai/gateway.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAE7C,MAAM,CAAC,MAAM,uBAAuB,GAAG,+BAA+B,CAAA;AACtE,MAAM,CAAC,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,uBAAuB,CAAA;AACrF,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,CAAA;AACjC,MAAM,CAAC,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,aAAa,CAAA;AAEvE,IAAI,GAAG,GAA2C,IAAI,CAAA;AAEtD,MAAM,UAAU,UAAU;IACxB,IAAI,GAAG;QAAE,OAAO,GAAG,CAAA;IAEnB,yDAAyD;IACzD,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAA;IACpD,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAA;IACjD,MAAM,MAAM,GAAG,aAAa,IAAI,UAAU,CAAA;IAE1C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,mGAAmG,CACpG,CAAA;IACH,CAAC;IAED,MAAM,OAAO,GAAG,aAAa;QAC3B,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,8BAA8B,CAAC;QACrE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,iCAAiC,CAAC,CAAA;IAE1E,GAAG,GAAG,YAAY,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAA;IACvC,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,YAAY;IAC1B,GAAG,GAAG,IAAI,CAAA;AACZ,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}