@metabase/cli 0.5.3 → 0.5.5

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 (332) hide show
  1. package/dist/{add-collection-c1dfnavz.mjs → add-collection-lgBkahhW.mjs} +3 -3
  2. package/dist/{alert-BkpPdBQ_.mjs → alert-32mfYFy1.mjs} +7 -7
  3. package/dist/{alerts-TDYiIxjs.mjs → alerts-DWVmtHEJ.mjs} +6 -6
  4. package/dist/api-key-DJfhPe4x.mjs +13 -0
  5. package/dist/{append-CoeZRD6u.mjs → append-CdzKyHhn.mjs} +4 -4
  6. package/dist/{archive-DZDkfhdH.mjs → archive-24zg3Ckb.mjs} +5 -5
  7. package/dist/{archive-DY76wU2k.mjs → archive-B3_fjvid.mjs} +3 -3
  8. package/dist/{archive-N3lp1o-k.mjs → archive-B4G4EE9n.mjs} +5 -5
  9. package/dist/{archive-D-BJwRLc.mjs → archive-BTxrM_x2.mjs} +5 -5
  10. package/dist/{archive-DQRehboT.mjs → archive-BZxa5zJk.mjs} +5 -5
  11. package/dist/{archive-CH1h8jz5.mjs → archive-BmutUzPS.mjs} +5 -5
  12. package/dist/{archive-DxUnaV4x.mjs → archive-CHQOH-nm.mjs} +3 -3
  13. package/dist/{archive-CAJHrKwd.mjs → archive-DEOq8OqU.mjs} +5 -5
  14. package/dist/{archive-NfFWXRx5.mjs → archive-DOXrtANg.mjs} +5 -5
  15. package/dist/{archive-f7wKQ7Uy.mjs → archive-DbaGJh7E.mjs} +3 -3
  16. package/dist/{archive-9uzfWGuq.mjs → archive-NhewLr23.mjs} +5 -5
  17. package/dist/{archive-3fdIvMsf.mjs → archive-Uws1E_p8.mjs} +5 -5
  18. package/dist/{auth-hgkvHPpH.mjs → auth-bVvBp9Tl.mjs} +5 -5
  19. package/dist/{branch-flag-CPTQ4wXp.mjs → branch-flag-BvIdNB5z.mjs} +1 -1
  20. package/dist/{branches-B3gHAanU.mjs → branches-CkOAJk1H.mjs} +3 -3
  21. package/dist/{breaking-DuWIof4T.mjs → breaking-CMpEeieT.mjs} +6 -6
  22. package/dist/{broken-D2ST8sTx.mjs → broken-DFFLXiYc.mjs} +8 -8
  23. package/dist/{bulk-discard-values-77khEfpj.mjs → bulk-discard-values-D4eFBtKc.mjs} +5 -5
  24. package/dist/{bulk-edit-girwcjY6.mjs → bulk-edit-CLJeGY2E.mjs} +7 -7
  25. package/dist/{bulk-rescan-values-Df8KK8um.mjs → bulk-rescan-values-DsaGKIo_.mjs} +5 -5
  26. package/dist/{bulk-sync-schema-DEPUZ6MN.mjs → bulk-sync-schema-CN797vy9.mjs} +5 -5
  27. package/dist/{cancel-Oek5PJnu.mjs → cancel-CRpzqIUu.mjs} +3 -3
  28. package/dist/{cancel-task-rkqY0qLU.mjs → cancel-task-CpZcj7HB.mjs} +2 -2
  29. package/dist/{card-QR3zEu6E.mjs → card-BJRZ5D5Q.mjs} +2 -2
  30. package/dist/{card-3NmUiU5F.mjs → card-VipF12MJ.mjs} +1 -1
  31. package/dist/card-nOX_QJ9u.mjs +25 -0
  32. package/dist/{cards-DWJOtZoN.mjs → cards-DJMTrFQg.mjs} +6 -6
  33. package/dist/cli.mjs +128 -294
  34. package/dist/{client-BFP2Ssqu.mjs → client-D2ycdMRO.mjs} +299 -189
  35. package/dist/{collection-D5vt2IJ_.mjs → collection-Bf3lv-zo.mjs} +1 -1
  36. package/dist/collection-Cs84x7WB.mjs +21 -0
  37. package/dist/{collection-DNmFKALA.mjs → collection-Cz2qLX2m.mjs} +1 -1
  38. package/dist/{collection-namespace-Dl8_7xII.mjs → collection-namespace-C0JcSEwN.mjs} +2 -1
  39. package/dist/{content-translation-B3oARbgD.mjs → content-translation-7qPz-dIU.mjs} +3 -3
  40. package/dist/{copy-C7Rc4qM7.mjs → copy-BeFjqXXc.mjs} +6 -6
  41. package/dist/{copy-BzBp5FaY.mjs → copy-D9-xBYYq.mjs} +6 -6
  42. package/dist/{copy-flags-CTM91pxH.mjs → copy-flags-DZ5Cy-Gk.mjs} +2 -2
  43. package/dist/{create-YBDXZgyP.mjs → create--eZb0G2P.mjs} +4 -5
  44. package/dist/{create-f0bBEfDI.mjs → create-B57Sj0om.mjs} +9 -10
  45. package/dist/{create-Bh3q3eHg.mjs → create-Ba8nYAWL.mjs} +9 -9
  46. package/dist/{create-CjGEkSFz.mjs → create-BfgTvPvI.mjs} +9 -10
  47. package/dist/{create-C-2ivjed.mjs → create-BlYxQv6U.mjs} +4 -5
  48. package/dist/{create-DQJ9h86G.mjs → create-CLQVeQ0w.mjs} +4 -5
  49. package/dist/{create-CGxxztoE.mjs → create-Cc07ouKB.mjs} +6 -7
  50. package/dist/{create-C0gmhNnk.mjs → create-CplN1ey8.mjs} +9 -10
  51. package/dist/{create-myJ8HR4B.mjs → create-D4FT8Xj8.mjs} +6 -7
  52. package/dist/{create-BKRD11i-.mjs → create-DBCOYtqn.mjs} +4 -4
  53. package/dist/{create-CfXKNnkK.mjs → create-DBSpDnaA.mjs} +6 -7
  54. package/dist/{create-ByeGK_BE.mjs → create-DHTl3wVv.mjs} +4 -5
  55. package/dist/{create-DO4ic4Kx.mjs → create-DIZGUCFE.mjs} +7 -8
  56. package/dist/{create-CGE1D0Ay.mjs → create-DJ0Xrpod.mjs} +6 -7
  57. package/dist/{create-Bc4ADRmU.mjs → create-Dj5y7xbA.mjs} +4 -5
  58. package/dist/{create-vgP9K2eH2.mjs → create-Dr-q5oWx2.mjs} +10 -11
  59. package/dist/{create-YIC3Q0a-.mjs → create-Dt6gdbAH.mjs} +7 -7
  60. package/dist/{create-branch-DBCBMDWh.mjs → create-branch-CKQkpU-_.mjs} +3 -2
  61. package/dist/{create-C3H9ljex.mjs → create-cpxnjNMC.mjs} +4 -5
  62. package/dist/{create-_rOCx5b8.mjs → create-n5cH8Tq-.mjs} +5 -6
  63. package/dist/{create-71_DODbc.mjs → create-ypwRxrcj.mjs} +9 -10
  64. package/dist/{csv-Dtgy7sTg.mjs → csv-CxWsxJIB.mjs} +4 -4
  65. package/dist/{current-task-AwQisIT9.mjs → current-task-DB7UTh6J.mjs} +3 -3
  66. package/dist/{dashboard-D5E3fMKh.mjs → dashboard-Bx2sYijR.mjs} +1 -1
  67. package/dist/{dashboard-Chjy5GKR.mjs → dashboard-Cwsf-e9T.mjs} +23 -6
  68. package/dist/dashboard-DeSaOt2Z.mjs +35 -0
  69. package/dist/{data-action-DZxVB_hD.mjs → data-action-CuvI17XF.mjs} +8 -2
  70. package/dist/{data-action-R9qxJDPA.mjs → data-action-DLj9cgpu.mjs} +1 -1
  71. package/dist/data-action-Dpt7pfzd.mjs +24 -0
  72. package/dist/{data-app-BIAvvV2P.mjs → data-app--T695WPM.mjs} +10 -10
  73. package/dist/{data-app-project-BL4xpu_m.mjs → data-app-project-DjDDWgUF.mjs} +2 -1
  74. package/dist/{database-CxykzTAA.mjs → database-C5ucihp3.mjs} +2 -2
  75. package/dist/{database-D8XzgJEz.mjs → database-u_spHEa0.mjs} +1 -1
  76. package/dist/{dataset-DIQLAp2P.mjs → dataset-LJxNN6p5.mjs} +3 -3
  77. package/dist/{db-wsSYKqCB.mjs → db-CpK3QluZ.mjs} +8 -8
  78. package/dist/{delete-E-g5b-HU.mjs → delete-9YIc_L2E.mjs} +4 -4
  79. package/dist/{delete-WrV9I9uw.mjs → delete-AwOJ1x-D.mjs} +4 -4
  80. package/dist/{delete-D9FB7qjX.mjs → delete-B9GABYEJ.mjs} +4 -4
  81. package/dist/{delete-4v4_lDwD.mjs → delete-Bw8CMa_A.mjs} +4 -4
  82. package/dist/{delete-BSgxmR3y.mjs → delete-CE19WUPi.mjs} +4 -4
  83. package/dist/{delete-BegkpIGi.mjs → delete-CUzOTiI8.mjs} +4 -4
  84. package/dist/{delete-BP9-YS-f.mjs → delete-D784J3ur.mjs} +4 -4
  85. package/dist/{delete-BHX_F8Qu.mjs → delete-DbAOgu73.mjs} +4 -4
  86. package/dist/{delete-DmM5L0wO.mjs → delete-TcDfZNz2.mjs} +4 -4
  87. package/dist/{delete-runtime-CJ7gMYA1.mjs → delete-runtime-DOOy3gzH.mjs} +2 -1
  88. package/dist/{delete-table-CJq8JM5J.mjs → delete-table-a8acciQe.mjs} +4 -4
  89. package/dist/{delete-wnHUWWjX.mjs → delete-trx8NIoh.mjs} +4 -4
  90. package/dist/{dependencies-CnW3RSoZ.mjs → dependencies-BcPdAMYB.mjs} +6 -6
  91. package/dist/{dependency-CaC6XuTY.mjs → dependency-C1DExO6k.mjs} +8 -5
  92. package/dist/{dependency-Dxvb2Znu.mjs → dependency-DcM96XJ-.mjs} +4 -3
  93. package/dist/dependency-_SgcW_fL.mjs +15 -0
  94. package/dist/{dependents-DHXqJ3lo.mjs → dependents-CRZaiOzg.mjs} +9 -9
  95. package/dist/{dirty-BRPWPUYH.mjs → dirty-1taTj3G6.mjs} +3 -3
  96. package/dist/{discard-values-DBHfVN8o.mjs → discard-values-DmiWAx4J.mjs} +5 -5
  97. package/dist/{document-UMG6me2P.mjs → document-BHOQq2T_.mjs} +2 -2
  98. package/dist/document-DB6YGtqX.mjs +20 -0
  99. package/dist/{document-DVIu3Vj4.mjs → document-lS1XZEF3.mjs} +1 -1
  100. package/dist/{download-BFu9xYHX.mjs → download-CEvVDGeC.mjs} +2 -2
  101. package/dist/{eid-eKmbXRds.mjs → eid-pO_lMTI9.mjs} +5 -5
  102. package/dist/{entity-DjDkaMkB.mjs → entity-DICgy4Bl.mjs} +2 -2
  103. package/dist/{entry-flags-Cxn09krz.mjs → entry-flags-CswcY8bG.mjs} +5 -6
  104. package/dist/{events-CZL9MU64.mjs → events-cGDPEL5_.mjs} +4 -4
  105. package/dist/{execute-DBJGtHHG.mjs → execute-DHGbJpPr.mjs} +7 -8
  106. package/dist/{export-xn_-NaBd.mjs → export-B0NUSP6q.mjs} +6 -5
  107. package/dist/{export-flags-CpukgETM.mjs → export-flags-iXfEILf7.mjs} +3 -2
  108. package/dist/{export-preflight-SM9zJkBp.mjs → export-preflight-Bbl1bHpI.mjs} +3 -3
  109. package/dist/field-AkNMIQnx.mjs +21 -0
  110. package/dist/{field-DoWQUl7V.mjs → field-C2qOViFZ.mjs} +8 -2
  111. package/dist/{field-BVz4AS3f.mjs → field-CT4r6b92.mjs} +1 -1
  112. package/dist/{fields-CB06GFBW.mjs → fields-M66PaKdV.mjs} +6 -6
  113. package/dist/{filter-flags-DhE4olyf.mjs → filter-flags-ezqqsOQL.mjs} +4 -4
  114. package/dist/{fks-CSoS7xZ6.mjs → fks-BvOGzID5.mjs} +6 -6
  115. package/dist/{get-BZFyqZXa.mjs → get-0T9zeyCh.mjs} +5 -5
  116. package/dist/{get-BSPxmP_W.mjs → get-B3Ceh_7N.mjs} +5 -5
  117. package/dist/{get-BW3jSR3y.mjs → get-BL_9fN-q.mjs} +3 -3
  118. package/dist/{get-Bt4C-oxh.mjs → get-Bt2Q516N.mjs} +5 -5
  119. package/dist/{get--1qTpTIQ.mjs → get-BzIQK_mt.mjs} +5 -5
  120. package/dist/{get-BQ7SAJEz.mjs → get-C3j93ivl.mjs} +3 -3
  121. package/dist/{get-DIfjxTP8.mjs → get-CCLnfOOE.mjs} +3 -3
  122. package/dist/{get-CtDZOoWy.mjs → get-CEreA2oN.mjs} +5 -5
  123. package/dist/{get-jbOgdCQg.mjs → get-CJFg7YM5.mjs} +3 -3
  124. package/dist/{get-Cl-O6dFs.mjs → get-Cfg52y7X.mjs} +4 -4
  125. package/dist/{get-eZEHipwA.mjs → get-D-8G7EoL.mjs} +5 -5
  126. package/dist/{get-BYAzIaGG.mjs → get-D-ElxFlQ.mjs} +5 -5
  127. package/dist/{get-Bq_9VSTa.mjs → get-D7y_z5K7.mjs} +3 -3
  128. package/dist/{get-DePbMmjc.mjs → get-DGlZtzfB.mjs} +5 -5
  129. package/dist/{get-C1rCVUYw.mjs → get-DKZLwN3K.mjs} +6 -5
  130. package/dist/{get-BIf6eFWZ.mjs → get-HtzPLpy2.mjs} +5 -5
  131. package/dist/{get-B8xQXMU6.mjs → get-SOCVdBIU.mjs} +5 -5
  132. package/dist/{get-BZDUOZxI.mjs → get-ZSLfDowg.mjs} +3 -3
  133. package/dist/{get-B8xrmipw.mjs → get-jzChrvn2.mjs} +5 -5
  134. package/dist/{get-run-C-CNDbv3.mjs → get-run-BZP_z57v.mjs} +5 -5
  135. package/dist/{get-CuSwArMS.mjs → get-uB7jROcz.mjs} +5 -5
  136. package/dist/{get-BfFZCiZR.mjs → get-wPkejadn.mjs} +5 -5
  137. package/dist/{get-Cc4-e-Q_.mjs → get-zGreQNnG.mjs} +5 -5
  138. package/dist/git-sync-DaHnqpYi.mjs +29 -0
  139. package/dist/glossary-Br_mSh3i.mjs +14 -0
  140. package/dist/{glossary-BkGmCI4X.mjs → glossary-CQRW3Z4d.mjs} +1 -1
  141. package/dist/{glossary-QN-hHfOT.mjs → glossary-iPIWrEbP.mjs} +1 -1
  142. package/dist/{graph-CqBe2J4N.mjs → graph-CdKi07zZ.mjs} +6 -6
  143. package/dist/{group-81zoQcEP.mjs → group--6adAsqG.mjs} +2 -2
  144. package/dist/{has-remote-changes-CDe1teSb.mjs → has-remote-changes-d_JyzdPX.mjs} +2 -2
  145. package/dist/{import-B64gjryF.mjs → import-1bK-qrjV.mjs} +6 -5
  146. package/dist/input-gate-registry-Dh8QjKgW.mjs +14 -0
  147. package/dist/{is-dirty-DhR3Xqjk.mjs → is-dirty-BXwvuTeD.mjs} +2 -2
  148. package/dist/{items-CLl2QgS8.mjs → items-Dzh_xylQ.mjs} +7 -6
  149. package/dist/{key-BYEv4Wgq.mjs → key-DLrpCgdZ.mjs} +1 -1
  150. package/dist/{library-C3eO2J07.mjs → library-BA1QssFE.mjs} +5 -5
  151. package/dist/{library-wUiD2jbw.mjs → library-BprxuqZl.mjs} +1 -1
  152. package/dist/{library-DKIqzyUk.mjs → library-C4g9pdiS.mjs} +8 -3
  153. package/dist/{list-CdcFNphE.mjs → list-0TtkxraU.mjs} +3 -3
  154. package/dist/{list-YFwmHXGG.mjs → list-2doqGDIF.mjs} +3 -3
  155. package/dist/{list-DLLzMaS3.mjs → list-7qcWFDXR.mjs} +5 -5
  156. package/dist/{list-DyFu5Wx1.mjs → list-B5D3leO9.mjs} +6 -6
  157. package/dist/{list-CjpZCiks.mjs → list-B70w2ibG.mjs} +3 -3
  158. package/dist/{list-B7Yvz5vA.mjs → list-B75rzgSi.mjs} +5 -5
  159. package/dist/{list-BmvitCWZ.mjs → list-BWNw-ppI.mjs} +6 -6
  160. package/dist/{list-DyQNRVAf.mjs → list-BjpGurur.mjs} +3 -3
  161. package/dist/{list-DrIcKxXX.mjs → list-CdpEUPL-.mjs} +5 -5
  162. package/dist/{list-DpfOh8-5.mjs → list-CvQlMhrb.mjs} +4 -4
  163. package/dist/{list-DDPvxfhe.mjs → list-DDV5PylU.mjs} +6 -6
  164. package/dist/{list-BIqqh3A-.mjs → list-DHX7-8Ae.mjs} +5 -5
  165. package/dist/{list-BiSwl56L.mjs → list-DQNbHPNQ.mjs} +5 -5
  166. package/dist/{list-CFaJA317.mjs → list-DR9dswtK.mjs} +6 -6
  167. package/dist/{list-eGlqXJno.mjs → list-DSi8mf_F.mjs} +5 -5
  168. package/dist/{list-D4HNG282.mjs → list-D_SfsO_-.mjs} +5 -5
  169. package/dist/{list-CA939qkn.mjs → list-Dn5_L-85.mjs} +6 -6
  170. package/dist/{list-C7TFreS8.mjs → list-DuaFmOCz.mjs} +5 -5
  171. package/dist/{list-Cu6ET_yD.mjs → list-Dv3s_PIu.mjs} +5 -5
  172. package/dist/{list-s9_LlXCV.mjs → list-Dz2YVD7m.mjs} +3 -3
  173. package/dist/{list-DLwby7YA.mjs → list-TzuL_7GA.mjs} +5 -5
  174. package/dist/{list-C3GBocvB.mjs → list-_jHBfq0E.mjs} +5 -5
  175. package/dist/{list-D_IT8l9B.mjs → list-iFtmAfVv.mjs} +3 -3
  176. package/dist/{login-Bg7Z3jDy.mjs → login-LAFw5Xxu.mjs} +6 -5
  177. package/dist/{logout-CAmp7DcS.mjs → logout-CjYzDvLH.mjs} +3 -2
  178. package/dist/{measure-CtfjqJFP.mjs → measure-BMWrX1ca.mjs} +3 -1
  179. package/dist/{measure-C31HLp1C.mjs → measure-DgtOyIoD.mjs} +6 -6
  180. package/dist/{measure-4P0bOlZ3.mjs → measure-DqZG65of.mjs} +1 -1
  181. package/dist/{notification-DaA4bBHW.mjs → notification-B5yMuPmo.mjs} +6 -1
  182. package/dist/{notification-W2SVssyY.mjs → notification-BEJaEZS2.mjs} +2 -2
  183. package/dist/{parameter-DvRQMKd2.mjs → parameter-Bucz3cLJ.mjs} +1 -1
  184. package/dist/{parameter-values-3eHpqznI.mjs → parameter-values-CRwVLpRD.mjs} +6 -5
  185. package/dist/{parse-enum-DAcTzG1q.mjs → parse-enum-CEXPuN9H.mjs} +1 -1
  186. package/dist/{parse-id-5BZGQ9Vd.mjs → parse-id-EXZan40V.mjs} +1 -1
  187. package/dist/{parse-ref-ByXc-pls.mjs → parse-ref-D28NvwpR.mjs} +1 -1
  188. package/dist/{parse-text-FgX60vIg.mjs → parse-text-C4LYzIFa.mjs} +1 -1
  189. package/dist/{path-XwdHlC23.mjs → path-BA_GGcGN.mjs} +4 -4
  190. package/dist/{poll-CgtcJopL.mjs → poll-vxN__4cz.mjs} +2 -2
  191. package/dist/predicates-0C0TsWcJ.mjs +219 -0
  192. package/dist/{preflight-ChTKhB8Y.mjs → preflight-4iy2qzsh.mjs} +3 -2
  193. package/dist/{process-aqXm5QgC.mjs → process-BQC3mK2k.mjs} +1 -1
  194. package/dist/{publish-C2-pK0cf.mjs → publish--I94MMjp.mjs} +13 -6
  195. package/dist/{pulse-CC81N2_0.mjs → pulse-B2AmIWsO.mjs} +11 -4
  196. package/dist/{pulse-JPrq6NFP.mjs → pulse-CPSpum5U.mjs} +2 -2
  197. package/dist/{query-pmw23KkL.mjs → query-C8w04b4F.mjs} +7 -6
  198. package/dist/{query-DkaUzmMa.mjs → query-O1b4qIr4.mjs} +17 -29
  199. package/dist/{remapping-C3htTzJn.mjs → remapping-BdX4nj-U.mjs} +5 -5
  200. package/dist/{remove-collection-p_t9rUUX.mjs → remove-collection-dltKu8LO.mjs} +3 -3
  201. package/dist/{render-C50k8-jc.mjs → render-DK2j4BwQ.mjs} +3 -2
  202. package/dist/{replace-BQBuFJ1B.mjs → replace-BfO6UFZN.mjs} +4 -4
  203. package/dist/{requirement-check-CpiVuL0H.mjs → requirement-check-1fcffJj6.mjs} +2 -2
  204. package/dist/{rescan-values-DOqXJ3qH.mjs → rescan-values-0-c5Gqtx.mjs} +5 -5
  205. package/dist/{rescan-values-Ctq8wpkI.mjs → rescan-values-7bbNa7WZ.mjs} +5 -5
  206. package/dist/{run-CtMbObQI.mjs → run-5b123gPi.mjs} +5 -4
  207. package/dist/{run-2xgPRuKR.mjs → run-BS_wUAsi.mjs} +6 -5
  208. package/dist/{run-Dn3e8G4x.mjs → run-Cf1nJahe.mjs} +4 -4
  209. package/dist/{runs-CgNoQW6M.mjs → runs-BJ4uy05c.mjs} +6 -6
  210. package/dist/{runtime-Y4VFWmmr.mjs → runtime-D5yfliIq.mjs} +51 -9
  211. package/dist/{scaffold-BuL0_ptz.mjs → scaffold-aUy3Ik4q.mjs} +4 -4
  212. package/dist/schema-Ds_j3N1I.mjs +283 -0
  213. package/dist/{schema-tables-BdXuo_lG.mjs → schema-tables-BuFF9Psy.mjs} +6 -6
  214. package/dist/{schemas-Dap3KFwY.mjs → schemas-DaDsur6B.mjs} +4 -4
  215. package/dist/{search-Dxcejw_v.mjs → search-BcJgmPU3.mjs} +6 -5
  216. package/dist/{search-DUmFK5Sy.mjs → search-CTZzUd_j.mjs} +7 -6
  217. package/dist/{segment-C_gl5ag6.mjs → segment-CWVASaM1.mjs} +1 -1
  218. package/dist/{segment-OsrbUDzw.mjs → segment-Cf-DrdjE.mjs} +3 -1
  219. package/dist/{segment-CebcXn2E.mjs → segment-zy4VVraQ.mjs} +6 -6
  220. package/dist/{send-DYdwcPTb.mjs → send-D0ikejQU.mjs} +3 -3
  221. package/dist/{set-xs8Vyvy2.mjs → set-CjAaw2ZL.mjs} +5 -6
  222. package/dist/{set-active-CVI_DDQi.mjs → set-active-BQjKw1wk.mjs} +4 -4
  223. package/dist/{set-bundle-DX5HqDji.mjs → set-bundle-6Qknum1I.mjs} +6 -6
  224. package/dist/{set-data-actions-DAI1XCQ2.mjs → set-data-actions-LcWEP4RO.mjs} +6 -6
  225. package/dist/{set-enabled-0QI-CHAT.mjs → set-enabled-C8Yww7Yv.mjs} +7 -7
  226. package/dist/{set-sensitivity-DqGkiLSr.mjs → set-sensitivity-OVcL2PbY.mjs} +6 -6
  227. package/dist/{setting-Bmry-y_l.mjs → setting-BsIBFvRP.mjs} +4 -4
  228. package/dist/{setup-CO7QV-ao.mjs → setup-BnczkG96.mjs} +1 -1
  229. package/dist/{setup-UM8N9Vh3.mjs → setup-BxUd_KYl.mjs} +5 -6
  230. package/dist/{signal-D8MH3LMN.mjs → signal-BNUlQjpc.mjs} +1 -1
  231. package/dist/{skill-list-D2FhhYS3.mjs → skill-list-BoJITnw8.mjs} +1 -1
  232. package/dist/{skills-CcLTm3_2.mjs → skills-BQBerRAk.mjs} +3 -2
  233. package/dist/{skills-BiSfuDqA.mjs → skills-CQRgUHI3.mjs} +3 -3
  234. package/dist/{slug-qeARO25o.mjs → slug-COdwq63O.mjs} +1 -1
  235. package/dist/snippet-CRLI3fYI.mjs +19 -0
  236. package/dist/{stash-e5cSvnj3.mjs → stash-BGqpcbSW.mjs} +5 -4
  237. package/dist/{status-BHiO3YBe.mjs → status-CcOJIgfB.mjs} +4 -4
  238. package/dist/{status-CH7BAWGe.mjs → status-_wPMW1wq.mjs} +3 -3
  239. package/dist/{subscription-DWleULhN.mjs → subscription-CqUIrnUJ.mjs} +6 -6
  240. package/dist/{subscriptions-DcCOA2p1.mjs → subscriptions-BP83ctRX.mjs} +6 -6
  241. package/dist/{summary-CipFA6M6.mjs → summary-Byz6Y1v-.mjs} +5 -5
  242. package/dist/{sync-schema-D7p-P8F6.mjs → sync-schema-BJ-AtcCq.mjs} +5 -5
  243. package/dist/{sync-schema-C45WO4db.mjs → sync-schema-DQBHc3bt.mjs} +6 -6
  244. package/dist/{sync-task-DNKG9Bk4.mjs → sync-task-Gf86CU9l.mjs} +1 -1
  245. package/dist/{table-BDsqRU7a.mjs → table-BNA9Or05.mjs} +3 -3
  246. package/dist/{table-CF7zW9cI.mjs → table-hgrX1i-R.mjs} +36 -5
  247. package/dist/table-nIdBLcz7.mjs +26 -0
  248. package/dist/{table-selector-flags-CHfrVpYQ.mjs → table-selector-flags-BpFFoSaT.mjs} +4 -3
  249. package/dist/{text-C2VwKF9D.mjs → text-8nOxfC17.mjs} +1 -1
  250. package/dist/timeline-Cw581KRT.mjs +21 -0
  251. package/dist/{timeline-event-BF1dqSnD.mjs → timeline-event-BHfAXJrB.mjs} +6 -6
  252. package/dist/{transform-DiW7bREn.mjs → transform-B62craFM.mjs} +76 -7
  253. package/dist/{transform-CWGJB8N_.mjs → transform-DKPtUV54.mjs} +2 -2
  254. package/dist/{transform-job-ClX8e5iO.mjs → transform-job-CDIb3B9o.mjs} +6 -1
  255. package/dist/{transform-job-BBZ5L46v.mjs → transform-job-D18XRLBK.mjs} +1 -1
  256. package/dist/transform-job-sCgr8r-t.mjs +22 -0
  257. package/dist/transform-l1oC8oUS.mjs +28 -0
  258. package/dist/{transform-tag-BYoguZfW.mjs → transform-tag-DZEvmwxY.mjs} +5 -5
  259. package/dist/transform-test-hxkx6M8c.mjs +16 -0
  260. package/dist/{transforms-C6MFnT4C.mjs → transforms-D1wwWdCY.mjs} +6 -6
  261. package/dist/{transport-k_fabWdF.mjs → transport-BWixD5Ww.mjs} +5 -4
  262. package/dist/{tree-C4Igf6xs.mjs → tree-IymKTTCK.mjs} +4 -3
  263. package/dist/{typed-schema-Bo0Q6C0S.mjs → typed-schema-gaIKntyu.mjs} +4 -4
  264. package/dist/{unpublish-EStytsEm.mjs → unpublish-BnP0O_S2.mjs} +3 -3
  265. package/dist/{unreferenced-DtUuyBrr.mjs → unreferenced-DDkcn8EQ.mjs} +6 -6
  266. package/dist/{update-DAG8m5f4.mjs → update-BAocDvnS.mjs} +7 -8
  267. package/dist/{update-DECiEVlk.mjs → update-BemK_tZ_.mjs} +10 -11
  268. package/dist/{update-B0Na11v-.mjs → update-Bfws2FN2.mjs} +7 -8
  269. package/dist/{update-FfeHReec.mjs → update-BiBazvzY.mjs} +10 -11
  270. package/dist/{update-DhgfReDD.mjs → update-BsEG1dRm.mjs} +7 -8
  271. package/dist/{update-BogvN2Mb.mjs → update-C0p1E-hs.mjs} +8 -8
  272. package/dist/{update-pk0b6vXU.mjs → update-CFkOEnUu.mjs} +5 -6
  273. package/dist/{update-DT8U29Kk.mjs → update-CKrLtJWZ.mjs} +7 -8
  274. package/dist/{update-Bba_zPSE.mjs → update-C_VV1SP8.mjs} +10 -11
  275. package/dist/{update-DkvIK-eL.mjs → update-CgJwzutJ.mjs} +7 -8
  276. package/dist/{update-BOg1jZ_s.mjs → update-Cga4lrX5.mjs} +11 -12
  277. package/dist/{update-z8ZGCg1r.mjs → update-CsH0lSpQ2.mjs} +7 -8
  278. package/dist/{update-HbggUPuy.mjs → update-CyfLXFKR.mjs} +5 -6
  279. package/dist/{update-CCLpNTPa.mjs → update-D9eA9UOf.mjs} +5 -6
  280. package/dist/{update-B1UXRPN-.mjs → update-D9fs-PSK.mjs} +8 -9
  281. package/dist/{update-DIBaY-fy.mjs → update-DL1Iex_M.mjs} +7 -8
  282. package/dist/{update-BXLY92om.mjs → update-DntKWUl2.mjs} +10 -11
  283. package/dist/{update-CciLWH_s.mjs → update-K8FwdJEV.mjs} +11 -11
  284. package/dist/{update-DT2ViMFr.mjs → update-ReSzm23m.mjs} +5 -6
  285. package/dist/{update-dashcard-m-hkIGUu.mjs → update-dashcard-Bpv45o1_.mjs} +7 -8
  286. package/dist/{update-VGgvQu7W.mjs → update-gAPjldWw.mjs} +5 -6
  287. package/dist/{upgrade-iETJ8fH2.mjs → upgrade-D3aDQZwd.mjs} +5 -4
  288. package/dist/{upload-CoK5LnyA.mjs → upload-B8HbBXb8.mjs} +3 -3
  289. package/dist/{upload-BF5pdKRN.mjs → upload-BAD_9okY.mjs} +2 -2
  290. package/dist/upload-BqJLNCb1.mjs +13 -0
  291. package/dist/{uuid-8Ha-8bDX.mjs → uuid-BIDBDPLI.mjs} +3 -2
  292. package/dist/{validate-BQ7vI8_x.mjs → validate-moGYlHxB.mjs} +8 -18
  293. package/dist/validate-query-Bf5FhhMI.mjs +72 -0
  294. package/dist/{values-BsgA7-6h.mjs → values-BDekphEw.mjs} +5 -5
  295. package/dist/{verify-cjUPMF_B.mjs → verify-BnZqq9Aw.mjs} +3 -3
  296. package/dist/{verify-CzBZbP4W.mjs → verify-CMTrDvAW.mjs} +3 -3
  297. package/dist/{verify-crIrbGZV.mjs → verify-k0WHgc16.mjs} +4 -3
  298. package/dist/{wait-mFUfpSaj.mjs → wait-C69z8PSw.mjs} +5 -5
  299. package/dist/{wait-flags-BnZ-dfE2.mjs → wait-flags-C3cu_O0w.mjs} +2 -2
  300. package/dist/{window-CfK_j6m1.mjs → window-D1w7B0vh.mjs} +1 -1
  301. package/package.json +1 -1
  302. package/skill-data/core/SKILL.md +18 -14
  303. package/skill-data/data-app/SKILL.md +16 -17
  304. package/skill-data/data-app/template/README.md +1 -1
  305. package/skill-data/data-app-actions/SKILL.md +3 -0
  306. package/skill-data/data-app-data/SKILL.md +6 -6
  307. package/skill-data/data-app-filters/SKILL.md +12 -4
  308. package/skill-data/data-app-ui/SKILL.md +11 -11
  309. package/skill-data/data-workflow/references/reusable-definitions.md +2 -2
  310. package/skill-data/git-sync/SKILL.md +13 -11
  311. package/skill-data/mbql/SKILL.md +3 -3
  312. package/skill-data/native-sql/SKILL.md +1 -1
  313. package/skill-data/native-sql/references/template-tags.md +1 -1
  314. package/dist/api-key-wGtJ8kYy.mjs +0 -13
  315. package/dist/body-M5qsJoub.mjs +0 -16
  316. package/dist/card-BWTjCE93.mjs +0 -25
  317. package/dist/collection-9_mj8rVX.mjs +0 -21
  318. package/dist/dashboard-BZV-0Qjp.mjs +0 -35
  319. package/dist/data-action-B7kRpvCH.mjs +0 -24
  320. package/dist/dependency-DDRGBlFL.mjs +0 -15
  321. package/dist/document-BeLf7NqP.mjs +0 -20
  322. package/dist/field-CQDz-6DK.mjs +0 -21
  323. package/dist/git-sync-D_T9oNC6.mjs +0 -29
  324. package/dist/glossary-BVK5DaLV.mjs +0 -14
  325. package/dist/snippet-DKARah-L.mjs +0 -19
  326. package/dist/table-BPYHnzyV.mjs +0 -26
  327. package/dist/timeline-C2m_QHxn.mjs +0 -21
  328. package/dist/transform-N_elgRuh.mjs +0 -28
  329. package/dist/transform-job-CTV6KRP2.mjs +0 -22
  330. package/dist/transform-test-jaBpHOM5.mjs +0 -16
  331. package/dist/upload-CqSGEZQW.mjs +0 -13
  332. package/dist/validate-query-YtH3d5BP.mjs +0 -38
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: core
3
- description: Foundations for driving Metabase from the terminal with the `mb` CLI — authentication and named profiles, the flag/output/`--json` conventions every command shares, JSON body input, command discovery via `--help` (add `--json` for machine-readable schemas), and the per-resource footguns (db, table, field, upload, content translation, card, dashboard, collection, segment, measure, timeline, alert, subscription, library, setting, search, eid). Load first for any `mb` task; it routes to the specialized skills for deeper work.
3
+ description: Foundations for driving Metabase from the terminal with the `mb` CLI — authentication and named profiles, the flag/output/`--json` conventions every command shares, JSON body input, command discovery via `--help` and `mb schema`, and the per-resource footguns (db, table, field, upload, content translation, card, dashboard, collection, segment, measure, timeline, alert, subscription, library, setting, search, eid). Load first for any `mb` task; it routes to the specialized skills for deeper work.
4
4
  allowed-tools: Read, Write, Edit, Bash, AskUserQuestion
