@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,1698 @@
1
+ /**
2
+ * Plan Formatter
3
+ *
4
+ * Formats installation instructions as a sequential plan (similar to Cursor's plan mode)
5
+ * that the AI can follow to complete the implementation.
6
+ */
7
+
8
+ import { getDocUrl, getDocMarkdownUrl } from './velt-docs-urls.js';
9
+ import { getSkillReferences } from './velt-docs-fetcher.js';
10
+
11
+ /**
12
+ * Generates a "Skills & Sources" section for the plan output.
13
+ * Skills-covered features get a skill reference; others get docs URLs.
14
+ *
15
+ * @param {string[]} features - Features being installed
16
+ * @param {Object} [options] - Options (commentType, crdtEditorType)
17
+ * @returns {string} Markdown section
18
+ */
19
+ function formatSkillsSourceSection(features, options = {}) {
20
+ const refs = getSkillReferences(features, options);
21
+
22
+ let section = `## Implementation Sources (Priority Order)\n\n`;
23
+ section += `> **PREREQUISITE:** Install Velt Agent Skills via \`npx skills add velt-js/agent-skills\`\n>\n`;
24
+ section += `> **Source Priority:** 1) Agent Skills (primary) → 2) Docs URLs (secondary) → 3) Velt Docs MCP (user follow-up only)\n\n`;
25
+
26
+ const skillRefs = refs.filter(r => r.skillName);
27
+ const docsRefs = refs.filter(r => r.docsUrl);
28
+
29
+ if (skillRefs.length > 0) {
30
+ section += `### Primary: Agent Skills (use these first)\n\n`;
31
+ // Deduplicate skill names
32
+ const seen = new Set();
33
+ for (const ref of skillRefs) {
34
+ if (!seen.has(ref.skillName)) {
35
+ seen.add(ref.skillName);
36
+ const features = skillRefs.filter(r => r.skillName === ref.skillName).map(r => r.feature);
37
+ section += `- **${ref.skillName}** — covers: ${features.join(', ')}${ref.description ? ` (${ref.description})` : ''}\n`;
38
+ }
39
+ }
40
+ section += `\n`;
41
+ }
42
+
43
+ if (docsRefs.length > 0) {
44
+ section += `### Secondary: Docs URLs (for features without skills)\n\n`;
45
+ for (const ref of docsRefs) {
46
+ section += `- ${ref.feature}: ${ref.docsUrl}\n`;
47
+ }
48
+ section += `\n`;
49
+ }
50
+
51
+ section += `### Tertiary: Velt Docs MCP\n`;
52
+ section += `- Only use for user follow-up questions AFTER implementation\n`;
53
+ section += `- Do NOT query Velt Docs MCP during initial implementation if skills cover the feature\n\n`;
54
+
55
+ return section;
56
+ }
57
+
58
+ /**
59
+ * Formats a plan with numbered steps, details, and a to-do checklist
60
+ *
61
+ * @param {Object} options - Plan options
62
+ * @param {string} options.title - Plan title (e.g., "Plan for Velt Freestyle Comments Installation")
63
+ * @param {Array} options.steps - Array of step objects
64
+ * @param {string} options.steps[].title - Step title
65
+ * @param {string} options.steps[].details - Step details/instructions
66
+ * @param {string[]} [options.steps[].codeExamples] - Optional code examples
67
+ * @param {Array} [options.additionalInfo] - Additional information sections
68
+ * @returns {string} Formatted plan as markdown
69
+ */
70
+ export function formatInstallationPlan(options) {
71
+ const { title, steps, additionalInfo = [] } = options;
72
+
73
+ let plan = `# ${title}\n\n`;
74
+
75
+ // Add numbered steps with details
76
+ steps.forEach((step, index) => {
77
+ const stepNumber = index + 1;
78
+ plan += `## ${stepNumber}. ${step.title}\n`;
79
+ plan += `* **Details:** ${step.details}\n`;
80
+
81
+ // Add code examples if provided
82
+ if (step.codeExamples && step.codeExamples.length > 0) {
83
+ step.codeExamples.forEach((example) => {
84
+ plan += `\n${example.description ? `* ${example.description}:` : ''}
85
+ \`\`\`${example.language || 'tsx'}
86
+ ${example.code}
87
+ \`\`\`\n`;
88
+ });
89
+ }
90
+
91
+ plan += '\n';
92
+ });
93
+
94
+ // Add additional information sections
95
+ if (additionalInfo.length > 0) {
96
+ additionalInfo.forEach((info) => {
97
+ plan += `## ${info.title}\n`;
98
+ plan += `${info.content}\n\n`;
99
+ });
100
+ }
101
+
102
+ // Add To-Do checklist
103
+ plan += `## To-Do List\n`;
104
+ steps.forEach((step, index) => {
105
+ const checkbox = index === 0 ? '[✓]' : '[ ]';
106
+ plan += `- ${checkbox} ${step.title}\n`;
107
+ });
108
+
109
+ plan += '\n';
110
+
111
+ return plan;
112
+ }
113
+
114
+ /**
115
+ * Creates a plan for Velt Comments installation
116
+ *
117
+ * @param {Object} options - Installation options
118
+ * @param {string} options.commentType - Type of comments (freestyle, popover, page, stream, text)
119
+ * @param {Object} options.implementation - Implementation details from Velt Docs
120
+ * @param {Array} options.detectedFiles - Files detected for modification
121
+ * @param {string} options.apiKey - API key preview
122
+ * @param {string} options.headerPosition - Header position
123
+ * @param {string} options.veltProviderLocation - Where to install VeltProvider
124
+ * @param {string} options.crdtEditorType - CRDT editor type (tiptap, codemirror, blocknote)
125
+ * @returns {string} Formatted installation plan
126
+ */
127
+ export function createVeltCommentsPlan(options) {
128
+ const {
129
+ commentType,
130
+ implementation,
131
+ detectedFiles = [],
132
+ apiKey,
133
+ headerPosition,
134
+ veltProviderLocation = 'app/page.tsx',
135
+ crdtEditorType,
136
+ } = options;
137
+
138
+ const commentTypeTitle = commentType.charAt(0).toUpperCase() + commentType.slice(1);
139
+
140
+ const steps = [];
141
+
142
+ // Add warning about only implementing requested features
143
+ steps.push({
144
+ title: `⚠️ CRITICAL: Only implement ${commentTypeTitle} Comments`,
145
+ details: `You are ONLY installing ${commentTypeTitle} Comments. DO NOT implement: VeltNotificationsTool, VeltPresence, VeltCursor, VeltRecorder, or any other components unless the user specifically requested them. Only use authentication, user setup, and document setup from CLI. Get ${commentTypeTitle} Comments implementation from: ${implementation?.mdUrl || getDocMarkdownUrl('comments', commentType)}`,
146
+ });
147
+
148
+ // Step 2: Use CLI-generated Velt components
149
+ const locationText = veltProviderLocation === 'auto-detect'
150
+ ? 'the appropriate layout file (analyze the project structure to determine the best location)'
151
+ : veltProviderLocation;
152
+
153
+ steps.push({
154
+ title: `Import and use CLI-generated Velt components in ${locationText}`,
155
+ details: `The Velt CLI has generated the necessary component files in \`components/velt/\`. DO NOT create new files. Use the existing files:
156
+
157
+ **CLI-Generated Files:**
158
+ - \`components/velt/VeltInitializeUser.tsx\` - Exports \`useVeltAuthProvider\` hook (NOT a wrapper component)
159
+ - \`components/velt/VeltInitializeDocument.tsx\` - Exports \`useCurrentDocument\` hook for document context
160
+ - \`components/velt/VeltCollaboration.tsx\` - Velt feature components (comments, presence, etc.)
161
+ - \`app/userAuth/AppUserContext.tsx\` - User context provider wrapper
162
+ - \`app/userAuth/useAppUser.tsx\` - User data hook (add TODOs here)
163
+ - \`app/api/velt/token/route.ts\` - Token generation API
164
+
165
+ ⚠️ **CRITICAL ARCHITECTURE (Sample App Pattern):**
166
+
167
+ **1. app/layout.tsx - Wrap with AppUserProvider:**
168
+ \`\`\`tsx
169
+ import { AppUserProvider } from './userAuth/AppUserContext'
170
+
171
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
172
+ return (
173
+ <html lang="en">
174
+ <body>
175
+ <AppUserProvider>
176
+ {children}
177
+ </AppUserProvider>
178
+ </body>
179
+ </html>
180
+ )
181
+ }
182
+ \`\`\`
183
+ **Why**: AppUserProvider provides user context to all components including Velt auth.
184
+
185
+ **2. app/page.tsx (or root page) - VeltProvider with authProvider hook:**
186
+ \`\`\`tsx
187
+ "use client";
188
+ import { VeltProvider } from '@veltdev/react';
189
+ import { useVeltAuthProvider } from '@/components/velt/VeltInitializeUser';
190
+ import { useCurrentDocument } from '@/components/velt/VeltInitializeDocument';
191
+ import { VeltCollaboration } from '@/components/velt/VeltCollaboration';
192
+
193
+ const VELT_API_KEY = process.env.NEXT_PUBLIC_VELT_API_KEY!;
194
+
195
+ export default function Page() {
196
+ const { authProvider } = useVeltAuthProvider();
197
+ const { documentId } = useCurrentDocument();
198
+
199
+ return (
200
+ <VeltProvider apiKey={VELT_API_KEY} authProvider={authProvider}>
201
+ <VeltCollaboration documentId={documentId} />
202
+ {/* Your page content */}
203
+ </VeltProvider>
204
+ );
205
+ }
206
+ \`\`\`
207
+ **Why**: VeltProvider must be in page.tsx with authProvider from hook, NOT nested wrapper components.
208
+
209
+ **What to do:**
210
+ 1. Add AppUserProvider wrapper to app/layout.tsx
211
+ 2. Add VeltProvider to ${locationText} using useVeltAuthProvider hook
212
+ 3. Import VeltCollaboration for feature components
213
+ 4. Follow the markdown documentation for ${commentType} comment-specific implementation: ${implementation?.mdUrl || getDocMarkdownUrl('comments', commentType)}
214
+
215
+ **CRITICAL - For Tiptap/Lexical/Slate Comments:**
216
+ - ✅ **FIND EXISTING EDITOR** - Search the project for existing Tiptap/Lexical/Slate editor components
217
+ - ✅ **INTEGRATE INTO EXISTING EDITOR** - Add Velt comments to the existing editor, DO NOT create a new editor
218
+ - ✅ **USE BUBBLE MENU PATTERN** - Add comment button to bubble menu (appears on text selection), NOT a fixed toolbar
219
+ - ✅ Use ONLY the comment-specific package: @veltdev/tiptap-velt-comments, @veltdev/lexical-velt-comments, or @veltdev/slate-velt-comments
220
+ - ❌ DO NOT create a new editor component if one already exists
221
+ - ❌ DO NOT create a fixed toolbar with Bold/Italic/Comment buttons
222
+ - ❌ DO NOT use CRDT packages (@veltdev/tiptap-velt-collaboration or similar)
223
+ - ❌ DO NOT implement real-time collaborative editing - only comments on the editor
224
+
225
+ **Tiptap Pattern:**
226
+ \`\`\`tsx
227
+ import { BubbleMenu } from '@tiptap/react'
228
+ import { TiptapVeltComments, addComment, renderComments } from '@veltdev/tiptap-velt-comments'
229
+ import { useCommentAnnotations } from '@veltdev/react'
230
+
231
+ // Add to editor extensions: TiptapVeltComments
232
+ // Use BubbleMenu component with comment button
233
+ <BubbleMenu editor={editor}>
234
+ <button onClick={() => addComment({ editor })}>💬 Comment</button>
235
+ </BubbleMenu>
236
+ \`\`\`
237
+
238
+ **Lexical Pattern:**
239
+ - Add VeltCommentsPlugin to editor plugins
240
+ - Use custom bubble menu that appears on selection
241
+ - Trigger addComment() from bubble menu button
242
+
243
+ **Slate Pattern:**
244
+ - Wrap editor with withVeltComments()
245
+ - Implement bubble menu with position tracking on selection
246
+ - Trigger addComment({ editor }) from bubble menu button
247
+
248
+ **If NO existing editor found:** Provide minimal integration example with bubble menu, but recommend user add to their existing editor.
249
+
250
+ **IMPORTANT:** All Velt-related files should remain in \`components/velt/\`. Do not create new Velt files outside this folder.`,
251
+ codeExamples: [
252
+ {
253
+ description: `Correct pattern: VeltProvider in page.tsx with authProvider hook`,
254
+ language: 'tsx',
255
+ code: `// app/page.tsx (or your root page component)
256
+ "use client";
257
+ import { VeltProvider } from '@veltdev/react';
258
+ import { useVeltAuthProvider } from '@/components/velt/VeltInitializeUser';
259
+ import { useCurrentDocument } from '@/components/velt/VeltInitializeDocument';
260
+ import { VeltCollaboration } from '@/components/velt/VeltCollaboration';
261
+
262
+ const VELT_API_KEY = process.env.NEXT_PUBLIC_VELT_API_KEY!;
263
+
264
+ export default function Page() {
265
+ const { authProvider } = useVeltAuthProvider();
266
+ const { documentId } = useCurrentDocument();
267
+
268
+ return (
269
+ <VeltProvider apiKey={VELT_API_KEY} authProvider={authProvider}>
270
+ <VeltCollaboration documentId={documentId} />
271
+ {/* Your page content here */}
272
+ </VeltProvider>
273
+ );
274
+ }
275
+
276
+ // For ${commentType} comments: Follow implementation at ${implementation?.mdUrl || getDocMarkdownUrl('comments', commentType)}`,
277
+ },
278
+ ],
279
+ });
280
+
281
+ // Step 3: Add TODO comments to CLI-generated auth files
282
+ steps.push({
283
+ title: `Add TODO comments to CLI-generated authentication files`,
284
+ details: `The Velt CLI has generated authentication files in specific locations. Add TODO comments to these existing files (DO NOT create new files):
285
+
286
+ **1. User Hook: \`app/userAuth/useAppUser.tsx\`**
287
+ - This file already exists from CLI
288
+ - Add TODO: Connect to your existing authentication system
289
+ - Add TODO: Replace mock user data with actual user from your auth provider
290
+ - Examples: Next-auth useSession(), Clerk useUser(), Auth0, etc.
291
+
292
+ **2. Auth Provider: \`components/velt/VeltInitializeUser.tsx\`**
293
+ - This file already exists from CLI (references useAppUser)
294
+ - Contains \`useVeltAuthProvider\` hook
295
+ - File location may vary, look for useVeltAuthProvider hook
296
+
297
+ **3. Document Hook: Check for \`app/document/useCurrentDocument.tsx\`**
298
+ - May be in CLI-generated files or needs to be created
299
+ - Add TODO: Implement document identification logic
300
+ - Add TODO: Return unique document ID based on current page/route
301
+ - Examples: router.query.id, pathname, page slug
302
+
303
+ **4. Token API: \`app/api/velt/token/route.ts\`**
304
+ - This file already exists from CLI
305
+ - Add TODO: Connect to your backend authentication
306
+ - Add TODO: Validate user session before generating token
307
+
308
+ **IMPORTANT:** Only modify CLI-generated files. Do not create new files. Keep all Velt code in \`components/velt/\` and the specified locations.
309
+
310
+ **FOR TESTING PRESENCE/CURSORS:** Add logic to test with multiple users:
311
+ 1. Hardcode a fixed document ID (e.g., "demo-document") so all tabs use the same document
312
+ 2. Provide 2 hardcoded users (user-1 and user-2) with different names/avatars
313
+ 3. Allow switching users via URL parameter (?user=1 or ?user=2) to test presence/cursors
314
+ 4. Open multiple browser tabs with different user parameters to see live presence and cursors`,
315
+ codeExamples: [
316
+ {
317
+ description: 'Example: Hardcoded document ID and multiple users for testing',
318
+ language: 'typescript',
319
+ code: `// In useCurrentDocument.tsx - Hardcode document ID for testing:
320
+ export function useCurrentDocument() {
321
+ // [Velt] HARDCODED for testing presence/cursors
322
+ // TODO: Replace with dynamic document ID based on your routing
323
+ const documentId = "demo-document"; // Fixed ID so all tabs see same document
324
+
325
+ return { documentId, documentName: "Demo Document" };
326
+ }
327
+
328
+ // In useAppUser.tsx - Multiple users for testing:
329
+ export function useAppUser() {
330
+ // [Velt] HARDCODED USERS for testing presence/cursors
331
+ // TODO: Replace with actual user from your auth provider
332
+
333
+ // Get user from URL parameter (?user=1 or ?user=2)
334
+ const searchParams = typeof window !== 'undefined'
335
+ ? new URLSearchParams(window.location.search)
336
+ : null;
337
+ const userParam = searchParams?.get('user') || '1';
338
+
339
+ const users = {
340
+ '1': {
341
+ userId: "user-1",
342
+ name: "Demo User 1",
343
+ email: "user1@example.com",
344
+ photoUrl: "https://i.pravatar.cc/150?img=1",
345
+ organizationId: "demo-org",
346
+ },
347
+ '2': {
348
+ userId: "user-2",
349
+ name: "Demo User 2",
350
+ email: "user2@example.com",
351
+ photoUrl: "https://i.pravatar.cc/150?img=2",
352
+ organizationId: "demo-org",
353
+ }
354
+ };
355
+
356
+ const user = users[userParam] || users['1'];
357
+
358
+ return { user, isUserLoggedIn: true };
359
+ }
360
+
361
+ // TESTING INSTRUCTIONS:
362
+ // 1. Open http://localhost:3000?user=1 in one tab
363
+ // 2. Open http://localhost:3000?user=2 in another tab
364
+ // 3. You should see 2 different avatars in presence
365
+ // 4. Move mouse in one tab to see cursor in the other tab`,
366
+ },
367
+ {
368
+ description: 'Example TODO comments to add',
369
+ language: 'typescript',
370
+ code: `// In useAppUser.tsx:
371
+ // TODO: Connect to your authentication system
372
+ // TODO: Replace this mock user data with actual user from your auth provider
373
+ // Example: const user = useAuth(); // Your auth hook
374
+ // Example: const user = useSession(); // Next-auth
375
+ // Example: const user = useUser(); // Clerk, Auth0, etc.
376
+
377
+ // In useCurrentDocument.tsx:
378
+ // TODO: Implement document identification logic
379
+ // TODO: Return a unique document ID based on your app's routing
380
+ // Example: Use route params, URL, page ID, etc.
381
+ // Example: const documentId = router.query.id;
382
+ // Example: const documentId = \`page-\${pathname}\`;
383
+
384
+ // In app/api/velt/auth/route.ts:
385
+ // TODO: SECURITY - Validate user session before generating token
386
+ // TODO: Connect to your backend authentication
387
+ // TODO: Verify user is authenticated and authorized
388
+
389
+ // If JWT generation is present:
390
+ // TODO: SECURITY - Replace 'your-secret-key' with actual secret
391
+ // TODO: Store secret in environment variables (process.env.JWT_SECRET)
392
+ // TODO: NEVER commit secrets to version control
393
+ // TODO: Implement token expiration (expiresIn: '24h')`,
394
+ },
395
+ ],
396
+ });
397
+
398
+ // Step 4: Implement JWT token generation (Production Pattern)
399
+ steps.push({
400
+ title: `Implement JWT token generation via Velt API (Production Pattern)`,
401
+ details: `The CLI generates a placeholder JWT route. Replace it with the production pattern that calls Velt's token API.
402
+
403
+ **Production Pattern (FireHydrant Reference):**
404
+ Server-side API calls Velt's token endpoint to generate secure JWT tokens.
405
+
406
+ **Velt Token API:**
407
+ \`\`\`
408
+ POST https://api.velt.dev/v2/auth/token/get
409
+ Headers:
410
+ x-velt-api-key: YOUR_VELT_PUBLIC_API_KEY
411
+ x-velt-auth-token: YOUR_VELT_AUTH_TOKEN (keep secret!)
412
+ Body:
413
+ { "data": { "userId": "...", "userProperties": { "organizationId": "...", "email": "..." } } }
414
+ Response:
415
+ { "result": { "data": { "token": "eyJ..." } } }
416
+ \`\`\`
417
+
418
+ **Environment Variables to Set:**
419
+ \`\`\`
420
+ VELT_PUBLIC_API_KEY=your_api_key_here
421
+ VELT_AUTH_TOKEN=your_auth_token_here # NEVER expose to client!
422
+ \`\`\`
423
+
424
+ **Security Requirements:**
425
+ - Keep VELT_AUTH_TOKEN server-side only (never expose to client)
426
+ - Validate user session before generating tokens
427
+ - The auth token should only be used in API routes, not client components`,
428
+ codeExamples: [
429
+ {
430
+ description: 'Production API Route: app/api/velt/token/route.ts',
431
+ language: 'typescript',
432
+ code: `import { NextRequest, NextResponse } from 'next/server';
433
+
434
+ // [Velt] JWT Token Generation - Production Pattern
435
+ const VELT_API_KEY = process.env.VELT_PUBLIC_API_KEY;
436
+ const VELT_AUTH_TOKEN = process.env.VELT_AUTH_TOKEN;
437
+
438
+ export async function POST(request: NextRequest) {
439
+ try {
440
+ // [Velt] TODO: Add user session validation here
441
+ // const session = await getServerSession(authOptions);
442
+ // if (!session) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
443
+
444
+ const body = await request.json();
445
+ const { userId, organizationId, email } = body;
446
+
447
+ if (!userId) {
448
+ return NextResponse.json({ error: 'userId is required' }, { status: 400 });
449
+ }
450
+
451
+ if (!VELT_API_KEY || !VELT_AUTH_TOKEN) {
452
+ console.error('[Velt] Missing VELT_PUBLIC_API_KEY or VELT_AUTH_TOKEN');
453
+ return NextResponse.json({ error: 'Velt credentials not configured' }, { status: 500 });
454
+ }
455
+
456
+ // [Velt] Call Velt Token API
457
+ const response = await fetch('https://api.velt.dev/v2/auth/token/get', {
458
+ method: 'POST',
459
+ headers: {
460
+ 'Content-Type': 'application/json',
461
+ 'x-velt-api-key': VELT_API_KEY,
462
+ 'x-velt-auth-token': VELT_AUTH_TOKEN,
463
+ },
464
+ body: JSON.stringify({
465
+ data: {
466
+ userId,
467
+ userProperties: { organizationId: organizationId || 'default-org', email: email || '' },
468
+ },
469
+ }),
470
+ });
471
+
472
+ if (!response.ok) {
473
+ console.error('[Velt] Token API error:', await response.text());
474
+ return NextResponse.json({ error: 'Failed to generate token' }, { status: 500 });
475
+ }
476
+
477
+ const json = await response.json();
478
+ const token = json?.result?.data?.token;
479
+ if (!token) {
480
+ return NextResponse.json({ error: 'Invalid token response' }, { status: 500 });
481
+ }
482
+
483
+ return NextResponse.json({ token });
484
+ } catch (error) {
485
+ console.error('[Velt] Token generation error:', error);
486
+ return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
487
+ }
488
+ }`,
489
+ },
490
+ {
491
+ description: 'Client-side auth provider with backend token fetch',
492
+ language: 'typescript',
493
+ code: `// In VeltInitializeUser.tsx - useVeltAuthProvider hook
494
+ export function useVeltAuthProvider() {
495
+ const { user } = useAppUser();
496
+
497
+ // [Velt] Token generation - calls backend API
498
+ const generateToken = useCallback(async (): Promise<string> => {
499
+ const response = await fetch('/api/velt/token', {
500
+ method: 'POST',
501
+ headers: { 'Content-Type': 'application/json' },
502
+ body: JSON.stringify({
503
+ userId: user?.userId,
504
+ organizationId: user?.organizationId,
505
+ email: user?.email,
506
+ }),
507
+ });
508
+ if (!response.ok) throw new Error('Token fetch failed');
509
+ const data = await response.json();
510
+ return data.token;
511
+ }, [user]);
512
+
513
+ const authProvider: VeltAuthProvider | undefined = useMemo(() => {
514
+ if (!user?.userId) return undefined;
515
+ return {
516
+ user: { userId: user.userId, name: user.name, email: user.email, organizationId: user.organizationId },
517
+ generateToken,
518
+ retryConfig: { retryCount: 3, retryDelay: 1000 },
519
+ };
520
+ }, [user, generateToken]);
521
+
522
+ return { authProvider };
523
+ }`,
524
+ },
525
+ ],
526
+ });
527
+
528
+ // Step 5: Replace API key placeholders
529
+ steps.push({
530
+ title: `Replace API key with actual value`,
531
+ details: `Update all instances of "YOUR_VELT_API_KEY" and "YOUR_VELT_AUTH_TOKEN" with your actual values: ${apiKey}. Make sure to replace in VeltProvider configuration.`,
532
+ });
533
+
534
+ // Step 6: Test the installation
535
+ steps.push({
536
+ title: `Test the ${commentTypeTitle} comments functionality`,
537
+ details: `Start your development server and test ONLY the ${commentType} comments feature. ${getTestInstructions(commentType)} DO NOT test or implement other features.`,
538
+ });
539
+
540
+ // Step 7: Check dev console for errors
541
+ steps.push({
542
+ title: `Check browser console for Velt errors/warnings`,
543
+ details: `Open browser DevTools Console (Press F12 or Cmd+Option+I on Mac) and look for any Velt errors or warnings. Common errors include: "Please set document id to continue", "Velt API key not found", "Failed to authenticate user". If you find any errors, use the Velt Docs MCP to query for solutions. Example query: "How do I fix 'Please set document id to continue' error in Velt?"`,
544
+ });
545
+
546
+ // Additional information
547
+ const additionalInfo = [
548
+ {
549
+ title: '🚨 CRITICAL IMPLEMENTATION RULES',
550
+ content: `**WHAT TO USE FROM CLI:**
551
+ - ✅ Authentication setup (user identification)
552
+ - ✅ Document setup (document context)
553
+ - ✅ API key configuration
554
+
555
+ **PRIMARY SOURCE: Agent Skills (installed via \`npx skills add velt-js/agent-skills\`):**
556
+ - ✅ **velt-setup-best-practices** — VeltProvider setup, auth, document identity, project structure
557
+ - ✅ **velt-comments-best-practices** — ${commentType} comments implementation patterns and best practices
558
+ - Use skills as your FIRST reference for implementation details
559
+
560
+ **SECONDARY SOURCE: Docs URLs (only if skills don't cover it):**
561
+ - ${implementation?.mdUrl || getDocMarkdownUrl('comments', commentType)}
562
+
563
+ **TERTIARY SOURCE: Velt Docs MCP:**
564
+ - Only use for user follow-up questions AFTER implementation
565
+
566
+ **WHAT NOT TO IMPLEMENT:**
567
+ - ❌ VeltTools component
568
+ - ❌ ui-customization folder (unless user asks)
569
+ - ❌ Components user didn't request
570
+ - ❌ DO NOT copy code from CLI-generated files in components/velt/*`,
571
+ },
572
+ {
573
+ title: 'Additional Features Available',
574
+ content: `After completing the comments installation, you can add more Velt features:
575
+
576
+ **Presence (Live Users):**
577
+ - Setup: ${getDocMarkdownUrl('presence', null, 'setup')}
578
+ - Add \`<VeltPresence />\` to show online users with avatars
579
+ - Customize behavior: ${getDocMarkdownUrl('presence', null, 'customizeBehavior')}
580
+
581
+ **Cursors (Real-time Tracking):**
582
+ - Setup: ${getDocMarkdownUrl('cursors', null, 'setup')}
583
+ - Add \`<VeltCursor />\` to show live cursor positions
584
+ - Customize behavior: ${getDocMarkdownUrl('cursors', null, 'customizeBehavior')}
585
+
586
+ **Notifications:**
587
+ - Setup: ${getDocMarkdownUrl('notifications', null, 'setup')}
588
+ - Add \`<VeltNotificationsTool />\` for notification bell
589
+ - Customize behavior: ${getDocMarkdownUrl('notifications', null, 'customizeBehavior')}
590
+
591
+ **Recorder:**
592
+ - Setup: ${getDocMarkdownUrl('recorder', null, 'setup')}
593
+ - Add \`<VeltRecorder />\` for screen/audio recording
594
+ - Customize behavior: ${getDocMarkdownUrl('recorder', null, 'customizeBehavior')}`,
595
+ },
596
+ {
597
+ title: 'Post-Installation: Dev Tools & Troubleshooting',
598
+ content: `**Check Browser Console:**
599
+ After installation, open your browser DevTools Console (Press F12 or Cmd+Option+I on Mac):
600
+ 1. Look for Velt SDK messages (usually prefixed with "[Velt]")
601
+ 2. Check for warnings about configuration issues
602
+ 3. Verify API key is loaded correctly
603
+ 4. Watch for authentication errors or document ID issues
604
+
605
+ **Common Console Warnings:**
606
+ - "Velt API key not found" - Check that YOUR_VELT_API_KEY was replaced
607
+ - "Failed to authenticate user" - Verify auth token and user setup
608
+ - "Document ID missing" - Ensure document context is initialized
609
+
610
+ **Get Help:**
611
+ - For questions or issues AFTER installation, use the Velt Docs MCP server
612
+ - Query example: "How do I fix authentication errors in Velt?"
613
+ - The Velt Docs MCP has up-to-date solutions for common issues`,
614
+ },
615
+ {
616
+ title: 'Documentation Reference',
617
+ content: `**Primary: Agent Skills (installed via \`npx skills add velt-js/agent-skills\`)**
618
+ - **velt-setup-best-practices** — VeltProvider, auth, document identity, project structure
619
+ - **velt-comments-best-practices** — ${commentType} comments implementation patterns
620
+
621
+ **Secondary: Docs URLs**
622
+ - This feature: ${implementation?.mdUrl || getDocMarkdownUrl('comments', commentType)}
623
+ - Pattern: https://docs.velt.dev/[feature]/[page].md
624
+
625
+ **Tertiary: Velt Docs MCP**
626
+ After installation, query the Velt Docs MCP server for:
627
+ - Feature customization
628
+ - Troubleshooting
629
+ - Advanced configuration
630
+ - Integration patterns
631
+ Do NOT query during initial implementation — use Agent Skills first.`,
632
+ },
633
+ ];
634
+
635
+ // Generate skills source section
636
+ const skillsSection = formatSkillsSourceSection(['comments'], { commentType });
637
+
638
+ const basePlan = formatInstallationPlan({
639
+ title: `Plan for Velt ${commentTypeTitle} Comments Installation`,
640
+ steps,
641
+ additionalInfo,
642
+ });
643
+
644
+ // Insert skills section right after the title line
645
+ const titleEnd = basePlan.indexOf('\n\n') + 2;
646
+ return basePlan.slice(0, titleEnd) + skillsSection + basePlan.slice(titleEnd);
647
+ }
648
+
649
+ /**
650
+ * Gets CSS position styles for header positioning
651
+ */
652
+ function getPositionStyles(position) {
653
+ const styles = {
654
+ 'top-left': 'top: \'20px\',\n left: \'20px\',',
655
+ 'top-right': 'top: \'20px\',\n right: \'20px\',',
656
+ 'bottom-left': 'bottom: \'20px\',\n left: \'20px\',',
657
+ 'bottom-right': 'bottom: \'20px\',\n right: \'20px\',',
658
+ };
659
+ return styles[position] || styles['top-right'];
660
+ }
661
+
662
+ /**
663
+ * Gets test instructions for a comment type
664
+ */
665
+ function getTestInstructions(commentType) {
666
+ const instructions = {
667
+ freestyle: 'Click the Comment Tool button, then click anywhere on the page to add a comment.',
668
+ popover: 'Click the Comment Tool button next to an element to attach a comment to it.',
669
+ page: 'Open the Comments Sidebar and add a page-level comment at the bottom.',
670
+ stream: 'Select text to see comments appear in the stream column on the right.',
671
+ text: 'Highlight any text to see the Comment Tool button appear, then click it to add a comment.',
672
+ inline: 'Navigate to your content area and test adding inline comments.',
673
+ tiptap: 'Open your Tiptap editor, select text, and add comments using the Velt comment integration.',
674
+ lexical: 'Open your Lexical editor, select text, and add comments using the Velt comment integration.',
675
+ slate: 'Open your Slate.js editor, select text, and add comments using the Velt comment integration.',
676
+ };
677
+ return instructions[commentType] || 'Test adding comments in your application.';
678
+ }
679
+
680
+ /**
681
+ * Creates a comprehensive plan for multiple Velt features
682
+ *
683
+ * @param {Object} options - Installation options
684
+ * @param {string[]} options.features - Features to install (comments, presence, cursors, notifications, recorder, crdt)
685
+ * @param {string} options.commentType - Type of comments (if comments feature is included)
686
+ * @param {string} options.crdtEditorType - CRDT editor type (tiptap, codemirror, blocknote)
687
+ * @param {Object} options.implementation - Comment implementation details from Velt Docs
688
+ * @param {Object} options.crdtImplementation - CRDT implementation details from Velt Docs
689
+ * @param {Object} options.featureImplementations - Other feature implementations from Velt Docs
690
+ * @param {Array} options.detectedFiles - Files detected for modification
691
+ * @param {string} options.apiKey - API key preview
692
+ * @param {string} options.headerPosition - Header position
693
+ * @param {string} options.veltProviderLocation - Where to install VeltProvider
694
+ * @returns {string} Formatted installation plan
695
+ */
696
+ export function createMultiFeaturePlan(options) {
697
+ const {
698
+ features = [],
699
+ commentType,
700
+ crdtEditorType,
701
+ implementation,
702
+ crdtImplementation,
703
+ featureImplementations = {},
704
+ detectedFiles = [],
705
+ apiKey,
706
+ headerPosition,
707
+ veltProviderLocation = 'app/page.tsx',
708
+ } = options;
709
+
710
+ const steps = [];
711
+ const hasComments = features.includes('comments');
712
+ const hasPresence = features.includes('presence');
713
+ const hasCursors = features.includes('cursors');
714
+ const hasNotifications = features.includes('notifications');
715
+ const hasRecorder = features.includes('recorder');
716
+ const hasCRDT = features.includes('crdt');
717
+
718
+ const featureList = [];
719
+ if (hasComments) featureList.push(`${commentType.charAt(0).toUpperCase() + commentType.slice(1)} Comments`);
720
+ if (hasPresence) featureList.push('Presence');
721
+ if (hasCursors) featureList.push('Cursors');
722
+ if (hasNotifications) featureList.push('Notifications');
723
+ if (hasRecorder) featureList.push('Recorder');
724
+ if (hasCRDT) featureList.push(`CRDT (${crdtEditorType ? crdtEditorType.charAt(0).toUpperCase() + crdtEditorType.slice(1) : 'Collaborative Editing'})`);
725
+
726
+ // Add warning about only implementing requested features
727
+ steps.push({
728
+ title: `⚠️ CRITICAL: Only implement ${featureList.join(', ')}`,
729
+ details: `You are ONLY installing: ${featureList.join(', ')}. DO NOT implement any other components unless the user specifically requested them. Only use authentication, user setup, and document setup from CLI. Get implementation details from markdown URLs at docs.velt.dev.`,
730
+ });
731
+
732
+ // Step 2: Add VeltProvider
733
+ const componentsToAdd = [];
734
+ if (hasComments) componentsToAdd.push('VeltComments');
735
+ if (hasPresence) componentsToAdd.push('VeltPresence');
736
+ if (hasCursors) componentsToAdd.push('VeltCursor');
737
+ if (hasNotifications) componentsToAdd.push('VeltNotificationsTool');
738
+ if (hasRecorder) componentsToAdd.push('VeltRecorder');
739
+
740
+ const locationText = veltProviderLocation === 'auto-detect'
741
+ ? 'the appropriate layout file (analyze the project structure to determine the best location)'
742
+ : veltProviderLocation;
743
+
744
+ steps.push({
745
+ title: `Import and use CLI-generated Velt components in ${locationText}`,
746
+ details: `The Velt CLI has generated the necessary component files in \`components/velt/\`. DO NOT create new files. Use the existing files:
747
+
748
+ **CLI-Generated Files:**
749
+ - \`components/velt/VeltInitializeUser.tsx\` - Exports \`useVeltAuthProvider\` hook (NOT a wrapper component)
750
+ - \`components/velt/VeltInitializeDocument.tsx\` - Exports \`useCurrentDocument\` hook for document context
751
+ - \`components/velt/VeltCollaboration.tsx\` - Velt feature components (comments, presence, etc.)
752
+ - \`app/userAuth/AppUserContext.tsx\` - User context provider wrapper
753
+ - \`app/userAuth/useAppUser.tsx\` - User data hook (add TODOs here)
754
+ - \`app/api/velt/token/route.ts\` - Token generation API
755
+
756
+ ⚠️ **CRITICAL ARCHITECTURE (Sample App Pattern):**
757
+
758
+ **1. app/layout.tsx - Wrap with AppUserProvider:**
759
+ \`\`\`tsx
760
+ import { AppUserProvider } from './userAuth/AppUserContext'
761
+
762
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
763
+ return (
764
+ <html lang="en">
765
+ <body>
766
+ <AppUserProvider>
767
+ {children}
768
+ </AppUserProvider>
769
+ </body>
770
+ </html>
771
+ )
772
+ }
773
+ \`\`\`
774
+ **Why**: AppUserProvider provides user context to all components including Velt auth.
775
+
776
+ **2. app/page.tsx (or root page) - VeltProvider with authProvider hook:**
777
+ \`\`\`tsx
778
+ "use client";
779
+ import { VeltProvider } from '@veltdev/react';
780
+ import { useVeltAuthProvider } from '@/components/velt/VeltInitializeUser';
781
+ import { useCurrentDocument } from '@/components/velt/VeltInitializeDocument';
782
+ import { VeltCollaboration } from '@/components/velt/VeltCollaboration';
783
+
784
+ const VELT_API_KEY = process.env.NEXT_PUBLIC_VELT_API_KEY!;
785
+
786
+ export default function Page() {
787
+ const { authProvider } = useVeltAuthProvider();
788
+ const { documentId } = useCurrentDocument();
789
+
790
+ return (
791
+ <VeltProvider apiKey={VELT_API_KEY} authProvider={authProvider}>
792
+ <VeltCollaboration documentId={documentId} />
793
+ {/* Your page content */}
794
+ </VeltProvider>
795
+ );
796
+ }
797
+ \`\`\`
798
+ **Why**: VeltProvider must be in page.tsx with authProvider from hook, NOT nested wrapper components.
799
+
800
+ **What to do:**
801
+ 1. Add AppUserProvider wrapper to app/layout.tsx
802
+ 2. Add VeltProvider to ${locationText} using useVeltAuthProvider hook
803
+ 3. Import VeltCollaboration for feature components
804
+ 4. Follow the markdown documentation for feature-specific implementation
805
+
806
+ ⚠️ **CRITICAL: Position ALL Velt Components in User's Chosen Corner:**
807
+ All Velt feature components (presence, notifications, comments sidebar, etc.) should be placed in the SAME corner that the user specified. This creates a consistent, grouped UI.
808
+
809
+ **Position based on user's chosen corner:**
810
+ - **top-left**: \`fixed top-4 left-4\`
811
+ - **top-right**: \`fixed top-4 right-4\`
812
+ - **bottom-left**: \`fixed bottom-4 left-4\`
813
+ - **bottom-right**: \`fixed bottom-4 right-4\`
814
+
815
+ \`\`\`tsx
816
+ // VeltCollaboration.tsx - Place ALL Velt components in the chosen corner
817
+ export function VeltCollaboration({ documentId }: { documentId: string }) {
818
+ return (
819
+ <>
820
+ {/* [Velt] ALL features grouped in user's chosen corner */}
821
+ <div className="fixed [POSITION] z-50 flex flex-col gap-2">
822
+ {/* Presence avatars */}
823
+ <VeltPresence flockMode={false} maxUsers={5} />
824
+
825
+ {/* Notifications bell */}
826
+ <VeltNotificationsTool />
827
+
828
+ {/* Comments sidebar trigger (if using comments) */}
829
+ <VeltCommentsSidebar />
830
+ </div>
831
+
832
+ {/* Cursors render across the whole page */}
833
+ <VeltCursor />
834
+
835
+ {/* Comments tool for freestyle comments */}
836
+ <VeltCommentTool />
837
+ </>
838
+ );
839
+ }
840
+ \`\`\`
841
+
842
+ **Replace [POSITION] with user's choice:**
843
+ - top-left → \`top-4 left-4\`
844
+ - top-right → \`top-4 right-4\`
845
+ - bottom-left → \`bottom-4 left-4\`
846
+ - bottom-right → \`bottom-4 right-4\`
847
+
848
+ **Why**: Grouping all Velt features in one corner creates a clean, consistent UI. Without positioned containers, Velt components create a white bar at the top of the page.
849
+
850
+ Also add to globals.css or VeltCustomization.css:
851
+ \`\`\`css
852
+ /* Remove default white background from Velt components */
853
+ velt-presence-container,
854
+ velt-presence-container *,
855
+ velt-notifications-tool-container,
856
+ velt-notifications-tool-container *,
857
+ velt-comments-sidebar-container,
858
+ velt-comments-sidebar-container * {
859
+ background: transparent !important;
860
+ }
861
+ \`\`\`
862
+
863
+ **CRITICAL - For Tiptap/Lexical/Slate Comments:**
864
+ - ✅ **FIND EXISTING EDITOR** - Search the project for existing Tiptap/Lexical/Slate editor components
865
+ - ✅ **INTEGRATE INTO EXISTING EDITOR** - Add Velt comments to the existing editor, DO NOT create a new editor
866
+ - ✅ **USE BUBBLE MENU PATTERN** - Add comment button to bubble menu (appears on text selection), NOT a fixed toolbar
867
+ - ✅ Use ONLY the comment-specific package: @veltdev/tiptap-velt-comments, @veltdev/lexical-velt-comments, or @veltdev/slate-velt-comments
868
+ - ❌ DO NOT create a new editor component if one already exists
869
+ - ❌ DO NOT create a fixed toolbar with Bold/Italic/Comment buttons
870
+ - ❌ DO NOT use CRDT packages (@veltdev/tiptap-velt-collaboration or similar)
871
+ - ❌ DO NOT implement real-time collaborative editing - only comments on the editor
872
+
873
+ **Tiptap Pattern:**
874
+ \`\`\`tsx
875
+ import { BubbleMenu } from '@tiptap/react'
876
+ import { TiptapVeltComments, addComment, renderComments } from '@veltdev/tiptap-velt-comments'
877
+ import { useCommentAnnotations } from '@veltdev/react'
878
+
879
+ // Add to editor extensions: TiptapVeltComments
880
+ // Use BubbleMenu component with comment button
881
+ <BubbleMenu editor={editor}>
882
+ <button onClick={() => addComment({ editor })}>💬 Comment</button>
883
+ </BubbleMenu>
884
+ \`\`\`
885
+
886
+ **Lexical Pattern:**
887
+ - Add VeltCommentsPlugin to editor plugins
888
+ - Use custom bubble menu that appears on selection
889
+ - Trigger addComment() from bubble menu button
890
+
891
+ **Slate Pattern:**
892
+ - Wrap editor with withVeltComments()
893
+ - Implement bubble menu with position tracking on selection
894
+ - Trigger addComment({ editor }) from bubble menu button
895
+
896
+ **If NO existing editor found:** Provide minimal integration example with bubble menu, but recommend user add to their existing editor.
897
+
898
+ **IMPORTANT:** All Velt-related files should remain in \`components/velt/\`. Do not create new Velt files outside this folder.
899
+
900
+ ${hasCRDT && crdtEditorType ? `
901
+ **CRITICAL - For ${crdtEditorType.charAt(0).toUpperCase() + crdtEditorType.slice(1)} CRDT (Collaborative Real-Time Document Editing):**
902
+ ${crdtEditorType === 'tiptap' ? `- ✅ **Required Packages:**
903
+ - @veltdev/tiptap-crdt-react (exact version: 4.5.8)
904
+ - @veltdev/tiptap-crdt (exact version: 4.5.8)
905
+ - @tiptap/y-tiptap (for Yjs integration)
906
+ - yjs (CRDT framework)
907
+ - y-prosemirror (ProseMirror bindings for Yjs)
908
+
909
+ ⚠️ **CRITICAL: ID MAPPING PATTERN (FireHydrant Pattern):**
910
+ - **documentId**: Set via VeltInitializeDocument - one per page/resource (e.g., \`retrospective-123\`)
911
+ - **editorId**: Unique per editor instance - use format \`\${documentId}/\${fieldId}\` (e.g., \`retrospective-123/question-456\`)
912
+ - **Why**: This allows multiple editors per document, each with independent CRDT state
913
+
914
+ ⚠️ **CRITICAL STARTERKIT CONFIGURATION:**
915
+ - ❌ **WRONG**: \`StarterKit.configure({ history: false })\` - DO NOT USE "history"
916
+ - ✅ **CORRECT**: \`StarterKit.configure({ undoRedo: false })\`
917
+ - **Why**: StarterKit doesn't have a "history" option. Use "undoRedo" instead. CRDT handles undo/redo.
918
+
919
+ ⚠️ **CRITICAL INITIAL CONTENT:**
920
+ - ❌ **WRONG**: \`content: initialContent\` in useEditor
921
+ - ✅ **CORRECT**: \`// content: initialContent\` (comment it out)
922
+ - **Why**: Let CRDT handle initial content loading. Seed from backend only when CRDT doc is empty.
923
+
924
+ ⚠️ **CRITICAL AUTO-SAVE PATTERN (FireHydrant Pattern):**
925
+ - ✅ Use 2-second debounce to avoid excessive backend saves
926
+ - ✅ Detect remote syncs: check \`transaction.getMeta('y-sync$')\`, \`transaction.getMeta('remote')\`, \`transaction.getMeta('velt-sync')\`, \`transaction.getMeta('isRemote')\`
927
+ - ✅ Skip saving for remote syncs (these are changes from other users)
928
+ - ✅ Only save local changes to backend
929
+
930
+ ⚠️ **CRITICAL BACKEND CONTENT SEEDING:**
931
+ - ✅ When CRDT doc is empty AND backend has content, seed once
932
+ - ✅ Use \`useServerConnectionStateChangeHandler()\` to check connection is 'online' before seeding
933
+ - ✅ Track seeding state with ref to avoid double-seeding
934
+
935
+ ⚠️ **CRITICAL COMMENTS EXTENSION (if adding comments to CRDT editor):**
936
+ - ❌ **WRONG**: \`TiptapVeltComments.configure({ editorId, HTMLAttributes })\`
937
+ - ✅ **CORRECT**: \`TiptapVeltComments\` (no .configure())
938
+ - **Why**: The extension works without configuration
939
+
940
+ **Tiptap CRDT Pattern (PRODUCTION CODE - FireHydrant Pattern):**
941
+ \`\`\`tsx
942
+ import { useEffect, useRef, useMemo } from 'react';
943
+ import { useEditor, EditorContent } from '@tiptap/react';
944
+ import StarterKit from '@tiptap/starter-kit';
945
+ import { useVeltTiptapCrdtExtension } from '@veltdev/tiptap-crdt-react';
946
+ import { useServerConnectionStateChangeHandler } from '@veltdev/react';
947
+
948
+ interface TipTapCollabEditorProps {
949
+ documentId: string; // From VeltInitializeDocument context
950
+ fieldId: string; // Unique field ID within document
951
+ backendfallbackContent?: any; // Backend content for seeding
952
+ onUpdate?: (params: { fieldId: string; value: any }) => void;
953
+ }
954
+
955
+ export function TipTapCollabEditor({
956
+ documentId,
957
+ fieldId,
958
+ backendfallbackContent,
959
+ onUpdate,
960
+ }: TipTapCollabEditorProps) {
961
+ const saveTimeoutRef = useRef<NodeJS.Timeout | null>(null);
962
+ const hasSeededContentRef = useRef(false);
963
+ const isEditorReadyRef = useRef(false);
964
+
965
+ // [Velt] CRITICAL: Combine documentId and fieldId for unique editorId
966
+ const editorId = \`\${documentId}/\${fieldId}\`;
967
+
968
+ // [Velt] Format initial content for CRDT
969
+ const veltInitialContent = useMemo(() => {
970
+ if (!backendfallbackContent) return undefined;
971
+ if (Array.isArray(backendfallbackContent)) {
972
+ return { type: 'doc', content: backendfallbackContent };
973
+ }
974
+ return backendfallbackContent;
975
+ }, [backendfallbackContent]);
976
+
977
+ // [Velt] Initialize CRDT extension with unique editorId
978
+ const { VeltCrdt, isLoading } = useVeltTiptapCrdtExtension({
979
+ editorId,
980
+ initialContent: veltInitialContent,
981
+ });
982
+
983
+ // [Velt] Monitor server connection state
984
+ const serverConnectionState = useServerConnectionStateChangeHandler();
985
+
986
+ const editor = useEditor({
987
+ extensions: [
988
+ StarterKit.configure({
989
+ undoRedo: false, // CRITICAL: CRDT handles undo/redo
990
+ }),
991
+ ...(VeltCrdt ? [VeltCrdt] : []),
992
+ ],
993
+ // content: initialContent, // CRITICAL: comment out - CRDT manages content
994
+ immediatelyRender: false,
995
+ onUpdate: ({ editor, transaction }) => {
996
+ // [Velt] CRITICAL: Detect remote syncs - skip saving these
997
+ const isRemoteSync =
998
+ transaction.getMeta('y-sync$') ||
999
+ transaction.getMeta('remote') ||
1000
+ transaction.getMeta('velt-sync') ||
1001
+ transaction.getMeta('isRemote');
1002
+ if (isRemoteSync) return;
1003
+
1004
+ // [Velt] Debounced auto-save (2 seconds)
1005
+ if (saveTimeoutRef.current) clearTimeout(saveTimeoutRef.current);
1006
+ if (transaction.docChanged && isEditorReadyRef.current) {
1007
+ saveTimeoutRef.current = setTimeout(() => {
1008
+ const content = editor.getJSON()?.content || [];
1009
+ onUpdate?.({ fieldId, value: content });
1010
+ }, 2000);
1011
+ }
1012
+ },
1013
+ }, [VeltCrdt]);
1014
+
1015
+ // [Velt] Seed from backend when CRDT doc is empty
1016
+ useEffect(() => {
1017
+ if (
1018
+ editor && !isLoading &&
1019
+ serverConnectionState === 'online' &&
1020
+ !hasSeededContentRef.current &&
1021
+ isEditorEmpty(editor) &&
1022
+ hasBackendContent(backendfallbackContent)
1023
+ ) {
1024
+ hasSeededContentRef.current = true;
1025
+ setTimeout(() => {
1026
+ if (editor && !editor.isDestroyed) {
1027
+ editor.commands.setContent(backendfallbackContent);
1028
+ isEditorReadyRef.current = true;
1029
+ }
1030
+ }, 100);
1031
+ } else if (editor && !isLoading && serverConnectionState === 'online') {
1032
+ isEditorReadyRef.current = true;
1033
+ }
1034
+ }, [editor, isLoading, serverConnectionState, backendfallbackContent]);
1035
+
1036
+ if (isLoading) return <div>Loading...</div>;
1037
+ return <EditorContent editor={editor} />;
1038
+ }
1039
+
1040
+ // Helper functions
1041
+ const isEditorEmpty = (editor) => {
1042
+ const json = editor?.getJSON();
1043
+ if (!json?.content?.length) return true;
1044
+ if (json.content.length === 1 && json.content[0].type === 'paragraph' && !json.content[0].content?.length) return true;
1045
+ return false;
1046
+ };
1047
+ const hasBackendContent = (content) => content && !(Array.isArray(content) && content.length === 0);
1048
+ \`\`\`
1049
+ ` : ''}${crdtEditorType === 'codemirror' ? `- ✅ Package: @veltdev/codemirror-crdt-react
1050
+ - ✅ Hook: useVeltCodeMirrorCrdtExtension({ editorId, initialContent })
1051
+ - ✅ Returns: { store, isLoading }
1052
+ - ✅ Use store.getYText(), store.getAwareness(), store.getUndoManager()
1053
+ - ✅ Requires y-codemirror.next package for yCollab
1054
+
1055
+ **CodeMirror CRDT Pattern:**
1056
+ \`\`\`tsx
1057
+ import { useVeltCodeMirrorCrdtExtension } from '@veltdev/codemirror-crdt-react'
1058
+ import { yCollab } from 'y-codemirror.next'
1059
+ import { EditorState } from '@codemirror/state'
1060
+ import { EditorView } from 'codemirror'
1061
+
1062
+ const { store, isLoading } = useVeltCodeMirrorCrdtExtension({
1063
+ editorId: 'codemirror-editor-1',
1064
+ initialContent: yourInitialContent
1065
+ })
1066
+
1067
+ // In useEffect:
1068
+ const startState = EditorState.create({
1069
+ doc: store.getYText()?.toString() ?? '',
1070
+ extensions: [
1071
+ // ... other extensions
1072
+ yCollab(store.getYText()!, store.getAwareness(), {
1073
+ undoManager: store.getUndoManager()
1074
+ }),
1075
+ ],
1076
+ })
1077
+
1078
+ const view = new EditorView({ state: startState, parent: editorRef.current })
1079
+ \`\`\`
1080
+ ` : ''}${crdtEditorType === 'blocknote' ? `- ✅ Package: @veltdev/blocknote-crdt-react
1081
+ - ✅ Hook: useVeltBlockNoteCrdtExtension({ editorId, initialContent })
1082
+ - ✅ Returns: { collaborationConfig, isLoading }
1083
+ - ✅ Pass collaborationConfig to useCreateBlockNote
1084
+ - ✅ BlockNote handles CRDT automatically with the config
1085
+
1086
+ **BlockNote CRDT Pattern:**
1087
+ \`\`\`tsx
1088
+ import { useVeltBlockNoteCrdtExtension } from '@veltdev/blocknote-crdt-react'
1089
+ import { useCreateBlockNote } from '@blocknote/react'
1090
+ import { BlockNoteView } from '@blocknote/mantine'
1091
+
1092
+ const { collaborationConfig, isLoading } = useVeltBlockNoteCrdtExtension({
1093
+ editorId: 'blocknote-editor-1',
1094
+ initialContent: JSON.stringify([{ type: "paragraph", content: "" }])
1095
+ })
1096
+
1097
+ const editor = useCreateBlockNote({
1098
+ collaboration: collaborationConfig,
1099
+ }, [collaborationConfig])
1100
+
1101
+ return <BlockNoteView editor={editor} />
1102
+ \`\`\`
1103
+ ` : ''}
1104
+ ` : ''}
1105
+ **Get implementation details from markdown docs:**
1106
+ ${hasComments ? `- Comments (${commentType}): ${implementation?.mdUrl || getDocMarkdownUrl('comments', commentType)}${implementation?.source ? ` (fetched from ${implementation.source})` : ''}\n` : ''}${hasPresence ? `- Presence: ${featureImplementations.presence?.mdUrl || getDocMarkdownUrl('presence')}${featureImplementations.presence?.source ? ` (fetched from ${featureImplementations.presence.source})` : ''}\n` : ''}${hasCursors ? `- Cursors: ${featureImplementations.cursors?.mdUrl || getDocMarkdownUrl('cursors')}${featureImplementations.cursors?.source ? ` (fetched from ${featureImplementations.cursors.source})` : ''}\n` : ''}${hasNotifications ? `- Notifications: ${featureImplementations.notifications?.mdUrl || getDocMarkdownUrl('notifications')}${featureImplementations.notifications?.source ? ` (fetched from ${featureImplementations.notifications.source})` : ''}\n` : ''}${hasRecorder ? `- Recorder: ${featureImplementations.recorder?.mdUrl || getDocMarkdownUrl('recorder')}${featureImplementations.recorder?.source ? ` (fetched from ${featureImplementations.recorder.source})` : ''}\n` : ''}${hasCRDT && crdtEditorType ? `- CRDT (${crdtEditorType}): ${crdtImplementation?.mdUrl || getDocMarkdownUrl('crdt', crdtEditorType)}${crdtImplementation?.source ? ` (fetched from ${crdtImplementation.source})` : ''}\n` : ''}`,
1107
+ codeExamples: [
1108
+ {
1109
+ description: `Correct pattern: VeltProvider in page.tsx with authProvider hook`,
1110
+ language: 'tsx',
1111
+ code: `// app/page.tsx (or your root page component)
1112
+ "use client";
1113
+ import { VeltProvider } from '@veltdev/react';
1114
+ import { useVeltAuthProvider } from '@/components/velt/VeltInitializeUser';
1115
+ import { useCurrentDocument } from '@/components/velt/VeltInitializeDocument';
1116
+ import { VeltCollaboration } from '@/components/velt/VeltCollaboration';
1117
+
1118
+ const VELT_API_KEY = process.env.NEXT_PUBLIC_VELT_API_KEY!;
1119
+
1120
+ export default function Page() {
1121
+ const { authProvider } = useVeltAuthProvider();
1122
+ const { documentId } = useCurrentDocument();
1123
+
1124
+ return (
1125
+ <VeltProvider apiKey={VELT_API_KEY} authProvider={authProvider}>
1126
+ <VeltCollaboration documentId={documentId} />
1127
+ {/* Your page content here */}
1128
+ </VeltProvider>
1129
+ );
1130
+ }
1131
+
1132
+ // Get component implementations from markdown docs above`,
1133
+ },
1134
+ ],
1135
+ });
1136
+
1137
+ // Step 3: Set up authentication and user identification with TODOs
1138
+ steps.push({
1139
+ title: `Add TODO comments to CLI-generated authentication files`,
1140
+ details: `The Velt CLI has generated authentication files in specific locations. Add TODO comments to these existing files (DO NOT create new files):
1141
+
1142
+ **1. User Hook: \`app/userAuth/useAppUser.tsx\`**
1143
+ - This file already exists from CLI
1144
+ - Add TODO: Connect to your existing authentication system
1145
+ - Add TODO: Replace mock user data with actual user from your auth provider
1146
+ - Examples: Next-auth useSession(), Clerk useUser(), Auth0, etc.
1147
+
1148
+ **2. Auth Provider: \`components/velt/VeltInitializeUser.tsx\`**
1149
+ - This file already exists from CLI (references useAppUser)
1150
+ - Contains \`useVeltAuthProvider\` hook
1151
+ - File location may vary, look for useVeltAuthProvider hook
1152
+
1153
+ **3. Document Hook: Check for \`app/document/useCurrentDocument.tsx\`**
1154
+ - May be in CLI-generated files or needs to be created
1155
+ - Add TODO: Implement document identification logic
1156
+ - Add TODO: Return unique document ID based on current page/route
1157
+ - Examples: router.query.id, pathname, page slug
1158
+
1159
+ **4. Token API: \`app/api/velt/token/route.ts\`**
1160
+ - This file already exists from CLI
1161
+ - Add TODO: Connect to your backend authentication
1162
+ - Add TODO: Validate user session before generating token
1163
+
1164
+ **IMPORTANT:** Only modify CLI-generated files. Do not create new files. Keep all Velt code in \`components/velt/\` and the specified locations.
1165
+
1166
+ **FOR TESTING PRESENCE/CURSORS:** Add logic to test with multiple users:
1167
+ 1. Hardcode a fixed document ID (e.g., "demo-document") so all tabs use the same document
1168
+ 2. Provide 2 hardcoded users (user-1 and user-2) with different names/avatars
1169
+ 3. Allow switching users via URL parameter (?user=1 or ?user=2) to test presence/cursors
1170
+ 4. Open multiple browser tabs with different user parameters to see live presence and cursors`,
1171
+ codeExamples: [
1172
+ {
1173
+ description: 'Example: Hardcoded document ID and multiple users for testing',
1174
+ language: 'typescript',
1175
+ code: `// In useCurrentDocument.tsx - Hardcode document ID for testing:
1176
+ export function useCurrentDocument() {
1177
+ // [Velt] HARDCODED for testing presence/cursors
1178
+ // TODO: Replace with dynamic document ID based on your routing
1179
+ const documentId = "demo-document"; // Fixed ID so all tabs see same document
1180
+
1181
+ return { documentId, documentName: "Demo Document" };
1182
+ }
1183
+
1184
+ // In useAppUser.tsx - Multiple users for testing:
1185
+ export function useAppUser() {
1186
+ // [Velt] HARDCODED USERS for testing presence/cursors
1187
+ // TODO: Replace with actual user from your auth provider
1188
+
1189
+ // Get user from URL parameter (?user=1 or ?user=2)
1190
+ const searchParams = typeof window !== 'undefined'
1191
+ ? new URLSearchParams(window.location.search)
1192
+ : null;
1193
+ const userParam = searchParams?.get('user') || '1';
1194
+
1195
+ const users = {
1196
+ '1': {
1197
+ userId: "user-1",
1198
+ name: "Demo User 1",
1199
+ email: "user1@example.com",
1200
+ photoUrl: "https://i.pravatar.cc/150?img=1",
1201
+ organizationId: "demo-org",
1202
+ },
1203
+ '2': {
1204
+ userId: "user-2",
1205
+ name: "Demo User 2",
1206
+ email: "user2@example.com",
1207
+ photoUrl: "https://i.pravatar.cc/150?img=2",
1208
+ organizationId: "demo-org",
1209
+ }
1210
+ };
1211
+
1212
+ const user = users[userParam] || users['1'];
1213
+
1214
+ return { user, isUserLoggedIn: true };
1215
+ }
1216
+
1217
+ // TESTING INSTRUCTIONS:
1218
+ // 1. Open http://localhost:3000?user=1 in one tab
1219
+ // 2. Open http://localhost:3000?user=2 in another tab
1220
+ // 3. You should see 2 different avatars in presence
1221
+ // 4. Move mouse in one tab to see cursor in the other tab`,
1222
+ },
1223
+ {
1224
+ description: 'Example TODO comments to add',
1225
+ language: 'typescript',
1226
+ code: `// In useAppUser.tsx:
1227
+ // TODO: Connect to your authentication system
1228
+ // TODO: Replace this mock user data with actual user from your auth provider
1229
+ // Example: const user = useAuth(); // Your auth hook
1230
+ // Example: const user = useSession(); // Next-auth
1231
+ // Example: const user = useUser(); // Clerk, Auth0, etc.
1232
+
1233
+ // In useCurrentDocument.tsx:
1234
+ // TODO: Implement document identification logic
1235
+ // TODO: Return a unique document ID based on your app's routing
1236
+ // Example: Use route params, URL, page ID, etc.
1237
+ // Example: const documentId = router.query.id;
1238
+ // Example: const documentId = \`page-\${pathname}\`;
1239
+
1240
+ // In app/api/velt/auth/route.ts:
1241
+ // TODO: SECURITY - Validate user session before generating token
1242
+ // TODO: Connect to your backend authentication
1243
+ // TODO: Verify user is authenticated and authorized
1244
+
1245
+ // If JWT generation is present:
1246
+ // TODO: SECURITY - Replace 'your-secret-key' with actual secret
1247
+ // TODO: Store secret in environment variables (process.env.JWT_SECRET)
1248
+ // TODO: NEVER commit secrets to version control
1249
+ // TODO: Implement token expiration (expiresIn: '24h')`,
1250
+ },
1251
+ ],
1252
+ });
1253
+
1254
+ // Step 4: Implement JWT token generation (Production Pattern)
1255
+ steps.push({
1256
+ title: `Implement JWT token generation via Velt API (Production Pattern)`,
1257
+ details: `The CLI generates a placeholder JWT route. Replace it with the production pattern that calls Velt's token API.
1258
+
1259
+ **Production Pattern (FireHydrant Reference):**
1260
+ Server-side API calls Velt's token endpoint to generate secure JWT tokens.
1261
+
1262
+ **Velt Token API:**
1263
+ \`\`\`
1264
+ POST https://api.velt.dev/v2/auth/token/get
1265
+ Headers:
1266
+ x-velt-api-key: YOUR_VELT_PUBLIC_API_KEY
1267
+ x-velt-auth-token: YOUR_VELT_AUTH_TOKEN (keep secret!)
1268
+ Body:
1269
+ { "data": { "userId": "...", "userProperties": { "organizationId": "...", "email": "..." } } }
1270
+ Response:
1271
+ { "result": { "data": { "token": "eyJ..." } } }
1272
+ \`\`\`
1273
+
1274
+ **Environment Variables to Set:**
1275
+ \`\`\`
1276
+ VELT_PUBLIC_API_KEY=your_api_key_here
1277
+ VELT_AUTH_TOKEN=your_auth_token_here # NEVER expose to client!
1278
+ \`\`\`
1279
+
1280
+ **Security Requirements:**
1281
+ - Keep VELT_AUTH_TOKEN server-side only (never expose to client)
1282
+ - Validate user session before generating tokens
1283
+ - The auth token should only be used in API routes, not client components`,
1284
+ codeExamples: [
1285
+ {
1286
+ description: 'Production API Route: app/api/velt/token/route.ts',
1287
+ language: 'typescript',
1288
+ code: `import { NextRequest, NextResponse } from 'next/server';
1289
+
1290
+ // [Velt] JWT Token Generation - Production Pattern
1291
+ const VELT_API_KEY = process.env.VELT_PUBLIC_API_KEY;
1292
+ const VELT_AUTH_TOKEN = process.env.VELT_AUTH_TOKEN;
1293
+
1294
+ export async function POST(request: NextRequest) {
1295
+ try {
1296
+ // [Velt] TODO: Add user session validation here
1297
+ // const session = await getServerSession(authOptions);
1298
+ // if (!session) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
1299
+
1300
+ const body = await request.json();
1301
+ const { userId, organizationId, email } = body;
1302
+
1303
+ if (!userId) {
1304
+ return NextResponse.json({ error: 'userId is required' }, { status: 400 });
1305
+ }
1306
+
1307
+ if (!VELT_API_KEY || !VELT_AUTH_TOKEN) {
1308
+ console.error('[Velt] Missing VELT_PUBLIC_API_KEY or VELT_AUTH_TOKEN');
1309
+ return NextResponse.json({ error: 'Velt credentials not configured' }, { status: 500 });
1310
+ }
1311
+
1312
+ // [Velt] Call Velt Token API
1313
+ const response = await fetch('https://api.velt.dev/v2/auth/token/get', {
1314
+ method: 'POST',
1315
+ headers: {
1316
+ 'Content-Type': 'application/json',
1317
+ 'x-velt-api-key': VELT_API_KEY,
1318
+ 'x-velt-auth-token': VELT_AUTH_TOKEN,
1319
+ },
1320
+ body: JSON.stringify({
1321
+ data: {
1322
+ userId,
1323
+ userProperties: { organizationId: organizationId || 'default-org', email: email || '' },
1324
+ },
1325
+ }),
1326
+ });
1327
+
1328
+ if (!response.ok) {
1329
+ console.error('[Velt] Token API error:', await response.text());
1330
+ return NextResponse.json({ error: 'Failed to generate token' }, { status: 500 });
1331
+ }
1332
+
1333
+ const json = await response.json();
1334
+ const token = json?.result?.data?.token;
1335
+ if (!token) {
1336
+ return NextResponse.json({ error: 'Invalid token response' }, { status: 500 });
1337
+ }
1338
+
1339
+ return NextResponse.json({ token });
1340
+ } catch (error) {
1341
+ console.error('[Velt] Token generation error:', error);
1342
+ return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
1343
+ }
1344
+ }`,
1345
+ },
1346
+ {
1347
+ description: 'Client-side auth provider with backend token fetch',
1348
+ language: 'typescript',
1349
+ code: `// In VeltInitializeUser.tsx - useVeltAuthProvider hook
1350
+ export function useVeltAuthProvider() {
1351
+ const { user } = useAppUser();
1352
+
1353
+ // [Velt] Token generation - calls backend API
1354
+ const generateToken = useCallback(async (): Promise<string> => {
1355
+ const response = await fetch('/api/velt/token', {
1356
+ method: 'POST',
1357
+ headers: { 'Content-Type': 'application/json' },
1358
+ body: JSON.stringify({
1359
+ userId: user?.userId,
1360
+ organizationId: user?.organizationId,
1361
+ email: user?.email,
1362
+ }),
1363
+ });
1364
+ if (!response.ok) throw new Error('Token fetch failed');
1365
+ const data = await response.json();
1366
+ return data.token;
1367
+ }, [user]);
1368
+
1369
+ const authProvider: VeltAuthProvider | undefined = useMemo(() => {
1370
+ if (!user?.userId) return undefined;
1371
+ return {
1372
+ user: { userId: user.userId, name: user.name, email: user.email, organizationId: user.organizationId },
1373
+ generateToken,
1374
+ retryConfig: { retryCount: 3, retryDelay: 1000 },
1375
+ };
1376
+ }, [user, generateToken]);
1377
+
1378
+ return { authProvider };
1379
+ }`,
1380
+ },
1381
+ ],
1382
+ });
1383
+
1384
+ // Step 5: Replace API key placeholders
1385
+ steps.push({
1386
+ title: `Replace API keys with actual values`,
1387
+ details: `Update all instances of "YOUR_VELT_API_KEY" and "YOUR_VELT_AUTH_TOKEN" with your actual values: ${apiKey}. Make sure to replace in VeltProvider configuration.`,
1388
+ });
1389
+
1390
+ // Step 6: Test the installation
1391
+ const testInstructions = [];
1392
+ if (hasComments) testInstructions.push(`${commentType} comments: ${getTestInstructions(commentType)}`);
1393
+ if (hasPresence) testInstructions.push('Presence: Check that user avatars appear in the presence component');
1394
+ if (hasCursors) testInstructions.push('Cursors: Open in two browser windows and move your mouse to see cursors');
1395
+ if (hasNotifications) testInstructions.push('Notifications: Check the notification bell icon appears');
1396
+ if (hasRecorder) testInstructions.push('Recorder: Check the recorder controls appear');
1397
+
1398
+ steps.push({
1399
+ title: `Test all requested features`,
1400
+ details: `Start your development server and test ONLY the features you requested:\n${testInstructions.map(t => `- ${t}`).join('\n')}\n\nDO NOT test or implement other features.`,
1401
+ });
1402
+
1403
+ // Step 7: Check dev console for errors
1404
+ steps.push({
1405
+ title: `Check browser console for Velt errors/warnings`,
1406
+ details: `Open browser DevTools Console (Press F12 or Cmd+Option+I on Mac) and look for any Velt errors or warnings. Common errors include: "Please set document id to continue", "Velt API key not found", "Failed to authenticate user". If you find any errors, use the Velt Docs MCP to query for solutions.`,
1407
+ });
1408
+
1409
+ // Additional information
1410
+ const additionalInfo = [
1411
+ {
1412
+ title: '🚨 CRITICAL IMPLEMENTATION RULES',
1413
+ content: `**WHAT TO USE FROM CLI:**
1414
+ - ✅ Authentication setup (user identification)
1415
+ - ✅ Document setup (document context)
1416
+ - ✅ API key configuration
1417
+
1418
+ **PRIMARY SOURCE: Agent Skills (installed via \`npx skills add velt-js/agent-skills\`):**
1419
+ - ✅ **velt-setup-best-practices** — VeltProvider setup, auth, document identity, project structure
1420
+ ${hasComments ? `- ✅ **velt-comments-best-practices** — ${commentType} comments implementation patterns\n` : ''}${hasCRDT ? `- ✅ **velt-crdt-best-practices** — ${crdtEditorType || 'collaborative editing'} CRDT patterns\n` : ''}${hasNotifications ? `- ✅ **velt-notifications-best-practices** — notifications setup and customization\n` : ''}- Use skills as your FIRST reference for implementation details
1421
+
1422
+ **SECONDARY SOURCE: Docs URLs (for features without skills coverage):**
1423
+ ${hasPresence ? `- Presence: ${featureImplementations.presence?.mdUrl || getDocMarkdownUrl('presence')}\n` : ''}${hasCursors ? `- Cursors: ${featureImplementations.cursors?.mdUrl || getDocMarkdownUrl('cursors')}\n` : ''}${hasRecorder ? `- Recorder: ${featureImplementations.recorder?.mdUrl || getDocMarkdownUrl('recorder')}\n` : ''}${!hasPresence && !hasCursors && !hasRecorder ? `- All selected features are covered by Agent Skills\n` : ''}
1424
+ **TERTIARY SOURCE: Velt Docs MCP:**
1425
+ - Only use for user follow-up questions AFTER implementation
1426
+
1427
+ **WHAT NOT TO IMPLEMENT:**
1428
+ - ❌ VeltTools component (unless explicitly requested)
1429
+ - ❌ ui-customization folder (unless user asks)
1430
+ - ❌ Components user didn't request
1431
+ - ❌ DO NOT copy code from CLI-generated files in components/velt/*`,
1432
+ },
1433
+ {
1434
+ title: 'Documentation References',
1435
+ content: `**Primary: Agent Skills (installed via \`npx skills add velt-js/agent-skills\`)**
1436
+ - velt-setup-best-practices — setup, provider, auth, document
1437
+ ${hasComments ? `- velt-comments-best-practices — ${commentType} comments\n` : ''}${hasCRDT ? `- velt-crdt-best-practices — ${crdtEditorType || 'collaborative editing'}\n` : ''}${hasNotifications ? `- velt-notifications-best-practices — notifications\n` : ''}
1438
+ **Secondary: Docs URLs (for features without skills)**
1439
+ ${hasPresence ? `- Presence: ${featureImplementations.presence?.mdUrl || getDocMarkdownUrl('presence')}\n` : ''}${hasCursors ? `- Cursors: ${featureImplementations.cursors?.mdUrl || getDocMarkdownUrl('cursors')}\n` : ''}${hasRecorder ? `- Recorder: ${featureImplementations.recorder?.mdUrl || getDocMarkdownUrl('recorder')}\n` : ''}All Velt docs available as markdown at: https://docs.velt.dev/[feature]/[page].md
1440
+ ${hasCRDT && crdtImplementation?.data?.markdown ? `\n**CRDT Implementation Details (from ${crdtImplementation.source}):**\n${crdtImplementation.data.markdown.substring(0, 2000)}${crdtImplementation.data.markdown.length > 2000 ? '...\n\n[See full documentation at: ' + crdtImplementation.mdUrl + ']' : ''}\n` : ''}
1441
+ **Tertiary: Velt Docs MCP**
1442
+ After installation, query the Velt Docs MCP server for customization, troubleshooting, and advanced configuration. Do NOT use during initial implementation if skills cover the feature.`,
1443
+ },
1444
+ ];
1445
+
1446
+ // Generate skills source section
1447
+ const skillsSection = formatSkillsSourceSection(features, { commentType, crdtEditorType });
1448
+
1449
+ const basePlan = formatInstallationPlan({
1450
+ title: `Plan for Velt Installation: ${featureList.join(', ')}`,
1451
+ steps,
1452
+ additionalInfo,
1453
+ });
1454
+
1455
+ // Insert skills section right after the title line
1456
+ const titleEnd = basePlan.indexOf('\n\n') + 2;
1457
+ return basePlan.slice(0, titleEnd) + skillsSection + basePlan.slice(titleEnd);
1458
+ }
1459
+
1460
+ /**
1461
+ * Creates CLI-only installation report with TODO checklist
1462
+ *
1463
+ * Used when user types SKIP at feature selection.
1464
+ * Returns a checklist of what was created and what user needs to do.
1465
+ *
1466
+ * @param {Object} options
1467
+ * @param {Object} options.cliResult - Result from Velt CLI execution
1468
+ * @param {Object} options.qaResult - Basic QA validation results
1469
+ * @param {string} options.apiKey - API key (masked)
1470
+ * @param {string} [options.cliMethod] - CLI execution method ('linked' or 'direct')
1471
+ * @param {Object} [options.frameworkInfo] - Framework detection info
1472
+ * @returns {string} Markdown report
1473
+ */
1474
+ export function createCliOnlyReport({ cliResult, qaResult, apiKey, cliMethod, frameworkInfo }) {
1475
+ const validationLines = qaResult.checks.map(c => {
1476
+ const icon = c.status === 'pass' ? '✅' : c.status === 'warning' ? '⚠️' : '❌';
1477
+ return `- ${icon} **${c.name}**: ${c.message}`;
1478
+ }).join('\n');
1479
+
1480
+ // CLI execution method info
1481
+ const cliMethodInfo = cliMethod
1482
+ ? '**CLI Method:** npx @velt-js/add-velt'
1483
+ : '';
1484
+
1485
+ // Framework info
1486
+ const frameworkInfoSection = frameworkInfo
1487
+ ? `**Framework:** ${frameworkInfo.projectType}${frameworkInfo.needsUseClient ? ' (with "use client" enforcement)' : ''}`
1488
+ : '';
1489
+
1490
+ return `# ✅ Velt CLI Installation Complete (CLI-Only Mode)
1491
+
1492
+ ${cliMethodInfo}
1493
+ ${frameworkInfoSection}
1494
+
1495
+ You chose **SKIP** - the Velt CLI scaffolding has been run without feature integration.
1496
+ You can now wire up the features yourself, or re-run the installer without SKIP for guided setup.
1497
+
1498
+ ---
1499
+
1500
+ ## Files Created by Velt CLI
1501
+
1502
+ \`\`\`
1503
+ components/velt/
1504
+ ├── VeltCollaboration.tsx # Velt feature components (comments, presence, etc.)
1505
+ ├── VeltInitializeDocument.tsx # Exports useCurrentDocument hook
1506
+ └── VeltInitializeUser.tsx # Exports useVeltAuthProvider hook
1507
+
1508
+ app/userAuth/
1509
+ ├── AppUserContext.tsx # User context provider wrapper
1510
+ └── useAppUser.tsx # User data hook
1511
+ \`\`\`
1512
+
1513
+ ---
1514
+
1515
+ ## Validation Results
1516
+
1517
+ ${validationLines}
1518
+
1519
+ **Score:** ${qaResult.score} | **Status:** ${qaResult.status}
1520
+
1521
+ ---
1522
+
1523
+ ## 📋 TODO Checklist (You Need To Complete)
1524
+
1525
+ ### 1. Verify @veltdev/react is installed
1526
+
1527
+ The CLI should have installed \`@veltdev/react\`. If not, run:
1528
+ \`\`\`bash
1529
+ npm install @veltdev/react
1530
+ \`\`\`
1531
+
1532
+ ### 2. Configure environment variables
1533
+
1534
+ Create or update \`.env.local\`:
1535
+
1536
+ \`\`\`env
1537
+ NEXT_PUBLIC_VELT_API_KEY=${apiKey}
1538
+ \`\`\`
1539
+
1540
+ ### 3. Set up app/layout.tsx with AppUserProvider
1541
+
1542
+ Wrap your app with AppUserProvider for user context:
1543
+
1544
+ \`\`\`tsx
1545
+ // app/layout.tsx
1546
+ import { AppUserProvider } from './userAuth/AppUserContext';
1547
+
1548
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
1549
+ return (
1550
+ <html lang="en">
1551
+ <body>
1552
+ <AppUserProvider>
1553
+ {children}
1554
+ </AppUserProvider>
1555
+ </body>
1556
+ </html>
1557
+ );
1558
+ }
1559
+ \`\`\`
1560
+
1561
+ ### 4. Set up app/page.tsx with VeltProvider
1562
+
1563
+ Add VeltProvider to your root page using the authProvider hook:
1564
+
1565
+ \`\`\`tsx
1566
+ // app/page.tsx
1567
+ "use client";
1568
+ import { VeltProvider } from '@veltdev/react';
1569
+ import { useVeltAuthProvider } from '@/components/velt/VeltInitializeUser';
1570
+ import { useCurrentDocument } from '@/components/velt/VeltInitializeDocument';
1571
+ import { VeltCollaboration } from '@/components/velt/VeltCollaboration';
1572
+
1573
+ const VELT_API_KEY = process.env.NEXT_PUBLIC_VELT_API_KEY!;
1574
+
1575
+ export default function Page() {
1576
+ const { authProvider } = useVeltAuthProvider();
1577
+ const { documentId } = useCurrentDocument();
1578
+
1579
+ return (
1580
+ <VeltProvider apiKey={VELT_API_KEY} authProvider={authProvider}>
1581
+ <VeltCollaboration documentId={documentId} />
1582
+ {/* Your page content */}
1583
+ </VeltProvider>
1584
+ );
1585
+ }
1586
+ \`\`\`
1587
+
1588
+ ### 5. Configure user authentication
1589
+
1590
+ Edit \`app/userAuth/useAppUser.tsx\` to connect your auth:
1591
+
1592
+ \`\`\`tsx
1593
+ // TODO: Replace hardcoded user with your auth provider
1594
+ // Examples:
1595
+ // - Next-Auth: const { data: session } = useSession();
1596
+ // - Clerk: const { user } = useUser();
1597
+ // - Auth0: const { user } = useAuth0();
1598
+
1599
+ const user = {
1600
+ userId: "your-user-id", // Required: unique user ID
1601
+ name: "User Name", // Required: display name
1602
+ email: "user@example.com", // Required: email
1603
+ photoUrl: "https://...", // Optional: avatar URL
1604
+ organizationId: "your-org", // Optional: for multi-tenant apps
1605
+ };
1606
+ \`\`\`
1607
+
1608
+ ### 6. Configure document identification
1609
+
1610
+ Edit \`components/velt/VeltInitializeDocument.tsx\` to set document ID in the useCurrentDocument hook:
1611
+
1612
+ \`\`\`tsx
1613
+ // TODO: Replace with your document ID logic
1614
+ // The document ID determines which users see each other's comments/cursors
1615
+ // Examples:
1616
+ // - Page-based: const documentId = pathname;
1617
+ // - Route param: const documentId = params.id;
1618
+ // - Custom: const documentId = getCurrentProjectId();
1619
+
1620
+ const documentId = "your-document-id";
1621
+ \`\`\`
1622
+
1623
+ ### 6. Add Velt feature components
1624
+
1625
+ Add specific features where needed in your app:
1626
+
1627
+ \`\`\`tsx
1628
+ import { VeltComments, VeltPresence, VeltCursor } from '@veltdev/react';
1629
+
1630
+ // Comments - add where you want commenting
1631
+ <VeltComments />
1632
+
1633
+ // Presence - shows online users
1634
+ <VeltPresence />
1635
+
1636
+ // Cursors - shows live cursor positions
1637
+ <VeltCursor />
1638
+ \`\`\`
1639
+
1640
+ ---
1641
+
1642
+ ## 🔗 Documentation
1643
+
1644
+ - **Quick Start:** https://docs.velt.dev/get-started/quickstart
1645
+ - **Authentication:** https://docs.velt.dev/get-started/quickstart#step-5-authenticate-users
1646
+ - **Document Setup:** https://docs.velt.dev/get-started/quickstart#step-6-initialize-document
1647
+ - **Comments:** https://docs.velt.dev/async-collaboration/comments/setup
1648
+ - **Presence:** https://docs.velt.dev/realtime-collaboration/presence/setup
1649
+ - **Cursors:** https://docs.velt.dev/realtime-collaboration/cursors/setup
1650
+
1651
+ ---
1652
+
1653
+ ## 🚀 Next Steps
1654
+
1655
+ **Option A: Manual Setup**
1656
+ Follow the TODO checklist above to wire up Velt features yourself.
1657
+
1658
+ **Option B: Guided Setup**
1659
+ Re-run the installer and select specific features (don't type SKIP):
1660
+ \`\`\`
1661
+ @velt-installer install
1662
+ \`\`\`
1663
+
1664
+ The guided mode will:
1665
+ - Generate a detailed implementation plan for your selected features
1666
+ - Detect your project structure and recommend file placements
1667
+ - Provide feature-specific code examples from Velt docs
1668
+ - Apply changes only after your approval
1669
+
1670
+ ---
1671
+
1672
+ ## ⚠️ Common Issues
1673
+
1674
+ **"Velt API key not found"**
1675
+ - Make sure \`NEXT_PUBLIC_VELT_API_KEY\` is in your \`.env.local\`
1676
+ - Restart your dev server after adding environment variables
1677
+
1678
+ **"Please set document id to continue"**
1679
+ - Ensure \`VeltInitializeDocument\` is properly configured with a document ID
1680
+ - The document ID should be unique per collaborative context
1681
+
1682
+ **"Failed to authenticate user"**
1683
+ - Check that your user data includes required fields: \`userId\`, \`name\`, \`email\`
1684
+ - Verify the auth token is correct in your environment variables
1685
+
1686
+ ---
1687
+
1688
+ *Generated by Velt MCP Installer (CLI-Only Mode)*
1689
+ `;
1690
+ }
1691
+
1692
+ export default {
1693
+ formatInstallationPlan,
1694
+ formatSkillsSourceSection,
1695
+ createVeltCommentsPlan,
1696
+ createMultiFeaturePlan,
1697
+ createCliOnlyReport,
1698
+ };