@vibecodeqa/cli 0.17.0 → 0.18.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/README.md +73 -63
- package/dist/check-meta.d.ts +1 -0
- package/dist/check-meta.js +34 -2
- package/dist/cli.js +35 -10
- package/dist/detect.js +24 -2
- package/dist/fs-utils.d.ts +4 -0
- package/dist/fs-utils.js +12 -6
- package/dist/report/html.d.ts +17 -10
- package/dist/report/html.js +106 -73
- package/dist/report/pages.d.ts +2 -1
- package/dist/report/pages.js +88 -82
- package/dist/report/sarif.d.ts +3 -0
- package/dist/report/sarif.js +67 -0
- package/dist/report/styles.d.ts +1 -1
- package/dist/report/styles.js +82 -36
- package/dist/runners/architecture.d.ts +2 -0
- package/dist/runners/architecture.js +232 -20
- package/dist/runners/code-coherence.d.ts +17 -0
- package/dist/runners/code-coherence.js +39 -0
- package/dist/runners/complexity.js +7 -37
- package/dist/runners/confusion.js +3 -31
- package/dist/runners/context.js +9 -40
- package/dist/runners/dependencies.js +28 -0
- package/dist/runners/doc-coherence.d.ts +14 -0
- package/dist/runners/doc-coherence.js +48 -0
- package/dist/runners/docs.js +7 -32
- package/dist/runners/duplication.js +9 -37
- package/dist/runners/lint.js +17 -0
- package/dist/runners/performance.d.ts +10 -0
- package/dist/runners/performance.js +174 -0
- package/dist/runners/react.js +15 -10
- package/dist/runners/secrets.js +8 -29
- package/dist/runners/security.js +15 -38
- package/dist/runners/standards.js +3 -36
- package/dist/runners/structure.js +35 -55
- package/dist/runners/testing.js +2 -36
- package/dist/runners/type-safety.d.ts +1 -1
- package/dist/runners/type-safety.js +19 -37
- package/dist/runners/types-check.d.ts +1 -1
- package/dist/runners/types-check.js +38 -20
- package/dist/types.d.ts +5 -5
- package/package.json +11 -10
package/README.md
CHANGED
|
@@ -2,19 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
**Code health scanner for the AI coding era.**
|
|
4
4
|
|
|
5
|
-
One command.
|
|
5
|
+
One command. 21 checks. Full report. Zero config.
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
npx @vibecodeqa/cli
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
  
