AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Markup Mode

skill-spunt-markup-mode-markup-mode · by spunt

Add a frontend-only review/markup layer to any HTML artifact, Markdown document, or live UI so a reviewer can anchor comments to specific text or specific elements, then compile them into tagged Markdown for an agent to act on. Use when the user wants to "mark up this page", "add a review/comment layer", "annotate this artifact", "leave feedback on specific parts", invokes "markup mode", or wants…

No reviews yet
0 installs
0 views
view→install

Install

$ agentstack add skill-spunt-markup-mode-markup-mode

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-spunt-markup-mode-markup-mode)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
1mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Markup Mode? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Markup Mode

Markup Mode is a single, self-contained block of HTML/CSS/JS that turns any web page into a reviewable surface. A reviewer starts Markup Mode, then:

  • selects text → the selection snaps to word boundaries, is highlighted in place, and the exact quote is captured (good for commenting on a specific claim or sentence), or
  • clicks an element → a numbered mark + outline labels it unambiguously (good for design notes on a card, chart, button, etc.).

Notes accumulate in a collapsible dock and compile to tagged Markdown (with a human "Where", the exact quote or a CSS selector, and the comment) that is copied or exported as a .md file to hand to an agent. Everything is client-side: notes live in localStorage; nothing is written to the host's content and no server is involved.

When to use

  • The user wants to review an agent-produced document/artifact and leave precise, per-claim feedback.
  • The user wants to give design notes on specific components of a live UI.
  • The user is building a new HTML artifact and wants a built-in review affordance.
  • The user says "markup mode", "add a comment/review layer", "annotate", "let me flag specific parts", or wants location-anchored notes to pass back to an agent.

When NOT to use

  • A persistent, multi-user, or queryable feedback backend is required (SQLite + endpoints + a triage UI) → that is a heavier, server-backed tool, not this.
  • Non-HTML targets (native apps, PDFs, images).
  • Simple "add a feedback button that POSTs somewhere" — Markup Mode hands back Markdown, not a server round-trip.
  • Client-rendered app shells — pages whose ` is built at load by a JavaScript bundle rather than shipped as static HTML: **Databricks notebook exports**, Next.js/Nuxt/Angular/React SPA exports, or any "empty + bundle" page. The host's own JS replaces the body after load, discarding the injected layer — so the marks never appear. scripts/apply.sh **detects and refuses these by default** with an explanation (--force overrides). To review one anyway, open it in a browser, let it render, **Save Page As → "Webpage, Complete"**, and apply Markup Mode to that static snapshot — or export the source as Markdown and use the one-command .md` route.

How to apply it to a target

Fast path (default) — one command. Use the bundled applier. It splices the layer, writes the markup-enabled copy to the current working directory, records the original artifact's path in the compiled Source: line, and runs a static self-check — all in one round:

scripts/apply.sh  [--accent "#hex"] [--ns name] [--out DIR] [--source PATH]
  • Output defaults to ./.markup.html — the original is never touched.
  • --source defaults to the original target's absolute path, so a reviewer who opens the copy still exports Source: . Override only when the doc will be served from a different real path.
  • The applier's closing static self-check (block present, closing-`` count preserved by the splice, host bytes intact, config echoed) is the default verification tier — see Verifying below.

Keep narration terse on this routine path: run the command, report the self-check result, stop. No step-by-step play-by-play unless the user asks to be walked through it.

Markdown — one command (same fast path). Point the applier straight at a .md/.markdown file and it does the whole route itself: renders the doc to a self-contained HTML preview, fills assets/templates/markdown-host.html, splices the review layer, writes ./.markup.html to CWD with Source: = the original .md's absolute path, and runs the same static self-check.

