@vibecodeqa/cli 0.17.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +73 -63
- package/dist/check-meta.d.ts +1 -0
- package/dist/check-meta.js +34 -2
- package/dist/cli.js +35 -10
- package/dist/detect.js +24 -2
- package/dist/fs-utils.d.ts +4 -0
- package/dist/fs-utils.js +12 -6
- package/dist/report/html.d.ts +17 -10
- package/dist/report/html.js +106 -73
- package/dist/report/pages.d.ts +2 -1
- package/dist/report/pages.js +88 -82
- package/dist/report/sarif.d.ts +3 -0
- package/dist/report/sarif.js +67 -0
- package/dist/report/styles.d.ts +1 -1
- package/dist/report/styles.js +82 -36
- package/dist/runners/architecture.d.ts +2 -0
- package/dist/runners/architecture.js +232 -20
- package/dist/runners/code-coherence.d.ts +17 -0
- package/dist/runners/code-coherence.js +39 -0
- package/dist/runners/complexity.js +7 -37
- package/dist/runners/confusion.js +3 -31
- package/dist/runners/context.js +9 -40
- package/dist/runners/dependencies.js +28 -0
- package/dist/runners/doc-coherence.d.ts +14 -0
- package/dist/runners/doc-coherence.js +48 -0
- package/dist/runners/docs.js +7 -32
- package/dist/runners/duplication.js +9 -37
- package/dist/runners/lint.js +17 -0
- package/dist/runners/performance.d.ts +10 -0
- package/dist/runners/performance.js +174 -0
- package/dist/runners/react.js +15 -10
- package/dist/runners/secrets.js +8 -29
- package/dist/runners/security.js +15 -38
- package/dist/runners/standards.js +3 -36
- package/dist/runners/structure.js +35 -55
- package/dist/runners/testing.js +2 -36
- package/dist/runners/type-safety.d.ts +1 -1
- package/dist/runners/type-safety.js +19 -37
- package/dist/runners/types-check.d.ts +1 -1
- package/dist/runners/types-check.js +38 -20
- package/dist/types.d.ts +5 -5
- package/package.json +11 -10
|
@@ -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
|
+
}
|