Build Novelcrafter
KINDAback to the verdict
A novel-writing environment whose real problem is continuity across a hundred thousand words: a character's eye colour established in chapter three, a timeline that has to hold, and an assistant that must be given the relevant facts rather than allowed to invent them.
Before step 1
Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.
- installfree
Why Runs the local application.
Get it nodejs.org, or nvm install 22. open ↗
Verify
node --version prints v22 or higher - installfree, needs memory
Why An unpublished manuscript is the most private thing a novelist has, and the default should not be sending it to a service.
Get it Any local runner with a model that handles long context and follows structured instructions. open ↗
Verify
A generation completes locally using supplied context - have readyfree
Why Editor performance, fact extraction and continuity checking all behave completely differently at novel length, and a short test project demonstrates none of the problems.
Get it Your own draft, or a public domain novel imported as scenes and chapters.
Verify
Sixty thousand words are loaded as scenes with chapter structure - have readyfree
Why Continuity checking cannot be evaluated without knowing the right answer, and the interesting cases are the deliberate deviations fiction is full of.
Get it From your manuscript, list twenty established facts and introduce one genuine contradiction and one intentional deviation.
Verify
Both the contradiction and the intentional deviation are identified by hand first
Data model
Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.
- projects(id, title, model_policy, created_at) - chapters(id, project_id, ordinal, title) and scenes(id, chapter_id, ordinal, body, word_count, updated_at) - entities(id, project_id, kind, name, aliases_json) · kind is character, place, object or faction - facts(id, entity_id, attribute, value, established_in_scene_id, confidence, superseded_by) - timeline(id, project_id, scene_id, story_time, duration, ordering_note) · story order is not manuscript order - continuity_flags(id, scene_id, fact_id, kind, detail, resolved_at) · kind is contradiction, unestablished or anachronism - generations(id, scene_id, prompt, context_fact_ids_json, output, accepted) · what the assistant was told, kept - A fact records the scene that established it, so when that scene is rewritten the fact is flagged rather than silently orphaned.
Environment variables
These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.
| Variable | Needed | Example | Where the value comes from |
|---|---|---|---|
DB_PATH | required | ~/.local/share/novel/project.db | The manuscript and the bible. Back this up above everything else. |
DEFAULT_MODEL_POLICY | required | local | What a new project gets. Local, so sending a manuscript is a decision. |
CONTEXT_BUDGET_TOKENS | required | 8000 | How much context a generation may carry. Selection matters more than size. |
SESSION_GAP_MINUTES | required | 30 | The gap that ends a writing session and snapshots a version. |
EXPORT_FORMAT | required | docx | The format editors expect. Chapters and scene breaks preserved. |
HOSTED_API_KEYsecret | optional | sk-... | Only for projects explicitly set to a hosted model. Absent means local only. |
The build, in order
A manuscript that survives a hundred thousand words
Writing, reordering and searching stay fast at novel length.
Whole-manuscript loading fails exactly when the stakes are highest.
Files
src/scenes.tsGenerate a hundred thousand word fixture. Ten thousand proves nothing.
Files
tests/scale.test.tsPer keystroke fills the history and makes it useless.
Files
src/versions.ts
done when · tick each as it passeswatch out- Loading the whole manuscript into the editor works beautifully at twenty thousand words and becomes unusable at a hundred thousand, which is exactly when the writer has the most to lose. Load and save per scene, and test against a manuscript of realistic length from the start.
A bible whose facts point at the text
Every established fact knows which scene established it.
A fact with no source cannot be rechecked when the text changes.
Files
src/facts.tsA hand-maintained bible diverges within a week.
Files
src/extract.tsCharacters change. A later fact is not always a contradiction.
Files
src/facts.ts
done when · tick each as it passeswatch out- A bible maintained by hand diverges from the manuscript within a week, because writers change things while writing and updating a separate document is the first thing to go. Extract candidates from the text and have the writer confirm them, so the bible is derived from the manuscript rather than parallel to it.
Telling the assistant what it needs to know
A generation request carries the relevant facts and nothing irrelevant.
Entities in this scene first, then timeline neighbours.
Files
src/retrieve.tssrc/retrieve.test.tsA bad selection should be visible rather than mysterious.
Files
src/context-view.tsSo a wrong output can be traced to a missing fact.
Files
src/generations.ts
done when · tick each as it passeswatch out- Sending whatever fits, in bible order, fills the context with the protagonist's full history while omitting the one fact about the character actually in the scene. Select by relevance to the scene's entities and its position in the story, and show the writer what was selected so a bad selection is visible rather than mysterious.
Catching contradictions before a reader does
The manuscript is checked against the bible and against itself.
The writer needs to see the two passages together.
Files
src/continuity.tsOne is a possible mistake, the other is possibly just new.
Files
src/continuity.tsFiction is full of intentional deviations. Re-flagging them ends the feature.
Files
src/flags.ts
done when · tick each as it passeswatch out- Flagging every deviation as an error makes the check unusable in fiction, where a character lying about their eye colour is a plot point rather than a mistake. Let a flag be dismissed as intentional, keep the dismissal, and never raise the same one again.
Where the manuscript goes, and getting it outproduct builder
You choose whether the book ever leaves the machine, and it exports cleanly.
A global setting sends the wrong manuscript eventually.
Files
src/policy.tssrc/policy.test.tsChapters, scene breaks, the conventions editors expect.
Files
src/export.tsUseful to the writer, and increasingly asked about by publishers.
Files
src/provenance.ts
done when · tick each as it passeswatch out- A global model setting means one careless change sends an unpublished manuscript to a service, and for a novelist that is a genuinely serious event. Put the policy on the project and enforce it where the request is made, so a local-only project raises rather than sends.
That is the whole plan for Novelcrafter. What it deliberately does not cover is below · check the gaps before you call it a replacement.
- Prose quality.
- Publishing and print formatting.
- Collaborative writing.
- Plot generation.
- its mature manuscript workflow, codex structure, and community templates
- cross-device sync
- large proprietary style models
- browser and office-suite extensions
- team style guides
- A timeline view, which is where continuity problems become obvious
- Per-character voice notes fed into context, once retrieval is trustworthy
- Revision tracking across drafts, which is the other half of a long project
Need the files? The project pack on the verdict page hands your agent the whole brief · more writing tools.