@metabase/cli 0.1.18 → 0.2.1-alpha.bcm-ghy-4075-spawn-default-profile.eb120d1

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 (237) hide show
  1. package/README.md +283 -45
  2. package/dist/add-collection-CCpC-GUz.mjs +9 -0
  3. package/dist/{add-collection-CRZAFCZy.mjs → add-collection-CLXfrgp4.mjs} +4 -5
  4. package/dist/append-DMcxP1LM.mjs +42 -0
  5. package/dist/archive-4WqMsPWB.mjs +38 -0
  6. package/dist/{archive-De4yVEV8.mjs → archive-5inmbMDL.mjs} +5 -7
  7. package/dist/{archive-3mEhK9_H.mjs → archive-BggM_AuL.mjs} +5 -7
  8. package/dist/{archive-Z8ZM-ilB.mjs → archive-BmhehbS0.mjs} +8 -9
  9. package/dist/{archive-d74nDcp4.mjs → archive-BwIZzWGe.mjs} +5 -7
  10. package/dist/{archive-CFHYRF65.mjs → archive-DErhQn8A.mjs} +5 -7
  11. package/dist/archive-DTnz1suV.mjs +38 -0
  12. package/dist/{archive-C3RWdM-2.mjs → archive-Q1387APe.mjs} +7 -8
  13. package/dist/{archive-errg-EKw.mjs → archive-h-3H5b4K.mjs} +5 -7
  14. package/dist/auth-DzCAiArV.mjs +22 -0
  15. package/dist/{body-BS2s2zDl.mjs → body-5snfmiwR.mjs} +3 -3
  16. package/dist/{branches-mcyT7wI-.mjs → branches-PL170CYS.mjs} +5 -7
  17. package/dist/{cancel-B2l-i2NS.mjs → cancel-EFKcF5J_.mjs} +4 -6
  18. package/dist/{cancel-task-CugcIeIi.mjs → cancel-task-mFFx0d5m.mjs} +5 -7
  19. package/dist/capabilities-CiaAfUgX.mjs +684 -0
  20. package/dist/{card-DEmcRlNO.mjs → card-B8G5jQQT.mjs} +24 -18
  21. package/dist/card-BsfdXK0O.mjs +26 -0
  22. package/dist/{cards-BGkz5BeZ.mjs → cards-BI7zKb9M.mjs} +7 -8
  23. package/dist/cli.mjs +178 -29
  24. package/dist/collection-2h_by9Ue.mjs +23 -0
  25. package/dist/{collection-DrLpA1SO.mjs → collection-C52Ndi_O.mjs} +5 -3
  26. package/dist/{collection-namespace-BP_LJrAD.mjs → collection-namespace-uNA5WhLB.mjs} +2 -2
  27. package/dist/{create-Cztp5kES.mjs → create-BLD0YniB.mjs} +6 -7
  28. package/dist/create-BP8EIWrP.mjs +161 -0
  29. package/dist/{create-CkRFKtFG.mjs → create-BpizGzgQ.mjs} +8 -9
  30. package/dist/create-BxCsAdqc.mjs +45 -0
  31. package/dist/{create-DzyMvAVy.mjs → create-CBKT7SE_.mjs} +8 -9
  32. package/dist/{create-D8KA4zJA.mjs → create-CQ32FwVe.mjs} +11 -11
  33. package/dist/{create-YfGzANNB.mjs → create-CU3bYpsT.mjs} +8 -9
  34. package/dist/create-DAH24ltu.mjs +45 -0
  35. package/dist/{create-BOmmMSjF.mjs → create-DutkaD7P.mjs} +11 -12
  36. package/dist/{create-B4HAEEE0.mjs → create-FuZa4j1N.mjs} +8 -9
  37. package/dist/{create-wpvDXSSY.mjs → create-KveE3j3p.mjs} +12 -13
  38. package/dist/{create-DhrftYre.mjs → create-VrSowEbj.mjs} +14 -14
  39. package/dist/{create-CD9Rp8qs.mjs → create-acWKw4Y1.mjs} +14 -14
  40. package/dist/{create-branch-DsoMfx_2.mjs → create-branch-CP1Y4_El.mjs} +5 -7
  41. package/dist/{create-kWzqvTGR.mjs → create-xPfmI1Tu.mjs} +11 -12
  42. package/dist/csv-CZyYi0vi.mjs +58 -0
  43. package/dist/{current-task-WuKPZpZ6.mjs → current-task-BR4koo9z.mjs} +5 -7
  44. package/dist/dashboard-ARgJfamb.mjs +35 -0
  45. package/dist/{dashboard-DOplbKyQ.mjs → dashboard-BLihjDXg.mjs} +35 -15
  46. package/dist/{database-D9fftP-i.mjs → database-BOqzcexD.mjs} +2 -2
  47. package/dist/db-dbnxMPSR.mjs +27 -0
  48. package/dist/{delete-Buai0_e_.mjs → delete-BpL3vaaT.mjs} +6 -8
  49. package/dist/{delete-BCitmugk.mjs → delete-C5DqDwev.mjs} +6 -8
  50. package/dist/delete-DCDFLHFa.mjs +49 -0
  51. package/dist/delete-P2Qlsr_s.mjs +49 -0
  52. package/dist/{delete-CmGh0jo1.mjs → delete-ea_ns1uM.mjs} +6 -8
  53. package/dist/{delete-runtime-B0ha5QR4.mjs → delete-runtime-DdBn6poA.mjs} +2 -3
  54. package/dist/{delete-table-BOkYxdjA.mjs → delete-table-tM048btr.mjs} +6 -8
  55. package/dist/{dependencies-XZrEvHUA.mjs → dependencies-DdOTClO2.mjs} +5 -7
  56. package/dist/destroy-B51EPvOJ.mjs +168 -0
  57. package/dist/{dirty-B-5NDbG9.mjs → dirty-CEQYmhNF.mjs} +5 -7
  58. package/dist/{document-1W7NRaO_.mjs → document-D4zB-5a0.mjs} +20 -4
  59. package/dist/document-DR1FLOn9.mjs +22 -0
  60. package/dist/{eid-BCuJRv7a.mjs → eid-CtKGjAkz.mjs} +10 -11
  61. package/dist/embedding-CKdw8q7p.mjs +11 -0
  62. package/dist/events-C26DZ6yZ.mjs +44 -0
  63. package/dist/{export-Dod2gdyE.mjs → export-CippuWZJ.mjs} +5 -10
  64. package/dist/export-Cy2DFtR2.mjs +10 -0
  65. package/dist/field-CXDndCbB.mjs +21 -0
  66. package/dist/{field-CMY_LWUe.mjs → field-DsXTeqBy.mjs} +6 -4
  67. package/dist/{fields-B0yttrzR.mjs → fields-Xm_HVNcX.mjs} +6 -8
  68. package/dist/get-3O7hv4zv.mjs +67 -0
  69. package/dist/{get-D-pNrQGA.mjs → get-B0cq1uGy.mjs} +5 -7
  70. package/dist/{get-k6M5nGMC.mjs → get-B90bXbpw.mjs} +5 -7
  71. package/dist/{get-D7QMqPOK.mjs → get-BQBu2c94.mjs} +5 -7
  72. package/dist/{get-CfeYa0v5.mjs → get-BQz45IBF.mjs} +6 -7
  73. package/dist/get-BSfKWZNJ.mjs +35 -0
  74. package/dist/get-BdNC8WM8.mjs +38 -0
  75. package/dist/{get-DBY5esTW.mjs → get-Bj66bcuU.mjs} +5 -7
  76. package/dist/{get-BpZ81hX8.mjs → get-BrKqMM1d.mjs} +5 -7
  77. package/dist/{get-9p0Gt1c7.mjs → get-CjZWjRa5.mjs} +5 -7
  78. package/dist/{get-DISgP66L.mjs → get-CwGqARAf.mjs} +16 -9
  79. package/dist/{get-BkGh9BPd.mjs → get-D5crrFSf.mjs} +5 -7
  80. package/dist/{get-Dz7fcqoG.mjs → get-DTkvGu_J.mjs} +8 -9
  81. package/dist/{get-u5Uq9vms.mjs → get-DX2fp6BB.mjs} +4 -6
  82. package/dist/get-DYuJVlP-.mjs +35 -0
  83. package/dist/{get-DJ8huA8y.mjs → get-FIE88hj9.mjs} +7 -8
  84. package/dist/{get-B0OsW6jk.mjs → get-Ihx_XaaI.mjs} +5 -7
  85. package/dist/{get-run-DQJVpDw1.mjs → get-run-lVwhQVFx.mjs} +5 -7
  86. package/dist/{get-CbpHt4Hs.mjs → get-z0BKkiRX.mjs} +4 -6
  87. package/dist/git-sync-CmB4YBmS.mjs +31 -0
  88. package/dist/{group-BNE_RiH5.mjs → group-CILtQqCL.mjs} +2 -1
  89. package/dist/{has-remote-changes-DvQBXudi.mjs → has-remote-changes-DHrYSnTg.mjs} +5 -7
  90. package/dist/{import-PGS8-DwE.mjs → import-BU40xXSF.mjs} +7 -9
  91. package/dist/{input-7Sj85_K7.mjs → input-BGyHn0Kr.mjs} +6 -3
  92. package/dist/{is-dirty-DyEVFQVJ.mjs → is-dirty-BPluqPPP.mjs} +3 -4
  93. package/dist/is-dirty-DOy1TRcf.mjs +8 -0
  94. package/dist/{items-CkFEy2Du.mjs → items-BZpYkImP.mjs} +6 -8
  95. package/dist/{key-bltP32Pm.mjs → key-B8UAsQmB.mjs} +1 -1
  96. package/dist/{library-1AAVbk-K.mjs → library-BGWDIfEM.mjs} +6 -6
  97. package/dist/list-B037kJtH.mjs +39 -0
  98. package/dist/{list-82NkvRpI.mjs → list-B7Ihu6XI.mjs} +7 -8
  99. package/dist/{list-pQ22nXhQ.mjs → list-BCeZlfpQ.mjs} +4 -6
  100. package/dist/{list-CKzpoTgP.mjs → list-Bct00qku.mjs} +6 -8
  101. package/dist/{list-D-rgDFa5.mjs → list-Be8xMtS3.mjs} +8 -9
  102. package/dist/{list-Bb8YRON_.mjs → list-CjBPVJqd.mjs} +4 -6
  103. package/dist/{list-BCYTTCFD.mjs → list-CoqHnusK.mjs} +4 -6
  104. package/dist/{list-B9wXg3qi.mjs → list-CvaoHnl-.mjs} +3 -5
  105. package/dist/{list-BVzu2RIZ.mjs → list-DamRE5Ds.mjs} +4 -6
  106. package/dist/{list-BWAYDSbQ.mjs → list-DmopauvJ.mjs} +4 -6
  107. package/dist/{list-CJx5q-Yn.mjs → list-M_1sdYNH.mjs} +6 -8
  108. package/dist/{list-CK0p7vvK.mjs → list-Ov6bYzT9.mjs} +7 -8
  109. package/dist/{list-Sgo3RfDY.mjs → list-SfpASSFx.mjs} +4 -6
  110. package/dist/{list-BBxRjuMn.mjs → list-fPfgG0PY.mjs} +4 -6
  111. package/dist/list-lsPeumLP.mjs +34 -0
  112. package/dist/{list-BY4S32Lg.mjs → list-sn9M7QQX.mjs} +4 -6
  113. package/dist/{list-CjF12k1G.mjs → list-wxIVl45_.mjs} +5 -7
  114. package/dist/listing-wFZuS5li.mjs +10 -0
  115. package/dist/{login-C6ZAnGHz.mjs → login-CRlmR4op.mjs} +61 -23
  116. package/dist/{logout-CWjyY3Y8.mjs → logout-DSZEUxSQ.mjs} +5 -7
  117. package/dist/{measure-DoJvtCaA.mjs → measure-C3G1srWb.mjs} +5 -4
  118. package/dist/measure-Ka0n3426.mjs +25 -0
  119. package/dist/{parameter-values-DmDOuE-j.mjs → parameter-values-CwW8Xes7.mjs} +5 -7
  120. package/dist/{parse-enum-BL9i_brN.mjs → parse-enum-ooCsJWn6.mjs} +1 -1
  121. package/dist/parse-id-xS1tWJAq.mjs +18 -0
  122. package/dist/{parse-ref-CB_KvF9h.mjs → parse-ref-j4lxaQmd.mjs} +1 -1
  123. package/dist/{path-D8IJ4YrW.mjs → path-CVmg-bmf.mjs} +4 -6
  124. package/dist/{poll-hgnrHBoh.mjs → poll-CuTlWBwI.mjs} +2 -2
  125. package/dist/{poll-task-NQNLT_aA.mjs → poll-task-DhsYs8Hx.mjs} +2 -2
  126. package/dist/{preflight-DvaPQHHf.mjs → preflight-D3VgjEg7.mjs} +4 -5
  127. package/dist/{process-j8UHMHc2.mjs → process-7c8BcQEi.mjs} +1 -1
  128. package/dist/profile-name-CZ1-Vtrh.mjs +7 -0
  129. package/dist/{prompt-C85xd9HR.mjs → prompt-jQnNKtlx.mjs} +1 -1
  130. package/dist/{publish-BntmFR6g.mjs → publish-DFTCsjHU.mjs} +13 -10
  131. package/dist/{query-DSKQZu91.mjs → query-CPq-dhFV.mjs} +15 -15
  132. package/dist/{query-BQfgKV68.mjs → query-Dfx69iJ4.mjs} +9 -10
  133. package/dist/{query-result-D6mfoVfQ.mjs → query-result-Cg7odyR_.mjs} +1 -1
  134. package/dist/{remove-collection-BguaI3-6.mjs → remove-collection-DUAMhx5L.mjs} +7 -9
  135. package/dist/replace-DZWd6avZ.mjs +42 -0
  136. package/dist/requests-DYrDe-D0.mjs +96 -0
  137. package/dist/{rescan-values-DjD6IW3J.mjs → rescan-values-DZSnEFA0.mjs} +7 -9
  138. package/dist/{resolve-Dj2MTBkn.mjs → resolve-BTZuO3Yq.mjs} +22 -15
  139. package/dist/{run-Bp1yxkBN.mjs → run-B2b3Bjmv.mjs} +4 -6
  140. package/dist/{run-RQfQj7Rk.mjs → run-ryZ4sXK5.mjs} +7 -9
  141. package/dist/{runs-CSsatfWb.mjs → runs-F82HVSie.mjs} +5 -7
  142. package/dist/{runtime-oxjmrYoP.mjs → runtime-C6ASrgVA.mjs} +944 -909
  143. package/dist/{schema-tables-5I5pCxHl.mjs → schema-tables-CCOKb715.mjs} +6 -8
  144. package/dist/{schemas-CfzFCfBt.mjs → schemas-LtVrNECH.mjs} +4 -6
  145. package/dist/{search-Vo-BqliA.mjs → search-9GSZHgJQ.mjs} +4 -6
  146. package/dist/segment-C40bORNZ.mjs +25 -0
  147. package/dist/{segment-TXktTCfU.mjs → segment-CxRYEXeO.mjs} +6 -5
  148. package/dist/{selectors-DBnJsKlW.mjs → selectors-B2nDmrIV.mjs} +3 -3
  149. package/dist/{set-1Vh0AF-T.mjs → set-C57KDMGM.mjs} +9 -9
  150. package/dist/{set-active-C0mUZlNN.mjs → set-active-BDHGHtj2.mjs} +4 -6
  151. package/dist/setting-zrJwy48C.mjs +20 -0
  152. package/dist/{setup-CzYFvFK5.mjs → setup-Doq8atTG.mjs} +11 -12
  153. package/dist/{skills-B6gfH0iR.mjs → skills-y-KsUqT9.mjs} +1 -1
  154. package/dist/{skills-CR2xyV33.mjs → skills-yuzH_o8s.mjs} +3 -3
  155. package/dist/{snippet-CtA2Pkoa.mjs → snippet-C8PVaPY9.mjs} +7 -4
  156. package/dist/snippet-ScFM8re8.mjs +22 -0
  157. package/dist/{stash-CQHXwBu_.mjs → stash-D_u6z4q6.mjs} +7 -9
  158. package/dist/{status-C_7aqTvB.mjs → status--Dm2Jv6k.mjs} +5 -7
  159. package/dist/{status-DXQkM18v.mjs → status-CDPhDX5I.mjs} +27 -13
  160. package/dist/{summary-1aNpQc8j.mjs → summary-C9a2cBTE.mjs} +5 -7
  161. package/dist/{sync-schema-BqLp5uTS.mjs → sync-schema-CDIWNCGI.mjs} +9 -11
  162. package/dist/sync-scope-DW2QpGwv.mjs +55 -0
  163. package/dist/{table-DE3i82T_.mjs → table-3onHpwcb.mjs} +16 -4
  164. package/dist/table-BKehBm_E.mjs +21 -0
  165. package/dist/timeline-DEaiuB5V.mjs +157 -0
  166. package/dist/timeline-event-CLWMxvE0.mjs +22 -0
  167. package/dist/timeline-n3Nw9Egt.mjs +24 -0
  168. package/dist/transform-CrOOqKen.mjs +31 -0
  169. package/dist/{transform-DEF38FWe.mjs → transform-DP6J2ber.mjs} +31 -5
  170. package/dist/transform-job-J-E0Mec7.mjs +25 -0
  171. package/dist/transform-tag-Bkyasy_s.mjs +21 -0
  172. package/dist/{transforms-BelyllUL.mjs → transforms-w2a5qsjJ.mjs} +5 -7
  173. package/dist/{tree-YvmwGql7.mjs → tree-JZC6vD9q.mjs} +4 -6
  174. package/dist/{unpublish-CHjGLMu9.mjs → unpublish-BsfIH46y.mjs} +5 -7
  175. package/dist/{update-D7vc8GBF.mjs → update-B8hn6VxO.mjs} +12 -13
  176. package/dist/{update-BKhekuqr.mjs → update-BSNK36fk.mjs} +15 -15
  177. package/dist/update-BTFj6WHo.mjs +52 -0
  178. package/dist/{update-BACl_bJS.mjs → update-BfIGjAVA.mjs} +9 -10
  179. package/dist/{update-CrRz47aj.mjs → update-C1_aIL6X.mjs} +15 -15
  180. package/dist/{update-K-jjQ0Aw.mjs → update-CXKWHOIN.mjs} +10 -11
  181. package/dist/{update-BnHYSu96.mjs → update-Cg09lBb5.mjs} +13 -14
  182. package/dist/update-D01S8hnw.mjs +52 -0
  183. package/dist/{update-txUfAJxy.mjs → update-DKQjMsuE.mjs} +9 -10
  184. package/dist/{update-Zz01Woxj.mjs → update-DSlSr7Ky.mjs} +9 -10
  185. package/dist/{update-C9fndCqb.mjs → update-QaBVvlz9.mjs} +9 -10
  186. package/dist/{update-BGMJTpA6.mjs → update-WLb9099y.mjs} +12 -13
  187. package/dist/{update-BWlN3QDo.mjs → update-bFNKpay8.mjs} +9 -10
  188. package/dist/{update-dashcard-Dvth-yLC.mjs → update-dashcard-CKZC7scy.mjs} +11 -11
  189. package/dist/{upgrade-DsXlfPef.mjs → upgrade-DkK1ZbEY.mjs} +5 -7
  190. package/dist/upload-DOMoaR9f.mjs +16 -0
  191. package/dist/{uuid-ByJmWV7b.mjs → uuid-DDbLKnbe.mjs} +3 -5
  192. package/dist/{validate-BqNW4Sk1.mjs → validate-DqQ7DQnP.mjs} +1 -2
  193. package/dist/{validate-query-CcZVKYPV.mjs → validate-query-BYG7lyaY.mjs} +2 -3
  194. package/dist/{values-CIjJI4sz.mjs → values-Bc99Kkuv.mjs} +5 -7
  195. package/dist/{verify-Jsuc2dat.mjs → verify-WvMbaT8W.mjs} +2 -2
  196. package/dist/{wait-5ltTFvSa.mjs → wait-DmgEPnHW.mjs} +6 -8
  197. package/dist/{wait-flags-D6kd_G7c.mjs → wait-flags-BpzyrpAL.mjs} +2 -2
  198. package/dist/workspace-BgH_4xFO.mjs +75 -0
  199. package/dist/workspace-LFtHxcw9.mjs +19 -0
  200. package/package.json +1 -1
  201. package/skill-data/core/SKILL.md +31 -23
  202. package/skill-data/dashboard/SKILL.md +26 -3
  203. package/skill-data/data-workflow/SKILL.md +17 -15
  204. package/skill-data/git-sync/SKILL.md +20 -5
  205. package/skill-data/visualization/SKILL.md +1 -0
  206. package/dist/add-collection-DbalcC3_.mjs +0 -11
  207. package/dist/auth--PWX0oj5.mjs +0 -22
  208. package/dist/capabilities-N0jo5U7S.mjs +0 -239
  209. package/dist/card-D07WYCQY.mjs +0 -26
  210. package/dist/collection-Dss2qSEF.mjs +0 -23
  211. package/dist/dashboard-Crh4v6Fq.mjs +0 -35
  212. package/dist/db-BCAURQei.mjs +0 -28
  213. package/dist/document-upxL2nKH.mjs +0 -22
  214. package/dist/error-BaBm-UrT.mjs +0 -227
  215. package/dist/field-CloFa1oe.mjs +0 -21
  216. package/dist/get-B8tugTku.mjs +0 -50
  217. package/dist/git-sync-C9m-OHac.mjs +0 -31
  218. package/dist/is-dirty-DpyKeGAJ.mjs +0 -10
  219. package/dist/manifest-BVf8P4bl.mjs +0 -126
  220. package/dist/measure-Cbly1r0E.mjs +0 -25
  221. package/dist/metadata-CW5Lfw5d.mjs +0 -39
  222. package/dist/metadata-aQAqseCm.mjs +0 -38
  223. package/dist/notice-DyVl5aYB.mjs +0 -206
  224. package/dist/parse-id-DlXnOcmP.mjs +0 -12
  225. package/dist/segment-BuN_IoM8.mjs +0 -25
  226. package/dist/setting-BbdKR-lO.mjs +0 -20
  227. package/dist/snippet-Bzo2U9Fv.mjs +0 -22
  228. package/dist/table-BwGOz97O.mjs +0 -22
  229. package/dist/transform-B65ZD9-e.mjs +0 -31
  230. package/dist/transform-job-BWVKXSV6.mjs +0 -25
  231. package/dist/transform-tag-C4qvmicL.mjs +0 -21
  232. /package/dist/{body-flags-DWTTxJpP.mjs → body-flags-CgHgJBRC.mjs} +0 -0
  233. /package/dist/{command-augment-DdZIfx1V.mjs → command-augment-D9pI9Vbh.mjs} +0 -0
  234. /package/dist/{parameter-CiJ4CwWE.mjs → parameter-BZ6y-Gfs.mjs} +0 -0
  235. /package/dist/{render-BTKnWL0d.mjs → render-CkuFkWlQ.mjs} +0 -0
  236. /package/dist/{transform-job-CtixL4An.mjs → transform-job-B5x8aYfV.mjs} +0 -0
  237. /package/dist/{transform-tag-rsIrckCM.mjs → transform-tag-BRwfWeHf.mjs} +0 -0
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: core
3
- description: Foundations for driving Metabase from the terminal with the `mb` CLI — authentication and named profiles, the flag/output/`--json` conventions every command shares, JSON body input, command discovery via `mb __manifest`, and the per-resource footguns (db, table, field, card, dashboard, collection, segment, measure, library, setting, search, eid). Load first for any `mb` task; it routes to the specialized skills for deeper work.
3
+ description: Foundations for driving Metabase from the terminal with the `mb` CLI — authentication and named profiles, the flag/output/`--json` conventions every command shares, JSON body input, command discovery via `--help` (add `--json` for machine-readable schemas), and the per-resource footguns (db, table, field, upload, card, dashboard, collection, segment, measure, timeline, library, setting, search, eid). Load first for any `mb` task; it routes to the specialized skills for deeper work.
4
4
  allowed-tools: Read, Write, Edit, Bash, AskUserQuestion
