Skip to content

whiskeyjimbo/veska

Veska

Quickstart CI Docs Go Report Card Go Version License: AGPL v3

Give your AI coding agent real structural understanding of your codebase. Veska parses your repo into a queryable code graph + semantic index and serves it to your agent (and you) over MCP - so answers come back as exact file:line spans, not grep-and-guess. Fully local, in-process, zero-setup.

Veska - natural-language query in, exact file:line answer out

Veska is a local code-intelligence daemon. It runs on your laptop, parses your repository into a code graph (nodes + edges), embeds that graph semantically, and serves both to your editor and your AI agent over MCP - so they reason from the same structural ground truth instead of guessing.

What it gives you

  • Grounded structural answers. Every function, type, file, and call traces to a node, edge, or commit. Structural recall stays current within the save → staging freshness budget.
  • Eventually-consistent semantic search. semantic_search embeds the graph with an in-process embedder (model2vec by default - no external service); during the indexing lag window it falls back to a BM25 lexical index and flags the response degraded_reasons.
  • Promotion checks. On every commit, synchronous checks emit advisory Findings: dead code, contract drift, leaked secrets, and vulnerable go.mod dependencies via the OSV.dev advisory database. All four ship on by default: veska init writes an active [vuln_source] config block to ~/.veska/config.toml (see docs/operations/CONFIG-SURFACE.md) unless you opt out with veska init --no-vuln (or answer "no" at the interactive prompt). Lifecycle: the block is read at daemon start, so it takes effect from the next veska service start (restart the service after editing the block). New scans pick it up automatically; to scan already-promoted repos retroactively, run veska reindex <path>.
  • Duplicate & similar-code detection. Find copy-paste and drifted clones for de-dupe triage: eng_find_clones for one symbol-group mode at a time (exact byte-identical, or near fuzzy via stored similarity), and eng_find_clusters for a whole-repo (or cross-repo) sweep across exact, structural (Type-2, same shape after renaming), and near tiers, ranked tightest first. Exact/structural are deterministic hashes; near reads the similarity scores auto-link already stored (no new embedding sweep).
  • Optional LLM features. An off-by-default post-promotion review pipeline and per-node summaries (Ollama-backed).
  • Mechanical wiki. Hot-zones and entry-points computed from the graph, no LLM in the path. The eng_get_hot_zone and eng_get_entry_points MCP tools return data in-memory and write nothing; the veska wiki CLI renders the same data into docs/veska/{hot_zones,entry_points}.md inside the repo (re-runnable, idempotent - bracket markers in each page preserve any hand edits outside the managed block). A context-pack tool sits alongside.
  • Cross-actor attribution. A single actor_kind: human | agent | system enum distinguishes who changed what in the audit log.

Process topology - one binary, three personalities

make build produces a single binary at bin/veska; bin/veska-daemon and bin/veska-mcp are symlinks to it. The argv[0] dispatcher in cmd/veska/main.go routes each invocation into its own package.

Invocation Role
veska CLI - init, repo, reindex, service, doctor, backup, wiki, … Run veska --help for the full list.
veska-daemon (symlink) Long-running process - owns the SQLite store, the fsnotify watcher, the embedder, and the post-promotion queue. Composition root: internal/cli/daemon/wire.go.
veska-mcp (symlink) Thin stdio shim proxying an editor's MCP connection to the daemon's Unix socket. Routes into internal/cli/mcp.

Vector backends

Semantic search and auto-linking find the nearest embeddings to a query. Two backends do that, chosen with VESKA_VECTOR_BACKEND: memory (memvec, the default - exact linear scan, lowest RAM, no setup) and usearch (approximate HNSW - flat query latency at scale, needs libusearch_c.so). Measured across Go repos (memvec is the exact-recall oracle; regenerate with make eval-backend-matrix):

repo symbols q p95 memvec q p95 usearch usearch recall RAM memvec RAM usearch
go-git 11,262 4.0 ms 0.3 ms 0.9990 34 MiB 65 MiB
veska 12,900 4.3 ms 0.4 ms 0.9994 39 MiB 65 MiB
grpc-go 19,520 6.2 ms 0.3 ms 0.9979 59 MiB 129 MiB
consul 37,272 11.6 ms 0.4 ms 0.9965 113 MiB 129 MiB

memvec's query latency grows with repo size while usearch stays flat (~0.3 ms); usearch trades ~2x the RAM and a slower index build for that. Stick with the default until linear-scan latency is noticeable. Full discussion: Vector storage backends.

