@aigne/doc-smith 0.9.8-alpha.3 → 0.9.8-alpha.4

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 (257) hide show
  1. package/CLAUDE.md +43 -0
  2. package/README.md +94 -250
  3. package/aigne.yaml +2 -149
  4. package/doc-smith/SKILL.md +117 -0
  5. package/doc-smith/references/changeset_schema.md +118 -0
  6. package/doc-smith/references/document_structure_schema.md +139 -0
  7. package/doc-smith/references/document_update_guide.md +193 -0
  8. package/doc-smith/references/structure_confirmation_guide.md +133 -0
  9. package/doc-smith/references/structure_planning_guide.md +146 -0
  10. package/doc-smith/references/user_intent_guide.md +172 -0
  11. package/doc-smith.yaml +114 -0
  12. package/main-system-prompt.md +56 -0
  13. package/package.json +3 -69
  14. package/scripts/README.md +90 -0
  15. package/scripts/install.sh +86 -0
  16. package/scripts/uninstall.sh +52 -0
  17. package/CHANGELOG.md +0 -994
  18. package/LICENSE +0 -93
  19. package/agentic-agents/common/base-info.md +0 -53
  20. package/agentic-agents/common/completer.md +0 -54
  21. package/agentic-agents/common/planner.md +0 -168
  22. package/agentic-agents/common/worker.md +0 -93
  23. package/agentic-agents/create/index.yaml +0 -129
  24. package/agentic-agents/create/objective.md +0 -44
  25. package/agentic-agents/create/set-custom-prompt.mjs +0 -27
  26. package/agentic-agents/detail/index.yaml +0 -95
  27. package/agentic-agents/detail/objective.md +0 -9
  28. package/agentic-agents/detail/set-custom-prompt.mjs +0 -88
  29. package/agentic-agents/predict-resources/index.yaml +0 -44
  30. package/agentic-agents/predict-resources/instructions.md +0 -61
  31. package/agentic-agents/structure/design-rules.md +0 -39
  32. package/agentic-agents/structure/index.yaml +0 -86
  33. package/agentic-agents/structure/objective.md +0 -14
  34. package/agentic-agents/structure/review-criteria.md +0 -55
  35. package/agentic-agents/structure/set-custom-prompt.mjs +0 -78
  36. package/agentic-agents/utils/init-workspace-cache.mjs +0 -171
  37. package/agentic-agents/utils/load-base-sources.mjs +0 -20
  38. package/agentic-agents/workspace-cache-sharing-design.md +0 -671
  39. package/agents/chat/chat-system.md +0 -38
  40. package/agents/chat/index.mjs +0 -59
  41. package/agents/chat/skills/generate-document.yaml +0 -15
  42. package/agents/chat/skills/list-documents.mjs +0 -15
  43. package/agents/chat/skills/update-document.yaml +0 -24
  44. package/agents/clear/choose-contents.mjs +0 -192
  45. package/agents/clear/clear-auth-tokens.mjs +0 -88
  46. package/agents/clear/clear-deployment-config.mjs +0 -49
  47. package/agents/clear/clear-document-config.mjs +0 -36
  48. package/agents/clear/clear-document-structure.mjs +0 -102
  49. package/agents/clear/clear-generated-docs.mjs +0 -142
  50. package/agents/clear/clear-media-description.mjs +0 -129
  51. package/agents/clear/index.yaml +0 -26
  52. package/agents/create/analyze-diagram-type-llm.yaml +0 -160
  53. package/agents/create/analyze-diagram-type.mjs +0 -297
  54. package/agents/create/check-document-structure.yaml +0 -30
  55. package/agents/create/check-need-generate-structure.mjs +0 -105
  56. package/agents/create/document-structure-tools/add-document.mjs +0 -85
  57. package/agents/create/document-structure-tools/delete-document.mjs +0 -116
  58. package/agents/create/document-structure-tools/move-document.mjs +0 -109
  59. package/agents/create/document-structure-tools/update-document.mjs +0 -84
  60. package/agents/create/generate-diagram-image.yaml +0 -60
  61. package/agents/create/generate-structure.yaml +0 -117
  62. package/agents/create/index.yaml +0 -49
  63. package/agents/create/refine-document-structure.yaml +0 -12
  64. package/agents/create/replace-d2-with-image.mjs +0 -625
  65. package/agents/create/update-document-structure.yaml +0 -54
  66. package/agents/create/user-add-document/add-documents-to-structure.mjs +0 -90
  67. package/agents/create/user-add-document/find-documents-to-add-links.yaml +0 -47
  68. package/agents/create/user-add-document/index.yaml +0 -46
  69. package/agents/create/user-add-document/prepare-documents-to-translate.mjs +0 -22
  70. package/agents/create/user-add-document/print-add-document-summary.mjs +0 -63
  71. package/agents/create/user-add-document/review-documents-with-new-links.mjs +0 -110
  72. package/agents/create/user-remove-document/find-documents-with-invalid-links.mjs +0 -78
  73. package/agents/create/user-remove-document/index.yaml +0 -40
  74. package/agents/create/user-remove-document/prepare-documents-to-translate.mjs +0 -22
  75. package/agents/create/user-remove-document/print-remove-document-summary.mjs +0 -53
  76. package/agents/create/user-remove-document/remove-documents-from-structure.mjs +0 -99
  77. package/agents/create/user-remove-document/review-documents-with-invalid-links.mjs +0 -115
  78. package/agents/create/user-review-document-structure.mjs +0 -140
  79. package/agents/create/utils/init-current-content.mjs +0 -34
  80. package/agents/create/utils/merge-document-structures.mjs +0 -30
  81. package/agents/evaluate/code-snippet.mjs +0 -97
  82. package/agents/evaluate/document-structure.yaml +0 -67
  83. package/agents/evaluate/document.yaml +0 -82
  84. package/agents/evaluate/generate-report.mjs +0 -85
  85. package/agents/evaluate/index.yaml +0 -46
  86. package/agents/history/index.yaml +0 -6
  87. package/agents/history/view.mjs +0 -78
  88. package/agents/init/check.mjs +0 -16
  89. package/agents/init/index.mjs +0 -275
  90. package/agents/init/validate.mjs +0 -16
  91. package/agents/localize/choose-language.mjs +0 -107
  92. package/agents/localize/index.yaml +0 -58
  93. package/agents/localize/record-translation-history.mjs +0 -23
  94. package/agents/localize/translate-document.yaml +0 -24
  95. package/agents/localize/translate-multilingual.yaml +0 -51
  96. package/agents/media/batch-generate-media-description.yaml +0 -46
  97. package/agents/media/generate-media-description.yaml +0 -50
  98. package/agents/media/load-media-description.mjs +0 -256
  99. package/agents/prefs/index.mjs +0 -203
  100. package/agents/publish/index.yaml +0 -26
  101. package/agents/publish/publish-docs.mjs +0 -356
  102. package/agents/publish/translate-meta.mjs +0 -103
  103. package/agents/schema/document-structure-item.yaml +0 -26
  104. package/agents/schema/document-structure-refine-item.yaml +0 -23
  105. package/agents/schema/document-structure.yaml +0 -29
  106. package/agents/update/batch-generate-document.yaml +0 -27
  107. package/agents/update/batch-update-document.yaml +0 -7
  108. package/agents/update/check-diagram-flag.mjs +0 -116
  109. package/agents/update/check-document.mjs +0 -162
  110. package/agents/update/check-generate-diagram.mjs +0 -106
  111. package/agents/update/check-sync-image-flag.mjs +0 -55
  112. package/agents/update/check-update-is-single.mjs +0 -53
  113. package/agents/update/document-tools/update-document-content.mjs +0 -303
  114. package/agents/update/generate-diagram.yaml +0 -63
  115. package/agents/update/generate-document.yaml +0 -70
  116. package/agents/update/handle-document-update.yaml +0 -103
  117. package/agents/update/index.yaml +0 -79
  118. package/agents/update/pre-check-generate-diagram.yaml +0 -44
  119. package/agents/update/save-and-translate-document.mjs +0 -76
  120. package/agents/update/sync-images-and-exit.mjs +0 -148
  121. package/agents/update/update-document-detail.yaml +0 -71
  122. package/agents/update/update-single/update-single-document-detail.mjs +0 -280
  123. package/agents/update/update-single-document.yaml +0 -7
  124. package/agents/update/user-review-document.mjs +0 -272
  125. package/agents/utils/action-success.mjs +0 -16
  126. package/agents/utils/analyze-document-feedback-intent.yaml +0 -32
  127. package/agents/utils/analyze-feedback-intent.mjs +0 -136
  128. package/agents/utils/analyze-structure-feedback-intent.yaml +0 -29
  129. package/agents/utils/check-detail-result.mjs +0 -38
  130. package/agents/utils/check-feedback-refiner.mjs +0 -81
  131. package/agents/utils/choose-docs.mjs +0 -293
  132. package/agents/utils/document-icon-generate.yaml +0 -52
  133. package/agents/utils/document-title-streamline.yaml +0 -48
  134. package/agents/utils/ensure-document-icons.mjs +0 -129
  135. package/agents/utils/exit.mjs +0 -6
  136. package/agents/utils/feedback-refiner.yaml +0 -50
  137. package/agents/utils/find-item-by-path.mjs +0 -114
  138. package/agents/utils/find-user-preferences-by-path.mjs +0 -37
  139. package/agents/utils/format-document-structure.mjs +0 -35
  140. package/agents/utils/generate-document-or-skip.mjs +0 -41
  141. package/agents/utils/handle-diagram-operations.mjs +0 -263
  142. package/agents/utils/load-all-document-content.mjs +0 -30
  143. package/agents/utils/load-document-all-content.mjs +0 -84
  144. package/agents/utils/load-sources.mjs +0 -405
  145. package/agents/utils/map-reasoning-effort-level.mjs +0 -15
  146. package/agents/utils/post-generate.mjs +0 -144
  147. package/agents/utils/read-current-document-content.mjs +0 -46
  148. package/agents/utils/save-doc-translation.mjs +0 -61
  149. package/agents/utils/save-doc.mjs +0 -88
  150. package/agents/utils/save-output.mjs +0 -26
  151. package/agents/utils/save-sidebar.mjs +0 -51
  152. package/agents/utils/skip-if-content-exists.mjs +0 -27
  153. package/agents/utils/streamline-document-titles-if-needed.mjs +0 -88
  154. package/agents/utils/transform-detail-data-sources.mjs +0 -45
  155. package/agents/utils/update-branding.mjs +0 -84
  156. package/assets/report-template/report.html +0 -198
  157. package/docs-mcp/analyze-content-relevance.yaml +0 -50
  158. package/docs-mcp/analyze-docs-relevance.yaml +0 -59
  159. package/docs-mcp/docs-search.yaml +0 -42
  160. package/docs-mcp/get-docs-detail.mjs +0 -41
  161. package/docs-mcp/get-docs-structure.mjs +0 -16
  162. package/docs-mcp/read-doc-content.mjs +0 -119
  163. package/prompts/common/document/content-rules-core.md +0 -20
  164. package/prompts/common/document/markdown-syntax-rules.md +0 -65
  165. package/prompts/common/document/media-file-list-usage-rules.md +0 -18
  166. package/prompts/common/document/openapi-usage-rules.md +0 -189
  167. package/prompts/common/document/role-and-personality.md +0 -16
  168. package/prompts/common/document/user-preferences.md +0 -9
  169. package/prompts/common/document-structure/conflict-resolution-guidance.md +0 -16
  170. package/prompts/common/document-structure/document-icon-generate.md +0 -116
  171. package/prompts/common/document-structure/document-structure-rules.md +0 -43
  172. package/prompts/common/document-structure/document-title-streamline.md +0 -86
  173. package/prompts/common/document-structure/glossary.md +0 -7
  174. package/prompts/common/document-structure/intj-traits.md +0 -5
  175. package/prompts/common/document-structure/openapi-usage-rules.md +0 -28
  176. package/prompts/common/document-structure/output-constraints.md +0 -18
  177. package/prompts/common/document-structure/user-locale-rules.md +0 -10
  178. package/prompts/common/document-structure/user-preferences.md +0 -9
  179. package/prompts/detail/custom/admonition-usage-rules.md +0 -94
  180. package/prompts/detail/custom/code-block-usage-rules.md +0 -163
  181. package/prompts/detail/custom/custom-components/x-card-usage-rules.md +0 -63
  182. package/prompts/detail/custom/custom-components/x-cards-usage-rules.md +0 -83
  183. package/prompts/detail/custom/custom-components/x-field-desc-usage-rules.md +0 -120
  184. package/prompts/detail/custom/custom-components/x-field-group-usage-rules.md +0 -80
  185. package/prompts/detail/custom/custom-components/x-field-usage-rules.md +0 -189
  186. package/prompts/detail/custom/custom-components-usage-rules.md +0 -18
  187. package/prompts/detail/diagram/generate-image-system.md +0 -135
  188. package/prompts/detail/diagram/generate-image-user.md +0 -32
  189. package/prompts/detail/diagram/guide.md +0 -29
  190. package/prompts/detail/diagram/official-examples.md +0 -712
  191. package/prompts/detail/diagram/pre-check.md +0 -23
  192. package/prompts/detail/diagram/role-and-personality.md +0 -2
  193. package/prompts/detail/diagram/rules.md +0 -46
  194. package/prompts/detail/diagram/system-prompt.md +0 -1139
  195. package/prompts/detail/diagram/user-prompt.md +0 -43
  196. package/prompts/detail/generate/detail-example.md +0 -457
  197. package/prompts/detail/generate/document-rules.md +0 -45
  198. package/prompts/detail/generate/system-prompt.md +0 -61
  199. package/prompts/detail/generate/user-prompt.md +0 -99
  200. package/prompts/detail/jsx/rules.md +0 -6
  201. package/prompts/detail/update/system-prompt.md +0 -121
  202. package/prompts/detail/update/user-prompt.md +0 -41
  203. package/prompts/evaluate/document-structure.md +0 -93
  204. package/prompts/evaluate/document.md +0 -149
  205. package/prompts/media/media-description/system-prompt.md +0 -43
  206. package/prompts/media/media-description/user-prompt.md +0 -17
  207. package/prompts/structure/check-document-structure.md +0 -93
  208. package/prompts/structure/document-rules.md +0 -21
  209. package/prompts/structure/find-documents-to-add-links.md +0 -52
  210. package/prompts/structure/generate/system-prompt.md +0 -13
  211. package/prompts/structure/generate/user-prompt.md +0 -137
  212. package/prompts/structure/review/structure-review-system.md +0 -81
  213. package/prompts/structure/structure-example.md +0 -89
  214. package/prompts/structure/structure-getting-started.md +0 -10
  215. package/prompts/structure/update/system-prompt.md +0 -93
  216. package/prompts/structure/update/user-prompt.md +0 -43
  217. package/prompts/translate/admonition.md +0 -20
  218. package/prompts/translate/code-block.md +0 -33
  219. package/prompts/translate/glossary.md +0 -6
  220. package/prompts/translate/translate-document.md +0 -305
  221. package/prompts/utils/analyze-document-feedback-intent.md +0 -54
  222. package/prompts/utils/analyze-structure-feedback-intent.md +0 -43
  223. package/prompts/utils/feedback-refiner.md +0 -105
  224. package/types/document-schema.mjs +0 -55
  225. package/types/document-structure-schema.mjs +0 -261
  226. package/utils/auth-utils.mjs +0 -275
  227. package/utils/blocklet.mjs +0 -104
  228. package/utils/check-document-has-diagram.mjs +0 -95
  229. package/utils/conflict-detector.mjs +0 -149
  230. package/utils/constants/index.mjs +0 -620
  231. package/utils/constants/linter.mjs +0 -102
  232. package/utils/d2-utils.mjs +0 -198
  233. package/utils/debug.mjs +0 -3
  234. package/utils/delete-diagram-images.mjs +0 -99
  235. package/utils/deploy.mjs +0 -86
  236. package/utils/docs-finder-utils.mjs +0 -623
  237. package/utils/evaluate/report-utils.mjs +0 -132
  238. package/utils/extract-api.mjs +0 -32
  239. package/utils/file-utils.mjs +0 -960
  240. package/utils/history-utils.mjs +0 -203
  241. package/utils/icon-map.mjs +0 -26
  242. package/utils/image-compress.mjs +0 -75
  243. package/utils/kroki-utils.mjs +0 -173
  244. package/utils/linter/index.mjs +0 -50
  245. package/utils/load-config.mjs +0 -107
  246. package/utils/markdown/index.mjs +0 -26
  247. package/utils/markdown-checker.mjs +0 -694
  248. package/utils/mermaid-validator.mjs +0 -140
  249. package/utils/mermaid-worker-pool.mjs +0 -250
  250. package/utils/mermaid-worker.mjs +0 -233
  251. package/utils/openapi/index.mjs +0 -28
  252. package/utils/preferences-utils.mjs +0 -175
  253. package/utils/request.mjs +0 -10
  254. package/utils/store/index.mjs +0 -45
  255. package/utils/sync-diagram-to-translations.mjs +0 -262
  256. package/utils/upload-files.mjs +0 -231
  257. package/utils/utils.mjs +0 -1354
