@aigne/doc-smith 0.8.15-beta → 0.8.15-beta.10

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 (89) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/agents/clear/choose-contents.mjs +4 -4
  3. package/agents/clear/clear-auth-tokens.mjs +8 -8
  4. package/agents/clear/clear-deployment-config.mjs +2 -2
  5. package/agents/clear/clear-document-config.mjs +3 -3
  6. package/agents/clear/clear-document-structure.mjs +10 -10
  7. package/agents/clear/clear-generated-docs.mjs +103 -14
  8. package/agents/clear/clear-media-description.mjs +7 -7
  9. package/agents/evaluate/document-structure.yaml +3 -1
  10. package/agents/evaluate/document.yaml +3 -1
  11. package/agents/evaluate/index.yaml +1 -3
  12. package/agents/generate/check-diagram.mjs +1 -1
  13. package/agents/generate/check-need-generate-structure.mjs +2 -7
  14. package/agents/generate/draw-diagram.yaml +4 -0
  15. package/agents/generate/generate-structure.yaml +117 -65
  16. package/agents/generate/index.yaml +3 -3
  17. package/agents/generate/{merge-d2-diagram.yaml → merge-diagram.yaml} +7 -6
  18. package/agents/generate/update-document-structure.yaml +1 -1
  19. package/agents/generate/user-review-document-structure.mjs +1 -0
  20. package/agents/generate/utils/merge-document-structures.mjs +30 -0
  21. package/agents/init/check.mjs +3 -1
  22. package/agents/init/index.mjs +37 -7
  23. package/agents/media/load-media-description.mjs +12 -24
  24. package/agents/publish/publish-docs.mjs +3 -8
  25. package/agents/schema/document-execution-structure.yaml +1 -1
  26. package/agents/schema/document-structure-item.yaml +23 -0
  27. package/agents/schema/document-structure-refine-item.yaml +20 -0
  28. package/agents/schema/document-structure.yaml +1 -1
  29. package/agents/translate/index.yaml +1 -4
  30. package/agents/translate/record-translation-history.mjs +6 -2
  31. package/agents/translate/translate-multilingual.yaml +1 -1
  32. package/agents/update/batch-generate-document.yaml +1 -1
  33. package/agents/update/batch-update-document.yaml +1 -1
  34. package/agents/update/check-document.mjs +50 -13
  35. package/agents/update/check-generate-diagram.mjs +26 -0
  36. package/agents/update/generate-diagram.yaml +29 -0
  37. package/agents/update/generate-document.yaml +17 -30
  38. package/agents/update/handle-document-update.yaml +10 -1
  39. package/agents/update/save-and-translate-document.mjs +18 -47
  40. package/agents/update/update-document-detail.yaml +2 -1
  41. package/agents/update/update-single-document.yaml +1 -1
  42. package/agents/update/user-review-document.mjs +6 -5
  43. package/agents/utils/choose-docs.mjs +8 -2
  44. package/agents/utils/load-sources.mjs +132 -57
  45. package/agents/utils/{save-docs.mjs → post-generate.mjs} +2 -51
  46. package/agents/utils/save-doc-translation.mjs +27 -0
  47. package/agents/utils/{save-single-doc.mjs → save-doc.mjs} +17 -12
  48. package/agents/utils/save-sidebar.mjs +59 -0
  49. package/agents/utils/transform-detail-data-sources.mjs +45 -0
  50. package/aigne.yaml +16 -8
  51. package/package.json +2 -1
  52. package/prompts/common/document/content-rules-core.md +6 -6
  53. package/prompts/common/document/media-file-list-usage-rules.md +12 -0
  54. package/prompts/common/document/openapi-usage-rules.md +36 -0
  55. package/prompts/common/document/role-and-personality.md +1 -2
  56. package/prompts/common/document-structure/conflict-resolution-guidance.md +2 -2
  57. package/prompts/common/document-structure/document-structure-rules.md +8 -8
  58. package/prompts/common/document-structure/output-constraints.md +3 -3
  59. package/prompts/detail/custom/custom-components.md +38 -3
  60. package/prompts/detail/d2-diagram/rules.md +11 -14
  61. package/prompts/detail/d2-diagram/system-prompt.md +33 -21
  62. package/prompts/detail/d2-diagram/user-prompt.md +39 -0
  63. package/prompts/detail/generate/document-rules.md +3 -3
  64. package/prompts/detail/generate/system-prompt.md +2 -6
  65. package/prompts/detail/generate/user-prompt.md +20 -24
  66. package/prompts/detail/update/system-prompt.md +2 -6
  67. package/prompts/detail/update/user-prompt.md +7 -6
  68. package/prompts/evaluate/document.md +0 -4
  69. package/prompts/structure/check-document-structure.md +4 -4
  70. package/prompts/structure/generate/system-prompt.md +0 -31
  71. package/prompts/structure/generate/user-prompt.md +99 -26
  72. package/prompts/structure/review/structure-review-system.md +79 -0
  73. package/prompts/structure/update/system-prompt.md +1 -1
  74. package/prompts/structure/update/user-prompt.md +4 -4
  75. package/prompts/translate/code-block.md +13 -3
  76. package/prompts/translate/translate-document.md +1 -1
  77. package/types/document-structure-schema.mjs +3 -3
  78. package/utils/docs-finder-utils.mjs +48 -0
  79. package/utils/extract-api.mjs +32 -0
  80. package/utils/file-utils.mjs +124 -93
  81. package/utils/history-utils.mjs +20 -8
  82. package/utils/load-config.mjs +1 -1
  83. package/utils/markdown-checker.mjs +35 -1
  84. package/utils/openapi/index.mjs +24 -0
  85. package/utils/utils.mjs +67 -65
  86. package/agents/generate/document-structure-tools/generate-sub-structure.mjs +0 -131
  87. package/agents/generate/generate-structure-without-tools.yaml +0 -65
  88. package/agents/utils/transform-detail-datasources.mjs +0 -23
  89. package/prompts/common/document/media-handling-rules.md +0 -9
