@vibecheckai/cli 3.0.2 → 3.0.3
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/package.json +9 -1
- package/bin/cli-hygiene.js +0 -241
- package/bin/guardrail.js +0 -834
- package/bin/runners/cli-utils.js +0 -1070
- package/bin/runners/context/ai-task-decomposer.js +0 -337
- package/bin/runners/context/analyzer.js +0 -462
- package/bin/runners/context/api-contracts.js +0 -427
- package/bin/runners/context/context-diff.js +0 -342
- package/bin/runners/context/context-pruner.js +0 -291
- package/bin/runners/context/dependency-graph.js +0 -414
- package/bin/runners/context/generators/claude.js +0 -107
- package/bin/runners/context/generators/codex.js +0 -108
- package/bin/runners/context/generators/copilot.js +0 -119
- package/bin/runners/context/generators/cursor.js +0 -514
- package/bin/runners/context/generators/mcp.js +0 -151
- package/bin/runners/context/generators/windsurf.js +0 -180
- package/bin/runners/context/git-context.js +0 -302
- package/bin/runners/context/index.js +0 -1042
- package/bin/runners/context/insights.js +0 -173
- package/bin/runners/context/mcp-server/generate-rules.js +0 -337
- package/bin/runners/context/mcp-server/index.js +0 -1176
- package/bin/runners/context/mcp-server/package.json +0 -24
- package/bin/runners/context/memory.js +0 -200
- package/bin/runners/context/monorepo.js +0 -215
- package/bin/runners/context/multi-repo-federation.js +0 -404
- package/bin/runners/context/patterns.js +0 -253
- package/bin/runners/context/proof-context.js +0 -972
- package/bin/runners/context/security-scanner.js +0 -303
- package/bin/runners/context/semantic-search.js +0 -350
- package/bin/runners/context/shared.js +0 -264
- package/bin/runners/context/team-conventions.js +0 -310
- package/bin/runners/lib/ai-bridge.js +0 -416
- package/bin/runners/lib/analysis-core.js +0 -271
- package/bin/runners/lib/analyzers.js +0 -541
- package/bin/runners/lib/audit-bridge.js +0 -391
- package/bin/runners/lib/auth-truth.js +0 -193
- package/bin/runners/lib/auth.js +0 -215
- package/bin/runners/lib/backup.js +0 -62
- package/bin/runners/lib/billing.js +0 -107
- package/bin/runners/lib/claims.js +0 -118
- package/bin/runners/lib/cli-ui.js +0 -540
- package/bin/runners/lib/compliance-bridge-new.js +0 -0
- package/bin/runners/lib/compliance-bridge.js +0 -165
- package/bin/runners/lib/contracts/auth-contract.js +0 -194
- package/bin/runners/lib/contracts/env-contract.js +0 -178
- package/bin/runners/lib/contracts/external-contract.js +0 -198
- package/bin/runners/lib/contracts/guard.js +0 -168
- package/bin/runners/lib/contracts/index.js +0 -89
- package/bin/runners/lib/contracts/plan-validator.js +0 -311
- package/bin/runners/lib/contracts/route-contract.js +0 -192
- package/bin/runners/lib/detect.js +0 -89
- package/bin/runners/lib/doctor/autofix.js +0 -254
- package/bin/runners/lib/doctor/index.js +0 -37
- package/bin/runners/lib/doctor/modules/dependencies.js +0 -325
- package/bin/runners/lib/doctor/modules/index.js +0 -46
- package/bin/runners/lib/doctor/modules/network.js +0 -250
- package/bin/runners/lib/doctor/modules/project.js +0 -312
- package/bin/runners/lib/doctor/modules/runtime.js +0 -224
- package/bin/runners/lib/doctor/modules/security.js +0 -348
- package/bin/runners/lib/doctor/modules/system.js +0 -213
- package/bin/runners/lib/doctor/modules/vibecheck.js +0 -394
- package/bin/runners/lib/doctor/reporter.js +0 -262
- package/bin/runners/lib/doctor/service.js +0 -262
- package/bin/runners/lib/doctor/types.js +0 -113
- package/bin/runners/lib/doctor/ui.js +0 -263
- package/bin/runners/lib/doctor-enhanced.js +0 -233
- package/bin/runners/lib/doctor-v2.js +0 -608
- package/bin/runners/lib/enforcement.js +0 -72
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Route Contract Builder
|
|
3
|
-
* Builds routes.json contract from truthpack
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
"use strict";
|
|
7
|
-
|
|
8
|
-
const crypto = require("crypto");
|
|
9
|
-
|
|
10
|
-
function sha256(text) {
|
|
11
|
-
return crypto.createHash("sha256").update(text).digest("hex").slice(0, 16);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Build routes contract from truthpack
|
|
16
|
-
*/
|
|
17
|
-
function buildRouteContract(truthpack) {
|
|
18
|
-
const contract = {
|
|
19
|
-
version: "1.0.0",
|
|
20
|
-
generatedAt: new Date().toISOString(),
|
|
21
|
-
routes: []
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
const serverRoutes = truthpack?.routes?.server || [];
|
|
25
|
-
|
|
26
|
-
for (const route of serverRoutes) {
|
|
27
|
-
const routeSpec = {
|
|
28
|
-
id: `route_${sha256(route.method + "_" + route.path)}`,
|
|
29
|
-
method: route.method,
|
|
30
|
-
path: route.path,
|
|
31
|
-
handler: route.handler || "unknown",
|
|
32
|
-
auth: inferAuthRequirement(route, truthpack),
|
|
33
|
-
roles: inferRoles(route, truthpack),
|
|
34
|
-
confidence: route.confidence || "med",
|
|
35
|
-
evidence: route.evidence || []
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
contract.routes.push(routeSpec);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return contract;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Infer auth requirement from route and truthpack
|
|
46
|
-
*/
|
|
47
|
-
function inferAuthRequirement(route, truthpack) {
|
|
48
|
-
const authPatterns = truthpack?.auth?.nextMatcherPatterns || [];
|
|
49
|
-
const path = route.path;
|
|
50
|
-
|
|
51
|
-
// Check if path matches any protected pattern
|
|
52
|
-
for (const pattern of authPatterns) {
|
|
53
|
-
if (matchesPattern(path, pattern)) {
|
|
54
|
-
return "required";
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Check for auth hooks in Fastify
|
|
59
|
-
if (route.hooks?.includes("onRequest") || route.hooks?.includes("preHandler")) {
|
|
60
|
-
return "required";
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Check for public API patterns
|
|
64
|
-
if (path.includes("/public/") || path.includes("/health") || path.includes("/status")) {
|
|
65
|
-
return "none";
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return "optional";
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Infer roles from route metadata
|
|
73
|
-
*/
|
|
74
|
-
function inferRoles(route, truthpack) {
|
|
75
|
-
const roles = [];
|
|
76
|
-
|
|
77
|
-
// Check for admin patterns in path
|
|
78
|
-
if (route.path.includes("/admin")) {
|
|
79
|
-
roles.push("admin");
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Check handler for role patterns
|
|
83
|
-
const handler = route.handler || "";
|
|
84
|
-
if (handler.includes("admin")) {
|
|
85
|
-
roles.push("admin");
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return roles.length > 0 ? roles : undefined;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function matchesPattern(path, pattern) {
|
|
92
|
-
// Simple pattern matching
|
|
93
|
-
const normPattern = pattern.replace(/\*/g, ".*").replace(/\//g, "\\/");
|
|
94
|
-
try {
|
|
95
|
-
const rx = new RegExp(`^${normPattern}`, "i");
|
|
96
|
-
return rx.test(path);
|
|
97
|
-
} catch {
|
|
98
|
-
return false;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Validate code against route contract
|
|
104
|
-
*/
|
|
105
|
-
function validateAgainstRouteContract(contract, clientRefs) {
|
|
106
|
-
const violations = [];
|
|
107
|
-
const contractPaths = new Map(contract.routes.map(r => [`${r.method}_${r.path}`, r]));
|
|
108
|
-
|
|
109
|
-
for (const ref of clientRefs) {
|
|
110
|
-
const key = `${ref.method}_${ref.path}`;
|
|
111
|
-
const wildcardKey = `*_${ref.path}`;
|
|
112
|
-
|
|
113
|
-
if (!contractPaths.has(key) && !contractPaths.has(wildcardKey)) {
|
|
114
|
-
// Check parameterized match
|
|
115
|
-
const match = findParameterizedMatch(contract.routes, ref.method, ref.path);
|
|
116
|
-
|
|
117
|
-
if (!match) {
|
|
118
|
-
violations.push({
|
|
119
|
-
type: "undeclared_route",
|
|
120
|
-
severity: "BLOCK",
|
|
121
|
-
route: { method: ref.method, path: ref.path },
|
|
122
|
-
source: ref.source,
|
|
123
|
-
message: `Route ${ref.method} ${ref.path} used in client but not declared in contract`,
|
|
124
|
-
evidence: ref.evidence || []
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return violations;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function findParameterizedMatch(routes, method, path) {
|
|
134
|
-
for (const r of routes) {
|
|
135
|
-
if (r.method !== "*" && r.method !== method) continue;
|
|
136
|
-
if (matchesParameterized(r.path, path)) return r;
|
|
137
|
-
}
|
|
138
|
-
return null;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function matchesParameterized(pattern, actual) {
|
|
142
|
-
const patternParts = pattern.split("/").filter(Boolean);
|
|
143
|
-
const actualParts = actual.split("/").filter(Boolean);
|
|
144
|
-
|
|
145
|
-
if (patternParts.length !== actualParts.length) return false;
|
|
146
|
-
|
|
147
|
-
for (let i = 0; i < patternParts.length; i++) {
|
|
148
|
-
const p = patternParts[i];
|
|
149
|
-
if (p.startsWith(":") || p.startsWith("*")) continue;
|
|
150
|
-
if (p !== actualParts[i]) return false;
|
|
151
|
-
}
|
|
152
|
-
return true;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Diff two route contracts
|
|
157
|
-
*/
|
|
158
|
-
function diffRouteContracts(before, after) {
|
|
159
|
-
const diff = {
|
|
160
|
-
added: [],
|
|
161
|
-
removed: [],
|
|
162
|
-
changed: []
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
const beforeMap = new Map(before.routes.map(r => [r.id, r]));
|
|
166
|
-
const afterMap = new Map(after.routes.map(r => [r.id, r]));
|
|
167
|
-
|
|
168
|
-
for (const [id, route] of afterMap) {
|
|
169
|
-
if (!beforeMap.has(id)) {
|
|
170
|
-
diff.added.push(route);
|
|
171
|
-
} else {
|
|
172
|
-
const prev = beforeMap.get(id);
|
|
173
|
-
if (prev.auth !== route.auth || JSON.stringify(prev.roles) !== JSON.stringify(route.roles)) {
|
|
174
|
-
diff.changed.push({ before: prev, after: route });
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
for (const [id, route] of beforeMap) {
|
|
180
|
-
if (!afterMap.has(id)) {
|
|
181
|
-
diff.removed.push(route);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
return diff;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
module.exports = {
|
|
189
|
-
buildRouteContract,
|
|
190
|
-
validateAgainstRouteContract,
|
|
191
|
-
diffRouteContracts
|
|
192
|
-
};
|
|
@@ -1,89 +0,0 @@
|
|
|
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 +0,0 @@
|
|
|
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,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Doctor Service — Enterprise Environment Diagnostics
|
|
3
|
-
*
|
|
4
|
-
* Main entry point for the Doctor service
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const { DoctorService, diagnose } = require('./service');
|
|
8
|
-
const { SEVERITY, CATEGORY, FIX_TYPE, SEVERITY_WEIGHT, CATEGORY_META } = require('./types');
|
|
9
|
-
const modules = require('./modules');
|
|
10
|
-
const reporter = require('./reporter');
|
|
11
|
-
const autofix = require('./autofix');
|
|
12
|
-
const ui = require('./ui');
|
|
13
|
-
|
|
14
|
-
module.exports = {
|
|
15
|
-
// Main service
|
|
16
|
-
DoctorService,
|
|
17
|
-
diagnose,
|
|
18
|
-
|
|
19
|
-
// Types and constants
|
|
20
|
-
SEVERITY,
|
|
21
|
-
CATEGORY,
|
|
22
|
-
FIX_TYPE,
|
|
23
|
-
SEVERITY_WEIGHT,
|
|
24
|
-
CATEGORY_META,
|
|
25
|
-
|
|
26
|
-
// Modules
|
|
27
|
-
modules,
|
|
28
|
-
|
|
29
|
-
// Reporter
|
|
30
|
-
reporter,
|
|
31
|
-
|
|
32
|
-
// Auto-fix
|
|
33
|
-
autofix,
|
|
34
|
-
|
|
35
|
-
// UI utilities
|
|
36
|
-
ui,
|
|
37
|
-
};
|