5
5
  ---
6
6
 
@@ -11,11 +11,12 @@ The official Metabase CLI (`mb`) drives a Metabase instance over its REST API: a
11
11
  Top-level command groups (run `mb <group> --help` to discover verbs):
12
12
 
13
13
  ```
14
- auth | db | table | field | query | card | dashboard | snippet | segment | measure | collection | library
15
- document | transform | transform-job | transform-tag | setting | search | git-sync | setup | eid | uuid | upgrade | skills
14
+ auth | db | table | field | upload | query | card | dashboard | snippet | segment | measure | collection | library
15
+ document | timeline | timeline-event | transform | transform-job | transform-tag | setting | search | git-sync | setup
16
+ eid | uuid | upgrade | skills
16
17
  ```
17
18
 
18
- The conventions below — auth, flags, output, body input — hold across **every** group. Per-command flags, examples, and output schemas live in `mb __manifest`. A few flows have their own skills (see "Specialized skills"). When a card needs a query, prefer MBQL over native SQL (portable, pre-flight-validated — load `mbql`); fall back to native SQL when MBQL can't express it.
19
+ The conventions below — auth, flags, output, body input — hold across **every** group. Per-command flags and examples live in each command's `--help`; add `--json` for the machine-readable form with the output JSON Schema. A few flows have their own skills (see "Specialized skills"). When a card needs a query, prefer MBQL over native SQL (portable, pre-flight-validated — load `mbql`); fall back to native SQL when MBQL can't express it.
19
20
 