5
5
  ---
6
6
 
@@ -13,10 +13,10 @@ Top-level command groups (run `mb <group> --help` to discover verbs):
13
13
  ```
14
14
  auth | db | table | field | upload | content-translation | query | card | dashboard | snippet | segment | measure | collection | library
15
15
  document | glossary | timeline | timeline-event | transform | transform-job | transform-tag | transform-test | alert | subscription | setting
16
- search | dependency | git-sync | data-action | data-app | setup | eid | uuid | upgrade | skills
16
+ search | dependency | git-sync | data-action | data-app | setup | eid | uuid | upgrade | skills | schema
17
17
  ```
18
18
 
19
- The conventions below — auth, flags, output, body input — hold across **every** group. Per-command flags and examples live in each command's `--help`; add `--json` for the machine-readable form with the output JSON Schema. A few flows have their own skills (see "Specialized skills"). When a card needs a query, prefer MBQL over native SQL (portable, pre-flight-validated — load `mbql`); fall back to native SQL when MBQL can't express it.
19
+ The conventions below — auth, flags, output, body input — hold across **every** group. A few flows have their own skills (see "Specialized skills"). When a card needs a query, prefer MBQL over native SQL (portable, pre-flight-validated — load `mbql`); fall back to SQL when MBQL can't express it.
20
20
 
