Local Semantic File System — bridges your local filesystem with AI models.
Speedy indexes your codebase into a SQLite vector database and exposes semantic search over your code through a CLI and an MCP server for AI agents (Claude Code, Cursor, opencode, Windsurf, …).
Default behavior (no daemon). Out of the box Speedy does not run the background daemon. Synchronization happens only through git hooks, which run standalone (no daemon) on each commit/checkout/merge/rebase. The hooks are installed automatically when you register a repo with
speedy-cli workspace add(and removed byworkspace remove); you can also manage them by hand withspeedy install-hooks/uninstall-hooks. Every hook routes throughspeedy-cli, which fans the work out to all enabled contexts (ai-context, language-context, text-context). The daemon is still available for live file-watching but is never auto-started — launch it explicitly withspeedy daemonor the GUI's "Start daemon" button.All contexts are opt-in.
speedy_indexer(semantic index),language_context(code intelligence) andtext_contextare disabled by default for a new workspace; the workers no-op until you enable them (speedy enable speedy,speedy enable slc). Toggles live under[features]in.speedy/config.toml.
For the full per-binary option reference see
commands.md. For the end-to-end runtime flow seeflow.md.
┌─────────────────────────────────────────────────────────────┐
│ AI Agent / User │
│ │ │
│ ├─────────────────────┐ │
│ ▼ ▼ │
│ speedy-mcp.exe speedy-gui.exe (egui desktop app) │
│ (MCP / stdio) │ │
│ │ │ │
│ ▼ │ │
│ speedy-cli.exe ◄───────────┘ (thin client, no heavy deps)│
│ │ local socket "speedy-daemon" │
│ ▼ │
│ speedy-daemon.exe ← ONE process, global per user │
│ • IPC server │
│ • N file watchers (one task per workspace) │
│ • persistent memory: workspaces.json │
│ │ │
│ │ spawn subprocess │
│ ▼ │
│ speedy-ai-context.exe ← the worker │
│ • indexing, query, embedding, SQLite, chunking │
│ • can also run standalone (no daemon needed) │
└─────────────────────────────────────────────────────────────┘
- One daemon for everything. Never one daemon per workspace. The same
speedy-daemon.exewatches all your projects and survives across CLI invocations. - Persistent memory. The daemon's workspace registry lives in
workspaces.jsonunder your config dir. On reboot, the daemon reads it back and restarts every watcher. - Worker is autonomous.
speedy-ai-context.execan run on its own — the daemon just orchestrates it for live updates.
Pre-built binaries are available on the Releases page.
| Binary | Role |
|---|---|
speedy-ai-context.exe |
Worker — indexing, query, embedding |
speedy-daemon.exe |
Single global daemon + file watcher |
speedy-cli.exe |
Thin client (what AI agents / scripts call) |
speedy-mcp.exe |
MCP server for AI coding agents |
speedy-gui.exe |
Desktop GUI (egui) — manage daemon, workspaces, live logs |
Download speedy-setup-<version>.exe from the Releases page and run it.
No admin required. Installs the binaries and configures PATH. The daemon is not started or registered for login — Speedy works via git hooks by default; enable launch-at-login from the GUI (Dashboard → "Avvio al login") if you want live file-watching.
To build the installer from source:
.\scripts\build-installer.ps1 # requires Inno Setup 6: winget install JRSoftware.InnoSetup# 1. Install Rust: https://rustup.rs/
# 2. Install Ollama and pull an embedding model
ollama pull all-minilmBuild + copy to dist/ in one shot (recommended):
cargo xtask dist # incremental build — only recompiles what changed
cargo xtask dist --clean # force full rebuild of all 6 binariesThe xtask command builds all binaries in release mode and copies them into dist/.
Or, if you just want the raw release artifacts in target/release/ without copying:
cargo build-all
# (alias for: cargo build --release -p speedy-ai-context -p speedy-daemon
# -p speedy-cli -p speedy-mcp -p speedy-gui -p speedy-language-context)Speedy ships as 5 binaries with two distinct lifecycles: a long-running background daemon and four short-lived front-end tools. Install them accordingly.
1. The user-facing tools — in one folder, on PATH
Pick a stable install dir, e.g. C:\Program Files\Speedy\ (or
%LOCALAPPDATA%\Programs\Speedy\ if you don't want admin rights), and copy
the four front-end binaries into it:
C:\Program Files\Speedy\
├── speedy-ai-context.exe ← worker (also usable standalone)
├── speedy-cli.exe ← thin client for scripts / MCP
├── speedy-mcp.exe ← MCP server for AI coding agents
└── speedy-gui.exe ← desktop GUI
Adding this folder to your PATH is recommended but not required. With
it on PATH you can type speedy-ai-context query … and speedy-cli daemon status
from any shell, and MCP clients can reference speedy-mcp without a full
path. To add it permanently (PowerShell, current user only):
$dir = 'C:\Program Files\Speedy'
[Environment]::SetEnvironmentVariable(
'Path',
[Environment]::GetEnvironmentVariable('Path','User') + ';' + $dir,
'User')Open a fresh terminal afterwards.
2. The daemon — optional, opt-in
The daemon is not required: by default Speedy syncs through git hooks
(speedy install-hooks), which run the worker standalone on each commit. Run
the daemon only if you want live file-watching (auto-reindex on save).
Easiest way: launch the GUI and use Dashboard → "Start daemon", then tick "Launch at login" to have it start automatically at every logon.
Prefer to wire it up by hand? Drop a shortcut to speedy-daemon.exe into the
Startup folder (Win + R → shell:startup → Enter):
$startup = [Environment]::GetFolderPath('Startup')
$target = 'C:\Program Files\Speedy\speedy-daemon.exe'
$ws = New-Object -ComObject WScript.Shell
$lnk = $ws.CreateShortcut("$startup\speedy-daemon.lnk")
$lnk.TargetPath = $target
$lnk.Save()# Register the workspace once: the daemon adds a file watcher
# and keeps the index up to date in the background.
speedy-cli workspace add C:\path\to\project
# Index it (the daemon dispatches to speedy-ai-context.exe under the hood)
speedy-cli index
# Query
speedy-cli query "how does authentication work?" -k 10
# Sanity checks
speedy-cli daemon ping # → pong
speedy-cli daemon status # JSON with pid, uptime, watcher_count, …
speedy-cli daemon list # workspaces currently watchedOr just launch speedy-gui.exe and add workspaces from the Workspaces
tab — same operations, with live tail of the daemon logs.
If you don't want a background process at all, speedy-ai-context.exe works on its own:
SPEEDY_NO_DAEMON=1 speedy-ai-context index .
SPEEDY_NO_DAEMON=1 speedy-ai-context query "find auth"Without the daemon there is no live re-indexing on every save — but if the
workspace was added with speedy-cli workspace add (or you ran
speedy install-hooks), the git hooks keep the index fresh on every
commit/checkout/merge/rebase. Between commits, a query reflects what the last
hook run (or manual index / sync / update) captured.
The exhaustive option list is in commands.md. Brief summary below.
Common subcommands (each honors --json and -p, --path <PATH>):
| Command | What it does |
|---|---|
index [<subdir>] |
Index a directory into the vector DB |
query <q> [-k <N>] |
Semantic search (top-K, default 5) |
context |
Project context summary |
sync |
Incremental sync of filesystem changes |
daemon |
Spawn the central daemon |
workspace list |
List registered workspaces |
Top-level shortcut flags (alternative to subcommands): -r/--read, -m/--modify --file, -d/--daemons, -w/--workspaces. Full table in commands.md.
Global flags: -p/--path, --daemon-socket, --json.
| Command | What it does |
|---|---|
index [<subdir>] |
Send exec ... index <subdir> to the daemon |
query <q> [-k <N>] |
Send exec ... query <q> -k <N> — semantic search (requires embedding model) |
grep <pattern> [-k <N>] |
FTS5 keyword search directly on the local index (no embedding, no daemon) |
context |
Send exec ... context |
sync |
Incremental sync across all enabled contexts (mtime+hash skip; prunes deletions) |
update <files…> |
Per-file incremental update across all enabled contexts (used by the post-commit hook) |
force [-p <path>] |
Same fan-out incremental sync, targeting an explicit path |
reindex [-p <path>] |
Full rebuild across all enabled contexts |
daemon {status,list,stop,ping} |
Talk to the daemon directly (no speedy-ai-context.exe involved) |
workspace {list,add,remove} |
Register/unregister workspaces (path is positional); add/remove also install/remove the git hooks |
Minimal CLI; all management is via the IPC protocol.
| Flag | Default | Purpose |
|---|---|---|
--daemon-socket <NAME> |
speedy-daemon |
Local socket name (Named Pipe on Windows, UDS elsewhere) |
--daemon-dir <DIR> |
platform config | Override the dir holding daemon.pid and workspaces.json |
An egui-based desktop app that wraps the daemon's IPC. Launch with no
arguments. Four tabs:
| Tab | What it does |
|---|---|
| Dashboard | Daemon status (pid/uptime/version), metrics, restart/reload/stop, "notify on error" toggle |
| Workspaces | List + add (native file picker) + per-workspace Index/Sync/Open folder/Remove + feature toggles (Speedy Indexer / Language Context) |
| Scan | Walk a root path looking for existing .speedy/index.sqlite and bulk-register what it finds |
| Logs | Live tail (subscribe-log IPC) or historical view of any daemon.log.* file, with level/substring/target/workspace filters and JSON/JSONL export |
A system tray icon (green = daemon alive, red = down) provides quick Open Speedy / Restart daemon / Quit actions.
Communicates over stdio. Tools exposed:
| Tool | Args | Underlying call |
|---|---|---|
speedy_query |
{ query: string, top_k?: number, mode?: string } |
$SPEEDY_BIN query <q> -k <top_k> --json — semantic search, requires embedding model |
speedy_grep |
{ pattern: string, top_k?: number } |
$SPEEDY_BIN grep <pattern> -k <top_k> --json — FTS5 keyword search, no embedding required |
speedy_index |
{ path?: string } |
$SPEEDY_BIN index <path> |
speedy_context |
{} |
$SPEEDY_BIN context --json |
Set SPEEDY_BIN to choose the underlying binary (default: speedy-cli).
Minimal:
{
"mcpServers": {
"speedy": {
"command": "speedy-mcp",
"args": [],
"env": { "SPEEDY_BIN": "speedy-cli" }
}
}
}Full example for Claude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"speedy": {
"command": "C:\\Program Files\\Speedy\\speedy-mcp.exe",
"args": [],
"env": {
"SPEEDY_BIN": "speedy-cli",
"SPEEDY_DEFAULT_SOCKET": "speedy-daemon",
"SPEEDY_MCP_TOP_K": "10"
}
}
}
}Typical agent flow:
- User opens their repo and runs
speedy-cli workspace add .(or justspeedy-ai-context index .once —ensure_daemonauto-registers). - The daemon spawns a watcher and keeps the SQLite index fresh on every file change.
- The agent (Claude / Cursor / opencode / …) calls
speedy_query { "query": "where is auth handled?", "top_k": 10 }— the MCP server invokesspeedy-cli query "where is auth handled?" -k 10 --jsonand pipes the ranked chunks back to the agent. - Subsequent edits trigger a re-index in the background; the next query sees them.
The daemon listens on a local socket (Windows Named Pipe / Unix Domain Socket via the interprocess crate; default name speedy-daemon). Wire format: one line of UTF-8 per request, one line per response. Full reference in docs/ipc-protocol.md.
| Request | Response |
|---|---|
ping |
pong |
status |
JSON {pid, uptime_secs, workspace_count, watcher_count, version} |
list / watch-count / daemon-pid |
JSON / number |
is-workspace <path> |
true / false |
add <path> / remove <path> |
ok / error: ... |
sync <path> |
ok / error: ... |
reload |
ok: N workspaces reloaded |
exec <args> (or exec\t<cwd>\t<args>) |
stdout of speedy-ai-context.exe <args> (with SPEEDY_NO_DAEMON=1) |
stop |
ok then graceful shutdown |
Speedy reads configuration in this priority order (highest first):
- Environment variables
.speedy/config.speedy.jsonin the workspace root~/.speedy/config.speedy.json(user-level defaults)speedy.tomlor.speedy/config.tomlin the workspace root
Full provider reference: CONFIG.md.
| Variable | Default | Purpose |
|---|---|---|
SPEEDY_NO_DAEMON |
unset | If set, the worker skips ensure_daemon (the daemon itself sets this when spawning speedy-ai-context.exe) |
SPEEDY_DEFAULT_SOCKET |
speedy-daemon |
Override the default IPC socket name |
SPEEDY_DAEMON_DIR |
platform config dir | Override the dir for daemon.pid / workspaces.json |
SPEEDY_BIN |
speedy-cli |
Binary that speedy-mcp invokes for tool calls |
SPEEDY_PROVIDER |
ollama |
Embedding provider type (ollama, openai, gemini, anthropic, agent, …) |
SPEEDY_MODEL |
all-minilm |
Embedding model name |
SPEEDY_BASE_URL |
(provider default) | Base URL of the embedding endpoint |
SPEEDY_API_KEY |
(empty) | API key for remote providers |
SPEEDY_AGENT_COMMAND |
(empty) | External command when SPEEDY_PROVIDER=agent |
SPEEDY_OLLAMA_URL |
http://localhost:11434 |
Legacy alias for SPEEDY_BASE_URL (Ollama) |
SPEEDY_TOP_K |
5 |
Default top-K for query |
RUST_LOG |
(empty) | Tracing filter |
Place .speedy/config.speedy.json in your workspace (or ~/.speedy/config.speedy.json for user-level defaults). This format supports all providers:
{
"provider": {
"type": "ollama",
"model": "all-minilm"
},
"top_k": 10
}Note: add
.speedy/config.speedy.jsonto your.gitignore— it may contain API keys.
model = "all-minilm"
ollama_url = "http://localhost:11434"
provider_type = "ollama"
top_k = 10
max_chunk_size = 1000
chunk_overlap = 200
watch_delay_ms = 500
ignore_patterns = ["target/", ".git/", "node_modules/"]Each workspace can independently enable or disable the two Speedy subsystems. The setting is stored in <workspace>/.speedy/config.toml under [features].
GUI — Workspaces tab, per-workspace row:
- Speedy Indexer checkbox — file indexer (
speedy-ai-context) - Language Context checkbox — code intelligence / symbol graph (
speedy-language-context)
Changes take effect on the next daemon restart for that workspace.
Config file — edit <workspace>/.speedy/config.toml directly:
[features]
speedy_indexer = true # file indexer (speedy-ai-context)
language_context = true # code intelligence (speedy-language-context)The GUI and config file are always in sync: the GUI reads and writes the same config.toml.
CLI shorthand (
speedy enable speedy,speedy enable slc, …) is planned but not yet implemented.
| Provider | type |
API key required | Notes |
|---|---|---|---|
| Ollama | ollama |
No | Default. Requires Ollama running locally. |
| OpenAI | openai |
Yes | Native embedding API. |
| Gemini | gemini |
Yes | Native embedding API. |
| Azure OpenAI | azure-openai |
Yes | Set base_url to your deployment endpoint. |
| Any OpenAI-compatible | openai-compatible |
Optional | LM Studio, vLLM, etc. Requires base_url. |
| Anthropic (proxy) | anthropic |
Yes | No native embedding — uses generative model. |
| DeepSeek (proxy) | deepseek |
Yes | No native embedding — uses generative model. |
| External process | agent |
No | Process reads text, writes JSON float array to stdout. |
Full examples and field reference in CONFIG.md.
Speedy honors .gitignore automatically (via the ignore crate). Add a .speedyignore in the project root for Speedy-specific rules — same syntax as .gitignore.
# .speedyignore
build/
dist/
*.logThe daemon's watcher additionally hardcodes ignores for: target/, .git/, node_modules/, .speedy/, .speedy-daemon/, .idea/, .vscode/, dist/, build/, __pycache__/, .cargo/.
~/.config/speedy/ (Windows: %APPDATA%\speedy)
├── workspaces.json ← global registry of every workspace the user
│ has added — the daemon's persistent memory
└── daemon.pid ← PID of the running daemon (one only)
<workspace>/
├── .speedy/
│ ├── index.sqlite ← vector store for THIS workspace
│ ├── config.toml ← optional per-workspace overrides (TOML)
│ └── config.speedy.json ← optional per-workspace overrides (JSON, supports all providers)
└── .speedyignore ← optional, gitignore syntax
- Only the daemon writes to
workspaces.json. CLI / MCP / scripts ask the daemon toadd/remove; they never write the file directly. daemon.pidis used only to clean up a stale instance after a reboot.- Each workspace has its own
.speedy/index.sqlite— the daemon centralizes orchestration, not data.
Cargo.toml (workspace)
└── packages/
├── speedy-core/ # shared lib: DaemonClient, workspace registry,
│ config, local-socket helpers, embedding type
├── speedy-ai-context/ # bin = speedy-ai-context.exe (worker)
├── speedy-daemon/ # bin = speedy-daemon.exe (central daemon)
├── speedy-cli/ # bin = speedy-cli.exe (thin client)
├── speedy-mcp/ # bin = speedy-mcp.exe (MCP server)
├── speedy-gui/ # bin = speedy-gui.exe (egui desktop app)
└── testexe/ # internal test fixture
cargo test --workspace # run all tests
cargo build --workspace # debug build
cargo clippy --workspace # lintSome daemon tests touch process-wide state (cwd, SPEEDY_DAEMON_DIR) and serialize on a shared mutex — they may run slower on a single thread.
speedy-cli says "Cannot connect to daemon. Is it running?"
Either the daemon was never started or its named pipe is orphaned. Start a fresh one:
speedy-ai-context daemon # spawns the central daemon detached
speedy-cli daemon ping # should print `pong`daemon-pid file is stale (PID points to a dead process)
The daemon now takes an advisory lock on daemon.pid at startup, so a stale PID file alone won't block a new daemon — kill_existing_daemon clears it. If you see "address in use" anyway, kill leftover processes manually:
Get-Process speedy-daemon -ErrorAction SilentlyContinue | Stop-Process -Force
Remove-Item "$env:APPDATA\speedy\daemon.pid" -ErrorAction SilentlyContinuepkill -f speedy-daemon || true
rm -f ~/.config/speedy/daemon.pidspeedy-cli daemon status hangs
Almost always a named-pipe owned by a frozen daemon. Stop-Process / pkill the daemon and re-spawn.
Watcher is not picking up file changes
Check that the workspace is registered (speedy-cli daemon list). If absent, add it: speedy-cli workspace add <PATH>. Verify the path is not under an ignored prefix (target/, .git/, .speedy/, node_modules/, etc.).
speedy-mcp doesn't see my repo
The MCP server runs SPEEDY_BIN against whatever cwd Claude Desktop sets when launching it — usually not your project. Pass -p <PATH> from the agent prompt or speedy-cli workspace add <PATH> once to register the repo. After that, speedy_query works from anywhere because the daemon already monitors it.
MIT