20
21
  ## Auth & profiles
21
22
 
@@ -59,22 +60,25 @@ Every list/get verb supports the same output flags:
59
60
  - `--json` — emit the full JSON envelope, safe for `jq`. Default is human-readable text.
60
61
  - `--full` — include every field (the compact projection is the default, and is the agent-facing contract).
61
62
  - `--fields a,b.c.d` — project specific dot-paths. Mutually exclusive with `--full`. **Paths are relative to each `data[]` item on list verbs, and to the root on single-item verbs.** So it's `--fields id,name` on `… list` / `database schema-tables` (`data.id` and `data[].id` both fail with `unknown field path: "data.id"`), and `--fields id,name,display` on `card get`, `--fields data.rows` on `mb query` (whose `data` is an object).
62
- - `--max-bytes <n>` — cap **list** output size (drops trailing items, sets `truncated`). Default 65536; `0` disables. Single-item commands (`get`, `metadata`) never truncate — when their output exceeds the cap they throw a `ConfigError` (exit 2: "output is N bytes, over the M-byte --max-bytes cap…"); raise `--max-bytes` or narrow with `--fields`.
63
+ - `--max-bytes <n>` — cap **list** output size (drops trailing items, sets `truncated`). Default 24576 (sized to fit under agent-harness tool-output limits); `0` disables. Single-item commands (`get`) never truncate — when their output exceeds the cap they throw a `ConfigError` (exit 2: "output is N bytes, over the M-byte --max-bytes cap; …") whose tail names the remedy: on schema-shaped commands it is the exact narrower command to run instead — follow it rather than raising the cap.
64
+ - JSON output is a single line when stdout is piped (pretty-printed only at a TTY) — always parse it, never scrape by line position.
63
65
 
64
- List envelope shape:
66
+ List envelope shape (metadata first, so counts and the truncation marker survive if a consumer cuts the tail):
65
67
 
