@cometchat/skills 4.4.2 → 5.0.1

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