@aigne/doc-smith 0.0.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.
Files changed (44) hide show
  1. package/README.md +87 -0
  2. package/agents/batch-docs-detail-generator.yaml +14 -0
  3. package/agents/batch-translate.yaml +44 -0
  4. package/agents/check-detail-generated.mjs +128 -0
  5. package/agents/check-detail-result.mjs +141 -0
  6. package/agents/check-structure-planning-result.yaml +30 -0
  7. package/agents/check-structure-planning.mjs +54 -0
  8. package/agents/content-detail-generator.yaml +50 -0
  9. package/agents/detail-generator-and-translate.yaml +88 -0
  10. package/agents/detail-regenerator.yaml +107 -0
  11. package/agents/docs-generator.yaml +93 -0
  12. package/agents/format-structure-plan.mjs +23 -0
  13. package/agents/input-generator.mjs +142 -0
  14. package/agents/load-sources.mjs +329 -0
  15. package/agents/publish-docs.mjs +212 -0
  16. package/agents/reflective-structure-planner.yaml +10 -0
  17. package/agents/save-docs.mjs +153 -0
  18. package/agents/save-output.mjs +25 -0
  19. package/agents/save-single-doc.mjs +18 -0
  20. package/agents/schema/structure-plan-result.yaml +32 -0
  21. package/agents/schema/structure-plan.yaml +26 -0
  22. package/agents/structure-planning.yaml +49 -0
  23. package/agents/transform-detail-datasources.mjs +14 -0
  24. package/agents/translate.yaml +28 -0
  25. package/aigne.yaml +28 -0
  26. package/biome.json +51 -0
  27. package/docs-mcp/aigne.yaml +8 -0
  28. package/docs-mcp/get-docs-detail.mjs +42 -0
  29. package/docs-mcp/get-docs-structure.mjs +11 -0
  30. package/package.json +33 -0
  31. package/prompts/check-structure-planning-result.md +82 -0
  32. package/prompts/content-detail-generator.md +99 -0
  33. package/prompts/document/detail-example.md +441 -0
  34. package/prompts/document/detail-generator.md +95 -0
  35. package/prompts/document/structure-example.md +98 -0
  36. package/prompts/document/structure-getting-started.md +10 -0
  37. package/prompts/document/structure-planning.md +17 -0
  38. package/prompts/structure-planning.md +108 -0
  39. package/prompts/translator.md +69 -0
  40. package/tests/README.md +93 -0
  41. package/tests/check-detail-result.test.mjs +103 -0
  42. package/tests/load-sources.test.mjs +642 -0
  43. package/tests/test-save-docs.mjs +132 -0
  44. package/utils/utils.mjs +86 -0
