@codebakers/mcp 5.4.1 → 5.4.3

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.
@@ -13,73 +13,73 @@ export async function generateUnitTests(args) {
13
13
  const testPath = file_path.replace(/\.(tsx?|js)$/, '.test.$1');
14
14
  let code = '';
15
15
  if (test_type === 'component') {
16
- code = `import { describe, it, expect, vi } from 'vitest';
17
- import { render, screen } from '@testing-library/react';
18
- import { Component } from './component';
19
-
20
- describe('Component', () => {
21
- it('renders loading state', () => {
22
- render(<Component />);
23
- expect(screen.getByRole('status')).toBeInTheDocument();
24
- });
25
-
26
- it('renders error state', () => {
27
- render(<Component />);
28
- expect(screen.getByRole('alert')).toBeInTheDocument();
29
- });
30
-
31
- it('renders empty state', () => {
32
- render(<Component />);
33
- expect(screen.getByText(/No items/)).toBeInTheDocument();
34
- });
35
-
36
- it('renders success state', () => {
37
- render(<Component />);
38
- expect(screen.getByText('Item')).toBeInTheDocument();
39
- });
40
- });
16
+ code = `import { describe, it, expect, vi } from 'vitest';
17
+ import { render, screen } from '@testing-library/react';
18
+ import { Component } from './component';
19
+
20
+ describe('Component', () => {
21
+ it('renders loading state', () => {
22
+ render(<Component />);
23
+ expect(screen.getByRole('status')).toBeInTheDocument();
24
+ });
25
+
26
+ it('renders error state', () => {
27
+ render(<Component />);
28
+ expect(screen.getByRole('alert')).toBeInTheDocument();
29
+ });
30
+
31
+ it('renders empty state', () => {
32
+ render(<Component />);
33
+ expect(screen.getByText(/No items/)).toBeInTheDocument();
34
+ });
35
+
36
+ it('renders success state', () => {
37
+ render(<Component />);
38
+ expect(screen.getByText('Item')).toBeInTheDocument();
39
+ });
40
+ });
41
41
  `;
42
42
  }
43
43
  else if (test_type === 'store') {
44
- code = `import { describe, it, expect, beforeEach } from 'vitest';
45
- import { renderHook, act } from '@testing-library/react';
46
- import { useStore } from './store';
47
-
48
- describe('Store', () => {
49
- beforeEach(() => {
50
- useStore.setState({ items: [], loading: false, error: null });
51
- });
52
-
53
- it('fetches items', async () => {
54
- const { result } = renderHook(() => useStore());
55
- await act(async () => {
56
- await result.current.fetchItems();
57
- });
58
- expect(result.current.items).toHaveLength(0);
59
- });
60
- });
44
+ code = `import { describe, it, expect, beforeEach } from 'vitest';
45
+ import { renderHook, act } from '@testing-library/react';
46
+ import { useStore } from './store';
47
+
48
+ describe('Store', () => {
49
+ beforeEach(() => {
50
+ useStore.setState({ items: [], loading: false, error: null });
51
+ });
52
+
53
+ it('fetches items', async () => {
54
+ const { result } = renderHook(() => useStore());
55
+ await act(async () => {
56
+ await result.current.fetchItems();
57
+ });
58
+ expect(result.current.items).toHaveLength(0);
59
+ });
60
+ });
61
61
  `;
62
62
  }
63
63
  else {
64
- code = `import { describe, it, expect } from 'vitest';
65
- import { GET } from './route';
66
-
67
- describe('API Route', () => {
68
- it('returns data', async () => {
69
- const req = new Request('http://localhost/api/test');
70
- const res = await GET(req);
71
- expect(res.status).toBe(200);
72
- });
73
- });
64
+ code = `import { describe, it, expect } from 'vitest';
65
+ import { GET } from './route';
66
+
67
+ describe('API Route', () => {
68
+ it('returns data', async () => {
69
+ const req = new Request('http://localhost/api/test');
70
+ const res = await GET(req);
71
+ expect(res.status).toBe(200);
72
+ });
73
+ });
74
74
  `;
75
75
  }
76
76
  const fullPath = path.join(cwd, testPath);
77
77
  await fs.writeFile(fullPath, code, 'utf-8');
78
- return `🍞 CodeBakers: Unit Tests Generated
79
-
80
- **File:** ${testPath}
81
- **Type:** ${test_type}
82
-
78
+ return `🍞 CodeBakers: Unit Tests Generated
79
+
80
+ **File:** ${testPath}
81
+ **Type:** ${test_type}
82
+
83
83
  ✅ Test file created (Vitest)`;
84
84
  }
85
85
  //# sourceMappingURL=generate-unit-tests.js.map
@@ -33,25 +33,25 @@ export async function mapDependencies(args) {
33
33
  const schemaExists = await fs.access(schemaPath).then(() => true).catch(() => false);
34
34
  const analysisExists = await fs.access(analysisPath).then(() => true).catch(() => false);
35
35
  if (!schemaExists) {
36
- return `🍞 CodeBakers: Phase 2C - Comprehensive Dependency Mapping
37
-
38
- ❌ BLOCKER: Schema file not found
39
-
40
- Expected location: ${schemaPath}
41
-
42
- Phase 2C requires Phase 2B schema to map dependencies.
43
-
36
+ return `🍞 CodeBakers: Phase 2C - Comprehensive Dependency Mapping
37
+
38
+ ❌ BLOCKER: Schema file not found
39
+
40
+ Expected location: ${schemaPath}
41
+
42
+ Phase 2C requires Phase 2B schema to map dependencies.
43
+
44
44
  Next step: Run Phase 2B first (use tool: codebakers_generate_schema)`;
45
45
  }
46
46
  if (!analysisExists) {
47
- return `🍞 CodeBakers: Phase 2C - Comprehensive Dependency Mapping
48
-
49
- ❌ BLOCKER: Mock analysis file not found
50
-
51
- Expected location: ${analysisPath}
52
-
53
- Phase 2C requires Phase 2A analysis to map dependencies.
54
-
47
+ return `🍞 CodeBakers: Phase 2C - Comprehensive Dependency Mapping
48
+
49
+ ❌ BLOCKER: Mock analysis file not found
50
+
51
+ Expected location: ${analysisPath}
52
+
53
+ Phase 2C requires Phase 2A analysis to map dependencies.
54
+
55
55
  Next step: Run Phase 2A first (use tool: codebakers_analyze_mockups_deep)`;
56
56
  }
57
57
  // 2. READ SCHEMA AND ANALYSIS
@@ -70,39 +70,39 @@ Next step: Run Phase 2A first (use tool: codebakers_analyze_mockups_deep)`;
70
70
  console.error(`✓ Dependency map written: ${mapPath}`);
71
71
  // 5. GENERATE SUMMARY
72
72
  const summary = generateDependencyMapSummary(dependencyMap);
73
- return `🍞 CodeBakers: Phase 2C - Comprehensive Dependency Mapping Complete
74
-
75
- ${summary}
76
-
77
- 📄 Dependency map written to: .codebakers/DEPENDENCY-MAP.md
78
-
79
- This map includes:
80
- ✓ Every read dependency (component → store → query)
81
- ✓ Every write dependency (mutation → updates → cascades)
82
- ✓ Every store-to-store connection
83
- ✓ Every cascade effect
84
- ✓ Every critical path (mutations that affect multiple stores)
85
-
86
- CRITICAL: Use this map BEFORE implementing ANY mutation
87
- → Check DEPENDENCY-MAP.md to see ALL stores that must be updated
88
- → This prevents stale UI bugs from moment 1
89
-
90
- Next steps:
91
- 1. Review DEPENDENCY-MAP.md to verify all connections mapped
92
- 2. Ready for Phase 3: Foundation Build
73
+ return `🍞 CodeBakers: Phase 2C - Comprehensive Dependency Mapping Complete
74
+
75
+ ${summary}
76
+
77
+ 📄 Dependency map written to: .codebakers/DEPENDENCY-MAP.md
78
+
79
+ This map includes:
80
+ ✓ Every read dependency (component → store → query)
81
+ ✓ Every write dependency (mutation → updates → cascades)
82
+ ✓ Every store-to-store connection
83
+ ✓ Every cascade effect
84
+ ✓ Every critical path (mutations that affect multiple stores)
85
+
86
+ CRITICAL: Use this map BEFORE implementing ANY mutation
87
+ → Check DEPENDENCY-MAP.md to see ALL stores that must be updated
88
+ → This prevents stale UI bugs from moment 1
89
+
90
+ Next steps:
91
+ 1. Review DEPENDENCY-MAP.md to verify all connections mapped
92
+ 2. Ready for Phase 3: Foundation Build
93
93
  3. REMEMBER: Check dependency map before implementing ANY mutation`;
94
94
  }
95
95
  catch (error) {
96
96
  console.error('Error during dependency mapping:', error);
97
- return `🍞 CodeBakers: Phase 2C - Comprehensive Dependency Mapping Failed
98
-
99
- Error: ${error instanceof Error ? error.message : String(error)}
100
-
101
- Please check:
102
- - Schema file and analysis file exist and are readable
103
- - Files contain valid content
104
- - File permissions allow writing
105
-
97
+ return `🍞 CodeBakers: Phase 2C - Comprehensive Dependency Mapping Failed
98
+
99
+ Error: ${error instanceof Error ? error.message : String(error)}
100
+
101
+ Please check:
102
+ - Schema file and analysis file exist and are readable
103
+ - Files contain valid content
104
+ - File permissions allow writing
105
+
106
106
  If issue persists, log to ERROR-LOG.md and request human assistance.`;
107
107
  }
108
108
  }
@@ -39,8 +39,8 @@ export async function runInterview(args) {
39
39
  }
40
40
  catch (error) {
41
41
  console.error('Error during interview:', error);
42
- return `🍞 CodeBakers: Interview Failed
43
-
42
+ return `🍞 CodeBakers: Interview Failed
43
+
44
44
  Error: ${error instanceof Error ? error.message : String(error)}`;
45
45
  }
46
46
  }
