# Dev mode

`liebstoeckel dev` serves a deck with hot reload and a **dev shell**: your deck
on the right, a **sidebar** on the left with the slide list, the annotation
tools, and the agent's status. Its first tool is **annotations**: draw on a
slide, drop comments, and send the batch to an AI agent that edits your slide
source directly. The deck hot-reloads with the result seconds later, and one
click reverts a batch you don't like.

Your deck files stay ordinary MDX/TSX throughout: the agent writes the same
code you would, and nothing about dev mode appears in built decks.

Dev mode ships with the CLI. `liebstoeckel dev` works in any install of
`@liebstoeckel/cli`; the machinery is its own package,
`@liebstoeckel/dev-server`, which the CLI depends on, so there is nothing
extra to install. You only reach for the package directly when you want to
embed the dev server in your own tooling (it exports `startDevServer`).

## The loop

1. Start the server from your deck:

   ```bash
   liebstoeckel dev
   ```

   Open the printed URL. The sidebar lists your slides, and its header names what
   the agent is doing: **agent polling** (waiting for work), **agent working** (it
   holds a batch), **agent offline** (nobody is listening), or **server stopped,
   waiting** while the page waits for a restarted `dev` to come back, after which
   it reloads itself. Click a slide to jump to it; the
   chevron collapses the sidebar to a thin rail so the deck gets the full
   width, and on narrow windows it floats over the deck instead of pushing it.

2. In a terminal, have your agent enter the loop (the bundled agent skill knows
   this flow):

   ```bash
   liebstoeckel dev poll
   ```

3. In the browser: hit **Draw** or **Comment** in the sidebar, mark up the
   slide (Escape leaves the mode), **Save** (or **Clear** to drop the marks),
   then **Send to agent**.

4. The agent receives the batch (comments, strokes, an annotated screenshot,
   and the resolved slide source file), edits the slide, and replies. Hot
   reload shows the change; the sidebar shows what was applied. Decks scaffolded
   with the current `main.tsx` shape keep their position: only the edited
   slide re-renders, and the current slide and step survive the edit.

5. Not what you wanted? **Revert** puts the deck's source files back exactly as
   they were when that batch was dispatched (every text source is snapshotted,
   so shared components and the entry are covered too; a file the agent created
   is removed, but anything you add after the agent replied is yours and stays),
   and your annotations reopen for another round. If a file cannot be restored
   (locked by an editor, say), nothing is marked reverted; free the file and
   revert again. A file that has since become a symlink pointing outside the
   deck is left alone rather than written through. If the agent replied with an
   error instead, the batch stays revertable, so a half-applied attempt can be
   undone the same way. **Revert** always undoes the most recent batch (or the
   last one the agent failed on); press it again to walk further back. Because
   the sources go back to before that batch, every batch applied after it is
   undone too, and those annotations reopen as well. While a batch is with the agent its
   annotations are locked and Send and Revert wait (a restore mid-edit would
   wipe the agent's work, and a new snapshot would capture it half done); all
   come back the moment it replies. Send also waits while a batch is staged
   with no agent listening yet: one batch in flight at a time, so that every
   snapshot really is the state before its own batch. The last ten batches stay revertable;
   older snapshots are pruned (git covers anything beyond that).

**No agent running?:** Sending with no agent listening simply stages the batch: the next
`liebstoeckel dev poll` picks it up, even after a dev-server restart (the shell page
notices the restart and reloads itself; a polling agent receives `exit` and has to be
told to poll again). The same
applies when an agent takes a batch and never replies (a crash, a killed
terminal): its claim expires after five minutes and the next poll receives the
batch again.

## Adding a slide

Hover between two slides in the sidebar's list (or below the last one) and press
the **+** that appears. Describe the slide in one line ("a pie chart of cat
breeds with one takeaway") and **Add**. It shows as a pending row at that
position, and the **+** below it lets you queue the next one right after it, so
you can lay out several new slides before anything is sent. **Send to agent**
dispatches them with your other annotations. The agent
writes the slide file in your deck's style, registers it in the entry at that
position, and the sidebar jumps to it when it lands. Not right? **Revert**
removes the file and the registration again.

## What the agent receives

Each annotation carries the slide index and its resolved source file, your
comments with the element under each comment as a hint, your strokes, and a
screenshot of the slide with the marks baked in. Positions are fractions of
the slide itself, so they mean the same thing whatever the window size or
sidebar state. Comments read as design direction ("make
this heading calmer"); strokes read by shape: a loop means *this thing*, an
arrow means direction or movement, a cross means *remove*.

## Housekeeping

- Dev-mode state lives in `.liebstoeckel/dev/` inside the deck (annotations,
  screenshots, revert snapshots, the session token). It is git-ignored.
- An annotation you no longer want can be dismissed with the ✕ on its row; one
  that is currently with the agent is locked until it replies.
- On start, `dev` applies any pending
  [scaffold migrations](https://docs.liebstoeckel.app/reference/cli/#scaffold-migrations) for decks created
  under older conventions, one notice per patched file. The first run on an
  older deck adds the inert loader `<script>` tag to `index.html` (it only
  activates while `dev` is serving, and `build` strips it, so built decks ship
  byte-identical to before) and, for pre-dev-mode decks, upgrades the entry to
  the state-preserving hot-reload pattern.
- The dev API binds `127.0.0.1` by default and every mutating request needs the
  per-session token; pass `--host 0.0.0.0` only on networks you trust. Requests
  whose `Host` header is not `localhost`, a loopback address, or the bound
  hostname are refused, so a web page cannot reach the server by pointing its
  own domain at your machine (binding `0.0.0.0` lifts that check, since you are
  reaching it by a LAN address then). The deck itself is served under a path
  that includes the session token; `/deck` is a checked redirect to it, so the
  address bar shows the token path once you open the plain deck.