@aigne/doc-smith 0.9.7-beta.3 → 0.9.8-alpha.0

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 (65) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/agents/create/analyze-diagram-type-llm.yaml +160 -0
  3. package/agents/create/analyze-diagram-type.mjs +297 -0
  4. package/agents/create/check-need-generate-structure.mjs +1 -34
  5. package/agents/create/generate-diagram-image.yaml +60 -0
  6. package/agents/create/index.yaml +9 -5
  7. package/agents/create/replace-d2-with-image.mjs +625 -0
  8. package/agents/create/user-review-document-structure.mjs +8 -7
  9. package/agents/create/utils/init-current-content.mjs +5 -9
  10. package/agents/evaluate/document.yaml +6 -0
  11. package/agents/evaluate/index.yaml +1 -0
  12. package/agents/init/index.mjs +36 -388
  13. package/agents/localize/index.yaml +4 -4
  14. package/agents/media/batch-generate-media-description.yaml +2 -0
  15. package/agents/media/generate-media-description.yaml +3 -0
  16. package/agents/media/load-media-description.mjs +44 -15
  17. package/agents/publish/index.yaml +1 -0
  18. package/agents/publish/publish-docs.mjs +1 -4
  19. package/agents/update/check-diagram-flag.mjs +116 -0
  20. package/agents/update/check-document.mjs +0 -1
  21. package/agents/update/check-generate-diagram.mjs +48 -30
  22. package/agents/update/check-sync-image-flag.mjs +55 -0
  23. package/agents/update/check-update-is-single.mjs +11 -0
  24. package/agents/update/generate-diagram.yaml +43 -9
  25. package/agents/update/generate-document.yaml +9 -0
  26. package/agents/update/handle-document-update.yaml +10 -8
  27. package/agents/update/index.yaml +25 -7
  28. package/agents/update/sync-images-and-exit.mjs +148 -0
  29. package/agents/update/update-single/update-single-document-detail.mjs +131 -17
  30. package/agents/utils/analyze-feedback-intent.mjs +136 -0
  31. package/agents/utils/choose-docs.mjs +185 -40
  32. package/agents/utils/generate-document-or-skip.mjs +41 -0
  33. package/agents/utils/handle-diagram-operations.mjs +263 -0
  34. package/agents/utils/load-all-document-content.mjs +30 -0
  35. package/agents/utils/load-sources.mjs +2 -2
  36. package/agents/utils/post-generate.mjs +14 -3
  37. package/agents/utils/read-current-document-content.mjs +46 -0
  38. package/agents/utils/save-doc-translation.mjs +34 -0
  39. package/agents/utils/save-doc.mjs +42 -0
  40. package/agents/utils/save-sidebar.mjs +19 -6
  41. package/agents/utils/skip-if-content-exists.mjs +27 -0
  42. package/aigne.yaml +15 -3
  43. package/assets/report-template/report.html +17 -17
  44. package/docs-mcp/read-doc-content.mjs +30 -1
  45. package/package.json +8 -7
  46. package/prompts/detail/diagram/generate-image-system.md +135 -0
  47. package/prompts/detail/diagram/generate-image-user.md +32 -0
  48. package/prompts/detail/generate/user-prompt.md +27 -13
  49. package/prompts/evaluate/document.md +23 -10
  50. package/prompts/media/media-description/system-prompt.md +10 -2
  51. package/prompts/media/media-description/user-prompt.md +9 -0
  52. package/utils/check-document-has-diagram.mjs +95 -0
  53. package/utils/constants/index.mjs +46 -0
  54. package/utils/d2-utils.mjs +119 -178
  55. package/utils/delete-diagram-images.mjs +99 -0
  56. package/utils/docs-finder-utils.mjs +133 -25
  57. package/utils/image-compress.mjs +75 -0
  58. package/utils/kroki-utils.mjs +2 -3
  59. package/utils/load-config.mjs +29 -0
  60. package/utils/sync-diagram-to-translations.mjs +262 -0
  61. package/utils/utils.mjs +24 -0
  62. package/agents/create/check-diagram.mjs +0 -40
  63. package/agents/create/draw-diagram.yaml +0 -27
  64. package/agents/create/merge-diagram.yaml +0 -39
  65. package/agents/create/wrap-diagram-code.mjs +0 -35
