skribe-editor
v0.5.1
Published
Local-first Markdown writing and review workbench with AI chat, comments, and reviewable diffs.
Downloads
1,837
Readme
Skribe
Local-first Markdown writing with an AI review partner.
Skribe gives you an editable Markdown canvas, anchored comment threads, chat, reviewable diffs, Flow mode, revision history, and clean Markdown export. The document stays local. Review state stays local.
Bring your own AI. Skribe uses the native agent CLIs you already have installed, such as Codex CLI or Claude Code, or any OpenAI-compatible local inference server you run yourself (Ollama, LM Studio, llama.cpp, vLLM, and similar). If your CLI is signed in or your local server is running, Skribe can use it.

Why Skribe?
Most AI writing tools treat long-form editing as a chat transcript. That gets clunky fast.
Skribe is built around the document:
- Editable rendered Markdown canvas with headings, links, images, lists, quotes, code, GFM tables, copy/paste, and keyboard shortcuts.
- Flow mode for distraction-free drafting with the app chrome, sidebars, toolbar, threads, chat, and agent UI hidden.
- Anchored comment threads for paragraph-level or selection-level review.
- Document chat for broader passes, structural edits, and agent collaboration.
- Agent skills for reusable writing passes such as voice, humanising, copyediting, newsletter review, or any local skill your CLI runtime already knows about.
- Reviewable diffs in split or unified view, so agent edits can be accepted, declined, or revised before they touch the draft.
- Per-document context memory so previous comments, decisions, accepted changes, and revision requests stay available to the agent.
- Local-only storage for the Markdown file, review state, settings, revisions, and sidecars.
- Local image assets from inserted, pasted, or dropped images, stored beside the active Markdown document and referenced with normal Markdown image syntax.
- Provider-agnostic agent runtime with support for Codex CLI, Claude Code, local OpenAI-compatible inference, or automatic runtime selection using your existing CLI authentication, local models, or provider plan.
Threads vs Chat
Skribe has two agent conversation surfaces because they serve different editorial jobs.
| Surface | Use it for | What the agent sees | | --- | --- | --- | | Threads | Anchored comments on selected text, paragraph-level rewrites, local clarification, and focused suggestions. | The selected passage, the thread history, relevant document context, and previous decisions. | | Chat | Article-level discussion, broad review passes, structural edits, skill-driven rewrites, and document-level diffs. | The wider document, chat history, context memory, open proposals, thread decisions, and selected skills. |
Use Threads when the question belongs to a specific passage. Use Chat when the question belongs to the whole draft.
Skills
Skribe treats skills as reusable instructions for the agent.
It discovers local SKILL.md files from:
~/.agents/skills~/.claude/skills~/.codex/skills~/.codex/plugins/cache- Any extra directories listed in
SKRIBE_SKILL_ROOTS
Skills show up in the chat and thread composers. You can click Skills to browse them, type /skill-name for autocomplete, or set favourite defaults in Settings.
When you send a message, Skribe passes the selected skills to the active agent runtime. Cloud CLIs load skills through their native skill support. The local inference runtime inlines selected SKILL.md bodies into the prompt because there is no CLI skill loader on that path.
Skills describe how the agent should work. The current surface decides where that work should focus.
| Surface | Skill scope | | --- | --- | | Thread | The anchored selection or passage is the focus. The agent also sees enough document context and previous decisions to make the local edit fit. | | Chat | The whole draft is the default focus, especially for broad review passes, structural rewrites, and skill-only requests. |
For example, /humanizer in a thread should humanise the selected passage and usually return a focused reply or replacement suggestion. /humanizer in Chat should treat the current draft as the writing context and, when useful, return a reviewable document proposal. Your wording can narrow or widen the scope: "use /humanizer on the intro only" keeps the edit focused, while "use /humanizer across the whole piece" asks for a broader pass.
Default skills are just preselected skills for new chat messages and new comment threads. They do not run in the background; they apply only when you send that message.
You can also send a skill by itself:
/humanizerIn that case Skribe asks the agent to apply the skill to the current writing context and return reviewable suggestions or document proposals when edits are useful.
To add another skill directory:
SKRIBE_SKILL_ROOTS=~/company/skills:~/personal/skills skribe ~/draft.mdScreenshots
Editor
Rendered Markdown stays editable, with a compact toolbar and collapsible sidebars.

