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

Surface

skill-aaryan-kapoor-surface-surface · by Aaryan-Kapoor

Surface-native display for AI agents, driven by the `surface` CLI. Use when the user says "surface this", "show me X", or "put it on my display/screen"; wants a live interactive UI, chart, or tool they act on; needs a question answerable from any device; or asks you to react to what they click — even while you're offline.

No reviews yet
0 installs
0 views
view→install

Install

$ agentstack add skill-aaryan-kapoor-surface-surface

✓ 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-aaryan-kapoor-surface-surface)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
yesterday

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 Surface? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Surface

Surface is the user's universal display. When the user says "surface this", "show me X", "put Y on my display", or "ask me when it's done", drive the surface CLI — a thin client over a local service (127.0.0.1:3000). Hot paths get verbs; everything else, including every custom template, goes through create --template. The table below tells you when; surface --help is authoritative for flags.

Surface-native

A live display the user acts on, not a chat transcript or file viewer — two-way and current: the user answers, clicks, or watches a value change, and you react. Pick by shape before writing HTML — match the source to its verb: markdown → doc --toc, video/URL → video, PDF/image → present, a file you keep editing → link, a yes/no or pick-one → ask --options, a scrolling log → create --template stream. Decompose a multi-source request first — a "presenter view", a "home screen", "a card next to it" is several surfaces (one verb each: present the PDF, video the clip, a bound chart…), composed with slot, never one HTML blob with tabs. Hand-build interactive HTML (create --content - — charts, maps, tools, anything in the shape of the bundled demo gallery — surface seed-demos to see it) only when no verb fits; rendering markdown as HTML or faking a decision card with buttons is the classic miss — the verb is shorter, hot-reloads, and renders natively everywhere. Dynamism earns its place when it adds a decision, a live value, or a visual relationship text can't carry — never less interactive than the task wants, never more.

Session start

  1. surface actions — drain your inbox: clicks that arrived while you were gone. Handle each, then surface ack .
  2. Read SURFACE.md if present — which surfaces this project maintains, which state keys to update when.
  3. surface list — never create a duplicate; update the existing card.
  4. Re-arm your action terminal for any interactive surfaces you own (see the delivery ladder). Terminals die with the session; the surfaces don't.

Commands

| Verb | When to use | |---|---| | create | Build a surface: ad-hoc HTML (--content -) or a template (--template ). The default only for a custom interactive/visual shape — otherwise pick a verb above. | | ask | Ask the user — --options a,b pick-one, --freetext typed answer, --wait blocks; --on targets one screen (else everywhere). Attach context, don't ask blind. | | append | Append to a running stream surface (pipe with -). | | video · doc · present | YouTube/web video · repo markdown (--toc, hot-reloads) · one-shot snapshot of a local PDF/image (web PDF → /proxy/pdf). | | link | Serve a project file live from disk; touch after each edit — your hot-reload target. | | set · patch · state | Live state — change a value without rewriting HTML (see the two-way loop). | | list · read · update · versions · rollback · delete | Artifact lifecycle. update revises a card; rollback restores an earlier version (don't re-type old values); delete removes one. | | template list/show/create | Inspect templates; promote a UI you've built twice (create --from ). | | wait · actions · ack · bind · bindings · unbind | React to clicks — see the delivery ladder. wait --id --event state_patch (or stream_append) wakes you on a peer's post — don't poll. | | reply · notify · open · exec · theme | Talk back / drive the display. theme sets the global look — colors, background, fonts, raw CSS (not per-surface styling); notify/open take --on ; exec pokes live JS into a surface. | | set board '{...}' | Shared fleet dashboard at id board; key by your --agent ('{"status":…,"project":…}'). Render dashboards bound to board's keys (data-surface-bind), don't invent a registry; post when you start/finish/block. | | slot renderer/home/overlay | renderer = whole homescreen launcher (gets injected window.__surfaces/navigate(id)); home = widget; overlay = floating layer (e.g. a DND pill). The user's space — only when asked. | | status · stream · devices | Presence (who's connected/awake — check before --on); tail every event; paired screens. | | init · sync | Scaffold .surface/ + SURFACE.md; reconcile project manifests across machines. | | pair · auth | Pair a new screen; mint/revoke remote SURFACE_SESSION bearers. | | seed-demos · clear-demos | Built-in demo gallery — the fast "show me what Surface can do" tour; clear-demos hides it again (don't delete them one by one; seed-demos revives). |