@@ -10,6 +10,9 @@ task_title: Evaluate document for '{{ title }}'
10
10
  input_schema:
11
11
  type: object
12
12
  properties:
13
+ path:
14
+ type: string
15
+ description: Document path to be evaluated
13
16
  content:
14
17
  type: string
15
18
  description: Document content to be evaluated
@@ -28,6 +31,9 @@ input_schema:
28
31
  readerKnowledgeLevel:
29
32
  type: string
30
33
  description: User-selected reader knowledge level
34
+ allDocumentContentList:
35
+ type: array
36
+ description: All document content list for better evaluate
31
37
  required:
32
38
  - content
33
39
  - description
@@ -20,6 +20,7 @@ skills:
20
20
  }
21
21
  ])
22
22
  - ./document-structure.yaml
23
+ - ../utils/load-all-document-content.mjs
23
24
  - type: team
24
25
  name: batchEvaluateDocument
25
26
  skills:
@@ -2,32 +2,16 @@ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
2
  import { dirname, join } from "node:path";
3
3
  import chalk from "chalk";
4
4
  import { stringify as yamlStringify } from "yaml";
5
- import { getFilteredOptions } from "../../utils/conflict-detector.mjs";
6
5
  import {
7
6
  DEFAULT_REASONING_EFFORT_LEVEL,
8
7
  DEFAULT_THINKING_EFFORT_LEVEL,
9
- DEPTH_RECOMMENDATION_LOGIC,
10
- DOCUMENT_STYLES,
11
- DOCUMENTATION_DEPTH,
12
- PURPOSE_TO_KNOWLEDGE_MAPPING,
13
- READER_KNOWLEDGE_LEVELS,
14
- SUPPORTED_LANGUAGES,
15
- TARGET_AUDIENCES,
8
+ DIAGRAM_STYLES,
16
9
  } from "../../utils/constants/index.mjs";
17
- import { isRemoteFile } from "../../utils/file-utils.mjs";
18
10
  import loadConfig from "../../utils/load-config.mjs";
19
- import {
20
- detectSystemLanguage,
21
- getAvailablePaths,
22
- getProjectInfo,
23
- isGlobPattern,
24
- validatePath,
25
- } from "../../utils/utils.mjs";
11
+ import { detectSystemLanguage, getProjectInfo } from "../../utils/utils.mjs";
26
12
  import mapReasoningEffortLevel from "../utils/map-reasoning-effort-level.mjs";
27
13
  import { validateDocDir } from "./validate.mjs";
28
14
 
