sumulige-claude 1.5.1 → 1.6.0

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 (223) hide show
  1. package/.claude/hooks/hook-registry.json +0 -15
  2. package/.claude/rules/coding-style.md +18 -7
  3. package/.claude/rules/hooks.md +15 -4
  4. package/.claude/rules/performance.md +15 -5
  5. package/.claude/rules/security.md +140 -4
  6. package/.claude/rules/testing.md +138 -9
  7. package/.claude/rules/web-design-standard.md +16 -5
  8. package/.claude/skills/algorithmic-art/metadata.yaml +28 -0
  9. package/.claude/skills/api-tester/SKILL.md +61 -0
  10. package/.claude/skills/api-tester/examples/basic.md +3 -0
  11. package/.claude/skills/api-tester/metadata.yaml +30 -0
  12. package/.claude/skills/api-tester/templates/default.md +3 -0
  13. package/.claude/skills/brand-guidelines/metadata.yaml +26 -0
  14. package/.claude/skills/canvas-design/metadata.yaml +27 -0
  15. package/.claude/skills/code-reviewer-123/SKILL.md +61 -0
  16. package/.claude/skills/code-reviewer-123/examples/basic.md +3 -0
  17. package/.claude/skills/code-reviewer-123/metadata.yaml +30 -0
  18. package/.claude/skills/code-reviewer-123/templates/default.md +3 -0
  19. package/.claude/skills/doc-coauthoring/metadata.yaml +27 -0
  20. package/.claude/skills/docx/metadata.yaml +30 -0
  21. package/.claude/skills/frontend-design/metadata.yaml +28 -0
  22. package/.claude/skills/internal-comms/metadata.yaml +28 -0
  23. package/.claude/skills/mcp-builder/metadata.yaml +26 -0
  24. package/.claude/skills/my-skill/SKILL.md +61 -0
  25. package/.claude/skills/my-skill/examples/basic.md +3 -0
  26. package/.claude/skills/my-skill/metadata.yaml +30 -0
  27. package/.claude/skills/my-skill/templates/default.md +3 -0
  28. package/.claude/skills/pdf/metadata.yaml +29 -0
  29. package/.claude/skills/pptx/metadata.yaml +29 -0
  30. package/.claude/skills/react-best-practices/metadata.yaml +26 -0
  31. package/.claude/skills/react-node-practices/SKILL.md +409 -0
  32. package/.claude/skills/react-node-practices/metadata.yaml +56 -0
  33. package/.claude/skills/skill-creator/metadata.yaml +25 -0
  34. package/.claude/skills/slack-gif-creator/metadata.yaml +28 -0
  35. package/.claude/skills/test-skill-name/SKILL.md +61 -0
  36. package/.claude/skills/test-skill-name/examples/basic.md +3 -0
  37. package/.claude/skills/test-skill-name/metadata.yaml +30 -0
  38. package/.claude/skills/test-skill-name/templates/default.md +3 -0
  39. package/.claude/skills/test-workflow/metadata.yaml +32 -0
  40. package/.claude/skills/theme-factory/metadata.yaml +26 -0
  41. package/.claude/skills/threejs-fundamentals/metadata.yaml +27 -0
  42. package/.claude/skills/web-artifacts-builder/metadata.yaml +30 -0
  43. package/.claude/skills/web-design-guidelines/metadata.yaml +26 -0
  44. package/.claude/skills/webapp-testing/metadata.yaml +26 -0
  45. package/.claude/skills/xlsx/metadata.yaml +29 -0
  46. package/LICENSE +21 -0
  47. package/README.md +280 -529
  48. package/cli.js +19 -3
  49. package/package.json +29 -3
  50. package/template/.codex/README.md +69 -0
  51. package/template/.codex/config.toml +56 -0
  52. package/template/AGENTS.md +94 -0
  53. package/.claude/.kickoff-hint.txt +0 -52
  54. package/.claude/.sumulige-claude-version +0 -1
  55. package/.claude/.version +0 -1
  56. package/.claude/AGENTS.md +0 -42
  57. package/.claude/ANCHORS.md +0 -40
  58. package/.claude/CLAUDE.md +0 -138
  59. package/.claude/MEMORY.md +0 -69
  60. package/.claude/PROJECT_LOG.md +0 -101
  61. package/.claude/THINKING_CHAIN_GUIDE.md +0 -287
  62. package/.claude/USAGE.md +0 -175
  63. package/.claude/boris-optimizations.md +0 -167
  64. package/.claude/handoffs/INDEX.md +0 -21
  65. package/.claude/handoffs/LATEST.md +0 -76
  66. package/.claude/handoffs/handoff_2026-01-22T13-07-04-757Z.md +0 -76
  67. package/.claude/quality-gate.json +0 -82
  68. package/.claude/rag/skill-index.json +0 -135
  69. package/.claude/settings.json +0 -99
  70. package/.claude/settings.local.json +0 -175
  71. package/.claude/templates/PROJECT_KICKOFF.md +0 -89
  72. package/.claude/templates/PROJECT_PROPOSAL.md +0 -227
  73. package/.claude/templates/TASK_PLAN.md +0 -121
  74. package/.claude/templates/hooks/README.md +0 -302
  75. package/.claude/templates/hooks/hook.sh.template +0 -94
  76. package/.claude/templates/hooks/user-prompt-submit.cjs.template +0 -116
  77. package/.claude/templates/hooks/user-response-submit.cjs.template +0 -94
  78. package/.claude/templates/hooks/validate.js +0 -173
  79. package/.claude/templates/tasks/develop.md +0 -69
  80. package/.claude/templates/tasks/research.md +0 -64
  81. package/.claude/templates/tasks/test.md +0 -96
  82. package/.claude/thinking-routes/.last-sync +0 -1
  83. package/.claude/thinking-routes/QUICKREF.md +0 -98
  84. package/.claude/workflow/document-scanner.js +0 -426
  85. package/.claude/workflow/knowledge-engine.js +0 -941
  86. package/.claude/workflow/notebooklm/browser.js +0 -1028
  87. package/.claude/workflow/phases/phase1-research.js +0 -578
  88. package/.claude/workflow/phases/phase1-research.ts +0 -465
  89. package/.claude/workflow/phases/phase2-approve.js +0 -722
  90. package/.claude/workflow/phases/phase3-plan.js +0 -1200
  91. package/.claude/workflow/phases/phase4-develop.js +0 -894
  92. package/.claude/workflow/search-cache.js +0 -230
  93. package/.claude/workflow/templates/approval.md +0 -315
  94. package/.claude/workflow/templates/development.md +0 -377
  95. package/.claude/workflow/templates/planning.md +0 -328
  96. package/.claude/workflow/templates/research.md +0 -250
  97. package/.claude/workflow/types.js +0 -37
  98. package/.claude/workflow/web-search.js +0 -278
  99. package/.claude-plugin/marketplace.json +0 -71
  100. package/.github/workflows/sync-skills.yml +0 -74
  101. package/.versionrc +0 -25
  102. package/AGENTS.md +0 -580
  103. package/CHANGELOG.md +0 -481
  104. package/CLAUDE-template.md +0 -114
  105. package/DEV_TOOLS_GUIDE.md +0 -190
  106. package/PROJECT_STRUCTURE.md +0 -266
  107. package/Q&A.md +0 -325
  108. package/config/defaults.json +0 -34
  109. package/config/official-skills.json +0 -183
  110. package/config/quality-gate.json +0 -67
  111. package/config/skill-categories.json +0 -40
  112. package/config/version-manifest.json +0 -85
  113. package/demos/power-3d-scatter.html +0 -683
  114. package/development/cache/web-search/search_1193d605f8eb364651fc2f2041b58a31.json +0 -36
  115. package/development/cache/web-search/search_3798bf06960edc125f744a1abb5b72c5.json +0 -36
  116. package/development/cache/web-search/search_37c7d4843a53f0d83f1122a6f908a2a3.json +0 -36
  117. package/development/cache/web-search/search_44166fa0153709ee168485a22aa0ab40.json +0 -36
  118. package/development/cache/web-search/search_4deaebb1f77e86a8ca066dc5a49c59fd.json +0 -36
  119. package/development/cache/web-search/search_94da91789466070a7f545612e73c7372.json +0 -36
  120. package/development/cache/web-search/search_dd5de8491b8b803a3cb01339cd210fb0.json +0 -36
  121. package/development/knowledge-base/.index.clean.json +0 -1
  122. package/development/knowledge-base/.index.json +0 -486
  123. package/development/knowledge-base/test-best-practices.md +0 -29
  124. package/development/projects/proj_mkh1pazz_ixmt1/phase1/feasibility-report.md +0 -160
  125. package/development/projects/proj_mkh4jvnb_z7rwf/phase1/feasibility-report.md +0 -160
  126. package/development/projects/proj_mkh4jxkd_ewz5a/phase1/feasibility-report.md +0 -160
  127. package/development/projects/proj_mkh4k84n_ni73k/phase1/feasibility-report.md +0 -160
  128. package/development/projects/proj_mkh4wfyd_u9w88/phase1/feasibility-report.md +0 -160
  129. package/development/projects/proj_mkh4wsbo_iahvf/development/projects/proj_mkh4xbpg_4na5w/phase1/feasibility-report.md +0 -160
  130. package/development/projects/proj_mkh4wsbo_iahvf/phase1/feasibility-report.md +0 -160
  131. package/development/projects/proj_mkh4xulg_1ka8x/phase1/feasibility-report.md +0 -160
  132. package/development/projects/proj_mkh4xwhj_gch8j/phase1/feasibility-report.md +0 -160
  133. package/development/projects/proj_mkh4y2qk_9lm8z/phase1/feasibility-report.md +0 -160
  134. package/development/projects/proj_mkh4y2qk_9lm8z/phase2/requirements.md +0 -226
  135. package/development/projects/proj_mkh4y2qk_9lm8z/phase3/PRD.md +0 -345
  136. package/development/projects/proj_mkh4y2qk_9lm8z/phase3/TASK_PLAN.md +0 -284
  137. package/development/projects/proj_mkh4y2qk_9lm8z/phase3/prototype/README.md +0 -14
  138. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/DEVELOPMENT_LOG.md +0 -35
  139. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/TASKS.md +0 -34
  140. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/.env.example +0 -5
  141. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/README.md +0 -60
  142. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/package.json +0 -25
  143. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/src/index.js +0 -70
  144. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/src/routes/index.js +0 -48
  145. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/tests/health.test.js +0 -20
  146. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/tests/jest.config.js +0 -21
  147. package/development/projects/proj_mkh7veqg_3lypc/phase1/feasibility-report.md +0 -160
  148. package/development/projects/proj_mkh7veqg_3lypc/phase2/requirements.md +0 -226
  149. package/development/projects/proj_mkh7veqg_3lypc/phase3/PRD.md +0 -345
  150. package/development/projects/proj_mkh7veqg_3lypc/phase3/TASK_PLAN.md +0 -284
  151. package/development/projects/proj_mkh7veqg_3lypc/phase3/prototype/README.md +0 -14
  152. package/development/projects/proj_mkh8k8fo_rmqn5/phase1/feasibility-report.md +0 -160
  153. package/development/projects/proj_mkh8xyhy_1vshq/phase1/feasibility-report.md +0 -178
  154. package/development/projects/proj_mkh8zddd_dhamf/phase1/feasibility-report.md +0 -377
  155. package/development/projects/proj_mkh8zddd_dhamf/phase2/requirements.md +0 -442
  156. package/development/projects/proj_mkh8zddd_dhamf/phase3/api-design.md +0 -800
  157. package/development/projects/proj_mkh8zddd_dhamf/phase3/architecture.md +0 -625
  158. package/development/projects/proj_mkh8zddd_dhamf/phase3/data-model.md +0 -830
  159. package/development/projects/proj_mkh8zddd_dhamf/phase3/risks.md +0 -957
  160. package/development/projects/proj_mkh8zddd_dhamf/phase3/wbs.md +0 -381
  161. package/development/todos/.state.json +0 -19
  162. package/development/todos/INDEX.md +0 -63
  163. package/development/todos/active/_README.md +0 -49
  164. package/development/todos/archived/_README.md +0 -11
  165. package/development/todos/backlog/_README.md +0 -11
  166. package/development/todos/backlog/mcp-integration.md +0 -35
  167. package/development/todos/completed/_README.md +0 -11
  168. package/development/todos/completed/boris-optimizations.md +0 -39
  169. package/development/todos/completed/develop/local-knowledge-index.md +0 -85
  170. package/development/todos/completed/develop/todo-system.md +0 -47
  171. package/development/todos/completed/develop/web-search-integration.md +0 -83
  172. package/development/todos/completed/test/phase1-e2e-test.md +0 -103
  173. package/docs/DEVELOPMENT.md +0 -461
  174. package/docs/MARKETPLACE.md +0 -352
  175. package/docs/RELEASE.md +0 -93
  176. package/jest.config.js +0 -63
  177. package/lib/commands.js +0 -3588
  178. package/lib/config-manager.js +0 -441
  179. package/lib/config-schema.js +0 -408
  180. package/lib/config-validator.js +0 -330
  181. package/lib/config.js +0 -122
  182. package/lib/errors.js +0 -305
  183. package/lib/incremental-sync.js +0 -274
  184. package/lib/marketplace.js +0 -487
  185. package/lib/migrations.js +0 -154
  186. package/lib/permission-audit.js +0 -255
  187. package/lib/quality-gate.js +0 -431
  188. package/lib/quality-rules.js +0 -373
  189. package/lib/utils.js +0 -150
  190. package/lib/version-check.js +0 -169
  191. package/lib/version-manifest.js +0 -171
  192. package/project-paradigm.md +0 -313
  193. package/prompts/how-to-find.md +0 -163
  194. package/prompts/linus-architect.md +0 -71
  195. package/prompts/software-architect.md +0 -173
  196. package/prompts/web-designer.md +0 -249
  197. package/scripts/fix-hooks.mjs +0 -97
  198. package/scripts/sync-external.mjs +0 -298
  199. package/scripts/sync-to-home.sh +0 -108
  200. package/scripts/update-registry.mjs +0 -325
  201. package/sources.yaml +0 -83
  202. package/tests/README.md +0 -263
  203. package/tests/commands.test.js +0 -1086
  204. package/tests/config-manager.test.js +0 -677
  205. package/tests/config-schema.test.js +0 -425
  206. package/tests/config-validator.test.js +0 -436
  207. package/tests/config.test.js +0 -100
  208. package/tests/errors.test.js +0 -477
  209. package/tests/manual/phase1-e2e.sh +0 -389
  210. package/tests/manual/phase2-test-cases.md +0 -311
  211. package/tests/manual/phase3-test-cases.md +0 -309
  212. package/tests/manual/phase4-test-cases.md +0 -414
  213. package/tests/manual/test-cases.md +0 -417
  214. package/tests/marketplace.test.js +0 -420
  215. package/tests/migrations.test.js +0 -187
  216. package/tests/quality-gate.test.js +0 -679
  217. package/tests/quality-rules.test.js +0 -619
  218. package/tests/sync-external.test.js +0 -214
  219. package/tests/update-registry.test.js +0 -251
  220. package/tests/utils.test.js +0 -171
  221. package/tests/version-check.test.js +0 -75
  222. package/tests/web-search.test.js +0 -392
  223. package/thinkinglens-silent.md +0 -138