|
|
12
12
|
|
|
13
13
|
## What it does
|
|
14
14
|
|
|
15
|
-
vcqa scans your TypeScript/JavaScript codebase and produces a scored health report with actionable findings. It auto-detects your stack (React, Vite, vitest, Biome, etc.) and runs
|
|
15
|
+
vcqa scans your TypeScript/JavaScript/Dart/Flutter codebase and produces a scored health report with actionable findings. It auto-detects your stack (React, Flutter, Vite, vitest, Biome, etc.) and runs 21 checks across 7 categories.
|
|
16
16
|
|
|
17
|
-
The output is a self-contained HTML report with radar charts, architecture diagrams,
|
|
17
|
+
The output is a self-contained HTML report with radar charts, architecture diagrams, score timeline, testing pyramid, and drill-down issue lists — all navigable via sidebar and tab navigation.
|
|
18
18
|
|
|
19
19
|
## Quick start
|
|
20
20
|
|
|
@@ -34,6 +34,12 @@ npx @vibecodeqa/cli --ci
|
|
|
34
34
|
# JSON output (pipe to other tools)
|
|
35
35
|
npx @vibecodeqa/cli --json
|
|
36
36
|
|
|
37
|
+
# Generate badge SVG for README
|
|
38
|
+
npx @vibecodeqa/cli --badge
|
|
39
|
+
|
|
40
|
+
# SARIF output for GitHub Security tab
|
|
41
|
+
npx @vibecodeqa/cli --sarif
|
|
42
|
+
|
|
37
43
|
# Scan a specific directory
|
|
38
44
|
npx @vibecodeqa/cli /path/to/project
|
|
39
45
|
```
|
|
@@ -41,6 +47,8 @@ npx @vibecodeqa/cli /path/to/project
|
|
|
41
47
|
Output goes to `.vibe-check/`:
|
|
42
48
|
- `report.html` — navigable multi-page dashboard (open in browser)
|
|
43
49
|
- `report.json` — machine-readable results
|
|
50
|
+
- `badge.svg` — shields.io-style badge (with `--badge`)
|
|
51
|
+
- `report.sarif` — SARIF 2.1.0 for GitHub Code Scanning (with `--sarif`)
|
|
44
52
|
- `history/` — last 30 reports for trend tracking
|
|
45
53
|
|
|
46
54
|
## Checks
|
|
@@ -53,17 +61,20 @@ Output goes to `.vibe-check/`:
|
|
|
53
61
|
| **Lint** | 5% | Biome or ESLint errors/warnings (auto-detected) |
|
|
54
62
|
| **Types** | 6% | TypeScript compilation errors (`tsc --noEmit`) |
|
|
55
63
|
| **Type Safety** | 3% | `as any`, `: any`, `@ts-ignore`, `@ts-nocheck` counts |
|
|
56
|
-
| **Standards** | 3% | File naming, large files (>300 lines), code smells (console.log, var, ==, eval
|
|
64
|
+
| **Standards** | 3% | File naming, large files (>300 lines), code smells (console.log, var, ==, eval), config hygiene |
|
|
57
65
|
|
|
58
|
-
### Quality (
|
|
66
|
+
### Quality (23%)
|
|
59
67
|
|
|
60
68
|
| Check | Weight | What it measures |
|
|
61
69
|
|-------|--------|-----------------|
|
|
62
|
-
| **Complexity** |
|
|
70
|
+
| **Complexity** | 3% | Cognitive complexity per function, functions >60 lines |
|
|
63
71
|
| **Duplication** | 5% | Copy-pasted 6+ line blocks |
|
|
72
|
+
| **Error Handling** | 5% | Empty catch blocks, throw string, missing Error Boundaries |
|
|
73
|
+
| **React Patterns** | 3% | Conditional hooks, missing keys, index keys, prop spreading |
|
|
74
|
+
| **Accessibility** | 4% | img alt, click on non-interactive elements, form labels, html lang |
|
|
64
75
|
| **Docs** | 3% | README quality, JSDoc coverage of exports |
|
|
65
76
|
|
|
66
|
-
### Testing (
|
|
77
|
+
### Testing (15%)
|
|
67
78
|
|
|
68
79
|
One deep check with 6 sub-dimensions:
|
|
69
80
|
|
|
@@ -74,34 +85,36 @@ One deep check with 6 sub-dimensions:
|
|
|
74
85
|
- **Quality** — assertion density, mock ratio, snapshot ratio
|
|
75
86
|
- **E2E detection** — Playwright/Cypress configured?
|
|
76
87
|
|
|
77
|
-
### Architecture (
|
|
88
|
+
### Architecture (10%)
|
|
78
89
|
|
|
79
90
|
| Check | Weight | What it measures |
|
|
80
91
|
|-------|--------|-----------------|
|
|
81
|
-
| **Architecture** |
|
|
92
|
+
| **Architecture** | 6% | Import graph, circular deps, god modules, orphan files, fan-out, SVG diagram with legend |
|
|
93
|
+
| **Performance** | 4% | Barrel imports, heavy dependencies, dynamic import opportunities, CSS-in-JS overhead |
|
|
82
94
|
|
|
83
|
-
### Security (
|
|
95
|
+
### Security (16%)
|
|
84
96
|
|
|
85
97
|
| Check | Weight | What it measures |
|
|
86
98
|
|-------|--------|-----------------|
|
|
87
99
|
| **Secrets** | 6% | 13 patterns (AWS, GitHub, Stripe, OpenAI, private keys) |
|
|
88
|
-
| **Security** |
|
|
89
|
-
| **Dependencies** | 5% | npm audit vulnerabilities + outdated packages |
|
|
100
|
+
| **Security** | 5% | 15 CWE-mapped patterns (XSS, injection, crypto, SSRF) |
|
|
101
|
+
| **Dependencies** | 5% | npm audit / dart pub outdated vulnerabilities + outdated packages |
|
|
90
102
|
|
|
91
|
-
###
|
|
103
|
+
### AI Readiness (13%)
|
|
92
104
|
|
|
93
105
|
Novel checks that no other tool offers:
|
|
94
106
|
|
|
95
107
|
| Check | Weight | What it measures |
|
|
96
108
|
|-------|--------|-----------------|
|
|
97
|
-
| **Confusion Index** |
|
|
98
|
-
| **Context Locality** |
|
|
109
|
+
| **Confusion Index** | 7% | File name similarity, generic names, export collisions, ambiguous abbreviations |
|
|
110
|
+
| **Context Locality** | 6% | Token density, import depth, circular deps, context sinks |
|
|
99
111
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
112
|
+
### AI Analysis (PRO — coming soon)
|
|
113
|
+
|
|
114
|
+
| Check | What it will do |
|
|
115
|
+
|-------|----------------|
|
|
116
|
+
| **Doc Coherence** | LLM-powered detection of contradictions between docs and code |
|
|
117
|
+
| **Code Coherence** | LLM-powered detection of internal inconsistencies across modules |
|
|
105
118
|
|
|
106
119
|
## Scoring
|
|
107
120
|
|
|
@@ -117,27 +130,19 @@ Each check produces a score from 0-100. The composite score is a weighted averag
|
|
|
117
130
|
|
|
118
131
|
## Report features
|
|
119
132
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
- **
|
|
123
|
-
- **
|
|
124
|
-
- **
|
|
125
|
-
- **Architecture SVG
|
|
126
|
-
- **
|
|
127
|
-
- **Trend comparison** — score delta vs. previous run
|
|
128
|
-
- **File map** — top files by issue count across all checks
|
|
133
|
+
- **Primary nav**: Overview + 7 dimension tabs (Foundations, Quality, Testing, Architecture, Security, AI Readiness, AI Analysis)
|
|
134
|
+
- **Secondary nav**: Issues + Files (cross-cutting data views)
|
|
135
|
+
- **Score ring + radar chart** — 6-axis view of category scores
|
|
136
|
+
- **Score timeline** — last 30 runs with grade-colored dots
|
|
137
|
+
- **Testing pyramid** — proportional SVG showing unit/integration/component/e2e distribution
|
|
138
|
+
- **Architecture SVG** — modules grouped by directory, bezier edges with arrows, color-coded nodes (god module, cycle, orphan), legend
|
|
139
|
+
- **File health map** — heatmap bars showing issue density per file
|
|
140
|
+
- **Trend comparison** — score delta vs. previous run
|
|
129
141
|
- **GitHub links** — click any file:line to open in GitHub (auto-detected from git remote)
|
|
130
|
-
- **Actionable prompts** —
|
|
142
|
+
- **Actionable prompts** — clipboard button on every issue copies a fix prompt for Claude/Codex
|
|
131
143
|
- **Info panels** — each check has What/Risk/Fix explanations with research citations
|
|
132
144
|
- **Priority badges** — critical/high/medium/low on each check
|
|
133
145
|
|
|
134
|
-
## Trend tracking
|
|
135
|
-
|
|
136
|
-
vcqa reads the previous `.vibe-check/report.json` on each run and shows:
|
|
137
|
-
- Score change (↑ improved / ↓ declined)
|
|
138
|
-
- Per-check deltas
|
|
139
|
-
- New vs. fixed issue counts
|
|
140
|
-
|
|
141
146
|
## CLI options
|
|
142
147
|
|
|
143
148
|
| Flag | Description |
|
|
@@ -146,33 +151,37 @@ vcqa reads the previous `.vibe-check/report.json` on each run and shows:
|
|
|
146
151
|
| `--watch` | Re-scan automatically on file changes |
|
|
147
152
|
| `--ci` | Exit code 1 if composite score < 60 |
|
|
148
153
|
| `--json` | Output JSON to stdout (no HTML, no browser) |
|
|
154
|
+
| `--badge` | Generate badge.svg in output directory |
|
|
155
|
+
| `--sarif` | Generate SARIF 2.1.0 for GitHub Code Scanning |
|
|
149
156
|
|
|
150
157
|
## Stack detection
|
|
151
158
|
|
|
152
|
-
Auto-detects from `package.json` and config files:
|
|
153
|
-
- **Language:** TypeScript, JavaScript
|
|
154
|
-
- **Framework:** React, Vue, Svelte
|
|
159
|
+
Auto-detects from `package.json`, `pubspec.yaml`, and config files:
|
|
160
|
+
- **Language:** TypeScript, JavaScript, Dart
|
|
161
|
+
- **Framework:** React, Vue, Svelte, Flutter
|
|
155
162
|
- **Bundler:** Vite, Webpack, esbuild
|
|
156
|
-
- **Test runner:** vitest, jest
|
|
157
|
-
- **Linter:** Biome, ESLint
|
|
158
|
-
- **Package manager:** pnpm, npm, yarn, bun
|
|
159
|
-
|
|
160
|
-
##
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
-
|
|
170
|
-
|
|
171
|
-
-
|
|
172
|
-
-
|
|
173
|
-
-
|
|
174
|
-
|
|
175
|
-
|
|
163
|
+
- **Test runner:** vitest, jest, flutter_test, dart_test
|
|
164
|
+
- **Linter:** Biome, ESLint, dart analyze
|
|
165
|
+
- **Package manager:** pnpm, npm, yarn, bun, pub
|
|
166
|
+
|
|
167
|
+
## GitHub Actions
|
|
168
|
+
|
|
169
|
+
Add this to `.github/workflows/vibecodeqa.yml` for automatic PR scanning:
|
|
170
|
+
|
|
171
|
+
```yaml
|
|
172
|
+
name: VibeCode QA
|
|
173
|
+
on: [pull_request]
|
|
174
|
+
jobs:
|
|
175
|
+
scan:
|
|
176
|
+
runs-on: ubuntu-latest
|
|
177
|
+
steps:
|
|
178
|
+
- uses: actions/checkout@v4
|
|
179
|
+
- run: npx @vibecodeqa/cli --skip-tests --ci --sarif
|
|
180
|
+
- uses: github/codeql-action/upload-sarif@v3
|
|
181
|
+
if: always()
|
|
182
|
+
with:
|
|
183
|
+
sarif_file: .vibe-check/report.sarif
|
|
184
|
+
```
|
|
176
185
|
|
|
177
186
|
## License
|
|
178
187
|
|
|
@@ -180,6 +189,7 @@ MIT — Free forever as a CLI tool.
|
|
|
180
189
|
|
|
181
190
|
## Links
|
|
182
191
|
|
|
183
|
-
- **GitHub:** https://github.com/
|
|
192
|
+
- **GitHub:** https://github.com/vibecodeqa/cli
|
|
184
193
|
- **Website:** https://vibecodeqa.online
|
|
185
|
-
- **
|
|
194
|
+
- **npm:** https://www.npmjs.com/package/@vibecodeqa/cli
|
|
195
|
+
- **Issues:** https://github.com/vibecodeqa/cli/issues
|
package/dist/check-meta.d.ts
CHANGED
package/dist/check-meta.js
CHANGED
|
@@ -96,7 +96,7 @@ export const CHECK_META = {
|
|
|
96
96
|
label: "Testing",
|
|
97
97
|
category: "Testing",
|
|
98
98
|
priority: "critical",
|
|
99
|
-
weight:
|
|
99
|
+
weight: 15,
|
|
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.",
|
|
@@ -116,7 +116,7 @@ export const CHECK_META = {
|
|
|
116
116
|
label: "Security Patterns",
|
|
117
117
|
category: "Security",
|
|
118
118
|
priority: "critical",
|
|
119
|
-
weight:
|
|
119
|
+
weight: 5,
|
|
120
120
|
description: "Static analysis for 15 vulnerability patterns mapped to CWE (Common Weakness Enumeration) IDs. Covers: XSS (innerHTML, dangerouslySetInnerHTML, document.write), injection (eval, new Function, SQL template literals, command injection), weak crypto (Math.random for tokens, MD5/SHA1), prototype pollution, path traversal, SSRF, and missing security headers.",
|
|
121
121
|
risk: "These patterns represent the most commonly exploited vulnerabilities in web applications (OWASP Top 10). A single XSS or injection vulnerability can lead to account takeover, data theft, or complete system compromise.",
|
|
122
122
|
recommendation: "Replace innerHTML with textContent or DOM APIs. Never use eval(). Use parameterized queries for SQL. Use crypto.randomUUID() instead of Math.random() for tokens. Validate all user input before use in file paths or URLs.",
|
|
@@ -181,6 +181,38 @@ export const CHECK_META = {
|
|
|
181
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
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
183
|
},
|
|
184
|
+
performance: {
|
|
185
|
+
name: "performance",
|
|
186
|
+
label: "Performance",
|
|
187
|
+
category: "Architecture",
|
|
188
|
+
priority: "medium",
|
|
189
|
+
weight: 4,
|
|
190
|
+
description: "Detects barrel imports that defeat tree-shaking, heavy dependencies with lighter alternatives, static imports of large libraries that could be lazy-loaded, and runtime CSS-in-JS overhead.",
|
|
191
|
+
risk: "Barrel files (index.ts re-exports) prevent bundlers from tree-shaking unused code, bloating bundles by 2-10x. Heavy dependencies like moment.js add 300KB when date-fns does the same in 7KB. Static imports of visualization libraries delay initial page load.",
|
|
192
|
+
recommendation: "Replace barrel re-exports with direct imports. Swap heavy deps for lighter alternatives. Use dynamic import() for large libraries only needed on interaction. Prefer zero-runtime CSS (Tailwind, CSS Modules) over styled-components.",
|
|
193
|
+
},
|
|
194
|
+
"doc-coherence": {
|
|
195
|
+
name: "doc-coherence",
|
|
196
|
+
label: "Doc Coherence",
|
|
197
|
+
category: "AI Analysis",
|
|
198
|
+
priority: "high",
|
|
199
|
+
weight: 0,
|
|
200
|
+
description: "LLM-powered analysis that detects contradictions between documentation and code. Finds stale README claims, incorrect JSDoc parameters, outdated CHANGELOG references, and comments that no longer match the implementation.",
|
|
201
|
+
risk: "Stale documentation is worse than no documentation — it actively misleads developers and LLMs. When README says 'supports X' but the feature was removed, new contributors waste time. When JSDoc says a param is required but code treats it as optional, callers crash.",
|
|
202
|
+
recommendation: "Enable doc-coherence with a VibeCode QA Pro subscription. The LLM scans all documentation against the actual code and surfaces contradictions with specific file references.",
|
|
203
|
+
premium: true,
|
|
204
|
+
},
|
|
205
|
+
"code-coherence": {
|
|
206
|
+
name: "code-coherence",
|
|
207
|
+
label: "Code Coherence",
|
|
208
|
+
category: "AI Analysis",
|
|
209
|
+
priority: "high",
|
|
210
|
+
weight: 0,
|
|
211
|
+
description: "LLM-powered analysis that detects internal contradictions within the codebase itself. Finds inconsistent validation logic, conflicting defaults across modules, naming convention drift, dead config flags, and behavioral mismatches.",
|
|
212
|
+
risk: "Incoherent codebases are the #1 source of 'it works on my machine' bugs. When module A validates email with regex and module B uses a different regex, some emails pass one and fail the other. When timeouts differ across modules, race conditions emerge under load.",
|
|
213
|
+
recommendation: "Enable code-coherence with a VibeCode QA Pro subscription. The LLM analyzes cross-module patterns and surfaces behavioral contradictions that static analysis cannot detect.",
|
|
214
|
+
premium: true,
|
|
215
|
+
},
|
|
184
216
|
};
|
|
185
217
|
export function getCheckMeta(name) {
|
|
186
218
|
return (CHECK_META[name] || {
|
package/dist/cli.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { existsSync, mkdirSync, readdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { join, resolve } from "node:path";
|
|
5
5
|
import { detectRepoUrl, detectStack } from "./detect.js";
|
|
6
|
-
import {
|
|
6
|
+
import { generatePages } from "./report/html.js";
|
|
7
7
|
import { runArchitecture } from "./runners/architecture.js";
|
|
8
8
|
import { runComplexity } from "./runners/complexity.js";
|
|
9
9
|
import { runConfusion } from "./runners/confusion.js";
|
|
@@ -11,10 +11,13 @@ import { runContext } from "./runners/context.js";
|
|
|
11
11
|
import { runDependencies } from "./runners/dependencies.js";
|
|
12
12
|
import { runDocs } from "./runners/docs.js";
|
|
13
13
|
import { runDuplication } from "./runners/duplication.js";
|
|
14
|
+
import { runPerformance } from "./runners/performance.js";
|
|
14
15
|
import { runErrorHandling } from "./runners/error-handling.js";
|
|
15
16
|
import { runLint } from "./runners/lint.js";
|
|
16
17
|
import { runReact } from "./runners/react.js";
|
|
17
18
|
import { runAccessibility } from "./runners/accessibility.js";
|
|
19
|
+
import { runDocCoherence } from "./runners/doc-coherence.js";
|
|
20
|
+
import { runCodeCoherence } from "./runners/code-coherence.js";
|
|
18
21
|
import { runSecrets } from "./runners/secrets.js";
|
|
19
22
|
import { runSecurity } from "./runners/security.js";
|
|
20
23
|
import { runStandards } from "./runners/standards.js";
|
|
@@ -36,6 +39,7 @@ const ciMode = flags.has("--ci");
|
|
|
36
39
|
const skipTests = flags.has("--skip-tests");
|
|
37
40
|
const watchMode = flags.has("--watch");
|
|
38
41
|
const badgeMode = flags.has("--badge");
|
|
42
|
+
const sarifMode = flags.has("--sarif");
|
|
39
43
|
function color(grade) {
|
|
40
44
|
if (grade === "A")
|
|
41
45
|
return "\x1b[32m";
|
|
@@ -58,13 +62,14 @@ async function main() {
|
|
|
58
62
|
console.log("");
|
|
59
63
|
}
|
|
60
64
|
const checks = [];
|
|
65
|
+
const isDart = stack.language === "dart";
|
|
61
66
|
// All runners grouped by category
|
|
62
67
|
const runners = [
|
|
63
68
|
// Foundations
|
|
64
69
|
{ name: "structure", fn: () => runStructure(cwd, stack) },
|
|
65
70
|
{ name: "lint", fn: () => runLint(cwd, stack) },
|
|
66
|
-
{ name: "types", fn: () => runTypeCheck(cwd) },
|
|
67
|
-
{ name: "type-safety", fn: () => runTypeSafety(cwd) },
|
|
71
|
+
{ name: "types", fn: () => runTypeCheck(cwd, isDart) },
|
|
72
|
+
{ name: "type-safety", fn: () => runTypeSafety(cwd, isDart) },
|
|
68
73
|
{ name: "standards", fn: () => runStandards(cwd, stack) },
|
|
69
74
|
// Quality
|
|
70
75
|
{ name: "complexity", fn: () => runComplexity(cwd) },
|
|
@@ -81,9 +86,13 @@ async function main() {
|
|
|
81
86
|
{ name: "dependencies", fn: () => runDependencies(cwd, stack) },
|
|
82
87
|
// Architecture
|
|
83
88
|
{ name: "architecture", fn: () => runArchitecture(cwd) },
|
|
89
|
+
{ name: "performance", fn: () => runPerformance(cwd) },
|
|
84
90
|
// LLM Readiness
|
|
85
91
|
{ name: "confusion", fn: () => runConfusion(cwd) },
|
|
86
92
|
{ name: "context", fn: () => runContext(cwd) },
|
|
93
|
+
// AI Analysis (premium)
|
|
94
|
+
{ name: "doc-coherence", fn: () => runDocCoherence(cwd) },
|
|
95
|
+
{ name: "code-coherence", fn: () => runCodeCoherence(cwd) },
|
|
87
96
|
];
|
|
88
97
|
for (const runner of runners) {
|
|
89
98
|
if (!jsonOnly)
|
|
@@ -91,10 +100,12 @@ async function main() {
|
|
|
91
100
|
const result = runner.fn();
|
|
92
101
|
checks.push(result);
|
|
93
102
|
if (!jsonOnly) {
|
|
94
|
-
const
|
|
95
|
-
const
|
|
96
|
-
const
|
|
97
|
-
const
|
|
103
|
+
const det = result.details;
|
|
104
|
+
const skipped = det.skipped;
|
|
105
|
+
const premium = det.comingSoon;
|
|
106
|
+
const c = premium ? "\x1b[2m" : skipped ? "\x1b[2m" : color(result.grade);
|
|
107
|
+
const label = premium ? "soon" : skipped ? "skip" : result.grade;
|
|
108
|
+
const scoreStr = premium ? "PRO" : skipped ? "—" : `${result.score}/100`;
|
|
98
109
|
const issueStr = result.issues.length > 0 ? ` \x1b[2m${result.issues.length} issues\x1b[0m` : "";
|
|
99
110
|
console.log(`${c}${label.padEnd(5)}${scoreStr}\x1b[0m \x1b[2m${result.duration}ms\x1b[0m${issueStr}`);
|
|
100
111
|
}
|
|
@@ -136,13 +147,25 @@ async function main() {
|
|
|
136
147
|
}
|
|
137
148
|
}
|
|
138
149
|
writeFileSync(join(outputDir, "report.json"), JSON.stringify(report, null, 2));
|
|
139
|
-
|
|
150
|
+
// Generate multi-page HTML report
|
|
151
|
+
const reportDir = join(outputDir, "report");
|
|
152
|
+
if (!existsSync(reportDir))
|
|
153
|
+
mkdirSync(reportDir, { recursive: true });
|
|
154
|
+
const pages = generatePages(report, historyDir);
|
|
155
|
+
for (const [filename, html] of pages) {
|
|
156
|
+
writeFileSync(join(reportDir, filename), html);
|
|
157
|
+
}
|
|
140
158
|
// Badge SVG
|
|
141
159
|
if (badgeMode) {
|
|
142
160
|
const { buildBadge } = await import("./report/svg.js");
|
|
143
161
|
const badgeSvg = buildBadge(score, grade);
|
|
144
162
|
writeFileSync(join(outputDir, "badge.svg"), badgeSvg);
|
|
145
163
|
}
|
|
164
|
+
// SARIF output for GitHub Code Scanning
|
|
165
|
+
if (sarifMode) {
|
|
166
|
+
const { generateSARIF } = await import("./report/sarif.js");
|
|
167
|
+
writeFileSync(join(outputDir, "report.sarif"), generateSARIF(report));
|
|
168
|
+
}
|
|
146
169
|
if (jsonOnly) {
|
|
147
170
|
console.log(JSON.stringify(report));
|
|
148
171
|
}
|
|
@@ -153,10 +176,12 @@ async function main() {
|
|
|
153
176
|
if (trend)
|
|
154
177
|
console.log(formatTrend(trend));
|
|
155
178
|
console.log("");
|
|
156
|
-
console.log(` \x1b[2mReport: ${join(outputDir, "report.html")}\x1b[0m`);
|
|
179
|
+
console.log(` \x1b[2mReport: ${join(outputDir, "report/index.html")}\x1b[0m`);
|
|
157
180
|
console.log(` \x1b[2mJSON: ${join(outputDir, "report.json")}\x1b[0m`);
|
|
158
181
|
if (badgeMode)
|
|
159
182
|
console.log(` \x1b[2mBadge: ${join(outputDir, "badge.svg")}\x1b[0m`);
|
|
183
|
+
if (sarifMode)
|
|
184
|
+
console.log(` \x1b[2mSARIF: ${join(outputDir, "report.sarif")}\x1b[0m`);
|
|
160
185
|
console.log("");
|
|
161
186
|
}
|
|
162
187
|
if (ciMode && score < 60) {
|
|
@@ -166,7 +191,7 @@ async function main() {
|
|
|
166
191
|
try {
|
|
167
192
|
const { execFileSync } = await import("node:child_process");
|
|
168
193
|
const openCmd = process.platform === "darwin" ? "open" : "xdg-open";
|
|
169
|
-
execFileSync(openCmd, [join(outputDir, "report.html")], { stdio: "ignore" });
|
|
194
|
+
execFileSync(openCmd, [join(outputDir, "report/index.html")], { stdio: "ignore" });
|
|
170
195
|
}
|
|
171
196
|
catch {
|
|
172
197
|
/* failed to open browser */
|
package/dist/detect.js
CHANGED
|
@@ -12,9 +12,31 @@ export function detectStack(cwd) {
|
|
|
12
12
|
return "";
|
|
13
13
|
}
|
|
14
14
|
};
|
|
15
|
+
// ── Dart/Flutter detection ──
|
|
16
|
+
const pubspec = read("pubspec.yaml");
|
|
17
|
+
if (pubspec || has("pubspec.lock")) {
|
|
18
|
+
const isFlutter = pubspec.includes("flutter:") || pubspec.includes("flutter_test:");
|
|
19
|
+
const hasTest = pubspec.includes("test:") || pubspec.includes("flutter_test:");
|
|
20
|
+
const hasAnalysis = has("analysis_options.yaml");
|
|
21
|
+
return {
|
|
22
|
+
language: "dart",
|
|
23
|
+
framework: isFlutter ? "flutter" : "none",
|
|
24
|
+
bundler: "none",
|
|
25
|
+
testRunner: isFlutter ? (hasTest ? "flutter_test" : "none") : hasTest ? "dart_test" : "none",
|
|
26
|
+
linter: hasAnalysis ? "dart_analyze" : "none",
|
|
27
|
+
packageManager: "pub",
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
// ── Node.js/TypeScript detection ──
|
|
15
31
|
const pkg = read("package.json");
|
|
16
|
-
|
|
17
|
-
|
|
32
|
+
let allDeps = {};
|
|
33
|
+
try {
|
|
34
|
+
const deps = pkg ? JSON.parse(pkg) : {};
|
|
35
|
+
allDeps = { ...deps.dependencies, ...deps.devDependencies };
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// invalid package.json
|
|
39
|
+
}
|
|
18
40
|
const language = has("tsconfig.json") || has("tsconfig.app.json") || allDeps.typescript
|
|
19
41
|
? "typescript"
|
|
20
42
|
: allDeps.react || allDeps.vue
|
package/dist/fs-utils.d.ts
CHANGED
|
@@ -21,3 +21,7 @@ export declare function getTestFiles(cwd: string): SourceFile[];
|
|
|
21
21
|
export declare function readSafe(cwd: string, path: string): string;
|
|
22
22
|
/** Parse package.json dependencies. */
|
|
23
23
|
export declare function readDeps(cwd: string): Record<string, string>;
|
|
24
|
+
/** Walk from cwd root (not just src/) — for checks like secrets that scan all project files. */
|
|
25
|
+
export declare function collectAllFiles(cwd: string, opts?: {
|
|
26
|
+
extraExts?: boolean;
|
|
27
|
+
}): SourceFile[];
|
package/dist/fs-utils.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
/** Shared filesystem utilities — eliminates duplicate file-walking across runners. */
|
|
2
2
|
import { lstatSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
3
3
|
import { basename, extname, join } from "node:path";
|
|
4
|
-
const SKIP_DIRS = new Set(["node_modules", "dist", ".git", ".vibe-check", "coverage", "test-results", "__pycache__"]);
|
|
5
|
-
const CODE_EXTS = new Set([".ts", ".tsx", ".js", ".jsx"]);
|
|
4
|
+
const SKIP_DIRS = new Set(["node_modules", "dist", ".git", ".vibe-check", "coverage", "test-results", "__pycache__", ".dart_tool", "build", ".flutter-plugins"]);
|
|
5
|
+
const CODE_EXTS = new Set([".ts", ".tsx", ".js", ".jsx", ".dart"]);
|
|
6
6
|
const ALL_EXTS = new Set([...CODE_EXTS, ".json", ".env", ".yaml", ".yml", ".toml"]);
|
|
7
7
|
/** Walk source directories and return all code files. */
|
|
8
8
|
export function collectSourceFiles(cwd, opts) {
|
|
9
9
|
const files = [];
|
|
10
|
-
const dirs = ["src", "web/src"];
|
|
10
|
+
const dirs = ["src", "web/src", "lib"];
|
|
11
|
+
if (opts?.includeTests)
|
|
12
|
+
dirs.push("test", "tests", "__tests__");
|
|
11
13
|
for (const dir of dirs) {
|
|
12
14
|
try {
|
|
13
15
|
walk(join(cwd, dir), cwd, files, opts?.extraExts ? ALL_EXTS : CODE_EXTS);
|
|
@@ -16,8 +18,6 @@ export function collectSourceFiles(cwd, opts) {
|
|
|
16
18
|
/* dir doesn't exist */
|
|
17
19
|
}
|
|
18
20
|
}
|
|
19
|
-
if (opts?.includeTests)
|
|
20
|
-
return files;
|
|
21
21
|
return files;
|
|
22
22
|
}
|
|
23
23
|
/** Get only production source files (no tests). */
|
|
@@ -50,6 +50,12 @@ export function readDeps(cwd) {
|
|
|
50
50
|
return {};
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
|
+
/** Walk from cwd root (not just src/) — for checks like secrets that scan all project files. */
|
|
54
|
+
export function collectAllFiles(cwd, opts) {
|
|
55
|
+
const files = [];
|
|
56
|
+
walk(cwd, cwd, files, opts?.extraExts ? ALL_EXTS : CODE_EXTS);
|
|
57
|
+
return files;
|
|
58
|
+
}
|
|
53
59
|
function walk(dir, cwd, out, exts) {
|
|
54
60
|
for (const entry of readdirSync(dir)) {
|
|
55
61
|
if (SKIP_DIRS.has(entry))
|
|
@@ -70,7 +76,7 @@ function walk(dir, cwd, out, exts) {
|
|
|
70
76
|
continue;
|
|
71
77
|
const content = readFileSync(full, "utf-8");
|
|
72
78
|
const relPath = full.replace(`${cwd}/`, "");
|
|
73
|
-
const isTest = entry.includes(".test.") || entry.includes(".spec.") || relPath.includes("__tests__");
|
|
79
|
+
const isTest = entry.includes(".test.") || entry.includes(".spec.") || entry.endsWith("_test.dart") || relPath.includes("__tests__") || relPath.includes("test/");
|
|
74
80
|
out.push({
|
|
75
81
|
path: relPath,
|
|
76
82
|
fullPath: full,
|
package/dist/report/html.d.ts
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
|
-
/** Generate a multi-page
|
|
1
|
+
/** Generate a multi-page HTML report as separate files.
|
|
2
2
|
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* Sidebar:
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
3
|
+
* Layout:
|
|
4
|
+
* Top nav: Logo | Overview | Foundations | Quality | ... | Issues | Files
|
|
5
|
+
* Page-level navigation. Scrollable on mobile.
|
|
6
|
+
* Sidebar: CONTEXTUAL to current page — NOT a duplicate of top nav.
|
|
7
|
+
* Overview: score + category scores
|
|
8
|
+
* Category: individual checks with grades (click to jump)
|
|
9
|
+
* Issues: severity breakdown
|
|
10
|
+
* Files: summary stats
|
|
11
|
+
* Mobile: Hamburger toggles both top nav dropdown and sidebar panel.
|
|
12
12
|
*/
|
|
13
13
|
import type { VibeReport } from "../types.js";
|
|
14
|
+
export declare const GROUPS: {
|
|
15
|
+
id: string;
|
|
16
|
+
label: string;
|
|
17
|
+
file: string;
|
|
18
|
+
checks: string[];
|
|
19
|
+
}[];
|
|
20
|
+
export declare function generatePages(report: VibeReport, historyDir?: string): Map<string, string>;
|
|
14
21
|
export declare function generateHTML(report: VibeReport, historyDir?: string): string;
|