Curtis AI Chat

by Jordan Newell
5
4
3
2
1
Score: 47/100

Description

Polyglot AI chat for Obsidian. 30+ providers, agent mode with 10 vault tools, multi-model arena, voice, memory. Local-first, free, MIT.

Reviews

No reviews yet.

Stats

1
stars
235
downloads
0
forks
72
days
0
days
0
days
11
total PRs
3
open PRs
3
closed PRs
5
merged PRs
1
total issues
0
open issues
1
closed issues
131
commits

Latest Version

20 hours ago

Changelog

MCP client support: ten built-in tools becomes any tool the user already runs. Plus vault retrieval (RAG) behind the existing settings, and the storage promise made literal — conversations now live as markdown files in your vault. Ships after a full-codebase audit pass: every line reviewed, ~40 defects fixed, all dead code removed.

Added

  • Conversations as vault files — every chat persists as one markdown note in AI/Conversations/ (folder configurable) instead of localStorage: YAML frontmatter for metadata, readable ## You / ## AI sections with hidden per-message metadata, chronological filenames. Native Obsidian search indexes history; hand edits round-trip; deleting a conversation (new delete action in the history dropdown) trashes its file; legacy localStorage history imports automatically.
  • MCP client — connect any number of Streamable HTTP MCP servers under Settings -> MCP servers. Every tool they expose joins the agent toolset as mcp__<server>__<tool> (collision-safe, 64-char provider-safe naming), requires agent mode, and is off by default. Speaks protocol 2025-06-18, negotiates down for older servers, re-initializes transparently on session loss, answers server pings, and skips tool negotiation for tool-less servers. Tools render text/structured results and truncate at 20k chars. No MCP OAuth — servers you enter are servers you trust.
  • Vault retrieval (RAG) — notes are chunked and embedded via any OpenAI-compatible /embeddings endpoint (local Ollama/LM Studio work offline; Anthropic and Azure are excluded), stored int8-quantized in the plugin dir, and the top-k excerpts for your message are injected into the system prompt. Explicit @-mention attachments win over retrieval, and retrieval failures never block a send.
  • Incremental indexing — rebuilds skip unchanged files; edits/deletes/renames update the index live (edits made during a rebuild are queued and indexed after). The index file is written atomically, so a crash can't force a full re-embed.
  • semantic_search agent tool — meaning-based vault search alongside keyword search_notes.
  • Settings — MCP servers group and Vault retrieval group (both indexed by Obsidian's settings search), plus a "Rebuild vault index" command.

Fixed

Highlights from the audit pass (full list in the changelog):

  • Local HTTP providers (Ollama, LM Studio, llama.cpp) stream again — streaming requests were routed through Node's https module regardless of protocol.
  • Mobile: a CORS-blocked stream now retries buffered via requestUrl instead of failing; aborting no longer surfaces spurious errors.
  • /paste inserts the clipboard instead of erasing it; edit-resend restores a message's image and note attachments.
  • Editing a custom provider no longer wipes its keychain-stored API key or leaves a stale duplicate registry entry.
  • Regenerate no longer deletes the reply before checking the provider is authenticated; mid-stream conversation switches can't deposit a response into the wrong chat.
  • Streaming token usage is real for OpenAI/Azure/OpenRouter (stream_options.include_usage); responses cut off by max-tokens say so; per-message and /stats costs are computed from provider pricing.
  • MCP hardening: 64-char tool-name cap actually enforced, disable-mid-connect races fixed, wrong-id responses rejected, HTML error bodies surfaced properly.
  • Cross-conversation search covers all conversations (older ones were invisible past the newest 200 messages).
  • Failed settings migrations retry next boot instead of being marked done; a v5 migration purges settings that never had a reader.

Removed

  • The never-wired prompt-templates module and dead settings (panel width, hotkeys, budget limit, cost-tracking toggle, daily-notes trio). No user-facing behavior lost — none of it was reachable.

Upgrade

Update via Settings -> Community plugins -> Check for updates, or download main.js, manifest.json, and styles.css from the assets below into <vault>/.obsidian/plugins/curtis-ai-chat/. Requires Obsidian 1.13.0 or later.

README file from

Github

Screenshots

Real captures, regenerated any time with npm run shots && npm run mockups (Playwright driving Obsidian over the DevTools protocol).


Quick start

60 seconds to your first message.

  1. Install — download the latest release (main.js, manifest.json, styles.css) into <vault>/.obsidian/plugins/curtis-ai-chat/, then enable it under Settings → Community plugins. Or use BRAT for auto-updates during the beta.

  2. Configure one provider — open Settings → Curtis AI Chat → Provider Configuration, enable a provider, paste an API key. Keys are stored in your OS keychain via the Obsidian SecretStorage API.

  3. Send a message — click the robot icon in the ribbon, pick a model from the header dropdown, type, hit Enter. (All commands are hotkey-assignable under Obsidian's Settings → Hotkeys — none are bound by default.)

[!TIP] Want fully private, free, offline AI? Install Ollama, run ollama pull qwen2.5:7b-instruct, then enable Ollama (Local) in provider settings. No API key. Nothing leaves your machine.


Highlights

The flagship features. Full details in CHANGELOG.md and the per-feature docs.

Feature What it does
🤖 Curtis Agent AI calls tools to read, create, and edit your vault notes. Eleven built-in tools, every provider.
🔌 MCP servers Connect MCP servers you already run — any tool they expose joins the agent's toolset, namespaced mcp__<server>__<tool>.
⚔️ Multi-model arena Stream one prompt to 2 models in parallel, side-by-side. Pick a winner, promote to chat.
🎨 Inline diff rewrite Cursor-style rewrite with an Accept/Reject diff modal. Assignable hotkey.
@ @-mention vault notes Type @ in chat → fuzzy-search your vault → attach note content as context.
🎙️ Voice I/O Whisper speech-to-text on the mic button. Browser TTS on every assistant message.
🔍 Cross-conversation search Assignable hotkey opens a fuzzy-matched picker across all conversations and messages.
📝 Markdown export Download any conversation as .md. /export slash command or download icon.
🧠 Memory editing UI Edit/delete individual memory facts from Settings → Memory. No more append-only.
🗂️ Conversations as vault files Every chat persists as a markdown note in AI/Conversations/ — synced across devices, in native Obsidian search, and readable by the agent. Old localStorage history imports itself.

Plus a full type-safety pass: every AI provider response shape is strictly typed, with type-guard narrowing at every JSON boundary. Zero lint warnings on npm run build.


Features

🤖 Curtis Agent

The AI can now call tools to modify your vault. Eleven built-in tools: read_note, search_notes, semantic_search, create_note, edit_note, list_notes, get_tags, get_backlinks, get_current_note, get_current_date, calculator.

  • Every major provider — Anthropic via native tool use, OpenAI-compatible endpoints (OpenAI, Gemini, Ollama, Groq, DeepSeek, custom). The model must support tool calling.
  • agentMaxTurns safety cap (default 5) prevents runaway tool loops
  • MCP servers — connect your existing Model Context Protocol servers (Settings → MCP servers) and every tool they expose becomes callable alongside the built-ins. Streamable HTTP transport — local stdio servers need an HTTP bridge such as mcp-proxy or supergateway.
  • Opt-in via Settings → Agent → Enable

→ docs/AGENT.md

⚔️ Multi-model arena

Pick 2 models, send one prompt, watch responses stream side-by-side. Click Promote to chat on any column to continue with that model.

  • Compare quality, latency, and cost live
  • All providers supported (mind per-provider rate limits)
  • Stacks vertically on mobile

→ docs/ARENA.md

🎨 Inline diff rewrite

Select text in any note → right-click → Rewrite with AI (diff) (or assign a hotkey under Settings → Hotkeys). The AI generates an improved version and a modal shows line-by-line green/red diff. Accept or reject.

  • Cursor-style review workflow
  • Reuses your active provider and model
  • Word-level diff and inline editor decorations planned for v1.1

→ docs/DIFF_REWRITE.md

@ @-mention vault notes

Type @ in the chat input → fuzzy-search your vault → click a result to attach. Note content is prepended to your message as invisible context. Chips above the input show what's attached.

  • Active-note pill in the chat header for one-click attach of the current note
  • AI uses attached content as the source of truth — no re-searching
  • Works with or without the Agent enabled

→ docs/MENTIONS.md

🎙️ Voice I/O

  • Speech-to-text via OpenAI Whisper — click the mic button, talk, transcribed text lands in the chat input
  • Text-to-speech via browser speechSynthesis — speaker button on every assistant message, no API key needed
  • Auto-speak toggle in the header for hands-free listening
  • Markdown is stripped before synthesis so the voice reads naturally

→ docs/VOICE.md

💬 Chat that gets out of the way

  • Streaming responses with a clean Telegram-style bubble layout
  • Model picker with capability pills (vision 👁, tools 🔧, context length)
  • Per-message hover actions: copy, quote-into-input, save-as-note, insert-into-active-note, regenerate, edit-and-resend
  • Conversation history dropdown
  • Cross-conversation search (assignable hotkey)

🖼️ Image attachments

  • Paste (Ctrl+V), drag-and-drop, or paperclip — three ways to attach
  • Images save as real vault files (not base64 blobs in localStorage)
  • Vision-capable models see them automatically; non-vision models get a clear "switch to a vision model" notice
  • Transcripts via /save-all embed images as ![[wikilinks]]

→ docs/IMAGES.md

✂️ Inline selection actions

Right-click any selection in a note for Explain · ELI5 · Summarize · TL;DR · Improve · Fix grammar · Shorten · Translate · Make a table · Pros & cons · Code review · Refactor · Add tests. Each writes the result directly back into the note — replace or insert-below.

→ docs/SELECTION_ACTIONS.md

🧠 Long-term memory

Curtis remembers durable facts about you across conversations — preferences, identity, projects, standing instructions. Facts live in a markdown file in your vault.

  • Auto-capture: background LLM extraction after each turn (0–3 facts)
  • Manual: /remember <fact> or right-click selection → Save to memory
  • Edit UI: edit/delete individual facts from Settings → Memory
  • Recall: every prompt includes a ## What you know about the user block

→ docs/MEMORY.md

🔎 Vault retrieval (RAG)

Ask about your vault in plain language — the most relevant note excerpts are retrieved and injected into the prompt automatically.

  • Any embeddings provider — OpenAI, Gemini, Z.ai, or fully local via Ollama/LM Studio (Anthropic and Azure are excluded — no embeddings API / deployment-specific URL scheme)
  • Automatic injection — top-k excerpts in every prompt; skipped when you @-attach a note, because curated context wins
  • Live index — edits re-embed in the background; rebuilds are incremental and cheap
  • semantic_search tool — the agent queries the vault by meaning, not just keywords
  • Your index stays yours — int8-quantized JSON in the plugin folder; nothing uploaded beyond the embeddings provider you configure

Enable in Settings → Vault retrieval, then Rebuild index.

⌨️ Slash commands

Type / in the chat input for an autocomplete menu of 17 commands — /clear, /regen (alias /regenerate), /title, /copy, /note, /save-all, /paste, /model, /provider, /system, /stats, /remember, /forget, /memory, /export, /help.

→ docs/SLASH_COMMANDS.md

⚙️ Customizable

  • Chat panel position (left/right), background (theme default or a wallpaper image from your vault)
  • Configurable system prompt, temperature, max tokens
  • Enter-to-send (default) or Enter-for-newline
  • Auto-save assistant responses to a folder of your choice
  • Show or hide token counts after each response

Why Curtis AI Chat

Curtis AI Chat is the agent layer for Obsidian. Where other plugins focus on a single workflow (chat, RAG, or text generation), Curtis ships all three with a polyglot provider model and a native Obsidian feel.

Curtis AI Chat Smart Connections Text Generator Copilot for Obsidian
All features free (no subscription) ✅ ✅ ✅ Core only — advanced features need Copilot Plus
Agent tools (vault-modifying) ✅ 11 built-in + MCP ❌ ❌ ✅ v4 agent chat
Semantic vault retrieval (RAG) ✅ any embeddings provider ✅ ❌ ✅
Provider count 30+ 1–2 1–2 10+
Local-first (Ollama, LM Studio) ✅ ❌ ✅ ✅
Multi-model arena ✅ ❌ ❌ ❌
Inline diff rewrite ✅ ❌ ❌ ❌
Voice I/O ✅ ❌ ❌ ❌
Long-term memory ✅ Markdown-file Vector index ❌ ✅ JSON
Native Obsidian rendering ✅ MarkdownRenderer Partial ❌ Partial

[!NOTE] Comparison refreshed 2026-10-03. Other plugins ship fast — Copilot's v4 agent chat is real and actively developed, Smart Connections remains the gold standard for RAG (Curtis' own retrieval is new; theirs is battle-tested), Text Generator excels at template-driven writing. Curtis aims to be the free, polyglot, local-first agent layer that ties chat, tools, and memory together.

Principles

  • Your data stays yours. Conversations as markdown files in your vault (AI/Conversations/ by default) — synced across devices, searchable in native Obsidian search, and readable by the agent. Images as real vault files. Memory as a markdown file you can read and edit. No telemetry, no tracking, no phone-home.
  • No vendor lock-in. Thirty providers ship built-in. Add any OpenAI-compatible endpoint as a custom provider in 30 seconds. Switch models mid-conversation.
  • Local-first when you need it. Enable Ollama and nothing ever leaves your machine. Useful for private notes, air-gapped machines, or when you just don't want to pay per token.
  • Native Obsidian feel. Real Obsidian setting components. Messages render through MarkdownRenderer. Themes respected — light, dark, Things, Minimal, all of them.

Configuration

Full configuration reference lives in the docs:

Or start at the docs index.


Privacy & security

Curtis AI Chat accesses your vault files only in user-initiated cases:

  1. Agent vault-search tool — when you explicitly invoke a tool in chat, the plugin enumerates markdown files. The agent sees file paths and contents you ask it to read.
  2. Image picker — when you click the paperclip, the plugin lists image files.
  3. Folder picker — when you configure auto-save or wallpaper folders.
  4. @-mention autocomplete — when you type @, the plugin fuzzy-searches note names. Note contents are only read when you actually attach and send.

No file contents are sent to AI providers except message text, attached images, attached note contents, and tool-call results. API keys are stored in your OS keychain (Windows Credential Manager / macOS Keychain / Linux Secret Service), never in the vault.

Clipboard: the plugin reads the system clipboard only when you paste into the chat input (Ctrl+V / long-press → Paste), and writes to it only when you click copy on a message. Nothing is read from or written to the clipboard in the background.

[!IMPORTANT] Tool calls go to your AI provider. Vault contents read by agent tools are sent to the provider as part of the conversation. If you're on a cloud provider, that content leaves your machine. Switch to Ollama for fully offline operation.

Network access

Curtis is vault-first — no background telemetry, no analytics, no auto-update checks. Every outbound request is user-initiated. The plugin may contact these domains:

When Domain Why
You send a message (cloud providers) Your provider's API (e.g. api.anthropic.com, api.openai.com, generativelanguage.googleapis.com) Chat completion / streaming
Vault retrieval builds or queries the index (opt-in) Your embeddings provider's API Note chunks and queries are sent for embedding
You send a message (Ollama / LM Studio) localhost / your custom endpoint Local model inference
You click "Test connection" or "Refresh models" Your provider's API Auth + reachability check, model list
You use voice transcription api.openai.com Whisper API (only when voice input is on)
The agent calls web_search (opt-in) html.duckduckgo.com DuckDuckGo search
The agent calls read_url (opt-in) r.jina.ai URL → markdown reader
The agent calls MCP tools (opt-in) your own MCP servers User-configured endpoints (Settings → MCP servers)

The two web tools (web_search, read_url), voice transcription, MCP, and vault retrieval are off by default. Without them, the only external calls are to whichever AI provider you configured — or none, if you're on Ollama. MCP tool calls go only to the server URLs you entered; tool results travel through your AI provider like any other tool result.


Installation

[!TIP] Curtis AI Chat is in the community plugin directory. Install from there, manually (below), or via BRAT for beta-channel updates.

Manual install

  1. Download the latest release main.js, manifest.json, and styles.css.
  2. In your vault, create .obsidian/plugins/curtis-ai-chat/.
  3. Copy the three files into that folder.
  4. Open Settings → Community plugins, refresh the list, enable Curtis AI Chat.

From source (developers)

git clone https://github.com/JordanNewell/curtis-ai-chat.git
cd curtis-ai-chat
npm install
npm run build

See CONTRIBUTING.md for dev setup, code style, and the audit checklist.


Mobile

Curtis AI Chat works on iOS and Android with a few caveats:

  • Hover-only elements (per-message toolbar, code-block copy) are always visible on touch at reduced opacity
  • Touch targets sized to Apple HIG minimums (44pt send button, 40pt header icons)
  • Wallpaper background auto-disabled on phones for scroll performance
  • /paste may fail if the OS blocks clipboard read — use Ctrl+V / long-press → Paste
  • Streaming falls back to buffered responses automatically when a provider blocks mobile CORS
  • Local providers work over LAN (http://192.168.1.50:11434/v1/chat/completions)

Roadmap

  • Curtis Agent: Anthropic, Gemini, and Ollama provider support (v1.1)
  • Inline diff rewrite: word-level diff and inline editor decorations
  • Settings: declarative getSettingDefinitions() — shipped in v1.2.0 (see ADR: settings API)
  • Vault retrieval (RAG): embedding index over the vault, auto-injected context, semantic_search agent tool (v1.4.0)
  • MCP client: connect external MCP servers, their tools join the agent toolset (v1.4.0)
  • Conversations as vault markdown files with automatic localStorage import (v1.4.0)
  • Voice: streaming TTS, wake-word detection
  • Conversation branching UI
  • Plugin settings import/export
  • Semantic memory retrieval via sqlite-vec (when memory exceeds ~150 facts)

See the open issues for the live list.


Contributing

PRs welcome — see CONTRIBUTING.md for dev setup, code style, and the audit checklist every change goes through before merge.

[!NOTE] Not accepting external PRs yet while the v1 line stabilizes. Bug reports and feature requests via Issues are very welcome.

Architecture decisions

Settings API

Since v1.2.0 Curtis targets Obsidian 1.13.0+ and uses the declarative getSettingDefinitions() API for its settings tab. Every section and row is indexed by Obsidian's settings search; dynamic re-renders go through the sanctioned SettingTab.update().

History: through v1.1.x the floor was 1.11.4 (set by the SecretStorage API for per-provider key storage) and the tab used the imperative display() API, which Obsidian 1.13 deprecated. The dual-path (getSettingDefinitions() + display() fallback) was evaluated and rejected — the declarative path's SettingTab.update() is 1.13-only, so supporting both meant either shipping a broken tab on older versions or tripping the no-unsupported-api lint rule. When Obsidian 1.13.6 reached the stable channel for all desktop and mobile users (August 2026), the migration shipped as v1.2.0: a single declarative path, zero deprecation warnings, and settings search that actually finds things.

💬 Feedback

All feedback is public and lives on GitHub:

No GitHub account? Email [email protected] and it gets posted publicly for you.


License

MIT © Jordan Newell