@metabase/cli 0.1.5 → 0.1.6

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 (217) hide show
  1. package/README.md +115 -102
  2. package/dist/{add-collection-C_iovi9i.mjs → add-collection-BU8r3r2M.mjs} +9 -4
  3. package/dist/add-collection-C0w6ACQF.mjs +11 -0
  4. package/dist/{archive-Dvzrmdbk.mjs → archive-BNinrUak.mjs} +9 -8
  5. package/dist/{archive-WaEW85NB.mjs → archive-C1enZgKV.mjs} +8 -7
  6. package/dist/archive-CDA0KxL8.mjs +40 -0
  7. package/dist/{archive-BKPO8lEO.mjs → archive-CRhiBpPJ.mjs} +9 -8
  8. package/dist/{archive-DdaP94H3.mjs → archive-DMPS8Kih.mjs} +9 -8
  9. package/dist/archive-lWgqiFAt.mjs +40 -0
  10. package/dist/auth-CzXb_zB2.mjs +19 -0
  11. package/dist/{body-XtR7-uCO.mjs → body-DjdFxjpg.mjs} +4 -4
  12. package/dist/{branches-XUY4JY-X.mjs → branches-B1WRfG7-.mjs} +11 -7
  13. package/dist/{cancel-BrUVO_ax.mjs → cancel-Dl_Ho056.mjs} +7 -6
  14. package/dist/{cancel-task-oXheTOB6.mjs → cancel-task-CdigdCaO.mjs} +11 -7
  15. package/dist/capabilities-7e9MgquN.mjs +29 -0
  16. package/dist/card-DP4rfoOi.mjs +21 -0
  17. package/dist/{card-CQxvHeyP.mjs → card-DlCAaAPq.mjs} +1 -1
  18. package/dist/{cards-CONTTAG9.mjs → cards-BGiJS675.mjs} +8 -7
  19. package/dist/cli.mjs +264 -44
  20. package/dist/collection-tY18ezvn.mjs +21 -0
  21. package/dist/{predicates-CGO17Q15.mjs → command-augment-BH9qgQ5u.mjs} +66 -14
  22. package/dist/create-BNiva__H.mjs +52 -0
  23. package/dist/{create-Ca9lIDwP.mjs → create-BTcpaop_.mjs} +9 -8
  24. package/dist/{create-V-q2rU0T.mjs → create-BYlIju0b.mjs} +14 -12
  25. package/dist/{create-DZxUeqdf.mjs → create-Be_0Vier.mjs} +10 -9
  26. package/dist/{create-kYpjobrq.mjs → create-CHF313Qg.mjs} +13 -9
  27. package/dist/{create-swbIXdo5.mjs → create-CwGtmwqm.mjs} +14 -12
  28. package/dist/{create-Dq25vsMu.mjs → create-CzzrbL0u.mjs} +10 -9
  29. package/dist/{create-Le3Bqn7b.mjs → create-DGth_uOp.mjs} +14 -12
  30. package/dist/{create-branch-D5u14AxL.mjs → create-branch-DKZkoQ64.mjs} +11 -7
  31. package/dist/{create-Cs2xntFG.mjs → create-dhxPxfF3.mjs} +16 -14
  32. package/dist/{credentials-BIQ1cEzM.mjs → credentials-dzeq7ckm.mjs} +12 -11
  33. package/dist/{current-task-DCq7rk9V.mjs → current-task-CCRzm0_7.mjs} +11 -7
  34. package/dist/dashboard-ChM_Tu0l.mjs +22 -0
  35. package/dist/{dashboard-CnMD04PQ.mjs → dashboard-FY5UzJ_Z.mjs} +2 -1
  36. package/dist/{database-BSvzYlRe.mjs → database-CIXwHKjK.mjs} +3 -3
  37. package/dist/{database-vvig8k4x.mjs → database-lH-B3G1I.mjs} +1 -1
  38. package/dist/db-DrQn_i3W.mjs +22 -0
  39. package/dist/{remove-C6bS0Z6w.mjs → delete-CM3jnAeQ.mjs} +21 -20
  40. package/dist/{delete-CUx6RT9e.mjs → delete-Dimc-2y8.mjs} +9 -8
  41. package/dist/{delete-VTAS9EUt.mjs → delete-ZjnV35OJ.mjs} +9 -8
  42. package/dist/{delete-runtime-DfFMWJJ6.mjs → delete-runtime-B6RQo_pw.mjs} +5 -3
  43. package/dist/{delete-table-DzUneMKe.mjs → delete-table-agZJpivt.mjs} +9 -8
  44. package/dist/{deprovision-CpJfGgCt.mjs → deprovision-CwxcIT3k.mjs} +16 -12
  45. package/dist/{dirty-nkAOXxgC.mjs → dirty-D4d0yHqj.mjs} +11 -7
  46. package/dist/{docker-D5FTIoD0.mjs → docker-Oq80q3tu.mjs} +4 -4
  47. package/dist/{translate-Cqsd0Px5.mjs → eid-BXzaQh0o.mjs} +37 -22
  48. package/dist/error-C9S6PN3-.mjs +190 -0
  49. package/dist/{export-BWvY7X_G.mjs → export-DTygoXBP.mjs} +17 -16
  50. package/dist/field-Z6Pcxf4n.mjs +19 -0
  51. package/dist/{fields-dH16G5UV.mjs → fields-CoQi99gv.mjs} +9 -8
  52. package/dist/{get-BnBRKHr7.mjs → get-Bzys7vgp.mjs} +8 -7
  53. package/dist/{get-B7i_nYJB.mjs → get-C2p383Qc.mjs} +8 -7
  54. package/dist/{get-D96QEU49.mjs → get-C3HdQ91a.mjs} +8 -7
  55. package/dist/{get-DNN1X2gN.mjs → get-CP3Z3NiH.mjs} +9 -8
  56. package/dist/{get-CACaBFLt.mjs → get-C_w1kvN3.mjs} +9 -8
  57. package/dist/{get-D8e_RzZ0.mjs → get-CzuzeKSe.mjs} +10 -9
  58. package/dist/{get-C6SR3A9t.mjs → get-D3SbEQSE.mjs} +10 -9
  59. package/dist/{get-7macOPAI.mjs → get-DFxZXaKz.mjs} +7 -7
  60. package/dist/{get-DAWofnzK.mjs → get-DQTZG_NP.mjs} +8 -7
  61. package/dist/{get-BcqxMVC1.mjs → get-DSWFjy7O.mjs} +8 -7
  62. package/dist/{get-R7OaVL_t.mjs → get-Ddr0XLh7.mjs} +8 -7
  63. package/dist/{get-B08K82JV.mjs → get-Hc93A0Yz.mjs} +8 -7
  64. package/dist/{get-CKxlhMy1.mjs → get-lb7q3JYs.mjs} +7 -6
  65. package/dist/get-run-B7sKdaDU.mjs +38 -0
  66. package/dist/git-sync-CiGAad76.mjs +28 -0
  67. package/dist/{has-remote-changes-BAnIXQXU.mjs → has-remote-changes-BY10-nnE.mjs} +11 -7
  68. package/dist/{import-CfdPEMng.mjs → import-CiMz4Wz-.mjs} +17 -16
  69. package/dist/{input-BQ-BZA8h.mjs → input-cMSEqISy.mjs} +7 -4
  70. package/dist/{is-dirty-CZWcG0vj.mjs → is-dirty-BZOaryxT.mjs} +9 -4
  71. package/dist/is-dirty-Ume4oV0j.mjs +10 -0
  72. package/dist/{items-DqwahOKf.mjs → items-BWfvkY-J.mjs} +9 -8
  73. package/dist/key-C2XG394c.mjs +17 -0
  74. package/dist/license-Dxarh-gG.mjs +17 -0
  75. package/dist/{list-vF4EneaE.mjs → list--OYdUTtu.mjs} +7 -6
  76. package/dist/{list-yxVAE1S7.mjs → list-2j7GsXsl.mjs} +7 -6
  77. package/dist/{list-D41gfkKb.mjs → list-BI4zr8LW.mjs} +10 -8
  78. package/dist/{list-BpNU1neq.mjs → list-Brgh-Z2v.mjs} +8 -6
  79. package/dist/{list-ViT2KWhv.mjs → list-C3hfovHv.mjs} +7 -6
  80. package/dist/{list-CQkDqphl.mjs → list-CL7eCOQE.mjs} +7 -6
  81. package/dist/{list-L63TpX1t.mjs → list-Clz5igWg.mjs} +7 -7
  82. package/dist/list-D4sFiqX8.mjs +173 -0
  83. package/dist/{list-oftHLFbE.mjs → list-DXH7TlkU.mjs} +9 -7
  84. package/dist/{list-BqNMpIXy.mjs → list-DZ8fNUoQ.mjs} +9 -8
  85. package/dist/{list-Bkd7Nbds.mjs → list-SOG0whQ-.mjs} +7 -6
  86. package/dist/{list-J277Qtki.mjs → list-d58BprgJ.mjs} +7 -6
  87. package/dist/{list-DJcGwJ4W.mjs → list-sD5N3fGk.mjs} +9 -8
  88. package/dist/{list-DBOYoJtA.mjs → list-zSO0DMw-.mjs} +10 -6
  89. package/dist/{login-D1nZwgKv.mjs → login-Bm2AnCez.mjs} +65 -80
  90. package/dist/{logout-DD4q5whi.mjs → logout-BlyRJODO.mjs} +8 -7
  91. package/dist/{logs-Ci3mJE2z.mjs → logs-CywPikkL.mjs} +9 -8
  92. package/dist/{manifest-CGM7XNLC.mjs → manifest-BBR46KFM.mjs} +15 -15
  93. package/dist/measure-C44EK_xt.mjs +20 -0
  94. package/dist/{measure-BEQfnLdN.mjs → measure-ClESGxIb.mjs} +2 -2
  95. package/dist/{metadata-BDat-jN9.mjs → metadata-B8ZSF9LA.mjs} +10 -9
  96. package/dist/{metadata-29_qlqbz.mjs → metadata-DqiI2q9q.mjs} +9 -8
  97. package/dist/parse-enum-CrEWOhuY.mjs +11 -0
  98. package/dist/{parse-id-CysSaCbf.mjs → parse-id-lk_K-CEF.mjs} +1 -1
  99. package/dist/{parse-ref-D1yeDOn8.mjs → parse-ref-BiETXmvm.mjs} +1 -1
  100. package/dist/{parse-schemas-B10n01ez.mjs → parse-schemas-BqUdWUwq.mjs} +2 -2
  101. package/dist/{path-DLByFMMA.mjs → path-AEtZ3mBq.mjs} +7 -7
  102. package/dist/{poll-p9Y7-JEQ.mjs → poll-DHKDpCiq.mjs} +2 -2
  103. package/dist/{poll-task-BQe0NvJZ.mjs → poll-task-Cooi0lQV.mjs} +3 -20
  104. package/dist/{preflight-CvFu0Cct.mjs → preflight-aXV5LyDs.mjs} +4 -4
  105. package/dist/{process-zJeVJZTM.mjs → process-C7V8LJ-j.mjs} +1 -1
  106. package/dist/{prompt-DgDNy_Pc.mjs → prompt-CFKoys7k.mjs} +3 -1
  107. package/dist/{provision-BP-b4Are.mjs → provision-UWcNDoDe.mjs} +29 -24
  108. package/dist/{ps-BxQdpkr5.mjs → ps-CJU0EbrC.mjs} +5 -3
  109. package/dist/ps-DEroLgbI.mjs +11 -0
  110. package/dist/{query-CFH4nBlK.mjs → query-AaKzYnTY.mjs} +9 -8
  111. package/dist/{query-C7zTlFJA.mjs → query-BlsVNZpD.mjs} +15 -13
  112. package/dist/{remove-BuWxx3hY.mjs → remove-BFWun0e8.mjs} +9 -8
  113. package/dist/{remove-collection-Bc4roCq0.mjs → remove-collection-CoCmrrQs.mjs} +13 -9
  114. package/dist/{render-DuoDUTVL.mjs → render-CfznwleY.mjs} +15 -17
  115. package/dist/render-OQn3iRsI.mjs +32 -0
  116. package/dist/{rescan-values-DabyRYQ_.mjs → rescan-values-C0FDsjT7.mjs} +10 -9
  117. package/dist/{run-Cl-9RtC4.mjs → run-B4Wn43zm.mjs} +10 -9
  118. package/dist/{runs-BH6s1Zao.mjs → runs-Bbaszr18.mjs} +9 -8
  119. package/dist/{runtime-CDu6fykq.mjs → runtime-Dmv5VtUK.mjs} +657 -428
  120. package/dist/{schema-tables-i58wp_p3.mjs → schema-tables-CaWinbuK.mjs} +9 -8
  121. package/dist/{schemas-_m8RYRl9.mjs → schemas-DUgGpAyB.mjs} +7 -6
  122. package/dist/{search-DObOsjbP.mjs → search-BLrBXLUk.mjs} +12 -16
  123. package/dist/segment-B3Uwwcsm.mjs +20 -0
  124. package/dist/{set-CJA9dpK6.mjs → set-B8cUbRLD.mjs} +13 -12
  125. package/dist/{set-CwVWeAsi.mjs → set-DfGsta5O.mjs} +11 -10
  126. package/dist/{setting-Czy4ws6h.mjs → setting-D2p2MA7f.mjs} +3 -3
  127. package/dist/{setup-DqBOe3HZ.mjs → setup-C9ikBRw_.mjs} +9 -8
  128. package/dist/{skills-C2rTVj0n.mjs → skills-CUHIcQS6.mjs} +3 -3
  129. package/dist/{skills-CHU7uuDU.mjs → skills-CiN1OQ8W.mjs} +2 -2
  130. package/dist/snippet-B7D0uWlz.mjs +20 -0
  131. package/dist/{start-CfruN4wF.mjs → start-3PX3ahjT.mjs} +68 -37
  132. package/dist/{stash-CWuXKSZq.mjs → stash-EIDcSvpF.mjs} +17 -16
  133. package/dist/{status-D-RYZB9G.mjs → status-95ElRAu9.mjs} +12 -8
  134. package/dist/status-B0_MiZEf.mjs +100 -0
  135. package/dist/status-CEplmC44.mjs +34 -0
  136. package/dist/{stop-D8Hr4cKX.mjs → stop-CQ0XGrN8.mjs} +11 -10
  137. package/dist/{summary-Lt2XLBK9.mjs → summary-C12LiEuJ.mjs} +8 -7
  138. package/dist/{sync-schema-BDElSynU.mjs → sync-schema-Ba8M3DiX.mjs} +10 -9
  139. package/dist/{table-B-PYcgGb.mjs → table-C7a5V6Zn.mjs} +1 -1
  140. package/dist/table-e6h8SLVX.mjs +20 -0
  141. package/dist/transform-BMYh1lsC.mjs +25 -0
  142. package/dist/transform-job-Cm7z5TfH.mjs +20 -0
  143. package/dist/{transform-job-BrhOLO4M.mjs → transform-job-DeTDPMxt.mjs} +1 -1
  144. package/dist/{tree-DfvjDjmk.mjs → tree-Des2ZG9d.mjs} +6 -5
  145. package/dist/{update-CqnDMNtZ.mjs → update-Bx54nWEI.mjs} +17 -15
  146. package/dist/{update-D9Z8cL7h.mjs → update-CyIZdbIQ.mjs} +11 -10
  147. package/dist/{update-CVxOxmt6.mjs → update-DBi5U8zb.mjs} +16 -14
  148. package/dist/{update-BYduslhn.mjs → update-DHZubok3.mjs} +18 -14
  149. package/dist/{update-BgcroYkF.mjs → update-DSgceARZ.mjs} +11 -10
  150. package/dist/{update-zp7pCBZH.mjs → update-DzAN4SPj.mjs} +15 -13
  151. package/dist/{update-qnFY5IuC.mjs → update-F6DmZncY.mjs} +11 -10
  152. package/dist/{update-B0bjPqKC.mjs → update-_QfgNa53.mjs} +12 -11
  153. package/dist/{update-dashcard-CQ3kmmss.mjs → update-dashcard-wpSjv4M7.mjs} +11 -10
  154. package/dist/{update-DzgXF082.mjs → update-mYVnoYNV.mjs} +15 -13
  155. package/dist/{update-DuA8-cCq.mjs → update-njHe3j-s.mjs} +15 -13
  156. package/dist/{upgrade-CIgTr2CG.mjs → upgrade-iAuvhX-W.mjs} +9 -8
  157. package/dist/{url-B5MgZXzg.mjs → url-DWaT6WIZ.mjs} +11 -10
  158. package/dist/{uuid-CJz9TmHI.mjs → uuid-CMKnS8-z.mjs} +8 -6
  159. package/dist/{validate-CB0bu50i.mjs → validate-dPEOnOf8.mjs} +2 -1
  160. package/dist/{validate-query-CavIA0Q2.mjs → validate-query-Cw6WE5Y8.mjs} +3 -3
  161. package/dist/{values-BXN6tx1i.mjs → values-BfSTAbzc.mjs} +8 -7
  162. package/dist/verify-D5YtTqqp.mjs +79 -0
  163. package/dist/{wait-BFqBlg0y.mjs → wait-8yV9_WIo.mjs} +2 -2
  164. package/dist/{wait-tDp9ZOou.mjs → wait-Bv3Tsnv4.mjs} +12 -8
  165. package/dist/{wait-flags-CN-e9zNq.mjs → wait-flags-Dzq9BGQY.mjs} +20 -9
  166. package/dist/workspace-CKLZrR7l.mjs +26 -0
  167. package/dist/{workspace-credentials-4lIxxz4g.mjs → workspace-credentials-BXpABsNZ.mjs} +2 -2
  168. package/dist/{yaml-ECiog374.mjs → yaml-YTQiYJ9s.mjs} +1 -1
  169. package/package.json +2 -1
  170. package/skill-data/core/SKILL.md +55 -453
  171. package/skill-data/git-sync/SKILL.md +1 -1
  172. package/skill-data/mbql/SKILL.md +156 -0
  173. package/skill-data/mbql/references/operators.md +253 -0
  174. package/skill-data/transform/SKILL.md +2 -40
  175. package/skill-data/viz/SKILL.md +137 -0
  176. package/skill-data/viz/references/settings.md +312 -0
  177. package/skill-data/workspace/SKILL.md +45 -63
  178. package/skills/metabase-cli/SKILL.md +5 -26
  179. package/dist/add-collection-ucsyAMkV.mjs +0 -11
  180. package/dist/api-key-BENHbTbV.mjs +0 -13
  181. package/dist/auth-DICRtJDy.mjs +0 -19
  182. package/dist/card-l-UmrUIo.mjs +0 -20
  183. package/dist/collection-oV0olVY-.mjs +0 -19
  184. package/dist/command-augment-D9pI9Vbh.mjs +0 -11
  185. package/dist/create-CrUq6sib.mjs +0 -125
  186. package/dist/create-D3Z878yr.mjs +0 -50
  187. package/dist/dashboard-hbKDd36X.mjs +0 -20
  188. package/dist/db-qVK6NsdB.mjs +0 -22
  189. package/dist/eid-CDFXX_6H.mjs +0 -13
  190. package/dist/field-C0LE7RQI.mjs +0 -18
  191. package/dist/flag-pair-Fmcdkrfx.mjs +0 -17
  192. package/dist/get-run-CwFuR4Uw.mjs +0 -36
  193. package/dist/git-sync-DV7YjniX.mjs +0 -28
  194. package/dist/is-dirty-LxVbm2C5.mjs +0 -10
  195. package/dist/key-CCJdVWKc.mjs +0 -12
  196. package/dist/license-Cb6ewEJO.mjs +0 -17
  197. package/dist/list-DV6CONhp.mjs +0 -55
  198. package/dist/measure-XhJuL77y.mjs +0 -19
  199. package/dist/package-DFUprkSZ.mjs +0 -85
  200. package/dist/ps-Bk6unzaX.mjs +0 -11
  201. package/dist/segment-DfxZdJmR.mjs +0 -19
  202. package/dist/snippet-BCY4KHBU.mjs +0 -19
  203. package/dist/status-1oUnw803.mjs +0 -56
  204. package/dist/status-J9HIDcA5.mjs +0 -32
  205. package/dist/table-BwX3Ib5f.mjs +0 -19
  206. package/dist/transform-iaAi37V0.mjs +0 -24
  207. package/dist/transform-job-Bemonf82.mjs +0 -19
  208. package/dist/workspace-BBsT0H0g.mjs +0 -24
  209. /package/dist/{body-flags-BK7J6Daz.mjs → body-flags-D7q87Btw.mjs} +0 -0
  210. /package/dist/{field-B3gvaqpK.mjs → field-yomXlkvl.mjs} +0 -0
  211. /package/dist/{paginate-CTSfuYiF.mjs → paginate-Dfm9eO9A.mjs} +0 -0
  212. /package/dist/{revision-message-flag-oyq2xrDU.mjs → revision-message-flag-WmsIzUOM.mjs} +0 -0
  213. /package/dist/{segment-BMrUBz94.mjs → segment-Be2v4ilr.mjs} +0 -0
  214. /package/dist/{setting-CTaAeMci.mjs → setting-oL97SNeO.mjs} +0 -0
  215. /package/dist/{snippet-CSWqkslB.mjs → snippet-COggaWxx.mjs} +0 -0
  216. /package/dist/{transform-DR4ejuPM.mjs → transform-GTW3G-01.mjs} +0 -0
  217. /package/dist/{workspace-DUfqhPm5.mjs → workspace-BBXJczJK.mjs} +0 -0
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: core
3
- description: Drive a Metabase instance from the terminal via the `mb` CLI. Authenticate with named profiles; inspect databases (list, get, full metadata rollup, schemas, tables in a schema) and trigger manual schema sync / field-values rescan; inspect tables, fields; list/get/create/update/archive cards (questions, models, metrics) and run them as JSON/CSV/XLSX; list/get/create/update dashboards and patch dashcards; list/get/create collections and traverse the hierarchy by id, entity_id, or "root"/"trash" (with items and recursive tree); list/get/create/update/archive native query snippets, segments, and measures; author/update/run transforms and schedule transform-jobs; read/update settings; search content (cards, dashboards, collections, transforms, metrics); manage Enterprise workspaces; git-sync to/from a git remote (status, dirty, import, export, branches, stash, add/remove a collection from sync). Use whenever the user wants to interact with a Metabase from the terminal — "log into metabase", "what profiles do I have", "list cards", "run card 42 as CSV", "create a transform", "list dashboards", "move a dashcard", "list collections", "what's in collection 4", "show the collection tree", "list snippets", "create a segment", "archive a measure", "search metabase for X", "spin up a workspace", "import the latest changes", "add a directory to git sync", "set a setting", "what schemas are in this database", "trigger a sync", "rescan field values", or anything hitting `mb <verb>`.
3
+ description: Drive a Metabase instance from the terminal via the `mb` CLI auth, databases, cards, dashboards, collections, transforms, queries, search, git-sync, Enterprise workspaces. Use for any `mb <verb>` task.
4
4
  allowed-tools: Read, Write, Edit, Bash, AskUserQuestion
