hale-commenting-system 1.0.7 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/cli/dist/index.js CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { Command } from "commander";
5
- import chalk12 from "chalk";
5
+ import chalk4 from "chalk";
6
6
 
7
7
  // src/commands/init.ts
8
- import chalk10 from "chalk";
8
+ import chalk3 from "chalk";
9
9
  import ora from "ora";
10
- import inquirer3 from "inquirer";
10
+ import inquirer from "inquirer";
11
11
 
12
12
  // src/utils/detect.ts
13
13
  import fs from "fs/promises";
@@ -52,177 +52,20 @@ async function detectBuildTool(cwd, deps) {
52
52
  }
53
53
  return "Unknown";
54
54
  }
55
- async function detectPlatform(cwd) {
56
- try {
57
- await fs.access(path.join(cwd, "vercel.json"));
58
- return "vercel";
59
- } catch {
60
- }
61
- try {
62
- await fs.access(path.join(cwd, "netlify.toml"));
63
- return "netlify";
64
- } catch {
65
- }
66
- return null;
67
- }
68
55
 
69
56
  // src/utils/logger.ts
70
57
  import chalk from "chalk";
71
58
  function printWelcome() {
72
- console.log(chalk.bold.cyan("\n\u{1F680} Hale Commenting System Setup Wizard\n"));
73
- }
74
- function printSetupInstructions(platform, project) {
75
- console.log(chalk.bold("\n\u{1F4DD} Manual Setup Required:\n"));
76
- console.log(chalk.cyan("1. Add the CommentProvider to your App.tsx:\n"));
77
- console.log(chalk.white(`import {
78
- CommentProvider,
79
- VersionProvider,
80
- GitHubAuthProvider,
81
- GitLabAuthProvider,
82
- CommentOverlay,
83
- CommentDrawer
84
- } from 'hale-commenting-system';
85
- import { BrowserRouter as Router } from 'react-router-dom';
86
- import haleCommentsConfig from '../../hale-comments.config.json';
87
- import React from 'react';
88
-
89
- function App() {
90
- const [selectedThreadId, setSelectedThreadId] = React.useState<string | null>(null);
91
-
92
- return (
93
- <Router>
94
- <GitHubAuthProvider config={haleCommentsConfig}>
95
- <GitLabAuthProvider>
96
- <VersionProvider>
97
- <CommentProvider>
98
- <CommentDrawer
99
- selectedThreadId={selectedThreadId}
100
- onThreadSelect={setSelectedThreadId}
101
- >
102
- <CommentOverlay
103
- selectedThreadId={selectedThreadId}
104
- onThreadSelect={setSelectedThreadId}
105
- />
106
- {/* Your app content */}
107
- </CommentDrawer>
108
- </CommentProvider>
109
- </VersionProvider>
110
- </GitLabAuthProvider>
111
- </GitHubAuthProvider>
112
- </Router>
113
- );
114
- }
115
- `));
116
- console.log(chalk.cyan("\n2. Deploy your serverless functions:\n"));
117
- if (platform === "vercel") {
118
- console.log(chalk.white(" vercel --prod"));
119
- } else if (platform === "netlify") {
120
- console.log(chalk.white(" netlify deploy --prod"));
121
- } else {
122
- console.log(chalk.white(" Follow your platform's deployment guide"));
123
- }
124
- console.log(chalk.cyan("\n3. Update your GitHub OAuth App callback URL:\n"));
125
- console.log(chalk.white(" https://your-domain.com/api/github-oauth-callback\n"));
126
- }
127
- function printSuccess() {
128
- console.log(chalk.green.bold("\n\u2713 Setup complete!\n"));
129
- }
130
- function printNextSteps() {
131
- console.log(chalk.cyan("\nNext steps:"));
132
- console.log(chalk.white("1. Review generated files in your project"));
133
- console.log(chalk.white("2. Add the CommentProvider to your App.tsx (see instructions above)"));
134
- console.log(chalk.white("3. Deploy your serverless functions"));
135
- console.log(chalk.white("4. Run: npm run dev\n"));
136
- console.log(chalk.dim('Run "hale-commenting-system validate" to verify your setup.\n'));
59
+ console.log(chalk.bold.cyan("\n\u{1F680} Hale Commenting System - Local Setup\n"));
60
+ console.log(chalk.dim("Setting up a localStorage-based commenting system for your app.\n"));
137
61
  }
138
62
  function printWarning(message) {
139
63
  console.log(chalk.yellow(`\u26A0\uFE0F ${message}`));
140
64
  }
141
65
 
