@velt-js/mcp-installer 0.1.0 → 0.2.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.
@@ -1,8 +1,11 @@
1
1
  /**
2
2
  * Plan Formatter
3
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.
4
+ * Formats installation instructions as a sequential plan that the AI follows.
5
+ *
6
+ * ARCHITECTURE: This formatter generates ORCHESTRATION steps only.
7
+ * Implementation details (code patterns, CSS, configuration) live in agent-skills.
8
+ * The plan tells the AI WHAT to do and WHICH skill rule to follow for HOW.
6
9
  */
7
10
 
8
11
  import { getDocUrl, getDocMarkdownUrl } from './velt-docs-urls.js';
@@ -10,11 +13,6 @@ import { getSkillReferences } from './velt-docs-fetcher.js';
10
13
 
11
14
  /**
12
15
  * 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
16
  */
19
17
  function formatSkillsSourceSection(features, options = {}) {
20
18
  const refs = getSkillReferences(features, options);
@@ -28,13 +26,12 @@ function formatSkillsSourceSection(features, options = {}) {
28
26
 
29
27
  if (skillRefs.length > 0) {
30
28
  section += `### Primary: Agent Skills (use these first)\n\n`;
31
- // Deduplicate skill names
32
29
  const seen = new Set();
33
30
  for (const ref of skillRefs) {
34
31
  if (!seen.has(ref.skillName)) {
35
32
  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`;
33
+ const feats = skillRefs.filter(r => r.skillName === ref.skillName).map(r => r.feature);
34
+ section += `- **${ref.skillName}** — covers: ${feats.join(', ')}${ref.description ? ` (${ref.description})` : ''}\n`;
38
35
  }
39
36
  }
40
37
  section += `\n`;
@@ -56,29 +53,18 @@ function formatSkillsSourceSection(features, options = {}) {
56
53
  }
57
54
 
58
55
  /**
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
56
+ * Formats a plan with numbered steps, details, and a to-do checklist.
69
57
  */
70
58
  export function formatInstallationPlan(options) {
71
59
  const { title, steps, additionalInfo = [] } = options;
72
60
 
73
61
  let plan = `# ${title}\n\n`;
74
62
 
75
- // Add numbered steps with details
76
63
  steps.forEach((step, index) => {
77
64
  const stepNumber = index + 1;
78
65
  plan += `## ${stepNumber}. ${step.title}\n`;
79
66
  plan += `* **Details:** ${step.details}\n`;
80
67
 
81
- // Add code examples if provided
82
68
  if (step.codeExamples && step.codeExamples.length > 0) {
83
69
  step.codeExamples.forEach((example) => {
84
70
  plan += `\n${example.description ? `* ${example.description}:` : ''}
@@ -91,7 +77,6 @@ ${example.code}
91
77
  plan += '\n';
92
78
  });
93
79
 
94
- // Add additional information sections
95
80
  if (additionalInfo.length > 0) {
96
81
  additionalInfo.forEach((info) => {
97
82
  plan += `## ${info.title}\n`;
@@ -99,7 +84,6 @@ ${example.code}
99
84
  });
100
85
  }
101
86
 
102
- // Add To-Do checklist
103
87
  plan += `## To-Do List\n`;
104
88
  steps.forEach((step, index) => {
105
89
  const checkbox = index === 0 ? '[✓]' : '[ ]';
@@ -107,21 +91,31 @@ ${example.code}
107
91
  });
108
92
 
109
93
  plan += '\n';
110
-
111
94
  return plan;
112
95
  }
113
96
 
114
97
  /**
115
- * Creates a plan for Velt Comments installation
98
+ * Gets test instructions for a comment type.
99
+ */
100
+ function getTestInstructions(commentType) {
101
+ const instructions = {
102
+ freestyle: 'Click the Comment Tool button, then click anywhere on the page to add a comment.',
103
+ popover: 'Click the Comment Tool button next to an element to attach a comment to it.',
104
+ page: 'Open the Comments Sidebar and add a page-level comment at the bottom.',
105
+ stream: 'Select text to see comments appear in the stream column on the right.',
106
+ text: 'Highlight any text to see the Comment Tool button appear, then click it to add a comment.',
107
+ inline: 'Navigate to your content area and test adding inline comments.',
108
+ tiptap: 'Open your Tiptap editor, select text, and use the comment button in the bubble menu.',
109
+ lexical: 'Open your Lexical editor, select text, and add comments using the Velt comment integration.',
110
+ slate: 'Open your Slate.js editor, select text, and add comments using the Velt comment integration.',
111
+ };
112
+ return instructions[commentType] || 'Test adding comments in your application.';
113
+ }
114
+
115
+ /**
116
+ * Creates a plan for Velt Comments installation (single-feature).
116
117
  *
117
118
  * @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
119
  * @returns {string} Formatted installation plan
126
120
  */
127
121
  export function createVeltCommentsPlan(options) {
@@ -133,564 +127,113 @@ export function createVeltCommentsPlan(options) {
133
127
  headerPosition,
134
128
  veltProviderLocation = 'app/page.tsx',
135
129
  crdtEditorType,
130
+ frameworkInfo,
131
+ wiring,
136
132
  } = options;
137
133
 
138
134
  const commentTypeTitle = commentType.charAt(0).toUpperCase() + commentType.slice(1);
139
-
140
135
  const steps = [];
141
136
 
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
137
  const locationText = veltProviderLocation === 'auto-detect'
150
- ? 'the appropriate layout file (analyze the project structure to determine the best location)'
138
+ ? 'the appropriate layout file'
151
139
  : veltProviderLocation;
152
140
 
141
+ // Step 1: Scope warning
153
142
  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
- ],
143
+ title: `⚠️ CRITICAL: Only implement ${commentTypeTitle} Comments`,
144
+ details: `You are ONLY installing ${commentTypeTitle} Comments. DO NOT implement other components unless requested.`,
279
145
  });
280
146
 
281
- // Step 3: Add TODO comments to CLI-generated auth files
147
+ // Step 2: Wire VeltProvider + CLI components
282
148
  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
- ],
149
+ title: `Wire VeltProvider and CLI-generated components in ${locationText}`,
150
+ details: `**Skill reference:** \`velt-setup-best-practices\` provider-wiring rules
151
+
152
+ Use the CLI-generated files in \`components/velt/\`. Wire them following the skill patterns:
153
+ - Import \`useVeltAuthProvider\` from \`components/velt/VeltInitializeUser.tsx\`
154
+ - Import \`VeltCollaboration\` from \`components/velt/VeltCollaboration.tsx\`
155
+ - Wrap your page content with \`<VeltProvider apiKey={...} authProvider={authProvider}>\`
156
+ - Place \`<VeltCollaboration />\` inside the VeltProvider
157
+ - The page file MUST have \`"use client"\` directive (Next.js)
158
+ - VeltProvider goes in \`${locationText}\`, NOT in layout.tsx (layout.tsx exports metadata, which is server-only)
159
+
160
+ **IMPORTANT:** Do NOT create new Velt files. Use the CLI-generated files in \`components/velt/\`.`,
396
161
  });
397
162
 
398
- // Step 4: Implement JWT token generation (Production Pattern)
163
+ // Step 3: Configure authentication
399
164
  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.
165
+ title: `Set up authentication and JWT token generation`,
166
+ details: `**Skill reference:** \`velt-setup-best-practices\` identity-jwt-generation, identity-user-object-shape rules
405
167
 
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
- });
168
+ Follow the skill patterns to:
169
+ - Configure \`app/api/velt/token/route.ts\` for server-side JWT generation
170
+ - Set up user identification with required fields (userId, name, email, organizationId)
171
+ - Ensure auth token is server-only (VELT_AUTH_TOKEN in .env.local, NOT in client bundle)`,
172
+ });
471
173
 
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
- }
174
+ // Step 4: Configure .env.local
175
+ steps.push({
176
+ title: `Set up environment variables`,
177
+ details: `Create or update \`.env.local\` with your Velt credentials (API key: ${apiKey}).
178
+ Required variables: NEXT_PUBLIC_VELT_API_KEY, VELT_API_KEY, VELT_AUTH_TOKEN.`,
179
+ });
476
180
 
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
- }
181
+ // Step 5: Set up two-user testing
182
+ steps.push({
183
+ title: `Set up two-user testing`,
184
+ details: `**Skill reference:** \`velt-setup-best-practices\` debug-multi-user-testing rule
482
185
 
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
- ],
186
+ The app MUST support testing with two different users. Follow the skill pattern:
187
+ - Provide sign-in buttons for both Alice and Bob
188
+ - Do NOT auto-login with a default user — let the sign-in page render
189
+ - Support \`?user=user-1\` and \`?user=user-2\` URL params for quick switching`,
526
190
  });
