@forge-ts/gen 0.5.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
@@ -30,7 +30,6 @@ function pageSlug(pagePath) {
30
30
  }
31
31
  function buildDocsJson(context) {
32
32
  const { pages, projectName } = context;
33
- const topLevel = [];
34
33
  const byPackage = /* @__PURE__ */ new Map();
35
34
  for (const page of pages) {
36
35
  const slug = pageSlug(page.path);
@@ -40,29 +39,56 @@ function buildDocsJson(context) {
40
39
  const list = byPackage.get(pkgName) ?? [];
41
40
  list.push(slug);
42
41
  byPackage.set(pkgName, list);
43
- } else {
44
- topLevel.push(slug);
45
42
  }
46
43
  }
47
- const navigation = [];
48
- if (topLevel.length > 0) {
49
- navigation.push({ group: "Getting Started", pages: topLevel });
50
- }
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 });
51
63
  if (byPackage.size > 0) {
52
- const packageGroups = [];
64
+ const apiGroups = [];
53
65
  for (const [pkgName, slugs] of byPackage) {
54
- packageGroups.push({ group: pkgName, pages: slugs });
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 });
55
78
  }
56
- navigation.push({
57
- group: "Packages",
58
- pages: packageGroups
59
- });
79
+ tabs.push({ tab: "API Reference", groups: apiGroups });
60
80
  }
61
81
  return {
62
82
  name: projectName,
63
- theme: "venus",
64
- colors: { primary: "#0ea5e9" },
65
- navigation
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
+ }
66
92
  };
67
93
  }
68
94
  function addMintlifyFrontmatter(page) {
@@ -89,17 +115,39 @@ var mintlifyAdapter = {
89
115
  devDependencies: {},
90
116
  scripts: {},
91
117
  instructions: [
92
- "Install Mintlify CLI: npm i -g mint",
93
- "Preview locally: mint dev",
118
+ "Preview locally: npx @mintlify/cli dev",
94
119
  "Deploy: Push to GitHub and connect at mintlify.com"
95
120
  ]
96
121
  };
97
122
  },
98
123
  transformPages(pages, _context) {
99
- return pages.map((page) => ({
100
- path: page.path.replace(/\.md$/, ".mdx"),
101
- content: addMintlifyFrontmatter(page)
102
- }));
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
+ });
103
151
  },
104
152
  generateConfig(context) {
105
153
  const config = buildDocsJson(context);
@@ -111,10 +159,19 @@ var mintlifyAdapter = {
111
159
  }
112
160
  ];
113
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
+ },
114
171
  async detectExisting(outDir) {
115
- const { existsSync: existsSync2 } = await import("fs");
172
+ const { existsSync: existsSync3 } = await import("fs");
116
173
  const { join: join2 } = await import("path");
117
- return existsSync2(join2(outDir, "docs.json"));
174
+ return existsSync3(join2(outDir, "docs.json"));
118
175
  }
119
176
  };
120
177
  registerAdapter(mintlifyAdapter);
@@ -282,7 +339,8 @@ var docusaurusAdapter = {
282
339
  transformPages(pages, _context) {
283
340
  return pages.map((page) => ({
284
341
  path: page.path.replace(/\.md$/, ".mdx"),
285
- content: addDocusaurusFrontmatter(page)
342
+ content: addDocusaurusFrontmatter(page),
343
+ stub: page.stub
286
344
  }));
287
345
  },
288
346
  generateConfig(context) {
@@ -293,10 +351,19 @@ var docusaurusAdapter = {
293
351
  }
294
352
  ];
295
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
+ },
296
363
  async detectExisting(outDir) {
297
- const { existsSync: existsSync2 } = await import("fs");
364
+ const { existsSync: existsSync3 } = await import("fs");
298
365
  const { join: join2 } = await import("path");
299
- return existsSync2(join2(outDir, "sidebars.ts")) || existsSync2(join2(outDir, "docusaurus.config.ts")) || existsSync2(join2(outDir, "docusaurus.config.js"));
366
+ return existsSync3(join2(outDir, "sidebars.ts")) || existsSync3(join2(outDir, "docusaurus.config.ts")) || existsSync3(join2(outDir, "docusaurus.config.js"));
300
367
  }
