@aigne/doc-smith 0.8.6 → 0.8.8

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 (117) hide show
  1. package/.aigne/doc-smith/output/structure-plan.json +1 -5
  2. package/CHANGELOG.md +14 -0
  3. package/README.md +3 -3
  4. package/agents/{chat.yaml → chat/index.yaml} +7 -7
  5. package/agents/generate/check-document-structure.yaml +30 -0
  6. package/agents/{check-structure-plan.mjs → generate/check-need-generate-structure.mjs} +20 -20
  7. package/agents/{structure-planning.yaml → generate/generate-structure.yaml} +6 -6
  8. package/agents/{docs-generator.yaml → generate/index.yaml} +11 -12
  9. package/agents/generate/refine-document-structure.yaml +12 -0
  10. package/agents/{input-generator.mjs → init/index.mjs} +12 -5
  11. package/agents/{manage-prefs.mjs → prefs/index.mjs} +1 -1
  12. package/agents/{team-publish-docs.yaml → publish/index.yaml} +1 -2
  13. package/agents/{publish-docs.mjs → publish/publish-docs.mjs} +6 -5
  14. package/agents/schema/{structure-plan-result.yaml → document-execution-structure.yaml} +3 -3
  15. package/agents/schema/document-structure.yaml +26 -0
  16. package/agents/{language-selector.mjs → translate/choose-language.mjs} +5 -5
  17. package/agents/{retranslate.yaml → translate/index.yaml} +11 -12
  18. package/agents/{translate.yaml → translate/translate-document.yaml} +3 -2
  19. package/agents/{batch-translate.yaml → translate/translate-multilingual.yaml} +5 -5
  20. package/agents/update/batch-generate-document.yaml +19 -0
  21. package/agents/{check-detail.mjs → update/check-document.mjs} +16 -16
  22. package/agents/{detail-generator-and-translate.yaml → update/generate-and-translate-document.yaml} +23 -23
  23. package/agents/update/generate-document.yaml +50 -0
  24. package/agents/{detail-regenerator.yaml → update/index.yaml} +10 -11
  25. package/agents/{action-success.mjs → utils/action-success.mjs} +1 -1
  26. package/agents/{check-detail-result.mjs → utils/check-detail-result.mjs} +3 -3
  27. package/agents/{check-feedback-refiner.mjs → utils/check-feedback-refiner.mjs} +6 -6
  28. package/agents/{find-items-by-paths.mjs → utils/choose-docs.mjs} +25 -13
  29. package/agents/{docs-fs.yaml → utils/docs-fs-actor.yaml} +3 -1
  30. package/agents/{feedback-refiner.yaml → utils/feedback-refiner.yaml} +2 -4
  31. package/agents/{find-item-by-path.mjs → utils/find-item-by-path.mjs} +17 -11
  32. package/agents/{find-user-preferences-by-path.mjs → utils/find-user-preferences-by-path.mjs} +1 -1
  33. package/agents/utils/format-document-structure.mjs +25 -0
  34. package/agents/{load-sources.mjs → utils/load-sources.mjs} +41 -28
  35. package/agents/{save-docs.mjs → utils/save-docs.mjs} +16 -16
  36. package/agents/{save-single-doc.mjs → utils/save-single-doc.mjs} +2 -2
  37. package/agents/{transform-detail-datasources.mjs → utils/transform-detail-datasources.mjs} +1 -1
  38. package/aigne.yaml +35 -35
  39. package/docs-mcp/analyze-docs-relevance.yaml +10 -10
  40. package/docs-mcp/docs-search.yaml +5 -3
  41. package/package.json +10 -8
  42. package/prompts/{document → detail/custom}/custom-code-block.md +6 -6
  43. package/prompts/detail/custom/custom-components.md +172 -0
  44. package/prompts/{document → detail}/d2-chart/rules.md +95 -1
  45. package/prompts/{document → detail}/detail-example.md +80 -61
  46. package/prompts/{document/detail-generator.md → detail/document-rules.md} +4 -8
  47. package/prompts/{content-detail-generator.md → detail/generate-document.md} +48 -25
  48. package/prompts/{check-structure-planning-result.md → structure/check-document-structure.md} +23 -17
  49. package/prompts/{document/structure-planning.md → structure/document-rules.md} +0 -2
  50. package/prompts/{structure-planning.md → structure/generate-structure.md} +51 -30
  51. package/prompts/{document → structure}/structure-example.md +2 -2
  52. package/prompts/{document → structure}/structure-getting-started.md +2 -2
  53. package/prompts/translate/glossary.md +6 -0
  54. package/prompts/{translator.md → translate/translate-document.md} +29 -10
  55. package/prompts/{feedback-refiner.md → utils/feedback-refiner.md} +8 -8
  56. package/tests/agents/chat/chat.test.mjs +46 -0
  57. package/tests/agents/generate/check-document-structure.test.mjs +51 -0
  58. package/tests/agents/generate/check-need-generate-structure.test.mjs +292 -0
  59. package/tests/agents/generate/generate-structure.test.mjs +51 -0
  60. package/tests/{input-generator.test.mjs → agents/init/init.test.mjs} +13 -13
  61. package/tests/agents/prefs/prefs.test.mjs +431 -0
  62. package/tests/agents/publish/publish-docs.test.mjs +642 -0
  63. package/tests/agents/translate/choose-language.test.mjs +311 -0
  64. package/tests/agents/translate/translate-document.test.mjs +51 -0
  65. package/tests/agents/update/check-document.test.mjs +523 -0
  66. package/tests/agents/update/generate-document.test.mjs +51 -0
  67. package/tests/agents/utils/action-success.test.mjs +54 -0
  68. package/tests/{check-detail-result.test.mjs → agents/utils/check-detail-result.test.mjs} +98 -98
  69. package/tests/agents/utils/check-feedback-refiner.test.mjs +478 -0
  70. package/tests/agents/utils/choose-docs.test.mjs +413 -0
  71. package/tests/agents/utils/exit.test.mjs +70 -0
  72. package/tests/agents/utils/feedback-refiner.test.mjs +51 -0
  73. package/tests/agents/utils/find-item-by-path.test.mjs +517 -0
  74. package/tests/agents/utils/find-user-preferences-by-path.test.mjs +382 -0
  75. package/tests/agents/utils/format-document-structure.test.mjs +264 -0
  76. package/tests/agents/utils/fs.test.mjs +267 -0
  77. package/tests/{load-sources.test.mjs → agents/utils/load-sources.test.mjs} +153 -25
  78. package/tests/{save-docs.test.mjs → agents/utils/save-docs.test.mjs} +11 -5
  79. package/tests/agents/utils/save-output.test.mjs +315 -0
  80. package/tests/agents/utils/save-single-doc.test.mjs +364 -0
  81. package/tests/agents/utils/transform-detail-datasources.test.mjs +363 -0
  82. package/tests/utils/auth-utils.test.mjs +358 -0
  83. package/tests/utils/blocklet.test.mjs +334 -0
  84. package/tests/{conflict-resolution.test.mjs → utils/conflict-detector.test.mjs} +3 -3
  85. package/tests/utils/constants.test.mjs +295 -0
  86. package/tests/utils/d2-utils.test.mjs +423 -0
  87. package/tests/{deploy.test.mjs → utils/deploy.test.mjs} +25 -36
  88. package/tests/utils/docs-finder-utils.test.mjs +625 -0
  89. package/tests/utils/file-utils.test.mjs +213 -0
  90. package/tests/{kroki-utils.test.mjs → utils/kroki-utils.test.mjs} +2 -2
  91. package/tests/utils/load-config.test.mjs +141 -0
  92. package/tests/{mermaid-validation.test.mjs → utils/mermaid-validator.test.mjs} +2 -2
  93. package/tests/utils/mock-chat-model.mjs +12 -0
  94. package/tests/{preferences-utils.test.mjs → utils/preferences-utils.test.mjs} +1 -1
  95. package/tests/{save-value-to-config.test.mjs → utils/save-value-to-config.test.mjs} +61 -4
  96. package/tests/utils/utils.test.mjs +939 -0
  97. package/utils/conflict-detector.mjs +1 -1
  98. package/utils/constants.mjs +5 -3
  99. package/utils/d2-utils.mjs +194 -0
  100. package/utils/docs-finder-utils.mjs +26 -26
  101. package/utils/icon-map.mjs +26 -0
  102. package/{agents → utils}/load-config.mjs +2 -18
  103. package/utils/markdown-checker.mjs +5 -5
  104. package/agents/batch-docs-detail-generator.yaml +0 -19
  105. package/agents/check-structure-planning-result.yaml +0 -30
  106. package/agents/content-detail-generator.yaml +0 -50
  107. package/agents/format-structure-plan.mjs +0 -25
  108. package/agents/reflective-structure-planner.yaml +0 -12
  109. package/agents/schema/structure-plan.yaml +0 -26
  110. package/prompts/document/custom-components.md +0 -104
  111. package/tests/README.md +0 -93
  112. package/tests/utils.test.mjs +0 -2067
  113. /package/agents/{exit.mjs → utils/exit.mjs} +0 -0
  114. /package/agents/{fs.mjs → utils/fs.mjs} +0 -0
  115. /package/agents/{save-output.mjs → utils/save-output.mjs} +0 -0
  116. /package/prompts/{document → detail}/d2-chart/official-examples.md +0 -0
  117. /package/prompts/{document → detail}/jsx/rules.md +0 -0