527
191
 
528
- // Step 5: Replace API key placeholders
192
+ // Step 6: Clear .next cache
529
193
  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.`,
194
+ title: `Clear .next build cache before first run`,
195
+ details: `Run \`rm -rf .next\` before starting the dev server. This prevents stale cache issues after changing imports.`,
532
196
  });
533
197
 
534
- // Step 6: Test the installation
198
+ // Step 7: Test
535
199
  steps.push({
536
200
  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.`,
201
+ details: `Start your development server and test. ${getTestInstructions(commentType)} DO NOT test or implement other features.`,
538
202
  });
539
203
 
540
- // Step 7: Check dev console for errors
204
+ // Step 8: Check console
541
205
  steps.push({
542
206
  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?"`,
207
+ details: `Open browser DevTools Console and look for Velt errors. Common: "Please set document id", "Velt API key not found", "Failed to authenticate user". If errors occur, consult \`velt-setup-best-practices\` debug-common-issues rule.`,
544
208
  });
545
209
 
546
- // Additional information
210
+ // Additional info
547
211
  const additionalInfo = [
548
212
  {
549
213
  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.`,
214
+ content: `**Source priority:** Agent Skills > Embedded Rules > Docs URLs
215
+ - ✅ **velt-setup-best-practices** VeltProvider, auth, document identity, project structure
216
+ - ✅ **velt-comments-best-practices** ${commentType} comments implementation patterns
217
+ - Do NOT reimplement patterns from scratch — follow the skill rules exactly`,
632
218
  },
633
219
  ];
634
220
 
635
- // Generate skills source section
636
- const skillsSection = formatSkillsSourceSection(['comments'], { commentType });
637
-
638
- const basePlan = formatInstallationPlan({
221
+ // Format and add skills section
222
+ const plan = formatInstallationPlan({
639
223
  title: `Plan for Velt ${commentTypeTitle} Comments Installation`,
640
224
  steps,
641
225
  additionalInfo,
642
226
  });
643
227
 
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);
228
+ const skillsSection = formatSkillsSourceSection(['comments'], { commentType });
229
+ return plan + '\n' + skillsSection;
647
230
  }
648
231
 
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
232
 
662
233
  /**
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
234
+ * Creates a comprehensive plan for multiple Velt features.
682
235
  *
683
236
  * @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
237
  * @returns {string} Formatted installation plan
695
238
  */
696
239
  export function createMultiFeaturePlan(options) {
@@ -705,6 +248,8 @@ export function createMultiFeaturePlan(options) {
705
248
  apiKey,
706
249
  headerPosition,
707
250
  veltProviderLocation = 'app/page.tsx',
251
+ frameworkInfo,
252
+ wiring,
708
253
  } = options;
709
254
 
710
255
  const steps = [];
@@ -723,753 +268,205 @@ export function createMultiFeaturePlan(options) {
723
268
  if (hasRecorder) featureList.push('Recorder');
724
269
  if (hasCRDT) featureList.push(`CRDT (${crdtEditorType ? crdtEditorType.charAt(0).toUpperCase() + crdtEditorType.slice(1) : 'Collaborative Editing'})`);
725
270
 
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
271
  const locationText = veltProviderLocation === 'auto-detect'
741
- ? 'the appropriate layout file (analyze the project structure to determine the best location)'
272
+ ? 'the appropriate page file'
742
273
  : veltProviderLocation;
743
274
 
275
+ // Step 1: Scope warning
744
276
  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]);
277
+ title: `⚠️ CRITICAL: Only implement ${featureList.join(', ')}`,
278
+ details: `You are ONLY installing: ${featureList.join(', ')}. DO NOT implement any other components unless the user specifically requested them.`,
279
+ });
976
280
 
977
- // [Velt] Initialize CRDT extension with unique editorId
978
- const { VeltCrdt, isLoading } = useVeltTiptapCrdtExtension({
979
- editorId,
980
- initialContent: veltInitialContent,
281
+ // Step 2: Wire VeltProvider + CLI components
282
+ steps.push({
283
+ title: `Wire VeltProvider and CLI-generated components in ${locationText}`,
284
+ details: `**Skill reference:** \`velt-setup-best-practices\` → provider-wiring, identity rules
285
+
286
+ Use the CLI-generated files in \`components/velt/\`. Wire them following the skill patterns:
287
+ - Import \`useVeltAuthProvider\` from \`components/velt/VeltInitializeUser.tsx\`
288
+ - Import \`VeltCollaboration\` from \`components/velt/VeltCollaboration.tsx\`
289
+ - Wrap your page content with \`<VeltProvider apiKey={...} authProvider={authProvider}>\`
290
+ - Place \`<VeltCollaboration />\` inside the VeltProvider
291
+ - The page file MUST have \`"use client"\` directive (Next.js)
292
+ - VeltProvider goes in \`${locationText}\`, NOT in layout.tsx (layout.tsx exports metadata)
293
+ - Position Velt UI components (presence, notifications, sidebar) in the ${headerPosition} corner
294
+
295
+ **IMPORTANT:** Do NOT create new Velt files. Use the CLI-generated files in \`components/velt/\`.`,
981
296
  });
