@cometchat/skills 4.0.0 → 4.1.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/README.md CHANGED
@@ -10,7 +10,7 @@ v4 takes an AI-first approach: your agent has a short conversation with you to u
10
10
  npx @cometchat/skills add
11
11
  ```
12
12
 
13
- That's it — one command for every supported framework. The installer detects what you're working with (React, Next.js, React Router, Astro, Expo, bare React Native, Angular, native Android, Flutter, or native iOS) from your project and installs the right skills.
13
+ That's it — one command for every supported framework. The installer detects what you're working with (React, Next.js, React Router, Astro, Expo, bare React Native, Angular, native Android, Flutter, or native iOS) from your project, then opens an interactive picker so you can choose which AI agents to install for — Claude Code, Cursor, Codex, Cline, Kiro, Replit Agent, and [50+ more](https://github.com/vercel-labs/skills) all in one pass.
14
14
 
15
15
  Override detection if needed:
16
16
 
@@ -24,7 +24,47 @@ npx @cometchat/skills add --family ios # Native iOS (V5 stable)
24
24
  npx @cometchat/skills add --family all # install every published skill
25
25
  ```
26
26
 
27
- Supported IDEs: Claude Code (default), Cursor, Kiro, VS Code Copilot, Replit Agent. Use `--ide <name>` to target a specific one, or `--ide all`. Replit users: skills land in `.agents/skills/` automatically when you pass `--ide replit`.
27
+ ### Two install shapes
28
+
29
+ **Base install (default, recommended)** — `npx @cometchat/skills add` writes only the cross-family skills (the `cometchat` dispatcher and friends). Smallest initial install. After it lands, open your project in your IDE and run `/cometchat`:
30
+
31
+ 1. The dispatcher detects your framework (Vite + React → `web`, Expo → `native`, etc.)
32
+ 2. It asks the agent to install family-specific skills on demand via `npx @cometchat/skills add --family <X> --ide <Y>`
33
+ 3. The agent gets the 13 web skills (or 31 android, 28 flutter, etc.) at exactly the moment they're needed
34
+ 4. Integration continues with the full skill set loaded
35
+
36
+ You install once with the multi-agent picker (Claude Code, Cursor, Codex, Cline, Kiro, Replit Agent, [50+ more](https://github.com/vercel-labs/skills)); the dispatcher handles framework-specific expansion as the project actually needs it.
37
+
38
+ **Full family install** — `npx @cometchat/skills add --family <name>` writes the dispatcher + every skill for that family upfront (web=13, android=31, flutter=28, etc.). Use when you know the framework upfront and want all skills present without runtime expansion. Power users + CI smoke tests.
39
+
40
+ ```bash
41
+ # Base install (recommended) — picker + dispatcher routes the rest
42
+ npx @cometchat/skills add
43
+
44
+ # Full family install — power-user / CI flow
45
+ npx @cometchat/skills add --family web # all 13 web skills upfront
46
+ npx @cometchat/skills add --family native # all 13 RN skills upfront
47
+ npx @cometchat/skills add --family android # all 31 Android skills upfront
48
+ # ... etc
49
+ ```
50
+
51
+ ### Direct-write to one IDE (CI / Dockerfile)
52
+
53
+ When stdin isn't a TTY (CI, Docker), or when you pass `--ide <name>`, the picker is skipped and skills are written directly to one IDE's directory. Default is base install; pass `--family <name>` for the full family install.
54
+
55
+ ```bash
56
+ npx @cometchat/skills add --ide claude # base install to .claude/skills/
57
+ npx @cometchat/skills add --ide cursor # base install to .cursor/skills/
58
+ npx @cometchat/skills add --ide kiro # base install to .kiro/skills/
59
+ npx @cometchat/skills add --ide replit # base install to .agents/skills/
60
+ npx @cometchat/skills add --ide copilot # base install to .github/copilot-instructions.md
61
+ npx @cometchat/skills add --ide all # write base install to every supported IDE
62
+ npx @cometchat/skills add --ide claude --family web # full web family install to .claude/skills/
63
+ ```
64
+
65
+ Direct-write supports 10 agents: `claude`, `cursor`, `kiro`, `replit`, `copilot`, `continue`, `cline`, `aider`, `codex`, `gemini`. The interactive picker supports 55+ via the vercel-labs/skills ecosystem.
66
+
67
+ Pass `--no-picker` to force direct-write even in an interactive terminal.
28
68
 
29
69
  > **Migrating from v3?** `npx @cometchat/skills-native add` still works (with a deprecation notice). New projects should use the unified command.
30
70
 
package/bin/install.js CHANGED
@@ -6,6 +6,7 @@ const fs = require("fs");
6
6
  const path = require("path");
7
7
  const os = require("os");
8
8
  const readline = require("readline");
9
+ const { spawn } = require("child_process");
9
10
 
10
11
  // ── Lazy-load optional deps ───────────────────────────────────────────────────
11
12
  function tryRequire(name) {
@@ -157,6 +158,26 @@ const SKILLS = [
157
158
  { name: "cometchat-ios-troubleshooting", families: ["ios"], description: "iOS troubleshooting: SPM, CocoaPods, Xcode build errors, Info.plist, runtime crashes" },
158
159
  ];
159
160
 
161
+ // ── Base skills (cross-family) ────────────────────────────────────────────────
162
+ //
163
+ // Skills registered for ALL six families. These are the baseline a dispatcher
164
+ // needs to detect framework and route — they're installed in the default
165
+ // interactive flow, before any family is resolved. The dispatcher (read at
166
+ // runtime in the user's IDE) detects the framework and asks the agent to
167
+ // install the relevant family skills via:
168
+ //
169
+ // npx @cometchat/skills add --family <X> --ide <Y>
170
+ //
171
+ // (Logic already in `skills/cometchat/SKILL.md` Step 1 — "If `framework` is
172
+ // X AND `cometchat-{X}-core` is NOT loaded: <install command>".)
173
+ //
174
+ // This list grows automatically as more cross-family skills are registered
175
+ // (e.g. when the calls work merges, cometchat-calls/i18n/a11y join).
176
+ const ALL_FAMILIES_SET = ["web", "native", "flutter", "angular", "android", "ios"];
177
+ const BASE_SKILLS = SKILLS.filter(s =>
178
+ ALL_FAMILIES_SET.every(f => s.families.includes(f))
179
+ );
180
+
160
181
  // ── Framework → family routing ────────────────────────────────────────────────
161
182
  const FRAMEWORK_TO_FAMILY = {
162
183
  reactjs: "web",
@@ -384,13 +405,74 @@ function installCopilotSkills(skillsToInstall, baseDir) {
384
405
  return dest;
385
406
  }
386
407
 
408
+ // ── Multi-agent picker via vercel-labs/skills ────────────────────────────────
409
+ //
410
+ // Spawns `npx -y skills@<pin> add cometchat-team/cometchat-skills -s <names>`
411
+ // so the interactive multi-agent picker (Claude Code / Cursor / Codex /
412
+ // Cline / Kiro / Replit / 50+ agents) shows. The user picks which agents
413
+ // to write to; the skills CLI handles the per-agent path conventions.
414
+ //
415
+ // We pre-resolve the family-specific skill list (from `resolveFamilies` +
416
+ // SKILLS table) and pass the names via `-s name1 name2 ...` so the user
417
+ // only sees skills relevant to their detected family — not all 100+ in
418
+ // the marketplace.
419
+ //
420
+ // Pin: `skills@1.5.5` was the version verified against this codebase.
421
+ // Bump on intentional re-verification; pre-pin avoids breakage from a
422
+ // future major bump in the upstream CLI.
423
+ const SKILLS_CLI_PIN = "skills@1.5.5";
424
+ const SKILLS_REPO = "cometchat-team/cometchat-skills";
425
+
426
+ async function delegateToSkillsCli({ skills, families, isGlobal }) {
427
+ const skillNames = skills.map(s => s.name);
428
+ const familyLabel = families.includes("all") ? "all" : families.join("+");
429
+
430
+ console.log(`\n ${c.bold(c.cyan("CometChat Skills"))} — ${c.bold(familyLabel)} family — ${skillNames.length} skills`);
431
+ console.log(` ${c.gray("Launching multi-agent picker via vercel-labs/skills...")}\n`);
432
+
433
+ const npxArgs = [
434
+ "-y", // auto-accept the npx install prompt for the skills CLI itself
435
+ SKILLS_CLI_PIN,
436
+ "add",
437
+ SKILLS_REPO,
438
+ "-s", ...skillNames, // space-separated skill names (skills CLI's flag shape)
439
+ ];
440
+ if (isGlobal) npxArgs.push("-g");
441
+
442
+ return new Promise((resolve) => {
443
+ const child = spawn("npx", npxArgs, {
444
+ stdio: "inherit",
445
+ shell: false,
446
+ });
447
+ child.on("close", (code) => resolve(code ?? 1));
448
+ child.on("error", (err) => {
449
+ console.error(c.red(`\n ✗ Failed to spawn skills CLI: ${err.message}`));
450
+ console.error(c.dim(` Falling back to legacy direct-write — re-run with --ide <name> to bypass the picker.\n`));
451
+ resolve(2);
452
+ });
453
+ });
454
+ }
455
+
387
456
  function printHelp() {
388
457
  console.log(`
389
458
  ${c.bold("@cometchat/skills")} — Install CometChat AI coding skills
390
459
 
391
460
  ${c.bold("Usage:")}
392
- ${c.cyan("npx @cometchat/skills add")} Auto-detect framework + install
393
- ${c.cyan("npx @cometchat/skills add --family <name>")} Override detection
461
+ ${c.cyan("npx @cometchat/skills add")} Base install + interactive multi-agent picker
462
+ ${c.cyan("npx @cometchat/skills add --family <name>")} Install full family upfront (no runtime expansion)
463
+ ${c.cyan("npx @cometchat/skills add --ide <name>")} Direct-write base skills to one IDE (CI/scripted)
464
+
465
+ ${c.bold("Two install shapes:")}
466
+ ${c.bold("Base install")} (default — no --family flag): writes only the cross-family
467
+ skills (the cometchat dispatcher + cross-family helpers). Once installed,
468
+ open your project in your IDE and run /cometchat — the dispatcher detects
469
+ your framework and asks the agent to install the family-specific skills
470
+ via \`npx @cometchat/skills add --family <X> --ide <Y>\`. Smallest initial
471
+ install, dispatcher routes the rest. Recommended for most users.
472
+ ${c.bold("Full family install")} (--family flag): writes the dispatcher + every
473
+ skill registered for that family (web=13, android=31, flutter=28, etc.)
474
+ upfront. Use when you know the project's framework and prefer all skills
475
+ present immediately. Used by power users + CI smoke tests.
394
476
 
395
477
  ${c.bold("Family values:")}
396
478
  ${c.cyan("web")} React / Next.js / React Router / Astro
@@ -401,7 +483,7 @@ function printHelp() {
401
483
  ${c.cyan("ios")} iOS native (V5 stable)
402
484
  ${c.cyan("all")} Install every skill (legacy v3 behavior)
403
485
 
404
- ${c.bold("IDE selection (default: claude):")}
486
+ ${c.bold("IDE selection (direct-write mode only — default: claude):")}
405
487
  ${c.cyan("--ide cursor")} ${c.cyan("--ide kiro")} ${c.cyan("--ide copilot")} ${c.cyan("--ide replit")} ${c.cyan("--ide all")}
406
488
 
407
489
  ${c.bold("Multi-family / monorepo:")}
@@ -409,8 +491,9 @@ function printHelp() {
409
491
 
410
492
  ${c.bold("Other:")}
411
493
  ${c.cyan("--global")} Install globally (~/.claude/skills/, etc.)
412
- ${c.cyan("--clean")} Wipe existing cometchat-* skill dirs before install
494
+ ${c.cyan("--clean")} Wipe existing cometchat-* skill dirs before install (direct-write mode only)
413
495
  ${c.cyan("--list")} Show every skill with its family tags
496
+ ${c.cyan("--no-picker")} Force direct-write even in interactive TTY (useful for testing the legacy path)
414
497
 
415
498
  ${c.bold("After installing, open your project in your IDE and run:")}
416
499
  ${c.cyan("/cometchat")}
@@ -448,37 +531,88 @@ async function main() {
448
531
  process.exit(0);
449
532
  }
450
533
 
451
- // Resolve which families to install (flag(s) → detect → prompt).
452
- const families = await resolveFamilies(args);
453
-
454
- // Build the set of skills to install — union over all selected families.
455
- // "all" is a singleton meaning "every published skill" (legacy v3 behavior).
534
+ // ── Install mode selection ─────────────────────────────────────────────
535
+ //
536
+ // Three valid invocation shapes:
537
+ //
538
+ // 1. `npx @cometchat/skills add` (TTY, no --family, no --ide)
539
+ // → BASE INSTALL: install only cross-family skills (cometchat
540
+ // dispatcher + cometchat-calls + i18n + a11y) via the multi-agent
541
+ // picker. The dispatcher detects the framework AT RUNTIME inside
542
+ // the user's IDE and asks the agent to install family-specific
543
+ // skills via `npx @cometchat/skills add --family <X> --ide <Y>`.
544
+ // Smallest initial install; routing is the dispatcher's job.
545
+ //
546
+ // 2. `npx @cometchat/skills add --family <X>` (TTY)
547
+ // → FAMILY INSTALL: install the dispatcher + every skill registered
548
+ // for family X (web=13, android=31, etc.) via the picker. Use this
549
+ // when you know upfront which family you want and prefer all
550
+ // skills present immediately.
551
+ //
552
+ // 3. `npx @cometchat/skills add --ide <Y>` (or non-TTY, e.g. CI)
553
+ // → DIRECT WRITE: skips the picker and writes directly to one IDE's
554
+ // directory. With --family, writes the family subset; without, falls
555
+ // back to BASE skills (CI smoke). Used by Dockerfiles and the
556
+ // legacy single-agent flow.
557
+ const ideExplicit = ideIdx !== -1;
558
+ const familyExplicit = args.includes("--family");
559
+ const isTTY = process.stdin.isTTY && process.stdout.isTTY;
560
+ const skipPicker = ideExplicit || !isTTY || args.includes("--no-picker");
561
+
562
+ // Resolve skill set based on mode:
563
+ // - --family X → install the dispatcher + every skill registered for X
564
+ // (legacy power-user / CI flow; everything present immediately)
565
+ // - No --family (with or without --ide) → BASE install only. The
566
+ // dispatcher handles family-specific install at runtime via its own
567
+ // `npx @cometchat/skills add --family <X>` invocation logic.
456
568
  let skillsToInstall;
457
- if (families.includes("all")) {
458
- skillsToInstall = SKILLS;
459
- } else {
460
- const seen = new Set();
461
- skillsToInstall = [];
462
- for (const fam of families) {
463
- for (const s of SKILLS) {
464
- if (s.families.includes(fam) && !seen.has(s.name)) {
465
- seen.add(s.name);
466
- skillsToInstall.push(s);
569
+ let families;
570
+ if (familyExplicit) {
571
+ families = await resolveFamilies(args);
572
+ if (families.includes("all")) {
573
+ skillsToInstall = SKILLS;
574
+ } else {
575
+ const seen = new Set();
576
+ skillsToInstall = [];
577
+ for (const fam of families) {
578
+ for (const s of SKILLS) {
579
+ if (s.families.includes(fam) && !seen.has(s.name)) {
580
+ seen.add(s.name);
581
+ skillsToInstall.push(s);
582
+ }
467
583
  }
468
584
  }
469
585
  }
586
+
587
+ // If no pattern skills matched (only the dispatcher), all selected
588
+ // families are "coming soon" — bail with a friendly message rather than
589
+ // installing a half-broken set.
590
+ const patternSkills = skillsToInstall.filter(s => s.name !== "cometchat");
591
+ if (patternSkills.length === 0) {
592
+ const labels = families.map(f => FAMILY_LABELS[f] || f).join(", ");
593
+ console.log(c.yellow(`\n ⚠ Pattern skills for ${labels} aren't published yet.`));
594
+ console.log(` Supported families today: ${c.cyan("web")}, ${c.cyan("native")}, ${c.cyan("angular")}, ${c.cyan("android")}, ${c.cyan("flutter")}, ${c.cyan("ios")}.`);
595
+ console.log(` Run with one of those, or wait for ${families.join(" + ")} skills to ship.\n`);
596
+ process.exit(1);
597
+ }
598
+ } else {
599
+ // Default: base install only (whether interactive or with --ide).
600
+ // The dispatcher detects the framework at runtime and installs the
601
+ // family-specific skills via its own runtime npx invocation.
602
+ skillsToInstall = BASE_SKILLS;
603
+ families = ["base"];
604
+ console.log(`\n ${c.bold(c.cyan("CometChat Skills"))} ${c.dim("(base install — dispatcher routes the rest at runtime)")}`);
605
+ console.log(` ${c.gray(`Installing ${BASE_SKILLS.length} cross-family ${BASE_SKILLS.length === 1 ? "skill" : "skills"}: ${BASE_SKILLS.map(s => s.name).join(", ")}.`)}`);
606
+ console.log(` ${c.gray("After install, open your project in your IDE → /cometchat detects your framework and asks the agent to install family-specific skills on demand.")}\n`);
470
607
  }
471
608
 
472
- // If no pattern skills matched (only the dispatcher), all selected families
473
- // are "coming soon" — bail with a friendly message rather than installing a
474
- // half-broken set.
475
- const patternSkills = skillsToInstall.filter(s => s.name !== "cometchat");
476
- if (patternSkills.length === 0) {
477
- const labels = families.map(f => FAMILY_LABELS[f] || f).join(", ");
478
- console.log(c.yellow(`\n ⚠ Pattern skills for ${labels} aren't published yet.`));
479
- console.log(` Supported families today: ${c.cyan("web")}, ${c.cyan("native")}, ${c.cyan("angular")}, ${c.cyan("android")}, ${c.cyan("flutter")}, ${c.cyan("ios")}.`);
480
- console.log(` Run with one of those, or wait for ${families.join(" + ")} skills to ship.\n`);
481
- process.exit(1);
609
+ if (!skipPicker) {
610
+ const exitCode = await delegateToSkillsCli({
611
+ skills: skillsToInstall,
612
+ families,
613
+ isGlobal,
614
+ });
615
+ process.exit(exitCode);
482
616
  }
483
617
 
484
618
  const targets = ideArg === "all" ? Object.keys(IDE_TARGETS) : [ideArg];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cometchat/skills",
3
- "version": "4.0.0",
3
+ "version": "4.1.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -141,6 +141,55 @@ Save the choice into `.cometchat/config.json` under `flutter_version`.
141
141
 
142
142
  Store this mental map — you'll use it throughout the conversation.
143
143
 
144
+ #### Then show the user what you found — Step 1.5 (the "I see you" moment)
145
+
146
+ This is the most important moment of the whole flow. After running detection + reading the project, narrate what you found in **3–5 specific, observation-grounded bullets** BEFORE asking any question. The user should feel that you understand their project before deciding whether to trust you with it.
147
+
148
+ The shape (use it verbatim — the structure earns trust):
149
+
150
+ > Taking a look at your project...
151
+ >
152
+ > - **{Framework} + {Build tool} {version}** {with TypeScript / JavaScript / etc., as detected}
153
+ > - **{Router or nav state}** — {one observation about how routing is set up, or "no router yet" for greenfield}
154
+ > - **{Auth system status}** — {"NextAuth detected → I'll wire token-based login", or "no auth detected → I'll start with dev mode + a test user; you can upgrade later"}
155
+ > - **{Existing CometChat state}** — {"existing `cometchat/` folder with X — I'll patch around it", or "fresh start — no prior CometChat code"}
156
+ > - **{One personal observation}** — {something specific you noticed: "Tailwind classes throughout", "shadcn/ui components", "monorepo with apps/* and packages/*"}
157
+ >
158
+ > Ready to set this up? I'll walk you through account setup, then ask where chat should live.
159
+
160
+ **The rules for this moment:**
161
+
162
+ 1. **Be specific, not generic.** "Vite + React 19 + TypeScript" beats "a React project." Read the actual versions from `package.json`.
163
+ 2. **Five bullets max.** Beyond five, it stops feeling observational and starts feeling like a recital. Cut to what's *interesting* about the project.
164
+ 3. **Lead with what's load-bearing.** Framework + version, router, auth, existing CometChat, then one personal touch. The personal touch is what makes it land — show you actually looked.
165
+ 4. **Skip the bullet if there's nothing to say.** No router on greenfield → say "no router yet"; don't invent one. No auth → say "no auth detected"; don't list every package you didn't find.
166
+ 5. **End with a single confident question.** Not five questions. The flow continues into Step 2 (credentials) or Step 3 (placement) — let the next step ask.
167
+
168
+ **Examples of good vs bad bullets:**
169
+
170
+ | ✓ Good (specific, observational) | ✗ Bad (generic, unfounded) |
171
+ |---|---|
172
+ | "Vite + React 19 + TypeScript, Tailwind for styling" | "A React project with TypeScript" |
173
+ | "React Router v7 detected (`routes.ts` + `react-router.config.ts`)" | "Some routing is configured" |
174
+ | "NextAuth in `auth.config.ts` — I'll mint CometChat tokens server-side via your existing session cookie" | "Authentication is set up" |
175
+ | "shadcn/ui detected (`components/ui/*`) — I'll use your existing Button + Dialog primitives in the chat trigger" | "Some UI components are present" |
176
+ | "Monorepo: `apps/web` is your dashboard, `apps/marketing` is the public site — I'll integrate into apps/web" | "This is a monorepo" |
177
+
178
+ **For greenfield projects (the test case):**
179
+
180
+ > Taking a look at your project...
181
+ >
182
+ > - **Vite + React 19 + TypeScript** — fresh `cometchat-test-app` scaffold
183
+ > - **No router yet** — for the demo, chat will mount in `src/App.tsx` directly; we can move it to a route later
184
+ > - **No auth system detected** — I'll set you up in dev mode with a pre-seeded test user (`cometchat-uid-1`); production auth is a one-flag upgrade later
185
+ > - **Fresh start** — no existing CometChat code to patch around
186
+ >
187
+ > Ready to set this up? I'll get you a CometChat account first, then ask where chat should live.
188
+
189
+ This moment costs ~5 seconds of conversation but anchors the rest. Skip it and the user feels like they're talking to a script. Run it well and the rest of the flow feels effortless.
190
+
191
+ ---
192
+
144
193
  **Compatibility baselines (the CLI enforces these):**
145
194
  - Web: react@<18 → upgrade required; nextjs@<13 → warning; astro@<4 → warning
146
195
  - RN: react-native@<0.70 → upgrade required; expo@<49 → upgrade required
@@ -614,29 +663,46 @@ Otherwise, use `AskUserQuestion`:
614
663
 
615
664
  #### 3b. Show what you recommend and why
616
665
 
617
- The recommendation table differs by family because the placement vocabulary is different (web has routes/drawers/widgets; RN has screens/tabs/sheets):
666
+ This is the second wow moment after the detection summary. Don't just list a placement **tell the user why**. Two sentences of reasoning earn confidence; a table alone reads like a lookup.
667
+
668
+ The recommendation has two layers:
669
+
670
+ 1. **The placement** — what you'll set up (route / drawer / modal / tab / widget)
671
+ 2. **The reason** — why this fits the user's archetype (one or two sentences grounded in how their kind of app actually works)
672
+
673
+ When you write your response, lead with the reason, then the concrete placement, then offer to override:
674
+
675
+ > **For a marketplace app, I'd put a "Chat with seller" drawer on your product page + an inbox at `/messages`.**
676
+ >
677
+ > The drawer keeps buyers in the buying flow — they can ask a question without losing the listing. The inbox is for going back to past conversations. Two surfaces, one integration.
678
+ >
679
+ > Sound right, or want to try a different shape?
680
+
681
+ That's the shape. The recommendation tables below are the *what*; the reasoning column is the *why* you should narrate.
618
682
 
619
683
  **Web family (reactjs, nextjs, react-router, astro):**
620
684
 
621
- | Intent | What you'll set up |
622
- |---|---|
623
- | **Messaging app** | A dedicated messages page at a route you choose. Two-pane: conversation list + active chat. |
624
- | **Marketplace** | A "Chat with seller" drawer on your product page + an inbox page at /messages. |
625
- | **SaaS / dashboard** | A chat modal triggered from your navbar + a full messages page. |
626
- | **Social / community** | A full messenger page with tabs: Chats, Calls, Users, Groups. |
627
- | **Support** | A floating widget bubble in the bottom-right corner. |
685
+ | Intent | Placement | Why |
686
+ |---|---|---|
687
+ | **Messaging app** | Dedicated messages page (route you pick), two-pane: conversation list + active chat | Chat IS the product. Users land directly on it; the route is the home of your app. |
688
+ | **Marketplace** | "Chat with seller" drawer on the product page + inbox at `/messages` | Drawer keeps buyers in the buying flow; the inbox handles "go back to a past conversation." |
689
+ | **SaaS / dashboard** | Modal triggered from your navbar + full messages page | Modal feels lightweight (chat without leaving your work); the page is for serious conversations. |
690
+ | **Social / community** | Full messenger page with tabs: Chats, Calls, Users, Groups | Discovery matters as much as messaging — users want to find people, not just their existing threads. |
691
+ | **Support** | Floating widget bubble in the bottom-right | One-way customer-to-team — minimal cognitive load on the customer; your team triages from the dashboard. |
628
692
 
629
693
  **React Native family (expo, react-native):**
630
694
 
631
- | Intent | What you'll set up |
632
- |---|---|
633
- | **Messaging app** | A dedicated "Messages" bottom tab. Conversations listtap a conversation message thread. |
634
- | **Marketplace** | A "Chat with seller" button on the product screen that opens a modal with the message thread. Plus an "Inbox" stack screen for all conversations. |
635
- | **SaaS / productivity** | A "Chat" stack screen accessible from the nav or a header button. Optionally a bottom sheet for quick replies. |
636
- | **Social / community** | A "Messages" bottom tab with conversations list + message thread. Plus a "Message" button on user profile screens that opens a modal. |
637
- | **Support** | A modal triggered from a "Help" or "Support" button in the header or settings. |
695
+ | Intent | Placement | Why |
696
+ |---|---|---|
697
+ | **Messaging app** | Dedicated "Messages" bottom tab conversationsthread | Mobile users expect chat as a first-class destination; a tab puts it one tap away. |
698
+ | **Marketplace** | "Chat with seller" button on the product screen modal thread + Inbox stack screen | Modal preserves the buying context; the Inbox is the "back to a conversation" entry point. |
699
+ | **SaaS / productivity** | "Chat" stack screen accessible from nav, optionally bottom sheet for quick replies | Stack screen for focused conversations; bottom sheet for fast back-and-forth without leaving your current work. |
700
+ | **Social / community** | "Messages" bottom tab + "Message" button on profile screens → modal thread | Tab handles discovery; per-profile button is the "I want to talk to THIS person" path. |
701
+ | **Support** | Modal triggered from a "Help" or "Support" button in header/settings | Lightweight, doesn't compete with your product's primary tabs. |
638
702
 
639
- When explaining, reference the ASCII diagrams from `cometchat-placement` (web) or `cometchat-native-placement` (RN) so the user can visualize.
703
+ When explaining, reference the ASCII diagrams from `cometchat-placement` (web) or `cometchat-native-placement` (RN) so the user can visualize the shape.
704
+
705
+ **One sentence to hand them control:** end the recommendation with "Sound right, or want to try a different shape?" — never "Which would you like?" The first phrasing implies you've thought it through and they can override; the second implies you're just collecting answers.
640
706
 
641
707
  Ask: "Does this sound right, or do you want a different approach?" Let them override.
642
708
 
@@ -758,60 +824,132 @@ If the user has auth AND wants to set up production mode now:
758
824
 
759
825
  If they share an example, validate it's CometChat-compatible (alphanumeric, underscores, hyphens — no spaces or special chars; max 100 chars). Firebase UIDs, Clerk user IDs, Supabase UUIDs, and Auth0 `sub` claims are all CometChat-compatible by default.
760
826
 
761
- #### 3f. Confirm the plan
827
+ #### 3f. Confirm the plan — the third wow moment
828
+
829
+ **This is the trust contract. Show EXACTLY what you'll do BEFORE doing it.** Three sections, in this order:
830
+
831
+ 1. **Files I'll create** — new files, with a one-line purpose for each
832
+ 2. **Files I'll modify** — existing files, with the specific edit (not "wrap with provider" alone — say "wrap the children of `<Layout>` with `<CometChatProvider>` at line ~14")
833
+ 3. **Files I won't touch** — call out the load-bearing files that stay untouched (auth config, route definitions outside the chat surface, your existing components). This is the reassurance.
834
+
835
+ Then dependencies + auth mode + an approval line that hands the user control.
836
+
837
+ The shape (use it verbatim — three sections + reassurance):
838
+
839
+ > Here's the plan:
840
+ >
841
+ > **Files I'll create**
842
+ > - `cometchat/CometChatProvider.tsx` — wraps the kit's auth + theme providers, gates render on login
843
+ > - `cometchat/init.ts` — module-level CometChat.init + login, called from the provider
844
+ > - `app/messages/page.tsx` — full-page messages route (your inbox)
845
+ > - `app/components/ChatDrawer.tsx` — the "chat with seller" drawer for product pages
846
+ > - `.env.local` — your CometChat App ID + Region + Auth Key (gitignored)
847
+ >
848
+ > **Files I'll modify**
849
+ > - `app/layout.tsx` — wrap `{children}` with `<CometChatProvider>` (one line, around line 14)
850
+ > - `app/products/[id]/page.tsx` — add the `<ChatDrawer />` trigger button next to the seller info (your existing layout stays)
851
+ > - `app/components/Navbar.tsx` — add a `<Link href="/messages">Messages</Link>` next to your existing nav items
852
+ >
853
+ > **Files I won't touch**
854
+ > - `auth.config.ts` — your NextAuth setup stays as-is; we'll wire CometChat to it in production-auth mode later
855
+ > - `tailwind.config.ts`, `globals.css` — no styling changes outside `cometchat/`
856
+ > - Anything under `app/products/`, `app/cart/`, etc. — your existing routes are untouched
857
+ >
858
+ > **Dependencies**
859
+ > `@cometchat/chat-sdk-javascript`, `@cometchat/chat-uikit-react`
860
+ >
861
+ > **Auth mode** Development (Auth Key for now; production auth is a one-flag upgrade later)
862
+ >
863
+ > **Estimated time** ~30 seconds to write the code, ~1 minute for `npm install` to finish.
864
+ >
865
+ > If anything looks off, just tell me what to change. Otherwise, say "go" and I'll write it.
866
+
867
+ **The rules for this moment:**
762
868
 
763
- **This is critical. Show EXACTLY what you'll do before doing it.** The plan format differs by framework.
869
+ 1. **Be specific in the modify section.** "Wrap with provider" is vague. "Wrap `{children}` with `<CometChatProvider>` at line ~14" tells the user exactly what to expect when they git-diff later.
870
+ 2. **List the don't-touch files explicitly.** Users worry about agents stomping their auth config, their tailwind, their routes. Naming what stays untouched defuses that worry up front.
871
+ 3. **Estimated time matters.** Two short numbers — a few seconds to write code, a couple minutes for `npm install`. Sets expectations; reduces "is it stuck?" mid-flow.
872
+ 4. **End with a hand-off, not a yes/no.** "Say 'go' and I'll write it. Or tell me what to change." Beats "Proceed? [y/n]" — it implies the user can adjust without throwing the whole plan away.
873
+ 5. **Never abbreviate the plan in subsequent runs.** Every integration deserves a fresh, full plan. If the user has been through this before, they can skim — but don't pre-skim for them.
764
874
 
765
- **Web example (Next.js, marketplace):**
766
- > "Here's what I'll create:
875
+ **Web example (Next.js + NextAuth, marketplace):** see the shape above.
876
+
877
+ **Web example (Vite + React, greenfield messaging app):**
878
+
879
+ > Here's the plan:
880
+ >
881
+ > **Files I'll create**
882
+ > - `src/cometchat/CometChatProvider.tsx` — wraps the kit's providers, gates render on login
883
+ > - `src/cometchat/init.ts` — `CometChat.init` + `CometChat.login` (with `cometchat-uid-1` for dev)
884
+ > - `src/components/ChatScreen.tsx` — your full-page chat surface (conversations + messages)
885
+ > - `.env` — your CometChat App ID + Region + Auth Key (gitignored)
886
+ >
887
+ > **Files I'll modify**
888
+ > - `src/main.tsx` — wrap `<App />` with `<CometChatProvider>` (line ~10)
889
+ > - `src/App.tsx` — render `<ChatScreen />` instead of the Vite default (you can move it to a route later)
767
890
  >
768
- > **New files:**
769
- > - `app/providers/CometChatProvider.tsx`
770
- > - `app/messages/page.tsx`
771
- > - `app/components/ChatDrawer.tsx`
772
- > - `.env.local`
891
+ > **Files I won't touch**
892
+ > - `vite.config.ts`, `tsconfig.*` — no build config changes
893
+ > - `src/index.css`, `src/App.css` — kit ships its own CSS; your styles stay
773
894
  >
774
- > **Files I'll modify:**
775
- > - `app/products/[id]/page.tsx` — add ChatDrawer trigger
776
- > - `app/layout.tsx` — wrap with CometChatProvider
777
- > - `app/components/Navbar.tsx` — add 'Messages' link
895
+ > **Dependencies** `@cometchat/chat-sdk-javascript`, `@cometchat/chat-uikit-react`
778
896
  >
779
- > **Dependencies:** @cometchat/chat-sdk-javascript, @cometchat/chat-uikit-react
897
+ > **Auth mode** Development (Auth Key + `cometchat-uid-1`; you can log in as `cometchat-uid-1`–`uid-5` to chat with yourself across browser windows)
780
898
  >
781
- > **Auth mode:** Development (Auth Key).
782
- > Proceed? [y/n]"
899
+ > **Estimated time** ~30 seconds to write, ~1 minute for `npm install`.
900
+ >
901
+ > Say "go" or tell me what to change.
783
902
 
784
903
  **RN example (Expo Router, messaging):**
785
- > "Here's what I'll create:
904
+
905
+ > Here's the plan:
906
+ >
907
+ > **Files I'll create**
908
+ > - `cometchat/CometChatProvider.tsx` — four-wrapper chain (gesture handler → safe area → theme → CometChat)
909
+ > - `cometchat/init.ts` — init + login, module-level guard
910
+ > - `app/(tabs)/messages.tsx` — your Messages tab
911
+ > - `.env` — `EXPO_PUBLIC_COMETCHAT_APP_ID` + region + auth key
786
912
  >
787
- > **New files:**
788
- > - `providers/CometChatProvider.tsx`
789
- > - `app/(tabs)/messages.tsx`
790
- > - `.env`
913
+ > **Files I'll modify**
914
+ > - `app/_layout.tsx` — wrap with the four-wrapper chain (line ~10)
915
+ > - `app/(tabs)/_layout.tsx` — add the Messages tab as the third entry, after Home and Profile
916
+ > - `index.js` — `import 'react-native-gesture-handler'` at line 1 (mandatory; without it release builds break silently)
791
917
  >
792
- > **Files I'll modify:**
793
- > - `app/_layout.tsx` — wrap with the four-wrapper chain
794
- > - `app/(tabs)/_layout.tsx` — add the Messages tab
795
- > - `index.js` — `import 'react-native-gesture-handler'` at line 1 (if missing)
918
+ > **Files I won't touch**
919
+ > - `app.json` — no Expo config changes for dev mode (production push needs them, but that's later)
920
+ > - Your existing `app/(tabs)/index.tsx`, `profile.tsx`stay as-is
796
921
  >
797
- > **Dependencies (via `npx expo install`):**
798
- > @cometchat/chat-uikit-react-native, @cometchat/chat-sdk-react-native,
799
- > react-native-gesture-handler, react-native-reanimated,
800
- > react-native-safe-area-context, react-native-screens,
801
- > @react-native-async-storage/async-storage, @react-native-community/netinfo,
802
- > react-native-video, react-native-image-picker, react-native-document-picker,
803
- > react-native-vector-icons, react-native-fs
922
+ > **Dependencies (via `npx expo install`)**
923
+ > `@cometchat/chat-uikit-react-native`, `@cometchat/chat-sdk-react-native`, `react-native-gesture-handler`, `react-native-reanimated`, `react-native-safe-area-context`, `react-native-screens`, `@react-native-async-storage/async-storage`, `@react-native-community/netinfo`, `react-native-video`, `react-native-image-picker`, `react-native-document-picker`, `react-native-vector-icons`, `react-native-fs`
804
924
  >
805
- > **Auth mode:** Development (Auth Key).
806
- > Proceed? [y/n]"
925
+ > **Auth mode** Development (Auth Key).
926
+ >
927
+ > **Estimated time** ~30 seconds to write, ~3 minutes for `expo install` (RN deps are heavier).
928
+ >
929
+ > Say "go" or tell me what to change.
807
930
 
808
- **Bare RN variant** — same as Expo, except `npm install` instead of `npx expo install`, plus:
809
- - Run `cd ios && pod install`
810
- - Patch `ios/<Name>/Info.plist`, `android/app/src/main/AndroidManifest.xml` for permissions
931
+ **Bare RN variant** — same as Expo, except:
932
+ - `npm install` instead of `npx expo install`
933
+ - Run `cd ios && pod install` (~1-2 min extra)
934
+ - Patch `ios/<Name>/Info.plist`, `android/app/src/main/AndroidManifest.xml` for camera/mic permissions
811
935
  - Add `ios/<Name>/PrivacyInfo.xcprivacy` (Apple Privacy Manifest)
812
936
  - Patch `android/build.gradle` for the async-storage Maven repo (v3+)
813
937
 
814
- Wait for explicit confirmation. If the user says no or wants changes, go back to the relevant question and re-ask.
938
+ Surface these in the "Files I'll modify" section so the user knows they're coming.
939
+
940
+ **After approval — the writing moment:**
941
+
942
+ When the user says "go", narrate progress as you work. Don't be silent for 30 seconds while you write 5 files. Brief structured updates, one per beat:
943
+
944
+ > ✓ Created `cometchat/CometChatProvider.tsx`
945
+ > ✓ Created `cometchat/init.ts`
946
+ > ✓ Modified `src/main.tsx` (wrapped App with provider)
947
+ > ✓ Wrote `.env` (Auth Key hidden)
948
+ > Installing dependencies (this takes ~1 minute)...
949
+
950
+ The structured beats make the writing feel like a contract being executed, not a black box churning.
951
+
952
+ **If the user says no or wants changes:** go back to the relevant question and re-ask. Don't try to negotiate the plan in-line — the plan is atomic. Adjust the source decision, then regenerate the plan.
815
953
 
816
954
  ### Step 4 — Reference pattern skills
817
955
 
@@ -1278,6 +1278,34 @@ These rules apply to ALL placement patterns. Violating any of them causes integr
1278
1278
 
1279
1279
  6. **Every CometChat container must have explicit dimensions.** Components fill 100% of their parent. If the parent has no height, the components collapse to zero. Always set `height`, `min-height`, or use flex/grid layout with a bounded container.
1280
1280
 
1281
+ 6a. **Flex parents that hold a `CometChatMessageList` MUST set `minHeight: 0`** (and `minWidth: 0` for horizontal flex parents). The W3C default `min-height: auto` makes flex children refuse to shrink below their intrinsic content size — so once the conversation grows past the viewport, the list pushes the composer below the fold and the layout breaks. **This is the canonical chat-layout bug** — works fine for short conversations, breaks for long ones. The fix is one CSS property; the diagnosis takes hours if you don't know the rule.
1282
+
1283
+ ```tsx
1284
+ /* ✓ CORRECT — header + list + composer with the flex-shrink trap fixed */
1285
+ <div style={{ display: "flex", flexDirection: "column", height: "100vh" }}>
1286
+ <div style={{ flex: 1, display: "flex", flexDirection: "column", minHeight: 0 }}>
1287
+ <div style={{ flex: "0 0 auto" }}>
1288
+ <CometChatMessageHeader user={user} />
1289
+ </div>
1290
+ <div style={{ flex: "1 1 0", minHeight: 0, overflow: "hidden" }}>
1291
+ <CometChatMessageList user={user} hideReplyInThreadOption />
1292
+ </div>
1293
+ <div style={{ flex: "0 0 auto" }}>
1294
+ <CometChatMessageComposer user={user} />
1295
+ </div>
1296
+ </div>
1297
+ </div>
1298
+
1299
+ /* ✗ WRONG — list grows past the viewport once messages exceed visible area */
1300
+ <div style={{ display: "flex", flexDirection: "column", height: "100vh" }}>
1301
+ <CometChatMessageHeader user={user} />
1302
+ <CometChatMessageList user={user} /> {/* takes intrinsic height, no scroll */}
1303
+ <CometChatMessageComposer user={user} /> {/* falls off the bottom */}
1304
+ </div>
1305
+ ```
1306
+
1307
+ The wrap-each-component-in-a-flex-sized-div pattern is what makes this work. Don't pass the kit components directly as flex children — wrap them.
1308
+
1281
1309
  7. **Resolve target users/groups before rendering CometChat components.** Use `CometChat.getUser(uid)` or `CometChat.getGroup(guid)` to get the full `CometChat.User` or `CometChat.Group` object. Do not pass a raw UID string to `user` props -- they expect object instances.
1282
1310
 
1283
1311
  8. **For SSR frameworks, wrap CometChat components appropriately.** See the `cometchat-core` skill, section 5 (SSR safety), for framework-specific patterns.
@@ -468,30 +468,78 @@ Vite's HMR replaces modules without a full page reload. CometChat's SDK holds a
468
468
 
469
469
  However, if you change the provider file itself during development, HMR may re-execute the module. The `initialized` flag prevents double-init, but the WebSocket connection from the previous module instance may linger. If you see duplicate messages or connection issues during development, do a full page reload (`Ctrl+Shift+R`).
470
470
 
471
- ### Container height
471
+ ### Container height (and the flex-shrink trap that breaks chat layouts)
472
472
 
473
- CometChat components fill 100% of their container. The most common visual bug is components rendering with zero height because their container has no explicit dimensions. Always ensure the chat container has a height:
473
+ CometChat components fill 100% of their container. Two visual bugs to avoid:
474
+
475
+ **Bug 1 — zero height:** components render with zero height because the container has no explicit dimensions.
476
+ **Bug 2 — message list grows past the viewport:** the list scrolls fine until it has too many messages, then pushes the composer below the fold. **This is the classic flex-shrink trap.**
477
+
478
+ The hard rule for two-pane / header+list+composer layouts: every flex container in the chain MUST have `minHeight: 0` (and `minWidth: 0` for horizontal flex). Without it, browsers default flex-children to `min-height: auto` (their intrinsic content size), so the list grows beyond the parent's bounds as messages accumulate.
474
479
 
475
480
  ```tsx
476
- /* CORRECT: explicit height */
481
+ /* CORRECT: explicit height for a single-component surface */
477
482
  <div style={{ height: "100vh" }}>
478
483
  <CometChatConversations ... />
479
484
  </div>
480
485
 
481
- /* CORRECT: flex layout with bounded parent */
486
+ /* CORRECT: header + list + composer with the flex-shrink trap fixed */
482
487
  <div style={{ display: "flex", flexDirection: "column", height: "100vh" }}>
483
488
  <nav>...</nav>
484
- <div style={{ flex: 1 }}>
485
- <CometChatConversations ... />
489
+ <div style={{
490
+ flex: 1,
491
+ display: "flex",
492
+ flexDirection: "column",
493
+ minHeight: 0, // ← THIS IS THE HARD RULE — without it, list grows past the viewport
494
+ }}>
495
+ <div style={{ flex: "0 0 auto" }}>
496
+ <CometChatMessageHeader user={user} />
497
+ </div>
498
+ <div style={{ flex: "1 1 0", minHeight: 0, overflow: "hidden" }}>
499
+ <CometChatMessageList user={user} />
500
+ </div>
501
+ <div style={{ flex: "0 0 auto" }}>
502
+ <CometChatMessageComposer user={user} />
503
+ </div>
486
504
  </div>
487
505
  </div>
488
506
 
489
- /* WRONG: no height constraint -- component collapses to zero */
507
+ /* ✓ CORRECT: two-pane (sidebar + active chat) both axes need min-{width,height}: 0 */
508
+ <div style={{ display: "flex", height: "100vh" }}>
509
+ <div style={{ width: 360, display: "flex", flexDirection: "column" }}>
510
+ <CometChatConversations onItemClick={...} />
511
+ </div>
512
+ <div style={{
513
+ flex: 1,
514
+ display: "flex",
515
+ flexDirection: "column",
516
+ minWidth: 0, // ← horizontal flex parent: prevents content from forcing horizontal scroll
517
+ minHeight: 0, // ← vertical flex (the inner column): prevents the list-overflow bug
518
+ }}>
519
+ {/* header / list / composer wrapped as above */}
520
+ </div>
521
+ </div>
522
+
523
+ /* ✗ WRONG: no height constraint — component collapses to zero */
490
524
  <div>
491
525
  <CometChatConversations ... />
492
526
  </div>
527
+
528
+ /* ✗ WRONG: flex parent without minHeight: 0 — list grows past the viewport once
529
+ you accumulate more messages than fit on-screen. Composer falls off the bottom. */
530
+ <div style={{ display: "flex", flexDirection: "column", height: "100vh" }}>
531
+ <CometChatMessageHeader user={user} />
532
+ <CometChatMessageList user={user} /> {/* takes intrinsic content height */}
533
+ <CometChatMessageComposer user={user} /> {/* gets pushed below the fold */}
534
+ </div>
493
535
  ```
494
536
 
537
+ **Why `minHeight: 0` is non-obvious:** the W3C spec sets the default `min-height` of a flex item to `auto` (its intrinsic content height). For a scrollable list inside a flex column, this means the list refuses to shrink below its content even when the parent is bounded. Setting `minHeight: 0` on the flex parent overrides this, letting the list shrink and scroll within the bounded container instead of overflowing it.
538
+
539
+ **The rule, restated as something to grep for:**
540
+
541
+ > Every flex container that holds `CometChatMessageList` (directly or indirectly) MUST have `minHeight: 0` on it. Same for `minWidth: 0` on horizontal flex parents. Skip this and the layout works for short conversations and breaks for long ones.
542
+
495
543
  ### Vite dependency optimization
496
544
 
497
545
  Vite pre-bundles dependencies for faster dev startup. CometChat's packages are large and may trigger Vite's "new dependency found, reloading" message on first load. This is normal and only happens once. If it causes issues, you can pre-include the packages: