@aigne/doc-smith 0.9.8-alpha.2 → 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 (256) 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/planner.md +0 -168
  21. package/agentic-agents/common/worker.md +0 -93
  22. package/agentic-agents/create/index.yaml +0 -118
  23. package/agentic-agents/create/objective.md +0 -44
  24. package/agentic-agents/create/set-custom-prompt.mjs +0 -27
  25. package/agentic-agents/detail/index.yaml +0 -95
  26. package/agentic-agents/detail/objective.md +0 -9
  27. package/agentic-agents/detail/set-custom-prompt.mjs +0 -88
  28. package/agentic-agents/predict-resources/index.yaml +0 -44
  29. package/agentic-agents/predict-resources/instructions.md +0 -61
  30. package/agentic-agents/structure/design-rules.md +0 -39
  31. package/agentic-agents/structure/index.yaml +0 -86
  32. package/agentic-agents/structure/objective.md +0 -14
  33. package/agentic-agents/structure/review-criteria.md +0 -55
  34. package/agentic-agents/structure/set-custom-prompt.mjs +0 -78
  35. package/agentic-agents/utils/init-workspace-cache.mjs +0 -171
  36. package/agentic-agents/utils/load-base-sources.mjs +0 -20
  37. package/agentic-agents/workspace-cache-sharing-design.md +0 -671
  38. package/agents/chat/chat-system.md +0 -38
  39. package/agents/chat/index.mjs +0 -59
  40. package/agents/chat/skills/generate-document.yaml +0 -15
  41. package/agents/chat/skills/list-documents.mjs +0 -15
  42. package/agents/chat/skills/update-document.yaml +0 -24
  43. package/agents/clear/choose-contents.mjs +0 -192
  44. package/agents/clear/clear-auth-tokens.mjs +0 -88
  45. package/agents/clear/clear-deployment-config.mjs +0 -49
  46. package/agents/clear/clear-document-config.mjs +0 -36
  47. package/agents/clear/clear-document-structure.mjs +0 -102
  48. package/agents/clear/clear-generated-docs.mjs +0 -142
  49. package/agents/clear/clear-media-description.mjs +0 -129
  50. package/agents/clear/index.yaml +0 -26
  51. package/agents/create/analyze-diagram-type-llm.yaml +0 -160
  52. package/agents/create/analyze-diagram-type.mjs +0 -297
  53. package/agents/create/check-document-structure.yaml +0 -30
  54. package/agents/create/check-need-generate-structure.mjs +0 -105
  55. package/agents/create/document-structure-tools/add-document.mjs +0 -85
  56. package/agents/create/document-structure-tools/delete-document.mjs +0 -116
  57. package/agents/create/document-structure-tools/move-document.mjs +0 -109
  58. package/agents/create/document-structure-tools/update-document.mjs +0 -84
  59. package/agents/create/generate-diagram-image.yaml +0 -60
  60. package/agents/create/generate-structure.yaml +0 -117
  61. package/agents/create/index.yaml +0 -49
  62. package/agents/create/refine-document-structure.yaml +0 -12
  63. package/agents/create/replace-d2-with-image.mjs +0 -625
  64. package/agents/create/update-document-structure.yaml +0 -54
  65. package/agents/create/user-add-document/add-documents-to-structure.mjs +0 -90
  66. package/agents/create/user-add-document/find-documents-to-add-links.yaml +0 -47
  67. package/agents/create/user-add-document/index.yaml +0 -46
  68. package/agents/create/user-add-document/prepare-documents-to-translate.mjs +0 -22
  69. package/agents/create/user-add-document/print-add-document-summary.mjs +0 -63
  70. package/agents/create/user-add-document/review-documents-with-new-links.mjs +0 -110
  71. package/agents/create/user-remove-document/find-documents-with-invalid-links.mjs +0 -78
  72. package/agents/create/user-remove-document/index.yaml +0 -40
  73. package/agents/create/user-remove-document/prepare-documents-to-translate.mjs +0 -22
  74. package/agents/create/user-remove-document/print-remove-document-summary.mjs +0 -53
  75. package/agents/create/user-remove-document/remove-documents-from-structure.mjs +0 -99
  76. package/agents/create/user-remove-document/review-documents-with-invalid-links.mjs +0 -115
  77. package/agents/create/user-review-document-structure.mjs +0 -140
  78. package/agents/create/utils/init-current-content.mjs +0 -34
  79. package/agents/create/utils/merge-document-structures.mjs +0 -30
  80. package/agents/evaluate/code-snippet.mjs +0 -97
  81. package/agents/evaluate/document-structure.yaml +0 -67
  82. package/agents/evaluate/document.yaml +0 -82
  83. package/agents/evaluate/generate-report.mjs +0 -85
  84. package/agents/evaluate/index.yaml +0 -46
  85. package/agents/history/index.yaml +0 -6
  86. package/agents/history/view.mjs +0 -78
  87. package/agents/init/check.mjs +0 -16
  88. package/agents/init/index.mjs +0 -275
  89. package/agents/init/validate.mjs +0 -16
  90. package/agents/localize/choose-language.mjs +0 -107
  91. package/agents/localize/index.yaml +0 -58
  92. package/agents/localize/record-translation-history.mjs +0 -23
  93. package/agents/localize/translate-document.yaml +0 -24
  94. package/agents/localize/translate-multilingual.yaml +0 -51
  95. package/agents/media/batch-generate-media-description.yaml +0 -46
  96. package/agents/media/generate-media-description.yaml +0 -50
  97. package/agents/media/load-media-description.mjs +0 -256
  98. package/agents/prefs/index.mjs +0 -203
  99. package/agents/publish/index.yaml +0 -26
  100. package/agents/publish/publish-docs.mjs +0 -356
  101. package/agents/publish/translate-meta.mjs +0 -103
  102. package/agents/schema/document-structure-item.yaml +0 -26
  103. package/agents/schema/document-structure-refine-item.yaml +0 -23
  104. package/agents/schema/document-structure.yaml +0 -29
  105. package/agents/update/batch-generate-document.yaml +0 -27
  106. package/agents/update/batch-update-document.yaml +0 -7
  107. package/agents/update/check-diagram-flag.mjs +0 -116
  108. package/agents/update/check-document.mjs +0 -162
  109. package/agents/update/check-generate-diagram.mjs +0 -106
  110. package/agents/update/check-sync-image-flag.mjs +0 -55
  111. package/agents/update/check-update-is-single.mjs +0 -53
  112. package/agents/update/document-tools/update-document-content.mjs +0 -303
  113. package/agents/update/generate-diagram.yaml +0 -63
  114. package/agents/update/generate-document.yaml +0 -70
  115. package/agents/update/handle-document-update.yaml +0 -103
  116. package/agents/update/index.yaml +0 -79
  117. package/agents/update/pre-check-generate-diagram.yaml +0 -44
  118. package/agents/update/save-and-translate-document.mjs +0 -76
  119. package/agents/update/sync-images-and-exit.mjs +0 -148
  120. package/agents/update/update-document-detail.yaml +0 -71
  121. package/agents/update/update-single/update-single-document-detail.mjs +0 -280
  122. package/agents/update/update-single-document.yaml +0 -7
  123. package/agents/update/user-review-document.mjs +0 -272
  124. package/agents/utils/action-success.mjs +0 -16
  125. package/agents/utils/analyze-document-feedback-intent.yaml +0 -32
  126. package/agents/utils/analyze-feedback-intent.mjs +0 -136
  127. package/agents/utils/analyze-structure-feedback-intent.yaml +0 -29
  128. package/agents/utils/check-detail-result.mjs +0 -38
  129. package/agents/utils/check-feedback-refiner.mjs +0 -81
  130. package/agents/utils/choose-docs.mjs +0 -293
  131. package/agents/utils/document-icon-generate.yaml +0 -52
  132. package/agents/utils/document-title-streamline.yaml +0 -48
  133. package/agents/utils/ensure-document-icons.mjs +0 -129
  134. package/agents/utils/exit.mjs +0 -6
  135. package/agents/utils/feedback-refiner.yaml +0 -50
  136. package/agents/utils/find-item-by-path.mjs +0 -114
  137. package/agents/utils/find-user-preferences-by-path.mjs +0 -37
  138. package/agents/utils/format-document-structure.mjs +0 -35
  139. package/agents/utils/generate-document-or-skip.mjs +0 -41
  140. package/agents/utils/handle-diagram-operations.mjs +0 -263
  141. package/agents/utils/load-all-document-content.mjs +0 -30
  142. package/agents/utils/load-document-all-content.mjs +0 -84
  143. package/agents/utils/load-sources.mjs +0 -405
  144. package/agents/utils/map-reasoning-effort-level.mjs +0 -15
  145. package/agents/utils/post-generate.mjs +0 -144
  146. package/agents/utils/read-current-document-content.mjs +0 -46
  147. package/agents/utils/save-doc-translation.mjs +0 -61
  148. package/agents/utils/save-doc.mjs +0 -88
  149. package/agents/utils/save-output.mjs +0 -26
  150. package/agents/utils/save-sidebar.mjs +0 -51
  151. package/agents/utils/skip-if-content-exists.mjs +0 -27
  152. package/agents/utils/streamline-document-titles-if-needed.mjs +0 -88
  153. package/agents/utils/transform-detail-data-sources.mjs +0 -45
  154. package/agents/utils/update-branding.mjs +0 -84
  155. package/assets/report-template/report.html +0 -198
  156. package/docs-mcp/analyze-content-relevance.yaml +0 -50
  157. package/docs-mcp/analyze-docs-relevance.yaml +0 -59
  158. package/docs-mcp/docs-search.yaml +0 -42
  159. package/docs-mcp/get-docs-detail.mjs +0 -41
  160. package/docs-mcp/get-docs-structure.mjs +0 -16
  161. package/docs-mcp/read-doc-content.mjs +0 -119
  162. package/prompts/common/document/content-rules-core.md +0 -20
  163. package/prompts/common/document/markdown-syntax-rules.md +0 -65
  164. package/prompts/common/document/media-file-list-usage-rules.md +0 -18
  165. package/prompts/common/document/openapi-usage-rules.md +0 -189
  166. package/prompts/common/document/role-and-personality.md +0 -16
  167. package/prompts/common/document/user-preferences.md +0 -9
  168. package/prompts/common/document-structure/conflict-resolution-guidance.md +0 -16
  169. package/prompts/common/document-structure/document-icon-generate.md +0 -116
  170. package/prompts/common/document-structure/document-structure-rules.md +0 -43
  171. package/prompts/common/document-structure/document-title-streamline.md +0 -86
  172. package/prompts/common/document-structure/glossary.md +0 -7
  173. package/prompts/common/document-structure/intj-traits.md +0 -5
  174. package/prompts/common/document-structure/openapi-usage-rules.md +0 -28
  175. package/prompts/common/document-structure/output-constraints.md +0 -18
  176. package/prompts/common/document-structure/user-locale-rules.md +0 -10
  177. package/prompts/common/document-structure/user-preferences.md +0 -9
  178. package/prompts/detail/custom/admonition-usage-rules.md +0 -94
  179. package/prompts/detail/custom/code-block-usage-rules.md +0 -163
  180. package/prompts/detail/custom/custom-components/x-card-usage-rules.md +0 -63
  181. package/prompts/detail/custom/custom-components/x-cards-usage-rules.md +0 -83
  182. package/prompts/detail/custom/custom-components/x-field-desc-usage-rules.md +0 -120
  183. package/prompts/detail/custom/custom-components/x-field-group-usage-rules.md +0 -80
  184. package/prompts/detail/custom/custom-components/x-field-usage-rules.md +0 -189
  185. package/prompts/detail/custom/custom-components-usage-rules.md +0 -18
  186. package/prompts/detail/diagram/generate-image-system.md +0 -135
  187. package/prompts/detail/diagram/generate-image-user.md +0 -32
  188. package/prompts/detail/diagram/guide.md +0 -29
  189. package/prompts/detail/diagram/official-examples.md +0 -712
  190. package/prompts/detail/diagram/pre-check.md +0 -23
  191. package/prompts/detail/diagram/role-and-personality.md +0 -2
  192. package/prompts/detail/diagram/rules.md +0 -46
  193. package/prompts/detail/diagram/system-prompt.md +0 -1139
  194. package/prompts/detail/diagram/user-prompt.md +0 -43
  195. package/prompts/detail/generate/detail-example.md +0 -457
  196. package/prompts/detail/generate/document-rules.md +0 -45
  197. package/prompts/detail/generate/system-prompt.md +0 -61
  198. package/prompts/detail/generate/user-prompt.md +0 -99
  199. package/prompts/detail/jsx/rules.md +0 -6
  200. package/prompts/detail/update/system-prompt.md +0 -121
  201. package/prompts/detail/update/user-prompt.md +0 -41
  202. package/prompts/evaluate/document-structure.md +0 -93
  203. package/prompts/evaluate/document.md +0 -149
  204. package/prompts/media/media-description/system-prompt.md +0 -43
  205. package/prompts/media/media-description/user-prompt.md +0 -17
  206. package/prompts/structure/check-document-structure.md +0 -93
  207. package/prompts/structure/document-rules.md +0 -21
  208. package/prompts/structure/find-documents-to-add-links.md +0 -52
  209. package/prompts/structure/generate/system-prompt.md +0 -13
  210. package/prompts/structure/generate/user-prompt.md +0 -137
  211. package/prompts/structure/review/structure-review-system.md +0 -81
  212. package/prompts/structure/structure-example.md +0 -89
  213. package/prompts/structure/structure-getting-started.md +0 -10
  214. package/prompts/structure/update/system-prompt.md +0 -93
  215. package/prompts/structure/update/user-prompt.md +0 -43
  216. package/prompts/translate/admonition.md +0 -20
  217. package/prompts/translate/code-block.md +0 -33
  218. package/prompts/translate/glossary.md +0 -6
  219. package/prompts/translate/translate-document.md +0 -305
  220. package/prompts/utils/analyze-document-feedback-intent.md +0 -54
  221. package/prompts/utils/analyze-structure-feedback-intent.md +0 -43
  222. package/prompts/utils/feedback-refiner.md +0 -105
  223. package/types/document-schema.mjs +0 -55
  224. package/types/document-structure-schema.mjs +0 -261
  225. package/utils/auth-utils.mjs +0 -275
  226. package/utils/blocklet.mjs +0 -104
  227. package/utils/check-document-has-diagram.mjs +0 -95
  228. package/utils/conflict-detector.mjs +0 -149
  229. package/utils/constants/index.mjs +0 -620
  230. package/utils/constants/linter.mjs +0 -102
  231. package/utils/d2-utils.mjs +0 -198
  232. package/utils/debug.mjs +0 -3
  233. package/utils/delete-diagram-images.mjs +0 -99
  234. package/utils/deploy.mjs +0 -86
  235. package/utils/docs-finder-utils.mjs +0 -623
  236. package/utils/evaluate/report-utils.mjs +0 -132
  237. package/utils/extract-api.mjs +0 -32
  238. package/utils/file-utils.mjs +0 -960
  239. package/utils/history-utils.mjs +0 -203
  240. package/utils/icon-map.mjs +0 -26
  241. package/utils/image-compress.mjs +0 -75
  242. package/utils/kroki-utils.mjs +0 -173
  243. package/utils/linter/index.mjs +0 -50
  244. package/utils/load-config.mjs +0 -107
  245. package/utils/markdown/index.mjs +0 -26
  246. package/utils/markdown-checker.mjs +0 -694
  247. package/utils/mermaid-validator.mjs +0 -140
  248. package/utils/mermaid-worker-pool.mjs +0 -250
  249. package/utils/mermaid-worker.mjs +0 -233
  250. package/utils/openapi/index.mjs +0 -28
  251. package/utils/preferences-utils.mjs +0 -175
  252. package/utils/request.mjs +0 -10
  253. package/utils/store/index.mjs +0 -45
  254. package/utils/sync-diagram-to-translations.mjs +0 -262
  255. package/utils/upload-files.mjs +0 -231
  256. 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
- }