@aigne/doc-smith 0.2.5 → 0.2.8

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 (41) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +1 -0
  3. package/agents/check-detail-result.mjs +13 -139
  4. package/agents/check-detail.mjs +4 -6
  5. package/agents/check-structure-plan.mjs +56 -12
  6. package/agents/detail-generator-and-translate.yaml +7 -1
  7. package/agents/detail-regenerator.yaml +3 -1
  8. package/agents/docs-generator.yaml +2 -1
  9. package/agents/find-item-by-path.mjs +64 -15
  10. package/agents/input-generator.mjs +31 -11
  11. package/agents/language-selector.mjs +89 -0
  12. package/agents/load-config.mjs +2 -2
  13. package/agents/load-sources.mjs +13 -40
  14. package/agents/publish-docs.mjs +47 -161
  15. package/agents/retranslate.yaml +74 -0
  16. package/agents/save-docs.mjs +19 -21
  17. package/agents/save-output.mjs +2 -9
  18. package/agents/save-single-doc.mjs +20 -1
  19. package/agents/schema/structure-plan.yaml +1 -1
  20. package/agents/structure-planning.yaml +6 -0
  21. package/agents/transform-detail-datasources.mjs +2 -5
  22. package/agents/translate.yaml +3 -0
  23. package/aigne.yaml +5 -1
  24. package/biome.json +13 -3
  25. package/docs-mcp/get-docs-structure.mjs +1 -1
  26. package/docs-mcp/read-doc-content.mjs +1 -4
  27. package/package.json +20 -7
  28. package/prompts/check-structure-planning-result.md +4 -7
  29. package/prompts/content-detail-generator.md +1 -2
  30. package/prompts/structure-planning.md +7 -2
  31. package/prompts/translator.md +4 -0
  32. package/tests/check-detail-result.test.mjs +8 -19
  33. package/tests/load-sources.test.mjs +65 -161
  34. package/tests/test-all-validation-cases.mjs +741 -0
  35. package/tests/test-save-docs.mjs +6 -17
  36. package/utils/constants.mjs +1 -2
  37. package/utils/markdown-checker.mjs +453 -0
  38. package/utils/mermaid-validator.mjs +153 -0
  39. package/utils/mermaid-worker-pool.mjs +250 -0
  40. package/utils/mermaid-worker.mjs +233 -0
  41. package/utils/utils.mjs +162 -114
