agent-whiteboard 1.2.0 → 1.3.2
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/CHANGELOG.md +34 -0
- package/README.md +125 -83
- package/bin/cli.js +11 -2
- package/bin/install-skills.js +108 -0
- package/dist/client/assets/{abnfDiagram-VCTEODGH-Dj8sd_5g.js → abnfDiagram-VCTEODGH-Bwh5lUgu.js} +1 -1
- package/dist/client/assets/{architectureDiagram-5GKGNRK7-B1QyVL0F.js → architectureDiagram-5GKGNRK7-B_ecYkha.js} +1 -1
- package/dist/client/assets/{blockDiagram-I7D4REHJ-CmU2SSFe.js → blockDiagram-I7D4REHJ-yKkzC4XQ.js} +1 -1
- package/dist/client/assets/{c4Diagram-7LVT6UL2-DWmhp77c.js → c4Diagram-7LVT6UL2-lHAqFGmA.js} +1 -1
- package/dist/client/assets/channel-B2NgRQfI.js +1 -0
- package/dist/client/assets/{chunk-2Q5K7J3B-CQLo8Sbv.js → chunk-2Q5K7J3B-B7zuI4_k.js} +1 -1
- package/dist/client/assets/{chunk-5VM5RSS4-fJTWmTTt.js → chunk-5VM5RSS4-jBsRKGwk.js} +1 -1
- package/dist/client/assets/{chunk-F27PBJKO-DqzEf5gi.js → chunk-F27PBJKO-CGIsMCIr.js} +1 -1
- package/dist/client/assets/{chunk-IMKFNOWR-CMamLTz3.js → chunk-IMKFNOWR-WXoBIg3r.js} +1 -1
- package/dist/client/assets/{chunk-JWPE2WC7-tyYPyD43.js → chunk-JWPE2WC7-DVxbj6OP.js} +1 -1
- package/dist/client/assets/{chunk-POPQ4Y6H-XdY4rO_V.js → chunk-POPQ4Y6H-B6_LAnue.js} +1 -1
- package/dist/client/assets/{chunk-SVP7TREG-COvqzqaO.js → chunk-SVP7TREG-DPtBhcA2.js} +1 -1
- package/dist/client/assets/{chunk-TICWLB2K-BsJSFuPC.js → chunk-TICWLB2K-BpTvKQOE.js} +1 -1
- package/dist/client/assets/{chunk-XXDRQBXY-HfVrYBJb.js → chunk-XXDRQBXY-ChPDJQUM.js} +1 -1
- package/dist/client/assets/classDiagram-ZZMXUADV-0PCo-FZi.js +1 -0
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-0PCo-FZi.js +1 -0
- package/dist/client/assets/{cose-bilkent-JH36ORCC-UXGdwSjS.js → cose-bilkent-JH36ORCC-CNOS6l2h.js} +1 -1
- package/dist/client/assets/{cynefin-OW5HDTMX-PWlPYnpn.js → cynefin-OW5HDTMX-yFRC4RbL.js} +1 -1
- package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-CYE5H1wR.js → cynefinDiagram-5FMLGOSQ-A1B_XDwz.js} +1 -1
- package/dist/client/assets/{dagre-GXQ25YYZ-DaknBOKq.js → dagre-GXQ25YYZ-CsGQlESG.js} +1 -1
- package/dist/client/assets/{diagram-S7CK7UJ4-Dz7rZHxF.js → diagram-S7CK7UJ4-oi40Oovb.js} +1 -1
- package/dist/client/assets/{diagram-UQ7AKVKN-DzCi6tiS.js → diagram-UQ7AKVKN-fXq7BGyX.js} +1 -1
- package/dist/client/assets/{diagram-VSXAHHWV-TlUzhXgv.js → diagram-VSXAHHWV-DGsF5-Gn.js} +1 -1
- package/dist/client/assets/{diagram-VX7I27RA-Ds7jdzK7.js → diagram-VX7I27RA-C2kM3ZeN.js} +1 -1
- package/dist/client/assets/{diagram-Z3DM3KII-C--JD7cf.js → diagram-Z3DM3KII-B-4vwwDz.js} +1 -1
- package/dist/client/assets/{ebnfDiagram-PWID7BFC-Dw5OM-dL.js → ebnfDiagram-PWID7BFC-IQpqM8Er.js} +1 -1
- package/dist/client/assets/{erDiagram-RLTQ6QDP-DXalBHjA.js → erDiagram-RLTQ6QDP-DZUhIRFv.js} +1 -1
- package/dist/client/assets/{flowDiagram-HODETNUW-DcI3UqyE.js → flowDiagram-HODETNUW-BogyNcsK.js} +1 -1
- package/dist/client/assets/{ganttDiagram-EL5Y4UJY-DyrkIo6c.js → ganttDiagram-EL5Y4UJY-DQrF2qTY.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-fJKfGRcg.js → gitGraphDiagram-WWUBYQGX-DCjdibDV.js} +1 -1
- package/dist/client/assets/{index-CIhkXT_z.js → index-CwpL9TCq.js} +9 -9
- package/dist/client/assets/{infoDiagram-27XIBGKW-Cijiqswb.js → infoDiagram-27XIBGKW-B9fQanxS.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-5VMMS53U-C1WqWU2l.js → ishikawaDiagram-5VMMS53U-C2Vylmfx.js} +1 -1
- package/dist/client/assets/{journeyDiagram-3NMN7TZE-7j05mbLR.js → journeyDiagram-3NMN7TZE-D89LoCjM.js} +1 -1
- package/dist/client/assets/{kanban-definition-UXKFOSKX-D5mGDfs_.js → kanban-definition-UXKFOSKX-PEjEU0rc.js} +1 -1
- package/dist/client/assets/{layout-KEin06-H.js → layout-BFcmmT4m.js} +1 -1
- package/dist/client/assets/{mermaid.core-CLvSDmrA.js → mermaid.core-DKnSsFxv.js} +5 -5
- package/dist/client/assets/{mindmap-definition-YA3MSWOX-BsCqKd_d.js → mindmap-definition-YA3MSWOX-B7LiyX5g.js} +1 -1
- package/dist/client/assets/{pegDiagram-XKGWAZYB-Du5lDU3W.js → pegDiagram-XKGWAZYB-BnJUZrSw.js} +1 -1
- package/dist/client/assets/{pieDiagram-E7YTZNPT-hjv5Ao6B.js → pieDiagram-E7YTZNPT-z_3XFa-J.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-AXDQQJYC-DNon8LWK.js → quadrantDiagram-AXDQQJYC-B-iV8Ep2.js} +1 -1
- package/dist/client/assets/{railroadDiagram-O6MQD6OU-yNZz2zxo.js → railroadDiagram-O6MQD6OU-DWcuukQV.js} +1 -1
- package/dist/client/assets/{requirementDiagram-BXWQKSXE-CZiiEUHW.js → requirementDiagram-BXWQKSXE-BL29FbE8.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-P5KCCOFB-D-u8ZQUV.js → sankeyDiagram-P5KCCOFB-DncoVK5c.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-BxjTf3q9.js → sequenceDiagram-WJ2MYXX4-4_9ywdq-.js} +1 -1
- package/dist/client/assets/{sizeCapture-INFHLROL-Dt4Wg8C5.js → sizeCapture-INFHLROL-DgpEl-H1.js} +1 -1
- package/dist/client/assets/{stateDiagram-D77RDMKH-B8BM6dln.js → stateDiagram-D77RDMKH-D3uAsi-s.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-BEkx2fLW.js +1 -0
- package/dist/client/assets/{swimlanes-42K2YHIH-eGWrsWAA.js → swimlanes-42K2YHIH-DchNJuY0.js} +1 -1
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-BHXAFRx6.js +8 -0
- package/dist/client/assets/{timeline-definition-24CTP7MA-CAM-3Qj9.js → timeline-definition-24CTP7MA-KWfqhWGR.js} +1 -1
- package/dist/client/assets/{vennDiagram-4TSXK5OY-CuFEwwD0.js → vennDiagram-4TSXK5OY-YJTdSPKH.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-VM6X3IG4-DI5yCVh0.js → wardleyDiagram-VM6X3IG4-CSN6rLUa.js} +1 -1
- package/dist/client/assets/{xychartDiagram-S5SC5T6Z-BI0Bn-OQ.js → xychartDiagram-S5SC5T6Z-DqPq1Pcp.js} +1 -1
- package/dist/client/index.html +1 -1
- package/package.json +3 -1
- package/skills/agent-whiteboard/SKILL.md +45 -0
- package/skills/whiteboard-analysis/SKILL.md +32 -0
- package/skills/whiteboard-assessment/SKILL.md +51 -0
- package/skills/whiteboard-design/SKILL.md +29 -0
- package/skills/whiteboard-teaching/SKILL.md +39 -0
- package/dist/client/assets/channel-C9ni1Via.js +0 -1
- package/dist/client/assets/classDiagram-ZZMXUADV-DktNEiGC.js +0 -1
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-DktNEiGC.js +0 -1
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-MJauX84T.js +0 -1
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-BYFu8tyL.js +0 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,37 @@
|
|
|
1
|
+
## 1.3.2 — 2026-11-08
|
|
2
|
+
|
|
3
|
+
Patch: documentation only.
|
|
4
|
+
|
|
5
|
+
- **README npm badge:** added an npm version badge linking to the package page, so the npmjs.com page and the repo point at each other.
|
|
6
|
+
|
|
7
|
+
## 1.3.1 — 2026-11-07
|
|
8
|
+
|
|
9
|
+
Patch: documentation only.
|
|
10
|
+
|
|
11
|
+
- **README reordered for users:** quickstart, MCP registration, agent skills, use cases and tools now come first; Environment variables and REST fallback follow; Development, Tests and Manual showcase are at the end. Added a "Jump to" line, moved the `/mcp` enable step into MCP registration, reworded the `.mcp.json` note for non-contributors, and listed `npm run check` under Tests.
|
|
12
|
+
|
|
13
|
+
## 1.3.0 — 2026-11-07
|
|
14
|
+
|
|
15
|
+
**Milestone v1.9 — Agent Skills & `install-skills` CLI (Sprint 92) complete.** Adds ready-made agent skills and a one-command installer (FR34 in `01`, F51–F54/NF58 in `03`).
|
|
16
|
+
|
|
17
|
+
- **Agent skills (F51):** five skills in `skills/` teach an agent when and how to use the tools — `agent-whiteboard` (core rules), `whiteboard-teaching`, `whiteboard-assessment`, `whiteboard-analysis`, `whiteboard-design`. Shipped in the npm package (`files` now includes `skills`, F54).
|
|
18
|
+
- **`install-skills` subcommand (F52):** `npx agent-whiteboard install-skills` copies them into `~/.claude/skills` (`--project` → `./.claude/skills`, `--dir <path>`, `--only a,b`, `--force`, `--list`). Existing skills are never overwritten without `--force`. `bin/cli.js` dispatches it before loading the server, so plain `npx agent-whiteboard` is unchanged.
|
|
19
|
+
- **README (F53):** new Use cases (workflows per context) and Agent skills sections.
|
|
20
|
+
- **Dev tooling:** `npm run check` (typecheck → lint → test → build) mirrors CI locally; ESLint ignores the gitignored `video/` folder. `test_skills.md` documents the manual skills test plan.
|
|
21
|
+
- Tests: `installSkills.test.ts` (10 tests); 755 unit tests passing; `tsc`/`svelte-check`/`eslint`/`npm run build` clean.
|
|
22
|
+
|
|
23
|
+
## 1.2.2 — 2026-11-07
|
|
24
|
+
|
|
25
|
+
Patch: test-only fix, no behavioural change.
|
|
26
|
+
|
|
27
|
+
- **Fix flaky CI test:** `import-zip.test.ts` "cleans up its temp directory" counted `agent-whiteboard-import-*` folders in the shared system tmpdir, which other test files (separate workers) also populate, so the before/after comparison raced. The test now uses a private `TMPDIR` and asserts it ends empty.
|
|
28
|
+
|
|
29
|
+
## 1.2.1 — 2026-11-07
|
|
30
|
+
|
|
31
|
+
Patch: lint fix, no behavioural change.
|
|
32
|
+
|
|
33
|
+
- **Fix CI lint crash:** `Quiz.svelte` used a reactive arrow function (`$: resultFor = ...`) that made `svelte/no-reactive-functions` crash in its autofix under ESLint 10, failing `npm run lint` in CI. Replaced with a reactive `resultById` map.
|
|
34
|
+
|
|
1
35
|
## 1.2.0 — 2026-11-06
|
|
2
36
|
|
|
3
37
|
**Milestone v1.8 — Quiz Support (Sprints 89–91) complete.** Adds interactive quizzes for reviewing the user's knowledge (FR33 in `01`, F44–F50/NF57 in `03`).
|
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Agent Whiteboard
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/agent-whiteboard)
|
|
4
|
+
|
|
3
5
|
A domain-agnostic interactive whiteboard for AI teacher agents. An AI agent renders diagrams, math, charts, and HTML to a local browser tab via MCP tools — and can pause to wait for the user to signal they're ready to continue.
|
|
4
6
|
|
|
5
7
|
Agent Whiteboard started as a tool for AI tutoring agents.
|
|
@@ -18,6 +20,8 @@ The browser also has a History panel for browsing, reloading, deleting, and expo
|
|
|
18
20
|
|
|
19
21
|
<img src="docs/screenshots/history-panel.png" width="700" alt="History panel listing past snapshots grouped by workspace">
|
|
20
22
|
|
|
23
|
+
**Jump to:** [Quickstart](#quickstart) · [MCP registration](#mcp-registration) · [Agent skills](#agent-skills) · [Use cases](#use-cases) · [MCP tools](#mcp-tools) · [Environment variables](#environment-variables) · [Development](#development) *(contributors)*
|
|
24
|
+
|
|
21
25
|
## How it works
|
|
22
26
|
|
|
23
27
|
```
|
|
@@ -54,38 +58,98 @@ By default the server only binds to loopback addresses (`localhost`/`127.0.0.1`/
|
|
|
54
58
|
|
|
55
59
|
This is a **single-user, local-only** tool: there is no authentication on any endpoint. That's an accepted trade-off, not an oversight — the server refuses to start if `HOST` is set to anything other than a loopback address, unless you explicitly set `ALLOW_NON_LOOPBACK=1` (not recommended; anyone who can reach the port can render arbitrary content, read/delete your snapshot history, and exfiltrate exported data). Don't expose this server beyond your own machine.
|
|
56
60
|
|
|
57
|
-
##
|
|
61
|
+
## MCP registration
|
|
58
62
|
|
|
59
|
-
|
|
63
|
+
Add a `.mcp.json` to your project root (this repository already has one) and Claude Code picks it up automatically:
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"mcpServers": {
|
|
68
|
+
"agent-whiteboard": {
|
|
69
|
+
"type": "sse",
|
|
70
|
+
"url": "http://localhost:3000/mcp"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The server must be running before Claude Code connects. The first time, run `/mcp` in Claude Code, select `agent-whiteboard` and enable it; the tools are available immediately. If you override `PORT`, update the URL above to match. See [Environment variables](#environment-variables) for the full list of overridable settings.
|
|
77
|
+
|
|
78
|
+
## Agent skills
|
|
79
|
+
|
|
80
|
+
The `skills/` folder ships ready-made [agent skills](https://docs.claude.com/en/docs/claude-code/skills) that teach an agent *when and how* to use the tools above. Start with `agent-whiteboard`; the others build on it.
|
|
81
|
+
|
|
82
|
+
| Skill | Covers |
|
|
83
|
+
|-------------------------|-------------------------------------------------------------------|
|
|
84
|
+
| `agent-whiteboard` | Core rules: workspaces, render types, pacing, step-frames, export |
|
|
85
|
+
| `whiteboard-teaching` | Explainers, build-ups, worked examples, guided lessons, branching |
|
|
86
|
+
| `whiteboard-assessment` | Inline and deferred quizzes, remediation, spaced review |
|
|
87
|
+
| `whiteboard-analysis` | Charts, exploration loops, findings walkthroughs, derivations |
|
|
88
|
+
| `whiteboard-design` | Architecture sessions, option comparison, codebase explanation |
|
|
89
|
+
|
|
90
|
+
Install them with one command (no server is started):
|
|
60
91
|
|
|
61
92
|
```bash
|
|
62
|
-
|
|
63
|
-
|
|
93
|
+
npx agent-whiteboard install-skills # all skills → ~/.claude/skills (every project)
|
|
94
|
+
npx agent-whiteboard install-skills --project # → ./.claude/skills (this project only)
|
|
95
|
+
npx agent-whiteboard install-skills --only agent-whiteboard,whiteboard-teaching
|
|
96
|
+
npx agent-whiteboard install-skills --force # overwrite skills that already exist
|
|
97
|
+
npx agent-whiteboard install-skills --list # show the bundled skills
|
|
64
98
|
```
|
|
65
99
|
|
|
66
|
-
|
|
100
|
+
Existing skill folders are left untouched unless you pass `--force`. `--dir <path>` installs into any other folder (for agents that keep skills elsewhere). Prefer to do it by hand? Copy the folders from [`skills/`](skills/) yourself. Restart your agent session afterwards so it picks the skills up.
|
|
101
|
+
|
|
102
|
+
## Use cases
|
|
103
|
+
|
|
104
|
+
Agent Whiteboard is domain-agnostic; these are the workflows it is built for. Each links the tools involved.
|
|
105
|
+
|
|
106
|
+
| Context | Workflow | Tools |
|
|
107
|
+
|---------------------------|-------------------------------------------------------|-------------------------------------------------------|
|
|
108
|
+
| **Teaching & training** | Concept explainer (one visual, then pause) | `render` → `wait_done` |
|
|
109
|
+
| | Progressive build-up of a diagram | step-frames + `wait_done` per frame |
|
|
110
|
+
| | Worked example (math / algorithm), one step per frame | `katex` / `mermaid` step-frames, `step` / `seek` |
|
|
111
|
+
| | Learner-driven branching on a topic map | `wait_click` with `node_actions` |
|
|
112
|
+
| **Assessment** | Inline self-check quiz (scored in the browser) | `render` quiz with a key → `wait_quiz` |
|
|
113
|
+
| | Deferred quiz graded by the agent | `render` quiz without a key → `wait_quiz` |
|
|
114
|
+
| | Teach → quiz → remediate; spaced review | quizzes + `list_snapshots` |
|
|
115
|
+
| **Analysis & data** | Iterative data exploration, version history | `vega-lite` + `wait_done`, History panel |
|
|
116
|
+
| | Findings walkthrough / report | step-frames (`html` + `vega-lite`) + `export_html` |
|
|
117
|
+
| | Statistics / math derivation with an illustration | `katex` + `vega-lite` |
|
|
118
|
+
| **Design & architecture** | Iterative system design, click to drill down | `mermaid` + `wait_click` |
|
|
119
|
+
| | Option comparison and decision | step-frames + `html` table |
|
|
120
|
+
| | Explaining a codebase or protocol; onboarding pack | sequence diagrams, step-frames, quiz, `export_html` |
|
|
121
|
+
| **Sharing** | Export a lesson or analysis; share a workspace | `export_html`; zip export/import in the History panel |
|
|
67
122
|
|
|
68
|
-
|
|
123
|
+
## Example agent flow
|
|
69
124
|
|
|
70
125
|
```
|
|
71
|
-
|
|
126
|
+
render(type="mermaid", payload="graph TD; A --> B", options={ workspace: "my-lesson", title: "System overview" })
|
|
127
|
+
→ wait_done() # agent pauses; user clicks Done when ready
|
|
128
|
+
→ render(type="katex", payload="E = mc^2", options={ workspace: "my-lesson" })
|
|
129
|
+
→ wait_done()
|
|
72
130
|
```
|
|
73
131
|
|
|
74
|
-
|
|
132
|
+
Paced, user-acknowledged reveal — interleave `wait_done()` between `append_frame()` calls so each frame waits for the user before the next one appears (useful for large or complex step-frames sequences the agent is building on the fly):
|
|
75
133
|
|
|
76
|
-
|
|
134
|
+
```
|
|
135
|
+
init_step_frames(frame_type="mermaid", workspace="my-lesson", title="TCP handshake")
|
|
136
|
+
→ append_frame(id, payload="graph LR; Client -- SYN --> Server", label="1. SYN")
|
|
137
|
+
→ wait_done() # user reviews frame 1, clicks Done
|
|
138
|
+
→ append_frame(id, payload="graph LR; Client -- SYN --> Server; Server -- SYN-ACK --> Client", label="2. SYN-ACK")
|
|
139
|
+
→ wait_done() # user reviews frame 2, clicks Done
|
|
140
|
+
→ append_frame(id, payload="...", label="3. ACK")
|
|
141
|
+
→ commit_step_frames(id)
|
|
142
|
+
```
|
|
77
143
|
|
|
78
|
-
|
|
79
|
-
|----------------------------|-----------------------|-------------|
|
|
80
|
-
| `PORT` | `3000` | Server port — serves the REST/MCP/WebSocket API, and (via `npx agent-whiteboard`) the built client too. |
|
|
81
|
-
| `HOST` | `localhost` | Bind address. Must resolve to a loopback address (`localhost`, `127.0.0.1`, `::1`) — the server refuses to start otherwise unless `ALLOW_NON_LOOPBACK` is set. See [Trust model](#trust-model). |
|
|
82
|
-
| `ALLOW_NON_LOOPBACK` | unset | Set to `1` to allow binding a non-loopback `HOST`. Not recommended — there is no authentication on any endpoint. |
|
|
83
|
-
| `WHITEBOARD_SNAPSHOTS_DIR` | `~/.agent-whiteboard` | Snapshot storage location. |
|
|
84
|
-
| `CHANNEL_PORT` | `3001` | Port for the stdio-channel HTTP relay used by Claude Code's channel forwarding (only relevant if Claude Code was started with channels enabled). |
|
|
144
|
+
`wait_done()` isn't coupled to step-frames — it just blocks on the Done button — so this pattern works with any number of frames and needs no dedicated tool support.
|
|
85
145
|
|
|
86
|
-
|
|
146
|
+
Knowledge check — render a quiz and wait for the user to submit it (see [Quizzes](#quizzes)):
|
|
87
147
|
|
|
88
|
-
|
|
148
|
+
```
|
|
149
|
+
render(type="quiz", payload='{"questions":[{"id":"q1","text":"What does SYN start?","kind":"single","options":[{"id":"a","text":"A handshake"},{"id":"b","text":"A teardown"}]}]}', options={ workspace: "my-lesson" })
|
|
150
|
+
→ wait_quiz() # no answer key in the payload → deferred: the answers come back to the agent
|
|
151
|
+
→ evaluate result.answers, explain the right answers, and score them
|
|
152
|
+
```
|
|
89
153
|
|
|
90
154
|
## MCP tools
|
|
91
155
|
|
|
@@ -93,14 +157,14 @@ Select `agent-whiteboard` and enable it. The tools will be available immediately
|
|
|
93
157
|
|
|
94
158
|
Push content to the whiteboard canvas. Replaces whatever is currently on screen.
|
|
95
159
|
|
|
96
|
-
| `type`
|
|
97
|
-
|
|
98
|
-
| `"mermaid"`
|
|
99
|
-
| `"svg"`
|
|
100
|
-
| `"html"`
|
|
101
|
-
| `"katex"`
|
|
102
|
-
| `"vega-lite"` | Vega-Lite JSON spec (must be valid JSON string).
|
|
103
|
-
| `"quiz"`
|
|
160
|
+
| `type` | `payload` |
|
|
161
|
+
|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
162
|
+
| `"mermaid"` | Mermaid diagram source. Must begin with a valid keyword (`graph`, `flowchart`, `sequenceDiagram`, `classDiagram`, `erDiagram`, `gantt`, `pie`, `mindmap`). |
|
|
163
|
+
| `"svg"` | Inline SVG markup. |
|
|
164
|
+
| `"html"` | HTML/CSS fragment. Sanitized via DOMPurify — inline styles only; `<script>` and `<style>` tags are stripped. |
|
|
165
|
+
| `"katex"` | LaTeX string, rendered in display mode. |
|
|
166
|
+
| `"vega-lite"` | Vega-Lite JSON spec (must be valid JSON string). |
|
|
167
|
+
| `"quiz"` | JSON quiz — see [Quizzes](#quizzes). Valid in `render()` only (not in `slideshow()` or step-frames). |
|
|
104
168
|
|
|
105
169
|
`render()` is single-frame only. For a step-through sequence (multiple frames navigable via `step()`/`seek()`), use [`init_step_frames`/`append_frame`/`commit_step_frames`](#init_step_framesframe_type-workspace-title) instead — see [Step-frames sequences](#step-frames-sequences).
|
|
106
170
|
|
|
@@ -251,36 +315,33 @@ There is no one-shot way to render a multi-frame sequence — `render()` is sing
|
|
|
251
315
|
}
|
|
252
316
|
```
|
|
253
317
|
|
|
254
|
-
##
|
|
318
|
+
## History & snapshots
|
|
255
319
|
|
|
256
|
-
|
|
257
|
-
render(type="mermaid", payload="graph TD; A --> B", options={ workspace: "my-lesson", title: "System overview" })
|
|
258
|
-
→ wait_done() # agent pauses; user clicks Done when ready
|
|
259
|
-
→ render(type="katex", payload="E = mc^2", options={ workspace: "my-lesson" })
|
|
260
|
-
→ wait_done()
|
|
261
|
-
```
|
|
320
|
+
Every `render()` / `commit_step_frames()` call writes a JSON snapshot to `~/.agent-whiteboard/<workspace>/` (override with the `WHITEBOARD_SNAPSHOTS_DIR` environment variable). The browser's right-side controls panel has a history toggle that opens a panel for browsing and loading past snapshots grouped by workspace, plus a delete button that opens a 2-step modal (`client/src/DeleteExportModal.svelte`) for deleting an entire workspace or a chosen subset of snapshots. Destructive actions require a second confirming click.
|
|
262
321
|
|
|
263
|
-
|
|
322
|
+
A single **Import / export** button (opposing-arrows icon) opens a small keyboard-accessible menu with two entries: **Export…** opens the same modal for exporting a workspace or subset of snapshots as self-contained HTML (the `export_html()` tool / `POST /export-html` endpoint described above) or as a portable `.zip` (`POST /export-zip`), and **Import…** opens a dialog for loading such a `.zip` into this or another whiteboard instance (`POST /import`, merging or creating a new workspace). Dropping a `.zip` anywhere on the page also starts an import.
|
|
264
323
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
→ wait_done() # user reviews frame 1, clicks Done
|
|
269
|
-
→ append_frame(id, payload="graph LR; Client -- SYN --> Server; Server -- SYN-ACK --> Client", label="2. SYN-ACK")
|
|
270
|
-
→ wait_done() # user reviews frame 2, clicks Done
|
|
271
|
-
→ append_frame(id, payload="...", label="3. ACK")
|
|
272
|
-
→ commit_step_frames(id)
|
|
273
|
-
```
|
|
324
|
+
| Step 1 — choose a workspace | Step 2 — delete requires a second confirming click |
|
|
325
|
+
|----------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------|
|
|
326
|
+
| <img src="docs/screenshots/delete-workspace-picker.png" width="380" alt="Delete/Export modal step 1: workspace picker with snapshot counts"> | <img src="docs/screenshots/delete-confirmation.png" width="380" alt="Delete confirmation: button relabels to Click again to confirm"> |
|
|
274
327
|
|
|
275
|
-
|
|
328
|
+
| Step 2 — export a subset of snapshots | The resulting self-contained HTML, opened directly in a browser |
|
|
329
|
+
|----------------------------------------------------------------------------------------------------------------|---|
|
|
330
|
+
| <img src="docs/screenshots/export-select-item.png" width="380" alt="Selecting individual snapshots to export"> | <img src="docs/screenshots/export-outputhtml.png" width="480" alt="Exported HTML file opened offline, showing the table of contents and rendered diagrams"> |
|
|
276
331
|
|
|
277
|
-
|
|
332
|
+
## Environment variables
|
|
278
333
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
334
|
+
| Variable | Default | Description |
|
|
335
|
+
|-----------------------------|------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
336
|
+
| `PORT` | `3000` | Server port — serves the REST/MCP/WebSocket API, and (via `npx agent-whiteboard`) the built client too. |
|
|
337
|
+
| `HOST` | `localhost` | Bind address. Must resolve to a loopback address (`localhost`, `127.0.0.1`, `::1`) — the server refuses to start otherwise unless `ALLOW_NON_LOOPBACK` is set. See [Trust model](#trust-model). |
|
|
338
|
+
| `ALLOW_NON_LOOPBACK` | unset | Set to `1` to allow binding a non-loopback `HOST`. Not recommended — there is no authentication on any endpoint. |
|
|
339
|
+
| `WHITEBOARD_SNAPSHOTS_DIR` | `~/.agent-whiteboard` | Snapshot storage location. |
|
|
340
|
+
| `CHANNEL_PORT` | `3001` | Port for the stdio-channel HTTP relay used by Claude Code's channel forwarding (only relevant if Claude Code was started with channels enabled). |
|
|
341
|
+
|
|
342
|
+
**Port conflicts:** if `:3000` (or `:3001`/`:5173`, see below) is already in use on your machine, set `PORT` (and `CHANNEL_PORT` if needed) to a free port before starting.
|
|
343
|
+
|
|
344
|
+
> Dev-only (`npm run dev`, not `npx agent-whiteboard`): the Vite client dev server always runs on `:5173`, hardcoded in `client/vite.config.ts` — not currently configurable via an environment variable.
|
|
284
345
|
|
|
285
346
|
## REST fallback
|
|
286
347
|
|
|
@@ -315,19 +376,24 @@ All tools have HTTP equivalents for scripting or testing without an MCP client:
|
|
|
315
376
|
|
|
316
377
|
All endpoints return the same JSON shapes as the MCP tools, except `POST /export-html`, which streams the HTML file directly (`Content-Disposition: attachment`). `mode: "cdn"` (default) links Mermaid/Bootstrap/KaTeX from a CDN; `mode: "offline"` embeds them for offline reading, at the cost of a much larger file (the Mermaid bundle alone is ~3.3MB).
|
|
317
378
|
|
|
318
|
-
##
|
|
379
|
+
## Development
|
|
319
380
|
|
|
320
|
-
|
|
381
|
+
Running from source (contributing, or testing an unreleased change) instead of `npx agent-whiteboard`:
|
|
321
382
|
|
|
322
|
-
|
|
383
|
+
```bash
|
|
384
|
+
npm install
|
|
385
|
+
npm run dev
|
|
386
|
+
```
|
|
323
387
|
|
|
324
|
-
|
|
325
|
-
|----------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------|
|
|
326
|
-
| <img src="docs/screenshots/delete-workspace-picker.png" width="380" alt="Delete/Export modal step 1: workspace picker with snapshot counts"> | <img src="docs/screenshots/delete-confirmation.png" width="380" alt="Delete confirmation: button relabels to Click again to confirm"> |
|
|
388
|
+
This starts the Node server (`:3000`), the Vite dev server (`:5173`), and opens the browser tab automatically.
|
|
327
389
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
390
|
+
## Tests
|
|
391
|
+
|
|
392
|
+
```bash
|
|
393
|
+
npm run check # everything CI runs: typecheck → lint → unit tests → build
|
|
394
|
+
npm test # Vitest unit/integration tests (server + client)
|
|
395
|
+
npm run test:e2e # Playwright end-to-end tests (requires npm run dev:test first)
|
|
396
|
+
```
|
|
331
397
|
|
|
332
398
|
## Manual showcase
|
|
333
399
|
|
|
@@ -358,30 +424,6 @@ Section flags (combinable):
|
|
|
358
424
|
--intro-delay <ms> How long each intro card stays up (default: 4000)
|
|
359
425
|
```
|
|
360
426
|
|
|
361
|
-
## Tests
|
|
362
|
-
|
|
363
|
-
```bash
|
|
364
|
-
npm test # Vitest unit/integration tests (server)
|
|
365
|
-
npm run test:e2e # Playwright end-to-end tests (requires npm run dev:test first)
|
|
366
|
-
```
|
|
367
|
-
|
|
368
|
-
## MCP registration
|
|
369
|
-
|
|
370
|
-
`.mcp.json` is committed to the repo root and picked up automatically by Claude Code:
|
|
371
|
-
|
|
372
|
-
```json
|
|
373
|
-
{
|
|
374
|
-
"mcpServers": {
|
|
375
|
-
"agent-whiteboard": {
|
|
376
|
-
"type": "sse",
|
|
377
|
-
"url": "http://localhost:3000/mcp"
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
```
|
|
382
|
-
|
|
383
|
-
The server must be running before Claude Code connects. If you override `PORT`, update the URL above to match. See [Environment variables](#environment-variables) for the full list of overridable settings.
|
|
384
|
-
|
|
385
427
|
## License
|
|
386
428
|
|
|
387
429
|
[PolyForm Noncommercial License 1.0.0](LICENSE) — free to use, modify, and redistribute for any non-commercial purpose. Commercial use isn't covered by this license; if you want to use this commercially, reach out at bobpuley@gmail.com to discuss a license.
|
package/bin/cli.js
CHANGED
|
@@ -10,8 +10,17 @@
|
|
|
10
10
|
|
|
11
11
|
import { dirname, join } from "node:path";
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
|
|
14
|
+
// Subcommand: `agent-whiteboard install-skills ...` — copies the bundled agent
|
|
15
|
+
// skills and exits, without loading the server. Dispatched before the server
|
|
16
|
+
// imports below (loaded dynamically) so it stays fast and side-effect free.
|
|
17
|
+
if (process.argv[2] === "install-skills") {
|
|
18
|
+
const { runInstallSkills } = await import("./install-skills.js");
|
|
19
|
+
process.exit(runInstallSkills(process.argv.slice(3)));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const { default: open } = await import("open");
|
|
23
|
+
const { startServer } = await import("../dist/server/index.js");
|
|
15
24
|
|
|
16
25
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
17
26
|
const staticRoot = join(here, "..", "dist", "client");
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// `agent-whiteboard install-skills` — copies the bundled agent skills into a
|
|
2
|
+
// Claude Code skills folder. Plain ESM JS, no dependencies, so it works from
|
|
3
|
+
// the npx cache without touching the server or the "open" package.
|
|
4
|
+
|
|
5
|
+
import { cpSync, existsSync, mkdirSync, readdirSync, statSync } from "node:fs";
|
|
6
|
+
import { homedir } from "node:os";
|
|
7
|
+
import { dirname, join, resolve } from "node:path";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
|
|
10
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
export const BUNDLED_SKILLS_DIR = join(here, "..", "skills");
|
|
12
|
+
|
|
13
|
+
export const USAGE = `Usage: agent-whiteboard install-skills [options]
|
|
14
|
+
|
|
15
|
+
Copies the bundled agent skills into a Claude Code skills folder.
|
|
16
|
+
|
|
17
|
+
Options:
|
|
18
|
+
--project Install into ./.claude/skills (current project) instead of ~/.claude/skills
|
|
19
|
+
--dir <path> Install into <path> (overrides --project)
|
|
20
|
+
--only <a,b,c> Install only the named skills (comma-separated)
|
|
21
|
+
--force Overwrite skills that already exist
|
|
22
|
+
--list List the bundled skills and exit
|
|
23
|
+
-h, --help Show this help`;
|
|
24
|
+
|
|
25
|
+
export function listBundledSkills(srcDir = BUNDLED_SKILLS_DIR) {
|
|
26
|
+
if (!existsSync(srcDir)) return [];
|
|
27
|
+
return readdirSync(srcDir)
|
|
28
|
+
.filter((n) => statSync(join(srcDir, n)).isDirectory() && existsSync(join(srcDir, n, "SKILL.md")))
|
|
29
|
+
.sort();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function parseArgs(argv) {
|
|
33
|
+
const opts = { project: false, dir: null, only: null, force: false, list: false, help: false };
|
|
34
|
+
for (let i = 0; i < argv.length; i++) {
|
|
35
|
+
const a = argv[i];
|
|
36
|
+
if (a === "--project") opts.project = true;
|
|
37
|
+
else if (a === "--force") opts.force = true;
|
|
38
|
+
else if (a === "--list") opts.list = true;
|
|
39
|
+
else if (a === "-h" || a === "--help") opts.help = true;
|
|
40
|
+
else if (a === "--dir" || a === "--only") {
|
|
41
|
+
const v = argv[++i];
|
|
42
|
+
if (!v || v.startsWith("--")) throw new Error(`${a} requires a value`);
|
|
43
|
+
if (a === "--dir") opts.dir = v;
|
|
44
|
+
else opts.only = v.split(",").map((s) => s.trim()).filter(Boolean);
|
|
45
|
+
} else throw new Error(`unknown option: ${a}`);
|
|
46
|
+
}
|
|
47
|
+
return opts;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Copy skills. Returns { dest, installed, skipped } (skipped = already present, no --force).
|
|
52
|
+
* Throws on unknown --only names.
|
|
53
|
+
*/
|
|
54
|
+
export function installSkills({ srcDir = BUNDLED_SKILLS_DIR, dest, only = null, force = false }) {
|
|
55
|
+
const available = listBundledSkills(srcDir);
|
|
56
|
+
const names = only ?? available;
|
|
57
|
+
const unknown = names.filter((n) => !available.includes(n));
|
|
58
|
+
if (unknown.length) {
|
|
59
|
+
throw new Error(`unknown skill(s): ${unknown.join(", ")} (available: ${available.join(", ")})`);
|
|
60
|
+
}
|
|
61
|
+
mkdirSync(dest, { recursive: true });
|
|
62
|
+
const installed = [];
|
|
63
|
+
const skipped = [];
|
|
64
|
+
for (const name of names) {
|
|
65
|
+
const target = join(dest, name);
|
|
66
|
+
if (existsSync(target) && !force) {
|
|
67
|
+
skipped.push(name);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
cpSync(join(srcDir, name), target, { recursive: true, force: true });
|
|
71
|
+
installed.push(name);
|
|
72
|
+
}
|
|
73
|
+
return { dest, installed, skipped };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** CLI entry. Returns the process exit code. */
|
|
77
|
+
export function runInstallSkills(argv, { log = console.log, err = console.error } = {}) {
|
|
78
|
+
let opts;
|
|
79
|
+
try {
|
|
80
|
+
opts = parseArgs(argv);
|
|
81
|
+
} catch (e) {
|
|
82
|
+
err(`${e.message}\n\n${USAGE}`);
|
|
83
|
+
return 2;
|
|
84
|
+
}
|
|
85
|
+
if (opts.help) {
|
|
86
|
+
log(USAGE);
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
89
|
+
if (opts.list) {
|
|
90
|
+
for (const n of listBundledSkills()) log(n);
|
|
91
|
+
return 0;
|
|
92
|
+
}
|
|
93
|
+
const dest = opts.dir
|
|
94
|
+
? resolve(opts.dir)
|
|
95
|
+
: opts.project
|
|
96
|
+
? resolve(process.cwd(), ".claude", "skills")
|
|
97
|
+
: join(homedir(), ".claude", "skills");
|
|
98
|
+
try {
|
|
99
|
+
const { installed, skipped } = installSkills({ dest, only: opts.only, force: opts.force });
|
|
100
|
+
for (const n of installed) log(`installed ${n}`);
|
|
101
|
+
for (const n of skipped) log(`skipped ${n} (already exists; use --force to overwrite)`);
|
|
102
|
+
log(`\nSkills folder: ${dest}`);
|
|
103
|
+
return 0;
|
|
104
|
+
} catch (e) {
|
|
105
|
+
err(`error: ${e.message}`);
|
|
106
|
+
return 1;
|
|
107
|
+
}
|
|
108
|
+
}
|
package/dist/client/assets/{abnfDiagram-VCTEODGH-Dj8sd_5g.js → abnfDiagram-VCTEODGH-Bwh5lUgu.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{g as p,r as u,d as a}from"./chunk-SVP7TREG-
|
|
1
|
+
import{g as p,r as u,d as a}from"./chunk-SVP7TREG-DPtBhcA2.js";import{p as f}from"./chunk-JWPE2WC7-DVxbj6OP.js";import{_ as n,l as o}from"./mermaid.core-DKnSsFxv.js";import{M as c,b as d}from"./cynefin-OW5HDTMX-yFRC4RbL.js";import"./index-CwpL9TCq.js";import"./step-BEQzeS8V.js";var v=d().RailroadAbnf.parser.LangiumParser,i=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),l=t?parseInt(t,10):0,m=s?parseInt(s,10):1/0;return{min:l,max:m}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return i(e.element);case"AbnfOptionalGroup":return{type:"optional",element:i(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:i(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new c(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},F={parser:R,db:a,renderer:u,styles:p};export{F as diagram};
|