@cometchat/skills 4.4.2 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (490) hide show
  1. package/.claude-plugin/marketplace.json +55 -0
  2. package/.claude-plugin/plugin.json +14 -0
  3. package/CHANGELOG.md +36 -0
  4. package/LICENSE +21 -0
  5. package/README.md +41 -163
  6. package/RULES.md +114 -0
  7. package/bin/cometchat-skills.mjs +1180 -0
  8. package/bin/legacy-skills.json +95 -0
  9. package/catalogs/android-v6.json +1646 -0
  10. package/catalogs/flutter-v6.json +651 -0
  11. package/catalogs/ios-v5.json +1383 -0
  12. package/catalogs/rn-calls-v5.json +134 -0
  13. package/catalogs/rn-calls-v5.symbols.txt +58 -0
  14. package/catalogs/rn-sdk-v4.json +672 -0
  15. package/catalogs/rn-v5.json +449 -0
  16. package/catalogs/rn-v5.symbols.txt +216 -0
  17. package/catalogs/sdk-android-v5.json +514 -0
  18. package/contracts.android-calls-v5.json +122 -0
  19. package/contracts.android-v6.json +135 -0
  20. package/contracts.angular-v5.json +694 -0
  21. package/contracts.flutter-calls-v5.json +77 -0
  22. package/contracts.flutter-v6.json +126 -0
  23. package/contracts.ios-calls-v5.json +67 -0
  24. package/contracts.ios-v5.json +436 -0
  25. package/contracts.json +82 -0
  26. package/contracts.rn-calls-v5.json +77 -0
  27. package/contracts.rn-v5.json +727 -0
  28. package/contracts.sdk-android-v5.json +15 -0
  29. package/contracts.web-calls-v5.json +45 -0
  30. package/features.android-calls-v5.json +246 -0
  31. package/features.android-v6.json +65 -0
  32. package/features.angular-v5.json +521 -0
  33. package/features.flutter-calls-v5.json +286 -0
  34. package/features.flutter-v6.json +484 -0
  35. package/features.ios-calls-v5.json +236 -0
  36. package/features.ios-v5.json +573 -0
  37. package/features.json +61 -0
  38. package/features.rn-calls-v5.json +187 -0
  39. package/features.rn-v5.json +614 -0
  40. package/features.sdk-android-v5.json +40 -0
  41. package/features.sdk-jsv4.json +24 -0
  42. package/features.web-calls-v5.json +108 -0
  43. package/package.json +151 -74
  44. package/peers.yaml +312 -0
  45. package/registry/README.md +31 -0
  46. package/registry/android-calls-v5.json +54 -0
  47. package/registry/android-v6.json +137 -0
  48. package/registry/angular-v5.json +114 -0
  49. package/registry/flutter-calls-v5.json +54 -0
  50. package/registry/flutter-v6.json +119 -0
  51. package/registry/ios-calls-v5.json +59 -0
  52. package/registry/ios-v5.json +97 -0
  53. package/registry/rn-calls-v5.json +59 -0
  54. package/registry/rn-v5.json +129 -0
  55. package/registry/sdk-android-v5.json +53 -0
  56. package/registry/sdk-jsv4.json +53 -0
  57. package/registry/web-calls-v5.json +54 -0
  58. package/registry/web-v7.json +99 -0
  59. package/skills/cometchat/SKILL.md +42 -2096
  60. package/skills/cometchat-android-v5-calls-sdk/SKILL.md +206 -0
  61. package/skills/cometchat-android-v5-calls-sdk/references/build-truth.md +63 -0
  62. package/skills/cometchat-android-v5-calls-sdk/references/docs-map.md +119 -0
  63. package/skills/cometchat-android-v5-calls-sdk/references/method-map.md +53 -0
  64. package/skills/cometchat-android-v5-calls-sdk/references/pitfalls.md +72 -0
  65. package/skills/cometchat-android-v5-calls-sdk/references/ringing.md +92 -0
  66. package/skills/cometchat-android-v5-calls-sdk/references/user-switch.md +90 -0
  67. package/skills/cometchat-android-v5-sdk/SKILL.md +127 -0
  68. package/skills/cometchat-android-v5-sdk/references/app-surface.md +88 -0
  69. package/skills/cometchat-android-v5-sdk/references/docs-map.md +91 -0
  70. package/skills/cometchat-android-v6-builder-settings/SKILL.md +51 -137
  71. package/skills/cometchat-android-v6-calls/SKILL.md +128 -413
  72. package/skills/cometchat-android-v6-compose-components/SKILL.md +84 -194
  73. package/skills/cometchat-android-v6-compose-customization/SKILL.md +66 -411
  74. package/skills/cometchat-android-v6-compose-placement/SKILL.md +154 -237
  75. package/skills/cometchat-android-v6-core/SKILL.md +236 -672
  76. package/skills/cometchat-android-v6-core/references/anti-patterns.md +31 -0
  77. package/skills/cometchat-android-v6-core/references/component-props.md +168 -0
  78. package/skills/cometchat-android-v6-core/references/docs-map.md +153 -0
  79. package/skills/cometchat-android-v6-core/references/layout.md +112 -0
  80. package/skills/cometchat-android-v6-core/references/lifecycle.md +75 -0
  81. package/skills/cometchat-android-v6-core/references/setup-credentials.md +105 -0
  82. package/skills/cometchat-android-v6-core/references/troubleshooting.md +36 -0
  83. package/skills/cometchat-android-v6-events/SKILL.md +46 -184
  84. package/skills/cometchat-android-v6-extensions/SKILL.md +39 -179
  85. package/skills/cometchat-android-v6-features/SKILL.md +74 -143
  86. package/skills/cometchat-android-v6-kotlin-components/SKILL.md +75 -193
  87. package/skills/cometchat-android-v6-kotlin-customization/SKILL.md +67 -419
  88. package/skills/cometchat-android-v6-kotlin-placement/SKILL.md +77 -223
  89. package/skills/cometchat-android-v6-migration/SKILL.md +67 -378
  90. package/skills/cometchat-android-v6-production/SKILL.md +40 -179
  91. package/skills/cometchat-android-v6-push/SKILL.md +39 -289
  92. package/skills/cometchat-android-v6-testing/SKILL.md +50 -265
  93. package/skills/cometchat-android-v6-troubleshooting/SKILL.md +70 -254
  94. package/skills/cometchat-angular-v5-calls/SKILL.md +193 -0
  95. package/skills/cometchat-angular-v5-components/SKILL.md +131 -0
  96. package/skills/cometchat-angular-v5-components/references/catalog.md +111 -0
  97. package/skills/cometchat-angular-v5-components/references/host-composed.md +46 -0
  98. package/skills/cometchat-angular-v5-core/SKILL.md +300 -0
  99. package/skills/cometchat-angular-v5-core/references/anti-patterns.md +49 -0
  100. package/skills/cometchat-angular-v5-core/references/component-props.md +55 -0
  101. package/skills/cometchat-angular-v5-core/references/dependencies.md +66 -0
  102. package/skills/cometchat-angular-v5-core/references/docs-map.md +132 -0
  103. package/skills/cometchat-angular-v5-core/references/layout.md +64 -0
  104. package/skills/cometchat-angular-v5-core/references/lifecycle.md +202 -0
  105. package/skills/cometchat-angular-v5-core/references/setup-credentials.md +137 -0
  106. package/skills/cometchat-angular-v5-core/references/theming.md +70 -0
  107. package/skills/cometchat-angular-v5-core/references/troubleshooting.md +27 -0
  108. package/skills/cometchat-angular-v5-customization/SKILL.md +94 -0
  109. package/skills/cometchat-angular-v5-features/SKILL.md +107 -0
  110. package/skills/cometchat-angular-v5-features/references/custom-messages.md +133 -0
  111. package/skills/cometchat-angular-v5-features/references/formatters.md +28 -0
  112. package/skills/cometchat-angular-v5-features/references/host-composed-extensions.md +85 -0
  113. package/skills/cometchat-angular-v5-migration/SKILL.md +40 -0
  114. package/skills/cometchat-angular-v5-migration/references/migration-guide.md +165 -0
  115. package/skills/cometchat-angular-v5-patterns/SKILL.md +107 -0
  116. package/skills/cometchat-angular-v5-placement/SKILL.md +133 -0
  117. package/skills/cometchat-angular-v5-placement/references/combined-app.md +174 -0
  118. package/skills/cometchat-angular-v5-placement/references/core-surface.md +199 -0
  119. package/skills/cometchat-angular-v5-production/SKILL.md +101 -0
  120. package/skills/cometchat-angular-v5-push/SKILL.md +61 -0
  121. package/skills/cometchat-angular-v5-testing/SKILL.md +112 -0
  122. package/skills/cometchat-angular-v5-troubleshooting/SKILL.md +96 -0
  123. package/skills/cometchat-flutter-v5-sdk/SKILL.md +188 -0
  124. package/skills/cometchat-flutter-v5-sdk/references/doc-corrections.md +55 -0
  125. package/skills/cometchat-flutter-v5-sdk/references/docs-map.md +211 -0
  126. package/skills/cometchat-flutter-v5-sdk/references/platform-notes.md +48 -0
  127. package/skills/cometchat-flutter-v5-sdk/references/transcription.md +87 -0
  128. package/skills/cometchat-flutter-v6-calls/SKILL.md +75 -389
  129. package/skills/cometchat-flutter-v6-components/SKILL.md +88 -361
  130. package/skills/cometchat-flutter-v6-core/SKILL.md +154 -593
  131. package/skills/cometchat-flutter-v6-core/references/anti-patterns.md +55 -0
  132. package/skills/cometchat-flutter-v6-core/references/docs-map.md +143 -0
  133. package/skills/cometchat-flutter-v6-core/references/layout.md +45 -0
  134. package/skills/cometchat-flutter-v6-core/references/lifecycle.md +87 -0
  135. package/skills/cometchat-flutter-v6-core/references/setup-credentials.md +84 -0
  136. package/skills/cometchat-flutter-v6-core/references/troubleshooting.md +26 -0
  137. package/skills/cometchat-flutter-v6-customization/SKILL.md +70 -712
  138. package/skills/cometchat-flutter-v6-events/SKILL.md +79 -174
  139. package/skills/cometchat-flutter-v6-features/SKILL.md +55 -394
  140. package/skills/cometchat-flutter-v6-migration/SKILL.md +68 -367
  141. package/skills/cometchat-flutter-v6-migration/references/migration-guide.md +131 -0
  142. package/skills/cometchat-flutter-v6-patterns/SKILL.md +61 -0
  143. package/skills/cometchat-flutter-v6-placement/SKILL.md +94 -381
  144. package/skills/cometchat-flutter-v6-production/SKILL.md +58 -615
  145. package/skills/cometchat-flutter-v6-push/SKILL.md +46 -455
  146. package/skills/cometchat-flutter-v6-testing/SKILL.md +68 -299
  147. package/skills/cometchat-flutter-v6-troubleshooting/SKILL.md +54 -878
  148. package/skills/cometchat-ios-calls/SKILL.md +112 -563
  149. package/skills/cometchat-ios-components/SKILL.md +44 -834
  150. package/skills/cometchat-ios-core/SKILL.md +181 -1068
  151. package/skills/cometchat-ios-core/references/anti-patterns.md +80 -0
  152. package/skills/cometchat-ios-core/references/docs-map.md +123 -0
  153. package/skills/cometchat-ios-core/references/install.md +23 -0
  154. package/skills/cometchat-ios-core/references/layout.md +186 -0
  155. package/skills/cometchat-ios-core/references/setup-credentials.md +197 -0
  156. package/skills/cometchat-ios-core/references/swiftui.md +91 -0
  157. package/skills/cometchat-ios-core/references/troubleshooting.md +100 -0
  158. package/skills/cometchat-ios-customization/SKILL.md +33 -968
  159. package/skills/cometchat-ios-features/SKILL.md +29 -738
  160. package/skills/cometchat-ios-migration/SKILL.md +39 -0
  161. package/skills/cometchat-ios-placement/SKILL.md +51 -729
  162. package/skills/cometchat-ios-push/SKILL.md +24 -602
  163. package/skills/cometchat-ios-v5-sdk/SKILL.md +161 -0
  164. package/skills/cometchat-ios-v5-sdk/references/docs-map.md +326 -0
  165. package/skills/cometchat-js-v5-sdk/SKILL.md +95 -0
  166. package/skills/cometchat-js-v5-sdk/references/docs-map.md +114 -0
  167. package/skills/cometchat-onboarding/SKILL.md +89 -0
  168. package/skills/cometchat-onboarding/references/archetypes.md +51 -0
  169. package/skills/cometchat-onboarding/references/calling-first.md +77 -0
  170. package/skills/cometchat-onboarding/references/not-in-this-pack.md +8 -0
  171. package/skills/cometchat-onboarding/references/plan-artifact.md +133 -0
  172. package/skills/cometchat-onboarding/references/platforms.md +90 -0
  173. package/skills/cometchat-react-native-bare-patterns/SKILL.md +120 -0
  174. package/skills/cometchat-react-native-calls/SKILL.md +200 -0
  175. package/skills/cometchat-react-native-components/SKILL.md +142 -0
  176. package/skills/cometchat-react-native-core/SKILL.md +313 -0
  177. package/skills/cometchat-react-native-core/references/anti-patterns.md +79 -0
  178. package/skills/cometchat-react-native-core/references/component-props.md +105 -0
  179. package/skills/cometchat-react-native-core/references/dependencies.md +62 -0
  180. package/skills/cometchat-react-native-core/references/docs-map.md +126 -0
  181. package/skills/cometchat-react-native-core/references/layout.md +61 -0
  182. package/skills/cometchat-react-native-core/references/lifecycle.md +64 -0
  183. package/skills/cometchat-react-native-core/references/setup-credentials.md +39 -0
  184. package/skills/cometchat-react-native-customization/SKILL.md +198 -0
  185. package/skills/cometchat-react-native-expo-patterns/SKILL.md +116 -0
  186. package/skills/cometchat-react-native-features/SKILL.md +122 -0
  187. package/skills/cometchat-react-native-migration/SKILL.md +103 -0
  188. package/skills/cometchat-react-native-placement/SKILL.md +158 -0
  189. package/skills/cometchat-react-native-production/SKILL.md +78 -0
  190. package/skills/cometchat-react-native-push/SKILL.md +88 -0
  191. package/skills/cometchat-react-native-sdk/SKILL.md +126 -0
  192. package/skills/cometchat-react-native-sdk/references/method-map.md +239 -0
  193. package/skills/cometchat-react-native-sdk/references/page-map.md +96 -0
  194. package/skills/cometchat-react-native-testing/SKILL.md +94 -0
  195. package/skills/cometchat-react-native-troubleshooting/SKILL.md +116 -0
  196. package/skills/cometchat-react-native-v5-sdk/SKILL.md +150 -0
  197. package/skills/cometchat-react-native-v5-sdk/references/docs-map.md +127 -0
  198. package/skills/cometchat-react-native-v5-sdk/references/ringing-voip.md +369 -0
  199. package/skills/cometchat-react-v7-calls/SKILL.md +75 -0
  200. package/skills/cometchat-react-v7-components/SKILL.md +72 -0
  201. package/skills/cometchat-react-v7-core/SKILL.md +101 -0
  202. package/skills/cometchat-react-v7-core/references/anti-patterns.md +19 -0
  203. package/skills/cometchat-react-v7-core/references/component-props.md +82 -0
  204. package/skills/cometchat-react-v7-core/references/dependencies.md +23 -0
  205. package/skills/cometchat-react-v7-core/references/docs-map.md +117 -0
  206. package/skills/cometchat-react-v7-core/references/i18n-rtl-a11y.md +22 -0
  207. package/skills/cometchat-react-v7-core/references/layout.md +158 -0
  208. package/skills/cometchat-react-v7-core/references/lifecycle.md +74 -0
  209. package/skills/cometchat-react-v7-core/references/setup-credentials.md +58 -0
  210. package/skills/cometchat-react-v7-core/references/ssr.md +11 -0
  211. package/skills/cometchat-react-v7-core/references/theming.md +52 -0
  212. package/skills/cometchat-react-v7-core/references/troubleshooting.md +28 -0
  213. package/skills/cometchat-react-v7-customization/SKILL.md +84 -0
  214. package/skills/cometchat-react-v7-features/SKILL.md +92 -0
  215. package/skills/cometchat-react-v7-migration/SKILL.md +39 -0
  216. package/skills/cometchat-react-v7-migration/references/migration-guide.md +119 -0
  217. package/skills/cometchat-react-v7-patterns/SKILL.md +43 -0
  218. package/skills/cometchat-react-v7-placement/SKILL.md +106 -0
  219. package/skills/cometchat-react-v7-placement/references/recipes.md +354 -0
  220. package/skills/cometchat-react-v7-push/SKILL.md +53 -0
  221. package/test-suite/README.md +70 -0
  222. package/test-suite/catalogs/android-calls-v5.json +830 -0
  223. package/test-suite/catalogs/android-calls-v5.symbols.txt +499 -0
  224. package/test-suite/catalogs/android-v6.json +1646 -0
  225. package/test-suite/catalogs/android-v6.symbols.txt +298 -0
  226. package/test-suite/catalogs/angular-v5.json +7587 -0
  227. package/test-suite/catalogs/flutter-calls-v5.json +196 -0
  228. package/test-suite/catalogs/flutter-calls-v5.symbols.txt +59 -0
  229. package/test-suite/catalogs/flutter-v6.json +651 -0
  230. package/test-suite/catalogs/ios-calls-v5.json +158 -0
  231. package/test-suite/catalogs/ios-calls-v5.symbols.txt +70 -0
  232. package/test-suite/catalogs/ios-v5.json +1383 -0
  233. package/test-suite/catalogs/ios-v5.symbols.txt +699 -0
  234. package/test-suite/catalogs/rn-calls-v5.json +134 -0
  235. package/test-suite/catalogs/rn-sdk-v4.json +672 -0
  236. package/test-suite/catalogs/rn-v5.json +449 -0
  237. package/test-suite/catalogs/sdk-android-v5.json +514 -0
  238. package/test-suite/catalogs/sdk-android-v5.symbols.txt +254 -0
  239. package/test-suite/catalogs/sdk-jsv4.json +800 -0
  240. package/test-suite/catalogs/web-calls-v5.json +195 -0
  241. package/test-suite/catalogs/web-v7.json +1243 -0
  242. package/test-suite/scripts/build-registry.mjs +113 -0
  243. package/test-suite/scripts/catalog-refresh-dart.mjs +151 -0
  244. package/test-suite/scripts/catalog-refresh.mjs +100 -0
  245. package/test-suite/scripts/lint-skills.mjs +909 -0
  246. package/test-suite/scripts/measure-tokens.mjs +169 -0
  247. package/test-suite/scripts/typecheck-fences-dart.mjs +437 -0
  248. package/test-suite/scripts/typecheck-fences-kotlin.mjs +269 -0
  249. package/test-suite/scripts/typecheck-fences.mjs +206 -0
  250. package/test-suite/scripts/verify-catalog.mjs +242 -0
  251. package/test-suite/scripts/verify-contract-fences.mjs +94 -0
  252. package/test-suite/scripts/verify-detection.mjs +178 -0
  253. package/test-suite/scripts/verify-docs-base.mjs +91 -0
  254. package/test-suite/scripts/verify-family-agnostic.mjs +130 -0
  255. package/test-suite/scripts/verify-golden-path.mjs +138 -0
  256. package/test-suite/scripts/verify-oracle-components.mjs +133 -0
  257. package/test-suite/scripts/verify-template-bindings.mjs +148 -0
  258. package/test-suite/typecheck/angular-v5/package.json +21 -0
  259. package/test-suite/typecheck/angular-v5/tsconfig.json +21 -0
  260. package/test-suite/typecheck/flutter-v6/analysis_options.yaml +25 -0
  261. package/test-suite/typecheck/flutter-v6/pubspec.yaml +21 -0
  262. package/test-suite/typecheck/web-v7/package.json +18 -0
  263. package/test-suite/typecheck/web-v7/tsconfig.json +19 -0
  264. package/bin/install.js +0 -1003
  265. package/skills/.claude-plugin/marketplace.json +0 -86
  266. package/skills/cometchat/references/asking-questions.md +0 -48
  267. package/skills/cometchat-a11y/SKILL.md +0 -477
  268. package/skills/cometchat-android-v5/SKILL.md +0 -121
  269. package/skills/cometchat-android-v5-calls/SKILL.md +0 -429
  270. package/skills/cometchat-android-v5-calls/references/README.md +0 -53
  271. package/skills/cometchat-android-v5-calls/references/add-calls-to-existing-chat.md +0 -197
  272. package/skills/cometchat-android-v5-calls/references/audio-controls.md +0 -91
  273. package/skills/cometchat-android-v5-calls/references/background-handling.md +0 -168
  274. package/skills/cometchat-android-v5-calls/references/call-layouts.md +0 -131
  275. package/skills/cometchat-android-v5-calls/references/call-logs.md +0 -113
  276. package/skills/cometchat-android-v5-calls/references/call-session.md +0 -274
  277. package/skills/cometchat-android-v5-calls/references/custom-ui.md +0 -150
  278. package/skills/cometchat-android-v5-calls/references/event-listeners.md +0 -123
  279. package/skills/cometchat-android-v5-calls/references/group-calls.md +0 -205
  280. package/skills/cometchat-android-v5-calls/references/idle-timeout.md +0 -117
  281. package/skills/cometchat-android-v5-calls/references/in-call-chat.md +0 -134
  282. package/skills/cometchat-android-v5-calls/references/join-session.md +0 -120
  283. package/skills/cometchat-android-v5-calls/references/migration-v4-to-v5.md +0 -222
  284. package/skills/cometchat-android-v5-calls/references/participant-management.md +0 -110
  285. package/skills/cometchat-android-v5-calls/references/picture-in-picture.md +0 -106
  286. package/skills/cometchat-android-v5-calls/references/raise-hand.md +0 -248
  287. package/skills/cometchat-android-v5-calls/references/recording.md +0 -101
  288. package/skills/cometchat-android-v5-calls/references/ringing-integration.md +0 -152
  289. package/skills/cometchat-android-v5-calls/references/screen-sharing.md +0 -78
  290. package/skills/cometchat-android-v5-calls/references/server-fcm-voip.md +0 -215
  291. package/skills/cometchat-android-v5-calls/references/session-settings.md +0 -121
  292. package/skills/cometchat-android-v5-calls/references/setup.md +0 -137
  293. package/skills/cometchat-android-v5-calls/references/share-invite.md +0 -156
  294. package/skills/cometchat-android-v5-calls/references/video-controls.md +0 -87
  295. package/skills/cometchat-android-v5-calls/references/voip-calling.md +0 -526
  296. package/skills/cometchat-android-v5-components/SKILL.md +0 -431
  297. package/skills/cometchat-android-v5-core/SKILL.md +0 -486
  298. package/skills/cometchat-android-v5-customization/SKILL.md +0 -469
  299. package/skills/cometchat-android-v5-extensions/SKILL.md +0 -127
  300. package/skills/cometchat-android-v5-features/SKILL.md +0 -131
  301. package/skills/cometchat-android-v5-placement/SKILL.md +0 -277
  302. package/skills/cometchat-android-v5-production/SKILL.md +0 -95
  303. package/skills/cometchat-android-v5-push/SKILL.md +0 -619
  304. package/skills/cometchat-android-v5-testing/SKILL.md +0 -188
  305. package/skills/cometchat-android-v5-theming/SKILL.md +0 -167
  306. package/skills/cometchat-android-v5-troubleshooting/SKILL.md +0 -74
  307. package/skills/cometchat-android-v6/SKILL.md +0 -108
  308. package/skills/cometchat-android-v6-calls/references/add-calls-to-existing-chat.md +0 -97
  309. package/skills/cometchat-android-v6-calls/references/advanced-features.md +0 -87
  310. package/skills/cometchat-android-v6-calls/references/call-layouts.md +0 -100
  311. package/skills/cometchat-android-v6-calls/references/call-session.md +0 -163
  312. package/skills/cometchat-android-v6-calls/references/device-management.md +0 -172
  313. package/skills/cometchat-android-v6-calls/references/group-calls.md +0 -191
  314. package/skills/cometchat-android-v6-calls/references/idle-timeout.md +0 -110
  315. package/skills/cometchat-android-v6-calls/references/in-call-chat.md +0 -141
  316. package/skills/cometchat-android-v6-calls/references/raise-hand.md +0 -188
  317. package/skills/cometchat-android-v6-calls/references/ringing-integration.md +0 -176
  318. package/skills/cometchat-android-v6-calls/references/server-fcm-voip.md +0 -127
  319. package/skills/cometchat-android-v6-calls/references/share-invite.md +0 -105
  320. package/skills/cometchat-android-v6-compose-theming/SKILL.md +0 -250
  321. package/skills/cometchat-android-v6-kotlin-theming/SKILL.md +0 -233
  322. package/skills/cometchat-angular-calls/SKILL.md +0 -380
  323. package/skills/cometchat-angular-calls/references/add-calls-to-existing-chat.md +0 -96
  324. package/skills/cometchat-angular-calls/references/call-layouts.md +0 -123
  325. package/skills/cometchat-angular-calls/references/call-session.md +0 -167
  326. package/skills/cometchat-angular-calls/references/custom-ui.md +0 -239
  327. package/skills/cometchat-angular-calls/references/device-management.md +0 -154
  328. package/skills/cometchat-angular-calls/references/group-calls.md +0 -433
  329. package/skills/cometchat-angular-calls/references/idle-timeout.md +0 -161
  330. package/skills/cometchat-angular-calls/references/in-call-chat.md +0 -148
  331. package/skills/cometchat-angular-calls/references/lazy-loading-pitfalls.md +0 -205
  332. package/skills/cometchat-angular-calls/references/migration-v4-to-v5.md +0 -53
  333. package/skills/cometchat-angular-calls/references/ngzone-and-async-callbacks.md +0 -184
  334. package/skills/cometchat-angular-calls/references/picture-in-picture.md +0 -285
  335. package/skills/cometchat-angular-calls/references/raise-hand.md +0 -172
  336. package/skills/cometchat-angular-calls/references/recording-screen-share.md +0 -260
  337. package/skills/cometchat-angular-calls/references/ringing-integration.md +0 -133
  338. package/skills/cometchat-angular-calls/references/server-web-push-vapid.md +0 -100
  339. package/skills/cometchat-angular-calls/references/share-invite.md +0 -122
  340. package/skills/cometchat-angular-calls/references/virtual-background.md +0 -29
  341. package/skills/cometchat-angular-components/SKILL.md +0 -235
  342. package/skills/cometchat-angular-core/SKILL.md +0 -386
  343. package/skills/cometchat-angular-customization/SKILL.md +0 -616
  344. package/skills/cometchat-angular-features/SKILL.md +0 -329
  345. package/skills/cometchat-angular-patterns/SKILL.md +0 -256
  346. package/skills/cometchat-angular-placement/SKILL.md +0 -314
  347. package/skills/cometchat-angular-production/SKILL.md +0 -564
  348. package/skills/cometchat-angular-push/SKILL.md +0 -389
  349. package/skills/cometchat-angular-testing/SKILL.md +0 -605
  350. package/skills/cometchat-angular-theming/SKILL.md +0 -290
  351. package/skills/cometchat-angular-troubleshooting/SKILL.md +0 -364
  352. package/skills/cometchat-astro-patterns/SKILL.md +0 -708
  353. package/skills/cometchat-calls/SKILL.md +0 -364
  354. package/skills/cometchat-calls/references/use-case-broadcast.md +0 -199
  355. package/skills/cometchat-calls/references/use-case-marketplace.md +0 -157
  356. package/skills/cometchat-calls/references/use-case-support.md +0 -189
  357. package/skills/cometchat-calls/references/use-case-team.md +0 -180
  358. package/skills/cometchat-calls/references/use-case-telehealth.md +0 -153
  359. package/skills/cometchat-components/SKILL.md +0 -1142
  360. package/skills/cometchat-core/SKILL.md +0 -1048
  361. package/skills/cometchat-customization/SKILL.md +0 -580
  362. package/skills/cometchat-customization/references/component-catalog.md +0 -236
  363. package/skills/cometchat-features/SKILL.md +0 -861
  364. package/skills/cometchat-flutter-v5/SKILL.md +0 -173
  365. package/skills/cometchat-flutter-v5-calls/SKILL.md +0 -455
  366. package/skills/cometchat-flutter-v5-calls/references/add-calls-to-existing-chat.md +0 -193
  367. package/skills/cometchat-flutter-v5-calls/references/call-layouts.md +0 -103
  368. package/skills/cometchat-flutter-v5-calls/references/call-session.md +0 -285
  369. package/skills/cometchat-flutter-v5-calls/references/device-management.md +0 -124
  370. package/skills/cometchat-flutter-v5-calls/references/group-calls.md +0 -181
  371. package/skills/cometchat-flutter-v5-calls/references/idle-timeout.md +0 -99
  372. package/skills/cometchat-flutter-v5-calls/references/in-call-chat.md +0 -135
  373. package/skills/cometchat-flutter-v5-calls/references/migration-v4-to-v5.md +0 -210
  374. package/skills/cometchat-flutter-v5-calls/references/raise-hand.md +0 -255
  375. package/skills/cometchat-flutter-v5-calls/references/ringing-integration.md +0 -196
  376. package/skills/cometchat-flutter-v5-calls/references/server-push-bridge.md +0 -101
  377. package/skills/cometchat-flutter-v5-calls/references/share-invite.md +0 -148
  378. package/skills/cometchat-flutter-v5-conversations/SKILL.md +0 -118
  379. package/skills/cometchat-flutter-v5-core/SKILL.md +0 -239
  380. package/skills/cometchat-flutter-v5-customization/SKILL.md +0 -358
  381. package/skills/cometchat-flutter-v5-events/SKILL.md +0 -156
  382. package/skills/cometchat-flutter-v5-messages/SKILL.md +0 -259
  383. package/skills/cometchat-flutter-v5-production/SKILL.md +0 -216
  384. package/skills/cometchat-flutter-v5-push/SKILL.md +0 -285
  385. package/skills/cometchat-flutter-v5-testing/SKILL.md +0 -401
  386. package/skills/cometchat-flutter-v5-theming/SKILL.md +0 -161
  387. package/skills/cometchat-flutter-v5-troubleshooting/SKILL.md +0 -130
  388. package/skills/cometchat-flutter-v5-users-groups/SKILL.md +0 -152
  389. package/skills/cometchat-flutter-v6/SKILL.md +0 -446
  390. package/skills/cometchat-flutter-v6-calls/references/add-calls-to-existing-chat.md +0 -136
  391. package/skills/cometchat-flutter-v6-calls/references/advanced-features.md +0 -89
  392. package/skills/cometchat-flutter-v6-calls/references/call-layouts.md +0 -105
  393. package/skills/cometchat-flutter-v6-calls/references/call-session.md +0 -229
  394. package/skills/cometchat-flutter-v6-calls/references/device-management.md +0 -105
  395. package/skills/cometchat-flutter-v6-calls/references/group-calls.md +0 -190
  396. package/skills/cometchat-flutter-v6-calls/references/idle-timeout.md +0 -88
  397. package/skills/cometchat-flutter-v6-calls/references/in-call-chat.md +0 -125
  398. package/skills/cometchat-flutter-v6-calls/references/raise-hand.md +0 -275
  399. package/skills/cometchat-flutter-v6-calls/references/ringing-integration.md +0 -171
  400. package/skills/cometchat-flutter-v6-calls/references/server-push-bridge.md +0 -93
  401. package/skills/cometchat-flutter-v6-calls/references/share-invite.md +0 -102
  402. package/skills/cometchat-flutter-v6-conversations/SKILL.md +0 -238
  403. package/skills/cometchat-flutter-v6-messages/SKILL.md +0 -384
  404. package/skills/cometchat-flutter-v6-theming/SKILL.md +0 -293
  405. package/skills/cometchat-flutter-v6-users-groups/SKILL.md +0 -224
  406. package/skills/cometchat-i18n/SKILL.md +0 -419
  407. package/skills/cometchat-ios/SKILL.md +0 -325
  408. package/skills/cometchat-ios-calls/references/add-calls-to-existing-chat.md +0 -181
  409. package/skills/cometchat-ios-calls/references/audio-modes-and-controls.md +0 -45
  410. package/skills/cometchat-ios-calls/references/avaudiosession-routing.md +0 -188
  411. package/skills/cometchat-ios-calls/references/call-layouts.md +0 -76
  412. package/skills/cometchat-ios-calls/references/call-session.md +0 -218
  413. package/skills/cometchat-ios-calls/references/callkit-and-pushkit.md +0 -357
  414. package/skills/cometchat-ios-calls/references/custom-ui.md +0 -322
  415. package/skills/cometchat-ios-calls/references/device-management.md +0 -171
  416. package/skills/cometchat-ios-calls/references/group-calls.md +0 -359
  417. package/skills/cometchat-ios-calls/references/idle-timeout.md +0 -184
  418. package/skills/cometchat-ios-calls/references/in-call-chat.md +0 -179
  419. package/skills/cometchat-ios-calls/references/migration-v4-to-v5.md +0 -196
  420. package/skills/cometchat-ios-calls/references/picture-in-picture.md +0 -246
  421. package/skills/cometchat-ios-calls/references/raise-hand.md +0 -26
  422. package/skills/cometchat-ios-calls/references/recording-screen-share.md +0 -252
  423. package/skills/cometchat-ios-calls/references/ringing-integration.md +0 -143
  424. package/skills/cometchat-ios-calls/references/server-apns-pushkit.md +0 -267
  425. package/skills/cometchat-ios-calls/references/share-invite.md +0 -148
  426. package/skills/cometchat-ios-calls/references/swiftui-uikit-hosting.md +0 -246
  427. package/skills/cometchat-ios-calls/references/voip-cert-setup.md +0 -166
  428. package/skills/cometchat-ios-production/SKILL.md +0 -738
  429. package/skills/cometchat-ios-testing/SKILL.md +0 -376
  430. package/skills/cometchat-ios-theming/SKILL.md +0 -667
  431. package/skills/cometchat-ios-troubleshooting/SKILL.md +0 -1003
  432. package/skills/cometchat-native-bare-patterns/SKILL.md +0 -550
  433. package/skills/cometchat-native-calls/SKILL.md +0 -671
  434. package/skills/cometchat-native-calls/references/add-calls-to-existing-chat.md +0 -203
  435. package/skills/cometchat-native-calls/references/call-layouts.md +0 -125
  436. package/skills/cometchat-native-calls/references/call-session.md +0 -182
  437. package/skills/cometchat-native-calls/references/custom-ui.md +0 -254
  438. package/skills/cometchat-native-calls/references/device-management.md +0 -158
  439. package/skills/cometchat-native-calls/references/expo-vs-bare.md +0 -179
  440. package/skills/cometchat-native-calls/references/group-calls.md +0 -296
  441. package/skills/cometchat-native-calls/references/idle-timeout.md +0 -128
  442. package/skills/cometchat-native-calls/references/in-call-chat.md +0 -145
  443. package/skills/cometchat-native-calls/references/migration-v4-to-v5.md +0 -72
  444. package/skills/cometchat-native-calls/references/picture-in-picture.md +0 -276
  445. package/skills/cometchat-native-calls/references/raise-hand.md +0 -205
  446. package/skills/cometchat-native-calls/references/recording-screen-share.md +0 -169
  447. package/skills/cometchat-native-calls/references/ringing-integration.md +0 -118
  448. package/skills/cometchat-native-calls/references/server-push-bridge.md +0 -135
  449. package/skills/cometchat-native-calls/references/server-push-payloads.md +0 -193
  450. package/skills/cometchat-native-calls/references/share-invite.md +0 -146
  451. package/skills/cometchat-native-calls/references/voip-push-end-to-end.md +0 -319
  452. package/skills/cometchat-native-components/SKILL.md +0 -628
  453. package/skills/cometchat-native-core/SKILL.md +0 -679
  454. package/skills/cometchat-native-customization/SKILL.md +0 -716
  455. package/skills/cometchat-native-expo-patterns/SKILL.md +0 -552
  456. package/skills/cometchat-native-features/SKILL.md +0 -638
  457. package/skills/cometchat-native-placement/SKILL.md +0 -509
  458. package/skills/cometchat-native-production/SKILL.md +0 -700
  459. package/skills/cometchat-native-push/SKILL.md +0 -594
  460. package/skills/cometchat-native-testing/SKILL.md +0 -582
  461. package/skills/cometchat-native-theming/SKILL.md +0 -681
  462. package/skills/cometchat-native-troubleshooting/SKILL.md +0 -501
  463. package/skills/cometchat-nextjs-patterns/SKILL.md +0 -899
  464. package/skills/cometchat-placement/SKILL.md +0 -1360
  465. package/skills/cometchat-production/SKILL.md +0 -1027
  466. package/skills/cometchat-react-calls/SKILL.md +0 -752
  467. package/skills/cometchat-react-calls/references/add-calls-to-existing-chat.md +0 -146
  468. package/skills/cometchat-react-calls/references/call-layouts.md +0 -160
  469. package/skills/cometchat-react-calls/references/call-session.md +0 -235
  470. package/skills/cometchat-react-calls/references/custom-ui.md +0 -247
  471. package/skills/cometchat-react-calls/references/device-management.md +0 -210
  472. package/skills/cometchat-react-calls/references/group-calls.md +0 -257
  473. package/skills/cometchat-react-calls/references/idle-timeout.md +0 -188
  474. package/skills/cometchat-react-calls/references/in-call-chat.md +0 -212
  475. package/skills/cometchat-react-calls/references/migration-v4-to-v5.md +0 -170
  476. package/skills/cometchat-react-calls/references/picture-in-picture.md +0 -245
  477. package/skills/cometchat-react-calls/references/raise-hand.md +0 -238
  478. package/skills/cometchat-react-calls/references/recording-screen-share.md +0 -144
  479. package/skills/cometchat-react-calls/references/ringing-integration.md +0 -255
  480. package/skills/cometchat-react-calls/references/server-web-push-vapid.md +0 -241
  481. package/skills/cometchat-react-calls/references/share-invite.md +0 -175
  482. package/skills/cometchat-react-calls/references/testing-calls-on-web.md +0 -216
  483. package/skills/cometchat-react-calls/references/virtual-background.md +0 -32
  484. package/skills/cometchat-react-calls/references/voip-and-web-push.md +0 -165
  485. package/skills/cometchat-react-patterns/SKILL.md +0 -628
  486. package/skills/cometchat-react-push/SKILL.md +0 -634
  487. package/skills/cometchat-react-router-patterns/SKILL.md +0 -758
  488. package/skills/cometchat-react-testing/SKILL.md +0 -413
  489. package/skills/cometchat-theming/SKILL.md +0 -498
  490. package/skills/cometchat-troubleshooting/SKILL.md +0 -294
