Local Writing App

These guides describe v1.1.0, the latest release. The same guides are in the app under Help → Guides.

Writing prompts

A prompt is the instruction the app sends to the AI for you: continue this scene, tighten this paragraph, flesh out this character. Every prompt the app ships with is one you can open and read — and you can write your own.

A prompt is mostly plain text. Where it needs something from your story, it uses a blank the app fills in. This guide builds up from a one-line prompt to a complete one, a step at a time. Each step adds one idea; you can stop wherever you have what you need. When you want the exact details of something, the Prompt reference lists everything.

Your first prompt

Open ≡ menu → Prompts, click +, and pick a type under New prompt. The prompt opens with three tabs:

  • Template — what you write. This guide is about this tab.
  • Preview — exactly what will be sent, and what it will cost, updated as you type. Keep it open; it's the quickest way to see what a change does.
  • Setup — where the prompt is offered, what it asks you for, and what happens to the reply.

Now type a prompt. The simplest one is just an instruction:

Continue the scene in the same voice. Do not summarize.

Everything you type is sent as it is. That's a working prompt — it just can't see your story yet.

Blanks: {{ }} and {% %}

There are two kinds of blank, and they're the whole language:

  • {{ … }} puts a value into the text — {{ scene.title }} becomes the scene's title.
  • {% … %} does something and leaves no text behind — a choice, a loop, a setting.
Continue "{{ scene.title }}" in the same voice.

{{ scene.body }}

Now the AI gets the scene. Behind the scenes this is Jinja, a common template language, so {% if %}, {% for %} and {% set %} work the way its documentation describes.

If a name is mistyped — {{ scnee.title }} — the prompt stops with an error rather than quietly sending nothing. The Preview shows the error.

Who's speaking: roles

The AI doesn't receive one long text. It receives a short conversation of messages, and each message has a role:

  • system — the standing instructions: who to be, which rules to follow, what's true in your world.
  • user — the request: the passage, the question, the thing to work on.
  • assistant — the AI's own turn: an earlier reply, or an example of the answer you want.
From template to messages what you write, and what the AI receives Your template You are a careful line editor. no role {% role "system" %} {% include "Project settings" %} {% do use(inputs.lore) %} {% role "user" %} Tighten this: {{ selection }} placed, not printed What the AI receives system You are a careful line editor. Use British spelling. Write in the past tense. Lore, added by the app Honor · the Fearless — each entry once user Tighten this: The rain came down hard on the deck… Text with no role joins the default one, usually system. Lore you choose is added after your instructions, so what rarely changes is sent first and can be reused cheaply from one run to the next.

Text you don't mark goes to the prompt's default role — usually system — so a one-message prompt needs nothing more. When you want the instructions and the request apart, wrap each in {% role %}:

{% role "system" %}
You are a co-writer on a Western. Match the author's prose.
{% endrole %}
{% role "user" %}
Continue from here:

{{ scene.body }}
{% endrole %}

Keeping the instructions in system also saves money: the parts of a prompt that don't change from run to run are sent first, so the provider can reuse them cheaply. The app arranges that for you.

Reading your story

From the scene, you can reach the rest of your story:

Write You get
scene The scene the prompt runs on: scene.title, scene.body, scene.summary, and any field you've added.
project The project: project.title, and project fields such as project.spelling.
entry(inputs.character) An entry you picked when the prompt ran (see Asking for input).
selection The text you had selected.
text_before, text_after The text on either side of the cursor.

Every entry has the fields its type gives it — you design those in Custom fields. Read one with a dot. A field that points at another entry takes you to it, so you can keep going:

{% set honor = entry(inputs.character) %}
{{ honor.title }} serves aboard {{ honor.ship.title }}.

As of this scene

Lore can change partway through your book — a promotion, a betrayal, a lost arm (see Mutations). A prompt reads your story as it stands at this scene: the changes up to here are applied, and nothing from later scenes leaks in. When you want an entry as the book began, use original():

As of this scene Honor's rank, as each call sees it Earlier scenes This scene Later scenes — never seen selection → Lieutenant → Captain → Commodore original(x) Ensign as the book began lore_as_of("start") Lieutenant as the scene opens lore_as_of("selection") Lieutenant where the selection starts entry(x) · the default Captain up to the scene's end ◆ a change to a lore entry. A prompt with no scene reads every entry as the book began.
{{ honor.title }} began the book as a {{ original(honor).rank }}
and is now a {{ honor.rank }}.

Asking for input

An input is something the prompt asks you for each time it runs — a tone, a character, a length. Add inputs on the Setup tab, then read them by name:

{% role "user" %}
Rewrite this in a {{ inputs.tone }} tone, in about {{ inputs.length }} words:

{{ selection }}
{% endrole %}

An input that picks entries gives you a list of them: entry(inputs.character) takes the first, and use(inputs.lore) (next section) takes them all.