@@ -1,619 +0,0 @@
1
- /**
2
- * Quality Rules 模块单元测试
3
- */
4
-
5
- const fs = require('fs');
6
- const path = require('path');
7
- const os = require('os');
8
-
9
- describe('Quality Rules Module', () => {
10
- const { RuleRegistry, registry } = require('../lib/quality-rules');
11
-
12
- // Create temporary test files
13
- const tempDir = path.join(os.tmpdir(), 'smc-rules-test-' + Date.now());
14
- const testJsFile = path.join(tempDir, 'test.js');
15
- const testPyFile = path.join(tempDir, 'test.py');
16
- const testMdFile = path.join(tempDir, 'test.md');
17
- const testJsonFile = path.join(tempDir, 'test.json');
18
- const largeJsFile = path.join(tempDir, 'large.js');
19
- const emptyJsFile = path.join(tempDir, 'empty.js');
20
- const rulesJsonFile = path.join(tempDir, 'rules.json');
21
- const rulesYamlFile = path.join(tempDir, 'rules.yaml');
22
-
23
- beforeAll(() => {
24
- fs.mkdirSync(tempDir, { recursive: true });
25
-
26
- // Create test files with various content
27
- fs.writeFileSync(testJsFile, 'console.log("hello");\nconsole.log("world");\nconst x = 1;\n');
28
- fs.writeFileSync(testPyFile, '# Python file\nprint("hello")');
29
- fs.writeFileSync(testMdFile, '# Title\n\nContent here\n');
30
- fs.writeFileSync(testJsonFile, '{"key": "value"}');
31
-
32
- // Create a large JavaScript file (over 800 lines)
33
- const lines = ['// Large file'];
34
- for (let i = 0; i < 850; i++) {
35
- lines.push(`const line${i} = ${i};`);
36
- }
37
- fs.writeFileSync(largeJsFile, lines.join('\n'));
38
-
39
- // Create an empty file
40
- fs.writeFileSync(emptyJsFile, '\n\n');
41
-
42
- // Create custom rules JSON file
43
- fs.writeFileSync(rulesJsonFile, JSON.stringify({
44
- rules: [
45
- {
46
- id: 'custom-rule-1',
47
- name: 'Custom Rule 1',
48
- severity: 'error',
49
- enabled: true,
50
- description: 'A custom rule',
51
- check: () => ({ pass: true, skip: true })
52
- }
53
- ]
54
- }));
55
-
56
- // Create custom rules YAML file
57
- fs.writeFileSync(rulesYamlFile, `
58
- rules:
59
- - id: custom-yaml-rule
60
- name: Custom YAML Rule
61
- severity: warn
62
- enabled: true
63
- description: A custom YAML rule
64
- `);
65
- });
66
-
67
- afterAll(() => {
68
- if (fs.existsSync(tempDir)) {
69
- fs.rmSync(tempDir, { recursive: true, force: true });
70
- }
71
- });
72
-
73
- describe('RuleRegistry', () => {
74
- let customRegistry;
75
-
76
- beforeEach(() => {
77
- customRegistry = new RuleRegistry();
78
- });
79
-
80
- describe('register', () => {
81
- it('should register a new rule', () => {
82
- const rule = customRegistry.register('test-rule', {
83
- name: 'Test Rule',
84
- severity: 'warn',
85
- check: () => ({ pass: true })
86
- });
87
-
88
- expect(rule.id).toBe('test-rule');
89
- expect(rule.name).toBe('Test Rule');
90
- expect(rule.severity).toBe('warn');
91
- expect(rule.enabled).toBe(true);
92
- });
93
-
94
- it('should use id as default name', () => {
95
- const rule = customRegistry.register('no-name', {
96
- check: () => ({ pass: true })
97
- });
98
-
99
- expect(rule.name).toBe('no-name');
100
- });
101
-
102
- it('should use default severity', () => {
103
- const rule = customRegistry.register('default-severity', {
104
- check: () => ({ pass: true })
105
- });
106
-
107
- expect(rule.severity).toBe('warn');
108
- });
109
-
110
- it('should enable rule by default', () => {
111
- const rule = customRegistry.register('enabled-default', {
112
- enabled: undefined,
113
- check: () => ({ pass: true })
114
- });
115
-
116
- expect(rule.enabled).toBe(true);
117
- });
118
-
119
- it('should allow explicit disable', () => {
120
- const rule = customRegistry.register('disabled-rule', {
121
- enabled: false,
122
- check: () => ({ pass: true })
123
- });
124
-
125
- expect(rule.enabled).toBe(false);
126
- });
127
-
128
- it('should store config object', () => {
129
- const rule = customRegistry.register('config-rule', {
130
- config: { maxLines: 100 },
131
- check: () => ({ pass: true })
132
- });
133
-
134
- expect(rule.config).toEqual({ maxLines: 100 });
135
- });
136
-
137
- it('should return registered rule', () => {
138
- const registered = customRegistry.register('return-test', {
139
- name: 'Return Test',
140
- check: () => ({ pass: true })
141
- });
142
-
143
- expect(registered).toBeDefined();
144
- expect(registered.id).toBe('return-test');
145
- });
146
- });
147
-
148
- describe('get', () => {
149
- beforeEach(() => {
150
- customRegistry.register('get-test', {
151
- name: 'Get Test',
152
- check: () => ({ pass: true })
153
- });
154
- });
155
-
156
- it('should get existing rule', () => {
157
- const rule = customRegistry.get('get-test');
158
-
159
- expect(rule).toBeDefined();
160
- expect(rule.id).toBe('get-test');
161
- expect(rule.name).toBe('Get Test');
162
- });
163
-
164
- it('should return null for non-existent rule', () => {
165
- const rule = customRegistry.get('does-not-exist');
166
-
167
- expect(rule).toBeNull();
168
- });
169
- });
170
-
171
- describe('has', () => {
172
- beforeEach(() => {
173
- customRegistry.register('has-test', {
174
- check: () => ({ pass: true })
175
- });
176
- });
177
-
178
- it('should return true for existing rule', () => {
179
- expect(customRegistry.has('has-test')).toBe(true);
180
- });
181
-
182
- it('should return false for non-existent rule', () => {
183
- expect(customRegistry.has('does-not-exist')).toBe(false);
184
- });
185
- });
186
-
187
- describe('getAll', () => {
188
- beforeEach(() => {
189
- customRegistry.register('rule-1', {
190
- severity: 'error',
191
- enabled: true,
192
- category: 'code-style',
193
- check: () => ({ pass: true })
194
- });
195
- customRegistry.register('rule-2', {
196
- severity: 'warn',
197
- enabled: false,
198
- category: 'code-style',
199
- check: () => ({ pass: true })
200
- });
201
- customRegistry.register('rule-3', {
202
- severity: 'warn',
203
- enabled: true,
204
- category: 'quality',
205
- check: () => ({ pass: true })
206
- });
207
- });
208
-
209
- it('should return all rules when no filter provided', () => {
210
- const rules = customRegistry.getAll();
211
-
212
- expect(rules.length).toBeGreaterThan(0);
213
- });
214
-
215
- it('should filter by severity', () => {
216
- const errorRules = customRegistry.getAll({ severity: 'error' });
217
-
218
- expect(errorRules.length).toBeGreaterThan(0);
219
- expect(errorRules.every(r => r.severity === 'error')).toBe(true);
220
- });
221
-
222
- it('should filter by enabled status', () => {
223
- const enabledRules = customRegistry.getAll({ enabled: true });
224
- const disabledRules = customRegistry.getAll({ enabled: false });
225
-
226
- expect(enabledRules.every(r => r.enabled === true)).toBe(true);
227
- expect(disabledRules.every(r => r.enabled === false)).toBe(true);
228
- });
229
-
230
- it('should filter by category', () => {
231
- const styleRules = customRegistry.getAll({ category: 'code-style' });
232
-
233
- expect(styleRules.every(r => r.category === 'code-style')).toBe(true);
234
- });
235
-
236
- it('should combine multiple filters', () => {
237
- const rules = customRegistry.getAll({
238
- enabled: true,
239
- severity: 'warn'
240
- });
241
-
242
- expect(rules.every(r => r.enabled === true && r.severity === 'warn')).toBe(true);
243
- });
244
- });
245
-
246
- describe('setEnabled', () => {
247
- it('should enable a disabled rule', () => {
248
- customRegistry.register('to-enable', {
249
- enabled: false,
250
- check: () => ({ pass: true })
251
- });
252
-
253
- customRegistry.setEnabled('to-enable', true);
254
- const rule = customRegistry.get('to-enable');
255
-
256
- expect(rule.enabled).toBe(true);
257
- });
258
-
259
- it('should disable an enabled rule', () => {
260
- customRegistry.register('to-disable', {
261
- enabled: true,
262
- check: () => ({ pass: true })
263
- });
264
-
265
- customRegistry.setEnabled('to-disable', false);
266
- const rule = customRegistry.get('to-disable');
267
-
268
- expect(rule.enabled).toBe(false);
269
- });
270
-
271
- it('should do nothing for non-existent rule', () => {
272
- expect(() => {
273
- customRegistry.setEnabled('non-existent', true);
274
- }).not.toThrow();
275
- });
276
- });
277
-
278
- describe('updateConfig', () => {
279
- it('should update rule config', () => {
280
- customRegistry.register('config-update', {
281
- config: { maxLines: 100 },
282
- check: () => ({ pass: true })
283
- });
284
-
285
- customRegistry.updateConfig('config-update', { maxLines: 200, newSize: 500 });
286
- const rule = customRegistry.get('config-update');
287
-
288
- expect(rule.config.maxLines).toBe(200);
289
- expect(rule.config.newSize).toBe(500);
290
- });
291
-
292
- it('should merge configs', () => {
293
- customRegistry.register('merge-config', {
294
- config: { a: 1, b: 2 },
295
- check: () => ({ pass: true })
296
- });
297
-
298
- customRegistry.updateConfig('merge-config', { b: 3, c: 4 });
299
- const rule = customRegistry.get('merge-config');
300
-
301
- expect(rule.config.a).toBe(1);
302
- expect(rule.config.b).toBe(3);
303
- expect(rule.config.c).toBe(4);
304
- });
305
-
306
- it('should do nothing for non-existent rule', () => {
307
- expect(() => {
308
- customRegistry.updateConfig('non-existent', {});
309
- }).not.toThrow();
310
- });
311
- });
312
-
313
- describe('loadFromFile', () => {
314
- it('should load rules from JSON file', () => {
315
- customRegistry.loadFromFile(rulesJsonFile);
316
-
317
- expect(customRegistry.has('custom-rule-1')).toBe(true);
318
- const rule = customRegistry.get('custom-rule-1');
319
- expect(rule.name).toBe('Custom Rule 1');
320
- expect(rule.severity).toBe('error');
321
- });
322
-
323
- it('should update existing rule from file', () => {
324
- customRegistry.register('custom-rule-1', {
325
- name: 'Original Name',
326
- severity: 'warn',
327
- check: () => ({ pass: true })
328
- });
329
-
330
- customRegistry.loadFromFile(rulesJsonFile);
331
-
332
- const rule = customRegistry.get('custom-rule-1');
333
- expect(rule.name).toBe('Custom Rule 1');
334
- expect(rule.severity).toBe('error');
335
- });
336
-
337
- it('should do nothing for non-existent file', () => {
338
- expect(() => {
339
- customRegistry.loadFromFile('/does/not/exist.json');
340
- }).not.toThrow();
341
- });
342
-
343
- it('should load rules from YAML file (if yaml available)', () => {
344
- const hasYaml = (() => {
345
- try {
346
- require('yaml');
347
- return true;
348
- } catch {
349
- return false;
350
- }
351
- })();
352
-
353
- customRegistry.loadFromFile(rulesYamlFile);
354
-
355
- if (hasYaml) {
356
- expect(customRegistry.has('custom-yaml-rule')).toBe(true);
357
- }
358
- // If yaml not available, just verify it doesn't crash
359
- });
360
- });
361
- });
362
-
363
- describe('Global Registry', () => {
364
- it('should export global registry instance', () => {
365
- expect(registry).toBeDefined();
366
- expect(registry instanceof RuleRegistry).toBe(true);
367
- });
368
-
369
- it('should export convenience functions', () => {
370
- const {
371
- register,
372
- get,
373
- has,
374
- getAll,
375
- setEnabled,
376
- updateConfig,
377
- loadFromFile
378
- } = require('../lib/quality-rules');
379
-
380
- expect(typeof register).toBe('function');
381
- expect(typeof get).toBe('function');
382
- expect(typeof has).toBe('function');
383
- expect(typeof getAll).toBe('function');
384
- expect(typeof setEnabled).toBe('function');
385
- expect(typeof updateConfig).toBe('function');
386
- expect(typeof loadFromFile).toBe('function');
387
- });
388
- });
389
-
390
- describe('Built-in Rules', () => {
391
- describe('file-size-limit', () => {
392
- const rule = registry.get('file-size-limit');
393
-
394
- it('should exist', () => {
395
- expect(rule).toBeDefined();
396
- expect(rule.id).toBe('file-size-limit');
397
- });
398
-
399
- it('should pass small files', () => {
400
- const result = rule.check(testJsFile, rule.config);
401
- expect(result.pass).toBe(true);
402
- });
403
-
404
- it('should fail files exceeding size limit', () => {
405
- // Create a large temporary file
406
- const largeFile = path.join(tempDir, 'large-size.js');
407
- const largeContent = 'x'.repeat(900000); // ~900KB
408
- fs.writeFileSync(largeFile, largeContent);
409
-
410
- const result = rule.check(largeFile, { maxSize: 800 * 1024 });
411
-
412
- expect(result.pass).toBe(false);
413
- expect(result.message).toContain('exceeds limit');
414
-
415
- fs.unlinkSync(largeFile);
416
- });
417
- });
418
-
419
- describe('line-count-limit', () => {
420
- const rule = registry.get('line-count-limit');
421
-
422
- it('should exist', () => {
423
- expect(rule).toBeDefined();
424
- expect(rule.id).toBe('line-count-limit');
425
- });
426
-
427
- it('should pass files under limit', () => {
428
- const result = rule.check(testJsFile, rule.config);
429
- expect(result.pass).toBe(true);
430
- });
431
-
432
- it('should fail files exceeding line limit', () => {
433
- const result = rule.check(largeJsFile, { maxLines: 800 });
434
-
435
- expect(result.pass).toBe(false);
436
- expect(result.message).toContain('exceeds line limit');
437
- });
438
- });
439
-
440
- describe('no-console-logs', () => {
441
- const rule = registry.get('no-console-logs');
442
-
443
- it('should exist', () => {
444
- expect(rule).toBeDefined();
445
- expect(rule.id).toBe('no-console-logs');
446
- });
447
-
448
- it('should detect console.log statements', () => {
449
- const result = rule.check(testJsFile);
450
-
451
- expect(result.pass).toBe(false);
452
- expect(result.message).toContain('console statement');
453
- });
454
-
455
- it('should skip non-JS files', () => {
456
- const result = rule.check(testMdFile);
457
-
458
- expect(result.pass).toBe(true);
459
- expect(result.skip).toBe(true);
460
- });
461
- });
462
-
463
- describe('todo-comments', () => {
464
- const rule = registry.get('todo-comments');
465
-
466
- it('should exist', () => {
467
- expect(rule).toBeDefined();
468
- expect(rule.id).toBe('todo-comments');
469
- });
470
-
471
- it('should find TODO comments', () => {
472
- const todoFile = path.join(tempDir, 'todo.js');
473
- fs.writeFileSync(todoFile, '// TODO: fix this\n// FIXME: later\n');
474
-
475
- const result = rule.check(todoFile);
476
-
477
- expect(result.pass).toBe(true); // TODO is just informational
478
- expect(result.message).toContain('TODO');
479
- });
480
-
481
- it('should skip non-code files', () => {
482
- const result = rule.check(testJsonFile);
483
-
484
- expect(result.pass).toBe(true);
485
- expect(result.skip).toBe(true);
486
- });
487
- });
488
-
489
- describe('directory-depth', () => {
490
- const rule = registry.get('directory-depth');
491
-
492
- it('should exist', () => {
493
- expect(rule).toBeDefined();
494
- expect(rule.id).toBe('directory-depth');
495
- });
496
-
497
- it('should pass normal depth paths', () => {
498
- const result = rule.check(testJsFile, { maxDepth: 10 });
499
-
500
- expect(result.pass).toBe(true);
501
- });
502
-
503
- it('should fail deep paths', () => {
504
- const deepPath = '/a/b/c/d/e/f/g/h/i/j/k/l/file.js';
505
- const result = rule.check(deepPath, { maxDepth: 6 });
506
-
507
- expect(result.pass).toBe(false);
508
- expect(result.message).toContain('depth');
509
- });
510
- });
511
-
512
- describe('no-empty-files', () => {
513
- const rule = registry.get('no-empty-files');
514
-
515
- it('should exist', () => {
516
- expect(rule).toBeDefined();
517
- expect(rule.id).toBe('no-empty-files');
518
- });
519
-
520
- it('should pass files with content', () => {
521
- const result = rule.check(testJsFile, rule.config);
522
-
523
- expect(result.pass).toBe(true);
524
- });
525
-
526
- it('should fail empty or near-empty files', () => {
527
- const result = rule.check(emptyJsFile, { minLines: 3 });
528
-
529
- expect(result.pass).toBe(false);
530
- expect(result.message).toContain('line');
531
- });
532
- });
533
-
534
- describe('no-trailing-whitespace', () => {
535
- const rule = registry.get('no-trailing-whitespace');
536
-
537
- it('should exist', () => {
538
- expect(rule).toBeDefined();
539
- expect(rule.id).toBe('no-trailing-whitespace');
540
- });
541
-
542
- it('should pass files without trailing whitespace', () => {
543
- const cleanFile = path.join(tempDir, 'clean.js');
544
- fs.writeFileSync(cleanFile, 'const x = 1;\nconst y = 2;\n');
545
-
546
- const result = rule.check(cleanFile);
547
-
548
- expect(result.pass).toBe(true);
549
- });
550
-
551
- it('should detect trailing whitespace', () => {
552
- const dirtyFile = path.join(tempDir, 'dirty.js');
553
- fs.writeFileSync(dirtyFile, 'const x = 1; \nconst y = 2;\t\n');
554
-
555
- const result = rule.check(dirtyFile);
556
-
557
- expect(result.pass).toBe(false);
558
- expect(result.message).toContain('Trailing whitespace');
559
- expect(result.autoFix).toBe(true);
560
- });
561
-
562
- it('should skip non-text files', () => {
563
- const result = rule.check(testJsonFile);
564
-
565
- // JSON is not in the check list, so it should be skipped
566
- expect(result.pass).toBe(true);
567
- });
568
- });
569
-
570
- describe('function-length', () => {
571
- const rule = registry.get('function-length');
572
-
573
- it('should exist', () => {
574
- expect(rule).toBeDefined();
575
- expect(rule.id).toBe('function-length');
576
- });
577
-
578
- it('should be disabled by default', () => {
579
- expect(rule.enabled).toBe(false);
580
- });
581
-
582
- it('should skip non-JS files', () => {
583
- const result = rule.check(testPyFile);
584
-
585
- expect(result.pass).toBe(true);
586
- expect(result.skip).toBe(true);
587
- });
588
- });
589
- });
590
-
591
- describe('Rule Execution', () => {
592
- it('should handle non-existent files by throwing or returning error', () => {
593
- const rule = registry.get('line-count-limit');
594
-
595
- // The rule will try to read the file, which will throw
596
- expect(() => {
597
- rule.check('/does/not/exist.js');
598
- }).toThrow();
599
- });
600
-
601
- it('should handle various file extensions', () => {
602
- const consoleRule = registry.get('no-console-logs');
603
-
604
- // Should check JS files
605
- let result = consoleRule.check(testJsFile);
606
- expect(result.pass).toBe(false); // Has console.log
607
-
608
- // Should check TS files
609
- const tsFile = path.join(tempDir, 'test.ts');
610
- fs.writeFileSync(tsFile, 'console.log("test");');
611
- result = consoleRule.check(tsFile);
612
- expect(result.pass).toBe(false);
613
-
614
- // Should skip markdown
615
- result = consoleRule.check(testMdFile);
616
- expect(result.skip).toBe(true);
617
- });
618
- });
619
- });