docmk 1.0.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.
Files changed (70) hide show
  1. package/.claude/skills/pdf/SKILL.md +89 -0
  2. package/.claude/skills/web-scraping/SKILL.md +78 -0
  3. package/CLAUDE.md +90 -0
  4. package/bin/docmk.js +3 -0
  5. package/dist/index.d.ts +1 -0
  6. package/dist/index.js +636 -0
  7. package/dist/index.js.map +1 -0
  8. package/final-site/assets/main-B4orIFxK.css +1 -0
  9. package/final-site/assets/main-CSoKXua6.js +25 -0
  10. package/final-site/favicon.svg +4 -0
  11. package/final-site/index.html +26 -0
  12. package/final-site/robots.txt +4 -0
  13. package/final-site/sitemap.xml +14 -0
  14. package/my-docs/api/README.md +152 -0
  15. package/my-docs/api/advanced.md +260 -0
  16. package/my-docs/getting-started/README.md +24 -0
  17. package/my-docs/tutorials/README.md +272 -0
  18. package/my-docs/tutorials/customization.md +492 -0
  19. package/package.json +59 -0
  20. package/postcss.config.js +6 -0
  21. package/site/assets/main-BZUsYUCF.css +1 -0
  22. package/site/assets/main-q6laQtCD.js +114 -0
  23. package/site/favicon.svg +4 -0
  24. package/site/index.html +23 -0
  25. package/site/robots.txt +4 -0
  26. package/site/sitemap.xml +34 -0
  27. package/site-output/assets/main-B4orIFxK.css +1 -0
  28. package/site-output/assets/main-CSoKXua6.js +25 -0
  29. package/site-output/favicon.svg +4 -0
  30. package/site-output/index.html +26 -0
  31. package/site-output/robots.txt +4 -0
  32. package/site-output/sitemap.xml +14 -0
  33. package/src/builder/index.ts +189 -0
  34. package/src/builder/vite-dev.ts +117 -0
  35. package/src/cli/commands/build.ts +48 -0
  36. package/src/cli/commands/dev.ts +53 -0
  37. package/src/cli/commands/preview.ts +57 -0
  38. package/src/cli/index.ts +42 -0
  39. package/src/client/App.vue +15 -0
  40. package/src/client/components/SearchBox.vue +204 -0
  41. package/src/client/components/Sidebar.vue +18 -0
  42. package/src/client/components/SidebarItem.vue +108 -0
  43. package/src/client/index.html +21 -0
  44. package/src/client/layouts/AppLayout.vue +99 -0
  45. package/src/client/lib/utils.ts +6 -0
  46. package/src/client/main.ts +42 -0
  47. package/src/client/pages/Home.vue +279 -0
  48. package/src/client/pages/SkillPage.vue +565 -0
  49. package/src/client/router.ts +16 -0
  50. package/src/client/styles/global.css +92 -0
  51. package/src/client/utils/routes.ts +69 -0
  52. package/src/parser/index.ts +253 -0
  53. package/src/scanner/index.ts +127 -0
  54. package/src/types/index.ts +45 -0
  55. package/tailwind.config.js +65 -0
  56. package/test-build/assets/main-C2ARPC0e.css +1 -0
  57. package/test-build/assets/main-CHIQpV3B.js +25 -0
  58. package/test-build/favicon.svg +4 -0
  59. package/test-build/index.html +47 -0
  60. package/test-build/robots.txt +4 -0
  61. package/test-build/sitemap.xml +19 -0
  62. package/test-dist/assets/main-B4orIFxK.css +1 -0
  63. package/test-dist/assets/main-CSoKXua6.js +25 -0
  64. package/test-dist/favicon.svg +4 -0
  65. package/test-dist/index.html +26 -0
  66. package/test-dist/robots.txt +4 -0
  67. package/test-dist/sitemap.xml +14 -0
  68. package/tsconfig.json +30 -0
  69. package/tsup.config.ts +13 -0
  70. package/vite.config.ts +21 -0
