@aigne/doc-smith 0.8.12-beta.7 → 0.8.12-beta.9

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 (284) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/agents/clear/choose-contents.mjs +14 -1
  3. package/agents/clear/clear-media-description.mjs +129 -0
  4. package/agents/clear/index.yaml +3 -1
  5. package/agents/evaluate/code-snippet.mjs +28 -24
  6. package/agents/evaluate/document-structure.yaml +0 -4
  7. package/agents/evaluate/document.yaml +1 -5
  8. package/agents/generate/index.yaml +1 -0
  9. package/agents/init/index.mjs +10 -0
  10. package/agents/media/batch-generate-media-description.yaml +44 -0
  11. package/agents/media/generate-media-description.yaml +47 -0
  12. package/agents/media/load-media-description.mjs +238 -0
  13. package/agents/publish/index.yaml +4 -0
  14. package/agents/publish/publish-docs.mjs +77 -5
  15. package/agents/publish/translate-meta.mjs +103 -0
  16. package/agents/update/generate-document.yaml +30 -28
  17. package/agents/update/index.yaml +1 -0
  18. package/agents/update/update-document-detail.yaml +3 -1
  19. package/agents/utils/load-sources.mjs +103 -53
  20. package/agents/utils/update-branding.mjs +69 -0
  21. package/aigne.yaml +6 -0
  22. package/assets/report-template/report.html +34 -34
  23. package/package.json +17 -2
  24. package/prompts/common/document/role-and-personality.md +3 -1
  25. package/prompts/detail/d2-diagram/guide.md +7 -1
  26. package/prompts/detail/d2-diagram/user-prompt.md +3 -0
  27. package/prompts/detail/generate/system-prompt.md +6 -7
  28. package/prompts/detail/generate/user-prompt.md +12 -3
  29. package/prompts/detail/update/user-prompt.md +0 -2
  30. package/prompts/evaluate/document-structure.md +6 -7
  31. package/prompts/evaluate/document.md +16 -25
  32. package/prompts/media/media-description/system-prompt.md +35 -0
  33. package/prompts/media/media-description/user-prompt.md +8 -0
  34. package/prompts/structure/update/user-prompt.md +0 -4
  35. package/utils/constants/index.mjs +0 -107
  36. package/utils/file-utils.mjs +86 -0
  37. package/utils/markdown-checker.mjs +0 -20
  38. package/utils/request.mjs +7 -0
  39. package/utils/upload-files.mjs +231 -0
  40. package/utils/utils.mjs +11 -1
  41. package/.aigne/doc-smith/config.yaml +0 -77
  42. package/.aigne/doc-smith/history.yaml +0 -37
  43. package/.aigne/doc-smith/output/structure-plan.json +0 -162
  44. package/.aigne/doc-smith/preferences.yml +0 -97
  45. package/.aigne/doc-smith/upload-cache.yaml +0 -1893
  46. package/.github/PULL_REQUEST_TEMPLATE.md +0 -28
  47. package/.github/workflows/ci.yml +0 -54
  48. package/.github/workflows/create-release-pr.yaml +0 -21
  49. package/.github/workflows/publish-docs.yml +0 -65
  50. package/.github/workflows/release.yml +0 -49
  51. package/.github/workflows/reviewer.yml +0 -54
  52. package/.release-please-manifest.json +0 -3
  53. package/RELEASE.md +0 -9
  54. package/assets/screenshots/doc-complete-setup.png +0 -0
  55. package/assets/screenshots/doc-generate-docs.png +0 -0
  56. package/assets/screenshots/doc-generate.png +0 -0
  57. package/assets/screenshots/doc-generated-successfully.png +0 -0
  58. package/assets/screenshots/doc-publish.png +0 -0
  59. package/assets/screenshots/doc-regenerate.png +0 -0
  60. package/assets/screenshots/doc-translate-langs.png +0 -0
  61. package/assets/screenshots/doc-translate.png +0 -0
  62. package/assets/screenshots/doc-update.png +0 -0
  63. package/biome.json +0 -73
  64. package/codecov.yml +0 -15
  65. package/docs/_sidebar.md +0 -15
  66. package/docs/configuration-initial-setup.ja.md +0 -179
  67. package/docs/configuration-initial-setup.md +0 -179
  68. package/docs/configuration-initial-setup.zh-TW.md +0 -179
  69. package/docs/configuration-initial-setup.zh.md +0 -179
  70. package/docs/configuration-managing-preferences.ja.md +0 -100
  71. package/docs/configuration-managing-preferences.md +0 -100
  72. package/docs/configuration-managing-preferences.zh-TW.md +0 -100
  73. package/docs/configuration-managing-preferences.zh.md +0 -100
  74. package/docs/configuration.ja.md +0 -96
  75. package/docs/configuration.md +0 -96
  76. package/docs/configuration.zh-TW.md +0 -96
  77. package/docs/configuration.zh.md +0 -96
  78. package/docs/getting-started.ja.md +0 -88
  79. package/docs/getting-started.md +0 -88
  80. package/docs/getting-started.zh-TW.md +0 -88
  81. package/docs/getting-started.zh.md +0 -88
  82. package/docs/guides-cleaning-up.ja.md +0 -51
  83. package/docs/guides-cleaning-up.md +0 -51
  84. package/docs/guides-cleaning-up.zh-TW.md +0 -51
  85. package/docs/guides-cleaning-up.zh.md +0 -51
  86. package/docs/guides-evaluating-documents.ja.md +0 -66
  87. package/docs/guides-evaluating-documents.md +0 -66
  88. package/docs/guides-evaluating-documents.zh-TW.md +0 -66
  89. package/docs/guides-evaluating-documents.zh.md +0 -66
  90. package/docs/guides-generating-documentation.ja.md +0 -151
  91. package/docs/guides-generating-documentation.md +0 -151
  92. package/docs/guides-generating-documentation.zh-TW.md +0 -151
  93. package/docs/guides-generating-documentation.zh.md +0 -151
  94. package/docs/guides-interactive-chat.ja.md +0 -85
  95. package/docs/guides-interactive-chat.md +0 -85
  96. package/docs/guides-interactive-chat.zh-TW.md +0 -85
  97. package/docs/guides-interactive-chat.zh.md +0 -85
  98. package/docs/guides-managing-history.ja.md +0 -48
  99. package/docs/guides-managing-history.md +0 -48
  100. package/docs/guides-managing-history.zh-TW.md +0 -48
  101. package/docs/guides-managing-history.zh.md +0 -48
  102. package/docs/guides-publishing-your-docs.ja.md +0 -78
  103. package/docs/guides-publishing-your-docs.md +0 -78
  104. package/docs/guides-publishing-your-docs.zh-TW.md +0 -78
  105. package/docs/guides-publishing-your-docs.zh.md +0 -78
  106. package/docs/guides-translating-documentation.ja.md +0 -95
  107. package/docs/guides-translating-documentation.md +0 -95
  108. package/docs/guides-translating-documentation.zh-TW.md +0 -95
  109. package/docs/guides-translating-documentation.zh.md +0 -95
  110. package/docs/guides-updating-documentation.ja.md +0 -77
  111. package/docs/guides-updating-documentation.md +0 -77
  112. package/docs/guides-updating-documentation.zh-TW.md +0 -77
  113. package/docs/guides-updating-documentation.zh.md +0 -77
  114. package/docs/guides.ja.md +0 -32
  115. package/docs/guides.md +0 -32
  116. package/docs/guides.zh-TW.md +0 -32
  117. package/docs/guides.zh.md +0 -32
  118. package/docs/overview.ja.md +0 -61
  119. package/docs/overview.md +0 -61
  120. package/docs/overview.zh-TW.md +0 -61
  121. package/docs/overview.zh.md +0 -61
  122. package/docs/release-notes.ja.md +0 -255
  123. package/docs/release-notes.md +0 -255
  124. package/docs/release-notes.zh-TW.md +0 -255
  125. package/docs/release-notes.zh.md +0 -255
  126. package/media.md +0 -19
  127. package/prompts/common/afs/afs-tools-usage.md +0 -5
  128. package/prompts/common/afs/use-afs-instruction.md +0 -1
  129. package/release-please-config.json +0 -14
  130. package/tests/agents/chat/chat.test.mjs +0 -46
  131. package/tests/agents/clear/choose-contents.test.mjs +0 -284
  132. package/tests/agents/clear/clear-auth-tokens.test.mjs +0 -268
  133. package/tests/agents/clear/clear-document-config.test.mjs +0 -167
  134. package/tests/agents/clear/clear-document-structure.test.mjs +0 -380
  135. package/tests/agents/clear/clear-generated-docs.test.mjs +0 -222
  136. package/tests/agents/evaluate/code-snippet.test.mjs +0 -163
  137. package/tests/agents/evaluate/fixtures/api-services.md +0 -87
  138. package/tests/agents/evaluate/fixtures/js-sdk.md +0 -94
  139. package/tests/agents/evaluate/generate-report.test.mjs +0 -312
  140. package/tests/agents/generate/check-document-structure.test.mjs +0 -45
  141. package/tests/agents/generate/check-need-generate-structure.test.mjs +0 -279
  142. package/tests/agents/generate/document-structure-tools/add-document.test.mjs +0 -449
  143. package/tests/agents/generate/document-structure-tools/delete-document.test.mjs +0 -410
  144. package/tests/agents/generate/document-structure-tools/generate-sub-structure.test.mjs +0 -277
  145. package/tests/agents/generate/document-structure-tools/move-document.test.mjs +0 -476
  146. package/tests/agents/generate/document-structure-tools/update-document.test.mjs +0 -548
  147. package/tests/agents/generate/generate-structure.test.mjs +0 -45
  148. package/tests/agents/generate/user-review-document-structure.test.mjs +0 -319
  149. package/tests/agents/history/view.test.mjs +0 -97
  150. package/tests/agents/init/init.test.mjs +0 -1657
  151. package/tests/agents/prefs/prefs.test.mjs +0 -431
  152. package/tests/agents/publish/publish-docs.test.mjs +0 -787
  153. package/tests/agents/translate/choose-language.test.mjs +0 -311
  154. package/tests/agents/translate/translate-document.test.mjs +0 -51
  155. package/tests/agents/update/check-document.test.mjs +0 -463
  156. package/tests/agents/update/check-update-is-single.test.mjs +0 -300
  157. package/tests/agents/update/document-tools/update-document-content.test.mjs +0 -329
  158. package/tests/agents/update/generate-document.test.mjs +0 -51
  159. package/tests/agents/update/save-and-translate-document.test.mjs +0 -369
  160. package/tests/agents/update/user-review-document.test.mjs +0 -582
  161. package/tests/agents/utils/action-success.test.mjs +0 -54
  162. package/tests/agents/utils/check-detail-result.test.mjs +0 -743
  163. package/tests/agents/utils/check-feedback-refiner.test.mjs +0 -478
  164. package/tests/agents/utils/choose-docs.test.mjs +0 -406
  165. package/tests/agents/utils/exit.test.mjs +0 -70
  166. package/tests/agents/utils/feedback-refiner.test.mjs +0 -51
  167. package/tests/agents/utils/find-item-by-path.test.mjs +0 -517
  168. package/tests/agents/utils/find-user-preferences-by-path.test.mjs +0 -382
  169. package/tests/agents/utils/format-document-structure.test.mjs +0 -364
  170. package/tests/agents/utils/fs.test.mjs +0 -267
  171. package/tests/agents/utils/load-sources.test.mjs +0 -1470
  172. package/tests/agents/utils/save-docs.test.mjs +0 -109
  173. package/tests/agents/utils/save-output.test.mjs +0 -315
  174. package/tests/agents/utils/save-single-doc.test.mjs +0 -364
  175. package/tests/agents/utils/transform-detail-datasources.test.mjs +0 -320
  176. package/tests/utils/auth-utils.test.mjs +0 -596
  177. package/tests/utils/blocklet.test.mjs +0 -336
  178. package/tests/utils/conflict-detector.test.mjs +0 -355
  179. package/tests/utils/constants.test.mjs +0 -295
  180. package/tests/utils/d2-utils.test.mjs +0 -437
  181. package/tests/utils/deploy.test.mjs +0 -399
  182. package/tests/utils/docs-finder-utils.test.mjs +0 -650
  183. package/tests/utils/file-utils.test.mjs +0 -521
  184. package/tests/utils/history-utils.test.mjs +0 -206
  185. package/tests/utils/kroki-utils.test.mjs +0 -646
  186. package/tests/utils/linter/fixtures/css/keyword-error.css +0 -1
  187. package/tests/utils/linter/fixtures/css/missing-semicolon.css +0 -1
  188. package/tests/utils/linter/fixtures/css/syntax-error.css +0 -1
  189. package/tests/utils/linter/fixtures/css/undeclare-variable.css +0 -1
  190. package/tests/utils/linter/fixtures/css/unused-variable.css +0 -2
  191. package/tests/utils/linter/fixtures/css/valid-code.css +0 -1
  192. package/tests/utils/linter/fixtures/dockerfile/keyword-error.dockerfile +0 -1
  193. package/tests/utils/linter/fixtures/dockerfile/missing-semicolon.dockerfile +0 -2
  194. package/tests/utils/linter/fixtures/dockerfile/syntax-error.dockerfile +0 -2
  195. package/tests/utils/linter/fixtures/dockerfile/undeclare-variable.dockerfile +0 -1
  196. package/tests/utils/linter/fixtures/dockerfile/unused-variable.dockerfile +0 -1
  197. package/tests/utils/linter/fixtures/dockerfile/valid-code.dockerfile +0 -2
  198. package/tests/utils/linter/fixtures/go/keyword-error.go +0 -5
  199. package/tests/utils/linter/fixtures/go/missing-semicolon.go +0 -5
  200. package/tests/utils/linter/fixtures/go/syntax-error.go +0 -6
  201. package/tests/utils/linter/fixtures/go/undeclare-variable.go +0 -5
  202. package/tests/utils/linter/fixtures/go/unused-variable.go +0 -5
  203. package/tests/utils/linter/fixtures/go/valid-code.go +0 -7
  204. package/tests/utils/linter/fixtures/js/keyword-error.js +0 -3
  205. package/tests/utils/linter/fixtures/js/missing-semicolon.js +0 -6
  206. package/tests/utils/linter/fixtures/js/syntax-error.js +0 -4
  207. package/tests/utils/linter/fixtures/js/undeclare-variable.js +0 -3
  208. package/tests/utils/linter/fixtures/js/unused-variable.js +0 -7
  209. package/tests/utils/linter/fixtures/js/valid-code.js +0 -15
  210. package/tests/utils/linter/fixtures/json/keyword-error.json +0 -1
  211. package/tests/utils/linter/fixtures/json/missing-semicolon.json +0 -1
  212. package/tests/utils/linter/fixtures/json/syntax-error.json +0 -1
  213. package/tests/utils/linter/fixtures/json/undeclare-variable.json +0 -1
  214. package/tests/utils/linter/fixtures/json/unused-variable.json +0 -1
  215. package/tests/utils/linter/fixtures/json/valid-code.json +0 -1
  216. package/tests/utils/linter/fixtures/jsx/keyword-error.jsx +0 -5
  217. package/tests/utils/linter/fixtures/jsx/missing-semicolon.jsx +0 -5
  218. package/tests/utils/linter/fixtures/jsx/syntax-error.jsx +0 -5
  219. package/tests/utils/linter/fixtures/jsx/undeclare-variable.jsx +0 -5
  220. package/tests/utils/linter/fixtures/jsx/unused-variable.jsx +0 -4
  221. package/tests/utils/linter/fixtures/jsx/valid-code.jsx +0 -5
  222. package/tests/utils/linter/fixtures/python/keyword-error.py +0 -3
  223. package/tests/utils/linter/fixtures/python/missing-semicolon.py +0 -2
  224. package/tests/utils/linter/fixtures/python/syntax-error.py +0 -3
  225. package/tests/utils/linter/fixtures/python/undeclare-variable.py +0 -3
  226. package/tests/utils/linter/fixtures/python/unused-variable.py +0 -6
  227. package/tests/utils/linter/fixtures/python/valid-code.py +0 -12
  228. package/tests/utils/linter/fixtures/ruby/keyword-error.rb +0 -2
  229. package/tests/utils/linter/fixtures/ruby/missing-semicolon.rb +0 -1
  230. package/tests/utils/linter/fixtures/ruby/syntax-error.rb +0 -2
  231. package/tests/utils/linter/fixtures/ruby/undeclare-variable.rb +0 -1
  232. package/tests/utils/linter/fixtures/ruby/unused-variable.rb +0 -2
  233. package/tests/utils/linter/fixtures/ruby/valid-code.rb +0 -1
  234. package/tests/utils/linter/fixtures/sass/keyword-error.sass +0 -2
  235. package/tests/utils/linter/fixtures/sass/missing-semicolon.sass +0 -3
  236. package/tests/utils/linter/fixtures/sass/syntax-error.sass +0 -3
  237. package/tests/utils/linter/fixtures/sass/undeclare-variable.sass +0 -2
  238. package/tests/utils/linter/fixtures/sass/unused-variable.sass +0 -4
  239. package/tests/utils/linter/fixtures/sass/valid-code.sass +0 -2
  240. package/tests/utils/linter/fixtures/scss/keyword-error.scss +0 -1
  241. package/tests/utils/linter/fixtures/scss/missing-semicolon.scss +0 -1
  242. package/tests/utils/linter/fixtures/scss/syntax-error.scss +0 -1
  243. package/tests/utils/linter/fixtures/scss/undeclare-variable.scss +0 -1
  244. package/tests/utils/linter/fixtures/scss/unused-variable.scss +0 -2
  245. package/tests/utils/linter/fixtures/scss/valid-code.scss +0 -1
  246. package/tests/utils/linter/fixtures/shell/keyword-error.sh +0 -5
  247. package/tests/utils/linter/fixtures/shell/missing-semicolon.sh +0 -3
  248. package/tests/utils/linter/fixtures/shell/syntax-error.sh +0 -4
  249. package/tests/utils/linter/fixtures/shell/undeclare-variable.sh +0 -3
  250. package/tests/utils/linter/fixtures/shell/unused-variable.sh +0 -4
  251. package/tests/utils/linter/fixtures/shell/valid-code.sh +0 -3
  252. package/tests/utils/linter/fixtures/ts/keyword-error.ts +0 -1
  253. package/tests/utils/linter/fixtures/ts/missing-semicolon.ts +0 -1
  254. package/tests/utils/linter/fixtures/ts/syntax-error.ts +0 -1
  255. package/tests/utils/linter/fixtures/ts/undeclare-variable.ts +0 -1
  256. package/tests/utils/linter/fixtures/ts/unused-variable.ts +0 -3
  257. package/tests/utils/linter/fixtures/ts/valid-code.ts +0 -3
  258. package/tests/utils/linter/fixtures/tsx/keyword-error.tsx +0 -5
  259. package/tests/utils/linter/fixtures/tsx/missing-semicolon.tsx +0 -5
  260. package/tests/utils/linter/fixtures/tsx/syntax-error.tsx +0 -5
  261. package/tests/utils/linter/fixtures/tsx/undeclare-variable.tsx +0 -6
  262. package/tests/utils/linter/fixtures/tsx/unused-variable.tsx +0 -6
  263. package/tests/utils/linter/fixtures/tsx/valid-code.tsx +0 -5
  264. package/tests/utils/linter/fixtures/vue/keyword-error.vue +0 -6
  265. package/tests/utils/linter/fixtures/vue/missing-semicolon.vue +0 -6
  266. package/tests/utils/linter/fixtures/vue/syntax-error.vue +0 -6
  267. package/tests/utils/linter/fixtures/vue/undeclare-variable.vue +0 -6
  268. package/tests/utils/linter/fixtures/vue/unused-variable.vue +0 -7
  269. package/tests/utils/linter/fixtures/vue/valid-code.vue +0 -6
  270. package/tests/utils/linter/fixtures/yaml/keyword-error.yml +0 -1
  271. package/tests/utils/linter/fixtures/yaml/missing-semicolon.yml +0 -2
  272. package/tests/utils/linter/fixtures/yaml/syntax-error.yml +0 -1
  273. package/tests/utils/linter/fixtures/yaml/undeclare-variable.yml +0 -1
  274. package/tests/utils/linter/fixtures/yaml/unused-variable.yml +0 -2
  275. package/tests/utils/linter/fixtures/yaml/valid-code.yml +0 -3
  276. package/tests/utils/linter/index.test.mjs +0 -440
  277. package/tests/utils/linter/scan-results.mjs +0 -42
  278. package/tests/utils/load-config.test.mjs +0 -141
  279. package/tests/utils/markdown/index.test.mjs +0 -478
  280. package/tests/utils/mermaid-validator.test.mjs +0 -541
  281. package/tests/utils/mock-chat-model.mjs +0 -12
  282. package/tests/utils/preferences-utils.test.mjs +0 -465
  283. package/tests/utils/save-value-to-config.test.mjs +0 -483
  284. package/tests/utils/utils.test.mjs +0 -941