301
368
  };
302
369
  registerAdapter(docusaurusAdapter);
@@ -510,16 +577,26 @@ var nextraAdapter = {
510
577
  transformPages(pages, _context) {
511
578
  return pages.map((page) => ({
512
579
  path: page.path.replace(/\.md$/, ".mdx"),
513
- content: addNextraFrontmatter(page)
580
+ content: addNextraFrontmatter(page),
581
+ stub: page.stub
514
582
  }));
515
583
  },
516
584
  generateConfig(context) {
517
585
  return buildMetaFiles(context.pages);
518
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
+ },
519
596
  async detectExisting(outDir) {
520
- const { existsSync: existsSync2 } = await import("fs");
597
+ const { existsSync: existsSync3 } = await import("fs");
521
598
  const { join: join2 } = await import("path");
522
- return existsSync2(join2(outDir, "content/_meta.js")) || existsSync2(join2(outDir, "next.config.ts")) || existsSync2(join2(outDir, "next.config.js"));
599
+ return existsSync3(join2(outDir, "content/_meta.js")) || existsSync3(join2(outDir, "next.config.ts")) || existsSync3(join2(outDir, "next.config.js"));
523
600
  }
524
601
  };
525
602
  registerAdapter(nextraAdapter);
@@ -650,7 +727,8 @@ var vitepressAdapter = {
650
727
  transformPages(pages, _context) {
651
728
  return pages.map((page) => ({
652
729
  path: page.path.endsWith(".mdx") ? page.path.replace(/\.mdx$/, ".md") : page.path,
653
- content: addVitepressFrontmatter(page)
730
+ content: addVitepressFrontmatter(page),
731
+ stub: page.stub
654
732
  }));
655
733
  },
656
734
  generateConfig(context) {
@@ -661,10 +739,19 @@ var vitepressAdapter = {
661
739
  }
662
740
  ];
663
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
+ },
664
751
  async detectExisting(outDir) {
665
- const { existsSync: existsSync2 } = await import("fs");
752
+ const { existsSync: existsSync3 } = await import("fs");
666
753
  const { join: join2 } = await import("path");
667
- return existsSync2(join2(outDir, ".vitepress"));
754
+ return existsSync3(join2(outDir, ".vitepress"));
668
755
  }
669
756
  };
670
757
  registerAdapter(vitepressAdapter);
@@ -984,7 +1071,7 @@ function generateMarkdown(symbols, config, options = {}) {
984
1071
  }
985
1072
  parts.push("# API Reference\n");
986
1073
  parts.push(
987
- `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}\`.
988
1075
  `
989
1076
  );
990
1077
  if (topLevel.length > 0) {
@@ -1133,6 +1220,15 @@ function buildFrontmatterFields(title, description, ssgTarget, sidebarPosition)
1133
1220
  return {};
1134
1221
  }
