@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.
- package/CHANGELOG.md +26 -0
- package/README.md +1 -0
- package/agents/check-detail-result.mjs +13 -139
- package/agents/check-detail.mjs +4 -6
- package/agents/check-structure-plan.mjs +56 -12
- package/agents/detail-generator-and-translate.yaml +7 -1
- package/agents/detail-regenerator.yaml +3 -1
- package/agents/docs-generator.yaml +2 -1
- package/agents/find-item-by-path.mjs +64 -15
- package/agents/input-generator.mjs +31 -11
- package/agents/language-selector.mjs +89 -0
- package/agents/load-config.mjs +2 -2
- package/agents/load-sources.mjs +13 -40
- package/agents/publish-docs.mjs +47 -161
- package/agents/retranslate.yaml +74 -0
- package/agents/save-docs.mjs +19 -21
- package/agents/save-output.mjs +2 -9
- package/agents/save-single-doc.mjs +20 -1
- package/agents/schema/structure-plan.yaml +1 -1
- package/agents/structure-planning.yaml +6 -0
- package/agents/transform-detail-datasources.mjs +2 -5
- package/agents/translate.yaml +3 -0
- package/aigne.yaml +5 -1
- package/biome.json +13 -3
- package/docs-mcp/get-docs-structure.mjs +1 -1
- package/docs-mcp/read-doc-content.mjs +1 -4
- package/package.json +20 -7
- package/prompts/check-structure-planning-result.md +4 -7
- package/prompts/content-detail-generator.md +1 -2
- package/prompts/structure-planning.md +7 -2
- package/prompts/translator.md +4 -0
- package/tests/check-detail-result.test.mjs +8 -19
- package/tests/load-sources.test.mjs +65 -161
- package/tests/test-all-validation-cases.mjs +741 -0
- package/tests/test-save-docs.mjs +6 -17
- package/utils/constants.mjs +1 -2
- package/utils/markdown-checker.mjs +453 -0
- package/utils/mermaid-validator.mjs +153 -0
- package/utils/mermaid-worker-pool.mjs +250 -0
- package/utils/mermaid-worker.mjs +233 -0
- 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
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
23
|
+
// Run comprehensive markdown validation with all checks
|
|
24
|
+
try {
|
|
25
|
+
const markdownErrors = await checkMarkdown(reviewContent, "result", {
|
|
26
|
+
allowedLinks,
|
|
27
|
+
});
|
|
33
28
|
|
|
34
|
-
|
|
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
|
-
|
|
161
|
-
|
|
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,
|
package/agents/check-detail.mjs
CHANGED
|
@@ -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
|
|
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,
|
|
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:
|
|
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:
|
|
@@ -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
|
-
{
|
|
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
|
|
33
|
-
source: async (input
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
115
|
-
|
|
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 {
|
|
2
|
-
import {
|
|
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
|
|
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
|
|
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
|
|
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
|
|
246
|
+
if (input.rules?.trim()) {
|
|
227
247
|
yaml += ` ${input.rules.split("\n").join("\n ")}\n\n`;
|
|
228
248
|
} else {
|
|
229
249
|
yaml += ` \n\n`;
|