@iamdangavin/claude-skill-vitepress-docs 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/install.js +17 -0
- package/package.json +21 -0
- package/skill/SKILL.md +759 -0
package/install.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { mkdirSync, copyFileSync } from 'fs';
|
|
3
|
+
import { join, dirname } from 'path';
|
|
4
|
+
import { homedir } from 'os';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
|
|
9
|
+
const skillName = 'vitepress-docs';
|
|
10
|
+
const dest = join(homedir(), '.claude', 'skills', skillName);
|
|
11
|
+
|
|
12
|
+
mkdirSync(dest, { recursive: true });
|
|
13
|
+
copyFileSync(join(__dirname, 'skill', 'SKILL.md'), join(dest, 'SKILL.md'));
|
|
14
|
+
|
|
15
|
+
console.log(`✓ Installed @iamdangavin/claude-skill-vitepress-docs`);
|
|
16
|
+
console.log(` → ${dest}/SKILL.md`);
|
|
17
|
+
console.log(` Invoke with: /vitepress-docs in Claude Code`);
|
package/package.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@iamdangavin/claude-skill-vitepress-docs",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Installs the vitepress:docs Claude Code skill — VitePress docs setup, generation, screenshots, and sync.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"claude-skill-vitepress-docs": "./install.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"install.js",
|
|
11
|
+
"skill/"
|
|
12
|
+
],
|
|
13
|
+
"keywords": [
|
|
14
|
+
"claude",
|
|
15
|
+
"claude-code",
|
|
16
|
+
"claude-skill",
|
|
17
|
+
"vitepress",
|
|
18
|
+
"docs"
|
|
19
|
+
],
|
|
20
|
+
"license": "MIT"
|
|
21
|
+
}
|
package/skill/SKILL.md
ADDED
|
@@ -0,0 +1,759 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vitepress:docs
|
|
3
|
+
description: VitePress documentation suite — setup GitHub Pages deployment, generate docs from a codebase, capture screenshots of any running app, and sync docs as code changes.
|
|
4
|
+
argument-hint: [mode: setup|generate|screenshot|sync] or omit to be prompted
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Read
|
|
7
|
+
- Write
|
|
8
|
+
- Edit
|
|
9
|
+
- Bash
|
|
10
|
+
- Glob
|
|
11
|
+
- Grep
|
|
12
|
+
- AskUserQuestion
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Skill: vitepress:docs
|
|
16
|
+
|
|
17
|
+
Four modes for full VitePress documentation lifecycle. If no mode is given as an argument, use AskUserQuestion to prompt for one:
|
|
18
|
+
|
|
19
|
+
- header: "vitepress:docs"
|
|
20
|
+
- question: "Which mode do you need?"
|
|
21
|
+
- options:
|
|
22
|
+
- "setup — Wire up VitePress + GitHub Actions for Pages deployment"
|
|
23
|
+
- "generate — Analyze the codebase and write documentation pages"
|
|
24
|
+
- "screenshot — Capture real screenshots and replace placeholders"
|
|
25
|
+
- "sync — Detect code drift and update docs that are out of date"
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Manifest
|
|
30
|
+
|
|
31
|
+
All modes read and write a manifest at `.vitepress/docs-manifest.json`. This file is **local-only** — always add it to `.gitignore` (under the docs folder's nearest `.gitignore`) so it is never committed or distributed.
|
|
32
|
+
|
|
33
|
+
Add this line if not already present:
|
|
34
|
+
```
|
|
35
|
+
.vitepress/docs-manifest.json
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Manifest schema
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"generated": "ISO-8601 timestamp",
|
|
43
|
+
"project": {
|
|
44
|
+
"type": "user-facing | developer | both",
|
|
45
|
+
"baseUrl": "http://localhost:3000",
|
|
46
|
+
"serverType": "node | wordpress | static | other",
|
|
47
|
+
"startCommand": "npm run dev"
|
|
48
|
+
},
|
|
49
|
+
"pages": [
|
|
50
|
+
{
|
|
51
|
+
"file": "docs/guides/timer.md",
|
|
52
|
+
"title": "Running a Timer",
|
|
53
|
+
"docType": "user-facing",
|
|
54
|
+
"sources": ["src/state/timer.js", "src/components/layouts/workouts.jsx"],
|
|
55
|
+
"images": [
|
|
56
|
+
{
|
|
57
|
+
"path": "docs/public/screenshots/timer-main.png",
|
|
58
|
+
"placeholder": true,
|
|
59
|
+
"caption": "Timer in active interval state",
|
|
60
|
+
"captureUrl": "/workouts/123",
|
|
61
|
+
"captureNote": "Timer should be running with intervals visible"
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
"lastSynced": "ISO-8601 timestamp",
|
|
65
|
+
"syncHash": "sha of source file contents at last sync"
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Mode: setup
|
|
74
|
+
|
|
75
|
+
### Step 1 — Gather project details
|
|
76
|
+
|
|
77
|
+
Use AskUserQuestion for each choice below. For freeform text inputs (repo URL, domain name, docs path) ask as plain text immediately after the relevant choice is made.
|
|
78
|
+
|
|
79
|
+
**Q1 — VitePress status:**
|
|
80
|
+
- header: "VitePress"
|
|
81
|
+
- question: "Is VitePress already installed in this project?"
|
|
82
|
+
- options:
|
|
83
|
+
- "Already installed"
|
|
84
|
+
- "Starting from scratch"
|
|
85
|
+
|
|
86
|
+
**Q2 — Docs folder** (plain text): Ask — "Where do the docs live (or where should they go)? e.g. `docs/` at project root, or the repo root itself."
|
|
87
|
+
|
|
88
|
+
**Q3 — GitHub repo** (plain text): Ask — "What is the GitHub repo? (full URL or `owner/repo`)"
|
|
89
|
+
|
|
90
|
+
**Q4 — Domain type:**
|
|
91
|
+
- header: "Hosting"
|
|
92
|
+
- question: "Where will the docs be served?"
|
|
93
|
+
- options:
|
|
94
|
+
- "Custom domain I own (e.g. docs.myapp.com)"
|
|
95
|
+
- "GitHub Pages subdomain (e.g. owner.github.io/repo-name)"
|
|
96
|
+
|
|
97
|
+
If custom domain: ask as plain text — "What is the domain? (e.g. `docs.myapp.com`)"
|
|
98
|
+
|
|
99
|
+
**Q5 — Deploy trigger:**
|
|
100
|
+
- header: "Deploy trigger"
|
|
101
|
+
- question: "When should docs deploy?"
|
|
102
|
+
- options:
|
|
103
|
+
- "Every push to main"
|
|
104
|
+
- "Every push to a specific branch — I'll tell you which"
|
|
105
|
+
- "Only when a version tag is pushed (v*)"
|
|
106
|
+
- "Manual only (workflow_dispatch)"
|
|
107
|
+
|
|
108
|
+
If specific branch: ask as plain text — "Which branch?"
|
|
109
|
+
|
|
110
|
+
**Q6 — Docs on GitHub:**
|
|
111
|
+
- header: "Repo state"
|
|
112
|
+
- question: "Are the docs already committed and pushed to GitHub?"
|
|
113
|
+
- options:
|
|
114
|
+
- "Yes, already pushed"
|
|
115
|
+
- "No, this is all new"
|
|
116
|
+
|
|
117
|
+
Wait for all answers before proceeding.
|
|
118
|
+
|
|
119
|
+
### Step 2 — Prerequisites checklist
|
|
120
|
+
|
|
121
|
+
Present the relevant checklist as plain text, then use AskUserQuestion to confirm before writing files:
|
|
122
|
+
|
|
123
|
+
- header: "Ready to proceed?"
|
|
124
|
+
- question: "Have you completed the steps above?"
|
|
125
|
+
- options:
|
|
126
|
+
- "Yes, all done — generate the files"
|
|
127
|
+
- "Not yet — I need more time"
|
|
128
|
+
- "I have a question"
|
|
129
|
+
|
|
130
|
+
If "Not yet": tell the user to run `/vitepress:docs setup` again when ready and exit.
|
|
131
|
+
If "I have a question": answer it, then re-ask this question.
|
|
132
|
+
|
|
133
|
+
The checklist to present (tailor to their answers):
|
|
134
|
+
|
|
135
|
+
**Always required:**
|
|
136
|
+
- [ ] GitHub repo exists with push access
|
|
137
|
+
- [ ] In repo **Settings → Pages**, set Source to **"GitHub Actions"**
|
|
138
|
+
_(You do not need to configure anything else here — the workflow handles the entire deployment. Just flip the source and leave everything else alone.)_
|
|
139
|
+
|
|
140
|
+
**If custom domain:**
|
|
141
|
+
- [ ] CNAME DNS record: `docs.yourdomain.com` → `<owner>.github.io`
|
|
142
|
+
- [ ] In repo **Settings → Pages → Custom domain**, enter the domain after DNS propagates
|
|
143
|
+
- [ ] TLS cert will provision automatically after first deploy
|
|
144
|
+
|
|
145
|
+
**If VitePress not installed:**
|
|
146
|
+
- [ ] Node.js 18+ installed
|
|
147
|
+
|
|
148
|
+
Before proceeding to Step 3, ask permission to handle the docs scaffold automatically:
|
|
149
|
+
|
|
150
|
+
- header: "Docs scaffold"
|
|
151
|
+
- question: "VitePress isn't installed yet. Can I create the docs folder and run the install for you?"
|
|
152
|
+
- options:
|
|
153
|
+
- "Yes — set it up for me"
|
|
154
|
+
- "No — I'll do it myself"
|
|
155
|
+
|
|
156
|
+
If yes, run:
|
|
157
|
+
```bash
|
|
158
|
+
mkdir -p DOCS_FOLDER
|
|
159
|
+
cd DOCS_FOLDER && npm init -y && npm install -D vitepress
|
|
160
|
+
```
|
|
161
|
+
Then confirm it succeeded before continuing.
|
|
162
|
+
If no, tell the user to run those two commands in the docs folder and come back when done.
|
|
163
|
+
|
|
164
|
+
**Playwright (required for screenshot mode):**
|
|
165
|
+
|
|
166
|
+
Check if it's already installed:
|
|
167
|
+
```bash
|
|
168
|
+
ls /opt/homebrew/lib/node_modules/playwright/index.mjs 2>/dev/null && echo "Found" || echo "Not found"
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
If not found, install it:
|
|
172
|
+
```bash
|
|
173
|
+
brew install playwright
|
|
174
|
+
playwright install chrome
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
If the user is **not on macOS / Homebrew**, note that the screenshot mode hardcodes the Homebrew path. They'll need to find their global Playwright path (`npm root -g`) and will have to update the import line in any capture scripts the skill generates. Offer to help with that when screenshot mode is first used.
|
|
178
|
+
|
|
179
|
+
### Step 3 — Generate files
|
|
180
|
+
|
|
181
|
+
**`.github/workflows/docs.yml`** (at repo root, not inside docs folder):
|
|
182
|
+
|
|
183
|
+
```yaml
|
|
184
|
+
name: Deploy Docs
|
|
185
|
+
|
|
186
|
+
on:
|
|
187
|
+
push:
|
|
188
|
+
branches: [BRANCH] # from answer 5
|
|
189
|
+
workflow_dispatch:
|
|
190
|
+
|
|
191
|
+
permissions:
|
|
192
|
+
contents: read
|
|
193
|
+
pages: write
|
|
194
|
+
id-token: write
|
|
195
|
+
|
|
196
|
+
concurrency:
|
|
197
|
+
group: pages
|
|
198
|
+
cancel-in-progress: false
|
|
199
|
+
|
|
200
|
+
jobs:
|
|
201
|
+
build:
|
|
202
|
+
runs-on: ubuntu-latest
|
|
203
|
+
steps:
|
|
204
|
+
- uses: actions/checkout@v4
|
|
205
|
+
with:
|
|
206
|
+
fetch-depth: 0
|
|
207
|
+
- uses: actions/setup-node@v4
|
|
208
|
+
with:
|
|
209
|
+
node-version: 20
|
|
210
|
+
cache: npm
|
|
211
|
+
cache-dependency-path: DOCS_FOLDER/package-lock.json
|
|
212
|
+
- run: npm ci
|
|
213
|
+
working-directory: DOCS_FOLDER
|
|
214
|
+
- run: npm run build
|
|
215
|
+
working-directory: DOCS_FOLDER
|
|
216
|
+
- uses: actions/upload-pages-artifact@v3
|
|
217
|
+
with:
|
|
218
|
+
path: DOCS_FOLDER/.vitepress/dist
|
|
219
|
+
|
|
220
|
+
deploy:
|
|
221
|
+
environment:
|
|
222
|
+
name: github-pages
|
|
223
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
224
|
+
needs: build
|
|
225
|
+
runs-on: ubuntu-latest
|
|
226
|
+
steps:
|
|
227
|
+
- uses: actions/deploy-pages@v4
|
|
228
|
+
id: deployment
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Fill `BRANCH` and `DOCS_FOLDER` from answers. For tag-based triggers replace `branches` with `tags: ['v*']`. For manual-only, remove the `push:` block.
|
|
232
|
+
|
|
233
|
+
**VitePress config updates:**
|
|
234
|
+
|
|
235
|
+
Read the existing config before editing. Make only these targeted changes:
|
|
236
|
+
|
|
237
|
+
1. Set `base`:
|
|
238
|
+
- Custom domain → `base: '/'`
|
|
239
|
+
- GitHub subdomain → `base: '/repo-name/'`
|
|
240
|
+
|
|
241
|
+
2. Add PostCSS override if not already present. This prevents VitePress from inheriting a PostCSS config from a parent project (e.g. a Next.js or Laravel root that uses Tailwind), which would cause the Actions build to fail with a "Cannot find module" error:
|
|
242
|
+
```js
|
|
243
|
+
vite: {
|
|
244
|
+
css: {
|
|
245
|
+
postcss: {},
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
```
|
|
249
|
+
Always add this — it is harmless when there is no parent PostCSS config and critical when there is.
|
|
250
|
+
|
|
251
|
+
**`DOCS_FOLDER/public/CNAME`** (custom domain only):
|
|
252
|
+
```
|
|
253
|
+
docs.yourdomain.com
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
**`.gitignore` check:** ensure `node_modules` and `.vitepress/cache` are present.
|
|
257
|
+
|
|
258
|
+
### Step 4 — Summary
|
|
259
|
+
|
|
260
|
+
List files written and give the user a next-steps checklist with the expected deploy URL.
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## Mode: generate
|
|
265
|
+
|
|
266
|
+
### Step 0 — Detect tech stack
|
|
267
|
+
|
|
268
|
+
Before asking any questions, scan the codebase root for stack markers. Use Glob and Grep — do not ask the user what their stack is until you've made an attempt to detect it yourself.
|
|
269
|
+
|
|
270
|
+
**WordPress indicators** — check for any of these:
|
|
271
|
+
```
|
|
272
|
+
wp-config.php
|
|
273
|
+
wp-content/
|
|
274
|
+
wp-includes/
|
|
275
|
+
functions.php (in a theme directory)
|
|
276
|
+
style.css with "Theme Name:" header
|
|
277
|
+
composer.json with "johnpbloch/wordpress" or "roots/wordpress"
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
**Node/Next indicators:**
|
|
281
|
+
```
|
|
282
|
+
next.config.js / next.config.ts / next.config.mjs
|
|
283
|
+
package.json with "next" dependency
|
|
284
|
+
.next/ directory
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
**Other Node indicators:** `package.json`, `vite.config.*`, `astro.config.*`, `nuxt.config.*`
|
|
288
|
+
|
|
289
|
+
**Static:** no package.json, no wp-config.php, only HTML/CSS/JS files.
|
|
290
|
+
|
|
291
|
+
After scanning, present your finding and confirm with the user:
|
|
292
|
+
|
|
293
|
+
- header: "Tech stack detected"
|
|
294
|
+
- question: "I detected this as a [DETECTED_STACK] project — is that right?"
|
|
295
|
+
- options:
|
|
296
|
+
- "Yes, that's correct"
|
|
297
|
+
- "No — let me tell you what it actually is"
|
|
298
|
+
|
|
299
|
+
If the user corrects you, accept their answer and proceed with the corrected stack.
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
**If WordPress is detected**, ask the following WP-specific questions before running the git root check:
|
|
304
|
+
|
|
305
|
+
**WP-Q1 — WordPress project type:**
|
|
306
|
+
- header: "WordPress setup"
|
|
307
|
+
- question: "What kind of WordPress project is this?"
|
|
308
|
+
- options:
|
|
309
|
+
- "Traditional theme (PHP templates, The Loop, etc.)"
|
|
310
|
+
- "Block theme (Full Site Editing / Gutenberg blocks)"
|
|
311
|
+
- "Headless WordPress (REST API or WPGraphQL feeding a front-end)"
|
|
312
|
+
- "Plugin (documenting a plugin, not a theme)"
|
|
313
|
+
- "Full site — theme + plugins together"
|
|
314
|
+
|
|
315
|
+
**WP-Q2 — Local dev environment** (plain text): Ask — "What URL is the WordPress site running on locally? (e.g. `http://localhost:8888`, `http://mysite.local`)"
|
|
316
|
+
|
|
317
|
+
**WP-Q3 — Page builder / block editor:**
|
|
318
|
+
- header: "Page builder"
|
|
319
|
+
- question: "Is this site built with a page builder or block toolkit?"
|
|
320
|
+
- options:
|
|
321
|
+
- "Standard Gutenberg / block editor only"
|
|
322
|
+
- "Elementor"
|
|
323
|
+
- "Divi"
|
|
324
|
+
- "ACF Blocks (Advanced Custom Fields)"
|
|
325
|
+
- "Other — I'll tell you"
|
|
326
|
+
- "No page builder — mostly PHP templates"
|
|
327
|
+
|
|
328
|
+
**WP-Q4 — Key plugins to document** (plain text): Ask — "Are there any plugins that are central to how the site works and should be included in the docs? (e.g. WooCommerce, ACF, Yoast — or 'none')"
|
|
329
|
+
|
|
330
|
+
**WP-Q5 — Headless front-end** (only if headless was selected in WP-Q1):
|
|
331
|
+
- header: "Front-end"
|
|
332
|
+
- question: "What is the headless front-end stack?"
|
|
333
|
+
- options:
|
|
334
|
+
- "Next.js"
|
|
335
|
+
- "Nuxt"
|
|
336
|
+
- "SvelteKit"
|
|
337
|
+
- "Astro"
|
|
338
|
+
- "Other — I'll tell you"
|
|
339
|
+
|
|
340
|
+
**WP-Q6 — Theme / plugin folder** (plain text): Scan `wp-content/themes/` and `wp-content/plugins/` for likely candidates first — present your best guess and let the user confirm or correct it. Ask — "Which theme or plugin should I focus on? (e.g. `wp-content/themes/my-theme` or `wp-content/plugins/my-plugin`)"
|
|
341
|
+
|
|
342
|
+
Once WP-Q6 is answered, the **focus path** is now the theme/plugin folder — not the WP install root. All subsequent analysis, docs placement, and git root detection use this path as the base.
|
|
343
|
+
|
|
344
|
+
Store all WP answers and carry them through the rest of generate mode. They affect codebase analysis (Step 2) and the doc structure proposal (Step 3).
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
#### Git root detection (all stacks)
|
|
349
|
+
|
|
350
|
+
**For WordPress:** run this check against the theme/plugin path confirmed in WP-Q6.
|
|
351
|
+
**For all other stacks:** run this check against the detected codebase root (current working directory unless corrected above).
|
|
352
|
+
|
|
353
|
+
Check for a `.git` directory at **that exact path**:
|
|
354
|
+
|
|
355
|
+
```bash
|
|
356
|
+
ls FOCUS_PATH/.git
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
Do not check parent folders — the `.git` must be in the specific folder being documented. For WordPress this means checking inside `wp-content/themes/my-theme/` or `wp-content/plugins/my-plugin/`, not in `wp-content/themes/`, `wp-content/`, or the WP install root.
|
|
360
|
+
|
|
361
|
+
**If `.git` is found at the focus path:** no question needed — the repo root and focus path are the same. Proceed.
|
|
362
|
+
|
|
363
|
+
**If `.git` is not found:** ask:
|
|
364
|
+
|
|
365
|
+
- header: "Git root"
|
|
366
|
+
- question: "I didn't find a `.git` folder at [FOCUS_PATH]. Where is the git root for this project?"
|
|
367
|
+
- options:
|
|
368
|
+
- "It's at the WP install root (the folder I was invoked from)"
|
|
369
|
+
- "It's somewhere else — I'll tell you the path"
|
|
370
|
+
- "There's no git repo yet"
|
|
371
|
+
|
|
372
|
+
Then ask as plain text — "Where inside the git repo should the docs folder and VitePress config live? (Default: `docs/` inside [FOCUS_PATH].)"
|
|
373
|
+
|
|
374
|
+
This matters because the GitHub Actions workflow file must go in `.github/workflows/` at the **git root**, even if the VitePress config and docs folder live deeper inside it (e.g. inside the theme or plugin subfolder).
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
### Step 1 — Gather project details
|
|
379
|
+
|
|
380
|
+
**Q1 — Doc type:**
|
|
381
|
+
- header: "Documentation type"
|
|
382
|
+
- question: "What type of docs should I generate?"
|
|
383
|
+
- options:
|
|
384
|
+
- "User-facing guides — how to use the app"
|
|
385
|
+
- "Developer reference — architecture, API, data layer"
|
|
386
|
+
- "Both"
|
|
387
|
+
|
|
388
|
+
**Q2 — App description** (plain text): Ask — "What is this app called, and what does it do in one sentence? (Used for headings and introductions.)"
|
|
389
|
+
|
|
390
|
+
**Q3 — Codebase root** (plain text): Ask — "Where is the codebase root? (Press enter to use the current directory.)" — **Skip for WordPress; already established by WP-Q6.**
|
|
391
|
+
|
|
392
|
+
**Q4 — Docs output** (plain text): Ask — "Where should docs be written? (Default: `docs/` inside [FOCUS_PATH].)" — For WordPress, default to `docs/` inside the theme/plugin folder from WP-Q6, not the WP install root.
|
|
393
|
+
|
|
394
|
+
**Q5 — Skip anything:**
|
|
395
|
+
- header: "Exclusions"
|
|
396
|
+
- question: "Anything to exclude from analysis? (node_modules, dist, .git, .next, coverage are always skipped.)"
|
|
397
|
+
- options:
|
|
398
|
+
- "No, defaults are fine"
|
|
399
|
+
- "Yes — I'll tell you what to skip"
|
|
400
|
+
|
|
401
|
+
If yes: ask as plain text — "Which folders or files should I skip?"
|
|
402
|
+
|
|
403
|
+
Wait for all answers.
|
|
404
|
+
|
|
405
|
+
### Step 2 — Analyze the codebase
|
|
406
|
+
|
|
407
|
+
Scan the codebase systematically. Build a mental map before writing anything. Look for:
|
|
408
|
+
|
|
409
|
+
**For user-facing docs:**
|
|
410
|
+
- App routes / pages (what screens exist)
|
|
411
|
+
- Key user flows (what can a user do)
|
|
412
|
+
- Forms and interactive features
|
|
413
|
+
- Settings and configuration surfaces
|
|
414
|
+
|
|
415
|
+
**For developer docs:**
|
|
416
|
+
- Entry points and routing structure
|
|
417
|
+
- Data layer (API routes, DB queries, service modules)
|
|
418
|
+
- State management
|
|
419
|
+
- Auth and middleware
|
|
420
|
+
- Reusable utilities and lib functions
|
|
421
|
+
- Environment variable requirements
|
|
422
|
+
|
|
423
|
+
Use Glob and Grep to find these. Do not read every file — read enough to understand what each area does.
|
|
424
|
+
|
|
425
|
+
### Step 3 — Plan the doc structure
|
|
426
|
+
|
|
427
|
+
Before writing any pages, output a proposed outline:
|
|
428
|
+
|
|
429
|
+
Present the proposed outline as plain text, then use AskUserQuestion to confirm:
|
|
430
|
+
|
|
431
|
+
- header: "Doc structure"
|
|
432
|
+
- question: "Does this outline look right?"
|
|
433
|
+
- options:
|
|
434
|
+
- "Looks good — start writing"
|
|
435
|
+
- "I want to make changes"
|
|
436
|
+
|
|
437
|
+
If changes: ask as plain text — "What would you like to add, remove, or rename?" Then re-present the updated outline and ask again.
|
|
438
|
+
|
|
439
|
+
### Step 4 — Write pages
|
|
440
|
+
|
|
441
|
+
Write pages one at a time. For each page:
|
|
442
|
+
|
|
443
|
+
1. Read the relevant source files
|
|
444
|
+
2. Write the markdown based on what you can confidently determine
|
|
445
|
+
3. Where information is unclear or missing, insert a gap comment and continue:
|
|
446
|
+
```md
|
|
447
|
+
<!-- GAP: What are the valid values for rotation frequency? -->
|
|
448
|
+
```
|
|
449
|
+
4. Where a screenshot would help understanding, insert a placeholder image and record it in the manifest:
|
|
450
|
+
```md
|
|
451
|
+

|
|
452
|
+
```
|
|
453
|
+
Then generate the placeholder PNG (see **Placeholder generation** below).
|
|
454
|
+
|
|
455
|
+
Do not stop to ask gap questions mid-page. Keep writing and accumulate all gaps.
|
|
456
|
+
|
|
457
|
+
### Step 5 — Gap review
|
|
458
|
+
|
|
459
|
+
After all pages are written, present all gaps in one batch:
|
|
460
|
+
|
|
461
|
+
```
|
|
462
|
+
I've written all X pages. I hit N gaps where I couldn't confidently determine
|
|
463
|
+
the correct information. Can you fill these in?
|
|
464
|
+
|
|
465
|
+
1. [guides/timer.md] What are the valid values for rotation frequency?
|
|
466
|
+
2. [developer/auth.md] Is the OAuth callback scoped to a single provider or does it support multiple?
|
|
467
|
+
...
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
Go back and fill in the answers the user provides.
|
|
471
|
+
|
|
472
|
+
### Step 6 — Update VitePress config sidebar
|
|
473
|
+
|
|
474
|
+
Read the existing `.vitepress/config.mjs` (or `.ts`) and update the `sidebar` and `nav` to include all newly generated pages. Edit the config in place — do not rewrite unrelated sections.
|
|
475
|
+
|
|
476
|
+
### Step 7 — Write the manifest
|
|
477
|
+
|
|
478
|
+
Write `.vitepress/docs-manifest.json` capturing all pages, their source file mappings, image placeholder status, and a `syncHash` (SHA of source file contents at generation time — use a simple string hash if needed).
|
|
479
|
+
|
|
480
|
+
Add `docs-manifest.json` to `.vitepress/` entry in `.gitignore`.
|
|
481
|
+
|
|
482
|
+
### Step 8 — Summary
|
|
483
|
+
|
|
484
|
+
```
|
|
485
|
+
Generated X pages (Y user-facing, Z developer).
|
|
486
|
+
Created N placeholder screenshots — run /vitepress:docs screenshot when ready to capture real images.
|
|
487
|
+
Filled M of P gaps — X remaining gap comments left in files for manual review.
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
---
|
|
491
|
+
|
|
492
|
+
## Placeholder generation
|
|
493
|
+
|
|
494
|
+
When a page needs a screenshot, generate a 1200×630 grey PNG at the specified path before moving on.
|
|
495
|
+
|
|
496
|
+
Try in order:
|
|
497
|
+
|
|
498
|
+
**Option A — ImageMagick:**
|
|
499
|
+
```bash
|
|
500
|
+
convert -size 1200x630 xc:'#888888' path/to/placeholder.png
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
**Option B — Node.js (no external deps):**
|
|
504
|
+
|
|
505
|
+
Write this to `/tmp/make-placeholder.mjs`, run it, then delete it:
|
|
506
|
+
|
|
507
|
+
```js
|
|
508
|
+
import zlib from 'zlib';
|
|
509
|
+
import { writeFileSync, mkdirSync } from 'fs';
|
|
510
|
+
import { dirname } from 'path';
|
|
511
|
+
|
|
512
|
+
const W = 1200, H = 630;
|
|
513
|
+
const OUTPUT = 'FILL_IN_OUTPUT_PATH';
|
|
514
|
+
|
|
515
|
+
const scanlines = [];
|
|
516
|
+
for (let y = 0; y < H; y++) {
|
|
517
|
+
const row = Buffer.alloc(1 + W * 3);
|
|
518
|
+
row[0] = 0; // filter type: None
|
|
519
|
+
row.fill(0x88, 1); // grey RGB pixels
|
|
520
|
+
scanlines.push(row);
|
|
521
|
+
}
|
|
522
|
+
const raw = Buffer.concat(scanlines);
|
|
523
|
+
const compressed = zlib.deflateSync(raw, { level: 9 });
|
|
524
|
+
|
|
525
|
+
const crcTable = new Uint32Array(256);
|
|
526
|
+
for (let i = 0; i < 256; i++) {
|
|
527
|
+
let c = i;
|
|
528
|
+
for (let j = 0; j < 8; j++) c = (c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1);
|
|
529
|
+
crcTable[i] = c;
|
|
530
|
+
}
|
|
531
|
+
function crc32(buf) {
|
|
532
|
+
let c = 0xFFFFFFFF;
|
|
533
|
+
for (const b of buf) c = crcTable[(c ^ b) & 0xFF] ^ (c >>> 8);
|
|
534
|
+
return (c ^ 0xFFFFFFFF) >>> 0;
|
|
535
|
+
}
|
|
536
|
+
function makeChunk(type, data) {
|
|
537
|
+
const t = Buffer.from(type, 'ascii');
|
|
538
|
+
const len = Buffer.alloc(4); len.writeUInt32BE(data.length);
|
|
539
|
+
const crcVal = Buffer.alloc(4); crcVal.writeUInt32BE(crc32(Buffer.concat([t, data])));
|
|
540
|
+
return Buffer.concat([len, t, data, crcVal]);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
const sig = Buffer.from([137,80,78,71,13,10,26,10]);
|
|
544
|
+
const ihdr = Buffer.alloc(13);
|
|
545
|
+
ihdr.writeUInt32BE(W, 0); ihdr.writeUInt32BE(H, 4);
|
|
546
|
+
ihdr[8] = 8; ihdr[9] = 2; // 8-bit RGB
|
|
547
|
+
|
|
548
|
+
mkdirSync(dirname(OUTPUT), { recursive: true });
|
|
549
|
+
writeFileSync(OUTPUT, Buffer.concat([
|
|
550
|
+
sig,
|
|
551
|
+
makeChunk('IHDR', ihdr),
|
|
552
|
+
makeChunk('IDAT', compressed),
|
|
553
|
+
makeChunk('IEND', Buffer.alloc(0))
|
|
554
|
+
]));
|
|
555
|
+
console.log('Created', OUTPUT);
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
If both options fail, tell the user and skip the placeholder for that image — leave the `` reference in the markdown so it renders as a broken image, making it obvious during preview.
|
|
559
|
+
|
|
560
|
+
---
|
|
561
|
+
|
|
562
|
+
## Mode: screenshot
|
|
563
|
+
|
|
564
|
+
### Step 1 — Gather capture details
|
|
565
|
+
|
|
566
|
+
**Q1 — Source:**
|
|
567
|
+
- header: "Capture source"
|
|
568
|
+
- question: "Where should I capture screenshots from?"
|
|
569
|
+
- options:
|
|
570
|
+
- "Local dev server — I'll capture from a running server"
|
|
571
|
+
- "Deployed URL — give me the base URL"
|
|
572
|
+
|
|
573
|
+
If deployed: ask as plain text — "What is the base URL? (e.g. `https://docs.myapp.com`)"
|
|
574
|
+
|
|
575
|
+
**Q2 — Stack type** (only if local):
|
|
576
|
+
- header: "Project type"
|
|
577
|
+
- question: "What type of project is this?"
|
|
578
|
+
- options:
|
|
579
|
+
- "Node/npm — you can optionally start the server for me"
|
|
580
|
+
- "WordPress, PHP, or other non-Node stack"
|
|
581
|
+
- "Static files"
|
|
582
|
+
|
|
583
|
+
**Q3 — Server status** (only if local Node):
|
|
584
|
+
- header: "Dev server"
|
|
585
|
+
- question: "Is the dev server already running?"
|
|
586
|
+
- options:
|
|
587
|
+
- "Already running"
|
|
588
|
+
- "Not running — please start it for me"
|
|
589
|
+
|
|
590
|
+
If starting: ask as plain text — "What command starts it? And what URL/port does it run on?"
|
|
591
|
+
|
|
592
|
+
**Q4 — Authentication:**
|
|
593
|
+
- header: "Login required?"
|
|
594
|
+
- question: "Does reaching the screens that need screenshots require login?"
|
|
595
|
+
- options:
|
|
596
|
+
- "No — all target screens are public"
|
|
597
|
+
- "Yes — I'll provide credentials"
|
|
598
|
+
|
|
599
|
+
If yes: ask as plain text — "Please provide the username and password. ⚠️ Credentials are used only to drive the browser in this session — they will NEVER be written to any file, the manifest, or anywhere outside this conversation."
|
|
600
|
+
|
|
601
|
+
**Q5 — Scope:**
|
|
602
|
+
- header: "Capture scope"
|
|
603
|
+
- question: "Which screenshots should I capture?"
|
|
604
|
+
- options:
|
|
605
|
+
- "All placeholders from the manifest"
|
|
606
|
+
- "Specific pages only — I'll tell you which"
|
|
607
|
+
|
|
608
|
+
If specific: ask as plain text — "Which pages or screenshots?"
|
|
609
|
+
|
|
610
|
+
Wait for all answers.
|
|
611
|
+
|
|
612
|
+
### Step 2 — Find all placeholders
|
|
613
|
+
|
|
614
|
+
Read `.vitepress/docs-manifest.json`. Filter to all images where `"placeholder": true`. Group by the doc page they belong to.
|
|
615
|
+
|
|
616
|
+
If no manifest exists, scan all markdown files in the docs folder for images referencing `public/screenshots/` — treat all of them as needing capture.
|
|
617
|
+
|
|
618
|
+
### Step 3 — Capture screenshots
|
|
619
|
+
|
|
620
|
+
For each placeholder, refer to the manifest's `captureUrl` and `captureNote` fields to understand what state to capture.
|
|
621
|
+
|
|
622
|
+
Use Playwright via Homebrew for all captures:
|
|
623
|
+
```js
|
|
624
|
+
import { chromium } from '/opt/homebrew/lib/node_modules/playwright/index.mjs';
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
**⛔ Credential rule — NEVER write credentials to any file.** Do not write them to the capture script, a `.env` file, the manifest, a temp file, or anywhere else. Use them only as inline values passed directly to `page.fill()` calls in the script held in memory. Delete the script immediately after running it (`rm /tmp/screenshot.mjs`). If Playwright needs credentials in a reusable way, use a saved storage state file — but prompt the user for credentials fresh each time rather than caching them.
|
|
628
|
+
|
|
629
|
+
**For pages requiring auth:** if credentials were provided, drive a login flow before navigating to the target URL. If no credentials were given, skip auth-gated pages and list them in the final summary so the user can run screenshot mode again with credentials.
|
|
630
|
+
|
|
631
|
+
**Standard capture script template:**
|
|
632
|
+
```js
|
|
633
|
+
import { chromium } from '/opt/homebrew/lib/node_modules/playwright/index.mjs';
|
|
634
|
+
const browser = await chromium.launch({ channel: 'chrome', args: ['--no-sandbox'] });
|
|
635
|
+
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
|
|
636
|
+
await page.goto('BASE_URL + captureUrl', { waitUntil: 'load', timeout: 60000 });
|
|
637
|
+
await page.waitForTimeout(2000); // settle time for SPAs
|
|
638
|
+
await page.screenshot({ path: 'OUTPUT_PATH', fullPage: false });
|
|
639
|
+
await browser.close();
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
Adjust settle time based on stack type. For WordPress/non-SPA sites use 500ms. For Next.js/SPA use 2000–4000ms.
|
|
643
|
+
|
|
644
|
+
After each capture, display the image inline with the Read tool so the user can verify it before moving on.
|
|
645
|
+
|
|
646
|
+
### Step 4 — Rewrite prose around captured images
|
|
647
|
+
|
|
648
|
+
After capturing an image, go back to the doc page that references it and rewrite the paragraph or section surrounding that image. The placeholder was written with generic framing — now that you can see the actual screenshot, make the description specific and accurate.
|
|
649
|
+
|
|
650
|
+
Read the screenshot with the Read tool to inform the rewrite.
|
|
651
|
+
|
|
652
|
+
### Step 5 — Update manifest
|
|
653
|
+
|
|
654
|
+
For each successfully captured image, update `"placeholder": false` in the manifest and record the capture timestamp.
|
|
655
|
+
|
|
656
|
+
### Step 6 — Summary
|
|
657
|
+
|
|
658
|
+
```
|
|
659
|
+
Captured X of Y screenshots.
|
|
660
|
+
Skipped: [list any skipped with reason]
|
|
661
|
+
|
|
662
|
+
Rewrote prose in X doc pages.
|
|
663
|
+
|
|
664
|
+
Run /vitepress:docs sync after future code changes to keep docs current.
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
---
|
|
668
|
+
|
|
669
|
+
## Mode: sync
|
|
670
|
+
|
|
671
|
+
### Step 1 — Gather sync preferences
|
|
672
|
+
|
|
673
|
+
**Q1 — Drift detection method:**
|
|
674
|
+
- header: "Sync method"
|
|
675
|
+
- question: "How should I check for drift?"
|
|
676
|
+
- options:
|
|
677
|
+
- "Git diff — compare code changes since last sync (fast)"
|
|
678
|
+
- "Full re-analysis — re-read source files and compare to docs (thorough)"
|
|
679
|
+
- "Both — git diff first, then deep re-analysis on flagged pages"
|
|
680
|
+
|
|
681
|
+
**Q2 — Update style:**
|
|
682
|
+
- header: "Update approach"
|
|
683
|
+
- question: "How should I handle pages that need updating?"
|
|
684
|
+
- options:
|
|
685
|
+
- "Update all automatically, then show me a summary"
|
|
686
|
+
- "Show me each changed page and confirm before updating"
|
|
687
|
+
|
|
688
|
+
Wait for answers.
|
|
689
|
+
|
|
690
|
+
### Step 2 — Detect drift
|
|
691
|
+
|
|
692
|
+
**Git diff approach:**
|
|
693
|
+
```bash
|
|
694
|
+
git diff --name-only LAST_SYNC_COMMIT HEAD
|
|
695
|
+
```
|
|
696
|
+
Get `LAST_SYNC_COMMIT` from the manifest's `generated` timestamp — find the nearest commit at or before that time with `git log --before="TIMESTAMP" -1 --format="%H"`.
|
|
697
|
+
|
|
698
|
+
Cross-reference changed files against the manifest's `sources` arrays to find which doc pages are affected.
|
|
699
|
+
|
|
700
|
+
**Full re-analysis approach:**
|
|
701
|
+
For each page in the manifest, re-read the source files listed in `sources`. Compute a hash of their current contents and compare to `syncHash`. Flag any mismatch as drifted.
|
|
702
|
+
|
|
703
|
+
**Both:** run git diff first for speed, then re-analyze only the flagged pages for depth.
|
|
704
|
+
|
|
705
|
+
### Step 3 — Report drift before changing anything
|
|
706
|
+
|
|
707
|
+
```
|
|
708
|
+
Found N pages with drift:
|
|
709
|
+
|
|
710
|
+
guides/timer.md — src/state/timer.js changed (interval logic updated)
|
|
711
|
+
developer/auth.md — src/proxy.js changed (new middleware added)
|
|
712
|
+
developer/api/users.md — src/app/api/users/route.js changed (new endpoint)
|
|
713
|
+
|
|
714
|
+
Images that may need recapture:
|
|
715
|
+
public/screenshots/timer-main.png — source page changed
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
If the user chose "confirm before each", use AskUserQuestion per page:
|
|
719
|
+
- header: "[filename]"
|
|
720
|
+
- question: "[source file] changed — [brief description of what changed]. Update this page?"
|
|
721
|
+
- options:
|
|
722
|
+
- "Yes — update it"
|
|
723
|
+
- "Skip this one"
|
|
724
|
+
- "Update all remaining without asking"
|
|
725
|
+
|
|
726
|
+
Otherwise update all and summarize at the end.
|
|
727
|
+
|
|
728
|
+
### Step 4 — Update affected pages
|
|
729
|
+
|
|
730
|
+
For each drifted page:
|
|
731
|
+
1. Re-read the source files
|
|
732
|
+
2. Update the relevant sections in the doc
|
|
733
|
+
3. Add a `<!-- GAP: ... -->` comment if something is now unclear
|
|
734
|
+
4. If images on the page likely show outdated UI, mark them as `"placeholder": true` again in the manifest and note that they need recapture
|
|
735
|
+
|
|
736
|
+
Do not touch pages that have not drifted.
|
|
737
|
+
|
|
738
|
+
### Step 5 — Update manifest
|
|
739
|
+
|
|
740
|
+
Update `lastSynced` and `syncHash` for all updated pages. Update the top-level `generated` timestamp.
|
|
741
|
+
|
|
742
|
+
### Step 6 — Summary
|
|
743
|
+
|
|
744
|
+
```
|
|
745
|
+
Updated X of N drifted pages.
|
|
746
|
+
X gap comments added for manual review.
|
|
747
|
+
Y screenshots flagged for recapture — run /vitepress:docs screenshot when ready.
|
|
748
|
+
```
|
|
749
|
+
|
|
750
|
+
---
|
|
751
|
+
|
|
752
|
+
## Notes
|
|
753
|
+
|
|
754
|
+
- Never write the GitHub Actions workflow inside the docs folder — it goes in `.github/workflows/` at the repo root.
|
|
755
|
+
- Always read an existing config before editing it — never rewrite the whole file.
|
|
756
|
+
- For non-Node stacks in screenshot mode (WordPress, PHP, static): do not attempt to start a server. Just ask for the URL.
|
|
757
|
+
- `workflow_dispatch` should always be present in the workflow so manual deploys are possible.
|
|
758
|
+
- Do not use `peaceiris/actions-gh-pages` — use `actions/upload-pages-artifact` + `actions/deploy-pages`.
|
|
759
|
+
- The manifest is intentionally not committed. Each environment generates its own. Don't suggest committing it.
|