66
68
  ```json
67
69
  {
68
- "data": [
69
- /* items */
70
- ],
71
70
  "returned": 10,
72
71
  "total": 42,
73
72
  "limit": 50,
74
- "truncated": false
73
+ "truncated": { "reason": "max_bytes", "bytes": 123456 },
74
+ "data": [
75
+ /* items */
76
+ ]
75
77
  }
76
78
  ```
77
79
 
80
+ `truncated` is present only when the cap dropped trailing items.
81
+
78
82
  `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.
79
83
 
80
84
  ## Body input (create / update / run)
@@ -99,38 +103,42 @@ Single-quoted `'EOF'` stops the shell interpolating `$vars` inside the JSON.
99
103
 
100
104
  Write working files to **`./.scratch`** in the current directory (`mkdir -p ./.scratch` first), never `/tmp` — better permissions, they persist across the session, and the user can review them.
101
105
 
102
- ## Discover the full surface: `mb __manifest`
106
+ ## Discovering commands and schemas
107
+
108
+ Cheapest source that answers the question wins:
103
109
 
104
- 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:
110
+ - What groups/verbs exist? → `mb --help`, then `mb <group> --help`. Add `--json` for a machine-readable `{command, description}` index (`mb --help --json` lists every command).
111
+ - What flags does a command take? → `mb <command> --help` — flags with enums and defaults, examples, ~1 KB.
112
+ - Output JSON Schema before parsing, JSON-body input schema before authoring, machine-readable arg types, min server version? → `mb <command> --help --json` — that command's full entry (`inputSchema` is the exact validator the command runs on the body; `null` when it takes none).
105
113
 
106
114
  ```bash
107
- mb __manifest | jq -r '.commands[].command' # every command name
108
- mb __manifest | jq -r '.commands[] | select(.command | startswith("transform")) | .command' # verbs under "transform"
109
- mb __manifest | jq '.commands[] | select(.command == "card query") | .args' # flags + types for a command
110
- mb __manifest | jq '.commands[] | select(.command == "card list") | .outputSchema' # output schema before parsing
115
+ mb card query --help # flags, enums, defaults, examples
116
+ mb card list --help --json | jq .outputSchema # output schema before parsing
117
+ mb card create --help --json | jq .inputSchema # body schema before authoring
118
+ mb transform --help --json | jq -r '.commands[].command' # verbs under "transform"
111
119
  ```
112
120
 
113
- The leading `__` hides it from `--help`, but it's stable. Reach for it instead of per-command `--help` to enumerate verbs, validate flag names, or read an output schema before parsing.
114
-
115
121
  ## Resource quirks worth memorizing
116
122
 
117
- Routine verb shapes (list / get / create / update), every flag, and output schemas live in `mb __manifest`. Below is only what the manifest does _not_ tell you: footguns and non-obvious behaviors.
123
+ Routine verb shapes (list / get / create / update), every flag, and output schemas live in each command's `--help` (add `--json` for output schemas). Below is only what help does _not_ tell you: footguns and non-obvious behaviors.
118
124
 
119
- - **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; `sync-schema --wait` blocks until `initial_sync_status: complete`.
120
- - **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.
125
+ - **db traversal: the hydration ladder.** Start with `database get <db-id> --include tables` — the compact table map (id, name, schema, description per table), one call that fits most databases. Pick the relevant tables, then `table fields <table-id>` per table (bounded: fields are per-table). `--include tables.fields` is the full rollup — small databases only; when either outgrows the cap the error message names the next command down the ladder. Hundreds of tables? Traverse by schema (`database schemas <db-id>` → `database schema-tables <db-id> <schema>`) or look tables up by name (`search <term> --models table --db-id <db-id> --limit 10`). `sync-schema` / `rescan-values` queue async work and return `{status:"ok"}` immediately; `sync-schema --wait` blocks until `initial_sync_status: complete`.
126
+ - **table fields.** `table get` never returns fields on its own — pass `--include fields` (compact; the underlying query_metadata response also carries FK targets and dimensions, visible under `--full`) or use `table fields <id>` (list envelope). `table update` patches table-level metadata only; physical columns aren't editable.
121
127
  - **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) — this is where you set a column's `semantic_type` or foreign-key target.
128
+ - **upload (CSV → tables).** `upload csv --file <path>` creates a new table + model (prints `{model_id, table_id}`); `upload append <table-id>` / `upload replace <table-id> --file <path>` add to / overwrite a table **previously created by upload** (columns must match). The destination db+schema is admin-configured, not per-call — check with `mb setting get uploads-settings --json` (`db_id: null` ⇒ uploads off/unconfigured; needs admin to read). `--collection <id|root>` only sets the model's collection. Max 50 MB. Errors: **"The uploads database is not configured."** = no db has uploads enabled; **"Uploads are not enabled."** = the append/replace target isn't an uploaded table.
122
129
  - **card.** `dataset_query` is the **flat** `mbql/query` value, not a legacy `{type:"query",query:…}` envelope (→ `mbql`). `--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 `visualization`.
123
- - **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; negative ids for new cards). `create` accepts the **same** `dashcards` array in its initial body — lay out the whole dashboard in one call: negative ids for new cards, and `card_id:null` plus a `visualization_settings.virtual_card` block (`{display:"text"|"heading"|"link"|…}`) for non-question cards. `create`/`update` pre-flight every positive `card_id` and exit **2** with `{ok:false,errors:[…]}` on a bad ref (non-bypassable). `dashboard get <id>` (or `--full`) hydrates dashcards/tabs; `list` omits them. **The grid is 24 columns wide:** each dashcard's `{col, row, size_x, size_y}` is in grid units — **full-width is `size_x: 24`** (`size_x: 12` is half a row, the usual cause of a card filling only half the width). Keep `col + size_x ≤ 24`, start a full-width stack's `col` at 0, and don't overlap (the server stores collisions as sent — no auto-fix).
130
+ - **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; negative ids for new cards). `create` accepts the **same** `dashcards` array in its initial body — lay out the whole dashboard in one call: negative ids for new cards, and `card_id:null` plus a `visualization_settings.virtual_card` block (`{display:"text"|"heading"|"link"|…}`) for non-question cards. `create`/`update` pre-flight every positive `card_id` and exit **2** with `{ok:false,errors:[…]}` on a bad ref (non-bypassable). `dashboard get <id>` (or `--full`) hydrates dashcards/tabs; `list` omits them. **The grid is 24 columns wide:** each dashcard's `{col, row, size_x, size_y}` is in grid units — **full-width is `size_x: 24`** (`size_x: 12` is half a row, the usual cause of a card filling only half the width). Keep `col + size_x ≤ 24`, start a full-width stack's `col` at 0, and don't overlap (the server stores collisions as sent — no auto-fix). Layout patterns and per-chart default sizes → the `dashboard` skill; load it before composing any `dashcards` array.
124
131
  - **dashboard parameters (filters).** A dashboard's `parameters` array holds its filter widgets; they're part of the dashboard record, so read them with `dashboard get <id> --fields parameters --json` (no separate verb). **Editing replaces the _whole_ array** (like dashcards), so it's a read-modify-write loop: pull the current set with `dashboard get <id> --fields parameters --json`, add/change entries, and send the full array back via `dashboard update --body '{"parameters":[…]}'` (or supply it in `create`). Omitting a parameter deletes it. Each parameter is `{id, type, …}`. **`id` is a descriptive slug-like string you pick (e.g. `order_status`), unique within this dashboard — Metabase stores any non-blank string verbatim. Do NOT invent a random/opaque id by guessing; reuse the `slug`. If you genuinely need an opaque id, mint one with `mb uuid` — never fabricate one.** `type` is a **closed enum**; an unlisted value is a hard parse error that echoes the full allowed set back to you: string ops `string/=` `string/!=` `string/contains` `string/does-not-contain` `string/starts-with` `string/ends-with`; number ops `number/=` `number/!=` `number/between` `number/>=` `number/<=`; date `date/single` `date/range` `date/relative` `date/month-year` `date/quarter-year` `date/all-options`; plus `category`, `id`, `boolean/=`, `temporal-unit`, and bare `number`/`text`/`date`/`boolean`. A parameter only filters a card once it is **mapped**: each dashcard's `parameter_mappings` is `[{parameter_id, target}]` where `parameter_id` must match a parameter's `id` exactly, and `target` is `["dimension", ["field", <field-id>, null]]` for an MBQL card column, `["dimension", ["template-tag", "<tag>"]]` for a native field-filter tag, or `["variable", ["template-tag", "<tag>"]]` for a native raw-value tag. Populate a dropdown with `values_source_type`: `"static-list"` + `values_source_config.values`, or `"card"` + `{card_id, value_field, label_field}`; omit it to pull live distinct values from the mapped field. `dashboard parameter-values <id> <parameter-id> [--query <substr>]` fetches those selectable values (`{values, has_more_values}`); `--query` is a case-insensitive substring search.
125
132
  - **snippet `--archived` is a swap, not a union** — list returns _either_ active _or_ archived rows, never both. (Same for `--filter archived` on dashboard/collection.)