982
297
 
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;
298
+ // Step 3: CRDT editor setup (conditional)
299
+ if (hasCRDT && crdtEditorType) {
300
+ const editorName = crdtEditorType.charAt(0).toUpperCase() + crdtEditorType.slice(1);
301
+
302
+ let skillRules = '';
303
+ let requirements = '';
304
+
305
+ if (crdtEditorType === 'tiptap') {
306
+ skillRules = 'tiptap-setup-react, tiptap-editor-id, tiptap-disable-history, tiptap-initial-content';
307
+ requirements = `- Use \`useVeltTiptapCrdtExtension\` hook with editorId = \`\${documentId}/\${fieldId}\`
308
+ - Disable \`undoRedo\` in StarterKit (NOT \`history\` StarterKit has no "history" option)
309
+ - Use HTML strings for initialContent, NOT JSON objects (see tiptap-initial-content rule)
310
+ - Set \`immediatelyRender: false\` in useEditor options
311
+ - CRDT extension should be LAST in the extensions array`;
312
+ } else if (crdtEditorType === 'codemirror') {
313
+ skillRules = 'codemirror-setup-react, codemirror-ycollab, codemirror-editor-id';
314
+ requirements = `- Use \`useVeltCodeMirrorCrdtExtension\` hook
315
+ - Wire yCollab extension with store.getYText() and store.getAwareness()
316
+ - Use store.getUndoManager() for undo/redo`;
317
+ } else if (crdtEditorType === 'blocknote') {
318
+ skillRules = 'blocknote-setup-react, blocknote-editor-id';
319
+ requirements = `- Use \`useVeltBlockNoteCrdtExtension\` hook
320
+ - Pass collaborationConfig to useCreateBlockNote
321
+ - BlockNote handles CRDT automatically with the config`;
1033
322
  }
1034
- }, [editor, isLoading, serverConnectionState, backendfallbackContent]);
1035
323
 
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
- }
324
+ steps.push({
325
+ title: `Create ${editorName} CRDT editor component`,
326
+ details: `**Skill reference:** \`velt-crdt-best-practices\` → ${skillRules}
1131
327
 
1132
- // Get component implementations from markdown docs above`,
1133
- },
1134
- ],
1135
- });
328
+ Create \`components/velt/${editorName}CollabEditor.tsx\` following the skill patterns.
1136
329
 
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
- }
330
+ Requirements:
331
+ ${requirements}`,
332
+ });
333
+ }
1183
334
 
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
- };
335
+ // Step 4: Comments integration with editor (conditional)
336
+ if (hasComments && hasCRDT && crdtEditorType) {
337
+ steps.push({
338
+ title: `MANDATORY: Integrate TiptapVeltComments extension in editor`,
339
+ details: `**Skill reference:** \`velt-crdt-best-practices\` → tiptap-comments-integration rule
1211
340
 
1212
- const user = users[userParam] || users['1'];
341
+ ⚠️ WITHOUT THIS, THE APP WILL FREEZE WHEN COMMENTS ARE TRIGGERED.
1213
342
 
1214
- return { user, isUserLoggedIn: true };
1215
- }
343
+ The global \`<VeltComments>\` component is necessary but NOT sufficient for editor comments.
344
+ You MUST also:
345
+ - Add \`TiptapVeltComments\` to the editor's extensions array (BEFORE the CRDT extension)
346
+ - Import and call \`highlightComments(editor, commentAnnotations)\` in a useEffect (v4 API)
347
+ - Import and use \`triggerAddComment(editor)\` for the comment button (v4 API)
348
+ - Import \`useCommentAnnotations\` from \`@veltdev/react\` to subscribe to comment data
1216
349
 
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
- });
350
+ Check your installed package version — v4 uses \`triggerAddComment\`/\`highlightComments\`, v5 uses \`addComment\`/\`renderComments\`.`,
351
+ });
352
+ }
1253
353
 
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.
354
+ // Step 5: SSR safety (conditional, Next.js + Tiptap)
355
+ if (hasCRDT && crdtEditorType === 'tiptap') {
356
+ steps.push({
357
+ title: `MANDATORY: Load editor with next/dynamic (SSR safety)`,
358
+ details: `**Skill reference:** \`velt-crdt-best-practices\` → tiptap-nextjs-ssr rule
1258
359
 
1259
- **Production Pattern (FireHydrant Reference):**
1260
- Server-side API calls Velt's token endpoint to generate secure JWT tokens.
360
+ Tiptap and @veltdev/tiptap-velt-comments use browser-only APIs. In Next.js, the editor component MUST be loaded with \`next/dynamic\` and \`ssr: false\` in the page that renders it. Without this, the app will crash with a \`g.catch is not a function\` error.
1261
361
 
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
- \`\`\`
362
+ In your page file, use \`dynamic(() => import(...), { ssr: false })\` — do NOT import the editor component directly.`,
363
+ });
364
+ }
1273
365
 
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
- \`\`\`
366
+ // Step 6: Cursor CSS (conditional)
367
+ if (hasCRDT && crdtEditorType === 'tiptap') {
368
+ steps.push({
369
+ title: `MANDATORY: Add collaboration cursor CSS to globals.css`,
370
+ details: `**Skill reference:** \`velt-crdt-best-practices\` → tiptap-cursor-css rule
1279
371
 
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
- }
372
+ Without this CSS, remote user cursors appear as thick full-width blocks instead of thin carets.
1306
373
 
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
- }
374
+ Follow the skill rule exactly — it targets BOTH:
375
+ - \`.ProseMirror-yjs-cursor\` classes (from y-prosemirror, used by Velt CRDT)
376
+ - \`.collaboration-cursor__caret\` classes (from @tiptap/extension-collaboration-cursor)
1311
377
 
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
- }),
378
+ Critical: the \`> span { display: inline !important }\` rule is required to prevent block-level rendering.`,
1326
379
  });
