@vibecodeqa/cli 0.21.0 → 0.23.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.
@@ -28,7 +28,14 @@ export function runDuplication(cwd) {
28
28
  const block = lines
29
29
  .slice(i, i + MIN_LINES)
30
30
  .map((l) => l.trim())
31
- .filter((l) => l.length > 0 && !l.startsWith("//") && !l.startsWith("*") && !l.startsWith("import ") && !l.startsWith("export {") && l !== "{" && l !== "}" && l !== "");
31
+ .filter((l) => l.length > 0 &&
32
+ !l.startsWith("//") &&
33
+ !l.startsWith("*") &&
34
+ !l.startsWith("import ") &&
35
+ !l.startsWith("export {") &&
36
+ l !== "{" &&
37
+ l !== "}" &&
38
+ l !== "");
32
39
  if (block.length < MIN_LINES - 2)
33
40
  continue; // too many empty/trivial lines
34
41
  const key = block.join("\n");
@@ -1,12 +1,19 @@
1
1
  /** Error handling check — detects poor error handling patterns. */
2
- import { gradeFromScore } from "../types.js";
3
2
  import { getProductionFiles } from "../fs-utils.js";
3
+ import { gradeFromScore } from "../types.js";
4
4
  export function runErrorHandling(cwd, stack) {
5
5
  const start = Date.now();
6
6
  const issues = [];
7
7
  const files = getProductionFiles(cwd);
8
8
  if (files.length === 0) {
9
- return { name: "error-handling", score: 100, grade: "A", details: { skipped: true, reason: "no source files" }, issues: [], duration: Date.now() - start };
9
+ return {
10
+ name: "error-handling",
11
+ score: 100,
12
+ grade: "A",
13
+ details: { skipped: true, reason: "no source files" },
14
+ issues: [],
15
+ duration: Date.now() - start,
16
+ };
10
17
  }
11
18
  let emptyCatch = 0;
12
19
  let throwString = 0;
@@ -20,7 +27,13 @@ export function runErrorHandling(cwd, stack) {
20
27
  }
21
28
  if (/\bthrow\s+["'`]/.test(line)) {
22
29
  throwString++;
23
- issues.push({ severity: "warning", message: "throw string literal — use throw new Error()", file: f.path, line: i + 1, rule: "throw-string" });
30
+ issues.push({
31
+ severity: "warning",
32
+ message: "throw string literal — use throw new Error()",
33
+ file: f.path,
34
+ line: i + 1,
35
+ rule: "throw-string",
36
+ });
24
37
  }
25
38
  }
26
39
  }
@@ -135,7 +135,9 @@ export function runPerformance(cwd) {
135
135
  try {
136
136
  bundleSizeKB = Math.round(dirSizeKB(distPath));
137
137
  }
138
- catch { /* can't read dist */ }
138
+ catch {
139
+ /* can't read dist */
140
+ }
139
141
  break;
140
142
  }
141
143
  }
@@ -1,14 +1,28 @@
1
1
  /** React-specific checks — hooks rules, conditional hooks, missing keys, prop spreading. */
2
- import { gradeFromScore } from "../types.js";
3
2
  import { getProductionFiles } from "../fs-utils.js";