21
21
  ## Auth & profiles
22
22
 
@@ -28,13 +28,13 @@ mb auth status --json # → {profile, present, url} for the d
28
28
  mb auth status --profile <name> --json # health probe for one profile
29
29
  ```
30
30
 
31
- `auth list` is the primary enumeration path — one call returns every profile with sanitized URL, an `authenticated` flag, and a probe `status` (`ok` / `auth-failed` / `network-error` / `server-error` / `not-probed`). Use it before asking which profile to pick.
31
+ `auth list` returns every profile in one call, with sanitized URL, an `authenticated` flag, and a probe `status` (`ok` / `auth-failed` / `network-error` / `server-error` / `not-probed`). Use it before asking which profile to pick.
32
32
 
33
33
  - One profile and intent doesn't disambiguate → use it.
34
34
  - Several → ask via `AskUserQuestion`, presenting the names from `auth list`.
35
35
  - Empty `data: []` → ask the user to run `mb auth login` themselves and tell you the profile name.
36
36
 
37
- Once a name is established, pass `--profile <name>` to **every** subsequent command. Profile names are arbitrary local labels (`prod`, `staging`).
37
+ Profile names are arbitrary local labels (`prod`, `staging`).
38
38
 
39
39
  ## Flag conventions
40
40
 
@@ -46,7 +46,7 @@ Once a name is established, pass `--profile <name>` to **every** subsequent comm
46
46
  ❌ mb table --profile prod list # → unknown command: prod
47
47
  ```
