@abstractdata/starlight-theme 0.3.2 → 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.
- package/bin/install-skills.js +72 -4
- package/package.json +5 -4
- package/scripts/build-python-docs.mjs +385 -0
- package/scripts/build-ts-docs.mjs +349 -0
- package/scripts/python-autodoc.json +10 -0
- package/scripts/ts-autodoc.json +10 -0
- package/skills/claude/CLAUDE.md +46 -0
- package/skills/claude/abstract-data-docs-author/SKILL.md +305 -0
- package/skills/claude/abstract-data-setup/SKILL.md +441 -147
- package/skills/cursor/abstract-data-docs-author.mdc +311 -0
- package/skills/cursor/abstract-data-setup.mdc +441 -147
- package/skills/cursor/welcome.mdc +29 -0
- package/skills/github/copilot-instructions.md +773 -155
- package/src/components/SocialIcons.astro +17 -2
- package/src/components/VersionPicker.astro +238 -0
- package/src/index.ts +17 -1
- package/src/styles/hud.css +222 -210
- package/src/styles/theme.css +444 -432
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* build-ts-docs.mjs
|
|
4
|
+
*
|
|
5
|
+
* Generates Starlight-compatible Markdown API reference from a
|
|
6
|
+
* TypeScript project's source via TypeDoc + typedoc-plugin-markdown.
|
|
7
|
+
*
|
|
8
|
+
* Reads `scripts/ts-autodoc.json` for configuration:
|
|
9
|
+
* - entryPoints: array of TS entry files (e.g. ["../../my-lib/src/index.ts"])
|
|
10
|
+
* - tsconfig: path to the project's tsconfig.json
|
|
11
|
+
* - outputDir: where the generated .md pages land
|
|
12
|
+
* - githubPages: pass through to TypeDoc
|
|
13
|
+
* - skipErrorChecking: pass through to TypeDoc
|
|
14
|
+
* - repoUrl: (optional) base URL for "View on GitHub" footer
|
|
15
|
+
* - repoBranch: (optional) default 'main'
|
|
16
|
+
* - versions: (optional) array of { tag, label, default } for versioned docs.
|
|
17
|
+
* When present, each tag triggers an independent build
|
|
18
|
+
* from a `git worktree` checkout into <outputDir>/<safeTag>/.
|
|
19
|
+
* The default version is also aliased at the un-versioned
|
|
20
|
+
* URL so existing links keep resolving.
|
|
21
|
+
*
|
|
22
|
+
* For each module, TypeDoc emits a markdown file. The orchestrator
|
|
23
|
+
* post-processes each one to add Starlight `title:` frontmatter, a `version:`
|
|
24
|
+
* field when versioned, and a stale-version banner on non-default builds.
|
|
25
|
+
*
|
|
26
|
+
* Run:
|
|
27
|
+
* bun run docs:ts
|
|
28
|
+
*
|
|
29
|
+
* Requires:
|
|
30
|
+
* bun add -d typedoc typedoc-plugin-markdown
|
|
31
|
+
*/
|
|
32
|
+
import { execSync } from 'node:child_process';
|
|
33
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
34
|
+
import { dirname, join, resolve, relative } from 'node:path';
|
|
35
|
+
import { fileURLToPath } from 'node:url';
|
|
36
|
+
import { tmpdir } from 'node:os';
|
|
37
|
+
|
|
38
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
39
|
+
const PROJECT_ROOT = resolve(__dirname, '..');
|
|
40
|
+
const CONFIG_PATH = resolve(__dirname, 'ts-autodoc.json');
|
|
41
|
+
|
|
42
|
+
const c = {
|
|
43
|
+
reset: '\x1b[0m', dim: '\x1b[2m', cyan: '\x1b[36m', gold: '\x1b[33m',
|
|
44
|
+
red: '\x1b[31m', green: '\x1b[32m',
|
|
45
|
+
};
|
|
46
|
+
const log = (...a) => console.log(...a);
|
|
47
|
+
const die = (msg) => { console.error(`${c.red}error${c.reset} ${msg}`); process.exit(1); };
|
|
48
|
+
|
|
49
|
+
// ─── Load config ──────────────────────────────────────────────────────
|
|
50
|
+
if (!existsSync(CONFIG_PATH)) die(`Missing config: ${CONFIG_PATH}`);
|
|
51
|
+
const cfg = JSON.parse(readFileSync(CONFIG_PATH, 'utf8'));
|
|
52
|
+
if (!Array.isArray(cfg.entryPoints) || cfg.entryPoints.length === 0) {
|
|
53
|
+
die('ts-autodoc.json: `entryPoints` must be a non-empty array.');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const ROOT_OUTPUT = resolve(PROJECT_ROOT, cfg.outputDir ?? 'src/content/docs/api/ts');
|
|
57
|
+
const ROOT_TSCONFIG = cfg.tsconfig ? resolve(PROJECT_ROOT, cfg.tsconfig) : null;
|
|
58
|
+
|
|
59
|
+
// ─── Verify TypeDoc available ─────────────────────────────────────────
|
|
60
|
+
log(`${c.dim}→ checking typedoc${c.reset}`);
|
|
61
|
+
try {
|
|
62
|
+
execSync('npx --no-install typedoc --version', { cwd: PROJECT_ROOT, stdio: 'ignore' });
|
|
63
|
+
} catch {
|
|
64
|
+
die(`typedoc not found. Install it as a dev dep:
|
|
65
|
+
bun add -d typedoc typedoc-plugin-markdown
|
|
66
|
+
Then re-run this script.`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ─── Versioning setup ────────────────────────────────────────────────
|
|
70
|
+
function findGitRoot(start) {
|
|
71
|
+
let dir = start;
|
|
72
|
+
while (true) {
|
|
73
|
+
if (existsSync(join(dir, '.git'))) return dir;
|
|
74
|
+
const parent = dirname(dir);
|
|
75
|
+
if (parent === dir) return null;
|
|
76
|
+
dir = parent;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const versions = Array.isArray(cfg.versions) ? cfg.versions : null;
|
|
81
|
+
let SOURCE_REPO_ROOT = null;
|
|
82
|
+
let ENTRY_POINTS_REL = null;
|
|
83
|
+
let TSCONFIG_REL = null;
|
|
84
|
+
if (versions) {
|
|
85
|
+
if (versions.length === 0) die('`versions` is an empty array — set it to null/omit, or list at least one version.');
|
|
86
|
+
if (!versions.some((v) => v.tag)) die('`versions[].tag` is required on every entry.');
|
|
87
|
+
if (versions.filter((v) => v.default).length > 1) die('Only one `versions[].default: true` allowed.');
|
|
88
|
+
if (!versions.some((v) => v.default)) {
|
|
89
|
+
log(`${c.gold}warn${c.reset} no version marked default; treating the first one (${versions[0].tag}) as default`);
|
|
90
|
+
versions[0].default = true;
|
|
91
|
+
}
|
|
92
|
+
// Use the first entry point to find the source git repo. All entries
|
|
93
|
+
// must live under the same repo for versioned builds to make sense.
|
|
94
|
+
const firstEntry = resolve(PROJECT_ROOT, cfg.entryPoints[0]);
|
|
95
|
+
SOURCE_REPO_ROOT = findGitRoot(firstEntry);
|
|
96
|
+
if (!SOURCE_REPO_ROOT) {
|
|
97
|
+
die(`versions[] is configured but no .git directory was found above ${firstEntry}.\n Versioned builds require the source to be a git checkout.`);
|
|
98
|
+
}
|
|
99
|
+
ENTRY_POINTS_REL = cfg.entryPoints.map((e) =>
|
|
100
|
+
relative(SOURCE_REPO_ROOT, resolve(PROJECT_ROOT, e)),
|
|
101
|
+
);
|
|
102
|
+
TSCONFIG_REL = ROOT_TSCONFIG ? relative(SOURCE_REPO_ROOT, ROOT_TSCONFIG) : null;
|
|
103
|
+
log(`${c.dim}→ source repo: ${SOURCE_REPO_ROOT}${c.reset}`);
|
|
104
|
+
log(`${c.dim}→ entryPoints (rel): ${ENTRY_POINTS_REL.join(', ')}${c.reset}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Make a tag filesystem-safe for use as a directory name. We have to be
|
|
108
|
+
// strict here: Astro's slug normalizer strips dots from URL segments
|
|
109
|
+
// (`0.1.0` → `010`), so if our directory names contain dots the URL the
|
|
110
|
+
// VersionPicker constructs won't match the rendered URL. Convert dots
|
|
111
|
+
// to dashes (and any other non-alphanumeric to dashes).
|
|
112
|
+
// v0.3.0 → 0-3-0
|
|
113
|
+
// v1.0.0-rc.1 → 1-0-0-rc-1
|
|
114
|
+
function safeTag(tag) {
|
|
115
|
+
return tag.replace(/^v/, '').replace(/[^a-zA-Z0-9_-]/g, '-');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ─── Per-build pipeline ──────────────────────────────────────────────
|
|
119
|
+
function buildOnce({ entryPoints, tsconfig, version }) {
|
|
120
|
+
const outDir = version ? join(ROOT_OUTPUT, safeTag(version.tag)) : ROOT_OUTPUT;
|
|
121
|
+
mkdirSync(outDir, { recursive: true });
|
|
122
|
+
|
|
123
|
+
const tagPrefix = version ? `${c.cyan}[${version.label ?? version.tag}]${c.reset} ` : '';
|
|
124
|
+
log(`${c.dim}→ ${tagPrefix}generating TypeScript API pages${c.reset}`);
|
|
125
|
+
|
|
126
|
+
const args = [
|
|
127
|
+
'--plugin', 'typedoc-plugin-markdown',
|
|
128
|
+
'--out', outDir,
|
|
129
|
+
'--readme', 'none',
|
|
130
|
+
'--hideBreadcrumbs', 'true',
|
|
131
|
+
'--hidePageHeader', 'true',
|
|
132
|
+
];
|
|
133
|
+
if (tsconfig) args.push('--tsconfig', tsconfig);
|
|
134
|
+
if (cfg.skipErrorChecking) args.push('--skipErrorChecking');
|
|
135
|
+
for (const entry of entryPoints) args.push(entry);
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
execSync(`npx typedoc ${args.map((a) => `"${a}"`).join(' ')}`,
|
|
139
|
+
{ cwd: PROJECT_ROOT, stdio: 'inherit' });
|
|
140
|
+
} catch {
|
|
141
|
+
log(`${c.red}error${c.reset} ${tagPrefix}typedoc failed; skipping this build`);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Post-process every emitted .md
|
|
146
|
+
log(`${c.dim}→ ${tagPrefix}adding Starlight frontmatter${c.reset}`);
|
|
147
|
+
|
|
148
|
+
function walk(dir) {
|
|
149
|
+
const out = [];
|
|
150
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
151
|
+
const full = join(dir, entry.name);
|
|
152
|
+
if (entry.isDirectory()) out.push(...walk(full));
|
|
153
|
+
else if (entry.name.endsWith('.md')) out.push(full);
|
|
154
|
+
}
|
|
155
|
+
return out;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const pages = [];
|
|
159
|
+
for (const file of walk(outDir)) {
|
|
160
|
+
let content = readFileSync(file, 'utf8');
|
|
161
|
+
if (content.startsWith('---\n')) continue;
|
|
162
|
+
|
|
163
|
+
const h1 = content.match(/^# (.+?)$/m);
|
|
164
|
+
const fallbackTitle = relative(outDir, file).replace(/\.md$/, '').replace(/[\\/_]/g, ' ');
|
|
165
|
+
const title = (h1?.[1] ?? fallbackTitle).trim().replace(/\\_/g, '_');
|
|
166
|
+
const body = h1 ? content.replace(h1[0] + '\n', '') : content;
|
|
167
|
+
|
|
168
|
+
const desc = body.split('\n').find((l) => {
|
|
169
|
+
const t = l.trim();
|
|
170
|
+
if (!t) return false;
|
|
171
|
+
if (/^#{1,6} /.test(t)) return false;
|
|
172
|
+
if (t.startsWith('```')) return false;
|
|
173
|
+
if (t.startsWith('|')) return false;
|
|
174
|
+
if (/^[-*+] /.test(t)) return false;
|
|
175
|
+
if (/^<[^>]+>/.test(t)) return false;
|
|
176
|
+
return true;
|
|
177
|
+
});
|
|
178
|
+
const description = (desc ?? `API reference for \`${title}\`.`)
|
|
179
|
+
.trim().replace(/`/g, '').replace(/"/g, "'").slice(0, 160);
|
|
180
|
+
|
|
181
|
+
const fmLines = ['---', `title: ${title}`, `description: "${description}"`];
|
|
182
|
+
if (version) {
|
|
183
|
+
// Emit version metadata. `versionDefault: true` lets the bundled
|
|
184
|
+
// <VersionPicker> auto-discover which version to pre-select without
|
|
185
|
+
// duplicating the canonical list outside the autodoc JSON config.
|
|
186
|
+
fmLines.push(`version: "${version.tag}"`);
|
|
187
|
+
if (version.label) fmLines.push(`versionLabel: "${version.label}"`);
|
|
188
|
+
if (version.default) fmLines.push(`versionDefault: true`);
|
|
189
|
+
}
|
|
190
|
+
fmLines.push('---', '');
|
|
191
|
+
const frontmatter = fmLines.join('\n');
|
|
192
|
+
pages.push({ file, title, description, body, frontmatter });
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Thin-page post-processor (TS)
|
|
196
|
+
let bannered = 0, enriched = 0;
|
|
197
|
+
const landingPage = pages.find((p) => /^(index|readme|globals|modules)\.md$/i.test(relative(outDir, p.file)));
|
|
198
|
+
|
|
199
|
+
for (const page of pages) {
|
|
200
|
+
const proseLines = page.body.split('\n').filter((l) => {
|
|
201
|
+
const t = l.trim();
|
|
202
|
+
if (!t) return false;
|
|
203
|
+
if (/^#{1,6} /.test(t)) return false;
|
|
204
|
+
if (t.startsWith('```')) return false;
|
|
205
|
+
if (t.startsWith('|') || /^[-=]{3,}/.test(t)) return false;
|
|
206
|
+
if (/^[-*+] /.test(t)) return false;
|
|
207
|
+
if (/^<[^>]+>/.test(t)) return false;
|
|
208
|
+
return true;
|
|
209
|
+
}).length;
|
|
210
|
+
const bodyChars = page.body.replace(/\s+/g, '').length;
|
|
211
|
+
const isThin = bodyChars < 150 && proseLines < 1;
|
|
212
|
+
|
|
213
|
+
let newBody = page.body;
|
|
214
|
+
let touched = false;
|
|
215
|
+
|
|
216
|
+
if (version && !version.default) {
|
|
217
|
+
const defaultV = (cfg.versions ?? []).find((v) => v.default);
|
|
218
|
+
const latestLabel = defaultV ? (defaultV.label ?? defaultV.tag) : 'latest';
|
|
219
|
+
const apiBase = `/${cfg.outputDir.replace(/^src\/content\/docs\/?/, '').replace(/\/$/, '')}`;
|
|
220
|
+
const sameRel = relative(outDir, page.file).replace(/\.md$/, '');
|
|
221
|
+
const latestPath = defaultV
|
|
222
|
+
? `${apiBase}/${safeTag(defaultV.tag)}/${sameRel}/`
|
|
223
|
+
: null;
|
|
224
|
+
const link = latestPath ? `[${latestLabel} →](${latestPath})` : latestLabel;
|
|
225
|
+
const stale = [
|
|
226
|
+
'', `:::caution[Older version]`,
|
|
227
|
+
`You're viewing **${version.label ?? version.tag}**. Latest is ${link}.`,
|
|
228
|
+
':::', '',
|
|
229
|
+
].join('\n');
|
|
230
|
+
newBody = stale + newBody;
|
|
231
|
+
touched = true;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (page === landingPage) {
|
|
235
|
+
const others = pages.filter((p) => p !== landingPage);
|
|
236
|
+
if (others.length > 0) {
|
|
237
|
+
const lines = ['', '## Submodules', ''];
|
|
238
|
+
for (const sib of others) {
|
|
239
|
+
const rel = relative(outDir, sib.file).replace(/\.md$/, '');
|
|
240
|
+
const summary = sib.description && !sib.description.startsWith('API reference for')
|
|
241
|
+
? ` — ${sib.description}` : '';
|
|
242
|
+
lines.push(`- [\`${sib.title}\`](./${rel}.md)${summary}`);
|
|
243
|
+
}
|
|
244
|
+
lines.push('');
|
|
245
|
+
const submodulesSection = lines.join('\n');
|
|
246
|
+
if (isThin) {
|
|
247
|
+
newBody = newBody + `\nTop-level entry — see modules below for the full API surface.\n${submodulesSection}`;
|
|
248
|
+
} else {
|
|
249
|
+
newBody = newBody.replace(/\s+$/, '') + '\n' + submodulesSection;
|
|
250
|
+
}
|
|
251
|
+
enriched += 1;
|
|
252
|
+
touched = true;
|
|
253
|
+
log(`${c.green} ✓${c.reset} ${tagPrefix}added Submodules to ${relative(outDir, page.file)}`);
|
|
254
|
+
}
|
|
255
|
+
} else if (isThin) {
|
|
256
|
+
const noteBlock = [
|
|
257
|
+
'', ':::note[This page is sparse]',
|
|
258
|
+
`The auto-generated reference for \`${page.title}\` is short. Expanding the leading \`/** ... */\` TSDoc comment in the source (purpose, when to use it, a tiny example) would populate this page with real context.`,
|
|
259
|
+
':::', '',
|
|
260
|
+
].join('\n');
|
|
261
|
+
newBody = noteBlock + newBody;
|
|
262
|
+
bannered += 1;
|
|
263
|
+
touched = true;
|
|
264
|
+
log(`${c.gold} ⚠${c.reset} ${tagPrefix}thin-page banner on ${relative(outDir, page.file)}`);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (touched && cfg.repoUrl) {
|
|
268
|
+
const branch = version ? version.tag : (cfg.repoBranch ?? 'main');
|
|
269
|
+
const repo = cfg.repoUrl.replace(/\/$/, '');
|
|
270
|
+
newBody = newBody.replace(/\s+$/, '') +
|
|
271
|
+
`\n\n## See also\n\n- [View on GitHub](${repo}/tree/${branch})\n`;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
writeFileSync(page.file, page.frontmatter + newBody);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
log('');
|
|
278
|
+
log(`${c.green}✓${c.reset} ${tagPrefix}Generated ${c.gold}${pages.length}${c.reset} TS API page${pages.length === 1 ? '' : 's'} in ${c.cyan}${relative(PROJECT_ROOT, outDir)}${c.reset}/`);
|
|
279
|
+
if (enriched || bannered) log(`${c.dim} ${enriched} landing page${enriched === 1 ? '' : 's'} enriched, ${bannered} thin page${bannered === 1 ? '' : 's'} flagged${c.reset}`);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// ─── Run: single-build or per-version with worktrees ──────────────────
|
|
283
|
+
const createdWorktrees = [];
|
|
284
|
+
|
|
285
|
+
function cleanup() {
|
|
286
|
+
for (const wt of createdWorktrees) {
|
|
287
|
+
try {
|
|
288
|
+
execSync(`git -C "${SOURCE_REPO_ROOT}" worktree remove --force "${wt}"`, { stdio: 'ignore' });
|
|
289
|
+
} catch {
|
|
290
|
+
try { rmSync(wt, { recursive: true, force: true }); } catch {}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
process.on('exit', cleanup);
|
|
295
|
+
process.on('SIGINT', () => { cleanup(); process.exit(130); });
|
|
296
|
+
|
|
297
|
+
try {
|
|
298
|
+
if (!versions) {
|
|
299
|
+
buildOnce({
|
|
300
|
+
entryPoints: cfg.entryPoints.map((e) => resolve(PROJECT_ROOT, e)),
|
|
301
|
+
tsconfig: ROOT_TSCONFIG,
|
|
302
|
+
version: null,
|
|
303
|
+
});
|
|
304
|
+
} else {
|
|
305
|
+
for (const v of versions) {
|
|
306
|
+
const wt = mkdtempSync(join(tmpdir(), `tsdoc-${safeTag(v.tag)}-`));
|
|
307
|
+
createdWorktrees.push(wt);
|
|
308
|
+
log(`${c.dim}→ git worktree add ${wt} ${v.tag}${c.reset}`);
|
|
309
|
+
try {
|
|
310
|
+
execSync(`git -C "${SOURCE_REPO_ROOT}" worktree add --detach "${wt}" "${v.tag}"`, { stdio: 'inherit' });
|
|
311
|
+
} catch {
|
|
312
|
+
log(`${c.red}error${c.reset} failed to create worktree for ${v.tag}; skipping`);
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
const wtEntries = ENTRY_POINTS_REL.map((rel) => join(wt, rel));
|
|
316
|
+
const wtTsconfig = TSCONFIG_REL ? join(wt, TSCONFIG_REL) : null;
|
|
317
|
+
const missing = wtEntries.find((p) => !existsSync(p));
|
|
318
|
+
if (missing) {
|
|
319
|
+
log(`${c.gold}warn${c.reset} ${v.tag}: entry point ${missing} not found; skipping`);
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
buildOnce({ entryPoints: wtEntries, tsconfig: wtTsconfig, version: v });
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Alias the default version at un-versioned URLs
|
|
326
|
+
const defaultV = versions.find((v) => v.default);
|
|
327
|
+
if (defaultV) {
|
|
328
|
+
log(`${c.dim}→ aliasing ${defaultV.tag} as the default (un-versioned) build${c.reset}`);
|
|
329
|
+
const wt = mkdtempSync(join(tmpdir(), `tsdoc-default-`));
|
|
330
|
+
createdWorktrees.push(wt);
|
|
331
|
+
try {
|
|
332
|
+
execSync(`git -C "${SOURCE_REPO_ROOT}" worktree add --detach "${wt}" "${defaultV.tag}"`, { stdio: 'ignore' });
|
|
333
|
+
const wtEntries = ENTRY_POINTS_REL.map((rel) => join(wt, rel));
|
|
334
|
+
const wtTsconfig = TSCONFIG_REL ? join(wt, TSCONFIG_REL) : null;
|
|
335
|
+
buildOnce({ entryPoints: wtEntries, tsconfig: wtTsconfig, version: null });
|
|
336
|
+
} catch (err) {
|
|
337
|
+
log(`${c.gold}warn${c.reset} default-alias build failed: ${err.message}`);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
} finally {
|
|
342
|
+
cleanup();
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
log('');
|
|
346
|
+
log(`${c.dim}Sidebar wiring (astro.config.mjs):${c.reset}`);
|
|
347
|
+
log(`${c.dim} { label: 'TS API', autogenerate: { directory: '${cfg.outputDir.replace(/^src\/content\/docs\/?/, '')}' } }${c.reset}`);
|
|
348
|
+
if (versions) log(`${c.dim} → with ${versions.length} version${versions.length === 1 ? '' : 's'}, the sidebar auto-groups by version subdirectory.${c.reset}`);
|
|
349
|
+
log('');
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "Configuration for scripts/build-python-docs.mjs. Adjust to point at your Python source.",
|
|
3
|
+
"searchPath": "../../python-example/src",
|
|
4
|
+
"modules": [
|
|
5
|
+
"example_module"
|
|
6
|
+
],
|
|
7
|
+
"outputDir": "src/content/docs/api",
|
|
8
|
+
"repoUrl": "",
|
|
9
|
+
"repoBranch": "main"
|
|
10
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "Configuration for scripts/build-ts-docs.mjs. Adjust to point at your TypeScript project.",
|
|
3
|
+
"entryPoints": ["../../packages/your-lib/src/index.ts"],
|
|
4
|
+
"tsconfig": "../../packages/your-lib/tsconfig.json",
|
|
5
|
+
"outputDir": "src/content/docs/api/ts",
|
|
6
|
+
"githubPages": false,
|
|
7
|
+
"skipErrorChecking": true,
|
|
8
|
+
"repoUrl": "",
|
|
9
|
+
"repoBranch": "main"
|
|
10
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Abstract Data Documentation Theme — agent context
|
|
2
|
+
|
|
3
|
+
This project uses the **Abstract Data Documentation Theme** (built on Astro Starlight, distributed as `@abstractdata/starlight-theme`). When talking to the user, refer to the product as the **"Abstract Data Documentation Theme"** — not "Astro Starlight" — Starlight is the substrate, not the product identity.
|
|
4
|
+
|
|
5
|
+
## First-interaction handshake — IMPORTANT
|
|
6
|
+
|
|
7
|
+
When you open a conversation in this project, immediately check the setup state:
|
|
8
|
+
|
|
9
|
+
- **`scripts/python-autodoc.json`** — does it have placeholder values like `searchPath: "../../../python-example/src"` and a `modules` list pointing at `example_module`? Or has it been customized for this project's actual Python source?
|
|
10
|
+
- **`astro.config.mjs`** — is `title: 'Your Project Docs'` still the literal placeholder? Is `site: 'https://example.com'` still the placeholder URL?
|
|
11
|
+
|
|
12
|
+
**If either signal looks like a fresh scaffold,** open the conversation with this offer (don't wait for the user to ask):
|
|
13
|
+
|
|
14
|
+
> "Looks like this is a freshly scaffolded Abstract Data Documentation Theme project. Want me to run the `abstract-data-setup` workflow now? It'll detect your Python source, audit docstring coverage, sniff your docstring style, ask a few config questions, and wire up `python-autodoc.json`, `astro.config.mjs`, and `package.json`. (Reply 'no' if you'd rather configure manually — I won't ask again.)"
|
|
15
|
+
|
|
16
|
+
If the user agrees, immediately invoke the `abstract-data-setup` skill (`.claude/skills/abstract-data-setup/SKILL.md`).
|
|
17
|
+
|
|
18
|
+
**If setup looks already-done** (JSON has real module names, `astro.config.mjs` has a real title): don't auto-offer the handshake. Just be a normal helpful assistant for whatever the user wants to work on.
|
|
19
|
+
|
|
20
|
+
**Once the user has declined or completed setup:** treat this handshake as satisfied for the rest of the conversation. Don't keep nagging.
|
|
21
|
+
|
|
22
|
+
## Skill location
|
|
23
|
+
|
|
24
|
+
The full setup workflow lives at `.claude/skills/abstract-data-setup/SKILL.md`. It's an 11-phase procedural skill: confirm context → locate source → detect Python signals → audit docstring coverage → detect docstring style → recommend modules → gather brand config → write configs → optionally run docs:python → optional pre-commit hook → summary.
|
|
25
|
+
|
|
26
|
+
## Project conventions
|
|
27
|
+
|
|
28
|
+
- **Package manager: Bun** — not npm/yarn/pnpm. Use `bun run <name>` and `bun add <pkg>`.
|
|
29
|
+
- **Commits: Conventional Commits** — release-please reads them to bump versions automatically. `feat:` for minor, `fix:` for patch, `feat!:` or `BREAKING CHANGE:` for major.
|
|
30
|
+
- **Don't hand-edit:** `.cursor/rules/abstract-data-setup.mdc`, `.github/copilot-instructions.md`, `.cursor/rules/welcome.mdc` — these are auto-generated.
|
|
31
|
+
|
|
32
|
+
## Quick reference
|
|
33
|
+
|
|
34
|
+
- `astro.config.mjs` — Starlight config: title, sidebar, plugin options
|
|
35
|
+
- `scripts/python-autodoc.json` — Python autodoc target config
|
|
36
|
+
- `scripts/build-python-docs.mjs` — orchestrator that wraps pydoc-markdown
|
|
37
|
+
- `src/content/docs/` — Markdown/MDX content
|
|
38
|
+
- `bun run dev` — start the docs dev server
|
|
39
|
+
- `bun run docs:python` — regenerate API pages from Python source
|
|
40
|
+
- `bun run build` — production build
|
|
41
|
+
|
|
42
|
+
## Links
|
|
43
|
+
|
|
44
|
+
- Source repo: https://github.com/Abstract-Data/abstract-data-doc-theme
|
|
45
|
+
- Theme on npm: https://www.npmjs.com/package/@abstractdata/starlight-theme
|
|
46
|
+
- Scaffolder on npm: https://www.npmjs.com/package/@abstractdata/create-docs
|