@aigne/doc-smith 0.6.0 → 0.7.1

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 (80) hide show
  1. package/.aigne/doc-smith/config.yaml +70 -0
  2. package/.aigne/doc-smith/output/structure-plan.json +152 -0
  3. package/.aigne/doc-smith/preferences.yml +31 -0
  4. package/.aigne/doc-smith/upload-cache.yaml +288 -0
  5. package/.github/workflows/ci.yml +46 -0
  6. package/.github/workflows/reviewer.yml +2 -1
  7. package/CHANGELOG.md +17 -0
  8. package/README.md +33 -15
  9. package/agents/chat.yaml +30 -0
  10. package/agents/check-structure-plan.mjs +1 -1
  11. package/agents/docs-fs.yaml +25 -0
  12. package/agents/exit.mjs +6 -0
  13. package/agents/feedback-refiner.yaml +5 -1
  14. package/agents/find-items-by-paths.mjs +10 -4
  15. package/agents/fs.mjs +60 -0
  16. package/agents/input-generator.mjs +150 -91
  17. package/agents/load-config.mjs +0 -5
  18. package/agents/load-sources.mjs +61 -8
  19. package/agents/publish-docs.mjs +27 -12
  20. package/agents/retranslate.yaml +1 -1
  21. package/agents/team-publish-docs.yaml +2 -2
  22. package/aigne.yaml +1 -0
  23. package/docs/_sidebar.md +17 -0
  24. package/docs/advanced-how-it-works.md +104 -0
  25. package/docs/advanced-how-it-works.zh.md +104 -0
  26. package/docs/advanced-quality-assurance.md +64 -0
  27. package/docs/advanced-quality-assurance.zh.md +64 -0
  28. package/docs/advanced.md +28 -0
  29. package/docs/advanced.zh.md +28 -0
  30. package/docs/changelog.md +272 -0
  31. package/docs/changelog.zh.md +272 -0
  32. package/docs/cli-reference.md +185 -0
  33. package/docs/cli-reference.zh.md +185 -0
  34. package/docs/configuration-interactive-setup.md +82 -0
  35. package/docs/configuration-interactive-setup.zh.md +82 -0
  36. package/docs/configuration-language-support.md +64 -0
  37. package/docs/configuration-language-support.zh.md +64 -0
  38. package/docs/configuration-llm-setup.md +90 -0
  39. package/docs/configuration-llm-setup.zh.md +90 -0
  40. package/docs/configuration-preferences.md +122 -0
  41. package/docs/configuration-preferences.zh.md +123 -0
  42. package/docs/configuration.md +173 -0
  43. package/docs/configuration.zh.md +173 -0
  44. package/docs/features-generate-documentation.md +82 -0
  45. package/docs/features-generate-documentation.zh.md +82 -0
  46. package/docs/features-publish-your-docs.md +98 -0
  47. package/docs/features-publish-your-docs.zh.md +98 -0
  48. package/docs/features-translate-documentation.md +83 -0
  49. package/docs/features-translate-documentation.zh.md +83 -0
  50. package/docs/features-update-and-refine.md +86 -0
  51. package/docs/features-update-and-refine.zh.md +86 -0
  52. package/docs/features.md +56 -0
  53. package/docs/features.zh.md +56 -0
  54. package/docs/getting-started.md +74 -0
  55. package/docs/getting-started.zh.md +74 -0
  56. package/docs/overview.md +48 -0
  57. package/docs/overview.zh.md +48 -0
  58. package/media.md +19 -0
  59. package/package.json +13 -10
  60. package/prompts/content-detail-generator.md +7 -3
  61. package/prompts/document/custom-components.md +80 -0
  62. package/prompts/document/d2-chart/diy-examples.md +44 -0
  63. package/prompts/document/d2-chart/official-examples.md +708 -0
  64. package/prompts/document/d2-chart/rules.md +48 -0
  65. package/prompts/document/detail-generator.md +12 -15
  66. package/prompts/document/structure-planning.md +1 -3
  67. package/prompts/feedback-refiner.md +81 -60
  68. package/prompts/structure-planning.md +20 -3
  69. package/tests/check-detail-result.test.mjs +3 -4
  70. package/tests/conflict-resolution.test.mjs +237 -0
  71. package/tests/input-generator.test.mjs +940 -0
  72. package/tests/load-sources.test.mjs +627 -3
  73. package/tests/preferences-utils.test.mjs +94 -0
  74. package/tests/save-value-to-config.test.mjs +182 -5
  75. package/tests/utils.test.mjs +49 -0
  76. package/utils/conflict-detector.mjs +72 -1
  77. package/utils/constants.mjs +125 -124
  78. package/utils/kroki-utils.mjs +162 -0
  79. package/utils/markdown-checker.mjs +98 -70
  80. package/utils/utils.mjs +96 -28