142
- // src/prompts/platform.ts
143
- import inquirer from "inquirer";
144
- import chalk2 from "chalk";
145
- async function promptPlatform(projectRoot) {
146
- const detectedPlatform = await detectPlatform(projectRoot);
147
- if (detectedPlatform) {
148
- console.log(chalk2.green(`\u2713 Detected ${detectedPlatform} configuration`));
149
- const { usePlatform } = await inquirer.prompt([
150
- {
151
- type: "confirm",
152
- name: "usePlatform",
153
- message: `Use ${detectedPlatform} for serverless functions?`,
154
- default: true
155
- }
156
- ]);
157
- if (usePlatform) {
158
- return detectedPlatform;
159
- }
160
- }
161
- const { platform } = await inquirer.prompt([
162
- {
163
- type: "list",
164
- name: "platform",
165
- message: "Select your deployment platform:",
166
- choices: [
167
- { name: "Local development (runs locally only)", value: "local" },
168
- { name: "Vercel", value: "vercel" },
169
- { name: "Netlify", value: "netlify" },
170
- { name: "Manual (I will configure myself)", value: "manual" }
171
- ]
172
- }
173
- ]);
174
- return platform;
175
- }
176
-
177
- // src/prompts/github.ts
178
- import inquirer2 from "inquirer";
179
- import chalk3 from "chalk";
180
- async function promptGitHubConfig(skipPrompts = false) {
181
- if (skipPrompts) {
182
- console.log(chalk3.yellow("Using default/environment values..."));
183
- return {
184
- clientId: process.env.GITHUB_CLIENT_ID || "",
185
- clientSecret: process.env.GITHUB_CLIENT_SECRET || "",
186
- owner: process.env.GITHUB_OWNER || "",
187
- repo: process.env.GITHUB_REPO || ""
188
- };
189
- }
190
- console.log(chalk3.bold("\n\u{1F4CB} GitHub OAuth Configuration\n"));
191
- console.log(chalk3.dim("You need to create a GitHub OAuth App:"));
192
- console.log(chalk3.dim("https://github.com/settings/developers\n"));
193
- const answers = await inquirer2.prompt([
194
- {
195
- type: "input",
196
- name: "clientId",
197
- message: "GitHub OAuth Client ID:",
198
- validate: (input) => input.length > 0 || "Client ID is required"
199
- },
200
- {
201
- type: "password",
202
- name: "clientSecret",
203
- message: "GitHub OAuth Client Secret:",
204
- mask: "*",
205
- validate: (input) => input.length > 0 || "Client Secret is required"
206
- },
207
- {
208
- type: "input",
209
- name: "owner",
210
- message: "GitHub Repository Owner (user or org):",
211
- validate: (input) => input.length > 0 || "Owner is required"
212
- },
213
- {
214
- type: "input",
215
- name: "repo",
216
- message: "GitHub Repository Name:",
217
- validate: (input) => input.length > 0 || "Repository name is required"
218
- }
219
- ]);
220
- return answers;
221
- }
222
-
223
- // src/generators/serverless.ts
66
+ // src/generators/code.ts
224
67
  import path2 from "path";
225
- import chalk4 from "chalk";
68
+ import chalk2 from "chalk";
226
69
 
227
70
  // src/utils/fs.ts
228
71
  import fs2 from "fs/promises";
@@ -234,12 +77,6 @@ async function fileExists(filePath) {
234
77
  return false;
235
78
  }
236
79
  }
237
- async function ensureDir(dirPath) {
238
- await fs2.mkdir(dirPath, { recursive: true });
239
- }
240
- async function appendToFile(filePath, content) {
241
- await fs2.appendFile(filePath, content, "utf-8");
242
- }
243
80
  async function readFile(filePath) {
244
81
  return fs2.readFile(filePath, "utf-8");
245
82
  }
@@ -247,371 +84,7 @@ async function writeFile(filePath, content) {
247
84
  await fs2.writeFile(filePath, content, "utf-8");
248
85
  }
249
86
 
