qi - query engine cli for ai agents and humans
Save tokens by delegating some of your AI Agent's work to qi. Agent skills included.
- ⚡ Ultra-fast indexing
- ⚡ Lower tokens + latency
- 🧠 Better reasoning (agents focus on thinking, not retrieval)
- 🔒 Fully local + offline
- 🧩 Works with Ollama, LM Studio, Claude, OpenAI, MLX, etc.
Features
- Blazing-fast full-text search — BM25 via SQLite FTS5, no external search engine required
- Flexible vector search — embeddings stored and queried on your machine; works with Ollama, LM Studio, llama.cpp, or OpenAI's SOTA models.
- Hybrid search with RRF fusion — combines BM25 and vector rankings for results that are both precise and semantically aware
- Smart chunking — breakpoint scoring prioritizes headings, code fences, and paragraph boundaries so chunks stay meaningful, not arbitrary
- Zero-dependency storage — a single SQLite file holds your entire index; content-addressable blobs (SHA-256) eliminate duplicates automatically
- Works offline, always — vector search is an optional enhancement; BM25 search works out of the box with no providers configured
Install
curl -fsSL https://raw.githubusercontent.com/itsmostafa/qi/main/install.sh | shThe script detects your OS and architecture, and installs the
latest release.
Run qi update later to upgrade in place.
Or via go install:
go install github.com/itsmostafa/qi@latestClaude Code Plugin
qi is available as a Claude Code plugin. Add the marketplace and install with:
# Add the marketplace
/plugin marketplace add itsmostafa/qi
# Install the plugin
/plugin install qi
Quickstart
# Initialize config and database
qi init
# Index current directory
qi index
# Or index a specific path
qi index ~/notes
# Collection names are generated from paths
# ~/notes -> notes
# Re-index it later by generated collection name
qi index notes
# Search
qi search "my query"
# Search a specific collection
qi search "my query" -c notes
# Hybrid search (BM25 + vector, requires embedding provider)
qi query "my query" --mode hybrid
# Hybrid search a specific collection
qi query "my query" --mode hybrid -c notes
# List all collections
qi list
# Delete a collection and all its indexed data
qi delete notes
# Health check
qi doctorCommands
| Command | Description |
|---|---|
qi init | Create config and database |
qi index [path|collection] | Index directory (current dir by default) or collection |
qi search <query> | BM25 full-text search |
qi query <query> | Hybrid search (BM25 + vector) |
qi get <hash> | Retrieve indexed source by full content hash or unambiguous prefix (--lines A:B, --max-bytes N) |
qi list | List all collections |
qi delete <collection> | Delete a collection and all its indexed data |
qi stats | Show index statistics |
qi doctor | Health check |
Search Modes
qi query supports three modes via --mode, defaulting to search.default_mode from the config:
lexical: BM25 full-text search onlyhybrid(default): BM25 + vector search fused with Reciprocal Rank Fusion (RRF)deep: alias forhybrid
Use --explain to see scoring breakdown:
qi query "chunking algorithm" --mode hybrid --explainDocumentation
Full documentation is in the docs/ directory:
docs/architecture.md— system architecture, data flows, and design decisionsdocs/configuration.md— all config options with explanationsdocs/config.example.yaml— fully annotated example configdocs/named-collections.md— collections guide
Configuration
The config lives at ~/.config/qi/config.yaml. See docs/configuration.md for all options or docs/config.example.yaml for a fully annotated example.
database_path: ~/.local/share/qi/qi.db
collections:
- name: notes
path: ~/notes
extensions: [.md, .txt]
providers:
# Local (Ollama / llama.cpp)
embedding:
name: ollama
base_url: http://localhost:11434
model: nomic-embed-text
dimension: 768
# Or: OpenAI cloud (set OPENAI_API_KEY in your environment)
# embedding:
# name: openai
# model: text-embedding-3-small
# dimension: 1536
# batch_size: 32Retrieving and citing search hits
Every search and query hit includes a full SHA-256 hash, a source_uri
identifying its collection/path, and 1-indexed, inclusive start_line and
end_line ranges in the original indexed source. The hash identifies the source
version; cite the URI, hash, and line range together. Integer doc_id/chunk_id
fields are internal IDs, not arguments to get. If you override --config,
use the same config for search and retrieval.
qi search "deployment" --format json
# Copy hash, start_line and end_line from a hit:
qi get <hash> --lines <start_line>:<end_line>
qi get <hash> --max-bytes 4096
# Include up to two additional matching passages per document:
qi query "deployment" --passages 2 --format json--passages accepts 0–5 (default 0), without giving a document extra result slots
or ranking votes. Supporting passages inherit the parent hit's hash and source
URI. Ranges cover the source behind each chunk, not just highlighted words;
Markdown syntax and frontmatter remain present when retrieved.
get reads the indexed snapshot, not the live file. Editing a file does not
change that snapshot until reindexing. Old hashes are not retained as revision
history after replacement/deletion and index compaction. A hash alone does not
prove the current file is unchanged.
Unambiguous hash prefixes (such as abc123) also work. Identical content at
multiple paths is returned once, with other locations in also_at; use the
search hit's source_uri to retain the intended citation identity. Prefixes
matching different content hashes are errors.
Search skips chunks with missing or invalid source ranges. After upgrading an
older index, run qi index <collection> to repair them. If the collection is no
longer in config, index its source directory with qi index /path/to/directory.
License
This project is licensed under the MIT License - see the LICENSE file for details.