@aigne/doc-smith 0.8.11-beta → 0.8.11-beta.2

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 (257) hide show
  1. package/.aigne/doc-smith/config.yaml +2 -0
  2. package/.aigne/doc-smith/output/structure-plan.json +3 -3
  3. package/.aigne/doc-smith/upload-cache.yaml +252 -0
  4. package/.github/workflows/publish-docs.yml +67 -0
  5. package/.release-please-manifest.json +1 -1
  6. package/CHANGELOG.md +22 -0
  7. package/README.md +45 -115
  8. package/agents/clear/choose-contents.mjs +170 -0
  9. package/agents/clear/clear-auth-tokens.mjs +111 -0
  10. package/agents/clear/clear-document-config.mjs +39 -0
  11. package/agents/clear/clear-document-structure.mjs +106 -0
  12. package/agents/clear/clear-generated-docs.mjs +51 -0
  13. package/agents/clear/index.yaml +23 -0
  14. package/agents/evaluate/code-snippet.mjs +93 -0
  15. package/agents/evaluate/document-structure.yaml +70 -0
  16. package/agents/evaluate/document.yaml +79 -0
  17. package/agents/evaluate/generate-report.mjs +78 -0
  18. package/agents/evaluate/index.yaml +39 -0
  19. package/agents/generate/document-structure-tools/add-document.mjs +56 -0
  20. package/agents/generate/document-structure-tools/delete-document.mjs +49 -0
  21. package/agents/generate/document-structure-tools/move-document.mjs +82 -0
  22. package/agents/generate/document-structure-tools/update-document.mjs +50 -0
  23. package/agents/generate/generate-structure.yaml +1 -1
  24. package/agents/generate/update-document-structure.yaml +42 -0
  25. package/agents/generate/user-review-document-structure.mjs +6 -4
  26. package/agents/init/index.mjs +1 -1
  27. package/agents/publish/publish-docs.mjs +12 -3
  28. package/agents/translate/choose-language.mjs +1 -1
  29. package/agents/update/batch-update-document.yaml +7 -0
  30. package/agents/update/check-update-is-single.mjs +38 -0
  31. package/agents/update/document-tools/update-document-content.mjs +293 -0
  32. package/agents/update/index.yaml +4 -10
  33. package/agents/update/update-document-detail.yaml +52 -0
  34. package/agents/update/update-single-document.yaml +15 -0
  35. package/agents/update/user-review-document.mjs +248 -0
  36. package/agents/utils/choose-docs.mjs +4 -2
  37. package/agents/utils/format-document-structure.mjs +12 -2
  38. package/agents/utils/load-document-all-content.mjs +84 -0
  39. package/agents/utils/load-sources.mjs +4 -1
  40. package/aigne.yaml +59 -20
  41. package/assets/report-template/report.html +198 -0
  42. package/biome.json +14 -2
  43. package/docs/advanced-how-it-works.ja.md +101 -0
  44. package/docs/advanced-how-it-works.zh-TW.md +101 -0
  45. package/docs/advanced-how-it-works.zh.md +20 -20
  46. package/docs/advanced-quality-assurance.ja.md +96 -0
  47. package/docs/advanced-quality-assurance.zh-TW.md +96 -0
  48. package/docs/advanced-quality-assurance.zh.md +18 -18
  49. package/docs/advanced.ja.md +16 -0
  50. package/docs/advanced.zh-TW.md +16 -0
  51. package/docs/advanced.zh.md +4 -4
  52. package/docs/changelog.ja.md +309 -0
  53. package/docs/changelog.zh-TW.md +309 -0
  54. package/docs/changelog.zh.md +23 -23
  55. package/docs/cli-reference.ja.md +210 -0
  56. package/docs/cli-reference.zh-TW.md +210 -0
  57. package/docs/cli-reference.zh.md +21 -21
  58. package/docs/configuration-interactive-setup.ja.md +135 -0
  59. package/docs/configuration-interactive-setup.zh-TW.md +135 -0
  60. package/docs/configuration-interactive-setup.zh.md +29 -29
  61. package/docs/configuration-language-support.ja.md +94 -0
  62. package/docs/configuration-language-support.zh-TW.md +94 -0
  63. package/docs/configuration-language-support.zh.md +13 -13
  64. package/docs/configuration-llm-setup.ja.md +54 -0
  65. package/docs/configuration-llm-setup.zh-TW.md +54 -0
  66. package/docs/configuration-llm-setup.zh.md +12 -12
  67. package/docs/configuration-preferences.ja.md +129 -0
  68. package/docs/configuration-preferences.zh-TW.md +129 -0
  69. package/docs/configuration-preferences.zh.md +36 -36
  70. package/docs/configuration.ja.md +172 -0
  71. package/docs/configuration.zh-TW.md +172 -0
  72. package/docs/configuration.zh.md +49 -49
  73. package/docs/features-generate-documentation.ja.md +101 -0
  74. package/docs/features-generate-documentation.zh-TW.md +101 -0
  75. package/docs/features-generate-documentation.zh.md +17 -17
  76. package/docs/features-publish-your-docs.ja.md +107 -0
  77. package/docs/features-publish-your-docs.zh-TW.md +107 -0
  78. package/docs/features-publish-your-docs.zh.md +22 -22
  79. package/docs/features-translate-documentation.ja.md +79 -0
  80. package/docs/features-translate-documentation.zh-TW.md +79 -0
  81. package/docs/features-translate-documentation.zh.md +12 -12
  82. package/docs/features-update-and-refine.ja.md +138 -0
  83. package/docs/features-update-and-refine.zh-TW.md +138 -0
  84. package/docs/features-update-and-refine.zh.md +21 -21
  85. package/docs/features.ja.md +52 -0
  86. package/docs/features.zh-TW.md +52 -0
  87. package/docs/features.zh.md +8 -8
  88. package/docs/getting-started.ja.md +123 -0
  89. package/docs/getting-started.zh-TW.md +123 -0
  90. package/docs/getting-started.zh.md +24 -24
  91. package/docs/overview.ja.md +30 -0
  92. package/docs/overview.zh-TW.md +30 -0
  93. package/docs/overview.zh.md +8 -8
  94. package/package.json +19 -11
  95. package/prompts/common/document/content-rules-core.md +19 -0
  96. package/prompts/common/document/media-handling-rules.md +9 -0
  97. package/prompts/common/document/role-and-personality.md +15 -0
  98. package/prompts/common/document/user-preferences.md +9 -0
  99. package/prompts/common/document-structure/conflict-resolution-guidance.md +16 -0
  100. package/prompts/common/document-structure/document-structure-rules.md +45 -0
  101. package/prompts/common/document-structure/glossary.md +7 -0
  102. package/prompts/common/document-structure/intj-traits.md +5 -0
  103. package/prompts/common/document-structure/output-constraints.md +9 -0
  104. package/prompts/common/document-structure/user-locale-rules.md +10 -0
  105. package/prompts/common/document-structure/user-preferences.md +9 -0
  106. package/prompts/detail/custom/custom-components.md +9 -1
  107. package/prompts/detail/document-rules.md +6 -6
  108. package/prompts/detail/generate-document.md +5 -45
  109. package/prompts/detail/update-document.md +145 -0
  110. package/prompts/evaluate/document-structure.md +94 -0
  111. package/prompts/evaluate/document.md +149 -0
  112. package/prompts/structure/document-rules.md +1 -1
  113. package/prompts/structure/generate-structure-system.md +74 -0
  114. package/prompts/structure/generate-structure-user.md +41 -0
  115. package/prompts/structure/update-document-structure.md +118 -0
  116. package/prompts/translate/translate-document.md +1 -1
  117. package/prompts/utils/feedback-refiner.md +3 -3
  118. package/release-please-config.json +1 -7
  119. package/tests/agents/clear/choose-contents.test.mjs +280 -0
  120. package/tests/agents/clear/clear-auth-tokens.test.mjs +268 -0
  121. package/tests/agents/clear/clear-document-config.test.mjs +167 -0
  122. package/tests/agents/clear/clear-document-structure.test.mjs +374 -0
  123. package/tests/agents/clear/clear-generated-docs.test.mjs +222 -0
  124. package/tests/agents/evaluate/code-snippet.test.mjs +163 -0
  125. package/tests/agents/evaluate/fixtures/api-services.md +87 -0
  126. package/tests/agents/evaluate/fixtures/js-sdk.md +94 -0
  127. package/tests/agents/evaluate/generate-report.test.mjs +312 -0
  128. package/tests/agents/generate/check-document-structure.test.mjs +0 -6
  129. package/tests/agents/generate/document-structure-tools/add-document.test.mjs +449 -0
  130. package/tests/agents/generate/document-structure-tools/delete-document.test.mjs +410 -0
  131. package/tests/agents/generate/document-structure-tools/move-document.test.mjs +476 -0
  132. package/tests/agents/generate/document-structure-tools/update-document.test.mjs +548 -0
  133. package/tests/agents/generate/generate-structure.test.mjs +0 -6
  134. package/tests/agents/generate/user-review-document-structure.test.mjs +9 -9
  135. package/tests/agents/publish/publish-docs.test.mjs +2 -2
  136. package/tests/agents/update/check-update-is-single.test.mjs +300 -0
  137. package/tests/agents/update/document-tools/update-document-content.test.mjs +326 -0
  138. package/tests/agents/update/user-review-document.test.mjs +561 -0
  139. package/tests/agents/utils/format-document-structure.test.mjs +100 -0
  140. package/tests/utils/auth-utils.test.mjs +239 -1
  141. package/tests/utils/blocklet.test.mjs +9 -7
  142. package/tests/utils/constants.test.mjs +1 -1
  143. package/tests/utils/d2-utils.test.mjs +1 -1
  144. package/tests/utils/deploy.test.mjs +310 -366
  145. package/tests/utils/kroki-utils.test.mjs +2 -15
  146. package/tests/utils/linter/fixtures/css/keyword-error.css +1 -0
  147. package/tests/utils/linter/fixtures/css/missing-semicolon.css +1 -0
  148. package/tests/utils/linter/fixtures/css/syntax-error.css +1 -0
  149. package/tests/utils/linter/fixtures/css/undeclare-variable.css +1 -0
  150. package/tests/utils/linter/fixtures/css/unused-variable.css +2 -0
  151. package/tests/utils/linter/fixtures/css/valid-code.css +1 -0
  152. package/tests/utils/linter/fixtures/dockerfile/keyword-error.dockerfile +1 -0
  153. package/tests/utils/linter/fixtures/dockerfile/missing-semicolon.dockerfile +2 -0
  154. package/tests/utils/linter/fixtures/dockerfile/syntax-error.dockerfile +2 -0
  155. package/tests/utils/linter/fixtures/dockerfile/undeclare-variable.dockerfile +1 -0
  156. package/tests/utils/linter/fixtures/dockerfile/unused-variable.dockerfile +1 -0
  157. package/tests/utils/linter/fixtures/dockerfile/valid-code.dockerfile +2 -0
  158. package/tests/utils/linter/fixtures/go/keyword-error.go +5 -0
  159. package/tests/utils/linter/fixtures/go/missing-semicolon.go +5 -0
  160. package/tests/utils/linter/fixtures/go/syntax-error.go +6 -0
  161. package/tests/utils/linter/fixtures/go/undeclare-variable.go +5 -0
  162. package/tests/utils/linter/fixtures/go/unused-variable.go +5 -0
  163. package/tests/utils/linter/fixtures/go/valid-code.go +7 -0
  164. package/tests/utils/linter/fixtures/js/keyword-error.js +3 -0
  165. package/tests/utils/linter/fixtures/js/missing-semicolon.js +6 -0
  166. package/tests/utils/linter/fixtures/js/syntax-error.js +4 -0
  167. package/tests/utils/linter/fixtures/js/undeclare-variable.js +3 -0
  168. package/tests/utils/linter/fixtures/js/unused-variable.js +7 -0
  169. package/tests/utils/linter/fixtures/js/valid-code.js +15 -0
  170. package/tests/utils/linter/fixtures/json/keyword-error.json +1 -0
  171. package/tests/utils/linter/fixtures/json/missing-semicolon.json +1 -0
  172. package/tests/utils/linter/fixtures/json/syntax-error.json +1 -0
  173. package/tests/utils/linter/fixtures/json/undeclare-variable.json +1 -0
  174. package/tests/utils/linter/fixtures/json/unused-variable.json +1 -0
  175. package/tests/utils/linter/fixtures/json/valid-code.json +1 -0
  176. package/tests/utils/linter/fixtures/jsx/keyword-error.jsx +5 -0
  177. package/tests/utils/linter/fixtures/jsx/missing-semicolon.jsx +5 -0
  178. package/tests/utils/linter/fixtures/jsx/syntax-error.jsx +5 -0
  179. package/tests/utils/linter/fixtures/jsx/undeclare-variable.jsx +5 -0
  180. package/tests/utils/linter/fixtures/jsx/unused-variable.jsx +4 -0
  181. package/tests/utils/linter/fixtures/jsx/valid-code.jsx +5 -0
  182. package/tests/utils/linter/fixtures/python/keyword-error.py +3 -0
  183. package/tests/utils/linter/fixtures/python/missing-semicolon.py +2 -0
  184. package/tests/utils/linter/fixtures/python/syntax-error.py +3 -0
  185. package/tests/utils/linter/fixtures/python/undeclare-variable.py +3 -0
  186. package/tests/utils/linter/fixtures/python/unused-variable.py +6 -0
  187. package/tests/utils/linter/fixtures/python/valid-code.py +12 -0
  188. package/tests/utils/linter/fixtures/ruby/keyword-error.rb +2 -0
  189. package/tests/utils/linter/fixtures/ruby/missing-semicolon.rb +1 -0
  190. package/tests/utils/linter/fixtures/ruby/syntax-error.rb +2 -0
  191. package/tests/utils/linter/fixtures/ruby/undeclare-variable.rb +1 -0
  192. package/tests/utils/linter/fixtures/ruby/unused-variable.rb +2 -0
  193. package/tests/utils/linter/fixtures/ruby/valid-code.rb +1 -0
  194. package/tests/utils/linter/fixtures/sass/keyword-error.sass +2 -0
  195. package/tests/utils/linter/fixtures/sass/missing-semicolon.sass +3 -0
  196. package/tests/utils/linter/fixtures/sass/syntax-error.sass +3 -0
  197. package/tests/utils/linter/fixtures/sass/undeclare-variable.sass +2 -0
  198. package/tests/utils/linter/fixtures/sass/unused-variable.sass +4 -0
  199. package/tests/utils/linter/fixtures/sass/valid-code.sass +2 -0
  200. package/tests/utils/linter/fixtures/scss/keyword-error.scss +1 -0
  201. package/tests/utils/linter/fixtures/scss/missing-semicolon.scss +1 -0
  202. package/tests/utils/linter/fixtures/scss/syntax-error.scss +1 -0
  203. package/tests/utils/linter/fixtures/scss/undeclare-variable.scss +1 -0
  204. package/tests/utils/linter/fixtures/scss/unused-variable.scss +2 -0
  205. package/tests/utils/linter/fixtures/scss/valid-code.scss +1 -0
  206. package/tests/utils/linter/fixtures/shell/keyword-error.sh +5 -0
  207. package/tests/utils/linter/fixtures/shell/missing-semicolon.sh +3 -0
  208. package/tests/utils/linter/fixtures/shell/syntax-error.sh +4 -0
  209. package/tests/utils/linter/fixtures/shell/undeclare-variable.sh +3 -0
  210. package/tests/utils/linter/fixtures/shell/unused-variable.sh +4 -0
  211. package/tests/utils/linter/fixtures/shell/valid-code.sh +3 -0
  212. package/tests/utils/linter/fixtures/ts/keyword-error.ts +1 -0
  213. package/tests/utils/linter/fixtures/ts/missing-semicolon.ts +1 -0
  214. package/tests/utils/linter/fixtures/ts/syntax-error.ts +1 -0
  215. package/tests/utils/linter/fixtures/ts/undeclare-variable.ts +1 -0
  216. package/tests/utils/linter/fixtures/ts/unused-variable.ts +3 -0
  217. package/tests/utils/linter/fixtures/ts/valid-code.ts +3 -0
  218. package/tests/utils/linter/fixtures/tsx/keyword-error.tsx +5 -0
  219. package/tests/utils/linter/fixtures/tsx/missing-semicolon.tsx +5 -0
  220. package/tests/utils/linter/fixtures/tsx/syntax-error.tsx +5 -0
  221. package/tests/utils/linter/fixtures/tsx/undeclare-variable.tsx +6 -0
  222. package/tests/utils/linter/fixtures/tsx/unused-variable.tsx +6 -0
  223. package/tests/utils/linter/fixtures/tsx/valid-code.tsx +5 -0
  224. package/tests/utils/linter/fixtures/vue/keyword-error.vue +6 -0
  225. package/tests/utils/linter/fixtures/vue/missing-semicolon.vue +6 -0
  226. package/tests/utils/linter/fixtures/vue/syntax-error.vue +6 -0
  227. package/tests/utils/linter/fixtures/vue/undeclare-variable.vue +6 -0
  228. package/tests/utils/linter/fixtures/vue/unused-variable.vue +7 -0
  229. package/tests/utils/linter/fixtures/vue/valid-code.vue +6 -0
  230. package/tests/utils/linter/fixtures/yaml/keyword-error.yml +1 -0
  231. package/tests/utils/linter/fixtures/yaml/missing-semicolon.yml +2 -0
  232. package/tests/utils/linter/fixtures/yaml/syntax-error.yml +1 -0
  233. package/tests/utils/linter/fixtures/yaml/undeclare-variable.yml +1 -0
  234. package/tests/utils/linter/fixtures/yaml/unused-variable.yml +2 -0
  235. package/tests/utils/linter/fixtures/yaml/valid-code.yml +3 -0
  236. package/tests/utils/linter/index.test.mjs +440 -0
  237. package/tests/utils/linter/scan-results.mjs +42 -0
  238. package/tests/utils/markdown/index.test.mjs +478 -0
  239. package/tests/utils/mermaid-validator.test.mjs +2 -2
  240. package/tests/utils/utils.test.mjs +3 -1
  241. package/types/document-schema.mjs +54 -0
  242. package/types/document-structure-schema.mjs +244 -0
  243. package/utils/auth-utils.mjs +131 -6
  244. package/utils/conflict-detector.mjs +5 -1
  245. package/utils/{constants.mjs → constants/index.mjs} +109 -0
  246. package/utils/constants/linter.mjs +102 -0
  247. package/utils/d2-utils.mjs +2 -4
  248. package/utils/debug.mjs +3 -0
  249. package/utils/deploy.mjs +81 -385
  250. package/utils/evaluate/report-utils.mjs +131 -0
  251. package/utils/file-utils.mjs +36 -1
  252. package/utils/kroki-utils.mjs +1 -1
  253. package/utils/linter/index.mjs +50 -0
  254. package/utils/markdown/index.mjs +26 -0
  255. package/utils/markdown-checker.mjs +1 -1
  256. package/utils/utils.mjs +19 -7
  257. package/prompts/structure/generate-structure.md +0 -161
