@readme/cli 0.0.26
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/README.md +55 -0
- package/bin/readme.js +8 -0
- package/package.json +58 -0
- package/src/bootstrap.js +97 -0
- package/src/cli.js +189 -0
- package/src/commands/dev.js +119 -0
- package/src/commands/eyes.js +37 -0
- package/src/commands/import.js +2565 -0
- package/src/commands/lint.js +70 -0
- package/src/commands/oas-sync.js +364 -0
- package/src/commands/oas-validate.js +208 -0
- package/src/commands/play.js +17 -0
- package/src/commands/pretty.js +133 -0
- package/src/commands/setup.js +256 -0
- package/src/commands/versions.js +81 -0
- package/src/dev/.next/app-build-manifest.json +20 -0
- package/src/dev/.next/build-manifest.json +31 -0
- package/src/dev/.next/cache/.rscinfo +1 -0
- package/src/dev/.next/cache/next-devtools-config.json +1 -0
- package/src/dev/.next/cache/webpack/client-development/0.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/client-development/1.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/client-development/10.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/client-development/11.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/client-development/2.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/client-development/3.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/client-development/3.pack.gz_ +0 -0
- package/src/dev/.next/cache/webpack/client-development/4.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/client-development/5.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/client-development/5.pack.gz_ +0 -0
- package/src/dev/.next/cache/webpack/client-development/6.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/client-development/7.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/client-development/7.pack.gz_ +0 -0
- package/src/dev/.next/cache/webpack/client-development/8.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/client-development/9.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/client-development/index.pack.gz.old +0 -0
- package/src/dev/.next/cache/webpack/client-development-fallback/0.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/client-development-fallback/1.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/client-development-fallback/index.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/client-development-fallback/index.pack.gz.old +0 -0
- package/src/dev/.next/cache/webpack/edge-server-development/0.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/edge-server-development/1.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/edge-server-development/index.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/edge-server-development/index.pack.gz.old +0 -0
- package/src/dev/.next/cache/webpack/server-development/0.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/1.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/10.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/11.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/12.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/13.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/14.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/15.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/2.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/2.pack.gz_ +0 -0
- package/src/dev/.next/cache/webpack/server-development/3.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/3.pack.gz_ +0 -0
- package/src/dev/.next/cache/webpack/server-development/4.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/5.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/6.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/6.pack.gz_ +0 -0
- package/src/dev/.next/cache/webpack/server-development/7.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/7.pack.gz_ +0 -0
- package/src/dev/.next/cache/webpack/server-development/8.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/9.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/9.pack.gz_ +0 -0
- package/src/dev/.next/cache/webpack/server-development/index.pack.gz +0 -0
- package/src/dev/.next/cache/webpack/server-development/index.pack.gz.old +0 -0
- package/src/dev/.next/package.json +1 -0
- package/src/dev/.next/prerender-manifest.json +11 -0
- package/src/dev/.next/react-loadable-manifest.json +1 -0
- package/src/dev/.next/routes-manifest.json +1 -0
- package/src/dev/.next/server/app/[...slug]/page.js +360 -0
- package/src/dev/.next/server/app/[...slug]/page_client-reference-manifest.js +1 -0
- package/src/dev/.next/server/app/page.js +349 -0
- package/src/dev/.next/server/app/page_client-reference-manifest.js +1 -0
- package/src/dev/.next/server/app-paths-manifest.json +3 -0
- package/src/dev/.next/server/edge-runtime-webpack.js +1151 -0
- package/src/dev/.next/server/interception-route-rewrite-manifest.js +1 -0
- package/src/dev/.next/server/middleware-build-manifest.js +33 -0
- package/src/dev/.next/server/middleware-manifest.json +32 -0
- package/src/dev/.next/server/middleware-react-loadable-manifest.js +1 -0
- package/src/dev/.next/server/middleware.js +1113 -0
- package/src/dev/.next/server/next-font-manifest.js +1 -0
- package/src/dev/.next/server/next-font-manifest.json +1 -0
- package/src/dev/.next/server/pages-manifest.json +5 -0
- package/src/dev/.next/server/server-reference-manifest.js +1 -0
- package/src/dev/.next/server/server-reference-manifest.json +5 -0
- package/src/dev/.next/server/static/webpack/633457081244afec._.hot-update.json +1 -0
- package/src/dev/.next/server/vendor-chunks/@readme.js +25 -0
- package/src/dev/.next/server/vendor-chunks/@swc.js +55 -0
- package/src/dev/.next/server/vendor-chunks/next.js +3659 -0
- package/src/dev/.next/server/webpack-runtime.js +209 -0
- package/src/dev/.next/static/chunks/app/[...slug]/loading.js +28 -0
- package/src/dev/.next/static/chunks/app/[...slug]/page.js +28 -0
- package/src/dev/.next/static/chunks/app/layout.js +171 -0
- package/src/dev/.next/static/chunks/app/page.js +28 -0
- package/src/dev/.next/static/chunks/app-pages-internals.js +182 -0
- package/src/dev/.next/static/chunks/main-app.js +1882 -0
- package/src/dev/.next/static/chunks/polyfills.js +1 -0
- package/src/dev/.next/static/chunks/webpack.js +1393 -0
- package/src/dev/.next/static/css/app/layout.css +559 -0
- package/src/dev/.next/static/development/_buildManifest.js +1 -0
- package/src/dev/.next/static/development/_ssgManifest.js +1 -0
- package/src/dev/.next/static/webpack/633457081244afec._.hot-update.json +1 -0
- package/src/dev/.next/static/webpack/ec52a3fce0f78db0.webpack.hot-update.json +1 -0
- package/src/dev/.next/static/webpack/webpack.ec52a3fce0f78db0.hot-update.js +12 -0
- package/src/dev/.next/trace +21 -0
- package/src/dev/.next/types/app/[...slug]/page.ts +84 -0
- package/src/dev/.next/types/app/layout.ts +84 -0
- package/src/dev/.next/types/app/page.ts +84 -0
- package/src/dev/.next/types/cache-life.d.ts +141 -0
- package/src/dev/.next/types/package.json +1 -0
- package/src/dev/.next/types/routes.d.ts +55 -0
- package/src/dev/app/Sidebar.js +149 -0
- package/src/dev/app/[...slug]/loading.js +16 -0
- package/src/dev/app/[...slug]/page.js +43 -0
- package/src/dev/app/globals.css +167 -0
- package/src/dev/app/layout.js +73 -0
- package/src/dev/app/page.js +19 -0
- package/src/dev/lib/docs.js +337 -0
- package/src/dev/middleware.js +7 -0
- package/src/dev/next.config.mjs +22 -0
- package/src/index.js +12 -0
- package/src/prompts/index.js +352 -0
- package/src/utils/claude.js +15 -0
- package/src/utils/eyes.js +365 -0
- package/src/utils/git.js +143 -0
- package/src/utils/lint.js +99 -0
- package/src/utils/reporter.js +319 -0
- package/src/utils/setup-templates.js +323 -0
- package/src/utils/styles.js +50 -0
- package/src/utils/tamagotchi.js +1139 -0
- package/src/utils/tips.js +90 -0
- package/src/validators/components.js +230 -0
- package/src/validators/content.js +53 -0
- package/src/validators/duplicates.js +45 -0
- package/src/validators/frontmatter.js +247 -0
- package/src/validators/links.js +68 -0
- package/src/validators/nesting.js +50 -0
- package/src/validators/numbering.js +136 -0
- package/src/validators/oas-reference.js +126 -0
- package/src/validators/oas-schema.js +106 -0
- package/src/validators/ordering.js +121 -0
- package/src/validators/recipes.js +143 -0
- package/vendor/TOOLS.md +19 -0
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompts used by the `import` command to organize a scraped or llms.txt-derived
|
|
3
|
+
* documentation site into a category/page hierarchy. Exposed as a stable public
|
|
4
|
+
* API so other tools can reuse the exact prompts without depending on the CLI's
|
|
5
|
+
* runtime — bring your own model client and call `runJsonQuery`-equivalent
|
|
6
|
+
* yourself.
|
|
7
|
+
*
|
|
8
|
+
* Each prompt is exposed three ways:
|
|
9
|
+
* - `<name>SystemPrompt` — the static system prompt string
|
|
10
|
+
* - `build<Name>UserPrompt(in)` — builds the dynamic user prompt for a given input
|
|
11
|
+
* - `<name>Prompt(input)` — convenience wrapper returning { systemPrompt, userPrompt }
|
|
12
|
+
*
|
|
13
|
+
* Inputs use plain JS shapes documented per builder. They intentionally avoid
|
|
14
|
+
* importing anything else from this package so the prompts module stays
|
|
15
|
+
* dependency-free at runtime.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
// ─── slotOrphans ─────────────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
export const slotOrphansSystemPrompt = [
|
|
21
|
+
'You assign orphan documentation pages to existing categories.',
|
|
22
|
+
'Output ONLY a valid JSON array of integers — one per orphan page, in order.',
|
|
23
|
+
'Each integer is the category index (0-based) the orphan belongs in, or -1 if none fit.',
|
|
24
|
+
'',
|
|
25
|
+
'Example output: [0, 2, 0, 1, -1, 3]',
|
|
26
|
+
'',
|
|
27
|
+
'Guidance:',
|
|
28
|
+
'- Choose the best semantic fit based on the orphan\'s title and URL path.',
|
|
29
|
+
'- Only use -1 when no existing category is plausible.',
|
|
30
|
+
].join('\n');
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @param {object} input
|
|
34
|
+
* @param {Array<{ title: string, pages?: Array<{ title: string }> }>} input.categories
|
|
35
|
+
* @param {Array<{ title: string, url: string }>} input.orphans
|
|
36
|
+
* @returns {string}
|
|
37
|
+
*/
|
|
38
|
+
export function buildSlotOrphansUserPrompt({ categories, orphans }) {
|
|
39
|
+
const catList = categories.map((c, i) => {
|
|
40
|
+
const sample = (c.pages || []).slice(0, 3).map((p) => p.title).join(', ');
|
|
41
|
+
return `${i}. ${c.title}${sample ? ` (e.g. ${sample})` : ''}`;
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const orphanList = orphans.map((p, i) => {
|
|
45
|
+
let relPath = p.url;
|
|
46
|
+
try { relPath = new URL(p.url).pathname; } catch {}
|
|
47
|
+
return `${i}. ${p.title} — ${relPath}`;
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return [
|
|
51
|
+
'Categories:',
|
|
52
|
+
...catList,
|
|
53
|
+
'',
|
|
54
|
+
`${orphans.length} orphan pages to slot:`,
|
|
55
|
+
...orphanList,
|
|
56
|
+
'',
|
|
57
|
+
`Output a JSON array of ${orphans.length} integers, one per orphan in order (category index 0..${categories.length - 1}, or -1 for none).`,
|
|
58
|
+
].join('\n');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function slotOrphansPrompt(input) {
|
|
62
|
+
return {
|
|
63
|
+
systemPrompt: slotOrphansSystemPrompt,
|
|
64
|
+
userPrompt: buildSlotOrphansUserPrompt(input),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ─── iconizeNav ──────────────────────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
export const iconizeNavSystemPrompt = [
|
|
71
|
+
'You assign one FontAwesome Free Solid icon to each documentation category.',
|
|
72
|
+
'Output ONLY a valid JSON array of icon name strings, one per input category, in order.',
|
|
73
|
+
'Use the icon name only (no "fa-" prefix, no object wrapper).',
|
|
74
|
+
'',
|
|
75
|
+
'Example output: ["rocket", "book", "code", "gear"]',
|
|
76
|
+
].join('\n');
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @param {object} input
|
|
80
|
+
* @param {Array<{ title: string }>} input.categories
|
|
81
|
+
* @returns {string}
|
|
82
|
+
*/
|
|
83
|
+
export function buildIconizeNavUserPrompt({ categories }) {
|
|
84
|
+
return [
|
|
85
|
+
`${categories.length} categories:`,
|
|
86
|
+
'',
|
|
87
|
+
...categories.map((c, i) => `${i}. ${c.title}`),
|
|
88
|
+
'',
|
|
89
|
+
'Return a JSON array of FontAwesome icon names, one per category, in order.',
|
|
90
|
+
].join('\n');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function iconizeNavPrompt(input) {
|
|
94
|
+
return {
|
|
95
|
+
systemPrompt: iconizeNavSystemPrompt,
|
|
96
|
+
userPrompt: buildIconizeNavUserPrompt(input),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ─── organizeFromSections ────────────────────────────────────────────────────
|
|
101
|
+
|
|
102
|
+
export const organizeFromSectionsSystemPrompt = [
|
|
103
|
+
'You assign a FontAwesome Free Solid icon to each documentation section, and lightly polish the section title.',
|
|
104
|
+
'Output ONLY a valid JSON array — no prose, no markdown, no code fences.',
|
|
105
|
+
'',
|
|
106
|
+
'Schema:',
|
|
107
|
+
'[',
|
|
108
|
+
' { "title": "<lightly polished Title Case, 1-4 words>", "icon": "<fontawesome icon name, no fa- prefix>" },',
|
|
109
|
+
' ...',
|
|
110
|
+
']',
|
|
111
|
+
'',
|
|
112
|
+
'Rules:',
|
|
113
|
+
'- Return exactly one entry per input section, in the same order.',
|
|
114
|
+
'- Keep the original title unless it clearly benefits from Title Case fixes; do not rename for topic/tone.',
|
|
115
|
+
'- Pick an icon that semantically fits the section (e.g. rocket for Getting Started, code for API, plug for Integrations).',
|
|
116
|
+
].join('\n');
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* @param {object} input
|
|
120
|
+
* @param {string|null|undefined} input.siteTitle
|
|
121
|
+
* @param {Array<{ title: string, items: Array<unknown> }>} input.sections
|
|
122
|
+
* @returns {string}
|
|
123
|
+
*/
|
|
124
|
+
export function buildOrganizeFromSectionsUserPrompt({ siteTitle, sections }) {
|
|
125
|
+
return [
|
|
126
|
+
`Site title: ${siteTitle || '(unknown)'}`,
|
|
127
|
+
`${sections.length} sections:`,
|
|
128
|
+
'',
|
|
129
|
+
...sections.map((s, i) => `${i}. ${s.title} (${s.items.length} pages)`),
|
|
130
|
+
'',
|
|
131
|
+
'Output the JSON array now.',
|
|
132
|
+
].join('\n');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function organizeFromSectionsPrompt(input) {
|
|
136
|
+
return {
|
|
137
|
+
systemPrompt: organizeFromSectionsSystemPrompt,
|
|
138
|
+
userPrompt: buildOrganizeFromSectionsUserPrompt(input),
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ─── organizeFromScratch ─────────────────────────────────────────────────────
|
|
143
|
+
|
|
144
|
+
export const organizeFromScratchSystemPrompt = [
|
|
145
|
+
'You organize documentation pages into a category hierarchy for a ReadMe-style docs site.',
|
|
146
|
+
'Output ONLY a valid JSON object that matches the schema below — no prose, no markdown, no code fences.',
|
|
147
|
+
'',
|
|
148
|
+
'Schema:',
|
|
149
|
+
'{',
|
|
150
|
+
' "title": "<short site or doc title>",',
|
|
151
|
+
' "categories": [',
|
|
152
|
+
' {',
|
|
153
|
+
' "title": "<Title Case, 1-4 words>",',
|
|
154
|
+
' "icon": "<FontAwesome solid icon name, e.g. rocket, book, code, gear, plug, key, chart-line>",',
|
|
155
|
+
' "pageIds": [<integer id from the input list>, ...]',
|
|
156
|
+
' }',
|
|
157
|
+
' ]',
|
|
158
|
+
'}',
|
|
159
|
+
'',
|
|
160
|
+
'Rules:',
|
|
161
|
+
'- Refer to pages by their integer `id` only — do NOT echo back titles, urls, or descriptions. The caller reconstructs full page data from the ids.',
|
|
162
|
+
'- If the input sections look meaningful, use them as category starting points; merge or rename ones that are redundant, too-broad, or generic (e.g. "Resources", "English", "Root URL").',
|
|
163
|
+
'- If the input sections are not useful, invent good categories from page titles and URLs.',
|
|
164
|
+
'- Every category MUST include a FontAwesome Free Solid icon name that semantically fits the category theme. Use the icon name only (no "fa-" prefix).',
|
|
165
|
+
'- Every input page id MUST appear in exactly one category. Do not drop or duplicate ids.',
|
|
166
|
+
'- Keep category titles human-readable, Title Case, 1-4 words.',
|
|
167
|
+
].join('\n');
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* @param {object} input
|
|
171
|
+
* @param {string|null|undefined} input.siteTitle
|
|
172
|
+
* @param {Array<{ title: string, url: string, description?: string }>} input.items
|
|
173
|
+
* Pre-flattened page list. The id used in the prompt is the array index.
|
|
174
|
+
* @returns {string}
|
|
175
|
+
*/
|
|
176
|
+
export function buildOrganizeFromScratchUserPrompt({ siteTitle, items }) {
|
|
177
|
+
const compactLines = items.map((it, idx) => {
|
|
178
|
+
let relPath = it.url;
|
|
179
|
+
try { relPath = new URL(it.url).pathname + new URL(it.url).search; } catch {}
|
|
180
|
+
return `${idx}\t${it.title}\t${relPath}`;
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
let origin = '(unknown)';
|
|
184
|
+
if (items.length > 0) {
|
|
185
|
+
try { origin = new URL(items[0].url).origin; } catch {}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return [
|
|
189
|
+
`Site title: ${siteTitle || '(unknown)'}`,
|
|
190
|
+
`Origin: ${origin}`,
|
|
191
|
+
`${items.length} pages to organize. Each line below is: \`id\\ttitle\\tpath\`.`,
|
|
192
|
+
'',
|
|
193
|
+
...compactLines,
|
|
194
|
+
'',
|
|
195
|
+
`Output the organized JSON object now. Reference pages by \`pageIds\` (integers 0..${items.length - 1}) — do not echo page data back.`,
|
|
196
|
+
].join('\n');
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export function organizeFromScratchPrompt(input) {
|
|
200
|
+
return {
|
|
201
|
+
systemPrompt: organizeFromScratchSystemPrompt,
|
|
202
|
+
userPrompt: buildOrganizeFromScratchUserPrompt(input),
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// ─── prettifyPage ────────────────────────────────────────────────────────────
|
|
207
|
+
|
|
208
|
+
export const prettifyPageSystemPrompt = `You are a documentation page analyzer and MDX fixer. Given source markdown, you must:
|
|
209
|
+
|
|
210
|
+
1. Fix MDX compatibility issues for ReadMe's parser
|
|
211
|
+
2. Classify whether this page is an API reference
|
|
212
|
+
3. Suggest the best category for this page
|
|
213
|
+
|
|
214
|
+
## Target renderer: ReadMe v2 MDX
|
|
215
|
+
Output must render correctly in ReadMe. Prefer plain CommonMark — it always renders. Only use the components ReadMe supports (below). Anything else must be converted to plain markdown.
|
|
216
|
+
|
|
217
|
+
### ReadMe-supported callouts (use these exact prefixes; emoji matters)
|
|
218
|
+
Use a blockquote whose first line starts with one of these emojis:
|
|
219
|
+
- \`> 📘 Title\` — Info
|
|
220
|
+
- \`> 🚧 Title\` — Warning
|
|
221
|
+
- \`> ❗️ Title\` — Critical / danger
|
|
222
|
+
- \`> 👍 Title\` — Success / tip
|
|
223
|
+
Example:
|
|
224
|
+
\`\`\`
|
|
225
|
+
> 📘 Good to know
|
|
226
|
+
>
|
|
227
|
+
> Authentication uses Bearer tokens.
|
|
228
|
+
\`\`\`
|
|
229
|
+
Convert any <Callout>, <Note>, <Info>, <Warning>, <Tip>, <Aside>, :::note, :::info, :::warning, :::danger blocks into this form, mapping the semantic type to the ReadMe-supported callout — **apply aggressively**.
|
|
230
|
+
Default bias: **upgrade when in doubt.** Plain markdown is rarely the best representation when one of these components fits even loosely. Aim for 2–5 component upgrades on a typical page; pages that are pure prose can have zero. Don't pile components on top of each other (no Cards inside Tabs inside Accordions).
|
|
231
|
+
|
|
232
|
+
- \`<Tabs>\` / \`<Tab title="...">\` — tabbed content. Use whenever the source has parallel-variant siblings:
|
|
233
|
+
- 2+ sibling \`##\`/\`###\`/\`####\` headings that read as variants (OS, language, framework, audience, plan tier, version). Wrap each sibling's content in \`<Tab title="<heading>">\` and drop the original heading.
|
|
234
|
+
- 2+ consecutive fenced code blocks with different language tags showing the same action — tab them, titled by language (\`Python\`, \`Node.js\`, \`cURL\`, \`Go\`).
|
|
235
|
+
- "Examples" / "Usage" / "Quick reference" sections containing multiple code blocks across languages — tab the code blocks even if some have inline prose.
|
|
236
|
+
- "Before / After", "v1 / v2", "Old / New" sections with two parallel blocks.
|
|
237
|
+
|
|
238
|
+
- \`<Accordion title="...">\` — collapsible content. Use for any block that's reference-y or skippable:
|
|
239
|
+
- FAQs, Troubleshooting, "Common issues", "Common errors", "Known issues", "Edge cases" — wrap each entry as one Accordion, title = the question or the error name.
|
|
240
|
+
- "Advanced", "Optional", "Details", "Internals", "Under the hood" sub-sections — wrap each as one Accordion.
|
|
241
|
+
- Long parameter / config / error-code tables where each row has its own description — convert each row to an Accordion if rows have multi-paragraph descriptions; keep table form for short rows.
|
|
242
|
+
- "Glossary", "Terms", "Definitions" — each term as its own Accordion.
|
|
243
|
+
|
|
244
|
+
- \`<Cards>\` / \`<Card title="..." href="...">\` — card grids. Use for any link list of 3+ entries pointing at other docs pages:
|
|
245
|
+
- Trailing "Next steps", "See also", "Further reading", "Related", "Related guides", "Recommended reading", "Where to go next".
|
|
246
|
+
- Top-of-page "What's covered" indices that link to in-doc anchors or sibling pages.
|
|
247
|
+
- Overview / hub / category-landing pages where the body is mostly a list of links to children — convert the whole list to Cards.
|
|
248
|
+
- Feature comparison summaries that include a link per feature.
|
|
249
|
+
- Use the link text as \`title\` and the link target as \`href\`. Drop list bullets.
|
|
250
|
+
|
|
251
|
+
- \`<Image src="..." alt="..." />\` — for any markdown image whose alt text is more than a few words and informative. Plain \`\` is fine for tiny incidental icons.
|
|
252
|
+
|
|
253
|
+
- \`<Columns>\` / \`<Column>\` — DO NOT add proactively; keep if already present.
|
|
254
|
+
|
|
255
|
+
### Callouts — be liberal
|
|
256
|
+
Convert anything that *signals* an aside into a ReadMe emoji-blockquote. Drop the original prefix word/emphasis once you add the emoji+title.
|
|
257
|
+
|
|
258
|
+
Triggers for callouts (any of these → upgrade):
|
|
259
|
+
- A paragraph or blockquote whose first line starts with \`Note\`, \`Tip\`, \`Important\`, \`Warning\`, \`Caution\`, \`Heads up\`, \`Pro tip\`, \`FYI\`, \`Remember\`, \`Don't\` — bolded or not, with or without trailing colon.
|
|
260
|
+
- A standalone short paragraph that starts with \`⚠️\`, \`💡\`, \`📝\`, \`ℹ️\`, \`✅\`, \`❌\`, \`🚨\` — strip the emoji and re-emit as the right callout type.
|
|
261
|
+
- A short single-sentence paragraph that uses categorical "must" / "required" / "do not" / "never" / "cannot" language and is parenthetical to the surrounding flow → \`> ❗️ Required\` or \`> 🚧 Warning\`.
|
|
262
|
+
- "Limitations", "Known issues", "Caveats", "Gotchas" subsections → wrap the section's prose in \`> 🚧 Limitations\`.
|
|
263
|
+
- "Quick reminder", "Reminder", "Note that", "Keep in mind" stand-alone paragraphs → \`> 📘\`.
|
|
264
|
+
|
|
265
|
+
Mapping:
|
|
266
|
+
- Info / FYI / context → \`📘\`
|
|
267
|
+
- Tip / pro tip / suggestion / success → \`👍\`
|
|
268
|
+
- Warning / caution / careful / limitations → \`🚧\`
|
|
269
|
+
- Critical / required / must / never / danger / breaking → \`❗️\`
|
|
270
|
+
|
|
271
|
+
### Forbidden (always remove or convert)
|
|
272
|
+
- MDX imports / exports (\`import X from ...\`, \`export const ...\`) — strip entirely
|
|
273
|
+
- HTML comments (\`<!-- ... -->\`)
|
|
274
|
+
- \`<script>\`, \`<style>\`, inline event handlers
|
|
275
|
+
- Literal unescaped \`{\`, \`}\`, \`<\`, \`>\` in prose — escape with a backslash or convert to entities
|
|
276
|
+
|
|
277
|
+
### Preservation rules
|
|
278
|
+
- The enhancements above are the ONLY allowed content transformations. Everything else is preserved verbatim.
|
|
279
|
+
- Never invent, summarize, or drop prose. Never change wording, code, or link targets.
|
|
280
|
+
- Heading hierarchy, code-block bodies (language tags intact), tables, and images stay exact — except when you're consuming them to build a component above (e.g., sibling headings → Tabs, link list → Cards).
|
|
281
|
+
- When unsure between two component upgrades for the same content, pick the one that requires less wrapping (Tabs > Cards > Accordion > callout > plain markdown).
|
|
282
|
+
- Don't upgrade so heavily that a page becomes mostly nested components. If you've made 5 upgrades, stop.
|
|
283
|
+
|
|
284
|
+
## API Reference Detection
|
|
285
|
+
A page is an API reference if it primarily documents REST/GraphQL/gRPC endpoints:
|
|
286
|
+
- Lists HTTP methods with paths (GET /api/users, POST /v1/orders)
|
|
287
|
+
- Documents request/response schemas, parameters, status codes
|
|
288
|
+
- Is an OpenAPI/Swagger specification or generated from one
|
|
289
|
+
- Has GraphQL type definitions (Query, Mutation, Subscription) with field docs
|
|
290
|
+
NOT API reference: tutorials mentioning APIs, authentication guides, SDK quickstarts, changelogs.
|
|
291
|
+
|
|
292
|
+
## OpenAPI Extraction (only when isApiRef=true)
|
|
293
|
+
If the page IS an API reference, extract the endpoints into a partial OpenAPI 3.0 fragment:
|
|
294
|
+
- Extract paths with their HTTP methods, parameters, request bodies, and response schemas
|
|
295
|
+
- Extract any reusable schema definitions (components/schemas)
|
|
296
|
+
- Use realistic types inferred from the documentation (don't use "object" or "string" for everything)
|
|
297
|
+
- Include the server/base URL if mentioned
|
|
298
|
+
|
|
299
|
+
## Output Format
|
|
300
|
+
Respond with ONLY a JSON object (no markdown fences, no explanation):
|
|
301
|
+
{
|
|
302
|
+
"excerpt": "one-line summary of the page",
|
|
303
|
+
"body": "the full MDX-fixed markdown content (no frontmatter)",
|
|
304
|
+
"isApiRef": true/false,
|
|
305
|
+
"suggestedCategory": "Best Category Name",
|
|
306
|
+
"oasPartial": null OR {"paths": {"/endpoint": {"get": {...}}}, "schemas": {"ModelName": {...}}}
|
|
307
|
+
}
|
|
308
|
+
When isApiRef=false, set oasPartial to null. When isApiRef=true, include the extracted paths and schemas.`;
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* @param {object} input
|
|
312
|
+
* @param {string} input.source The raw markdown body (no frontmatter).
|
|
313
|
+
* @param {string} [input.title] Optional page title hint from frontmatter.
|
|
314
|
+
* @param {string} [input.relativePath] Optional repo-relative path hint (helps the model anchor context).
|
|
315
|
+
* @returns {string}
|
|
316
|
+
*/
|
|
317
|
+
export function buildPrettifyPageUserPrompt({ source, title, relativePath }) {
|
|
318
|
+
const header = [];
|
|
319
|
+
if (relativePath) header.push(`Path: ${relativePath}`);
|
|
320
|
+
if (title) header.push(`Title: ${title}`);
|
|
321
|
+
return [
|
|
322
|
+
...(header.length ? [...header, ''] : []),
|
|
323
|
+
'Source markdown follows. Apply the rules and respond with the JSON object only.',
|
|
324
|
+
'',
|
|
325
|
+
source,
|
|
326
|
+
].join('\n');
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export function prettifyPagePrompt(input) {
|
|
330
|
+
return {
|
|
331
|
+
systemPrompt: prettifyPageSystemPrompt,
|
|
332
|
+
userPrompt: buildPrettifyPageUserPrompt(input),
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// ─── helpers ─────────────────────────────────────────────────────────────────
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Strip leading/trailing markdown code fences from a model response so the
|
|
340
|
+
* remaining text can be passed straight to JSON.parse. Models occasionally
|
|
341
|
+
* wrap output in ```json … ``` even when told not to.
|
|
342
|
+
*
|
|
343
|
+
* @param {string} text
|
|
344
|
+
* @returns {string}
|
|
345
|
+
*/
|
|
346
|
+
export function stripCodeFences(text) {
|
|
347
|
+
return String(text).trim()
|
|
348
|
+
.replace(/^```json\s*/i, '')
|
|
349
|
+
.replace(/^```\s*/i, '')
|
|
350
|
+
.replace(/\s*```$/, '')
|
|
351
|
+
.trim();
|
|
352
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
|
|
3
|
+
let _hasClaude;
|
|
4
|
+
|
|
5
|
+
export function hasClaude() {
|
|
6
|
+
if (_hasClaude === undefined) {
|
|
7
|
+
try {
|
|
8
|
+
execSync('which claude', { stdio: 'pipe' });
|
|
9
|
+
_hasClaude = true;
|
|
10
|
+
} catch {
|
|
11
|
+
_hasClaude = false;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return _hasClaude;
|
|
15
|
+
}
|