250
- // src/templates/vercel-auth.ts
251
- var vercelAuthLoginTemplate = `import { VercelRequest, VercelResponse } from '@vercel/node';
252
-
253
- export default async function handler(req: VercelRequest, res: VercelResponse): Promise<void> {
254
- try {
255
- const clientId = process.env.GITHUB_CLIENT_ID || process.env.VITE_GITHUB_CLIENT_ID;
256
-
257
- if (!clientId) {
258
- res.status(500).json({ error: 'GitHub OAuth not configured - missing client ID' });
259
- return;
260
- }
261
-
262
- // Get the base URL from the request
263
- const protocol = (req.headers['x-forwarded-proto'] as string) || 'https';
264
- const host = (req.headers.host || req.headers['x-forwarded-host']) as string;
265
-
266
- if (!host) {
267
- res.status(500).json({ error: 'Could not determine host' });
268
- return;
269
- }
270
-
271
- const baseUrl = \`\${protocol}://\${host}\`;
272
- const redirectUri = \`\${baseUrl}/api/github-oauth-callback\`;
273
-
274
- // Redirect to GitHub OAuth
275
- // Scope: public_repo allows read/write access to public repositories only
276
- const githubAuthUrl = \`https://github.com/login/oauth/authorize?client_id=\${clientId}&redirect_uri=\${encodeURIComponent(redirectUri)}&scope=public_repo\`;
277
-
278
- res.redirect(302, githubAuthUrl);
279
- } catch (error: any) {
280
- res.status(500).json({
281
- error: 'Internal server error',
282
- details: error.message
283
- });
284
- }
285
- }
286
- `;
287
- var vercelAuthCallbackTemplate = `import { VercelRequest, VercelResponse } from '@vercel/node';
288
- import axios from 'axios';
289
-
290
- export default async function handler(req: VercelRequest, res: VercelResponse): Promise<void> {
291
- try {
292
- const code = req.query.code as string;
293
- const clientId = process.env.GITHUB_CLIENT_ID || process.env.VITE_GITHUB_CLIENT_ID;
294
- const clientSecret = process.env.GITHUB_CLIENT_SECRET;
295
-
296
- if (!code) {
297
- res.status(400).json({ error: 'No code provided' });
298
- return;
299
- }
300
-
301
- if (!clientId || !clientSecret) {
302
- res.status(500).json({ error: 'GitHub OAuth not configured properly' });
303
- return;
304
- }
305
-
306
- // Exchange code for access token
307
- const tokenResponse = await axios.post(
308
- 'https://github.com/login/oauth/access_token',
309
- {
310
- client_id: clientId,
311
- client_secret: clientSecret,
312
- code,
313
- },
314
- {
315
- headers: {
316
- Accept: 'application/json',
317
- },
318
- }
319
- );
320
-
321
- const accessToken = tokenResponse.data.access_token;
322
-
323
- if (!accessToken) {
324
- throw new Error('No access token received');
325
- }
326
-
327
- // Get user info
328
- const userResponse = await axios.get('https://api.github.com/user', {
329
- headers: {
330
- Authorization: \`token \${accessToken}\`,
331
- },
332
- });
333
-
334
- const user = userResponse.data;
335
-
336
- // Redirect back to the app with token in URL fragment (client-side only)
337
- const protocol = req.headers['x-forwarded-proto'] || 'https';
338
- const host = req.headers.host || req.headers['x-forwarded-host'];
339
- const baseUrl = \`\${protocol}://\${host}\`;
340
- const redirectUrl = \`\${baseUrl}/#/auth-callback?token=\${accessToken}&login=\${user.login}&avatar=\${encodeURIComponent(user.avatar_url)}\`;
341
-
342
- res.redirect(302, redirectUrl);
343
- } catch (error: any) {
344
- res.status(500).json({
345
- error: 'Failed to exchange code for token',
346
- details: error.message,
347
- });
348
- }
349
- }
350
- `;
351
-
352
- // src/templates/netlify-auth.ts
353
- var netlifyAuthLoginTemplate = `import { Handler, HandlerEvent, HandlerContext } from '@netlify/functions';
354
- import axios from 'axios';
355
-
356
- const handler: Handler = async (event: HandlerEvent, context: HandlerContext) => {
357
- try {
358
- const clientId = process.env.GITHUB_CLIENT_ID || process.env.VITE_GITHUB_CLIENT_ID;
359
-
360
- if (!clientId) {
361
- return {
362
- statusCode: 500,
363
- body: JSON.stringify({ error: 'GitHub OAuth not configured - missing client ID' })
364
- };
365
- }
366
-
367
- // Get the base URL from the request
368
- const protocol = event.headers['x-forwarded-proto'] || 'https';
369
- const host = event.headers.host;
370
-
371
- if (!host) {
372
- return {
373
- statusCode: 500,
374
- body: JSON.stringify({ error: 'Could not determine host' })
375
- };
376
- }
377
-
378
- const baseUrl = \`\${protocol}://\${host}\`;
379
- const redirectUri = \`\${baseUrl}/.netlify/functions/github-oauth-callback\`;
380
-
381
- // Redirect to GitHub OAuth
382
- const githubAuthUrl = \`https://github.com/login/oauth/authorize?client_id=\${clientId}&redirect_uri=\${encodeURIComponent(redirectUri)}&scope=public_repo\`;
383
-
384
- return {
385
- statusCode: 302,
386
- headers: {
387
- Location: githubAuthUrl
388
- },
389
- body: ''
390
- };
391
- } catch (error: any) {
392
- return {
393
- statusCode: 500,
394
- body: JSON.stringify({
395
- error: 'Internal server error',
396
- details: error.message
397
- })
398
- };
399
- }
400
- };
401
-
402
- export { handler };
403
- `;
404
- var netlifyAuthCallbackTemplate = `import { Handler, HandlerEvent, HandlerContext } from '@netlify/functions';
405
- import axios from 'axios';
406
-
407
- const handler: Handler = async (event: HandlerEvent, context: HandlerContext) => {
408
- try {
409
- const code = event.queryStringParameters?.code;
410
- const clientId = process.env.GITHUB_CLIENT_ID || process.env.VITE_GITHUB_CLIENT_ID;
411
- const clientSecret = process.env.GITHUB_CLIENT_SECRET;
412
-
413
- if (!code) {
414
- return {
415
- statusCode: 400,
416
- body: JSON.stringify({ error: 'No code provided' })
417
- };
418
- }
419
-
420
- if (!clientId || !clientSecret) {
421
- return {
422
- statusCode: 500,
423
- body: JSON.stringify({ error: 'GitHub OAuth not configured properly' })
424
- };
425
- }
426
-
427
- // Exchange code for access token
428
- const tokenResponse = await axios.post(
429
- 'https://github.com/login/oauth/access_token',
430
- {
431
- client_id: clientId,
432
- client_secret: clientSecret,
433
- code,
434
- },
435
- {
436
- headers: {
437
- Accept: 'application/json',
438
- },
439
- }
440
- );
441
-
442
- const accessToken = tokenResponse.data.access_token;
443
-
444
- if (!accessToken) {
445
- throw new Error('No access token received');
446
- }
447
-
448
- // Get user info
449
- const userResponse = await axios.get('https://api.github.com/user', {
450
- headers: {
451
- Authorization: \`token \${accessToken}\`,
452
- },
453
- });
454
-
455
- const user = userResponse.data;
456
-
457
- // Redirect back to the app with token in URL fragment
458
- const protocol = event.headers['x-forwarded-proto'] || 'https';
459
- const host = event.headers.host;
460
- const baseUrl = \`\${protocol}://\${host}\`;
461
- const redirectUrl = \`\${baseUrl}/#/auth-callback?token=\${accessToken}&login=\${user.login}&avatar=\${encodeURIComponent(user.avatar_url)}\`;
462
-
463
- return {
464
- statusCode: 302,
465
- headers: {
466
- Location: redirectUrl
467
- },
468
- body: ''
469
- };
470
- } catch (error: any) {
471
- return {
472
- statusCode: 500,
473
- body: JSON.stringify({
474
- error: 'Failed to exchange code for token',
475
- details: error.message,
476
- })
477
- };
478
- }
479
- };
480
-
481
- export { handler };
482
- `;
483
-
484
- // src/generators/serverless.ts
485
- async function generateServerless(platform, projectRoot) {
486
- if (platform === "vercel" || platform === "local") {
487
- const apiDir = path2.join(projectRoot, "api");
488
- await ensureDir(apiDir);
489
- await writeFile(
490
- path2.join(apiDir, "github-oauth-login.ts"),
491
- vercelAuthLoginTemplate
492
- );
493
- await writeFile(
494
- path2.join(apiDir, "github-oauth-callback.ts"),
495
- vercelAuthCallbackTemplate
496
- );
497
- console.log(chalk4.dim(" \u2192 Created api/github-oauth-login.ts"));
498
- console.log(chalk4.dim(" \u2192 Created api/github-oauth-callback.ts"));
499
- } else if (platform === "netlify") {
500
- const functionsDir = path2.join(projectRoot, "netlify", "functions");
501
- await ensureDir(functionsDir);
502
- await writeFile(
503
- path2.join(functionsDir, "github-oauth-login.ts"),
504
- netlifyAuthLoginTemplate
505
- );
506
- await writeFile(
507
- path2.join(functionsDir, "github-oauth-callback.ts"),
508
- netlifyAuthCallbackTemplate
509
- );
510
- console.log(chalk4.dim(" \u2192 Created netlify/functions/github-oauth-login.ts"));
511
- console.log(chalk4.dim(" \u2192 Created netlify/functions/github-oauth-callback.ts"));
512
- } else {
513
- console.log(chalk4.yellow(" Manual platform selected. You must create serverless functions yourself."));
514
- console.log(chalk4.dim(" See: https://github.com/apollo/commenting-system/blob/main/docs/manual-setup.md"));
515
- }
516
- }
517
-
518
- // src/generators/env.ts
519
- import path3 from "path";
520
- import chalk5 from "chalk";
521
- async function generateEnvFiles(githubConfig, platform, projectRoot) {
522
- const envContent = `
523
- # Apollo Commenting System Configuration
524
- # Generated by apollo-comments CLI
525
-
526
- # GitHub OAuth
527
- GITHUB_CLIENT_ID=${githubConfig.clientId}
528
- GITHUB_CLIENT_SECRET=${githubConfig.clientSecret}
529
- GITHUB_OWNER=${githubConfig.owner}
530
- GITHUB_REPO=${githubConfig.repo}
531
-
532
- # Vite (if using Vite)
533
- VITE_GITHUB_CLIENT_ID=${githubConfig.clientId}
534
- VITE_GITHUB_OWNER=${githubConfig.owner}
535
- VITE_GITHUB_REPO=${githubConfig.repo}
536
- `;
537
- const envLocalPath = path3.join(projectRoot, ".env.local");
538
- const envPath = path3.join(projectRoot, ".env");
539
- const envLocalExists = await fileExists(envLocalPath);
540
- if (envLocalExists) {
541
- const existingEnv = await readFile(envLocalPath);
542
- if (existingEnv.includes("GITHUB_CLIENT_ID")) {
543
- console.log(chalk5.yellow(" .env.local already contains GitHub config. Skipping..."));
544
- } else {
545
- await appendToFile(envLocalPath, envContent);
546
- console.log(chalk5.dim(" \u2192 Updated .env.local"));
547
- }
548
- } else {
549
- await writeFile(envLocalPath, envContent.trim());
550
- console.log(chalk5.dim(" \u2192 Created .env.local"));
551
- }
552
- const envExampleContent = `# Apollo Commenting System Configuration
553
-
554
- # GitHub OAuth (get from https://github.com/settings/developers)
555
- GITHUB_CLIENT_ID=your_client_id_here
556
- GITHUB_CLIENT_SECRET=your_client_secret_here
557
- GITHUB_OWNER=your_github_org
558
- GITHUB_REPO=your_repo_name
559
-
560
- # Vite (if using Vite)
561
- VITE_GITHUB_CLIENT_ID=your_client_id_here
562
- VITE_GITHUB_OWNER=your_github_org
563
- VITE_GITHUB_REPO=your_repo_name
564
- `;
565
- await writeFile(path3.join(projectRoot, ".env.example"), envExampleContent);
566
- console.log(chalk5.dim(" \u2192 Created .env.example"));
567
- const gitignorePath = path3.join(projectRoot, ".gitignore");
568
- if (await fileExists(gitignorePath)) {
569
- const gitignoreContent = await readFile(gitignorePath);
570
- if (!gitignoreContent.includes(".env.local")) {
571
- await appendToFile(gitignorePath, "\n# Apollo Commenting System\n.env.local\n");
572
- console.log(chalk5.dim(" \u2192 Updated .gitignore"));
573
- }
574
- }
575
- }
576
-
577
- // src/generators/config.ts
578
- import path4 from "path";
579
- import chalk6 from "chalk";
580
- async function generateConfig(githubConfig, platform, projectRoot) {
581
- let redirectUri = "http://localhost:9000/api/github-oauth-callback";
582
- if (platform === "netlify") {
583
- redirectUri = "https://your-domain.com/.netlify/functions/github-oauth-callback";
584
- } else if (platform === "vercel") {
585
- redirectUri = "https://your-domain.com/api/github-oauth-callback";
586
- }
587
- const config = {
588
- version: "1.0.0",
589
- platform,
590
- github: {
591
- owner: githubConfig.owner,
592
- repo: githubConfig.repo,
593
- clientId: githubConfig.clientId
594
- },
595
- redirectUri,
596
- features: {
597
- aiSummarization: true,
598
- versionTracking: true,
599
- gitlabIntegration: false
600
- },
601
- labels: [
602
- { name: "comment", color: "0075ca", description: "User feedback comment" },
603
- { name: "prototype-feedback", color: "d93f0b", description: "Prototype feedback" },
604
- { name: "needs-review", color: "fbca04", description: "Needs team review" }
605
- ]
606
- };
607
- const configPath = path4.join(projectRoot, "hale-comments.config.json");
608
- await writeFile(configPath, JSON.stringify(config, null, 2));
609
- console.log(chalk6.dim(" \u2192 Created hale-comments.config.json"));
610
- }
611
-
612
87
  // src/generators/code.ts