29
- const _PRESS_ENTER_TO_FINISH = "Press Enter to finish";
30
-
31
15
  /**
32
16
  * Guides the user through a multi-turn dialog to generate a YAML configuration file.
33
17
  * @param {Object} params
@@ -58,16 +42,13 @@ export default async function init(input, options) {
58
42
  };
59
43
  }
60
44
 
61
- async function _init(
62
- {
63
- outputPath = ".aigne/doc-smith",
64
- fileName = "config.yaml",
65
- skipIfExists = false,
66
- appUrl,
67
- checkOnly = false,
68
- },
69
- options,
70
- ) {
45
+ async function _init({
46
+ outputPath = ".aigne/doc-smith",
47
+ fileName = "config.yaml",
48
+ skipIfExists = false,
49
+ appUrl,
50
+ checkOnly = false,
51
+ }) {
71
52
  // Check if we're in checkOnly mode
72
53
  if (checkOnly) {
73
54
  const filePath = join(outputPath, fileName);
@@ -107,307 +88,24 @@ async function _init(
107
88
  }
108
89
  }
109
90
 
110
- console.log("🚀 Welcome to AIGNE DocSmith!");
111
- console.log("Let's set up your documentation preferences.\n");
112
-
113
91
  const input = {};
114
92
 
115
- const purposeChoices = await options.prompts.checkbox({
116
- message: "📝 [1/9]: What should your documentation help readers achieve?",
117
- choices: Object.entries(DOCUMENT_STYLES)
118
- .filter(([key]) => key !== "custom")
119
- .map(([key, style]) => ({
120
- name: `${style.name}`,
121
- description: style.description,
122
- value: key,
123
- })),
124
- validate: (input) => {
125
- if (input.length === 0) {
126
- return "You must choose at least one goal for your documentation.";
127
- }
128
- return true;
129
- },
130
- });
131
-
132
- let prioritizedPurposes = purposeChoices;
133
- if (purposeChoices.length === 1 && purposeChoices.includes("mixedPurpose")) {
134
- const topPriorities = await options.prompts.checkbox({
135
- message: "🎯 Which is most important? (Select up to 2 priorities)",
136
- choices: Object.entries(DOCUMENT_STYLES)
137
- .filter(([key]) => key !== "custom" && key !== "mixedPurpose")
138
- .map(([key, style]) => ({
139
- name: `${style.name}`,
140
- description: style.description,
141
- value: key,
142
- })),
143
- validate: (input) => {
144
- if (input.length === 0) {
145
- return "You must choose at least one priority.";
146
- }
147
- if (input.length > 2) {
148
- return "You may only choose up to 2 priorities.";
149
- }
150
- return true;
151
- },
152
- });
153
-
154
- prioritizedPurposes = topPriorities;
155
- }
156
-
157
- input.documentPurpose = prioritizedPurposes;
158
-
159
- const audienceChoices = await options.prompts.checkbox({
160
- message: "👥 [2/9]: Who will be reading your documentation?",
161
- choices: Object.entries(TARGET_AUDIENCES)
162
- .filter(([key]) => key !== "custom")
163
- .map(([key, audience]) => ({
164
- name: `${audience.name}`,
165
- description: audience.description,
166
- value: key,
167
- })),
168
- validate: (input) => {
169
- if (input.length === 0) {
170
- return "You must choose at least one audience.";
171
- }
172
- return true;
173
- },
174
- });
175
-
176
- input.targetAudienceTypes = audienceChoices;
177
-
178
- const mappedPurpose = prioritizedPurposes.find(
179
- (purpose) => PURPOSE_TO_KNOWLEDGE_MAPPING[purpose],
180
- );
181
- const defaultKnowledge = mappedPurpose ? PURPOSE_TO_KNOWLEDGE_MAPPING[mappedPurpose] : null;
182
-
183
- const { filteredOptions: filteredKnowledgeOptions } = getFilteredOptions(
184
- "readerKnowledgeLevel",
185
- { documentPurpose: prioritizedPurposes, targetAudienceTypes: audienceChoices },
186
- READER_KNOWLEDGE_LEVELS,
187
- );
188
-
189
- const knowledgeChoice = await options.prompts.select({
190
- message: "🧠 [3/9]: How much do your readers already know about your project?",
191
- choices: Object.entries(filteredKnowledgeOptions).map(([key, level]) => ({
192
- name: `${level.name}`,
193
- description: level.description,
194
- value: key,
195
- })),
196
- default: defaultKnowledge,
197
- });
198
-
199
- // Save reader knowledge level choice as key
200
- input.readerKnowledgeLevel = knowledgeChoice;
201
-
202
- // 4. Documentation depth - how comprehensive should the documentation be?
203
- // Determine default based on priority: Purpose > Audience > Knowledge Level
204
- const getDepthDefault = () => {
205
- // Check priority order: purposes -> audiences -> knowledgeLevels
206
- const checks = [
207
- () => {
208
- const purpose = prioritizedPurposes.find((p) => DEPTH_RECOMMENDATION_LOGIC.purposes[p]);
209
- return purpose ? DEPTH_RECOMMENDATION_LOGIC.purposes[purpose] : null;
210
- },
211
- () => {
212
- const audience = audienceChoices.find((a) => DEPTH_RECOMMENDATION_LOGIC.audiences[a]);
213
- return audience ? DEPTH_RECOMMENDATION_LOGIC.audiences[audience] : null;
214
- },
215
- () => DEPTH_RECOMMENDATION_LOGIC.knowledgeLevels[knowledgeChoice] || null,
216
- ];
217
-
218
- return checks.find((check) => check())?.() || null;
219
- };
220
-
221
- const defaultDepth = getDepthDefault();
222
-
223
- // Filter documentation depth options based on all previous selections
224
- const { filteredOptions: filteredDepthOptions } = getFilteredOptions(
225
- "documentationDepth",
226
- {
227
- documentPurpose: prioritizedPurposes,
228
- targetAudienceTypes: audienceChoices,
229
- readerKnowledgeLevel: knowledgeChoice,
230
- },
231
- DOCUMENTATION_DEPTH,
232
- );
233
-
234
- const depthChoice = await options.prompts.select({
235
- message: "📊 [4/9]: How detailed should your documentation be?",
236
- choices: Object.entries(filteredDepthOptions).map(([key, depth]) => ({
237
- name: `${depth.name}`,
238
- description: depth.description,
239
- value: key,
240
- })),
241
- default: defaultDepth,
242
- });
243
-
244
- // Save documentation depth choice as key
245
- input.documentationDepth = depthChoice;
246
-
247
- // 5. Language settings
248
- // Detect system language and use as default
249
- const systemLanguage = detectSystemLanguage();
250
-
251
- // Let user select primary language from supported list
252
- const primaryLanguageChoice = await options.prompts.select({
253
- message: "🌐 [5/9]: What is the main language of your documentation?",
254
- choices: SUPPORTED_LANGUAGES.map((lang) => ({
255
- name: `${lang.label} - ${lang.sample}`,
256
- value: lang.code,
257
- })),
258
- default: systemLanguage,
259
- });
260
-
261
- input.locale = primaryLanguageChoice;
262
-
263
- // 6. Translation languages
264
- // Filter out the primary language from available choices
265
- const availableTranslationLanguages = SUPPORTED_LANGUAGES.filter(
266
- (lang) => lang.code !== primaryLanguageChoice,
267
- );
268
-
269
- const translateLanguageChoices = await options.prompts.checkbox({
270
- message: "🔄 [6/9]: What languages should we translate to?",
271
- choices: availableTranslationLanguages.map((lang) => ({
272
- name: `${lang.label} - ${lang.sample}`,
273
- value: lang.code,
274
- })),
275
- });
276
-
277
- input.translateLanguages = translateLanguageChoices;
278
-
279
- // 7. Documentation directory
280
- const docsDirInput = await options.prompts.input({
281
- message: `📁 [7/9]: Where should we save your documentation?`,
282
- default: `${outputPath}/docs`,
283
- validate: validateDocDir,
284
- });
285
- input.docsDir = docsDirInput.trim() || `${outputPath}/docs`;
286
-
287
- // 8. Content sources
288
- console.log("🔍 [8/9]: Data Sources");
289
- console.log("Please specify the data source we should analyze to generate your documentation.");
290
- console.log(
291
- ` 1. Use paths like ${chalk.green("./src")}, ${chalk.green("./README.md")} or ${chalk.green("!./src/private")}.`,
292
- );
293
- console.log(
294
- ` 2. Use globs like ${chalk.green("src/**/*.js")} or ${chalk.green(
295
- "!private/**/*.js",
296
- )} for more specific file matching.`,
297
- );
298
- console.log(` 3. Use URLs like ${chalk.green("https://example.com/openapi.yaml")}.`);
299
- console.log("💡 If you leave this empty, we will scan the entire directory.");
300
-
301
- const sourcePaths = [];
302
- while (true) {
303
- const selectedPath = await options.prompts.search({
304
- message: "Please enter a valid data source:",
305
- source: async (input) => {
306
- if (!input || input.trim() === "") {
307
- return [
308
- {
309
- name: "",
310
- value: "",
311
- description: _PRESS_ENTER_TO_FINISH,
312
- },
313
- ];
314
- }
315
-
316
- let isIgnore = false;
317
- const searchTerm = input.trim();
318
- let cleanSearchTerm = searchTerm;
319
- if (cleanSearchTerm.startsWith("!")) {
320
- isIgnore = true;
321
- cleanSearchTerm = searchTerm.slice(1);
322
- }
323
-
324
- // Search for matching files and folders in current directory
325
- const availablePaths = getAvailablePaths(cleanSearchTerm);
326
-
327
- // Also add option to use as glob pattern
328
- const options = [...availablePaths].map((x) => ({
329
- ...x,
330
- name: isIgnore ? `!${x.name}` : x.name,
331
- value: isIgnore ? `!${x.value}` : x.value,
332
- }));
333
-
334
- // Check if input looks like a glob pattern
335
- const isGlobPatternResult = isGlobPattern(searchTerm);
336
- if (isGlobPatternResult) {
337
- // If it looks like a glob pattern, allow direct input
338
- options.push({
339
- name: searchTerm,
340
- value: searchTerm,
341
- description: "Use this glob pattern for file matching.",
342
- });
343
- }
344
-
345
- if (!isIgnore && isRemoteFile(searchTerm)) {
346
- options.push({
347
- name: searchTerm,
348
- value: searchTerm,
349
- description: "Use this remote url for content source.",
350
- });
351
- }
352
-
353
- return options;
354
- },
355
- });
356
-
357
- // Check if user chose to exit
358
- if (!selectedPath || selectedPath.trim() === "" || selectedPath === _PRESS_ENTER_TO_FINISH) {
359
- break;
360
- }
361
-
362
- const trimmedPath = selectedPath.trim();
93
+ // 5. Language settings - use system language detection as default
94
+ // const systemLanguage = detectSystemLanguage();
95
+ // FIXME: 临时使用中文,框架优化后需要修改
96
+ input.locale = "zh";
363
97
 
364
- // Check if it's a glob pattern
365
- const isGlobPatternResult = isGlobPattern(trimmedPath);
98
+ // 6. Translation languages - default to empty
99
+ input.translateLanguages = [];
366
100
 
367
- if (isRemoteFile(trimmedPath)) {
368
- // For remote urls, just add them without validation
369
- if (sourcePaths.includes(trimmedPath)) {
370
- console.log(`⚠️ URL already exists: ${trimmedPath}`);
371
- continue;
372
- }
373
- sourcePaths.push(trimmedPath);
374
- } else if (isGlobPatternResult) {
375
- // For glob patterns, just add them without validation
376
- if (sourcePaths.includes(trimmedPath)) {
377
- console.log(`⚠️ Pattern already exists: ${trimmedPath}`);
378
- continue;
379
- }
380
- sourcePaths.push(trimmedPath);
381
- } else {
382
- const cleanTrimmedPath = trimmedPath.startsWith("!") ? trimmedPath.slice(1) : trimmedPath;
383
- // Use validatePath to check if path is valid for regular paths
384
- const validation = validatePath(cleanTrimmedPath);
385
-
386
- if (!validation.isValid) {
387
- console.log(`⚠️ ${validation.error}`);
388
- continue;
389
- }
390
-
391
- // Avoid duplicate paths
392
- if (sourcePaths.includes(trimmedPath)) {
393
- console.log(`⚠️ Path already exists: ${trimmedPath}`);
394
- continue;
395
- }
396
-
397
- sourcePaths.push(trimmedPath);
398
- }
399
- }
101
+ // 7. Documentation directory - use default value
102
+ input.docsDir = `${outputPath}/docs`;
400
103
 
401
- // If no paths entered, use default
402
- input.sourcesPath = sourcePaths.length > 0 ? sourcePaths : ["./"];
104
+ // 8. Content sources - use "./" as default
105
+ input.sourcesPath = ["./"];
403
106
 
404
- // 9. Custom rules - any specific requirements for the documentation?
405
- const rulesInput = await options.prompts.input({
406
- message:
407
- "📋 [9/9]: Do you have any custom rules or requirements for your documentation? (Optional, press Enter to skip)",
408
- default: "",
409
- });
410
- input.rules = rulesInput.trim();
107
+ // 9. Custom rules - default to empty
108
+ input.rules = "";
411
109
 
412
110
  // Save project info to config
413
111
  const projectInfo = await getProjectInfo();
@@ -468,12 +166,6 @@ export function generateYAML(input) {
468
166
  effort: input.thinking?.effort || DEFAULT_THINKING_EFFORT_LEVEL,
469
167
  },
470
168
 
471
- // Documentation configuration
472
- documentPurpose: input.documentPurpose || [],
473
- targetAudienceTypes: input.targetAudienceTypes || [],
474
- readerKnowledgeLevel: input.readerKnowledgeLevel || "",
475
- documentationDepth: input.documentationDepth || "",
476
-
477
169
  // Custom rules and target audience (empty for user to fill)
478
170
  rules: input.rules || "",
479
171
  targetAudience: "",
@@ -516,65 +208,6 @@ export function generateYAML(input) {
516
208
  ${modelSection}
517
209
  \n`;
