@forge-ts/gen 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,3 +1,761 @@
1
+ // src/adapters/registry.ts
2
+ var adapters = /* @__PURE__ */ new Map();
3
+ function registerAdapter(adapter) {
4
+ adapters.set(adapter.target, adapter);
5
+ }
6
+ function getAdapter(target) {
7
+ const adapter = adapters.get(target);
8
+ if (!adapter) {
9
+ const available = [...adapters.keys()].join(", ");
10
+ throw new Error(`Unknown SSG target "${target}". Available targets: ${available}`);
11
+ }
12
+ return adapter;
13
+ }
14
+ function getAvailableTargets() {
15
+ return [...adapters.keys()];
16
+ }
17
+ var DEFAULT_TARGET = "mintlify";
18
+
19
+ // src/adapters/mintlify.ts
20
+ var styleGuide = {
21
+ pageExtension: "mdx",
22
+ supportsMdx: true,
23
+ requiresFrontmatter: true,
24
+ maxHeadingDepth: 3,
25
+ defaultImports: [],
26
+ codeBlockLanguage: "typescript"
27
+ };
28
+ function pageSlug(pagePath) {
29
+ return pagePath.replace(/\.[^.]+$/, "");
30
+ }
31
+ function buildDocsJson(context) {
32
+ const { pages, projectName } = context;
33
+ const byPackage = /* @__PURE__ */ new Map();
34
+ for (const page of pages) {
35
+ const slug = pageSlug(page.path);
36
+ const packageMatch = /^packages\/([^/]+)\/(.+)$/.exec(slug);
37
+ if (packageMatch) {
38
+ const pkgName = packageMatch[1];
39
+ const list = byPackage.get(pkgName) ?? [];
40
+ list.push(slug);
41
+ byPackage.set(pkgName, list);
42
+ }
43
+ }
44
+ const tabs = [];
45
+ const docGroups = [];
46
+ docGroups.push({
47
+ group: "Getting Started",
48
+ pages: ["index", "getting-started"]
49
+ });
50
+ docGroups.push({
51
+ group: "Learn",
52
+ pages: ["concepts", "guides/index"]
53
+ });
54
+ docGroups.push({
55
+ group: "Reference",
56
+ pages: ["configuration", "changelog"]
57
+ });
58
+ docGroups.push({
59
+ group: "Community",
60
+ pages: ["faq", "contributing"]
61
+ });
62
+ tabs.push({ tab: "Documentation", groups: docGroups });
63
+ if (byPackage.size > 0) {
64
+ const apiGroups = [];
65
+ for (const [pkgName, slugs] of byPackage) {
66
+ const sorted = slugs.sort((a, b) => {
67
+ const order = (s) => {
68
+ if (s.endsWith("/index")) return 0;
69
+ if (s.includes("/api/index")) return 1;
70
+ if (s.includes("/api/functions")) return 2;
71
+ if (s.includes("/api/types")) return 3;
72
+ if (s.includes("/api/examples")) return 4;
73
+ return 5;
74
+ };
75
+ return order(a) - order(b);
76
+ });
77
+ apiGroups.push({ group: `@forge-ts/${pkgName}`, pages: sorted });
78
+ }
79
+ tabs.push({ tab: "API Reference", groups: apiGroups });
80
+ }
81
+ return {
82
+ name: projectName,
83
+ theme: "mint",
84
+ colors: { primary: "#0ea5e9", light: "#38bdf8", dark: "#0284c7" },
85
+ navigation: { tabs },
86
+ footer: {
87
+ socials: context.config.project.repository ? { github: context.config.project.repository } : {}
88
+ },
89
+ contextual: {
90
+ options: ["copy", "view", "chatgpt", "claude", "perplexity"]
91
+ }
92
+ };
93
+ }
94
+ function addMintlifyFrontmatter(page) {
95
+ const title = String(page.frontmatter.title ?? "");
96
+ const description = page.frontmatter.description ? String(page.frontmatter.description) : void 0;
97
+ const lines = ["---", `title: "${title}"`];
98
+ if (description) {
99
+ lines.push(`description: "${description}"`);
100
+ }
101
+ lines.push("---", "");
102
+ const body = page.content.replace(/^---[\s\S]*?---\n+/, "");
103
+ return lines.join("\n") + body;
104
+ }
105
+ var mintlifyAdapter = {
106
+ target: "mintlify",
107
+ displayName: "Mintlify",
108
+ styleGuide,
109
+ scaffold(context) {
110
+ const docsJson = buildDocsJson(context);
111
+ return {
112
+ target: "mintlify",
113
+ files: [{ path: "docs.json", content: JSON.stringify(docsJson, null, 2) }],
114
+ dependencies: {},
115
+ devDependencies: {},
116
+ scripts: {},
117
+ instructions: [
118
+ "Preview locally: npx @mintlify/cli dev",
119
+ "Deploy: Push to GitHub and connect at mintlify.com"
120
+ ]
121
+ };
122
+ },
123
+ transformPages(pages, _context) {
124
+ return pages.map((page) => {
125
+ let content = addMintlifyFrontmatter(page);
126
+ content = content.replace(/^# .+$/m, "").replace(/\n{3,}/g, "\n\n");
127
+ let insideFence = false;
128
+ content = content.split("\n").map((line) => {
129
+ if (insideFence) {
130
+ if (/^```\s*$/.test(line)) {
131
+ insideFence = false;
132
+ }
133
+ return line;
134
+ }
135
+ if (/^```\S/.test(line)) {
136
+ insideFence = true;
137
+ return line;
138
+ }
139
+ if (/^```\s*$/.test(line)) {
140
+ insideFence = true;
141
+ return "```typescript";
142
+ }
143
+ return line;
144
+ }).join("\n");
145
+ return {
146
+ path: page.path.replace(/\.md$/, ".mdx"),
147
+ content,
148
+ stub: page.stub
149
+ };
150
+ });
151
+ },
152
+ generateConfig(context) {
153
+ const config = buildDocsJson(context);
154
+ return [
155
+ {
156
+ path: "docs.json",
157
+ content: `${JSON.stringify(config, null, 2)}
158
+ `
159
+ }
160
+ ];
161
+ },
162
+ getDevCommand(outDir) {
163
+ return {
164
+ bin: "npx",
165
+ args: ["@mintlify/cli", "dev"],
166
+ cwd: outDir,
167
+ label: "Mintlify Dev Server",
168
+ url: "http://localhost:3000"
169
+ };
170
+ },
171
+ async detectExisting(outDir) {
172
+ const { existsSync: existsSync3 } = await import("fs");
173
+ const { join: join2 } = await import("path");
174
+ return existsSync3(join2(outDir, "docs.json"));
175
+ }
176
+ };
177
+ registerAdapter(mintlifyAdapter);
178
+
179
+ // src/adapters/docusaurus.ts
180
+ var styleGuide2 = {
181
+ pageExtension: "mdx",
182
+ supportsMdx: true,
183
+ requiresFrontmatter: true,
184
+ maxHeadingDepth: 4,
185
+ defaultImports: [],
186
+ codeBlockLanguage: "typescript"
187
+ };
188
+ function pageSlug2(pagePath) {
189
+ return pagePath.replace(/\.[^.]+$/, "");
190
+ }
191
+ function buildSidebarsTs(pages) {
192
+ const topLevel = [];
193
+ const byPackage = /* @__PURE__ */ new Map();
194
+ for (const page of pages) {
195
+ const slug = pageSlug2(page.path);
196
+ const packageMatch = /^packages\/([^/]+)\/(.+)$/.exec(slug);
197
+ if (packageMatch) {
198
+ const pkgName = packageMatch[1];
199
+ const list = byPackage.get(pkgName) ?? [];
200
+ list.push(slug);
201
+ byPackage.set(pkgName, list);
202
+ } else {
203
+ topLevel.push(slug);
204
+ }
205
+ }
206
+ const items = [...topLevel];
207
+ for (const [pkgName, slugs] of byPackage) {
208
+ items.push({
209
+ type: "category",
210
+ label: pkgName,
211
+ items: slugs
212
+ });
213
+ }
214
+ const sidebarObj = { docs: items };
215
+ const json = JSON.stringify(sidebarObj, null, 2);
216
+ return `import type { SidebarsConfig } from "@docusaurus/plugin-content-docs";
217
+
218
+ const sidebars: SidebarsConfig = ${json};
219
+
220
+ export default sidebars;
221
+ `;
222
+ }
223
+ function buildDocusaurusConfig(context) {
224
+ const { projectName, projectDescription } = context;
225
+ const description = projectDescription ?? `${projectName} documentation`;
226
+ return `import { themes as prismThemes } from "prism-react-renderer";
227
+ import type { Config } from "@docusaurus/types";
228
+ import type * as Preset from "@docusaurus/preset-classic";
229
+
230
+ const config: Config = {
231
+ title: "${projectName}",
232
+ tagline: "${description}",
233
+ url: "https://your-domain.com",
234
+ baseUrl: "/",
235
+ onBrokenLinks: "throw",
236
+ onBrokenMarkdownLinks: "warn",
237
+ i18n: {
238
+ defaultLocale: "en",
239
+ locales: ["en"],
240
+ },
241
+ presets: [
242
+ [
243
+ "classic",
244
+ {
245
+ docs: {
246
+ routeBasePath: "/",
247
+ sidebarPath: "./sidebars.ts",
248
+ },
249
+ blog: false,
250
+ } satisfies Preset.Options,
251
+ ],
252
+ ],
253
+ themeConfig: {
254
+ prism: {
255
+ theme: prismThemes.github,
256
+ darkTheme: prismThemes.dracula,
257
+ },
258
+ } satisfies Preset.ThemeConfig,
259
+ } satisfies Config;
260
+
261
+ export default config;
262
+ `;
263
+ }
264
+ function buildPackageJson(context) {
265
+ const pkg = {
266
+ name: `${context.projectName}-docs`,
267
+ version: "0.0.0",
268
+ private: true,
269
+ scripts: {
270
+ "docs:dev": "docusaurus start",
271
+ "docs:build": "docusaurus build",
272
+ "docs:serve": "docusaurus serve"
273
+ },
274
+ dependencies: {
275
+ "@docusaurus/core": "^3.9.2",
276
+ "@docusaurus/preset-classic": "^3.9.2",
277
+ "@mdx-js/react": "^3.0.0",
278
+ react: "^19.0.0",
279
+ "react-dom": "^19.0.0",
280
+ "prism-react-renderer": "^2.3.0"
281
+ },
282
+ devDependencies: {
283
+ "@docusaurus/tsconfig": "^3.9.2",
284
+ "@docusaurus/types": "^3.9.2"
285
+ }
286
+ };
287
+ return `${JSON.stringify(pkg, null, 2)}
288
+ `;
289
+ }
290
+ function addDocusaurusFrontmatter(page) {
291
+ const title = String(page.frontmatter.title ?? "");
292
+ const description = page.frontmatter.description ? String(page.frontmatter.description) : void 0;
293
+ const sidebarLabel = page.frontmatter.sidebar_label ? String(page.frontmatter.sidebar_label) : title;
294
+ const sidebarPosition = page.frontmatter.sidebar_position;
295
+ const lines = ["---", `title: "${title}"`, `sidebar_label: "${sidebarLabel}"`];
296
+ if (sidebarPosition !== void 0) {
297
+ lines.push(`sidebar_position: ${sidebarPosition}`);
298
+ }
299
+ if (description) {
300
+ lines.push(`description: "${description}"`);
301
+ }
302
+ lines.push("---", "");
303
+ const body = page.content.replace(/^---[\s\S]*?---\n+/, "");
304
+ return lines.join("\n") + body;
305
+ }
306
+ var docusaurusAdapter = {
307
+ target: "docusaurus",
308
+ displayName: "Docusaurus",
309
+ styleGuide: styleGuide2,
310
+ scaffold(context) {
311
+ return {
312
+ target: "docusaurus",
313
+ files: [
314
+ { path: "docusaurus.config.ts", content: buildDocusaurusConfig(context) },
315
+ { path: "sidebars.ts", content: buildSidebarsTs(context.pages) },
316
+ { path: "package.json", content: buildPackageJson(context) },
317
+ { path: "tsconfig.json", content: '{ "extends": "@docusaurus/tsconfig" }\n' }
318
+ ],
319
+ dependencies: {
320
+ "@docusaurus/core": "^3.9.2",
321
+ "@docusaurus/preset-classic": "^3.9.2",
322
+ "@mdx-js/react": "^3.0.0",
323
+ react: "^19.0.0",
324
+ "react-dom": "^19.0.0",
325
+ "prism-react-renderer": "^2.3.0"
326
+ },
327
+ devDependencies: {
328
+ "@docusaurus/tsconfig": "^3.9.2",
329
+ "@docusaurus/types": "^3.9.2"
330
+ },
331
+ scripts: {
332
+ "docs:dev": "docusaurus start",
333
+ "docs:build": "docusaurus build",
334
+ "docs:serve": "docusaurus serve"
335
+ },
336
+ instructions: [`Run \`npm run docs:dev\` inside ${context.outDir} to preview your docs`]
337
+ };
338
+ },
339
+ transformPages(pages, _context) {
340
+ return pages.map((page) => ({
341
+ path: page.path.replace(/\.md$/, ".mdx"),
342
+ content: addDocusaurusFrontmatter(page),
343
+ stub: page.stub
344
+ }));
345
+ },
346
+ generateConfig(context) {
347
+ return [
348
+ {
349
+ path: "sidebars.ts",
350
+ content: buildSidebarsTs(context.pages)
351
+ }
352
+ ];
353
+ },
354
+ getDevCommand(outDir) {
355
+ return {
356
+ bin: "npx",
357
+ args: ["docusaurus", "start"],
358
+ cwd: outDir,
359
+ label: "Docusaurus Dev Server",
360
+ url: "http://localhost:3000"
361
+ };
362
+ },
363
+ async detectExisting(outDir) {
364
+ const { existsSync: existsSync3 } = await import("fs");
365
+ const { join: join2 } = await import("path");
366
+ return existsSync3(join2(outDir, "sidebars.ts")) || existsSync3(join2(outDir, "docusaurus.config.ts")) || existsSync3(join2(outDir, "docusaurus.config.js"));
367
+ }
368
+ };
369
+ registerAdapter(docusaurusAdapter);
370
+
371
+ // src/adapters/nextra.ts
372
+ var styleGuide3 = {
373
+ pageExtension: "mdx",
374
+ supportsMdx: true,
375
+ requiresFrontmatter: false,
376
+ maxHeadingDepth: 4,
377
+ defaultImports: [],
378
+ codeBlockLanguage: "typescript"
379
+ };
380
+ function pageSlug3(pagePath) {
381
+ return pagePath.replace(/\.[^.]+$/, "");
382
+ }
383
+ function slugToLabel(slug) {
384
+ return slug.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
385
+ }
386
+ function renderMetaJs(meta) {
387
+ const json = JSON.stringify(meta, null, 2);
388
+ return `export default ${json};
389
+ `;
390
+ }
391
+ function buildMetaFiles(pages) {
392
+ const topLevel = [];
393
+ const byPackage = /* @__PURE__ */ new Map();
394
+ for (const page of pages) {
395
+ const slug = pageSlug3(page.path);
396
+ const packageMatch = /^packages\/([^/]+)\/(.+)$/.exec(slug);
397
+ if (packageMatch) {
398
+ const pkgName = packageMatch[1];
399
+ const list = byPackage.get(pkgName) ?? [];
400
+ list.push(slug);
401
+ byPackage.set(pkgName, list);
402
+ } else {
403
+ topLevel.push(slug);
404
+ }
405
+ }
406
+ const files = [];
407
+ const rootMeta = {};
408
+ for (const slug of topLevel) {
409
+ const segment = slug.split("/").pop() ?? slug;
410
+ rootMeta[segment] = slugToLabel(segment);
411
+ }
412
+ if (byPackage.size > 0) {
413
+ rootMeta.packages = "Packages";
414
+ }
415
+ files.push({
416
+ path: "content/_meta.js",
417
+ content: renderMetaJs(rootMeta)
418
+ });
419
+ for (const [pkgName, slugs] of byPackage) {
420
+ const pkgMeta = {};
421
+ for (const slug of slugs) {
422
+ const segment = slug.split("/").pop() ?? slug;
423
+ pkgMeta[segment] = slugToLabel(segment);
424
+ }
425
+ files.push({
426
+ path: `content/packages/${pkgName}/_meta.js`,
427
+ content: renderMetaJs(pkgMeta)
428
+ });
429
+ }
430
+ if (byPackage.size > 1) {
431
+ const packagesMeta = {};
432
+ for (const pkgName of byPackage.keys()) {
433
+ packagesMeta[pkgName] = pkgName;
434
+ }
435
+ files.push({
436
+ path: "content/packages/_meta.js",
437
+ content: renderMetaJs(packagesMeta)
438
+ });
439
+ }
440
+ return files;
441
+ }
442
+ function buildAppLayout(context) {
443
+ const { projectName } = context;
444
+ return `import { Layout } from "nextra-theme-docs";
445
+ import { Head } from "nextra/components";
446
+ import { getPageMap } from "nextra/page-map";
447
+ import type { ReactNode } from "react";
448
+
449
+ export const metadata = {
450
+ title: {
451
+ template: \`%s \u2013 ${projectName}\`,
452
+ },
453
+ };
454
+
455
+ export default async function RootLayout({ children }: { children: ReactNode }) {
456
+ const pageMap = await getPageMap();
457
+ return (
458
+ <html lang="en" suppressHydrationWarning>
459
+ <Head />
460
+ <body>
461
+ <Layout pageMap={pageMap} docsRepositoryBase="">
462
+ {children}
463
+ </Layout>
464
+ </body>
465
+ </html>
466
+ );
467
+ }
468
+ `;
469
+ }
470
+ function buildCatchAllPage() {
471
+ return `import { generateStaticParamsFor, importPage } from "nextra/pages";
472
+
473
+ export const generateStaticParams = generateStaticParamsFor("mdxPath");
474
+
475
+ export async function generateMetadata(props: {
476
+ params: Promise<Record<string, string[]>>;
477
+ }) {
478
+ const params = await props.params;
479
+ const { metadata } = await importPage(params.mdxPath);
480
+ return metadata;
481
+ }
482
+
483
+ const Wrapper = importPage;
484
+ export default async function Page(props: {
485
+ params: Promise<Record<string, string[]>>;
486
+ }) {
487
+ const params = await props.params;
488
+ const result = await importPage(params.mdxPath);
489
+ const { default: MDXContent } = result;
490
+ return <MDXContent {...props} params={params} />;
491
+ }
492
+ `;
493
+ }
494
+ function buildMdxComponents() {
495
+ return `import { useMDXComponents as getNextraComponents } from "nextra-theme-docs";
496
+
497
+ export function useMDXComponents(components: Record<string, unknown>) {
498
+ return getNextraComponents(components);
499
+ }
500
+ `;
501
+ }
502
+ function buildNextConfig() {
503
+ return `import nextra from "nextra";
504
+
505
+ const withNextra = nextra({
506
+ contentDirBasePath: "/",
507
+ });
508
+
509
+ export default withNextra({
510
+ output: "export",
511
+ images: { unoptimized: true },
512
+ });
513
+ `;
514
+ }
515
+ function buildPackageJson2(context) {
516
+ const pkg = {
517
+ name: `${context.projectName}-docs`,
518
+ version: "0.0.0",
519
+ private: true,
520
+ scripts: {
521
+ "docs:dev": "next dev",
522
+ "docs:build": "next build",
523
+ "docs:serve": "next start"
524
+ },
525
+ dependencies: {
526
+ next: "^15",
527
+ nextra: "^4",
528
+ "nextra-theme-docs": "^4",
529
+ react: "^19",
530
+ "react-dom": "^19"
531
+ }
532
+ };
533
+ return `${JSON.stringify(pkg, null, 2)}
534
+ `;
535
+ }
536
+ function addNextraFrontmatter(page) {
537
+ const title = String(page.frontmatter.title ?? "");
538
+ if (!title) {
539
+ return page.content;
540
+ }
541
+ const lines = ["---", `title: "${title}"`, "---", ""];
542
+ const body = page.content.replace(/^---[\s\S]*?---\n+/, "");
543
+ return lines.join("\n") + body;
544
+ }
545
+ var nextraAdapter = {
546
+ target: "nextra",
547
+ displayName: "Nextra",
548
+ styleGuide: styleGuide3,
549
+ scaffold(context) {
550
+ const metaFiles = buildMetaFiles(context.pages);
551
+ return {
552
+ target: "nextra",
553
+ files: [
554
+ { path: "next.config.ts", content: buildNextConfig() },
555
+ { path: "package.json", content: buildPackageJson2(context) },
556
+ { path: "mdx-components.ts", content: buildMdxComponents() },
557
+ { path: "app/layout.tsx", content: buildAppLayout(context) },
558
+ { path: "app/[[...mdxPath]]/page.tsx", content: buildCatchAllPage() },
559
+ ...metaFiles
560
+ ],
561
+ dependencies: {
562
+ next: "^15",
563
+ nextra: "^4",
564
+ "nextra-theme-docs": "^4",
565
+ react: "^19",
566
+ "react-dom": "^19"
567
+ },
568
+ devDependencies: {},
569
+ scripts: {
570
+ "docs:dev": "next dev",
571
+ "docs:build": "next build",
572
+ "docs:serve": "next start"
573
+ },
574
+ instructions: [`Run \`npm run docs:dev\` inside ${context.outDir} to preview your docs`]
575
+ };
576
+ },
577
+ transformPages(pages, _context) {
578
+ return pages.map((page) => ({
579
+ path: page.path.replace(/\.md$/, ".mdx"),
580
+ content: addNextraFrontmatter(page),
581
+ stub: page.stub
582
+ }));
583
+ },
584
+ generateConfig(context) {
585
+ return buildMetaFiles(context.pages);
586
+ },
587
+ getDevCommand(outDir) {
588
+ return {
589
+ bin: "npx",
590
+ args: ["next", "dev"],
591
+ cwd: outDir,
592
+ label: "Nextra Dev Server (Next.js)",
593
+ url: "http://localhost:3000"
594
+ };
595
+ },
596
+ async detectExisting(outDir) {
597
+ const { existsSync: existsSync3 } = await import("fs");
598
+ const { join: join2 } = await import("path");
599
+ return existsSync3(join2(outDir, "content/_meta.js")) || existsSync3(join2(outDir, "next.config.ts")) || existsSync3(join2(outDir, "next.config.js"));
600
+ }
601
+ };
602
+ registerAdapter(nextraAdapter);
603
+
604
+ // src/adapters/vitepress.ts
605
+ var styleGuide4 = {
606
+ pageExtension: "md",
607
+ supportsMdx: false,
608
+ requiresFrontmatter: true,
609
+ maxHeadingDepth: 4,
610
+ defaultImports: [],
611
+ codeBlockLanguage: "typescript"
612
+ };
613
+ function pageSlug4(pagePath) {
614
+ return pagePath.replace(/\.[^.]+$/, "");
615
+ }
616
+ function slugToLabel2(slug) {
617
+ return slug.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
618
+ }
619
+ function buildSidebar(pages) {
620
+ const topLevel = [];
621
+ const byPackage = /* @__PURE__ */ new Map();
622
+ for (const page of pages) {
623
+ const slug = pageSlug4(page.path);
624
+ const packageMatch = /^packages\/([^/]+)\/(.+)$/.exec(slug);
625
+ if (packageMatch) {
626
+ const pkgName = packageMatch[1];
627
+ const list = byPackage.get(pkgName) ?? [];
628
+ list.push(slug);
629
+ byPackage.set(pkgName, list);
630
+ } else {
631
+ topLevel.push(slug);
632
+ }
633
+ }
634
+ const sidebar = [];
635
+ if (topLevel.length > 0) {
636
+ const items = topLevel.map((slug) => {
637
+ const segment = slug.split("/").pop() ?? slug;
638
+ const link = slug === "index" ? "/" : `/${slug}`;
639
+ return { text: slugToLabel2(segment), link };
640
+ });
641
+ sidebar.push({ text: "Getting Started", items });
642
+ }
643
+ for (const [pkgName, slugs] of byPackage) {
644
+ const items = slugs.map((slug) => {
645
+ const segment = slug.split("/").pop() ?? slug;
646
+ const isIndex = segment === "index";
647
+ const link = isIndex ? `/packages/${pkgName}/` : `/${slug}`;
648
+ return { text: slugToLabel2(segment), link };
649
+ });
650
+ sidebar.push({ text: pkgName, items });
651
+ }
652
+ return sidebar;
653
+ }
654
+ function buildVitePressConfig(context) {
655
+ const { projectName, projectDescription } = context;
656
+ const description = projectDescription ?? `${projectName} documentation`;
657
+ const sidebar = buildSidebar(context.pages);
658
+ const sidebarJson = JSON.stringify(sidebar, null, 4).split("\n").join("\n ");
659
+ return `import { defineConfig } from "vitepress";
660
+
661
+ // https://vitepress.dev/reference/site-config
662
+ export default defineConfig({
663
+ title: "${projectName}",
664
+ description: "${description}",
665
+ themeConfig: {
666
+ nav: [
667
+ { text: "Home", link: "/" },
668
+ ],
669
+ sidebar: ${sidebarJson},
670
+ socialLinks: [],
671
+ },
672
+ });
673
+ `;
674
+ }
675
+ function buildPackageJson3(context) {
676
+ const pkg = {
677
+ name: `${context.projectName}-docs`,
678
+ version: "0.0.0",
679
+ private: true,
680
+ type: "module",
681
+ scripts: {
682
+ "docs:dev": "vitepress dev",
683
+ "docs:build": "vitepress build",
684
+ "docs:preview": "vitepress preview"
685
+ },
686
+ devDependencies: {
687
+ vitepress: "^2.0.0"
688
+ }
689
+ };
690
+ return `${JSON.stringify(pkg, null, 2)}
691
+ `;
692
+ }
693
+ function addVitepressFrontmatter(page) {
694
+ const title = String(page.frontmatter.title ?? "");
695
+ const description = page.frontmatter.description ? String(page.frontmatter.description) : void 0;
696
+ const lines = ["---", `title: "${title}"`, "outline: deep"];
697
+ if (description) {
698
+ lines.push(`description: "${description}"`);
699
+ }
700
+ lines.push("---", "");
701
+ const body = page.content.replace(/^---[\s\S]*?---\n+/, "");
702
+ return lines.join("\n") + body;
703
+ }
704
+ var vitepressAdapter = {
705
+ target: "vitepress",
706
+ displayName: "VitePress",
707
+ styleGuide: styleGuide4,
708
+ scaffold(context) {
709
+ return {
710
+ target: "vitepress",
711
+ files: [
712
+ { path: ".vitepress/config.mts", content: buildVitePressConfig(context) },
713
+ { path: "package.json", content: buildPackageJson3(context) }
714
+ ],
715
+ dependencies: {},
716
+ devDependencies: {
717
+ vitepress: "^2.0.0"
718
+ },
719
+ scripts: {
720
+ "docs:dev": "vitepress dev",
721
+ "docs:build": "vitepress build",
722
+ "docs:preview": "vitepress preview"
723
+ },
724
+ instructions: [`Run \`npm run docs:dev\` inside ${context.outDir} to preview your docs`]
725
+ };
726
+ },
727
+ transformPages(pages, _context) {
728
+ return pages.map((page) => ({
729
+ path: page.path.endsWith(".mdx") ? page.path.replace(/\.mdx$/, ".md") : page.path,
730
+ content: addVitepressFrontmatter(page),
731
+ stub: page.stub
732
+ }));
733
+ },
734
+ generateConfig(context) {
735
+ return [
736
+ {
737
+ path: ".vitepress/config.mts",
738
+ content: buildVitePressConfig(context)
739
+ }
740
+ ];
741
+ },
742
+ getDevCommand(outDir) {
743
+ return {
744
+ bin: "npx",
745
+ args: ["vitepress", "dev"],
746
+ cwd: outDir,
747
+ label: "VitePress Dev Server",
748
+ url: "http://localhost:5173"
749
+ };
750
+ },
751
+ async detectExisting(outDir) {
752
+ const { existsSync: existsSync3 } = await import("fs");
753
+ const { join: join2 } = await import("path");
754
+ return existsSync3(join2(outDir, ".vitepress"));
755
+ }
756
+ };
757
+ registerAdapter(vitepressAdapter);
758
+
1
759
  // src/llms.ts
2
760
  function compactEntry(symbol) {
3
761
  if (symbol.signature) {
@@ -313,7 +1071,7 @@ function generateMarkdown(symbols, config, options = {}) {
313
1071
  }
314
1072
  parts.push("# API Reference\n");
315
1073
  parts.push(
316
- `Generated by [forge-ts](https://github.com/forge-ts/forge-ts) from \`${config.rootDir}\`.
1074
+ `Generated by [forge-ts](https://github.com/kryptobaseddev/forge-ts) from \`${config.rootDir}\`.
317
1075
  `
318
1076
  );
319
1077
  if (topLevel.length > 0) {
@@ -462,6 +1220,15 @@ function buildFrontmatterFields(title, description, ssgTarget, sidebarPosition)
462
1220
  return {};
463
1221
  }
464
1222
  }
1223
+ function slugLink(path) {
1224
+ let slug = path.startsWith("./") ? path.slice(2) : path;
1225
+ slug = slug.replace(/\.(mdx?)$/, "");
1226
+ return `/${slug}`;
1227
+ }
1228
+ function truncate(text, maxLen = 80) {
1229
+ if (text.length <= maxLen) return text;
1230
+ return `${text.slice(0, maxLen - 3)}...`;
1231
+ }
465
1232
  function groupSymbolsByPackage(symbols, rootDir) {
466
1233
  const result = /* @__PURE__ */ new Map();
467
1234
  for (const symbol of symbols) {
@@ -476,55 +1243,295 @@ function groupSymbolsByPackage(symbols, rootDir) {
476
1243
  }
477
1244
  var TYPE_KINDS = /* @__PURE__ */ new Set(["interface", "type", "enum"]);
478
1245
  var FUNCTION_KINDS = /* @__PURE__ */ new Set(["function", "class"]);
479
- function renderPropertyRow(child) {
480
- const name = `\`${child.name}\``;
481
- const type = child.signature ? `\`${escapePipe(child.signature)}\`` : "\u2014";
482
- const optional = child.signature?.includes("?") || child.signature?.includes("undefined") ? "No" : "Yes";
483
- const description = escapePipe(child.documentation?.summary ?? "");
484
- return `| ${name} | ${type} | ${optional} | ${description} |`;
485
- }
486
- function renderOverviewPage(packageName, symbols, _options) {
487
- const exported = symbols.filter(
488
- (s) => s.exported && s.kind !== "method" && s.kind !== "property"
489
- );
490
- const pkgDoc = symbols.map((s) => s.documentation?.tags?.packageDocumentation?.[0]).find(Boolean);
1246
+ function renderProjectIndexPage(symbolsByPackage, options) {
491
1247
  const lines = [];
492
- lines.push(`# ${packageName}`);
493
- lines.push("");
494
- if (pkgDoc) {
495
- lines.push(pkgDoc);
496
- lines.push("");
1248
+ if (options.projectDescription) {
1249
+ lines.push(`**${options.projectName}** \u2014 ${options.projectDescription}`);
1250
+ } else {
1251
+ lines.push(
1252
+ `**${options.projectName}** is a TypeScript documentation toolkit that performs a single AST traversal of your project and produces API docs, OpenAPI specs, executable doctests, and AI context files in one pass.`
1253
+ );
497
1254
  }
498
- if (exported.length > 0) {
499
- lines.push("## Exported Symbols");
500
- lines.push("");
501
- lines.push("| Symbol | Kind | Description |");
502
- lines.push("|--------|------|-------------|");
503
- for (const s of exported) {
504
- const ext = s.kind === "function" ? "()" : "";
505
- const name = `[\`${s.name}${ext}\`](./api-reference.md#${toAnchor2(`${s.name}${ext}`)})`;
506
- const summary = escapePipe(s.documentation?.summary ?? "");
507
- lines.push(`| ${name} | ${s.kind} | ${summary} |`);
508
- }
509
- lines.push("");
1255
+ lines.push("");
1256
+ lines.push("## Features");
1257
+ lines.push("");
1258
+ const pkgCount = symbolsByPackage.size;
1259
+ if (pkgCount > 1) {
1260
+ lines.push(`- ${pkgCount} packages with full TypeScript support`);
1261
+ } else {
1262
+ lines.push("- Full TypeScript support with TSDoc extraction");
510
1263
  }
511
- return lines.join("\n");
512
- }
513
- function renderTypesPage(packageName, symbols, _options) {
514
- const typeSymbols = symbols.filter((s) => s.exported && TYPE_KINDS.has(s.kind));
515
- const lines = [];
516
- lines.push(`# ${packageName} \u2014 Types`);
1264
+ lines.push("- Auto-generated API reference from source code");
1265
+ lines.push("- Executable `@example` blocks as doctests");
1266
+ lines.push("- AI-ready context files from a single build pass");
517
1267
  lines.push("");
518
- lines.push("Type contracts exported by this package: interfaces, type aliases, and enums.");
1268
+ lines.push("## Installation");
519
1269
  lines.push("");
520
- for (const s of typeSymbols) {
521
- lines.push(`## ${s.name}`);
522
- lines.push("");
523
- if (s.documentation?.deprecated) {
524
- lines.push(`> **Deprecated**: ${s.documentation.deprecated}`);
525
- lines.push("");
526
- }
527
- if (s.documentation?.summary) {
1270
+ lines.push("```bash");
1271
+ lines.push(`npm install -D ${options.packageName ?? "@forge-ts/cli"}`);
1272
+ lines.push("```");
1273
+ lines.push("");
1274
+ let firstExample;
1275
+ outer: for (const [, symbols] of symbolsByPackage) {
1276
+ for (const s of symbols) {
1277
+ if (!s.exported || s.kind !== "function") continue;
1278
+ const ex = s.documentation?.examples?.[0];
1279
+ if (ex) {
1280
+ firstExample = ex;
1281
+ break outer;
1282
+ }
1283
+ }
1284
+ }
1285
+ if (firstExample) {
1286
+ lines.push("## Quick Example");
1287
+ lines.push("");
1288
+ lines.push(`\`\`\`${firstExample.language || "typescript"}`);
1289
+ lines.push(firstExample.code.trim());
1290
+ lines.push("```");
1291
+ lines.push("");
1292
+ }
1293
+ if (symbolsByPackage.size > 0) {
1294
+ lines.push("## Packages");
1295
+ lines.push("");
1296
+ lines.push("| Package | Description |");
1297
+ lines.push("|---------|-------------|");
1298
+ for (const [pkgName, symbols] of symbolsByPackage) {
1299
+ const pkgDoc = symbols.map((s) => s.documentation?.tags?.packageDocumentation?.[0]).find(Boolean);
1300
+ const exported = symbols.filter(
1301
+ (s) => s.exported && s.kind !== "method" && s.kind !== "property"
1302
+ );
1303
+ const rawDesc = pkgDoc ?? `${exported.length} exported symbol(s).`;
1304
+ const desc = escapePipe(truncate(rawDesc));
1305
+ const link = `[\`${pkgName}\`](${slugLink(`packages/${pkgName}/index`)})`;
1306
+ lines.push(`| ${link} | ${desc} |`);
1307
+ }
1308
+ lines.push("");
1309
+ }
1310
+ lines.push("## Next Steps");
1311
+ lines.push("");
1312
+ lines.push(`- [Getting Started](/getting-started) \u2014 Step-by-step guide`);
1313
+ lines.push(`- [API Reference](/packages) \u2014 Full API documentation`);
1314
+ lines.push(`- [Concepts](/concepts) \u2014 How it works`);
1315
+ return lines.join("\n");
1316
+ }
1317
+ function renderGettingStartedPage(symbolsByPackage, options) {
1318
+ let firstExample;
1319
+ outer: for (const [, symbols] of symbolsByPackage) {
1320
+ for (const s of symbols) {
1321
+ if (!s.exported || s.kind !== "function") continue;
1322
+ const ex = s.documentation?.examples?.[0];
1323
+ if (ex) {
1324
+ firstExample = ex;
1325
+ break outer;
1326
+ }
1327
+ }
1328
+ }
1329
+ const lines = [];
1330
+ lines.push(`Get up and running with **${options.projectName}** in minutes.`);
1331
+ if (options.projectDescription) {
1332
+ lines.push("");
1333
+ lines.push(options.projectDescription);
1334
+ }
1335
+ lines.push("");
1336
+ lines.push("## Step 1: Install");
1337
+ lines.push("");
1338
+ lines.push("```bash");
1339
+ lines.push(`npm install -D ${options.packageName ?? "@forge-ts/cli"}`);
1340
+ lines.push("```");
1341
+ lines.push("");
1342
+ lines.push("## Step 2: Add TSDoc to your code");
1343
+ lines.push("");
1344
+ lines.push("Add TSDoc comments to your exported functions and types:");
1345
+ lines.push("");
1346
+ lines.push("```typescript");
1347
+ lines.push("/**");
1348
+ lines.push(" * Adds two numbers together.");
1349
+ lines.push(" * @param a - First number");
1350
+ lines.push(" * @param b - Second number");
1351
+ lines.push(" * @returns The sum of a and b");
1352
+ lines.push(" * @example");
1353
+ lines.push(" * ```typescript");
1354
+ lines.push(" * const result = add(1, 2); // => 3");
1355
+ lines.push(" * ```");
1356
+ lines.push(" */");
1357
+ lines.push("export function add(a: number, b: number): number {");
1358
+ lines.push(" return a + b;");
1359
+ lines.push("}");
1360
+ lines.push("```");
1361
+ lines.push("");
1362
+ lines.push("## Step 3: Run forge-ts check");
1363
+ lines.push("");
1364
+ lines.push("Lint your TSDoc coverage before generating docs:");
1365
+ lines.push("");
1366
+ lines.push("```bash");
1367
+ lines.push("npx forge-ts check");
1368
+ lines.push("```");
1369
+ lines.push("");
1370
+ lines.push("Expected output:");
1371
+ lines.push("");
1372
+ lines.push("```");
1373
+ lines.push("forge-ts: checking TSDoc coverage...");
1374
+ lines.push(" \u2713 All public symbols documented");
1375
+ lines.push("```");
1376
+ lines.push("");
1377
+ lines.push("## Step 4: Generate docs");
1378
+ lines.push("");
1379
+ lines.push("Build your documentation site:");
1380
+ lines.push("");
1381
+ lines.push("```bash");
1382
+ lines.push("npx forge-ts build");
1383
+ lines.push("```");
1384
+ lines.push("");
1385
+ if (firstExample) {
1386
+ lines.push("Your code examples become live documentation:");
1387
+ lines.push("");
1388
+ lines.push(`\`\`\`${firstExample.language || "typescript"}`);
1389
+ lines.push(firstExample.code.trim());
1390
+ lines.push("```");
1391
+ lines.push("");
1392
+ }
1393
+ lines.push("## What's Next?");
1394
+ lines.push("");
1395
+ lines.push("- [Concepts](/concepts) \u2014 Understand how forge-ts works");
1396
+ lines.push("- [API Reference](/packages) \u2014 Full API documentation");
1397
+ lines.push("- [Guides](/guides) \u2014 Practical how-to guides");
1398
+ return lines.join("\n");
1399
+ }
1400
+ function renderConceptsPage(symbolsByPackage, options) {
1401
+ const lines = [];
1402
+ lines.push(`This page explains the core concepts behind **${options.projectName}**.`);
1403
+ lines.push("");
1404
+ lines.push(
1405
+ "> This is a stub page. Edit this file to add your project's conceptual documentation."
1406
+ );
1407
+ lines.push("> Auto-generated sections below (inside FORGE:AUTO markers) update on every build.");
1408
+ lines.push("");
1409
+ const pkgDoc = [...symbolsByPackage.values()].flatMap((syms) => syms.map((s) => s.documentation?.tags?.packageDocumentation?.[0])).find(Boolean);
1410
+ lines.push("<!-- FORGE:AUTO-START how-it-works -->");
1411
+ lines.push("## How It Works");
1412
+ lines.push("");
1413
+ if (pkgDoc) {
1414
+ lines.push(pkgDoc);
1415
+ } else {
1416
+ lines.push(
1417
+ `${options.projectName} processes your TypeScript source with a single AST traversal, extracting TSDoc comments and type information to generate documentation.`
1418
+ );
1419
+ }
1420
+ lines.push("");
1421
+ lines.push("<!-- FORGE:AUTO-END how-it-works -->");
1422
+ lines.push("");
1423
+ const allTypeSymbols = [...symbolsByPackage.values()].flat().filter((s) => s.exported && TYPE_KINDS.has(s.kind));
1424
+ if (allTypeSymbols.length > 0) {
1425
+ lines.push("<!-- FORGE:AUTO-START key-abstractions -->");
1426
+ lines.push("## Key Abstractions");
1427
+ lines.push("");
1428
+ for (const s of allTypeSymbols) {
1429
+ const desc = s.documentation?.summary ?? `The \`${s.name}\` ${s.kind}.`;
1430
+ lines.push(`- **\`${s.name}\`** \u2014 ${desc}`);
1431
+ }
1432
+ lines.push("");
1433
+ lines.push("<!-- FORGE:AUTO-END key-abstractions -->");
1434
+ lines.push("");
1435
+ }
1436
+ return lines.join("\n");
1437
+ }
1438
+ function renderGuidesIndexPage() {
1439
+ return [
1440
+ "Practical how-to guides for common tasks.",
1441
+ "",
1442
+ "> Add your guides to the `guides/` directory. Each `.md` or `.mdx` file will appear here automatically.",
1443
+ "",
1444
+ "## Getting Things Done",
1445
+ "",
1446
+ "Guides will appear here as you add them. Start by creating a file like `guides/my-guide.md`.",
1447
+ ""
1448
+ ].join("\n");
1449
+ }
1450
+ function renderApiIndexPage(pkgName, symbols) {
1451
+ const exported = symbols.filter(
1452
+ (s) => s.exported && s.kind !== "method" && s.kind !== "property"
1453
+ );
1454
+ const functions = exported.filter((s) => FUNCTION_KINDS.has(s.kind));
1455
+ const types = exported.filter((s) => TYPE_KINDS.has(s.kind));
1456
+ const others = exported.filter((s) => !FUNCTION_KINDS.has(s.kind) && !TYPE_KINDS.has(s.kind));
1457
+ const lines = [];
1458
+ const pkgDoc = symbols.map((s) => s.documentation?.tags?.packageDocumentation?.[0]).find(Boolean);
1459
+ if (pkgDoc) {
1460
+ lines.push(pkgDoc);
1461
+ lines.push("");
1462
+ } else {
1463
+ lines.push(`API reference for the \`${pkgName}\` package.`);
1464
+ lines.push("");
1465
+ }
1466
+ const renderGroup = (group, heading, pathSuffix) => {
1467
+ if (group.length === 0) return;
1468
+ lines.push(`## ${heading}`);
1469
+ lines.push("");
1470
+ lines.push("| Symbol | Kind | Description |");
1471
+ lines.push("|--------|------|-------------|");
1472
+ for (const s of group) {
1473
+ const ext = s.kind === "function" ? "()" : "";
1474
+ const anchor = toAnchor2(`${s.name}${ext}`);
1475
+ const link = `[\`${s.name}${ext}\`](${slugLink(`packages/${pkgName}/${pathSuffix}`)}#${anchor})`;
1476
+ const rawSummary = s.documentation?.summary ?? "";
1477
+ const summary = escapePipe(truncate(rawSummary));
1478
+ lines.push(`| ${link} | ${s.kind} | ${summary} |`);
1479
+ }
1480
+ lines.push("");
1481
+ };
1482
+ renderGroup(functions, "Functions & Classes", "api/functions");
1483
+ renderGroup(types, "Types & Interfaces", "api/types");
1484
+ renderGroup(others, "Other Exports", "api/functions");
1485
+ return lines.join("\n");
1486
+ }
1487
+ function renderPackageOverviewPage(packageName, symbols, _options) {
1488
+ const exported = symbols.filter(
1489
+ (s) => s.exported && s.kind !== "method" && s.kind !== "property"
1490
+ );
1491
+ const pkgDoc = symbols.map((s) => s.documentation?.tags?.packageDocumentation?.[0]).find(Boolean);
1492
+ const lines = [];
1493
+ if (pkgDoc) {
1494
+ lines.push(pkgDoc);
1495
+ lines.push("");
1496
+ }
1497
+ if (exported.length > 0) {
1498
+ const functions = exported.filter((s) => FUNCTION_KINDS.has(s.kind));
1499
+ const types = exported.filter((s) => TYPE_KINDS.has(s.kind));
1500
+ const others = exported.filter((s) => !FUNCTION_KINDS.has(s.kind) && !TYPE_KINDS.has(s.kind));
1501
+ const renderGroup = (group, heading) => {
1502
+ if (group.length === 0) return;
1503
+ lines.push(`## ${heading}`);
1504
+ lines.push("");
1505
+ lines.push("| Symbol | Kind | Description |");
1506
+ lines.push("|--------|------|-------------|");
1507
+ for (const s of group) {
1508
+ const ext = s.kind === "function" ? "()" : "";
1509
+ const name = `[\`${s.name}${ext}\`](${slugLink(`packages/${packageName}/api/index`)}#${toAnchor2(`${s.name}${ext}`)})`;
1510
+ const rawSummary = s.documentation?.summary ?? "";
1511
+ const summary = escapePipe(truncate(rawSummary));
1512
+ lines.push(`| ${name} | ${s.kind} | ${summary} |`);
1513
+ }
1514
+ lines.push("");
1515
+ };
1516
+ renderGroup(functions, "Functions & Classes");
1517
+ renderGroup(types, "Types & Interfaces");
1518
+ renderGroup(others, "Other Exports");
1519
+ }
1520
+ return lines.join("\n");
1521
+ }
1522
+ function renderTypesPage(packageName, symbols, _options) {
1523
+ const typeSymbols = symbols.filter((s) => s.exported && TYPE_KINDS.has(s.kind));
1524
+ const lines = [];
1525
+ lines.push("Type contracts exported by this package: interfaces, type aliases, and enums.");
1526
+ lines.push("");
1527
+ for (const s of typeSymbols) {
1528
+ lines.push(`## ${s.name}`);
1529
+ lines.push("");
1530
+ if (s.documentation?.deprecated) {
1531
+ lines.push(`> **Deprecated**: ${s.documentation.deprecated}`);
1532
+ lines.push("");
1533
+ }
1534
+ if (s.documentation?.summary) {
528
1535
  lines.push(s.documentation.summary);
529
1536
  lines.push("");
530
1537
  }
@@ -539,22 +1546,24 @@ function renderTypesPage(packageName, symbols, _options) {
539
1546
  lines.push("| Property | Type | Required | Description |");
540
1547
  lines.push("|----------|------|----------|-------------|");
541
1548
  for (const child of children) {
542
- lines.push(renderPropertyRow(child));
1549
+ const name = `\`${child.name}\``;
1550
+ const type = child.signature ? `\`${escapePipe(child.signature)}\`` : "\u2014";
1551
+ const optional = child.signature?.includes("?") || child.signature?.includes("undefined") ? "No" : "Yes";
1552
+ const description = escapePipe(child.documentation?.summary || child.name);
1553
+ lines.push(`| ${name} | ${type} | ${optional} | ${description} |`);
543
1554
  }
544
1555
  lines.push("");
545
1556
  }
546
1557
  }
1558
+ void packageName;
547
1559
  return lines.join("\n");
548
1560
  }
549
1561
  function renderFunctionsPage(packageName, symbols, _options) {
550
1562
  const fnSymbols = symbols.filter((s) => s.exported && FUNCTION_KINDS.has(s.kind));
551
1563
  const lines = [];
552
- lines.push(`# ${packageName} \u2014 Functions & Classes`);
553
- lines.push("");
554
1564
  lines.push("Functions and classes exported by this package.");
555
1565
  lines.push("");
556
1566
  for (const s of fnSymbols) {
557
- const _ext = s.kind === "function" ? "()" : "";
558
1567
  const paramSig = s.kind === "function" && s.documentation?.params ? s.documentation.params.map((p) => p.name).join(", ") : "";
559
1568
  const heading = s.kind === "function" ? `${s.name}(${paramSig})` : s.name;
560
1569
  lines.push(`## ${heading}`);
@@ -582,7 +1591,14 @@ function renderFunctionsPage(packageName, symbols, _options) {
582
1591
  lines.push("| Name | Type | Description |");
583
1592
  lines.push("|------|------|-------------|");
584
1593
  for (const p of params) {
585
- const type = p.type ? `\`${escapePipe(p.type)}\`` : "\u2014";
1594
+ let resolvedType = p.type;
1595
+ if (!resolvedType && s.signature) {
1596
+ const typeMatch = new RegExp(`\\b${p.name}\\s*[?:]\\s*([^,)]+)`).exec(s.signature);
1597
+ if (typeMatch) {
1598
+ resolvedType = typeMatch[1].trim();
1599
+ }
1600
+ }
1601
+ const type = resolvedType ? `\`${escapePipe(resolvedType)}\`` : "\u2014";
586
1602
  lines.push(`| \`${p.name}\` | ${type} | ${escapePipe(p.description)} |`);
587
1603
  }
588
1604
  lines.push("");
@@ -607,7 +1623,7 @@ function renderFunctionsPage(packageName, symbols, _options) {
607
1623
  const ex = examples[0];
608
1624
  lines.push("**Example**");
609
1625
  lines.push("");
610
- lines.push(`\`\`\`${ex.language}`);
1626
+ lines.push(`\`\`\`${ex.language || "typescript"}`);
611
1627
  lines.push(ex.code.trim());
612
1628
  lines.push("```");
613
1629
  lines.push("");
@@ -632,6 +1648,7 @@ function renderFunctionsPage(packageName, symbols, _options) {
632
1648
  }
633
1649
  }
634
1650
  }
1651
+ void packageName;
635
1652
  return lines.join("\n");
636
1653
  }
637
1654
  function renderExamplesPage(packageName, symbols, _options) {
@@ -639,8 +1656,6 @@ function renderExamplesPage(packageName, symbols, _options) {
639
1656
  (s) => s.exported && s.kind !== "method" && s.kind !== "property"
640
1657
  );
641
1658
  const lines = [];
642
- lines.push(`# ${packageName} \u2014 Examples`);
643
- lines.push("");
644
1659
  lines.push("All usage examples from the package, aggregated for quick reference.");
645
1660
  lines.push("");
646
1661
  let hasExamples = false;
@@ -655,10 +1670,12 @@ function renderExamplesPage(packageName, symbols, _options) {
655
1670
  lines.push(`_${s.documentation.summary}_`);
656
1671
  lines.push("");
657
1672
  }
658
- lines.push(`[View in API reference](./api-reference.md#${toAnchor2(s.name)})`);
1673
+ lines.push(
1674
+ `[View in API reference](${slugLink(`packages/${packageName}/api/functions`)}#${toAnchor2(s.name)})`
1675
+ );
659
1676
  lines.push("");
660
1677
  for (const ex of examples) {
661
- lines.push(`\`\`\`${ex.language}`);
1678
+ lines.push(`\`\`\`${ex.language || "typescript"}`);
662
1679
  lines.push(ex.code.trim());
663
1680
  lines.push("```");
664
1681
  lines.push("");
@@ -670,185 +1687,95 @@ function renderExamplesPage(packageName, symbols, _options) {
670
1687
  }
671
1688
  return lines.join("\n");
672
1689
  }
673
- var KIND_ORDER2 = [
674
- "function",
675
- "class",
676
- "interface",
677
- "type",
678
- "enum",
679
- "variable"
680
- ];
681
- var KIND_LABELS2 = {
682
- function: "Functions",
683
- class: "Classes",
684
- interface: "Interfaces",
685
- type: "Types",
686
- enum: "Enums",
687
- variable: "Variables"
688
- };
689
- function renderApiSymbol(symbol, depth) {
690
- const hashes = "#".repeat(depth);
691
- const ext = symbol.kind === "function" || symbol.kind === "method" ? "()" : "";
692
- const lines = [];
693
- lines.push(`${hashes} \`${symbol.name}${ext}\``);
694
- lines.push("");
695
- if (symbol.documentation?.deprecated) {
696
- lines.push(`> **Deprecated**: ${symbol.documentation.deprecated}`);
697
- lines.push("");
698
- }
699
- if (symbol.signature) {
700
- lines.push("```typescript");
701
- lines.push(symbol.signature);
702
- lines.push("```");
703
- lines.push("");
704
- }
705
- if (symbol.documentation?.summary) {
706
- lines.push(symbol.documentation.summary);
707
- lines.push("");
708
- }
709
- const params = symbol.documentation?.params ?? [];
710
- if (params.length > 0) {
711
- lines.push("**Parameters**");
712
- lines.push("");
713
- for (const p of params) {
714
- const typeStr = p.type ? ` (\`${p.type}\`)` : "";
715
- lines.push(`- \`${p.name}\`${typeStr} \u2014 ${p.description}`);
716
- }
717
- lines.push("");
718
- }
719
- if (symbol.documentation?.returns) {
720
- const retType = symbol.documentation.returns.type ? ` (\`${symbol.documentation.returns.type}\`)` : "";
721
- lines.push(`**Returns**${retType}: ${symbol.documentation.returns.description}`);
722
- lines.push("");
723
- }
724
- const throws = symbol.documentation?.throws ?? [];
725
- if (throws.length > 0) {
726
- lines.push("**Throws**");
727
- lines.push("");
728
- for (const t of throws) {
729
- const typeStr = t.type ? `\`${t.type}\` \u2014 ` : "";
730
- lines.push(`- ${typeStr}${t.description}`);
731
- }
732
- lines.push("");
733
- }
734
- const examples = symbol.documentation?.examples ?? [];
735
- if (examples.length > 0) {
736
- lines.push("**Examples**");
737
- lines.push("");
738
- for (const ex of examples) {
739
- lines.push(`\`\`\`${ex.language}`);
740
- lines.push(ex.code.trim());
741
- lines.push("```");
742
- lines.push("");
743
- }
744
- }
745
- const children = symbol.children ?? [];
746
- if (children.length > 0 && depth < 5) {
747
- for (const child of children) {
748
- lines.push(renderApiSymbol(child, depth + 1));
749
- }
750
- }
751
- return lines.join("\n");
752
- }
753
- function renderApiReferencePage(packageName, symbols) {
754
- const exported = symbols.filter(
755
- (s) => s.exported && s.kind !== "method" && s.kind !== "property"
756
- );
757
- const groups = /* @__PURE__ */ new Map();
758
- for (const s of exported) {
759
- const list = groups.get(s.kind) ?? [];
760
- list.push(s);
761
- groups.set(s.kind, list);
762
- }
1690
+ function renderConfigurationPage(symbolsByPackage, options) {
1691
+ const configSymbol = [...symbolsByPackage.values()].flat().find((s) => s.exported && TYPE_KINDS.has(s.kind) && /config/i.test(s.name));
763
1692
  const lines = [];
764
- lines.push(`# ${packageName} \u2014 API Reference`);
1693
+ lines.push(`Configuration reference for **${options.projectName}**.`);
765
1694
  lines.push("");
766
- for (const kind of KIND_ORDER2) {
767
- const group = groups.get(kind);
768
- if (!group || group.length === 0) continue;
769
- lines.push(`## ${KIND_LABELS2[kind]}`);
770
- lines.push("");
771
- for (const s of group) {
772
- lines.push(renderApiSymbol(s, 3));
773
- lines.push("");
774
- }
775
- }
776
- return lines.join("\n");
777
- }
778
- function renderProjectIndexPage(symbolsByPackage, options) {
779
- const lines = [];
780
- lines.push(`# ${options.projectName}`);
781
- lines.push("");
782
- if (options.projectDescription) {
783
- lines.push(options.projectDescription);
784
- lines.push("");
785
- }
786
- lines.push("## Packages");
787
- lines.push("");
788
- for (const [pkgName, symbols] of symbolsByPackage) {
789
- const exported = symbols.filter(
790
- (s) => s.exported && s.kind !== "method" && s.kind !== "property"
791
- );
792
- const pkgDoc = symbols.map((s) => s.documentation?.tags?.packageDocumentation?.[0]).find(Boolean);
793
- const summary = pkgDoc ?? `${exported.length} exported symbol(s).`;
794
- lines.push(`### [${pkgName}](./packages/${pkgName}/index.md)`);
795
- lines.push("");
796
- lines.push(summary);
797
- lines.push("");
798
- }
799
- return lines.join("\n");
800
- }
801
- function renderGettingStartedPage(symbolsByPackage, options) {
802
- let firstExample;
803
- let firstSymbolName = "";
804
- let firstPackageName = "";
805
- outer: for (const [pkgName, symbols] of symbolsByPackage) {
806
- for (const s of symbols) {
807
- if (!s.exported || s.kind !== "function") continue;
808
- const ex = s.documentation?.examples?.[0];
809
- if (ex) {
810
- firstExample = ex;
811
- firstSymbolName = s.name;
812
- firstPackageName = pkgName;
813
- break outer;
814
- }
815
- }
816
- }
817
- const lines = [];
818
- lines.push("# Getting Started");
1695
+ lines.push("## forge-ts.config.ts");
819
1696
  lines.push("");
820
- lines.push(`Welcome to **${options.projectName}**.`);
821
- if (options.projectDescription) {
822
- lines.push("");
823
- lines.push(options.projectDescription);
824
- }
1697
+ lines.push("Create a `forge-ts.config.ts` file in your project root:");
825
1698
  lines.push("");
826
- lines.push("## Installation");
1699
+ lines.push("```typescript");
1700
+ lines.push('import { defineConfig } from "@forge-ts/core";');
827
1701
  lines.push("");
828
- lines.push("```bash");
829
- lines.push(`npm install ${options.projectName}`);
1702
+ lines.push("export default defineConfig({");
1703
+ lines.push(' rootDir: ".",');
1704
+ lines.push(' outDir: "docs/generated",');
1705
+ lines.push("});");
830
1706
  lines.push("```");
831
1707
  lines.push("");
832
- if (firstExample) {
833
- lines.push("## Quick Start");
1708
+ if (configSymbol) {
1709
+ lines.push(`## \`${configSymbol.name}\``);
834
1710
  lines.push("");
835
- lines.push(
836
- `The following example demonstrates \`${firstSymbolName}\` from the \`${firstPackageName}\` package.`
1711
+ if (configSymbol.documentation?.summary) {
1712
+ lines.push(configSymbol.documentation.summary);
1713
+ lines.push("");
1714
+ }
1715
+ const children = (configSymbol.children ?? []).filter(
1716
+ (c) => c.kind === "property" || c.kind === "method"
837
1717
  );
838
- lines.push("");
839
- lines.push(`\`\`\`${firstExample.language}`);
840
- lines.push(firstExample.code.trim());
841
- lines.push("```");
842
- lines.push("");
843
- }
844
- lines.push("## Next Steps");
845
- lines.push("");
846
- lines.push("- Browse the [API Reference](./packages/)");
847
- for (const pkgName of symbolsByPackage.keys()) {
848
- lines.push(` - [${pkgName}](./packages/${pkgName}/api-reference.md)`);
1718
+ if (children.length > 0) {
1719
+ lines.push("| Property | Type | Required | Description |");
1720
+ lines.push("|----------|------|----------|-------------|");
1721
+ for (const child of children) {
1722
+ const name = `\`${child.name}\``;
1723
+ const type = child.signature ? `\`${escapePipe(child.signature)}\`` : "\u2014";
1724
+ const optional = child.signature?.includes("?") || child.signature?.includes("undefined") ? "No" : "Yes";
1725
+ const description = escapePipe(child.documentation?.summary || child.name);
1726
+ lines.push(`| ${name} | ${type} | ${optional} | ${description} |`);
1727
+ }
1728
+ lines.push("");
1729
+ }
849
1730
  }
850
1731
  return lines.join("\n");
851
1732
  }
1733
+ function renderChangelogPage(options) {
1734
+ const repoUrl = options.repositoryUrl ?? "";
1735
+ const changelogLink = repoUrl ? `See [CHANGELOG.md](${repoUrl}/blob/main/CHANGELOG.md) for the full release history.` : "See your project's `CHANGELOG.md` for the full release history.";
1736
+ return [
1737
+ `Release history for **${options.projectName}**.`,
1738
+ "",
1739
+ "> This is a stub page. Link to or embed your `CHANGELOG.md` here.",
1740
+ "",
1741
+ changelogLink,
1742
+ ""
1743
+ ].join("\n");
1744
+ }
1745
+ function renderFaqPage(options) {
1746
+ return [
1747
+ `Frequently asked questions about **${options.projectName}**.`,
1748
+ "",
1749
+ "> This is a stub page. Common questions will be added here as they arise.",
1750
+ "",
1751
+ "## How do I configure forge-ts?",
1752
+ "",
1753
+ "Create a `forge-ts.config.ts` file in your project root. See [Configuration](/configuration).",
1754
+ "",
1755
+ "## What TypeScript version is required?",
1756
+ "",
1757
+ "forge-ts requires TypeScript 5.0 or later.",
1758
+ "",
1759
+ "## How do I run @example blocks as tests?",
1760
+ "",
1761
+ "```bash",
1762
+ "npx forge-ts test",
1763
+ "```",
1764
+ ""
1765
+ ].join("\n");
1766
+ }
1767
+ function renderContributingPage(options) {
1768
+ const repoUrl = options.repositoryUrl ?? "";
1769
+ const contribLink = repoUrl ? `See [CONTRIBUTING.md](${repoUrl}/blob/main/CONTRIBUTING.md) for contribution guidelines.` : "See your project's `CONTRIBUTING.md` for contribution guidelines.";
1770
+ return [
1771
+ `Contributing to **${options.projectName}**.`,
1772
+ "",
1773
+ "> This is a stub page. Link to or embed your `CONTRIBUTING.md` here.",
1774
+ "",
1775
+ contribLink,
1776
+ ""
1777
+ ].join("\n");
1778
+ }
852
1779
  function generateDocSite(symbolsByPackage, config, options) {
853
1780
  const ext = options.format === "mdx" ? "mdx" : "md";
854
1781
  const pages = [];
@@ -878,10 +1805,38 @@ function generateDocSite(symbolsByPackage, config, options) {
878
1805
  `,
879
1806
  frontmatter: gettingStartedFrontmatter
880
1807
  });
1808
+ const conceptsContent = renderConceptsPage(symbolsByPackage, options);
1809
+ const conceptsFrontmatter = buildFrontmatterFields(
1810
+ "Concepts",
1811
+ `Core concepts behind ${options.projectName}`,
1812
+ options.ssgTarget,
1813
+ 3
1814
+ );
1815
+ pages.push({
1816
+ path: `concepts.${ext}`,
1817
+ content: `${serializeFrontmatter(conceptsFrontmatter)}${conceptsContent.trimEnd()}
1818
+ `,
1819
+ frontmatter: conceptsFrontmatter,
1820
+ stub: true
1821
+ });
1822
+ const guidesContent = renderGuidesIndexPage();
1823
+ const guidesFrontmatter = buildFrontmatterFields(
1824
+ "Guides",
1825
+ `How-to guides for ${options.projectName}`,
1826
+ options.ssgTarget,
1827
+ 4
1828
+ );
1829
+ pages.push({
1830
+ path: `guides/index.${ext}`,
1831
+ content: `${serializeFrontmatter(guidesFrontmatter)}${guidesContent.trimEnd()}
1832
+ `,
1833
+ frontmatter: guidesFrontmatter,
1834
+ stub: true
1835
+ });
881
1836
  let pkgPosition = 1;
882
1837
  for (const [pkgName, symbols] of symbolsByPackage) {
883
1838
  const pkgBase = `packages/${pkgName}`;
884
- const overviewContent = renderOverviewPage(pkgName, symbols, options);
1839
+ const overviewContent = renderPackageOverviewPage(pkgName, symbols, options);
885
1840
  const overviewFrontmatter = buildFrontmatterFields(
886
1841
  pkgName,
887
1842
  `${pkgName} package overview`,
@@ -894,29 +1849,17 @@ function generateDocSite(symbolsByPackage, config, options) {
894
1849
  `,
895
1850
  frontmatter: overviewFrontmatter
896
1851
  });
897
- const apiContent = renderApiReferencePage(pkgName, symbols);
898
- const apiFrontmatter = buildFrontmatterFields(
1852
+ const apiIndexContent = renderApiIndexPage(pkgName, symbols);
1853
+ const apiIndexFrontmatter = buildFrontmatterFields(
899
1854
  `${pkgName} \u2014 API Reference`,
900
1855
  `Full API reference for the ${pkgName} package`,
901
1856
  options.ssgTarget
902
1857
  );
903
1858
  pages.push({
904
- path: `${pkgBase}/api-reference.${ext}`,
905
- content: `${serializeFrontmatter(apiFrontmatter)}${apiContent.trimEnd()}
906
- `,
907
- frontmatter: apiFrontmatter
908
- });
909
- const typesContent = renderTypesPage(pkgName, symbols, options);
910
- const typesFrontmatter = buildFrontmatterFields(
911
- `${pkgName} \u2014 Types`,
912
- `Type contracts for the ${pkgName} package`,
913
- options.ssgTarget
914
- );
915
- pages.push({
916
- path: `${pkgBase}/types.${ext}`,
917
- content: `${serializeFrontmatter(typesFrontmatter)}${typesContent.trimEnd()}
1859
+ path: `${pkgBase}/api/index.${ext}`,
1860
+ content: `${serializeFrontmatter(apiIndexFrontmatter)}${apiIndexContent.trimEnd()}
918
1861
  `,
919
- frontmatter: typesFrontmatter
1862
+ frontmatter: apiIndexFrontmatter
920
1863
  });
921
1864
  const functionsContent = renderFunctionsPage(pkgName, symbols, options);
922
1865
  const functionsFrontmatter = buildFrontmatterFields(
@@ -925,11 +1868,23 @@ function generateDocSite(symbolsByPackage, config, options) {
925
1868
  options.ssgTarget
926
1869
  );
927
1870
  pages.push({
928
- path: `${pkgBase}/functions.${ext}`,
1871
+ path: `${pkgBase}/api/functions.${ext}`,
929
1872
  content: `${serializeFrontmatter(functionsFrontmatter)}${functionsContent.trimEnd()}
930
1873
  `,
931
1874
  frontmatter: functionsFrontmatter
932
1875
  });
1876
+ const typesContent = renderTypesPage(pkgName, symbols, options);
1877
+ const typesFrontmatter = buildFrontmatterFields(
1878
+ `${pkgName} \u2014 Types`,
1879
+ `Type contracts for the ${pkgName} package`,
1880
+ options.ssgTarget
1881
+ );
1882
+ pages.push({
1883
+ path: `${pkgBase}/api/types.${ext}`,
1884
+ content: `${serializeFrontmatter(typesFrontmatter)}${typesContent.trimEnd()}
1885
+ `,
1886
+ frontmatter: typesFrontmatter
1887
+ });
933
1888
  const examplesContent = renderExamplesPage(pkgName, symbols, options);
934
1889
  const examplesFrontmatter = buildFrontmatterFields(
935
1890
  `${pkgName} \u2014 Examples`,
@@ -937,26 +1892,604 @@ function generateDocSite(symbolsByPackage, config, options) {
937
1892
  options.ssgTarget
938
1893
  );
939
1894
  pages.push({
940
- path: `${pkgBase}/examples.${ext}`,
1895
+ path: `${pkgBase}/api/examples.${ext}`,
941
1896
  content: `${serializeFrontmatter(examplesFrontmatter)}${examplesContent.trimEnd()}
942
1897
  `,
943
1898
  frontmatter: examplesFrontmatter
944
1899
  });
945
1900
  pkgPosition++;
946
1901
  }
1902
+ const configContent = renderConfigurationPage(symbolsByPackage, options);
1903
+ const configFrontmatter = buildFrontmatterFields(
1904
+ "Configuration",
1905
+ `Configuration reference for ${options.projectName}`,
1906
+ options.ssgTarget
1907
+ );
1908
+ pages.push({
1909
+ path: `configuration.${ext}`,
1910
+ content: `${serializeFrontmatter(configFrontmatter)}${configContent.trimEnd()}
1911
+ `,
1912
+ frontmatter: configFrontmatter
1913
+ });
1914
+ const changelogContent = renderChangelogPage(options);
1915
+ const changelogFrontmatter = buildFrontmatterFields(
1916
+ "Changelog",
1917
+ `Release history for ${options.projectName}`,
1918
+ options.ssgTarget
1919
+ );
1920
+ pages.push({
1921
+ path: `changelog.${ext}`,
1922
+ content: `${serializeFrontmatter(changelogFrontmatter)}${changelogContent.trimEnd()}
1923
+ `,
1924
+ frontmatter: changelogFrontmatter,
1925
+ stub: true
1926
+ });
1927
+ const faqContent = renderFaqPage(options);
1928
+ const faqFrontmatter = buildFrontmatterFields(
1929
+ "FAQ",
1930
+ `Frequently asked questions about ${options.projectName}`,
1931
+ options.ssgTarget
1932
+ );
1933
+ pages.push({
1934
+ path: `faq.${ext}`,
1935
+ content: `${serializeFrontmatter(faqFrontmatter)}${faqContent.trimEnd()}
1936
+ `,
1937
+ frontmatter: faqFrontmatter,
1938
+ stub: true
1939
+ });
1940
+ const contributingContent = renderContributingPage(options);
1941
+ const contributingFrontmatter = buildFrontmatterFields(
1942
+ "Contributing",
1943
+ `How to contribute to ${options.projectName}`,
1944
+ options.ssgTarget
1945
+ );
1946
+ pages.push({
1947
+ path: `contributing.${ext}`,
1948
+ content: `${serializeFrontmatter(contributingFrontmatter)}${contributingContent.trimEnd()}
1949
+ `,
1950
+ frontmatter: contributingFrontmatter,
1951
+ stub: true
1952
+ });
947
1953
  void config;
948
1954
  return pages;
949
1955
  }
950
1956
 
1957
+ // src/skill.ts
1958
+ function toDirectoryName(name) {
1959
+ return name.replace(/^@[^/]+\//, "").toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
1960
+ }
1961
+ function isConceptKind(kind) {
1962
+ return kind === "interface" || kind === "type" || kind === "class" || kind === "enum";
1963
+ }
1964
+ function primaryBinName(config) {
1965
+ const bin = config.project.bin;
1966
+ if (!bin) return void 0;
1967
+ const keys = Object.keys(bin);
1968
+ if (keys.length === 0) return void 0;
1969
+ const pkgShort = config.project.packageName?.replace(/^@[^/]+\//, "");
1970
+ return keys.find((k) => k === pkgShort) ?? keys[0];
1971
+ }
1972
+ function buildDescription(symbols, config) {
1973
+ const exported = symbols.filter((s) => s.exported);
1974
+ const projectName = config.project.packageName ?? config.rootDir.split("/").pop() ?? "this project";
1975
+ const pkgDoc = symbols.find((s) => s.documentation?.tags?.packageDocumentation);
1976
+ const leadSentence = config.project.description ?? pkgDoc?.documentation?.summary ?? exported.find((s) => s.documentation?.summary)?.documentation?.summary;
1977
+ const triggers = [];
1978
+ let n = 1;
1979
+ const cliName = primaryBinName(config);
1980
+ if (cliName) {
1981
+ triggers.push(`(${n++}) running ${cliName} CLI commands`);
1982
+ }
1983
+ const functionCount = exported.filter((s) => s.kind === "function").length;
1984
+ if (functionCount > 0) {
1985
+ triggers.push(`(${n++}) calling its ${functionCount} API functions`);
1986
+ }
1987
+ const hasRoutes = exported.some((s) => s.documentation?.tags?.route !== void 0);
1988
+ if (hasRoutes) {
1989
+ triggers.push(`(${n++}) building HTTP API routes`);
1990
+ }
1991
+ const configSym = exported.find(
1992
+ (s) => (s.kind === "interface" || s.kind === "type") && /config/i.test(s.name) && (s.children?.length ?? 0) > 0
1993
+ );
1994
+ if (configSym) {
1995
+ triggers.push(`(${n++}) configuring ${projectName}`);
1996
+ }
1997
+ const typeCount = exported.filter(
1998
+ (s) => s.kind === "interface" || s.kind === "type" || s.kind === "enum"
1999
+ ).length;
2000
+ if (typeCount > 0) {
2001
+ triggers.push(`(${n++}) understanding its ${typeCount} type definitions`);
2002
+ }
2003
+ const classCount = exported.filter((s) => s.kind === "class").length;
2004
+ if (classCount > 0) {
2005
+ triggers.push(`(${n++}) working with its ${classCount} classes`);
2006
+ }
2007
+ const keywords = config.project.keywords ?? [];
2008
+ if (keywords.length > 0) {
2009
+ const kwStr = keywords.slice(0, 5).join('", "');
2010
+ triggers.push(`(${n++}) user mentions "${kwStr}"`);
2011
+ }
2012
+ triggers.push(`(${n}) user mentions "${projectName}" or asks about its API`);
2013
+ let description = "";
2014
+ if (leadSentence) {
2015
+ description += `${leadSentence} `;
2016
+ }
2017
+ description += `Use when: ${triggers.join(", ")}.`;
2018
+ if (description.length > 1024) {
2019
+ description = `${description.slice(0, 1021)}...`;
2020
+ }
2021
+ return description;
2022
+ }
2023
+ function renderQuickStart(symbols, config) {
2024
+ const lines = [];
2025
+ const projectName = config.project.packageName ?? "project";
2026
+ const cliName = primaryBinName(config);
2027
+ lines.push("## Quick Start");
2028
+ lines.push("");
2029
+ lines.push("```bash");
2030
+ lines.push(`npm install ${cliName ? "-D " : ""}${projectName}`);
2031
+ lines.push("```");
2032
+ lines.push("");
2033
+ if (cliName && config.project.scripts) {
2034
+ const scripts = config.project.scripts;
2035
+ const relevantKeys = ["check", "test", "build", "lint", "dev", "start", "generate"];
2036
+ const cliCommands = [];
2037
+ for (const key of relevantKeys) {
2038
+ const script = scripts[key];
2039
+ if (script && script.includes(cliName)) {
2040
+ cliCommands.push(`npx ${cliName} ${key}`);
2041
+ }
2042
+ }
2043
+ if (cliCommands.length === 0) {
2044
+ cliCommands.push(`npx ${cliName} --help`);
2045
+ }
2046
+ if (cliCommands.length > 0) {
2047
+ lines.push("```bash");
2048
+ for (const cmd of cliCommands.slice(0, 5)) {
2049
+ lines.push(cmd);
2050
+ }
2051
+ lines.push("```");
2052
+ lines.push("");
2053
+ }
2054
+ } else {
2055
+ const exported = symbols.filter((s) => s.exported);
2056
+ for (const sym of exported) {
2057
+ if (sym.kind !== "function" && sym.kind !== "class") continue;
2058
+ const ex = sym.documentation?.examples?.[0];
2059
+ if (ex) {
2060
+ lines.push(`\`\`\`${ex.language || "typescript"}`);
2061
+ lines.push(ex.code.trim());
2062
+ lines.push("```");
2063
+ lines.push("");
2064
+ break;
2065
+ }
2066
+ }
2067
+ }
2068
+ return lines;
2069
+ }
2070
+ function renderApiSummaryTable(symbols) {
2071
+ const exported = symbols.filter((s) => s.exported);
2072
+ const functions = exported.filter((s) => s.kind === "function");
2073
+ if (functions.length === 0) return [];
2074
+ const lines = [];
2075
+ lines.push("## API");
2076
+ lines.push("");
2077
+ lines.push("| Function | Description |");
2078
+ lines.push("|----------|-------------|");
2079
+ for (const fn of functions.slice(0, 15)) {
2080
+ const desc = fn.documentation?.summary ?? "";
2081
+ lines.push(`| \`${fn.name}()\` | ${desc} |`);
2082
+ }
2083
+ if (functions.length > 15) {
2084
+ lines.push(`| ... | ${functions.length - 15} more \u2014 see API reference |`);
2085
+ }
2086
+ lines.push("");
2087
+ return lines;
2088
+ }
2089
+ function renderKeyTypes(symbols, max = 10) {
2090
+ const lines = [];
2091
+ let count = 0;
2092
+ for (const sym of symbols) {
2093
+ if (count >= max) break;
2094
+ if (!sym.exported) continue;
2095
+ if (!isConceptKind(sym.kind)) continue;
2096
+ const summary = sym.documentation?.summary ?? "";
2097
+ const label = summary ? `**\`${sym.name}\`** \u2014 ${summary}` : `**\`${sym.name}\`**`;
2098
+ lines.push(`- ${label}`);
2099
+ count++;
2100
+ }
2101
+ return lines;
2102
+ }
2103
+ function renderConfigSection(symbols, config) {
2104
+ const exported = symbols.filter((s) => s.exported);
2105
+ const configSymbol = exported.find(
2106
+ (s) => (s.kind === "interface" || s.kind === "type") && /config/i.test(s.name) && (s.children?.length ?? 0) > 0
2107
+ );
2108
+ if (!configSymbol) return [];
2109
+ const lines = [];
2110
+ lines.push("## Configuration");
2111
+ lines.push("");
2112
+ const children = configSymbol.children ?? [];
2113
+ if (children.length > 0) {
2114
+ const projectName = config.project.packageName ?? "project";
2115
+ const importSource = projectName.includes("/") ? projectName.split("/")[0] + "/" + projectName.split("/")[1] : projectName;
2116
+ lines.push("```typescript");
2117
+ lines.push(`import type { ${configSymbol.name} } from "${importSource}";`);
2118
+ lines.push("");
2119
+ lines.push(`const config: Partial<${configSymbol.name}> = {`);
2120
+ for (const child of children.slice(0, 10)) {
2121
+ const comment = child.documentation?.summary;
2122
+ if (comment) {
2123
+ lines.push(` // ${comment}`);
2124
+ }
2125
+ const type = extractType(child.signature);
2126
+ const defaultVal = inferDefaultValue(type, child.name);
2127
+ lines.push(` ${child.name}: ${defaultVal},`);
2128
+ }
2129
+ if (children.length > 10) {
2130
+ lines.push(` // ... ${children.length - 10} more options`);
2131
+ }
2132
+ lines.push("};");
2133
+ lines.push("```");
2134
+ lines.push("");
2135
+ }
2136
+ lines.push("See [references/CONFIGURATION.md](references/CONFIGURATION.md) for full details.");
2137
+ lines.push("");
2138
+ return lines;
2139
+ }
2140
+ function extractType(signature) {
2141
+ if (!signature) return "";
2142
+ const trimmed = signature.trim();
2143
+ if (/^[{(]/.test(trimmed) || /[|]/.test(trimmed.split(":")[0])) {
2144
+ return trimmed;
2145
+ }
2146
+ const colonIdx = trimmed.indexOf(":");
2147
+ if (colonIdx === -1) return trimmed;
2148
+ const left = trimmed.slice(0, colonIdx).trim();
2149
+ if (/^[a-zA-Z_$][a-zA-Z0-9_$?]*$/.test(left)) {
2150
+ return trimmed.slice(colonIdx + 1).trim();
2151
+ }
2152
+ return trimmed;
2153
+ }
2154
+ function inferDefaultValue(type, name) {
2155
+ const t = type.trim();
2156
+ if (t === "boolean" || t.startsWith("boolean")) return "true";
2157
+ if (t === "string" || t.startsWith("string")) return `"..."`;
2158
+ if (t === "number" || t.startsWith("number")) return "0";
2159
+ if (t.includes("[]") || t.startsWith("Array")) return "[]";
2160
+ if (t.startsWith("{") || /^[A-Z]/.test(t)) return "{ /* ... */ }";
2161
+ if (/dir|path/i.test(name)) return `"."`;
2162
+ return `undefined`;
2163
+ }
2164
+ function renderGotchas(symbols) {
2165
+ const lines = [];
2166
+ const exported = symbols.filter((s) => s.exported);
2167
+ const deprecated = exported.filter((s) => s.documentation?.deprecated);
2168
+ for (const sym of deprecated) {
2169
+ const reason = sym.documentation?.deprecated ?? "";
2170
+ const msg = reason && reason !== "true" ? `: ${reason}` : "";
2171
+ lines.push(`- \`${sym.name}\` is deprecated${msg}`);
2172
+ }
2173
+ const throwers = exported.filter(
2174
+ (s) => s.kind === "function" && (s.documentation?.throws?.length ?? 0) > 0
2175
+ );
2176
+ for (const sym of throwers) {
2177
+ for (const t of sym.documentation?.throws ?? []) {
2178
+ lines.push(
2179
+ `- \`${sym.name}()\` throws${t.type ? ` \`${t.type}\`` : ""}: ${t.description}`
2180
+ );
2181
+ }
2182
+ }
2183
+ const enums = exported.filter((s) => s.kind === "enum" && (s.children?.length ?? 0) > 0);
2184
+ for (const sym of enums) {
2185
+ const values = (sym.children ?? []).map((c) => c.name).join(", ");
2186
+ lines.push(`- \`${sym.name}\` enum values: ${values}`);
2187
+ }
2188
+ return lines;
2189
+ }
2190
+ function buildSkillMd(symbols, config, directoryName) {
2191
+ const projectName = config.project.packageName ?? config.rootDir.split("/").pop() ?? "Project";
2192
+ const description = buildDescription(symbols, config);
2193
+ const lines = [];
2194
+ lines.push("---");
2195
+ lines.push(`name: ${directoryName}`);
2196
+ lines.push("description: >");
2197
+ for (const segment of description.split("\n")) {
2198
+ lines.push(` ${segment}`);
2199
+ }
2200
+ lines.push("---");
2201
+ lines.push("");
2202
+ lines.push(`# ${projectName}`);
2203
+ lines.push("");
2204
+ const pkgDoc = symbols.find((s) => s.documentation?.tags?.packageDocumentation);
2205
+ const overview = config.project.description ?? pkgDoc?.documentation?.summary ?? symbols.filter((s) => s.exported).find((s) => s.documentation?.summary)?.documentation?.summary;
2206
+ if (overview) {
2207
+ lines.push(overview);
2208
+ lines.push("");
2209
+ }
2210
+ lines.push(...renderQuickStart(symbols, config));
2211
+ lines.push(...renderApiSummaryTable(symbols));
2212
+ lines.push(...renderConfigSection(symbols, config));
2213
+ const customSections = config.skill.customSections ?? [];
2214
+ for (const section of customSections) {
2215
+ lines.push(`## ${section.heading}`);
2216
+ lines.push("");
2217
+ lines.push(section.content);
2218
+ lines.push("");
2219
+ }
2220
+ const gotchaLines = renderGotchas(symbols);
2221
+ const extraGotchas = config.skill.extraGotchas ?? [];
2222
+ for (const gotcha of extraGotchas) {
2223
+ gotchaLines.push(`- ${gotcha}`);
2224
+ }
2225
+ if (gotchaLines.length > 0) {
2226
+ lines.push("## Gotchas");
2227
+ lines.push("");
2228
+ lines.push(...gotchaLines);
2229
+ lines.push("");
2230
+ }
2231
+ const keyTypeLines = renderKeyTypes(symbols);
2232
+ if (keyTypeLines.length > 0) {
2233
+ lines.push("## Key Types");
2234
+ lines.push("");
2235
+ lines.push(...keyTypeLines);
2236
+ lines.push("");
2237
+ }
2238
+ lines.push("## References");
2239
+ lines.push("");
2240
+ lines.push("- [references/CONFIGURATION.md](references/CONFIGURATION.md) \u2014 Full config options");
2241
+ lines.push(
2242
+ "- [references/API-REFERENCE.md](references/API-REFERENCE.md) \u2014 Signatures, parameters, examples"
2243
+ );
2244
+ lines.push("");
2245
+ return `${lines.join("\n").replace(/\n{3,}/g, "\n\n").trimEnd()}
2246
+ `;
2247
+ }
2248
+ function buildApiReferenceMd(symbols, config) {
2249
+ const projectName = config.project.packageName ?? config.rootDir.split("/").pop() ?? "Project";
2250
+ const exported = symbols.filter((s) => s.exported);
2251
+ const lines = [];
2252
+ lines.push(`# ${projectName} \u2014 API Reference`);
2253
+ lines.push("");
2254
+ const functions = exported.filter((s) => s.kind === "function");
2255
+ const classes = exported.filter((s) => s.kind === "class");
2256
+ const types = exported.filter(
2257
+ (s) => s.kind === "interface" || s.kind === "type" || s.kind === "enum"
2258
+ );
2259
+ const variables = exported.filter((s) => s.kind === "variable");
2260
+ const tocEntries = [];
2261
+ if (functions.length > 0) tocEntries.push("- [Functions](#functions)");
2262
+ if (types.length > 0) tocEntries.push("- [Types](#types)");
2263
+ if (classes.length > 0) tocEntries.push("- [Classes](#classes)");
2264
+ if (variables.length > 0) tocEntries.push("- [Constants](#constants)");
2265
+ if (tocEntries.length > 0) {
2266
+ lines.push("## Table of Contents");
2267
+ lines.push("");
2268
+ lines.push(...tocEntries);
2269
+ lines.push("");
2270
+ }
2271
+ if (functions.length > 0) {
2272
+ lines.push("## Functions");
2273
+ lines.push("");
2274
+ for (const sym of functions) {
2275
+ lines.push(...renderSymbolDetail(sym));
2276
+ }
2277
+ }
2278
+ if (types.length > 0) {
2279
+ lines.push("## Types");
2280
+ lines.push("");
2281
+ for (const sym of types) {
2282
+ lines.push(...renderSymbolDetail(sym));
2283
+ }
2284
+ }
2285
+ if (classes.length > 0) {
2286
+ lines.push("## Classes");
2287
+ lines.push("");
2288
+ for (const sym of classes) {
2289
+ lines.push(...renderSymbolDetail(sym));
2290
+ }
2291
+ }
2292
+ if (variables.length > 0) {
2293
+ lines.push("## Constants");
2294
+ lines.push("");
2295
+ for (const sym of variables) {
2296
+ lines.push(...renderSymbolDetail(sym));
2297
+ }
2298
+ }
2299
+ return `${lines.join("\n").replace(/\n{3,}/g, "\n\n").trimEnd()}
2300
+ `;
2301
+ }
2302
+ function renderSymbolDetail(sym) {
2303
+ const lines = [];
2304
+ lines.push(`### \`${sym.name}\``);
2305
+ lines.push("");
2306
+ if (sym.documentation?.summary) {
2307
+ lines.push(sym.documentation.summary);
2308
+ lines.push("");
2309
+ }
2310
+ if (sym.signature) {
2311
+ lines.push("```typescript");
2312
+ lines.push(sym.signature);
2313
+ lines.push("```");
2314
+ lines.push("");
2315
+ }
2316
+ if (sym.documentation?.params && sym.documentation.params.length > 0) {
2317
+ lines.push("**Parameters:**");
2318
+ lines.push("");
2319
+ for (const p of sym.documentation.params) {
2320
+ lines.push(`- \`${p.name}\`${p.type ? ` (\`${p.type}\`)` : ""} \u2014 ${p.description}`);
2321
+ }
2322
+ lines.push("");
2323
+ }
2324
+ if (sym.documentation?.returns) {
2325
+ lines.push(`**Returns:** ${sym.documentation.returns.description}`);
2326
+ lines.push("");
2327
+ }
2328
+ if (sym.children && sym.children.length > 0) {
2329
+ lines.push("**Members:**");
2330
+ lines.push("");
2331
+ for (const child of sym.children) {
2332
+ const desc = child.documentation?.summary ?? "";
2333
+ lines.push(`- \`${child.name}\`${desc ? ` \u2014 ${desc}` : ""}`);
2334
+ }
2335
+ lines.push("");
2336
+ }
2337
+ for (const ex of sym.documentation?.examples ?? []) {
2338
+ const lang = ex.language || "typescript";
2339
+ lines.push(`\`\`\`${lang}`);
2340
+ lines.push(ex.code.trim());
2341
+ lines.push("```");
2342
+ lines.push("");
2343
+ }
2344
+ return lines;
2345
+ }
2346
+ function buildConfigurationMd(symbols, config) {
2347
+ const projectName = config.project.packageName ?? config.rootDir.split("/").pop() ?? "Project";
2348
+ const exported = symbols.filter((s) => s.exported);
2349
+ const lines = [];
2350
+ lines.push(`# ${projectName} \u2014 Configuration Reference`);
2351
+ lines.push("");
2352
+ const configSymbols = exported.filter(
2353
+ (s) => (s.kind === "interface" || s.kind === "type") && /config/i.test(s.name) && (s.children?.length ?? 0) > 0
2354
+ );
2355
+ for (const configSym of configSymbols) {
2356
+ lines.push(`## \`${configSym.name}\``);
2357
+ lines.push("");
2358
+ if (configSym.documentation?.summary) {
2359
+ lines.push(configSym.documentation.summary);
2360
+ lines.push("");
2361
+ }
2362
+ const children = configSym.children ?? [];
2363
+ if (children.length > 0) {
2364
+ const importSource = projectName.includes("/") ? projectName : projectName;
2365
+ lines.push("```typescript");
2366
+ lines.push(`import type { ${configSym.name} } from "${importSource}";`);
2367
+ lines.push("");
2368
+ lines.push(`const config: Partial<${configSym.name}> = {`);
2369
+ for (const child of children) {
2370
+ if (child.documentation?.summary) {
2371
+ lines.push(` // ${child.documentation.summary}`);
2372
+ }
2373
+ const type = extractType(child.signature);
2374
+ const defaultVal = inferDefaultValue(type, child.name);
2375
+ lines.push(` ${child.name}: ${defaultVal},`);
2376
+ }
2377
+ lines.push("};");
2378
+ lines.push("```");
2379
+ lines.push("");
2380
+ }
2381
+ lines.push("| Property | Type | Description |");
2382
+ lines.push("|----------|------|-------------|");
2383
+ for (const child of children) {
2384
+ const type = extractType(child.signature);
2385
+ const desc = child.documentation?.summary ?? "";
2386
+ lines.push(`| \`${child.name}\` | \`${type}\` | ${desc} |`);
2387
+ }
2388
+ lines.push("");
2389
+ }
2390
+ if (configSymbols.length === 0) {
2391
+ lines.push("No configuration types detected in this project.");
2392
+ lines.push("");
2393
+ }
2394
+ return `${lines.join("\n").replace(/\n{3,}/g, "\n\n").trimEnd()}
2395
+ `;
2396
+ }
2397
+ function buildScripts(config) {
2398
+ const scripts = [];
2399
+ const cliName = primaryBinName(config);
2400
+ const pkgScripts = config.project.scripts ?? {};
2401
+ if (cliName) {
2402
+ if (pkgScripts.build) {
2403
+ scripts.push({
2404
+ path: "scripts/build.sh",
2405
+ content: [
2406
+ "#!/usr/bin/env bash",
2407
+ `# Run ${cliName} build pipeline`,
2408
+ "set -euo pipefail",
2409
+ `npx ${cliName} build "$@"`,
2410
+ ""
2411
+ ].join("\n")
2412
+ });
2413
+ }
2414
+ const checkCmd = pkgScripts.check ?? pkgScripts.lint;
2415
+ if (checkCmd) {
2416
+ const subcommand = checkCmd.includes("check") ? "check" : "lint";
2417
+ scripts.push({
2418
+ path: "scripts/check.sh",
2419
+ content: [
2420
+ "#!/usr/bin/env bash",
2421
+ `# Run ${cliName} ${subcommand}`,
2422
+ "set -euo pipefail",
2423
+ `npx ${cliName} ${subcommand} "$@"`,
2424
+ ""
2425
+ ].join("\n")
2426
+ });
2427
+ }
2428
+ if (pkgScripts.test) {
2429
+ scripts.push({
2430
+ path: "scripts/test.sh",
2431
+ content: [
2432
+ "#!/usr/bin/env bash",
2433
+ `# Run ${cliName} test suite`,
2434
+ "set -euo pipefail",
2435
+ `npx ${cliName} test "$@"`,
2436
+ ""
2437
+ ].join("\n")
2438
+ });
2439
+ }
2440
+ }
2441
+ if (scripts.length === 0) {
2442
+ scripts.push({
2443
+ path: "scripts/test.sh",
2444
+ content: [
2445
+ "#!/usr/bin/env bash",
2446
+ "# Run the project's test suite",
2447
+ "# Usage: ./scripts/test.sh [additional args]",
2448
+ "",
2449
+ "if [ -f package.json ]; then",
2450
+ ' npm test "$@"',
2451
+ "else",
2452
+ ' echo "No package.json found"',
2453
+ " exit 1",
2454
+ "fi",
2455
+ ""
2456
+ ].join("\n")
2457
+ });
2458
+ }
2459
+ return scripts;
2460
+ }
2461
+ function generateSkillPackage(symbols, config) {
2462
+ const projectName = config.project.packageName ?? config.rootDir.split("/").pop() ?? "Project";
2463
+ const directoryName = toDirectoryName(projectName);
2464
+ const skillMd = buildSkillMd(symbols, config, directoryName);
2465
+ const apiRefMd = buildApiReferenceMd(symbols, config);
2466
+ const configMd = buildConfigurationMd(symbols, config);
2467
+ const scripts = buildScripts(config);
2468
+ return {
2469
+ directoryName,
2470
+ files: [
2471
+ { path: "SKILL.md", content: skillMd },
2472
+ { path: "references/API-REFERENCE.md", content: apiRefMd },
2473
+ { path: "references/CONFIGURATION.md", content: configMd },
2474
+ ...scripts
2475
+ ]
2476
+ };
2477
+ }
2478
+ function generateSkillMd(symbols, config) {
2479
+ const projectName = config.project.packageName ?? config.rootDir.split("/").pop() ?? "Project";
2480
+ const directoryName = toDirectoryName(projectName);
2481
+ return buildSkillMd(symbols, config, directoryName);
2482
+ }
2483
+
951
2484
  // src/ssg-config.ts
952
- function pageSlug(pagePath) {
2485
+ function pageSlug5(pagePath) {
953
2486
  return pagePath.replace(/\.[^.]+$/, "");
954
2487
  }
955
2488
  function partitionPages(pages) {
956
2489
  const topLevel = [];
957
2490
  const byPackage = /* @__PURE__ */ new Map();
958
2491
  for (const page of pages) {
959
- const slug = pageSlug(page.path);
2492
+ const slug = pageSlug5(page.path);
960
2493
  const packageMatch = /^packages\/([^/]+)\/(.+)$/.exec(slug);
961
2494
  if (packageMatch) {
962
2495
  const pkgName = packageMatch[1];
@@ -969,7 +2502,7 @@ function partitionPages(pages) {
969
2502
  }
970
2503
  return { topLevel, byPackage };
971
2504
  }
972
- function slugToLabel(slug) {
2505
+ function slugToLabel3(slug) {
973
2506
  return slug.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
974
2507
  }
975
2508
  function generateMintlifyConfig(pages, projectName) {
@@ -1032,7 +2565,7 @@ function generateNextraConfigs(pages, _projectName) {
1032
2565
  const rootMeta = {};
1033
2566
  for (const slug of topLevel) {
1034
2567
  const segment = slug.split("/").pop() ?? slug;
1035
- rootMeta[segment] = slugToLabel(segment);
2568
+ rootMeta[segment] = slugToLabel3(segment);
1036
2569
  }
1037
2570
  if (byPackage.size > 0) {
1038
2571
  rootMeta.packages = "Packages";
@@ -1046,7 +2579,7 @@ function generateNextraConfigs(pages, _projectName) {
1046
2579
  const pkgMeta = {};
1047
2580
  for (const slug of slugs) {
1048
2581
  const segment = slug.split("/").pop() ?? slug;
1049
- pkgMeta[segment] = slugToLabel(segment);
2582
+ pkgMeta[segment] = slugToLabel3(segment);
1050
2583
  }
1051
2584
  files.push({
1052
2585
  path: `packages/${pkgName}/_meta.json`,
@@ -1074,7 +2607,7 @@ function generateVitePressConfig(pages, _projectName) {
1074
2607
  const items = topLevel.map((slug) => {
1075
2608
  const segment = slug.split("/").pop() ?? slug;
1076
2609
  const link = slug === "index" ? "/" : `/${slug}`;
1077
- return { text: slugToLabel(segment), link };
2610
+ return { text: slugToLabel3(segment), link };
1078
2611
  });
1079
2612
  sidebar.push({ text: "Getting Started", items });
1080
2613
  }
@@ -1083,7 +2616,7 @@ function generateVitePressConfig(pages, _projectName) {
1083
2616
  const segment = slug.split("/").pop() ?? slug;
1084
2617
  const isIndex = segment === "index";
1085
2618
  const link = isIndex ? `/packages/${pkgName}/` : `/${slug}`;
1086
- return { text: slugToLabel(segment), link };
2619
+ return { text: slugToLabel3(segment), link };
1087
2620
  });
1088
2621
  sidebar.push({ text: pkgName, items });
1089
2622
  }
@@ -1112,11 +2645,34 @@ function generateSSGConfigs(pages, target, projectName) {
1112
2645
  }
1113
2646
 
1114
2647
  // src/index.ts
1115
- import { mkdir, writeFile as writeFile2 } from "fs/promises";
1116
- import { join } from "path";
2648
+ import { existsSync as existsSync2 } from "fs";
2649
+ import { mkdir, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
2650
+ import { dirname, join } from "path";
1117
2651
  import { createWalker } from "@forge-ts/core";
1118
- async function generate(config) {
2652
+ function updateAutoSections(existing, generated) {
2653
+ const markerPattern = /<!-- FORGE:AUTO-START (\S+) -->([\s\S]*?)<!-- FORGE:AUTO-END \1 -->/g;
2654
+ const newSections = /* @__PURE__ */ new Map();
2655
+ let match;
2656
+ while ((match = markerPattern.exec(generated)) !== null) {
2657
+ newSections.set(match[1], match[0]);
2658
+ }
2659
+ if (newSections.size === 0) return null;
2660
+ let updated = existing;
2661
+ let changed = false;
2662
+ for (const [id, replacement] of newSections) {
2663
+ const sectionPattern = new RegExp(
2664
+ `<!-- FORGE:AUTO-START ${id} -->[\\s\\S]*?<!-- FORGE:AUTO-END ${id} -->`
2665
+ );
2666
+ if (sectionPattern.test(updated)) {
2667
+ updated = updated.replace(sectionPattern, replacement);
2668
+ changed = true;
2669
+ }
2670
+ }
2671
+ return changed ? updated : null;
2672
+ }
2673
+ async function generate(config, options) {
1119
2674
  const start = Date.now();
2675
+ const forceStubs = options?.forceStubs ?? false;
1120
2676
  const walker = createWalker(config);
1121
2677
  const symbols = walker.walk();
1122
2678
  await mkdir(config.outDir, { recursive: true });
@@ -1127,27 +2683,46 @@ async function generate(config) {
1127
2683
  const ext = format === "mdx" ? "mdx" : "md";
1128
2684
  await writeFile2(join(config.outDir, `api-reference.${ext}`), content, "utf8");
1129
2685
  }
1130
- const projectName = config.rootDir.split("/").pop() ?? "Project";
2686
+ const resolvedRoot = config.rootDir === "." ? process.cwd() : config.rootDir;
2687
+ const projectName = resolvedRoot.split("/").pop() ?? "Project";
1131
2688
  const symbolsByPackage = groupSymbolsByPackage(symbols, config.rootDir);
2689
+ const target = config.gen.ssgTarget ?? DEFAULT_TARGET;
2690
+ const adapter = getAdapter(target);
1132
2691
  for (const format of config.gen.formats) {
1133
2692
  const pages = generateDocSite(symbolsByPackage, config, {
1134
2693
  format,
1135
2694
  ssgTarget: config.gen.ssgTarget,
1136
- projectName
2695
+ projectName,
2696
+ repositoryUrl: config.project.repository,
2697
+ packageName: config.project.packageName
1137
2698
  });
1138
- for (const page of pages) {
1139
- const pagePath = join(config.outDir, page.path);
1140
- const pageDir = pagePath.substring(0, pagePath.lastIndexOf("/"));
1141
- await mkdir(pageDir, { recursive: true });
1142
- await writeFile2(pagePath, page.content, "utf8");
2699
+ const adapterContext = {
2700
+ config,
2701
+ projectName,
2702
+ pages,
2703
+ symbols,
2704
+ outDir: config.outDir
2705
+ };
2706
+ const transformedPages = adapter.transformPages(pages, adapterContext);
2707
+ for (const file of transformedPages) {
2708
+ const filePath = join(config.outDir, file.path);
2709
+ if (file.stub && existsSync2(filePath) && !forceStubs) {
2710
+ const existingContent = await readFile2(filePath, "utf8");
2711
+ const merged = updateAutoSections(existingContent, file.content);
2712
+ if (merged) {
2713
+ await writeFile2(filePath, merged, "utf8");
2714
+ }
2715
+ continue;
2716
+ }
2717
+ await mkdir(dirname(filePath), { recursive: true });
2718
+ await writeFile2(filePath, file.content, "utf8");
1143
2719
  }
1144
2720
  if (config.gen.ssgTarget) {
1145
- const configFiles = generateSSGConfigs(pages, config.gen.ssgTarget, projectName);
1146
- for (const configFile of configFiles) {
1147
- const configPath = join(config.outDir, configFile.path);
1148
- const configDir = configPath.substring(0, configPath.lastIndexOf("/"));
1149
- await mkdir(configDir, { recursive: true });
1150
- await writeFile2(configPath, configFile.content, "utf8");
2721
+ const configFiles = adapter.generateConfig(adapterContext);
2722
+ for (const file of configFiles) {
2723
+ const filePath = join(config.outDir, file.path);
2724
+ await mkdir(dirname(filePath), { recursive: true });
2725
+ await writeFile2(filePath, file.content, "utf8");
1151
2726
  }
1152
2727
  }
1153
2728
  }
@@ -1156,6 +2731,17 @@ async function generate(config) {
1156
2731
  await writeFile2(join(config.outDir, "llms.txt"), llms, "utf8");
1157
2732
  const llmsFull = generateLlmsFullTxt(symbols, config);
1158
2733
  await writeFile2(join(config.outDir, "llms-full.txt"), llmsFull, "utf8");
2734
+ const skillEnabled = config.skill.enabled !== false;
2735
+ if (skillEnabled) {
2736
+ const skillPkg = generateSkillPackage(symbols, config);
2737
+ const skillDir = join(config.outDir, skillPkg.directoryName);
2738
+ await mkdir(skillDir, { recursive: true });
2739
+ for (const file of skillPkg.files) {
2740
+ const filePath = join(skillDir, file.path);
2741
+ await mkdir(dirname(filePath), { recursive: true });
2742
+ await writeFile2(filePath, file.content, "utf8");
2743
+ }
2744
+ }
1159
2745
  }
1160
2746
  if (config.gen.readmeSync) {
1161
2747
  await syncReadme(join(config.rootDir, "README.md"), symbols);
@@ -1169,13 +2755,19 @@ async function generate(config) {
1169
2755
  };
1170
2756
  }
1171
2757
  export {
2758
+ DEFAULT_TARGET,
1172
2759
  generate,
1173
2760
  generateDocSite,
1174
2761
  generateLlmsFullTxt,
1175
2762
  generateLlmsTxt,
1176
2763
  generateMarkdown,
1177
2764
  generateSSGConfigs,
2765
+ generateSkillMd,
2766
+ generateSkillPackage,
2767
+ getAdapter,
2768
+ getAvailableTargets,
1178
2769
  groupSymbolsByPackage,
2770
+ registerAdapter,
1179
2771
  syncReadme
1180
2772
  };
1181
2773
  //# sourceMappingURL=index.js.map