@aigne/doc-smith 0.2.6 → 0.2.9
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/agents/check-detail-result.mjs +2 -7
- package/agents/check-detail.mjs +4 -6
- package/agents/check-structure-plan.mjs +5 -10
- package/agents/find-item-by-path.mjs +13 -31
- package/agents/input-generator.mjs +15 -35
- package/agents/language-selector.mjs +6 -18
- package/agents/load-config.mjs +2 -2
- package/agents/load-sources.mjs +29 -117
- package/agents/publish-docs.mjs +15 -28
- package/agents/save-docs.mjs +8 -20
- package/agents/save-output.mjs +2 -9
- package/agents/save-single-doc.mjs +2 -2
- package/agents/schema/structure-plan.yaml +1 -1
- package/agents/transform-detail-datasources.mjs +2 -5
- 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 +10 -6
- package/tests/check-detail-result.test.mjs +8 -19
- package/tests/load-sources.test.mjs +65 -161
- package/tests/test-all-validation-cases.mjs +71 -37
- package/tests/test-save-docs.mjs +6 -17
- package/utils/constants.mjs +1 -2
- package/utils/file-utils.mjs +205 -0
- package/utils/markdown-checker.mjs +124 -57
- package/utils/mermaid-validator.mjs +5 -10
- package/utils/mermaid-worker-pool.mjs +7 -11
- package/utils/mermaid-worker.mjs +8 -17
- package/utils/utils.mjs +52 -104
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.2.9](https://github.com/AIGNE-io/aigne-doc-smith/compare/v0.2.8...v0.2.9) (2025-08-13)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* polish ignore check ([#25](https://github.com/AIGNE-io/aigne-doc-smith/issues/25)) ([90bc866](https://github.com/AIGNE-io/aigne-doc-smith/commit/90bc866513fef7b47047b1016e07bf38881c101c))
|
|
9
|
+
|
|
10
|
+
## [0.2.8](https://github.com/AIGNE-io/aigne-doc-smith/compare/v0.2.7...v0.2.8) (2025-08-13)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Miscellaneous Chores
|
|
14
|
+
|
|
15
|
+
* release 0.2.8 ([da19bc0](https://github.com/AIGNE-io/aigne-doc-smith/commit/da19bc0b2c6c4e5fddaff84b4fa85c9d495b3ba0))
|
|
16
|
+
|
|
17
|
+
## [0.2.7](https://github.com/AIGNE-io/aigne-doc-smith/compare/v0.2.6...v0.2.7) (2025-08-12)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Bug Fixes
|
|
21
|
+
|
|
22
|
+
* 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))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### Miscellaneous Chores
|
|
26
|
+
|
|
27
|
+
* release 0.2.7 ([3b807fe](https://github.com/AIGNE-io/aigne-doc-smith/commit/3b807fed833a5160931747bce37aac00cf11d9ac))
|
|
28
|
+
|
|
3
29
|
## [0.2.6](https://github.com/AIGNE-io/aigne-doc-smith/compare/v0.2.5...v0.2.6) (2025-08-12)
|
|
4
30
|
|
|
5
31
|
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { checkMarkdown } from "../utils/markdown-checker.mjs";
|
|
2
2
|
|
|
3
|
-
export default async function checkDetailResult({
|
|
4
|
-
structurePlan,
|
|
5
|
-
reviewContent,
|
|
6
|
-
}) {
|
|
3
|
+
export default async function checkDetailResult({ structurePlan, reviewContent }) {
|
|
7
4
|
let isApproved = true;
|
|
8
5
|
const detailFeedback = [];
|
|
9
6
|
|
|
@@ -35,9 +32,7 @@ export default async function checkDetailResult({
|
|
|
35
32
|
}
|
|
36
33
|
} catch (error) {
|
|
37
34
|
isApproved = false;
|
|
38
|
-
detailFeedback.push(
|
|
39
|
-
`Found markdown validation error in result: ${error.message}`
|
|
40
|
-
);
|
|
35
|
+
detailFeedback.push(`Found markdown validation error in result: ${error.message}`);
|
|
41
36
|
}
|
|
42
37
|
|
|
43
38
|
return {
|
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,14 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getCurrentGitHead,
|
|
3
|
+
getProjectInfo,
|
|
3
4
|
hasFileChangesBetweenCommits,
|
|
4
5
|
loadConfigFromFile,
|
|
5
6
|
saveValueToConfig,
|
|
6
|
-
getProjectInfo,
|
|
7
7
|
} from "../utils/utils.mjs";
|
|
8
8
|
|
|
9
9
|
export default async function checkStructurePlan(
|
|
10
10
|
{ originalStructurePlan, feedback, lastGitHead, ...rest },
|
|
11
|
-
options
|
|
11
|
+
options,
|
|
12
12
|
) {
|
|
13
13
|
// Check if we need to regenerate structure plan
|
|
14
14
|
let shouldRegenerate = false;
|
|
@@ -23,10 +23,7 @@ export default async function checkStructurePlan(
|
|
|
23
23
|
// Check if there are relevant file changes since last generation
|
|
24
24
|
const currentGitHead = getCurrentGitHead();
|
|
25
25
|
if (currentGitHead && currentGitHead !== lastGitHead) {
|
|
26
|
-
const hasChanges = hasFileChangesBetweenCommits(
|
|
27
|
-
lastGitHead,
|
|
28
|
-
currentGitHead
|
|
29
|
-
);
|
|
26
|
+
const hasChanges = hasFileChangesBetweenCommits(lastGitHead, currentGitHead);
|
|
30
27
|
if (hasChanges) {
|
|
31
28
|
shouldRegenerate = true;
|
|
32
29
|
}
|
|
@@ -71,11 +68,9 @@ export default async function checkStructurePlan(
|
|
|
71
68
|
|
|
72
69
|
// Check if user has modified project information
|
|
73
70
|
const userModifiedProjectName =
|
|
74
|
-
currentConfig?.projectName &&
|
|
75
|
-
currentConfig.projectName !== projectInfo.name;
|
|
71
|
+
currentConfig?.projectName && currentConfig.projectName !== projectInfo.name;
|
|
76
72
|
const userModifiedProjectDesc =
|
|
77
|
-
currentConfig?.projectDesc &&
|
|
78
|
-
currentConfig.projectDesc !== projectInfo.description;
|
|
73
|
+
currentConfig?.projectDesc && currentConfig.projectDesc !== projectInfo.description;
|
|
79
74
|
|
|
80
75
|
// If user hasn't modified project info and it's not from GitHub, save AI output
|
|
81
76
|
if (!userModifiedProjectName && !userModifiedProjectDesc) {
|
|
@@ -8,15 +8,8 @@ function getActionText(isTranslate, baseText) {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export default async function findItemByPath(
|
|
11
|
-
{
|
|
12
|
-
|
|
13
|
-
structurePlanResult,
|
|
14
|
-
boardId,
|
|
15
|
-
docsDir,
|
|
16
|
-
isTranslate,
|
|
17
|
-
feedback,
|
|
18
|
-
},
|
|
19
|
-
options
|
|
11
|
+
{ "doc-path": docPath, structurePlanResult, boardId, docsDir, isTranslate, feedback },
|
|
12
|
+
options,
|
|
20
13
|
) {
|
|
21
14
|
let foundItem = null;
|
|
22
15
|
let selectedFileContent = null;
|
|
@@ -30,9 +23,7 @@ export default async function findItemByPath(
|
|
|
30
23
|
// Filter for main language .md files (exclude _sidebar.md and language-specific files)
|
|
31
24
|
const mainLanguageFiles = files.filter(
|
|
32
25
|
(file) =>
|
|
33
|
-
file.endsWith(".md") &&
|
|
34
|
-
file !== "_sidebar.md" &&
|
|
35
|
-
!file.match(/\.\w+(-\w+)?\.md$/) // Exclude language-specific files like .en.md, .zh-CN.md, etc.
|
|
26
|
+
file.endsWith(".md") && file !== "_sidebar.md" && !file.match(/\.\w+(-\w+)?\.md$/), // Exclude language-specific files like .en.md, .zh-CN.md, etc.
|
|
36
27
|
);
|
|
37
28
|
|
|
38
29
|
if (mainLanguageFiles.length === 0) {
|
|
@@ -42,7 +33,7 @@ export default async function findItemByPath(
|
|
|
42
33
|
// Let user select a file
|
|
43
34
|
const selectedFile = await options.prompts.search({
|
|
44
35
|
message: getActionText(isTranslate, "Select a document to {action}:"),
|
|
45
|
-
source: async (input
|
|
36
|
+
source: async (input) => {
|
|
46
37
|
if (!input || input.trim() === "") {
|
|
47
38
|
return mainLanguageFiles.map((file) => ({
|
|
48
39
|
name: file,
|
|
@@ -52,7 +43,7 @@ export default async function findItemByPath(
|
|
|
52
43
|
|
|
53
44
|
const searchTerm = input.trim().toLowerCase();
|
|
54
45
|
const filteredFiles = mainLanguageFiles.filter((file) =>
|
|
55
|
-
file.toLowerCase().includes(searchTerm)
|
|
46
|
+
file.toLowerCase().includes(searchTerm),
|
|
56
47
|
);
|
|
57
48
|
|
|
58
49
|
return filteredFiles.map((file) => ({
|
|
@@ -71,10 +62,7 @@ export default async function findItemByPath(
|
|
|
71
62
|
const selectedFilePath = join(docsDir, selectedFile);
|
|
72
63
|
selectedFileContent = await readFile(selectedFilePath, "utf-8");
|
|
73
64
|
} catch (readError) {
|
|
74
|
-
console.warn(
|
|
75
|
-
`⚠️ Could not read content from ${selectedFile}:`,
|
|
76
|
-
readError.message
|
|
77
|
-
);
|
|
65
|
+
console.warn(`⚠️ Could not read content from ${selectedFile}:`, readError.message);
|
|
78
66
|
selectedFileContent = null;
|
|
79
67
|
}
|
|
80
68
|
|
|
@@ -87,9 +75,7 @@ export default async function findItemByPath(
|
|
|
87
75
|
|
|
88
76
|
// First try without boardId prefix
|
|
89
77
|
foundItemByFile = structurePlanResult.find((item) => {
|
|
90
|
-
const itemFlattenedPath = item.path
|
|
91
|
-
.replace(/^\//, "")
|
|
92
|
-
.replace(/\//g, "-");
|
|
78
|
+
const itemFlattenedPath = item.path.replace(/^\//, "").replace(/\//g, "-");
|
|
93
79
|
return itemFlattenedPath === flatName;
|
|
94
80
|
});
|
|
95
81
|
if (!foundItemByFile) {
|
|
@@ -102,8 +88,8 @@ export default async function findItemByPath(
|
|
|
102
88
|
throw new Error(
|
|
103
89
|
getActionText(
|
|
104
90
|
isTranslate,
|
|
105
|
-
"Please provide a doc-path parameter to specify which document to {action}"
|
|
106
|
-
)
|
|
91
|
+
"Please provide a doc-path parameter to specify which document to {action}",
|
|
92
|
+
),
|
|
107
93
|
);
|
|
108
94
|
}
|
|
109
95
|
}
|
|
@@ -121,18 +107,14 @@ export default async function findItemByPath(
|
|
|
121
107
|
// Find item by comparing flattened paths
|
|
122
108
|
foundItem = structurePlanResult.find((item) => {
|
|
123
109
|
// Convert item.path to flattened format (replace / with -)
|
|
124
|
-
const itemFlattenedPath = item.path
|
|
125
|
-
.replace(/^\//, "")
|
|
126
|
-
.replace(/\//g, "-");
|
|
110
|
+
const itemFlattenedPath = item.path.replace(/^\//, "").replace(/\//g, "-");
|
|
127
111
|
return itemFlattenedPath === flattenedPath;
|
|
128
112
|
});
|
|
129
113
|
}
|
|
130
114
|
}
|
|
131
115
|
|
|
132
116
|
if (!foundItem) {
|
|
133
|
-
throw new Error(
|
|
134
|
-
`Item with path "${docPath}" not found in structurePlanResult`
|
|
135
|
-
);
|
|
117
|
+
throw new Error(`Item with path "${docPath}" not found in structurePlanResult`);
|
|
136
118
|
}
|
|
137
119
|
|
|
138
120
|
// Prompt for feedback if not provided
|
|
@@ -140,7 +122,7 @@ export default async function findItemByPath(
|
|
|
140
122
|
if (!userFeedback) {
|
|
141
123
|
const feedbackMessage = getActionText(
|
|
142
124
|
isTranslate,
|
|
143
|
-
"Please provide feedback for the {action} (press Enter to skip):"
|
|
125
|
+
"Please provide feedback for the {action} (press Enter to skip):",
|
|
144
126
|
);
|
|
145
127
|
|
|
146
128
|
userFeedback = await options.prompts.input({
|
|
@@ -159,7 +141,7 @@ export default async function findItemByPath(
|
|
|
159
141
|
}
|
|
160
142
|
|
|
161
143
|
// Add feedback to result if provided
|
|
162
|
-
if (userFeedback
|
|
144
|
+
if (userFeedback?.trim()) {
|
|
163
145
|
result.feedback = userFeedback.trim();
|
|
164
146
|
}
|
|
165
147
|
|
|
@@ -1,18 +1,11 @@
|
|
|
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 {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
getProjectInfo,
|
|
8
|
-
} from "../utils/utils.mjs";
|
|
9
|
-
import {
|
|
10
|
-
SUPPORTED_LANGUAGES,
|
|
11
|
-
DOCUMENT_STYLES,
|
|
12
|
-
TARGET_AUDIENCES,
|
|
13
|
-
} from "../utils/constants.mjs";
|
|
4
|
+
import { DOCUMENT_STYLES, SUPPORTED_LANGUAGES, TARGET_AUDIENCES } from "../utils/constants.mjs";
|
|
5
|
+
import { getAvailablePaths, getProjectInfo, validatePath } from "../utils/utils.mjs";
|
|
6
|
+
|
|
14
7
|
// UI constants
|
|
15
|
-
const
|
|
8
|
+
const _PRESS_ENTER_TO_FINISH = "Press Enter to finish";
|
|
16
9
|
|
|
17
10
|
/**
|
|
18
11
|
* Guide users through multi-turn dialogue to collect information and generate YAML configuration
|
|
@@ -22,12 +15,8 @@ const PRESS_ENTER_TO_FINISH = "Press Enter to finish";
|
|
|
22
15
|
* @returns {Promise<Object>}
|
|
23
16
|
*/
|
|
24
17
|
export default async function init(
|
|
25
|
-
{
|
|
26
|
-
|
|
27
|
-
fileName = "config.yaml",
|
|
28
|
-
skipIfExists = false,
|
|
29
|
-
},
|
|
30
|
-
options
|
|
18
|
+
{ outputPath = ".aigne/doc-smith", fileName = "config.yaml", skipIfExists = false },
|
|
19
|
+
options,
|
|
31
20
|
) {
|
|
32
21
|
if (skipIfExists) {
|
|
33
22
|
const filePath = join(outputPath, fileName);
|
|
@@ -103,7 +92,7 @@ export default async function init(
|
|
|
103
92
|
// 4. Translation languages
|
|
104
93
|
// Filter out the primary language from available choices
|
|
105
94
|
const availableTranslationLanguages = SUPPORTED_LANGUAGES.filter(
|
|
106
|
-
(lang) => lang.code !== primaryLanguageChoice
|
|
95
|
+
(lang) => lang.code !== primaryLanguageChoice,
|
|
107
96
|
);
|
|
108
97
|
|
|
109
98
|
const translateLanguageChoices = await options.prompts.checkbox({
|
|
@@ -132,7 +121,7 @@ export default async function init(
|
|
|
132
121
|
while (true) {
|
|
133
122
|
const selectedPath = await options.prompts.search({
|
|
134
123
|
message: "Path:",
|
|
135
|
-
source: async (input
|
|
124
|
+
source: async (input) => {
|
|
136
125
|
if (!input || input.trim() === "") {
|
|
137
126
|
return [
|
|
138
127
|
{
|
|
@@ -153,11 +142,7 @@ export default async function init(
|
|
|
153
142
|
});
|
|
154
143
|
|
|
155
144
|
// Check if user chose to exit
|
|
156
|
-
if (
|
|
157
|
-
!selectedPath ||
|
|
158
|
-
selectedPath.trim() === "" ||
|
|
159
|
-
selectedPath === "Press Enter to finish"
|
|
160
|
-
) {
|
|
145
|
+
if (!selectedPath || selectedPath.trim() === "" || selectedPath === "Press Enter to finish") {
|
|
161
146
|
break;
|
|
162
147
|
}
|
|
163
148
|
|
|
@@ -206,13 +191,9 @@ export default async function init(
|
|
|
206
191
|
console.log(chalk.cyan("---"));
|
|
207
192
|
console.log(chalk.cyan(yamlContent));
|
|
208
193
|
console.log(chalk.cyan("---"));
|
|
194
|
+
console.log("💡 You can edit the configuration file anytime to modify settings.\n");
|
|
209
195
|
console.log(
|
|
210
|
-
|
|
211
|
-
);
|
|
212
|
-
console.log(
|
|
213
|
-
`🚀 Run ${chalk.cyan(
|
|
214
|
-
"'aigne doc generate'"
|
|
215
|
-
)} to start documentation generation!\n`
|
|
196
|
+
`🚀 Run ${chalk.cyan("'aigne doc generate'")} to start documentation generation!\n`,
|
|
216
197
|
);
|
|
217
198
|
|
|
218
199
|
return {};
|
|
@@ -242,7 +223,7 @@ function generateYAML(input) {
|
|
|
242
223
|
|
|
243
224
|
// Add rules (required field)
|
|
244
225
|
yaml += `rules: |\n`;
|
|
245
|
-
if (input.rules
|
|
226
|
+
if (input.rules?.trim()) {
|
|
246
227
|
yaml += ` ${input.rules.split("\n").join("\n ")}\n\n`;
|
|
247
228
|
} else {
|
|
248
229
|
yaml += ` \n\n`;
|
|
@@ -283,5 +264,4 @@ function generateYAML(input) {
|
|
|
283
264
|
return yaml;
|
|
284
265
|
}
|
|
285
266
|
|
|
286
|
-
init.description =
|
|
287
|
-
"Generate a configuration file for the documentation generation process";
|
|
267
|
+
init.description = "Generate a configuration file for the documentation generation process";
|
|
@@ -8,10 +8,7 @@ import { SUPPORTED_LANGUAGES } from "../utils/constants.mjs";
|
|
|
8
8
|
* @param {Object} options - Options object with prompts
|
|
9
9
|
* @returns {Promise<Object>} Selected languages
|
|
10
10
|
*/
|
|
11
|
-
export default async function languageSelector(
|
|
12
|
-
{ languages, translateLanguages },
|
|
13
|
-
options
|
|
14
|
-
) {
|
|
11
|
+
export default async function languageSelector({ languages, translateLanguages }, options) {
|
|
15
12
|
let selectedLanguages = [];
|
|
16
13
|
|
|
17
14
|
// Check if translateLanguages is available from config
|
|
@@ -21,24 +18,19 @@ export default async function languageSelector(
|
|
|
21
18
|
translateLanguages.length === 0
|
|
22
19
|
) {
|
|
23
20
|
throw new Error(
|
|
24
|
-
"No translation languages configured in config.yaml. Please add translateLanguages to your configuration."
|
|
21
|
+
"No translation languages configured in config.yaml. Please add translateLanguages to your configuration.",
|
|
25
22
|
);
|
|
26
23
|
}
|
|
27
24
|
|
|
28
25
|
// If languages are provided as parameter, validate against configured languages
|
|
29
26
|
if (languages && Array.isArray(languages) && languages.length > 0) {
|
|
30
|
-
const validLanguages = languages.filter((lang) =>
|
|
31
|
-
translateLanguages.includes(lang)
|
|
32
|
-
);
|
|
27
|
+
const validLanguages = languages.filter((lang) => translateLanguages.includes(lang));
|
|
33
28
|
|
|
34
29
|
if (validLanguages.length > 0) {
|
|
35
30
|
selectedLanguages = validLanguages;
|
|
36
31
|
} else {
|
|
37
32
|
console.log(`⚠️ Invalid languages provided: ${languages.join(", ")}`);
|
|
38
|
-
console.log(
|
|
39
|
-
"Available configured languages:",
|
|
40
|
-
translateLanguages.join(", ")
|
|
41
|
-
);
|
|
33
|
+
console.log("Available configured languages:", translateLanguages.join(", "));
|
|
42
34
|
}
|
|
43
35
|
}
|
|
44
36
|
|
|
@@ -46,13 +38,9 @@ export default async function languageSelector(
|
|
|
46
38
|
if (selectedLanguages.length === 0) {
|
|
47
39
|
// Create choices from configured languages with labels
|
|
48
40
|
const choices = translateLanguages.map((langCode) => {
|
|
49
|
-
const supportedLang = SUPPORTED_LANGUAGES.find(
|
|
50
|
-
(l) => l.code === langCode
|
|
51
|
-
);
|
|
41
|
+
const supportedLang = SUPPORTED_LANGUAGES.find((l) => l.code === langCode);
|
|
52
42
|
return {
|
|
53
|
-
name: supportedLang
|
|
54
|
-
? `${supportedLang.label} (${supportedLang.sample})`
|
|
55
|
-
: langCode,
|
|
43
|
+
name: supportedLang ? `${supportedLang.label} (${supportedLang.sample})` : langCode,
|
|
56
44
|
value: langCode,
|
|
57
45
|
short: langCode,
|
|
58
46
|
};
|
package/agents/load-config.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
1
|
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
3
|
import { parse } from "yaml";
|
|
4
4
|
|
|
5
5
|
export default async function loadConfig({ config }) {
|
|
@@ -8,7 +8,7 @@ export default async function loadConfig({ config }) {
|
|
|
8
8
|
try {
|
|
9
9
|
// Check if config file exists
|
|
10
10
|
await fs.access(configPath);
|
|
11
|
-
} catch (
|
|
11
|
+
} catch (_error) {
|
|
12
12
|
console.log(`Config file not found: ${configPath}`);
|
|
13
13
|
console.log("Please run 'aigne doc init' to create the config file.");
|
|
14
14
|
throw new Error(`Config file not found: ${configPath}`);
|
package/agents/load-sources.mjs
CHANGED
|
@@ -1,99 +1,8 @@
|
|
|
1
1
|
import { access, readFile, stat } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
getModifiedFilesBetweenCommits,
|
|
7
|
-
} from "../utils/utils.mjs";
|
|
8
|
-
import {
|
|
9
|
-
DEFAULT_INCLUDE_PATTERNS,
|
|
10
|
-
DEFAULT_EXCLUDE_PATTERNS,
|
|
11
|
-
} from "../utils/constants.mjs";
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Load .gitignore patterns from a directory
|
|
15
|
-
* @param {string} dir - Directory path
|
|
16
|
-
* @returns {object|null} Ignore instance or null if no .gitignore found
|
|
17
|
-
*/
|
|
18
|
-
async function loadGitignore(dir) {
|
|
19
|
-
const gitignorePath = path.join(dir, ".gitignore");
|
|
20
|
-
try {
|
|
21
|
-
await access(gitignorePath);
|
|
22
|
-
const gitignoreContent = await readFile(gitignorePath, "utf8");
|
|
23
|
-
// Create ignore patterns from .gitignore content
|
|
24
|
-
const ignorePatterns = gitignoreContent
|
|
25
|
-
.split("\n")
|
|
26
|
-
.map((line) => line.trim())
|
|
27
|
-
.filter((line) => line && !line.startsWith("#"))
|
|
28
|
-
.map((line) => line.replace(/^\//, "")); // Remove leading slash
|
|
29
|
-
|
|
30
|
-
return ignorePatterns.length > 0 ? ignorePatterns : null;
|
|
31
|
-
} catch {
|
|
32
|
-
// .gitignore file doesn't exist
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Get files using glob patterns
|
|
39
|
-
* @param {string} dir - Directory to search
|
|
40
|
-
* @param {string[]} includePatterns - Include patterns
|
|
41
|
-
* @param {string[]} excludePatterns - Exclude patterns
|
|
42
|
-
* @param {string[]} gitignorePatterns - .gitignore patterns
|
|
43
|
-
* @returns {Promise<string[]>} Array of file paths
|
|
44
|
-
*/
|
|
45
|
-
async function getFilesWithGlob(
|
|
46
|
-
dir,
|
|
47
|
-
includePatterns,
|
|
48
|
-
excludePatterns,
|
|
49
|
-
gitignorePatterns
|
|
50
|
-
) {
|
|
51
|
-
// Prepare all ignore patterns
|
|
52
|
-
const allIgnorePatterns = [];
|
|
53
|
-
|
|
54
|
-
if (excludePatterns) {
|
|
55
|
-
allIgnorePatterns.push(...excludePatterns);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (gitignorePatterns) {
|
|
59
|
-
allIgnorePatterns.push(...gitignorePatterns);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Add default exclusions if not already present
|
|
63
|
-
const defaultExclusions = ["node_modules/**", "test/**", "temp/**"];
|
|
64
|
-
for (const exclusion of defaultExclusions) {
|
|
65
|
-
if (!allIgnorePatterns.includes(exclusion)) {
|
|
66
|
-
allIgnorePatterns.push(exclusion);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Convert patterns to be relative to the directory
|
|
71
|
-
const patterns = includePatterns.map((pattern) => {
|
|
72
|
-
// If pattern doesn't start with / or **, make it relative to dir
|
|
73
|
-
if (!pattern.startsWith("/") && !pattern.startsWith("**")) {
|
|
74
|
-
return `**/${pattern}`; // Use ** to search recursively
|
|
75
|
-
}
|
|
76
|
-
return pattern;
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
try {
|
|
80
|
-
const files = await glob(patterns, {
|
|
81
|
-
cwd: dir,
|
|
82
|
-
ignore: allIgnorePatterns.length > 0 ? allIgnorePatterns : undefined,
|
|
83
|
-
absolute: true,
|
|
84
|
-
nodir: true, // Only return files, not directories
|
|
85
|
-
dot: false, // Don't include dot files by default
|
|
86
|
-
gitignore: true, // Enable .gitignore support
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
return files;
|
|
90
|
-
} catch (error) {
|
|
91
|
-
console.warn(
|
|
92
|
-
`Warning: Error during glob search in ${dir}: ${error.message}`
|
|
93
|
-
);
|
|
94
|
-
return [];
|
|
95
|
-
}
|
|
96
|
-
}
|
|
3
|
+
import { DEFAULT_EXCLUDE_PATTERNS, DEFAULT_INCLUDE_PATTERNS } from "../utils/constants.mjs";
|
|
4
|
+
import { getFilesWithGlob, loadGitignore } from "../utils/file-utils.mjs";
|
|
5
|
+
import { getCurrentGitHead, getModifiedFilesBetweenCommits } from "../utils/utils.mjs";
|
|
97
6
|
|
|
98
7
|
export default async function loadSources({
|
|
99
8
|
sources = [],
|
|
@@ -143,14 +52,8 @@ export default async function loadSources({
|
|
|
143
52
|
: [excludePatterns]
|
|
144
53
|
: [];
|
|
145
54
|
|
|
146
|
-
finalIncludePatterns = [
|
|
147
|
-
|
|
148
|
-
...userInclude,
|
|
149
|
-
];
|
|
150
|
-
finalExcludePatterns = [
|
|
151
|
-
...DEFAULT_EXCLUDE_PATTERNS,
|
|
152
|
-
...userExclude,
|
|
153
|
-
];
|
|
55
|
+
finalIncludePatterns = [...DEFAULT_INCLUDE_PATTERNS, ...userInclude];
|
|
56
|
+
finalExcludePatterns = [...DEFAULT_EXCLUDE_PATTERNS, ...userExclude];
|
|
154
57
|
} else {
|
|
155
58
|
// Use only user patterns
|
|
156
59
|
if (includePatterns) {
|
|
@@ -170,7 +73,7 @@ export default async function loadSources({
|
|
|
170
73
|
dir,
|
|
171
74
|
finalIncludePatterns,
|
|
172
75
|
finalExcludePatterns,
|
|
173
|
-
gitignorePatterns
|
|
76
|
+
gitignorePatterns,
|
|
174
77
|
);
|
|
175
78
|
allFiles = allFiles.concat(filesInDir);
|
|
176
79
|
}
|
|
@@ -194,7 +97,7 @@ export default async function loadSources({
|
|
|
194
97
|
sourceId: relativePath,
|
|
195
98
|
content,
|
|
196
99
|
};
|
|
197
|
-
})
|
|
100
|
+
}),
|
|
198
101
|
);
|
|
199
102
|
|
|
200
103
|
// Get the last structure plan result
|
|
@@ -253,19 +156,29 @@ export default async function loadSources({
|
|
|
253
156
|
try {
|
|
254
157
|
currentGitHead = getCurrentGitHead();
|
|
255
158
|
if (currentGitHead && currentGitHead !== lastGitHead) {
|
|
256
|
-
modifiedFiles = getModifiedFilesBetweenCommits(
|
|
257
|
-
|
|
258
|
-
currentGitHead
|
|
259
|
-
);
|
|
260
|
-
console.log(
|
|
261
|
-
`Detected ${modifiedFiles.length} modified files since last generation`
|
|
262
|
-
);
|
|
159
|
+
modifiedFiles = getModifiedFilesBetweenCommits(lastGitHead, currentGitHead);
|
|
160
|
+
console.log(`Detected ${modifiedFiles.length} modified files since last generation`);
|
|
263
161
|
}
|
|
264
162
|
} catch (error) {
|
|
265
163
|
console.warn("Failed to detect git changes:", error.message);
|
|
266
164
|
}
|
|
267
165
|
}
|
|
268
166
|
|
|
167
|
+
// Count words and lines in allSources
|
|
168
|
+
let totalWords = 0;
|
|
169
|
+
let totalLines = 0;
|
|
170
|
+
|
|
171
|
+
for (const source of Object.values(allSources)) {
|
|
172
|
+
if (typeof source === "string") {
|
|
173
|
+
// Count English words (simple regex for words containing a-zA-Z)
|
|
174
|
+
const words = source.match(/[a-zA-Z]+/g) || [];
|
|
175
|
+
totalWords += words.length;
|
|
176
|
+
|
|
177
|
+
// Count lines
|
|
178
|
+
totalLines += source.split("\n").length;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
269
182
|
return {
|
|
270
183
|
datasourcesList: sourceFiles,
|
|
271
184
|
datasources: allSources,
|
|
@@ -273,6 +186,8 @@ export default async function loadSources({
|
|
|
273
186
|
originalStructurePlan,
|
|
274
187
|
files,
|
|
275
188
|
modifiedFiles,
|
|
189
|
+
totalWords,
|
|
190
|
+
totalLines,
|
|
276
191
|
};
|
|
277
192
|
}
|
|
278
193
|
|
|
@@ -290,18 +205,15 @@ loadSources.input_schema = {
|
|
|
290
205
|
},
|
|
291
206
|
includePatterns: {
|
|
292
207
|
anyOf: [{ type: "string" }, { type: "array", items: { type: "string" } }],
|
|
293
|
-
description:
|
|
294
|
-
"Glob patterns to filter files by path or filename. If not set, include all.",
|
|
208
|
+
description: "Glob patterns to filter files by path or filename. If not set, include all.",
|
|
295
209
|
},
|
|
296
210
|
excludePatterns: {
|
|
297
211
|
anyOf: [{ type: "string" }, { type: "array", items: { type: "string" } }],
|
|
298
|
-
description:
|
|
299
|
-
"Glob patterns to exclude files by path or filename. If not set, exclude none.",
|
|
212
|
+
description: "Glob patterns to exclude files by path or filename. If not set, exclude none.",
|
|
300
213
|
},
|
|
301
214
|
useDefaultPatterns: {
|
|
302
215
|
type: "boolean",
|
|
303
|
-
description:
|
|
304
|
-
"Whether to use default include/exclude patterns. Defaults to true.",
|
|
216
|
+
description: "Whether to use default include/exclude patterns. Defaults to true.",
|
|
305
217
|
},
|
|
306
218
|
"doc-path": {
|
|
307
219
|
type: "string",
|