@@ -1,431 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, mock, spyOn, test } from "bun:test";
2
- import prefs from "../../../agents/prefs/index.mjs";
3
-
4
- // Import internal utils for selective spying
5
- import * as preferencesUtils from "../../../utils/preferences-utils.mjs";
6
-
7
- describe("prefs", () => {
8
- let mockOptions;
9
-
10
- // Spies for internal utils
11
- let readPreferencesSpy;
12
- let removeRuleSpy;
13
- let writePreferencesSpy;
14
-
15
- beforeEach(() => {
16
- mock.restore();
17
-
18
- mockOptions = {
19
- prompts: {
20
- checkbox: mock(async () => []),
21
- },
22
- };
23
-
24
- // Set up spies for internal utils
25
- readPreferencesSpy = spyOn(preferencesUtils, "readPreferences").mockReturnValue({ rules: [] });
26
- removeRuleSpy = spyOn(preferencesUtils, "removeRule").mockReturnValue(true);
27
- writePreferencesSpy = spyOn(preferencesUtils, "writePreferences").mockImplementation(() => {});
28
-
29
- // Clear prompts mock call history
30
- mockOptions.prompts.checkbox.mockClear();
31
- });
32
-
33
- afterEach(() => {
34
- // Restore all spies
35
- readPreferencesSpy?.mockRestore();
36
- removeRuleSpy?.mockRestore();
37
- writePreferencesSpy?.mockRestore();
38
- });
39
-
40
- test("should return help message when no action specified", async () => {
41
- const result = await prefs({}, mockOptions);
42
-
43
- expect(result).toBeDefined();
44
- expect(result.message).toBe("Please choose an action: --list, --remove, or --toggle.");
45
- });
46
-
47
- // LIST PREFERENCES TESTS
48
- describe("list preferences", () => {
49
- test("should return no preferences message when list is empty", async () => {
50
- readPreferencesSpy.mockReturnValue({ rules: [] });
51
-
52
- const result = await prefs({ list: true }, mockOptions);
53
-
54
- expect(result.message).toBe("No saved preferences found.");
55
- });
56
-
57
- test("should format preferences list with active rules", async () => {
58
- const mockRules = [
59
- {
60
- id: "rule1",
61
- scope: "global",
62
- rule: "Use TypeScript for new files",
63
- active: true,
64
- },
65
- {
66
- id: "rule2",
67
- scope: "structure",
68
- rule: "Organize imports alphabetically",
69
- active: false,
70
- },
71
- ];
72
-
73
- readPreferencesSpy.mockReturnValue({ rules: mockRules });
74
-
75
- const result = await prefs({ list: true }, mockOptions);
76
-
77
- expect(result.message).toContain("# User Preferences");
78
- expect(result.message).toContain("🟢 [global] rule1");
79
- expect(result.message).toContain("⚪ [structure] rule2");
80
- expect(result.message).toContain("Use TypeScript for new files");
81
- expect(result.message).toContain("Organize imports alphabetically");
82
- });
83
-
84
- test("should include paths information when available", async () => {
85
- const mockRules = [
86
- {
87
- id: "rule1",
88
- scope: "document",
89
- rule: "Add JSDoc comments",
90
- active: true,
91
- paths: ["src/**/*.js", "lib/**/*.ts"],
92
- },
93
- ];
94
-
95
- readPreferencesSpy.mockReturnValue({ rules: mockRules });
96
-
97
- const result = await prefs({ list: true }, mockOptions);
98
-
99
- expect(result.message).toContain("Paths: src/**/*.js, lib/**/*.ts");
100
- });
101
-
102
- test("should truncate long rules in list", async () => {
103
- const longRule = "A".repeat(150); // Longer than 120 chars
104
- const mockRules = [
105
- {
106
- id: "rule1",
107
- scope: "global",
108
- rule: longRule,
109
- active: true,
110
- },
111
- ];
112
-
113
- readPreferencesSpy.mockReturnValue({ rules: mockRules });
114
-
115
- const result = await prefs({ list: true }, mockOptions);
116
-
117
- expect(result.message).toContain(`${"A".repeat(120)}...`);
118
- expect(result.message).not.toContain(longRule);
119
- });
120
- });
121
-
122
- // REMOVE PREFERENCES TESTS
123
- describe("remove preferences", () => {
124
- test("should remove preferences by provided IDs", async () => {
125
- removeRuleSpy.mockReturnValue(true);
126
-
127
- const result = await prefs({ remove: true, id: ["rule1", "rule2"] }, mockOptions);
128
-
129
- expect(removeRuleSpy).toHaveBeenCalledWith("rule1");
130
- expect(removeRuleSpy).toHaveBeenCalledWith("rule2");
131
- expect(result.message).toBe("Successfully removed 2 preferences.");
132
- });
133
-
134
- test("should handle partial failures when removing", async () => {
135
- removeRuleSpy.mockReturnValueOnce(true).mockReturnValueOnce(false);
136
-
137
- const result = await prefs({ remove: true, id: ["rule1", "rule2"] }, mockOptions);
138
-
139
- expect(result.message).toBe("Successfully removed 1 preferences, 1 failed.");
140
- });
141
-
142
- test("should return message when no preferences exist for removal", async () => {
143
- readPreferencesSpy.mockReturnValue({ rules: [] });
144
-
145
- const result = await prefs({ remove: true }, mockOptions);
146
-
147
- expect(result.message).toBe("No preferences available to remove.");
148
- });
149
-
150
- test("should prompt for interactive selection when no IDs provided", async () => {
151
- const mockRules = [
152
- {
153
- id: "rule1",
154
- scope: "global",
155
- rule: "Use TypeScript for new files",
156
- active: true,
157
- },
158
- ];
159
-
160
- readPreferencesSpy.mockReturnValue({ rules: mockRules });
161
- mockOptions.prompts.checkbox.mockResolvedValue(["rule1"]);
162
-
163
- const result = await prefs({ remove: true }, mockOptions);
164
-
165
- expect(mockOptions.prompts.checkbox).toHaveBeenCalledWith({
166
- message: "Choose preferences to delete:",
167
- choices: expect.arrayContaining([
168
- expect.objectContaining({
169
- name: expect.stringContaining("🟢 [global] Use TypeScript for new files"),
170
- value: "rule1",
171
- }),
172
- ]),
173
- validate: expect.any(Function),
174
- });
175
- expect(result.message).toBe("Successfully removed 1 preferences.");
176
- });
177
-
178
- test("should handle validation error in interactive selection", async () => {
179
- const mockRules = [
180
- {
181
- id: "rule1",
182
- scope: "global",
183
- rule: "Test rule",
184
- active: true,
185
- },
186
- ];
187
-
188
- readPreferencesSpy.mockReturnValue({ rules: mockRules });
189
- mockOptions.prompts.checkbox.mockResolvedValue(["rule1"]);
190
-
191
- await prefs({ remove: true }, mockOptions);
192
-
193
- const validateFn = mockOptions.prompts.checkbox.mock.calls[0][0].validate;
194
-
195
- expect(validateFn([])).toBe("Please choose at least one preference to delete");
196
- expect(validateFn(["rule1"])).toBe(true);
197
- });
198
-
199
- test("should return message when no preferences selected", async () => {
200
- const mockRules = [
201
- {
202
- id: "rule1",
203
- scope: "global",
204
- rule: "Test rule",
205
- active: true,
206
- },
207
- ];
208
-
209
- readPreferencesSpy.mockReturnValue({ rules: mockRules });
210
- mockOptions.prompts.checkbox.mockResolvedValue([]);
211
-
212
- const result = await prefs({ remove: true }, mockOptions);
213
-
214
- expect(result.message).toBe("No preferences selected for deletion.");
215
- });
216
-
217
- test("should truncate long rules in interactive choices", async () => {
218
- const longRule = "A".repeat(80); // Longer than 60 chars
219
- const mockRules = [
220
- {
221
- id: "rule1",
222
- scope: "global",
223
- rule: longRule,
224
- active: true,
225
- },
226
- ];
227
-
228
- readPreferencesSpy.mockReturnValue({ rules: mockRules });
229
- mockOptions.prompts.checkbox.mockResolvedValue(["rule1"]);
230
-
231
- await prefs({ remove: true }, mockOptions);
232
-
233
- const choices = mockOptions.prompts.checkbox.mock.calls[0][0].choices;
234
- expect(choices[0].name).toContain(`${"A".repeat(60)}...`);
235
- });
236
- });
237
-
238
- // TOGGLE PREFERENCES TESTS
239
- describe("toggle preferences", () => {
240
- test("should toggle preferences by provided IDs", async () => {
241
- const mockRules = [
242
- {
243
- id: "rule1",
244
- scope: "global",
245
- rule: "Test rule 1",
246
- active: true,
247
- },
248
- {
249
- id: "rule2",
250
- scope: "structure",
251
- rule: "Test rule 2",
252
- active: false,
253
- },
254
- ];
255
-
256
- readPreferencesSpy.mockReturnValue({ rules: mockRules });
257
-
258
- const result = await prefs({ toggle: true, id: ["rule1", "rule2"] }, mockOptions);
259
-
260
- expect(writePreferencesSpy).toHaveBeenCalled();
261
- expect(result.message).toBe("Successfully toggled 2 preferences.");
262
-
263
- // Check that the active status was toggled
264
- const writtenPrefs = writePreferencesSpy.mock.calls[0][0];
265
- expect(writtenPrefs.rules[0].active).toBe(false); // was true, now false
266
- expect(writtenPrefs.rules[1].active).toBe(true); // was false, now true
267
- });
268
-
269
- test("should handle non-existent rule IDs", async () => {
270
- const mockRules = [
271
- {
272
- id: "rule1",
273
- scope: "global",
274
- rule: "Test rule",
275
- active: true,
276
- },
277
- ];
278
-
279
- readPreferencesSpy.mockReturnValue({ rules: mockRules });
280
-
281
- const result = await prefs({ toggle: true, id: ["rule1", "nonexistent"] }, mockOptions);
282
-
283
- expect(result.message).toBe("Successfully toggled 1 preferences, 1 failed.");
284
- });
285
-
286
- test("should return message when no preferences exist for toggling", async () => {
287
- readPreferencesSpy.mockReturnValue({ rules: [] });
288
-
289
- const result = await prefs({ toggle: true }, mockOptions);
290
-
291
- expect(result.message).toBe("No preferences available to toggle.");
292
- });
293
-
294
- test("should prompt for interactive selection when no IDs provided", async () => {
295
- const mockRules = [
296
- {
297
- id: "rule1",
298
- scope: "global",
299
- rule: "Use TypeScript",
300
- active: false,
301
- },
302
- ];
303
-
304
- readPreferencesSpy.mockReturnValue({ rules: mockRules });
305
- mockOptions.prompts.checkbox.mockResolvedValue(["rule1"]);
306
-
307
- const result = await prefs({ toggle: true }, mockOptions);
308
-
309
- expect(mockOptions.prompts.checkbox).toHaveBeenCalledWith({
310
- message: "Choose preferences to enable/disable:",
311
- choices: expect.arrayContaining([
312
- expect.objectContaining({
313
- name: expect.stringContaining("⚪ [global] Use TypeScript"),
314
- value: "rule1",
315
- }),
316
- ]),
317
- validate: expect.any(Function),
318
- });
319
- expect(result.message).toBe("Successfully toggled 1 preferences.");
320
- });
321
-
322
- test("should handle validation error in toggle interactive selection", async () => {
323
- const mockRules = [
324
- {
325
- id: "rule1",
326
- scope: "global",
327
- rule: "Test rule",
328
- active: true,
329
- },
330
- ];
331
-
332
- readPreferencesSpy.mockReturnValue({ rules: mockRules });
333
- mockOptions.prompts.checkbox.mockResolvedValue(["rule1"]);
334
-
335
- await prefs({ toggle: true }, mockOptions);
336
-
337
- const validateFn = mockOptions.prompts.checkbox.mock.calls[0][0].validate;
338
-
339
- expect(validateFn([])).toBe("Please choose at least one preference to toggle");
340
- expect(validateFn(["rule1"])).toBe(true);
341
- });
342
-
343
- test("should return message when no preferences selected for toggling", async () => {
344
- const mockRules = [
345
- {
346
- id: "rule1",
347
- scope: "global",
348
- rule: "Test rule",
349
- active: true,
350
- },
351
- ];
352
-
353
- readPreferencesSpy.mockReturnValue({ rules: mockRules });
354
- mockOptions.prompts.checkbox.mockResolvedValue([]);
355
-
356
- const result = await prefs({ toggle: true }, mockOptions);
357
-
358
- expect(result.message).toBe("No preferences selected to toggle.");
359
- });
360
- });
361
-
362
- // EDGE CASES
363
- test("should handle empty ID array", async () => {
364
- await prefs({ remove: true, id: [] }, mockOptions);
365
-
366
- expect(readPreferencesSpy).toHaveBeenCalled();
367
- // Should fall back to interactive selection
368
- });
369
-
370
- test("should handle null ID array", async () => {
371
- await prefs({ toggle: true, id: null }, mockOptions);
372
-
373
- expect(readPreferencesSpy).toHaveBeenCalled();
374
- // Should fall back to interactive selection
375
- });
376
-
377
- test("should handle mixed success/failure in operations", async () => {
378
- removeRuleSpy.mockReturnValueOnce(true).mockReturnValueOnce(false).mockReturnValueOnce(true);
379
-
380
- const result = await prefs({ remove: true, id: ["rule1", "rule2", "rule3"] }, mockOptions);
381
-
382
- expect(result.message).toBe("Successfully removed 2 preferences, 1 failed.");
383
- });
384
-
385
- test("should handle all operations with same rule format", async () => {
386
- const mockRules = [
387
- {
388
- id: "rule1",
389
- scope: "translation",
390
- rule: "Translate technical terms consistently",
391
- active: true,
392
- paths: ["docs/**/*.md"],
393
- },
394
- ];
395
-
396
- readPreferencesSpy.mockReturnValue({ rules: mockRules });
397
-
398
- // Test list
399
- const listResult = await prefs({ list: true }, mockOptions);
400
- expect(listResult.message).toContain("🟢 [translation] rule1");
401
- expect(listResult.message).toContain("Paths: docs/**/*.md");
402
-
403
- // Test remove
404
- mockOptions.prompts.checkbox.mockResolvedValue(["rule1"]);
405
- const removeResult = await prefs({ remove: true }, mockOptions);
406
- expect(removeResult.message).toContain("Successfully removed 1 preferences.");
407
-
408
- // Test toggle
409
- mockOptions.prompts.checkbox.mockResolvedValue(["rule1"]);
410
- const toggleResult = await prefs({ toggle: true }, mockOptions);
411
- expect(toggleResult.message).toContain("Successfully toggled 1 preferences.");
412
- });
413
-
414
- test("should handle checkbox returning null", async () => {
415
- const mockRules = [
416
- {
417
- id: "rule1",
418
- scope: "global",
419
- rule: "Test rule",
420
- active: true,
421
- },
422
- ];
423
-
424
- readPreferencesSpy.mockReturnValue({ rules: mockRules });
425
- mockOptions.prompts.checkbox.mockResolvedValue(null);
426
-
427
- const result = await prefs({ remove: true }, mockOptions);
428
-
429
- expect(result.message).toBe("No preferences selected for deletion.");
430
- });
431
- });