518
210
 
519
- // Add documentation configuration with comments
520
- yaml += "# =============================================================================\n";
521
- yaml += "# Documentation Configuration\n";
522
- yaml += "# =============================================================================\n\n";
523
-
524
- // Document Purpose with all available options
525
- yaml += "# Purpose: What's the main outcome you want readers to achieve?\n";
526
- yaml += "# Available options (uncomment and modify as needed):\n";
527
- Object.entries(DOCUMENT_STYLES).forEach(([key, style]) => {
528
- if (key !== "custom") {
529
- yaml += `# ${key.padEnd(16)} - ${style.name}: ${style.description}\n`;
530
- }
531
- });
532
-
533
- // Safely serialize documentPurpose
534
- const documentPurposeSection = yamlStringify({ documentPurpose: config.documentPurpose }).trim();
535
- yaml += `${documentPurposeSection.replace(/^documentPurpose:/, "documentPurpose:")}\n\n`;
536
-
537
- // Target Audience Types with all available options
538
- yaml += "# Target Audience: Who will be reading this most often?\n";
539
- yaml += "# Available options (uncomment and modify as needed):\n";
540
- Object.entries(TARGET_AUDIENCES).forEach(([key, audience]) => {
541
- if (key !== "custom") {
542
- yaml += `# ${key.padEnd(16)} - ${audience.name}: ${audience.description}\n`;
543
- }
544
- });
545
-
546
- // Safely serialize targetAudienceTypes
547
- const targetAudienceTypesSection = yamlStringify({
548
- targetAudienceTypes: config.targetAudienceTypes,
549
- }).trim();
550
- yaml += `${targetAudienceTypesSection.replace(/^targetAudienceTypes:/, "targetAudienceTypes:")}\n\n`;
551
-
552
- // Reader Knowledge Level with all available options
553
- yaml += "# Reader Knowledge Level: What do readers typically know when they arrive?\n";
554
- yaml += "# Available options (uncomment and modify as needed):\n";
555
- Object.entries(READER_KNOWLEDGE_LEVELS).forEach(([key, level]) => {
556
- yaml += `# ${key.padEnd(20)} - ${level.name}: ${level.description}\n`;
557
- });
558
-
559
- // Safely serialize readerKnowledgeLevel
560
- const readerKnowledgeLevelSection = yamlStringify({
561
- readerKnowledgeLevel: config.readerKnowledgeLevel,
562
- }).trim();
563
- yaml += `${readerKnowledgeLevelSection.replace(/^readerKnowledgeLevel:/, "readerKnowledgeLevel:")}\n\n`;
564
-
565
- // Documentation Depth with all available options
566
- yaml += "# Documentation Depth: How comprehensive should the documentation be?\n";
567
- yaml += "# Available options (uncomment and modify as needed):\n";
568
- Object.entries(DOCUMENTATION_DEPTH).forEach(([key, depth]) => {
569
- yaml += `# ${key.padEnd(18)} - ${depth.name}: ${depth.description}\n`;
570
- });
571
-
572
- // Safely serialize documentationDepth
573
- const documentationDepthSection = yamlStringify({
574
- documentationDepth: config.documentationDepth,
575
- }).trim();
576
- yaml += `${documentationDepthSection.replace(/^documentationDepth:/, "documentationDepth:")}\n\n`;
577
-
578
211
  // Custom Documentation Rules and Requirements
