vybekiit 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (665) hide show
  1. package/dist/bin.js +840 -296
  2. package/dist/global-skills/add-ai/SKILL.md +53 -0
  3. package/dist/global-skills/add-analytics/SKILL.md +65 -0
  4. package/dist/global-skills/add-app-clip/SKILL.md +280 -0
  5. package/dist/global-skills/add-app-clip/agents/openai.yaml +4 -0
  6. package/dist/global-skills/add-app-clip/references/native-module.md +96 -0
  7. package/dist/global-skills/add-blog/SKILL.md +49 -0
  8. package/dist/global-skills/add-crud/SKILL.md +28 -0
  9. package/dist/global-skills/add-files/SKILL.md +65 -0
  10. package/dist/global-skills/add-images/SKILL.md +64 -0
  11. package/dist/global-skills/add-language/SKILL.md +44 -0
  12. package/dist/global-skills/add-notifications/SKILL.md +61 -0
  13. package/dist/global-skills/add-realtime/SKILL.md +49 -0
  14. package/dist/global-skills/add-route/SKILL.md +28 -0
  15. package/dist/global-skills/add-search/SKILL.md +43 -0
  16. package/dist/global-skills/add-signin/SKILL.md +78 -0
  17. package/dist/global-skills/add-teams/SKILL.md +64 -0
  18. package/dist/global-skills/add-upload/SKILL.md +27 -0
  19. package/dist/global-skills/aws-cdk/SKILL.md +72 -0
  20. package/dist/global-skills/aws-cdk/references/bootstrap-and-project-setup.md +257 -0
  21. package/dist/global-skills/aws-cdk/references/compliance-and-drift.md +193 -0
  22. package/dist/global-skills/aws-cdk/references/construct-patterns.md +315 -0
  23. package/dist/global-skills/aws-cdk/references/import-and-migrate.md +184 -0
  24. package/dist/global-skills/aws-cdk/references/refactor-and-prevent-replacement.md +198 -0
  25. package/dist/global-skills/aws-cdk/references/troubleshooting-credentials.md +183 -0
  26. package/dist/global-skills/aws-cdk/references/troubleshooting-deployment.md +293 -0
  27. package/dist/global-skills/aws-cdk/references/troubleshooting-synth.md +274 -0
  28. package/dist/global-skills/aws-cdk/references/v1-to-v2-migration.md +119 -0
  29. package/dist/global-skills/aws-cloudformation/SKILL.md +88 -0
  30. package/dist/global-skills/aws-cloudformation/references/author-cloudformation-best-practices.script.md +179 -0
  31. package/dist/global-skills/aws-cloudformation/references/check-cloudformation-template-compliance.script.md +159 -0
  32. package/dist/global-skills/aws-cloudformation/references/cloudformation-pre-deploy-validation.script.md +217 -0
  33. package/dist/global-skills/aws-cloudformation/references/deploy-with-express-mode.script.md +271 -0
  34. package/dist/global-skills/aws-cloudformation/references/lookup-resource-properties.script.md +130 -0
  35. package/dist/global-skills/aws-cloudformation/references/troubleshoot-deployment.script.md +200 -0
  36. package/dist/global-skills/aws-cloudformation/references/validate-cloudformation-template.script.md +135 -0
  37. package/dist/global-skills/aws-serverless/SKILL.md +51 -0
  38. package/dist/global-skills/aws-serverless/assets/powertools-handler.py +49 -0
  39. package/dist/global-skills/aws-serverless/references/api-gateway.md +553 -0
  40. package/dist/global-skills/aws-serverless/references/architecture.md +262 -0
  41. package/dist/global-skills/aws-serverless/references/concurrency.md +200 -0
  42. package/dist/global-skills/aws-serverless/references/deployment.md +94 -0
  43. package/dist/global-skills/aws-serverless/references/event-sources.md +484 -0
  44. package/dist/global-skills/aws-serverless/references/lambda.md +548 -0
  45. package/dist/global-skills/aws-serverless/references/orchestration.md +449 -0
  46. package/dist/global-skills/aws-serverless/references/production.md +493 -0
  47. package/dist/global-skills/aws-serverless/references/troubleshooting.md +711 -0
  48. package/dist/global-skills/back-up-my-code/SKILL.md +48 -0
  49. package/dist/global-skills/better-auth-best-practices/SKILL.md +182 -0
  50. package/dist/global-skills/better-auth-security-best-practices/SKILL.md +432 -0
  51. package/dist/global-skills/building-native-ui/SKILL.md +377 -0
  52. package/dist/global-skills/building-native-ui/agents/openai.yaml +4 -0
  53. package/dist/global-skills/building-native-ui/references/animations.md +220 -0
  54. package/dist/global-skills/building-native-ui/references/controls.md +270 -0
  55. package/dist/global-skills/building-native-ui/references/form-sheet.md +253 -0
  56. package/dist/global-skills/building-native-ui/references/gradients.md +106 -0
  57. package/dist/global-skills/building-native-ui/references/icons.md +213 -0
  58. package/dist/global-skills/building-native-ui/references/media.md +198 -0
  59. package/dist/global-skills/building-native-ui/references/route-structure.md +229 -0
  60. package/dist/global-skills/building-native-ui/references/search.md +248 -0
  61. package/dist/global-skills/building-native-ui/references/storage.md +121 -0
  62. package/dist/global-skills/building-native-ui/references/tabs.md +433 -0
  63. package/dist/global-skills/building-native-ui/references/toolbar-and-headers.md +284 -0
  64. package/dist/global-skills/building-native-ui/references/visual-effects.md +197 -0
  65. package/dist/global-skills/building-native-ui/references/webgpu-three.md +605 -0
  66. package/dist/global-skills/building-native-ui/references/zoom-transitions.md +158 -0
  67. package/dist/global-skills/buy-domain/SKILL.md +64 -0
  68. package/dist/global-skills/check-safety/SKILL.md +94 -0
  69. package/dist/global-skills/claimable-postgres/SKILL.md +249 -0
  70. package/dist/global-skills/configure-capabilities/SKILL.md +38 -0
  71. package/dist/global-skills/connect-account/SKILL.md +64 -0
  72. package/dist/global-skills/connect-account-backend/SKILL.md +41 -0
  73. package/dist/global-skills/create-auth-skill/SKILL.md +321 -0
  74. package/dist/global-skills/design-my-data/SKILL.md +48 -0
  75. package/dist/global-skills/doctor/SKILL.md +84 -0
  76. package/dist/global-skills/eas-app-stores/SKILL.md +152 -0
  77. package/dist/global-skills/eas-app-stores/agents/openai.yaml +4 -0
  78. package/dist/global-skills/eas-app-stores/references/app-store-metadata.md +497 -0
  79. package/dist/global-skills/eas-app-stores/references/ios-app-store.md +371 -0
  80. package/dist/global-skills/eas-app-stores/references/play-store.md +246 -0
  81. package/dist/global-skills/eas-app-stores/references/testflight.md +58 -0
  82. package/dist/global-skills/eas-app-stores/references/workflows.md +120 -0
  83. package/dist/global-skills/eas-hosting/SKILL.md +423 -0
  84. package/dist/global-skills/eas-hosting/agents/openai.yaml +4 -0
  85. package/dist/global-skills/eas-observe/SKILL.md +32 -0
  86. package/dist/global-skills/eas-observe/agents/openai.yaml +4 -0
  87. package/dist/global-skills/eas-observe/references/metrics.md +47 -0
  88. package/dist/global-skills/eas-observe/references/queries.md +342 -0
  89. package/dist/global-skills/eas-observe/references/setup.md +317 -0
  90. package/dist/global-skills/eas-simulator/SKILL.md +156 -0
  91. package/dist/global-skills/eas-simulator/agents/openai.yaml +4 -0
  92. package/dist/global-skills/eas-simulator/references/controllers.md +67 -0
  93. package/dist/global-skills/eas-simulator/references/run-your-app.md +185 -0
  94. package/dist/global-skills/eas-simulator/references/troubleshooting.md +38 -0
  95. package/dist/global-skills/eas-update-insights/SKILL.md +230 -0
  96. package/dist/global-skills/eas-update-insights/agents/openai.yaml +4 -0
  97. package/dist/global-skills/eas-update-insights/references/channel-insights-schema.md +47 -0
  98. package/dist/global-skills/eas-update-insights/references/update-insights-schema.md +69 -0
  99. package/dist/global-skills/eas-workflows/SKILL.md +94 -0
  100. package/dist/global-skills/eas-workflows/agents/openai.yaml +4 -0
  101. package/dist/global-skills/eas-workflows/scripts/fetch.js +109 -0
  102. package/dist/global-skills/eas-workflows/scripts/package.json +11 -0
  103. package/dist/global-skills/eas-workflows/scripts/validate.js +84 -0
  104. package/dist/global-skills/email-best-practices/SKILL.md +73 -0
  105. package/dist/global-skills/expo-api-routes/SKILL.md +368 -0
  106. package/dist/global-skills/expo-api-routes/agents/openai.yaml +4 -0
  107. package/dist/global-skills/expo-app-clip/SKILL.md +282 -0
  108. package/dist/global-skills/expo-app-clip/agents/openai.yaml +4 -0
  109. package/dist/global-skills/expo-app-clip/references/native-module.md +96 -0
  110. package/dist/global-skills/expo-brownfield/SKILL.md +54 -0
  111. package/dist/global-skills/expo-brownfield/agents/openai.yaml +4 -0
  112. package/dist/global-skills/expo-brownfield/references/brownfield-integrated.md +526 -0
  113. package/dist/global-skills/expo-brownfield/references/brownfield-isolated.md +402 -0
  114. package/dist/global-skills/expo-brownfield/references/comparison.md +63 -0
  115. package/dist/global-skills/expo-brownfield/references/troubleshooting.md +88 -0
  116. package/dist/global-skills/expo-cicd-workflows/SKILL.md +92 -0
  117. package/dist/global-skills/expo-cicd-workflows/agents/openai.yaml +4 -0
  118. package/dist/global-skills/expo-cicd-workflows/scripts/fetch.js +109 -0
  119. package/dist/global-skills/expo-cicd-workflows/scripts/package.json +11 -0
  120. package/dist/global-skills/expo-cicd-workflows/scripts/validate.js +84 -0
  121. package/dist/global-skills/expo-data-fetching/SKILL.md +449 -0
  122. package/dist/global-skills/expo-data-fetching/agents/openai.yaml +4 -0
  123. package/dist/global-skills/expo-data-fetching/references/expo-router-loaders.md +341 -0
  124. package/dist/global-skills/expo-data-fetching/references/offline-and-cancellation.md +68 -0
  125. package/dist/global-skills/expo-deployment/SKILL.md +167 -0
  126. package/dist/global-skills/expo-deployment/agents/openai.yaml +4 -0
  127. package/dist/global-skills/expo-deployment/references/app-store-metadata.md +497 -0
  128. package/dist/global-skills/expo-deployment/references/ios-app-store.md +371 -0
  129. package/dist/global-skills/expo-deployment/references/play-store.md +246 -0
  130. package/dist/global-skills/expo-deployment/references/testflight.md +58 -0
  131. package/dist/global-skills/expo-deployment/references/workflows.md +160 -0
  132. package/dist/global-skills/expo-dev-client/SKILL.md +174 -0
  133. package/dist/global-skills/expo-dev-client/agents/openai.yaml +4 -0
  134. package/dist/global-skills/expo-dom/SKILL.md +417 -0
  135. package/dist/global-skills/expo-dom/agents/openai.yaml +4 -0
  136. package/dist/global-skills/expo-examples/SKILL.md +99 -0
  137. package/dist/global-skills/expo-examples/agents/openai.yaml +4 -0
  138. package/dist/global-skills/expo-examples/references/catalog.md +105 -0
  139. package/dist/global-skills/expo-module/SKILL.md +141 -0
  140. package/dist/global-skills/expo-module/agents/openai.yaml +4 -0
  141. package/dist/global-skills/expo-module/references/config-plugin.md +90 -0
  142. package/dist/global-skills/expo-module/references/create-expo-module.md +206 -0
  143. package/dist/global-skills/expo-module/references/lifecycle.md +127 -0
  144. package/dist/global-skills/expo-module/references/module-config.md +48 -0
  145. package/dist/global-skills/expo-module/references/native-module.md +286 -0
  146. package/dist/global-skills/expo-module/references/native-view.md +171 -0
  147. package/dist/global-skills/expo-native-ui/SKILL.md +181 -0
  148. package/dist/global-skills/expo-native-ui/agents/openai.yaml +4 -0
  149. package/dist/global-skills/expo-native-ui/references/animations.md +220 -0
  150. package/dist/global-skills/expo-native-ui/references/controls.md +270 -0
  151. package/dist/global-skills/expo-native-ui/references/gradients.md +106 -0
  152. package/dist/global-skills/expo-native-ui/references/icons.md +213 -0
  153. package/dist/global-skills/expo-native-ui/references/media.md +198 -0
  154. package/dist/global-skills/expo-native-ui/references/storage.md +121 -0
  155. package/dist/global-skills/expo-native-ui/references/visual-effects.md +197 -0
  156. package/dist/global-skills/expo-native-ui/references/webgpu-three.md +605 -0
  157. package/dist/global-skills/expo-observe/SKILL.md +30 -0
  158. package/dist/global-skills/expo-observe/references/metrics.md +47 -0
  159. package/dist/global-skills/expo-observe/references/queries.md +342 -0
  160. package/dist/global-skills/expo-observe/references/setup.md +317 -0
  161. package/dist/global-skills/expo-router/SKILL.md +230 -0
  162. package/dist/global-skills/expo-router/agents/openai.yaml +4 -0
  163. package/dist/global-skills/expo-router/references/form-sheet.md +253 -0
  164. package/dist/global-skills/expo-router/references/route-structure.md +229 -0
  165. package/dist/global-skills/expo-router/references/search.md +248 -0
  166. package/dist/global-skills/expo-router/references/tabs.md +433 -0
  167. package/dist/global-skills/expo-router/references/toolbar-and-headers.md +284 -0
  168. package/dist/global-skills/expo-router/references/zoom-transitions.md +158 -0
  169. package/dist/global-skills/expo-skill-eval/SKILL.md +314 -0
  170. package/dist/global-skills/expo-skill-eval/agents/visual-grader.md +52 -0
  171. package/dist/global-skills/expo-skill-eval/references/design-rubric.md +45 -0
  172. package/dist/global-skills/expo-skill-eval/references/runtime-matrix.md +34 -0
  173. package/dist/global-skills/expo-skill-eval/scripts/check-static.sh +64 -0
  174. package/dist/global-skills/expo-skill-eval/scripts/clean-fixture.sh +56 -0
  175. package/dist/global-skills/expo-skill-eval/scripts/generate_viewer.py +450 -0
  176. package/dist/global-skills/expo-skill-eval/scripts/latest-sdk.sh +46 -0
  177. package/dist/global-skills/expo-skill-eval/scripts/make-fixture.sh +95 -0
  178. package/dist/global-skills/expo-skill-eval/scripts/make-workspace.sh +31 -0
  179. package/dist/global-skills/expo-skill-eval/scripts/snapshot-android.sh +284 -0
  180. package/dist/global-skills/expo-skill-eval/scripts/snapshot-ios.sh +132 -0
  181. package/dist/global-skills/expo-skill-eval/scripts/snapshot-web.sh +61 -0
  182. package/dist/global-skills/expo-skill-feedback/SKILL.md +54 -0
  183. package/dist/global-skills/expo-skill-feedback/agents/openai.yaml +4 -0
  184. package/dist/global-skills/expo-skill-feedback/scripts/skill-event.cjs +180 -0
  185. package/dist/global-skills/expo-skill-feedback/scripts/skill-feedback.cjs +118 -0
  186. package/dist/global-skills/expo-skill-feedback/scripts/telemetry.cjs +65 -0
  187. package/dist/global-skills/expo-skill-feedback/scripts/telemetry_common.cjs +171 -0
  188. package/dist/global-skills/expo-tailwind-setup/SKILL.md +480 -0
  189. package/dist/global-skills/expo-tailwind-setup/agents/openai.yaml +4 -0
  190. package/dist/global-skills/expo-ui/SKILL.md +47 -0
  191. package/dist/global-skills/expo-ui/agents/openai.yaml +4 -0
  192. package/dist/global-skills/expo-ui/references/drop-in-replacements.md +27 -0
  193. package/dist/global-skills/expo-ui/references/jetpack-compose.md +73 -0
  194. package/dist/global-skills/expo-ui/references/swift-ui.md +73 -0
  195. package/dist/global-skills/expo-ui/references/universal.md +73 -0
  196. package/dist/global-skills/expo-ui/scripts/list-components.js +193 -0
  197. package/dist/global-skills/expo-upgrade/SKILL.md +134 -0
  198. package/dist/global-skills/expo-upgrade/agents/openai.yaml +4 -0
  199. package/dist/global-skills/expo-upgrade/references/expo-av-to-audio.md +132 -0
  200. package/dist/global-skills/expo-upgrade/references/expo-av-to-video.md +160 -0
  201. package/dist/global-skills/expo-upgrade/references/native-tabs.md +124 -0
  202. package/dist/global-skills/expo-upgrade/references/new-architecture.md +79 -0
  203. package/dist/global-skills/expo-upgrade/references/react-19.md +79 -0
  204. package/dist/global-skills/expo-upgrade/references/react-compiler.md +59 -0
  205. package/dist/global-skills/expo-upgrade/references/react-navigation-to-expo-router.md +61 -0
  206. package/dist/global-skills/expo-web-to-native/SKILL.md +83 -0
  207. package/dist/global-skills/expo-web-to-native/agents/openai.yaml +4 -0
  208. package/dist/global-skills/expo-web-to-native/references/false-friends.md +119 -0
  209. package/dist/global-skills/expo-web-to-native/references/native-patterns.md +39 -0
  210. package/dist/global-skills/expo-web-to-native/references/run-as-goal.md +48 -0
  211. package/dist/global-skills/expo-web-to-native/references/verify-on-device.md +45 -0
  212. package/dist/global-skills/firebase-ai-logic-basics/SKILL.md +165 -0
  213. package/dist/global-skills/firebase-ai-logic-basics/references/flutter_setup.md +95 -0
  214. package/dist/global-skills/firebase-ai-logic-basics/references/ios_setup.md +156 -0
  215. package/dist/global-skills/firebase-ai-logic-basics/references/usage_patterns_android.md +157 -0
  216. package/dist/global-skills/firebase-ai-logic-basics/references/usage_patterns_web.md +186 -0
  217. package/dist/global-skills/firebase-app-hosting-basics/SKILL.md +73 -0
  218. package/dist/global-skills/firebase-app-hosting-basics/references/cli_commands.md +85 -0
  219. package/dist/global-skills/firebase-app-hosting-basics/references/configuration.md +59 -0
  220. package/dist/global-skills/firebase-app-hosting-basics/references/emulation.md +59 -0
  221. package/dist/global-skills/firebase-auth-basics/SKILL.md +118 -0
  222. package/dist/global-skills/firebase-auth-basics/references/client_sdk_android.md +160 -0
  223. package/dist/global-skills/firebase-auth-basics/references/client_sdk_web.md +301 -0
  224. package/dist/global-skills/firebase-auth-basics/references/flutter_setup.md +149 -0
  225. package/dist/global-skills/firebase-auth-basics/references/ios_setup.md +87 -0
  226. package/dist/global-skills/firebase-auth-basics/references/security_rules.md +49 -0
  227. package/dist/global-skills/firebase-basics/SKILL.md +148 -0
  228. package/dist/global-skills/firebase-basics/references/android_setup.md +41 -0
  229. package/dist/global-skills/firebase-basics/references/firebase-cli-guide.md +18 -0
  230. package/dist/global-skills/firebase-basics/references/firebase-service-init.md +20 -0
  231. package/dist/global-skills/firebase-basics/references/flutter_setup.md +143 -0
  232. package/dist/global-skills/firebase-basics/references/ios_setup.md +113 -0
  233. package/dist/global-skills/firebase-basics/references/local-env-setup.md +80 -0
  234. package/dist/global-skills/firebase-basics/references/refresh/android_studio.md +41 -0
  235. package/dist/global-skills/firebase-basics/references/refresh/antigravity.md +64 -0
  236. package/dist/global-skills/firebase-basics/references/refresh/claude.md +12 -0
  237. package/dist/global-skills/firebase-basics/references/refresh/gemini-cli.md +13 -0
  238. package/dist/global-skills/firebase-basics/references/refresh/other-agents.md +67 -0
  239. package/dist/global-skills/firebase-basics/references/setup/android_studio.md +23 -0
  240. package/dist/global-skills/firebase-basics/references/setup/antigravity.md +98 -0
  241. package/dist/global-skills/firebase-basics/references/setup/claude_code.md +45 -0
  242. package/dist/global-skills/firebase-basics/references/setup/cursor.md +93 -0
  243. package/dist/global-skills/firebase-basics/references/setup/gemini_cli.md +53 -0
  244. package/dist/global-skills/firebase-basics/references/setup/github_copilot.md +104 -0
  245. package/dist/global-skills/firebase-basics/references/setup/other_agents.md +99 -0
  246. package/dist/global-skills/firebase-basics/references/web_setup.md +77 -0
  247. package/dist/global-skills/firebase-crashlytics/SKILL.md +42 -0
  248. package/dist/global-skills/firebase-crashlytics/references/android_setup.md +152 -0
  249. package/dist/global-skills/firebase-crashlytics/references/ios_setup.md +115 -0
  250. package/dist/global-skills/firebase-data-connect/SKILL.md +191 -0
  251. package/dist/global-skills/firebase-data-connect/examples.md +638 -0
  252. package/dist/global-skills/firebase-data-connect/reference/cloud_functions.md +184 -0
  253. package/dist/global-skills/firebase-data-connect/reference/config.md +271 -0
  254. package/dist/global-skills/firebase-data-connect/reference/data_seeding.md +185 -0
  255. package/dist/global-skills/firebase-data-connect/reference/native_sql.md +170 -0
  256. package/dist/global-skills/firebase-data-connect/reference/operations.md +385 -0
  257. package/dist/global-skills/firebase-data-connect/reference/realtime.md +210 -0
  258. package/dist/global-skills/firebase-data-connect/reference/schema.md +290 -0
  259. package/dist/global-skills/firebase-data-connect/reference/sdk_admin_node.md +141 -0
  260. package/dist/global-skills/firebase-data-connect/reference/sdk_android.md +126 -0
  261. package/dist/global-skills/firebase-data-connect/reference/sdk_flutter.md +134 -0
  262. package/dist/global-skills/firebase-data-connect/reference/sdk_ios.md +157 -0
  263. package/dist/global-skills/firebase-data-connect/reference/sdk_web.md +146 -0
  264. package/dist/global-skills/firebase-data-connect/reference/search.md +264 -0
  265. package/dist/global-skills/firebase-data-connect/reference/security.md +295 -0
  266. package/dist/global-skills/firebase-data-connect/templates.md +318 -0
  267. package/dist/global-skills/firebase-firestore/SKILL.md +89 -0
  268. package/dist/global-skills/firebase-firestore/references/enterprise/android_sdk_usage.md +230 -0
  269. package/dist/global-skills/firebase-firestore/references/enterprise/data_model.md +66 -0
  270. package/dist/global-skills/firebase-firestore/references/enterprise/flutter_setup.md +180 -0
  271. package/dist/global-skills/firebase-firestore/references/enterprise/indexes.md +135 -0
  272. package/dist/global-skills/firebase-firestore/references/enterprise/ios_setup.md +189 -0
  273. package/dist/global-skills/firebase-firestore/references/enterprise/provisioning.md +118 -0
  274. package/dist/global-skills/firebase-firestore/references/enterprise/python_sdk_usage.md +138 -0
  275. package/dist/global-skills/firebase-firestore/references/enterprise/security_rules.md +577 -0
  276. package/dist/global-skills/firebase-firestore/references/enterprise/web_sdk_usage.md +127 -0
  277. package/dist/global-skills/firebase-firestore/references/standard/android_sdk_usage.md +193 -0
  278. package/dist/global-skills/firebase-firestore/references/standard/flutter_setup.md +176 -0
  279. package/dist/global-skills/firebase-firestore/references/standard/indexes.md +113 -0
  280. package/dist/global-skills/firebase-firestore/references/standard/ios_setup.md +174 -0
  281. package/dist/global-skills/firebase-firestore/references/standard/provisioning.md +110 -0
  282. package/dist/global-skills/firebase-firestore/references/standard/security_rules.md +577 -0
  283. package/dist/global-skills/firebase-firestore/references/standard/web_sdk_usage.md +192 -0
  284. package/dist/global-skills/firebase-hosting-basics/SKILL.md +64 -0
  285. package/dist/global-skills/firebase-hosting-basics/references/configuration.md +115 -0
  286. package/dist/global-skills/firebase-hosting-basics/references/deploying.md +48 -0
  287. package/dist/global-skills/firebase-remote-config-basics/SKILL.md +124 -0
  288. package/dist/global-skills/firebase-remote-config-basics/references/android_setup.md +91 -0
  289. package/dist/global-skills/firebase-remote-config-basics/references/ios_setup.md +89 -0
  290. package/dist/global-skills/firebase-security-rules-auditor/SKILL.md +70 -0
  291. package/dist/global-skills/go-live/SKILL.md +90 -0
  292. package/dist/global-skills/harden/SKILL.md +101 -0
  293. package/dist/global-skills/instrument-feature-flags/SKILL.md +88 -0
  294. package/dist/global-skills/instrument-feature-flags/references/adding-feature-flag-code.md +3578 -0
  295. package/dist/global-skills/instrument-feature-flags/references/android.md +146 -0
  296. package/dist/global-skills/instrument-feature-flags/references/api.md +194 -0
  297. package/dist/global-skills/instrument-feature-flags/references/best-practices.md +231 -0
  298. package/dist/global-skills/instrument-feature-flags/references/django.md +263 -0
  299. package/dist/global-skills/instrument-feature-flags/references/dotnet.md +763 -0
  300. package/dist/global-skills/instrument-feature-flags/references/elixir.md +47 -0
  301. package/dist/global-skills/instrument-feature-flags/references/flask.md +131 -0
  302. package/dist/global-skills/instrument-feature-flags/references/flutter.md +212 -0
  303. package/dist/global-skills/instrument-feature-flags/references/go.md +208 -0
  304. package/dist/global-skills/instrument-feature-flags/references/ios.md +146 -0
  305. package/dist/global-skills/instrument-feature-flags/references/java.md +94 -0
  306. package/dist/global-skills/instrument-feature-flags/references/laravel.md +170 -0
  307. package/dist/global-skills/instrument-feature-flags/references/next-js.md +383 -0
  308. package/dist/global-skills/instrument-feature-flags/references/nodejs.md +216 -0
  309. package/dist/global-skills/instrument-feature-flags/references/php.md +187 -0
  310. package/dist/global-skills/instrument-feature-flags/references/python.md +191 -0
  311. package/dist/global-skills/instrument-feature-flags/references/react-native.md +166 -0
  312. package/dist/global-skills/instrument-feature-flags/references/react.md +302 -0
  313. package/dist/global-skills/instrument-feature-flags/references/ruby-on-rails.md +604 -0
  314. package/dist/global-skills/instrument-feature-flags/references/ruby.md +200 -0
  315. package/dist/global-skills/instrument-feature-flags/references/rust.md +222 -0
  316. package/dist/global-skills/instrument-feature-flags/references/usage.md +600 -0
  317. package/dist/global-skills/instrument-feature-flags/references/web.md +193 -0
  318. package/dist/global-skills/instrument-product-analytics/SKILL.md +139 -0
  319. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-android.md +1639 -0
  320. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-angular.md +944 -0
  321. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-astro-hybrid.md +1001 -0
  322. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-astro-ssr.md +1004 -0
  323. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-astro-static.md +720 -0
  324. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-astro-view-transitions.md +810 -0
  325. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-django.md +1167 -0
  326. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-expo.md +1412 -0
  327. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-fastapi.md +1585 -0
  328. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-flask.md +1208 -0
  329. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-laravel.md +2267 -0
  330. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-next-app-router.md +712 -0
  331. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-next-pages-router.md +767 -0
  332. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-nuxt-3-6.md +939 -0
  333. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-nuxt-4.md +1078 -0
  334. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-php.md +527 -0
  335. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-python.md +481 -0
  336. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-react-native.md +1823 -0
  337. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-react-react-router-6.md +582 -0
  338. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-react-react-router-7-data.md +892 -0
  339. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-react-react-router-7-declarative.md +649 -0
  340. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-react-react-router-7-framework.md +1218 -0
  341. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-react-tanstack-router-code-based.md +793 -0
  342. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-react-tanstack-router-file-based.md +780 -0
  343. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-ruby-on-rails.md +1320 -0
  344. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-ruby.md +452 -0
  345. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-sveltekit.md +854 -0
  346. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-swift.md +665 -0
  347. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-tanstack-start.md +1199 -0
  348. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-vue-3.md +910 -0
  349. package/dist/global-skills/instrument-product-analytics/references/android.md +811 -0
  350. package/dist/global-skills/instrument-product-analytics/references/angular.md +376 -0
  351. package/dist/global-skills/instrument-product-analytics/references/astro.md +165 -0
  352. package/dist/global-skills/instrument-product-analytics/references/configuration.md +299 -0
  353. package/dist/global-skills/instrument-product-analytics/references/django.md +263 -0
  354. package/dist/global-skills/instrument-product-analytics/references/dotnet.md +763 -0
  355. package/dist/global-skills/instrument-product-analytics/references/elixir.md +414 -0
  356. package/dist/global-skills/instrument-product-analytics/references/flask.md +131 -0
  357. package/dist/global-skills/instrument-product-analytics/references/flutter.md +845 -0
  358. package/dist/global-skills/instrument-product-analytics/references/go.md +567 -0
  359. package/dist/global-skills/instrument-product-analytics/references/identify-users.md +272 -0
  360. package/dist/global-skills/instrument-product-analytics/references/ios.md +154 -0
  361. package/dist/global-skills/instrument-product-analytics/references/laravel.md +170 -0
  362. package/dist/global-skills/instrument-product-analytics/references/next-js.md +383 -0
  363. package/dist/global-skills/instrument-product-analytics/references/nuxt-js-3-6.md +244 -0
  364. package/dist/global-skills/instrument-product-analytics/references/nuxt-js.md +253 -0
  365. package/dist/global-skills/instrument-product-analytics/references/php.md +646 -0
  366. package/dist/global-skills/instrument-product-analytics/references/posthog-python.md +1675 -0
  367. package/dist/global-skills/instrument-product-analytics/references/python.md +898 -0
  368. package/dist/global-skills/instrument-product-analytics/references/react-native.md +1338 -0
  369. package/dist/global-skills/instrument-product-analytics/references/react-router-v6.md +376 -0
  370. package/dist/global-skills/instrument-product-analytics/references/react-router-v7-data-mode.md +364 -0
  371. package/dist/global-skills/instrument-product-analytics/references/react-router-v7-declarative-mode.md +376 -0
  372. package/dist/global-skills/instrument-product-analytics/references/react-router-v7-framework-mode.md +479 -0
  373. package/dist/global-skills/instrument-product-analytics/references/ruby-on-rails.md +604 -0
  374. package/dist/global-skills/instrument-product-analytics/references/ruby.md +759 -0
  375. package/dist/global-skills/instrument-product-analytics/references/svelte.md +234 -0
  376. package/dist/global-skills/instrument-product-analytics/references/tanstack-start.md +193 -0
  377. package/dist/global-skills/instrument-product-analytics/references/usage.md +600 -0
  378. package/dist/global-skills/instrument-product-analytics/references/vue-js.md +306 -0
  379. package/dist/global-skills/manifest.json +125 -0
  380. package/dist/global-skills/mongodb-atlas-stream-processing/SKILL.md +284 -0
  381. package/dist/global-skills/mongodb-atlas-stream-processing/references/connection-configs.md +298 -0
  382. package/dist/global-skills/mongodb-atlas-stream-processing/references/development-workflow.md +304 -0
  383. package/dist/global-skills/mongodb-atlas-stream-processing/references/mcp-troubleshooting.md +55 -0
  384. package/dist/global-skills/mongodb-atlas-stream-processing/references/output-diagnostics.md +150 -0
  385. package/dist/global-skills/mongodb-atlas-stream-processing/references/pipeline-patterns.md +457 -0
  386. package/dist/global-skills/mongodb-atlas-stream-processing/references/sizing-and-parallelism.md +178 -0
  387. package/dist/global-skills/mongodb-connection/SKILL.md +201 -0
  388. package/dist/global-skills/mongodb-connection/references/monitoring-guide.md +191 -0
  389. package/dist/global-skills/mongodb-mcp-setup/SKILL.md +324 -0
  390. package/dist/global-skills/mongodb-natural-language-querying/SKILL.md +195 -0
  391. package/dist/global-skills/mongodb-query-optimizer/SKILL.md +150 -0
  392. package/dist/global-skills/mongodb-query-optimizer/references/aggregation-optimization.md +210 -0
  393. package/dist/global-skills/mongodb-query-optimizer/references/antipattern-examples.md +74 -0
  394. package/dist/global-skills/mongodb-query-optimizer/references/core-indexing-principles.md +134 -0
  395. package/dist/global-skills/mongodb-query-optimizer/references/update-query-examples.md +39 -0
  396. package/dist/global-skills/mongodb-schema-design/SKILL.md +181 -0
  397. package/dist/global-skills/mongodb-schema-design/references/antipattern-excessive-lookups.md +84 -0
  398. package/dist/global-skills/mongodb-schema-design/references/antipattern-unnecessary-collections.md +91 -0
  399. package/dist/global-skills/mongodb-schema-design/references/antipattern-unnecessary-indexes.md +95 -0
  400. package/dist/global-skills/mongodb-schema-design/references/fundamental-document-model.md +91 -0
  401. package/dist/global-skills/mongodb-schema-design/references/fundamental-document-size.md +226 -0
  402. package/dist/global-skills/mongodb-schema-design/references/fundamental-embed-vs-reference.md +418 -0
  403. package/dist/global-skills/mongodb-schema-design/references/fundamental-schema-validation.md +131 -0
  404. package/dist/global-skills/mongodb-schema-design/references/pattern-approximation.md +80 -0
  405. package/dist/global-skills/mongodb-schema-design/references/pattern-archive.md +143 -0
  406. package/dist/global-skills/mongodb-schema-design/references/pattern-attribute.md +77 -0
  407. package/dist/global-skills/mongodb-schema-design/references/pattern-bucket.md +102 -0
  408. package/dist/global-skills/mongodb-schema-design/references/pattern-computed.md +162 -0
  409. package/dist/global-skills/mongodb-schema-design/references/pattern-document-versioning.md +166 -0
  410. package/dist/global-skills/mongodb-schema-design/references/pattern-extended-reference.md +72 -0
  411. package/dist/global-skills/mongodb-schema-design/references/pattern-outlier.md +159 -0
  412. package/dist/global-skills/mongodb-schema-design/references/pattern-polymorphic.md +167 -0
  413. package/dist/global-skills/mongodb-schema-design/references/pattern-schema-versioning.md +273 -0
  414. package/dist/global-skills/mongodb-schema-design/references/pattern-time-series-collections.md +190 -0
  415. package/dist/global-skills/mongodb-schema-design/references/source-query-stats.md +123 -0
  416. package/dist/global-skills/mongodb-schema-design/references/source-slow-query-logs.md +63 -0
  417. package/dist/global-skills/mongodb-search-and-ai/SKILL.md +142 -0
  418. package/dist/global-skills/mongodb-search-and-ai/references/hybrid-search.md +697 -0
  419. package/dist/global-skills/mongodb-search-and-ai/references/lexical-search-indexing.md +584 -0
  420. package/dist/global-skills/mongodb-search-and-ai/references/lexical-search-querying.md +636 -0
  421. package/dist/global-skills/mongodb-search-and-ai/references/vector-search.md +746 -0
  422. package/dist/global-skills/native-data-fetching/SKILL.md +505 -0
  423. package/dist/global-skills/native-data-fetching/agents/openai.yaml +4 -0
  424. package/dist/global-skills/native-data-fetching/references/expo-router-loaders.md +341 -0
  425. package/dist/global-skills/neon/SKILL.md +343 -0
  426. package/dist/global-skills/neon-ai-gateway/SKILL.md +236 -0
  427. package/dist/global-skills/neon-functions/SKILL.md +490 -0
  428. package/dist/global-skills/neon-functions/references/ai-sdk.md +139 -0
  429. package/dist/global-skills/neon-functions/references/hono-websockets.md +145 -0
  430. package/dist/global-skills/neon-functions/references/mastra-studio.md +127 -0
  431. package/dist/global-skills/neon-functions/references/mcp.md +137 -0
  432. package/dist/global-skills/neon-functions/references/sentry.md +131 -0
  433. package/dist/global-skills/neon-functions/references/sse.md +143 -0
  434. package/dist/global-skills/neon-object-storage/SKILL.md +197 -0
  435. package/dist/global-skills/neon-postgres/SKILL.md +376 -0
  436. package/dist/global-skills/neon-postgres-branches/SKILL.md +281 -0
  437. package/dist/global-skills/neon-postgres-egress-optimizer/SKILL.md +247 -0
  438. package/dist/global-skills/onboarding/SKILL.md +101 -0
  439. package/dist/global-skills/plan-my-idea/SKILL.md +67 -0
  440. package/dist/global-skills/publish-app/SKILL.md +72 -0
  441. package/dist/global-skills/publish-extension/SKILL.md +47 -0
  442. package/dist/global-skills/resend/SKILL.md +294 -0
  443. package/dist/global-skills/resend/references/api-keys.md +98 -0
  444. package/dist/global-skills/resend/references/automations.md +228 -0
  445. package/dist/global-skills/resend/references/broadcasts.md +125 -0
  446. package/dist/global-skills/resend/references/contact-properties.md +111 -0
  447. package/dist/global-skills/resend/references/contacts.md +174 -0
  448. package/dist/global-skills/resend/references/domains.md +179 -0
  449. package/dist/global-skills/resend/references/events.md +119 -0
  450. package/dist/global-skills/resend/references/fetch-all-templates.mjs +37 -0
  451. package/dist/global-skills/resend/references/installation.md +142 -0
  452. package/dist/global-skills/resend/references/logs.md +161 -0
  453. package/dist/global-skills/resend/references/receiving.md +287 -0
  454. package/dist/global-skills/resend/references/segments.md +77 -0
  455. package/dist/global-skills/resend/references/sending/batch-email-examples.md +807 -0
  456. package/dist/global-skills/resend/references/sending/best-practices.md +453 -0
  457. package/dist/global-skills/resend/references/sending/email-management.md +135 -0
  458. package/dist/global-skills/resend/references/sending/overview.md +209 -0
  459. package/dist/global-skills/resend/references/sending/single-email-examples.md +470 -0
  460. package/dist/global-skills/resend/references/templates.md +202 -0
  461. package/dist/global-skills/resend/references/topics.md +104 -0
  462. package/dist/global-skills/resend/references/webhooks.md +317 -0
  463. package/dist/global-skills/reset-password/SKILL.md +59 -0
  464. package/dist/global-skills/save-data/SKILL.md +70 -0
  465. package/dist/global-skills/sentry-sdk-setup/SKILL.md +116 -0
  466. package/dist/global-skills/sentry-workflow/SKILL.md +32 -0
  467. package/dist/global-skills/setup-email/SKILL.md +69 -0
  468. package/dist/global-skills/setup-payments/SKILL.md +82 -0
  469. package/dist/global-skills/setup-sms/SKILL.md +51 -0
  470. package/dist/global-skills/sign-in-with-email-link/SKILL.md +53 -0
  471. package/dist/global-skills/sign-in-with-google/SKILL.md +68 -0
  472. package/dist/global-skills/sign-in-with-phone/SKILL.md +53 -0
  473. package/dist/global-skills/stripe-best-practices/SKILL.md +48 -0
  474. package/dist/global-skills/stripe-best-practices/references/billing.md +58 -0
  475. package/dist/global-skills/stripe-best-practices/references/connect.md +173 -0
  476. package/dist/global-skills/stripe-best-practices/references/payments.md +79 -0
  477. package/dist/global-skills/stripe-best-practices/references/security.md +109 -0
  478. package/dist/global-skills/stripe-best-practices/references/tax.md +37 -0
  479. package/dist/global-skills/stripe-best-practices/references/treasury.md +16 -0
  480. package/dist/global-skills/supabase/CHANGELOG.md +53 -0
  481. package/dist/global-skills/supabase/SKILL.md +145 -0
  482. package/dist/global-skills/supabase/assets/feedback-issue-template.md +17 -0
  483. package/dist/global-skills/supabase/references/skill-feedback.md +17 -0
  484. package/dist/global-skills/supabase-postgres-best-practices/CHANGELOG.md +43 -0
  485. package/dist/global-skills/supabase-postgres-best-practices/SKILL.md +64 -0
  486. package/dist/global-skills/supabase-postgres-best-practices/references/_contributing.md +170 -0
  487. package/dist/global-skills/supabase-postgres-best-practices/references/_sections.md +39 -0
  488. package/dist/global-skills/supabase-postgres-best-practices/references/_template.md +34 -0
  489. package/dist/global-skills/supabase-postgres-best-practices/references/advanced-full-text-search.md +55 -0
  490. package/dist/global-skills/supabase-postgres-best-practices/references/advanced-jsonb-indexing.md +49 -0
  491. package/dist/global-skills/supabase-postgres-best-practices/references/conn-idle-timeout.md +46 -0
  492. package/dist/global-skills/supabase-postgres-best-practices/references/conn-limits.md +44 -0
  493. package/dist/global-skills/supabase-postgres-best-practices/references/conn-pooling.md +41 -0
  494. package/dist/global-skills/supabase-postgres-best-practices/references/conn-prepared-statements.md +46 -0
  495. package/dist/global-skills/supabase-postgres-best-practices/references/data-batch-inserts.md +54 -0
  496. package/dist/global-skills/supabase-postgres-best-practices/references/data-n-plus-one.md +53 -0
  497. package/dist/global-skills/supabase-postgres-best-practices/references/data-pagination.md +50 -0
  498. package/dist/global-skills/supabase-postgres-best-practices/references/data-upsert.md +50 -0
  499. package/dist/global-skills/supabase-postgres-best-practices/references/lock-advisory.md +56 -0
  500. package/dist/global-skills/supabase-postgres-best-practices/references/lock-deadlock-prevention.md +68 -0
  501. package/dist/global-skills/supabase-postgres-best-practices/references/lock-short-transactions.md +50 -0
  502. package/dist/global-skills/supabase-postgres-best-practices/references/lock-skip-locked.md +54 -0
  503. package/dist/global-skills/supabase-postgres-best-practices/references/monitor-explain-analyze.md +45 -0
  504. package/dist/global-skills/supabase-postgres-best-practices/references/monitor-pg-stat-statements.md +55 -0
  505. package/dist/global-skills/supabase-postgres-best-practices/references/monitor-vacuum-analyze.md +55 -0
  506. package/dist/global-skills/supabase-postgres-best-practices/references/query-composite-indexes.md +44 -0
  507. package/dist/global-skills/supabase-postgres-best-practices/references/query-covering-indexes.md +40 -0
  508. package/dist/global-skills/supabase-postgres-best-practices/references/query-index-types.md +48 -0
  509. package/dist/global-skills/supabase-postgres-best-practices/references/query-missing-indexes.md +43 -0
  510. package/dist/global-skills/supabase-postgres-best-practices/references/query-partial-indexes.md +45 -0
  511. package/dist/global-skills/supabase-postgres-best-practices/references/schema-constraints.md +80 -0
  512. package/dist/global-skills/supabase-postgres-best-practices/references/schema-data-types.md +46 -0
  513. package/dist/global-skills/supabase-postgres-best-practices/references/schema-foreign-key-indexes.md +59 -0
  514. package/dist/global-skills/supabase-postgres-best-practices/references/schema-lowercase-identifiers.md +55 -0
  515. package/dist/global-skills/supabase-postgres-best-practices/references/schema-partitioning.md +55 -0
  516. package/dist/global-skills/supabase-postgres-best-practices/references/schema-primary-keys.md +61 -0
  517. package/dist/global-skills/supabase-postgres-best-practices/references/security-privileges.md +54 -0
  518. package/dist/global-skills/supabase-postgres-best-practices/references/security-rls-basics.md +50 -0
  519. package/dist/global-skills/supabase-postgres-best-practices/references/security-rls-performance.md +63 -0
  520. package/dist/global-skills/track-errors/SKILL.md +67 -0
  521. package/dist/global-skills/update-kit/SKILL.md +75 -0
  522. package/dist/global-skills/upgrading-expo/SKILL.md +134 -0
  523. package/dist/global-skills/upgrading-expo/agents/openai.yaml +4 -0
  524. package/dist/global-skills/upgrading-expo/references/expo-av-to-audio.md +132 -0
  525. package/dist/global-skills/upgrading-expo/references/expo-av-to-video.md +160 -0
  526. package/dist/global-skills/upgrading-expo/references/native-tabs.md +124 -0
  527. package/dist/global-skills/upgrading-expo/references/new-architecture.md +79 -0
  528. package/dist/global-skills/upgrading-expo/references/react-19.md +79 -0
  529. package/dist/global-skills/upgrading-expo/references/react-compiler.md +59 -0
  530. package/dist/global-skills/upgrading-expo/references/react-navigation-to-expo-router.md +61 -0
  531. package/dist/global-skills/use-dom/SKILL.md +417 -0
  532. package/dist/global-skills/use-dom/agents/openai.yaml +4 -0
  533. package/dist/global-skills/use-kit-mcp/SKILL.md +100 -0
  534. package/dist/global-skills/use-railway/SKILL.md +357 -0
  535. package/dist/global-skills/use-railway/references/analyze-db-mongo.md +84 -0
  536. package/dist/global-skills/use-railway/references/analyze-db-mysql.md +254 -0
  537. package/dist/global-skills/use-railway/references/analyze-db-postgres.md +479 -0
  538. package/dist/global-skills/use-railway/references/analyze-db-redis.md +208 -0
  539. package/dist/global-skills/use-railway/references/analyze-db.md +344 -0
  540. package/dist/global-skills/use-railway/references/configure.md +359 -0
  541. package/dist/global-skills/use-railway/references/deploy.md +225 -0
  542. package/dist/global-skills/use-railway/references/feature-flags.md +89 -0
  543. package/dist/global-skills/use-railway/references/iac.md +108 -0
  544. package/dist/global-skills/use-railway/references/operate.md +273 -0
  545. package/dist/global-skills/use-railway/references/request.md +262 -0
  546. package/dist/global-skills/use-railway/references/sandbox.md +167 -0
  547. package/dist/global-skills/use-railway/references/setup.md +364 -0
  548. package/dist/global-skills/use-railway/scripts/analyze-mongo.py +1549 -0
  549. package/dist/global-skills/use-railway/scripts/analyze-mysql.py +1195 -0
  550. package/dist/global-skills/use-railway/scripts/analyze-postgres.py +3058 -0
  551. package/dist/global-skills/use-railway/scripts/analyze-redis.py +1090 -0
  552. package/dist/global-skills/use-railway/scripts/dal.py +671 -0
  553. package/dist/global-skills/use-railway/scripts/enable-pg-stats.py +170 -0
  554. package/dist/global-skills/use-railway/scripts/pg-extensions.py +370 -0
  555. package/dist/global-skills/use-railway/scripts/railway-api.sh +52 -0
  556. package/dist/global-skills/vercel-composition-patterns/AGENTS.md +946 -0
  557. package/dist/global-skills/vercel-composition-patterns/README.md +60 -0
  558. package/dist/global-skills/vercel-composition-patterns/SKILL.md +89 -0
  559. package/dist/global-skills/vercel-composition-patterns/metadata.json +11 -0
  560. package/dist/global-skills/vercel-composition-patterns/rules/_sections.md +29 -0
  561. package/dist/global-skills/vercel-composition-patterns/rules/_template.md +24 -0
  562. package/dist/global-skills/vercel-composition-patterns/rules/architecture-avoid-boolean-props.md +100 -0
  563. package/dist/global-skills/vercel-composition-patterns/rules/architecture-compound-components.md +112 -0
  564. package/dist/global-skills/vercel-composition-patterns/rules/patterns-children-over-render-props.md +87 -0
  565. package/dist/global-skills/vercel-composition-patterns/rules/patterns-explicit-variants.md +100 -0
  566. package/dist/global-skills/vercel-composition-patterns/rules/react19-no-forwardref.md +42 -0
  567. package/dist/global-skills/vercel-composition-patterns/rules/state-context-interface.md +191 -0
  568. package/dist/global-skills/vercel-composition-patterns/rules/state-decouple-implementation.md +113 -0
  569. package/dist/global-skills/vercel-composition-patterns/rules/state-lift-state.md +125 -0
  570. package/dist/global-skills/vercel-react-best-practices/AGENTS.md +3810 -0
  571. package/dist/global-skills/vercel-react-best-practices/README.md +123 -0
  572. package/dist/global-skills/vercel-react-best-practices/SKILL.md +149 -0
  573. package/dist/global-skills/vercel-react-best-practices/metadata.json +15 -0
  574. package/dist/global-skills/vercel-react-best-practices/rules/_sections.md +46 -0
  575. package/dist/global-skills/vercel-react-best-practices/rules/_template.md +28 -0
  576. package/dist/global-skills/vercel-react-best-practices/rules/advanced-effect-event-deps.md +56 -0
  577. package/dist/global-skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  578. package/dist/global-skills/vercel-react-best-practices/rules/advanced-init-once.md +42 -0
  579. package/dist/global-skills/vercel-react-best-practices/rules/advanced-use-latest.md +39 -0
  580. package/dist/global-skills/vercel-react-best-practices/rules/async-api-routes.md +38 -0
  581. package/dist/global-skills/vercel-react-best-practices/rules/async-cheap-condition-before-await.md +37 -0
  582. package/dist/global-skills/vercel-react-best-practices/rules/async-defer-await.md +82 -0
  583. package/dist/global-skills/vercel-react-best-practices/rules/async-dependencies.md +51 -0
  584. package/dist/global-skills/vercel-react-best-practices/rules/async-parallel.md +28 -0
  585. package/dist/global-skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +99 -0
  586. package/dist/global-skills/vercel-react-best-practices/rules/bundle-analyzable-paths.md +63 -0
  587. package/dist/global-skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +60 -0
  588. package/dist/global-skills/vercel-react-best-practices/rules/bundle-conditional.md +31 -0
  589. package/dist/global-skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +49 -0
  590. package/dist/global-skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +35 -0
  591. package/dist/global-skills/vercel-react-best-practices/rules/bundle-preload.md +50 -0
  592. package/dist/global-skills/vercel-react-best-practices/rules/client-event-listeners.md +74 -0
  593. package/dist/global-skills/vercel-react-best-practices/rules/client-localstorage-schema.md +71 -0
  594. package/dist/global-skills/vercel-react-best-practices/rules/client-passive-event-listeners.md +48 -0
  595. package/dist/global-skills/vercel-react-best-practices/rules/client-swr-dedup.md +56 -0
  596. package/dist/global-skills/vercel-react-best-practices/rules/js-batch-dom-css.md +107 -0
  597. package/dist/global-skills/vercel-react-best-practices/rules/js-cache-function-results.md +80 -0
  598. package/dist/global-skills/vercel-react-best-practices/rules/js-cache-property-access.md +28 -0
  599. package/dist/global-skills/vercel-react-best-practices/rules/js-cache-storage.md +70 -0
  600. package/dist/global-skills/vercel-react-best-practices/rules/js-combine-iterations.md +32 -0
  601. package/dist/global-skills/vercel-react-best-practices/rules/js-early-exit.md +50 -0
  602. package/dist/global-skills/vercel-react-best-practices/rules/js-flatmap-filter.md +60 -0
  603. package/dist/global-skills/vercel-react-best-practices/rules/js-hoist-regexp.md +45 -0
  604. package/dist/global-skills/vercel-react-best-practices/rules/js-index-maps.md +37 -0
  605. package/dist/global-skills/vercel-react-best-practices/rules/js-length-check-first.md +49 -0
  606. package/dist/global-skills/vercel-react-best-practices/rules/js-min-max-loop.md +82 -0
  607. package/dist/global-skills/vercel-react-best-practices/rules/js-request-idle-callback.md +105 -0
  608. package/dist/global-skills/vercel-react-best-practices/rules/js-set-map-lookups.md +24 -0
  609. package/dist/global-skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +57 -0
  610. package/dist/global-skills/vercel-react-best-practices/rules/rendering-activity.md +26 -0
  611. package/dist/global-skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
  612. package/dist/global-skills/vercel-react-best-practices/rules/rendering-conditional-render.md +40 -0
  613. package/dist/global-skills/vercel-react-best-practices/rules/rendering-content-visibility.md +38 -0
  614. package/dist/global-skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +46 -0
  615. package/dist/global-skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
  616. package/dist/global-skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
  617. package/dist/global-skills/vercel-react-best-practices/rules/rendering-resource-hints.md +85 -0
  618. package/dist/global-skills/vercel-react-best-practices/rules/rendering-script-defer-async.md +68 -0
  619. package/dist/global-skills/vercel-react-best-practices/rules/rendering-svg-precision.md +28 -0
  620. package/dist/global-skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md +75 -0
  621. package/dist/global-skills/vercel-react-best-practices/rules/rerender-defer-reads.md +39 -0
  622. package/dist/global-skills/vercel-react-best-practices/rules/rerender-dependencies.md +45 -0
  623. package/dist/global-skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
  624. package/dist/global-skills/vercel-react-best-practices/rules/rerender-derived-state.md +29 -0
  625. package/dist/global-skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +74 -0
  626. package/dist/global-skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +58 -0
  627. package/dist/global-skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
  628. package/dist/global-skills/vercel-react-best-practices/rules/rerender-memo.md +44 -0
  629. package/dist/global-skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
  630. package/dist/global-skills/vercel-react-best-practices/rules/rerender-no-inline-components.md +82 -0
  631. package/dist/global-skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
  632. package/dist/global-skills/vercel-react-best-practices/rules/rerender-split-combined-hooks.md +64 -0
  633. package/dist/global-skills/vercel-react-best-practices/rules/rerender-transitions.md +40 -0
  634. package/dist/global-skills/vercel-react-best-practices/rules/rerender-use-deferred-value.md +59 -0
  635. package/dist/global-skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
  636. package/dist/global-skills/vercel-react-best-practices/rules/server-after-nonblocking.md +73 -0
  637. package/dist/global-skills/vercel-react-best-practices/rules/server-auth-actions.md +96 -0
  638. package/dist/global-skills/vercel-react-best-practices/rules/server-cache-lru.md +41 -0
  639. package/dist/global-skills/vercel-react-best-practices/rules/server-cache-react.md +76 -0
  640. package/dist/global-skills/vercel-react-best-practices/rules/server-dedup-props.md +65 -0
  641. package/dist/global-skills/vercel-react-best-practices/rules/server-hoist-static-io.md +149 -0
  642. package/dist/global-skills/vercel-react-best-practices/rules/server-no-shared-module-state.md +50 -0
  643. package/dist/global-skills/vercel-react-best-practices/rules/server-parallel-fetching.md +83 -0
  644. package/dist/global-skills/vercel-react-best-practices/rules/server-parallel-nested-fetching.md +34 -0
  645. package/dist/global-skills/vercel-react-best-practices/rules/server-serialization.md +38 -0
  646. package/dist/global-skills/web-to-native/SKILL.md +83 -0
  647. package/dist/global-skills/web-to-native/references/false-friends.md +119 -0
  648. package/dist/global-skills/web-to-native/references/native-patterns.md +39 -0
  649. package/dist/global-skills/web-to-native/references/run-as-goal.md +48 -0
  650. package/dist/global-skills/web-to-native/references/verify-on-device.md +45 -0
  651. package/dist/global-skills/wire-auth/SKILL.md +28 -0
  652. package/dist/global-skills/wire-database/SKILL.md +28 -0
  653. package/dist/global-skills/wire-email/SKILL.md +27 -0
  654. package/dist/global-skills/wire-payments/SKILL.md +27 -0
  655. package/dist/global-skills/workers-best-practices/SKILL.md +127 -0
  656. package/dist/global-skills/workers-best-practices/references/review.md +174 -0
  657. package/dist/global-skills/workers-best-practices/references/rules.md +463 -0
  658. package/dist/global-skills/wrangler/SKILL.md +922 -0
  659. package/dist/global-skills/xcode-project-setup/SKILL.md +140 -0
  660. package/dist/global-skills/xcode-project-setup/scripts/xcode_spm_setup/Package.resolved +41 -0
  661. package/dist/global-skills/xcode-project-setup/scripts/xcode_spm_setup/Package.swift +17 -0
  662. package/dist/global-skills/xcode-project-setup/scripts/xcode_spm_setup/Sources/main.swift +232 -0
  663. package/package.json +21 -20
  664. package/LICENSE.md +0 -47
  665. package/dist/index.js +0 -21593
