sumulige-claude 1.5.1 → 1.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) 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/cli.js +1 -1
  48. package/package.json +25 -3
  49. package/.claude/.kickoff-hint.txt +0 -52
  50. package/.claude/.sumulige-claude-version +0 -1
  51. package/.claude/.version +0 -1
  52. package/.claude/AGENTS.md +0 -42
  53. package/.claude/ANCHORS.md +0 -40
  54. package/.claude/CLAUDE.md +0 -138
  55. package/.claude/MEMORY.md +0 -69
  56. package/.claude/PROJECT_LOG.md +0 -101
  57. package/.claude/THINKING_CHAIN_GUIDE.md +0 -287
  58. package/.claude/USAGE.md +0 -175
  59. package/.claude/boris-optimizations.md +0 -167
  60. package/.claude/handoffs/INDEX.md +0 -21
  61. package/.claude/handoffs/LATEST.md +0 -76
  62. package/.claude/handoffs/handoff_2026-01-22T13-07-04-757Z.md +0 -76
  63. package/.claude/quality-gate.json +0 -82
  64. package/.claude/rag/skill-index.json +0 -135
  65. package/.claude/settings.json +0 -99
  66. package/.claude/settings.local.json +0 -175
  67. package/.claude/templates/PROJECT_KICKOFF.md +0 -89
  68. package/.claude/templates/PROJECT_PROPOSAL.md +0 -227
  69. package/.claude/templates/TASK_PLAN.md +0 -121
  70. package/.claude/templates/hooks/README.md +0 -302
  71. package/.claude/templates/hooks/hook.sh.template +0 -94
  72. package/.claude/templates/hooks/user-prompt-submit.cjs.template +0 -116
  73. package/.claude/templates/hooks/user-response-submit.cjs.template +0 -94
  74. package/.claude/templates/hooks/validate.js +0 -173
  75. package/.claude/templates/tasks/develop.md +0 -69
  76. package/.claude/templates/tasks/research.md +0 -64
  77. package/.claude/templates/tasks/test.md +0 -96
  78. package/.claude/thinking-routes/.last-sync +0 -1
  79. package/.claude/thinking-routes/QUICKREF.md +0 -98
  80. package/.claude/workflow/document-scanner.js +0 -426
  81. package/.claude/workflow/knowledge-engine.js +0 -941
  82. package/.claude/workflow/notebooklm/browser.js +0 -1028
  83. package/.claude/workflow/phases/phase1-research.js +0 -578
  84. package/.claude/workflow/phases/phase1-research.ts +0 -465
  85. package/.claude/workflow/phases/phase2-approve.js +0 -722
  86. package/.claude/workflow/phases/phase3-plan.js +0 -1200
  87. package/.claude/workflow/phases/phase4-develop.js +0 -894
  88. package/.claude/workflow/search-cache.js +0 -230
  89. package/.claude/workflow/templates/approval.md +0 -315
  90. package/.claude/workflow/templates/development.md +0 -377
  91. package/.claude/workflow/templates/planning.md +0 -328
  92. package/.claude/workflow/templates/research.md +0 -250
  93. package/.claude/workflow/types.js +0 -37
  94. package/.claude/workflow/web-search.js +0 -278
  95. package/.claude-plugin/marketplace.json +0 -71
  96. package/.github/workflows/sync-skills.yml +0 -74
  97. package/.versionrc +0 -25
  98. package/AGENTS.md +0 -580
  99. package/CHANGELOG.md +0 -481
  100. package/CLAUDE-template.md +0 -114
  101. package/DEV_TOOLS_GUIDE.md +0 -190
  102. package/PROJECT_STRUCTURE.md +0 -266
  103. package/Q&A.md +0 -325
  104. package/config/defaults.json +0 -34
  105. package/config/official-skills.json +0 -183
  106. package/config/quality-gate.json +0 -67
  107. package/config/skill-categories.json +0 -40
  108. package/config/version-manifest.json +0 -85
  109. package/demos/power-3d-scatter.html +0 -683
  110. package/development/cache/web-search/search_1193d605f8eb364651fc2f2041b58a31.json +0 -36
  111. package/development/cache/web-search/search_3798bf06960edc125f744a1abb5b72c5.json +0 -36
  112. package/development/cache/web-search/search_37c7d4843a53f0d83f1122a6f908a2a3.json +0 -36
  113. package/development/cache/web-search/search_44166fa0153709ee168485a22aa0ab40.json +0 -36
  114. package/development/cache/web-search/search_4deaebb1f77e86a8ca066dc5a49c59fd.json +0 -36
  115. package/development/cache/web-search/search_94da91789466070a7f545612e73c7372.json +0 -36
  116. package/development/cache/web-search/search_dd5de8491b8b803a3cb01339cd210fb0.json +0 -36
  117. package/development/knowledge-base/.index.clean.json +0 -1
  118. package/development/knowledge-base/.index.json +0 -486
  119. package/development/knowledge-base/test-best-practices.md +0 -29
  120. package/development/projects/proj_mkh1pazz_ixmt1/phase1/feasibility-report.md +0 -160
  121. package/development/projects/proj_mkh4jvnb_z7rwf/phase1/feasibility-report.md +0 -160
  122. package/development/projects/proj_mkh4jxkd_ewz5a/phase1/feasibility-report.md +0 -160
  123. package/development/projects/proj_mkh4k84n_ni73k/phase1/feasibility-report.md +0 -160
  124. package/development/projects/proj_mkh4wfyd_u9w88/phase1/feasibility-report.md +0 -160
  125. package/development/projects/proj_mkh4wsbo_iahvf/development/projects/proj_mkh4xbpg_4na5w/phase1/feasibility-report.md +0 -160
  126. package/development/projects/proj_mkh4wsbo_iahvf/phase1/feasibility-report.md +0 -160
  127. package/development/projects/proj_mkh4xulg_1ka8x/phase1/feasibility-report.md +0 -160
  128. package/development/projects/proj_mkh4xwhj_gch8j/phase1/feasibility-report.md +0 -160
  129. package/development/projects/proj_mkh4y2qk_9lm8z/phase1/feasibility-report.md +0 -160
  130. package/development/projects/proj_mkh4y2qk_9lm8z/phase2/requirements.md +0 -226
  131. package/development/projects/proj_mkh4y2qk_9lm8z/phase3/PRD.md +0 -345
  132. package/development/projects/proj_mkh4y2qk_9lm8z/phase3/TASK_PLAN.md +0 -284
  133. package/development/projects/proj_mkh4y2qk_9lm8z/phase3/prototype/README.md +0 -14
  134. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/DEVELOPMENT_LOG.md +0 -35
  135. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/TASKS.md +0 -34
  136. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/.env.example +0 -5
  137. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/README.md +0 -60
  138. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/package.json +0 -25
  139. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/src/index.js +0 -70
  140. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/src/routes/index.js +0 -48
  141. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/tests/health.test.js +0 -20
  142. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/tests/jest.config.js +0 -21
  143. package/development/projects/proj_mkh7veqg_3lypc/phase1/feasibility-report.md +0 -160
  144. package/development/projects/proj_mkh7veqg_3lypc/phase2/requirements.md +0 -226
  145. package/development/projects/proj_mkh7veqg_3lypc/phase3/PRD.md +0 -345
  146. package/development/projects/proj_mkh7veqg_3lypc/phase3/TASK_PLAN.md +0 -284
  147. package/development/projects/proj_mkh7veqg_3lypc/phase3/prototype/README.md +0 -14
  148. package/development/projects/proj_mkh8k8fo_rmqn5/phase1/feasibility-report.md +0 -160
  149. package/development/projects/proj_mkh8xyhy_1vshq/phase1/feasibility-report.md +0 -178
  150. package/development/projects/proj_mkh8zddd_dhamf/phase1/feasibility-report.md +0 -377
  151. package/development/projects/proj_mkh8zddd_dhamf/phase2/requirements.md +0 -442
  152. package/development/projects/proj_mkh8zddd_dhamf/phase3/api-design.md +0 -800
  153. package/development/projects/proj_mkh8zddd_dhamf/phase3/architecture.md +0 -625
  154. package/development/projects/proj_mkh8zddd_dhamf/phase3/data-model.md +0 -830
  155. package/development/projects/proj_mkh8zddd_dhamf/phase3/risks.md +0 -957
  156. package/development/projects/proj_mkh8zddd_dhamf/phase3/wbs.md +0 -381
  157. package/development/todos/.state.json +0 -19
  158. package/development/todos/INDEX.md +0 -63
  159. package/development/todos/active/_README.md +0 -49
  160. package/development/todos/archived/_README.md +0 -11
  161. package/development/todos/backlog/_README.md +0 -11
  162. package/development/todos/backlog/mcp-integration.md +0 -35
  163. package/development/todos/completed/_README.md +0 -11
  164. package/development/todos/completed/boris-optimizations.md +0 -39
  165. package/development/todos/completed/develop/local-knowledge-index.md +0 -85
  166. package/development/todos/completed/develop/todo-system.md +0 -47
  167. package/development/todos/completed/develop/web-search-integration.md +0 -83
  168. package/development/todos/completed/test/phase1-e2e-test.md +0 -103
  169. package/docs/DEVELOPMENT.md +0 -461
  170. package/docs/MARKETPLACE.md +0 -352
  171. package/docs/RELEASE.md +0 -93
  172. package/jest.config.js +0 -63
  173. package/lib/commands.js +0 -3588
  174. package/lib/config-manager.js +0 -441
  175. package/lib/config-schema.js +0 -408
  176. package/lib/config-validator.js +0 -330
  177. package/lib/config.js +0 -122
  178. package/lib/errors.js +0 -305
  179. package/lib/incremental-sync.js +0 -274
  180. package/lib/marketplace.js +0 -487
  181. package/lib/migrations.js +0 -154
  182. package/lib/permission-audit.js +0 -255
  183. package/lib/quality-gate.js +0 -431
  184. package/lib/quality-rules.js +0 -373
  185. package/lib/utils.js +0 -150
  186. package/lib/version-check.js +0 -169
  187. package/lib/version-manifest.js +0 -171
  188. package/project-paradigm.md +0 -313
  189. package/prompts/how-to-find.md +0 -163
  190. package/prompts/linus-architect.md +0 -71
  191. package/prompts/software-architect.md +0 -173
  192. package/prompts/web-designer.md +0 -249
  193. package/scripts/fix-hooks.mjs +0 -97
  194. package/scripts/sync-external.mjs +0 -298
  195. package/scripts/sync-to-home.sh +0 -108
  196. package/scripts/update-registry.mjs +0 -325
  197. package/sources.yaml +0 -83
  198. package/tests/README.md +0 -263
  199. package/tests/commands.test.js +0 -1086
  200. package/tests/config-manager.test.js +0 -677
  201. package/tests/config-schema.test.js +0 -425
  202. package/tests/config-validator.test.js +0 -436
  203. package/tests/config.test.js +0 -100
  204. package/tests/errors.test.js +0 -477
  205. package/tests/manual/phase1-e2e.sh +0 -389
  206. package/tests/manual/phase2-test-cases.md +0 -311
  207. package/tests/manual/phase3-test-cases.md +0 -309
  208. package/tests/manual/phase4-test-cases.md +0 -414
  209. package/tests/manual/test-cases.md +0 -417
  210. package/tests/marketplace.test.js +0 -420
  211. package/tests/migrations.test.js +0 -187
  212. package/tests/quality-gate.test.js +0 -679
  213. package/tests/quality-rules.test.js +0 -619
  214. package/tests/sync-external.test.js +0 -214
  215. package/tests/update-registry.test.js +0 -251
  216. package/tests/utils.test.js +0 -171
  217. package/tests/version-check.test.js +0 -75
  218. package/tests/web-search.test.js +0 -392
  219. package/thinkinglens-silent.md +0 -138