126
133
  - **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`): segment = a filter, measure = exactly one aggregation.
134
+ - **timeline / timeline-event.** Timelines are collection-scoped event annotations for time-series charts: a timeline's events render only on time-series questions saved in the **same collection** (`collection_id`; null = root) — sub-collections do **not** inherit, and events never draw on dashboard cards, only in the question (and collection) view. To annotate a question's chart, create the timeline in that question's collection, then add events; an event only draws when its `timestamp` falls inside the chart's displayed time range. Event `create` requires `timestamp` (ISO 8601), `timezone` (IANA name), `time_matters` (true = the time of day is significant, false = date-only), and `timeline_id` — the API never auto-creates a default timeline (that's UI-only). There is no `timeline-event list`; enumerate with `timeline events <id>` (`--archived` to include archived). Archiving a timeline cascades `archived` to its events; `delete` is a **hard** delete of the timeline and all its events — prefer `archive`.
127
135
  - **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). A transform collection needs `collection create --namespace transforms`.
128
136
  - **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 (wrapping bare-text responses into `{key, value}`).
129
137
  - **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.
130
138
  - **transform.** Iterate with `transform update <id>`, never `delete` + `create` (keeps the row, `entity_id`, materialized table, and YAML filename — avoids `_2` suffixes and noisy git history). `transform run` needs `--wait` (or `--sync`, which also waits for the output table to register and returns `target_table_id`) or you get only `{run_id, final:null}`. (→ `transform`.)
131
139
  - **setup is one-shot.** `mb setup` walks `/api/setup` for a **fresh** instance only — errors against an already-configured one. Mostly for bootstrapping local / e2e instances.
132
140
  - **eid** translates a string entity id → numeric id: `mb eid --model <model> <eid1,eid2> --json`. Entity ids are NanoIDs that can start with `-`, which the positional form misreads as a flag (shell quotes don't help) — for those, use `--body '{"entity_ids":{"card":["-…"]}}'` (the id is a JSON string value, immune to flag parsing).
133
- - **library.** EE-only (`library` premium feature, v59+). The Library is a curated subtree (`library-data` "Data" + `library-metrics` "Metrics" under a `library` root): tables published to **Data** appear first in data pickers and rank up in search; metrics saved to **Metrics** are prioritized in nav, search, and the query builder — it's how you tell people (and agents) "start from these, they're trusted." `library get` shows the Library and its Data/Metrics collection ids; `library create` provisions it (idempotent). `library publish --table-ids/--db-ids/--schemas` publishes tables into Data — it **resolves the Data collection itself and creates the Library if absent** (no collection id to find); each `--schemas` entry is `<db-id>:<schema>` (e.g. `1:public`), not a bare name. `publish` cascades to upstream FK dependencies, `unpublish` to downstream dependents; both need **admin or data-analyst** (Curate alone won't publish) and exit **403** without write **and** query permission on every affected table. Publish status shows on the table: `table get`/`table list` carry `is_published` (`collection_id` under `--full`). Good candidates are finished, analysis-ready tables — clean/combine via transforms first, then publish the polished result.
141
+ - **library.** EE-only (`library` premium feature, v59+). The Library is a curated subtree (`library-data` "Data" + `library-metrics` "Metrics" under a `library` root): tables published to **Data** appear first in data pickers and rank up in search; metrics saved to **Metrics** are prioritized in nav, search, and the query builder — it's how you tell people (and agents) "start from these, they're trusted." `library get` shows the Library and its Data/Metrics collection ids; `library create` provisions it (idempotent). `library publish --table-ids/--db-ids/--schemas` publishes tables into Data — it **resolves the Data collection itself and creates the Library if absent** (no collection id to find); each `--schemas` entry is `<db-id>:<schema>` (e.g. `1:public`), not a bare name. `publish` cascades to upstream FK dependencies, `unpublish` to downstream dependents; both need **admin or data-analyst** (Curate alone won't publish) and exit **403** without write **and** query permission on every affected table. Publish status shows on the table: `table get`/`table list` carry `is_published` (`collection_id` under `--full`). Good candidates are finished, analysis-ready tables — clean/combine via transforms first, then publish the polished result. Publishing does not put the Data collection in the git-sync scope: on a remote-sync instance, `mb git-sync add-collection <data-collection-id>` is what makes exports carry the published tables' metadata (see the `git-sync` skill).
134
142
  - **query / uuid.** `mb query` is the ad-hoc MBQL surface (`--print-schema` → `--dry-run` → run); `mb uuid --count <n>` mints the `lib/uuid` values MBQL clauses need. Both live in `mbql`.
135
143
 
136
144
  ## Specialized skills (load on demand)
@@ -1,17 +1,39 @@
1
1
  ---
2
2
  name: dashboard
3
- description: Build interactive Metabase dashboards via the `mb` CLI — turn cards into a filterable, cross-linked app. Covers wiring filters to cards (parameters + parameter_mappings), the field-filter vs. raw-variable target grammar, linked/cascading filters and their foreign-key requirement, cross-filtering (click a chart to filter the rest), click-through to a question/dashboard/URL, multi-series overlays, and tabs. Grid math, the parameter type enum, and whole-array replace semantics live in `core`. Triggers — "wire a filter to these cards", "make a filter cascade", "click a bar to filter the other charts", "add a dashboard tab", "add a second series", "make clicking a row open another dashboard", "why isn't my filter showing".
3
+ description: Build Metabase dashboards via the `mb` CLI — lay out dashcards on the 24-column grid (`{col,row,size_x,size_y}` math, per-chart default sizes) and turn cards into a filterable, cross-linked app. Covers wiring filters to cards (parameters + parameter_mappings), the field-filter vs. raw-variable target grammar, linked/cascading filters and their foreign-key requirement, cross-filtering, click-through, multi-series overlays, and tabs. The parameter type enum and whole-array replace semantics live in `core`. Triggers — "build a dashboard from these cards", "my dashboard is squished into half the width", "wire a filter to these cards", "make a filter cascade", "click a bar to filter the other charts", "add a dashboard tab", "add a second series", "why isn't my filter showing".
4
4
  allowed-tools: Read, Write, Edit, Bash, AskUserQuestion
5
5
  ---
6
6
 
7
7
  # Dashboard
8
8
 
9
- A dashboard starts as cards on a grid; it becomes an **app** when filters drive the cards, charts cross-filter each other, and clicks navigate. This skill is that interactive layer.
9
+ A dashboard starts as cards on a grid; it becomes an **app** when filters drive the cards, charts cross-filter each other, and clicks navigate. This skill owns both: the grid layout and the interactive layer.
10
10
 
11
- **`core` owns the mechanics** — the 24-column grid and `{col,row,size_x,size_y}` math, the whole-array replace semantics (editing `dashcards` or `parameters` replaces the entire set; omitted entries are deleted; new cards use negative ids), `update-dashcard` for a single safe patch vs. `update --body` for a full replace, the closed `parameter.type` enum, and the `parameter-values` verb. Read it first (`mb skills get core`). **`visualization`** owns each card's chart and the full `click_behavior` key catalog. This file assumes both and focuses on wiring.
11
+ **`core` owns the transport mechanics** — the whole-array replace semantics (editing `dashcards` or `parameters` replaces the entire set; omitted entries are deleted; new cards use negative ids), `update-dashcard` for a single safe patch vs. `update --body` for a full replace, the closed `parameter.type` enum, and the `parameter-values` verb. Read it first (`mb skills get core`). **`visualization`** owns each card's chart and the full `click_behavior` key catalog.
12
12
 
13
13
  Inspect before you wire: `mb dashboard get <id> --json` hydrates `parameters`, `dashcards`, and `tabs`; `mb dashboard cards <id>` lists just the dashcards.
14
14
 
15
+ ## Layout: the grid is 24 columns — not 12
16
+
17
+ Every dashcard carries `{col, row, size_x, size_y}` in grid units: `col` is 0-indexed from the left edge, `row` grows downward, and `col + size_x ≤ 24`. **Full-width is `size_x: 24` — Metabase's per-chart _default_ width of 12 is half a row.** A layout authored on the usual 12-column web-grid assumption crams the whole dashboard into the left half of the viewport. The server stores whatever geometry you send — overlaps and gaps included, no auto-fix.
18
+
19
+ Default sizes (w×h): `scalar`/`smartscalar` 6×3, `pie` 12×8, `table`/`pivot`/`object` 12×9, `waterfall` 14×6, `sankey` 16×10, `heading` 24×1, `text` 12×3, every other chart 12×6.
20
+
21
+ The standard shape — a KPI row of scalars across the full 24, charts in halves or thirds below, wide tables full-width:
22
+
23
+ ```jsonc
24
+ "dashcards": [
25
+ { "id": -1, "card_id": 101, "col": 0, "row": 0, "size_x": 6, "size_y": 3 }, // 4 KPIs × 6 = 24
26
+ { "id": -2, "card_id": 102, "col": 6, "row": 0, "size_x": 6, "size_y": 3 },
27
+ { "id": -3, "card_id": 103, "col": 12, "row": 0, "size_x": 6, "size_y": 3 },
28
+ { "id": -4, "card_id": 104, "col": 18, "row": 0, "size_x": 6, "size_y": 3 },
29
+ { "id": -5, "card_id": 105, "col": 0, "row": 3, "size_x": 12, "size_y": 6 }, // two halves
30
+ { "id": -6, "card_id": 106, "col": 12, "row": 3, "size_x": 12, "size_y": 6 },
31
+ { "id": -7, "card_id": 107, "col": 0, "row": 9, "size_x": 24, "size_y": 9 } // full-width table
32
+ ]
33
+ ```
34
+
35
+ **Sanity-check before sending:** rows should fill to 24 and at least one card must end at `col + size_x = 24`. If nothing in the array crosses column 12, you've authored a 12-column layout — double every width.
36
+
15
37
  ## The wiring loop: a filter is a parameter + a mapping per card
16
38
 
17
39
  A dashboard filter is one entry in the dashboard's `parameters` array **plus** a `parameter_mappings` entry on every dashcard it should control. A parameter with no mapping is an inert widget — the most common "my filter does nothing" cause.
@@ -101,6 +123,7 @@ Set the **driver** chart's whole-card click behavior to `crossfilter`, mapping t
101
123
 
102
124
  ## Don't
103
125
 
126
+ - Don't lay out on a 12-column assumption — full-width is `size_x: 24`; a layout where no card crosses column 12 renders in the left half of the viewport.
104
127
  - Don't declare a parameter and forget the per-card `parameter_mappings` — the widget won't filter anything.
105
128
  - Don't expect a linked filter to work off a model/question join, a custom column, or with a static/card value source — it needs a metadata FK and a live source.
106
129
  - Don't hand-author a complex `click_behavior` — copy a UI-built one.
@@ -10,14 +10,14 @@ The front door for turning a raw database into clean tables, reusable definition
10
10
 
11
11
  A data project moves through stages. A user can start at any of them — detect where their data already is, don't assume.
12
12
 
13
- | Stage | What it does | Where the method lives |
14
- | --------------------------- | --------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
15
- | **Build clean tables** | Raw, normalized source DB → a small set of wide, clean, analysis-ready tables (built as transforms) | `references/building-clean-tables.md` |
16
- | **Define reusable metrics** | Clean tables → shared segments (saved filters), measures (saved calculations), metrics (official numbers) | `references/reusable-definitions.md` |
17
- | **Answer questions** | Clean tables → a trustworthy plain-language written answer | `references/answering-questions.md` |
18
- | **Build dashboards** | Clean tables / definitions → charts and dashboards people look at | the `visualization` skill (charts) + `dashboard` skill (wiring filters, interactivity) |
13
+ | Stage | What it does | Where the method lives |
14
+ | --------------------------- | --------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
15
+ | **Build clean tables** | Raw, normalized source DB → a small set of wide, clean, analysis-ready tables (built as transforms) | `references/building-clean-tables.md` |
16
+ | **Define reusable metrics** | Clean tables → shared segments (saved filters), measures (saved calculations), metrics (official numbers) | `references/reusable-definitions.md` |
17
+ | **Answer questions** | Clean tables → a trustworthy plain-language written answer | `references/answering-questions.md` |
18
+ | **Build dashboards** | Clean tables / definitions → charts and dashboards people look at | the `visualization` skill (charts) + `dashboard` skill (grid layout, filters, interactivity) |
19
19
 
20
- The first three methods are references in this skill — read **only the one the current stage needs**: run `mb skills path data-workflow` and Read `references/<file>.md`. (`mb skills get data-workflow --full` appends all three at once — heavier; prefer the single Read.) "Build dashboards" lives in two standalone skills — `visualization` for authoring each chart, `dashboard` for wiring filters and interactivity across the dashboard — both CLI capabilities in their own right.
20
+ The first three methods are references in this skill — read **only the one the current stage needs**: run `mb skills path data-workflow` and Read `references/<file>.md`. (`mb skills get data-workflow --full` appends all three at once — heavier; prefer the single Read.) "Build dashboards" lives in two standalone skills — `visualization` for authoring each chart, `dashboard` for laying cards out on the grid and wiring filters and interactivity — both CLI capabilities in their own right. Load `dashboard` before composing any dashboard, even a plain no-filter layout: the grid geometry lives there.
21
21
 
22
22
  CLI mechanics come from the reference skills, not from here: `mb skills get core` (auth, inspection, the `field`/`table`/`library`/`segment`/`measure` verbs), `mbql` (query and definition bodies), `transform` (creating/running transforms). This skill owns the _judgment_ — which tables, which definitions, what to keep — and the conventions below.
23
23
 
@@ -88,19 +88,21 @@ The rules every stage follows. The reference files assume this contract rather t
88
88
 
89
89
  Don't make the user name a _stage_ — but do find out _where their data lives_ before going looking.
90
90
 
91
+ **Data not in a database yet?** A local CSV gets in via `mb upload csv --file <path>` (creates a table + model); treat the result as a starting table and clean it with a transform if needed. Uploads must be admin-enabled — check `mb setting get uploads-settings --json` (`db_id: null` ⇒ not configured). See `core`'s **upload** quirk for the rest.
92
+
91
93
  **Ask before you crawl.** If you don't already know which database/schema/table the user means, ask — one plain question short-circuits a dozen tool calls. The asymmetry: if they name a **database**, ask which **schema**; if they name a **table**, ask which **database**. "If you don't know, no problem — I'll look" is the fallback, not the opener.
92
94
 
93
- **When you do crawl,** use `core`'s cheap, narrowest-first ladder (never whole-warehouse rollups): `mb db list` → `db schemas <id>` → `db schema-tables <id> <schema>` → `table list [--db-id]` → `table fields <id>` (or `table metadata <id>` for FK targets and dimensions — heavier). Have a _name_ rather than a tree to walk? `mb search <query> [--models] [--db-id]`. Need to know what's in a column? `mb field summary <id>` (counts) and `field values <id>` (sample values). If a database looks freshly connected or an expected table is missing, offer `mb db sync-schema <id> --wait` before concluding it doesn't exist.
95
+ **When you do crawl,** use `core`'s cheap, narrowest-first ladder (never whole-warehouse rollups): `mb db list` → `db schemas <id>` → `db schema-tables <id> <schema>` → `table list [--db-id]` → `table fields <id>` (or `table get <id> --include fields --full` for FK targets and dimensions). Have a _name_ rather than a tree to walk? `mb search <query> [--models] [--db-id]`. Need to know what's in a column? `mb field summary <id>` (counts) and `field values <id>` (sample values). If a database looks freshly connected or an expected table is missing, offer `mb db sync-schema <id> --wait` before concluding it doesn't exist.
94
96
 
95
97
  **Read the shape to pick a stage.** Raw, normalized, SaaS-synced tables (many tables, coded columns, `*_field`/`*_choice` lookups)? → **build clean tables** first. Already wide, clean, human-readable ones? Then it depends on the goal:
96
98
 
97
- | What the user wants / what's there | Stage |
98
- | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
99
- | "Clean up / flatten / make sense of" raw, normalized data; no clean tables yet | Build clean tables → `references/building-clean-tables.md` |
100
- | Clean tables exist; "make this reusable", "define active customers / revenue / MRR officially" | Define reusable metrics → `references/reusable-definitions.md` |
101
- | Clean tables exist; "answer this question", "who registered", "analyze / report on X" (wants a written answer) | Answer questions → `references/answering-questions.md` |
102
- | Clean tables (and maybe definitions) exist; "chart this", "build a dashboard", "show me X over time" | Build dashboards → `visualization` (charts) + `dashboard` (interactivity) |
103
- | "Do the whole thing" / "set up analytics for X" from raw data | Start at build-clean-tables, then continue down the stages |
99
+ | What the user wants / what's there | Stage |
100
+ | -------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
101
+ | "Clean up / flatten / make sense of" raw, normalized data; no clean tables yet | Build clean tables → `references/building-clean-tables.md` |
102
+ | Clean tables exist; "make this reusable", "define active customers / revenue / MRR officially" | Define reusable metrics → `references/reusable-definitions.md` |
103
+ | Clean tables exist; "answer this question", "who registered", "analyze / report on X" (wants a written answer) | Answer questions → `references/answering-questions.md` |
104
+ | Clean tables (and maybe definitions) exist; "chart this", "build a dashboard", "show me X over time" | Build dashboards → `visualization` (charts) + `dashboard` (layout, interactivity) |
105
+ | "Do the whole thing" / "set up analytics for X" from raw data | Start at build-clean-tables, then continue down the stages |
104
106
 
105
107
  **Answer and dashboard are alternative endpoints, not a sequence.** Once tables are clean (and maybe defined), answering in prose and building a dashboard are two different things you can do with the data — route to whichever the goal calls for; neither has to precede the other.
106
108
 
@@ -1,12 +1,14 @@
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, 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 …`.
3
+ description: Round-trip Metabase content (cards, dashboards, transforms, snippets, collections, Library-published table/field metadata) 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", "push my changes to the repo", "open a PR with my Metabase changes", "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
 
