@metabase/cli 0.1.7 → 0.1.8-alpha.drop-workspaces.a42f32e

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 (204) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/README.md +2 -224
  3. package/dist/{add-collection-DQjTlDNF.mjs → add-collection-CuLyFlrE.mjs} +5 -5
  4. package/dist/add-collection-DkucDB2N.mjs +10 -0
  5. package/dist/{archive-BXzghEQX.mjs → archive-C1lk1rR7.mjs} +6 -7
  6. package/dist/{archive-CBGKzEAl.mjs → archive-C2AfP9G_.mjs} +5 -6
  7. package/dist/{archive-DTN9tLGT.mjs → archive-COAWrOVp.mjs} +5 -6
  8. package/dist/{archive-De8jzzq7.mjs → archive-DNVqPMhp.mjs} +6 -7
  9. package/dist/{archive-CuVk8iwN.mjs → archive-Dt1qDaIF.mjs} +6 -7
  10. package/dist/{archive-B3qiL-kK.mjs → archive-mdSqCzVw.mjs} +5 -6
  11. package/dist/auth-WILFa6Xf.mjs +19 -0
  12. package/dist/{body-tcURGnGh.mjs → body-b76dLVpL.mjs} +2 -2
  13. package/dist/{branches-CIGkjXIk.mjs → branches-DwV1q-4h.mjs} +5 -6
  14. package/dist/{cancel-pPsvgJ0Z.mjs → cancel-DDKstWQM.mjs} +4 -5
  15. package/dist/{cancel-task-BLGE4UlL.mjs → cancel-task-DX_OdGan.mjs} +5 -6
  16. package/dist/{render-0_GsapXa.mjs → capabilities-Dj3NgPHg.mjs} +28 -2
  17. package/dist/card-Bd8jLmZW.mjs +20 -0
  18. package/dist/{card-ezYiriML.mjs → card-BtPiKjbB.mjs} +1 -1
  19. package/dist/{cards-Dq3nx_9n.mjs → cards-CYKPNOp3.mjs} +5 -6
  20. package/dist/cli.mjs +23 -24
  21. package/dist/collection-gz4k8jWl.mjs +20 -0
  22. package/dist/{create-BrUqxreg.mjs → create-B4aKAWaQ.mjs} +7 -8
  23. package/dist/{create-9DBTkbMq.mjs → create-BfRIHlHk.mjs} +11 -12
  24. package/dist/{create-aPaUEGdr.mjs → create-BfseKO3F.mjs} +7 -8
  25. package/dist/{create-w3mQg9n4.mjs → create-BqiR1kvi.mjs} +7 -8
  26. package/dist/{create-BcgoukG4.mjs → create-BscN4jFB.mjs} +9 -10
  27. package/dist/{create-BdPoSk_7.mjs → create-CE1WZ4du.mjs} +9 -10
  28. package/dist/{create-DHscDhRd.mjs → create-CQxJpVXk.mjs} +9 -10
  29. package/dist/{create-BIphz0kO.mjs → create-DRhRPfD-.mjs} +10 -11
  30. package/dist/{create-branch-DGoc9CUU.mjs → create-branch-2yEa7-9x.mjs} +5 -6
  31. package/dist/{current-task-DZM28rnr.mjs → current-task-5buIatuG.mjs} +5 -6
  32. package/dist/dashboard-BL8_Ysst.mjs +21 -0
  33. package/dist/{database-BXiue1in.mjs → database-_Td7rG4o.mjs} +1 -1
  34. package/dist/db-Bw8m4M2J.mjs +22 -0
  35. package/dist/{delete-BrJOotpW.mjs → delete-C3i5dE4s.mjs} +6 -7
  36. package/dist/{delete-BPaFdHZP.mjs → delete-CF8in6gw.mjs} +6 -7
  37. package/dist/{delete-runtime-uuYbd4k2.mjs → delete-runtime-Bo7v-0au.mjs} +2 -2
  38. package/dist/{delete-table-CNupWUO0.mjs → delete-table-m-6cOPnX.mjs} +6 -7
  39. package/dist/{dirty-BCkNOY8c.mjs → dirty-hGWiemB-.mjs} +5 -6
  40. package/dist/{eid-CLY5X0Uw.mjs → eid-BP0H-LTl.mjs} +6 -7
  41. package/dist/{error-ZsFeevV2.mjs → error-BLukG0Vf.mjs} +1 -1
  42. package/dist/{export-CgHgWW3I.mjs → export-wh9l0s7F.mjs} +7 -8
  43. package/dist/field-GgVH-HUD.mjs +18 -0
  44. package/dist/{fields-RkRWU-u9.mjs → fields-BD52yx5k.mjs} +6 -7
  45. package/dist/{get-CTDqioaj.mjs → get-436DayeX.mjs} +5 -6
  46. package/dist/{get-B8l4t4Pz.mjs → get-B1jyFBkg.mjs} +5 -6
  47. package/dist/{get-42tJ7BNp.mjs → get-BLUldOxi.mjs} +5 -6
  48. package/dist/{get-sMpa-X4E.mjs → get-BVMyLnhh.mjs} +6 -7
  49. package/dist/{get-B9kwSs6U.mjs → get-BhFiIyyH.mjs} +5 -6
  50. package/dist/{get-CRvbChoX.mjs → get-C5PKCCzj.mjs} +5 -6
  51. package/dist/{get-y17zJMnU.mjs → get-C_LAPJ7J.mjs} +5 -6
  52. package/dist/{get-CiZrZJLt.mjs → get-CgdzDOTX.mjs} +7 -8
  53. package/dist/{get-DmzgSgrl.mjs → get-Cu7Om0g2.mjs} +6 -7
  54. package/dist/{get-DsqGHNHN.mjs → get-DETcYP1F.mjs} +5 -6
  55. package/dist/{get-Bo4Cpd_c.mjs → get-DSmnRBrw.mjs} +5 -7
  56. package/dist/{get-CvmqPN30.mjs → get-EJOWmpkC.mjs} +5 -6
  57. package/dist/{get-run-CBwcRc8E.mjs → get-run-BEvwTmr7.mjs} +5 -6
  58. package/dist/{get-C9O_aEGo.mjs → get-z9XIWbVG.mjs} +5 -6
  59. package/dist/git-sync-oYYp_Z6v.mjs +28 -0
  60. package/dist/{has-remote-changes-CfRidwXT.mjs → has-remote-changes-BNjTil7k.mjs} +5 -6
  61. package/dist/{import-BZV0Z2KR.mjs → import-C0iBM0UR.mjs} +7 -8
  62. package/dist/is-dirty-C_RyVkc1.mjs +9 -0
  63. package/dist/{is-dirty-hKcB4OH9.mjs → is-dirty-D_e5Xoke.mjs} +4 -4
  64. package/dist/{items-C94eW2Yd.mjs → items-D6h0ejme.mjs} +7 -8
  65. package/dist/{list-DfDZr55C.mjs → list-B3CgwSU4.mjs} +6 -7
  66. package/dist/{list-BS_Bxejg.mjs → list-BJxm0-6m.mjs} +6 -7
  67. package/dist/{list-DuSoEk_J.mjs → list-BZkOPBmK.mjs} +6 -7
  68. package/dist/{list-BJXaGk-z.mjs → list-CSX_xieo.mjs} +4 -5
  69. package/dist/{list-C-oZe1_p.mjs → list-CWItVq2v.mjs} +5 -6
  70. package/dist/{list-CU6sOfI-.mjs → list-CkCgkJFh.mjs} +5 -6
  71. package/dist/{list-BmHoYJr7.mjs → list-CmVbbX3f.mjs} +4 -5
  72. package/dist/{list-DrINpVLM.mjs → list-D0ZVA1e1.mjs} +4 -5
  73. package/dist/{list-HS15y_WN.mjs → list-DLC82gFo.mjs} +4 -5
  74. package/dist/{list-BFlzLGlw.mjs → list-DcV2bfg8.mjs} +4 -5
  75. package/dist/{list-B0V7FeL2.mjs → list-ZX3ooQ9h.mjs} +4 -6
  76. package/dist/{list-CF1pMN4S.mjs → list-pXgd_HTy.mjs} +4 -5
  77. package/dist/{list-CqN4gvCk.mjs → list-r3gm10tz.mjs} +5 -6
  78. package/dist/{login-enh9Yimb.mjs → login-DSS8QywX.mjs} +9 -10
  79. package/dist/{logout-BWLPLDh8.mjs → logout-B6acbVng.mjs} +4 -5
  80. package/dist/{manifest-BNh0Lw6p.mjs → manifest-B_OfG-Ss.mjs} +1 -2
  81. package/dist/measure-DDn0jAB8.mjs +19 -0
  82. package/dist/{metadata-D2TxboMm.mjs → metadata-C-bcafH-.mjs} +6 -7
  83. package/dist/{metadata-BTTEBWdS.mjs → metadata-D_wFDaCb.mjs} +7 -8
  84. package/dist/{parse-id-0_tOPvfI.mjs → parse-id-BapRoz4R.mjs} +1 -1
  85. package/dist/{path-C8GrBdgT.mjs → path-DmYXSF59.mjs} +4 -6
  86. package/dist/{poll-4eoh5J0r.mjs → poll-CYFFYOiB.mjs} +1 -1
  87. package/dist/{poll-task-51WRdugU.mjs → poll-task-Dalt24ov.mjs} +2 -2
  88. package/dist/{preflight-BhsErYz3.mjs → preflight-G7NUAKod.mjs} +3 -3
  89. package/dist/{query-BBCAF-tG.mjs → query-COWTai5j.mjs} +11 -12
  90. package/dist/{query-DYVBnu9d.mjs → query-DRosi9Ha.mjs} +7 -8
  91. package/dist/{query-result-ABPLz6I4.mjs → query-result-PoQE05Pe.mjs} +1 -1
  92. package/dist/{remove-collection-CBAHz0Dk.mjs → remove-collection-CLWw-48s.mjs} +7 -8
  93. package/dist/{rescan-values-cfTSNQZo.mjs → rescan-values-C3L03Crf.mjs} +7 -8
  94. package/dist/{run-qgdEJv-I.mjs → run-PL8TRDGx.mjs} +7 -8
  95. package/dist/{runs-BFIIH4GL.mjs → runs-B86YP36a.mjs} +6 -7
  96. package/dist/{runtime-Duawf5lE.mjs → runtime-C2EDX9E7.mjs} +10 -77
  97. package/dist/{schema-tables-C2xM3dho.mjs → schema-tables-CZr0QT1g.mjs} +6 -7
  98. package/dist/{schemas-BP7xiktH.mjs → schemas-DnGJlmQl.mjs} +4 -5
  99. package/dist/{search-DYP3lOlq.mjs → search-thFe-n3F.mjs} +4 -5
  100. package/dist/segment-ydERbF0L.mjs +19 -0
  101. package/dist/{set-DpRQqdo7.mjs → set-CDQE4s8H.mjs} +7 -8
  102. package/dist/{setting-DUa96KF3.mjs → setting-BTMmbmKB.mjs} +3 -3
  103. package/dist/{setup-BPlllnim.mjs → setup-mOHveOFI.mjs} +6 -7
  104. package/dist/{skills-BkregMyb.mjs → skills-BDPVoBWU.mjs} +33 -2
  105. package/dist/{skills-SqbPo0BI.mjs → skills-d2iLclsk.mjs} +3 -3
  106. package/dist/snippet-i1Z8-yJQ.mjs +19 -0
  107. package/dist/{stash-C89zNKxo.mjs → stash-B4RTStPo.mjs} +7 -8
  108. package/dist/{status-B1EJ_jv0.mjs → status-B7H0JH52.mjs} +6 -7
  109. package/dist/{status-BNvFPemM.mjs → status-DoVvjifV.mjs} +4 -5
  110. package/dist/{summary-Cihbx0Qs.mjs → summary-BsdiF7LZ.mjs} +5 -6
  111. package/dist/{sync-schema-C3odu0ZH.mjs → sync-schema-CvXkT9Rg.mjs} +7 -8
  112. package/dist/{table-qDD2kApF.mjs → table-CkWja2UH.mjs} +1 -1
  113. package/dist/table-DoAu2iwB.mjs +19 -0
  114. package/dist/transform-JzFSQdFi.mjs +24 -0
  115. package/dist/transform-job-C8q3Fcf7.mjs +19 -0
  116. package/dist/{tree-mvq9gM9w.mjs → tree-CGBtDXfk.mjs} +4 -5
  117. package/dist/{update-CbBnHz42.mjs → update-B89kI8l9.mjs} +10 -11
  118. package/dist/{update-CtOo3LsX.mjs → update-BA48uHRd.mjs} +9 -10
  119. package/dist/{update-DCrOQ1PW.mjs → update-BHit5RyS.mjs} +11 -12
  120. package/dist/{update-BoIiuC70.mjs → update-BJhk_A4R.mjs} +10 -11
  121. package/dist/{update-tRparnUs.mjs → update-BJn9ZitD.mjs} +12 -13
  122. package/dist/{update-C0jP0AKT.mjs → update-C44bumhM.mjs} +8 -9
  123. package/dist/{update-Rr4usmCo.mjs → update-DqAKFmkw.mjs} +8 -9
  124. package/dist/{update-VvKMnwsM.mjs → update-JpO3b8g3.mjs} +10 -11
  125. package/dist/{update-DwRxdflw.mjs → update-WMzbyUw2.mjs} +8 -9
  126. package/dist/{update-dashcard-DFvIz8Qj.mjs → update-dashcard-C8BGZtY7.mjs} +8 -9
  127. package/dist/{upgrade-D-Rl_fH9.mjs → upgrade-DeiR3fjf.mjs} +34 -7
  128. package/dist/{uuid-BSVUk8u2.mjs → uuid-BWvbC2Z8.mjs} +3 -4
  129. package/dist/{validate-dPEOnOf8.mjs → validate-CqVB_3_p.mjs} +1 -1
  130. package/dist/{validate-query-CYvOP8Ld.mjs → validate-query-BUTNK00f.mjs} +2 -2
  131. package/dist/{values-D1RJE4H6.mjs → values-07BZxAK5.mjs} +5 -6
  132. package/dist/{verify-A7BWfBPZ.mjs → verify-D2q5RI4i.mjs} +1 -1
  133. package/dist/{wait-DK5QDZ8n.mjs → wait-DWyuBO_X.mjs} +6 -7
  134. package/dist/{wait-flags-DlfbIXHw.mjs → wait-flags-Crs-j7Z8.mjs} +2 -2
  135. package/package.json +1 -1
  136. package/skill-data/core/SKILL.md +13 -26
  137. package/skill-data/git-sync/SKILL.md +8 -63
  138. package/skill-data/transform/SKILL.md +3 -4
  139. package/skill-data/visualization/SKILL.md +158 -0
  140. package/skill-data/visualization/references/settings.md +414 -0
  141. package/skills/metabase-cli/SKILL.md +1 -1
  142. package/dist/add-collection-C9BdVBs2.mjs +0 -11
  143. package/dist/auth-D9eAyVoG.mjs +0 -19
  144. package/dist/capabilities-7e9MgquN.mjs +0 -29
  145. package/dist/card-DDDrWcDU.mjs +0 -20
  146. package/dist/collection-DkEvCDar.mjs +0 -20
  147. package/dist/create-B1dyuL9Y.mjs +0 -54
  148. package/dist/credentials-qryRLUed.mjs +0 -88
  149. package/dist/dashboard-BLf1RZlk.mjs +0 -21
  150. package/dist/database-Ce1gOJF7.mjs +0 -17
  151. package/dist/db-CWTFe_FZ.mjs +0 -22
  152. package/dist/delete-FFj1xQWO.mjs +0 -104
  153. package/dist/deprovision-BNr9fPDY.mjs +0 -67
  154. package/dist/docker-Ds252Mwc.mjs +0 -515
  155. package/dist/field-LL6W_c-c.mjs +0 -18
  156. package/dist/git-sync-CrWTo3YX.mjs +0 -28
  157. package/dist/is-dirty-CPzOnnH6.mjs +0 -10
  158. package/dist/license-B37055sr.mjs +0 -17
  159. package/dist/list-DUXdt0XI.mjs +0 -36
  160. package/dist/logs-Cu3QtvPs.mjs +0 -60
  161. package/dist/measure-CDlEPFtB.mjs +0 -19
  162. package/dist/parse-schemas-D-qVLl4z.mjs +0 -12
  163. package/dist/process-CM7Uu5q_.mjs +0 -105
  164. package/dist/provision-Chf86BF0.mjs +0 -83
  165. package/dist/ps-CEYtsKBj.mjs +0 -80
  166. package/dist/ps-CIDwaubS.mjs +0 -11
  167. package/dist/remove-2yInufA6.mjs +0 -64
  168. package/dist/segment-B6HnNGDs.mjs +0 -19
  169. package/dist/set-Tt-ioa4L.mjs +0 -68
  170. package/dist/snippet-dJ68tGsl.mjs +0 -19
  171. package/dist/start-DJZA67WF.mjs +0 -414
  172. package/dist/status-D5wSqYV_.mjs +0 -34
  173. package/dist/stop-5rCLmkCQ.mjs +0 -87
  174. package/dist/table-J2f0STnB.mjs +0 -19
  175. package/dist/transform-job-OW4SDhsQ.mjs +0 -19
  176. package/dist/transform-q1LYWQtW.mjs +0 -24
  177. package/dist/update-DEZayTb4.mjs +0 -78
  178. package/dist/url-BB6jeNQj.mjs +0 -56
  179. package/dist/wait-B17I_pWy.mjs +0 -19
  180. package/dist/workspace-D8HtUN0y.mjs +0 -72
  181. package/dist/workspace-credentials-8CBMQJFz.mjs +0 -100
  182. package/dist/workspace-ri6r3zWo.mjs +0 -25
  183. package/dist/yaml-Gv6wRFMF.mjs +0 -43
  184. package/skill-data/viz/SKILL.md +0 -137
  185. package/skill-data/viz/references/settings.md +0 -312
  186. package/skill-data/workspace/SKILL.md +0 -390
  187. /package/dist/{body-flags-D7q87Btw.mjs → body-flags-D78h_-Ua.mjs} +0 -0
  188. /package/dist/{collection-Bcy8cWYH.mjs → collection-B3sPXRLs.mjs} +0 -0
  189. /package/dist/{dashboard-B4bn3z6t.mjs → dashboard-FY5UzJ_Z.mjs} +0 -0
  190. /package/dist/{field-E0IBy4Uw.mjs → field-MGxpNQUH.mjs} +0 -0
  191. /package/dist/{input-cMSEqISy.mjs → input-xewHccej.mjs} +0 -0
  192. /package/dist/{key-vkNkH82H.mjs → key-C2XG394c.mjs} +0 -0
  193. /package/dist/{measure-Bt3InQsA.mjs → measure-ClESGxIb.mjs} +0 -0
  194. /package/dist/{paginate-BexjkjbY.mjs → paginate-Dfm9eO9A.mjs} +0 -0
  195. /package/dist/{parse-enum-CrEWOhuY.mjs → parse-enum-Dr7ACrTR.mjs} +0 -0
  196. /package/dist/{parse-ref-DKag6a6I.mjs → parse-ref-BiETXmvm.mjs} +0 -0
  197. /package/dist/{prompt-CFKoys7k.mjs → prompt-u4WhE4T5.mjs} +0 -0
  198. /package/dist/{render-khznBlla.mjs → render-BQHJhSP1.mjs} +0 -0
  199. /package/dist/{revision-message-flag-DY29-cgz.mjs → revision-message-flag-WmsIzUOM.mjs} +0 -0
  200. /package/dist/{segment-DhBmcr_E.mjs → segment-Be2v4ilr.mjs} +0 -0
  201. /package/dist/{setting-BzCng1Ub.mjs → setting-oL97SNeO.mjs} +0 -0
  202. /package/dist/{snippet-bi_0XbNT.mjs → snippet-xh42tYly.mjs} +0 -0
  203. /package/dist/{transform-BKahefz_.mjs → transform-DyJb0bV0.mjs} +0 -0
  204. /package/dist/{transform-job-DjhoJbiV.mjs → transform-job-DpDGoqQt.mjs} +0 -0
