Skip to content

Blocks

A block is data the agent writes instead of markup: it names what to show, and the host draws it, so the agent never writes layout, styles, or the script that sends an answer back. A block is a fenced code block whose first word is the kind, or a GitHub alert; the fence may use backticks or tildes. Most kinds take YAML. Text fields (body, hint, note, …) take restricted inline Markdown: bold, italic, code, http(s) links, line breaks. No raw HTML anywhere.

Every kind at a glance
Kind Written as Answer kind
alert > [!NOTE|TIP|IMPORTANT|WARNING|CAUTION] title quote
mermaid Mermaid source
diff unified diff
cards YAML list of {title, tone, body}
options YAML list of {title, recommended, body, pros, cons} pick
form YAML {title, submit, questions: [{name, label, type, hint, required, other, choices}]} form
review YAML {title, submit, pick, columns, items: [{name, label, image, page, height, body}]} review
mindmap indented Markdown list; the first item is the root
suggest YAML list of strings or {label, text} suggest
checklist YAML {title, submit, items: [string | {name, label, checked, note}]} checklist
table YAML {columns, rows, sort, filter}
steps YAML {items: [{title, status: done|doing|todo|blocked, when, body}]}
stats YAML list of {label, value, delta, tone, note}
tree indented lines (two spaces per level, first line the root): name/ for a folder, then {tags} and # note

Four example pages, each mixing kinds:

A decision page: an alert, two options, and a form

Stats tiles, a steps timeline, and a sortable table

A directory tree with tags, a flowchart, and a diff

Two header designs to compare, with quick-reply chips below

A block that does not parse becomes an invalid segment that keeps its source and the reason; nothing throws, and the page shows the source with the reason.

A callout for what the reader must not miss: a GitHub alert, as in a README.

