skrypt-ai 0.3.4 → 0.4.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 (95) hide show
  1. package/README.md +1 -1
  2. package/dist/auth/index.d.ts +0 -1
  3. package/dist/auth/index.js +3 -5
  4. package/dist/autofix/index.js +15 -3
  5. package/dist/cli.js +19 -4
  6. package/dist/commands/check-links.js +164 -174
  7. package/dist/commands/deploy.js +5 -2
  8. package/dist/commands/generate.js +206 -199
  9. package/dist/commands/i18n.js +3 -20
  10. package/dist/commands/init.js +47 -40
  11. package/dist/commands/lint.js +3 -20
  12. package/dist/commands/mcp.js +125 -122
  13. package/dist/commands/monitor.js +125 -108
  14. package/dist/commands/review-pr.js +1 -1
  15. package/dist/commands/sdk.js +1 -1
  16. package/dist/config/loader.js +21 -2
  17. package/dist/generator/organizer.d.ts +3 -0
  18. package/dist/generator/organizer.js +4 -9
  19. package/dist/generator/writer.js +2 -10
  20. package/dist/github/pr-comments.js +21 -8
  21. package/dist/plugins/index.js +1 -0
  22. package/dist/scanner/index.js +8 -2
  23. package/dist/template/docs.json +2 -1
  24. package/dist/template/next.config.mjs +2 -1
  25. package/dist/template/package.json +17 -15
  26. package/dist/template/public/favicon.svg +4 -0
  27. package/dist/template/public/search-index.json +1 -1
  28. package/dist/template/scripts/build-search-index.mjs +120 -25
  29. package/dist/template/src/app/api/chat/route.ts +11 -3
  30. package/dist/template/src/app/docs/README.md +28 -0
  31. package/dist/template/src/app/docs/[...slug]/page.tsx +139 -16
  32. package/dist/template/src/app/docs/auth/page.mdx +589 -0
  33. package/dist/template/src/app/docs/autofix/page.mdx +624 -0
  34. package/dist/template/src/app/docs/cli/page.mdx +217 -0
  35. package/dist/template/src/app/docs/config/page.mdx +428 -0
  36. package/dist/template/src/app/docs/configuration/page.mdx +86 -0
  37. package/dist/template/src/app/docs/deployment/page.mdx +112 -0
  38. package/dist/template/src/app/docs/error.tsx +20 -0
  39. package/dist/template/src/app/docs/generator/generator.md +504 -0
  40. package/dist/template/src/app/docs/generator/organizer.md +779 -0
  41. package/dist/template/src/app/docs/generator/page.mdx +613 -0
  42. package/dist/template/src/app/docs/github/page.mdx +502 -0
  43. package/dist/template/src/app/docs/llm/anthropic-client.md +549 -0
  44. package/dist/template/src/app/docs/llm/index.md +471 -0
  45. package/dist/template/src/app/docs/llm/page.mdx +428 -0
  46. package/dist/template/src/app/docs/llms-full.md +256 -0
  47. package/dist/template/src/app/docs/llms.txt +2971 -0
  48. package/dist/template/src/app/docs/not-found.tsx +23 -0
  49. package/dist/template/src/app/docs/page.mdx +0 -3
  50. package/dist/template/src/app/docs/plugins/page.mdx +1793 -0
  51. package/dist/template/src/app/docs/pro/page.mdx +121 -0
  52. package/dist/template/src/app/docs/quickstart/page.mdx +93 -0
  53. package/dist/template/src/app/docs/scanner/content-type.md +599 -0
  54. package/dist/template/src/app/docs/scanner/index.md +212 -0
  55. package/dist/template/src/app/docs/scanner/page.mdx +307 -0
  56. package/dist/template/src/app/docs/scanner/python.md +469 -0
  57. package/dist/template/src/app/docs/scanner/python_parser.md +1056 -0
  58. package/dist/template/src/app/docs/scanner/rust.md +325 -0
  59. package/dist/template/src/app/docs/scanner/typescript.md +201 -0
  60. package/dist/template/src/app/error.tsx +3 -3
  61. package/dist/template/src/app/icon.tsx +29 -0
  62. package/dist/template/src/app/layout.tsx +42 -0
  63. package/dist/template/src/app/not-found.tsx +35 -0
  64. package/dist/template/src/app/page.tsx +62 -28
  65. package/dist/template/src/components/ai-chat.tsx +26 -21
  66. package/dist/template/src/components/breadcrumbs.tsx +46 -2
  67. package/dist/template/src/components/copy-button.tsx +17 -3
  68. package/dist/template/src/components/docs-layout.tsx +142 -8
  69. package/dist/template/src/components/feedback.tsx +4 -2
  70. package/dist/template/src/components/footer.tsx +42 -0
  71. package/dist/template/src/components/header.tsx +29 -5
  72. package/dist/template/src/components/mdx/accordion.tsx +7 -6
  73. package/dist/template/src/components/mdx/card.tsx +19 -7
  74. package/dist/template/src/components/mdx/code-block.tsx +17 -3
  75. package/dist/template/src/components/mdx/code-group.tsx +65 -18
  76. package/dist/template/src/components/mdx/code-playground.tsx +3 -0
  77. package/dist/template/src/components/mdx/go-playground.tsx +3 -0
  78. package/dist/template/src/components/mdx/highlighted-code.tsx +171 -76
  79. package/dist/template/src/components/mdx/python-playground.tsx +2 -0
  80. package/dist/template/src/components/mdx/tabs.tsx +74 -6
  81. package/dist/template/src/components/page-header.tsx +19 -0
  82. package/dist/template/src/components/scroll-to-top.tsx +33 -0
  83. package/dist/template/src/components/search-dialog.tsx +206 -52
  84. package/dist/template/src/components/sidebar.tsx +136 -77
  85. package/dist/template/src/components/table-of-contents.tsx +23 -7
  86. package/dist/template/src/lib/highlight.ts +90 -31
  87. package/dist/template/src/lib/search.ts +14 -4
  88. package/dist/template/src/lib/theme-utils.ts +140 -0
  89. package/dist/template/src/styles/globals.css +307 -166
  90. package/dist/template/src/types/remark-gfm.d.ts +2 -0
  91. package/dist/utils/files.d.ts +9 -0
  92. package/dist/utils/files.js +33 -0
  93. package/dist/utils/validation.d.ts +4 -0
  94. package/dist/utils/validation.js +38 -0
  95. package/package.json +1 -4