@@ -0,0 +1,548 @@
1
+ import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test";
2
+ import updateDocument from "../../../../agents/generate/document-structure-tools/update-document.mjs";
3
+
4
+ describe("update-document", () => {
5
+ let consoleSpy;
6
+ let baseDocumentStructure;
7
+
8
+ beforeEach(() => {
9
+ consoleSpy = spyOn(console, "log").mockImplementation(() => {});
10
+ baseDocumentStructure = [
11
+ {
12
+ title: "Getting Started",
13
+ description: "Introduction to the project",
14
+ path: "/getting-started",
15
+ parentId: null,
16
+ sourceIds: ["intro.md"],
17
+ },
18
+ {
19
+ title: "API Reference",
20
+ description: "Complete API documentation",
21
+ path: "/api",
22
+ parentId: null,
23
+ sourceIds: ["api.js"],
24
+ },
25
+ {
26
+ title: "Authentication",
27
+ description: "How to authenticate with the API",
28
+ path: "/api/auth",
29
+ parentId: "/api",
30
+ sourceIds: ["auth.js", "security.md"],
31
+ },
32
+ {
33
+ title: "Rate Limiting",
34
+ description: "API rate limiting documentation",
35
+ path: "/api/rate-limiting",
36
+ parentId: "/api",
37
+ sourceIds: ["rate-limit.js"],
38
+ },
39
+ ];
40
+ });
41
+
42
+ afterEach(() => {
43
+ consoleSpy?.mockRestore();
44
+ });
45
+
46
+ // SUCCESSFUL UPDATE TESTS - TITLE
47
+ test("should update document title successfully", async () => {
48
+ const result = await updateDocument({
49
+ documentStructure: baseDocumentStructure,
50
+ path: "/getting-started",
51
+ title: "Quick Start Guide",
52
+ });
53
+
54
+ expect(result.documentStructure).toHaveLength(4);
55
+ expect(result.updatedDocument).toEqual({
56
+ title: "Quick Start Guide",
57
+ description: "Introduction to the project",
58
+ path: "/getting-started",
59
+ parentId: null,
60
+ sourceIds: ["intro.md"],
61
+ });
62
+
63
+ expect(result.originalDocument).toEqual({
64
+ title: "Getting Started",
65
+ description: "Introduction to the project",
66
+ path: "/getting-started",
67
+ parentId: null,
68
+ sourceIds: ["intro.md"],
69
+ });
70
+
71
+ // Verify the document was actually updated in the structure
72
+ const updatedDoc = result.documentStructure.find((doc) => doc.path === "/getting-started");
73
+ expect(updatedDoc.title).toBe("Quick Start Guide");
74
+ expect(consoleSpy).not.toHaveBeenCalled();
75
+ });
76
+
77
+ // SUCCESSFUL UPDATE TESTS - DESCRIPTION
78
+ test("should update document description successfully", async () => {
79
+ const result = await updateDocument({
80
+ documentStructure: baseDocumentStructure,
81
+ path: "/api",
82
+ description: "Comprehensive API documentation with examples",
83
+ });
84
+
85
+ expect(result.updatedDocument.description).toBe(
86
+ "Comprehensive API documentation with examples",
87
+ );
88
+ expect(result.originalDocument.description).toBe("Complete API documentation");
89
+
90
+ const updatedDoc = result.documentStructure.find((doc) => doc.path === "/api");
91
+ expect(updatedDoc.description).toBe("Comprehensive API documentation with examples");
92
+ expect(consoleSpy).not.toHaveBeenCalled();
93
+ });
94
+
95
+ // SUCCESSFUL UPDATE TESTS - SOURCE IDS
96
+ test("should update document sourceIds successfully", async () => {
97
+ const newSourceIds = ["new-auth.js", "updated-security.md", "examples.js"];
98
+ const result = await updateDocument({
99
+ documentStructure: baseDocumentStructure,
100
+ path: "/api/auth",
101
+ sourceIds: newSourceIds,
102
+ });
103
+
104
+ expect(result.updatedDocument.sourceIds).toEqual(newSourceIds);
105
+ expect(result.originalDocument.sourceIds).toEqual(["auth.js", "security.md"]);
106
+
107
+ const updatedDoc = result.documentStructure.find((doc) => doc.path === "/api/auth");
108
+ expect(updatedDoc.sourceIds).toEqual(newSourceIds);
109
+ expect(consoleSpy).not.toHaveBeenCalled();
110
+ });
111
+
112
+ test("should create a copy of sourceIds array", async () => {
113
+ const originalSourceIds = ["source1.js", "source2.md"];
114
+ const result = await updateDocument({
115
+ documentStructure: baseDocumentStructure,
116
+ path: "/getting-started",
117
+ sourceIds: originalSourceIds,
118
+ });
119
+
120
+ // Verify it's a copy, not a reference
121
+ expect(result.updatedDocument.sourceIds).toEqual(originalSourceIds);
122
+ expect(result.updatedDocument.sourceIds).not.toBe(originalSourceIds);
123
+
124
+ // Modifying original should not affect the updated document
125
+ originalSourceIds.push("source3.js");
126
+ expect(result.updatedDocument.sourceIds).not.toContain("source3.js");
127
+ });
128
+
129
+ // SUCCESSFUL UPDATE TESTS - MULTIPLE FIELDS
130
+ test("should update multiple fields simultaneously", async () => {
131
+ const result = await updateDocument({
132
+ documentStructure: baseDocumentStructure,
133
+ path: "/api/rate-limiting",
134
+ title: "Advanced Rate Limiting",
135
+ description: "Comprehensive guide to API rate limiting with examples",
136
+ sourceIds: ["rate-limit-v2.js", "examples.md", "best-practices.md"],
137
+ });
138
+
139
+ expect(result.updatedDocument).toEqual({
140
+ title: "Advanced Rate Limiting",
141
+ description: "Comprehensive guide to API rate limiting with examples",
142
+ path: "/api/rate-limiting",
143
+ parentId: "/api",
144
+ sourceIds: ["rate-limit-v2.js", "examples.md", "best-practices.md"],
145
+ });
146
+
147
+ expect(result.originalDocument).toEqual({
148
+ title: "Rate Limiting",
149
+ description: "API rate limiting documentation",
150
+ path: "/api/rate-limiting",
151
+ parentId: "/api",
152
+ sourceIds: ["rate-limit.js"],
153
+ });
154
+
155
+ expect(consoleSpy).not.toHaveBeenCalled();
156
+ });
157
+
158
+ test("should update only specified fields, leaving others unchanged", async () => {
159
+ const result = await updateDocument({
160
+ documentStructure: baseDocumentStructure,
161
+ path: "/api/auth",
162
+ title: "Advanced Authentication",
163
+ });
164
+
165
+ expect(result.updatedDocument).toEqual({
166
+ title: "Advanced Authentication",
167
+ description: "How to authenticate with the API", // Unchanged
168
+ path: "/api/auth", // Unchanged
169
+ parentId: "/api", // Unchanged
170
+ sourceIds: ["auth.js", "security.md"], // Unchanged
171
+ });
172
+ });
173
+
174
+ // VALIDATION ERROR TESTS
175
+ test("should return error when path is missing", async () => {
176
+ const result = await updateDocument({
177
+ documentStructure: baseDocumentStructure,
178
+ title: "New Title",
179
+ });
180
+
181
+ expect(result.documentStructure).toEqual(baseDocumentStructure);
182
+ expect(result.originalDocument).toBeUndefined();
183
+ expect(result.updatedDocument).toBeUndefined();
184
+ expect(consoleSpy).toHaveBeenCalledWith("⚠️ Cannot update document: path: Required");
185
+ });
186
+
187
+ test("should return error when path is empty string", async () => {
188
+ const result = await updateDocument({
189
+ documentStructure: baseDocumentStructure,
190
+ path: "",
191
+ title: "New Title",
192
+ });
193
+
194
+ expect(result.documentStructure).toEqual(baseDocumentStructure);
195
+ expect(consoleSpy).toHaveBeenCalledWith("⚠️ Cannot update document: path: Path is required");
196
+ });
197
+
198
+ test("should return error when no update fields are provided", async () => {
199
+ const result = await updateDocument({
200
+ documentStructure: baseDocumentStructure,
201
+ path: "/getting-started",
202
+ });
203
+
204
+ expect(result.documentStructure).toEqual(baseDocumentStructure);
205
+ expect(consoleSpy).toHaveBeenCalledWith(
206
+ "⚠️ Cannot update document: : At least one field (title, description, or sourceIds) must be provided for update",
207
+ );
208
+ });
209
+
210
+ test("should return error when all update fields are undefined", async () => {
211
+ const result = await updateDocument({
212
+ documentStructure: baseDocumentStructure,
213
+ path: "/getting-started",
214
+ title: undefined,
215
+ description: undefined,
216
+ sourceIds: undefined,
217
+ });
218
+
219
+ expect(result.documentStructure).toEqual(baseDocumentStructure);
220
+ expect(consoleSpy).toHaveBeenCalledWith(
221
+ "⚠️ Cannot update document: : At least one field (title, description, or sourceIds) must be provided for update",
222
+ );
223
+ });
224
+
225
+ test("should return error when document does not exist", async () => {
226
+ const result = await updateDocument({
227
+ documentStructure: baseDocumentStructure,
228
+ path: "/nonexistent-document",
229
+ title: "New Title",
230
+ });
231
+
232
+ expect(result.documentStructure).toEqual(baseDocumentStructure);
233
+ expect(consoleSpy).toHaveBeenCalledWith(
234
+ "⚠️ Cannot update document: Document '/nonexistent-document' does not exist. Choose an existing document to update.",
235
+ );
236
+ });
237
+
238
+ // SOURCE IDS VALIDATION TESTS
239
+ test("should return error when sourceIds is not an array", async () => {
240
+ const result = await updateDocument({
241
+ documentStructure: baseDocumentStructure,
242
+ path: "/getting-started",
243
+ sourceIds: "not-an-array",
244
+ });
245
+
246
+ expect(result.documentStructure).toEqual(baseDocumentStructure);
247
+ expect(consoleSpy).toHaveBeenCalledWith(
248
+ "⚠️ Cannot update document: sourceIds: Expected array, received string",
249
+ );
250
+ });
251
+
252
+ test("should return error when sourceIds is empty array", async () => {
253
+ const result = await updateDocument({
254
+ documentStructure: baseDocumentStructure,
255
+ path: "/getting-started",
256
+ sourceIds: [],
257
+ });
258
+
259
+ expect(result.documentStructure).toEqual(baseDocumentStructure);
260
+ expect(consoleSpy).toHaveBeenCalledWith(
261
+ "⚠️ Cannot update document: sourceIds: Array must contain at least 1 element(s)",
262
+ );
263
+ });
264
+
265
+ test("should return error when sourceIds is null", async () => {
266
+ const result = await updateDocument({
267
+ documentStructure: baseDocumentStructure,
268
+ path: "/getting-started",
269
+ sourceIds: null,
270
+ });
271
+
272
+ expect(result.documentStructure).toEqual(baseDocumentStructure);
273
+ expect(result.originalDocument).toBeUndefined();
274
+ expect(result.updatedDocument).toBeUndefined();
275
+ // null is not an array, so it triggers sourceIds validation error
276
+ expect(consoleSpy).toHaveBeenCalledWith(
277
+ "⚠️ Cannot update document: sourceIds: Expected array, received null",
278
+ );
279
+ });
280
+
281
+ // EDGE CASES
282
+ test("should return error when updating with empty strings (falsy values)", async () => {
283
+ const result = await updateDocument({
284
+ documentStructure: baseDocumentStructure,
285
+ path: "/getting-started",
286
+ title: "",
287
+ description: "",
288
+ });
289
+
290
+ // Empty strings trigger specific field validation errors
291
+ expect(result.documentStructure).toEqual(baseDocumentStructure);
292
+ expect(result.originalDocument).toBeUndefined();
293
+ expect(result.updatedDocument).toBeUndefined();
294
+ expect(consoleSpy).toHaveBeenCalledWith(
295
+ "⚠️ Cannot update document: title: String must contain at least 1 character(s), description: String must contain at least 1 character(s)",
296
+ );
297
+ });
298
+
299
+ test("should handle updating deeply nested document", async () => {
300
+ const nestedStructure = [
301
+ ...baseDocumentStructure,
302
+ {
303
+ title: "OAuth",
304
+ description: "OAuth authentication flow",
305
+ path: "/api/auth/oauth",
306
+ parentId: "/api/auth",
307
+ sourceIds: ["oauth.js"],
308
+ },
309
+ ];
310
+
311
+ const result = await updateDocument({
312
+ documentStructure: nestedStructure,
313
+ path: "/api/auth/oauth",
314
+ title: "OAuth 2.0 Implementation",
315
+ });
316
+
317
+ expect(result.updatedDocument.title).toBe("OAuth 2.0 Implementation");
318
+ expect(result.updatedDocument.path).toBe("/api/auth/oauth");
319
+ expect(consoleSpy).not.toHaveBeenCalled();
320
+ });
321
+
322
+ test("should handle empty document structure", async () => {
323
+ const result = await updateDocument({
324
+ documentStructure: [],
325
+ path: "/any-path",
326
+ title: "New Title",
327
+ });
328
+
329
+ expect(result.documentStructure).toEqual([]);
330
+ expect(consoleSpy).toHaveBeenCalledWith(
331
+ "⚠️ Cannot update document: Document '/any-path' does not exist. Choose an existing document to update.",
332
+ );
333
+ });
334
+
335
+ test("should handle single document structure", async () => {
336
+ const singleDocStructure = [
337
+ {
338
+ title: "Only Document",
339
+ description: "The only document",
340
+ path: "/only",
341
+ parentId: null,
342
+ sourceIds: ["only.md"],
343
+ },
344
+ ];
345
+
346
+ const result = await updateDocument({
347
+ documentStructure: singleDocStructure,
348
+ path: "/only",
349
+ title: "Updated Only Document",
350
+ });
351
+
352
+ expect(result.documentStructure).toHaveLength(1);
353
+ expect(result.updatedDocument.title).toBe("Updated Only Document");
354
+ expect(consoleSpy).not.toHaveBeenCalled();
355
+ });
356
+
357
+ test("should handle special characters in paths", async () => {
358
+ const specialStructure = [
359
+ ...baseDocumentStructure,
360
+ {
361
+ title: "Special Document",
362
+ description: "Document with special characters",
363
+ path: "/api/special-chars_and.numbers123",
364
+ parentId: "/api",
365
+ sourceIds: ["special.md"],
366
+ },
367
+ ];
368
+
369
+ const result = await updateDocument({
370
+ documentStructure: specialStructure,
371
+ path: "/api/special-chars_and.numbers123",
372
+ title: "Updated Special Document",
373
+ });
374
+
375
+ expect(result.updatedDocument.title).toBe("Updated Special Document");
376
+ expect(consoleSpy).not.toHaveBeenCalled();
377
+ });
378
+
379
+ // DATA INTEGRITY TESTS
380
+ test("should not modify original document structure", async () => {
381
+ const originalStructure = [...baseDocumentStructure];
382
+
383
+ await updateDocument({
384
+ documentStructure: baseDocumentStructure,
385
+ path: "/getting-started",
386
+ title: "Updated Title",
387
+ });
388
+
389
+ expect(baseDocumentStructure).toEqual(originalStructure);
390
+ });
391
+
392
+ test("should update only the specified document", async () => {
393
+ const result = await updateDocument({
394
+ documentStructure: baseDocumentStructure,
395
+ path: "/api/auth",
396
+ title: "Updated Authentication",
397
+ });
398
+
399
+ // Count how many documents have changed
400
+ let changedCount = 0;
401
+ for (let i = 0; i < baseDocumentStructure.length; i++) {
402
+ if (
403
+ JSON.stringify(baseDocumentStructure[i]) !== JSON.stringify(result.documentStructure[i])
404
+ ) {
405
+ changedCount++;
406
+ }
407
+ }
408
+
409
+ expect(changedCount).toBe(1);
410
+ });
411
+
412
+ test("should preserve document order in array", async () => {
413
+ const result = await updateDocument({
414
+ documentStructure: baseDocumentStructure,
415
+ path: "/api",
416
+ description: "Updated API documentation",
417
+ });
418
+
419
+ // Verify all documents are in the same positions
420
+ for (let i = 0; i < baseDocumentStructure.length; i++) {
421
+ expect(result.documentStructure[i].path).toBe(baseDocumentStructure[i].path);
422
+ if (baseDocumentStructure[i].path === "/api") {
423
+ // This is the updated document
424
+ expect(result.documentStructure[i].description).toBe("Updated API documentation");
425
+ } else {
426
+ // Other documents should be unchanged
427
+ expect(result.documentStructure[i]).toEqual(baseDocumentStructure[i]);
428
+ }
429
+ }
430
+ });
431
+
432
+ test("should preserve parentId and path when updating other fields", async () => {
433
+ const result = await updateDocument({
434
+ documentStructure: baseDocumentStructure,
435
+ path: "/api/auth",
436
+ title: "New Authentication Title",
437
+ description: "New authentication description",
438
+ sourceIds: ["new-auth.js"],
439
+ });
440
+
441
+ expect(result.updatedDocument.path).toBe("/api/auth");
442
+ expect(result.updatedDocument.parentId).toBe("/api");
443
+ expect(result.originalDocument.path).toBe("/api/auth");
444
+ expect(result.originalDocument.parentId).toBe("/api");
445
+ });
446
+
447
+ // RETURN VALUE TESTS
448
+ test("should return complete original and updated document information", async () => {
449
+ const result = await updateDocument({
450
+ documentStructure: baseDocumentStructure,
451
+ path: "/api/rate-limiting",
452
+ title: "Enhanced Rate Limiting",
453
+ description: "Advanced rate limiting techniques",
454
+ });
455
+
456
+ expect(result.originalDocument).toEqual({
457
+ title: "Rate Limiting",
458
+ description: "API rate limiting documentation",
459
+ path: "/api/rate-limiting",
460
+ parentId: "/api",
461
+ sourceIds: ["rate-limit.js"],
462
+ });
463
+
464
+ expect(result.updatedDocument).toEqual({
465
+ title: "Enhanced Rate Limiting",
466
+ description: "Advanced rate limiting techniques",
467
+ path: "/api/rate-limiting",
468
+ parentId: "/api",
469
+ sourceIds: ["rate-limit.js"],
470
+ });
471
+
472
+ // Verify both have all required properties
473
+ ["title", "description", "path", "parentId", "sourceIds"].forEach((prop) => {
474
+ expect(result.originalDocument).toHaveProperty(prop);
475
+ expect(result.updatedDocument).toHaveProperty(prop);
476
+ });
477
+ });
478
+
479
+ test("should handle updating with multiple source IDs", async () => {
480
+ const multipleSourceIds = [
481
+ "main.js",
482
+ "helper.js",
483
+ "documentation.md",
484
+ "examples/example1.js",
485
+ "examples/example2.js",
486
+ ];
487
+
488
+ const result = await updateDocument({
489
+ documentStructure: baseDocumentStructure,
490
+ path: "/getting-started",
491
+ sourceIds: multipleSourceIds,
492
+ });
493
+
494
+ expect(result.updatedDocument.sourceIds).toEqual(multipleSourceIds);
495
+ expect(result.updatedDocument.sourceIds).toHaveLength(5);
496
+ expect(consoleSpy).not.toHaveBeenCalled();
497
+ });
498
+
499
+ // PARTIAL UPDATE TESTS
500
+ test("should handle partial updates with mixed defined/undefined values", async () => {
501
+ const result = await updateDocument({
502
+ documentStructure: baseDocumentStructure,
503
+ path: "/getting-started",
504
+ title: "New Title",
505
+ description: undefined, // Should not be updated
506
+ sourceIds: ["new-source.md"], // Should be updated
507
+ });
508
+
509
+ expect(result.updatedDocument).toEqual({
510
+ title: "New Title", // Updated
511
+ description: "Introduction to the project", // Unchanged
512
+ path: "/getting-started",
513
+ parentId: null,
514
+ sourceIds: ["new-source.md"], // Updated
515
+ });
516
+ });
517
+
518
+ test("should correctly identify when any update field is provided", async () => {
519
+ // Test with only title
520
+ let result = await updateDocument({
521
+ documentStructure: baseDocumentStructure,
522
+ path: "/getting-started",
523
+ title: "Only Title Updated",
524
+ });
525
+ expect(result.updatedDocument.title).toBe("Only Title Updated");
526
+ expect(consoleSpy).not.toHaveBeenCalled();
527
+
528
+ // Test with only description
529
+ consoleSpy.mockClear();
530
+ result = await updateDocument({
531
+ documentStructure: baseDocumentStructure,
532
+ path: "/api",
533
+ description: "Only Description Updated",
534
+ });
535
+ expect(result.updatedDocument.description).toBe("Only Description Updated");
536
+ expect(consoleSpy).not.toHaveBeenCalled();
537
+
538
+ // Test with only sourceIds
539
+ consoleSpy.mockClear();
540
+ result = await updateDocument({
541
+ documentStructure: baseDocumentStructure,
542
+ path: "/api/auth",
543
+ sourceIds: ["only-sources-updated.js"],
544
+ });
545
+ expect(result.updatedDocument.sourceIds).toEqual(["only-sources-updated.js"]);
546
+ expect(consoleSpy).not.toHaveBeenCalled();
547
+ });
548
+ });
@@ -25,7 +25,6 @@ describe("generateStructure Agent", () => {
25
25
  // Verify agent exists and is correct type
26
26
  expect(agent).toBeDefined();
27
27
  expect(agent).toBeInstanceOf(AIAgent);
28
- expect(agent.name).toBe("generateStructure");
29
28
  });
