Tooling & Lab
CommandVault
Universal AI command manager.
2 min read · 479 words
CommandVault indexes the 350+ slash commands, skills, agents, plugins, rules, and hooks scattered across Claude Code, Cursor, Copilot, Windsurf, and Aider into a single searchable vault.
Built as a pnpm + Turborepo monorepo with three packages: a core engine (parsers for all six entry types, a three-tier search stack of Fuse.js fuzzy + MiniSearch indexed + SQLite FTS5 full-text with weighted ranking, an LRU cache, and a Chokidar file watcher), a 20-command Commander.js CLI, and a VS Code extension with a TreeView sidebar and React stats webview. SQLite persistence handles favorites, usage, tags, and versioned schema migrations, with FTS5 token sanitization and SSRF protection on remote sync.
- Three-tier search: fuzzy + indexed + FTS5
- VS Code extension · 20-command CLI · core engine
- 6 AI-assistant formats unified
140 commits.
Architecture
Three packages wired by turbo.json's build graph: @commandvault/core builds first, then @commandvault/cli and commandvault-ai (the VS Code extension) build in parallel against it. Core owns persistence, parsing, and search.
Persistence runs through a shared DatabaseAdapter interface: better-sqlite3 (native, WAL-mode) is preferred, but database-factory.ts falls back to sql.js (pure WASM) when native bindings aren't available — the case inside the VS Code extension, since Electron's bundled Node breaks native-module ABI compatibility. Parsing is 13 files under parsers/: the live file-watch flow routes to six format parsers (skill, agent, command, plugin, rule, hook); a seventh, multi-agent parser (Cursor/Copilot/Windsurf/Aider/Continue) also lives in the registry but isn't wired into that same watch path. A Chokidar watcher in vault.ts debounces filesystem events before any of it runs.
How It Works
- A file changes on disk;
vault.tsholds the event behind a hard-coded 500ms debounce so a burst of saves collapses into one re-parse. - The path router matches the file to one of the six format parsers wired into the watcher and produces a typed vault entry.
- The entry writes through the active
DatabaseAdapterinto SQLite, which persists favorites, usage counts, tags, and the entry's full content — untruncated, since an earlier 2000-char cap on this tier was deliberately removed. - The same entry is pushed into Fuse.js and MiniSearch, both of which truncate content to 500 characters before indexing.
- A search query hits whichever tier is active — Fuse.js, MiniSearch, or FTS5, selected by an explicit
tieroption or theminisearchdefault — and scores its hits with the same formula: 55% text relevance, 20% usage frequency, 15% recency, 10% favorite boost. The three tiers are swappable strategies, not a concurrent fan-out. - The CLI and VS Code extension both read this through core's public API — one prints to the terminal, the other renders it in a TreeView sidebar and stats webview.
vault syncandvault import <url>share the sameimportFromUrl()path, where outbound URLs pass throughvalidateUrl()— it blocks private/internal hostnames and re-checks IPv4-mapped-IPv6 addresses after stripping the mapping, closing a bypass a naive hostname check would miss.vault registry's own remote fetch is a separate code path that doesn't go through this guard.