@@ -1,18 +1,18 @@
1
1
  ---
2
2
  name: core
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.
3
+ description: Drive a Metabase instance from the terminal via the `mb` CLI — auth, databases, cards, dashboards, collections, transforms, queries, search, git-sync. Use for any `mb <verb>` task.
4
4
  allowed-tools: Read, Write, Edit, Bash, AskUserQuestion
5
5
  ---
6
6
 
7
7
  # metabase-cli (core)
8
8
 
9
- The official Metabase CLI (`mb`) drives a Metabase instance over its REST API. It covers auth, list/get/create/update/delete on every resource, query and transform execution, content search, git-sync (representations ↔ instance), Enterprise workspaces, and entity-id translation.
9
+ The official Metabase CLI (`mb`) drives a Metabase instance over its REST API. It covers auth, list/get/create/update/delete on every resource, query and transform execution, content search, git-sync (representations ↔ instance), and entity-id translation.
10
10
 
11
11
  Top-level command groups (run `mb <group> --help` to discover verbs):
12
12
 
13
13
  ```
14
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
15
+ transform | transform-job | setting | search | git-sync | setup | eid | uuid | upgrade | skills
16
16
  ```
17
17
 
18
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.
@@ -21,8 +21,6 @@ The patterns below — auth, flag conventions, output flags, body input — appl
21
21
 