Requirements

  • Go 1.26+
  • Go repositories only, for now. The tree-sitter parser ships a single Go grammar, so the code graph is built from .go files. Other languages are a deliberate future step, not a current capability.
  • No external services for core use. SQLite, the vector index, and the default embedder all run in-process. A fresh machine indexes and searches with nothing else installed or running.

Embedder

Semantic search needs an embedder. Veska elects one at boot in preference order - it never mixes vector spaces, so exactly one embedder owns the index at a time:

  1. model2vec (potion-code-16M) - a fast, in-process static code embedder. The default and recommended choice. Get it either way:
    • Fat binary (make build, default) - the model is compiled into the binary. Zero setup: nothing to install, no download, no network.
    • Thin binary (make build-small) + veska install model2vec - a one-time ~62 MB download into ~/.veska/.
  2. static-v2 - an in-binary fallback that works with no model files at all (lower quality). Used only when model2vec is unavailable.

No Ollama, no network, and no separate process is required for search.

Optional: Ollama

Ollama is only for the optional LLM features - the post-promotion review pipeline and per-node summaries (both off by default). It is not used for embeddings in the default config. (Power users can force an Ollama embedding model with VESKA_EMBEDDER=ollama, but model2vec is faster and higher-quality on code, so this is rarely worthwhile.)

Install Ollama only if you want those LLM features:

# macOS:        brew install ollama && ollama serve &
# Linux (snap): sudo snap install ollama && ollama serve &
# Linux (curl): curl -fsSL https://ollama.com/install.sh | sh && ollama serve &

Build

make build is the fat binary by default - it embeds the model2vec weights into the binary so the install is zero-setup: no separate download, no network, no static-v2 fallback at boot.

make build        # default: ~104 MB fat binary (model2vec ~62 MB embedded
                  # into a ~42 MB thin build). Zero setup at runtime.
make build-small  # ~42 MB thin: veska, veska-daemon, veska-mcp (+ layercheck).
                  # Use this only when you want size-sensitive binaries
                  # (CI, containers); you must then run `veska install model2vec`
                  # to avoid booting on the low-quality static-v2 fallback.
make test         # go test ./...
make all          # build-small + test + vet + lint + layercheck
                  # (uses the thin build to keep the test loop fast)

Binaries land in ./bin/. Either export PATH="$PWD/bin:$PATH" or use the ./bin/ prefix in the Quick Start below.

Install into your PATH

After a make build, drop the binaries into a user bin directory in one step:

make install                         # → ~/.local/bin (default)
VESKA_INSTALL_DIR=/usr/local/bin sudo make install   # system-wide

For a self-contained tarball (the three fat binaries + install.sh + a README), run make release-archive. The archive at dist/veska-<version>-<os>-<arch>.tar.gz is the same shape a future GitHub release will ship - ./install.sh from inside the extracted directory does the same thing as make install .

Quick start

# 1. Build veska (default: fat, zero-setup embedder).
make build
# Size-sensitive builds can `make build-small` instead, then run
# `./bin/veska install model2vec` to avoid the low-quality static-v2 fallback.

# 2. Initialize veska's data directory at ~/.veska/.
./bin/veska init

# 3. Start the daemon.
#
#    Pick one:
#      - Just kicking the tires? Background it:    ./bin/veska-daemon &
#      - Want it on every boot, auto-restart on
#        crash, logs under ~/.veska/logs?           use the service form below.
#
#    For a real install, run it as an OS service (systemd --user on Linux,
#    launchd on macOS). Uninstall with `./bin/veska service uninstall`.
./bin/veska service install
./bin/veska service start

# 4. Register a repo. --wait blocks until the cold scan finishes (a few
#    seconds for most repos) so the first search below is already hot.
#    Without --wait the scan kicks off in the background; the next
#    `eng_search_semantic` call may then return `[]` with
#    `degraded_reasons=embeddings_pending` until indexing catches up.
#    Tail ~/.veska/logs/daemon.log for the "cold scan: complete" line.
./bin/veska repo add /path/to/your/repo --wait

# 5. Sanity-check.
./bin/veska doctor status

The first veska repo add registers the repo, installs the git post-commit hook with an absolute path to the veska binary, and dispatches a cold scan through the daemon. Subsequent commits drive promotion via eng_promote_repo on the daemon's MCP socket.

To force a re-scan of an already-registered repo (e.g. after a model swap):

./bin/veska reindex /path/to/your/repo

Safe to run while the daemon is up - the CLI dispatches the cold-scan through the daemon's eng_reindex_repo MCP tool , so your editor's MCP connection is not interrupted. With the daemon stopped, the same command falls back to a direct in-process reparse.

Next steps