package/README.md CHANGED
@@ -103,7 +103,7 @@ Optimize specific documents with targeted feedback:
103
103
  aigne doc update
104
104
 
105
105
  # Update a specific document
106
- aigne doc update --doc-path /faq --feedback "Add more comprehensive FAQ entries"
106
+ aigne doc update --docs overview.md --feedback "Add more comprehensive FAQ entries"
107
107
  ```
108
108
 
109
109
  **Interactive Mode:** When run without parameters, `aigne doc update` will present an interactive menu for you to select which document to regenerate and provide feedback.
@@ -122,6 +122,29 @@ aigne doc generate --feedback "Add more detailed installation guide and troubles
122
122
 
123
123
  **Structure Optimization:** Use `aigne doc generate` with `--feedback` to refine the overall documentation structure, add new sections, or reorganize existing content.
124
124
 
125
+ #### Document Translation
126
+
127
+ Translate existing documentation to multiple languages:
128
+
129
+ ```bash
130
+ # Translate specific documents to multiple languages
131
+ aigne doc translate --langs zh --langs ja --docs examples.md --docs overview.md
132
+
133
+ # Interactive translation with document and language selection
134
+ aigne doc translate
135
+ ```
136
+
137
+ **Command Parameters:**
138
+ - `--langs`: Specify target languages (can be used multiple times)
139
+ - `--docs`: Specify document paths to translate (can be used multiple times)
140
+ - `--feedback`: Provide feedback for translation improvement
141
+ - `--glossary`: Use a glossary file for consistent terminology (@path/to/glossary.md)
142
+
143
+ **Interactive Mode:** When run without parameters, `aigne doc translate` will present interactive menus to:
144
+ - Select documents to translate from your documentation
145
+ - Choose target languages from 12 supported languages
146
+ - Add new translation languages to your configuration
147
+
125
148
  #### Publishing to Discuss Kit
126
149
 
127
150
  Publish your documentation to Discuss Kit platforms:
@@ -185,6 +208,15 @@ aigne doc generate --feedback "Remove About section and add API Reference"
185
208
 
186
209
  # Update specific document
187
210
  aigne doc update --doc-path /faq --feedback "Add more comprehensive FAQ entries"
211
+
212
+ # Translate documents to multiple languages
213
+ aigne doc translate --langs zh --langs ja --docs examples.md --docs overview.md
214
+
215
+ # Interactive translation (select documents and languages)
216
+ aigne doc translate
217
+
218
+ # Translate with custom glossary and feedback
219
+ aigne doc translate --glossary @glossary.md --feedback "Use technical terminology consistently"
188
220
  ```
189
221
 
190
222
  ### Publishing and Integration
@@ -198,17 +230,3 @@ aigne doc publish --appUrl https://your-discuss-kit-instance.com
198
230
 
199
231
 
