All notable changes to AiDex will be documented in this file.
Two new dashboard controls, Kotlin and Swift, and three Viewer fixes — one of which could freeze the whole MCP server.
-
Dashboard switches and buttons — the Live tab could only ever set a value (
slider,number). Two control types join them:togglefor state (a two-position switch, value0/1, withunitdoubling as the"ON|OFF"captions) andbuttonfor events. A button's value is a monotonically rising press counter, not a flag, because a source pollsGET /controlat its own pace — with a boolean, every press landing between two polls would be lost. The source compares against the last count it saw and learns both that and how often it was pressed; a backwards jump (wrap at 1e6,/panel/clear, hub restart) means "restart, adopt the value". Presses arrive via the newPOST /control/press, which takes only anid: the hub owns the counter, so two open dashboards both count instead of overwriting each other. Re-announcing a widget (a device rebooting) no longer clobbers a value the user dialed in, and the press count survives it. -
Kotlin and Swift support —
.kt,.ktsand.swiftfiles are indexed via tree-sitter and classified as code across every extension list. Brings the language count to 14. A small share of files using very recent syntax falls back gracefully when the community grammar can't parse them.
- The Viewer's
Debugtab is now calledLive— sitting right next toLogs, the old name said nothing about what set the two apart.Logsis history that scrolls past;Liveis the current state, held in fixed slots that overwrite in place. The heading inside the tab reads Live Dashboard. Nothing about the API changed: the panel endpoints, widget types and thedebugtab id are all untouched, so existing senders and deep links keep working.
-
A single indexed file could freeze the entire MCP server — the per-file stats in
buildTreeused three LEFT JOINs withCOUNT(DISTINCT)in oneGROUP BY. The joins multiply per file before the DISTINCT collapses them again: occurrences × methods × types. One generated header in an indexed project (191.134 occurrences × 3.138 methods × 786 types) came to 471 billion intermediate rows for that one file. better-sqlite3 runs synchronously in native code, so the first tree request from a connecting browser killed the process for good: event loop dead, 98 % CPU for hours, 1,5 GB RSS, MCP calls hanging, dashboard empty — while the ports kept listening, which made it look "on but broken". Now three correlated subqueries per file, each walking one table by itsfile_idindex, returning the same numbers in milliseconds. Both thecodeandallbranches carried the pattern. -
The Viewer's file watcher opened tens of thousands of OS handles — chokidar removed glob support in v4, but the watcher still passed v3 globs (
'**/node_modules/**'and friends). In v5 a string matcher is an exact comparison, so the ignore list silently never matched and every directory below the project root got watched — one non-recursivefs.watch, and therefore one OS handle, each. Measured on this repo (2.520 directories): 19.619 handles and 224 MB RSS, down to 2.375 and 84 MB after the fix; a control run without the Viewer stayed flat at 191.ignoredis now a predicate that returns true for the directory itself (matching only files inside it still lets chokidar descend), and it reuses the exclusion list the indexer already had — a directory not worth indexing is not worth watching. Fixes the spurious tree rebuilds too: everynode_modulesevent used to trigger a full rebuild and broadcast, for data that is not even in the database. -
managed_componentsis now excluded as well — the predicate fix above was necessary but not sufficient on ESP-IDF projects, where that directory is the embedded world'snode_modules. The arithmetic on one such repo is the real finding: 14.858 handles = 1.865 watched directories + 12.713 watched files + base. On Windows chokidar holds a handle per watched file, not merely per directory, and 12.407 of those files sat inmanaged_components. This one entry brings the process back to a few hundred handles. It disappears from global-init scanning too, for the same reason asnode_modules. -
The
▷ Demobutton was missing from an empty dashboard — the Live tab rendered two different toolbars, and the empty-state one carried only the heading, dropping Demo, Pause and Clear. That hid the button exactly when it was needed: it is the way to get your first widgets, but you only saw it once widgets already existed. Both states now share one toolbar, and the empty-state hint points at the button instead of only naming the POST endpoint.
Community contribution. Adds Astro component support.
.astrofile support (#15, thanks @zlegein) — Astro components are now indexed by parsing their TypeScript frontmatter (the code between the---fences) with the existing TSX grammar. The template/markup below the frontmatter is skipped. Line numbers are preserved exactly: template lines are blanked (not removed) before parsing, so every reported method/type position matches the original file. No new dependency — reuses the bundled TypeScript grammar.
Install fix. On Node.js 24, npm install -g aidex-mcp failed for many users with node-gyp errors — they had to install a full C++ toolchain (Visual Studio Build Tools on Windows, the Xcode sysroot on macOS) just to build better-sqlite3 from source. Fixes #13.
better-sqlite3build-from-source on Node 24 —better-sqlite3@^11ships no prebuilt binary for Node 24 (ABInode-v137), soprebuild-installfell through tonode-gyp rebuild, which needs a local C/C++ toolchain most users don't have. Bumped tobetter-sqlite3@^12, which adds Node 24 (and newer) prebuilds — the install now downloads a ready binary on every supported platform, no compiler required. No API changes; the database layer is untouched.
- Minimum Node.js is now 20 (was 18).
better-sqlite3@12dropped end-of-life Node 18 from its build matrix, so that is the new floor.engines,.nvmrc, the runtime version check (src/index.ts), and the README are aligned. Node 18 is itself past EOL.
Feature release. Turns the Debug Dashboard from a one-way display into a two-way control surface: a source can now expose interactive sliders/numbers whose values flow back to it, and both the user and the AI can tune a running program live. Also gives plots sender-controlled Y-axis scaling, separates a gauge LED's colour from its text, stops dashboard flicker, and trims the npm package.
- Interactive controls +
/controlback-channel — two new widget types,sliderandnumber, are editable in the viewer; when the user changes one, the new value flows back to the source. This is the first path on which data travels from AiDex to the program (everything else is source → AiDex). The mechanism is a deliberately dumb, source-agnostic{ id: value }store — it knows nothing about what a value means.- HTTP:
POST /control(set one value, mirrors it onto the card and broadcasts to every viewer) andGET /control(the whole store as a flat{ id: value }map, which the source polls at its own pace to learn the current set-points). Cleared together with their widgets viaPOST /panel/clear— there is no separate control-clear endpoint. - MCP: the AI can drive controls too.
aidex_loggainscontrol_get(read all control values) andcontrol_set(change one — the same set-point the user's dashboard slider drives). So Claude can tune a live program — e.g. a barge-in threshold, a gain, a sample rate — and watch the effect, without touching the source. - New
stepfield (slider/number increment per tick, default 1). New file:src/loghub/control-store.ts. First real consumer: the GeminiPod (ESP32) barge-in tuning; the same API works unchanged from C#, Python, or shell.
- HTTP:
- Sender-controlled plot Y-axis — three new plot fields, all decided by the sender (the renderer only renders):
scale—"linear"(default) or"log". Logarithmic scaling suits high-dynamic signals like audio levels, where quiet speech and a loud peak need to be visible at once. Bounds are lifted to ≥ 1.autoMin— the plot's lower bound follows the data minimum (the ceiling stays fixed atmax), so a noise floor sits at the bottom edge and the full plot height goes to the signal above it.decimals— decimal places in the footer (cur/min/max/avg);0for integers. The recipe that finally made an audio-level plot readable across the whole loudness range:scale:"log"+autoMin:true+ fixedmax+decimals:0.
- Panel-Dashboard user guide (
docs/loghub-panel-dashboard.md) — full walkthrough: stream vs. dashboard, quickstart, the complete HTTP API (display + control back-channel), all six widget types, a field reference checked againstpanel-types.ts, plot-scaling deep-dive, best practices from real (ESP32) use, and an end-to-end audio-dashboard example.
- Gauge LED colour separated from its text — a new
statefield drives a gauge's LED colour ("ok"/"warn"/"error"/…) independently ofvalue, which stays the free display text. Previously the status word was the displayed text, so you were stuck looking at a literal "WARN"/"OK". Now the LED can be red while the card shows whatever text you want. - npm package trimmed —
.npmignorenow ships onlybuild/plus the postinstall hook;docs/, theCHANGELOG, test scripts, and source are excluded from the published tarball. Smaller install, nothing functional removed. (scripts/verify-package.ps1,scripts/check-npm-auth.ps1updated to match.)
- Dashboard no longer flickers on unchanged values —
updateCardValuere-rendered (and flashed) label/progress/gauge cards on every sample, even when the value was identical, causing constant flicker at high update rates. Cards now redraw only when the value actually changes. aidex_global_guidelinelisttoken overflow —listdumped the full text of every guideline (~1,246 lines / 68 KB for 15 guidelines), overflowing the MCP tool result so the content spilled to the swap file instead of the chat, making the overview unusable.listnow renders a compact one-line-per-guideline index (key — short description (updated date)) via a newsummarizeGuideline()helper; usegetwith a key to read the full text. 1,246 → 17 lines.
Feature + stability release. Adds the live Debug Dashboard and closes the WebSocket memory leak the v2.1.0/2.1.1 fixes had missed.
- Debug Dashboard (Panel API) — a live, fixed-slot dashboard alongside the scrolling log stream. External programs
POST /panelwith{ id, type, value, group? }; sending the sameidagain overwrites the value in place instead of scrolling away. Built for high-frequency / repeated values (audio levels, buffer fill, FPS, sensors).- Four widget types: label, progress (with warn/crit threshold colouring), gauge (radial tachometer in the MSI Afterburner / ASUS GPU Tweak style, or a pulsing status LED for string values), and plot (real-time line graph in the HWiNFO style with grid + min/max/avg).
- Endpoints
POST /panel,POST /panels(batch),POST /panel/clear. The server keeps the last state perid, so a freshly-connected or reloaded viewer gets the full dashboard snapshot immediately; cards with no update for ~3 s grey out as "stale". Clear is a full reset — a source reappears only if it re-sends widgets with theirtype. - New Viewer Debug tab (Tokyo-Night cockpit look). Plots are redrawn on a single
requestAnimationFrametick so audio-rate updates stay smooth. All panel broadcasts use the WS backpressure guard. - New files:
src/loghub/panel-types.ts,src/loghub/panel-store.ts.
- Showcase demo (
scripts/demo-dashboard.mjs) — an endless animation of all widget types (audio waveform, GPU gauges drifting through their zones, a signal generator cycling sine → sawtooth → triangle → square, latency spikes). A ▷ Demo button on the Debug tab copies the run command to the clipboard. Launcher:scripts/demo-dashboard.ps1.
- WebSocket backpressure leak in
broadcastTreeUpdate(src/viewer/server.ts): the v2.1.0 fix added abufferedAmountguard tobroadcastLogEntryandbroadcastTaskUpdate, butbroadcastTreeUpdatestill calledclient.send()with no guard. On actively-changing projects (chokidar fires continuously while files churn) it broadcast full code + all trees (~0.5 MB/frame); a slow viewer client let ws's internal send-queue grow without bound → 50+ GB committed. This was the dominant leak the v2.1.0 fix missed. The same guard (drop +wsDropCounts+ rate-limited stderr) is now applied tobroadcastTreeUpdateandbroadcastFocusTab. Proof:scripts/test-tree-backpressure.mjs— unguarded 2559 MB buffered, guarded 1 MB. - Dashboard layout flicker: latency spikes / growing numbers made plot-stats wrap and value rows widen, changing card height and reflowing every card below. Plot-stats are now fixed-height
nowrapcells, value rows arenowrap, and the grid uses a stablegrid-auto-rowsbaseline. - Plot-stats overlap: a
curvalue with its unit (e.g.-19.71 dB) ran into the next stat. Stats are now key+value cells with a fixed gap; long values clip with an ellipsis instead of colliding.
scripts/test-panel-store.mjs(25 checks: validation, all widget types, plot ring + array frames + NaN filter + caps, snapshot, clear) andscripts/test-panel-http.mjs(8 checks: endpoints + error handling) cover the new panel layer. Backpressure proofs:scripts/test-backpressure.mjs,scripts/test-tree-backpressure.mjs.
Bugfix release. C and C++ functions were silently dropped from the index — aidex_signature and aidex_signatures returned types but zero methods for every C/C++ file.
-
C/C++ function names were never extracted (
src/parser/extractor.ts): The generic method extractor only looked for a directidentifierchild of afunction_definitionnode. But the tree-sitter-c/c++ grammar never places the name there — it nests it under a declarator chain:function_definition → (pointer_declarator)* → function_declarator → identifier. As a resultnamestayednulland every function was discarded, so C/C++ files showed types but no methods at all.Fix: A new
findCFunctionName()helper walks thedeclaratorfield chain (through any number ofpointer_declarator/reference_declaratorwrappers) down to thefunction_declaratorand reads the real name. Applied only on thec/cpppath — other languages are untouched.Verified:
loghub_client.cwent from 0 → 11 functions extracted (including pointer-return functions likeuint8_t *slot_at(...)); regression-tested against C#, TypeScript and C++ with no change to their output.Action required: Already-indexed C/C++ projects keep the old (empty) result in their DB because the file hash is unchanged and won't auto-reindex. Force a re-index with
aidex_init(or remove + update the affected files) to pick up the now-extracted functions.
Stability release. Two independent root causes that crashed the embedding pipeline in production — one silently killing the entire MCP server process, the other silently generating impossible memory allocations — are now both permanently prevented.
-
ONNX OOM killed the entire MCP server process: Projects with large generated files (Blazor WASM bundles, large C++ translation units, minified JS) caused the ONNX runtime to request allocations of 67–93 GB of RAM. Because the ONNX model ran inside the MCP server process, the resulting
std::bad_allockilled the server — disconnecting Claude from every tool and requiring a manual/mcpreconnect. Root cause: the embedding model ran directly in the MCP server's Node.js heap with no crash isolation.Fix — Worker process isolation (
src/commands/init.ts+src/embeddings/embed-worker.ts):aidex_init({ embeddings: true })now spawns a dedicated child process viaspawn(process.execPath, [workerPath], { stdio: ['pipe','pipe','pipe'] })for each embedding run. The worker reads the project path from stdin as JSON, runs the full pipeline, and writes the result to stdout. When ONNX OOMs, only the worker dies — the MCP server survives and reports a Warning in theaidex_initresult instead of crashing. A 10-minute timeout kills runaway workers automatically.Verified: projects that previously killed the server (
YouTubeVoiceOver,CiscoWebExTranslator/cpp,UcHome) now complete with a Warning rather than disconnecting Claude. -
ONNX OOM on large source files: Even with worker isolation, files larger than ~50 KB reliably triggered the ONNX
Non-zero status code returned while running Add nodeerror inside the worker, causing the worker to exit with code 3228369023 (WindowsSTATUS_ACCESS_VIOLATION). Root cause: the jina-code model's attention layers allocate O(n²) memory relative to token count — a 63 KB file with deeply nested methods produces a sequence too long for the model's fixed-size buffers.Fix — 25 KB file size limit (
src/embeddings/pipeline.ts): Methods, types, and doc-sections from files larger than 25 KB are silently skipped during embedding. File sizes are resolved once per file viastatSyncand cached for the run. The limit applies incollectCodeWrites(),collectDocsWrites(), andupdateFile()— covering both full re-index and incremental update paths. Files below the limit are embedded normally; files above it are excluded without error or warning.Verified:
TwSudoku(Blazor WASM JS bundles >1 MB + 14 C# files),YouTubeVoiceOver(63 KBTranscriptProcessor.cs), andUcHome(ESP32 SDK managed components) all complete cleanly. A 6-worker parallel stress test across all 252 registered projects completed in 33 minutes with 0 crashes and 0 errors.
src/embeddings/embed-worker.ts: Standalone embedding worker entry point. Reads{ projectPath, force? }from stdin, callscreateRealModule().enable()+.indexProject(), writes{ ok, embedded, skipped, removed, durationMs }(or{ ok: false, error }) to stdout, then exits. Has no IPC channel — crash isolation relies entirely on process separation.
aidex_initwithembeddings: trueno longer runs the embedding model in-process. The spawn-based worker adds ~100 ms overhead per project but eliminates the possibility of an ONNX crash affecting the MCP server.
Bug-fix release. Two issues that surfaced when running aidex_init({ embeddings: true }) in real-world workflows with mixed-age project DBs and concurrent calls.
- Embedder crashed on legacy project DBs with "no such column": Projects whose
index.dbpredated the v1.19a / v1.15 / archive-summary migrations crashedaidex_init({ embeddings: true })withSqliteError: no such column: m.body_text(ortasks.summary/note_history.summary). The embedder opens each project'sindex.dbread-only, so the writeablemigrateLegacySchema()path indb/database.tsnever ran on those DBs — and theSELECTs inembeddings/store.tsreferenced the missing columns directly.openProjectIndexDb()now opens the DB writeable briefly to apply the same idempotent ALTER TABLEs, then reopens read-only. Belt-and-suspenders:readMethods/readMethodsForFile/readAllTasks/readNoteHistorynow checkPRAGMA table_infoand substituteNULLfor missing columns, so even a locked / read-only DB no longer crashes — it just produces embeddings without the optional context fields.migrateLegacySchemaitself was missing thenote_history.summaryALTER and got it added. - Concurrent
indexProjectcalls loaded N copies of the embedding model: When severalaidex_init(orindexProject) calls fired before the embedder was warm — a typical user pattern after global enable — every caller raced past thethis.embedder == nullcheck inRealEmbeddings.getEmbedder()and started its owncreateEmbedder(). Each load pulled the ~7 GB ONNX model into RAM. 14 parallel calls reproducibly produced ~98 GB RSS. Fix: the in-flight load is now cached as aPromise<Embedder>, so concurrent callersawaitthe same load and the model is loaded exactly once. Verified with a 14-parallel stress harness: peak RSS during the run dropped from 12 GB to 92 MB; end-of-run RSS dropped from 98 GB to 12 GB.
- Regression tests for the above (
tests/embedder-fixes.test.js): 12 jest tests covering legacy-schema migration on each affected table, defensive read paths when columns are missing, idempotency ofopenProjectIndexDb, and the Promise-cache mechanism that prevents the N-way embedder load.
Major release — AiDex grew a brain. Semantic search across code, docs, and workspace items via locally-run embeddings (jina-code, 768d). Optional LLM layer for multilingual queries and reranking. New Settings tab in the Viewer. Schema migrated to v1.2 (additive — existing indexes keep working).
19 commits, ~8400 lines. Restore points: tag v1.18.0-pre-bugsweep (clean v1.18.0 working tree) and v2.0-pre-cleanup (mid-branch checkpoint).
-
Method body storage (v1.19a, prerequisite for embeddings):
aidex_initacceptsstore_bodies(orembeddings) flag. When enabled, full method bodies are stored inmethods.body_textfor snippet display and re-embedding. Bodies >8000 chars are truncated to head + tail. Setting persists inmetadatasoaidex_updatecontinues to store bodies on incremental updates. Schema migrated to v1.1 (additive, backward compatible). -
Embeddings module skeleton (v1.19b): New encapsulated module
src/embeddings/with stable public API (getEmbeddings()), lazy loading, and additive schema migration on~/.aidex/global.db. Adds theembeddingstable andembedding_model_id/embedding_dim/embedding_version/last_full_embed_at/files_changed_sincecolumns toprojects. Model registry includesjina-code(default),nomic-text,bge-small. Heavy dependencies (@xenova/transformers,sqlite-vec) are declared asoptionalDependenciesand only loaded whenenable()is called — AiDex still installs and runs without them. -
Code embeddings (v1.19c):
aidex_init({ embeddings: true })andgetEmbeddings().indexProject()produce semantic vectors for every method and type via three-tier chunking (signature + doc-comment + weighted identifier bag with string literals). Vectors live in avec0virtual table per dimension (vec_embeddings_<dim>); metadata in theembeddingstable joins them by rowid. Hash-based skip-on-no-change avoids redundant model calls on re-runs (~500ms for a fully cached re-index). Default modeljina-embeddings-v2-base-codeis downloaded into~/.aidex/modelson first use. Indexing AiDex itself (402 methods + 132 types) takes ~30s on CPU, ~600ms on subsequent runs. -
Workspace embeddings (v1.20): Tasks, task logs, the active session note, and all archived note-history entries are embedded alongside code.
commands/task.tsandcommands/note.tsfire fire-and-forgetonTaskChanged/onNoteChangedhooks that re-embed only the affected items. Deleted tasks and stale anchors are pruned automatically (pruneEmbeddingsByType). Combined index for AiDex now covers 402 methods + 132 types + 144 workspace items (35 tasks, 85 task-logs, 1 active note, 23 history entries). Semantic queries like "release process for npm publishing" return the right tasks; "visualization of progress in the browser" pinpoints thestartProgress/getProgressHTML/stopProgressmethods insrc/viewer/progress.ts. -
Docs embeddings (v1.21): All Markdown / MDX files surfaced by
project_files.type='doc'(README, CHANGELOG, CLAUDE.md, docs/**, plan/release notes, etc.) are split at heading boundaries and embedded asdoc-sections. The chunker keeps code fences intact, sub-splits oversize sections with overlap, and prepends the document title for context — so a single retrieval is "Auth: token rotation", not just "token rotation". Stale sections and removed files are pruned viapruneDocSectionsExcept. AiDex's own index grew to 984 embeddings (534 code + 144 workspace + 306 doc-sections from 10 files). Hybrid retrieval works as designed: a query like "how to write logs from external programs" returns the README's## Log Hubsection first, then thelogmethod insrc/commands/log.ts. -
aidex_searchMCP tool (v1.22): New tool for semantic / exact / hybrid retrieval across embedded code, docs, and workspace items. Three modes:semantic(pure vec0 KNN against the query embedding),exact(identifier match — same asaidex_query), andhybrid(default — fuses both via Reciprocal Rank Fusion with k=60). Filters:scope(current project / all enabled / linked),project_filter(glob over project paths),source_kinds(code / docs / workspace),source_types(method, type, doc-section, task, task-log, note, note-history), andk. Output is a ranked list with file:line, snippet (≤180 chars), distance, and project name when scope is global. The module auto-loads on first search call — no explicitenable()needed for read-only use. -
Viewer Search tab (v1.24): Interactive embedding search inside the project Viewer. New "Search" tab in the detail panel with a debounced text input (350 ms), mode selector (Hybrid / Semantic / Exact), source-kind checkboxes (Code / Docs / Workspace), and a
kslider (5–50). Results stream in via WebSocket (searchEmbeddings→searchResultswith arequestIdto discard stale responses). Hits render as clickable cards with a kind/type badge, distance score, file:line, and snippet — clicking a code/docs hit opens the file in the Overview tab; workspace hits switch to the Tasks tab. State survives tab switches and the panel rebuilds itself if other tabs overwrite the shared#detailcontainer. -
Code chunk display split: The chunker now produces two separate texts —
embeddingText(still uses weighted token repetition for the model) anddisplayText(compactIdentifiers: files (8), Filter (7), path (7), …form for the DB and viewer). Search results stop showing the noisy "files files files files files" pattern. ThemigrateDisplayText()helper rewritessource_textfor all existing code embeddings without re-running the model — ~130 ms for AiDex's 534 vectors. -
Embeddings lifecycle (v1.23): Embeddings now track and refresh themselves automatically as code, docs, and workspace items change.
aidex_updateandaidex_removefire fire-and-forget hooks that re-embed the affected file (or drop it). The pipeline uses content-hash skip-on-no-change so a no-op edit costs nothing. Anchors that disappear (renamed methods, deleted sections) are pruned.aidex_sessionreports an embeddings status block (fresh/drifting/stale) with a human-readable hint — "55 files changed since last full embed, consider re-indexing". Model migration:migrate({ fromModel, toModel, apply })re-embeds every project pinned to the source model after a transactional wipe; dry-run mode reports affected counts without touching anything. The lazy-loaded module stub now auto-promotes itself to the real implementation on hooks for projects that have embeddings enabled — previously the hook silently no-op'd in fresh processes until something explicitly calledenable(). -
Settings tab +
aidex_settingstool: A single Settings tab in the viewer is now the home for all user-facing configuration — embeddings on/off and model choice, LLM provider / endpoint / API key / model, and thellm_send_codeprivacy switch. Live status probes show what's already active (e.g. "OpenAI / gpt-4o-mini from env"). A "Test connection" button verifies the LLM round-trip with latency. API keys persist to~/.aidex/llm.json(chmod 600). The new MCP toolaidex_settings({ path, open: true })opens the viewer and jumps straight to the Settings tab — so the user can say "open my AiDex settings" and the AI just calls it. First session after install/upgrade prepends a one-time welcome banner toaidex_session("🎉 AiDex X.Y — new features available, open Settings to enable") that stops once the user opens Settings. -
LLM layer (v1.25): Optional intelligence layer over
aidex_search. When an Anthropic / OpenAI / OpenRouter API key is configured, or a local Ollama is running, AiDex can: translate non-English queries into English (so "wie speichere ich Logs lokal" finds the right code), expand vague queries into 2-4 concrete subqueries (RRF-merged), and rerank top-N retrieval candidates. Newllmparameter onaidex_search(auto/off/translate/rerank/expand+rerank). Per-project privacy switchllm_send_code(default off): when off, only the user's literal query and metadata (paths, names, anchors) are sent to the LLM — never source bodies, doc sections, or notes. Newaidex_initparameters:llm_endpoint,llm_model,llm_send_code. Credentials discovered in this order: project config → env vars →aidex_global_guidelinekeys →~/.aidex/llm.json→ local Ollama probe. If nothing is found, AiDex stays in pure-embeddings mode. Encapsulated insrc/llm/with a stablegetLlm()proxy and a strictsafety.tschokepoint that defensively asserts no body content leaks whensendCode=false. -
HuggingFace backend: Sixth LLM provider option, alongside Anthropic / OpenAI / OpenRouter / Ollama / Custom. Targets the OpenAI-compatible Router endpoint (
https://router.huggingface.co/v1); auto-detected fromHF_TOKEN(HuggingFace standard) orHUGGINGFACE_API_KEYenv vars. Suggested model dropdown includesLlama-3.1-8B-Instruct,Llama-3.3-70B-Instruct,Qwen2.5-Coder-32B-Instruct,Qwen2.5-72B-Instruct,Mistral-7B-Instruct-v0.3, andDeepSeek-V3— but free typing works for any HF model. 503 cold-start responses surface as an explicit error with the model-loading hint. Endpoint URLs containinghuggingface.coare recognised byinferBackendso per-project overrides and~/.aidex/llm.jsonentries route correctly.
aidex_search: semantic results no longer depend onk: With smallk(e.g.k=5) the KNN candidate pool was onlyk * 3 = 15vectors, while ak=10call pulled30. sqlite-vec's KNN is not strictly stable acrosskvariations, so the user-facing top-5 was sometimes a different set than the top-5 of ak=10call — occasionally returning "No matches" whilek=10returned plausible hits for the same query. Pinned the underlying KNN candidate pool to a floor of 60 (SEMANTIC_FETCH_FLOOR); the JS slice still trims to the user's requestedk. Result:aidex_search k=5is now a proper subset ofk=10for the same query.- Viewer: task description rendering: Task descriptions in the viewer's Tasks tab were piped through
escapeHtml()and rendered as a single wall-of-text — Markdown headings, lists, tables, and code fences collapsed into one unreadable line. Added a small inline Markdown renderer (no dependencies) that handles H1-H6, fenced + inline code, bold/italic/strikethrough, ordered/unordered lists, blockquotes, GFM tables, horizontal rules, and links. Anyone who writes multi-paragraph task descriptions sees them properly formatted now. - Viewer: Settings tab auto-switch via
aidex_settings({ open: true }): The auto-switch never fired becausereadPendingFocusTabandwritePendingFocusTabinsrc/viewer/server.tsusedrequire('better-sqlite3')inside a pure-ESM module ("type": "module"). Node threwReferenceError: require is not defined, the surrounding silenttry/catchreturnednull, and the metadata round-trip via~/.aidex/global.dbwas a no-op. Replaced with the existing top-levelimport Database from 'better-sqlite3'and agetGlobalDbPath()helper usingprocess.env.USERPROFILE. The same anti-pattern inwriteLlmConfigFilewas fixed alongside.
- HCL/Terraform support (#9): Indexes
.tf,.tfvars, and.hclfiles — now 12 supported languages- Blocks (
resource,module,variable,output,data,locals,provider, ...) → types with dotted names (e.g.resource.aws_instance.web) - Function calls → methods
- Attributes → properties
- Block labels (incl. keyword-like names:
default,root,type,data) indexed as searchable items - Terraform projects auto-discovered via
*.tfand.terraform.lock.hclmarkers inglobal_init .terraform/excluded from indexing- Uses
@tree-sitter-grammars/tree-sitter-hclgrammar
- Blocks (
- tree-sitter upgrade (#8): Bumped
tree-sitterfrom 0.21 to 0.25 and all 10 grammar packages to latest — enables newer grammars requiring tree-sitter ^0.25.0 - Parser refactor: Centralized grammar mapping in
GRAMMAR_MAP— adding a new language is now a one-line change - File watcher: Viewer's live re-indexing watcher now uses
parser.isSupported()instead of a hardcoded extension regex — automatically tracks every supported language
- Node.js 22+ recommended: tree-sitter 0.25 requires native compilation; Node 24 not yet compatible.
.node-versionpins to 22. tree-sitter-c-sharppinned to ^0.23.1: 0.23.5+ switched to ESM-only with top-level await, breaking CJS imports.
- Node.js 18+ enforcement: Server now exits immediately with an OS-specific install hint when run on Node <18, instead of crashing later on native modules
- Prerequisites section: Added to README with install commands for macOS (brew/nvm), Linux (nvm), and Windows
- .nvmrc: Added so
nvm usepicks the correct version automatically
- Task Scheduler: Tasks can now have due dates (
due), repeat intervals (interval), actions (task_action), and auto-execute flag (auto_go)- Due dates: Relative (
"3d","1w") or ISO date ("2026-04-10") - Recurring tasks: Automatically advance due date by interval after each trigger
- One-shot tasks: Due date cleared after trigger
- Cross-project: Overdue tasks reported at every
aidex_sessioncall — even from other projects - Global mirror:
scheduled_taskstable in~/.aidex/global.dbfor fast cross-project lookups - Auto-migration: Existing databases get new columns automatically
- Due dates: Relative (
- Test suite: First tests for AiDex — 26 tests covering scheduler logic, task CRUD, global sync
- Monorepo support: Removed
**/packages/**fromDEFAULT_EXCLUDE— was incorrectly blocking indexing of JS/TS monorepo workspaces (pnpm, npm workspaces, etc.) (#4)
- Auto-setup instructions overhaul: Complete rewrite of the CLAUDE.md/GEMINI.md block installed by
aidex setup- Added Log Hub section with full usage guide (init, query, HTTP API, Viewer integration)
- Added missing query parameters:
modified_before,file_filter,type_filter - Added
show_progressforglobal_init - Added task
summaryfield documentation - Added note
summaryfield documentation - Updated tool count from 28 → 30
- Updated Viewer description to include Logs tab
- Added "Debug my app" to Question → Right Tool table
- README: Added
aidex_logto Available Tools table, updated tool counts to 30 - Projekt-CLAUDE.md: Updated tool count to 30
- Log Hub consume pattern: New
consumeparameter onaidex_logquery — returned entries are removed from the buffer, ideal for polling without duplicates - Viewer: Clear Logs button: "Clear" button in the Logs tab to reset the log display
- Viewer: WebSocket auto-reconnect: Viewer automatically reconnects when WebSocket connection drops (2s retry)
- LogHub Developer Guide: Added comprehensive integration guide to project CLAUDE.md with code examples for C#, Python, JavaScript, C/C++, PowerShell
- Log entry
data: nulldisplay: Entries withdata: nullno longer show "null" text in Viewer and MCP output
- Log Hub (
aidex_log): Universal logging system — any program (C#, Python, Node, etc.) sends logs via HTTP POST to AiDex, queryable by the LLM via MCP tool. Zero-cost when not used — no server, no buffer, no resources untilinitis called.- HTTP Server on port 3335 (configurable):
POST /log(single),POST /logs(batch),GET /health - Ring Buffer: In-memory circular buffer (default 10,000 entries), oldest entries overwritten
- Query: Filter by
since,level,source,contains,limit— newest first - Write: LLM can inject entries (source: "claude")
- Persistence: Optional SQLite storage with 7-day auto-cleanup
- Viewer integration: New "Logs" tab with WebSocket live-stream, level/source/text filters, auto-scroll
- HTTP Server on port 3335 (configurable):
- Task summaries: Tasks now support a
summaryfield — a one-sentence table-of-contents entry (~150 chars) that the AI writes on create/update.aidex_tasksshows summaries inline so you can scan the backlog without reading full details. - Note history summaries: Archived notes now get an optional
summaryfield. When a note is overwritten or cleared, a summary can be provided for the archive.aidex_notewithhistory: trueshows summaries (with fallback to truncated preview for older notes). Search also matches summaries. - Auto-migration: Existing databases are automatically upgraded with
ALTER TABLE ADD COLUMN summary— no manual migration needed. - Viewer integration: Task summaries shown in italic between title and description in the browser viewer.
aidex_global_guideline: New tool (#28) — persistent key-value store in~/.aidex/global.dbfor AI guidelines and coding conventions. Store named instructions like "review" → review checklist, "release-prep" → release steps. Actions:set,get,list,delete. Works without priorglobal_init.- Viewer file size limit:
getFileContent()now refuses files larger than 1 MB — prevents browser from freezing on large binary or generated files
- Command injection in Linux screenshot tools: All
execSynccalls with shell string interpolation replaced withexecFileSyncusing argument arrays — window titles, file paths and IDs are no longer injectable via shell - Global query cache grows unbounded: Cache entries are now evicted on write when they exceed the 5-minute TTL — prevents memory leak in long-running sessions
- Viewer race condition on file change:
pendingChangesset is now snapshotted and cleared before processing — new events arriving during async re-indexing are no longer silently dropped - Viewer buildTree() N+1 queries: Correlated subqueries for
methodsandtypescounts replaced withLEFT JOIN— single query instead of one subquery per file - WebSocket unknown message type: Viewer now sends an error response for unrecognized message types instead of silently ignoring them
- Viewer taskId not validated:
updateTaskStatusnow checksNumber.isInteger(taskId)before processing - Viewer mode not whitelisted:
getTreemessage mode is now constrained to'code' | 'all'— arbitrary values no longer passed through getProjects()SQL injection via tag/namePattern:escapeLikeTerm()now applied to both filter parameters inglobal-database.ts- Silent fails in viewer and global DB:
catch {}blocks now log errors viaconsole.error - Git status refresh on every file event: Added 5-second minimum interval between git status refreshes — reduces git subprocess spam during rapid file saves
- Global query cache not invalidated after init/update:
aidex_initandaidex_updatenow callinvalidateGlobalCache()so global searches immediately see fresh data
screenshot/shared.ts: New module with centralizedhasTool()andrunPowerShell()— both useexecFileSync(no shell). Imported byplatform-win32.ts,platform-linux.ts, andpost-process.ts— eliminates duplicate implementationsnormalizePath(): Private duplicates inglobal-database.tsandgit-status.tsremoved — both now import fromcommands/shared.tsescapeLikeTerm(): Exported fromcommands/shared.tsand used consistently across all LIKE queries- macOS sips output parsing: Replaced shell pipe (
| tail -1 | awk) with regex on directsipsoutput
- Screenshot optimization: New
scale(0.1-1.0) andcolors(2/4/16/256) parameters foraidex_screenshot- Reduces file size up to 95% (e.g., 108 KB → 5 KB with
scale: 0.5, colors: 2) - Black & white mode (
colors: 2) ideal for text-only screenshots — saves thousands of tokens - Cross-platform post-processing: Windows (System.Drawing), macOS (sips + ImageMagick), Linux (ImageMagick)
- Size reporting: shows original → optimized size and percentage saved
- Reduces file size up to 95% (e.g., 108 KB → 5 KB with
- LLM auto-optimization strategy: Tool description guides AI assistants to start with aggressive settings, retry if unreadable, and remember working settings per app during the session
- Update notifications:
aidex_sessionnow shows "What's New" when AiDex was updated since the last session- Compares installed version with
last_seen_versionstored in project DB - Shows highlights + changelog link, only once per version update
- Compares installed version with
- Auto-setup on install:
npm install -g aidex-mcpnow automatically registers AiDex with all detected AI clients and installs AI instructions (CLAUDE.md,GEMINI.md)- Opt-out via
AIDEX_NO_SETUP=1orCIenvironment variable - Graceful fallback: shows manual hint if auto-setup fails
- Opt-out via
- Comprehensive AI instructions: The CLAUDE.md block installed by
aidex setupnow covers all 27 tools- Decision tree: "Do I want to search code? → .aidex/ exists? → STOP, use AiDex"
- Explicit ❌/✅ examples (never Grep when .aidex exists)
- Search modes explained (exact/contains/starts_with)
- Session notes, task backlog, global search, screenshots — all with examples
- Duplicate detection:
aidex setupskips CLAUDE.md/GEMINI.md if manual AiDex instructions already exist (avoids double entries)
- Refactored commands: Extracted shared utilities into
shared.tsandglobal-shared.tsvalidateIndex(),noIndexError(),withDatabase(),withProjectDb()— ~200 lines of boilerplate eliminated across 10 command fileswithGlobalDb(),EMPTY_TOTALS— 4 global commands refactored
- README: Expanded "Make your AI use it" section with full best-practice instruction block
- DB transactions:
clearFileData()andbulkInsert*()now wrapped in transactions - N+1 query: Batch
getOccurrencesByItems()replaces per-item queries - Stats query:
getStats()reduced from 7 queries to 1 - SQL injection:
global-signatures.ts—t.kind = '${kind}'→ parameterized query - Session: Eliminated duplicate
getMetadatacalls - Tasks: Added null-check for
tableInforesult
- Global Search: Search across ALL indexed projects at once — 5 new tools
aidex_global_init— Scan directory tree, register indexed projects in~/.aidex/global.db, detect unindexed projects by project markers (.csproj,package.json,Cargo.toml, etc.)aidex_global_status— List all registered projects with stats, sortable by name/size/recentaidex_global_query— Cross-project term search (exact/contains/starts_with) with in-memory session caching (5-min TTL)aidex_global_signatures— Search methods/types by name across all projects, filterable by kindaidex_global_refresh— Update stats and remove stale projects- Uses SQLite
ATTACH DATABASEfor zero-copy queries — each project DB remains the single source of truth excludeparameter onglobal_initto skip external repos (e.g.,["llama.cpp"])- Auto-updates global registry after
aidex_init/aidex_update
- Bulk Indexing:
global_initcan auto-index all unindexed projects in one callindex_unindexed: true— Auto-index projects with ≤500 code files- Large projects (>500 files) are listed separately for user decision
- File count estimation uses code-only extensions (matches what
init()actually processes)
- Progress UI: Browser-based progress display for bulk indexing
show_progress: true— Openshttp://localhost:3334with live progress bar- Server-Sent Events (SSE) for real-time updates
- Shows per-project status (indexing/done/error), progress bar, scrolling log
- Dark theme, auto-closes after completion
- Project deduplication: Parent projects that contain sub-projects are automatically removed
- e.g.,
AudioGrabber/is skipped whenAudioGrabber/AudioGrabber/andAudioGrabber/AudioGrabber2/exist - Existing duplicates in global DB are cleaned up on next
global_initrun - Reduced test index from 215 to 167 projects (48 parent-duplicates removed)
- e.g.,
- Extended excludes: Better handling of embedded runtimes and external code
init.ts: Added**/site-packages/**,**/Lib/**,**/fdk-aac/**to DEFAULT_EXCLUDEglobal-init.ts: Added Python venvs, embedded Python runtimes (Python310-313),.cargo,packages,fdk-aacto DEFAULT_EXCLUDED_DIRS
- npm package: Exclude token files and
futureWork.mdfrom published package - gitignore negation patterns: Filter out
!negation patterns in.gitignoreto prevent excluding all files- Negation patterns (e.g.,
!.vscode/settings.json) were passed to minimatch, which interpreted!as "NOT this pattern" — matching ALL files - This caused the entire index to be purged after initialization in projects with negation patterns (common in monorepos)
- Negation patterns (e.g.,
- Note History: Archived notes are now searchable across sessions
- Old notes are automatically archived when overwritten or cleared
history: trueparameter to browse archived notes (newest first)search: "term"parameter to search note history (case-insensitive)limitparameter to control how many history entries are returned (default: 20)
- Rect Screenshot Mode: New
mode: "rect"for coordinate-based screen capture- Specify exact
x,y,width,heightin pixels - Useful with accessibility bounds (e.g., from WinfoMCP
get_element_details)
- Specify exact
- Region screenshot flicker on Windows: Fixed visual flicker during interactive region selection
- Cross-Platform Screenshots: New
aidex_screenshottool for capturing screenshots directly from AI assistants- 4 capture modes:
fullscreen,active_window,window(by title),region(interactive selection) - Cross-platform: Windows (PowerShell + .NET), macOS (screencapture), Linux (maim/scrot)
- Multi-monitor support (select monitor by index)
- Delay parameter (wait N seconds before capture)
- Default: Saves to temp directory with fixed filename (overwrites for quick iteration)
- Custom filename and save path supported
- Returns file path so AI can immediately
Readthe image - No project index required - standalone utility
- 4 capture modes:
- Window Listing: New
aidex_windowstool to list all open windows- Shows title, PID, and process name
- Optional substring filter (case-insensitive)
- Helper for
aidex_screenshotmode="window"
- New directory module:
src/commands/screenshot/with platform-specific implementations - Windows: PowerShell scripts written to temp .ps1 files (avoids quoting issues with inline C#)
- macOS: Uses native
screencapturecommand (interactive selection built-in) - Linux: Uses
maim(preferred) withscrotfallback;xdotool/wmctrlfor window operations - Synchronous delay via
Atomics.wait(Node >= 18)
- Cancelled status for tasks:
backlog → active → done | cancelled- Cancelled tasks preserved as documentation (not deleted)
- Viewer: collapsible ❌ Cancelled section with strikethrough styling
aidex_updatenow respects exclude patterns: Files inbuild/,node_modules/,.gitignorepatterns are rejected- Previously the viewer's file watcher could re-index excluded files via
aidex_update
- Previously the viewer's file watcher could re-index excluded files via
- Auto-migration: existing
taskstable CHECK constraint updated to includecancelled - Exported
DEFAULT_EXCLUDEandreadGitignorefrominit.tsfor reuse
- Task Backlog: Built-in project task management persisted in AiDex database
aidex_task- Create, read, update, delete tasks with priority, tags, and descriptionsaidex_tasks- List and filter tasks by status, priority, or tag- Auto-logging: Status changes and task creation are automatically recorded in task history
- Manual log entries: Add notes to any task with the
logaction - Priorities: high (🔴), medium (🟡), low (⚪)
- Statuses: backlog → active → done
- Sort order support for custom ordering within same priority
- Viewer Tasks Tab: Interactive task management in the browser viewer
- Priority-colored task list grouped by status
- Done toggle directly from the viewer
- Tag display
- New database tables:
tasksandtask_logwith auto-migration - Tasks survive between sessions (persisted in SQLite)
- Gemini CLI support:
aidex setupnow detects and registers AiDex with Gemini CLI (~/.gemini/settings.json) - VS Code Copilot support:
aidex setupnow detects and registers AiDex with VS Code (mcp.jsonwith"servers"key and"type": "stdio")
- JSON client config is now flexible: supports custom server key (
serversKey) and extra fields (extraFields) per client - Updated README with Gemini CLI and VS Code Copilot config examples
- MCP Server version: Now reads version dynamically from package.json (was hardcoded to 1.3.0)
aidex setupfor local installs: Detects ifaidexis globally available; falls back tonode /full/path/index.jswhen not installed globally
- Auto CLAUDE.md instructions:
aidex setupnow installs AI instructions in~/.claude/CLAUDE.md- Tells Claude to auto-run
aidex_initwhen no.aidex/exists - Provides tool usage guide (prefer AiDex over Grep/Glob)
aidex unsetupcleanly removes the instructions block
- Tells Claude to auto-run
- Idempotent setup: Re-running
aidex setupupdates existing config without errors
aidex setupfor Claude Code: Usesclaude mcp add --scope userinstead of editing settings.json directly- Claude Desktop, Cursor, Windsurf still use JSON config editing
aidex setup: Now creates config file if client directory exists but config is missing (e.g. fresh Claude Code install)
aidex setup: Auto-register AiDex as MCP server in all detected AI clients- Supports: Claude Code, Claude Desktop, Cursor, Windsurf
- Cross-platform: Windows, macOS, Linux
aidex unsetup: Remove AiDex registration from all clients- Postinstall hint: Shows
Run "aidex setup"after npm install
- npm package: Published as
aidex-mcpon npm (npm install -g aidex-mcp) - Dual CLI commands: Both
aidexandaidex-mcpwork as command names - npm-publish.bat: Script for easy npm publishing
- README updated with npm install instructions
- Git Status for Subfolder Projects: Viewer now correctly shows git status for projects that are subdirectories of a git repo (e.g., a library inside a monorepo)
isGitRepo()now usessimpleGit().checkIsRepo()instead of checking for.gitdirectory — traverses parent dirs- New
toProjectRelative()helper maps git-root-relative paths to project-relative paths - Files outside the project subfolder are properly filtered out
- Renamed from CodeGraph to AiDex: Package name, MCP server name, and all internal references updated
- MCP prefix changes from
mcp__codegraph__tomcp__aidex__(requires config update) - Index directory changed from
.codegraph/to.aidex/ - Batch scripts renamed:
codegraph-scan.bat→aidex-scan.bat,codegraph-init-all.bat→aidex-init-all.bat - Old
.codegraph/directories can be safely deleted
- MCP prefix changes from
- Automatic Cleanup:
aidex_initnow removes files that became excluded (e.g., build outputs)- Reports
filesRemovedcount in result - Uses minimatch for proper glob pattern matching
- Reports
- Git Status in Viewer: File tree now shows git status with cat icons
- 🟢 Pushed (committed and up-to-date)
- 🟡 Modified (uncommitted changes)
- 🔵 Staged (added to index)
- ⚪ Untracked (new files)
- aidex-init-all.bat: New batch script to recursively index all git projects in a directory tree
- Added minimatch dependency for exclude pattern handling
- Updated all documentation (README, CLAUDE.md, MCP-API-REFERENCE) with correct MCP prefix info
- Interactive Viewer: New
aidex_viewertool opens a browser-based project explorer- Interactive file tree (click to expand directories)
- Click files to view signatures (types, methods)
- Tabs: Code files / All files, Overview / Source code
- Live reload with chokidar file watcher
- WebSocket for real-time updates
- Syntax highlighting with highlight.js
- Runs on
http://localhost:3333
- Recent Files Filter: New
modified_sinceparameter foraidex_files- Find files changed in current session:
modified_since: "30m" - Supports relative time (
2h,1d,1w) and ISO dates
- Find files changed in current session:
- Viewer auto-reindexes changed files before refreshing tree
- Server version now correctly reports 1.3.0
- Session Notes: New
aidex_notetool to persist reminders between sessions- Write, append, read, and clear notes
- Stored in SQLite database (survives restarts)
- Use cases: handover notes, test reminders, context for next session
- Session Tracking: New
aidex_sessiontool for automatic session management- Detects new sessions (>5 min since last activity)
- Records session start/end times
- Detects files modified externally (outside sessions)
- Auto-reindexes changed files on session start
- Returns session note if one exists
- Database schema: Added
metadatatable for key-value storage (session times, notes)
- Time-based Filtering: New
modified_sinceandmodified_beforeparameters foraidex_query- Relative time:
30m,2h,1d,1w - ISO dates:
2026-01-27or2026-01-27T14:30:00 - Track line-level changes across updates
- Relative time:
- Project Structure: New
aidex_filestool to query all project files- File types:
code,config,doc,asset,test,other,dir - Glob pattern filtering
- Statistics by file type
- File types:
aidex_initnow indexes complete project structure (all files, not just code)aidex_updatepreserves modification timestamps for unchanged lines (hash-based diff)- Path normalization to forward slashes across all commands
- New
project_filestable in database schema - New
line_hashandmodifiedcolumns inlinestable - Hash-based change detection for accurate timestamps
- 11 Language Support: C#, TypeScript, JavaScript, Rust, Python, C, C++, Java, Go, PHP, Ruby
- Core Tools:
aidex_init- Index a projectaidex_query- Search terms (exact/contains/starts_with)aidex_signature- Get file signatures (methods, types)aidex_signatures- Batch signatures with glob patternsaidex_update- Re-index single filesaidex_remove- Remove files from indexaidex_summary- Project overview with auto-detected entry pointsaidex_tree- File tree with statisticsaidex_describe- Add documentation to summaryaidex_status- Index statistics
- Cross-Project Support:
aidex_link- Link dependency projectsaidex_unlink- Remove linked projectsaidex_links- List all linked projects
- Discovery:
aidex_scan- Find all indexed projects in directory tree- CLI commands:
scan,init
- Technical:
- Tree-sitter parsing for accurate identifier extraction
- SQLite with WAL mode for fast, reliable storage
- Keyword filtering per language (excludes language keywords from index)
- 1MB parser buffer for large files
- MCP Server protocol implementation
- MIT License
- Comprehensive documentation