@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 +582 -69
- package/dist/registry/v6/features/catalog.json +1 -1
- package/package.json +2 -2
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.
|
|
2026
|
+
if (r.env_file) {
|
|
1844
2027
|
lines.push("");
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
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
|
|
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 =
|
|
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
|
|
2446
|
-
import { join as
|
|
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
|
|
2652
|
+
return join9(findRegistryRoot(), "v6", "production");
|
|
2458
2653
|
}
|
|
2459
2654
|
function loadProductionManifest(framework) {
|
|
2460
2655
|
const root = productionRoot();
|
|
2461
|
-
const manifestPath =
|
|
2462
|
-
const content =
|
|
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
|
|
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 =
|
|
2693
|
+
const fullPath = join9(root, relPath);
|
|
2499
2694
|
let content;
|
|
2500
2695
|
try {
|
|
2501
|
-
content =
|
|
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
|
-
|
|
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
|
|
3045
|
-
import { join as
|
|
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
|
|
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(
|
|
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
|
-
`
|
|
3087
|
-
`
|
|
3088
|
-
`
|
|
3089
|
-
`
|
|
3090
|
-
|
|
3091
|
-
`
|
|
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
|
|
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(
|
|
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(
|
|
3682
|
+
function buildSummary(status2, errors, warnings, integrated) {
|
|
3464
3683
|
if (!integrated) return "No CometChat integration found. Run `cometchat apply` to get started.";
|
|
3465
|
-
if (
|
|
3466
|
-
if (
|
|
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
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
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
|
|
3689
|
-
import { join as
|
|
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
|
|
3916
|
+
return join11(findRegistryRoot(), "v6", "widget");
|
|
3701
3917
|
}
|
|
3702
3918
|
function loadWidgetManifest(framework) {
|
|
3703
3919
|
const root = widgetRoot();
|
|
3704
|
-
const path =
|
|
3705
|
-
return JSON.parse(
|
|
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
|
|
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
|
|
3844
|
-
import { join as
|
|
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
|
|
4071
|
+
return join12(findRegistryRoot(), "v6", "user-mgmt");
|
|
3856
4072
|
}
|
|
3857
4073
|
function loadManifest2(framework) {
|
|
3858
4074
|
const root = userMgmtRoot();
|
|
3859
|
-
return JSON.parse(
|
|
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
|
|
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
|
|
4021
|
-
import { join as
|
|
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
|
|
4247
|
+
return join13(findRegistryRoot(), "v6", "features", "catalog.json");
|
|
4032
4248
|
}
|
|
4033
4249
|
function loadCatalog2() {
|
|
4034
|
-
return JSON.parse(
|
|
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 =
|
|
4326
|
+
const fullPath = join13(root, relPath);
|
|
4111
4327
|
let content;
|
|
4112
4328
|
try {
|
|
4113
|
-
content =
|
|
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
|
-
|
|
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.
|
|
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": "
|
|
8
|
+
"cometchat": "bin/cometchat.mjs"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"bin/",
|