@@ -156,67 +156,67 @@ async function generateInterviewResult(description, context) {
156
156
  };
157
157
  }
158
158
  async function writeProjectProfile(cwd, result) {
159
- const content = `# Project Profile
160
-
161
- **Project:** ${result.project_name}
162
- **Type:** ${result.project_type}
163
- **Generated:** ${new Date().toISOString()}
164
-
165
- ## Core Entities
166
-
167
- ${result.core_entities.map(e => `- ${e}`).join('\n')}
168
-
169
- ## Tech Stack
170
-
171
- - **Database:** ${result.tech_stack.database}
172
- - **Backend:** ${result.tech_stack.backend}
173
- - **Frontend:** ${result.tech_stack.frontend}
174
- - **Auth:** ${result.tech_stack.auth}
175
-
176
- ## External Services
177
-
178
- ${result.external_services.length > 0 ? result.external_services.map(s => `- ${s.name} (${s.purpose})`).join('\n') : 'None'}
179
-
180
- ## Domain Context
181
-
182
- Domain: ${result.project_type}
183
-
184
- Auto-loaded domain file: agents/domains/${result.project_type}.md (if exists)
185
-
186
- This provides domain-specific expectations:
187
- - Feature defaults
188
- - Field display logic
189
- - UX patterns
190
- - Mutation patterns
159
+ const content = `# Project Profile
160
+
161
+ **Project:** ${result.project_name}
162
+ **Type:** ${result.project_type}
163
+ **Generated:** ${new Date().toISOString()}
164
+
165
+ ## Core Entities
166
+
167
+ ${result.core_entities.map(e => `- ${e}`).join('\n')}
168
+
169
+ ## Tech Stack
170
+
171
+ - **Database:** ${result.tech_stack.database}
172
+ - **Backend:** ${result.tech_stack.backend}
173
+ - **Frontend:** ${result.tech_stack.frontend}
174
+ - **Auth:** ${result.tech_stack.auth}
175
+
176
+ ## External Services
177
+
178
+ ${result.external_services.length > 0 ? result.external_services.map(s => `- ${s.name} (${s.purpose})`).join('\n') : 'None'}
179
+
180
+ ## Domain Context
181
+
182
+ Domain: ${result.project_type}
183
+
184
+ Auto-loaded domain file: agents/domains/${result.project_type}.md (if exists)
185
+
186
+ This provides domain-specific expectations:
187
+ - Feature defaults
188
+ - Field display logic
189
+ - UX patterns
190
+ - Mutation patterns
191
191
  `;
192
192
  await fs.writeFile(path.join(cwd, 'project-profile.md'), content, 'utf-8');
193
193
  }
194
194
  async function writeFlows(cwd, result) {
195
- const content = `# User Flows
196
-
197
- **Project:** ${result.project_name}
198
- **Total flows:** ${result.user_flows.length}
199
-
200
- ${result.user_flows.map((flow, i) => `
201
- ## ${i + 1}. ${flow.name} (${flow.priority})
202
-
203
- **Description:** ${flow.description}
204
-
205
- **Steps:**
206
- ${flow.steps.map((s, j) => `${j + 1}. ${s}`).join('\n')}
207
-
208
- **Status:** ${flow.status}
209
- `).join('\n')}
210
-
211
- ## Flow Status Summary
212
-
213
- - **P0 (Critical):** ${result.user_flows.filter(f => f.priority === 'P0').length} flows
214
- - **P1 (Important):** ${result.user_flows.filter(f => f.priority === 'P1').length} flows
215
- - **P2 (Nice to have):** ${result.user_flows.filter(f => f.priority === 'P2').length} flows
216
-
217
- **Pending:** ${result.user_flows.filter(f => f.status === 'pending').length}
218
- **In progress:** ${result.user_flows.filter(f => f.status === 'in_progress').length}
219
- **Complete:** ${result.user_flows.filter(f => f.status === 'complete').length}
195
+ const content = `# User Flows
196
+
197
+ **Project:** ${result.project_name}
198
+ **Total flows:** ${result.user_flows.length}
199
+
200
+ ${result.user_flows.map((flow, i) => `
201
+ ## ${i + 1}. ${flow.name} (${flow.priority})
202
+
203
+ **Description:** ${flow.description}
204
+
205
+ **Steps:**
206
+ ${flow.steps.map((s, j) => `${j + 1}. ${s}`).join('\n')}
207
+
208
+ **Status:** ${flow.status}
209
+ `).join('\n')}
210
+
211
+ ## Flow Status Summary
212
+
213
+ - **P0 (Critical):** ${result.user_flows.filter(f => f.priority === 'P0').length} flows
214
+ - **P1 (Important):** ${result.user_flows.filter(f => f.priority === 'P1').length} flows
215
+ - **P2 (Nice to have):** ${result.user_flows.filter(f => f.priority === 'P2').length} flows
216
+
217
+ **Pending:** ${result.user_flows.filter(f => f.status === 'pending').length}
218
+ **In progress:** ${result.user_flows.filter(f => f.status === 'in_progress').length}
219
+ **Complete:** ${result.user_flows.filter(f => f.status === 'complete').length}
220
220
  `;
221
221
  await fs.writeFile(path.join(cwd, 'FLOWS.md'), content, 'utf-8');
222
222
  }