Point your editor's MCP client at bin/veska-mcp (a stdio command), and seed a per-agent instruction file with veska init --agent <name>. The full walkthroughs - editor configs (Claude Desktop, Cursor, Zed, Continue), the shell JSON-RPC interface, and the first-call sanity check - live in the manual: Quickstart and Connecting your editor.

Configuration

State lives under ~/.veska/ (VESKA_HOME). Daemon config is ~/.veska/config.toml - see docs/operations/CONFIG-SURFACE.md. Key environment variables:

Var Purpose Default
VESKA_HOME Data root ~/.veska
VESKA_EMBEDDER Embedder election: auto (model2vec→static-v2), or force model2vec / static / ollama auto
VESKA_VECTOR_BACKEND memory (in-process memvec linear scan) or usearch (HNSW) memory
VESKA_OLLAMA_URL Ollama endpoint - LLM review + summaries, and VESKA_EMBEDDER=ollama http://localhost:11434
VESKA_EMBED_MODEL Ollama embedding model - only when VESKA_EMBEDDER=ollama nomic-embed-text

The elected embedder is recorded in ~/.veska/embedder.locked. Switching embedders requires a re-index (veska reindex) since their vectors aren't comparable.

Architecture

cmd/veska/            single binary entry point; argv[0] dispatcher in main.go
internal/
  core/
    domain/           pure entities: Node, Edge, Graph, Task, Finding
    ports/            interface contracts (GraphStorage, VectorStorage, VulnSource, …)
  application/        use-case services: ingester, promoter, embedder, checks, review, wiki
  cli/                composition roots: daemon/wire.go and the mcp stdio shim
  infrastructure/     adapters: sqlite, vector, embedding/{model2vec,static,ollama,elect}, treesitter, mcp, git
  repo/               repos-table registry
  platform/           cross-cutting operational concerns (config, doctor, health, …)
docs/                 user manual, architecture summary, and operational runbooks

MCP tools

The daemon exposes 38 tools over a Unix-socket JSON-RPC server (forwarded to editors by veska-mcp). Tool names follow eng_<verb>_<object>. Quick map:

Family Tools
Admin eng_get_status, eng_get_config, eng_get_current_repo, eng_get_repo, eng_list_repos
Repo lifecycle eng_add_repo, eng_remove_repo, eng_promote_repo, eng_reindex_repo, eng_set_repo_alias, eng_remove_repo_alias
Graph eng_find_symbol, eng_get_node, eng_get_file_nodes, eng_get_call_chain
Search eng_search_semantic, eng_search_similar, eng_find_related (semantic neighbors of the code at a file_path+line)
Duplicates eng_find_clones (duplicate groups for one mode: exact byte-identical or near fuzzy), eng_find_clusters (whole-repo / cross-repo de-dupe triage across exact/structural/near tiers, tightest first)
Blast radius eng_get_blast_radius, eng_get_diff_blast_radius, eng_get_dirty_blast_radius
Context eng_get_context_pack, eng_find_changed_symbols (takes ref_a/ref_b or aliases base/head; defaults to HEAD~1..HEAD; chunks filtered, comment-only diffs surface non_symbol_changes_only in degraded_reasons)
Dependencies eng_list_dependencies (external modules the repo CALLS into, ranked by call-site count)
Misc eng_find_owner, eng_find_todos
Findings eng_list_findings, eng_get_finding, eng_close_finding, eng_reopen_finding
Suppressions eng_list_suppressions, eng_get_suppression, eng_suppress_finding, eng_close_suppression
Wiki eng_get_hot_zone, eng_get_entry_points

See the MCP tools reference for the full response shape, repo_id aliasing, parameter aliases, cross-repo edges, and the parked task-tool family.

Testing

make test          # go test ./... - unit + integration suites
make test-mcp      # python pytest harness against a running daemon (fast)
make test-mcp-deep # add cross-validation against the live SQLite

tests/mcp/ spawns bin/veska-mcp as a subprocess, drives every registered tool with happy/bad/edge inputs, and pretty-prints each call's transcript so the suite doubles as a human-readable smoke. Requires VESKA_HOME to point at a running daemon's data dir and at least one veska repo add'd repo.

Documentation

License

Veska is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0-only) - see LICENSE. You may use, study, modify, and share it freely; if you run a modified version as a network service, the AGPL requires you to offer that version's source to its users.

Third-party components (Go dependencies and, in embed_model builds, the potion-code-16M model weights) are redistributed under their own permissive licenses, reproduced in THIRD_PARTY_NOTICES. Regenerate that file with make notices after a dependency change.

About

Local-first code-intelligence daemon: parses your repo into a queryable code graph + semantic index and serves it to AI agents (and you) over MCP/CLI - exact file:line answers, not grep.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages