Skip to content

Add sqlite-vec plugin to Python server store#247

Open
javiermtorres wants to merge 18 commits intomainfrom
21-semantic-similarity-queries
Open

Add sqlite-vec plugin to Python server store#247
javiermtorres wants to merge 18 commits intomainfrom
21-semantic-similarity-queries

Conversation

@javiermtorres
Copy link
Copy Markdown

@javiermtorres javiermtorres commented Apr 7, 2026

Adds semantic similarity search to the local knowledge store using sqlite-vec and sentence-transformers.

Changes Made

  • sdk/python/src/cq/store.py: Added sqlite-vec virtual table (knowledge_units_vec) and SentenceTransformer embedding model support to LocalStore. Embeddings are generated on insert/update and used for vector similarity search in query().
  • sdk/python/tests/test_store.py: Added embedding_model pytest fixture and updated all LocalStore constructor calls to pass the embedding model.
  • sdk/python/pyproject.toml: Added sqlite-vec==0.1.7a10 and sentence-transformers[onnx] as dependencies.

Note: The original implementation targeted plugins/cq/server/ (the Python MCP server). That server was removed on main (replaced by the Go CLI cq mcp). The sqlite-vec feature has been ported to its new home in the Python SDK (sdk/python/src/cq/store.py).

@javiermtorres
Copy link
Copy Markdown
Author

@copilot check if conflicts can be solved easily

…k/python/src/cq/store.py

- Remove Python MCP server (deleted on main, replaced by Go CLI)
- Port sqlite-vec and sentence-transformers additions to the new
  sdk/python/src/cq/store.py location (renamed from local_store.py)
- Port test additions to sdk/python/tests/test_store.py
- Add sqlite-vec and sentence-transformers deps to sdk/python/pyproject.toml
- Resolve uv.lock conflict (pygments 2.19.2 -> 2.20.0)
- Remove sqlite-vec from team-api/pyproject.toml (deleted, now server/backend)

Co-authored-by: javiermtorres <4608715+javiermtorres@users.noreply.github.com>
Copy link
Copy Markdown

Copilot AI commented Apr 7, 2026

@copilot check if conflicts can be solved easily

Conflicts resolved in f7e3464. Here's what was needed:

  • plugins/cq/server/ (modify/delete): Main deleted the Python MCP server (replaced by Go CLI). Accepted the deletion and ported our sqlite-vec changes to the new location.
  • sdk/python/src/cq/store.py (content, rename-detected): Git detected that plugins/cq/server/cq_mcp/local_store.py was renamed to sdk/python/src/cq/store.py on main. Merged our sqlite-vec additions (_VEC_SCHEMA_SQL, embedding_model param, vec insert/update/delete/query logic) into main's updated version.
  • sdk/python/tests/test_store.py (content, rename-detected): Similarly ported embedding_model fixture and updated LocalStore constructor calls.
  • server/backend/uv.lock (content): Took main's pygments 2.20.0 version.
  • team-api/pyproject.toml (modify/delete): Accepted deletion (renamed to server/backend/pyproject.toml on main; sqlite-vec now goes via the SDK dependency).

Also added sqlite-vec==0.1.7a10 and sentence-transformers[onnx] to sdk/python/pyproject.toml.

Copilot AI changed the title Add sqlite-vec plugin Add sqlite-vec plugin to Python SDK store Apr 7, 2026
@peteski22
Copy link
Copy Markdown
Collaborator

@copilot rebase from origin/main and solve conflicts.

peteski22 and others added 16 commits April 9, 2026 19:23
* Document tier model in READMEs and cq-status plugin

Adds a "Knowledge tiers" section to the top README explaining what
local, private, and public mean and where knowledge units land when
propose is called with or without a remote configured. Shorter blocks
in the CLI README, Go SDK README, and Python SDK README link back to
the canonical explanation. The cq-status plugin command doc prints a
one-line tier legend directly under the tier counts.

Fixes #246.

* Use SDK-consistent symbol names in tier docs