package/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.2.8](https://github.com/AIGNE-io/aigne-doc-smith/compare/v0.2.7...v0.2.8) (2025-08-13)
4
+
5
+
6
+ ### Miscellaneous Chores
7
+
8
+ * release 0.2.8 ([da19bc0](https://github.com/AIGNE-io/aigne-doc-smith/commit/da19bc0b2c6c4e5fddaff84b4fa85c9d495b3ba0))
9
+
10
+ ## [0.2.7](https://github.com/AIGNE-io/aigne-doc-smith/compare/v0.2.6...v0.2.7) (2025-08-12)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * polish detail check ([#21](https://github.com/AIGNE-io/aigne-doc-smith/issues/21)) ([0268732](https://github.com/AIGNE-io/aigne-doc-smith/commit/02687329c3507b73f9cbf1aa2ff1b87921452516))
16
+
17
+
18
+ ### Miscellaneous Chores
19
+
20
+ * release 0.2.7 ([3b807fe](https://github.com/AIGNE-io/aigne-doc-smith/commit/3b807fed833a5160931747bce37aac00cf11d9ac))
21
+
22
+ ## [0.2.6](https://github.com/AIGNE-io/aigne-doc-smith/compare/v0.2.5...v0.2.6) (2025-08-12)
23
+
24
+
25
+ ### Miscellaneous Chores
26
+
27
+ * release 0.2.6 ([c5b5ea5](https://github.com/AIGNE-io/aigne-doc-smith/commit/c5b5ea5c404d44f3b0d420f0b57e4ae64ae5d624))
28
+
3
29
  ## [0.2.5](https://github.com/AIGNE-io/aigne-doc-smith/compare/v0.2.4...v0.2.5) (2025-08-08)
4
30
 
5
31
 
package/README.md CHANGED
@@ -206,6 +206,7 @@ aigne doc publish --appUrl https://your-discuss-kit-instance.com
206
206
  npx --no doc-smith run --entry-agent init
207
207
  npx --no doc-smith run --entry-agent generate
208
208
  npx --no doc-smith run --entry-agent update
209
+ npx --no doc-smith run --entry-agent retranslate
209
210
  npx --no doc-smith run --entry-agent publish
210
211
  ```
211
212
 
@@ -1,11 +1,6 @@
1
- export default async function checkDetailResult({
2
- structurePlan,
3
- reviewContent,
4
- }) {
5
- const linkRegex = /(?<!\!)\[([^\]]+)\]\(([^)]+)\)/g;
6
- const tableSeparatorRegex = /^\s*\|\s*-+\s*\|\s*$/;
7
- const codeBlockRegex = /^\s+```(?:\w+)?$/;
1
+ import { checkMarkdown } from "../utils/markdown-checker.mjs";
8
2
 
3
+ export default async function checkDetailResult({ structurePlan, reviewContent }) {
9
4
  let isApproved = true;
10
5
  const detailFeedback = [];
11
6
 
@@ -25,141 +20,20 @@ export default async function checkDetailResult({
25
20
  allowedLinks.add(flatPath);
26
21
  });
27
22
 
28
- const checkLinks = (text, source) => {
29
- let match;
30
- while ((match = linkRegex.exec(text)) !== null) {
31
- const link = match[2];
32
- const trimLink = link.trim();
23
+ // Run comprehensive markdown validation with all checks
24
+ try {
25
+ const markdownErrors = await checkMarkdown(reviewContent, "result", {
26
+ allowedLinks,
27
+ });
33
28
 
34
- // Only check links that processContent would process
35
- // Exclude external links and mailto
36
- if (/^(https?:\/\/|mailto:)/.test(trimLink)) continue;
37
-
38
- // Preserve anchors
39
- const [path, hash] = trimLink.split("#");
40
-
41
- // Only process relative paths or paths starting with /
42
- if (!path) continue;
43
-
44
- // Check if this link is in the allowed links set
45
- if (!allowedLinks.has(trimLink)) {
46
- isApproved = false;
47
- detailFeedback.push(
48
- `Found a dead link in ${source}: [${match[1]}](${trimLink}), ensure the link exists in the structure plan path`
49
- );
50
- }
51
- }
52
- };
53
-
54
- const performAllChecks = (text, source) => {
55
- // Split text into lines once and perform all checks in a single pass
56
- const lines = text.split("\n");
57
-
58
- // State variables for different checks
59
- let inCodeBlock = false;
60
- let codeBlockIndentLevel = 0;
61
- let codeBlockStartLine = 0;
62
- let inMermaidBlock = false;
63
- let mermaidStartLine = 0;
64
-
65
- for (let i = 0; i < lines.length; i++) {
66
- const line = lines[i];
67
- const lineNumber = i + 1;
68
-
69
- // Check table separators
70
- if (tableSeparatorRegex.test(line)) {
71
- isApproved = false;
72
- detailFeedback.push(
73
- `Found an incorrect table separator in ${source} at line ${lineNumber}: ${line.trim()}`
74
- );
75
- }
76
-
77
- // Check code block markers and indentation
78
- if (codeBlockRegex.test(line)) {
79
- if (!inCodeBlock) {
80
- // Starting a new code block
81
- inCodeBlock = true;
82
- codeBlockStartLine = lineNumber;
83
- // Calculate indentation level of the code block marker
84
- const match = line.match(/^(\s*)(```)/);
85
- codeBlockIndentLevel = match ? match[1].length : 0;
86
- } else {
87
- // Ending the code block
88
- inCodeBlock = false;
89
- codeBlockIndentLevel = 0;
90
- }
91
- } else if (inCodeBlock) {
92
- // If we're inside a code block, check if content has proper indentation
93
- const contentIndentLevel = line.match(/^(\s*)/)[1].length;
94
-
95
- // If code block marker has indentation, content should have at least the same indentation
96
- if (
97
- codeBlockIndentLevel > 0 &&
98
- contentIndentLevel < codeBlockIndentLevel
99
- ) {
100
- isApproved = false;
101
- detailFeedback.push(
102
- `Found code block with inconsistent indentation in ${source} at line ${codeBlockStartLine}: code block marker has ${codeBlockIndentLevel} spaces indentation but content at line ${lineNumber} has only ${contentIndentLevel} spaces indentation`
103
- );
104
- // Reset to avoid multiple errors for the same code block
105
- inCodeBlock = false;
106
- codeBlockIndentLevel = 0;
107
- }
108
- }
109
-
110
- // Check mermaid block markers
111
- if (/^\s*```mermaid\s*$/.test(line)) {
112
- inMermaidBlock = true;
113
- mermaidStartLine = lineNumber;
114
- } else if (inMermaidBlock && /^\s*```\s*$/.test(line)) {
115
- inMermaidBlock = false;
116
- } else if (inMermaidBlock) {
117
- // If we're inside a mermaid block, check for backticks in node labels
118
- // Check for node definitions with backticks in labels
119
- // Pattern: A["label with backticks"] or A{"label with backticks"}
120
- const nodeLabelRegex =
121
- /[A-Za-z0-9_]+\["([^"]*`[^"]*)"\]|[A-Za-z0-9_]+{"([^}]*`[^}]*)"}/g;
122
- let match;
123
-
124
- while ((match = nodeLabelRegex.exec(line)) !== null) {
125
- const label = match[1] || match[2];
126
- isApproved = false;
127
- detailFeedback.push(
128
- `Found backticks in Mermaid node label in ${source} at line ${lineNumber}: "${label}" - backticks in node labels cause rendering issues in Mermaid diagrams`
129
- );
130
- }
131
-
132
- // Check for edge descriptions with numbered list format
133
- // Pattern: -- "1. description" --> or similar variants
134
- const edgeDescriptionRegex = /--\s*"([^"]*)"\s*-->/g;
135
- let edgeMatch;
136
-
137
- while ((edgeMatch = edgeDescriptionRegex.exec(line)) !== null) {
138
- const description = edgeMatch[1];
139
- // Check if description starts with number followed by period
140
- if (/^\d+\.\s/.test(description)) {
141
- isApproved = false;
142
- detailFeedback.push(
143
- `Unsupported markdown: list - Found numbered list format in Mermaid edge description in ${source} at line ${lineNumber}: "${description}" - numbered lists in edge descriptions are not supported`
144
- );
145
- }
146
- }
147
- }
148
- }
149
-
150
- // Check single line content (this needs to be done after the loop)
151
- const newlineCount = (text.match(/\n/g) || []).length;
152
- if (newlineCount === 0 && text.trim().length > 0) {
29
+ if (markdownErrors.length > 0) {
153
30
  isApproved = false;
154
- detailFeedback.push(
155
- `Found single line content in ${source}: content appears to be on only one line, check for missing line breaks`
156
- );
31
+ detailFeedback.push(...markdownErrors);
157
32
  }
158
- };
159
-
160
- // Check content
161
- checkLinks(reviewContent, "result");
162
- performAllChecks(reviewContent, "result");
33
+ } catch (error) {
34
+ isApproved = false;
35
+ detailFeedback.push(`Found markdown validation error in result: ${error.message}`);
36
+ }
163
37
 
164
38
  return {
165
39
  isApproved,
@@ -2,8 +2,8 @@ import { access, readFile } from "node:fs/promises";
2
2
  import { dirname, join } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { TeamAgent } from "@aigne/core";
5
- import checkDetailResult from "./check-detail-result.mjs";
6
5
  import { hasSourceFilesChanged } from "../utils/utils.mjs";
6
+ import checkDetailResult from "./check-detail-result.mjs";
7
7
 
8
8
  // Get current script directory
9
9
  const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -20,7 +20,7 @@ export default async function checkDetail(
20
20
  forceRegenerate,
21
21
  ...rest
22
22
  },
23
- options
23
+ options,
24
24
  ) {
25
25
  // Check if the detail file already exists
26
26
  const flatName = path.replace(/^\//, "").replace(/\//g, "-");
@@ -41,11 +41,9 @@ export default async function checkDetail(
41
41
  let sourceIdsChanged = false;
42
42
  if (originalStructurePlan && sourceIds) {
43
43
  // Find the original node in the structure plan
44
- const originalNode = originalStructurePlan.find(
45
- (node) => node.path === path
46
- );
44
+ const originalNode = originalStructurePlan.find((node) => node.path === path);
47
45
 
48
- if (originalNode && originalNode.sourceIds) {
46
+ if (originalNode?.sourceIds) {
49
47
  const originalSourceIds = originalNode.sourceIds;
50
48
  const currentSourceIds = sourceIds;
51
49
 
@@ -1,11 +1,14 @@
1
1
  import {
2
2
  getCurrentGitHead,
3
+ getProjectInfo,
3
4
  hasFileChangesBetweenCommits,
5
+ loadConfigFromFile,
6
+ saveValueToConfig,
4
7
  } from "../utils/utils.mjs";
5
8
 
6
9
  export default async function checkStructurePlan(
7
- { originalStructurePlan, feedback, lastGitHead, forceRegenerate, ...rest },
8
- options
10
+ { originalStructurePlan, feedback, lastGitHead, ...rest },
11
+ options,
9
12
  ) {
10
13
  // Check if we need to regenerate structure plan
11
14
  let shouldRegenerate = false;
@@ -20,10 +23,7 @@ export default async function checkStructurePlan(
20
23
  // Check if there are relevant file changes since last generation
21
24
  const currentGitHead = getCurrentGitHead();
22
25
  if (currentGitHead && currentGitHead !== lastGitHead) {
23
- const hasChanges = hasFileChangesBetweenCommits(
24
- lastGitHead,
25
- currentGitHead
26
- );
26
+ const hasChanges = hasFileChangesBetweenCommits(lastGitHead, currentGitHead);
27
27
  if (hasChanges) {
28
28
  shouldRegenerate = true;
29
29
  }
@@ -38,17 +38,13 @@ export default async function checkStructurePlan(
38
38
  1. åÆ¹äŗŽę–°å¢žēš„å†…å®¹ļ¼ŒåÆä»„ę ¹ę®éœ€č¦ę–°å¢žčŠ‚ē‚¹ļ¼Œęˆ–č”„å……åˆ°åŽŸęœ‰čŠ‚ē‚¹å±•ē¤ŗ
39
39
  2. č°Øę…Žåˆ é™¤čŠ‚ē‚¹ļ¼Œé™¤éžčŠ‚ē‚¹å…³č” sourceIds éƒ½č¢«åˆ é™¤äŗ†
40
40
  3. äøčƒ½äæ®ę”¹åŽŸęœ‰čŠ‚ē‚¹ēš„ path
41
+ 4. ę ¹ę®ęœ€ę–°ēš„ Data Sources ęŒ‰éœ€č¦ę›“ę–°čŠ‚ē‚¹ēš„ sourceIdsļ¼Œå¦‚ę²”ęœ‰å¤§ēš„å˜åŒ–ļ¼ŒåÆä»„äøę›“ę–°ć€‚
41
42
  `;
42
43
  }
43
44
  }
44
45
 
45
46
  // If no regeneration needed, return original structure plan
46
- if (
47
- originalStructurePlan &&
48
- !feedback &&
49
- !shouldRegenerate &&
50
- !forceRegenerate
51
- ) {
47
+ if (originalStructurePlan && !feedback && !shouldRegenerate) {
52
48
  return {
53
49
  structurePlan: originalStructurePlan,
54
50
  };
@@ -62,9 +58,57 @@ export default async function checkStructurePlan(
62
58
  ...rest,
63
59
  });
64
60
 
61
+ let message = "";
62
+
63
+ // Check and save project information if user hasn't modified it
64
+ if (result.projectName || result.projectDesc) {
65
+ try {
66
+ const currentConfig = await loadConfigFromFile();
67
+ const projectInfo = await getProjectInfo();
68
+
69
+ // Check if user has modified project information
70
+ const userModifiedProjectName =
71
+ currentConfig?.projectName && currentConfig.projectName !== projectInfo.name;
72
+ const userModifiedProjectDesc =
73
+ currentConfig?.projectDesc && currentConfig.projectDesc !== projectInfo.description;
74
+
75
+ // If user hasn't modified project info and it's not from GitHub, save AI output
76
+ if (!userModifiedProjectName && !userModifiedProjectDesc) {
77
+ let hasUpdated = false;
78
+ // Don't update if the current info is from GitHub (meaningful repository info)
79
+ if (
80
+ result.projectName &&
81
+ result.projectName !== projectInfo.name &&
82
+ !projectInfo.fromGitHub
83
+ ) {
84
+ await saveValueToConfig("projectName", result.projectName);
85
+ message += `Project name: \`${result.projectName}\``;
86
+ hasUpdated = true;
87
+ }
88
+
89
+ if (
90
+ result.projectDesc &&
91
+ result.projectDesc !== projectInfo.description &&
92
+ !projectInfo.fromGitHub
93
+ ) {
94
+ await saveValueToConfig("projectDesc", result.projectDesc);
95
+ message += `\nProject description: \`${result.projectDesc}\``;
96
+ hasUpdated = true;
97
+ }
98
+
99
+ if (hasUpdated) {
100
+ message = `\n### Auto-updated Project Info to \`.aigne/doc-smith/config.yaml\`\n\n${message}\n\n`;
101
+ }
102
+ }
103
+ } catch (error) {
104
+ console.warn("Failed to check/save project information:", error.message);
105
+ }
106
+ }
107
+
65
108
  return {
66
109
  ...result,
67
110
  feedback: "", // clear feedback
111
+ projectInfoMessage: message,
68
112
  originalStructurePlan: originalStructurePlan
69
113
  ? originalStructurePlan
70
114
  : JSON.parse(JSON.stringify(result.structurePlan || [])),
@@ -15,9 +15,15 @@ skills:
15
15
  reflection:
16
16
  reviewer: ./check-detail-result.mjs
17
17
  is_approved: isApproved
18
- max_iterations: 3
18
+ max_iterations: 5
19
19
  return_last_on_max_iterations: true
20
20
  task_title: Generate detail for '{{ title }}'
21
+ - type: transform
22
+ jsonata: |
23
+ $merge([
24
+ $,
25
+ { "feedback": "" }
26
+ ])
21
27
  - ./batch-translate.yaml
22
28
  - ./save-single-doc.mjs
23
29
  input_schema:
@@ -27,7 +27,9 @@ skills:
27
27
  ])
28
28
  - ./find-item-by-path.mjs
29
29
  - ./format-structure-plan.mjs
30
- - ./detail-generator-and-translate.yaml
30
+ - url: ./detail-generator-and-translate.yaml
31
+ default_input:
32
+ isShowMessage: true
31
33
  input_schema:
32
34
  type: object
33
35
  properties:
@@ -30,7 +30,8 @@ skills:
30
30
  'translates': [$map(translateLanguages, function($lang) { {"language": $lang} })]
31
31
  }
32
32
  ])
33
- })
33
+ }),
34
+ "datasources": ""
34
35
  }
35
36
  ])
36
37
  - ./format-structure-plan.mjs
@@ -1,11 +1,25 @@
1
- import { readdir } from "node:fs/promises";
1
+ import { readdir, readFile } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
3
 
4
+ // Helper function to get action-specific text based on isTranslate flag
5
+ function getActionText(isTranslate, baseText) {
6
+ const action = isTranslate ? "retranslate" : "update";
7
+ return baseText.replace("{action}", action);
8
+ }
9
+
4
10
  export default async function findItemByPath(
5
- { "doc-path": docPath, structurePlanResult, boardId, docsDir },
11
+ {
12
+ "doc-path": docPath,
13
+ structurePlanResult,
14
+ boardId,
15
+ docsDir,
16
+ isTranslate,
17
+ feedback,
18
+ },
6
19
  options
7
20
  ) {
8
21
  let foundItem = null;
22
+ let selectedFileContent = null;
9
23
 
10
24
  // If docPath is empty, let user select from available documents
11
25
  if (!docPath) {
@@ -22,15 +36,13 @@ export default async function findItemByPath(
22
36
  );
23
37
 
24
38
  if (mainLanguageFiles.length === 0) {
25
- throw new Error(
26
- "Please provide a doc-path parameter to specify which document to update"
27
- );
39
+ throw new Error("No documents found in the docs directory");
28
40
  }
29
41
 
30
42
  // Let user select a file
31
43
  const selectedFile = await options.prompts.search({
32
- message: "Select a document to update:",
33
- source: async (input, { signal }) => {
44
+ message: getActionText(isTranslate, "Select a document to {action}:"),
45
+ source: async (input) => {
34
46
  if (!input || input.trim() === "") {
35
47
  return mainLanguageFiles.map((file) => ({
36
48
  name: file,
@@ -51,9 +63,19 @@ export default async function findItemByPath(
51
63
  });
52
64
 
53
65
  if (!selectedFile) {
54
- throw new Error(
55
- "Please provide a doc-path parameter to specify which document to update"
66
+ throw new Error("No document selected");
67
+ }
68
+
69
+ // Read the selected .md file content
70
+ try {
71
+ const selectedFilePath = join(docsDir, selectedFile);
72
+ selectedFileContent = await readFile(selectedFilePath, "utf-8");
73
+ } catch (readError) {
74
+ console.warn(
75
+ `āš ļø Could not read content from ${selectedFile}:`,
76
+ readError.message
56
77
  );
78
+ selectedFileContent = null;
57
79
  }
58
80
 
59
81
  // Convert filename back to path
@@ -71,15 +93,17 @@ export default async function findItemByPath(
71
93
  return itemFlattenedPath === flatName;
72
94
  });
73
95
  if (!foundItemByFile) {
74
- throw new Error(
75
- "Please provide a doc-path parameter to specify which document to update"
76
- );
96
+ throw new Error("No document found");
77
97
  }
78
98
 
79
99
  docPath = foundItemByFile.path;
80
100
  } catch (error) {
101
+ console.error(error);
81
102
  throw new Error(
82
- "Please provide a doc-path parameter to specify which document to update"
103
+ getActionText(
104
+ isTranslate,
105
+ "Please provide a doc-path parameter to specify which document to {action}"
106
+ )
83
107
  );
84
108
  }
85
109
  }
@@ -111,8 +135,33 @@ export default async function findItemByPath(
111
135
  );
112
136
  }
113
137
 
114
- // Merge the found item with originalStructurePlan
115
- return {
138
+ // Prompt for feedback if not provided
139
+ let userFeedback = feedback;
140
+ if (!userFeedback) {
141
+ const feedbackMessage = getActionText(
142
+ isTranslate,
143
+ "Please provide feedback for the {action} (press Enter to skip):"
144
+ );
145
+
146
+ userFeedback = await options.prompts.input({
147
+ message: feedbackMessage,
148
+ });
149
+ }
150
+
151
+ // Merge the found item with originalStructurePlan and add content if available
152
+ const result = {
116
153
  ...foundItem,
117
154
  };
155
+
156
+ // Add content if we read it from user selection
157
+ if (selectedFileContent !== null) {
158
+ result.content = selectedFileContent;
159
+ }
160
+
161
+ // Add feedback to result if provided
162
+ if (userFeedback?.trim()) {
163
+ result.feedback = userFeedback.trim();
164
+ }
165
+
166
+ return result;
118
167
  }
@@ -1,15 +1,19 @@
1
- import { writeFile, mkdir, readFile } from "node:fs/promises";
2
- import { join, dirname } from "node:path";
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { dirname, join } from "node:path";
3
3
  import chalk from "chalk";
4
- import { validatePath, getAvailablePaths } from "../utils/utils.mjs";
5
4
  import {
6
- SUPPORTED_LANGUAGES,
7
5
  DOCUMENT_STYLES,
6
+ SUPPORTED_LANGUAGES,
8
7
  TARGET_AUDIENCES,
9
8
  } from "../utils/constants.mjs";
9
+ import {
10
+ getAvailablePaths,
11
+ getProjectInfo,
12
+ validatePath,
13
+ } from "../utils/utils.mjs";
10
14
 
11
15
  // UI constants
12
- const PRESS_ENTER_TO_FINISH = "Press Enter to finish";
16
+ const _PRESS_ENTER_TO_FINISH = "Press Enter to finish";
13
17
 
14
18
  /**
15
19
  * Guide users through multi-turn dialogue to collect information and generate YAML configuration
@@ -129,7 +133,7 @@ export default async function init(
129
133
  while (true) {
130
134
  const selectedPath = await options.prompts.search({
131
135
  message: "Path:",
132
- source: async (input, { signal }) => {
136
+ source: async (input) => {
133
137
  if (!input || input.trim() === "") {
134
138
  return [
135
139
  {
@@ -180,6 +184,12 @@ export default async function init(
180
184
  // If no paths entered, use default
181
185
  input.sourcesPath = sourcePaths.length > 0 ? sourcePaths : ["./"];
182
186
 
187
+ // Save project info to config
188
+ const projectInfo = await getProjectInfo();
189
+ input.projectName = projectInfo.name;
190
+ input.projectDesc = projectInfo.description;
191
+ input.projectLogo = projectInfo.icon;
192
+
183
193
  // Generate YAML content
184
194
  const yamlContent = generateYAML(input, outputPath);
185
195
 
@@ -193,13 +203,17 @@ export default async function init(
193
203
 
194
204
  await writeFile(filePath, yamlContent, "utf8");
195
205
  console.log(`\nšŸŽ‰ Configuration saved to: ${chalk.cyan(filePath)}`);
206
+ // Print YAML content for user review
207
+ console.log(chalk.cyan("---"));
208
+ console.log(chalk.cyan(yamlContent));
209
+ console.log(chalk.cyan("---"));
196
210
  console.log(
197
- "šŸ’” You can edit the configuration file anytime to modify settings."
211
+ "šŸ’” You can edit the configuration file anytime to modify settings.\n"
198
212
  );
199
213
  console.log(
200
214
  `šŸš€ Run ${chalk.cyan(
201
215
  "'aigne doc generate'"
202
- )} to start documentation generation!`
216
+ )} to start documentation generation!\n`
203
217
  );
204
218
 
205
219
  return {};
@@ -215,15 +229,21 @@ export default async function init(
215
229
  /**
216
230
  * Generate YAML configuration content
217
231
  * @param {Object} input - Input object
218
- * @param {string} outputPath - Output path for directory configuration
219
232
  * @returns {string} YAML string
220
233
  */
221
- function generateYAML(input, outputPath) {
234
+ function generateYAML(input) {
222
235
  let yaml = "";
223
236
 
237
+ // Add project information at the beginning
238
+ yaml += `# Project information for documentation publishing\n`;
239
+ yaml += `projectName: ${input.projectName || ""}\n`;
240
+ yaml += `projectDesc: ${input.projectDesc || ""}\n`;
241
+ yaml += `projectLogo: ${input.projectLogo || ""}\n`;
242
+ yaml += `\n`;
243
+
224
244
  // Add rules (required field)
225
245
  yaml += `rules: |\n`;
226
- if (input.rules && input.rules.trim()) {
246
+ if (input.rules?.trim()) {
227
247
  yaml += ` ${input.rules.split("\n").join("\n ")}\n\n`;
228
248
  } else {
229
249
  yaml += ` \n\n`;