7
7
  # git-sync (representations ↔ instance)
8
8
 
9
- Metabase content (cards, dashboards, transforms, snippets, collections, …) can live in a git repo as YAML and round-trip in and out of a Metabase instance via the `git-sync` verbs. The instance is configured with a `remote-sync-*` settings block (URL, branch, token, type read-only/read-write); the CLI drives the sync tasks against `/api/ee/remote-sync/*`. Only collections flagged for sync serialize; everything else is local-only.
9
+ Metabase content (cards, dashboards, transforms, snippets, collections, …) can live in a git repo as YAML and round-trip in and out of a Metabase instance via the `git-sync` verbs. The instance is configured with a `remote-sync-*` settings block (URL, branch, token, type read-only/read-write); the CLI drives the sync tasks against `/api/ee/remote-sync/*`. Only collections flagged for sync serialize; everything else is local-only. Table and field metadata round-trips too, for Library-published tables in a flagged collection — see "Published table metadata" below.
10
+
11
+ The repo and the instance are two ends of the same state; what "push my changes" means depends on where the changes were made. If they were made **in the instance** (CLI verbs against a read-write instance — cards edited, transforms created, tables published, metadata written), "push my changes" / "save this to the repo" / "open a PR with these changes" means driving the instance's export to a branch (`stash` or `create-branch` + `export`) and opening the PR from that branch — not reconstructing the changes as files. If the workflow is **repo-first** (content YAML edited in the repo, the instance imports it), plain `git` against the repo is the way — but only in the serialized layout already there: files in paths or formats the serializer doesn't own are invisible to Metabase and never apply on import.
10
12
 