Match the surrounding Quick Start vocabulary: the Python SDK section
now uses `cq.propose(...)` (the Quick Start instance is named `cq`,
not `client`), and the Go SDK section qualifies the returned tier
constants as `cq.Private` / `cq.Local` to match the preceding
sentence.
Co-authored-by: peteski22 <487783+peteski22@users.noreply.github.com>
plugins/cq/uv.lock was left at version 0.5.0 in 556c2ba ("Bump
plugin to 0.6.0", #239) because the lockfile was not regenerated
alongside pyproject.toml. The existing uv-lock pre-commit hook in
the root config never fired because its default files regex is
anchored to the repo root and cq has no root pyproject.toml (three
independent uv sub-projects, no workspace). The same inert hook
existed in sdk/python/.pre-commit-config.yaml.

Regenerate plugins/cq/uv.lock so it matches the bumped 0.6.0
version. Add two layers of drift protection covering all three Python
sub-projects (plugins/cq, sdk/python, server/backend):

- Switch the lint-plugin, lint-sdk-python, and lint-server-backend
  Make targets to invoke uv run --locked. uv run silently regenerates
  a stale lockfile before any wrapped command sees it, so without
  --locked no downstream check could ever fail in CI. --locked makes
  uv abort with a clear error message instead, and CI runs the Make
  targets directly.

- Replace the inert upstream uv-lock hook with three local pre-commit
  hooks that invoke uv lock --check --directory <subdir>. These fire
  at git commit time (when pre-commit is invoked outside of uv run)
  for any change touching a sub-project's pyproject.toml or uv.lock,
  giving early feedback before the change leaves the developer's
  machine.
Co-authored-by: peteski22 <487783+peteski22@users.noreply.github.com>
* Clarify recent additions are local-only in cq-status

When a remote is configured, Client.Propose writes directly to the
remote and Client.Status fills the recent slice from the local store
only, so users who only propose against a remote saw an empty "Recent
Additions" section without context.

Rename the section to "Recent Local Additions" in the slash command,
add explicit guidance to render "(no recent local additions)" when the
slice is empty, and update the status MCP tool description so it
reflects that tier counts and domains aggregate across local and
remote while recent additions and confidence distribution are local
only. Closes #245.

* Qualify Propose fallback note in cq-status doc

The existing wording claimed proposed units always go to the remote
when one is configured, but Client.Propose falls back to local storage
when the remote is unreachable. Note that the remote must be both
configured and reachable for units to bypass the local recent list.
Co-authored-by: peteski22 <487783+peteski22@users.noreply.github.com>
* Merge remote domain counts into client Status()

* Cover remote domain count merge in Status() tests

Add regression tests for both SDKs asserting that Client.Status()
merges remote /stats domain counts into StoreStats.domain_counts,
accumulating counts where a domain appears in both local and remote.

sdk/go: TestStatusWithRemoteMergesDomainCounts
sdk/python: test_status_merges_remote_domain_counts

* Isolate TestDrain from host CQ_* environment variables

TestDrain was the only test in sdk/go/client_test.go that constructed
a Client directly without first calling testClearEnv(t). A host shell
with CQ_ADDR set would cause the "local-only" seeding client to hit
the real remote, leaving the local store empty and failing the drain
assertions.

---------

Co-authored-by: Peter Wilson <peter@mozilla.ai>
Co-authored-by: peteski22 <487783+peteski22@users.noreply.github.com>
feat(installer): multi-host installer for Cursor, Windsurf, OpenCode, and Claude

Replace the legacy OpenCode-only shell installer with a stdlib-only Python installer at scripts/install/ supporting four hosts. Drops jq as a prerequisite.

Installer core:
  - Idempotent primitives for JSON merge, hook entries, Markdown blocks,
    manifest-tracked file copies, and symlinks. --dry-run across all.
  - Per-host adapters under cq_install/hosts/ with a shared RunState for
    dedup (shared skills installed once, binary fetched once).
  - Config write hardening: malformed JSON, non-dict leaves, and
    unexpected hook shapes raise clear errors naming the file.

Binary management:
  - New plugins/cq/scripts/cq_binary.py (stdlib-only) owns the binary
    download, version check, and cache logic. Single source of truth
    consumed by both the Claude plugin bootstrap and the installer.
  - Non-Claude hosts invoke the cq binary directly for MCP, fixing
    startup timeouts on Windows caused by Python stdio buffering.
  - bootstrap.py shrinks to a thin Claude-only launcher.

Hosts:
  - Cursor: mcp.json, rules/cq.mdc, four lifecycle hooks with
    platform-aware shell escaping, shared runtime hook script.
  - Windsurf: mcp_config.json, shared skills.
  - OpenCode: opencode.json with schema seeding, AGENTS.md block,
    generated commands with user-edit protection on uninstall.
  - Claude: thin marketplace wrapper (add/install/remove).

Build:
  - Makefile targets for all hosts plus install-all/uninstall-all.
  - PowerShell wrapper for Windows.
  - Installer CI workflow, pre-commit ty and uv-lock checks.

Co-authored-by: Jonathan Kingston (#166)

Closes #154
Co-authored-by: peteski22 <487783+peteski22@users.noreply.github.com>
Add author, repository, license, and keywords to plugin.json.
Remove duplicated version from marketplace.json since plugin.json
is authoritative. Bump CLI version pin to 0.2.1 in bootstrap.json.
Update test fixtures to match.
Co-authored-by: peteski22 <487783+peteski22@users.noreply.github.com>
Commit 3454b2e squash-merged PR #254 with a malformed co-author
trailer. This empty commit corrects the attribution for Jonathan
Kingston's contribution to the multi-host installer (see #166).

Co-authored-by: Jonathan Kingston <338988+jonathanKingston@users.noreply.github.com>
Co-authored-by: peteski22 <487783+peteski22@users.noreply.github.com>
Replace the v0.0.0 placeholder with the actual tagged version.
The replace directive remains because the SDK module proxy copy
is missing embedded skill.md (see #259).
Co-authored-by: peteski22 <487783+peteski22@users.noreply.github.com>
The previous pin was stale; the server imports cq.models for shared
Pydantic schemas which are current in sdk/python/0.6.1.
Co-authored-by: peteski22 <487783+peteski22@users.noreply.github.com>
The skill description is what Claude sees in the available skills list
to decide whether to invoke it. The previous description explained what
cq is rather than when to use it, causing Claude to skip activation.

Rewrite the description as an imperative trigger that makes querying
unconditional, names the blind-spot failure mode (stale training data),
and lists the full per-task loop (query, propose, confirm/flag).
Co-authored-by: peteski22 <487783+peteski22@users.noreply.github.com>
* Commit SKILL.md in SDKs for module proxy completeness

The Go and Python SDK copies of the plugin skill prompt were gitignored
and generated at build time. This left the Go module proxy copy
incomplete, breaking go get/go install for external consumers.

Commit the SKILL.md copies (renamed from skill.md to match the source),
remove them from .gitignore, and add sync enforcement via per-SDK
check-skill-sync Makefile targets and a dedicated CI workflow that
blocks merges when copies drift from the plugin source.

Fixes #259

* Address review: check-skill-sync as gatekeeper, not sync-skill

Replace sync-skill with check-skill-sync as the dependency of lint and
test in both SDK Makefiles, matching the check-notice pattern. Drift now
fails the build instead of being silently fixed. Add root-level
sync-skill target so the error message guidance works from any directory.
Co-authored-by: peteski22 <487783+peteski22@users.noreply.github.com>
The Go module proxy now has a complete copy of the SDK (sdk/go/v0.2.2
includes the committed SKILL.md), so the local replace directive is no
longer needed. This unblocks go install for external consumers.

The replace directive is kept commented out for local monorepo
development convenience.
Co-authored-by: peteski22 <487783+peteski22@users.noreply.github.com>
)

* Replace exact cli_version check with cli_min_version >= comparison

The plugin bootstrap previously required an exact CLI version match,
forcing a plugin release for every CLI bump even when the contract
hadn't changed. Switch to a minimum version (>=) comparison using
tuple-based semver so the plugin only needs updating when it genuinely
requires a newer CLI feature.

Renames: check_version -> meets_min_version,
load_required_version -> load_min_version.

* Address review feedback: show actual binary version and rename variable

Report the actual cached binary version (not the minimum) in installer
output when the binary is already valid. Rename the leftover
required_version variable in bootstrap.py to min_version.
Co-authored-by: peteski22 <487783+peteski22@users.noreply.github.com>
Co-authored-by: peteski22 <487783+peteski22@users.noreply.github.com>
Co-authored-by: peteski22 <487783+peteski22@users.noreply.github.com>
This file was incorrectly modified by a prior merge commit. Restoring
to the version from main (revision=3 with upload-time metadata fields).

Co-authored-by: peteski22 <487783+peteski22@users.noreply.github.com>
Copy link
Copy Markdown

Copilot AI commented Apr 9, 2026

@copilot rebase from origin/main and solve conflicts.

Done in 45f715e. The branch is now rebased on origin/main — the diff from main contains exactly our 3 intended files:

  • sdk/python/src/cq/store.py — sqlite-vec virtual table + embedding model
  • sdk/python/tests/test_store.pyembedding_model fixture + updated constructors
  • sdk/python/pyproject.tomlsqlite-vec==0.1.7a10 and sentence-transformers[onnx] deps

Also fixed an unintended regression where server/backend/uv.lock had been modified by an old merge commit (restored to match main).

@javiermtorres javiermtorres changed the title Add sqlite-vec plugin to Python SDK store Add sqlite-vec plugin to Python server store Apr 17, 2026
@javiermtorres
Copy link
Copy Markdown
Author

Since the SDK and server stores were split at some point during the development of this PR, the agent tried to reconcile changes and this resulted in unwanted functionality.
@peteski22 as discussed, this will be implemented in the server sqlite instance using an encoderfile, behind a clean interface so other alternatives can be plugged in as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants