@aigne/doc-smith 0.9.8 → 0.9.9-beta.1

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 (37) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/agents/create/aggregate-document-structure.mjs +21 -0
  3. package/agents/create/analyze-diagram-type-llm.yaml +1 -2
  4. package/agents/create/analyze-diagram-type.mjs +160 -2
  5. package/agents/create/generate-diagram-image.yaml +31 -0
  6. package/agents/create/generate-structure.yaml +1 -12
  7. package/agents/create/replace-d2-with-image.mjs +12 -27
  8. package/agents/create/utils/merge-document-structures.mjs +9 -3
  9. package/agents/localize/index.yaml +4 -0
  10. package/agents/localize/save-doc-translation-or-skip.mjs +18 -0
  11. package/agents/localize/set-review-content.mjs +58 -0
  12. package/agents/localize/translate-diagram.yaml +62 -0
  13. package/agents/localize/translate-document-wrapper.mjs +34 -0
  14. package/agents/localize/translate-multilingual.yaml +15 -9
  15. package/agents/localize/translate-or-skip-diagram.mjs +52 -0
  16. package/agents/publish/translate-meta.mjs +58 -6
  17. package/agents/update/generate-diagram.yaml +25 -8
  18. package/agents/update/index.yaml +1 -8
  19. package/agents/update/save-and-translate-document.mjs +5 -1
  20. package/agents/update/update-single/update-single-document-detail.mjs +52 -10
  21. package/agents/utils/analyze-feedback-intent.mjs +197 -80
  22. package/agents/utils/check-detail-result.mjs +14 -1
  23. package/agents/utils/choose-docs.mjs +3 -43
  24. package/agents/utils/save-doc-translation.mjs +2 -33
  25. package/agents/utils/save-doc.mjs +3 -37
  26. package/aigne.yaml +2 -2
  27. package/package.json +1 -1
  28. package/prompts/detail/diagram/generate-image-user.md +49 -0
  29. package/utils/d2-utils.mjs +10 -3
  30. package/utils/delete-diagram-images.mjs +3 -3
  31. package/utils/diagram-version-utils.mjs +14 -0
  32. package/utils/file-utils.mjs +40 -5
  33. package/utils/image-compress.mjs +1 -1
  34. package/utils/sync-diagram-to-translations.mjs +3 -3
  35. package/utils/translate-diagram-images.mjs +790 -0
  36. package/agents/update/check-sync-image-flag.mjs +0 -55
  37. package/agents/update/sync-images-and-exit.mjs +0 -148
@@ -1,88 +1,108 @@
1
1
  import { AIAgent } from "@aigne/core";
2
2
  import z from "zod";
3
+ import { diagramImageWithPathRegex } from "../../utils/d2-utils.mjs";
4
+
5
+ /**
6
+ * Detect existing diagram in document content using regex
7
+ * Uses generic regex patterns to detect diagram images inserted by the system
8
+ * @param {string} documentContent - Document content to analyze
9
+ * @returns {Object|null} - Diagram info if found, null otherwise
10
+ */
11
+ function detectExistingDiagram(documentContent) {
12
+ if (!documentContent || typeof documentContent !== "string") {
13
+ return null;
14
+ }
15
+
16
+ // Use generic regex to detect diagram image blocks with special markers
17
+ // Format: <!-- DIAGRAM_IMAGE_START:type:aspectRatio -->![alt](path)<!-- DIAGRAM_IMAGE_END -->
18
+ const pathMatches = Array.from(documentContent.matchAll(diagramImageWithPathRegex));
19
+
20
+ if (pathMatches.length > 0) {
21
+ // Currently each document has only one diagram, so use the first one
22
+ const firstPathMatch = pathMatches[0];
23
+ const imagePath = firstPathMatch[4]; // Path is in capture group 4 (groups: 1=type, 2=aspectRatio, 3=timestamp, 4=path)
24
+ const fullMatch = firstPathMatch[0];
25
+
26
+ return {
27
+ path: imagePath,
28
+ index: 0,
29
+ markdown: fullMatch,
30
+ };
31
+ }
32
+
33
+ return null;
34
+ }
3
35
 