@@ -2,6 +2,10 @@ import { access, readFile } from "node:fs/promises";
2
2
  import { dirname, join } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { TeamAgent } from "@aigne/core";
5
+ import fs from "fs-extra";
6
+ import pMap from "p-map";
7
+
8
+ import { getFileName } from "../../utils/utils.mjs";
5
9
  import checkDetailResult from "../utils/check-detail-result.mjs";
6
10
 
7
11
  // Get current script directory
@@ -17,13 +21,13 @@ export default async function checkDocument(
17
21
  modifiedFiles,
18
22
  forceRegenerate,
19
23
  locale,
24
+ translates,
20
25
  ...rest
21
26
  },
22
27
  options,
23
28
  ) {
24
29
  // Check if the detail file already exists
25
- const flatName = path.replace(/^\//, "").replace(/\//g, "-");
26
- const fileFullName = locale === "en" ? `${flatName}.md` : `${flatName}.${locale}.md`;
30
+ const fileFullName = getFileName(path, locale);
27
31
  const filePath = join(docsDir, fileFullName);
28
32
  let detailGenerated = true;
29
33
  let fileContent = null;
@@ -83,28 +87,60 @@ export default async function checkDocument(
83
87
  contentValidationFailed = true;
84
88
  }
85
89
  }
90
+ const languages = translates.map((x) => x.language);
91
+ const lackLanguages = new Set(languages);
92
+ const skills = [];
86
93
 
87
94
  // If file exists, sourceIds haven't changed, source files haven't changed, and content validation passes, no need to regenerate
88
95
  if (detailGenerated && !sourceIdsChanged && !contentValidationFailed && !forceRegenerate) {
89
- return {
90
- path,
91
- docsDir,
92
- ...rest,
93
- detailGenerated: true,
94
- };
96
+ await pMap(
97
+ languages,
98
+ async (x) => {
99
+ const languageFileName = getFileName(path, x);
100
+ const languageFilePath = join(docsDir, languageFileName);
101
+ if (await fs.exists(languageFilePath)) {
102
+ lackLanguages.delete(x);
103
+ }
104
+ },
105
+ { concurrency: 10 },
106
+ );
107
+ if (lackLanguages.size === 0) {
108
+ return {
109
+ path,
110
+ docsDir,
111
+ ...rest,
112
+ detailGenerated: true,
113
+ };
114
+ }
115
+ // translations during generation don't need feedback, content is satisfactory
116
+ rest.content = fileContent;
117
+ } else {
118
+ skills.push(options.context.agents["handleDocumentUpdate"]);
95
119
  }
96
120
 
121
+ skills.push(options.context.agents["translateMultilingual"]);
122
+
97
123
  const teamAgent = TeamAgent.from({
98
124
  name: "generateDocument",
99
- skills: [
100
- options.context.agents["handleDocumentUpdate"],
101
- options.context.agents["translateMultilingual"],
102
- options.context.agents["saveSingleDoc"],
103
- ],
125
+ skills,
104
126
  });
127
+ let openAPISpec = null;
128
+
129
+ if (options.context?.userContext?.openAPISpec?.sourceId) {
130
+ const matchingDocument = originalDocumentStructure.find((item) => {
131
+ if (item.path === path) {
132
+ return item.sourceIds.find((x) => x === options.context.userContext.openAPISpec.sourceId);
133
+ }
134
+ return false;
135
+ });
136
+ if (matchingDocument) {
137
+ openAPISpec = options.context.userContext.openAPISpec;
138
+ }
139
+ }
105
140
 
106
141
  const result = await options.context.invoke(teamAgent, {
107
142
  ...rest,
143
+ translates: translates.filter((x) => lackLanguages.has(x.language)),
108
144
  locale,
109
145
  docsDir,
110
146
  path,
@@ -112,6 +148,7 @@ export default async function checkDocument(
112
148
  originalDocumentStructure,
113
149
  documentStructure,
114
150
  detailFeedback: contentValidationFailed ? validationResult.detailFeedback : "",
151
+ openAPISpec,
115
152
  });
116
153
 
117
154
  return {
@@ -0,0 +1,26 @@
1
+ const placeholder = "DIAGRAM_PLACEHOLDER";
2
+
3
+ export default async function checkGenerateDiagram(
4
+ { needDiagram, documentContent, locale },
5
+ options,
6
+ ) {
7
+ if (!needDiagram) {
8
+ return {};
9
+ }
10
+
11
+ const generateAgent = options.context?.agents?.["generateDiagram"];
12
+ let content = documentContent;
13
+
14
+ try {
15
+ const { diagramSourceCode } = await options.context.invoke(generateAgent, {
16
+ documentContent,
17
+ locale,
18
+ });
19
+ content = content.replace(placeholder, diagramSourceCode);
20
+ } catch (error) {
21
+ // FIXME: @zhanghan should regenerate document without diagram
22
+ content = content.replace(placeholder, "");
23
+ console.log(`⚠️ Skip generate any diagram: ${error.message}`);
24
+ }
25
+ return { content };
26
+ }
@@ -0,0 +1,29 @@
1
+ type: team
2
+ task_render_mode: collapse
3
+ name: generateDiagram
4
+ skills:
5
+ - ../generate/draw-diagram.yaml
6
+ - ../generate/wrap-diagram-code.mjs
7
+ reflection:
8
+ reviewer: ../generate/check-diagram.mjs
9
+ is_approved: isValid
10
+ max_iterations: 5
11
+ return_last_on_max_iterations: false
12
+ input_schema:
13
+ type: object
14
+ properties:
15
+ documentContent:
16
+ type: string
17
+ description: The **raw text content** of the current document. (**Note:** This is the original document and **does not include** any diagram source code.)
18
+ locale:
19
+ type: string
20
+ description: Language for diagram labels and text
21
+ default: en
22
+ required:
23
+ - documentContent
24
+ output_schema:
25
+ type: object
26
+ properties:
27
+ diagramSourceCode:
28
+ type: string
29
+ description: The **diagram source code** generated from the input text.
@@ -16,7 +16,7 @@ input_schema:
16
16
  locale:
17
17
  type: string
18
18
  description: User language, such as zh, en
19
- datasources:
19
+ detailDataSource:
20
20
  type: string
21
21
  description: Source data and context for document content generation
22
22
  targetAudience:
@@ -44,9 +44,21 @@ input_schema:
44
44
  description: Additional supplementary information
45
45
  required:
46
46
  - rules
47
- - datasources
47
+ - detailDataSource
48
48
  - originalDocumentStructure
49
- output_key: content
49
+ # output_key: content
50
+ output_schema:
51
+ type: object
52
+ properties:
53
+ content:
54
+ type: string
55
+ description: Document content
56
+ needDiagram:
57
+ type: boolean
58
+ description: Does this document need to generate diagram?
59
+ required:
60
+ - content
61
+ - detailDataSource
50
62
  afs:
51
63
  modules:
52
64
  - module: system-fs
@@ -57,30 +69,5 @@ afs:
57
69
  Codebase of the project to be documented used as context for document generation,
58
70
  should search and read as needed while generating document content
59
71
  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 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.
72
+ # skills:
73
+ # - ./generate-diagram.yaml
@@ -2,7 +2,7 @@ type: team
2
2
  name: handleDocumentUpdate
3
3
  description: Update a document in a batch
4
4
  skills:
5
- - ../utils/transform-detail-datasources.mjs
5
+ - ../utils/transform-detail-data-sources.mjs
6
6
  - type: team
7
7
  task_render_mode: collapse
8
8
  name: generateDocumentContent
@@ -23,6 +23,15 @@ skills:
23
23
  max_iterations: 5
24
24
  return_last_on_max_iterations: true
25
25
  task_title: Generate document for '{{ title }}'
26
+ - type: transform
27
+ jsonata: |
28
+ $merge([
29
+ $,
30
+ { "documentContent": content }
31
+ ])
32
+ - ./check-generate-diagram.mjs
33
+ # - ../generate/merge-diagram.yaml
34
+ - ../utils/save-doc.mjs
26
35
  input_schema:
27
36
  type: object
28
37
  properties:
@@ -1,3 +1,4 @@
1
+ import pMap from "p-map";
1
2
  import { recordUpdate } from "../../utils/history-utils.mjs";
2
3
 
3
4
  export default async function saveAndTranslateDocument(input, options) {
@@ -7,20 +8,15 @@ export default async function saveAndTranslateDocument(input, options) {
7
8
  return {};
8
9
  }
9
10
 
10
- // Saves a document with optional translation data
11
- const saveDocument = async (doc, translates = null, isTranslate = false) => {
12
- const saveAgent = options.context.agents["saveSingleDoc"];
13
-
14
- return await options.context.invoke(saveAgent, {
15
- path: doc.path,
16
- content: doc.content,
17
- docsDir: docsDir,
18
- locale: locale,
19
- translates: translates || doc.translates,
20
- labels: doc.labels,
21
- isTranslate: isTranslate,
11
+ // Record history if feedback is provided
12
+ const doc = selectedDocs[0];
13
+ if (doc.feedback?.trim()) {
14
+ recordUpdate({
15
+ operation: "document_update",
16
+ feedback: doc.feedback.trim(),
17
+ docPaths: selectedDocs.map((v) => v.path),
22
18
  });
23
- };
19
+ }
24
20
 
25
21
  // Only prompt user if translation is actually needed
26
22
  let shouldTranslate = false;
@@ -46,28 +42,6 @@ export default async function saveAndTranslateDocument(input, options) {
46
42
 
47
43
  // Save documents in batches
48
44
  const batchSize = 3;
49
- for (let i = 0; i < selectedDocs.length; i += batchSize) {
50
- const batch = selectedDocs.slice(i, i + batchSize);
51
-
52
- const savePromises = batch.map(async (doc) => {
53
- try {
54
- await saveDocument(doc);
55
-
56
- // Record history for each document if feedback is provided
57
- if (doc.feedback?.trim()) {
58
- recordUpdate({
59
- operation: "document_update",
60
- feedback: doc.feedback.trim(),
61
- documentPath: doc.path,
62
- });
63
- }
64
- } catch (error) {
65
- console.error(`❌ Failed to save document ${doc.path}:`, error.message);
66
- }
67
- });
68
-
69
- await Promise.all(savePromises);
70
- }
71
45
 
72
46
  // Return results if user chose to skip translation
73
47
  if (!shouldTranslate) {
@@ -77,30 +51,27 @@ export default async function saveAndTranslateDocument(input, options) {
77
51
  // Translate documents in batches
78
52
  const translateAgent = options.context.agents["translateMultilingual"];
79
53
 
80
- for (let i = 0; i < selectedDocs.length; i += batchSize) {
81
- const batch = selectedDocs.slice(i, i + batchSize);
82
-
83
- const translatePromises = batch.map(async (doc) => {
54
+ await pMap(
55
+ selectedDocs,
56
+ async (doc) => {
84
57
  try {
85
58
  // Clear feedback to ensure translation is not affected by update feedback
86
59
  doc.feedback = "";
87
60
 
88
- const result = await options.context.invoke(translateAgent, {
61
+ await options.context.invoke(translateAgent, {
89
62
  ...input, // context is required
90
63
  content: doc.content,
91
64
  translates: doc.translates,
92
65
  title: doc.title,
66
+ path: doc.path,
67
+ docsDir,
93
68
  });
94
-
95
- // Save the translated content
96
- await saveDocument(doc, result.translates, true);
97
69
  } catch (error) {
98
70
  console.error(`❌ Failed to translate document ${doc.path}:`, error.message);
99
71
  }
100
- });
101
-
102
- await Promise.all(translatePromises);
103
- }
72
+ },
73
+ { concurrency: batchSize },
74
+ );
104
75
 
105
76
  return {};
106
77
  }
@@ -23,7 +23,7 @@ input_schema:
23
23
  locale:
24
24
  type: string
25
25
  description: User language, e.g. zh, en
26
- datasources:
26
+ detailDataSource:
27
27
  type: string
28
28
  description: Context for document content
29
29
  glossary:
@@ -57,4 +57,5 @@ afs:
57
57
  keep_text_in_tool_uses: false
58
58
  skills:
59
59
  - ./document-tools/update-document-content.mjs
60
+ # - ./generate-diagram.yaml
60
61
  task_render_mode: collapse
@@ -1,7 +1,7 @@
1
1
  type: team
2
2
  name: updateSingleDocument
3
3
  skills:
4
- - ../utils/transform-detail-datasources.mjs
4
+ - ../utils/transform-detail-data-sources.mjs
5
5
  - ../update/user-review-document.mjs
6
6
  iterate_on: selectedDocs
7
7
  concurrency: 1
@@ -105,6 +105,7 @@ async function showDocumentDetail(content, title) {
105
105
  renderer: new markedTerminal(),
106
106
  });
107
107
 
108
+ // FIXME: @zhanghan fix error "Could not find the language 'd2', did you forget to load/include a language module?"
108
109
  const renderedMarkdown = marked(content);
109
110
 
110
111
  // Restore original console.error
@@ -121,16 +122,15 @@ async function showDocumentDetail(content, title) {
121
122
  }
122
123
  }
123
124
 
124
- export default async function userReviewDocument(
125
- { content, title, description, ...rest },
126
- options,
127
- ) {
125
+ export default async function userReviewDocument({ content, description, ...rest }, options) {
128
126
  // Check if document content exists
129
127
  if (!content || typeof content !== "string" || content.trim().length === 0) {
130
128
  console.log("Please provide document content to review.");
131
129
  return { content };
132
130
  }
133
131
 
132
+ const title = rest.documentStructure?.find((x) => x.path === rest.path)?.title;
133
+
134
134
  // Print current document headings structure
135
135
  printDocumentHeadings(content, title || "Untitled Document");
136
136
 
@@ -190,7 +190,7 @@ export default async function userReviewDocument(
190
190
  feedbacks.push(feedback.trim());
191
191
 
192
192
  // Get the updateDocument agent
193
- const updateAgent = options.context.agents["updateDocumentDetail"];
193
+ const updateAgent = options.context.agents["handleDocumentUpdate"];
194
194
  if (!updateAgent) {
195
195
  console.log(
196
196
  "We can't process your feedback right now. The document update feature is temporarily unavailable.",
@@ -213,6 +213,7 @@ export default async function userReviewDocument(
213
213
  originalContent: options.context.userContext.currentContent,
214
214
  feedback: feedback.trim(),
215
215
  userPreferences,
216
+ title,
216
217
  });
217
218
 
218
219
  // Check if feedback should be saved as user preference
@@ -18,6 +18,7 @@ export default async function chooseDocs(
18
18
  locale,
19
19
  reset = false,
20
20
  requiredFeedback = true,
21
+ title,
21
22
  },
22
23
  options,
23
24
  ) {
@@ -50,7 +51,12 @@ export default async function chooseDocs(
50
51
  });
51
52
 
52
53
  // Use title if available, otherwise fall back to filename
53
- const displayName = docItem?.title || file;
54
+ let displayName = docItem?.title;
55
+ if (displayName) {
56
+ displayName = `${displayName} (${file})`;
57
+ } else {
58
+ displayName = file;
59
+ }
54
60
 
55
61
  return {
56
62
  name: displayName,
@@ -60,7 +66,7 @@ export default async function chooseDocs(
60
66
 
61
67
  // Let user select multiple files
62
68
  selectedFiles = await options.prompts.checkbox({
63
- message: getActionText(isTranslate, "Select documents to {action}:"),
69
+ message: title || getActionText(isTranslate, "Select documents to {action}:"),
64
70
  source: (term) => {
65
71
  if (!term) return choices;
66
72