@kai-0131/projiq
v0.1.0
Published
ProjIQ — project intelligence MCP server for AI-assisted dev. Retrieves only task-relevant files, audits stale / duplicate / broken refs, runs Markdown checklists. Cuts token cost, surfaces dead docs / configs.
Downloads
105
Maintainers
Readme
ProjIQ
An MCP server that delivers only task-relevant files as context to AI coding tools — across code, docs, config, and markup, at any scale from a handful of files to 10,000+. Cut token cost, latency, and noise-induced accuracy loss.
Status: Phase 3 landed — vector search, dependency-graph expansion, and both re-ranker backends (Anthropic Claude and local BGE cross-encoder) are implemented and wired into search_context. An in-process file watcher keeps the index fresh between sync_index calls. Phase 4a foundations landed — the Observer records every search_context response and tails .projiq/observations.jsonl for Read events so retrieval quality can be evaluated later. Phase 5 landed — HTML / CSS / JSON chunkers and dependency extraction, four new audit tools (run_review, audit_stale, detect_duplicates, check_broken_refs), a pure-TypeScript BM25 embedder for small / offline projects (PROJIQ_EMBEDDER=bm25), and an automatic small-project BM25 advice log on sync_index. Phase 5+1 landed — claude-projects mode with JSONL chunker for Claude Code session logs and a five-field session filter (sessionId / timeFrom / timeTo / messageType / workspace) on search_context, plus extraRoots[] in config to index ~/.claude/projects/ alongside your main repo.
Why
When you ask an AI coding assistant for help, loading an entire repository wastes tokens, slows responses, and dilutes accuracy through the "lost in the middle" effect. ProjIQ selects only the files that actually matter for the current task.
How it works
task description
↓
[A] Vector search → top-20 candidates ← Phase 1 ✅
[B] Dependency graph walk → follow imports / md links / wikilinks ← Phase 2 ✅
[C] LLM re-ranking → score 0-10 + threshold combo ← Phase 3 ✅
↓
final shortlist
↓
[D] Observer → learns from what you actually used ← Phase 4Use cases
ProjIQ is not just about shrinking prompts — the same index + dependency graph powers three everyday workflows that span any repo size, from a handful of files to 10,000+:
- Day-to-day housekeeping.
audit_stalesurfaces files nobody has touched in N days,detect_duplicatesflags byte-for-byte copies of the same file drifting across folders, andcheck_broken_refscatches dangling imports, dead Markdown links, missing<script src>/@import/tsconfig.extendsbefore they break the build or publish. All three run withoutsync_indexand without downloadingbge-m3, so they work on a fresh clone. - Improvement proposals for existing files.
search_contextpulls the handful of files actually relevant to a task (vector + dependency graph + optional LLM rerank), thenrun_reviewapplies any Markdown checklist —skills/lp-seo.md, a coding-style rubric, an accessibility audit — and returns per-itempass/fail/unclearverdicts with evidence snippets. Pair it with any AI coding assistant to turn "please improve this" into a grounded, item-by-item diff. - Adding what's missing. The same retrieval pipeline lets you ask "what should this repo have that it doesn't?" — feed the shortlist + a gap-analysis checklist to
run_review, or usesearch_contextto find where a new feature / config fits naturally. Good for "what settings are we missing?", "where should this new module live?", and "is there a pattern to follow?" - Searching past Claude Code conversations. Point ProjIQ at
~/.claude/projects/(seeextraRootsin config) andsearch_contextbecomes a semantic search over your own past sessions — recall what you decided, what bug you hit, what alternative you ruled out. Combine withsessionId/timeFrom/timeTo/messageType/workspacefilters to pinpoint a single decision moment. Unique to ProjIQ.
Claude Code session search (claude-projects mode)
Claude Code stores every conversation as a JSONL file under
~/.claude/projects/<workspace>/<uuid>.jsonl. ProjIQ can index these
session logs alongside your code repo and let search_context retrieve
past conversation chunks by semantic similarity + a five-field filter.
Enable it by adding the directory as an extraRoots[] entry in your
.projiq.json:
{
"include": ["**/*.md", "**/*.ts", "**/*.jsonl"],
"extraRoots": [
{
"path": "~/.claude/projects",
"name": "claude-projects"
}
]
}Then run sync_index once. The JSONL chunker emits one chunk per
content-bearing message (user / assistant / tool_use / tool_result /
queue-operation enqueue) and skips dequeue events and hook errors. Each chunk
carries metadata in the chunk_metadata sqlite table:
| Field | Source | Used by |
|-------|--------|---------|
| sessionId | obj.sessionId (or filename UUID) | sessionId filter |
| timestamp | obj.timestamp ISO 8601 | timeFrom / timeTo filter |
| messageType | obj.type or obj.message.role | messageType filter |
| parentUuid | obj.parentUuid | conversation tree reconstruction |
| workspace | derived from parent directory name | workspace filter |
Example search: find the moment you decided on the API design for a feature called "billing", restricted to assistant messages last week:
{
"task": "billing API design",
"messageType": "assistant",
"timeFrom": "2026-05-12T00:00:00Z",
"timeTo": "2026-05-19T00:00:00Z"
}Filters combine with AND semantics; an empty intersection short-circuits to
zero hits without invoking the embedder. The metadata table is dropped via
sqlite ON DELETE CASCADE when a session log file is removed, so the index
stays consistent with the filesystem.
See docs/COMPATIBILITY.md for platform notes on the ~/.claude/projects/
path layout.
Stack
- Runtime: Node.js (≥ 20), TypeScript, ESM
- Protocol: Model Context Protocol via
@modelcontextprotocol/sdk - Vector store: LanceDB (
@lancedb/lancedb) - Metadata store: SQLite (
better-sqlite3) - Embeddings: Local
bge-m3(1024-dim) via@huggingface/transformers— offline, no data leaves the machine. Also a pure-TypeScript BM25 embedder (PROJIQ_EMBEDDER=bm25) — no model download, sub-second startup, well-suited for small projects (< 500 files) and air-gapped environments. - Code chunker: tree-sitter (TypeScript / JavaScript / Python / HTML / CSS / JSON)
- Dependency extraction: tree-sitter for JS/TS (
import/require()/ dynamicimport(), plusextends/implementstype-refs), Python (import/from … import/ relative, plus class base-class type-refs), HTML (<script src>/<link href>/<img src>/<a href>, internal refs only), and CSS (@import,url()); regex-based for Markdown[text](path)+[[wikilink]]; fast-path JSON parse for known dependency keys (extendsin tsconfig,main/modulein package.json, etc.). External URLs (http(s)://…) are intentionally skipped to avoid graph noise. - Re-ranker: swappable
Rerankerinterface — Anthropic Claude Haiku 4.5 (claude, requiresANTHROPIC_API_KEY), BGE-reranker-base cross-encoder via@huggingface/transformers(local-bge, fully local), ornone(default; pass-through using vector similarity / graph distance). Task analysis (intent / category / keywords) is fused into the Claude re-rank prompt as a single LLM call;local-bgereturns only scores.
Roadmap
- [x] Phase 0 — scaffolding, minimal MCP server
- [x] Phase 1 — vector-search MVP:
sync_index+search_contexttools - [x] Phase 2 — dependency graph expansion (
graphExpansionfield insearch_contextresponse) - [x] Phase 3 — swappable re-ranker (
ClaudeReranker,LocalBgeReranker,NullRerankerfallback) wired intosearch_context, Task Analyzer fused into the rerank prompt, Python chunker + dependency extractor,type-refdep kind,imported_namespersisted,NullRerankerfallback on rerank failure. - [x] Phase 4a — auto-update watcher + Observer foundation: every
search_contextresponse is recorded inquery_sessions/query_session_hits, and file Read events (via PostToolUse hook →.projiq/observations.jsonltail, or therecord_readMCP tool) are persisted inreadswith TTL-based session matching. - [x] Phase 5 — multi-language generalization + audit tools: HTML / CSS / JSON chunkers and dependency extractors, four new audit MCP tools (
run_review,audit_stale,detect_duplicates,check_broken_refs), a pure-TypeScript BM25 embedder (PROJIQ_EMBEDDER=bm25) for small / offline projects, and an automatic small-project BM25 advice log onsync_index(< 500 files). - [ ] Phase 4 — observer / feedback loop (learning / rerank feedback)
Install
npm install @kai-0131/projiqOr run from source:
git clone https://github.com/Kai-0131/projiq
cd projiq
npm install
npm run buildConfigure
Drop a .projiq.json at your repo root to override defaults. A minimal example:
{
"include": [
"**/*.md", "**/*.mdx",
"**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.mjs", "**/*.cjs",
"**/*.py", "**/*.pyi"
],
"chunking": { "maxTokens": 512, "overlapTokens": 100 },
"embedding": { "model": "Xenova/bge-m3", "dimensions": 1024 },
"search": {
"defaultLimit": 20,
"maxLimit": 100,
"graphDepth": 2,
"graphDirection": "forward"
},
"maxFileSizeBytes": 1000000,
"respectGitignore": true
}See src/config/schema.ts for the full schema.
Run as an MCP server
With Claude Code
Add to your .claude/mcp_servers.json (or equivalent for your client):
{
"mcpServers": {
"projiq": {
"command": "node",
"args": ["/absolute/path/to/projiq/dist/index.js"],
"env": {
"PROJIQ_REPO_ROOT": "/absolute/path/to/the/repo/you/want/indexed"
}
}
}
}Environment variables
| Variable | Values | Purpose |
|---|---|---|
| PROJIQ_REPO_ROOT | absolute path | Repo to index (default: process.cwd()). |
| PROJIQ_EMBEDDER | bge (default) / bm25 / mock | bge: real bge-m3 model (accurate, requires ~500 MB–1 GB download on first run). bm25: pure-TypeScript BM25 embedder — no model download, sub-second startup, recommended for small projects (< 500 files) and offline / air-gapped dev. mock: deterministic mock for tests / offline dev. |
| PROJIQ_TOKENIZER | bge (default) / whitespace / charcount | Tokenizer used for chunk-token counting. |
| PROJIQ_MODEL_CACHE | absolute path | Where @huggingface/transformers caches the downloaded model. |
| PROJIQ_RERANKER | claude / local-bge / none (default) | Re-ranker backend. none returns vector + graph results without re-ranking. claude additionally requires ANTHROPIC_API_KEY. |
| PROJIQ_RERANK_MODEL | model id | Overrides the default for the chosen backend: claude-haiku-4-5-20251001 for claude, Xenova/bge-reranker-base for local-bge. |
| ANTHROPIC_API_KEY | API key | Required when PROJIQ_RERANKER=claude. |
| PROJIQ_WATCH | on (default) / off | Enable the in-process file watcher that keeps the index fresh between sync_index calls. off is useful for CI, one-shot indexing, or debugging. |
| PROJIQ_WATCH_DEBOUNCE_MS | positive integer | Coalesces rapid edits to the same file before re-indexing (default 500). |
| PROJIQ_OBSERVE | on (default) / off | Enable the Observer. When on, every search_context response is persisted to query_sessions / query_session_hits, and the server tails .projiq/observations.jsonl for Read events. off disables both sides (the record_read tool becomes a no-op). |
| PROJIQ_OBSERVE_QUERIES | plain (default) / hash / off | How task queries are stored in query_sessions. plain keeps the text and its SHA-256, hash keeps only the hash, off stops session recording entirely (reads are still written without a matched session). |
| PROJIQ_OBSERVE_MATCH_TTL_MINUTES | positive integer | How long a recorded session remains eligible to be matched to a subsequent Read (default 30). Reads that arrive after the TTL expires are stored with matched_session_id = NULL. |
| PROJIQ_QUIET | 1 / true / yes | Suppress ProjIQ's informational notices (currently: the small-project BM25 advice emitted after sync_index when the indexed file count is below 500). Unset / 0 / empty string / other values leave notices enabled. Case-insensitive. |
Project size recommendations
There is no single best setting for every repo. The embedder / reranker combo matters most; the table below is the default ProjIQ suggests. sync_index will print a one-time advice line after the scan when the indexed file count is below 500 and the embedder is bge — suppress it with PROJIQ_QUIET=1.
| Project size | Recommended embedder | Recommended reranker | Rationale |
|---|---|---|---|
| < 500 files (personal sites, prototypes, single-topic notebooks, docs-only repos) | PROJIQ_EMBEDDER=bm25 | PROJIQ_RERANKER=claude (or none offline) | No 500 MB–1 GB bge-m3 download, sub-second startup, identifier / keyword matches carry most of the retrieval signal at this scale. Claude reranker closes the semantic gap without changing embedder. |
| 500 – 10,000 files (typical application repo, small monorepo) | PROJIQ_EMBEDDER=bge (default) | PROJIQ_RERANKER=claude or local-bge | Semantic recall from bge-m3 starts to pay back the model download. claude is strongest for natural-language tasks; local-bge is fully offline. |
| 10,000+ files (large monorepo) | PROJIQ_EMBEDDER=bge | PROJIQ_RERANKER=claude or local-bge, larger graphDepth | Rerank quality becomes the main bottleneck; budget for the extra LLM cost and tune search.graphDepth / search.defaultLimit to keep shortlists crisp. |
| Any size, air-gapped / no API key | PROJIQ_EMBEDDER=bm25 | PROJIQ_RERANKER=none or local-bge | Fully offline retrieval path. audit_stale / detect_duplicates / check_broken_refs do not need the index at all. |
Tools
sync_index
Scan the repository, detect added/changed/deleted files by content hash, and rebuild the vector index for all non-unchanged files.
- Input: none
- Output:
IndexStats— scanned / kept / added / changed / unchanged / deleted counts, total chunks, total tokens, duration, error list - Notes: First run on a new repo has to download
bge-m3(~500 MB–1 GB). Subsequent runs are fast; unchanged files are skipped via SHA-256. WithPROJIQ_WATCH=on(the default) the watcher keeps the index fresh between calls;sync_indexis still the authoritative full scan for bootstrapping, drift repair, and CI.
search_context
Retrieve chunks most similar to a task description, plus files reachable through the dependency graph from the top hits.
- Input:
task(string, required) — natural-language querylimit(number, optional) — max vector hits (default 20, max 100)includeContent(boolean, optional) — include raw chunk text (default false)paths(string[], optional) — restrict search to these repo-relative pathsgraphDepth(number, optional) — BFS depth over the dependency graph (default 2, max 5)graphDirection(optional) —forward(default) follows imports out of seed files;reversefinds files that import seeds;bothwalks bidirectionally
- Output:
query,rerankerMode,taskAnalysis({intent, category, keywords}when the Claude reranker ran, otherwisenull),limit,includeContent,graphDepth,graphDirection,totalHits, and rankedhits[]. Each hit includespath,source(vector/graph/both),rerankScore(0-10), optionalrerankReason,inclusion(above-threshold/min-fill),startLine/endLine,startByte/endByte,symbol,semanticType,tokenCount,distance,similarity=1 − distance,graphDistance,viaKinds(import/md-link/wikilink/type-ref), and (whenincludeContent=true)content. - Notes: Run
sync_indexfirst. Query is embedded with the same model used for chunks. The graph walker usesfile_depsrows populated during indexing (JS/TSimport/require()/ dynamicimport()andextends/implementstype-refs; Pythonimport/from … import/ relative and class base-class type-refs; Markdown[text](path); Obsidian[[wikilink]]). The vector top-N and graph expansion are deduplicated into a file-level candidate list, sent to the configured reranker (PROJIQ_RERANKER=none|claude|local-bge), and filtered by the threshold combo (min = min(3, max),max = max(1, min(limit, 10)),threshold = 6). When theclaudeorlocal-bgereranker throws, the server logs the failure and falls back toNullRerankerso the tool still returns results.
Semantic types
Each hit carries a semanticType label from the chunker:
| Type | Source |
|---|---|
| function | Top-level function, arrow-function, or method declaration |
| class | class declaration (including interfaces/types) |
| variable | Top-level const / let / var / type / interface / enum declarations |
| text-block | Markdown content, or fallback for unparsed regions |
| split | Overflow fragment when a single unit exceeds chunking.maxTokens and is token-split |
record_read
Record that a specific file was read, so it can be linked to the most recent search_context session within the configured TTL. Most Claude Code users will prefer the PostToolUse hook path (see Observation); record_read covers programmatic clients (Cursor, scripts) that can't easily emit a hook.
- Input:
file_path(string, required) — repo-relative or absolute path of the file that was readread_at(string, optional) — ISO 8601 timestamp; defaults to the server's current time
- Output:
{ recorded, readId, matchedSessionId, filePath, readAt, source }.recorded=falsewith all other fieldsnullwhenPROJIQ_OBSERVE=off; otherwiserecorded=true,source="tool", andmatchedSessionIdis the id of the matchingquery_sessionsrow when one exists withinPROJIQ_OBSERVE_MATCH_TTL_MINUTES,nullotherwise. - Notes: The tool writes to the same
readstable as the hook-backed tail, so you should configure one path or the other per client (enabling both will double-record). Errors in session matching never block the insert — an unmatched Read is still stored withmatched_session_id = NULL.
run_review
Apply a Markdown checklist (e.g. a lint / SEO / QA skill file) to a target file or directory. For each bullet / numbered / checkbox item, ProjIQ searches the index inside target_path for relevant evidence, then asks Claude to judge pass / fail / unclear with a short reason and an optional recommended action. Returns per-item verdicts plus a ready-to-paste Markdown report.
- Input:
checklist_path(string, required) — repo-relative or absolute path to the Markdown checklist. Every-/*/+/1.bullet (with or without[ ]/[x]) across all headings becomes one review item.target_path(string, required) — repo-relative or absolute path (file or directory). Searches are restricted to files at-or-under this path.reranker_mode(optional) —none/local-bge/claude. Overrides the search-side reranker for this call; independent of the LLM judge (which is always Claude). Defaults to the env-resolved mode (PROJIQ_RERANKER).max_items(number, optional) — cap on checklist items judged (default50). Items past the cap are dropped andtruncated=trueis returned.
- Output:
items[](each withid,text,section,level,verdict,reason,recommendedAction?,evidence[],candidates,durationMs),summary(pass/fail/unclear/total),truncated,mdReport(Markdown rendering of the verdicts, also surfaced as the tool's text content), andstats(itemsScanned,durationMs). - Notes: Requires
ANTHROPIC_API_KEY— the per-item judge always runs on Claude. Works alongside the small-project BM25 embedder:PROJIQ_EMBEDDER=bm25+PROJIQ_RERANKER=claudeletsrun_reviewoperate without downloadingbge-m3. Every checklist item is a separate LLM call, somax_itemsis the primary cost knob.
audit_stale
List files whose modification time is at least threshold_days days old. Useful for spotting abandoned docs, dead configs, and out-of-date assets during routine housekeeping.
- Input:
threshold_days(number, required, ≥ 0) — minimum file age in whole days. A file is reported whenfloor((now − mtime) / 1 day) >= threshold_days.0reports every scanned file.path(string, optional) — repo-relative or absolute scope. When provided, only files at-or-under this path are reported.
- Output:
stale[](filePath,lastModifiedIso,daysOld),stats(filesScanned,staleCount,durationMs). - Notes: Language-agnostic and index-independent — runs without
sync_indexand withoutbge-m3. Honoursexclude.directories/exclude.secrets/.gitignore, but intentionally ignoresincludeandexclude.files/exclude.binariesso stale lockfiles, images, and binary assets are still surfaced.
detect_duplicates
Find files with byte-for-byte identical content (SHA-256). Groups of 2+ matching files are returned, sorted by size descending.
- Input:
path(string, optional) — repo-relative or absolute scope.min_size_bytes(number, optional, ≥ 0) — files strictly smaller than this are skipped entirely. Defaults to0(include all). Useful for ignoring tiny boilerplate files whose duplication is noise.
- Output:
groups[](sizeBytes,sha256,files[]),stats(filesScanned,filesHashed,duplicateGroups,duplicateFiles,durationMs). - Notes: Language-agnostic and index-independent. Files are first bucketed by size so only size-collision candidates are hashed. Like
audit_stale, honoursexclude.directories/exclude.secrets/.gitignorebut ignoresinclude/exclude.files/exclude.binariesso duplicate images, PDFs, lockfiles etc. are surfaced.
check_broken_refs
Walk the repository, extract every dependency reference, and report those whose target file does not exist. Catches dangling import paths, missing Markdown / wikilink targets, broken <script src> / <link href> / @import / url(), and unresolved type-refs — all the kinds of breakage that only fail at runtime or on publish.
- Input:
path(string, optional) — repo-relative or absolute path. When provided, only broken refs whose source file is inside this path are reported; target resolution still considers the whole project.
- Output:
broken[](srcPath,target,kind∈import/md-link/wikilink/type-ref,startLine),stats(filesScanned,filesWithDeps,totalDeps,resolvedDeps,brokenDeps,durationMs). - Notes: Index-independent — runs without
sync_index. Uses the same dependency extractors assync_index(JS/TS / Python / Markdown / HTML / CSS / JSON), so adding new language support in Phase 5 meanscheck_broken_refscatches strictly more breakage: dangling HTML<script src="./missing.js">, CSS@import './gone.css',tsconfig.json extends "./removed.json", etc.
ping / status
Health and diagnostics. status reports resolved paths, config source, embedder / tokenizer / reranker modes, applied migrations, a watcher block (mode, active, queueDepth, eventsProcessed, lastEventAt) for the auto-update watcher, and an observer block (mode, queriesMode, matchTtlMinutes, tailActive, tailStats, session / read counters) for the Phase 4 foundation.
Automatic incremental updates
When PROJIQ_WATCH=on (the default), the MCP server spawns an in-process chokidar watcher rooted at PROJIQ_REPO_ROOT. File creates, edits, and deletes are debounced per-path (default 500 ms; see PROJIQ_WATCH_DEBOUNCE_MS) and routed through the same single-file upsert / delete path used by sync_index, so the vector index, dependency graph, and SQLite metadata stay consistent while the server is running.
Heavy directories (node_modules/, .git/, .projiq/, dist/, build artefacts) are filtered at the chokidar layer; the remaining events are re-checked against the configured include / exclude patterns and the project's .gitignore chain, so files the full scan would skip are skipped here too. Events are processed serially to avoid SQLite / LanceDB write contention — typical editor saves produce one event and complete in tens of milliseconds.
Set PROJIQ_WATCH=off to disable the watcher (e.g. during one-shot CI indexing); sync_index continues to work either way.
Observation (Phase 4 foundation)
When PROJIQ_OBSERVE=on (the default), ProjIQ captures the raw data needed to evaluate rerank quality later. Nothing is sent off the machine and no feedback loop runs yet — this block only builds up the SQLite tables that Phase 4 proper will learn from.
What gets recorded:
query_sessions/query_session_hits— every response fromsearch_context(task, rerank mode, graph depth, the ranked hits withrerankScore/source/inclusion). Controlled byPROJIQ_OBSERVE_QUERIES:plainstores the raw task text plus its SHA-256 (default),hashkeeps only the hash,offskips session recording entirely.reads— each time a file is read during the session. Reads arriving withinPROJIQ_OBSERVE_MATCH_TTL_MINUTESof a session row are linked viamatched_session_id; later or ProjIQ-external reads are stored unmatched (still useful for analysis).
Two write paths into reads — use whichever matches your client, not both:
PostToolUse hook (Claude Code) — add
scripts/record-read.mjsto.claude/settings.json. The script filters fortool_name === "Read", resolves the file againstCLAUDE_PROJECT_DIR, and appends a line to.projiq/observations.jsonl. The MCP server tails the file (chokidar +.projiq/observations.cursorbyte offset, truncation-safe) and commits each line toreadsthrough the same code path as the tool. The script always exits 0 so it can never block another hook.// .claude/settings.json { "hooks": { "PostToolUse": [ { "matcher": "Read", "hooks": [ { "type": "command", "command": "node /absolute/path/to/projiq/scripts/record-read.mjs" } ] } ] } }record_readMCP tool — for programmatic clients that can't easily register a hook (Cursor, automation scripts). See the tool docs above.
Set PROJIQ_OBSERVE=off to disable the Observer end-to-end: search_context stops writing sessions, the tail is not started, and record_read becomes a recorded=false no-op. query_sessions / query_session_hits / reads remain in the SQLite schema either way (they are part of migration v5).
Workspace layout
Runtime data lives in .projiq/ under the repo root:
.projiq/
├── meta.sqlite # files metadata, query_sessions / query_session_hits / reads
├── index.lance/ # LanceDB vector index
├── observations.jsonl # append-only Read log written by the hook / `record_read`
├── observations.cursor # byte offset the MCP server has tailed up to
└── cache/Add .projiq/ to your .gitignore.
Development
npm run typecheck # tsc --noEmit on sources + tests
npm test # vitest run (unit + e2e via in-memory MCP transport)
npx tsx scripts/dogfood-sync.ts # self-index with MockEmbedder
npx tsx scripts/dogfood-sync.ts --bge # self-index with real bge-m3 (slow first run)Hosted version (interest signup)
ProjIQ is a local CLI / MCP server today. A hosted (browser-based, no install)
version is being evaluated — if you'd use it, please open a Discussion in the
Ideas category and say "interested in hosted version":
Zero responses means we ship the local-only version forever, which is also a perfectly good outcome.