@@ -0,0 +1,909 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * lint-skills.mjs — Tier-0 static integrity gate (dependency-free, no network).
4
+ *
5
+ * The cheapest CI gate: it proves each shipped skill is structurally shippable
6
+ * BEFORE the more expensive catalog/compile tiers run. It fails the build on the
7
+ * machine-checkable invariants a hand-authored or drifted skill breaks:
8
+ *
9
+ * 1. Frontmatter present with name / description / license / compatibility
10
+ * and metadata.version (we publish versioned skills).
11
+ * 2. Frontmatter `name` === directory name (dir/name parity).
12
+ * 3. Every non-core skill carries the "Companion skills" block pointing at core
13
+ * (BUILD-ORDER.md DRY mechanism — non-core must not restate core).
14
+ * 4. Every skill dir maps to a routing entry: its capability slug is listed in
15
+ * peers.yaml under a peer's `skills:` (routing-as-data can't silently drift).
16
+ * 5. The thin router `cometchat/SKILL.md` exists (the entry the pack advertises).
17
+ *
18
+ * Plus three PACK-LEVEL durability gates (retroactive hardening of past AUDIT fixes
19
+ * that lived only as prose — the AUDIT-020→024 "correct-but-ungated silently regresses"
20
+ * lesson applied to the CLASS, so a regression fails CI instead of shipping):
21
+ * A. AUDIT-025 — no broken per-theme CSS selector in any ```css fence: the descendant
22
+ * `[data-theme=…] .cometchat {` targets a `.cometchat` NESTED in a `[data-theme]`
23
+ * ancestor, but the provider puts both on ONE div, so it matches nothing (silent
24
+ * no-op). Scans FENCES only (prose/anti-patterns may quote the bad form to warn).
25
+ * B. AUDIT-018 — shipped RULES.md carries NO skill-authoring/CI artifacts (a customer's
26
+ * agent must never be nudged to test/CI the app it built). Blocklist of authoring-only
27
+ * artifact tokens (predicate.mjs / verify.yml / verify:ci / "run the gates" / …).
28
+ * C. AUDIT-013 — no NEW `[v7-verify]` provenance marker ships: the known-intentional
29
+ * carry-over markers are allowlisted; any ADDITIONAL one fails (a regressed/unverified
30
+ * claim can't ship silently).
31
+ * D. AUDIT-029 — the follow-OS-theme RECIPE stays taught: core/references/theming.md must
32
+ * keep the matchMedia('(prefers-color-scheme: dark)') + change-listener + theme/data-theme
33
+ * sync recipe (the only way to follow the OS since the kit has no theme="system", AUDIT-004).
34
+ * Gates that the how-to is TAUGHT — NOT that OS-follow is the product default (open decision).
35
+ *
36
+ * Scope: the react-v7 pack (skills/cometchat-react-v7-*). Family-agnostic — reads
37
+ * peers.yaml, so a new family the skill-creator adds is linted automatically.
38
+ *
39
+ * Usage: node test-suite/scripts/lint-skills.mjs [--json]
40
+ * Exit: 0 all green · 1 one or more skills failed · 2 bad invocation.
41
+ */
42
+ import { readFileSync, readdirSync, existsSync, statSync } from "node:fs";
43
+ import { join, dirname } from "node:path";
44
+ import { fileURLToPath } from "node:url";
45
+
46
+ const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
47
+ const SKILLS_DIR = join(ROOT, "skills");
48
+ const PEERS = join(ROOT, "peers.yaml");
49
+ const JSON_OUT = process.argv.includes("--json");
50
+
51
+ // ── minimal YAML-ish readers (no dep) ────────────────────────────────────────
52
+ function frontmatter(txt) {
53
+ const m = txt.match(/^---\n([\s\S]*?)\n---/);
54
+ return m ? m[1] : null;
55
+ }
56
+ const topKey = (b, k) => (b.match(new RegExp("^" + k + ":\\s*(.+)$", "m")) || [])[1];
57
+ const nestedKey = (b, k) => (b.match(new RegExp("^\\s+" + k + ":\\s*(.+)$", "m")) || [])[1];
58
+
59
+ // Bare, platform-NEUTRAL skills that carry NO <platform>-<major> segment
60
+ // (NAMING.md): the cross-family skills (a11y, i18n) authored once for every family,
61
+ // and the onboarding JOURNEY gate (the DISCOVER→UNDERSTAND→PLAN front door). These are
62
+ // named `cometchat-<slug>` and are routed via peers.yaml's `gate:` / top-level pointers
63
+ // (the entry router ALWAYS routes to the gate first), NOT via a peer's `skills:` list —
64
+ // so they must be recognized here instead of failing the per-platform grammar.
65
+ const CROSS_FAMILY_SLUGS = new Set(["a11y", "i18n", "onboarding"]);
66
+
67
+ // The gates grafted below were authored for the ANGULAR family + the shared skills. Other
68
+ // families are maintained elsewhere; applied pack-wide they turned this repo's 15
69
+ // pre-existing problems into 123 by firing on 40+ android/ios/flutter/rn skills. A gate we
70
+ // wrote must not fail a family we do not own — same scoping the token and fence gates need.
71
+ const OWNED_SKILL = (d) =>
72
+ /^cometchat-angular-v5-/.test(d) || d === "cometchat" || d === "cometchat-onboarding";
73
+
74
+ // Parse the capability slugs each peer routes to (peers.yaml `skills: [...]`), plus the
75
+ // platform-neutral routing pointers (`gate:` and any bare cross-family targets).
76
+ function routedSlugs() {
77
+ if (!existsSync(PEERS)) return { platforms: [], slugs: new Set(), neutral: new Set() };
78
+ const y = readFileSync(PEERS, "utf8");
79
+ const slugs = new Set();
80
+ const neutral = new Set();
81
+ const platforms = [];
82
+ for (const m of y.matchAll(/^\s*-\s*platform:\s*(\S+)/gm)) platforms.push(m[1].trim());
83
+ for (const m of y.matchAll(/^\s*skills:\s*\[([^\]]*)\]/gm)) {
84
+ for (const s of m[1].split(",")) { const t = s.trim(); if (t) slugs.add(t); }
85
+ }
86
+ // `gate: cometchat-onboarding` (and any future bare cross-family pointer) → routed.
87
+ const gate = (y.match(/^\s*gate:\s*(\S+)/m) || [])[1];
88
+ if (gate) neutral.add(gate.trim());
89
+ return { platforms, slugs, neutral };
90
+ }
91
+
92
+ // react-v7-core → {platform:"react", major:"v7", slug:"core"}
93
+ // android-v6-builder-settings → {platform:"android", major:"v6", slug:"builder-settings"}
94
+ // (SKILL-INVENTORY sanctions HYPHENATED type slugs — the android extras `builder-settings`,
95
+ // `events`, `extensions` — so the slug group allows internal hyphens, not just [a-z]+.)
96
+ // cometchat-onboarding / cometchat-a11y → {crossFamily:true, slug:"onboarding"}
97
+ // The sanctioned platforms, from NAMING.md. Longest FIRST so `react-native` is matched before
98
+ // `react` would swallow it. Anchoring on this list (rather than a bare [a-z]+ segment) is what
99
+ // lets a HYPHENATED platform exist at all.
100
+ // AUDIT-167: `js` is the headless web-JavaScript SDK leaf platform — the framework-agnostic SDK
101
+ // families that have no UI Kit and thus no `<framework>` platform (chat `core-v4` → the parked
102
+ // `cometchat-js-v4-sdk`; calls `calls-v5` → `cometchat-js-v5-calls`). NAMING.md line 9 lists it.
103
+ const PLATFORMS = ["react-native", "react", "angular", "android", "flutter", "ios", "js"];
104
+
105
+ // Previously: /^cometchat-([a-z]+)-(v\d+)-([a-z]+)$/ — which could not express React Native in
106
+ // THREE ways at once: [a-z]+ forbids the hyphen in `react-native`, the major was MANDATORY
107
+ // (NAMING.md: "included ONLY when two majors coexist for that platform"), and the slug forbade
108
+ // the hyphen in `expo-patterns` / `bare-patterns` (the two-patterns split SKILL-INVENTORY.md
109
+ // sanctions for react-native specifically). So every correctly-named RN skill was rejected.
110
+ // Same class as the six --family hardcodes (AUDIT-091/087): a rule written for one family.
111
+ function classify(dir) {
112
+ const c = dir.match(/^cometchat-([a-z0-9]+)$/);
113
+ if (c && CROSS_FAMILY_SLUGS.has(c[1])) return { crossFamily: true, slug: c[1] };
114
+ for (const p of PLATFORMS) {
115
+ let m = dir.match(new RegExp(`^cometchat-${p}-(v\\d+)-([a-z][a-z-]*)$`));
116
+ if (m) return { platform: p, major: m[1], slug: m[2] };
117
+ m = dir.match(new RegExp(`^cometchat-${p}-([a-z][a-z-]*)$`));
118
+ if (m && !/^v\d+$/.test(m[1])) return { platform: p, major: null, slug: m[1] };
119
+ }
120
+ return null;
121
+ }
122
+
123
+ function lintSkill(dir, routed) {
124
+ const errs = [];
125
+ const file = join(SKILLS_DIR, dir, "SKILL.md");
126
+ if (!existsSync(file)) return [`${dir}: missing SKILL.md`];
127
+ const txt = readFileSync(file, "utf8");
128
+
129
+ const b = frontmatter(txt);
130
+ if (!b) return [`${dir}: missing YAML frontmatter block`];
131
+
132
+ const name = (topKey(b, "name") || "").trim();
133
+ if (!name) errs.push("frontmatter.name missing");
134
+ else if (name !== dir) errs.push(`frontmatter.name "${name}" != dir "${dir}"`);
135
+ if (!topKey(b, "description")) errs.push("frontmatter.description missing");
136
+ if (!topKey(b, "license")) errs.push("frontmatter.license missing");
137
+ if (!topKey(b, "compatibility")) errs.push("frontmatter.compatibility missing (version pins)");
138
+ if (!nestedKey(b, "version")) errs.push("frontmatter.metadata.version missing");
139
+
140
+ const cls = classify(dir);
141
+ if (!cls) {
142
+ errs.push(`name does not match the NAMING grammar cometchat-<platform>-<major>-<slug> (or a sanctioned bare cross-family/gate name)`);
143
+ } else if (cls.crossFamily) {
144
+ // Platform-NEUTRAL skill (a11y/i18n/onboarding): no per-platform Companion→core
145
+ // block (the section spine OMITS it for cross-family types — its companion is
146
+ // whichever family core the plan/context names). Routed via a neutral pointer
147
+ // (peers.yaml `gate:` / entry), not a peer's `skills:` list.
148
+ if (!routed.neutral.has(dir)) {
149
+ errs.push(`cross-family skill "${dir}" not routed in peers.yaml (add it as gate: or a neutral pointer)`);
150
+ }
151
+ } else {
152
+ // Companion block — required on every non-core per-platform skill (BUILD-ORDER DRY rule).
153
+ // Exception: an `sdk` skill is the ENTRY/core of a HEADLESS family (self-contained init/login);
154
+ // it needs a Companion→core block ONLY if that platform ALSO ships a UI-Kit `core` skill. A
155
+ // standalone SDK family (e.g. cometchat-js-v4-sdk) has no core to reference — it IS the entry.
156
+ const coreExists = existsSync(join(SKILLS_DIR, `cometchat-${cls.platform}-${cls.major}-core`));
157
+ // `sdk` OR a `<artifact>-sdk` slug (calls-sdk): a platform can ship two headless entries — the
158
+ // chat SDK (`cometchat-android-v5-sdk`) AND the calls SDK (`cometchat-android-v5-calls-sdk`) —
159
+ // and the second cannot reuse the bare slug. Both are coreless entries (AUDIT: android-calls-v5).
160
+ const isSdkEntry = cls.slug === "sdk" || cls.slug.endsWith("-sdk");
161
+ const needsCompanion = cls.slug !== "core" && (!isSdkEntry || coreExists);
162
+ if (needsCompanion) {
163
+ // The core's name must be rebuilt from the parts that EXIST. Interpolating a null major
164
+ // produced "cometchat-react-native-null-core", which can never match — so every non-core
165
+ // skill in a single-major family failed this check no matter how correct its block was.
166
+ const coreName = `cometchat-${cls.platform}${cls.major ? `-${cls.major}` : ""}-core`;
167
+ const hasCompanion = /##\s*Companion skills/i.test(txt) && new RegExp(coreName).test(txt);
168
+ if (!hasCompanion) errs.push("non-core skill missing the Companion-skills → core block");
169
+ }
170
+ // Routing-as-data: the slug must be declared in peers.yaml.
171
+ if (!routed.slugs.has(cls.slug)) {
172
+ errs.push(`slug "${cls.slug}" not routed in peers.yaml (add to a peer's skills: [...])`);
173
+ }
174
+ }
175
+ return errs.map((e) => `${dir}: ${e}`);
176
+ }
177
+
178
+ // ── pack-level durability gates (retroactive hardening of prose-only AUDIT fixes) ──
179
+
180
+ // Recursively collect .md files under a dir (skills tree).
181
+ function mdFiles(dir, out = []) {
182
+ if (!existsSync(dir)) return out;
183
+ for (const name of readdirSync(dir)) {
184
+ const p = join(dir, name);
185
+ if (statSync(p).isDirectory()) mdFiles(p, out);
186
+ else if (name.endsWith(".md")) out.push(p);
187
+ }
188
+ return out;
189
+ }
190
+
191
+ // Extract the bodies of ```css / ```scss fenced code blocks from a markdown string.
192
+ function cssFences(txt) {
193
+ const blocks = [];
194
+ const re = /```(?:css|scss)\b[^\n]*\n([\s\S]*?)```/gi;
195
+ let m;
196
+ while ((m = re.exec(txt))) blocks.push(m[1]);
197
+ return blocks;
198
+ }
199
+
200
+ // GATE A (AUDIT-025): a per-theme override written as the DESCENDANT selector
201
+ // `[data-theme=…] .cometchat {` (or `.cometchat,`) matches nothing — the provider
202
+ // puts data-theme + .cometchat on ONE element. `.cometchat[data-theme=…]` (same
203
+ // element) and `[data-theme=…] .cometchat-<bem>` (kit internal descendant) are FINE.
204
+ // FENCE-ONLY so anti-patterns.md / prose can still quote the bad form to warn about it.
205
+ const BAD_THEME_SELECTOR =
206
+ /\[data-theme\s*=\s*["']?[a-z]+["']?\s*\]\s+\.cometchat(?=[\s,{])/i;
207
+ function gateThemeSelector(files) {
208
+ const errs = [];
209
+ for (const f of files) {
210
+ for (const css of cssFences(readFileSync(f, "utf8"))) {
211
+ if (BAD_THEME_SELECTOR.test(css)) {
212
+ errs.push(
213
+ `${f.replace(ROOT + "/", "")}: broken per-theme selector in a css fence — ` +
214
+ `\`[data-theme=…] .cometchat {…}\` matches nothing (data-theme + .cometchat are the SAME div). ` +
215
+ `Use \`.cometchat[data-theme="dark"]\` (same element) or \`[data-theme="dark"] .cometchat-<bem>\` (kit internal). (AUDIT-025)`
216
+ );
217
+ }
218
+ }
219
+ }
220
+ return errs;
221
+ }
222
+
223
+ // GATE B (AUDIT-018): the shipped RULES.md must be integration-only — it must NOT carry
224
+ // skill-authoring / CI artifacts (which read to a customer's agent as "test the app").
225
+ // Blocklist is artifact tokens that ONLY appear in authoring content — NOT generic words
226
+ // like "test"/"Playwright" (the shipped "do NOT test" rule legitimately uses those).
227
+ const RULES_FORBIDDEN = [
228
+ "predicate.mjs", "verify.yml", "verify:ci", "verify:predicate",
229
+ "smoke-runner", "run the gates", "CI-enforced", "CI-green",
230
+ "reviewer smoke", "reviewer's runtime smoke", "shippability",
231
+ ];
232
+ function gateShippedRules() {
233
+ const errs = [];
234
+ const rulesPath = join(ROOT, "RULES.md");
235
+ if (!existsSync(rulesPath)) return [`RULES.md (shipped) missing`];
236
+ const txt = readFileSync(rulesPath, "utf8").toLowerCase();
237
+ for (const tok of RULES_FORBIDDEN) {
238
+ if (txt.includes(tok.toLowerCase())) {
239
+ errs.push(
240
+ `RULES.md: contains authoring/CI artifact "${tok}" — the shipped RULES must be ` +
241
+ `integration-only (no CI/authoring/reviewer content; a customer's agent must not be ` +
242
+ `nudged to test the app it built). Authoring rules live in the factory references/RULES.md. (AUDIT-018)`
243
+ );
244
+ }
245
+ }
246
+ return errs;
247
+ }
248
+
249
+ // GATE C (AUDIT-013): no NEW `[v7-verify]` provenance marker ships. The known-intentional
250
+ // v6-carry-over markers are allowlisted by (file-relative-path : substring); any ADDITIONAL
251
+ // `[v7-verify]` in the shipped skills fails, so a regressed/unverified claim can't ship.
252
+ const V7VERIFY_ALLOW = [
253
+ // path suffix ⇒ how many [v7-verify] markers are sanctioned in that file
254
+ { path: "skills/cometchat-react-v7-core/SKILL.md", count: 1 }, // the Ground-truth callout DEFINING the marker
255
+ { path: "skills/cometchat-react-v7-core/references/setup-credentials.md", count: 1 }, // initFromSettings carry-over
256
+ ];
257
+ function gateV7VerifyMarkers(files) {
258
+ const errs = [];
259
+ for (const f of files) {
260
+ const rel = f.replace(ROOT + "/", "");
261
+ const n = (readFileSync(f, "utf8").match(/\[v7-verify\]/g) || []).length;
262
+ if (n === 0) continue;
263
+ const allow = V7VERIFY_ALLOW.find((a) => rel.endsWith(a.path));
264
+ const allowed = allow ? allow.count : 0;
265
+ if (n > allowed) {
266
+ errs.push(
267
+ `${rel}: ${n} [v7-verify] marker(s) but only ${allowed} allowlisted — a NEW unverified ` +
268
+ `v6-carry-over marker cannot ship (verify it against v7 and remove the marker, or ` +
269
+ `allowlist it in lint-skills.mjs with a reason). (AUDIT-013)`
270
+ );
271
+ }
272
+ }
273
+ return errs;
274
+ }
275
+
276
+ // GATE D (AUDIT-029): the follow-OS-theme RECIPE must stay TAUGHT. The kit has no
277
+ // theme="system" and never reads the OS (AUDIT-004), so following the OS light/dark
278
+ // setting is a host-side recipe: sync CometChatProvider's theme (→ the wrapper's
279
+ // data-theme) to window.matchMedia('(prefers-color-scheme: dark)') with a change
280
+ // listener. That how-to lives in core/references/theming.md as a standalone recipe.
281
+ // Because it's prose+a fenced hook (the AUDIT-020→026 "correct-but-ungated silently
282
+ // regresses" class), assert its load-bearing tokens are present so an edit can't quietly
283
+ // delete the recipe and leave only the "kit ignores the OS" pitfall with no fix.
284
+ // NOTE: this gates that the RECIPE is TAUGHT — it does NOT assert OS-follow is the
285
+ // product DEFAULT (that remains an OPEN product decision, AUDIT-004; see AUDIT.md).
286
+ const OS_THEME_RECIPE_FILE = "skills/cometchat-react-v7-core/references/theming.md";
287
+ const OS_THEME_REQUIRED = [
288
+ { re: /matchMedia\s*[?.]*\s*\(\s*["']\(prefers-color-scheme:\s*dark\)["']\s*\)/i,
289
+ what: "matchMedia('(prefers-color-scheme: dark)') detection" },
290
+ { re: /addEventListener\s*\(\s*["']change["']/i,
291
+ what: "a 'change' listener so it live-updates on OS theme flip" },
292
+ { re: /data-theme|CometChatProvider[^\n]*theme=|theme=\{[^\n]*[Ss]ystem|useSystemTheme/i,
293
+ what: "syncing the result to CometChatProvider's theme prop / data-theme" },
294
+ ];
295
+ function gateOsThemeRecipe() {
296
+ const p = join(ROOT, OS_THEME_RECIPE_FILE);
297
+ if (!existsSync(p)) {
298
+ return [`${OS_THEME_RECIPE_FILE}: missing — the follow-OS-theme recipe file is gone (AUDIT-029/004)`];
299
+ }
300
+ const txt = readFileSync(p, "utf8");
301
+ const missing = OS_THEME_REQUIRED.filter((r) => !r.re.test(txt)).map((r) => r.what);
302
+ if (missing.length) {
303
+ return [
304
+ `${OS_THEME_RECIPE_FILE}: the follow-OS-theme recipe lost — missing ${missing.map((m) => `[${m}]`).join(", ")}. ` +
305
+ `The kit has no theme="system" (AUDIT-004), so this host-side matchMedia→data-theme recipe is the only way to follow the OS ` +
306
+ `light/dark setting; it must stay taught. Restore the recipe (or update this gate if it moved). (AUDIT-029)`
307
+ ];
308
+ }
309
+ return [];
310
+ }
311
+
312
+ // GATE E (AUDIT-084): the core INIT recipe must use `initFromSettings` (telemetry — the UI Kit/SDK
313
+ // persist integrationSource="ai-agent" ONLY on that path; the classic builder init does not). The
314
+ // reviewer's per-feature emit never includes init (harness-provided), so NO G0–G7 gate sees it — this
315
+ // static check is the only guard against a regression back to the classic init(). Framework-agnostic:
316
+ // every platform's <core>/references/lifecycle.md (+ core SKILL.md) that ships an init recipe must
317
+ // reference initFromSettings, and the canonical lifecycle recipe must not use the classic init(.
318
+ function gateInitTelemetry() {
319
+ const errs = [];
320
+ const coreDirs = readdirSync(SKILLS_DIR, { withFileTypes: true })
321
+ .filter((d) => d.isDirectory() && d.name.endsWith("-core"))
322
+ .map((d) => d.name);
323
+ for (const dir of coreDirs) {
324
+ for (const rel of [`${dir}/references/lifecycle.md`, `${dir}/SKILL.md`]) {
325
+ const p = join(SKILLS_DIR, rel);
326
+ if (!existsSync(p)) continue;
327
+ const txt = readFileSync(p, "utf8");
328
+ if (!/CometChatUIKit\.init|initCometChat|CometChat\.init\(/.test(txt)) continue; // only files that show an init recipe
329
+ if (!/initFromSettings/.test(txt)) {
330
+ errs.push(
331
+ `skills/${rel}: shows an init recipe but does NOT use initFromSettings — telemetry ` +
332
+ `attribution (integrationSource="ai-agent") requires CometChatUIKit.initFromSettings / ` +
333
+ `CometChat.initFromSettings, not the classic builder init. (AUDIT-084)`
334
+ );
335
+ }
336
+ }
337
+ const lc = join(SKILLS_DIR, `${dir}/references/lifecycle.md`);
338
+ if (existsSync(lc) && /CometChatUIKit\.init\(/.test(readFileSync(lc, "utf8"))) {
339
+ errs.push(
340
+ `skills/${dir}/references/lifecycle.md: the baked init recipe uses the classic ` +
341
+ `CometChatUIKit.init( — the default init must be initFromSettings (telemetry). (AUDIT-084)`
342
+ );
343
+ }
344
+ }
345
+ return errs;
346
+ }
347
+
348
+ // GATE F: every reference to one of the PACK'S OWN generated artifacts must resolve.
349
+ //
350
+ // The iOS skills told the agent to verify each symbol against `catalogs/ios-v5.json` — a path
351
+ // that does not exist anywhere (the catalog ships at test-suite/catalogs/ios-v5.json). Eight
352
+ // references across six files, and the pack's own anti-hallucination control therefore pointed
353
+ // at nothing: an agent told to "check the catalog before emitting a symbol" would find no
354
+ // catalog and carry on emitting. A control that silently no-ops is worse than no control,
355
+ // because the skill reads as if it is guarded.
356
+ //
357
+ // Scoped DELIBERATELY NARROWLY to repo-root artifact roots. A general "every path in a skill
358
+ // must exist" rule is unworkable: skills legitimately name paths in the USER'S project
359
+ // (.cometchat/config.json, src/vite-env.d.ts, node_modules/...) which are absent here by
360
+ // definition, and many intra-skill links are written relative to the skill root rather than the
361
+ // containing file. Both produce noise, and noise in a gate is how a gate gets ignored. These
362
+ // roots are unambiguous: they exist in this repo or they are a defect.
363
+ // Root-level oracles (`features.ios-v5.json`, `contracts.ios-v5.json`, `peers.yaml`) are matched
364
+ // too. The first version of this gate only knew directory-rooted paths, so a bare filename slipped
365
+ // through — which is exactly how the iOS feature/contract oracles ended up referenced by a skill
366
+ // while missing from package.json `files`.
367
+ const ARTIFACT_REF = /`((?:test-suite|registry|catalogs|golden|\.claude)\/[A-Za-z0-9_.\-/]+\.\w+|(?:features|contracts|peers)[A-Za-z0-9_.\-]*\.(?:json|yaml))`/g;
368
+
369
+ // Is a repo-relative path covered by package.json "files"? Directory entries cover their subtree.
370
+ function shipsInPackage(ref) {
371
+ try {
372
+ const allow = JSON.parse(readFileSync(join(ROOT, "package.json"), "utf8")).files || [];
373
+ return allow.some((f) => ref === f || ref.startsWith(f.replace(/\/$/, "") + "/"));
374
+ } catch { return true; } // no package.json to judge against: do not invent a failure
375
+ }
376
+
377
+ // GATE G: every SHIPPED platform must be discoverable in the distribution metadata.
378
+ //
379
+ // Three separate defects in one pass were the same thing — metadata still describing a React-only
380
+ // pack after iOS shipped: marketplace.json's description and keywords, package.json's description,
381
+ // and finally package.json's keywords. Each was found by a human reading a different file. A
382
+ // platform nobody can search for is invisible however good its skills are, so this is checked
383
+ // rather than remembered.
384
+ //
385
+ // Keyed off peers.yaml, so onboarding a platform makes this fail until its metadata is updated —
386
+ // which is the moment it matters, not months later. Parked peers are exempt: they ship no skills.
387
+ function gatePlatformMetadata() {
388
+ const errs = [];
389
+ let peers = "";
390
+ try { peers = readFileSync(PEERS, "utf8"); } catch { return []; }
391
+
392
+ // platform blocks, minus parked ones
393
+ const blocks = peers.split(/^\s*-\s*platform:/m).slice(1);
394
+ const shipped = [];
395
+ for (const b of blocks) {
396
+ if (/^\s*parked:\s*true/m.test(b)) continue;
397
+ const name = (b.match(/^\s*(\S+)/) || [])[1];
398
+ if (name) shipped.push(name.trim());
399
+ }
400
+ if (!shipped.length) return [];
401
+
402
+ const read = (rel) => { try { return JSON.parse(readFileSync(join(ROOT, rel), "utf8")); } catch { return null; } };
403
+ const pkg = read("package.json");
404
+ const mkt = read(".claude-plugin/marketplace.json");
405
+
406
+ const check = (label, keywords, text) => {
407
+ const hay = `${(keywords || []).join(" ")} ${text || ""}`.toLowerCase();
408
+ for (const p of shipped) {
409
+ if (!hay.includes(p.toLowerCase())) {
410
+ errs.push(`${label}: ships a '${p}' platform (peers.yaml) but never names it — a platform absent from the distribution metadata cannot be found by anyone searching for it`);
411
+ }
412
+ }
413
+ };
414
+ if (pkg) check("package.json", pkg.keywords, pkg.description);
415
+ if (mkt && mkt.plugins && mkt.plugins[0]) {
416
+ const pl = mkt.plugins[0];
417
+ check(".claude-plugin/marketplace.json", pl.keywords, `${mkt.description || ""} ${pl.description || ""}`);
418
+ }
419
+ return errs;
420
+ }
421
+
422
+ function gateArtifactRefs(files) {
423
+ const errs = [];
424
+ for (const f of files) {
425
+ const txt = readFileSync(f, "utf8");
426
+ for (const m of txt.matchAll(ARTIFACT_REF)) {
427
+ const ref = m[1];
428
+ if (!existsSync(join(ROOT, ref))) {
429
+ errs.push(`${f.replace(ROOT + "/", "")}: references \`${ref}\`, which does not exist — ` +
430
+ `a skill pointing at a missing pack artifact silently no-ops the check it describes`);
431
+ } else if (!shipsInPackage(ref)) {
432
+ // EXISTING IS NOT ENOUGH — it has to reach the user. `features.ios-v5.json` and
433
+ // `contracts.ios-v5.json` were present in the repo and named by a skill as its oracle, but
434
+ // absent from package.json `files`, so an INSTALLED pack would not contain them: the skill
435
+ // would point at an oracle that exists only on our machines. That is the same silent no-op
436
+ // as a missing file, one step later in the pipeline.
437
+ errs.push(`${f.replace(ROOT + "/", "")}: references \`${ref}\`, which exists but is NOT in ` +
438
+ `package.json "files" — it would not ship, so the reference dies on an installed pack`);
439
+ }
440
+ }
441
+ }
442
+ return errs;
443
+ }
444
+
445
+ // GATE F (AUDIT-085): SECTION-SPINE conformance — templates/_SECTION-SPINE.md fixes the
446
+ // heading set every shipped skill shares. Matched by PREFIX so a qualifying suffix stays
447
+ // legal (`## Common pitfalls (top 4 — …)`). `Prerequisites & install` is CONDITIONAL:
448
+ // required only when the skill ships its own `npm install` (restating core's is the DRY
449
+ // violation). `Common pitfalls` is n/a for troubleshooting, which is all symptom→fix.
450
+ function gateSectionSpine() {
451
+ const errs = [];
452
+ const dirs = readdirSync(SKILLS_DIR, { withFileTypes: true })
453
+ .filter((d) => d.isDirectory() && OWNED_SKILL(d.name) && d.name.startsWith("cometchat-"))
454
+ .map((d) => d.name);
455
+ for (const dir of dirs) {
456
+ const f = join(SKILLS_DIR, dir, "SKILL.md");
457
+ if (!existsSync(f)) continue;
458
+ const txt = readFileSync(f, "utf8");
459
+ const h2 = [...txt.matchAll(/^## (.+)$/gm)].map((m) => m[1].trim());
460
+ const has = (want) => h2.some((h) => h === want || h.startsWith(want + " ") || h.startsWith(want + "("));
461
+ const isCore = dir.endsWith("-core");
462
+ const isRouter = dir === "cometchat";
463
+ const isJourney = dir === "cometchat-onboarding";
464
+ if (isRouter || isJourney) continue; // router + onboarding have their own shapes
465
+
466
+ if (!isCore && !has("Companion skills")) errs.push(`${dir}: missing "## Companion skills" (spine; non-core)`);
467
+ if (!has("Use this skill when") && !has("Use when")) errs.push(`${dir}: missing "## Use this skill when" (spine)`);
468
+ if (!has("Verify it works")) errs.push(`${dir}: missing "## Verify it works" (spine)`);
469
+ if (!dir.endsWith("-troubleshooting") && !has("Common pitfalls")) {
470
+ errs.push(`${dir}: missing "## Common pitfalls" (spine; n/a only for troubleshooting)`);
471
+ }
472
+ // conditional: ships its own install command ⇒ must declare the section
473
+ if (/^```bash\n[^`]*npm install /m.test(txt) && !has("Prerequisites & install")) {
474
+ errs.push(`${dir}: ships an \`npm install\` but has no "## Prerequisites & install" (spine, conditional)`);
475
+ }
476
+ }
477
+ return errs;
478
+ }
479
+
480
+
481
+ // GATE G: a preview-deploy DOCS_BASE is allowed (docs-maximal work happens against a
482
+ // Mintlify preview) but must carry a `SWAP-FOR-PROD` marker within 3 lines, so a release
483
+ // can grep for every one. Shipping an unlabelled preview URL 404s every fetch once the
484
+ // docs branch is deleted.
485
+ function gatePreviewDocsBase() {
486
+ const errs = [];
487
+ for (const f of mdFiles(SKILLS_DIR)) {
488
+ if (!OWNED_SKILL((f.slice(SKILLS_DIR.length + 1).split('/')[0]))) continue;
489
+ const lines = readFileSync(f, "utf8").split("\n");
490
+ lines.forEach((line, i) => {
491
+ if (!/mintlify\.site/.test(line)) return;
492
+ const near = lines.slice(Math.max(0, i - 1), i + 4).join("\n");
493
+ if (!/SWAP-FOR-PROD/.test(near)) {
494
+ errs.push(
495
+ `${f.replace(ROOT + "/", "")}:${i + 1}: preview docs base (mintlify.site) with no ` +
496
+ `SWAP-FOR-PROD marker within 3 lines — label it so it cannot ship unnoticed`
497
+ );
498
+ }
499
+ });
500
+ }
501
+ return errs;
502
+ }
503
+
504
+
505
+ // GATE H (AUDIT-091): a features skill's description must cover its oracle's vocabulary.
506
+ // Under progressive disclosure the description is the only thing in context at routing
507
+ // time, so a feature with no lexical hook there is unreachable — the skill never loads and
508
+ // the agent hand-rolls what the product already ships. Lexical, not semantic: every feature
509
+ // id must contribute at least one word (>3 chars) present in the description.
510
+ function gateFeatureDescriptionCoverage() {
511
+ const errs = [];
512
+ for (const dir of readdirSync(SKILLS_DIR, { withFileTypes: true })
513
+ .filter((d) => d.isDirectory() && OWNED_SKILL(d.name) && /-features$/.test(d.name))
514
+ .map((d) => d.name)) {
515
+ const f = join(SKILLS_DIR, dir, "SKILL.md");
516
+ if (!existsSync(f)) continue;
517
+ const txt = readFileSync(f, "utf8");
518
+ const desc = ((txt.match(/^description:.*$/m) || [""])[0]).toLowerCase();
519
+ const m = dir.match(/^cometchat-(.+)-features$/);
520
+ if (!m) continue;
521
+ // features.<platform>-<major>.json, or the unsuffixed react default
522
+ const cand = [join(ROOT, `features.${m[1]}.json`), join(ROOT, "features.json")];
523
+ const oraclePath = cand.find((p) => existsSync(p));
524
+ if (!oraclePath) continue;
525
+ let ids = [];
526
+ try { ids = (JSON.parse(readFileSync(oraclePath, "utf8")).features || []).map((x) => x.id).filter(Boolean); }
527
+ catch { continue; }
528
+ const missing = ids.filter((id) => !id.split("-").filter((w) => w.length > 3).some((w) => desc.includes(w)));
529
+ if (missing.length) {
530
+ errs.push(
531
+ `${dir}: ${missing.length}/${ids.length} feature(s) in ${oraclePath.replace(ROOT + "/", "")} have no lexical hook ` +
532
+ `in the skill description, so the router cannot reach them: ${missing.slice(0, 8).join(", ")}` +
533
+ (missing.length > 8 ? ` …+${missing.length - 8}` : "")
534
+ );
535
+ }
536
+ }
537
+ return errs;
538
+ }
539
+
540
+
541
+ // GATE I (AUDIT-092): every shipped family must be reachable through the shared skills.
542
+ // The router and onboarding are authored while one family is the only one that exists, so
543
+ // a second family can ship without ever being added to their routing prose — every other
544
+ // gate stays green because each skill is individually valid. A family is shipped if
545
+ // skills/cometchat-<family>-core exists; each shared skill must NAME that core. Structural
546
+ // on purpose: reading prose sentiment produced false positives.
547
+ function gateSharedSkillsRouteEveryFamily() {
548
+ const errs = [];
549
+ const shipped = readdirSync(SKILLS_DIR, { withFileTypes: true })
550
+ .filter((d) => d.isDirectory() && OWNED_SKILL(d.name))
551
+ .map((d) => (d.name.match(/^cometchat-(.+)-core$/) || [])[1])
552
+ .filter(Boolean);
553
+ if (shipped.length < 2) return errs; // single-family pack: nothing to orphan
554
+ for (const shared of ["cometchat", "cometchat-onboarding"]) {
555
+ const f = join(SKILLS_DIR, shared, "SKILL.md");
556
+ if (!existsSync(f)) continue;
557
+ const txt = readFileSync(f, "utf8");
558
+ for (const fam of shipped) {
559
+ if (!txt.includes(`cometchat-${fam}-core`)) {
560
+ errs.push(`${shared}: never names \`cometchat-${fam}-core\`, so the shipped ${fam} family is unreachable through it`);
561
+ }
562
+ }
563
+ }
564
+ return errs;
565
+ }
566
+
567
+
568
+ // GATE J (AUDIT-093): field-proven facts that must not silently vanish. Each is one
569
+ // sentence a later edit could drop with every other gate still green — the recipe would
570
+ // still compile, pass the catalog, and fit the budget.
571
+ function gateFieldProvenFacts() {
572
+ const errs = [];
573
+ const MUST = [
574
+ { file: "cometchat-angular-v5-features/references/custom-messages.md",
575
+ needle: /\{type\}_\{category\}/,
576
+ why: "the bubble key format — setBubbleView('location') is accepted silently, never matches, and renders \"This message type is not supported\"" },
577
+ { file: "cometchat-angular-v5-placement/references/core-surface.md",
578
+ needle: /getConversationType\(\)/,
579
+ why: "(itemClick) emits a Conversation; duck-typing it sends every GROUP into the [user] slot -> 'getUid is not a function'" },
580
+ { file: "cometchat-angular-v5-placement/SKILL.md",
581
+ needle: /getConversationType\(\)/,
582
+ why: "same discrimination bug, in the entry-level recipe" },
583
+ { file: "cometchat-angular-v5-core/SKILL.md",
584
+ needle: /Which user should the app log in as/,
585
+ why: "the startup UID must be ASKED; a baked cometchat-uid-1 logs in nowhere on an app without seeded samples" },
586
+ { file: "cometchat-angular-v5-core/SKILL.md",
587
+ needle: /getConversationType\(\)/,
588
+ why: "the GOLDEN PATH itself — the entry recipe an unscoped 'add chat' emits. It was guarded in placement/* but not here, so the one-slot [user] binding survived and shipped: 1:1 works, groups throw 'getUid is not a function' (contracts.angular-v5.json conversation-subject-discriminated)" },
589
+ { file: "cometchat-angular-v5-components/SKILL.md",
590
+ needle: /cometchat-avatar/,
591
+ why: "the kit ships presentational atoms; without this the agent hand-rolls an <img> avatar" },
592
+ { file: "cometchat-onboarding/references/calling-first.md",
593
+ needle: /VoIP push/,
594
+ why: "the ringing shape question must ALSO ask about delivery: 'one-on-one ringing' reads as WhatsApp, but the recipe is foreground-only — background/killed ringing is a separate VoIP-push build (FCM + ConnectionService + a real device). Asked after the plan, the limit cannot inform the choice (AUDIT-195)" },
595
+ { file: "cometchat-onboarding/references/calling-first.md",
596
+ needle: /background|backgrounded/,
597
+ why: "the foreground-only limit must be stated at PLAN time; the foreground-only build passes every casual test (both apps open in dev) and fails first in the user's real world (AUDIT-195)" },
598
+ { file: "cometchat-android-v5-calls-sdk/SKILL.md",
599
+ needle: /getLoggedInUser\(\)\?\.uid == uid/,
600
+ why: "the session guard must branch on IDENTITY: guarding login on `getLoggedInUser() != null` (the docs' own snippet) silently places calls as the PREVIOUS user on a shared/re-used device (AUDIT-194)" },
601
+ { file: "cometchat-android-v5-calls-sdk/references/user-switch.md",
602
+ needle: /isInitialized/,
603
+ why: "logout leaves the Calls SDK de-initialized, so a user switch needs logout -> RE-INIT -> login; without the re-init generateToken fails with ERROR_COMETCHAT_CALLS_SDK_INIT, an error naming the wrong cause (AUDIT-194)" },
604
+ ];
605
+ for (const { file, needle, why } of MUST) {
606
+ const f = join(SKILLS_DIR, file);
607
+ if (!existsSync(f)) { errs.push(`${file}: missing (GATE J expects it)`); continue; }
608
+ if (!needle.test(readFileSync(f, "utf8"))) {
609
+ errs.push(`${file}: lost a field-proven fact — ${why}`);
610
+ }
611
+ }
612
+ return errs;
613
+ }
614
+
615
+
616
+ // GATE K (AUDIT-094): every code-emitting skill must carry a docs route. A skill that
617
+ // emits code while naming no docs path leaves the installed bundle as the only place an
618
+ // agent can look — that is how node_modules excursions start. Satisfied by referencing
619
+ // docs-map.md / DOCS_BASE / a docs URL, or by declaring a labelled DOCS GAP.
620
+ function gateDocsRouteOrDeclaredGap() {
621
+ const errs = [];
622
+ const EXEMPT = new Set(["cometchat", "cometchat-onboarding"]); // router + journey: emit no symbols
623
+ for (const dir of readdirSync(SKILLS_DIR, { withFileTypes: true })
624
+ .filter((d) => d.isDirectory() && OWNED_SKILL(d.name) && !EXEMPT.has(d.name)).map((d) => d.name)) {
625
+ const f = join(SKILLS_DIR, dir, "SKILL.md");
626
+ if (!existsSync(f)) continue;
627
+ let txt = readFileSync(f, "utf8");
628
+ for (const ref of ["references"]) {
629
+ const rd = join(SKILLS_DIR, dir, ref);
630
+ if (existsSync(rd)) for (const r of readdirSync(rd)) txt += readFileSync(join(rd, r), "utf8");
631
+ }
632
+ const hasRoute = /docs-map|DOCS_BASE|docs_topic/.test(txt);
633
+ const declaresGap = /docs (?:have|do) no|not in the docs|no dedicated .{0,40}page|no testing page|DOCS GAP/i.test(txt);
634
+ if (!hasRoute && !declaresGap) {
635
+ errs.push(`${dir}: emits code but names no docs route (no docs-map / DOCS_BASE / docs_topic) and declares no DOCS GAP — the installed bundle becomes the only lookup path (RULES §20)`);
636
+ }
637
+ }
638
+ return errs;
639
+ }
640
+
641
+
642
+ // GATE L (AUDIT-097): the six kit list components size their inner scroll region off the
643
+ // HOST element's height and never size themselves (verified vs the compiled 5.1.0 bundle).
644
+ // Bare in a flex column they default to min-height:auto, so they grow past the shell and
645
+ // get clipped instead of scrolling. Every occurrence in a layout recipe must carry
646
+ // class="cc-fill" — or another class defined in the SAME skill with both `flex:` and
647
+ // `min-height: 0`.
648
+ function gateLeafListsAreSized() {
649
+ const errs = [];
650
+ const NEEDS_FILL = ["cometchat-conversations", "cometchat-users", "cometchat-groups",
651
+ "cometchat-call-logs", "cometchat-search", "cometchat-message-list"];
652
+ for (const dir of readdirSync(SKILLS_DIR, { withFileTypes: true })
653
+ .filter((d) => d.isDirectory() && OWNED_SKILL(d.name) && /^cometchat-angular-v5-/.test(d.name)).map((d) => d.name)) {
654
+ const files = [join(SKILLS_DIR, dir, "SKILL.md")];
655
+ const rd = join(SKILLS_DIR, dir, "references");
656
+ if (existsSync(rd)) for (const r of readdirSync(rd)) files.push(join(rd, r));
657
+ const texts = files.filter(existsSync).map((f) => readFileSync(f, "utf8"));
658
+ // classes defined ANYWHERE in this skill's files that carry both flex and
659
+ // min-height: 0 — a reference file legitimately reuses a class the companion
660
+ // SKILL.md or another reference defines ("Built on core-surface.md — the
661
+ // shell is identical" is exactly this pattern, not a missing definition).
662
+ const sizedClasses = new Set(
663
+ texts.flatMap((txt) =>
664
+ [...txt.matchAll(/\.([a-zA-Z0-9_-]+)\s*\{[^}]*\}/gs)]
665
+ .filter((m) => /flex\s*:/.test(m[0]) && /min-height\s*:\s*0/.test(m[0]))
666
+ .map((m) => m[1])
667
+ )
668
+ );
669
+ for (const f of files) {
670
+ if (!existsSync(f)) continue;
671
+ const txt = readFileSync(f, "utf8");
672
+ // Only check INSIDE an html fence that is itself a shell/column layout (contains
673
+ // cc-side/cc-main/cc-panel/cc-tabs). A snippet illustrating one prop in isolation —
674
+ // e.g. formatters.md's `<cometchat-message-list [textFormatters]="…">` — is not
675
+ // claiming to be a droppable layout and is correctly exempt.
676
+ for (const fence of txt.matchAll(/```html\n([\s\S]*?)```/g)) {
677
+ const block = fence[1];
678
+ if (!/\bcc-(side|main|panel|tabs)\b/.test(block)) continue;
679
+ // AUDIT-106: <cometchat-error-boundary> ships no :host rule (verified vs 5.1.0), so
680
+ // wrapping the shell in it inserts an auto-height flex item that breaks a
681
+ // `height: 100%` chain below it. Styled by ELEMENT here, not by class.
682
+ if (/<cometchat-error-boundary\b/.test(block)) {
683
+ const styled = texts.some((t) =>
684
+ /cometchat-error-boundary\s*\{[^}]*\}/s.test(t) &&
685
+ [...t.matchAll(/cometchat-error-boundary\s*\{([^}]*)\}/gs)]
686
+ .some((m) => /flex\s*:/.test(m[1]) && /min-height\s*:\s*0/.test(m[1]))
687
+ );
688
+ if (!styled) {
689
+ errs.push(`${dir}/${f.split("/").pop()}: <cometchat-error-boundary> wraps a shell layout but no \`cometchat-error-boundary { flex: …; min-height: 0 }\` rule is defined — the kit ships no :host sizing, so the height chain breaks under a routed \`height: 100%\` shell (AUDIT-106)`);
690
+ }
691
+ }
692
+ for (const tag of NEEDS_FILL) {
693
+ const re = new RegExp(`<${tag}\\b([^>]*)>`, "g");
694
+ let m;
695
+ while ((m = re.exec(block))) {
696
+ const attrs = m[1];
697
+ const classMatch = attrs.match(/class="([^"]*)"/);
698
+ const classes = classMatch ? classMatch[1].split(/\s+/) : [];
699
+ if (!classes.some((c) => sizedClasses.has(c))) {
700
+ errs.push(`${dir}/${f.split("/").pop()}: <${tag}> inside a shell layout has no sized class (flex + min-height:0) — it will clip instead of scroll`);
701
+ }
702
+ }
703
+ }
704
+ }
705
+ }
706
+ }
707
+ return errs;
708
+ }
709
+
710
+ // GATE M (AUDIT-106): a skill must route to docs for its OWN subject, not merely name some
711
+ // docs path — GATE K is satisfied by any docs-map mention, which a migration skill passes
712
+ // while carrying no route to the migration guide at all. Add future subject-specific skills
713
+ // to SUBJECT_ROUTES.
714
+ const SUBJECT_ROUTES = [
715
+ { suffix: "-migration", needle: /migration-guide|migration-overview|migration-property-changes|\/upgrade/i,
716
+ what: "a migration/upgrade docs path" },
717
+ ];
718
+ function gateSubjectDocsRoute() {
719
+ const errs = [];
720
+ for (const dir of readdirSync(SKILLS_DIR, { withFileTypes: true })
721
+ .filter((d) => d.isDirectory() && OWNED_SKILL(d.name)).map((d) => d.name)) {
722
+ const rule = SUBJECT_ROUTES.find((r) => dir.endsWith(r.suffix));
723
+ if (!rule) continue;
724
+ const f = join(SKILLS_DIR, dir, "SKILL.md");
725
+ if (!existsSync(f)) continue;
726
+ // The route must be reachable from the skill itself OR from the docs-map it points at.
727
+ let txt = readFileSync(f, "utf8");
728
+ const rd = join(SKILLS_DIR, dir, "references");
729
+ if (existsSync(rd)) for (const r of readdirSync(rd)) txt += readFileSync(join(rd, r), "utf8");
730
+ const family = (dir.match(/^cometchat-(.+)-migration$/) || [])[1];
731
+ const dm = family && join(SKILLS_DIR, `cometchat-${family}-core`, "references", "docs-map.md");
732
+ const dmTxt = dm && existsSync(dm) ? readFileSync(dm, "utf8") : "";
733
+ const inSkill = rule.needle.test(txt);
734
+ const inDocsMap = rule.needle.test(dmTxt);
735
+ if (!inSkill || !inDocsMap) {
736
+ const where = !inSkill && !inDocsMap ? "its own files AND its family's docs-map.md"
737
+ : !inSkill ? "its own files (SKILL.md + references/)"
738
+ : "its family's core docs-map.md";
739
+ errs.push(`${dir}: no ${rule.what} in ${where} — a subject-specific skill must route to ITS OWN docs, not only to the component docs-map (RULES §Fetch discipline; AUDIT-106)`);
740
+ }
741
+ }
742
+ return errs;
743
+ }
744
+
745
+ // GATE N (AUDIT-107): the app-level `chat-experience` oracle must list every list component
746
+ // its family's grow-target recipe actually renders — an oracle listing fewer components than
747
+ // the recipe emits makes the agent drop a tab. Checked against the RECIPE, not the sibling
748
+ // family: families may diverge, but a family must not disagree with itself.
749
+ function gateChatExperienceMatchesRecipe() {
750
+ const errs = [];
751
+ const FAMILIES = [
752
+ { features: "features.angular-v5.json", recipe: "cometchat-angular-v5-placement/references/combined-app.md",
753
+ tags: { "cometchat-conversations": "CometChatConversationsComponent", "cometchat-users": "CometChatUsersComponent",
754
+ "cometchat-groups": "CometChatGroupsComponent", "cometchat-call-logs": "CometChatCallLogsComponent" } },
755
+ ];
756
+ for (const fam of FAMILIES) {
757
+ const fp = join(SKILLS_DIR, "..", fam.features);
758
+ const rp = join(SKILLS_DIR, fam.recipe);
759
+ if (!existsSync(fp) || !existsSync(rp)) continue;
760
+ let feats;
761
+ try { feats = JSON.parse(readFileSync(fp, "utf8")); } catch { continue; }
762
+ const flat = Array.isArray(feats) ? feats : Object.values(feats).flat().filter((x) => x && x.id);
763
+ const ce = flat.find((x) => x.id === "chat-experience");
764
+ if (!ce) continue;
765
+ const declared = new Set(ce.ui_components || []);
766
+ const recipe = readFileSync(rp, "utf8");
767
+ for (const [tag, cls] of Object.entries(fam.tags)) {
768
+ // the recipe renders it (as an element) but the oracle omits it
769
+ if (new RegExp(`<${tag}\\b`).test(recipe) && !declared.has(cls)) {
770
+ errs.push(`${fam.features}: chat-experience omits ${cls}, but ${fam.recipe} renders <${tag}> — the app-level oracle must list every component its own grow-target recipe emits (AUDIT-107)`);
771
+ }
772
+ }
773
+ }
774
+ return errs;
775
+ }
776
+
777
+
778
+ // GATE Q (AUDIT-180): a skill may only tell an agent to run CLI commands that EXIST. The
779
+ // shipped dashboard CLI (@cometchat/skills-cli) has auth/provision/config/features — no
780
+ // `detect`. The skills pack CLI (@cometchat/skills) DOES ship `detect` (bin/cometchat-skills.mjs
781
+ // since v5.0.0, the authority for framework / UI Kit / version_conflict / existing_cometchat —
782
+ // RULES §3/§21), so its command set is DERIVED from the bin's dispatcher, never hard-coded: the
783
+ // hard-coded add/install list flagged the real `detect` as nonexistent. A bare `detect --json`
784
+ // that names no tool is still flagged (an agent can't tell which CLI to run). Applied pack-wide
785
+ // plus the shipped RULES.md (which the same agents read).
786
+ function packCliCommands() {
787
+ try {
788
+ const src = readFileSync(join(ROOT, "bin", "cometchat-skills.mjs"), "utf8");
789
+ const cmds = [...src.slice(src.lastIndexOf("switch (CMD)")).matchAll(/case "([a-z][a-z-]*)":/g)].map((m) => m[1]);
790
+ if (cmds.length) return new Set(cmds);
791
+ } catch { /* fall through */ }
792
+ return new Set(["add", "install", "detect"]);
793
+ }
794
+ function gateCliCommandsExist(files) {
795
+ const errs = [];
796
+ const CLI_OK = new Set(["auth", "provision", "config", "features"]);
797
+ const PACK_OK = packCliCommands();
798
+ const RE = /(@cometchat\/skills-cli(?:@\d+)?|@cometchat\/skills)\s+([a-z][a-z-]*)/g;
799
+ const targets = [...files];
800
+ const shippedRules = join(ROOT, "RULES.md");
801
+ if (existsSync(shippedRules)) targets.push(shippedRules);
802
+ for (const f of targets) {
803
+ const rel = f.startsWith(SKILLS_DIR) ? f.slice(SKILLS_DIR.length + 1) : f.slice(ROOT.length + 1);
804
+ readFileSync(f, "utf8").split("\n").forEach((line, i) => {
805
+ for (const m of line.matchAll(RE)) {
806
+ const isCli = m[1].startsWith("@cometchat/skills-cli");
807
+ const ok = isCli ? CLI_OK : PACK_OK;
808
+ if (!ok.has(m[2])) {
809
+ errs.push(`${rel}:${i + 1}: tells the agent to run \`${m[0]}\` — ${isCli ? "@cometchat/skills-cli" : "@cometchat/skills"} has no \`${m[2]}\` command (real: ${[...ok].join("/")}) (RULES §21)`);
810
+ }
811
+ }
812
+ if (/\bdetect --json\b/.test(line) && !/@cometchat\/skills\s+detect --json\b/.test(line)) {
813
+ errs.push(`${rel}:${i + 1}: references \`detect --json\` without naming the tool — write \`npx @cometchat/skills detect --json\` (the skills pack CLI; the dashboard CLI has no detect) (RULES §21)`);
814
+ }
815
+ });
816
+ }
817
+ return errs;
818
+ }
819
+
820
+ // GATE R (AUDIT-181): the shared skills' PLATFORM COVERAGE must match the shipped families.
821
+ // GATE I checks a shipped core is NAMED; this checks the journey can actually run for it:
822
+ // platforms.md needs a column per shipped family (else beat 1 is improvised), and the
823
+ // "not-yet-built" lists must not name a family that ships (a literal agent declines the user —
824
+ // onboarding listed Android/RN/Flutter/Angular as not-built after all four had shipped).
825
+ function gateSharedSkillsCoverShippedFamilies() {
826
+ const errs = [];
827
+ const LABEL = { react: "React", angular: "Angular", "react-native": "React Native", android: "Android", flutter: "Flutter", ios: "iOS" };
828
+ const shipped = readdirSync(SKILLS_DIR, { withFileTypes: true })
829
+ .filter((d) => d.isDirectory())
830
+ .map((d) => (d.name.match(/^cometchat-(.+)-core$/) || [])[1])
831
+ .filter(Boolean)
832
+ .map((fam) => ({ fam, label: LABEL[fam.replace(/-v\d+$/, "")] }))
833
+ .filter((x) => x.label);
834
+ if (shipped.length < 2) return errs;
835
+ const onb = join(SKILLS_DIR, "cometchat-onboarding");
836
+ const plat = join(onb, "references", "platforms.md");
837
+ if (existsSync(plat)) {
838
+ const txt = readFileSync(plat, "utf8");
839
+ for (const { fam, label } of shipped) {
840
+ if (!new RegExp(`\\|[^|\\n]*\\b${label}\\b[^|\\n]*\\|`).test(txt)) {
841
+ errs.push(`cometchat-onboarding/references/platforms.md: no table column for the shipped ${fam} family ("${label}") — beat 1 gets improvised on that platform`);
842
+ }
843
+ }
844
+ }
845
+ for (const f of [join(onb, "SKILL.md"), join(onb, "references", "not-in-this-pack.md")]) {
846
+ if (!existsSync(f)) continue;
847
+ const txt = readFileSync(f, "utf8");
848
+ for (const m of txt.matchAll(/Not-yet-built core \([^)]*?peer — (.*?) today/g)) {
849
+ for (const { fam, label } of shipped) {
850
+ if (new RegExp(`\\b${label}\\b`).test(m[1])) {
851
+ errs.push(`${f.slice(SKILLS_DIR.length + 1)}: lists the shipped ${fam} family ("${label}") as not-yet-built — a literal agent will decline that platform`);
852
+ }
853
+ }
854
+ }
855
+ }
856
+ return errs;
857
+ }
858
+
859
+ function main() {
860
+ if (!existsSync(SKILLS_DIR)) { console.error("no skills/ dir"); process.exit(2); }
861
+ const routed = routedSlugs();
862
+ const dirs = readdirSync(SKILLS_DIR, { withFileTypes: true })
863
+ .filter((d) => d.isDirectory())
864
+ .map((d) => d.name);
865
+
866
+ const routerOk = existsSync(join(SKILLS_DIR, "cometchat", "SKILL.md"));
867
+ const allErrs = [];
868
+ let checked = 0;
869
+ for (const dir of dirs) {
870
+ if (dir === "cometchat") continue; // the thin router, linted separately below
871
+ checked++;
872
+ allErrs.push(...lintSkill(dir, routed));
873
+ }
874
+ if (!routerOk) allErrs.push("cometchat/SKILL.md (thin router) missing");
875
+
876
+ // Pack-level durability gates (AUDIT-025 / AUDIT-018 / AUDIT-013 / AUDIT-029).
877
+ const allMd = mdFiles(SKILLS_DIR);
878
+ allErrs.push(...gateThemeSelector(allMd));
879
+ allErrs.push(...gateShippedRules());
880
+ allErrs.push(...gateV7VerifyMarkers(allMd));
881
+ allErrs.push(...gateOsThemeRecipe());
882
+ allErrs.push(...gateInitTelemetry());
883
+ allErrs.push(...gateSectionSpine());
884
+ allErrs.push(...gatePreviewDocsBase());
885
+ allErrs.push(...gateFeatureDescriptionCoverage());
886
+ allErrs.push(...gateSharedSkillsRouteEveryFamily());
887
+ allErrs.push(...gateFieldProvenFacts());
888
+ allErrs.push(...gateDocsRouteOrDeclaredGap());
889
+ allErrs.push(...gateLeafListsAreSized());
890
+ allErrs.push(...gateSubjectDocsRoute());
891
+ allErrs.push(...gateChatExperienceMatchesRecipe());
892
+ allErrs.push(...gateArtifactRefs(allMd));
893
+ allErrs.push(...gatePlatformMetadata());
894
+ allErrs.push(...gateCliCommandsExist(allMd));
895
+ allErrs.push(...gateSharedSkillsCoverShippedFamilies());
896
+
897
+ const ok = allErrs.length === 0;
898
+ if (JSON_OUT) {
899
+ console.log(JSON.stringify({ ok, checked, routerOk, errors: allErrs }, null, 0));
900
+ } else if (ok) {
901
+ console.log(`✅ lint-skills: ${checked} skill(s) valid; router present; ${routed.slugs.size} routed slug(s); pack durability gates green (theme-selector / shipped-RULES / v7-verify / os-theme-recipe / init-telemetry / artifact-refs / platform-metadata / section-spine / preview-docs-base / feature-description-coverage / shared-routes-every-family / field-proven-facts / docs-route / leaf-lists-sized / subject-docs-route / chat-experience-matches-recipe / cli-commands-exist / shared-skills-cover-shipped-families).`);
902
+ } else {
903
+ console.log(`❌ lint-skills: ${allErrs.length} problem(s) across ${checked} skill(s):`);
904
+ for (const e of allErrs) console.log(` - ${e}`);
905
+ }
906
+ process.exit(ok ? 0 : 1);
907
+ }
908
+
909
+ main();