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,255 +0,0 @@
1
- /**
2
- * Permission Audit - Security scanner for approved commands
3
- * Inspired by cc-safe from ykdojo/claude-code-tips
4
- */
5
-
6
- const fs = require('fs');
7
- const path = require('path');
8
- const os = require('os');
9
-
10
- // Dangerous patterns with severity levels
11
- const DANGEROUS_PATTERNS = [
12
- // Critical - Must remove
13
- { pattern: /rm\s+-rf\s+\/(?!\w)/, level: 'critical', desc: 'Delete root directory' },
14
- { pattern: />\s*\/dev\/sd[a-z]/, level: 'critical', desc: 'Overwrite disk device' },
15
- { pattern: /mkfs/, level: 'critical', desc: 'Format disk' },
16
- { pattern: /dd\s+if=\/dev\/zero/, level: 'critical', desc: 'Overwrite with zeros' },
17
- { pattern: /:\(\)\s*\{\s*:\|:&\s*\}\s*;:/, level: 'critical', desc: 'Fork bomb' },
18
-
19
- // High - Should review
20
- { pattern: /sudo/, level: 'high', desc: 'Privilege escalation' },
21
- { pattern: /rm\s+-rf/, level: 'high', desc: 'Recursive force delete' },
22
- { pattern: /chmod\s+777/, level: 'high', desc: 'World-writable permissions' },
23
- { pattern: /--privileged/, level: 'high', desc: 'Privileged container' },
24
- { pattern: /curl.*\|\s*(sh|bash)/, level: 'high', desc: 'Remote script execution' },
25
- { pattern: /wget.*\|\s*(sh|bash)/, level: 'high', desc: 'Remote script execution' },
26
- { pattern: /eval\s/, level: 'high', desc: 'Dynamic code execution' },
27
- { pattern: /--no-verify/, level: 'high', desc: 'Skip verification hooks' },
28
-
29
- // Medium - Optional review
30
- { pattern: /npm\s+install\s+-g/, level: 'medium', desc: 'Global npm install' },
31
- { pattern: /pip\s+install/, level: 'medium', desc: 'Python package install' },
32
- { pattern: /git\s+push\s+--force/, level: 'medium', desc: 'Force push' },
33
- { pattern: /git\s+reset\s+--hard/, level: 'medium', desc: 'Hard reset' },
34
- { pattern: /DROP\s+(TABLE|DATABASE)/i, level: 'medium', desc: 'Drop database objects' },
35
- { pattern: /TRUNCATE/i, level: 'medium', desc: 'Truncate table' },
36
- ];
37
-
38
- /**
39
- * Find all settings files to scan
40
- */
41
- function findSettingsFiles(projectDir) {
42
- const files = [];
43
- const homeDir = os.homedir();
44
-
45
- // Global settings
46
- const globalSettings = path.join(homeDir, '.claude', 'settings.local.json');
47
- if (fs.existsSync(globalSettings)) {
48
- files.push({ path: globalSettings, scope: 'global' });
49
- }
50
-
51
- // Project settings
52
- if (projectDir) {
53
- const projectSettings = path.join(projectDir, '.claude', 'settings.local.json');
54
- if (fs.existsSync(projectSettings)) {
55
- files.push({ path: projectSettings, scope: 'project' });
56
- }
57
- }
58
-
59
- // All project settings in ~/.claude/projects/
60
- const projectsDir = path.join(homeDir, '.claude', 'projects');
61
- if (fs.existsSync(projectsDir)) {
62
- try {
63
- const projects = fs.readdirSync(projectsDir);
64
- for (const proj of projects) {
65
- const projSettings = path.join(projectsDir, proj, 'settings.local.json');
66
- if (fs.existsSync(projSettings)) {
67
- files.push({ path: projSettings, scope: `project:${proj}` });
68
- }
69
- }
70
- } catch (e) {
71
- // Ignore read errors
72
- }
73
- }
74
-
75
- return files;
76
- }
77
-
78
- /**
79
- * Extract permissions from settings file
80
- */
81
- function extractPermissions(settingsPath) {
82
- try {
83
- const content = fs.readFileSync(settingsPath, 'utf-8');
84
- const settings = JSON.parse(content);
85
-
86
- const permissions = [];
87
-
88
- // Check permissions.allow array
89
- if (settings.permissions?.allow) {
90
- permissions.push(...settings.permissions.allow);
91
- }
92
-
93
- // Check allowedTools (older format)
94
- if (settings.allowedTools) {
95
- permissions.push(...settings.allowedTools);
96
- }
97
-
98
- return permissions;
99
- } catch (e) {
100
- return [];
101
- }
102
- }
103
-
104
- /**
105
- * Scan a permission string for dangerous patterns
106
- */
107
- function scanPermission(permission) {
108
- const issues = [];
109
-
110
- for (const { pattern, level, desc } of DANGEROUS_PATTERNS) {
111
- if (pattern.test(permission)) {
112
- issues.push({ level, desc, pattern: pattern.toString(), match: permission });
113
- }
114
- }
115
-
116
- return issues;
117
- }
118
-
119
- /**
120
- * Run full audit
121
- */
122
- function audit(options = {}) {
123
- const { projectDir = process.cwd(), global: scanGlobal = true } = options;
124
-
125
- const results = {
126
- scanned: 0,
127
- issues: {
128
- critical: [],
129
- high: [],
130
- medium: [],
131
- },
132
- files: [],
133
- };
134
-
135
- const files = findSettingsFiles(scanGlobal ? projectDir : null);
136
- results.scanned = files.length;
137
- results.files = files.map(f => f.path);
138
-
139
- for (const { path: filePath, scope } of files) {
140
- const permissions = extractPermissions(filePath);
141
-
142
- for (const perm of permissions) {
143
- const issues = scanPermission(perm);
144
-
145
- for (const issue of issues) {
146
- const entry = {
147
- ...issue,
148
- file: filePath,
149
- scope,
150
- permission: perm,
151
- };
152
-
153
- results.issues[issue.level].push(entry);
154
- }
155
- }
156
- }
157
-
158
- return results;
159
- }
160
-
161
- /**
162
- * Generate markdown report
163
- */
164
- function generateReport(results) {
165
- const { scanned, issues, files } = results;
166
- const totalIssues = issues.critical.length + issues.high.length + issues.medium.length;
167
-
168
- let report = `# Permission Audit Report
169
-
170
- **Date**: ${new Date().toISOString().split('T')[0]}
171
- **Scanned**: ${scanned} files
172
- **Issues**: ${totalIssues} found
173
-
174
- `;
175
-
176
- // Critical issues
177
- report += `## 🔴 Critical Issues (${issues.critical.length})\n\n`;
178
- if (issues.critical.length === 0) {
179
- report += 'None found.\n\n';
180
- } else {
181
- issues.critical.forEach((issue, i) => {
182
- report += `### ${i + 1}. ${issue.desc}
183
- **Location**: ${issue.file}
184
- **Pattern**: \`${issue.permission}\`
185
- **Risk**: ${issue.desc}
186
-
187
- `;
188
- });
189
- }
190
-
191
- // High risk
192
- report += `## 🟠 High Risk (${issues.high.length})\n\n`;
193
- if (issues.high.length === 0) {
194
- report += 'None found.\n\n';
195
- } else {
196
- issues.high.forEach((issue, i) => {
197
- report += `### ${i + 1}. ${issue.desc}
198
- **Location**: ${issue.file}
199
- **Pattern**: \`${issue.permission}\`
200
- **Risk**: ${issue.desc}
201
-
202
- `;
203
- });
204
- }
205
-
206
- // Medium risk
207
- report += `## 🟡 Medium Risk (${issues.medium.length})\n\n`;
208
- if (issues.medium.length === 0) {
209
- report += 'None found.\n\n';
210
- } else {
211
- issues.medium.forEach((issue, i) => {
212
- report += `### ${i + 1}. ${issue.desc}
213
- **Location**: ${issue.file}
214
- **Pattern**: \`${issue.permission}\`
215
-
216
- `;
217
- });
218
- }
219
-
220
- // Summary
221
- report += `## Summary
222
-
223
- | Level | Count | Action |
224
- |-------|-------|--------|
225
- | 🔴 Critical | ${issues.critical.length} | Must remove |
226
- | 🟠 High | ${issues.high.length} | Should review |
227
- | 🟡 Medium | ${issues.medium.length} | Optional review |
228
-
229
- `;
230
-
231
- return report;
232
- }
233
-
234
- /**
235
- * Check if audit passes (for CI)
236
- */
237
- function passes(results, options = {}) {
238
- const { allowMedium = true, allowHigh = false } = options;
239
-
240
- if (results.issues.critical.length > 0) return false;
241
- if (!allowHigh && results.issues.high.length > 0) return false;
242
- if (!allowMedium && results.issues.medium.length > 0) return false;
243
-
244
- return true;
245
- }
246
-
247
- module.exports = {
248
- audit,
249
- generateReport,
250
- passes,
251
- DANGEROUS_PATTERNS,
252
- findSettingsFiles,
253
- extractPermissions,
254
- scanPermission,
255
- };
@@ -1,431 +0,0 @@
1
- /**
2
- * Quality Gate Implementation
3
- *
4
- * Multi-level validation engine with pluggable rules.
5
- * Supports multiple output formats and gate enforcement.
6
- *
7
- * @module lib/quality-gate
8
- */
9
-
10
- const fs = require('fs');
11
- const path = require('path');
12
- const { registry } = require('./quality-rules');
13
- const { QualityGateError } = require('./errors');
14
-
15
- /**
16
- * Quality Gate class
17
- */
18
- class QualityGate {
19
- /**
20
- * @param {Object} options - Gate options
21
- * @param {string} options.projectDir - Project directory
22
- * @param {Object} options.config - Gate configuration
23
- * @param {Array} options.reporters - Output reporters
24
- */
25
- constructor(options = {}) {
26
- this.projectDir = options.projectDir || process.cwd();
27
- this.config = options.config || this._loadConfig();
28
- this.reporters = options.reporters || [new ConsoleReporter()];
29
- }
30
-
31
- /**
32
- * Run quality gate check
33
- * @param {Object} options - Check options
34
- * @returns {Promise<Object>} Check results
35
- */
36
- async check(options = {}) {
37
- const {
38
- files = null,
39
- severity = this.config.severity || 'warn',
40
- rules = null,
41
- fix = false
42
- } = options;
43
-
44
- const filesToCheck = files || this._getProjectFiles();
45
- const rulesToRun = rules || this._getActiveRules();
46
-
47
- const results = [];
48
- let criticalCount = 0;
49
- let errorCount = 0;
50
- let warnCount = 0;
51
- let infoCount = 0;
52
-
53
- for (const file of filesToCheck) {
54
- const fileResults = await this._checkFile(file, rulesToRun);
55
- results.push(...fileResults);
56
-
57
- for (const r of fileResults) {
58
- switch (r.severity) {
59
- case 'critical': criticalCount++; break;
60
- case 'error': errorCount++; break;
61
- case 'warn': warnCount++; break;
62
- case 'info': infoCount++; break;
63
- }
64
- }
65
- }
66
-
67
- const summary = {
68
- total: results.length,
69
- critical: criticalCount,
70
- error: errorCount,
71
- warn: warnCount,
72
- info: infoCount,
73
- filesChecked: filesToCheck.length,
74
- rulesRun: rulesToRun.length
75
- };
76
-
77
- // Apply fixes if requested
78
- let fixedCount = 0;
79
- if (fix) {
80
- fixedCount = await this._applyFixes(results.filter(r => r.autoFix));
81
- summary.fixed = fixedCount;
82
- }
83
-
84
- // Determine if gate passes
85
- const minSeverity = this._severityLevel(severity);
86
- const passed = this._hasBlockingIssues(results, minSeverity) === false;
87
-
88
- const checkResult = {
89
- passed,
90
- results,
91
- summary
92
- };
93
-
94
- // Run reporters
95
- for (const reporter of this.reporters) {
96
- reporter.report(checkResult);
97
- }
98
-
99
- return checkResult;
100
- }
101
-
102
- /**
103
- * Check single file against all rules
104
- * @param {string} filePath - File path
105
- * @param {Array} rules - Rules to run
106
- * @returns {Promise<Array>} File check results
107
- */
108
- async _checkFile(filePath, rules) {
109
- const results = [];
110
-
111
- if (!fs.existsSync(filePath)) {
112
- return [{
113
- file: filePath,
114
- rule: 'file-exists',
115
- ruleName: 'File Exists',
116
- severity: 'error',
117
- message: 'File not found',
118
- pass: false
119
- }];
120
- }
121
-
122
- for (const rule of rules) {
123
- if (!rule.enabled) continue;
124
-
125
- try {
126
- const result = await rule.check(filePath, rule.config || {});
127
- results.push({
128
- file: filePath,
129
- rule: rule.id,
130
- ruleName: rule.name,
131
- severity: rule.severity,
132
- message: result.message,
133
- pass: result.pass,
134
- skip: result.skip || false,
135
- fix: result.fix || rule.fix,
136
- autoFix: result.autoFix || false,
137
- details: result.details
138
- });
139
- } catch (e) {
140
- results.push({
141
- file: filePath,
142
- rule: rule.id,
143
- ruleName: rule.name,
144
- severity: 'error',
145
- message: `Rule execution error: ${e.message}`,
146
- pass: false
147
- });
148
- }
149
- }
150
-
151
- return results;
152
- }
153
-
154
- /**
155
- * Apply automatic fixes
156
- * @param {Array} fixableResults - Results with autoFix flag
157
- * @returns {Promise<number>} Number of fixes applied
158
- */
159
- async _applyFixes(fixableResults) {
160
- let fixedCount = 0;
161
-
162
- for (const result of fixableResults) {
163
- try {
164
- const content = fs.readFileSync(result.file, 'utf-8');
165
- let fixed = content;
166
-
167
- // Trailing whitespace fix
168
- if (result.rule === 'no-trailing-whitespace') {
169
- fixed = content.replace(/[ \t]+$/gm, '');
170
- }
171
-
172
- if (fixed !== content) {
173
- fs.writeFileSync(result.file, fixed, 'utf-8');
174
- fixedCount++;
175
- }
176
- } catch (e) {
177
- // Skip files that can't be fixed
178
- }
179
- }
180
-
181
- return fixedCount;
182
- }
183
-
184
- /**
185
- * Get all project files
186
- * @returns {Array<string>} File paths
187
- */
188
- _getProjectFiles() {
189
- const files = [];
190
- const ignoreDirs = new Set([
191
- 'node_modules', '.git', 'dist', 'build', '.next',
192
- 'coverage', '.nyc_output', '.cache', 'vendor'
193
- ]);
194
- const checkExts = new Set([
195
- '.js', '.ts', '.jsx', '.tsx', '.cjs', '.mjs',
196
- '.json', '.md', '.py', '.go', '.rs'
197
- ]);
198
-
199
- const scanDir = (dir, depth = 0) => {
200
- if (depth > 10) return; // Max depth limit
201
-
202
- try {
203
- const entries = fs.readdirSync(dir, { withFileTypes: true });
204
-
205
- for (const entry of entries) {
206
- const fullPath = path.join(dir, entry.name);
207
-
208
- if (entry.isDirectory()) {
209
- if (!ignoreDirs.has(entry.name)) {
210
- scanDir(fullPath, depth + 1);
211
- }
212
- } else if (entry.isFile()) {
213
- const ext = path.extname(entry.name);
214
- if (checkExts.has(ext)) {
215
- files.push(fullPath);
216
- }
217
- }
218
- }
219
- } catch (e) {
220
- // Skip directories we can't read
221
- }
222
- };
223
-
224
- scanDir(this.projectDir);
225
- return files;
226
- }
227
-
228
- /**
229
- * Get active rules from config
230
- * @returns {Array} Active rules
231
- */
232
- _getActiveRules() {
233
- const rules = registry.getAll({ enabled: true });
234
- const configured = this.config.rules || [];
235
-
236
- // Apply config overrides
237
- for (const override of configured) {
238
- const rule = registry.get(override.id);
239
- if (rule) {
240
- if (override.enabled !== undefined) {
241
- rule.enabled = override.enabled;
242
- }
243
- if (override.severity) {
244
- rule.severity = override.severity;
245
- }
246
- if (override.config) {
247
- rule.config = { ...rule.config, ...override.config };
248
- }
249
- }
250
- }
251
-
252
- return rules.filter(r => r.enabled);
253
- }
254
-
255
- /**
256
- * Load quality gate config
257
- * @returns {Object} Configuration
258
- */
259
- _loadConfig() {
260
- const configPath = path.join(this.projectDir, '.claude', 'quality-gate.json');
261
-
262
- if (fs.existsSync(configPath)) {
263
- try {
264
- return JSON.parse(fs.readFileSync(configPath, 'utf-8'));
265
- } catch {
266
- // Fall back to default
267
- }
268
- }
269
-
270
- // Default config
271
- return {
272
- enabled: true,
273
- severity: 'warn',
274
- rules: [
275
- { id: 'line-count-limit', enabled: true, severity: 'error' },
276
- { id: 'file-size-limit', enabled: true, severity: 'warn' },
277
- { id: 'no-empty-files', enabled: true, severity: 'warn' },
278
- { id: 'no-trailing-whitespace', enabled: true, severity: 'warn' }
279
- ],
280
- gates: {
281
- preCommit: true,
282
- prePush: true,
283
- onToolUse: false
284
- },
285
- reporting: {
286
- format: 'console'
287
- }
288
- };
289
- }
290
-
291
- /**
292
- * Convert severity string to number
293
- * @param {string} severity - Severity level
294
- * @returns {number} Numeric level
295
- */
296
- _severityLevel(severity) {
297
- const levels = { info: 0, warn: 1, error: 2, critical: 3 };
298
- return levels[severity] || 1;
299
- }
300
-
301
- /**
302
- * Check if results contain blocking issues
303
- * @param {Array} results - Check results
304
- * @param {number} minSeverity - Minimum blocking severity
305
- * @returns {boolean} Has blocking issues
306
- */
307
- _hasBlockingIssues(results, minSeverity) {
308
- return results.some(r =>
309
- !r.pass &&
310
- !r.skip &&
311
- this._severityLevel(r.severity) >= minSeverity
312
- );
313
- }
314
- }
315
-
316
- /**
317
- * Console reporter
318
- */
319
- class ConsoleReporter {
320
- report(checkResult) {
321
- const { passed, results, summary } = checkResult;
322
-
323
- console.log('\n' + '='.repeat(60));
324
- console.log('Quality Gate Report');
325
- console.log('='.repeat(60));
326
- console.log(`Status: ${passed ? 'PASS' : 'FAIL'}`);
327
- console.log(`Files: ${summary.filesChecked}`);
328
- console.log(`Issues: ${summary.total} (${summary.critical} critical, ${summary.error} errors, ${summary.warn} warnings)`);
329
- console.log('='.repeat(60));
330
-
331
- // Group by file
332
- const byFile = {};
333
- for (const result of results) {
334
- if (!result.pass && !result.skip) {
335
- const relPath = path.relative(process.cwd(), result.file);
336
- if (!byFile[relPath]) byFile[relPath] = [];
337
- byFile[relPath].push(result);
338
- }
339
- }
340
-
341
- for (const [file, issues] of Object.entries(byFile)) {
342
- console.log(`\n${file}:`);
343
- for (const issue of issues) {
344
- const icon = { critical: 'X', error: 'E', warn: 'W', info: 'I' }[issue.severity];
345
- console.log(` [${icon}] ${issue.ruleName}: ${issue.message}`);
346
- if (issue.fix) {
347
- console.log(` Fix: ${issue.fix}`);
348
- }
349
- }
350
- }
351
-
352
- console.log('\n' + '='.repeat(60) + '\n');
353
- }
354
- }
355
-
356
- /**
357
- * JSON reporter
358
- */
359
- class JsonReporter {
360
- report(checkResult) {
361
- console.log(JSON.stringify(checkResult, null, 2));
362
- }
363
- }
364
-
365
- /**
366
- * Markdown reporter
367
- */
368
- class MarkdownReporter {
369
- report(checkResult) {
370
- const { passed, results, summary } = checkResult;
371
-
372
- let output = `# Quality Gate Report\n\n`;
373
- output += `**Status**: ${passed ? 'PASS :white_check_mark:' : 'FAIL :x:'}\n\n`;
374
- output += `## Summary\n\n`;
375
- output += `- Files: ${summary.filesChecked}\n`;
376
- output += `- Issues: ${summary.total}\n`;
377
- output += ` - Critical: ${summary.critical}\n`;
378
- output += ` - Errors: ${summary.error}\n`;
379
- output += ` - Warnings: ${summary.warn}\n\n`;
380
-
381
- if (results.length > 0) {
382
- output += `## Issues\n\n`;
383
- const byFile = {};
384
- for (const result of results.filter(r => !r.pass && !r.skip)) {
385
- const relPath = path.relative(process.cwd(), result.file);
386
- if (!byFile[relPath]) byFile[relPath] = [];
387
- byFile[relPath].push(result);
388
- }
389
-
390
- for (const [file, issues] of Object.entries(byFile)) {
391
- output += `### ${file}\n`;
392
- for (const issue of issues) {
393
- output += `- **${issue.ruleName}** (${issue.severity}): ${issue.message}\n`;
394
- if (issue.fix) {
395
- output += ` - Fix: ${issue.fix}\n`;
396
- }
397
- }
398
- output += '\n';
399
- }
400
- }
401
-
402
- console.log(output);
403
- }
404
- }
405
-
406
- /**
407
- * Check quality gate and throw if failed
408
- * @param {Object} options - Check options
409
- * @throws {QualityGateError} If check fails
410
- */
411
- async function checkOrThrow(options = {}) {
412
- const gate = new QualityGate(options);
413
- const result = await gate.check(options);
414
-
415
- if (!result.passed) {
416
- throw new QualityGateError(
417
- 'Quality gate check failed',
418
- result
419
- );
420
- }
421
-
422
- return result;
423
- }
424
-
425
- module.exports = {
426
- QualityGate,
427
- ConsoleReporter,
428
- JsonReporter,
429
- MarkdownReporter,
430
- checkOrThrow
431
- };