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