@metabase/cli 0.1.16 → 0.1.17

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 (216) hide show
  1. package/README.md +21 -0
  2. package/dist/{add-collection-H4LcP-9B.mjs → add-collection-C-t9SQBk.mjs} +5 -5
  3. package/dist/add-collection-Dek6kiRI.mjs +11 -0
  4. package/dist/{archive-BCXoM1nX.mjs → archive-C7dnyzVY.mjs} +9 -7
  5. package/dist/{archive-D1mfiftv.mjs → archive-CaoUUTIb.mjs} +8 -7
  6. package/dist/{archive-CQQWkC5S.mjs → archive-D1FX-sbU.mjs} +7 -6
  7. package/dist/{archive-hN8PfvhX.mjs → archive-DS-KEB4a.mjs} +7 -6
  8. package/dist/{archive-CdUS-OG9.mjs → archive-LG8u7ec5.mjs} +7 -6
  9. package/dist/{archive-C6MDtV1F.mjs → archive-qSOcACQo.mjs} +8 -6
  10. package/dist/{archive-B3cjzOIK.mjs → archive-wNXwIiF0.mjs} +8 -7
  11. package/dist/auth-Kv2MRkRk.mjs +22 -0
  12. package/dist/{body-DB2upz6a.mjs → body-IcJ5kFtk.mjs} +3 -3
  13. package/dist/{branches-B6R60Vr1.mjs → branches-BlNCTmFB.mjs} +7 -6
  14. package/dist/{cancel-oPsWomYc.mjs → cancel-BnveTPNw.mjs} +6 -5
  15. package/dist/{cancel-task-CleJVDNI.mjs → cancel-task-CSVI8Zgl.mjs} +7 -6
  16. package/dist/{capabilities-BX1rnVuH.mjs → capabilities-N0jo5U7S.mjs} +1 -1
  17. package/dist/card-Bp58WEUF.mjs +26 -0
  18. package/dist/{card-wCPcuKSi.mjs → card-DEmcRlNO.mjs} +6 -5
  19. package/dist/{cards-Bw37jizL.mjs → cards-CMgGA8OZ.mjs} +8 -6
  20. package/dist/cli.mjs +54 -27
  21. package/dist/collection-D8TI20Ir.mjs +23 -0
  22. package/dist/{collection-namespace-CUDPh2rF.mjs → collection-namespace-CsaxEqOb.mjs} +2 -2
  23. package/dist/command-augment-DdZIfx1V.mjs +11 -0
  24. package/dist/{create-Bj6PuaAW.mjs → create-AeWNv0v-.mjs} +9 -8
  25. package/dist/{create-CZ0_s5ap.mjs → create-B1xfaZJB.mjs} +7 -6
  26. package/dist/{create-B87ZQjNM.mjs → create-B6LQutd0.mjs} +17 -12
  27. package/dist/{create-CF2Zn4pT.mjs → create-B79M8YpX.mjs} +10 -9
  28. package/dist/{create-Cy2TNnJB.mjs → create-Bg_uMu0p.mjs} +9 -8
  29. package/dist/{create-DSWjS09p.mjs → create-BpkWlgoR.mjs} +18 -12
  30. package/dist/{create-D-qE3Oq8.mjs → create-C7u3umLj.mjs} +9 -8
  31. package/dist/{create-B-mvVFIl.mjs → create-FZOrCw5k.mjs} +21 -12
  32. package/dist/{create-CfLaBO0V.mjs → create-I5Cv-MBa.mjs} +16 -11
  33. package/dist/{create-BEkuqChu.mjs → create-LHqwcVbS.mjs} +16 -11
  34. package/dist/{create-DwoqFYb9.mjs → create-SHKlj0z3.mjs} +9 -8
  35. package/dist/{create-branch-BaZ00MIc.mjs → create-branch-CGyT99Ny.mjs} +7 -6
  36. package/dist/{current-task-Dr5dOD4V.mjs → current-task-C-0hw2Ae.mjs} +7 -6
  37. package/dist/dashboard-C9pQCnX6.mjs +28 -0
  38. package/dist/{dashboard-B4bn3z6t.mjs → dashboard-DOplbKyQ.mjs} +7 -5
  39. package/dist/{database-BJxGUXhA.mjs → database-D9fftP-i.mjs} +1 -1
  40. package/dist/db-DWykmBOh.mjs +28 -0
  41. package/dist/{delete-DHrFA1SZ.mjs → delete--NYYN6wv.mjs} +8 -7
  42. package/dist/{delete-evwMw6Hk.mjs → delete-Do3nn9sl.mjs} +8 -7
  43. package/dist/{delete-runtime-CZMw_AGX.mjs → delete-runtime-B0ha5QR4.mjs} +3 -3
  44. package/dist/{delete-DJti0TOA.mjs → delete-tCVrYjKD.mjs} +8 -7
  45. package/dist/{delete-table-DvuuwVqu.mjs → delete-table-CWSGPw0i.mjs} +8 -7
  46. package/dist/{dependencies-DrV31Rj5.mjs → dependencies-CyFocD8I.mjs} +7 -6
  47. package/dist/{dirty-C-rkrnVM.mjs → dirty-BXM0YJ_a.mjs} +7 -6
  48. package/dist/document-DNDm8_Py.mjs +22 -0
  49. package/dist/{eid-BeXI-eII.mjs → eid-p-zn_1RJ.mjs} +13 -7
  50. package/dist/{error-CIObEXLY.mjs → error-5H_tcfL8.mjs} +2 -2
  51. package/dist/{export-CGa-SgEV.mjs → export-B_8wghyo.mjs} +9 -8
  52. package/dist/field-CFt1-KDR.mjs +21 -0
  53. package/dist/{fields-Recymu7n.mjs → fields-BsufL9kv.mjs} +8 -7
  54. package/dist/{get-rFcAVIch.mjs → get-4lufgahL.mjs} +7 -6
  55. package/dist/{get-BQxLtFE-.mjs → get-68P-L4ja.mjs} +7 -6
  56. package/dist/{get-Nc5GOs6-.mjs → get-B45uYMAs.mjs} +8 -6
  57. package/dist/{get-b4xbfFbA.mjs → get-B67pe00Z.mjs} +7 -6
  58. package/dist/{get-BAH_M5vj.mjs → get-BJoZkATD.mjs} +7 -6
  59. package/dist/{get-DaixPDU9.mjs → get-BTKeO4vE.mjs} +7 -6
  60. package/dist/{get-CYh2DBsq.mjs → get-C7mq17-2.mjs} +7 -6
  61. package/dist/{get-CHi8tU_1.mjs → get-CDLa6LiC.mjs} +9 -8
  62. package/dist/{get-CARdLkmp.mjs → get-CPw0ROO4.mjs} +7 -6
  63. package/dist/{get-CXMv-r1p.mjs → get-Cl5Ak73C.mjs} +9 -7
  64. package/dist/{get-BM-d3-zk.mjs → get-D3k2JUsa.mjs} +7 -6
  65. package/dist/{get-7fWSU6ow.mjs → get-DTqBuIf3.mjs} +6 -5
  66. package/dist/{get-Q2WZ79q_.mjs → get-DeLnNVQE.mjs} +8 -7
  67. package/dist/{get-C6g2C4dM.mjs → get-Df_3LCVC.mjs} +7 -6
  68. package/dist/{get-BD_P_Ejc.mjs → get-DurjkUKJ.mjs} +7 -6
  69. package/dist/{get-run-CfQR6ZNa.mjs → get-run-Dq4qfGfD.mjs} +7 -6
  70. package/dist/git-sync-ByvjqSiy.mjs +31 -0
  71. package/dist/group-BNE_RiH5.mjs +28 -0
  72. package/dist/{has-remote-changes-Bvyv4FLP.mjs → has-remote-changes-E-4N_O_t.mjs} +7 -6
  73. package/dist/{import-c3o3OAx0.mjs → import-Dg0j3HCP.mjs} +9 -8
  74. package/dist/{input-BXWgdKiS.mjs → input-7Sj85_K7.mjs} +1 -1
  75. package/dist/is-dirty-D7UMN0mt.mjs +10 -0
  76. package/dist/{is-dirty-BE53XwOC.mjs → is-dirty-DWw1yBeR.mjs} +4 -4
  77. package/dist/{items-1KkBMiO4.mjs → items-CQtt9X4M.mjs} +9 -8
  78. package/dist/{key-DwiMOWRQ.mjs → key-bltP32Pm.mjs} +1 -1
  79. package/dist/library-B5AvpACG.mjs +24 -0
  80. package/dist/{list-C4KnM3Rq.mjs → list-B1uVWy6A.mjs} +8 -6
  81. package/dist/{list-DPcPTqFU.mjs → list-BAOTQHst.mjs} +6 -5
  82. package/dist/{list-D52_BozQ.mjs → list-BF-W4jOZ.mjs} +9 -7
  83. package/dist/{list-7rwzxX6t.mjs → list-BFVuPodI.mjs} +6 -5
  84. package/dist/{list-DHb4vQUM.mjs → list-BHAWYZ1z.mjs} +6 -5
  85. package/dist/{list-BqgbrpQQ.mjs → list-BKUvhAs4.mjs} +6 -5
  86. package/dist/{list-CO5J3SZU.mjs → list-BgUWqZa2.mjs} +8 -7
  87. package/dist/{list-F0vkE22V.mjs → list-C4bALfCs.mjs} +7 -6
  88. package/dist/{list-Drr4JiWg.mjs → list-C9O2RY5u.mjs} +6 -5
  89. package/dist/{list-CSjFJDls.mjs → list-CfqpDTna.mjs} +6 -5
  90. package/dist/{list-FR8Q1SzV.mjs → list-CvhVYOVl.mjs} +7 -6
  91. package/dist/{list-BC2B02IR.mjs → list-D3TSAqwl.mjs} +6 -5
  92. package/dist/{list-BwdO1_gX.mjs → list-D5Gdz8hi.mjs} +6 -5
  93. package/dist/{list-DUEYX3bX.mjs → list-DG0FIhTK.mjs} +6 -5
  94. package/dist/{list-DIvOPW1g.mjs → list-KxQNqp4T.mjs} +8 -7
  95. package/dist/{login-C0Rf2hg0.mjs → login-Bt6j6yBM.mjs} +10 -9
  96. package/dist/{logout-C7_UON-s.mjs → logout-ANkL02p0.mjs} +6 -5
  97. package/dist/{manifest-B2F8iL7X.mjs → manifest-BVf8P4bl.mjs} +9 -2
  98. package/dist/measure-CgFwtL1r.mjs +25 -0
  99. package/dist/{metadata-Db3Kpo-z.mjs → metadata-ClehtgZj.mjs} +9 -8
  100. package/dist/{metadata-FltZq5Ek.mjs → metadata-DplshwI3.mjs} +8 -7
  101. package/dist/{command-augment-CAur0XOQ.mjs → notice-DyVl5aYB.mjs} +1 -11
  102. package/dist/parameter-CiJ4CwWE.mjs +118 -0
  103. package/dist/parameter-values-D4J8Ctu0.mjs +56 -0
  104. package/dist/{parse-enum-BatHQ-Gs.mjs → parse-enum-BL9i_brN.mjs} +1 -1
  105. package/dist/{parse-id-B5adfBlS.mjs → parse-id-D4LeTUsP.mjs} +1 -1
  106. package/dist/{parse-ref-CZr1bYIl.mjs → parse-ref-CB_KvF9h.mjs} +1 -1
  107. package/dist/{path-BojuJkE4.mjs → path-5nQgdvrs.mjs} +6 -5
  108. package/dist/{poll-AduuU55-.mjs → poll-BpAJpvb-.mjs} +2 -2
  109. package/dist/{poll-task-B00Qwd87.mjs → poll-task-TilgciQn.mjs} +2 -2
  110. package/dist/{preflight-QVPvG_Xg.mjs → preflight-OfHU3Toi.mjs} +4 -4
  111. package/dist/{process-DsGf7Mg5.mjs → process-j8UHMHc2.mjs} +1 -1
  112. package/dist/{prompt-Bc_bHSD0.mjs → prompt-C85xd9HR.mjs} +1 -1
  113. package/dist/{publish-h5RJh6im.mjs → publish-DKiqyDwo.mjs} +9 -8
  114. package/dist/{query-Dvi-Rksy.mjs → query-BWJ5h1g3.mjs} +19 -13
  115. package/dist/{query-BUkuB4bZ.mjs → query-CQ3xXa9P.mjs} +10 -8
  116. package/dist/{query-result-L5_NrwQR.mjs → query-result-D6mfoVfQ.mjs} +1 -1
  117. package/dist/{remove-collection-D8ZfB2RN.mjs → remove-collection-CO-aMzTO.mjs} +9 -8
  118. package/dist/{rescan-values-DOsDLrRG.mjs → rescan-values-DpL6LGAK.mjs} +9 -8
  119. package/dist/{resolve-BQ9vjlNJ.mjs → resolve-Dj2MTBkn.mjs} +1 -1
  120. package/dist/{run-CeG0KH5W.mjs → run-DUcdaZs3.mjs} +6 -5
  121. package/dist/{run-CjhD-Zbr.mjs → run-QYJ-mAaG.mjs} +9 -8
  122. package/dist/{runs-6k8C6kXF.mjs → runs-DgasTnGd.mjs} +8 -7
  123. package/dist/{runtime-CmAIahm5.mjs → runtime-BJtuxWM8.mjs} +5 -3
  124. package/dist/{schema-tables-C45QegaY.mjs → schema-tables-ooYjimrV.mjs} +8 -7
  125. package/dist/{schemas-EVwEFuTj.mjs → schemas-HjFPzsd-.mjs} +6 -5
  126. package/dist/{search-C_uw_D1U.mjs → search-vaT5EwhG.mjs} +11 -5
  127. package/dist/segment-BPC725mo.mjs +25 -0
  128. package/dist/{selectors-AktxTEMK.mjs → selectors-DlmZpo2L.mjs} +3 -3
  129. package/dist/{set-C3EAuyb8.mjs → set-CLHJauzG.mjs} +9 -8
  130. package/dist/{set-active-BW6LN6y0.mjs → set-active-oZOUe9V7.mjs} +6 -5
  131. package/dist/setting-DHYO-W5g.mjs +20 -0
  132. package/dist/{setup-CYrbNrlG.mjs → setup-D1de5Sbc.mjs} +8 -7
  133. package/dist/{skills-DJsuBguh.mjs → skills-B6gfH0iR.mjs} +1 -1
  134. package/dist/{skills-D6xQkmhu.mjs → skills-NhgVypJ7.mjs} +3 -3
  135. package/dist/snippet-DtLmHr2J.mjs +22 -0
  136. package/dist/{stash-C1V2FvJR.mjs → stash-DoRwelwY.mjs} +9 -8
  137. package/dist/{status-CxYw6zQM.mjs → status-BpMOlfsA.mjs} +8 -7
  138. package/dist/{status-DY92F9mn.mjs → status-CmRgHTrV.mjs} +6 -5
  139. package/dist/{summary-DOxgqJoA.mjs → summary-BEu7pmpq.mjs} +7 -6
  140. package/dist/{sync-schema-CQPfffjU.mjs → sync-schema-DTUXubMg.mjs} +11 -10
  141. package/dist/table-B1iBqmNu.mjs +22 -0
  142. package/dist/{table-CDMG0Zi5.mjs → table-DE3i82T_.mjs} +1 -1
  143. package/dist/transform-BuIooRQh.mjs +31 -0
  144. package/dist/transform-job-C3yz0krA.mjs +25 -0
  145. package/dist/transform-tag-CJD6mJUe.mjs +21 -0
  146. package/dist/{transforms-DzBJDydn.mjs → transforms-CWvMpLc9.mjs} +7 -6
  147. package/dist/{tree-B3f5F_dP.mjs → tree-D18vYSe4.mjs} +6 -5
  148. package/dist/{unpublish-B5RDeN-V.mjs → unpublish-CJbL4ZsC.mjs} +7 -6
  149. package/dist/{update-DSueNZRw.mjs → update-7DacwIvi.mjs} +10 -9
  150. package/dist/{update-D5gioyBa.mjs → update-BjjZIe2W.mjs} +10 -9
  151. package/dist/{update-C0pFSc1B.mjs → update-Bos8nnv0.mjs} +18 -13
  152. package/dist/{update-PZPNx0Xd.mjs → update-BvsvyBw9.mjs} +17 -12
  153. package/dist/{update-DudyZ-FP.mjs → update-CSxwZ2us.mjs} +11 -10
  154. package/dist/{update-CiWPEqQ-.mjs → update-Ck0Kxv8u.mjs} +10 -9
  155. package/dist/{update-D698CaeV.mjs → update-DV7IY7IQ.mjs} +10 -9
  156. package/dist/{update-DOfL_KPx.mjs → update-DhOfrW1j.mjs} +19 -13
  157. package/dist/{update-BIZ9XhjS.mjs → update-Doia9MP_.mjs} +17 -12
  158. package/dist/{update-I3TA2Tem.mjs → update-Dxa_6H2A.mjs} +22 -13
  159. package/dist/{update-dashcard-BXZ4vS15.mjs → update-dashcard-CZUWZhal.mjs} +11 -9
  160. package/dist/{update-CcvDVqNd.mjs → update-mr9todHq.mjs} +10 -9
  161. package/dist/{upgrade-4cWfLu90.mjs → upgrade-CBW8V9qZ.mjs} +7 -6
  162. package/dist/{uuid---pAboNQ.mjs → uuid-D6JVJ-R1.mjs} +10 -5
  163. package/dist/{validate-VawhJ5Sc.mjs → validate-BqNW4Sk1.mjs} +2 -2
  164. package/dist/{validate-query-CSV-TTnd.mjs → validate-query-CcZVKYPV.mjs} +3 -3
  165. package/dist/{values-I503dI7K.mjs → values-DBvuGnnA.mjs} +7 -6
  166. package/dist/{verify-BMhTWW9s.mjs → verify-LIShMNZ2.mjs} +2 -2
  167. package/dist/{wait-oMSs_IdS.mjs → wait-DUHze3_B.mjs} +8 -7
  168. package/dist/{wait-flags-HtCL2l1r.mjs → wait-flags-Ybpt98PK.mjs} +2 -2
  169. package/package.json +1 -1
  170. package/skill-data/core/SKILL.md +44 -56
  171. package/skill-data/data-workflow/SKILL.md +116 -0
  172. package/skill-data/{data-analysis/SKILL.md → data-workflow/references/answering-questions.md} +7 -13
  173. package/skill-data/{data-transformation/SKILL.md → data-workflow/references/building-clean-tables.md} +23 -32
  174. package/skill-data/{semantic-layer/SKILL.md → data-workflow/references/reusable-definitions.md} +26 -56
  175. package/skill-data/document/SKILL.md +10 -20
  176. package/skill-data/git-sync/SKILL.md +36 -36
  177. package/skill-data/mbql/SKILL.md +3 -15
  178. package/skill-data/mbql/references/operators.md +9 -0
  179. package/skill-data/transform/SKILL.md +26 -42
  180. package/skill-data/visualization/SKILL.md +6 -6
  181. package/skill-data/visualization/references/settings.md +12 -0
  182. package/skills/metabase-cli/SKILL.md +2 -2
  183. package/dist/add-collection-BqfYL4FU.mjs +0 -10
  184. package/dist/auth-BaCMFLTA.mjs +0 -19
  185. package/dist/card-DzH3aK0a.mjs +0 -20
  186. package/dist/collection-Wagz-ira.mjs +0 -20
  187. package/dist/dashboard-OUgS1Gi-.mjs +0 -21
  188. package/dist/db-Btfl5JMZ.mjs +0 -22
  189. package/dist/document-CU28GfFw.mjs +0 -19
  190. package/dist/field-BTbzlcyC.mjs +0 -18
  191. package/dist/git-sync-CBxS2urR.mjs +0 -28
  192. package/dist/is-dirty-Z-pqyVyB.mjs +0 -9
  193. package/dist/library-BlbH0xyK.mjs +0 -18
  194. package/dist/measure-BUedPu4K.mjs +0 -19
  195. package/dist/segment-CkZUZcWz.mjs +0 -19
  196. package/dist/setting-C50HEiGG.mjs +0 -17
  197. package/dist/snippet-BjaWAxCu.mjs +0 -19
  198. package/dist/table-B35ovbcd.mjs +0 -19
  199. package/dist/transform-DF79sJ0_.mjs +0 -25
  200. package/dist/transform-job-PmA_D8gz.mjs +0 -22
  201. package/dist/transform-tag-CE3cuO1K.mjs +0 -18
  202. package/skill-data/robot-data-engineer/SKILL.md +0 -142
  203. /package/dist/{body-flags-D7q87Btw.mjs → body-flags-DWTTxJpP.mjs} +0 -0
  204. /package/dist/{collection-Deiziuu2.mjs → collection-DrLpA1SO.mjs} +0 -0
  205. /package/dist/{document-qfwR0r63.mjs → document-1W7NRaO_.mjs} +0 -0
  206. /package/dist/{field-E0IBy4Uw.mjs → field-CMY_LWUe.mjs} +0 -0
  207. /package/dist/{measure-BCv5wDDN.mjs → measure-DoJvtCaA.mjs} +0 -0
  208. /package/dist/{paginate-BexjkjbY.mjs → paginate-FVZUxL4J.mjs} +0 -0
  209. /package/dist/{render-CkuFkWlQ.mjs → render-BTKnWL0d.mjs} +0 -0
  210. /package/dist/{revision-message-flag-CP5NFrWQ.mjs → revision-message-flag-CHrJgFFx.mjs} +0 -0
  211. /package/dist/{segment-BAUuELKs.mjs → segment-TXktTCfU.mjs} +0 -0
  212. /package/dist/{setting-DhMk0TNo.mjs → setting-m46MUtW5.mjs} +0 -0
  213. /package/dist/{snippet-D4SyVLKB.mjs → snippet-CtA2Pkoa.mjs} +0 -0
  214. /package/dist/{transform-MmqHKGU-.mjs → transform-DEF38FWe.mjs} +0 -0
  215. /package/dist/{transform-job-CtVziW85.mjs → transform-job-CtixL4An.mjs} +0 -0
  216. /package/dist/{transform-tag-wFiWmiyO.mjs → transform-tag-rsIrckCM.mjs} +0 -0