579
212
  yaml += "# Custom Rules: Define specific documentation generation rules and requirements\n";
580
213
  const rulesSection = yamlStringify({ rules: config.rules }).trim();
@@ -621,6 +254,21 @@ ${modelSection}
621
254
  }).trim();
622
255
  yaml += `# minImageWidth: Only images wider than this value (in pixels) will be used in the page generation.\n${mediaInfoSection}\n`;
623
256
 
257
+ // Diagramming configuration
258
+ yaml += "\n# Diagramming Configuration\n";
259
+ yaml +=
260
+ "# diagramming.effort: AI effort level for diagramming, 0-10, larger value means fewer diagrams\n";
261
+ yaml += "diagramming:\n";
262
+ yaml += " effort: 5 # AI effort level for diagramming, 0-10, large is less diagram\n";
263
+ yaml +=
264
+ " # Default diagram style: The primary style to use when no style is specified in feedback\n";
265
+ yaml += " # This style will be applied if feedback doesn't specify a different style\n";
266
+ yaml += " # Available options:\n";
267
+ Object.entries(DIAGRAM_STYLES).forEach(([key, style]) => {
268
+ yaml += ` # ${key.padEnd(16)} - ${style.name}: ${style.description}\n`;
269
+ });
270
+ yaml += ' # style: "modern"\n';
271
+
624
272
  return yaml;
