@vibecodeqa/cli 0.9.1 → 0.10.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/dist/cli.js +18 -2
- package/dist/report/html.js +10 -3
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/** vibe-check — code health scanner for the AI coding era. */
|
|
3
|
-
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { existsSync, mkdirSync, readdirSync, unlinkSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { join, resolve } from "node:path";
|
|
5
5
|
import { detectRepoUrl, detectStack } from "./detect.js";
|
|
6
6
|
import { generateHTML } from "./report/html.js";
|
|
@@ -22,7 +22,7 @@ import { runTypeSafety } from "./runners/type-safety.js";
|
|
|
22
22
|
import { computeScore } from "./score.js";
|
|
23
23
|
import { computeTrend, formatTrend } from "./trend.js";
|
|
24
24
|
import { gradeFromScore } from "./types.js";
|
|
25
|
-
const VERSION = "0.
|
|
25
|
+
const VERSION = "0.10.0";
|
|
26
26
|
const args = process.argv.slice(2);
|
|
27
27
|
const flags = new Set(args.filter((a) => a.startsWith("--")));
|
|
28
28
|
const cwd = resolve(args.find((a) => !a.startsWith("--")) || ".");
|
|
@@ -106,6 +106,22 @@ async function main() {
|
|
|
106
106
|
const trend = computeTrend(report, outputDir);
|
|
107
107
|
if (!existsSync(outputDir))
|
|
108
108
|
mkdirSync(outputDir, { recursive: true });
|
|
109
|
+
// Save to history before overwriting current report
|
|
110
|
+
const historyDir = join(outputDir, "history");
|
|
111
|
+
if (!existsSync(historyDir))
|
|
112
|
+
mkdirSync(historyDir, { recursive: true });
|
|
113
|
+
const historyFile = join(historyDir, `${report.timestamp.replace(/[:.]/g, "-")}.json`);
|
|
114
|
+
writeFileSync(historyFile, JSON.stringify(report, null, 2));
|
|
115
|
+
// Keep only last 30 history entries
|
|
116
|
+
const historyFiles = readdirSync(historyDir).filter((f) => f.endsWith(".json")).sort();
|
|
117
|
+
if (historyFiles.length > 30) {
|
|
118
|
+
for (const old of historyFiles.slice(0, historyFiles.length - 30)) {
|
|
119
|
+
try {
|
|
120
|
+
unlinkSync(join(historyDir, old));
|
|
121
|
+
}
|
|
122
|
+
catch { /* ignore */ }
|
|
123
|
+
}
|
|
124
|
+
}
|
|
109
125
|
writeFileSync(join(outputDir, "report.json"), JSON.stringify(report, null, 2));
|
|
110
126
|
writeFileSync(join(outputDir, "report.html"), generateHTML(report));
|
|
111
127
|
if (jsonOnly) {
|
package/dist/report/html.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* All in one self-contained HTML file using hash routing + show/hide.
|
|
10
10
|
*/
|
|
11
11
|
import { getCheckMeta } from "../check-meta.js";
|
|
12
|
+
import { generateArchSVG } from "../runners/architecture.js";
|
|
12
13
|
function e(s) {
|
|
13
14
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
14
15
|
}
|
|
@@ -116,7 +117,7 @@ export function generateHTML(report) {
|
|
|
116
117
|
const subPages = cs.checks.map((c, i) => {
|
|
117
118
|
const meta = getCheckMeta(c.name);
|
|
118
119
|
const sk = c.details.skipped;
|
|
119
|
-
const
|
|
120
|
+
const detailsFiltered = Object.entries(c.details).filter(([k]) => k !== "skipped" && k !== "reason" && k !== "graph").map(([k, v]) => {
|
|
120
121
|
const d = Array.isArray(v) ? v.join(", ") : typeof v === "object" ? JSON.stringify(v) : String(v);
|
|
121
122
|
return `<div class="kv"><span class="k">${e(k)}</span><span class="v">${e(d)}</span></div>`;
|
|
122
123
|
}).join("");
|
|
@@ -138,7 +139,8 @@ export function generateHTML(report) {
|
|
|
138
139
|
for (const [file, issues] of byFile) {
|
|
139
140
|
issuesHtml += `<div class="fg"><div class="fn">${fl(file)} <span class="fc">${issues.length}</span></div>`;
|
|
140
141
|
for (const iss of issues) {
|
|
141
|
-
|
|
142
|
+
const prompt = `Fix this issue in ${file}${iss.line ? ":" + iss.line : ""}\\n${iss.severity}: ${iss.message}${iss.rule ? " (" + iss.rule + ")" : ""}\\nCheck: ${c.name}`;
|
|
143
|
+
issuesHtml += `<div class="ir ${iss.severity}"><span class="is">${iss.severity[0].toUpperCase()}</span>${iss.line ? `<span class="il">${iss.line}</span>` : ""}<span class="im">${e(iss.message)}</span>${iss.rule ? `<span class="iru">${e(iss.rule)}</span>` : ""}<button class="cp-btn" onclick="navigator.clipboard.writeText('${prompt.replace(/'/g, "\\'")}');this.textContent='✓';setTimeout(()=>this.textContent='📋',1000)" title="Copy fix prompt">📋</button></div>`;
|
|
142
144
|
}
|
|
143
145
|
issuesHtml += `</div>`;
|
|
144
146
|
}
|
|
@@ -153,7 +155,8 @@ export function generateHTML(report) {
|
|
|
153
155
|
<div class="ch-head"><span class="ch-g" style="color:${sk ? "#555" : gc(c.grade)}">${sk ? "—" : c.grade}</span><div><b>${e(meta.label)}</b><span class="ch-s">${sk ? "skipped" : c.score + "/100"} · weight ${meta.weight}% · ${c.duration}ms · ${c.issues.length} issues</span></div><span class="pri" style="color:${pc(meta.priority)}">${meta.priority}</span></div>
|
|
154
156
|
${meta.description ? `<div class="info-panel"><div class="ip-row"><span class="ip-label">What</span><span>${e(meta.description)}</span></div><div class="ip-row"><span class="ip-label">Risk</span><span>${e(meta.risk)}</span></div><div class="ip-row"><span class="ip-label">Fix</span><span>${e(meta.recommendation)}</span></div></div>` : ""}
|
|
155
157
|
${sk ? `<p class="skip-r">${e(c.details.reason || "skipped")}</p>` : ""}
|
|
156
|
-
${
|
|
158
|
+
${c.name === "architecture" && !sk ? `<div class="arch-svg">${generateArchSVG(c.details)}</div>` : ""}
|
|
159
|
+
${detailsFiltered ? `<div class="kvs">${detailsFiltered}</div>` : ""}
|
|
157
160
|
${issuesHtml ? `<div class="iss-list">${issuesHtml}</div>` : '<p style="color:var(--muted);font-size:0.8rem;margin-top:1rem">No issues found.</p>'}
|
|
158
161
|
</div>`;
|
|
159
162
|
}).join("");
|
|
@@ -312,6 +315,10 @@ h3{font-size:0.85rem;color:var(--muted);text-transform:uppercase;letter-spacing:
|
|
|
312
315
|
.footer{text-align:center;color:var(--muted);font-size:0.58rem;margin-top:2rem;padding:0.8rem 0;border-top:1px solid var(--border)}
|
|
313
316
|
.footer a{color:var(--muted)}
|
|
314
317
|
.flink{color:var(--accent);text-decoration:none;font-family:"SF Mono",monospace}.flink:hover{text-decoration:underline}
|
|
318
|
+
.arch-svg{margin:1rem 0;overflow-x:auto}
|
|
319
|
+
.arch-svg svg{border-radius:8px}
|
|
320
|
+
.cp-btn{background:none;border:none;cursor:pointer;font-size:0.6rem;opacity:0.3;padding:0 0.2rem;flex-shrink:0}.cp-btn:hover{opacity:1}
|
|
321
|
+
.ir:hover .cp-btn{opacity:0.6}
|
|
315
322
|
@media(max-width:768px){.side{display:none}.content{margin-left:0;padding:1rem}.cats{grid-template-columns:1fr 1fr}.dash{flex-direction:column}}
|
|
316
323
|
</style>
|
|
317
324
|
</head>
|