5
5
  ---
6
6
 
@@ -11,55 +11,43 @@ The official Metabase CLI (`mb`) drives a Metabase instance over its REST API. I
11
11
  Top-level command groups (run `mb <group> --help` to discover verbs):
12
12
 
13
13
  ```
14
- auth | license | db | table | field | query | card | dashboard | snippet | segment | measure | collection | transform | transform-job
15
- setting | search | git-sync | workspace | setup | api-key | eid
14
+ auth | db | table | field | query | card | dashboard | snippet | segment | measure | collection
15
+ transform | transform-job | setting | search | git-sync | workspace | setup | eid | uuid | upgrade | skills
16
16
  ```
17
17
 
18
- The general patterns below — auth, flag conventions, output flags, body input, common verb shapes — apply across **every** group. Three flows have enough surface to warrant their own specialized skills; load them on demand (see "Specialized skills" near the bottom).
18
+ The patterns below — auth, flag conventions, output flags, body input — apply across **every** group. Per-command flags, examples, and output schemas live in `mb __manifest` (see below). A few flows have their own specialized skills; load them on demand (see "Specialized skills"). Authoring any query body (cards, transforms, measures, segments, ad-hoc `mb query`) is one — load `mbql` whenever you build MBQL by hand.
19
19
 
20
20
  ## Auth & profiles
