# Prompts

A **prompt** in Inkhaven is a reusable template you can dispatch to the
AI pane without retyping the whole instruction. Type `/` in the AI prompt
bar to open the picker, choose one, hit Enter, and the prompt's text
(with `{{selection}}` / `{{context}}` substituted) lands in the bar ready
to send.

Two sources of prompts are merged in the picker:

1. **System prompts** — `prompts.hjson` in the project root. Ship-able,
   versionable, written in HJSON. Marked `[ system ]` in cyan.
2. **Book prompts** — paragraphs in the **Prompts** system book of the
   current project. Author them in the TUI like any other paragraph,
   carry them with the manuscript. Marked `[ book ]` in green.

Both kinds support `{{selection}}` and `{{context}}` substitution.

## Table of contents

- [The picker workflow](#the-picker-workflow)
- [Direct invocation: `/name`](#direct-invocation-name)
- [System prompts in `prompts.hjson`](#system-prompts-in-promptshjson)
- [Book prompts in the Prompts system book](#book-prompts-in-the-prompts-system-book)
- [Substitutions: `{{selection}}` and `{{context}}`](#substitutions-selection-and-context)
- [Built-in special prompts](#built-in-special-prompts)
- [Patterns for good prompts](#patterns-for-good-prompts)

## The picker workflow

1. Focus the AI prompt bar (`Ctrl+I`).
2. Type `/`. The picker overlay opens just above the bar.
3. Keep typing — the list filters live by substring across both the
   prompt name and description.
4. `↑` / `↓` to move the selection, `Enter` (or `Tab`) to expand the
   chosen template into the bar.
5. Edit the expanded text if you want; then `Enter` to send.

```
┌── Prompts ─────────────────────────────────────────────────┐
│  system  /tighten                                          │
│         Tighten the prose without changing meaning         │
│   book   /worldbuilding-pass                               │
│         Run a worldbuilding consistency pass on selection  │
│  system  /darker                                           │
│         Make the tone darker, keep facts                   │
└────────────────────────────────────────────────────────────┘
> /tigh│
```

Press `Esc` to close the picker without picking.

## Direct invocation: `/name`

You can skip the picker by typing the full name yourself. From the AI
prompt:

```
/tighten
```

`Enter` looks up `tighten` (system first, then the Prompts book by
slug or title), expands the template with the current selection, and
sends. If the name doesn't match anything you get a status like
`no prompt 'tighten' — type / to see the list`.

Trailing text after the name is **ignored** by the resolver — the
template is what gets sent. To append a custom note, expand the prompt
into the bar (Enter inside the picker) and edit it before submitting.

## System prompts in `prompts.hjson`

The file lives at `<project-root>/prompts.hjson` and is generated by
`inkhaven init` from a shipped default. Its shape:

```hjson
{
  prompts: [
    {
      name: "tighten"
      description: "Tighten the prose without changing meaning"
      template: '''
        Tighten the prose below without changing meaning or voice.
        Aim for 10–20 % fewer words. Keep Typst markup verbatim.

        --- begin
        {{selection}}
        --- end
      '''
    }

    {
      name: "darker"
      description: "Make the tone darker, keep facts"
      template: '''
        Rewrite the passage in a darker tone — somber palette, unease,
        understated dread. Keep every fact, name, and timeline intact.
        Preserve any Typst markup.

        Context: {{context}}

        --- begin
        {{selection}}
        --- end
      '''
    }
  ]
}
```

Each entry has three fields:

| Field | Description |
| ----- | ----------- |
| `name`        | The `/name` identifier. Lowercase, no spaces, hyphens allowed. |
| `description` | One-line blurb shown in the picker. Make it scannable. |
| `template`    | The actual prompt body. Use HJSON triple-quoted strings (`'''`) for multi-line content; placeholders `{{selection}}` and `{{context}}` are substituted at send time. |

You edit this file with any text editor. Inkhaven reloads it when the
TUI launches; live reloads are not (yet) supported, so close and
reopen after editing.

### Add a new system prompt

Append an entry to the `prompts:` list:

```hjson
{
  name: "expand"
  description: "Expand the selected passage by ~30 %, same voice"
  template: '''
    Expand the passage below by roughly 30 %. Preserve voice and
    Typst markup. Add concrete sensory detail; do not introduce new
    plot facts.

    --- begin
    {{selection}}
    --- end
  '''
}
```

Save. Restart the TUI. `/expand` shows in the picker.

## Book prompts in the Prompts system book

Project-local prompts live as **paragraphs** under the `Prompts` system
book. To create one:

1. Navigate to the **Prompts** book in the Tree pane.
2. Press `+` to add a paragraph, or `P` to insert one after the
   cursor.
3. In the Add modal, enter a title (this becomes the prompt's
   description in the picker).
4. Press Enter to open the new paragraph in the editor.
5. Write the prompt body. It can include `{{selection}}` / `{{context}}`
   substitutions exactly like system prompts.
6. `Ctrl+S` to save.

The paragraph's **slug** is the `/name`; the **title** is the
description in the picker.

For example, a title of `"Worldbuilding consistency pass"` slugs to
`worldbuilding-consistency-pass`. Typing `/world` in the picker
filters straight to it.

### Why use a book prompt instead of `prompts.hjson`?

- **Travels with the manuscript.** Anyone who clones the project gets
  your prompts for free.
- **Editable in the TUI.** No round-trip through a text editor.
- **Indexed for search.** Semantic search and Tantivy hits both find
  it; `inkhaven search "tighten"` lists your book prompts alongside
  prose.
- **Snapshot-able.** F5 captures a snapshot before you tweak the
  prompt; F6 picks an older version back out.

Use `prompts.hjson` for prompts you want to ship between projects (your
"signature" templates). Use the Prompts book for project-specific
prompts that depend on this manuscript's voice / world / glossary.

### Heading stripping

When a book prompt is expanded into the AI prompt bar, the leading
`= Title` Typst heading line is stripped so the model doesn't receive
editor chrome — only the prose. Empty leading blank lines after the
heading are also dropped.

## Substitutions: `{{selection}}` and `{{context}}`

Two placeholders are filled in at send time:

| Placeholder | What it becomes |
| ----------- | --------------- |
| `{{selection}}` | The current editor selection. If no selection is active, the **entire open paragraph** is substituted. Used as the working text in most prompts. |
| `{{context}}`   | A breadcrumb of titles for the open paragraph: `Book › Chapter › Subchapter › Paragraph`. Useful when you want the model to know where in the manuscript you're working without flooding it with the full text. |

You can use both in the same template:

```text
Context: {{context}}

Selection:
{{selection}}
```

Both substitutions are pre-rendered before the prompt is sent — what
the model receives is plain text with the placeholders already
replaced. Inkhaven never modifies your `prompts.hjson` or book
paragraphs at runtime.

## Built-in special prompts

A few prompt-driven flows are powered by **named lookups** with fallback
to a built-in default:

### Grammar check (`F7`)

Resolver, in order:

1. Paragraph slugged `grammar-check` (or title containing "Grammar
   check") in the Prompts book.
2. Entry named `grammar-check` (or `grammar check`) in `prompts.hjson`.
3. Built-in fallback: a copy-editor prompt scoped to the configured
   `language` that preserves Typst markup and emits the corrected
   paragraph between `<<<CORRECTED>>>` / `<<<END>>>` markers.

You can override #1 or #2 to customise the grammar style guide. Make
sure your custom prompt **also emits the markers** if you want the AI
pane's `g`-apply key to extract just the corrected text. The markers
look like:

```
<<<CORRECTED>>>
the corrected paragraph here, Typst markup preserved
<<<END>>>
```

See `Tutorials/06-grammar-check.md` for the full apply flow.

### Help question (`F1` / `Help!` prefix)

This one is **not** user-customisable — F1 uses a fixed strict-grounding
system prompt, RAGs against the Help book paragraphs, and pins the
inference to Local mode so the model never invents features.

## Patterns for good prompts

A few patterns that hold up across providers:

1. **Lead with the role.** `You are a meticulous copy-editor reviewing
   …`. Models drift less from clear personas.
2. **Bound the change set.** `Keep voice and pacing. Do not introduce
   new facts. Preserve every Typst markup token verbatim.` Stronger
   constraints, fewer regressions.
3. **Frame the input.** Wrap `{{selection}}` in `--- begin / --- end`
   markers so the model can see exactly where the work text starts and
   stops.
4. **Ask for structured output.** A summary line + a list of changes +
   the corrected text is more useful than a wall of prose, and easier
   to apply with `r` / `i` / `b` / `g`.
5. **Defer style.** When you want the model to match an existing
   chapter's voice, pass that chapter as scope (`F9` set to Chapter
   then Enter). The chapter content arrives with the prompt; the
   model has a sample to imitate.

### Provider-specific notes

- **Gemini** — handles long contexts well; the F9 *Book* scope is
  practical even on full manuscripts up to ~200K tokens.
- **DeepSeek** — fast and inexpensive for tighten / rewrite passes;
  occasionally emits empty streaming chunks (a warning lands in
  `.inkhaven.log` and is harmless).
- **Ollama** — local, free, no API key. Smaller open models (`llama3.2`,
  `qwen2.5`) cope with editing tasks but struggle with long manuscripts;
  start with scope=Paragraph or scope=Selection.
- **Anthropic / OpenAI** — work via genai; add a provider entry per
  [`CONFIGURATION.md`](CONFIGURATION.md#llm).

## Where prompts intersect with scope and mode

Be aware of these orthogonal axes when designing prompts:

- **Scope (F9)** prepends a context block (selection / paragraph /
  subchapter / chapter / book) **above** the prompt text. The model
  sees both.
- **Inference mode (F10)** swaps the system prompt: Local forbids
  general knowledge, Full allows it. Grammar / Help inferences pin to
  Local regardless.
- **Chat history** is replayed on every non-Help / non-Grammar
  inference. F9-scoped content lands in history too — that's how
  follow-up questions work.

For one-shot tasks (tighten this paragraph, run grammar check), the
prompt itself can contain `{{selection}}` and you don't need scope or
chat history. For ongoing conversations (brainstorm a subplot, plan a
chapter), keep scope at None and let chat history accumulate; clear it
with `Ctrl+B C` when you want a fresh thread.