1135
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
+ }
1136
1232
  function groupSymbolsByPackage(symbols, rootDir) {
1137
1233
  const result = /* @__PURE__ */ new Map();
1138
1234
  for (const symbol of symbols) {
@@ -1147,45 +1243,285 @@ function groupSymbolsByPackage(symbols, rootDir) {
1147
1243
  }
1148
1244
  var TYPE_KINDS = /* @__PURE__ */ new Set(["interface", "type", "enum"]);
1149
1245
  var FUNCTION_KINDS = /* @__PURE__ */ new Set(["function", "class"]);
1150
- function renderPropertyRow(child) {
1151
- const name = `\`${child.name}\``;
1152
- const type = child.signature ? `\`${escapePipe(child.signature)}\`` : "\u2014";
1153
- const optional = child.signature?.includes("?") || child.signature?.includes("undefined") ? "No" : "Yes";
1154
- const description = escapePipe(child.documentation?.summary ?? "");
1155
- return `| ${name} | ${type} | ${optional} | ${description} |`;
1156
- }
1157
- function renderOverviewPage(packageName, symbols, _options) {
1246
+ function renderProjectIndexPage(symbolsByPackage, options) {
1247
+ const lines = [];
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
+ );
1254
+ }
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");
1263
+ }
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");
1267
+ lines.push("");
1268
+ lines.push("## Installation");
1269
+ lines.push("");
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) {
1158
1451
  const exported = symbols.filter(
1159
1452
  (s) => s.exported && s.kind !== "method" && s.kind !== "property"
1160
1453
  );
1161
- const pkgDoc = symbols.map((s) => s.documentation?.tags?.packageDocumentation?.[0]).find(Boolean);
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));
1162
1457
  const lines = [];
1163
- lines.push(`# ${packageName}`);
1164
- lines.push("");
1458
+ const pkgDoc = symbols.map((s) => s.documentation?.tags?.packageDocumentation?.[0]).find(Boolean);
1165
1459
  if (pkgDoc) {
1166
1460
  lines.push(pkgDoc);
1167
1461
  lines.push("");
1462
+ } else {
1463
+ lines.push(`API reference for the \`${pkgName}\` package.`);
1464
+ lines.push("");
1168
1465
  }
