@vibecodeqa/cli 0.9.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/LICENSE +21 -0
- package/README.md +174 -0
- package/dist/check-meta.d.ts +15 -0
- package/dist/check-meta.js +166 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +140 -0
- package/dist/detect.d.ts +8 -0
- package/dist/detect.js +67 -0
- package/dist/fs-utils.d.ts +23 -0
- package/dist/fs-utils.js +77 -0
- package/dist/report/html.d.ts +12 -0
- package/dist/report/html.js +400 -0
- package/dist/runners/architecture.d.ts +28 -0
- package/dist/runners/architecture.js +272 -0
- package/dist/runners/complexity.d.ts +3 -0
- package/dist/runners/complexity.js +152 -0
- package/dist/runners/confusion.d.ts +16 -0
- package/dist/runners/confusion.js +198 -0
- package/dist/runners/context.d.ts +15 -0
- package/dist/runners/context.js +200 -0
- package/dist/runners/coverage.d.ts +3 -0
- package/dist/runners/coverage.js +65 -0
- package/dist/runners/dependencies.d.ts +3 -0
- package/dist/runners/dependencies.js +106 -0
- package/dist/runners/docs.d.ts +3 -0
- package/dist/runners/docs.js +97 -0
- package/dist/runners/duplication.d.ts +3 -0
- package/dist/runners/duplication.js +100 -0
- package/dist/runners/exec.d.ts +6 -0
- package/dist/runners/exec.js +25 -0
- package/dist/runners/lint.d.ts +3 -0
- package/dist/runners/lint.js +78 -0
- package/dist/runners/secrets.d.ts +3 -0
- package/dist/runners/secrets.js +108 -0
- package/dist/runners/security.d.ts +3 -0
- package/dist/runners/security.js +121 -0
- package/dist/runners/standards.d.ts +3 -0
- package/dist/runners/standards.js +153 -0
- package/dist/runners/structure.d.ts +3 -0
- package/dist/runners/structure.js +110 -0
- package/dist/runners/testing.d.ts +12 -0
- package/dist/runners/testing.js +401 -0
- package/dist/runners/tests.d.ts +3 -0
- package/dist/runners/tests.js +54 -0
- package/dist/runners/type-safety.d.ts +3 -0
- package/dist/runners/type-safety.js +74 -0
- package/dist/runners/types-check.d.ts +3 -0
- package/dist/runners/types-check.js +44 -0
- package/dist/score.d.ts +6 -0
- package/dist/score.js +19 -0
- package/dist/trend.d.ts +19 -0
- package/dist/trend.js +63 -0
- package/dist/types.d.ts +40 -0
- package/dist/types.js +12 -0
- package/package.json +53 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 freeappstore.online
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# vibe-check
|
|
2
|
+
|
|
3
|
+
**Code health scanner for the AI coding era.**
|
|
4
|
+
|
|
5
|
+
One command. 15 checks. Full report. Zero config.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx vibe-check
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
  
