@aigne/doc-smith 0.8.15-beta.1 → 0.8.15-beta.10
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 +76 -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 +1 -0
- package/agents/generate/utils/merge-document-structures.mjs +30 -0
- package/agents/init/check.mjs +3 -1
- 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 +26 -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 +2 -1
- package/agents/utils/load-sources.mjs +62 -45
- 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 +59 -0
- package/agents/utils/{transform-detail-datasources.mjs → transform-detail-data-sources.mjs} +7 -7
- package/aigne.yaml +16 -8
- package/package.json +2 -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 +68 -29
- package/prompts/structure/review/structure-review-system.md +79 -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 +1 -1
- package/types/document-structure-schema.mjs +3 -3
- package/utils/docs-finder-utils.mjs +48 -0
- package/utils/extract-api.mjs +32 -0
- package/utils/file-utils.mjs +56 -101
- package/utils/history-utils.mjs +20 -8
- package/utils/load-config.mjs +1 -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
|
@@ -2,7 +2,10 @@ 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 fs from "fs-extra";
|
|
6
|
+
import pMap from "p-map";
|
|
5
7
|
|
|
8
|
+
import { getFileName } from "../../utils/utils.mjs";
|
|
6
9
|
import checkDetailResult from "../utils/check-detail-result.mjs";
|
|
7
10
|
|
|
8
11
|
// Get current script directory
|
|
@@ -18,13 +21,13 @@ export default async function checkDocument(
|
|
|
18
21
|
modifiedFiles,
|
|
19
22
|
forceRegenerate,
|
|
20
23
|
locale,
|
|
24
|
+
translates,
|
|
21
25
|
...rest
|
|
22
26
|
},
|
|
23
27
|
options,
|
|
24
28
|
) {
|
|
25
29
|
// Check if the detail file already exists
|
|
26
|
-
const
|
|
27
|
-
const fileFullName = locale === "en" ? `${flatName}.md` : `${flatName}.${locale}.md`;
|
|
30
|
+
const fileFullName = getFileName(path, locale);
|
|
28
31
|
const filePath = join(docsDir, fileFullName);
|
|
29
32
|
let detailGenerated = true;
|
|
30
33
|
let fileContent = null;
|
|
@@ -84,24 +87,42 @@ export default async function checkDocument(
|
|
|
84
87
|
contentValidationFailed = true;
|
|
85
88
|
}
|
|
86
89
|
}
|
|
90
|
+
const languages = translates.map((x) => x.language);
|
|
91
|
+
const lackLanguages = new Set(languages);
|
|
92
|
+
const skills = [];
|
|
87
93
|
|
|
88
94
|
// If file exists, sourceIds haven't changed, source files haven't changed, and content validation passes, no need to regenerate
|
|
89
95
|
if (detailGenerated && !sourceIdsChanged && !contentValidationFailed && !forceRegenerate) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
+
await pMap(
|
|
97
|
+
languages,
|
|
98
|
+
async (x) => {
|
|
99
|
+
const languageFileName = getFileName(path, x);
|
|
100
|
+
const languageFilePath = join(docsDir, languageFileName);
|
|
101
|
+
if (await fs.exists(languageFilePath)) {
|
|
102
|
+
lackLanguages.delete(x);
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
{ concurrency: 10 },
|
|
106
|
+
);
|
|
107
|
+
if (lackLanguages.size === 0) {
|
|
108
|
+
return {
|
|
109
|
+
path,
|
|
110
|
+
docsDir,
|
|
111
|
+
...rest,
|
|
112
|
+
detailGenerated: true,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
// translations during generation don't need feedback, content is satisfactory
|
|
116
|
+
rest.content = fileContent;
|
|
117
|
+
} else {
|
|
118
|
+
skills.push(options.context.agents["handleDocumentUpdate"]);
|
|
96
119
|
}
|
|
97
120
|
|
|
121
|
+
skills.push(options.context.agents["translateMultilingual"]);
|
|
122
|
+
|
|
98
123
|
const teamAgent = TeamAgent.from({
|
|
99
124
|
name: "generateDocument",
|
|
100
|
-
skills
|
|
101
|
-
options.context.agents["handleDocumentUpdate"],
|
|
102
|
-
options.context.agents["translateMultilingual"],
|
|
103
|
-
options.context.agents["saveSingleDoc"],
|
|
104
|
-
],
|
|
125
|
+
skills,
|
|
105
126
|
});
|
|
106
127
|
let openAPISpec = null;
|
|
107
128
|
|
|
@@ -119,6 +140,7 @@ export default async function checkDocument(
|
|
|
119
140
|
|
|
120
141
|
const result = await options.context.invoke(teamAgent, {
|
|
121
142
|
...rest,
|
|
143
|
+
translates: translates.filter((x) => lackLanguages.has(x.language)),
|
|
122
144
|
locale,
|
|
123
145
|
docsDir,
|
|
124
146
|
path,
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const placeholder = "DIAGRAM_PLACEHOLDER";
|
|
2
|
+
|
|
3
|
+
export default async function checkGenerateDiagram(
|
|
4
|
+
{ needDiagram, documentContent, locale },
|
|
5
|
+
options,
|
|
6
|
+
) {
|
|
7
|
+
if (!needDiagram) {
|
|
8
|
+
return {};
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const generateAgent = options.context?.agents?.["generateDiagram"];
|
|
12
|
+
let content = documentContent;
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
const { diagramSourceCode } = await options.context.invoke(generateAgent, {
|
|
16
|
+
documentContent,
|
|
17
|
+
locale,
|
|
18
|
+
});
|
|
19
|
+
content = content.replace(placeholder, diagramSourceCode);
|
|
20
|
+
} catch (error) {
|
|
21
|
+
// FIXME: @zhanghan should regenerate document without diagram
|
|
22
|
+
content = content.replace(placeholder, "");
|
|
23
|
+
console.log(`⚠️ Skip generate any diagram: ${error.message}`);
|
|
24
|
+
}
|
|
25
|
+
return { content };
|
|
26
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
type: team
|
|
2
|
+
task_render_mode: collapse
|
|
3
|
+
name: generateDiagram
|
|
4
|
+
skills:
|
|
5
|
+
- ../generate/draw-diagram.yaml
|
|
6
|
+
- ../generate/wrap-diagram-code.mjs
|
|
7
|
+
reflection:
|
|
8
|
+
reviewer: ../generate/check-diagram.mjs
|
|
9
|
+
is_approved: isValid
|
|
10
|
+
max_iterations: 5
|
|
11
|
+
return_last_on_max_iterations: false
|
|
12
|
+
input_schema:
|
|
13
|
+
type: object
|
|
14
|
+
properties:
|
|
15
|
+
documentContent:
|
|
16
|
+
type: string
|
|
17
|
+
description: The **raw text content** of the current document. (**Note:** This is the original document and **does not include** any diagram source code.)
|
|
18
|
+
locale:
|
|
19
|
+
type: string
|
|
20
|
+
description: Language for diagram labels and text
|
|
21
|
+
default: en
|
|
22
|
+
required:
|
|
23
|
+
- documentContent
|
|
24
|
+
output_schema:
|
|
25
|
+
type: object
|
|
26
|
+
properties:
|
|
27
|
+
diagramSourceCode:
|
|
28
|
+
type: string
|
|
29
|
+
description: The **diagram source code** generated from the input text.
|
|
@@ -16,7 +16,7 @@ input_schema:
|
|
|
16
16
|
locale:
|
|
17
17
|
type: string
|
|
18
18
|
description: User language, such as zh, en
|
|
19
|
-
|
|
19
|
+
detailDataSource:
|
|
20
20
|
type: string
|
|
21
21
|
description: Source data and context for document content generation
|
|
22
22
|
targetAudience:
|
|
@@ -44,9 +44,21 @@ input_schema:
|
|
|
44
44
|
description: Additional supplementary information
|
|
45
45
|
required:
|
|
46
46
|
- rules
|
|
47
|
-
-
|
|
47
|
+
- detailDataSource
|
|
48
48
|
- originalDocumentStructure
|
|
49
|
-
output_key: content
|
|
49
|
+
# output_key: content
|
|
50
|
+
output_schema:
|
|
51
|
+
type: object
|
|
52
|
+
properties:
|
|
53
|
+
content:
|
|
54
|
+
type: string
|
|
55
|
+
description: Document content
|
|
56
|
+
needDiagram:
|
|
57
|
+
type: boolean
|
|
58
|
+
description: Does this document need to generate diagram?
|
|
59
|
+
required:
|
|
60
|
+
- content
|
|
61
|
+
- detailDataSource
|
|
50
62
|
afs:
|
|
51
63
|
modules:
|
|
52
64
|
- module: system-fs
|
|
@@ -57,30 +69,5 @@ afs:
|
|
|
57
69
|
Codebase of the project to be documented used as context for document generation,
|
|
58
70
|
should search and read as needed while generating document content
|
|
59
71
|
keep_text_in_tool_uses: false
|
|
60
|
-
skills:
|
|
61
|
-
|
|
62
|
-
task_render_mode: collapse
|
|
63
|
-
name: generateDiagram
|
|
64
|
-
skills:
|
|
65
|
-
- ../generate/draw-diagram.yaml
|
|
66
|
-
- ../generate/wrap-diagram-code.mjs
|
|
67
|
-
reflection:
|
|
68
|
-
reviewer: ../generate/check-diagram.mjs
|
|
69
|
-
is_approved: isValid
|
|
70
|
-
max_iterations: 5
|
|
71
|
-
return_last_on_max_iterations: false
|
|
72
|
-
custom_error_message: "MUST NOT generate any diagram: validation failed after max iterations."
|
|
73
|
-
input_schema:
|
|
74
|
-
type: object
|
|
75
|
-
properties:
|
|
76
|
-
documentContent:
|
|
77
|
-
type: string
|
|
78
|
-
description: The **raw text content** of the current document. (**Note:** This is the original document and **does not include** any diagram source code.)
|
|
79
|
-
required:
|
|
80
|
-
- documentContent
|
|
81
|
-
output_schema:
|
|
82
|
-
type: object
|
|
83
|
-
properties:
|
|
84
|
-
diagramSourceCode:
|
|
85
|
-
type: string
|
|
86
|
-
description: The **diagram source code** generated from the input text.
|
|
72
|
+
# skills:
|
|
73
|
+
# - ./generate-diagram.yaml
|
|
@@ -2,7 +2,7 @@ type: team
|
|
|
2
2
|
name: handleDocumentUpdate
|
|
3
3
|
description: Update a document in a batch
|
|
4
4
|
skills:
|
|
5
|
-
- ../utils/transform-detail-
|
|
5
|
+
- ../utils/transform-detail-data-sources.mjs
|
|
6
6
|
- type: team
|
|
7
7
|
task_render_mode: collapse
|
|
8
8
|
name: generateDocumentContent
|
|
@@ -23,6 +23,15 @@ skills:
|
|
|
23
23
|
max_iterations: 5
|
|
24
24
|
return_last_on_max_iterations: true
|
|
25
25
|
task_title: Generate document for '{{ title }}'
|
|
26
|
+
- type: transform
|
|
27
|
+
jsonata: |
|
|
28
|
+
$merge([
|
|
29
|
+
$,
|
|
30
|
+
{ "documentContent": content }
|
|
31
|
+
])
|
|
32
|
+
- ./check-generate-diagram.mjs
|
|
33
|
+
# - ../generate/merge-diagram.yaml
|
|
34
|
+
- ../utils/save-doc.mjs
|
|
26
35
|
input_schema:
|
|
27
36
|
type: object
|
|
28
37
|
properties:
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import pMap from "p-map";
|
|
1
2
|
import { recordUpdate } from "../../utils/history-utils.mjs";
|
|
2
3
|
|
|
3
4
|
export default async function saveAndTranslateDocument(input, options) {
|
|
@@ -7,20 +8,15 @@ export default async function saveAndTranslateDocument(input, options) {
|
|
|
7
8
|
return {};
|
|
8
9
|
}
|
|
9
10
|
|
|
10
|
-
//
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
docsDir: docsDir,
|
|
18
|
-
locale: locale,
|
|
19
|
-
translates: translates || doc.translates,
|
|
20
|
-
labels: doc.labels,
|
|
21
|
-
isTranslate: isTranslate,
|
|
11
|
+
// Record history if feedback is provided
|
|
12
|
+
const doc = selectedDocs[0];
|
|
13
|
+
if (doc.feedback?.trim()) {
|
|
14
|
+
recordUpdate({
|
|
15
|
+
operation: "document_update",
|
|
16
|
+
feedback: doc.feedback.trim(),
|
|
17
|
+
docPaths: selectedDocs.map((v) => v.path),
|
|
22
18
|
});
|
|
23
|
-
}
|
|
19
|
+
}
|
|
24
20
|
|
|
25
21
|
// Only prompt user if translation is actually needed
|
|
26
22
|
let shouldTranslate = false;
|
|
@@ -46,28 +42,6 @@ export default async function saveAndTranslateDocument(input, options) {
|
|
|
46
42
|
|
|
47
43
|
// Save documents in batches
|
|
48
44
|
const batchSize = 3;
|
|
49
|
-
for (let i = 0; i < selectedDocs.length; i += batchSize) {
|
|
50
|
-
const batch = selectedDocs.slice(i, i + batchSize);
|
|
51
|
-
|
|
52
|
-
const savePromises = batch.map(async (doc) => {
|
|
53
|
-
try {
|
|
54
|
-
await saveDocument(doc);
|
|
55
|
-
|
|
56
|
-
// Record history for each document if feedback is provided
|
|
57
|
-
if (doc.feedback?.trim()) {
|
|
58
|
-
recordUpdate({
|
|
59
|
-
operation: "document_update",
|
|
60
|
-
feedback: doc.feedback.trim(),
|
|
61
|
-
documentPath: doc.path,
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
} catch (error) {
|
|
65
|
-
console.error(`❌ Failed to save document ${doc.path}:`, error.message);
|
|
66
|
-
}
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
await Promise.all(savePromises);
|
|
70
|
-
}
|
|
71
45
|
|
|
72
46
|
// Return results if user chose to skip translation
|
|
73
47
|
if (!shouldTranslate) {
|
|
@@ -77,30 +51,27 @@ export default async function saveAndTranslateDocument(input, options) {
|
|
|
77
51
|
// Translate documents in batches
|
|
78
52
|
const translateAgent = options.context.agents["translateMultilingual"];
|
|
79
53
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const translatePromises = batch.map(async (doc) => {
|
|
54
|
+
await pMap(
|
|
55
|
+
selectedDocs,
|
|
56
|
+
async (doc) => {
|
|
84
57
|
try {
|
|
85
58
|
// Clear feedback to ensure translation is not affected by update feedback
|
|
86
59
|
doc.feedback = "";
|
|
87
60
|
|
|
88
|
-
|
|
61
|
+
await options.context.invoke(translateAgent, {
|
|
89
62
|
...input, // context is required
|
|
90
63
|
content: doc.content,
|
|
91
64
|
translates: doc.translates,
|
|
92
65
|
title: doc.title,
|
|
66
|
+
path: doc.path,
|
|
67
|
+
docsDir,
|
|
93
68
|
});
|
|
94
|
-
|
|
95
|
-
// Save the translated content
|
|
96
|
-
await saveDocument(doc, result.translates, true);
|
|
97
69
|
} catch (error) {
|
|
98
70
|
console.error(`❌ Failed to translate document ${doc.path}:`, error.message);
|
|
99
71
|
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
72
|
+
},
|
|
73
|
+
{ concurrency: batchSize },
|
|
74
|
+
);
|
|
104
75
|
|
|
105
76
|
return {};
|
|
106
77
|
}
|
|
@@ -23,7 +23,7 @@ input_schema:
|
|
|
23
23
|
locale:
|
|
24
24
|
type: string
|
|
25
25
|
description: User language, e.g. zh, en
|
|
26
|
-
|
|
26
|
+
detailDataSource:
|
|
27
27
|
type: string
|
|
28
28
|
description: Context for document content
|
|
29
29
|
glossary:
|
|
@@ -57,4 +57,5 @@ afs:
|
|
|
57
57
|
keep_text_in_tool_uses: false
|
|
58
58
|
skills:
|
|
59
59
|
- ./document-tools/update-document-content.mjs
|
|
60
|
+
# - ./generate-diagram.yaml
|
|
60
61
|
task_render_mode: collapse
|
|
@@ -105,6 +105,7 @@ async function showDocumentDetail(content, title) {
|
|
|
105
105
|
renderer: new markedTerminal(),
|
|
106
106
|
});
|
|
107
107
|
|
|
108
|
+
// FIXME: @zhanghan fix error "Could not find the language 'd2', did you forget to load/include a language module?"
|
|
108
109
|
const renderedMarkdown = marked(content);
|
|
109
110
|
|
|
110
111
|
// Restore original console.error
|
|
@@ -121,16 +122,15 @@ async function showDocumentDetail(content, title) {
|
|
|
121
122
|
}
|
|
122
123
|
}
|
|
123
124
|
|
|
124
|
-
export default async function userReviewDocument(
|
|
125
|
-
{ content, title, description, ...rest },
|
|
126
|
-
options,
|
|
127
|
-
) {
|
|
125
|
+
export default async function userReviewDocument({ content, description, ...rest }, options) {
|
|
128
126
|
// Check if document content exists
|
|
129
127
|
if (!content || typeof content !== "string" || content.trim().length === 0) {
|
|
130
128
|
console.log("Please provide document content to review.");
|
|
131
129
|
return { content };
|
|
132
130
|
}
|
|
133
131
|
|
|
132
|
+
const title = rest.documentStructure?.find((x) => x.path === rest.path)?.title;
|
|
133
|
+
|
|
134
134
|
// Print current document headings structure
|
|
135
135
|
printDocumentHeadings(content, title || "Untitled Document");
|
|
136
136
|
|
|
@@ -190,7 +190,7 @@ export default async function userReviewDocument(
|
|
|
190
190
|
feedbacks.push(feedback.trim());
|
|
191
191
|
|
|
192
192
|
// Get the updateDocument agent
|
|
193
|
-
const updateAgent = options.context.agents["
|
|
193
|
+
const updateAgent = options.context.agents["handleDocumentUpdate"];
|
|
194
194
|
if (!updateAgent) {
|
|
195
195
|
console.log(
|
|
196
196
|
"We can't process your feedback right now. The document update feature is temporarily unavailable.",
|
|
@@ -213,6 +213,7 @@ export default async function userReviewDocument(
|
|
|
213
213
|
originalContent: options.context.userContext.currentContent,
|
|
214
214
|
feedback: feedback.trim(),
|
|
215
215
|
userPreferences,
|
|
216
|
+
title,
|
|
216
217
|
});
|
|
217
218
|
|
|
218
219
|
// Check if feedback should be saved as user preference
|
|
@@ -18,6 +18,7 @@ export default async function chooseDocs(
|
|
|
18
18
|
locale,
|
|
19
19
|
reset = false,
|
|
20
20
|
requiredFeedback = true,
|
|
21
|
+
title,
|
|
21
22
|
},
|
|
22
23
|
options,
|
|
23
24
|
) {
|
|
@@ -65,7 +66,7 @@ export default async function chooseDocs(
|
|
|
65
66
|
|
|
66
67
|
// Let user select multiple files
|
|
67
68
|
selectedFiles = await options.prompts.checkbox({
|
|
68
|
-
message: getActionText(isTranslate, "Select documents to {action}:"),
|
|
69
|
+
message: title || getActionText(isTranslate, "Select documents to {action}:"),
|
|
69
70
|
source: (term) => {
|
|
70
71
|
if (!term) return choices;
|
|
71
72
|
|
|
@@ -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);
|
|
@@ -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
|
-
}
|