@vibecodeqa/cli 0.17.0 → 0.18.1

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 (42) hide show
  1. package/README.md +73 -63
  2. package/dist/check-meta.d.ts +1 -0
  3. package/dist/check-meta.js +34 -2
  4. package/dist/cli.js +35 -10
  5. package/dist/detect.js +24 -2
  6. package/dist/fs-utils.d.ts +4 -0
  7. package/dist/fs-utils.js +12 -6
  8. package/dist/report/html.d.ts +17 -10
  9. package/dist/report/html.js +106 -73
  10. package/dist/report/pages.d.ts +2 -1
  11. package/dist/report/pages.js +88 -82
  12. package/dist/report/sarif.d.ts +3 -0
  13. package/dist/report/sarif.js +67 -0
  14. package/dist/report/styles.d.ts +1 -1
  15. package/dist/report/styles.js +82 -36
  16. package/dist/runners/architecture.d.ts +2 -0
  17. package/dist/runners/architecture.js +232 -20
  18. package/dist/runners/code-coherence.d.ts +17 -0
  19. package/dist/runners/code-coherence.js +39 -0
  20. package/dist/runners/complexity.js +7 -37
  21. package/dist/runners/confusion.js +3 -31
  22. package/dist/runners/context.js +9 -40
  23. package/dist/runners/dependencies.js +28 -0
  24. package/dist/runners/doc-coherence.d.ts +14 -0
  25. package/dist/runners/doc-coherence.js +48 -0
  26. package/dist/runners/docs.js +7 -32
  27. package/dist/runners/duplication.js +9 -37
  28. package/dist/runners/lint.js +17 -0
  29. package/dist/runners/performance.d.ts +10 -0
  30. package/dist/runners/performance.js +174 -0
  31. package/dist/runners/react.js +15 -10
  32. package/dist/runners/secrets.js +8 -29
  33. package/dist/runners/security.js +15 -38
  34. package/dist/runners/standards.js +3 -36
  35. package/dist/runners/structure.js +35 -55
  36. package/dist/runners/testing.js +2 -36
  37. package/dist/runners/type-safety.d.ts +1 -1
  38. package/dist/runners/type-safety.js +19 -37
  39. package/dist/runners/types-check.d.ts +1 -1
  40. package/dist/runners/types-check.js +38 -20
  41. package/dist/types.d.ts +5 -5
  42. 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. 15 checks. Full report. Zero config.
5
+ One command. 21 checks. Full report. Zero config.
6
6
 
7
7
  ```bash
8
8
  npx @vibecodeqa/cli
9
9
  ```
10
10
 
