@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.
- package/dist/check-meta.js +25 -5
- package/dist/cli.js +13 -0
- package/dist/report/components.d.ts +10 -0
- package/dist/report/components.js +21 -0
- package/dist/report/html.d.ts +7 -5
- package/dist/report/html.js +45 -422
- package/dist/report/pages.d.ts +25 -0
- package/dist/report/pages.js +210 -0
- package/dist/report/styles.d.ts +2 -0
- package/dist/report/styles.js +156 -0
- package/dist/report/svg.d.ts +26 -6
- package/dist/report/svg.js +163 -20
- package/dist/runners/accessibility.d.ts +3 -0
- package/dist/runners/accessibility.js +85 -0
- package/dist/runners/react.d.ts +3 -0
- package/dist/runners/react.js +81 -0
- package/package.json +2 -2
package/dist/check-meta.js
CHANGED
|
@@ -56,7 +56,7 @@ export const CHECK_META = {
|
|
|
56
56
|
label: "Error Handling",
|
|
57
57
|
category: "Quality",
|
|
58
58
|
priority: "high",
|
|
59
|
-
weight:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
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
|
+
}
|
package/dist/report/html.d.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
/** Generate a multi-page navigable HTML report.
|
|
2
2
|
*
|
|
3
3
|
* Architecture:
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
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
|
|
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;
|