21
21
 
22
- **The agent does not log in for the user.** Authentication is the human's job — they pick the base URL, paste credentials, and store them as a named profile under their own login. The agent's role is to _check_ what profiles exist, _ask_ which to use, and pass `--profile <name>` through every command.
22
+ **The agent does not log in for the user.** Authentication is the human's job — they pick the base URL, paste credentials, and store them as a named profile. The agent's role is to _check_ what profiles exist, _ask_ which to use, and pass `--profile <name>` through every command.
23
23
 
24
- **The one exception** is a freshly bootstrapped workspace child. The child's API credentials are minted by the parent the human already authorized; the agent reads them via `mb workspace credentials <ws-id>` and saves them as a new profile non-interactively. This is the **only** legitimate place for the agent to call `auth login`. See the `workspace` skill, step 4 and even there, pipe the key on stdin (`--api-key-stdin`), never on a flag value.
25
-
26
- For everything else (parent profile, staging, prod, anything pointing at a Metabase the user has direct credentials for), follow the flow below.
24
+ **The one exception** is a freshly bootstrapped workspace child: its API key is minted by the parent the human already authorized, so the agent reads it via `mb workspace credentials <ws-id>` and saves it with `auth login` piping the key on **stdin** (`printf '%s' "$KEY" | mb auth login …`), never on an `--api-key` flag (the CLI rejects the flag form). See the `workspace` skill, step 4.
27
25
 
28
26
  ### Discover what's already configured
29
27
 
30
28
  ```bash
31
- mb auth list --json # → {data: [{profile, url, present}], returned, total}
29
+ mb auth list --json # → {data: [{profile, url, authenticated, status, …}], returned, total}
32
30
  mb auth status --json # → {profile, present, url} for the default profile
33
31
  mb auth status --profile <name> --json # → status of a specific profile
34
32
  ```
35
33
 
36
- `auth list` is the primary enumeration path — one call returns every configured profile with sanitized URL and `present` flag. Use it before asking the user which profile to pick. `auth status` is a single-profile probe; reach for it when you know the name and want a quick health check.
37
-
38
- If `auth list` returns an empty `data: []` or the user has no profile set up, **stop and ask them to log in themselves**:
39
-
40
- > Please run, yourself, `mb auth login --url <your-base-url> --profile <name>`. Tell me the profile name when you're done.
41
-
42
- Don't suggest a base URL, paste an API key, or run `auth login` on their behalf. Profile names are arbitrary local labels — `prod`, `staging`, the workspace name — let the user pick.
34
+ `auth list` is the primary enumeration path — one call returns every configured profile with sanitized URL, an `authenticated` flag, and a probe `status` (`ok` / `auth-failed` / `network-error` / `server-error` / `not-probed`). Use it before asking the user which profile to pick. If it returns an empty `data: []`, ask the user to run `mb auth login` themselves (see the policy above) and tell you the profile name. `auth status` is a single-profile health probe when you already know the name.
43
35
 
44
36
  ### Pick the profile to use
45
37
 
46
- Run `mb auth list --json` first. If exactly one profile is configured and the user's intent doesn't disambiguate, use it. If multiple profiles exist and the user hasn't named one, ask via `AskUserQuestion`, presenting the names from `auth list` as options. Once a name is established, pass `--profile <name>` to **every** subsequent command.
38
+ If exactly one profile is configured and the user's intent doesn't disambiguate, use it. If multiple profiles exist and the user hasn't named one, ask via `AskUserQuestion`, presenting the names from `auth list`. Once a name is established, pass `--profile <name>` to **every** subsequent command. Profile names are arbitrary local labels — `prod`, `staging`, the workspace name — let the user pick.
47
39
 
48
40
  ### Other secrets (license, warehouse passwords)
49
41
 
50
42
  Same rule: the human runs the storing command. To check whether a license is present:
51
43
 
52
44
  ```bash
53
- mb license status --profile <name> --json # → {present: bool}
45
+ mb workspace license status --json # → {present: bool}
54
46
  ```
55
47
 
56
- If `present: false`, ask:
57
-
58
- > Please run `echo "<your-token>" | mb license set --profile <name>` from your terminal — don't paste the token in chat.
59
-
60
- ## Flag conventions (read once, internalize)
48
+ If `present: false`, ask the user to run `echo "<your-token>" | mb workspace license set` from their terminal — don't paste the token in chat.
61
49
 
62
- These trip up every fresh run.
50
+ ## Flag conventions
63
51
 
64
52
  ### `--profile` is per-subcommand, not global
65
53
 
@@ -70,27 +58,16 @@ These trip up every fresh run.
70
58
 
71
59
  `--profile` attaches **after** the full verb chain (`table list`, `card get`, `workspace start`).
72
60
 