1169
- if (exported.length > 0) {
1170
- lines.push("## Exported Symbols");
1466
+ const renderGroup = (group, heading, pathSuffix) => {
1467
+ if (group.length === 0) return;
1468
+ lines.push(`## ${heading}`);
1171
1469
  lines.push("");
1172
1470
  lines.push("| Symbol | Kind | Description |");
1173
1471
  lines.push("|--------|------|-------------|");
1174
- for (const s of exported) {
1472
+ for (const s of group) {
1175
1473
  const ext = s.kind === "function" ? "()" : "";
1176
- const name = `[\`${s.name}${ext}\`](./api-reference.md#${toAnchor2(`${s.name}${ext}`)})`;
1177
- const summary = escapePipe(s.documentation?.summary ?? "");
1178
- lines.push(`| ${name} | ${s.kind} | ${summary} |`);
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} |`);
1179
1479
  }
1180
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");
1181
1519
  }
1182
1520
  return lines.join("\n");
1183
1521
  }
1184
1522
  function renderTypesPage(packageName, symbols, _options) {
1185
1523
  const typeSymbols = symbols.filter((s) => s.exported && TYPE_KINDS.has(s.kind));
1186
1524
  const lines = [];
1187
- lines.push(`# ${packageName} \u2014 Types`);
1188
- lines.push("");
1189
1525
  lines.push("Type contracts exported by this package: interfaces, type aliases, and enums.");
1190
1526
  lines.push("");
1191
1527
  for (const s of typeSymbols) {
@@ -1210,22 +1546,24 @@ function renderTypesPage(packageName, symbols, _options) {
1210
1546
  lines.push("| Property | Type | Required | Description |");
1211
1547
  lines.push("|----------|------|----------|-------------|");
1212
1548
  for (const child of children) {
1213
- 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} |`);
1214
1554
  }
1215
1555
  lines.push("");
1216
1556
  }
1217
1557
  }
1558
+ void packageName;
1218
1559
  return lines.join("\n");
1219
1560
  }
1220
1561
  function renderFunctionsPage(packageName, symbols, _options) {
1221
1562
  const fnSymbols = symbols.filter((s) => s.exported && FUNCTION_KINDS.has(s.kind));
1222
1563
  const lines = [];
1223
- lines.push(`# ${packageName} \u2014 Functions & Classes`);
1224
- lines.push("");
1225
1564
  lines.push("Functions and classes exported by this package.");
1226
1565
  lines.push("");
1227
1566
  for (const s of fnSymbols) {
1228
- const _ext = s.kind === "function" ? "()" : "";
1229
1567
  const paramSig = s.kind === "function" && s.documentation?.params ? s.documentation.params.map((p) => p.name).join(", ") : "";
1230
1568
  const heading = s.kind === "function" ? `${s.name}(${paramSig})` : s.name;
1231
1569
  lines.push(`## ${heading}`);
@@ -1253,7 +1591,14 @@ function renderFunctionsPage(packageName, symbols, _options) {
1253
1591
  lines.push("| Name | Type | Description |");
1254
1592
  lines.push("|------|------|-------------|");
1255
1593
  for (const p of params) {
1256
- 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";
1257
1602
  lines.push(`| \`${p.name}\` | ${type} | ${escapePipe(p.description)} |`);
1258
1603
  }
1259
1604
  lines.push("");
@@ -1278,7 +1623,7 @@ function renderFunctionsPage(packageName, symbols, _options) {
1278
1623
  const ex = examples[0];
1279
1624
  lines.push("**Example**");
1280
1625
  lines.push("");
1281
- lines.push(`\`\`\`${ex.language}`);
1626
+ lines.push(`\`\`\`${ex.language || "typescript"}`);
1282
1627
  lines.push(ex.code.trim());
1283
1628
  lines.push("```");
1284
1629
  lines.push("");
@@ -1303,6 +1648,7 @@ function renderFunctionsPage(packageName, symbols, _options) {
1303
1648
  }
1304
1649
  }
1305
1650
  }
1651
+ void packageName;
1306
1652
  return lines.join("\n");
1307
1653
  }
1308
1654
  function renderExamplesPage(packageName, symbols, _options) {
@@ -1310,8 +1656,6 @@ function renderExamplesPage(packageName, symbols, _options) {
1310
1656
  (s) => s.exported && s.kind !== "method" && s.kind !== "property"
1311
1657
  );
1312
1658
  const lines = [];
1313
- lines.push(`# ${packageName} \u2014 Examples`);
1314
- lines.push("");
1315
1659
  lines.push("All usage examples from the package, aggregated for quick reference.");
1316
1660
  lines.push("");
1317
1661
  let hasExamples = false;
@@ -1326,10 +1670,12 @@ function renderExamplesPage(packageName, symbols, _options) {
1326
1670
  lines.push(`_${s.documentation.summary}_`);
1327
1671
  lines.push("");
1328
1672
  }
1329
- 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
+ );
1330
1676
  lines.push("");
1331
1677
  for (const ex of examples) {
1332
- lines.push(`\`\`\`${ex.language}`);
1678
+ lines.push(`\`\`\`${ex.language || "typescript"}`);
1333
1679
  lines.push(ex.code.trim());
1334
1680
  lines.push("```");
1335
1681
  lines.push("");
@@ -1341,184 +1687,94 @@ function renderExamplesPage(packageName, symbols, _options) {
1341
1687
  }
1342
1688
  return lines.join("\n");
1343
1689
  }
1344
- var KIND_ORDER2 = [
1345
- "function",
1346
- "class",
1347
- "interface",
1348
- "type",
1349
- "enum",
1350
- "variable"
1351
- ];
1352
- var KIND_LABELS2 = {
1353
- function: "Functions",
1354
- class: "Classes",
1355
- interface: "Interfaces",
1356
- type: "Types",
1357
- enum: "Enums",
1358
- variable: "Variables"
1359
- };
1360
- function renderApiSymbol(symbol, depth) {
1361
- const hashes = "#".repeat(depth);
1362
- const ext = symbol.kind === "function" || symbol.kind === "method" ? "()" : "";
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));
1363
1692
  const lines = [];
1364
- lines.push(`${hashes} \`${symbol.name}${ext}\``);
1693
+ lines.push(`Configuration reference for **${options.projectName}**.`);
1365
1694
  lines.push("");
1366
- if (symbol.documentation?.deprecated) {
1367
- lines.push(`> **Deprecated**: ${symbol.documentation.deprecated}`);
1368
- lines.push("");
1369
- }
1370
- if (symbol.signature) {
1371
- lines.push("```typescript");
1372
- lines.push(symbol.signature);
1373
- lines.push("```");
1374
- lines.push("");
1375
- }
1376
- if (symbol.documentation?.summary) {
1377
- lines.push(symbol.documentation.summary);
1378
- lines.push("");
1379
- }
1380
- const params = symbol.documentation?.params ?? [];
1381
- if (params.length > 0) {
1382
- lines.push("**Parameters**");
1383
- lines.push("");
1384
- for (const p of params) {
1385
- const typeStr = p.type ? ` (\`${p.type}\`)` : "";
1386
- lines.push(`- \`${p.name}\`${typeStr} \u2014 ${p.description}`);
1387
- }
1388
- lines.push("");
1389
- }
1390
- if (symbol.documentation?.returns) {
1391
- const retType = symbol.documentation.returns.type ? ` (\`${symbol.documentation.returns.type}\`)` : "";
1392
- lines.push(`**Returns**${retType}: ${symbol.documentation.returns.description}`);
1393
- lines.push("");
1394
- }
1395
- const throws = symbol.documentation?.throws ?? [];
1396
- if (throws.length > 0) {
1397
- lines.push("**Throws**");
1695
+ lines.push("## forge-ts.config.ts");
1696
+ lines.push("");
1697
+ lines.push("Create a `forge-ts.config.ts` file in your project root:");
1698
+ lines.push("");
1699
+ lines.push("```typescript");
1700
+ lines.push('import { defineConfig } from "@forge-ts/core";');
1701
+ lines.push("");
1702
+ lines.push("export default defineConfig({");
1703
+ lines.push(' rootDir: ".",');
1704
+ lines.push(' outDir: "docs/generated",');
1705
+ lines.push("});");
1706
+ lines.push("```");
1707
+ lines.push("");
1708
+ if (configSymbol) {
1709
+ lines.push(`## \`${configSymbol.name}\``);
1398
1710
  lines.push("");
1399
- for (const t of throws) {
1400
- const typeStr = t.type ? `\`${t.type}\` \u2014 ` : "";
1401
- lines.push(`- ${typeStr}${t.description}`);
1711
+ if (configSymbol.documentation?.summary) {
1712
+ lines.push(configSymbol.documentation.summary);
1713
+ lines.push("");
1402
1714
  }
1403
- lines.push("");
1404
- }
1405
- const examples = symbol.documentation?.examples ?? [];
1406
- if (examples.length > 0) {
1407
- lines.push("**Examples**");
1408
- lines.push("");
1409
- for (const ex of examples) {
1410
- lines.push(`\`\`\`${ex.language}`);
1411
- lines.push(ex.code.trim());
1412
- lines.push("```");
1715
+ const children = (configSymbol.children ?? []).filter(
1716
+ (c) => c.kind === "property" || c.kind === "method"
1717
+ );
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
+ }
1413
1728
  lines.push("");
1414
1729
  }
1415
1730
  }
1416
- const children = symbol.children ?? [];
1417
- if (children.length > 0 && depth < 5) {
1418
- for (const child of children) {
1419
- lines.push(renderApiSymbol(child, depth + 1));
1420
- }
1421
- }
1422
1731
  return lines.join("\n");
1423
1732
  }
1424
- function renderApiReferencePage(packageName, symbols) {
1425
- const exported = symbols.filter(
1426
- (s) => s.exported && s.kind !== "method" && s.kind !== "property"
1427
- );
1428
- const groups = /* @__PURE__ */ new Map();
1429
- for (const s of exported) {
1430
- const list = groups.get(s.kind) ?? [];
1431
- list.push(s);
1432
- groups.set(s.kind, list);
1433
- }
1434
- const lines = [];
1435
- lines.push(`# ${packageName} \u2014 API Reference`);
1436
- lines.push("");
1437
- for (const kind of KIND_ORDER2) {
1438
- const group = groups.get(kind);
1439
- if (!group || group.length === 0) continue;
1440
- lines.push(`## ${KIND_LABELS2[kind]}`);
1441
- lines.push("");
1442
- for (const s of group) {
1443
- lines.push(renderApiSymbol(s, 3));
1444
- lines.push("");
1445
- }
1446
- }
1447
- return lines.join("\n");
1448
- }
1449
- function renderProjectIndexPage(symbolsByPackage, options) {
1450
- const lines = [];
1451
- lines.push(`# ${options.projectName}`);
1452
- lines.push("");
1453
- if (options.projectDescription) {
1454
- lines.push(options.projectDescription);
1455
- lines.push("");
1456
- }
1457
- lines.push("## Packages");
1458
- lines.push("");
1459
- for (const [pkgName, symbols] of symbolsByPackage) {
1460
- const exported = symbols.filter(
1461
- (s) => s.exported && s.kind !== "method" && s.kind !== "property"
1462
- );
1463
- const pkgDoc = symbols.map((s) => s.documentation?.tags?.packageDocumentation?.[0]).find(Boolean);
1464
- const summary = pkgDoc ?? `${exported.length} exported symbol(s).`;
1465
- lines.push(`### [${pkgName}](./packages/${pkgName}/index.md)`);
1466
- lines.push("");
1467
- lines.push(summary);
1468
- lines.push("");
1469
- }
1470
- return lines.join("\n");
1471
- }
1472
- function renderGettingStartedPage(symbolsByPackage, options) {
1473
- let firstExample;
1474
- let firstSymbolName = "";
1475
- let firstPackageName = "";
1476
- outer: for (const [pkgName, symbols] of symbolsByPackage) {
1477
- for (const s of symbols) {
1478
- if (!s.exported || s.kind !== "function") continue;
1479
- const ex = s.documentation?.examples?.[0];
1480
- if (ex) {
1481
- firstExample = ex;
1482
- firstSymbolName = s.name;
1483
- firstPackageName = pkgName;
1484
- break outer;
1485
- }
1486
- }
1487
- }
1488
- const lines = [];
1489
- lines.push("# Getting Started");
1490
- lines.push("");
1491
- lines.push(`Welcome to **${options.projectName}**.`);
1492
- if (options.projectDescription) {
1493
- lines.push("");
1494
- lines.push(options.projectDescription);
1495
- }
1496
- lines.push("");
1497
- lines.push("## Installation");
1498
- lines.push("");
1499
- lines.push("```bash");
1500
- lines.push(`npm install ${options.projectName}`);
1501
- lines.push("```");
1502
- lines.push("");
1503
- if (firstExample) {
1504
- lines.push("## Quick Start");
1505
- lines.push("");
1506
- lines.push(
1507
- `The following example demonstrates \`${firstSymbolName}\` from the \`${firstPackageName}\` package.`
1508
- );
1509
- lines.push("");
1510
- lines.push(`\`\`\`${firstExample.language}`);
1511
- lines.push(firstExample.code.trim());
1512
- lines.push("```");
1513
- lines.push("");
1514
- }
1515
- lines.push("## Next Steps");
1516
- lines.push("");
1517
- lines.push("- Browse the [API Reference](./packages/)");
1518
- for (const pkgName of symbolsByPackage.keys()) {
1519
- lines.push(` - [${pkgName}](./packages/${pkgName}/api-reference.md)`);
1520
- }
1521
- return lines.join("\n");
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");
1522
1778
  }
1523
1779
  function generateDocSite(symbolsByPackage, config, options) {
1524
1780
  const ext = options.format === "mdx" ? "mdx" : "md";
@@ -1549,10 +1805,38 @@ function generateDocSite(symbolsByPackage, config, options) {
1549
1805
  `,
1550
1806
  frontmatter: gettingStartedFrontmatter
1551
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
+ });
1552
1836
  let pkgPosition = 1;
1553
1837
  for (const [pkgName, symbols] of symbolsByPackage) {
1554
1838
  const pkgBase = `packages/${pkgName}`;
1555
- const overviewContent = renderOverviewPage(pkgName, symbols, options);
1839
+ const overviewContent = renderPackageOverviewPage(pkgName, symbols, options);
1556
1840
  const overviewFrontmatter = buildFrontmatterFields(
1557
1841
  pkgName,
1558
1842
  `${pkgName} package overview`,
@@ -1565,29 +1849,17 @@ function generateDocSite(symbolsByPackage, config, options) {
1565
1849
  `,
1566
1850
  frontmatter: overviewFrontmatter
1567
1851
  });
1568
- const apiContent = renderApiReferencePage(pkgName, symbols);
1569
- const apiFrontmatter = buildFrontmatterFields(
1852
+ const apiIndexContent = renderApiIndexPage(pkgName, symbols);
1853
+ const apiIndexFrontmatter = buildFrontmatterFields(
1570
1854
  `${pkgName} \u2014 API Reference`,
1571
1855
  `Full API reference for the ${pkgName} package`,
1572
1856
  options.ssgTarget
1573
1857
  );
1574
1858
  pages.push({
1575
- path: `${pkgBase}/api-reference.${ext}`,
1576
- content: `${serializeFrontmatter(apiFrontmatter)}${apiContent.trimEnd()}
1577
- `,
1578
- frontmatter: apiFrontmatter
1579
- });
1580
- const typesContent = renderTypesPage(pkgName, symbols, options);
1581
- const typesFrontmatter = buildFrontmatterFields(
1582
- `${pkgName} \u2014 Types`,
1583
- `Type contracts for the ${pkgName} package`,
1584
- options.ssgTarget
1585
- );
1586
- pages.push({
1587
- path: `${pkgBase}/types.${ext}`,
1588
- content: `${serializeFrontmatter(typesFrontmatter)}${typesContent.trimEnd()}
1859
+ path: `${pkgBase}/api/index.${ext}`,
1860
+ content: `${serializeFrontmatter(apiIndexFrontmatter)}${apiIndexContent.trimEnd()}
1589
1861
  `,
1590
- frontmatter: typesFrontmatter
1862
+ frontmatter: apiIndexFrontmatter
1591
1863
  });
1592
1864
  const functionsContent = renderFunctionsPage(pkgName, symbols, options);
1593
1865
  const functionsFrontmatter = buildFrontmatterFields(
@@ -1596,11 +1868,23 @@ function generateDocSite(symbolsByPackage, config, options) {
1596
1868
  options.ssgTarget
1597
1869
  );
1598
1870
  pages.push({
1599
- path: `${pkgBase}/functions.${ext}`,
1871
+ path: `${pkgBase}/api/functions.${ext}`,
1600
1872
  content: `${serializeFrontmatter(functionsFrontmatter)}${functionsContent.trimEnd()}
1601
1873
  `,
1602
1874
  frontmatter: functionsFrontmatter
1603
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
+ });
1604
1888
  const examplesContent = renderExamplesPage(pkgName, symbols, options);
1605
1889
  const examplesFrontmatter = buildFrontmatterFields(
1606
1890
  `${pkgName} \u2014 Examples`,
@@ -1608,17 +1892,595 @@ function generateDocSite(symbolsByPackage, config, options) {
1608
1892
  options.ssgTarget
1609
1893
  );
1610
1894
  pages.push({
1611
- path: `${pkgBase}/examples.${ext}`,
1895
+ path: `${pkgBase}/api/examples.${ext}`,
1612
1896
  content: `${serializeFrontmatter(examplesFrontmatter)}${examplesContent.trimEnd()}
1613
1897
  `,
1614
1898
  frontmatter: examplesFrontmatter
1615
1899
  });
1616
1900
  pkgPosition++;
1617
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
+ });
1618
1953
  void config;
1619
1954
  return pages;
1620
1955
  }
1621
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
+
1622
2484
  // src/ssg-config.ts
1623
2485
  function pageSlug5(pagePath) {
1624
2486
  return pagePath.replace(/\.[^.]+$/, "");
@@ -1783,11 +2645,34 @@ function generateSSGConfigs(pages, target, projectName) {
1783
2645
  }
1784
2646
 
1785
2647
  // src/index.ts
1786
- import { mkdir, writeFile as writeFile2 } from "fs/promises";
2648
+ import { existsSync as existsSync2 } from "fs";
2649
+ import { mkdir, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
1787
2650
  import { dirname, join } from "path";
1788
2651
  import { createWalker } from "@forge-ts/core";
1789
- 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) {
1790
2674
  const start = Date.now();
2675
+ const forceStubs = options?.forceStubs ?? false;
1791
2676
  const walker = createWalker(config);
1792
2677
  const symbols = walker.walk();
1793
2678
  await mkdir(config.outDir, { recursive: true });
@@ -1798,7 +2683,8 @@ async function generate(config) {
1798
2683
  const ext = format === "mdx" ? "mdx" : "md";
1799
2684
  await writeFile2(join(config.outDir, `api-reference.${ext}`), content, "utf8");
1800
2685
  }
1801
- const projectName = config.rootDir.split("/").pop() ?? "Project";
2686
+ const resolvedRoot = config.rootDir === "." ? process.cwd() : config.rootDir;
2687
+ const projectName = resolvedRoot.split("/").pop() ?? "Project";
1802
2688
  const symbolsByPackage = groupSymbolsByPackage(symbols, config.rootDir);
1803
2689
  const target = config.gen.ssgTarget ?? DEFAULT_TARGET;
1804
2690
  const adapter = getAdapter(target);
@@ -1806,7 +2692,9 @@ async function generate(config) {
1806
2692
  const pages = generateDocSite(symbolsByPackage, config, {
1807
2693
  format,
1808
2694
  ssgTarget: config.gen.ssgTarget,
1809
- projectName
2695
+ projectName,
2696
+ repositoryUrl: config.project.repository,
2697
+ packageName: config.project.packageName
1810
2698
  });
1811
2699
  const adapterContext = {
1812
2700
  config,
@@ -1818,6 +2706,14 @@ async function generate(config) {
1818
2706
  const transformedPages = adapter.transformPages(pages, adapterContext);
1819
2707
  for (const file of transformedPages) {
1820
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
+ }
1821
2717
  await mkdir(dirname(filePath), { recursive: true });
1822
2718
  await writeFile2(filePath, file.content, "utf8");
1823
2719
  }
@@ -1835,6 +2731,17 @@ async function generate(config) {
1835
2731
  await writeFile2(join(config.outDir, "llms.txt"), llms, "utf8");
1836
2732
  const llmsFull = generateLlmsFullTxt(symbols, config);
1837
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
+ }
1838
2745
  }
1839
2746
  if (config.gen.readmeSync) {
1840
2747
  await syncReadme(join(config.rootDir, "README.md"), symbols);
@@ -1855,6 +2762,8 @@ export {
1855
2762
  generateLlmsTxt,
1856
2763
  generateMarkdown,
1857
2764
  generateSSGConfigs,
2765
+ generateSkillMd,
2766
+ generateSkillPackage,
1858
2767
  getAdapter,
1859
2768
  getAvailableTargets,
1860
2769
  groupSymbolsByPackage,