@vibecheckai/cli 3.2.2 → 3.2.4
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/bin/.generated +25 -25
- package/bin/dev/run-v2-torture.js +30 -30
- package/bin/runners/ENHANCEMENT_GUIDE.md +121 -121
- package/bin/runners/lib/__tests__/entitlements-v2.test.js +295 -295
- package/bin/runners/lib/agent-firewall/ai/false-positive-analyzer.js +474 -0
- package/bin/runners/lib/agent-firewall/claims/extractor.js +117 -28
- package/bin/runners/lib/agent-firewall/evidence/env-evidence.js +23 -14
- package/bin/runners/lib/agent-firewall/evidence/route-evidence.js +72 -1
- package/bin/runners/lib/agent-firewall/interceptor/base.js +2 -2
- package/bin/runners/lib/agent-firewall/policy/default-policy.json +6 -0
- package/bin/runners/lib/agent-firewall/policy/engine.js +34 -3
- package/bin/runners/lib/agent-firewall/policy/rules/fake-success.js +29 -4
- package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +12 -0
- package/bin/runners/lib/agent-firewall/truthpack/loader.js +21 -0
- package/bin/runners/lib/agent-firewall/utils/ignore-checker.js +118 -0
- package/bin/runners/lib/analyzers.js +606 -325
- package/bin/runners/lib/auth-truth.js +193 -193
- package/bin/runners/lib/backup.js +62 -62
- package/bin/runners/lib/billing.js +107 -107
- package/bin/runners/lib/claims.js +118 -118
- package/bin/runners/lib/cli-ui.js +540 -540
- package/bin/runners/lib/contracts/auth-contract.js +202 -202
- package/bin/runners/lib/contracts/env-contract.js +181 -181
- package/bin/runners/lib/contracts/external-contract.js +206 -206
- package/bin/runners/lib/contracts/guard.js +168 -168
- package/bin/runners/lib/contracts/index.js +89 -89
- package/bin/runners/lib/contracts/plan-validator.js +311 -311
- package/bin/runners/lib/contracts/route-contract.js +199 -199
- package/bin/runners/lib/contracts.js +804 -804
- package/bin/runners/lib/detect.js +89 -89
- package/bin/runners/lib/doctor/autofix.js +254 -254
- package/bin/runners/lib/doctor/index.js +37 -37
- package/bin/runners/lib/doctor/modules/dependencies.js +325 -325
- package/bin/runners/lib/doctor/modules/index.js +46 -46
- package/bin/runners/lib/doctor/modules/network.js +250 -250
- package/bin/runners/lib/doctor/modules/project.js +312 -312
- package/bin/runners/lib/doctor/modules/runtime.js +224 -224
- package/bin/runners/lib/doctor/modules/security.js +348 -348
- package/bin/runners/lib/doctor/modules/system.js +213 -213
- package/bin/runners/lib/doctor/modules/vibecheck.js +394 -394
- package/bin/runners/lib/doctor/reporter.js +262 -262
- package/bin/runners/lib/doctor/service.js +262 -262
- package/bin/runners/lib/doctor/types.js +113 -113
- package/bin/runners/lib/doctor/ui.js +263 -263
- package/bin/runners/lib/doctor-v2.js +608 -608
- package/bin/runners/lib/drift.js +425 -425
- package/bin/runners/lib/enforcement.js +72 -72
- package/bin/runners/lib/engines/accessibility-engine.js +190 -0
- package/bin/runners/lib/engines/api-consistency-engine.js +162 -0
- package/bin/runners/lib/engines/ast-cache.js +99 -0
- package/bin/runners/lib/engines/code-quality-engine.js +255 -0
- package/bin/runners/lib/engines/console-logs-engine.js +115 -0
- package/bin/runners/lib/engines/cross-file-analysis-engine.js +268 -0
- package/bin/runners/lib/engines/dead-code-engine.js +198 -0
- package/bin/runners/lib/engines/deprecated-api-engine.js +226 -0
- package/bin/runners/lib/engines/empty-catch-engine.js +150 -0
- package/bin/runners/lib/engines/file-filter.js +131 -0
- package/bin/runners/lib/engines/hardcoded-secrets-engine.js +251 -0
- package/bin/runners/lib/engines/mock-data-engine.js +272 -0
- package/bin/runners/lib/engines/parallel-processor.js +71 -0
- package/bin/runners/lib/engines/performance-issues-engine.js +265 -0
- package/bin/runners/lib/engines/security-vulnerabilities-engine.js +243 -0
- package/bin/runners/lib/engines/todo-fixme-engine.js +115 -0
- package/bin/runners/lib/engines/type-aware-engine.js +152 -0
- package/bin/runners/lib/engines/unsafe-regex-engine.js +225 -0
- package/bin/runners/lib/engines/vibecheck-engines/README.md +53 -0
- package/bin/runners/lib/engines/vibecheck-engines/index.js +15 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +164 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +291 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +83 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +198 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +275 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +167 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +217 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +139 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +140 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +164 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +234 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +217 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +78 -0
- package/bin/runners/lib/engines/vibecheck-engines/package.json +13 -0
- package/bin/runners/lib/enterprise-detect.js +603 -603
- package/bin/runners/lib/enterprise-init.js +942 -942
- package/bin/runners/lib/env-resolver.js +417 -417
- package/bin/runners/lib/env-template.js +66 -66
- package/bin/runners/lib/env.js +189 -189
- package/bin/runners/lib/extractors/client-calls.js +990 -990
- package/bin/runners/lib/extractors/fastify-route-dump.js +573 -573
- package/bin/runners/lib/extractors/fastify-routes.js +426 -426
- package/bin/runners/lib/extractors/index.js +363 -363
- package/bin/runners/lib/extractors/next-routes.js +524 -524
- package/bin/runners/lib/extractors/proof-graph.js +431 -431
- package/bin/runners/lib/extractors/route-matcher.js +451 -451
- package/bin/runners/lib/extractors/truthpack-v2.js +377 -377
- package/bin/runners/lib/extractors/ui-bindings.js +547 -547
- package/bin/runners/lib/findings-schema.js +281 -281
- package/bin/runners/lib/firewall-prompt.js +50 -50
- package/bin/runners/lib/global-flags.js +213 -213
- package/bin/runners/lib/graph/graph-builder.js +265 -265
- package/bin/runners/lib/graph/html-renderer.js +413 -413
- package/bin/runners/lib/graph/index.js +32 -32
- package/bin/runners/lib/graph/runtime-collector.js +215 -215
- package/bin/runners/lib/graph/static-extractor.js +518 -518
- package/bin/runners/lib/html-report.js +650 -650
- package/bin/runners/lib/interactive-menu.js +1496 -1496
- package/bin/runners/lib/llm.js +75 -75
- package/bin/runners/lib/meter.js +61 -61
- package/bin/runners/lib/missions/evidence.js +126 -126
- package/bin/runners/lib/patch.js +40 -40
- package/bin/runners/lib/permissions/auth-model.js +213 -213
- package/bin/runners/lib/permissions/idor-prover.js +205 -205
- package/bin/runners/lib/permissions/index.js +45 -45
- package/bin/runners/lib/permissions/matrix-builder.js +198 -198
- package/bin/runners/lib/pkgjson.js +28 -28
- package/bin/runners/lib/policy.js +295 -295
- package/bin/runners/lib/preflight.js +142 -142
- package/bin/runners/lib/reality/correlation-detectors.js +359 -359
- package/bin/runners/lib/reality/index.js +318 -318
- package/bin/runners/lib/reality/request-hashing.js +416 -416
- package/bin/runners/lib/reality/request-mapper.js +453 -453
- package/bin/runners/lib/reality/safety-rails.js +463 -463
- package/bin/runners/lib/reality/semantic-snapshot.js +408 -408
- package/bin/runners/lib/reality/toast-detector.js +393 -393
- package/bin/runners/lib/reality-findings.js +84 -84
- package/bin/runners/lib/receipts.js +179 -179
- package/bin/runners/lib/redact.js +29 -29
- package/bin/runners/lib/replay/capsule-manager.js +154 -154
- package/bin/runners/lib/replay/index.js +263 -263
- package/bin/runners/lib/replay/player.js +348 -348
- package/bin/runners/lib/replay/recorder.js +331 -331
- package/bin/runners/lib/report-output.js +187 -187
- package/bin/runners/lib/report.js +135 -135
- package/bin/runners/lib/route-detection.js +1140 -1140
- package/bin/runners/lib/sandbox/index.js +59 -59
- package/bin/runners/lib/sandbox/proof-chain.js +399 -399
- package/bin/runners/lib/sandbox/sandbox-runner.js +205 -205
- package/bin/runners/lib/sandbox/worktree.js +174 -174
- package/bin/runners/lib/scan-output.js +525 -190
- package/bin/runners/lib/schema-validator.js +350 -350
- package/bin/runners/lib/schemas/contracts.schema.json +160 -160
- package/bin/runners/lib/schemas/finding.schema.json +100 -100
- package/bin/runners/lib/schemas/mission-pack.schema.json +206 -206
- package/bin/runners/lib/schemas/proof-graph.schema.json +176 -176
- package/bin/runners/lib/schemas/reality-report.schema.json +162 -162
- package/bin/runners/lib/schemas/share-pack.schema.json +180 -180
- package/bin/runners/lib/schemas/ship-report.schema.json +117 -117
- package/bin/runners/lib/schemas/truthpack-v2.schema.json +303 -303
- package/bin/runners/lib/schemas/validator.js +438 -438
- package/bin/runners/lib/score-history.js +282 -282
- package/bin/runners/lib/share-pack.js +239 -239
- package/bin/runners/lib/snippets.js +67 -67
- package/bin/runners/lib/status-output.js +253 -253
- package/bin/runners/lib/terminal-ui.js +351 -271
- package/bin/runners/lib/upsell.js +510 -510
- package/bin/runners/lib/usage.js +153 -153
- package/bin/runners/lib/validate-patch.js +156 -156
- package/bin/runners/lib/verdict-engine.js +628 -628
- package/bin/runners/reality/engine.js +917 -917
- package/bin/runners/reality/flows.js +122 -122
- package/bin/runners/reality/report.js +378 -378
- package/bin/runners/reality/session.js +193 -193
- package/bin/runners/runGuard.js +168 -168
- package/bin/runners/runProof.zip +0 -0
- package/bin/runners/runProve.js +8 -0
- package/bin/runners/runReality.js +14 -0
- package/bin/runners/runScan.js +17 -1
- package/bin/runners/runTruth.js +15 -3
- package/mcp-server/tier-auth.js +4 -4
- package/mcp-server/tools/index.js +72 -72
- package/package.json +1 -1
|
@@ -1,89 +1,89 @@
|
|
|
1
|
-
// bin/runners/lib/detect.js
|
|
2
|
-
const fs = require("fs");
|
|
3
|
-
const path = require("path");
|
|
4
|
-
const fg = require("fast-glob");
|
|
5
|
-
|
|
6
|
-
function fileExists(root, rel) {
|
|
7
|
-
return fs.existsSync(path.join(root, rel));
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
function detectPackageManager(root) {
|
|
11
|
-
if (fileExists(root, "pnpm-lock.yaml")) return "pnpm";
|
|
12
|
-
if (fileExists(root, "yarn.lock")) return "yarn";
|
|
13
|
-
if (fileExists(root, "package-lock.json")) return "npm";
|
|
14
|
-
return "npm";
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function detectNext(root, pkg) {
|
|
18
|
-
const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
|
|
19
|
-
const hasDep = !!deps.next;
|
|
20
|
-
const hasConfig = fileExists(root, "next.config.js") || fileExists(root, "next.config.mjs") || fileExists(root, "next.config.ts");
|
|
21
|
-
const hasApp = fileExists(root, "app") || fileExists(root, "src/app");
|
|
22
|
-
const hasPages = fileExists(root, "pages") || fileExists(root, "src/pages");
|
|
23
|
-
return { enabled: hasDep || hasConfig || hasApp || hasPages, hasDep, hasConfig, hasApp, hasPages };
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function detectFastify(root, pkg) {
|
|
27
|
-
const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
|
|
28
|
-
const hasDep = !!deps.fastify;
|
|
29
|
-
return { enabled: hasDep, hasDep };
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function scoreFastifyEntry(code, rel) {
|
|
33
|
-
let s = 0;
|
|
34
|
-
if (/\bfastify\s*\(/.test(code)) s += 10;
|
|
35
|
-
if (/from\s+['"]fastify['"]|require\(['"]fastify['"]\)/.test(code)) s += 8;
|
|
36
|
-
if (/\baddHook\s*\(/.test(code)) s += 2;
|
|
37
|
-
if (/\bregister\s*\(/.test(code)) s += 2;
|
|
38
|
-
if (/\.(listen|ready)\s*\(/.test(code)) s += 10;
|
|
39
|
-
if (/createServer|http\.createServer/.test(code)) s += 1;
|
|
40
|
-
if (rel.includes("server")) s += 2;
|
|
41
|
-
if (rel.includes("api")) s += 1;
|
|
42
|
-
if (rel.endsWith(".ts")) s += 1;
|
|
43
|
-
return s;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async function detectFastifyEntry(root) {
|
|
47
|
-
const common = [
|
|
48
|
-
"src/server.ts", "src/server.js",
|
|
49
|
-
"server.ts", "server.js",
|
|
50
|
-
"src/index.ts", "src/index.js",
|
|
51
|
-
"index.ts", "index.js",
|
|
52
|
-
"src/app.ts", "src/app.js",
|
|
53
|
-
"app.ts", "app.js"
|
|
54
|
-
];
|
|
55
|
-
|
|
56
|
-
for (const rel of common) {
|
|
57
|
-
const abs = path.join(root, rel);
|
|
58
|
-
if (!fs.existsSync(abs)) continue;
|
|
59
|
-
const code = fs.readFileSync(abs, "utf8");
|
|
60
|
-
if (/\bfastify\s*\(/.test(code)) return rel;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const files = await fg(["**/*.{ts,js}"], {
|
|
64
|
-
cwd: root,
|
|
65
|
-
onlyFiles: true,
|
|
66
|
-
absolute: false,
|
|
67
|
-
ignore: ["**/node_modules/**", "**/.next/**", "**/dist/**", "**/build/**"]
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
let best = null;
|
|
71
|
-
for (const rel of files.slice(0, 800)) {
|
|
72
|
-
const abs = path.join(root, rel);
|
|
73
|
-
let code;
|
|
74
|
-
try { code = fs.readFileSync(abs, "utf8"); } catch { continue; }
|
|
75
|
-
if (!/\bfastify\s*\(/.test(code)) continue;
|
|
76
|
-
|
|
77
|
-
const s = scoreFastifyEntry(code, rel);
|
|
78
|
-
if (!best || s > best.score) best = { rel, score: s };
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return best?.rel || null;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
module.exports = {
|
|
85
|
-
detectPackageManager,
|
|
86
|
-
detectNext,
|
|
87
|
-
detectFastify,
|
|
88
|
-
detectFastifyEntry
|
|
89
|
-
};
|
|
1
|
+
// bin/runners/lib/detect.js
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const fg = require("fast-glob");
|
|
5
|
+
|
|
6
|
+
function fileExists(root, rel) {
|
|
7
|
+
return fs.existsSync(path.join(root, rel));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function detectPackageManager(root) {
|
|
11
|
+
if (fileExists(root, "pnpm-lock.yaml")) return "pnpm";
|
|
12
|
+
if (fileExists(root, "yarn.lock")) return "yarn";
|
|
13
|
+
if (fileExists(root, "package-lock.json")) return "npm";
|
|
14
|
+
return "npm";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function detectNext(root, pkg) {
|
|
18
|
+
const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
|
|
19
|
+
const hasDep = !!deps.next;
|
|
20
|
+
const hasConfig = fileExists(root, "next.config.js") || fileExists(root, "next.config.mjs") || fileExists(root, "next.config.ts");
|
|
21
|
+
const hasApp = fileExists(root, "app") || fileExists(root, "src/app");
|
|
22
|
+
const hasPages = fileExists(root, "pages") || fileExists(root, "src/pages");
|
|
23
|
+
return { enabled: hasDep || hasConfig || hasApp || hasPages, hasDep, hasConfig, hasApp, hasPages };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function detectFastify(root, pkg) {
|
|
27
|
+
const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
|
|
28
|
+
const hasDep = !!deps.fastify;
|
|
29
|
+
return { enabled: hasDep, hasDep };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function scoreFastifyEntry(code, rel) {
|
|
33
|
+
let s = 0;
|
|
34
|
+
if (/\bfastify\s*\(/.test(code)) s += 10;
|
|
35
|
+
if (/from\s+['"]fastify['"]|require\(['"]fastify['"]\)/.test(code)) s += 8;
|
|
36
|
+
if (/\baddHook\s*\(/.test(code)) s += 2;
|
|
37
|
+
if (/\bregister\s*\(/.test(code)) s += 2;
|
|
38
|
+
if (/\.(listen|ready)\s*\(/.test(code)) s += 10;
|
|
39
|
+
if (/createServer|http\.createServer/.test(code)) s += 1;
|
|
40
|
+
if (rel.includes("server")) s += 2;
|
|
41
|
+
if (rel.includes("api")) s += 1;
|
|
42
|
+
if (rel.endsWith(".ts")) s += 1;
|
|
43
|
+
return s;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function detectFastifyEntry(root) {
|
|
47
|
+
const common = [
|
|
48
|
+
"src/server.ts", "src/server.js",
|
|
49
|
+
"server.ts", "server.js",
|
|
50
|
+
"src/index.ts", "src/index.js",
|
|
51
|
+
"index.ts", "index.js",
|
|
52
|
+
"src/app.ts", "src/app.js",
|
|
53
|
+
"app.ts", "app.js"
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
for (const rel of common) {
|
|
57
|
+
const abs = path.join(root, rel);
|
|
58
|
+
if (!fs.existsSync(abs)) continue;
|
|
59
|
+
const code = fs.readFileSync(abs, "utf8");
|
|
60
|
+
if (/\bfastify\s*\(/.test(code)) return rel;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const files = await fg(["**/*.{ts,js}"], {
|
|
64
|
+
cwd: root,
|
|
65
|
+
onlyFiles: true,
|
|
66
|
+
absolute: false,
|
|
67
|
+
ignore: ["**/node_modules/**", "**/.next/**", "**/dist/**", "**/build/**"]
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
let best = null;
|
|
71
|
+
for (const rel of files.slice(0, 800)) {
|
|
72
|
+
const abs = path.join(root, rel);
|
|
73
|
+
let code;
|
|
74
|
+
try { code = fs.readFileSync(abs, "utf8"); } catch { continue; }
|
|
75
|
+
if (!/\bfastify\s*\(/.test(code)) continue;
|
|
76
|
+
|
|
77
|
+
const s = scoreFastifyEntry(code, rel);
|
|
78
|
+
if (!best || s > best.score) best = { rel, score: s };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return best?.rel || null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = {
|
|
85
|
+
detectPackageManager,
|
|
86
|
+
detectNext,
|
|
87
|
+
detectFastify,
|
|
88
|
+
detectFastifyEntry
|
|
89
|
+
};
|
|
@@ -1,254 +1,254 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Doctor Auto-Fix Engine
|
|
3
|
-
*
|
|
4
|
-
* Executes fixes for diagnostic issues automatically when safe
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const fs = require('fs');
|
|
8
|
-
const path = require('path');
|
|
9
|
-
const { execSync, spawn } = require('child_process');
|
|
10
|
-
const { FIX_TYPE, SEVERITY } = require('./types');
|
|
11
|
-
|
|
12
|
-
const FIX_RESULT = {
|
|
13
|
-
SUCCESS: 'success',
|
|
14
|
-
FAILED: 'failed',
|
|
15
|
-
SKIPPED: 'skipped',
|
|
16
|
-
REQUIRES_CONFIRMATION: 'requires_confirmation',
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
function canAutoFix(fix) {
|
|
20
|
-
if (!fix) return false;
|
|
21
|
-
if (fix.dangerous) return false;
|
|
22
|
-
if (fix.autoFixable === false) return false;
|
|
23
|
-
|
|
24
|
-
// Only auto-fix commands and file operations
|
|
25
|
-
return [FIX_TYPE.COMMAND, FIX_TYPE.FILE_CREATE, FIX_TYPE.FILE_EDIT].includes(fix.type);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
async function executeFix(fix, projectPath, options = {}) {
|
|
29
|
-
const { dryRun = false, interactive = false } = options;
|
|
30
|
-
|
|
31
|
-
if (!fix) {
|
|
32
|
-
return { status: FIX_RESULT.SKIPPED, reason: 'No fix provided' };
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (fix.dangerous && !options.allowDangerous) {
|
|
36
|
-
return {
|
|
37
|
-
status: FIX_RESULT.REQUIRES_CONFIRMATION,
|
|
38
|
-
reason: 'Fix is marked as dangerous',
|
|
39
|
-
fix,
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (dryRun) {
|
|
44
|
-
return {
|
|
45
|
-
status: FIX_RESULT.SKIPPED,
|
|
46
|
-
reason: 'Dry run mode',
|
|
47
|
-
wouldExecute: fix,
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
try {
|
|
52
|
-
switch (fix.type) {
|
|
53
|
-
case FIX_TYPE.COMMAND:
|
|
54
|
-
return await executeCommand(fix, projectPath, options);
|
|
55
|
-
|
|
56
|
-
case FIX_TYPE.FILE_CREATE:
|
|
57
|
-
return await createFile(fix, projectPath);
|
|
58
|
-
|
|
59
|
-
case FIX_TYPE.FILE_EDIT:
|
|
60
|
-
return await editFile(fix, projectPath);
|
|
61
|
-
|
|
62
|
-
case FIX_TYPE.MANUAL:
|
|
63
|
-
return {
|
|
64
|
-
status: FIX_RESULT.SKIPPED,
|
|
65
|
-
reason: 'Manual fix required',
|
|
66
|
-
instructions: fix.description,
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
case FIX_TYPE.LINK:
|
|
70
|
-
return {
|
|
71
|
-
status: FIX_RESULT.SKIPPED,
|
|
72
|
-
reason: 'External documentation',
|
|
73
|
-
url: fix.url,
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
default:
|
|
77
|
-
return {
|
|
78
|
-
status: FIX_RESULT.SKIPPED,
|
|
79
|
-
reason: `Unknown fix type: ${fix.type}`,
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
} catch (err) {
|
|
83
|
-
return {
|
|
84
|
-
status: FIX_RESULT.FAILED,
|
|
85
|
-
error: err.message,
|
|
86
|
-
fix,
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
async function executeCommand(fix, projectPath, options = {}) {
|
|
92
|
-
const { timeout = 60000 } = options;
|
|
93
|
-
|
|
94
|
-
if (!fix.command) {
|
|
95
|
-
return { status: FIX_RESULT.SKIPPED, reason: 'No command specified' };
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
try {
|
|
99
|
-
const result = execSync(fix.command, {
|
|
100
|
-
cwd: projectPath,
|
|
101
|
-
encoding: 'utf8',
|
|
102
|
-
timeout,
|
|
103
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
return {
|
|
107
|
-
status: FIX_RESULT.SUCCESS,
|
|
108
|
-
command: fix.command,
|
|
109
|
-
output: result.trim(),
|
|
110
|
-
};
|
|
111
|
-
} catch (err) {
|
|
112
|
-
return {
|
|
113
|
-
status: FIX_RESULT.FAILED,
|
|
114
|
-
command: fix.command,
|
|
115
|
-
error: err.message,
|
|
116
|
-
stderr: err.stderr,
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
async function createFile(fix, projectPath) {
|
|
122
|
-
if (!fix.path || fix.content === undefined) {
|
|
123
|
-
return { status: FIX_RESULT.SKIPPED, reason: 'No path or content specified' };
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const fullPath = path.isAbsolute(fix.path) ? fix.path : path.join(projectPath, fix.path);
|
|
127
|
-
|
|
128
|
-
// Don't overwrite existing files
|
|
129
|
-
if (fs.existsSync(fullPath)) {
|
|
130
|
-
return {
|
|
131
|
-
status: FIX_RESULT.SKIPPED,
|
|
132
|
-
reason: 'File already exists',
|
|
133
|
-
path: fullPath,
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Ensure directory exists
|
|
138
|
-
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
|
|
139
|
-
|
|
140
|
-
// Write file
|
|
141
|
-
fs.writeFileSync(fullPath, fix.content);
|
|
142
|
-
|
|
143
|
-
return {
|
|
144
|
-
status: FIX_RESULT.SUCCESS,
|
|
145
|
-
action: 'created',
|
|
146
|
-
path: fullPath,
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
async function editFile(fix, projectPath) {
|
|
151
|
-
if (!fix.path || fix.content === undefined) {
|
|
152
|
-
return { status: FIX_RESULT.SKIPPED, reason: 'No path or content specified' };
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const fullPath = path.isAbsolute(fix.path) ? fix.path : path.join(projectPath, fix.path);
|
|
156
|
-
|
|
157
|
-
// Backup existing file
|
|
158
|
-
if (fs.existsSync(fullPath)) {
|
|
159
|
-
const backupPath = `${fullPath}.doctor-backup`;
|
|
160
|
-
fs.copyFileSync(fullPath, backupPath);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Write new content
|
|
164
|
-
fs.writeFileSync(fullPath, fix.content);
|
|
165
|
-
|
|
166
|
-
return {
|
|
167
|
-
status: FIX_RESULT.SUCCESS,
|
|
168
|
-
action: 'edited',
|
|
169
|
-
path: fullPath,
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
async function autoFixAll(diagnostics, projectPath, options = {}) {
|
|
174
|
-
const {
|
|
175
|
-
dryRun = false,
|
|
176
|
-
severity = [SEVERITY.CRITICAL, SEVERITY.ERROR],
|
|
177
|
-
maxFixes = 10,
|
|
178
|
-
} = options;
|
|
179
|
-
|
|
180
|
-
const results = [];
|
|
181
|
-
let fixCount = 0;
|
|
182
|
-
|
|
183
|
-
// Sort by severity (critical first)
|
|
184
|
-
const severityOrder = [SEVERITY.CRITICAL, SEVERITY.ERROR, SEVERITY.WARNING];
|
|
185
|
-
const sortedDiagnostics = [...diagnostics].sort((a, b) => {
|
|
186
|
-
return severityOrder.indexOf(a.severity) - severityOrder.indexOf(b.severity);
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
for (const diagnostic of sortedDiagnostics) {
|
|
190
|
-
if (fixCount >= maxFixes) break;
|
|
191
|
-
if (!severity.includes(diagnostic.severity)) continue;
|
|
192
|
-
if (!diagnostic.fixes || diagnostic.fixes.length === 0) continue;
|
|
193
|
-
|
|
194
|
-
// Try the first auto-fixable fix
|
|
195
|
-
const fix = diagnostic.fixes.find(f => canAutoFix(f));
|
|
196
|
-
if (!fix) continue;
|
|
197
|
-
|
|
198
|
-
const result = await executeFix(fix, projectPath, { ...options, dryRun });
|
|
199
|
-
results.push({
|
|
200
|
-
diagnosticId: diagnostic.id,
|
|
201
|
-
diagnosticName: diagnostic.name,
|
|
202
|
-
...result,
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
if (result.status === FIX_RESULT.SUCCESS) {
|
|
206
|
-
fixCount++;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
return {
|
|
211
|
-
attempted: results.length,
|
|
212
|
-
succeeded: results.filter(r => r.status === FIX_RESULT.SUCCESS).length,
|
|
213
|
-
failed: results.filter(r => r.status === FIX_RESULT.FAILED).length,
|
|
214
|
-
skipped: results.filter(r => r.status === FIX_RESULT.SKIPPED).length,
|
|
215
|
-
results,
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
function getFixCommands(diagnostics, options = {}) {
|
|
220
|
-
const { severity = [SEVERITY.CRITICAL, SEVERITY.ERROR, SEVERITY.WARNING] } = options;
|
|
221
|
-
|
|
222
|
-
const commands = [];
|
|
223
|
-
|
|
224
|
-
for (const diagnostic of diagnostics) {
|
|
225
|
-
if (!severity.includes(diagnostic.severity)) continue;
|
|
226
|
-
if (!diagnostic.fixes) continue;
|
|
227
|
-
|
|
228
|
-
for (const fix of diagnostic.fixes) {
|
|
229
|
-
if (fix.type === FIX_TYPE.COMMAND && fix.command) {
|
|
230
|
-
commands.push({
|
|
231
|
-
diagnosticId: diagnostic.id,
|
|
232
|
-
diagnosticName: diagnostic.name,
|
|
233
|
-
command: fix.command,
|
|
234
|
-
description: fix.description,
|
|
235
|
-
dangerous: fix.dangerous,
|
|
236
|
-
autoFixable: canAutoFix(fix),
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
return commands;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
module.exports = {
|
|
246
|
-
FIX_RESULT,
|
|
247
|
-
canAutoFix,
|
|
248
|
-
executeFix,
|
|
249
|
-
executeCommand,
|
|
250
|
-
createFile,
|
|
251
|
-
editFile,
|
|
252
|
-
autoFixAll,
|
|
253
|
-
getFixCommands,
|
|
254
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Doctor Auto-Fix Engine
|
|
3
|
+
*
|
|
4
|
+
* Executes fixes for diagnostic issues automatically when safe
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const { execSync, spawn } = require('child_process');
|
|
10
|
+
const { FIX_TYPE, SEVERITY } = require('./types');
|
|
11
|
+
|
|
12
|
+
const FIX_RESULT = {
|
|
13
|
+
SUCCESS: 'success',
|
|
14
|
+
FAILED: 'failed',
|
|
15
|
+
SKIPPED: 'skipped',
|
|
16
|
+
REQUIRES_CONFIRMATION: 'requires_confirmation',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
function canAutoFix(fix) {
|
|
20
|
+
if (!fix) return false;
|
|
21
|
+
if (fix.dangerous) return false;
|
|
22
|
+
if (fix.autoFixable === false) return false;
|
|
23
|
+
|
|
24
|
+
// Only auto-fix commands and file operations
|
|
25
|
+
return [FIX_TYPE.COMMAND, FIX_TYPE.FILE_CREATE, FIX_TYPE.FILE_EDIT].includes(fix.type);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function executeFix(fix, projectPath, options = {}) {
|
|
29
|
+
const { dryRun = false, interactive = false } = options;
|
|
30
|
+
|
|
31
|
+
if (!fix) {
|
|
32
|
+
return { status: FIX_RESULT.SKIPPED, reason: 'No fix provided' };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (fix.dangerous && !options.allowDangerous) {
|
|
36
|
+
return {
|
|
37
|
+
status: FIX_RESULT.REQUIRES_CONFIRMATION,
|
|
38
|
+
reason: 'Fix is marked as dangerous',
|
|
39
|
+
fix,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (dryRun) {
|
|
44
|
+
return {
|
|
45
|
+
status: FIX_RESULT.SKIPPED,
|
|
46
|
+
reason: 'Dry run mode',
|
|
47
|
+
wouldExecute: fix,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
switch (fix.type) {
|
|
53
|
+
case FIX_TYPE.COMMAND:
|
|
54
|
+
return await executeCommand(fix, projectPath, options);
|
|
55
|
+
|
|
56
|
+
case FIX_TYPE.FILE_CREATE:
|
|
57
|
+
return await createFile(fix, projectPath);
|
|
58
|
+
|
|
59
|
+
case FIX_TYPE.FILE_EDIT:
|
|
60
|
+
return await editFile(fix, projectPath);
|
|
61
|
+
|
|
62
|
+
case FIX_TYPE.MANUAL:
|
|
63
|
+
return {
|
|
64
|
+
status: FIX_RESULT.SKIPPED,
|
|
65
|
+
reason: 'Manual fix required',
|
|
66
|
+
instructions: fix.description,
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
case FIX_TYPE.LINK:
|
|
70
|
+
return {
|
|
71
|
+
status: FIX_RESULT.SKIPPED,
|
|
72
|
+
reason: 'External documentation',
|
|
73
|
+
url: fix.url,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
default:
|
|
77
|
+
return {
|
|
78
|
+
status: FIX_RESULT.SKIPPED,
|
|
79
|
+
reason: `Unknown fix type: ${fix.type}`,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
} catch (err) {
|
|
83
|
+
return {
|
|
84
|
+
status: FIX_RESULT.FAILED,
|
|
85
|
+
error: err.message,
|
|
86
|
+
fix,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function executeCommand(fix, projectPath, options = {}) {
|
|
92
|
+
const { timeout = 60000 } = options;
|
|
93
|
+
|
|
94
|
+
if (!fix.command) {
|
|
95
|
+
return { status: FIX_RESULT.SKIPPED, reason: 'No command specified' };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
const result = execSync(fix.command, {
|
|
100
|
+
cwd: projectPath,
|
|
101
|
+
encoding: 'utf8',
|
|
102
|
+
timeout,
|
|
103
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
status: FIX_RESULT.SUCCESS,
|
|
108
|
+
command: fix.command,
|
|
109
|
+
output: result.trim(),
|
|
110
|
+
};
|
|
111
|
+
} catch (err) {
|
|
112
|
+
return {
|
|
113
|
+
status: FIX_RESULT.FAILED,
|
|
114
|
+
command: fix.command,
|
|
115
|
+
error: err.message,
|
|
116
|
+
stderr: err.stderr,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async function createFile(fix, projectPath) {
|
|
122
|
+
if (!fix.path || fix.content === undefined) {
|
|
123
|
+
return { status: FIX_RESULT.SKIPPED, reason: 'No path or content specified' };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const fullPath = path.isAbsolute(fix.path) ? fix.path : path.join(projectPath, fix.path);
|
|
127
|
+
|
|
128
|
+
// Don't overwrite existing files
|
|
129
|
+
if (fs.existsSync(fullPath)) {
|
|
130
|
+
return {
|
|
131
|
+
status: FIX_RESULT.SKIPPED,
|
|
132
|
+
reason: 'File already exists',
|
|
133
|
+
path: fullPath,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Ensure directory exists
|
|
138
|
+
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
|
|
139
|
+
|
|
140
|
+
// Write file
|
|
141
|
+
fs.writeFileSync(fullPath, fix.content);
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
status: FIX_RESULT.SUCCESS,
|
|
145
|
+
action: 'created',
|
|
146
|
+
path: fullPath,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async function editFile(fix, projectPath) {
|
|
151
|
+
if (!fix.path || fix.content === undefined) {
|
|
152
|
+
return { status: FIX_RESULT.SKIPPED, reason: 'No path or content specified' };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const fullPath = path.isAbsolute(fix.path) ? fix.path : path.join(projectPath, fix.path);
|
|
156
|
+
|
|
157
|
+
// Backup existing file
|
|
158
|
+
if (fs.existsSync(fullPath)) {
|
|
159
|
+
const backupPath = `${fullPath}.doctor-backup`;
|
|
160
|
+
fs.copyFileSync(fullPath, backupPath);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Write new content
|
|
164
|
+
fs.writeFileSync(fullPath, fix.content);
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
status: FIX_RESULT.SUCCESS,
|
|
168
|
+
action: 'edited',
|
|
169
|
+
path: fullPath,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async function autoFixAll(diagnostics, projectPath, options = {}) {
|
|
174
|
+
const {
|
|
175
|
+
dryRun = false,
|
|
176
|
+
severity = [SEVERITY.CRITICAL, SEVERITY.ERROR],
|
|
177
|
+
maxFixes = 10,
|
|
178
|
+
} = options;
|
|
179
|
+
|
|
180
|
+
const results = [];
|
|
181
|
+
let fixCount = 0;
|
|
182
|
+
|
|
183
|
+
// Sort by severity (critical first)
|
|
184
|
+
const severityOrder = [SEVERITY.CRITICAL, SEVERITY.ERROR, SEVERITY.WARNING];
|
|
185
|
+
const sortedDiagnostics = [...diagnostics].sort((a, b) => {
|
|
186
|
+
return severityOrder.indexOf(a.severity) - severityOrder.indexOf(b.severity);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
for (const diagnostic of sortedDiagnostics) {
|
|
190
|
+
if (fixCount >= maxFixes) break;
|
|
191
|
+
if (!severity.includes(diagnostic.severity)) continue;
|
|
192
|
+
if (!diagnostic.fixes || diagnostic.fixes.length === 0) continue;
|
|
193
|
+
|
|
194
|
+
// Try the first auto-fixable fix
|
|
195
|
+
const fix = diagnostic.fixes.find(f => canAutoFix(f));
|
|
196
|
+
if (!fix) continue;
|
|
197
|
+
|
|
198
|
+
const result = await executeFix(fix, projectPath, { ...options, dryRun });
|
|
199
|
+
results.push({
|
|
200
|
+
diagnosticId: diagnostic.id,
|
|
201
|
+
diagnosticName: diagnostic.name,
|
|
202
|
+
...result,
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
if (result.status === FIX_RESULT.SUCCESS) {
|
|
206
|
+
fixCount++;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return {
|
|
211
|
+
attempted: results.length,
|
|
212
|
+
succeeded: results.filter(r => r.status === FIX_RESULT.SUCCESS).length,
|
|
213
|
+
failed: results.filter(r => r.status === FIX_RESULT.FAILED).length,
|
|
214
|
+
skipped: results.filter(r => r.status === FIX_RESULT.SKIPPED).length,
|
|
215
|
+
results,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function getFixCommands(diagnostics, options = {}) {
|
|
220
|
+
const { severity = [SEVERITY.CRITICAL, SEVERITY.ERROR, SEVERITY.WARNING] } = options;
|
|
221
|
+
|
|
222
|
+
const commands = [];
|
|
223
|
+
|
|
224
|
+
for (const diagnostic of diagnostics) {
|
|
225
|
+
if (!severity.includes(diagnostic.severity)) continue;
|
|
226
|
+
if (!diagnostic.fixes) continue;
|
|
227
|
+
|
|
228
|
+
for (const fix of diagnostic.fixes) {
|
|
229
|
+
if (fix.type === FIX_TYPE.COMMAND && fix.command) {
|
|
230
|
+
commands.push({
|
|
231
|
+
diagnosticId: diagnostic.id,
|
|
232
|
+
diagnosticName: diagnostic.name,
|
|
233
|
+
command: fix.command,
|
|
234
|
+
description: fix.description,
|
|
235
|
+
dangerous: fix.dangerous,
|
|
236
|
+
autoFixable: canAutoFix(fix),
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return commands;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
module.exports = {
|
|
246
|
+
FIX_RESULT,
|
|
247
|
+
canAutoFix,
|
|
248
|
+
executeFix,
|
|
249
|
+
executeCommand,
|
|
250
|
+
createFile,
|
|
251
|
+
editFile,
|
|
252
|
+
autoFixAll,
|
|
253
|
+
getFixCommands,
|
|
254
|
+
};
|