73
- ### When you do call `auth login` (workspace child only), pipe the key on stdin
74
-
75
- The agent normally doesn't run `auth login` (see "Auth & profiles" — the human does). The one place it _does_ — saving a workspace child's API key after `workspace credentials` — must use stdin, not a flag value:
76
-
77
- ```bash
78
- ✅ printf '%s' "$KEY" | mb auth login --url <url> --api-key-stdin --profile <n> --json
79
- ❌ mb auth login --api-key "$KEY" … # → warns + rejects
80
- ```
81
-
82
- Reason: shell history and process listings leak the value. The CLI rejects the flag form on purpose.
83
-
84
61
  ### `--wait` for async operations
85
62
 
86
- `workspace start`, `workspace database provision`, `transform run`, and similar async verbs return immediately by default. Pass `--wait` for any interactive flow where the next step depends on completion. Without `--wait` you'll race the operation and see "not ready" / `state: starting` / transient connection refusals.
63
+ `workspace start`, `workspace database provision`, `transform run`, and similar async verbs return immediately by default. Pass `--wait` for any interactive flow where the next step depends on completion. Without it you'll race the operation and see "not ready" / `state: starting` / transient connection refusals.
87
64
 
88
65
  ### Some outputs are JSON envelopes, not bare strings
89
66
 
90
67
  A handful of "lookup" verbs return a JSON object even when you only want a single field. `mb workspace url <id>` returns `{"workspace_id": ..., "url": "http://..."}`, not `"http://..."`. Don't drop them raw into another flag — extract:
91
68
 
92
69
  ```bash
93
- WS_URL=$(mb workspace url <id> --profile <n> --json | jq -r '.url')
70
+ WS_URL=$(mb workspace url <id> --json | jq -r '.url')
94
71
  ```
95
72
 
96
73
  If you find yourself writing `--url $(mb ...)` and the receiving command rejects it with "URL must start with http://", this is what happened.
@@ -99,12 +76,10 @@ If you find yourself writing `--url $(mb ...)` and the receiving command rejects
99
76
 
100
77
  Every list/get verb supports the same output flags:
101
78
 
102
- | Flag | Effect |
103
- | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
104
- | `--json` | Emit full JSON envelope; safe for piping into `jq`. Default is human-readable text. |
105
- | `--full` | Include every field (compact projection is the default for list/get). |
106
- | `--fields a,b.c.d` | Project specific dot-paths. Mutually exclusive with `--full`. |
107
- | `--max-bytes <n>` | Cap **list** output size (drops trailing items, sets `truncated`). Default 65 536; `0` disables. Single-item commands (`get`, `metadata`) never truncate — they only emit a stderr advisory when the body is over the cap. |
79
+ - `--json` — emit the full JSON envelope, safe for `jq`. Default is human-readable text.
80
+ - `--full` include every field (compact projection is the default for list/get).
81
+ - `--fields a,b.c.d` project specific dot-paths. Mutually exclusive with `--full`.
82
+ - `--max-bytes <n>` cap **list** output size (drops trailing items, sets `truncated`). Default 65 536; `0` disables. Single-item commands (`get`, `metadata`) never truncate — they emit a stderr advisory when over the cap.
108
83
 
109
84
  List envelope shape:
110
85
 
@@ -120,9 +95,7 @@ List envelope shape:
120
95
  }
121
96
  ```
122
97
 
123
- Use `jq '.data[] | { ... }'` to slice it. The compact item projection is the agent-facing contract — for full Metabase fields, add `--full`.
124
-
125
- `total` is best-effort and may be omitted or `null` — the server returns `null` for empty/permissions-filtered collections, and `--limit` early-stop omits it because the true total is unknown. Don't depend on it being a number; use `returned` for the count you actually got back and `data.length` for the rendered slice.
98
+ The compact item projection is the agent-facing contract — add `--full` for all Metabase fields. `total` is best-effort and may be `null` (empty / permissions-filtered collections, or `--limit` early-stop); use `returned` for the count you got and `data.length` for the rendered slice.
126
99
 
127
100
  ## Body input (create / update / run)
128
101
 
@@ -130,13 +103,11 @@ Verbs that take a payload accept it from one of four sources, **first non-empty
130
103
 
131
104
  1. `--body '<inline JSON>'`
132
105
  2. `--file <path>` — JSON file
133
- 3. stdin (auto-detected when piped, or explicit with `--stdin` on commands that support it)
106
+ 3. stdin (auto-detected when piped, or explicit `--stdin` where supported)
134
107
  4. positional argument
135
108
 
136
109
  Picking exactly one is required; passing two of `--body` + `--file` + `--stdin` is rejected with a `ConfigError`.
137
110
 
138
- Common pattern:
139
-
140
111
  ```bash
141
112
  cat > /tmp/body.json <<'EOF'
142
113
  { ... }
@@ -144,432 +115,63 @@ EOF
144
115
  mb <noun> create --file /tmp/body.json --profile <n> --json
145
116
  ```
146
117
 
147
- Heredoc with single-quoted `'EOF'` prevents shell from interpolating `$vars` inside the JSON.
118
+ Single-quoted `'EOF'` prevents the shell from interpolating `$vars` inside the JSON.
148
119
 
149
120
  ## Discover the full surface: `mb __manifest`
150
121
 
151
- For the canonical, machine-readable inventory of every command — name, description, examples, every flag with type and default, and the output JSON Schema — run:
122
+ For the canonical, machine-readable inventory of every command — name, description, per-command `details`, examples, every flag with type and default, and the output JSON Schema — run:
152
123
 
153
124
  ```bash
154
125
  mb __manifest
