@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.
@@ -0,0 +1,886 @@
1
+ /**
2
+ * Unified Velt Installer
3
+ *
4
+ * Single orchestration module that handles both installation paths:
5
+ * 1. CLI-only (SKIP path): Run Velt CLI scaffolding only, basic QA
6
+ * 2. Guided path: Generate plan, await approval, apply edits, full QA
7
+ *
8
+ * Uses npx @velt-js/add-velt to run the published Velt CLI package.
9
+ */
10
+
11
+ import { runVeltCliWithFeatures, runVeltCliCoreOnly } from '../utils/cli.js';
12
+ import { detectLibraries } from '../utils/velt-mcp.js';
13
+ import { getFrameworkInfo } from '../utils/framework-detection.js';
14
+ import {
15
+ fetchCommentImplementation,
16
+ fetchCrdtImplementation,
17
+ fetchFeatureImplementation,
18
+ } from '../utils/velt-docs-fetcher.js';
19
+ import {
20
+ createVeltCommentsPlan,
21
+ createMultiFeaturePlan,
22
+ createCliOnlyReport,
23
+ } from '../utils/plan-formatter.js';
24
+ import {
25
+ validateNextJsProject,
26
+ validateBasicCliInstall,
27
+ validateInstallation,
28
+ applyUseClientFixes as applyFixes,
29
+ } from '../utils/validation.js';
30
+ import {
31
+ discoverHostAppWiring,
32
+ formatDiscoveryForVerification,
33
+ getManualWiringQuestionnaire,
34
+ createWiringFromManualAnswers,
35
+ resolveWiringFromVerification,
36
+ } from '../utils/host-app-discovery.js';
37
+ import path from 'path';
38
+
39
+ /**
40
+ * Masks API key for display (shows first 8 chars + ...)
41
+ */
42
+ function maskApiKey(apiKey) {
43
+ if (!apiKey || apiKey.length < 8) return '***';
44
+ return `${apiKey.substring(0, 8)}...`;
45
+ }
46
+
47
+ /**
48
+ * Unified Velt Installer - Main Entry Point
49
+ *
50
+ * @param {Object} params - Installation parameters
51
+ * @param {string} params.projectPath - Path to project (REQUIRED)
52
+ * @param {string} params.apiKey - Velt API key (REQUIRED)
53
+ * @param {string} params.authToken - Velt Auth Token (REQUIRED)
54
+ * @param {string} [params.mode='guided'] - Installation mode: 'guided' | 'cli-only'
55
+ * @param {string} [params.stage='plan'] - Guided mode stage: 'plan' | 'apply'
56
+ * @param {boolean} [params.approved=false] - Whether user approved the plan (for apply stage)
57
+ * @param {string[]} [params.features=['comments']] - Features to install
58
+ * @param {string} [params.commentType='freestyle'] - Comment type
59
+ * @param {string} [params.crdtEditorType=null] - CRDT editor type
60
+ * @param {string} [params.headerPosition='top-right'] - Sidebar header position
61
+ * @param {string} [params.veltProviderLocation='app/page.tsx'] - VeltProvider location
62
+ * @param {string} [params.discoveryConsent] - User consent for codebase scanning: 'yes' | 'no'
63
+ * @param {Object} [params.discoveryVerification] - Verification of scan results
64
+ * @param {Object} [params.manualWiring] - Manual wiring answers (if consent='no')
65
+ * @param {Object} [params.server=null] - MCP server instance
66
+ * @returns {Promise<Object>} Installation result with status field
67
+ */
68
+ export async function installVeltUnified(params) {
69
+ const {
70
+ projectPath,
71
+ apiKey,
72
+ authToken,
73
+ mode = 'guided',
74
+ stage = 'plan',
75
+ approved = false,
76
+ features = ['comments'],
77
+ commentType = 'freestyle',
78
+ crdtEditorType = null,
79
+ headerPosition = 'top-right',
80
+ veltProviderLocation = 'app/page.tsx',
81
+ discoveryConsent, // NEW: 'yes' | 'no' | undefined
82
+ discoveryVerification, // NEW: { status, overrides? }
83
+ manualWiring, // NEW: { documentId, user, auth, insertion }
84
+ server = null,
85
+ } = params;
86
+
87
+ const resolvedPath = path.resolve(projectPath);
88
+
89
+ // Validate required parameters
90
+ if (!apiKey) {
91
+ return {
92
+ status: 'error',
93
+ error: 'API key is required. Please provide your Velt API Key from https://console.velt.dev',
94
+ };
95
+ }
96
+ if (!authToken) {
97
+ return {
98
+ status: 'error',
99
+ error: 'Auth token is required. Please provide your Velt Auth Token from https://console.velt.dev',
100
+ };
101
+ }
102
+
103
+ // Step 1: Validate Next.js project
104
+ console.error('\nšŸ” Validating project...');
105
+ const projectValidation = validateNextJsProject(resolvedPath);
106
+ if (!projectValidation.valid) {
107
+ return {
108
+ status: 'error',
109
+ error: projectValidation.error,
110
+ hint: 'Make sure you are in a Next.js project directory with a package.json that includes "next" as a dependency.',
111
+ };
112
+ }
113
+ console.error('āœ… Valid Next.js project\n');
114
+
115
+ // Step 2: Detect framework type
116
+ console.error('šŸ”Ž Detecting framework...');
117
+ const frameworkInfo = getFrameworkInfo(resolvedPath);
118
+ console.error(` Project type: ${frameworkInfo.projectType}`);
119
+ console.error(` Needs "use client": ${frameworkInfo.needsUseClient}`);
120
+ if (frameworkInfo.routerType) {
121
+ console.error(` Router type: ${frameworkInfo.routerType}`);
122
+ }
123
+ console.error('');
124
+
125
+ // Step 3: Route based on mode
126
+ if (mode === 'cli-only') {
127
+ console.error('šŸ“¦ CLI-Only Mode (SKIP)\n');
128
+ return runCliOnlyInstall({
129
+ projectPath: resolvedPath,
130
+ apiKey,
131
+ authToken,
132
+ frameworkInfo,
133
+ });
134
+ }
135
+
136
+ // Step 4: Guided mode - route based on stage
137
+ if (mode === 'guided') {
138
+ if (stage === 'plan') {
139
+ console.error('šŸ“‹ Guided Mode - Plan Stage\n');
140
+ return runGuidedPlanStage({
141
+ projectPath: resolvedPath,
142
+ apiKey,
143
+ authToken,
144
+ features,
145
+ commentType,
146
+ crdtEditorType,
147
+ headerPosition,
148
+ veltProviderLocation,
149
+ frameworkInfo,
150
+ // NEW: Pass discovery-related params
151
+ discoveryConsent,
152
+ discoveryVerification,
153
+ manualWiring,
154
+ });
155
+ }
156
+
157
+ if (stage === 'apply') {
158
+ if (!approved) {
159
+ return {
160
+ status: 'error',
161
+ error: 'Cannot apply without user approval. Set approved=true after user confirms the plan.',
162
+ };
163
+ }
164
+ console.error('šŸš€ Guided Mode - Apply Stage\n');
165
+ return runGuidedApplyStage({
166
+ projectPath: resolvedPath,
167
+ apiKey,
168
+ authToken,
169
+ features,
170
+ commentType,
171
+ crdtEditorType,
172
+ headerPosition,
173
+ veltProviderLocation,
174
+ frameworkInfo,
175
+ });
176
+ }
177
+ }
178
+
179
+ return {
180
+ status: 'error',
181
+ error: `Invalid mode "${mode}" or stage "${stage}". Use mode="guided"|"cli-only" and stage="plan"|"apply".`,
182
+ };
183
+ }
184
+
185
+ /**
186
+ * CLI-Only Installation (SKIP path)
187
+ *
188
+ * Runs Velt CLI scaffolding only (NO feature flags = core only),
189
+ * validates basic installation, applies "use client" fixes for Next.js,
190
+ * returns TODO checklist. Does NOT modify project files beyond CLI.
191
+ *
192
+ * @param {Object} params
193
+ * @param {string} params.projectPath - Path to project
194
+ * @param {string} params.apiKey - Velt API key
195
+ * @param {string} params.authToken - Velt Auth Token
196
+ * @param {Object} params.frameworkInfo - Framework detection info
197
+ * @returns {Promise<Object>} CLI-only installation result
198
+ */
199
+ export async function runCliOnlyInstall({ projectPath, apiKey, authToken, frameworkInfo }) {
200
+ const report = {
201
+ status: 'cli_only_complete',
202
+ mode: 'cli-only',
203
+ steps: [],
204
+ startTime: new Date().toISOString(),
205
+ };
206
+
207
+ try {
208
+ // Step 1: Run Velt CLI (core only, no feature flags)
209
+ console.error('āš™ļø Running Velt CLI (core scaffolding only)...');
210
+ const cliResult = await runVeltCliCoreOnly({
211
+ projectPath,
212
+ apiKey,
213
+ authToken,
214
+ });
215
+
216
+ report.steps.push({
217
+ step: 1,
218
+ name: 'run_velt_cli',
219
+ status: cliResult.success ? 'complete' : 'complete_with_warnings',
220
+ result: {
221
+ exitCode: cliResult.exitCode,
222
+ method: cliResult.method, // 'linked' or 'direct'
223
+ command: cliResult.command,
224
+ warning: cliResult.success ? null : 'CLI had issues but may have created files',
225
+ },
226
+ });
227
+
228
+ if (cliResult.success) {
229
+ console.error(`āœ… Velt CLI completed (via ${cliResult.method})\n`);
230
+ } else {
231
+ console.error(`āš ļø Velt CLI completed with warnings (via ${cliResult.method})\n`);
232
+ }
233
+
234
+ // Step 2: Apply "use client" fixes for Next.js
235
+ if (frameworkInfo.needsUseClient) {
236
+ console.error('šŸ”§ Applying "use client" directives for Next.js...');
237
+ const useClientResult = applyFixes(projectPath);
238
+
239
+ report.steps.push({
240
+ step: 2,
241
+ name: 'apply_use_client',
242
+ status: 'complete',
243
+ result: {
244
+ filesFixed: useClientResult.fixed?.length || 0,
245
+ filesScanned: useClientResult.scanned?.length || 0,
246
+ },
247
+ });
248
+
249
+ if (useClientResult.fixed?.length > 0) {
250
+ console.error(` Fixed ${useClientResult.fixed.length} file(s)`);
251
+ for (const fix of useClientResult.fixed) {
252
+ console.error(` - ${fix.path}`);
253
+ }
254
+ } else {
255
+ console.error(' No files needed fixing');
256
+ }
257
+ console.error('');
258
+ }
259
+
260
+ // Step 3: Run basic QA (no integration checks)
261
+ console.error('šŸ” Running basic CLI validation...');
262
+ const qaResult = await validateBasicCliInstall({
263
+ projectPath,
264
+ cliResult, // Pass CLI result for method reporting
265
+ });
266
+
267
+ report.steps.push({
268
+ step: frameworkInfo.needsUseClient ? 3 : 2,
269
+ name: 'basic_qa_validation',
270
+ status: 'complete',
271
+ result: qaResult,
272
+ });
273
+
274
+ console.error(`āœ… Validation complete: ${qaResult.score}\n`);
275
+
276
+ // Step 4: Generate CLI-only report
277
+ console.error('šŸ“‹ Generating installation report...\n');
278
+ const cliReport = createCliOnlyReport({
279
+ cliResult,
280
+ qaResult,
281
+ apiKey: maskApiKey(apiKey),
282
+ cliMethod: cliResult.method, // Add CLI method to report
283
+ frameworkInfo,
284
+ });
285
+
286
+ report.report = cliReport;
287
+ report.validation = qaResult;
288
+ report.endTime = new Date().toISOString();
289
+ report.cliMethod = cliResult.method;
290
+ report.frameworkInfo = {
291
+ projectType: frameworkInfo.projectType,
292
+ needsUseClient: frameworkInfo.needsUseClient,
293
+ };
294
+ report.nextSteps = 'Re-run installer without SKIP to generate a plan + implement features';
295
+
296
+ return report;
297
+ } catch (error) {
298
+ console.error(`\nāŒ Error: ${error.message}\n`);
299
+ report.status = 'failed';
300
+ report.endTime = new Date().toISOString();
301
+ report.error = {
302
+ message: error.message,
303
+ stack: error.stack,
304
+ };
305
+ return report;
306
+ }
307
+ }
308
+
309
+ /**
310
+ * Guided Plan Stage
311
+ *
312
+ * Multi-step flow with discovery consent and verification:
313
+ * 1. Run CLI with feature flags
314
+ * 2. Scan codebase
315
+ * 3. Check discovery consent:
316
+ * - No consent → return awaiting_discovery_consent
317
+ * - consent=yes, no verification → run scan, return awaiting_discovery_verification
318
+ * - consent=yes, verification provided → use verified/overridden results
319
+ * - consent=no, no manualWiring → return awaiting_manual_wiring_answers
320
+ * - consent=no, manualWiring provided → use manual answers
321
+ * 4. Fetch docs
322
+ * 5. Generate plan with wiring data
323
+ *
324
+ * @param {Object} params
325
+ * @param {string} params.projectPath - Path to project
326
+ * @param {string} params.apiKey - Velt API key
327
+ * @param {string} params.authToken - Velt Auth Token
328
+ * @param {string[]} params.features - Features to install
329
+ * @param {string} params.commentType - Comment type
330
+ * @param {string} params.crdtEditorType - CRDT editor type
331
+ * @param {string} params.headerPosition - Header position
332
+ * @param {string} params.veltProviderLocation - VeltProvider location
333
+ * @param {Object} params.frameworkInfo - Framework detection info
334
+ * @param {string} [params.discoveryConsent] - 'yes' | 'no' | undefined
335
+ * @param {Object} [params.discoveryVerification] - { status, overrides? }
336
+ * @param {Object} [params.manualWiring] - { documentId, user, auth, insertion }
337
+ * @returns {Promise<Object>} Plan generation result with status field
338
+ */
339
+ export async function runGuidedPlanStage({
340
+ projectPath,
341
+ apiKey,
342
+ authToken,
343
+ features,
344
+ commentType,
345
+ crdtEditorType,
346
+ headerPosition,
347
+ veltProviderLocation,
348
+ frameworkInfo,
349
+ discoveryConsent,
350
+ discoveryVerification,
351
+ manualWiring,
352
+ }) {
353
+ const report = {
354
+ status: 'in_progress',
355
+ mode: 'guided',
356
+ stage: 'plan',
357
+ steps: [],
358
+ startTime: new Date().toISOString(),
359
+ };
360
+
361
+ try {
362
+ console.error('šŸš€ Starting Guided Installation - Plan Stage');
363
+ console.error(`šŸ“ Project: ${projectPath}`);
364
+ console.error(`šŸ”‘ API Key: ${maskApiKey(apiKey)}`);
365
+ console.error(`✨ Features: ${features.join(', ')}`);
366
+ console.error(`šŸ–„ļø Framework: ${frameworkInfo.projectType}`);
367
+ if (features.includes('comments')) {
368
+ console.error(`šŸ’¬ Comment Type: ${commentType}`);
369
+ }
370
+ if (features.includes('crdt') && crdtEditorType) {
371
+ console.error(`šŸ“ CRDT Editor: ${crdtEditorType}`);
372
+ }
373
+ console.error('');
374
+
375
+ // Step 1: Run Velt CLI with feature flags
376
+ console.error('āš™ļø Step 1/5: Running Velt CLI with feature flags...');
377
+ const cliResult = await runVeltCliWithFeatures({
378
+ projectPath,
379
+ apiKey,
380
+ authToken,
381
+ features,
382
+ commentType,
383
+ crdtEditorType,
384
+ });
385
+
386
+ report.steps.push({
387
+ step: 1,
388
+ name: 'run_velt_cli',
389
+ status: cliResult.success ? 'complete' : 'complete_with_warnings',
390
+ result: {
391
+ exitCode: cliResult.exitCode,
392
+ method: cliResult.method,
393
+ command: cliResult.command,
394
+ warning: cliResult.success ? null : 'CLI had issues but may have created files',
395
+ },
396
+ });
397
+
398
+ if (cliResult.success) {
399
+ console.error(`āœ… Step 1/5: Velt CLI completed (via ${cliResult.method})\n`);
400
+ } else {
401
+ console.error(`āš ļø Step 1/5: Velt CLI completed with warnings (via ${cliResult.method})\n`);
402
+ }
403
+
404
+ // Step 1.5: Apply "use client" fixes for Next.js
405
+ if (frameworkInfo.needsUseClient) {
406
+ console.error('šŸ”§ Applying "use client" directives...');
407
+ const useClientResult = applyFixes(projectPath);
408
+ if (useClientResult.fixed?.length > 0) {
409
+ console.error(` Fixed ${useClientResult.fixed.length} file(s)\n`);
410
+ } else {
411
+ console.error(' No files needed fixing\n');
412
+ }
413
+ }
414
+
415
+ // Step 2: Scan codebase (detect libraries)
416
+ console.error('šŸ” Step 2/5: Scanning codebase...');
417
+ const libraryDetection = detectLibraries(projectPath);
418
+ const detectedLibs = Object.entries(libraryDetection)
419
+ .filter(([_, detected]) => detected)
420
+ .map(([key]) => key);
421
+
422
+ if (detectedLibs.length > 0) {
423
+ console.error(` šŸ“š Detected libraries: ${detectedLibs.join(', ')}`);
424
+ }
425
+
426
+ report.steps.push({
427
+ step: 2,
428
+ name: 'scan_codebase',
429
+ status: 'complete',
430
+ result: {
431
+ librariesDetected: detectedLibs,
432
+ frameworkInfo: {
433
+ projectType: frameworkInfo.projectType,
434
+ routerType: frameworkInfo.routerType,
435
+ },
436
+ },
437
+ });
438
+
439
+ console.error('āœ… Step 2/5: Codebase scanned\n');
440
+
441
+ // ============================================================
442
+ // Step 3: Discovery Consent Gate
443
+ // ============================================================
444
+
445
+ // 3A: No consent provided yet → ask for consent
446
+ if (!discoveryConsent) {
447
+ console.error('šŸ” Step 3/5: Awaiting discovery consent...\n');
448
+ report.status = 'awaiting_discovery_consent';
449
+ report.cliMethod = cliResult.method;
450
+ report.cliResult = {
451
+ success: cliResult.success,
452
+ method: cliResult.method,
453
+ command: cliResult.command,
454
+ };
455
+ report.frameworkInfo = {
456
+ projectType: frameworkInfo.projectType,
457
+ needsUseClient: frameworkInfo.needsUseClient,
458
+ };
459
+ report.message = 'CLI scaffolding complete. Ready to discover integration points.';
460
+ report.nextAction = {
461
+ question: 'Do you want me to scan your codebase to infer Document ID, User identity, setDocuments placement, and JWT/Auth wiring?',
462
+ options: [
463
+ { value: 'yes', label: 'YES (recommended) - Scan codebase automatically' },
464
+ { value: 'no', label: 'NO - I\'ll provide the wiring info manually' },
465
+ ],
466
+ };
467
+ return report;
468
+ }
469
+
470
+ // 3B: Consent = YES → scan path
471
+ let wiring = null;
472
+ let discoveryResult = null;
473
+
474
+ if (discoveryConsent === 'yes') {
475
+ console.error('šŸ”Ž Step 3/5: Running host app wiring discovery...');
476
+ discoveryResult = discoverHostAppWiring(projectPath);
477
+
478
+ report.steps.push({
479
+ step: 3,
480
+ name: 'host_app_discovery',
481
+ status: 'complete',
482
+ result: {
483
+ totalSignals: discoveryResult.summary.totalSignals,
484
+ questionsCount: discoveryResult.summary.questionsForDeveloper.length,
485
+ documentIdConfidence: discoveryResult.documentId.confidence,
486
+ userAuthConfidence: discoveryResult.user.confidence,
487
+ authProvider: discoveryResult.user.authProvider,
488
+ recommendedSetupLocation: discoveryResult.setDocuments.recommendedLocation?.file,
489
+ },
490
+ });
491
+
492
+ console.error(` Found ${discoveryResult.summary.totalSignals} signals\n`);
493
+
494
+ // 3B-i: Scan done but no verification yet → ask for verification
495
+ if (!discoveryVerification) {
496
+ console.error(' āøļø Awaiting user verification of scan results...\n');
497
+ report.status = 'awaiting_discovery_verification';
498
+ report.discovery = discoveryResult;
499
+ report.formattedFindings = formatDiscoveryForVerification(discoveryResult);
500
+ report.cliMethod = cliResult.method;
501
+ report.frameworkInfo = {
502
+ projectType: frameworkInfo.projectType,
503
+ needsUseClient: frameworkInfo.needsUseClient,
504
+ };
505
+ report.message = 'Discovery scan complete. Please verify the findings.';
506
+ report.nextAction = {
507
+ question: 'Are these findings correct?',
508
+ options: [
509
+ { value: 'confirmed', label: 'CONFIRM ALL - Findings are correct' },
510
+ { value: 'edited', label: 'EDIT - I need to correct some items' },
511
+ { value: 'unsure', label: 'UNSURE - Need human help to determine this' },
512
+ ],
513
+ };
514
+ return report;
515
+ }
516
+
517
+ // 3B-ii: Verification provided → resolve wiring
518
+ console.error(' āœ… Verification received, resolving wiring...\n');
519
+ wiring = resolveWiringFromVerification(discoveryResult, discoveryVerification);
520
+
521
+ } else if (discoveryConsent === 'no') {
522
+ // 3C: Consent = NO → manual path
523
+ console.error('šŸ”Ž Step 3/5: Manual wiring path (user declined scanning)...');
524
+
525
+ // 3C-i: No manual wiring yet → ask questionnaire
526
+ if (!manualWiring) {
527
+ console.error(' āøļø Awaiting manual wiring answers...\n');
528
+ report.status = 'awaiting_manual_wiring_answers';
529
+ report.questionnaire = getManualWiringQuestionnaire();
530
+ report.cliMethod = cliResult.method;
531
+ report.frameworkInfo = {
532
+ projectType: frameworkInfo.projectType,
533
+ needsUseClient: frameworkInfo.needsUseClient,
534
+ };
535
+ report.message = 'Please provide wiring information manually.';
536
+ return report;
537
+ }
538
+
539
+ // 3C-ii: Manual wiring provided → use it
540
+ console.error(' āœ… Manual wiring received, processing...\n');
541
+ wiring = createWiringFromManualAnswers(manualWiring);
542
+ }
543
+
544
+ // At this point we MUST have wiring data
545
+ if (!wiring) {
546
+ throw new Error('Internal error: wiring data not resolved after discovery/manual flow');
547
+ }
548
+
549
+ report.steps.push({
550
+ step: 3.5,
551
+ name: 'wiring_resolved',
552
+ status: 'complete',
553
+ result: {
554
+ source: wiring.source,
555
+ hasTodos: wiring.todos.length > 0,
556
+ todoCount: wiring.todos.length,
557
+ },
558
+ });
559
+
560
+ if (wiring.todos.length > 0) {
561
+ console.error(` āš ļø ${wiring.todos.length} item(s) marked as TODO (need developer input)\n`);
562
+ }
563
+
564
+ // ============================================================
565
+ // Step 4: Fetch implementation docs (parallel)
566
+ // ============================================================
567
+ console.error('šŸ“š Step 4/5: Fetching implementation details from Velt Docs...');
568
+
569
+ const fetchPromises = [];
570
+ const fetchResults = {};
571
+
572
+ // Fetch comments implementation
573
+ if (features.includes('comments')) {
574
+ fetchPromises.push(
575
+ fetchCommentImplementation({ commentType, mcpClient: null })
576
+ .then(result => { fetchResults.comments = result; })
577
+ .catch(err => { fetchResults.comments = { error: err.message }; })
578
+ );
579
+ }
580
+
581
+ // Fetch CRDT implementation
582
+ if (features.includes('crdt') && !crdtEditorType) {
583
+ console.error(' āš ļø CRDT feature selected but no crdtEditorType provided — skipping CRDT docs fetch');
584
+ }
585
+ if (features.includes('crdt') && crdtEditorType) {
586
+ fetchPromises.push(
587
+ fetchCrdtImplementation({ editorType: crdtEditorType, mcpClient: null })
588
+ .then(result => { fetchResults.crdt = result; })
589
+ .catch(err => { fetchResults.crdt = { error: err.message }; })
590
+ );
591
+ }
592
+
593
+ // Fetch other feature implementations
594
+ for (const feature of features) {
595
+ if (feature !== 'comments' && feature !== 'crdt') {
596
+ fetchPromises.push(
597
+ fetchFeatureImplementation({ feature, mcpClient: null })
598
+ .then(result => { fetchResults[feature] = result; })
599
+ .catch(err => { fetchResults[feature] = { error: err.message }; })
600
+ );
601
+ }
602
+ }
603
+
604
+ await Promise.all(fetchPromises);
605
+
606
+ // Extract results — filter out error objects so they don't pass as valid implementations
607
+ const isValidResult = (r) => r && !r.error;
608
+
609
+ const implementation = isValidResult(fetchResults.comments) ? fetchResults.comments : null;
610
+ const crdtImplementation = isValidResult(fetchResults.crdt) ? fetchResults.crdt : null;
611
+ const featureImplementations = {};
612
+
613
+ for (const [key, value] of Object.entries(fetchResults)) {
614
+ if (key !== 'comments' && key !== 'crdt') {
615
+ if (isValidResult(value)) {
616
+ featureImplementations[key] = value;
617
+ }
618
+ }
619
+ }
620
+
621
+ // Log results
622
+ if (implementation) {
623
+ console.error(` āœ… Got ${commentType} comments from: ${implementation.source || 'docs'}`);
624
+ } else if (fetchResults.comments?.error) {
625
+ console.error(` āš ļø Failed to fetch ${commentType} comments: ${fetchResults.comments.error}`);
626
+ }
627
+ if (crdtImplementation) {
628
+ console.error(` āœ… Got ${crdtEditorType} CRDT from: ${crdtImplementation.source || 'docs'}`);
629
+ } else if (fetchResults.crdt?.error) {
630
+ console.error(` āš ļø Failed to fetch ${crdtEditorType} CRDT: ${fetchResults.crdt.error}`);
631
+ }
632
+ for (const [feature, impl] of Object.entries(featureImplementations)) {
633
+ console.error(` āœ… Got ${feature} from: ${impl.source || 'docs'}`);
634
+ }
635
+ // Log failed feature fetches
636
+ for (const [key, value] of Object.entries(fetchResults)) {
637
+ if (key !== 'comments' && key !== 'crdt' && value?.error) {
638
+ console.error(` āš ļø Failed to fetch ${key}: ${value.error}`);
639
+ }
640
+ }
641
+
642
+ report.steps.push({
643
+ step: 4,
644
+ name: 'fetch_implementation',
645
+ status: 'complete',
646
+ result: {
647
+ comments: implementation ? { source: implementation.source, docUrl: implementation.docUrl } : null,
648
+ crdt: crdtImplementation ? { source: crdtImplementation.source, docUrl: crdtImplementation.docUrl } : null,
649
+ otherFeatures: Object.keys(featureImplementations).length,
650
+ },
651
+ });
652
+
653
+ console.error('āœ… Step 4/5: Implementation details fetched\n');
654
+
655
+ // ============================================================
656
+ // Step 5: Generate plan with wiring data
657
+ // ============================================================
658
+ console.error('šŸ“‹ Step 5/5: Generating implementation plan...');
659
+
660
+ const plan = features.length > 1 || (features.length === 1 && features[0] !== 'comments')
661
+ ? createMultiFeaturePlan({
662
+ features,
663
+ commentType,
664
+ implementation,
665
+ crdtImplementation,
666
+ featureImplementations,
667
+ detectedFiles: [],
668
+ apiKey: maskApiKey(apiKey),
669
+ headerPosition,
670
+ veltProviderLocation,
671
+ crdtEditorType,
672
+ frameworkInfo,
673
+ wiring, // Pass resolved wiring data
674
+ })
675
+ : createVeltCommentsPlan({
676
+ commentType,
677
+ implementation,
678
+ detectedFiles: [],
679
+ apiKey: maskApiKey(apiKey),
680
+ headerPosition,
681
+ veltProviderLocation,
682
+ crdtEditorType,
683
+ frameworkInfo,
684
+ wiring, // Pass resolved wiring data
685
+ });
686
+
687
+ // Append wiring section to the plan
688
+ let wiringSection = `\n## šŸ”Œ Host App Wiring (${wiring.source})\n\n`;
689
+
690
+ if (wiring.documentId && !wiring.documentId.unsure) {
691
+ wiringSection += `### Document ID\n`;
692
+ if (wiring.documentId.method) wiringSection += `- **Method**: ${wiring.documentId.method}\n`;
693
+ if (wiring.documentId.filePath) wiringSection += `- **File**: \`${wiring.documentId.filePath}\`\n`;
694
+ if (wiring.documentId.variableName) wiringSection += `- **Variable**: \`${wiring.documentId.variableName}\`\n`;
695
+ wiringSection += '\n';
696
+ }
697
+
698
+ if (wiring.user && !wiring.user.unsure) {
699
+ wiringSection += `### User Authentication\n`;
700
+ if (wiring.user.providerType) wiringSection += `- **Provider**: ${wiring.user.providerType}\n`;
701
+ if (wiring.user.filePath) wiringSection += `- **File**: \`${wiring.user.filePath}\`\n`;
702
+ if (wiring.user.fields) wiringSection += `- **Fields**: ${wiring.user.fields.join(', ')}\n`;
703
+ wiringSection += '\n';
704
+ }
705
+
706
+ if (wiring.insertion && !wiring.insertion.unsure) {
707
+ wiringSection += `### Velt Initialization Location\n`;
708
+ if (wiring.insertion.locationType) wiringSection += `- **Location type**: ${wiring.insertion.locationType}\n`;
709
+ if (wiring.insertion.filePath) wiringSection += `- **File**: \`${wiring.insertion.filePath}\`\n`;
710
+ wiringSection += '\n';
711
+ }
712
+
713
+ if (wiring.todos.length > 0) {
714
+ wiringSection += `### āš ļø TODOs (Need Developer Input)\n\n`;
715
+ wiringSection += wiring.todos.map(t => `- ${t}`).join('\n') + '\n\n';
716
+ wiringSection += `**IMPORTANT**: The items above need human clarification before implementation.\n`;
717
+ }
718
+
719
+ const planWithWiring = plan + wiringSection;
720
+
721
+ report.steps.push({
722
+ step: 5,
723
+ name: 'generate_plan',
724
+ status: 'complete',
725
+ });
726
+
727
+ report.status = 'plan_generated';
728
+ report.plan = planWithWiring;
729
+ report.wiring = wiring;
730
+ report.endTime = new Date().toISOString();
731
+ report.cliMethod = cliResult.method;
732
+ report.frameworkInfo = {
733
+ projectType: frameworkInfo.projectType,
734
+ needsUseClient: frameworkInfo.needsUseClient,
735
+ };
736
+ report.message = 'Plan generated. Present to user and await approval before applying.';
737
+
738
+ console.error('āœ… Step 5/5: Plan generated\n');
739
+ console.error('šŸ“ Present the plan to the user and ask for approval.\n');
740
+
741
+ if (wiring.todos.length > 0) {
742
+ console.error('āš ļø IMPORTANT: The plan includes TODOs that need developer input.');
743
+ console.error(' Please review the "TODOs" section before proceeding.\n');
744
+ }
745
+
746
+ return report;
747
+ } catch (error) {
748
+ console.error(`\nāŒ Error: ${error.message}\n`);
749
+ report.status = 'error';
750
+ report.endTime = new Date().toISOString();
751
+ report.error = {
752
+ message: error.message,
753
+ stack: error.stack,
754
+ };
755
+ return report;
756
+ }
757
+ }
758
+
759
+ /**
760
+ * Guided Apply Stage
761
+ *
762
+ * Called after user approves the plan. The AI is expected to apply
763
+ * the plan by making file edits. This function runs full QA validation
764
+ * and ensures "use client" directives are applied for Next.js.
765
+ *
766
+ * Note: The actual file edits are done by the AI based on the plan,
767
+ * not by this function. This function validates the result.
768
+ *
769
+ * @param {Object} params
770
+ * @param {string} params.projectPath - Path to project
771
+ * @param {string} params.apiKey - Velt API key
772
+ * @param {string} params.authToken - Velt Auth Token
773
+ * @param {string[]} params.features - Features installed
774
+ * @param {string} params.commentType - Comment type
775
+ * @param {string} params.crdtEditorType - CRDT editor type
776
+ * @param {string} params.headerPosition - Header position
777
+ * @param {string} params.veltProviderLocation - VeltProvider location
778
+ * @param {Object} params.frameworkInfo - Framework detection info
779
+ * @returns {Promise<Object>} Apply stage result
780
+ */
781
+ export async function runGuidedApplyStage({
782
+ projectPath,
783
+ apiKey,
784
+ authToken,
785
+ features,
786
+ commentType,
787
+ crdtEditorType,
788
+ headerPosition,
789
+ veltProviderLocation,
790
+ frameworkInfo,
791
+ }) {
792
+ const report = {
793
+ status: 'apply_complete',
794
+ mode: 'guided',
795
+ stage: 'apply',
796
+ steps: [],
797
+ startTime: new Date().toISOString(),
798
+ };
799
+
800
+ try {
801
+ console.error('šŸš€ Guided Installation - Apply Stage');
802
+ console.error('šŸ“ AI should have applied the plan. Running validation...\n');
803
+
804
+ // Step 1: Apply "use client" fixes for any files the AI may have created
805
+ if (frameworkInfo.needsUseClient) {
806
+ console.error('šŸ”§ Ensuring "use client" directives are applied...');
807
+ const useClientResult = applyFixes(projectPath);
808
+
809
+ report.steps.push({
810
+ step: 1,
811
+ name: 'apply_use_client',
812
+ status: 'complete',
813
+ result: {
814
+ filesFixed: useClientResult.fixed?.length || 0,
815
+ filesScanned: useClientResult.scanned?.length || 0,
816
+ },
817
+ });
818
+
819
+ if (useClientResult.fixed?.length > 0) {
820
+ console.error(` Fixed ${useClientResult.fixed.length} additional file(s)`);
821
+ for (const fix of useClientResult.fixed) {
822
+ console.error(` - ${fix.path}`);
823
+ }
824
+ } else {
825
+ console.error(' All files have correct directives');
826
+ }
827
+ console.error('');
828
+ }
829
+
830
+ // Step 2: Run full integration QA
831
+ console.error('šŸ” Running full integration validation...');
832
+ const qaResult = await validateInstallation({ projectPath });
833
+
834
+ report.steps.push({
835
+ step: frameworkInfo.needsUseClient ? 2 : 1,
836
+ name: 'full_qa_validation',
837
+ status: 'complete',
838
+ result: qaResult,
839
+ });
840
+
841
+ report.validation = qaResult;
842
+ report.endTime = new Date().toISOString();
843
+ report.frameworkInfo = {
844
+ projectType: frameworkInfo.projectType,
845
+ needsUseClient: frameworkInfo.needsUseClient,
846
+ };
847
+
848
+ console.error(`āœ… Validation complete: ${qaResult.score}\n`);
849
+
850
+ // Generate summary message
851
+ const passedAll = qaResult.passed === qaResult.total;
852
+ if (passedAll) {
853
+ report.message = 'Installation complete! All validation checks passed. Check browser DevTools for Velt errors.';
854
+ console.error('šŸŽ‰ Installation complete! All checks passed.\n');
855
+ } else {
856
+ report.message = `Installation complete with some checks not passing (${qaResult.score}). Review the validation results and check browser DevTools for Velt errors.`;
857
+ console.error(`āš ļø Installation complete. Some checks may need attention: ${qaResult.score}\n`);
858
+ }
859
+
860
+ // Add guidance for next steps
861
+ report.nextSteps = [
862
+ 'Start your development server (npm run dev)',
863
+ 'Open browser DevTools Console (F12) and look for Velt messages',
864
+ 'Test the installed features',
865
+ 'If errors occur, query Velt Docs MCP for solutions',
866
+ ];
867
+
868
+ return report;
869
+ } catch (error) {
870
+ console.error(`\nāŒ Error: ${error.message}\n`);
871
+ report.status = 'failed';
872
+ report.endTime = new Date().toISOString();
873
+ report.error = {
874
+ message: error.message,
875
+ stack: error.stack,
876
+ };
877
+ return report;
878
+ }
879
+ }
880
+
881
+ export default {
882
+ installVeltUnified,
883
+ runCliOnlyInstall,
884
+ runGuidedPlanStage,
885
+ runGuidedApplyStage,
886
+ };