22
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: 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.
25
-
26
24
  ### Discover what's already configured
27
25
 
28
26
  ```bash
@@ -35,17 +33,7 @@ mb auth status --profile <name> --json # → status of a specific profile
35
33
 
36
34
  ### Pick the profile to use
37
35
 
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.
39
-
40
- ### Other secrets (license, warehouse passwords)
41
-
42
- Same rule: the human runs the storing command. To check whether a license is present:
43
-
44
- ```bash
45
- mb workspace license status --json # → {present: bool}
46
- ```
47
-
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.
36
+ 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` — let the user pick.
49
37
 
50
38
  ## Flag conventions
51
39
 
@@ -56,21 +44,21 @@ If `present: false`, ask the user to run `echo "<your-token>" | mb workspace lic
56
44
  ❌ mb --profile prod table list # → error: "Unknown command prod"
57
45
  ```
58
46
 
59
- `--profile` attaches **after** the full verb chain (`table list`, `card get`, `workspace start`).
47
+ `--profile` attaches **after** the full verb chain (`table list`, `card get`, `git-sync export`).
60
48
 
61
49
  ### `--wait` for async operations
62
50
 
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.
51
+ `transform run`, `git-sync import`, 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" / transient connection refusals.
64
52
 
65
53
  ### Some outputs are JSON envelopes, not bare strings
