Vibecode Sudowrite
track this build5 phases, 15 steps, beginner friendly0%The core loop is buildable, but a dependable replacement becomes a real weekend or multi-day project. For Sudowrite, brainstorm fiction, expand scenes, inspect story context, and generate constrained prose suggestions. The hard boundary is its fiction-tuned workflows, context handling, and model orchestration, plus polish, integrations, and proprietary language models.
You are building a lean indie version of Sudowrite. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # Sudowrite · indie build An editor over your own Markdown files with a story bible it builds from the text, retrieval that assembles relevant context instead of sending the whole manuscript, suggestions shown as a diff you accept or reject, and a per-request record of exactly what was sent and what it cost. Estimated effort: **weekend**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | TypeScript with a desktop shell | local files, a real editor and no browser storage limits | | Files | Plain Markdown, one file per scene or chapter | your manuscript must outlive this application and open in anything | | Index | SQLite with full text search over scenes and entities | retrieval needs an index; the files remain the truth | | Models | Any provider behind one adapter, plus a local option | the choice between a hosted model and a local one is a privacy decision the writer should make, not you | | Diffs | Word-level diff shown before anything is applied | a suggestion you cannot inspect is a suggestion you cannot trust | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the build and the desktop shell. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **A real manuscript of at least twenty thousand words** · free - Why: Context assembly, extraction and retrieval are all trivial on a short story and revealing on a novel. The budget problem only exists at length. - Get it: Use your own work in progress, or a public domain novel split into scene files for testing. - Verify: The manuscript loads and its scene count and word count are reported - [ ] **A model API key, or Ollama for local** · pay per token; a paragraph rewrite is a fraction of a cent and a long session is more than you expect - Why: Suggestions need a model. Phases 1 to 3 work entirely without one, which is deliberate. - Get it: Create an account with a provider and set a spend limit when you create the key. Or install Ollama and pull a model to keep everything local. - Verify: A test request returns text and its cost is recorded - [ ] **A decision about whether your manuscript may leave your machine** · free - Why: This is the question the whole build is arranged around, and it is the writer's to answer rather than the tool's to assume. - Get it: Read your chosen provider's terms on training and retention before Phase 4. If the answer is no, use the local model path and accept lower quality. - Verify: The decision and the terms you read are recorded with a date - [ ] **A backup of the manuscript folder** · free - Why: These are plain files and this application writes to them. A backup is the safety net that atomic saves do not replace. - Get it: Version control the manuscript folder, which also gives you history, or ensure your ordinary backup covers it. - Verify: The folder is in a backup that has been restored from at least once ## Quick start Follow Phase 1 in `BUILD_PLAN.md`; it creates the project. Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Fiction-tuned workflows and accumulated prompt craft. - Cross-device manuscript sync. - Large proprietary style models. - Any claim that the output is publishable prose. - its fiction-tuned workflows, context handling, and model orchestration - cross-device sync - large proprietary style models - browser and office-suite extensions - team style guides If one of those is essential to you, that is the reason to keep paying for Sudowrite, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Sudowrite The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a local fiction writing assistant that reads your manuscript from disk and never uploads it without asking to replace Sudowrite. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Stack (fixed, do not substitute) - Runtime: TypeScript with a desktop shell · local files, a real editor and no browser storage limits - Files: Plain Markdown, one file per scene or chapter · your manuscript must outlive this application and open in anything - Index: SQLite with full text search over scenes and entities · retrieval needs an index; the files remain the truth - Models: Any provider behind one adapter, plus a local option · the choice between a hosted model and a local one is a privacy decision the writer should make, not you - Diffs: Word-level diff shown before anything is applied · a suggestion you cannot inspect is a suggestion you cannot trust ### Data model (create this before Phase 1) - manuscripts(id, path, title, updated_at) - scenes(id, manuscript_id, path, ordinal, title, word_count, content_hash) - entities(id, manuscript_id, kind, name, description, first_seen_scene_id) · kind is character, place, object or thread - mentions(entity_id, scene_id, count) · which scenes an entity appears in, which is how context is assembled - requests(id, scene_id, at, purpose, model, prompt_tokens, completion_tokens, cost_usd, context_scene_ids) - suggestions(id, request_id, original_text, suggested_text, state) · state is pending, accepted or rejected - The requests table records which scenes were sent. A writer should always be able to answer what left their machine. ### Phase 1 · Editing your own files Build: Open a folder of Markdown files as a manuscript. A distraction-light editor with word count and scene navigation. Write with debounce and atomic replace. Watch for external edits so another editor can be used on the same files. Trap: Writing directly over a file means a crash mid-save can truncate a chapter, and for a writer that is the worst possible failure. Write to a temporary file and rename over the target, which is atomic and cannot leave a half-written manuscript. Done when: Typing and quitting immediately leaves the text on disk. Editing a file in another editor updates the view without losing cursor position. Killing the process during a save leaves either the old file or the new one, never a truncated one. Scene order follows a documented convention rather than filesystem order. Do not build yet: No model calls at all. A reliable editor first. ### Phase 2 · A story bible built from the text Build: Extract candidate characters, places and objects from the manuscript, present them for confirmation, and track which scenes each appears in. Let the writer correct and add entries. Store it as a Markdown file in the manuscript folder so it travels with the book. Trap: Automatic extraction will confidently invent a character from a repeated phrase and miss one introduced only by a nickname. Present extraction as suggestions requiring confirmation rather than as facts, and make manual entry as fast as accepting a suggestion. Done when: Extraction over a real manuscript proposes characters that are mostly correct, and every proposal requires confirmation. A manually added entity is never overwritten by a later extraction. The bible is a readable Markdown file in the manuscript folder. Mentions correctly list the scenes an entity appears in. Do not build yet: No suggestions and no model calls for prose yet. ### Phase 3 · Context assembled, not dumped Build: Assemble context for a request: the current scene, the previous scene, bible entries for entities present, and the most relevant earlier scenes by search. Show the assembled context before sending, and record which scenes were included. Trap: Sending the whole manuscript with every request is expensive, slow, and worse for quality than a focused selection, because the relevant paragraph is buried. It also means the entire book leaves the machine on every keystroke-triggered suggestion, which is a privacy decision made by accident. Done when: A request's assembled context is visible before sending and lists the scenes included. Context stays within a configured token budget regardless of manuscript length. The requests table records exactly which scenes were sent. Sending is refused if the budget would be exceeded rather than truncating silently mid-scene. Do not build yet: No suggestions applied to the text yet. ### Phase 4 · Suggestions as diffs Build: Suggestion actions such as expand, rewrite and describe. Present every result as a word-level diff against the original, with accept, reject and accept-in-part. Keep rejected suggestions so a writer can revisit one. Trap: Applying a suggestion directly into the text and relying on undo loses the original as soon as anything else is typed, and a writer who accepts a rewrite and then dislikes it has no way back. Keep the original on the suggestion row, so accepted suggestions remain reversible for as long as the record exists. Done when: Every suggestion is shown as a diff before it can be applied. Accepting part of a suggestion applies only that part. A rejected suggestion is retrievable afterwards. The original text is recoverable from an accepted suggestion after further editing. Do not build yet: No cost tracking or local model yet. ### Phase 5 · Cost, and the local option Build: Record tokens and cost per request. Show a running total per session and per manuscript. Add a local model option through Ollama, and make the choice between local and hosted prominent rather than buried in settings. Trap: A per-request cost of a fraction of a cent feels free and adds up quickly during a writing session where you try twenty variations of a paragraph. Show the session total in the interface rather than only in a settings pane, because that number changes behaviour and a monthly bill does not. Done when: Each request records tokens and cost, and the session total is visible while writing. Switching to a local model produces suggestions with no network request, verified with the network disabled. The interface states clearly which model is active and whether text leaves the machine. Do not build yet: Export and distribution come last. ### Phase 6 · Export, backup and the ownership question Build: Export to Markdown, which is already the storage format, plus a compiled document for submission. A request log the writer can read and export. A clear statement of which provider was used and what their terms say about training on submitted text. Trap: Provider terms on whether submitted text may be used for training vary and change, and for an unpublished manuscript that question matters more than it does for most content. Record which provider and which terms were in effect, with the date, rather than assuming the writer checked. Done when: The manuscript exports as Markdown and as a compiled document with correct scene order. The request log exports and shows what was sent, when, to which provider. The README records each supported provider's data use terms with the date checked. Deleting a manuscript's request log is possible and documented. Do not build yet: Nothing after this. ### Out of scope (and why) - The fiction-tuned workflows and prompt craft behind the commercial product, which are the accumulated result of watching many writers work. - Cross-device sync of manuscripts, which is a file sync problem you already have solutions for. - Large proprietary style models trained on fiction specifically. - Any claim that the output is publishable prose. It is a drafting aid, and the diff view exists because you will reject most of it. ### README must contain - That manuscripts are plain Markdown files and the application is not required to read them - That saves are atomic and a crash cannot truncate a chapter - That context is assembled rather than dumped, and how to see exactly what is sent - That every suggestion is a diff and originals are preserved beyond the undo stack - Which providers are supported, what their terms say about training, and when that was checked ===== AGENTS.md ===== # Agent instructions · Sudowrite indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: TypeScript with a desktop shell, Plain Markdown, one file per scene or chapter, SQLite with full text search over scenes and entities, Any provider behind one adapter, plus a local option, Word-level diff shown before anything is applied. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Writing directly over a file means a crash mid-save can truncate a chapter, and for a writer that is the worst possible failure. Write to a temporary file and rename over the target, which is atomic and cannot leave a half-written manuscript. - Automatic extraction will confidently invent a character from a repeated phrase and miss one introduced only by a nickname. Present extraction as suggestions requiring confirmation rather than as facts, and make manual entry as fast as accepting a suggestion. - Sending the whole manuscript with every request is expensive, slow, and worse for quality than a focused selection, because the relevant paragraph is buried. It also means the entire book leaves the machine on every keystroke-triggered suggestion, which is a privacy decision made by accident. - Applying a suggestion directly into the text and relying on undo loses the original as soon as anything else is typed, and a writer who accepts a rewrite and then dislikes it has no way back. Keep the original on the suggestion row, so accepted suggestions remain reversible for as long as the record exists. - A per-request cost of a fraction of a cent feels free and adds up quickly during a writing session where you try twenty variations of a paragraph. Show the session total in the interface rather than only in a settings pane, because that number changes behaviour and a monthly bill does not. ===== BUILD_PLAN.md ===== # Build plan · Sudowrite An editor over your own Markdown files with a story bible it builds from the text, retrieval that assembles relevant context instead of sending the whole manuscript, suggestions shown as a diff you accept or reject, and a per-request record of exactly what was sent and what it cost. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Editing your own files You can write comfortably, and the files stay ordinary Markdown. ### Steps 1. Open a folder as a manuscript Scene order from a filename convention you document. Filesystem order is not stable and will reorder your book. Files: `src/manuscript.ts` 2. Save atomically with debounce Temporary file plus rename. Test by killing the process mid-save. Files: `src/storage.ts` 3. Watch for external edits Hash what you wrote and ignore your own events, so the watcher does not fight the editor. Files: `src/watcher.ts` ### Done when - [ ] Text typed and immediately quit is on disk - [ ] An external edit appears without losing cursor position - [ ] A killed save leaves a complete file - [ ] Scene order follows the documented convention ### Watch out - Writing directly over a file means a crash mid-save can truncate a chapter, and for a writer that is the worst possible failure. Write to a temporary file and rename over the target, which is atomic and cannot leave a half-written manuscript. ## Phase 2 · A story bible built from the text The application knows who is in your book without you filling in a form. ### Steps 1. Extract candidates Proper nouns with frequency and first appearance. A model pass can improve it, but the deterministic pass is the baseline. Files: `src/bible/extract.ts` 2. Require confirmation Nothing enters the bible without the writer saying so. Manual entries are marked and never overwritten. Files: `src/bible/confirm.ts` 3. Store as Markdown in the folder It travels with the manuscript and is readable without this application, which is the point. Files: `src/bible/write.ts` ### Done when - [ ] Extraction proposes mostly correct characters on a real manuscript - [ ] Every proposal requires confirmation - [ ] A manual entry survives re-extraction - [ ] The bible is readable Markdown in the manuscript folder ### Watch out - Automatic extraction will confidently invent a character from a repeated phrase and miss one introduced only by a nickname. Present extraction as suggestions requiring confirmation rather than as facts, and make manual entry as fast as accepting a suggestion. ## Phase 3 · Context assembled, not dumped A request carries what is relevant, and you can see exactly what was sent. ### Steps 1. Assemble by relevance Current and previous scene always, bible entries for present entities, then search results until the budget is reached. Files: `src/context.ts` 2. Show what will be sent The actual assembled text, not a summary of it. A writer deciding whether to use a hosted model needs to see this. Files: `src/ui/ContextPreview.tsx` 3. Record it Scene identifiers on the request row. This is the audit trail that makes the privacy claim checkable. Files: `src/requests.ts` ### Done when - [ ] Assembled context is visible before sending - [ ] Context stays within the token budget at any manuscript length - [ ] Included scenes are recorded on the request - [ ] Exceeding the budget refuses rather than truncating mid-scene ### Watch out - Sending the whole manuscript with every request is expensive, slow, and worse for quality than a focused selection, because the relevant paragraph is buried. It also means the entire book leaves the machine on every keystroke-triggered suggestion, which is a privacy decision made by accident. ## Phase 4 · Suggestions as diffs Nothing changes your text without you seeing exactly what would change. ### Steps 1. Implement suggestion actions Expand, rewrite, describe and continue. Each is a prompt template plus the assembled context. Files: `src/actions/` 2. Diff at word level Sentence-level diffs on prose are unreadable. Word level with sentence grouping is what a writer can actually judge. Files: `src/diff.ts` 3. Preserve originals On the suggestion row, so an accepted change stays reversible beyond the undo stack. Files: `src/suggestions.ts` ### Done when - [ ] Every suggestion is shown as a diff first - [ ] Partial acceptance applies only the selected part - [ ] A rejected suggestion can be retrieved - [ ] The original is recoverable after further editing ### Watch out - Applying a suggestion directly into the text and relying on undo loses the original as soon as anything else is typed, and a writer who accepts a rewrite and then dislikes it has no way back. Keep the original on the suggestion row, so accepted suggestions remain reversible for as long as the record exists. ## Phase 5 · Cost, and the local option You know what each request cost, and you can choose to send nothing off the machine. ### Steps 1. Record cost per request Tokens in and out, priced by model. Aggregate by session and by manuscript. Files: `src/cost.ts` 2. Add a local model option Through a local endpoint. Quality will be lower and privacy absolute, which is a trade the writer should make knowingly. Files: `src/models/local.ts` 3. Make the active model visible Persistently in the interface, with whether text leaves the machine stated plainly. Files: `src/ui/ModelBadge.tsx` ### Done when - [ ] Every request records tokens and cost - [ ] The session total is visible while writing - [ ] Local model suggestions work with networking disabled - [ ] The interface states whether text leaves the machine ### Watch out - A per-request cost of a fraction of a cent feels free and adds up quickly during a writing session where you try twenty variations of a paragraph. Show the session total in the interface rather than only in a settings pane, because that number changes behaviour and a monthly bill does not. ## Not in this build - Fiction-tuned workflows and accumulated prompt craft. - Cross-device manuscript sync. - Large proprietary style models. - Any claim that the output is publishable prose. ## After v1, if you want it - Embedding-based retrieval, once full text search starts missing thematically relevant scenes - A consistency check that flags a character described differently in two places - Per-character voice notes fed into the context for dialogue suggestions ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. The folder of Markdown files. Your data, in your backup. MANUSCRIPT_PATH=~/Writing/Novel # Required. Search index and request log. Deliberately outside the manuscript folder so syncing cannot corrupt it. INDEX_PATH=~/.local/share/write/index.db # Required. Which adapter is active. Local means nothing leaves the machine. MODEL_PROVIDER=local # Optional · secret. Only needed for a hosted provider. Set a spend limit at the provider as well. MODEL_API_KEY=sk-... # Required. Maximum assembled context. Exceeding it refuses rather than truncating. CONTEXT_TOKEN_BUDGET=8000 # Required. Session spend at which a warning appears. Visible while writing rather than in settings. SESSION_COST_WARN_USD=2
You are building a lean indie version of Sudowrite. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # Sudowrite · indie build An editor over your own Markdown files with a story bible it builds from the text, retrieval that assembles relevant context instead of sending the whole manuscript, suggestions shown as a diff you accept or reject, and a per-request record of exactly what was sent and what it cost. Estimated effort: **weekend**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | TypeScript with a desktop shell | local files, a real editor and no browser storage limits | | Files | Plain Markdown, one file per scene or chapter | your manuscript must outlive this application and open in anything | | Index | SQLite with full text search over scenes and entities | retrieval needs an index; the files remain the truth | | Models | Any provider behind one adapter, plus a local option | the choice between a hosted model and a local one is a privacy decision the writer should make, not you | | Diffs | Word-level diff shown before anything is applied | a suggestion you cannot inspect is a suggestion you cannot trust | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the build and the desktop shell. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **A real manuscript of at least twenty thousand words** · free - Why: Context assembly, extraction and retrieval are all trivial on a short story and revealing on a novel. The budget problem only exists at length. - Get it: Use your own work in progress, or a public domain novel split into scene files for testing. - Verify: The manuscript loads and its scene count and word count are reported - [ ] **A model API key, or Ollama for local** · pay per token; a paragraph rewrite is a fraction of a cent and a long session is more than you expect - Why: Suggestions need a model. Phases 1 to 3 work entirely without one, which is deliberate. - Get it: Create an account with a provider and set a spend limit when you create the key. Or install Ollama and pull a model to keep everything local. - Verify: A test request returns text and its cost is recorded - [ ] **A decision about whether your manuscript may leave your machine** · free - Why: This is the question the whole build is arranged around, and it is the writer's to answer rather than the tool's to assume. - Get it: Read your chosen provider's terms on training and retention before Phase 4. If the answer is no, use the local model path and accept lower quality. - Verify: The decision and the terms you read are recorded with a date - [ ] **A backup of the manuscript folder** · free - Why: These are plain files and this application writes to them. A backup is the safety net that atomic saves do not replace. - Get it: Version control the manuscript folder, which also gives you history, or ensure your ordinary backup covers it. - Verify: The folder is in a backup that has been restored from at least once ## Quick start Follow Phase 1 in `BUILD_PLAN.md`; it creates the project. Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Fiction-tuned workflows and accumulated prompt craft. - Cross-device manuscript sync. - Large proprietary style models. - Any claim that the output is publishable prose. - its fiction-tuned workflows, context handling, and model orchestration - cross-device sync - large proprietary style models - browser and office-suite extensions - team style guides If one of those is essential to you, that is the reason to keep paying for Sudowrite, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Sudowrite The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a local fiction writing assistant that reads your manuscript from disk and never uploads it without asking to replace Sudowrite. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Stack (fixed, do not substitute) - Runtime: TypeScript with a desktop shell · local files, a real editor and no browser storage limits - Files: Plain Markdown, one file per scene or chapter · your manuscript must outlive this application and open in anything - Index: SQLite with full text search over scenes and entities · retrieval needs an index; the files remain the truth - Models: Any provider behind one adapter, plus a local option · the choice between a hosted model and a local one is a privacy decision the writer should make, not you - Diffs: Word-level diff shown before anything is applied · a suggestion you cannot inspect is a suggestion you cannot trust ### Data model (create this before Phase 1) - manuscripts(id, path, title, updated_at) - scenes(id, manuscript_id, path, ordinal, title, word_count, content_hash) - entities(id, manuscript_id, kind, name, description, first_seen_scene_id) · kind is character, place, object or thread - mentions(entity_id, scene_id, count) · which scenes an entity appears in, which is how context is assembled - requests(id, scene_id, at, purpose, model, prompt_tokens, completion_tokens, cost_usd, context_scene_ids) - suggestions(id, request_id, original_text, suggested_text, state) · state is pending, accepted or rejected - The requests table records which scenes were sent. A writer should always be able to answer what left their machine. ### Phase 1 · Editing your own files Build: Open a folder of Markdown files as a manuscript. A distraction-light editor with word count and scene navigation. Write with debounce and atomic replace. Watch for external edits so another editor can be used on the same files. Trap: Writing directly over a file means a crash mid-save can truncate a chapter, and for a writer that is the worst possible failure. Write to a temporary file and rename over the target, which is atomic and cannot leave a half-written manuscript. Done when: Typing and quitting immediately leaves the text on disk. Editing a file in another editor updates the view without losing cursor position. Killing the process during a save leaves either the old file or the new one, never a truncated one. Scene order follows a documented convention rather than filesystem order. Do not build yet: No model calls at all. A reliable editor first. ### Phase 2 · A story bible built from the text Build: Extract candidate characters, places and objects from the manuscript, present them for confirmation, and track which scenes each appears in. Let the writer correct and add entries. Store it as a Markdown file in the manuscript folder so it travels with the book. Trap: Automatic extraction will confidently invent a character from a repeated phrase and miss one introduced only by a nickname. Present extraction as suggestions requiring confirmation rather than as facts, and make manual entry as fast as accepting a suggestion. Done when: Extraction over a real manuscript proposes characters that are mostly correct, and every proposal requires confirmation. A manually added entity is never overwritten by a later extraction. The bible is a readable Markdown file in the manuscript folder. Mentions correctly list the scenes an entity appears in. Do not build yet: No suggestions and no model calls for prose yet. ### Phase 3 · Context assembled, not dumped Build: Assemble context for a request: the current scene, the previous scene, bible entries for entities present, and the most relevant earlier scenes by search. Show the assembled context before sending, and record which scenes were included. Trap: Sending the whole manuscript with every request is expensive, slow, and worse for quality than a focused selection, because the relevant paragraph is buried. It also means the entire book leaves the machine on every keystroke-triggered suggestion, which is a privacy decision made by accident. Done when: A request's assembled context is visible before sending and lists the scenes included. Context stays within a configured token budget regardless of manuscript length. The requests table records exactly which scenes were sent. Sending is refused if the budget would be exceeded rather than truncating silently mid-scene. Do not build yet: No suggestions applied to the text yet. ### Phase 4 · Suggestions as diffs Build: Suggestion actions such as expand, rewrite and describe. Present every result as a word-level diff against the original, with accept, reject and accept-in-part. Keep rejected suggestions so a writer can revisit one. Trap: Applying a suggestion directly into the text and relying on undo loses the original as soon as anything else is typed, and a writer who accepts a rewrite and then dislikes it has no way back. Keep the original on the suggestion row, so accepted suggestions remain reversible for as long as the record exists. Done when: Every suggestion is shown as a diff before it can be applied. Accepting part of a suggestion applies only that part. A rejected suggestion is retrievable afterwards. The original text is recoverable from an accepted suggestion after further editing. Do not build yet: No cost tracking or local model yet. ### Phase 5 · Cost, and the local option Build: Record tokens and cost per request. Show a running total per session and per manuscript. Add a local model option through Ollama, and make the choice between local and hosted prominent rather than buried in settings. Trap: A per-request cost of a fraction of a cent feels free and adds up quickly during a writing session where you try twenty variations of a paragraph. Show the session total in the interface rather than only in a settings pane, because that number changes behaviour and a monthly bill does not. Done when: Each request records tokens and cost, and the session total is visible while writing. Switching to a local model produces suggestions with no network request, verified with the network disabled. The interface states clearly which model is active and whether text leaves the machine. Do not build yet: Export and distribution come last. ### Phase 6 · Export, backup and the ownership question Build: Export to Markdown, which is already the storage format, plus a compiled document for submission. A request log the writer can read and export. A clear statement of which provider was used and what their terms say about training on submitted text. Trap: Provider terms on whether submitted text may be used for training vary and change, and for an unpublished manuscript that question matters more than it does for most content. Record which provider and which terms were in effect, with the date, rather than assuming the writer checked. Done when: The manuscript exports as Markdown and as a compiled document with correct scene order. The request log exports and shows what was sent, when, to which provider. The README records each supported provider's data use terms with the date checked. Deleting a manuscript's request log is possible and documented. Do not build yet: Nothing after this. ### Out of scope (and why) - The fiction-tuned workflows and prompt craft behind the commercial product, which are the accumulated result of watching many writers work. - Cross-device sync of manuscripts, which is a file sync problem you already have solutions for. - Large proprietary style models trained on fiction specifically. - Any claim that the output is publishable prose. It is a drafting aid, and the diff view exists because you will reject most of it. ### README must contain - That manuscripts are plain Markdown files and the application is not required to read them - That saves are atomic and a crash cannot truncate a chapter - That context is assembled rather than dumped, and how to see exactly what is sent - That every suggestion is a diff and originals are preserved beyond the undo stack - Which providers are supported, what their terms say about training, and when that was checked ===== AGENTS.md ===== # Agent instructions · Sudowrite indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: TypeScript with a desktop shell, Plain Markdown, one file per scene or chapter, SQLite with full text search over scenes and entities, Any provider behind one adapter, plus a local option, Word-level diff shown before anything is applied. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Writing directly over a file means a crash mid-save can truncate a chapter, and for a writer that is the worst possible failure. Write to a temporary file and rename over the target, which is atomic and cannot leave a half-written manuscript. - Automatic extraction will confidently invent a character from a repeated phrase and miss one introduced only by a nickname. Present extraction as suggestions requiring confirmation rather than as facts, and make manual entry as fast as accepting a suggestion. - Sending the whole manuscript with every request is expensive, slow, and worse for quality than a focused selection, because the relevant paragraph is buried. It also means the entire book leaves the machine on every keystroke-triggered suggestion, which is a privacy decision made by accident. - Applying a suggestion directly into the text and relying on undo loses the original as soon as anything else is typed, and a writer who accepts a rewrite and then dislikes it has no way back. Keep the original on the suggestion row, so accepted suggestions remain reversible for as long as the record exists. - A per-request cost of a fraction of a cent feels free and adds up quickly during a writing session where you try twenty variations of a paragraph. Show the session total in the interface rather than only in a settings pane, because that number changes behaviour and a monthly bill does not. ===== BUILD_PLAN.md ===== # Build plan · Sudowrite An editor over your own Markdown files with a story bible it builds from the text, retrieval that assembles relevant context instead of sending the whole manuscript, suggestions shown as a diff you accept or reject, and a per-request record of exactly what was sent and what it cost. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Editing your own files You can write comfortably, and the files stay ordinary Markdown. ### Steps 1. Open a folder as a manuscript Scene order from a filename convention you document. Filesystem order is not stable and will reorder your book. Files: `src/manuscript.ts` 2. Save atomically with debounce Temporary file plus rename. Test by killing the process mid-save. Files: `src/storage.ts` 3. Watch for external edits Hash what you wrote and ignore your own events, so the watcher does not fight the editor. Files: `src/watcher.ts` ### Done when - [ ] Text typed and immediately quit is on disk - [ ] An external edit appears without losing cursor position - [ ] A killed save leaves a complete file - [ ] Scene order follows the documented convention ### Watch out - Writing directly over a file means a crash mid-save can truncate a chapter, and for a writer that is the worst possible failure. Write to a temporary file and rename over the target, which is atomic and cannot leave a half-written manuscript. ## Phase 2 · A story bible built from the text The application knows who is in your book without you filling in a form. ### Steps 1. Extract candidates Proper nouns with frequency and first appearance. A model pass can improve it, but the deterministic pass is the baseline. Files: `src/bible/extract.ts` 2. Require confirmation Nothing enters the bible without the writer saying so. Manual entries are marked and never overwritten. Files: `src/bible/confirm.ts` 3. Store as Markdown in the folder It travels with the manuscript and is readable without this application, which is the point. Files: `src/bible/write.ts` ### Done when - [ ] Extraction proposes mostly correct characters on a real manuscript - [ ] Every proposal requires confirmation - [ ] A manual entry survives re-extraction - [ ] The bible is readable Markdown in the manuscript folder ### Watch out - Automatic extraction will confidently invent a character from a repeated phrase and miss one introduced only by a nickname. Present extraction as suggestions requiring confirmation rather than as facts, and make manual entry as fast as accepting a suggestion. ## Phase 3 · Context assembled, not dumped A request carries what is relevant, and you can see exactly what was sent. ### Steps 1. Assemble by relevance Current and previous scene always, bible entries for present entities, then search results until the budget is reached. Files: `src/context.ts` 2. Show what will be sent The actual assembled text, not a summary of it. A writer deciding whether to use a hosted model needs to see this. Files: `src/ui/ContextPreview.tsx` 3. Record it Scene identifiers on the request row. This is the audit trail that makes the privacy claim checkable. Files: `src/requests.ts` ### Done when - [ ] Assembled context is visible before sending - [ ] Context stays within the token budget at any manuscript length - [ ] Included scenes are recorded on the request - [ ] Exceeding the budget refuses rather than truncating mid-scene ### Watch out - Sending the whole manuscript with every request is expensive, slow, and worse for quality than a focused selection, because the relevant paragraph is buried. It also means the entire book leaves the machine on every keystroke-triggered suggestion, which is a privacy decision made by accident. ## Phase 4 · Suggestions as diffs Nothing changes your text without you seeing exactly what would change. ### Steps 1. Implement suggestion actions Expand, rewrite, describe and continue. Each is a prompt template plus the assembled context. Files: `src/actions/` 2. Diff at word level Sentence-level diffs on prose are unreadable. Word level with sentence grouping is what a writer can actually judge. Files: `src/diff.ts` 3. Preserve originals On the suggestion row, so an accepted change stays reversible beyond the undo stack. Files: `src/suggestions.ts` ### Done when - [ ] Every suggestion is shown as a diff first - [ ] Partial acceptance applies only the selected part - [ ] A rejected suggestion can be retrieved - [ ] The original is recoverable after further editing ### Watch out - Applying a suggestion directly into the text and relying on undo loses the original as soon as anything else is typed, and a writer who accepts a rewrite and then dislikes it has no way back. Keep the original on the suggestion row, so accepted suggestions remain reversible for as long as the record exists. ## Phase 5 · Cost, and the local option You know what each request cost, and you can choose to send nothing off the machine. ### Steps 1. Record cost per request Tokens in and out, priced by model. Aggregate by session and by manuscript. Files: `src/cost.ts` 2. Add a local model option Through a local endpoint. Quality will be lower and privacy absolute, which is a trade the writer should make knowingly. Files: `src/models/local.ts` 3. Make the active model visible Persistently in the interface, with whether text leaves the machine stated plainly. Files: `src/ui/ModelBadge.tsx` ### Done when - [ ] Every request records tokens and cost - [ ] The session total is visible while writing - [ ] Local model suggestions work with networking disabled - [ ] The interface states whether text leaves the machine ### Watch out - A per-request cost of a fraction of a cent feels free and adds up quickly during a writing session where you try twenty variations of a paragraph. Show the session total in the interface rather than only in a settings pane, because that number changes behaviour and a monthly bill does not. ## Not in this build - Fiction-tuned workflows and accumulated prompt craft. - Cross-device manuscript sync. - Large proprietary style models. - Any claim that the output is publishable prose. ## After v1, if you want it - Embedding-based retrieval, once full text search starts missing thematically relevant scenes - A consistency check that flags a character described differently in two places - Per-character voice notes fed into the context for dialogue suggestions ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. The folder of Markdown files. Your data, in your backup. MANUSCRIPT_PATH=~/Writing/Novel # Required. Search index and request log. Deliberately outside the manuscript folder so syncing cannot corrupt it. INDEX_PATH=~/.local/share/write/index.db # Required. Which adapter is active. Local means nothing leaves the machine. MODEL_PROVIDER=local # Optional · secret. Only needed for a hosted provider. Set a spend limit at the provider as well. MODEL_API_KEY=sk-... # Required. Maximum assembled context. Exceeding it refuses rather than truncating. CONTEXT_TOKEN_BUDGET=8000 # Required. Session spend at which a warning appears. Visible while writing rather than in settings. SESSION_COST_WARN_USD=2
You are building a production product version of Sudowrite. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== PRODUCT.md ===== # Sudowrite · product brief ## Problem The core loop is buildable, but a dependable replacement becomes a real weekend or multi-day project. For Sudowrite, brainstorm fiction, expand scenes, inspect story context, and generate constrained prose suggestions. The hard boundary is its fiction-tuned workflows, context handling, and model orchestration, plus polish, integrations, and proprietary language models. ## Product outcome A writing tool where the manuscript is plain files you own, nothing is sent without being shown first, every suggestion is a reversible diff, the running cost is visible while you work, and there is a genuinely local option. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - macOS, Windows, or Linux - local files - optional OpenAI or Anthropic API key for semantic rewrites ## Explicit non-goals for v1 - Fiction-tuned workflows and accumulated prompt craft. - Cross-device manuscript sync. - Large proprietary style models. - Any claim that the output is publishable prose. - its fiction-tuned workflows, context handling, and model orchestration - cross-device sync - large proprietary style models - browser and office-suite extensions - team style guides ## Success criteria - A killed process during save is confirmed to leave a complete file - Assembled context is confirmed visible before every send - Originals are confirmed recoverable after an accepted suggestion and further editing - Local model suggestions are confirmed to work with networking disabled - Provider data terms are recorded with a check date ===== BRIEF.md ===== # Build brief · Sudowrite The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a local fiction writing assistant that reads your manuscript from disk and never uploads it without asking to replace Sudowrite. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Stack (fixed, do not substitute) - Runtime: TypeScript with a desktop shell · local files, a real editor and no browser storage limits - Files: Plain Markdown, one file per scene or chapter · your manuscript must outlive this application and open in anything - Index: SQLite with full text search over scenes and entities · retrieval needs an index; the files remain the truth - Models: Any provider behind one adapter, plus a local option · the choice between a hosted model and a local one is a privacy decision the writer should make, not you - Diffs: Word-level diff shown before anything is applied · a suggestion you cannot inspect is a suggestion you cannot trust ### Data model (create this before Phase 1) - manuscripts(id, path, title, updated_at) - scenes(id, manuscript_id, path, ordinal, title, word_count, content_hash) - entities(id, manuscript_id, kind, name, description, first_seen_scene_id) · kind is character, place, object or thread - mentions(entity_id, scene_id, count) · which scenes an entity appears in, which is how context is assembled - requests(id, scene_id, at, purpose, model, prompt_tokens, completion_tokens, cost_usd, context_scene_ids) - suggestions(id, request_id, original_text, suggested_text, state) · state is pending, accepted or rejected - The requests table records which scenes were sent. A writer should always be able to answer what left their machine. ### Phase 1 · Editing your own files Build: Open a folder of Markdown files as a manuscript. A distraction-light editor with word count and scene navigation. Write with debounce and atomic replace. Watch for external edits so another editor can be used on the same files. Trap: Writing directly over a file means a crash mid-save can truncate a chapter, and for a writer that is the worst possible failure. Write to a temporary file and rename over the target, which is atomic and cannot leave a half-written manuscript. Done when: Typing and quitting immediately leaves the text on disk. Editing a file in another editor updates the view without losing cursor position. Killing the process during a save leaves either the old file or the new one, never a truncated one. Scene order follows a documented convention rather than filesystem order. Do not build yet: No model calls at all. A reliable editor first. ### Phase 2 · A story bible built from the text Build: Extract candidate characters, places and objects from the manuscript, present them for confirmation, and track which scenes each appears in. Let the writer correct and add entries. Store it as a Markdown file in the manuscript folder so it travels with the book. Trap: Automatic extraction will confidently invent a character from a repeated phrase and miss one introduced only by a nickname. Present extraction as suggestions requiring confirmation rather than as facts, and make manual entry as fast as accepting a suggestion. Done when: Extraction over a real manuscript proposes characters that are mostly correct, and every proposal requires confirmation. A manually added entity is never overwritten by a later extraction. The bible is a readable Markdown file in the manuscript folder. Mentions correctly list the scenes an entity appears in. Do not build yet: No suggestions and no model calls for prose yet. ### Phase 3 · Context assembled, not dumped Build: Assemble context for a request: the current scene, the previous scene, bible entries for entities present, and the most relevant earlier scenes by search. Show the assembled context before sending, and record which scenes were included. Trap: Sending the whole manuscript with every request is expensive, slow, and worse for quality than a focused selection, because the relevant paragraph is buried. It also means the entire book leaves the machine on every keystroke-triggered suggestion, which is a privacy decision made by accident. Done when: A request's assembled context is visible before sending and lists the scenes included. Context stays within a configured token budget regardless of manuscript length. The requests table records exactly which scenes were sent. Sending is refused if the budget would be exceeded rather than truncating silently mid-scene. Do not build yet: No suggestions applied to the text yet. ### Phase 4 · Suggestions as diffs Build: Suggestion actions such as expand, rewrite and describe. Present every result as a word-level diff against the original, with accept, reject and accept-in-part. Keep rejected suggestions so a writer can revisit one. Trap: Applying a suggestion directly into the text and relying on undo loses the original as soon as anything else is typed, and a writer who accepts a rewrite and then dislikes it has no way back. Keep the original on the suggestion row, so accepted suggestions remain reversible for as long as the record exists. Done when: Every suggestion is shown as a diff before it can be applied. Accepting part of a suggestion applies only that part. A rejected suggestion is retrievable afterwards. The original text is recoverable from an accepted suggestion after further editing. Do not build yet: No cost tracking or local model yet. ### Phase 5 · Cost, and the local option Build: Record tokens and cost per request. Show a running total per session and per manuscript. Add a local model option through Ollama, and make the choice between local and hosted prominent rather than buried in settings. Trap: A per-request cost of a fraction of a cent feels free and adds up quickly during a writing session where you try twenty variations of a paragraph. Show the session total in the interface rather than only in a settings pane, because that number changes behaviour and a monthly bill does not. Done when: Each request records tokens and cost, and the session total is visible while writing. Switching to a local model produces suggestions with no network request, verified with the network disabled. The interface states clearly which model is active and whether text leaves the machine. Do not build yet: Export and distribution come last. ### Phase 6 · Export, backup and the ownership question Build: Export to Markdown, which is already the storage format, plus a compiled document for submission. A request log the writer can read and export. A clear statement of which provider was used and what their terms say about training on submitted text. Trap: Provider terms on whether submitted text may be used for training vary and change, and for an unpublished manuscript that question matters more than it does for most content. Record which provider and which terms were in effect, with the date, rather than assuming the writer checked. Done when: The manuscript exports as Markdown and as a compiled document with correct scene order. The request log exports and shows what was sent, when, to which provider. The README records each supported provider's data use terms with the date checked. Deleting a manuscript's request log is possible and documented. Do not build yet: Nothing after this. ### Out of scope (and why) - The fiction-tuned workflows and prompt craft behind the commercial product, which are the accumulated result of watching many writers work. - Cross-device sync of manuscripts, which is a file sync problem you already have solutions for. - Large proprietary style models trained on fiction specifically. - Any claim that the output is publishable prose. It is a drafting aid, and the diff view exists because you will reject most of it. ### README must contain - That manuscripts are plain Markdown files and the application is not required to read them - That saves are atomic and a crash cannot truncate a chapter - That context is assembled rather than dumped, and how to see exactly what is sent - That every suggestion is a diff and originals are preserved beyond the undo stack - Which providers are supported, what their terms say about training, and when that was checked ===== ARCHITECTURE.md ===== # Architecture · Sudowrite ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | TypeScript with a desktop shell | local files, a real editor and no browser storage limits | | Files | Plain Markdown, one file per scene or chapter | your manuscript must outlive this application and open in anything | | Index | SQLite with full text search over scenes and entities | retrieval needs an index; the files remain the truth | | Models | Any provider behind one adapter, plus a local option | the choice between a hosted model and a local one is a privacy decision the writer should make, not you | | Diffs | Word-level diff shown before anything is applied | a suggestion you cannot inspect is a suggestion you cannot trust | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Storage | Reading and writing Markdown atomically. | Not swappable without giving up the ownership property that justifies the whole build. | | Bible | Entities, mentions and confirmation. | Extraction can improve; the requirement that nothing enters without confirmation should not. | | Context | Assembling and disclosing what is sent. | Retrieval can be replaced with embeddings. The disclosure and the recorded scene list must survive. | | Models | Provider adapters, local and hosted. | One interface, several providers. Making the active model visible is the part that matters. | | Suggestions | Diffs, acceptance and preserved originals. | Any diff implementation, provided originals remain recoverable beyond undo. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `MANUSCRIPT_PATH` · required · The folder of Markdown files. Your data, in your backup. - `INDEX_PATH` · required · Search index and request log. Deliberately outside the manuscript folder so syncing cannot corrupt it. - `MODEL_PROVIDER` · required · Which adapter is active. Local means nothing leaves the machine. - `MODEL_API_KEY` · optional, secret · Only needed for a hosted provider. Set a spend limit at the provider as well. - `CONTEXT_TOKEN_BUDGET` · required · Maximum assembled context. Exceeding it refuses rather than truncating. - `SESSION_COST_WARN_USD` · required · Session spend at which a warning appears. Visible while writing rather than in settings. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it. ===== AGENTS.md ===== # Agent instructions · Sudowrite product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: TypeScript with a desktop shell, Plain Markdown, one file per scene or chapter, SQLite with full text search over scenes and entities, Any provider behind one adapter, plus a local option, Word-level diff shown before anything is applied. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - Writing directly over a file means a crash mid-save can truncate a chapter, and for a writer that is the worst possible failure. Write to a temporary file and rename over the target, which is atomic and cannot leave a half-written manuscript. - Automatic extraction will confidently invent a character from a repeated phrase and miss one introduced only by a nickname. Present extraction as suggestions requiring confirmation rather than as facts, and make manual entry as fast as accepting a suggestion. - Sending the whole manuscript with every request is expensive, slow, and worse for quality than a focused selection, because the relevant paragraph is buried. It also means the entire book leaves the machine on every keystroke-triggered suggestion, which is a privacy decision made by accident. - Applying a suggestion directly into the text and relying on undo loses the original as soon as anything else is typed, and a writer who accepts a rewrite and then dislikes it has no way back. Keep the original on the suggestion row, so accepted suggestions remain reversible for as long as the record exists. - A per-request cost of a fraction of a cent feels free and adds up quickly during a writing session where you try twenty variations of a paragraph. Show the session total in the interface rather than only in a settings pane, because that number changes behaviour and a monthly bill does not. - Provider terms on whether submitted text may be used for training vary and change, and for an unpublished manuscript that question matters more than it does for most content. Record which provider and which terms were in effect, with the date, rather than assuming the writer checked. ===== MILESTONES.md ===== # Delivery milestones · Sudowrite Estimated effort: **weekend** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Editing your own files You can write comfortably, and the files stay ordinary Markdown. ### Steps 1. Open a folder as a manuscript Scene order from a filename convention you document. Filesystem order is not stable and will reorder your book. Files: `src/manuscript.ts` 2. Save atomically with debounce Temporary file plus rename. Test by killing the process mid-save. Files: `src/storage.ts` 3. Watch for external edits Hash what you wrote and ignore your own events, so the watcher does not fight the editor. Files: `src/watcher.ts` ### Done when - [ ] Text typed and immediately quit is on disk - [ ] An external edit appears without losing cursor position - [ ] A killed save leaves a complete file - [ ] Scene order follows the documented convention ### Watch out - Writing directly over a file means a crash mid-save can truncate a chapter, and for a writer that is the worst possible failure. Write to a temporary file and rename over the target, which is atomic and cannot leave a half-written manuscript. ## M2 · A story bible built from the text The application knows who is in your book without you filling in a form. ### Steps 1. Extract candidates Proper nouns with frequency and first appearance. A model pass can improve it, but the deterministic pass is the baseline. Files: `src/bible/extract.ts` 2. Require confirmation Nothing enters the bible without the writer saying so. Manual entries are marked and never overwritten. Files: `src/bible/confirm.ts` 3. Store as Markdown in the folder It travels with the manuscript and is readable without this application, which is the point. Files: `src/bible/write.ts` ### Done when - [ ] Extraction proposes mostly correct characters on a real manuscript - [ ] Every proposal requires confirmation - [ ] A manual entry survives re-extraction - [ ] The bible is readable Markdown in the manuscript folder ### Watch out - Automatic extraction will confidently invent a character from a repeated phrase and miss one introduced only by a nickname. Present extraction as suggestions requiring confirmation rather than as facts, and make manual entry as fast as accepting a suggestion. ## M3 · Context assembled, not dumped A request carries what is relevant, and you can see exactly what was sent. ### Steps 1. Assemble by relevance Current and previous scene always, bible entries for present entities, then search results until the budget is reached. Files: `src/context.ts` 2. Show what will be sent The actual assembled text, not a summary of it. A writer deciding whether to use a hosted model needs to see this. Files: `src/ui/ContextPreview.tsx` 3. Record it Scene identifiers on the request row. This is the audit trail that makes the privacy claim checkable. Files: `src/requests.ts` ### Done when - [ ] Assembled context is visible before sending - [ ] Context stays within the token budget at any manuscript length - [ ] Included scenes are recorded on the request - [ ] Exceeding the budget refuses rather than truncating mid-scene ### Watch out - Sending the whole manuscript with every request is expensive, slow, and worse for quality than a focused selection, because the relevant paragraph is buried. It also means the entire book leaves the machine on every keystroke-triggered suggestion, which is a privacy decision made by accident. ## M4 · Suggestions as diffs Nothing changes your text without you seeing exactly what would change. ### Steps 1. Implement suggestion actions Expand, rewrite, describe and continue. Each is a prompt template plus the assembled context. Files: `src/actions/` 2. Diff at word level Sentence-level diffs on prose are unreadable. Word level with sentence grouping is what a writer can actually judge. Files: `src/diff.ts` 3. Preserve originals On the suggestion row, so an accepted change stays reversible beyond the undo stack. Files: `src/suggestions.ts` ### Done when - [ ] Every suggestion is shown as a diff first - [ ] Partial acceptance applies only the selected part - [ ] A rejected suggestion can be retrieved - [ ] The original is recoverable after further editing ### Watch out - Applying a suggestion directly into the text and relying on undo loses the original as soon as anything else is typed, and a writer who accepts a rewrite and then dislikes it has no way back. Keep the original on the suggestion row, so accepted suggestions remain reversible for as long as the record exists. ## M5 · Cost, and the local option You know what each request cost, and you can choose to send nothing off the machine. ### Steps 1. Record cost per request Tokens in and out, priced by model. Aggregate by session and by manuscript. Files: `src/cost.ts` 2. Add a local model option Through a local endpoint. Quality will be lower and privacy absolute, which is a trade the writer should make knowingly. Files: `src/models/local.ts` 3. Make the active model visible Persistently in the interface, with whether text leaves the machine stated plainly. Files: `src/ui/ModelBadge.tsx` ### Done when - [ ] Every request records tokens and cost - [ ] The session total is visible while writing - [ ] Local model suggestions work with networking disabled - [ ] The interface states whether text leaves the machine ### Watch out - A per-request cost of a fraction of a cent feels free and adds up quickly during a writing session where you try twenty variations of a paragraph. Show the session total in the interface rather than only in a settings pane, because that number changes behaviour and a monthly bill does not. ## M6 · Export, backup and the ownership question (production only) The manuscript is yours in every sense, and you can prove what was sent where. ### Steps 1. Export and compile Markdown is already the format. Compilation is concatenation with scene ordering and front matter. Files: `src/export.ts` 2. Make the request log readable and exportable A writer should be able to answer what left their machine, in a file they can keep. Files: `src/requests.ts` 3. Record provider data terms with a date Which provider, what their terms say about training, and when you checked. Terms change. Files: `docs/providers.md` ### Done when - [ ] Export produces correct scene order in both formats - [ ] The request log exports and shows destinations - [ ] Provider data terms are recorded with a check date - [ ] A request log can be deleted deliberately ### Watch out - Provider terms on whether submitted text may be used for training vary and change, and for an unpublished manuscript that question matters more than it does for most content. Record which provider and which terms were in effect, with the date, rather than assuming the writer checked. ===== OPERATIONS.md ===== # Operations · Sudowrite ## Backup The manuscript folder is everything and is plain files. Version control it if you can, because history for a manuscript is worth more than for most data. The index and request log are local and rebuildable except for the cost history. ## Restore Restore the folder and open it. The index rebuilds. If anything else is required, something authoritative has leaked out of the files. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring Session cost against the warning threshold, and index freshness. There is no service to watch, which is the point. ## Incident checklist The incident that matters is a lost manuscript, and the controls are atomic saves plus your own backup. The second is discovering a provider changed its data terms, which is why they are recorded with a date rather than assumed. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] A killed process during save is confirmed to leave a complete file - [ ] Assembled context is confirmed visible before every send - [ ] Originals are confirmed recoverable after an accepted suggestion and further editing - [ ] Local model suggestions are confirmed to work with networking disabled - [ ] Provider data terms are recorded with a check date ## Launch constraint Do not market omitted Sudowrite capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered. ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. The folder of Markdown files. Your data, in your backup. MANUSCRIPT_PATH=~/Writing/Novel # Required. Search index and request log. Deliberately outside the manuscript folder so syncing cannot corrupt it. INDEX_PATH=~/.local/share/write/index.db # Required. Which adapter is active. Local means nothing leaves the machine. MODEL_PROVIDER=local # Optional · secret. Only needed for a hosted provider. Set a spend limit at the provider as well. MODEL_API_KEY=sk-... # Required. Maximum assembled context. Exceeding it refuses rather than truncating. CONTEXT_TOKEN_BUDGET=8000 # Required. Session spend at which a warning appears. Visible while writing rather than in settings. SESSION_COST_WARN_USD=2
# Sudowrite · indie build An editor over your own Markdown files with a story bible it builds from the text, retrieval that assembles relevant context instead of sending the whole manuscript, suggestions shown as a diff you accept or reject, and a per-request record of exactly what was sent and what it cost. Estimated effort: **weekend**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | TypeScript with a desktop shell | local files, a real editor and no browser storage limits | | Files | Plain Markdown, one file per scene or chapter | your manuscript must outlive this application and open in anything | | Index | SQLite with full text search over scenes and entities | retrieval needs an index; the files remain the truth | | Models | Any provider behind one adapter, plus a local option | the choice between a hosted model and a local one is a privacy decision the writer should make, not you | | Diffs | Word-level diff shown before anything is applied | a suggestion you cannot inspect is a suggestion you cannot trust | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the build and the desktop shell. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **A real manuscript of at least twenty thousand words** · free - Why: Context assembly, extraction and retrieval are all trivial on a short story and revealing on a novel. The budget problem only exists at length. - Get it: Use your own work in progress, or a public domain novel split into scene files for testing. - Verify: The manuscript loads and its scene count and word count are reported - [ ] **A model API key, or Ollama for local** · pay per token; a paragraph rewrite is a fraction of a cent and a long session is more than you expect - Why: Suggestions need a model. Phases 1 to 3 work entirely without one, which is deliberate. - Get it: Create an account with a provider and set a spend limit when you create the key. Or install Ollama and pull a model to keep everything local. - Verify: A test request returns text and its cost is recorded - [ ] **A decision about whether your manuscript may leave your machine** · free - Why: This is the question the whole build is arranged around, and it is the writer's to answer rather than the tool's to assume. - Get it: Read your chosen provider's terms on training and retention before Phase 4. If the answer is no, use the local model path and accept lower quality. - Verify: The decision and the terms you read are recorded with a date - [ ] **A backup of the manuscript folder** · free - Why: These are plain files and this application writes to them. A backup is the safety net that atomic saves do not replace. - Get it: Version control the manuscript folder, which also gives you history, or ensure your ordinary backup covers it. - Verify: The folder is in a backup that has been restored from at least once ## Quick start Follow Phase 1 in `BUILD_PLAN.md`; it creates the project. Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Fiction-tuned workflows and accumulated prompt craft. - Cross-device manuscript sync. - Large proprietary style models. - Any claim that the output is publishable prose. - its fiction-tuned workflows, context handling, and model orchestration - cross-device sync - large proprietary style models - browser and office-suite extensions - team style guides If one of those is essential to you, that is the reason to keep paying for Sudowrite, and the README should say so rather than pretend.
# Build brief · Sudowrite The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a local fiction writing assistant that reads your manuscript from disk and never uploads it without asking to replace Sudowrite. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Stack (fixed, do not substitute) - Runtime: TypeScript with a desktop shell · local files, a real editor and no browser storage limits - Files: Plain Markdown, one file per scene or chapter · your manuscript must outlive this application and open in anything - Index: SQLite with full text search over scenes and entities · retrieval needs an index; the files remain the truth - Models: Any provider behind one adapter, plus a local option · the choice between a hosted model and a local one is a privacy decision the writer should make, not you - Diffs: Word-level diff shown before anything is applied · a suggestion you cannot inspect is a suggestion you cannot trust ### Data model (create this before Phase 1) - manuscripts(id, path, title, updated_at) - scenes(id, manuscript_id, path, ordinal, title, word_count, content_hash) - entities(id, manuscript_id, kind, name, description, first_seen_scene_id) · kind is character, place, object or thread - mentions(entity_id, scene_id, count) · which scenes an entity appears in, which is how context is assembled - requests(id, scene_id, at, purpose, model, prompt_tokens, completion_tokens, cost_usd, context_scene_ids) - suggestions(id, request_id, original_text, suggested_text, state) · state is pending, accepted or rejected - The requests table records which scenes were sent. A writer should always be able to answer what left their machine. ### Phase 1 · Editing your own files Build: Open a folder of Markdown files as a manuscript. A distraction-light editor with word count and scene navigation. Write with debounce and atomic replace. Watch for external edits so another editor can be used on the same files. Trap: Writing directly over a file means a crash mid-save can truncate a chapter, and for a writer that is the worst possible failure. Write to a temporary file and rename over the target, which is atomic and cannot leave a half-written manuscript. Done when: Typing and quitting immediately leaves the text on disk. Editing a file in another editor updates the view without losing cursor position. Killing the process during a save leaves either the old file or the new one, never a truncated one. Scene order follows a documented convention rather than filesystem order. Do not build yet: No model calls at all. A reliable editor first. ### Phase 2 · A story bible built from the text Build: Extract candidate characters, places and objects from the manuscript, present them for confirmation, and track which scenes each appears in. Let the writer correct and add entries. Store it as a Markdown file in the manuscript folder so it travels with the book. Trap: Automatic extraction will confidently invent a character from a repeated phrase and miss one introduced only by a nickname. Present extraction as suggestions requiring confirmation rather than as facts, and make manual entry as fast as accepting a suggestion. Done when: Extraction over a real manuscript proposes characters that are mostly correct, and every proposal requires confirmation. A manually added entity is never overwritten by a later extraction. The bible is a readable Markdown file in the manuscript folder. Mentions correctly list the scenes an entity appears in. Do not build yet: No suggestions and no model calls for prose yet. ### Phase 3 · Context assembled, not dumped Build: Assemble context for a request: the current scene, the previous scene, bible entries for entities present, and the most relevant earlier scenes by search. Show the assembled context before sending, and record which scenes were included. Trap: Sending the whole manuscript with every request is expensive, slow, and worse for quality than a focused selection, because the relevant paragraph is buried. It also means the entire book leaves the machine on every keystroke-triggered suggestion, which is a privacy decision made by accident. Done when: A request's assembled context is visible before sending and lists the scenes included. Context stays within a configured token budget regardless of manuscript length. The requests table records exactly which scenes were sent. Sending is refused if the budget would be exceeded rather than truncating silently mid-scene. Do not build yet: No suggestions applied to the text yet. ### Phase 4 · Suggestions as diffs Build: Suggestion actions such as expand, rewrite and describe. Present every result as a word-level diff against the original, with accept, reject and accept-in-part. Keep rejected suggestions so a writer can revisit one. Trap: Applying a suggestion directly into the text and relying on undo loses the original as soon as anything else is typed, and a writer who accepts a rewrite and then dislikes it has no way back. Keep the original on the suggestion row, so accepted suggestions remain reversible for as long as the record exists. Done when: Every suggestion is shown as a diff before it can be applied. Accepting part of a suggestion applies only that part. A rejected suggestion is retrievable afterwards. The original text is recoverable from an accepted suggestion after further editing. Do not build yet: No cost tracking or local model yet. ### Phase 5 · Cost, and the local option Build: Record tokens and cost per request. Show a running total per session and per manuscript. Add a local model option through Ollama, and make the choice between local and hosted prominent rather than buried in settings. Trap: A per-request cost of a fraction of a cent feels free and adds up quickly during a writing session where you try twenty variations of a paragraph. Show the session total in the interface rather than only in a settings pane, because that number changes behaviour and a monthly bill does not. Done when: Each request records tokens and cost, and the session total is visible while writing. Switching to a local model produces suggestions with no network request, verified with the network disabled. The interface states clearly which model is active and whether text leaves the machine. Do not build yet: Export and distribution come last. ### Phase 6 · Export, backup and the ownership question Build: Export to Markdown, which is already the storage format, plus a compiled document for submission. A request log the writer can read and export. A clear statement of which provider was used and what their terms say about training on submitted text. Trap: Provider terms on whether submitted text may be used for training vary and change, and for an unpublished manuscript that question matters more than it does for most content. Record which provider and which terms were in effect, with the date, rather than assuming the writer checked. Done when: The manuscript exports as Markdown and as a compiled document with correct scene order. The request log exports and shows what was sent, when, to which provider. The README records each supported provider's data use terms with the date checked. Deleting a manuscript's request log is possible and documented. Do not build yet: Nothing after this. ### Out of scope (and why) - The fiction-tuned workflows and prompt craft behind the commercial product, which are the accumulated result of watching many writers work. - Cross-device sync of manuscripts, which is a file sync problem you already have solutions for. - Large proprietary style models trained on fiction specifically. - Any claim that the output is publishable prose. It is a drafting aid, and the diff view exists because you will reject most of it. ### README must contain - That manuscripts are plain Markdown files and the application is not required to read them - That saves are atomic and a crash cannot truncate a chapter - That context is assembled rather than dumped, and how to see exactly what is sent - That every suggestion is a diff and originals are preserved beyond the undo stack - Which providers are supported, what their terms say about training, and when that was checked
# Agent instructions · Sudowrite indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: TypeScript with a desktop shell, Plain Markdown, one file per scene or chapter, SQLite with full text search over scenes and entities, Any provider behind one adapter, plus a local option, Word-level diff shown before anything is applied. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Writing directly over a file means a crash mid-save can truncate a chapter, and for a writer that is the worst possible failure. Write to a temporary file and rename over the target, which is atomic and cannot leave a half-written manuscript. - Automatic extraction will confidently invent a character from a repeated phrase and miss one introduced only by a nickname. Present extraction as suggestions requiring confirmation rather than as facts, and make manual entry as fast as accepting a suggestion. - Sending the whole manuscript with every request is expensive, slow, and worse for quality than a focused selection, because the relevant paragraph is buried. It also means the entire book leaves the machine on every keystroke-triggered suggestion, which is a privacy decision made by accident. - Applying a suggestion directly into the text and relying on undo loses the original as soon as anything else is typed, and a writer who accepts a rewrite and then dislikes it has no way back. Keep the original on the suggestion row, so accepted suggestions remain reversible for as long as the record exists. - A per-request cost of a fraction of a cent feels free and adds up quickly during a writing session where you try twenty variations of a paragraph. Show the session total in the interface rather than only in a settings pane, because that number changes behaviour and a monthly bill does not.
# Build plan · Sudowrite An editor over your own Markdown files with a story bible it builds from the text, retrieval that assembles relevant context instead of sending the whole manuscript, suggestions shown as a diff you accept or reject, and a per-request record of exactly what was sent and what it cost. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Editing your own files You can write comfortably, and the files stay ordinary Markdown. ### Steps 1. Open a folder as a manuscript Scene order from a filename convention you document. Filesystem order is not stable and will reorder your book. Files: `src/manuscript.ts` 2. Save atomically with debounce Temporary file plus rename. Test by killing the process mid-save. Files: `src/storage.ts` 3. Watch for external edits Hash what you wrote and ignore your own events, so the watcher does not fight the editor. Files: `src/watcher.ts` ### Done when - [ ] Text typed and immediately quit is on disk - [ ] An external edit appears without losing cursor position - [ ] A killed save leaves a complete file - [ ] Scene order follows the documented convention ### Watch out - Writing directly over a file means a crash mid-save can truncate a chapter, and for a writer that is the worst possible failure. Write to a temporary file and rename over the target, which is atomic and cannot leave a half-written manuscript. ## Phase 2 · A story bible built from the text The application knows who is in your book without you filling in a form. ### Steps 1. Extract candidates Proper nouns with frequency and first appearance. A model pass can improve it, but the deterministic pass is the baseline. Files: `src/bible/extract.ts` 2. Require confirmation Nothing enters the bible without the writer saying so. Manual entries are marked and never overwritten. Files: `src/bible/confirm.ts` 3. Store as Markdown in the folder It travels with the manuscript and is readable without this application, which is the point. Files: `src/bible/write.ts` ### Done when - [ ] Extraction proposes mostly correct characters on a real manuscript - [ ] Every proposal requires confirmation - [ ] A manual entry survives re-extraction - [ ] The bible is readable Markdown in the manuscript folder ### Watch out - Automatic extraction will confidently invent a character from a repeated phrase and miss one introduced only by a nickname. Present extraction as suggestions requiring confirmation rather than as facts, and make manual entry as fast as accepting a suggestion. ## Phase 3 · Context assembled, not dumped A request carries what is relevant, and you can see exactly what was sent. ### Steps 1. Assemble by relevance Current and previous scene always, bible entries for present entities, then search results until the budget is reached. Files: `src/context.ts` 2. Show what will be sent The actual assembled text, not a summary of it. A writer deciding whether to use a hosted model needs to see this. Files: `src/ui/ContextPreview.tsx` 3. Record it Scene identifiers on the request row. This is the audit trail that makes the privacy claim checkable. Files: `src/requests.ts` ### Done when - [ ] Assembled context is visible before sending - [ ] Context stays within the token budget at any manuscript length - [ ] Included scenes are recorded on the request - [ ] Exceeding the budget refuses rather than truncating mid-scene ### Watch out - Sending the whole manuscript with every request is expensive, slow, and worse for quality than a focused selection, because the relevant paragraph is buried. It also means the entire book leaves the machine on every keystroke-triggered suggestion, which is a privacy decision made by accident. ## Phase 4 · Suggestions as diffs Nothing changes your text without you seeing exactly what would change. ### Steps 1. Implement suggestion actions Expand, rewrite, describe and continue. Each is a prompt template plus the assembled context. Files: `src/actions/` 2. Diff at word level Sentence-level diffs on prose are unreadable. Word level with sentence grouping is what a writer can actually judge. Files: `src/diff.ts` 3. Preserve originals On the suggestion row, so an accepted change stays reversible beyond the undo stack. Files: `src/suggestions.ts` ### Done when - [ ] Every suggestion is shown as a diff first - [ ] Partial acceptance applies only the selected part - [ ] A rejected suggestion can be retrieved - [ ] The original is recoverable after further editing ### Watch out - Applying a suggestion directly into the text and relying on undo loses the original as soon as anything else is typed, and a writer who accepts a rewrite and then dislikes it has no way back. Keep the original on the suggestion row, so accepted suggestions remain reversible for as long as the record exists. ## Phase 5 · Cost, and the local option You know what each request cost, and you can choose to send nothing off the machine. ### Steps 1. Record cost per request Tokens in and out, priced by model. Aggregate by session and by manuscript. Files: `src/cost.ts` 2. Add a local model option Through a local endpoint. Quality will be lower and privacy absolute, which is a trade the writer should make knowingly. Files: `src/models/local.ts` 3. Make the active model visible Persistently in the interface, with whether text leaves the machine stated plainly. Files: `src/ui/ModelBadge.tsx` ### Done when - [ ] Every request records tokens and cost - [ ] The session total is visible while writing - [ ] Local model suggestions work with networking disabled - [ ] The interface states whether text leaves the machine ### Watch out - A per-request cost of a fraction of a cent feels free and adds up quickly during a writing session where you try twenty variations of a paragraph. Show the session total in the interface rather than only in a settings pane, because that number changes behaviour and a monthly bill does not. ## Not in this build - Fiction-tuned workflows and accumulated prompt craft. - Cross-device manuscript sync. - Large proprietary style models. - Any claim that the output is publishable prose. ## After v1, if you want it - Embedding-based retrieval, once full text search starts missing thematically relevant scenes - A consistency check that flags a character described differently in two places - Per-character voice notes fed into the context for dialogue suggestions
# Copy to .env and fill in. Never commit .env; this file documents it. # Required. The folder of Markdown files. Your data, in your backup. MANUSCRIPT_PATH=~/Writing/Novel # Required. Search index and request log. Deliberately outside the manuscript folder so syncing cannot corrupt it. INDEX_PATH=~/.local/share/write/index.db # Required. Which adapter is active. Local means nothing leaves the machine. MODEL_PROVIDER=local # Optional · secret. Only needed for a hosted provider. Set a spend limit at the provider as well. MODEL_API_KEY=sk-... # Required. Maximum assembled context. Exceeding it refuses rather than truncating. CONTEXT_TOKEN_BUDGET=8000 # Required. Session spend at which a warning appears. Visible while writing rather than in settings. SESSION_COST_WARN_USD=2
# Sudowrite · product brief ## Problem The core loop is buildable, but a dependable replacement becomes a real weekend or multi-day project. For Sudowrite, brainstorm fiction, expand scenes, inspect story context, and generate constrained prose suggestions. The hard boundary is its fiction-tuned workflows, context handling, and model orchestration, plus polish, integrations, and proprietary language models. ## Product outcome A writing tool where the manuscript is plain files you own, nothing is sent without being shown first, every suggestion is a reversible diff, the running cost is visible while you work, and there is a genuinely local option. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - macOS, Windows, or Linux - local files - optional OpenAI or Anthropic API key for semantic rewrites ## Explicit non-goals for v1 - Fiction-tuned workflows and accumulated prompt craft. - Cross-device manuscript sync. - Large proprietary style models. - Any claim that the output is publishable prose. - its fiction-tuned workflows, context handling, and model orchestration - cross-device sync - large proprietary style models - browser and office-suite extensions - team style guides ## Success criteria - A killed process during save is confirmed to leave a complete file - Assembled context is confirmed visible before every send - Originals are confirmed recoverable after an accepted suggestion and further editing - Local model suggestions are confirmed to work with networking disabled - Provider data terms are recorded with a check date
# Build brief · Sudowrite The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a local fiction writing assistant that reads your manuscript from disk and never uploads it without asking to replace Sudowrite. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Stack (fixed, do not substitute) - Runtime: TypeScript with a desktop shell · local files, a real editor and no browser storage limits - Files: Plain Markdown, one file per scene or chapter · your manuscript must outlive this application and open in anything - Index: SQLite with full text search over scenes and entities · retrieval needs an index; the files remain the truth - Models: Any provider behind one adapter, plus a local option · the choice between a hosted model and a local one is a privacy decision the writer should make, not you - Diffs: Word-level diff shown before anything is applied · a suggestion you cannot inspect is a suggestion you cannot trust ### Data model (create this before Phase 1) - manuscripts(id, path, title, updated_at) - scenes(id, manuscript_id, path, ordinal, title, word_count, content_hash) - entities(id, manuscript_id, kind, name, description, first_seen_scene_id) · kind is character, place, object or thread - mentions(entity_id, scene_id, count) · which scenes an entity appears in, which is how context is assembled - requests(id, scene_id, at, purpose, model, prompt_tokens, completion_tokens, cost_usd, context_scene_ids) - suggestions(id, request_id, original_text, suggested_text, state) · state is pending, accepted or rejected - The requests table records which scenes were sent. A writer should always be able to answer what left their machine. ### Phase 1 · Editing your own files Build: Open a folder of Markdown files as a manuscript. A distraction-light editor with word count and scene navigation. Write with debounce and atomic replace. Watch for external edits so another editor can be used on the same files. Trap: Writing directly over a file means a crash mid-save can truncate a chapter, and for a writer that is the worst possible failure. Write to a temporary file and rename over the target, which is atomic and cannot leave a half-written manuscript. Done when: Typing and quitting immediately leaves the text on disk. Editing a file in another editor updates the view without losing cursor position. Killing the process during a save leaves either the old file or the new one, never a truncated one. Scene order follows a documented convention rather than filesystem order. Do not build yet: No model calls at all. A reliable editor first. ### Phase 2 · A story bible built from the text Build: Extract candidate characters, places and objects from the manuscript, present them for confirmation, and track which scenes each appears in. Let the writer correct and add entries. Store it as a Markdown file in the manuscript folder so it travels with the book. Trap: Automatic extraction will confidently invent a character from a repeated phrase and miss one introduced only by a nickname. Present extraction as suggestions requiring confirmation rather than as facts, and make manual entry as fast as accepting a suggestion. Done when: Extraction over a real manuscript proposes characters that are mostly correct, and every proposal requires confirmation. A manually added entity is never overwritten by a later extraction. The bible is a readable Markdown file in the manuscript folder. Mentions correctly list the scenes an entity appears in. Do not build yet: No suggestions and no model calls for prose yet. ### Phase 3 · Context assembled, not dumped Build: Assemble context for a request: the current scene, the previous scene, bible entries for entities present, and the most relevant earlier scenes by search. Show the assembled context before sending, and record which scenes were included. Trap: Sending the whole manuscript with every request is expensive, slow, and worse for quality than a focused selection, because the relevant paragraph is buried. It also means the entire book leaves the machine on every keystroke-triggered suggestion, which is a privacy decision made by accident. Done when: A request's assembled context is visible before sending and lists the scenes included. Context stays within a configured token budget regardless of manuscript length. The requests table records exactly which scenes were sent. Sending is refused if the budget would be exceeded rather than truncating silently mid-scene. Do not build yet: No suggestions applied to the text yet. ### Phase 4 · Suggestions as diffs Build: Suggestion actions such as expand, rewrite and describe. Present every result as a word-level diff against the original, with accept, reject and accept-in-part. Keep rejected suggestions so a writer can revisit one. Trap: Applying a suggestion directly into the text and relying on undo loses the original as soon as anything else is typed, and a writer who accepts a rewrite and then dislikes it has no way back. Keep the original on the suggestion row, so accepted suggestions remain reversible for as long as the record exists. Done when: Every suggestion is shown as a diff before it can be applied. Accepting part of a suggestion applies only that part. A rejected suggestion is retrievable afterwards. The original text is recoverable from an accepted suggestion after further editing. Do not build yet: No cost tracking or local model yet. ### Phase 5 · Cost, and the local option Build: Record tokens and cost per request. Show a running total per session and per manuscript. Add a local model option through Ollama, and make the choice between local and hosted prominent rather than buried in settings. Trap: A per-request cost of a fraction of a cent feels free and adds up quickly during a writing session where you try twenty variations of a paragraph. Show the session total in the interface rather than only in a settings pane, because that number changes behaviour and a monthly bill does not. Done when: Each request records tokens and cost, and the session total is visible while writing. Switching to a local model produces suggestions with no network request, verified with the network disabled. The interface states clearly which model is active and whether text leaves the machine. Do not build yet: Export and distribution come last. ### Phase 6 · Export, backup and the ownership question Build: Export to Markdown, which is already the storage format, plus a compiled document for submission. A request log the writer can read and export. A clear statement of which provider was used and what their terms say about training on submitted text. Trap: Provider terms on whether submitted text may be used for training vary and change, and for an unpublished manuscript that question matters more than it does for most content. Record which provider and which terms were in effect, with the date, rather than assuming the writer checked. Done when: The manuscript exports as Markdown and as a compiled document with correct scene order. The request log exports and shows what was sent, when, to which provider. The README records each supported provider's data use terms with the date checked. Deleting a manuscript's request log is possible and documented. Do not build yet: Nothing after this. ### Out of scope (and why) - The fiction-tuned workflows and prompt craft behind the commercial product, which are the accumulated result of watching many writers work. - Cross-device sync of manuscripts, which is a file sync problem you already have solutions for. - Large proprietary style models trained on fiction specifically. - Any claim that the output is publishable prose. It is a drafting aid, and the diff view exists because you will reject most of it. ### README must contain - That manuscripts are plain Markdown files and the application is not required to read them - That saves are atomic and a crash cannot truncate a chapter - That context is assembled rather than dumped, and how to see exactly what is sent - That every suggestion is a diff and originals are preserved beyond the undo stack - Which providers are supported, what their terms say about training, and when that was checked
# Architecture · Sudowrite ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | TypeScript with a desktop shell | local files, a real editor and no browser storage limits | | Files | Plain Markdown, one file per scene or chapter | your manuscript must outlive this application and open in anything | | Index | SQLite with full text search over scenes and entities | retrieval needs an index; the files remain the truth | | Models | Any provider behind one adapter, plus a local option | the choice between a hosted model and a local one is a privacy decision the writer should make, not you | | Diffs | Word-level diff shown before anything is applied | a suggestion you cannot inspect is a suggestion you cannot trust | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Storage | Reading and writing Markdown atomically. | Not swappable without giving up the ownership property that justifies the whole build. | | Bible | Entities, mentions and confirmation. | Extraction can improve; the requirement that nothing enters without confirmation should not. | | Context | Assembling and disclosing what is sent. | Retrieval can be replaced with embeddings. The disclosure and the recorded scene list must survive. | | Models | Provider adapters, local and hosted. | One interface, several providers. Making the active model visible is the part that matters. | | Suggestions | Diffs, acceptance and preserved originals. | Any diff implementation, provided originals remain recoverable beyond undo. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `MANUSCRIPT_PATH` · required · The folder of Markdown files. Your data, in your backup. - `INDEX_PATH` · required · Search index and request log. Deliberately outside the manuscript folder so syncing cannot corrupt it. - `MODEL_PROVIDER` · required · Which adapter is active. Local means nothing leaves the machine. - `MODEL_API_KEY` · optional, secret · Only needed for a hosted provider. Set a spend limit at the provider as well. - `CONTEXT_TOKEN_BUDGET` · required · Maximum assembled context. Exceeding it refuses rather than truncating. - `SESSION_COST_WARN_USD` · required · Session spend at which a warning appears. Visible while writing rather than in settings. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it.
# Agent instructions · Sudowrite product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: TypeScript with a desktop shell, Plain Markdown, one file per scene or chapter, SQLite with full text search over scenes and entities, Any provider behind one adapter, plus a local option, Word-level diff shown before anything is applied. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - Writing directly over a file means a crash mid-save can truncate a chapter, and for a writer that is the worst possible failure. Write to a temporary file and rename over the target, which is atomic and cannot leave a half-written manuscript. - Automatic extraction will confidently invent a character from a repeated phrase and miss one introduced only by a nickname. Present extraction as suggestions requiring confirmation rather than as facts, and make manual entry as fast as accepting a suggestion. - Sending the whole manuscript with every request is expensive, slow, and worse for quality than a focused selection, because the relevant paragraph is buried. It also means the entire book leaves the machine on every keystroke-triggered suggestion, which is a privacy decision made by accident. - Applying a suggestion directly into the text and relying on undo loses the original as soon as anything else is typed, and a writer who accepts a rewrite and then dislikes it has no way back. Keep the original on the suggestion row, so accepted suggestions remain reversible for as long as the record exists. - A per-request cost of a fraction of a cent feels free and adds up quickly during a writing session where you try twenty variations of a paragraph. Show the session total in the interface rather than only in a settings pane, because that number changes behaviour and a monthly bill does not. - Provider terms on whether submitted text may be used for training vary and change, and for an unpublished manuscript that question matters more than it does for most content. Record which provider and which terms were in effect, with the date, rather than assuming the writer checked.
# Delivery milestones · Sudowrite Estimated effort: **weekend** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Editing your own files You can write comfortably, and the files stay ordinary Markdown. ### Steps 1. Open a folder as a manuscript Scene order from a filename convention you document. Filesystem order is not stable and will reorder your book. Files: `src/manuscript.ts` 2. Save atomically with debounce Temporary file plus rename. Test by killing the process mid-save. Files: `src/storage.ts` 3. Watch for external edits Hash what you wrote and ignore your own events, so the watcher does not fight the editor. Files: `src/watcher.ts` ### Done when - [ ] Text typed and immediately quit is on disk - [ ] An external edit appears without losing cursor position - [ ] A killed save leaves a complete file - [ ] Scene order follows the documented convention ### Watch out - Writing directly over a file means a crash mid-save can truncate a chapter, and for a writer that is the worst possible failure. Write to a temporary file and rename over the target, which is atomic and cannot leave a half-written manuscript. ## M2 · A story bible built from the text The application knows who is in your book without you filling in a form. ### Steps 1. Extract candidates Proper nouns with frequency and first appearance. A model pass can improve it, but the deterministic pass is the baseline. Files: `src/bible/extract.ts` 2. Require confirmation Nothing enters the bible without the writer saying so. Manual entries are marked and never overwritten. Files: `src/bible/confirm.ts` 3. Store as Markdown in the folder It travels with the manuscript and is readable without this application, which is the point. Files: `src/bible/write.ts` ### Done when - [ ] Extraction proposes mostly correct characters on a real manuscript - [ ] Every proposal requires confirmation - [ ] A manual entry survives re-extraction - [ ] The bible is readable Markdown in the manuscript folder ### Watch out - Automatic extraction will confidently invent a character from a repeated phrase and miss one introduced only by a nickname. Present extraction as suggestions requiring confirmation rather than as facts, and make manual entry as fast as accepting a suggestion. ## M3 · Context assembled, not dumped A request carries what is relevant, and you can see exactly what was sent. ### Steps 1. Assemble by relevance Current and previous scene always, bible entries for present entities, then search results until the budget is reached. Files: `src/context.ts` 2. Show what will be sent The actual assembled text, not a summary of it. A writer deciding whether to use a hosted model needs to see this. Files: `src/ui/ContextPreview.tsx` 3. Record it Scene identifiers on the request row. This is the audit trail that makes the privacy claim checkable. Files: `src/requests.ts` ### Done when - [ ] Assembled context is visible before sending - [ ] Context stays within the token budget at any manuscript length - [ ] Included scenes are recorded on the request - [ ] Exceeding the budget refuses rather than truncating mid-scene ### Watch out - Sending the whole manuscript with every request is expensive, slow, and worse for quality than a focused selection, because the relevant paragraph is buried. It also means the entire book leaves the machine on every keystroke-triggered suggestion, which is a privacy decision made by accident. ## M4 · Suggestions as diffs Nothing changes your text without you seeing exactly what would change. ### Steps 1. Implement suggestion actions Expand, rewrite, describe and continue. Each is a prompt template plus the assembled context. Files: `src/actions/` 2. Diff at word level Sentence-level diffs on prose are unreadable. Word level with sentence grouping is what a writer can actually judge. Files: `src/diff.ts` 3. Preserve originals On the suggestion row, so an accepted change stays reversible beyond the undo stack. Files: `src/suggestions.ts` ### Done when - [ ] Every suggestion is shown as a diff first - [ ] Partial acceptance applies only the selected part - [ ] A rejected suggestion can be retrieved - [ ] The original is recoverable after further editing ### Watch out - Applying a suggestion directly into the text and relying on undo loses the original as soon as anything else is typed, and a writer who accepts a rewrite and then dislikes it has no way back. Keep the original on the suggestion row, so accepted suggestions remain reversible for as long as the record exists. ## M5 · Cost, and the local option You know what each request cost, and you can choose to send nothing off the machine. ### Steps 1. Record cost per request Tokens in and out, priced by model. Aggregate by session and by manuscript. Files: `src/cost.ts` 2. Add a local model option Through a local endpoint. Quality will be lower and privacy absolute, which is a trade the writer should make knowingly. Files: `src/models/local.ts` 3. Make the active model visible Persistently in the interface, with whether text leaves the machine stated plainly. Files: `src/ui/ModelBadge.tsx` ### Done when - [ ] Every request records tokens and cost - [ ] The session total is visible while writing - [ ] Local model suggestions work with networking disabled - [ ] The interface states whether text leaves the machine ### Watch out - A per-request cost of a fraction of a cent feels free and adds up quickly during a writing session where you try twenty variations of a paragraph. Show the session total in the interface rather than only in a settings pane, because that number changes behaviour and a monthly bill does not. ## M6 · Export, backup and the ownership question (production only) The manuscript is yours in every sense, and you can prove what was sent where. ### Steps 1. Export and compile Markdown is already the format. Compilation is concatenation with scene ordering and front matter. Files: `src/export.ts` 2. Make the request log readable and exportable A writer should be able to answer what left their machine, in a file they can keep. Files: `src/requests.ts` 3. Record provider data terms with a date Which provider, what their terms say about training, and when you checked. Terms change. Files: `docs/providers.md` ### Done when - [ ] Export produces correct scene order in both formats - [ ] The request log exports and shows destinations - [ ] Provider data terms are recorded with a check date - [ ] A request log can be deleted deliberately ### Watch out - Provider terms on whether submitted text may be used for training vary and change, and for an unpublished manuscript that question matters more than it does for most content. Record which provider and which terms were in effect, with the date, rather than assuming the writer checked.
# Operations · Sudowrite ## Backup The manuscript folder is everything and is plain files. Version control it if you can, because history for a manuscript is worth more than for most data. The index and request log are local and rebuildable except for the cost history. ## Restore Restore the folder and open it. The index rebuilds. If anything else is required, something authoritative has leaked out of the files. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring Session cost against the warning threshold, and index freshness. There is no service to watch, which is the point. ## Incident checklist The incident that matters is a lost manuscript, and the controls are atomic saves plus your own backup. The second is discovering a provider changed its data terms, which is why they are recorded with a date rather than assumed. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] A killed process during save is confirmed to leave a complete file - [ ] Assembled context is confirmed visible before every send - [ ] Originals are confirmed recoverable after an accepted suggestion and further editing - [ ] Local model suggestions are confirmed to work with networking disabled - [ ] Provider data terms are recorded with a check date ## Launch constraint Do not market omitted Sudowrite capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered.
# Copy to .env and fill in. Never commit .env; this file documents it. # Required. The folder of Markdown files. Your data, in your backup. MANUSCRIPT_PATH=~/Writing/Novel # Required. Search index and request log. Deliberately outside the manuscript folder so syncing cannot corrupt it. INDEX_PATH=~/.local/share/write/index.db # Required. Which adapter is active. Local means nothing leaves the machine. MODEL_PROVIDER=local # Optional · secret. Only needed for a hosted provider. Set a spend limit at the provider as well. MODEL_API_KEY=sk-... # Required. Maximum assembled context. Exceeding it refuses rather than truncating. CONTEXT_TOKEN_BUDGET=8000 # Required. Session spend at which a warning appears. Visible while writing rather than in settings. SESSION_COST_WARN_USD=2
$ choose a build depth, inspect the files, then open the complete pack in your agent
People still pay for Sudowrite because the paid tool is already present everywhere they write and its suggestions have been tuned across millions of documents. The recurring cost buys model quality, extension compatibility, dictionaries, releases, and sync, not just the visible interface.
xits fiction-tuned workflows, context handling, and model orchestration
xcross-device sync
xlarge proprietary style models
xbrowser and office-suite extensions
xteam style guides
Don't feel like building it? These folks already made it free.
no votes, no pay-to-list · just what's real
Sudowrite pricing
| plan | monthly | annual (per mo) | what you get |
|---|---|---|---|
| hobby & student | $19/user | $10/user | 225,000 credits/month |
| professional | $29/user | $22/user | 1,000,000 credits/month |
| max | $59/user | $44/user | 2,000,000 credits/month; unused included credits roll over for up to 12 months |
| enterprise | custom | — | Custom limits and terms |
free tierno free tier
billingmonthly + annual (up to 50% lower); no-card free trial; subscriptions auto-renew
hidden costsHobby and Professional included credits expire every monthly refresh; Max rollover lasts at most 12 months. Extra credits can be purchased, never expire, but can only be used while a subscription is active.
verified 2026-08-12 · source ↗
Vibecode Sudowrite
Kinda. The core of Sudowrite is buildable in a weekend with the prompt on this page, but there are real gaps: its fiction-tuned workflows, context handling, and model orchestration, cross-device sync. Read the honest list above before committing.
How much does Sudowrite cost?
Sudowrite's pricing is usage-based or varies by plan · Official entry monthly tier; credit allowances differ by plan..
What do I lose by replacing Sudowrite?
Honestly: its fiction-tuned workflows, context handling, and model orchestration; cross-device sync; large proprietary style models; browser and office-suite extensions; team style guides. If any of those are load-bearing for you, keep paying.
Is there an open-source alternative to Sudowrite?
Yes: KoboldCpp (One executable plus a model download gets you story continuation, memory, world info and author’s notes; elegance was not bundled.) The prompt is for when you want it exactly your way.