@abstractdata/starlight-theme 0.3.1 → 0.3.3

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.
@@ -0,0 +1,893 @@
1
+ # Abstract Data Documentation Theme — Reference
2
+
3
+ <!--
4
+ Auto-generated from the SKILL.md sources under .claude/skills/.
5
+ Edit those and run `bun run sync-skills` to regenerate.
6
+ Do not hand-edit this file — changes will be overwritten.
7
+ -->
8
+
9
+ > **Note:** GitHub Copilot applies these instructions globally to every Chat
10
+ > interaction in this repo. The workflows below are procedural — Copilot can
11
+ > guide the user through them but cannot natively run interactive prompts.
12
+ > Use Claude Code or Cursor for fully automated execution.
13
+
14
+ ## Available workflows
15
+
16
+ - **abstract-data-setup** — detection + configuration + generator wiring
17
+ - **abstract-data-docs-author** — read source code, write narrative docs
18
+
19
+ When a user request matches one of these, follow the relevant workflow below.
20
+
21
+ ---
22
+
23
+ ## Setup workflow (`abstract-data-setup`)
24
+
25
+ ### When this applies
26
+
27
+ Set up the Abstract Data Documentation Theme (built on Astro Starlight) for a project. Detect source code across stacks (Python, TypeScript, Next.js, TanStack, OpenAPI, Prisma, Drizzle), audit docstring coverage for Python, sniff docstring style (Google/NumPy/Sphinx), detect or pick a logo asset, ask configuration questions (modules/entry points, motion, credit, version), wire up config files (scripts/python-autodoc.json, scripts/ts-autodoc.json, astro.config.mjs sidebar + plugin options, package.json scripts), and optionally install a docstring-coverage pre-commit hook. Use when the user says "set up docs", "configure docs", "wire up Python autodoc", "wire up TypeScript autodoc", "scan my project for docs", "set up Abstract Data docs", "add API reference", "audit docstrings", or similar phrases inside a docs project that uses @abstractdata/starlight-theme (the npm package name; product is the Abstract Data Documentation Theme).
28
+
29
+ ### Procedure
30
+
31
+ # Abstract Data Documentation Theme — Setup
32
+
33
+ Bootstrap the Abstract Data Documentation Theme — the branded docs system Abstract Data uses across client projects, built on Astro Starlight and shipped as the npm package `@abstractdata/starlight-theme`. Round 2 covers Python and TypeScript autodoc with full automation, plus detection-and-recipe handling for Next.js, TanStack Router, OpenAPI, Prisma, and Drizzle.
34
+
35
+ ## When to invoke
36
+
37
+ Run this skill when the user says "set up docs", "configure docs", "wire up Python autodoc", "wire up TypeScript autodoc", "scan my project for docs", "audit docstrings", or similar inside a project that has `@abstractdata/starlight-theme` in its `package.json`. If the cwd doesn't have that dep, stop and point them at `bun create @abstractdata/docs`.
38
+
39
+ ## Workflow
40
+
41
+ Use interactive prompts for every choice — never assume.
42
+
43
+ ### Phase 1 — Confirm context
44
+
45
+ Read `package.json`. Verify `@abstractdata/starlight-theme` is in deps; verify `astro.config.mjs` and `src/content/docs/` exist. Stop with a clear message if any check fails. Don't ask the user to confirm — just announce findings and move on.
46
+
47
+ ### Phase 2 — Locate the source project(s)
48
+
49
+ Ask via interactive prompt: where does the source project live?
50
+ - "This directory" — docs ARE the source (rare)
51
+ - "Parent directory (..)" — docs sit inside the source repo
52
+ - "Sibling directory" — separate repos at the same level
53
+ - "Custom path" — prompt for it
54
+
55
+ Validate the path exists. Reprompt on invalid.
56
+
57
+ ### Phase 3 — Detect all stack signals
58
+
59
+ In the source path, scan for these signals in parallel. Report what you find before asking any per-stack questions.
60
+
61
+ **Python:**
62
+ - `pyproject.toml`, `setup.py`, `requirements.txt`, `Pipfile`
63
+ - `src/<pkg>/__init__.py` or `<pkg>/__init__.py`
64
+
65
+ **TypeScript library:**
66
+ - `tsconfig.json` AND `package.json` with `main`/`exports`/`types` fields
67
+ - `src/index.ts` or similar entry point
68
+
69
+ **Next.js:**
70
+ - `next.config.{js,mjs,ts}`
71
+ - `app/` directory (App Router) or `pages/` directory (Pages Router)
72
+ - `next` in dependencies
73
+
74
+ **TanStack Router / Start:**
75
+ - `@tanstack/react-router`, `@tanstack/react-start`, or `@tanstack/start` in dependencies
76
+ - `src/routes/` directory
77
+ - `src/routeTree.gen.ts` (generated route tree)
78
+
79
+ **OpenAPI:**
80
+ - `openapi.yaml`, `openapi.json`, `swagger.yaml`, `swagger.json`
81
+ - Files matching `*.openapi.{yaml,json}`
82
+
83
+ **Prisma:**
84
+ - `prisma/schema.prisma`
85
+
86
+ **Drizzle:**
87
+ - `drizzle.config.{ts,js}`
88
+ - Files in a `schema/` directory exporting `pgTable` / `mysqlTable` / `sqliteTable`
89
+
90
+ **Logo asset (in the docs project, not the source):**
91
+ - `src/assets/*.{png,svg,jpg,jpeg,webp}` — anything that looks like a logo (`logo.*`, `*-logo.*`, `brand.*`)
92
+
93
+ Display the detection summary in a table:
94
+
95
+ ```
96
+ Stack Detected Action
97
+ Python yes will offer Python autodoc
98
+ TypeScript yes will offer TypeScript autodoc
99
+ Next.js no —
100
+ TanStack yes recipe-only (no auto-config)
101
+ OpenAPI no —
102
+ Prisma yes recipe-only (no auto-config)
103
+ Drizzle no —
104
+ Logo 1 file will confirm choice
105
+ ```
106
+
107
+ If no source signals at all → exit politely.
108
+
109
+ ### Phase 4 — Python: audit + style (only if Python detected)
110
+
111
+ #### 4a — Audit docstring coverage
112
+
113
+ Preferred tool: `interrogate` (`pipx install pydoc-markdown` first if not installed). Fall back to a Python AST one-liner. Categorize per-module:
114
+ - **≥ 80%** green
115
+ - **50-79%** yellow
116
+ - **< 50%** red
117
+
118
+ Show the table; don't editorialize.
119
+
120
+ #### 4b — Detect docstring style
121
+
122
+ Sample 10–20 docstrings, count distinctive markers (Google `Args:`/`Returns:`, NumPy `Parameters\n----`, Sphinx `:param x:`). Pick the leader if it has ≥60% of markers; otherwise call it "mixed."
123
+
124
+ ### Phase 5 — TypeScript: entry-point detection (only if TS library detected)
125
+
126
+ Read `package.json` `main`/`exports`/`types` fields. Walk `src/` for `index.ts` files. Build a candidate list of entry points (one per public module surface).
127
+
128
+ Common shapes:
129
+ - Single entry: `src/index.ts` → one entry point
130
+ - Multi-entry: `package.json` `exports` lists multiple → one entry per exported subpath
131
+ - Monorepo: each package has its own entry
132
+
133
+ #### 5a — Audit TSDoc coverage
134
+
135
+ Mirror Phase 4a for TypeScript. Run TypeDoc in **validation-only** mode against the chosen entry points:
136
+
137
+ ```bash
138
+ bunx typedoc \
139
+ --plugin typedoc-plugin-markdown \
140
+ --validation.notDocumented \
141
+ --treatValidationWarningsAsErrors false \
142
+ --emit none \
143
+ <entryPoints>
144
+ ```
145
+
146
+ Parse the resulting warnings — TypeDoc emits one line per undocumented symbol with `[warning]` prefix. Group by source file and report per-file coverage as a percentage of public exports that have at least one TSDoc block. Use the same color thresholds as the Python audit:
147
+
148
+ - **≥ 80%** green
149
+ - **50–79%** yellow
150
+ - **< 50%** red
151
+
152
+ If TypeDoc isn't installed yet, fall back to a quick AST sniff: count files in `src/` with `/**` blocks vs total exported declarations. Coarser, but no install required.
153
+
154
+ Show the table; don't editorialize. The result feeds Phase 12 (pre-commit hook offer).
155
+
156
+ ### Phase 6 — Logo detection
157
+
158
+ Scan `src/assets/**/*.{png,svg,jpg,jpeg,webp}` recursively. Build candidate categories:
159
+
160
+ - **Topbar mark candidates**: filename matches `(logo|brand|mark|icon)\.(svg|png|webp)$` (case-insensitive). SVG preferred.
161
+ - **Light/dark pair**: filenames contain `light` or `dark` and otherwise match logo conventions (e.g. `logo-light.svg` + `logo-dark.svg`). Starlight supports both via `logo.light` + `logo.dark`.
162
+ - **Hero candidates**: any image larger than ~80KB or with `hero` / `splash` in the name — usually a bigger variant for the splash page.
163
+ - **Other images**: no logo conventions; treat as miscellaneous.
164
+
165
+ For each candidate, gather:
166
+ - File size in bytes
167
+ - Format (extension)
168
+ - Whether the filename indicates light/dark intent
169
+
170
+ Display a 4-7 line summary table:
171
+
172
+ ```
173
+ File Size Format Suggested role
174
+ src/assets/logo.svg 12 KB SVG topbar mark (recommended)
175
+ src/assets/logo-light.svg 12 KB SVG topbar (light mode)
176
+ src/assets/logo-dark.svg 12 KB SVG topbar (dark mode)
177
+ src/assets/hero-mark.png 145 KB PNG splash hero image
178
+ ```
179
+
180
+ Branch on what you found:
181
+
182
+ - **One mark + light/dark pair** → propose using the pair (Starlight handles auto-switching by `data-theme`)
183
+ - **One mark, no pair** → propose using it for both modes
184
+ - **Multiple marks, no clear winner** → multiselect: which for topbar? Which for hero? `multiSelect: true` interactive prompt.
185
+ - **Zero candidates** → ask: "I don't see a logo in `src/assets/`. Drop one in (SVG preferred, ~256x256 minimum), tell me where it is, or skip for now and configure manually later."
186
+
187
+ Format guidance to surface alongside the prompt:
188
+ - SVG renders crispest at any size; preferred for both topbar and hero.
189
+ - PNG works fine; 256×256 minimum for the topbar (Astro will downscale via the asset pipeline).
190
+ - JPG is fine for hero photos but bad for marks (compression artifacts on edges).
191
+ - Files > 200KB will bloat first-paint; suggest optimizing or using a smaller source.
192
+
193
+ ### Phase 7 — Recommend documentation surfaces
194
+
195
+ For each detected stack, ask whether to wire it up. Stack questions are per-stack, not lumped together.
196
+
197
+ #### Python (if detected)
198
+
199
+ Interactive prompt with up to 4 options:
200
+ - "Top-level package only" (Recommended)
201
+ - "All green-coverage modules" (≥80%)
202
+ - "Specific submodules I'll pick" → multiselect
203
+ - "Everything" (warn about red-coverage modules)
204
+
205
+ #### TypeScript (if detected)
206
+
207
+ Interactive prompt:
208
+ - "Single root entry point (src/index.ts)" (Recommended for libs with one public API)
209
+ - "All paths in package.json `exports`" (Recommended for multi-entry libs)
210
+ - "I'll pick specific entry points" → free-form
211
+
212
+ #### Other detected stacks (Next.js, TanStack, OpenAPI, Prisma, Drizzle)
213
+
214
+ Don't auto-configure. Tell the user the recipe and let them follow up:
215
+
216
+ - **Next.js**: "I detected Next.js. There isn't a standard one-shot route documenter, but you can write a small build script that walks `app/` or `pages/` and emits a `routes.md`. Want me to draft one?"
217
+ - **TanStack Router**: "I detected TanStack Router. Generate a route map by importing `routeTree.gen.ts` and walking it in a build script. Want a starter?"
218
+ - **OpenAPI**: "I detected an OpenAPI spec. Recommend `@astrojs/starlight-openapi` for the cleanest integration. Want me to install it and wire it up?"
219
+ - **Prisma**: "I detected a Prisma schema. Try `prisma-markdown` for schema docs. Want me to add it to dev deps and wire a script?"
220
+ - **Drizzle**: "I detected a Drizzle config. There isn't a mature schema-to-markdown tool yet — most teams write their own walker over the schema exports. Want me to draft a starter?"
221
+
222
+ If the user says yes to any of these, fall back to free-form work — these aren't covered by the main config files.
223
+
224
+ ### Phase 7.5 — Optional Starlight plugins
225
+
226
+ Three plugins are cheap, high-value DX wins for most docs sites. Don't auto-install — surface them as an interactive multi-select prompt with sensible per-project defaults. Skip the prompt entirely if the user has already declined them in a previous run (look for a `.abstractdata-setup.json` marker or for the plugins already being absent + the import already pruned).
227
+
228
+ For each, you decide the default based on signals you've already gathered:
229
+
230
+ - **`starlight-llms-txt`** — emits an `llms.txt` summary at `/llms.txt` (per [llmstxt.org](https://llmstxt.org)) so AI crawlers can ingest the site as plain Markdown rather than parsing rendered HTML. **Default: ON.** No real downside.
231
+ - **`@expressive-code/plugin-package-managers`** *(code-block plugin, not a Starlight plugin)* — auto-renders npm/pnpm/yarn/bun tabs from a single `npm install foo` code block. **Default: ON if** the README, quickstart, or any docs page contains `bun add`, `npm install`, `pnpm add`, or `yarn add`. **Default: OFF otherwise.**
232
+ - **`starlight-image-zoom`** — click-to-zoom on images. **Default: ON if** the docs project has more than ~5 images under `src/assets/` or `src/content/docs/**/*.{png,jpg,svg,webp}` (a heuristic for "this site uses screenshots"). **Default: OFF otherwise.**
233
+
234
+ Show the user the three options with their suggested defaults pre-checked, and let them un-check / toggle. For each accepted plugin:
235
+
236
+ 1. Add to `dependencies` in `package.json`.
237
+ 2. Add the import to `astro.config.mjs`.
238
+ 3. Wire the plugin entry into the `plugins` array (or `expressiveCode.plugins` for the package-managers plugin).
239
+ 4. Tell the user the bun command to run: `bun add <plugin-name>`.
240
+
241
+ Show the resulting diff before writing. Don't run `bun add` yourself — the user does it.
242
+
243
+ ### Phase 8 — Gather brand + contributor-loop configuration
244
+
245
+ Read existing `astro.config.mjs`. If `motion`, `credit`, `version`, `lastUpdated`, and `editLink` are already set and the user hasn't asked to change them, skip this phase.
246
+
247
+ Otherwise, batch into one prompt:
248
+
249
+ 1. Motion: full | calm (Recommended)
250
+ 2. Credit: auto | hide
251
+ 3. Version chip: show with version string | omit
252
+ 4. **Last-updated timestamps** (Recommended on): renders a "Last updated" footer on each page using `git log`. Requires a git checkout at build time. Set `lastUpdated: true` if yes, omit otherwise.
253
+ 5. **"Edit on GitHub" link** (Recommended on for OSS): renders an Edit link in the footer pointing at the source. Needs a repo URL — derive it from `package.json` `repository.url` or the source-project remote (`git remote get-url origin`). If found, write `editLink: { baseUrl: '<repo>/edit/<default-branch>/' }`; if not, leave the commented-out scaffold in place and tell the user how to fill it in.
254
+
255
+ Both items 4 and 5 are cheap, high-impact contributor-loop wins per the Astro/Starlight best-practices guide — default to enabling them unless the user opts out.
256
+
257
+ ### Phase 9 — Configure logo
258
+
259
+ Two surfaces need updating:
260
+
261
+ **a) Topbar logo — `astro.config.mjs` `starlight.logo`**
262
+
263
+ First, look at what Phase 6 detected. Three branches:
264
+
265
+ 1. **Phase 6 found a light/dark pair** (e.g. `logo-light.svg` + `logo-dark.svg`) → use the `{ light, dark }` form. Starlight auto-switches based on `data-theme`. Don't ask which to use as the default — both render at the right time.
266
+
267
+ 2. **Phase 6 found a single mark** → use the single-`src` form. Optionally suggest the user create a dark variant later if the mark is colored in a way that won't survive a dark background.
268
+
269
+ 3. **Phase 6 found nothing** → leave the existing commented `// logo: { ... }` block in `astro.config.mjs` and tell the user where to drop a logo file.
270
+
271
+ Then ask one final question (skip if you already know): does the logo include the project name as text/wordmark?
272
+
273
+ - **Yes** (logo + wordmark in one image) → `replacesTitle: true` — Starlight hides the text title and shows just the logo.
274
+ - **No** (just a mark/icon, no text) → `replacesTitle: false` — Starlight shows the mark beside the text title.
275
+
276
+ Then write the config:
277
+
278
+ ```js
279
+ // Single mark
280
+ logo: {
281
+ src: './src/assets/logo.svg',
282
+ replacesTitle: false,
283
+ }
284
+
285
+ // Light/dark pair (preferred when Phase 6 found both files)
286
+ logo: {
287
+ light: './src/assets/logo-light.svg',
288
+ dark: './src/assets/logo-dark.svg',
289
+ replacesTitle: true,
290
+ }
291
+ ```
292
+
293
+ If a `logo:` block already exists in the config, edit it in place — don't add a duplicate. If `logo:` is in a comment or commented-out, uncomment and update.
294
+
295
+ **b) Splash hero image — `src/content/docs/index.mdx` `hero.image.file`**
296
+
297
+ Default Starlight splash hero supports a separate (usually larger) image. Check the existing `index.mdx` frontmatter:
298
+
299
+ ```yaml
300
+ hero:
301
+ image:
302
+ file: ../../assets/hero-mark.png
303
+ alt: <project name>
304
+ ```
305
+
306
+ If a hero candidate was identified in Phase 6, update this path. If the same logo serves both roles, point both at the same file. If the user wants to use the branded `<Hero>` MDX component (richer than the frontmatter hero), point them at the migration guide section that covers it.
307
+
308
+ **Verify after writing:**
309
+
310
+ After both surfaces are updated, encourage the user to run `bun dev` and visually confirm the logo renders correctly in both light and dark modes (toggle from the topbar). Common issues:
311
+ - Logo is white-on-white in light mode → need a dark variant or use the `light`/`dark` pair pattern
312
+ - Logo is way too small/large → adjust the source dimensions or add CSS overrides via `customCss`
313
+ - File path is wrong → relative paths in `astro.config.mjs` are relative to the project root, not the file itself
314
+
315
+ ### Phase 10 — Write configs
316
+
317
+ Show diffs in your reasoning before writing.
318
+
319
+ #### a) `scripts/python-autodoc.json` (if Python wired up)
320
+
321
+ ```json
322
+ {
323
+ "searchPath": "<relative path>",
324
+ "modules": [...],
325
+ "outputDir": "src/content/docs/api"
326
+ }
327
+ ```
328
+
329
+ #### b) `scripts/ts-autodoc.json` (if TypeScript wired up)
330
+
331
+ ```json
332
+ {
333
+ "entryPoints": [...],
334
+ "tsconfig": "<relative path>",
335
+ "outputDir": "src/content/docs/api/ts"
336
+ }
337
+ ```
338
+
339
+ #### c) `astro.config.mjs`
340
+
341
+ Multi-edit:
342
+
343
+ 1. Sidebar — ensure entries exist for each enabled generator:
344
+ - `{ label: 'Python API', autogenerate: { directory: 'api' } }` — if Python
345
+ - `{ label: 'TypeScript API', autogenerate: { directory: 'api/ts' } }` — if TS
346
+ 2. Plugin call — update `motion`/`credit`/`version` from Phase 8.
347
+ 3. Logo — update `logo.src` from Phase 9.
348
+
349
+ Don't duplicate sidebar entries. Don't add a second `abstractData(...)` plugin call.
350
+
351
+ #### d) `package.json`
352
+
353
+ Add scripts conditionally:
354
+ - `"docs:python": "node scripts/build-python-docs.mjs"` — if Python wired up
355
+ - `"docs:ts": "node scripts/build-ts-docs.mjs"` — if TS wired up
356
+
357
+ Update the `build` script to chain them:
358
+ ```json
359
+ "build": "bun run docs:python && bun run docs:ts && astro check && astro build"
360
+ ```
361
+ (Skip the chains for stacks not enabled.)
362
+
363
+ #### e) Required dev deps for TS
364
+
365
+ If TS wired up, add to dev deps:
366
+ ```bash
367
+ bun add -d typedoc typedoc-plugin-markdown
368
+ ```
369
+
370
+ Tell the user to run this; don't run it yourself.
371
+
372
+ #### f) Tailor `src/content/docs/quickstart.md` to the detected stack
373
+
374
+ The scaffolded `quickstart.md` ships with both Python and TypeScript autodoc subsections wrapped in HTML comment markers:
375
+
376
+ ```html
377
+ <!-- abstract-data-setup:python-autodoc -->
378
+ …Python instructions…
379
+ <!-- /abstract-data-setup:python-autodoc -->
380
+
381
+ <!-- abstract-data-setup:ts-autodoc -->
382
+ …TypeScript instructions…
383
+ <!-- /abstract-data-setup:ts-autodoc -->
384
+ ```
385
+
386
+ After you've finalized the stack(s) for this project (Phase 7), edit `quickstart.md` to remove the irrelevant block:
387
+
388
+ - **Python only** → strip everything between `<!-- abstract-data-setup:ts-autodoc -->` and `<!-- /abstract-data-setup:ts-autodoc -->` (inclusive).
389
+ - **TypeScript only** → strip the Python block similarly.
390
+ - **Both** → leave both blocks; remove only the comment markers themselves so the published page is clean.
391
+ - **Neither** (no autodoc wired up) → strip both blocks plus the "## Add API reference" heading and intro paragraph.
392
+
393
+ This is idempotent: re-runs of the skill check whether the markers still exist before pruning. If the user has already removed the markers (or hand-edited the file), leave it alone — never re-inject content into a customized quickstart.
394
+
395
+ ### Phase 11 — Optionally run generators
396
+
397
+ Per enabled generator, ask: "Generate now? [Yes / No]"
398
+
399
+ - Python → `bun run docs:python`
400
+ - TypeScript → `bun run docs:ts`
401
+
402
+ Pass through any tool-not-installed errors verbatim.
403
+
404
+ ### Phase 11.5 — Offer docs-author dispatch (if generators ran)
405
+
406
+ After generation, scan `src/content/docs/api/` for **thin pages** — auto-generated files whose body (after frontmatter and the auto-rendered H1) is fewer than ~200 bytes, OR pages that consist of just signatures with no descriptive prose. These are the "what the heck is this?" pages — the source's docstrings are too sparse for the mechanical autodoc to produce useful output.
407
+
408
+ If any thin pages are found, surface this to the user:
409
+
410
+ > "I noticed N of the generated API pages are sparse — your source's docstrings are thin. The companion skill `abstract-data-docs-author` reads the source code itself and writes narrative prose to enrich those pages (module overview, usage example from tests, related-modules cross-references). Want me to invoke it now?"
411
+
412
+ If yes: hand off to the `abstract-data-docs-author` skill (Claude Code: load the skill at `.claude/skills/abstract-data-docs-author/SKILL.md`; Cursor: refer to `.cursor/rules/abstract-data-docs-author.mdc`). Pass along the project profile and detected stack info so the docs-author skill doesn't have to re-discover.
413
+
414
+ If no thin pages found, skip this phase silently. Don't push the docs-author skill on a project that doesn't need it.
415
+
416
+ ### Phase 11.7 — Versioned API reference (only if source has 2+ tags)
417
+
418
+ Run `git -C <source-repo> tag --list 'v*' | wc -l` (or equivalent) on the **source** repo (not the docs project). If the result is < 2, skip this phase silently.
419
+
420
+ Otherwise, surface the choice:
421
+
422
+ > "I see your source has N tagged releases. Versioned API reference is supported four ways. Which do you want?"
423
+
424
+ Multi-choice prompt:
425
+
426
+ 1. **Source-driven (Recommended for API-only versioning).** Adds a `versions` array to `python-autodoc.json` / `ts-autodoc.json`. Each rebuild checks out the source repo at each tag (via `git worktree add`), regenerates the API reference per tag into `<outputDir>/<safeTag>/`, and aliases the default version at the un-versioned URL. Cheap, composable, no branches to maintain. The bundled `<VersionPicker>` component renders a topbar dropdown.
427
+ 2. **`starlight-versions` plugin** — opinionated, archives the entire site (guides + API). Pick this only if guides drift between versions too. Pre-1.0; expect rough edges.
428
+ 3. **Branch-per-version** — each major version is a git branch deployed to a subdomain, the main branch's host (Vercel/CF) rewrites `/v2/*` → that subdomain. Best when teams already maintain per-version branches.
429
+ 4. **Single version (no versioning).** Default if the user is unsure. Easy to add versioning later.
430
+
431
+ Default the recommendation to **option 1** when the project has Python or TypeScript autodoc wired (Phases 4/5 ran). Default to **option 2** when the docs project has substantial hand-written guides and the user expects them to differ per version.
432
+
433
+ If the user picks option 1:
434
+
435
+ a. Surface up to the 5 most recent tags as candidates. Ask which to publish — let them deselect noisy point releases. Mark the most recent as `default: true` unless the user picks a different one (e.g. an LTS tag).
436
+
437
+ b. Write the `versions` array into the appropriate autodoc JSON config. Example shape:
438
+
439
+ ```jsonc
440
+ {
441
+ "searchPath": "../../auditkit/src",
442
+ "modules": [...],
443
+ "outputDir": "src/content/docs/api",
444
+ "versions": [
445
+ { "tag": "v0.4.0", "label": "0.4 (latest)", "default": true },
446
+ { "tag": "v0.3.2", "label": "0.3" },
447
+ { "tag": "v0.2.0", "label": "0.2 (legacy)" }
448
+ ]
449
+ }
450
+ ```
451
+
452
+ c. **No override needed.** The `abstractData()` plugin already overrides `SocialIcons` to render `<VersionPicker>` next to the existing chip and social links. As soon as the autodoc orchestrator emits per-version pages with `version:` frontmatter, the picker appears in the topbar automatically — no user-side wiring, no `versions` prop to maintain. The picker walks the docs collection at build time, dedupes by tag, picks up the `versionDefault: true` flag for the default version. The autodoc JSON is the single source of truth.
453
+
454
+ If the autodoc base path differs from the default `/api` (e.g. `outputDir: "src/content/docs/api/ts"` for TypeScript-only sites), pass `apiBase` to the plugin so the picker constructs the right URLs:
455
+
456
+ ```js
457
+ starlight({
458
+ plugins: [
459
+ abstractData({
460
+ motion: 'calm',
461
+ apiBase: '/api/ts',
462
+ }),
463
+ ],
464
+ })
465
+ ```
466
+
467
+ d. **Verify the docs project's content schema accepts the version frontmatter fields.** Read `src/content.config.ts` and confirm the `docsSchema.extend` zod object includes:
468
+
469
+ ```ts
470
+ version: z.string().optional(),
471
+ versionLabel: z.string().optional(),
472
+ versionDefault: z.boolean().optional(),
473
+ ```
474
+
475
+ The `create-docs` template scaffold already ships with these. For projects upgraded via `bunx abstract-data-install-skills`, you'll need to add them — without these optional fields, Zod will reject the autodoc-emitted frontmatter and the build fails with `InvalidContentEntryDataError`.
476
+
477
+ e. Tell the user to run `bun run docs:python` (or `docs:ts`) to populate the per-version directories. The script handles the worktrees automatically.
478
+
479
+ f. **Optional curated override.** If the user wants to hide pre-release tags or reorder the dropdown, they can wire a user-level override of `SocialIcons` that imports `<VersionPicker>` and passes an explicit `versions` prop — that bypasses auto-discovery. Don't recommend this by default; auto-discovery keeps the autodoc JSON as the single source of truth.
480
+
481
+ If the user picks option 2 (`starlight-versions`):
482
+
483
+ - Install: `bun add starlight-versions`
484
+ - Add to `astro.config.mjs` plugins array
485
+ - Run the plugin's CLI to archive the current state
486
+ - Configure `starlight-versions.versions` to match the user's version list
487
+ - Note: this conflicts with option 1 — pick *one*.
488
+
489
+ If the user picks option 3 (branch-per-version):
490
+
491
+ - This is mostly a deployment-platform concern. Suggest the Knip pattern (Vercel rewrites) and link them at [webpro.nl/scraps/versioned-docs-with-starlight-and-vercel](https://webpro.nl/scraps/versioned-docs-with-starlight-and-vercel). Don't try to wire this yourself — too platform-specific.
492
+
493
+ If the user picks option 4 (none): skip silently, leave `versions` field absent from the autodoc config.
494
+
495
+ **Site version vs. API version** — they're different. The `version` chip in the theme's plugin call (e.g. `version: 'v1.0.0'`) is the *site's own* marketing version. The `versions` array in autodoc configs is the *documented API's* versions. A site might be at `v1.2.0` while documenting API `v0.4.0` — that's normal, don't conflate them.
496
+
497
+ ### Phase 12 — Optional pre-commit hook (per-stack)
498
+
499
+ Fires only if Phase 4a (Python) or Phase 5a (TypeScript) found modules below the 80% coverage threshold.
500
+
501
+ **Python branch** — if Phase 4a found yellow/red modules, offer:
502
+
503
+ - Tool: `interrogate` (lightweight, no project changes beyond the hook entry).
504
+ - Config: append a `[tool.interrogate]` table to `pyproject.toml` setting `fail-under = 80`, `exclude = ["tests"]`, etc.
505
+ - `.pre-commit-config.yaml`: add the `econchick/interrogate` repo with the chosen revision.
506
+
507
+ **TypeScript branch** — if Phase 5a found yellow/red entry points, offer either:
508
+
509
+ - **Local script hook (preferred)**: a one-liner `pre-commit` config that runs the docs build script with `--validation.notDocumented` and fails the commit if any new warnings appear. No extra dependencies beyond `typedoc` (already a dev dep).
510
+
511
+ ```yaml
512
+ # .pre-commit-config.yaml fragment
513
+ - repo: local
514
+ hooks:
515
+ - id: tsdoc-coverage
516
+ name: TSDoc coverage
517
+ entry: bunx typedoc --plugin typedoc-plugin-markdown --validation.notDocumented --treatValidationWarningsAsErrors true --emit none
518
+ language: system
519
+ types: [ts]
520
+ pass_filenames: false
521
+ ```
522
+
523
+ - **`tsdoc-coverage` package** (if the user prefers a dedicated tool): npm install `tsdoc-coverage` as a dev dep and wire it as the hook entry instead. Threshold defaults to 80% to match the Python side.
524
+
525
+ In both stacks: show the user the exact config diff before writing. The pre-commit hook lives in the **source repo**, not the docs repo — extra caution since it's a different project.
526
+
527
+ ### Phase 12.5 — Confirm `starlight-links-validator` is wired
528
+
529
+ The template ships with `starlight-links-validator` already installed and registered as a Starlight plugin. Verify both during this phase:
530
+
531
+ 1. Read the docs project's `package.json` — confirm `starlight-links-validator` is in `dependencies` (or `devDependencies`).
532
+ 2. Read `astro.config.mjs` — confirm the plugin is referenced inside `plugins: [...]` on the `starlight(...)` call.
533
+ 3. Read the docs project's CI workflow (`.github/workflows/*.yml` or equivalent) — confirm `bun run build` (or `astro build`) runs on PRs. The links validator runs as part of the build, so a build-on-PR workflow is enough; no separate step needed.
534
+
535
+ If any of those three pieces are missing, surface the gap and offer to add it. The plugin's failure mode is "fail the build on any broken internal link" — exactly what you want for CI.
536
+
537
+ For migrating projects (running `bunx abstract-data-install-skills` rather than scaffolding via `bun create @abstractdata/docs`), the plugin will not be installed automatically. Tell the user:
538
+
539
+ ```bash
540
+ bun add starlight-links-validator
541
+ ```
542
+
543
+ then add `starlightLinksValidator()` to the `plugins` array in `astro.config.mjs`. Show the diff.
544
+
545
+ ### Phase 13 — Summary
546
+
547
+ 6–10 line markdown summary covering: detected stacks, what got wired up per stack, logo update, mode (motion/credit/version), files updated, generated counts (if Phase 11 ran), next steps.
548
+
549
+ ## Idempotency
550
+
551
+ - Don't duplicate sidebar entries — check before adding.
552
+ - Don't append to `modules`/`entryPoints` arrays — replace cleanly.
553
+ - Don't add a second `abstractData(...)` call — update the existing one.
554
+ - Don't overwrite content under `src/content/docs/` (only `api/` pages, regenerated by the script).
555
+ - Don't add a second logo line — replace.
556
+
557
+ ## Out of scope (this round)
558
+
559
+ - TanStack route detection auto-config (recipe-only)
560
+ - Next.js route map auto-config (recipe-only)
561
+ - Prisma schema-doc auto-config (recipe-only)
562
+ - Drizzle schema-doc auto-config (recipe-only — no mature tooling)
563
+ - README/CHANGELOG/ADR auto-import into the sidebar
564
+
565
+ ## Files this skill reads / writes
566
+
567
+ **Reads (docs project):** `package.json`, `astro.config.mjs`, `src/assets/`.
568
+ **Reads (source project):** `pyproject.toml`, `setup.py`, source tree, `tsconfig.json`, `next.config.*`, dependency manifests, schema files, OpenAPI specs.
569
+
570
+ **Writes (docs project):** `scripts/python-autodoc.json`, `scripts/ts-autodoc.json`, `astro.config.mjs` (edits), `package.json` (scripts only).
571
+
572
+ **Writes (source project, with explicit pre-commit consent only):** `.pre-commit-config.yaml`, `pyproject.toml` (dev deps section), `requirements-dev.txt`, or `package.json` (TS dev deps for `tsdoc-coverage` if chosen).
573
+
574
+ ## Notes for the agent
575
+
576
+ - Be conservative with edits. Show diffs in your reasoning before writing.
577
+ - Phase 12 modifies a different repo than the docs project — extra caution.
578
+ - For TypeScript: TypeDoc requires `typescript` AND `typedoc` AND `typedoc-plugin-markdown`. If any are missing, the orchestrator will tell the user via the script output. Don't try to install them yourself unless the user explicitly asks.
579
+ - For OpenAPI: prefer `@astrojs/starlight-openapi` (mature plugin) over building from scratch.
580
+ - Keep conversation tight: detection in 1–3 sentences, audit table 5–8 lines, questions one round at a time, summary 6–10 lines.
581
+
582
+ ---
583
+
584
+ ## Docs Author workflow (`abstract-data-docs-author`)
585
+
586
+ ### When this applies
587
+
588
+ Read a project's source code and write substantive narrative documentation alongside the auto-generated API reference. Use when the user says "flesh out the docs", "write better docs", "the API pages are too thin", "enrich the docs", "author the docs", "explain this codebase", "fill out the documentation", "the autodoc pages need more context", or similar phrases inside a project that uses @abstractdata/starlight-theme. Typically invoked by abstract-data-setup after generators have produced bare-signatures pages, but can also be invoked standalone when a user says their docs are too sparse.
589
+
590
+ ### Procedure
591
+
592
+ # Abstract Data Documentation Theme — Docs Author
593
+
594
+ Read a project's source code and *write* documentation. Complements the auto-generated mechanical API reference (signatures, type hints, structure produced by pydoc-markdown / TypeDoc) by adding narrative prose, motivation, examples, and cross-references — the things mechanical autodoc can never produce well, especially when the source's docstrings are thin.
595
+
596
+ This skill **enriches**, never **replaces**, the mechanical autodoc output. Always preserve the auto-generated signatures section. Layer prose above it.
597
+
598
+ ## When to invoke
599
+
600
+ Run this skill when:
601
+
602
+ - The user says "flesh out the docs", "write better docs", "the API pages are too thin", "enrich the docs", or similar.
603
+ - The `abstract-data-setup` skill has just finished generating mechanical autodoc pages that read as terse/empty.
604
+ - The user has run `bun run docs:python` (or `docs:ts`) and asks "now make these readable."
605
+
606
+ If the cwd doesn't have `@abstractdata/starlight-theme` in `package.json` deps, stop and point the user at `bun create @abstractdata/docs`. If `src/content/docs/api/` doesn't exist or is empty, run the setup skill first (or tell the user to).
607
+
608
+ ## Operating principles
609
+
610
+ 1. **Enrich, don't replace.** Always preserve the existing auto-generated content (signatures, type hints, structure). Layer your prose *before* it (as a "Module overview" preface) or *after* it (as "Examples", "Related", "See also" sections). Never delete the mechanical scaffold.
611
+
612
+ 2. **Be honest about uncertainty.** If you're inferring intent from a function name without good context, say so. Phrases like "appears to handle…" are better than confident wrong claims.
613
+
614
+ 3. **Read tests for examples.** If the project has tests, they're the most reliable source of usage patterns. Quote test code (lightly cleaned up) for examples whenever possible — it's true by construction.
615
+
616
+ 4. **Token-budget discipline.** Don't try to read the whole codebase in one pass. Loop module by module, smallest first. Each iteration's context is just one module's source + that module's existing autodoc page. For 30-module projects, that's 30 small conversations, not one giant one.
617
+
618
+ 5. **Idempotent.** If a page already has an "Overview" preface (you wrote one before), refresh it rather than appending a second. Look for marker comments or distinctive heading patterns.
619
+
620
+ 6. **Don't hallucinate features.** If the source code doesn't do something, don't write that it does. The reader will trust your prose; lying is worse than terse pages.
621
+
622
+ ## Workflow
623
+
624
+ ### Phase 1 — Discover the project
625
+
626
+ Read these files (top-down, bail if absent):
627
+
628
+ - `package.json` — confirm `@abstractdata/starlight-theme` dep
629
+ - `astro.config.mjs` — confirm Starlight project
630
+ - `scripts/python-autodoc.json` and/or `scripts/ts-autodoc.json` — confirm autodoc has been wired
631
+ - `src/content/docs/api/` — list existing auto-generated pages
632
+ - The source project's `README.md`, `CHANGELOG.md`, `CONTRIBUTING.md` if present
633
+ - Any `docs/adr/` directory or `ARCHITECTURE.md` — important context
634
+
635
+ Note the source project root (from `searchPath` in `python-autodoc.json` or `entryPoints` in `ts-autodoc.json`). All source reads go relative to that.
636
+
637
+ ### Phase 1.5 — Inventory existing prose
638
+
639
+ Before profiling or writing anything new, build a manifest of prose that **already exists in the source project**. Most projects have a substantial amount of usable narrative scattered across `README.md`, `CHANGELOG.md`, ADRs, and existing docstrings — rewriting it from scratch wastes tokens and risks contradicting the source's own voice.
640
+
641
+ For each candidate file, read it once and produce a line-item inventory:
642
+
643
+ - **`README.md`** — for each `##` section, note: heading, ~10-word summary, candidate destination. Common reuse targets:
644
+ - "Quick start" / "Installation" / "Getting started" → `src/content/docs/quickstart.md`
645
+ - "Features" / "What it does" / "Why use this" → `src/content/docs/index.mdx` hero subtitle + `concepts.md` intro
646
+ - "Architecture" / "How it works" / "Design" → `src/content/docs/concepts.md` body
647
+ - "Configuration" / "Options" → a guide or the relevant module overview
648
+ - "Examples" / "Usage" → split across module Example blocks
649
+ - "Contributing" → leave in repo, link from docs sidebar
650
+ - **`CHANGELOG.md`** — note the most recent 1–3 entries. Don't import wholesale; harvest noteworthy feature additions for the concepts page ("Recent additions") or for module overviews ("Added in v0.4 to address …").
651
+ - **`docs/adr/*.md`** or **`ARCHITECTURE.md`** — for each ADR, note: title, decision, the 1–2 sentence rationale. ADRs are *gold* for the `concepts.md` page — they explain *why* the architecture looks the way it does. Quote the rationale, link out to the ADR for full context.
652
+ - **`CONTRIBUTING.md`** — usually stays in repo; mine for any "How to add a new X" sections that should become how-to guides under `src/content/docs/guides/`.
653
+ - **Existing module docstrings** — even if the autodoc page reads as thin, the source `.py` / `.ts` may have a leading module docstring with usable framing. Note which modules have them (Phase 6 will reuse the wording verbatim where appropriate).
654
+
655
+ Output the inventory as a brief plan to memory before moving on:
656
+
657
+ ```
658
+ README sections worth lifting:
659
+ - "## Quick start" → quickstart.md (verbatim, light edit)
660
+ - "## How it works" → concepts.md intro (paraphrase)
661
+ - "## Why httpx?" → core/http_scan module overview (paraphrase + link to ADR-007)
662
+
663
+ CHANGELOG highlights:
664
+ - v0.4: Added BaseHttpScanModule (link to ADR-007)
665
+ - v0.3: AI integration via Ollama (concepts.md "Optional integrations" section)
666
+
667
+ ADRs:
668
+ - ADR-001 "Use httpx not requests" → quote in concepts.md
669
+ - ADR-007 "BaseHttpScanModule" → quote in core/http_scan overview
670
+
671
+ Modules with usable existing docstrings: auditkit.core, auditkit.transport.curl_impersonate
672
+ ```
673
+
674
+ When you reach Phases 5 and 6, **prefer lifting existing prose** (with light cleanup and proper attribution if it's a paraphrase from an ADR) over fabricating new wording. Always offer the user a side-by-side: "README says X, ADR says Y, here's the merged version — keep, edit, or rewrite?"
675
+
676
+ If the source project has *no* README beyond a one-liner and *no* ADRs, say so up front — the user should know the docs-author run will need to invent more, and that voice will be yours rather than the project's.
677
+
678
+ ### Phase 2 — Profile the project
679
+
680
+ Spend 1–2 conversation turns building a project profile. Not a deep code read yet — orientation only:
681
+
682
+ - Read `README.md` (one document) — extract the elevator pitch, the core problem the project solves, the primary user.
683
+ - Read top-level CLI entry points or top-level `index.ts` exports — understand the public surface.
684
+ - Read `pyproject.toml` / `package.json` description fields and keywords.
685
+ - Skim test directory names (don't read full tests yet) — understand testing organization.
686
+
687
+ Write a **3–5 sentence project profile** to memory. This profile is the lens for every module-level write that follows. Examples:
688
+
689
+ > "auditkit is a security-audit CLI that scans websites for misconfigurations across 9 categories. It's modular: each scan module inherits from `ScanModule` and runs async via httpx. Heavy use of pydantic for data validation. AI integration via Ollama is optional."
690
+
691
+ Reuse this voice in every module overview you write.
692
+
693
+ ### Phase 3 — Build the module map
694
+
695
+ For each existing page in `src/content/docs/api/`:
696
+
697
+ 1. Read the page (current state — may be terse/empty).
698
+ 2. Read the corresponding source file from the source project.
699
+ 3. Note: file size, public symbols, imports (which other modules does it depend on?), test coverage (is there a `test_<module>.py`?).
700
+
701
+ Build an in-memory map: `{ pageName, sourcePath, publicSymbols, dependencies, hasTests, currentPageBytes }`. Smallest pages first — they're cheapest to enrich and the user gets early wins.
702
+
703
+ ### Phase 4 — Find usage examples
704
+
705
+ For each module with tests, locate 1–2 representative test cases. Look for:
706
+
707
+ - Tests with descriptive names (`test_login_browser_with_valid_credentials`)
708
+ - Tests that exercise the public API directly (not internal helpers)
709
+ - Short tests (under ~20 lines) — easier to inline as examples
710
+
711
+ Note these locations. You'll quote them in Phase 6.
712
+
713
+ ### Phase 5 — Write narrative pages
714
+
715
+ Before per-module work, write the high-leverage **narrative pages** that don't exist yet. Check first — if `src/content/docs/concepts.md` (or similar) already has user-written content, leave it alone. Otherwise, draft:
716
+
717
+ - **`src/content/docs/concepts.md`** — architecture overview. 300–600 words. Source the structure from imports + ADRs + your project profile. Cover: core abstractions, data flow, key types, extension points.
718
+
719
+ - **`src/content/docs/guides/getting-started.md`** *(if not present)* — distilled from README quickstart. Should be hands-on, end with the user having run a thing.
720
+
721
+ - **`src/content/docs/guides/<workflow>.md`** — for each repeated test pattern, write a how-to. Examples: "Adding a new scan module", "Configuring authentication", "Writing custom callbacks". One workflow per file.
722
+
723
+ Don't generate every possible guide — pick the 2–3 most valuable based on the project profile. Quality over quantity.
724
+
725
+ After writing, update `astro.config.mjs` sidebar to add a "Concepts" / "Guides" group if not present.
726
+
727
+ ### Phase 6 — Module-by-module enrichment loop
728
+
729
+ For each module page in your map, smallest-first:
730
+
731
+ 1. **Read the source file** (the actual `.py` / `.ts` file, not just docstrings). Understand what it does.
732
+ 2. **Detect existing enrichment.** If the page already has a `<!-- abstract-data-docs-author:overview -->` comment marker, you wrote a preface before — refresh it instead of duplicating.
733
+ 3. **Write a "Module overview" preface** (150–300 words) covering:
734
+ - **What this module is** — one-sentence description tied to the project profile
735
+ - **Why it exists** — what role does it play in the larger system
736
+ - **When to use it** — typical entry point or trigger
737
+ - **Key types or functions** — a 3–5 bullet list pointing at the most important public symbols
738
+ 4. **Add an "Example" section** quoting a test case (cleaned up if needed). Wrap in a code block with the right language.
739
+ 5. **Add a "Related" footer** linking 2–3 sibling modules (from the imports map). Format: `- [`module.name`](/api/module_name/)` — one-line description.
740
+ 6. **Inject** the preface above the existing auto-generated content. Keep the example and related sections after.
741
+ 7. **Save** with the marker comments so the next run is idempotent:
742
+
743
+ ```markdown
744
+ <!-- abstract-data-docs-author:overview -->
745
+ [your preface]
746
+ <!-- /abstract-data-docs-author:overview -->
747
+
748
+ [existing auto-generated content untouched]
749
+
750
+ <!-- abstract-data-docs-author:example -->
751
+ [example section]
752
+ <!-- /abstract-data-docs-author:example -->
753
+
754
+ <!-- abstract-data-docs-author:related -->
755
+ [related links]
756
+ <!-- /abstract-data-docs-author:related -->
757
+ ```
758
+
759
+ After each module, **stop and confirm with the user** before continuing. Show the diff. Let them approve, edit, or reject. This is mandatory — never bulk-apply prose without per-module review.
760
+
761
+ ### Phase 7 — Cross-reference pass
762
+
763
+ After all module pages enriched (or as many as the user opted into), do one final pass to cross-reference:
764
+
765
+ - Inside any page, if you mention another module by name, link it: ``` `auditkit.config.AuditConfig` ``` → `[\`auditkit.config.AuditConfig\`](/api/auditkit_config/#auditconfig-objects)`.
766
+ - Add a "Used by" reverse-lookup section to high-leverage modules — "this is imported by X, Y, Z".
767
+
768
+ This is the polish pass. Skip if the user is fatigued.
769
+
770
+ ### Phase 8 — Summary
771
+
772
+ Write a 6–10 line markdown summary covering:
773
+
774
+ - Modules enriched / skipped
775
+ - Narrative pages written
776
+ - Total prose added (rough word count)
777
+ - Any modules where you got stuck or recommended manual follow-up
778
+ - Suggested next step (e.g., "Run `bun dev` and walk the new pages")
779
+
780
+ ## Templates
781
+
782
+ Use these as starting points, not rigid forms.
783
+
784
+ ### Module overview template (Python)
785
+
786
+ ```markdown
787
+ **What this is:** [One-sentence description in the project's voice. E.g., "OAuth lifecycle helpers for jre-vidget's YouTube integration."]
788
+
789
+ **Why it exists:** [What role does it play. E.g., "Isolates browser-based auth from the CLI surface — no Rich, no video logic, no terminal state."]
790
+
791
+ **When to use it:** [Typical entry point. E.g., "Call `login_browser()` once during onboarding to mint a refresh token. Subsequent runs use `get_credentials()` to refresh on demand."]
792
+
793
+ **Key surfaces:**
794
+ - `login_browser(client_id, client_secret)` — interactive OAuth flow, returns `AuthConfig`
795
+ - `get_credentials(auth)` — refreshes on demand, returns `google.oauth2.credentials.Credentials`
796
+ - `AuthError` — raised when credentials are missing or unrefreshable
797
+ ```
798
+
799
+ ### Class/function preface template
800
+
801
+ For pages that document a single class or function (rare with pydoc-markdown's per-module output but happens), use:
802
+
803
+ ```markdown
804
+ **Purpose:** [why this exists]
805
+
806
+ **Key behavior:** [what it does, 2-3 sentences]
807
+
808
+ **Common usage:**
809
+ ```python
810
+ [short example]
811
+ ```
812
+ ```
813
+
814
+ ### Example block template
815
+
816
+ ```markdown
817
+ ## Example
818
+
819
+ [1-line context: "Logging in for the first time:"]
820
+
821
+ ```python
822
+ [10-15 lines of cleaned test code or synthesized usage]
823
+ ```
824
+
825
+ [1-line outcome: "After this, `auth.refresh_token` is set and persisted via `write_config_safely`."]
826
+ ```
827
+
828
+ ## Where dynamic `<head>` / metadata belongs
829
+
830
+ If during the Phase 5 narrative-page work you find yourself wanting to inject something dynamic into `<head>` — JSON-LD structured data (`Article`, `BreadcrumbList`, `SoftwareSourceCode`), per-page Open Graph image URLs, breadcrumb meta tags, or any computed-from-frontmatter `<meta>` tag — **the right layer is route middleware, not a component override.**
831
+
832
+ Starlight ≥ 0.32 lets you mutate `Astro.locals.starlightRoute.head` from a function exported by a file referenced as `routeMiddleware:` in `astro.config.mjs`. This is much cleaner than overriding the `<Head>` component because it composes with other plugins, doesn't break when Starlight bumps versions, and lets you read the current page's frontmatter before deciding what to inject.
833
+
834
+ Sketch:
835
+
836
+ ```ts
837
+ // src/routeData.ts
838
+ import { defineRouteMiddleware } from '@astrojs/starlight/route-data';
839
+
840
+ export const onRequest = defineRouteMiddleware((context) => {
841
+ const route = context.locals.starlightRoute;
842
+ // route.entry.data is the frontmatter; route.headings is the TOC tree.
843
+ route.head.push({
844
+ tag: 'script',
845
+ attrs: { type: 'application/ld+json' },
846
+ content: JSON.stringify({
847
+ '@context': 'https://schema.org',
848
+ '@type': 'Article',
849
+ headline: route.entry.data.title,
850
+ description: route.entry.data.description,
851
+ }),
852
+ });
853
+ });
854
+ ```
855
+
856
+ ```js
857
+ // astro.config.mjs
858
+ starlight({
859
+ routeMiddleware: './src/routeData.ts',
860
+ // …
861
+ })
862
+ ```
863
+
864
+ If the user explicitly wants any of these features (JSON-LD, dynamic OG images, breadcrumb meta), point them at this file as the place to wire it. Don't override the `<Head>` component just to inject computed meta — that's an anti-pattern documented in the Starlight migration notes for ≥ 0.33.
865
+
866
+ ## What this skill does NOT do
867
+
868
+ - **Doesn't rewrite source code.** That's a different operation. If the user wants to add docstrings to the source, that's `abstract-data-setup`'s Phase 4 territory (audit + suggest enrichment), not this skill's.
869
+ - **Doesn't generate API reference from scratch.** That's pydoc-markdown / TypeDoc's job. This skill *layers on top of* that output.
870
+ - **Doesn't guess at private symbols.** Only document the public API surface. Helper functions with leading underscores are skipped.
871
+ - **Doesn't auto-commit.** Writes files, leaves git up to the user.
872
+
873
+ ## Files this skill reads
874
+
875
+ - The docs project's `package.json`, `astro.config.mjs`, `scripts/python-autodoc.json` / `scripts/ts-autodoc.json`, existing `src/content/docs/api/*.md`.
876
+ - The source project's `README.md`, source files (`.py` / `.ts`), test files, ADRs, `pyproject.toml`/`package.json`.
877
+
878
+ ## Files this skill writes
879
+
880
+ - `src/content/docs/api/*.md` — enriched (preface + example + related sections injected around existing autodoc).
881
+ - `src/content/docs/concepts.md` — narrative architecture overview (only if not already user-authored).
882
+ - `src/content/docs/guides/*.md` — per-workflow how-to (only if not already user-authored).
883
+ - `astro.config.mjs` — sidebar updates to add new groups for Concepts / Guides.
884
+
885
+ ## Notes for the agent
886
+
887
+ - **Never bulk-apply.** Always confirm per-module before writing.
888
+ - **Token discipline.** Read one module's source per loop iteration. Don't try to hold the whole codebase in context.
889
+ - **Preserve markers.** The HTML comment markers (`<!-- abstract-data-docs-author:overview -->`) are how the next run finds and refreshes existing prose. Don't strip them.
890
+ - **When stuck:** if you can't form a coherent preface for a module (the source is too thin or too obscure), say so and skip. Don't write nonsense.
891
+ - **The mechanical autodoc is the floor.** Your job is to raise the ceiling.
892
+
893
+ ---