@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
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Check if --diagram or --diagram-all flag is set via command line arguments or environment variable
3
+ * Returns the flag values and passes through all input
4
+ *
5
+ * --diagram: Filter to show only documents with diagrams, let user select
6
+ * --diagram-all: Auto-select all documents with diagrams, no user selection
7
+ */
8
+ export default function checkDiagramFlag(input) {
9
+ let shouldUpdateDiagrams = false;
10
+ let shouldAutoSelectDiagrams = false;
11
+
12
+ // Priority order: command line args > input params > environment variables
13
+ // Check command line arguments first (highest priority)
14
+ if (process.argv) {
15
+ // Check for --diagram-all or -da (exact match)
16
+ const hasDiagramAllFlag = process.argv.some((arg) => arg === "--diagram-all" || arg === "-da");
17
+
18
+ const hasDiagramFlag = process.argv.some((arg) => arg === "--diagram" || arg === "-d");
19
+
20
+ if (hasDiagramAllFlag) {
21
+ shouldUpdateDiagrams = true;
22
+ shouldAutoSelectDiagrams = true;
23
+ // Return early if CLI arg found (highest priority)
24
+ return {
25
+ ...input,
26
+ shouldUpdateDiagrams,
27
+ shouldAutoSelectDiagrams,
28
+ };
29
+ } else if (hasDiagramFlag) {
30
+ shouldUpdateDiagrams = true;
31
+ shouldAutoSelectDiagrams = false;
32
+ // Return early if CLI arg found (highest priority)
33
+ return {
34
+ ...input,
35
+ shouldUpdateDiagrams,
36
+ shouldAutoSelectDiagrams,
37
+ };
38
+ }
39
+ }
40
+
41
+ // Check input parameter (second priority - CLI framework might parse --diagram-all as separate parameter)
42
+ if (input["diagram-all"] === true || input["diagram-all"] === "true") {
43
+ shouldUpdateDiagrams = true;
44
+ shouldAutoSelectDiagrams = true;
45
+ // Return early if input param found (second priority)
46
+ return {
47
+ ...input,
48
+ shouldUpdateDiagrams,
49
+ shouldAutoSelectDiagrams,
50
+ };
51
+ } else if (input.diagram === true || input.diagram === "true") {
52
+ shouldUpdateDiagrams = true;
53
+ shouldAutoSelectDiagrams = false;
54
+ // Return early if input param found (second priority)
55
+ return {
56
+ ...input,
57
+ shouldUpdateDiagrams,
58
+ shouldAutoSelectDiagrams,
59
+ };
60
+ }
61
+
62
+ // Check environment variable (lowest priority)
63
+ if (
64
+ process.env.DOC_SMITH_UPDATE_DIAGRAMS === "all" ||
65
+ process.env.DOC_SMITH_UPDATE_DIAGRAMS_ALL === "true" ||
66
+ process.env.DOC_SMITH_UPDATE_DIAGRAMS_ALL === "1"
67
+ ) {
68
+ shouldUpdateDiagrams = true;
69
+ shouldAutoSelectDiagrams = true;
70
+ } else if (
71
+ process.env.DOC_SMITH_UPDATE_DIAGRAMS === "true" ||
72
+ process.env.DOC_SMITH_UPDATE_DIAGRAMS === "1"
73
+ ) {
74
+ shouldUpdateDiagrams = true;
75
+ shouldAutoSelectDiagrams = false;
76
+ }
77
+
78
+ // Return all input plus the flags
79
+ return {
80
+ ...input,
81
+ shouldUpdateDiagrams,
82
+ shouldAutoSelectDiagrams,
83
+ };
84
+ }
85
+
86
+ checkDiagramFlag.input_schema = {
87
+ type: "object",
88
+ properties: {
89
+ diagram: {
90
+ type: ["boolean", "string"],
91
+ description:
92
+ "Flag to trigger diagram update: true for user selection (can also use --diagram CLI arg)",
93
+ },
94
+ "diagram-all": {
95
+ type: ["boolean", "string"],
96
+ description:
97
+ "Flag to auto-select all documents with diagrams (can also use --diagram-all CLI arg)",
98
+ },
99
+ },
100
+ };
101
+
102
+ checkDiagramFlag.output_schema = {
103
+ type: "object",
104
+ properties: {
105
+ shouldUpdateDiagrams: {
106
+ type: "boolean",
107
+ description: "Whether to filter and update diagrams",
108
+ },
109
+ shouldAutoSelectDiagrams: {
110
+ type: "boolean",
111
+ description:
112
+ "Whether to auto-select all documents with diagrams (true for --diagram-all, false for --diagram)",
113
+ },
114
+ },
115
+ required: ["shouldUpdateDiagrams", "shouldAutoSelectDiagrams"],
116
+ };
@@ -31,7 +31,6 @@ export default async function checkDocument(
31
31
  const filePath = join(docsDir, fileFullName);
32
32
  let detailGenerated = true;
33
33
  let fileContent = null;
34
-
35
34
  try {
36
35
  await access(filePath);
37
36
  // If file exists, read its content for validation
@@ -1,4 +1,4 @@
1
- import { DIAGRAM_PLACEHOLDER, replacePlaceholderWithD2 } from "../../utils/d2-utils.mjs";
1
+ import { hasDiagramContent } from "../../utils/check-document-has-diagram.mjs";
2
2
 
3
3
  const DEFAULT_DIAGRAMMING_EFFORT = 5;
4
4
  const MIN_DIAGRAMMING_EFFORT = 0;
@@ -11,23 +11,39 @@ export default async function checkGenerateDiagram(
11
11
  feedback,
12
12
  detailFeedback,
13
13
  originalContent,
14
- path: docPath,
15
14
  diagramming,
15
+ path,
16
+ docsDir,
17
+ shouldUpdateDiagrams,
16
18
  },
17
19
  options,
18
20
  ) {
19
21
  let content = documentContent;
20
- let diagramSourceCode;
21
22
  let skipGenerateDiagram = false;
22
23
 
23
- const preCheckAgent = options.context?.agents?.["preCheckGenerateDiagram"];
24
+ // If --diagram flag is set and document already has d2 code blocks,
25
+ // skip preCheck and directly replace existing diagrams
26
+ // This is because when using --diagram/--diagram-all, the user explicitly wants to update diagrams,
27
+ // so we should skip the "do we need to generate diagram" check and directly proceed to replacement
28
+ const hasExistingDiagrams = originalContent && hasDiagramContent(originalContent);
24
29
 
25
- const preCheckResult = await options.context.invoke(preCheckAgent, {
26
- documentContent,
27
- feedback,
28
- detailFeedback,
29
- previousGenerationContent: originalContent,
30
- });
30
+ // Skip preCheck if:
31
+ // 1. Using --diagram/--diagram-all flag (shouldUpdateDiagrams === true) AND
32
+ // 2. Document already has d2 code blocks (hasExistingDiagrams === true)
33
+ // This means user explicitly wants to update existing diagrams, no need to check if diagram is needed
34
+ const shouldSkipPreCheck = shouldUpdateDiagrams === true && hasExistingDiagrams;
35
+
36
+ let preCheckResult = { details: [], content: null };
37
+ if (!shouldSkipPreCheck) {
38
+ const preCheckAgent = options.context?.agents?.["preCheckGenerateDiagram"];
39
+
40
+ preCheckResult = await options.context.invoke(preCheckAgent, {
41
+ documentContent,
42
+ feedback,
43
+ detailFeedback,
44
+ previousGenerationContent: originalContent,
45
+ });
46
+ }
31
47
 
32
48
  const totalScore = (preCheckResult.details || []).reduce((acc, curr) => acc + curr.score, 0);
33
49
  if (![false, "false", "", undefined, null].includes(preCheckResult.content)) {
@@ -47,7 +63,11 @@ export default async function checkGenerateDiagram(
47
63
  );
48
64
  }
49
65
 
50
- if (totalScore <= diagrammingEffort) {
66
+ // If we skipped preCheck because document has existing diagrams and --diagram flag is set,
67
+ // we should NOT skip generating diagram (we need to replace existing ones)
68
+ if (shouldSkipPreCheck) {
69
+ skipGenerateDiagram = false;
70
+ } else if (totalScore <= diagrammingEffort) {
51
71
  skipGenerateDiagram = true;
52
72
  }
53
73
 
@@ -56,30 +76,28 @@ export default async function checkGenerateDiagram(
56
76
  } else {
57
77
  try {
58
78
  const generateAgent = options.context?.agents?.["generateDiagram"];
59
- ({ diagramSourceCode } = await options.context.invoke(generateAgent, {
79
+ const result = await options.context.invoke(generateAgent, {
60
80
  documentContent: content,
61
81
  locale,
62
- }));
63
- } catch (error) {
64
- diagramSourceCode = "";
65
- skipGenerateDiagram = true;
66
- console.log(`⚠️ Skip generate any diagram for ${docPath}: ${error.message}`);
67
- }
82
+ diagramming: diagramming || {},
83
+ feedback: feedback || "",
84
+ originalContent: originalContent || documentContent,
85
+ path,
86
+ docsDir,
87
+ });
68
88
 
69
- if (diagramSourceCode && !skipGenerateDiagram) {
70
- if (content.includes(DIAGRAM_PLACEHOLDER)) {
71
- content = replacePlaceholderWithD2({
72
- content,
73
- diagramSourceCode,
74
- });
89
+ // generateDiagram now returns { content } with image already inserted
90
+ // The image replaces DIAGRAM_PLACEHOLDER or D2 code blocks
91
+ if (result?.content) {
92
+ content = result.content;
75
93
  } else {
76
- const mergeAgent = options.context?.agents?.["mergeDiagramToDocument"];
77
- ({ content } = await options.context.invoke(mergeAgent, {
78
- diagramSourceCode,
79
- content,
80
- }));
94
+ // Fallback: if no content returned, use original document content
95
+ content = documentContent;
81
96
  }
82
- } else {
97
+ } catch (error) {
98
+ skipGenerateDiagram = true;
99
+ console.log(`⚠️ Skip generate any diagram: ${error.message}`);
100
+ // On error, return original document content
83
101
  content = documentContent;
84
102
  }
85
103
  }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Check if --diagram-sync flag is set via command line arguments or environment variable
3
+ * Returns the flag value and passes through all input
4
+ *
5
+ * --diagram-sync: Auto-select all documents with banana images and sync to translations
6
+ */
7
+ export default function checkSyncImageFlag(input) {
8
+ let shouldSyncImages = false;
9
+
10
+ // Check command line arguments first (highest priority)
11
+ if (process.argv) {
12
+ const hasSyncImageFlag = process.argv.some((arg) => arg === "--diagram-sync" || arg === "-ds");
13
+ if (hasSyncImageFlag) {
14
+ shouldSyncImages = true;
15
+ }
16
+ }
17
+
18
+ // Check input parameter
19
+ if (input["diagram-sync"] === true || input.diagramSync === true) {
20
+ shouldSyncImages = true;
21
+ }
22
+
23
+ // Check environment variable
24
+ if (process.env.DOC_SMITH_SYNC_IMAGES === "true" || process.env.DOC_SMITH_SYNC_IMAGES === "1") {
25
+ shouldSyncImages = true;
26
+ }
27
+
28
+ // Return all input plus the flag
29
+ return {
30
+ ...input,
31
+ shouldSyncImages,
32
+ };
33
+ }
34
+
35
+ checkSyncImageFlag.input_schema = {
36
+ type: "object",
37
+ properties: {
38
+ "diagram-sync": {
39
+ type: ["boolean", "string"],
40
+ description:
41
+ "Flag to sync images to translations (can also use --diagram-sync CLI arg or DOC_SMITH_SYNC_IMAGES env var)",
42
+ },
43
+ },
44
+ };
45
+
46
+ checkSyncImageFlag.output_schema = {
47
+ type: "object",
48
+ properties: {
49
+ shouldSyncImages: {
50
+ type: "boolean",
51
+ description: "Whether to sync images to translations",
52
+ },
53
+ },
54
+ required: ["shouldSyncImages"],
55
+ };
@@ -29,6 +29,17 @@ export default async function checkUpdateIsSingle({ selectedDocs, ...rest }, opt
29
29
  ...rest,
30
30
  });
31
31
 
32
+ // For batch updates, preserve selectedDocs and other context for save-and-translate-document
33
+ // batchUpdateDocument returns an array of results, but save-and-translate-document needs selectedDocs
34
+ if (agentName === "batchUpdateDocument") {
35
+ return {
36
+ ...rest,
37
+ selectedDocs, // Preserve selectedDocs for save-and-translate-document.mjs
38
+ result, // Include the batch update results
39
+ };
40
+ }
41
+
42
+ // For single document updates, return result as-is
32
43
  return result;
33
44
  } catch (error) {
34
45
  console.error(
@@ -2,13 +2,22 @@ type: team
2
2
  task_render_mode: collapse
3
3
  name: generateDiagram
4
4
  skills:
5
- - ../create/draw-diagram.yaml
6
- - ../create/wrap-diagram-code.mjs
7
- reflection:
8
- reviewer: ../create/check-diagram.mjs
9
- is_approved: isValid
10
- max_iterations: 5
11
- return_last_on_max_iterations: false
5
+ # Step 1: Analyze document content to determine diagram type and style
6
+ - ../create/analyze-diagram-type.mjs
7
+ # Step 2: Transform output for image generation agent (map aspectRatio to ratio, add size)
8
+ - type: transform
9
+ jsonata: |
10
+ $merge([
11
+ $,
12
+ {
13
+ "ratio": aspectRatio,
14
+ "size": "1K"
15
+ }
16
+ ])
17
+ # Step 3: Generate diagram image directly with unified agent
18
+ - ../create/generate-diagram-image.yaml
19
+ # Step 4: Replace placeholder with generated image
20
+ - ../create/replace-d2-with-image.mjs
12
21
  input_schema:
13
22
  type: object
14
23
  properties:
@@ -19,11 +28,36 @@ input_schema:
19
28
  type: string
20
29
  description: Language for diagram labels and text
21
30
  default: en
31
+ diagramming:
32
+ type: object
33
+ description: Diagramming configuration
34
+ properties:
35
+ style:
36
+ type: string
37
+ description: Default diagram style to use when no style is specified in feedback
38
+ feedback:
39
+ type: string
40
+ description: User feedback that may contain style or type preferences, or diagram index (e.g., 'use anthropomorphic style', 'update the second diagram')
41
+ default: ""
42
+ originalContent:
43
+ type: string
44
+ description: Original document content before modifications. Used to find existing diagrams when updating (may contain DIAGRAM_IMAGE_START, ```d2, or ```mermaid code blocks)
45
+ diagramIndex:
46
+ type: number
47
+ description: Index of the diagram to replace (0-based). If not provided, will try to extract from feedback.
48
+ path:
49
+ type: string
50
+ description: Document path (e.g., "guides/getting-started.md") used for generating image filename
51
+ docsDir:
52
+ type: string
53
+ description: Documentation directory where assets will be saved (relative to project root)
22
54
  required:
23
55
  - documentContent
24
56
  output_schema:
25
57
  type: object
26
58
  properties:
27
- diagramSourceCode:
59
+ content:
28
60
  type: string
29
- description: The **diagram source code** generated from the input text.
61
+ description: The document content with D2 code blocks replaced by generated diagram image.
62
+ required:
63
+ - content
@@ -32,6 +32,9 @@ input_schema:
32
32
  type: string
33
33
  path:
34
34
  type: string
35
+ originalContent:
36
+ type: string
37
+ description: The original content of the document before update
35
38
  parentId:
36
39
  type:
37
40
  - string
@@ -42,6 +45,12 @@ input_schema:
42
45
  additionalInformation:
43
46
  type: string
44
47
  description: Additional supplementary information
48
+ intentType:
49
+ type:
50
+ - string
51
+ - "null"
52
+ description: User intent type analyzed from feedback (addDiagram, updateDiagram, deleteDiagram, updateDocument). Can be null if not specified.
53
+ enum: ["addDiagram", "updateDiagram", "deleteDiagram", "updateDocument", null]
45
54
  required:
46
55
  - rules
47
56
  - detailDataSource
@@ -3,6 +3,10 @@ name: handleDocumentUpdate
3
3
  description: Update a document in a batch
4
4
  skills:
5
5
  - ../utils/transform-detail-data-sources.mjs
6
+ # Check if document generation should be skipped (if content already exists)
7
+ # If intentType is diagram-related and content exists, mark skipGenerateDocument so generation can be short-circuited
8
+ - url: ../utils/skip-if-content-exists.mjs
9
+ # Generate document content (skipped when skipGenerateDocument is true and content exists)
6
10
  - type: team
7
11
  task_render_mode: collapse
8
12
  name: generateDocumentContent
@@ -10,24 +14,22 @@ skills:
10
14
  - url: ../utils/find-user-preferences-by-path.mjs
11
15
  default_input:
12
16
  scope: document
13
- - ../update/generate-document.yaml
14
- - type: transform
15
- jsonata: |
16
- $merge([
17
- $,
18
- { "reviewContent": content }
19
- ])
17
+ - ../utils/generate-document-or-skip.mjs
20
18
  reflection:
21
19
  reviewer: ../utils/check-detail-result.mjs
22
20
  is_approved: isApproved
23
21
  max_iterations: 5
24
22
  return_last_on_max_iterations: true
25
23
  task_title: Generate document for '{{ title }}'
24
+ # Ensure content, documentContent, and originalContent are set
26
25
  - type: transform
27
26
  jsonata: |
28
27
  $merge([
29
28
  $,
30
- { "documentContent": content }
29
+ {
30
+ "documentContent": content,
31
+ "originalContent": content
32
+ }
31
33
  ])
32
34
  - ./check-generate-diagram.mjs
33
35
  # - ../generate/merge-diagram.yaml
@@ -2,7 +2,7 @@ type: team
2
2
  name: update
3
3
  alias:
4
4
  - up
5
- description: Update specific documents and their translations
5
+ description: Update specific documents and their translations. Use --diagram to filter and select documents with diagrams, --diagram-all to auto-update all diagrams, or --diagram-sync to sync existing images to translations.
6
6
  skills:
7
7
  - url: ../init/index.mjs
8
8
  default_input:
@@ -19,12 +19,18 @@ skills:
19
19
  'documentStructure': originalDocumentStructure
20
20
  }
21
21
  ])
22
+ # Check if --diagram or --diagram-all flag is set
23
+ - url: ../update/check-diagram-flag.mjs
24
+ # Check if --diagram-sync flag is set
25
+ - url: ../update/check-sync-image-flag.mjs
22
26
  - url: ../utils/choose-docs.mjs
23
27
  default_input:
24
28
  requiredFeedback: false
25
29
  - ../utils/format-document-structure.mjs
26
30
  - ../utils/ensure-document-icons.mjs
27
31
  - ../media/load-media-description.mjs
32
+ - ../utils/analyze-feedback-intent.mjs
33
+ - ../update/sync-images-and-exit.mjs
28
34
  - ../update/check-update-is-single.mjs
29
35
  - ../update/save-and-translate-document.mjs
30
36
  - url: ../utils/action-success.mjs
@@ -33,20 +39,32 @@ skills:
33
39
  input_schema:
34
40
  type: object
35
41
  properties:
36
- glossary:
37
- type: string
38
- description: Glossary file for consistent terminology (use @filename.md)
42
+ # glossary:
43
+ # type: string
44
+ # description: Glossary file for consistent terminology (use @filename.md)
39
45
  docs:
40
46
  type: array
41
47
  items:
42
48
  type: string
43
- description: Documents to update
49
+ description: Documents to update, use document path in document structure to specify, eg ["/getting-started", "/overview"]
44
50
  feedback:
45
51
  type: string
46
52
  description: Tell us what to change in this content
47
- reset:
53
+ isChat:
48
54
  type: boolean
49
- description: Start fresh - ignore previous versions
55
+ description: Whether the update is from chat
56
+ # reset:
57
+ # type: boolean
58
+ # description: Start fresh - ignore previous versions
59
+ # diagram:
60
+ # type: ["boolean", "string"]
61
+ # description: "Flag to update diagrams: true for user selection (can also use --diagram CLI arg)"
62
+ # "diagram-all":
63
+ # type: ["boolean", "string"]
64
+ # description: "Flag to auto-select all documents with diagrams (can also use --diagram-all CLI arg)"
65
+ # "diagram-sync":
66
+ # type: ["boolean", "string"]
67
+ # description: "Flag to sync existing banana images to translations (can also use --diagram-sync CLI arg or DOC_SMITH_SYNC_IMAGES env var)"
50
68
  output_schema:
51
69
  type: object
52
70
  properties:
@@ -0,0 +1,148 @@
1
+ import { syncDiagramToTranslations } from "../../utils/sync-diagram-to-translations.mjs";
2
+ import { readFileContent } from "../../utils/docs-finder-utils.mjs";
3
+ import { getFileName } from "../../utils/utils.mjs";
4
+ import { debug } from "../../utils/debug.mjs";
5
+
6
+ /**
7
+ * Sync images to translations and exit if shouldSyncImages is true
8
+ * Otherwise, passes through input for normal update flow
9
+ *
10
+ * This agent combines:
11
+ * - Image synchronization logic
12
+ * - Flow routing (sync vs normal update)
13
+ * - Early exit handling
14
+ */
15
+ export default async function syncImagesAndExit(input, options) {
16
+ // If shouldSyncImages is false, pass through for normal update flow
17
+ if (!input.shouldSyncImages) {
18
+ return input;
19
+ }
20
+
21
+ // Sync images flow
22
+ const { selectedDocs = [], docsDir, locale = "en" } = input;
23
+
24
+ if (!docsDir) {
25
+ throw new Error("docsDir is required for image sync. Please ensure config is loaded.");
26
+ }
27
+
28
+ if (!Array.isArray(selectedDocs) || selectedDocs.length === 0) {
29
+ const actionSuccessAgent = options.context?.agents?.["actionSuccess"];
30
+ if (actionSuccessAgent) {
31
+ await options.context.invoke(actionSuccessAgent, {
32
+ action: "ℹ️ No documents selected for image sync",
33
+ });
34
+ }
35
+ // Exit normally to prevent passing to next steps
36
+ process.exit(0);
37
+ }
38
+
39
+ const results = {
40
+ updated: 0,
41
+ skipped: 0,
42
+ errors: [],
43
+ };
44
+
45
+ // Process each document
46
+ for (const doc of selectedDocs) {
47
+ try {
48
+ // Use content from doc if available, otherwise read from file
49
+ let mainContent = doc.content;
50
+ if (!mainContent) {
51
+ const mainFileName = getFileName(doc.path, locale);
52
+ mainContent = await readFileContent(docsDir, mainFileName);
53
+ }
54
+
55
+ if (!mainContent) {
56
+ debug(`⚠️ Could not read main document: ${doc.path}`);
57
+ results.skipped++;
58
+ continue;
59
+ }
60
+
61
+ // Sync images to translation documents
62
+ const syncResult = await syncDiagramToTranslations(mainContent, doc.path, docsDir, locale);
63
+
64
+ results.updated += syncResult.updated;
65
+ results.skipped += syncResult.skipped;
66
+ results.errors.push(...syncResult.errors);
67
+
68
+ if (syncResult.updated > 0) {
69
+ debug(`✅ Synced images from ${doc.path} to ${syncResult.updated} translation file(s)`);
70
+ } else if (syncResult.skipped > 0) {
71
+ debug(
72
+ `⏭️ No changes needed for ${doc.path} (${syncResult.skipped} translation file(s) already in sync)`,
73
+ );
74
+ }
75
+ } catch (error) {
76
+ debug(`❌ Error syncing images for ${doc.path}: ${error.message}`);
77
+ results.errors.push({
78
+ doc: doc.path,
79
+ error: error.message,
80
+ });
81
+ }
82
+ }
83
+
84
+ // Generate success message
85
+ const message = `✅ Image sync completed: ${results.updated} translation file(s) updated, ${results.skipped} skipped${
86
+ results.errors.length > 0 ? `, ${results.errors.length} error(s)` : ""
87
+ }`;
88
+
89
+ // Show success message
90
+ const actionSuccessAgent = options.context?.agents?.["actionSuccess"];
91
+ if (actionSuccessAgent) {
92
+ await options.context.invoke(actionSuccessAgent, {
93
+ action: message,
94
+ });
95
+ }
96
+
97
+ // Exit normally to prevent passing to next steps and avoid validation errors
98
+ // This ensures a clean exit after successful image sync
99
+ process.exit(0);
100
+ }
101
+
102
+ syncImagesAndExit.input_schema = {
103
+ type: "object",
104
+ properties: {
105
+ shouldSyncImages: {
106
+ type: "boolean",
107
+ description: "Whether to trigger image sync to translations",
108
+ },
109
+ selectedDocs: {
110
+ type: "array",
111
+ description: "Array of selected documents to sync images from",
112
+ },
113
+ docsDir: {
114
+ type: "string",
115
+ description: "Documentation directory where documents are stored",
116
+ },
117
+ locale: {
118
+ type: "string",
119
+ description: "Main language locale (e.g., 'en', 'zh')",
120
+ default: "en",
121
+ },
122
+ },
123
+ required: ["shouldSyncImages"],
124
+ };
125
+
126
+ syncImagesAndExit.output_schema = {
127
+ type: "object",
128
+ properties: {
129
+ message: {
130
+ type: "string",
131
+ description: "Summary message of the sync operation",
132
+ },
133
+ updated: {
134
+ type: "number",
135
+ description: "Number of translation files successfully updated",
136
+ },
137
+ skipped: {
138
+ type: "number",
139
+ description: "Number of translation files skipped (no changes needed)",
140
+ },
141
+ errors: {
142
+ type: "array",
143
+ description: "Array of errors encountered during sync",
144
+ },
145
+ },
146
+ };
147
+
148
+ syncImagesAndExit.task_render_mode = "hide";