The two-way loop

A surface that only renders is half-built: state flows out, actions flow back. Never regenerate HTML to change a value — every surface has a JSON state doc. surface set writes one key (dotted keys ok); surface patch '{...}' writes many at once (deep-merge; pipe JSON with -) — prefer it over a chain of sets. State flows out bound in markup with data-surface-bind / data-surface-show, re-rendered live on every screen, and persists across sessions (surface state reads it back — don't blindly re-seed values that are already there). Actions flow back with Surface.action("name", {...}). For a multi-step interaction, keep intermediate clicks local with Surface.stage(key, value) and fire one action at the commit with Surface.commit("name") — so you wake once, on the user's actual intent, not per click. State is a claim, not an animation — never patch a status, progress value, or "running…" for work you didn't actually execute or observe; if you substitute a cheaper check (a probe instead of a re-run), the surface must say so, not render the run you skipped.

Delivery ladder — reacting to clicks

Each action wakes you. Default outside Codex: arm a live action terminal (surface wait --follow) the moment you put up an interactive surface — once, and keep it running for the whole interaction. It drains the pending inbox on connect, shows "agent listening", prints one JSON line per action, and auto-acks each action it hands you (--no-ack to keep them pending) — so only the actions inbox-drain needs a manual ack.

  • Codex CLI: run surface codex setup once. Thereafter Codex-created surfaces flow back into their exact live session without a waiter; dead-session wakes remain consent-gated and fail closed on approvals. Use surface codex status to diagnose the bridge. If setup is unavailable, fall back to a one-shot surface wait.
  • Claude Code: arm it with the Monitor tool (persistent: true), not a backgrounded shell. For anything two-way or ongoing, Monitor is the rule: a one-shot shell only wakes when its process exits, so it catches the first action and sleeps through the rest, leaving the surface unguarded. A backgrounded one-shot surface wait --id is fine only for a single fire-and-forget answer.
  • Other harnesses: per-line watchdog → --follow; wake-on-exit only → one-shot wait, re-arm after each; always-on daemon → a --webhook binding. Recipes: surface wait --help, and docs/interaction/delivery-ladder.md in the Surface repo.
  • The terminal dies with your session — re-arm on return (the inbox drain covers everything clicked while you were gone).
  • One click, one agent. The action event reaches every listener, but each waiter must claim an action before printing it and only one claim wins — so several sessions can safely hold terminals at once without doubling the work. A waiter is scoped to its own project (the git root it started in); --all restores machine-wide consumption, --project targets another repo, --no-ack makes a pure observer that claims nothing.
  • Delivery is handoff, not completion. Surface records an action handled once the line has left the CLI; it cannot know whether you finished the work. Treat action.id as an idempotency key.
  • Offline (clicks land while you're gone)? surface bind --action --run ''/--webhook is the answer — fires when no waiter claims within a five-second first refusal — so an idle or wedged terminal can no longer black-hole your wake bindings; never hand-roll a server, daemon, or systemd unit for this. **A bind runs ` (or wakes a headless session) on the user's machine and quota while they're away with no one in the loop — so a recorded yes is a hard prerequisite, and the user wanting the feature is not that yes.** Before your first bind in a project, read .surface/config.json → bindings.enabled; if it isn't already true, **stop and ask the user in chat** ("wake me on clicks? each wake runs unattended / spends a headless session") and wait for their reply — **never set enabled: true yourself to unblock your task**: the request that created this work (even an urgent "make it fire while I'm away") is *not* that yes; only a separate, explicit user confirmation is. Record *their* answer there, then bind only once it's true. To revoke: unbind and set enabled back to false`.
  • Unhandled clicks always wait in the inbox — nothing is lost.

Conventions

  • Surfaces are self-contained — inline CSS/JS, no CDNs — so they render offline and screenshot headlessly.
  • Most sites block iframes — use embed URLs (open.spotify.com/embed/...), or /proxy/pdf?url=ENCODED for web PDFs.
  • Pass --agent for attribution and --id for recurring cards.
  • Remote/non-loopback callers — CI, scripts, another box, not just agents — point SURFACE_URL at the reachable host and set SURFACE_SESSION, then run the same CLI (surface set, notify); mint/audit/revoke the bearer with auth session issue --role system --label · list · revoke. surface --help / surface --help are authoritative.

Source & license

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

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.