Public
Remixed
Shareable agent skill library
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data โ€“ all from the browser, and deployed in milliseconds.

Skill Library (template)

A forkable directory of reusable agent skills. Each skill is a folder of Markdown and code; the app renders every skill's SKILL.md and gives you a one-click Copy skill button that puts a ready-to-run payload on your clipboard.

๐Ÿ”— Live: https://templates--576a3bb6645411f181421607ee4eb77e.web.val.run

Getting started

The intended way to manage this library is with an agent โ€” let it add, edit, and deploy skills for you. You don't need to touch the file tree by hand.

  1. Remix this val to get your own copy.

  2. Connect your coding agent with the Val Town plugin, which bundles the Val Town MCP server and platform skills. It works with Claude Code, Codex, and Cursor:

    npx plugins add val-town/plugins

    See the plugin guide for per-agent install commands.

  3. Ask it to add a skill. For example:

    Add a skill called pr-summarizer to my skills val that reads a GitHub PR diff and writes a release note. Put the prompt in SKILL.md and a fetch script in scripts/.

    The agent reads the existing skills for the pattern, creates skills/<slug>/ with the right files, registers it in the SKILLS manifest, and deploys โ€” all in place.

Prefer not to set up a local agent? Open this val on val.town and talk to Townie in the sidebar instead. Same workflow, in the browser.

You can also just edit a skill by hand โ€” every file opens in the Val Town editor, e.g. skills/hello-world/SKILL.md. A skill's body is plain Markdown, so tweaking wording or fixing a typo is a two-second edit that deploys on save.

How a skill is structured

A skill is a folder under skills/<slug>/ following the standard Agent-Skills layout, so it stays valid and discoverable by agents reading it outside this app:

skills/<slug>/
  SKILL.md       # entry point โ€” what the skill does and the flow to follow
  scripts/       # runnable code the agent writes locally
  knowledge/     # reference docs the agent reads for context
  assets/        # optional HTML fragment, shown as a live example

Only SKILL.md is required. Each skill is also listed in the SKILLS array in lib/skills.ts (slug, name, description, category, and its files), which drives the home grid, the sidebar file tree, and the copy payload โ€” your agent keeps this in sync for you.

Live examples

A skill can ship a live preview โ€” a working UI rendered right on its page, so you can see what the skill produces instead of just reading about it.

To add one, drop an HTML fragment in the skill's assets/ folder (e.g. assets/showcase.html) and list it in the manifest's files. That's it โ€” the app renders the first assets/*.html it finds as an Examples panel on the skill page, and also serves it standalone (and embeddable in an iframe) at /skills/<slug>/example.

The fragment is just HTML; it can pull in CSS or a <script> to be interactive. Link to it from the skill's SKILL.md with a normal Markdown link so agents reading the skill outside this app find it too.

The ui-example skill ships one and walks through the feature.


Technical details

Files

http.tsx                 # routes
lib/skills.ts            # SKILLS manifest, file + copy-payload helpers, frontmatter parse
components/
  Layout.tsx             # HTML shell: Twind, fonts, markdown CSS
  Home.tsx               # skill grid
  SkillDetail.tsx        # rendered SKILL.md, copy button, live example, file tree
  FileView.tsx           # raw file viewer
  FileTree.tsx           # sidebar file tree
frontend/client.js       # clipboard wiring for [data-copy] buttons
skills/<slug>/           # the skills themselves

Routes

RoutePurpose
/Home โ€” grid of skill cards
/skills/:slugSkill detail โ€” rendered SKILL.md, Copy button, file tree
/skills/:slug/exampleStandalone live example (when a skill ships an assets/*.html)
/skills/:slug/files/*Raw file viewer
/client.jsClipboard wiring, served as a module
/sourceRedirects to this val's source

Gating it to your team

By default the library is fully public. To restrict it to members of a Val Town organization, sign requests through std/oauth and check the signed-in user against your org's members.

Requires a Teams account. Create an org, then enable it at https://www.val.town/orgs/ORGNAME/settings/billing (swap in your org name).

Wrap the app's fetch export with the OAuth middleware and add a gate that runs before your routes:

// http.tsx import { getOAuthUserData, oauthMiddleware, } from "https://esm.town/v/std/oauth/middleware.ts"; import { isOrgMember } from "./lib/auth.ts"; app.use("*", async (c, next) => { const session = await getOAuthUserData(c.req.raw); if (!session?.user) return c.text("Sign in required", 401); const { username } = session.user as { username: string }; if (!(await isOrgMember(username))) return c.text("Not authorized", 403); return next(); }); export default oauthMiddleware(app.fetch);

The membership check calls the org members API, with a small set of usernames as a fallback if the token is missing or the call fails:

// lib/auth.ts const ORG_ID = "your-org-uuid"; const FALLBACK = new Set(["you", "teammate"]); export async function isOrgMember(username: string): Promise<boolean> { const token = Deno.env.get("valtown"); if (!token) return FALLBACK.has(username); try { const resp = await fetch( `https://api.val.town/v2/orgs/${ORG_ID}/members?limit=100`, { headers: { Authorization: `Bearer ${token}` } }, ); if (!resp.ok) throw new Error(`${resp.status}`); const { data = [] } = await resp.json(); return data.some((m: any) => (m.username ?? m.user?.username) === username); } catch { return FALLBACK.has(username); } }

OAuth itself needs no config โ€” std/oauth handles credentials and callbacks. The members lookup needs a valtown API token in the val's environment variables. Register /client.js and /source before the gate if you want them to stay public.