3
+ import { gradeFromScore } from "../types.js";
4
4
  export function runReact(cwd, stack) {
5
5
  const start = Date.now();
6
6
  if (stack.framework !== "react") {
7
- return { name: "react", score: 100, grade: "A", details: { skipped: true, reason: "not a React project" }, issues: [], duration: Date.now() - start };
7
+ return {
8
+ name: "react",
9
+ score: 100,
10
+ grade: "A",
11
+ details: { skipped: true, reason: "not a React project" },
12
+ issues: [],
13
+ duration: Date.now() - start,
14
+ };
8
15
  }
9
16
  const files = getProductionFiles(cwd).filter((f) => f.ext === ".tsx" || f.ext === ".jsx");
10
17
  if (files.length === 0) {
11
- return { name: "react", score: 100, grade: "A", details: { skipped: true, reason: "no JSX/TSX files" }, issues: [], duration: Date.now() - start };
18
+ return {
19
+ name: "react",
20
+ score: 100,
21
+ grade: "A",
22
+ details: { skipped: true, reason: "no JSX/TSX files" },
23
+ issues: [],
24
+ duration: Date.now() - start,
25
+ };
12
26
  }
13
27
  const issues = [];
14
28
  let conditionalHooks = 0;
@@ -41,7 +55,13 @@ export function runReact(cwd, stack) {
41
55
  // 1. Hooks called inside conditionals
42
56
  if (condBraceDepth > 0 && /\buse[A-Z]\w*\s*\(/.test(trimmed) && !/\/\//.test(trimmed.split("use")[0])) {
43
57
  conditionalHooks++;
44
- issues.push({ severity: "error", message: "Hook called inside conditional — violates Rules of Hooks", file: f.path, line: i + 1, rule: "conditional-hook" });
58
+ issues.push({
59
+ severity: "error",
60
+ message: "Hook called inside conditional — violates Rules of Hooks",
61
+ file: f.path,
62
+ line: i + 1,
63
+ rule: "conditional-hook",
64
+ });
45
65
  }
46
66
  // 2. Missing key in .map() returning JSX
47
67
  if (/\.map\s*\(/.test(trimmed)) {
@@ -55,12 +75,24 @@ export function runReact(cwd, stack) {
55
75
  // 3. index as key
56
76
  if (/key=\{(?:i|idx|index)\}/.test(trimmed) || /key=\{.*(?:, *(?:i|idx|index)\))/.test(trimmed)) {
57
77
  indexKeys++;
58
- issues.push({ severity: "warning", message: "Using index as key — can cause rendering bugs with reorderable lists", file: f.path, line: i + 1, rule: "index-key" });
78
+ issues.push({
79
+ severity: "warning",
80
+ message: "Using index as key — can cause rendering bugs with reorderable lists",
81
+ file: f.path,
82
+ line: i + 1,
83
+ rule: "index-key",
84
+ });
59
85
  }
60
86
  // 4. Prop spreading ({...props} on DOM elements)
61
87
  if (/\{\.\.\.(?!children)\w+\}/.test(trimmed) && /<[a-z]/.test(trimmed)) {
62
88
  propSpreading++;
63
- issues.push({ severity: "warning", message: "Spreading props onto DOM element — can pass unexpected attributes", file: f.path, line: i + 1, rule: "prop-spreading" });
89
+ issues.push({
90
+ severity: "warning",
91
+ message: "Spreading props onto DOM element — can pass unexpected attributes",
92
+ file: f.path,
93
+ line: i + 1,
94
+ rule: "prop-spreading",
95
+ });
64
96
  }
65
97
  // 5. Inline arrow functions in JSX event handlers (performance)
66
98
  if (/on[A-Z]\w*=\{(?:\(\) =>|function)/.test(trimmed)) {
@@ -70,7 +102,11 @@ export function runReact(cwd, stack) {
70
102
  }
71
103
  // Only warn about inline handlers if there are many
72
104
  if (inlineHandlers > 15) {
73
- issues.push({ severity: "warning", message: `${inlineHandlers} inline arrow functions in JSX handlers — extract to named functions for readability`, rule: "inline-handlers" });
105
+ issues.push({
106
+ severity: "warning",
107
+ message: `${inlineHandlers} inline arrow functions in JSX handlers — extract to named functions for readability`,
108
+ rule: "inline-handlers",
109
+ });
74
110
  }
75
111
  const errors = issues.filter((i) => i.severity === "error").length;
76
112
  const warnings = issues.filter((i) => i.severity === "warning").length;
@@ -28,7 +28,7 @@ const CODE_SMELLS = [
28
28
  message: "dangerouslySetInnerHTML bypasses React's XSS protection",
29
29
  },
30
30
  { name: "document.write", pattern: /document\.write\s*\(/, severity: "error", message: "document.write blocks rendering" },
31
- { name: "http:// URL", pattern: /['"]http:\/\/(?!localhost|127\.0\.0\.1)/, severity: "warning", message: "Non-HTTPS URL — use https://" },
31
+ { name: "http:// URL", pattern: /['"]http:\/\/(?!localhost|127\.0\.0\.1|www\.w3\.org|schemas?\.)/, severity: "warning", message: "Non-HTTPS URL — use https://" },
32
32
  { name: "TODO/FIXME", pattern: /\b(TODO|FIXME|HACK|XXX)\b/, severity: "warning", message: "Unresolved TODO/FIXME comment" },
33
33
  {
34
34
  name: "magic number",
@@ -96,6 +96,9 @@ export function runStandards(cwd, stack) {
96
96
  continue;
97
97
  if (/\bpattern\s*:|name:\s*["']|message:\s*["']|description:\s*["']|risk:\s*["']|recommendation:\s*["']/.test(trimmed))
98
98
  continue;
99
+ // Skip string-only lines (check-meta descriptions, inline scripts)
100
+ if (/^\s*["'`].*["'`][,;]?\s*$/.test(line))
101
+ continue;
99
102
  for (const check of CODE_SMELLS) {
100
103
  // Skip console.log in CLI entry points (intentional output)
101
104
  if (check.name === "console.log" && (f.path.includes("cli.") || f.path.includes("bin/")))
@@ -42,9 +42,11 @@ export function runTypeSafety(cwd, isDart = false) {
42
42
  const trimmed = line.trim();
43
43
  if (trimmed.startsWith("//") || trimmed.startsWith("*"))
44
44
  continue;
45
- // Skip pattern definition lines (prevents false positives when scanning own code)
45
+ // Skip pattern definition lines and string-heavy lines (prevents false positives)
46
46
  if (/\bpattern\s*:|name:\s*["']|message:\s*["']|description:\s*["']|risk:\s*["']|recommendation:\s*["']/.test(trimmed))
47
47
  continue;
48
+ if (/^\s*["'`].*["'`][,;]?\s*$/.test(line))
49
+ continue;
48
50
  for (const p of PATTERNS) {
49
51
  const matches = line.match(p.pattern);
50
52
  if (matches) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibecodeqa/cli",
3
- "version": "0.21.0",
3
+ "version": "0.23.0",
4
4
  "description": "Code health scanner for the AI coding era. 21 checks, zero config, full report.",
5
5
  "type": "module",
6
6
  "bin": {