380
+ }
1327
381
 
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
- }
382
+ // Step 7: Authentication setup
383
+ steps.push({
384
+ title: `Set up authentication and JWT token generation`,
385
+ details: `**Skill reference:** \`velt-setup-best-practices\` → identity-jwt-generation, identity-user-object-shape rules
1332
386
 
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
- }
387
+ Follow the skill patterns to:
388
+ - Configure \`app/api/velt/token/route.ts\` for server-side JWT generation
389
+ - Set up user identification with required fields (userId, name, email, organizationId)
390
+ - Ensure auth token is server-only (VELT_AUTH_TOKEN in .env.local, NOT in client bundle)`,
391
+ });
1338
392
 
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
- ],
393
+ // Step 8: Environment variables
394
+ steps.push({
395
+ title: `Set up environment variables`,
396
+ details: `Create or update \`.env.local\` with your Velt credentials (API key: ${apiKey}).
397
+ Required variables: NEXT_PUBLIC_VELT_API_KEY, VELT_API_KEY, VELT_AUTH_TOKEN.`,
1382
398
  });
1383
399
 
1384
- // Step 5: Replace API key placeholders
400
+ // Step 9: Two-user testing
1385
401
  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.`,
402
+ title: `Set up two-user testing`,
403
+ details: `**Skill reference:** \`velt-setup-best-practices\` debug-multi-user-testing rule
404
+
405
+ The app MUST support testing with two different users. Follow the skill pattern:
406
+ - Provide sign-in buttons for both Alice and Bob
407
+ - Do NOT auto-login with a default user — if the scaffolding has \`params.get("user") || "user-1"\`, remove the \`|| "user-1"\` default
408
+ - Support \`?user=user-1\` and \`?user=user-2\` URL params for quick switching`,
1388
409
  });