66
54
 
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:
55
+ A handful of "lookup" verbs return a JSON object even when you only want a single field. `mb setting get <key>` returns `{"key": "...", "value": ...}`, not the bare value. Don't drop them raw into another flag — extract:
68
56
 
69
57
  ```bash
70
- WS_URL=$(mb workspace url <id> --json | jq -r '.url')
58
+ VALUE=$(mb setting get <key> --json | jq -r '.value')
71
59
  ```
72
60
 
73
- If you find yourself writing `--url $(mb ...)` and the receiving command rejects it with "URL must start with http://", this is what happened.
61
+ If you find yourself piping a `--json` envelope straight into another flag and the receiving command rejects it, this is what happened.
74
62
 
75
63
  ## Output
76
64
 
@@ -157,11 +145,10 @@ Routine verb shapes (list / get / create / update), every flag, and output JSON
157
145
 
158
146
  ## Specialized skills (load on demand)
159
147
 
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>`.
148
+ This core file is enough for any single-command task. Load the relevant skill **proactively** when intent matches — don't wing an MBQL body, a transform body, or the git-sync workflow from this overview alone. Load each via `mb skills get <name>`.
161
149
 
162
150
  - **`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
151
  - **`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
152
  - **`transform`** — "create a transform", "run a transform", authoring transform body JSON, run inspection.
166
153
  - **`git-sync`** — "import the latest changes", "export to git", "git sync", "dirty check", "stash before pulling".
167
154
 
@@ -169,9 +156,9 @@ If a task spans more than one, load each. Specialized skills assume the conventi
169
156
 
170
157
  ## Don't
171
158
 
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.
159
+ - **Don't run `mb auth login` for the user** — authentication is theirs (see §Auth).
160
+ - Don't paste credentials or warehouse passwords in chat. Have the user run the storing command.
174
161
  - Don't put `--profile` before the verb chain — the CLI parses it as a subcommand and errors out.
175
- - Don't omit `--wait` on `workspace start` / `transform run` / `workspace database provision` for interactive flows; the next step will race the operation.
162
+ - Don't omit `--wait` on `transform run` / `git-sync import` for interactive flows; the next step will race the operation.
176
163
  - Don't drop a JSON-envelope verb's output raw into another flag. Extract with `--json | jq -r '.<field>'`.
177
164
  - 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.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: git-sync
3
- description: Round-trip Metabase content (cards, dashboards, transforms, snippets, collections) between an instance and a git remote via `mb git-sync …` — status, dirty / has-remote-changes checks, import (with first-fresh-workspace exception), export (with branch guard + working-tree drift), branches, stash, add/remove a collection from sync. Load when the user wants to "import the latest changes", "export to git", "git sync", "dirty check", "stash before pulling", "add a collection to sync", or anything `mb git-sync …`.
3
+ description: Round-trip Metabase content (cards, dashboards, transforms, snippets, collections) between an instance and a git remote via `mb git-sync …` — status, dirty / has-remote-changes checks, import, export (with branch guard), branches, stash, add/remove a collection from sync. Load when the user wants to "import the latest changes", "export to git", "git sync", "dirty check", "stash before pulling", "add a collection to sync", or anything `mb git-sync …`.
4
4
  allowed-tools: Read, Write, Edit, Bash, AskUserQuestion
5
5
  ---
6
6
 
@@ -75,25 +75,6 @@ Workflow:
75
75
  2. `git-sync has-remote-changes` — confirm there's actually something to import.
76
76
  3. `git-sync import --branch <branch>` — runs to terminal status by default.
77
77
 
78
- ### First import on a fresh workspace
79
-
80
- After `workspace start --repo …` brings up a brand-new workspace, the repo content **must be applied** before any other work — without it the instance has none of the repo content and subsequent edits will diverge from what's on disk.
81
-
82
- The container runs a boot-time auto-import on first start, so in most cases the import has already completed by the time `workspace start --wait` returns. Check `git-sync status` first — if `current_task.sync_task_type == "import"` with `status == "successful"` and `.branch` matches the host's branch, you're done; skip the explicit call (it's a wasted round-trip). Only run the explicit `git-sync import` when the auto-import hasn't landed yet.
83
-
84
- When you do need the explicit import, the first one on a fresh instance can report `status: conflict` (typically `conflicts: ["Transforms"]`) even when nothing is dirty — the boot-time auto-import sometimes leaves a stale task record that the first explicit import collides with. Retry the same command once; the second call usually succeeds. If it keeps reporting conflict, `git-sync import --force` is safe in this specific case because the workspace is empty — there's no instance-side work for `--force` to discard. (This is a narrow exception to the usual "confirm with the user before `--force`" rule.)
85
-
86
- ```bash
87
- HOST_BRANCH=$(git -C <repo-path> symbolic-ref --short HEAD)
88
- SYNC_STATUS=$(mb git-sync status --profile <ws-name> --json)
89
- if ! echo "$SYNC_STATUS" | jq -e --arg b "$HOST_BRANCH" \
90
- '.current_task.sync_task_type == "import" and .current_task.status == "successful" and (.branch == $b)' >/dev/null; then
91
- mb git-sync import --branch "$HOST_BRANCH" --profile <ws-name> --json \
92
- || mb git-sync import --branch "$HOST_BRANCH" --profile <ws-name> --json \
93
- || mb git-sync import --branch "$HOST_BRANCH" --force --profile <ws-name> --json
94
- fi
95
- ```
96
-
97
78
  ## Export (instance → remote)
98
79
 
99
80
  ```bash