613
- import path5 from "path";
614
- import chalk7 from "chalk";
615
88
  async function integrateProviders(projectRoot) {
616
89
  const possiblePaths = [
617
90
  "src/app/index.tsx",
@@ -621,7 +94,7 @@ async function integrateProviders(projectRoot) {
621
94
  ];
622
95
  let appFilePath = null;
623
96
  for (const p of possiblePaths) {
624
- const fullPath = path5.join(projectRoot, p);
97
+ const fullPath = path2.join(projectRoot, p);
625
98
  if (await fileExists(fullPath)) {
626
99
  appFilePath = fullPath;
627
100
  break;
@@ -636,11 +109,11 @@ async function integrateProviders(projectRoot) {
636
109
  }
637
110
  try {
638
111
  const content = await readFile(appFilePath);
639
- if (content.includes("hale-commenting-system") || content.includes("GitHubAuthProvider")) {
112
+ if (content.includes("hale-commenting-system") || content.includes("CommentProvider")) {
640
113
  return {
641
114
  success: false,
642
115
  filePath: appFilePath,
643
- message: "Providers already integrated or commenting system imports found."
116
+ message: "Commenting system already integrated."
644
117
  };
645
118
  }
646
119
  const modifiedContent = injectProviders(content);
@@ -655,7 +128,7 @@ async function integrateProviders(projectRoot) {
655
128
  return {
656
129
  success: true,
657
130
  filePath: appFilePath,
658
- message: `Successfully integrated providers into ${path5.relative(projectRoot, appFilePath)}`
131
+ message: `Successfully integrated providers into ${path2.relative(projectRoot, appFilePath)}`
659
132
  };
660
133
  } catch (error) {
661
134
  return {
@@ -668,13 +141,9 @@ async function integrateProviders(projectRoot) {
668
141
  function injectProviders(content) {
669
142
  const imports = `import {
670
143
  CommentProvider,
671
- VersionProvider,
672
- GitHubAuthProvider,
673
- GitLabAuthProvider,
674
144
  CommentOverlay,
675
145
  CommentDrawer
676
- } from 'hale-commenting-system';
677
- import haleCommentsConfig from '../../hale-comments.config.json';`;
146
+ } from 'hale-commenting-system';`;
678
147
  const importRegex = /import\s+.*?from\s+['"].*?['"];?/g;
679
148
  const matches = content.match(importRegex);
680
149
  if (!matches || matches.length === 0) {
@@ -685,7 +154,12 @@ import haleCommentsConfig from '../../hale-comments.config.json';`;
685
154
  const insertPosition = lastImportIndex + lastImport.length;
686
155
  content = content.slice(0, insertPosition) + "\n\n" + imports + content.slice(insertPosition);
687
156
  }
688
- const returnMatch = content.match(/return\s*\(?[\s\n]*<(\w+)/);
157
+ let returnMatch = content.match(/return\s*\(?[\s\n]*<(\w+)/);
158
+ let isImplicitReturn = false;
159
+ if (!returnMatch) {
160
+ returnMatch = content.match(/=>\s*\(?[\s\n]*<(\w+)/);
161
+ isImplicitReturn = true;
162
+ }
689
163
  if (!returnMatch) {
690
164
  return content;
691
165
  }
@@ -695,6 +169,18 @@ import haleCommentsConfig from '../../hale-comments.config.json';`;
695
169
  if (closingIndex === -1) {
696
170
  return content;
697
171
  }
172
+ if (isImplicitReturn) {
173
+ const arrowMatch = content.match(/(const\s+\w+.*?=.*?\(\).*?=>\s*)\(/);
174
+ if (arrowMatch) {
175
+ const beforeParen = arrowMatch[1];
176
+ const arrowEndPos = arrowMatch.index + arrowMatch[0].length - 1;
177
+ const closingParenPos = content.indexOf(");", closingIndex);
178
+ if (closingParenPos !== -1) {
179
+ const jsxContent = content.slice(arrowEndPos + 1, closingParenPos);
180
+ content = content.slice(0, arrowEndPos) + " {\n return" + jsxContent + "\n}" + content.slice(closingParenPos + 2);
181
+ }
182
+ }
183
+ }
698
184
  const stateHook = ` const [selectedThreadId, setSelectedThreadId] = React.useState<string | null>(null);
699
185
 
700
186
  `;
@@ -703,9 +189,9 @@ import haleCommentsConfig from '../../hale-comments.config.json';`;
703
189
  const insertPos = functionMatch.index + functionMatch[0].length;
704
190
  content = content.slice(0, insertPos) + "\n" + stateHook + content.slice(insertPos);
705
191
  }
706
- const returnStartMatch = content.match(/return\s*\(/);
192
+ const returnStartMatch = content.match(/return\s*\(?[\s\n]*</);
707
193
  if (returnStartMatch) {
708
- const returnStart = returnStartMatch.index + returnStartMatch[0].length;
194
+ const returnStart = returnStartMatch.index + returnStartMatch[0].length - 1;
709
195
  const returnEnd = content.indexOf(closingTag, returnStart) + closingTag.length;
710
196
  const originalReturn = content.slice(returnStart, returnEnd).trim();
711
197
  const isRouter = originalReturn.match(/^\s*<(Router|BrowserRouter)/);
@@ -717,68 +203,50 @@ import haleCommentsConfig from '../../hale-comments.config.json';`;
717
203
  const innerContent = routerMatch[2];
718
204
  wrappedReturn = `
719
205
  <${routerType}>
720
- <GitHubAuthProvider config={haleCommentsConfig}>
721
- <GitLabAuthProvider>
722
- <VersionProvider>
723
- <CommentProvider>
724
- <CommentDrawer
725
- selectedThreadId={selectedThreadId}
726
- onThreadSelect={setSelectedThreadId}
727
- >
728
- <CommentOverlay
729
- selectedThreadId={selectedThreadId}
730
- onThreadSelect={setSelectedThreadId}
731
- />
732
- ${innerContent.trim()}
733
- </CommentDrawer>
734
- </CommentProvider>
735
- </VersionProvider>
736
- </GitLabAuthProvider>
737
- </GitHubAuthProvider>
206
+ <CommentProvider>
207
+ <CommentDrawer
208
+ selectedThreadId={selectedThreadId}
209
+ onThreadSelect={setSelectedThreadId}
210
+ >
211
+ <CommentOverlay
212
+ selectedThreadId={selectedThreadId}
213
+ onThreadSelect={setSelectedThreadId}
214
+ />
215
+ ${innerContent.trim()}
216
+ </CommentDrawer>
217
+ </CommentProvider>
738
218
  </${routerType}>
739
219
  `;
740
220
  } else {
741
221
  wrappedReturn = `
742
- <GitHubAuthProvider config={haleCommentsConfig}>
743
- <GitLabAuthProvider>
744
- <VersionProvider>
745
- <CommentProvider>
746
- <CommentDrawer
747
- selectedThreadId={selectedThreadId}
748
- onThreadSelect={setSelectedThreadId}
749
- >
750
- <CommentOverlay
751
- selectedThreadId={selectedThreadId}
752
- onThreadSelect={setSelectedThreadId}
753
- />
754
- ${originalReturn}
755
- </CommentDrawer>
756
- </CommentProvider>
757
- </VersionProvider>
758
- </GitLabAuthProvider>
759
- </GitHubAuthProvider>
222
+ <CommentProvider>
223
+ <CommentDrawer
224
+ selectedThreadId={selectedThreadId}
225
+ onThreadSelect={setSelectedThreadId}
226
+ >
227
+ <CommentOverlay
228
+ selectedThreadId={selectedThreadId}
229
+ onThreadSelect={setSelectedThreadId}
230
+ />
231
+ ${originalReturn}
232
+ </CommentDrawer>
233
+ </CommentProvider>
760
234
  `;
761
235
  }
762
236
  } else {
763
237
  wrappedReturn = `
764
- <GitHubAuthProvider config={haleCommentsConfig}>
765
- <GitLabAuthProvider>
766
- <VersionProvider>
767
- <CommentProvider>
768
- <CommentDrawer
769
- selectedThreadId={selectedThreadId}
770
- onThreadSelect={setSelectedThreadId}
771
- >
772
- <CommentOverlay
773
- selectedThreadId={selectedThreadId}
774
- onThreadSelect={setSelectedThreadId}
775
- />
776
- ${originalReturn}
777
- </CommentDrawer>
778
- </CommentProvider>
779
- </VersionProvider>
780
- </GitLabAuthProvider>
781
- </GitHubAuthProvider>
238
+ <CommentProvider>
239
+ <CommentDrawer
240
+ selectedThreadId={selectedThreadId}
241
+ onThreadSelect={setSelectedThreadId}
242
+ >
243
+ <CommentOverlay
244
+ selectedThreadId={selectedThreadId}
245
+ onThreadSelect={setSelectedThreadId}
246
+ />
247
+ ${originalReturn}
248
+ </CommentDrawer>
249
+ </CommentProvider>
782
250
  `;
783
251
  }
784
252
  content = content.slice(0, returnStart) + wrappedReturn + content.slice(returnEnd);
@@ -786,73 +254,15 @@ import haleCommentsConfig from '../../hale-comments.config.json';`;
786
254
  return content;
787
255
  }
788
256
  function showIntegrationDiff(filePath) {
789
- console.log(chalk7.cyan("\nChanges made to your App file:"));
790
- console.log(chalk7.white("\u2022 Added commenting system imports"));
791
- console.log(chalk7.white("\u2022 Added state hook for comment thread selection"));
792
- console.log(chalk7.white("\u2022 Wrapped app with GitHubAuthProvider, VersionProvider, and CommentProvider"));
793
- console.log(chalk7.white("\u2022 Added CommentDrawer and CommentOverlay components\n"));
794
- console.log(chalk7.dim(`Modified: ${filePath}
257
+ console.log(chalk2.cyan("\nChanges made to your App file:"));
258
+ console.log(chalk2.white("\u2022 Added commenting system imports"));
259
+ console.log(chalk2.white("\u2022 Added state hook for comment thread selection"));
260
+ console.log(chalk2.white("\u2022 Wrapped app with CommentProvider"));
261
+ console.log(chalk2.white("\u2022 Added CommentDrawer and CommentOverlay components\n"));
262
+ console.log(chalk2.dim(`Modified: ${filePath}
795
263
  `));
796
264
  }
797
265
 
798
- // src/validators/github.ts
799
- import axios from "axios";
800
- async function validateGitHubConnection(config) {
801
- try {
802
- const response = await axios.get("https://api.github.com/rate_limit", {
803
- headers: {
804
- "Accept": "application/vnd.github.v3+json"
805
- },
806
- timeout: 5e3
807
- });
808
- return response.status === 200;
809
- } catch (error) {
810
- console.error("GitHub connection error:", error);
811
- return false;
812
- }
813
- }
814
-
815
- // src/validators/repo.ts
816
- import axios2 from "axios";
817
- import chalk8 from "chalk";
818
- async function validateRepoAccess(config) {
819
- try {
820
- const response = await axios2.get(
821
- `https://api.github.com/repos/${config.owner}/${config.repo}`,
822
- {
823
- headers: {
824
- "Accept": "application/vnd.github.v3+json"
825
- },
826
- timeout: 5e3
827
- }
828
- );
829
- return response.status === 200;
830
- } catch (error) {
831
- if (error.response?.status === 404) {
832
- console.log(chalk8.yellow(` Repository ${config.owner}/${config.repo} not found or is private`));
833
- console.log(chalk8.yellow(" Note: Private repos require authentication at runtime"));
834
- return true;
835
- }
836
- console.error(" Repo access error:", error.message);
837
- return false;
838
- }
839
- }
840
-
841
- // src/validators/labels.ts
842
- import chalk9 from "chalk";
843
- var REQUIRED_LABELS = [
844
- { name: "comment", color: "0075ca", description: "User feedback comment" },
845
- { name: "prototype-feedback", color: "d93f0b", description: "Prototype feedback" },
846
- { name: "needs-review", color: "fbca04", description: "Needs team review" }
847
- ];
848
- async function ensureLabels(config) {
849
- console.log(chalk9.dim("\n Required labels for GitHub issues:"));
850
- REQUIRED_LABELS.forEach((label) => {
851
- console.log(chalk9.dim(` - ${label.name} (#${label.color}): ${label.description}`));
852
- });
853
- console.log(chalk9.dim(" These will be created automatically at runtime with proper authentication.\n"));
854
- }
855
-
856
266
  // src/commands/init.ts
857
267
  async function initCommand(options) {
858
268
  printWelcome();
@@ -867,13 +277,13 @@ async function initCommand(options) {
867
277
  }
868
278
  if (!project.isReact) {
869
279
  spinner.fail("This package requires a React project");
870
- console.log(chalk10.red("\n\u2717 Apollo Commenting System requires React.\n"));
280
+ console.log(chalk3.red("\n\u2717 Hale Commenting System requires React.\n"));
871
281
  process.exit(1);
872
282
  }
873
283
  if (!project.hasPatternFly) {
874
284
  printWarning("PatternFly not detected. This package is optimized for PatternFly projects.");
875
285
  if (!options.yes) {
876
- const { continueAnyway } = await inquirer3.prompt([
286
+ const { continueAnyway } = await inquirer.prompt([
877
287
  {
878
288
  type: "confirm",
879
289
  name: "continueAnyway",
@@ -882,185 +292,67 @@ async function initCommand(options) {
882
292
  }
883
293
  ]);
884
294
  if (!continueAnyway) {
885
- console.log(chalk10.dim("\nSetup cancelled.\n"));
295
+ console.log(chalk3.dim("\nSetup cancelled.\n"));
886
296
  process.exit(0);
887
297
  }
888
298
  }
889
299
  }
890
- const platform = options.platform || await promptPlatform(project.root);
891
- const githubConfig = await promptGitHubConfig(options.yes || false);
892
- spinner.start("Validating GitHub connection...");
893
- const isValid = await validateGitHubConnection(githubConfig);
894
- if (!isValid) {
895
- spinner.fail("GitHub connection failed. Please check your internet connection.");
896
- process.exit(1);
897
- }
898
- spinner.succeed("GitHub connection validated");
899
- spinner.start("Checking repository access...");
900
- const hasAccess = await validateRepoAccess(githubConfig);
901
- if (!hasAccess) {
902
- spinner.fail("Cannot access repository. Check the owner/repo name.");
903
- process.exit(1);
904
- }
905
- spinner.succeed("Repository access confirmed");
906
- spinner.start("Checking issue labels...");
907
- await ensureLabels(githubConfig);
908
- spinner.succeed("Issue labels documented");
909
- spinner.start("Generating serverless functions...");
910
- try {
911
- await generateServerless(platform, project.root);
912
- spinner.succeed("Serverless functions created");
913
- } catch (error) {
914
- spinner.fail(`Failed to generate serverless functions: ${error.message}`);
915
- process.exit(1);
916
- }
917
- spinner.start("Creating environment files...");
918
- try {
919
- await generateEnvFiles(githubConfig, platform, project.root);
920
- spinner.succeed("Environment files created");
921
- } catch (error) {
922
- spinner.fail(`Failed to create environment files: ${error.message}`);
923
- process.exit(1);
924
- }
925
- spinner.start("Creating hale-comments.config.json...");
926
- try {
927
- await generateConfig(githubConfig, platform, project.root);
928
- spinner.succeed("Configuration file created");
929
- } catch (error) {
930
- spinner.fail(`Failed to create config file: ${error.message}`);
931
- process.exit(1);
932
- }
933
- printSuccess();
934
- if (!options.yes) {
935
- const { autoIntegrate } = await inquirer3.prompt([
936
- {
937
- type: "confirm",
938
- name: "autoIntegrate",
939
- message: "Automatically integrate providers into your App file?",
940
- default: true
941
- }
942
- ]);
943
- if (autoIntegrate) {
944
- spinner.start("Integrating providers...");
945
- const result = await integrateProviders(project.root);
946
- if (result.success) {
947
- spinner.succeed("Providers integrated!");
948
- showIntegrationDiff(result.filePath);
949
- console.log(chalk10.green("\u2713 Your app is ready to use the commenting system!\n"));
950
- console.log(chalk10.cyan("Next steps:"));
951
- console.log(chalk10.white("1. Review the changes in your App file"));
952
- console.log(chalk10.white("2. Start your dev server: npm run start:dev"));
953
- console.log(chalk10.white("3. Open http://localhost:9000\n"));
954
- } else {
955
- spinner.warn(result.message);
956
- console.log(chalk10.yellow("\nFalling back to manual integration:\n"));
957
- printSetupInstructions(platform, project);
958
- printNextSteps();
959
- }
960
- } else {
961
- printSetupInstructions(platform, project);
962
- printNextSteps();
963
- }
300
+ console.log(chalk3.cyan("\n\u{1F4E6} Setting up local commenting system...\n"));
301
+ spinner.start("Integrating commenting system into your app...");
302
+ const result = await integrateProviders(project.root);
303
+ if (result.success) {
304
+ spinner.succeed("Commenting system integrated!");
305
+ showIntegrationDiff(result.filePath);
306
+ console.log(chalk3.green("\n\u2713 Setup complete! Your app is ready to use the commenting system.\n"));
307
+ console.log(chalk3.cyan("Next steps:"));
308
+ console.log(chalk3.white("1. Review the changes in your App file"));
309
+ console.log(chalk3.white("2. Start your dev server (e.g., npm run start:dev)"));
310
+ console.log(chalk3.white("3. Open your app in the browser"));
311
+ console.log(chalk3.white("4. Click anywhere to add comments (stored in localStorage)\n"));
312
+ console.log(chalk3.dim("\u{1F4A1} Tip: Comments persist across page refreshes and are stored locally in your browser.\n"));
964
313
  } else {
965
- printSetupInstructions(platform, project);
966
- printNextSteps();
967
- }
968
- }
314
+ spinner.warn(result.message);
315
+ console.log(chalk3.yellow("\n\u26A0\uFE0F Could not automatically integrate the commenting system.\n"));
316
+ console.log(chalk3.white("Please manually add the following to your App component:\n"));
317
+ console.log(chalk3.dim(`import { CommentProvider, CommentOverlay, CommentDrawer } from 'hale-commenting-system';
318
+ import { BrowserRouter as Router } from 'react-router-dom';
319
+ import React from 'react';
969
320
 
970
- // src/commands/validate.ts
971
- import chalk11 from "chalk";
972
- import ora2 from "ora";
973
- import path6 from "path";
974
- async function validateCommand() {
975
- console.log(chalk11.bold.cyan("\n\u{1F50D} Validating Apollo Commenting System Setup\n"));
976
- const spinner = ora2("Checking configuration files...").start();
977
- const cwd = process.cwd();
978
- const configPath = path6.join(cwd, "hale-comments.config.json");
979
- const hasConfig = await fileExists(configPath);
980
- if (!hasConfig) {
981
- spinner.fail("Configuration file not found");
982
- console.log(chalk11.red("\n\u2717 hale-comments.config.json not found"));
983
- console.log(chalk11.dim(" Run: hale-commenting-system init\n"));
984
- process.exit(1);
985
- }
986
- let config;
987
- try {
988
- const configContent = await readFile(configPath);
989
- config = JSON.parse(configContent);
990
- spinner.succeed("Configuration file found");
991
- } catch (error) {
992
- spinner.fail("Invalid configuration file");
993
- console.log(chalk11.red("\n\u2717 Could not parse hale-comments.config.json\n"));
994
- process.exit(1);
995
- }
996
- spinner.start("Checking environment files...");
997
- const envPath = path6.join(cwd, ".env.local");
998
- const hasEnv = await fileExists(envPath);
999
- if (!hasEnv) {
1000
- spinner.warn("Environment file not found (.env.local)");
1001
- } else {
1002
- spinner.succeed("Environment file found");
1003
- }
1004
- spinner.start("Checking serverless functions...");
1005
- let hasServerless = false;
1006
- if (config.platform === "vercel") {
1007
- const apiDir = path6.join(cwd, "api");
1008
- const loginPath = path6.join(apiDir, "github-oauth-login.ts");
1009
- const callbackPath = path6.join(apiDir, "github-oauth-callback.ts");
1010
- hasServerless = await fileExists(loginPath) && await fileExists(callbackPath);
1011
- } else if (config.platform === "netlify") {
1012
- const functionsDir = path6.join(cwd, "netlify", "functions");
1013
- const loginPath = path6.join(functionsDir, "github-oauth-login.ts");
1014
- const callbackPath = path6.join(functionsDir, "github-oauth-callback.ts");
1015
- hasServerless = await fileExists(loginPath) && await fileExists(callbackPath);
1016
- }
1017
- if (!hasServerless && config.platform !== "manual") {
1018
- spinner.warn("Serverless functions not found");
1019
- } else {
1020
- spinner.succeed("Serverless functions found");
1021
- }
1022
- spinner.start("Validating GitHub connection...");
1023
- const isValid = await validateGitHubConnection(config.github);
1024
- if (!isValid) {
1025
- spinner.fail("GitHub connection failed");
1026
- console.log(chalk11.red("\n\u2717 Could not connect to GitHub API\n"));
1027
- process.exit(1);
1028
- }
1029
- spinner.succeed("GitHub connection validated");
1030
- spinner.start("Validating repository access...");
1031
- const hasAccess = await validateRepoAccess(config.github);
1032
- if (!hasAccess) {
1033
- spinner.warn("Repository access could not be confirmed");
1034
- } else {
1035
- spinner.succeed("Repository access confirmed");
321
+ function App() {
322
+ const [selectedThreadId, setSelectedThreadId] = React.useState<string | null>(null);
323
+
324
+ return (
325
+ <Router>
326
+ <CommentProvider>
327
+ <CommentDrawer
328
+ selectedThreadId={selectedThreadId}
329
+ onThreadSelect={setSelectedThreadId}
330
+ >
331
+ <CommentOverlay
332
+ selectedThreadId={selectedThreadId}
333
+ onThreadSelect={setSelectedThreadId}
334
+ />
335
+ {/* Your app content */}
336
+ </CommentDrawer>
337
+ </CommentProvider>
338
+ </Router>
339
+ );
340
+ }
341
+ `));
1036
342
  }
1037
- console.log(chalk11.green.bold("\n\u2713 Validation complete!\n"));
1038
- console.log(chalk11.white("Your Apollo Commenting System setup looks good.\n"));
1039
343
  }
1040
344
 
1041
345
  // src/index.ts
1042
346
  var program = new Command();
1043
- program.name("hale-commenting-system").description("Hale Commenting System CLI - Setup wizard and tooling").version("1.0.0");
1044
- program.command("init").description("Initialize Apollo Commenting System in your project").option("-y, --yes", "Skip prompts and use defaults").option("--platform <platform>", "Target platform (vercel, netlify, manual)").action(async (options) => {
347
+ program.name("hale-commenting-system").description("Hale Commenting System CLI - Local setup wizard").version("2.0.0");
348
+ program.command("init").description("Initialize Hale Commenting System in your project").option("-y, --yes", "Skip prompts and use defaults").action(async (options) => {
1045
349
  try {
1046
350
  await initCommand(options);
1047
351
  } catch (error) {
1048
- console.error(chalk12.red("\n\u2717 Error:"), error.message);
352
+ console.error(chalk4.red("\n\u2717 Error:"), error.message);
1049
353
  process.exit(1);
1050
354
  }
1051
355
  });
1052
- program.command("validate").description("Validate your commenting system setup").action(async () => {
1053
- try {
1054
- await validateCommand();
1055
- } catch (error) {
1056
- console.error(chalk12.red("\n\u2717 Error:"), error.message);
1057
- process.exit(1);
1058
- }
1059
- });
1060
- program.command("update").description("Update existing configuration").action(() => {
1061
- console.log(chalk12.yellow("\n\u26A0\uFE0F Coming soon: Update configuration\n"));
1062
- console.log(chalk12.dim("For now, you can manually edit apollo-comments.config.json\n"));
1063
- });
1064
356
  if (!process.argv.slice(2).length) {
1065
357
  program.help();
1066
358
  }