@cometchat/skills 3.1.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.
Files changed (101) hide show
  1. package/README.md +75 -17
  2. package/bin/install.js +523 -43
  3. package/package.json +17 -8
  4. package/skills/cometchat/SKILL.md +850 -109
  5. package/skills/cometchat-android-v5/SKILL.md +120 -0
  6. package/skills/cometchat-android-v5-components/SKILL.md +430 -0
  7. package/skills/cometchat-android-v5-core/SKILL.md +468 -0
  8. package/skills/cometchat-android-v5-customization/SKILL.md +305 -0
  9. package/skills/cometchat-android-v5-extensions/SKILL.md +124 -0
  10. package/skills/cometchat-android-v5-features/SKILL.md +130 -0
  11. package/skills/cometchat-android-v5-placement/SKILL.md +276 -0
  12. package/skills/cometchat-android-v5-production/SKILL.md +94 -0
  13. package/skills/cometchat-android-v5-push/SKILL.md +620 -0
  14. package/skills/cometchat-android-v5-testing/SKILL.md +187 -0
  15. package/skills/cometchat-android-v5-theming/SKILL.md +166 -0
  16. package/skills/cometchat-android-v5-troubleshooting/SKILL.md +73 -0
  17. package/skills/cometchat-android-v6/SKILL.md +107 -0
  18. package/skills/cometchat-android-v6-builder-settings/SKILL.md +145 -0
  19. package/skills/cometchat-android-v6-compose-components/SKILL.md +202 -0
  20. package/skills/cometchat-android-v6-compose-customization/SKILL.md +280 -0
  21. package/skills/cometchat-android-v6-compose-placement/SKILL.md +213 -0
  22. package/skills/cometchat-android-v6-compose-theming/SKILL.md +224 -0
  23. package/skills/cometchat-android-v6-core/SKILL.md +354 -0
  24. package/skills/cometchat-android-v6-events/SKILL.md +212 -0
  25. package/skills/cometchat-android-v6-extensions/SKILL.md +191 -0
  26. package/skills/cometchat-android-v6-features/SKILL.md +130 -0
  27. package/skills/cometchat-android-v6-kotlin-components/SKILL.md +197 -0
  28. package/skills/cometchat-android-v6-kotlin-customization/SKILL.md +268 -0
  29. package/skills/cometchat-android-v6-kotlin-placement/SKILL.md +248 -0
  30. package/skills/cometchat-android-v6-kotlin-theming/SKILL.md +202 -0
  31. package/skills/cometchat-android-v6-production/SKILL.md +187 -0
  32. package/skills/cometchat-android-v6-push/SKILL.md +274 -0
  33. package/skills/cometchat-android-v6-testing/SKILL.md +279 -0
  34. package/skills/cometchat-android-v6-troubleshooting/SKILL.md +249 -0
  35. package/skills/cometchat-angular-components/SKILL.md +706 -0
  36. package/skills/cometchat-angular-core/SKILL.md +444 -0
  37. package/skills/cometchat-angular-customization/SKILL.md +424 -0
  38. package/skills/cometchat-angular-features/SKILL.md +447 -0
  39. package/skills/cometchat-angular-patterns/SKILL.md +447 -0
  40. package/skills/cometchat-angular-placement/SKILL.md +685 -0
  41. package/skills/cometchat-angular-production/SKILL.md +415 -0
  42. package/skills/cometchat-angular-theming/SKILL.md +365 -0
  43. package/skills/cometchat-angular-troubleshooting/SKILL.md +329 -0
  44. package/skills/cometchat-astro-patterns/SKILL.md +3 -3
  45. package/skills/cometchat-components/SKILL.md +2 -0
  46. package/skills/cometchat-core/SKILL.md +11 -6
  47. package/skills/cometchat-customization/references/component-catalog.md +1 -1
  48. package/skills/cometchat-features/SKILL.md +176 -108
  49. package/skills/cometchat-flutter-v5/SKILL.md +167 -0
  50. package/skills/cometchat-flutter-v5-calls/SKILL.md +192 -0
  51. package/skills/cometchat-flutter-v5-conversations/SKILL.md +117 -0
  52. package/skills/cometchat-flutter-v5-core/SKILL.md +221 -0
  53. package/skills/cometchat-flutter-v5-customization/SKILL.md +227 -0
  54. package/skills/cometchat-flutter-v5-events/SKILL.md +155 -0
  55. package/skills/cometchat-flutter-v5-messages/SKILL.md +258 -0
  56. package/skills/cometchat-flutter-v5-production/SKILL.md +207 -0
  57. package/skills/cometchat-flutter-v5-push/SKILL.md +284 -0
  58. package/skills/cometchat-flutter-v5-theming/SKILL.md +160 -0
  59. package/skills/cometchat-flutter-v5-troubleshooting/SKILL.md +129 -0
  60. package/skills/cometchat-flutter-v5-users-groups/SKILL.md +151 -0
  61. package/skills/cometchat-flutter-v6/SKILL.md +445 -0
  62. package/skills/cometchat-flutter-v6-calls/SKILL.md +333 -0
  63. package/skills/cometchat-flutter-v6-components/SKILL.md +365 -0
  64. package/skills/cometchat-flutter-v6-conversations/SKILL.md +210 -0
  65. package/skills/cometchat-flutter-v6-core/SKILL.md +305 -0
  66. package/skills/cometchat-flutter-v6-customization/SKILL.md +666 -0
  67. package/skills/cometchat-flutter-v6-events/SKILL.md +216 -0
  68. package/skills/cometchat-flutter-v6-features/SKILL.md +406 -0
  69. package/skills/cometchat-flutter-v6-messages/SKILL.md +333 -0
  70. package/skills/cometchat-flutter-v6-migration/SKILL.md +381 -0
  71. package/skills/cometchat-flutter-v6-placement/SKILL.md +398 -0
  72. package/skills/cometchat-flutter-v6-production/SKILL.md +632 -0
  73. package/skills/cometchat-flutter-v6-theming/SKILL.md +260 -0
  74. package/skills/cometchat-flutter-v6-troubleshooting/SKILL.md +726 -0
  75. package/skills/cometchat-flutter-v6-users-groups/SKILL.md +216 -0
  76. package/skills/cometchat-ios/SKILL.md +323 -0
  77. package/skills/cometchat-ios-components/SKILL.md +651 -0
  78. package/skills/cometchat-ios-core/SKILL.md +822 -0
  79. package/skills/cometchat-ios-customization/SKILL.md +825 -0
  80. package/skills/cometchat-ios-features/SKILL.md +725 -0
  81. package/skills/cometchat-ios-placement/SKILL.md +750 -0
  82. package/skills/cometchat-ios-production/SKILL.md +730 -0
  83. package/skills/cometchat-ios-push/SKILL.md +582 -0
  84. package/skills/cometchat-ios-theming/SKILL.md +631 -0
  85. package/skills/cometchat-ios-troubleshooting/SKILL.md +983 -0
  86. package/skills/cometchat-native-bare-patterns/SKILL.md +470 -0
  87. package/skills/cometchat-native-components/SKILL.md +594 -0
  88. package/skills/cometchat-native-core/SKILL.md +407 -0
  89. package/skills/cometchat-native-customization/SKILL.md +594 -0
  90. package/skills/cometchat-native-expo-patterns/SKILL.md +485 -0
  91. package/skills/cometchat-native-features/SKILL.md +608 -0
  92. package/skills/cometchat-native-placement/SKILL.md +500 -0
  93. package/skills/cometchat-native-production/SKILL.md +694 -0
  94. package/skills/cometchat-native-push/SKILL.md +593 -0
  95. package/skills/cometchat-native-testing/SKILL.md +583 -0
  96. package/skills/cometchat-native-theming/SKILL.md +656 -0
  97. package/skills/cometchat-native-troubleshooting/SKILL.md +433 -0
  98. package/skills/cometchat-nextjs-patterns/SKILL.md +5 -5
  99. package/skills/cometchat-placement/SKILL.md +28 -0
  100. package/skills/cometchat-react-patterns/SKILL.md +55 -7
  101. package/skills/cometchat-react-router-patterns/SKILL.md +23 -21