@@ -111,61 +92,26 @@ Pushes Metabase-side changes back to the configured remote. `-m` is the commit m
111
92
 
112
93
  Workflow:
113
94
 
114
- 1. **Branch guard** (below) — confirm the workspace isn't tracking `main`/`master`, or that the user has explicitly accepted exporting to it.
95
+ 1. **Branch guard** (below) — confirm the instance isn't tracking `main`/`master`, or that the user has explicitly accepted exporting to it.
115
96
  2. `git-sync is-dirty` — confirm there's something to export.
116
97
  3. `git-sync export -m "..."` — pushes and polls.
117
98
  4. (Optional) `git-sync status` — verify `dirty: false` after.
118
- 5. **Working-tree drift** (below) — if this is a `--repo` bind-mount workspace, the host repo's working tree + index will lag behind the new HEAD. Surface this and offer to realign.
119
99
 
120
100
  ### Branch guard: don't export to main/master without confirmation
121
101
 
122
- Workspace work is conventionally done on a feature branch — exporting to `main` (or `master`) commits team-shared content directly. Before `git-sync export`, check the tracked branch and if it's `main`/`master`, ask the user whether to switch first.
123
-
124
- Reading the current branch:
102
+ Sync work is conventionally done on a feature branch — exporting to `main` (or `master`) commits team-shared content directly. Before `git-sync export`, check the tracked branch and if it's `main`/`master`, ask the user whether to switch first.
125
103
 
126
- - For a `--repo` bind-mount workspace, `git -C <repo-path> symbolic-ref --short HEAD` is the most reliable read — that's what the workspace's `remote-sync-branch` was bound to at start time.
127
- - Otherwise: `mb git-sync status --profile <n> --json | jq -r '.branch'`.
104
+ Read the current branch with `mb git-sync status --profile <n> --json | jq -r '.branch'`.
128
105
 
