@henryavila/mdprobe 0.1.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 +404 -0
- package/bin/cli.js +335 -0
- package/dist/assets/index-DPysqH1p.js +2 -0
- package/dist/assets/index-nl9v2RuJ.css +1 -0
- package/dist/index.html +19 -0
- package/package.json +75 -0
- package/schema.json +104 -0
- package/skills/mdprobe/SKILL.md +358 -0
- package/src/anchoring.js +262 -0
- package/src/annotations.js +504 -0
- package/src/cli-utils.js +58 -0
- package/src/config.js +76 -0
- package/src/export.js +211 -0
- package/src/handler.js +229 -0
- package/src/hash.js +51 -0
- package/src/renderer.js +247 -0
- package/src/server.js +849 -0
- package/src/ui/app.jsx +152 -0
- package/src/ui/components/AnnotationForm.jsx +72 -0
- package/src/ui/components/Content.jsx +334 -0
- package/src/ui/components/ExportMenu.jsx +62 -0
- package/src/ui/components/LeftPanel.jsx +99 -0
- package/src/ui/components/Popover.jsx +94 -0
- package/src/ui/components/ReplyThread.jsx +28 -0
- package/src/ui/components/RightPanel.jsx +171 -0
- package/src/ui/components/SectionApproval.jsx +31 -0
- package/src/ui/components/ThemePicker.jsx +18 -0
- package/src/ui/hooks/useAnnotations.js +160 -0
- package/src/ui/hooks/useClientLibs.js +97 -0
- package/src/ui/hooks/useKeyboard.js +128 -0
- package/src/ui/hooks/useTheme.js +57 -0
- package/src/ui/hooks/useWebSocket.js +126 -0
- package/src/ui/index.html +19 -0
- package/src/ui/state/store.js +76 -0
- package/src/ui/styles/themes.css +1243 -0
package/README.md
ADDED
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
# mdprobe
|
|
4
|
+
|
|
5
|
+
**The missing link between AI coding agents and human review.**
|
|
6
|
+
|
|
7
|
+
AI agents generate specs, docs, and RFCs — but you need to actually *read* them, *annotate* them, and *send structured feedback back*. mdprobe closes that loop: it renders markdown in the browser, lets you annotate inline, and returns structured YAML that agents can parse and act on.
|
|
8
|
+
|
|
9
|
+
It works standalone too — as a markdown viewer with live reload and persistent annotations for any review workflow.
|
|
10
|
+
|
|
11
|
+
[Install](#install) | [Quick Start](#quick-start) | [Features](#features) | [CLI Reference](#cli-reference) | [Library API](#library-api) | [Schema](#annotation-schema) | [AI Integration](#ai-agent-integration)
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## What is mdprobe?
|
|
16
|
+
|
|
17
|
+
A CLI tool that turns markdown files into a review environment in your browser.
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
AI Agent writes spec.md ──> mdprobe spec.md --once ──> Human reviews & annotates ──> spec.annotations.yaml ──> Agent reads feedback
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### The problem
|
|
24
|
+
|
|
25
|
+
AI agents produce markdown output (specs, architecture docs, RFCs) but have no way to get **structured, line-level feedback** from humans. You either paste comments back into chat (losing context) or approve blindly.
|
|
26
|
+
|
|
27
|
+
### The solution
|
|
28
|
+
|
|
29
|
+
mdprobe renders your markdown with full GFM, syntax highlighting, Mermaid diagrams and math, then lets you select text and add annotations — `bug`, `question`, `suggestion`, `nitpick` — that are saved as a YAML sidecar. In `--once` mode, the agent's process blocks until you finish reviewing, then reads the annotations programmatically.
|
|
30
|
+
|
|
31
|
+
It also works as a standalone tool for any markdown review workflow — no AI required.
|
|
32
|
+
|
|
33
|
+
### Three workflows
|
|
34
|
+
|
|
35
|
+
| Workflow | Command | Use case |
|
|
36
|
+
|----------|---------|----------|
|
|
37
|
+
| **View** | `mdprobe spec.md` | Render complex markdown in the browser with live reload |
|
|
38
|
+
| **Review** | `mdprobe spec.md --once` | Agent blocks until human finishes annotating, then reads feedback |
|
|
39
|
+
| **Embed** | `import { createHandler }` | Mount mdprobe inside your own Node.js server or tool |
|
|
40
|
+
|
|
41
|
+
Annotations are stored as plain YAML — readable by humans, parseable by machines, and version-controllable with git.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Install
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npm install -g @henryavila/mdprobe
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Or use directly with npx:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npx @henryavila/mdprobe spec.md
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Requirements
|
|
58
|
+
|
|
59
|
+
- Node.js 20+
|
|
60
|
+
- A browser (auto-opens on macOS, Linux, and WSL)
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Quick Start
|
|
65
|
+
|
|
66
|
+
### View a file
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
mdprobe README.md
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Opens the rendered markdown in your browser. Edit the file in your editor — the browser updates instantly.
|
|
73
|
+
|
|
74
|
+
### View a directory
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
mdprobe docs/
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Discovers all `.md` files recursively and shows a file picker.
|
|
81
|
+
|
|
82
|
+
### Review mode (blocking)
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
mdprobe spec.md --once
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The process **blocks** until you click "Finish Review" in the UI. Annotations are saved to `spec.annotations.yaml`. Useful for AI agents that need human feedback before continuing.
|
|
89
|
+
|
|
90
|
+
### Configure your name
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
mdprobe config author "Your Name"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Your name is attached to every annotation and reply. On first use, mdprobe will prompt you interactively.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Features
|
|
101
|
+
|
|
102
|
+
### Markdown Rendering
|
|
103
|
+
|
|
104
|
+
- **GFM** — tables, task lists, strikethrough, autolinks
|
|
105
|
+
- **Syntax highlighting** — all languages via highlight.js
|
|
106
|
+
- **Mermaid diagrams** — rendered client-side
|
|
107
|
+
- **Math/LaTeX** — via KaTeX (inline and display)
|
|
108
|
+
- **Frontmatter** — YAML and TOML (parsed and stripped from output)
|
|
109
|
+
- **Raw HTML** — passthrough with allowlist
|
|
110
|
+
- **Images** — served from the markdown file's directory
|
|
111
|
+
|
|
112
|
+
### Live Reload
|
|
113
|
+
|
|
114
|
+
File changes are detected via chokidar and pushed to the browser over WebSocket. Debounced at 100ms to avoid flicker during rapid saves. Scroll position is preserved across reloads.
|
|
115
|
+
|
|
116
|
+
### Annotations
|
|
117
|
+
|
|
118
|
+
Select any text in the rendered markdown to open the annotation popover:
|
|
119
|
+
|
|
120
|
+
- **4 tags**: `bug`, `question`, `suggestion`, `nitpick` — color-coded pills
|
|
121
|
+
- **Threaded replies** — discuss annotations inline
|
|
122
|
+
- **Resolve/reopen** — mark items as handled
|
|
123
|
+
- **Persistent** — saved to `.annotations.yaml` sidecar (YAML format, git-friendly)
|
|
124
|
+
- **Draggable popover** — move the form to read the content underneath
|
|
125
|
+
- **Keyboard shortcuts** — `Ctrl+Enter` to save, `Esc` to close
|
|
126
|
+
|
|
127
|
+
### Section Approval
|
|
128
|
+
|
|
129
|
+
Every heading in your document gets approve/reject buttons:
|
|
130
|
+
|
|
131
|
+
- **Symmetric cascade** — approving a parent approves all children; rejecting or resetting does the same
|
|
132
|
+
- **Indeterminate state** — when children have mixed statuses, the parent shows a visual indicator
|
|
133
|
+
- **Approve All / Clear All** — bulk operations for the entire document
|
|
134
|
+
|
|
135
|
+
### Drift Detection
|
|
136
|
+
|
|
137
|
+
When the source markdown changes after annotations were created, mdprobe shows a warning banner. This prevents stale annotations from going unnoticed.
|
|
138
|
+
|
|
139
|
+
### Themes
|
|
140
|
+
|
|
141
|
+
Five built-in themes based on the Catppuccin palette:
|
|
142
|
+
|
|
143
|
+
| Theme | Style |
|
|
144
|
+
|-------|-------|
|
|
145
|
+
| **Mocha** | Dark (default) |
|
|
146
|
+
| **Macchiato** | Dark, warm |
|
|
147
|
+
| **Frappe** | Dark, deep blue |
|
|
148
|
+
| **Latte** | Light |
|
|
149
|
+
| **Light** | Pure white |
|
|
150
|
+
|
|
151
|
+
### Keyboard Shortcuts
|
|
152
|
+
|
|
153
|
+
| Key | Action |
|
|
154
|
+
|-----|--------|
|
|
155
|
+
| `[` | Toggle left panel (files + TOC) |
|
|
156
|
+
| `]` | Toggle right panel (annotations) |
|
|
157
|
+
| `\` | Toggle both panels (focus mode) |
|
|
158
|
+
| `j` / `k` | Next / previous annotation |
|
|
159
|
+
| `?` | Show help overlay |
|
|
160
|
+
| `Ctrl+Enter` | Save annotation |
|
|
161
|
+
| `Esc` | Close popover / modal |
|
|
162
|
+
|
|
163
|
+
### Export
|
|
164
|
+
|
|
165
|
+
Export annotations in four formats:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
mdprobe export spec.md --report # Markdown review report
|
|
169
|
+
mdprobe export spec.md --inline # HTML comments inserted into source
|
|
170
|
+
mdprobe export spec.md --json # Plain JSON
|
|
171
|
+
mdprobe export spec.md --sarif # SARIF 2.1.0 (for CI/CD integration)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Also available via the HTTP API: `GET /api/export?path=spec.md&format=json`
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## CLI Reference
|
|
179
|
+
|
|
180
|
+
```
|
|
181
|
+
mdprobe [files...] [options]
|
|
182
|
+
|
|
183
|
+
Options:
|
|
184
|
+
--port <n> Port number (default: 3000, auto-increments if busy)
|
|
185
|
+
--once Review mode — blocks until human finishes
|
|
186
|
+
--no-open Don't auto-open browser
|
|
187
|
+
--help, -h Show help
|
|
188
|
+
--version, -v Show version
|
|
189
|
+
|
|
190
|
+
Subcommands:
|
|
191
|
+
config [key] [value] Manage configuration
|
|
192
|
+
export <path> [format-flag] Export annotations
|
|
193
|
+
install --plugin Install Claude Code skill
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Config
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
mdprobe config # Show all configuration
|
|
200
|
+
mdprobe config author # Show current author
|
|
201
|
+
mdprobe config author "Name" # Set author
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Configuration is stored in `~/.mdprobe.json`.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Library API
|
|
209
|
+
|
|
210
|
+
### Embedding in your own server
|
|
211
|
+
|
|
212
|
+
```javascript
|
|
213
|
+
import { createHandler } from '@henryavila/mdprobe'
|
|
214
|
+
|
|
215
|
+
const handler = createHandler({
|
|
216
|
+
resolveFile: (req) => '/path/to/file.md',
|
|
217
|
+
listFiles: () => [
|
|
218
|
+
{ id: 'spec', path: '/docs/spec.md', label: 'Specification' },
|
|
219
|
+
{ id: 'adr', path: '/docs/adr.md', label: 'Architecture Decision' },
|
|
220
|
+
],
|
|
221
|
+
basePath: '/review',
|
|
222
|
+
author: 'Review Bot',
|
|
223
|
+
onComplete: (result) => {
|
|
224
|
+
console.log(`Review done: ${result.annotations} annotations`)
|
|
225
|
+
},
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
import http from 'node:http'
|
|
229
|
+
http.createServer(handler).listen(3000)
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Working with annotations programmatically
|
|
233
|
+
|
|
234
|
+
```javascript
|
|
235
|
+
import { AnnotationFile } from '@henryavila/mdprobe/annotations'
|
|
236
|
+
|
|
237
|
+
// Load existing annotations
|
|
238
|
+
const af = await AnnotationFile.load('spec.annotations.yaml')
|
|
239
|
+
|
|
240
|
+
// Query
|
|
241
|
+
const open = af.getOpen()
|
|
242
|
+
const bugs = af.getByTag('bug')
|
|
243
|
+
const mine = af.getByAuthor('Henry')
|
|
244
|
+
|
|
245
|
+
// Mutate
|
|
246
|
+
af.add({
|
|
247
|
+
selectors: {
|
|
248
|
+
position: { startLine: 10, startColumn: 1, endLine: 10, endColumn: 40 },
|
|
249
|
+
quote: { exact: 'selected text', prefix: '', suffix: '' },
|
|
250
|
+
},
|
|
251
|
+
comment: 'This needs clarification',
|
|
252
|
+
tag: 'question',
|
|
253
|
+
author: 'Henry',
|
|
254
|
+
})
|
|
255
|
+
af.resolve(bugs[0].id)
|
|
256
|
+
|
|
257
|
+
// Persist
|
|
258
|
+
await af.save('spec.annotations.yaml')
|
|
259
|
+
|
|
260
|
+
// Export
|
|
261
|
+
import { exportJSON, exportSARIF } from '@henryavila/mdprobe/export'
|
|
262
|
+
const json = exportJSON(af)
|
|
263
|
+
const sarif = exportSARIF(af, 'spec.md')
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## Annotation Schema
|
|
269
|
+
|
|
270
|
+
Annotations are stored in YAML sidecar files (`<filename>.annotations.yaml`):
|
|
271
|
+
|
|
272
|
+
```yaml
|
|
273
|
+
version: 1
|
|
274
|
+
source: spec.md
|
|
275
|
+
source_hash: "sha256:abc123..."
|
|
276
|
+
sections:
|
|
277
|
+
- heading: Introduction
|
|
278
|
+
level: 2
|
|
279
|
+
status: approved
|
|
280
|
+
- heading: Requirements
|
|
281
|
+
level: 2
|
|
282
|
+
status: pending
|
|
283
|
+
annotations:
|
|
284
|
+
- id: "a1b2c3d4"
|
|
285
|
+
selectors:
|
|
286
|
+
position:
|
|
287
|
+
startLine: 15
|
|
288
|
+
startColumn: 1
|
|
289
|
+
endLine: 15
|
|
290
|
+
endColumn: 42
|
|
291
|
+
quote:
|
|
292
|
+
exact: "The system shall support concurrent users"
|
|
293
|
+
prefix: ""
|
|
294
|
+
suffix: ""
|
|
295
|
+
comment: "How many concurrent users? Need a number."
|
|
296
|
+
tag: question
|
|
297
|
+
status: open
|
|
298
|
+
author: Henry
|
|
299
|
+
created_at: "2026-04-08T10:30:00.000Z"
|
|
300
|
+
updated_at: "2026-04-08T10:30:00.000Z"
|
|
301
|
+
replies:
|
|
302
|
+
- author: Alice
|
|
303
|
+
comment: "Target is 500 concurrent."
|
|
304
|
+
created_at: "2026-04-08T11:00:00.000Z"
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
A JSON Schema is available at `@henryavila/mdprobe/schema.json` for validation.
|
|
308
|
+
|
|
309
|
+
### Tags
|
|
310
|
+
|
|
311
|
+
| Tag | Meaning | SARIF Severity |
|
|
312
|
+
|-----|---------|----------------|
|
|
313
|
+
| `bug` | Something is wrong | error |
|
|
314
|
+
| `question` | Needs clarification | note |
|
|
315
|
+
| `suggestion` | Improvement idea | warning |
|
|
316
|
+
| `nitpick` | Minor style/wording | note |
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## AI Agent Integration
|
|
321
|
+
|
|
322
|
+
mdprobe ships with a Claude Code skill that teaches the AI when and how to use it.
|
|
323
|
+
|
|
324
|
+
### Install the skill
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
mdprobe install --plugin
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### Agent workflow
|
|
331
|
+
|
|
332
|
+
1. Agent writes a spec/document to a `.md` file
|
|
333
|
+
2. Agent launches `mdprobe spec.md --once` (blocks)
|
|
334
|
+
3. Human reviews in the browser, adds annotations
|
|
335
|
+
4. Human clicks "Finish Review"
|
|
336
|
+
5. Agent reads `spec.annotations.yaml` and addresses each annotation
|
|
337
|
+
|
|
338
|
+
```javascript
|
|
339
|
+
// Agent reads feedback after review
|
|
340
|
+
import { AnnotationFile } from '@henryavila/mdprobe/annotations'
|
|
341
|
+
|
|
342
|
+
const af = await AnnotationFile.load('spec.annotations.yaml')
|
|
343
|
+
for (const ann of af.getOpen()) {
|
|
344
|
+
console.log(`[${ann.tag}] Line ${ann.selectors.position.startLine}: ${ann.comment}`)
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
## HTTP API
|
|
351
|
+
|
|
352
|
+
All endpoints are available when the server is running.
|
|
353
|
+
|
|
354
|
+
| Method | Endpoint | Description |
|
|
355
|
+
|--------|----------|-------------|
|
|
356
|
+
| `GET` | `/api/files` | List markdown files |
|
|
357
|
+
| `GET` | `/api/file?path=<file>` | Get rendered HTML + TOC + frontmatter |
|
|
358
|
+
| `GET` | `/api/annotations?path=<file>` | Get annotations + sections + drift status |
|
|
359
|
+
| `POST` | `/api/annotations` | Create/update/delete annotations |
|
|
360
|
+
| `POST` | `/api/sections` | Approve/reject/reset sections |
|
|
361
|
+
| `GET` | `/api/export?path=<file>&format=<fmt>` | Export (json, report, inline, sarif) |
|
|
362
|
+
| `GET` | `/api/config` | Get current author |
|
|
363
|
+
|
|
364
|
+
WebSocket at `/ws` provides real-time file change notifications.
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## Development
|
|
369
|
+
|
|
370
|
+
```bash
|
|
371
|
+
git clone https://github.com/henryavila/mdprobe.git
|
|
372
|
+
cd mdprobe
|
|
373
|
+
npm install
|
|
374
|
+
npm run build:ui # Build the Preact UI
|
|
375
|
+
npm test # Run test suite (489 tests)
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### Project structure
|
|
379
|
+
|
|
380
|
+
```
|
|
381
|
+
bin/cli.js CLI entry point
|
|
382
|
+
src/
|
|
383
|
+
server.js HTTP + WebSocket server
|
|
384
|
+
renderer.js Markdown rendering pipeline (remark/rehype)
|
|
385
|
+
annotations.js Annotation CRUD + section approval + cascade
|
|
386
|
+
export.js 4 export formats (report, inline, JSON, SARIF)
|
|
387
|
+
handler.js Library API for embedding
|
|
388
|
+
config.js User configuration (~/.mdprobe.json)
|
|
389
|
+
hash.js SHA-256 drift detection
|
|
390
|
+
anchoring.js Text position matching for highlights
|
|
391
|
+
ui/
|
|
392
|
+
components/ Preact components (App, Content, Popover, Panels...)
|
|
393
|
+
hooks/ Custom hooks (WebSocket, keyboard, theme, annotations)
|
|
394
|
+
state/store.js Preact Signals state management
|
|
395
|
+
styles/themes.css Catppuccin theme system
|
|
396
|
+
schema.json JSON Schema for annotation YAML
|
|
397
|
+
skills/ Claude Code integration skill
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
---
|
|
401
|
+
|
|
402
|
+
## License
|
|
403
|
+
|
|
404
|
+
MIT © [Henry Avila](https://github.com/henryavila)
|