@aigne/doc-smith 0.8.15-beta.1 → 0.8.15-beta.11
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 +89 -0
- package/agents/clear/choose-contents.mjs +4 -4
- package/agents/clear/clear-auth-tokens.mjs +8 -8
- package/agents/clear/clear-deployment-config.mjs +2 -2
- package/agents/clear/clear-document-config.mjs +3 -3
- package/agents/clear/clear-document-structure.mjs +10 -10
- package/agents/clear/clear-generated-docs.mjs +103 -14
- package/agents/clear/clear-media-description.mjs +7 -7
- package/agents/evaluate/document-structure.yaml +3 -1
- package/agents/evaluate/document.yaml +3 -1
- package/agents/evaluate/index.yaml +1 -3
- package/agents/generate/check-diagram.mjs +1 -1
- package/agents/generate/check-need-generate-structure.mjs +2 -7
- package/agents/generate/draw-diagram.yaml +4 -0
- package/agents/generate/generate-structure.yaml +117 -65
- package/agents/generate/index.yaml +3 -3
- package/agents/generate/{merge-d2-diagram.yaml → merge-diagram.yaml} +7 -6
- package/agents/generate/update-document-structure.yaml +1 -1
- package/agents/generate/user-review-document-structure.mjs +3 -25
- package/agents/generate/utils/merge-document-structures.mjs +30 -0
- package/agents/init/check.mjs +4 -2
- package/agents/init/index.mjs +37 -7
- package/agents/media/load-media-description.mjs +12 -24
- package/agents/publish/publish-docs.mjs +3 -8
- package/agents/schema/document-execution-structure.yaml +1 -1
- package/agents/schema/document-structure-item.yaml +23 -0
- package/agents/schema/document-structure-refine-item.yaml +20 -0
- package/agents/schema/document-structure.yaml +1 -1
- package/agents/translate/index.yaml +1 -4
- package/agents/translate/record-translation-history.mjs +6 -2
- package/agents/translate/translate-multilingual.yaml +1 -1
- package/agents/update/batch-generate-document.yaml +1 -1
- package/agents/update/batch-update-document.yaml +1 -1
- package/agents/update/check-document.mjs +35 -13
- package/agents/update/check-generate-diagram.mjs +29 -0
- package/agents/update/generate-diagram.yaml +29 -0
- package/agents/update/generate-document.yaml +17 -30
- package/agents/update/handle-document-update.yaml +10 -1
- package/agents/update/save-and-translate-document.mjs +18 -47
- package/agents/update/update-document-detail.yaml +2 -1
- package/agents/update/update-single-document.yaml +1 -1
- package/agents/update/user-review-document.mjs +6 -5
- package/agents/utils/choose-docs.mjs +16 -5
- package/agents/utils/find-item-by-path.mjs +4 -2
- package/agents/utils/load-sources.mjs +63 -46
- package/agents/utils/{save-docs.mjs → post-generate.mjs} +2 -51
- package/agents/utils/save-doc-translation.mjs +27 -0
- package/agents/utils/{save-single-doc.mjs → save-doc.mjs} +17 -12
- package/agents/utils/save-sidebar.mjs +38 -0
- package/agents/utils/{transform-detail-datasources.mjs → transform-detail-data-sources.mjs} +7 -7
- package/aigne.yaml +16 -8
- package/package.json +3 -1
- package/prompts/common/document/content-rules-core.md +6 -6
- package/prompts/common/document/media-file-list-usage-rules.md +12 -0
- package/prompts/common/document/openapi-usage-rules.md +36 -0
- package/prompts/common/document/role-and-personality.md +1 -2
- package/prompts/common/document-structure/conflict-resolution-guidance.md +2 -2
- package/prompts/common/document-structure/document-structure-rules.md +8 -8
- package/prompts/common/document-structure/output-constraints.md +3 -3
- package/prompts/detail/custom/custom-components.md +38 -3
- package/prompts/detail/d2-diagram/rules.md +11 -14
- package/prompts/detail/d2-diagram/system-prompt.md +0 -14
- package/prompts/detail/d2-diagram/user-prompt.md +39 -0
- package/prompts/detail/generate/document-rules.md +3 -3
- package/prompts/detail/generate/system-prompt.md +2 -6
- package/prompts/detail/generate/user-prompt.md +20 -61
- package/prompts/detail/update/system-prompt.md +2 -6
- package/prompts/detail/update/user-prompt.md +7 -6
- package/prompts/evaluate/document.md +0 -4
- package/prompts/structure/check-document-structure.md +4 -4
- package/prompts/structure/generate/system-prompt.md +0 -31
- package/prompts/structure/generate/user-prompt.md +73 -29
- package/prompts/structure/review/structure-review-system.md +81 -0
- package/prompts/structure/update/system-prompt.md +1 -1
- package/prompts/structure/update/user-prompt.md +4 -4
- package/prompts/translate/code-block.md +13 -3
- package/prompts/translate/translate-document.md +3 -3
- package/types/document-structure-schema.mjs +3 -3
- package/utils/constants/index.mjs +6 -0
- package/utils/docs-finder-utils.mjs +85 -3
- package/utils/extract-api.mjs +32 -0
- package/utils/file-utils.mjs +153 -101
- package/utils/history-utils.mjs +20 -8
- package/utils/load-config.mjs +20 -1
- package/utils/markdown-checker.mjs +35 -1
- package/utils/utils.mjs +67 -65
- package/agents/generate/document-structure-tools/generate-sub-structure.mjs +0 -131
- package/agents/generate/generate-structure-without-tools.yaml +0 -65
- package/prompts/common/document/media-handling-rules.md +0 -9
|
@@ -4,11 +4,11 @@ import path from "node:path";
|
|
|
4
4
|
import imageSize from "image-size";
|
|
5
5
|
import {
|
|
6
6
|
buildSourcesContent,
|
|
7
|
-
calculateFileStats,
|
|
8
7
|
loadFilesFromPaths,
|
|
9
8
|
readFileContents,
|
|
10
9
|
getMimeType,
|
|
11
|
-
|
|
10
|
+
isRemoteFile,
|
|
11
|
+
calculateTokens,
|
|
12
12
|
} from "../../utils/file-utils.mjs";
|
|
13
13
|
import {
|
|
14
14
|
getCurrentGitHead,
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
DEFAULT_INCLUDE_PATTERNS,
|
|
22
22
|
} from "../../utils/constants/index.mjs";
|
|
23
23
|
import { isOpenAPISpecFile } from "../../utils/openapi/index.mjs";
|
|
24
|
+
import { loadDocumentStructure } from "../../utils/docs-finder-utils.mjs";
|
|
24
25
|
|
|
25
26
|
export default async function loadSources(
|
|
26
27
|
{
|
|
@@ -144,7 +145,7 @@ export default async function loadSources(
|
|
|
144
145
|
files.map(async (file) => {
|
|
145
146
|
const ext = path.extname(file).toLowerCase();
|
|
146
147
|
|
|
147
|
-
if (mediaExtensions.includes(ext) && !
|
|
148
|
+
if (mediaExtensions.includes(ext) && !isRemoteFile(file)) {
|
|
148
149
|
// This is a media file
|
|
149
150
|
const relativePath = path.relative(docsDir, file);
|
|
150
151
|
const fileName = path.basename(file);
|
|
@@ -170,7 +171,7 @@ export default async function loadSources(
|
|
|
170
171
|
if (dimensions.width < minImageWidth) {
|
|
171
172
|
filteredImageCount++;
|
|
172
173
|
console.log(
|
|
173
|
-
`
|
|
174
|
+
`Ignored image: ${fileName} (${dimensions.width}x${dimensions.height}px < ${minImageWidth}px minimum)`,
|
|
174
175
|
);
|
|
175
176
|
return;
|
|
176
177
|
}
|
|
@@ -195,13 +196,10 @@ export default async function loadSources(
|
|
|
195
196
|
}
|
|
196
197
|
|
|
197
198
|
// Read all source files using the utility function
|
|
198
|
-
let sourceFiles = await readFileContents(sourceFilesPaths, process.cwd())
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
// check if totalTokens is too large
|
|
204
|
-
const isLargeContext = totalTokens > INTELLIGENT_SUGGESTION_TOKEN_THRESHOLD;
|
|
199
|
+
let sourceFiles = (await readFileContents(sourceFilesPaths, process.cwd())).map((i) => ({
|
|
200
|
+
...i,
|
|
201
|
+
tokens: calculateTokens(`\n${i.sourceId}\n${i.content}`),
|
|
202
|
+
}));
|
|
205
203
|
|
|
206
204
|
// filter OpenAPI doc should after check isLargeContext
|
|
207
205
|
sourceFiles = sourceFiles.filter((file) => {
|
|
@@ -214,48 +212,32 @@ export default async function loadSources(
|
|
|
214
212
|
return !isOpenAPI;
|
|
215
213
|
});
|
|
216
214
|
|
|
217
|
-
const
|
|
215
|
+
const totalTokens = sourceFiles.reduce((sum, file) => sum + file.tokens, 0);
|
|
216
|
+
const totalLines = sourceFiles.reduce(
|
|
217
|
+
(sum, file) => sum + file.content.split("\n").filter(Boolean).length,
|
|
218
|
+
0,
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
const dataSources = splitSourcesToChunks(sourceFiles, INTELLIGENT_SUGGESTION_TOKEN_THRESHOLD).map(
|
|
222
|
+
(i) => ({ dataSourceChunk: buildSourcesContent(i) }),
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
const remoteFileList = [];
|
|
218
226
|
|
|
219
227
|
sourceFiles.forEach((file) => {
|
|
220
|
-
if (
|
|
221
|
-
|
|
228
|
+
if (isRemoteFile(file.sourceId)) {
|
|
229
|
+
remoteFileList.push(file);
|
|
222
230
|
}
|
|
223
231
|
});
|
|
224
232
|
if (options?.context?.userContext) {
|
|
225
|
-
options.context.userContext.
|
|
233
|
+
options.context.userContext.remoteFileList = remoteFileList;
|
|
226
234
|
}
|
|
227
235
|
|
|
228
|
-
// Build allSources string using utility function
|
|
229
|
-
const allSources = buildSourcesContent(sourceFiles, isLargeContext);
|
|
230
236
|
// all files path
|
|
231
237
|
const allFilesPaths = sourceFiles.map((x) => `- ${toRelativePath(x.sourceId)}`).join("\n");
|
|
232
238
|
|
|
233
239
|
// Get the last documentation structure
|
|
234
|
-
|
|
235
|
-
if (outputDir) {
|
|
236
|
-
const documentStructurePath = path.join(outputDir, "structure-plan.json");
|
|
237
|
-
try {
|
|
238
|
-
const documentExecutionStructure = await readFile(documentStructurePath, "utf8");
|
|
239
|
-
if (documentExecutionStructure?.trim()) {
|
|
240
|
-
try {
|
|
241
|
-
// Validate that the content looks like JSON before parsing
|
|
242
|
-
const trimmedContent = documentExecutionStructure.trim();
|
|
243
|
-
if (trimmedContent.startsWith("{") || trimmedContent.startsWith("[")) {
|
|
244
|
-
originalDocumentStructure = JSON.parse(documentExecutionStructure);
|
|
245
|
-
} else {
|
|
246
|
-
console.warn(`structure-plan.json contains non-JSON content, skipping parse`);
|
|
247
|
-
}
|
|
248
|
-
} catch (err) {
|
|
249
|
-
console.error(`Failed to parse structure-plan.json: ${err.message}`);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
} catch (err) {
|
|
253
|
-
if (err.code !== "ENOENT") {
|
|
254
|
-
console.warn(`Error reading structure-plan.json: ${err.message}`);
|
|
255
|
-
}
|
|
256
|
-
// The file does not exist or is not readable, originalDocumentStructure remains undefined
|
|
257
|
-
}
|
|
258
|
-
}
|
|
240
|
+
const originalDocumentStructure = await loadDocumentStructure(outputDir);
|
|
259
241
|
|
|
260
242
|
// Get the last output result of the specified path
|
|
261
243
|
let content;
|
|
@@ -308,7 +290,7 @@ export default async function loadSources(
|
|
|
308
290
|
}
|
|
309
291
|
|
|
310
292
|
return {
|
|
311
|
-
|
|
293
|
+
dataSources,
|
|
312
294
|
content,
|
|
313
295
|
originalDocumentStructure,
|
|
314
296
|
files,
|
|
@@ -316,7 +298,6 @@ export default async function loadSources(
|
|
|
316
298
|
totalTokens,
|
|
317
299
|
totalLines,
|
|
318
300
|
mediaFiles,
|
|
319
|
-
isLargeContext,
|
|
320
301
|
allFilesPaths,
|
|
321
302
|
};
|
|
322
303
|
}
|
|
@@ -364,8 +345,14 @@ loadSources.input_schema = {
|
|
|
364
345
|
loadSources.output_schema = {
|
|
365
346
|
type: "object",
|
|
366
347
|
properties: {
|
|
367
|
-
|
|
368
|
-
type: "
|
|
348
|
+
dataSources: {
|
|
349
|
+
type: "array",
|
|
350
|
+
items: {
|
|
351
|
+
type: "object",
|
|
352
|
+
properties: {
|
|
353
|
+
dataSourceChunk: { type: "string" },
|
|
354
|
+
},
|
|
355
|
+
},
|
|
369
356
|
},
|
|
370
357
|
files: {
|
|
371
358
|
type: "array",
|
|
@@ -396,3 +383,33 @@ loadSources.output_schema = {
|
|
|
396
383
|
};
|
|
397
384
|
|
|
398
385
|
loadSources.task_render_mode = "hide";
|
|
386
|
+
|
|
387
|
+
function splitSourcesToChunks(sources, maxTokens) {
|
|
388
|
+
const chunks = [];
|
|
389
|
+
|
|
390
|
+
let currentChunk = [];
|
|
391
|
+
let currentTokens = 0;
|
|
392
|
+
|
|
393
|
+
for (const source of sources) {
|
|
394
|
+
const sourceTokens = source.tokens;
|
|
395
|
+
|
|
396
|
+
if (currentTokens + sourceTokens > maxTokens) {
|
|
397
|
+
// Start a new chunk
|
|
398
|
+
if (currentChunk.length > 0) {
|
|
399
|
+
chunks.push(currentChunk);
|
|
400
|
+
}
|
|
401
|
+
currentChunk = [source];
|
|
402
|
+
currentTokens = sourceTokens;
|
|
403
|
+
} else {
|
|
404
|
+
// Add to current chunk
|
|
405
|
+
currentChunk.push(source);
|
|
406
|
+
currentTokens += sourceTokens;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (currentChunk.length > 0) {
|
|
411
|
+
chunks.push(currentChunk);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
return chunks;
|
|
415
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readdir, unlink
|
|
1
|
+
import { readdir, unlink } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { shutdownMermaidWorkerPool } from "../../utils/mermaid-worker-pool.mjs";
|
|
4
4
|
import { getCurrentGitHead, saveGitHeadToConfig } from "../../utils/utils.mjs";
|
|
@@ -10,7 +10,7 @@ import { getCurrentGitHead, saveGitHeadToConfig } from "../../utils/utils.mjs";
|
|
|
10
10
|
* @param {Array<string>} [params.translateLanguages] - Translation languages
|
|
11
11
|
* @returns {Promise<Array<{ path: string, success: boolean, error?: string }>>}
|
|
12
12
|
*/
|
|
13
|
-
export default async function
|
|
13
|
+
export default async function postGenerate({
|
|
14
14
|
documentExecutionStructure: documentStructure,
|
|
15
15
|
docsDir,
|
|
16
16
|
translateLanguages = [],
|
|
@@ -26,15 +26,6 @@ export default async function saveDocs({
|
|
|
26
26
|
console.warn("Failed to save git HEAD:", err.message);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
// Generate _sidebar.md
|
|
30
|
-
try {
|
|
31
|
-
const sidebar = generateSidebar(documentStructure);
|
|
32
|
-
const sidebarPath = join(docsDir, "_sidebar.md");
|
|
33
|
-
await writeFile(sidebarPath, sidebar, "utf8");
|
|
34
|
-
} catch (err) {
|
|
35
|
-
console.error("Failed to save _sidebar.md:", err.message);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
29
|
// Clean up invalid .md files that are no longer in the documentation structure
|
|
39
30
|
try {
|
|
40
31
|
await cleanupInvalidFiles(documentStructure, docsDir, translateLanguages, locale);
|
|
@@ -140,43 +131,3 @@ async function cleanupInvalidFiles(documentStructure, docsDir, translateLanguage
|
|
|
140
131
|
}
|
|
141
132
|
|
|
142
133
|
// Generate sidebar content, support nested structure, and the order is consistent with documentStructure
|
|
143
|
-
function generateSidebar(documentStructure) {
|
|
144
|
-
// Build tree structure
|
|
145
|
-
const root = {};
|
|
146
|
-
for (const { path, title, parentId } of documentStructure) {
|
|
147
|
-
const relPath = path.replace(/^\//, "");
|
|
148
|
-
const segments = relPath.split("/");
|
|
149
|
-
let node = root;
|
|
150
|
-
for (let i = 0; i < segments.length; i++) {
|
|
151
|
-
const seg = segments[i];
|
|
152
|
-
if (!node[seg])
|
|
153
|
-
node[seg] = {
|
|
154
|
-
__children: {},
|
|
155
|
-
__title: null,
|
|
156
|
-
__fullPath: segments.slice(0, i + 1).join("/"),
|
|
157
|
-
__parentId: parentId,
|
|
158
|
-
};
|
|
159
|
-
if (i === segments.length - 1) node[seg].__title = title;
|
|
160
|
-
node = node[seg].__children;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
// Recursively generate sidebar text, the link path is the flattened file name
|
|
164
|
-
function walk(node, parentSegments = [], indent = "") {
|
|
165
|
-
let out = "";
|
|
166
|
-
for (const key of Object.keys(node)) {
|
|
167
|
-
const item = node[key];
|
|
168
|
-
const fullSegments = [...parentSegments, key];
|
|
169
|
-
const flatFile = `${fullSegments.join("-")}.md`;
|
|
170
|
-
if (item.__title) {
|
|
171
|
-
const realIndent = item.__parentId === null ? "" : indent;
|
|
172
|
-
out += `${realIndent}* [${item.__title}](/${flatFile})\n`;
|
|
173
|
-
}
|
|
174
|
-
const children = item.__children;
|
|
175
|
-
if (Object.keys(children).length > 0) {
|
|
176
|
-
out += walk(children, fullSegments, `${indent} `);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
return out;
|
|
180
|
-
}
|
|
181
|
-
return walk(root).replace(/\n+$/, "");
|
|
182
|
-
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { saveDocTranslation as _saveDocTranslation } from "../../utils/utils.mjs";
|
|
2
|
+
|
|
3
|
+
export default async function saveDocTranslation({
|
|
4
|
+
path,
|
|
5
|
+
docsDir,
|
|
6
|
+
translation,
|
|
7
|
+
language,
|
|
8
|
+
labels,
|
|
9
|
+
isShowMessage = false,
|
|
10
|
+
}) {
|
|
11
|
+
await _saveDocTranslation({
|
|
12
|
+
path,
|
|
13
|
+
docsDir,
|
|
14
|
+
language,
|
|
15
|
+
translation,
|
|
16
|
+
labels,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
if (isShowMessage) {
|
|
20
|
+
const message = `✅ Translation completed successfully.`;
|
|
21
|
+
return { message };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
saveDocTranslation.task_render_mode = "hide";
|
|
@@ -1,24 +1,22 @@
|
|
|
1
1
|
import { shutdownMermaidWorkerPool } from "../../utils/mermaid-worker-pool.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { saveDoc as _saveDoc } from "../../utils/utils.mjs";
|
|
3
3
|
|
|
4
|
-
export default async function
|
|
4
|
+
export default async function saveDoc({
|
|
5
5
|
path,
|
|
6
6
|
content,
|
|
7
7
|
docsDir,
|
|
8
|
-
translates,
|
|
9
8
|
labels,
|
|
10
9
|
locale,
|
|
11
|
-
|
|
10
|
+
feedback,
|
|
12
11
|
isShowMessage = false,
|
|
12
|
+
...rest
|
|
13
13
|
}) {
|
|
14
|
-
|
|
14
|
+
await _saveDoc({
|
|
15
15
|
path,
|
|
16
16
|
content,
|
|
17
17
|
docsDir,
|
|
18
|
-
translates,
|
|
19
18
|
labels,
|
|
20
19
|
locale,
|
|
21
|
-
isTranslate,
|
|
22
20
|
});
|
|
23
21
|
|
|
24
22
|
if (isShowMessage) {
|
|
@@ -29,13 +27,20 @@ export default async function saveSingleDoc({
|
|
|
29
27
|
console.warn("Failed to shutdown mermaid worker pool:", error.message);
|
|
30
28
|
}
|
|
31
29
|
|
|
32
|
-
const message =
|
|
33
|
-
? `✅ Translation completed successfully`
|
|
34
|
-
: `✅ Document updated successfully`;
|
|
30
|
+
const message = `✅ Document updated successfully.`;
|
|
35
31
|
return { message };
|
|
36
32
|
}
|
|
37
33
|
|
|
38
|
-
return {
|
|
34
|
+
return {
|
|
35
|
+
path,
|
|
36
|
+
content,
|
|
37
|
+
docsDir,
|
|
38
|
+
labels,
|
|
39
|
+
locale,
|
|
40
|
+
feedback,
|
|
41
|
+
isShowMessage,
|
|
42
|
+
...rest,
|
|
43
|
+
};
|
|
39
44
|
}
|
|
40
45
|
|
|
41
|
-
|
|
46
|
+
saveDoc.task_render_mode = "hide";
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import fs from "fs-extra";
|
|
3
|
+
import { buildDocumentTree } from "../../utils/docs-finder-utils.mjs";
|
|
4
|
+
|
|
5
|
+
export default async function saveSidebar({ documentStructure, docsDir }) {
|
|
6
|
+
// Generate _sidebar.md
|
|
7
|
+
try {
|
|
8
|
+
const sidebar = generateSidebar(documentStructure);
|
|
9
|
+
const sidebarPath = join(docsDir, "_sidebar.md");
|
|
10
|
+
|
|
11
|
+
await fs.ensureDir(docsDir);
|
|
12
|
+
await fs.writeFile(sidebarPath, sidebar, "utf8");
|
|
13
|
+
} catch (err) {
|
|
14
|
+
console.error("Failed to save _sidebar.md:", err.message);
|
|
15
|
+
}
|
|
16
|
+
return {};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Recursively generate sidebar text, the link path is the flattened file name
|
|
20
|
+
function walk(nodes, indent = "") {
|
|
21
|
+
let out = "";
|
|
22
|
+
for (const node of nodes) {
|
|
23
|
+
const relPath = node.path.replace(/^\//, "");
|
|
24
|
+
const flatFile = `${relPath.split("/").join("-")}.md`;
|
|
25
|
+
const realIndent = node.parentId === null ? "" : indent;
|
|
26
|
+
out += `${realIndent}* [${node.title}](/${flatFile})\n`;
|
|
27
|
+
|
|
28
|
+
if (node.children && node.children.length > 0) {
|
|
29
|
+
out += walk(node.children, `${indent} `);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function generateSidebar(documentStructure) {
|
|
36
|
+
const { rootNodes } = buildDocumentTree(documentStructure);
|
|
37
|
+
return walk(rootNodes).replace(/\n+$/, "");
|
|
38
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
+
import { isRemoteFile } from "../../utils/file-utils.mjs";
|
|
2
3
|
import { normalizePath, toRelativePath } from "../../utils/utils.mjs";
|
|
3
|
-
import { checkIsRemoteFile } from "../../utils/file-utils.mjs";
|
|
4
4
|
|
|
5
|
-
export default function
|
|
5
|
+
export default function transformDetailDataSource({ sourceIds }, options = {}) {
|
|
6
6
|
// Read file content for each sourceId, ignoring failures
|
|
7
7
|
let openAPISpec;
|
|
8
|
-
const
|
|
8
|
+
const remoteFileList = options?.context?.userContext?.remoteFileList || [];
|
|
9
9
|
const contents = (sourceIds || [])
|
|
10
10
|
.filter((id) => {
|
|
11
11
|
const openApiSourceId = options?.context?.userContext?.openAPISpec?.sourceId;
|
|
@@ -17,8 +17,8 @@ export default function transformDetailDatasources({ sourceIds }, options = {})
|
|
|
17
17
|
})
|
|
18
18
|
.map((id) => {
|
|
19
19
|
try {
|
|
20
|
-
if (
|
|
21
|
-
const findFile =
|
|
20
|
+
if (isRemoteFile(id)) {
|
|
21
|
+
const findFile = remoteFileList.find((f) => f.sourceId === id);
|
|
22
22
|
if (findFile) {
|
|
23
23
|
return `// sourceId: ${id}\n${findFile.content}\n`;
|
|
24
24
|
}
|
|
@@ -37,9 +37,9 @@ export default function transformDetailDatasources({ sourceIds }, options = {})
|
|
|
37
37
|
.filter(Boolean);
|
|
38
38
|
|
|
39
39
|
return {
|
|
40
|
-
|
|
40
|
+
detailDataSource: contents.join(""),
|
|
41
41
|
openAPISpec,
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
transformDetailDataSource.task_render_mode = "hide";
|
package/aigne.yaml
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env aigne
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
model:
|
|
4
|
+
model: aignehub/gemini-2.5-pro # reasoning_effort 128-32768
|
|
5
|
+
# https://github.com/AIGNE-io/aigne-framework/blob/main/models/gemini/src/gemini-chat-model.ts#L115
|
|
6
|
+
reasoning_effort: 502
|
|
6
7
|
# name: gemini-2.5-flash
|
|
7
8
|
temperature: 0.8
|
|
8
9
|
agents:
|
|
@@ -11,14 +12,13 @@ agents:
|
|
|
11
12
|
|
|
12
13
|
# Documentation Structure Generation
|
|
13
14
|
- ./agents/generate/generate-structure.yaml
|
|
14
|
-
- ./agents/generate/generate-structure-without-tools.yaml
|
|
15
15
|
- ./agents/generate/update-document-structure.yaml
|
|
16
16
|
- ./agents/generate/check-need-generate-structure.mjs
|
|
17
17
|
- ./agents/generate/refine-document-structure.yaml
|
|
18
18
|
- ./agents/generate/check-document-structure.yaml
|
|
19
19
|
- ./agents/generate/user-review-document-structure.mjs
|
|
20
20
|
- ./agents/generate/index.yaml
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
# Documentation Structure Tools
|
|
23
23
|
- ./agents/generate/document-structure-tools/add-document.mjs
|
|
24
24
|
- ./agents/generate/document-structure-tools/delete-document.mjs
|
|
@@ -47,6 +47,7 @@ agents:
|
|
|
47
47
|
|
|
48
48
|
# Publishing
|
|
49
49
|
- ./agents/publish/publish-docs.mjs
|
|
50
|
+
- ./agents/publish/translate-meta.mjs
|
|
50
51
|
- ./agents/publish/index.yaml
|
|
51
52
|
|
|
52
53
|
# Media
|
|
@@ -65,9 +66,11 @@ agents:
|
|
|
65
66
|
|
|
66
67
|
# Utilities
|
|
67
68
|
- ./agents/utils/load-sources.mjs
|
|
68
|
-
- ./agents/utils/
|
|
69
|
-
- ./agents/utils/
|
|
70
|
-
- ./agents/utils/
|
|
69
|
+
- ./agents/utils/post-generate.mjs
|
|
70
|
+
- ./agents/utils/save-sidebar.mjs
|
|
71
|
+
- ./agents/utils/transform-detail-data-sources.mjs
|
|
72
|
+
- ./agents/utils/save-doc.mjs
|
|
73
|
+
- ./agents/utils/save-doc-translation.mjs
|
|
71
74
|
- ./agents/utils/save-output.mjs
|
|
72
75
|
- ./agents/utils/format-document-structure.mjs
|
|
73
76
|
- ./agents/utils/find-item-by-path.mjs
|
|
@@ -92,6 +95,11 @@ agents:
|
|
|
92
95
|
- ./agents/evaluate/document-structure.yaml
|
|
93
96
|
- ./agents/evaluate/document.yaml
|
|
94
97
|
- ./agents/evaluate/code-snippet.mjs
|
|
98
|
+
|
|
99
|
+
# Diagram
|
|
100
|
+
- ./agents/generate/merge-diagram.yaml
|
|
101
|
+
- ./agents/update/generate-diagram.yaml
|
|
102
|
+
- ./agents/update/check-generate-diagram.mjs
|
|
95
103
|
cli:
|
|
96
104
|
chat: ./agents/chat/index.yaml
|
|
97
105
|
agents:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aigne/doc-smith",
|
|
3
|
-
"version": "0.8.15-beta.
|
|
3
|
+
"version": "0.8.15-beta.11",
|
|
4
4
|
"description": "AI-driven documentation generation tool built on the AIGNE Framework",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"marked": "^15.0.12",
|
|
49
49
|
"marked-terminal": "^7.3.0",
|
|
50
50
|
"mermaid": "^11.9.0",
|
|
51
|
+
"minimatch": "^10.1.1",
|
|
51
52
|
"open": "^10.2.0",
|
|
52
53
|
"p-limit": "^7.1.1",
|
|
53
54
|
"p-map": "^7.0.3",
|
|
@@ -56,6 +57,7 @@
|
|
|
56
57
|
"remark-lint": "^10.0.1",
|
|
57
58
|
"remark-parse": "^11.0.0",
|
|
58
59
|
"terminal-link": "^4.0.0",
|
|
60
|
+
"typescript": "^5.9.3",
|
|
59
61
|
"ufo": "^1.6.1",
|
|
60
62
|
"unified": "^11.0.5",
|
|
61
63
|
"unist-util-visit": "^5.0.0",
|
|
@@ -2,12 +2,12 @@ Target Audience: {{targetAudience}}
|
|
|
2
2
|
|
|
3
3
|
Content Generation Rules:
|
|
4
4
|
|
|
5
|
-
- Use only information from
|
|
5
|
+
- Use only information from `<detail_data_source>`, never fabricate or supplement content not present in the sources
|
|
6
6
|
- Combine the current {{nodeName}} title and description to create a well-structured content plan that is rich, organized, and engaging
|
|
7
7
|
- Content style must match the target audience
|
|
8
|
-
- Clearly differentiate content from other {{nodeName}} items in the
|
|
8
|
+
- Clearly differentiate content from other {{nodeName}} items in the `<document_structure>` to avoid duplication and highlight this {{nodeName}}'s unique value
|
|
9
9
|
{% if enforceInfoCompleteness %}
|
|
10
|
-
- If
|
|
10
|
+
- If `<detail_data_source>` lack sufficient information, return an error message requesting users to provide additional content. Ensure page content is sufficiently rich, don't hesitate to ask users for supplementary information
|
|
11
11
|
- Display only valuable, engaging information. If information is insufficient, prompt users to provide more details
|
|
12
12
|
{% endif %}
|
|
13
13
|
- Output complete information including all content planned for the {{nodeName}}
|
|
@@ -15,6 +15,6 @@ Content Generation Rules:
|
|
|
15
15
|
- Maintain a strict, sequential heading hierarchy; no skipping (e.g., no jump from level-1 to level-3).
|
|
16
16
|
- Format markdown output with proper line breaks and spacing for easy reading
|
|
17
17
|
- For list data with many items, prioritize using markdown table for cleaner, more readable presentation
|
|
18
|
-
- Do not mention
|
|
19
|
-
- Do not include file paths from
|
|
20
|
-
- Avoid phrases like 'current {{nodeName}}'
|
|
18
|
+
- Do not mention `<detail_data_source>` in output, your content is for user consumption, and users are unaware of detailDataSource
|
|
19
|
+
- Do not include file paths from `<data_sources>` in output as they are meaningless to users
|
|
20
|
+
- Avoid phrases like 'current {{nodeName}}'
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<media_file_list_usage_rules>
|
|
2
|
+
|
|
3
|
+
**Usage Workflow**
|
|
4
|
+
1. Read the `<media_file_list>` data and take note of each file's `path` and `description` as references.
|
|
5
|
+
2. Combine those descriptions with the current document's content to decide which images should be used and where they should be inserted.
|
|
6
|
+
3. Confirm that every inserted image path comes from `<media_file_list>`. If a path is missing from that list, replace it with one that is included.
|
|
7
|
+
|
|
8
|
+
**Usage Requirements**
|
|
9
|
+
- Insert images with Markdown syntax: ``.
|
|
10
|
+
- Never invent, reinterpret, fabricate, normalize, or rewrite any media file path under any circumstances.
|
|
11
|
+
|
|
12
|
+
</media_file_list_usage_rules>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{% if openAPISpec %}
|
|
2
|
+
<openapi_usage_rules>
|
|
3
|
+
|
|
4
|
+
**Goal:** Use the provided OpenAPI (Swagger) specification, align it with the current page objective, and leverage it to refine this document.
|
|
5
|
+
|
|
6
|
+
**OpenAPI File Content:**
|
|
7
|
+
<openapi_doc>
|
|
8
|
+
|
|
9
|
+
{{ openAPISpec }}
|
|
10
|
+
|
|
11
|
+
</openapi_doc>
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
### **Documentation Requirements and Constraints**
|
|
16
|
+
|
|
17
|
+
1. **Extract the core content:**
|
|
18
|
+
* Organize the document by functional modules.
|
|
19
|
+
* For each path item, include the following elements:
|
|
20
|
+
* HTTP method and path.
|
|
21
|
+
* Concise summary.
|
|
22
|
+
* Detailed description.
|
|
23
|
+
* Request parameters: name, location (`in`), type, required flag, description.
|
|
24
|
+
* Request body: describe its structure when present.
|
|
25
|
+
* Responses: at least the key status codes (e.g., 200, 201, 400, 500) and their schemas.
|
|
26
|
+
|
|
27
|
+
2. **Mandatory API description constraints (deduplication rule):**
|
|
28
|
+
* **Ensure that throughout the document (including preface, overview, etc.), any introduction to the project APIs appears only within this OpenAPI-generated "API reference" section.**
|
|
29
|
+
* **Never** repeat or expand the interface list elsewhere in the document (for example, "Quick Start" or "Architecture Overview" sections).
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
**Expected output format:** A concise, clear, and easy-to-scan Markdown document.
|
|
34
|
+
|
|
35
|
+
</openapi_usage_rules>
|
|
36
|
+
{% endif %}
|
|
@@ -13,5 +13,4 @@ Your key strengths include:
|
|
|
13
13
|
1. **Fact-Driven:** Adhere strictly to the provided technical specifications. Do not infer or embellish information.
|
|
14
14
|
2. **Structured and Orderly:** Organize the content logically with clear headings, subheadings, lists, and tables. Present information sequentially where appropriate (e.g., installation steps).
|
|
15
15
|
3. **Clarity and Precision:** Use precise, unambiguous language. Define technical terms clearly. Avoid marketing jargon or emotionally charged words.
|
|
16
|
-
|
|
17
|
-
5. **Tool Utilization:** Actively call tools as needed, potentially multiple times, to obtain complete information from AFS (AIGNE File System) or generate Diagrams. Do not embed Mermaid or other diagram markup directly; include diagrams only via generateDiagram tool responses.
|
|
16
|
+
5. **Tool Utilization:** Actively call tools as needed, potentially multiple times, to obtain complete information from AFS (AIGNE File System).
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<conflict_resolution_guidance>
|
|
2
|
-
When users select potentially conflicting options, conflict resolution guidance will be provided in user_rules
|
|
2
|
+
When users select potentially conflicting options, conflict resolution guidance will be provided in `<user_rules>`. Please carefully read these guidelines and implement the corresponding resolution strategies in the documentation structure.
|
|
3
3
|
|
|
4
4
|
Core principles for conflict resolution:
|
|
5
5
|
1. **Layered need satisfaction**: Simultaneously satisfy multiple purposes and audiences through reasonable documentation structure hierarchy
|
|
@@ -13,4 +13,4 @@ Common conflict resolution patterns:
|
|
|
13
13
|
- **Depth conflicts**: Adopt progressive structures that allow users to choose appropriate depth levels
|
|
14
14
|
|
|
15
15
|
When generating documentation structure, prioritize conflict resolution strategies to ensure the final structure can harmoniously satisfy all user needs.
|
|
16
|
-
</conflict_resolution_guidance>
|
|
16
|
+
</conflict_resolution_guidance>
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
<document_structure_rules>
|
|
2
2
|
The target audience for this document is: {{targetAudience}}
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
1. When planning the structure, reasonably organize and display all information from
|
|
6
|
-
2. Users may provide limited
|
|
7
|
-
3. For information provided in user
|
|
8
|
-
4. If
|
|
4
|
+
`<data_sources>` usage rules:
|
|
5
|
+
1. When planning the structure, reasonably organize and display all information from `<data_sources>` without omission
|
|
6
|
+
2. Users may provide limited `<data_sources>`. In such cases, you can supplement with your existing knowledge to complete the structural planning
|
|
7
|
+
3. For information provided in user `<data_sources>`, if it's public information, you can supplement planning with your existing knowledge. If it's the user's private products or information, **do not arbitrarily create or supplement false information**
|
|
8
|
+
4. If `<data_sources>` don't match the target audience, you need to reframe the `<data_sources>` to match the target audience
|
|
9
9
|
|
|
10
10
|
Structural planning rules:
|
|
11
11
|
|
|
12
12
|
1. {{nodeName}} planning should prioritize user-specified rules, especially requirements like "number of {{nodeName}}", "must include xxx {{nodeName}}", "cannot include xxx {{nodeName}}"
|
|
13
13
|
2. {{nodeName}} planning should display as much information as possible from the user-provided context
|
|
14
14
|
3. Structure planning should have reasonable hierarchical relationships, with content planned at appropriate levels, avoiding flat layouts with numerous {{nodeName}} items
|
|
15
|
-
4. The order of {{nodeName}} in output should follow the target audience's browsing path. It doesn't need to follow the exact order in
|
|
15
|
+
4. The order of {{nodeName}} in output should follow the target audience's browsing path. It doesn't need to follow the exact order in `<data_sources>` progress from simple to advanced, from understanding to exploration, with reasonable pathways
|
|
16
16
|
5. Each {{nodeName}} should have a clear content plan and must not duplicate content from other {{nodeName}} items
|
|
17
17
|
6. Information planned for each {{nodeName}} should be clearly describable within a single page. If there's too much information to display or the concepts are too broad, consider splitting into sub-{{nodeName}} items
|
|
18
18
|
7. If previous documentation structure and user feedback are provided, make only necessary modifications based on user feedback without major changes
|
|
@@ -26,7 +26,7 @@ Structural planning rules:
|
|
|
26
26
|
- Title
|
|
27
27
|
- Description of the important information this {{nodeName}} plans to display, with descriptions tailored to the target audience
|
|
28
28
|
|
|
29
|
-
2. Content planning should prioritize displaying information from user-provided
|
|
29
|
+
2. Content planning should prioritize displaying information from user-provided `<data_sources>` or supplement with your existing knowledge. Do not arbitrarily fabricate information.
|
|
30
30
|
|
|
31
31
|
{% ifAsync docsType == 'general' %}
|
|
32
32
|
{% include "../../structure/document-rules.md" %}
|
|
@@ -41,4 +41,4 @@ Other requirements:
|
|
|
41
41
|
|
|
42
42
|
1. Must satisfy user specified rules
|
|
43
43
|
2. Return information using the user's language {{locale}}
|
|
44
|
-
</document_structure_rules>
|
|
44
|
+
</document_structure_rules>
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<output_constraints>
|
|
2
2
|
|
|
3
|
-
1. Associated sourceIds should be as comprehensive as possible. You can include as many related
|
|
4
|
-
- If
|
|
3
|
+
1. Associated sourceIds should be as comprehensive as possible. You can include as many related `<data_sources>` as possible.
|
|
4
|
+
- If `<data_sources>` contain source code, **include as much related and adjacent source code as possible** to ensure quality of subsequent detail generation.
|
|
5
5
|
- First identify the most relevant source code files, then analyze the source code referenced within them. Referenced file paths, referenced files, and files in referenced paths all need to be included in sourceIds
|
|
6
6
|
- For referenced files, analyze another layer of source code files referenced within them and add to sourceIds to ensure complete context for detail generation
|
|
7
7
|
2. **Ensure sourceIds are never empty**. Do not plan {{nodeName}} items without related data sources
|
|
8
8
|
|
|
9
|
-
</output_constraints>
|
|
9
|
+
</output_constraints>
|