@aigne/doc-smith 0.8.12-beta.7 → 0.8.12-beta.9

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 (284) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/agents/clear/choose-contents.mjs +14 -1
  3. package/agents/clear/clear-media-description.mjs +129 -0
  4. package/agents/clear/index.yaml +3 -1
  5. package/agents/evaluate/code-snippet.mjs +28 -24
  6. package/agents/evaluate/document-structure.yaml +0 -4
  7. package/agents/evaluate/document.yaml +1 -5
  8. package/agents/generate/index.yaml +1 -0
  9. package/agents/init/index.mjs +10 -0
  10. package/agents/media/batch-generate-media-description.yaml +44 -0
  11. package/agents/media/generate-media-description.yaml +47 -0
  12. package/agents/media/load-media-description.mjs +238 -0
  13. package/agents/publish/index.yaml +4 -0
  14. package/agents/publish/publish-docs.mjs +77 -5
  15. package/agents/publish/translate-meta.mjs +103 -0
  16. package/agents/update/generate-document.yaml +30 -28
  17. package/agents/update/index.yaml +1 -0
  18. package/agents/update/update-document-detail.yaml +3 -1
  19. package/agents/utils/load-sources.mjs +103 -53
  20. package/agents/utils/update-branding.mjs +69 -0
  21. package/aigne.yaml +6 -0
  22. package/assets/report-template/report.html +34 -34
  23. package/package.json +17 -2
  24. package/prompts/common/document/role-and-personality.md +3 -1
  25. package/prompts/detail/d2-diagram/guide.md +7 -1
  26. package/prompts/detail/d2-diagram/user-prompt.md +3 -0
  27. package/prompts/detail/generate/system-prompt.md +6 -7
  28. package/prompts/detail/generate/user-prompt.md +12 -3
  29. package/prompts/detail/update/user-prompt.md +0 -2
  30. package/prompts/evaluate/document-structure.md +6 -7
  31. package/prompts/evaluate/document.md +16 -25
  32. package/prompts/media/media-description/system-prompt.md +35 -0
  33. package/prompts/media/media-description/user-prompt.md +8 -0
  34. package/prompts/structure/update/user-prompt.md +0 -4
  35. package/utils/constants/index.mjs +0 -107
  36. package/utils/file-utils.mjs +86 -0
  37. package/utils/markdown-checker.mjs +0 -20
  38. package/utils/request.mjs +7 -0
  39. package/utils/upload-files.mjs +231 -0
  40. package/utils/utils.mjs +11 -1
  41. package/.aigne/doc-smith/config.yaml +0 -77
  42. package/.aigne/doc-smith/history.yaml +0 -37
  43. package/.aigne/doc-smith/output/structure-plan.json +0 -162
  44. package/.aigne/doc-smith/preferences.yml +0 -97
  45. package/.aigne/doc-smith/upload-cache.yaml +0 -1893
  46. package/.github/PULL_REQUEST_TEMPLATE.md +0 -28
  47. package/.github/workflows/ci.yml +0 -54
  48. package/.github/workflows/create-release-pr.yaml +0 -21
  49. package/.github/workflows/publish-docs.yml +0 -65
  50. package/.github/workflows/release.yml +0 -49
  51. package/.github/workflows/reviewer.yml +0 -54
  52. package/.release-please-manifest.json +0 -3
  53. package/RELEASE.md +0 -9
  54. package/assets/screenshots/doc-complete-setup.png +0 -0
  55. package/assets/screenshots/doc-generate-docs.png +0 -0
  56. package/assets/screenshots/doc-generate.png +0 -0
  57. package/assets/screenshots/doc-generated-successfully.png +0 -0
  58. package/assets/screenshots/doc-publish.png +0 -0
  59. package/assets/screenshots/doc-regenerate.png +0 -0
  60. package/assets/screenshots/doc-translate-langs.png +0 -0
  61. package/assets/screenshots/doc-translate.png +0 -0
  62. package/assets/screenshots/doc-update.png +0 -0
  63. package/biome.json +0 -73
  64. package/codecov.yml +0 -15
  65. package/docs/_sidebar.md +0 -15
  66. package/docs/configuration-initial-setup.ja.md +0 -179
  67. package/docs/configuration-initial-setup.md +0 -179
  68. package/docs/configuration-initial-setup.zh-TW.md +0 -179
  69. package/docs/configuration-initial-setup.zh.md +0 -179
  70. package/docs/configuration-managing-preferences.ja.md +0 -100
  71. package/docs/configuration-managing-preferences.md +0 -100
  72. package/docs/configuration-managing-preferences.zh-TW.md +0 -100
  73. package/docs/configuration-managing-preferences.zh.md +0 -100
  74. package/docs/configuration.ja.md +0 -96
  75. package/docs/configuration.md +0 -96
  76. package/docs/configuration.zh-TW.md +0 -96
  77. package/docs/configuration.zh.md +0 -96
  78. package/docs/getting-started.ja.md +0 -88
  79. package/docs/getting-started.md +0 -88
  80. package/docs/getting-started.zh-TW.md +0 -88
  81. package/docs/getting-started.zh.md +0 -88
  82. package/docs/guides-cleaning-up.ja.md +0 -51
  83. package/docs/guides-cleaning-up.md +0 -51
  84. package/docs/guides-cleaning-up.zh-TW.md +0 -51
  85. package/docs/guides-cleaning-up.zh.md +0 -51
  86. package/docs/guides-evaluating-documents.ja.md +0 -66
  87. package/docs/guides-evaluating-documents.md +0 -66
  88. package/docs/guides-evaluating-documents.zh-TW.md +0 -66
  89. package/docs/guides-evaluating-documents.zh.md +0 -66
  90. package/docs/guides-generating-documentation.ja.md +0 -151
  91. package/docs/guides-generating-documentation.md +0 -151
  92. package/docs/guides-generating-documentation.zh-TW.md +0 -151
  93. package/docs/guides-generating-documentation.zh.md +0 -151
  94. package/docs/guides-interactive-chat.ja.md +0 -85
  95. package/docs/guides-interactive-chat.md +0 -85
  96. package/docs/guides-interactive-chat.zh-TW.md +0 -85
  97. package/docs/guides-interactive-chat.zh.md +0 -85
  98. package/docs/guides-managing-history.ja.md +0 -48
  99. package/docs/guides-managing-history.md +0 -48
  100. package/docs/guides-managing-history.zh-TW.md +0 -48
  101. package/docs/guides-managing-history.zh.md +0 -48
  102. package/docs/guides-publishing-your-docs.ja.md +0 -78
  103. package/docs/guides-publishing-your-docs.md +0 -78
  104. package/docs/guides-publishing-your-docs.zh-TW.md +0 -78
  105. package/docs/guides-publishing-your-docs.zh.md +0 -78
  106. package/docs/guides-translating-documentation.ja.md +0 -95
  107. package/docs/guides-translating-documentation.md +0 -95
  108. package/docs/guides-translating-documentation.zh-TW.md +0 -95
  109. package/docs/guides-translating-documentation.zh.md +0 -95
  110. package/docs/guides-updating-documentation.ja.md +0 -77
  111. package/docs/guides-updating-documentation.md +0 -77
  112. package/docs/guides-updating-documentation.zh-TW.md +0 -77
  113. package/docs/guides-updating-documentation.zh.md +0 -77
  114. package/docs/guides.ja.md +0 -32
  115. package/docs/guides.md +0 -32
  116. package/docs/guides.zh-TW.md +0 -32
  117. package/docs/guides.zh.md +0 -32
  118. package/docs/overview.ja.md +0 -61
  119. package/docs/overview.md +0 -61
  120. package/docs/overview.zh-TW.md +0 -61
  121. package/docs/overview.zh.md +0 -61
  122. package/docs/release-notes.ja.md +0 -255
  123. package/docs/release-notes.md +0 -255
  124. package/docs/release-notes.zh-TW.md +0 -255
  125. package/docs/release-notes.zh.md +0 -255
  126. package/media.md +0 -19
  127. package/prompts/common/afs/afs-tools-usage.md +0 -5
  128. package/prompts/common/afs/use-afs-instruction.md +0 -1
  129. package/release-please-config.json +0 -14
  130. package/tests/agents/chat/chat.test.mjs +0 -46
  131. package/tests/agents/clear/choose-contents.test.mjs +0 -284
  132. package/tests/agents/clear/clear-auth-tokens.test.mjs +0 -268
  133. package/tests/agents/clear/clear-document-config.test.mjs +0 -167
  134. package/tests/agents/clear/clear-document-structure.test.mjs +0 -380
  135. package/tests/agents/clear/clear-generated-docs.test.mjs +0 -222
  136. package/tests/agents/evaluate/code-snippet.test.mjs +0 -163
  137. package/tests/agents/evaluate/fixtures/api-services.md +0 -87
  138. package/tests/agents/evaluate/fixtures/js-sdk.md +0 -94
  139. package/tests/agents/evaluate/generate-report.test.mjs +0 -312
  140. package/tests/agents/generate/check-document-structure.test.mjs +0 -45
  141. package/tests/agents/generate/check-need-generate-structure.test.mjs +0 -279
  142. package/tests/agents/generate/document-structure-tools/add-document.test.mjs +0 -449
  143. package/tests/agents/generate/document-structure-tools/delete-document.test.mjs +0 -410
  144. package/tests/agents/generate/document-structure-tools/generate-sub-structure.test.mjs +0 -277
  145. package/tests/agents/generate/document-structure-tools/move-document.test.mjs +0 -476
  146. package/tests/agents/generate/document-structure-tools/update-document.test.mjs +0 -548
  147. package/tests/agents/generate/generate-structure.test.mjs +0 -45
  148. package/tests/agents/generate/user-review-document-structure.test.mjs +0 -319
  149. package/tests/agents/history/view.test.mjs +0 -97
  150. package/tests/agents/init/init.test.mjs +0 -1657
  151. package/tests/agents/prefs/prefs.test.mjs +0 -431
  152. package/tests/agents/publish/publish-docs.test.mjs +0 -787
  153. package/tests/agents/translate/choose-language.test.mjs +0 -311
  154. package/tests/agents/translate/translate-document.test.mjs +0 -51
  155. package/tests/agents/update/check-document.test.mjs +0 -463
  156. package/tests/agents/update/check-update-is-single.test.mjs +0 -300
  157. package/tests/agents/update/document-tools/update-document-content.test.mjs +0 -329
  158. package/tests/agents/update/generate-document.test.mjs +0 -51
  159. package/tests/agents/update/save-and-translate-document.test.mjs +0 -369
  160. package/tests/agents/update/user-review-document.test.mjs +0 -582
  161. package/tests/agents/utils/action-success.test.mjs +0 -54
  162. package/tests/agents/utils/check-detail-result.test.mjs +0 -743
  163. package/tests/agents/utils/check-feedback-refiner.test.mjs +0 -478
  164. package/tests/agents/utils/choose-docs.test.mjs +0 -406
  165. package/tests/agents/utils/exit.test.mjs +0 -70
  166. package/tests/agents/utils/feedback-refiner.test.mjs +0 -51
  167. package/tests/agents/utils/find-item-by-path.test.mjs +0 -517
  168. package/tests/agents/utils/find-user-preferences-by-path.test.mjs +0 -382
  169. package/tests/agents/utils/format-document-structure.test.mjs +0 -364
  170. package/tests/agents/utils/fs.test.mjs +0 -267
  171. package/tests/agents/utils/load-sources.test.mjs +0 -1470
  172. package/tests/agents/utils/save-docs.test.mjs +0 -109
  173. package/tests/agents/utils/save-output.test.mjs +0 -315
  174. package/tests/agents/utils/save-single-doc.test.mjs +0 -364
  175. package/tests/agents/utils/transform-detail-datasources.test.mjs +0 -320
  176. package/tests/utils/auth-utils.test.mjs +0 -596
  177. package/tests/utils/blocklet.test.mjs +0 -336
  178. package/tests/utils/conflict-detector.test.mjs +0 -355
  179. package/tests/utils/constants.test.mjs +0 -295
  180. package/tests/utils/d2-utils.test.mjs +0 -437
  181. package/tests/utils/deploy.test.mjs +0 -399
  182. package/tests/utils/docs-finder-utils.test.mjs +0 -650
  183. package/tests/utils/file-utils.test.mjs +0 -521
  184. package/tests/utils/history-utils.test.mjs +0 -206
  185. package/tests/utils/kroki-utils.test.mjs +0 -646
  186. package/tests/utils/linter/fixtures/css/keyword-error.css +0 -1
  187. package/tests/utils/linter/fixtures/css/missing-semicolon.css +0 -1
  188. package/tests/utils/linter/fixtures/css/syntax-error.css +0 -1
  189. package/tests/utils/linter/fixtures/css/undeclare-variable.css +0 -1
  190. package/tests/utils/linter/fixtures/css/unused-variable.css +0 -2
  191. package/tests/utils/linter/fixtures/css/valid-code.css +0 -1
  192. package/tests/utils/linter/fixtures/dockerfile/keyword-error.dockerfile +0 -1
  193. package/tests/utils/linter/fixtures/dockerfile/missing-semicolon.dockerfile +0 -2
  194. package/tests/utils/linter/fixtures/dockerfile/syntax-error.dockerfile +0 -2
  195. package/tests/utils/linter/fixtures/dockerfile/undeclare-variable.dockerfile +0 -1
  196. package/tests/utils/linter/fixtures/dockerfile/unused-variable.dockerfile +0 -1
  197. package/tests/utils/linter/fixtures/dockerfile/valid-code.dockerfile +0 -2
  198. package/tests/utils/linter/fixtures/go/keyword-error.go +0 -5
  199. package/tests/utils/linter/fixtures/go/missing-semicolon.go +0 -5
  200. package/tests/utils/linter/fixtures/go/syntax-error.go +0 -6
  201. package/tests/utils/linter/fixtures/go/undeclare-variable.go +0 -5
  202. package/tests/utils/linter/fixtures/go/unused-variable.go +0 -5
  203. package/tests/utils/linter/fixtures/go/valid-code.go +0 -7
  204. package/tests/utils/linter/fixtures/js/keyword-error.js +0 -3
  205. package/tests/utils/linter/fixtures/js/missing-semicolon.js +0 -6
  206. package/tests/utils/linter/fixtures/js/syntax-error.js +0 -4
  207. package/tests/utils/linter/fixtures/js/undeclare-variable.js +0 -3
  208. package/tests/utils/linter/fixtures/js/unused-variable.js +0 -7
  209. package/tests/utils/linter/fixtures/js/valid-code.js +0 -15
  210. package/tests/utils/linter/fixtures/json/keyword-error.json +0 -1
  211. package/tests/utils/linter/fixtures/json/missing-semicolon.json +0 -1
  212. package/tests/utils/linter/fixtures/json/syntax-error.json +0 -1
  213. package/tests/utils/linter/fixtures/json/undeclare-variable.json +0 -1
  214. package/tests/utils/linter/fixtures/json/unused-variable.json +0 -1
  215. package/tests/utils/linter/fixtures/json/valid-code.json +0 -1
  216. package/tests/utils/linter/fixtures/jsx/keyword-error.jsx +0 -5
  217. package/tests/utils/linter/fixtures/jsx/missing-semicolon.jsx +0 -5
  218. package/tests/utils/linter/fixtures/jsx/syntax-error.jsx +0 -5
  219. package/tests/utils/linter/fixtures/jsx/undeclare-variable.jsx +0 -5
  220. package/tests/utils/linter/fixtures/jsx/unused-variable.jsx +0 -4
  221. package/tests/utils/linter/fixtures/jsx/valid-code.jsx +0 -5
  222. package/tests/utils/linter/fixtures/python/keyword-error.py +0 -3
  223. package/tests/utils/linter/fixtures/python/missing-semicolon.py +0 -2
  224. package/tests/utils/linter/fixtures/python/syntax-error.py +0 -3
  225. package/tests/utils/linter/fixtures/python/undeclare-variable.py +0 -3
  226. package/tests/utils/linter/fixtures/python/unused-variable.py +0 -6
  227. package/tests/utils/linter/fixtures/python/valid-code.py +0 -12
  228. package/tests/utils/linter/fixtures/ruby/keyword-error.rb +0 -2
  229. package/tests/utils/linter/fixtures/ruby/missing-semicolon.rb +0 -1
  230. package/tests/utils/linter/fixtures/ruby/syntax-error.rb +0 -2
  231. package/tests/utils/linter/fixtures/ruby/undeclare-variable.rb +0 -1
  232. package/tests/utils/linter/fixtures/ruby/unused-variable.rb +0 -2
  233. package/tests/utils/linter/fixtures/ruby/valid-code.rb +0 -1
  234. package/tests/utils/linter/fixtures/sass/keyword-error.sass +0 -2
  235. package/tests/utils/linter/fixtures/sass/missing-semicolon.sass +0 -3
  236. package/tests/utils/linter/fixtures/sass/syntax-error.sass +0 -3
  237. package/tests/utils/linter/fixtures/sass/undeclare-variable.sass +0 -2
  238. package/tests/utils/linter/fixtures/sass/unused-variable.sass +0 -4
  239. package/tests/utils/linter/fixtures/sass/valid-code.sass +0 -2
  240. package/tests/utils/linter/fixtures/scss/keyword-error.scss +0 -1
  241. package/tests/utils/linter/fixtures/scss/missing-semicolon.scss +0 -1
  242. package/tests/utils/linter/fixtures/scss/syntax-error.scss +0 -1
  243. package/tests/utils/linter/fixtures/scss/undeclare-variable.scss +0 -1
  244. package/tests/utils/linter/fixtures/scss/unused-variable.scss +0 -2
  245. package/tests/utils/linter/fixtures/scss/valid-code.scss +0 -1
  246. package/tests/utils/linter/fixtures/shell/keyword-error.sh +0 -5
  247. package/tests/utils/linter/fixtures/shell/missing-semicolon.sh +0 -3
  248. package/tests/utils/linter/fixtures/shell/syntax-error.sh +0 -4
  249. package/tests/utils/linter/fixtures/shell/undeclare-variable.sh +0 -3
  250. package/tests/utils/linter/fixtures/shell/unused-variable.sh +0 -4
  251. package/tests/utils/linter/fixtures/shell/valid-code.sh +0 -3
  252. package/tests/utils/linter/fixtures/ts/keyword-error.ts +0 -1
  253. package/tests/utils/linter/fixtures/ts/missing-semicolon.ts +0 -1
  254. package/tests/utils/linter/fixtures/ts/syntax-error.ts +0 -1
  255. package/tests/utils/linter/fixtures/ts/undeclare-variable.ts +0 -1
  256. package/tests/utils/linter/fixtures/ts/unused-variable.ts +0 -3
  257. package/tests/utils/linter/fixtures/ts/valid-code.ts +0 -3
  258. package/tests/utils/linter/fixtures/tsx/keyword-error.tsx +0 -5
  259. package/tests/utils/linter/fixtures/tsx/missing-semicolon.tsx +0 -5
  260. package/tests/utils/linter/fixtures/tsx/syntax-error.tsx +0 -5
  261. package/tests/utils/linter/fixtures/tsx/undeclare-variable.tsx +0 -6
  262. package/tests/utils/linter/fixtures/tsx/unused-variable.tsx +0 -6
  263. package/tests/utils/linter/fixtures/tsx/valid-code.tsx +0 -5
  264. package/tests/utils/linter/fixtures/vue/keyword-error.vue +0 -6
  265. package/tests/utils/linter/fixtures/vue/missing-semicolon.vue +0 -6
  266. package/tests/utils/linter/fixtures/vue/syntax-error.vue +0 -6
  267. package/tests/utils/linter/fixtures/vue/undeclare-variable.vue +0 -6
  268. package/tests/utils/linter/fixtures/vue/unused-variable.vue +0 -7
  269. package/tests/utils/linter/fixtures/vue/valid-code.vue +0 -6
  270. package/tests/utils/linter/fixtures/yaml/keyword-error.yml +0 -1
  271. package/tests/utils/linter/fixtures/yaml/missing-semicolon.yml +0 -2
  272. package/tests/utils/linter/fixtures/yaml/syntax-error.yml +0 -1
  273. package/tests/utils/linter/fixtures/yaml/undeclare-variable.yml +0 -1
  274. package/tests/utils/linter/fixtures/yaml/unused-variable.yml +0 -2
  275. package/tests/utils/linter/fixtures/yaml/valid-code.yml +0 -3
  276. package/tests/utils/linter/index.test.mjs +0 -440
  277. package/tests/utils/linter/scan-results.mjs +0 -42
  278. package/tests/utils/load-config.test.mjs +0 -141
  279. package/tests/utils/markdown/index.test.mjs +0 -478
  280. package/tests/utils/mermaid-validator.test.mjs +0 -541
  281. package/tests/utils/mock-chat-model.mjs +0 -12
  282. package/tests/utils/preferences-utils.test.mjs +0 -465
  283. package/tests/utils/save-value-to-config.test.mjs +0 -483
  284. package/tests/utils/utils.test.mjs +0 -941