Flow Mode
Hide the UI and AI surfaces when you just want to write. Flow mode leaves the editable canvas visible with a small exit button, and Escape returns you to the full review workspace.

Threads
Anchor comments to highlighted passages, discuss the local edit, and review focused replacement suggestions.

Chat
Use chat for article-level discussion, skill-driven passes, and reviewable document diffs.

Diff Review
Review proposed document changes inline, then accept, decline, rewrite, or comment on each change block. Choose split view for side-by-side current/proposed text, or unified view for compact - and + lines.

Settings
Persist writing preferences, theme, document font, agent runtime, model, effort, local inference endpoint settings, default skills, diff view, and workspace defaults.

Quick Start
Requirements:
- Node.js 20 or newer
npm- Optional: Codex CLI or Claude Code if you want live agent replies from your existing provider subscription
- Optional: any OpenAI-compatible local inference server if you want on-device agent replies without a cloud CLI
Run without installing:
npx skribe-editor ~/draft.mdOr install globally:
npm install -g skribe-editor
skribe ~/draft.mdSkribe starts a local server, opens your browser automatically, and prints the app URL as the last line of output.
CLI Commands
Common commands:
skribe [file.md] # open a Markdown file or the default local draft
skribe open [file.md] # same as skribe [file.md]
skribe --version # print the installed version
skribe --help # show CLI helpOptions (since 0.1.6):
skribe --create newdoc.md # create the file if it does not exist yet
skribe -c newdoc.md # short form of --create
skribe --no-open ~/draft.md # start without opening the browserIf you open a file that does not exist yet, Skribe asks in the terminal:
Create new document at /home/you/newdoc.md? [Y/n]Press Enter or y to create it. Skribe seeds the file with a heading based on the filename, then opens it in the browser. Press n to cancel.
In non-interactive contexts such as scripts or CI, pass --create explicitly. Without it, Skribe prints the resolved path and exits with a clear error instead of creating the file silently.
Disable browser auto-open for a single run with --no-open, or for every run with SKRIBE_NO_OPEN_BROWSER=1.
Diagnostics:
skribe doctor # check storage, build assets, port, runtimes, and skills
skribe runtimes # list detected agent CLIs, models, and effort support
skribe skills # list discovered local skills
skribe config # print resolved local paths and defaults
skribe status # show the running local app statusUtility:
skribe export # active Skribe document -> stdout
skribe export ~/draft.md # ~/draft.md -> stdout
skribe export --out clean.md # active Skribe document -> clean.md
skribe export ~/draft.md --out clean.md # ~/draft.md -> clean.mdOpening Multiple Documents
Skribe runs one local app instance and keeps one document open at a time.
The first command starts the local server:
skribe ~/draft-one.mdIf Skribe is already running, another command hands the new file to the existing server instead of starting a second server:
skribe ~/draft-two.mdThe existing browser tab switches to draft-two.md, and the second terminal command exits after printing the running app URL. The previous document is saved before the switch, and each Markdown file keeps its own comments, chat, proposals, revisions, and agent session sidecar state.
If an agent turn is running, Skribe will not switch documents until that turn finishes. This prevents an agent response from being applied to the wrong draft.
If port 4327 is already used by another app, Skribe prints a clear port conflict message. Use a different port if needed:
PORT=4330 skribe ~/draft.mdRun From Source
Clone and run:
git clone [email protected]:devtunehq/skribe.git
cd skribe
npm install
npm run build
npm run serveOpen http://127.0.0.1:4327.
Open A Markdown File
Run Skribe against a specific file:
npm run serve -- ~/draft.mdOr link the local CLI:
npm link
skribe ~/draft.mdRelative paths resolve from the directory where you run the command, including with npx:
cd ~/writing
npx skribe-editor chapter-one.mdThat opens or offers to create ~/writing/chapter-one.md, not a path inside node_modules.
To start a brand-new file in the current directory:
skribe --create newdoc.mdSkribe keeps the .md file as the clean document source. Comments, chat, proposals, revisions, and agent memory are stored in a sidecar directory under the local Skribe config directory.
Agent Runtime
Skribe can use:
- Cloud agent CLIs — Codex CLI and Claude Code
- Local inference — any OpenAI-compatible server on your machine
- Auto — pick the first healthy runtime from your configured priority order
Skribe does not hard-code one model provider. For cloud CLIs, your normal setup still owns authentication, model access, limits, and billing. For local inference, Skribe talks directly to a server you run separately. Skribe does not bundle model weights or GPU binaries.
Configure in Settings
Most runtime options belong in the app, not in shell environment variables.
Open Settings → Agent (or use the sparkles provider pill in the header) to choose:
| Setting | What it does |
| --- | --- |
| Agent provider | Auto, Codex CLI, Claude Code, or Local inference |
| Agent model | Default model for the selected runtime, or Default model to let the runtime decide |
| Agent effort | Reasoning effort where the selected CLI supports it |
| Local inference → Base URL | OpenAI-compatible base URL such as http://127.0.0.1:11434/v1. Leave blank to auto-detect common local servers |
| Local inference → API key | Optional bearer token for servers that require auth |
| Local inference → Max completion tokens | Output budget per agent turn. Defaults to 4096. Does not enlarge the model context window |
Settings are saved to ~/.config/skribe/settings.json. Environment variables still override the UI when set, which is useful for scripts, CI, or one-off launches.
Supported runtime values:
auto— pick the first healthy runtime fromSKRIBE_AGENT_RUNTIME_PRIORITYcodex— use Codex CLIclaude— use Claude Codelocal— use an OpenAI-compatible local inference serverstub— deterministic local responses for development and tests
If no cloud CLI or local inference server is detected, Skribe still works as a local Markdown editor with threads, revisions, settings, and export. Agent chat and comment replies are disabled until a supported runtime is available. Run skribe doctor or skribe runtimes to see what Skribe can detect.
Example shell overrides:
SKRIBE_AGENT_RUNTIME=auto npm run serve -- ~/draft.md
SKRIBE_AGENT_RUNTIME=claude SKRIBE_AGENT_MODEL=opus SKRIBE_AGENT_EFFORT=high npm run serve -- ~/draft.md
SKRIBE_AGENT_RUNTIME=codex SKRIBE_AGENT_MODEL=gpt-5 npm run serve -- ~/draft.md
SKRIBE_AGENT_RUNTIME=local SKRIBE_LOCAL_BASE_URL=http://127.0.0.1:11434/v1 npm run serve -- ~/draft.mdLocal inference
Skribe's local runtime sends each agent turn to an OpenAI-compatible /v1/chat/completions endpoint. Any server that implements that API can work — Ollama is a common example, but LM Studio, llama.cpp server, vLLM, LocalAI, and similar tools are supported the same way.
Example: Ollama
# 1. Pull a model (instruction-tuned models with 32k+ context work best)
ollama pull qwen2.5:14b-instruct
# 2. Start Ollama with enough context for long drafts and skills
OLLAMA_NUM_CTX=32768 ollama serve
# 3. Open Skribe
skribe ~/draft.mdThen in Skribe:
- Open Settings → Agent
- Set Agent provider to Local inference
- Leave Base URL blank to use Ollama at
http://127.0.0.1:11434/v1, or enter your endpoint explicitly - Set Max completion tokens to
8192if replies truncate to a word or two - Choose a model from the header model picker if you want something other than the server default
- Save
You can also attach skills in chat or thread composers the same way as with cloud CLIs. Skribe inlines selected SKILL.md instructions into the local prompt.
Other OpenAI-compatible servers
Skribe does not require Ollama. Point Settings → Agent → Base URL at any compatible endpoint, for example:
| Server | Typical base URL |
| --- | --- |
| Ollama | http://127.0.0.1:11434/v1 |
| LM Studio | http://127.0.0.1:1234/v1 |
| llama.cpp server | http://127.0.0.1:8080/v1 |
| Custom / remote gateway | your server's /v1 base URL |
If Base URL is blank, Skribe auto-probes the Ollama, LM Studio, and llama.cpp defaults above and uses the first reachable server.
Model selection
When Local inference is active, the header model picker lists models returned by the server's /v1/models endpoint. Choose Default model to use the first model the server reports.
Skills on the local path
Cloud CLIs load skills through their own runtime. The local path has no CLI skill loader, so Skribe reads selected SKILL.md files from your configured skill roots and inlines them into the agent prompt. Keep skill count modest on smaller local models — three large skills can consume context quickly even with Skribe's local prompt budget.
Quality tips
Local models vary in JSON compliance, context size, and instruction following. These settings usually help:
- Use an instruction-tuned model with 32k+ context when possible
- For Ollama, set
OLLAMA_NUM_CTX=32768beforeollama serve— this is separate from Max completion tokens in Settings - In Settings → Agent, raise Max completion tokens to
8192only after the server context window is large enough - Start with one skill per turn until the model proves reliable
- Prefer threads for focused rewrites; use chat for whole-draft passes
Skribe skips strict JSON response mode by default for local models and automatically retries with a compact prompt if the first reply is empty or incomplete.
Troubleshooting
| Symptom | Likely fix |
| --- | --- |
| Local inference unavailable in Settings | Start your inference server, or set Base URL to the correct /v1 endpoint |
| Replies truncate to a word or two | Raise your inference server context window (for Ollama: OLLAMA_NUM_CTX=32768), then raise Max completion tokens in Settings → Agent |
| empty completion or agent run failed after a long wait | Skribe now skips JSON response mode by default and retries with a compact prompt. Also reduce skills and raise server context |
| incomplete response after raising max completion tokens | Usually a context-window issue, not output tokens. Use fewer skills and raise server context (OLLAMA_NUM_CTX=32768) |
| Wrong model used | Pick the model explicitly in the header model picker |
| Custom server not detected | Enter its full base URL in Settings → Agent → Base URL |
Run skribe runtimes to confirm Skribe can reach your endpoint and list models.
Environment variable overrides
These override Settings → Agent when set:
| Variable | Purpose |
| --- | --- |
| SKRIBE_AGENT_RUNTIME | auto, codex, claude, local, or stub |
| SKRIBE_AGENT_RUNTIME_PRIORITY | Comma-separated runtime order for auto. Defaults to codex,claude,local |
| SKRIBE_AGENT_MODEL | Model id, or auto to let the selected runtime decide |
| SKRIBE_AGENT_EFFORT | Reasoning effort where supported |
| SKRIBE_AGENT_TIMEOUT_MS | Agent command timeout. Defaults to 10 minutes |
| SKRIBE_LOCAL_BASE_URL | OpenAI-compatible base URL for the local runtime |
| SKRIBE_LOCAL_API_KEY | Optional bearer token for local inference servers that require auth |
| SKRIBE_LOCAL_MAX_TOKENS | Max completion tokens for local inference requests. Defaults to 4096 |
Other useful environment variables:
| Variable | Purpose |
| --- | --- |
| PORT | Server port. Defaults to 4327. |
| SKRIBE_NO_OPEN_BROWSER | Set to 1 to disable browser auto-open on startup. |
| SKRIBE_CONFIG_DIR | Config root. Defaults to ~/.config/skribe. |
| SKRIBE_DATA_DIR | Local document/review storage root. Defaults to ~/.config/skribe/data. |
| SKRIBE_DOCUMENT / SKRIBE_DOCUMENT_PATH | Markdown file to open when no CLI path is passed. |
| SKRIBE_SKILL_ROOTS | Additional skill roots for slash-command style skills. |
You can also change language, document font, theme, tone, default skills, split/unified diff view, and review preferences from the settings panel.
Storage Model
Skribe keeps one active document in memory for fast editing, then checkpoints to disk.
Default local document:
~/.config/skribe/data/docs/default/draft.md
~/.config/skribe/data/docs/default/review.json
~/.config/skribe/data/docs/default/session.json
~/.config/skribe/data/docs/default/snapshots/External Markdown file:
~/draft.md
~/.config/skribe/data/external/<doc-id>/review.json
~/.config/skribe/data/external/<doc-id>/session.json
~/.config/skribe/data/external/<doc-id>/snapshots/Global settings live at:
~/.config/skribe/settings.jsonDevelopment
Run the server:
npm run serveRun the Vite dev server separately:
npm run devValidation:
npm test
npm run typecheck
npm run buildProject Status
Skribe is early and intentionally local-first. It is useful today for Markdown drafting and AI-assisted review, but the editor and agent workflow are still being hardened. The source is public for transparency and local use, but external contributions are not open at this stage.
License
MIT. See LICENSE.