@@ -11,6 +11,15 @@ when noted: an operator-specific option named in the row, or an explicit `lib/uu
11
11
  you mint to reference the clause. Field refs are numeric: `["field", {…}, <field-id>]`.
12
12
  Everything here passes `mb query --dry-run`; when in doubt, that loop is the authority.
13
13
 
14
+ **Contents**
15
+
16
+ - [Filter operators](#filter-operators) — Logical, Comparison, Null/empty, String match, Temporal, Segment
17
+ - [Aggregation functions](#aggregation-functions) — including naming and the `offset` window function
18
+ - [Expression operators](#expression-operators) — Arithmetic, Math, String, Temporal, Type conversion, Conditional
19
+ - [References (within clauses)](#references-within-clauses) — field, expression, aggregation
20
+ - [Field option: temporal bucketing](#field-option-temporal-bucketing)
21
+ - [Field option: binning](#field-option-binning)
22
+
14
23
  > For relative date filters, prefer **`time-interval`** / **`relative-time-interval`**
15
24
  > (below). `relative-datetime` / `absolute-datetime` literals (e.g. from a UI-built
16
25
  > query) also work.
@@ -8,7 +8,7 @@ allowed-tools: Read, Write, Edit, Bash, AskUserQuestion
8
8
 
9
9
  A **transform** persists the result of a query (native SQL or MBQL) to a warehouse table the user can read from cards, dashboards, and other transforms. It runs on a schedule (via `transform-job`) or on-demand (`transform run`).
10
10
 
11
- Flag conventions, body-input precedence, and output flags live in the `core` skill (`mb skills get core`). Deciding _which_ transforms to build — modeling a whole raw database into a set of clean, analysis-ready tables — is the `data-transformation` skill (`mb skills get data-transformation`).
11
+ Flag conventions, body-input precedence, and the `./.scratch` convention live in `core` (`mb skills get core`). Deciding _which_ transforms to build — modeling a whole raw database into clean, analysis-ready tables — is the `data-workflow` skill's build-clean-tables stage (`mb skills get data-workflow`).
12
12
 
13
13
  ## Body shape
14
14
 
@@ -17,14 +17,13 @@ A transform has two halves:
17
17
  - `source` — the query to run (`type: "query"`, with `query.type` of `native` or `mbql`).
18
18
  - `target` — the warehouse destination (`type: "table"`, with `database`, `schema`, `name`).
19
19
 
20
- Native SQL is the simplest source and the easiest to author by hand. MBQL is what the Metabase UI emits and is more verbose; pull a sample with `mb transform get <id> --full --json` if you need its shape.
21
-
22
- For an **MBQL 5** `source.query` (`lib/type: "mbql/query"`), the body shape, the "options object is always second" clause rule, UUID minting, aggregation/order-by refs, naming aggregation output columns, and the `--print-schema` → `--dry-run` validation loop are all in the `mbql` skill — **`mb skills get mbql`**. The MBQL-5 pre-flight on `transform create`/`update` is documented there too (legacy MBQL 4 and native sources skip it). For a transform target, naming your aggregation output columns matters more than usual — a bare `count` / `avg_2` becomes the warehouse column name; see the `mbql` skill's "Naming aggregation output columns".
20
+ Native SQL is the simplest source and the easiest to author by hand. For an **MBQL 5** `source.query` (`lib/type: "mbql/query"`) — the body shape, the options-object-is-always-second clause rule, UUID minting, aggregation/order-by refs, naming aggregation output columns, the `--print-schema` → `--dry-run` validation loop, and the MBQL-5 pre-flight that `transform create`/`update` run (legacy MBQL 4 and native sources skip it) — see `mbql` (**`mb skills get mbql`**). Pull a sample MBQL body with `mb transform get <id> --full --json`. For a transform target, naming aggregation output columns matters more than usual: a bare `count` / `avg_2` becomes the warehouse column name.
23
21
 
24
22
  ## Create + run (native SQL)
25
23
 
24
+ **Keep the SQL formatted.** Author it multi-line in `./.scratch/<name>.sql` and embed with `jq --rawfile` (jq ≥1.6, which JSON-encodes the file so newlines become `\n`). The stored `native.query` is what `mb transform get` and the Metabase editor render — a single-line blob is valid JSON but unreadable when anyone opens the transform. Single-quote the heredoc delimiter (`<<'SQL'`) so the shell leaves `$vars` in the query alone (e.g. Postgres `$1`, `$$`).
25
+
26
26
  ```bash
