@aigne/doc-smith 0.2.5 → 0.2.8
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 +26 -0
- package/README.md +1 -0
- package/agents/check-detail-result.mjs +13 -139
- package/agents/check-detail.mjs +4 -6
- package/agents/check-structure-plan.mjs +56 -12
- package/agents/detail-generator-and-translate.yaml +7 -1
- package/agents/detail-regenerator.yaml +3 -1
- package/agents/docs-generator.yaml +2 -1
- package/agents/find-item-by-path.mjs +64 -15
- package/agents/input-generator.mjs +31 -11
- package/agents/language-selector.mjs +89 -0
- package/agents/load-config.mjs +2 -2
- package/agents/load-sources.mjs +13 -40
- package/agents/publish-docs.mjs +47 -161
- package/agents/retranslate.yaml +74 -0
- package/agents/save-docs.mjs +19 -21
- package/agents/save-output.mjs +2 -9
- package/agents/save-single-doc.mjs +20 -1
- package/agents/schema/structure-plan.yaml +1 -1
- package/agents/structure-planning.yaml +6 -0
- package/agents/transform-detail-datasources.mjs +2 -5
- package/agents/translate.yaml +3 -0
- package/aigne.yaml +5 -1
- package/biome.json +13 -3
- package/docs-mcp/get-docs-structure.mjs +1 -1
- package/docs-mcp/read-doc-content.mjs +1 -4
- package/package.json +20 -7
- package/prompts/check-structure-planning-result.md +4 -7
- package/prompts/content-detail-generator.md +1 -2
- package/prompts/structure-planning.md +7 -2
- package/prompts/translator.md +4 -0
- package/tests/check-detail-result.test.mjs +8 -19
- package/tests/load-sources.test.mjs +65 -161
- package/tests/test-all-validation-cases.mjs +741 -0
- package/tests/test-save-docs.mjs +6 -17
- package/utils/constants.mjs +1 -2
- package/utils/markdown-checker.mjs +453 -0
- package/utils/mermaid-validator.mjs +153 -0
- package/utils/mermaid-worker-pool.mjs +250 -0
- package/utils/mermaid-worker.mjs +233 -0
- package/utils/utils.mjs +162 -114
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { SUPPORTED_LANGUAGES } from "../utils/constants.mjs";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Interactive language selector for translation from configured languages
|
|
5
|
+
* @param {Object} params
|
|
6
|
+
* @param {Array<string>} [params.languages] - Pre-selected languages
|
|
7
|
+
* @param {Array<string>} params.translateLanguages - Available languages from config
|
|
8
|
+
* @param {Object} options - Options object with prompts
|
|
9
|
+
* @returns {Promise<Object>} Selected languages
|
|
10
|
+
*/
|
|
11
|
+
export default async function languageSelector({ languages, translateLanguages }, options) {
|
|
12
|
+
let selectedLanguages = [];
|
|
13
|
+
|
|
14
|
+
// Check if translateLanguages is available from config
|
|
15
|
+
if (
|
|
16
|
+
!translateLanguages ||
|
|
17
|
+
!Array.isArray(translateLanguages) ||
|
|
18
|
+
translateLanguages.length === 0
|
|
19
|
+
) {
|
|
20
|
+
throw new Error(
|
|
21
|
+
"No translation languages configured in config.yaml. Please add translateLanguages to your configuration.",
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// If languages are provided as parameter, validate against configured languages
|
|
26
|
+
if (languages && Array.isArray(languages) && languages.length > 0) {
|
|
27
|
+
const validLanguages = languages.filter((lang) => translateLanguages.includes(lang));
|
|
28
|
+
|
|
29
|
+
if (validLanguages.length > 0) {
|
|
30
|
+
selectedLanguages = validLanguages;
|
|
31
|
+
} else {
|
|
32
|
+
console.log(`⚠️ Invalid languages provided: ${languages.join(", ")}`);
|
|
33
|
+
console.log("Available configured languages:", translateLanguages.join(", "));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// If no valid languages were provided, let user select from configured languages
|
|
38
|
+
if (selectedLanguages.length === 0) {
|
|
39
|
+
// Create choices from configured languages with labels
|
|
40
|
+
const choices = translateLanguages.map((langCode) => {
|
|
41
|
+
const supportedLang = SUPPORTED_LANGUAGES.find((l) => l.code === langCode);
|
|
42
|
+
return {
|
|
43
|
+
name: supportedLang ? `${supportedLang.label} (${supportedLang.sample})` : langCode,
|
|
44
|
+
value: langCode,
|
|
45
|
+
short: langCode,
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
selectedLanguages = await options.prompts.checkbox({
|
|
50
|
+
message: "Select languages to translate:",
|
|
51
|
+
choices: choices,
|
|
52
|
+
validate: (answer) => {
|
|
53
|
+
if (answer.length === 0) {
|
|
54
|
+
return "Please select at least one language";
|
|
55
|
+
}
|
|
56
|
+
return true;
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (selectedLanguages.length === 0) {
|
|
62
|
+
throw new Error("No languages selected for re-translation");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
selectedLanguages,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
languageSelector.input_schema = {
|
|
71
|
+
type: "object",
|
|
72
|
+
properties: {
|
|
73
|
+
languages: {
|
|
74
|
+
type: "array",
|
|
75
|
+
items: {
|
|
76
|
+
type: "string",
|
|
77
|
+
},
|
|
78
|
+
description: "Pre-selected languages for translation",
|
|
79
|
+
},
|
|
80
|
+
translateLanguages: {
|
|
81
|
+
type: "array",
|
|
82
|
+
items: {
|
|
83
|
+
type: "string",
|
|
84
|
+
},
|
|
85
|
+
description: "Available translation languages from config",
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
required: ["translateLanguages"],
|
|
89
|
+
};
|
package/agents/load-config.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
1
|
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
3
|
import { parse } from "yaml";
|
|
4
4
|
|
|
5
5
|
export default async function loadConfig({ config }) {
|
|
@@ -8,7 +8,7 @@ export default async function loadConfig({ config }) {
|
|
|
8
8
|
try {
|
|
9
9
|
// Check if config file exists
|
|
10
10
|
await fs.access(configPath);
|
|
11
|
-
} catch (
|
|
11
|
+
} catch (_error) {
|
|
12
12
|
console.log(`Config file not found: ${configPath}`);
|
|
13
13
|
console.log("Please run 'aigne doc init' to create the config file.");
|
|
14
14
|
throw new Error(`Config file not found: ${configPath}`);
|
package/agents/load-sources.mjs
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
import { access, readFile, stat } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { glob } from "glob";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
getModifiedFilesBetweenCommits,
|
|
7
|
-
} from "../utils/utils.mjs";
|
|
8
|
-
import {
|
|
9
|
-
DEFAULT_INCLUDE_PATTERNS,
|
|
10
|
-
DEFAULT_EXCLUDE_PATTERNS,
|
|
11
|
-
} from "../utils/constants.mjs";
|
|
4
|
+
import { DEFAULT_EXCLUDE_PATTERNS, DEFAULT_INCLUDE_PATTERNS } from "../utils/constants.mjs";
|
|
5
|
+
import { getCurrentGitHead, getModifiedFilesBetweenCommits } from "../utils/utils.mjs";
|
|
12
6
|
|
|
13
7
|
/**
|
|
14
8
|
* Load .gitignore patterns from a directory
|
|
@@ -42,12 +36,7 @@ async function loadGitignore(dir) {
|
|
|
42
36
|
* @param {string[]} gitignorePatterns - .gitignore patterns
|
|
43
37
|
* @returns {Promise<string[]>} Array of file paths
|
|
44
38
|
*/
|
|
45
|
-
async function getFilesWithGlob(
|
|
46
|
-
dir,
|
|
47
|
-
includePatterns,
|
|
48
|
-
excludePatterns,
|
|
49
|
-
gitignorePatterns
|
|
50
|
-
) {
|
|
39
|
+
async function getFilesWithGlob(dir, includePatterns, excludePatterns, gitignorePatterns) {
|
|
51
40
|
// Prepare all ignore patterns
|
|
52
41
|
const allIgnorePatterns = [];
|
|
53
42
|
|
|
@@ -88,9 +77,7 @@ async function getFilesWithGlob(
|
|
|
88
77
|
|
|
89
78
|
return files;
|
|
90
79
|
} catch (error) {
|
|
91
|
-
console.warn(
|
|
92
|
-
`Warning: Error during glob search in ${dir}: ${error.message}`
|
|
93
|
-
);
|
|
80
|
+
console.warn(`Warning: Error during glob search in ${dir}: ${error.message}`);
|
|
94
81
|
return [];
|
|
95
82
|
}
|
|
96
83
|
}
|
|
@@ -143,14 +130,8 @@ export default async function loadSources({
|
|
|
143
130
|
: [excludePatterns]
|
|
144
131
|
: [];
|
|
145
132
|
|
|
146
|
-
finalIncludePatterns = [
|
|
147
|
-
|
|
148
|
-
...userInclude,
|
|
149
|
-
];
|
|
150
|
-
finalExcludePatterns = [
|
|
151
|
-
...DEFAULT_EXCLUDE_PATTERNS,
|
|
152
|
-
...userExclude,
|
|
153
|
-
];
|
|
133
|
+
finalIncludePatterns = [...DEFAULT_INCLUDE_PATTERNS, ...userInclude];
|
|
134
|
+
finalExcludePatterns = [...DEFAULT_EXCLUDE_PATTERNS, ...userExclude];
|
|
154
135
|
} else {
|
|
155
136
|
// Use only user patterns
|
|
156
137
|
if (includePatterns) {
|
|
@@ -170,7 +151,7 @@ export default async function loadSources({
|
|
|
170
151
|
dir,
|
|
171
152
|
finalIncludePatterns,
|
|
172
153
|
finalExcludePatterns,
|
|
173
|
-
gitignorePatterns
|
|
154
|
+
gitignorePatterns,
|
|
174
155
|
);
|
|
175
156
|
allFiles = allFiles.concat(filesInDir);
|
|
176
157
|
}
|
|
@@ -194,7 +175,7 @@ export default async function loadSources({
|
|
|
194
175
|
sourceId: relativePath,
|
|
195
176
|
content,
|
|
196
177
|
};
|
|
197
|
-
})
|
|
178
|
+
}),
|
|
198
179
|
);
|
|
199
180
|
|
|
200
181
|
// Get the last structure plan result
|
|
@@ -253,13 +234,8 @@ export default async function loadSources({
|
|
|
253
234
|
try {
|
|
254
235
|
currentGitHead = getCurrentGitHead();
|
|
255
236
|
if (currentGitHead && currentGitHead !== lastGitHead) {
|
|
256
|
-
modifiedFiles = getModifiedFilesBetweenCommits(
|
|
257
|
-
|
|
258
|
-
currentGitHead
|
|
259
|
-
);
|
|
260
|
-
console.log(
|
|
261
|
-
`Detected ${modifiedFiles.length} modified files since last generation`
|
|
262
|
-
);
|
|
237
|
+
modifiedFiles = getModifiedFilesBetweenCommits(lastGitHead, currentGitHead);
|
|
238
|
+
console.log(`Detected ${modifiedFiles.length} modified files since last generation`);
|
|
263
239
|
}
|
|
264
240
|
} catch (error) {
|
|
265
241
|
console.warn("Failed to detect git changes:", error.message);
|
|
@@ -290,18 +266,15 @@ loadSources.input_schema = {
|
|
|
290
266
|
},
|
|
291
267
|
includePatterns: {
|
|
292
268
|
anyOf: [{ type: "string" }, { type: "array", items: { type: "string" } }],
|
|
293
|
-
description:
|
|
294
|
-
"Glob patterns to filter files by path or filename. If not set, include all.",
|
|
269
|
+
description: "Glob patterns to filter files by path or filename. If not set, include all.",
|
|
295
270
|
},
|
|
296
271
|
excludePatterns: {
|
|
297
272
|
anyOf: [{ type: "string" }, { type: "array", items: { type: "string" } }],
|
|
298
|
-
description:
|
|
299
|
-
"Glob patterns to exclude files by path or filename. If not set, exclude none.",
|
|
273
|
+
description: "Glob patterns to exclude files by path or filename. If not set, exclude none.",
|
|
300
274
|
},
|
|
301
275
|
useDefaultPatterns: {
|
|
302
276
|
type: "boolean",
|
|
303
|
-
description:
|
|
304
|
-
"Whether to use default include/exclude patterns. Defaults to true.",
|
|
277
|
+
description: "Whether to use default include/exclude patterns. Defaults to true.",
|
|
305
278
|
},
|
|
306
279
|
"doc-path": {
|
|
307
280
|
type: "string",
|
package/agents/publish-docs.mjs
CHANGED
|
@@ -1,125 +1,17 @@
|
|
|
1
|
-
import { join } from "node:path";
|
|
2
|
-
import { joinURL } from "ufo";
|
|
3
|
-
import open from "open";
|
|
4
|
-
import { publishDocs as publishDocsFn } from "@aigne/publish-docs";
|
|
5
|
-
import { createConnect } from "@aigne/cli/utils/load-aigne.js";
|
|
6
1
|
import { existsSync, mkdirSync } from "node:fs";
|
|
7
2
|
import { readFile, writeFile } from "node:fs/promises";
|
|
8
3
|
import { homedir } from "node:os";
|
|
4
|
+
import { basename, join } from "node:path";
|
|
5
|
+
import { createConnect } from "@aigne/aigne-hub";
|
|
6
|
+
import { publishDocs as publishDocsFn } from "@aigne/publish-docs";
|
|
7
|
+
import open from "open";
|
|
8
|
+
import { joinURL } from "ufo";
|
|
9
9
|
import { parse, stringify } from "yaml";
|
|
10
|
-
import { execSync } from "node:child_process";
|
|
11
|
-
import { basename } from "node:path";
|
|
12
10
|
import { loadConfigFromFile, saveValueToConfig } from "../utils/utils.mjs";
|
|
13
11
|
|
|
14
12
|
const WELLKNOWN_SERVICE_PATH_PREFIX = "/.well-known/service";
|
|
15
13
|
const DEFAULT_APP_URL = "https://docsmith.aigne.io";
|
|
16
14
|
|
|
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
15
|
/**
|
|
124
16
|
* Get access token from environment, config file, or prompt user for authorization
|
|
125
17
|
* @param {string} appUrl - The application URL
|
|
@@ -142,7 +34,7 @@ async function getAccessToken(appUrl) {
|
|
|
142
34
|
const data = await readFile(DOC_SMITH_ENV_FILE, "utf8");
|
|
143
35
|
if (data.includes("DOC_DISCUSS_KIT_ACCESS_TOKEN")) {
|
|
144
36
|
const envs = parse(data);
|
|
145
|
-
if (envs[hostname]
|
|
37
|
+
if (envs[hostname]?.DOC_DISCUSS_KIT_ACCESS_TOKEN) {
|
|
146
38
|
accessToken = envs[hostname].DOC_DISCUSS_KIT_ACCESS_TOKEN;
|
|
147
39
|
}
|
|
148
40
|
}
|
|
@@ -207,7 +99,7 @@ async function getAccessToken(appUrl) {
|
|
|
207
99
|
}
|
|
208
100
|
|
|
209
101
|
export default async function publishDocs(
|
|
210
|
-
{ docsDir, appUrl, boardId,
|
|
102
|
+
{ docsDir, appUrl, boardId, projectName, projectDesc, projectLogo },
|
|
211
103
|
options
|
|
212
104
|
) {
|
|
213
105
|
// Check if DOC_DISCUSS_KIT_URL is set in environment variables
|
|
@@ -222,7 +114,7 @@ export default async function publishDocs(
|
|
|
222
114
|
// Check if appUrl is default and not saved in config (only when not using env variable)
|
|
223
115
|
const config = await loadConfigFromFile();
|
|
224
116
|
const isDefaultAppUrl = appUrl === DEFAULT_APP_URL;
|
|
225
|
-
const hasAppUrlInConfig = config
|
|
117
|
+
const hasAppUrlInConfig = config?.appUrl;
|
|
226
118
|
|
|
227
119
|
if (!useEnvAppUrl && isDefaultAppUrl && !hasAppUrlInConfig) {
|
|
228
120
|
const choice = await options.prompts.select({
|
|
@@ -260,58 +152,52 @@ export default async function publishDocs(
|
|
|
260
152
|
|
|
261
153
|
const sidebarPath = join(docsDir, "_sidebar.md");
|
|
262
154
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
155
|
+
// Get project info from config
|
|
156
|
+
const projectInfo = {
|
|
157
|
+
name: projectName || config?.projectName || basename(process.cwd()),
|
|
158
|
+
description: projectDesc || config?.projectDesc || "",
|
|
159
|
+
icon: projectLogo || config?.projectLogo || "",
|
|
267
160
|
};
|
|
268
161
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
162
|
+
try {
|
|
163
|
+
const { success, boardId: newBoardId } = await publishDocsFn({
|
|
164
|
+
sidebarPath,
|
|
165
|
+
accessToken,
|
|
166
|
+
appUrl,
|
|
167
|
+
boardId,
|
|
168
|
+
autoCreateBoard: true,
|
|
169
|
+
// Pass additional project information if available
|
|
170
|
+
boardName: projectInfo.name,
|
|
171
|
+
boardDesc: projectInfo.description,
|
|
172
|
+
boardCover: projectInfo.icon,
|
|
173
|
+
});
|
|
278
174
|
|
|
279
|
-
|
|
280
|
-
success
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
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
|
-
});
|
|
175
|
+
// Save values to config.yaml if publish was successful
|
|
176
|
+
if (success) {
|
|
177
|
+
// Save appUrl to config only when not using environment variable
|
|
178
|
+
if (!useEnvAppUrl) {
|
|
179
|
+
await saveValueToConfig("appUrl", appUrl);
|
|
180
|
+
}
|
|
294
181
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
182
|
+
// Save boardId to config if it was auto-created
|
|
183
|
+
if (boardId !== newBoardId) {
|
|
184
|
+
await saveValueToConfig(
|
|
185
|
+
"boardId",
|
|
186
|
+
newBoardId,
|
|
187
|
+
"⚠️ Warning: boardId is auto-generated by system, please do not edit manually"
|
|
188
|
+
);
|
|
189
|
+
}
|
|
300
190
|
}
|
|
301
191
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
}
|
|
192
|
+
const message = `✅ Documentation Published Successfully!`;
|
|
193
|
+
return {
|
|
194
|
+
message,
|
|
195
|
+
};
|
|
196
|
+
} catch (error) {
|
|
197
|
+
return {
|
|
198
|
+
message: `❌ Failed to publish docs: ${error.message}`,
|
|
199
|
+
};
|
|
306
200
|
}
|
|
307
|
-
|
|
308
|
-
// const message = `## ✅ Documentation Published Successfully!
|
|
309
|
-
|
|
310
|
-
// Documentation is now available at: \`${docsUrl}\`
|
|
311
|
-
// `;
|
|
312
|
-
return {
|
|
313
|
-
// message,
|
|
314
|
-
};
|
|
315
201
|
}
|
|
316
202
|
|
|
317
203
|
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
|
package/agents/save-docs.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readdir, unlink, writeFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
+
import { shutdownMermaidWorkerPool } from "../utils/mermaid-worker-pool.mjs";
|
|
3
4
|
import { getCurrentGitHead, saveGitHeadToConfig } from "../utils/utils.mjs";
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -14,8 +15,9 @@ export default async function saveDocs({
|
|
|
14
15
|
docsDir,
|
|
15
16
|
translateLanguages = [],
|
|
16
17
|
locale,
|
|
18
|
+
projectInfoMessage,
|
|
17
19
|
}) {
|
|
18
|
-
const
|
|
20
|
+
const _results = [];
|
|
19
21
|
// Save current git HEAD to config.yaml for change detection
|
|
20
22
|
try {
|
|
21
23
|
const gitHead = getCurrentGitHead();
|
|
@@ -35,20 +37,16 @@ export default async function saveDocs({
|
|
|
35
37
|
|
|
36
38
|
// Clean up invalid .md files that are no longer in the structure plan
|
|
37
39
|
try {
|
|
38
|
-
await cleanupInvalidFiles(
|
|
39
|
-
structurePlan,
|
|
40
|
-
docsDir,
|
|
41
|
-
translateLanguages,
|
|
42
|
-
locale
|
|
43
|
-
);
|
|
40
|
+
await cleanupInvalidFiles(structurePlan, docsDir, translateLanguages, locale);
|
|
44
41
|
} catch (err) {
|
|
45
42
|
console.error("Failed to cleanup invalid .md files:", err.message);
|
|
46
43
|
}
|
|
47
44
|
|
|
48
45
|
const message = `## ✅ Documentation Generated Successfully!
|
|
49
46
|
|
|
50
|
-
Successfully generated **${structurePlan.length}** documents and saved to:
|
|
51
|
-
|
|
47
|
+
Successfully generated **${structurePlan.length}** documents and saved to:
|
|
48
|
+
\`${docsDir}\`
|
|
49
|
+
${projectInfoMessage || ""}
|
|
52
50
|
### 🚀 Next Steps
|
|
53
51
|
|
|
54
52
|
1. Publish Documentation
|
|
@@ -78,6 +76,13 @@ export default async function saveDocs({
|
|
|
78
76
|
---
|
|
79
77
|
`;
|
|
80
78
|
|
|
79
|
+
// Shutdown mermaid worker pool to ensure clean exit
|
|
80
|
+
try {
|
|
81
|
+
await shutdownMermaidWorkerPool();
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.warn("Failed to shutdown mermaid worker pool:", error.message);
|
|
84
|
+
}
|
|
85
|
+
|
|
81
86
|
return {
|
|
82
87
|
message,
|
|
83
88
|
};
|
|
@@ -102,12 +107,7 @@ function generateFileName(flatName, language) {
|
|
|
102
107
|
* @param {string} locale - Main language locale (e.g., 'en', 'zh', 'fr')
|
|
103
108
|
* @returns {Promise<Array<{ path: string, success: boolean, error?: string }>>}
|
|
104
109
|
*/
|
|
105
|
-
async function cleanupInvalidFiles(
|
|
106
|
-
structurePlan,
|
|
107
|
-
docsDir,
|
|
108
|
-
translateLanguages,
|
|
109
|
-
locale
|
|
110
|
-
) {
|
|
110
|
+
async function cleanupInvalidFiles(structurePlan, docsDir, translateLanguages, locale) {
|
|
111
111
|
const results = [];
|
|
112
112
|
|
|
113
113
|
try {
|
|
@@ -135,7 +135,7 @@ async function cleanupInvalidFiles(
|
|
|
135
135
|
|
|
136
136
|
// Find files to delete (files that are not in expectedFiles and not _sidebar.md)
|
|
137
137
|
const filesToDelete = mdFiles.filter(
|
|
138
|
-
(file) => !expectedFiles.has(file) && file !== "_sidebar.md"
|
|
138
|
+
(file) => !expectedFiles.has(file) && file !== "_sidebar.md",
|
|
139
139
|
);
|
|
140
140
|
|
|
141
141
|
// Delete invalid files
|
|
@@ -158,9 +158,7 @@ async function cleanupInvalidFiles(
|
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
if (filesToDelete.length > 0) {
|
|
161
|
-
console.log(
|
|
162
|
-
`Cleaned up ${filesToDelete.length} invalid .md files from ${docsDir}`
|
|
163
|
-
);
|
|
161
|
+
console.log(`Cleaned up ${filesToDelete.length} invalid .md files from ${docsDir}`);
|
|
164
162
|
}
|
|
165
163
|
} catch (err) {
|
|
166
164
|
// If docsDir doesn't exist or can't be read, that's okay
|
|
@@ -199,7 +197,7 @@ function generateSidebar(structurePlan) {
|
|
|
199
197
|
for (const key of Object.keys(node)) {
|
|
200
198
|
const item = node[key];
|
|
201
199
|
const fullSegments = [...parentSegments, key];
|
|
202
|
-
const flatFile = fullSegments.join("-")
|
|
200
|
+
const flatFile = `${fullSegments.join("-")}.md`;
|
|
203
201
|
if (item.__title) {
|
|
204
202
|
const realIndent = item.__parentId === null ? "" : indent;
|
|
205
203
|
out += `${realIndent}* [${item.__title}](/${flatFile})\n`;
|
package/agents/save-output.mjs
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import { promises as fs } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
|
|
4
|
-
export default async function saveOutput({
|
|
5
|
-
savePath,
|
|
6
|
-
fileName,
|
|
7
|
-
saveKey,
|
|
8
|
-
...rest
|
|
9
|
-
}) {
|
|
4
|
+
export default async function saveOutput({ savePath, fileName, saveKey, ...rest }) {
|
|
10
5
|
if (!(saveKey in rest)) {
|
|
11
6
|
console.warn(`saveKey "${saveKey}" not found in input, skip saving.`);
|
|
12
7
|
return {
|
|
@@ -17,9 +12,7 @@ export default async function saveOutput({
|
|
|
17
12
|
|
|
18
13
|
const value = rest[saveKey];
|
|
19
14
|
const content =
|
|
20
|
-
typeof value === "object" && value !== null
|
|
21
|
-
? JSON.stringify(value, null, 2)
|
|
22
|
-
: String(value);
|
|
15
|
+
typeof value === "object" && value !== null ? JSON.stringify(value, null, 2) : String(value);
|
|
23
16
|
await fs.mkdir(savePath, { recursive: true });
|
|
24
17
|
const filePath = join(savePath, fileName);
|
|
25
18
|
await fs.writeFile(filePath, content, "utf8");
|