Choosing what the AI sees

The AI knows only what you send it. You could paste your whole world into every prompt, but that's slow, costly, and buries what matters. Instead you choose, and the app does the sending:

{% do use(inputs.lore) %}
{% do use(scene.pov) %}
{% do auto_lore() %}
  • use(x) adds an entry — or every entry in a picker — to what the AI sees.
  • auto_lore() lets the app add what the scene needs: the entries it links to and the ones it names.

These calls print nothing where you write them — that's why they sit in {% do %}. The app gathers everything you chose, sends each entry once, and puts it where it caches best. That's the difference between the three kinds of call you'll meet:

Three kinds of call what each leaves behind where you write it Read a value {{ entry(inputs.character).rank }} The value lands in your text … ranked Captain … also original, fields, pov, todos … Choose what the AI sees {% do use(inputs.character) %} Nothing here — the app places the entry with the lore, sent once also auto_lore, no_lore, lore_as_of … Print a ready-made block {{ story_so_far(scene) }} A whole block lands in your text <story_so_far> … </story_so_far> also plot_context, character_turns … Choosing calls print nothing, so write them as {% do … %}. The reference groups every call under one of these three.

What the app adds for auto_lore(), and how it keeps a big world within budget, is its own guide: What the AI sees.

Ready-made blocks

Some context is common enough that the app builds it for you. These print a block of text where you write them:

{% if story_so_far(scene) %}
## The story so far
{{ story_so_far(scene) }}
{% endif %}
  • story_so_far(scene) — a recap of every earlier scene, from their summaries.
  • plot_context(as_of=scene) — the plot board up to here, later reveals hidden.
  • character_turns(scene, character) — a roleplay scene as a back-and-forth chat (see Roleplay).

The {% if %} around story_so_far skips the heading on the first scene, where there is no story so far yet.

Stopping early

When a prompt can't do its job — no summary to draft from, nothing selected — say so, instead of sending the AI an empty request:

{% if not scene.summary %}
{{ stop("This scene has no summary yet. Write one, then run Draft again.") }}
{% endif %}

stop() ends the run before anything is sent and shows you the message.

Reusing pieces: snippets

Instructions you want in many prompts — your house style, the project's spelling and tense — belong in a snippet: a prompt you can't run on its own, kept to be included in others. Include one by its title:

{% role "system" %}
You are a line editor.
{% include "Project settings" %}
{% endrole %}

The snippet's text lands where you include it, and it can use the same blanks your prompt can. Change the snippet once, and every prompt that includes it follows.

Asking the AI to fill in fields

A prompt can ask the AI to fill in fields — "flesh out this character" — instead of writing prose. You promise which fields it will fill, show it the list, and the app accepts exactly those from the reply:

{% set e = entry(inputs.character) %}
{% for f in fields(e) if f.proposable %}{% do field_contract.store(f) %}{% endfor %}
Suggest values for these fields of {{ e.title }}:
{{ field_contract.render }}

fields(e) lists the entry's fields, so the same prompt works for a character, a place or a faction without naming a single field.

Putting it together

Here is a complete prompt that summarizes a scene. Every line is something this guide has covered:

{% if not scene or not scene.body.strip() %}
{{ stop("This scene is empty — write something first.") }}
{% endif %}
{% set nar = resolved_narration(scene) %}
{% role "system" %}
Summarize the scene in two or three sentences: what happens, who is in it, how it
ends. Neutral and factual; no commentary.
{% include "Project settings" %}
{% endrole %}
{% role "user" %}
{% do auto_lore() %}
{% if nar.character %}The point-of-view character is {{ nar.character.title }}.{% endif %}

{{ scene.body }}
{% endrole %}

resolved_narration(scene) is the scene's point of view and tense — set on the scene, or inherited from its chapter or book. The reference has it, and everything else, in detail.

Organizing prompts into menus

A / in a prompt's title files it in a submenu, so related prompts fold together:

Title In the menu
Revise / Tone Revise ▸ Tone
Revise / Length Revise ▸ Length
Summarize Summarize
  • Spaces around the / don't matter, and you can nest as deep as you like: Draft / Scene / Action.
  • A group never doubles as a prompt: with both Revise and Revise / Tone, the plain Revise moves inside the Revise group.
  • It only changes how the title is shown — nothing else about the prompt.

The same menu appears wherever you pick a prompt: the chat's prompt chip and an entry's + New conversation menu.

Where to go next

  • Prompt reference — every value, call and tag, one entry each.
  • What the AI sees — what auto_lore() adds and how the app keeps it within budget.
  • Context picker — giving a prompt exactly the scenes and lore you choose.
  • The built-in prompts — open any of them in ≡ menu → Prompts. They are written to be read, and they use only what this guide and the reference describe.