48
48
 
49
- **`--wait` for async operations.** `transform run`, `git-sync import`, and similar verbs return immediately by default. Pass `--wait` whenever the next step depends on completion — without it you race the operation and see "not ready" / transient connection refusals.
49
+ **`--wait` for async operations.** `transform run`, `git-sync import`, and similar verbs return immediately; pass `--wait` whenever the next step depends on completion — without it you race the operation and see "not ready" / transient connection refusals.
50
50
 
51
51
  **Some "lookup" verbs return JSON envelopes, not bare values.** `mb setting get <key>` returns `{"key": "...", "value": ...}`. Extract before reusing:
52
52
 
@@ -94,7 +94,7 @@ Verbs that take a payload accept it from one of four sources, **first non-empty
94
94
  3. stdin (auto-detected when piped; `--file -` names it explicitly)
95
95
  4. positional argument
96
96
 
97
- Exactly one required; passing more than one of `--body` / `--file` / a positional argument is rejected with a `ConfigError`.
97
+ Exactly one; more than one of `--body` / `--file` / a positional argument is a `ConfigError`.
98
98
 
99
99
  ```bash
100
100
  cat > ./.scratch/body.json <<'EOF'
@@ -105,7 +105,7 @@ mb <noun> create --file ./.scratch/body.json --profile <n> --json
105
105
 
106
106
  Single-quoted `'EOF'` stops the shell interpolating `$vars` inside the JSON.
107
107
 
108
- Write working files to **`./.scratch`** in the current directory (`mkdir -p ./.scratch` first), never `/tmp` — better permissions, they persist across the session, and the user can review them.
108
+ Write working files to **`./.scratch`** in the current directory (`mkdir -p ./.scratch` first), never `/tmp`: better permissions, kept for the session, reviewable by the user.
109
109
 
110
110
  ## Discovering commands and schemas
111
111
 
@@ -113,12 +113,13 @@ Cheapest source that answers the question wins:
113
113
 
114
114
  - What groups/verbs exist? → `mb --help`, then `mb <group> --help`. Add `--json` for a machine-readable `{command, description}` index (`mb --help --json` lists every command).
115
115
  - What flags does a command take? → `mb <command> --help` — flags with enums and defaults, examples, ~1 KB.
116
- - Output JSON Schema before parsing, JSON-body input schema before authoring, machine-readable arg types, min server version? → `mb <command> --help --json` — that command's full entry (`inputSchema` is the exact validator the command runs on the body; `null` when it takes none).
116
+ - Body shape before authoring, output before parsing? → `mb schema <command>`: the body's JSON Schema, a feature a field needs in its `description`; `--output` for output.
117
+ - Flags, arg types, needed features? → `mb <command> --help --json`.
117
118
 
118
119
  ```bash
119
120
  mb card query --help # flags, enums, defaults, examples
120
- mb card list --help --json | jq .outputSchema # output schema before parsing
121
- mb card create --help --json | jq .inputSchema # body schema before authoring
121
+ mb schema card create # body schema before authoring
122
+ mb schema card list --output # output schema before parsing
122
123
  mb transform --help --json | jq -r '.commands[].command' # verbs under "transform"
123
124
  ```
124
125
 
@@ -152,10 +153,10 @@ What `--help` doesn't say: footguns and non-obvious behaviors.
152
153
  <!-- requires: transforms -->
153
154
  - **transform.** Iterate with `transform update <id>`, never `delete` + `create` (keeps the row, `entity_id`, materialized table, and YAML filename — avoids `_2` suffixes and noisy git history). `transform run` needs `--wait` (or `--sync`, which also waits for the output table to register and returns `target_table_id`) or you get only `{run_id, final:null}`. (→ `transform`.)
154
155
  <!-- /requires -->
155
- - **setup is one-shot.** `mb setup` walks `/api/setup` for a **fresh** instance only — errors against an already-configured one. Mostly for bootstrapping local / e2e instances.
156
+ - **setup is one-shot.** `mb setup` walks `/api/setup` for a **fresh** instance only and errors on a configured one; mostly for bootstrapping local / e2e instances.
156
157
  - **eid** translates a string entity id → numeric id: `mb eid --model <model> <eid1,eid2> --json`. Entity ids are NanoIDs that can start with `-`, which the positional form misreads as a flag (shell quotes don't help) — for those, use `--body '{"entity_ids":{"card":["-…"]}}'` (the id is a JSON string value, immune to flag parsing).
157
158
  <!-- requires: library -->
158
- - **library.** The Library is a curated subtree (`library-data` "Data" + `library-metrics` "Metrics" under a `library` root): tables in **Data** lead data pickers and rank up in search; metrics in **Metrics** lead nav, search, and the query builder. `library get` shows its Data/Metrics collection ids; `library create` provisions it (idempotent). `library publish --table-ids/--db-ids/--schemas` publishes tables into Data, **resolving the Data collection and creating the Library if absent**; each `--schemas` entry is `<db-id>:<schema>` (`1:public`), not a bare name. `publish` cascades to upstream FK dependencies, `unpublish` to downstream dependents; both need **admin or data-analyst** (Curate alone won't publish) and exit **403** without write **and** query permission on every affected table. `table get`/`table list` carry `is_published` (`collection_id` under `--full`). Publishing leaves the Data collection out of git-sync scope: on a remote-sync instance, `mb git-sync add-collection <data-collection-id>` makes exports carry published tables' metadata (see `git-sync`).
159
+ - **library.** The Library is a curated subtree (`library-data` "Data" + `library-metrics` "Metrics" under a `library` root): tables in **Data** lead data pickers and rank up in search; metrics in **Metrics** lead nav, search, and the query builder. `library get` shows its Data/Metrics collection ids; `library create` provisions it (idempotent). `library publish --table-ids/--db-ids/--schemas` publishes tables into Data, **resolving the Data collection and creating the Library if absent**; each `--schemas` entry is `<db-id>:<schema>` (`1:public`), not a bare name. `publish` cascades to upstream FK dependencies, `unpublish` to downstream dependents; both need **admin or data-analyst** (Curate alone won't publish) and exit **403** without write **and** query permission on every affected table. `table get`/`table list` carry `is_published` (`collection_id` under `--full`). On a remote-sync instance, `mb git-sync add-collection <library-id>` (the root) makes exports carry published tables' metadata (see `git-sync`).
159
160
  <!-- /requires -->
160
161
  - **query / uuid.** `mb query` runs MBQL (`--dry-run` → run; `mbql`). `mb uuid --count <n>` mints an aggregation's `lib/uuid` (`mbql`) and document node `_id`s (`document`).
161
162
  <!-- requires: library, tableListPublishedFilter -->
@@ -164,7 +165,7 @@ What `--help` doesn't say: footguns and non-obvious behaviors.
164
165
 
165
166
  ## Specialized skills (load on demand)
166
167
 
167
- This file is enough for any single-command task. For anything deeper, load the relevant skill **proactively** — don't wing an MBQL body, a transform body, or the git-sync workflow from this overview. Load via `mb skills get <name>`.
168
+ This file is enough for any single-command task. For anything deeper, load the relevant skill (`mb skills get <name>`) **proactively** — don't wing it from this overview.
168
169
 
169
170
  - **`mbql`** — authoring/fixing any MBQL query body (`mb query`, card `dataset_query`, transform `source.query`, measure/segment `definition`); reading `--dry-run` errors. The query-body reference.
170
171
  - **`native-sql`** — authoring a native SQL `dataset_query` with parameters: template tags, field filters vs. raw variables, snippets, card references, and wiring a tag to a dashboard filter. The SQL fallback when MBQL can't express it (`mbql` first).
@@ -184,6 +185,9 @@ This file is enough for any single-command task. For anything deeper, load the r
184
185
  <!-- /requires -->
185
186
  <!-- requires: remoteSync -->
186
187
  - **`git-sync`** — round-tripping content to/from a git remote.
188
+ <!-- /requires -->
189
+ <!-- requires: dataApps -->
190
+ - **`data-app`** — data apps: create, preview, publish (start here).
187
191
  <!-- /requires -->
188
192
  - **`data-workflow`** — the guided, end-to-end data workflow: investigate raw data, build clean analysis-ready tables, define reusable segments/measures/metrics, answer questions, build dashboards. **Start here when the user states a goal rather than a single verb** — "make sense of my data", "build a data model", "be my data analyst". It detects where the data is and routes to the right stage.
189
193
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: data-app
3
- description: Create, preview, publish and manage a Metabase data app — a React app Metabase serves at /apps/<slug> — create it, scaffold its project, run the dev preview and read its diagnostics feed, build and publish the bundle, turn it on or off, move it to another instance, delete it. Triggers — "build a data app", "make an internal tool in Metabase", "scaffold a data app", "publish my data app", "update the app", "the app is blank". Start here; the UI is in `data-app-ui`, questions and charts in `data-app-data`, filter controls in `data-app-filters`, writes in `data-app-actions`.
3
+ description: Create, preview, publish and manage a Metabase data app — a React app Metabase serves at /apps/<slug> — create it, scaffold its project, run the dev preview and read its diagnostics feed, build and publish the bundle, turn it off, move it to another instance, delete it. Triggers — "build a data app", "make an internal tool in Metabase", "scaffold a data app", "publish my data app", "update the app", "the app is blank". Start here; the UI is in `data-app-ui`, questions and charts in `data-app-data`, filter controls in `data-app-filters`, writes in `data-app-actions`.
4
4
  allowed-tools: Read, Write, Edit, Bash, AskUserQuestion
5
5
  requires: [dataApps]
6
6
  ---
@@ -17,11 +17,11 @@ The work, in order:
17
17
  4. Start the dev preview (_Dev preview_), then write the UI under `src/` (`data-app-ui`; filter controls in `data-app-filters`).
18
18
  5. Build and publish (_Build and publish_).
19
19
 
20
- For every later change: regenerate `src/metabase.ts` if the app's collection changed, then build and set the bundle. Metabase receives only the built bundle; the source stays in the project directory, under git if the user keeps one.
20
+ For every later change: regenerate `src/metabase.ts` if the app's collection changed, then build and set the bundle; in a synced repository, then export and commit the source as in _Move to another instance_. Metabase receives only the built bundle; the source stays in the project directory, which is the app's folder in the synced git repository when there is one (_Scaffold and install_).
21
21
 
22
22
  ## Before you start
23
23
 
24
- - Use an **admin** profile — only admins create apps and write the app's collection. `mb auth status --json | jq .user.isAdmin` prints `true` for one; otherwise ask the user for an admin profile (`mb auth list`).
24
+ - Use an **admin** profile — only admins create apps and write the app's collection. `mb auth status --profile <name> --json | jq .user.isAdmin` prints `true` for one, and every command below takes that `--profile`; otherwise ask the user for an admin profile (`mb auth list`).
25
25
  - Run `mb data-app list`. A failure means the instance has no data apps: stop and tell the user.
26
26
  - Settle the **slug**, the `/apps/<slug>` URL: lowercase letters, digits and single dashes (`sales-overview`), at most 100 characters. If the app's purpose is unclear, ask "what's this app for?" and propose one.
27
27
 
@@ -31,18 +31,18 @@ For every later change: regenerate `src/metabase.ts` if the app's collection cha
31
31
  mb data-app create --body '{"name":"sales-overview","display_name":"Sales overview"}' --json
32
32
  ```