11
- ![Grade](https://img.shields.io/badge/checks-15-blue) ![TypeScript](https://img.shields.io/badge/TypeScript-first-3178C6) ![License](https://img.shields.io/badge/license-MIT-green)
11
+ ![Grade](https://img.shields.io/badge/checks-21-blue) ![TypeScript](https://img.shields.io/badge/TypeScript-first-3178C6) ![License](https://img.shields.io/badge/license-MIT-green)
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 checks across 6 categories.
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, file heatmaps, and drill-down issue lists — all navigable via sidebar and tab navigation.
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, innerHTML), config hygiene |
64
+ | **Standards** | 3% | File naming, large files (>300 lines), code smells (console.log, var, ==, eval), config hygiene |
57
65
 
58
- ### Quality (15%)
66
+ ### Quality (23%)
59
67
 
60
68
  | Check | Weight | What it measures |
61
69
  |-------|--------|-----------------|
62
- | **Complexity** | 7% | Cognitive complexity per function, functions >60 lines |
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 (22%)
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 (7%)
88
+ ### Architecture (10%)
78
89
 
79
90
  | Check | Weight | What it measures |
80
91
  |-------|--------|-----------------|
81
- | **Architecture** | 7% | Import graph, circular deps, god modules, orphan files, fan-out, SVG diagram |
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 (18%)
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** | 7% | 15 CWE-mapped patterns (XSS, injection, crypto, SSRF) |
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
- ### LLM Readiness (15%)
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** | 8% | File name similarity, generic names, export collisions, ambiguous abbreviations |
98
- | **Context Locality** | 7% | Token density, import depth, circular deps, context sinks |
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
- **Research backing:**
101
- - "When Names Disappear" (arXiv:2510.03178): GPT-4o drops 28.6% on summarization with ambiguous names
102
- - "Lost in the Middle" (Liu et al. 2023): 30%+ accuracy drop for mid-context information
103
- - "Context Rot" (Chroma 2025): all frontier models degrade with input length
104
- - "Variable Naming Impact" (Research Square 2024): descriptive names = 8.9% better AI performance
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
- The report is a multi-page navigable dashboard:
121
-
122
- - **10 pages**: Overview, Foundations, Quality, Testing, Architecture, Security, LLM Readiness, Issues, File Map, Heatmap
123
- - **Top nav + sidebar** — navigate by category and check
124
- - **Radar chart** — 6-axis view of category scores
125
- - **Architecture SVG diagram** — modules grouped by directory, import edges, node size by fan-in
126
- - **Code heatmap** — colored bars showing issue density per file
127
- - **Trend comparison** — score delta vs. previous run (reads previous report.json)
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** — 📋 button on every issue copies a fix prompt for Claude/Codex
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
- ## References
161
-
162
- Standards and research cited in vibe-check's analysis:
163
-
164
- - ISO/IEC 25010:2023 — Software product quality model
165
- - McCabe, T.J. "A Complexity Measure" (IEEE, 1976) — Cyclomatic complexity
166
- - Campbell, G.A. "Cognitive Complexity" (SonarSource, 2017) — Understandability metric
167
- - Martin, R.C. "Clean Code" (2008) — Naming principles
168
- - OWASP Top 10 — Web application security risks
169
- - CWE Top 25 — Most dangerous software weaknesses
170
- - Liu et al. "Lost in the Middle" (TACL, 2023) — LLM context attention
171
- - Chroma Research "Context Rot" (2025) — LLM degradation with input length
172
- - Wang et al. "How Does Naming Affect LLMs?" (JSEA, 2024) — Naming impact on AI
173
- - arXiv:2510.03178 "When Names Disappear" (2025) — 28.6% accuracy drop
174
- - Arnaoudova et al. "Linguistic Antipatterns" — 17-pattern naming catalog
175
- - Vassilev "Codified Context" (arXiv, 2025) — AI agent context architecture
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/freeappstore-online/vibe-check
192
+ - **GitHub:** https://github.com/vibecodeqa/cli
184
193
  - **Website:** https://vibecodeqa.online
185
- - **Issues:** https://github.com/freeappstore-online/vibe-check/issues
194
+ - **npm:** https://www.npmjs.com/package/@vibecodeqa/cli
195
+ - **Issues:** https://github.com/vibecodeqa/cli/issues
@@ -10,6 +10,7 @@ export interface CheckMeta {
10
10
  description: string;
11
11
  risk: string;
12
12
  recommendation: string;
13
+ premium?: boolean;
13
14
  }
14
15
  export declare const CHECK_META: Record<string, CheckMeta>;
15
16
  export declare function getCheckMeta(name: string): CheckMeta;
@@ -96,7 +96,7 @@ export const CHECK_META = {
96
96
  label: "Testing",
97
97
  category: "Testing",
98
98
  priority: "critical",
99
- weight: 17,
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: 7,
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 { generateHTML } from "./report/html.js";
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 skipped = result.details.skipped;
95
- const c = skipped ? "\x1b[2m" : color(result.grade);
96
- const label = skipped ? "skip" : result.grade;
97
- const scoreStr = skipped ? "" : `${result.score}/100`;
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
- writeFileSync(join(outputDir, "report.html"), generateHTML(report, historyDir));
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
- const deps = pkg ? JSON.parse(pkg) : {};
17
- const allDeps = { ...deps.dependencies, ...deps.devDependencies };
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
@@ -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,
@@ -1,14 +1,21 @@
1
- /** Generate a multi-page navigable HTML report.
1
+ /** Generate a multi-page HTML report as separate files.
2
2
  *
3
- * Architecture:
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)
10
- *
11
- * All in one self-contained HTML file using show/hide navigation.
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;