@khanglvm/relay 0.12.3 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/docs/AGENT.md +75 -6
- package/package.json +1 -1
- package/skills/relay/SKILL.md +180 -4
- package/src/cli.js +98 -17
- package/src/mcp-ui/board.js +213 -10
- package/src/server.js +8 -3
- package/src/spec.js +272 -47
- package/src/ui/annotate.css +15 -1
- package/src/ui/annotate.js +40 -2
- package/src/ui/app.js +232 -8
- package/src/ui/blocks.css +164 -4
- package/src/ui/blocks.js +430 -33
- package/src/ui/style.css +90 -0
package/README.md
CHANGED
|
@@ -87,6 +87,7 @@ Node ≥ 18; Chart.js / Mermaid / Graphviz are vendored and lazy-loaded offline.
|
|
|
87
87
|
| | |
|
|
88
88
|
|---|---|
|
|
89
89
|
| `rly help` | every command at a glance |
|
|
90
|
+
| `rly view <file.md> …` | open a quick read-only board that renders local markdown file(s) — library-free; great for plans, READMEs, reports |
|
|
90
91
|
| `rly install --target <agent>` | write relay's rules into an agent's instruction file — `claude` `codex` `cursor` `copilot` `kiro` `windsurf` `cline` `gemini` `opencode` `droid` `agents`; `--all`, `--scope`, `--print`, `--list` |
|
|
91
92
|
| `rly upgrade` | update the CLI **and** refresh the skill in one step (safe around open boards; `--dry-run`, `--cli-only`, `--skill-only`) |
|
|
92
93
|
| `rly mcp` | run relay as an MCP App server so boards render **inline** in the chat — **stdio** for local desktop hosts (Claude Desktop, Codex), or `rly mcp --http` (Streamable HTTP) for web/mobile/remote; `rly mcp config` / `rly mcp install --target claude\|codex` to register it |
|
package/docs/AGENT.md
CHANGED
|
@@ -5,6 +5,15 @@ rich content blocks (markdown, charts, diagrams, tables, code, custom HTML),
|
|
|
5
5
|
then **wait for them to click Submit** and read the answers as JSON from stdout.
|
|
6
6
|
No "type 'done' in the terminal", no hand-rolled HTML+server.
|
|
7
7
|
|
|
8
|
+
**Match the surface to the content.** You run inside a terminal, an IDE side-panel
|
|
9
|
+
(VS Code / JetBrains / Cursor), or a plain-text chat — none of which can render a
|
|
10
|
+
markdown file, an image, a chart, a table, a diagram, or a diff. They degrade to
|
|
11
|
+
walls of monospace text, or can't show it at all (a terminal can't draw a PNG).
|
|
12
|
+
relay renders it in a real browser board. So whenever your output would read better
|
|
13
|
+
than monospace — or is a file/visual the user should look at — show it in relay
|
|
14
|
+
instead of printing it. Fast paths: `rly view file.md` (or `data.csv`), `rly diff`,
|
|
15
|
+
or an `image`/`chart`/`table` block on a board.
|
|
16
|
+
|
|
8
17
|
**Use relay even in plan mode** — for any clarifying or decision question (Claude
|
|
9
18
|
Code / Codex), route it through a relay board, **not** the native
|
|
10
19
|
`AskUserQuestion` / ask-user tool: that tool is **not** a substitute, even though
|
|
@@ -135,6 +144,21 @@ Visualization-only (no questions; submit button reads "Acknowledge"):
|
|
|
135
144
|
rly show --html-file prototype.html --title "Dashboard concept" --height 600
|
|
136
145
|
```
|
|
137
146
|
|
|
147
|
+
Read a markdown file (no questions; library-free renderer; submit reads "Done"):
|
|
148
|
+
|
|
149
|
+
```sh
|
|
150
|
+
rly view PLAN.md # one file
|
|
151
|
+
rly view README.md CHANGELOG.md # several, each under a filename heading
|
|
152
|
+
rly view data.csv # .csv/.tsv/.json → a sortable, filterable table
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Show a git diff in one step (sugar — runs git diff, opens a diff board):
|
|
156
|
+
|
|
157
|
+
```sh
|
|
158
|
+
rly diff --detach # working-tree diff (git args pass through:
|
|
159
|
+
rly diff --staged --split # --staged, HEAD~1, -- path, …); --split = side-by-side
|
|
160
|
+
```
|
|
161
|
+
|
|
138
162
|
## Board spec (JSON)
|
|
139
163
|
|
|
140
164
|
```jsonc
|
|
@@ -188,11 +212,27 @@ rly show --html-file prototype.html --title "Dashboard concept" --height 600
|
|
|
188
212
|
| `text` | `"string"` |
|
|
189
213
|
| `textarea` | `"string"` |
|
|
190
214
|
| `scale` | number (`min`…`max`, default 1–5) |
|
|
191
|
-
| `color` |
|
|
215
|
+
| `color` | color string (e.g. `"#c2674b"`, `"rgb(...)"`, a name) — native picker + hex field; optional `presets:["#…"]` small swatches, or a `palette:[…]` of labeled swatch CARDS (click = answer, each card commentable, any CSS color system) |
|
|
216
|
+
| `rank` | `["b","a","c"]` — ordered option values, highest priority first |
|
|
217
|
+
| `checklist`| `{ "login":"pass", "checkout":"fail" }` — per-item status map |
|
|
218
|
+
| `allocate` | `{ "eng":50, "design":30, "ops":20 }` — budget distributed across options |
|
|
219
|
+
|
|
220
|
+
`rank` renders the `options` as a reorderable list (drag or ↑/↓ buttons); the
|
|
221
|
+
user prioritizes them and the answer is the ordered array of values. Needs ≥2
|
|
222
|
+
options, takes no "Other", and **always returns a value** (an untouched rank
|
|
223
|
+
submits the authored order), so it never appears in `skipped`. Reach for it on
|
|
224
|
+
roadmap / feature-prioritization questions instead of forcing a single pick.
|
|
225
|
+
|
|
226
|
+
`checklist` gives each `option` a status control (default Pass / Fail / N·A;
|
|
227
|
+
override with `"statuses"`); answer is `{optionValue: statusValue}`. `allocate`
|
|
228
|
+
distributes a budget (`"total"`, default 100) across the options with sliders +
|
|
229
|
+
a live total bar; answer is `{optionValue: number}`. Both take per-option
|
|
230
|
+
`description`/`blocks` like single/multi.
|
|
192
231
|
|
|
193
232
|
Aliases accepted: radio/choice/select→single, checkbox→multi,
|
|
194
233
|
boolean/bool/yn→yesno, input→text, longtext→textarea, rating/likert→scale,
|
|
195
|
-
colour/swatch→color
|
|
234
|
+
colour/swatch→color, ranking/order/ordering/prioritize/sort→rank,
|
|
235
|
+
signoff/qa→checklist, budget/distribute/points→allocate.
|
|
196
236
|
|
|
197
237
|
### Result JSON (stdout)
|
|
198
238
|
|
|
@@ -279,11 +319,14 @@ Rules of thumb:
|
|
|
279
319
|
|
|
280
320
|
```jsonc
|
|
281
321
|
// Markdown — built-in mini renderer, no library. Headings, lists, code, quotes,
|
|
282
|
-
// links, and GFM pipe tables all render. For real tabular DATA use a
|
|
283
|
-
// block instead (sortable + per-cell comments); markdown tables are display-only.
|
|
322
|
+
// links, images, and GFM pipe tables all render. For real tabular DATA use a
|
|
323
|
+
// `table` block instead (sortable + per-cell comments); markdown tables are display-only.
|
|
284
324
|
{ "type": "markdown", "md": "## Heading\nAny **CommonMark** prose." }
|
|
325
|
+
// Load the body from a local .md file instead of inlining it (README, plan, report).
|
|
326
|
+
// Quick view of whole file(s) with no questions: `rly view file.md [more.md …]`.
|
|
327
|
+
{ "type": "markdown", "mdFile": "README.md" }
|
|
285
328
|
|
|
286
|
-
// Mermaid diagram — vendored, lazy-loaded; natural height, max
|
|
329
|
+
// Mermaid diagram — vendored, lazy-loaded; natural height, max 800 px + scroll (set height: to override)
|
|
287
330
|
{ "type": "mermaid", "code": "graph TD; A-->B; B-->C", "height": 400 }
|
|
288
331
|
|
|
289
332
|
// Graphviz diagram — vendored viz-standalone.js (Graphviz-WASM), fully offline
|
|
@@ -397,6 +440,23 @@ real path over telling the user to paste it into a terminal.
|
|
|
397
440
|
|
|
398
441
|
### When to use which block
|
|
399
442
|
|
|
443
|
+
**Reach for the most specific component — never default to prose.** relay has a
|
|
444
|
+
purpose-built block/question type for most content; falling back to a `markdown`
|
|
445
|
+
block (or terminal text) when a dedicated one fits is the #1 way agents
|
|
446
|
+
under-use relay. A metric → `kpi`, a before/after → `compare`, a priority call →
|
|
447
|
+
a `rank` question. When unsure what exists, consult this table, `rly schema`, and
|
|
448
|
+
the question-types section above.
|
|
449
|
+
|
|
450
|
+
**By domain** (serve the reader the right shape):
|
|
451
|
+
- **Business / PM / exec** — `kpi` + `chart` + `table` for numbers; `rank`
|
|
452
|
+
(prioritize), `allocate` (split a budget), `checklist` (sign-off), `scale` (confidence).
|
|
453
|
+
- **Designer** — `image` (+`pins`), `compare` (before/after), `palette`, `typography`;
|
|
454
|
+
a visual inside each option.
|
|
455
|
+
- **Engineer** — `diff` (`rly diff`), `code` (line-comments), `mermaid`/`graphviz`
|
|
456
|
+
(architecture; editable mermaid to co-design).
|
|
457
|
+
- **Data / analyst** — `table` with `rowsFile`/`filterable`/`exportable`
|
|
458
|
+
(`rly view data.csv`), `chart`.
|
|
459
|
+
|
|
400
460
|
| Block | Best for |
|
|
401
461
|
|---|---|
|
|
402
462
|
| `mermaid` | flows, state machines, architecture overviews, sequence diagrams |
|
|
@@ -404,14 +464,23 @@ real path over telling the user to paste it into a terminal.
|
|
|
404
464
|
| `plantuml` | UML diagrams (sequence, class, component) via server rendering; great for detailed interface contracts |
|
|
405
465
|
| `chart` | numbers, trends, comparisons, metrics |
|
|
406
466
|
| `table` | structured comparisons, option matrices, data grids — **use this for any tabular data**: it's sortable and every cell is commentable, unlike a markdown pipe table |
|
|
407
|
-
| `markdown` | prose context, background, instructions, section headings (renders GFM pipe tables too, but reach for a `table` block for real data) |
|
|
467
|
+
| `markdown` | prose context, background, instructions, section headings (renders GFM pipe tables + images too, but reach for a `table` block for real data); load a whole `.md` file with `mdFile` — or `rly view file.md` for a quick read-only board |
|
|
408
468
|
| `code` | code snippets, config examples, command output — syntax-highlighted + line-numbered; load from a file with `codeFile` |
|
|
409
469
|
| `diff` | proposed code changes / before-after — a unified diff rendered as a colored git-style comparison (no git needed) |
|
|
410
470
|
| `video` | demos, screen recordings, walkthroughs — YouTube/Vimeo embeds, a media URL, or a local video file (streamed) |
|
|
411
471
|
| `image` | screenshots, mockup exports, photos — local files embed and work offline |
|
|
412
472
|
| `palette` | color palettes / themes — swatch cards with hover-hex + click-to-copy; pair with a `color` question to let the user pick |
|
|
473
|
+
| `kpi` | big-number metric cards (`items:[{label,value,delta?,dir?,sub?}]`) with up/down/flat-tinted deltas — at-a-glance numbers without a chart |
|
|
474
|
+
| `typography` | type specimens (`specimens:[{label?,size?,weight?,font?,text?}]`) — react to type choices like a palette |
|
|
475
|
+
| `compare` | before/after images with a draggable divider (`before`/`after` = url/path/`{src,label}`) — redesigns, before-after fixes |
|
|
413
476
|
| `html` | anything else — pixel-perfect mockups, custom widgets, embeds |
|
|
414
477
|
|
|
478
|
+
Two cross-block fields work on **any** block: `"ref":"name"` makes it openable in
|
|
479
|
+
a modal via a markdown `[label](#ref:name)` link (point a question at a visual
|
|
480
|
+
shown earlier — no scrolling); `image` blocks also take `"pins":true` for
|
|
481
|
+
click-to-drop coordinate pin-comments. `table` blocks take `"rowsFile"` (load
|
|
482
|
+
rows from .csv/.tsv/.json), `"filterable"`, and `"exportable"` (CSV download).
|
|
483
|
+
|
|
415
484
|
### Height rules
|
|
416
485
|
|
|
417
486
|
- `markdown`, `code`: natural flow (no fixed height).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@khanglvm/relay",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Question boards with rich blocks (markdown, charts, mermaid, tables, code, diffs, video, sandboxed HTML), clickable local file-links, and element-level annotations for AI coding agents (Claude Code, Codex, …): ask users structured questions, present interactive visuals, collect inline comments, read answers as JSON — in a local browser board OR rendered INLINE inside the Claude & Codex apps as an MCP App (SEP-1865).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-agents",
|
package/skills/relay/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: relay
|
|
3
|
-
description: "
|
|
3
|
+
description: "Collect user decisions & answers — choice, multi, yes-no, text, scale, rank (prioritize), checklist (sign-off), allocate (split a budget), color — and present plans, data, code, or designs with blocks: diagrams (mermaid/graphviz/plantuml), charts, KPI cards, tables (sortable/filterable/CSV, load .csv/.json), code, multi-file diffs, before/after compare, typography, palettes, video, images (click-to-pin), HTML — plus inline element comments and links that open a visual in a modal. Opens a browser board (or inline in Claude/Codex), returns JSON answers/notes/comments/annotations on Submit. Use PROACTIVELY over native ask tools (2+ answers), ASCII/prose for structures/metrics/designs, or printing a file/image/chart/diff the terminal can't render. A dedicated component exists for most content — use the most specific, don't fall back to prose. Triggers: ask the user, decisions, prioritize, sign-off, allocate, plan review, chart, metrics, table, view a CSV, before/after, git diff, markdown file. Skip single yes/no."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# relay (`rly`)
|
|
@@ -25,9 +25,19 @@ Schema).** The essentials are below.
|
|
|
25
25
|
|
|
26
26
|
## When to use rly vs your native question tool
|
|
27
27
|
|
|
28
|
+
**The core principle — match the surface to the content.** You run inside a
|
|
29
|
+
terminal, an IDE side-panel (VS Code / JetBrains / Cursor), or a plain-text chat.
|
|
30
|
+
None of those can actually render a markdown file, an image, a chart, a table, a
|
|
31
|
+
diagram, or a diff — they degrade to walls of monospace text, or can't show it at
|
|
32
|
+
all (a terminal can't draw a PNG). relay opens a real browser board that renders it
|
|
33
|
+
properly and reads the user's reply back as JSON. **So whenever what you're about to
|
|
34
|
+
output would read better than monospace — or is a file/visual the user should look
|
|
35
|
+
at — show it in a relay board instead of printing it.**
|
|
36
|
+
|
|
28
37
|
| Situation | Use |
|
|
29
38
|
|---|---|
|
|
30
39
|
| One trivial confirmation ("proceed?") | native tool |
|
|
40
|
+
| Show an image / screenshot / mockup / rendered output | **rly** (`image` block — the terminal can't display pixels) |
|
|
31
41
|
| 2+ questions, or options that need descriptions | **rly** |
|
|
32
42
|
| Choice is easier to make visually (layouts, designs, diagrams) | **rly** (blocks per question) |
|
|
33
43
|
| Each OPTION has its own visual (design variants, screenshots, charts) | **rly** (blocks per option) |
|
|
@@ -38,6 +48,7 @@ Schema).** The essentials are below.
|
|
|
38
48
|
| "Show me the diff" / git diff / code changes / before-after | **rly** (`diff` block — run `git diff`, render it; never dump it in the terminal) |
|
|
39
49
|
| A demo, screen recording or walkthrough | **rly** (`video` block) |
|
|
40
50
|
| Point the user at a file to open (log, capture, report) | **rly** (a clickable local file-link in markdown) |
|
|
51
|
+
| Let the user read a markdown file (README, plan, report) | **rly view file.md** (or a `markdown` block with `mdFile`) — never dump the file into the terminal |
|
|
41
52
|
| Plan-mode clarifying question (Claude Code / Codex) | **rly** (not AskUserQuestion / the native ask tool) |
|
|
42
53
|
| Something you can decide yourself from context | neither — just decide |
|
|
43
54
|
|
|
@@ -51,6 +62,64 @@ Once the user has answered one board in a session, prefer boards for later
|
|
|
51
62
|
question rounds too — they've shown they engage with them. Batch related
|
|
52
63
|
questions into ONE board rather than opening several in a row.
|
|
53
64
|
|
|
65
|
+
## The full toolbox — reach for the MOST SPECIFIC component
|
|
66
|
+
|
|
67
|
+
relay ships a purpose-built component for most kinds of content. **Before you
|
|
68
|
+
build a board, scan this list and pick the most specific component that fits — do
|
|
69
|
+
NOT fall back to a plain `markdown`/prose block (or the terminal) when a dedicated
|
|
70
|
+
one renders it better.** A KPI belongs in `kpi`, a before/after in `compare`, a
|
|
71
|
+
priority call in a `rank` question — not in paragraphs. Unsure which exists? Run
|
|
72
|
+
`rly agent` (full guide) and `rly schema` (every field).
|
|
73
|
+
|
|
74
|
+
**Blocks** — add under `"blocks": [...]` at the board, a question, or an option:
|
|
75
|
+
|
|
76
|
+
| Block | Reach for it when you have… |
|
|
77
|
+
|---|---|
|
|
78
|
+
| `table` | tabular data — sortable, per-cell comments; `rowsFile` (.csv/.json), `filterable`, `exportable` |
|
|
79
|
+
| `chart` | numbers / trends / comparisons (bar·line·pie·doughnut·radar·scatter) |
|
|
80
|
+
| `kpi` | headline metrics — big-number cards with ↑/↓/flat deltas (no chart needed) |
|
|
81
|
+
| `mermaid` | flows, sequences, state machines, architecture (set `editable:true` to co-edit) |
|
|
82
|
+
| `graphviz` | precise dependency / call graphs |
|
|
83
|
+
| `plantuml` | UML (sequence / class / component) |
|
|
84
|
+
| `code` | source / config / command output — highlighted, line numbers, hover-a-line to comment |
|
|
85
|
+
| `diff` | code changes — colored unified/split, multi-file (`rly diff` builds the whole board) |
|
|
86
|
+
| `image` | screenshots / mockups / renders — zoom+pan; `pins:true` → click-to-drop point comments |
|
|
87
|
+
| `compare` | a before/after pair — draggable divider |
|
|
88
|
+
| `video` | a demo / screen recording / walkthrough |
|
|
89
|
+
| `palette` | color schemes — swatch cards, hover-hex, click-to-copy |
|
|
90
|
+
| `typography` | type choices — specimens at given size/weight/font |
|
|
91
|
+
| `html` | anything bespoke — custom widgets, pixel-perfect mockups |
|
|
92
|
+
| `markdown` | prose / context ONLY (not data, metrics, or visuals — those have their own block) |
|
|
93
|
+
|
|
94
|
+
Any block also takes `"ref":"name"` → a question can link to it with
|
|
95
|
+
`[label](#ref:name)` and it opens **in a full-screen modal**, so the user views the
|
|
96
|
+
data without scrolling back up.
|
|
97
|
+
|
|
98
|
+
**Question types** — pick by the shape of the answer you need:
|
|
99
|
+
|
|
100
|
+
| Type | Reach for it when you need… | Answer JSON |
|
|
101
|
+
|---|---|---|
|
|
102
|
+
| `single` | one choice (radio; "Other" + a note are on by default) | `"value"` |
|
|
103
|
+
| `multi` | several choices | `["a","b"]` |
|
|
104
|
+
| `rank` | a **priority order** over options (roadmap, triage) | `["b","a","c"]` |
|
|
105
|
+
| `allocate` | a **budget split** across options (tradeoffs, %, points) | `{opt: number}` |
|
|
106
|
+
| `checklist` | **per-item sign-off / QA** (Pass·Fail·N·A, or custom) | `{opt: status}` |
|
|
107
|
+
| `scale` | a rating on a 1–N scale | number |
|
|
108
|
+
| `yesno` | a binary decision | `"yes"`/`"no"` |
|
|
109
|
+
| `color` | a color pick — native picker + presets, or a `palette` of labeled swatch cards (each commentable); any CSS color system | color string |
|
|
110
|
+
| `text` / `textarea` | short / long free text | string |
|
|
111
|
+
|
|
112
|
+
**By who you're serving** (don't make a business user read a wall of prose):
|
|
113
|
+
|
|
114
|
+
- **Business / PM / exec** → `kpi` + `chart` + `table` for the numbers; `rank` to
|
|
115
|
+
prioritize, `allocate` for tradeoffs, `checklist` for sign-off, `scale` for confidence.
|
|
116
|
+
- **Designer** → `image` (+`pins` for point feedback), `compare` (before/after),
|
|
117
|
+
`palette`, `typography`; put a visual INSIDE each option so they pick by looking.
|
|
118
|
+
- **Engineer** → `diff` (`rly diff`), `code` (line-comments), `mermaid`/`graphviz`
|
|
119
|
+
for architecture, editable mermaid to co-design.
|
|
120
|
+
- **Data / analyst** → `table` with `rowsFile`/`filterable`/`exportable`
|
|
121
|
+
(`rly view data.csv`), `chart` for the shape of it.
|
|
122
|
+
|
|
54
123
|
## Choose a pattern
|
|
55
124
|
|
|
56
125
|
**DEFAULT: detached.** Most agent shell tools kill long-running commands, and a
|
|
@@ -105,10 +174,49 @@ answers and any annotations written so far.
|
|
|
105
174
|
```
|
|
106
175
|
|
|
107
176
|
Types: `single`, `multi`, `yesno`, `text`, `textarea`, `scale`, `color`
|
|
108
|
-
(native picker + hex; optional `"presets":["#…"]
|
|
109
|
-
|
|
177
|
+
(native picker + hex; optional `"presets":["#…"]` or a `"palette"` of labeled,
|
|
178
|
+
commentable swatch cards — any CSS color system), `rank`, `checklist`, `allocate`.
|
|
179
|
+
Users may submit with unanswered questions (returned in `skipped`) unless
|
|
110
180
|
`"allowPartial": false` or per-question `"required": true`.
|
|
111
181
|
|
|
182
|
+
```jsonc
|
|
183
|
+
{ "id": "brand", "type": "color", "label": "Pick a brand color",
|
|
184
|
+
"palette": [{ "value": "#c2674b", "label": "Terracotta" }, { "value": "rgb(77,138,102)", "label": "Forest" }, "rebeccapurple"] }
|
|
185
|
+
// clicking a swatch = the answer; hover a swatch to comment on that specific color.
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The optional per-question note box (`result.notes[id]`) defaults ON for the
|
|
189
|
+
**decision types** — `single`, `rank`, `checklist`, `allocate` — so the user can
|
|
190
|
+
qualify a pick; `"note": false` hides it, `"note": true` adds it to other types.
|
|
191
|
+
|
|
192
|
+
`rank` — the user drags (or uses ↑/↓) to order the `options` by priority; the
|
|
193
|
+
answer is the **ordered array of option values**, highest first. Needs ≥2
|
|
194
|
+
options; always returns a value (an untouched rank submits the authored order),
|
|
195
|
+
so it's never `skipped`. Use for roadmap/feature prioritization instead of a
|
|
196
|
+
single pick. Options take `description` and per-option `blocks` like single/multi.
|
|
197
|
+
|
|
198
|
+
```json
|
|
199
|
+
{ "id": "roadmap", "type": "rank", "label": "Order these by priority",
|
|
200
|
+
"options": [{ "value": "diff", "label": "rly diff", "description": "git diff → board" }, "rank type", "image pins"] }
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
`checklist` — each `option` gets a per-item status (default **Pass / Fail / N·A**;
|
|
204
|
+
override with `"statuses"`). Answer is a map `{optionValue: statusValue}`. For QA
|
|
205
|
+
passes and sign-off gates.
|
|
206
|
+
|
|
207
|
+
```json
|
|
208
|
+
{ "id": "qa", "type": "checklist", "label": "Release sign-off", "options": ["login","search","checkout"] }
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
`allocate` — the user distributes a budget (`"total"`, default 100) across the
|
|
212
|
+
`options` with sliders + a live running-total bar. Answer is a map
|
|
213
|
+
`{optionValue: number}`. Captures intensity/tradeoffs, not just a pick.
|
|
214
|
+
|
|
215
|
+
```json
|
|
216
|
+
{ "id": "spend", "type": "allocate", "label": "Split the quarter", "total": 100, "unit": "pts",
|
|
217
|
+
"options": ["features","tech debt","infra"] }
|
|
218
|
+
```
|
|
219
|
+
|
|
112
220
|
Set `"note": true` on a question to add a small optional free-text field under
|
|
113
221
|
it — use when the user may want to qualify their choice. Returned as
|
|
114
222
|
`result.notes[questionId]`. `single` (radio) questions include this note by
|
|
@@ -127,6 +235,7 @@ single/multi question.
|
|
|
127
235
|
|
|
128
236
|
```jsonc
|
|
129
237
|
{ "type": "markdown", "md": "## Section\n**prose**" }
|
|
238
|
+
{ "type": "markdown", "mdFile": "README.md" } // render a local .md file (no lib)
|
|
130
239
|
{ "type": "mermaid", "code": "graph TD; A-->B", "height": 400 }
|
|
131
240
|
{ "type": "graphviz", "dot": "digraph { a -> b }", "height": 300 }
|
|
132
241
|
{ "type": "plantuml", "code": "@startuml\nA->B\n@enduml", "height": 300 }
|
|
@@ -137,6 +246,19 @@ single/multi question.
|
|
|
137
246
|
{ "type": "table", "columns": ["A","B"], "rows": [["x","y"]], "sortable": true }
|
|
138
247
|
// ^ use a `table` block for tabular data — sortable + per-cell comments.
|
|
139
248
|
// (markdown blocks render GFM pipe tables too, but those are display-only.)
|
|
249
|
+
{ "type": "table", "rowsFile": "data.csv", "filterable": true, "exportable": true }
|
|
250
|
+
// ^ load rows from a local .csv/.tsv/.json; filterable = live filter box,
|
|
251
|
+
// exportable = CSV download. (`rly view data.csv` does all of this for you.)
|
|
252
|
+
{ "type": "kpi", "title": "This quarter", "items": [
|
|
253
|
+
{ "label": "Revenue", "value": "$1.2M", "delta": "12%", "dir": "up" },
|
|
254
|
+
{ "label": "Churn", "value": "2.1%", "delta": "0.4pp", "dir": "down", "sub": "lower=better" } ] }
|
|
255
|
+
// ^ big-number metric cards with up/down/flat-tinted deltas — no chart needed.
|
|
256
|
+
{ "type": "typography", "font": "Georgia, serif", "specimens": [
|
|
257
|
+
{ "label": "Display", "size": "40px", "weight": "600", "text": "Ship faster" },
|
|
258
|
+
{ "label": "Body", "size": "16px", "text": "The quick brown fox…" } ] }
|
|
259
|
+
// ^ type specimens at given size/weight/font — react to type like a palette.
|
|
260
|
+
{ "type": "compare", "before": "v1.png", "after": "v2.png", "beforeLabel": "Old", "afterLabel": "New" }
|
|
261
|
+
// ^ before/after images with a draggable divider (redesign / before-after fix).
|
|
140
262
|
{ "type": "code", "lang": "js", "code": "const x = 1;", "filename": "demo.js" }
|
|
141
263
|
{ "type": "code", "codeFile": "src/server.js" } // load text from a local file
|
|
142
264
|
{ "type": "diff", "lang": "js", "filename": "src/auth.js", "view": "split",
|
|
@@ -160,6 +282,23 @@ single/multi question.
|
|
|
160
282
|
// ^ color palettes as swatch cards: hover=hex, click=copy. Shorthand {"type":"palette","colors":[…]}; pair with a `color` question.
|
|
161
283
|
```
|
|
162
284
|
|
|
285
|
+
### View a markdown file quickly
|
|
286
|
+
|
|
287
|
+
To let the user *read* a `.md` file (README, a plan you wrote, a generated
|
|
288
|
+
report), don't paste it into the terminal — render it:
|
|
289
|
+
|
|
290
|
+
```sh
|
|
291
|
+
rly view PLAN.md # one file → board titled "PLAN.md", "Done" button
|
|
292
|
+
rly view README.md CHANGELOG.md # several files, each with a filename heading
|
|
293
|
+
rly view docs/spec.md --detach # detached like ask/show; then `rly wait <id>`
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
`rly view` is sugar over `rly show` with `markdown` blocks (`mdFile`). The
|
|
297
|
+
built-in renderer is library-free and covers headings, lists, `**/_` emphasis,
|
|
298
|
+
code, quotes, GFM pipe tables, remote/data images, and click-to-open local
|
|
299
|
+
links. To mix a file into a larger board, use a `markdown` block with
|
|
300
|
+
`"mdFile"` alongside questions or other blocks.
|
|
301
|
+
|
|
163
302
|
### Visual options — show each choice, don't describe it
|
|
164
303
|
|
|
165
304
|
When the options themselves are visual (design variants, layouts, color
|
|
@@ -197,6 +336,43 @@ renders as a click-to-open link that opens the file in the user's OS default app
|
|
|
197
336
|
wrote on the board can be opened (same-origin + allowlist guarded). Surface a real
|
|
198
337
|
clickable path instead of telling the user to paste it into a terminal.
|
|
199
338
|
|
|
339
|
+
### Connect a question to a visual shown above (reference modal)
|
|
340
|
+
|
|
341
|
+
When a board has visuals up top and questions below, the user loses the link
|
|
342
|
+
between them. Give any block a stable `"ref"` name, then reference it from a
|
|
343
|
+
markdown link — clicking it opens that visual in a full-screen modal **in place**,
|
|
344
|
+
no scrolling:
|
|
345
|
+
|
|
346
|
+
```jsonc
|
|
347
|
+
{ "type": "chart", "ref": "velocity", "kind": "line", "labels": [...], "series": [...] }
|
|
348
|
+
// then in the intro, a markdown block, or a question's own markdown block:
|
|
349
|
+
{ "type": "markdown", "md": "Decide from the [📈 Velocity](#ref:velocity) chart above." }
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
`[label](#ref:name)` opens the block named `name`; `[label](#block:b2)` opens by
|
|
353
|
+
id. Use it so every question that depends on data points right at it.
|
|
354
|
+
|
|
355
|
+
### Pin comments on a mockup (image coordinates)
|
|
356
|
+
|
|
357
|
+
Add `"pins": true` to an `image` block: the user clicks any point on the image to
|
|
358
|
+
drop a comment anchored to that exact spot (Figma-style), returned as an
|
|
359
|
+
`{kind:"image-point", x, y}` annotation. Ideal for design/mockup review.
|
|
360
|
+
|
|
361
|
+
### Show a git diff in one step — `rly diff`
|
|
362
|
+
|
|
363
|
+
`rly diff [git args…]` runs `git diff` and opens the result as a diff board —
|
|
364
|
+
sugar for the "show me the diff" flow. Git args/flags pass straight through; a
|
|
365
|
+
multi-file diff renders with a per-file header + a jump bar.
|
|
366
|
+
|
|
367
|
+
```sh
|
|
368
|
+
rly diff --detach # working-tree diff
|
|
369
|
+
rly diff --staged --split # staged changes, side-by-side
|
|
370
|
+
rly diff HEAD~1 HEAD -- src/ # a commit's diff, scoped to a path
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
`code` blocks also support **line-anchored comments**: hover a line number to
|
|
374
|
+
comment on that exact line (returned as `{kind:"code-line", line}`).
|
|
375
|
+
|
|
200
376
|
## Annotations
|
|
201
377
|
|
|
202
378
|
Users can hover chart points, diagram nodes (mermaid + graphviz), table cells,
|
|
@@ -213,7 +389,7 @@ mention annotation in the board intro.
|
|
|
213
389
|
"id": "a1",
|
|
214
390
|
"questionId": "q-id or null",
|
|
215
391
|
"blockId": "b2",
|
|
216
|
-
"target": { "kind": "chart-element | mermaid-node | graphviz-node | table-cell | text | html-element | image", "..." },
|
|
392
|
+
"target": { "kind": "chart-element | mermaid-node | graphviz-node | table-cell | text | html-element | image | image-point | code-line", "..." },
|
|
217
393
|
"text": "user comment",
|
|
218
394
|
"author": "user",
|
|
219
395
|
"createdAt": "ISO",
|
package/src/cli.js
CHANGED
|
@@ -236,6 +236,61 @@ async function cmdAsk(args, mode) {
|
|
|
236
236
|
return runOrDetach(record, args);
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
+
// `rly diff [git args…]` — run `git diff` for the user and open the result as a
|
|
240
|
+
// diff-block board in one step (sugar like `rly view`). Everything that isn't a
|
|
241
|
+
// recognized rly flag is forwarded to git verbatim, so `rly diff --staged`,
|
|
242
|
+
// `rly diff HEAD~1`, `rly diff main -- src/` all work. rly flags: --detach,
|
|
243
|
+
// --no-open, --split (side-by-side), --title.
|
|
244
|
+
async function cmdDiff(rest) {
|
|
245
|
+
const RLY_FLAGS = new Set(['--detach', '--no-open', '--split']);
|
|
246
|
+
const gitArgs = [];
|
|
247
|
+
const rlyArgv = [];
|
|
248
|
+
for (let i = 0; i < rest.length; i++) {
|
|
249
|
+
const t = rest[i];
|
|
250
|
+
if (t === '--title') { rlyArgv.push(t, rest[++i]); continue; }
|
|
251
|
+
if (t.startsWith('--title=')) { rlyArgv.push(t); continue; }
|
|
252
|
+
if (RLY_FLAGS.has(t)) { rlyArgv.push(t); continue; }
|
|
253
|
+
gitArgs.push(t);
|
|
254
|
+
}
|
|
255
|
+
const args = parseArgs(rlyArgv);
|
|
256
|
+
const res = spawnSync('git', ['--no-pager', 'diff', ...gitArgs], { encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 });
|
|
257
|
+
if (res.error) throw new CliError(`could not run git: ${res.error.message}. Is git installed and is this a repo?`);
|
|
258
|
+
if (res.status !== 0 && res.stderr) throw new CliError(`git diff failed: ${res.stderr.trim()}`, res.status || 1);
|
|
259
|
+
const diff = res.stdout || '';
|
|
260
|
+
if (!diff.trim()) {
|
|
261
|
+
printJson({ status: 'no-diff', hint: `git diff${gitArgs.length ? ' ' + gitArgs.join(' ') : ''} produced no output — nothing to show` });
|
|
262
|
+
return 0;
|
|
263
|
+
}
|
|
264
|
+
const title = args.title || ('git diff' + (gitArgs.length ? ' ' + gitArgs.join(' ') : ''));
|
|
265
|
+
const spec = normalizeSpec({ title, blocks: [{ type: 'diff', diff, view: args.split ? 'split' : 'unified' }] });
|
|
266
|
+
const record = createBoard(spec);
|
|
267
|
+
return runOrDetach(record, args);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// `rly view <file.md> [more.md …]` — quick read-only board that renders one or
|
|
271
|
+
// more markdown files (README, plan, report) with the built-in no-library
|
|
272
|
+
// renderer. Each file becomes a markdown block; with 2+ files a small filename
|
|
273
|
+
// heading separates them. Sugar over `rly show` with markdown mdFile blocks.
|
|
274
|
+
async function cmdView(args) {
|
|
275
|
+
const files = args._;
|
|
276
|
+
if (!files.length) throw new CliError('usage: rly view <file.md|.csv|.json> [more …] [--title T] [--detach]');
|
|
277
|
+
const multi = files.length > 1;
|
|
278
|
+
const DATA_EXT = new Set(['csv', 'tsv', 'json']);
|
|
279
|
+
const blocks = [];
|
|
280
|
+
for (const f of files) {
|
|
281
|
+
if (multi) blocks.push({ type: 'markdown', md: `## ${path.basename(f)}` });
|
|
282
|
+
const ext = path.extname(f).slice(1).toLowerCase();
|
|
283
|
+
if (DATA_EXT.has(ext)) blocks.push({ type: 'table', rowsFile: f, sortable: true, filterable: true, exportable: true });
|
|
284
|
+
else blocks.push({ type: 'markdown', mdFile: f });
|
|
285
|
+
}
|
|
286
|
+
const raw = { blocks };
|
|
287
|
+
raw.title = args.title || (multi ? `${files.length} files` : path.basename(files[0]));
|
|
288
|
+
raw.submitLabel = args.submitLabel || 'Done';
|
|
289
|
+
const spec = normalizeSpec(raw); // reads + validates each file, clear error if unreadable
|
|
290
|
+
const record = createBoard(spec);
|
|
291
|
+
return runOrDetach(record, args);
|
|
292
|
+
}
|
|
293
|
+
|
|
239
294
|
// Seeds the draft from the last result (as runBoard would on reopen) and
|
|
240
295
|
// appends agent replies to the matching annotations, so an agent can ANSWER
|
|
241
296
|
// the user's element comments and re-open the board as a conversation.
|
|
@@ -798,24 +853,42 @@ function stampSkillVersion() {
|
|
|
798
853
|
// AGENTS.md, …). A skill is only a tier-1 hint that agents can ignore; these
|
|
799
854
|
// rules, appended to the instructions the agent always reads, enforce usage.
|
|
800
855
|
// Printed to stdout so `rly skill rules >> CLAUDE.md` is the whole install.
|
|
801
|
-
const SKILL_RULES = `## relay —
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
-
|
|
856
|
+
const SKILL_RULES = `## relay — show it in the browser, don't dump it in the terminal
|
|
857
|
+
|
|
858
|
+
**Why:** your output surface — a terminal, an IDE side-panel (VS Code, JetBrains,
|
|
859
|
+
Cursor), a plain-text chat — can't actually render a markdown file, an image, a
|
|
860
|
+
chart, a table, a diagram, or a diff. It shows them as walls of monospace text, or
|
|
861
|
+
not at all (a terminal can't display a PNG). relay opens a real browser board that
|
|
862
|
+
renders them properly AND reads the user's reply back as JSON. **Rule of thumb: if
|
|
863
|
+
what you're about to output would look better than monospace text — or is a file
|
|
864
|
+
the user should view — put it in relay instead of printing it.**
|
|
865
|
+
|
|
866
|
+
- **Markdown file / doc / README / plan / report** → \`rly view file.md\` (renders it
|
|
867
|
+
in the browser, no library; \`.csv\`/\`.json\` → a sortable table). Never \`cat\` a
|
|
868
|
+
doc into the terminal for the user to read.
|
|
869
|
+
- **Image / screenshot / mockup / diagram / chart / table** → a relay board with an
|
|
870
|
+
\`image\` block (or \`mermaid\`/\`graphviz\`/\`chart\`/\`table\`/\`kpi\`/\`compare\`). The
|
|
871
|
+
terminal can't show pixels; relay can — with click-to-zoom and comment-in-place.
|
|
872
|
+
- **The user's decisions, feedback, requirements, or approval** (anything beyond one
|
|
873
|
+
trivial yes/no) → a relay board, NOT plain terminal text and NOT the native
|
|
874
|
+
\`AskUserQuestion\` / ask-user tool. Build a spec and run
|
|
875
|
+
\`rly ask --file spec.json --detach\`, then \`rly wait <boardId>\`. **Holds in plan
|
|
876
|
+
mode too**; reserve \`ExitPlanMode\` for the final plan-approval gate only.
|
|
877
|
+
- **A plan, structure, architecture, data, or prototype** → a relay board with
|
|
878
|
+
diagram/chart/table/code/image/html blocks — never ASCII diagrams or walls of prose.
|
|
879
|
+
- **"Show me the diff / git diff / these changes"** → \`rly diff\` (runs git diff →
|
|
880
|
+
a diff board), or render diff text in a \`diff\` block — never paste a raw diff.
|
|
881
|
+
- Point the user at a file with a clickable local path in a markdown block; embed a
|
|
882
|
+
screen recording with a \`video\` block; when answer choices are visual, give each
|
|
818
883
|
option its own visual (\`options[].blocks\`) so the user picks by looking.
|
|
884
|
+
- **There's a purpose-built component for most content — use the MOST SPECIFIC one,
|
|
885
|
+
never plain prose when a block fits.** Blocks: \`table\` (sortable/filterable/CSV,
|
|
886
|
+
load from .csv/.json), \`chart\`, \`kpi\` (stat cards), \`mermaid\`/\`graphviz\`/\`plantuml\`,
|
|
887
|
+
\`code\`, \`diff\`, \`image\` (+\`pins\`), \`compare\` (before/after), \`video\`, \`palette\`,
|
|
888
|
+
\`typography\`, \`html\`. Question types: \`single\`/\`multi\`/\`yesno\`/\`scale\`/\`color\`/
|
|
889
|
+
\`text\`/\`textarea\` plus \`rank\` (prioritize), \`allocate\` (split a budget), \`checklist\`
|
|
890
|
+
(per-item sign-off). For a business user, reach for \`kpi\`+\`chart\`+\`table\` and
|
|
891
|
+
\`rank\`/\`allocate\`/\`checklist\`; full menu + fields: \`rly agent\` and \`rly schema\`.
|
|
819
892
|
- Read the result JSON from stdout; treat \`comment\` and \`annotations\` as
|
|
820
893
|
first-class feedback. Full guide: \`rly agent\`.`;
|
|
821
894
|
|
|
@@ -1296,6 +1369,10 @@ USAGE
|
|
|
1296
1369
|
rly ask ... --detach no blocking: prints {boardId,url} now; collect via \`rly wait <id>\`
|
|
1297
1370
|
rly ask ... --on-result "<cmd>" push-wake: run <cmd> when the board finishes (result JSON on stdin)
|
|
1298
1371
|
rly show --html-file viz.html visualization-only board (submit button = acknowledge)
|
|
1372
|
+
rly view <file.md> [more.md …] quick read-only board rendering markdown file(s) (no lib)
|
|
1373
|
+
(.csv/.tsv/.json render as a filterable, sortable table)
|
|
1374
|
+
rly diff [git args…] run git diff and show it in a diff board (--split, --detach,
|
|
1375
|
+
--title; other args pass to git: rly diff --staged | HEAD~1 | -- path)
|
|
1299
1376
|
rly wait <id> [--timeout 3600] block until board finishes, print result JSON
|
|
1300
1377
|
--while-active [--idle-grace 180]: keep waiting past the deadline
|
|
1301
1378
|
while the user is still viewing/focused & recently active
|
|
@@ -1366,6 +1443,10 @@ export async function main(argv) {
|
|
|
1366
1443
|
return await cmdAsk(parseArgs(rest), 'ask');
|
|
1367
1444
|
case 'show':
|
|
1368
1445
|
return await cmdAsk(parseArgs(rest), 'show');
|
|
1446
|
+
case 'view':
|
|
1447
|
+
return await cmdView(parseArgs(rest));
|
|
1448
|
+
case 'diff':
|
|
1449
|
+
return await cmdDiff(rest);
|
|
1369
1450
|
case 'reopen':
|
|
1370
1451
|
return await cmdReopen(parseArgs(rest));
|
|
1371
1452
|
case 'rescue':
|