Turn any folder of source material into a professional Fumadocs documentation
website and deploy it to https://{product-id}.example.com/docs.
A code repo and a folder of articles are the same thing: a folder of source material. There is one unified flow, not separate modes. The difference is only in what each file contributes — code becomes explained API/usage docs; an article becomes a presented page; an image becomes embedded media.
The core idea: read the folder one unit at a time and incrementally refine the docs. Don't read everything then write everything. Each file you ingest updates the evolving outline and fills in or improves pages — including earlier ones. This scales past the context window and produces a coherent, deduplicated result.
User Configuration
Defaults are portable. Base domain is example.com; subdomain is the product id.
Deploy delegates to lov-deploy-to-vercel (reads CLOUDFLARE_API_KEY).
See references/user-config.md.
When to Use
- "Use fumadocs to generate a documentation site for this project and deploy it to xxx.example.com/docs"
- "Organize this folder full of articles and images into a documentation website"
- A code repo, an article collection, or a mixed knowledge folder needs a polished, navigable, image-rich docs site
Workflow (MANDATORY — follow in order)
Step 0: Resolve skill root
export SKILL_DIR="${SKILL_DIR:-$(pwd)}" # or the installed lov-repo2docs dir
If any network command times out in the sandbox, export the proxy once:
export https_proxy=http://127.0.0.1:7890 http_proxy=http://127.0.0.1:7890 all_proxy=socks5://127.0.0.1:7891
Step 1: Collect inputs with AskUserQuestion
Use AskUserQuestion BEFORE doing anything, unless the user already gave all:
- Source: GitHub URL, local folder path, or current directory?
- Product id: the subdomain →
{product-id}.example.com. Propose a slug from the folder/repo name; confirm. - Title: human-facing name for the docs.
- Deploy now?: deploy to Vercel + bind subdomain immediately, or generate only.
Step 2: Resolve the source folder
| Source | Action |
|---|---|
| GitHub URL | git clone --depth 1 <url> <tmp>/src |
| Local path | use as-is (read-only) |
| Current dir | use $(pwd) |
Never write into the source folder. The docs site is a separate directory.
Step 3: Inventory the folder
Build a manifest of every file, classified and ordered (overview material first):
python3 "$SKILL_DIR/scripts/inventory.py" --src "<src>" --out /tmp/manifest.json
The manifest lists units (path, kind ∈ article/code/image/pdf/office/data/other,
title guess, reading order) and counts. This is cheap — it does NOT read full
contents. You read contents later, incrementally. Use counts to gauge scale and
to decide whether to delegate batches to subagents (see Step 5 scaling note).
Step 4: Scaffold the Fumadocs site
python3 "$SKILL_DIR/scripts/scaffold_docs.py" \
--product-id "<id>" --out "<docs-out-dir>" --title "<Title>" --pm pnpm
Sets basePath: '/docs' and resets content/docs/ to a placeholder. See
references/fumadocs.md for layout, the /docs double-prefix gotcha, and
components.
Step 5: Copy images into the site
python3 "$SKILL_DIR/scripts/copy_assets.py" \
--src "<src>" --site "<docs-out-dir>" --out /tmp/assets-map.json
This copies (and, if Pillow is present, downsizes/transcodes HEIC/TIFF/BMP) every
image into public/assets/... and returns a {source-path: /assets/...} map.
Reference images in MDX with the root-relative web path from the map
() — with basePath /docs, Next resolves it to
/docs/assets/foo.png automatically.
Step 6: Incremental authoring loop (the core)
Initialize the outline from the folder's top-level structure — by default,
preserve the source directory hierarchy as the sidebar (a folder → a sidebar
group, its meta.json). Then walk units in order and, for each unit:
- Read the unit (the file contents).
- Place it in the evolving outline: a new page, a section of an existing
page, or merged into a group index. Update the relevant
meta.json. - Write or refine the MDX page:
- For articles/notes/PDF/office: present the content as a real page — preserve the author's substance; clean up formatting; embed its images via the assets map; if a folder holds many images, render a gallery/grid.
- For code: write explained docs — purpose, install, usage, public API, examples — derived from the code, README, and comments. Don't dump source.
- For images with no surrounding article: group them into a gallery page for their folder.
- Refine backward: as later units add context, improve earlier pages, the intro, cross-links, and sidebar order. The structure is emergent and incrementally polished, not one-shot.
Quality bar: a professional, polished site — coherent IA, working internal
links, real content (never lorem ipsum), inviting landing page, images that render
crisply. Use Fumadocs components (<Cards>, <Tabs>, <Steps>, <Callout>,
<ImageZoom>/gallery) per references/fumadocs.md.
Scaling note (large folders): when counts is large, delegate batches to
subagents (Explore / general-purpose). Give each a slice of units + the assets
map; have it return structured page contributions and outline deltas (which group,
which order). The main thread owns and merges the evolving IA so the result stays
coherent. Don't let subagents each invent a separate top-level structure.
Step 7: Verify the build locally
cd "<docs-out-dir>" && pnpm build # must succeed — fix MDX/link/image errors
Step 8: Deploy (if requested)
Delegate to lov-deploy-to-vercel — do NOT reimplement Vercel/DNS here.
Ensure package.json "name" is a valid lowercase slug, then deploy with domain
{product-id}.example.com. The site serves at …/docs via the basePath. After
deploy, verify the live URL returns 200 (curl), not just that the alias was set.
CLI Reference
scripts/inventory.py — enumerate + classify a folder:
| Argument | Default | Description |
|---|---|---|
--src |
(required) | Source folder |
--out |
stdout | Manifest JSON path |
--max-bytes |
2000000 |
Skip title-reading files larger than this |
scripts/copy_assets.py — images → public/assets/:
| Argument | Default | Description |
|---|---|---|
--src |
(required) | Source folder |
--site |
(required) | Fumadocs site root |
--max-width |
1600 |
Downscale wider images (needs Pillow) |
--no-optimize |
off | Copy verbatim, never transcode/resize |
--out |
stdout | Asset path-map JSON |
scripts/scaffold_docs.py — scaffold the site:
| Argument | Default | Description |
|---|---|---|
--product-id |
(required) | Slug → {id}.example.com |
--out |
(required) | Output dir (separate from source) |
--title |
=product-id |
Human-facing title |
--pm |
pnpm |
Package manager |
Dependencies
- Node.js 18+,
npx,git, Python 3.8+ - Optional: Pillow (
pip install Pillow) for image optimization - Vercel CLI (
npm i -g vercel) for deploy lov-deploy-to-vercelskill (Vercel + Cloudflare DNS)
For Fumadocs structure, components, and config, see references/fumadocs.md.
Runtime context (shared)
Before running, read this Skill package's skill.yaml, provided by the host as skill-runtime/v1 context. The field resolution order is: current request, project context, personal Preferences, brand Profile, general defaults.
- Use only fields declared in the Manifest; Profile stores public brand facts, Preferences stores personal work preferences.
required: trueWhen a field is missing, ask the user one focused question based on the Manifest's question configuration; save the answer only after the user explicitly agrees.- Errors provide a copyable
context_id, field path and source; diagnostic content avoids secrets, full private paths and raw configuration.
General feedback loop
When the user raises modification suggestions during a Skill-driven task, before continuing the current artifact you must:
- First determine whether the suggestion is
task-specific(this time only) orreusable(reusable across tasks). task-specificOnly modify the current task, do not change the Skill.reusableFirst determine the scope: domain rules first update the corresponding canonical Skill; rules applicable to all Skills first update the shared specification.- After completing rule updates, versioning, lint and distribution verification, then apply the modification to the current task.
reusableThe modification invalidates previous "confirm", "continue", "send it"; after completing the current artifact modification and read-back you must stop and wait for the user's next instruction, without automatically proceeding to publishing, submission, or other external writes.