@vibecodeqa/cli 0.15.0 → 0.17.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.
@@ -56,7 +56,7 @@ export const CHECK_META = {
56
56
  label: "Error Handling",
57
57
  category: "Quality",
58
58
  priority: "high",
59
- weight: 2,
59
+ weight: 3,
60
60
  description: "Detects poor error handling: empty catch blocks, throw with string literals, catch-and-rethrow without context, Promise.then() without .catch(), missing React Error Boundaries.",
61
61
  risk: "Empty catch blocks silently swallow errors. throw 'string' loses stack traces. Missing Error Boundaries in React cause the entire app to crash on render errors.",
62
62
  recommendation: "Handle or log every catch. Use throw new Error() for stack traces. Add Error Boundaries in React. Chain .catch() on promises.",
@@ -96,7 +96,7 @@ export const CHECK_META = {
96
96
  label: "Testing",
97
97
  category: "Testing",
98
98
  priority: "critical",
99
- weight: 22,
99
+ weight: 17,
100
100
  description: "Deep assessment of test quality across 6 dimensions: pyramid presence (unit/integration/component/E2E layers), test execution (pass/fail), coverage (statement/branch/line/function), file pairing (test file per source file), test quality (assertion density, mock ratio, snapshot ratio), and E2E tool detection (Playwright/Cypress).",
101
101
  risk: "Code without tests is code you can't safely change. Missing test layers mean entire categories of bugs go undetected: unit tests catch logic bugs, integration tests catch API contract breaks, E2E tests catch user-visible regressions. Low coverage means large portions of code are never exercised.",
102
102
  recommendation: "Follow the testing pyramid: many unit tests, some integration tests, fewer E2E tests. Aim for >80% branch coverage. Every source file should have a corresponding test file. Use Playwright for E2E if you have a web frontend.",
@@ -136,7 +136,7 @@ export const CHECK_META = {
136
136
  label: "Architecture",
137
137
  category: "Architecture",
138
138
  priority: "high",
139
- weight: 7,
139
+ weight: 6,
140
140
  description: "Analyzes the import graph to detect structural problems: circular dependencies, god modules (imported by >50% of files), orphan modules (dead code), high fan-out (importing too many modules), and connector modules (high coupling). Generates an SVG architecture diagram.",
141
141
  risk: "Circular dependencies create build order issues and make refactoring impossible without breaking changes. God modules become bottlenecks — any change ripples through the entire codebase. High coupling means you can't change one module without testing everything it touches.",
142
142
  recommendation: "Break circular deps by extracting shared types to a separate file. Split god modules by concern. Reduce fan-out by co-locating related code. Use dependency injection for loose coupling.",
@@ -146,7 +146,7 @@ export const CHECK_META = {
146
146
  label: "Confusion Index",
147
147
  category: "LLM Readiness",
148
148
  priority: "high",
149
- weight: 8,
149
+ weight: 7,
150
150
  description: "Measures naming ambiguity that causes LLMs to misunderstand or edit the wrong code. Checks: file name confusability (Levenshtein distance + synonym detection), generic function/variable names, export name collisions across files, and ambiguous abbreviations.",
151
151
  risk: "GPT-4o drops 28.6 percentage points on code summarization when names are ambiguous (arXiv:2510.03178). LLMs editing similar-named files is the #1 reported failure mode in AI-assisted development. Generic names like process(), handle(), data cause models to misinterpret intent.",
152
152
  recommendation: "Use descriptive, unique names. Avoid synonym files (utils.ts + helpers.ts — pick one). Avoid generic exports. Disambiguate abbreviations (use 'authentication' not 'auth' if both auth meanings exist in the codebase).",
@@ -156,11 +156,31 @@ export const CHECK_META = {
156
156
  label: "Context Locality",
157
157
  category: "LLM Readiness",
158
158
  priority: "high",
159
- weight: 7,
159
+ weight: 6,
160
160
  description: "Measures how self-contained code is for LLM consumption. Checks: token density per file, import count, circular dependencies, and context sinks (files that import many modules but export little). Based on the finding that LLMs lose 30%+ accuracy for information in the middle of long contexts.",
161
161
  risk: "Files over ~4000 tokens exceed the 'sweet spot' for LLM attention (Liu et al. 2023 'Lost in the Middle'). Circular dependencies create infinite loops in LLM code navigation. Heavy import chains force LLMs to load many files, burning context window budget (Chroma 'Context Rot' 2025).",
162
162
  recommendation: "Keep files under 400 lines / 4000 tokens. Limit imports to <15 per file. Break circular dependencies. Co-locate related code to reduce cross-file jumps.",
163
163
  },
164
+ react: {
165
+ name: "react",
166
+ label: "React Patterns",
167
+ category: "Quality",
168
+ priority: "high",
169
+ weight: 3,
170
+ description: "Checks React-specific patterns: conditional hook calls (violates Rules of Hooks), missing key props in .map(), index as key, prop spreading on DOM elements, and excessive inline handlers.",
171
+ risk: "Conditional hooks cause React to crash at runtime. Missing keys cause incorrect reconciliation — items can swap, duplicate, or lose state. Index keys break when lists are reordered or filtered.",
172
+ recommendation: "Never call hooks inside conditions, loops, or nested functions. Always provide a unique, stable key in .map(). Avoid spreading unknown props onto DOM elements. Extract inline handlers for readability.",
173
+ },
174
+ accessibility: {
175
+ name: "accessibility",
176
+ label: "Accessibility",
177
+ category: "Quality",
178
+ priority: "high",
179
+ weight: 4,
180
+ description: "Checks common accessibility violations: images without alt text, click handlers on non-interactive elements without keyboard support, form controls without labels, autoFocus usage, positive tabIndex, and missing html lang attribute.",
181
+ risk: "1 in 4 adults has a disability (CDC). Missing alt text makes images invisible to screen readers. Click-only divs exclude keyboard users. Unlabeled inputs are unusable with assistive technology. Missing lang attribute breaks screen reader pronunciation.",
182
+ recommendation: "Add alt text to all images (use alt=\"\" for decorative). Use <button> for clickable elements, not <div onClick>. Label all form controls with <label>, aria-label, or aria-labelledby. Set lang on <html>.",
183
+ },
164
184
  };
165
185
  export function getCheckMeta(name) {
166
186
  return (CHECK_META[name] || {
package/dist/cli.js CHANGED
@@ -13,6 +13,8 @@ import { runDocs } from "./runners/docs.js";
13
13
  import { runDuplication } from "./runners/duplication.js";
14
14
  import { runErrorHandling } from "./runners/error-handling.js";
15
15
  import { runLint } from "./runners/lint.js";
16
+ import { runReact } from "./runners/react.js";
17
+ import { runAccessibility } from "./runners/accessibility.js";
16
18
  import { runSecrets } from "./runners/secrets.js";
17
19
  import { runSecurity } from "./runners/security.js";
18
20
  import { runStandards } from "./runners/standards.js";
@@ -33,6 +35,7 @@ const jsonOnly = flags.has("--json");
33
35
  const ciMode = flags.has("--ci");
34
36
  const skipTests = flags.has("--skip-tests");
35
37
  const watchMode = flags.has("--watch");
38
+ const badgeMode = flags.has("--badge");
36
39
  function color(grade) {
37
40
  if (grade === "A")
38
41
  return "\x1b[32m";
@@ -67,6 +70,8 @@ async function main() {
67
70
  { name: "complexity", fn: () => runComplexity(cwd) },
68
71
  { name: "duplication", fn: () => runDuplication(cwd) },
69
72
  { name: "error-handling", fn: () => runErrorHandling(cwd, stack) },
73
+ { name: "react", fn: () => runReact(cwd, stack) },
74
+ { name: "accessibility", fn: () => runAccessibility(cwd) },
70
75
  { name: "docs", fn: () => runDocs(cwd) },
71
76
  // Testing
72
77
  { name: "testing", fn: () => runTesting(cwd, stack, skipTests) },
@@ -132,6 +137,12 @@ async function main() {
132
137
  }
133
138
  writeFileSync(join(outputDir, "report.json"), JSON.stringify(report, null, 2));
134
139
  writeFileSync(join(outputDir, "report.html"), generateHTML(report, historyDir));
140
+ // Badge SVG
141
+ if (badgeMode) {
142
+ const { buildBadge } = await import("./report/svg.js");
143
+ const badgeSvg = buildBadge(score, grade);
144
+ writeFileSync(join(outputDir, "badge.svg"), badgeSvg);
145
+ }
135
146
  if (jsonOnly) {
136
147
  console.log(JSON.stringify(report));
137
148
  }
@@ -144,6 +155,8 @@ async function main() {
144
155
  console.log("");
145
156
  console.log(` \x1b[2mReport: ${join(outputDir, "report.html")}\x1b[0m`);
146
157
  console.log(` \x1b[2mJSON: ${join(outputDir, "report.json")}\x1b[0m`);
158
+ if (badgeMode)
159
+ console.log(` \x1b[2mBadge: ${join(outputDir, "badge.svg")}\x1b[0m`);
147
160
  console.log("");
148
161
  }
149
162
  if (ciMode && score < 60) {
@@ -0,0 +1,10 @@
1
+ /** Reusable HTML components and helpers for the report. */
2
+ import type { Priority } from "../check-meta.js";
3
+ /** HTML-escape a string. */
4
+ export declare function e(s: string): string;
5
+ /** Make a file path a clickable GitHub link if repoUrl is available. */
6
+ export declare function fileLink(path: string, line: number | undefined, repoUrl: string | null, branch: string): string;
7
+ /** Grade color. */
8
+ export declare function gc(grade: string): string;
9
+ /** Priority color. */
10
+ export declare function pc(p: Priority): string;
@@ -0,0 +1,21 @@
1
+ /** Reusable HTML components and helpers for the report. */
2
+ /** HTML-escape a string. */
3
+ export function e(s) {
4
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
5
+ }
6
+ /** Make a file path a clickable GitHub link if repoUrl is available. */
7
+ export function fileLink(path, line, repoUrl, branch) {
8
+ const clean = path.split(":")[0];
9
+ if (!repoUrl || !/^https?:\/\//.test(repoUrl))
10
+ return e(path);
11
+ const href = `${repoUrl}/blob/${branch}/${clean}${line ? `#L${line}` : ""}`;
12
+ return `<a href="${e(href)}" target="_blank" rel="noopener" class="flink">${e(path)}</a>`;
13
+ }
14
+ /** Grade color. */
15
+ export function gc(grade) {
16
+ return { A: "#22c55e", B: "#84cc16", C: "#eab308", D: "#f97316", F: "#ef4444" }[grade] || "#6b7280";
17
+ }
18
+ /** Priority color. */
19
+ export function pc(p) {
20
+ return { critical: "#ef4444", high: "#f97316", medium: "#eab308", low: "#6b7280" }[p];
21
+ }
@@ -1,12 +1,14 @@
1
1
  /** Generate a multi-page navigable HTML report.
2
2
  *
3
3
  * Architecture:
4
- * Top nav: Overview | Foundations | Quality | Testing | Security | Issues
5
- * Sub-nav: Tabs for each check within a category
6
- * Detail: Per-check stats + full issue list grouped by file
7
- * File map: Cross-check heatmap of issues per file
4
+ * Primary nav: Overview | Foundations | Quality | Testing | Security | Architecture | AI Readiness
5
+ * Secondary nav: Issues (N) | Files (right-aligned, visually distinct)
6
+ * Sidebar: Score + dimension tree + view links
7
+ * Overview: Dashboard with score, radar, timeline, category cards, top issues, file hotspots
8
+ * Dimensions: Sub-tabs for each check within a category
9
+ * Views: Cross-cutting data slices (issues table, file health map)
8
10
  *
9
- * All in one self-contained HTML file using hash routing + show/hide.
11
+ * All in one self-contained HTML file using show/hide navigation.
10
12
  */
11
13
  import type { VibeReport } from "../types.js";
12
14
  export declare function generateHTML(report: VibeReport, historyDir?: string): string;