@aigne/doc-smith 0.2.5 → 0.2.6

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.
@@ -7,119 +7,12 @@ import { existsSync, mkdirSync } from "node:fs";
7
7
  import { readFile, writeFile } from "node:fs/promises";
8
8
  import { homedir } from "node:os";
9
9
  import { parse, stringify } from "yaml";
10
- import { execSync } from "node:child_process";
11
10
  import { basename } from "node:path";
12
11
  import { loadConfigFromFile, saveValueToConfig } from "../utils/utils.mjs";
13
12
 
14
13
  const WELLKNOWN_SERVICE_PATH_PREFIX = "/.well-known/service";
15
14
  const DEFAULT_APP_URL = "https://docsmith.aigne.io";
16
15
 
17
- /**
18
- * Get GitHub repository information
19
- * @param {string} repoUrl - The repository URL
20
- * @returns {Promise<Object>} - Repository information
21
- */
22
- async function getGitHubRepoInfo(repoUrl) {
23
- try {
24
- // Extract owner and repo from GitHub URL
25
- const match = repoUrl.match(
26
- /github\.com[\/:]([^\/]+)\/([^\/]+?)(?:\.git)?$/
27
- );
28
- if (!match) return null;
29
-
30
- const [, owner, repo] = match;
31
- const apiUrl = `https://api.github.com/repos/${owner}/${repo}`;
32
-
33
- const response = await fetch(apiUrl);
34
- if (!response.ok) return null;
35
-
36
- const data = await response.json();
37
- return {
38
- name: data.name,
39
- description: data.description || "",
40
- icon: data.owner?.avatar_url || "",
41
- };
42
- } catch (error) {
43
- console.warn("Failed to fetch GitHub repository info:", error.message);
44
- return null;
45
- }
46
- }
47
-
48
- /**
49
- * Get project information with user confirmation
50
- * @param {Object} options - Options object containing prompts
51
- * @returns {Promise<Object>} - Project information including name, description, and icon
52
- */
53
- async function getProjectInfo(options) {
54
- let repoInfo = null;
55
- let defaultName = basename(process.cwd());
56
- let defaultDescription = "";
57
- let defaultIcon = "";
58
-
59
- // Check if we're in a git repository
60
- try {
61
- const gitRemote = execSync("git remote get-url origin", {
62
- encoding: "utf8",
63
- stdio: ["pipe", "pipe", "ignore"],
64
- }).trim();
65
-
66
- // Extract repository name from git remote URL
67
- const repoName = gitRemote.split("/").pop().replace(".git", "");
68
- defaultName = repoName;
69
-
70
- // If it's a GitHub repository, try to get additional info
71
- if (gitRemote.includes("github.com")) {
72
- repoInfo = await getGitHubRepoInfo(gitRemote);
73
- if (repoInfo) {
74
- defaultDescription = repoInfo.description;
75
- defaultIcon = repoInfo.icon;
76
- }
77
- }
78
- } catch (error) {
79
- // Not in git repository or no origin remote, use current directory name
80
- console.warn("No git repository found, using current directory name");
81
- }
82
-
83
- // Prompt user for project information
84
- console.log("\n📋 Project Information for Documentation Platform");
85
-
86
- const projectName = await options.prompts.input({
87
- message: "Project name:",
88
- default: defaultName,
89
- validate: (input) => {
90
- if (!input || input.trim() === "") {
91
- return "Project name cannot be empty";
92
- }
93
- return true;
94
- },
95
- });
96
-
97
- const projectDescription = await options.prompts.input({
98
- message: "Project description (optional):",
99
- default: defaultDescription,
100
- });
101
-
102
- const projectIcon = await options.prompts.input({
103
- message: "Project icon URL (optional):",
104
- default: defaultIcon,
105
- validate: (input) => {
106
- if (!input || input.trim() === "") return true;
107
- try {
108
- new URL(input);
109
- return true;
110
- } catch {
111
- return "Please enter a valid URL";
112
- }
113
- },
114
- });
115
-
116
- return {
117
- name: projectName.trim(),
118
- description: projectDescription.trim(),
119
- icon: projectIcon.trim(),
120
- };
121
- }
122
-
123
16
  /**
124
17
  * Get access token from environment, config file, or prompt user for authorization
125
18
  * @param {string} appUrl - The application URL
@@ -207,7 +100,7 @@ async function getAccessToken(appUrl) {
207
100
  }
208
101
 
209
102
  export default async function publishDocs(
210
- { docsDir, appUrl, boardId, boardName, boardDesc, boardCover },
103
+ { docsDir, appUrl, boardId, projectName, projectDesc, projectLogo },
211
104
  options
212
105
  ) {
213
106
  // Check if DOC_DISCUSS_KIT_URL is set in environment variables
@@ -260,58 +153,56 @@ export default async function publishDocs(
260
153
 
261
154
  const sidebarPath = join(docsDir, "_sidebar.md");
262
155
 
263
- let projectInfo = {
264
- name: boardName,
265
- description: boardDesc,
266
- icon: boardCover,
156
+ // Get project info from config
157
+ const projectInfo = {
158
+ name: projectName || config?.projectName || basename(process.cwd()),
159
+ description: projectDesc || config?.projectDesc || "",
160
+ icon: projectLogo || config?.projectLogo || "",
267
161
  };
268
162
 
269
- // Only get project info if we need to create a new board
270
- if (!boardName) {
271
- projectInfo = await getProjectInfo(options);
272
-
273
- // save project info to config
274
- await saveValueToConfig("boardName", projectInfo.name);
275
- await saveValueToConfig("boardDesc", projectInfo.description);
276
- await saveValueToConfig("boardCover", projectInfo.icon);
277
- }
163
+ try {
164
+ const {
165
+ success,
166
+ boardId: newBoardId,
167
+ docsUrl,
168
+ } = await publishDocsFn({
169
+ sidebarPath,
170
+ accessToken,
171
+ appUrl,
172
+ boardId,
173
+ autoCreateBoard: true,
174
+ // Pass additional project information if available
175
+ boardName: projectInfo.name,
176
+ boardDesc: projectInfo.description,
177
+ boardCover: projectInfo.icon,
178
+ });
278
179
 
279
- const {
280
- success,
281
- boardId: newBoardId,
282
- docsUrl,
283
- } = await publishDocsFn({
284
- sidebarPath,
285
- accessToken,
286
- appUrl,
287
- boardId,
288
- autoCreateBoard: true,
289
- // Pass additional project information if available
290
- boardName: projectInfo.name,
291
- boardDesc: projectInfo.description,
292
- boardCover: projectInfo.icon,
293
- });
180
+ // Save values to config.yaml if publish was successful
181
+ if (success) {
182
+ // Save appUrl to config only when not using environment variable
183
+ if (!useEnvAppUrl) {
184
+ await saveValueToConfig("appUrl", appUrl);
185
+ }
294
186
 
295
- // Save values to config.yaml if publish was successful
296
- if (success) {
297
- // Save appUrl to config only when not using environment variable
298
- if (!useEnvAppUrl) {
299
- await saveValueToConfig("appUrl", appUrl);
187
+ // Save boardId to config if it was auto-created
188
+ if (boardId !== newBoardId) {
189
+ await saveValueToConfig(
190
+ "boardId",
191
+ newBoardId,
192
+ "⚠️ Warning: boardId is auto-generated by system, please do not edit manually"
193
+ );
194
+ }
300
195
  }
301
196
 
302
- // Save boardId to config if it was auto-created
303
- if (boardId !== newBoardId) {
304
- await saveValueToConfig("boardId", newBoardId);
305
- }
197
+ const message = `✅ Documentation Published Successfully!`;
198
+ return {
199
+ message,
200
+ };
201
+ } catch (error) {
202
+ return {
203
+ message: `❌ Failed to publish docs: ${error.message}`,
204
+ };
306
205
  }
307
-
308
- // const message = `## ✅ Documentation Published Successfully!
309
-
310
- // Documentation is now available at: \`${docsUrl}\`
311
- // `;
312
- return {
313
- // message,
314
- };
315
206
  }
316
207
 
317
208
  publishDocs.input_schema = {
@@ -0,0 +1,74 @@
1
+ type: team
2
+ name: retranslate
3
+ alias:
4
+ - rt
5
+ - translate-again
6
+ description: Re-translate individual document content to selected languages
7
+ skills:
8
+ - url: ./input-generator.mjs
9
+ default_input:
10
+ skipIfExists: true
11
+ - ./load-config.mjs
12
+ - ./load-sources.mjs
13
+ - type: transform
14
+ jsonata: |
15
+ $merge([
16
+ $,
17
+ {
18
+ 'structurePlan': originalStructurePlan,
19
+ 'structurePlanResult': $map(originalStructurePlan, function($item) {
20
+ $merge([
21
+ $item,
22
+ {
23
+ 'translates': [$map(translateLanguages, function($lang) { {"language": $lang} })]
24
+ }
25
+ ])
26
+ })
27
+ }
28
+ ])
29
+ - url: ./find-item-by-path.mjs
30
+ default_input:
31
+ isTranslate: true
32
+ - ./language-selector.mjs
33
+ - type: transform
34
+ jsonata: |
35
+ $merge([
36
+ $,
37
+ {
38
+ 'translates': [$map(selectedLanguages, function($lang) { {"language": $lang} })]
39
+ }
40
+ ])
41
+ - ./batch-translate.yaml
42
+ - url: ./save-single-doc.mjs
43
+ default_input:
44
+ isTranslate: true
45
+ isShowMessage: true
46
+ input_schema:
47
+ type: object
48
+ properties:
49
+ glossary:
50
+ type: string
51
+ description: Glossary of terms for consistent terminology
52
+ doc-path:
53
+ type: string
54
+ description: Document path to retranslate
55
+ # languages:
56
+ # type: array
57
+ # items:
58
+ # type: string
59
+ # description: Languages to translate to
60
+ feedback:
61
+ type: string
62
+ description: Feedback for translation improvement
63
+ output_schema:
64
+ type: object
65
+ properties:
66
+ title:
67
+ type: string
68
+ description:
69
+ type: string
70
+ path:
71
+ type: string
72
+ content:
73
+ type: string
74
+ mode: sequential
@@ -1,6 +1,7 @@
1
1
  import { writeFile, readdir, unlink } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
3
  import { getCurrentGitHead, saveGitHeadToConfig } from "../utils/utils.mjs";
4
+ import { shutdownMermaidWorkerPool } from "../utils/mermaid-worker-pool.mjs";
4
5
 
5
6
  /**
6
7
  * @param {Object} params
@@ -14,6 +15,7 @@ export default async function saveDocs({
14
15
  docsDir,
15
16
  translateLanguages = [],
16
17
  locale,
18
+ projectInfoMessage,
17
19
  }) {
18
20
  const results = [];
19
21
  // Save current git HEAD to config.yaml for change detection
@@ -47,8 +49,9 @@ export default async function saveDocs({
47
49
 
48
50
  const message = `## ✅ Documentation Generated Successfully!
49
51
 
50
- Successfully generated **${structurePlan.length}** documents and saved to: \`${docsDir}\`
51
-
52
+ Successfully generated **${structurePlan.length}** documents and saved to:
53
+ \`${docsDir}\`
54
+ ${projectInfoMessage || ""}
52
55
  ### 🚀 Next Steps
53
56
 
54
57
  1. Publish Documentation
@@ -78,6 +81,13 @@ export default async function saveDocs({
78
81
  ---
79
82
  `;
80
83
 
84
+ // Shutdown mermaid worker pool to ensure clean exit
85
+ try {
86
+ await shutdownMermaidWorkerPool();
87
+ } catch (error) {
88
+ console.warn("Failed to shutdown mermaid worker pool:", error.message);
89
+ }
90
+
81
91
  return {
82
92
  message,
83
93
  };
@@ -1,4 +1,5 @@
1
1
  import { saveDocWithTranslations } from "../utils/utils.mjs";
2
+ import { shutdownMermaidWorkerPool } from "../utils/mermaid-worker-pool.mjs";
2
3
 
3
4
  export default async function saveSingleDoc({
4
5
  path,
@@ -7,6 +8,8 @@ export default async function saveSingleDoc({
7
8
  translates,
8
9
  labels,
9
10
  locale,
11
+ isTranslate = false,
12
+ isShowMessage = false,
10
13
  }) {
11
14
  const results = await saveDocWithTranslations({
12
15
  path,
@@ -15,6 +18,22 @@ export default async function saveSingleDoc({
15
18
  translates,
16
19
  labels,
17
20
  locale,
21
+ isTranslate,
18
22
  });
23
+
24
+ if (isShowMessage) {
25
+ // Shutdown mermaid worker pool to ensure clean exit
26
+ try {
27
+ await shutdownMermaidWorkerPool();
28
+ } catch (error) {
29
+ console.warn("Failed to shutdown mermaid worker pool:", error.message);
30
+ }
31
+
32
+ const message = isTranslate
33
+ ? `✅ Translation completed successfully`
34
+ : `✅ Document updated successfully`;
35
+ return { message };
36
+ }
37
+
19
38
  return {};
20
39
  }
@@ -36,6 +36,12 @@ input_schema:
36
36
  output_schema:
37
37
  type: object
38
38
  properties:
39
+ projectName:
40
+ type: string
41
+ description: 根据 DataSources 分析项目名称,注意是原始项目名称
42
+ projectDesc:
43
+ type: string
44
+ description: 根据 DataSources 分析生成当前项目描述,为原始项目生成描述,描述简洁,不超过 50 个单词
39
45
  structurePlan: ./schema/structure-plan.yaml
40
46
  structurePlanTree:
41
47
  type: string
@@ -14,6 +14,9 @@ input_schema:
14
14
  glossary:
15
15
  type: string
16
16
  description: 术语表
17
+ feedback:
18
+ type: string
19
+ description: Feedback for translation improvement
17
20
  required:
18
21
  - language
19
22
  - content
package/aigne.yaml CHANGED
@@ -2,7 +2,8 @@
2
2
 
3
3
  chat_model:
4
4
  provider: google
5
- name: gemini-2.5-pro
5
+ # name: gemini-2.5-pro
6
+ name: gemini-2.5-flash
6
7
  temperature: 0.8
7
8
  agents:
8
9
  - ./agents/structure-planning.yaml
@@ -28,6 +29,8 @@ agents:
28
29
  - ./agents/load-config.mjs
29
30
  - ./agents/team-publish-docs.yaml
30
31
  - ./agents/find-item-by-path.mjs
32
+ - ./agents/retranslate.yaml
33
+ - ./agents/language-selector.mjs
31
34
  - ./docs-mcp/get-docs-structure.mjs
32
35
  - ./docs-mcp/get-docs-detail.mjs
33
36
  - ./docs-mcp/docs-search.yaml
@@ -40,6 +43,7 @@ cli:
40
43
  - ./agents/docs-generator.yaml
41
44
  - ./agents/detail-regenerator.yaml
42
45
  - ./agents/team-publish-docs.yaml
46
+ - ./agents/retranslate.yaml
43
47
  mcp_server:
44
48
  agents:
45
49
  - ./docs-mcp/get-docs-structure.mjs
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aigne/doc-smith",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "description": "",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -12,17 +12,26 @@
12
12
  "author": "Arcblock <blocklet@arcblock.io> https://github.com/blocklet",
13
13
  "license": "MIT",
14
14
  "dependencies": {
15
- "@aigne/anthropic": "^0.10.10",
16
- "@aigne/cli": "^1.31.0",
17
- "@aigne/core": "^1.46.0",
18
- "@aigne/gemini": "^0.8.14",
19
- "@aigne/openai": "^0.10.14",
20
- "@aigne/publish-docs": "^0.5.3",
15
+ "@aigne/anthropic": "^0.10.13",
16
+ "@aigne/cli": "^1.33.1",
17
+ "@aigne/core": "^1.48.0",
18
+ "@aigne/gemini": "^0.8.17",
19
+ "@aigne/openai": "^0.10.17",
20
+ "@aigne/publish-docs": "^0.5.4",
21
21
  "chalk": "^5.5.0",
22
+ "dompurify": "^3.2.6",
22
23
  "glob": "^11.0.3",
24
+ "jsdom": "^26.1.0",
25
+ "mermaid": "^11.9.0",
23
26
  "open": "^10.2.0",
27
+ "remark-gfm": "^4.0.1",
28
+ "remark-lint": "^10.0.1",
29
+ "remark-parse": "^11.0.0",
24
30
  "terminal-link": "^4.0.0",
25
31
  "ufo": "^1.6.1",
32
+ "unified": "^11.0.5",
33
+ "unist-util-visit": "^5.0.0",
34
+ "vfile": "^6.0.3",
26
35
  "yaml": "^2.8.0"
27
36
  },
28
37
  "scripts": {
@@ -16,15 +16,13 @@
16
16
  - **用户反馈**:
17
17
  ```
18
18
  {{ feedback }}
19
-
20
- 根据最新的 Data Sources 按需要更新节点的 sourceIds。
21
19
  ```
22
20
  </context>
23
21
 
24
22
  <goal>
25
- 你的主要目标是验证两条关键规则:
23
+ 你的主要目标是验证三条关键规则:
26
24
  1. **反馈的实现**:新的结构规划**必须**正确地实现用户反馈中要求的所有变更。
27
- 2. **无关节点的稳定性**:没有在用户反馈中被提及的节点 ** path 属性不能被修改 **。`path` 是关联现有内容的关键标识符,其稳定性至关重要。
25
+ 2. **无关节点的稳定性**:没有在用户反馈中被提及的节点 ** path、sourcesIds 属性不能被修改 **。`path`、`sourcesIds` 是关联现有内容的关键标识符,其稳定性至关重要。
28
26
  3. **数据有效性**: 所有 {{ nodeName }} 都有关联数据源,sourceIds 中都有值。
29
27
  </goal>
30
28
 
@@ -40,9 +38,8 @@
40
38
  1. **分析反馈**:仔细阅读并理解 `<context>` 中 用户反馈 提出的每一项变更要求。明确哪些节点是需要被修改、添加或删除的目标。
41
39
  2. **验证反馈的实现**:对比 `<context>` 中的 `structurePlan` 和 `originalStructurePlan`,确认所要求的变更是否已执行。例如,如果反馈是“移除‘示例’部分”,你必须检查该部分在 `structurePlan` 中是否已不存在。
42
40
  3. **验证无关节点的稳定性**:这是最关键的检查。遍历 `structurePlan` 中的所有节点。对于每一个在 `originalStructurePlan` 中也存在、但并未在反馈中被提及的节点:
43
- * **至关重要**:其 `path` 属性**必须**与 `originalStructurePlan` 中的完全相同。
44
- * 理想情况下,其他属性(如 `title`)也应保持稳定,除非这些变更是由某个被要求的变更直接导致的。
45
- 4. **`sourcesIds` 是允许变更的**,每次结构规划可以根据最新的 DataSources 变更依赖的数据源,sourceIds 不能为空。
41
+ * **至关重要**:其 `path`、`sourcesIds` 属性**必须**与 `originalStructurePlan` 中的完全相同。
42
+ * 理想情况下,其他属性(如 `title`、`description`)也应保持稳定,除非这些变更是由某个被要求的变更直接导致的,或者是 DataSource 变更导致。
46
43
  </rules>
47
44
 
48
45
  <output>
@@ -96,6 +96,5 @@ parentId: {{parentId}}
96
96
 
97
97
  1. 输内容为{{nodeName}}的详细文本。
98
98
  2. 直接输出{{nodeName}}内容,不要包含其他信息.
99
- 3. 如果无法生成内容,返回错误提示用户需要补充哪些内容。
100
- 4. 以用户语言 {{locale}} 输出
99
+ 3. 以用户语言 {{locale}} 输出
101
100
  </output_schema>
@@ -27,10 +27,15 @@
27
27
  {{originalStructurePlan}}
28
28
  </last_structure_plan>
29
29
 
30
+ <last_structure_plan_rule>
31
+ 如果提供了上一轮生成的结构规划(last_structure_plan),需要遵循以下规则:
32
+ 1. **反馈的实现**:新的结构规划**必须**正确地实现用户反馈中要求的所有变更。
33
+ 2. **无关节点的稳定性**:没有在用户反馈中被提及的节点 ** path、sourcesIds 属性不能被修改 **。`path`、`sourcesIds` 是关联现有内容的关键标识符,其稳定性至关重要。
34
+ 理想情况下,其他属性(如 `title`、`description`)也应保持稳定,除非这些变更是由某个被要求的变更直接导致的,或者是 DataSource 变更导致。
35
+ </last_structure_plan_rule>
36
+
30
37
  <structure_plan_feedback>
31
38
  {{ feedback }}
32
-
33
- 根据最新的 Data Sources 按需要更新节点的 sourceIds,如没有大的变化,可以不更新。
34
39
  </structure_plan_feedback>
35
40
 
36
41
  <review_structure_plan>
@@ -61,6 +61,10 @@
61
61
  {{content}}
62
62
  </content>
63
63
 
64
+ <translate_feedback>
65
+ {{ feedback }}
66
+ </translate_feedback>
67
+
64
68
  <review_feedback>
65
69
  {{ detailFeedback }}
66
70
  </review_feedback>