33
33
 
34
- `name` is the slug; `display_name` and `description` are optional. The app starts disabled, with an empty bundle. The answer's `resource_collection_id` is the id of the app's collection — note it. A `409` means the slug is taken: `mb data-app list` shows that app; reuse it or pick another slug.
34
+ `name` is the slug; `display_name` and `description` are optional. The app starts enabled, with an empty bundle, open to admins until a group is assigned. The answer's `resource_collection_id` is the id of the app's collection — note it. A `409` means the slug is taken: `mb data-app list` shows that app; reuse it or pick another slug.
35
35
 
36
36
  ## Scaffold and install
37
37
 
38
38
  ```bash
39
- mb data-app scaffold sales-overview && cd sales-overview
39
+ mb data-app scaffold <repo>/data_apps/sales-overview && cd <repo>/data_apps/sales-overview
40
40
  ```
41
41
 
42
- `scaffold [dir]` takes a **directory**, not the slug: it copies the Vite + React + TypeScript template into a new or empty directory (the current one when omitted) and needs no server. Then install the SDK, then the rest:
42
+ `scaffold [dir]` takes a **directory**, not the slug: it copies the Vite + React + TypeScript template into a new or empty directory (the current one when omitted) and needs no server. When the instance syncs with a git repository (`mb git-sync status`), the directory is `data_apps/<slug>/` in a local clone of that repository (ask the user for its path, or clone the remote), where an export writes the app's `data_app.yaml` and bundle: an export leaves the source alone and an import ignores it, so the repository keeps the source with the app. `scaffold` needs an empty directory, so scaffold before the app's first export; for an app already in the repository, scaffold into a new directory and copy its files, dotfiles included (`cp -R <dir>/. data_apps/<slug>/`), in beside `data_app.yaml` and `dist/`; check `.gitignore` is there before `npm install`. Without a repository, use any new directory, and once remote sync is set up, move the project (without `node_modules/`, `dist/`, `.env.local` and a hand-written `data_app.yaml`) into `data_apps/<slug>/` before committing its source. Then install the SDK, then the rest:
43
43
 
44
44
  ```bash
45
- npm install @metabase/embedding-sdk-react@<major>-beta # <major>: 66 for a version tag v1.66.x (`mb auth status --json | jq -r .version.tag`)
45
+ npm install @metabase/embedding-sdk-react@<major>-beta # <major>: 66 for a version tag v1.66.x (`mb auth status --profile <name> --json | jq -r .version.tag`)
46
46
  npm install && npm run typecheck && npm run build
47
47
  ```
48
48
 
@@ -58,12 +58,12 @@ The template is the bundle contract. Don't edit `package.json` by hand, and neve
58
58
 
59
59
  ```bash
60
60
  ( umask 077
61
- URL=$(mb auth status --json | jq -r .url)
62
- KEY=$(mb api-key create --body '{"name":"data-app <slug> dev","group_id":2}' --json | jq -r .unmasked_key)
61
+ URL=$(mb auth status --profile <name> --json | jq -r .url)
62
+ KEY=$(mb api-key create --profile <name> --body '{"name":"data-app <slug> dev","group_id":2}' --json | jq -r .unmasked_key)
63
63
  [ -n "$KEY" ] && [ "$KEY" != null ] && printf 'DATA_APP_MB_URL=%s\nDATA_APP_MB_API_KEY=%s\n' "$URL" "$KEY" > .env.local && echo written )
64
64
  ```
65
65
 
66
- Group `2` is Administrators. Never print `.env.local`. Delete the key at handoff (`mb api-key list`, then `mb api-key delete <id> --yes`); the next session writes a new one. A CORS error means the instance doesn't allow the preview's origin: ask the user to add `http://localhost:5174` under Admin → Embedding → Embedded analytics SDK → CORS.
66
+ Group `2` is Administrators. Never print `.env.local`. Delete the key at handoff (`mb api-key list --profile <name>`, then `mb api-key delete <id> --yes --profile <name>`); the next session writes a new one. Metabase allows any localhost origin unless `MB_DISABLE_CORS_ON_LOCALHOST` is set, so a CORS error from the preview means it is: ask the user to unset it on this instance. Otherwise check `DATA_APP_MB_URL`.
67
67
 
68
68
  ### The diagnostics feed
69
69
 
@@ -96,7 +96,7 @@ curl -s "http://localhost:5174/__data-app/diagnostics?startEventId=0"
96
96
  - `clients: 0` means no tab is open, so empty `entries` prove nothing.
97
97
  - Before an edit, note `nextEventId`; after it, read again with that value as `startEventId`.
98
98
  - The feed answers for the running bundle: entries from replaced builds are counted in `staleEntries`, and anything still broken reports again under the new `buildId` (`?includeStale=true` shows the withheld ones). A failed build doesn't advance `buildId` and doesn't reach the feed — read the `npm run dev` terminal.
99
- - Quote `summary` as it is when you report it, and follow `hint` when there is one — except that you never add the Metabase URL to `allowed_hosts`. The feed keeps the last 200 entries; `curl -X DELETE …/__data-app/diagnostics` clears it.
99
+ - Quote `summary` as it is when you report it, and follow `hint` when there is one — except that you never add the Metabase URL to `allowed_hosts`, and in a synced repository you set `allowed_hosts` with `mb data-app update`, export, and pull the export branch (_Move to another instance_) instead of editing `data_app.yaml`. A `frame-src` or `form-action` block can't be lifted in the preview (`data-app-ui`). The feed keeps the last 200 entries; `curl -X DELETE …/__data-app/diagnostics` clears it.
100
100
 
101
101
  | `kind` | Fix |
102
102
  | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
@@ -110,12 +110,11 @@ curl -s "http://localhost:5174/__data-app/diagnostics?startEventId=0"
110
110
  ```bash
111
111
  npm run typecheck && npm run build
112
112
  mb data-app set-bundle sales-overview
113
- mb data-app set-enabled sales-overview true # the first time
114
113
  ```
115
114
 
116
- `set-bundle` uploads `dist/index.js` (`--bundle` for another path) and replaces the app's bundle, live at once. A new app is disabled until `set-enabled` turns it on. Give the user `<metabase-url>/apps/<slug>`.
115
+ `set-bundle` uploads `dist/index.js` (`--bundle` for another path) and replaces the app's bundle, live at once. A new app is on from the start; `mb data-app set-enabled <slug> false` turns it off, which only the user decides. An app turned off opens only in Data Studio → Data apps → the app → Overview; `/apps/<slug>` says "not found", for admins too. Give the user `<metabase-url>/apps/<slug>`.
117
116
 
118
- Admins open every app, enabled or not. Anyone else needs the app enabled (otherwise "not found") and one of their groups assigned to it (otherwise "You don't have access"), which only the user can do (Admin → Settings → Data apps → the app → Groups). A group reaches the app's collection and nothing more, so a viewer also needs data access to the tables its questions read.
117
+ A viewer needs one of their groups assigned to the app (otherwise "You don't have access"), which only the user can do (Data Studio → Data apps → the app → Permissions). A group reaches the app's collection and nothing more, so a viewer also needs data access to the tables its questions read.
119
118
 
120
119
  ## Pitfalls
121
120
 
