@vibecodeqa/cli 0.12.1 → 0.13.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 +9 -4
- package/dist/fs-utils.js +7 -1
- package/dist/report/html.js +12 -6
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -145,9 +145,9 @@ async function main() {
|
|
|
145
145
|
}
|
|
146
146
|
if (!jsonOnly && !ciMode && !watchMode) {
|
|
147
147
|
try {
|
|
148
|
-
const {
|
|
148
|
+
const { execFileSync } = await import("node:child_process");
|
|
149
149
|
const openCmd = process.platform === "darwin" ? "open" : "xdg-open";
|
|
150
|
-
|
|
150
|
+
execFileSync(openCmd, [join(outputDir, "report.html")], { stdio: "ignore" });
|
|
151
151
|
}
|
|
152
152
|
catch { /* failed to open browser */ }
|
|
153
153
|
}
|
|
@@ -162,15 +162,20 @@ async function main() {
|
|
|
162
162
|
console.log(" \x1b[2mWatching for changes... (Ctrl+C to stop)\x1b[0m");
|
|
163
163
|
console.log("");
|
|
164
164
|
let debounce = null;
|
|
165
|
+
let running = false;
|
|
165
166
|
for (const dir of srcDirs) {
|
|
166
167
|
watch(dir, { recursive: true }, (_event, filename) => {
|
|
167
168
|
if (!filename || filename.includes("node_modules") || filename.includes(".vibe-check"))
|
|
168
169
|
return;
|
|
170
|
+
if (running)
|
|
171
|
+
return; // prevent concurrent re-runs (M5)
|
|
169
172
|
if (debounce)
|
|
170
173
|
clearTimeout(debounce);
|
|
171
|
-
debounce = setTimeout(() => {
|
|
174
|
+
debounce = setTimeout(async () => {
|
|
175
|
+
running = true;
|
|
172
176
|
console.log(` \x1b[2mChanged: ${filename} — re-scanning...\x1b[0m`);
|
|
173
|
-
main().catch(() => { });
|
|
177
|
+
await main().catch(() => { });
|
|
178
|
+
running = false;
|
|
174
179
|
}, 500);
|
|
175
180
|
});
|
|
176
181
|
}
|
package/dist/fs-utils.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Shared filesystem utilities — eliminates duplicate file-walking across runners. */
|
|
2
|
-
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { lstatSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
3
3
|
import { basename, extname, join } from "node:path";
|
|
4
4
|
const SKIP_DIRS = new Set(["node_modules", "dist", ".git", ".vibe-check", "coverage", "test-results", "__pycache__"]);
|
|
5
5
|
const CODE_EXTS = new Set([".ts", ".tsx", ".js", ".jsx"]);
|
|
@@ -53,6 +53,9 @@ function walk(dir, cwd, out, exts) {
|
|
|
53
53
|
if (SKIP_DIRS.has(entry))
|
|
54
54
|
continue;
|
|
55
55
|
const full = join(dir, entry);
|
|
56
|
+
// Skip symlinks to prevent traversal attacks (H3)
|
|
57
|
+
if (lstatSync(full).isSymbolicLink())
|
|
58
|
+
continue;
|
|
56
59
|
if (statSync(full).isDirectory()) {
|
|
57
60
|
walk(full, cwd, out, exts);
|
|
58
61
|
}
|
|
@@ -60,6 +63,9 @@ function walk(dir, cwd, out, exts) {
|
|
|
60
63
|
const ext = extname(entry);
|
|
61
64
|
if (!exts.has(ext))
|
|
62
65
|
continue;
|
|
66
|
+
// Skip files over 1MB to prevent memory issues (M1)
|
|
67
|
+
if (statSync(full).size > 1_000_000)
|
|
68
|
+
continue;
|
|
63
69
|
const content = readFileSync(full, "utf-8");
|
|
64
70
|
const relPath = full.replace(cwd + "/", "");
|
|
65
71
|
const isTest = entry.includes(".test.") || entry.includes(".spec.") || relPath.includes("__tests__");
|
package/dist/report/html.js
CHANGED
|
@@ -11,12 +11,12 @@
|
|
|
11
11
|
import { getCheckMeta } from "../check-meta.js";
|
|
12
12
|
import { generateArchSVG } from "../runners/architecture.js";
|
|
13
13
|
function e(s) {
|
|
14
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
14
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
15
15
|
}
|
|
16
16
|
/** Make a file path a clickable GitHub link if repoUrl is available. */
|
|
17
17
|
function fileLink(path, line, repoUrl, branch) {
|
|
18
|
-
const clean = path.split(":")[0];
|
|
19
|
-
if (!repoUrl)
|
|
18
|
+
const clean = path.split(":")[0];
|
|
19
|
+
if (!repoUrl || !/^https?:\/\//.test(repoUrl))
|
|
20
20
|
return e(path);
|
|
21
21
|
const href = `${repoUrl}/blob/${branch}/${clean}${line ? "#L" + line : ""}`;
|
|
22
22
|
return `<a href="${e(href)}" target="_blank" rel="noopener" class="flink">${e(path)}</a>`;
|
|
@@ -140,9 +140,8 @@ export function generateHTML(report) {
|
|
|
140
140
|
for (const [file, issues] of byFile) {
|
|
141
141
|
issuesHtml += `<div class="fg"><div class="fn">${fl(file)} <span class="fc">${issues.length}</span></div>`;
|
|
142
142
|
for (const iss of issues) {
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
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('${safePrompt}');this.textContent='✓';setTimeout(()=>this.textContent='📋',1000)" title="Copy fix prompt">📋</button></div>`;
|
|
143
|
+
const prompt = `Fix this issue in ${file}${iss.line ? ":" + iss.line : ""}\n${iss.severity}: ${iss.message}${iss.rule ? " (" + iss.rule + ")" : ""}\nCheck: ${c.name}`;
|
|
144
|
+
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" data-prompt="${e(prompt)}" title="Copy fix prompt">📋</button></div>`;
|
|
146
145
|
}
|
|
147
146
|
issuesHtml += `</div>`;
|
|
148
147
|
}
|
|
@@ -390,6 +389,13 @@ function sub(el,cat){
|
|
|
390
389
|
el.classList.add('active');
|
|
391
390
|
document.querySelectorAll('#p-'+cat+' .sp').forEach(s=>{s.classList.toggle('active',s.dataset.sub===id)});
|
|
392
391
|
}
|
|
392
|
+
// Copy-prompt buttons — read from data-attribute (no inline JS with user data)
|
|
393
|
+
document.addEventListener('click',function(ev){
|
|
394
|
+
var btn=ev.target.closest('.cp-btn');
|
|
395
|
+
if(!btn)return;
|
|
396
|
+
navigator.clipboard.writeText(btn.dataset.prompt||'');
|
|
397
|
+
btn.textContent='\\u2713';setTimeout(function(){btn.textContent='\\ud83d\\udccb'},1000);
|
|
398
|
+
});
|
|
393
399
|
// Init: show overview
|
|
394
400
|
document.querySelector('.tn').classList.add('active');
|
|
395
401
|
</script>
|