129
106
  If the branch is `main` or `master`, prompt with `AskUserQuestion`:
130
107
 
131
- > "The workspace is tracking `<branch>` — exporting commits straight to it. Switch to a feature branch first?"
108
+ > "The instance is tracking `<branch>` — exporting commits straight to it. Switch to a feature branch first?"
132
109
  >
133
- > 1. **Create a feature branch via the workspace** — agent suggests a name (e.g., `agent/<task>`); run `mb git-sync create-branch <name> --profile <n>`. This exports current dirty state to the new branch and switches the workspace's tracked branch to it; subsequent `git-sync export` calls go to that branch.
134
- > 2. **Switch the host's branch first (bind-mount workspaces)** — `git -C <repo> checkout -b <name>` on the host, then pass `--branch <name>` on the next `git-sync export` so the export targets the new branch (the workspace's `remote-sync-branch` setting won't auto-update from a host-side checkout).
135
- > 3. **Proceed on `main`/`master`** — explicitly accepted; surface the resulting commit (`git -C <repo> log --oneline -1`) afterwards so the user can amend or revert.
110
+ > 1. **Create a feature branch** — agent suggests a name (e.g., `agent/<task>`); run `mb git-sync create-branch <name> --profile <n>`. This exports current dirty state to the new branch and switches the instance's tracked branch to it; subsequent `git-sync export` calls go to that branch.
111
+ > 2. **Proceed on `main`/`master`** — explicitly accepted.
136
112
 
137
113
  Skip the prompt only if the user's instructions already specified the branch (e.g., they explicitly said "export to main" or named a feature branch). Don't silently default to whatever `remote-sync-branch` happens to point at.
138
114
 
139
- ### Post-export: working-tree drift on `--repo` bind-mount workspaces
140
-
141
- When the workspace exports against a host bind mount, the in-container serializer writes the new commit object directly into the bind-mounted `.git/` (creating tree/blob objects and advancing the branch ref) but **does not update the host's working tree or index**. After a successful export, the host repo state is:
142
-
143
- - HEAD: the new export commit.
144
- - Index: still matches the _previous_ HEAD (whatever the user had staged before).
145
- - Working tree: still matches the _previous_ HEAD.
146
-
147
- `git status` then shows "Changes to be committed" that look like the export's content reverting back — purely a display artifact, not an actual revert. The container does this on purpose to avoid clobbering work-in-progress on the host. **Realigning is _applying_ the new HEAD's content to your worktree, not discarding work** — the new commit was written by the exporter, not by your local edits, and your tree/index are stale relative to the new HEAD until you realign.
148
-
149
- **Surface this to the user** after an export against a `--repo` workspace — don't leave them staring at a confusing `git status`. Offer to realign.
150
-
151
- **Prefer `git restore` over `git reset --hard`.** When the only "changes" are the drift artifact (no real local edits), `git restore` does the same job and isn't classified as a destructive operation by Claude Code's permission system — `git reset --hard` is, and gets blocked even after a user-confirmation dialog:
152
-
153
- ```bash
154
- git -C <repo> restore --staged --worktree . # non-destructive; aligns index + working tree to HEAD
155
- ```
156
-
157
- This is the right default after a `git-sync export` realignment when the user had nothing else staged. If `git status` shows a mix of drift artifacts and real pending work, fall back to the stash sequence:
158
-
159
- ```bash
160
- git -C <repo> stash --include-untracked
161
- git -C <repo> restore --staged --worktree .
162
- git -C <repo> stash pop
163
- ```
164
-
165
- `git reset --hard HEAD` is the canonical equivalent and still valid — but **confirm with the user** before running it, and expect Claude Code to gate it as destructive even after the dialog. `git restore --staged --worktree .` produces the same end-state with less friction.
166
-
167
- Or pull in the new files selectively with `git -C <repo> checkout HEAD -- <path>`. Quick check that this is what you're seeing: `git -C <repo> diff --cached HEAD~1 --stat` returns empty (the index matches the parent commit, not the new HEAD).
168
-
169
115
  ## Branches
170
116
 
171
117
  ```bash
@@ -191,6 +137,5 @@ Use `wait` after `import --no-wait` / `export --no-wait`. Use `cancel-task` if a
191
137
  - Don't drive `git-sync` against a Metabase instance that doesn't have remote-sync configured — every verb returns an error pointing at the missing `remote-sync-*` settings. To check: `mb setting get remote-sync-url --profile <n> --json`.
192
138
  - Don't author content directly via `card create` / `transform create` and then assume `git-sync export` will commit it cleanly — the instance and repo can drift if you mix direct API writes with sync-tracked changes. If you do, follow direct writes immediately with `git-sync export -m "..."` to keep them in step.
193
139
  - Don't omit `-m` on `export` if the user wants a meaningful commit message — the default server-generated message is generic.
194
- - Don't `git-sync export` to `main`/`master` without explicit user confirmation — workspace work is conventionally on a feature branch. See "Branch guard" above.
195
- - Don't pretend the host's `git status` is clean after `git-sync export` against a `--repo` bind mount — the export advances HEAD but leaves the working tree + index behind. See "Working-tree drift" above.
140
+ - Don't `git-sync export` to `main`/`master` without explicit user confirmation — sync work is conventionally on a feature branch. See "Branch guard" above.
196
141
  - Don't reach for `mb setting set` to mark a collection as remote-synced — that endpoint writes single-key settings, not the bulk `collections` map. Use `mb git-sync add-collection <id>` / `mb git-sync remove-collection <id>` (see "Adding / removing a directory (collection) to sync" above), and remember the toggle cascades to descendants.
@@ -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
- This skill covers the create-and-run flow. The general flag conventions, body-input precedence, and output flags live in the `core` skill (`mb skills get core`). If you're authoring a transform inside a workspace, also load the `workspace` skill for the canonical-vs-isolation-schema rule.
11
+ This skill covers the create-and-run flow. The general flag conventions, body-input precedence, and output flags live in the `core` skill (`mb skills get core`).
12
12
 
13
13
  ## Body shape
14
14
 
@@ -53,8 +53,8 @@ mb transform run "$TRANSFORM_ID" --wait --profile <name> --json
53
53
 
54
54
  Notes:
55
55
 
56
- - `<db-id>` comes from `mb database list --profile <name> --json`. Database ids are per-instance — a workspace child re-numbers them independently of the parent.
57
- - Target `schema` is the **canonical** name (e.g. `public`). In a workspace, the QP rewrites it to the per-workspace isolation schema (`mb__isolation_<hash>_<ws-id>`) at execution time — don't hard-code that prefix.
56
+ - `<db-id>` comes from `mb database list --profile <name> --json`. Database ids are per-instance.
57
+ - Target `schema` is the schema the result table is written into (e.g. `public`).
58
58
  - `--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.