27
- # Author the SQL formatted — it's what `mb transform get` and the Metabase editor show.
28
27
  cat > ./.scratch/user_counts_by_signup_year.sql <<'SQL'
29
28
  SELECT
30
29
  date_trunc('year', created_at)::date AS signup_year,
@@ -34,7 +33,6 @@ GROUP BY 1
34
33
  ORDER BY 1
35
34
  SQL
36
35
 
37
- # Embed it with jq --rawfile so the newlines survive as \n in valid JSON (don't hand-write the SQL as one line).
38
36
  jq -n --rawfile q ./.scratch/user_counts_by_signup_year.sql \
39
37
  '{ name: "user_counts_by_signup_year",
40
38
  description: "Sample transform: counts users by year of signup",
@@ -46,17 +44,13 @@ TRANSFORM_ID=$(mb transform create --file ./.scratch/transform.json --profile <n
46
44
  mb transform run "$TRANSFORM_ID" --wait --profile <name> --json
47
45
  ```
48
46
 
49
- Notes:
50
-
51
- - `<db-id>` comes from `mb database list --profile <name> --json`. Database ids are per-instance.
52
- - Target `schema` is the schema the result table is written into (e.g. `public`).
53
- - `--wait` on `transform run` polls until status is `succeeded` or `failed`. Without it you only get `{message: "Transform run started", run_id, final: null}` and have to poll yourself.
54
- - `--sync` implies `--wait`, then waits until the run's output table is registered — the run registers it itself, no `db sync-schema` needed — adding `target_table_id` to the envelope. Use it when you'll build MBQL on the output (see "Inspect").
55
- - The `--json` envelope is shape-stable: `{message, run_id, final}` (plus `target_table_id` under `--sync` — a number, or `null` if the table didn't register before the timeout). `final` is `null` when `--wait` is omitted or the run never started, otherwise a full `TransformRun` object with `status` and `message`. On a failed run (`final.status` ∈ {`failed`, `timeout`, `canceled`}) the CLI exits 1 and writes a one-line summary `transform run <id> failed` to stderr; the failure detail lives only in `final.message` on stdout, so `jq -r '.final.message'` is where to look.
56
- - **Keep the SQL formatted.** Author it multi-line in `./.scratch/<name>.sql` and embed with `jq --rawfile` (jq ≥1.6, which JSON-encodes the file so newlines become `\n`). The stored `native.query` is what `mb transform get` and the Metabase editor render — a single-line blob is valid JSON but unreadable when anyone opens the transform. Single-quote the heredoc delimiter (`<<'SQL'`) so the shell leaves `$vars` in the query alone (e.g. Postgres `$1`, `$$`).
57
- - `transform create --json` returns the agent-facing compact projection: `{id, name, description, source_type, target: {type, database, schema, name}, target_db_id}`. Read `target.schema`/`target.name` directly off the create output — no follow-up `transform get` needed to verify where the transform will write.
58
- - If a transform with the same `name` already has a YAML representation on disk under the configured remote-sync repo, `create` mints a `_2` suffix on the exported filename (the new transform gets a fresh `entity_id`; the prior one isn't touched). For "iterate on the same concept" workflows, prefer `transform update <id>` — see "Iterating on a failing transform" below.
59
- - **`collection_id` only accepts a collection in the `:transforms` namespace.** Transforms aren't filed next to cards and dashboards — passing a normal analytics collection id (the kind a dashboard lives in) fails create/update with `collection_id: A Transform can only go in Collections in the :transforms namespace.` Omit `collection_id` to leave the transform uncollected (the common case), or create one with `mb collection create --body '{"name":"…"}' --namespace transforms --json` and pass the returned `id`. Cards and dashboards you build **on top of** the transform's output table go in ordinary collections as usual — so "put the transform and its dashboard in collection X" generally means _X holds the dashboard + cards; the transform stays in the transforms namespace._
47
+ - `<db-id>` comes from `mb database list --profile <name> --json`; ids are per-instance. Target `schema` is the schema the result table is written into (e.g. `public`).
48
+ - `--wait` polls until status is `succeeded` or `failed`. Without it you get only `{message: "Transform run started", run_id, final: null}` and must poll yourself — don't put bare `transform run` in a tight loop; let `--wait` do the polling.
49
+ - `--sync` implies `--wait`, then waits until the run registers its output table (the run registers it itself — no `db sync-schema` needed), adding `target_table_id` to the envelope. Use it when you'll build MBQL on the output (see "Inspect").
50
+ - The `--json` envelope is shape-stable: `{message, run_id, final}` (plus `target_table_id` under `--sync` — a number, or `null` if the table didn't register before the timeout). `final` is `null` when `--wait` is omitted or the run never started, otherwise a full `TransformRun` with `status` and `message`. On a failed run (`final.status` ∈ {`failed`, `timeout`, `canceled`}) the CLI exits 1 and writes a one-line `transform run <id> failed` to stderr; the failure detail lives only in `final.message` on stdout, so `jq -r '.final.message'` is where to look.
51
+ - `transform create --json` returns the agent-facing compact projection: `{id, name, description, source_type, target: {type, database, schema, name}, target_db_id}`. Read `target.schema`/`target.name` directly off it — no follow-up `transform get`.
52
+ - If a transform with the same `name` already has a YAML representation on disk under the configured remote-sync repo, `create` mints a `_2` suffix on the exported filename (the new transform gets a fresh `entity_id`; the prior one isn't touched). For "iterate on the same concept", prefer `transform update <id>` — see "Iterating on a failing transform".
53
+ - **`collection_id` only accepts a collection in the `:transforms` namespace.** Transforms aren't filed next to cards and dashboards — a normal analytics collection id fails create/update with `collection_id: A Transform can only go in Collections in the :transforms namespace.` Omit `collection_id` to leave the transform uncollected (the common case), or provision one with `mb collection create --body '{"name":"…"}' --namespace transforms --json` (see `core`) and pass the returned `id`. Cards and dashboards you build **on top of** the output table go in ordinary collections — so "put the transform and its dashboard in collection X" means _X holds the dashboard + cards; the transform stays in the transforms namespace._
60
54
 
61
55
  ## Inspect
62
56
 
@@ -66,7 +60,7 @@ mb transform get <id> --profile <name> --full --json # full transform i
66
60
  mb transform dependencies <id> --profile <name> --json # upstream transforms this one must run after
67
61
  ```
68
62
 
69
- After a run the table physically exists in the warehouse, but Metabase addresses tables/columns by numeric id, so **MBQL and the UI can't reference a brand-new table until the instance syncs** (native SQL — a native `card` or `mb query` against `<schema>.<name>` — reads it immediately). Run and register in one step with `--sync`.
63
+ After a run the table physically exists in the warehouse, but Metabase addresses tables/columns by numeric id, so **MBQL and the UI can't reference a brand-new table until the instance syncs** (native SQL — a native `card` or `mb query` against `<schema>.<name>` — reads it immediately). Run and register in one step with `--sync`:
70
64
 
71
65
  ```bash
72
66
  TABLE_ID=$(mb transform run <id> --sync --profile <name> --json | jq -r '.target_table_id')
@@ -91,12 +85,10 @@ mb transform get-run <run-id> --profile <name> --json
91
85
  mb transform cancel <id> --profile <name> --json
92
86
  ```
93
87
 
94
- Notes:
95
-
96
88
  - `transform runs` and `transform get-run` parse against the same `TransformRun` schema, so `get-run` returns the same per-run shape as one entry of `runs`. The compact projection is `{id, transform_id, status, run_method, start_time, end_time, message}`. Pass `--full` on `get-run` for the hydrated row including `is_active`, `user_id`, `transform_name`, `transform_entity_id`, `checkpoint_*` fields, and a nested `transform: {id, name, …}` block.
97
- - `transform cancel` takes the **transform** id and 404s with `Endpoint not found — is this a Metabase instance?` if there is no active run. The response shape is `{canceled: true, id: <transform-id>}`.
98
- - For native-SQL transforms, cancel marks the run as `canceling` but does **not** kill the warehouse query mid-flight — the query runs to completion, then the run lands as `canceled` (or stays `succeeded` if the cancel arrived after the writer committed). For Python transforms the worker is interrupted directly. Don't expect cancel to free warehouse resources instantly on long native queries; expect it to flip state and prevent downstream consumers from treating the result as good.
99
- - The `--transform-id` filter on `runs` accepts a single integer; the CLI translates to the server's `transform-ids` query vector. To cross-filter multiple transforms, run `transform runs --json` and `jq` post-hoc.
89
+ - `transform cancel` takes the **transform** id and returns `{canceled: true, id: <transform-id>}`. It 404s with `Endpoint not found — is this a Metabase instance?` if there is no active run.
90
+ - **Cancel semantics differ by source.** For native SQL, cancel marks the run `canceling` but does **not** kill the warehouse query mid-flight — the query runs to completion, then the run lands as `canceled` (or stays `succeeded` if the cancel arrived after the writer committed). For Python transforms the worker is interrupted directly. Don't expect cancel to free warehouse resources instantly on long native queries; expect it to flip state and prevent downstream consumers from treating the result as good.
91
+ - The `--transform-id` filter on `runs` accepts a single integer (translated to the server's `transform-ids` vector). To cross-filter multiple transforms, run `transform runs --json` and `jq` post-hoc.
100
92
 
101
93
  ## Update body: send only writable keys, never round-trip the GET body
102
94
 
@@ -107,7 +99,7 @@ name, description, source, target, run_trigger,
107
99
  tag_ids, collection_id, owner_user_id, owner_email
108
100
  ```
109
101
 
110
- **Don't paste the output of `transform get` into a `transform update` body.** The GET response carries server-side fields (`id`, `entity_id`, `created_at`, `updated_at`, `creator_id`, `last_run`, `target_db_id`, `target_table_id`, `source_type`, `source_database_id`, `source_readable`, `creator`, `owner`, `table`, …) that the PUT endpoint isn't built to handle. Unknown top-level keys flow into `t2/update!` and produce a leaked H2 SQL error like:
102
+ **Never paste the output of `transform get` into a `transform update` body.** The GET response carries server-side fields (`id`, `entity_id`, `created_at`, `updated_at`, `creator_id`, `last_run`, `target_db_id`, `target_table_id`, `source_type`, `source_database_id`, `source_readable`, `creator`, `owner`, `table`, …) that the PUT endpoint isn't built to handle. Unknown top-level keys flow into `t2/update!` and leak a raw H2 SQL error like:
111
103
 
112
104
  ```
113
105
  Column "TAGS" not found; SQL statement:
@@ -116,11 +108,11 @@ UPDATE "TRANSFORM" SET "TAGS" = (), "UPDATED_AT" = NOW() WHERE "ID" = ? [42122-2
116
108
 
117
109
  Three specific footguns:
118
110
 
119
- - **`tags` is not a key on the REST API.** The serdes/YAML representation uses `tags`; the REST contract uses `tag_ids` (an array of integer ids). If you pulled a YAML representation and want to PUT it, translate `tags: [...]` → `tag_ids: [...]` first (or omit it entirely if you're not changing tag membership).
111
+ - **`tags` is not a REST key.** The serdes/YAML representation uses `tags`; the REST contract uses `tag_ids` (an array of integer ids). If you pulled a YAML representation and want to PUT it, translate `tags: [...]` → `tag_ids: [...]` first (or omit it if you're not changing tag membership).
120
112
  - **`source_type`, `target_db_id`, `target_table_id`, `entity_id`** are derived/computed by the server. They appear in GET responses for the agent's benefit; the server doesn't accept them on update.
121
- - **`collection_id` must be a `:transforms`-namespace collection** — a regular card/dashboard collection id is rejected with `A Transform can only go in Collections in the :transforms namespace.` Omit it unless you have one (see the create notes above). Round-tripping the existing value is safe; setting it to an ordinary collection is what fails.
113
+ - **`collection_id` must be a `:transforms`-namespace collection** — a regular card/dashboard collection id is rejected with `A Transform can only go in Collections in the :transforms namespace.` Round-tripping the existing value is safe; setting it to an ordinary collection is what fails.
122
114
 
123
- Right shape — patch only what changes:
115
+ Patch only what changes:
124
116
 
125
117
  ```bash
126
118
  # Rename only:
@@ -140,7 +132,7 @@ mb transform update <id> --file ./.scratch/patch.json --profile <name> --json
140
132
  mb transform update <id> --body '{"tag_ids":[1,3]}' --profile <name> --json
141
133
  ```
142
134
 
143
- `tag_ids` are the integer ids of **transform tags** — discover and manage them with the `transform-tag` group: `mb transform-tag list --json` (find ids; the four built-ins `hourly`/`daily`/`weekly`/`monthly` are seeded), `mb transform-tag create --body '{"name":"nightly"}' --json`, `mb transform-tag update <id>`, `mb transform-tag delete <id>`. Tags are also how a `transform-job` selects what to run — a job executes every transform carrying one of the job's tags (see "Transform jobs" below).
135
+ `tag_ids` are the integer ids of **transform tags** — manage them with the `transform-tag` group: `mb transform-tag list --json` (find ids; the four built-ins `hourly`/`daily`/`weekly`/`monthly` are seeded), `mb transform-tag create --body '{"name":"nightly"}' --json`, `mb transform-tag update <id>`, `mb transform-tag delete <id>`. Tags are also how a `transform-job` selects what to run — a job executes every transform carrying one of the job's tags (see "Transform jobs").
144
136
 
145
137
  If you really must round-trip, project to the writable subset:
146
138
 
@@ -153,13 +145,11 @@ mb transform get <id> --full --profile <name> --json \
153
145
 
154
146
  ## Iterating on a failing transform
155
147
 
156
- When `transform run` fails and you want to retry with a fixed body, **prefer `transform update <id> --file body.json` over `transform delete <id>` + `transform create`.** Update keeps the same row, the same `entity_id`, the same materialized table, and the same on-disk YAML filename:
148
+ When `transform run` fails and you want to retry with a fixed body, **prefer `transform update <id> --file body.json` over `transform delete <id>` + `transform create`.** Update keeps the same row, `entity_id`, materialized table, and on-disk YAML filename:
157
149
 
158
150
  - `git-sync export` produces **one** clean commit containing only the fix, instead of "broken transform" + "remove broken transform" landing as two commits in `git log`.
159
- - You don't have to chase `_2` suffixes minted when two YAMLs share a `name` on disk (see the `transform create` notes above).
160
- - The materialized output table either updates in place or, if the SELECT shape changed incompatibly, errors loudly on the next run rather than landing in a parallel `..._2` table the agent has to clean up. (`transform delete-table <id>` resets the column shape if you need a clean slate.)
161
-
162
- Recipe:
151
+ - You don't chase `_2` suffixes minted when two YAMLs share a `name` on disk.
152
+ - The materialized output table either updates in place or, if the SELECT shape changed incompatibly, errors loudly on the next run rather than landing in a parallel `..._2` table you have to clean up. (`transform delete-table <id>` resets the column shape for a clean slate.)
163
153
 
164
154
  ```bash
165
155
  # 1. Try once
@@ -180,7 +170,7 @@ mb transform update "$ID" --file ./.scratch/source-patch.json --profile <n> --js
180
170
  mb transform run "$ID" --wait --profile <n> --json # → succeeded
181
171
  ```
182
172
 
183
- If you really must `create + delete` instead, do the `delete` **before** the first `git-sync export` so the failed entity never lands in git history. Order matters: agents reflex to "export to checkpoint progress," but for transforms an export of a soft-failed state is mostly noise that needs a follow-up cleanup commit. See the `git-sync` skill, "Read state before mutating" for the ordering rule.
173
+ If you really must `create + delete` instead, do the `delete` **before** the first `git-sync export` so the failed entity never lands in git history — an export of a soft-failed state is noise that needs a follow-up cleanup commit. See `git-sync`, "Read state before mutating", for the ordering rule.
184
174
 
185
175
  ## Drop the materialized table (keep the transform)
186
176
 
@@ -188,7 +178,7 @@ If you really must `create + delete` instead, do the `delete` **before** the fir
188
178
  mb transform delete-table <id> --yes --profile <name>
189
179
  ```
190
180
 
191
- Useful when you've changed the SELECT and want a fresh `CREATE TABLE` on the next run. **`--yes` is required** in non-interactive contexts; without it the command exits with `--yes required to delete non-interactively`.
181
+ Useful when you've changed the SELECT and want a fresh `CREATE TABLE` on the next run. **`--yes` is required** non-interactively; without it the command exits with `refusing to delete <id> without confirmation — pass --yes to proceed non-interactively`.
192
182
 
193
183
  ## Delete the transform
194
184
 
@@ -196,7 +186,7 @@ Useful when you've changed the SELECT and want a fresh `CREATE TABLE` on the nex
196
186
  mb transform delete <id> --yes --profile <name>
197
187
  ```
198
188
 
199
- Removes the definition. Whether the materialized table is dropped depends on the server — check with `mb table list --db-id <db-id> --profile <name> --json` if it matters. Same `--yes` rule as `delete-table`.
189
+ Removes the definition. Whether the materialized table is dropped depends on the server — check with `mb table list --db-id <db-id> --profile <name> --json` if it matters. Same `--yes` rule and message as `delete-table`.
200
190
 
201
191
  ## Transform jobs (schedules)
202
192
 
@@ -212,9 +202,3 @@ mb transform-job set-active false --profile <name> --json # disable every job a
212
202
  ```
213
203
 
214
204
  `transform-job run` is fire-and-forget — the server returns `{message, job_run_id}` immediately, with no per-job-run polling (no `--wait`). Most ad-hoc agent work is one-off `transform run`, not job authoring.
215
-
216
- ## Don't (transform-specific)
217
-
218
- - Don't put `transform run` calls in tight polling loops — pass `--wait` and let the CLI handle the polling. Manual loops without `--wait` will hammer the server.
219
- - Don't author MBQL 4 (the legacy nested `{ type: "query", query: {...} }` shape) by hand — pull a sample with `mb transform get <id> --full --json`. MBQL 5 (`lib/type: "mbql/query"`) **is** authorable by hand thanks to the `mb query --print-schema` + `--dry-run` feedback loop; for non-trivial pipelines you may still prefer building in the UI and exporting.
220
- - Don't paste a `transform get` body into `transform update` — the PUT endpoint only accepts writable keys, and unknown keys (notably `tags`, `source_type`, `entity_id`, `created_at`, `last_run`) leak as raw SQL errors. See "Update body: send only writable keys" above. Use `tag_ids` (not `tags`) on the REST contract.
@@ -1,21 +1,21 @@
1
1
  ---
2
2
  name: visualization
3
- description: Pick the right `display` (chart type) for a card's data and author its `visualization_settings` via the `mb` CLI. Covers which chart fits which data shape, the required and optional settings per chart type, the rule that settings reference OUTPUT columns by name, minimum-viable settings per chart family, and the `column_settings` JSON-string-key footgun. The full per-chart key catalog lives in references. Load whenever choosing or shaping how a card renders — "what chart should I use for this", "create a bar chart", "make this a line chart", "turn this into a pie", "map this by state", "format this column as currency", "set the pie dimension and metric", "the card renders as a table instead of a chart", "add conditional formatting", or any `display` / `visualization_settings` work.
3
+ description: Choose a card's `display` (chart type) and author its `visualization_settings` for the `mb` CLI — which chart fits which data shape, the required keys per chart, the rule that settings name OUTPUT columns, and the `column_settings` JSON-string-key footgun; the full per-chart key catalog is in references. Use when deciding or fixing how a card renders — "what chart should I use", "make this a bar/line/pie chart", "map this by state", "format this column as currency", "add conditional formatting", "the card renders as a table instead of a chart", or any `display` / `visualization_settings` work.
4
4
  allowed-tools: Read, Write, Edit, Bash, AskUserQuestion
5
5
  ---
6
6
 
7
7
  # Visualization: pick the chart, then set it
8
8
 
9
- > **Shared contract (read first).** This skill is part of the `robot-data-engineer` family and follows its shared rules: audience is a non-technical user, so no database jargon (skip "normalize"/"grain"; ERD/foreign key are fine; explain "wide"/"long" the first time you use them). Ask before showing PII row-by-row (names, emails, phones) — default to aggregates. When asked for something the CLI can't do (alerts, dashboard filters), name the limit instead of erroring into raw SQL. Honor the autonomy mode the user picked. Full text and the autonomy slider live in the router — run `mb skills get robot-data-engineer` and read its **Shared Contract** if you haven't.
9
+ > **Building charts as part of a guided data project?** Follow the `data-workflow` **Shared Contract** — answer-first with detail on demand, ask before showing PII, honor the autonomy mode, name what the CLI can't do instead of erroring into raw SQL: `mb skills get data-workflow`.
10
10
 
11
11
  A card has two presentation fields alongside its `dataset_query`:
12
12
 
13
- - **`display`** — the chart type (`bar`, `line`, `pie`, `scalar`, `map`, `table`, …). One closed set; pick from the enum below.
13
+ - **`display`** — the chart type (`bar`, `line`, `pie`, `scalar`, `map`, `table`, …); pick from the valid values below.
14
14
  - **`visualization_settings`** — a map whose keys are **namespaced by `display`** (`graph.*` for bar/line/area/combo, `pie.*` for pie, `table.*` for table, …). The server stores almost anything and **silently ignores keys that don't apply** to the chosen `display`.
15
15
 
16
16
  Nothing validates `visualization_settings` — there is no pre-flight to fail past. A `display` typo or a misnamed key is accepted by the API; the card just renders as a default table or drops the setting. So **the feedback loop is read-back, not pre-flight**: after `card create`/`update`, confirm with `mb card get <id> --full --json` (or open the card) that it rendered as intended.
17
17
 
18
- Flag conventions and body-input precedence live in the `core` skill (`mb skills get core`); the `dataset_query` itself is the `mbql` skill's job (`mb skills get mbql`). This skill is only about how the result is displayed.
18
+ Flag conventions and body-input precedence live in `core` (`mb skills get core`); the `dataset_query` itself is the `mbql` skill's job (`mb skills get mbql`). This skill is only about how the result is displayed.
19
19
 
20
20
  Two steps: **(1) pick the `display` that fits the data**, then **(2) bind the data columns and set options**.
21
21
 
@@ -35,7 +35,7 @@ Decide which relationship in the data matters most, then pick the chart. The sha
35
35
  - **Geographic** → `map`: region/choropleth (a region dimension + a measure), pin (lat + long), or grid/heat (coordinates + measure).
36
36
  - **Precise values, many columns, mixed types, or no chart fits** → `table`; `pivot` for a cross-tab of two dimensions; `object` for a single record's detail.
37
37
 
38
- Closed `display` enum (card-level, non-hidden): `table`, `bar`, `line`, `area`, `row`, `pie`, `scalar`, `smartscalar`, `combo`, `pivot`, `funnel`, `map`, `scatter`, `waterfall`, `progress`, `gauge`, `object`, `sankey`, `boxplot`. (`scalar` **is** the "Number" viz — `display: number` is a legacy serialization alias, not a registered visualization; use `scalar`. `list` exists but is hidden — don't pick it. `heading`/`text`/`link`/`iframe`/`action` are dashcard virtuals, not standalone cards — see references.) An unknown `display` is accepted by the API but renders nothing — typos like `bargraph`/`linechart` are the most common "why is my chart blank" cause.
38
+ Valid `display` values — the registered visualizations: `table`, `bar`, `line`, `area`, `row`, `pie`, `scalar`, `smartscalar`, `combo`, `pivot`, `funnel`, `map`, `scatter`, `waterfall`, `progress`, `gauge`, `object`, `sankey`, `boxplot`. The API types `display` as a plain string and accepts any value — it renders an unknown one as nothing. (`scalar` **is** the "Number" viz — `display: number` is a legacy serialization alias, not a registered visualization; use `scalar`. `list` exists but is hidden — don't pick it. `heading`/`text`/`link`/`iframe`/`action` are dashcard virtuals, not standalone cards — see references.) A typo like `bargraph`/`linechart` is accepted and renders blank — the most common "why is my chart blank" cause.
39
39
 
40
40
  ## Step 2 — bind data columns and set options
41
41
 
@@ -151,7 +151,7 @@ mb skills path visualization # → the skill dir; then Read references
151
151
 
152
152
  ## Don't
153
153
 
154
- - Don't invent `display` values (`bargraph`, `linechart`, `histogram`) or use `number`/`list` — use the closed non-hidden enum; the API accepts a typo and renders nothing.
154
+ - Don't invent `display` values (`bargraph`, `linechart`, `histogram`) or use `number`/`list` — use a registered value; the API accepts a typo and renders nothing.
155
155
  - Don't put numeric field ids in `graph.dimensions`/`pie.metric`/`scalar.field`/`map.latitude_column` etc. — they take **output column-name strings**.
156
156
  - Don't reach for a `pie` with >5 slices, a `combo` of unrelated metrics, or a `pie`/`scalar` to show a trend — see Step 1.
157
157
  - Don't write a `column_settings` key as an object — it's a JSON **string** (`"[\"name\",\"COL\"]"`), inner quotes escaped.
@@ -1,5 +1,17 @@
1
1
  # visualization_settings — per-chart key reference
2
2
 
3
+ ## Contents
4
+
5
+ - [Cartesian — `bar`, `line`, `area`, `combo`, `scatter`, `waterfall`, `row`, `boxplot`](#cartesian--bar-line-area-combo-scatter-waterfall-row-boxplot) — shared keys (binding, stacking, goal/trend, data labels, axes, tooltip) plus `scatter`, `waterfall`, `row`, `boxplot` extras
6
+ - [Part-to-whole & single value — `pie`, `funnel`, `gauge`, `progress`, `scalar`, `smartscalar`](#part-to-whole--single-value--pie-funnel-gauge-progress-scalar-smartscalar)
7
+ - [Tabular, geographic & flow — `table`, `pivot`, `object`, `map`, `sankey`](#tabular-geographic--flow--table-pivot-object-map-sankey) — includes `table.column_formatting` conditional formatting
8
+ - [`column_settings` — per-column formatting](#column_settings--per-column-formatting) — number/date/currency, `view_as`, alignment, mini bars
9
+ - [`series_settings` — per-series styling (cartesian)](#series_settings--per-series-styling-cartesian)
10
+ - [Virtual cards (dashcards only, `card_id: null`)](#virtual-cards-dashcards-only-card_id-null) — heading/text/link/iframe
11
+ - [Click behavior (dashcards only)](#click-behavior-dashcards-only)
12
+
13
+ ---
14
+
3
15
  Authorable keys per `display`, plus the data shape each chart suits and the minimum needed to render. Set keys only to override defaults — an empty `{}` works for a simple aggregate.
4
16
 
5
17
  All column-naming keys (`graph.dimensions`, `pie.dimension`, `table.columns[].name`, `map.latitude_column`, …) take **output column-name strings** — the names the query produces. Every key and value below is identical in the API form (`mb card create`) and the portable git-sync form, with two exceptions: `column_settings` `["ref", …]` keys and click-behavior dimension targets carry a numeric field id in the API form and a name-path in the portable form. In a JSON body, `column_settings` keys are escaped strings: `"[\"name\",\"TOTAL\"]"`.
@@ -20,8 +20,8 @@ mb skills get core # auth, flag conventions, every command group
20
20
  mb skills list # everything available on the installed version
21
21
  ```
22
22
 
23
- **Doing a whole job, not one command?** If the user wants an outcome — "make sense of my data", "build a data model", "go from raw data to a dashboard", "answer questions about my data", "be my data analyst", "set up analytics for X" — load the front-door router instead and let it drive:
23
+ **Doing a whole job, not one command?** If the user wants an outcome — "make sense of my data", "build a data model", "go from raw data to a dashboard", "answer questions about my data", "be my data analyst", "set up analytics for X" — load the guided end-to-end skill instead and let it drive:
24
24
 
25
25
  ```bash
26
- mb skills get robot-data-engineer
26
+ mb skills get data-workflow
27
27
  ```
@@ -1,10 +0,0 @@
1
- import "./command-augment-CAur0XOQ.mjs";
2
- import "./error-CIObEXLY.mjs";
3
- import "./runtime-CmAIahm5.mjs";
4
- import "./capabilities-BX1rnVuH.mjs";
5
- import "./parse-id-B5adfBlS.mjs";
6
- import "./poll-AduuU55-.mjs";
7
- import "./poll-task-B00Qwd87.mjs";
8
- import { SyncSettingsUpdateResult, add_collection_default, setCollectionRemoteSynced, syncSettingsUpdateView } from "./add-collection-H4LcP-9B.mjs";
9
-
10
- export { add_collection_default as default };
@@ -1,19 +0,0 @@
1
- import { defineCommand } from "citty";
2
-
3
- //#region src/commands/auth/index.ts
4
- var auth_default = defineCommand({
5
- meta: {
6
- name: "auth",
7
- description: "Authenticate against a Metabase instance"
8
- },
9
- default: "login",
10
- subCommands: {
11
- login: () => import("./login-C0Rf2hg0.mjs").then((m) => m.default),
12
- status: () => import("./status-DY92F9mn.mjs").then((m) => m.default),
13
- list: () => import("./list-FR8Q1SzV.mjs").then((m) => m.default),
14
- logout: () => import("./logout-C7_UON-s.mjs").then((m) => m.default)
15
- }
16
- });
17
-
18
- //#endregion
19
- export { auth_default as default };
@@ -1,20 +0,0 @@
1
- import { defineCommand } from "citty";
2
-
3
- //#region src/commands/card/index.ts
4
- var card_default = defineCommand({
5
- meta: {
6
- name: "card",
7
- description: "Manage Metabase cards (questions, models, metrics)"
8
- },
9
- subCommands: {
10
- list: () => import("./list-D52_BozQ.mjs").then((mod) => mod.default),
11
- get: () => import("./get-CXMv-r1p.mjs").then((mod) => mod.default),
12
- query: () => import("./query-BUkuB4bZ.mjs").then((mod) => mod.default),
13
- create: () => import("./create-B-mvVFIl.mjs").then((mod) => mod.default),
14
- update: () => import("./update-I3TA2Tem.mjs").then((mod) => mod.default),
15
- archive: () => import("./archive-BCXoM1nX.mjs").then((mod) => mod.default)
16
- }
17
- });
18
-
19
- //#endregion
20
- export { card_default as default };
@@ -1,20 +0,0 @@
1
- import { defineCommand } from "citty";
2
-
3
- //#region src/commands/collection/index.ts
4
- var collection_default = defineCommand({
5
- meta: {
6
- name: "collection",
7
- description: "Manage Metabase collections"
8
- },
9
- subCommands: {
10
- list: () => import("./list-F0vkE22V.mjs").then((mod) => mod.default),
11
- get: () => import("./get-rFcAVIch.mjs").then((mod) => mod.default),
12
- items: () => import("./items-1KkBMiO4.mjs").then((mod) => mod.default),
13
- tree: () => import("./tree-B3f5F_dP.mjs").then((mod) => mod.default),
14
- create: () => import("./create-CF2Zn4pT.mjs").then((mod) => mod.default),
15
- archive: () => import("./archive-hN8PfvhX.mjs").then((mod) => mod.default)
16
- }
17
- });
18
-
19
- //#endregion
20
- export { collection_default as default };
@@ -1,21 +0,0 @@
1
- import { defineCommand } from "citty";
2
-
3
- //#region src/commands/dashboard/index.ts
4
- var dashboard_default = defineCommand({
5
- meta: {
6
- name: "dashboard",
7
- description: "Manage Metabase dashboards"
8
- },
9
- subCommands: {
10
- list: () => import("./list-C4KnM3Rq.mjs").then((mod) => mod.default),
11
- get: () => import("./get-Nc5GOs6-.mjs").then((mod) => mod.default),
12
- cards: () => import("./cards-Bw37jizL.mjs").then((mod) => mod.default),
13
- create: () => import("./create-DSWjS09p.mjs").then((mod) => mod.default),
14
- update: () => import("./update-DOfL_KPx.mjs").then((mod) => mod.default),
15
- "update-dashcard": () => import("./update-dashcard-BXZ4vS15.mjs").then((mod) => mod.default),
16
- archive: () => import("./archive-C6MDtV1F.mjs").then((mod) => mod.default)
17
- }
18
- });
19
-
20
- //#endregion
21
- export { dashboard_default as default };
@@ -1,22 +0,0 @@
1
- import { defineCommand } from "citty";
2
-
3
- //#region src/commands/db/index.ts
4
- var db_default = defineCommand({
5
- meta: {
6
- name: "db",
7
- description: "Inspect and sync Metabase databases",
8
- alias: "database"
9
- },
10
- subCommands: {
11
- list: () => import("./list-CO5J3SZU.mjs").then((m) => m.default),
12
- get: () => import("./get-CHi8tU_1.mjs").then((m) => m.default),
13
- metadata: () => import("./metadata-Db3Kpo-z.mjs").then((m) => m.default),
14
- schemas: () => import("./schemas-EVwEFuTj.mjs").then((m) => m.default),
15
- "schema-tables": () => import("./schema-tables-C45QegaY.mjs").then((m) => m.default),
16
- "sync-schema": () => import("./sync-schema-CQPfffjU.mjs").then((m) => m.default),
17
- "rescan-values": () => import("./rescan-values-DOsDLrRG.mjs").then((m) => m.default)
18
- }
19
- });
20
-
21
- //#endregion
22
- export { db_default as default };
@@ -1,19 +0,0 @@
1
- import { defineCommand } from "citty";
2
-
3
- //#region src/commands/document/index.ts
4
- var document_default = defineCommand({
5
- meta: {
6
- name: "document",
7
- description: "Manage Metabase documents"
8
- },
9
- subCommands: {
10
- list: () => import("./list-DUEYX3bX.mjs").then((mod) => mod.default),
11
- get: () => import("./get-BD_P_Ejc.mjs").then((mod) => mod.default),
12
- create: () => import("./create-D-qE3Oq8.mjs").then((mod) => mod.default),
13
- update: () => import("./update-D5gioyBa.mjs").then((mod) => mod.default),
14
- archive: () => import("./archive-CdUS-OG9.mjs").then((mod) => mod.default)
15
- }
16
- });
17
-
18
- //#endregion
19
- export { document_default as default };
@@ -1,18 +0,0 @@
1
- import { defineCommand } from "citty";
2
-
3
- //#region src/commands/field/index.ts
4
- var field_default = defineCommand({
5
- meta: {
6
- name: "field",
7
- description: "Manage Metabase fields"
8
- },
9
- subCommands: {
10
- get: () => import("./get-CYh2DBsq.mjs").then((m) => m.default),
11
- values: () => import("./values-I503dI7K.mjs").then((m) => m.default),
12
- summary: () => import("./summary-DOxgqJoA.mjs").then((m) => m.default),
13
- update: () => import("./update-CcvDVqNd.mjs").then((m) => m.default)
14
- }
15
- });
16
-
17
- //#endregion
18
- export { field_default as default };
@@ -1,28 +0,0 @@
1
- import { defineCommand } from "citty";
2
-
3
- //#region src/commands/git-sync/index.ts
4
- var git_sync_default = defineCommand({
5
- meta: {
6
- name: "git-sync",
7
- description: "Sync Metabase content with a git remote"
8
- },
9
- subCommands: {
10
- status: () => import("./status-CxYw6zQM.mjs").then((mod) => mod.default),
11
- "is-dirty": () => import("./is-dirty-Z-pqyVyB.mjs").then((mod) => mod.default),
12
- "has-remote-changes": () => import("./has-remote-changes-Bvyv4FLP.mjs").then((mod) => mod.default),
13
- dirty: () => import("./dirty-C-rkrnVM.mjs").then((mod) => mod.default),
14
- "current-task": () => import("./current-task-Dr5dOD4V.mjs").then((mod) => mod.default),
15
- "cancel-task": () => import("./cancel-task-CleJVDNI.mjs").then((mod) => mod.default),
16
- wait: () => import("./wait-oMSs_IdS.mjs").then((mod) => mod.default),
17
- import: () => import("./import-c3o3OAx0.mjs").then((mod) => mod.default),
18
- export: () => import("./export-CGa-SgEV.mjs").then((mod) => mod.default),
19
- stash: () => import("./stash-C1V2FvJR.mjs").then((mod) => mod.default),
20
- branches: () => import("./branches-B6R60Vr1.mjs").then((mod) => mod.default),
21
- "create-branch": () => import("./create-branch-BaZ00MIc.mjs").then((mod) => mod.default),
22
- "add-collection": () => import("./add-collection-BqfYL4FU.mjs").then((mod) => mod.default),
23
- "remove-collection": () => import("./remove-collection-D8ZfB2RN.mjs").then((mod) => mod.default)
24
- }
25
- });
26
-
27
- //#endregion
28
- export { git_sync_default as default };
@@ -1,9 +0,0 @@
1
- import "./command-augment-CAur0XOQ.mjs";
2
- import "./error-CIObEXLY.mjs";
3
- import "./runtime-CmAIahm5.mjs";
4
- import "./capabilities-BX1rnVuH.mjs";
5
- import "./poll-AduuU55-.mjs";
6
- import "./poll-task-B00Qwd87.mjs";
7
- import { IsDirtyResult, is_dirty_default } from "./is-dirty-BE53XwOC.mjs";
8
-
9
- export { is_dirty_default as default };
@@ -1,18 +0,0 @@
1
- import { defineCommand } from "citty";
2
-
3
- //#region src/commands/library/index.ts
4
- var library_default = defineCommand({
5
- meta: {
6
- name: "library",
7
- description: "Curate the Metabase Library — publish trusted tables to its Data collection"
8
- },
9
- subCommands: {
10
- get: () => import("./get-b4xbfFbA.mjs").then((m) => m.default),
11
- create: () => import("./create-CZ0_s5ap.mjs").then((m) => m.default),
12
- publish: () => import("./publish-h5RJh6im.mjs").then((m) => m.default),
13
- unpublish: () => import("./unpublish-B5RDeN-V.mjs").then((m) => m.default)
14
- }
15
- });
16
-
17
- //#endregion
18
- export { library_default as default };
@@ -1,19 +0,0 @@
1
- import { defineCommand } from "citty";
2
-
3
- //#region src/commands/measure/index.ts
4
- var measure_default = defineCommand({
5
- meta: {
6
- name: "measure",
7
- description: "Manage Metabase measures"
8
- },
9
- subCommands: {
10
- list: () => import("./list-DHb4vQUM.mjs").then((mod) => mod.default),
11
- get: () => import("./get-C6g2C4dM.mjs").then((mod) => mod.default),
12
- create: () => import("./create-BEkuqChu.mjs").then((mod) => mod.default),
13
- update: () => import("./update-PZPNx0Xd.mjs").then((mod) => mod.default),
14
- archive: () => import("./archive-B3cjzOIK.mjs").then((mod) => mod.default)
15
- }
16
- });
17
-
18
- //#endregion
19
- export { measure_default as default };
@@ -1,19 +0,0 @@
1
- import { defineCommand } from "citty";
2
-
3
- //#region src/commands/segment/index.ts
4
- var segment_default = defineCommand({
5
- meta: {
6
- name: "segment",
7
- description: "Manage Metabase segments"
8
- },
9
- subCommands: {
10
- list: () => import("./list-BqgbrpQQ.mjs").then((mod) => mod.default),
11
- get: () => import("./get-BM-d3-zk.mjs").then((mod) => mod.default),
12
- create: () => import("./create-CfLaBO0V.mjs").then((mod) => mod.default),
13
- update: () => import("./update-BIZ9XhjS.mjs").then((mod) => mod.default),
14
- archive: () => import("./archive-D1mfiftv.mjs").then((mod) => mod.default)
15
- }
16
- });
17
-
18
- //#endregion
19
- export { segment_default as default };
@@ -1,17 +0,0 @@
1
- import { defineCommand } from "citty";
2
-
3
- //#region src/commands/setting/index.ts
4
- var setting_default = defineCommand({
5
- meta: {
6
- name: "setting",
7
- description: "Inspect and update Metabase settings"
8
- },
9
- subCommands: {
10
- list: () => import("./list-Drr4JiWg.mjs").then((m) => m.default),
11
- get: () => import("./get-CARdLkmp.mjs").then((m) => m.default),
12
- set: () => import("./set-C3EAuyb8.mjs").then((m) => m.default)
13
- }
14
- });
15
-
16
- //#endregion
17
- export { setting_default as default };
@@ -1,19 +0,0 @@
1
- import { defineCommand } from "citty";
2
-
3
- //#region src/commands/snippet/index.ts
4
- var snippet_default = defineCommand({
5
- meta: {
6
- name: "snippet",
7
- description: "Manage Metabase native query snippets"
8
- },
9
- subCommands: {
10
- list: () => import("./list-CSjFJDls.mjs").then((mod) => mod.default),
11
- get: () => import("./get-BQxLtFE-.mjs").then((mod) => mod.default),
12
- create: () => import("./create-DwoqFYb9.mjs").then((mod) => mod.default),
13
- update: () => import("./update-CiWPEqQ-.mjs").then((mod) => mod.default),
14
- archive: () => import("./archive-CQQWkC5S.mjs").then((mod) => mod.default)
15
- }
16
- });
17
-
18
- //#endregion
19
- export { snippet_default as default };
@@ -1,19 +0,0 @@
1
- import { defineCommand } from "citty";
2
-
3
- //#region src/commands/table/index.ts
4
- var table_default = defineCommand({
5
- meta: {
6
- name: "table",
7
- description: "Manage Metabase tables"
8
- },
9
- subCommands: {
10
- list: () => import("./list-DIvOPW1g.mjs").then((m) => m.default),
11
- get: () => import("./get-Q2WZ79q_.mjs").then((m) => m.default),
12
- metadata: () => import("./metadata-FltZq5Ek.mjs").then((m) => m.default),
13
- fields: () => import("./fields-Recymu7n.mjs").then((m) => m.default),
14
- update: () => import("./update-DudyZ-FP.mjs").then((m) => m.default)
15
- }
16
- });
17
-
18
- //#endregion
19
- export { table_default as default };