|
|
12
|
+
|
|
13
|
+
## What it does
|
|
14
|
+
|
|
15
|
+
vibe-check 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.
|
|
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.
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Scan current directory (runs tests + coverage)
|
|
23
|
+
npx vibe-check
|
|
24
|
+
|
|
25
|
+
# Fast mode (skip test execution)
|
|
26
|
+
npx vibe-check --skip-tests
|
|
27
|
+
|
|
28
|
+
# CI mode (exit code 1 if score < 60)
|
|
29
|
+
npx vibe-check --ci
|
|
30
|
+
|
|
31
|
+
# JSON output (pipe to other tools)
|
|
32
|
+
npx vibe-check --json
|
|
33
|
+
|
|
34
|
+
# Scan a specific directory
|
|
35
|
+
npx vibe-check /path/to/project
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Output goes to `.vibe-check/`:
|
|
39
|
+
- `report.html` — navigable dashboard (open in browser)
|
|
40
|
+
- `report.json` — machine-readable results
|
|
41
|
+
|
|
42
|
+
## Checks
|
|
43
|
+
|
|
44
|
+
### Foundations (23%)
|
|
45
|
+
|
|
46
|
+
| Check | Weight | What it measures |
|
|
47
|
+
|-------|--------|-----------------|
|
|
48
|
+
| **Structure** | 6% | Standard files (package.json, tsconfig, LICENSE, README, .gitignore), lockfile, test-to-source ratio |
|
|
49
|
+
| **Lint** | 5% | Biome or ESLint errors/warnings (auto-detected) |
|
|
50
|
+
| **Types** | 6% | TypeScript compilation errors (`tsc --noEmit`) |
|
|
51
|
+
| **Type Safety** | 3% | `as any`, `: any`, `@ts-ignore`, `@ts-nocheck` counts |
|
|
52
|
+
| **Standards** | 3% | File naming, large files (>300 lines), code smells (console.log, var, ==, eval, innerHTML), config hygiene |
|
|
53
|
+
|
|
54
|
+
### Quality (15%)
|
|
55
|
+
|
|
56
|
+
| Check | Weight | What it measures |
|
|
57
|
+
|-------|--------|-----------------|
|
|
58
|
+
| **Complexity** | 7% | Cognitive complexity per function, functions >60 lines |
|
|
59
|
+
| **Duplication** | 5% | Copy-pasted 6+ line blocks |
|
|
60
|
+
| **Docs** | 3% | README quality, JSDoc coverage of exports |
|
|
61
|
+
|
|
62
|
+
### Testing (22%)
|
|
63
|
+
|
|
64
|
+
One deep check with 6 sub-dimensions:
|
|
65
|
+
|
|
66
|
+
- **Pyramid presence** — unit, integration, component, E2E layers detected
|
|
67
|
+
- **Execution** — pass/fail from vitest/jest
|
|
68
|
+
- **Coverage** — statement, branch, line, function (v8/istanbul)
|
|
69
|
+
- **File pairing** — test file per source file
|
|
70
|
+
- **Quality** — assertion density, mock ratio, snapshot ratio
|
|
71
|
+
- **E2E detection** — Playwright/Cypress configured?
|
|
72
|
+
|
|
73
|
+
### Architecture (7%)
|
|
74
|
+
|
|
75
|
+
| Check | Weight | What it measures |
|
|
76
|
+
|-------|--------|-----------------|
|
|
77
|
+
| **Architecture** | 7% | Import graph, circular deps, god modules, orphan files, fan-out, SVG diagram |
|
|
78
|
+
|
|
79
|
+
### Security (18%)
|
|
80
|
+
|
|
81
|
+
| Check | Weight | What it measures |
|
|
82
|
+
|-------|--------|-----------------|
|
|
83
|
+
| **Secrets** | 6% | 13 patterns (AWS, GitHub, Stripe, OpenAI, private keys) |
|
|
84
|
+
| **Security** | 7% | 15 CWE-mapped patterns (XSS, injection, crypto, SSRF) |
|
|
85
|
+
| **Dependencies** | 5% | npm audit vulnerabilities + outdated packages |
|
|
86
|
+
|
|
87
|
+
### LLM Readiness (15%)
|
|
88
|
+
|
|
89
|
+
Novel checks that no other tool offers:
|
|
90
|
+
|
|
91
|
+
| Check | Weight | What it measures |
|
|
92
|
+
|-------|--------|-----------------|
|
|
93
|
+
| **Confusion Index** | 8% | File name similarity, generic names, export collisions, ambiguous abbreviations |
|
|
94
|
+
| **Context Locality** | 7% | Token density, import depth, circular deps, context sinks |
|
|
95
|
+
|
|
96
|
+
**Research backing:**
|
|
97
|
+
- "When Names Disappear" (arXiv:2510.03178): GPT-4o drops 28.6% on summarization with ambiguous names
|
|
98
|
+
- "Lost in the Middle" (Liu et al. 2023): 30%+ accuracy drop for mid-context information
|
|
99
|
+
- "Context Rot" (Chroma 2025): all frontier models degrade with input length
|
|
100
|
+
- "Variable Naming Impact" (Research Square 2024): descriptive names = 8.9% better AI performance
|
|
101
|
+
|
|
102
|
+
## Scoring
|
|
103
|
+
|
|
104
|
+
Each check produces a score from 0-100. The composite score is a weighted average (weights shown above, sum to 100%). Grades:
|
|
105
|
+
|
|
106
|
+
| Grade | Score | Meaning |
|
|
107
|
+
|-------|-------|---------|
|
|
108
|
+
| **A** | 90-100 | Excellent — production-ready |
|
|
109
|
+
| **B** | 75-89 | Good — minor issues |
|
|
110
|
+
| **C** | 60-74 | Fair — needs attention |
|
|
111
|
+
| **D** | 40-59 | Poor — significant issues |
|
|
112
|
+
| **F** | 0-39 | Critical — major problems |
|
|
113
|
+
|
|
114
|
+
## Report features
|
|
115
|
+
|
|
116
|
+
- **Radar chart** — 6-axis view of category scores
|
|
117
|
+
- **Architecture diagram** — SVG showing module relationships and import edges
|
|
118
|
+
- **Trend comparison** — score delta vs. previous run
|
|
119
|
+
- **File heatmap** — top files by issue count across all checks
|
|
120
|
+
- **GitHub links** — click any file:line to open in GitHub
|
|
121
|
+
- **Info panels** — each check explains what it measures, why it matters, and how to fix issues
|
|
122
|
+
- **Priority badges** — critical/high/medium/low on each check
|
|
123
|
+
|
|
124
|
+
## Trend tracking
|
|
125
|
+
|
|
126
|
+
vibe-check reads the previous `.vibe-check/report.json` on each run and shows:
|
|
127
|
+
- Score change (↑ improved / ↓ declined)
|
|
128
|
+
- Per-check deltas
|
|
129
|
+
- New vs. fixed issue counts
|
|
130
|
+
|
|
131
|
+
## CLI options
|
|
132
|
+
|
|
133
|
+
| Flag | Description |
|
|
134
|
+
|------|-------------|
|
|
135
|
+
| `--skip-tests` | Skip test execution and coverage (fast mode) |
|
|
136
|
+
| `--ci` | Exit code 1 if composite score < 60 |
|
|
137
|
+
| `--json` | Output JSON to stdout (no HTML, no browser) |
|
|
138
|
+
|
|
139
|
+
## Stack detection
|
|
140
|
+
|
|
141
|
+
Auto-detects from `package.json` and config files:
|
|
142
|
+
- **Language:** TypeScript, JavaScript
|
|
143
|
+
- **Framework:** React, Vue, Svelte
|
|
144
|
+
- **Bundler:** Vite, Webpack, esbuild
|
|
145
|
+
- **Test runner:** vitest, jest
|
|
146
|
+
- **Linter:** Biome, ESLint
|
|
147
|
+
- **Package manager:** pnpm, npm, yarn, bun
|
|
148
|
+
|
|
149
|
+
## References
|
|
150
|
+
|
|
151
|
+
Standards and research cited in vibe-check's analysis:
|
|
152
|
+
|
|
153
|
+
- ISO/IEC 25010:2023 — Software product quality model
|
|
154
|
+
- McCabe, T.J. "A Complexity Measure" (IEEE, 1976) — Cyclomatic complexity
|
|
155
|
+
- Campbell, G.A. "Cognitive Complexity" (SonarSource, 2017) — Understandability metric
|
|
156
|
+
- Martin, R.C. "Clean Code" (2008) — Naming principles
|
|
157
|
+
- OWASP Top 10 — Web application security risks
|
|
158
|
+
- CWE Top 25 — Most dangerous software weaknesses
|
|
159
|
+
- Liu et al. "Lost in the Middle" (TACL, 2023) — LLM context attention
|
|
160
|
+
- Chroma Research "Context Rot" (2025) — LLM degradation with input length
|
|
161
|
+
- Wang et al. "How Does Naming Affect LLMs?" (JSEA, 2024) — Naming impact on AI
|
|
162
|
+
- arXiv:2510.03178 "When Names Disappear" (2025) — 28.6% accuracy drop
|
|
163
|
+
- Arnaoudova et al. "Linguistic Antipatterns" — 17-pattern naming catalog
|
|
164
|
+
- Vassilev "Codified Context" (arXiv, 2025) — AI agent context architecture
|
|
165
|
+
|
|
166
|
+
## License
|
|
167
|
+
|
|
168
|
+
MIT — Free forever as a CLI tool.
|
|
169
|
+
|
|
170
|
+
## Links
|
|
171
|
+
|
|
172
|
+
- **GitHub:** https://github.com/freeappstore-online/vibe-check
|
|
173
|
+
- **Website:** https://vibechecker.online (coming soon)
|
|
174
|
+
- **Issues:** https://github.com/freeappstore-online/vibe-check/issues
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Metadata for each check — description, risk, priority, weight, recommendations.
|
|
2
|
+
* This is what makes the report educational, not just a scorecard. */
|
|
3
|
+
export type Priority = "critical" | "high" | "medium" | "low";
|
|
4
|
+
export interface CheckMeta {
|
|
5
|
+
name: string;
|
|
6
|
+
label: string;
|
|
7
|
+
category: string;
|
|
8
|
+
priority: Priority;
|
|
9
|
+
weight: number;
|
|
10
|
+
description: string;
|
|
11
|
+
risk: string;
|
|
12
|
+
recommendation: string;
|
|
13
|
+
}
|
|
14
|
+
export declare const CHECK_META: Record<string, CheckMeta>;
|
|
15
|
+
export declare function getCheckMeta(name: string): CheckMeta;
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/** Metadata for each check — description, risk, priority, weight, recommendations.
|
|
2
|
+
* This is what makes the report educational, not just a scorecard. */
|
|
3
|
+
export const CHECK_META = {
|
|
4
|
+
structure: {
|
|
5
|
+
name: "structure",
|
|
6
|
+
label: "Project Structure",
|
|
7
|
+
category: "Foundations",
|
|
8
|
+
priority: "high",
|
|
9
|
+
weight: 6,
|
|
10
|
+
description: "Checks for standard project files: package.json, tsconfig.json, LICENSE, README, .gitignore, lockfile. Verifies test-to-source file ratio and that essential scripts (test, build) exist.",
|
|
11
|
+
risk: "Missing config files cause build failures in CI. Missing LICENSE makes the project legally ambiguous. No lockfile means non-reproducible builds — a dependency update can break production silently.",
|
|
12
|
+
recommendation: "Ensure every project has package.json, tsconfig.json, LICENSE, .gitignore, and a lockfile. Add 'test' and 'build' scripts. Aim for at least one test file per source file.",
|
|
13
|
+
},
|
|
14
|
+
lint: {
|
|
15
|
+
name: "lint",
|
|
16
|
+
label: "Lint",
|
|
17
|
+
category: "Foundations",
|
|
18
|
+
priority: "high",
|
|
19
|
+
weight: 5,
|
|
20
|
+
description: "Runs the project's linter (Biome or ESLint, auto-detected) and counts errors and warnings. Lint rules catch bugs, enforce consistency, and prevent common mistakes before they reach production.",
|
|
21
|
+
risk: "Unlinted code accumulates inconsistencies and latent bugs. Studies show that projects with active linting have 15-20% fewer production defects (Microsoft Research, 2019).",
|
|
22
|
+
recommendation: "Fix all lint errors. Warnings can be addressed incrementally. If no linter is configured, add Biome (@biomejs/biome) — it's the fastest linter for TypeScript with zero config needed.",
|
|
23
|
+
},
|
|
24
|
+
types: {
|
|
25
|
+
name: "types",
|
|
26
|
+
label: "Type Check",
|
|
27
|
+
category: "Foundations",
|
|
28
|
+
priority: "critical",
|
|
29
|
+
weight: 6,
|
|
30
|
+
description: "Runs tsc --noEmit to find TypeScript compilation errors. Type errors mean the code may crash at runtime in ways the compiler could have prevented.",
|
|
31
|
+
risk: "Type errors are bugs. Every unresolved type error is a potential runtime crash. TypeScript's type system exists to prevent entire categories of bugs — ignoring it negates its value.",
|
|
32
|
+
recommendation: "Fix all type errors. If you're migrating from JavaScript, enable strict mode gradually — start with 'strict: true' and fix errors file by file.",
|
|
33
|
+
},
|
|
34
|
+
"type-safety": {
|
|
35
|
+
name: "type-safety",
|
|
36
|
+
label: "Type Safety",
|
|
37
|
+
category: "Foundations",
|
|
38
|
+
priority: "medium",
|
|
39
|
+
weight: 3,
|
|
40
|
+
description: "Counts unsafe type patterns: 'as any' casts, explicit ': any' annotations, @ts-ignore directives, @ts-nocheck, and non-null assertions (!.). Each weakens the type system's protection.",
|
|
41
|
+
risk: "'as any' silences the type checker at that point — any bug the types would have caught now slips through. @ts-ignore and @ts-nocheck disable type checking entirely for a line or file. Accumulated 'any' usage correlates with higher defect density.",
|
|
42
|
+
recommendation: "Replace 'as any' with proper types or type guards. Use 'unknown' instead of 'any' when the type is genuinely unknown. Remove @ts-ignore comments by fixing the underlying type issue.",
|
|
43
|
+
},
|
|
44
|
+
standards: {
|
|
45
|
+
name: "standards",
|
|
46
|
+
label: "Code Standards",
|
|
47
|
+
category: "Foundations",
|
|
48
|
+
priority: "medium",
|
|
49
|
+
weight: 3,
|
|
50
|
+
description: "Checks coding conventions: file naming (PascalCase for components, kebab-case for modules), file size limits (>300 lines flagged), code smells (console.log, var, ==, eval, innerHTML, TODO/FIXME), config hygiene (strict mode), and framework best practices (Tailwind vs inline styles).",
|
|
51
|
+
risk: "Large files are hard to review and test. console.log in production leaks internal data. var causes hoisting bugs. == causes type coercion surprises. eval/innerHTML are security vulnerabilities. Inconsistent naming makes the codebase harder to navigate.",
|
|
52
|
+
recommendation: "Split files over 300 lines. Replace console.log with a proper logger or remove it. Use const/let, ===, and safe DOM APIs. Enable TypeScript strict mode.",
|
|
53
|
+
},
|
|
54
|
+
complexity: {
|
|
55
|
+
name: "complexity",
|
|
56
|
+
label: "Complexity",
|
|
57
|
+
category: "Quality",
|
|
58
|
+
priority: "high",
|
|
59
|
+
weight: 7,
|
|
60
|
+
description: "Measures cognitive complexity of each function: how many branches (if/else/switch/for/while/ternary/&&/||) and how many lines. Functions over 60 lines or with complexity over 15 are flagged.",
|
|
61
|
+
risk: "Complex functions are the #1 source of bugs. Research shows defect density increases exponentially with cyclomatic complexity above 10 (McCabe, 1976). Complex code is also harder to review, test, and modify safely.",
|
|
62
|
+
recommendation: "Extract complex functions into smaller ones. Use early returns to reduce nesting. Replace conditional chains with lookup tables or strategy patterns. Aim for functions under 30 lines with complexity under 10.",
|
|
63
|
+
},
|
|
64
|
+
duplication: {
|
|
65
|
+
name: "duplication",
|
|
66
|
+
label: "Duplication",
|
|
67
|
+
category: "Quality",
|
|
68
|
+
priority: "medium",
|
|
69
|
+
weight: 5,
|
|
70
|
+
description: "Detects copy-pasted code blocks of 6+ lines across source files. Duplication is measured as a percentage of total source lines involved in duplicate blocks.",
|
|
71
|
+
risk: "Duplicated code means bugs must be fixed in multiple places. Miss one copy and the bug persists. DRY (Don't Repeat Yourself) violations increase maintenance cost linearly with each copy.",
|
|
72
|
+
recommendation: "Extract duplicated logic into shared functions or modules. If two files share the same pattern, create a helper. If the duplication is across repos, consider vendoring a shared module.",
|
|
73
|
+
},
|
|
74
|
+
docs: {
|
|
75
|
+
name: "docs",
|
|
76
|
+
label: "Documentation",
|
|
77
|
+
category: "Quality",
|
|
78
|
+
priority: "low",
|
|
79
|
+
weight: 3,
|
|
80
|
+
description: "Checks README quality (existence, length, sections) and JSDoc coverage (what percentage of exported functions/classes have documentation comments).",
|
|
81
|
+
risk: "Undocumented code is hard to onboard to and easy to misuse. Missing README means new contributors can't get started. Undocumented exports become tribal knowledge that leaves when people leave.",
|
|
82
|
+
recommendation: "Write a README with: what it does, how to install, how to run, how to develop. Add JSDoc comments to all public exports — even a one-line description helps.",
|
|
83
|
+
},
|
|
84
|
+
testing: {
|
|
85
|
+
name: "testing",
|
|
86
|
+
label: "Testing",
|
|
87
|
+
category: "Testing",
|
|
88
|
+
priority: "critical",
|
|
89
|
+
weight: 22,
|
|
90
|
+
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).",
|
|
91
|
+
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.",
|
|
92
|
+
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.",
|
|
93
|
+
},
|
|
94
|
+
secrets: {
|
|
95
|
+
name: "secrets",
|
|
96
|
+
label: "Secrets",
|
|
97
|
+
category: "Security",
|
|
98
|
+
priority: "critical",
|
|
99
|
+
weight: 6,
|
|
100
|
+
description: "Scans source files for hardcoded secrets: AWS keys, GitHub tokens, Stripe keys, OpenAI/Anthropic API keys, Google API keys, private keys, and generic secret patterns. Checks 13 regex patterns against every non-test source file.",
|
|
101
|
+
risk: "Hardcoded secrets in source code are the #1 cause of credential leaks. Once pushed to Git, secrets are in the history forever — even if deleted in a later commit. Leaked API keys can be exploited within minutes by automated scanners.",
|
|
102
|
+
recommendation: "Never hardcode secrets. Use environment variables or a secret manager (Bitwarden, AWS Secrets Manager, Cloudflare Secrets). If a secret was committed, rotate it immediately — deleting the file is not enough.",
|
|
103
|
+
},
|
|
104
|
+
security: {
|
|
105
|
+
name: "security",
|
|
106
|
+
label: "Security Patterns",
|
|
107
|
+
category: "Security",
|
|
108
|
+
priority: "critical",
|
|
109
|
+
weight: 7,
|
|
110
|
+
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.",
|
|
111
|
+
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.",
|
|
112
|
+
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.",
|
|
113
|
+
},
|
|
114
|
+
dependencies: {
|
|
115
|
+
name: "dependencies",
|
|
116
|
+
label: "Dependencies",
|
|
117
|
+
category: "Security",
|
|
118
|
+
priority: "high",
|
|
119
|
+
weight: 5,
|
|
120
|
+
description: "Runs npm/pnpm audit to find known vulnerabilities (CVEs) in dependencies. Also checks for outdated packages — major version gaps indicate potential security debt and breaking API changes.",
|
|
121
|
+
risk: "Vulnerable dependencies are the most common attack vector for supply chain attacks. 84% of codebases contain at least one known vulnerability in their dependencies (Synopsys OSSRA 2024). Outdated major versions often have unpatched security issues.",
|
|
122
|
+
recommendation: "Run 'pnpm audit' regularly and fix critical/high vulnerabilities immediately. Keep dependencies updated — use Dependabot or Renovate for automated PRs. Pin versions with a lockfile.",
|
|
123
|
+
},
|
|
124
|
+
architecture: {
|
|
125
|
+
name: "architecture",
|
|
126
|
+
label: "Architecture",
|
|
127
|
+
category: "Architecture",
|
|
128
|
+
priority: "high",
|
|
129
|
+
weight: 7,
|
|
130
|
+
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.",
|
|
131
|
+
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.",
|
|
132
|
+
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.",
|
|
133
|
+
},
|
|
134
|
+
confusion: {
|
|
135
|
+
name: "confusion",
|
|
136
|
+
label: "Confusion Index",
|
|
137
|
+
category: "LLM Readiness",
|
|
138
|
+
priority: "high",
|
|
139
|
+
weight: 8,
|
|
140
|
+
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.",
|
|
141
|
+
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.",
|
|
142
|
+
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).",
|
|
143
|
+
},
|
|
144
|
+
context: {
|
|
145
|
+
name: "context",
|
|
146
|
+
label: "Context Locality",
|
|
147
|
+
category: "LLM Readiness",
|
|
148
|
+
priority: "high",
|
|
149
|
+
weight: 7,
|
|
150
|
+
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.",
|
|
151
|
+
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).",
|
|
152
|
+
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.",
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
export function getCheckMeta(name) {
|
|
156
|
+
return CHECK_META[name] || {
|
|
157
|
+
name,
|
|
158
|
+
label: name,
|
|
159
|
+
category: "Other",
|
|
160
|
+
priority: "medium",
|
|
161
|
+
weight: 5,
|
|
162
|
+
description: "",
|
|
163
|
+
risk: "",
|
|
164
|
+
recommendation: "",
|
|
165
|
+
};
|
|
166
|
+
}
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/** vibe-check — code health scanner for the AI coding era. */
|
|
3
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { join, resolve } from "node:path";
|
|
5
|
+
import { detectRepoUrl, detectStack } from "./detect.js";
|
|
6
|
+
import { generateHTML } from "./report/html.js";
|
|
7
|
+
import { runComplexity } from "./runners/complexity.js";
|
|
8
|
+
import { runArchitecture } from "./runners/architecture.js";
|
|
9
|
+
import { runConfusion } from "./runners/confusion.js";
|
|
10
|
+
import { runContext } from "./runners/context.js";
|
|
11
|
+
import { runDependencies } from "./runners/dependencies.js";
|
|
12
|
+
import { runDocs } from "./runners/docs.js";
|
|
13
|
+
import { runDuplication } from "./runners/duplication.js";
|
|
14
|
+
import { runLint } from "./runners/lint.js";
|
|
15
|
+
import { runSecrets } from "./runners/secrets.js";
|
|
16
|
+
import { runSecurity } from "./runners/security.js";
|
|
17
|
+
import { runStandards } from "./runners/standards.js";
|
|
18
|
+
import { runStructure } from "./runners/structure.js";
|
|
19
|
+
import { runTesting } from "./runners/testing.js";
|
|
20
|
+
import { runTypeCheck } from "./runners/types-check.js";
|
|
21
|
+
import { runTypeSafety } from "./runners/type-safety.js";
|
|
22
|
+
import { computeScore } from "./score.js";
|
|
23
|
+
import { computeTrend, formatTrend } from "./trend.js";
|
|
24
|
+
import { gradeFromScore } from "./types.js";
|
|
25
|
+
const VERSION = "0.9.0";
|
|
26
|
+
const args = process.argv.slice(2);
|
|
27
|
+
const flags = new Set(args.filter((a) => a.startsWith("--")));
|
|
28
|
+
const cwd = resolve(args.find((a) => !a.startsWith("--")) || ".");
|
|
29
|
+
const outputDir = join(cwd, ".vibe-check");
|
|
30
|
+
const jsonOnly = flags.has("--json");
|
|
31
|
+
const ciMode = flags.has("--ci");
|
|
32
|
+
const skipTests = flags.has("--skip-tests");
|
|
33
|
+
function color(grade) {
|
|
34
|
+
if (grade === "A")
|
|
35
|
+
return "\x1b[32m";
|
|
36
|
+
if (grade === "B")
|
|
37
|
+
return "\x1b[33m";
|
|
38
|
+
return "\x1b[31m";
|
|
39
|
+
}
|
|
40
|
+
async function main() {
|
|
41
|
+
const start = Date.now();
|
|
42
|
+
if (!jsonOnly) {
|
|
43
|
+
console.log("");
|
|
44
|
+
console.log(" \x1b[1mvibe-check\x1b[0m v" + VERSION);
|
|
45
|
+
console.log(" \x1b[2m" + cwd + "\x1b[0m");
|
|
46
|
+
console.log("");
|
|
47
|
+
}
|
|
48
|
+
const stack = detectStack(cwd);
|
|
49
|
+
if (!jsonOnly) {
|
|
50
|
+
const parts = [stack.language, stack.framework, stack.bundler, stack.testRunner, stack.linter, stack.packageManager].filter((v) => v !== "none" && v !== "unknown");
|
|
51
|
+
console.log(" stack: " + parts.join(" + "));
|
|
52
|
+
console.log("");
|
|
53
|
+
}
|
|
54
|
+
const checks = [];
|
|
55
|
+
// All runners grouped by category
|
|
56
|
+
const runners = [
|
|
57
|
+
// Foundations
|
|
58
|
+
{ name: "structure", fn: () => runStructure(cwd, stack) },
|
|
59
|
+
{ name: "lint", fn: () => runLint(cwd, stack) },
|
|
60
|
+
{ name: "types", fn: () => runTypeCheck(cwd) },
|
|
61
|
+
{ name: "type-safety", fn: () => runTypeSafety(cwd) },
|
|
62
|
+
{ name: "standards", fn: () => runStandards(cwd, stack) },
|
|
63
|
+
// Quality
|
|
64
|
+
{ name: "complexity", fn: () => runComplexity(cwd) },
|
|
65
|
+
{ name: "duplication", fn: () => runDuplication(cwd) },
|
|
66
|
+
{ name: "docs", fn: () => runDocs(cwd) },
|
|
67
|
+
// Testing
|
|
68
|
+
{ name: "testing", fn: () => runTesting(cwd, stack, skipTests) },
|
|
69
|
+
// Security
|
|
70
|
+
{ name: "secrets", fn: () => runSecrets(cwd) },
|
|
71
|
+
{ name: "security", fn: () => runSecurity(cwd) },
|
|
72
|
+
{ name: "dependencies", fn: () => runDependencies(cwd, stack) },
|
|
73
|
+
// Architecture
|
|
74
|
+
{ name: "architecture", fn: () => runArchitecture(cwd) },
|
|
75
|
+
// LLM Readiness
|
|
76
|
+
{ name: "confusion", fn: () => runConfusion(cwd) },
|
|
77
|
+
{ name: "context", fn: () => runContext(cwd) },
|
|
78
|
+
];
|
|
79
|
+
for (const runner of runners) {
|
|
80
|
+
if (!jsonOnly)
|
|
81
|
+
process.stdout.write(" " + runner.name.padEnd(14));
|
|
82
|
+
const result = runner.fn();
|
|
83
|
+
checks.push(result);
|
|
84
|
+
if (!jsonOnly) {
|
|
85
|
+
const skipped = result.details.skipped;
|
|
86
|
+
const c = skipped ? "\x1b[2m" : color(result.grade);
|
|
87
|
+
const label = skipped ? "skip" : result.grade;
|
|
88
|
+
const scoreStr = skipped ? "—" : result.score + "/100";
|
|
89
|
+
const issueStr = result.issues.length > 0 ? ` \x1b[2m${result.issues.length} issues\x1b[0m` : "";
|
|
90
|
+
console.log(`${c}${label.padEnd(5)}${scoreStr}\x1b[0m \x1b[2m${result.duration}ms\x1b[0m${issueStr}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const score = computeScore(checks);
|
|
94
|
+
const grade = gradeFromScore(score);
|
|
95
|
+
const duration = Date.now() - start;
|
|
96
|
+
const totalIssues = checks.reduce((s, c) => s + c.issues.length, 0);
|
|
97
|
+
const report = {
|
|
98
|
+
version: VERSION,
|
|
99
|
+
timestamp: new Date().toISOString(),
|
|
100
|
+
score,
|
|
101
|
+
grade,
|
|
102
|
+
checks,
|
|
103
|
+
meta: { cwd, node: process.version, duration, stack, ...detectRepoUrl(cwd) },
|
|
104
|
+
};
|
|
105
|
+
// Trend comparison (read previous report before overwriting)
|
|
106
|
+
const trend = computeTrend(report, outputDir);
|
|
107
|
+
if (!existsSync(outputDir))
|
|
108
|
+
mkdirSync(outputDir, { recursive: true });
|
|
109
|
+
writeFileSync(join(outputDir, "report.json"), JSON.stringify(report, null, 2));
|
|
110
|
+
writeFileSync(join(outputDir, "report.html"), generateHTML(report));
|
|
111
|
+
if (jsonOnly) {
|
|
112
|
+
console.log(JSON.stringify(report));
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
const gc = color(grade);
|
|
116
|
+
console.log("");
|
|
117
|
+
console.log(` ${gc}\x1b[1m${grade}\x1b[0m ${gc}${score}/100\x1b[0m \x1b[2m${checks.length} checks · ${totalIssues} issues · ${duration}ms\x1b[0m`);
|
|
118
|
+
if (trend)
|
|
119
|
+
console.log(formatTrend(trend));
|
|
120
|
+
console.log("");
|
|
121
|
+
console.log(" \x1b[2mReport: " + join(outputDir, "report.html") + "\x1b[0m");
|
|
122
|
+
console.log(" \x1b[2mJSON: " + join(outputDir, "report.json") + "\x1b[0m");
|
|
123
|
+
console.log("");
|
|
124
|
+
}
|
|
125
|
+
if (ciMode && score < 60) {
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
if (!jsonOnly && !ciMode) {
|
|
129
|
+
try {
|
|
130
|
+
const { execSync } = await import("node:child_process");
|
|
131
|
+
const openCmd = process.platform === "darwin" ? "open" : "xdg-open";
|
|
132
|
+
execSync(`${openCmd} "${join(outputDir, "report.html")}"`, { stdio: "ignore" });
|
|
133
|
+
}
|
|
134
|
+
catch { /* failed to open browser */ }
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
main().catch((err) => {
|
|
138
|
+
console.error("vibe-check error:", err);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
});
|
package/dist/detect.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Auto-detect project stack and git repo from files in the working directory. */
|
|
2
|
+
import type { StackInfo } from "./types.js";
|
|
3
|
+
export declare function detectStack(cwd: string): StackInfo;
|
|
4
|
+
/** Detect GitHub/GitLab repo URL from git remote. */
|
|
5
|
+
export declare function detectRepoUrl(cwd: string): {
|
|
6
|
+
repoUrl: string | null;
|
|
7
|
+
branch: string;
|
|
8
|
+
};
|
package/dist/detect.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/** Auto-detect project stack and git repo from files in the working directory. */
|
|
2
|
+
import { execSync } from "node:child_process";
|
|
3
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
export function detectStack(cwd) {
|
|
6
|
+
const has = (f) => existsSync(join(cwd, f));
|
|
7
|
+
const read = (f) => {
|
|
8
|
+
try {
|
|
9
|
+
return readFileSync(join(cwd, f), "utf-8");
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return "";
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
const pkg = read("package.json");
|
|
16
|
+
const deps = pkg ? JSON.parse(pkg) : {};
|
|
17
|
+
const allDeps = { ...deps.dependencies, ...deps.devDependencies };
|
|
18
|
+
const language = has("tsconfig.json") || has("tsconfig.app.json") || allDeps.typescript
|
|
19
|
+
? "typescript"
|
|
20
|
+
: allDeps.react || allDeps.vue
|
|
21
|
+
? "javascript"
|
|
22
|
+
: "unknown";
|
|
23
|
+
const framework = allDeps.react
|
|
24
|
+
? "react"
|
|
25
|
+
: allDeps.vue
|
|
26
|
+
? "vue"
|
|
27
|
+
: allDeps.svelte
|
|
28
|
+
? "svelte"
|
|
29
|
+
: "none";
|
|
30
|
+
const bundler = allDeps.vite
|
|
31
|
+
? "vite"
|
|
32
|
+
: allDeps.webpack
|
|
33
|
+
? "webpack"
|
|
34
|
+
: allDeps.esbuild
|
|
35
|
+
? "esbuild"
|
|
36
|
+
: "none";
|
|
37
|
+
const testRunner = allDeps.vitest ? "vitest" : allDeps.jest ? "jest" : "none";
|
|
38
|
+
const linter = allDeps["@biomejs/biome"]
|
|
39
|
+
? "biome"
|
|
40
|
+
: allDeps.eslint
|
|
41
|
+
? "eslint"
|
|
42
|
+
: "none";
|
|
43
|
+
const packageManager = has("pnpm-lock.yaml")
|
|
44
|
+
? "pnpm"
|
|
45
|
+
: has("bun.lockb")
|
|
46
|
+
? "bun"
|
|
47
|
+
: has("yarn.lock")
|
|
48
|
+
? "yarn"
|
|
49
|
+
: "npm";
|
|
50
|
+
return { language, framework, bundler, testRunner, linter, packageManager };
|
|
51
|
+
}
|
|
52
|
+
/** Detect GitHub/GitLab repo URL from git remote. */
|
|
53
|
+
export function detectRepoUrl(cwd) {
|
|
54
|
+
try {
|
|
55
|
+
const remote = execSync("git remote get-url origin", { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
56
|
+
const branch = execSync("git branch --show-current", { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim() || "main";
|
|
57
|
+
// Convert SSH to HTTPS
|
|
58
|
+
let url = remote
|
|
59
|
+
.replace(/^git@github\.com:/, "https://github.com/")
|
|
60
|
+
.replace(/^git@gitlab\.com:/, "https://gitlab.com/")
|
|
61
|
+
.replace(/\.git$/, "");
|
|
62
|
+
return { repoUrl: url, branch };
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return { repoUrl: null, branch: "main" };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Shared filesystem utilities — eliminates duplicate file-walking across runners. */
|
|
2
|
+
export interface SourceFile {
|
|
3
|
+
path: string;
|
|
4
|
+
fullPath: string;
|
|
5
|
+
base: string;
|
|
6
|
+
ext: string;
|
|
7
|
+
content: string;
|
|
8
|
+
lines: number;
|
|
9
|
+
isTest: boolean;
|
|
10
|
+
}
|
|
11
|
+
/** Walk source directories and return all code files. */
|
|
12
|
+
export declare function collectSourceFiles(cwd: string, opts?: {
|
|
13
|
+
includeTests?: boolean;
|
|
14
|
+
extraExts?: boolean;
|
|
15
|
+
}): SourceFile[];
|
|
16
|
+
/** Get only production source files (no tests). */
|
|
17
|
+
export declare function getProductionFiles(cwd: string): SourceFile[];
|
|
18
|
+
/** Get only test files. */
|
|
19
|
+
export declare function getTestFiles(cwd: string): SourceFile[];
|
|
20
|
+
/** Read a file relative to cwd, return empty string on error. */
|
|
21
|
+
export declare function readSafe(cwd: string, path: string): string;
|
|
22
|
+
/** Parse package.json dependencies. */
|
|
23
|
+
export declare function readDeps(cwd: string): Record<string, string>;
|