200
232
  ```
201
-
202
- ### Development Commands
203
-
204
- ```shell
205
- # Development and debugging commands using npx to run local code
206
- npx --no doc-smith run --entry-agent init
207
- npx --no doc-smith run --entry-agent generate
208
- npx --no doc-smith run --entry-agent update
209
- npx --no doc-smith run --entry-agent translate
210
- npx --no doc-smith run --entry-agent publish
211
- ```
212
-
213
- **Development Mode:** These commands use `npx` to run the local code version for development and debugging purposes, bypassing the globally installed CLI.
214
-
@@ -0,0 +1,30 @@
1
+ type: ai
2
+ name: chat
3
+ description: Start interactive document generation assistant
4
+ instructions: |
5
+ You are a professional document generation assistant that helps users create, modify, and manage documentation through interactive chat. Your primary role is to understand user requirements and intelligently call upon various specialized skills to complete documentation tasks efficiently.
6
+
7
+ Core Capabilities:
8
+ - Generate comprehensive documentation from user inputs and specifications
9
+ - Regenerate and refine document details based on feedback
10
+ - Translate and localize documentation content
11
+ - Publish and manage team documentation workflows
12
+ - Provide interactive guidance throughout the document creation process
13
+
14
+ Interaction Guidelines:
15
+ - Engage users in a professional yet friendly manner
16
+ - Ask clarifying questions to understand specific documentation needs
17
+ - Suggest appropriate skills and workflows based on user requests
18
+ - Provide clear explanations of available capabilities and processes
19
+ - Maintain context throughout multi-step documentation tasks
20
+ - Offer proactive suggestions for improving document quality and structure
21
+ input_key: message
22
+ memory: true
23
+ skills:
24
+ - ./input-generator.mjs
25
+ - ./docs-generator.yaml
26
+ - ./detail-regenerator.yaml
27
+ - ./team-publish-docs.yaml
28
+ - ./retranslate.yaml
29
+ - ./docs-fs.yaml
30
+ - ./exit.mjs
@@ -63,7 +63,7 @@ export default async function checkStructurePlan(
63
63
  1. 对于新增的内容,可以根据需要新增节点,或补充到原有节点展示
64
64
  2. 谨慎删除节点,除非节点关联 sourceIds 都被删除了
65
65
  3. 不能修改原有节点的 path
66
- 4. 根据最新的 Data Sources 按需要更新节点的 sourceIds,如没有大的变化,可以不更新。
66
+ 4. 根据最新的 Data Sources 可以按需要更新节点的 sourceIds
67
67
  `;
68
68
  }
69
69
  }
@@ -0,0 +1,25 @@
1
+ type: team
2
+ name: docs_fs_actor
3
+ description: File system operations for documentation management
4
+ skills:
5
+ - ./load-config.mjs
6
+ - url: ./fs.mjs
7
+ default_input:
8
+ rootDir:
9
+ $get: docsDir
10
+ input_schema:
11
+ type: object
12
+ properties:
13
+ action:
14
+ type: "string"
15
+ enum: ["read_file", "write_file", "delete_file", "list_directory"]
16
+ description:
17
+ "The file system action to perform, available actions are: read_file, write_file, delete_file, list_directory"
18
+ path:
19
+ type: "string"
20
+ description: "The path to the file or directory to operate on"
21
+ content:
22
+ type: "string"
23
+ description: "The content to write to the file, required for write_file action"
24
+ required: ["action", "path"]
25
+ task_render_mode: hide
@@ -0,0 +1,6 @@
1
+ export default async function exit() {
2
+ process.exit(0);
3
+ }
4
+
5
+ exit.description =
6
+ "Exit the chat session. Equivalent to saying goodbye, quit, exit, close, or see you. Must print a bye message before calling this tool, as it will exit the process immediately.";
@@ -41,8 +41,12 @@ output_schema:
41
41
  limitToInputPaths:
42
42
  type: boolean
43
43
  description: Whether to limit to the "paths specified in current input" when used subsequently
44
+ reason:
45
+ type: string
46
+ description: Explanation of why the save decision was made and how the rule and scope were derived
44
47
  required:
45
48
  - rule
46
49
  - scope
47
50
  - save
