@vibecodeqa/cli 0.16.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 +58 -6
- package/dist/cli.js +48 -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 +18 -9
- package/dist/report/html.js +108 -68
- package/dist/report/pages.d.ts +4 -4
- package/dist/report/pages.js +165 -115
- 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 +105 -33
- package/dist/report/svg.d.ts +17 -0
- package/dist/report/svg.js +99 -0
- package/dist/runners/accessibility.d.ts +3 -0
- package/dist/runners/accessibility.js +85 -0
- 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.d.ts +3 -0
- package/dist/runners/react.js +86 -0
- 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
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** Code standards check — naming conventions, anti-patterns, config hygiene. */
|
|
2
|
-
import {
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
3
|
import { basename, extname, join } from "node:path";
|
|
4
|
+
import { getProductionFiles, readDeps } from "../fs-utils.js";
|
|
4
5
|
import { gradeFromScore } from "../types.js";
|
|
5
6
|
const CODE_SMELLS = [
|
|
6
7
|
{
|
|
@@ -40,16 +41,7 @@ export function runStandards(cwd, stack) {
|
|
|
40
41
|
const start = Date.now();
|
|
41
42
|
const issues = [];
|
|
42
43
|
// Collect source files
|
|
43
|
-
const files =
|
|
44
|
-
const dirs = ["src", "web/src"];
|
|
45
|
-
for (const dir of dirs) {
|
|
46
|
-
try {
|
|
47
|
-
collectFiles(join(cwd, dir), cwd, files);
|
|
48
|
-
}
|
|
49
|
-
catch {
|
|
50
|
-
/* dir doesn't exist */
|
|
51
|
-
}
|
|
52
|
-
}
|
|
44
|
+
const files = getProductionFiles(cwd);
|
|
53
45
|
// ── File naming conventions ──
|
|
54
46
|
let namingViolations = 0;
|
|
55
47
|
for (const f of files) {
|
|
@@ -170,28 +162,3 @@ export function runStandards(cwd, stack) {
|
|
|
170
162
|
duration: Date.now() - start,
|
|
171
163
|
};
|
|
172
164
|
}
|
|
173
|
-
function collectFiles(dir, cwd, out) {
|
|
174
|
-
for (const entry of readdirSync(dir)) {
|
|
175
|
-
if (entry === "node_modules" || entry === "dist" || entry === ".git")
|
|
176
|
-
continue;
|
|
177
|
-
const full = join(dir, entry);
|
|
178
|
-
if (statSync(full).isDirectory()) {
|
|
179
|
-
collectFiles(full, cwd, out);
|
|
180
|
-
}
|
|
181
|
-
else {
|
|
182
|
-
const ext = extname(entry);
|
|
183
|
-
if ([".ts", ".tsx", ".js", ".jsx"].includes(ext) && !entry.includes(".test.") && !entry.includes(".spec.")) {
|
|
184
|
-
out.push({ path: full.replace(`${cwd}/`, ""), content: readFileSync(full, "utf-8") });
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
function readDeps(cwd) {
|
|
190
|
-
try {
|
|
191
|
-
const pkg = JSON.parse(readFileSync(join(cwd, "package.json"), "utf-8"));
|
|
192
|
-
return { ...pkg.dependencies, ...pkg.devDependencies };
|
|
193
|
-
}
|
|
194
|
-
catch {
|
|
195
|
-
return {};
|
|
196
|
-
}
|
|
197
|
-
}
|
|
@@ -1,19 +1,29 @@
|
|
|
1
1
|
/** Project structure check — does the repo have standard files and conventions? */
|
|
2
|
-
import { existsSync,
|
|
3
|
-
import {
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { collectSourceFiles } from "../fs-utils.js";
|
|
4
5
|
import { gradeFromScore } from "../types.js";
|
|
5
|
-
const
|
|
6
|
+
const NODE_FILES = [
|
|
6
7
|
{ name: "package.json", path: "package.json", required: true, description: "Package manifest" },
|
|
7
8
|
{ name: "tsconfig.json", path: "tsconfig.json", required: false, description: "TypeScript configuration" },
|
|
8
9
|
{ name: "LICENSE", path: "LICENSE", required: true, description: "Open source license" },
|
|
9
10
|
{ name: ".gitignore", path: ".gitignore", required: true, description: "Git ignore rules" },
|
|
10
11
|
{ name: "README.md", path: "README.md", required: false, description: "Project documentation" },
|
|
11
12
|
];
|
|
13
|
+
const DART_FILES = [
|
|
14
|
+
{ name: "pubspec.yaml", path: "pubspec.yaml", required: true, description: "Dart package manifest" },
|
|
15
|
+
{ name: "analysis_options.yaml", path: "analysis_options.yaml", required: true, description: "Dart analysis options" },
|
|
16
|
+
{ name: "LICENSE", path: "LICENSE", required: true, description: "Open source license" },
|
|
17
|
+
{ name: ".gitignore", path: ".gitignore", required: true, description: "Git ignore rules" },
|
|
18
|
+
{ name: "README.md", path: "README.md", required: false, description: "Project documentation" },
|
|
19
|
+
];
|
|
12
20
|
export function runStructure(cwd, stack) {
|
|
13
21
|
const start = Date.now();
|
|
14
22
|
const issues = [];
|
|
15
23
|
const found = [];
|
|
16
24
|
const missing = [];
|
|
25
|
+
const isDart = stack.language === "dart";
|
|
26
|
+
const EXPECTED_FILES = isDart ? DART_FILES : NODE_FILES;
|
|
17
27
|
// Check standard files
|
|
18
28
|
for (const fc of EXPECTED_FILES) {
|
|
19
29
|
// tsconfig is required only for TS projects
|
|
@@ -31,24 +41,24 @@ export function runStructure(cwd, stack) {
|
|
|
31
41
|
}
|
|
32
42
|
}
|
|
33
43
|
// Check for lockfile
|
|
34
|
-
const
|
|
44
|
+
const lockfiles = isDart ? ["pubspec.lock"] : ["pnpm-lock.yaml", "package-lock.json", "yarn.lock", "bun.lockb"];
|
|
45
|
+
const hasLock = lockfiles.some((f) => existsSync(join(cwd, f)));
|
|
35
46
|
if (hasLock) {
|
|
36
47
|
found.push("lockfile");
|
|
37
48
|
}
|
|
38
49
|
else {
|
|
39
50
|
issues.push({ severity: "warning", message: "No lockfile found — builds may not be reproducible", rule: "missing-lockfile" });
|
|
40
51
|
}
|
|
41
|
-
// Check for
|
|
42
|
-
const
|
|
52
|
+
// Check for source directory
|
|
53
|
+
const srcDirs = isDart ? ["lib"] : ["src", "web/src"];
|
|
54
|
+
const hasSrc = srcDirs.some((d) => existsSync(join(cwd, d)));
|
|
43
55
|
if (!hasSrc) {
|
|
44
|
-
issues.push({ severity: "error", message:
|
|
56
|
+
issues.push({ severity: "error", message: `No ${srcDirs[0]}/ directory found`, rule: "no-src" });
|
|
45
57
|
}
|
|
46
58
|
// Count source vs test files
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
const srcCount = srcFiles.length;
|
|
51
|
-
const testCount = testFiles.length;
|
|
59
|
+
const allFiles = collectSourceFiles(cwd, { includeTests: true });
|
|
60
|
+
const srcCount = allFiles.filter((f) => !f.isTest).length;
|
|
61
|
+
const testCount = allFiles.filter((f) => f.isTest).length;
|
|
52
62
|
const testRatio = srcCount > 0 ? testCount / srcCount : 0;
|
|
53
63
|
if (testCount === 0 && srcCount > 0) {
|
|
54
64
|
issues.push({ severity: "error", message: `No test files found (${srcCount} source files with zero tests)`, rule: "no-tests" });
|
|
@@ -60,17 +70,19 @@ export function runStructure(cwd, stack) {
|
|
|
60
70
|
rule: "low-test-ratio",
|
|
61
71
|
});
|
|
62
72
|
}
|
|
63
|
-
// Check
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
// Check manifest has essential config
|
|
74
|
+
if (!isDart) {
|
|
75
|
+
try {
|
|
76
|
+
const pkg = JSON.parse(readFileSync(join(cwd, "package.json"), "utf-8"));
|
|
77
|
+
const scripts = pkg.scripts || {};
|
|
78
|
+
if (!scripts.test)
|
|
79
|
+
issues.push({ severity: "warning", message: "No 'test' script in package.json", rule: "no-test-script" });
|
|
80
|
+
if (!scripts.build && !scripts.dev)
|
|
81
|
+
issues.push({ severity: "info", message: "No 'build' or 'dev' script in package.json", rule: "no-build-script" });
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
/* no package.json or parse error */
|
|
85
|
+
}
|
|
74
86
|
}
|
|
75
87
|
const errors = issues.filter((i) => i.severity === "error").length;
|
|
76
88
|
const warnings = issues.filter((i) => i.severity === "warning").length;
|
|
@@ -84,35 +96,3 @@ export function runStructure(cwd, stack) {
|
|
|
84
96
|
duration: Date.now() - start,
|
|
85
97
|
};
|
|
86
98
|
}
|
|
87
|
-
function collectAll(cwd, src, test) {
|
|
88
|
-
const dirs = ["src", "web/src"];
|
|
89
|
-
for (const dir of dirs) {
|
|
90
|
-
try {
|
|
91
|
-
walk(join(cwd, dir), src, test);
|
|
92
|
-
}
|
|
93
|
-
catch {
|
|
94
|
-
/* dir doesn't exist */
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
function walk(dir, src, test) {
|
|
99
|
-
for (const entry of readdirSync(dir)) {
|
|
100
|
-
if (entry === "node_modules" || entry === "dist")
|
|
101
|
-
continue;
|
|
102
|
-
const full = join(dir, entry);
|
|
103
|
-
if (statSync(full).isDirectory()) {
|
|
104
|
-
walk(full, src, test);
|
|
105
|
-
}
|
|
106
|
-
else {
|
|
107
|
-
const ext = extname(entry);
|
|
108
|
-
if ([".ts", ".tsx", ".js", ".jsx"].includes(ext)) {
|
|
109
|
-
if (entry.includes(".test.") || entry.includes(".spec.")) {
|
|
110
|
-
test.push(full);
|
|
111
|
-
}
|
|
112
|
-
else {
|
|
113
|
-
src.push(full);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
package/dist/runners/testing.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
12
12
|
import { basename, extname, join } from "node:path";
|
|
13
|
+
import { getProductionFiles, readDeps } from "../fs-utils.js";
|
|
13
14
|
import { gradeFromScore } from "../types.js";
|
|
14
15
|
import { run } from "./exec.js";
|
|
15
16
|
// ── Classification rules ──
|
|
@@ -88,33 +89,7 @@ function walkTests(dir, cwd, out) {
|
|
|
88
89
|
}
|
|
89
90
|
}
|
|
90
91
|
function findSourceFiles(cwd) {
|
|
91
|
-
|
|
92
|
-
const dirs = ["src", "web/src"];
|
|
93
|
-
for (const dir of dirs) {
|
|
94
|
-
try {
|
|
95
|
-
walkSource(join(cwd, dir), cwd, files);
|
|
96
|
-
}
|
|
97
|
-
catch {
|
|
98
|
-
/* dir doesn't exist */
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
return files;
|
|
102
|
-
}
|
|
103
|
-
function walkSource(dir, cwd, out) {
|
|
104
|
-
for (const entry of readdirSync(dir)) {
|
|
105
|
-
if (entry === "node_modules" || entry === "dist")
|
|
106
|
-
continue;
|
|
107
|
-
const full = join(dir, entry);
|
|
108
|
-
if (statSync(full).isDirectory()) {
|
|
109
|
-
walkSource(full, cwd, out);
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
const ext = extname(entry);
|
|
113
|
-
if ([".ts", ".tsx", ".js", ".jsx"].includes(ext) && !entry.includes(".test.") && !entry.includes(".spec.")) {
|
|
114
|
-
out.push(full.replace(`${cwd}/`, ""));
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
92
|
+
return getProductionFiles(cwd).map((f) => f.path);
|
|
118
93
|
}
|
|
119
94
|
// ── Pairing analysis ──
|
|
120
95
|
function computePairing(srcFiles, testFiles) {
|
|
@@ -151,15 +126,6 @@ function detectE2E(cwd) {
|
|
|
151
126
|
}
|
|
152
127
|
return { tool: "none", configured: false };
|
|
153
128
|
}
|
|
154
|
-
function readDeps(cwd) {
|
|
155
|
-
try {
|
|
156
|
-
const pkg = JSON.parse(readFileSync(join(cwd, "package.json"), "utf-8"));
|
|
157
|
-
return { ...pkg.dependencies, ...pkg.devDependencies };
|
|
158
|
-
}
|
|
159
|
-
catch {
|
|
160
|
-
return {};
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
129
|
// ── Coverage collection ──
|
|
164
130
|
function collectCoverage(cwd, stack) {
|
|
165
131
|
if (stack.testRunner === "none")
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
/** Type safety check — count unsafe patterns: `as any`, explicit `any`, non-null assertions. */
|
|
2
2
|
import type { CheckResult } from "../types.js";
|
|
3
|
-
export declare function runTypeSafety(cwd: string): CheckResult;
|
|
3
|
+
export declare function runTypeSafety(cwd: string, isDart?: boolean): CheckResult;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/** Type safety check — count unsafe patterns: `as any`, explicit `any`, non-null assertions. */
|
|
2
|
-
import {
|
|
3
|
-
import { extname, join } from "node:path";
|
|
2
|
+
import { getProductionFiles } from "../fs-utils.js";
|
|
4
3
|
import { gradeFromScore } from "../types.js";
|
|
5
|
-
|
|
4
|
+
// TypeScript unsafe patterns
|
|
5
|
+
const TS_PATTERNS = [
|
|
6
6
|
{ name: "as any", pattern: /\bas any\b/g, severity: "warning", weight: 2 },
|
|
7
7
|
{ name: ": any", pattern: /:\s*any\b/g, severity: "warning", weight: 1 },
|
|
8
8
|
{ name: "non-null assertion (!.)", pattern: /\w+!\./g, severity: "info", weight: 0.5 },
|
|
@@ -10,22 +10,22 @@ const PATTERNS = [
|
|
|
10
10
|
{ name: "@ts-expect-error", pattern: /@ts-expect-error/g, severity: "warning", weight: 2 },
|
|
11
11
|
{ name: "@ts-nocheck", pattern: /@ts-nocheck/g, severity: "error", weight: 10 },
|
|
12
12
|
];
|
|
13
|
-
|
|
13
|
+
// Dart unsafe patterns
|
|
14
|
+
const DART_PATTERNS = [
|
|
15
|
+
{ name: "dynamic type", pattern: /\bdynamic\b/g, severity: "warning", weight: 1 },
|
|
16
|
+
{ name: "as dynamic", pattern: /\bas dynamic\b/g, severity: "warning", weight: 2 },
|
|
17
|
+
{ name: "// ignore:", pattern: /\/\/\s*ignore:/g, severity: "error", weight: 5 },
|
|
18
|
+
{ name: "// ignore_for_file:", pattern: /\/\/\s*ignore_for_file:/g, severity: "error", weight: 10 },
|
|
19
|
+
{ name: "late keyword", pattern: /\blate\s+(?!final)/g, severity: "info", weight: 0.5 },
|
|
20
|
+
];
|
|
21
|
+
export function runTypeSafety(cwd, isDart = false) {
|
|
14
22
|
const start = Date.now();
|
|
15
23
|
const issues = [];
|
|
16
24
|
const counts = {};
|
|
17
25
|
let totalPenalty = 0;
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
try {
|
|
22
|
-
collectFiles(join(cwd, dir), files);
|
|
23
|
-
}
|
|
24
|
-
catch {
|
|
25
|
-
/* dir doesn't exist */
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
if (files.length === 0) {
|
|
26
|
+
const PATTERNS = isDart ? DART_PATTERNS : TS_PATTERNS;
|
|
27
|
+
const sourceFiles = getProductionFiles(cwd);
|
|
28
|
+
if (sourceFiles.length === 0) {
|
|
29
29
|
return {
|
|
30
30
|
name: "type-safety",
|
|
31
31
|
score: 100,
|
|
@@ -35,10 +35,8 @@ export function runTypeSafety(cwd) {
|
|
|
35
35
|
duration: Date.now() - start,
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
|
-
for (const
|
|
39
|
-
const
|
|
40
|
-
const relPath = file.replace(`${cwd}/`, "");
|
|
41
|
-
const lines = content.split("\n");
|
|
38
|
+
for (const sf of sourceFiles) {
|
|
39
|
+
const lines = sf.content.split("\n");
|
|
42
40
|
for (let i = 0; i < lines.length; i++) {
|
|
43
41
|
const line = lines[i];
|
|
44
42
|
const trimmed = line.trim();
|
|
@@ -53,7 +51,7 @@ export function runTypeSafety(cwd) {
|
|
|
53
51
|
counts[p.name] = (counts[p.name] || 0) + matches.length;
|
|
54
52
|
totalPenalty += p.weight * matches.length;
|
|
55
53
|
for (const _m of matches) {
|
|
56
|
-
issues.push({ severity: p.severity, message: p.name, file:
|
|
54
|
+
issues.push({ severity: p.severity, message: p.name, file: sf.path, line: i + 1, rule: "unsafe-type" });
|
|
57
55
|
}
|
|
58
56
|
}
|
|
59
57
|
}
|
|
@@ -64,24 +62,8 @@ export function runTypeSafety(cwd) {
|
|
|
64
62
|
name: "type-safety",
|
|
65
63
|
score,
|
|
66
64
|
grade: gradeFromScore(score),
|
|
67
|
-
details: { ...counts, filesScanned:
|
|
65
|
+
details: { ...counts, filesScanned: sourceFiles.length, totalUnsafe: issues.length },
|
|
68
66
|
issues,
|
|
69
67
|
duration: Date.now() - start,
|
|
70
68
|
};
|
|
71
69
|
}
|
|
72
|
-
function collectFiles(dir, out) {
|
|
73
|
-
for (const entry of readdirSync(dir)) {
|
|
74
|
-
if (entry === "node_modules" || entry === "dist")
|
|
75
|
-
continue;
|
|
76
|
-
const full = join(dir, entry);
|
|
77
|
-
if (statSync(full).isDirectory()) {
|
|
78
|
-
collectFiles(full, out);
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
const ext = extname(entry);
|
|
82
|
-
if ((ext === ".ts" || ext === ".tsx") && !entry.includes(".test.") && !entry.includes(".spec.")) {
|
|
83
|
-
out.push(full);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
@@ -3,33 +3,51 @@ import { existsSync } from "node:fs";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { gradeFromScore } from "../types.js";
|
|
5
5
|
import { run } from "./exec.js";
|
|
6
|
-
export function runTypeCheck(cwd) {
|
|
6
|
+
export function runTypeCheck(cwd, isDart = false) {
|
|
7
7
|
const start = Date.now();
|
|
8
8
|
const issues = [];
|
|
9
|
-
if (
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
duration: Date.now() - start,
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
const { stdout } = run("npx tsc --noEmit 2>&1 || true", cwd, 30_000);
|
|
20
|
-
const lines = stdout.split("\n");
|
|
21
|
-
for (const line of lines) {
|
|
22
|
-
const match = line.match(/^(.+)\((\d+),\d+\): error (TS\d+): (.+)/);
|
|
23
|
-
if (match) {
|
|
9
|
+
if (isDart) {
|
|
10
|
+
// Dart uses dart analyze for type checking — errors are type errors
|
|
11
|
+
const { stdout } = run("dart analyze --format=machine 2>/dev/null || true", cwd, 30_000);
|
|
12
|
+
for (const line of stdout.split("\n")) {
|
|
13
|
+
const parts = line.split("|");
|
|
14
|
+
if (parts.length < 8 || parts[0] !== "ERROR")
|
|
15
|
+
continue;
|
|
24
16
|
issues.push({
|
|
25
17
|
severity: "error",
|
|
26
|
-
file:
|
|
27
|
-
line: parseInt(
|
|
28
|
-
rule:
|
|
29
|
-
message:
|
|
18
|
+
file: parts[3],
|
|
19
|
+
line: parseInt(parts[4], 10) || undefined,
|
|
20
|
+
rule: parts[2],
|
|
21
|
+
message: parts[7],
|
|
30
22
|
});
|
|
31
23
|
}
|
|
32
24
|
}
|
|
25
|
+
else {
|
|
26
|
+
if (!existsSync(join(cwd, "tsconfig.json")) && !existsSync(join(cwd, "tsconfig.app.json"))) {
|
|
27
|
+
return {
|
|
28
|
+
name: "types",
|
|
29
|
+
score: 0,
|
|
30
|
+
grade: "F",
|
|
31
|
+
details: { skipped: true, reason: "no tsconfig.json" },
|
|
32
|
+
issues: [],
|
|
33
|
+
duration: Date.now() - start,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
const { stdout } = run("npx tsc --noEmit 2>&1 || true", cwd, 30_000);
|
|
37
|
+
const lines = stdout.split("\n");
|
|
38
|
+
for (const line of lines) {
|
|
39
|
+
const match = line.match(/^(.+)\((\d+),\d+\): error (TS\d+): (.+)/);
|
|
40
|
+
if (match) {
|
|
41
|
+
issues.push({
|
|
42
|
+
severity: "error",
|
|
43
|
+
file: match[1],
|
|
44
|
+
line: parseInt(match[2], 10),
|
|
45
|
+
rule: match[3],
|
|
46
|
+
message: match[4],
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
33
51
|
const errorCount = issues.length;
|
|
34
52
|
const score = errorCount === 0 ? 100 : Math.max(0, 100 - errorCount * 5);
|
|
35
53
|
return {
|
package/dist/types.d.ts
CHANGED
|
@@ -30,11 +30,11 @@ export interface VibeReport {
|
|
|
30
30
|
};
|
|
31
31
|
}
|
|
32
32
|
export interface StackInfo {
|
|
33
|
-
language: "typescript" | "javascript" | "unknown";
|
|
34
|
-
framework: "react" | "vue" | "svelte" | "none" | "unknown";
|
|
33
|
+
language: "typescript" | "javascript" | "dart" | "unknown";
|
|
34
|
+
framework: "react" | "vue" | "svelte" | "flutter" | "none" | "unknown";
|
|
35
35
|
bundler: "vite" | "webpack" | "esbuild" | "none" | "unknown";
|
|
36
|
-
testRunner: "vitest" | "jest" | "none" | "unknown";
|
|
37
|
-
linter: "biome" | "eslint" | "none" | "unknown";
|
|
38
|
-
packageManager: "pnpm" | "npm" | "yarn" | "bun" | "unknown";
|
|
36
|
+
testRunner: "vitest" | "jest" | "flutter_test" | "dart_test" | "none" | "unknown";
|
|
37
|
+
linter: "biome" | "eslint" | "dart_analyze" | "none" | "unknown";
|
|
38
|
+
packageManager: "pnpm" | "npm" | "yarn" | "bun" | "pub" | "unknown";
|
|
39
39
|
}
|
|
40
40
|
export declare function gradeFromScore(score: number): "A" | "B" | "C" | "D" | "F";
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibecodeqa/cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Code health scanner for the AI coding era.
|
|
3
|
+
"version": "0.18.0",
|
|
4
|
+
"description": "Code health scanner for the AI coding era. 21 checks, zero config, full report.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"vcqa": "./dist/cli.js",
|
|
@@ -12,6 +12,13 @@
|
|
|
12
12
|
"LICENSE",
|
|
13
13
|
"README.md"
|
|
14
14
|
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc",
|
|
17
|
+
"dev": "tsc --watch",
|
|
18
|
+
"test": "vitest run",
|
|
19
|
+
"lint": "biome check src/",
|
|
20
|
+
"prepublishOnly": "tsc && chmod +x dist/cli.js"
|
|
21
|
+
},
|
|
15
22
|
"keywords": [
|
|
16
23
|
"code-quality",
|
|
17
24
|
"code-health",
|
|
@@ -32,7 +39,7 @@
|
|
|
32
39
|
"license": "MIT",
|
|
33
40
|
"repository": {
|
|
34
41
|
"type": "git",
|
|
35
|
-
"url": "https://github.com/
|
|
42
|
+
"url": "https://github.com/vibecodeqa/cli"
|
|
36
43
|
},
|
|
37
44
|
"homepage": "https://vibecodeqa.online",
|
|
38
45
|
"engines": {
|
|
@@ -43,11 +50,5 @@
|
|
|
43
50
|
"@types/node": "^25.8.0",
|
|
44
51
|
"typescript": "^5.8.3",
|
|
45
52
|
"vitest": "^4.1.6"
|
|
46
|
-
},
|
|
47
|
-
"scripts": {
|
|
48
|
-
"build": "tsc",
|
|
49
|
-
"dev": "tsc --watch",
|
|
50
|
-
"test": "vitest run",
|
|
51
|
-
"lint": "biome check src/"
|
|
52
53
|
}
|
|
53
|
-
}
|
|
54
|
+
}
|