1389
410
 
1390
- // Step 6: Test the installation
411
+ // Step 10: Clear .next cache
412
+ steps.push({
413
+ title: `Clear .next build cache before first run`,
414
+ details: `Run \`rm -rf .next\` before starting the dev server. This prevents stale cache issues, especially after changing SSR patterns (adding \`next/dynamic\`).`,
415
+ });
416
+
417
+ // Step 11: Test
1391
418
  const testInstructions = [];
1392
419
  if (hasComments) testInstructions.push(`${commentType} comments: ${getTestInstructions(commentType)}`);
1393
420
  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');
421
+ if (hasCursors) testInstructions.push('Cursors: Open in two browser windows and verify thin caret cursors with name labels');
1395
422
  if (hasNotifications) testInstructions.push('Notifications: Check the notification bell icon appears');
1396
423
  if (hasRecorder) testInstructions.push('Recorder: Check the recorder controls appear');
1397
424
 
1398
425
  steps.push({
1399
426
  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.`,
427
+ details: `Start your development server and test:\n${testInstructions.map(t => `- ${t}`).join('\n')}\n\nDO NOT test or implement other features.`,
1401
428
  });
1402
429
 
1403
- // Step 7: Check dev console for errors
430
+ // Step 12: Check console
1404
431
  steps.push({
1405
432
  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.`,
433
+ details: `Open browser DevTools Console and look for Velt errors. Common: "Please set document id", "Velt API key not found", "Failed to authenticate user". If errors occur, consult \`velt-setup-best-practices\` debug-common-issues rule.`,
1407
434
  });
