@vibecheckai/cli 3.7.0 → 3.8.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/README.md +135 -63
- package/bin/_deprecations.js +447 -19
- package/bin/_router.js +1 -1
- package/bin/registry.js +347 -280
- package/bin/runners/context/generators/cursor-enhanced.js +2439 -0
- package/bin/runners/lib/agent-firewall/enforcement/gateway.js +1059 -0
- package/bin/runners/lib/agent-firewall/enforcement/index.js +98 -0
- package/bin/runners/lib/agent-firewall/enforcement/mode.js +318 -0
- package/bin/runners/lib/agent-firewall/enforcement/orchestrator.js +484 -0
- package/bin/runners/lib/agent-firewall/enforcement/proof-artifact.js +418 -0
- package/bin/runners/lib/agent-firewall/enforcement/schemas/change-event.schema.json +173 -0
- package/bin/runners/lib/agent-firewall/enforcement/schemas/intent.schema.json +181 -0
- package/bin/runners/lib/agent-firewall/enforcement/schemas/verdict.schema.json +222 -0
- package/bin/runners/lib/agent-firewall/enforcement/verdict-v2.js +333 -0
- package/bin/runners/lib/agent-firewall/index.js +200 -0
- package/bin/runners/lib/agent-firewall/integration/index.js +20 -0
- package/bin/runners/lib/agent-firewall/integration/ship-gate.js +437 -0
- package/bin/runners/lib/agent-firewall/intent/alignment-engine.js +622 -0
- package/bin/runners/lib/agent-firewall/intent/auto-detect.js +426 -0
- package/bin/runners/lib/agent-firewall/intent/index.js +102 -0
- package/bin/runners/lib/agent-firewall/intent/schema.js +352 -0
- package/bin/runners/lib/agent-firewall/intent/store.js +283 -0
- package/bin/runners/lib/agent-firewall/interception/fs-interceptor.js +502 -0
- package/bin/runners/lib/agent-firewall/interception/index.js +23 -0
- package/bin/runners/lib/agent-firewall/session/collector.js +451 -0
- package/bin/runners/lib/agent-firewall/session/index.js +26 -0
- package/bin/runners/lib/artifact-envelope.js +540 -0
- package/bin/runners/lib/auth-shared.js +977 -0
- package/bin/runners/lib/checkpoint.js +941 -0
- package/bin/runners/lib/cleanup/engine.js +571 -0
- package/bin/runners/lib/cleanup/index.js +53 -0
- package/bin/runners/lib/cleanup/output.js +375 -0
- package/bin/runners/lib/cleanup/rules.js +1060 -0
- package/bin/runners/lib/doctor/diagnosis-receipt.js +454 -0
- package/bin/runners/lib/doctor/failure-signatures.js +526 -0
- package/bin/runners/lib/doctor/fix-script.js +336 -0
- package/bin/runners/lib/doctor/modules/build-tools.js +453 -0
- package/bin/runners/lib/doctor/modules/index.js +62 -3
- package/bin/runners/lib/doctor/modules/os-quirks.js +706 -0
- package/bin/runners/lib/doctor/modules/repo-integrity.js +485 -0
- package/bin/runners/lib/doctor/safe-repair.js +384 -0
- package/bin/runners/lib/engines/attack-detector.js +1192 -0
- package/bin/runners/lib/entitlements-v2.js +2 -2
- package/bin/runners/lib/missions/briefing.js +427 -0
- package/bin/runners/lib/missions/checkpoint.js +753 -0
- package/bin/runners/lib/missions/hardening.js +851 -0
- package/bin/runners/lib/missions/plan.js +421 -32
- package/bin/runners/lib/missions/safety-gates.js +645 -0
- package/bin/runners/lib/missions/schema.js +478 -0
- package/bin/runners/lib/packs/bundle.js +675 -0
- package/bin/runners/lib/packs/evidence-pack.js +671 -0
- package/bin/runners/lib/packs/pack-factory.js +837 -0
- package/bin/runners/lib/packs/permissions-pack.js +686 -0
- package/bin/runners/lib/packs/proof-graph-pack.js +779 -0
- package/bin/runners/lib/safelist/index.js +96 -0
- package/bin/runners/lib/safelist/integration.js +334 -0
- package/bin/runners/lib/safelist/matcher.js +696 -0
- package/bin/runners/lib/safelist/schema.js +948 -0
- package/bin/runners/lib/safelist/store.js +438 -0
- package/bin/runners/lib/schemas/ship-manifest.schema.json +251 -0
- package/bin/runners/lib/ship-gate.js +832 -0
- package/bin/runners/lib/ship-manifest.js +1153 -0
- package/bin/runners/lib/ship-output.js +1 -1
- package/bin/runners/lib/unified-cli-output.js +710 -383
- package/bin/runners/lib/upsell.js +3 -3
- package/bin/runners/lib/why-tree.js +650 -0
- package/bin/runners/runAllowlist.js +33 -4
- package/bin/runners/runApprove.js +240 -1122
- package/bin/runners/runAudit.js +692 -0
- package/bin/runners/runAuth.js +325 -29
- package/bin/runners/runCheckpoint.js +442 -494
- package/bin/runners/runCleanup.js +343 -0
- package/bin/runners/runDoctor.js +269 -19
- package/bin/runners/runFix.js +411 -32
- package/bin/runners/runForge.js +411 -0
- package/bin/runners/runIntent.js +906 -0
- package/bin/runners/runKickoff.js +878 -0
- package/bin/runners/runLaunch.js +2000 -0
- package/bin/runners/runLink.js +785 -0
- package/bin/runners/runMcp.js +1741 -837
- package/bin/runners/runPacks.js +2089 -0
- package/bin/runners/runPolish.js +41 -0
- package/bin/runners/runSafelist.js +1190 -0
- package/bin/runners/runScan.js +21 -9
- package/bin/runners/runShield.js +1282 -0
- package/bin/runners/runShip.js +395 -16
- package/bin/vibecheck.js +34 -6
- package/mcp-server/README.md +117 -158
- package/mcp-server/handlers/tool-handler.ts +3 -3
- package/mcp-server/index.js +16 -0
- package/mcp-server/intent-firewall-interceptor.js +529 -0
- package/mcp-server/manifest.json +473 -0
- package/mcp-server/package.json +1 -1
- package/mcp-server/registry/tool-registry.js +315 -523
- package/mcp-server/registry/tools.json +442 -428
- package/mcp-server/tier-auth.js +68 -11
- package/mcp-server/tools-v3.js +70 -16
- package/package.json +1 -1
- package/bin/runners/runProof.zip +0 -0
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build Tools Diagnostics Module
|
|
3
|
+
*
|
|
4
|
+
* Checks tsconfig, ESLint, Prettier, Jest, Vitest, and build configuration
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const { execSync } = require('child_process');
|
|
10
|
+
const { SEVERITY, CATEGORY, FIX_TYPE } = require('../types');
|
|
11
|
+
|
|
12
|
+
const MODULE_ID = 'build';
|
|
13
|
+
|
|
14
|
+
function createDiagnostics(projectPath) {
|
|
15
|
+
return [
|
|
16
|
+
{
|
|
17
|
+
id: `${MODULE_ID}.tsconfig`,
|
|
18
|
+
name: 'TypeScript Config',
|
|
19
|
+
category: CATEGORY.PROJECT,
|
|
20
|
+
parallel: true,
|
|
21
|
+
check: async () => {
|
|
22
|
+
const tsconfigPath = path.join(projectPath, 'tsconfig.json');
|
|
23
|
+
|
|
24
|
+
if (!fs.existsSync(tsconfigPath)) {
|
|
25
|
+
// Check for jsconfig.json as fallback
|
|
26
|
+
const jsconfigPath = path.join(projectPath, 'jsconfig.json');
|
|
27
|
+
if (fs.existsSync(jsconfigPath)) {
|
|
28
|
+
return {
|
|
29
|
+
severity: SEVERITY.PASS,
|
|
30
|
+
message: 'jsconfig.json found (JavaScript project)',
|
|
31
|
+
metadata: { type: 'javascript', path: jsconfigPath },
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
severity: SEVERITY.INFO,
|
|
37
|
+
message: 'Not found (JavaScript project)',
|
|
38
|
+
metadata: { type: 'javascript' },
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
const content = fs.readFileSync(tsconfigPath, 'utf8');
|
|
44
|
+
const config = JSON.parse(content);
|
|
45
|
+
|
|
46
|
+
const issues = [];
|
|
47
|
+
const metadata = { path: tsconfigPath, config: {} };
|
|
48
|
+
|
|
49
|
+
// Check for common misconfigurations
|
|
50
|
+
const compilerOptions = config.compilerOptions || {};
|
|
51
|
+
metadata.config = compilerOptions;
|
|
52
|
+
|
|
53
|
+
// Check strict mode
|
|
54
|
+
if (!compilerOptions.strict) {
|
|
55
|
+
issues.push('strict mode disabled');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Check module resolution
|
|
59
|
+
if (compilerOptions.moduleResolution === 'node' && compilerOptions.module?.toLowerCase().includes('esnext')) {
|
|
60
|
+
issues.push('moduleResolution should be "bundler" or "nodenext" for ESM');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Check for skipLibCheck (acceptable but worth noting)
|
|
64
|
+
if (compilerOptions.skipLibCheck === false) {
|
|
65
|
+
metadata.slowBuild = true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Check target
|
|
69
|
+
const target = (compilerOptions.target || 'es5').toLowerCase();
|
|
70
|
+
if (['es5', 'es6', 'es2015', 'es2016'].includes(target)) {
|
|
71
|
+
issues.push(`target "${compilerOptions.target}" is outdated, recommend ES2020+`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (issues.length > 0) {
|
|
75
|
+
return {
|
|
76
|
+
severity: SEVERITY.WARNING,
|
|
77
|
+
message: issues.length === 1 ? issues[0] : `${issues.length} issues found`,
|
|
78
|
+
detail: issues.join('; '),
|
|
79
|
+
metadata,
|
|
80
|
+
fixes: [{
|
|
81
|
+
type: FIX_TYPE.LINK,
|
|
82
|
+
description: 'TypeScript configuration reference',
|
|
83
|
+
url: 'https://www.typescriptlang.org/tsconfig',
|
|
84
|
+
}],
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
severity: SEVERITY.PASS,
|
|
90
|
+
message: `Valid (target: ${compilerOptions.target || 'default'})`,
|
|
91
|
+
metadata,
|
|
92
|
+
};
|
|
93
|
+
} catch (err) {
|
|
94
|
+
return {
|
|
95
|
+
severity: SEVERITY.ERROR,
|
|
96
|
+
message: 'Invalid tsconfig.json',
|
|
97
|
+
detail: err.message,
|
|
98
|
+
fixes: [{
|
|
99
|
+
type: FIX_TYPE.MANUAL,
|
|
100
|
+
description: 'Fix JSON syntax errors in tsconfig.json',
|
|
101
|
+
autoFixable: false,
|
|
102
|
+
}],
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
id: `${MODULE_ID}.eslint`,
|
|
109
|
+
name: 'ESLint Configuration',
|
|
110
|
+
category: CATEGORY.PROJECT,
|
|
111
|
+
parallel: true,
|
|
112
|
+
check: async () => {
|
|
113
|
+
const eslintConfigs = [
|
|
114
|
+
'eslint.config.js',
|
|
115
|
+
'eslint.config.mjs',
|
|
116
|
+
'eslint.config.cjs',
|
|
117
|
+
'.eslintrc.js',
|
|
118
|
+
'.eslintrc.cjs',
|
|
119
|
+
'.eslintrc.json',
|
|
120
|
+
'.eslintrc.yml',
|
|
121
|
+
'.eslintrc.yaml',
|
|
122
|
+
'.eslintrc',
|
|
123
|
+
];
|
|
124
|
+
|
|
125
|
+
let foundConfig = null;
|
|
126
|
+
for (const configFile of eslintConfigs) {
|
|
127
|
+
const configPath = path.join(projectPath, configFile);
|
|
128
|
+
if (fs.existsSync(configPath)) {
|
|
129
|
+
foundConfig = configFile;
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
// Also check config/ directory
|
|
133
|
+
const configDirPath = path.join(projectPath, 'config', configFile);
|
|
134
|
+
if (fs.existsSync(configDirPath)) {
|
|
135
|
+
foundConfig = `config/${configFile}`;
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Check package.json for eslintConfig
|
|
141
|
+
const pkgPath = path.join(projectPath, 'package.json');
|
|
142
|
+
let pkgHasEslint = false;
|
|
143
|
+
if (fs.existsSync(pkgPath)) {
|
|
144
|
+
try {
|
|
145
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
146
|
+
if (pkg.eslintConfig) {
|
|
147
|
+
pkgHasEslint = true;
|
|
148
|
+
}
|
|
149
|
+
} catch {}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (!foundConfig && !pkgHasEslint) {
|
|
153
|
+
return {
|
|
154
|
+
severity: SEVERITY.INFO,
|
|
155
|
+
message: 'No ESLint config found',
|
|
156
|
+
fixes: [{
|
|
157
|
+
type: FIX_TYPE.COMMAND,
|
|
158
|
+
description: 'Initialize ESLint',
|
|
159
|
+
command: 'npm init @eslint/config',
|
|
160
|
+
autoFixable: false,
|
|
161
|
+
}],
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const configType = foundConfig?.startsWith('eslint.config') ? 'flat config' : 'legacy config';
|
|
166
|
+
const metadata = { config: foundConfig || 'package.json', type: configType };
|
|
167
|
+
|
|
168
|
+
// Check if eslint is installed
|
|
169
|
+
try {
|
|
170
|
+
execSync('npx eslint --version', {
|
|
171
|
+
cwd: projectPath,
|
|
172
|
+
encoding: 'utf8',
|
|
173
|
+
timeout: 10000,
|
|
174
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
return {
|
|
178
|
+
severity: SEVERITY.PASS,
|
|
179
|
+
message: `${foundConfig || 'package.json'} (${configType})`,
|
|
180
|
+
metadata,
|
|
181
|
+
};
|
|
182
|
+
} catch {
|
|
183
|
+
return {
|
|
184
|
+
severity: SEVERITY.WARNING,
|
|
185
|
+
message: `Config found but ESLint not installed`,
|
|
186
|
+
metadata,
|
|
187
|
+
fixes: [{
|
|
188
|
+
type: FIX_TYPE.COMMAND,
|
|
189
|
+
description: 'Install ESLint',
|
|
190
|
+
command: 'npm install -D eslint',
|
|
191
|
+
autoFixable: true,
|
|
192
|
+
}],
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
id: `${MODULE_ID}.prettier`,
|
|
199
|
+
name: 'Prettier Configuration',
|
|
200
|
+
category: CATEGORY.PROJECT,
|
|
201
|
+
parallel: true,
|
|
202
|
+
check: async () => {
|
|
203
|
+
const prettierConfigs = [
|
|
204
|
+
'.prettierrc',
|
|
205
|
+
'.prettierrc.json',
|
|
206
|
+
'.prettierrc.yml',
|
|
207
|
+
'.prettierrc.yaml',
|
|
208
|
+
'.prettierrc.js',
|
|
209
|
+
'.prettierrc.cjs',
|
|
210
|
+
'.prettierrc.mjs',
|
|
211
|
+
'prettier.config.js',
|
|
212
|
+
'prettier.config.cjs',
|
|
213
|
+
'prettier.config.mjs',
|
|
214
|
+
];
|
|
215
|
+
|
|
216
|
+
let foundConfig = null;
|
|
217
|
+
for (const configFile of prettierConfigs) {
|
|
218
|
+
if (fs.existsSync(path.join(projectPath, configFile))) {
|
|
219
|
+
foundConfig = configFile;
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Check package.json
|
|
225
|
+
const pkgPath = path.join(projectPath, 'package.json');
|
|
226
|
+
let pkgHasPrettier = false;
|
|
227
|
+
if (fs.existsSync(pkgPath)) {
|
|
228
|
+
try {
|
|
229
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
230
|
+
if (pkg.prettier) {
|
|
231
|
+
pkgHasPrettier = true;
|
|
232
|
+
}
|
|
233
|
+
} catch {}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (!foundConfig && !pkgHasPrettier) {
|
|
237
|
+
return {
|
|
238
|
+
severity: SEVERITY.INFO,
|
|
239
|
+
message: 'No Prettier config found',
|
|
240
|
+
fixes: [{
|
|
241
|
+
type: FIX_TYPE.FILE_CREATE,
|
|
242
|
+
description: 'Create default Prettier config',
|
|
243
|
+
path: '.prettierrc',
|
|
244
|
+
content: JSON.stringify({ semi: true, singleQuote: true, tabWidth: 2 }, null, 2),
|
|
245
|
+
autoFixable: true,
|
|
246
|
+
}],
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
severity: SEVERITY.PASS,
|
|
252
|
+
message: foundConfig || 'package.json',
|
|
253
|
+
metadata: { config: foundConfig || 'package.json' },
|
|
254
|
+
};
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
id: `${MODULE_ID}.test_framework`,
|
|
259
|
+
name: 'Test Framework',
|
|
260
|
+
category: CATEGORY.PROJECT,
|
|
261
|
+
parallel: true,
|
|
262
|
+
check: async () => {
|
|
263
|
+
const pkgPath = path.join(projectPath, 'package.json');
|
|
264
|
+
if (!fs.existsSync(pkgPath)) {
|
|
265
|
+
return {
|
|
266
|
+
severity: SEVERITY.INFO,
|
|
267
|
+
message: 'No package.json',
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
try {
|
|
272
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
273
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
274
|
+
|
|
275
|
+
const frameworks = [];
|
|
276
|
+
|
|
277
|
+
if (deps.vitest) frameworks.push({ name: 'Vitest', version: deps.vitest });
|
|
278
|
+
if (deps.jest) frameworks.push({ name: 'Jest', version: deps.jest });
|
|
279
|
+
if (deps.mocha) frameworks.push({ name: 'Mocha', version: deps.mocha });
|
|
280
|
+
if (deps.ava) frameworks.push({ name: 'AVA', version: deps.ava });
|
|
281
|
+
if (deps.playwright || deps['@playwright/test']) {
|
|
282
|
+
frameworks.push({ name: 'Playwright', version: deps.playwright || deps['@playwright/test'] });
|
|
283
|
+
}
|
|
284
|
+
if (deps.cypress) frameworks.push({ name: 'Cypress', version: deps.cypress });
|
|
285
|
+
|
|
286
|
+
if (frameworks.length === 0) {
|
|
287
|
+
return {
|
|
288
|
+
severity: SEVERITY.INFO,
|
|
289
|
+
message: 'No test framework detected',
|
|
290
|
+
fixes: [{
|
|
291
|
+
type: FIX_TYPE.COMMAND,
|
|
292
|
+
description: 'Install Vitest (recommended)',
|
|
293
|
+
command: 'npm install -D vitest',
|
|
294
|
+
autoFixable: false,
|
|
295
|
+
}],
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Check for test config files
|
|
300
|
+
const configFiles = [];
|
|
301
|
+
const testConfigs = [
|
|
302
|
+
'vitest.config.ts', 'vitest.config.js',
|
|
303
|
+
'jest.config.ts', 'jest.config.js', 'jest.config.json',
|
|
304
|
+
'playwright.config.ts', 'playwright.config.js',
|
|
305
|
+
];
|
|
306
|
+
|
|
307
|
+
for (const config of testConfigs) {
|
|
308
|
+
if (fs.existsSync(path.join(projectPath, config))) {
|
|
309
|
+
configFiles.push(config);
|
|
310
|
+
}
|
|
311
|
+
if (fs.existsSync(path.join(projectPath, 'config', config))) {
|
|
312
|
+
configFiles.push(`config/${config}`);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const primary = frameworks[0];
|
|
317
|
+
return {
|
|
318
|
+
severity: SEVERITY.PASS,
|
|
319
|
+
message: `${primary.name}${frameworks.length > 1 ? ` (+${frameworks.length - 1} more)` : ''}`,
|
|
320
|
+
metadata: { frameworks, configFiles },
|
|
321
|
+
};
|
|
322
|
+
} catch (err) {
|
|
323
|
+
return {
|
|
324
|
+
severity: SEVERITY.WARNING,
|
|
325
|
+
message: 'Could not parse package.json',
|
|
326
|
+
detail: err.message,
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
id: `${MODULE_ID}.bundler`,
|
|
333
|
+
name: 'Build/Bundler',
|
|
334
|
+
category: CATEGORY.PROJECT,
|
|
335
|
+
parallel: true,
|
|
336
|
+
check: async () => {
|
|
337
|
+
const pkgPath = path.join(projectPath, 'package.json');
|
|
338
|
+
if (!fs.existsSync(pkgPath)) {
|
|
339
|
+
return { severity: SEVERITY.INFO, message: 'No package.json' };
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
try {
|
|
343
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
344
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
345
|
+
|
|
346
|
+
const bundlers = [];
|
|
347
|
+
|
|
348
|
+
// Frameworks (which include bundlers)
|
|
349
|
+
if (deps.next) bundlers.push({ name: 'Next.js', version: deps.next, type: 'framework' });
|
|
350
|
+
if (deps.nuxt) bundlers.push({ name: 'Nuxt', version: deps.nuxt, type: 'framework' });
|
|
351
|
+
if (deps['@remix-run/node'] || deps.remix) bundlers.push({ name: 'Remix', type: 'framework' });
|
|
352
|
+
if (deps.astro) bundlers.push({ name: 'Astro', version: deps.astro, type: 'framework' });
|
|
353
|
+
|
|
354
|
+
// Bundlers
|
|
355
|
+
if (deps.vite) bundlers.push({ name: 'Vite', version: deps.vite, type: 'bundler' });
|
|
356
|
+
if (deps.webpack) bundlers.push({ name: 'Webpack', version: deps.webpack, type: 'bundler' });
|
|
357
|
+
if (deps.esbuild) bundlers.push({ name: 'esbuild', version: deps.esbuild, type: 'bundler' });
|
|
358
|
+
if (deps.rollup) bundlers.push({ name: 'Rollup', version: deps.rollup, type: 'bundler' });
|
|
359
|
+
if (deps.parcel) bundlers.push({ name: 'Parcel', version: deps.parcel, type: 'bundler' });
|
|
360
|
+
if (deps.turbo || deps.turbopack) bundlers.push({ name: 'Turbopack', type: 'bundler' });
|
|
361
|
+
if (deps.tsup) bundlers.push({ name: 'tsup', version: deps.tsup, type: 'bundler' });
|
|
362
|
+
|
|
363
|
+
if (bundlers.length === 0) {
|
|
364
|
+
return {
|
|
365
|
+
severity: SEVERITY.INFO,
|
|
366
|
+
message: 'No bundler detected (possibly Node.js project)',
|
|
367
|
+
metadata: { type: 'node' },
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const primary = bundlers[0];
|
|
372
|
+
return {
|
|
373
|
+
severity: SEVERITY.PASS,
|
|
374
|
+
message: `${primary.name}${primary.version ? ` ${primary.version}` : ''}`,
|
|
375
|
+
metadata: { bundlers, primary: primary.name },
|
|
376
|
+
};
|
|
377
|
+
} catch (err) {
|
|
378
|
+
return {
|
|
379
|
+
severity: SEVERITY.WARNING,
|
|
380
|
+
message: 'Could not detect bundler',
|
|
381
|
+
detail: err.message,
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
},
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
id: `${MODULE_ID}.typescript_installed`,
|
|
388
|
+
name: 'TypeScript Installation',
|
|
389
|
+
category: CATEGORY.PROJECT,
|
|
390
|
+
parallel: true,
|
|
391
|
+
dependsOn: [`${MODULE_ID}.tsconfig`],
|
|
392
|
+
check: async () => {
|
|
393
|
+
const tsconfigPath = path.join(projectPath, 'tsconfig.json');
|
|
394
|
+
if (!fs.existsSync(tsconfigPath)) {
|
|
395
|
+
return {
|
|
396
|
+
severity: SEVERITY.INFO,
|
|
397
|
+
message: 'Not applicable (no tsconfig.json)',
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Check if TypeScript is installed
|
|
402
|
+
try {
|
|
403
|
+
const version = execSync('npx tsc --version', {
|
|
404
|
+
cwd: projectPath,
|
|
405
|
+
encoding: 'utf8',
|
|
406
|
+
timeout: 15000,
|
|
407
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
408
|
+
}).trim();
|
|
409
|
+
|
|
410
|
+
const match = version.match(/Version\s+(\d+\.\d+\.\d+)/);
|
|
411
|
+
const versionNum = match ? match[1] : version;
|
|
412
|
+
const major = parseInt(versionNum.split('.')[0]);
|
|
413
|
+
|
|
414
|
+
const metadata = { version: versionNum, major };
|
|
415
|
+
|
|
416
|
+
if (major < 5) {
|
|
417
|
+
return {
|
|
418
|
+
severity: SEVERITY.WARNING,
|
|
419
|
+
message: `${versionNum} — recommend TypeScript 5+`,
|
|
420
|
+
metadata,
|
|
421
|
+
fixes: [{
|
|
422
|
+
type: FIX_TYPE.COMMAND,
|
|
423
|
+
description: 'Update TypeScript',
|
|
424
|
+
command: 'npm install -D typescript@latest',
|
|
425
|
+
autoFixable: true,
|
|
426
|
+
}],
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
return {
|
|
431
|
+
severity: SEVERITY.PASS,
|
|
432
|
+
message: versionNum,
|
|
433
|
+
metadata,
|
|
434
|
+
};
|
|
435
|
+
} catch (err) {
|
|
436
|
+
return {
|
|
437
|
+
severity: SEVERITY.ERROR,
|
|
438
|
+
message: 'TypeScript not installed',
|
|
439
|
+
detail: 'tsconfig.json exists but TypeScript is not installed',
|
|
440
|
+
fixes: [{
|
|
441
|
+
type: FIX_TYPE.COMMAND,
|
|
442
|
+
description: 'Install TypeScript',
|
|
443
|
+
command: 'npm install -D typescript',
|
|
444
|
+
autoFixable: true,
|
|
445
|
+
}],
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
},
|
|
449
|
+
},
|
|
450
|
+
];
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
module.exports = { MODULE_ID, createDiagnostics };
|
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Diagnostic Modules Index
|
|
3
3
|
*
|
|
4
|
-
* Exports all diagnostic modules for the Doctor service
|
|
4
|
+
* Exports all diagnostic modules for the Doctor service.
|
|
5
|
+
*
|
|
6
|
+
* Modules:
|
|
7
|
+
* - system: OS, memory, disk, CPU resources
|
|
8
|
+
* - runtime: Node.js, npm, package managers
|
|
9
|
+
* - project: package.json, dependencies, config files
|
|
10
|
+
* - dependencies: Outdated packages, vulnerabilities
|
|
11
|
+
* - security: Secrets, gitignore, env files
|
|
12
|
+
* - network: Internet, npm registry, API connectivity
|
|
13
|
+
* - vibecheck: Config, truth pack, scan status
|
|
14
|
+
* - buildTools: TypeScript, ESLint, Prettier, test frameworks
|
|
15
|
+
* - repoIntegrity: Git state, lock files, caches
|
|
16
|
+
* - osQuirks: Windows/macOS/Linux specific checks
|
|
5
17
|
*/
|
|
6
18
|
|
|
7
19
|
const system = require('./system');
|
|
@@ -11,11 +23,17 @@ const dependencies = require('./dependencies');
|
|
|
11
23
|
const security = require('./security');
|
|
12
24
|
const network = require('./network');
|
|
13
25
|
const vibecheck = require('./vibecheck');
|
|
26
|
+
const buildTools = require('./build-tools');
|
|
27
|
+
const repoIntegrity = require('./repo-integrity');
|
|
28
|
+
const osQuirks = require('./os-quirks');
|
|
14
29
|
|
|
15
30
|
const ALL_MODULES = [
|
|
16
31
|
system,
|
|
32
|
+
osQuirks, // OS-specific after system
|
|
17
33
|
runtime,
|
|
34
|
+
buildTools, // Build tools after runtime
|
|
18
35
|
project,
|
|
36
|
+
repoIntegrity, // Repo integrity with project
|
|
19
37
|
dependencies,
|
|
20
38
|
security,
|
|
21
39
|
network,
|
|
@@ -26,13 +44,49 @@ function getAllDiagnostics(projectPath) {
|
|
|
26
44
|
const diagnostics = [];
|
|
27
45
|
|
|
28
46
|
for (const mod of ALL_MODULES) {
|
|
29
|
-
|
|
30
|
-
|
|
47
|
+
try {
|
|
48
|
+
const moduleDiagnostics = mod.createDiagnostics(projectPath);
|
|
49
|
+
diagnostics.push(...moduleDiagnostics);
|
|
50
|
+
} catch (err) {
|
|
51
|
+
// If a module fails to load, log but continue
|
|
52
|
+
console.error(`Warning: Could not load diagnostics from ${mod.MODULE_ID || 'unknown'}: ${err.message}`);
|
|
53
|
+
}
|
|
31
54
|
}
|
|
32
55
|
|
|
33
56
|
return diagnostics;
|
|
34
57
|
}
|
|
35
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Get diagnostics for specific categories only
|
|
61
|
+
* @param {string} projectPath - Project path
|
|
62
|
+
* @param {string[]} categories - Categories to include
|
|
63
|
+
* @returns {object[]} Filtered diagnostics
|
|
64
|
+
*/
|
|
65
|
+
function getDiagnosticsByCategory(projectPath, categories) {
|
|
66
|
+
const all = getAllDiagnostics(projectPath);
|
|
67
|
+
return all.filter(d => categories.includes(d.category));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Get a count of diagnostics by module
|
|
72
|
+
* @param {string} projectPath - Project path
|
|
73
|
+
* @returns {object} Module -> count mapping
|
|
74
|
+
*/
|
|
75
|
+
function getDiagnosticCounts(projectPath) {
|
|
76
|
+
const counts = {};
|
|
77
|
+
|
|
78
|
+
for (const mod of ALL_MODULES) {
|
|
79
|
+
try {
|
|
80
|
+
const diagnostics = mod.createDiagnostics(projectPath);
|
|
81
|
+
counts[mod.MODULE_ID] = diagnostics.length;
|
|
82
|
+
} catch {
|
|
83
|
+
counts[mod.MODULE_ID] = 0;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return counts;
|
|
88
|
+
}
|
|
89
|
+
|
|
36
90
|
module.exports = {
|
|
37
91
|
system,
|
|
38
92
|
runtime,
|
|
@@ -41,6 +95,11 @@ module.exports = {
|
|
|
41
95
|
security,
|
|
42
96
|
network,
|
|
43
97
|
vibecheck,
|
|
98
|
+
buildTools,
|
|
99
|
+
repoIntegrity,
|
|
100
|
+
osQuirks,
|
|
44
101
|
ALL_MODULES,
|
|
45
102
|
getAllDiagnostics,
|
|
103
|
+
getDiagnosticsByCategory,
|
|
104
|
+
getDiagnosticCounts,
|
|
46
105
|
};
|