vibe-and-thrive 1.0.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 (145) hide show
  1. package/.claude/commands/add-tests.md +240 -0
  2. package/.claude/commands/e2e-scaffold.md +212 -0
  3. package/.claude/commands/explain.md +110 -0
  4. package/.claude/commands/fix-types.md +238 -0
  5. package/.claude/commands/refactor.md +184 -0
  6. package/.claude/commands/review.md +136 -0
  7. package/.claude/commands/security-check.md +223 -0
  8. package/.claude/commands/styleguide.md +446 -0
  9. package/.claude/commands/tdd-feature.md +227 -0
  10. package/.claude/commands/vibe-check.md +112 -0
  11. package/.pre-commit-hooks.yaml +77 -0
  12. package/LICENSE +21 -0
  13. package/README.md +167 -0
  14. package/bin/vibe-check.js +19 -0
  15. package/dist/cli.d.ts +13 -0
  16. package/dist/cli.d.ts.map +1 -0
  17. package/dist/cli.js +206 -0
  18. package/dist/cli.js.map +1 -0
  19. package/dist/eslint-plugin/index.d.ts +66 -0
  20. package/dist/eslint-plugin/index.d.ts.map +1 -0
  21. package/dist/eslint-plugin/index.js +67 -0
  22. package/dist/eslint-plugin/index.js.map +1 -0
  23. package/dist/eslint-plugin/rules/max-function-length.d.ts +8 -0
  24. package/dist/eslint-plugin/rules/max-function-length.d.ts.map +1 -0
  25. package/dist/eslint-plugin/rules/max-function-length.js +69 -0
  26. package/dist/eslint-plugin/rules/max-function-length.js.map +1 -0
  27. package/dist/eslint-plugin/rules/no-any-type.d.ts +8 -0
  28. package/dist/eslint-plugin/rules/no-any-type.d.ts.map +1 -0
  29. package/dist/eslint-plugin/rules/no-any-type.js +29 -0
  30. package/dist/eslint-plugin/rules/no-any-type.js.map +1 -0
  31. package/dist/eslint-plugin/rules/no-debug-statements.d.ts +8 -0
  32. package/dist/eslint-plugin/rules/no-debug-statements.d.ts.map +1 -0
  33. package/dist/eslint-plugin/rules/no-debug-statements.js +59 -0
  34. package/dist/eslint-plugin/rules/no-debug-statements.js.map +1 -0
  35. package/dist/eslint-plugin/rules/no-deep-nesting.d.ts +8 -0
  36. package/dist/eslint-plugin/rules/no-deep-nesting.d.ts.map +1 -0
  37. package/dist/eslint-plugin/rules/no-deep-nesting.js +56 -0
  38. package/dist/eslint-plugin/rules/no-deep-nesting.js.map +1 -0
  39. package/dist/eslint-plugin/rules/no-empty-catch.d.ts +8 -0
  40. package/dist/eslint-plugin/rules/no-empty-catch.d.ts.map +1 -0
  41. package/dist/eslint-plugin/rules/no-empty-catch.js +31 -0
  42. package/dist/eslint-plugin/rules/no-empty-catch.js.map +1 -0
  43. package/dist/eslint-plugin/rules/no-magic-numbers.d.ts +8 -0
  44. package/dist/eslint-plugin/rules/no-magic-numbers.d.ts.map +1 -0
  45. package/dist/eslint-plugin/rules/no-magic-numbers.js +58 -0
  46. package/dist/eslint-plugin/rules/no-magic-numbers.js.map +1 -0
  47. package/dist/eslint-plugin/rules/no-snake-case-props.d.ts +8 -0
  48. package/dist/eslint-plugin/rules/no-snake-case-props.d.ts.map +1 -0
  49. package/dist/eslint-plugin/rules/no-snake-case-props.js +48 -0
  50. package/dist/eslint-plugin/rules/no-snake-case-props.js.map +1 -0
  51. package/dist/hooks/check-any-types.d.ts +6 -0
  52. package/dist/hooks/check-any-types.d.ts.map +1 -0
  53. package/dist/hooks/check-any-types.js +73 -0
  54. package/dist/hooks/check-any-types.js.map +1 -0
  55. package/dist/hooks/check-commented-code.d.ts +6 -0
  56. package/dist/hooks/check-commented-code.d.ts.map +1 -0
  57. package/dist/hooks/check-commented-code.js +81 -0
  58. package/dist/hooks/check-commented-code.js.map +1 -0
  59. package/dist/hooks/check-console-error.d.ts +6 -0
  60. package/dist/hooks/check-console-error.d.ts.map +1 -0
  61. package/dist/hooks/check-console-error.js +41 -0
  62. package/dist/hooks/check-console-error.js.map +1 -0
  63. package/dist/hooks/check-debug-statements.d.ts +6 -0
  64. package/dist/hooks/check-debug-statements.d.ts.map +1 -0
  65. package/dist/hooks/check-debug-statements.js +120 -0
  66. package/dist/hooks/check-debug-statements.js.map +1 -0
  67. package/dist/hooks/check-deep-nesting.d.ts +6 -0
  68. package/dist/hooks/check-deep-nesting.d.ts.map +1 -0
  69. package/dist/hooks/check-deep-nesting.js +116 -0
  70. package/dist/hooks/check-deep-nesting.js.map +1 -0
  71. package/dist/hooks/check-docker-platform.d.ts +6 -0
  72. package/dist/hooks/check-docker-platform.d.ts.map +1 -0
  73. package/dist/hooks/check-docker-platform.js +42 -0
  74. package/dist/hooks/check-docker-platform.js.map +1 -0
  75. package/dist/hooks/check-dry-violations.d.ts +6 -0
  76. package/dist/hooks/check-dry-violations.d.ts.map +1 -0
  77. package/dist/hooks/check-dry-violations.js +124 -0
  78. package/dist/hooks/check-dry-violations.js.map +1 -0
  79. package/dist/hooks/check-empty-catch.d.ts +6 -0
  80. package/dist/hooks/check-empty-catch.d.ts.map +1 -0
  81. package/dist/hooks/check-empty-catch.js +111 -0
  82. package/dist/hooks/check-empty-catch.js.map +1 -0
  83. package/dist/hooks/check-function-length.d.ts +6 -0
  84. package/dist/hooks/check-function-length.d.ts.map +1 -0
  85. package/dist/hooks/check-function-length.js +152 -0
  86. package/dist/hooks/check-function-length.js.map +1 -0
  87. package/dist/hooks/check-hardcoded-urls.d.ts +6 -0
  88. package/dist/hooks/check-hardcoded-urls.d.ts.map +1 -0
  89. package/dist/hooks/check-hardcoded-urls.js +124 -0
  90. package/dist/hooks/check-hardcoded-urls.js.map +1 -0
  91. package/dist/hooks/check-magic-numbers.d.ts +6 -0
  92. package/dist/hooks/check-magic-numbers.d.ts.map +1 -0
  93. package/dist/hooks/check-magic-numbers.js +116 -0
  94. package/dist/hooks/check-magic-numbers.js.map +1 -0
  95. package/dist/hooks/check-secrets.d.ts +6 -0
  96. package/dist/hooks/check-secrets.d.ts.map +1 -0
  97. package/dist/hooks/check-secrets.js +138 -0
  98. package/dist/hooks/check-secrets.js.map +1 -0
  99. package/dist/hooks/check-snake-case-ts.d.ts +6 -0
  100. package/dist/hooks/check-snake-case-ts.d.ts.map +1 -0
  101. package/dist/hooks/check-snake-case-ts.js +78 -0
  102. package/dist/hooks/check-snake-case-ts.js.map +1 -0
  103. package/dist/hooks/check-todo-fixme.d.ts +6 -0
  104. package/dist/hooks/check-todo-fixme.d.ts.map +1 -0
  105. package/dist/hooks/check-todo-fixme.js +41 -0
  106. package/dist/hooks/check-todo-fixme.js.map +1 -0
  107. package/dist/hooks/check-unsafe-html.d.ts +6 -0
  108. package/dist/hooks/check-unsafe-html.d.ts.map +1 -0
  109. package/dist/hooks/check-unsafe-html.js +101 -0
  110. package/dist/hooks/check-unsafe-html.js.map +1 -0
  111. package/dist/hooks/index.d.ts +29 -0
  112. package/dist/hooks/index.d.ts.map +1 -0
  113. package/dist/hooks/index.js +54 -0
  114. package/dist/hooks/index.js.map +1 -0
  115. package/dist/index.d.ts +9 -0
  116. package/dist/index.d.ts.map +1 -0
  117. package/dist/index.js +10 -0
  118. package/dist/index.js.map +1 -0
  119. package/dist/lint-staged/config.d.ts +20 -0
  120. package/dist/lint-staged/config.d.ts.map +1 -0
  121. package/dist/lint-staged/config.js +27 -0
  122. package/dist/lint-staged/config.js.map +1 -0
  123. package/dist/utils/file-reader.d.ts +24 -0
  124. package/dist/utils/file-reader.d.ts.map +1 -0
  125. package/dist/utils/file-reader.js +140 -0
  126. package/dist/utils/file-reader.js.map +1 -0
  127. package/dist/utils/patterns.d.ts +27 -0
  128. package/dist/utils/patterns.d.ts.map +1 -0
  129. package/dist/utils/patterns.js +84 -0
  130. package/dist/utils/patterns.js.map +1 -0
  131. package/dist/utils/reporters.d.ts +21 -0
  132. package/dist/utils/reporters.d.ts.map +1 -0
  133. package/dist/utils/reporters.js +115 -0
  134. package/dist/utils/reporters.js.map +1 -0
  135. package/dist/utils/types.d.ts +71 -0
  136. package/dist/utils/types.d.ts.map +1 -0
  137. package/dist/utils/types.js +5 -0
  138. package/dist/utils/types.js.map +1 -0
  139. package/integrations/cursorrules.template +147 -0
  140. package/integrations/eslint.config.js +34 -0
  141. package/integrations/lint-staged.config.js +34 -0
  142. package/integrations/ruff.toml +125 -0
  143. package/integrations/vibe-check.yml +116 -0
  144. package/integrations/vscode-settings.json +127 -0
  145. package/package.json +81 -0
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Check for hardcoded URLs that should use environment variables
3
+ */
4
+ // Common CDN and safe domains to ignore
5
+ const SAFE_DOMAINS = new Set([
6
+ 'cdn.jsdelivr.net',
7
+ 'cdnjs.cloudflare.com',
8
+ 'unpkg.com',
9
+ 'fonts.googleapis.com',
10
+ 'fonts.gstatic.com',
11
+ 'fonts.bunny.net',
12
+ 'api.github.com',
13
+ 'raw.githubusercontent.com',
14
+ 'registry.npmjs.org',
15
+ 'pypi.org',
16
+ 'schema.org',
17
+ 'www.w3.org',
18
+ 'example.com',
19
+ 'example.org',
20
+ ]);
21
+ // Patterns that indicate this is likely test/example code
22
+ const TEST_PATTERNS = [
23
+ /\.test\./,
24
+ /\.spec\./,
25
+ /\/__tests__\//,
26
+ /\/test\//,
27
+ /\/fixtures?\//,
28
+ /\.mock\./,
29
+ ];
30
+ export const checkHardcodedUrls = {
31
+ id: 'urls',
32
+ name: 'Check Hardcoded URLs',
33
+ description: 'Detect hardcoded URLs that should use environment variables',
34
+ severity: 'error',
35
+ fileTypes: ['js', 'jsx', 'ts', 'tsx', 'mjs', 'cjs', 'py'],
36
+ check(context) {
37
+ const results = [];
38
+ const lines = context.content.split('\n');
39
+ // Skip test files
40
+ if (TEST_PATTERNS.some((p) => p.test(context.filePath))) {
41
+ return results;
42
+ }
43
+ for (let i = 0; i < lines.length; i++) {
44
+ const line = lines[i];
45
+ const lineNum = i + 1;
46
+ // Skip comments
47
+ if (isCommentLine(line, context.extension)) {
48
+ continue;
49
+ }
50
+ // Check for localhost URLs
51
+ const localhostMatches = line.matchAll(/https?:\/\/localhost(?::\d+)?(?:\/[^\s'")\]]*)?/g);
52
+ for (const match of localhostMatches) {
53
+ // Skip if in a fallback/default pattern
54
+ if (isFallbackPattern(line, match.index || 0)) {
55
+ continue;
56
+ }
57
+ results.push({
58
+ line: lineNum,
59
+ column: match.index || 0,
60
+ message: 'Hardcoded localhost URL - use environment variable',
61
+ severity: 'warning',
62
+ ruleId: 'urls/localhost',
63
+ fix: 'Use process.env.API_URL or similar',
64
+ });
65
+ }
66
+ // Check for 127.0.0.1 URLs
67
+ const ipMatches = line.matchAll(/https?:\/\/127\.0\.0\.1(?::\d+)?(?:\/[^\s'")\]]*)?/g);
68
+ for (const match of ipMatches) {
69
+ if (isFallbackPattern(line, match.index || 0)) {
70
+ continue;
71
+ }
72
+ results.push({
73
+ line: lineNum,
74
+ column: match.index || 0,
75
+ message: 'Hardcoded localhost IP - use environment variable',
76
+ severity: 'warning',
77
+ ruleId: 'urls/localhost-ip',
78
+ });
79
+ }
80
+ // Check for production URLs
81
+ const urlMatches = line.matchAll(/https?:\/\/([a-zA-Z0-9][a-zA-Z0-9-]*\.[a-zA-Z]{2,}(?:\.[a-zA-Z]{2,})?)(?::\d+)?(?:\/[^\s'")\]]*)?/g);
82
+ for (const match of urlMatches) {
83
+ const domain = match[1].toLowerCase();
84
+ // Skip safe domains
85
+ if (SAFE_DOMAINS.has(domain)) {
86
+ continue;
87
+ }
88
+ // Skip if in a fallback pattern
89
+ if (isFallbackPattern(line, match.index || 0)) {
90
+ continue;
91
+ }
92
+ // Skip common documentation/example patterns
93
+ if (domain.includes('example') || domain.includes('placeholder')) {
94
+ continue;
95
+ }
96
+ // Flag as potential hardcoded production URL
97
+ results.push({
98
+ line: lineNum,
99
+ column: match.index || 0,
100
+ message: `Hardcoded URL (${domain}) - consider using environment variable`,
101
+ severity: 'warning',
102
+ ruleId: 'urls/hardcoded',
103
+ });
104
+ }
105
+ }
106
+ return results;
107
+ },
108
+ };
109
+ function isCommentLine(line, extension) {
110
+ const trimmed = line.trim();
111
+ if (['js', 'jsx', 'ts', 'tsx', 'mjs', 'cjs'].includes(extension)) {
112
+ return trimmed.startsWith('//') || trimmed.startsWith('*') || trimmed.startsWith('/*');
113
+ }
114
+ if (['py', 'pyw'].includes(extension)) {
115
+ return trimmed.startsWith('#');
116
+ }
117
+ return false;
118
+ }
119
+ function isFallbackPattern(line, urlIndex) {
120
+ // Check if URL appears after || or ?? or as a default value
121
+ const before = line.substring(0, urlIndex);
122
+ return /(\|\||\?\?|:\s*$|,\s*$|=\s*$|default[:\s]*$)/i.test(before.trim());
123
+ }
124
+ //# sourceMappingURL=check-hardcoded-urls.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-hardcoded-urls.js","sourceRoot":"","sources":["../../src/hooks/check-hardcoded-urls.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Check for magic numbers that should be named constants
3
+ */
4
+ import type { Hook } from '../utils/types.js';
5
+ export declare const checkMagicNumbers: Hook;
6
+ //# sourceMappingURL=check-magic-numbers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-magic-numbers.d.ts","sourceRoot":"","sources":["../../src/hooks/check-magic-numbers.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,IAAI,EAA2B,MAAM,mBAAmB,CAAC;AA0CvE,eAAO,MAAM,iBAAiB,EAAE,IAkE/B,CAAC"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Check for magic numbers that should be named constants
3
+ */
4
+ // Numbers that are commonly acceptable and don't need constants
5
+ const ACCEPTABLE_NUMBERS = new Set([
6
+ -1, 0, 1, 2, 10, 100, 1000,
7
+ // Common time values
8
+ 60, 1000, 3600, 86400,
9
+ // Array/string indices
10
+ 0, 1, 2,
11
+ ]);
12
+ // Contexts where magic numbers are acceptable
13
+ const ACCEPTABLE_CONTEXTS = [
14
+ /\.length\s*[<>=!]/, // array length comparisons
15
+ /\[\s*\d+\s*\]/, // array indexing
16
+ /slice\s*\(\s*\d+/, // slice operations
17
+ /substring\s*\(\s*\d+/, // substring operations
18
+ /repeat\s*\(\s*\d+/, // repeat operations
19
+ /^\s*(?:return|throw)\s+\d+/, // return/throw numeric values
20
+ /port\s*[:=]/i, // port numbers
21
+ /\.toFixed\s*\(\s*\d+/, // decimal places
22
+ /Math\./, // Math operations
23
+ /parseInt|parseFloat/, // parsing functions
24
+ /0x[0-9a-fA-F]+/, // hex numbers (color codes, etc.)
25
+ /rgba?\s*\(/, // CSS colors
26
+ /version/i, // version numbers
27
+ /new Date\(/, // date constructors
28
+ /setTimeout|setInterval/, // timer functions (often have inline ms)
29
+ /padding|margin|width|height/i, // CSS-related
30
+ ];
31
+ // Paths that indicate frontend component files (skip these)
32
+ const FRONTEND_PATH_PATTERNS = [
33
+ /\/components\//i,
34
+ /\/pages\//i,
35
+ /\/views\//i,
36
+ /\/layouts\//i,
37
+ /\/ui\//i,
38
+ /\.styled\./i,
39
+ /\.styles\./i,
40
+ ];
41
+ export const checkMagicNumbers = {
42
+ id: 'magic-numbers',
43
+ name: 'Check Magic Numbers',
44
+ description: 'Detect magic numbers that should be named constants',
45
+ severity: 'warning',
46
+ // Skip JSX/TSX - too many false positives with CSS values
47
+ fileTypes: ['js', 'ts', 'mjs', 'cjs', 'py'],
48
+ check(context) {
49
+ const results = [];
50
+ // Skip frontend component files even if they're .js/.ts
51
+ if (FRONTEND_PATH_PATTERNS.some((pattern) => pattern.test(context.filePath))) {
52
+ return results;
53
+ }
54
+ const lines = context.content.split('\n');
55
+ for (let i = 0; i < lines.length; i++) {
56
+ const line = lines[i];
57
+ const lineNum = i + 1;
58
+ // Skip comments
59
+ if (isCommentLine(line, context.extension)) {
60
+ continue;
61
+ }
62
+ // Skip constant/variable declarations with descriptive names
63
+ if (isConstantDeclaration(line)) {
64
+ continue;
65
+ }
66
+ // Find all numbers in the line
67
+ const numberMatches = line.matchAll(/(?<![a-zA-Z_])(-?\d+(?:\.\d+)?)\b/g);
68
+ for (const match of numberMatches) {
69
+ const numStr = match[1];
70
+ const num = parseFloat(numStr);
71
+ // Skip acceptable numbers
72
+ if (ACCEPTABLE_NUMBERS.has(num)) {
73
+ continue;
74
+ }
75
+ // Skip if in acceptable context
76
+ if (ACCEPTABLE_CONTEXTS.some((pattern) => pattern.test(line))) {
77
+ continue;
78
+ }
79
+ // Skip very small numbers (likely not magic)
80
+ if (num >= -10 && num <= 10 && Number.isInteger(num)) {
81
+ continue;
82
+ }
83
+ results.push({
84
+ line: lineNum,
85
+ column: match.index || 0,
86
+ message: `Magic number ${numStr} - consider using a named constant`,
87
+ severity: 'warning',
88
+ ruleId: 'magic-numbers/unnamed',
89
+ });
90
+ }
91
+ }
92
+ return results;
93
+ },
94
+ };
95
+ function isCommentLine(line, extension) {
96
+ const trimmed = line.trim();
97
+ if (['js', 'jsx', 'ts', 'tsx', 'mjs', 'cjs'].includes(extension)) {
98
+ return trimmed.startsWith('//') || trimmed.startsWith('*') || trimmed.startsWith('/*');
99
+ }
100
+ if (['py', 'pyw'].includes(extension)) {
101
+ return trimmed.startsWith('#');
102
+ }
103
+ return false;
104
+ }
105
+ function isConstantDeclaration(line) {
106
+ // JavaScript/TypeScript const with UPPER_CASE name
107
+ if (/const\s+[A-Z][A-Z0-9_]+\s*=/.test(line)) {
108
+ return true;
109
+ }
110
+ // Python uppercase variable (convention for constants)
111
+ if (/^[A-Z][A-Z0-9_]+\s*=/.test(line.trim())) {
112
+ return true;
113
+ }
114
+ return false;
115
+ }
116
+ //# sourceMappingURL=check-magic-numbers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-magic-numbers.js","sourceRoot":"","sources":["../../src/hooks/check-magic-numbers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Check for hardcoded secrets, API keys, and tokens
3
+ */
4
+ import type { Hook } from '../utils/types.js';
5
+ export declare const checkSecrets: Hook;
6
+ //# sourceMappingURL=check-secrets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-secrets.d.ts","sourceRoot":"","sources":["../../src/hooks/check-secrets.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,IAAI,EAA2B,MAAM,mBAAmB,CAAC;AAGvE,eAAO,MAAM,YAAY,EAAE,IAkI1B,CAAC"}
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Check for hardcoded secrets, API keys, and tokens
3
+ */
4
+ import { SECRET_PATTERNS, isPlaceholder } from '../utils/patterns.js';
5
+ export const checkSecrets = {
6
+ id: 'secrets',
7
+ name: 'Check Secrets',
8
+ description: 'Detect hardcoded API keys, passwords, and tokens',
9
+ severity: 'error',
10
+ fileTypes: ['js', 'jsx', 'ts', 'tsx', 'mjs', 'cjs', 'py', 'json', 'yaml', 'yml', 'toml', 'env'],
11
+ check(context) {
12
+ const results = [];
13
+ const lines = context.content.split('\n');
14
+ for (let i = 0; i < lines.length; i++) {
15
+ const line = lines[i];
16
+ const lineNum = i + 1;
17
+ // Skip comments
18
+ if (isCommentLine(line, context.extension)) {
19
+ continue;
20
+ }
21
+ // AWS Access Key
22
+ const awsMatch = line.match(SECRET_PATTERNS.awsAccessKey);
23
+ if (awsMatch && !isPlaceholder(awsMatch[0])) {
24
+ results.push({
25
+ line: lineNum,
26
+ column: line.indexOf(awsMatch[0]),
27
+ message: 'Possible AWS Access Key detected',
28
+ severity: 'error',
29
+ ruleId: 'secrets/aws-key',
30
+ });
31
+ }
32
+ // Stripe Key
33
+ const stripeMatch = line.match(SECRET_PATTERNS.stripeKey);
34
+ if (stripeMatch && !isPlaceholder(stripeMatch[0])) {
35
+ results.push({
36
+ line: lineNum,
37
+ column: line.indexOf(stripeMatch[0]),
38
+ message: 'Stripe API key detected',
39
+ severity: 'error',
40
+ ruleId: 'secrets/stripe-key',
41
+ });
42
+ }
43
+ // GitHub Token
44
+ const githubMatch = line.match(SECRET_PATTERNS.githubToken);
45
+ if (githubMatch && !isPlaceholder(githubMatch[0])) {
46
+ results.push({
47
+ line: lineNum,
48
+ column: line.indexOf(githubMatch[0]),
49
+ message: 'GitHub token detected',
50
+ severity: 'error',
51
+ ruleId: 'secrets/github-token',
52
+ });
53
+ }
54
+ // Slack Token
55
+ const slackMatch = line.match(SECRET_PATTERNS.slackToken);
56
+ if (slackMatch && !isPlaceholder(slackMatch[0])) {
57
+ results.push({
58
+ line: lineNum,
59
+ column: line.indexOf(slackMatch[0]),
60
+ message: 'Slack token detected',
61
+ severity: 'error',
62
+ ruleId: 'secrets/slack-token',
63
+ });
64
+ }
65
+ // SendGrid Key
66
+ const sendgridMatch = line.match(SECRET_PATTERNS.sendgridKey);
67
+ if (sendgridMatch && !isPlaceholder(sendgridMatch[0])) {
68
+ results.push({
69
+ line: lineNum,
70
+ column: line.indexOf(sendgridMatch[0]),
71
+ message: 'SendGrid API key detected',
72
+ severity: 'error',
73
+ ruleId: 'secrets/sendgrid-key',
74
+ });
75
+ }
76
+ // Private Key
77
+ if (SECRET_PATTERNS.privateKey.test(line)) {
78
+ results.push({
79
+ line: lineNum,
80
+ column: 0,
81
+ message: 'Private key detected - never commit private keys',
82
+ severity: 'error',
83
+ ruleId: 'secrets/private-key',
84
+ });
85
+ }
86
+ // Generic API key patterns
87
+ const genericApiMatch = line.match(SECRET_PATTERNS.genericApiKey);
88
+ if (genericApiMatch && !isPlaceholder(genericApiMatch[0])) {
89
+ // Only flag if it looks like a real key (long enough, not a placeholder)
90
+ const value = genericApiMatch[0];
91
+ if (value.length > 30) {
92
+ results.push({
93
+ line: lineNum,
94
+ column: line.indexOf(value),
95
+ message: 'Possible hardcoded API key - use environment variables',
96
+ severity: 'error',
97
+ ruleId: 'secrets/generic-api-key',
98
+ });
99
+ }
100
+ }
101
+ // Generic secrets (password, token, etc.)
102
+ const secretMatch = line.match(SECRET_PATTERNS.genericSecret);
103
+ if (secretMatch && !isPlaceholder(secretMatch[0])) {
104
+ // Skip obvious non-secrets
105
+ const value = secretMatch[0].toLowerCase();
106
+ if (!value.includes('password:') && // Not a type annotation
107
+ !value.includes('password =') && // Assignment with placeholder
108
+ value.length > 20) {
109
+ results.push({
110
+ line: lineNum,
111
+ column: line.indexOf(secretMatch[0]),
112
+ message: 'Possible hardcoded secret - use environment variables',
113
+ severity: 'warning',
114
+ ruleId: 'secrets/generic-secret',
115
+ });
116
+ }
117
+ }
118
+ }
119
+ return results;
120
+ },
121
+ };
122
+ function isCommentLine(line, extension) {
123
+ const trimmed = line.trim();
124
+ // JavaScript/TypeScript style comments
125
+ if (['js', 'jsx', 'ts', 'tsx', 'mjs', 'cjs'].includes(extension)) {
126
+ return trimmed.startsWith('//') || trimmed.startsWith('*') || trimmed.startsWith('/*');
127
+ }
128
+ // Python style comments
129
+ if (['py', 'pyw'].includes(extension)) {
130
+ return trimmed.startsWith('#');
131
+ }
132
+ // YAML style comments
133
+ if (['yaml', 'yml'].includes(extension)) {
134
+ return trimmed.startsWith('#');
135
+ }
136
+ return false;
137
+ }
138
+ //# sourceMappingURL=check-secrets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-secrets.js","sourceRoot":"","sources":["../../src/hooks/check-secrets.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Check for snake_case property names in TypeScript interfaces/types
3
+ */
4
+ import type { Hook } from '../utils/types.js';
5
+ export declare const checkSnakeCaseTs: Hook;
6
+ //# sourceMappingURL=check-snake-case-ts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-snake-case-ts.d.ts","sourceRoot":"","sources":["../../src/hooks/check-snake-case-ts.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,IAAI,EAA2B,MAAM,mBAAmB,CAAC;AAEvE,eAAO,MAAM,gBAAgB,EAAE,IAmF9B,CAAC"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Check for snake_case property names in TypeScript interfaces/types
3
+ */
4
+ export const checkSnakeCaseTs = {
5
+ id: 'snake-case',
6
+ name: 'Check Snake Case',
7
+ description: 'Detect snake_case properties in TypeScript that should be camelCase',
8
+ severity: 'warning',
9
+ fileTypes: ['ts', 'tsx', 'mts', 'cts'],
10
+ check(context) {
11
+ const results = [];
12
+ const lines = context.content.split('\n');
13
+ // Track if we're inside an interface or type definition
14
+ let inInterfaceOrType = false;
15
+ let braceDepth = 0;
16
+ for (let i = 0; i < lines.length; i++) {
17
+ const line = lines[i];
18
+ const lineNum = i + 1;
19
+ // Skip comments
20
+ if (line.trim().startsWith('//') || line.trim().startsWith('*')) {
21
+ continue;
22
+ }
23
+ // Detect interface or type start
24
+ if (/^\s*(?:export\s+)?(?:interface|type)\s+\w+/.test(line)) {
25
+ inInterfaceOrType = true;
26
+ braceDepth = 0;
27
+ }
28
+ // Track brace depth
29
+ const openBraces = (line.match(/\{/g) || []).length;
30
+ const closeBraces = (line.match(/\}/g) || []).length;
31
+ if (inInterfaceOrType) {
32
+ braceDepth += openBraces - closeBraces;
33
+ // Check for snake_case properties
34
+ // Match property names in interface/type definitions
35
+ const propertyMatch = line.match(/^\s*['"]?([a-z][a-z0-9]*(?:_[a-z0-9]+)+)['"]?\s*[?]?\s*:/);
36
+ if (propertyMatch) {
37
+ const propertyName = propertyMatch[1];
38
+ const suggestedName = toCamelCase(propertyName);
39
+ results.push({
40
+ line: lineNum,
41
+ column: line.indexOf(propertyName),
42
+ message: `Property "${propertyName}" uses snake_case - consider using camelCase "${suggestedName}"`,
43
+ severity: 'warning',
44
+ ruleId: 'snake-case/property',
45
+ fix: suggestedName,
46
+ });
47
+ }
48
+ // End of interface/type
49
+ if (braceDepth <= 0 && closeBraces > 0) {
50
+ inInterfaceOrType = false;
51
+ }
52
+ }
53
+ // Also check for snake_case in object destructuring from API responses
54
+ // This is a common issue when copying from API response types
55
+ const destructMatch = line.match(/const\s*\{\s*([^}]+)\s*\}\s*=/);
56
+ if (destructMatch) {
57
+ const props = destructMatch[1].split(',');
58
+ for (const prop of props) {
59
+ const propName = prop.trim().split(':')[0].trim();
60
+ if (/^[a-z][a-z0-9]*(?:_[a-z0-9]+)+$/.test(propName)) {
61
+ results.push({
62
+ line: lineNum,
63
+ column: line.indexOf(propName),
64
+ message: `Destructured property "${propName}" uses snake_case - API response may need transformation`,
65
+ severity: 'info',
66
+ ruleId: 'snake-case/destructure',
67
+ });
68
+ }
69
+ }
70
+ }
71
+ }
72
+ return results;
73
+ },
74
+ };
75
+ function toCamelCase(snakeCase) {
76
+ return snakeCase.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
77
+ }
78
+ //# sourceMappingURL=check-snake-case-ts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-snake-case-ts.js","sourceRoot":"","sources":["../../src/hooks/check-snake-case-ts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Check for TODO/FIXME comments that indicate incomplete work
3
+ */
4
+ import type { Hook } from '../utils/types.js';
5
+ export declare const checkTodoFixme: Hook;
6
+ //# sourceMappingURL=check-todo-fixme.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-todo-fixme.d.ts","sourceRoot":"","sources":["../../src/hooks/check-todo-fixme.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,IAAI,EAA2B,MAAM,mBAAmB,CAAC;AAKvE,eAAO,MAAM,cAAc,EAAE,IAwC5B,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Check for TODO/FIXME comments that indicate incomplete work
3
+ */
4
+ // Patterns to match TODO/FIXME comments
5
+ const TODO_PATTERN = /\b(TODO|FIXME|XXX|HACK|BUG|OPTIMIZE)\b[:\s]*(.*)/i;
6
+ export const checkTodoFixme = {
7
+ id: 'todo',
8
+ name: 'Check TODO/FIXME',
9
+ description: 'Detect TODO, FIXME, and other incomplete work markers',
10
+ severity: 'info',
11
+ fileTypes: ['js', 'jsx', 'ts', 'tsx', 'mjs', 'cjs', 'py', 'html', 'css'],
12
+ check(context) {
13
+ const results = [];
14
+ const lines = context.content.split('\n');
15
+ for (let i = 0; i < lines.length; i++) {
16
+ const line = lines[i];
17
+ const lineNum = i + 1;
18
+ const match = line.match(TODO_PATTERN);
19
+ if (match) {
20
+ const type = match[1].toUpperCase();
21
+ const description = match[2]?.trim() || '';
22
+ // Determine severity based on type
23
+ let severity = 'info';
24
+ if (type === 'FIXME' || type === 'BUG') {
25
+ severity = 'warning';
26
+ }
27
+ results.push({
28
+ line: lineNum,
29
+ column: line.indexOf(match[0]),
30
+ message: description
31
+ ? `${type}: ${description}`
32
+ : `${type} marker without description`,
33
+ severity,
34
+ ruleId: `todo/${type.toLowerCase()}`,
35
+ });
36
+ }
37
+ }
38
+ return results;
39
+ },
40
+ };
41
+ //# sourceMappingURL=check-todo-fixme.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-todo-fixme.js","sourceRoot":"","sources":["../../src/hooks/check-todo-fixme.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,wCAAwC;AACxC,MAAM,YAAY,GAAG,mDAAmD,CAAC;AAEzE,MAAM,CAAC,MAAM,cAAc,GAAS;IAClC,EAAE,EAAE,MAAM;IACV,IAAI,EAAE,kBAAkB;IACxB,WAAW,EAAE,uDAAuD;IACpE,QAAQ,EAAE,MAAM;IAChB,SAAS,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC;IAExE,KAAK,CAAC,OAAoB;QACxB,MAAM,OAAO,GAAiB,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAE1C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACtB,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;YAEtB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YACvC,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;gBACpC,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;gBAE3C,mCAAmC;gBACnC,IAAI,QAAQ,GAAuB,MAAM,CAAC;gBAC1C,IAAI,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;oBACvC,QAAQ,GAAG,SAAS,CAAC;gBACvB,CAAC;gBAED,OAAO,CAAC,IAAI,CAAC;oBACX,IAAI,EAAE,OAAO;oBACb,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;oBAC9B,OAAO,EAAE,WAAW;wBAClB,CAAC,CAAC,GAAG,IAAI,KAAK,WAAW,EAAE;wBAC3B,CAAC,CAAC,GAAG,IAAI,6BAA6B;oBACxC,QAAQ;oBACR,MAAM,EAAE,QAAQ,IAAI,CAAC,WAAW,EAAE,EAAE;iBACrC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;CACF,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Check for unsafe HTML/DOM manipulation that could lead to XSS
3
+ */
4
+ import type { Hook } from '../utils/types.js';
5
+ export declare const checkUnsafeHtml: Hook;
6
+ //# sourceMappingURL=check-unsafe-html.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-unsafe-html.d.ts","sourceRoot":"","sources":["../../src/hooks/check-unsafe-html.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,IAAI,EAA2B,MAAM,mBAAmB,CAAC;AAEvE,eAAO,MAAM,eAAe,EAAE,IA6G7B,CAAC"}