@metabase/cli 0.3.2-alpha.fix-transform-test-ee-path.8ac2a7a → 0.4.0
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.
- package/dist/{add-collection-B_-eMU_k.mjs → add-collection-CC6KSwjX.mjs} +5 -5
- package/dist/{alert-Dalin_DD.mjs → alert-Dt6La9tK.mjs} +7 -7
- package/dist/{alerts-DpBgrjPI.mjs → alerts-ClKnJqgB.mjs} +6 -6
- package/dist/{append-C1JNVALm.mjs → append-pnaQWP_A.mjs} +4 -4
- package/dist/{archive-B51prFgS.mjs → archive-By1pLYtO.mjs} +3 -3
- package/dist/{archive-CwITzj_j.mjs → archive-CLu2TGW5.mjs} +5 -5
- package/dist/{archive-52ZHbFDm2.mjs → archive-COP9joWU.mjs} +4 -4
- package/dist/{archive-cAdUa1-2.mjs → archive-CSnBFcQ_.mjs} +3 -3
- package/dist/{archive-DA3OiLEE.mjs → archive-Ct1eyolN.mjs} +3 -3
- package/dist/{archive-CdKf_Eqq.mjs → archive-D7nAQcCz.mjs} +3 -3
- package/dist/{archive-CISXgZAI.mjs → archive-DEDA48w3.mjs} +5 -5
- package/dist/{archive-DVHxeDKS.mjs → archive-DJAsSzCz.mjs} +5 -5
- package/dist/{archive-BkITQyWW.mjs → archive-DtabbPUk.mjs} +4 -4
- package/dist/{archive-DckxlWBt.mjs → archive-YNpa8daf.mjs} +3 -3
- package/dist/{archive-CicPYX27.mjs → archive-ewitrKdx.mjs} +5 -5
- package/dist/{auth-A96JOCQx.mjs → auth-DdLhXCn-.mjs} +5 -5
- package/dist/body-Dd-TwYYL.mjs +17 -0
- package/dist/branch-flag-pFQPNYKr.mjs +9 -0
- package/dist/{branches-CbcwWYL9.mjs → branches-ZvGNhed6.mjs} +3 -3
- package/dist/breaking-BLswgvkA.mjs +36 -0
- package/dist/broken-BN0nL2hf.mjs +37 -0
- package/dist/bulk-discard-values-DFJUjSnE.mjs +49 -0
- package/dist/bulk-edit-Brqr67bu.mjs +53 -0
- package/dist/bulk-rescan-values-DndXXi2N.mjs +28 -0
- package/dist/bulk-sync-schema-mO-t2QlL.mjs +28 -0
- package/dist/{cancel-DpDcVY7M.mjs → cancel-DP03dXBf.mjs} +3 -3
- package/dist/{cancel-task-B4RDvIYb.mjs → cancel-task-CELBfBPP.mjs} +4 -4
- package/dist/{card-voh1k4hl.mjs → card-Bv4ZN0oV.mjs} +5 -3
- package/dist/card-CFd-GUEM.mjs +25 -0
- package/dist/{card-CEkp1mcI.mjs → card-DSrgU7lT.mjs} +1 -1
- package/dist/{cards-BC0JX8Yg.mjs → cards-Cn2ywFTJ.mjs} +6 -6
- package/dist/cli.mjs +410 -111
- package/dist/{client-ClL6gf7G.mjs → client-ToLiPu9q.mjs} +785 -316
- package/dist/{collection-DYM0p3Xl.mjs → collection-BG3Ksea-.mjs} +1 -1
- package/dist/collection-BKNShKcY.mjs +21 -0
- package/dist/{collection-BxdPxD38.mjs → collection-D3KXhvnc.mjs} +7 -5
- package/dist/{collection-namespace-Dv0l-V9K.mjs → collection-namespace-CWW6hZxK.mjs} +2 -2
- package/dist/{content-translation-BXIXL4eW.mjs → content-translation-C19SqZWp.mjs} +3 -3
- package/dist/copy-BGK8Wz6d.mjs +55 -0
- package/dist/copy-COLUuiuF.mjs +36 -0
- package/dist/copy-flags-eU8aa0zj.mjs +32 -0
- package/dist/{create-BbjSOTN-.mjs → create-B2tuInV9.mjs} +4 -4
- package/dist/{create-Za1EskpV.mjs → create-B3TKri1E.mjs} +4 -4
- package/dist/{create--fiwuTWc.mjs → create-B4gXKDRr.mjs} +3 -3
- package/dist/create-B63JphYe.mjs +34 -0
- package/dist/{create-NCKHHdov.mjs → create-BH6kLjHS.mjs} +4 -4
- package/dist/{create-Dhai9zAt.mjs → create-BwifyhyL.mjs} +3 -3
- package/dist/{create-DWFJdyIr.mjs → create-Colz5bOO.mjs} +3 -3
- package/dist/{create-BcsVwXgS2.mjs → create-CulUZgN1.mjs} +4 -4
- package/dist/{create-D--Z1S3p2.mjs → create-Cxuqr3kT.mjs} +6 -6
- package/dist/{create-Bryrkb9g.mjs → create-CzW_UTE7.mjs} +6 -6
- package/dist/{create-kZIIEN2Y.mjs → create-DNR8RMvV.mjs} +6 -6
- package/dist/{create-DmN2gFc4.mjs → create-DSHeoV7e.mjs} +4 -4
- package/dist/{create-BVNhtAgF.mjs → create-DWbW2WSc.mjs} +6 -6
- package/dist/{create-CHr8MPPq.mjs → create-Dx8nfjGs.mjs} +3 -3
- package/dist/{create-B9qXQ_wV.mjs → create-XGomrTz5.mjs} +5 -5
- package/dist/{create-branch-CseVn3lp.mjs → create-branch-DxL7KUZp.mjs} +4 -4
- package/dist/{create-YetX69Re.mjs → create-g1f2yJ4s.mjs} +3 -3
- package/dist/{csv-DNzesu8y.mjs → csv-C6TEfLMi.mjs} +4 -4
- package/dist/{current-task-DRTKZcP9.mjs → current-task-CHs0zNFM.mjs} +4 -4
- package/dist/dashboard-AWrXqQ7a.mjs +35 -0
- package/dist/{dashboard-D0hwTC-f.mjs → dashboard-Phz3XpRM.mjs} +2 -1
- package/dist/{dashboard-DgK81azj.mjs → dashboard-XSTr9QCr.mjs} +6 -2
- package/dist/database-D-CRyRIv.mjs +45 -0
- package/dist/{database-C3E9PZSx.mjs → database-MlUPpitM.mjs} +1 -1
- package/dist/dataset-p5oNPQgL.mjs +66 -0
- package/dist/{db-BDijt81U.mjs → db-BFSeETMO.mjs} +7 -7
- package/dist/{delete-Dc8E5YwZ.mjs → delete-BUKgSJhh.mjs} +4 -4
- package/dist/{delete-BxjJ66BS.mjs → delete-CPBByycV.mjs} +4 -4
- package/dist/{delete-wFsy80db.mjs → delete-Cucqn4-V.mjs} +4 -4
- package/dist/{delete--15it-A8.mjs → delete-D79TlpcE.mjs} +4 -4
- package/dist/delete-DZJxBmdU.mjs +44 -0
- package/dist/{delete-BudvZkvK.mjs → delete-DndykJn2.mjs} +4 -4
- package/dist/delete-runtime-BbmRg_in.mjs +55 -0
- package/dist/{delete-table-5AQpn8OB.mjs → delete-table-CbsWcjh5.mjs} +4 -4
- package/dist/{dependencies-C1ogC3BV.mjs → dependencies-B-880phi.mjs} +5 -5
- package/dist/dependency-DBUJwXqv.mjs +15 -0
- package/dist/dependency-DKSjKT8P.mjs +165 -0
- package/dist/dependency-DSxKm9oS.mjs +111 -0
- package/dist/dependents-C_FDnpZs.mjs +52 -0
- package/dist/{dirty-CSLDYO-K.mjs → dirty-B-Is68--.mjs} +5 -5
- package/dist/discard-values-yzH-kIsr.mjs +55 -0
- package/dist/document-BRaba87v.mjs +20 -0
- package/dist/{document-CA0Q6lqu.mjs → document-C18b5rM6.mjs} +1 -1
- package/dist/{document-BHpLJbKU.mjs → document-DFCj1Az_.mjs} +2 -1
- package/dist/{download-Ddap1OAo.mjs → download-DluF-vnU.mjs} +2 -2
- package/dist/{eid-HnrWhPoe.mjs → eid-Dz-mP40j.mjs} +11 -10
- package/dist/entity-CA4zfRt4.mjs +20 -0
- package/dist/entry-flags-7x7bIyOQ.mjs +42 -0
- package/dist/{events-CXqetjcS.mjs → events-2sOk6H09.mjs} +4 -4
- package/dist/export-BKkAJOFb.mjs +74 -0
- package/dist/export-flags-NiZ7C3Gm.mjs +81 -0
- package/dist/export-preflight-CwJ_3-iX.mjs +35 -0
- package/dist/{field-CcN4oZy6.mjs → field-5nivEfd6.mjs} +33 -3
- package/dist/field-BARgq5I9.mjs +21 -0
- package/dist/field-wmN6raOG.mjs +128 -0
- package/dist/{fields-DxHxuxwO.mjs → fields-Bod0s_uV.mjs} +6 -6
- package/dist/filter-flags-BYcbB85j.mjs +71 -0
- package/dist/fks-IUw66K_Q.mjs +36 -0
- package/dist/{get-eJilYnHk.mjs → get-B8DUMrCw.mjs} +5 -5
- package/dist/{get-C3cuZNeW.mjs → get-BGqvoGQM.mjs} +3 -3
- package/dist/{get-DmKtPNOy.mjs → get-BSUsfysk.mjs} +3 -3
- package/dist/{get-DW7mCaGe.mjs → get-BXsLkoit.mjs} +5 -5
- package/dist/{get-Dtre5jzh.mjs → get-BXssHyJ1.mjs} +5 -5
- package/dist/get-Bct52yPw.mjs +36 -0
- package/dist/{get-BiPclcIP.mjs → get-Besqwy32.mjs} +3 -3
- package/dist/{get-BcoJ9Aha.mjs → get-BfSX-OeS.mjs} +3 -3
- package/dist/{get-BF2vdF3W.mjs → get-Biy4gOzu2.mjs} +3 -3
- package/dist/{get-C_tdSMwW2.mjs → get-CYIeiTZ9.mjs} +4 -4
- package/dist/{get-b0CoPz5B.mjs → get-CaEiQnSw.mjs} +4 -4
- package/dist/{get-CxUQyhte.mjs → get-Cl81y0Md.mjs} +5 -5
- package/dist/{get-B2pj2q3X.mjs → get-D1NjMXAH.mjs} +4 -4
- package/dist/{get-D_x-ENFe.mjs → get-D2cPuOPR.mjs} +5 -5
- package/dist/{get-DjlQhgD4.mjs → get-DITlshJH.mjs} +3 -3
- package/dist/{get-CE9_4Q7E.mjs → get-DLyg6Al9.mjs} +3 -3
- package/dist/{get-CDnmpfHQ.mjs → get-DQZfaVNq.mjs} +4 -4
- package/dist/{get-BGLvyOO3.mjs → get-DtBqoGTa.mjs} +5 -5
- package/dist/{get-H6kInHIx.mjs → get-_ik_5_lQ.mjs} +5 -5
- package/dist/{get-run-BE1HtJDW.mjs → get-run-CEc_tr5b.mjs} +4 -4
- package/dist/{git-sync-BwBRmEhE.mjs → git-sync-COokpYgc.mjs} +12 -3
- package/dist/git-sync-DKcaddkV.mjs +29 -0
- package/dist/git-sync-DspFahwV.mjs +165 -0
- package/dist/glossary-BlNCrYbw.mjs +30 -0
- package/dist/glossary-CWIX8Zh6.mjs +14 -0
- package/dist/glossary-LmZWwCNP.mjs +21 -0
- package/dist/graph-f6Bp1e8U.mjs +34 -0
- package/dist/{group-YQQ5GIcP.mjs → group-bpD_KcjA.mjs} +1 -1
- package/dist/{has-remote-changes-Cj1KhBOT.mjs → has-remote-changes-B8MmN6yh.mjs} +4 -4
- package/dist/{import-DLb9VN4l.mjs → import-Bhq15KgP.mjs} +19 -8
- package/dist/{input-D5tQEfqN.mjs → input-D8qW-KtR.mjs} +6 -2
- package/dist/{is-dirty-BxtgV2xF.mjs → is-dirty-CAsVuVx8.mjs} +2 -2
- package/dist/{items-DLUYc5-z.mjs → items-BZJomiDE.mjs} +8 -8
- package/dist/{key-PYetuQ13.mjs → key-BynJSK9u.mjs} +1 -1
- package/dist/{library-BNSxXOpc.mjs → library-B7wDH-Nc.mjs} +5 -5
- package/dist/{library-PGCfm0UA.mjs → library-BOv5SY24.mjs} +1 -1
- package/dist/{library-BPIzb3K_.mjs → library-Ck0EK5bi.mjs} +4 -2
- package/dist/{list-BUz6KOfw.mjs → list-B2uUl26x.mjs} +6 -6
- package/dist/{list-CERbeLbQ.mjs → list-B4luDSYj.mjs} +6 -6
- package/dist/{list-jhQSgh6i.mjs → list-B8ND9t2h.mjs} +3 -3
- package/dist/{list-M8J10Ozk.mjs → list-BT5t8ZOl.mjs} +4 -4
- package/dist/{list-Dat8QNcf.mjs → list-BT7xiUQe.mjs} +3 -3
- package/dist/{list-cIMww2Me.mjs → list-BWFCv_zk.mjs} +5 -5
- package/dist/{list-B1soS8QW.mjs → list-BtZVe1Nq.mjs} +6 -6
- package/dist/{list-C5mS_ehX.mjs → list-BwdSG-P5.mjs} +5 -5
- package/dist/{list-BQnOEB3W.mjs → list-C-A-2HFq.mjs} +3 -3
- package/dist/list-C9x--9Jq.mjs +101 -0
- package/dist/{list-CX9qZ-bx.mjs → list-CCdXig7b.mjs} +4 -4
- package/dist/{list-Sd7jGZoX.mjs → list-CJDFoOHb.mjs} +3 -3
- package/dist/{list-CzRzCbCY.mjs → list-CLFu3mQB.mjs} +3 -3
- package/dist/{list-CBLxhaNZ.mjs → list-Cd40Kr0R.mjs} +3 -3
- package/dist/{list-COLpwXW4.mjs → list-Cj_mTxbY.mjs} +4 -4
- package/dist/{list-CQbiSx26.mjs → list-CtS50R7m.mjs} +5 -5
- package/dist/{list-CehE6KBV.mjs → list-CvZyTwmV.mjs} +5 -5
- package/dist/list-DcnNlYQA.mjs +35 -0
- package/dist/{list-BcP1DTJH.mjs → list-DtgbtE3_.mjs} +3 -3
- package/dist/{login-BfLqBfCd.mjs → login-Bi_nGMJ9.mjs} +6 -6
- package/dist/{logout-BpRXB3Ze.mjs → logout-CiHQJWTu.mjs} +2 -2
- package/dist/{measure-Ds8O5Evr.mjs → measure-D2qq5e8f.mjs} +6 -6
- package/dist/moderation-review-BdqEH6N1.mjs +31 -0
- package/dist/{notification-DVrLkjFT.mjs → notification-DOYdl0ao.mjs} +1 -1
- package/dist/{parameter-values-DhrkLXoN.mjs → parameter-values-CZow98nb.mjs} +3 -3
- package/dist/{parse-enum-Dc0dMZ3P.mjs → parse-enum-CLIXMsd8.mjs} +1 -1
- package/dist/parse-id-BW09NysN.mjs +13 -0
- package/dist/{parse-ref-DTDld_LJ.mjs → parse-ref-Cw5fa4FI.mjs} +1 -1
- package/dist/parse-text-T0lTXzTZ.mjs +11 -0
- package/dist/{path-_GHFrTLx.mjs → path-K6LPLYPb.mjs} +4 -4
- package/dist/{poll-KLl4Eq8Y.mjs → poll-Dz4tUm2f.mjs} +2 -2
- package/dist/{preflight-BpYtF0IU.mjs → preflight-CX6cqB90.mjs} +3 -3
- package/dist/{process-CLtOWKBn.mjs → process-D0HdqZ3r.mjs} +1 -1
- package/dist/{publish-D-7Zlmuk.mjs → publish-COatW4Un.mjs} +4 -4
- package/dist/{pulse-x0gFxWfG.mjs → pulse-D1uh8UIU.mjs} +1 -1
- package/dist/query-BJXTIDhy.mjs +328 -0
- package/dist/{query-B1CZztxs.mjs → query-DmvkyDfo.mjs} +14 -33
- package/dist/remapping-CPAvDKHV.mjs +53 -0
- package/dist/{remove-collection-C4saCFxz.mjs → remove-collection-Bg-sNX3U.mjs} +5 -5
- package/dist/{render-6O_eF-P1.mjs → render-BSfDD6ip.mjs} +2 -2
- package/dist/{replace-BuR4f2Ai.mjs → replace-RCHeZLUT.mjs} +4 -4
- package/dist/{rescan-values-CNOiiWQ5.mjs → rescan-values-CpDGYZvf.mjs} +5 -5
- package/dist/rescan-values-PeMnpaFY.mjs +32 -0
- package/dist/{run-DijA_BXt.mjs → run-CaQ7kubO.mjs} +3 -3
- package/dist/{run-QEUbWNm7.mjs → run-DkRkZRgf.mjs} +7 -7
- package/dist/{runs-BSlhVeDO.mjs → runs-BDjoVJ0P.mjs} +5 -5
- package/dist/{runtime-Dtb93zKy.mjs → runtime-CZ5zh32z.mjs} +21 -82
- package/dist/{schema-tables-BE3rEb6I.mjs → schema-tables-C2qjLcPq.mjs} +6 -6
- package/dist/{schemas-yDjBiwlH.mjs → schemas-D8hfNduk.mjs} +4 -4
- package/dist/search-BrQQuNmh.mjs +131 -0
- package/dist/search-CnTXKJgx.mjs +85 -0
- package/dist/{segment-Cm6zQjJW.mjs → segment-Bi6eqj3e.mjs} +6 -6
- package/dist/{send-kC8oerkT.mjs → send-CAjgVNlA.mjs} +3 -3
- package/dist/{set-tFhPkxrL.mjs → set-BXTkYWid.mjs} +4 -4
- package/dist/{set-active-lDKhAlij.mjs → set-active-CBQVCePU.mjs} +3 -3
- package/dist/set-sensitivity-D39kGTob.mjs +57 -0
- package/dist/{setting-DrJXCaFE.mjs → setting-CPkvkc4H.mjs} +4 -4
- package/dist/{setup-BWY9M0iR.mjs → setup-Be3xDXHi.mjs} +3 -3
- package/dist/{signal-Csom0mt5.mjs → signal-CxWeLszu.mjs} +1 -1
- package/dist/{skill-list-CCes8pKe.mjs → skill-list-CBGq30sI.mjs} +2 -2
- package/dist/{skills-DE9ej5Rg.mjs → skills-CJ9eXouJ.mjs} +2 -2
- package/dist/{skills-DPRN2384.mjs → skills-DyiLycMY.mjs} +3 -3
- package/dist/snippet-QirK70R_.mjs +19 -0
- package/dist/{stash-C9oDvQ9P.mjs → stash-BVN3s5N_.mjs} +6 -6
- package/dist/{status-CkpujA26.mjs → status-BtbMmIWo.mjs} +7 -7
- package/dist/{status-DSdXmNC3.mjs → status-CxP6w3aL.mjs} +3 -3
- package/dist/{subscription-DKOKoHRG.mjs → subscription-Dr-YHARg.mjs} +6 -6
- package/dist/{subscriptions-C5SyJL4B.mjs → subscriptions-8tM8TTeQ.mjs} +6 -6
- package/dist/{summary-DTPgXsc0.mjs → summary-DKx6ptQe.mjs} +5 -5
- package/dist/sync-schema-B8RY0Ra4.mjs +32 -0
- package/dist/{sync-schema-DKKkNSR7.mjs → sync-schema-kNriXZjG.mjs} +6 -6
- package/dist/sync-task-CXFoqlpF.mjs +53 -0
- package/dist/table-CZnT6d9x.mjs +26 -0
- package/dist/{table-DfuY0N8e.mjs → table-Ci6bhZkb.mjs} +35 -12
- package/dist/table-CxvaDiIZ.mjs +155 -0
- package/dist/table-selector-flags-1bYkAKld.mjs +42 -0
- package/dist/text-DaNkPRwt.mjs +6 -0
- package/dist/timeline-CUzx6uXi.mjs +21 -0
- package/dist/{timeline-event-n4Lzet7l.mjs → timeline-event-DpNm4dkn.mjs} +6 -6
- package/dist/{transform-CtqB1WYo.mjs → transform-CNBIV7No.mjs} +1 -1
- package/dist/transform-g4-HHzDp.mjs +28 -0
- package/dist/transform-job-DqBtnTrG.mjs +22 -0
- package/dist/{transform-tag-D6NXqWaX.mjs → transform-tag-nTb0Ivpj.mjs} +5 -5
- package/dist/{transforms-O4NtXqFz.mjs → transforms-BSsAfwAg.mjs} +5 -5
- package/dist/tree-lMvw1jnZ.mjs +34 -0
- package/dist/{unpublish-DvWefKJi.mjs → unpublish-CYFDd828.mjs} +3 -3
- package/dist/unreferenced-8Xdq76Kw.mjs +36 -0
- package/dist/{update-AJ2JmwDl.mjs → update-6P_GYCLT.mjs} +5 -5
- package/dist/{update-76nzOc71.mjs → update-B28EJzrx.mjs} +13 -8
- package/dist/{update-To6A1Ugu.mjs → update-BDunZa7G.mjs} +4 -4
- package/dist/{update-2KIpYgyj.mjs → update-BT2yNB_g.mjs} +6 -6
- package/dist/{update-DmgRIp7o.mjs → update-BnMUSFYZ.mjs} +4 -4
- package/dist/update-BwrYeKso.mjs +41 -0
- package/dist/{update-DOdcflLH.mjs → update-C7DEMiu-.mjs} +7 -7
- package/dist/{update-CGJvIS-l.mjs → update-CITzAFG8.mjs} +4 -4
- package/dist/update-CLl8tE20.mjs +181 -0
- package/dist/{update-BB6Go_RX.mjs → update-CM5uuhjB.mjs} +4 -4
- package/dist/{update-xEegxIyH.mjs → update-CQXcsvaa.mjs} +5 -5
- package/dist/{update-DV54H3L5.mjs → update-Cs2zv_L2.mjs} +7 -7
- package/dist/{update-Bah7N8QX.mjs → update-D--Fw7-_.mjs} +5 -5
- package/dist/{update-H_fdjugs.mjs → update-D9p0N8ky.mjs} +4 -4
- package/dist/{update-7oKvP1HC.mjs → update-DAzRx0of.mjs} +5 -5
- package/dist/{update-DXmZuJho.mjs → update-D_DCOOAB.mjs} +6 -6
- package/dist/{update-D7dG7H-z.mjs → update-_sYM1GuU.mjs} +7 -7
- package/dist/{update-dashcard-DjT5QOkL.mjs → update-dashcard-VNnIqHDI.mjs} +6 -6
- package/dist/{upgrade-CuJx9fX4.mjs → upgrade-Tj_reACv.mjs} +4 -4
- package/dist/upload-BKxOpSz3.mjs +13 -0
- package/dist/{upload-DOO94rCQ.mjs → upload-C2fhpKkP.mjs} +2 -2
- package/dist/{upload-CboARPZm.mjs → upload-Dg91ldGg.mjs} +3 -3
- package/dist/{uuid-InwZlrpQ.mjs → uuid-Blpv_e0S.mjs} +2 -2
- package/dist/{validate-D6-Vg_kn.mjs → validate-D7e110oU.mjs} +1 -1
- package/dist/{validate-query-B90KYPQr.mjs → validate-query-C7LuKiUT.mjs} +2 -2
- package/dist/{values-CsZLk1Ls.mjs → values-PonkbYAK.mjs} +5 -5
- package/dist/verify-DixUBbd2.mjs +38 -0
- package/dist/verify-Dv7ER11c.mjs +38 -0
- package/dist/{verify-DrGjZwBM.mjs → verify-Mpb65a5v.mjs} +3 -3
- package/dist/verify-flags-CMpRkMPa.mjs +58 -0
- package/dist/{wait-BmzdarP9.mjs → wait-CaH6XHQV.mjs} +6 -6
- package/dist/{wait-flags-CiJGrTCT.mjs → wait-flags-rUU4NUrv.mjs} +2 -2
- package/dist/{window-DSb1tqSe.mjs → window-CI_QD04s.mjs} +1 -1
- package/package.json +1 -1
- package/skill-data/core/SKILL.md +24 -23
- package/skill-data/dashboard/SKILL.md +1 -1
- package/skill-data/document/SKILL.md +12 -1
- package/skill-data/git-sync/SKILL.md +61 -9
- package/skill-data/mbql/SKILL.md +5 -0
- package/skill-data/metadata/SKILL.md +36 -6
- package/skill-data/transform/SKILL.md +1 -63
- package/dist/body-CshgcV6U.mjs +0 -17
- package/dist/card-CefVYtF7.mjs +0 -24
- package/dist/collection-Be5AHVWP.mjs +0 -20
- package/dist/create-DiJ8bUb-.mjs +0 -34
- package/dist/dashboard-CMw13KXM.mjs +0 -33
- package/dist/database-JtHTqSpq.mjs +0 -26
- package/dist/delete-Bsdwqh7H.mjs +0 -44
- package/dist/delete-runtime-BKEzJn22.mjs +0 -49
- package/dist/document-BGzVip5_.mjs +0 -19
- package/dist/export-IFZdgYZ2.mjs +0 -63
- package/dist/field-BJ6HoLRd.mjs +0 -18
- package/dist/field-fDtIrY6D.mjs +0 -80
- package/dist/get-i0KInCw9.mjs +0 -32
- package/dist/get-jofVjU6e.mjs +0 -31
- package/dist/git-sync-TjX1r_wF.mjs +0 -136
- package/dist/git-sync-xKC1sqC4.mjs +0 -28
- package/dist/list-Cuhf2lKL.mjs +0 -35
- package/dist/list-D-SyhKFw.mjs +0 -34
- package/dist/parse-id-DtM6q9HU.mjs +0 -10
- package/dist/query-CxESKMve.mjs +0 -114
- package/dist/query-result-E4Q-Gra6.mjs +0 -15
- package/dist/run-FmRruANb.mjs +0 -64
- package/dist/search-CR_xNArA.mjs +0 -97
- package/dist/selectors-C-vgMcGK.mjs +0 -35
- package/dist/snippet-DWHTSo9c.mjs +0 -19
- package/dist/sync-task-BtDnTXCP.mjs +0 -38
- package/dist/table-C6h4tIRw.mjs +0 -37
- package/dist/table-I8NXSdUC.mjs +0 -18
- package/dist/timeline-B_ESH-6W.mjs +0 -21
- package/dist/transform-BNuNiH9d.mjs +0 -28
- package/dist/transform-job-B7yc6dfH.mjs +0 -22
- package/dist/transform-test-BjjCQ2xm.mjs +0 -169
- package/dist/transform-test-C1Oqtfb-.mjs +0 -16
- package/dist/transform-test-gzDcbypy.mjs +0 -25
- package/dist/transport-CyVo29u7.mjs +0 -313
- package/dist/tree-CuyF116R.mjs +0 -26
- package/dist/update-l3PyNQKG.mjs +0 -41
- package/dist/upload-2YFFBSln.mjs +0 -13
- package/skill-data/transform-test-plan/SKILL.md +0 -171
- package/skill-data/transform-test-plan/references/checklist.md +0 -81
- package/skill-data/transform-test-plan/references/checks.md +0 -263
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { n as ModerationReviewCompact } from "./moderation-review-BdqEH6N1.mjs";
|
|
2
|
+
//#region src/output/views/moderation-review.ts
|
|
3
|
+
const moderationReviewView = {
|
|
4
|
+
compactPick: ModerationReviewCompact,
|
|
5
|
+
tableColumns: [
|
|
6
|
+
{
|
|
7
|
+
key: "id",
|
|
8
|
+
label: "ID"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
key: "moderated_item_type",
|
|
12
|
+
label: "Item type"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
key: "moderated_item_id",
|
|
16
|
+
label: "Item ID"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
key: "status",
|
|
20
|
+
label: "Status"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
key: "text",
|
|
24
|
+
label: "Text"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
key: "most_recent",
|
|
28
|
+
label: "Most recent"
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
};
|
|
32
|
+
function describeReview(review) {
|
|
33
|
+
const target = `${review.moderated_item_type} ${review.moderated_item_id}`;
|
|
34
|
+
if (review.status === null) return `Marked ${target} unverified.`;
|
|
35
|
+
return `Verified ${target}.`;
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
//#region src/commands/verify-flags.ts
|
|
39
|
+
const verifyFlags = {
|
|
40
|
+
text: {
|
|
41
|
+
type: "string",
|
|
42
|
+
description: "Note stored with the review"
|
|
43
|
+
},
|
|
44
|
+
remove: {
|
|
45
|
+
type: "boolean",
|
|
46
|
+
description: "Withdraw the verification instead of granting it"
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
function reviewInput(itemType, itemId, args) {
|
|
50
|
+
return {
|
|
51
|
+
moderated_item_id: itemId,
|
|
52
|
+
moderated_item_type: itemType,
|
|
53
|
+
status: args.remove ? null : "verified",
|
|
54
|
+
text: args.text
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
export { moderationReviewView as i, verifyFlags as n, describeReview as r, reviewInput as t };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { H as
|
|
2
|
-
import { t as defineMetabaseCommand } from "./runtime-
|
|
3
|
-
import { n as DEFAULT_TIMEOUT_MS, t as DEFAULT_INTERVAL_MS } from "./poll-
|
|
4
|
-
import { r as parseWaitSchedule } from "./wait-flags-
|
|
5
|
-
import {
|
|
6
|
-
import { a as throwIfFailedTask, i as taskPollOptions, n as formatSyncTask, r as syncTaskIdleView, t as SyncTaskOrIdle } from "./sync-task-
|
|
1
|
+
import { H as connectionFlags, J as profileFlag, p as renderSummary, q as outputFlags } from "./cli.mjs";
|
|
2
|
+
import { t as defineMetabaseCommand } from "./runtime-CZ5zh32z.mjs";
|
|
3
|
+
import { n as DEFAULT_TIMEOUT_MS, t as DEFAULT_INTERVAL_MS } from "./poll-Dz4tUm2f.mjs";
|
|
4
|
+
import { r as parseWaitSchedule } from "./wait-flags-rUU4NUrv.mjs";
|
|
5
|
+
import { u as syncTaskView } from "./git-sync-DspFahwV.mjs";
|
|
6
|
+
import { a as throwIfFailedTask, i as taskPollOptions, n as formatSyncTask, r as syncTaskIdleView, t as SyncTaskOrIdle } from "./sync-task-CXFoqlpF.mjs";
|
|
7
7
|
//#region src/commands/git-sync/wait.ts
|
|
8
8
|
const WaitResult = SyncTaskOrIdle;
|
|
9
9
|
var wait_default = defineMetabaseCommand({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as interruptSignal } from "./cli.mjs";
|
|
2
|
-
import { t as parseId } from "./parse-id-
|
|
3
|
-
import { n as DEFAULT_TIMEOUT_MS, t as DEFAULT_INTERVAL_MS } from "./poll-
|
|
2
|
+
import { t as parseId } from "./parse-id-BW09NysN.mjs";
|
|
3
|
+
import { n as DEFAULT_TIMEOUT_MS, t as DEFAULT_INTERVAL_MS } from "./poll-Dz4tUm2f.mjs";
|
|
4
4
|
//#region src/commands/wait-flags.ts
|
|
5
5
|
const waitScheduleFlags = {
|
|
6
6
|
timeout: {
|
package/package.json
CHANGED
package/skill-data/core/SKILL.md
CHANGED
|
@@ -6,17 +6,17 @@ allowed-tools: Read, Write, Edit, Bash, AskUserQuestion
|
|
|
6
6
|
|
|
7
7
|
# metabase-cli (core)
|
|
8
8
|
|
|
9
|
-
`mb` drives a Metabase instance over its REST API:
|
|
9
|
+
The official Metabase CLI (`mb`) drives a Metabase instance over its REST API: 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 | upload | content-translation | query | card | dashboard | snippet | segment | measure | collection | library
|
|
15
|
-
document | timeline | timeline-event | transform | transform-job | transform-tag |
|
|
16
|
-
search | git-sync | setup | eid | uuid | upgrade | skills
|
|
15
|
+
document | glossary | timeline | timeline-event | transform | transform-job | transform-tag | alert | subscription | setting
|
|
16
|
+
search | dependency | git-sync | setup | eid | uuid | upgrade | skills
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
The conventions below — auth, flags, output, body input — hold across **every** group. 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.
|
|
20
20
|
|
|
21
21
|
## Auth & profiles
|
|
22
22
|
|
|
@@ -28,7 +28,7 @@ mb auth status --json # → {profile, present, url} for the d
|
|
|
28
28
|
mb auth status --profile <name> --json # health probe for one profile
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
`auth list` is the primary enumeration path — one call returns every profile with sanitized URL, an `authenticated` flag, and a probe `status` (`ok` / `auth-failed` / `network-error` / `server-error` / `not-probed`).
|
|
31
|
+
`auth list` is the primary enumeration path — one call returns every profile with sanitized URL, an `authenticated` flag, and a probe `status` (`ok` / `auth-failed` / `network-error` / `server-error` / `not-probed`). Use it before asking which profile to pick.
|
|
32
32
|
|
|
33
33
|
- One profile and intent doesn't disambiguate → use it.
|
|
34
34
|
- Several → ask via `AskUserQuestion`, presenting the names from `auth list`.
|
|
@@ -38,11 +38,12 @@ Once a name is established, pass `--profile <name>` to **every** subsequent comm
|
|
|
38
38
|
|
|
39
39
|
## Flag conventions
|
|
40
40
|
|
|
41
|
-
**`--profile`
|
|
41
|
+
**`--profile` goes before or after the verb chain, never inside it.**
|
|
42
42
|
|
|
43
43
|
```bash
|
|
44
44
|
✅ mb table list --profile prod --json
|
|
45
|
-
|
|
45
|
+
✅ mb --profile prod table list
|
|
46
|
+
❌ mb table --profile prod list # → unknown command: prod
|
|
46
47
|
```
|
|
47
48
|
|
|
48
49
|
**`--wait` for async operations.** `transform run`, `git-sync import`, and similar verbs return immediately by default. Pass `--wait` whenever the next step depends on completion — without it you race the operation and see "not ready" / transient connection refusals.
|
|
@@ -69,10 +70,10 @@ Every list verb takes `--limit <n>` (items this call returns) and `--offset <n>`
|
|
|
69
70
|
|
|
70
71
|
- **`has_more` decides whether to keep going — never compare counts.** `total` is the server's count on endpoints that report one and `null` on those that don't, so arithmetic over it is not a termination condition.
|
|
71
72
|
- **To continue, pass `next_offset` back as `--offset`.** When `has_more` is true `next_offset` is past the offset you sent, so the loop advances; when false the walk is over and `next_offset` is `null`.
|
|
72
|
-
- **`truncated` means the byte cap cut the output, not that the data ran out.** `has_more`/`next_offset` are recomputed to the cut point, so a capped list resumes like any window. Its `bytes` is what the untruncated answer would have measured
|
|
73
|
+
- **`truncated` means the byte cap cut the output, not that the data ran out.** `has_more`/`next_offset` are recomputed to the cut point, so a capped list resumes like any window. Its `bytes` is what the untruncated answer would have measured. Narrow rows with `--fields` rather than raising `--max-bytes` — a bigger cap spends context on fields you didn't ask for, and the cap counts only what you asked for, so `--fields` buys rows directly. A capped list always returns at least one row; when not even one fits it exits 2 with "the smallest response this list can produce is N bytes, over the M-byte --max-bytes cap; …".
|
|
73
74
|
- `limit` is echoed only when you passed `--limit` — except `mb search`, which defaults to `--limit 20` (an unbounded search is expensive server-side) and so always reports one. On nouns the server doesn't page, one large `--limit` with narrow `--fields` is a single request; many small `--offset` hops are one request each.
|
|
74
75
|
|
|
75
|
-
The whole walk:
|
|
76
|
+
The whole walk, literally:
|
|
76
77
|
|
|
77
78
|
```bash
|
|
78
79
|
offset=0
|
|
@@ -104,7 +105,7 @@ mb <noun> create --file ./.scratch/body.json --profile <n> --json
|
|
|
104
105
|
|
|
105
106
|
Single-quoted `'EOF'` stops the shell interpolating `$vars` inside the JSON.
|
|
106
107
|
|
|
107
|
-
Write working files to **`./.scratch`** in the current directory (`mkdir -p ./.scratch` first), never `/tmp` — they persist across the session and the user can review them.
|
|
108
|
+
Write working files to **`./.scratch`** in the current directory (`mkdir -p ./.scratch` first), never `/tmp` — better permissions, they persist across the session, and the user can review them.
|
|
108
109
|
|
|
109
110
|
## Discovering commands and schemas
|
|
110
111
|
|
|
@@ -123,9 +124,9 @@ mb transform --help --json | jq -r '.commands[].command' # verbs under "transfo
|
|
|
123
124
|
|
|
124
125
|
## Resource quirks worth memorizing
|
|
125
126
|
|
|
126
|
-
|
|
127
|
+
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.
|
|
127
128
|
|
|
128
|
-
- **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. 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"}`
|
|
129
|
+
- **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. 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`). `db sync-schema` / `rescan-values` queue async work and return `{status:"ok"}` at once; `sync-schema --wait` blocks until `initial_sync_status: complete`.
|
|
129
130
|
- **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.
|
|
130
131
|
- **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.
|
|
131
132
|
- **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.
|
|
@@ -135,25 +136,28 @@ Only what `--help` does _not_ tell you: footguns and non-obvious behaviors.
|
|
|
135
136
|
- **card.** `dataset_query` is the `mbql/query` object itself (→ `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`.
|
|
136
137
|
- **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). Every dashcard must include `card_id`, including existing rows; use `card_id:null` plus a `visualization_settings.virtual_card` block (`{display:"text"|"heading"|"link"|…}`) for non-question cards. `create` accepts the **same** `dashcards` array in its initial body, so lay out the whole dashboard in one call. `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`**. 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.
|
|
137
138
|
- **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 and omitting a parameter deletes it. A parameter only filters a card once it is **mapped** onto that dashcard's `parameter_mappings` — an unmapped parameter is an inert widget. `type` is a **closed enum**; an unlisted value is a hard parse error that echoes the full allowed set back to you. `dashboard parameter-values <id> <parameter-id> [--query <substr>]` fetches a widget's selectable values (`{values, has_more_values}`; `--query` is a case-insensitive substring search). Parameter types, ids, mapping targets, and value sources → the `dashboard` skill; load it before authoring a `parameters` array.
|
|
139
|
+
<!-- requires: contentVerification -->
|
|
140
|
+
- **verify.** `card verify <id>` / `dashboard verify <id>` (admins only) mark it verified (rank boost, `search --verified`). `--remove` (or a card query edit) withdraws it; `--text` adds a note; `get <id> --fields moderation_reviews` reads it. Verify only what you checked and the user approved.
|
|
141
|
+
<!-- /requires -->
|
|
138
142
|
- **alert / subscription are two unrelated systems.** `alert` watches one **card** and fires on a send condition (`/api/notification`: a cron string, `channel/email`-prefixed handlers, typed recipients); `subscription` delivers one **dashboard** on a schedule (`/api/pulse`: structured `schedule_type` + hour/day/frame, bare `email` channels, `{id}|{email}` recipients). The bodies are not interchangeable. Both silently deliver nowhere if the server has no SMTP / Slack app — check `mb setting get 'email-configured?'` (quote it; the `?` is a shell glob) before creating either. Their list-valued fields (`handlers`/`subscriptions`, `channels`/`cards`) **replace wholesale** on update, so adding one recipient is a read-modify-write. `mb card alerts <id>` and `mb dashboard subscriptions <id>` list what's already attached to a card/dashboard; `archive` deactivates rather than deletes. Load the `notification` skill before authoring either body.
|
|
139
143
|
- **snippet `--archived` is a swap, not a union** — list returns _either_ active _or_ archived rows, never both. (Same for `--filter archived` on dashboard/collection.)
|
|
140
144
|
- **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.
|
|
141
|
-
- **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
|
|
142
|
-
- **collection `<ref>`**
|
|
145
|
+
- **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. 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`.
|
|
146
|
+
- **collection `<ref>`** (`get`/`items`): positive int, `root`, `trash`, or a 21-char entity_id; `update`/`archive` take the int only. `collection items` pages the server, pulling only what the cap can show — read `has_more`/`next_offset` to continue. `collection tree` is **JSON-only** and hides the Library without `--include-library`. A transform collection needs `collection create --namespace transforms`. `update` keeps `archived` unless `--archived` is given.
|
|
143
147
|
- **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}`).
|
|
144
|
-
- **search vs. list.**
|
|
148
|
+
- **search vs. list.** Enumerate with `… list` verbs; `search` ranks a query or spans resources. `--search-native-query` matches native query text and keeps only query-bearing models; dashboard questions need `--include-dashboard-questions`.
|
|
145
149
|
<!-- requires: transforms -->
|
|
146
150
|
- **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`.)
|
|
147
|
-
<!-- /requires -->
|
|
148
|
-
<!-- requires: transformTests -->
|
|
149
|
-
- **transform-test.** `transform-test run <id>` checks a transform against fixtures in temp tables — only a `format: "sql"` input reads the source database — and exits 1 unless it passes. (→ `transform`.)
|
|
150
151
|
<!-- /requires -->
|
|
151
152
|
- **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.
|
|
152
153
|
- **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).
|
|
153
154
|
<!-- requires: library -->
|
|
154
|
-
- **library.** The Library is a curated subtree (`library-data` "Data" + `library-metrics` "Metrics" under a `library` root): tables
|
|
155
|
+
- **library.** The Library is a curated subtree (`library-data` "Data" + `library-metrics` "Metrics" under a `library` root): tables in **Data** lead data pickers and rank up in search; metrics in **Metrics** lead nav, search, and the query builder. `library get` shows its Data/Metrics collection ids; `library create` provisions it (idempotent). `library publish --table-ids/--db-ids/--schemas` publishes tables into Data, **resolving the Data collection and creating the Library if absent**; each `--schemas` entry is `<db-id>:<schema>` (`1:public`), not a bare name. `publish` cascades to upstream FK dependencies, `unpublish` to downstream dependents; both need **admin or data-analyst** (Curate alone won't publish) and exit **403** without write **and** query permission on every affected table. `table get`/`table list` carry `is_published` (`collection_id` under `--full`). Publishing leaves the Data collection out of git-sync scope: on a remote-sync instance, `mb git-sync add-collection <data-collection-id>` makes exports carry published tables' metadata (see `git-sync`).
|
|
155
156
|
<!-- /requires -->
|
|
156
157
|
- **query / uuid.** `mb query` runs MBQL (`--dry-run` → run; `mbql`). `mb uuid --count <n>` mints an aggregation's `lib/uuid` (`mbql`) and document node `_id`s (`document`).
|
|
158
|
+
<!-- requires: library, tableListPublishedFilter -->
|
|
159
|
+
- `table list --published-only` lists only published tables.
|
|
160
|
+
<!-- /requires -->
|
|
157
161
|
|
|
158
162
|
## Specialized skills (load on demand)
|
|
159
163
|
|
|
@@ -167,15 +171,12 @@ This file is enough for any single-command task. For anything deeper, load the r
|
|
|
167
171
|
- **`notification`** — scheduled delivery: question alerts (`mb alert`) and dashboard subscriptions (`mb subscription`). Choosing between them, the two schedule/recipient contracts, channel prerequisites, testing a send.
|
|
168
172
|
<!-- requires: transforms -->
|
|
169
173
|
- **`transform`** — transform body JSON, create + run-with-wait, run inspection, tags, jobs.
|
|
170
|
-
<!-- /requires -->
|
|
171
|
-
<!-- requires: transformTests -->
|
|
172
|
-
- **`transform-test-plan`** — deciding _what_ to test in a transform: the fixture cast, the expectations, the coverage matrix. (`transform` has the `mb transform-test` shapes.)
|
|
173
174
|
<!-- /requires -->
|
|
174
175
|
- **`document`** — Metabase documents (TipTap body, embedding cards).
|
|
175
176
|
<!-- requires: remoteSync -->
|
|
176
177
|
- **`git-sync`** — round-tripping content to/from a git remote.
|
|
177
178
|
<!-- /requires -->
|
|
178
|
-
- **`data-workflow`** — the guided, end-to-end data workflow: investigate raw data, build clean analysis-ready tables, define reusable segments/measures/metrics, answer questions, build dashboards. **Start here when the user states a goal rather than a single verb** — "make sense of my data", "build a data model", "
|
|
179
|
+
- **`data-workflow`** — the guided, end-to-end data workflow: investigate raw data, build clean analysis-ready tables, define reusable segments/measures/metrics, answer questions, build dashboards. **Start here when the user states a goal rather than a single verb** — "make sense of my data", "build a data model", "be my data analyst". It detects where the data is and routes to the right stage.
|
|
179
180
|
|
|
180
181
|
If a task spans more than one, load each. `mb skills list` enumerates the skills the profile's server can use, and `mb skills get` prints a skill with the sections that server cannot use left out; `--unfiltered` shows everything.
|
|
181
182
|
|
|
@@ -10,7 +10,7 @@ A dashboard starts as cards on a grid; it becomes an **app** when filters drive
|
|
|
10
10
|
|
|
11
11
|
**`core` owns the transport mechanics** — the whole-array replace semantics (editing `dashcards` or `parameters` replaces the entire set; omitted entries are deleted; every dashcard carries `card_id`, with `null` for virtual cards; new cards use negative ids), `update-dashcard` for a single safe patch vs. `update --body` for a full replace, 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
|
-
Inspect before you wire: `mb dashboard get <id> --json` hydrates `parameters`, `dashcards`, and `tabs`; `mb dashboard cards <id>` lists just the dashcards.
|
|
13
|
+
Inspect before you wire: `mb dashboard get <id> --json` hydrates `parameters`, `dashcards`, and `tabs`; `mb dashboard cards <id>` lists just the dashcards. To start from an existing dashboard, `mb dashboard copy <id> --collection-id <id> --json` copies it with its tabs and dashcards (the cards stay referenced; `--deep` duplicates the questions and metrics into the target collection, and a dashboard holding dashboard questions needs it). Cards left out of the copy come back by id as `uncopied`.
|
|
14
14
|
|
|
15
15
|
## Layout: the grid is 24 columns — not 12
|
|
16
16
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: document
|
|
3
|
-
description: Author and edit Metabase documents via `mb` — the TipTap (ProseMirror) JSON body shape, the node-type inventory (paragraphs, headings, lists, plus the Metabase-custom `cardEmbed` / `smartLink` / `flexContainer` / `resizeNode`), the per-node-type `_id` requirement, embedding existing or brand-new cards, and the list/get/create/update/archive verbs. Load when the user touches documents — "create a document", "add a card to a document", "edit a document", "list documents", or anything `mb document …`.
|
|
3
|
+
description: Author and edit Metabase documents via `mb` — the TipTap (ProseMirror) JSON body shape, the node-type inventory (paragraphs, headings, lists, plus the Metabase-custom `cardEmbed` / `smartLink` / `flexContainer` / `resizeNode`), the per-node-type `_id` requirement, embedding existing or brand-new cards, and the list/get/create/update/copy/archive verbs. Load when the user touches documents — "create a document", "add a card to a document", "edit a document", "copy a document", "list documents", or anything `mb document …`.
|
|
4
4
|
allowed-tools: Read, Write, Edit, Bash, AskUserQuestion
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -20,9 +20,20 @@ mb document update <id> --file patch.json --profile <name> --json # PATCH sema
|
|
|
20
20
|
mb document archive <id> --profile <name> --json # soft-delete (PUT archived:true)
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
<!-- requires: documentCopy -->
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
mb document copy <id> --name "<name>" --collection-id <id> --profile <name> --json # duplicate with its saved cards
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
<!-- /requires -->
|
|
30
|
+
|
|
23
31
|
- `list` returns the standard list envelope (see `core`). The compact item is `{id, name, collection_id, archived, creator_id, can_write}` and omits the (potentially huge) `document` body — pull the body with `get --full`.
|
|
24
32
|
- `archive` is the only delete, mirroring `card` / `dashboard`. **Unarchive** with `mb document update <id> --body '{"archived":false}'`.
|
|
25
33
|
- `update` is PATCH — send only the keys you want to change: `name`, `document`, `collection_id`, `collection_position`, `archived`, and `cards` (inline card creation works on update too, not just create — see below). Replacing `document` replaces the **whole** body; there is no partial-node patch.
|
|
34
|
+
<!-- requires: documentCopy -->
|
|
35
|
+
- `copy` duplicates a document together with the cards saved inside it, whose embeds in the copied body point at the duplicates. `--name` defaults to the source name. Without `--collection-id` the copy lands in the root collection; an archived source is not found.
|
|
36
|
+
<!-- /requires -->
|
|
26
37
|
|
|
27
38
|
## Node ids (`_id`)
|
|
28
39
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: 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 …`.
|
|
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 and a preflight preview), 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
|
requires: [remoteSync]
|
|
6
6
|
---
|
|
@@ -15,7 +15,13 @@ This skill covers the import/export workflow. Flag conventions and auth setup li
|
|
|
15
15
|
|
|
16
16
|
## Precondition: read state before mutating
|
|
17
17
|
|
|
18
|
-
Always run `status` (or `is-dirty` + `has-remote-changes`) before `import` or `export`. Importing on a dirty instance silently rejects unless you pass `--force
|
|
18
|
+
Always run `status` (or `is-dirty` + `has-remote-changes`) before `import` or `export`. Importing on a dirty instance silently rejects unless you pass `--force`.
|
|
19
|
+
|
|
20
|
+
<!-- requires: remoteSyncMerge -->
|
|
21
|
+
|
|
22
|
+
Exporting after the remote moved on ends in a `conflict` task unless the export merges or forces.
|
|
23
|
+
|
|
24
|
+
<!-- /requires -->
|
|
19
25
|
|
|
20
26
|
```bash
|
|
21
27
|
mb git-sync status --profile <n> --json # → branch, dirty, current task
|
|
@@ -50,6 +56,14 @@ Workflow:
|
|
|
50
56
|
2. Confirm `has-remote-changes` reports `has_changes: true` — there's actually something to import.
|
|
51
57
|
3. `git-sync import --branch <branch>` — runs to terminal status by default.
|
|
52
58
|
|
|
59
|
+
<!-- requires: remoteSyncMerge -->
|
|
60
|
+
|
|
61
|
+
### Pull without discarding local work
|
|
62
|
+
|
|
63
|
+
`mb git-sync import --merge` keeps un-pushed local changes and folds the remote's in by a three-way merge, where a plain import refuses on a dirty instance and `--force` discards the local work. Entities changed on both sides, or no merge base (a remote whose history was rewritten, or an instance that has never synced), end the task in `conflict` without touching local state; the conflicting entities are listed in the task's `conflicts`. It does not combine with `--force`.
|
|
64
|
+
|
|
65
|
+
<!-- /requires -->
|
|
66
|
+
|
|
53
67
|
## Export (instance → remote)
|
|
54
68
|
|
|
55
69
|
```bash
|
|
@@ -58,12 +72,12 @@ mb git-sync export -m "commit message" --branch <branch> --profile <n>
|
|
|
58
72
|
|
|
59
73
|
Pushes Metabase-side changes back to the configured remote. `-m` is the commit message; without it the server picks a default. Defaults to `--wait`.
|
|
60
74
|
|
|
61
|
-
| Flag | Purpose
|
|
62
|
-
| ------------------- |
|
|
63
|
-
| `--branch <name>` |
|
|
64
|
-
| `-m, --message <s>` | Commit message.
|
|
65
|
-
| `--force` | Force-push / overwrite remote. Confirm with the user.
|
|
66
|
-
| `--no-wait` | Don't poll.
|
|
75
|
+
| Flag | Purpose |
|
|
76
|
+
| ------------------- | -------------------------------------------------------------------------------------------------- |
|
|
77
|
+
| `--branch <name>` | Defaults to the tracked `remote-sync-branch`. To push elsewhere, `stash` or `create-branch` first. |
|
|
78
|
+
| `-m, --message <s>` | Commit message. |
|
|
79
|
+
| `--force` | Force-push / overwrite remote. Confirm with the user. |
|
|
80
|
+
| `--no-wait` | Don't poll. |
|
|
67
81
|
|
|
68
82
|
Workflow:
|
|
69
83
|
|
|
@@ -72,6 +86,44 @@ Workflow:
|
|
|
72
86
|
3. `git-sync export -m "..."` — pushes and polls.
|
|
73
87
|
4. (Optional) `git-sync status` — verify `is_dirty: false` after.
|
|
74
88
|
|
|
89
|
+
<!-- requires: remoteSyncBranchGuard -->
|
|
90
|
+
|
|
91
|
+
`--branch` only asserts the branch you believe is tracked: the server rejects any other with a 409 naming the current one. Left out, the CLI reads the tracked branch first and refuses when none is tracked or the caller is not an admin.
|
|
92
|
+
|
|
93
|
+
<!-- /requires -->
|
|
94
|
+
|
|
95
|
+
<!-- requires: remoteSyncMerge -->
|
|
96
|
+
|
|
97
|
+
### Merge when the remote moved on
|
|
98
|
+
|
|
99
|
+
`mb git-sync export --merge -m "..."` folds the remote's changes in by a three-way merge, pushes the result and reconciles it into Metabase, where a plain export ends in a `conflict` task. Entities changed on both sides, or a remote whose history was rewritten, still end it in `conflict` and write nothing. It does not combine with `--force`.
|
|
100
|
+
|
|
101
|
+
<!-- /requires -->
|
|
102
|
+
|
|
103
|
+
<!-- requires: remoteSyncExportPreflight, remoteSyncMerge -->
|
|
104
|
+
|
|
105
|
+
### Preview the push first
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
mb git-sync export-preflight --profile <n> --json # → {has_changes, clean, conflicts, summary, force_push_casualties, reason}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
A dry run against the live remote branch, writing nothing. Read it between the state check and the export, and decide from the fields:
|
|
112
|
+
|
|
113
|
+
| Answer | Meaning | Move |
|
|
114
|
+
| ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
115
|
+
| `has_changes: false` | The remote has not moved past the last sync, or nothing has been synced yet — and then `force_push_casualties` is empty whatever the remote holds. | `export -m "..."` applies as-is. Don't read empty casualties on a never-synced instance as leave to `--force`. |
|
|
116
|
+
| `has_changes: true`, `reason: null` | The remote moved on. `clean` says whether a three-way merge would apply; `conflicts` names the entities changed on both sides; `summary` counts what a merge would fold in. | `clean: true`: `export --merge -m "..."`. `clean: false`: the conflicts need the user's call — `create-branch <name>` then `export -m "..."` pushes Metabase's side to a new branch and leaves the old one untouched, `import --force` takes the remote's side, `export --force` takes Metabase's. A plain `export` ends in a `conflict` task, and so does `stash`: its new branch starts at the moved remote tip. |
|
|
117
|
+
| `reason: "history-rewritten"` | The remote was force-pushed or rebased, so there is no merge base. | Only `export --force` can push, and only with the user's explicit go-ahead; `force_push_casualties` is exactly what it would delete or overwrite. |
|
|
118
|
+
|
|
119
|
+
`force_push_casualties` is reported on every answer: it is the remote content a force push would discard instead of merging, so read it before ever passing `--force`. It is empty when nothing has been synced yet, yet `export --force` then replaces the remote's managed directories wholesale: check the remote before forcing a first export. `--branch` defaults to the tracked `remote-sync-branch`; the server rejects any other branch with a 409 naming the current one, so pass it only to assert the branch you believe is tracked.
|
|
120
|
+
|
|
121
|
+
### After a `conflict` task
|
|
122
|
+
|
|
123
|
+
A task that ends in `conflict` makes the server count the remote commit it saw as synced. From then on `has-remote-changes` and `export-preflight` report nothing pending, and a retried `export`, `export --merge` or `import --merge` succeeds without bringing the remote's changes into Metabase; a full re-export can then remove them from the remote. So read `export-preflight` before exporting rather than letting an export find the divergence, and never answer a conflict with a retry: take the user's call between `import --force` (the remote's side), `export --force` (Metabase's side) and `create-branch <name>` then `export` (Metabase's side on a new branch).
|
|
124
|
+
|
|
125
|
+
<!-- /requires -->
|
|
126
|
+
|
|
75
127
|
### Branch guard: don't export to main/master without confirmation
|
|
76
128
|
|
|
77
129
|
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.
|
|
@@ -82,7 +134,7 @@ If the branch is `main` or `master`, prompt with `AskUserQuestion`:
|
|
|
82
134
|
|
|
83
135
|
> "The instance is tracking `<branch>` — exporting commits straight to it. Switch to a feature branch first?"
|
|
84
136
|
>
|
|
85
|
-
> 1. **Create a feature branch** — agent suggests a name (e.g., `agent/<task>`); run `mb git-sync create-branch <name> --profile <n>`. This
|
|
137
|
+
> 1. **Create a feature branch** — agent suggests a name (e.g., `agent/<task>`); run `mb git-sync create-branch <name> --profile <n>`. This creates the branch on the remote from the last synced commit (the tracked branch's tip when nothing has synced yet) and switches the instance's tracked branch to it; the next `git-sync export` pushes the dirty state there.
|
|
86
138
|
> 2. **Proceed on `main`/`master`** — explicitly accepted.
|
|
87
139
|
|
|
88
140
|
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.
|
package/skill-data/mbql/SKILL.md
CHANGED
|
@@ -153,12 +153,17 @@ A measure is an aggregation, `["measure", {}, <measure id>]`, on its table, and
|
|
|
153
153
|
mb query --file q.json --dry-run --profile <n> # check + compile on the server, no run
|
|
154
154
|
mb query --file q.json --profile <n> --json # check + run
|
|
155
155
|
mb query --print-schema --profile <n> > ./.scratch/mbql-schema.json # the full JSON Schema
|
|
156
|
+
mb query --file q.json --compile --profile <n> # the SQL the server generates
|
|
157
|
+
mb query --file q.json --metadata --profile <n> --json # tables (with FK targets), fields, snippets it touches
|
|
158
|
+
mb query --file q.json --export-format csv --profile <n> > rows.csv # stream the rows up to the server's download limit, no envelope cap
|
|
156
159
|
```
|
|
157
160
|
|
|
158
161
|
- `--dry-run` checks the shape locally, then has the server compile the query to SQL without running it. It answers `{ ok, errors: [{ path, message }], sql }`: exit `0` with the compiled `sql`, or exit `2` with `sql: null` when either check rejects the body. A local error's `path` is a JSON Pointer into the body; a server error's is `""`. Exit `1` means the compile could not run (the server refused permission, server unreachable).
|
|
159
162
|
- The server compile catches what the shape check cannot: a ref to a missing aggregation or expression, an unknown clause, a duplicate `lib/uuid`, a missing table, field, card or segment, a raw-variable template tag with no value or `default` outside an optional `[[ ]]` clause, a `required` tag with no value. A column's type not suiting its operator and a misspelled column name in a later stage are caught only by the warehouse, so a mistake there compiles and fails on the run. When a run fails, read the message and fix the body it names; an error naming nothing in the body (a `NullPointerException`) is a server fault, so stop editing a body that is otherwise correct.
|
|
160
163
|
- A run checks the shape first and never sends an invalid body; exit `1` is a server or warehouse error after that.
|
|
161
164
|
- A server error `lib/uuid: missing required key` at a clause means that clause's arguments are wrong: their count, a unit it doesn't take, a bad time zone.
|
|
165
|
+
- `--compile` shows what a clause becomes: read the SQL when an aggregation, join or temporal bucket misbehaves, or seed a native query from a working body (`--no-pretty --format text` prints it bare). It needs native query permission on the database, and its SQL leaves out the row limit a run adds. `--metadata` shows what a body reaches before running it: every source table with its columns, the tables their foreign keys point at, and the fields and snippets a native body's template tags name. Both pre-flight the body like a run; `--dry-run`, `--compile`, `--metadata` and `--export-format` are mutually exclusive.
|
|
166
|
+
- A csv or xlsx export of a pivot passes `--pivot-results` with `--visualization-settings '{"pivot_table.column_split":{"rows":[…],"columns":[…],"values":[…]}}'`, naming the body's breakout and aggregation columns; an ad-hoc query has no card to take the layout from.
|
|
162
167
|
- A run answers `data.rows` and slim `data.cols` (`name`, `display_name`, `base_type`, `semantic_type`); `--full` returns the raw `/api/dataset` envelope.
|
|
163
168
|
|
|
164
169
|
## Where the query goes
|
|
@@ -8,7 +8,7 @@ allowed-tools: Read, Write, Edit, Bash, AskUserQuestion
|
|
|
8
8
|
|
|
9
9
|
Metabase reads the raw column types from your warehouse; **metadata** is the layer you edit on top to make columns behave well — the right filter widget, joins, formatting, maps. You set it per-column with `mb field update <id>` and per-table with `mb table update <id>`. Both are **PATCH** — send only the keys you're changing.
|
|
10
10
|
|
|
11
|
-
Metadata is a small set of fields with large, indirect effects. Get the field ids from `mb table get <id> --include fields` (or `mb table fields <id>`); inspect a column's shape with `mb field get <id>`, its live cardinality with `mb field summary <id>`, its cached distinct set with `mb field values <id>`. General flag/output/body mechanics live in `core`.
|
|
11
|
+
Metadata is a small set of fields with large, indirect effects. Get the field ids from `mb table get <id> --include fields` (or `mb table fields <id>`); inspect a column's shape with `mb field get <id>`, its live cardinality with `mb field summary <id>`, its cached distinct set with `mb field values <id>`. To find rows by what a person typed, `mb field search <id> <search-id> --value <text>` answers `{ value, label }` pairs (an id column searched by a name column gives id/name pairs across the FK; both must land on one table, or it answers nothing), and `mb field remapping <id> <remapped-id> <value>` resolves one value to its display value. General flag/output/body mechanics live in `core`.
|
|
12
12
|
|
|
13
13
|
## The causal chain — set X, unlock Y
|
|
14
14
|
|
|
@@ -30,6 +30,18 @@ This is the whole point of the skill. Each edit below is a key in the `field upd
|
|
|
30
30
|
|
|
31
31
|
`table update` carries the table-level equivalents: `display_name`, `description`, `visibility_type` (`hidden` / `technical` / `cruft` — hides the whole table from the builder), `field_order`, and `entity_type`.
|
|
32
32
|
|
|
33
|
+
<!-- requires: bulkTableEdit -->
|
|
34
|
+
|
|
35
|
+
To set the same `entity_type`, `data_layer`, `data_source`, `data_authority`, `owner_email`, or `owner_user_id` on many tables at once, `mb table bulk-edit --body '{"schema_ids":["1:public"],"data_layer":"final"}'` takes `table_ids`, `database_ids`, and `schema_ids` (`"<db-id>:<schema>"`) selectors in the same body. A configured `data_authority` cannot be set back to `unconfigured`, and `data_source` never moves to or from `metabase-transform`; a selected table that breaks either rule fails the call. The server answers the same whether or not a selector matched a table, so confirm the edit with `mb table list` (e.g. `--data-layer final`).
|
|
36
|
+
|
|
37
|
+
<!-- /requires -->
|
|
38
|
+
|
|
39
|
+
<!-- requires: tableUserValueWithdrawal, bulkTableEdit -->
|
|
40
|
+
|
|
41
|
+
`null` withdraws an edit: `data_source` and `data_layer` read back empty, while `entity_type`, `owner_email`, `owner_user_id`, and `data_authority` fall back to the values Metabase keeps for the table — the entity type it derives from the table name, and usually no owner and `unconfigured`. A call that fails on a rule may already have edited the selected tables that carried no earlier edit, so re-check with `mb table list` after a refusal.
|
|
42
|
+
|
|
43
|
+
<!-- /requires -->
|
|
44
|
+
|
|
33
45
|
## Foreign keys are the highest-leverage edit
|
|
34
46
|
|
|
35
47
|
A FK relationship is what makes a warehouse browsable. Set it in **two keys on the FK column**, in one PATCH:
|
|
@@ -43,7 +55,7 @@ mb field update 1711 --body '{"semantic_type":"type/FK","fk_target_field_id":168
|
|
|
43
55
|
- Queries can pull columns from the related table with no explicit join — `["field", {"source-field": 1711}, 1682]` in MBQL (see `mbql`).
|
|
44
56
|
- Dashboard **linked filters** become possible (a State filter narrowing a City filter). **Linked filters read only these table-metadata FKs** — never a join you wrote inside a saved question — which is why a linked filter that "shows values it shouldn't" almost always means the FK isn't set in metadata. (See `dashboard`.)
|
|
45
57
|
|
|
46
|
-
Removing the `type/FK` semantic type auto-clears `fk_target_field_id`. Point a FK only at a field in the **same database** — a cross-database target is invalid and never resolves to a relationship.
|
|
58
|
+
`mb table fks <id>` lists the FKs already pointing at a table (origin field with its table, destination field), so check it before adding one; it leaves out origins in tables you cannot read, so an absent FK is conclusive only for an admin. Removing the `type/FK` semantic type auto-clears `fk_target_field_id`. Point a FK only at a field in the **same database** — a cross-database target is invalid and never resolves to a relationship.
|
|
47
59
|
|
|
48
60
|
## Semantic types are labels, not casts
|
|
49
61
|
|
|
@@ -61,13 +73,31 @@ The full semantic-type catalog — every value grouped by the base type it attac
|
|
|
61
73
|
|
|
62
74
|
## Sync, scan, fingerprint — three different refreshes
|
|
63
75
|
|
|
64
|
-
When a column looks stale or missing, know which one you need (`db` verbs, mechanics in `core`):
|
|
76
|
+
When a column looks stale or missing, know which one you need (`db` and `table` verbs, mechanics in `core`):
|
|
65
77
|
|
|
66
|
-
- **Sync** (`mb db sync-schema <id> --wait`) — re-reads table/column **structure** (new tables, new columns, types). Run after a schema change.
|
|
67
|
-
- **Scan / rescan** (`mb db rescan-values <id>`) — refreshes the **distinct-value sets** behind dropdown filters. Run when a `list` column's values changed but its dropdown is stale.
|
|
78
|
+
- **Sync** (`mb db sync-schema <id> --wait`) — re-reads table/column **structure** (new tables, new columns, types). Run after a schema change. `mb table sync-schema <id>` re-syncs one known table (columns, fingerprints, field values), never finds a new table, and offers nothing to wait on.
|
|
79
|
+
- **Scan / rescan** (`mb db rescan-values <id>`, or `mb table rescan-values <id>` for one table) — refreshes the cached **distinct-value sets** behind dropdown filters, skipping a set unread for 14 days or never read. Run when a `list` column's values changed but its dropdown is stale. `mb table discard-values <id> --yes` deletes a table's sets and their custom display values; no scan recreates them, only the next read does.
|
|
68
80
|
- **Fingerprint** — value-distribution stats (min/max, null count) computed on a sample; drives smart defaults. Refreshed by sync; not a separate CLI verb.
|
|
69
81
|
|
|
70
|
-
A newly connected database or a missing expected column usually just needs a `sync-schema --wait` before you conclude anything.
|
|
82
|
+
A newly connected database or a missing expected column usually just needs a `db sync-schema <id> --wait` before you conclude anything.
|
|
83
|
+
|
|
84
|
+
<!-- requires: bulkTableSync -->
|
|
85
|
+
|
|
86
|
+
For a set of tables, `mb table bulk-sync-schema`, `bulk-rescan-values`, and `bulk-discard-values --yes` take `--table-ids`, `--db-ids`, and `--schemas 1:public` selectors (`1:` for the tables with no schema) and need an admin or data analyst.
|
|
87
|
+
|
|
88
|
+
<!-- /requires -->
|
|
89
|
+
|
|
90
|
+
<!-- requires: fieldDataSensitivity -->
|
|
91
|
+
|
|
92
|
+
## Data sensitivity is a label a person owns
|
|
93
|
+
|
|
94
|
+
Each column carries a `data_sensitivity` label (`SEC_KEY`, `SYS_TELEMETRY`, `PHI`, `BIO_GEN`, `PCI_FIN`, `SENS_PERS`, `PII`, `CORP_IP`, `BIZ_CONF`, `PUBLIC`, most severe first) or `null`. A server that enables its classifier (off by default) labels columns from their name, types and fingerprint during sync; otherwise only people do. `mb field set-sensitivity <id> PII` labels one column and the classifier never overwrites it; `mb field set-sensitivity <id> none` withdraws that label, leaving the classifier's if there is one. Read it back with `mb field get <id>`. The label masks and restricts nothing.
|
|
95
|
+
|
|
96
|
+
<!-- /requires -->
|
|
97
|
+
|
|
98
|
+
## Glossary — the business terms behind the columns
|
|
99
|
+
|
|
100
|
+
Column metadata says what a field is; the glossary says what the business means by a word. Entries are instance-wide `{term, definition}` pairs, listed on the Glossary pages of Data Studio and the Data Reference and fed to Metabot as context, so defining "Churn" or "Active customer" once shapes every answer built on those columns. `mb glossary list --search <text>` matches terms and definitions (there is no `get`); `mb glossary create --term "Churn" --definition "…"` adds one, or pass a body with exactly `term` and `definition`; `mb glossary update <id>` replaces both fields; `mb glossary delete <id> --yes` removes one. Terms are unique.
|
|
71
101
|
|
|
72
102
|
## Don't
|
|
73
103
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: transform
|
|
3
|
-
description: Author and run Metabase transforms via `mb` — body shape (native SQL or structured MBQL), create + run-with-wait, run inspection, dependencies, cancel, the `update`-vs-recreate iteration rule, the writable-keys-only PATCH contract,
|
|
3
|
+
description: Author and run Metabase transforms via `mb` — body shape (native SQL or structured MBQL), create + run-with-wait, run inspection, dependencies, cancel, the `update`-vs-recreate iteration rule, the writable-keys-only PATCH contract, plus transform tags and tag-driven transform-job schedules. Load when the user touches transforms — "create a transform", "run a transform", "fix a failing transform", "list transform runs", "cancel a running transform", "manage transform tags", "run a transform job", or anything `mb transform …` / `mb transform-job …` / `mb transform-tag …`.
|
|
4
4
|
allowed-tools: Read, Write, Edit, Bash, AskUserQuestion
|
|
5
5
|
requires: [transforms]
|
|
6
6
|
---
|
|
@@ -173,68 +173,6 @@ mb transform run "$ID" --wait --profile <n> --json # → succeeded
|
|
|
173
173
|
|
|
174
174
|
If you really must `create + delete` instead, do the `delete` **before** the first `git-sync export` so the failed entity never lands in git history — an export of a soft-failed state is noise that needs a follow-up cleanup commit. See `git-sync`, "Read state before mutating", for the ordering rule.
|
|
175
175
|
|
|
176
|
-
<!-- requires: transformTests -->
|
|
177
|
-
|
|
178
|
-
## Transform tests
|
|
179
|
-
|
|
180
|
-
A transform test replaces every table the transform reads with a fixture, runs it into a temp table, and checks that output. The transform and the expectations read only the run's temp tables; a `format: "sql"` input runs verbatim against the transform's source database, so it may read real tables. Only query transforms (native SQL or MBQL) can be tested, and only on Postgres, MySQL, H2, ClickHouse, Redshift or SQL Server; otherwise `create`/`update`/`run` refuse with `transform-test.unsupported-transform` / `unsupported-driver` (422).
|
|
181
|
-
|
|
182
|
-
```bash
|
|
183
|
-
mb transform-test list --transform <id> --profile <n> --json # --transform is optional
|
|
184
|
-
mb transform-test get <id> --full --profile <n> --json # --full for inputs/expectations
|
|
185
|
-
mb transform-test create --file ./.scratch/test.json --profile <n> --json
|
|
186
|
-
mb transform-test update <id> --file ./.scratch/patch.json --profile <n> --json
|
|
187
|
-
mb transform-test delete <id> --yes --profile <n>
|
|
188
|
-
mb transform-test run <id> --profile <n> --json # exits 1 unless it passes
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
**`inputs`** — one per table the transform reads, each naming a `table` plus either `format: "sql"` with `sql`, or `format: "rows"` with `columns` (each a `name` and a `cast_type`) and `rows`.
|
|
192
|
-
|
|
193
|
-
**`cast_type` is a `CAST` target, not a column type**, and the two vocabularies differ per warehouse: MySQL casts to `SIGNED` and reports `BIGINT`; ClickHouse takes `Nullable(Int32)` for a column that is `Int64`. So a body is warehouse-specific — don't copy a `database_type` out of a run result into a `cast_type`, and don't expect one body to run everywhere.
|
|
194
|
-
|
|
195
|
-
**`expectations`** — `type: "empty"` with the `sql` that must return no rows, or `type: "equals"` with `format: "rows"` and the `columns` and `rows` the output must hold exactly; an `equals` without a `format` is refused.
|
|
196
|
-
|
|
197
|
-
**Expectation SQL may only name the transform's target table and its declared input tables.** Those are rewritten to the run's temp tables; any other table is refused with `transform-test.unremapped-reference` (400), at `create`/`update` as well as `run`. A column qualified by its table name (`people.id`) is refused the same way, in expectation SQL and in the transform's own SQL alike: qualify by an alias (`FROM people p … p.id`) or leave the column bare — for the transform's SQL that means editing the transform. Expectation names are unique within a test (refused with "expectation names must be unique within a test"), and every `rows` row carries exactly its declared columns (refused naming the missing or undeclared columns).
|
|
198
|
-
|
|
199
|
-
```json
|
|
200
|
-
{
|
|
201
|
-
"transform_id": 1,
|
|
202
|
-
"name": "adults only",
|
|
203
|
-
"inputs": [
|
|
204
|
-
{
|
|
205
|
-
"table": { "schema": "public", "name": "people" },
|
|
206
|
-
"format": "rows",
|
|
207
|
-
"columns": [
|
|
208
|
-
{ "name": "id", "cast_type": "INTEGER" },
|
|
209
|
-
{ "name": "age", "cast_type": "INTEGER" }
|
|
210
|
-
],
|
|
211
|
-
"rows": [
|
|
212
|
-
{ "id": 1, "age": 30 },
|
|
213
|
-
{ "id": 2, "age": 12 }
|
|
214
|
-
]
|
|
215
|
-
}
|
|
216
|
-
],
|
|
217
|
-
"expectations": [
|
|
218
|
-
{
|
|
219
|
-
"type": "equals",
|
|
220
|
-
"name": "exactly one row, id 1",
|
|
221
|
-
"format": "rows",
|
|
222
|
-
"columns": [{ "name": "id", "cast_type": "INTEGER" }],
|
|
223
|
-
"rows": [{ "id": 1 }]
|
|
224
|
-
},
|
|
225
|
-
{
|
|
226
|
-
"type": "empty",
|
|
227
|
-
"name": "no null ids",
|
|
228
|
-
"sql": "SELECT * FROM public.adults WHERE id IS NULL"
|
|
229
|
-
}
|
|
230
|
-
]
|
|
231
|
-
}
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
Create and update bodies are closed at every level (the top level is checked before sending; a stray nested key is a 400) — strip `id`, `entity_id`, `creator_id`, `created_at` and `updated_at` from a `get --full` body before sending it back.
|
|
235
|
-
|
|
236
|
-
<!-- /requires -->
|
|
237
|
-
|
|
238
176
|
## Drop the materialized table (keep the transform)
|
|
239
177
|
|
|
240
178
|
```bash
|