30
29
 
31
30
  test("should have instructions loaded from file", async () => {
@@ -42,10 +41,5 @@ describe("generateStructure Agent", () => {
42
41
  expect(agent.instructions).toBeDefined();
43
42
  const instructions = await agent.instructions.build({});
44
43
  expect(instructions.messages).toBeDefined();
45
- expect(instructions.messages.length).toBeGreaterThan(0);
46
-
47
- // The instructions should contain content from the prompt file
48
- const systemMessage = instructions.messages.find((m) => m.role === "system");
49
- expect(systemMessage).toBeDefined();
50
44
  });
51
45
  });
@@ -43,7 +43,7 @@ describe("user-review-document-structure", () => {
43
43
  },
44
44
  context: {
45
45
  agents: {
46
- refineDocumentStructure: {},
46
+ updateDocumentStructure: {},
47
47
  checkFeedbackRefiner: {},
48
48
  },
49
49
  invoke: mock(async () => ({
@@ -137,10 +137,10 @@ describe("user-review-document-structure", () => {
137
137
  const result = await userReviewDocumentStructure({ documentStructure }, mockOptions);
138
138
 
139
139
  expect(mockOptions.context.invoke).toHaveBeenCalledWith(
140
- mockOptions.context.agents.refineDocumentStructure,
140
+ mockOptions.context.agents.updateDocumentStructure,
141
141
  expect.objectContaining({
142
142
  feedback: feedback,
143
- originalDocumentStructure: documentStructure,
143
+ documentStructure: documentStructure,
144
144
  userPreferences: "",
145
145
  }),
146
146
  );
@@ -172,15 +172,15 @@ describe("user-review-document-structure", () => {
172
172
  expect(getActiveRulesForScopeSpy).toHaveBeenCalledWith("structure", []);
173
173
  expect(getActiveRulesForScopeSpy).toHaveBeenCalledWith("global", []);
174
174
  expect(mockOptions.context.invoke).toHaveBeenCalledWith(
175
- mockOptions.context.agents.refineDocumentStructure,
175
+ mockOptions.context.agents.updateDocumentStructure,
176
176
  expect.objectContaining({
177
177
  userPreferences: expectedPreferences,
178
178
  }),
179
179
  );
180
180
  });
181
181
 
182
- test("should handle missing refineDocumentStructure agent", async () => {
183
- mockOptions.context.agents = {}; // No refineDocumentStructure agent
182
+ test("should handle missing updateDocumentStructure agent", async () => {
183
+ mockOptions.context.agents = {}; // No updateDocumentStructure agent
184
184
  mockOptions.prompts.select.mockImplementation(async () => "yes");
185
185
  mockOptions.prompts.input.mockImplementation(async () => "Some feedback");
186
186
 
@@ -188,11 +188,11 @@ describe("user-review-document-structure", () => {
188
188
 
189
189
  expect(result.documentStructure).toEqual(documentStructure);
190
190
  expect(consoleSpy).toHaveBeenCalledWith(
191
- "Unable to process your feedback - the structure refinement feature is unavailable.",
191
+ "Unable to process your feedback - the document structure update feature is unavailable.",
192
192
  );
193
193
  });
194
194
 
195
- test("should handle errors from refineDocumentStructure agent", async () => {
195
+ test("should handle errors from updateDocumentStructure agent", async () => {
196
196
  mockOptions.prompts.select.mockImplementation(async () => "yes");
197
197
  mockOptions.prompts.input
198
198
  .mockImplementationOnce(async () => "Some feedback")
@@ -254,7 +254,7 @@ describe("user-review-document-structure", () => {
254
254
 
255
255
  test("should handle missing checkFeedbackRefiner agent gracefully", async () => {
256
256
  const feedback = "Some feedback";
257
- mockOptions.context.agents = { refineDocumentStructure: {} }; // No checkFeedbackRefiner agent
257
+ mockOptions.context.agents = { updateDocumentStructure: {} }; // No checkFeedbackRefiner agent
258
258
 
259
259
  mockOptions.prompts.select.mockImplementation(async () => "yes");
260
260
  mockOptions.prompts.input
@@ -145,7 +145,7 @@ describe("publish-docs", () => {
145
145
  expect(beforePublishHookSpy).toHaveBeenCalled();
146
146
  expect(getAccessTokenSpy).toHaveBeenCalledWith("https://docsmith.aigne.io", "");
147
147
  expect(mockPublishDocs.publishDocs).toHaveBeenCalled();
148
- expect(result.message).toBe("✅ Documentation Published Successfully!");
148
+ expect(result.message).toBe("✅ Documentation published successfully!");
149
149
  });
150
150
 
151
151
  // ENVIRONMENT VARIABLE TESTS
@@ -551,7 +551,7 @@ describe("publish-docs", () => {
551
551
  mockOptions,
552
552
  );
553
553
 
554
- expect(result.message).toBe("✅ Documentation Published Successfully!");
554
+ expect(result.message).toBe("✅ Documentation published successfully!");
555
555
  });
556
556
 
557
557
  test("should handle empty config", async () => {