@@ -224,95 +224,95 @@ async function writeCredentialsNeeded(cwd, result) {
224
224
  if (result.external_services.length === 0) {
225
225
  return; // No credentials needed
226
226
  }
227
- const content = `# Credentials Needed
228
-
229
- **Project:** ${result.project_name}
230
-
231
- ${result.external_services.map(service => `
232
- ## ${service.name}
233
-
234
- **Purpose:** ${service.purpose}
235
- **Auth method:** ${service.auth_method}
236
-
237
- **Environment variables needed:**
238
-
239
- ${service.credentials_needed.map(cred => `\`\`\`bash
240
- ${cred}=[your-${cred.toLowerCase()}]
241
- \`\`\``).join('\n\n')}
242
-
243
- Add to \`.env.local\`:
244
- \`\`\`
245
- ${service.credentials_needed.map(cred => `${cred}=`).join('\n')}
246
- \`\`\`
247
-
248
- **Setup instructions:** See INTEGRATION-CONFIG.md for ${service.name}
249
- `).join('\n')}
227
+ const content = `# Credentials Needed
228
+
229
+ **Project:** ${result.project_name}
230
+
231
+ ${result.external_services.map(service => `
232
+ ## ${service.name}
233
+
234
+ **Purpose:** ${service.purpose}
235
+ **Auth method:** ${service.auth_method}
236
+
237
+ **Environment variables needed:**
238
+
239
+ ${service.credentials_needed.map(cred => `\`\`\`bash
240
+ ${cred}=[your-${cred.toLowerCase()}]
241
+ \`\`\``).join('\n\n')}
242
+
243
+ Add to \`.env.local\`:
244
+ \`\`\`
245
+ ${service.credentials_needed.map(cred => `${cred}=`).join('\n')}
246
+ \`\`\`
247
+
248
+ **Setup instructions:** See INTEGRATION-CONFIG.md for ${service.name}
249
+ `).join('\n')}
250
250
  `;
251
251
  const credsPath = path.join(cwd, '.codebakers', 'CREDENTIALS-NEEDED.md');
252
252
  await fs.mkdir(path.dirname(credsPath), { recursive: true });
253
253
  await fs.writeFile(credsPath, content, 'utf-8');
254
254
  }
255
255
  async function initializeBrain(cwd, result) {
256
- const content = `# BRAIN.md
257
-
258
- **Project:** ${result.project_name}
259
- **Type:** ${result.project_type}
260
- **Initialized:** ${new Date().toISOString()}
261
-
262
- ## Project Intent
263
-
264
- Building: ${result.project_name} (${result.project_type})
265
-
266
- Core entities: ${result.core_entities.join(', ')}
267
-
268
- ## Tech Stack (CodeBakers Standard)
269
-
270
- - Database: ${result.tech_stack.database}
271
- - Backend: ${result.tech_stack.backend}
272
- - Frontend: ${result.tech_stack.frontend}
273
- - Auth: ${result.tech_stack.auth}
274
-
275
- ## Current Phase
276
-
277
- **Phase:** 1 (Interview complete)
278
-
279
- **Next:** Phase 2 (Deep Analysis)
280
-
281
- Recommended next steps:
282
- 1. codebakers_validate_mockups (if mockups provided)
283
- 2. codebakers_analyze_mockups (after validation)
284
- 3. codebakers_generate_schema
285
- 4. codebakers_map_dependencies
286
-
287
- ## Architectural Decisions
288
-
289
- 1. **Stack:** Supabase + Next.js 14 (CodeBakers standard - not negotiable)
290
- 2. **Auth:** Supabase Auth only (no NextAuth, Auth0, Clerk)
291
- 3. **State:** Zustand stores (mapped with codebakers_map_dependencies)
292
- 4. **Testing:** Vitest (unit) + Playwright (E2E)
293
- 5. **Versioning:** Exact versions only (no ^ or ~)
294
-
295
- ## External Services
296
-
297
- ${result.external_services.length > 0 ? result.external_services.map(s => `- ${s.name}`).join('\n') : 'None'}
298
-
299
- ${result.external_services.length > 0 ? '\n**Action required:** Add credentials to .env.local (see CREDENTIALS-NEEDED.md)' : ''}
300
-
301
- ## Flows
302
-
303
- Total: ${result.user_flows.length} user flows
304
-
305
- See FLOWS.md for complete list.
306
-
307
- ## Memory Files
308
-
309
- - ✅ project-profile.md (created)
310
- - ✅ FLOWS.md (created)
311
- - ✅ BRAIN.md (this file)
312
- ${result.external_services.length > 0 ? '- ✅ CREDENTIALS-NEEDED.md (created)' : ''}
313
- - ⏳ DEPENDENCY-MAP.md (pending - run after schema)
314
- - ⏳ BUILD-LOG.md (pending - starts with first build)
315
- - ⏳ ERROR-LOG.md (pending - starts with first error)
256
+ const content = `# BRAIN.md
257
+
258
+ **Project:** ${result.project_name}
259
+ **Type:** ${result.project_type}
260
+ **Initialized:** ${new Date().toISOString()}
261
+
262
+ ## Project Intent
263
+
264
+ Building: ${result.project_name} (${result.project_type})
265
+
266
+ Core entities: ${result.core_entities.join(', ')}
267
+
268
+ ## Tech Stack (CodeBakers Standard)
269
+
270
+ - Database: ${result.tech_stack.database}
271
+ - Backend: ${result.tech_stack.backend}
272
+ - Frontend: ${result.tech_stack.frontend}
273
+ - Auth: ${result.tech_stack.auth}
274
+
275
+ ## Current Phase
276
+
277
+ **Phase:** 1 (Interview complete)
278
+
279
+ **Next:** Phase 2 (Deep Analysis)
280
+
281
+ Recommended next steps:
282
+ 1. codebakers_validate_mockups (if mockups provided)
283
+ 2. codebakers_analyze_mockups (after validation)
284
+ 3. codebakers_generate_schema
285
+ 4. codebakers_map_dependencies
286
+
287
+ ## Architectural Decisions
288
+
289
+ 1. **Stack:** Supabase + Next.js 14 (CodeBakers standard - not negotiable)
290
+ 2. **Auth:** Supabase Auth only (no NextAuth, Auth0, Clerk)
291
+ 3. **State:** Zustand stores (mapped with codebakers_map_dependencies)
292
+ 4. **Testing:** Vitest (unit) + Playwright (E2E)
293
+ 5. **Versioning:** Exact versions only (no ^ or ~)
294
+
295
+ ## External Services
296
+
297
+ ${result.external_services.length > 0 ? result.external_services.map(s => `- ${s.name}`).join('\n') : 'None'}
298
+
299
+ ${result.external_services.length > 0 ? '\n**Action required:** Add credentials to .env.local (see CREDENTIALS-NEEDED.md)' : ''}
300
+
301
+ ## Flows
302
+
303
+ Total: ${result.user_flows.length} user flows
304
+
305
+ See FLOWS.md for complete list.
306
+
307
+ ## Memory Files
308
+
309
+ - ✅ project-profile.md (created)
310
+ - ✅ FLOWS.md (created)
311
+ - ✅ BRAIN.md (this file)
312
+ ${result.external_services.length > 0 ? '- ✅ CREDENTIALS-NEEDED.md (created)' : ''}
313
+ - ⏳ DEPENDENCY-MAP.md (pending - run after schema)
314
+ - ⏳ BUILD-LOG.md (pending - starts with first build)
315
+ - ⏳ ERROR-LOG.md (pending - starts with first error)
316
316
  `;
