@vibecodeqa/cli 0.10.0 → 0.11.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 CHANGED
@@ -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.10.0";
25
+ const VERSION = "0.11.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("--")) || ".");
@@ -30,6 +30,7 @@ const outputDir = join(cwd, ".vibe-check");
30
30
  const jsonOnly = flags.has("--json");
31
31
  const ciMode = flags.has("--ci");
32
32
  const skipTests = flags.has("--skip-tests");
33
+ const watchMode = flags.has("--watch");
33
34
  function color(grade) {
34
35
  if (grade === "A")
35
36
  return "\x1b[32m";
@@ -141,7 +142,7 @@ async function main() {
141
142
  if (ciMode && score < 60) {
142
143
  process.exit(1);
143
144
  }
144
- if (!jsonOnly && !ciMode) {
145
+ if (!jsonOnly && !ciMode && !watchMode) {
145
146
  try {
146
147
  const { execSync } = await import("node:child_process");
147
148
  const openCmd = process.platform === "darwin" ? "open" : "xdg-open";
@@ -149,6 +150,32 @@ async function main() {
149
150
  }
150
151
  catch { /* failed to open browser */ }
151
152
  }
153
+ // Watch mode — re-run on file changes
154
+ if (watchMode) {
155
+ const { watch } = await import("node:fs");
156
+ const srcDirs = ["src", "web/src"].map((d) => join(cwd, d)).filter((d) => existsSync(d));
157
+ if (srcDirs.length === 0) {
158
+ console.log(" \x1b[31mNo src/ directory to watch\x1b[0m");
159
+ process.exit(1);
160
+ }
161
+ console.log(" \x1b[2mWatching for changes... (Ctrl+C to stop)\x1b[0m");
162
+ console.log("");
163
+ let debounce = null;
164
+ for (const dir of srcDirs) {
165
+ watch(dir, { recursive: true }, (_event, filename) => {
166
+ if (!filename || filename.includes("node_modules") || filename.includes(".vibe-check"))
167
+ return;
168
+ if (debounce)
169
+ clearTimeout(debounce);
170
+ debounce = setTimeout(() => {
171
+ console.log(` \x1b[2mChanged: ${filename} — re-scanning...\x1b[0m`);
172
+ main().catch(() => { });
173
+ }, 500);
174
+ });
175
+ }
176
+ // Keep process alive
177
+ await new Promise(() => { });
178
+ }
152
179
  }
153
180
  main().catch((err) => {
154
181
  console.error("vibe-check error:", err);
@@ -78,6 +78,7 @@ export function generateHTML(report) {
78
78
  ...GROUPS.map((g) => ({ id: g.id, label: g.label })),
79
79
  { id: "issues", label: `Issues (${totalIssues})` },
80
80
  { id: "files", label: "File Map" },
81
+ { id: "heatmap", label: "Heatmap" },
81
82
  ];
82
83
  const topNav = topNavItems.map((t) => `<a class="tn" data-page="${t.id}" onclick="go('${t.id}')">${t.label}</a>`).join("");
83
84
  // Overview page
@@ -189,6 +190,29 @@ ${allIssues.length > 200 ? `<p style="color:var(--muted);text-align:center;margi
189
190
  <h2>File Heatmap</h2>
190
191
  <p style="color:var(--muted);font-size:0.78rem;margin-bottom:1rem">Top ${topFiles.length} files by total issues across all checks</p>
191
192
  ${fileRows || '<p style="color:var(--muted)">No file-level issues found.</p>'}
193
+ </div>`;
194
+ // Codebase heatmap — each file = row of pixels, color = issue density
195
+ const heatmapFiles = [...fileIssues.entries()]
196
+ .sort((a, b) => b[1].errors + b[1].warnings - a[1].errors - a[1].warnings)
197
+ .slice(0, 30);
198
+ let heatmapHtml = "";
199
+ if (heatmapFiles.length > 0) {
200
+ const maxIssues = Math.max(...heatmapFiles.map(([, d]) => d.errors + d.warnings));
201
+ heatmapHtml = heatmapFiles.map(([file, d]) => {
202
+ const total = d.errors + d.warnings;
203
+ const intensity = maxIssues > 0 ? total / maxIssues : 0;
204
+ const r = Math.round(239 * intensity); // red channel
205
+ const g = Math.round(68 * (1 - intensity) + 197 * (d.errors === 0 ? 0.3 : 0)); // green
206
+ const color = `rgb(${r},${g},30)`;
207
+ const barW = Math.max(4, Math.round(intensity * 200));
208
+ const checks = [...d.checks].join(", ");
209
+ return `<div class="hm-row"><span class="hm-name">${fl(file)}</span><div class="hm-bar" style="width:${barW}px;background:${color}" title="${total} issues (${checks})"></div><span class="hm-count">${d.errors}E ${d.warnings}W</span></div>`;
210
+ }).join("");
211
+ }
212
+ const heatmapPage = `<div id="p-heatmap" class="page">
213
+ <h2>Code Heatmap</h2>
214
+ <p style="color:var(--muted);font-size:0.78rem;margin-bottom:1rem">Visual density of issues per file. Red = errors, orange = warnings. Bar width = relative issue count.</p>
215
+ ${heatmapHtml || '<p style="color:var(--muted)">No issues to visualize.</p>'}
192
216
  </div>`;
193
217
  return `<!DOCTYPE html>
194
218
  <html lang="en">
@@ -316,6 +340,10 @@ h3{font-size:0.85rem;color:var(--muted);text-transform:uppercase;letter-spacing:
316
340
  .footer a{color:var(--muted)}
317
341
  .flink{color:var(--accent);text-decoration:none;font-family:"SF Mono",monospace}.flink:hover{text-decoration:underline}
318
342
  .arch-svg{margin:1rem 0;overflow-x:auto}
343
+ .hm-row{display:flex;align-items:center;gap:0.5rem;margin-bottom:0.2rem;font-size:0.7rem}
344
+ .hm-name{width:200px;flex-shrink:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:"SF Mono",monospace;font-size:0.65rem}
345
+ .hm-bar{height:14px;border-radius:3px;min-width:4px}
346
+ .hm-count{color:var(--muted);font-size:0.65rem;flex-shrink:0}
319
347
  .arch-svg svg{border-radius:8px}
320
348
  .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
349
  .ir:hover .cp-btn{opacity:0.6}
@@ -345,6 +373,7 @@ h3{font-size:0.85rem;color:var(--muted);text-transform:uppercase;letter-spacing:
345
373
  ${catPages}
346
374
  ${issuesPage}
347
375
  ${filesPage}
376
+ ${heatmapPage}
348
377
  <div class="footer">Generated by <a href="https://vibecodeqa.online">VibeCode QA</a> v${report.version} &mdash; <code>npx @vibecodeqa/cli</code></div>
349
378
  </div>
350
379
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibecodeqa/cli",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "Code health scanner for the AI coding era. 15 checks, zero config, full report.",
5
5
  "type": "module",
6
6
  "bin": {