scripts/apply.sh doc.md [--safe] [--md-engine pandoc|node|python] [--accent "#hex"] [--ns name] [--out DIR] [--source PATH]
  • Converter: auto-detect prefers pandoc (pandoc -f gfm -t html, best GFM fidelity) → falls back to node (npx --yes marked --gfm). --md-engine forces one. python (python3 -m markdown) is reachable only via --md-engine python and prints a one-line reduced GFM fidelity warning (task lists render as literal [ ], bare URLs aren't autolinked). The applier always prints which engine it used. If no usable converter exists for the selected engine, it exits non-zero with an install hint (brew install pandoc, or use Node).
  • Raw HTML: passed through by default (you trust your own doc). --safe does a best-effort post-render strip of `/ blocks, inline on*= handlers, and javascript:/data:text/html` URLs — for locally-trusted review, not a security boundary.
  • Title: first # H1 in the doc, else the filename stem (HTML-escaped).
  • Front-matter: a leading YAML ---…--- block is stripped only when --- is the very first line; a mid-doc --- thematic break is preserved.
  • Relative links/images are rewritten to absolute, URL-encoded file:// URLs anchored at the source .md's directory, with query/fragment preserved. Pure fragment #…, scheme, protocol-relative //…, and filesystem-absolute /… refs are left untouched. Note: file:// rewriting binds images to this machine — a future --embed-images flag would inline them for portability. Anchoring/review itself does not need images to resolve.

Markdown — manual 3-step fallback (no python3, or to understand the render→fill→splice flow):

  1. Render the .md to an HTML fragment (no // wrapper).
  2. Drop it into the content slot of assets/templates/markdown-host.html and set its `; save as .preview.html`.
  3. scripts/apply.sh .preview.html --source /abs/path/to/.md — so Source: points at the original .md, not the preview.

Other artifact types (PDF, images, plain text) are backlog — see references/open-items.md.

No-shell manual splice (any target) — when no shell is available, or to understand the splice: open assets/templates/markup-mode.html, copy everything between the two MARKUP MODE - copy from here / to here markers (both ` elements + the ), paste it immediately before the target's closing tag, and add window.MarkupModeConfig = { sourcePath: "" }; just before it. Do not modify the host's existing markup. For a brand-new artifact you are authoring, bake the block in before ` from the start.

Theme it to the host (optional). The layer reads seven --mm-* variables and auto-detects common host :root tokens (--accent, --bg, --text, --border, …). Override any color — plus namespace, toggle shortcut, and the Source path — via MarkupModeConfig (or apply.sh --accent), with no source editing. Skip it and neutral light/dark defaults are used. See references/adaptation.md for the mapping recipe and full config schema. (Namespace + source name otherwise auto-derive from the filename; localStorage key is markup-mode:.)

> Agent cue — act on this: when a user asks to change any Markup Mode preference or keybinding ("set the markup-mode accent to #6366f1", "rebind the toggle to Ctrl+Shift+M", "default to dark mode"), run scripts/config.sh set — it validates and writes the one config file that governs all future applications. Offer it proactively if they mention wanting a persistent look/shortcut. (scripts/config.sh help lists every key.) For a one-off on a single artifact, pass the matching apply.sh flag instead.

One settings file for every application (markup-mode.config.jsonc). To set defaults once that apply to all future applications (not per-artifact), edit the repo-root markup-mode.config.jsonc. apply.sh reads it as the default source for --accent/--ns/--shortcut/--theme/keymap/behavior and bakes the resolved values in at apply time. Precedence: CLI flag > config file > built-in default (per key); the file ships empty so present-with-defaults == today's behavior; a missing/malformed file is loud-but-non-fatal (warn + fall back, never crash apply). An agent should honor it (read with scripts/config.sh list) and update it on explicit request with the validated, comment-preserving writer:

scripts/config.sh set accent "#6366f1"      # validate + write (preserves the header & other keys)
scripts/config.sh set keymap.addRef.mod shift
scripts/config.sh set behavior.themeMode dark
scripts/config.sh unset accent              # revert one key to default
scripts/config.sh list                      # show every key = resolved value

set/unset are strict (bad input exits non-zero, writes nothing). apply.sh also takes --keymap KEY=CHORD, --config PATH, and --no-config. Full key table + schema: references/adaptation.md §0a.

Verifying (tiered — fast by default)

  • Default — static (one round, no browser). Trust the applier's static self-check. The layer ships a comprehensive regression suite (tests/regression.js), so its behavior is already proven; the only per-application risks — injection correctness and theme clash — are statically checkable.
  • Escalate to a headless browser check only when the layer source was modified or a custom theme was applied — the cases static checks can't cover. Do the whole check in one browser_evaluate returning a single object (mount + a text-selection note + an element-mark note + Compile output), not several round-trips.
  • Force it any time by asking ("verify in a browser", "full verification") or running apply.sh --verify full.
  • For a headless check, serve the file (python3 -m http.server) — Playwright blocks file:; for a human glance, open .
  • The full manual checklist lives in test_cases.md.

Runtime behavior (what the reviewer gets)

  • Enter/exit: the dock's "Start Markup Mode" button or Cmd/Ctrl+Shift+K (configurable); Esc exits (or closes an open note first, or clears the selected note).
  • Text anchor: drag-select → popup beside the selection → type → Save. Draft text is persisted on every keystroke, so clicking away never loses it. The popup is titled "New note" with Discard / Save note.
  • Element anchor: hover/click runs through an intent resolver that prefers real controls, semantic/component boundaries, and prose blocks over anonymous wrapper nodes; ↑/↓ grows/shrinks along that meaningful target chain; click → popup → Save. A persistent outline + compact unnumbered mark label it. In Markdown previews, clicking prose blocks (p, li, headings, blockquote, pre) creates a whole-block text anchor instead of a generic element anchor, while links/images/tables/controls stay element targets.
  • Navigate vs. edit (select-then-edit): a single click on a mark or a dock row reveals + selects the note — its anchor scrolls into view and it gets a persistent active highlight (it does not open the editor and does not arm comment mode, so you can browse freely). Minimized marks remain clickable; the Open dock on reveal preference controls whether revealing a mark also expands the dock. To edit, double-click the mark or row, or click the row's button; the popup is then titled "Editing · Note N" with Cancel / Save. Deleting (the row's ) shows an Undo toast for a few seconds.
  • Cross-references: while a note is open, hold ⌥ Alt/Option and click an element (or ⌥-drag to select a run of text) to insert a concise [ref: ] token into the note text at the cursor (text refs carry the full quote, untruncated). The dashed candidate outline only appears while the add-reference modifier is held. A plain click, plain double-click, or plain selection while the popup is open is a no-op — the Alt modifier is what distinguishes adding a reference from the browser's own click/word-select. Copy-ref support copies that token to the clipboard instead of leaving a note when enabled; the default visible workflow is inline Alt insertion. The add-reference modifier is configurable (keymap.addRef.mod).
  • Hover to view: hovering a mark's pin (when comment mode is off) shows a floating tooltip with the note text — so shared/exported files can be browsed without entering comment mode.
  • Dock: opens as a right rail by default; the minimize glyph collapses it to a bottom-right pill. Drag the header to move when floating; resize from the corner in float mode or the rail grip in docked mode. The dock header shows direct position buttons (float / dock-left / dock-right) — click one to move there immediately. On wide screens a rail reserves a resizable column so the page is not covered; below the compact breakpoint it spans the viewport and clears body margins so overlaid marks continue to line up with the page. Minimizing a rail schedules a follow-up reposition after the page margin settles.
  • Sort marks: a sort control in the dock lets you order marks by Created (default — insertion order), Document order (DOM position), or Manual (drag-reorder dock rows). Dock row ordinals, edit headers, and compiled .md headings reflect the active sort order. On-page marks are intentionally unnumbered so transient sort order does not obstruct or imply stable document identity. Internal note ids are stable regardless of sort.
  • Keyboard / a11y: the notes list is a role="list" with roving arrow-key focus (↑/↓, Enter reveals); the popup is a focus-trapped role="dialog" that restores focus on close; toasts announce via an ARIA live region.
  • Output: A Show Compiled / Hide Compiled toggle reveals a read-only Markdown preview generated from the notes. Copy is visible only while compiled is shown; Export .md is always present (disabled when there are no notes). While the compiled view is open, the preview auto-updates on every note add, edit, delete, or sort change.
  • Export reviewed HTML: a button beside Export .md downloads a self-contained .reviewed.html — the same artifact with the current notes baked into an embedded ` block. **Opening it anywhere rehydrates the review** (marks, dock rows, highlights), so the file itself becomes the shareable container — email it, reopen it on another machine, hand it to a second reviewer. The export is a **clean snapshot** (runtime dock/overlay/popup nodes and any rail body margins are stripped before saving), so re-opening mounts the layer exactly once and re-exporting replaces the notes block rather than stacking it. This is **complementary to Export .md**: the .md is the agent task-list handback (the deliverable to action); the reviewed HTML preserves full context for human re-sharing and multi-session review. **Seed-on-mount precedence:** on open, if localStorage` already has notes for this namespace it wins (a returning reviewer's in-session edits are never clobbered); the baked block is used only as the fallback for a freshly-opened shared file. Re-export to share new edits. (A shared file can't be rewritten in place — Export reviewed HTML always downloads a new copy.)
  • Settings / preferences: the gear opens Appearance and Behavior controls. Appearance supports auto/light/dark theme, reduce motion, accent picker/swatches, and a live contrast readout. Behavior currently includes Show marks and Open dock on reveal. Preferences persist per namespace and still respect MarkupModeConfig color overrides.
  • Copy-ref confirmation: in Copy-ref mode, the confirmation appears as a small pointer chip (near the click point or selection end) instead of the corner toast; the corner toast continues to serve all other messages (inserts, exports, deletes).

Compiled Markdown contract

# Feedback —  (review)
Source:  · N notes · YYYY-MM-DD
How to apply each note: its Locator is the surrounding text with the exact edit span wrapped in ⟪ ⟫. Find that span in the artifact — whitespace and markup may differ from this rendered text, so match on the words, not byte-for-byte. If it resolves to exactly one place, act on it (edit only what was inside ⟪ ⟫); if it resolves to zero or several places, do no

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [spunt](https://github.com/spunt)
- **Source:** [spunt/markup-mode](https://github.com/spunt/markup-mode)
- **License:** MIT
- **Homepage:** https://www.bobspunt.com/software/markup-mode/

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.