@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,167 @@
1
+ import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test";
2
+ import * as fsPromises from "node:fs/promises";
3
+ import { mkdir, rm, writeFile } from "node:fs/promises";
4
+ import { dirname, join } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import clearDocumentConfig from "../../../agents/clear/clear-document-config.mjs";
7
+
8
+ const __dirname = dirname(fileURLToPath(import.meta.url));
9
+
10
+ describe("clear-document-config", () => {
11
+ let testDir;
12
+ let configPath;
13
+
14
+ beforeEach(async () => {
15
+ // Create a temporary test directory
16
+ testDir = join(__dirname, "test-clear-config");
17
+ await mkdir(testDir, { recursive: true });
18
+
19
+ // Create .aigne/doc-smith directory structure
20
+ const aigneDir = join(testDir, ".aigne", "doc-smith");
21
+ await mkdir(aigneDir, { recursive: true });
22
+
23
+ configPath = join(aigneDir, "config.yaml");
24
+ });
25
+
26
+ afterEach(async () => {
27
+ // Clean up test directory
28
+ try {
29
+ await rm(testDir, { recursive: true, force: true });
30
+ } catch {
31
+ // Ignore cleanup errors since they don't affect test results
32
+ }
33
+ });
34
+
35
+ test("should clear existing document configuration successfully", async () => {
36
+ // Create a test config file
37
+ const configContent = `
38
+ projectName: "Test Project"
39
+ projectDesc: "Test Description"
40
+ locale: "en"
41
+ documentPurpose: ["API", "Tutorial"]
42
+ `;
43
+ await writeFile(configPath, configContent);
44
+
45
+ const result = await clearDocumentConfig({ workDir: testDir });
46
+
47
+ expect(result.cleared).toBe(true);
48
+ expect(result.message).toContain("Cleared document configuration");
49
+ expect(result.path).toBeDefined();
50
+ expect(result.suggestions).toEqual([
51
+ "Run `aigne doc init` to generate a fresh configuration file.",
52
+ ]);
53
+
54
+ // Verify file is actually deleted
55
+ const { pathExists } = await import("../../../utils/file-utils.mjs");
56
+ const exists = await pathExists(configPath);
57
+ expect(exists).toBe(false);
58
+ });
59
+
60
+ test("should handle non-existent configuration file", async () => {
61
+ // Don't create the config file
62
+ const result = await clearDocumentConfig({ workDir: testDir });
63
+
64
+ expect(result.cleared).toBe(false);
65
+ expect(result.message).toContain("Document configuration already empty");
66
+ expect(result.path).toBeDefined();
67
+ expect(result.suggestions).toEqual([]);
68
+ });
69
+
70
+ test("should use current working directory when workDir not provided", async () => {
71
+ const originalCwd = process.cwd();
72
+
73
+ try {
74
+ // Change to test directory
75
+ process.chdir(testDir);
76
+
77
+ // Create config in current directory's .aigne structure
78
+ await writeFile(configPath, "test: content");
79
+
80
+ const result = await clearDocumentConfig({});
81
+
82
+ expect(result.cleared).toBe(true);
83
+ expect(result.message).toContain("Cleared document configuration");
84
+ } finally {
85
+ // Restore original working directory
86
+ process.chdir(originalCwd);
87
+ }
88
+ });
89
+
90
+ test("should provide correct return structure", async () => {
91
+ await writeFile(configPath, "test: content");
92
+
93
+ const result = await clearDocumentConfig({ workDir: testDir });
94
+
95
+ expect(result).toHaveProperty("message");
96
+ expect(result).toHaveProperty("cleared");
97
+ expect(result).toHaveProperty("path");
98
+ expect(result).toHaveProperty("suggestions");
99
+ expect(typeof result.message).toBe("string");
100
+ expect(typeof result.cleared).toBe("boolean");
101
+ expect(typeof result.path).toBe("string");
102
+ expect(Array.isArray(result.suggestions)).toBe(true);
103
+ });
104
+
105
+ test("should have correct task metadata", () => {
106
+ expect(clearDocumentConfig.taskTitle).toBe("Clear document configuration");
107
+ expect(clearDocumentConfig.description).toBe("Clear the document configuration file");
108
+ });
109
+
110
+ test("should handle nested directory structures", async () => {
111
+ // Create nested test directory
112
+ const nestedDir = join(testDir, "nested", "project");
113
+ await mkdir(nestedDir, { recursive: true });
114
+
115
+ const nestedAigneDir = join(nestedDir, ".aigne", "doc-smith");
116
+ await mkdir(nestedAigneDir, { recursive: true });
117
+
118
+ const nestedConfigPath = join(nestedAigneDir, "config.yaml");
119
+ await writeFile(nestedConfigPath, "nested: config");
120
+
121
+ const result = await clearDocumentConfig({ workDir: nestedDir });
122
+
123
+ expect(result.cleared).toBe(true);
124
+ expect(result.message).toContain("Cleared document configuration");
125
+
126
+ // Verify nested file is deleted
127
+ const { pathExists } = await import("../../../utils/file-utils.mjs");
128
+ const exists = await pathExists(nestedConfigPath);
129
+ expect(exists).toBe(false);
130
+ });
131
+
132
+ test("should handle path with special characters", async () => {
133
+ // Create directory with spaces and special characters
134
+ const specialDir = join(testDir, "special dir-with_chars");
135
+ await mkdir(specialDir, { recursive: true });
136
+
137
+ const specialAigneDir = join(specialDir, ".aigne", "doc-smith");
138
+ await mkdir(specialAigneDir, { recursive: true });
139
+
140
+ const specialConfigPath = join(specialAigneDir, "config.yaml");
141
+ await writeFile(specialConfigPath, "special: config");
142
+
143
+ const result = await clearDocumentConfig({ workDir: specialDir });
144
+
145
+ expect(result.cleared).toBe(true);
146
+ expect(result.message).toContain("Cleared document configuration");
147
+ });
148
+
149
+ test("should handle file system errors gracefully", async () => {
150
+ // Create a spy on rm to simulate an error
151
+ const rmSpy = spyOn(fsPromises, "rm");
152
+ rmSpy.mockImplementation(() => {
153
+ throw new Error("Permission denied");
154
+ });
155
+
156
+ try {
157
+ const result = await clearDocumentConfig({ workDir: testDir });
158
+
159
+ expect(result.error).toBe(true);
160
+ expect(result.message).toContain("Failed to clear document configuration");
161
+ expect(result.message).toContain("Permission denied");
162
+ expect(result.path).toBeDefined();
163
+ } finally {
164
+ rmSpy.mockRestore();
165
+ }
166
+ });
167
+ });
@@ -0,0 +1,374 @@
1
+ import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test";
2
+ import * as fsPromises from "node:fs/promises";
3
+ import { mkdir, rm, writeFile } from "node:fs/promises";
4
+ import { dirname, join } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import clearDocumentStructure from "../../../agents/clear/clear-document-structure.mjs";
7
+
8
+ const __dirname = dirname(fileURLToPath(import.meta.url));
9
+
10
+ describe("clear-document-structure", () => {
11
+ let testDir;
12
+ let structurePlanPath;
13
+ let docsDir;
14
+
15
+ beforeEach(async () => {
16
+ // Create a temporary test directory
17
+ testDir = join(__dirname, "test-clear-structure");
18
+ await mkdir(testDir, { recursive: true });
19
+
20
+ // Create .aigne/doc-smith/output directory structure
21
+ const outputDir = join(testDir, ".aigne", "doc-smith", "output");
22
+ await mkdir(outputDir, { recursive: true });
23
+
24
+ structurePlanPath = join(outputDir, "structure-plan.json");
25
+ docsDir = join(testDir, "docs");
26
+ });
27
+
28
+ afterEach(async () => {
29
+ // Clean up test directory
30
+ try {
31
+ await rm(testDir, { recursive: true, force: true });
32
+ } catch {
33
+ // Ignore cleanup errors since they don't affect test results
34
+ }
35
+ });
36
+
37
+ test("should clear structure plan only when no docsDir provided", async () => {
38
+ // Create a test structure plan file
39
+ const structurePlan = {
40
+ documents: [
41
+ { path: "/intro", title: "Introduction" },
42
+ { path: "/guide", title: "User Guide" },
43
+ ],
44
+ };
45
+ await writeFile(structurePlanPath, JSON.stringify(structurePlan, null, 2));
46
+
47
+ const result = await clearDocumentStructure({ workDir: testDir });
48
+
49
+ expect(result.message).toContain("Document structure cleared successfully!");
50
+ expect(result.hasError).toBe(false);
51
+ expect(result.clearedCount).toBe(1);
52
+
53
+ // Verify structure plan file is actually deleted
54
+ const { pathExists } = await import("../../../utils/file-utils.mjs");
55
+ const exists = await pathExists(structurePlanPath);
56
+ expect(exists).toBe(false);
57
+ });
58
+
59
+ test("should clear both structure plan and docs directory when docsDir provided", async () => {
60
+ // Create structure plan
61
+ await writeFile(structurePlanPath, JSON.stringify({ test: "data" }));
62
+
63
+ // Create docs directory with files
64
+ await mkdir(docsDir, { recursive: true });
65
+ await writeFile(join(docsDir, "index.md"), "# Index");
66
+ await writeFile(join(docsDir, "guide.md"), "# Guide");
67
+
68
+ const result = await clearDocumentStructure({ workDir: testDir, docsDir });
69
+
70
+ expect(result.message).toContain("Document structure cleared successfully!");
71
+ expect(result.clearedCount).toBe(2);
72
+
73
+ // Verify both are deleted
74
+ const { pathExists } = await import("../../../utils/file-utils.mjs");
75
+ const structureExists = await pathExists(structurePlanPath);
76
+ const docsExists = await pathExists(docsDir);
77
+ expect(structureExists).toBe(false);
78
+ expect(docsExists).toBe(false);
79
+ });
80
+
81
+ test("should handle non-existent structure plan file", async () => {
82
+ // Don't create the structure plan file
83
+ const result = await clearDocumentStructure({ workDir: testDir });
84
+
85
+ expect(result.message).toContain("Document structure already empty.");
86
+ expect(result.clearedCount).toBe(0);
87
+ expect(result.hasError).toBe(false);
88
+ });
89
+
90
+ test("should handle non-existent docs directory", async () => {
91
+ // Create structure plan but not docs directory
92
+ await writeFile(structurePlanPath, JSON.stringify({ test: "data" }));
93
+
94
+ const nonExistentDocsDir = join(testDir, "non-existent-docs");
95
+
96
+ const result = await clearDocumentStructure({
97
+ workDir: testDir,
98
+ docsDir: nonExistentDocsDir,
99
+ });
100
+
101
+ expect(result.message).toContain("Document structure cleared successfully!");
102
+ expect(result.clearedCount).toBe(1); // Only structure plan cleared
103
+
104
+ // Verify structure plan is deleted
105
+ const { pathExists } = await import("../../../utils/file-utils.mjs");
106
+ const exists = await pathExists(structurePlanPath);
107
+ expect(exists).toBe(false);
108
+ });
109
+
110
+ test("should use current working directory when workDir not provided", async () => {
111
+ const originalCwd = process.cwd();
112
+
113
+ try {
114
+ // Change to test directory
115
+ process.chdir(testDir);
116
+
117
+ // Create structure plan in current directory's structure
118
+ await writeFile(structurePlanPath, JSON.stringify({ test: "data" }));
119
+
120
+ const result = await clearDocumentStructure({});
121
+
122
+ expect(result.message).toContain("Document structure cleared successfully!");
123
+ expect(result.clearedCount).toBe(1);
124
+ } finally {
125
+ // Restore original working directory
126
+ process.chdir(originalCwd);
127
+ }
128
+ });
129
+
130
+ test("should provide correct return structure", async () => {
131
+ await writeFile(structurePlanPath, JSON.stringify({ test: "data" }));
132
+
133
+ const result = await clearDocumentStructure({ workDir: testDir });
134
+
135
+ expect(result).toHaveProperty("message");
136
+ expect(result).toHaveProperty("results");
137
+ expect(result).toHaveProperty("hasError");
138
+ expect(result).toHaveProperty("clearedCount");
139
+ expect(typeof result.message).toBe("string");
140
+ expect(Array.isArray(result.results)).toBe(true);
141
+ expect(typeof result.hasError).toBe("boolean");
142
+ expect(typeof result.clearedCount).toBe("number");
143
+ });
144
+
145
+ test("should have correct input schema", () => {
146
+ expect(clearDocumentStructure.input_schema).toBeDefined();
147
+ expect(clearDocumentStructure.input_schema.type).toBe("object");
148
+ expect(clearDocumentStructure.input_schema.properties.docsDir).toBeDefined();
149
+ expect(clearDocumentStructure.input_schema.properties.docsDir.type).toBe("string");
150
+ expect(clearDocumentStructure.input_schema.properties.docsDir.description).toBe(
151
+ "The documents directory to clear (optional)",
152
+ );
153
+ expect(clearDocumentStructure.input_schema.properties.workDir).toBeDefined();
154
+ expect(clearDocumentStructure.input_schema.properties.workDir.type).toBe("string");
155
+ expect(clearDocumentStructure.input_schema.properties.workDir.description).toBe(
156
+ "The working directory (defaults to current directory)",
157
+ );
158
+ });
159
+
160
+ test("should have correct task metadata", () => {
161
+ expect(clearDocumentStructure.taskTitle).toBe(
162
+ "Clear document structure and all generated documents",
163
+ );
164
+ expect(clearDocumentStructure.description).toBe(
165
+ "Clear the document structure plan (structure-plan.json) and optionally the documents directory",
166
+ );
167
+ });
168
+
169
+ test("should handle complex document structures", async () => {
170
+ // Create complex structure plan
171
+ const complexStructure = {
172
+ documents: [
173
+ {
174
+ path: "/introduction",
175
+ title: "Introduction",
176
+ children: [
177
+ { path: "/introduction/overview", title: "Overview" },
178
+ { path: "/introduction/getting-started", title: "Getting Started" },
179
+ ],
180
+ },
181
+ {
182
+ path: "/api",
183
+ title: "API Reference",
184
+ children: [
185
+ { path: "/api/authentication", title: "Authentication" },
186
+ { path: "/api/endpoints", title: "Endpoints" },
187
+ ],
188
+ },
189
+ ],
190
+ metadata: {
191
+ version: "1.0.0",
192
+ generated: new Date().toISOString(),
193
+ },
194
+ };
195
+
196
+ await writeFile(structurePlanPath, JSON.stringify(complexStructure, null, 2));
197
+
198
+ // Create corresponding docs structure
199
+ await mkdir(docsDir, { recursive: true });
200
+ const nestedDirs = [
201
+ join(docsDir, "introduction"),
202
+ join(docsDir, "api"),
203
+ join(docsDir, "tutorials"),
204
+ ];
205
+
206
+ for (const dir of nestedDirs) {
207
+ await mkdir(dir, { recursive: true });
208
+ await writeFile(join(dir, "index.md"), `# ${dir} content`);
209
+ }
210
+
211
+ const result = await clearDocumentStructure({ workDir: testDir, docsDir });
212
+
213
+ expect(result.clearedCount).toBe(2);
214
+ expect(result.hasError).toBe(false);
215
+
216
+ // Verify everything is cleaned up
217
+ const { pathExists } = await import("../../../utils/file-utils.mjs");
218
+ const structureExists = await pathExists(structurePlanPath);
219
+ const docsExists = await pathExists(docsDir);
220
+ expect(structureExists).toBe(false);
221
+ expect(docsExists).toBe(false);
222
+ });
223
+
224
+ test("should handle paths with special characters", async () => {
225
+ // Create directory with special characters
226
+ const specialTestDir = join(__dirname, "test-clear-structure with spaces & symbols");
227
+ await mkdir(specialTestDir, { recursive: true });
228
+
229
+ const specialOutputDir = join(specialTestDir, ".aigne", "doc-smith", "output");
230
+ await mkdir(specialOutputDir, { recursive: true });
231
+
232
+ const specialStructurePath = join(specialOutputDir, "structure-plan.json");
233
+ await writeFile(specialStructurePath, JSON.stringify({ special: "test" }));
234
+
235
+ try {
236
+ const result = await clearDocumentStructure({ workDir: specialTestDir });
237
+
238
+ expect(result.clearedCount).toBe(1);
239
+ expect(result.hasError).toBe(false);
240
+
241
+ // Verify special path works
242
+ const { pathExists } = await import("../../../utils/file-utils.mjs");
243
+ const exists = await pathExists(specialStructurePath);
244
+ expect(exists).toBe(false);
245
+ } finally {
246
+ // Clean up special directory
247
+ try {
248
+ await rm(specialTestDir, { recursive: true, force: true });
249
+ } catch {
250
+ // Ignore cleanup errors
251
+ }
252
+ }
253
+ });
254
+
255
+ test("should handle relative and absolute paths for docsDir", async () => {
256
+ await writeFile(structurePlanPath, JSON.stringify({ test: "data" }));
257
+
258
+ // Create docs with relative path reference
259
+ const relativeDocs = join(testDir, "relative-docs");
260
+ await mkdir(relativeDocs, { recursive: true });
261
+ await writeFile(join(relativeDocs, "test.md"), "test content");
262
+
263
+ const originalCwd = process.cwd();
264
+
265
+ try {
266
+ process.chdir(testDir);
267
+ const result = await clearDocumentStructure({
268
+ workDir: testDir,
269
+ docsDir: "./relative-docs",
270
+ });
271
+
272
+ expect(result.clearedCount).toBe(2);
273
+ expect(result.hasError).toBe(false);
274
+ } finally {
275
+ process.chdir(originalCwd);
276
+ }
277
+ });
278
+
279
+ test("should provide detailed results information", async () => {
280
+ await writeFile(structurePlanPath, JSON.stringify({ test: "data" }));
281
+ await mkdir(docsDir, { recursive: true });
282
+ await writeFile(join(docsDir, "test.md"), "test content");
283
+
284
+ const result = await clearDocumentStructure({ workDir: testDir, docsDir });
285
+
286
+ expect(result.results).toHaveLength(2);
287
+
288
+ // Check structure result
289
+ const structureResult = result.results.find((r) => r.type === "structure");
290
+ expect(structureResult).toBeDefined();
291
+ expect(structureResult.cleared).toBe(true);
292
+ expect(structureResult.message).toContain("structure plan");
293
+
294
+ // Check documents result
295
+ const docsResult = result.results.find((r) => r.type === "documents");
296
+ expect(docsResult).toBeDefined();
297
+ expect(docsResult.cleared).toBe(true);
298
+ expect(docsResult.message).toContain("documents directory");
299
+ });
300
+
301
+ test("should handle structure plan file removal errors", async () => {
302
+ // Create a spy on rm to simulate an error for structure plan
303
+ const rmSpy = spyOn(fsPromises, "rm");
304
+ rmSpy.mockImplementation((path, _options) => {
305
+ if (path.includes("structure-plan.json")) {
306
+ throw new Error("Permission denied for structure plan");
307
+ }
308
+ return Promise.resolve();
309
+ });
310
+
311
+ try {
312
+ const result = await clearDocumentStructure({ workDir: testDir });
313
+
314
+ expect(result.hasError).toBe(true);
315
+ expect(result.message).toContain("Document structure cleanup finished with some issues.");
316
+
317
+ const structureResult = result.results.find((r) => r.type === "structure");
318
+ expect(structureResult.error).toBe(true);
319
+ expect(structureResult.message).toContain("Failed to clear document structure plan");
320
+ expect(structureResult.message).toContain("Permission denied for structure plan");
321
+ } finally {
322
+ rmSpy.mockRestore();
323
+ }
324
+ });
325
+
326
+ test("should handle documents directory removal errors", async () => {
327
+ // Create structure plan file
328
+ await writeFile(structurePlanPath, JSON.stringify({ test: "data" }));
329
+
330
+ // Create a spy on rm to simulate an error for docs directory
331
+ const rmSpy = spyOn(fsPromises, "rm");
332
+ rmSpy.mockImplementation((path, _options) => {
333
+ if (path === docsDir) {
334
+ throw new Error("Permission denied for docs directory");
335
+ }
336
+ return Promise.resolve();
337
+ });
338
+
339
+ try {
340
+ const result = await clearDocumentStructure({ workDir: testDir, docsDir });
341
+
342
+ expect(result.hasError).toBe(true);
343
+ expect(result.message).toContain("Document structure cleanup finished with some issues.");
344
+
345
+ const docsResult = result.results.find((r) => r.type === "documents");
346
+ expect(docsResult.error).toBe(true);
347
+ expect(docsResult.message).toContain("Failed to clear documents directory");
348
+ expect(docsResult.message).toContain("Permission denied for docs directory");
349
+ } finally {
350
+ rmSpy.mockRestore();
351
+ }
352
+ });
353
+
354
+ test("should handle both structure and docs errors simultaneously", async () => {
355
+ // Create a spy on rm to simulate errors for both operations
356
+ const rmSpy = spyOn(fsPromises, "rm");
357
+ rmSpy.mockImplementation(() => {
358
+ throw new Error("File system error");
359
+ });
360
+
361
+ try {
362
+ const result = await clearDocumentStructure({ workDir: testDir, docsDir });
363
+
364
+ expect(result.hasError).toBe(true);
365
+ expect(result.message).toContain("Document structure cleanup finished with some issues.");
366
+ expect(result.results).toHaveLength(2);
367
+
368
+ // Both operations should have errors
369
+ expect(result.results.every((r) => r.error)).toBe(true);
370
+ } finally {
371
+ rmSpy.mockRestore();
372
+ }
373
+ });
374
+ });