@@ -0,0 +1,613 @@
1
+ ## Functions
2
+
3
+ ### `writeLlmsTxt`
4
+
5
+ ```typescript
6
+ async function writeLlmsTxt(docs: GeneratedDoc[], outputDir: string, options: { projectName?: string; description?: string } = {}): Promise<void>
7
+ ```
8
+
9
+ Use this to generate an `llms.txt` file for your API documentation, making it discoverable and consumable by LLM-powered answer engines following the [llmstxt.org](https://llmstxt.org) convention.
10
+
11
+ This is particularly useful for Answer Engine Optimization (AEO) — the equivalent of SEO but for AI assistants and LLM-based search tools.
12
+
13
+ #### Parameters
14
+
15
+ | Name | Type | Required | Description |
16
+ |------|------|----------|-------------|
17
+ | `docs` | `GeneratedDoc[]` | Yes | Array of generated documentation objects to include in the output file |
18
+ | `outputDir` | `string` | Yes | Directory path where the `llms.txt` file will be written |
19
+ | `options` | `object` | No | Optional configuration for the output file |
20
+ | `options.projectName` | `string` | No | Name of the project/API (defaults to `'API'`) |
21
+ | `options.description` | `string` | No | Short description of the project included in the file header |
22
+
23
+ #### Returns
24
+
25
+ `Promise<void>` — Resolves when the `llms.txt` file has been successfully written to `outputDir`. Rejects if the directory cannot be created or the file cannot be written.
26
+
27
+ ## Output Format
28
+
29
+ The generated `llms.txt` file follows the [llmstxt.org](https://llmstxt.org) specification, which structures documentation in a way that LLMs can efficiently parse and reference when answering questions about your API.
30
+
31
+ **Example:**
32
+
33
+ ```typescript example.ts
34
+ import { mkdir, writeFile } from 'fs/promises'
35
+ import { join } from 'path'
36
+ import * as os from 'os'
37
+ import * as path from 'path'
38
+
39
+ // --- Inline types (mirrors the real GeneratedDoc shape) ---
40
+ type GeneratedDoc = {
41
+ filePath: string
42
+ elementName: string
43
+ content: string
44
+ description?: string
45
+ }
46
+
47
+ // --- Self-contained implementation of writeLlmsTxt ---
48
+ async function writeLlmsTxt(
49
+ docs: GeneratedDoc[],
50
+ outputDir: string,
51
+ options: { projectName?: string; description?: string } = {}
52
+ ): Promise<void> {
53
+ const projectName = options.projectName || 'API'
54
+ const description = options.description || ''
55
+
56
+ const lines: string[] = []
57
+
58
+ // Header block following llmstxt.org convention
59
+ lines.push(`# ${projectName}`)
60
+ if (description) {
61
+ lines.push('')
62
+ lines.push(`> ${description}`)
63
+ }
64
+ lines.push('')
65
+ lines.push('## Documentation')
66
+ lines.push('')
67
+
68
+ // List each documented element
69
+ for (const doc of docs) {
70
+ const label = doc.elementName || path.basename(doc.filePath)
71
+ lines.push(`- **${label}**: ${doc.description || doc.content.slice(0, 120).replace(/\n/g, ' ')}`)
72
+ }
73
+
74
+ lines.push('')
75
+ lines.push(`---`)
76
+ lines.push(`Generated by autodocs • ${new Date().toISOString()}`)
77
+
78
+ const outputPath = join(outputDir, 'llms.txt')
79
+
80
+ await mkdir(outputDir, { recursive: true })
81
+ await writeFile(outputPath, lines.join('\n'), 'utf-8')
82
+
83
+ console.log(`✅ llms.txt written to: ${outputPath}`)
84
+ }
85
+
86
+ // --- Example usage ---
87
+ const exampleDocs: GeneratedDoc[] = [
88
+ {
89
+ filePath: 'src/auth/login.ts',
90
+ elementName: 'login',
91
+ description: 'Authenticates a user and returns a session token.',
92
+ content: 'async function login(email: string, password: string): Promise<string>',
93
+ },
94
+ {
95
+ filePath: 'src/auth/logout.ts',
96
+ elementName: 'logout',
97
+ description: 'Invalidates the current session token.',
98
+ content: 'async function logout(token: string): Promise<void>',
99
+ },
100
+ {
101
+ filePath: 'src/users/getUser.ts',
102
+ elementName: 'getUser',
103
+ description: 'Fetches a user record by their unique ID.',
104
+ content: 'async function getUser(userId: string): Promise<User>',
105
+ },
106
+ ]
107
+
108
+ async function main() {
109
+ // Write to a temp directory so this example is safe to run anywhere
110
+ const outputDir = join(os.tmpdir(), 'autodocs-example')
111
+
112
+ try {
113
+ await writeLlmsTxt(exampleDocs, outputDir, {
114
+ projectName: 'MyApp API',
115
+ description: 'REST and SDK documentation for the MyApp platform.',
116
+ })
117
+
118
+ // Verify the output
119
+ const { readFile } = await import('fs/promises')
120
+ const content = await readFile(join(outputDir, 'llms.txt'), 'utf-8')
121
+ console.log('\n--- Generated llms.txt ---\n')
122
+ console.log(content)
123
+ // Expected output:
124
+ // # MyApp API
125
+ //
126
+ // > REST and SDK documentation for the MyApp platform.
127
+ //
128
+ // ## Documentation
129
+ //
130
+ // - **login**: Authenticates a user and returns a session token.
131
+ // - **logout**: Invalidates the current session token.
132
+ // - **getUser**: Fetches a user record by their unique ID.
133
+ //
134
+ // ---
135
+ // Generated by autodocs • 2024-01-15T10:30:00.000Z
136
+ } catch (error) {
137
+ console.error('❌ Failed to write llms.txt:', error)
138
+ process.exit(1)
139
+ }
140
+ }
141
+
142
+ main()
143
+ ```
144
+
145
+ ### `writeDocsToDirectory`
146
+
147
+ ```typescript
148
+ async function writeDocsToDirectory(results: FileGenerationResult[], outputDir: string, sourceDir: string): Promise<{ filesWritten: number; totalDocs: number }>
149
+ ```
150
+
151
+ Use this to persist generated documentation to disk, writing markdown files into an output directory while preserving the relative structure of your source directory.
152
+
153
+ After generating docs for your source files, pass the results to this function to write them out as organized markdown files. It handles directory creation automatically and maps each source file to a corresponding output path.
154
+
155
+ ### Parameters
156
+
157
+ | Name | Type | Required | Description |
158
+ |------|------|----------|-------------|
159
+ | `results` | `FileGenerationResult[]` | ✅ | Array of generation results, each containing a source file path and its generated documentation entries |
160
+ | `outputDir` | `string` | ✅ | Root directory where documentation files will be written (created if it doesn't exist) |
161
+ | `sourceDir` | `string` | ✅ | Root directory of your source files — used to compute relative paths so the output structure mirrors your source layout |
162
+
163
+ #### Returns
164
+
165
+ Returns a `Promise` that resolves to an object with:
166
+
167
+ | Field | Type | Description |
168
+ |-------|------|-------------|
169
+ | `filesWritten` | `number` | Number of markdown files successfully written to disk |
170
+ | `totalDocs` | `number` | Total number of individual documentation entries across all files |
171
+
172
+ ### Notes
173
+
174
+ - Output directories are created recursively — no need to pre-create nested folders
175
+ - Source file paths are made relative to `sourceDir` to determine output paths, so `/src/utils/math.ts` becomes `<outputDir>/utils/math.md`
176
+ - Files with no generated docs are skipped and won't count toward `filesWritten`
177
+
178
+ **Example:**
179
+
180
+ ```typescript example.ts
181
+ import { mkdir, writeFile } from 'fs/promises'
182
+ import { join, dirname, relative, basename } from 'path'
183
+
184
+ // --- Inline types (mirrors autodocs internals) ---
185
+ interface GeneratedDoc {
186
+ name: string
187
+ kind: 'function' | 'class' | 'interface' | 'type'
188
+ markdown: string
189
+ }
190
+
191
+ interface FileGenerationResult {
192
+ sourceFile: string
193
+ docs: GeneratedDoc[]
194
+ error?: string
195
+ }
196
+
197
+ // --- Inline implementation of writeDocsToDirectory ---
198
+ async function writeDocsToDirectory(
199
+ results: FileGenerationResult[],
200
+ outputDir: string,
201
+ sourceDir: string
202
+ ): Promise<{ filesWritten: number; totalDocs: number }> {
203
+ let filesWritten = 0
204
+ let totalDocs = 0
205
+
206
+ for (const result of results) {
207
+ if (result.error || result.docs.length === 0) continue
208
+
209
+ // Mirror source structure in output directory
210
+ const relativePath = relative(sourceDir, result.sourceFile)
211
+ const outputFile = join(
212
+ outputDir,
213
+ relativePath.replace(/\.(ts|tsx|js|jsx)$/, '.md')
214
+ )
215
+
216
+ // Combine all docs for this file into one markdown file
217
+ const content = result.docs
218
+ .map((doc) => `## ${doc.name}\n\n${doc.markdown}`)
219
+ .join('\n\n---\n\n')
220
+
221
+ await mkdir(dirname(outputFile), { recursive: true })
222
+ await writeFile(outputFile, content, 'utf-8')
223
+
224
+ filesWritten++
225
+ totalDocs += result.docs.length
226
+ }
227
+
228
+ return { filesWritten, totalDocs }
229
+ }
230
+
231
+ // --- Realistic usage example ---
232
+ const mockResults: FileGenerationResult[] = [
233
+ {
234
+ sourceFile: '/project/src/utils/math.ts',
235
+ docs: [
236
+ {
237
+ name: 'add',
238
+ kind: 'function',
239
+ markdown: 'Adds two numbers together.\n\n**Params:** `a: number`, `b: number`\n\n**Returns:** `number`',
240
+ },
241
+ {
242
+ name: 'multiply',
243
+ kind: 'function',
244
+ markdown: 'Multiplies two numbers.\n\n**Params:** `a: number`, `b: number`\n\n**Returns:** `number`',
245
+ },
246
+ ],
247
+ },
248
+ {
249
+ sourceFile: '/project/src/api/client.ts',
250
+ docs: [
251
+ {
252
+ name: 'ApiClient',
253
+ kind: 'class',
254
+ markdown: 'HTTP client for interacting with the REST API.\n\n**Constructor:** `new ApiClient(baseUrl: string)`',
255
+ },
256
+ ],
257
+ },
258
+ {
259
+ // Simulates a file that failed to generate — should be skipped
260
+ sourceFile: '/project/src/broken/module.ts',
261
+ docs: [],
262
+ error: 'Parse error: unexpected token',
263
+ },
264
+ ]
265
+
266
+ async function main() {
267
+ const outputDir = process.env.DOCS_OUTPUT_DIR || './docs-output'
268
+ const sourceDir = '/project/src'
269
+
270
+ try {
271
+ const { filesWritten, totalDocs } = await writeDocsToDirectory(
272
+ mockResults,
273
+ outputDir,
274
+ sourceDir
275
+ )
276
+
277
+ console.log(`✅ Documentation written successfully`)
278
+ console.log(` Files written : ${filesWritten}`) // Files written : 2
279
+ console.log(` Total docs : ${totalDocs}`) // Total docs : 3
280
+ // Output files:
281
+ // ./docs-output/utils/math.md (2 docs: add, multiply)
282
+ // ./docs-output/api/client.md (1 doc: ApiClient)
283
+ // ./docs-output/broken/module.md ← skipped (error + no docs)
284
+ } catch (error) {
285
+ console.error('Failed to write docs:', error)
286
+ process.exit(1)
287
+ }
288
+ }
289
+
290
+ main()
291
+ ```
292
+
293
+ ### `groupDocsByFile`
294
+
295
+ ```typescript
296
+ function groupDocsByFile(docs: GeneratedDoc[]): FileGenerationResult[]
297
+ ```
298
+
299
+ Use this to organize a flat list of generated documentation objects into groups by their source file, making it easy to write one output file per source file.
300
+
301
+ This is the key step between generating individual doc entries and writing them to disk — it collapses a flat array into a file-keyed structure so you can iterate over files and write each one independently.
302
+
303
+ #### Parameters
304
+
305
+ | Name | Type | Required | Description |
306
+ |------|------|----------|-------------|
307
+ | `docs` | `GeneratedDoc[]` | ✅ | Flat array of generated documentation objects, each containing an `element` with a `filePath` property |
308
+
309
+ #### Returns
310
+
311
+ Returns `FileGenerationResult[]` — an array of objects where each entry represents one source file and contains all the `GeneratedDoc` entries that belong to it.
312
+
313
+ | Scenario | Result |
314
+ |----------|--------|
315
+ | Multiple docs from the same file | Grouped into a single `FileGenerationResult` |
316
+ | Docs from different files | Each file gets its own `FileGenerationResult` |
317
+ | Empty array passed | Returns an empty array `[]` |
318
+
319
+ **Example:**
320
+
321
+ ```typescript example.ts
322
+ // ── Inline types (do not import from autodocs) ──────────────────────────────
323
+
324
+ interface CodeElement {
325
+ name: string
326
+ filePath: string
327
+ kind: 'function' | 'class' | 'interface' | 'type'
328
+ signature: string
329
+ }
330
+
331
+ interface GeneratedDoc {
332
+ element: CodeElement
333
+ markdown: string
334
+ generatedAt: Date
335
+ }
336
+
337
+ interface FileGenerationResult {
338
+ filePath: string
339
+ docs: GeneratedDoc[]
340
+ }
341
+
342
+ // ── Inline implementation ────────────────────────────────────────────────────
343
+
344
+ function groupDocsByFile(docs: GeneratedDoc[]): FileGenerationResult[] {
345
+ const byFile = new Map<string, GeneratedDoc[]>()
346
+
347
+ for (const doc of docs) {
348
+ const file = doc.element.filePath
349
+ if (!byFile.has(file)) {
350
+ byFile.set(file, [])
351
+ }
352
+ byFile.get(file)!.push(doc)
353
+ }
354
+
355
+ return Array.from(byFile.entries()).map(([filePath, docs]) => ({
356
+ filePath,
357
+ docs,
358
+ }))
359
+ }
360
+
361
+ // ── Realistic usage example ──────────────────────────────────────────────────
362
+
363
+ const generatedDocs: GeneratedDoc[] = [
364
+ {
365
+ element: {
366
+ name: 'fetchUser',
367
+ filePath: 'src/api/users.ts',
368
+ kind: 'function',
369
+ signature: 'function fetchUser(id: string): Promise<User>',
370
+ },
371
+ markdown: '## fetchUser\n\nFetches a user by ID.',
372
+ generatedAt: new Date('2024-01-15T10:00:00Z'),
373
+ },
374
+ {
375
+ element: {
376
+ name: 'createUser',
377
+ filePath: 'src/api/users.ts', // same file as fetchUser
378
+ kind: 'function',
379
+ signature: 'function createUser(data: UserInput): Promise<User>',
380
+ },
381
+ markdown: '## createUser\n\nCreates a new user.',
382
+ generatedAt: new Date('2024-01-15T10:00:01Z'),
383
+ },
384
+ {
385
+ element: {
386
+ name: 'AuthService',
387
+ filePath: 'src/services/auth.ts', // different file
388
+ kind: 'class',
389
+ signature: 'class AuthService',
390
+ },
391
+ markdown: '## AuthService\n\nHandles authentication.',
392
+ generatedAt: new Date('2024-01-15T10:00:02Z'),
393
+ },
394
+ {
395
+ element: {
396
+ name: 'UserRole',
397
+ filePath: 'src/types/roles.ts', // another file
398
+ kind: 'type',
399
+ signature: "type UserRole = 'admin' | 'editor' | 'viewer'",
400
+ },
401
+ markdown: '## UserRole\n\nDefines available user roles.',
402
+ generatedAt: new Date('2024-01-15T10:00:03Z'),
403
+ },
404
+ ]
405
+
406
+ try {
407
+ const fileGroups = groupDocsByFile(generatedDocs)
408
+
409
+ console.log(`Grouped ${generatedDocs.length} docs into ${fileGroups.length} files:\n`)
410
+
411
+ for (const group of fileGroups) {
412
+ const names = group.docs.map(d => d.element.name).join(', ')
413
+ console.log(` 📄 ${group.filePath}`)
414
+ console.log(` ${group.docs.length} export(s): ${names}\n`)
415
+ }
416
+
417
+ // Expected output:
418
+ // Grouped 4 docs into 3 files:
419
+ //
420
+ // 📄 src/api/users.ts
421
+ // 2 export(s): fetchUser, createUser
422
+ //
423
+ // 📄 src/services/auth.ts
424
+ // 1 export(s): AuthService
425
+ //
426
+ // 📄 src/types/roles.ts
427
+ // 1 export(s): UserRole
428
+
429
+ // Show how you'd use this to write files
430
+ console.log('--- Ready to write output files ---')
431
+ for (const group of fileGroups) {
432
+ const combinedMarkdown = group.docs.map(d => d.markdown).join('\n\n---\n\n')
433
+ console.log(`Would write ${combinedMarkdown.length} chars to docs for: ${group.filePath}`)
434
+ }
435
+ } catch (error) {
436
+ console.error('Failed to group docs:', error)
437
+ }
438
+ ```
439
+
440
+ ### `writeDocsByTopic`
441
+
442
+ ```typescript
443
+ async function writeDocsByTopic(docs: GeneratedDoc[], outputDir: string): Promise<{ filesWritten: number; totalDocs: number; topics: Topic[] }>
444
+ ```
445
+
446
+ Use this to write generated documentation files to disk, organized into topic-based subdirectories — ideal for creating structured doc sites where related functions and classes are grouped together.
447
+
448
+ Instead of dumping all docs into a flat directory, `writeDocsByTopic` automatically clusters your `GeneratedDoc` entries by topic and writes each one into the appropriate subfolder, returning a summary of what was written.
449
+
450
+ #### Parameters
451
+
452
+ | Name | Type | Required | Description |
453
+ |------|------|----------|-------------|
454
+ | `docs` | `GeneratedDoc[]` | ✅ | Array of generated documentation objects to organize and write |
455
+ | `outputDir` | `string` | ✅ | Root directory where topic-organized subdirectories and files will be created |
456
+
457
+ #### Returns
458
+
459
+ Returns a `Promise` that resolves to an object with:
460
+
461
+ | Field | Type | Description |
462
+ |-------|------|-------------|
463
+ | `filesWritten` | `number` | Number of markdown files successfully written to disk |
464
+ | `totalDocs` | `number` | Total number of docs processed (may differ from `filesWritten` if some were skipped) |
465
+ | `topics` | `Topic[]` | Array of topic objects describing how docs were grouped, useful for generating a table of contents |
466
+
467
+ ## Notes
468
+
469
+ - Output directories are created automatically if they don't exist
470
+ - Each `Topic` in the returned array contains the topic name and the docs assigned to it
471
+ - Docs without an explicit topic may be grouped under a default/uncategorized topic
472
+ - Useful as a final step in a doc generation pipeline after parsing and formatting
473
+
474
+ **Example:**
475
+
476
+ ```typescript example.ts
477
+ import fs from 'fs/promises'
478
+ import path from 'path'
479
+ import os from 'os'
480
+
481
+ // ── Inline types (mirrors autodocs internals) ────────────────────────────────
482
+
483
+ type GeneratedDoc = {
484
+ elementName: string
485
+ topic: string
486
+ filePath: string
487
+ markdown: string
488
+ }
489
+
490
+ type Topic = {
491
+ name: string
492
+ slug: string
493
+ docs: GeneratedDoc[]
494
+ }
495
+
496
+ // ── Inline implementation (mirrors autodocs behaviour) ───────────────────────
497
+
498
+ function organizeByTopic(docs: GeneratedDoc[]): Topic[] {
499
+ const map = new Map<string, GeneratedDoc[]>()
500
+ for (const doc of docs) {
501
+ const key = doc.topic || 'general'
502
+ if (!map.has(key)) map.set(key, [])
503
+ map.get(key)!.push(doc)
504
+ }
505
+ return Array.from(map.entries()).map(([name, topicDocs]) => ({
506
+ name,
507
+ slug: name.toLowerCase().replace(/\s+/g, '-'),
508
+ docs: topicDocs,
509
+ }))
510
+ }
511
+
512
+ async function writeDocsByTopic(
513
+ docs: GeneratedDoc[],
514
+ outputDir: string
515
+ ): Promise<{ filesWritten: number; totalDocs: number; topics: Topic[] }> {
516
+ let filesWritten = 0
517
+ const topics = organizeByTopic(docs)
518
+
519
+ for (const topic of topics) {
520
+ const topicDir = path.join(outputDir, topic.slug)
521
+ await fs.mkdir(topicDir, { recursive: true })
522
+
523
+ for (const doc of topic.docs) {
524
+ const fileName = `${doc.elementName.toLowerCase().replace(/\s+/g, '-')}.md`
525
+ const filePath = path.join(topicDir, fileName)
526
+ await fs.writeFile(filePath, doc.markdown, 'utf8')
527
+ filesWritten++
528
+ }
529
+ }
530
+
531
+ return { filesWritten, totalDocs: docs.length, topics }
532
+ }
533
+
534
+ // ── Example usage ─────────────────────────────────────────────────────────────
535
+
536
+ const sampleDocs: GeneratedDoc[] = [
537
+ {
538
+ elementName: 'fetchUser',
539
+ topic: 'API',
540
+ filePath: 'src/api/fetchUser.ts',
541
+ markdown: '# fetchUser\n\nFetches a user by ID from the REST API.\n',
542
+ },
543
+ {
544
+ elementName: 'createUser',
545
+ topic: 'API',
546
+ filePath: 'src/api/createUser.ts',
547
+ markdown: '# createUser\n\nCreates a new user record.\n',
548
+ },
549
+ {
550
+ elementName: 'hashPassword',
551
+ topic: 'Auth',
552
+ filePath: 'src/auth/hashPassword.ts',
553
+ markdown: '# hashPassword\n\nHashes a plaintext password using bcrypt.\n',
554
+ },
555
+ {
556
+ elementName: 'formatDate',
557
+ topic: 'Utils',
558
+ filePath: 'src/utils/formatDate.ts',
559
+ markdown: '# formatDate\n\nFormats a Date object into a locale string.\n',
560
+ },
561
+ ]
562
+
563
+ async function main() {
564
+ // Write to a temp directory so the example is safe to run anywhere
565
+ const outputDir = path.join(os.tmpdir(), 'autodocs-example-output')
566
+
567
+ try {
568
+ console.log(`Writing docs to: ${outputDir}`)
569
+
570
+ const result = await writeDocsByTopic(sampleDocs, outputDir)
571
+
572
+ console.log('\n✅ Done!')
573
+ console.log(` Files written : ${result.filesWritten}`)
574
+ console.log(` Total docs : ${result.totalDocs}`)
575
+ console.log(` Topics found : ${result.topics.map(t => t.name).join(', ')}`)
576
+
577
+ // Show the directory tree that was created
578
+ console.log('\n📁 Output structure:')
579
+ for (const topic of result.topics) {
580
+ console.log(` ${outputDir}/${topic.slug}/`)
581
+ for (const doc of topic.docs) {
582
+ console.log(` └─ ${doc.elementName.toLowerCase()}.md`)
583
+ }
584
+ }
585
+
586
+ // Expected output:
587
+ // ✅ Done!
588
+ // Files written : 4
589
+ // Total docs : 4
590
+ // Topics found : API, Auth, Utils
591
+ //
592
+ // 📁 Output structure:
593
+ // /tmp/autodocs-example-output/api/
594
+ // └─ fetchuser.md
595
+ // └─ createuser.md
596
+ // /tmp/autodocs-example-output/auth/
597
+ // └─ hashpassword.md
598
+ // /tmp/autodocs-example-output/utils/
599
+ // └─ formatdate.md
600
+
601
+ } catch (error) {
602
+ if (error instanceof Error) {
603
+ console.error('Failed to write docs:', error.message)
604
+ } else {
605
+ console.error('Unexpected error:', error)
606
+ }
607
+ process.exit(1)
608
+ }
609
+ }
610
+
611
+ main()
612
+ ```
613
+