625
273
  }
626
274
 
@@ -39,14 +39,14 @@ skills:
39
39
  input_schema:
40
40
  type: object
41
41
  properties:
42
- glossary:
43
- type: string
44
- description: Glossary file for consistent terminology (use @filename.md)
42
+ # glossary:
43
+ # type: string
44
+ # description: Glossary file for consistent terminology (use @filename.md)
45
45
  docs:
46
46
  type: array
47
47
  items:
48
48
  type: string
49
- description: Documents to translate
49
+ description: Documents to update, use document path in document structure to specify
50
50
  langs:
51
51
  type: array
52
52
  items:
@@ -25,6 +25,8 @@ input_schema:
25
25
  type: string
26
26
  type:
27
27
  type: string
28
+ svgContent:
29
+ type: string
28
30
  mediaFile:
29
31
  type: array
30
32
  items:
@@ -29,6 +29,9 @@ input_schema:
29
29
  type:
30
30
  type: string
31
31
  description: Media type (image/video)
32
+ svgContent:
33
+ type: string
34
+ description: SVG content
32
35
  mediaFile:
33
36
  type: array
34
37
  items:
@@ -6,6 +6,7 @@ import { parse, stringify } from "yaml";
6
6
  import { getMediaDescriptionCachePath } from "../../utils/file-utils.mjs";