11
13
  This skill covers the import/export workflow. Flag conventions and auth setup live in `core` (`mb skills get core`). To author content YAML by hand: the per-resource clause and settings shapes mirror the API form — query bodies follow the `mbql` skill, `visualization_settings` follow the `visualization` skill — except the portable YAML uses **name-based** references (e.g. `[Sample Database, PUBLIC, ORDERS, TOTAL]`, and entity-ids for cross-entity FKs) where the API form uses numeric ids. For the on-disk folder layout, model new files on what the synced repo already contains.
12
14
 
@@ -126,13 +128,26 @@ mb setting set remote-sync-type '"read-write"' --profile <n>
126
128
 
127
129
  (`setting set` parses the value as strict JSON — mind the inner double quotes; see `core`.) The server also rejects switching to `:read-only` while the Remote Sync collection is dirty; export or `--force` import first if you're going the other way.
128
130
 
129
- **Verifying the result.** The CLI's `Collection` schema doesn't yet expose `is_remote_synced`, so `collection get --json` won't show the flag. The pragmatic confirmation paths are:
131
+ **Verifying the result.** `mb git-sync status --profile <n> --json` lists the flagged collections under `synced_collections`, and `mb collection get <id> --json` shows the per-collection `is_remote_synced` flag.
132
+
133
+ ## Published table metadata (Library) and sync scope
134
+
135
+ Table and field metadata — table/field descriptions, semantic types (`type/PK`, `type/FK`), FK targets, plus segments and measures on the table — serializes for **Library-published tables only**, under `databases/<db>/schemas/<schema>/tables/<table>/…` in the repo. Eligibility is two-gated: the table must be published (`mb library publish`), **and** the Library collection holding it must itself carry `is_remote_synced: true`. An ordinary warehouse table, or a transform's target table that isn't published, never serializes — a transform's YAML carries only the transform definition (query, target, description), not the output table's field metadata.
136
+
137
+ The classic trap: publish tables, write field metadata, then `git-sync dirty` comes back empty and nothing lands in the repo. That does not mean git-sync can't carry table metadata — it means the Library collection isn't in the sync scope, so nothing was eligible for dirty-tracking when the writes happened. Check the scope (`synced_collections` in `status`), then:
138
+
139
+ ```bash
140
+ mb library get --profile <n> --json # Library Data collection id
141
+ mb git-sync add-collection <library-data-id> --profile <n>
142
+ mb git-sync stash --new-branch <branch> -m "..." --profile <n> # or create-branch + export
143
+ ```
130
144
 
131
- - `mb git-sync is-dirty --profile <n> --json` after editing a card in the now-synced collection — a `true` reading proves it's tracked.
132
- - The Metabase Admin UI's Remote Sync page renders the per-collection toggles.
145
+ Flagging the collection records it for the next export, which serializes its current content — including already-published tables and their field metadata. `mb library publish` prints a reminder when the target collection is outside the sync scope on an instance with a configured remote.
133
146
 
134
147
  ## Don't (git-sync-specific)
135
148
 