317
317
  const brainPath = path.join(cwd, '.codebakers', 'BRAIN.md');
318
318
  await fs.mkdir(path.dirname(brainPath), { recursive: true });
@@ -148,14 +148,14 @@ async function startOAuthFlow() {
148
148
  // Exchange code for access token
149
149
  const credentials = await exchangeCodeForToken(code);
150
150
  res.writeHead(200, { 'Content-Type': 'text/html' });
151
- res.end(`
152
- <html>
153
- <head><title>GitHub Authentication</title></head>
154
- <body style="font-family: system-ui; max-width: 600px; margin: 100px auto; text-align: center;">
155
- <h1 style="color: #28a745;">✅ Authentication Successful!</h1>
156
- <p>You can close this window and return to your terminal.</p>
157
- </body>
158
- </html>
151
+ res.end(`
152
+ <html>
153
+ <head><title>GitHub Authentication</title></head>
154
+ <body style="font-family: system-ui; max-width: 600px; margin: 100px auto; text-align: center;">
155
+ <h1 style="color: #28a745;">✅ Authentication Successful!</h1>
156
+ <p>You can close this window and return to your terminal.</p>
157
+ </body>
158
+ </html>
159
159
  `);
160
160
  server.close();
161
161
  resolve(credentials);
@@ -134,14 +134,14 @@ async function startOAuthFlow() {
134
134
  // Exchange code for access token
135
135
  const credentials = await exchangeCodeForToken(code);
136
136
  res.writeHead(200, { 'Content-Type': 'text/html' });
137
- res.end(`
138
- <html>
139
- <head><title>Supabase Authentication</title></head>
140
- <body style="font-family: system-ui; max-width: 600px; margin: 100px auto; text-align: center;">
141
- <h1 style="color: #3ECF8E;">✅ Authentication Successful!</h1>
142
- <p>You can close this window and return to your terminal.</p>
143
- </body>
144
- </html>
137
+ res.end(`
138
+ <html>
139
+ <head><title>Supabase Authentication</title></head>
140
+ <body style="font-family: system-ui; max-width: 600px; margin: 100px auto; text-align: center;">
141
+ <h1 style="color: #3ECF8E;">✅ Authentication Successful!</h1>
142
+ <p>You can close this window and return to your terminal.</p>
143
+ </body>
144
+ </html>
145
145
  `);
146
146
  server.close();
147
147
  resolve(credentials);
@@ -169,14 +169,14 @@ async function startOAuthFlow() {
169
169
  // Exchange code for access token
170
170
  const credentials = await exchangeCodeForToken(code);
171
171
  res.writeHead(200, { 'Content-Type': 'text/html' });
172
- res.end(`
173
- <html>
174
- <head><title>Vercel Authentication</title></head>
175
- <body style="font-family: system-ui; max-width: 600px; margin: 100px auto; text-align: center;">
176
- <h1 style="color: #000;">✅ Authentication Successful!</h1>
177
- <p>You can close this window and return to your terminal.</p>
178
- </body>
179
- </html>
172
+ res.end(`
173
+ <html>
174
+ <head><title>Vercel Authentication</title></head>
175
+ <body style="font-family: system-ui; max-width: 600px; margin: 100px auto; text-align: center;">
176
+ <h1 style="color: #000;">✅ Authentication Successful!</h1>
177
+ <p>You can close this window and return to your terminal.</p>
178
+ </body>
179
+ </html>
180
180
  `);
181
181
  server.close();
182
182
  resolve(credentials);