@@ -1,175 +0,0 @@
1
- import { randomBytes } from "node:crypto";
2
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
- import { join } from "node:path";
4
- import { parse, stringify } from "yaml";
5
-
6
- const PREFERENCES_DIR = ".aigne/doc-smith";
7
- const PREFERENCES_FILE = "preferences.yml";
8
-
9
- /**
10
- * Generate a random preference ID
11
- * @returns {string} Random ID with pref_ prefix
12
- */
13
- function generatePreferenceId() {
14
- return `pref_${randomBytes(8).toString("hex")}`;
15
- }
16
-
17
- /**
18
- * Get the full path to the preferences file
19
- * @returns {string} Full path to preferences.yml
20
- */
21
- function getPreferencesFilePath() {
22
- return join(process.cwd(), PREFERENCES_DIR, PREFERENCES_FILE);
23
- }
24
-
25
- /**
26
- * Ensure the preferences directory exists
27
- */
28
- function ensurePreferencesDir() {
29
- const preferencesDir = join(process.cwd(), PREFERENCES_DIR);
30
- if (!existsSync(preferencesDir)) {
31
- mkdirSync(preferencesDir, { recursive: true });
32
- }
33
- }
34
-
35
- /**
36
- * Read existing preferences from file
37
- * @returns {Object} Preferences object with rules array
38
- */
39
- export function readPreferences() {
40
- const filePath = getPreferencesFilePath();
41
-
42
- if (!existsSync(filePath)) {
43
- return { rules: [] };
44
- }
45
-
46
- try {
47
- const content = readFileSync(filePath, "utf8");
48
- const preferences = parse(content);
49
- return preferences || { rules: [] };
50
- } catch (error) {
51
- console.warn(`Warning: Failed to read preferences file at ${filePath}: ${error.message}`);
52
- return { rules: [] };
53
- }
54
- }
55
-
56
- /**
57
- * Write preferences to file
58
- * @param {Object} preferences - Preferences object to save
59
- */
60
- export function writePreferences(preferences) {
61
- ensurePreferencesDir();
62
- const filePath = getPreferencesFilePath();
63
-
64
- try {
65
- const yamlContent = stringify(preferences, {
66
- indent: 2,
67
- lineWidth: 120,
68
- });
69
-
70
- writeFileSync(filePath, yamlContent, "utf8");
71
- } catch (error) {
72
- throw new Error(`Failed to write preferences file: ${error.message}`);
73
- }
74
- }
75
-
76
- /**
77
- * Add a new preference rule
78
- * @param {Object} ruleData - Rule data from feedbackRefiner
79
- * @param {string} ruleData.rule - The rule text
80
- * @param {string} ruleData.scope - Rule scope (global, structure, document, translation)
81
- * @param {boolean} ruleData.limitToInputPaths - Whether to limit to input paths
82
- * @param {string} feedback - Original user feedback
83
- * @param {string[]} [paths] - Optional paths to save with the rule
84
- * @returns {Object} The created preference rule
85
- */
86
- export function addPreferenceRule(ruleData, feedback, paths = []) {
87
- const preferences = readPreferences();
88
-
89
- const newRule = {
90
- id: generatePreferenceId(),
91
- active: true,
92
- scope: ruleData.scope,
93
- rule: ruleData.rule,
94
- feedback: feedback,
95
- createdAt: new Date().toISOString(),
96
- };
97
-
98
- // Add paths if limitToInputPaths is true and paths are provided
99
- if (ruleData.limitToInputPaths && paths && paths.length > 0) {
100
- newRule.paths = paths;
101
- }
102
-
103
- // Add the new rule to the beginning of the array (newest first)
104
- preferences.rules.unshift(newRule);
105
-
106
- writePreferences(preferences);
107
-
108
- return newRule;
109
- }
110
-
111
- /**
112
- * Get all active preference rules for a specific scope
113
- * @param {string} scope - The scope to filter by (global, structure, document, translation)
114
- * @param {string[]} [currentPaths] - Current paths to match against rules with path restrictions
115
- * @returns {Object[]} Array of matching active rules
116
- */
117
- export function getActiveRulesForScope(scope, currentPaths = []) {
118
- const preferences = readPreferences();
119
-
120
- return preferences.rules.filter((rule) => {
121
- // Must be active and match scope
122
- if (!rule.active || rule.scope !== scope) {
123
- return false;
124
- }
125
-
126
- // If rule has path restrictions, check if any current path matches
127
- if (rule.paths && rule.paths.length > 0) {
128
- if (currentPaths.length === 0) {
129
- return false; // Rule has path restrictions but no current paths provided
130
- }
131
-
132
- // Check if any current path matches any rule path pattern
133
- return currentPaths.some((currentPath) => rule.paths.includes(currentPath));
134
- }
135
-
136
- return true; // No path restrictions, include the rule
137
- });
138
- }
139
-
140
- /**
141
- * Deactivate a preference rule by ID
142
- * @param {string} ruleId - The ID of the rule to deactivate
143
- * @returns {boolean} True if rule was found and deactivated
144
- */
145
- export function deactivateRule(ruleId) {
146
- const preferences = readPreferences();
147
- const rule = preferences.rules.find((r) => r.id === ruleId);
148
-
149
- if (rule) {
150
- rule.active = false;
151
- writePreferences(preferences);
152
- return true;
153
- }
154
-
155
- return false;
156
- }
157
-
158
- /**
159
- * Remove a preference rule by ID
160
- * @param {string} ruleId - The ID of the rule to remove
161
- * @returns {boolean} True if rule was found and removed
162
- */
163
- export function removeRule(ruleId) {
164
- const preferences = readPreferences();
165
- const initialLength = preferences.rules.length;
166
-
167
- preferences.rules = preferences.rules.filter((r) => r.id !== ruleId);
168
-
169
- if (preferences.rules.length < initialLength) {
170
- writePreferences(preferences);
171
- return true;
172
- }
173
-
174
- return false;
175
- }
package/utils/request.mjs DELETED
@@ -1,10 +0,0 @@
1
- export async function requestWithAuthToken(url, options, authToken) {
2
- if (!authToken) {
3
- console.error("No authentication token provided");
4
- }
5
- const response = await fetch(url, {
6
- ...options,
7
- headers: { ...options.headers, Authorization: `Bearer ${authToken}` },
8
- });
9
- return response.json();
10
- }
@@ -1,45 +0,0 @@
1
- import { access, rm } from "node:fs/promises";
2
- import { homedir } from "node:os";
3
- import { join } from "node:path";
4
- import createSecretStore, { FileStore } from "@aigne/secrets";
5
-
6
- export async function createStore() {
7
- const filepath = join(homedir(), ".aigne", "doc-smith-connected.yaml");
8
- const secretStore = await createSecretStore({
9
- filepath,
10
- serviceName: "aigne-doc-smith-publish",
11
- });
12
-
13
- async function migrate() {
14
- // system doesn't support keyring
15
- if (secretStore instanceof FileStore) {
16
- return true;
17
- }
18
- // already migrated
19
- try {
20
- await access(filepath);
21
- } catch {
22
- return true;
23
- }
24
-
25
- const fileStore = new FileStore({ filepath });
26
- const map = await fileStore.listMap();
27
- for (const [key, value] of Object.entries(map)) {
28
- await secretStore.setItem(key, value);
29
- }
30
- await rm(filepath);
31
- }
32
-
33
- async function clear() {
34
- const map = await secretStore.listMap();
35
- for (const key of Object.keys(map)) {
36
- await secretStore.deleteItem(key);
37
- }
38
- }
39
-
40
- await migrate();
41
-
42
- secretStore.clear = clear;
43
-
44
- return secretStore;
45
- }
@@ -1,262 +0,0 @@
1
- import { readdirSync } from "node:fs";
2
- import { readFileContent } from "./docs-finder-utils.mjs";
3
- import { debug } from "./debug.mjs";
4
- import path from "node:path";
5
- import fs from "fs-extra";
6
- import { d2CodeBlockRegex, diagramImageWithPathRegex } from "./d2-utils.mjs";
7
-
8
- /**
9
- * Find all translation files for a document
10
- * @param {string} docPath - Document path (e.g., "/guides/getting-started")
11
- * @param {string} docsDir - Documentation directory
12
- * @param {string} locale - Main language locale (e.g., "en")
13
- * @returns {Promise<Array<{language: string, fileName: string}>>} - Array of translation file info
14
- */
15
- async function findTranslationFiles(docPath, docsDir, locale) {
16
- // Convert path to flat filename format
17
- const flatName = docPath.replace(/^\//, "").replace(/\//g, "-");
18
-
19
- try {
20
- const files = readdirSync(docsDir);
21
- const translationFiles = [];
22
- const mainFileName = locale === "en" ? `${flatName}.md` : `${flatName}.${locale}.md`;
23
-
24
- // Filter files to find translation files matching the pattern
25
- for (const file of files) {
26
- if (!file.endsWith(".md")) continue;
27
- if (file === mainFileName) continue; // Skip main language file
28
-
29
- // Case 1: File without language suffix (xxx.md) - this is English translation when main language is not English
30
- if (file === `${flatName}.md` && locale !== "en") {
31
- translationFiles.push({
32
- language: "en",
33
- fileName: file,
34
- });
35
- continue;
36
- }
37
-
38
- // Case 2: File with language suffix (xxx.{lang}.md) - all other translations
39
- if (file.startsWith(`${flatName}.`) && file.match(/\.\w+(-\w+)?\.md$/)) {
40
- const langMatch = file.match(/\.(\w+(-\w+)?)\.md$/);
41
- if (langMatch && langMatch[1] !== locale) {
42
- translationFiles.push({
43
- language: langMatch[1],
44
- fileName: file,
45
- });
46
- }
47
- }
48
- }
49
-
50
- return translationFiles;
51
- } catch (error) {
52
- debug(`⚠️ Could not read translation files from ${docsDir}: ${error.message}`);
53
- return [];
54
- }
55
- }
56
-
57
- /**
58
- * Extract diagram image paths from content
59
- * @param {string} content - Document content
60
- * @returns {Array<{path: string, fullMatch: string, index: number}>} - Array of diagram image info
61
- */
62
- function extractDiagramImagePaths(content) {
63
- const images = [];
64
- const matches = Array.from(content.matchAll(diagramImageWithPathRegex));
65
-
66
- for (const match of matches) {
67
- images.push({
68
- path: match[1],
69
- fullMatch: match[0],
70
- index: match.index,
71
- });
72
- }
73
-
74
- return images;
75
- }
76
-
77
- /**
78
- * Replace diagram images in translation files
79
- * @param {string} mainContent - Main document content (already updated)
80
- * @param {string} docPath - Document path
81
- * @param {string} docsDir - Documentation directory
82
- * @param {string} locale - Main language locale
83
- * @param {string} operationType - Operation type: "delete", "add", "update", or "sync" (default)
84
- * @returns {Promise<{updated: number, skipped: number, errors: Array}>} - Sync result
85
- */
86
- export async function syncDiagramToTranslations(
87
- mainContent,
88
- docPath,
89
- docsDir,
90
- locale = "en",
91
- operationType = "sync",
92
- ) {
93
- const result = {
94
- updated: 0,
95
- skipped: 0,
96
- errors: [],
97
- };
98
-
99
- // Find all translation files
100
- const translationFiles = await findTranslationFiles(docPath, docsDir, locale);
101
-
102
- if (translationFiles.length === 0) {
103
- debug("ℹ️ No translation files found, skipping sync");
104
- return result;
105
- }
106
-
107
- // Extract diagram images from updated main content
108
- const mainImages = extractDiagramImagePaths(mainContent);
109
-
110
- // If no diagrams in main content and operation is not delete, skip sync
111
- // For delete operations, we need to process translations even if main has 0 diagrams
112
- // to remove diagrams from translation files
113
- if (mainImages.length === 0 && operationType !== "delete") {
114
- debug("ℹ️ No diagram images in main content, skipping sync");
115
- return result;
116
- }
117
-
118
- // Process each translation file
119
- for (const { fileName } of translationFiles) {
120
- try {
121
- const translationFilePath = path.join(docsDir, fileName);
122
- const translationContent = await readFileContent(docsDir, fileName);
123
-
124
- // Check for null or undefined (file read failure), but allow empty string (valid content)
125
- if (translationContent === null || translationContent === undefined) {
126
- debug(`⚠️ Could not read translation file: ${fileName}`);
127
- result.skipped++;
128
- continue;
129
- }
130
-
131
- let hasChanges = false;
132
- let updatedContent = translationContent;
133
-
134
- // Strategy 1: Replace D2 code blocks with AI images (if main doc switched from D2 to AI)
135
- const translationD2Blocks = Array.from(translationContent.matchAll(d2CodeBlockRegex));
136
- if (translationD2Blocks.length > 0 && mainImages.length > 0) {
137
- // If main doc has AI images and translation has D2 blocks, replace them by index
138
- for (let i = 0; i < Math.min(translationD2Blocks.length, mainImages.length); i++) {
139
- const d2Match = translationD2Blocks[i];
140
- const mainImage = mainImages[i];
141
-
142
- if (d2Match && mainImage) {
143
- // Replace D2 block with AI image from main doc
144
- updatedContent = updatedContent.replace(d2Match[0], mainImage.fullMatch);
145
- hasChanges = true;
146
- debug(`🔄 Replaced D2 block with AI image in ${fileName} (index ${i})`);
147
- }
148
- }
149
- }
150
-
151
- // Strategy 2: Replace old image paths with new ones (if paths changed)
152
- const translationImages = extractDiagramImagePaths(updatedContent); // Re-extract after D2 replacement
153
- if (mainImages.length > 0) {
154
- for (let i = 0; i < Math.min(translationImages.length, mainImages.length); i++) {
155
- const translationImage = translationImages[i];
156
- const mainImage = mainImages[i];
157
-
158
- // If image path changed, update it
159
- if (translationImage && mainImage && translationImage.path !== mainImage.path) {
160
- // Replace old image path with new one (escape special regex characters)
161
- const escapedPath = translationImage.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
162
- const oldImagePattern = new RegExp(
163
- `<!--\\s*DIAGRAM_IMAGE_START:[^>]+-->\\s*!\\[[^\\]]*\\]\\(${escapedPath}\\)\\s*<!--\\s*DIAGRAM_IMAGE_END\\s*-->`,
164
- "g",
165
- );
166
- updatedContent = updatedContent.replace(oldImagePattern, mainImage.fullMatch);
167
- hasChanges = true;
168
- debug(
169
- `🔄 Updated image path in ${fileName} (index ${i}): ${translationImage.path} -> ${mainImage.path}`,
170
- );
171
- }
172
- }
173
- }
174
-
175
- // Strategy 3: If translation has fewer images than main, add missing ones
176
- // (This handles cases where new diagrams were added)
177
- let finalTranslationImages = extractDiagramImagePaths(updatedContent); // Re-extract after all replacements
178
- if (mainImages.length > 0 && finalTranslationImages.length < mainImages.length) {
179
- // Find the last diagram position in updated content
180
- const lastDiagramIndex =
181
- finalTranslationImages.length > 0
182
- ? finalTranslationImages[finalTranslationImages.length - 1].index +
183
- finalTranslationImages[finalTranslationImages.length - 1].fullMatch.length
184
- : updatedContent.length;
185
-
186
- // Add missing images after the last diagram
187
- const missingImages = mainImages.slice(finalTranslationImages.length);
188
- const imagesToAdd = missingImages.map((img) => img.fullMatch).join("\n\n");
189
-
190
- updatedContent =
191
- updatedContent.slice(0, lastDiagramIndex) +
192
- "\n\n" +
193
- imagesToAdd +
194
- "\n\n" +
195
- updatedContent.slice(lastDiagramIndex);
196
- hasChanges = true;
197
- debug(`➕ Added ${missingImages.length} missing diagram(s) to ${fileName}`);
198
- // Re-extract after adding images
199
- finalTranslationImages = extractDiagramImagePaths(updatedContent);
200
- }
201
-
202
- // Strategy 4: If translation has more images than main, remove excess ones
203
- // (This handles cases where diagrams were deleted from main document, including all diagrams)
204
- if (finalTranslationImages.length > mainImages.length) {
205
- // Remove excess images from translation (keep only the first N images matching main)
206
- // Process from end to start to preserve indices
207
- const excessCount = finalTranslationImages.length - mainImages.length;
208
- for (let i = finalTranslationImages.length - 1; i >= mainImages.length; i--) {
209
- const imageToRemove = finalTranslationImages[i];
210
- const before = updatedContent.substring(0, imageToRemove.index);
211
- const after = updatedContent.substring(
212
- imageToRemove.index + imageToRemove.fullMatch.length,
213
- );
214
- // Remove the image and clean up extra newlines
215
- updatedContent = `${before.replace(/\n+$/, "")}\n${after.replace(/^\n+/, "")}`;
216
- hasChanges = true;
217
- }
218
- debug(`➖ Removed ${excessCount} excess diagram(s) from ${fileName}`);
219
- }
220
-
221
- // Strategy 5: Remove D2 code blocks from translation if main has no diagrams
222
- // (This handles cases where all diagrams were deleted from main document)
223
- if (mainImages.length === 0) {
224
- // Re-extract D2 blocks from updated content (in case some were already replaced)
225
- const remainingD2Blocks = Array.from(updatedContent.matchAll(d2CodeBlockRegex));
226
- if (remainingD2Blocks.length > 0) {
227
- // Remove all D2 code blocks from translation
228
- // Process from end to start to preserve indices
229
- for (let i = remainingD2Blocks.length - 1; i >= 0; i--) {
230
- const d2Match = remainingD2Blocks[i];
231
- const before = updatedContent.substring(0, d2Match.index);
232
- const after = updatedContent.substring(d2Match.index + d2Match[0].length);
233
- // Remove the D2 block and clean up extra newlines
234
- updatedContent = `${before.replace(/\n+$/, "")}\n${after.replace(/^\n+/, "")}`;
235
- hasChanges = true;
236
- }
237
- // Clean up extra newlines
238
- updatedContent = updatedContent.replace(/\n{3,}/g, "\n\n");
239
- debug(`➖ Removed ${remainingD2Blocks.length} D2 code block(s) from ${fileName}`);
240
- }
241
- }
242
-
243
- // Save updated translation file if there were changes
244
- if (hasChanges) {
245
- await fs.writeFile(translationFilePath, updatedContent, "utf8");
246
- result.updated++;
247
- debug(`✅ Synced diagram images to ${fileName}`);
248
- } else {
249
- result.skipped++;
250
- debug(`⏭️ No changes needed for ${fileName}`);
251
- }
252
- } catch (error) {
253
- debug(`❌ Error syncing diagram to ${fileName}: ${error.message}`);
254
- result.errors.push({
255
- file: fileName,
256
- error: error.message,
257
- });
258
- }
259
- }
260
-
261
- return result;
262
- }