1408
435
 
1409
- // Additional information
436
+ // Additional info
437
+ const skillsList = [];
438
+ skillsList.push('- ✅ **velt-setup-best-practices** — VeltProvider, auth, document identity, project structure');
439
+ if (hasComments) skillsList.push(`- ✅ **velt-comments-best-practices** — ${commentType} comments implementation patterns`);
440
+ if (hasCRDT) skillsList.push(`- ✅ **velt-crdt-best-practices** — ${crdtEditorType || 'collaborative editing'} CRDT patterns`);
441
+ if (hasNotifications) skillsList.push('- ✅ **velt-notifications-best-practices** — notifications setup and customization');
442
+
1410
443
  const additionalInfo = [
1411
444
  {
1412
445
  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.`,
446
+ content: `**Source priority:** Agent Skills > Embedded Rules > Docs URLs
447
+
448
+ ${skillsList.join('\n')}
449
+
450
+ - Do NOT reimplement patterns from scratch — follow the skill rules exactly
451
+ - If a skill rule and this plan conflict, the skill rule is correct
452
+ - Do NOT create files outside \`components/velt/\` unless necessary for app-specific wiring`,
1443
453
  },
1444
454
  ];
1445
455
 
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(', ')}`,
456
+ // Format and add skills section
457
+ const plan = formatInstallationPlan({
458
+ title: `Plan for Velt ${featureList.join(' + ')} Installation`,
1451
459
  steps,
1452
460
  additionalInfo,
1453
461
  });
1454
462
 
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);
463
+ const skillsSection = formatSkillsSourceSection(features, { commentType, crdtEditorType });
464
+ return plan + '\n' + skillsSection;
1458
465
  }
1459
466
 
467
+
1460
468
  /**
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
469
+ * Creates a CLI-only installation report (SKIP mode).
1473
470
  */
1474
471
  export function createCliOnlyReport({ cliResult, qaResult, apiKey, cliMethod, frameworkInfo }) {
1475
472
  const validationLines = qaResult.checks.map(c => {
@@ -1477,12 +474,8 @@ export function createCliOnlyReport({ cliResult, qaResult, apiKey, cliMethod, fr
1477
474
  return `- ${icon} **${c.name}**: ${c.message}`;
1478
475
  }).join('\n');
1479
476
 
1480
- // CLI execution method info
1481
- const cliMethodInfo = cliMethod
1482
- ? '**CLI Method:** npx @velt-js/add-velt'
1483
- : '';
477
+ const cliMethodInfo = cliMethod ? '**CLI Method:** npx @velt-js/add-velt' : '';
1484
478
 
1485
- // Framework info
1486
479
  const frameworkInfoSection = frameworkInfo
1487
480
  ? `**Framework:** ${frameworkInfo.projectType}${frameworkInfo.needsUseClient ? ' (with "use client" enforcement)' : ''}`
1488
481
  : '';
@@ -1520,168 +513,24 @@ ${validationLines}
1520
513
 
1521
514
  ---
1522
515
 
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';
516
+ ## 📋 Next Steps
1547
517
 
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();
518
+ **Skill reference:** Install agent-skills via \`npx skills add velt-js/agent-skills\`, then follow:
519
+ - \`velt-setup-best-practices\` — for VeltProvider wiring, auth, document setup
520
+ - \`velt-comments-best-practices\` — for comments integration
521
+ - \`velt-crdt-best-practices\` — for CRDT/collaborative editing
522
+ - \`velt-notifications-best-practices\` — for notifications
1578
523
 
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
524
+ Or re-run the installer with specific features (don't type SKIP) for guided setup.
1669
525
 
1670
526
  ---
1671
527
 
1672
528
  ## ⚠️ Common Issues
1673
529
 
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
530
+ Consult \`velt-setup-best-practices\` debug-common-issues rule for:
531
+ - "Velt API key not found" check .env.local
532
+ - "Please set document id" check VeltInitializeDocument
533
+ - "Failed to authenticate user" — check user object fields
1685
534
 
1686
535
  ---
1687
536