Bespoke AI
AI autocomplete for prose and code in VS Code — powered by your Claude subscription or API key
Actively developed. Completion quality varies by model and context — see How quality is measured and Known issues. Open an issue if something looks wrong.
💻 macOS, Linux, and Windows — Also works in VSCodium (Open VSX).
🖊️ Writing, not just code — Inline completions for prompts, journals, notes, docs, and code. Matches your voice and style in prose; language-aware in code.
🔑 Subscription or API key — Use your Claude subscription through Claude Code (no per-token costs), or bring your own API key from xAI, OpenAI, Anthropic, Google, OpenRouter, or Ollama.
✨ One-click commit messages — Hit the sparkle button in Source Control to generate a commit message from your staged diffs.
✏️ Suggest edits — One-command typo, grammar, and bug fixes for visible text.
🔧 Context menu — Right-click to Explain, Fix, or Do custom actions on selected text. Opens an agent session in a terminal — Claude Code by default, or opencode (works with local models, any backend).
Screenshots
Writing — autocompleting a prompt to Claude:
Code — filling in a function body:
Commit messages — the sparkle button in Source Control:
🚀 Getting Started
- Install from the VS Code Marketplace, or in VSCodium from Open VSX (search for "Bespoke AI" in the Extensions view either way)
- On first launch, the extension detects whether you have the Claude Code CLI — if not, it offers to help you set it up or switch to API key mode
- Start typing — completions appear as gray suggestion text after a ~2-second pause plus the model's response time (typically another 2–3 seconds on the Claude Code backend)
That's it. For API mode, set bespokeAI.backend to api and choose a model from the status bar menu. Details for each path below.
Claude subscription — no per-token costs, uses your existing paid Claude plan (Pro, Max, Team, or Enterprise)
- Install Claude Code:
curl -fsSL https://claude.ai/install.sh | bash (macOS/Linux) or npm install -g @anthropic-ai/claude-code (any platform)
- Run
claude in your terminal and follow the login prompts
- Reload VS Code — the extension detects the CLI automatically
The default model is Sonnet — best balance of quality and speed. Switch models anytime via the status bar menu.
API key — pay per token with xAI, OpenAI, Anthropic, Google, OpenRouter, or Ollama
- Choose "Use an API key instead" when prompted (or set
bespokeAI.backend to api in settings)
- Run Bespoke AI: Enter API Key from the Command Palette (
Ctrl/Cmd+Shift+P) to store your key securely
The default model is Grok 4.3 by xAI, with reasoning turned off for speed. Change models anytime via the status bar menu. See Available Models for the full list.
Triggering Completions
Completions appear automatically after a ~2-second pause (the relaxed preset). Press Alt+Enter to trigger one instantly at any time.
If Alt+Enter doesn't work, another keybinding is likely intercepting it. Open Keyboard Shortcuts (Ctrl/Cmd+K Ctrl/Cmd+S), search for alt+enter, and rebind any conflicts — Inline Chat is the most common one.
Change trigger behavior via the status bar menu: relaxed (~2s), eager (~800ms), or on-demand (Alt+Enter only).
Modes
| Mode |
Activates for |
Strategy |
| Writing |
markdown, plaintext, latex, restructuredtext, and others |
Continuation-style. Matches voice, style, and format. |
| Code |
All recognized programming languages |
Uses code before and after your cursor, language-aware. |
| Auto (default) |
— |
Selects Writing or Code based on file type. |
Override via settings or the status bar menu.
💡 Why This Exists
I tried every open-source AI autocomplete extension I could find. Most handled code fine but fell apart with prose — breaking paragraphs mid-thought, injecting code syntax into journal entries, producing gibberish outside of source files. Nothing came close to Copilot for non-code text.
So I built my own. And since I was already paying for a Claude subscription, I realized I could wire it up to use Claude Code instead of raw API calls — getting frontier model completions (Haiku, Sonnet, even Opus) at no additional per-request cost. Built on the Claude Agent SDK, it took extensive prompt engineering.
The Claude Code backend uses your existing paid Claude plan (Pro, Max, Team, or Enterprise). Heavy use may be subject to Anthropic's rate limits. The API backend uses standard per-token pricing from your chosen provider.
🧪 How quality is measured
Autocomplete fails quietly — you just dismiss bad ghost text — so quality is measured offline against ~105 scenarios modelled on real use: journal entries, prompts to Claude, mid-document edits, gaps between paragraphs, and code in several languages. Each run generates completions through the production prompt and extraction code, runs seven deterministic checks, and has an LLM judge score what the user would actually see. Details and all numbers: evals/.
What this has caught so far:
- Reading outputs beats trusting the judge. An error analysis of the last full run (by an AI agent: every failed output, a spot check of passes, and a mechanical whitespace check over all 99) found the judge had passed 15 completions that glued two words together (
behind + inadequate → behindinadequate) and had given opposite verdicts to identical completions. Those failures are now caught by deterministic checks, and the judge is being validated against human labels before its pass rates are treated as more than relative.
- A prompt fix for the missing space. Glued words at a word boundary: CLI Sonnet 59% → 0%, xAI Grok 90% → 0% (targeted test, 2–3 samples per scenario); in the full suite, missing-space failures went from 27 to 0 and 36 to 1. On the scenarios the fix does not target, the judge's pass rate was mixed: Sonnet slightly down, Grok up. GPT-4.1 Nano did not respond to any of five prompt variants.
- A whitespace bug on the Anthropic API path that doubled spaces and blank lines at the cursor (blank lines: 100% → 0% after the fix).
Latency on the Claude Code backend, from real use: p50 1.9–2.6 s, p95 4.1–6.1 s depending on model, measured from request to response and separate from the deliberate ~2 s pause before a request is sent.
CI replays 52 recorded model outputs through the current extraction and cleanup code on every push, so a change to that code shows up as a test diff without calling a model.
Known issues
- GPT-4.1 Nano often glues the completion to the previous word (no leading space). Prompting did not fix it; prefer another model for prose.
- Models sometimes copy the text after the cursor. The copy is trimmed, which can leave no suggestion at all. xAI Grok does this more often since the whitespace fix.
- The
anthropic-haiku API preset sometimes returns nothing, and occasionally shows its own reasoning. In one test (348 requests per arm), 16–17% of requests produced no suggestion, mostly because the model closed its answer immediately or ran out of room while second-guessing itself. Rarely, text the model writes before its answer (such as a repeated line of the instruction) reaches the ghost text. Fourteen prompt variants did not fix this without breaking other completions (details); the Claude Code backend and the other presets are not affected in the same way.
- Cost is only shown for the Claude Code backend, where the CLI reports it; the API backend shows tokens only.
🧩 Available Models
The API backend includes presets for popular providers. Change the active preset via the status bar menu or the bespokeAI.api.preset setting.
| Preset |
Provider |
Model |
xai-grok (default) |
xAI |
grok-4.3 (reasoning off) |
xai-grok-code |
xAI |
grok-build-0.1 |
xai-grok-4 |
xAI |
grok-4.3 (low reasoning; slower) |
anthropic-haiku |
Anthropic |
claude-haiku-4-5-20251001 |
anthropic-sonnet |
Anthropic |
claude-sonnet-5 |
openai-gpt-4.1-nano |
OpenAI |
gpt-4.1-nano |
openai-gpt-4o-mini |
OpenAI |
gpt-4o-mini |
google-gemini-flash |
Google |
gemini-2.5-flash |
openrouter-haiku |
OpenRouter |
anthropic/claude-haiku-4.5 |
openrouter-gpt-4.1-nano |
OpenRouter |
openai/gpt-4.1-nano |
ollama-default |
Ollama (local, free) |
qwen2.5-coder:7b |
ollama-qwen3-4b |
Ollama (local, free) |
qwen3:4b |
ollama-qwen3-8b |
Ollama (local, free) |
qwen3:8b |
ollama-qwen35-9b |
Ollama (local, free) |
qwen3.5:9b (experimental; many empty completions in testing) |
API keys: Store keys via the Enter API Key command (Command Palette → "Enter API Key"). Keys are saved in your OS keychain. As a fallback, the extension also checks environment variables and ~/.creds/api-keys.env:
xAI → XAI_API_KEY · Anthropic → ANTHROPIC_API_KEY · OpenAI → OPENAI_API_KEY · Google → GEMINI_API_KEY · OpenRouter → OPENROUTER_API_KEY · Ollama → no key needed
Custom models: Run Bespoke AI: Add Custom Model from the Command Palette for a guided setup wizard. Any OpenAI-compatible endpoint works (LM Studio, Together, Mistral, etc.), plus Anthropic, Google, and OpenRouter. You can also add presets manually:
"bespokeAI.api.customPresets": [
{ "name": "My Llama", "provider": "openai-compat", "modelId": "llama3.2", "baseUrl": "http://localhost:1234/v1" }
]
⚙️ Configuration
All settings live under bespokeAI.* in VS Code settings.
General
| Setting |
Default |
Description |
enabled |
true |
Master on/off toggle |
mode |
"auto" |
Completion mode (auto-detects) |
triggerPreset |
"relaxed" |
Trigger preset: relaxed (~2s), eager (~800ms), on-demand (Alt+Enter only) |
debounceMs |
2000 |
Override the debounce delay from your trigger preset |
logLevel |
"info" |
Logging verbosity in Output channel |
customInstructions |
"" |
Standing instructions appended to the inline-completion prompt to steer output (e.g. Follow MISRA C rules). Both backends; completions only. Use Workspace scope for per-project rules. |
Backend
| Setting |
Default |
Description |
backend |
"claude-code" |
Active backend: claude-code (CLI) or api (HTTP) |
api.preset |
"xai-grok" |
Active API model (dropdown in settings, or status bar menu) |
Model (Claude Code backend)
| Setting |
Default |
Description |
claudeCode.model |
"sonnet" |
Active model (sonnet, haiku, opus, etc.) |
claudeCode.models |
["haiku", "sonnet", "opus", "fable"] |
Available models catalog |
Context Windows
| Setting |
Default |
Description |
prose.contextChars |
2500 |
Prefix context (characters) for writing |
prose.suffixChars |
2000 |
Suffix context (characters) for writing |
prose.fileTypes |
[] |
Additional language IDs to treat as writing |
code.contextChars |
2500 |
Prefix context (characters) for code |
code.suffixChars |
2000 |
Suffix context (characters) for code |
Code Override
Route code completions to a different backend or model than prose. For example, use Claude Code CLI for writing and an xAI preset for code — or vice versa.
| Setting |
Default |
Description |
codeOverride.backend |
"" |
Backend for code files: claude-code, api, or empty (use global default) |
codeOverride.model |
"" |
Model for code files. CLI: model name (e.g. haiku). API: preset ID (e.g. xai-grok-code). Empty = default. |
Context Menu Agent and Permissions
| Setting |
Default |
Description |
contextMenu.agent |
"claude-code" |
Agent CLI that Explain, Fix, Do open in a terminal |
contextMenu.permissionMode |
"default" |
Permission mode for Explain, Fix, Do (Claude Code) |
Agent: claude-code shows the menu only when the backend is Claude Code. opencode shows it with any backend and runs opencode --prompt=... — the model and permissions come from your own opencode config (opencode.json), so point opencode at LM Studio, Ollama, or any provider it supports.
- opencode only runs in trusted workspaces. opencode loads a project's own
opencode.json, .opencode/ plugins, and MCP servers without asking, so in a VS Code Restricted Mode window the commands refuse to start it.
permissionMode does not apply to opencode. Set the permission block in your opencode config instead — opencode's default allows file edits and shell commands without asking.
- Local models: opencode's system prompt is ~13k tokens. Set your model's context length to 16k or more (LM Studio and Ollama default lower and truncate silently), and prefer a 7B+ model with tool calling.
- Windows: needs
opencode.exe on PATH (scoop, choco, or the install script). The npm opencode.cmd shim is not used.
Permission mode options (Claude Code):
default — Ask before every action (safest)
acceptEdits — Auto-approve file reads and edits
bypassPermissions — Skip all permission checks (use with caution)
Tracing
User settings only. See Seeing what the model saw.
| Setting |
Default |
Description |
trace.captureContent |
true |
Keep prompt and output text in trace records (Recent Completions view, trace file, export) |
trace.file |
false |
Append trace records to ~/.bespokeai/traces.jsonl |
trace.otlp.endpoint |
"" |
OTLP/HTTP (JSON) endpoint to export trace records to. Empty = off |
trace.otlp.headersEnvVar |
"BESPOKE_OTLP_HEADERS" |
Environment variable holding exporter headers (key=value,key2=value2) |
trace.otlp.captureContent |
false |
Include prompt and output text in exported spans (needs trace.captureContent on too) |
📋 Commands
| Command |
Keybinding |
Description |
Trigger Completion |
Alt+Enter |
Manually trigger a completion |
Toggle Enabled |
— |
Toggle the extension on/off |
Cycle Mode |
— |
Cycle through auto → prose → code |
Clear Completion Cache |
— |
Clear the LRU cache |
Show Recent Completions |
— |
Browse the last 200 requests and what the model saw |
Show Menu |
— |
Status bar menu |
Generate Commit Message |
— |
AI commit message from staged diffs |
Suggest Edits |
— |
Fix typos/grammar/bugs in visible text |
Explain / Fix / Do |
— |
Context menu actions on selected text |
Enter API Key |
— |
Store an API key in the OS keychain |
Remove API Key |
— |
Remove a stored API key |
Add Custom Model |
— |
Guided wizard to add a custom API model |
Remove Custom Model |
— |
Remove a custom API model |
Restart Pools |
— |
Restart Claude Code subprocesses |
Architecture
User types → Mode detection → Context extraction → LRU cache check
→ Debounce → Backend Router → Claude Code CLI or API → Cleanup → Ghost text
A backend router dispatches requests to the active backend. The Claude Code CLI backend uses the Claude Agent SDK and manages subprocesses through a shared pool server — multiple VS Code windows share subprocesses via IPC (Unix sockets on macOS/Linux, named pipes on Windows). The API backend makes direct HTTP calls to Anthropic, OpenAI-compatible (OpenAI, Google Gemini, xAI, OpenRouter), or local Ollama endpoints.
All backends share the same prompt strategy ({{FILL_HERE}} marker, <COMPLETION> tags) with backend-specific extraction (prefill for Claude models that accept one, such as Haiku 4.5; tag extraction for newer Claude models, which reject a prefill; preamble stripping for OpenAI-compat).
Key design decisions:
| Decision |
Rationale |
| Writing-first defaults |
Unrecognized languages fall back to writing, not code |
| Dual backend |
Claude Code CLI for subscribers, API for key-based access |
| Shared prompts |
Same system prompt across all backends — only extraction differs |
| No streaming |
VS Code's inline completion API requires complete strings |
| LRU cache (50 entries) |
5-minute TTL prevents redundant calls when revisiting positions |
| Session reuse (CLI) |
One subprocess serves many requests — avoids cold-start per call |
🔎 Seeing what the model saw
Every completion request produces a trace record: what the model was sent, what it returned, how long it took, tokens used, cost (when the backend reports it), and what happened to the result — shown, cached, empty, aborted, or error.
Run Bespoke AI: Show Recent Completions (or pick Recent Completions from the status bar menu) to browse the last 200 requests in the current window, newest first. Click a row to see the system instructions, the exact message sent, the raw model output, the ghost text that was shown, and a timing breakdown (debounce, pool-slot wait, model time). Commit-message and Suggest Edits requests appear in the same list.
Rendered from the view's own HTML with synthetic requests and VS Code's Dark Modern theme colours.
Records use the attribute names of the OpenTelemetry GenAI semantic conventions (gen_ai.request.model, gen_ai.usage.input_tokens, …; the conventions are still in development; the extension follows semantic-conventions-genai 1.42.0-dev, checked 2026-09-27). That makes the same records usable outside the editor:
bespokeAI.trace.file appends them to ~/.bespokeai/traces.jsonl, one span per line (rotated at 5 MB; archives older than 7 days are deleted).
bespokeAI.trace.otlp.endpoint exports them over OTLP/HTTP (JSON) to your own backend. See Privacy and data flow for what is sent and when.
bespokeAI.trace.captureContent: false keeps prompt and output text out of trace records (the Recent Completions view, the trace file, and any export); timing, tokens, and outcomes are still recorded. It does not govern the Output channel log: at bespokeAI.logLevel: trace the extension writes full prompts there, and VS Code keeps output logs on disk. Leave logLevel at info or debug if that matters to you. logLevel can only be set in user settings, so a repository cannot turn it on.
Sending traces to Langfuse or an OpenTelemetry Collector
Langfuse
- Create an API key pair in your Langfuse project and base64-encode
publicKey:secretKey.
- Put the headers in an environment variable (header values never go in
settings.json):
BESPOKE_OTLP_HEADERS="Authorization=Basic <base64>,x-langfuse-ingestion-version=4"
- Set
"bespokeAI.trace.otlp.endpoint": "https://cloud.langfuse.com/api/public/otel" (or your region / self-hosted URL; /v1/traces is appended).
- Optionally set
"bespokeAI.trace.otlp.captureContent": true to see prompts and outputs in Langfuse.
OpenTelemetry Collector (Jaeger, Tempo, Phoenix, …): point the endpoint at the Collector's OTLP/HTTP receiver, e.g. http://localhost:4318. The exporter sends JSON encoding only, so protobuf-only backends such as Arize Phoenix need a Collector in between.
Spans are batched (every 5 s or 20 spans), each request times out after 5 s, and failures are dropped with at most one log line per minute. The exporter never delays a completion. Cost goes in gen_ai.usage.cost, which is not part of the standard (Langfuse reads it).
🔍 Troubleshooting
Completions not appearing?
- Check the status bar — is it showing "AI Off"? Click to re-enable.
- If it shows "Setup needed", the Claude Code CLI may not be installed or authenticated.
- Check if trigger preset is "on-demand" — in that mode, press
Alt+Enter to trigger.
- Open the Output panel ("Bespoke AI") and check for errors.
- Run the "Bespoke AI: Restart Pools" command from the Command Palette.
"Claude Code CLI not found"?
- Install Claude Code:
curl -fsSL https://claude.ai/install.sh | bash (macOS/Linux) or npm install -g @anthropic-ai/claude-code (any platform)
- Restart VS Code after installation.
"Authentication required"?
- Run
claude in your terminal and follow the login prompts.
- Ensure you have an active paid Claude plan (Pro, Max, Team, or Enterprise).
Explain/Fix/Do says the Claude Code executable was not found (Windows)?
- The commands start Claude Code directly, not through a shell. On Windows this needs the native
claude.exe (native installer, ~/.local/bin/claude.exe) or the bundled CLI with node on PATH — the npm claude.cmd shim is not used.
Still not working?
- Check for orphaned processes:
- macOS/Linux:
pkill -f "claude.*dangerously-skip-permissions"
- Windows: Use Task Manager to end
node.exe processes running Claude
- Check for a stale lockfile at
~/.bespokeai/pool.lock and remove it.
- Disable and re-enable the extension.
🔒 Privacy and data flow
What leaves your machine. The text around your cursor (prefix and suffix context) goes to the backend you configured. Commit message generation sends your staged diff; Suggest Edits sends the visible editor text. With the Claude Code backend, requests go through your local Claude Code installation; with the API backend, they go directly to your chosen provider's endpoint (or to Ollama on your machine). Nothing else is sent.
No telemetry. The extension collects no usage analytics and makes no network calls other than to your configured backend — plus the optional trace export below, which you have to point at an endpoint yourself. (The Claude Code CLI, when you use that backend, has its own telemetry settings.)
What stays on your machine, in ~/.bespokeai/ (a folder only your user account can open):
| File |
Contents |
When |
usage-ledger.jsonl |
Per-request model, token counts, duration, cost where reported, the workspace folder name, and (Claude Code backend) the CLI session id. No prompt or document text |
Always (feeds the Usage view in the status menu). Owner-only (0600) |
traces.jsonl |
Per-request trace records; includes prompt and output text unless you turn capture off. Rotated at 5 MB; archives older than 7 days are deleted |
Only with bespokeAI.trace.file on. Owner-only (0600) |
pool.lock, pool.sock |
Coordination between VS Code windows sharing one Claude Code pool |
Claude Code backend |
Optional trace export. bespokeAI.trace.otlp.endpoint sends trace records to an OpenTelemetry backend you run or subscribe to (for example Langfuse, or an OTel Collector). It is off by default, pauses while VS Code telemetry is disabled (telemetry.telemetryLevel; VSCodium ships with telemetry off, so there export stays paused until you turn telemetry on), and exports prompt text only if you also turn on bespokeAI.trace.otlp.captureContent. The first export to a non-local host shows a notice saying what will be sent where.
Keys. API keys are stored in your OS keychain via VS Code SecretStorage. Settings that choose a backend, endpoint, model, or key variable can only be set in your user (or remote-machine) settings, never by a repository's .vscode/settings.json.
🛠️ Development
npm install && npm run compile # Build
npm run watch # Watch mode (F5 to launch dev host)
npm run check # Lint + type-check
npm run test:unit # Unit tests (incl. the replay set)
npm run test:coverage # Unit tests with v8 coverage
npm run test:quality # Generate completions for every eval scenario (Layer 1)
npm run test:quality:rescore # Re-run the deterministic checks over saved eval runs
npm run latency-report # p50/p90/p95 from your local usage ledger
CI runs on every push and pull request to main: lint, type-check, Prettier, the unit suite with coverage on Ubuntu, macOS, and Windows (the pool IPC is platform-specific), and a VSIX build for pull requests. The unit suite is ~1,540 tests at ~71% line coverage; most of what is uncovered is VS Code wiring in extension.ts. Tests that call real models (test:api, test:quality) need keys or a Claude subscription and run locally, not in CI.
Model choice for code. Dedicated autocomplete products mostly use small models trained for fill-in-the-middle (FIM). This extension sends the same instruction-style prompt to general chat models for both prose and code, because prose is its main use and FIM models are trained mostly on code. For code, a FIM-trained model may well be faster and more accurate; that comparison has not been run yet. Any OpenAI-compatible or Ollama model can be added as a custom preset, and bespokeAI.codeOverride can route code to it.
Tested Models
The quality suite has been run against these models. The reference set for prompt changes is CLI sonnet, xai-grok and openai-gpt-4.1-nano, one per extraction strategy; the others were last run before the 2026-09 prompt change.
| # |
Backend |
Preset ID |
Model |
Last full run |
| 1 |
CLI |
(default) |
sonnet |
2026-09-28 |
| 2 |
CLI |
— |
haiku |
2026-03-02 |
| 3 |
API |
xai-grok |
grok-4.3 (reasoning off) |
2026-09-28 ¹ |
| 4 |
API |
xai-grok-code |
grok-build-0.1 |
not run ² |
| 5 |
API |
anthropic-haiku |
claude-haiku-4-5-20251001 |
2026-03-02 |
| 6 |
API |
anthropic-sonnet |
claude-sonnet-5 |
sampled 51 of 106 (2026-09-28); no full run |
| 7 |
API |
openai-gpt-4.1-nano |
gpt-4.1-nano |
2026-09-28 |
| 8 |
API |
google-gemini-flash |
gemini-2.5-flash |
2026-03-01 |
| 9 |
API |
ollama-default |
qwen2.5-coder:7b |
2026-03-01 |
¹ Run under the retired id grok-4-1-fast-non-reasoning, which xAI has served since 2026-05-15 as grok-4.3 with reasoning off: the same request this preset now sends.
² The earlier run (2026-03-01) was on grok-code-fast-1, retired 2026-05-15 and now served as grok-build-0.1.
Coverage: all 3 extraction strategies, 6 providers, 3 cost tiers, code-specialized model, local option. See AGENTS.md for testing commands.
🤝 Contributing
Contributions welcome — fork the repo, create a branch, and open a pull request.
- Run
npm run check before submitting (must pass)
- See
AGENTS.md for architecture details, testing workflows, and coding conventions
- Open an issue for bugs or feature ideas
👤 Author
Built by Trent McNitt — AI developer specializing in agent development, prompt engineering, and full-stack applications.
Available for contract work →