7
7
 
8
8
  const SIZE_THRESHOLD = 10 * 1024 * 1024; // 10MB
9
+ const SVG_SIZE_THRESHOLD = 50 * 1024; // 50KB for SVG files
9
10
 
10
11
  // Supported MIME types for Gemini AI
11
12
  const SUPPORTED_IMAGE_TYPES = new Set([
@@ -16,6 +17,8 @@ const SUPPORTED_IMAGE_TYPES = new Set([
16
17
  "image/heif",
17
18
  ]);
18
19
 
20
+ const SUPPORTED_SVG_TYPES = new Set(["image/svg+xml"]);
21
+
19
22
  const SUPPORTED_VIDEO_TYPES = new Set([
20
23
  "video/mp4",
21
24
  "video/mpeg",
@@ -60,10 +63,10 @@ async function calculateMediaHash(absolutePath) {
60
63
  export default async function loadMediaDescription(input, options) {
61
64
  const { mediaFiles = [], docsDir } = input;
62
65
 
63
- // Filter to get image and video files with supported MIME types
66
+ // Filter to get image, video and svg files with supported MIME types
64
67
  const mediaFilesToProcess = mediaFiles.filter((file) => {
65
68
  if (file.type === "image") {
66
- return SUPPORTED_IMAGE_TYPES.has(file.mimeType);
69
+ return SUPPORTED_IMAGE_TYPES.has(file.mimeType) || SUPPORTED_SVG_TYPES.has(file.mimeType);
67
70
  }
68
71
  if (file.type === "video") {
69
72
  return SUPPORTED_VIDEO_TYPES.has(file.mimeType);
@@ -101,19 +104,45 @@ export default async function loadMediaDescription(input, options) {
101
104
  mediaHashMap.set(mediaFile.path, mediaHash);
102
105
 
103
106
  if (!cache[mediaHash]) {
104
- mediaToDescribe.push({
105
- ...mediaFile,
106
- hash: mediaHash,
107
- path: mediaFile.path,
108
- mediaFile: [
109
- {
110
- type: "local",
111
- path: absolutePath,
112
- filename: mediaFile.name,
113
- mimeType: mediaFile.mimeType,
114
- },
115
- ],
116
- });
107
+ const isSvg = SUPPORTED_SVG_TYPES.has(mediaFile.mimeType);
108
+
109
+ if (isSvg) {
110
+ // For SVG files, check size and read content
111
+ try {
112
+ const stats = await stat(absolutePath);
113
+ if (stats.size > SVG_SIZE_THRESHOLD) {
114
+ console.warn(
115
+ `SVG file ${mediaFile.path} exceeds ${SVG_SIZE_THRESHOLD / 1024}KB limit, skipping`,
116
+ );
117
+ continue;
118
+ }
119
+
120
+ const svgContent = await readFile(absolutePath, "utf8");
121
+ mediaToDescribe.push({
122
+ ...mediaFile,
123
+ hash: mediaHash,
124
+ path: mediaFile.path,
125
+ svgContent,
126
+ });
127
+ } catch (error) {
128
+ console.warn(`Failed to read SVG file ${mediaFile.path}:`, error.message);
129
+ }
130
+ } else {
131
+ // For non-SVG media files, use mediaFile field
132
+ mediaToDescribe.push({
133
+ ...mediaFile,
134
+ hash: mediaHash,
135
+ path: mediaFile.path,
136
+ mediaFile: [
137
+ {
138
+ type: "local",
139
+ path: absolutePath,
140
+ filename: mediaFile.name,
141
+ mimeType: mediaFile.mimeType,
142
+ },
143
+ ],
144
+ });
145
+ }
117
146
  }
118
147
  }
119
148
 
@@ -11,6 +11,7 @@ skills:
11
11
  checkOnly: true
12
12
  - url: ../init/check.mjs
13
13
  - url: ../utils/load-sources.mjs
14
+ - ../utils/save-sidebar.mjs
14
15
  - ../utils/ensure-document-icons.mjs
15
16
  - translate-meta.mjs
16
17
  - publish-docs.mjs
@@ -16,7 +16,7 @@ import {
16
16
  TMP_DIR,
17
17
  TMP_DOCS_DIR,
18
18
  } from "../../utils/constants/index.mjs";
19
- import { beforePublishHook, ensureTmpDir } from "../../utils/d2-utils.mjs";
19
+ import { ensureTmpDir } from "../../utils/d2-utils.mjs";
20
20
  import { deploy } from "../../utils/deploy.mjs";
21
21
  import { getGithubRepoUrl, loadConfigFromFile, saveValueToConfig } from "../../utils/utils.mjs";
22
22
  import updateBranding from "../utils/update-branding.mjs";
@@ -53,9 +53,6 @@ export default async function publishDocs(
53
53
  });
54
54
  await fs.cp(rawDocsDir, docsDir, { recursive: true });
55
55
 
56
- // ----------------- trigger beforePublishHook -----------------------------
57
- await beforePublishHook({ docsDir });
58
-
59
56
  // ----------------- main publish process flow -----------------------------
60
57
  // Check if DOC_DISCUSS_KIT_URL is set in environment variables
61
58
  const useEnvAppUrl = !!(