> [!WARNING] Heads up
> This **overwrites** the cache, see [the runbook](https://example.com/runbook).

The kinds are NOTE, TIP, IMPORTANT, WARNING, and CAUTION; the text after the marker is the title, and without it the title is the kind’s name. The body is inline Markdown over several lines. It takes no answer.

A warning callout

Short tiles side by side, each with a tone (note, ok, warn, risk) that shows as a tint, a badge, and an icon, never as a coloured edge.

```cards
- {title: Speed, tone: ok, body: "**2x** faster than the old path"}
- {title: Risk, tone: warn, body: "Needs a migration window"}
```

Cards with tones

It takes no answer.

Choices side by side with pros and cons; the reader picks one, and the pick is the answer.

```options
- title: SQLite
recommended: true
body: Embedded, zero setup
pros: [simple, fast]
cons: [single writer]
- title: Postgres
body: A server to run
pros: [concurrent writers]
cons: [more to operate, needs backups]
```

Two options with pros and cons

The agent receives, as soon as the reader picks (the element then locks itself):

User picked "SQLite".
Fields and details
  • title and body take inline Markdown; recommended is true (the badge reads “Recommended”) or a label of your own; pros and cons are lists of strings.
  • The answer names the option by its plain title, else by its number (1 for the first).
  • A pick sends at once, so there is no Send button. The “Other comments” box above the options is read when the option is picked (see Other comments).

Several questions answered in one go: single choice, multiple choice, or free text. Send delivers every answer in one message.

```form
title: Before I start
submit: Send
questions:
- name: ttl
label: How long should entries live?
required: true
choices: [1 hour, 1 day, {value: week, label: 1 week, recommended: true}]
- name: extras
label: Also do
type: multi
choices: [WAL mode, Nightly backup, Metrics]
- name: note
label: Anything I should know?
```

A form with a single choice, a multiple choice, and a text question

The agent receives:

User submitted a form in the HTML review pane:
- ttl: week
- extras: WAL mode, Nightly backup
- note: The API runs on two hosts next month.
Fields and details
  • A question has a name (the key in the answer; it defaults to the label), a label, an optional hint, and required. Its type is single (the default when it has choices), multi, or text (the default without choices).
  • A choices entry is a string, or {value, label, note, recommended}.
  • other adds a box for an answer of the reader’s own: a label for the box, or false to remove it. A choice question ends with such a box unless other: false.
  • A required question must be answered before Send. A question left empty is sent as (no answer).
  • The form stays editable after sending, so the reader can send a changed answer.

Designs, screenshots, or prototypes to compare: the reader comments on each item and, when pick is set, picks a favourite.

```review
title: Landing page
pick: true
columns: 2
items:
- {name: A, label: dark header, image: a.svg, body: "Calm, **high contrast**"}
- {name: B, label: light header, image: b.svg}
```

Two header designs to compare

The agent receives the picked item, each commented item with its comment, and the items left blank:

User reviewed "Landing page":
Picked: A (dark header)
- A (dark header): The contrast is right.
No feedback on: B (light header)
Fields and details
  • image is a path relative to the Markdown file (an item may be just that path); page is the path of an HTML prototype, shown in a sandboxed frame of height pixels. Neither may leave the page’s directory.
  • Items are named A, B, C… unless name says otherwise; the answer writes name (label).
  • pick: true shows a radio per item (a string sets its label); columns is 1 to 6.
  • A blank comment box means no feedback, so a review with every box blank is still an answer.
  • A picture opens enlarged in a lightbox.

A collapsible outline: an indented Markdown list whose first item is the root.

```mindmap
- Release
- Build
- bundle
- sign
- Ship
- tag
```

A collapsible mindmap

A mindmap has one root; indent the other top-level items under it. It takes no answer. Arrow keys move, Right and Left open and close, Enter and Space toggle.

Quick-reply chips: clicking one sends its text as the reader’s own message.

```suggest
- Go ahead
- {label: Not now, text: "Not now, ask me tomorrow"}
```

Quick-reply chips

The agent receives the chip’s text itself: Not now, ask me tomorrow. A string chip’s label and text are the same.

Items to tick; the ticked and unticked items come back together.

```checklist
title: Before release
items:
- {label: Run tests, checked: true}
- {label: Bump version, note: semver}
```

A checklist with a progress count

The agent receives:

User updated the checklist "Before release":
☑ Run tests
☐ Bump version

An item is a string or {name, label, checked, note}; the answer names it by name, which defaults to its label.

Sortable, filterable rows.

```table
columns:
- name
- {name: size, label: Size, type: number}
- {name: day, label: Shipped, type: date}
rows:
- [alpha, 1204, 2026-03-01]
- [beta, 2, 2026-01-15]
sort: -size
filter: true
```

A sortable table with a filter box

It takes no answer. A column is a name or {name, label, align, type: text|number|date}; a row is a list in column order or a map by column name. sort names a column, with a leading - for descending; filter: true adds a search box.

A timeline with a status per step.

```steps
items:
- {title: Design, status: done, when: Mon}
- {title: Build, status: doing, when: Tue, body: "Two of **four** services left"}
- {title: Legal review, status: blocked, body: Waiting on the vendor}
- {title: Ship, status: todo}
```

A steps timeline

It takes no answer. status is done, doing, todo, or blocked.

Metric tiles with an optional change and tone.

```stats
- {label: Users, value: "1,204", delta: "+4%", tone: ok}
- {label: Latency p95, value: 420 ms, delta: "+60 ms", tone: risk}
```

Metric tiles

It takes no answer.

A coloured unified diff.

```diff
@@ -1,4 +1,5 @@
export function get(key: string) {
- return store.get(key);
+ const hit = store.get(key);
+ return hit ?? load(key);
}
```

A coloured diff

It takes no answer.

A diagram, drawn on the server in the page’s colour scheme. Flowcharts and state diagrams can be commented on node by node (see Markdown pages).

```mermaid
graph TD
A[Start] --> B{Ok?}
B -->|yes| C[Done]
B -->|no| D[Fix]
```

A flowchart

It takes no answer of its own. Flowchart, state, sequence, class, ER, and xychart diagrams draw; use a flowchart for flows and concept maps.

A directory tree for structure reviews, with folders that collapse and expand:

pi-better-show/
src/ # one package, four entries
core/ {focus} # parser and text, no DOM
parse.ts
validate.ts {new} # checks answers
elements/ # web components
tests/
README.md {changed}

A directory tree with tags and notes

A folder’s name ends with /; tags are new, changed, removed, focus, risk (an unknown tag makes the block invalid); the note after # takes inline Markdown; a … N more line stands for entries a limit cut. It takes no answer.

Limits and fallback

A tree has at most 2,000 nodes and 32 levels (the root is level 1). The block carries a snapshot, so every host draws it; toText writes it in tree style (├──, └──, tags as [new], notes after —), in a fence on Discord.

pbs-tree prints the tree fence for a folder: npx pbs-tree [dir] or bunx pbs-tree [dir] (plain JavaScript on node built-ins; runs under node and bun). Output is sorted by name in byte order, so a snapshot is reproducible; symlinks are not followed. Flags follow Linux tree where they exist:

Flag Meaning
-L n depth
-d folders only
-a include dot files (default skips them; .git is always skipped)
-I pattern leave out names matching the glob (* ? [..]); | separates several; repeatable
-P pattern list only files matching; a folder is kept when it holds a match (even below the -L cut); ignored with -d
--gitignore honour .gitignore files, those of the folder and of the folders above it up to the repository’s top
--dirsfirst folders before files
--filelimit n do not open a folder holding more than n entries; writes … N more in its place
--prune drop folders that hold nothing (what -L hides still counts); ignored with -d
--changed-since <rev> tag new, changed, removed from git diff --name-status <rev> against the working tree; removed files are listed though absent, untracked files are new, a rename is a removed and a new file
Limits and exit codes

A tree over the block’s limits is refused (exit 1) with the node count; narrow it with -L, -I, --gitignore. Names that would read as a note or tags get a no-break space, and a newline in a name becomes ?. Exit codes: 0, 1 for a failure (not a folder, git failed, too large), 2 for a bad flag.

Every submittable element (pbs-form, pbs-review, pbs-checklist, pbs-options) shows a fixed “Other comments” box above its submit button (above the options in pbs-options; the box is read when the option is picked). Its trimmed text travels in the same answer as the reserved field pi-note (NOTE_FIELD), once, and a blank box sends nothing; the agent reads it as a last line, Other comments: ….

For hosts

validateAnswer accepts pi-note for every interactive kind; a form question or checklist item may not be named pi-note. A host that shows the elements needs to do nothing new: the field is in answer.fields, so validateAnswer and encodeAnswer already pass it through. A host that rebuilds the answer from selected fields must forward pi-note; set answered to the submitted fields so the locked element shows it. The words are otherComments in configure({strings}).

A block that gathers several values (a form with a multi question, a checklist, a review) sends nothing on a tick, a pick, or typing: choices stay changeable until the reader presses Send, and a required multi question with none chosen is refused at that question. Only options and suggest send on the click.