@cometchat/skills-cli 2.0.0 → 2.0.2

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/dist/index.js CHANGED
@@ -170,9 +170,24 @@ function detectReactjs(root, deps) {
170
170
  bundler: isVite ? "vite" : isCra ? "webpack" : "vite",
171
171
  // default to vite for ambiguous
172
172
  ssr_strategy: "spa-no-ssr",
173
- env_prefix: isVite ? "VITE_" : isCra ? "REACT_APP_" : "VITE_"
173
+ env_prefix: isVite ? "VITE_" : isCra ? "REACT_APP_" : "VITE_",
174
+ uses_jsx: detectUsesJsx(root)
174
175
  };
175
176
  }
177
+ function detectUsesJsx(root) {
178
+ const indexHtml = readFileOrNull(p(root, "index.html"));
179
+ if (indexHtml) {
180
+ if (/src=["']\/?src\/main\.jsx["']/.test(indexHtml)) return true;
181
+ if (/src=["']\/?src\/main\.tsx["']/.test(indexHtml)) return false;
182
+ }
183
+ const hasJsxMain = pathExists(p(root, "src/main.jsx"));
184
+ const hasTsxMain = pathExists(p(root, "src/main.tsx"));
185
+ if (hasJsxMain && !hasTsxMain) return true;
186
+ const hasTsconfig = pathExists(p(root, "tsconfig.json"));
187
+ const hasJsxApp = pathExists(p(root, "src/App.jsx"));
188
+ if (!hasTsconfig && hasJsxApp) return true;
189
+ return false;
190
+ }
176
191
  function detectFramework(root) {
177
192
  const pkg = readJsonOrNull(p(root, "package.json"));
178
193
  if (!pkg) return EMPTY;
@@ -500,6 +515,7 @@ async function runDetectors(root) {
500
515
  bundler: fw.bundler,
501
516
  ssr_strategy: fw.ssr_strategy,
502
517
  env_prefix: fw.env_prefix,
518
+ uses_jsx: fw.uses_jsx,
503
519
  package_manager,
504
520
  credentials,
505
521
  existing_integration,
@@ -536,6 +552,9 @@ function printHumanReadable(r) {
536
552
  if (r.ssr_strategy !== null) lines.push(` SSR strategy: ${r.ssr_strategy}`);
537
553
  if (r.env_prefix !== null) lines.push(` Env var prefix: ${r.env_prefix}`);
538
554
  if (r.package_manager !== null) lines.push(` Package manager: ${r.package_manager}`);
555
+ if (r.uses_jsx === true) {
556
+ lines.push(` Source language: JavaScript (.jsx) \u2014 \u26A0 apply will refuse, the v6 templates are TypeScript-only`);
557
+ }
539
558
  lines.push("");
540
559
  lines.push(" Credentials:");
541
560
  lines.push(` Source: ${r.credentials.source}`);
@@ -1350,6 +1369,128 @@ function checkOverwriteSafetyBatch(root, framework, filePaths) {
1350
1369
  return filePaths.map((p2) => checkOverwriteSafety(root, framework, p2));
1351
1370
  }
1352
1371
 
1372
+ // src/utils/env-file.ts
1373
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "node:fs";
1374
+ import { join as join7 } from "node:path";
1375
+ var PLACEHOLDER_SENTINEL = "YOUR_";
1376
+ var PLACEHOLDER_SUFFIX = "_HERE";
1377
+ function envFileNameFor(framework) {
1378
+ return framework === "nextjs" ? ".env.local" : ".env";
1379
+ }
1380
+ function placeholderFor(varName) {
1381
+ let stripped = varName.replace(/^[A-Z]+_/, "");
1382
+ stripped = stripped.replace(/^COMETCHAT_/, "");
1383
+ return `${PLACEHOLDER_SENTINEL}${stripped}${PLACEHOLDER_SUFFIX}`;
1384
+ }
1385
+ function isPlaceholder(value) {
1386
+ const trimmed = value.trim();
1387
+ return trimmed.startsWith(PLACEHOLDER_SENTINEL) && trimmed.endsWith(PLACEHOLDER_SUFFIX);
1388
+ }
1389
+ function parseEnvKeys(content) {
1390
+ const keys = /* @__PURE__ */ new Set();
1391
+ for (const line of content.split(/\r?\n/)) {
1392
+ const trimmed = line.trim();
1393
+ if (!trimmed || trimmed.startsWith("#")) continue;
1394
+ const eq = trimmed.indexOf("=");
1395
+ if (eq === -1) continue;
1396
+ const key = trimmed.slice(0, eq).trim();
1397
+ if (key) keys.add(key);
1398
+ }
1399
+ return keys;
1400
+ }
1401
+ function parseEnvPairs(content) {
1402
+ const pairs = /* @__PURE__ */ new Map();
1403
+ for (const line of content.split(/\r?\n/)) {
1404
+ const trimmed = line.trim();
1405
+ if (!trimmed || trimmed.startsWith("#")) continue;
1406
+ const eq = trimmed.indexOf("=");
1407
+ if (eq === -1) continue;
1408
+ const key = trimmed.slice(0, eq).trim();
1409
+ let value = trimmed.slice(eq + 1).trim();
1410
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
1411
+ value = value.slice(1, -1);
1412
+ }
1413
+ const commentIdx = value.indexOf(" #");
1414
+ if (commentIdx !== -1) {
1415
+ value = value.slice(0, commentIdx).trim();
1416
+ }
1417
+ if (key) pairs.set(key, value);
1418
+ }
1419
+ return pairs;
1420
+ }
1421
+ function writeEnvFile(args) {
1422
+ const { projectRoot, framework, envVars } = args;
1423
+ const fileName = envFileNameFor(framework);
1424
+ const fullPath = join7(projectRoot, fileName);
1425
+ let existing = "";
1426
+ if (pathExists(fullPath)) {
1427
+ try {
1428
+ existing = readFileSync7(fullPath, "utf8");
1429
+ } catch {
1430
+ existing = "";
1431
+ }
1432
+ }
1433
+ const presentKeys = parseEnvKeys(existing);
1434
+ const toWrite = [];
1435
+ const skipped = [];
1436
+ for (const v of envVars) {
1437
+ if (presentKeys.has(v.name)) {
1438
+ skipped.push(v.name);
1439
+ continue;
1440
+ }
1441
+ toWrite.push({ name: v.name, placeholder: placeholderFor(v.name) });
1442
+ }
1443
+ if (toWrite.length > 0) {
1444
+ const lines = [];
1445
+ if (existing.length > 0 && !existing.endsWith("\n")) lines.push("");
1446
+ if (existing.length > 0) lines.push("");
1447
+ lines.push("# CometChat \u2014 fill in real values from https://app.cometchat.com \u2192 Your App \u2192 API & Auth Keys");
1448
+ for (const v of envVars) {
1449
+ if (presentKeys.has(v.name)) continue;
1450
+ const placeholder = placeholderFor(v.name);
1451
+ const inlineComment = v.secret ? " # secret \u2014 do not commit" : v.description ? ` # ${v.description}` : "";
1452
+ lines.push(`${v.name}=${placeholder}${inlineComment}`);
1453
+ }
1454
+ lines.push("");
1455
+ writeFileSync4(fullPath, existing + lines.join("\n"), "utf8");
1456
+ }
1457
+ const gitignoreUpdated = ensureEnvIgnored(projectRoot, fileName);
1458
+ return {
1459
+ path: fileName,
1460
+ written: toWrite,
1461
+ skipped,
1462
+ gitignore_updated: gitignoreUpdated
1463
+ };
1464
+ }
1465
+ function ensureEnvIgnored(projectRoot, envFileName) {
1466
+ const gitignorePath = join7(projectRoot, ".gitignore");
1467
+ let existing = "";
1468
+ if (pathExists(gitignorePath)) {
1469
+ try {
1470
+ existing = readFileSync7(gitignorePath, "utf8");
1471
+ } catch {
1472
+ existing = "";
1473
+ }
1474
+ }
1475
+ const lines = existing.split(/\r?\n/).map((l) => l.trim());
1476
+ for (const line of lines) {
1477
+ if (!line || line.startsWith("#")) continue;
1478
+ if (line === envFileName) return false;
1479
+ if (line === `/${envFileName}`) return false;
1480
+ if (line === ".env*" || line === "/.env*") return false;
1481
+ if (line === ".env*.local" && envFileName.endsWith(".local")) return false;
1482
+ if (line === ".env" && envFileName === ".env") return false;
1483
+ }
1484
+ const block = [];
1485
+ if (existing.length > 0 && !existing.endsWith("\n")) block.push("");
1486
+ if (existing.length > 0) block.push("");
1487
+ block.push("# CometChat \u2014 keep credentials out of source control");
1488
+ block.push(envFileName);
1489
+ block.push("");
1490
+ writeFileSync4(gitignorePath, existing + block.join("\n"), "utf8");
1491
+ return true;
1492
+ }
1493
+
1353
1494
  // src/commands/apply.ts
1354
1495
  function isJsonMode4(args) {
1355
1496
  return args.flags.json === true || args.flags.json === "true";
@@ -1526,6 +1667,7 @@ async function apply(args) {
1526
1667
  let frameworkVersion = null;
1527
1668
  let credentialsSource = "none";
1528
1669
  let detectedRouter = null;
1670
+ let usesJsx = false;
1529
1671
  if (!framework) {
1530
1672
  const detected = await runDetectors(root);
1531
1673
  if (detected.framework === null) {
@@ -1540,15 +1682,44 @@ async function apply(args) {
1540
1682
  frameworkVersion = detected.framework_version;
1541
1683
  credentialsSource = detected.credentials.source;
1542
1684
  detectedRouter = detected.router ?? null;
1685
+ usesJsx = detected.uses_jsx === true;
1543
1686
  } else if (framework === "nextjs") {
1544
1687
  const detected = await runDetectors(root);
1545
1688
  detectedRouter = detected.router ?? null;
1546
1689
  if (!frameworkVersion) frameworkVersion = detected.framework_version;
1547
1690
  if (credentialsSource === "none") credentialsSource = detected.credentials.source;
1691
+ } else if (framework === "reactjs") {
1692
+ const detected = await runDetectors(root);
1693
+ usesJsx = detected.uses_jsx === true;
1694
+ if (!frameworkVersion) frameworkVersion = detected.framework_version;
1695
+ if (credentialsSource === "none") credentialsSource = detected.credentials.source;
1548
1696
  }
1549
1697
  if (!envPrefix) {
1550
1698
  envPrefix = framework === "nextjs" ? "NEXT_PUBLIC_" : framework === "astro" ? "PUBLIC_" : "VITE_";
1551
1699
  }
1700
+ if (framework === "reactjs" && usesJsx) {
1701
+ return errorOut(
1702
+ args,
1703
+ [
1704
+ "Error: This project uses JavaScript (.jsx) entry files. The CometChat React UI Kit v6 templates are TypeScript-only.",
1705
+ "",
1706
+ "Detected: src/main.jsx (or index.html references /src/main.jsx) and no tsconfig.json.",
1707
+ "",
1708
+ "Options:",
1709
+ " 1. Recreate the project as TypeScript (recommended for new projects):",
1710
+ " npm create vite@latest my-react-app -- --template react-ts",
1711
+ " 2. Convert this project to TypeScript first:",
1712
+ " - Add a tsconfig.json (see https://vite.dev/guide/features#typescript)",
1713
+ " - Rename src/main.jsx \u2192 src/main.tsx",
1714
+ " - Rename src/App.jsx \u2192 src/App.tsx",
1715
+ " - Update index.html to reference /src/main.tsx",
1716
+ " Then re-run `cometchat init`.",
1717
+ "",
1718
+ "JSX template variants ship in a future release. Tracking: v2.1."
1719
+ ].join("\n"),
1720
+ 1
1721
+ );
1722
+ }
1552
1723
  let manifestKey = framework;
1553
1724
  if (framework === "nextjs" && detectedRouter === "pages") {
1554
1725
  manifestKey = "nextjs-pages";
@@ -1714,6 +1885,18 @@ async function apply(args) {
1714
1885
  state_file: ".cometchat/state.json",
1715
1886
  next_steps: (manifest.next_steps ?? []).map((s) => substitute(s, subs))
1716
1887
  };
1888
+ if (manifest.env_vars.length > 0) {
1889
+ result.env_file = writeEnvFile({
1890
+ projectRoot: root,
1891
+ framework,
1892
+ envVars: result.env_vars_to_add.map((v) => ({
1893
+ name: v.name,
1894
+ required: v.required,
1895
+ secret: v.secret,
1896
+ description: manifest.env_vars.find((e) => substitute(e.name, subs) === v.name)?.description
1897
+ }))
1898
+ });
1899
+ }
1717
1900
  if (embedPath !== null && entryPath !== null) {
1718
1901
  const embedHints = embedNextSteps(framework, embedPath);
1719
1902
  result.next_steps = [...embedHints, ...result.next_steps];
@@ -1840,12 +2023,24 @@ function printHumanReadable3(r) {
1840
2023
  lines.push(" (or pass --auto-install next time to do this automatically)");
1841
2024
  }
1842
2025
  }
1843
- if (r.env_vars_to_add.length > 0) {
2026
+ if (r.env_file) {
1844
2027
  lines.push("");
1845
- lines.push(" Add these to your .env file:");
1846
- for (const e of r.env_vars_to_add) {
1847
- const tag = e.secret ? " # secret \u2014 do not commit" : "";
1848
- lines.push(` ${e.name}=YOUR_VALUE_HERE${tag}`);
2028
+ if (r.env_file.written.length > 0) {
2029
+ lines.push(
2030
+ ` \u2713 Wrote ${r.env_file.written.length} placeholder env var(s) to ${r.env_file.path}`
2031
+ );
2032
+ lines.push(` Open ${r.env_file.path} and replace the YOUR_*_HERE values:`);
2033
+ for (const v of r.env_file.written) {
2034
+ lines.push(` ${v.name}=${v.placeholder}`);
2035
+ }
2036
+ lines.push(` (get real values from https://app.cometchat.com \u2192 Your App \u2192 API & Auth Keys)`);
2037
+ } else if (r.env_vars_to_add.length > 0) {
2038
+ lines.push(
2039
+ ` \u2713 All ${r.env_vars_to_add.length} env var(s) already present in ${r.env_file.path}`
2040
+ );
2041
+ }
2042
+ if (r.env_file.gitignore_updated) {
2043
+ lines.push(` + Added ${r.env_file.path} to .gitignore`);
1849
2044
  }
1850
2045
  }
1851
2046
  lines.push("");
@@ -2127,7 +2322,7 @@ function printHumanReadable5(r) {
2127
2322
 
2128
2323
  // src/commands/uninstall.ts
2129
2324
  import { rmSync as rmSync2, statSync as statSync3, rmdirSync, readdirSync as readdirSync3 } from "node:fs";
2130
- import { dirname as dirname5, join as join7, resolve as resolve7 } from "node:path";
2325
+ import { dirname as dirname5, join as join8, resolve as resolve7 } from "node:path";
2131
2326
  function isJsonMode7(args) {
2132
2327
  return args.flags.json === true || args.flags.json === "true";
2133
2328
  }
@@ -2188,7 +2383,7 @@ async function uninstall(args) {
2188
2383
  const skipped = [];
2189
2384
  const dirsToCheck = /* @__PURE__ */ new Set();
2190
2385
  for (const file of state.files_owned) {
2191
- const fullPath = join7(root, file);
2386
+ const fullPath = join8(root, file);
2192
2387
  if (!pathExists(fullPath)) {
2193
2388
  skipped.push({ path: file, reason: "file not found" });
2194
2389
  continue;
@@ -2442,8 +2637,8 @@ function printHumanReadable7(r) {
2442
2637
  }
2443
2638
 
2444
2639
  // src/commands/production-auth.ts
2445
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "node:fs";
2446
- import { join as join8, resolve as resolve9 } from "node:path";
2640
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
2641
+ import { join as join9, resolve as resolve9 } from "node:path";
2447
2642
  function isJsonMode9(args) {
2448
2643
  return args.flags.json === true || args.flags.json === "true";
2449
2644
  }
@@ -2454,17 +2649,17 @@ function projectPath8(args) {
2454
2649
  }
2455
2650
  var SUPPORTED_FRAMEWORKS = /* @__PURE__ */ new Set(["nextjs", "react-router", "astro"]);
2456
2651
  function productionRoot() {
2457
- return join8(findRegistryRoot(), "v6", "production");
2652
+ return join9(findRegistryRoot(), "v6", "production");
2458
2653
  }
2459
2654
  function loadProductionManifest(framework) {
2460
2655
  const root = productionRoot();
2461
- const manifestPath = join8(root, `${framework}.template.json`);
2462
- const content = readFileSync7(manifestPath, "utf8");
2656
+ const manifestPath = join9(root, `${framework}.template.json`);
2657
+ const content = readFileSync8(manifestPath, "utf8");
2463
2658
  return JSON.parse(content);
2464
2659
  }
2465
2660
  function readProductionTemplate(framework, ref) {
2466
2661
  const root = productionRoot();
2467
- return readFileSync7(join8(root, framework, ref), "utf8");
2662
+ return readFileSync8(join9(root, framework, ref), "utf8");
2468
2663
  }
2469
2664
  function findClientLoginFile(state) {
2470
2665
  const candidates = [
@@ -2495,16 +2690,16 @@ ${indent} .catch((e: unknown) => setError(String(e)));`
2495
2690
  function clientPatch(root, state) {
2496
2691
  const relPath = findClientLoginFile(state);
2497
2692
  if (relPath === null) return { status: "n/a" };
2498
- const fullPath = join8(root, relPath);
2693
+ const fullPath = join9(root, relPath);
2499
2694
  let content;
2500
2695
  try {
2501
- content = readFileSync7(fullPath, "utf8");
2696
+ content = readFileSync8(fullPath, "utf8");
2502
2697
  } catch {
2503
2698
  return { status: "skipped", file: relPath };
2504
2699
  }
2505
2700
  const next = rewriteLoginChain(content);
2506
2701
  if (next === null) return { status: "skipped", file: relPath };
2507
- writeFileSync4(fullPath, next, "utf8");
2702
+ writeFileSync5(fullPath, next, "utf8");
2508
2703
  return { status: "applied", file: relPath, newChecksum: sha256(next) };
2509
2704
  }
2510
2705
  async function productionAuth(args) {
@@ -3041,19 +3236,19 @@ function printHumanReadable9(r) {
3041
3236
  }
3042
3237
 
3043
3238
  // src/commands/features.ts
3044
- import { readFileSync as readFileSync8 } from "node:fs";
3045
- import { join as join9 } from "node:path";
3239
+ import { readFileSync as readFileSync9 } from "node:fs";
3240
+ import { join as join10 } from "node:path";
3046
3241
  function isJsonMode11(args) {
3047
3242
  return args.flags.json === true || args.flags.json === "true";
3048
3243
  }
3049
3244
  function catalogPath() {
3050
- return join9(findRegistryRoot(), "v6", "features", "catalog.json");
3245
+ return join10(findRegistryRoot(), "v6", "features", "catalog.json");
3051
3246
  }
3052
3247
  var cachedCatalog = null;
3053
3248
  function loadCatalog() {
3054
3249
  if (cachedCatalog) return cachedCatalog;
3055
3250
  const path = catalogPath();
3056
- cachedCatalog = JSON.parse(readFileSync8(path, "utf8"));
3251
+ cachedCatalog = JSON.parse(readFileSync9(path, "utf8"));
3057
3252
  return cachedCatalog;
3058
3253
  }
3059
3254
  function nextStepsForFeature(feature) {
@@ -3066,6 +3261,7 @@ function nextStepsForFeature(feature) {
3066
3261
  ...feature.docs_topic ? [`For details, query the docs MCP for "${feature.docs_topic}" or visit https://www.cometchat.com/docs/ui-kit/react/${feature.docs_topic}`] : []
3067
3262
  ];
3068
3263
  case "dashboard-toggle": {
3264
+ const docsUrl = feature.docs_topic ? `https://www.cometchat.com/docs/ui-kit/react/${feature.docs_topic}` : "https://www.cometchat.com/docs/ui-kit/react/extensions";
3069
3265
  const lines = [];
3070
3266
  if (feature.auto_wired_in_uikit) {
3071
3267
  lines.push(
@@ -3074,8 +3270,13 @@ function nextStepsForFeature(feature) {
3074
3270
  "Steps to enable:",
3075
3271
  ` 1. Go to https://app.cometchat.com and select your app`,
3076
3272
  ` 2. Navigate to: ${feature.dashboard_path ?? "Extensions \u2192 " + feature.name}`,
3273
+ ` (this is a hint \u2014 if the dashboard UI has changed, see the canonical docs link below)`,
3077
3274
  ` 3. Toggle the feature on (and configure any required values)`,
3078
- ` 4. Refresh your dev server \u2014 no code changes required`
3275
+ ` 4. Refresh your dev server \u2014 no code changes required`,
3276
+ "",
3277
+ ` \u{1F4D6} Canonical docs (always-current \u2014 use this if the dashboard navigation above doesn't match what you see):`,
3278
+ ` ${docsUrl}`,
3279
+ ` For exact, up-to-date integration steps, query the cometchat-docs MCP for "${feature.id}".`
3079
3280
  );
3080
3281
  } else {
3081
3282
  lines.push(
@@ -3083,23 +3284,14 @@ function nextStepsForFeature(feature) {
3083
3284
  "",
3084
3285
  "Steps to enable:",
3085
3286
  ` 1. Go to https://app.cometchat.com \u2192 ${feature.dashboard_path ?? "Extensions \u2192 " + feature.name}, toggle on`,
3086
- ` 2. In your CometChat init (the file that calls UIKitSettingsBuilder), add the extension:`,
3087
- ` import { CometChatUIKit, UIKitSettingsBuilder } from "@cometchat/chat-uikit-react";`,
3088
- ` // Pseudocode \u2014 match your existing builder chain:`,
3089
- ` const settings = new UIKitSettingsBuilder()`,
3090
- ` .setAppId(APP_ID)`,
3091
- ` .setRegion(REGION)`,
3092
- ` .setAuthKey(AUTH_KEY)`,
3093
- ` .subscribePresenceForAllUsers()`,
3094
- ` .setExtensions([/* your extension instance here */])`,
3095
- ` .build();`,
3096
- ` CometChatUIKit.init(settings);`,
3097
- ` 3. Restart your dev server`
3287
+ ` (dashboard hint \u2014 see canonical docs link below if the navigation has changed)`,
3288
+ ` 2. Register the extension via UIKitSettingsBuilder.setExtensions([...]) in your CometChat init.`,
3289
+ ` For the exact import path + extension class name + builder syntax, query the cometchat-docs MCP for "${feature.id}" \u2014 DO NOT invent the API from memory.`,
3290
+ ` 3. Restart your dev server`,
3291
+ "",
3292
+ ` \u{1F4D6} Canonical docs: ${docsUrl}`
3098
3293
  );
3099
3294
  }
3100
- if (feature.docs_topic) {
3101
- lines.push(` Docs: https://www.cometchat.com/docs/ui-kit/react/${feature.docs_topic}`);
3102
- }
3103
3295
  return lines;
3104
3296
  }
3105
3297
  case "package-install":
@@ -3377,6 +3569,14 @@ function buildIssues(args) {
3377
3569
  fix: "Add the missing variables to your .env file. The chat will fail to initialize without them."
3378
3570
  });
3379
3571
  }
3572
+ if (args.envPlaceholder.length > 0) {
3573
+ issues.push({
3574
+ severity: "warning",
3575
+ code: "env-placeholder",
3576
+ message: `${args.envPlaceholder.length} env var(s) still contain YOUR_*_HERE placeholders: ${args.envPlaceholder.join(", ")}`,
3577
+ fix: `Open ${args.envFilePath ?? ".env"} and replace each YOUR_*_HERE value with the real one from https://app.cometchat.com \u2192 Your App \u2192 API & Auth Keys. Without this, the chat will fail to initialize at runtime.`
3578
+ });
3579
+ }
3380
3580
  return issues;
3381
3581
  }
3382
3582
  async function doctor(args) {
@@ -3419,17 +3619,34 @@ async function doctor(args) {
3419
3619
  } else if (expectedEnvVars[2]) {
3420
3620
  envMissing.push(expectedEnvVars[2]);
3421
3621
  }
3622
+ const envFileName = stateInfo ? envFileNameFor(stateInfo.framework) : detected.framework ? envFileNameFor(detected.framework) : null;
3623
+ const envFilePath = envFileName && pathExists(p(root, envFileName)) ? envFileName : null;
3624
+ const envPlaceholder = [];
3625
+ if (envFilePath) {
3626
+ const content = readFileOrNull(p(root, envFilePath));
3627
+ if (content) {
3628
+ const pairs = parseEnvPairs(content);
3629
+ for (const expected of expectedEnvVars) {
3630
+ const value = pairs.get(expected);
3631
+ if (value !== void 0 && isPlaceholder(value)) {
3632
+ envPlaceholder.push(expected);
3633
+ }
3634
+ }
3635
+ }
3636
+ }
3422
3637
  const issues = buildIssues({
3423
3638
  drift,
3424
3639
  verify: verifyResult,
3425
3640
  envMissing,
3641
+ envPlaceholder,
3642
+ envFilePath,
3426
3643
  hasIntegration: stateInfo !== null
3427
3644
  });
3428
3645
  const errorCount = issues.filter((i) => i.severity === "error").length;
3429
3646
  const warnCount = issues.filter((i) => i.severity === "warning").length;
3430
- const status = errorCount > 0 ? "issues" : warnCount > 0 ? "warnings" : "healthy";
3647
+ const status2 = errorCount > 0 ? "issues" : warnCount > 0 ? "warnings" : "healthy";
3431
3648
  const result = {
3432
- status,
3649
+ status: status2,
3433
3650
  detect: {
3434
3651
  framework: detected.framework,
3435
3652
  framework_version: detected.framework_version,
@@ -3453,17 +3670,19 @@ async function doctor(args) {
3453
3670
  },
3454
3671
  environment: {
3455
3672
  env_vars_present: envPresent.filter(Boolean),
3456
- env_vars_missing: envMissing
3673
+ env_vars_missing: envMissing,
3674
+ env_vars_placeholder: envPlaceholder,
3675
+ env_file_path: envFilePath
3457
3676
  },
3458
3677
  issues,
3459
- summary: buildSummary(status, errorCount, warnCount, stateInfo !== null)
3678
+ summary: buildSummary(status2, errorCount, warnCount, stateInfo !== null)
3460
3679
  };
3461
3680
  return outputResult7(args, result, errorCount > 0 ? 1 : 0);
3462
3681
  }
3463
- function buildSummary(status, errors, warnings, integrated) {
3682
+ function buildSummary(status2, errors, warnings, integrated) {
3464
3683
  if (!integrated) return "No CometChat integration found. Run `cometchat apply` to get started.";
3465
- if (status === "healthy") return "\u2713 All checks pass. Integration is healthy.";
3466
- if (status === "warnings") return `\u26A0 ${warnings} warning(s) found. Integration works but has minor issues.`;
3684
+ if (status2 === "healthy") return "\u2713 All checks pass. Integration is healthy.";
3685
+ if (status2 === "warnings") return `\u26A0 ${warnings} warning(s) found. Integration works but has minor issues.`;
3467
3686
  return `\u2717 ${errors} error(s) and ${warnings} warning(s) found. Integration may not work correctly.`;
3468
3687
  }
3469
3688
  function outputResult7(args, result, exitCode) {
@@ -3636,13 +3855,10 @@ async function init(args) {
3636
3855
  `Run \`cometchat install\` (or pass --install next time) to install ${applyResult.deps_to_install.length} npm package(s).`
3637
3856
  );
3638
3857
  }
3639
- try {
3640
- const manifest = loadManifest(6, detected.framework, parseInt(experienceArg, 10));
3641
- if (manifest.env_vars.length > 0) {
3642
- const envNames = manifest.env_vars.map((v) => v.name.replace("{{ENV_PREFIX}}", detected.env_prefix ?? "")).join(", ");
3643
- result.next_actions.unshift(`Add these env vars to .env: ${envNames}`);
3644
- }
3645
- } catch {
3858
+ if (applyResult.env_file && applyResult.env_file.written.length > 0) {
3859
+ result.next_actions.unshift(
3860
+ `Open ${applyResult.env_file.path} and replace ${applyResult.env_file.written.length} YOUR_*_HERE placeholder(s) with real values from https://app.cometchat.com \u2192 Your App \u2192 API & Auth Keys.`
3861
+ );
3646
3862
  }
3647
3863
  return outputResult8(args, result, 0);
3648
3864
  }
@@ -3685,8 +3901,8 @@ function printHumanReadable11(r) {
3685
3901
  }
3686
3902
 
3687
3903
  // src/commands/add-widget.ts
3688
- import { readFileSync as readFileSync9 } from "node:fs";
3689
- import { join as join10, resolve as resolve13 } from "node:path";
3904
+ import { readFileSync as readFileSync10 } from "node:fs";
3905
+ import { join as join11, resolve as resolve13 } from "node:path";
3690
3906
  var SUPPORTED = /* @__PURE__ */ new Set(["reactjs", "nextjs", "react-router", "astro"]);
3691
3907
  function isJsonMode14(args) {
3692
3908
  return args.flags.json === true || args.flags.json === "true";
@@ -3697,16 +3913,16 @@ function projectPath12(args) {
3697
3913
  return resolve13(process.cwd());
3698
3914
  }
3699
3915
  function widgetRoot() {
3700
- return join10(findRegistryRoot(), "v6", "widget");
3916
+ return join11(findRegistryRoot(), "v6", "widget");
3701
3917
  }
3702
3918
  function loadWidgetManifest(framework) {
3703
3919
  const root = widgetRoot();
3704
- const path = join10(root, `${framework}.template.json`);
3705
- return JSON.parse(readFileSync9(path, "utf8"));
3920
+ const path = join11(root, `${framework}.template.json`);
3921
+ return JSON.parse(readFileSync10(path, "utf8"));
3706
3922
  }
3707
3923
  function readWidgetTemplate(framework, ref) {
3708
3924
  const root = widgetRoot();
3709
- return readFileSync9(join10(root, framework, ref), "utf8");
3925
+ return readFileSync10(join11(root, framework, ref), "utf8");
3710
3926
  }
3711
3927
  async function addWidget(args) {
3712
3928
  const root = projectPath12(args);
@@ -3840,8 +4056,8 @@ function printHumanReadable12(r) {
3840
4056
  }
3841
4057
 
3842
4058
  // src/commands/add-user-mgmt.ts
3843
- import { readFileSync as readFileSync10 } from "node:fs";
3844
- import { join as join11, resolve as resolve14 } from "node:path";
4059
+ import { readFileSync as readFileSync11 } from "node:fs";
4060
+ import { join as join12, resolve as resolve14 } from "node:path";
3845
4061
  var SUPPORTED2 = /* @__PURE__ */ new Set(["nextjs", "react-router", "astro"]);
3846
4062
  function isJsonMode15(args) {
3847
4063
  return args.flags.json === true || args.flags.json === "true";
@@ -3852,15 +4068,15 @@ function projectPath13(args) {
3852
4068
  return resolve14(process.cwd());
3853
4069
  }
3854
4070
  function userMgmtRoot() {
3855
- return join11(findRegistryRoot(), "v6", "user-mgmt");
4071
+ return join12(findRegistryRoot(), "v6", "user-mgmt");
3856
4072
  }
3857
4073
  function loadManifest2(framework) {
3858
4074
  const root = userMgmtRoot();
3859
- return JSON.parse(readFileSync10(join11(root, `${framework}.template.json`), "utf8"));
4075
+ return JSON.parse(readFileSync11(join12(root, `${framework}.template.json`), "utf8"));
3860
4076
  }
3861
4077
  function readTemplate(framework, ref) {
3862
4078
  const root = userMgmtRoot();
3863
- return readFileSync10(join11(root, framework, ref), "utf8");
4079
+ return readFileSync11(join12(root, framework, ref), "utf8");
3864
4080
  }
3865
4081
  async function addUserMgmt(args) {
3866
4082
  const root = projectPath13(args);
@@ -4017,8 +4233,8 @@ function printHumanReadable13(r) {
4017
4233
  }
4018
4234
 
4019
4235
  // src/commands/apply-feature.ts
4020
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync5 } from "node:fs";
4021
- import { join as join12, resolve as resolve15 } from "node:path";
4236
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync6 } from "node:fs";
4237
+ import { join as join13, resolve as resolve15 } from "node:path";
4022
4238
  function isJsonMode16(args) {
4023
4239
  return args.flags.json === true || args.flags.json === "true";
4024
4240
  }
@@ -4028,10 +4244,10 @@ function projectPath14(args) {
4028
4244
  return resolve15(process.cwd());
4029
4245
  }
4030
4246
  function catalogPath2() {
4031
- return join12(findRegistryRoot(), "v6", "features", "catalog.json");
4247
+ return join13(findRegistryRoot(), "v6", "features", "catalog.json");
4032
4248
  }
4033
4249
  function loadCatalog2() {
4034
- return JSON.parse(readFileSync11(catalogPath2(), "utf8"));
4250
+ return JSON.parse(readFileSync12(catalogPath2(), "utf8"));
4035
4251
  }
4036
4252
  function swapIdentifier(content, from, to) {
4037
4253
  const escaped = from.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -4107,16 +4323,16 @@ async function applyFeature(args) {
4107
4323
  const modified = [];
4108
4324
  let totalReplacements = 0;
4109
4325
  for (const relPath of state.files_owned) {
4110
- const fullPath = join12(root, relPath);
4326
+ const fullPath = join13(root, relPath);
4111
4327
  let content;
4112
4328
  try {
4113
- content = readFileSync11(fullPath, "utf8");
4329
+ content = readFileSync12(fullPath, "utf8");
4114
4330
  } catch {
4115
4331
  continue;
4116
4332
  }
4117
4333
  const [next, count] = swapIdentifier(content, feature.swap_from, feature.swap_to);
4118
4334
  if (count > 0) {
4119
- writeFileSync5(fullPath, next, "utf8");
4335
+ writeFileSync6(fullPath, next, "utf8");
4120
4336
  newChecksums[relPath] = sha256(next);
4121
4337
  modified.push(relPath);
4122
4338
  totalReplacements += count;
@@ -4188,6 +4404,295 @@ function printHumanReadable14(r) {
4188
4404
  console.log(lines.join("\n"));
4189
4405
  }
4190
4406
 
4407
+ // src/commands/status.ts
4408
+ import { readFileSync as readFileSync13 } from "node:fs";
4409
+ import { resolve as resolve16 } from "node:path";
4410
+ function isJsonMode17(args) {
4411
+ return args.flags.json === true || args.flags.json === "true";
4412
+ }
4413
+ function projectPath15(args) {
4414
+ const fromFlag = args.flags.path;
4415
+ if (typeof fromFlag === "string") return resolve16(fromFlag);
4416
+ return resolve16(process.cwd());
4417
+ }
4418
+ function widgetMarkerPath(state) {
4419
+ switch (state.framework) {
4420
+ case "nextjs":
4421
+ return state.router === "pages" ? "src/cometchat/CometChatWidget.tsx" : "src/app/cometchat/CometChatWidget.tsx";
4422
+ case "react-router":
4423
+ return "app/cometchat/CometChatWidget.tsx";
4424
+ case "astro":
4425
+ return "src/cometchat/CometChatWidget.tsx";
4426
+ default:
4427
+ return "src/cometchat/CometChatWidget.tsx";
4428
+ }
4429
+ }
4430
+ function productionAuthMarkerPath(state) {
4431
+ switch (state.framework) {
4432
+ case "nextjs":
4433
+ return state.router === "pages" ? "src/pages/api/cometchat-token.ts" : "src/app/api/cometchat-token/route.ts";
4434
+ case "react-router":
4435
+ return "app/routes/api.cometchat-token.ts";
4436
+ case "astro":
4437
+ return "src/pages/api/cometchat-token.ts";
4438
+ default:
4439
+ return "";
4440
+ }
4441
+ }
4442
+ function userMgmtMarkerPath(state) {
4443
+ switch (state.framework) {
4444
+ case "nextjs":
4445
+ return state.router === "pages" ? "src/pages/api/cometchat-user.ts" : "src/app/api/cometchat-user/route.ts";
4446
+ case "react-router":
4447
+ return "app/routes/api.cometchat-user.ts";
4448
+ case "astro":
4449
+ return "src/pages/api/cometchat-user.ts";
4450
+ default:
4451
+ return "";
4452
+ }
4453
+ }
4454
+ function themeFilePath(state) {
4455
+ switch (state.framework) {
4456
+ case "nextjs":
4457
+ return state.router === "pages" ? "src/styles/globals.css" : "src/app/globals.css";
4458
+ case "react-router":
4459
+ return "app/app.css";
4460
+ case "astro":
4461
+ return "src/cometchat/ChatApp.tsx";
4462
+ // theme block lives inside the React island
4463
+ default:
4464
+ return "src/index.css";
4465
+ }
4466
+ }
4467
+ function fileContains(root, relPath, marker) {
4468
+ const fullPath = p(root, relPath);
4469
+ if (!pathExists(fullPath)) return false;
4470
+ try {
4471
+ const content = readFileSync13(fullPath, "utf8");
4472
+ return content.includes(marker);
4473
+ } catch {
4474
+ return false;
4475
+ }
4476
+ }
4477
+ function countAuditEntries(root) {
4478
+ const path = p(root, AUDIT_LOG_PATH);
4479
+ if (!pathExists(path)) return 0;
4480
+ try {
4481
+ const content = readFileSync13(path, "utf8");
4482
+ const matches = content.match(/^## \d{4}-\d{2}-\d{2}T/gm);
4483
+ return matches ? matches.length : 0;
4484
+ } catch {
4485
+ return 0;
4486
+ }
4487
+ }
4488
+ async function status(args) {
4489
+ const root = projectPath15(args);
4490
+ if (!hasState(root)) {
4491
+ const result2 = {
4492
+ integrated: false,
4493
+ items: [],
4494
+ audit_log_entries: 0,
4495
+ audit_log_path: null,
4496
+ files_owned_count: 0,
4497
+ drift_count: 0,
4498
+ next_suggested: "Run `cometchat init` to scaffold a CometChat integration in this project."
4499
+ };
4500
+ return outputResult12(args, result2, 0);
4501
+ }
4502
+ const state = readState(root);
4503
+ if (!state) {
4504
+ const result2 = {
4505
+ integrated: false,
4506
+ items: [],
4507
+ audit_log_entries: 0,
4508
+ audit_log_path: null,
4509
+ files_owned_count: 0,
4510
+ drift_count: 0,
4511
+ next_suggested: "Could not parse .cometchat/state.json \u2014 schema mismatch?"
4512
+ };
4513
+ return outputResult12(args, result2, 1);
4514
+ }
4515
+ const items = [];
4516
+ items.push({
4517
+ step: 2,
4518
+ state: "done",
4519
+ label: "Base integration",
4520
+ detail: `experience ${state.experience} (${state.experience_slug}) on ${state.framework}${state.router ? ` (${state.router} router)` : ""}`
4521
+ });
4522
+ for (const feature of state.applied_features ?? []) {
4523
+ items.push({
4524
+ step: 4,
4525
+ state: "done",
4526
+ label: "Feature applied",
4527
+ detail: feature
4528
+ });
4529
+ }
4530
+ const widgetPath = widgetMarkerPath(state);
4531
+ if (widgetPath && pathExists(p(root, widgetPath))) {
4532
+ items.push({
4533
+ step: 4,
4534
+ state: "done",
4535
+ label: "Floating chat widget",
4536
+ detail: widgetPath
4537
+ });
4538
+ } else {
4539
+ items.push({
4540
+ step: 4,
4541
+ state: "todo",
4542
+ label: "Floating chat widget",
4543
+ detail: "not added",
4544
+ next_command: "cometchat add-widget"
4545
+ });
4546
+ }
4547
+ const userMgmtPath = userMgmtMarkerPath(state);
4548
+ if (state.framework === "reactjs") {
4549
+ items.push({
4550
+ step: 4,
4551
+ state: "skipped",
4552
+ label: "Server user management",
4553
+ detail: "reactjs has no built-in server (BYO backend)"
4554
+ });
4555
+ } else if (userMgmtPath && pathExists(p(root, userMgmtPath))) {
4556
+ items.push({
4557
+ step: 4,
4558
+ state: "done",
4559
+ label: "Server user management",
4560
+ detail: userMgmtPath
4561
+ });
4562
+ } else {
4563
+ items.push({
4564
+ step: 4,
4565
+ state: "todo",
4566
+ label: "Server user management",
4567
+ detail: "no /api/cometchat-user endpoint",
4568
+ next_command: "cometchat add-user-mgmt"
4569
+ });
4570
+ }
4571
+ const prodAuthPath = productionAuthMarkerPath(state);
4572
+ if (state.framework === "reactjs") {
4573
+ items.push({
4574
+ step: 5,
4575
+ state: "skipped",
4576
+ label: "Production auth",
4577
+ detail: "reactjs has no built-in server (BYO backend)"
4578
+ });
4579
+ } else if (state.credentials_source === "token-endpoint" || prodAuthPath && pathExists(p(root, prodAuthPath))) {
4580
+ items.push({
4581
+ step: 5,
4582
+ state: "done",
4583
+ label: "Production auth",
4584
+ detail: prodAuthPath || "server-side token endpoint"
4585
+ });
4586
+ } else {
4587
+ items.push({
4588
+ step: 5,
4589
+ state: "todo",
4590
+ label: "Production auth",
4591
+ detail: "still on dev Auth Key (insecure for prod)",
4592
+ next_command: "cometchat production-auth"
4593
+ });
4594
+ }
4595
+ const drift = detectDrift(root, state);
4596
+ const customizationCount = drift.modified_files.length;
4597
+ items.push({
4598
+ step: 5,
4599
+ state: customizationCount > 0 ? "done" : "todo",
4600
+ label: "Customizations",
4601
+ detail: customizationCount > 0 ? `${customizationCount} file modification(s) outside templates` : "0 modifications (still on default templates)",
4602
+ next_command: customizationCount > 0 ? void 0 : void 0
4603
+ });
4604
+ const themePath = themeFilePath(state);
4605
+ if (themePath && fileContains(root, themePath, "CometChat theme overrides")) {
4606
+ items.push({
4607
+ step: 7,
4608
+ state: "done",
4609
+ label: "Theme",
4610
+ detail: `applied to ${themePath}`
4611
+ });
4612
+ } else {
4613
+ items.push({
4614
+ step: 7,
4615
+ state: "todo",
4616
+ label: "Theme",
4617
+ detail: "no overrides applied (still on UI Kit defaults)",
4618
+ next_command: "cometchat apply-theme --preset slack"
4619
+ });
4620
+ }
4621
+ let nextSuggested;
4622
+ const todoItems = items.filter((i) => i.state === "todo" && i.next_command);
4623
+ const prodAuthTodo = todoItems.find((i) => i.label === "Production auth");
4624
+ const widgetTodo = todoItems.find((i) => i.label === "Floating chat widget");
4625
+ const themeTodo = todoItems.find((i) => i.label === "Theme");
4626
+ if (prodAuthTodo) {
4627
+ nextSuggested = `Run \`${prodAuthTodo.next_command}\` before deploying to production \u2014 your dev Auth Key is currently exposed to the browser.`;
4628
+ } else if (themeTodo) {
4629
+ nextSuggested = `Run \`${themeTodo.next_command}\` to brand the chat UI (or pick a different preset: whatsapp, imessage, discord, notion).`;
4630
+ } else if (widgetTodo) {
4631
+ nextSuggested = `Run \`${widgetTodo.next_command}\` to add a floating chat overlay.`;
4632
+ } else if (todoItems.length === 0) {
4633
+ nextSuggested = "Looks complete! Run `cometchat verify` for a final correctness check.";
4634
+ } else {
4635
+ nextSuggested = `Run \`/cometchat\` to open the iteration menu and pick what's next.`;
4636
+ }
4637
+ const result = {
4638
+ integrated: true,
4639
+ framework: state.framework,
4640
+ framework_version: state.framework_version,
4641
+ experience: state.experience,
4642
+ experience_slug: state.experience_slug,
4643
+ router: state.router,
4644
+ items,
4645
+ audit_log_entries: countAuditEntries(root),
4646
+ audit_log_path: pathExists(p(root, AUDIT_LOG_PATH)) ? AUDIT_LOG_PATH : null,
4647
+ files_owned_count: state.files_owned.length,
4648
+ drift_count: drift.modified_files.length,
4649
+ next_suggested: nextSuggested
4650
+ };
4651
+ return outputResult12(args, result, 0);
4652
+ }
4653
+ function outputResult12(args, result, exitCode) {
4654
+ if (isJsonMode17(args)) {
4655
+ console.log(JSON.stringify(result, null, 2));
4656
+ return exitCode;
4657
+ }
4658
+ printHumanReadable15(result);
4659
+ return exitCode;
4660
+ }
4661
+ function printHumanReadable15(r) {
4662
+ const lines = [];
4663
+ lines.push("");
4664
+ if (!r.integrated) {
4665
+ lines.push(" CometChat React integration \u2014 not yet started");
4666
+ lines.push("");
4667
+ if (r.next_suggested) lines.push(` \u2192 ${r.next_suggested}`);
4668
+ lines.push("");
4669
+ console.log(lines.join("\n"));
4670
+ return;
4671
+ }
4672
+ lines.push(` CometChat React integration \u2014 your progress`);
4673
+ lines.push("");
4674
+ for (const item of r.items) {
4675
+ const icon = item.state === "done" ? "\u2713" : item.state === "skipped" ? "\u2014" : "\u25CB";
4676
+ const stepCol = `Step ${item.step}:`.padEnd(8);
4677
+ const labelCol = item.label.padEnd(28);
4678
+ const detail = item.detail ? ` (${item.detail})` : "";
4679
+ lines.push(` ${icon} ${stepCol} ${labelCol}${detail}`);
4680
+ }
4681
+ lines.push("");
4682
+ lines.push(
4683
+ ` Audit log: ${r.audit_log_path ?? "none"}` + (r.audit_log_path ? ` (${r.audit_log_entries} entr${r.audit_log_entries === 1 ? "y" : "ies"})` : "")
4684
+ );
4685
+ lines.push(` Files owned: ${r.files_owned_count}`);
4686
+ lines.push(` Drift: ${r.drift_count > 0 ? `${r.drift_count} customized file(s)` : "none"}`);
4687
+ if (r.next_suggested) {
4688
+ lines.push("");
4689
+ lines.push(" Next suggested step:");
4690
+ lines.push(` \u2192 ${r.next_suggested}`);
4691
+ }
4692
+ lines.push("");
4693
+ console.log(lines.join("\n"));
4694
+ }
4695
+
4191
4696
  // src/index.ts
4192
4697
  var VERSION = "0.0.1";
4193
4698
  var HELP = `
@@ -4273,6 +4778,12 @@ Commands:
4273
4778
  for production sign-up flows. Currently nextjs,
4274
4779
  react-router, and astro.
4275
4780
 
4781
+ status Journey-shaped progress summary. Aggregates state +
4782
+ applied_features + file markers into a checklist
4783
+ that mirrors the React UI Kit Integration Journey
4784
+ (base, features, widget, prod-auth, theme, etc.)
4785
+ and suggests the highest-leverage next action.
4786
+
4276
4787
  apply-feature <id> Apply a component-swap feature on top of the
4277
4788
  current integration (e.g.
4278
4789
  \`apply-feature rich-text-formatting\` swaps
@@ -4373,6 +4884,8 @@ async function run(argv) {
4373
4884
  return addUserMgmt(args);
4374
4885
  case "apply-feature":
4375
4886
  return applyFeature(args);
4887
+ case "status":
4888
+ return status(args);
4376
4889
  default:
4377
4890
  console.error(`Unknown command: ${args.command}`);
4378
4891
  console.error("Run `cometchat --help` for usage.");
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "version": "1.0.0",
4
4
  "sdk_compatibility": ">=6.0.0 <7.0.0",
5
- "description": "Catalog of CometChat features available in the React UI Kit. Categorized by what work is needed to enable each one.",
5
+ "description": "Catalog of CometChat features available in the React UI Kit. Categorized by what work is needed to enable each one. NOTE: this catalog stores OUR taxonomy (default vs dashboard-toggle vs package-install vs component-swap) plus a `dashboard_path` HINT for navigation. The hint is short and intentionally fragile — the canonical source is always the docs URL constructed from `docs_topic`. Agents must query the cometchat-docs MCP at runtime for current dashboard navigation, prop signatures, builder methods, and SDK reference. This catalog is for routing, not for SDK reference.",
6
6
  "feature_types": {
7
7
  "default": "Already enabled by default in the UI Kit components your integration uses. Zero code changes needed — the skill just shows you which component renders it.",
8
8
  "dashboard-toggle": "Requires flipping a toggle in the CometChat Dashboard at https://app.cometchat.com. Some have their UI decorator auto-attached by the UI Kit (auto_wired_in_uikit: true) — the dashboard flip is the only thing needed. Others additionally require passing the extension to UIKitSettingsBuilder.setExtensions([...]) before init.",
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@cometchat/skills-cli",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "CLI for the CometChat skills v2 architecture — detect, view, apply, verify CometChat integrations in React projects.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "bin": {
8
- "cometchat": "./bin/cometchat.mjs"
8
+ "cometchat": "bin/cometchat.mjs"
9
9
  },
10
10
  "files": [
11
11
  "bin/",