@@ -133,6 +132,6 @@ Admins open every app, enabled or not. Anyone else needs the app enabled (otherw
133
132
  ## Change, move, delete
134
133
 
135
134
  - **Turn off:** `mb data-app set-enabled <slug> false`. **Rename:** `mb data-app update <slug> --body '{"display_name":"…"}'` (also `description`, `allowed_hosts`).
136
- - **Outdated:** an app Metabase lists as _Outdated_ targets an older data-app contract: it won't open for anyone, and only admins see it listed, until it's migrated. Tell the user; don't change its code ad hoc.
137
- - **Move to another instance:** the app, its collection and what the collection holds are git-synced content. `mb git-sync export` (`git-sync`) writes `data_apps/<slug>/` with the bundle, and the collection under `collections/data_apps/`; importing the repository on the other instance recreates them with the same entity IDs, so the same `src/metabase.ts` and bundle work there. On a read-only instance the app's collection can't be edited: change it where it was built and export again.
138
- - **Delete:** confirm with the user, then `mb data-app delete <slug> --yes` — it removes the app, its bundle, its collection and the questions and actions in it. The project directory stays.
135
+ - **Outdated:** an app Data Studio lists as _Outdated_ targets an older data-app contract: it won't open for anyone, and only admins see it, until it's migrated. Tell the user; don't change its code ad hoc.
136
+ - **Move to another instance:** the app, its collection and what the collection holds are git-synced content. `mb git-sync export` (`git-sync`) writes `data_apps/<slug>/` with the bundle, and the collection under `collections/data_apps/`; importing the repository on the other instance recreates them with the same entity IDs, so the same `src/metabase.ts` works there. The first import there creates the app on, like `create`, open to admins until the user assigns groups; later imports keep its on/off state. The import doesn't check that the questions' tables are published on that instance, so the Library content they read goes in the same export: flag the Library root once (`mb git-sync add-collection <library-id>`, `git-sync`), or its metrics, measures and segments stay out of the repository; a data action needs its database's toggle on there too. Once the app is done, commit its source in `data_apps/<slug>/` on the branch you exported to, after the export. Pull the export branch: `git restore` a modified `dist/index.js`, then `git fetch`, `git switch <branch>`, `git pull --ff-only`. Then `git add data_apps/<slug>`, commit and push, and `mb git-sync import` whatever a cached `has-remote-changes` says, or the next export ends in `conflict`. The export writes `data_app.yaml` and `dist/index.js`, the bundle you uploaded; never edit or commit those two yourself, and when a fresh build changes `dist/index.js`, set the bundle and export again instead. The template's `.gitignore` keeps `node_modules/` and `.env.local` out. On a read-only synced instance only `set-enabled` works: creating, bundling, updating and deleting an app answer 403, and so does editing its collection; change it where it was built and export again.
137
+ - **Delete:** confirm with the user, then `mb data-app delete <slug> --yes` — it removes the app, its bundle, its collection and the questions and actions in it. The project directory stays: in a synced repository, the next export removes the app's `data_app.yaml`, bundle and collection files; after that export, switch and pull the same way, delete the source in `data_apps/<slug>/` in a commit of your own, push, and import.
@@ -7,7 +7,7 @@ and the `mb` data-app skills.
7
7
  ## Install
8
8
 
9
9
  Install the SDK tag matching the Metabase instance first, then the rest.
10
- `<major>` is 66 for a version tag v1.66.x (`mb auth status --json | jq -r .version.tag`);
10
+ `<major>` is 66 for a version tag v1.66.x (`mb auth status --profile <name> --json | jq -r .version.tag`);
11
11
  `package.json` asks for any version, which would install `latest`.
12
12
 
13
13
  ```bash
@@ -13,6 +13,8 @@ An **action** is a saved, parameterized SQL write — `INSERT`, `UPDATE` or `DEL
13
13
  2. Regenerate `src/metabase.ts` (`data-app-data`).
14
14
  3. Pass `actions.<name>` from it to `useAction`.
15
15
 
16
+ An action in the app's collection can't be archived: to drop one, move it to a data actions collection (`mb data-action update <id> --body '{"collection_id":<id>}'`), then archive it.
17
+
16
18
  Each action there lists its parameters: the parameter slug (the key `execute` takes), `displayName` (a label, never a key), `jsType` (`string`, `number`, `Date`, `boolean` or `unknown`) and `required`. Never fake a call to an action `src/metabase.ts` doesn't list.
17
19
 
18
20
  ## The hook
@@ -83,6 +85,7 @@ export function NoteForm({ orderId, onSaved }: { orderId: number; onSaved: () =>
83
85
  After a write succeeds, refetch every query on screen it could have changed: `await Promise.all([orders.refetch(), totals.refetch()])`, the `refetch` that `useMetabaseQuery` returns.
84
86
 
85
87
  - Those queries live in a component above the trigger, which passes the refresh down; a query in the trigger's own component or below it can't be wired to it.
88
+ - A `StaticQuestion` panel has no `refetch`: give it a `key` the parent bumps after the write, which remounts it and runs the question again.
86
89
  - Await the refresh before clearing the form or closing the modal, so stale rows never flash.
87
90
  - Never prepend `result` to a local list instead: ids, timestamps and computed columns come from the server.
88
91
 
@@ -15,11 +15,11 @@ A data app runs only the questions and actions in **the app's collection** (`dat
15
15
 
16
16
  ## Sources: the semantic layer only
17
17
 
18
- An app reads only what the user curated in the Library. These are hard rules:
18
+ An app reads only what the user curated in the Library. Metabase refuses a question in the app's collection that breaks these rules:
19
19
 
20
- - **Tables published to the Library**, and nothing else: `mb table list --published-only --json`. That covers the question's source table, every join, and every table reached through a foreign key column.
21
- - **Metrics in the Library's Metrics collection** (its id is in `mb library get`; list it with `mb collection items <id> --json`), and the segments and measures of published tables (`mb segment list`, `mb measure list`). Prefer one that matches the concept over rebuilding it with filters and thresholds of your own.
22
- - **Other questions in the app's collection**, as a source. Never a question, model or metric from anywhere else.
20
+ - **Tables published to the Library**, and nothing else: `mb table list --published-only --json`. That covers the question's source table, every join, every table reached through a foreign key column, the tables behind its segments and measures, and the tables whose columns Metabase shows in place of a foreign key.
21
+ - **Metrics in the Library's Metrics collection**, as aggregations (its id is in `mb library get`; list it with `mb collection items <id> --json`), and the segments and measures of published tables (`mb segment list`, `mb measure list`). Prefer one that matches the concept over rebuilding it with filters and thresholds of your own.
22
+ - **No other question as a source** — not a question in the app's collection, a model or a metric. Every question starts from a published table.
23
23
  - **MBQL only**, never native SQL.
24
24
 
25
25
  When the app needs a table or metric the Library lacks, stop: name what's missing and ask the user to publish the table or move the metric into the Library. Don't publish it yourself unless the user says to, and never work around the gap with an unpublished table, a copy of the data, or SQL.
@@ -44,7 +44,7 @@ mb card query <id> --json --max-bytes 0 | jq '{row_count, first: .data.rows[0]}'
44
44
  Plan the questions from what the app shows and picks from — each chart, KPI, table and filter option list needs one, and several panels may share one. Each takes one of two shapes:
45
45
 
46
46
  - **Aggregated**, when the breakdown is fixed: revenue by month, top products. Runtime clauses can then name only the columns it returns, its breakouts and aggregations.
47
- - **Rows**, when a runtime filter targets a column an aggregation would drop — revenue by month, filtered by product category. The question returns the raw columns (`CREATED_AT`, `TOTAL`, `CATEGORY`), and the hook filters and aggregates them at runtime (_Runtime clauses_).
47
+ - **Rows**, when a runtime filter targets a column an aggregation would drop — revenue by month, filtered by product category. The question returns the raw columns (`CREATED_AT`, `TOTAL`, `CATEGORY`), and the hook filters and aggregates them at runtime (_Runtime clauses_). When a Library metric or measure defines the number, never rebuild it from rows: filter an aggregated question instead (_Runtime clauses_).
48
48
 
49
49
  Also:
50
50
 
@@ -150,7 +150,7 @@ const { query } = useMetabaseQueryObject(queries.orderRows, {
150
150
  - `filter(...)` takes one value: `filter(col, "=", [a, b])` compiles but is not an IN filter, and several filters AND together. So a filter picks one value or All; for a multi-select, ask the user whether a single choice will do.
151
151
  - Text operators are case-sensitive, with no option to change it: say so in the search box's placeholder.
152
152
  - `time-interval` (`[-30, "day"]`, `[0, "year"]` for this year) is relative to when the query runs: a fixed window belongs in the question, not the hook.
153
- - On a rows question, aggregate with any of `aggregations.count`, `sum`, `avg`, `median`, `min`, `max`, `distinct`, naming each result (`{ name: "revenue" }`) to order or read it by — two unnamed results of one operator collide, and generated names such as `sum_2` aren't stable. On an aggregated question, re-aggregate only sums and counts, which add up.
153
+ - On a rows question, aggregate with any of `aggregations.count`, `sum`, `avg`, `median`, `min`, `max`, `distinct`, naming each result (`{ name: "revenue" }`) to order or read it by — two unnamed results of one operator collide, and generated names such as `sum_2` aren't stable. Never aggregate an aggregated question at runtime: a sum over its rows is wrong for distinct counts, averages, ratios and levels such as MRR. A runtime filter only picks its rows. So a date filter on it offers presets of whole, complete buckets at the question's own grain (Last month, Last 12 months on a month question), each range from the first bucket's start to the last bucket's start, and no Custom range; a KPI shows one bucket, as `scalar` (a `smartscalar` filtered to one bucket loses its comparison), with no All time. A filter on a column needs a question broken out by that column too; its All is no clause when the panel shows that column (a series per value), and otherwise a second question without that breakout, both hooks called and switched with `enabled`. When a request needs more, say so.
154
154
  - A sort the user picks: map each choice to a column explicitly (`{ revenue: "revenue", orders: "order_count" }`) rather than passing the user's value through.
155
155
  - `enabled: false` holds the query back, for example until the user picks an item.
156
156
  - Write the arrays inline (spreads are fine): the hooks compare by content, so a new array per render doesn't refetch. A clauses object built outside the call needs `as const`, or `aggregations` loses the tuple type `breakouts` need; a shared `ReturnType<typeof filter>[]` built with `push` breaks the operator types the same way.
@@ -19,12 +19,13 @@ Before any control, write one row per filter: the question that gives its option
19
19
  | Plan | orders broken out by plan | plan text | orders | revenue, stock |
20
20
 
21
21
  - A filter in the page's global bar changes every panel that can take it. One that can't reach a panel is scoped to its section, visibly, or left out. A KPI and its detail table about the same thing take the same filters, unless the UI says why not.
22
+ - A KPI on an aggregated question shows one complete bucket (`data-app-data`, _Runtime clauses_): it gets its own control of one-bucket presets starting at the last complete bucket, never All time, rather than a shared bar's ranges.
22
23
  - One date control per scope. Never a global date range beside a page-specific date where one of the two does nothing; a page whose cards use another date column gets one control for that column.
23
24
  - Build each panel's clauses from its own question's columns, inline at its hook call. Never reuse an array built for another question.
24
25
 
25
26
  ## Values and options
26
27
 
27
- - Every filter starts at **All**. Keep it as `"all"` in state and send it as no clause (`[]`); treat `""`, `null` and `undefined` the same way. In a Yes/No/All filter, Yes and No each filter (`true`, `false`), and only All sends nothing.
28
+ - Every filter starts at **All**, except a KPI's own date control (above). Keep it as `"all"` in state and send it as no clause (`[]`), except on an aggregated question whose panel doesn't show the filtered column, where All is its own question (`data-app-data`, _Runtime clauses_); treat `""`, `null` and `undefined` the same way. In a Yes/No/All filter, Yes and No each filter (`true`, `false`), and only All sends nothing.
28
29
  - Options come at runtime from their own question in the app's collection, broken out on the filtered column — never hardcoded unless the user gave a closed list. Dedupe them from its rows, and keep the label apart from the value.
29
30
  - Filter by a stable raw value (an id) and show its label; never filter by a display label when an id exists, and never feed free text into an exact-id filter.
30
31
  - All stays selectable while options load, come back empty or fail; don't disable the control. When fresh options no longer hold the selected value, reset to All:
@@ -48,7 +49,7 @@ Any list whose options come from a question — an id shown as a name, a categor
48
49
 
49
50
  ## Date ranges
50
51
 
51
- A preset bar lists the durations, then All time, then Custom last — leave Custom out only if the user asked for fixed presets. Every option ends in the same `[start, end]` range (All time is `[null, null]`), so one `between` filter serves them all. Custom is `DateRangePopover` around your own trigger, or `DateRangeCalendar` inline, both from `@metabase/embedding-sdk-react/data-app`. No date-picker library and no `<input type="date">` for a range.
52
+ A preset bar lists the durations, then All time, then Custom last — leave Custom out only if the user asked for fixed presets, or the bar filters an aggregated question, which offers only whole, complete buckets and no All time on a KPI (`data-app-data`, _Runtime clauses_). Every option ends in the same `[start, end]` range (All time is `[null, null]`), so one `between` filter serves them all. Custom is `DateRangePopover` around your own trigger, or `DateRangeCalendar` inline, both from `@metabase/embedding-sdk-react/data-app`. No date-picker library and no `<input type="date">` for a range.
52
53
 
53
54
  ```tsx
54
55
  import { DateRangePopover, filter, useDateFormatter } from "@metabase/embedding-sdk-react/data-app";
@@ -68,7 +69,7 @@ const [start, end] = range;
68
69
  - Label it with `formatDateRange(range)` (or `formatDate` for one end), which formats in the instance's locale; both take a dayjs `format`, and `formatDateRange` a `separator`. It returns `""` until the SDK loads. Never `new Date("YYYY-MM-DD")`: a date-only string parses as UTC and shows the previous day west of Greenwich.
69
70
  - The popover closes once both ends are picked (`closeOnSelect`); `opened` and `onOpenedChange` control it yourself. Also `defaultValue`, `minDate` and `maxDate` (`YYYY-MM-DD`), `numberOfColumns` (default 2) and `position` (default `bottom-start`). `DateRangeCalendar` takes the same value props plus `className` and `style`.
70
71
 
71
- A preset's range, from local dates — never `toISOString()`, which shifts the day:
72
+ A preset's range, from local dates — never `toISOString()`, which shifts the day. `lastMonths` is a rolling window, for a rows question; on a month question, `lastCompleteMonths` gives bucket starts:
72
73
 
73
74
  ```ts
74
75
  const ymd = (d: Date) =>
@@ -78,10 +79,17 @@ const lastMonths = (n: number): [string, string] => {
78
79
  const start = new Date(end.getFullYear(), end.getMonth() - n, end.getDate());
79
80
  return [ymd(start), ymd(end)];
80
81
  };
82
+ const lastCompleteMonths = (n: number): [string, string] => {
83
+ const now = new Date();
84
+ return [
85
+ ymd(new Date(now.getFullYear(), now.getMonth() - n, 1)),
86
+ ymd(new Date(now.getFullYear(), now.getMonth() - 1, 1)),
87
+ ];
88
+ };
81
89
  ```
82
90
 
83
91
  **A single date or a date-time** is the one case for a library: `npm install react-datepicker`, import `react-datepicker/dist/react-datepicker.css`, type the callback (`(date: Date | null) => …`), and convert with `ymd` above. Pass `null`, never `new Date("")`, for an empty value.
84
92
 
85
93
  ## Check
86
94
 
87
- For every visible filter: All runs the unfiltered question; its options come from a question; each option changes every panel the contract says it reaches, and no other; a preset bar ends with Custom; and no scope has two date controls. Search the filter controls for `<select` and `type="date"` — an action form keeps its date inputs. Then build and publish (`data-app`, _Build and publish_).
95
+ For every visible filter except a KPI's own date control: All runs the unfiltered question, or its All question; its options come from a question; each option changes every panel the contract says it reaches, and no other; a preset bar ends with Custom unless it filters an aggregated question; and no scope has two date controls. Search the filter controls for `<select` and `type="date"` — an action form keeps its date inputs. Then build and publish (`data-app`, _Build and publish_).
@@ -35,7 +35,7 @@ Everything here runs inside the sandbox, the same in the dev preview as in Metab
35
35
  | `navigator.geolocation` and other device APIs | Nothing. |
36
36
  | Key, input, composition and clipboard listeners (`keydown`, `paste`, `input`, …) on `window` or `document` | A React handler (`onKeyDown`, `onPaste`, …) on your own element. |
37
37
 
38
- **External APIs.** A raw `fetch` reaches only the origins in the app's `allowed_hosts`, exact or with a `*.` subdomain wildcard: `mb data-app update <slug> --body '{"allowed_hosts":["https://api.example.com"]}'`, plus an `allowed_hosts:` list in `data_app.yaml` beside `package.json` for the preview, then restart `npm run dev`. Never list the Metabase URL. In Metabase the same list governs `<iframe src>` and native `<form action>` submits, and the target must allow framing; the preview blocks both. Use `<form onSubmit>` with `preventDefault`: a native submit navigates the app away.
38
+ **External APIs.** A raw `fetch` reaches only the origins in the app's `allowed_hosts`, exact or with a `*.` subdomain wildcard: `mb data-app update <slug> --body '{"allowed_hosts":["https://api.example.com"]}'`. The preview reads the `allowed_hosts:` list in `data_app.yaml` beside `package.json`: in a synced repository that file is the app's exported manifest, so export again and pull the export branch (`data-app`, _Move to another instance_) to refresh it rather than writing it; without one, write the list there yourself. Then restart `npm run dev`. Never list the Metabase URL. In Metabase the same list governs `<iframe src>` and native `<form action>` submits, and the target must allow framing; the preview blocks both. Use `<form onSubmit>` with `preventDefault`: a native submit navigates the app away.
39
39
 
40
40
  ## Layout
41
41
 
@@ -69,12 +69,12 @@ export function AppError({ message }: SdkErrorComponentProps) {
69
69
 
70
70
  ## SDK components
71
71
 
72
- | Import | For |
73
- | -------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
74
- | `StaticQuestion` | A chart, KPI or table without drill-through — the default. |
75
- | `InteractiveQuestion` | One meant to be explored: drill-through, chart switching, the editor. |
76
- | `StaticDashboard`, `InteractiveDashboard`, `EditableDashboard` | A dashboard. |
77
- | `CreateQuestion`, `MetabotQuestion`, `CollectionBrowser`, `CreateDashboardModal` | Authoring flows, rarely in an app. |
72
+ | Import | For |
73
+ | --------------------- | --------------------------------------------------------------------------------------- |
74
+ | `StaticQuestion` | A chart, KPI or table without drill-through — the default. |
75
+ | `InteractiveQuestion` | One meant to be explored: drill-through, chart switching; pass `isSaveEnabled={false}`. |
76
+
77
+ Both take a question in the app's collection. Dashboards and the authoring components (`CreateQuestion`, `CreateDashboardModal`, saving from the editor) don't work in an app: its collection can't hold a dashboard, and an app can't save content.
78
78
 
79
79
  ## A Metabase chart or your own
80
80
 
@@ -82,13 +82,13 @@ Ask "can Metabase draw this at all?", never "which looks closer to the design?"
82
82
 
83
83
  - **Yes** — bar, line, area, combo, row, pie, scalar, smartscalar, gauge, progress, funnel, scatter, waterfall, boxplot, sankey, pivot, map, tables: render it with `useMetabaseQueryObject` and `StaticQuestion` (`data-app-data`). It keeps the instance's formatting, tooltips, theming and drill-through. Styling is never a reason to draw it yourself.
84
84
  - **No**, or the user asked for a custom one — scorecards, timelines, calendar grids, one visual from several queries: draw it from `useMetabaseQuery` rows, with SVG or a chart library the app already has.
85
- - A KPI is `scalar`, `smartscalar`, `gauge` or `progress`; put what Metabase doesn't draw (a caption, stars) beside it.
85
+ - A KPI is `scalar`, `smartscalar`, `gauge` or `progress` — on a question filtered at runtime to one bucket, `scalar` (a `smartscalar` loses its comparison); put what Metabase doesn't draw (a caption, stars) beside it.
86
86
  - `InteractiveQuestion`: pass `isSaveEnabled={false}`, and `title={false}` in a panel with its own heading. Children replace its layout: `<InteractiveQuestion card={card}><InteractiveQuestion.QuestionVisualization height={360} /></InteractiveQuestion>` is the chart alone.
87
87
 
88
- **Visualization settings.** Add `visualizationSettings` only for a change the user asked for. Their keys are a question's `visualization_settings` keys — the `visualization` skill lists them — and the installed types decide what's legal: `rg -n "<key>|declare type .*VisualizationSettings" node_modules/@metabase/embedding-sdk-react/dist/index.d.ts`, never the whole file, never from memory. Keep them few: keys such as `graph.dimensions`, `pie.metric` and `column_settings` name result columns and break when the question changes.
88
+ **Visualization settings.** Add `visualizationSettings` only for a change the user asked for, or `scalar.field` on a KPI whose first column isn't the number. Their keys are a question's `visualization_settings` keys — the `visualization` skill lists them — and the installed types decide what's legal: `rg -n "<key>|declare type .*VisualizationSettings" node_modules/@metabase/embedding-sdk-react/dist/index.d.ts`, never the whole file, never from memory. Keep them few: keys such as `graph.dimensions`, `pie.metric` and `column_settings` name result columns and break when the question changes.
89
89
 
90
90
  - A `table` with two breakouts and one aggregation renders as a pivot when a breakout has at most 16 values; `{ "table.pivot": false }` keeps one row per group.
91
- - A scalar shows the first column: with a key column first, point it at the number with `{ "scalar.field": "avg_rating" }`.
91
+ - A scalar shows the first column: with a key or breakout column first (a date on a month question), point it at the number with `{ "scalar.field": "avg_rating" }`.
92
92
  - A header: `column_settings: { '["name","approvers"]': { column_title: "Approvers" } }`.
93
93
 
94
94
  ## Pages
@@ -117,4 +117,4 @@ Metabase mirrors each route to `/apps/<slug>/<path>`, and a reload there opens t
117
117
 
118
118
  ## Before handoff
119
119
 
120
- `npm run dev` only transpiles: run `npm run typecheck` after every round of edits and fix what it reports, and report the result, errors grouped by cause. Search the changed files and fix what turns up: `card={{` (every `card` comes from `useMemo`), `row[0]` and `as unknown as` (rows are keyed by column name), `<select` for a list from a question (`data-app-filters`), `type="date"` and `new Date(` in a date filter, `* 100` and `%`. Read the diagnostics feed with every panel mounted, then build and publish (`data-app`, _Build and publish_).
120
+ `npm run dev` only transpiles: run `npm run typecheck` after every round of edits and fix what it reports, and report the result, errors grouped by cause. Search the changed files and fix what turns up: `card={{` (every `card` comes from `useMemo`), `row[0]` and `as unknown as` (rows are keyed by column name), `<select` for a list from a question (`data-app-filters`), `type="date"` and `new Date("` (a date string parsed as UTC) in a date filter, `* 100` and `%`. Read the diagnostics feed with every panel mounted, then build and publish (`data-app`, _Build and publish_).
@@ -49,7 +49,7 @@ Two things never bend in any mode: when genuinely unsure, **ask** (the Shared Co
49
49
  - "Let me filter to just the active ones" → a **segment** (saved filter).
50
50
  - "Let me add up revenue the same way everywhere, on this table" → a **measure** on the table.
51
51
  - "Revenue is an _official company number_ people pull onto dashboards" → a **metric** in a collection, with a default month-by-month view so it charts cleanly. Lean: make it a metric when it's a headline figure the org reuses across many questions/dashboards; keep it a measure when it's a table-local convenience.
52
- - **Where the metric lives.** Metrics sit in a collection (folder). Lean: put the org's blessed ones in the shared **Library** so they surface prominently; keep experimental ones in a working collection until trusted.
52
+ - **Where the metric lives.** Metrics sit in a collection (folder). Lean: put the org's blessed ones in the Library's **Metrics** collection so they surface prominently; keep experimental ones in a working collection until trusted.
53
53
  <!-- requires: library -->
54
54
  - **Publish the official tables to the Library.** The clean, analysis-ready tables your definitions sit on are the org's official starting points — the **Library** is how you mark them as such. Tables published to the Library's **Data** section appear _first_ when anyone picks a data source, nudging people toward your curated tables instead of raw warehouse ones. Lean: publish the wide clean tables you built the semantic layer on; hold back raw or half-built ones. Surface which tables you'd publish and confirm. (Only admins and data analysts can publish — mechanics in `core`.)
55
55
  <!-- /requires -->
@@ -102,7 +102,7 @@ Build each agreed definition. The verb mechanics (create/update flags, the `revi
102
102
 
103
103
  - **Segment** → `mb segment create`. A flat MBQL filter clause on a table.
104
104
  - **Measure** → `mb measure create`. **Exactly one** aggregation on a table.
105
- - **Metric** → `mb card create` with the metric shape (`type: "metric"`) — it lives in a **collection**, carries the aggregation plus an optional default time dimension. Put org-blessed ones in the Library collection.
105
+ - **Metric** → `mb card create` with the metric shape (`type: "metric"`) — it lives in a **collection**, carries the aggregation plus an optional default time dimension. Put org-blessed ones in the Library's **Metrics** collection (id from `mb library get`).
106
106
  <!-- requires: library -->
107
107
  - **Publish the official tables** → `mb library create` then `mb library publish` (mechanics in `core`) to move the clean tables your definitions sit on into the Library's **Data** section, so people start from your curated set, not raw warehouse tables.
108
108
  <!-- /requires -->
@@ -7,7 +7,7 @@ requires: [remoteSync]
7
7
 
8
8
  # git-sync (representations ↔ instance)
9
9
 
10
- Metabase content (cards, dashboards, transforms, snippets, collections, …) can live in a git repo as YAML and round-trip in and out of a Metabase instance via the `git-sync` verbs. The instance is configured with a `remote-sync-*` settings block (URL, branch, token, type read-only/read-write); the CLI drives the sync tasks against `/api/ee/remote-sync/*`. Only collections flagged for sync serialize; everything else is local-only. Table and field metadata round-trips too, for Library-published tables in a flagged collection — see "Published table metadata" below.
10
+ Metabase content (cards, dashboards, transforms, snippets, collections, …) can live in a git repo as YAML and round-trip in and out of a Metabase instance via the `git-sync` verbs. The instance is configured with a `remote-sync-*` settings block (URL, branch, token, type read-only/read-write); the CLI drives the sync tasks against `/api/ee/remote-sync/*`. Only flagged collections serialize, plus, unflagged: each data app's collection (with its app, `data_apps/<slug>/`), transforms (`remote-sync-transforms`), and snippets, data actions and glossary once the Library is synced. Everything else is local-only. Table and field metadata round-trips too, for Library-published tables in a flagged collection — see "Published table metadata" below.
11
11
 
12
12
  The repo and the instance are two ends of the same state; what "push my changes" means depends on where the changes were made. If they were made **in the instance** (CLI verbs against a read-write instance — cards edited, transforms created, tables published, metadata written), "push my changes" / "save this to the repo" / "open a PR with these changes" means driving the instance's export to a branch (`stash` or `create-branch` + `export`) and opening the PR from that branch — not reconstructing the changes as files. If the workflow is **repo-first** (content YAML edited in the repo, the instance imports it), plain `git` against the repo is the way — but only in the serialized layout already there: files in paths or formats the serializer doesn't own are invisible to Metabase and never apply on import.
13
13
 
@@ -64,7 +64,7 @@ Workflow:
64
64
 
65
65
  <!-- requires: remoteSyncRemoteChanges -->
66
66
 
67
- Between the two, confirm `has-remote-changes` reports `has_changes: true` — there's actually something to import.
67
+ Between the two, confirm `has-remote-changes` reports `has_changes: true` (`--force-refresh` after your own push: the answer is cached).
68
68
 
69
69
  <!-- /requires -->
70
70
 
@@ -168,7 +168,7 @@ Skip the prompt only if the user's instructions already specified the branch (e.
168
168
  ```bash
169
169
  mb git-sync branches --profile <n> --json # list remote branches
170
170
  mb git-sync create-branch <name> --profile <n> # create + switch sync to it
171
- mb git-sync stash --profile <n> # export current state to a NEW branch
171
+ mb git-sync stash --new-branch <name> --profile <n> # export current state to a NEW branch
172
172
  ```
173
173
 
174
174
  `stash` is the safe move when the instance has team work you don't want to lose, but you need to pivot to a different branch (`import` would discard, `export --force` would overwrite). It exports current state to a fresh branch first.
@@ -184,19 +184,21 @@ Use `wait` after `import --no-wait` / `export --no-wait`. Use `cancel-task` if a
184
184
 
185
185
  ## Adding / removing a directory (collection) to sync
186
186
 
187
- The set of directories under sync is governed by which **collections** carry `is_remote_synced: true`. Every collection so flagged serializes to its own folder under `collections/` in the repo; everything outside that set is local-only. The CLI exposes per-collection toggles that route to the underlying bulk endpoint (`PUT /api/ee/remote-sync/settings`):
187
+ The set of directories under sync is governed by which **collections** carry `is_remote_synced: true`. Every collection so flagged serializes to its own folder under `collections/` in the repo; so do the unflagged ones above (a data app's collection can't be flagged). Everything else is local-only. Toggle one collection at a time:
188
188
 
189
189
  ```bash
190
190
  mb git-sync add-collection <collection-id> --profile <n> --json
191
191
  mb git-sync remove-collection <collection-id> --profile <n> --json
192
192
  ```
193
193
 
194
- `<collection-id>` is a **positive integer** (the bulk endpoint's schema is `pos-int? → boolean`; nano-id / `root` / `trash` refs are not supported). Get the id from `collection list` (see `core`).
194
+ `<collection-id>` is a **positive integer** (no nano-id, `root` or `trash`). Get the id from `collection list` (see `core`).
195
195
 
196
- Both verbs return `{ success: true }`, plus a `task_id` only when the toggle started a follow-up task (e.g. a finalization import after switching to read-only).
196
+ Both return `{ success: true }`, plus a `task_id` when the toggle started a follow-up task.
197
197
 
198
198
  **Cascade.** A toggle on a parent cascades to every descendant by `location` prefix — `add-collection 4` flips `4` plus every collection nested under it. `remove-collection 4` is the symmetric inverse. There is no per-leaf-only mode.
199
199
 
200
+ **Custom collections.** Outside the Library, transforms, snippets and data actions, a flag answers 400 unless the admin setting `remote-sync-custom-collections-enabled` is on (a data app's collection can't be flagged at all).
201
+
200
202
  **Mode prerequisite.** The server rejects toggles while `remote-sync-type` is `:read-only` (the install default; `mb git-sync status --json` reports it as `sync_type`). If `mb git-sync add-collection 12` returns `Metabase returned 400 … Cannot change synced collections when remote-sync-type is read-only.`, switch first with:
201
203
 
202
204
  ```bash
@@ -237,13 +239,13 @@ To plan the order before the first move, `mb dependency graph dashboard <id> --j
237
239
 
238
240
  ## Published table metadata (Library) and sync scope
239
241
 
240
- Table and field metadata — table/field descriptions, semantic types (`type/PK`, `type/FK`), FK targets, plus segments and measures on the table — serializes for **Library-published tables only**, under `databases/<db>/schemas/<schema>/tables/<table>/…` in the repo. Eligibility is two-gated: the table must be published (`mb library publish`), **and** the Library collection holding it must itself carry `is_remote_synced: true`. An ordinary warehouse table, or a transform's target table that isn't published, never serializes — a transform's YAML carries only the transform definition (query, target, description), not the output table's field metadata.
242
+ Table and field metadata — table/field descriptions, semantic types (`type/PK`, `type/FK`), FK targets, plus segments and measures on the table — serializes for **Library-published tables only**, under `databases/<db>/schemas/<schema>/tables/<table>/…` in the repo. Eligibility is two-gated: the table must be published (`mb library publish`), **and** Library Data must be synced; flag the Library root, as Data alone leaves out Metrics, Dashboards, snippets, data actions and glossary. An ordinary warehouse table, or a transform's target table that isn't published, never serializes — a transform's YAML carries only the transform definition (query, target, description), not the output table's field metadata.
241
243
 
242
- The classic trap: publish tables, write field metadata, then `git-sync dirty` comes back empty and nothing lands in the repo. That does not mean git-sync can't carry table metadata — it means the Library collection isn't in the sync scope, so nothing was eligible for dirty-tracking when the writes happened. Check the scope (`synced_collections` in `status`), then:
244
+ The classic trap: publish tables, write field metadata, then `git-sync dirty` comes back empty and nothing lands in the repo. It means the Library collection isn't in the sync scope, so nothing was eligible for dirty-tracking when the writes happened. Check the scope (`synced_collections` in `status`), then:
243
245
 
244
246
  ```bash
245
- mb library get --profile <n> --json # Library Data collection id
246
- mb git-sync add-collection <library-data-id> --profile <n>
247
+ mb collection list --profile <n> --json # the "type": "library" entry's id
248
+ mb git-sync add-collection <library-id> --profile <n>
247
249
  mb git-sync stash --new-branch <branch> -m "..." --profile <n> # or create-branch + export
248
250
  ```
249
251
 
@@ -256,7 +258,7 @@ Flagging the collection records it for the next export, which serializes its cur
256
258
  - Don't turn instance-side changes into hand-written repo files: export them and PR the exported branch (see the top of this skill). Files pushed behind Metabase's back race its own sync tasks.
257
259
  - Don't conclude from an empty `dirty` list that a change type isn't tracked. Dirty-tracking only records changes to _eligible_ objects; the usual cause is scope (the collection isn't flagged — see "Published table metadata"), not capability. Check `synced_collections` in `status` before concluding.
258
260
  - Don't run `git-sync import --force` or `git-sync export --force` without explicit user confirmation. Both are lossy — `--force` import discards instance-side work, `--force` export overwrites the remote branch.
259
- - Don't drive `git-sync` against a Metabase instance that doesn't have remote-sync configured — every verb returns an error pointing at the missing `remote-sync-*` settings. To check: `mb git-sync status --profile <n> --json` reports `remote_configured: false` when no remote is configured.
261
+ - Don't drive `git-sync` against a Metabase instance that doesn't have remote-sync configured — import, export, has-remote-changes and export-preflight answer `Remote sync is not configured.` To check: `mb git-sync status --profile <n> --json` succeeds and reports `remote_configured: false`.
260
262
  - Don't author content directly via `card create` / `transform create` and then assume `git-sync export` will commit it cleanly — the instance and repo can drift if you mix direct API writes with sync-tracked changes. If you do, follow direct writes immediately with `git-sync export -m "..."` to keep them in step.
261
263
  - Don't move a dashboard or question into a synced collection before what it depends on, and don't retry a `Uses content that is not remote synced.` refusal unchanged. Move the listed dependencies first, move the whole containing collection, or build the content in the synced collection to begin with (see "Moving content into or out of a synced collection").
262
264
  - Don't omit `-m` on `export` if the user wants a meaningful commit message — the default server-generated message is generic.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: mbql
3
- description: Author and debug MBQL query bodies for the `mb` CLI — the query shape, clauses, filters, aggregation and breakout, order and limit, expressions, joins and FK columns, multi-stage pipelines, saved questions and definitions as sources, output column names, and the print-schema → dry-run → run loop. Use when writing or fixing any query body — `mb query`, a card's `dataset_query`, a transform's `source.query`, or a segment/measure `definition` — or when `--dry-run` or a run reports errors. Triggers — "write an MBQL query", "the dataset_query is wrong", "aggregate and group by", "join two tables", "month-over-month".
3
+ description: Author and debug MBQL query bodies for the `mb` CLI — the query shape, clauses, filters, aggregation and breakout, order and limit, expressions, joins and FK columns, multi-stage pipelines, saved questions and definitions as sources, output column names, and the schema → dry-run → run loop. Use when writing or fixing any query body — `mb query`, a card's `dataset_query`, a transform's `source.query`, or a segment/measure `definition` — or when `--dry-run` or a run reports errors. Triggers — "write an MBQL query", "the dataset_query is wrong", "aggregate and group by", "join two tables", "month-over-month".
4
4
  allowed-tools: Read, Write, Edit, Bash, AskUserQuestion
5
5
  ---
6
6
 
@@ -147,12 +147,12 @@ A measure is an aggregation, `["measure", {}, <measure id>]`, on its table, and
147
147
 
148
148
  <!-- /requires -->
149
149
 
150
- ## Authoring loop: print-schema → dry-run → run
150
+ ## Authoring loop: schema → dry-run → run
151
151
 
152
152
  ```bash
153
153
  mb query --file q.json --dry-run --profile <n> # check + compile on the server, no run
154
154
  mb query --file q.json --profile <n> --json # check + run
155
- mb query --print-schema --profile <n> > ./.scratch/mbql-schema.json # the full JSON Schema
155
+ mb schema query > ./.scratch/mbql-schema.json # the JSON Schema bundle, no server needed
156
156
  mb query --file q.json --compile --profile <n> # the SQL the server generates
157
157
  mb query --file q.json --metadata --profile <n> --json # tables (with FK targets), fields, snippets it touches
158
158
  mb query --file q.json --export-format csv --profile <n> > rows.csv # stream the rows up to the server's download limit, no envelope cap
@@ -28,7 +28,7 @@ A native `dataset_query` is a query with one **native stage** — the `lib/type:
28
28
  }
29
29
  ```
30
30
 
31
- This is the form a card stores and returns — author it. The CLI **pre-flight-validates** it — the envelope, the template-tag shapes, the field refs — through the usual `--print-schema → --dry-run → run` loop (`mbql`), and a saved card **round-trips** in exactly this shape: `mb card get <id> --full --json`, edit the `stages[0].native` string, send it straight back. Only the warehouse parses the SQL string, so a **SQL** syntax error surfaces when you run it, not at `--dry-run`. A parameterless query needs no `template-tags` — just the `native` string.
31
+ Author it in this form. The CLI **pre-flight-validates** it — the envelope, the template-tag shapes, the field refs — through the usual `mb schema query` → `--dry-run` → run loop (`mbql`). A saved card **round-trips**: `mb card get <id> --full --json` returns the same stage with `template-tags` as a list of the same entries, each carrying its `name`; edit the `stages[0].native` string and send it straight back, list and all. Only the warehouse parses the SQL string, so a **SQL** syntax error surfaces when you run it, not at `--dry-run`. A parameterless query needs no `template-tags` — just the `native` string.
32
32
 
33
33
  You may see an older flat form in cards created long ago — `{database, type:"native", native:{query}}`. The server still accepts it (it normalizes to the above) but it skips pre-flight and doesn't round-trip — **don't author it**.
34
34
 
@@ -4,7 +4,7 @@ Every template-tag body, the widget-type vocabulary, and the parameter-object sh
4
4
 
5
5
  ## Template-tag bodies by `type`
6
6
 
7
- The `template-tags` value is a map keyed by tag name; each entry's `name` must equal its key and the `{{name}}` in the SQL. `id` is any string unique among the tags; the tag's name serves.
7
+ Author the `template-tags` value as a map keyed by tag name; each entry's `name` must equal its key and the `{{name}}` in the SQL. A card read back carries the same entries as a list, which the server also takes. `id` is any string unique among the tags; the tag's name serves.
8
8
 
9
9
  ### Raw variable — `text` / `number` / `date` / `boolean`
10
10
 
@@ -1,13 +0,0 @@
1
- import { t as defineCommandGroup } from "./group-81zoQcEP.mjs";
2
- //#region src/commands/api-key/index.ts
3
- var api_key_default = defineCommandGroup({
4
- name: "api-key",
5
- description: "Manage Metabase API keys",
6
- subCommands: {
7
- list: () => import("./list-s9_LlXCV.mjs").then((mod) => mod.default),
8
- create: () => import("./create-C3H9ljex.mjs").then((mod) => mod.default),
9
- delete: () => import("./delete-4v4_lDwD.mjs").then((mod) => mod.default)
10
- }
11
- });
12
- //#endregion
13
- export { api_key_default as default };