149
+ - Don't turn instance-side changes into hand-written repo files. When the changes were made against the instance, export them (`stash` / `create-branch` + `export`) and PR the exported branch; reconstructing them as YAML by hand — or pushing files in paths/formats the serializer doesn't own — produces content that never applies on import, and pushing behind Metabase's back races its own sync tasks. Hand-editing YAML belongs to the repo-first workflow, in the serialized layout the repo already uses.
150
+ - Don't conclude from an empty `dirty` list that a change type isn't tracked. Dirty-tracking only records changes to _eligible_ objects; the usual cause is scope (the collection isn't flagged — see "Published table metadata"), not capability. Check `synced_collections` in `status` before concluding.
136
151
  - Don't run `git-sync import --force` or `git-sync export --force` without explicit user confirmation. Both are lossy — `--force` import discards instance-side work, `--force` export overwrites the remote branch.
137
152
  - 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`.
138
153
  - 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.
@@ -158,3 +158,4 @@ mb skills path visualization # → the skill dir; then Read references
158
158
  - 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.
159
159
  - Don't expect a pre-flight to catch viz mistakes — there is none. Verify by reading the card back.
160
160
  - Don't hand-author complex charts when you can pull a working `visualization_settings` from a UI-built card.
161
+ - Don't look for an event/annotation key in `visualization_settings` — vertical event markers on time-series charts come from timelines (`mb timeline` / `mb timeline-event`) living in the question's own collection (same collection only, no sub-collection inheritance; question view only, never dashboard cards).
@@ -1,11 +0,0 @@
1
- import "./notice-DyVl5aYB.mjs";
2
- import "./error-BaBm-UrT.mjs";
3
- import "./command-augment-DdZIfx1V.mjs";
4
- import "./runtime-oxjmrYoP.mjs";
5
- import "./capabilities-N0jo5U7S.mjs";
6
- import "./parse-id-DlXnOcmP.mjs";
7
- import "./poll-hgnrHBoh.mjs";
8
- import "./poll-task-NQNLT_aA.mjs";
9
- import { SyncSettingsUpdateResult, add_collection_default, setCollectionRemoteSynced, syncSettingsUpdateView } from "./add-collection-CRZAFCZy.mjs";
10
-
11
- export { add_collection_default as default };
@@ -1,22 +0,0 @@
1
- import "./command-augment-DdZIfx1V.mjs";
2
- import { defineCommandGroup } from "./group-BNE_RiH5.mjs";
3
-
4
- //#region src/commands/auth/index.ts
5
- var auth_default = defineCommandGroup({
6
- name: "auth",
7
- description: "Authenticate against a Metabase instance",
8
- defaultCommand: "login",
9
- skills: [{
10
- skill: "core",
11
- purpose: "authentication and named profiles"
12
- }],
13
- subCommands: {
14
- login: () => import("./login-C6ZAnGHz.mjs").then((m) => m.default),
15
- status: () => import("./status-C_7aqTvB.mjs").then((m) => m.default),
16
- list: () => import("./list-CjF12k1G.mjs").then((m) => m.default),
17
- logout: () => import("./logout-CWjyY3Y8.mjs").then((m) => m.default)
18
- }
19
- });
20
-
21
- //#endregion
22
- export { auth_default as default };
@@ -1,239 +0,0 @@
1
- import { ConfigError, itemOversizeMessage, listTruncationNotice, warn } from "./notice-DyVl5aYB.mjs";
2
- import { z } from "zod";
3
- import Table from "cli-table3";
4
-
5
- //#region src/runtime/predicates.ts
6
- function isPlainObject(value) {
7
- return typeof value === "object" && value !== null && !Array.isArray(value);
8
- }
9
-
10
- //#endregion
11
- //#region src/output/cap.ts
12
- function capListEnvelope(envelope, maxBytes) {
13
- if (maxBytes <= 0) return envelope;
14
- const fullBytes = jsonByteLength(envelope);
15
- if (fullBytes <= maxBytes) return envelope;
16
- let lo = 0;
17
- let hi = envelope.data.length;
18
- while (lo < hi) {
19
- const mid = Math.ceil((lo + hi) / 2);
20
- if (jsonByteLength(truncate(envelope, mid, fullBytes)) <= maxBytes) lo = mid;
21
- else hi = mid - 1;
22
- }
23
- return truncate(envelope, lo, fullBytes);
24
- }
25
- function truncate(envelope, count, originalBytes) {
26
- return {
27
- ...envelope,
28
- data: envelope.data.slice(0, count),
29
- returned: count,
30
- truncated: {
31
- reason: "max_bytes",
32
- bytes: originalBytes
33
- }
34
- };
35
- }
36
- function jsonByteLength(value) {
37
- return Buffer.byteLength(JSON.stringify(value), "utf8");
38
- }
39
-
40
- //#endregion
41
- //#region src/output/projection.ts
42
- function applyProjection(value, view, full, fields) {
43
- if (fields !== void 0) {
44
- if (fields.length === 0) throw new ConfigError("--fields requires at least one path");
45
- return projectFields(value, fields);
46
- }
47
- if (full) return value;
48
- const parsed = view.compactPick.safeParse(value);
49
- if (parsed.success) return parsed.data;
50
- throw new ConfigError(`compact projection failed: ${parsed.error.message}`);
51
- }
52
- function projectFields(value, fields) {
53
- const out = {};
54
- for (const path of fields) {
55
- if (path.length === 0) throw new ConfigError(`empty field path`);
56
- const parts = path.split(".");
57
- if (parts.some((part) => part.length === 0)) throw new ConfigError(`invalid field path: "${path}"`);
58
- setPath(out, parts, pickPath(value, parts));
59
- }
60
- return out;
61
- }
62
- function pickPath(value, parts) {
63
- let cursor = value;
64
- for (const part of parts) {
65
- if (!isPlainObject(cursor) || !Object.hasOwn(cursor, part)) throw new ConfigError(`unknown field path: "${parts.join(".")}"`);
66
- cursor = Reflect.get(cursor, part);
67
- }
68
- return cursor;
69
- }
70
- function setPath(target, parts, value) {
71
- let cursor = target;
72
- const lastIndex = parts.length - 1;
73
- for (const [index, part] of parts.entries()) {
74
- if (index === lastIndex) {
75
- cursor[part] = value;
76
- return;
77
- }
78
- const existing = cursor[part];
79
- if (isPlainObject(existing)) cursor = existing;
80
- else {
81
- const next = {};
82
- cursor[part] = next;
83
- cursor = next;
84
- }
85
- }
86
- }
87
-
88
- //#endregion
89
- //#region src/output/table.ts
90
- const PLAIN_TABLE_STYLE = {
91
- head: [],
92
- border: []
93
- };
94
- function renderTable(rows, columns) {
95
- const head = columns.map((column) => column.label ?? column.key);
96
- const widths = columns.map((column) => column.width ?? null);
97
- const hasWidth = widths.some((width) => width !== null);
98
- const table = new Table(hasWidth ? {
99
- head,
100
- colWidths: widths,
101
- style: PLAIN_TABLE_STYLE
102
- } : {
103
- head,
104
- style: PLAIN_TABLE_STYLE
105
- });
106
- for (const row of rows) table.push(columns.map((column) => formatCell(row, column)));
107
- return table.toString();
108
- }
109
- function renderRows(head, rows) {
110
- const table = new Table({
111
- head: [...head],
112
- style: PLAIN_TABLE_STYLE
113
- });
114
- for (const row of rows) table.push([...row]);
115
- return table.toString();
116
- }
117
- function formatCell(row, column) {
118
- const value = row[column.key];
119
- if (column.format !== void 0) return column.format(value);
120
- return formatScalar(value);
121
- }
122
- function formatScalar(value) {
123
- if (value === null || value === void 0) return "";
124
- if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return String(value);
125
- return JSON.stringify(value);
126
- }
127
-
128
- //#endregion
129
- //#region src/output/render.ts
130
- function writeJson(value) {
131
- process.stdout.write(JSON.stringify(value, null, 2) + "\n");
132
- }
133
- function writeText(text) {
134
- process.stdout.write(text + "\n");
135
- }
136
- function renderItem(item, view, opts) {
137
- const projected = applyProjection(item, view, opts.full, opts.fields);
138
- const body = renderItemBody(item, view, projected, opts) + "\n";
139
- assertItemWithinMaxBytes(body, opts.maxBytes);
140
- process.stdout.write(body);
141
- }
142
- function renderSummary(item, view, summaryText, opts) {
143
- if (opts.format === "json" || opts.fields !== void 0 || opts.full) {
144
- renderItem(item, view, opts);
145
- return;
146
- }
147
- const body = (typeof summaryText === "function" ? summaryText() : summaryText) + "\n";
148
- assertItemWithinMaxBytes(body, opts.maxBytes);
149
- process.stdout.write(body);
150
- }
151
- function renderList(envelope, view, opts) {
152
- if (opts.format === "json") {
153
- renderJsonEnvelope(envelope, view, opts);
154
- return;
155
- }
156
- if (envelope.data.length === 0) {
157
- process.stdout.write("(no results)\n");
158
- return;
159
- }
160
- if (opts.fields !== void 0) {
161
- renderProjectedTable(envelope, view, opts.fields, opts.maxBytes);
162
- return;
163
- }
164
- const capped = capListEnvelope(envelope, opts.maxBytes);
165
- process.stdout.write(renderTable(capped.data, view.tableColumns) + "\n");
166
- if (capped.truncated !== void 0) warn(listTruncationNotice(capped.truncated.bytes));
167
- }
168
- function renderProjectedTable(envelope, view, fields, maxBytes) {
169
- const projectedItems = projectListItems(envelope.data, view, false, fields);
170
- const capped = capListEnvelope({
171
- ...envelope,
172
- data: projectedItems
173
- }, maxBytes);
174
- const rows = capped.data.map((item) => fields.map((path) => formatScalar(pickPath(item, path.split(".")))));
175
- process.stdout.write(renderRows(fields, rows) + "\n");
176
- if (capped.truncated !== void 0) warn(listTruncationNotice(capped.truncated.bytes));
177
- }
178
- function projectListItems(items, view, full, fields) {
179
- try {
180
- return items.map((item) => applyProjection(item, view, full, fields));
181
- } catch (error) {
182
- throw enrichListFieldPathError(error, fields);
183
- }
184
- }
185
- function enrichListFieldPathError(error, fields) {
186
- if (fields === void 0 || !(error instanceof ConfigError) || !error.message.startsWith("unknown field path")) return error;
187
- const prefix = "data.";
188
- const culprit = fields.find((field) => field === "data" || field.startsWith(prefix));
189
- if (culprit === void 0) return error;
190
- const suggestion = culprit.startsWith(prefix) ? culprit.slice(prefix.length) : "<field>";
191
- return new ConfigError(`${error.message} — on list commands --fields paths are relative to each item in \`data\`, not the envelope. Drop the \`data.\` prefix (e.g. use \`${suggestion}\` instead of \`${culprit}\`).`);
192
- }
193
- function renderJsonEnvelope(envelope, view, opts) {
194
- const projectedItems = projectListItems(envelope.data, view, opts.full, opts.fields);
195
- const projectedEnvelope = {
196
- ...envelope,
197
- data: projectedItems
198
- };
199
- const capped = capListEnvelope(projectedEnvelope, opts.maxBytes);
200
- process.stdout.write(JSON.stringify(capped, null, 2) + "\n");
201
- if (capped.truncated !== void 0) warn(listTruncationNotice(capped.truncated.bytes));
202
- }
203
- function renderItemBody(item, view, projected, opts) {
204
- if (opts.format === "json") return JSON.stringify(projected, null, 2);
205
- if (opts.fields !== void 0 || opts.full) return renderKeyValueLines(objectPairs(projected));
206
- return renderKeyValueLines(columnPairs(item, view.tableColumns));
207
- }
208
- function columnPairs(item, columns) {
209
- return columns.map((column) => [column.label ?? column.key, formatCell(item, column)]);
210
- }
211
- function objectPairs(value) {
212
- if (!isPlainObject(value)) {
213
- const scalar = formatScalar(value);
214
- return scalar === "" ? [] : [["", scalar]];
215
- }
216
- return Object.entries(value).map(([key, raw]) => [key, formatScalar(raw)]);
217
- }
218
- function renderKeyValueLines(pairs) {
219
- if (pairs.length === 0) return "";
220
- const padding = Math.max(...pairs.map(([label]) => label.length));
221
- return pairs.map(([label, value]) => `${label.padEnd(padding)} ${value}`).join("\n");
222
- }
223
- function assertItemWithinMaxBytes(body, maxBytes) {
224
- if (maxBytes <= 0) return;
225
- const bytes = Buffer.byteLength(body, "utf8");
226
- if (bytes <= maxBytes) return;
227
- throw new ConfigError(itemOversizeMessage(bytes, maxBytes));
228
- }
229
-
230
- //#endregion
231
- //#region src/runtime/capabilities.ts
232
- const Capabilities = z.object({
233
- minVersion: z.number(),
234
- tokenFeature: z.string().optional()
235
- });
236
- const BASELINE_CAPABILITIES = Object.freeze({ minVersion: 58 });
237
-
238
- //#endregion
239
- export { BASELINE_CAPABILITIES, Capabilities, formatScalar, isPlainObject, renderItem, renderList, renderRows, renderSummary, writeJson, writeText };
@@ -1,26 +0,0 @@
1
- import "./command-augment-DdZIfx1V.mjs";
2
- import { defineCommandGroup } from "./group-BNE_RiH5.mjs";
3
-
4
- //#region src/commands/card/index.ts
5
- var card_default = defineCommandGroup({
6
- name: "card",
7
- description: "Manage Metabase cards (questions, models, metrics)",
8
- skills: [{
9
- skill: "mbql",
10
- purpose: "author the dataset_query"
11
- }, {
12
- skill: "visualization",
13
- purpose: "choose display and visualization_settings"
14
- }],
15
- subCommands: {
16
- list: () => import("./list-D-rgDFa5.mjs").then((mod) => mod.default),
17
- get: () => import("./get-Dz7fcqoG.mjs").then((mod) => mod.default),
18
- query: () => import("./query-BQfgKV68.mjs").then((mod) => mod.default),
19
- create: () => import("./create-DhrftYre.mjs").then((mod) => mod.default),
20
- update: () => import("./update-CrRz47aj.mjs").then((mod) => mod.default),
21
- archive: () => import("./archive-Z8ZM-ilB.mjs").then((mod) => mod.default)
22
- }
23
- });
24
-
25
- //#endregion
26
- export { card_default as default };