@@ -0,0 +1,363 @@
1
+ import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test";
2
+ import transformDetailDatasources from "../../../agents/utils/transform-detail-datasources.mjs";
3
+ import * as utils from "../../../utils/utils.mjs";
4
+
5
+ describe("transformDetailDatasources utility", () => {
6
+ let normalizePathSpy;
7
+ let toRelativePathSpy;
8
+
9
+ beforeEach(() => {
10
+ // Spy on utility functions
11
+ normalizePathSpy = spyOn(utils, "normalizePath").mockImplementation((path) =>
12
+ path?.replace(/\\/g, "/").replace(/^\.\//, ""),
13
+ );
14
+ toRelativePathSpy = spyOn(utils, "toRelativePath").mockImplementation((path) =>
15
+ path?.startsWith("/") ? path.substring(1) : path,
16
+ );
17
+ });
18
+
19
+ afterEach(() => {
20
+ // Restore all spies
21
+ normalizePathSpy?.mockRestore();
22
+ toRelativePathSpy?.mockRestore();
23
+ });
24
+
25
+ // BASIC FUNCTIONALITY TESTS
26
+ test("should transform simple datasources correctly", () => {
27
+ const input = {
28
+ sourceIds: ["/docs/guide.md", "/docs/api.md"],
29
+ datasourcesList: [
30
+ { sourceId: "/docs/guide.md", content: "# User Guide\n\nThis is a guide." },
31
+ { sourceId: "/docs/api.md", content: "# API Reference\n\nAPI documentation." },
32
+ ],
33
+ };
34
+
35
+ const result = transformDetailDatasources(input);
36
+
37
+ expect(normalizePathSpy).toHaveBeenCalledWith("/docs/guide.md");
38
+ expect(normalizePathSpy).toHaveBeenCalledWith("/docs/api.md");
39
+ expect(toRelativePathSpy).toHaveBeenCalledWith("/docs/guide.md");
40
+ expect(toRelativePathSpy).toHaveBeenCalledWith("/docs/api.md");
41
+
42
+ expect(result).toEqual({
43
+ detailDataSources:
44
+ "// sourceId: docs/guide.md\n# User Guide\n\nThis is a guide.\n" +
45
+ "// sourceId: docs/api.md\n# API Reference\n\nAPI documentation.\n",
46
+ });
47
+ });
48
+
49
+ test("should handle single datasource", () => {
50
+ const input = {
51
+ sourceIds: ["/docs/readme.md"],
52
+ datasourcesList: [
53
+ { sourceId: "/docs/readme.md", content: "# README\n\nProject documentation." },
54
+ ],
55
+ };
56
+
57
+ const result = transformDetailDatasources(input);
58
+
59
+ expect(result.detailDataSources).toBe(
60
+ "// sourceId: docs/readme.md\n# README\n\nProject documentation.\n",
61
+ );
62
+ });
63
+
64
+ test("should maintain order of sourceIds", () => {
65
+ const input = {
66
+ sourceIds: ["/docs/c.md", "/docs/a.md", "/docs/b.md"],
67
+ datasourcesList: [
68
+ { sourceId: "/docs/a.md", content: "Content A" },
69
+ { sourceId: "/docs/b.md", content: "Content B" },
70
+ { sourceId: "/docs/c.md", content: "Content C" },
71
+ ],
72
+ };
73
+
74
+ const result = transformDetailDatasources(input);
75
+
76
+ expect(result.detailDataSources).toBe(
77
+ "// sourceId: docs/c.md\nContent C\n" +
78
+ "// sourceId: docs/a.md\nContent A\n" +
79
+ "// sourceId: docs/b.md\nContent B\n",
80
+ );
81
+ });
82
+
83
+ // PATH NORMALIZATION TESTS
84
+ test("should normalize paths correctly", () => {
85
+ normalizePathSpy.mockImplementation((path) => path?.replace(/\\/g, "/"));
86
+
87
+ const input = {
88
+ sourceIds: ["docs\\guide.md", "/docs/api.md"],
89
+ datasourcesList: [
90
+ { sourceId: "docs\\guide.md", content: "Guide content" },
91
+ { sourceId: "/docs/api.md", content: "API content" },
92
+ ],
93
+ };
94
+
95
+ const result = transformDetailDatasources(input);
96
+
97
+ expect(normalizePathSpy).toHaveBeenCalledWith("docs\\guide.md");
98
+ expect(normalizePathSpy).toHaveBeenCalledWith("/docs/api.md");
99
+ expect(result.detailDataSources).toContain("Guide content");
100
+ expect(result.detailDataSources).toContain("API content");
101
+ });
102
+
103
+ test("should handle relative path conversion", () => {
104
+ toRelativePathSpy.mockImplementation((path) => path?.replace(/^\/+/, "").replace(/^\.\//, ""));
105
+
106
+ const input = {
107
+ sourceIds: ["/abs/path/file.md", "./rel/path/file.md"],
108
+ datasourcesList: [
109
+ { sourceId: "/abs/path/file.md", content: "Absolute content" },
110
+ { sourceId: "./rel/path/file.md", content: "Relative content" },
111
+ ],
112
+ };
113
+
114
+ const result = transformDetailDatasources(input);
115
+
116
+ expect(toRelativePathSpy).toHaveBeenCalledWith("/abs/path/file.md");
117
+ expect(toRelativePathSpy).toHaveBeenCalledWith("./rel/path/file.md");
118
+ expect(result.detailDataSources).toContain("abs/path/file.md");
119
+ expect(result.detailDataSources).toContain("rel/path/file.md");
120
+ });
121
+
122
+ // MISSING DATA TESTS
123
+ test("should filter out sourceIds not found in datasourcesList", () => {
124
+ const input = {
125
+ sourceIds: ["/docs/guide.md", "/docs/missing.md", "/docs/api.md"],
126
+ datasourcesList: [
127
+ { sourceId: "/docs/guide.md", content: "Guide content" },
128
+ { sourceId: "/docs/api.md", content: "API content" },
129
+ ],
130
+ };
131
+
132
+ const result = transformDetailDatasources(input);
133
+
134
+ expect(result.detailDataSources).toContain("Guide content");
135
+ expect(result.detailDataSources).toContain("API content");
136
+ expect(result.detailDataSources).not.toContain("missing");
137
+ expect(result.detailDataSources.split("// sourceId:")).toHaveLength(3); // Empty first element + 2 sources
138
+ });
139
+
140
+ test("should handle empty sourceIds array", () => {
141
+ const input = {
142
+ sourceIds: [],
143
+ datasourcesList: [
144
+ { sourceId: "/docs/guide.md", content: "Guide content" },
145
+ { sourceId: "/docs/api.md", content: "API content" },
146
+ ],
147
+ };
148
+
149
+ const result = transformDetailDatasources(input);
150
+
151
+ expect(result.detailDataSources).toBe("");
152
+ });
153
+
154
+ test("should handle empty datasourcesList array", () => {
155
+ const input = {
156
+ sourceIds: ["/docs/guide.md", "/docs/api.md"],
157
+ datasourcesList: [],
158
+ };
159
+
160
+ const result = transformDetailDatasources(input);
161
+
162
+ expect(result.detailDataSources).toBe("");
163
+ });
164
+
165
+ // NULL AND UNDEFINED HANDLING
166
+ test("should handle null sourceIds", () => {
167
+ const input = {
168
+ sourceIds: null,
169
+ datasourcesList: [{ sourceId: "/docs/guide.md", content: "Guide content" }],
170
+ };
171
+
172
+ const result = transformDetailDatasources(input);
173
+
174
+ expect(result.detailDataSources).toBe("");
175
+ });
176
+
177
+ test("should handle undefined sourceIds", () => {
178
+ const input = {
179
+ sourceIds: undefined,
180
+ datasourcesList: [{ sourceId: "/docs/guide.md", content: "Guide content" }],
181
+ };
182
+
183
+ const result = transformDetailDatasources(input);
184
+
185
+ expect(result.detailDataSources).toBe("");
186
+ });
187
+
188
+ test("should handle null datasourcesList", () => {
189
+ const input = {
190
+ sourceIds: ["/docs/guide.md"],
191
+ datasourcesList: null,
192
+ };
193
+
194
+ const result = transformDetailDatasources(input);
195
+
196
+ expect(result.detailDataSources).toBe("");
197
+ });
198
+
199
+ test("should handle undefined datasourcesList", () => {
200
+ const input = {
201
+ sourceIds: ["/docs/guide.md"],
202
+ datasourcesList: undefined,
203
+ };
204
+
205
+ const result = transformDetailDatasources(input);
206
+
207
+ expect(result.detailDataSources).toBe("");
208
+ });
209
+
210
+ // CONTENT FORMATTING TESTS
211
+ test("should format content with proper sourceId comments", () => {
212
+ const input = {
213
+ sourceIds: ["/project/src/main.js"],
214
+ datasourcesList: [
215
+ {
216
+ sourceId: "/project/src/main.js",
217
+ content: "console.log('Hello World');\nprocess.exit(0);",
218
+ },
219
+ ],
220
+ };
221
+
222
+ const result = transformDetailDatasources(input);
223
+
224
+ expect(result.detailDataSources).toBe(
225
+ "// sourceId: project/src/main.js\nconsole.log('Hello World');\nprocess.exit(0);\n",
226
+ );
227
+ });
228
+
229
+ test("should handle empty content", () => {
230
+ const input = {
231
+ sourceIds: ["/docs/empty.md"],
232
+ datasourcesList: [{ sourceId: "/docs/empty.md", content: "" }],
233
+ };
234
+
235
+ const result = transformDetailDatasources(input);
236
+
237
+ // Empty content is falsy, so it gets filtered out
238
+ expect(result.detailDataSources).toBe("");
239
+ });
240
+
241
+ test("should handle whitespace-only content", () => {
242
+ const input = {
243
+ sourceIds: ["/docs/whitespace.md"],
244
+ datasourcesList: [{ sourceId: "/docs/whitespace.md", content: " \n\t " }],
245
+ };
246
+
247
+ const result = transformDetailDatasources(input);
248
+
249
+ // Whitespace content is truthy, so it should be included
250
+ expect(result.detailDataSources).toBe("// sourceId: docs/whitespace.md\n \n\t \n");
251
+ });
252
+
253
+ test("should handle content with special characters", () => {
254
+ const input = {
255
+ sourceIds: ["/docs/特殊字符.md"],
256
+ datasourcesList: [
257
+ {
258
+ sourceId: "/docs/特殊字符.md",
259
+ content: "# 中文标题\n\n这是一个包含特殊字符的文档: @#$%^&*()",
260
+ },
261
+ ],
262
+ };
263
+
264
+ const result = transformDetailDatasources(input);
265
+
266
+ expect(result.detailDataSources).toContain("特殊字符.md");
267
+ expect(result.detailDataSources).toContain("中文标题");
268
+ expect(result.detailDataSources).toContain("@#$%^&*()");
269
+ });
270
+
271
+ // LARGE DATA TESTS
272
+ test("should handle multiple large datasources", () => {
273
+ const largeContent = "A".repeat(1000);
274
+ const input = {
275
+ sourceIds: Array.from({ length: 50 }, (_, i) => `/docs/file-${i}.md`),
276
+ datasourcesList: Array.from({ length: 50 }, (_, i) => ({
277
+ sourceId: `/docs/file-${i}.md`,
278
+ content: `${largeContent}-${i}`,
279
+ })),
280
+ };
281
+
282
+ const result = transformDetailDatasources(input);
283
+
284
+ expect(result.detailDataSources.split("// sourceId:")).toHaveLength(51); // 50 files + empty first
285
+ expect(result.detailDataSources).toContain("file-0.md");
286
+ expect(result.detailDataSources).toContain("file-49.md");
287
+ expect(result.detailDataSources.length).toBeGreaterThan(50000); // Should be quite large
288
+ });
289
+
290
+ // DUPLICATE HANDLING TESTS
291
+ test("should handle duplicate sourceIds in list", () => {
292
+ const input = {
293
+ sourceIds: ["/docs/guide.md", "/docs/guide.md"],
294
+ datasourcesList: [{ sourceId: "/docs/guide.md", content: "Guide content" }],
295
+ };
296
+
297
+ const result = transformDetailDatasources(input);
298
+
299
+ expect(result.detailDataSources).toBe(
300
+ "// sourceId: docs/guide.md\nGuide content\n" + "// sourceId: docs/guide.md\nGuide content\n",
301
+ );
302
+ });
303
+
304
+ test("should handle duplicate entries in datasourcesList", () => {
305
+ const input = {
306
+ sourceIds: ["/docs/guide.md"],
307
+ datasourcesList: [
308
+ { sourceId: "/docs/guide.md", content: "First content" },
309
+ { sourceId: "/docs/guide.md", content: "Second content" },
310
+ ],
311
+ };
312
+
313
+ const result = transformDetailDatasources(input);
314
+
315
+ // Should use the last entry due to Object.fromEntries behavior
316
+ expect(result.detailDataSources).toBe("// sourceId: docs/guide.md\nSecond content\n");
317
+ });
318
+
319
+ // RETURN VALUE STRUCTURE TESTS
320
+ test("should always return object with detailDataSources property", () => {
321
+ const inputs = [
322
+ { sourceIds: [], datasourcesList: [] },
323
+ { sourceIds: null, datasourcesList: null },
324
+ { sourceIds: ["/test"], datasourcesList: [{ sourceId: "/test", content: "test" }] },
325
+ ];
326
+
327
+ inputs.forEach((input) => {
328
+ const result = transformDetailDatasources(input);
329
+ expect(result).toHaveProperty("detailDataSources");
330
+ expect(typeof result.detailDataSources).toBe("string");
331
+ });
332
+ });
333
+
334
+ // EDGE CASES
335
+ test("should handle sourceId with null or undefined values", () => {
336
+ normalizePathSpy.mockImplementation((path) => path || "");
337
+ toRelativePathSpy.mockImplementation((path) => path || "");
338
+
339
+ const input = {
340
+ sourceIds: [null, undefined, "/docs/valid.md"],
341
+ datasourcesList: [{ sourceId: "/docs/valid.md", content: "Valid content" }],
342
+ };
343
+
344
+ const result = transformDetailDatasources(input);
345
+
346
+ // The toRelativePath spy is returning the original path, let's adjust expectation
347
+ expect(result.detailDataSources).toBe("// sourceId: /docs/valid.md\nValid content\n");
348
+ });
349
+
350
+ test("should handle datasource with null sourceId", () => {
351
+ const input = {
352
+ sourceIds: ["/docs/guide.md"],
353
+ datasourcesList: [
354
+ { sourceId: null, content: "Null sourceId content" },
355
+ { sourceId: "/docs/guide.md", content: "Valid content" },
356
+ ],
357
+ };
358
+
359
+ const result = transformDetailDatasources(input);
360
+
361
+ expect(result.detailDataSources).toBe("// sourceId: docs/guide.md\nValid content\n");
362
+ });
363
+ });