@aigne/doc-smith 0.8.12-beta.8 → 0.8.12

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 (264) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/agents/publish/index.yaml +4 -0
  3. package/agents/publish/publish-docs.mjs +77 -5
  4. package/agents/publish/translate-meta.mjs +103 -0
  5. package/agents/update/generate-document.yaml +30 -28
  6. package/agents/update/update-document-detail.yaml +3 -1
  7. package/agents/utils/update-branding.mjs +69 -0
  8. package/package.json +16 -2
  9. package/prompts/common/document/role-and-personality.md +3 -1
  10. package/prompts/detail/d2-diagram/guide.md +7 -1
  11. package/prompts/detail/d2-diagram/user-prompt.md +3 -0
  12. package/prompts/detail/generate/system-prompt.md +6 -7
  13. package/prompts/detail/generate/user-prompt.md +12 -3
  14. package/prompts/detail/update/user-prompt.md +0 -2
  15. package/prompts/structure/update/user-prompt.md +0 -4
  16. package/utils/file-utils.mjs +69 -24
  17. package/utils/markdown-checker.mjs +0 -20
  18. package/utils/request.mjs +7 -0
  19. package/utils/upload-files.mjs +231 -0
  20. package/utils/utils.mjs +11 -1
  21. package/.aigne/doc-smith/config.yaml +0 -77
  22. package/.aigne/doc-smith/history.yaml +0 -37
  23. package/.aigne/doc-smith/media-description.yaml +0 -91
  24. package/.aigne/doc-smith/output/structure-plan.json +0 -162
  25. package/.aigne/doc-smith/preferences.yml +0 -97
  26. package/.aigne/doc-smith/upload-cache.yaml +0 -1830
  27. package/.github/PULL_REQUEST_TEMPLATE.md +0 -28
  28. package/.github/workflows/ci.yml +0 -54
  29. package/.github/workflows/create-release-pr.yaml +0 -21
  30. package/.github/workflows/publish-docs.yml +0 -65
  31. package/.github/workflows/release.yml +0 -49
  32. package/.github/workflows/reviewer.yml +0 -54
  33. package/.release-please-manifest.json +0 -3
  34. package/RELEASE.md +0 -9
  35. package/assets/screenshots/doc-complete-setup.png +0 -0
  36. package/assets/screenshots/doc-generate-docs.png +0 -0
  37. package/assets/screenshots/doc-generate.png +0 -0
  38. package/assets/screenshots/doc-generated-successfully.png +0 -0
  39. package/assets/screenshots/doc-publish.png +0 -0
  40. package/assets/screenshots/doc-regenerate.png +0 -0
  41. package/assets/screenshots/doc-translate-langs.png +0 -0
  42. package/assets/screenshots/doc-translate.png +0 -0
  43. package/assets/screenshots/doc-update.png +0 -0
  44. package/biome.json +0 -73
  45. package/codecov.yml +0 -15
  46. package/docs/_sidebar.md +0 -15
  47. package/docs/configuration-initial-setup.ja.md +0 -179
  48. package/docs/configuration-initial-setup.md +0 -198
  49. package/docs/configuration-initial-setup.zh-TW.md +0 -179
  50. package/docs/configuration-initial-setup.zh.md +0 -179
  51. package/docs/configuration-managing-preferences.ja.md +0 -100
  52. package/docs/configuration-managing-preferences.md +0 -100
  53. package/docs/configuration-managing-preferences.zh-TW.md +0 -100
  54. package/docs/configuration-managing-preferences.zh.md +0 -100
  55. package/docs/configuration.ja.md +0 -69
  56. package/docs/configuration.md +0 -69
  57. package/docs/configuration.zh-TW.md +0 -69
  58. package/docs/configuration.zh.md +0 -69
  59. package/docs/getting-started.ja.md +0 -107
  60. package/docs/getting-started.md +0 -107
  61. package/docs/getting-started.zh-TW.md +0 -107
  62. package/docs/getting-started.zh.md +0 -107
  63. package/docs/guides-cleaning-up.ja.md +0 -51
  64. package/docs/guides-cleaning-up.md +0 -52
  65. package/docs/guides-cleaning-up.zh-TW.md +0 -51
  66. package/docs/guides-cleaning-up.zh.md +0 -51
  67. package/docs/guides-evaluating-documents.ja.md +0 -66
  68. package/docs/guides-evaluating-documents.md +0 -107
  69. package/docs/guides-evaluating-documents.zh-TW.md +0 -66
  70. package/docs/guides-evaluating-documents.zh.md +0 -66
  71. package/docs/guides-generating-documentation.ja.md +0 -151
  72. package/docs/guides-generating-documentation.md +0 -89
  73. package/docs/guides-generating-documentation.zh-TW.md +0 -151
  74. package/docs/guides-generating-documentation.zh.md +0 -151
  75. package/docs/guides-interactive-chat.ja.md +0 -85
  76. package/docs/guides-interactive-chat.md +0 -93
  77. package/docs/guides-interactive-chat.zh-TW.md +0 -85
  78. package/docs/guides-interactive-chat.zh.md +0 -85
  79. package/docs/guides-managing-history.ja.md +0 -48
  80. package/docs/guides-managing-history.md +0 -53
  81. package/docs/guides-managing-history.zh-TW.md +0 -48
  82. package/docs/guides-managing-history.zh.md +0 -48
  83. package/docs/guides-publishing-your-docs.ja.md +0 -78
  84. package/docs/guides-publishing-your-docs.md +0 -83
  85. package/docs/guides-publishing-your-docs.zh-TW.md +0 -78
  86. package/docs/guides-publishing-your-docs.zh.md +0 -78
  87. package/docs/guides-translating-documentation.ja.md +0 -95
  88. package/docs/guides-translating-documentation.md +0 -100
  89. package/docs/guides-translating-documentation.zh-TW.md +0 -95
  90. package/docs/guides-translating-documentation.zh.md +0 -95
  91. package/docs/guides-updating-documentation.ja.md +0 -77
  92. package/docs/guides-updating-documentation.md +0 -79
  93. package/docs/guides-updating-documentation.zh-TW.md +0 -77
  94. package/docs/guides-updating-documentation.zh.md +0 -77
  95. package/docs/guides.ja.md +0 -32
  96. package/docs/guides.md +0 -32
  97. package/docs/guides.zh-TW.md +0 -32
  98. package/docs/guides.zh.md +0 -32
  99. package/docs/overview.ja.md +0 -61
  100. package/docs/overview.md +0 -61
  101. package/docs/overview.zh-TW.md +0 -61
  102. package/docs/overview.zh.md +0 -61
  103. package/docs/release-notes.ja.md +0 -255
  104. package/docs/release-notes.md +0 -288
  105. package/docs/release-notes.zh-TW.md +0 -255
  106. package/docs/release-notes.zh.md +0 -255
  107. package/prompts/common/afs/afs-tools-usage.md +0 -5
  108. package/prompts/common/afs/use-afs-instruction.md +0 -1
  109. package/release-please-config.json +0 -14
  110. package/tests/agents/chat/chat.test.mjs +0 -46
  111. package/tests/agents/clear/choose-contents.test.mjs +0 -284
  112. package/tests/agents/clear/clear-auth-tokens.test.mjs +0 -268
  113. package/tests/agents/clear/clear-document-config.test.mjs +0 -167
  114. package/tests/agents/clear/clear-document-structure.test.mjs +0 -380
  115. package/tests/agents/clear/clear-generated-docs.test.mjs +0 -222
  116. package/tests/agents/evaluate/code-snippet.test.mjs +0 -163
  117. package/tests/agents/evaluate/fixtures/api-services.md +0 -87
  118. package/tests/agents/evaluate/fixtures/js-sdk.md +0 -94
  119. package/tests/agents/evaluate/generate-report.test.mjs +0 -312
  120. package/tests/agents/generate/check-document-structure.test.mjs +0 -45
  121. package/tests/agents/generate/check-need-generate-structure.test.mjs +0 -279
  122. package/tests/agents/generate/document-structure-tools/add-document.test.mjs +0 -449
  123. package/tests/agents/generate/document-structure-tools/delete-document.test.mjs +0 -410
  124. package/tests/agents/generate/document-structure-tools/generate-sub-structure.test.mjs +0 -277
  125. package/tests/agents/generate/document-structure-tools/move-document.test.mjs +0 -476
  126. package/tests/agents/generate/document-structure-tools/update-document.test.mjs +0 -548
  127. package/tests/agents/generate/generate-structure.test.mjs +0 -45
  128. package/tests/agents/generate/user-review-document-structure.test.mjs +0 -319
  129. package/tests/agents/history/view.test.mjs +0 -97
  130. package/tests/agents/init/init.test.mjs +0 -1657
  131. package/tests/agents/prefs/prefs.test.mjs +0 -431
  132. package/tests/agents/publish/publish-docs.test.mjs +0 -787
  133. package/tests/agents/translate/choose-language.test.mjs +0 -311
  134. package/tests/agents/translate/translate-document.test.mjs +0 -51
  135. package/tests/agents/update/check-document.test.mjs +0 -463
  136. package/tests/agents/update/check-update-is-single.test.mjs +0 -300
  137. package/tests/agents/update/document-tools/update-document-content.test.mjs +0 -329
  138. package/tests/agents/update/generate-document.test.mjs +0 -51
  139. package/tests/agents/update/save-and-translate-document.test.mjs +0 -369
  140. package/tests/agents/update/user-review-document.test.mjs +0 -582
  141. package/tests/agents/utils/action-success.test.mjs +0 -54
  142. package/tests/agents/utils/check-detail-result.test.mjs +0 -743
  143. package/tests/agents/utils/check-feedback-refiner.test.mjs +0 -478
  144. package/tests/agents/utils/choose-docs.test.mjs +0 -406
  145. package/tests/agents/utils/exit.test.mjs +0 -70
  146. package/tests/agents/utils/feedback-refiner.test.mjs +0 -51
  147. package/tests/agents/utils/find-item-by-path.test.mjs +0 -517
  148. package/tests/agents/utils/find-user-preferences-by-path.test.mjs +0 -382
  149. package/tests/agents/utils/format-document-structure.test.mjs +0 -364
  150. package/tests/agents/utils/fs.test.mjs +0 -267
  151. package/tests/agents/utils/load-sources.test.mjs +0 -1470
  152. package/tests/agents/utils/save-docs.test.mjs +0 -109
  153. package/tests/agents/utils/save-output.test.mjs +0 -315
  154. package/tests/agents/utils/save-single-doc.test.mjs +0 -364
  155. package/tests/agents/utils/transform-detail-datasources.test.mjs +0 -320
  156. package/tests/utils/auth-utils.test.mjs +0 -596
  157. package/tests/utils/blocklet.test.mjs +0 -336
  158. package/tests/utils/conflict-detector.test.mjs +0 -355
  159. package/tests/utils/constants.test.mjs +0 -295
  160. package/tests/utils/d2-utils.test.mjs +0 -437
  161. package/tests/utils/deploy.test.mjs +0 -399
  162. package/tests/utils/docs-finder-utils.test.mjs +0 -650
  163. package/tests/utils/file-utils.test.mjs +0 -521
  164. package/tests/utils/history-utils.test.mjs +0 -206
  165. package/tests/utils/kroki-utils.test.mjs +0 -646
  166. package/tests/utils/linter/fixtures/css/keyword-error.css +0 -1
  167. package/tests/utils/linter/fixtures/css/missing-semicolon.css +0 -1
  168. package/tests/utils/linter/fixtures/css/syntax-error.css +0 -1
  169. package/tests/utils/linter/fixtures/css/undeclare-variable.css +0 -1
  170. package/tests/utils/linter/fixtures/css/unused-variable.css +0 -2
  171. package/tests/utils/linter/fixtures/css/valid-code.css +0 -1
  172. package/tests/utils/linter/fixtures/dockerfile/keyword-error.dockerfile +0 -1
  173. package/tests/utils/linter/fixtures/dockerfile/missing-semicolon.dockerfile +0 -2
  174. package/tests/utils/linter/fixtures/dockerfile/syntax-error.dockerfile +0 -2
  175. package/tests/utils/linter/fixtures/dockerfile/undeclare-variable.dockerfile +0 -1
  176. package/tests/utils/linter/fixtures/dockerfile/unused-variable.dockerfile +0 -1
  177. package/tests/utils/linter/fixtures/dockerfile/valid-code.dockerfile +0 -2
  178. package/tests/utils/linter/fixtures/go/keyword-error.go +0 -5
  179. package/tests/utils/linter/fixtures/go/missing-semicolon.go +0 -5
  180. package/tests/utils/linter/fixtures/go/syntax-error.go +0 -6
  181. package/tests/utils/linter/fixtures/go/undeclare-variable.go +0 -5
  182. package/tests/utils/linter/fixtures/go/unused-variable.go +0 -5
  183. package/tests/utils/linter/fixtures/go/valid-code.go +0 -7
  184. package/tests/utils/linter/fixtures/js/keyword-error.js +0 -3
  185. package/tests/utils/linter/fixtures/js/missing-semicolon.js +0 -6
  186. package/tests/utils/linter/fixtures/js/syntax-error.js +0 -4
  187. package/tests/utils/linter/fixtures/js/undeclare-variable.js +0 -3
  188. package/tests/utils/linter/fixtures/js/unused-variable.js +0 -7
  189. package/tests/utils/linter/fixtures/js/valid-code.js +0 -15
  190. package/tests/utils/linter/fixtures/json/keyword-error.json +0 -1
  191. package/tests/utils/linter/fixtures/json/missing-semicolon.json +0 -1
  192. package/tests/utils/linter/fixtures/json/syntax-error.json +0 -1
  193. package/tests/utils/linter/fixtures/json/undeclare-variable.json +0 -1
  194. package/tests/utils/linter/fixtures/json/unused-variable.json +0 -1
  195. package/tests/utils/linter/fixtures/json/valid-code.json +0 -1
  196. package/tests/utils/linter/fixtures/jsx/keyword-error.jsx +0 -5
  197. package/tests/utils/linter/fixtures/jsx/missing-semicolon.jsx +0 -5
  198. package/tests/utils/linter/fixtures/jsx/syntax-error.jsx +0 -5
  199. package/tests/utils/linter/fixtures/jsx/undeclare-variable.jsx +0 -5
  200. package/tests/utils/linter/fixtures/jsx/unused-variable.jsx +0 -4
  201. package/tests/utils/linter/fixtures/jsx/valid-code.jsx +0 -5
  202. package/tests/utils/linter/fixtures/python/keyword-error.py +0 -3
  203. package/tests/utils/linter/fixtures/python/missing-semicolon.py +0 -2
  204. package/tests/utils/linter/fixtures/python/syntax-error.py +0 -3
  205. package/tests/utils/linter/fixtures/python/undeclare-variable.py +0 -3
  206. package/tests/utils/linter/fixtures/python/unused-variable.py +0 -6
  207. package/tests/utils/linter/fixtures/python/valid-code.py +0 -12
  208. package/tests/utils/linter/fixtures/ruby/keyword-error.rb +0 -2
  209. package/tests/utils/linter/fixtures/ruby/missing-semicolon.rb +0 -1
  210. package/tests/utils/linter/fixtures/ruby/syntax-error.rb +0 -2
  211. package/tests/utils/linter/fixtures/ruby/undeclare-variable.rb +0 -1
  212. package/tests/utils/linter/fixtures/ruby/unused-variable.rb +0 -2
  213. package/tests/utils/linter/fixtures/ruby/valid-code.rb +0 -1
  214. package/tests/utils/linter/fixtures/sass/keyword-error.sass +0 -2
  215. package/tests/utils/linter/fixtures/sass/missing-semicolon.sass +0 -3
  216. package/tests/utils/linter/fixtures/sass/syntax-error.sass +0 -3
  217. package/tests/utils/linter/fixtures/sass/undeclare-variable.sass +0 -2
  218. package/tests/utils/linter/fixtures/sass/unused-variable.sass +0 -4
  219. package/tests/utils/linter/fixtures/sass/valid-code.sass +0 -2
  220. package/tests/utils/linter/fixtures/scss/keyword-error.scss +0 -1
  221. package/tests/utils/linter/fixtures/scss/missing-semicolon.scss +0 -1
  222. package/tests/utils/linter/fixtures/scss/syntax-error.scss +0 -1
  223. package/tests/utils/linter/fixtures/scss/undeclare-variable.scss +0 -1
  224. package/tests/utils/linter/fixtures/scss/unused-variable.scss +0 -2
  225. package/tests/utils/linter/fixtures/scss/valid-code.scss +0 -1
  226. package/tests/utils/linter/fixtures/shell/keyword-error.sh +0 -5
  227. package/tests/utils/linter/fixtures/shell/missing-semicolon.sh +0 -3
  228. package/tests/utils/linter/fixtures/shell/syntax-error.sh +0 -4
  229. package/tests/utils/linter/fixtures/shell/undeclare-variable.sh +0 -3
  230. package/tests/utils/linter/fixtures/shell/unused-variable.sh +0 -4
  231. package/tests/utils/linter/fixtures/shell/valid-code.sh +0 -3
  232. package/tests/utils/linter/fixtures/ts/keyword-error.ts +0 -1
  233. package/tests/utils/linter/fixtures/ts/missing-semicolon.ts +0 -1
  234. package/tests/utils/linter/fixtures/ts/syntax-error.ts +0 -1
  235. package/tests/utils/linter/fixtures/ts/undeclare-variable.ts +0 -1
  236. package/tests/utils/linter/fixtures/ts/unused-variable.ts +0 -3
  237. package/tests/utils/linter/fixtures/ts/valid-code.ts +0 -3
  238. package/tests/utils/linter/fixtures/tsx/keyword-error.tsx +0 -5
  239. package/tests/utils/linter/fixtures/tsx/missing-semicolon.tsx +0 -5
  240. package/tests/utils/linter/fixtures/tsx/syntax-error.tsx +0 -5
  241. package/tests/utils/linter/fixtures/tsx/undeclare-variable.tsx +0 -6
  242. package/tests/utils/linter/fixtures/tsx/unused-variable.tsx +0 -6
  243. package/tests/utils/linter/fixtures/tsx/valid-code.tsx +0 -5
  244. package/tests/utils/linter/fixtures/vue/keyword-error.vue +0 -6
  245. package/tests/utils/linter/fixtures/vue/missing-semicolon.vue +0 -6
  246. package/tests/utils/linter/fixtures/vue/syntax-error.vue +0 -6
  247. package/tests/utils/linter/fixtures/vue/undeclare-variable.vue +0 -6
  248. package/tests/utils/linter/fixtures/vue/unused-variable.vue +0 -7
  249. package/tests/utils/linter/fixtures/vue/valid-code.vue +0 -6
  250. package/tests/utils/linter/fixtures/yaml/keyword-error.yml +0 -1
  251. package/tests/utils/linter/fixtures/yaml/missing-semicolon.yml +0 -2
  252. package/tests/utils/linter/fixtures/yaml/syntax-error.yml +0 -1
  253. package/tests/utils/linter/fixtures/yaml/undeclare-variable.yml +0 -1
  254. package/tests/utils/linter/fixtures/yaml/unused-variable.yml +0 -2
  255. package/tests/utils/linter/fixtures/yaml/valid-code.yml +0 -3
  256. package/tests/utils/linter/index.test.mjs +0 -440
  257. package/tests/utils/linter/scan-results.mjs +0 -42
  258. package/tests/utils/load-config.test.mjs +0 -141
  259. package/tests/utils/markdown/index.test.mjs +0 -478
  260. package/tests/utils/mermaid-validator.test.mjs +0 -541
  261. package/tests/utils/mock-chat-model.mjs +0 -12
  262. package/tests/utils/preferences-utils.test.mjs +0 -465
  263. package/tests/utils/save-value-to-config.test.mjs +0 -483
  264. 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
- });