4
36
  /**
5
37
  * Analyze user feedback to determine the intent type for document updates
6
- * Returns one of: "addDiagram", "deleteDiagram", "updateDiagram", "updateDocument"
7
- * Returns null if feedback is empty or invalid
38
+ * Supports two modes:
39
+ * - Global mode (no documentContent): Only analyzes feedback to determine intentType
40
+ * - Single document mode (with documentContent): Detects diagrams and provides full analysis
41
+ * Returns: { intentType, diagramInfo, generationMode, changes }
8
42
  */
9
- export default async function analyzeFeedbackIntent({ feedback, shouldUpdateDiagrams }, options) {
43
+ export default async function analyzeFeedbackIntent(
44
+ { feedback, shouldUpdateDiagrams, documentContent },
45
+ options,
46
+ ) {
47
+ // Check if documentContent is available (single document mode vs global mode)
48
+ const hasDocumentContent =
49
+ documentContent && typeof documentContent === "string" && documentContent.trim();
50
+
10
51
  // Check if feedback exists and is not empty
11
52
  // If feedback is empty and --diagram flag is set, default to updateDiagram
12
- // Otherwise return null
13
53
  if (!feedback || typeof feedback !== "string" || !feedback.trim()) {
14
54
  // If --diagram flag is set, default to updateDiagram (user wants to update diagrams)
15
55
  if (shouldUpdateDiagrams) {
16
- return { intentType: "updateDiagram" };
56
+ const diagramInfo = hasDocumentContent ? detectExistingDiagram(documentContent) : null;
57
+ return {
58
+ intentType: "updateDiagram",
59
+ diagramInfo,
60
+ generationMode: diagramInfo ? "image-to-image" : "add-new",
61
+ changes: [],
62
+ };
17
63
  }
18
- return { intentType: null };
64
+ // No feedback and no flag - return minimal structure
65
+ const diagramInfo = hasDocumentContent ? detectExistingDiagram(documentContent) : null;
66
+ return {
67
+ intentType: null,
68
+ diagramInfo,
69
+ generationMode: null,
70
+ changes: [],
71
+ };
19
72
  }
20
73
 
21
- // Always analyze user feedback first, even if --diagram flag is set
22
- // This ensures user's explicit intent (e.g., "remove image", "delete diagram") is respected
23
- // The --diagram flag should only be a hint, not override explicit user commands
74
+ // Step 1: Detect existing diagram only if documentContent is available (single document mode)
75
+ const diagramInfo = hasDocumentContent ? detectExistingDiagram(documentContent) : null;
24
76
 
25
- const instructions = `<role>
26
- You are a feedback intent analyzer. Your task is to determine which type of content modifications are needed based on the user's feedback.
77
+ // Step 2: Build instructions with diagram context (only in single document mode)
78
+ let diagramContextSection = "";
79
+ if (hasDocumentContent) {
80
+ if (diagramInfo) {
81
+ diagramContextSection = `The document contains an existing diagram: ${diagramInfo.path}`;
82
+ } else {
83
+ diagramContextSection = `The document does not contain any diagram images.`;
84
+ }
85
+ }
27
86
 
28
- You must analyze the user's feedback and classify it into one of the following intent types:
29
- - addDiagram: User wants to add a new diagram/image/chart
30
- - deleteDiagram: User wants to remove/delete a diagram/image/chart
87
+ const instructions = `Analyze the user feedback and classify the intent:
88
+
89
+ **Intent Types:**
90
+ - addDiagram: User wants to add/create a new diagram/image/chart
91
+ - deleteDiagram: User wants to remove/delete an existing diagram/image/chart
31
92
  - updateDiagram: User wants to modify/update an existing diagram/image/chart
32
- - updateDocument: User wants to update document content (text, sections, etc.) without diagram operations
33
- </role>
34
-
35
- <intent_classification_rules>
36
- **deleteDiagram** - Use this when user explicitly wants to remove or delete a diagram/image/chart:
37
- - Keywords: remove, delete, 删除, 移除, 去掉, 清除
38
- - Combined with: diagram, image, picture, chart, graph, 图表, 图片, 图, 架构图
39
- - Examples:
40
- - "Remove the diagram"
41
- - "Delete the image"
42
- - "删除这张图片"
43
- - "Remove the second diagram"
44
- - "去掉架构图"
45
- - "Remove image from page 3"
46
- - "Delete the chart showing the flow"
47
-
48
- **addDiagram** - Use this when user wants to add a new diagram:
49
- - Keywords: add, create, insert, 添加, 创建, 插入
50
- - Combined with: diagram, image, picture, chart, graph, 图表, 图片, 图
51
- - Examples:
52
- - "Add a diagram showing the architecture"
53
- - "Create a flow chart"
54
- - "添加一个架构图"
55
-
56
- **updateDiagram** - Use this when user wants to modify an existing diagram:
57
- - Keywords: update, modify, change, improve, 更新, 修改, 改进
58
- - Combined with: diagram, image, picture, chart, graph, 图表, 图片, 图
59
- - Examples:
60
- - "Update the diagram to show the new process"
61
- - "Modify the chart to include more details"
62
- - "更新架构图"
63
-
64
- **updateDocument** - Use this for all other content modifications:
65
- - Text changes, section updates, content improvements
66
- - No mention of diagrams/images/charts
67
- - Examples:
68
- - "Update the introduction section"
69
- - "Fix the typo in paragraph 2"
70
- - "Improve the explanation"
71
- </intent_classification_rules>
72
-
73
- <user_feedback>
74
- {{feedback}}
75
- </user_feedback>
76
-
77
- <analysis_guidelines>
78
- 1. Pay close attention to action verbs (remove, delete, add, update, etc.)
79
- 2. Identify the target object (diagram, image, chart, or general content)
80
- 3. If feedback mentions removing/deleting a diagram/image/chart → deleteDiagram
81
- 4. If feedback mentions adding a diagram/image/chart → addDiagram
82
- 5. If feedback mentions updating a diagram/image/chart → updateDiagram
83
- 6. If feedback is about general content without diagram references → updateDocument
84
- 7. When in doubt, prioritize the most explicit action mentioned in the feedback
85
- </analysis_guidelines>`;
93
+ - updateDocument: User wants to update text/content (not diagram-related)
94
+
95
+ ${diagramContextSection ? `**Context:** ${diagramContextSection}` : ""}
96
+
97
+ **User Feedback:**
98
+ ${feedback.trim()}
99
+
100
+ **For updateDiagram, determine generationMode:**
101
+ - "image-to-image": Diagram exists AND user describes changes (default when diagram exists)
102
+ - "text-only": User explicitly requests regenerating from text only
103
+ - "add-new": No existing diagram
104
+
105
+ Analyze the feedback and return the intent type, generationMode (if applicable), and any specific changes mentioned.`;
86
106
 
87
107
  try {
88
108
  const analyzeUpdateFeedbackIntentAgent = AIAgent.from({
@@ -93,6 +113,14 @@ You must analyze the user's feedback and classify it into one of the following i
93
113
  instructions,
94
114
  inputSchema: z.object({
95
115
  feedback: z.string().describe("User feedback for content modifications"),
116
+ diagramInfo: z
117
+ .object({
118
+ path: z.string(),
119
+ index: z.number(),
120
+ markdown: z.string(),
121
+ })
122
+ .nullable()
123
+ .describe("Existing diagram information if found, null otherwise"),
96
124
  }),
97
125
  outputSchema: z.object({
98
126
  intentType: z
@@ -100,36 +128,125 @@ You must analyze the user's feedback and classify it into one of the following i
100
128
  .describe(
101
129
  "The primary type of user intention: one of addDiagram, deleteDiagram, updateDiagram, updateDocument",
102
130
  ),
131
+ generationMode: z
132
+ .enum(["image-to-image", "text-only", "add-new", "remove-image"])
133
+ .optional()
134
+ .describe(
135
+ "Generation mode for diagram operations. Only relevant for addDiagram/updateDiagram. 'image-to-image' for modifying existing, 'text-only' for regenerating from text, 'add-new' for creating new.",
136
+ ),
137
+ changes: z
138
+ .array(z.string())
139
+ .optional()
140
+ .describe(
141
+ "Specific changes mentioned in feedback (e.g., ['add node: database', 'modify style: modern']). Only relevant for updateDiagram.",
142
+ ),
103
143
  }),
104
144
  });
105
145
 
106
- const { intentType } = await options.context.invoke(analyzeUpdateFeedbackIntentAgent, {
146
+ const llmInput = {
107
147
  feedback: feedback.trim(),
108
- });
148
+ diagramInfo,
149
+ };
150
+ const llmResult = await options.context.invoke(analyzeUpdateFeedbackIntentAgent, llmInput);
151
+
152
+ // Handle case where LLM returns null, undefined, or invalid result
153
+ if (!llmResult || typeof llmResult !== "object") {
154
+ throw new Error(`LLM returned invalid result: ${JSON.stringify(llmResult)}`);
155
+ }
156
+
157
+ let { intentType, generationMode, changes } = llmResult;
158
+
159
+ // If LLM returned null/undefined intentType, try to infer from feedback content
160
+ if (!intentType) {
161
+ const feedbackLower = feedback.toLowerCase();
162
+ const hasDiagramTerms =
163
+ feedbackLower.includes("图") ||
164
+ feedbackLower.includes("diagram") ||
165
+ feedbackLower.includes("chart") ||
166
+ feedbackLower.includes("节点") ||
167
+ feedbackLower.includes("node");
168
+
169
+ if (hasDiagramTerms && diagramInfo) {
170
+ intentType = "updateDiagram";
171
+ } else if (hasDiagramTerms && !diagramInfo) {
172
+ intentType = "addDiagram";
173
+ } else {
174
+ intentType = "updateDocument";
175
+ }
176
+ }
109
177
 
110
178
  // If --diagram flag is set and user didn't explicitly request delete/add,
111
179
  // default to updateDiagram (for backward compatibility)
112
- // But if user explicitly requested delete/add, respect that intent
113
180
  if (
114
181
  shouldUpdateDiagrams &&
115
182
  intentType &&
116
183
  !["deleteDiagram", "addDiagram"].includes(intentType)
117
184
  ) {
118
- return { intentType: "updateDiagram" };
185
+ intentType = "updateDiagram";
119
186
  }
120
187
 
121
- return { intentType };
188
+ // Set default generationMode based on intentType and diagramInfo if not provided by LLM
189
+ if (!generationMode) {
190
+ if (intentType === "deleteDiagram") {
191
+ generationMode = "remove-image";
192
+ } else if (intentType === "addDiagram") {
193
+ generationMode = "add-new";
194
+ } else if (intentType === "updateDiagram") {
195
+ generationMode = diagramInfo ? "image-to-image" : "add-new";
196
+ }
197
+ }
198
+
199
+ // Ensure we always have a valid intentType at this point
200
+ if (!intentType) {
201
+ intentType = "updateDocument";
202
+ generationMode = generationMode || null;
203
+ }
204
+
205
+ return {
206
+ intentType,
207
+ diagramInfo,
208
+ generationMode,
209
+ changes: changes || [],
210
+ };
122
211
  } catch (error) {
212
+ console.warn(`[analyzeFeedbackIntent] Failed to analyze feedback intent: ${error.message}`);
213
+
123
214
  // If analysis fails and --diagram flag is set, default to updateDiagram
124
- // Otherwise return null to fall back to default document update flow
125
215
  if (shouldUpdateDiagrams) {
126
- console.warn(
127
- `Failed to analyze feedback intent, defaulting to updateDiagram due to --diagram flag: ${error.message}`,
128
- );
129
- return { intentType: "updateDiagram" };
216
+ return {
217
+ intentType: "updateDiagram",
218
+ diagramInfo,
219
+ generationMode: diagramInfo ? "image-to-image" : "add-new",
220
+ changes: [],
221
+ };
130
222
  }
131
- console.warn(`Failed to analyze feedback intent: ${error.message}`);
132
- return { intentType: null };
223
+
224
+ // Fallback: try to infer intent from feedback content
225
+ const feedbackLower = feedback.toLowerCase();
226
+ const hasDiagramTerms =
227
+ feedbackLower.includes("图") ||
228
+ feedbackLower.includes("diagram") ||
229
+ feedbackLower.includes("chart") ||
230
+ feedbackLower.includes("节点") ||
231
+ feedbackLower.includes("node");
232
+
233
+ let fallbackIntentType = "updateDocument";
234
+ let fallbackGenerationMode = null;
235
+
236
+ if (hasDiagramTerms && diagramInfo) {
237
+ fallbackIntentType = "updateDiagram";
238
+ fallbackGenerationMode = "image-to-image";
239
+ } else if (hasDiagramTerms && !diagramInfo) {
240
+ fallbackIntentType = "addDiagram";
241
+ fallbackGenerationMode = "add-new";
242
+ }
243
+
244
+ return {
245
+ intentType: fallbackIntentType,
246
+ diagramInfo,
247
+ generationMode: fallbackGenerationMode,
248
+ changes: [],
249
+ };
133
250
  }
134
251
  }
135
252
 
@@ -1,7 +1,20 @@
1
1
  import { buildAllowedLinksFromStructure } from "../../utils/docs-finder-utils.mjs";
2
2
  import { checkMarkdown } from "../../utils/markdown-checker.mjs";
3
3
 
4
- export default async function checkDetailResult({ documentStructure, reviewContent, docsDir }) {
4
+ export default async function checkDetailResult({
5
+ documentStructure,
6
+ reviewContent,
7
+ docsDir,
8
+ isApproved: preApproved,
9
+ }) {
10
+ // If already approved (e.g., --diagram mode), skip validation
11
+ if (preApproved === true) {
12
+ return {
13
+ isApproved: true,
14
+ detailFeedback: "",
15
+ };
16
+ }
17
+
5
18
  if (!reviewContent || reviewContent.trim() === "") {
6
19
  return {
7
20
  isApproved: false,
@@ -9,7 +9,6 @@ import {
9
9
  } from "../../utils/docs-finder-utils.mjs";
10
10
  import {
11
11
  hasDiagramContent,
12
- hasBananaImages,
13
12
  getDiagramTypeLabels,
14
13
  formatDiagramTypeSuffix,
15
14
  } from "../../utils/check-document-has-diagram.mjs";
@@ -37,7 +36,6 @@ export default async function chooseDocs(
37
36
  action,
38
37
  shouldUpdateDiagrams = false,
39
38
  shouldAutoSelectDiagrams = false,
40
- shouldSyncImages = false,
41
39
  },
42
40
  options,
43
41
  ) {
@@ -59,41 +57,8 @@ export default async function chooseDocs(
59
57
  );
60
58
  }
61
59
 
62
- // If --diagram-sync flag is set, filter documents by banana images only
63
- if (shouldSyncImages) {
64
- debug("🔄 Filtering documents with banana images...");
65
-
66
- // Read content for all files and filter by banana images only
67
- const filesWithImages = [];
68
- for (const fileName of mainLanguageFiles) {
69
- const content = await readFileContent(docsDir, fileName);
70
- if (content && hasBananaImages(content)) {
71
- filesWithImages.push(fileName);
72
- }
73
- }
74
-
75
- if (filesWithImages.length === 0) {
76
- debug("ℹ️ No documents found with banana images (DIAGRAM_IMAGE_START markers).");
77
- return {
78
- selectedDocs: [],
79
- feedback: "",
80
- selectedPaths: [],
81
- };
82
- }
83
-
84
- debug(`✅ Found ${filesWithImages.length} document(s) with banana images.`);
85
- debug("📋 Auto-selecting all documents with banana images...");
86
- // Show diagram types for each document
87
- for (const file of filesWithImages) {
88
- const content = await readFileContent(docsDir, file);
89
- const diagramLabels = content ? getDiagramTypeLabels(content) : [];
90
- const diagramSuffix = formatDiagramTypeSuffix(diagramLabels);
91
- debug(` • ${file}${diagramSuffix}`);
92
- }
93
- selectedFiles = filesWithImages;
94
- }
95
60
  // If --diagram flag is set, filter documents by diagram content
96
- else if (shouldUpdateDiagrams) {
61
+ if (shouldUpdateDiagrams) {
97
62
  debug("🔄 Filtering documents with diagram content...");
98
63
 
99
64
  // Read content for all files and filter by diagram content
@@ -257,14 +222,9 @@ export default async function chooseDocs(
257
222
  }
258
223
 
259
224
  // Prompt for feedback if not provided
260
- // Skip feedback prompt if --diagram, --diagram-all, or --diagram-sync flag is set
225
+ // Skip feedback prompt if --diagram or --diagram-all flag is set
261
226
  let userFeedback = feedback;
262
- if (
263
- !userFeedback &&
264
- (requiredFeedback || foundItems?.length > 1) &&
265
- !shouldUpdateDiagrams &&
266
- !shouldSyncImages
267
- ) {
227
+ if (!userFeedback && (requiredFeedback || foundItems?.length > 1) && !shouldUpdateDiagrams) {
268
228
  const feedbackMessage = getFeedbackMessage(docAction);
269
229
 
270
230
  userFeedback = await options.prompts.input({
@@ -1,8 +1,4 @@
1
1
  import { saveDocTranslation as _saveDocTranslation } from "../../utils/utils.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
- import { syncDiagramToTranslations } from "../../utils/sync-diagram-to-translations.mjs";
6
2
 
7
3
  export default async function saveDocTranslation({
8
4
  path,
@@ -11,7 +7,6 @@ export default async function saveDocTranslation({
11
7
  language,
12
8
  labels,
13
9
  isShowMessage = false,
14
- locale,
15
10
  }) {
16
11
  await _saveDocTranslation({
17
12
  path,
@@ -21,34 +16,8 @@ export default async function saveDocTranslation({
21
16
  labels,
22
17
  });
23
18
 
24
- // Sync diagram images from main document to translations
25
- // This ensures all images (including diagrams) in the main document are synced to translation files
26
- if (path && docsDir && locale) {
27
- try {
28
- // Read main document content (it should already be saved)
29
- const mainFileName = getFileName(path, locale);
30
- const mainContent = await readFileContent(docsDir, mainFileName);
31
-
32
- if (mainContent) {
33
- const syncResult = await syncDiagramToTranslations(
34
- mainContent,
35
- path,
36
- docsDir,
37
- locale,
38
- "sync",
39
- );
40
-
41
- if (syncResult.updated > 0) {
42
- debug(
43
- `✅ Synced diagram images to ${syncResult.updated} translation file(s) after translation`,
44
- );
45
- }
46
- }
47
- } catch (error) {
48
- // Don't fail the translation if sync fails
49
- debug(`⚠️ Failed to sync diagram images after translation: ${error.message}`);
50
- }
51
- }
19
+ // Note: Diagram image translation is now handled by translate-diagram-images.mjs
20
+ // in the translate workflow, which compares timestamps and only translates when needed
52
21
 
53
22
  if (isShowMessage) {
54
23
  const message = `✅ Translation completed successfully.`;
@@ -1,6 +1,5 @@
1
1
  import { shutdownMermaidWorkerPool } from "../../utils/mermaid-worker-pool.mjs";
2
2
  import { saveDoc as _saveDoc } from "../../utils/utils.mjs";
3
- import { debug } from "../../utils/debug.mjs";
4
3
 
5
4
  export default async function saveDoc({
6
5
  path,
@@ -22,42 +21,9 @@ export default async function saveDoc({
22
21
  locale,
23
22
  });
24
23
 
25
- // Sync diagram changes to translation documents if needed
26
- // Only sync for diagram-related operations (addDiagram, updateDiagram, deleteDiagram)
27
- if (
28
- docsDir &&
29
- path &&
30
- intentType &&
31
- ["addDiagram", "updateDiagram", "deleteDiagram"].includes(intentType)
32
- ) {
33
- try {
34
- const { syncDiagramToTranslations } = await import(
35
- "../../utils/sync-diagram-to-translations.mjs"
36
- );
37
-
38
- // Determine operation type for sync
39
- // deleteDiagram -> "delete" (process even if 0 diagrams)
40
- // addDiagram/updateDiagram -> "update" (skip if 0 diagrams)
41
- const operationType = intentType === "deleteDiagram" ? "delete" : "update";
42
-
43
- const syncResult = await syncDiagramToTranslations(
44
- content,
45
- path,
46
- docsDir,
47
- locale || "en",
48
- operationType,
49
- );
50
-
51
- if (syncResult.updated > 0) {
52
- debug(
53
- `✅ Synced diagram changes to ${syncResult.updated} translation file(s) for ${intentType}`,
54
- );
55
- }
56
- } catch (error) {
57
- // Don't fail the operation if sync fails
58
- debug(`⚠️ Failed to sync diagram to translations: ${error.message}`);
59
- }
60
- }
24
+ // Note: Diagram image translation is handled separately during translate operation
25
+ // In update/add operations, we only update the main document with timestamp
26
+ // Translation documents will be updated during translate operation when timestamps differ
61
27
 
62
28
  if (isShowMessage) {
63
29
  // Shutdown mermaid worker pool to ensure clean exit
package/aigne.yaml CHANGED
@@ -80,6 +80,7 @@ agents:
80
80
  - ./agents/utils/feedback-refiner.yaml
81
81
  - ./agents/utils/analyze-structure-feedback-intent.yaml
82
82
  - ./agents/utils/analyze-document-feedback-intent.yaml
83
+ - ./agents/utils/analyze-feedback-intent.mjs
83
84
 
84
85
  - ./agents/utils/document-title-streamline.yaml
85
86
  - ./agents/utils/streamline-document-titles-if-needed.mjs
@@ -112,8 +113,7 @@ agents:
112
113
  - ./agents/update/generate-diagram.yaml
113
114
  - ./agents/update/check-generate-diagram.mjs
114
115
  - ./agents/update/pre-check-generate-diagram.yaml
115
- - ./agents/update/check-sync-image-flag.mjs
116
- - ./agents/update/sync-images-and-exit.mjs
116
+ - ./agents/localize/translate-diagram.yaml
117
117
  cli:
118
118
  chat: ./agents/chat/index.mjs
119
119
  agents:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aigne/doc-smith",
3
- "version": "0.9.8",
3
+ "version": "0.9.9-beta.1",
4
4
  "description": "AI-driven documentation generation tool built on the AIGNE Framework",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -1,3 +1,51 @@
1
+ {% if useImageToImage and existingImage %}
2
+ # Image-to-Image Generation Mode
3
+
4
+ Your task is to **update an existing diagram** based on the current document content and user feedback.
5
+
6
+ **CRITICAL INSTRUCTIONS:**
7
+ 1. **Use the existing image as the primary reference** - maintain its overall structure, layout, and visual style
8
+ 2. **Analyze the document content** to understand what changes are needed
9
+ 3. **Apply user feedback** to modify the diagram appropriately
10
+ 4. **Maintain visual consistency** - keep the same style, color scheme, and general layout unless explicitly requested to change
11
+ 5. **Make maximum changes** where needed - update content, add/remove elements, adjust relationships based on the document
12
+ 6. **Preserve what works** - keep elements that are still accurate and relevant
13
+
14
+ **Task Parameters:**
15
+ - **Diagram Type:** {{ diagramType }}
16
+ - **Visual Style:** {{ diagramStyle }} (maintain consistency with existing image unless feedback requests change)
17
+ - **Aspect Ratio:** {{ aspectRatio }}
18
+ - **Language:** English
19
+
20
+ **Existing Diagram:**
21
+ [The existing diagram image is provided as input to the model]
22
+
23
+ **Document Content:**
24
+ {% if documentSummary %}
25
+ {{ documentSummary }}
26
+ {% else %}
27
+ {{ documentContent }}
28
+ {% endif %}
29
+
30
+ **User Feedback:**
31
+ {{ feedback if feedback else "Update the diagram to match the current document content." }}
32
+
33
+ **Your responsibilities:**
34
+ 1. Analyze the existing diagram structure, style, and layout
35
+ 2. Review the document content to identify what needs to be updated
36
+ 3. Apply user feedback to modify the diagram
37
+ 4. Maintain visual consistency with the original design where appropriate
38
+ 5. Update the diagram to accurately reflect the current document content
39
+ 6. Make necessary changes while preserving the overall visual style and structure
40
+
41
+ **Style Consistency:**
42
+ - Keep the same visual style as the existing image unless user feedback explicitly requests a style change
43
+ - Maintain color scheme, node shapes, and layout patterns
44
+ - Preserve the overall aesthetic and design language
45
+
46
+ {% else %}
47
+ # Standard Text-to-Image Generation Mode
48
+
1
49
  Your task is to create a professional diagram image based on the document content below.
2
50
 
3
51
  Please follow **all global rules, styles, aspect ratio logic, and diagram-type rules** defined in the system prompt.
@@ -30,3 +78,4 @@ Now analyze the following document content to understand what should be drawn:
30
78
  {% endif %}
31
79
 
32
80
  (Use this content to determine node structure, relationships, and flow.)
81
+ {% endif %}
@@ -12,15 +12,22 @@ export const DIAGRAM_PLACEHOLDER = "DIAGRAM_PLACEHOLDER";
12
12
 
13
13
  // Diagram image regex patterns for reuse across the codebase
14
14
  // Pattern 1: Match only the start marker (for checking existence)
15
- export const diagramImageStartRegex = /<!--\s*DIAGRAM_IMAGE_START:[^>]+-->/g;
15
+ export const diagramImageStartRegex =
16
+ /<!--\s*DIAGRAM_IMAGE_START:([A-Za-z0-9_-]+):(\d+:\d+)(:\d+)?\s*-->/g;
16
17
 
17
18
  // Pattern 2: Match full diagram image block without capturing image path (for finding/replacing)
19
+ // Supports both old format (without aspectRatio) and new format (with aspectRatio)
18
20
  export const diagramImageBlockRegex =
19
- /<!--\s*DIAGRAM_IMAGE_START:[^>]+-->\s*[\s\S]*?<!--\s*DIAGRAM_IMAGE_END\s*-->/g;
21
+ /<!--\s*DIAGRAM_IMAGE_START:([A-Za-z0-9_-]+)(?::(\d+:\d+))?(:\d+)?\s*-->\s*[\s\S]*?<!--\s*DIAGRAM_IMAGE_END\s*-->/g;
20
22
 
21
23
  // Pattern 3: Match full diagram image block with image path capture (for extracting paths)
24
+ // Compatible with old format (without timestamp): <!-- DIAGRAM_IMAGE_START:type:aspectRatio -->
22
25
  export const diagramImageWithPathRegex =
23
- /<!--\s*DIAGRAM_IMAGE_START:[^>]+-->\s*!\[[^\]]*\]\(([^)]+)\)\s*<!--\s*DIAGRAM_IMAGE_END\s*-->/g;
26
+ /<!--\s*DIAGRAM_IMAGE_START:([A-Za-z0-9_-]+):(\d+:\d+)(:\d+)?\s*-->\s*!\[[^\]]*\]\(([^)]+)\)\s*<!--\s*DIAGRAM_IMAGE_END\s*-->/g;
27
+
28
+ // Pattern 4: Match full diagram image block with all details (type, aspectRatio, timestamp, path, altText)
29
+ export const diagramImageFullRegex =
30
+ /<!--\s*DIAGRAM_IMAGE_START:([A-Za-z0-9_-]+):(\d+:\d+)(:\d+)?\s*-->\s*!\[([^\]]*)\]\(([^)]+)\)\s*<!--\s*DIAGRAM_IMAGE_END\s*-->/g;
24
31
 
25
32
  export async function ensureTmpDir() {
26
33
  const tmpDir = path.join(DOC_SMITH_DIR, TMP_DIR);
@@ -2,6 +2,7 @@ import { unlink } from "node:fs/promises";
2
2
  import { join, dirname, normalize } from "node:path";
3
3
  import fs from "fs-extra";
4
4
  import { debug } from "./debug.mjs";
5
+ import { diagramImageWithPathRegex } from "./d2-utils.mjs";
5
6
 
6
7
  /**
7
8
  * Extract image file paths from markdown content
@@ -19,11 +20,10 @@ export async function extractDiagramImagePaths(content, path, docsDir) {
19
20
  const imagePaths = [];
20
21
 
21
22
  // Pattern to match: <!-- DIAGRAM_IMAGE_START:... -->![alt](path)<!-- DIAGRAM_IMAGE_END -->
22
- const { diagramImageWithPathRegex } = await import("./d2-utils.mjs");
23
- const matches = Array.from(content.matchAll(diagramImageWithPathRegex));
23
+ const matches = Array.from((content || "").matchAll(diagramImageWithPathRegex));
24
24
 
25
25
  for (const match of matches) {
26
- const imagePath = match[1];
26
+ const imagePath = match[4] || ""; // Path is in capture group 4 (groups: 1=type, 2=aspectRatio, 3=timestamp, 4=path)
27
27
 
28
28
  // Resolve absolute path
29
29
  // If imagePath is relative, resolve from document location