Pattern → SKILL.md compiler for Claude Code. Three pluggable ingestors (MDX lessons, Jupyter notebooks, custom), one type-safe pipeline, local atomic writes to ~/.claude/skills/. Next.js 16 + TypeScript. MCP server output is the planned second target.
SaferSkills independently audited collapse (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
A Claude Code skill-building framework. Next.js 16 + TypeScript — three pluggable ingestors (MDX lessons, Jupyter .ipynb / MyST .md, and a one-file extension pattern for any source format) feed a typed pipeline that compiles each pattern into a SKILL.md and atomically writes it to ~/.claude/skills/.
Collapse exists because Claude's default knowledge is stack-agnostic, but most developers live inside one stack at a time. The same idea — reactive state, lifecycle, error boundaries, circuit composition — lands differently in React, Vue, Nuxt, and Qiskit, and a "generic" answer costs round-trips. Collapsed skills carry your cross-stack vocabulary so Claude reaches for the right idiom on the first try, with trigger phrases derived from your annotations.
The repo ships with 17 cross-stack reference lessons under examples/concepts/ and a sample notebook under examples/notebooks/ that exercises the import flow end-to-end. MCP server scaffold generation is the planned second output target — see docs/roadmap.md.
{lines#id} metadata linked to sibling <Note> JSX blocks, scoped per-stack via <LangTab lang="...">. Frontmatter-validated. Cached at module load. Sources read from examples/concepts/*.mdx./import accepts pasted .ipynb JSON, uploaded files, or MyST .md strings. Parses cell_type ∈ { code, markdown }, coerces source: string | string[] correctly, infers kernel language from metadata.kernelspec. Extracts MyST admonitions (:::{note}, :::{warning}, :::{important}, :::{tip}) from adjacent markdown cells for automatic annotation prefill.lib/notebook/ template: types.ts, parse-*.ts, optional extractors, to-annotation-input.ts. Worked example in docs/build-your-own-ingestor.md.generateAnnotationSkillDraft(input) (one annotation → one skill) and generateSkillDraft(lesson) (whole lesson → one skill), both in lib/skill-template.ts.tip, remember, and lesson title; packs ≤5 per skill.<LangTab> blocks; reduces stack-mismatch round-trips.renderSkillFile(draft) emits YAML-frontmatter markdown via js-yaml; lineWidth: -1 to keep descriptions unwrapped.lib/skill-quality.ts returns one of clean | info | warn based on description length, trigger-phrase ambiguity, missing kebab-case names, and oversized bodies. Surfaced as colored dots in /skills.POST /api/skills uses .tmp + rename for crash safety. Zod-validated payloads. Path traversal rejected (any name escaping ~/.claude/skills/ returns 400). Collision returns 409 with the existing description for diff context; no auto-suffix./skills reads ~/.claude/skills/ directly via node:fs, sorted by mtimeMs descending, with quality verdicts and (size / 1024).toFixed(1) KB per skill.<span class="annot" data-annot-id> for hover/click reveal.core | note | gotcha | mistake | mnemonic | cross, each with a calibrated OKLCH background + dashed-to-solid border state for pinned notes./concepts/{slug}/grid stacks all <LangTab>s side-by-side for direct comparison.motion/react (formerly framer-motion), duration: 0.2. Sparing by design.corepack enable or npm i -g pnpm~/.claude/skills/ on session start)git clone https://github.com/akaieuan/collapse.git
cd collapse
pnpm install
pnpm devOpen http://localhost:3000:
quantum-audio-encoding, hover an annotated token to reveal the note.~/.claude/skills/{name}/SKILL.md is written, toast confirms the path.For the notebook on-ramp, drop examples/notebooks/bell-pair-preparation.ipynb into /import.
Three-layer pipeline. Each boundary is a TypeScript interface; no layer reaches into another.
┌─────────────────┐ ┌────────────────────────┐ ┌───────────────┐
│ ingestor │──▶│ template engine │──▶│ persistence │
│ (the on-ramps) │ │ lib/skill-template │ │ /api/skills │
└─────────────────┘ └────────────────────────┘ └───────────────┘
MDX lessons generateAnnotation- ~/.claude/
.ipynb SkillDraft() skills/
MyST .md generateSkillDraft() {name}/
your own ▲ renderSkillFile() SKILL.md
│
▼
(roadmap: MCP server
scaffold output)Type-safe by construction: ingestors produce AnnotationSkillInput, the template engine consumes it without knowing the source format, and persistence consumes a validated SkillDraft. Adding a new ingestor doesn't touch the engine. Adding a second output target (MCP server scaffold) doesn't touch the ingestors.
Stateless: no database, no runtime config, no global state. The skills directory IS the state — read directly on every request.
Atomic: writes go to {file}.tmp then fs.rename() into place, so concurrent or interrupted writes never produce a partial SKILL.md.
app/
├── api/skills/route.ts POST handler (Zod, atomic write, 409)
├── api/skills/draft/route.ts Lesson → draft preview endpoint
├── concepts/[slug]/ MDX lesson viewer (tabs view)
├── concepts/[slug]/grid/ Cross-stack grid view
├── import/ Notebook import flow
├── skills/ ~/.claude/skills/ directory viewer
├── layout.tsx Root layout (theme, fonts, nav)
└── page.tsx Concepts index
lib/
├── lessons/ MDX ingestor (loader, extract, types)
├── notebook/ .ipynb + MyST ingestor (parsers, admonition
│ extractor, adapter)
├── shiki/ Syntax highlighting + annotation transformer
├── skill-template.ts Template engine — draft generation, frontmatter
├── skill-quality.ts Skill linter (clean | info | warn)
└── skill-body.ts Body composition helpers
examples/
├── concepts/ 17 cross-stack reference MDX lessons
└── notebooks/ Sample .ipynb for the import flow
docs/
├── architecture.md Full version of this section
├── build-your-own-ingestor.md Worked example for new source formats
├── skill-md-spec.md SKILL.md format reference
└── roadmap.md MCP track + non-goals
scripts/
└── screenshot.mjs Playwright-based README screenshot pipeline| Language | TypeScript 5 |
| Framework | Next.js 16 (App Router, RSC), Turbopack dev server |
| UI | Tailwind v4 (CSS-first config), shadcn/ui (Nova preset, Base UI under the hood), Geist Sans/Mono |
| MDX | next-mdx-remote-client, remark-mdx, @shikijs/rehype with a custom annotation transformer |
| State | React 19 server components; no client state library |
| Motion | motion (formerly framer-motion) — used sparingly for stage transitions |
| Validation | Zod 4 on all API surfaces |
| Test | Vitest (39 tests across parsers, extractors, and templates) |
| Tooling | Playwright (screenshot capture), pnpm 10 workspaces |
| Runtime | Node 20+, local filesystem persistence |
The leverage isn't "I have skills" — it's "I have skills that move with me when I switch stacks." Five things happen when one pattern lives across multiple <LangTab>s:
ref is pull-based, mutates .value in place, the wrapper itself is the dependency edge. A distinction you only feel by writing both side-by-side. The lesson captures it.SKILL.md are pulled from your other <LangTab>s. Asking "how do I do reactive state in Nuxt" in a Vue project loads the Vue skill, sees the Nuxt equivalent inline, and answers correctly first try.vue-watch-effect cites vue-ref-computed. Trigger phrases inherit. The skills directory becomes a vocabulary, not a pile of files.SKILL.md is a plain markdown file with kebab-case frontmatter. Ship via dotfiles or a private gist; teammates drop them in. A Next dev opening a Vue file gets answers shaped like their mental model with the translation key inline.Active development.
<LangTab> / <Note> model, 17 reference lessons.ipynb + MyST .md) with admonition auto-prefillConcepts index
Cross-stack grid view — Quantum audio encoding
Annotated lesson page with pinned note
Notebook import flow with admonition prefill
~/.claude/skills/ directory viewer
Reactivity model — Next.js and Vue side-by-side
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.