package/dist/index.js ADDED
@@ -0,0 +1,636 @@
1
+ #!/usr/bin/env node
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __esm = (fn, res) => function __init() {
4
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
+ };
6
+
7
+ // src/scanner/index.ts
8
+ import fs from "fs/promises";
9
+ import path from "path";
10
+ async function scanSkillsDirectory(sourceDir) {
11
+ try {
12
+ await fs.access(sourceDir);
13
+ } catch {
14
+ throw new Error(`Source directory not found: ${sourceDir}`);
15
+ }
16
+ const entries = await fs.readdir(sourceDir, { withFileTypes: true });
17
+ const directories = [];
18
+ for (const entry of entries) {
19
+ if (entry.isDirectory()) {
20
+ const dirPath = path.join(sourceDir, entry.name);
21
+ const skillDirectory = await scanDirectory(dirPath, entry.name);
22
+ directories.push(skillDirectory);
23
+ }
24
+ }
25
+ return directories;
26
+ }
27
+ async function scanDirectory(dirPath, name) {
28
+ const entries = await fs.readdir(dirPath, { withFileTypes: true });
29
+ const children = [];
30
+ let skillFile;
31
+ for (const entry of entries) {
32
+ const entryPath = path.join(dirPath, entry.name);
33
+ if (entry.isDirectory()) {
34
+ const subDir = await scanDirectory(entryPath, entry.name);
35
+ children.push(subDir);
36
+ } else if (entry.isFile() && entry.name.endsWith(".md")) {
37
+ const file = await parseMarkdownFile(entryPath, entry.name);
38
+ if (entry.name === "SKILL.md") {
39
+ skillFile = file;
40
+ } else {
41
+ children.push(file);
42
+ }
43
+ }
44
+ }
45
+ return {
46
+ path: dirPath,
47
+ name,
48
+ children,
49
+ skillFile
50
+ };
51
+ }
52
+ async function parseMarkdownFile(filePath, fileName) {
53
+ const content = await fs.readFile(filePath, "utf-8");
54
+ const stats = await fs.stat(filePath);
55
+ const frontmatterMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n/);
56
+ const frontmatter = {};
57
+ let markdownContent = content;
58
+ if (frontmatterMatch) {
59
+ markdownContent = content.slice(frontmatterMatch[0].length);
60
+ const fmLines = frontmatterMatch[1].split(/\r?\n/);
61
+ for (const line of fmLines) {
62
+ const colonIndex = line.indexOf(":");
63
+ if (colonIndex > 0) {
64
+ const key = line.slice(0, colonIndex).trim();
65
+ const value = line.slice(colonIndex + 1).trim().replace(/^["']|["']$/g, "");
66
+ if (key && value) {
67
+ frontmatter[key] = value;
68
+ }
69
+ }
70
+ }
71
+ }
72
+ return {
73
+ path: filePath,
74
+ name: fileName,
75
+ title: frontmatter.title || fileName.replace(".md", ""),
76
+ description: frontmatter.description,
77
+ content: markdownContent,
78
+ frontmatter,
79
+ lastModified: stats.mtime.getTime()
80
+ };
81
+ }
82
+ async function watchSkillsDirectory(sourceDir, callback) {
83
+ const chokidar = await import("chokidar");
84
+ const watcher = chokidar.watch(sourceDir, {
85
+ ignored: /node_modules/,
86
+ persistent: true,
87
+ ignoreInitial: true
88
+ });
89
+ let debounceTimer;
90
+ const handleChange = () => {
91
+ clearTimeout(debounceTimer);
92
+ debounceTimer = setTimeout(async () => {
93
+ try {
94
+ const directories = await scanSkillsDirectory(sourceDir);
95
+ callback(directories);
96
+ } catch (error) {
97
+ console.error("Error rescanning source directory:", error);
98
+ }
99
+ }, 300);
100
+ };
101
+ watcher.on("add", handleChange);
102
+ watcher.on("change", handleChange);
103
+ watcher.on("unlink", handleChange);
104
+ watcher.on("addDir", handleChange);
105
+ watcher.on("unlinkDir", handleChange);
106
+ return watcher;
107
+ }
108
+ var init_scanner = __esm({
109
+ "src/scanner/index.ts"() {
110
+ "use strict";
111
+ }
112
+ });
113
+
114
+ // src/parser/index.ts
115
+ import MarkdownIt from "markdown-it";
116
+ import { createHighlighter } from "shiki";
117
+ async function getHighlighter() {
118
+ if (!highlighter) {
119
+ highlighter = await createHighlighter({
120
+ themes: ["github-dark", "github-light"],
121
+ langs: ["javascript", "typescript", "bash", "shell", "json", "html", "css", "vue", "jsx", "tsx", "python", "markdown", "yaml", "sql", "go", "rust", "java", "c", "cpp"]
122
+ });
123
+ }
124
+ return highlighter;
125
+ }
126
+ async function parseSkillsToConfig(directories, siteConfig) {
127
+ const files = [];
128
+ const navigation = await generateNavigation(directories);
129
+ collectAllFiles(directories, files);
130
+ for (const file of files) {
131
+ await enhanceFileContent(file);
132
+ }
133
+ return {
134
+ siteConfig,
135
+ navigation,
136
+ files,
137
+ directories
138
+ };
139
+ }
140
+ function collectAllFiles(items, files) {
141
+ for (const item of items) {
142
+ if ("content" in item) {
143
+ files.push(item);
144
+ } else {
145
+ if (item.skillFile) {
146
+ files.push(item.skillFile);
147
+ }
148
+ collectAllFiles(item.children, files);
149
+ }
150
+ }
151
+ }
152
+ async function enhanceFileContent(file) {
153
+ try {
154
+ let content = file.content;
155
+ file.title = file.frontmatter.title || file.title;
156
+ file.description = file.frontmatter.description || file.description;
157
+ if (file.frontmatter.title) {
158
+ const h1Match = content.match(/^\s*#\s+(.+)$/m);
159
+ if (h1Match) {
160
+ const h1Text = h1Match[1].trim();
161
+ if (h1Text === file.frontmatter.title) {
162
+ content = content.replace(/^\s*#\s+.+\n*/, "");
163
+ }
164
+ }
165
+ }
166
+ let html = md.render(content);
167
+ html = await highlightCodeBlocks(html);
168
+ file.frontmatter.html = html;
169
+ const headings = extractHeadings(content);
170
+ file.frontmatter.headings = headings;
171
+ } catch (error) {
172
+ console.warn(`Failed to enhance content for ${file.path}:`, error);
173
+ try {
174
+ file.frontmatter.html = md.render(file.content);
175
+ } catch (e) {
176
+ console.error(`Failed to render markdown for ${file.path}:`, e);
177
+ }
178
+ }
179
+ }
180
+ async function highlightCodeBlocks(html) {
181
+ const hl = await getHighlighter();
182
+ const codeBlockRegex = /<pre class="shiki-pending" data-lang="([^"]*)"[^>]*><code>([^]*?)<\/code><\/pre>/g;
183
+ const matches = [...html.matchAll(codeBlockRegex)];
184
+ for (const match of matches) {
185
+ const [fullMatch, lang, escapedCode] = match;
186
+ const code = escapedCode.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&").replace(/&quot;/g, '"').replace(/&#39;/g, "'");
187
+ try {
188
+ const validLang = hl.getLoadedLanguages().includes(lang) ? lang : "text";
189
+ const highlighted = hl.codeToHtml(code, {
190
+ lang: validLang,
191
+ theme: "github-dark"
192
+ });
193
+ html = html.replace(fullMatch, highlighted);
194
+ } catch (e) {
195
+ console.warn(`Failed to highlight ${lang}:`, e);
196
+ }
197
+ }
198
+ return html;
199
+ }
200
+ function extractHeadings(content) {
201
+ const headings = [];
202
+ const lines = content.split("\n");
203
+ for (const line of lines) {
204
+ const match = line.match(/^(#{1,6})\\s+(.+)$/);
205
+ if (match) {
206
+ const level = match[1].length;
207
+ const text = match[2].trim();
208
+ const anchor = text.toLowerCase().replace(/[^\\w\\s-]/g, "").replace(/\\s+/g, "-").trim();
209
+ headings.push({ level, text, anchor });
210
+ }
211
+ }
212
+ return headings;
213
+ }
214
+ async function generateNavigation(directories) {
215
+ const navigation = [];
216
+ for (const dir of directories) {
217
+ const navItem = {
218
+ text: dir.skillFile?.title || formatDirName(dir.name),
219
+ link: dir.skillFile ? getFileRoute(dir.skillFile) : void 0
220
+ };
221
+ if (dir.children.length > 0) {
222
+ navItem.children = [];
223
+ for (const child of dir.children) {
224
+ if ("content" in child) {
225
+ navItem.children.push({
226
+ text: child.title || formatFileName(child.name),
227
+ link: getFileRoute(child)
228
+ });
229
+ } else {
230
+ const subNav = await generateNavigation([child]);
231
+ navItem.children.push(...subNav);
232
+ }
233
+ }
234
+ }
235
+ navigation.push(navItem);
236
+ }
237
+ return navigation;
238
+ }
239
+ function getFileRoute(file) {
240
+ const filePath = file.path;
241
+ const patterns = ["my-docs", "docs", "documentation", ".claude/skills"];
242
+ let relativePath = "";
243
+ for (const pattern of patterns) {
244
+ const index = filePath.indexOf(pattern);
245
+ if (index !== -1) {
246
+ relativePath = filePath.slice(index + pattern.length);
247
+ break;
248
+ }
249
+ }
250
+ if (!relativePath) {
251
+ const segments2 = filePath.split("/").filter(Boolean);
252
+ if (segments2.length >= 2) {
253
+ relativePath = "/" + segments2.slice(-2).join("/");
254
+ } else {
255
+ return "/";
256
+ }
257
+ }
258
+ const segments = relativePath.split("/").filter(Boolean);
259
+ if (segments.length === 0) return "/";
260
+ const lastSegment = segments[segments.length - 1];
261
+ if (lastSegment.endsWith(".md")) {
262
+ segments[segments.length - 1] = lastSegment.slice(0, -3);
263
+ }
264
+ const finalSegment = segments[segments.length - 1];
265
+ if (finalSegment === "SKILL" || finalSegment === "README") {
266
+ segments.pop();
267
+ }
268
+ return "/" + segments.join("/");
269
+ }
270
+ function formatDirName(name) {
271
+ return name.split("-").map(
272
+ (word) => word.charAt(0).toUpperCase() + word.slice(1)
273
+ ).join(" ");
274
+ }
275
+ function formatFileName(name) {
276
+ const baseName = name.replace(".md", "");
277
+ return formatDirName(baseName);
278
+ }
279
+ var highlighter, md;
280
+ var init_parser = __esm({
281
+ "src/parser/index.ts"() {
282
+ "use strict";
283
+ highlighter = null;
284
+ md = new MarkdownIt({
285
+ html: true,
286
+ linkify: true,
287
+ typographer: true,
288
+ highlight: function(str, lang) {
289
+ return `<pre class="shiki-pending" data-lang="${lang || "text"}"><code>${md.utils.escapeHtml(str)}</code></pre>`;
290
+ }
291
+ });
292
+ }
293
+ });
294
+
295
+ // src/cli/index.ts
296
+ import { Command } from "commander";
297
+
298
+ // src/cli/commands/dev.ts
299
+ import path3 from "path";
300
+
301
+ // src/builder/vite-dev.ts
302
+ init_scanner();
303
+ init_parser();
304
+ import { createServer } from "vite";
305
+ import vue from "@vitejs/plugin-vue";
306
+ import path2 from "path";
307
+ async function createViteDevServer(options) {
308
+ let currentConfig = options.config;
309
+ const server = await createServer({
310
+ root: path2.resolve(process.cwd(), "src/client"),
311
+ server: {
312
+ port: options.port,
313
+ host: "localhost"
314
+ },
315
+ plugins: [
316
+ vue(),
317
+ // Custom plugin to inject config and handle API routes
318
+ {
319
+ name: "docgen-dev",
320
+ configureServer(server2) {
321
+ server2.middlewares.use("/api/config", (req, res, next) => {
322
+ if (req.method === "GET") {
323
+ res.setHeader("Content-Type", "application/json");
324
+ res.end(JSON.stringify(currentConfig));
325
+ } else {
326
+ next();
327
+ }
328
+ });
329
+ server2.middlewares.use((req, res, next) => {
330
+ if (req.url === "/" || req.url === "/index.html") {
331
+ next();
332
+ } else {
333
+ next();
334
+ }
335
+ });
336
+ },
337
+ transformIndexHtml: {
338
+ order: "pre",
339
+ handler(html) {
340
+ const configScript = `
341
+ <script>
342
+ // Config will be loaded from /api/config
343
+ globalThis.__DOCGEN_CONFIG__ = null;
344
+ </script>
345
+ `;
346
+ return html.replace("<head>", `<head>${configScript}`);
347
+ }
348
+ }
349
+ }
350
+ ],
351
+ resolve: {
352
+ alias: {
353
+ "@": path2.resolve(process.cwd(), "src/client")
354
+ }
355
+ },
356
+ define: {
357
+ __DOCGEN_CONFIG__: JSON.stringify(currentConfig)
358
+ }
359
+ });
360
+ const watcher = await watchSkillsDirectory(options.skillsDir, async (directories) => {
361
+ console.log("\u{1F4DD} Skills directory changed, updating configuration...");
362
+ try {
363
+ currentConfig = await parseSkillsToConfig(directories, currentConfig.siteConfig);
364
+ server.ws.send({
365
+ type: "full-reload"
366
+ });
367
+ console.log("\u2705 Configuration updated");
368
+ } catch (error) {
369
+ console.error("\u274C Failed to update configuration:", error);
370
+ server.ws.send({
371
+ type: "error",
372
+ err: {
373
+ message: `Failed to update configuration: ${error.message}`,
374
+ stack: error.stack
375
+ }
376
+ });
377
+ }
378
+ });
379
+ await server.listen();
380
+ const originalClose = server.close.bind(server);
381
+ server.close = async () => {
382
+ await watcher?.close();
383
+ return originalClose();
384
+ };
385
+ return server;
386
+ }
387
+
388
+ // src/cli/commands/dev.ts
389
+ init_scanner();
390
+ init_parser();
391
+ async function devCommand(options) {
392
+ console.log("\u{1F680} Starting DocGen development server...");
393
+ const sourceDir = path3.resolve(process.cwd(), options.dir);
394
+ const port = parseInt(options.port, 10);
395
+ try {
396
+ console.log(`\u{1F4C1} Scanning source directory: ${sourceDir}`);
397
+ const directories = await scanSkillsDirectory(sourceDir);
398
+ console.log(`\u2705 Found ${directories.length} directories`);
399
+ const config = await parseSkillsToConfig(directories, {
400
+ title: "Documentation",
401
+ description: "Documentation generated from source directory",
402
+ baseUrl: "/",
403
+ skillsDir: sourceDir,
404
+ outputDir: "dist"
405
+ });
406
+ const server = await createViteDevServer({
407
+ port,
408
+ skillsDir: sourceDir,
409
+ config
410
+ });
411
+ console.log(`\u{1F389} Dev server running at http://localhost:${port}`);
412
+ process.on("SIGINT", async () => {
413
+ console.log("\\n\u{1F44B} Shutting down dev server...");
414
+ await server.close();
415
+ process.exit(0);
416
+ });
417
+ } catch (error) {
418
+ console.error("\u274C Failed to start dev server:", error);
419
+ process.exit(1);
420
+ }
421
+ }
422
+
423
+ // src/cli/commands/build.ts
424
+ import path5 from "path";
425
+
426
+ // src/builder/index.ts
427
+ import { build } from "vite";
428
+ import vue2 from "@vitejs/plugin-vue";
429
+ import path4 from "path";
430
+ import fs2 from "fs/promises";
431
+ async function buildSite(options, config) {
432
+ console.log("\u{1F3D7}\uFE0F Building documentation site...");
433
+ await fs2.mkdir(options.output, { recursive: true });
434
+ const configPath = path4.join(process.cwd(), "temp-config.json");
435
+ await fs2.writeFile(configPath, JSON.stringify(config, null, 2));
436
+ try {
437
+ await build({
438
+ root: path4.resolve(process.cwd(), "src/client"),
439
+ base: config.siteConfig.baseUrl,
440
+ build: {
441
+ outDir: options.output,
442
+ emptyOutDir: true,
443
+ rollupOptions: {
444
+ input: {
445
+ main: path4.resolve(process.cwd(), "src/client/index.html")
446
+ }
447
+ }
448
+ },
449
+ plugins: [
450
+ vue2(),
451
+ // Plugin to inject config during build
452
+ {
453
+ name: "docgen-build",
454
+ transformIndexHtml: {
455
+ order: "pre",
456
+ handler(html) {
457
+ const configJson = JSON.stringify(config);
458
+ const utf8Encoded = unescape(encodeURIComponent(configJson));
459
+ const configBase64 = Buffer.from(utf8Encoded, "binary").toString("base64");
460
+ const configScript = `<script>globalThis.__DOCGEN_CONFIG__=JSON.parse(decodeURIComponent(escape(atob("${configBase64}"))));</script>`;
461
+ return html.replace("</head>", `${configScript}
462
+ </head>`);
463
+ }
464
+ }
465
+ }
466
+ ],
467
+ resolve: {
468
+ alias: {
469
+ "@": path4.resolve(process.cwd(), "src/client")
470
+ }
471
+ },
472
+ define: {
473
+ __DOCGEN_CONFIG__: JSON.stringify(config)
474
+ }
475
+ });
476
+ await generateSitemap(options.output, config);
477
+ await generateRobotsTxt(options.output, config);
478
+ await copyAssets(options.output);
479
+ console.log("\u2705 Build completed successfully!");
480
+ } finally {
481
+ try {
482
+ await fs2.unlink(configPath);
483
+ } catch {
484
+ }
485
+ }
486
+ }
487
+ async function generateSitemap(outputDir, config) {
488
+ console.log("\u{1F4C4} Generating sitemap...");
489
+ const baseUrl = config.siteConfig.baseUrl.replace(/\/$/, "");
490
+ const urls = [];
491
+ urls.push(`${baseUrl}/`);
492
+ for (const file of config.files) {
493
+ const route = getFileRoute2(file.path, config.siteConfig.skillsDir);
494
+ if (route !== "/") {
495
+ urls.push(`${baseUrl}${route}`);
496
+ }
497
+ }
498
+ const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
499
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
500
+ ${urls.map((url) => ` <url>
501
+ <loc>${url}</loc>
502
+ <lastmod>${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}</lastmod>
503
+ <changefreq>weekly</changefreq>
504
+ <priority>0.8</priority>
505
+ </url>`).join("\\n")}
506
+ </urlset>`;
507
+ await fs2.writeFile(path4.join(outputDir, "sitemap.xml"), sitemap);
508
+ }
509
+ async function generateRobotsTxt(outputDir, config) {
510
+ console.log("\u{1F916} Generating robots.txt...");
511
+ const baseUrl = config.siteConfig.baseUrl.replace(/\/$/, "");
512
+ const robotsTxt = `User-agent: *
513
+ Allow: /
514
+
515
+ Sitemap: ${baseUrl}/sitemap.xml`;
516
+ await fs2.writeFile(path4.join(outputDir, "robots.txt"), robotsTxt);
517
+ }
518
+ async function copyAssets(outputDir) {
519
+ console.log("\u{1F4C1} Copying static assets...");
520
+ const faviconPath = path4.join(outputDir, "favicon.ico");
521
+ try {
522
+ await fs2.access(faviconPath);
523
+ } catch {
524
+ const faviconSvg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
525
+ <rect width="32" height="32" rx="4" fill="#3182ce"/>
526
+ <text x="16" y="22" text-anchor="middle" fill="white" font-family="system-ui" font-size="18" font-weight="bold">D</text>
527
+ </svg>`;
528
+ await fs2.writeFile(path4.join(outputDir, "favicon.svg"), faviconSvg);
529
+ }
530
+ }
531
+ function getFileRoute2(filePath, baseDir) {
532
+ const normalizedFilePath = path4.normalize(filePath);
533
+ const normalizedBaseDir = path4.normalize(baseDir);
534
+ if (!normalizedFilePath.startsWith(normalizedBaseDir)) {
535
+ return "/";
536
+ }
537
+ const relativePath = normalizedFilePath.slice(normalizedBaseDir.length);
538
+ const segments = relativePath.split(path4.sep).filter(Boolean);
539
+ if (segments.length === 0) return "/";
540
+ const lastSegment = segments[segments.length - 1];
541
+ if (lastSegment.endsWith(".md")) {
542
+ segments[segments.length - 1] = lastSegment.slice(0, -3);
543
+ }
544
+ const finalSegment = segments[segments.length - 1];
545
+ if (finalSegment === "SKILL" || finalSegment === "README") {
546
+ segments.pop();
547
+ }
548
+ return "/" + segments.join("/");
549
+ }
550
+
551
+ // src/cli/commands/build.ts
552
+ init_scanner();
553
+ init_parser();
554
+ async function buildCommand(options) {
555
+ console.log("\u{1F3D7}\uFE0F Building DocGen documentation site...");
556
+ const sourceDir = path5.resolve(process.cwd(), options.dir);
557
+ const outputDir = path5.resolve(process.cwd(), options.output);
558
+ try {
559
+ console.log(`\u{1F4C1} Scanning source directory: ${sourceDir}`);
560
+ const directories = await scanSkillsDirectory(sourceDir);
561
+ console.log(`\u2705 Found ${directories.length} directories`);
562
+ const config = await parseSkillsToConfig(directories, {
563
+ title: "Documentation",
564
+ description: "Documentation generated from source directory",
565
+ baseUrl: "/",
566
+ skillsDir: sourceDir,
567
+ outputDir
568
+ });
569
+ console.log(`\u{1F4E6} Building to: ${outputDir}`);
570
+ await buildSite({
571
+ input: sourceDir,
572
+ output: outputDir,
573
+ mode: "production"
574
+ }, config);
575
+ console.log("\u2705 Build completed successfully!");
576
+ console.log(`\u{1F4C2} Built files are in: ${outputDir}`);
577
+ } catch (error) {
578
+ console.error("\u274C Build failed:", error);
579
+ process.exit(1);
580
+ }
581
+ }
582
+
583
+ // src/cli/commands/preview.ts
584
+ import path6 from "path";
585
+ import fs3 from "fs/promises";
586
+ import sirv from "sirv";
587
+ import { createServer as createServer2 } from "http";
588
+ async function previewCommand(options) {
589
+ console.log("\u{1F440} Starting DocGen preview server...");
590
+ const outputDir = path6.resolve(process.cwd(), options.output);
591
+ const port = parseInt(options.port, 10);
592
+ try {
593
+ try {
594
+ await fs3.access(outputDir);
595
+ } catch {
596
+ console.error(`\u274C Build directory not found: ${outputDir}`);
597
+ console.log('\u{1F4A1} Run "docgen build" first to generate the static site');
598
+ process.exit(1);
599
+ }
600
+ const serve = sirv(outputDir, {
601
+ single: true,
602
+ // SPA mode
603
+ dev: false,
604
+ setHeaders: (res, pathname) => {
605
+ if (pathname.endsWith(".html") || pathname === "/") {
606
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
607
+ }
608
+ }
609
+ });
610
+ const server = createServer2(serve);
611
+ server.listen(port, () => {
612
+ console.log(`\u{1F389} Preview server running at http://localhost:${port}`);
613
+ console.log(`\u{1F4C2} Serving files from: ${outputDir}`);
614
+ });
615
+ process.on("SIGINT", () => {
616
+ console.log("\\n\u{1F44B} Shutting down preview server...");
617
+ server.close(() => {
618
+ process.exit(0);
619
+ });
620
+ });
621
+ } catch (error) {
622
+ console.error("\u274C Failed to start preview server:", error);
623
+ process.exit(1);
624
+ }
625
+ }
626
+
627
+ // src/cli/index.ts
628
+ var program = new Command();
629
+ program.name("docmk").description("CLI tool for generating documentation from any directory").version("1.0.0").argument("[directory]", "Source directory path (starts dev server)", "./docs").option("-p, --port <port>", "Port to run dev server on", "3000").action((directory, options) => {
630
+ devCommand({ dir: directory, port: options.port });
631
+ });
632
+ program.command("dev").description("Start development server").option("-p, --port <port>", "Port to run dev server on", "3000").option("-d, --dir <directory>", "Source directory path", "./docs").action(devCommand);
633
+ program.command("build").description("Build static documentation site").option("-d, --dir <directory>", "Source directory path", "./docs").option("-o, --output <directory>", "Output directory", "dist").action(buildCommand);
634
+ program.command("preview").description("Preview built documentation site").option("-p, --port <port>", "Port to run preview server on", "4173").option("-o, --output <directory>", "Built site directory", "dist").action(previewCommand);
635
+ program.parse();
636
+ //# sourceMappingURL=index.js.map