@aigne/doc-smith 0.0.1 → 0.0.2

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.
@@ -0,0 +1,49 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ permissions:
9
+ contents: write
10
+ pull-requests: write
11
+
12
+ jobs:
13
+ release-please:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: googleapis/release-please-action@v4
17
+ id: release
18
+ with:
19
+ token: ${{ secrets.RELEASE_PLEASE_TOKEN }}
20
+ release-type: node
21
+
22
+ - name: Checkout
23
+ uses: actions/checkout@v4
24
+ if: ${{ steps.release.outputs.release_created }}
25
+ with:
26
+ fetch-depth: 2
27
+
28
+ - uses: pnpm/action-setup@v3
29
+ if: ${{ steps.release.outputs.release_created }}
30
+ with:
31
+ version: 10
32
+
33
+ - name: Setup node
34
+ uses: actions/setup-node@v4
35
+ if: ${{ steps.release.outputs.release_created }}
36
+ with:
37
+ node-version: 23
38
+ cache: pnpm
39
+
40
+ - name: Install dependencies
41
+ run: |
42
+ pnpm install
43
+ if: ${{ steps.release.outputs.release_created }}
44
+
45
+ - name: Publish to NPM
46
+ if: ${{ steps.release.outputs.release_created }}
47
+ run: |
48
+ npm config set '//registry.npmjs.org/:_authToken' "${{ secrets.NPM_TOKEN }}"
49
+ pnpm publish --access public --no-git-checks
package/CHANGELOG.md ADDED
@@ -0,0 +1,36 @@
1
+ # Changelog
2
+
3
+ ## 0.0.2 (2025-07-30)
4
+
5
+
6
+ ### Features
7
+
8
+ * add auto create board ([3ff06ad](https://github.com/AIGNE-io/aigne-doc-smith/commit/3ff06ad0241e208b09bcf828c52c2c5051c67ef8))
9
+ * add docs-mcp ([a7508a1](https://github.com/AIGNE-io/aigne-doc-smith/commit/a7508a13abb2222968b1bc9c14948427af509f97))
10
+ * add input generator agent ([20c01bb](https://github.com/AIGNE-io/aigne-doc-smith/commit/20c01bbca6d6f9414695071fc907bd7cf43d7f62))
11
+ * add publish docs ([41bb126](https://github.com/AIGNE-io/aigne-doc-smith/commit/41bb126caeb1c3c242c7a2be27abb114aeab9953))
12
+ * add support docs labels ([4522c07](https://github.com/AIGNE-io/aigne-doc-smith/commit/4522c07b1ceb05664a1f5b5fb4df06feee536eba))
13
+ * detail add review ([8f1aa4f](https://github.com/AIGNE-io/aigne-doc-smith/commit/8f1aa4f22e2d2e590d7aa37288c2e1ee7ea48f07))
14
+ * init commit ([dafc40e](https://github.com/AIGNE-io/aigne-doc-smith/commit/dafc40e94f3c407e50b2c46ecb46237f23a15cf7))
15
+ * structure plan add review ([b56e83e](https://github.com/AIGNE-io/aigne-doc-smith/commit/b56e83e558f509302b422205f30e9b2adb42d452))
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * polish agent name ([25875a0](https://github.com/AIGNE-io/aigne-doc-smith/commit/25875a0688ebbca71f6c25bf4bd5246361f3dd2d))
21
+ * polish agent param and description ([290eb24](https://github.com/AIGNE-io/aigne-doc-smith/commit/290eb240ce986b0f1f406bf42824ce1235df11e5))
22
+ * polish code ([34f9a24](https://github.com/AIGNE-io/aigne-doc-smith/commit/34f9a24fc3748b4177cad2b5330fe6b3ccd99175))
23
+ * polish code ([0343486](https://github.com/AIGNE-io/aigne-doc-smith/commit/0343486aa086bbe2ced8de849de6a4a42567719c))
24
+ * polish code ([7b7dfb9](https://github.com/AIGNE-io/aigne-doc-smith/commit/7b7dfb925b3aa55956ef7a99ededc749fb6a42d7))
25
+ * polish code ([4fa4694](https://github.com/AIGNE-io/aigne-doc-smith/commit/4fa4694dbbd5880d501883a7cf3c0d3494509fb4))
26
+ * polish code ([74fee51](https://github.com/AIGNE-io/aigne-doc-smith/commit/74fee51ad6337af8811a35f2a4334b67ec109439))
27
+ * polish code ([7fa1675](https://github.com/AIGNE-io/aigne-doc-smith/commit/7fa1675b2cab6144d1fb9d4388130209c6cfa0bc))
28
+ * polish docs review ([70374ab](https://github.com/AIGNE-io/aigne-doc-smith/commit/70374abed74946eafa7b0f87331c2e496fa61592))
29
+ * polish input generator agent ([ae908bb](https://github.com/AIGNE-io/aigne-doc-smith/commit/ae908bbc0cb98b9b196e8b08f23149e5693e0abe))
30
+ * polish structure plan ([3a0a196](https://github.com/AIGNE-io/aigne-doc-smith/commit/3a0a196a97196ba445c4709d3466ff355917ac53))
31
+ * save docs remove useless docs ([bec5ba3](https://github.com/AIGNE-io/aigne-doc-smith/commit/bec5ba3afd462c990a0aa813bbe38ce9a61363ee))
32
+
33
+
34
+ ### Miscellaneous Chores
35
+
36
+ * release 0.0.2 ([73bf26a](https://github.com/AIGNE-io/aigne-doc-smith/commit/73bf26a5c55fa4726d866cff64bd48d1ca37a3b3))
package/README.md CHANGED
@@ -61,17 +61,36 @@ Contributions are welcome! Please feel free to submit a pull request or open an
61
61
  npx --no doc-smith run --entry-agent init
62
62
 
63
63
  # 生成命令
64
- npx --no doc-smith run --input @./doc-smith/input.yaml --format yaml --entry-agent generator --model gemini:gemini-2.5-pro
64
+ npx --no doc-smith run --entry-agent generator --model gemini:gemini-2.5-flash
65
65
 
66
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
67
+ npx --no doc-smith run --entry-agent regenerator --input-path bitnet-m4XQ-core-concepts-quantization
68
68
 
69
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
70
+ npx --no doc-smith run --input @./doc-smith/config.yaml --format yaml --entry-agent generator --input-structurePlanFeedback "补充节点的 sourceIds,确保所有节点 sourceIds 都有值" --model gemini:gemini-2.5-pro
71
71
 
72
72
 
73
73
  # 发布文档
74
- npx --no doc-smith run --input @./doc-smith/input.yaml --format yaml --entry-agent publish
74
+ npx --no doc-smith run --input @./doc-smith/config.yaml --format yaml --entry-agent publish
75
+
76
+
77
+ ```
78
+
79
+ 使用 `aigne doc` 运行
80
+
81
+ ```shell
82
+ # 初始化
83
+ aigne doc init
84
+
85
+ # 生成文档
86
+ aigne doc generator
87
+
88
+ # 优化单篇文档
89
+ aigne doc regenerator --path bitnet-m4XQ-core-concepts-quantization # 可使用 structure-plan.json 中的 path ,或 Discuss Kit 中访问的 path
90
+
91
+ # 发布文档
92
+ aigne doc publish
93
+
75
94
 
76
95
  ```
77
96
 
@@ -1,7 +1,8 @@
1
1
  type: team
2
2
  name: regenerator
3
- description: 生成文档详情并翻译
3
+ description: Optimize and regenerate individual document content and translations
4
4
  skills:
5
+ - ./load-config.mjs
5
6
  - ./load-sources.mjs
6
7
  - type: transform
7
8
  jsonata: |
@@ -19,80 +20,78 @@ skills:
19
20
  })
20
21
  }
21
22
  ])
22
- - type: transform
23
- jsonata: |
24
- (
25
- $array := structurePlanResult;
26
- $path := currentPath;
27
- $foundItem := $array[path=$path][0];
28
- $merge([
29
- $foundItem,
30
- { "originalStructurePlan": originalStructurePlan }
31
-
32
- ])
33
- )
23
+ - ./find-item-by-path.mjs
34
24
  - ./format-structure-plan.mjs
35
25
  - ./detail-generator-and-translate.yaml
36
26
  input_schema:
37
27
  type: object
38
28
  properties:
39
- nodeName:
40
- type: string
41
- description: 节点名称
42
- default: 部分
43
- locale:
44
- type: string
45
- description: 语言
46
- targetAudience:
29
+ config:
47
30
  type: string
48
- description: 目标受众
31
+ description: Path to the config file
32
+ default: ./doc-smith/config.yaml
33
+ # nodeName:
34
+ # type: string
35
+ # description: Name of the section to generate documentation for
36
+ # default: Section
37
+ # locale:
38
+ # type: string
39
+ # description: Primary language for documentation (e.g., zh, en)
40
+ # targetAudience:
41
+ # type: string
42
+ # description: Target audience for the documentation
49
43
  glossary:
50
44
  type: string
51
- description: 术语表
52
- currentPath:
45
+ description: Glossary of terms for consistent terminology
46
+ path:
53
47
  type: string
54
- description: 当前路径
48
+ description: Document path to regenerate
55
49
  feedback:
56
50
  type: string
57
- description: 反馈
58
- sources:
59
- type: array
60
- items:
61
- type: string
62
- description: 源码路径
63
- sourcesPath:
64
- type: array
65
- items:
66
- type: string
67
- description: 源码路径
68
- default:
69
- - ./
70
- includePatterns:
71
- type: array
72
- items:
73
- type: string
74
- description: 包含的文件名
75
- excludePatterns:
76
- type: array
77
- items:
78
- type: string
79
- description: 排除的文件名
80
- outputDir:
81
- type: string
82
- description: 输出目录
83
- default: ./aigne-docs/output
84
- docsDir:
85
- type: string
86
- description: 文档目录
87
- default: ./aigne-docs/docs
88
- additionalInformation:
89
- type: string
90
- description: 额外补充信息
91
- labels:
92
- type: array
93
- items:
94
- type: string
95
- description: 标签
51
+ description: Feedback for content improvement
52
+ # sources:
53
+ # type: array
54
+ # items:
55
+ # type: string
56
+ # description: Source code files for documentation generation
57
+ # sourcesPath:
58
+ # type: array
59
+ # description: Source code paths
60
+ # items:
61
+ # type: string
62
+ # default:
63
+ # - ./
64
+ # includePatterns:
65
+ # type: array
66
+ # description: File patterns to include
67
+ # items:
68
+ # type: string
69
+ # excludePatterns:
70
+ # type: array
71
+ # description: File patterns to exclude
72
+ # items:
73
+ # type: string
74
+ # outputDir:
75
+ # type: string
76
+ # description: Output directory for intermediate files
77
+ # default: ./doc-smith/output
78
+ # docsDir:
79
+ # type: string
80
+ # description: Directory to save generated documentation
81
+ # default: ./doc-smith/docs
82
+ # additionalInformation:
83
+ # type: string
84
+ # description: Additional context or information for documentation
85
+ # translateLanguages:
86
+ # type: array
87
+ # items:
88
+ # type: string
89
+ # description: Target languages for translation (e.g., zh, en)
90
+ # labels:
91
+ # type: array
92
+ # items:
93
+ # type: string
94
+ # description: Tags or labels for categorization
96
95
  output_schema:
97
96
  type: object
98
97
  properties:
@@ -1,7 +1,8 @@
1
1
  type: team
2
2
  name: generator
3
- description: 文档生成
3
+ description: Automatically generates comprehensive project documentation
4
4
  skills:
5
+ - ./load-config.mjs
5
6
  - ./load-sources.mjs
6
7
  - ./check-structure-planning.mjs
7
8
  - type: transform
@@ -34,60 +35,73 @@ skills:
34
35
  input_schema:
35
36
  type: object
36
37
  properties:
37
- nodeName:
38
+ config:
38
39
  type: string
39
- description: 节点名称
40
- default: Section
41
- rules:
42
- type: string
43
- description: 用户的结构规划的要求和规则
44
- locale:
45
- type: string
46
- description: 用户语言,如 zh、en
47
- sources:
48
- type: array
49
- items:
50
- type: string
51
- description: 用户生成文档的源码文件
52
- sourcesPath:
53
- type: array
54
- items:
55
- type: string
56
- description: 源码路径
57
- default:
58
- - ./
59
- docsDir:
60
- type: string
61
- description: 文档保存目录
62
- default: ./aigne-docs/docs
63
- outputDir:
64
- type: string
65
- description: 输出目录
66
- default: ./aigne-docs/output
67
- translateLanguages:
68
- type: array
69
- items:
70
- type: string
71
- description: 翻译语言,如 zh、en
40
+ description: Path to the config file
41
+ default: ./doc-smith/config.yaml
42
+ # nodeName:
43
+ # type: string
44
+ # description: Name of the section to generate documentation for
45
+ # default: Section
46
+ # rules:
47
+ # type: string
48
+ # description: Documentation generation requirements and rules
49
+ # locale:
50
+ # type: string
51
+ # description: Primary language for documentation (e.g., zh, en)
52
+ # sources:
53
+ # type: array
54
+ # items:
55
+ # type: string
56
+ # description: Source code files for documentation generation
57
+ # sourcesPath:
58
+ # type: array
59
+ # description: Source code paths
60
+ # items:
61
+ # type: string
62
+ # default:
63
+ # - ./
64
+ # includePatterns:
65
+ # type: array
66
+ # description: File patterns to include
67
+ # items:
68
+ # type: string
69
+ # excludePatterns:
70
+ # type: array
71
+ # description: File patterns to exclude
72
+ # items:
73
+ # type: string
74
+ # docsDir:
75
+ # type: string
76
+ # description: Directory to save generated documentation
77
+ # default: ./doc-smith/docs
78
+ # outputDir:
79
+ # type: string
80
+ # description: Output directory for intermediate files
81
+ # default: ./doc-smith/output
82
+ # translateLanguages:
83
+ # type: array
84
+ # items:
85
+ # type: string
86
+ # description: Target languages for translation (e.g., zh, en)
72
87
  glossary:
73
88
  type: string
74
- description: 术语表
75
- additionalInformation:
76
- type: string
77
- description: 额外补充信息
78
- docsType:
79
- type: string
80
- description: 文档类型,支持:generalgetting-startedreferencefaq
81
- default: general
89
+ description: Glossary of terms for consistent terminology
90
+ # additionalInformation:
91
+ # type: string
92
+ # description: Additional context or information for documentation
93
+ # docsType:
94
+ # type: string
95
+ # description: Type of documentation (general, getting-started, reference, faq)
96
+ # default: general
82
97
  structurePlanFeedback:
83
98
  type: string
84
- description: 结构规划反馈
85
- labels:
86
- type: array
87
- items:
88
- type: string
89
- description: 标签
99
+ description: Feedback for structure planning adjustments
100
+ # labels:
101
+ # type: array
102
+ # items:
103
+ # type: string
104
+ # description: Tags or labels for categorization
90
105
  required:
91
- - rules
92
- - docsDir
106
+ - config
93
107
  mode: sequential
@@ -0,0 +1,39 @@
1
+ export default async function findItemByPath({
2
+ path,
3
+ structurePlanResult,
4
+ boardId,
5
+ }) {
6
+ let foundItem = null;
7
+
8
+ // First try direct path matching
9
+ foundItem = structurePlanResult.find((item) => item.path === path);
10
+
11
+ // If not found and boardId is provided, try boardId-flattenedPath format matching
12
+ if (!foundItem && boardId) {
13
+ // Check if path starts with boardId followed by a dash
14
+ if (path.startsWith(`${boardId}-`)) {
15
+ // Extract the flattened path part after boardId-
16
+ const flattenedPath = path.substring(boardId.length + 1);
17
+
18
+ // Find item by comparing flattened paths
19
+ foundItem = structurePlanResult.find((item) => {
20
+ // Convert item.path to flattened format (replace / with -)
21
+ const itemFlattenedPath = item.path
22
+ .replace(/^\//, "")
23
+ .replace(/\//g, "-");
24
+ return itemFlattenedPath === flattenedPath;
25
+ });
26
+ }
27
+ }
28
+
29
+ if (!foundItem) {
30
+ throw new Error(
31
+ `Item with path "${path}" not found in structurePlanResult`
32
+ );
33
+ }
34
+
35
+ // Merge the found item with originalStructurePlan
36
+ return {
37
+ ...foundItem,
38
+ };
39
+ }
@@ -9,7 +9,7 @@ import { join, dirname } from "node:path";
9
9
  * @returns {Promise<Object>}
10
10
  */
11
11
  export default async function init(
12
- { outputPath = "./doc-smith", fileName = "input.yaml" },
12
+ { outputPath = "./doc-smith", fileName = "config.yaml" },
13
13
  options
14
14
  ) {
15
15
  console.log("Welcome to AIGNE Doc Smith!");
@@ -31,16 +31,16 @@ export default async function init(
31
31
  console.log("\n=== Target Audience ===");
32
32
  const targetAudienceInput = await options.prompts.input({
33
33
  message:
34
- "What is the target audience? (e.g., developers, users, press Enter to skip):",
34
+ "What is the target audience? (e.g., developers, users, press Enter for default 'developers'):",
35
35
  });
36
- input.targetAudience = targetAudienceInput.trim() || "";
36
+ input.targetAudience = targetAudienceInput.trim() || "developers";
37
37
 
38
38
  // 3. Language settings
39
39
  console.log("\n=== Language Settings ===");
40
40
  const localeInput = await options.prompts.input({
41
- message: "Primary language (e.g., en, zh, press Enter to skip):",
41
+ message: "Primary language (e.g., en, zh, press Enter for default 'en'):",
42
42
  });
43
- input.locale = localeInput.trim() || "";
43
+ input.locale = localeInput.trim() || "en";
44
44
 
45
45
  // 4. Translation languages
46
46
  console.log("\n=== Translation Settings ===");
@@ -89,25 +89,19 @@ export default async function init(
89
89
  function generateYAML(input, outputPath) {
90
90
  let yaml = "";
91
91
 
92
- // Add rules
93
- if (input.rules) {
94
- yaml += `rules: |\n`;
92
+ // Add rules (required field)
93
+ yaml += `rules: |\n`;
94
+ if (input.rules && input.rules.trim()) {
95
95
  yaml += ` ${input.rules.split("\n").join("\n ")}\n\n`;
96
+ } else {
97
+ yaml += ` \n\n`;
96
98
  }
97
99
 
98
100
  // Add target audience
99
- if (input.targetAudience && input.targetAudience.trim()) {
100
- yaml += `targetAudience: ${input.targetAudience}\n`;
101
- } else {
102
- yaml += `# targetAudience: developers # Target audience for the documentation (e.g., developers, users)\n`;
103
- }
101
+ yaml += `targetAudience: ${input.targetAudience}\n`;
104
102
 
105
103
  // Add language settings
106
- if (input.locale && input.locale.trim()) {
107
- yaml += `locale: ${input.locale}\n`;
108
- } else {
109
- yaml += `# locale: en # Primary language for the documentation (e.g., en, zh)\n`;
110
- }
104
+ yaml += `locale: ${input.locale}\n`;
111
105
 
112
106
  // Add translation languages
113
107
  if (
@@ -136,7 +130,5 @@ function generateYAML(input, outputPath) {
136
130
  return yaml;
137
131
  }
138
132
 
139
- // Execute the function if this file is run directly
140
- if (import.meta.url === `file://${process.argv[1]}`) {
141
- inputGenerator({}).catch(console.error);
142
- }
133
+ init.description =
134
+ "Generate a configuration file for the documentation generation process";
@@ -0,0 +1,43 @@
1
+ import path from "node:path";
2
+ import fs from "node:fs/promises";
3
+ import { parse } from "yaml";
4
+
5
+ export default async function loadConfig({ config }) {
6
+ const configPath = path.join(process.cwd(), config);
7
+
8
+ try {
9
+ // Check if config file exists
10
+ await fs.access(configPath);
11
+ } catch (error) {
12
+ console.log(`Config file not found: ${configPath}`);
13
+ console.log("Please run 'aigne doc init' to create the config file.");
14
+ throw new Error(`Config file not found: ${configPath}`);
15
+ }
16
+
17
+ try {
18
+ // Read and parse YAML file
19
+ const configContent = await fs.readFile(configPath, "utf-8");
20
+ const parsedConfig = parse(configContent);
21
+ return {
22
+ nodeName: "Section",
23
+ locale: "en",
24
+ sourcesPath: ["./"],
25
+ docDir: "./doc-smith/docs",
26
+ outputDir: "./doc-smith/output",
27
+ ...parsedConfig,
28
+ };
29
+ } catch (error) {
30
+ console.error(`Error parsing config file: ${error.message}`);
31
+ throw new Error(`Failed to parse config file: ${error.message}`);
32
+ }
33
+ }
34
+
35
+ loadConfig.input_schema = {
36
+ type: "object",
37
+ properties: {
38
+ config: {
39
+ type: "string",
40
+ default: "./doc-smith/config.yaml",
41
+ },
42
+ },
43
+ };
@@ -44,8 +44,9 @@ const DEFAULT_EXCLUDE_PATTERNS = [
44
44
  "**/*test/**",
45
45
  "**/*tests/**",
46
46
  "**/*examples/**",
47
+ "**/playgrounds/**",
47
48
  "v1/**",
48
- "**/*dist/**",
49
+ "**/dist/**",
49
50
  "**/*build/**",
50
51
  "**/*experimental/**",
51
52
  "**/*deprecated/**",
@@ -59,6 +60,7 @@ const DEFAULT_EXCLUDE_PATTERNS = [
59
60
  "**/*bin/**",
60
61
  "**/*node_modules/**",
61
62
  "*.log",
63
+ "**/*test.*",
62
64
  ];
63
65
 
64
66
  /**
@@ -117,7 +117,7 @@ async function getAccessToken(appUrl) {
117
117
  }
118
118
 
119
119
  /**
120
- * Save boardId to input.yaml file if it was auto-created
120
+ * Save boardId to config.yaml file if it was auto-created
121
121
  * @param {string} boardId - The original boardId (may be empty)
122
122
  * @param {string} newBoardId - The boardId returned from publishDocsFn
123
123
  */
@@ -130,7 +130,7 @@ async function saveBoardIdToInput(boardId, newBoardId) {
130
130
  mkdirSync(docSmithDir, { recursive: true });
131
131
  }
132
132
 
133
- const inputFilePath = join(docSmithDir, "input.yaml");
133
+ const inputFilePath = join(docSmithDir, "config.yaml");
134
134
  let fileContent = "";
135
135
 
136
136
  // Read existing file content if it exists
@@ -156,12 +156,12 @@ async function saveBoardIdToInput(boardId, newBoardId) {
156
156
  await writeFile(inputFilePath, fileContent);
157
157
  console.log(`Board ID saved to: ${inputFilePath}`);
158
158
  } catch (error) {
159
- console.warn("Failed to save board ID to input.yaml:", error.message);
159
+ console.warn("Failed to save board ID to config.yaml:", error.message);
160
160
  }
161
161
  }
162
162
  }
163
163
 
164
- export default async function publish({ docsDir, appUrl, boardId }) {
164
+ export default async function publishDocs({ docsDir, appUrl, boardId }) {
165
165
  const accessToken = await getAccessToken(appUrl);
166
166
 
167
167
  process.env.DOC_ROOT_DIR = docsDir;
@@ -180,7 +180,7 @@ export default async function publish({ docsDir, appUrl, boardId }) {
180
180
  autoCreateBoard: !boardId,
181
181
  });
182
182
 
183
- // Save boardId to input.yaml if it was auto-created
183
+ // Save boardId to config.yaml if it was auto-created
184
184
  await saveBoardIdToInput(boardId, newBoardId);
185
185
 
186
186
  return {
@@ -190,7 +190,7 @@ export default async function publish({ docsDir, appUrl, boardId }) {
190
190
  };
191
191
  }
192
192
 
193
- publish.input_schema = {
193
+ publishDocs.input_schema = {
194
194
  type: "object",
195
195
  properties: {
196
196
  docsDir: {
@@ -210,3 +210,5 @@ publish.input_schema = {
210
210
  },
211
211
  },
212
212
  };
213
+
214
+ publishDocs.description = "Publish the documentation to Discuss Kit";
@@ -12,6 +12,7 @@ export default async function saveDocs({
12
12
  structurePlanResult: structurePlan,
13
13
  docsDir,
14
14
  translateLanguages = [],
15
+ locale,
15
16
  }) {
16
17
  const results = [];
17
18
 
@@ -30,7 +31,8 @@ export default async function saveDocs({
30
31
  const cleanupResults = await cleanupInvalidFiles(
31
32
  structurePlan,
32
33
  docsDir,
33
- translateLanguages
34
+ translateLanguages,
35
+ locale
34
36
  );
35
37
  results.push(...cleanupResults);
36
38
  } catch (err) {
@@ -40,14 +42,31 @@ export default async function saveDocs({
40
42
  return { saveDocsResult: results };
41
43
  }
42
44
 
45
+ /**
46
+ * Generate filename based on flatName and language
47
+ * @param {string} flatName - Flattened path name
48
+ * @param {string} language - Language code
49
+ * @returns {string} - Generated filename
50
+ */
51
+ function generateFileName(flatName, language) {
52
+ const isEnglish = language === "en";
53
+ return isEnglish ? `${flatName}.md` : `${flatName}.${language}.md`;
54
+ }
55
+
43
56
  /**
44
57
  * Clean up .md files that are no longer in the structure plan
45
58
  * @param {Array<{path: string, title: string}>} structurePlan
46
59
  * @param {string} docsDir
47
60
  * @param {Array<string>} translateLanguages
61
+ * @param {string} locale - Main language locale (e.g., 'en', 'zh', 'fr')
48
62
  * @returns {Promise<Array<{ path: string, success: boolean, error?: string }>>}
49
63
  */
50
- async function cleanupInvalidFiles(structurePlan, docsDir, translateLanguages) {
64
+ async function cleanupInvalidFiles(
65
+ structurePlan,
66
+ docsDir,
67
+ translateLanguages,
68
+ locale
69
+ ) {
51
70
  const results = [];
52
71
 
53
72
  try {
@@ -61,12 +80,14 @@ async function cleanupInvalidFiles(structurePlan, docsDir, translateLanguages) {
61
80
  // Add main document files
62
81
  for (const { path } of structurePlan) {
63
82
  const flatName = path.replace(/^\//, "").replace(/\//g, "-");
64
- const fileName = `${flatName}.md`;
65
- expectedFiles.add(fileName);
83
+
84
+ // Main language file
85
+ const mainFileName = generateFileName(flatName, locale);
86
+ expectedFiles.add(mainFileName);
66
87
 
67
88
  // Add translation files for each language
68
89
  for (const lang of translateLanguages) {
69
- const translateFileName = `${flatName}.${lang}.md`;
90
+ const translateFileName = generateFileName(flatName, lang);
70
91
  expectedFiles.add(translateFileName);
71
92
  }
72
93
  }
@@ -6,6 +6,7 @@ export default async function saveSingleDoc({
6
6
  docsDir,
7
7
  translates,
8
8
  labels,
9
+ locale,
9
10
  }) {
10
11
  const results = await saveDocWithTranslations({
11
12
  path,
@@ -13,6 +14,7 @@ export default async function saveSingleDoc({
13
14
  docsDir,
14
15
  translates,
15
16
  labels,
17
+ locale,
16
18
  });
17
19
  return { saveSingleDocResult: results };
18
20
  }
@@ -0,0 +1,13 @@
1
+ type: team
2
+ name: publish
3
+ description: Publish the documentation to Discuss Kit
4
+ skills:
5
+ - load-config.mjs
6
+ - publish-docs.mjs
7
+ input_schema:
8
+ type: object
9
+ properties:
10
+ config:
11
+ type: string
12
+ description: Path to the config file
13
+ default: ./doc-smith/config.yaml
package/aigne.yaml CHANGED
@@ -6,7 +6,6 @@ chat_model:
6
6
  # name: gemini-2.5-flash-preview-05-20
7
7
  temperature: 0.8
8
8
  agents:
9
- - ./agents/docs-generator.yaml
10
9
  - ./agents/structure-planning.yaml
11
10
  - ./agents/batch-docs-detail-generator.yaml
12
11
  - ./agents/load-sources.mjs
@@ -23,6 +22,10 @@ agents:
23
22
  - ./agents/reflective-structure-planner.yaml
24
23
  - ./agents/check-structure-planning-result.yaml
25
24
  - ./agents/input-generator.mjs
25
+ - ./agents/docs-generator.yaml
26
26
  - ./agents/detail-regenerator.yaml
27
27
  - ./agents/publish-docs.mjs
28
28
  - ./agents/format-structure-plan.mjs
29
+ - ./agents/load-config.mjs
30
+ - ./agents/team-publish-docs.yaml
31
+ - ./agents/find-item-by-path.mjs
File without changes
package/package.json CHANGED
@@ -1,18 +1,11 @@
1
1
  {
2
2
  "name": "@aigne/doc-smith",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
8
8
  "main": "index.js",
9
- "scripts": {
10
- "test": "echo \"Error: no test specified\" && exit 1",
11
- "lint": "biome check && pnpm -r run lint",
12
- "update:deps": "npx -y taze major -r -w -f -n '/@abtnode|@aigne|@arcblock|@blocklet|@did-connect|@did-pay|@did-space|@nft-store|@nft-studio|@ocap/' && pnpm install && pnpm run deduplicate",
13
- "deduplicate": "pnpm dedupe",
14
- "lint:fix": "biome check --write && pnpm -r run lint"
15
- },
16
9
  "type": "module",
17
10
  "bin": "aigne.yaml",
18
11
  "keywords": [],
@@ -20,14 +13,21 @@
20
13
  "license": "MIT",
21
14
  "dependencies": {
22
15
  "@aigne/anthropic": "^0.10.2",
23
- "@aigne/cli": "^1.26.0",
16
+ "@aigne/cli": "^1.27.0",
24
17
  "@aigne/core": "^1.39.0",
25
18
  "@aigne/gemini": "^0.8.6",
26
19
  "@aigne/openai": "^0.10.6",
27
- "@aigne/publish-docs": "^0.4.0",
20
+ "@aigne/publish-docs": "^0.5.0",
28
21
  "glob": "^11.0.3",
29
22
  "open": "^10.2.0",
30
23
  "ufo": "^1.6.1",
31
24
  "yaml": "^2.8.0"
25
+ },
26
+ "scripts": {
27
+ "test": "echo \"Error: no test specified\" && exit 1",
28
+ "lint": "biome check && pnpm -r run lint",
29
+ "update:deps": "npx -y taze major -r -w -f -n '/@abtnode|@aigne|@arcblock|@blocklet|@did-connect|@did-pay|@did-space|@nft-store|@nft-studio|@ocap/' && pnpm install && pnpm run deduplicate",
30
+ "deduplicate": "pnpm dedupe",
31
+ "lint:fix": "biome check --write && pnpm -r run lint"
32
32
  }
33
- }
33
+ }
@@ -23,6 +23,7 @@
23
23
  你的主要目标是验证两条关键规则:
24
24
  1. **反馈的实现**:新的结构规划**必须**正确地实现用户反馈中要求的所有变更。
25
25
  2. **无关节点的稳定性**:没有在用户反馈中被提及的节点**绝不能**被修改,特别是它们的 `path` 属性。`path` 是关联现有内容的关键标识符,其稳定性至关重要。
26
+ 3. **数据有效性**: 所有 {{ nodeName }} 都有关联数据源,sourceIds 中都有值。
26
27
  </goal>
27
28
 
28
29
  <rules>
@@ -71,6 +72,15 @@
71
72
  "reason": "The new structure plan modified unrelated nodes, which is not allowed. [Please provide specific details, e.g.: 'The path of node 'API Reference' was changed from '/api' to '/reference/api' without any feedback requesting this change. This is a critical error.']"
72
73
  }
73
74
  ```
75
+
76
+ * ** 如果数据无效 **:
77
+ ```json
78
+ {
79
+ "isValid": false,
80
+ "reason": "The structure plan contains nodes without associated data sources. Each node must have at least one source file linked through sourcesIds."
81
+ }
82
+ ```
83
+
74
84
  * **如果是首次运行**:
75
85
 
76
86
  ```json
@@ -64,8 +64,10 @@ parentId: {{parentId}}
64
64
  - 结合当前{{nodeName}}的标题、描述,合理规划{{nodeName}}内容结构,内容要丰富、有条理、有吸引力。
65
65
  - 内容风格需要匹配目标受众
66
66
  - 明确区分与 structurePlan 其他{{nodeName}}的内容,避免重复,突出本{{nodeName}}的独特价值。
67
+ {% if enforceInfoCompleteness %}
67
68
  - 如果 DataSources 相关信息不足,直接返回错误信息,提示用户补充内容,要确保页面内容足够丰富,你可以放心的向用户提出补充信息的要求。
68
69
  - 只展示有价值、能吸引用户的信息,如信息不足,提示用户补充信息
70
+ {% endif %}
69
71
  - 输出为完整的信息,包含{{nodeName}}计划展示的全部信息。
70
72
  - 确保每个{{nodeName}}的详情中,都包含一个 markdown 的一级标题,展示当前{{nodeName}}的标题:{{title}}
71
73
  - markdown 输出内容正常换行、添加空行,让内容容易阅读
package/utils/utils.mjs CHANGED
@@ -34,6 +34,7 @@ export function processContent({ content }) {
34
34
  * @param {string} params.path - Relative path (without extension)
35
35
  * @param {string} params.content - Main document content
36
36
  * @param {string} params.docsDir - Root directory
37
+ * @param {string} params.locale - Main content language (e.g., 'en', 'zh', 'fr')
37
38
  * @param {Array<{language: string, translation: string}>} [params.translates] - Translation content
38
39
  * @param {Array<string>} [params.labels] - Document labels for front matter
39
40
  * @returns {Promise<Array<{ path: string, success: boolean, error?: string }>>}
@@ -42,6 +43,7 @@ export async function saveDocWithTranslations({
42
43
  path: docPath,
43
44
  content,
44
45
  docsDir,
46
+ locale,
45
47
  translates = [],
46
48
  labels,
47
49
  }) {
@@ -49,10 +51,18 @@ export async function saveDocWithTranslations({
49
51
  try {
50
52
  // Flatten path: remove leading /, replace all / with -
51
53
  const flatName = docPath.replace(/^\//, "").replace(/\//g, "-");
52
- const fileFullName = `${flatName}.md`;
53
- const filePath = path.join(docsDir, fileFullName);
54
54
  await fs.mkdir(docsDir, { recursive: true });
55
55
 
56
+ // Helper function to generate filename based on language
57
+ const getFileName = (language) => {
58
+ const isEnglish = language === "en";
59
+ return isEnglish ? `${flatName}.md` : `${flatName}.${language}.md`;
60
+ };
61
+
62
+ // Save main content with appropriate filename based on locale
63
+ const mainFileName = getFileName(locale);
64
+ const mainFilePath = path.join(docsDir, mainFileName);
65
+
56
66
  // Add labels front matter if labels are provided
57
67
  let finalContent = processContent({ content });
58
68
  if (labels && labels.length > 0) {
@@ -60,11 +70,12 @@ export async function saveDocWithTranslations({
60
70
  finalContent = frontMatter + finalContent;
61
71
  }
62
72
 
63
- await fs.writeFile(filePath, finalContent, "utf8");
64
- results.push({ path: filePath, success: true });
73
+ await fs.writeFile(mainFilePath, finalContent, "utf8");
74
+ results.push({ path: mainFilePath, success: true });
65
75
 
76
+ // Process all translations
66
77
  for (const translate of translates) {
67
- const translateFileName = `${flatName}.${translate.language}.md`;
78
+ const translateFileName = getFileName(translate.language);
68
79
  const translatePath = path.join(docsDir, translateFileName);
69
80
 
70
81
  // Add labels front matter to translation content if labels are provided