5.6 KiB
SQL Server qAI
Paste a SQL Server ShowPlan XML (or drop a .sqlplan), get a report with several ranked remediation options and
rewritten queries. Two models, one job: a generative analyst model proposes the options and writes the report, and
Jev (TypeSafe System One) scores every option and decides which one to run first. Jev never writes SQL; it returns
typed scores, a choice, and confidences. The report renders as OpenUI elements, not a markdown blob.
Pipeline
lib/digest.tsparses the XML in code: top operators, missing indexes, warnings, waits, estimate-vs-actual row errors, tables, statement text, subtree cost, parallelism. Raw XML never reaches a model.- The analyst model receives the digest JSON and proposes 2–4 genuinely different options (index, rewrite, statistics, parallelism, schema), each with ordered action steps and an optional rewrite.
- Jev answers four questions per option (
bottleneck_fit,semantic_safety,easescore;root_causenoul) and one cross-option call (first_to_runchoice,anything_worth_runningnoul). lib/jev.tscombines them with visible weights (0.40 / 0.30 / 0.15 / 0.15 — four numbers in that file, the only tuning knob) and prints a verdict. Jev's pick leads only when its confidence clears 0.5; otherwise the report says the order came from composite scoring.- The analyst streams the final report as OpenUI Lang;
<Renderer>paints it as it arrives.
Getting the XML: SSMS → Ctrl+M (include actual execution plan) → run the query → right-click the Execution Plan tab
→ Save As .sqlplan. No SSMS: prefix the batch with SET STATISTICS XML ON; and copy the XML result cell.
Run it
This repo runs as a plain Node process. Containerization is written (Dockerfile, Task 12 of the plan) but is not
required for local work — npm run dev is the normal path on a small machine.
npm install
cp .env.example .env.local # then fill in APP_SECRET and DATA_DIR
npm run dev # http://localhost:3000
.env.local:
| key | why |
|---|---|
APP_SECRET |
≥16 chars. Encrypts API keys at rest (AES-256-GCM). Changing it makes stored keys unreadable. |
DATA_DIR |
where qai.db (SQLite, node:sqlite) lives. Default ./data. Docker uses /data. |
Model keys are not config. Each user types them in the app (first-run guide or /settings), so a second user
brings their own endpoint and their own spend. Blank key field = keep the stored key.
- Analyst: any OpenAI-compatible base URL + key + model, plus optional extra JSON params.
A local endpoint such as
http://192.168.88.131:8888/v1works; use keyEMPTYwhen the server has no auth. For a reasoning model (Qwen3 on vLLM) put{"chat_template_kwargs":{"enable_thinking":false}}in Extra params: with thinking on, the model spends the whole context budget reasoning and the report arrives empty. Both model calls default tomax_tokens: 2048; the same field overrides it. - Jev: a TypeSafe API key from
console.typesafe.ai/keys, model usuallyjev-latest.
Tests
npm test # node --test on tests/*.test.ts, no test framework installed
npm run build # production build + type check
npm run e2e # Playwright: signup, settings, real analyst + real Jev run, copy, print, history, 4 widths
The unit suites run offline: the analyst and Jev are exercised through injected fetch, so no key and no network.
npm run e2e is the opposite — it drives the real app against the real models, and reads JEV_API_KEY from
.env.local. Expect it to be slow; that is the models, not the harness.
tests/digest.test.ts skips unless a genuine plan exists at fixtures/real.sqlplan. Drop one there from SSMS and the
skip becomes the real check on ShowPlan attribute spellings.
Git remote (Gitea)
Origin points at the self-hosted Gitea:
git remote -v # http://192.168.88.39:3000/ahmed/sql-server-query-ai.git
git push origin feat/sql-server-qai
The remote URL deliberately contains no password. Git prompts for the username (ahmed) and password the first
time, or you can keep them out of prompts:
git config credential.helper osxkeychain # remember them locally
git push origin feat/sql-server-qai
Never commit the password into .git/config, a remote URL, or this README — a stored credential in the repo is
readable by whoever you hand the repo to. If you want it non-interactive in CI, create a Gitea token
(Settings → Applications) and use http://192.168.88.39:3000/ahmed/sql-server-query-ai.git with the token as password.
Layout
lib/digest.ts XML → bounded digest (the only shape models see)
lib/analyst.ts propose 2–4 options; stream the final OpenUI report
lib/jev.ts questions, weights, ranking, verdict, fallback
lib/report-ui.tsx OpenUI component library — the same module builds the prompt and renders the answer
lib/db.ts node:sqlite: users, sessions, settings, analyses
lib/vault.ts AES-256-GCM seal/open/mask for credentials
lib/auth.ts scrypt passwords, hashed session tokens (DB stores the SHA-256, never the token)
app/api/analyze SSE pipeline: digesting → proposing → judging → writing
tests/ offline unit suites tests-e2e/ Playwright
docs/superpowers/ spec + the 13-task implementation plan this was built from
Design: dark terminal console, JetBrains Mono for data, IBM Plex Sans for prose, one phosphor accent. Print stylesheet flips to paper white and drops the rail, buttons and chrome — that is the Export to PDF path.