package/bin/install.js CHANGED
@@ -5,6 +5,8 @@
5
5
  const fs = require("fs");
6
6
  const path = require("path");
7
7
  const os = require("os");
8
+ const readline = require("readline");
9
+ const { spawn } = require("child_process");
8
10
 
9
11
  // ── Lazy-load optional deps ───────────────────────────────────────────────────
10
12
  function tryRequire(name) {
@@ -27,25 +29,178 @@ const c = {
27
29
  // ── Skills registry ───────────────────────────────────────────────────────────
28
30
  const SKILLS_SRC_DIR = path.join(__dirname, "..", "skills");
29
31
 
32
+ // Each skill declares which families it belongs to. The dispatcher (`cometchat`)
33
+ // is shared across all families; pattern skills are family-specific. Adding a
34
+ // new family (e.g. flutter) means flipping its `coming soon` placeholder below
35
+ // to real entries with `families: ["flutter"]`.
30
36
  const SKILLS = [
31
- // Core (always installed)
32
- { name: "cometchat", description: "Entry point — detects framework, handles onboarding, guides integration" },
33
- { name: "cometchat-core", description: "Foundational rules — init, login, CSS, env vars, SSR safety, provider" },
34
- { name: "cometchat-components", description: "Component catalog names, props, composition patterns" },
35
- { name: "cometchat-placement", description: "Where to put chatroute, modal, drawer, widget, embedded" },
36
- // Framework patterns
37
- { name: "cometchat-react-patterns", description: "React / Vite / CRA integration patterns" },
38
- { name: "cometchat-nextjs-patterns", description: "Next.js App Router + Pages Router patterns" },
39
- { name: "cometchat-react-router-patterns", description: "React Router v6 / v7 patterns" },
40
- { name: "cometchat-astro-patterns", description: "Astro React islands patterns" },
41
- // Phase B
42
- { name: "cometchat-theming", description: "Theme presets, brand colors, CSS variable overrides" },
43
- { name: "cometchat-features", description: "40+ feature catalog — calls, polls, AI, moderation" },
44
- { name: "cometchat-customization", description: "Component customization — custom views, builders, events" },
45
- { name: "cometchat-production", description: "Production auth — token endpoints, user management, security" },
46
- { name: "cometchat-troubleshooting", description: "Diagnostics — verify, drift, doctor, symptom lookup" },
37
+ // Dispatcher shared across every family
38
+ { name: "cometchat", families: ["web", "native", "flutter", "angular", "android", "ios"], description: "Entry point — detects framework, handles onboarding, guides integration" },
39
+
40
+ // Web (React / Next.js / React Router / Astro)
41
+ { name: "cometchat-core", families: ["web"], description: "Foundational rulesinit, login, CSS, env vars, SSR safety, provider" },
42
+ { name: "cometchat-components", families: ["web"], description: "Component catalog — names, props, composition patterns" },
43
+ { name: "cometchat-placement", families: ["web"], description: "Where to put chat route, modal, drawer, widget, embedded" },
44
+ { name: "cometchat-react-patterns", families: ["web"], description: "React / Vite / CRA integration patterns" },
45
+ { name: "cometchat-nextjs-patterns", families: ["web"], description: "Next.js App Router + Pages Router patterns" },
46
+ { name: "cometchat-react-router-patterns", families: ["web"], description: "React Router v6 / v7 patterns" },
47
+ { name: "cometchat-astro-patterns", families: ["web"], description: "Astro React islands patterns" },
48
+ { name: "cometchat-theming", families: ["web"], description: "Theme presets, brand colors, CSS variable overrides" },
49
+ { name: "cometchat-features", families: ["web"], description: "40+ feature catalog — calls, polls, AI, moderation" },
50
+ { name: "cometchat-customization", families: ["web"], description: "Component customization — custom views, builders, events" },
51
+ { name: "cometchat-production", families: ["web"], description: "Production auth — token endpoints, user management, security" },
52
+ { name: "cometchat-troubleshooting", families: ["web"], description: "Diagnostics — verify, drift, doctor, symptom lookup" },
53
+
54
+ // React Native (Expo + bare RN)
55
+ { name: "cometchat-native-core", families: ["native"], description: "RN: init, login, provider chain, env vars, anti-patterns" },
56
+ { name: "cometchat-native-components", families: ["native"], description: "RN component catalog — names, props, slot views, request builders" },
57
+ { name: "cometchat-native-placement", families: ["native"], description: "RN: stack screen, bottom tab, modal, bottom sheet, embedded" },
58
+ { name: "cometchat-native-expo-patterns", families: ["native"], description: "Expo managed workflow + Expo Router + app.json config" },
59
+ { name: "cometchat-native-bare-patterns", families: ["native"], description: "Bare RN: pod install, native modules, privacy manifest" },
60
+ { name: "cometchat-native-theming", families: ["native"], description: "RN: CometChatThemeProvider — colors, typography, dark mode" },
61
+ { name: "cometchat-native-features", families: ["native"], description: "RN feature catalog — calls, extensions, AI agent, reactions, polls" },
62
+ { name: "cometchat-native-customization", families: ["native"], description: "RN: custom text formatters, events, request builder filtering" },
63
+ { name: "cometchat-native-production", families: ["native"], description: "RN: server-minted auth tokens, user management, external-backend recipes" },
64
+ { name: "cometchat-native-push", families: ["native"], description: "RN push: APNs + FCM, dashboard providers, client registration, deep-link" },
65
+ { name: "cometchat-native-testing", families: ["native"], description: "RN testing: Jest + RNTL, mocking the kit/SDK, Detox vs Maestro, CI" },
66
+ { name: "cometchat-native-troubleshooting", families: ["native"], description: "RN troubleshooting: Metro cache, pod install, native module linking" },
67
+
68
+ // Angular (12-15)
69
+ { name: "cometchat-angular-core", families: ["angular"], description: "Angular: UIKitSettingsBuilder init, login order, environment config, provider" },
70
+ { name: "cometchat-angular-components", families: ["angular"], description: "Angular component catalog — selectors, inputs, content projection, modules" },
71
+ { name: "cometchat-angular-placement", families: ["angular"], description: "Angular: route, modal, drawer, embedded panel — where to put chat" },
72
+ { name: "cometchat-angular-patterns", families: ["angular"], description: "Angular integration patterns — modules, lazy loading, environment files" },
73
+ { name: "cometchat-angular-theming", families: ["angular"], description: "Angular: CometChatThemeService — colors, typography, dark mode" },
74
+ { name: "cometchat-angular-features", families: ["angular"], description: "Angular feature catalog — calls, polls, reactions, extensions, AI" },
75
+ { name: "cometchat-angular-customization", families: ["angular"], description: "Angular: content projection, ng-template slots, custom views" },
76
+ { name: "cometchat-angular-production", families: ["angular"], description: "Angular: server-minted auth tokens, user management, external-backend recipes" },
77
+ { name: "cometchat-angular-troubleshooting", families: ["angular"], description: "Angular troubleshooting: build errors, module imports, Zone.js issues" },
78
+
79
+ // Android native — both V5 (live, Java + Kotlin Views) and V6 (beta,
80
+ // Compose + Kotlin Views) ship together. The V5 + V6 dispatcher entries
81
+ // (`cometchat-android-v5` / `cometchat-android-v6`) are routed by the
82
+ // unified `cometchat` dispatcher after detecting the gradle dep.
83
+ { name: "cometchat-android-v5", families: ["android"], description: "V5 dispatcher — Java + Kotlin Views (com.cometchat:chat-uikit-android:5.x)" },
84
+ { name: "cometchat-android-v5-core", families: ["android"], description: "V5: UIKitSettings init, login, dependency setup" },
85
+ { name: "cometchat-android-v5-components", families: ["android"], description: "V5 component catalog — Conversations, Messages, Users, Groups, Calls" },
86
+ { name: "cometchat-android-v5-placement", families: ["android"], description: "V5: Activity, Fragment, BottomSheet — where to put chat" },
87
+ { name: "cometchat-android-v5-theming", families: ["android"], description: "V5: theme attrs, colors.xml, dark mode, brand styling" },
88
+ { name: "cometchat-android-v5-features", families: ["android"], description: "V5 feature catalog — calls, reactions, polls, extensions, AI" },
89
+ { name: "cometchat-android-v5-customization", families: ["android"], description: "V5: custom views, DataSource decorators, request builders" },
90
+ { name: "cometchat-android-v5-production", families: ["android"], description: "V5: server-minted auth tokens, user management, external-backend recipes" },
91
+ { name: "cometchat-android-v5-push", families: ["android"], description: "V5 push: FCM, dashboard providers, deep-link, foreground/background" },
92
+ { name: "cometchat-android-v5-extensions", families: ["android"], description: "V5 extensions — moderation, smart replies, message translation" },
93
+ { name: "cometchat-android-v5-testing", families: ["android"], description: "V5 testing: Espresso, Robolectric, mocking the kit/SDK" },
94
+ { name: "cometchat-android-v5-troubleshooting", families: ["android"], description: "V5 troubleshooting: gradle conflicts, manifest, lifecycle, ProGuard" },
95
+
96
+ { name: "cometchat-android-v6", families: ["android"], description: "V6 dispatcher — Compose + Kotlin Views (chatuikit-{compose,kotlin}-android:6.x)" },
97
+ { name: "cometchat-android-v6-core", families: ["android"], description: "V6: gradle deps, init, login, message sending" },
98
+ { name: "cometchat-android-v6-events", families: ["android"], description: "V6: SDK event subscriptions and lifecycle" },
99
+ { name: "cometchat-android-v6-compose-components", families: ["android"], description: "V6 Compose component catalog — Composables for chat UI" },
100
+ { name: "cometchat-android-v6-compose-placement", families: ["android"], description: "V6 Compose: NavHost, modal, bottom sheet — where to put chat" },
101
+ { name: "cometchat-android-v6-compose-theming", families: ["android"], description: "V6 Compose: CometChatTheme, color schemes, typography" },
102
+ { name: "cometchat-android-v6-compose-customization", families: ["android"], description: "V6 Compose: custom slots, DataSource, message templates" },
103
+ { name: "cometchat-android-v6-kotlin-components", families: ["android"], description: "V6 Kotlin Views component catalog — custom View classes" },
104
+ { name: "cometchat-android-v6-kotlin-placement", families: ["android"], description: "V6 Kotlin Views: Activity, Fragment, BottomSheet placement" },
105
+ { name: "cometchat-android-v6-kotlin-theming", families: ["android"], description: "V6 Kotlin Views: style attributes, colors.xml, dark mode" },
106
+ { name: "cometchat-android-v6-kotlin-customization", families: ["android"], description: "V6 Kotlin Views: custom Views, DataSource, request builders" },
107
+ { name: "cometchat-android-v6-features", families: ["android"], description: "V6 feature catalog — calls, reactions, polls, AI agent, extensions" },
108
+ { name: "cometchat-android-v6-extensions", families: ["android"], description: "V6 extensions — moderation, smart replies, message translation" },
109
+ { name: "cometchat-android-v6-builder-settings", families: ["android"], description: "V6: UIKitSettingsBuilder options — presence, typing, receipts" },
110
+ { name: "cometchat-android-v6-production", families: ["android"], description: "V6: server-minted auth tokens, user management, external-backend recipes" },
111
+ { name: "cometchat-android-v6-push", families: ["android"], description: "V6 push: FCM, dashboard providers, deep-link" },
112
+ { name: "cometchat-android-v6-testing", families: ["android"], description: "V6 testing: Espresso, Robolectric, Compose UI tests, mocking" },
113
+ { name: "cometchat-android-v6-troubleshooting", families: ["android"], description: "V6 troubleshooting: gradle, Compose runtime, R8, BuildConfig" },
114
+
115
+ // Flutter — V5 stable (`cometchat_chat_uikit:^5.2`) + V6 beta
116
+ // (`cometchat_chat_uikit:^6.0.0-beta`) ship together. Routed by the
117
+ // unified `cometchat` dispatcher after detecting the pubspec.yaml dep.
118
+ { name: "cometchat-flutter-v5", families: ["flutter"], description: "V5 dispatcher — cometchat_chat_uikit ^5.2 + cometchat_calls_uikit ^5.0" },
119
+ { name: "cometchat-flutter-v5-core", families: ["flutter"], description: "V5: pubspec deps, UIKitSettings, init/login, app entry" },
120
+ { name: "cometchat-flutter-v5-conversations", families: ["flutter"], description: "V5: CometChatConversations widget — props, callbacks, request builder" },
121
+ { name: "cometchat-flutter-v5-messages", families: ["flutter"], description: "V5: CometChatMessages screen — message list, header, composer wiring" },
122
+ { name: "cometchat-flutter-v5-users-groups", families: ["flutter"], description: "V5: CometChatUsers / CometChatGroups / CometChatGroupMembers" },
123
+ { name: "cometchat-flutter-v5-calls", families: ["flutter"], description: "V5: voice/video calling — incoming/outgoing/ongoing screens, call buttons" },
124
+ { name: "cometchat-flutter-v5-theming", families: ["flutter"], description: "V5: CometChatTheme, color palette, typography, dark mode" },
125
+ { name: "cometchat-flutter-v5-customization", families: ["flutter"], description: "V5: custom message templates, options, text formatters, builders" },
126
+ { name: "cometchat-flutter-v5-events", families: ["flutter"], description: "V5: CometChatMessageEvents / GroupEvents / CallEvents subscription" },
127
+ { name: "cometchat-flutter-v5-production", families: ["flutter"], description: "V5: server-minted auth tokens, user management, external-backend recipes" },
128
+ { name: "cometchat-flutter-v5-push", families: ["flutter"], description: "V5: FCM/APNs push, token lifecycle, deep-link" },
129
+ { name: "cometchat-flutter-v5-troubleshooting", families: ["flutter"], description: "V5 troubleshooting: pubspec, GetX, Pod errors, runtime crashes" },
130
+
131
+ { name: "cometchat-flutter-v6", families: ["flutter"], description: "V6 dispatcher — cometchat_chat_uikit ^6.0.0-beta (Bloc-based)" },
132
+ { name: "cometchat-flutter-v6-core", families: ["flutter"], description: "V6: pubspec deps, UIKitSettings, init/login, app entry" },
133
+ { name: "cometchat-flutter-v6-components", families: ["flutter"], description: "V6 component catalog — Bloc-driven widgets, slots, request builders" },
134
+ { name: "cometchat-flutter-v6-conversations", families: ["flutter"], description: "V6: CometChatConversations widget — Bloc, request builder, callbacks" },
135
+ { name: "cometchat-flutter-v6-messages", families: ["flutter"], description: "V6: CometChatMessages — list / header / composer composition" },
136
+ { name: "cometchat-flutter-v6-users-groups", families: ["flutter"], description: "V6: CometChatUsers / CometChatGroups / CometChatGroupMembers" },
137
+ { name: "cometchat-flutter-v6-calls", families: ["flutter"], description: "V6: voice/video calling — incoming/outgoing/ongoing screens" },
138
+ { name: "cometchat-flutter-v6-features", families: ["flutter"], description: "V6 feature catalog — calls, polls, reactions, AI, extensions" },
139
+ { name: "cometchat-flutter-v6-placement", families: ["flutter"], description: "V6: route, modal sheet, embedded widget — where chat lives in the app" },
140
+ { name: "cometchat-flutter-v6-theming", families: ["flutter"], description: "V6: CometChatThemeHelper, CometChatColorPalette, light/dark schemes" },
141
+ { name: "cometchat-flutter-v6-customization", families: ["flutter"], description: "V6: bubble factories, message templates, text formatters, slot widgets" },
142
+ { name: "cometchat-flutter-v6-events", families: ["flutter"], description: "V6: Bloc-based event streams + listener registration" },
143
+ { name: "cometchat-flutter-v6-production", families: ["flutter"], description: "V6: server-minted auth tokens, user management, external-backend recipes" },
144
+ { name: "cometchat-flutter-v6-troubleshooting", families: ["flutter"], description: "V6 troubleshooting: pubspec, Bloc, theme cache, build errors" },
145
+ { name: "cometchat-flutter-v6-migration", families: ["flutter"], description: "V5→V6 migration recipes — GetX → Bloc, theme API rewrite, breaking changes" },
146
+
147
+ // iOS — V5 stable (`CometChatUIKitSwift:^5`). No V6 beta yet; when
148
+ // it ships, add per-cohort namespacing as Android/Flutter do today.
149
+ { name: "cometchat-ios", families: ["ios"], description: "iOS dispatcher — Swift project setup, dependency manager routing" },
150
+ { name: "cometchat-ios-core", families: ["ios"], description: "iOS: SDK init, login, UIKit settings builder, SwiftUI/UIKit entry points" },
151
+ { name: "cometchat-ios-components", families: ["ios"], description: "iOS component catalog — view controllers, SwiftUI views, request builders" },
152
+ { name: "cometchat-ios-placement", families: ["ios"], description: "iOS: navigation controller, modal, tab, embedded — where chat lives" },
153
+ { name: "cometchat-ios-theming", families: ["ios"], description: "iOS: CometChatTheme, color tokens, typography, dark mode" },
154
+ { name: "cometchat-ios-features", families: ["ios"], description: "iOS feature catalog — calls, polls, reactions, AI, extensions" },
155
+ { name: "cometchat-ios-customization", families: ["ios"], description: "iOS: custom views, message templates, text formatters, DataSource decorators" },
156
+ { name: "cometchat-ios-production", families: ["ios"], description: "iOS: server-minted auth tokens, user management, external-backend recipes" },
157
+ { name: "cometchat-ios-push", families: ["ios"], description: "iOS push: APNs + VoIP, CallKit, token lifecycle, deep-link" },
158
+ { name: "cometchat-ios-troubleshooting", families: ["ios"], description: "iOS troubleshooting: SPM, CocoaPods, Xcode build errors, Info.plist, runtime crashes" },
47
159
  ];
48
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
+
181
+ // ── Framework → family routing ────────────────────────────────────────────────
182
+ const FRAMEWORK_TO_FAMILY = {
183
+ reactjs: "web",
184
+ nextjs: "web",
185
+ "react-router": "web",
186
+ astro: "web",
187
+ expo: "native",
188
+ "react-native": "native",
189
+ flutter: "flutter",
190
+ angular: "angular",
191
+ android: "android",
192
+ ios: "ios",
193
+ };
194
+
195
+ const FAMILY_LABELS = {
196
+ web: "Web (React / Next.js / React Router / Astro)",
197
+ native: "React Native (Expo / bare RN)",
198
+ angular: "Angular (12-15)",
199
+ flutter: "Flutter (V5 stable + V6 beta)",
200
+ android: "Android native (V5 stable + V6 beta)",
201
+ ios: "iOS native (V5 stable)",
202
+ };
203
+
49
204
  // ── Helpers ───────────────────────────────────────────────────────────────────
50
205
  function ensureDir(dir) {
51
206
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
@@ -53,16 +208,166 @@ function ensureDir(dir) {
53
208
 
54
209
  // ── IDE target directories ────────────────────────────────────────────────────
55
210
  const IDE_TARGETS = {
56
- claude: { local: ".claude/skills", global: path.join(os.homedir(), ".claude", "skills"), skillFile: "SKILL.md" },
211
+ claude: { local: ".claude/skills", global: path.join(os.homedir(), ".claude", "skills"), skillFile: "SKILL.md" },
57
212
  // Cursor loads agent "skills" from `.cursor/skills/` (not `.cursor/rules/`).
58
- cursor: { local: ".cursor/skills", global: path.join(os.homedir(), ".cursor", "skills"), skillFile: "SKILL.md" },
59
- kiro: { local: ".kiro/skills", global: path.join(os.homedir(), ".kiro", "skills"), skillFile: "SKILL.md" },
60
- copilot: { local: ".github", global: null, skillFile: null }, // single-file mode
213
+ cursor: { local: ".cursor/skills", global: path.join(os.homedir(), ".cursor", "skills"), skillFile: "SKILL.md" },
214
+ kiro: { local: ".kiro/skills", global: path.join(os.homedir(), ".kiro", "skills"), skillFile: "SKILL.md" },
215
+ copilot: { local: ".github", global: null, skillFile: null }, // single-file mode
216
+ // Replit Agent reads `.agents/skills/` per docs.replit.com/core-concepts/agent/skills.
217
+ // Global path matches the convention published by vercel-labs/skills.
218
+ replit: { local: ".agents/skills", global: path.join(os.homedir(), ".config", "agents", "skills"), skillFile: "SKILL.md" },
61
219
  };
62
220
 
63
- // Each skill is installed as <base>/<name>/SKILL.md (or concatenated for copilot)
64
- // Returns the destination path on success, or null if the source SKILL.md
65
- // doesn't exist.
221
+ // ── Framework detection (inline no subprocess) ─────────────────────────────
222
+ //
223
+ // Reads package.json + signal files in cwd to determine which family this
224
+ // project belongs to. Returns the family string ("web", "native", "flutter",
225
+ // "angular", "android", "ios") or null if undetectable.
226
+ //
227
+ // We inline this rather than subprocess to @cometchat/skills-cli because:
228
+ // 1. Cold `npx -y` adds 5-15 s on first run — bad first impression
229
+ // 2. Avoids attack surface of auto-installing a separate package
230
+ // 3. The unified installer only needs family resolution, not the CLI's
231
+ // richer detection (router/bundler/env-prefix/SSR strategy etc.)
232
+ //
233
+ // Detection order matters — more specific first:
234
+ // 1. RN (expo / react-native dep) — must come before generic React
235
+ // 2. Next.js (next dep)
236
+ // 3. React Router (any of the v6/v7 deps)
237
+ // 4. Astro (deps OR astro.config.*)
238
+ // 5. Angular (@angular/core dep OR angular.json)
239
+ // 6. Generic React (react / react-dom dep) → web
240
+ // 7. Flutter (pubspec.yaml with `flutter:` key)
241
+ // 8. Android (build.gradle / settings.gradle)
242
+ // 9. iOS (*.xcodeproj, *.xcworkspace, Package.swift with .iOS, Podfile with platform :ios)
243
+ function detectFamily(cwd) {
244
+ const pkgPath = path.join(cwd, "package.json");
245
+ if (fs.existsSync(pkgPath)) {
246
+ try {
247
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
248
+ const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
249
+ if (deps["expo"] || deps["react-native"]) return "native";
250
+ if (deps["next"]) return "web";
251
+ if (deps["@react-router/dev"] || deps["react-router-dom"] || deps["react-router"]) return "web";
252
+ if (deps["astro"]) return "web";
253
+ if (deps["@angular/core"]) return "angular";
254
+ if (deps["react"] || deps["react-dom"]) return "web";
255
+ } catch { /* malformed package.json — fall through */ }
256
+ }
257
+
258
+ // Astro config exists even before deps install
259
+ for (const ext of ["ts", "mjs", "js", "cjs"]) {
260
+ if (fs.existsSync(path.join(cwd, `astro.config.${ext}`))) return "web";
261
+ }
262
+
263
+ // Flutter — pubspec.yaml with `flutter:` key
264
+ const pubspecPath = path.join(cwd, "pubspec.yaml");
265
+ if (fs.existsSync(pubspecPath)) {
266
+ try {
267
+ const pubspec = fs.readFileSync(pubspecPath, "utf-8");
268
+ if (/^flutter:/m.test(pubspec)) return "flutter";
269
+ } catch { /* fall through */ }
270
+ }
271
+
272
+ // Angular workspace
273
+ if (fs.existsSync(path.join(cwd, "angular.json"))) return "angular";
274
+
275
+ // Android (Gradle-based)
276
+ if (
277
+ fs.existsSync(path.join(cwd, "build.gradle")) ||
278
+ fs.existsSync(path.join(cwd, "build.gradle.kts")) ||
279
+ fs.existsSync(path.join(cwd, "settings.gradle"))
280
+ ) {
281
+ return "android";
282
+ }
283
+
284
+ // iOS — Xcode project / workspace, or Package.swift with .iOS, or Podfile with `platform :ios`
285
+ try {
286
+ for (const f of fs.readdirSync(cwd)) {
287
+ if (f.endsWith(".xcodeproj") || f.endsWith(".xcworkspace")) return "ios";
288
+ }
289
+ } catch { /* unreadable cwd — fall through */ }
290
+ const podfilePath = path.join(cwd, "Podfile");
291
+ if (fs.existsSync(podfilePath)) {
292
+ try {
293
+ if (/platform\s+:ios/.test(fs.readFileSync(podfilePath, "utf-8"))) return "ios";
294
+ } catch { /* fall through */ }
295
+ }
296
+ const packageSwiftPath = path.join(cwd, "Package.swift");
297
+ if (fs.existsSync(packageSwiftPath)) {
298
+ try {
299
+ if (/\.iOS\s*\(/.test(fs.readFileSync(packageSwiftPath, "utf-8"))) return "ios";
300
+ } catch { /* fall through */ }
301
+ }
302
+
303
+ return null;
304
+ }
305
+
306
+ function promptFamily() {
307
+ // Refuse to prompt in non-interactive environments — readline would block
308
+ // forever waiting for stdin that never comes (CI, Dockerfile, scripted
309
+ // installs). Tell the user how to recover.
310
+ if (!process.stdin.isTTY) {
311
+ console.error(c.red(`\n Cannot prompt for family in a non-interactive environment.`));
312
+ console.error(` Pass ${c.cyan("--family <name>")} explicitly. Valid:`);
313
+ console.error(` ${Object.keys(FAMILY_LABELS).join(", ")}, all\n`);
314
+ process.exit(1);
315
+ }
316
+
317
+ const families = ["web", "native", "flutter", "angular", "android", "ios"];
318
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
319
+ console.log(`\n ${c.bold("Which framework does your project use?")}\n`);
320
+ families.forEach((f, i) => {
321
+ console.log(` ${c.cyan(String(i + 1))}. ${FAMILY_LABELS[f]}`);
322
+ });
323
+ return new Promise((resolve) => {
324
+ rl.question(`\n Enter 1-${families.length}: `, (answer) => {
325
+ rl.close();
326
+ const idx = parseInt(answer.trim(), 10) - 1;
327
+ if (Number.isNaN(idx) || idx < 0 || idx >= families.length) {
328
+ console.error(c.red(`\n Invalid choice: ${answer}\n`));
329
+ process.exit(1);
330
+ }
331
+ resolve(families[idx]);
332
+ });
333
+ });
334
+ }
335
+
336
+ // Returns an array of families. Most calls return a single-item array;
337
+ // repeated `--family` flags (`--family web --family native`) return multiple,
338
+ // useful for monorepos with both web + RN packages. `--family all` is a single
339
+ // item that means "every family" downstream.
340
+ async function resolveFamilies(args) {
341
+ // 1. Explicit --family flag(s) — repeatable
342
+ const explicit = [];
343
+ for (let i = 0; i < args.length; i++) {
344
+ if (args[i] === "--family" && args[i + 1]) {
345
+ const f = args[i + 1];
346
+ if (f === "all") { explicit.length = 0; explicit.push("all"); break; }
347
+ if (!Object.keys(FAMILY_LABELS).includes(f)) {
348
+ console.error(c.red(`\n Unknown family: ${f}. Valid: ${Object.keys(FAMILY_LABELS).join(", ")}, all\n`));
349
+ process.exit(1);
350
+ }
351
+ if (!explicit.includes(f)) explicit.push(f);
352
+ i++; // skip the value
353
+ }
354
+ }
355
+ if (explicit.length > 0) return explicit;
356
+
357
+ // 2. Auto-detect (inline, no subprocess)
358
+ process.stdout.write(c.dim("\n Detecting framework... "));
359
+ const family = detectFamily(process.cwd());
360
+ if (family) {
361
+ console.log(c.green(`✓ ${family} — ${FAMILY_LABELS[family]}`));
362
+ return [family];
363
+ }
364
+ console.log(c.dim("could not detect from project files"));
365
+
366
+ // 3. Prompt the user (with TTY guard)
367
+ return [await promptFamily()];
368
+ }
369
+
370
+ // ── Install one skill (per-IDE write) ─────────────────────────────────────────
66
371
  function installSkill(skill, baseDir, skillFile) {
67
372
  const src = path.join(SKILLS_SRC_DIR, skill.name, "SKILL.md");
68
373
  if (!fs.existsSync(src)) {
@@ -87,35 +392,108 @@ function installSkill(skill, baseDir, skillFile) {
87
392
  return dest;
88
393
  }
89
394
 
90
- function installCopilotSkills(baseDir) {
395
+ function installCopilotSkills(skillsToInstall, baseDir) {
91
396
  ensureDir(baseDir);
92
397
  const dest = path.join(baseDir, "copilot-instructions.md");
93
398
  let content = "# CometChat Skills\n\n";
94
- for (const skill of SKILLS) {
399
+ for (const skill of skillsToInstall) {
95
400
  const src = path.join(SKILLS_SRC_DIR, skill.name, "SKILL.md");
96
- if (!fs.existsSync(src)) {
97
- continue; // skip skills that haven't been promoted yet
98
- }
401
+ if (!fs.existsSync(src)) continue;
99
402
  content += fs.readFileSync(src, "utf8") + "\n\n---\n\n";
100
403
  }
101
404
  fs.writeFileSync(dest, content, "utf8");
102
405
  return dest;
103
406
  }
104
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
+
105
456
  function printHelp() {
106
457
  console.log(`
107
- ${c.bold("cometchat-skills")} — Install CometChat AI coding skills
458
+ ${c.bold("@cometchat/skills")} — Install CometChat AI coding skills
108
459
 
109
460
  ${c.bold("Usage:")}
110
- ${c.cyan("npx @cometchat/skills add")} Install for Claude Code (default)
111
- ${c.cyan("npx @cometchat/skills add --ide cursor")} Install for Cursor
112
- ${c.cyan("npx @cometchat/skills add --ide kiro")} Install for Kiro
113
- ${c.cyan("npx @cometchat/skills add --ide copilot")} Install for VS Code Copilot
114
- ${c.cyan("npx @cometchat/skills add --ide all")} Install for all supported IDEs
115
- ${c.cyan("npx @cometchat/skills add --global")} Install globally
116
- ${c.cyan("npx @cometchat/skills add --list")} Show available skills
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.
476
+
477
+ ${c.bold("Family values:")}
478
+ ${c.cyan("web")} React / Next.js / React Router / Astro
479
+ ${c.cyan("native")} Expo / bare React Native
480
+ ${c.cyan("angular")} Angular (12-15)
481
+ ${c.cyan("android")} Android native (V5 stable + V6 beta)
482
+ ${c.cyan("flutter")} Flutter (V5 stable + V6 beta)
483
+ ${c.cyan("ios")} iOS native (V5 stable)
484
+ ${c.cyan("all")} Install every skill (legacy v3 behavior)
485
+
486
+ ${c.bold("IDE selection (direct-write mode only — default: claude):")}
487
+ ${c.cyan("--ide cursor")} ${c.cyan("--ide kiro")} ${c.cyan("--ide copilot")} ${c.cyan("--ide replit")} ${c.cyan("--ide all")}
117
488
 
118
- ${c.bold("Supported IDEs:")} claude, cursor, kiro, copilot, all
489
+ ${c.bold("Multi-family / monorepo:")}
490
+ ${c.cyan("--family web --family native")} Install BOTH families (repeat flag)
491
+
492
+ ${c.bold("Other:")}
493
+ ${c.cyan("--global")} Install globally (~/.claude/skills/, etc.)
494
+ ${c.cyan("--clean")} Wipe existing cometchat-* skill dirs before install (direct-write mode only)
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)
119
497
 
120
498
  ${c.bold("After installing, open your project in your IDE and run:")}
121
499
  ${c.cyan("/cometchat")}
@@ -140,18 +518,103 @@ async function main() {
140
518
 
141
519
  const isGlobal = args.includes("--global") || args.includes("-g");
142
520
  const listOnly = args.includes("--list") || args.includes("-l");
521
+ const isClean = args.includes("--clean");
143
522
  const ideIdx = args.indexOf("--ide");
144
523
  const ideArg = ideIdx !== -1 ? args[ideIdx + 1] : "claude";
145
524
 
146
525
  if (listOnly) {
147
526
  console.log(`\n ${c.bold("Available skills:")}\n`);
148
527
  for (const s of SKILLS) {
149
- console.log(` ${c.cyan(s.name)}`);
528
+ console.log(` ${c.cyan(s.name)} ${c.dim(`[${s.families.join(", ")}]`)}`);
150
529
  console.log(` ${c.dim(s.description)}\n`);
151
530
  }
152
531
  process.exit(0);
153
532
  }
154
533
 
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.
568
+ let skillsToInstall;
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
+ }
583
+ }
584
+ }
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`);
607
+ }
608
+
609
+ if (!skipPicker) {
610
+ const exitCode = await delegateToSkillsCli({
611
+ skills: skillsToInstall,
612
+ families,
613
+ isGlobal,
614
+ });
615
+ process.exit(exitCode);
616
+ }
617
+
155
618
  const targets = ideArg === "all" ? Object.keys(IDE_TARGETS) : [ideArg];
156
619
 
157
620
  for (const ide of targets) {
@@ -171,23 +634,40 @@ async function main() {
171
634
  ? `global ${c.gray(`(${target.global}/`)}`
172
635
  : `local ${c.gray(`(${target.local}/)`)}`;
173
636
 
174
- console.log(`\n ${c.bold(c.cyan("CometChat Skills"))} — ${ide} integration\n`);
637
+ const familyLabel = families.includes("all") ? "all" : families.join("+");
638
+ console.log(`\n ${c.bold(c.cyan("CometChat Skills"))} — ${ide} — ${c.bold(familyLabel)} family\n`);
175
639
  console.log(` Scope: ${scopeLabel}\n`);
176
640
 
177
641
  ensureDir(baseDir);
178
642
 
643
+ // --clean: wipe existing cometchat-* skill dirs in baseDir before install.
644
+ // Avoids stale skills accumulating across multiple --family runs in the
645
+ // same project (e.g. user runs --family web then later switches to
646
+ // --family native and wants only the new family present). Only touches
647
+ // dirs prefixed `cometchat` so we never blow away unrelated skills.
648
+ if (isClean && ide !== "copilot" && fs.existsSync(baseDir)) {
649
+ let wiped = 0;
650
+ for (const entry of fs.readdirSync(baseDir)) {
651
+ if (entry === "cometchat" || entry.startsWith("cometchat-")) {
652
+ fs.rmSync(path.join(baseDir, entry), { recursive: true, force: true });
653
+ wiped++;
654
+ }
655
+ }
656
+ if (wiped > 0) console.log(c.dim(` ✓ cleaned ${wiped} existing cometchat-* skill ${wiped === 1 ? "dir" : "dirs"}\n`));
657
+ }
658
+
179
659
  if (ide === "copilot") {
180
660
  const spinner = ora ? ora({ text: "copilot-instructions.md", prefixText: " " }).start() : null;
181
661
  try {
182
- const dest = installCopilotSkills(baseDir);
183
- if (spinner) spinner.succeed(c.green("✓ ") + c.bold("copilot-instructions.md") + ` ${c.dim("All skills concatenated")}`);
184
- else console.log(` ✓ copilot-instructions.md`);
662
+ installCopilotSkills(skillsToInstall, baseDir);
663
+ if (spinner) spinner.succeed(c.green("✓ ") + c.bold("copilot-instructions.md") + ` ${c.dim(`(${skillsToInstall.length} skills concatenated)`)}`);
664
+ else console.log(` ✓ copilot-instructions.md (${skillsToInstall.length} skills)`);
185
665
  } catch (err) {
186
666
  if (spinner) spinner.fail(c.red(`✗ copilot-instructions.md: ${err.message}`));
187
667
  else console.error(` ✗ copilot-instructions.md: ${err.message}`);
188
668
  }
189
669
  } else {
190
- for (const skill of SKILLS) {
670
+ for (const skill of skillsToInstall) {
191
671
  const spinner = ora ? ora({ text: skill.name, prefixText: " " }).start() : null;
192
672
  try {
193
673
  installSkill(skill, baseDir, target.skillFile);