@@ -1,679 +0,0 @@
1
- /**
2
- * Quality Gate 模块单元测试
3
- */
4
-
5
- const fs = require('fs');
6
- const path = require('path');
7
- const os = require('os');
8
-
9
- describe('Quality Gate Module', () => {
10
- const {
11
- QualityGate,
12
- ConsoleReporter,
13
- JsonReporter,
14
- MarkdownReporter,
15
- checkOrThrow
16
- } = require('../lib/quality-gate');
17
-
18
- const { QualityGateError } = require('../lib/errors');
19
-
20
- // Temporary test directory
21
- const tempDir = path.join(os.tmpdir(), 'smc-gate-test-' + Date.now());
22
- const cleanJsFile = path.join(tempDir, 'clean.js');
23
- const dirtyJsFile = path.join(tempDir, 'dirty.js');
24
- const largeJsFile = path.join(tempDir, 'large.js');
25
- const emptyJsFile = path.join(tempDir, 'empty.js');
26
- const subDir = path.join(tempDir, 'sub');
27
- const subDirFile = path.join(subDir, 'file.js');
28
-
29
- beforeAll(() => {
30
- fs.mkdirSync(tempDir, { recursive: true });
31
- fs.mkdirSync(subDir, { recursive: true });
32
-
33
- // Create test files
34
- fs.writeFileSync(cleanJsFile, 'const x = 1;\nconst y = 2;\n');
35
- fs.writeFileSync(dirtyJsFile, 'const x = 1; \nconst y = 2;\t\n');
36
-
37
- // Create large file
38
- const lines = ['// Large file'];
39
- for (let i = 0; i < 850; i++) {
40
- lines.push(`const line${i} = ${i};`);
41
- }
42
- fs.writeFileSync(largeJsFile, lines.join('\n'));
43
-
44
- fs.writeFileSync(emptyJsFile, '\n');
45
- fs.writeFileSync(subDirFile, 'const x = 1;\n');
46
- });
47
-
48
- afterAll(() => {
49
- if (fs.existsSync(tempDir)) {
50
- fs.rmSync(tempDir, { recursive: true, force: true });
51
- }
52
- });
53
-
54
- describe('QualityGate', () => {
55
- describe('constructor', () => {
56
- it('should create gate with default options', () => {
57
- const gate = new QualityGate();
58
-
59
- expect(gate.projectDir).toBeDefined();
60
- expect(gate.config).toBeDefined();
61
- expect(gate.reporters).toBeDefined();
62
- expect(gate.reporters.length).toBeGreaterThan(0);
63
- });
64
-
65
- it('should accept custom project directory', () => {
66
- const gate = new QualityGate({ projectDir: tempDir });
67
-
68
- expect(gate.projectDir).toBe(tempDir);
69
- });
70
-
71
- it('should accept custom config', () => {
72
- const customConfig = {
73
- enabled: true,
74
- severity: 'error',
75
- rules: []
76
- };
77
-
78
- const gate = new QualityGate({ config: customConfig });
79
-
80
- expect(gate.config).toEqual(customConfig);
81
- });
82
-
83
- it('should accept custom reporters', () => {
84
- const customReporter = {
85
- report: jest.fn()
86
- };
87
-
88
- const gate = new QualityGate({ reporters: [customReporter] });
89
-
90
- expect(gate.reporters).toHaveLength(1);
91
- expect(gate.reporters[0]).toBe(customReporter);
92
- });
93
- });
94
-
95
- describe('check', () => {
96
- let gate;
97
-
98
- beforeEach(() => {
99
- gate = new QualityGate({
100
- projectDir: tempDir,
101
- reporters: [] // Suppress output during tests
102
- });
103
- });
104
-
105
- it('should return check result object', async () => {
106
- const result = await gate.check();
107
-
108
- expect(result).toBeDefined();
109
- expect(typeof result.passed).toBe('boolean');
110
- expect(Array.isArray(result.results)).toBe(true);
111
- expect(result.summary).toBeDefined();
112
- });
113
-
114
- it('should scan project files', async () => {
115
- const result = await gate.check();
116
-
117
- expect(result.summary.filesChecked).toBeGreaterThan(0);
118
- });
119
-
120
- it('should run quality rules', async () => {
121
- const result = await gate.check();
122
-
123
- expect(result.summary.rulesRun).toBeGreaterThan(0);
124
- });
125
-
126
- it('should detect issues in files', async () => {
127
- const result = await gate.check();
128
-
129
- // Large file should trigger line-count-limit
130
- const lineCountIssues = result.results.filter(r => r.rule === 'line-count-limit');
131
- expect(lineCountIssues.length).toBeGreaterThan(0);
132
- });
133
-
134
- it('should count issues by severity', async () => {
135
- const result = await gate.check();
136
-
137
- expect(result.summary).toMatchObject({
138
- total: expect.any(Number),
139
- critical: expect.any(Number),
140
- error: expect.any(Number),
141
- warn: expect.any(Number),
142
- info: expect.any(Number)
143
- });
144
- });
145
-
146
- it('should pass when severity is critical and no critical issues', async () => {
147
- const result = await gate.check({ severity: 'critical' });
148
-
149
- // Should pass if no critical issues
150
- expect(typeof result.passed).toBe('boolean');
151
- });
152
-
153
- it('should fail when blocking issues exist', async () => {
154
- // Use a file with known issues
155
- const gateWithIssues = new QualityGate({
156
- projectDir: tempDir,
157
- reporters: [],
158
- config: {
159
- rules: [
160
- { id: 'line-count-limit', enabled: true, severity: 'error' }
161
- ]
162
- }
163
- });
164
-
165
- const result = await gateWithIssues.check({ severity: 'error' });
166
-
167
- // Large file should cause error severity issue
168
- if (result.summary.error > 0) {
169
- expect(result.passed).toBe(false);
170
- }
171
- });
172
-
173
- it('should respect severity threshold', async () => {
174
- const resultWarn = await gate.check({ severity: 'warn' });
175
- const resultCritical = await gate.check({ severity: 'critical' });
176
-
177
- // Critical threshold is less strict than warn (fewer blocking issues)
178
- // So critical should pass more often than warn
179
- if (resultWarn.passed === false && resultCritical.passed === true) {
180
- // This is the expected case: warn fails but critical passes
181
- expect(true).toBe(true);
182
- } else {
183
- // If both pass or both fail, that's also acceptable
184
- expect(resultCritical.passed).toBe(resultWarn.passed);
185
- }
186
- });
187
-
188
- it('should check specific files when provided', async () => {
189
- const result = await gate.check({ files: [cleanJsFile] });
190
-
191
- expect(result.summary.filesChecked).toBe(1);
192
- });
193
-
194
- it('should run specific rules when provided', async () => {
195
- const result = await gate.check({
196
- rules: [{ id: 'no-empty-files', enabled: true }]
197
- });
198
-
199
- expect(result.summary.rulesRun).toBe(1);
200
- });
201
-
202
- it('should apply auto-fixes when requested', async () => {
203
- // Create a file with trailing whitespace for auto-fix test
204
- const fixableFile = path.join(tempDir, 'fixable.js');
205
- fs.writeFileSync(fixableFile, 'const x = 1; \nconst y = 2;\t\n');
206
-
207
- const result = await gate.check({
208
- files: [fixableFile],
209
- fix: true
210
- });
211
-
212
- // The file should now be fixed
213
- const content = fs.readFileSync(fixableFile, 'utf-8');
214
- const hasTrailing = content.match(/[ \t]$/gm);
215
- expect(hasTrailing).toBeNull();
216
- });
217
-
218
- it('should track fixed issues in summary', async () => {
219
- const fixableFile = path.join(tempDir, 'fixable2.js');
220
- fs.writeFileSync(fixableFile, 'const x = 1; \n');
221
-
222
- const result = await gate.check({
223
- files: [fixableFile],
224
- fix: true
225
- });
226
-
227
- expect(result.summary).toHaveProperty('fixed');
228
- });
229
- });
230
-
231
- describe('_getProjectFiles', () => {
232
- it('should ignore common directories', async () => {
233
- // Create node_modules directory
234
- const nodeModulesDir = path.join(tempDir, 'node_modules');
235
- fs.mkdirSync(nodeModulesDir, { recursive: true });
236
- fs.writeFileSync(path.join(nodeModulesDir, 'index.js'), 'ignored');
237
-
238
- const gate = new QualityGate({ projectDir: tempDir, reporters: [] });
239
- const result = await gate.check();
240
-
241
- // Should not include node_modules files
242
- const nodeModulesFiles = result.results.filter(r =>
243
- r.file.includes('node_modules')
244
- );
245
- expect(nodeModulesFiles).toHaveLength(0);
246
-
247
- fs.rmSync(nodeModulesDir, { recursive: true, force: true });
248
- });
249
-
250
- it('should check supported file extensions', async () => {
251
- const gate = new QualityGate({ projectDir: tempDir, reporters: [] });
252
- const result = await gate.check();
253
-
254
- // Should have checked .js files
255
- const jsFiles = result.results.filter(r => r.file.endsWith('.js'));
256
- expect(jsFiles.length).toBeGreaterThan(0);
257
- });
258
- });
259
-
260
- describe('_getActiveRules', () => {
261
- it('should load rules from config file', () => {
262
- const configDir = path.join(tempDir, '.claude');
263
- fs.mkdirSync(configDir, { recursive: true });
264
-
265
- fs.writeFileSync(
266
- path.join(configDir, 'quality-gate.json'),
267
- JSON.stringify({
268
- enabled: true,
269
- severity: 'warn',
270
- rules: [
271
- { id: 'line-count-limit', enabled: true, severity: 'error' }
272
- ]
273
- })
274
- );
275
-
276
- const gate = new QualityGate({ projectDir: tempDir, reporters: [] });
277
- const rules = gate._getActiveRules();
278
-
279
- expect(rules.length).toBeGreaterThan(0);
280
-
281
- fs.rmSync(configDir, { recursive: true, force: true });
282
- });
283
-
284
- it('should use default config when file doesn\'t exist', () => {
285
- const gate = new QualityGate({ projectDir: tempDir, reporters: [] });
286
- const rules = gate._getActiveRules();
287
-
288
- expect(rules.length).toBeGreaterThan(0);
289
- });
290
- });
291
- });
292
-
293
- describe('ConsoleReporter', () => {
294
- let reporter;
295
- let consoleSpy;
296
-
297
- beforeEach(() => {
298
- reporter = new ConsoleReporter();
299
- consoleSpy = jest.spyOn(console, 'log').mockImplementation();
300
- });
301
-
302
- afterEach(() => {
303
- consoleSpy.mockRestore();
304
- });
305
-
306
- it('should report pass status', () => {
307
- const result = {
308
- passed: true,
309
- summary: {
310
- filesChecked: 10,
311
- total: 0,
312
- critical: 0,
313
- error: 0,
314
- warn: 0
315
- },
316
- results: []
317
- };
318
-
319
- reporter.report(result);
320
-
321
- expect(consoleSpy).toHaveBeenCalledWith(
322
- expect.stringContaining('PASS')
323
- );
324
- });
325
-
326
- it('should report fail status', () => {
327
- const result = {
328
- passed: false,
329
- summary: {
330
- filesChecked: 10,
331
- total: 5,
332
- critical: 1,
333
- error: 2,
334
- warn: 2
335
- },
336
- results: [
337
- {
338
- file: '/path/to/file.js',
339
- ruleName: 'Test Rule',
340
- severity: 'error',
341
- message: 'Test error',
342
- fix: 'Fix it'
343
- }
344
- ]
345
- };
346
-
347
- reporter.report(result);
348
-
349
- expect(consoleSpy).toHaveBeenCalledWith(
350
- expect.stringContaining('FAIL')
351
- );
352
- expect(consoleSpy).toHaveBeenCalledWith(
353
- expect.stringContaining('Test Rule')
354
- );
355
- });
356
-
357
- it('should display file paths', () => {
358
- const result = {
359
- passed: false,
360
- summary: {
361
- filesChecked: 1,
362
- total: 1,
363
- critical: 0,
364
- error: 1,
365
- warn: 0
366
- },
367
- results: [
368
- {
369
- file: path.join(tempDir, 'test.js'),
370
- ruleName: 'Line Count',
371
- severity: 'error',
372
- message: 'Too many lines',
373
- pass: false
374
- }
375
- ]
376
- };
377
-
378
- reporter.report(result);
379
-
380
- expect(consoleSpy).toHaveBeenCalledWith(
381
- expect.stringContaining('test.js')
382
- );
383
- });
384
-
385
- it('should show fix suggestions', () => {
386
- const result = {
387
- passed: false,
388
- summary: {
389
- filesChecked: 1,
390
- total: 1,
391
- critical: 0,
392
- error: 1,
393
- warn: 0
394
- },
395
- results: [
396
- {
397
- file: '/path/to/file.js',
398
- ruleName: 'Test Rule',
399
- severity: 'warn',
400
- message: 'Test warning',
401
- pass: false,
402
- fix: 'Apply this fix'
403
- }
404
- ]
405
- };
406
-
407
- reporter.report(result);
408
-
409
- expect(consoleSpy).toHaveBeenCalledWith(
410
- expect.stringContaining('Fix:')
411
- );
412
- expect(consoleSpy).toHaveBeenCalledWith(
413
- expect.stringContaining('Apply this fix')
414
- );
415
- });
416
- });
417
-
418
- describe('JsonReporter', () => {
419
- let reporter;
420
- let consoleSpy;
421
-
422
- beforeEach(() => {
423
- reporter = new JsonReporter();
424
- consoleSpy = jest.spyOn(console, 'log').mockImplementation();
425
- });
426
-
427
- afterEach(() => {
428
- consoleSpy.mockRestore();
429
- });
430
-
431
- it('should output JSON', () => {
432
- const result = {
433
- passed: true,
434
- summary: { filesChecked: 5, total: 0 },
435
- results: []
436
- };
437
-
438
- reporter.report(result);
439
-
440
- const output = consoleSpy.mock.calls[0][0];
441
-
442
- expect(() => JSON.parse(output)).not.toThrow();
443
- });
444
-
445
- it('should include all result data', () => {
446
- const result = {
447
- passed: false,
448
- summary: {
449
- filesChecked: 1,
450
- total: 1,
451
- critical: 0,
452
- error: 1,
453
- warn: 0
454
- },
455
- results: [
456
- {
457
- file: '/test.js',
458
- rule: 'test-rule',
459
- ruleName: 'Test Rule',
460
- severity: 'error',
461
- message: 'Test error',
462
- pass: false
463
- }
464
- ]
465
- };
466
-
467
- reporter.report(result);
468
-
469
- const output = JSON.parse(consoleSpy.mock.calls[0][0]);
470
-
471
- expect(output.passed).toBe(false);
472
- expect(output.summary).toBeDefined();
473
- expect(output.results).toHaveLength(1);
474
- });
475
- });
476
-
477
- describe('MarkdownReporter', () => {
478
- let reporter;
479
- let consoleSpy;
480
-
481
- beforeEach(() => {
482
- reporter = new MarkdownReporter();
483
- consoleSpy = jest.spyOn(console, 'log').mockImplementation();
484
- });
485
-
486
- afterEach(() => {
487
- consoleSpy.mockRestore();
488
- });
489
-
490
- it('should output markdown format', () => {
491
- const result = {
492
- passed: true,
493
- summary: {
494
- filesChecked: 5,
495
- total: 0,
496
- critical: 0,
497
- error: 0,
498
- warn: 0
499
- },
500
- results: []
501
- };
502
-
503
- reporter.report(result);
504
-
505
- expect(consoleSpy).toHaveBeenCalledWith(
506
- expect.stringContaining('# Quality Gate Report')
507
- );
508
- });
509
-
510
- it('should show pass/fail status with emoji', () => {
511
- const passResult = {
512
- passed: true,
513
- summary: { filesChecked: 1, total: 0, critical: 0, error: 0, warn: 0 },
514
- results: []
515
- };
516
-
517
- reporter.report(passResult);
518
-
519
- expect(consoleSpy).toHaveBeenCalledWith(
520
- expect.stringContaining(':white_check_mark:')
521
- );
522
-
523
- jest.clearAllMocks();
524
-
525
- const failResult = {
526
- passed: false,
527
- summary: { filesChecked: 1, total: 1, critical: 0, error: 1, warn: 0 },
528
- results: [
529
- {
530
- file: '/test.js',
531
- ruleName: 'Test Rule',
532
- severity: 'error',
533
- message: 'Test error',
534
- pass: false
535
- }
536
- ]
537
- };
538
-
539
- reporter.report(failResult);
540
-
541
- expect(consoleSpy).toHaveBeenCalledWith(
542
- expect.stringContaining(':x:')
543
- );
544
- });
545
-
546
- it('should include summary section', () => {
547
- const result = {
548
- passed: true,
549
- summary: {
550
- filesChecked: 10,
551
- total: 5,
552
- critical: 0,
553
- error: 2,
554
- warn: 3
555
- },
556
- results: []
557
- };
558
-
559
- reporter.report(result);
560
-
561
- expect(consoleSpy).toHaveBeenCalledWith(
562
- expect.stringContaining('## Summary')
563
- );
564
- });
565
- });
566
-
567
- describe('checkOrThrow', () => {
568
- it('should return result when check passes', async () => {
569
- const result = await checkOrThrow({
570
- projectDir: tempDir,
571
- severity: 'critical'
572
- });
573
-
574
- expect(result).toBeDefined();
575
- expect(typeof result.passed).toBe('boolean');
576
- });
577
-
578
- it('should throw QualityGateError when check fails', async () => {
579
- // Create a gate that will definitely fail
580
- const failDir = path.join(os.tmpdir(), 'smc-fail-test-' + Date.now());
581
- fs.mkdirSync(failDir, { recursive: true });
582
-
583
- // Create a large file that will fail line-count-limit
584
- const largeFile = path.join(failDir, 'large.js');
585
- const lines = [];
586
- for (let i = 0; i < 900; i++) {
587
- lines.push(`const x = ${i};`);
588
- }
589
- fs.writeFileSync(largeFile, lines.join('\n'));
590
-
591
- await expect(async () => {
592
- await checkOrThrow({
593
- projectDir: failDir,
594
- severity: 'error'
595
- });
596
- }).rejects.toThrow(QualityGateError);
597
-
598
- fs.rmSync(failDir, { recursive: true, force: true });
599
- });
600
-
601
- it('should include result in thrown error', async () => {
602
- const failDir = path.join(os.tmpdir(), 'smc-fail-test2-' + Date.now());
603
- fs.mkdirSync(failDir, { recursive: true });
604
-
605
- const largeFile = path.join(failDir, 'large.js');
606
- const lines = [];
607
- for (let i = 0; i < 900; i++) {
608
- lines.push(`const x = ${i};`);
609
- }
610
- fs.writeFileSync(largeFile, lines.join('\n'));
611
-
612
- try {
613
- await checkOrThrow({
614
- projectDir: failDir,
615
- severity: 'error'
616
- });
617
- fail('Should have thrown');
618
- } catch (e) {
619
- expect(e instanceof QualityGateError).toBe(true);
620
- expect(e.results).toBeDefined();
621
- } finally {
622
- fs.rmSync(failDir, { recursive: true, force: true });
623
- }
624
- });
625
- });
626
-
627
- describe('Integration Tests', () => {
628
- it('should handle complete check workflow', async () => {
629
- const gate = new QualityGate({
630
- projectDir: tempDir,
631
- reporters: []
632
- });
633
-
634
- const result = await gate.check();
635
-
636
- expect(result).toMatchObject({
637
- passed: expect.any(Boolean),
638
- results: expect.any(Array),
639
- summary: expect.objectContaining({
640
- filesChecked: expect.any(Number),
641
- rulesRun: expect.any(Number),
642
- total: expect.any(Number)
643
- })
644
- });
645
- });
646
-
647
- it('should handle files with various issues', async () => {
648
- const gate = new QualityGate({
649
- projectDir: tempDir,
650
- reporters: []
651
- });
652
-
653
- const result = await gate.check();
654
-
655
- // Should find issues in dirty file
656
- const dirtyFileIssues = result.results.filter(r =>
657
- r.file === dirtyJsFile || r.file.endsWith(path.basename(dirtyJsFile))
658
- );
659
-
660
- expect(dirtyFileIssues.length).toBeGreaterThan(0);
661
- });
662
-
663
- it('should handle empty project directory', async () => {
664
- const emptyDir = path.join(os.tmpdir(), 'smc-empty-' + Date.now());
665
- fs.mkdirSync(emptyDir, { recursive: true });
666
-
667
- const gate = new QualityGate({
668
- projectDir: emptyDir,
669
- reporters: []
670
- });
671
-
672
- const result = await gate.check();
673
-
674
- expect(result.summary.filesChecked).toBe(0);
675
-
676
- fs.rmSync(emptyDir, { recursive: true, force: true });
677
- });
678
- });
679
- });