@velt-js/mcp-installer 0.1.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 +373 -0
- package/bin/mcp-server.js +22 -0
- package/package.json +42 -0
- package/src/index.js +754 -0
- package/src/tools/orchestrator.js +299 -0
- package/src/tools/unified-installer.js +886 -0
- package/src/utils/cli.js +380 -0
- package/src/utils/comment-detector.js +305 -0
- package/src/utils/config.js +149 -0
- package/src/utils/framework-detection.js +262 -0
- package/src/utils/header-positioning.js +146 -0
- package/src/utils/host-app-discovery.js +1000 -0
- package/src/utils/integration.js +803 -0
- package/src/utils/plan-formatter.js +1698 -0
- package/src/utils/screenshot.js +151 -0
- package/src/utils/use-client.js +366 -0
- package/src/utils/validation.js +556 -0
- package/src/utils/velt-docs-fetcher.js +288 -0
- package/src/utils/velt-docs-urls.js +140 -0
- package/src/utils/velt-mcp-client.js +202 -0
- package/src/utils/velt-mcp.js +718 -0
|
@@ -0,0 +1,556 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation Utilities
|
|
3
|
+
*
|
|
4
|
+
* Validates Velt installation with basic and full integration checks.
|
|
5
|
+
* Includes CLI method verification and "use client" directive checks.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { getCliResolutionInfo } from './cli.js';
|
|
11
|
+
import { isNextJsProject, detectProjectType, ProjectType } from './framework-detection.js';
|
|
12
|
+
import { validateUseClientDirectives, scanAndFixUseClient } from './use-client.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Validates that directory is a valid React project (Next.js or plain React)
|
|
16
|
+
*
|
|
17
|
+
* @param {string} projectPath - Path to project directory
|
|
18
|
+
* @param {Object} [options] - Validation options
|
|
19
|
+
* @param {boolean} [options.requireNextJs=false] - If true, only accepts Next.js projects
|
|
20
|
+
* @returns {Object} Validation result { valid: boolean, error?: string, projectType?: string }
|
|
21
|
+
*/
|
|
22
|
+
export function validateProject(projectPath, options = {}) {
|
|
23
|
+
const { requireNextJs = false } = options;
|
|
24
|
+
const packageJsonPath = path.join(projectPath, 'package.json');
|
|
25
|
+
|
|
26
|
+
// Check package.json exists
|
|
27
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
28
|
+
return {
|
|
29
|
+
valid: false,
|
|
30
|
+
error: `No package.json found at ${projectPath}. Is this a Node.js project?`,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Parse package.json
|
|
35
|
+
let packageJson;
|
|
36
|
+
try {
|
|
37
|
+
packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
38
|
+
} catch (err) {
|
|
39
|
+
return {
|
|
40
|
+
valid: false,
|
|
41
|
+
error: `Failed to parse package.json: ${err.message}`,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const allDeps = {
|
|
46
|
+
...packageJson.dependencies,
|
|
47
|
+
...packageJson.devDependencies,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Check for React (required for all)
|
|
51
|
+
const hasReact = !!allDeps.react;
|
|
52
|
+
if (!hasReact) {
|
|
53
|
+
return {
|
|
54
|
+
valid: false,
|
|
55
|
+
error: '"react" not found in package.json dependencies. Velt requires a React project.',
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Detect project type
|
|
60
|
+
const hasNext = !!allDeps.next;
|
|
61
|
+
const hasVite = !!allDeps.vite;
|
|
62
|
+
const hasReactScripts = !!allDeps['react-scripts'];
|
|
63
|
+
|
|
64
|
+
let projectType;
|
|
65
|
+
if (hasNext) {
|
|
66
|
+
projectType = 'nextjs';
|
|
67
|
+
} else if (hasVite) {
|
|
68
|
+
projectType = 'vite-react';
|
|
69
|
+
} else if (hasReactScripts) {
|
|
70
|
+
projectType = 'create-react-app';
|
|
71
|
+
} else {
|
|
72
|
+
projectType = 'react-unknown';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// If Next.js is required but not found
|
|
76
|
+
if (requireNextJs && !hasNext) {
|
|
77
|
+
return {
|
|
78
|
+
valid: false,
|
|
79
|
+
error: `"next" not found in package.json dependencies. This feature requires a Next.js project. Detected project type: ${projectType}`,
|
|
80
|
+
projectType,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
valid: true,
|
|
86
|
+
projectType,
|
|
87
|
+
isNextJs: hasNext,
|
|
88
|
+
framework: projectType,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Validates that directory is a valid Next.js project
|
|
94
|
+
* @deprecated Use validateProject() instead for broader framework support
|
|
95
|
+
*
|
|
96
|
+
* @param {string} projectPath - Path to project directory
|
|
97
|
+
* @returns {Object} Validation result { valid: boolean, error?: string }
|
|
98
|
+
*/
|
|
99
|
+
export function validateNextJsProject(projectPath) {
|
|
100
|
+
// For backward compatibility, delegate to validateProject with requireNextJs=false
|
|
101
|
+
// This allows React projects to pass, but validates that it's at least a React project
|
|
102
|
+
const result = validateProject(projectPath, { requireNextJs: false });
|
|
103
|
+
|
|
104
|
+
// Add warning for non-Next.js projects
|
|
105
|
+
if (result.valid && !result.isNextJs) {
|
|
106
|
+
console.error(` ⚠️ Non-Next.js project detected: ${result.projectType}`);
|
|
107
|
+
console.error(` ℹ️ Some features (like "use client" directives) are Next.js-specific`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Validates CLI resolution and execution method
|
|
115
|
+
*
|
|
116
|
+
* @returns {Object} CLI validation result
|
|
117
|
+
*/
|
|
118
|
+
export function validateCliResolution() {
|
|
119
|
+
const resolution = getCliResolutionInfo();
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
name: 'CLI Resolution',
|
|
123
|
+
status: resolution.method === 'error' ? 'fail' : 'pass',
|
|
124
|
+
method: resolution.method,
|
|
125
|
+
message: resolution.method === 'error'
|
|
126
|
+
? `CLI not found: ${resolution.error}`
|
|
127
|
+
: `Using npx @velt-js/add-velt`,
|
|
128
|
+
path: resolution.path,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Basic CLI installation validation (for SKIP/CLI-only path)
|
|
134
|
+
*
|
|
135
|
+
* Only checks CLI scaffolding was successful, NOT integration.
|
|
136
|
+
* Checks: CLI files exist, @veltdev/react in package.json, .env.local
|
|
137
|
+
* Does NOT check: VeltProvider placement, VeltComments integration
|
|
138
|
+
*
|
|
139
|
+
* @param {Object} params
|
|
140
|
+
* @param {string} params.projectPath - Project path
|
|
141
|
+
* @param {Object} [params.cliResult] - CLI execution result (optional, for method reporting)
|
|
142
|
+
* @returns {Promise<Object>} Validation result
|
|
143
|
+
*/
|
|
144
|
+
export async function validateBasicCliInstall({ projectPath, cliResult = null }) {
|
|
145
|
+
const checks = [];
|
|
146
|
+
let passed = 0;
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
// Check 0: CLI Resolution/Execution Method (new)
|
|
150
|
+
if (cliResult && cliResult.method) {
|
|
151
|
+
checks.push({
|
|
152
|
+
name: 'CLI Execution Method',
|
|
153
|
+
status: cliResult.method === 'error' ? 'fail' : 'pass',
|
|
154
|
+
message: cliResult.method === 'npx'
|
|
155
|
+
? 'Used npx @velt-js/add-velt'
|
|
156
|
+
: `CLI method: ${cliResult.method}`,
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
if (cliResult.method !== 'error') passed++;
|
|
160
|
+
} else {
|
|
161
|
+
// Validate CLI resolution if no result provided
|
|
162
|
+
const cliCheck = validateCliResolution();
|
|
163
|
+
checks.push({
|
|
164
|
+
name: cliCheck.name,
|
|
165
|
+
status: cliCheck.status,
|
|
166
|
+
message: cliCheck.message,
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
if (cliCheck.status === 'pass') passed++;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Check 1: @veltdev/react in package.json
|
|
173
|
+
const packageJsonPath = path.join(projectPath, 'package.json');
|
|
174
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
175
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
176
|
+
const hasVeltPkg = !!(
|
|
177
|
+
packageJson.dependencies?.['@veltdev/react'] ||
|
|
178
|
+
packageJson.devDependencies?.['@veltdev/react']
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
checks.push({
|
|
182
|
+
name: '@veltdev/react package',
|
|
183
|
+
status: hasVeltPkg ? 'pass' : 'fail',
|
|
184
|
+
message: hasVeltPkg
|
|
185
|
+
? 'Package found in package.json'
|
|
186
|
+
: 'Package not found - run: npm install @veltdev/react',
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
if (hasVeltPkg) passed++;
|
|
190
|
+
} else {
|
|
191
|
+
checks.push({
|
|
192
|
+
name: '@veltdev/react package',
|
|
193
|
+
status: 'fail',
|
|
194
|
+
message: 'package.json not found',
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Check 2-4: CLI scaffold files exist
|
|
199
|
+
// Check both standard and src/ paths
|
|
200
|
+
const scaffoldFiles = [
|
|
201
|
+
{
|
|
202
|
+
name: 'VeltInitializeUser.tsx',
|
|
203
|
+
paths: [
|
|
204
|
+
'components/velt/VeltInitializeUser.tsx',
|
|
205
|
+
'src/components/velt/VeltInitializeUser.tsx',
|
|
206
|
+
],
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
name: 'VeltInitializeDocument.tsx',
|
|
210
|
+
paths: [
|
|
211
|
+
'components/velt/VeltInitializeDocument.tsx',
|
|
212
|
+
'src/components/velt/VeltInitializeDocument.tsx',
|
|
213
|
+
],
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
name: 'VeltCollaboration.tsx',
|
|
217
|
+
paths: [
|
|
218
|
+
'components/velt/VeltCollaboration.tsx',
|
|
219
|
+
'src/components/velt/VeltCollaboration.tsx',
|
|
220
|
+
],
|
|
221
|
+
},
|
|
222
|
+
];
|
|
223
|
+
|
|
224
|
+
for (const file of scaffoldFiles) {
|
|
225
|
+
let exists = false;
|
|
226
|
+
let foundPath = '';
|
|
227
|
+
|
|
228
|
+
for (const filePath of file.paths) {
|
|
229
|
+
const fullPath = path.join(projectPath, filePath);
|
|
230
|
+
if (fs.existsSync(fullPath)) {
|
|
231
|
+
exists = true;
|
|
232
|
+
foundPath = filePath;
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
checks.push({
|
|
238
|
+
name: `CLI file: ${file.name}`,
|
|
239
|
+
status: exists ? 'pass' : 'fail',
|
|
240
|
+
message: exists ? `Found at ${foundPath}` : 'File not created by CLI',
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
if (exists) passed++;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Check 5: .env.local exists (or .env)
|
|
247
|
+
const envLocalPath = path.join(projectPath, '.env.local');
|
|
248
|
+
const envPath = path.join(projectPath, '.env');
|
|
249
|
+
const envExists = fs.existsSync(envLocalPath) || fs.existsSync(envPath);
|
|
250
|
+
|
|
251
|
+
checks.push({
|
|
252
|
+
name: 'Environment file',
|
|
253
|
+
status: envExists ? 'pass' : 'warning',
|
|
254
|
+
message: envExists
|
|
255
|
+
? 'Environment file exists'
|
|
256
|
+
: 'No .env.local or .env found - you may need to create one with NEXT_PUBLIC_VELT_API_KEY',
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
if (envExists) passed++;
|
|
260
|
+
|
|
261
|
+
// Check 6: If .env.local exists, check for API key
|
|
262
|
+
if (fs.existsSync(envLocalPath)) {
|
|
263
|
+
const envContent = fs.readFileSync(envLocalPath, 'utf-8');
|
|
264
|
+
const hasApiKey = envContent.includes('NEXT_PUBLIC_VELT_API_KEY') ||
|
|
265
|
+
envContent.includes('VELT_API_KEY');
|
|
266
|
+
|
|
267
|
+
checks.push({
|
|
268
|
+
name: 'API key in environment',
|
|
269
|
+
status: hasApiKey ? 'pass' : 'warning',
|
|
270
|
+
message: hasApiKey
|
|
271
|
+
? 'Velt API key found in environment file'
|
|
272
|
+
: 'No Velt API key found in .env.local - add NEXT_PUBLIC_VELT_API_KEY',
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
if (hasApiKey) passed++;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Check 7: "use client" directives (Next.js only)
|
|
279
|
+
const projectType = detectProjectType(projectPath);
|
|
280
|
+
if (projectType.projectType === ProjectType.NEXTJS) {
|
|
281
|
+
const useClientValidation = validateUseClientDirectives(projectPath);
|
|
282
|
+
|
|
283
|
+
for (const check of useClientValidation.checks) {
|
|
284
|
+
checks.push(check);
|
|
285
|
+
if (check.status === 'pass') passed++;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return {
|
|
290
|
+
checks,
|
|
291
|
+
passed,
|
|
292
|
+
total: checks.length,
|
|
293
|
+
score: `${passed}/${checks.length}`,
|
|
294
|
+
status: passed === checks.length
|
|
295
|
+
? 'excellent'
|
|
296
|
+
: passed >= checks.length * 0.6
|
|
297
|
+
? 'good'
|
|
298
|
+
: 'needs_attention',
|
|
299
|
+
};
|
|
300
|
+
} catch (error) {
|
|
301
|
+
return {
|
|
302
|
+
checks,
|
|
303
|
+
passed,
|
|
304
|
+
total: checks.length,
|
|
305
|
+
score: `${passed}/${checks.length}`,
|
|
306
|
+
error: error.message,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Full integration validation (for guided path after apply)
|
|
313
|
+
*
|
|
314
|
+
* Checks everything in basic validation PLUS integration:
|
|
315
|
+
* VeltProvider in layout, VeltComments in page, etc.
|
|
316
|
+
*
|
|
317
|
+
* @param {Object} params
|
|
318
|
+
* @param {string} params.projectPath - Project path
|
|
319
|
+
* @param {Object} [params.cliResult] - CLI execution result (optional)
|
|
320
|
+
* @returns {Promise<Object>} Validation result
|
|
321
|
+
*/
|
|
322
|
+
export async function validateInstallation({ projectPath, cliResult = null }) {
|
|
323
|
+
const checks = [];
|
|
324
|
+
let passed = 0;
|
|
325
|
+
let total = 0;
|
|
326
|
+
|
|
327
|
+
try {
|
|
328
|
+
// Check 0: CLI Execution Method (new)
|
|
329
|
+
total++;
|
|
330
|
+
if (cliResult && cliResult.method) {
|
|
331
|
+
checks.push({
|
|
332
|
+
name: 'CLI Execution',
|
|
333
|
+
status: cliResult.success ? 'pass' : 'warning',
|
|
334
|
+
message: cliResult.success
|
|
335
|
+
? `CLI succeeded via ${cliResult.method === 'npx' ? 'npx @velt-js/add-velt' : cliResult.method}`
|
|
336
|
+
: `CLI had issues (${cliResult.method}) but may have created files`,
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
if (cliResult.success || cliResult.method !== 'error') passed++;
|
|
340
|
+
} else {
|
|
341
|
+
const cliCheck = validateCliResolution();
|
|
342
|
+
checks.push({
|
|
343
|
+
name: 'CLI Resolution',
|
|
344
|
+
status: cliCheck.status,
|
|
345
|
+
message: cliCheck.message,
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
if (cliCheck.status === 'pass') passed++;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Check 1: package.json has @veltdev/react
|
|
352
|
+
total++;
|
|
353
|
+
const packageJsonPath = path.join(projectPath, 'package.json');
|
|
354
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
355
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
356
|
+
const hasVelt = !!(
|
|
357
|
+
packageJson.dependencies?.['@veltdev/react'] ||
|
|
358
|
+
packageJson.devDependencies?.['@veltdev/react']
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
checks.push({
|
|
362
|
+
name: 'Velt package installed',
|
|
363
|
+
status: hasVelt ? 'pass' : 'fail',
|
|
364
|
+
message: hasVelt
|
|
365
|
+
? '@veltdev/react found in package.json'
|
|
366
|
+
: '@veltdev/react not found in package.json',
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
if (hasVelt) passed++;
|
|
370
|
+
} else {
|
|
371
|
+
checks.push({
|
|
372
|
+
name: 'Velt package installed',
|
|
373
|
+
status: 'fail',
|
|
374
|
+
message: 'package.json not found',
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Check 2: .env.local has API key
|
|
379
|
+
total++;
|
|
380
|
+
const envPath = path.join(projectPath, '.env.local');
|
|
381
|
+
if (fs.existsSync(envPath)) {
|
|
382
|
+
const envContent = fs.readFileSync(envPath, 'utf-8');
|
|
383
|
+
const hasApiKey = envContent.includes('NEXT_PUBLIC_VELT_API_KEY');
|
|
384
|
+
|
|
385
|
+
checks.push({
|
|
386
|
+
name: 'Environment configured',
|
|
387
|
+
status: hasApiKey ? 'pass' : 'fail',
|
|
388
|
+
message: hasApiKey
|
|
389
|
+
? 'NEXT_PUBLIC_VELT_API_KEY found in .env.local'
|
|
390
|
+
: 'NEXT_PUBLIC_VELT_API_KEY not found in .env.local',
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
if (hasApiKey) passed++;
|
|
394
|
+
} else {
|
|
395
|
+
checks.push({
|
|
396
|
+
name: 'Environment configured',
|
|
397
|
+
status: 'fail',
|
|
398
|
+
message: '.env.local not found',
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Check 3: VeltProvider in layout
|
|
403
|
+
total++;
|
|
404
|
+
const layoutPaths = [
|
|
405
|
+
'app/layout.tsx',
|
|
406
|
+
'app/layout.js',
|
|
407
|
+
'src/app/layout.tsx',
|
|
408
|
+
'src/app/layout.js',
|
|
409
|
+
];
|
|
410
|
+
|
|
411
|
+
let layoutFound = false;
|
|
412
|
+
let hasVeltProvider = false;
|
|
413
|
+
|
|
414
|
+
for (const layoutPath of layoutPaths) {
|
|
415
|
+
const fullPath = path.join(projectPath, layoutPath);
|
|
416
|
+
if (fs.existsSync(fullPath)) {
|
|
417
|
+
layoutFound = true;
|
|
418
|
+
const layoutContent = fs.readFileSync(fullPath, 'utf-8');
|
|
419
|
+
hasVeltProvider = layoutContent.includes('VeltProvider');
|
|
420
|
+
break;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
checks.push({
|
|
425
|
+
name: 'VeltProvider configured',
|
|
426
|
+
status: hasVeltProvider ? 'pass' : 'fail',
|
|
427
|
+
message: layoutFound
|
|
428
|
+
? hasVeltProvider
|
|
429
|
+
? 'VeltProvider found in layout'
|
|
430
|
+
: 'VeltProvider not found in layout'
|
|
431
|
+
: 'Layout file not found',
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
if (hasVeltProvider) passed++;
|
|
435
|
+
|
|
436
|
+
// Check 4: VeltComments in page
|
|
437
|
+
total++;
|
|
438
|
+
const pagePaths = [
|
|
439
|
+
'app/page.tsx',
|
|
440
|
+
'app/page.js',
|
|
441
|
+
'src/app/page.tsx',
|
|
442
|
+
'src/app/page.js',
|
|
443
|
+
];
|
|
444
|
+
|
|
445
|
+
let pageFound = false;
|
|
446
|
+
let hasVeltComments = false;
|
|
447
|
+
|
|
448
|
+
for (const pagePath of pagePaths) {
|
|
449
|
+
const fullPath = path.join(projectPath, pagePath);
|
|
450
|
+
if (fs.existsSync(fullPath)) {
|
|
451
|
+
pageFound = true;
|
|
452
|
+
const pageContent = fs.readFileSync(fullPath, 'utf-8');
|
|
453
|
+
hasVeltComments = pageContent.includes('VeltComments');
|
|
454
|
+
break;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
checks.push({
|
|
459
|
+
name: 'VeltComments added',
|
|
460
|
+
status: hasVeltComments ? 'pass' : 'fail',
|
|
461
|
+
message: pageFound
|
|
462
|
+
? hasVeltComments
|
|
463
|
+
? 'VeltComments found in page'
|
|
464
|
+
: 'VeltComments not found in page'
|
|
465
|
+
: 'Page file not found',
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
if (hasVeltComments) passed++;
|
|
469
|
+
|
|
470
|
+
// Check 5: VeltCommentsSidebar in layout
|
|
471
|
+
total++;
|
|
472
|
+
let hasVeltSidebar = false;
|
|
473
|
+
|
|
474
|
+
if (layoutFound) {
|
|
475
|
+
for (const layoutPath of layoutPaths) {
|
|
476
|
+
const fullPath = path.join(projectPath, layoutPath);
|
|
477
|
+
if (fs.existsSync(fullPath)) {
|
|
478
|
+
const layoutContent = fs.readFileSync(fullPath, 'utf-8');
|
|
479
|
+
hasVeltSidebar = layoutContent.includes('VeltCommentsSidebar');
|
|
480
|
+
break;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
checks.push({
|
|
486
|
+
name: 'VeltCommentsSidebar added',
|
|
487
|
+
status: hasVeltSidebar ? 'pass' : 'fail',
|
|
488
|
+
message: layoutFound
|
|
489
|
+
? hasVeltSidebar
|
|
490
|
+
? 'VeltCommentsSidebar found in layout'
|
|
491
|
+
: 'VeltCommentsSidebar not found in layout'
|
|
492
|
+
: 'Layout file not found',
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
if (hasVeltSidebar) passed++;
|
|
496
|
+
|
|
497
|
+
// Check 6: "use client" directives (Next.js only)
|
|
498
|
+
const projectType = detectProjectType(projectPath);
|
|
499
|
+
if (projectType.projectType === ProjectType.NEXTJS) {
|
|
500
|
+
const useClientValidation = validateUseClientDirectives(projectPath);
|
|
501
|
+
|
|
502
|
+
for (const check of useClientValidation.checks) {
|
|
503
|
+
total++;
|
|
504
|
+
checks.push(check);
|
|
505
|
+
if (check.status === 'pass') passed++;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
return {
|
|
510
|
+
checks,
|
|
511
|
+
passed,
|
|
512
|
+
total,
|
|
513
|
+
score: `${passed}/${total}`,
|
|
514
|
+
status: passed === total ? 'excellent' : passed >= total * 0.8 ? 'good' : 'needs_improvement',
|
|
515
|
+
};
|
|
516
|
+
} catch (error) {
|
|
517
|
+
return {
|
|
518
|
+
checks,
|
|
519
|
+
passed,
|
|
520
|
+
total,
|
|
521
|
+
score: `${passed}/${total}`,
|
|
522
|
+
error: error.message,
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Applies "use client" fixes to files that need them (Next.js only)
|
|
529
|
+
*
|
|
530
|
+
* @param {string} projectPath - Project path
|
|
531
|
+
* @returns {Object} Fix results
|
|
532
|
+
*/
|
|
533
|
+
export function applyUseClientFixes(projectPath) {
|
|
534
|
+
const projectType = detectProjectType(projectPath);
|
|
535
|
+
|
|
536
|
+
if (projectType.projectType !== ProjectType.NEXTJS) {
|
|
537
|
+
return {
|
|
538
|
+
skipped: true,
|
|
539
|
+
reason: `Project type is ${projectType.projectType}, not Next.js`,
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
return scanAndFixUseClient({
|
|
544
|
+
projectPath,
|
|
545
|
+
fix: true,
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
export default {
|
|
550
|
+
validateProject,
|
|
551
|
+
validateNextJsProject,
|
|
552
|
+
validateCliResolution,
|
|
553
|
+
validateBasicCliInstall,
|
|
554
|
+
validateInstallation,
|
|
555
|
+
applyUseClientFixes,
|
|
556
|
+
};
|