48
- - limitToInputPaths
51
+ - limitToInputPaths
52
+ - reason
@@ -26,10 +26,16 @@ export default async function selectedDocs(
26
26
  // Let user select multiple files
27
27
  selectedFiles = await options.prompts.checkbox({
28
28
  message: getActionText(isTranslate, "Select documents to {action}:"),
29
- choices: mainLanguageFiles.map((file) => ({
30
- name: file,
31
- value: file,
32
- })),
29
+ source: (term) => {
30
+ const choices = mainLanguageFiles.map((file) => ({
31
+ name: file,
32
+ value: file,
33
+ }));
34
+
35
+ if (!term) return choices;
36
+
37
+ return choices.filter((choice) => choice.name.toLowerCase().includes(term.toLowerCase()));
38
+ },
33
39
  validate: (answer) => {
34
40
  if (answer.length === 0) {
35
41
  return "Please select at least one document";
package/agents/fs.mjs ADDED
@@ -0,0 +1,60 @@
1
+ import { mkdir, readdir, readFile, rm, writeFile } from "node:fs/promises";
2
+ import { dirname, join } from "node:path";
3
+
4
+ export default async function fs({ rootDir, action, path, content }) {
5
+ if (!rootDir) throw new Error("Root directory is not specified");
6
+
7
+ path = join(rootDir, path);
8
+
9
+ switch (action) {
10
+ case "read_file":
11
+ return {
12
+ status: "ok",
13
+ path,
14
+ content: await readFile(path, "utf-8"),
15
+ };
16
+ case "write_file": {
17
+ await mkdir(dirname(path), { recursive: true });
18
+ await writeFile(path, content || "");
19
+ return {
20
+ status: "ok",
21
+ path,
22
+ content,
23
+ };
24
+ }
25
+ case "delete_file":
26
+ await rm(path, { recursive: true, force: true });
27
+ return {
28
+ status: "ok",
29
+ path,
30
+ };
31
+ case "list_directory":
32
+ return {
33
+ status: "ok",
34
+ entries: await readdir(path, { withFileTypes: true }).then((list) =>
35
+ list.map((entry) => ({
36
+ path: join(entry.parentPath, entry.name),
37
+ isDirectory: entry.isDirectory(),
38
+ })),
39
+ ),
40
+ };
41
+ }
42
+ }
43
+
44
+ fs.input_schema = {
45
+ type: "object",
46
+ properties: {
47
+ action: {
48
+ type: "string",
49
+ enum: ["read_file", "write_file", "delete_file", "list_directory"],
50
+ description:
51
+ "The file system action to perform, available actions are: read_file, write_file, delete_file, list_directory",
52
+ },
53
+ path: { type: "string", description: "The path to the file or directory to operate on" },
54
+ content: {
55
+ type: "string",
56
+ description: "The content to write to the file, required for write_file action",
57
+ },
58
+ },
59
+ required: ["action", "path"],
60
+ };
@@ -1,6 +1,7 @@
1
1
  import { mkdir, readFile, writeFile } from "node:fs/promises";
2
2
  import { dirname, join } from "node:path";
3
3
  import chalk from "chalk";
4
+ import { stringify as yamlStringify } from "yaml";
4
5
  import { getFilteredOptions, validateSelection } from "../utils/conflict-detector.mjs";
5
6
  import {
6
7
  DEPTH_RECOMMENDATION_LOGIC,
@@ -15,6 +16,7 @@ import {
15
16
  detectSystemLanguage,
16
17
  getAvailablePaths,
17
18
  getProjectInfo,
19
+ isGlobPattern,
18
20
  validatePath,
19
21
  } from "../utils/utils.mjs";
20
22
 
@@ -34,7 +36,9 @@ export default async function init(
34
36
  ) {
35
37
  if (skipIfExists) {
36
38
  const filePath = join(outputPath, fileName);
37
- if (await readFile(filePath, "utf8").catch(() => null)) {
39
+ const configContent = await readFile(filePath, "utf8").catch(() => null);
40
+ // Only skip if file exists AND has non-empty content
41
+ if (configContent && configContent.trim() !== "") {
38
42
  return {};
39
43
  }
40
44
  }
@@ -228,19 +232,20 @@ export default async function init(
228
232
  // 8. Source code paths
229
233
  console.log("\n🔍 [8/8]: Source Code Paths");
230
234
  console.log("Enter paths to analyze for documentation (e.g., ./src, ./lib)");
235
+ console.log("💡 You can also enter glob patterns (e.g., src/**/*.js, **/*.md)");
231
236
  console.log("💡 If no paths are configured, './' will be used as default");
232
237
 
233
238
  const sourcePaths = [];
234
239
  while (true) {
235
240
  const selectedPath = await options.prompts.search({
236
- message: "Path:",
241
+ message: "Path or glob pattern:",
237
242
  source: async (input) => {
238
243
  if (!input || input.trim() === "") {
239
244
  return [
240
245
  {
241
- name: "Press Enter to finish",
246
+ name: "",
242
247
  value: "",
243
- description: "",
248
+ description: _PRESS_ENTER_TO_FINISH,
244
249
  },
245
250
  ];
246
251
  }
@@ -250,32 +255,58 @@ export default async function init(
250
255
  // Search for matching files and folders in current directory
251
256
  const availablePaths = getAvailablePaths(searchTerm);
252
257
 
253
- return [...availablePaths];
258
+ // Also add option to use as glob pattern
259
+ const options = [...availablePaths];
260
+
261
+ // Check if input looks like a glob pattern
262
+ const isGlobPatternResult = isGlobPattern(searchTerm);
263
+ if (isGlobPatternResult) {
264
+ // If it looks like a glob pattern, allow direct input
265
+ options.push({
266
+ name: searchTerm,
267
+ value: searchTerm,
268
+ description: "This input will be used as a glob pattern for file matching",
269
+ });
270
+ }
271
+
272
+ return options;
254
273
  },
255
274
  });
256
275
 
257
276
  // Check if user chose to exit
258
- if (!selectedPath || selectedPath.trim() === "" || selectedPath === "Press Enter to finish") {
277
+ if (!selectedPath || selectedPath.trim() === "" || selectedPath === _PRESS_ENTER_TO_FINISH) {
259
278
  break;
260
279
  }
261
280
 
262
281
  const trimmedPath = selectedPath.trim();
263
282
 
264
- // Use validatePath to check if path is valid
265
- const validation = validatePath(trimmedPath);
283
+ // Check if it's a glob pattern
284
+ const isGlobPatternResult = isGlobPattern(trimmedPath);
266
285
 
267
- if (!validation.isValid) {
268
- console.log(`⚠️ ${validation.error}`);
269
- continue;
270
- }
286
+ if (isGlobPatternResult) {
287
+ // For glob patterns, just add them without validation
288
+ if (sourcePaths.includes(trimmedPath)) {
289
+ console.log(`⚠️ Pattern already exists: ${trimmedPath}`);
290
+ continue;
291
+ }
292
+ sourcePaths.push(trimmedPath);
293
+ } else {
294
+ // Use validatePath to check if path is valid for regular paths
295
+ const validation = validatePath(trimmedPath);
296
+
297
+ if (!validation.isValid) {
298
+ console.log(`⚠️ ${validation.error}`);
299
+ continue;
300
+ }
271
301
 
272
- // Avoid duplicate paths
273
- if (sourcePaths.includes(trimmedPath)) {
274
- console.log(`⚠️ Path already exists: ${trimmedPath}`);
275
- continue;
276
- }
302
+ // Avoid duplicate paths
303
+ if (sourcePaths.includes(trimmedPath)) {
304
+ console.log(`⚠️ Path already exists: ${trimmedPath}`);
305
+ continue;
306
+ }
277
307
 
278
- sourcePaths.push(trimmedPath);
308
+ sourcePaths.push(trimmedPath);
309
+ }
279
310
  }
280
311
 
281
312
  // If no paths entered, use default
@@ -324,114 +355,142 @@ export default async function init(
324
355
  * @param {Object} input - Input object
325
356
  * @returns {string} YAML string
326
357
  */
327
- function generateYAML(input) {
328
- let yaml = "";
358
+ export function generateYAML(input) {
359
+ // Create the main configuration object that will be safely serialized
360
+ const config = {
361
+ // Project information (safely handled by yaml library)
362
+ projectName: input.projectName || "",
363
+ projectDesc: input.projectDesc || "",
364
+ projectLogo: input.projectLogo || "",
365
+
366
+ // Documentation configuration
367
+ documentPurpose: input.documentPurpose || [],
368
+ targetAudienceTypes: input.targetAudienceTypes || [],
369
+ readerKnowledgeLevel: input.readerKnowledgeLevel || "",
370
+ documentationDepth: input.documentationDepth || "",
371
+
372
+ // Custom rules and target audience (empty for user to fill)
373
+ rules: "",
374
+ targetAudience: "",
375
+
376
+ // Language settings
377
+ locale: input.locale || "en",
378
+ translateLanguages: input.translateLanguages?.filter((lang) => lang.trim()) || [],
379
+
380
+ // Paths
381
+ docsDir: input.docsDir || "./aigne/doc-smith/docs",
382
+ sourcesPath: input.sourcesPath || [],
383
+ };
329
384
 
330
- // Add project information at the beginning
331
- yaml += `# Project information for documentation publishing\n`;
332
- yaml += `projectName: ${input.projectName || ""}\n`;
333
- yaml += `projectDesc: ${input.projectDesc || ""}\n`;
334
- yaml += `projectLogo: ${input.projectLogo || ""}\n`;
335
- yaml += `\n`;
385
+ // Generate comments and structure
386
+ let yaml = "# Project information for documentation publishing\n";
336
387
 
337
- // Add documentation configuration choices with all available options
338
- yaml += `# =============================================================================\n`;
339
- yaml += `# Documentation Configuration\n`;
340
- yaml += `# =============================================================================\n\n`;
388
+ // Serialize the project info section safely
389
+ const projectSection = yamlStringify({
390
+ projectName: config.projectName,
391
+ projectDesc: config.projectDesc,
392
+ projectLogo: config.projectLogo,
393
+ }).trim();
394
+
395
+ yaml += `${projectSection}\n\n`;
396
+
397
+ // Add documentation configuration with comments
398
+ yaml += "# =============================================================================\n";
399
+ yaml += "# Documentation Configuration\n";
400
+ yaml += "# =============================================================================\n\n";
341
401
 
342
402
  // Document Purpose with all available options
343
- yaml += `# Purpose: What's the main outcome you want readers to achieve?\n`;
344
- yaml += `# Available options (uncomment and modify as needed):\n`;
403
+ yaml += "# Purpose: What's the main outcome you want readers to achieve?\n";
404
+ yaml += "# Available options (uncomment and modify as needed):\n";
345
405
  Object.entries(DOCUMENT_STYLES).forEach(([key, style]) => {
346
406
  if (key !== "custom") {
347
407
  yaml += `# ${key.padEnd(16)} - ${style.name}: ${style.description}\n`;
348
408
  }
349
409
  });
350
- yaml += `documentPurpose:\n`;
351
- if (input.documentPurpose && input.documentPurpose.length > 0) {
352
- input.documentPurpose.forEach((purpose) => {
353
- yaml += ` - ${purpose}\n`;
354
- });
355
- }
356
- yaml += `\n`;
410
+
411
+ // Safely serialize documentPurpose
412
+ const documentPurposeSection = yamlStringify({ documentPurpose: config.documentPurpose }).trim();
413
+ yaml += `${documentPurposeSection.replace(/^documentPurpose:/, "documentPurpose:")}\n\n`;
357
414
 
358
415
  // Target Audience Types with all available options
359
- yaml += `# Target Audience: Who will be reading this most often?\n`;
360
- yaml += `# Available options (uncomment and modify as needed):\n`;
416
+ yaml += "# Target Audience: Who will be reading this most often?\n";
417
+ yaml += "# Available options (uncomment and modify as needed):\n";
361
418
  Object.entries(TARGET_AUDIENCES).forEach(([key, audience]) => {
362
419
  if (key !== "custom") {
363
420
  yaml += `# ${key.padEnd(16)} - ${audience.name}: ${audience.description}\n`;
364
421
  }
365
422
  });
366
- yaml += `targetAudienceTypes:\n`;
367
- if (input.targetAudienceTypes && input.targetAudienceTypes.length > 0) {
368
- input.targetAudienceTypes.forEach((audience) => {
369
- yaml += ` - ${audience}\n`;
370
- });
371
- }
372
- yaml += `\n`;
423
+
424
+ // Safely serialize targetAudienceTypes
425
+ const targetAudienceTypesSection = yamlStringify({
426
+ targetAudienceTypes: config.targetAudienceTypes,
427
+ }).trim();
428
+ yaml += `${targetAudienceTypesSection.replace(/^targetAudienceTypes:/, "targetAudienceTypes:")}\n\n`;
373
429
 
374
430
  // Reader Knowledge Level with all available options
375
- yaml += `# Reader Knowledge Level: What do readers typically know when they arrive?\n`;
376
- yaml += `# Available options (uncomment and modify as needed):\n`;
431
+ yaml += "# Reader Knowledge Level: What do readers typically know when they arrive?\n";
432
+ yaml += "# Available options (uncomment and modify as needed):\n";
377
433
  Object.entries(READER_KNOWLEDGE_LEVELS).forEach(([key, level]) => {
378
434
  yaml += `# ${key.padEnd(20)} - ${level.name}: ${level.description}\n`;
379
435
  });
380
- yaml += `readerKnowledgeLevel: ${input.readerKnowledgeLevel || ""}\n`;
381
- yaml += `\n`;
436
+
437
+ // Safely serialize readerKnowledgeLevel
438
+ const readerKnowledgeLevelSection = yamlStringify({
439
+ readerKnowledgeLevel: config.readerKnowledgeLevel,
440
+ }).trim();
441
+ yaml += `${readerKnowledgeLevelSection.replace(/^readerKnowledgeLevel:/, "readerKnowledgeLevel:")}\n\n`;
382
442
 
383
443
  // Documentation Depth with all available options
384
- yaml += `# Documentation Depth: How comprehensive should the documentation be?\n`;
385
- yaml += `# Available options (uncomment and modify as needed):\n`;
444
+ yaml += "# Documentation Depth: How comprehensive should the documentation be?\n";
445
+ yaml += "# Available options (uncomment and modify as needed):\n";
386
446
  Object.entries(DOCUMENTATION_DEPTH).forEach(([key, depth]) => {
387
447
  yaml += `# ${key.padEnd(18)} - ${depth.name}: ${depth.description}\n`;
388
448
  });
389
- yaml += `documentationDepth: ${input.documentationDepth || ""}\n`;
390
- yaml += `\n`;
449
+
450
+ // Safely serialize documentationDepth
451
+ const documentationDepthSection = yamlStringify({
452
+ documentationDepth: config.documentationDepth,
453
+ }).trim();
454
+ yaml += `${documentationDepthSection.replace(/^documentationDepth:/, "documentationDepth:")}\n\n`;
391
455
 
392
456
  // Custom Documentation Rules and Requirements
393
- yaml += `# Custom Rules: Define specific documentation generation rules and requirements\n`;
394
- yaml += `rules: |\n`;
395
- yaml += ` \n\n`;
457
+ yaml += "# Custom Rules: Define specific documentation generation rules and requirements\n";
458
+ const rulesSection = yamlStringify({ rules: config.rules }).trim();
459
+ // Use literal style for multiline strings
460
+ yaml += `${rulesSection.replace(/rules: ''/, "rules: |\n ")}\n\n`;
396
461
 
397
462
  // Target Audience Description
398
- yaml += `# Target Audience: Describe your specific target audience and their characteristics\n`;
399
- yaml += `targetAudience: |\n`;
400
- yaml += ` \n\n`;
463
+ yaml += "# Target Audience: Describe your specific target audience and their characteristics\n";
464
+ const targetAudienceSection = yamlStringify({ targetAudience: config.targetAudience }).trim();
465
+ // Use literal style for multiline strings
466
+ yaml += `${targetAudienceSection.replace(/targetAudience: ''/, "targetAudience: |\n ")}\n\n`;
401
467
 
402
468
  // Glossary Configuration
403
- yaml += `# Glossary: Define project-specific terms and definitions\n`;
404
- yaml += `# glossary: "@glossary.md" # Path to markdown file containing glossary definitions\n`;
405
- yaml += `\n`;
406
-
407
- // Add language settings
408
- yaml += `locale: ${input.locale}\n`;
409
-
410
- // Add translation languages
411
- if (
412
- input.translateLanguages &&
413
- input.translateLanguages.length > 0 &&
414
- input.translateLanguages.some((lang) => lang.trim())
415
- ) {
416
- yaml += `translateLanguages:\n`;
417
- input.translateLanguages.forEach((lang) => {
418
- if (lang.trim()) {
419
- yaml += ` - ${lang}\n`;
420
- }
421
- });
469
+ yaml += "# Glossary: Define project-specific terms and definitions\n";
470
+ yaml += '# glossary: "@glossary.md" # Path to markdown file containing glossary definitions\n\n';
471
+
472
+ // Language settings - safely serialize
473
+ const localeSection = yamlStringify({ locale: config.locale }).trim();
474
+ yaml += `${localeSection.replace(/^locale:/, "locale:")}\n`;
475
+
476
+ // Translation languages
477
+ if (config.translateLanguages.length > 0) {
478
+ const translateLanguagesSection = yamlStringify({
479
+ translateLanguages: config.translateLanguages,
480
+ }).trim();
481
+ yaml += `${translateLanguagesSection.replace(/^translateLanguages:/, "translateLanguages:")}\n`;
422
482
  } else {
423
- yaml += `# translateLanguages: # List of languages to translate the documentation to\n`;
424
- yaml += `# - zh # Example: Chinese translation\n`;
425
- yaml += `# - en # Example: English translation\n`;
483
+ yaml += "# translateLanguages: # List of languages to translate the documentation to\n";
484
+ yaml += "# - zh # Example: Chinese translation\n";
485
+ yaml += "# - en # Example: English translation\n";
426
486
  }
427
487
 
428
- // Add directory and source path configurations
429
- yaml += `docsDir: ${input.docsDir} # Directory to save generated documentation\n`;
430
- // yaml += `outputDir: ${outputPath}/output # Directory to save output files\n`;
431
- yaml += `sourcesPath: # Source code paths to analyze\n`;
432
- input.sourcesPath.forEach((path) => {
433
- yaml += ` - ${path}\n`;
434
- });
488
+ // Directory and source path configurations - safely serialize
489
+ const docsDirSection = yamlStringify({ docsDir: config.docsDir }).trim();
490
+ yaml += `${docsDirSection.replace(/^docsDir:/, "docsDir:")} # Directory to save generated documentation\n`;
491
+
492
+ const sourcesPathSection = yamlStringify({ sourcesPath: config.sourcesPath }).trim();
493
+ yaml += `${sourcesPathSection.replace(/^sourcesPath:/, "sourcesPath: # Source code paths to analyze")}\n`;
435
494
 
436
495
  return yaml;
437
496
  }
@@ -31,11 +31,6 @@ export default async function loadConfig({ config, appUrl }) {
31
31
  const processedConfig = processConfigFields(parsedConfig);
32
32
 
33
33
  return {
34
- nodeName: "Section",
35
- locale: "en",
36
- sourcesPath: ["./"],
37
- docsDir: "./.aigne/doc-smith/docs",
38
- outputDir: "./.aigne/doc-smith/output",
39
34
  lastGitHead: parsedConfig.lastGitHead || "",
40
35
  ...parsedConfig,
41
36
  ...processedConfig,