@@ -1,10 +1,12 @@
1
- import { basename, join } from "node:path";
2
-
1
+ import { basename, extname, join, relative } from "node:path";
2
+ import slugify from "slugify";
3
3
  import { publishDocs as publishDocsFn } from "@aigne/publish-docs";
4
4
  import { BrokerClient } from "@blocklet/payment-broker-client/node";
5
5
  import chalk from "chalk";
6
6
  import fs from "fs-extra";
7
7
 
8
+ import { getExtnameFromContentType } from "../../utils/file-utils.mjs";
9
+ import { isHttp } from "../../utils/utils.mjs";
8
10
  import { getAccessToken, getOfficialAccessToken } from "../../utils/auth-utils.mjs";
9
11
  import {
10
12
  CLOUD_SERVICE_URL_PROD,
@@ -16,14 +18,25 @@ import {
16
18
  import { beforePublishHook, ensureTmpDir } from "../../utils/d2-utils.mjs";
17
19
  import { deploy } from "../../utils/deploy.mjs";
18
20
  import { getGithubRepoUrl, loadConfigFromFile, saveValueToConfig } from "../../utils/utils.mjs";
21
+ import updateBranding from "../utils/update-branding.mjs";
19
22
 
20
23
  const BASE_URL = process.env.DOC_SMITH_BASE_URL || CLOUD_SERVICE_URL_PROD;
21
24
 
22
25
  export default async function publishDocs(
23
- { docsDir: rawDocsDir, appUrl, boardId, projectName, projectDesc, projectLogo },
26
+ {
27
+ docsDir: rawDocsDir,
28
+ appUrl,
29
+ boardId,
30
+ projectName,
31
+ projectDesc,
32
+ projectLogo,
33
+ translatedMetadata,
34
+ "with-branding": withBrandingOption,
35
+ },
24
36
  options,
25
37
  ) {
26
38
  let message;
39
+ let shouldWithBranding = withBrandingOption || false;
27
40
 
28
41
  try {
29
42
  // move work dir to tmp-dir
@@ -119,6 +132,12 @@ export default async function publishDocs(
119
132
  // Ensure appUrl has protocol
120
133
  appUrl = userInput.includes("://") ? userInput : `https://${userInput}`;
121
134
  } else if (["new-instance", "new-instance-continue"].includes(choice)) {
135
+ if (options?.prompts?.confirm) {
136
+ shouldWithBranding = await options.prompts.confirm({
137
+ message: "Would you like to update the project branding (title, description, logo)?",
138
+ default: true,
139
+ });
140
+ }
122
141
  // Deploy a new Discuss Kit service
123
142
  let id = "";
124
143
  let paymentUrl = "";
@@ -143,6 +162,7 @@ export default async function publishDocs(
143
162
  process.env.DOC_ROOT_DIR = docsDir;
144
163
 
145
164
  const sidebarPath = join(docsDir, "_sidebar.md");
165
+ const publishCacheFilePath = join(DOC_SMITH_DIR, "upload-cache.yaml");
146
166
 
147
167
  // Get project info from config
148
168
  const projectInfo = {
@@ -151,6 +171,39 @@ export default async function publishDocs(
151
171
  icon: projectLogo || config?.projectLogo || "",
152
172
  };
153
173
 
174
+ // Handle project logo download if it's a URL
175
+ if (projectInfo.icon && isHttp(projectInfo.icon)) {
176
+ const tempFilePath = join(docsDir, slugify(basename(projectInfo.icon)));
177
+ const initialExt = extname(projectInfo.icon);
178
+ let ext = initialExt;
179
+
180
+ try {
181
+ const response = await fetch(projectInfo.icon);
182
+ const blob = await response.blob();
183
+ const arrayBuffer = await blob.arrayBuffer();
184
+ const buffer = Buffer.from(arrayBuffer);
185
+
186
+ if (response.ok) {
187
+ if (!ext) {
188
+ const contentType = response.headers.get("content-type");
189
+ ext = getExtnameFromContentType(contentType);
190
+ }
191
+
192
+ const finalPath = ext ? `${tempFilePath}.${ext}` : tempFilePath;
193
+ fs.writeFileSync(finalPath, buffer);
194
+
195
+ // Update to relative path
196
+ projectInfo.icon = relative(join(process.cwd(), DOC_SMITH_DIR), finalPath);
197
+ }
198
+ } catch (error) {
199
+ console.warn(`Failed to download project logo from ${projectInfo.icon}: ${error.message}`);
200
+ }
201
+ }
202
+
203
+ if (shouldWithBranding) {
204
+ updateBranding({ appUrl, projectInfo, accessToken });
205
+ }
206
+
154
207
  // Construct boardMeta object
155
208
  const boardMeta = {
156
209
  category: config?.documentPurpose || [],
@@ -161,6 +214,9 @@ export default async function publishDocs(
161
214
  ...(config?.translateLanguages || []),
162
215
  ].filter((lang, index, arr) => arr.indexOf(lang) === index), // Remove duplicates
163
216
  };
217
+ if (translatedMetadata) {
218
+ boardMeta.translation = translatedMetadata;
219
+ }
164
220
 
165
221
  const {
166
222
  success,
@@ -177,7 +233,7 @@ export default async function publishDocs(
177
233
  boardDesc: projectInfo.description,
178
234
  boardCover: projectInfo.icon,
179
235
  mediaFolder: rawDocsDir,
180
- cacheFilePath: join(DOC_SMITH_DIR, "upload-cache.yaml"),
236
+ cacheFilePath: publishCacheFilePath,
181
237
  boardMeta,
182
238
  });
183
239
 
@@ -193,6 +249,7 @@ export default async function publishDocs(
193
249
  await saveValueToConfig("boardId", newBoardId);
194
250
  }
195
251
  message = `✅ Documentation published successfully!`;
252
+ await saveValueToConfig("checkoutId", "", "Checkout ID for document deployment service");
196
253
  } else {
197
254
  // If the error is 401 or 403, it means the access token is invalid
198
255
  if (error?.includes("401") || error?.includes("403")) {
@@ -214,7 +271,6 @@ export default async function publishDocs(
214
271
  }
215
272
  }
216
273
 
217
- await saveValueToConfig("checkoutId", "", "Checkout ID for document deployment service");
218
274
  return message ? { message } : {};
219
275
  }
220
276
 
@@ -234,6 +290,22 @@ publishDocs.input_schema = {
234
290
  type: "string",
235
291
  description: "The id of the board",
236
292
  },
293
+ "with-branding": {
294
+ type: "boolean",
295
+ description: "Update your website branding (title, description, logo)",
296
+ },
297
+ projectName: {
298
+ type: "string",
299
+ description: "The name of the project",
300
+ },
301
+ projectDesc: {
302
+ type: "string",
303
+ description: "The description of the project",
304
+ },
305
+ projectLogo: {
306
+ type: "string",
307
+ description: "The logo/icon of the project",
308
+ },
237
309
  },
238
310
  };
239
311
 
@@ -0,0 +1,103 @@
1
+ import { join } from "node:path";
2
+
3
+ import { AIAgent } from "@aigne/core";
4
+ import fs from "fs-extra";
5
+ import { parse as yamlParse, stringify as yamlStringify } from "yaml";
6
+ import z from "zod";
7
+
8
+ import { DOC_SMITH_DIR } from "../../utils/constants/index.mjs";
9
+
10
+ export default async function translateMeta(
11
+ { projectName, projectDesc, locale, translateLanguages = [] },
12
+ options,
13
+ ) {
14
+ const languages = [...new Set([...(locale ? [locale] : []), ...(translateLanguages || [])])];
15
+
16
+ const translationCacheFilePath = join(DOC_SMITH_DIR, "translation-cache.yaml");
17
+ await fs.ensureFile(translationCacheFilePath);
18
+ const translationCache = await fs.readFile(translationCacheFilePath, "utf-8");
19
+ const parsedTranslationCache = yamlParse(translationCache || "{}");
20
+
21
+ const titleTranslation = parsedTranslationCache[projectName] || {};
22
+ const descTranslation = parsedTranslationCache[projectDesc] || {};
23
+
24
+ const titleLanguages = languages.filter((lang) => !titleTranslation[lang]);
25
+ const descLanguages = languages.filter((lang) => !descTranslation[lang]);
26
+ const titleTranslationSchema = z.object(
27
+ titleLanguages.reduce((shape, lang) => {
28
+ shape[lang] = z.string();
29
+ return shape;
30
+ }, {}),
31
+ );
32
+ const descTranslationSchema = z.object(
33
+ descLanguages.reduce((shape, lang) => {
34
+ shape[lang] = z.string();
35
+ return shape;
36
+ }, {}),
37
+ );
38
+
39
+ const agent = AIAgent.from({
40
+ name: "translateMeta",
41
+ instructions:
42
+ "You are an **Elite Polyglot Localization and Translation Specialist** with extensive professional experience across multiple domains. Your core mission is to produce translations that are not only **100% accurate** to the source meaning but are also **natively fluent, highly readable, and culturally appropriate** in the target language.",
43
+ inputKey: "message",
44
+ outputSchema: z.object({
45
+ title: titleTranslationSchema.describe("Translated titles with language codes as keys"),
46
+ desc: descTranslationSchema.describe("Translated descriptions with language codes as keys"),
47
+ }),
48
+ });
49
+ if (titleLanguages.length > 0 || descLanguages.length > 0) {
50
+ const translatedMetadata = await options.context.invoke(agent, {
51
+ message: `Translate the following title and description into all target languages except the source language. Provide the translations in a JSON object with the language codes as keys. If the project title or description is empty, return an empty string for that field.
52
+
53
+ Project Title: ${projectName || ""}
54
+ Project Description: ${projectDesc || ""}
55
+
56
+ Target Languages: { title: ${titleLanguages.join(", ")}, desc: ${descLanguages.join(", ")} }
57
+ Source Language: ${locale}
58
+
59
+ Respond with a JSON object in the following format:
60
+ {
61
+ "title": {
62
+ "fr": "Translated Project Title in French",
63
+ "es": "Translated Project Title in Spanish",
64
+ ...
65
+ },
66
+ "desc": {
67
+ "fr": "Translated Project Description in French",
68
+ "es": "Translated Project Description in Spanish",
69
+ ...
70
+ }
71
+ }
72
+
73
+ If no translation is needed, respond with:
74
+ {
75
+ "title": {},
76
+ "desc": {}
77
+ }`,
78
+ });
79
+ Object.keys(translatedMetadata.title || {}).forEach((lang) => {
80
+ if (translatedMetadata.title[lang]) {
81
+ titleTranslation[lang] = translatedMetadata.title[lang];
82
+ }
83
+ });
84
+ Object.keys(translatedMetadata.desc || {}).forEach((lang) => {
85
+ if (translatedMetadata.desc[lang]) {
86
+ descTranslation[lang] = translatedMetadata.desc[lang];
87
+ }
88
+ });
89
+ }
90
+ const saveResult = {
91
+ ...parsedTranslationCache,
92
+ [projectName]: titleTranslation,
93
+ [projectDesc]: descTranslation,
94
+ };
95
+ await fs.writeFile(translationCacheFilePath, yamlStringify(saveResult), { encoding: "utf8" });
96
+
97
+ return {
98
+ translatedMetadata: {
99
+ title: saveResult[projectName] || {},
100
+ desc: saveResult[projectDesc] || {},
101
+ },
102
+ };
103
+ }
@@ -5,6 +5,8 @@ instructions:
5
5
  url: ../../prompts/detail/generate/system-prompt.md
6
6
  - role: user
7
7
  url: ../../prompts/detail/generate/user-prompt.md
8
+ auto_reorder_system_messages: true
9
+ auto_merge_system_messages: true
8
10
  input_schema:
9
11
  type: object
10
12
  properties:
@@ -54,31 +56,31 @@ afs:
54
56
  description: |
55
57
  Codebase of the project to be documented used as context for document generation,
56
58
  should search and read as needed while generating document content
57
- # skills:
58
- # FIXME: @zhanghan temporary disable diagram tool
59
- # - type: team
60
- # task_render_mode: collapse
61
- # name: generateDiagram
62
- # skills:
63
- # - ../generate/draw-diagram.yaml
64
- # - ../generate/wrap-diagram-code.mjs
65
- # reflection:
66
- # reviewer: ../generate/check-diagram.mjs
67
- # is_approved: isValid
68
- # max_iterations: 5
69
- # return_last_on_max_iterations: false
70
- # custom_error_message: "MUST NOT generate any diagram: validation failed after max iterations."
71
- # input_schema:
72
- # type: object
73
- # properties:
74
- # documentContent:
75
- # type: string
76
- # description: The **raw text content** of the current document. (**Note:** This is the original document and **does not include** any existing diagram source code.)
77
- # required:
78
- # - documentContent
79
- # output_schema:
80
- # type: object
81
- # properties:
82
- # diagramSourceCode:
83
- # type: string
84
- # description: The **diagram source code** generated from the input text.
59
+ keep_text_in_tool_uses: false
60
+ skills:
61
+ - type: team
62
+ task_render_mode: collapse
63
+ name: generateDiagram
64
+ skills:
65
+ - ../generate/draw-diagram.yaml
66
+ - ../generate/wrap-diagram-code.mjs
67
+ reflection:
68
+ reviewer: ../generate/check-diagram.mjs
69
+ is_approved: isValid
70
+ max_iterations: 5
71
+ return_last_on_max_iterations: false
72
+ custom_error_message: "MUST NOT generate any diagram: validation failed after max iterations."
73
+ input_schema:
74
+ type: object
75
+ properties:
76
+ documentContent:
77
+ type: string
78
+ description: The **raw text content** of the current document. (**Note:** This is the original document and **does not include** any existing diagram source code.)
79
+ required:
80
+ - documentContent
81
+ output_schema:
82
+ type: object
83
+ properties:
84
+ diagramSourceCode:
85
+ type: string
86
+ description: The **diagram source code** generated from the input text.
@@ -30,6 +30,7 @@ skills:
30
30
  default_input:
31
31
  requiredFeedback: false
32
32
  - ../utils/format-document-structure.mjs
33
+ - ../media/load-media-description.mjs
33
34
  - ../update/check-update-is-single.mjs
34
35
  - ../update/save-and-translate-document.mjs
35
36
  - url: ../utils/action-success.mjs
@@ -6,7 +6,8 @@ instructions:
6
6
  url: ../../prompts/detail/update/system-prompt.md
7
7
  - role: user
8
8
  url: ../../prompts/detail/update/user-prompt.md
9
-
9
+ auto_reorder_system_messages: true
10
+ auto_merge_system_messages: true
10
11
  input_schema:
11
12
  type: object
12
13
  properties:
@@ -53,6 +54,7 @@ afs:
53
54
  description: |
54
55
  Codebase of the project to be documented used as context for document generation,
55
56
  should search and read as needed while generating document content
57
+ keep_text_in_tool_uses: false
56
58
  skills:
57
59
  - ./document-tools/update-document-content.mjs
58
60
  task_render_mode: collapse
@@ -1,10 +1,12 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
+ import imageSize from "image-size";
3
4
  import {
4
5
  buildSourcesContent,
5
6
  calculateFileStats,
6
7
  loadFilesFromPaths,
7
8
  readFileContents,
9
+ getMimeType,
8
10
  } from "../../utils/file-utils.mjs";
9
11
  import {
10
12
  getCurrentGitHead,
@@ -29,8 +31,10 @@ export default async function loadSources({
29
31
  useDefaultPatterns = true,
30
32
  lastGitHead,
31
33
  reset = false,
34
+ media,
32
35
  } = {}) {
33
36
  let files = Array.isArray(sources) ? [...sources] : [];
37
+ const { minImageWidth } = media || { minImageWidth: 800 };
34
38
 
35
39
  if (sourcesPath) {
36
40
  const allFiles = await loadFilesFromPaths(sourcesPath, {
@@ -58,36 +62,102 @@ export default async function loadSources({
58
62
  ".bmp",
59
63
  ".webp",
60
64
  ".svg",
65
+ ".heic",
66
+ ".heif",
61
67
  ".mp4",
68
+ ".mpeg",
69
+ ".mpg",
62
70
  ".mov",
63
71
  ".avi",
72
+ ".flv",
64
73
  ".mkv",
65
74
  ".webm",
75
+ ".wmv",
66
76
  ".m4v",
77
+ ".3gpp",
67
78
  ];
68
79
 
69
80
  // Separate source files from media files
70
81
  const sourceFilesPaths = [];
71
82
  const mediaFiles = [];
72
83
 
73
- for (const file of files) {
74
- const ext = path.extname(file).toLowerCase();
75
-
76
- if (mediaExtensions.includes(ext)) {
77
- // This is a media file
78
- const relativePath = path.relative(docsDir, file);
79
- const fileName = path.basename(file);
80
- const description = path.parse(fileName).name;
81
-
82
- mediaFiles.push({
83
- name: fileName,
84
- path: relativePath,
85
- description,
86
- });
87
- } else {
88
- // This is a source file
89
- sourceFilesPaths.push(file);
90
- }
84
+ // Helper function to determine file type from extension
85
+ const getFileType = (filePath) => {
86
+ const ext = path.extname(filePath).toLowerCase();
87
+ const imageExts = [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".svg", ".heic", ".heif"];
88
+ const videoExts = [
89
+ ".mp4",
90
+ ".mpeg",
91
+ ".mpg",
92
+ ".mov",
93
+ ".avi",
94
+ ".flv",
95
+ ".mkv",
96
+ ".webm",
97
+ ".wmv",
98
+ ".m4v",
99
+ ".3gpp",
100
+ ];
101
+
102
+ if (imageExts.includes(ext)) return "image";
103
+ if (videoExts.includes(ext)) return "video";
104
+ return "media";
105
+ };
106
+
107
+ let filteredImageCount = 0;
108
+
109
+ await Promise.all(
110
+ files.map(async (file) => {
111
+ const ext = path.extname(file).toLowerCase();
112
+
113
+ if (mediaExtensions.includes(ext)) {
114
+ // This is a media file
115
+ const relativePath = path.relative(docsDir, file);
116
+ const fileName = path.basename(file);
117
+ const description = path.parse(fileName).name;
118
+
119
+ const mediaItem = {
120
+ name: fileName,
121
+ path: relativePath,
122
+ type: getFileType(relativePath),
123
+ description,
124
+ mimeType: getMimeType(file),
125
+ };
126
+
127
+ // For image files, get dimensions and filter by width
128
+ if (mediaItem.type === "image") {
129
+ try {
130
+ const buffer = await readFile(file);
131
+ const dimensions = imageSize(buffer);
132
+ mediaItem.width = dimensions.width;
133
+ mediaItem.height = dimensions.height;
134
+
135
+ // Filter out images with width less than minImageWidth
136
+ if (dimensions.width < minImageWidth) {
137
+ filteredImageCount++;
138
+ console.log(
139
+ `Filtered image: ${fileName} (${dimensions.width}x${dimensions.height}px < ${minImageWidth}px minimum)`,
140
+ );
141
+ return;
142
+ }
143
+ } catch (err) {
144
+ console.warn(`⚠️ Failed to get dimensions for ${fileName}: ${err.message}`);
145
+ }
146
+ }
147
+
148
+ mediaFiles.push(mediaItem);
149
+ } else {
150
+ // This is a source file
151
+ sourceFilesPaths.push(file);
152
+ }
153
+ }),
154
+ );
155
+
156
+ // Log summary of filtered images
157
+ if (filteredImageCount > 0) {
158
+ console.log(
159
+ `\nTotal ${filteredImageCount} low-resolution image(s) filtered for better documentation quality (minimum width: ${minImageWidth}px)\n`,
160
+ );
91
161
  }
92
162
 
93
163
  // Read all source files using the utility function
@@ -179,37 +249,6 @@ export default async function loadSources({
179
249
  }
180
250
  }
181
251
 
182
- // Generate assets content from media files
183
- let assetsContent = "# Available Media Assets for Documentation\n\n";
184
-
185
- if (mediaFiles.length > 0) {
186
- // Helper function to determine file type from extension
187
- const getFileType = (filePath) => {
188
- const ext = path.extname(filePath).toLowerCase();
189
- const imageExts = [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".svg"];
190
- const videoExts = [".mp4", ".mov", ".avi", ".mkv", ".webm", ".m4v"];
191
-
192
- if (imageExts.includes(ext)) return "image";
193
- if (videoExts.includes(ext)) return "video";
194
- return "media";
195
- };
196
-
197
- const mediaYaml = mediaFiles.map((file) => ({
198
- name: file.name,
199
- path: file.path,
200
- type: getFileType(file.path),
201
- }));
202
-
203
- assetsContent += "```yaml\n";
204
- assetsContent += "assets:\n";
205
- mediaYaml.forEach((asset) => {
206
- assetsContent += ` - name: "${asset.name}"\n`;
207
- assetsContent += ` path: "${asset.path}"\n`;
208
- assetsContent += ` type: "${asset.type}"\n`;
209
- });
210
- assetsContent += "```\n";
211
- }
212
-
213
252
  return {
214
253
  datasources: allSources,
215
254
  content,
@@ -218,7 +257,7 @@ export default async function loadSources({
218
257
  modifiedFiles,
219
258
  totalTokens,
220
259
  totalLines,
221
- assetsContent,
260
+ mediaFiles,
222
261
  isLargeContext,
223
262
  allFilesPaths,
224
263
  };
@@ -280,9 +319,20 @@ loadSources.output_schema = {
280
319
  items: { type: "string" },
281
320
  description: "Array of modified files since last generation",
282
321
  },
283
- assetsContent: {
284
- type: "string",
285
- description: "Markdown content for available media assets",
322
+ mediaFiles: {
323
+ type: "array",
324
+ items: {
325
+ type: "object",
326
+ properties: {
327
+ name: { type: "string" },
328
+ path: { type: "string" },
329
+ type: { type: "string" },
330
+ width: { type: "number" },
331
+ height: { type: "number" },
332
+ mimeType: { type: "string" },
333
+ },
334
+ },
335
+ description: "Array of media file objects (images/videos)",
286
336
  },
287
337
  },
288
338
  };
@@ -0,0 +1,69 @@
1
+ import { stat } from "node:fs/promises";
2
+ import { resolve } from "node:path";
3
+ import chalk from "chalk";
4
+ import { joinURL } from "ufo";
5
+
6
+ import { getComponentInfoWithMountPoint } from "../../utils/blocklet.mjs";
7
+ import {
8
+ CLOUD_SERVICE_URL_PROD,
9
+ CLOUD_SERVICE_URL_STAGING,
10
+ DISCUSS_KIT_DID,
11
+ DOC_SMITH_DIR,
12
+ } from "../../utils/constants/index.mjs";
13
+ import { requestWithAuthToken } from "../../utils/request.mjs";
14
+ import { uploadFiles } from "../../utils/upload-files.mjs";
15
+
16
+ export default async function updateBranding({ appUrl, projectInfo, accessToken }) {
17
+ try {
18
+ const origin = new URL(appUrl).origin;
19
+ if ([CLOUD_SERVICE_URL_PROD, CLOUD_SERVICE_URL_STAGING].includes(origin)) {
20
+ console.log("Skipped updating branding for official service\n");
21
+ return;
22
+ }
23
+
24
+ console.log(`🔄 Updating branding for ${chalk.cyan(origin)}`);
25
+
26
+ const componentInfo = await getComponentInfoWithMountPoint(origin, DISCUSS_KIT_DID);
27
+ const mountPoint = componentInfo.mountPoint || "/";
28
+
29
+ const res = await requestWithAuthToken(
30
+ joinURL(origin, mountPoint, "/api/branding"),
31
+ {
32
+ method: "PUT",
33
+ headers: {
34
+ "Content-Type": "application/json",
35
+ },
36
+ body: JSON.stringify({
37
+ appName: projectInfo.name,
38
+ appDescription: projectInfo.description,
39
+ }),
40
+ },
41
+ accessToken,
42
+ );
43
+
44
+ if (res.success) {
45
+ try {
46
+ const projectLogoPath = resolve(process.cwd(), DOC_SMITH_DIR, projectInfo.icon);
47
+ const projectLogoStat = await stat(projectLogoPath);
48
+
49
+ if (projectLogoStat.isFile()) {
50
+ // Upload to blocklet logo endpoint
51
+ await uploadFiles({
52
+ appUrl: origin,
53
+ filePaths: [projectLogoPath],
54
+ accessToken,
55
+ concurrency: 1,
56
+ endpoint: `${origin}/.well-known/service/blocklet/logo/upload/square/${componentInfo.did}`,
57
+ });
58
+ }
59
+ console.log("✅ Updated branding successfully!\n");
60
+ } catch (error) {
61
+ console.warn(`⚠️ Just failed to update logo: ${error.message}\n`);
62
+ }
63
+ } else {
64
+ console.warn(`⚠️ Failed to update branding: ${res.error}\n`);
65
+ }
66
+ } catch (error) {
67
+ console.warn(`⚠️ Failed to update branding: ${error.message}\n`);
68
+ }
69
+ }
package/aigne.yaml CHANGED
@@ -49,6 +49,11 @@ agents:
49
49
  - ./agents/publish/publish-docs.mjs
50
50
  - ./agents/publish/index.yaml
51
51
 
52
+ # Media
53
+ - ./agents/media/load-media-description.mjs
54
+ - ./agents/media/batch-generate-media-description.yaml
55
+ - ./agents/media/generate-media-description.yaml
56
+
52
57
  # Clear/Cleanup
53
58
  - ./agents/clear/choose-contents.mjs
54
59
  - ./agents/clear/clear-document-structure.mjs
@@ -56,6 +61,7 @@ agents:
56
61
  - ./agents/clear/clear-document-config.mjs
57
62
  - ./agents/clear/clear-auth-tokens.mjs
58
63
  - ./agents/clear/clear-deployment-config.mjs
64
+ - ./agents/clear/clear-media-description.mjs
59
65
 
60
66
  # Utilities
61
67
  - ./agents/utils/load-sources.mjs