59
59
  - The `--json` envelope is shape-stable: `{message, run_id, final}`. `final` is always present — `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.
60
60
  - The heredoc with single-quoted `'EOF'` prevents shell from interpolating any `$vars` inside the SQL.
@@ -193,5 +193,4 @@ A schedule lives in a separate resource (`transform-job`) and references one or
193
193
 
194
194
  - 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.
195
195
  - 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.
196
- - Don't write the workspace isolation schema into `target.schema` or SQL. See the `workspace` skill for the canonical-name rule.
197
196
  - 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.
@@ -0,0 +1,158 @@
1
+ ---
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.
4
+ allowed-tools: Read, Write, Edit, Bash, AskUserQuestion
5
+ ---
6
+
7
+ # Visualization: pick the chart, then set it
8
+
9
+ A card has two presentation fields alongside its `dataset_query`:
10
+
11
+ - **`display`** — the chart type (`bar`, `line`, `pie`, `scalar`, `map`, `table`, …). One closed set; pick from the enum below.
12
+ - **`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`.
13
+
14
+ 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.
15
+
16
+ General 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.
17
+
18
+ Two steps: **(1) pick the `display` that fits the data**, then **(2) bind the data columns and set options**.
19
+
20
+ ## Step 1 — pick the `display` for your data
21
+
22
+ Decide which relationship in the data matters most, then pick the chart. The shape each one needs is in the per-display table further down.
23
+
24
+ - **Single headline number** → `scalar` (one KPI). `smartscalar` when the story is the change vs the previous period. `gauge`/`progress` for one value against a target/goal.
25
+ - **Compare a measure across categories** → `bar` (vertical). Use `row` (horizontal bar) when labels are long or there are many categories. Sort by value unless the dimension has a natural order.
26
+ - **Change over time / trend** → `line` for a continuous series; `bar`/`area` for a few discrete periods. Two measures on unlike scales → `combo` (line + bar, dual-axis) — only when the metrics are genuinely related.
27
+ - **Part-to-whole, one snapshot** → `pie`, but only for a meaningful whole with **≤5 slices**; beyond that use a sorted `bar`/`row`. Composition over time → stacked `area`/`bar`.
28
+ - **Distribution / spread / outliers** → `boxplot` (especially comparing several groups).
29
+ - **Correlation between two measures** → `scatter` (a third measure → bubble size).
30
+ - **Sequential additive contributions** (start → +/− steps → total) → `waterfall`.
31
+ - **Stage drop-off in an ordered, cumulative funnel** → `funnel`.
32
+ - **Flow volume between nodes** (source → target + weight) → `sankey`.
33
+ - **Geographic** → `map`: region/choropleth (a region dimension + a measure), pin (lat + long), or grid/heat (coordinates + measure).
34
+ - **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.
35
+
36
+ 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.
37
+
38
+ ## Step 2 — bind data columns and set options
39
+
40
+ ### Which chart for which data, and what to set
41
+
42
+ **Use for** is the data shape each chart suits. **Required** is the minimum to set for it to render as a chart — omit it and the card falls back to a "which columns?" prompt. Everything else is optional (full keys in references). **Empty `"visualization_settings": {}` is valid**: for a simple aggregate the binding is auto-picked, so set keys only to pin or override.
43
+
44
+ | `display` | Use for | Required |
45
+ | --------------------------- | --------------------------------------------------------- | ---------------------------------------------------------- |
46
+ | `scalar` | 1 row, 1 column | — (`scalar.field` only if >1 column) |
47
+ | `smartscalar` | one value grouped by a single **time** field | — (needs a time breakout; `scalar.field` auto) |
48
+ | `gauge` | 1 row, 1 numeric column | — (`gauge.segments` auto) |
49
+ | `progress` | 1 row, ≥1 numeric column | — (`progress.goal` defaults to 0) |
50
+ | `bar` `line` `area` `combo` | >1 row, ≥2 cols, ≥1 dimension + ≥1 measure | `graph.dimensions`, `graph.metrics` |
51
+ | `row` | as bar; prefer for long/many category labels | `graph.dimensions`, `graph.metrics` |
52
+ | `scatter` | two numeric measures (correlation) | `graph.dimensions`, `graph.metrics` (`scatter.bubble` opt) |
53
+ | `waterfall` | exactly 1 dimension + ≥1 measure; sequential | `graph.dimensions` (1), `graph.metrics` (1) |
54
+ | `boxplot` | ≥3 cols, ≥2 dimensions, ≥1 measure | `graph.dimensions`, `graph.metrics` |
55
+ | `pie` | ≥2 rows, ≥2 cols, ≥1 dimension + ≥1 measure; ≤~5 slices | `pie.dimension`, `pie.metric` |
56
+ | `funnel` | 2 columns (stage + value); ordered stages | `funnel.dimension`, `funnel.metric` |
57
+ | `map` (region) | a string/region dimension + a measure | `map.region`, `map.dimension`, `map.metric` |
58
+ | `map` (pin/grid) | latitude + longitude columns | `map.latitude_column`, `map.longitude_column` |
59
+ | `sankey` | ≥3 cols, ≥2 non-date dimensions, ≥1 measure; acyclic flow | `sankey.source`, `sankey.target`, `sankey.value` |
60
+ | `pivot` | ≥2 cols, all aggregated/breakout | — (`pivot_table.column_split` auto) |
61
+ | `table` `object` | anything (table is the universal fallback) | — (always renders) |
62
+
63
+ ### The rule that trips everyone: settings name **output columns**, by name
64
+
65
+ `graph.dimensions`, `graph.metrics`, `pie.dimension`, `pie.metric`, `scalar.field`, `funnel.metric`, `map.latitude_column`, `sankey.source`, … all take **output column-name strings** — the names the query _produces_, not field ids. A `count` aggregation outputs the column `count`; a breakout on a field outputs that field's name; a named aggregation outputs its `name`. These strings are **identical in the API form and the portable (git-sync) form** — no numeric-vs-name footgun here.
66
+
67
+ So the names you put in `visualization_settings` come from the query's output, not from `mb field`/`mb table`. If you set `name` on an aggregation (see the `mbql` skill), use that same string here.
68
+
69
+ ## Minimum-viable settings per chart family (API form)
70
+
71
+ Each block is the `visualization_settings` to pair with the given `display`. The `dataset_query` is elided — build it per the `mbql` skill. Output columns (`CATEGORY`, `count`, …) are whatever the query's breakout/aggregation produce.
72
+
73
+ **Bar / line / area / combo** — one dimension on the x-axis, one or more metrics (the four share an identical key set; switch `display` freely):
74
+
75
+ ```json
76
+ "display": "bar",
77
+ "visualization_settings": { "graph.dimensions": ["CATEGORY"], "graph.metrics": ["count"] }
78
+ ```
79
+
80
+ (Multiple metrics: `"graph.metrics": ["count","sum"]`. Stacked: add `"stackable.stack_type": "stacked"` — or `"normalized"` for 100%. A second dimension in `graph.dimensions` becomes a series breakout.)
81
+
82
+ **Row** — same keys; axes are visually swapped (horizontal bars).
83
+
84
+ **Pie** — one dimension, one metric:
85
+
86
+ ```json
87
+ "display": "pie",
88
+ "visualization_settings": { "pie.dimension": "CATEGORY", "pie.metric": "count" }
89
+ ```
90
+
91
+ **Scalar** (single big number) — the field to surface (only needed if >1 column):
92
+
93
+ ```json
94
+ "display": "scalar",
95
+ "visualization_settings": { "scalar.field": "count" }
96
+ ```
97
+
98
+ **Map (region/choropleth)** — region map + dimension + metric:
99
+
100
+ ```json
101
+ "display": "map",
102
+ "visualization_settings": { "map.type": "region", "map.region": "us_states", "map.dimension": "STATE", "map.metric": "count" }
103
+ ```
104
+
105
+ **Table** — column order/visibility plus per-column formatting:
106
+
107
+ ```json
108
+ "display": "table",
109
+ "visualization_settings": {
110
+ "table.columns": [ { "name": "CATEGORY", "enabled": true }, { "name": "count", "enabled": true } ],
111
+ "column_settings": { "[\"name\",\"count\"]": { "column_title": "Orders" } }
112
+ }
113
+ ```
114
+
115
+ ## `column_settings`: the JSON-string-key footgun
116
+
117
+ `column_settings` is a map **whose keys are themselves JSON-encoded arrays** — so inside a JSON body the inner quotes must be escaped. The key is a _string_, never an object.
118
+
119
+ - **Prefer the name form:** `["name", "<output column name>"]` → in a JSON body, `"[\"name\",\"count\"]"`. This is the canonical key Metabase writes, and it's **identical in API and portable form**. Use it unless you have a reason not to.
120
+ - **Ref form (legacy order!):** `["ref", ["field", <id>, <opts>]]`. The inner field ref uses the **legacy MBQL-4 order** `["field", id, options]` (id **second**) — _not_ the MBQL-5 order you use in `dataset_query`. In the API form `<id>` is the numeric field id. Because the order differs, this form is easy to get wrong — reach for the name form instead.
121
+
122
+ ```json
123
+ "column_settings": {
124
+ "[\"name\",\"TOTAL\"]": { "number_style": "currency", "currency": "USD", "decimals": 2 },
125
+ "[\"name\",\"CREATED_AT\"]": { "date_style": "MMMM D, YYYY" }
126
+ }
127
+ ```
128
+
129
+ The exhaustive per-column key list (number/date formatting, `view_as`, alignment, mini bars, click behavior) is in the references file.
130
+
131
+ ## Escape hatch: pull a real card instead of authoring from scratch
132
+
133
+ For anything beyond a single dimension + metric — combo charts, conditional formatting, pivot splits, click behavior, series colors — the cheapest **correct** path is to build it once in the Metabase UI and copy the result:
134
+
135
+ ```bash
136
+ mb card get <id> --full --json | jq '.visualization_settings'
137
+ ```
138
+
139
+ Paste that block into your `card create`/`update` body. The server produced it, so it's valid for that `display`. This beats guessing keys from memory, and it's token-cheap.
140
+
141
+ ## Full per-visualization key catalog
142
+
143
+ The body above covers the high-frequency 90%. The complete per-chart key tables — every key with its values and defaults, the data shape each chart suits, the full `column_settings` and `series_settings` vocabularies, conditional formatting, pivot splits, virtual cards (heading/text/link/iframe), and click behavior — live in the references file. Load on demand, not by default:
144
+
145
+ ```bash
146
+ mb skills get visualization --full # appends references/settings.md to this body
147
+ mb skills path visualization # → the skill dir; then Read references/settings.md
148
+ ```
149
+
150
+ ## Don't
151
+
152
+ - 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.
153
+ - Don't put numeric field ids in `graph.dimensions`/`pie.metric`/`scalar.field`/`map.latitude_column` etc. — they take **output column-name strings**.
154
+ - 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.
155
+ - Don't write a `column_settings` key as an object — it's a JSON **string** (`"[\"name\",\"COL\"]"`), inner quotes escaped.
156
+ - Don't use the MBQL-5 field-ref order inside a `column_settings` `["ref", …]` key — that key uses the **legacy** `["field", id, opts]` order. Prefer the `["name", …]` form.
157
+ - Don't expect a pre-flight to catch viz mistakes — there is none. Verify by reading the card back.
158
+ - Don't hand-author complex charts when you can pull a working `visualization_settings` from a UI-built card.