@aigne/doc-smith 0.1.4 ā 0.2.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.
- package/.github/PULL_REQUEST_TEMPLATE.md +28 -0
- package/CHANGELOG.md +15 -0
- package/README.md +3 -1
- package/agents/batch-docs-detail-generator.yaml +7 -2
- package/agents/batch-translate.yaml +1 -1
- package/agents/check-detail-result.mjs +52 -41
- package/agents/{check-detail-generated.mjs ā check-detail.mjs} +38 -6
- package/agents/check-structure-plan.mjs +72 -0
- package/agents/check-structure-planning-result.yaml +1 -1
- package/agents/content-detail-generator.yaml +1 -1
- package/agents/detail-generator-and-translate.yaml +1 -1
- package/agents/detail-regenerator.yaml +3 -0
- package/agents/docs-generator.yaml +14 -9
- package/agents/find-item-by-path.mjs +84 -5
- package/agents/input-generator.mjs +181 -38
- package/agents/load-config.mjs +1 -0
- package/agents/load-sources.mjs +44 -63
- package/agents/publish-docs.mjs +169 -58
- package/agents/reflective-structure-planner.yaml +1 -1
- package/agents/save-docs.mjs +12 -6
- package/agents/save-single-doc.mjs +1 -1
- package/agents/structure-planning.yaml +1 -1
- package/agents/team-publish-docs.yaml +3 -0
- package/agents/transform-detail-datasources.mjs +19 -5
- package/aigne.yaml +2 -2
- package/package.json +8 -6
- package/prompts/check-structure-planning-result.md +1 -1
- package/prompts/structure-planning.md +1 -1
- package/utils/constants.mjs +105 -0
- package/utils/utils.mjs +544 -0
- package/agents/check-structure-planning.mjs +0 -33
|
@@ -1,5 +1,15 @@
|
|
|
1
|
-
import { writeFile, mkdir } from "node:fs/promises";
|
|
1
|
+
import { writeFile, mkdir, readFile } from "node:fs/promises";
|
|
2
2
|
import { join, dirname } from "node:path";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { validatePath, getAvailablePaths } from "../utils/utils.mjs";
|
|
5
|
+
import {
|
|
6
|
+
SUPPORTED_LANGUAGES,
|
|
7
|
+
DOCUMENT_STYLES,
|
|
8
|
+
TARGET_AUDIENCES,
|
|
9
|
+
} from "../utils/constants.mjs";
|
|
10
|
+
|
|
11
|
+
// UI constants
|
|
12
|
+
const PRESS_ENTER_TO_FINISH = "Press Enter to finish";
|
|
3
13
|
|
|
4
14
|
/**
|
|
5
15
|
* Guide users through multi-turn dialogue to collect information and generate YAML configuration
|
|
@@ -9,48 +19,175 @@ import { join, dirname } from "node:path";
|
|
|
9
19
|
* @returns {Promise<Object>}
|
|
10
20
|
*/
|
|
11
21
|
export default async function init(
|
|
12
|
-
{
|
|
22
|
+
{
|
|
23
|
+
outputPath = "./doc-smith",
|
|
24
|
+
fileName = "config.yaml",
|
|
25
|
+
skipIfExists = false,
|
|
26
|
+
},
|
|
13
27
|
options
|
|
14
28
|
) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
29
|
+
if (skipIfExists) {
|
|
30
|
+
const filePath = join(outputPath, fileName);
|
|
31
|
+
if (await readFile(filePath, "utf8").catch(() => null)) {
|
|
32
|
+
return {};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
console.log("š Welcome to AIGNE DocSmith!");
|
|
37
|
+
console.log("Let's create your documentation configuration.\n");
|
|
19
38
|
|
|
20
39
|
// Collect user information
|
|
21
40
|
const input = {};
|
|
22
41
|
|
|
23
|
-
// 1. Document generation rules
|
|
24
|
-
console.log("
|
|
25
|
-
|
|
26
|
-
|
|
42
|
+
// 1. Document generation rules with style selection
|
|
43
|
+
console.log("š Step 1/6: Document Generation Rules");
|
|
44
|
+
|
|
45
|
+
// Let user select a document style
|
|
46
|
+
const styleChoice = await options.prompts.select({
|
|
47
|
+
message: "Choose your documentation style:",
|
|
48
|
+
choices: Object.entries(DOCUMENT_STYLES).map(([key, style]) => ({
|
|
49
|
+
name: `${style.name} - ${style.rules}`,
|
|
50
|
+
value: key,
|
|
51
|
+
})),
|
|
27
52
|
});
|
|
28
|
-
input.rules = rulesInput.trim();
|
|
29
53
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
54
|
+
let rules;
|
|
55
|
+
if (styleChoice === "custom") {
|
|
56
|
+
// User wants to input custom rules
|
|
57
|
+
rules = await options.prompts.input({
|
|
58
|
+
message: "Enter your custom documentation rules:",
|
|
59
|
+
});
|
|
60
|
+
} else {
|
|
61
|
+
// Use predefined style directly
|
|
62
|
+
rules = DOCUMENT_STYLES[styleChoice].rules;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
input.rules = rules.trim();
|
|
66
|
+
|
|
67
|
+
// 2. Target audience selection
|
|
68
|
+
console.log("\nš„ Step 2/6: Target Audience");
|
|
69
|
+
|
|
70
|
+
// Let user select target audience
|
|
71
|
+
const audienceChoice = await options.prompts.select({
|
|
72
|
+
message: "Who is your target audience?",
|
|
73
|
+
choices: Object.entries(TARGET_AUDIENCES).map(([key, audience]) => ({
|
|
74
|
+
name: audience,
|
|
75
|
+
value: key,
|
|
76
|
+
})),
|
|
35
77
|
});
|
|
36
|
-
|
|
78
|
+
|
|
79
|
+
let targetAudience;
|
|
80
|
+
if (audienceChoice === "custom") {
|
|
81
|
+
// User wants to input custom audience
|
|
82
|
+
targetAudience = await options.prompts.input({
|
|
83
|
+
message: "Enter your custom target audience:",
|
|
84
|
+
});
|
|
85
|
+
} else {
|
|
86
|
+
// Use predefined audience directly
|
|
87
|
+
targetAudience = TARGET_AUDIENCES[audienceChoice];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
input.targetAudience = targetAudience.trim();
|
|
37
91
|
|
|
38
92
|
// 3. Language settings
|
|
39
|
-
console.log("\n
|
|
40
|
-
|
|
41
|
-
|
|
93
|
+
console.log("\nš Step 3/6: Primary Language");
|
|
94
|
+
|
|
95
|
+
// Let user select primary language from supported list
|
|
96
|
+
const primaryLanguageChoice = await options.prompts.select({
|
|
97
|
+
message: "Choose primary documentation language:",
|
|
98
|
+
choices: SUPPORTED_LANGUAGES.map((lang) => ({
|
|
99
|
+
name: `${lang.label} - ${lang.sample}`,
|
|
100
|
+
value: lang.code,
|
|
101
|
+
})),
|
|
42
102
|
});
|
|
43
|
-
|
|
103
|
+
|
|
104
|
+
input.locale = primaryLanguageChoice;
|
|
44
105
|
|
|
45
106
|
// 4. Translation languages
|
|
46
|
-
console.log("\n
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
107
|
+
console.log("\nš Step 4/6: Translation Languages");
|
|
108
|
+
|
|
109
|
+
// Filter out the primary language from available choices
|
|
110
|
+
const availableTranslationLanguages = SUPPORTED_LANGUAGES.filter(
|
|
111
|
+
(lang) => lang.code !== primaryLanguageChoice
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
const translateLanguageChoices = await options.prompts.checkbox({
|
|
115
|
+
message: "Select translation languages:",
|
|
116
|
+
choices: availableTranslationLanguages.map((lang) => ({
|
|
117
|
+
name: `${lang.label} - ${lang.sample}`,
|
|
118
|
+
value: lang.code,
|
|
119
|
+
})),
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
input.translateLanguages = translateLanguageChoices;
|
|
123
|
+
|
|
124
|
+
// 5. Documentation directory
|
|
125
|
+
console.log("\nš Step 5/6: Output Directory");
|
|
126
|
+
const docsDirInput = await options.prompts.input({
|
|
127
|
+
message: `Where to save generated docs:`,
|
|
128
|
+
default: `${outputPath}/docs`,
|
|
50
129
|
});
|
|
51
|
-
input.
|
|
52
|
-
|
|
53
|
-
|
|
130
|
+
input.docsDir = docsDirInput.trim() || `${outputPath}/docs`;
|
|
131
|
+
|
|
132
|
+
// 6. Source code paths
|
|
133
|
+
console.log("\nš Step 6/6: Source Code Paths");
|
|
134
|
+
console.log("Enter paths to analyze for documentation (e.g., ./src, ./lib)");
|
|
135
|
+
console.log("š” If no paths are configured, './' will be used as default");
|
|
136
|
+
|
|
137
|
+
const sourcePaths = [];
|
|
138
|
+
while (true) {
|
|
139
|
+
const selectedPath = await options.prompts.search({
|
|
140
|
+
message: "Path:",
|
|
141
|
+
source: async (input, { signal }) => {
|
|
142
|
+
if (!input || input.trim() === "") {
|
|
143
|
+
return [
|
|
144
|
+
{
|
|
145
|
+
name: "Press Enter to finish",
|
|
146
|
+
value: "",
|
|
147
|
+
description: "",
|
|
148
|
+
},
|
|
149
|
+
];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const searchTerm = input.trim();
|
|
153
|
+
|
|
154
|
+
// Search for matching files and folders in current directory
|
|
155
|
+
const availablePaths = getAvailablePaths(searchTerm);
|
|
156
|
+
|
|
157
|
+
return [...availablePaths];
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// Check if user chose to exit
|
|
162
|
+
if (
|
|
163
|
+
!selectedPath ||
|
|
164
|
+
selectedPath.trim() === "" ||
|
|
165
|
+
selectedPath === "Press Enter to finish"
|
|
166
|
+
) {
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const trimmedPath = selectedPath.trim();
|
|
171
|
+
|
|
172
|
+
// Use validatePath to check if path is valid
|
|
173
|
+
const validation = validatePath(trimmedPath);
|
|
174
|
+
|
|
175
|
+
if (!validation.isValid) {
|
|
176
|
+
console.log(`ā ļø ${validation.error}`);
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Avoid duplicate paths
|
|
181
|
+
if (sourcePaths.includes(trimmedPath)) {
|
|
182
|
+
console.log(`ā ļø Path already exists: ${trimmedPath}`);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
sourcePaths.push(trimmedPath);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// If no paths entered, use default
|
|
190
|
+
input.sourcesPath = sourcePaths.length > 0 ? sourcePaths : ["./"];
|
|
54
191
|
|
|
55
192
|
// Generate YAML content
|
|
56
193
|
const yamlContent = generateYAML(input, outputPath);
|
|
@@ -64,13 +201,17 @@ export default async function init(
|
|
|
64
201
|
await mkdir(dirPath, { recursive: true });
|
|
65
202
|
|
|
66
203
|
await writeFile(filePath, yamlContent, "utf8");
|
|
67
|
-
console.log(`\n
|
|
204
|
+
console.log(`\nš Configuration saved to: ${chalk.cyan(filePath)}`);
|
|
205
|
+
console.log(
|
|
206
|
+
"š” You can edit the configuration file anytime to modify settings."
|
|
207
|
+
);
|
|
208
|
+
console.log(
|
|
209
|
+
`š Run ${chalk.cyan(
|
|
210
|
+
"'aigne doc generate'"
|
|
211
|
+
)} to start documentation generation!`
|
|
212
|
+
);
|
|
68
213
|
|
|
69
|
-
return {
|
|
70
|
-
inputGeneratorStatus: true,
|
|
71
|
-
inputGeneratorPath: filePath,
|
|
72
|
-
inputGeneratorContent: yamlContent,
|
|
73
|
-
};
|
|
214
|
+
return {};
|
|
74
215
|
} catch (error) {
|
|
75
216
|
console.error(`ā Failed to save configuration file: ${error.message}`);
|
|
76
217
|
return {
|
|
@@ -121,11 +262,13 @@ function generateYAML(input, outputPath) {
|
|
|
121
262
|
yaml += `# - en # Example: English translation\n`;
|
|
122
263
|
}
|
|
123
264
|
|
|
124
|
-
// Add
|
|
125
|
-
yaml += `docsDir: ${
|
|
126
|
-
yaml += `outputDir: ${outputPath}/output # Directory to save output files\n`;
|
|
265
|
+
// Add directory and source path configurations
|
|
266
|
+
yaml += `docsDir: ${input.docsDir} # Directory to save generated documentation\n`;
|
|
267
|
+
// yaml += `outputDir: ${outputPath}/output # Directory to save output files\n`;
|
|
127
268
|
yaml += `sourcesPath: # Source code paths to analyze\n`;
|
|
128
|
-
|
|
269
|
+
input.sourcesPath.forEach((path) => {
|
|
270
|
+
yaml += ` - ${path}\n`;
|
|
271
|
+
});
|
|
129
272
|
|
|
130
273
|
return yaml;
|
|
131
274
|
}
|
package/agents/load-config.mjs
CHANGED
package/agents/load-sources.mjs
CHANGED
|
@@ -1,67 +1,14 @@
|
|
|
1
1
|
import { access, readFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { glob } from "glob";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
"*.go",
|
|
13
|
-
"*.java",
|
|
14
|
-
"*.pyi",
|
|
15
|
-
"*.pyx",
|
|
16
|
-
"*.c",
|
|
17
|
-
"*.cc",
|
|
18
|
-
"*.cpp",
|
|
19
|
-
"*.h",
|
|
20
|
-
"*.md",
|
|
21
|
-
"*.rst",
|
|
22
|
-
"*.json",
|
|
23
|
-
"*Dockerfile",
|
|
24
|
-
"*Makefile",
|
|
25
|
-
"*.yaml",
|
|
26
|
-
"*.yml",
|
|
27
|
-
];
|
|
28
|
-
|
|
29
|
-
const DEFAULT_EXCLUDE_PATTERNS = [
|
|
30
|
-
"aigne-docs/**",
|
|
31
|
-
"doc-smith/**",
|
|
32
|
-
"assets/**",
|
|
33
|
-
"data/**",
|
|
34
|
-
"images/**",
|
|
35
|
-
"public/**",
|
|
36
|
-
"static/**",
|
|
37
|
-
"**/vendor/**",
|
|
38
|
-
"temp/**",
|
|
39
|
-
"**/*docs/**",
|
|
40
|
-
"**/*doc/**",
|
|
41
|
-
"**/*venv/**",
|
|
42
|
-
"*.venv/**",
|
|
43
|
-
"*test*",
|
|
44
|
-
"**/*test/**",
|
|
45
|
-
"**/*tests/**",
|
|
46
|
-
"**/*examples/**",
|
|
47
|
-
"**/playgrounds/**",
|
|
48
|
-
"v1/**",
|
|
49
|
-
"**/dist/**",
|
|
50
|
-
"**/*build/**",
|
|
51
|
-
"**/*experimental/**",
|
|
52
|
-
"**/*deprecated/**",
|
|
53
|
-
"**/*misc/**",
|
|
54
|
-
"**/*legacy/**",
|
|
55
|
-
".git/**",
|
|
56
|
-
".github/**",
|
|
57
|
-
".next/**",
|
|
58
|
-
".vscode/**",
|
|
59
|
-
"**/*obj/**",
|
|
60
|
-
"**/*bin/**",
|
|
61
|
-
"**/*node_modules/**",
|
|
62
|
-
"*.log",
|
|
63
|
-
"**/*test.*",
|
|
64
|
-
];
|
|
4
|
+
import {
|
|
5
|
+
getCurrentGitHead,
|
|
6
|
+
getModifiedFilesBetweenCommits,
|
|
7
|
+
} from "../utils/utils.mjs";
|
|
8
|
+
import {
|
|
9
|
+
DEFAULT_INCLUDE_PATTERNS,
|
|
10
|
+
DEFAULT_EXCLUDE_PATTERNS,
|
|
11
|
+
} from "../utils/constants.mjs";
|
|
65
12
|
|
|
66
13
|
/**
|
|
67
14
|
* Load .gitignore patterns from a directory
|
|
@@ -158,6 +105,7 @@ export default async function loadSources({
|
|
|
158
105
|
"doc-path": docPath,
|
|
159
106
|
boardId,
|
|
160
107
|
useDefaultPatterns = true,
|
|
108
|
+
lastGitHead,
|
|
161
109
|
} = {}) {
|
|
162
110
|
let files = Array.isArray(sources) ? [...sources] : [];
|
|
163
111
|
|
|
@@ -224,9 +172,11 @@ export default async function loadSources({
|
|
|
224
172
|
const sourceFiles = await Promise.all(
|
|
225
173
|
files.map(async (file) => {
|
|
226
174
|
const content = await readFile(file, "utf8");
|
|
227
|
-
|
|
175
|
+
// Convert absolute path to relative path from project root
|
|
176
|
+
const relativePath = path.relative(process.cwd(), file);
|
|
177
|
+
allSources += `// sourceId: ${relativePath}\n${content}\n`;
|
|
228
178
|
return {
|
|
229
|
-
sourceId:
|
|
179
|
+
sourceId: relativePath,
|
|
230
180
|
content,
|
|
231
181
|
};
|
|
232
182
|
})
|
|
@@ -280,12 +230,34 @@ export default async function loadSources({
|
|
|
280
230
|
}
|
|
281
231
|
}
|
|
282
232
|
|
|
233
|
+
// Get git change detection data
|
|
234
|
+
let modifiedFiles = [];
|
|
235
|
+
let currentGitHead = null;
|
|
236
|
+
|
|
237
|
+
if (lastGitHead) {
|
|
238
|
+
try {
|
|
239
|
+
currentGitHead = getCurrentGitHead();
|
|
240
|
+
if (currentGitHead && currentGitHead !== lastGitHead) {
|
|
241
|
+
modifiedFiles = getModifiedFilesBetweenCommits(
|
|
242
|
+
lastGitHead,
|
|
243
|
+
currentGitHead
|
|
244
|
+
);
|
|
245
|
+
console.log(
|
|
246
|
+
`Detected ${modifiedFiles.length} modified files since last generation`
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
} catch (error) {
|
|
250
|
+
console.warn("Failed to detect git changes:", error.message);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
283
254
|
return {
|
|
284
255
|
datasourcesList: sourceFiles,
|
|
285
256
|
datasources: allSources,
|
|
286
257
|
content,
|
|
287
258
|
originalStructurePlan,
|
|
288
259
|
files,
|
|
260
|
+
modifiedFiles,
|
|
289
261
|
};
|
|
290
262
|
}
|
|
291
263
|
|
|
@@ -324,6 +296,10 @@ loadSources.input_schema = {
|
|
|
324
296
|
type: "string",
|
|
325
297
|
description: "The board ID for boardId-flattenedPath format matching",
|
|
326
298
|
},
|
|
299
|
+
lastGitHead: {
|
|
300
|
+
type: "string",
|
|
301
|
+
description: "The git HEAD from last generation for change detection",
|
|
302
|
+
},
|
|
327
303
|
},
|
|
328
304
|
required: [],
|
|
329
305
|
};
|
|
@@ -349,5 +325,10 @@ loadSources.output_schema = {
|
|
|
349
325
|
items: { type: "string" },
|
|
350
326
|
description: "Array of file paths that were loaded",
|
|
351
327
|
},
|
|
328
|
+
modifiedFiles: {
|
|
329
|
+
type: "array",
|
|
330
|
+
items: { type: "string" },
|
|
331
|
+
description: "Array of modified files since last generation",
|
|
332
|
+
},
|
|
352
333
|
},
|
|
353
334
|
};
|