An agent-native document workstation for people who write technical docs
MEditor is an agent-native document workstation for people who write technical docs on macOS. The core loop: the Agent edits your document → the preview renders it instantly → you iterate together.
Point it at a code repo or workspace and let the Agent do the writing engineers actually need — weekly reports, design proposals, changelogs, API references, meeting notes.
Pure SwiftUI + AppKit — no Electron. Bring-your-own-key — your documents and your model credentials never leave your machine.
- 26 tools on macOS (14 cross-platform), multi-turn loop — The Agent reads, writes, patches, and searches documents; locates content by outline/structure tree (Markdown headings, HTML structure with folded CSS); operates workspace files; drives the editor; and runs sandboxed shell commands — reasoning across tool calls until the job is done
- Three backends — OpenAI-compatible and Anthropic (9 built-in presets: Anthropic, OpenAI, DeepSeek, Kimi, GLM, Qwen, OpenRouter, Groq, Ollama), plus a local Claude CLI backend that reuses your Claude Code login
- BYOK — Any OpenAI-compatible endpoint works; keys stay in your local Keychain
- Streaming everywhere — Responses stream token-by-token; tool steps render inline with expand/collapse detail
- Skills — Packaged playbooks the Agent loads on demand: its prompt carries only each skill's name and one-liner, and it calls
load_skillto pull the full text when a task actually matches (progressive disclosure — a shelf of 30 skills costs you 30 lines, not 30 documents). A built-in set ships with the app, a curated gallery adds 8 installable ones, and your own live in~/.meditor/skillsor a workspace's.meditor/skills(a folder withSKILL.mdplus scripts/templates the Agent reads only if needed). Import/export shares a skill as a single Markdown file - MCP both ways — MEditor speaks MCP as a server (external agents drive your workspace) and as a client (the in-app Agent gains tools from servers you point it at). See MCP below
- Write confirmation — The Agent asks before writing files; "allow all for this run" when you're in flow
- Risk-tiered command sandbox — Shell commands are classified by risk before execution
- Context budget — Token budget with automatic eviction keeps long sessions on track; stall detection stops runaway loops; read-only tools run in parallel
- Observable runs — Token usage and elapsed time shown per run
- Inline diff review — Agent edits land as reviewable diffs that never clobber your own typing;
@mentionpulls files into context; multi-session history keeps every conversation
- Slash commands — Type
/in the editor to pull up/ask/polish/outline/translate/summary/fix/tableand more; commands that rewrite your document stream their changes as a diff you confirm before anything lands - Selection action bar — Select text and a floating bar offers rewrite / tighten / expand / translate (select a list and it can become a table); Esc dismisses
- Context automation — Conversations auto-attach the current document (removable chip in the composer, can be disabled in Settings); when over budget it's smartly trimmed to the cursor's neighborhood plus head and tail
- Diagnostics → one-click fix — The diagnostics panel and pre-export checks offer "Let Agent fix it"; the model repairs, then re-scans so you can compare
- Live preview — Real-time Markdown rendering (marked.js), code highlighting (highlight.js), diagrams (Mermaid.js), math formulas (KaTeX)
- Editor that keeps up — Native
NSTextViewediting (Markdown / HTML syntax modes), bidirectional scroll sync, find & replace, Quick Open (⌘P), tabs, and an FSEvents-driven file browser. Code blocks in the preview get 35 language grammars via highlight.js - Images land on disk by themselves — ⌘V a screenshot or drag an image in; it's saved to
assets/and inserted as a relative path - Local history snapshots — Every save keeps a snapshot with tiered retention; File History (⌃⌘H) diffs any two versions and restores in one click — and the restore itself is undoable
- Global search (⌘⇧F), TOC outline, tab context menus (close others / reveal in Finder / save as template), and Quick Look previews from Finder
- Todo inbox — Collect tasks in a sidebar with calendar & week timeline views (EventKit-backed)
- LAN share — Built-in HTTP server with one-time token auth; one-click Gist publishing too
- Online publish — One-click publish via Cloudflare. Hosted free tier: 20 docs/month per token, links expire after 30 days; Pro (early access) removes both limits
- iOS companion — Edit, chat, and publish over iCloud from your phone
(not yet on the App Store; build from source: open
Mobile/MEditorMobile.xcodeprojin Xcode 26+, select your own signing team, iOS 17+ simulator or device) - Presentation & focus modes, HTML / PDF / 2× PNG export, preview themes (GitHub / Nord / Dracula)
- Pre-flight export checks — PDF/HTML export first diagnoses dead links, missing images, and heading problems (can be turned off in Settings)
- PDF export themes — Paper size, margins, header/footer, and an optional cover page; the default export keeps links clickable
- Copy as rich text — ⌥⌘C copies rendered formatting, ready to paste into Feishu or email
- Auto-update via Sparkle — grab builds from meditorapp.pages.dev
More on the website.
- macOS 14.0+ (Sonoma) · iOS 17.0+ (companion app)
- Xcode 26+ —
Package.swiftisswift-tools-version: 6.2, so older Xcode cannot even load the package - Swift 6.2+ (app and test targets in Swift 6 language mode, strict concurrency)
First launch: the release build is ad-hoc signed (not notarized yet), so macOS may warn once about an unidentified developer. Right-click
MEditor.app→ Open, then confirm. Afterwards it launches normally, including in-app updates.
git clone https://github.com/migoric/Meditor.git
cd Meditor
swift build
swift run./scripts/test.sh # uses full Xcode toolchain
make test # short alias
# static guards (same scripts CI runs)
swiftlint lint --strict --config .swiftlint.yml Sources
python3 scripts/check_service_layer_deps.py
python3 scripts/check_dead_tokens.pyIf the first build hangs while fetching Sparkle, your network is likely behind a TLS-intercepting proxy. Escape hatch: unzip
Sparkle-for-Swift-Package-Manager.zipand dropSparkle.xcframeworkintoVendor/—Package.swiftauto-detects it and switches to a local binaryTarget.Vendor/is gitignored.
bash scripts/bundle.sh # builds both arches and creates a Universal .app
open .build/debug/MEditor.appThe bundle script compiles arm64 + x86_64 and lipos them into a single
Universal binary, so the app runs natively on both Apple Silicon and Intel Macs.
The
.appis ad-hoc signed automatically. After first launch, associate.md/.htmlfiles via Get Info → Open With → MEditor → Change All.
The app version has a single source of truth per platform — this is where you change it:
- macOS: edit
VERSIONat the repo root.scripts/bundle.shinjects it intoMEditor.app'sInfo.plistat packaging time (the version keys inSources/MEditor/Info.plistare placeholders). - iOS: edit
MARKETING_VERSION/CURRENT_PROJECT_VERSIONinMobile/MEditorMobile.xcodeproj(target build settings).Mobile/MEditorMobile/Info.plistreferences them as$(MARKETING_VERSION)/$(CURRENT_PROJECT_VERSION).
Run make version to see the current version and these pointers.
Pre-1.0, but boring on purpose: semantic versioning, a CHANGELOG entry for every release, and breaking changes called out explicitly — there have been none so far. Update packages are Ed25519-signed and verified by Sparkle before install. 1.0 criteria: today's feature set ships stable for a full release cycle — no date promised, no features held back.
open Package.swiftOpen Settings (⌘,) → AI and pick a backend:
| Backend | What you need |
|---|---|
| Built-in preset | Pick one of the 9 presets (Anthropic, OpenAI, DeepSeek, Kimi, GLM, Qwen, OpenRouter, Groq, Ollama), paste your API key, pick a model |
| Claude CLI | Nothing — reuses your local Claude Code login |
| Custom endpoint | Any OpenAI-compatible Base URL + key + model |
No key? The Claude CLI backend gets you running with zero configuration.
MEditor ships a built-in MCP server over stdio, so external agents can operate on a workspace through the same tool layer the in-app Agent uses (file read/write/patch, directory listing, workspace search — 11 tools; UI-dependent tools like open_file / insert_at_cursor are not exposed headlessly, and the shell tool run_command is off by default).
Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"meditor": {
"command": "/Applications/MEditor.app/Contents/MacOS/MEditor",
"args": ["mcp", "--workspace", "/path/to/your/workspace"]
}
}
}--workspace defaults to the current directory if omitted. Shell commands are not exposed by default — pass --allow-shell to add run_command (12 tools). When enabled they go through the same risk-tiered sandbox as the in-app Agent: BLOCKED commands are always rejected; WARN-level commands (e.g. git push, mv) are rejected by default in headless mode — add --allow-warn-commands to opt in. In headless mode there is no confirmation dialog, so the static command blocklist is not a security boundary; only enable --allow-shell for workspaces you trust.
The other direction works too — the in-app Agent can use tools you already have (a SQLite server, an issue tracker, your own scripts). Config uses Claude Code's mcp.json format, read from two places; a workspace entry overrides a global one with the same name:
~/.meditor/mcp.json— global<workspace>/.meditor/mcp.json— per workspace
{
"mcpServers": {
"sqlite": { "command": "uvx", "args": ["mcp-server-sqlite", "--db", "./data.db"] },
"remote": { "url": "https://example.com/mcp" }
}
}command starts a stdio subprocess; url speaks streamable HTTP. Servers connect lazily, at the moment a run starts — nothing is spawned at app launch. A server that fails to connect is skipped rather than blocking the run, and its name goes into the system prompt so the model knows those tools are unavailable instead of inventing calls. Their tools register as mcp__<server>__<tool> and join the same loop, step budget and tool-definition budget as the built-in ones — but the work happens on the server's side, so MEditor's write confirmation and command sandbox do not apply to it. Trust the server, not the shell around it.
Workspace config is untrusted until you approve it. A .meditor/mcp.json cloned from someone else's repo could otherwise grant itself tool-calling capability on your machine, so the settings page shows the config hash and its server names, and requires an explicit grant per workspace; editing the file revokes it again. Malformed entries are skipped with a readable reason instead of failing the whole file.
Too many tools? Tool definitions are re-sent every turn, so a few servers can push the tool table past 40 entries. Above 32, MEditor scores MCP tools against your actual message and folds away the ones that clearly don't match; if nothing matches (a Chinese question against English tool descriptions, say) it sends everything rather than guessing.
MEditor/
├── Package.swift
├── scripts/
│ ├── bundle.sh # .app assembler
│ └── test.sh # Xcode-toolchain test runner
├── docs/ # Design specs & historical analyses
└── Sources/MEditor/
├── Models/ # EditorTab, FileItem, AgentTool, PluginSkill…
├── Protocols/ # FileService, SyntaxHighlight, AgentContext…
├── Services/
│ ├── AI/
│ │ ├── Agent/
│ │ │ ├── AgentRunner.swift # Multi-turn loop & state
│ │ │ ├── CommandSandbox.swift # Risk-tiered shell sandbox
│ │ │ ├── AgentHistoryBudget.swift # Context token budget
│ │ │ ├── AgentRunCheckpoint.swift # Run-level snapshot for one-click rollback
│ │ │ ├── AgentRunTrace.swift # Opt-in run trace (metadata only)
│ │ │ ├── AgentToolBudget.swift # Tool-definition budget when MCP floods the table
│ │ │ ├── Backends/
│ │ │ │ ├── AgentBackend.swift # Protocol + default streaming fallback
│ │ │ │ ├── RestAgentBackend.swift # OpenAI & Anthropic SSE
│ │ │ │ └── ClaudeCLIBackend.swift # claude CLI subprocess
│ │ │ └── Tools/ # Document, editor, workspace, shell tools
│ │ ├── InlineEditAction.swift # Selection-scoped edits with diff review (UI: Views/Editor/InlineEditBar, Views/Preview/PreviewInlineEditBar)
│ │ ├── AIConfiguration.swift # Providers, presets, messages & scenes
│ │ ├── AIService.swift # Chat completions & streaming client
│ │ └── BeautifyAgent.swift # Single-shot document polish
│ ├── MCP/ # MCP server (stdio) and client (stdio / streamable HTTP)
│ ├── Core/ # AppSettings, Localization, MarkdownFormatter
│ ├── File/ # FileService, FileWatcher, FileType config
│ └── Calendar/ # CalendarService (EventKit)
├── Skills/ # SKILL.md store, built-in skills, curated gallery, import/export
├── Managers/ # Tab, FileTree, Share, Template, Todo…
└── Views/
├── Agent/ # AgentStepView, AgentResultPanel
├── AI/ # AIAssistant, AtMention composer
├── Editor/ # NativeEditorView, TabBar, InlineEdit…
├── Preview/ # MarkdownWebPreview, TOC, ExportBar
├── Sidebar/ # FileSidebar, FileRow, TodoSidebar…
└── Shared/ # DesignTokens, ChromeButton, Toast…
Delivered
- Real Agent: 26 tools on macOS (14 cross-platform), multi-turn loop, three backends (OpenAI-compatible / Anthropic / Claude CLI), BYOK
- Agent hardening: write confirmation, risk-tiered sandbox, context budget, stall detection, parallel read-only tools, usage display
- Inline edit diff review,
@mentioncontext, multi-session history - Slash command library, selection action bar, auto-attached document context
- Image paste/drag auto-save, local history snapshots, tab context menu
- Pre-export diagnostics with one-click Agent fix, PDF export themes, copy as rich text
- Global search (⌘⇧F), Quick Look plugin, Spotlight indexing & Shortcuts intents
- LAN share + Cloudflare online publish (free tier + Pro)
- iOS companion (iCloud edit / chat / publish)
- Presentation & focus modes, HTML export, preview themes, Sparkle auto-update
- MCP both ways — server for Claude Desktop / Cursor, client for external stdio & HTTP servers
- Skills —
SKILL.mdplaybooks with progressive disclosure (built-in set + 8-skill curated gallery), import/export as one Markdown file - Onboarding — first-launch guide, zero-config Claude CLI, scripted demo
- One-click rollback of an Agent run (run-level checkpoints)
- Diff review by default for Agent writes, refine loop, resume interrupted runs
- Post-write self-check diagnostics, prompt caching, per-run cost transparency
- Opt-in run traces (tool sequence / success / timing / tokens, metadata only) and a tool-definition budget for MCP-heavy setups
- LaTeX math rendering in the preview (KaTeX, offline-bundled)
Later
- Multi-window support
- Editor font/size preferences
- Developer-ID signing + notarization (removes the first-launch right-click)
PRs welcome. main is protected — CI must be green to merge.
- CONTRIBUTING.md — the human process: toolchain, what to run before opening a PR, the release flow.
- AGENTS.md — the code conventions (layering, the iOS shared kernel, Swift 6 concurrency rules, version sources). Also the entry point for AI coding agents.
- docs/engineering-practices.md — why the four quality gates exist and how to run them locally.
- docs/ai-roadmap.md — what the AI side deliberately does not do, what already works, and the milestone order. Read this before proposing a feature.
- CHANGELOG.md — released changes.
- SECURITY.md — report vulnerabilities privately, not in a public issue.
Built with Swift and SwiftUI on macOS