155
126
  ```
156
127
 
157
- The leading `__` marks it as an internal command (hidden from `--help`), but it's stable: the build relies on it, and so do the in-repo tests. Reach for it instead of running `--help` per command when you need flag/output details. It pairs naturally with `jq`:
158
-
159
- ```bash
160
- # Every command name:
161
- mb __manifest | jq -r '.commands[].command'
162
-
163
- # Every verb under "transform":
164
- mb __manifest | jq -r '.commands[] | select(.command | startswith("transform")) | .command'
165
-
166
- # Flags + types for `card query`:
167
- mb __manifest | jq '.commands[] | select(.command == "card query") | .args'
168
-
169
- # Output schema for `card list` (so you know what to parse):
170
- mb __manifest | jq '.commands[] | select(.command == "card list") | .outputSchema'
171
- ```
172
-
173
- Use it to (a) enumerate verbs you don't know by heart, (b) validate flag names before constructing a command, (c) read an output schema before parsing. Cheaper and more reliable than scraping `--help` text.
174
-
175
- ## Resources at a glance
176
-
177
- The CLI exposes the Metabase REST API in 13 command groups beyond `auth` / `license`. Each follows the same shape (list/get/create/…); flags + output schemas are in `__manifest`. Only the deviations and quirks worth memorizing are below.
178
-
179
- ### `db` (alias `database`) — list and inspect databases
180
-
181
- **Default agent traversal (granular, scales to real warehouses):**
182
-
183
- ```bash
184
- mb database list --profile <n> --json # discover db ids
185
- mb database schemas <db-id> --profile <n> --json # list schema names in one db
186
- mb database schema-tables <db-id> <schema> --profile <n> --json # tables in ONE schema (compact)
187
- mb table get <table-id> --include fields --profile <n> --json # fields for ONE table (see `table` section)
188
- ```
189
-
190
- This is the path to use. A production Metabase typically has dozens of schemas, hundreds of tables, and dozens of fields per table — walking three levels and pulling one table's fields at a time keeps each response in the kilobytes. The rollup endpoints below pull megabytes and will blow the context window on any real warehouse.
191
-
192
- **Other commands:**
193
-
194
- ```bash
195
- mb database list --saved --profile <n> --json # include the Saved Questions virtual db (id -1337)
196
- mb database get <db-id> --profile <n> --json # db record only (no tables)
197
- mb database sync-schema <db-id> --profile <n> # POST /sync_schema; queues async work, returns {status:"ok"}
198
- mb database rescan-values <db-id> --profile <n> # POST /rescan_values; queues async work, returns {status:"ok"}
199
- ```
200
-
201
- **Rollup commands — only on small/dev warehouses:**
202
-
203
- ```bash
204
- mb database list --include tables --profile <n> --full --json # every db with its full table list
205
- mb database get <db-id> --include tables.fields --profile <n> --full --json # one db, every table, every field
206
- mb database metadata <db-id> --profile <n> --full --json # alias for the above, server-rolled
207
- ```
208
-
209
- Reach for these only when you know the db is small (a seeded dev instance, a sample db, a freshly-bootstrapped test fixture) or when you genuinely need every column of every table in one shot. On a real warehouse the response will exceed the agent context — use the granular traversal instead.
210
-
211
- `sync-schema` / `rescan-values` are the two manual triggers admins reach for after warehouse-side changes; both queue work and return immediately.
212
-
213
- ### `table` — list and inspect tables
214
-
215
- ```bash
216
- mb table list --db-id <db-id> --profile <n> --json # all tables in a db (compact, no fields)
217
- mb table get <table-id> --profile <n> --json # table-level metadata only
218
- mb table get <table-id> --include fields --profile <n> --json # bundles compact-projected fields ← default for field-listing
219
- mb table fields <table-id> --profile <n> --json # just the fields, as a list envelope
220
- mb table metadata <table-id> --profile <n> --json # fields + FKs + dimensions hydrated (heavier)
221
- mb table update <table-id> --body '{"display_name":"Customers"}' --profile <n> --json
222
- ```
223
-
224
- `table get` hits `/api/table/:id` and never returns fields on its own — `--full` only widens the projection over the already-fetched object. Pass `--include fields` for the field shape needed to author a card, transform, or measure; the hydrated path goes through `/api/table/:id/query_metadata`. Use `table fields` when you want just the field array (no surrounding table metadata) and `table metadata` only when you also need FKs and dimensions hydrated.
225
-
226
- `table list --db-id <db-id>` returns every table across every schema as a flat compact list. On a real warehouse with hundreds of tables this is still smaller than `database get --include tables.fields`, but `database schema-tables <db-id> <schema>` is the right starting point when you know which schema you want.
227
-
228
- `table update <id>` patches table-level metadata only — `display_name`, `description`, `caveats`, `points_of_interest`, `entity_type`, `visibility_type` (`normal`/`hidden`/`details-only`/`technical`/`cruft`), `field_order` (`alphabetical`/`custom`/`database`/`smart`), `show_in_getting_started`. Only the keys you send are touched. The underlying physical schema (the columns themselves) is not editable here — that's the warehouse's responsibility.
229
-
230
- ### `field` — inspect a single field, edit metadata, peek at distinct values
231
-
232
- ```bash
233
- mb field get <field-id> --profile <n> --full --json
234
- mb field values <field-id> --profile <n> --json # cached distinct values (FieldValues)
235
- mb field summary <field-id> --profile <n> --json # {field_id, count, distincts} — live from the warehouse
236
- mb field update <field-id> --body '{"semantic_type":"type/Email"}' --profile <n> --json
237
- mb field update <field-id> --body '{"fk_target_field_id":<other-id>}' --profile <n> --json
238
- mb field update <field-id> --body '{"description":"customer email"}' --profile <n> --json
239
- ```
240
-
241
- No `list` — fields are per-table, so use `table get <table-id> --include fields` (compact) or `table fields <table-id>` (list envelope). Never try to enumerate fields across an entire database — that's what blows up the context.
242
-
243
- `field update` patches metadata only — `display_name`, `description`, `caveats`, `points_of_interest`, `semantic_type` (Metabase type hierarchy: `type/Email`, `type/Category`, `type/PK`, `type/FK`, …), `coercion_strategy`, `fk_target_field_id` (the foreign-key target field), `visibility_type` (`normal`/`hidden`/`details-only`/`sensitive`/`retired`), `has_field_values` (`list`/`search`/`none`/`auto-list`), `settings`, `nfc_path`, `json_unfolding`. Only the keys you send are touched. `base_type` is not editable — that's the column's type as the warehouse reports it.
244
-
245
- `field values` returns the _cached_ distinct values populated by the most recent field-values scan (`mb db rescan-values <db-id>` triggers a refresh). Useful when authoring a filter and you need the closed set of categorical values. Returns `{values, field_id, has_more_values, has_field_values}` — `has_more_values: true` means the cache was truncated; consider widening the cap server-side rather than treating the snapshot as exhaustive.
246
-
247
- `field summary` returns `{field_id, count, distincts}` — cardinality straight from the warehouse, not the cache. Cheap pre-flight when deciding whether a column makes sense as a `list`-widget filter (low cardinality) or a `search` widget (high cardinality), and a quick way to spot a field that's effectively constant before you build a card around it.
248
-
249
- ### `query` — run ad-hoc MBQL with pre-flight validation
250
-
251
- ```bash
252
- mb query --print-schema --profile <n> > /tmp/mbql.json # fetch the JSON Schema
253
- mb query --file q.json --dry-run --profile <n> # validate, no network
254
- mb query --file q.json --profile <n> --json # validate + run
255
- ```
256
-
257
- The canonical agent-side path for ad-hoc MBQL. Three modes:
258
-
259
- - `--print-schema` — emits `{ schema, defs }` where `defs` carries `id.yaml` / `parameter.yaml` / `ref.yaml` / `temporal_bucketing.yaml` keyed by the path used in the schema's `$ref`s. Use this **first** when authoring a non-trivial query — it's cheaper than guess-and-fail.
260
- - `--dry-run` — validates and emits `{ ok, errors: [{path, message}] }`. Exit 0 if valid, 2 if not. No request sent.
261
- - run (no flag) — validates, then on success runs the query. On validation failure: same envelope on stdout, exit 2, **never sends** the request.
262
-
263
- MBQL 5 bodies use numeric IDs (`database: 1`, `source-table: 7`) and POST to `/api/dataset`. The bundled schema's `id.yaml` is overridden to require positive integers for every ID `$def`.
264
-
265
- Validation error envelope (same shape across `query`, `card create`, `transform create/update`):
266
-
267
- ```json
268
- { "ok": false, "errors": [{ "path": "/stages/0/aggregation/0", "message": "must be array" }] }
269
- ```
270
-
271
- `path` is a JSON Pointer into the body, `message` is the validator error string. Iterate against `--dry-run` until `ok: true`, then drop `--dry-run` to run.
272
-
273
- Exit codes: `0` valid + ran, `2` validation failed / malformed body, `1` server-side error after a valid pre-flight.
274
-
275
- **Any non-MBQL 5 body skips pre-flight automatically.** Legacy MBQL 4 (`{type:"query", database:N, query:{source-table:T, …}}`), legacy native (`{type:"native", database:N, native:{query:"…"}}`), and any other shape that doesn't carry `lib/type:"mbql/query"` are accepted by `/api/dataset` as-is and normalized server-side by `lib-be/normalize-query` (the same normalizer that backs `card create` / `transform create`, so behavior is symmetric across endpoints). The bundled schema only models MBQL 5; the CLI skips validation for the rest. Just `mb query --file probe.json` works for ad-hoc native SQL or legacy MBQL 4 probes; no `--skip-validate` needed. `--dry-run` on a non-MBQL 5 body returns `{ ok: true, errors: [] }`. The double-wrap footgun (`{type:"query", query:{lib/type:"mbql/query",…}}`) is still rejected with a `ConfigError` before send.
276
-
277
- **`--skip-validate`** is the escape hatch for MBQL 5 bodies: bypasses the pre-flight and sends the body as-is. Use only when the bundled schema disagrees with what the server actually accepts (drift, false negative). Mutually exclusive with `--dry-run`. Same flag works on `mb card create` and `mb transform create / update`.
278
-
279
- **MBQL 5 clause shape — opts always second.** Every clause is `[op, {options}, ...args]`: options object is the **second** element, not the third. Field refs are `["field", {options}, fieldId]` (id third), not the legacy MBQL 4 shape `["field", id, opts]`. The same `[op, {options}, …]` rule applies to aggregations (`["count", {options}]`, `["sum", {options}, <expr>]`), filters (`["=", {options}, <a>, <b>]`), order-by (`["asc", {options}, <expr>]`), and every other clause. Slot-1 violations surface from `--dry-run` as `must be the field options object` / `must be the clause options object` at `/stages/0/<verb>/<n>/1`.
280
-
281
- ### `uuid` — mint UUID v4 strings for `lib/uuid` slots
282
-
283
- ```bash
284
- mb uuid # one UUID, v4 from crypto.randomUUID
285
- mb uuid --count 5 # five UUIDs (one per line in TTY, JSON when piped)
286
- mb uuid --count 5 --json # ["uuid1", "uuid2", …]
287
- ```
288
-
289
- **Hard rule for agents: never generate, invent, hard-code, or reuse UUID values.** Always call `mb uuid` for fresh UUIDs at the moment you need them. Do not copy UUIDs from documentation examples, prior conversations, prior queries you authored, or anywhere else — every `lib/uuid` slot gets a freshly-minted value. The bundled schema enforces RFC 4122 format strictly, so placeholder strings (`"a1"`, `"uuid-1"`, `"agg-uuid-001"`, …) fail pre-flight with `must be a UUID v4 (RFC 4122) — run \`mb uuid\` …`. The same rule applies to native template-tag `id`fields, parameter ids, and any other`format: "uuid"` slot.
290
-
291
- Workflow when assembling an MBQL 5 body:
292
-
293
- 1. Count the `lib/uuid` slots you need (one per clause options object, plus aggregation-ref ↔ aggregation pairings — those two share the same string).
294
- 2. `mb uuid --count <N> --json` — mint exactly that many in one call.
295
- 3. Substitute each minted value into its slot as you build the JSON.
296
-
297
- Aggregation-ref pairing: the `["aggregation", {options}, "<uuid>"]` ref's third arg must equal the target aggregation's own `lib/uuid` (string equality). Mint the aggregation's `lib/uuid` once, then reuse that _same minted value_ for the ref — that's the only legitimate "reuse" pattern, and it's intra-body, not across bodies or sessions.
298
-
299
- ### `card` — questions, models, metrics
300
-
301
- ```bash
302
- mb card list --profile <n> --json
303
- mb card get <id> --profile <n> --full --json
304
- mb card query <id> --profile <n> --json --limit 50
305
- mb card query <id> --profile <n> --export-format csv > /tmp/results.csv
306
- mb card query <id> --profile <n> --export-format xlsx > /tmp/results.xlsx
307
- mb card query <id> --profile <n> --parameters '[{"type":"category","value":"A","target":["variable",["template-tag","c"]]}]'
308
- mb card create --file body.json --profile <n> --json
309
- mb card update <id> --body '{"name":"renamed"}' --profile <n> --json
310
- mb card update <id> --body '{"display":"bar"}' --profile <n> --json
311
- mb card update <id> --body '{"archived":false}' --profile <n> --json # unarchive
312
- mb card archive <id> --profile <n> # soft-delete; not undoable from the CLI
313
- ```
314
-
315
- `--export-format csv|xlsx` bypasses the JSON envelope and streams the raw export — pipe to a file. There is no permanent-delete; `archive` is the only delete verb (and `update --body '{"archived":false}'` is the unarchive path).
316
-
317
- **`card update <id>`** patches a partial subset of the create shape (`name`, `display`, `dataset_query`, `visualization_settings`, `description`, `archived`, `collection_id`, `dashboard_id`, `cache_ttl`, `parameters`, `parameter_mappings`, …). Only the keys you send are touched. If `dataset_query` is MBQL 5 (`lib/type: "mbql/query"`) it goes through the same pre-flight validation as `card create` and `mb query`; pass `--skip-validate` to bypass.
318
-
319
- **MBQL 5 `dataset_query` is a _flat_ `mbql/query`, not a legacy envelope.** This is the most common authoring mistake — the legacy MBQL4 shape `{type:"query", database:N, query:{...}}` looks similar but the server _will silently double-wrap_ an MBQL5 body submitted that way (you'll see the second-level `stages` nested inside an outer empty stage on `card get`), and queries fail with `"Initial MBQL stage must have either :source-table or :source-card"`. The right shape:
320
-
321
- ```json
322
- {
323
- "name": "Total shipments",
324
- "display": "scalar",
325
- "collection_id": 8,
326
- "dataset_query": {
327
- "lib/type": "mbql/query",
328
- "database": 2,
329
- "stages": [
330
- {
331
- "lib/type": "mbql.stage/mbql",
332
- "source-table": 190,
333
- "aggregation": [["count", { "lib/uuid": "<mint via `mb uuid`>" }]]
334
- }
335
- ]
336
- },
337
- "visualization_settings": {}
338
- }
339
- ```
340
-
341
- `dataset_query` is the mbql/query value itself — no `type:"query"` envelope, no `query:` nesting.
342
-
343
- **MBQL 5 pre-flight on `card create` / `card update`:** when `dataset_query` has `lib/type: "mbql/query"`, the body is validated against the same schema as `mb query` before sending. On failure, exit 2 with the standard `{ ok, errors }` envelope on stdout. Legacy `dataset_query` shapes (MBQL 4, native) skip pre-flight. The pre-flight also rejects the double-wrap mistake above (MBQL 5 nested inside a legacy `{type:"query", query:…}` envelope) with a `ConfigError` pointing at the right shape — no `--skip-validate` will get that past pre-flight. Author MBQL 5 by fetching the schema via `mb query --print-schema` and iterating with `mb query --dry-run`. Pass `--skip-validate` to bypass the pre-flight on schema-shape disagreements and let the server be the authority.
344
-
345
- **Visualization settings.** The valid keys for `visualization_settings` are scoped by the card's `display` value (`scalar`, `bar`, `line`, `area`, `combo`, `pie`, `table`, `pivot`, `row`, `waterfall`, `scatter`, `boxplot`, …). The CLI does not validate this object client-side — the schema lives in the **`metabase-representation-format`** skill, `spec.md` "Visualization Settings" section (graph / series / table / pivot / pie / scalar subsections, plus common `column_settings`). Load that skill if it isn't active when authoring viz keys. Common keys you'll reach for:
346
-
347
- - `bar` / `line` / `area` / `combo` / `scatter` / `waterfall` / `row` / `boxplot`: `graph.dimensions`, `graph.metrics`, `graph.show_values`, `graph.x_axis.title_text`, `graph.y_axis.title_text`, `graph.show_goal`, `graph.goal_value`, `stackable.stack_type`, plus per-series settings (`series_settings`).
348
- - `pie`: `pie.dimension`, `pie.metric`, `pie.show_total`, `pie.percent_visibility`, `pie.show_legend`.
349
- - `scalar`: `scalar.prefix`, `scalar.suffix`, `scalar.decimals`, plus `column_settings` for number formatting on the displayed column.
350
- - `table`: `table.columns` (order + visibility), `table.column_formatting` (conditional formatting), `column_settings` for per-column display.
351
- - `pivot`: `pivot_table.column_split` (rows / columns / values), `pivot.show_row_totals`, `pivot.show_column_totals`.
352
-
353
- Empty `{}` is always valid; defaults apply.
354
-
355
- ### `dashboard` — dashboards and dashcards
356
-
357
- ```bash
358
- mb dashboard list --profile <n> --json
359
- mb dashboard list --filter archived --profile <n> --json
360
- mb dashboard get <id> --profile <n> --full --json # --full hydrates dashcards + tabs
361
- mb dashboard cards <id> --profile <n> --json # list of dashcards on the dashboard
362
- mb dashboard create --file body.json --profile <n> --json
363
- mb dashboard create --body '{"name":"D","dashcards":[{"id":-1,"card_id":42,"row":0,"col":0,"size_x":12,"size_y":6}]}' --profile <n> --json
364
- mb dashboard update <id> --body '{"name":"renamed"}' --profile <n> --json
365
- mb dashboard update-dashcard <dashboard-id> <dashcard-id> --body '{"row":4,"col":2}' --profile <n> --json
366
- ```
367
-
368
- A "dashcard" is a card placement on a dashboard — its own id, position (`row`/`col`), and size (`size_x`/`size_y`). Dashcards are nested inside the parent dashboard's response; the API has no per-dashcard endpoint, so dashcard edits round-trip through `PUT /api/dashboard/:id`.
369
-
370
- A dashcard's `visualization_settings` overrides the underlying card's — same key list as the `card` section above. Dashcards can additionally set `click_behavior` for cell-level navigation; see the `metabase-representation-format` skill's "Click Behavior" subsection for that schema.
371
-
372
- **`dashboard create` accepts `dashcards` and `tabs` in the body.** The create endpoint itself only sets dashboard metadata (name, description, collection, parameters); when the body carries `dashcards` or `tabs`, the CLI chains a `PUT /api/dashboard/:id` automatically and renders the hydrated dashboard back. The compact projection includes the resulting `dashcards` and `tabs` arrays (each entry projected to id / position / size / card_id / tab_id), so the agent can confirm the placements landed without a second call. Use `--full` to also see dashboard-level metadata (width, embedding flags, parameters, …). Use a negative id (`-1`, `-2`, …) for new dashcards.
373
-
374
- **Card-reference pre-flight on `dashboard create` / `dashboard update`.** Before either command sends anything, every positive `card_id` referenced from `dashcards` is checked against `GET /api/card/:id` in parallel (de-duplicated per id). Cards that don't exist, are archived, or aren't readable fail pre-flight: the CLI writes a `{ok:false, errors:[{path, message}]}` envelope to stdout (one entry per offending dashcard, `path` = JSON pointer like `/dashcards/3/card_id`) and exits **2** with `dashboard card-reference pre-flight failed: N error(s) — fix the dashcard card_id values listed above` on stderr. No dashboard is created or modified on a pre-flight miss — this is the contract that eliminates orphan dashboards from chained creates. The pre-flight is non-bypassable: it queries live server state (no bundled schema), so there is no `--skip-validate` escape hatch. If pre-flight rejects something you believe is valid, the input is stale — `card list --json` to confirm, then re-author.
375
-
376
- **Chained-PUT failures call out the orphan risk explicitly.** If the chained `PUT /api/dashboard/:id` fails after the `POST /api/dashboard` already created the row (rare with pre-flight, but possible on permission / 5xx / network mid-flight), the user-facing error becomes `dashboard <id> created but follow-up PUT /api/dashboard/<id> failed: <reason>; dashcards not applied`. Recovery: `mb dashboard get <id>` to confirm the empty row, then either `dashboard update <id> --body '{"dashcards":[...]}'` to retry the dashcards, or `dashboard update <id> --body '{"archived":true}'` to archive the orphan. Split-into-two recipe for debugging: `dashboard create` with a metadata-only body, then `dashboard update <id>` with the `dashcards` array — isolates which leg of the chain is at fault.
377
-
378
- Two ways to edit dashcards:
379
-
380
- - **`dashboard update <id> --body { "dashcards": [...] }`** — replaces the entire dashcard set. IDs in the array are kept (and updated to the values you send); IDs **absent** are deleted server-side. Use a negative id (`-1`, `-2`, …) for cards the server should create. You must include every existing dashcard you want to preserve.
381
- - **`dashboard update-dashcard <dashboard-id> <dashcard-id>`** — patches a single dashcard's layout / settings without touching the others. Internally: GET dashboard → merge patch into the targeted dashcard → PUT the whole array. Safer than hand-rolling the full-array variant if you only meant to nudge one card.
382
-
383
- `dashboard list` is a thin filter helper (`--filter all|mine|archived`; default `all`). The list endpoint omits `dashcards` / `tabs`; `dashboard get <id>` includes them as compact projections, and `dashboard get <id> --full` (or `dashboard cards <id>`) gives the full hydrated form.
384
-
385
- Patch fields supported by `update-dashcard`:
386
-
387
- | Field | Type |
388
- | ------------------------ | ---------------------------------- |
389
- | `row`, `col` | non-negative integer |
390
- | `size_x`, `size_y` | positive integer |
391
- | `dashboard_tab_id` | integer or `null` |
392
- | `parameter_mappings` | array of parameter-mapping objects |
393
- | `inline_parameters` | array of strings |
394
- | `visualization_settings` | object |
395
-
396
- Empty-object patches are rejected client-side before any network call.
397
-
398
- ### `snippet` — native query snippets (reusable SQL fragments)
399
-
400
- ```bash
401
- mb snippet list --profile <n> --json
402
- mb snippet list --archived --profile <n> --json # → ONLY archived (mutually exclusive with active)
403
- mb snippet get <id> --profile <n> --full --json
404
- mb snippet create --body '{"name":"active","content":"WHERE active = true"}' --profile <n> --json
405
- mb snippet update <id> --body '{"name":"renamed"}' --profile <n> --json
406
- mb snippet update <id> --body '{"archived":false}' --profile <n> --json # unarchive
407
- mb snippet archive <id> --profile <n> # soft-delete
408
- ```
409
-
410
- Hits `/api/native-query-snippet`. A snippet is a named, reusable piece of native (SQL) query text — referenced from cards via `{{snippet: Name}}`. **`--archived` is a swap, not a union**: list returns either active (default) or archived rows, never both. Compact projection: `id`, `name`, `description`, `archived`, `collection_id`. Create body required fields: `name`, `content`. Update body is partial — `name`, `content`, `description`, `archived`, `collection_id`.
411
-
412
- ### `segment` — saved MBQL filter macros
413
-
414
- ```bash
415
- mb segment list --profile <n> --json
416
- mb segment get <id> --profile <n> --full --json
417
- mb segment create --file segment.json --profile <n> --json
418
- mb segment update <id> --body '{"name":"renamed","revision_message":"rename"}' --profile <n> --json
419
- mb segment archive <id> --profile <n> # default audit message
420
- mb segment archive <id> --revision-message "deprecated" --profile <n> # custom audit message
421
- ```
422
-
423
- Hits `/api/segment`. A segment is a saved MBQL filter macro tied to a table — used in card filters to share a reusable predicate. Create body required: `name`, `table_id`, `definition` (MBQL filter object), optional `description`. **Update bodies MUST include `revision_message`** (a non-blank string captured in the audit log); the CLI does not synthesize it. The `archive` verb hardcodes `"Archived via mb CLI"` by default — override with `--revision-message`.
424
-
425
- Compact projection: `id`, `name`, `description`, `archived`, `table_id`. The list response is bare; only the get/list responses hydrate `creator` and (list-only) `definition_description`.
426
-
427
- ### `measure` — saved MBQL aggregation macros
428
-
429
- ```bash
430
- mb measure list --profile <n> --json
431
- mb measure get <id> --profile <n> --full --json
432
- mb measure create --file measure.json --profile <n> --json
433
- mb measure update <id> --body '{"name":"renamed","revision_message":"rename"}' --profile <n> --json
434
- mb measure archive <id> --profile <n>
435
- mb measure archive <id> --revision-message "deprecated" --profile <n>
436
- ```
437
-
438
- Hits `/api/measure`. A measure is a saved MBQL aggregation (a single `:aggregation` clause) tied to a table — referenced from cards and metrics to share a reusable computation. Create body required: `name`, `table_id`, `definition` (MBQL aggregation object), optional `description`. Same `revision_message` requirement on update / archive as `segment`.
439
-
440
- Compact projection: `id`, `name`, `description`, `archived`, `table_id`. The full response on `get` adds `dimensions`, `dimension_mappings`, `result_column_name`; the list response adds `definition_description` instead.
441
-
442
- ### `collection` — folder hierarchy for cards, dashboards, sub-collections
443
-
444
- ```bash
445
- mb collection list --profile <n> --json
446
- mb collection list --filter archived --profile <n> --json # → just the trash collection
447
- mb collection list --filter personal --profile <n> --json # → only personal collections
448
- mb collection get <ref> --profile <n> --json --full
449
- mb collection items <ref> --profile <n> --json
450
- mb collection items <ref> --models card,dashboard --pinned-state is_pinned --profile <n> --json
451
- mb collection tree --profile <n> # → JSON only, recursive
452
- mb collection create --body '{"name":"My Collection","parent_id":4}' --profile <n> --json
453
- ```
454
-
455
- `<ref>` (the positional id on `get` and `items`) accepts **four** forms — anything else is rejected client-side with a `ConfigError` before any HTTP call:
456
-
457
- | Form | Example | Notes |
458
- | ----------------- | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
459
- | Positive integer | `4` | Database id of the collection. |
460
- | `root` | `mb collection get root` | The virtual "Our analytics" root. Returns a stripped-down shape — `archived`, `description`, `location`, `type`, etc. are _absent_, not `null`. |
461
- | `trash` | `mb collection get trash` | The trash collection — paradoxically returns `archived: false`, `type: "trash"`. Filter via `list --filter archived` to enumerate it. |
462
- | 21-char entity_id | `voo1If9y8Sld0lXej6xl0` | NanoID form (regex `^[A-Za-z0-9_-]{21}$`). Works wherever an int does — Metabase resolves it server-side via the same route. |
463
-
464
- **`collection items` is auto-paginated.** The CLI drains all pages of `/api/collection/:id/items` by default; pass `--limit <n>` to cap the total returned. With `--limit` set, the result envelope omits `total` (true total is unknown after early-stop). Items at the root level (`collection items root`) carry `collection_id: null`.
465
-
466
- **`collection tree` is JSON-only.** The recursive `{id, name, location, here, children, …}` structure does not render meaningfully as a key/value table; passing `--format text` is rejected with `ConfigError` so the user gets a clear signal rather than silent JSON.
467
-
468
- **Compact projection** (default for `list` / `get`): `id`, `name`, `description`, `archived`, `location`, `parent_id`, `type`, `authority_level`, `is_personal`. Use `--full` for hydrated fields like `slug`, `entity_id`, `can_write`, `namespace`, `personal_owner_id`. The compact projection on items is even tighter: `id`, `model`, `name`, `description`, `archived`, `collection_id`.
469
-
470
- **`collection create` body** accepts the same fields as `POST /api/collection`: `name` (required, non-empty), `description`, `parent_id` (omit or `null` for the root), `namespace`, `authority_level`. Note: the create response does _not_ hydrate `parent_id` (only `location` reflects the parent path); use `collection get <id>` if you need `parent_id` populated.
471
-
472
- For dashboard / card / collection enumeration, prefer the dedicated `collection list` / `dashboard list` / `card list` verbs over `mb search --models collection` — search is for ranking against a query string or cross-resource lookup, not bulk enumeration.
473
-
474
- ### `transform` and `transform-job`
475
-
476
- ```bash
477
- mb transform list --profile <n> --json
478
- mb transform run <id> --wait --profile <n> --json
479
- mb transform runs --transform-id <id> --profile <n> --json # recent runs, optionally filtered
480
- mb transform get-run <run-id> --profile <n> --json # single run by RUN id (not transform id)
481
- mb transform cancel <id> --profile <n> --json # cancel the in-flight run for a transform
482
- mb transform-job list --profile <n> --json
483
- ```
484
-
485
- **MBQL 5 pre-flight on `transform create` / `update`:** when `source.query` has `lib/type: "mbql/query"`, it's validated against the same schema as `mb query` before sending; failures exit 2 with the standard `{ ok, errors }` envelope on stdout. Legacy `source.query` shapes and Python sources skip pre-flight. Pass `--skip-validate` to bypass.
486
-
487
- **Iterate via `transform update`, not re-`create`.** When a `transform run` fails and you want to retry with a fixed body, patch the existing transform with `transform update <id> --file new-body.json` rather than `transform delete <id>` + `transform create`. Update keeps the same row, `entity_id`, materialized table, and on-disk YAML filename — `git-sync export` produces one clean commit, and you avoid the `_2` suffix the YAML serializer mints when two same-named transforms exist on disk. See the `transform` skill, "Iterating on a failing transform".
488
-
489
- For the body shape, run-with-wait pattern, schedule authoring, and inspection load the `transform` skill via `mb skills get transform`.
490
-
491
- ### `setting` (alias `settings`) — admin settings
128
+ The leading `__` hides it from `--help`, but it's stable. Reach for it instead of `--help` per command. It pairs with `jq`:
492
129
 
493
130
  ```bash
494
- mb setting list --profile <n> --json # admin-only
495
- mb setting get <key> --profile <n> --json
496
- mb setting set <key> --body '"<string-value>"' --profile <n> # value parsed as STRICT JSON
131
+ mb __manifest | jq -r '.commands[].command' # every command name
132
+ mb __manifest | jq -r '.commands[] | select(.command | startswith("transform")) | .command' # verbs under "transform"
133
+ mb __manifest | jq '.commands[] | select(.command == "card query") | .args' # flags + types for a command
134
+ mb __manifest | jq '.commands[] | select(.command == "card list") | .outputSchema' # output schema before parsing
497
135
  ```
498
136
 
499
- The value is parsed as strict JSON: a string setting is `'"value"'` (note the inner double quotes), not `value`. Booleans are `true` / `false`, numbers bare. Wrong quoting silently produces a parse error confirm with `setting get <key>` after.
137
+ Use it to (a) enumerate verbs, (b) validate flag names before constructing a command, (c) read an output schema before parsing.
500
138
 
501
- **`setting get --json` works on every value type.** String-valued settings (e.g., `remote-sync-branch=agent/shipments-analysis`, `remote-sync-url=file:///mnt/repo`) come back from `/api/setting/<key>` as bare text rather than a JSON-quoted string; the CLI sniffs the response Content-Type and wraps bare text into the `{key, value}` envelope so `--json` is uniform. The same fix applies to `git-sync status --json` (which reads `remote-sync-branch` internally).
139
+ ## Resource quirks worth memorizing
502
140
 
503
- ### `search` — content search across types
141
+ Routine verb shapes (list / get / create / update), every flag, and output JSON Schemas live in `mb __manifest` — pull them on demand. Below is only what the manifest does _not_ tell you: the footguns and non-obvious behaviors.
504
142
 
505
- ```bash
506
- mb search "orders" --profile <n> --json
507
- mb search "orders" --models card,dashboard --limit 10 --profile <n> --json
508
- mb search "drafts" --archived --verified --profile <n> --json
509
- mb search "orders" --table-db-id <db-id> --profile <n> --json
510
- ```
511
-
512
- `--models` filters: `card,dataset,metric,dashboard,collection,database,table,segment,measure,snippet,document,action,transform,indexed-entity`. For plain enumeration / inspection of cards, dashboards, or collections, prefer the dedicated `card list` / `dashboard list` / `collection list` verbs above; reach for `search --models <kind>` only when you need ranking against a query string or a cross-resource lookup.
513
-
514
- ### `git-sync` content sync (representations instance)
515
-
516
- ```bash
517
- mb git-sync status --profile <n> --json
518
- mb git-sync import --branch <branch> --profile <n> # --wait is the default
519
- mb git-sync export -m "commit message" --profile <n>
520
- mb git-sync branches --profile <n> --json
521
- ```
522
-
523
- 14 verbs (status / is-dirty / has-remote-changes / dirty / current-task / cancel-task / wait / import / export / stash / branches / create-branch / add-collection / remove-collection). Both `import --force` and `export --force` are **lossy** — confirm with the user before either. `add-collection <id>` / `remove-collection <id>` toggle a collection's `is_remote_synced` and cascade to descendants by location prefix; the server rejects them in the default read-only mode (`mb setting set remote-sync-type '"read-write"'` first). For the dirty-check workflow, stash semantics, and the full collection-toggle prerequisites, load the `git-sync` skill via `mb skills get git-sync`.
524
-
525
- ### `workspace` — Enterprise workspaces (parent-side + local child)
526
-
527
- Lifecycle, provisioning, child-credential extraction, diagnose. Load the `workspace` skill via `mb skills get workspace` — it's the densest reference and assumes the conventions above.
528
-
529
- ### `api-key` — create API keys
530
-
531
- ```bash
532
- mb api-key create --body '{"name":"agent-demo","group_id":<id>}' --profile <n> --json
533
- ```
534
-
535
- Admin-only. The response includes the unmasked key once — capture it; the API never reveals it again.
536
-
537
- ### `eid translate` — string EID → numeric id
538
-
539
- ```bash
540
- mb eid translate <eid> --profile <n> --json
541
- ```
542
-
543
- Useful when an external system gives you a string entity id (like `Nd3A2qlmFIOYa5UZpQdsL`) and you need the numeric id for `card query`, `transform run`, etc.
544
-
545
- ### `setup` — initial setup wizard
546
-
547
- ```bash
548
- mb setup --file /path/to/setup-spec.json
549
- ```
550
-
551
- Walks the `/api/setup` endpoint with a default user. **Don't run this against an instance the user already set up** — it errors out, and even successful runs are one-shot. Mostly useful for bootstrapping a fresh local instance (e2e harnesses).
143
+ - **db traversal vs. rollup.** Default to granular: `database list` → `database schemas <db-id>` → `database schema-tables <db-id> <schema>` → `table get <table-id> --include fields`. The rollup endpoints (`database get --include tables.fields`, `database metadata <db-id>`) pull megabytes and blow the context window on any real warehouse — use them only on a small/dev db. `sync-schema` / `rescan-values` queue async work and return `{status:"ok"}` immediately.
144
+ - **table fields.** `table get` never returns fields on its own — pass `--include fields` (compact) or use `table fields <id>` (list envelope). `table metadata <id>` adds FKs + dimensions (heavier). `table update` patches table-level metadata only; physical columns aren't editable here.
145
+ - **field has no `list`.** Fields are per-table — get them via `table get <id> --include fields`. Never enumerate fields across a whole db (context blow-up). `field summary` is live cardinality `{field_id, count, distincts}`; `field values` is the cached distinct set (`has_more_values: true` ⇒ truncated cache). `field update` patches metadata only; `base_type` isn't editable.
146
+ - **card.** `dataset_query` is the **flat** `mbql/query` value, not a legacy `{type:"query",query:…}` envelope (→ `mbql` skill). `--export-format csv|xlsx` streams the raw export (pipe to a file), bypassing the JSON envelope. `archive` is the only delete; unarchive with `update --body '{"archived":false}'`. `visualization_settings` keys are scoped by `display` and aren't pre-flighted — see the `viz` skill.
147
+ - **dashboard.** Dashcards round-trip through `PUT /api/dashboard/:id` (no per-dashcard endpoint): `update-dashcard <dash-id> <dashcard-id>` patches one safely; `update --body '{"dashcards":[…]}'` replaces the whole set (omitted ids are deleted server-side; use negative ids for new cards). `create`/`update` pre-flight every positive `card_id` against live server state and exit **2** with `{ok:false,errors:[…]}` on a bad ref — non-bypassable (no `--skip-validate`). `dashboard get <id>` (or `--full`) hydrates dashcards/tabs; `list` omits them.
148
+ - **snippet `--archived` is a swap, not a union** — list returns _either_ active _or_ archived rows, never both. (Same shape for `--filter archived` on dashboard/collection.)
149
+ - **segment / measure** `update` and `archive` require a non-blank `revision_message` (audit-logged); the CLI does not synthesize it on `update`. `archive` defaults to `"Archived via mb CLI"` — override with `--revision-message`. `definition` is a flat MBQL clause (→ `mbql` skill): segment = a filter, measure = exactly one aggregation.
150
+ - **collection `<ref>`** accepts four forms only positive int, `root`, `trash`, or a 21-char entity_id anything else is a client-side `ConfigError`. `collection items` auto-paginates (cap with `--limit`, which then omits `total`). `collection tree` is **JSON-only** `--format text` is rejected.
151
+ - **setting set** parses the value as **strict JSON**: a string is `'"value"'` (inner quotes), booleans `true`/`false`, numbers bare. Wrong quoting silently errors — confirm with `setting get <key>` after. `setting get --json` works on every value type (it wraps bare-text responses into `{key, value}`).
152
+ - **search vs. list.** For plain enumeration of cards/dashboards/collections use the dedicated `… list` verbs; reach for `search --models <kind>` only for ranking against a query string or a cross-resource lookup.
153
+ - **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 you get only `{run_id, final:null}`. (→ `transform` skill.)
154
+ - **setup is one-shot.** `mb setup` walks `/api/setup` for a **fresh** instance only — it errors against an already-configured one. Mostly for bootstrapping local / e2e instances.
155
+ - **eid** translates a string entity id → numeric id: `mb eid --model <model> <eid1,eid2> --json` (EIDs are a positional used with `--model`; or pass `--body '{"entity_ids":{"card":["…"]}}'`). Useful when an external system hands you an entity id and a verb needs the numeric one.
156
+ - **query / uuid.** `mb query` is the ad-hoc MBQL surface (`--print-schema` → `--dry-run` → run); `mb uuid --count <n>` mints the `lib/uuid` values every MBQL 5 clause needs. Both workflows live in the `mbql` skill.
552
157
 
553
158
  ## Specialized skills (load on demand)
554
159
 
555
- This core file is enough for any single-command task. Specialized flows live in sibling skills, served by the same CLI. **Load the relevant skill proactively when the user's intent matches** — don't wing the workspace lifecycle, transform body, or git-sync workflow from this overview alone.
556
-
557
- | Load this skill | When the user's intent matches |
558
- | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
559
- | `mb skills get workspace` | "spin up a workspace", "provision", "start a local Metabase against my prod", anything `mb workspace …`. **Mandatory** before running `workspace start` — it tells you to ask the user about Remote Sync (current dir / custom path / none) up front, since the bind mount can only be set at container create. |
560
- | `mb skills get transform` | "create a transform", "run a transform", authoring transform body JSON, run inspection |
561
- | `mb skills get git-sync` | "import the latest changes", "export to git", "git sync", "dirty check", "stash before pulling" |
160
+ This core file is enough for any single-command task. Load the relevant skill **proactively** when intent matches — don't wing an MBQL body, the workspace lifecycle, a transform body, or the git-sync workflow from this overview alone. Load each via `mb skills get <name>`.
562
161
 
563
- If a task spans more than one (e.g., "spin up `my_ws`, sync transforms from `main`, run them"), load each. Specialized skills assume you've internalized the general flag conventions above and won't repeat them.
162
+ - **`mbql`** authoring or fixing any MBQL query body: `mb query`, a card `dataset_query`, a transform `source.query`, a measure/segment `definition`, "aggregate and group by", reading `--dry-run` errors. The query-body reference.
163
+ - **`viz`** — choosing a card's `display` and authoring `visualization_settings`: "make it a bar chart", "set the pie dimension/metric", "format this column as currency", "the card renders as a table instead of a chart". The presentation counterpart to `mbql`.
164
+ - **`workspace`** — "spin up a workspace", "provision", "start a local Metabase against my prod", anything `mb workspace …`. **Mandatory** before `workspace start` — ask the user about Remote Sync up front (the bind mount is create-time only).
165
+ - **`transform`** — "create a transform", "run a transform", authoring transform body JSON, run inspection.
166
+ - **`git-sync`** — "import the latest changes", "export to git", "git sync", "dirty check", "stash before pulling".
564
167
 
565
- `mb skills list` enumerates everything available on the installed CLI version.
168
+ If a task spans more than one, load each. Specialized skills assume the conventions above and won't repeat them. `mb skills list` enumerates everything on the installed version.
566
169
 
567
170
  ## Don't
568
171
 
569
- - **Don't run `mb auth login` for the user.** Authentication is theirs ask them to log in and tell you the profile name. The only legitimate exception is saving a freshly created workspace child's credentials (see the `workspace` skill); even there, pipe the key on stdin.
570
- - Don't paste credentials, license tokens, or warehouse passwords in chat. Have the user run the storing command themselves.
571
- - Don't put `--profile` before the verb chain — the CLI parses it as a top-level subcommand and errors out.
572
- - Don't pass an API key with `--api-key "$KEY"`; pipe it on stdin via `--api-key-stdin`. (Comes up only in the workspace-child case.)
172
+ - **Don't run `mb auth login` for the user** authentication is theirs (see §Auth). The only exception is saving a workspace child's credentials, and even there pipe the key on stdin.
173
+ - Don't paste credentials, license tokens, or warehouse passwords in chat. Have the user run the storing command.
174
+ - Don't put `--profile` before the verb chain — the CLI parses it as a subcommand and errors out.
573
175
  - Don't omit `--wait` on `workspace start` / `transform run` / `workspace database provision` for interactive flows; the next step will race the operation.
574
176
  - Don't drop a JSON-envelope verb's output raw into another flag. Extract with `--json | jq -r '.<field>'`.
575
- - Don't add a third-party HTTP library or shell into `curl` workflows when a `mb <verb>` exists — the CLI is the supported path; `curl` against `/api/...` bypasses retries, schema validation, and credential redaction.
177
+ - Don't add a third-party HTTP library or shell into `curl` against `/api/...` when a `mb <verb>` exists — that bypasses retries, schema validation, and credential redaction.