@@ -0,0 +1,1195 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ MySQL analysis for Railway deployments.
4
+
5
+ Produces a comprehensive report covering:
6
+ - Deployment status & resource metrics (CPU, memory, disk)
7
+ - Connection overview
8
+ - Query throughput & efficiency
9
+ - InnoDB buffer pool & row operations
10
+ - Lock contention
11
+ - Top queries (from performance_schema)
12
+ - Table sizes
13
+ - Active processes
14
+ - Recommendations
15
+
16
+ Usage:
17
+ analyze-mysql.py --service <name>
18
+ analyze-mysql.py --service <name> --json
19
+ analyze-mysql.py --service <name> --step ssh-test
20
+ """
21
+
22
+ import argparse
23
+ import json
24
+ import os
25
+ import re
26
+ import subprocess
27
+ import sys
28
+ from concurrent.futures import ThreadPoolExecutor
29
+ from dataclasses import asdict, dataclass, field
30
+ from datetime import datetime, timezone
31
+ from typing import Any, Dict, List, Optional, Tuple
32
+
33
+ import dal
34
+ from dal import (
35
+ LOG_LINES_DEFAULT, ProgressTimer, RailwayContext,
36
+ _init_context, progress, run_railway_command, run_ssh_query,
37
+ get_railway_status, get_deployment_status,
38
+ get_all_metrics_from_api, _analyze_window, _build_metrics_history,
39
+ get_recent_logs,
40
+ _safe_int, _safe_float, _format_uptime, _trend_indicator,
41
+ )
42
+
43
+
44
+ # ---------------------------------------------------------------------------
45
+ # Result container
46
+ # ---------------------------------------------------------------------------
47
+
48
+ @dataclass
49
+ class MySQLAnalysisResult:
50
+ """Container for MySQL analysis results."""
51
+ service: str
52
+ db_type: str
53
+ timestamp: str
54
+ deployment_status: str = "UNKNOWN"
55
+
56
+ # Resource metrics from Railway API
57
+ disk_usage: Optional[Dict[str, Any]] = None
58
+ cpu_memory: Optional[Dict[str, Any]] = None
59
+ metrics_history: Optional[Dict[str, Any]] = None
60
+
61
+ # MySQL data
62
+ overview: Optional[Dict[str, Any]] = None
63
+ query_throughput: Optional[Dict[str, Any]] = None
64
+ innodb_row_ops: Optional[Dict[str, Any]] = None
65
+ query_efficiency: Optional[Dict[str, Any]] = None
66
+ innodb_buffer_pool: Optional[Dict[str, Any]] = None
67
+ innodb_io: Optional[Dict[str, Any]] = None
68
+ network: Optional[Dict[str, Any]] = None
69
+ locks: Optional[Dict[str, Any]] = None
70
+ table_cache: Optional[Dict[str, Any]] = None
71
+ top_queries: List[Dict[str, Any]] = field(default_factory=list)
72
+ top_queries_status: Optional[str] = None
73
+ tables: List[Dict[str, Any]] = field(default_factory=list)
74
+ active_processes: List[Dict[str, Any]] = field(default_factory=list)
75
+
76
+ # Logs
77
+ recent_logs: List[str] = field(default_factory=list)
78
+ recent_errors: List[str] = field(default_factory=list)
79
+
80
+ # Metadata
81
+ collection_status: Dict[str, Dict[str, Any]] = field(default_factory=dict)
82
+ errors: List[str] = field(default_factory=list)
83
+ recommendations: List[Dict[str, str]] = field(default_factory=list)
84
+
85
+
86
+ # ---------------------------------------------------------------------------
87
+ # MySQL-specific helpers
88
+ # ---------------------------------------------------------------------------
89
+
90
+ def run_mysql_query(service: str, query: str, timeout: int = 30) -> Tuple[int, str]:
91
+ """Run a MySQL query via SSH and return (returncode, output).
92
+
93
+ Uses -B (batch) mode which produces tab-separated output with headers.
94
+ Filters out the mysql CLI password warning.
95
+ """
96
+ import base64
97
+ query = " ".join(query.split())
98
+ # Base64-encode the query to avoid all shell quoting issues
99
+ # (single quotes in SQL IN clauses break bash -c '...' wrapping)
100
+ encoded = base64.b64encode(query.encode()).decode()
101
+ command = (
102
+ f'''bash +H -c 'echo {encoded} | base64 -d | MYSQL_PWD="$MYSQLPASSWORD" mysql -h localhost -P 3306 '''
103
+ f'''-u "$MYSQLUSER" -D "$MYSQLDATABASE" --default-character-set=utf8mb4 '''
104
+ f'''-B' '''
105
+ )
106
+ code, stdout, stderr = run_ssh_query(service, command, timeout)
107
+ # Filter out the password warning from stdout (mysql sometimes writes it there)
108
+ lines = []
109
+ for line in stdout.split("\n"):
110
+ if "Using a password on the command line" in line:
111
+ continue
112
+ lines.append(line)
113
+ stdout = "\n".join(lines)
114
+ if code != 0:
115
+ # Also filter warning from stderr
116
+ stderr_clean = "\n".join(
117
+ l for l in stderr.split("\n")
118
+ if "Using a password on the command line" not in l
119
+ )
120
+ return code, stderr_clean or stdout
121
+ return 0, stdout
122
+
123
+
124
+ def parse_mysql_batch(output: str) -> List[Dict[str, str]]:
125
+ """Parse MySQL -B (batch/tab-separated) output into list of dicts.
126
+
127
+ First line is column headers, subsequent lines are values.
128
+ """
129
+ lines = [l for l in output.strip().split("\n") if l.strip()]
130
+ if len(lines) < 1:
131
+ return []
132
+ headers = lines[0].split("\t")
133
+ rows = []
134
+ for line in lines[1:]:
135
+ values = line.split("\t")
136
+ if len(values) == len(headers):
137
+ rows.append(dict(zip(headers, values)))
138
+ return rows
139
+
140
+
141
+ def parse_mysql_kv(output: str) -> Dict[str, str]:
142
+ """Parse MySQL SHOW output (Variable_name / Value pairs) into a dict."""
143
+ rows = parse_mysql_batch(output)
144
+ result: Dict[str, str] = {}
145
+ for row in rows:
146
+ name = row.get("Variable_name", "")
147
+ value = row.get("Value", "")
148
+ if name:
149
+ result[name] = value
150
+ return result
151
+
152
+
153
+ # ---------------------------------------------------------------------------
154
+ # Railway context / status / metrics helpers
155
+ # ---------------------------------------------------------------------------
156
+
157
+ # ---------------------------------------------------------------------------
158
+ # MySQL queries
159
+ # ---------------------------------------------------------------------------
160
+
161
+ QUERY_GLOBAL_STATUS = """SHOW GLOBAL STATUS WHERE Variable_name IN ('Threads_connected','Threads_running','Max_used_connections','Questions','Slow_queries','Com_select','Com_insert','Com_update','Com_delete','Innodb_buffer_pool_read_requests','Innodb_buffer_pool_reads','Innodb_buffer_pool_pages_data','Innodb_buffer_pool_pages_free','Innodb_buffer_pool_pages_dirty','Innodb_row_lock_waits','Innodb_row_lock_time','Uptime','Bytes_received','Bytes_sent','Connections','Aborted_clients','Aborted_connects','Innodb_rows_read','Innodb_rows_inserted','Innodb_rows_updated','Innodb_rows_deleted','Innodb_data_reads','Innodb_data_writes','Innodb_buffer_pool_bytes_data','Innodb_buffer_pool_bytes_dirty','Created_tmp_disk_tables','Created_tmp_tables','Handler_read_rnd_next','Handler_read_first','Handler_read_key','Select_full_join','Select_range','Sort_merge_passes','Table_locks_waited','Table_locks_immediate','Open_tables','Opened_tables')"""
162
+
163
+ QUERY_VARIABLES = """SHOW VARIABLES WHERE Variable_name IN ('max_connections','innodb_buffer_pool_size','long_query_time','version','table_open_cache','performance_schema')"""
164
+
165
+ QUERY_TABLE_SIZES = """SELECT TABLE_NAME, TABLE_ROWS, DATA_LENGTH, INDEX_LENGTH, DATA_LENGTH + INDEX_LENGTH AS TOTAL_SIZE FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() ORDER BY TOTAL_SIZE DESC LIMIT 15"""
166
+
167
+ QUERY_PROCESSLIST = """SHOW PROCESSLIST"""
168
+
169
+ QUERY_TOP_QUERIES = """SELECT DIGEST, LEFT(DIGEST_TEXT, 200) AS DIGEST_TEXT, COUNT_STAR, ROUND(SUM_TIMER_WAIT/1000000000, 2) AS TOTAL_LATENCY_MS, ROUND(AVG_TIMER_WAIT/1000000000, 2) AS AVG_LATENCY_MS, SUM_ROWS_EXAMINED, SUM_ROWS_SENT, SUM_CREATED_TMP_DISK_TABLES, SUM_NO_INDEX_USED FROM performance_schema.events_statements_summary_by_digest WHERE DIGEST IS NOT NULL AND COUNT_STAR > 0 ORDER BY SUM_TIMER_WAIT DESC LIMIT 15"""
170
+
171
+
172
+ # ---------------------------------------------------------------------------
173
+ # MySQL data collection
174
+ # ---------------------------------------------------------------------------
175
+
176
+ def collect_mysql_data(service: str, timeout: int = 30) -> Dict[str, Any]:
177
+ """Collect all MySQL metrics via SSH.
178
+
179
+ Batches queries into two SSH calls for efficiency:
180
+ 1. SHOW GLOBAL STATUS + SHOW VARIABLES
181
+ 2. Table sizes + processlist + top queries (performance_schema)
182
+
183
+ Returns a dict with raw parsed data keyed by section.
184
+ """
185
+ data: Dict[str, Any] = {
186
+ "global_status": {},
187
+ "variables": {},
188
+ "tables": [],
189
+ "processlist": [],
190
+ "top_queries": [],
191
+ "errors": [],
192
+ }
193
+
194
+ # --- Batch 1: SHOW GLOBAL STATUS; SHOW VARIABLES ---
195
+ batch1_query = QUERY_GLOBAL_STATUS + "; " + QUERY_VARIABLES
196
+ code, output = run_mysql_query(service, batch1_query, timeout=timeout)
197
+ if code != 0:
198
+ data["errors"].append(f"Batch 1 (status/variables) failed: {output}")
199
+ else:
200
+ # MySQL concatenates the two result sets; split them by detecting
201
+ # a second header line starting with Variable_name
202
+ sections = _split_mysql_resultsets(output, "Variable_name")
203
+ if len(sections) >= 1:
204
+ data["global_status"] = parse_mysql_kv(sections[0])
205
+ if len(sections) >= 2:
206
+ data["variables"] = parse_mysql_kv(sections[1])
207
+
208
+ # --- Batch 2: tables + processlist + top queries ---
209
+ batch2_query = QUERY_TABLE_SIZES + "; " + QUERY_PROCESSLIST + "; " + QUERY_TOP_QUERIES
210
+ code, output = run_mysql_query(service, batch2_query, timeout=timeout)
211
+ if code != 0:
212
+ # Top queries may fail if performance_schema is off; try without
213
+ batch2_fallback = QUERY_TABLE_SIZES + "; " + QUERY_PROCESSLIST
214
+ code2, output2 = run_mysql_query(service, batch2_fallback, timeout=timeout)
215
+ if code2 != 0:
216
+ data["errors"].append(f"Batch 2 (tables/processlist) failed: {output}")
217
+ else:
218
+ sections = _split_mysql_resultsets_multi(output2, [
219
+ "TABLE_NAME",
220
+ "Id",
221
+ ])
222
+ if len(sections) >= 1:
223
+ data["tables"] = parse_mysql_batch(sections[0])
224
+ if len(sections) >= 2:
225
+ data["processlist"] = parse_mysql_batch(sections[1])
226
+ else:
227
+ sections = _split_mysql_resultsets_multi(output, [
228
+ "TABLE_NAME",
229
+ "Id",
230
+ "DIGEST",
231
+ ])
232
+ if len(sections) >= 1:
233
+ data["tables"] = parse_mysql_batch(sections[0])
234
+ if len(sections) >= 2:
235
+ data["processlist"] = parse_mysql_batch(sections[1])
236
+ if len(sections) >= 3:
237
+ data["top_queries"] = parse_mysql_batch(sections[2])
238
+
239
+ return data
240
+
241
+
242
+ def _split_mysql_resultsets(output: str, header_key: str) -> List[str]:
243
+ """Split concatenated MySQL batch output into sections by header line."""
244
+ lines = output.strip().split("\n")
245
+ sections: List[List[str]] = []
246
+ current: List[str] = []
247
+
248
+ for line in lines:
249
+ if line.startswith(header_key + "\t") or line.strip() == header_key:
250
+ if current:
251
+ sections.append("\n".join(current))
252
+ current = [line]
253
+ else:
254
+ current.append(line)
255
+ if current:
256
+ sections.append("\n".join(current))
257
+
258
+ return sections
259
+
260
+
261
+ def _split_mysql_resultsets_multi(output: str, header_keys: List[str]) -> List[str]:
262
+ """Split concatenated MySQL batch output into sections by multiple different header keys."""
263
+ lines = output.strip().split("\n")
264
+ sections: List[List[str]] = []
265
+ current: List[str] = []
266
+ expected_idx = 0
267
+
268
+ for line in lines:
269
+ # Check if this line starts a new result set
270
+ matched = False
271
+ if expected_idx < len(header_keys):
272
+ key = header_keys[expected_idx]
273
+ if line.startswith(key + "\t") or line.strip() == key:
274
+ if current:
275
+ sections.append("\n".join(current))
276
+ current = [line]
277
+ expected_idx += 1
278
+ matched = True
279
+ # Also check remaining headers in case we skipped one
280
+ if not matched:
281
+ for idx in range(expected_idx, len(header_keys)):
282
+ key = header_keys[idx]
283
+ if line.startswith(key + "\t") or line.strip() == key:
284
+ if current:
285
+ sections.append("\n".join(current))
286
+ current = [line]
287
+ expected_idx = idx + 1
288
+ matched = True
289
+ break
290
+ if not matched:
291
+ current.append(line)
292
+
293
+ if current:
294
+ sections.append("\n".join(current))
295
+
296
+ return sections
297
+
298
+
299
+ # ---------------------------------------------------------------------------
300
+ # Parse collected data into result
301
+ # ---------------------------------------------------------------------------
302
+
303
+ def parse_mysql_data(data: Dict[str, Any], result: MySQLAnalysisResult) -> None:
304
+ """Transform raw MySQL data into structured result sections."""
305
+ gs = data.get("global_status", {})
306
+ vs = data.get("variables", {})
307
+
308
+ # --- Overview ---
309
+ version = vs.get("version", "unknown")
310
+ uptime_sec = _safe_int(gs.get("Uptime"))
311
+ threads_connected = _safe_int(gs.get("Threads_connected"))
312
+ threads_running = _safe_int(gs.get("Threads_running"))
313
+ max_used_connections = _safe_int(gs.get("Max_used_connections"))
314
+ max_connections = _safe_int(vs.get("max_connections"), 1)
315
+ aborted_clients = _safe_int(gs.get("Aborted_clients"))
316
+ aborted_connects = _safe_int(gs.get("Aborted_connects"))
317
+ connection_usage_pct = round(max_used_connections / max_connections * 100, 1) if max_connections > 0 else 0
318
+
319
+ result.overview = {
320
+ "version": version,
321
+ "uptime_seconds": uptime_sec,
322
+ "uptime_human": _format_uptime(uptime_sec),
323
+ "threads_connected": threads_connected,
324
+ "threads_running": threads_running,
325
+ "max_used_connections": max_used_connections,
326
+ "max_connections": max_connections,
327
+ "connection_usage_percent": connection_usage_pct,
328
+ "aborted_clients": aborted_clients,
329
+ "aborted_connects": aborted_connects,
330
+ }
331
+
332
+ # --- Query Throughput ---
333
+ questions = _safe_int(gs.get("Questions"))
334
+ slow_queries = _safe_int(gs.get("Slow_queries"))
335
+ long_query_time = vs.get("long_query_time", "10")
336
+ com_select = _safe_int(gs.get("Com_select"))
337
+ com_insert = _safe_int(gs.get("Com_insert"))
338
+ com_update = _safe_int(gs.get("Com_update"))
339
+ com_delete = _safe_int(gs.get("Com_delete"))
340
+
341
+ result.query_throughput = {
342
+ "questions": questions,
343
+ "slow_queries": slow_queries,
344
+ "long_query_time": long_query_time,
345
+ "com_select": com_select,
346
+ "com_insert": com_insert,
347
+ "com_update": com_update,
348
+ "com_delete": com_delete,
349
+ }
350
+
351
+ # --- InnoDB Row Operations ---
352
+ result.innodb_row_ops = {
353
+ "rows_read": _safe_int(gs.get("Innodb_rows_read")),
354
+ "rows_inserted": _safe_int(gs.get("Innodb_rows_inserted")),
355
+ "rows_updated": _safe_int(gs.get("Innodb_rows_updated")),
356
+ "rows_deleted": _safe_int(gs.get("Innodb_rows_deleted")),
357
+ }
358
+
359
+ # --- Query Efficiency ---
360
+ created_tmp_disk = _safe_int(gs.get("Created_tmp_disk_tables"))
361
+ created_tmp = _safe_int(gs.get("Created_tmp_tables"))
362
+ tmp_disk_pct = round(created_tmp_disk / created_tmp * 100, 1) if created_tmp > 0 else 0
363
+ handler_rnd_next = _safe_int(gs.get("Handler_read_rnd_next"))
364
+ handler_first = _safe_int(gs.get("Handler_read_first"))
365
+ handler_key = _safe_int(gs.get("Handler_read_key"))
366
+ scan_total = handler_rnd_next + handler_first + handler_key
367
+ table_scan_pct = round((handler_rnd_next + handler_first) / scan_total * 100, 1) if scan_total > 0 else 0
368
+ select_full_join = _safe_int(gs.get("Select_full_join"))
369
+ select_range = _safe_int(gs.get("Select_range"))
370
+ sort_merge_passes = _safe_int(gs.get("Sort_merge_passes"))
371
+
372
+ result.query_efficiency = {
373
+ "created_tmp_disk_tables": created_tmp_disk,
374
+ "created_tmp_tables": created_tmp,
375
+ "tmp_disk_table_percent": tmp_disk_pct,
376
+ "handler_read_rnd_next": handler_rnd_next,
377
+ "handler_read_first": handler_first,
378
+ "handler_read_key": handler_key,
379
+ "table_scan_percent": table_scan_pct,
380
+ "select_full_join": select_full_join,
381
+ "select_range": select_range,
382
+ "sort_merge_passes": sort_merge_passes,
383
+ }
384
+
385
+ # --- InnoDB Buffer Pool ---
386
+ read_requests = _safe_int(gs.get("Innodb_buffer_pool_read_requests"))
387
+ reads = _safe_int(gs.get("Innodb_buffer_pool_reads"))
388
+ hit_ratio = round((read_requests - reads) / read_requests * 100, 2) if read_requests > 0 else 0
389
+ pool_size = _safe_int(vs.get("innodb_buffer_pool_size"))
390
+ bytes_data = _safe_int(gs.get("Innodb_buffer_pool_bytes_data"))
391
+ bytes_dirty = _safe_int(gs.get("Innodb_buffer_pool_bytes_dirty"))
392
+ usage_pct = round(bytes_data / pool_size * 100, 1) if pool_size > 0 else 0
393
+ pages_free = _safe_int(gs.get("Innodb_buffer_pool_pages_free"))
394
+ pages_data = _safe_int(gs.get("Innodb_buffer_pool_pages_data"))
395
+ pages_dirty = _safe_int(gs.get("Innodb_buffer_pool_pages_dirty"))
396
+
397
+ result.innodb_buffer_pool = {
398
+ "hit_ratio": hit_ratio,
399
+ "read_requests": read_requests,
400
+ "reads": reads,
401
+ "buffer_pool_size": pool_size,
402
+ "bytes_data": bytes_data,
403
+ "bytes_dirty": bytes_dirty,
404
+ "usage_percent": usage_pct,
405
+ "pages_data": pages_data,
406
+ "pages_free": pages_free,
407
+ "pages_dirty": pages_dirty,
408
+ }
409
+
410
+ # --- InnoDB I/O ---
411
+ result.innodb_io = {
412
+ "data_reads": _safe_int(gs.get("Innodb_data_reads")),
413
+ "data_writes": _safe_int(gs.get("Innodb_data_writes")),
414
+ }
415
+
416
+ # --- Network ---
417
+ result.network = {
418
+ "bytes_received": _safe_int(gs.get("Bytes_received")),
419
+ "bytes_sent": _safe_int(gs.get("Bytes_sent")),
420
+ }
421
+
422
+ # --- Locks ---
423
+ row_lock_waits = _safe_int(gs.get("Innodb_row_lock_waits"))
424
+ row_lock_time = _safe_int(gs.get("Innodb_row_lock_time"))
425
+ table_locks_waited = _safe_int(gs.get("Table_locks_waited"))
426
+ table_locks_immediate = _safe_int(gs.get("Table_locks_immediate"))
427
+ lock_total = table_locks_waited + table_locks_immediate
428
+ table_lock_contention = round(table_locks_waited / lock_total * 100, 2) if lock_total > 0 else 0
429
+
430
+ result.locks = {
431
+ "row_lock_waits": row_lock_waits,
432
+ "row_lock_time": row_lock_time,
433
+ "table_locks_waited": table_locks_waited,
434
+ "table_locks_immediate": table_locks_immediate,
435
+ "table_lock_contention": table_lock_contention,
436
+ }
437
+
438
+ # --- Table Cache ---
439
+ open_tables = _safe_int(gs.get("Open_tables"))
440
+ opened_tables = _safe_int(gs.get("Opened_tables"))
441
+ table_open_cache = _safe_int(vs.get("table_open_cache"))
442
+ cache_utilization_pct = round(open_tables / table_open_cache * 100, 1) if table_open_cache > 0 else 0
443
+ opens_per_sec = round(opened_tables / uptime_sec, 2) if uptime_sec > 0 else 0
444
+
445
+ result.table_cache = {
446
+ "open_tables": open_tables,
447
+ "opened_tables": opened_tables,
448
+ "table_open_cache": table_open_cache,
449
+ "cache_utilization_percent": cache_utilization_pct,
450
+ "opens_per_second": opens_per_sec,
451
+ }
452
+
453
+ # --- Top Queries ---
454
+ for row in data.get("top_queries", []):
455
+ result.top_queries.append({
456
+ "digest": row.get("DIGEST", ""),
457
+ "digest_text": row.get("DIGEST_TEXT", ""),
458
+ "count_star": _safe_int(row.get("COUNT_STAR")),
459
+ "total_latency_ms": _safe_float(row.get("TOTAL_LATENCY_MS")),
460
+ "avg_latency_ms": _safe_float(row.get("AVG_LATENCY_MS")),
461
+ "rows_examined": _safe_int(row.get("SUM_ROWS_EXAMINED")),
462
+ "rows_sent": _safe_int(row.get("SUM_ROWS_SENT")),
463
+ "tmp_disk_tables": _safe_int(row.get("SUM_CREATED_TMP_DISK_TABLES")),
464
+ "no_index_used": _safe_int(row.get("SUM_NO_INDEX_USED")),
465
+ })
466
+
467
+ if result.top_queries:
468
+ result.top_queries_status = "ok"
469
+ else:
470
+ perf_schema = vs.get("performance_schema", "").upper()
471
+ if perf_schema == "OFF":
472
+ result.top_queries_status = "performance_schema_disabled"
473
+ elif perf_schema == "ON":
474
+ result.top_queries_status = "no_queries_recorded"
475
+ else:
476
+ result.top_queries_status = "unknown"
477
+
478
+ # --- Tables ---
479
+ for row in data.get("tables", []):
480
+ result.tables.append({
481
+ "name": row.get("TABLE_NAME", ""),
482
+ "rows": _safe_int(row.get("TABLE_ROWS")),
483
+ "data_length": _safe_int(row.get("DATA_LENGTH")),
484
+ "index_length": _safe_int(row.get("INDEX_LENGTH")),
485
+ "total_size": _safe_int(row.get("TOTAL_SIZE")),
486
+ })
487
+
488
+ # --- Active Processes ---
489
+ for row in data.get("processlist", []):
490
+ result.active_processes.append({
491
+ "id": row.get("Id", ""),
492
+ "user": row.get("User", ""),
493
+ "db": row.get("db", ""),
494
+ "command": row.get("Command", ""),
495
+ "time": _safe_int(row.get("Time")),
496
+ "state": row.get("State", ""),
497
+ "info": row.get("Info", ""),
498
+ })
499
+
500
+
501
+ # ---------------------------------------------------------------------------
502
+ # Formatting helpers
503
+ # ---------------------------------------------------------------------------
504
+
505
+ def _format_count(n: int) -> str:
506
+ """Format large numbers with K/M/G suffix."""
507
+ if n >= 1_000_000_000:
508
+ return f"{n / 1_000_000_000:.1f}G"
509
+ if n >= 1_000_000:
510
+ return f"{n / 1_000_000:.1f}M"
511
+ if n >= 1_000:
512
+ return f"{n / 1_000:.1f}K"
513
+ return str(n)
514
+
515
+
516
+ def _format_bytes(b: int) -> str:
517
+ """Format bytes to human-readable."""
518
+ if b >= 1_073_741_824:
519
+ return f"{b / 1_073_741_824:.1f} GB"
520
+ if b >= 1_048_576:
521
+ return f"{b / 1_048_576:.1f} MB"
522
+ if b >= 1_024:
523
+ return f"{b / 1_024:.1f} KB"
524
+ return f"{b} B"
525
+
526
+
527
+ def _status_ok_warn_crit(value: float, warn_threshold: float, crit_threshold: float) -> str:
528
+ if value >= crit_threshold:
529
+ return "CRITICAL"
530
+ if value >= warn_threshold:
531
+ return "WARN"
532
+ return "OK"
533
+
534
+
535
+ # ---------------------------------------------------------------------------
536
+ # Recommendations
537
+ # ---------------------------------------------------------------------------
538
+
539
+ def generate_recommendations(result: MySQLAnalysisResult) -> List[Dict[str, str]]:
540
+ recs: List[Dict[str, str]] = []
541
+
542
+ # Collection failures — surface critical issues when SSH/introspection failed
543
+ if result.collection_status:
544
+ failed = {k: v for k, v in result.collection_status.items()
545
+ if v.get("status") in ("failed", "error")}
546
+ ssh_sources = {"mysql_query"}
547
+ ssh_failed = {k: v for k, v in failed.items() if k in ssh_sources}
548
+ if ssh_failed:
549
+ sources = ", ".join(ssh_failed.keys())
550
+ errors = "; ".join(v.get("error", "unknown") for v in ssh_failed.values())
551
+ recs.append({
552
+ "severity": "critical",
553
+ "category": "collection",
554
+ "message": f"SSH introspection failed — unable to collect {sources}. "
555
+ f"Error: {errors}. "
556
+ f"Analysis is incomplete: InnoDB buffer pool, query throughput, "
557
+ f"locks, and tuning parameters could not be evaluated.",
558
+ })
559
+
560
+ def rec(severity: str, message: str):
561
+ recs.append({"severity": severity, "message": message})
562
+
563
+ ov = result.overview or {}
564
+ qt = result.query_throughput or {}
565
+ qe = result.query_efficiency or {}
566
+ bp = result.innodb_buffer_pool or {}
567
+ lk = result.locks or {}
568
+ tc = result.table_cache or {}
569
+
570
+ # Connection usage
571
+ conn_pct = ov.get("connection_usage_percent", 0)
572
+ max_conn = ov.get("max_connections", 0)
573
+ if conn_pct >= 90:
574
+ rec("critical", f"Connection usage critical at {conn_pct}%. Approaching max_connections ({max_conn}).")
575
+ elif conn_pct >= 70:
576
+ rec("warning", f"Connection usage at {conn_pct}%. Consider increasing max_connections.")
577
+
578
+ # Buffer pool hit ratio
579
+ hit_ratio = bp.get("hit_ratio", 100)
580
+ if hit_ratio < 95:
581
+ rec("critical", f"Buffer pool hit ratio at {hit_ratio}%. Increase innodb_buffer_pool_size.")
582
+ elif hit_ratio < 99:
583
+ rec("warning", f"Buffer pool hit ratio at {hit_ratio}% -- room for improvement with more RAM.")
584
+
585
+ # Buffer pool usage
586
+ bp_usage = bp.get("usage_percent", 0)
587
+ if bp_usage > 95:
588
+ rec("warning", f"Buffer pool {bp_usage}% full. Data pages may be evicted under load.")
589
+
590
+ # Temp tables to disk
591
+ tmp_pct = qe.get("tmp_disk_table_percent", 0)
592
+ created_disk = qe.get("created_tmp_disk_tables", 0)
593
+ created_total = qe.get("created_tmp_tables", 0)
594
+ if tmp_pct > 25:
595
+ rec("warning", f"{tmp_pct}% of temp tables going to disk. Increase tmp_table_size/max_heap_table_size or optimize queries.")
596
+ elif tmp_pct > 10:
597
+ rec("info", f"Temp tables to disk at {tmp_pct}%. Watch for queries creating large temporary results.")
598
+
599
+ # Table scan ratio
600
+ scan_pct = qe.get("table_scan_percent", 0)
601
+ if scan_pct > 75:
602
+ rec("critical", f"Table scan ratio at {scan_pct}%. Most reads are full scans -- add indexes.")
603
+ elif scan_pct > 50:
604
+ rec("warning", f"Table scan ratio at {scan_pct}%. Consider indexing frequently queried columns.")
605
+
606
+ # Full joins
607
+ full_joins = qe.get("select_full_join", 0)
608
+ if full_joins > 100:
609
+ rec("warning", f"{_format_count(full_joins)} full joins detected. These scan entire tables -- add indexes to join columns.")
610
+
611
+ # Sort merge passes
612
+ sort_passes = qe.get("sort_merge_passes", 0)
613
+ if sort_passes > 0:
614
+ rec("info", f"Sort merge passes ({_format_count(sort_passes)}). Increase sort_buffer_size or optimize queries.")
615
+
616
+ # Row lock waits
617
+ row_lock_waits = lk.get("row_lock_waits", 0)
618
+ if row_lock_waits > 1000:
619
+ rec("warning", f"InnoDB row lock waits ({_format_count(row_lock_waits)}). Check for lock contention in concurrent writes.")
620
+ elif row_lock_waits > 0:
621
+ rec("info", f"InnoDB row lock waits ({_format_count(row_lock_waits)}). Check for lock contention in concurrent writes.")
622
+
623
+ # Table lock contention
624
+ tl_contention = lk.get("table_lock_contention", 0)
625
+ if tl_contention > 5:
626
+ rec("warning", f"Table lock contention at {tl_contention}%. May indicate MyISAM tables -- convert to InnoDB.")
627
+
628
+ # Slow queries
629
+ slow = qt.get("slow_queries", 0)
630
+ threshold = qt.get("long_query_time", "10")
631
+ if slow > 0:
632
+ rec("info", f"{_format_count(slow)} slow queries (threshold: {threshold}s). Review with performance_schema or slow query log.")
633
+
634
+ # Aborted clients
635
+ aborted_clients = ov.get("aborted_clients", 0)
636
+ if aborted_clients > 0:
637
+ rec("info", f"{_format_count(aborted_clients)} aborted clients. Applications may not be closing connections properly.")
638
+
639
+ # Aborted connects
640
+ aborted_connects = ov.get("aborted_connects", 0)
641
+ if aborted_connects > 0:
642
+ rec("info", f"{_format_count(aborted_connects)} aborted connection attempts. Check authentication issues or connection limits.")
643
+
644
+ # No index used in top queries
645
+ if result.top_queries:
646
+ no_index_count = sum(1 for q in result.top_queries if q.get("no_index_used", 0) > 0)
647
+ if no_index_count > 0:
648
+ rec("warning", f"Top queries using no index ({no_index_count} of {len(result.top_queries)}). Missing indexes are likely impacting performance.")
649
+
650
+ # Table cache
651
+ tc = result.table_cache or {}
652
+ opens_per_sec = tc.get("opens_per_second", 0)
653
+ cache_util = tc.get("cache_utilization_percent", 0)
654
+ if cache_util >= 95:
655
+ rec("warning", f"Table cache {cache_util}% full ({tc.get('open_tables')}/{tc.get('table_open_cache')}). Increase table_open_cache.")
656
+ if opens_per_sec > 5:
657
+ rec("warning", f"Table opens at {opens_per_sec}/sec — cache may be undersized. Increase table_open_cache.")
658
+
659
+ # Top queries diagnostic
660
+ if not result.top_queries:
661
+ if result.top_queries_status == "performance_schema_disabled":
662
+ pass # Off by default on Railway; overhead (~400MB+) is too high to recommend casually
663
+ elif result.top_queries_status == "no_queries_recorded":
664
+ rec("info", "performance_schema is ON but no queries recorded. Database may be idle or recently restarted.")
665
+
666
+ return recs
667
+
668
+
669
+ # ---------------------------------------------------------------------------
670
+ # Report formatter
671
+ # ---------------------------------------------------------------------------
672
+
673
+ def format_report(result: MySQLAnalysisResult) -> str:
674
+ lines: List[str] = []
675
+
676
+ def heading(title: str, level: int = 2):
677
+ prefix = "#" * level
678
+ lines.append(f"\n{prefix} {title}")
679
+
680
+ def table_row(*cells: str):
681
+ lines.append("| " + " | ".join(cells) + " |")
682
+
683
+ def table_sep(ncols: int):
684
+ lines.append("| " + " | ".join(["---"] * ncols) + " |")
685
+
686
+ # Title
687
+ lines.append(f"# MySQL Analysis: {result.service}")
688
+ lines.append(f"Timestamp: {result.timestamp}")
689
+ lines.append(f"Deployment: {result.deployment_status}")
690
+
691
+ # --- Resource Overview (from Railway API) ---
692
+ if result.cpu_memory or result.disk_usage:
693
+ heading("Resource Overview")
694
+ table_row("Metric", "Value")
695
+ table_sep(2)
696
+ if result.cpu_memory:
697
+ cm = result.cpu_memory
698
+ if "cpu_percent" in cm:
699
+ trend = _trend_indicator(result.metrics_history, "cpu")
700
+ lines.append(f"| CPU | {cm['cpu_percent']}% vCPU{trend} |")
701
+ if "memory_gb" in cm:
702
+ trend = _trend_indicator(result.metrics_history, "memory")
703
+ mem_str = f"{cm['memory_gb']} GB"
704
+ if "memory_limit_gb" in cm and cm["memory_limit_gb"] > 0:
705
+ pct = round(cm["memory_gb"] / cm["memory_limit_gb"] * 100, 1)
706
+ mem_str += f" / {cm['memory_limit_gb']} GB ({pct}%)"
707
+ lines.append(f"| Memory | {mem_str}{trend} |")
708
+ if result.disk_usage:
709
+ trend = _trend_indicator(result.metrics_history, "disk")
710
+ lines.append(f"| Disk | {result.disk_usage.get('used', 'N/A')}{trend} |")
711
+
712
+ # --- Overview ---
713
+ ov = result.overview
714
+ if ov:
715
+ heading("Overview")
716
+ table_row("Metric", "Value", "Status")
717
+ table_sep(3)
718
+ table_row("Version", str(ov["version"]), "")
719
+ table_row("Uptime", ov["uptime_human"], "")
720
+ conn_status = _status_ok_warn_crit(ov["connection_usage_percent"], 70, 90)
721
+ table_row(
722
+ "Connections",
723
+ f"{ov['connection_usage_percent']}% ({ov['max_used_connections']}/{ov['max_connections']})",
724
+ conn_status,
725
+ )
726
+ table_row("Threads Running", str(ov["threads_running"]), "")
727
+ aborted_c_status = "WARN" if ov["aborted_clients"] > 0 else "OK"
728
+ table_row("Aborted Clients", _format_count(ov["aborted_clients"]), aborted_c_status)
729
+ aborted_conn_status = "WARN" if ov["aborted_connects"] > 0 else "OK"
730
+ table_row("Aborted Connects", _format_count(ov["aborted_connects"]), aborted_conn_status)
731
+
732
+ # --- Query Throughput ---
733
+ qt = result.query_throughput
734
+ if qt:
735
+ heading("Query Throughput")
736
+ table_row("Metric", "Value", "Status")
737
+ table_sep(3)
738
+ table_row("Total Queries", _format_count(qt["questions"]), "")
739
+ slow_status = "WARN" if qt["slow_queries"] > 0 else "OK"
740
+ table_row("Slow Queries", f"{_format_count(qt['slow_queries'])} (> {qt['long_query_time']}s threshold)", slow_status)
741
+ table_row("SELECT", _format_count(qt["com_select"]), "")
742
+ table_row("INSERT", _format_count(qt["com_insert"]), "")
743
+ table_row("UPDATE", _format_count(qt["com_update"]), "")
744
+ table_row("DELETE", _format_count(qt["com_delete"]), "")
745
+
746
+ # --- InnoDB Row Operations ---
747
+ ro = result.innodb_row_ops
748
+ if ro:
749
+ heading("InnoDB Row Operations")
750
+ table_row("Operation", "Count")
751
+ table_sep(2)
752
+ table_row("Rows Read", _format_count(ro["rows_read"]))
753
+ table_row("Rows Inserted", _format_count(ro["rows_inserted"]))
754
+ table_row("Rows Updated", _format_count(ro["rows_updated"]))
755
+ table_row("Rows Deleted", _format_count(ro["rows_deleted"]))
756
+
757
+ # --- Query Efficiency ---
758
+ qe = result.query_efficiency
759
+ if qe:
760
+ heading("Query Efficiency")
761
+ table_row("Metric", "Value", "Status")
762
+ table_sep(3)
763
+ tmp_status = _status_ok_warn_crit(qe["tmp_disk_table_percent"], 10, 25)
764
+ table_row(
765
+ "Temp Tables to Disk",
766
+ f"{qe['tmp_disk_table_percent']}% ({_format_count(qe['created_tmp_disk_tables'])}/{_format_count(qe['created_tmp_tables'])})",
767
+ tmp_status,
768
+ )
769
+ scan_status = _status_ok_warn_crit(qe["table_scan_percent"], 50, 75)
770
+ table_row("Table Scan Ratio", f"{qe['table_scan_percent']}%", scan_status)
771
+ fj_status = "WARN" if qe["select_full_join"] > 100 else "OK"
772
+ table_row("Full Joins", _format_count(qe["select_full_join"]), fj_status)
773
+ table_row("Sort Merge Passes", _format_count(qe["sort_merge_passes"]), "")
774
+
775
+ # --- InnoDB Buffer Pool ---
776
+ bp = result.innodb_buffer_pool
777
+ if bp:
778
+ heading("InnoDB Buffer Pool")
779
+ table_row("Metric", "Value", "Status")
780
+ table_sep(3)
781
+ hr_status = _status_ok_warn_crit(100 - bp["hit_ratio"], 1, 5) # inverted: lower hit = worse
782
+ table_row("Cache Hit Ratio", f"{bp['hit_ratio']}%", hr_status)
783
+ usage_status = _status_ok_warn_crit(bp["usage_percent"], 90, 95)
784
+ table_row(
785
+ "Pool Usage",
786
+ f"{bp['usage_percent']}% ({_format_bytes(bp['bytes_data'])}/{_format_bytes(bp['buffer_pool_size'])})",
787
+ usage_status,
788
+ )
789
+ table_row("Dirty Pages", _format_bytes(bp["bytes_dirty"]), "")
790
+ table_row("Free Pages", _format_count(bp["pages_free"]), "")
791
+
792
+ # --- Network ---
793
+ nw = result.network
794
+ if nw:
795
+ heading("Network")
796
+ table_row("Metric", "Value")
797
+ table_sep(2)
798
+ table_row("Bytes Received", _format_bytes(nw["bytes_received"]))
799
+ table_row("Bytes Sent", _format_bytes(nw["bytes_sent"]))
800
+
801
+ # --- Locks ---
802
+ lk = result.locks
803
+ if lk:
804
+ heading("Locks")
805
+ table_row("Metric", "Value", "Status")
806
+ table_sep(3)
807
+ table_row("Row Lock Waits", _format_count(lk["row_lock_waits"]), "")
808
+ table_row("Row Lock Time (ms)", _format_count(lk["row_lock_time"]), "")
809
+ tl_status = _status_ok_warn_crit(lk["table_lock_contention"], 1, 5)
810
+ table_row("Table Lock Contention", f"{lk['table_lock_contention']}%", tl_status)
811
+
812
+ # --- Table Cache ---
813
+ tc = result.table_cache
814
+ if tc:
815
+ heading("Table Cache")
816
+ table_row("Metric", "Value")
817
+ table_sep(2)
818
+ table_row("Open Tables", f"{_format_count(tc['open_tables'])} / {_format_count(tc.get('table_open_cache', 0))}")
819
+ table_row("Cache Utilization", f"{tc.get('cache_utilization_percent', 0)}%")
820
+ table_row("Table Opens/sec", f"{tc.get('opens_per_second', 0)}")
821
+
822
+ # --- Top Queries ---
823
+ if result.top_queries:
824
+ heading("Top Queries (by total latency)")
825
+ table_row("Query", "Calls", "Avg Latency", "Total Latency", "Rows Examined", "Rows Sent")
826
+ table_sep(6)
827
+ for q in result.top_queries[:15]:
828
+ digest = q["digest_text"][:60] + "..." if len(q["digest_text"]) > 60 else q["digest_text"]
829
+ # Escape pipe chars in query text
830
+ digest = digest.replace("|", "\\|")
831
+ table_row(
832
+ digest,
833
+ _format_count(q["count_star"]),
834
+ f"{q['avg_latency_ms']:.1f}ms",
835
+ f"{q['total_latency_ms']:.1f}ms",
836
+ _format_count(q["rows_examined"]),
837
+ _format_count(q["rows_sent"]),
838
+ )
839
+ else:
840
+ heading("Top Queries (by total latency)")
841
+ if result.top_queries_status == "performance_schema_disabled":
842
+ lines.append("performance_schema is disabled — no query-level data available.")
843
+ lines.append("Note: enabling it requires ~400MB+ additional memory; only advisable on larger instances.")
844
+ elif result.top_queries_status == "no_queries_recorded":
845
+ lines.append("No queries recorded. Database may be idle or recently restarted.")
846
+ else:
847
+ lines.append("No query data available.")
848
+ lines.append("")
849
+
850
+ # --- Tables ---
851
+ if result.tables:
852
+ heading("Tables (by size)")
853
+ table_row("Table", "Rows", "Data", "Indexes", "Total")
854
+ table_sep(5)
855
+ for t in result.tables[:15]:
856
+ table_row(
857
+ t["name"],
858
+ _format_count(t["rows"]),
859
+ _format_bytes(t["data_length"]),
860
+ _format_bytes(t["index_length"]),
861
+ _format_bytes(t["total_size"]),
862
+ )
863
+
864
+ # --- Active Queries ---
865
+ if result.active_processes:
866
+ heading("Active Queries")
867
+ # Filter out system processes
868
+ user_procs = [p for p in result.active_processes if p["command"] != "Daemon"]
869
+ if user_procs:
870
+ table_row("User", "Database", "Command", "Time (s)", "Query")
871
+ table_sep(5)
872
+ for p in user_procs[:20]:
873
+ info = (p["info"] or "")[:80]
874
+ info = info.replace("|", "\\|")
875
+ table_row(
876
+ p["user"],
877
+ p["db"] or "",
878
+ p["command"],
879
+ str(p["time"]),
880
+ info,
881
+ )
882
+ else:
883
+ lines.append("\nNo active user queries.")
884
+
885
+ # --- Infrastructure Metrics ---
886
+ if result.metrics_history:
887
+ windows = result.metrics_history.get("windows", {})
888
+ for window_label, window_data in windows.items():
889
+ mh = window_data.get("metrics", {})
890
+ if not mh:
891
+ continue
892
+ lines.append(f"## Infrastructure Metrics ({window_label})")
893
+ lines.append("| Metric | Current | Min | Max | Avg | Trend |")
894
+ lines.append("|--------|---------|-----|-----|-----|-------|")
895
+ for key in ["cpu", "memory", "disk", "network_rx", "network_tx"]:
896
+ if key in mh:
897
+ entry = mh[key]
898
+ trend = entry.get("trend", {})
899
+ trend_str = trend.get("direction", "N/A")
900
+ change = trend.get("change_pct", 0)
901
+ if change != 0:
902
+ trend_str += f" ({change:+.1f}%)"
903
+ lines.append(
904
+ f"| {key.replace('_', ' ').title()} "
905
+ f"| {entry['current']}{entry['unit']} "
906
+ f"| {entry['min']}{entry['unit']} "
907
+ f"| {entry['max']}{entry['unit']} "
908
+ f"| {entry['avg']}{entry['unit']} "
909
+ f"| {trend_str} |"
910
+ )
911
+ lines.append("")
912
+
913
+ # --- Collection Errors ---
914
+ if result.errors:
915
+ heading("Collection Errors")
916
+ for err in result.errors:
917
+ lines.append(f"- {err}")
918
+
919
+ # --- Recommendations ---
920
+ heading("Recommendations")
921
+ if result.recommendations:
922
+ for r in result.recommendations:
923
+ severity = r["severity"].upper()
924
+ lines.append(f"- **[{severity}]** {r['message']}")
925
+ else:
926
+ lines.append("No issues detected.")
927
+
928
+ return "\n".join(lines) + "\n"
929
+
930
+
931
+ # ---------------------------------------------------------------------------
932
+ # Main analysis function
933
+ # ---------------------------------------------------------------------------
934
+
935
+ def analyze_mysql(service: str, timeout: int = 60, quiet: bool = False,
936
+ skip_logs: bool = False, metrics_hours: int = 168,
937
+ project_id: Optional[str] = None,
938
+ environment_id: Optional[str] = None,
939
+ service_id: Optional[str] = None) -> MySQLAnalysisResult:
940
+ """Run complete MySQL analysis."""
941
+ if not quiet:
942
+ print(f"Analyzing mysql database: {service}", file=sys.stderr)
943
+
944
+ result = MySQLAnalysisResult(
945
+ service=service,
946
+ db_type="mysql",
947
+ timestamp=datetime.now(timezone.utc).isoformat(),
948
+ )
949
+
950
+ # === CONTEXT ===
951
+ if not quiet:
952
+ print(" [0/5] Getting Railway context...", file=sys.stderr, flush=True)
953
+ dal._progress_timer.start()
954
+
955
+ if environment_id and service_id:
956
+ dal._ctx = RailwayContext(project_id=project_id, environment_id=environment_id, service_id=service_id)
957
+ if not quiet:
958
+ print(f" using explicit IDs (env={environment_id[:8]}..., svc={service_id[:8]}...)", file=sys.stderr, flush=True)
959
+ else:
960
+ railway_status = get_railway_status()
961
+ if railway_status:
962
+ dal._ctx = RailwayContext(
963
+ project_id=railway_status.get("projectId"),
964
+ environment_id=railway_status.get("environmentId"),
965
+ service_id=railway_status.get("serviceId"),
966
+ )
967
+ environment_id = dal._ctx.environment_id
968
+ service_id = dal._ctx.service_id
969
+
970
+ # === DEPLOYMENT STATUS ===
971
+ progress(1, 5, "Fetching deployment status...", quiet)
972
+ result.deployment_status = get_deployment_status(service, service_id=service_id)
973
+
974
+ # === SSH PRE-CHECK ===
975
+ progress(2, 5, "Testing SSH connectivity...", quiet)
976
+ ssh_available = False
977
+ ssh_stderr = ""
978
+ ssh_attempts = [30, 60, 90]
979
+ for attempt, attempt_timeout in enumerate(ssh_attempts, 1):
980
+ ssh_code, ssh_stdout, ssh_stderr = run_ssh_query(service, "echo ok", timeout=attempt_timeout)
981
+ if ssh_code == 0 and "ok" in ssh_stdout:
982
+ ssh_available = True
983
+ if not quiet:
984
+ for line in ssh_stderr.splitlines():
985
+ if line.startswith("Using SSH key:"):
986
+ print(f" {line}", file=sys.stderr, flush=True)
987
+ break
988
+ break
989
+ if not quiet:
990
+ remaining = len(ssh_attempts) - attempt
991
+ if remaining > 0:
992
+ print(f" SSH attempt {attempt}/{len(ssh_attempts)} failed ({ssh_stderr or 'no response'}), retrying with {ssh_attempts[attempt]}s timeout...", file=sys.stderr, flush=True)
993
+ else:
994
+ print(f" SSH attempt {attempt}/{len(ssh_attempts)} failed ({ssh_stderr or 'no response'}), giving up", file=sys.stderr, flush=True)
995
+
996
+ # === PARALLEL EXECUTION ===
997
+ progress(3, 5, "Running analysis (metrics, queries, logs in parallel)...", quiet)
998
+
999
+ def task_metrics():
1000
+ if environment_id and service_id:
1001
+ return get_all_metrics_from_api(environment_id, service_id, hours=metrics_hours)
1002
+ return None
1003
+
1004
+ def task_mysql_queries():
1005
+ if not ssh_available:
1006
+ return {"errors": [f"SSH not available: {ssh_stderr or 'connection failed'}"]}
1007
+ data = collect_mysql_data(service, timeout=timeout)
1008
+ if data.get("errors") and not data.get("global_status"):
1009
+ # Retry once
1010
+ data = collect_mysql_data(service, timeout=timeout)
1011
+ return data
1012
+
1013
+ def task_logs():
1014
+ if skip_logs:
1015
+ return []
1016
+ return get_recent_logs(service, lines=LOG_LINES_DEFAULT,
1017
+ environment_id=environment_id,
1018
+ service_id=service_id)
1019
+
1020
+ with ThreadPoolExecutor(max_workers=3) as executor:
1021
+ future_metrics = executor.submit(task_metrics)
1022
+ future_mysql = executor.submit(task_mysql_queries)
1023
+ future_logs = executor.submit(task_logs)
1024
+
1025
+ metrics_result = future_metrics.result()
1026
+ mysql_data = future_mysql.result()
1027
+ logs_result = future_logs.result()
1028
+
1029
+ # Process metrics
1030
+ if metrics_result:
1031
+ result.disk_usage = metrics_result.get("disk_usage")
1032
+ result.cpu_memory = metrics_result.get("cpu_memory")
1033
+ result.metrics_history = metrics_result.get("metrics_history")
1034
+ result.collection_status["metrics_api"] = {"status": "success"}
1035
+ else:
1036
+ result.collection_status["metrics_api"] = {
1037
+ "status": "error",
1038
+ "error": "Metrics API returned no data",
1039
+ }
1040
+
1041
+ # Process MySQL data
1042
+ progress(4, 5, "Processing results...", quiet)
1043
+ mysql_errors = mysql_data.get("errors", [])
1044
+ if mysql_data.get("global_status"):
1045
+ parse_mysql_data(mysql_data, result)
1046
+ result.collection_status["mysql_query"] = {"status": "success"}
1047
+ if mysql_errors:
1048
+ result.collection_status["mysql_query"]["warnings"] = mysql_errors
1049
+ else:
1050
+ error_msg = "; ".join(mysql_errors) if mysql_errors else "No data returned"
1051
+ if not ssh_available:
1052
+ error_msg = f"SSH failed after {len(ssh_attempts)} attempts: {ssh_stderr or 'connection failed'}"
1053
+ result.errors.append(f"MySQL data collection failed: {error_msg}")
1054
+ result.collection_status["mysql_query"] = {
1055
+ "status": "error",
1056
+ "error": error_msg,
1057
+ }
1058
+
1059
+ # Process logs
1060
+ if skip_logs:
1061
+ result.collection_status["logs_api"] = {"status": "skipped", "reason": "skip_logs flag set"}
1062
+ elif logs_result:
1063
+ result.recent_logs = logs_result
1064
+ result.collection_status["logs_api"] = {"status": "success", "lines": len(logs_result)}
1065
+ result.recent_errors = [
1066
+ line for line in result.recent_logs
1067
+ if "ERROR" in line.upper() or "FATAL" in line.upper()
1068
+ ][:100]
1069
+ else:
1070
+ result.recent_logs = []
1071
+ result.collection_status["logs_api"] = {
1072
+ "status": "error",
1073
+ "error": "Logs API returned no data",
1074
+ }
1075
+
1076
+ # === RECOMMENDATIONS ===
1077
+ progress(5, 5, "Generating recommendations...", quiet)
1078
+ result.recommendations = generate_recommendations(result)
1079
+
1080
+ if not quiet:
1081
+ total = dal._progress_timer.total_elapsed()
1082
+ print(f"Done.{total}", file=sys.stderr)
1083
+
1084
+ return result
1085
+
1086
+
1087
+ # ---------------------------------------------------------------------------
1088
+ # Single-step debugging
1089
+ # ---------------------------------------------------------------------------
1090
+
1091
+ def run_single_step(args) -> int:
1092
+ """Run a single collection step for debugging."""
1093
+ service = args.service
1094
+ _init_context(args)
1095
+ environment_id = dal._ctx.environment_id
1096
+ service_id = dal._ctx.service_id
1097
+
1098
+ if args.step == "ssh-test":
1099
+ print(f"Testing SSH to service: {service}", file=sys.stderr)
1100
+ code, stdout, stderr = run_ssh_query(service, "echo ok", timeout=45)
1101
+ print(f"Exit code: {code}")
1102
+ print(f"Stdout: {stdout.strip()}")
1103
+ if stderr:
1104
+ print(f"Stderr: {stderr.strip()}")
1105
+ return 0 if (code == 0 and "ok" in stdout) else 1
1106
+
1107
+ elif args.step == "query":
1108
+ print(f"Running MySQL queries on: {service}", file=sys.stderr)
1109
+ data = collect_mysql_data(service, timeout=args.timeout)
1110
+ print(json.dumps(data, indent=2, default=str))
1111
+ return 0 if data.get("global_status") else 1
1112
+
1113
+ elif args.step == "logs":
1114
+ print(f"Fetching logs for: {service}", file=sys.stderr)
1115
+ logs = get_recent_logs(service, lines=LOG_LINES_DEFAULT,
1116
+ environment_id=environment_id,
1117
+ service_id=service_id)
1118
+ print(f"Lines fetched: {len(logs)}")
1119
+ for line in logs:
1120
+ print(line)
1121
+ return 0
1122
+
1123
+ elif args.step == "metrics":
1124
+ print(f"Fetching metrics for: {service}", file=sys.stderr)
1125
+ if environment_id and service_id:
1126
+ metrics = get_all_metrics_from_api(environment_id, service_id)
1127
+ if metrics:
1128
+ print(json.dumps(metrics, indent=2))
1129
+ else:
1130
+ print("Metrics API returned no data")
1131
+ return 1
1132
+ else:
1133
+ print("Missing environment_id or service_id from railway config")
1134
+ return 1
1135
+ return 0
1136
+
1137
+ return 1
1138
+
1139
+
1140
+ # ---------------------------------------------------------------------------
1141
+ # Main entry point
1142
+ # ---------------------------------------------------------------------------
1143
+
1144
+ def main():
1145
+ parser = argparse.ArgumentParser(
1146
+ description="MySQL analysis for Railway services.",
1147
+ formatter_class=argparse.RawDescriptionHelpFormatter,
1148
+ )
1149
+
1150
+ parser.add_argument("--service", required=True, help="Service name")
1151
+ parser.add_argument("--json", action="store_true",
1152
+ help="Output as JSON")
1153
+ parser.add_argument("--timeout", type=int, default=60,
1154
+ help="Timeout in seconds for SSH queries (default: 60)")
1155
+ parser.add_argument("--quiet", "-q", action="store_true",
1156
+ help="Suppress progress messages")
1157
+ parser.add_argument("--skip-logs", action="store_true",
1158
+ help="Skip log fetching for faster analysis")
1159
+ parser.add_argument("--metrics-hours", type=int, default=168,
1160
+ help="Hours of metrics history to fetch (default: 168, max: 168)")
1161
+ parser.add_argument("--step", choices=["ssh-test", "query", "logs", "metrics"],
1162
+ help="Run a single collection step for debugging")
1163
+ parser.add_argument("--project-id", help="Project ID (bypasses railway link)")
1164
+ parser.add_argument("--environment-id", help="Environment ID (bypasses railway link)")
1165
+ parser.add_argument("--service-id", help="Service ID (bypasses railway link)")
1166
+
1167
+ args = parser.parse_args()
1168
+
1169
+ # Single-step debugging mode
1170
+ if args.step:
1171
+ return run_single_step(args)
1172
+
1173
+ # Run analysis
1174
+ result = analyze_mysql(
1175
+ args.service,
1176
+ timeout=args.timeout,
1177
+ quiet=args.quiet,
1178
+ skip_logs=args.skip_logs,
1179
+ metrics_hours=min(args.metrics_hours, 168),
1180
+ project_id=args.project_id,
1181
+ environment_id=args.environment_id,
1182
+ service_id=args.service_id,
1183
+ )
1184
+
1185
+ # Output
1186
+ if args.json:
1187
+ print(json.dumps(asdict(result), indent=2))
1188
+ else:
1189
+ print(format_report(result))
1190
+
1191
+ return 0
1192
+
1193
+
1194
+ if __name__ == "__main__":
1195
+ sys.exit(main())