package/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # AIGNE DocSmith
2
+
3
+ AIGNE DocSmith is a powerful, AI-driven documentation generation tool built on the AIGNE Framework. It automates the creation of detailed, structured, and multi-language documentation directly from your source code.
4
+
5
+ ## Features
6
+
7
+ - **Automated Structure Planning:** Intelligently analyzes your codebase to generate a comprehensive and logical document structure.
8
+ - **AI-Powered Content Generation:** Populates the document structure with detailed, high-quality content.
9
+ - **Multi-Language Support:** Seamlessly translates your documentation into multiple languages.
10
+ - **Smart File Management:** Automatically cleans up invalid .md files that are no longer in the structure plan, preventing file accumulation.
11
+ - **Customizable Prompts:** Allows for fine-tuning the output by customizing the prompts used by the AI agents.
12
+ - **Extensible Workflow:** Built with a modular agent-based architecture, making it easy to extend and customize the documentation generation process.
13
+
14
+ ## How It Works
15
+
16
+ AIGNE DocSmith utilizes a chain of AI agents to generate documentation in a three-step process:
17
+
18
+ 1. **Structure Planning (`structure-planning`):** This agent analyzes the source code and creates a structured plan for the documentation. The plan is defined in `agents/structure-planning.yaml` and uses the prompt in `prompts/structure-planning.md`.
19
+ 2. **Content Detail Generation (`content-detail-generator`):** This agent takes the structured plan and generates detailed content for each section of the documentation. This process is defined in `agents/content-detail-generator.yaml` and guided by the prompt in `prompts/document/detail-generator.md`.
20
+ 3. **Translation (`translate`):** This agent translates the generated documentation into different languages. The translation agent is defined in `agents/translate.yaml` and uses the prompt in `prompts/translator.md`.
21
+
22
+ The main workflow is orchestrated by the `docs-generator` agent, as defined in `agents/docs-generator.yaml`.
23
+
24
+ ## Getting Started
25
+
26
+ ### Prerequisites
27
+
28
+ - Node.js and pnpm
29
+ - AIGNE Framework
30
+
31
+ ### Installation
32
+
33
+ 1. Clone the repository:
34
+ ```bash
35
+ git clone https://github.com/your-username/aigne-doc-smith.git
36
+ ```
37
+ 2. Install the dependencies:
38
+ ```bash
39
+ pnpm install
40
+ ```
41
+
42
+ ### Usage
43
+
44
+ To generate documentation, run the following command:
45
+
46
+ ```bash
47
+ aigne run docs-generator --datasources.source-code-path=/path/to/your/code
48
+ ```
49
+
50
+ This will initiate the documentation generation process, and the output will be saved in the `output` directory.
51
+
52
+ ## Contributing
53
+
54
+ Contributions are welcome! Please feel free to submit a pull request or open an issue if you have any suggestions or find any bugs.
55
+
56
+ ## Test Commands
57
+
58
+ ```shell
59
+
60
+ # 初始化
61
+ npx --no doc-smith run --entry-agent init
62
+
63
+ # 生成命令
64
+ npx --no doc-smith run --input @./doc-smith/input.yaml --format yaml --entry-agent generator --model gemini:gemini-2.5-pro
65
+
66
+ # 重新生成单篇
67
+ npx --no doc-smith run --input @./doc-smith/input.yaml --format yaml --entry-agent regenerator --input-currentPath /overview --input-feedback "输出只能使用 markdown 格式,不能使用 html 标签" --model gemini:gemini-2.5-pro
68
+
69
+ # 结构规划优化
70
+ npx --no doc-smith run --input @./doc-smith/input.yaml --format yaml --entry-agent generator --input-structurePlanFeedback "删除 Contributing 下的子节点,只保留主节点,相关信息在主节点中显示" --model gemini:gemini-2.5-pro
71
+
72
+
73
+ # 发布文档
74
+ npx --no doc-smith run --input @./doc-smith/input.yaml --format yaml --entry-agent publish
75
+
76
+ ```
77
+
78
+ ## Testing
79
+
80
+ 运行测试来验证功能:
81
+
82
+ ```bash
83
+ # 测试 saveDocs 方法的文件清理功能
84
+ node tests/test-save-docs.mjs
85
+ ```
86
+
87
+ 更多测试信息请查看 [tests/README.md](tests/README.md)。
@@ -0,0 +1,14 @@
1
+ type: team
2
+ name: batch-docs-detail-generator
3
+ description: 批量生成文档详情
4
+ skills:
5
+ - ./check-detail-generated.mjs
6
+ input_schema:
7
+ type: object
8
+ properties:
9
+ datasources:
10
+ type: string
11
+ description: 结构规划的上下文,用于辅助结构规划
12
+ structurePlanResult: ./schema/structure-plan-result.yaml
13
+ iterate_on: structurePlanResult
14
+ mode: sequential
@@ -0,0 +1,44 @@
1
+ type: team
2
+ name: batch-translate
3
+ description: 批量翻译文档到多个语言
4
+ skills:
5
+ - type: team
6
+ skills:
7
+ - ./translate.yaml
8
+ - type: transform
9
+ jsonata: |
10
+ $merge([
11
+ $,
12
+ { "reviewContent": translation }
13
+ ])
14
+ reflection:
15
+ reviewer: ./check-detail-result.mjs
16
+ is_approved: isApproved
17
+ max_iterations: 3
18
+ return_last_on_max_iterations: true
19
+ input_schema:
20
+ type: object
21
+ properties:
22
+ translates:
23
+ type: array
24
+ items:
25
+ type: object
26
+ properties:
27
+ language:
28
+ type: string
29
+ content:
30
+ type: string
31
+ output_schema:
32
+ type: object
33
+ properties:
34
+ translates:
35
+ type: array
36
+ items:
37
+ type: object
38
+ properties:
39
+ language:
40
+ type: string
41
+ translation:
42
+ type: string
43
+ iterate_on: translates
44
+ mode: sequential
@@ -0,0 +1,128 @@
1
+ import { access, readFile } from "node:fs/promises";
2
+ import { dirname, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { AnthropicChatModel } from "@aigne/anthropic";
5
+ import { AIGNE, TeamAgent } from "@aigne/core";
6
+ import { GeminiChatModel } from "@aigne/gemini";
7
+ import { OpenAIChatModel } from "@aigne/openai";
8
+ import checkDetailResult from "./check-detail-result.mjs";
9
+
10
+ // Get current script directory
11
+ const __dirname = dirname(fileURLToPath(import.meta.url));
12
+
13
+ export default async function checkDetailGenerated(
14
+ { path, docsDir, sourceIds, originalStructurePlan, structurePlan, ...rest },
15
+ options
16
+ ) {
17
+ // Check if the detail file already exists
18
+ const flatName = path.replace(/^\//, "").replace(/\//g, "-");
19
+ const fileFullName = `${flatName}.md`;
20
+ const filePath = join(docsDir, fileFullName);
21
+ let detailGenerated = true;
22
+ let fileContent = null;
23
+
24
+ try {
25
+ await access(filePath);
26
+ // If file exists, read its content for validation
27
+ fileContent = await readFile(filePath, "utf8");
28
+ } catch {
29
+ detailGenerated = false;
30
+ }
31
+
32
+ // Check if sourceIds have changed by comparing with original structure plan
33
+ let sourceIdsChanged = false;
34
+ if (originalStructurePlan && sourceIds) {
35
+ // Find the original node in the structure plan
36
+ const originalNode = originalStructurePlan.find(
37
+ (node) => node.path === path
38
+ );
39
+
40
+ if (originalNode && originalNode.sourceIds) {
41
+ const originalSourceIds = originalNode.sourceIds;
42
+ const currentSourceIds = sourceIds;
43
+
44
+ // Compare arrays (order doesn't matter, but content does)
45
+ if (originalSourceIds.length !== currentSourceIds.length) {
46
+ sourceIdsChanged = true;
47
+ } else {
48
+ // Check if any sourceId is different
49
+ const originalSet = new Set(originalSourceIds);
50
+ const currentSet = new Set(currentSourceIds);
51
+
52
+ if (originalSet.size !== currentSet.size) {
53
+ sourceIdsChanged = true;
54
+ } else {
55
+ // Check if any element is different
56
+ for (const sourceId of originalSourceIds) {
57
+ if (!currentSet.has(sourceId)) {
58
+ sourceIdsChanged = true;
59
+ break;
60
+ }
61
+ }
62
+ }
63
+ }
64
+ }
65
+ }
66
+
67
+ // If file exists, check content validation
68
+ let contentValidationFailed = false;
69
+ if (detailGenerated && fileContent && structurePlan) {
70
+ const validationResult = await checkDetailResult({
71
+ structurePlan,
72
+ reviewContent: fileContent,
73
+ });
74
+
75
+ if (!validationResult.isApproved) {
76
+ contentValidationFailed = true;
77
+ }
78
+ }
79
+
80
+ // If file exists, sourceIds haven't changed, and content validation passes, no need to regenerate
81
+ if (detailGenerated && !sourceIdsChanged && !contentValidationFailed) {
82
+ return {
83
+ path,
84
+ docsDir,
85
+ ...rest,
86
+ detailGenerated: true,
87
+ };
88
+ }
89
+
90
+ // If sourceIds have changed, regenerate even if file exists
91
+ const aigne = await AIGNE.load(join(__dirname, "../"), {
92
+ models: [
93
+ {
94
+ name: OpenAIChatModel.name,
95
+ create: (params) => new OpenAIChatModel({ ...params }),
96
+ },
97
+ {
98
+ name: AnthropicChatModel.name,
99
+ create: (params) => new AnthropicChatModel({ ...params }),
100
+ },
101
+ {
102
+ name: GeminiChatModel.name,
103
+ create: (params) => new GeminiChatModel({ ...params }),
104
+ },
105
+ ],
106
+ });
107
+
108
+ const teamAgent = TeamAgent.from({
109
+ name: "generate-detail",
110
+ skills: [aigne.agents["detail-generator-and-translate"]],
111
+ });
112
+
113
+ const result = await options.context.invoke(teamAgent, {
114
+ ...rest,
115
+ docsDir,
116
+ path,
117
+ sourceIds,
118
+ originalStructurePlan,
119
+ structurePlan,
120
+ });
121
+
122
+ return {
123
+ path,
124
+ docsDir,
125
+ ...rest,
126
+ result,
127
+ };
128
+ }
@@ -0,0 +1,141 @@
1
+ export default async function checkDetailResult({
2
+ structurePlan,
3
+ reviewContent,
4
+ }) {
5
+ const linkRegex = /(?<!\!)\[([^\]]+)\]\(([^)]+)\)/g;
6
+ const tableSeparatorRegex = /^\s*\|\s*-+\s*\|\s*$/;
7
+ const codeBlockRegex = /^\s+```(?:\w+)?$/;
8
+
9
+ let isApproved = true;
10
+ const detailFeedback = [];
11
+
12
+ // Create a set of allowed links, including both original paths and processed .md paths
13
+ const allowedLinks = new Set();
14
+ structurePlan.forEach((item) => {
15
+ // Add original path
16
+ allowedLinks.add(item.path);
17
+
18
+ // Add processed .md path (same logic as processContent in utils.mjs)
19
+ let processedPath = item.path;
20
+ if (processedPath.startsWith(".")) {
21
+ processedPath = processedPath.replace(/^\./, "");
22
+ }
23
+ let flatPath = processedPath.replace(/^\//, "").replace(/\//g, "-");
24
+ flatPath = `./${flatPath}.md`;
25
+ allowedLinks.add(flatPath);
26
+ });
27
+
28
+ const checkLinks = (text, source) => {
29
+ let match;
30
+ while ((match = linkRegex.exec(text)) !== null) {
31
+ const link = match[2];
32
+ const trimLink = link.trim();
33
+
34
+ // Only check links that processContent would process
35
+ // Exclude external links and mailto
36
+ if (/^(https?:\/\/|mailto:)/.test(trimLink)) continue;
37
+
38
+ // Preserve anchors
39
+ const [path, hash] = trimLink.split("#");
40
+
41
+ // Only process relative paths or paths starting with /
42
+ if (!path) continue;
43
+
44
+ // Check if this link is in the allowed links set
45
+ if (!allowedLinks.has(trimLink)) {
46
+ isApproved = false;
47
+ detailFeedback.push(
48
+ `Found a dead link in ${source}: [${match[1]}](${trimLink}), ensure the link exists in the structure plan path`
49
+ );
50
+ }
51
+ }
52
+ };
53
+
54
+ const checkTableSeparators = (text, source) => {
55
+ // Split text into lines and check each line
56
+ const lines = text.split("\n");
57
+ for (let i = 0; i < lines.length; i++) {
58
+ const line = lines[i];
59
+ if (tableSeparatorRegex.test(line)) {
60
+ isApproved = false;
61
+ detailFeedback.push(
62
+ `Found an incorrect table separator in ${source} at line ${
63
+ i + 1
64
+ }: ${line.trim()}`
65
+ );
66
+ }
67
+ }
68
+ };
69
+
70
+ const checkSingleLine = (text, source) => {
71
+ // Count newline characters to check if content is only on one line
72
+ const newlineCount = (text.match(/\n/g) || []).length;
73
+ if (newlineCount === 0 && text.trim().length > 0) {
74
+ isApproved = false;
75
+ detailFeedback.push(
76
+ `Found single line content in ${source}: content appears to be on only one line, check for missing line breaks`
77
+ );
78
+ }
79
+ };
80
+
81
+ const checkCodeBlockIndentation = (text, source) => {
82
+ // Split text into lines and check each line
83
+ const lines = text.split("\n");
84
+ let inCodeBlock = false;
85
+ let codeBlockIndentLevel = 0;
86
+ let codeBlockStartLine = 0;
87
+
88
+ for (let i = 0; i < lines.length; i++) {
89
+ const line = lines[i];
90
+
91
+ // Check if this line is a code block marker
92
+ if (codeBlockRegex.test(line)) {
93
+ if (!inCodeBlock) {
94
+ // Starting a new code block
95
+ inCodeBlock = true;
96
+ codeBlockStartLine = i + 1;
97
+ // Calculate indentation level of the code block marker
98
+ const match = line.match(/^(\s*)(```)/);
99
+ codeBlockIndentLevel = match ? match[1].length : 0;
100
+ } else {
101
+ // Ending the code block
102
+ inCodeBlock = false;
103
+ codeBlockIndentLevel = 0;
104
+ }
105
+ continue;
106
+ }
107
+
108
+ // If we're inside a code block, check if content has proper indentation
109
+ if (inCodeBlock) {
110
+ const contentIndentLevel = line.match(/^(\s*)/)[1].length;
111
+
112
+ // If code block marker has indentation, content should have at least the same indentation
113
+ if (
114
+ codeBlockIndentLevel > 0 &&
115
+ contentIndentLevel < codeBlockIndentLevel
116
+ ) {
117
+ isApproved = false;
118
+ detailFeedback.push(
119
+ `Found code block with inconsistent indentation in ${source} at line ${codeBlockStartLine}: code block marker has ${codeBlockIndentLevel} spaces indentation but content at line ${
120
+ i + 1
121
+ } has only ${contentIndentLevel} spaces indentation`
122
+ );
123
+ // Reset to avoid multiple errors for the same code block
124
+ inCodeBlock = false;
125
+ codeBlockIndentLevel = 0;
126
+ }
127
+ }
128
+ }
129
+ };
130
+
131
+ // Check content
132
+ checkLinks(reviewContent, "result");
133
+ checkTableSeparators(reviewContent, "result");
134
+ checkSingleLine(reviewContent, "result");
135
+ checkCodeBlockIndentation(reviewContent, "result");
136
+
137
+ return {
138
+ isApproved,
139
+ detailFeedback: detailFeedback.join("\n"),
140
+ };
141
+ }
@@ -0,0 +1,30 @@
1
+ name: check-structure-planning-result
2
+ description: 对 structure-planning agent 生成的结果进行检查,确保其符合预期,特别是在有上一轮生成结果和用户反馈的场景下。
3
+ instructions:
4
+ url: ../prompts/check-structure-planning-result.md
5
+ input_schema:
6
+ type: object
7
+ properties:
8
+ structurePlan:
9
+ $ref: ./schema/structure-plan.yaml
10
+ description: 新一轮生成的结构规划。
11
+ originalStructurePlan:
12
+ $ref: ./schema/structure-plan.yaml
13
+ description: 上一轮生成的结构规划,用于对比。如果不存在,则检查默认通过。
14
+ structurePlanFeedback:
15
+ type: string
16
+ description: 用户针对上一轮结果提供的反馈。
17
+ required:
18
+ - structurePlan
19
+ output_schema:
20
+ type: object
21
+ properties:
22
+ isValid:
23
+ type: boolean
24
+ description: 检查是否通过。true 表示通过,false 表示不通过。
25
+ structureReviewFeedback:
26
+ type: string
27
+ description: 检查结果的详细说明。如果不通过,需要明确指出哪里不符合要求。
28
+ required:
29
+ - isValid
30
+ - structureReviewFeedback
@@ -0,0 +1,54 @@
1
+ import { dirname, join } from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ import { AnthropicChatModel } from "@aigne/anthropic";
4
+ import { AIGNE } from "@aigne/core";
5
+ import { GeminiChatModel } from "@aigne/gemini";
6
+ import { OpenAIChatModel } from "@aigne/openai";
7
+
8
+ // Get current script directory
9
+ const __dirname = dirname(fileURLToPath(import.meta.url));
10
+
11
+ export default async function checkStructurePlanning(
12
+ { originalStructurePlan, structurePlanFeedback, ...rest },
13
+ options
14
+ ) {
15
+ // If originalStructurePlan exists, return directly
16
+ if (originalStructurePlan && !structurePlanFeedback) {
17
+ return {
18
+ structurePlan: originalStructurePlan,
19
+ };
20
+ }
21
+
22
+ const aigne = await AIGNE.load(join(__dirname, "../"), {
23
+ models: [
24
+ {
25
+ name: OpenAIChatModel.name,
26
+ create: (params) => new OpenAIChatModel({ ...params }),
27
+ },
28
+ {
29
+ name: AnthropicChatModel.name,
30
+ create: (params) => new AnthropicChatModel({ ...params }),
31
+ },
32
+ {
33
+ name: GeminiChatModel.name,
34
+ create: (params) => new GeminiChatModel({ ...params }),
35
+ },
36
+ ],
37
+ });
38
+
39
+ const panningAgent = aigne.agents["reflective-structure-planner"];
40
+
41
+ const result = await options.context.invoke(panningAgent, {
42
+ structurePlanFeedback,
43
+ originalStructurePlan,
44
+ ...rest,
45
+ });
46
+
47
+ return {
48
+ ...result,
49
+ structurePlanFeedback,
50
+ originalStructurePlan: originalStructurePlan
51
+ ? originalStructurePlan
52
+ : JSON.parse(JSON.stringify(result.structurePlan || [])),
53
+ };
54
+ }
@@ -0,0 +1,50 @@
1
+ name: content-detail-generator
2
+ description: 通用内容详情生成器,支持网站、文档、书籍、演示文稿等多种场景
3
+ instructions:
4
+ url: ../prompts/content-detail-generator.md
5
+ input_schema:
6
+ type: object
7
+ properties:
8
+ rules:
9
+ type: string
10
+ description: 用户的结构规划的要求
11
+ locale:
12
+ type: string
13
+ description: 用户语言,如 zh、en
14
+ datasources:
15
+ type: string
16
+ description: 结构规划的上下文,用于辅助结构规划
17
+ targetAudience:
18
+ type: string
19
+ description: 结构规划的目标受众
20
+ nodeName:
21
+ type: string
22
+ description: 结构规划的节点名称
23
+ originalStructurePlan: ./schema/structure-plan.yaml
24
+ title:
25
+ type: string
26
+ description:
27
+ type: string
28
+ path:
29
+ type: string
30
+ parentId:
31
+ type:
32
+ - string
33
+ - "null"
34
+ glossary:
35
+ type: string
36
+ description: 术语表
37
+ additionalInformation:
38
+ type: string
39
+ description: 额外补充信息
40
+ required:
41
+ - rules
42
+ - datasources
43
+ - originalStructurePlan
44
+ output_schema:
45
+ type: object
46
+ properties:
47
+ content:
48
+ type: string
49
+ required:
50
+ - content
@@ -0,0 +1,88 @@
1
+ type: team
2
+ name: detail-generator-and-translate
3
+ description: 生成文档详情并翻译
4
+ skills:
5
+ - ./transform-detail-datasources.mjs
6
+ - type: team
7
+ skills:
8
+ - ./content-detail-generator.yaml
9
+ - type: transform
10
+ jsonata: |
11
+ $merge([
12
+ $,
13
+ { "reviewContent": content }
14
+ ])
15
+ reflection:
16
+ reviewer: ./check-detail-result.mjs
17
+ is_approved: isApproved
18
+ max_iterations: 3
19
+ return_last_on_max_iterations: true
20
+ - ./batch-translate.yaml
21
+ - ./save-single-doc.mjs
22
+ input_schema:
23
+ type: object
24
+ properties:
25
+ nodeName:
26
+ type: string
27
+ description: 节点名称
28
+ default: Section
29
+ locale:
30
+ type: string
31
+ description: 语言
32
+ targetAudience:
33
+ type: string
34
+ description: 目标受众
35
+ glossary:
36
+ type: string
37
+ description: 术语表
38
+ currentPath:
39
+ type: string
40
+ description: 当前路径
41
+ feedback:
42
+ type: string
43
+ description: 反馈
44
+ sources:
45
+ type: array
46
+ items:
47
+ type: string
48
+ description: 源码路径
49
+ sourcesPath:
50
+ type: array
51
+ items:
52
+ type: string
53
+ description: 源码路径
54
+ default:
55
+ - ./
56
+ includePatterns:
57
+ type: array
58
+ items:
59
+ type: string
60
+ description: 包含的文件名
61
+ excludePatterns:
62
+ type: array
63
+ items:
64
+ type: string
65
+ description: 排除的文件名
66
+ outputDir:
67
+ type: string
68
+ description: 输出目录
69
+ default: ./aigne-docs/output
70
+ docsDir:
71
+ type: string
72
+ description: 文档目录
73
+ default: ./aigne-docs/docs
74
+ additionalInformation:
75
+ type: string
76
+ description: 额外补充信息
77
+ output_schema:
78
+ type: object
79
+ properties:
80
+ title:
81
+ type: string
82
+ description:
83
+ type: string
84
+ path:
85
+ type: string
86
+ content:
87
+ type: string
88
+ mode: sequential