@metabase/cli 0.1.10 → 0.1.12

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 (159) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/README.md +9 -6
  3. package/dist/add-collection-BPtBMh8Y.mjs +10 -0
  4. package/dist/{add-collection-DUqTrC5T.mjs → add-collection-DEME4IOy.mjs} +4 -4
  5. package/dist/{archive-_GMNY8wH.mjs → archive-BFk-oupo.mjs} +3 -3
  6. package/dist/{archive-44EWiXud.mjs → archive-BNioNJtG.mjs} +3 -3
  7. package/dist/{archive-BZfpjMir.mjs → archive-BtDvBsr8.mjs} +3 -3
  8. package/dist/{archive-0krZxAXq.mjs → archive-CMeTr8jv.mjs} +3 -3
  9. package/dist/{archive-BEIyIsin.mjs → archive-DUlrUlok.mjs} +3 -3
  10. package/dist/{archive-DtE2H4A6.mjs → archive-Dc-FwNZm.mjs} +3 -3
  11. package/dist/{archive-B59Y7ajB.mjs → archive-MTTQv3wR.mjs} +3 -3
  12. package/dist/auth-CjFnuPe9.mjs +19 -0
  13. package/dist/{body-BdRyuvU4.mjs → body-89w3r7In.mjs} +2 -2
  14. package/dist/{branches-Jpv-FNds.mjs → branches-DuVKH8Ye.mjs} +4 -4
  15. package/dist/{cancel-ChC4lFd4.mjs → cancel-BEMQKVuR.mjs} +3 -3
  16. package/dist/{cancel-task-DyhIkNaL.mjs → cancel-task-D2mSPFXO.mjs} +4 -4
  17. package/dist/card-DoDpwR4t.mjs +20 -0
  18. package/dist/{cards-O-nKkQKP.mjs → cards-L9jksUMD.mjs} +3 -3
  19. package/dist/cli.mjs +23 -23
  20. package/dist/collection-CF-1RIPY.mjs +20 -0
  21. package/dist/{collection-namespace-7724zUMx.mjs → collection-namespace-w26SavJH.mjs} +1 -1
  22. package/dist/{create-D45uFXlo.mjs → create-8hMjPz7j.mjs} +4 -4
  23. package/dist/{create-B4f4Pldw.mjs → create-Bu9Jayck.mjs} +5 -5
  24. package/dist/{create-BxRsXQrm.mjs → create-C3b3MoIN.mjs} +4 -4
  25. package/dist/{create-DeZ2x2Db.mjs → create-Cxg_h8Kf.mjs} +4 -4
  26. package/dist/{create-DS52EhPd.mjs → create-D6QqOX6u.mjs} +4 -4
  27. package/dist/{create-BbF9zVFP.mjs → create-DCYc032c.mjs} +5 -5
  28. package/dist/{create-BnFHcnlL.mjs → create-Dew3rKsR.mjs} +4 -4
  29. package/dist/{create-CvYKJOcE.mjs → create-DoeqRVmx.mjs} +4 -4
  30. package/dist/{create-branch-KOWUIE72.mjs → create-branch-C5P2y8U5.mjs} +4 -4
  31. package/dist/{create-BuKx7kw6.mjs → create-zEq4ZJAT.mjs} +4 -4
  32. package/dist/{current-task-ClcWPPMc.mjs → current-task-uD4QO5bM.mjs} +4 -4
  33. package/dist/dashboard-C5zTX345.mjs +21 -0
  34. package/dist/db-4y5Z1CQw.mjs +22 -0
  35. package/dist/{delete-D68oS73R.mjs → delete-Bftif2R3.mjs} +3 -3
  36. package/dist/{delete-CX2VUA5R.mjs → delete-DGaNAFdQ.mjs} +3 -3
  37. package/dist/{delete-table-DLvL9mDA.mjs → delete-table-C0qxP6FE.mjs} +3 -3
  38. package/dist/{dirty-1OrXpc7E.mjs → dirty-CZZLpWt1.mjs} +4 -4
  39. package/dist/document-97l9Non_.mjs +19 -0
  40. package/dist/{eid-CzLhHZMW.mjs → eid-C8xTmR8e.mjs} +4 -4
  41. package/dist/{error-BWXBhqLW.mjs → error-BGH4gxGN.mjs} +2 -1
  42. package/dist/{export-B5z8w-xo.mjs → export-DWdjnBKo.mjs} +6 -6
  43. package/dist/field-BwUTrelF.mjs +18 -0
  44. package/dist/{fields-sC7pzmPX.mjs → fields-Cq08PQec.mjs} +3 -3
  45. package/dist/{get-CHb6J908.mjs → get-7IhxPyE_.mjs} +2 -2
  46. package/dist/{get-BYw3xS0X.mjs → get-8nAjRL2H.mjs} +3 -3
  47. package/dist/{get-ByZ4HR2T.mjs → get-B2vGW7Aw.mjs} +3 -3
  48. package/dist/{get-BC60bhel.mjs → get-B_BJ10pV.mjs} +3 -3
  49. package/dist/{get-DLwb_gUh.mjs → get-BaKKF50e.mjs} +3 -3
  50. package/dist/{get-Dl62Fy6Y.mjs → get-Bbij6RjC.mjs} +3 -3
  51. package/dist/{get-reSMTfQi.mjs → get-Bcr_5fAb.mjs} +2 -2
  52. package/dist/{get-lcX52Skc.mjs → get-Cr8oz_jr.mjs} +3 -3
  53. package/dist/{get-DTHLETau.mjs → get-DHPN1716.mjs} +3 -3
  54. package/dist/{get-C6n86-dS.mjs → get-DtKv-gl3.mjs} +3 -3
  55. package/dist/{get-ZesERdyk.mjs → get-DzLAmSKd.mjs} +2 -2
  56. package/dist/{get-Bo1FGyFs.mjs → get-Rqz7UHv1.mjs} +3 -3
  57. package/dist/{get-4GEDd9YN.mjs → get-f5ENeUNw.mjs} +3 -3
  58. package/dist/{get-run-CpCbHJad.mjs → get-run-DhbHXaZE.mjs} +3 -3
  59. package/dist/{get-Fn9WkNhS.mjs → get-ug26gcOI.mjs} +3 -3
  60. package/dist/git-sync-JIMEYW-E.mjs +28 -0
  61. package/dist/{has-remote-changes-CPz_-uxd.mjs → has-remote-changes-CTLh3WjZ.mjs} +4 -4
  62. package/dist/{import-DaWgprK6.mjs → import-XakIQfRp.mjs} +6 -6
  63. package/dist/{input-xewHccej.mjs → input-BZwm4yby.mjs} +25 -3
  64. package/dist/is-dirty-4gf8ujws.mjs +9 -0
  65. package/dist/{is-dirty-DZlI7lQx.mjs → is-dirty-DkOOyjsZ.mjs} +3 -3
  66. package/dist/{items-hgbRYsYD.mjs → items-DC985V8K.mjs} +3 -3
  67. package/dist/{list-BgHESP7b.mjs → list-B83-hnq5.mjs} +2 -2
  68. package/dist/{list-BGerkRHH.mjs → list-BtNboYpZ.mjs} +3 -3
  69. package/dist/{list-BWv5y307.mjs → list-CEbMS6g3.mjs} +2 -2
  70. package/dist/{list-CKeVS1IZ.mjs → list-CIEI8XK_.mjs} +2 -2
  71. package/dist/{list-Cd2nOCAx.mjs → list-Cnb9mQdd.mjs} +2 -2
  72. package/dist/{list-B9J3ujwn.mjs → list-CtAFlkLe.mjs} +2 -2
  73. package/dist/{list-36H-dvJZ.mjs → list-CyIgwqFB.mjs} +2 -2
  74. package/dist/{list-CGdOC9zX.mjs → list-Df4MhR2A.mjs} +2 -2
  75. package/dist/{list-DO8T-nmF.mjs → list-DfPwBQrZ.mjs} +2 -2
  76. package/dist/{list-DBlsRSpZ.mjs → list-OGkVAG6b.mjs} +2 -2
  77. package/dist/{list-DOVX3vCb.mjs → list-PYNFVJGM.mjs} +3 -3
  78. package/dist/{list-kath_2cX.mjs → list-STlBcPs8.mjs} +2 -2
  79. package/dist/{list-CCZnH2-Z.mjs → list-gy64AAWO.mjs} +2 -2
  80. package/dist/{list-D9EuxFHO.mjs → list-xfmTxHNu.mjs} +2 -2
  81. package/dist/{login-BzrAGJfu.mjs → login-C0VFlX8y.mjs} +4 -4
  82. package/dist/{logout-CKBiltoS.mjs → logout-BUgUTFol.mjs} +2 -2
  83. package/dist/measure-BmcmxRQ3.mjs +19 -0
  84. package/dist/{metadata-BcGcUEVJ.mjs → metadata-B1EmV1l5.mjs} +3 -3
  85. package/dist/{metadata-BjOrKtnv.mjs → metadata-Dwkcsa1c.mjs} +3 -3
  86. package/dist/{parse-id--iVTCKSo.mjs → parse-id-sKNP6yu5.mjs} +1 -1
  87. package/dist/{path-LgGU6Bd0.mjs → path-BZJR7YLR.mjs} +2 -2
  88. package/dist/{poll-BucRFJT-.mjs → poll-Cyw-dQi4.mjs} +1 -1
  89. package/dist/{poll-task-DTzKB3T3.mjs → poll-task-BG0oVnfZ.mjs} +2 -2
  90. package/dist/{preflight-CzqVX0PP.mjs → preflight-BjR63JnX.mjs} +1 -1
  91. package/dist/{query-BVZkK6Qk.mjs → query-C2xqnz24.mjs} +3 -3
  92. package/dist/{query-DG_jygDF.mjs → query-Dk55Wp2p.mjs} +4 -4
  93. package/dist/{remove-collection-HdeAfLyi.mjs → remove-collection-DbVNWGyb.mjs} +6 -6
  94. package/dist/{rescan-values-hWubCruZ.mjs → rescan-values-CuLQ9UHP.mjs} +3 -3
  95. package/dist/{run-C1-lDmQF.mjs → run-PDN33QjN.mjs} +5 -5
  96. package/dist/{runs-Q6DYQyqj.mjs → runs-B9T-7lei.mjs} +3 -3
  97. package/dist/{runtime-colqvhLf.mjs → runtime-DgHh4T6t.mjs} +1 -1
  98. package/dist/{schema-tables-BEastV_8.mjs → schema-tables-DjmNwrlT.mjs} +3 -3
  99. package/dist/{schemas-CgawwI_k.mjs → schemas-CWL8gv1C.mjs} +3 -3
  100. package/dist/{search-BWo7xSPP.mjs → search-D7JzmvfR.mjs} +3 -3
  101. package/dist/segment-DrZNuxsM.mjs +19 -0
  102. package/dist/{set-7Nm2ZTb_.mjs → set-C0dCLnUA.mjs} +4 -4
  103. package/dist/{setting-DSGXJehQ.mjs → setting-Licy7ZSs.mjs} +3 -3
  104. package/dist/{setup-aJLGLrIT.mjs → setup-BL4fqxoU.mjs} +4 -4
  105. package/dist/{skills-Q2AFsYvc.mjs → skills-Cn8FMCxG.mjs} +3 -3
  106. package/dist/snippet-C5_4nPGj.mjs +19 -0
  107. package/dist/{stash-DPQ0c-Cd.mjs → stash-SGod4B8-.mjs} +6 -6
  108. package/dist/{status-CvKPrV5X.mjs → status-BCb5iCsQ.mjs} +5 -5
  109. package/dist/{status-CvAATvV0.mjs → status-CvUuZVvC.mjs} +2 -2
  110. package/dist/{summary-CeOnoOq2.mjs → summary-qaTPTLWs.mjs} +3 -3
  111. package/dist/{sync-schema-aOPBc3CY.mjs → sync-schema-CjjAYTbO.mjs} +5 -5
  112. package/dist/table-DK1B8Hzq.mjs +19 -0
  113. package/dist/transform-Dh5746zH.mjs +24 -0
  114. package/dist/transform-job-D0nRGPYN.mjs +19 -0
  115. package/dist/{tree-MOQOBeAP.mjs → tree-C0MFcweC.mjs} +2 -2
  116. package/dist/{update-BWyCK8QV.mjs → update-BImaz9vj.mjs} +6 -6
  117. package/dist/{update-B6mg3AZD.mjs → update-BTzsE8GU.mjs} +5 -5
  118. package/dist/{update-BRrnfG0q.mjs → update-BUoWwmQV.mjs} +5 -5
  119. package/dist/{update-CitS-QRN.mjs → update-BqsGGrzx.mjs} +6 -6
  120. package/dist/{update-57uxZWcR.mjs → update-CHUUY-IB.mjs} +5 -5
  121. package/dist/{update-Bj9s0ri8.mjs → update-CJASCsG4.mjs} +5 -5
  122. package/dist/{update-DfNKr_vS.mjs → update-DCR0wFNl.mjs} +5 -5
  123. package/dist/{update-BkMWBzvk.mjs → update-Dty6LvU2.mjs} +5 -5
  124. package/dist/{update-dashcard-D_-ura3Y.mjs → update-dashcard-BkJaVVvf.mjs} +5 -5
  125. package/dist/{update-BD9xkglP.mjs → update-qoP7hZTg.mjs} +5 -5
  126. package/dist/{update-Dri4Zg2H.mjs → update-vk_M0eOR.mjs} +5 -5
  127. package/dist/{upgrade-CFkZ4USY.mjs → upgrade-TdaiAYbH.mjs} +2 -2
  128. package/dist/{uuid-DpinhSxA.mjs → uuid-BeUz8VpP.mjs} +2 -2
  129. package/dist/{values-BSS4DRxk.mjs → values-DR-3HcK6.mjs} +3 -3
  130. package/dist/{verify-B_A7v8TY.mjs → verify-D2uBlvmt.mjs} +1 -1
  131. package/dist/{wait-D3iSnjMM.mjs → wait-FAnqO-LT.mjs} +5 -5
  132. package/dist/{wait-flags-_LnHOeBA.mjs → wait-flags-B5BI_xob.mjs} +2 -2
  133. package/package.json +2 -1
  134. package/skill-data/core/SKILL.md +22 -23
  135. package/skill-data/data-analysis/SKILL.md +65 -0
  136. package/skill-data/data-transformation/SKILL.md +200 -0
  137. package/skill-data/document/SKILL.md +4 -4
  138. package/skill-data/mbql/SKILL.md +20 -20
  139. package/skill-data/robot-data-engineer/SKILL.md +142 -0
  140. package/skill-data/semantic-layer/SKILL.md +166 -0
  141. package/skill-data/transform/SKILL.md +46 -48
  142. package/skill-data/visualization/SKILL.md +5 -3
  143. package/skills/metabase-cli/SKILL.md +6 -0
  144. package/dist/add-collection-D9wXgmRj.mjs +0 -10
  145. package/dist/auth-cFC5m69m.mjs +0 -19
  146. package/dist/card-ClvGX6dQ.mjs +0 -20
  147. package/dist/collection-DjvowSJC.mjs +0 -20
  148. package/dist/dashboard-BFeURTOw.mjs +0 -21
  149. package/dist/db-CSH1kwQr.mjs +0 -22
  150. package/dist/document-KdT_Xj6r.mjs +0 -19
  151. package/dist/field-CTFnZI8G.mjs +0 -18
  152. package/dist/git-sync-C2vib8rx.mjs +0 -28
  153. package/dist/is-dirty-Bb0Rtj7x.mjs +0 -9
  154. package/dist/measure-Dw1QpRZa.mjs +0 -19
  155. package/dist/segment-DMuYvFjg.mjs +0 -19
  156. package/dist/snippet-Df2TrP7-.mjs +0 -19
  157. package/dist/table-pK4OkVtL.mjs +0 -19
  158. package/dist/transform-D60veFH8.mjs +0 -24
  159. package/dist/transform-job-DXt5LsrY.mjs +0 -19
@@ -1,10 +1,10 @@
1
1
  import "./command-augment-BH9qgQ5u.mjs";
2
- import { connectionFlags, outputFlags, profileFlag } from "./error-BWXBhqLW.mjs";
3
- import { defineMetabaseCommand } from "./runtime-colqvhLf.mjs";
2
+ import { connectionFlags, outputFlags, profileFlag } from "./error-BGH4gxGN.mjs";
3
+ import { defineMetabaseCommand } from "./runtime-DgHh4T6t.mjs";
4
4
  import { renderSummary } from "./capabilities-7L9GVMd_.mjs";
5
- import "./input-xewHccej.mjs";
6
- import { parseId } from "./parse-id--iVTCKSo.mjs";
7
- import { readBody } from "./body-BdRyuvU4.mjs";
5
+ import "./input-BZwm4yby.mjs";
6
+ import { parseId } from "./parse-id-sKNP6yu5.mjs";
7
+ import { readBody } from "./body-89w3r7In.mjs";
8
8
  import { bodyInputFlags } from "./body-flags-D78h_-Ua.mjs";
9
9
  import { Snippet, SnippetUpdateInput, snippetView } from "./snippet-BZvo05ua.mjs";
10
10
 
@@ -1,16 +1,16 @@
1
1
  import "./command-augment-BH9qgQ5u.mjs";
2
- import { connectionFlags, outputFlags, profileFlag } from "./error-BWXBhqLW.mjs";
3
- import { defineMetabaseCommand } from "./runtime-colqvhLf.mjs";
2
+ import { connectionFlags, outputFlags, profileFlag } from "./error-BGH4gxGN.mjs";
3
+ import { defineMetabaseCommand } from "./runtime-DgHh4T6t.mjs";
4
4
  import { renderSummary } from "./capabilities-7L9GVMd_.mjs";
5
- import "./input-xewHccej.mjs";
5
+ import "./input-BZwm4yby.mjs";
6
6
  import "./field-MGxpNQUH.mjs";
7
7
  import "./card-DnIeMmUn.mjs";
8
- import { parseId } from "./parse-id--iVTCKSo.mjs";
9
- import { readBody } from "./body-BdRyuvU4.mjs";
8
+ import { parseId } from "./parse-id-sKNP6yu5.mjs";
9
+ import { readBody } from "./body-89w3r7In.mjs";
10
10
  import { bodyInputFlags } from "./body-flags-D78h_-Ua.mjs";
11
11
  import "./validate-B62TRDGV.mjs";
12
12
  import { DashboardDetail, DashboardUpdateInput, dashboardView } from "./dashboard-FY5UzJ_Z.mjs";
13
- import { preflightDashcardCardReferences } from "./preflight-CzqVX0PP.mjs";
13
+ import { preflightDashcardCardReferences } from "./preflight-BjR63JnX.mjs";
14
14
 
15
15
  //#region src/commands/dashboard/update.ts
16
16
  var update_default = defineMetabaseCommand({
@@ -1,10 +1,10 @@
1
1
  import "./command-augment-BH9qgQ5u.mjs";
2
- import { connectionFlags, outputFlags, profileFlag } from "./error-BWXBhqLW.mjs";
3
- import { defineMetabaseCommand } from "./runtime-colqvhLf.mjs";
2
+ import { connectionFlags, outputFlags, profileFlag } from "./error-BGH4gxGN.mjs";
3
+ import { defineMetabaseCommand } from "./runtime-DgHh4T6t.mjs";
4
4
  import { renderSummary } from "./capabilities-7L9GVMd_.mjs";
5
- import "./input-xewHccej.mjs";
6
- import { parseId } from "./parse-id--iVTCKSo.mjs";
7
- import { readBody } from "./body-BdRyuvU4.mjs";
5
+ import "./input-BZwm4yby.mjs";
6
+ import { parseId } from "./parse-id-sKNP6yu5.mjs";
7
+ import { readBody } from "./body-89w3r7In.mjs";
8
8
  import { bodyInputFlags } from "./body-flags-D78h_-Ua.mjs";
9
9
  import { Document, DocumentUpdateInput, documentView } from "./document-CcfiiV3b.mjs";
10
10
 
@@ -1,10 +1,10 @@
1
1
  import "./command-augment-BH9qgQ5u.mjs";
2
- import { connectionFlags, outputFlags, profileFlag } from "./error-BWXBhqLW.mjs";
3
- import { defineMetabaseCommand } from "./runtime-colqvhLf.mjs";
2
+ import { connectionFlags, outputFlags, profileFlag } from "./error-BGH4gxGN.mjs";
3
+ import { defineMetabaseCommand } from "./runtime-DgHh4T6t.mjs";
4
4
  import { renderSummary } from "./capabilities-7L9GVMd_.mjs";
5
- import "./input-xewHccej.mjs";
6
- import { parseId } from "./parse-id--iVTCKSo.mjs";
7
- import { readBody } from "./body-BdRyuvU4.mjs";
5
+ import "./input-BZwm4yby.mjs";
6
+ import { parseId } from "./parse-id-sKNP6yu5.mjs";
7
+ import { readBody } from "./body-89w3r7In.mjs";
8
8
  import { bodyInputFlags } from "./body-flags-D78h_-Ua.mjs";
9
9
  import { TransformJob, TransformJobUpdateInput, transformJobView } from "./transform-job-n8W6tLqF.mjs";
10
10
 
@@ -1,10 +1,10 @@
1
1
  import "./command-augment-BH9qgQ5u.mjs";
2
- import { connectionFlags, outputFlags, profileFlag } from "./error-BWXBhqLW.mjs";
3
- import { defineMetabaseCommand } from "./runtime-colqvhLf.mjs";
2
+ import { connectionFlags, outputFlags, profileFlag } from "./error-BGH4gxGN.mjs";
3
+ import { defineMetabaseCommand } from "./runtime-DgHh4T6t.mjs";
4
4
  import { renderSummary } from "./capabilities-7L9GVMd_.mjs";
5
- import "./input-xewHccej.mjs";
6
- import { parseId } from "./parse-id--iVTCKSo.mjs";
7
- import { readBody } from "./body-BdRyuvU4.mjs";
5
+ import "./input-BZwm4yby.mjs";
6
+ import { parseId } from "./parse-id-sKNP6yu5.mjs";
7
+ import { readBody } from "./body-89w3r7In.mjs";
8
8
  import { bodyInputFlags } from "./body-flags-D78h_-Ua.mjs";
9
9
  import "./validate-B62TRDGV.mjs";
10
10
  import { SEGMENT_DEFINITION_LABELS, preflightMbql5Query, skipValidateFlag } from "./validate-query-BpiN1CFu.mjs";
@@ -1,12 +1,12 @@
1
1
  import "./command-augment-BH9qgQ5u.mjs";
2
- import { connectionFlags, outputFlags, profileFlag } from "./error-BWXBhqLW.mjs";
3
- import { defineMetabaseCommand } from "./runtime-colqvhLf.mjs";
2
+ import { connectionFlags, outputFlags, profileFlag } from "./error-BGH4gxGN.mjs";
3
+ import { defineMetabaseCommand } from "./runtime-DgHh4T6t.mjs";
4
4
  import { renderSummary } from "./capabilities-7L9GVMd_.mjs";
5
- import "./input-xewHccej.mjs";
5
+ import "./input-BZwm4yby.mjs";
6
6
  import "./field-MGxpNQUH.mjs";
7
7
  import { Card, CardUpdateInput, cardView } from "./card-DnIeMmUn.mjs";
8
- import { parseId } from "./parse-id--iVTCKSo.mjs";
9
- import { readBody } from "./body-BdRyuvU4.mjs";
8
+ import { parseId } from "./parse-id-sKNP6yu5.mjs";
9
+ import { readBody } from "./body-89w3r7In.mjs";
10
10
  import { bodyInputFlags } from "./body-flags-D78h_-Ua.mjs";
11
11
  import "./validate-B62TRDGV.mjs";
12
12
  import { CARD_DATASET_QUERY_LABELS, preflightMbql5Query, skipValidateFlag } from "./validate-query-BpiN1CFu.mjs";
@@ -1,10 +1,10 @@
1
1
  import { ConfigError } from "./command-augment-BH9qgQ5u.mjs";
2
- import { connectionFlags, outputFlags, profileFlag } from "./error-BWXBhqLW.mjs";
3
- import { defineMetabaseCommand } from "./runtime-colqvhLf.mjs";
2
+ import { connectionFlags, outputFlags, profileFlag } from "./error-BGH4gxGN.mjs";
3
+ import { defineMetabaseCommand } from "./runtime-DgHh4T6t.mjs";
4
4
  import { renderSummary } from "./capabilities-7L9GVMd_.mjs";
5
- import "./input-xewHccej.mjs";
6
- import { parseId } from "./parse-id--iVTCKSo.mjs";
7
- import { readBody } from "./body-BdRyuvU4.mjs";
5
+ import "./input-BZwm4yby.mjs";
6
+ import { parseId } from "./parse-id-sKNP6yu5.mjs";
7
+ import { readBody } from "./body-89w3r7In.mjs";
8
8
  import { bodyInputFlags } from "./body-flags-D78h_-Ua.mjs";
9
9
  import { DashboardDetail, Dashcard, DashcardPatchInput, dashcardView } from "./dashboard-FY5UzJ_Z.mjs";
10
10
 
@@ -1,11 +1,11 @@
1
1
  import "./command-augment-BH9qgQ5u.mjs";
2
- import { connectionFlags, outputFlags, profileFlag } from "./error-BWXBhqLW.mjs";
3
- import { defineMetabaseCommand } from "./runtime-colqvhLf.mjs";
2
+ import { connectionFlags, outputFlags, profileFlag } from "./error-BGH4gxGN.mjs";
3
+ import { defineMetabaseCommand } from "./runtime-DgHh4T6t.mjs";
4
4
  import { renderSummary } from "./capabilities-7L9GVMd_.mjs";
5
- import "./input-xewHccej.mjs";
5
+ import "./input-BZwm4yby.mjs";
6
6
  import "./field-MGxpNQUH.mjs";
7
- import { parseId } from "./parse-id--iVTCKSo.mjs";
8
- import { readBody } from "./body-BdRyuvU4.mjs";
7
+ import { parseId } from "./parse-id-sKNP6yu5.mjs";
8
+ import { readBody } from "./body-89w3r7In.mjs";
9
9
  import { bodyInputFlags } from "./body-flags-D78h_-Ua.mjs";
10
10
  import { Table, TableUpdateInput, tableView } from "./table-CkWja2UH.mjs";
11
11
 
@@ -1,10 +1,10 @@
1
1
  import "./command-augment-BH9qgQ5u.mjs";
2
- import { connectionFlags, outputFlags, profileFlag } from "./error-BWXBhqLW.mjs";
3
- import { defineMetabaseCommand } from "./runtime-colqvhLf.mjs";
2
+ import { connectionFlags, outputFlags, profileFlag } from "./error-BGH4gxGN.mjs";
3
+ import { defineMetabaseCommand } from "./runtime-DgHh4T6t.mjs";
4
4
  import { renderSummary } from "./capabilities-7L9GVMd_.mjs";
5
- import "./input-xewHccej.mjs";
6
- import { parseId } from "./parse-id--iVTCKSo.mjs";
7
- import { readBody } from "./body-BdRyuvU4.mjs";
5
+ import "./input-BZwm4yby.mjs";
6
+ import { parseId } from "./parse-id-sKNP6yu5.mjs";
7
+ import { readBody } from "./body-89w3r7In.mjs";
8
8
  import { bodyInputFlags } from "./body-flags-D78h_-Ua.mjs";
9
9
  import "./validate-B62TRDGV.mjs";
10
10
  import { MEASURE_DEFINITION_LABELS, preflightMbql5Query, skipValidateFlag } from "./validate-query-BpiN1CFu.mjs";
@@ -1,6 +1,6 @@
1
1
  import { AbortError, NetworkError, TimeoutError, UnknownError, errorMessage, isNotFoundError } from "./command-augment-BH9qgQ5u.mjs";
2
- import { outputFlags, package_default } from "./error-BWXBhqLW.mjs";
3
- import { HttpError, USER_AGENT, combineAborts, defineMetabaseCommand, parseJson, throwIfAborted } from "./runtime-colqvhLf.mjs";
2
+ import { outputFlags, package_default } from "./error-BGH4gxGN.mjs";
3
+ import { HttpError, USER_AGENT, combineAborts, defineMetabaseCommand, parseJson, throwIfAborted } from "./runtime-DgHh4T6t.mjs";
4
4
  import { renderItem, writeText } from "./capabilities-7L9GVMd_.mjs";
5
5
  import { promptConfirm } from "./prompt-u4WhE4T5.mjs";
6
6
  import { z } from "zod";
@@ -1,6 +1,6 @@
1
1
  import { ConfigError } from "./command-augment-BH9qgQ5u.mjs";
2
- import { outputFlags } from "./error-BWXBhqLW.mjs";
3
- import { defineMetabaseCommand, parseInteger } from "./runtime-colqvhLf.mjs";
2
+ import { outputFlags } from "./error-BGH4gxGN.mjs";
3
+ import { defineMetabaseCommand, parseInteger } from "./runtime-DgHh4T6t.mjs";
4
4
  import { writeJson, writeText } from "./capabilities-7L9GVMd_.mjs";
5
5
  import { z } from "zod";
6
6
  import { randomUUID } from "node:crypto";
@@ -1,9 +1,9 @@
1
1
  import "./command-augment-BH9qgQ5u.mjs";
2
- import { connectionFlags, outputFlags, profileFlag } from "./error-BWXBhqLW.mjs";
3
- import { defineMetabaseCommand } from "./runtime-colqvhLf.mjs";
2
+ import { connectionFlags, outputFlags, profileFlag } from "./error-BGH4gxGN.mjs";
3
+ import { defineMetabaseCommand } from "./runtime-DgHh4T6t.mjs";
4
4
  import { formatScalar, renderSummary } from "./capabilities-7L9GVMd_.mjs";
5
5
  import { FieldValues, fieldValuesView } from "./field-MGxpNQUH.mjs";
6
- import { parseId } from "./parse-id--iVTCKSo.mjs";
6
+ import { parseId } from "./parse-id-sKNP6yu5.mjs";
7
7
 
8
8
  //#region src/commands/field/values.ts
9
9
  var values_default = defineMetabaseCommand({
@@ -1,5 +1,5 @@
1
1
  import { MetabaseError, NetworkError, TimeoutError, errorMessage } from "./command-augment-BH9qgQ5u.mjs";
2
- import { HttpError, createClient, probeServer } from "./runtime-colqvhLf.mjs";
2
+ import { HttpError, createClient, probeServer } from "./runtime-DgHh4T6t.mjs";
3
3
  import { z } from "zod";
4
4
 
5
5
  //#region src/domain/user.ts
@@ -1,10 +1,10 @@
1
1
  import "./command-augment-BH9qgQ5u.mjs";
2
- import { connectionFlags, outputFlags, profileFlag } from "./error-BWXBhqLW.mjs";
3
- import { defineMetabaseCommand } from "./runtime-colqvhLf.mjs";
2
+ import { connectionFlags, outputFlags, profileFlag } from "./error-BGH4gxGN.mjs";
3
+ import { defineMetabaseCommand } from "./runtime-DgHh4T6t.mjs";
4
4
  import { renderSummary } from "./capabilities-7L9GVMd_.mjs";
5
- import { parseId } from "./parse-id--iVTCKSo.mjs";
6
- import { DEFAULT_INTERVAL_MS, DEFAULT_TIMEOUT_MS } from "./poll-BucRFJT-.mjs";
7
- import { SyncTaskOrIdle, formatSyncTask, pollSyncTask, syncTaskIdleView, syncTaskView, throwIfFailedTask } from "./poll-task-DTzKB3T3.mjs";
5
+ import { parseId } from "./parse-id-sKNP6yu5.mjs";
6
+ import { DEFAULT_INTERVAL_MS, DEFAULT_TIMEOUT_MS } from "./poll-Cyw-dQi4.mjs";
7
+ import { SyncTaskOrIdle, formatSyncTask, pollSyncTask, syncTaskIdleView, syncTaskView, throwIfFailedTask } from "./poll-task-BG0oVnfZ.mjs";
8
8
 
9
9
  //#region src/commands/git-sync/wait.ts
10
10
  const WaitResult = SyncTaskOrIdle;
@@ -1,5 +1,5 @@
1
- import { parseId } from "./parse-id--iVTCKSo.mjs";
2
- import { DEFAULT_INTERVAL_MS, DEFAULT_TIMEOUT_MS } from "./poll-BucRFJT-.mjs";
1
+ import { parseId } from "./parse-id-sKNP6yu5.mjs";
2
+ import { DEFAULT_INTERVAL_MS, DEFAULT_TIMEOUT_MS } from "./poll-Cyw-dQi4.mjs";
3
3
 
4
4
  //#region src/commands/wait-flags.ts
5
5
  const waitScheduleFlags = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metabase/cli",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "description": "Metabase CLI",
5
5
  "license": "AGPL-3.0",
6
6
  "repository": {
@@ -37,6 +37,7 @@
37
37
  "typecheck": "tsc --noEmit",
38
38
  "lint": "oxlint",
39
39
  "lint:fix": "oxlint --fix",
40
+ "lint:skills": "uvx skillsaw lint skill-data/ --strict",
40
41
  "format": "oxfmt",
41
42
  "format:check": "oxfmt --check",
42
43
  "sync:representations": "bun run scripts/sync-representations.ts",
@@ -6,7 +6,7 @@ allowed-tools: Read, Write, Edit, Bash, AskUserQuestion
6
6
 
7
7
  # metabase-cli (core)
8
8
 
9
- The official Metabase CLI (`mb`) drives a Metabase instance over its REST API. It covers auth, list/get/create/update/delete on every resource, query and transform execution, content search, git-sync (representations ↔ instance), and entity-id translation.
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
 
@@ -15,7 +15,7 @@ auth | db | table | field | query | card | dashboard | snippet | segment | measu
15
15
  document | transform | transform-job | setting | search | git-sync | setup | eid | uuid | upgrade | skills
16
16
  ```
17
17
 
18
- The patterns below — auth, flag conventions, output flags, body input — apply across **every** group. Per-command flags, examples, and output schemas live in `mb __manifest` (see below). A few flows have their own specialized skills; load them on demand (see "Specialized skills"). Authoring any query body (cards, transforms, measures, segments, ad-hoc `mb query`) is one — load `mbql` whenever you build MBQL by hand. When a **question** (card) needs a query, prefer MBQL over native SQL — it's portable across warehouse engines and the CLI pre-flight-validates it. Try it first, but don't force it: fall back to native SQL when MBQL can't express the query, or when an MBQL body keeps failing server-side and you can't resolve it.
18
+ The patterns below — auth, flag conventions, output flags, body input — apply across **every** group. Per-command flags, examples, and output schemas live in `mb __manifest` (see below). A few flows have their own specialized skills (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
19
 
20
20
  ## Auth & profiles
21
21
 
@@ -29,11 +29,11 @@ mb auth status --json # → {profile, present, url} for the d
29
29
  mb auth status --profile <name> --json # → status of a specific profile
30
30
  ```
31
31
 
32
- `auth list` is the primary enumeration path — one call returns every configured profile with sanitized URL, an `authenticated` flag, and a probe `status` (`ok` / `auth-failed` / `network-error` / `server-error` / `not-probed`). Use it before asking the user which profile to pick. If it returns an empty `data: []`, ask the user to run `mb auth login` themselves (see the policy above) and tell you the profile name. `auth status` is a single-profile health probe when you already know the name.
32
+ `auth list` is the primary enumeration path — one call returns every configured profile with sanitized URL, an `authenticated` flag, and a probe `status` (`ok` / `auth-failed` / `network-error` / `server-error` / `not-probed`). Use it before asking which profile to pick. If it returns an empty `data: []`, ask the user to run `mb auth login` themselves (see the policy above) and tell you the profile name. `auth status` is a single-profile health probe when you already know the name.
33
33
 
34
34
  ### Pick the profile to use
35
35
 
36
- If exactly one profile is configured and the user's intent doesn't disambiguate, use it. If multiple profiles exist and the user hasn't named one, ask via `AskUserQuestion`, presenting the names from `auth list`. Once a name is established, pass `--profile <name>` to **every** subsequent command. Profile names are arbitrary local labels — `prod`, `staging` — let the user pick.
36
+ If exactly one profile is configured and intent doesn't disambiguate, use it. If multiple exist and the user hasn't named one, ask via `AskUserQuestion`, presenting the names from `auth list`. Once a name is established, pass `--profile <name>` to **every** subsequent command. Profile names are arbitrary local labels — `prod`, `staging` — let the user pick.
37
37
 
38
38
  ## Flag conventions
39
39
 
@@ -52,14 +52,12 @@ If exactly one profile is configured and the user's intent doesn't disambiguate,
52
52
 
53
53
  ### Some outputs are JSON envelopes, not bare strings
54
54
 
55
- A handful of "lookup" verbs return a JSON object even when you only want a single field. `mb setting get <key>` returns `{"key": "...", "value": ...}`, not the bare value. Don't drop them raw into another flag — extract:
55
+ A handful of "lookup" verbs return a JSON object even for a single field. `mb setting get <key>` returns `{"key": "...", "value": ...}`, not the bare value. Extract before reusing:
56
56
 
57
57
  ```bash
58
58
  VALUE=$(mb setting get <key> --json | jq -r '.value')
59
59
  ```
60
60
 
61
- If you find yourself piping a `--json` envelope straight into another flag and the receiving command rejects it, this is what happened.
62
-
63
61
  ## Output
64
62
 
65
63
  Every list/get verb supports the same output flags:
@@ -94,26 +92,28 @@ Verbs that take a payload accept it from one of four sources, **first non-empty
94
92
  3. stdin (auto-detected when piped, or explicit `--stdin` where supported)
95
93
  4. positional argument
96
94
 
97
- Picking exactly one is required; passing two of `--body` + `--file` + `--stdin` is rejected with a `ConfigError`.
95
+ Exactly one required; passing two of `--body` + `--file` + `--stdin` is rejected with a `ConfigError`.
98
96
 
99
97
  ```bash
100
- cat > /tmp/body.json <<'EOF'
98
+ cat > ./.scratch/body.json <<'EOF'
101
99
  { ... }
102
100
  EOF
103
- mb <noun> create --file /tmp/body.json --profile <n> --json
101
+ mb <noun> create --file ./.scratch/body.json --profile <n> --json
104
102
  ```
105
103
 
106
104
  Single-quoted `'EOF'` prevents the shell from interpolating `$vars` inside the JSON.
107
105
 
106
+ Write these 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.
107
+
108
108
  ## Discover the full surface: `mb __manifest`
109
109
 
110
- For the canonical, machine-readable inventory of every command — name, description, per-command `details`, examples, every flag with type and default, and the output JSON Schema — run:
110
+ 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:
111
111
 
112
112
  ```bash
113
113
  mb __manifest
114
114
  ```
115
115
 
116
- The leading `__` hides it from `--help`, but it's stable. Reach for it instead of `--help` per command. It pairs with `jq`:
116
+ The leading `__` hides it from `--help`, but it's stable. Reach for it instead of `--help` per command — to enumerate verbs, validate flag names before constructing a command, or read an output schema before parsing. Pairs with `jq`:
117
117
 
118
118
  ```bash
119
119
  mb __manifest | jq -r '.commands[].command' # every command name
@@ -122,43 +122,42 @@ mb __manifest | jq '.commands[] | select(.command == "card query") | .args'
122
122
  mb __manifest | jq '.commands[] | select(.command == "card list") | .outputSchema' # output schema before parsing
123
123
  ```
124
124
 
125
- Use it to (a) enumerate verbs, (b) validate flag names before constructing a command, (c) read an output schema before parsing.
126
-
127
125
  ## Resource quirks worth memorizing
128
126
 
129
- Routine verb shapes (list / get / create / update), every flag, and output JSON Schemas live in `mb __manifest` — pull them on demand. Below is only what the manifest does _not_ tell you: the footguns and non-obvious behaviors.
127
+ Routine verb shapes (list / get / create / update), every flag, and output JSON Schemas live in `mb __manifest` — pull on demand. Below is only what the manifest does _not_ tell you: footguns and non-obvious behaviors.
130
128
 
131
129
  - **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`.
132
130
  - **table fields.** `table get` never returns fields on its own — pass `--include fields` (compact) or use `table fields <id>` (list envelope). `table metadata <id>` adds FKs + dimensions (heavier). `table update` patches table-level metadata only; physical columns aren't editable here.
133
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.
134
132
  - **card.** `dataset_query` is the **flat** `mbql/query` value, not a legacy `{type:"query",query:…}` envelope (→ `mbql` skill). `--export-format csv|xlsx` streams the raw export (pipe to a file), bypassing the JSON envelope. `archive` is the only delete; unarchive with `update --body '{"archived":false}'`. `visualization_settings` keys are scoped by `display` and aren't pre-flighted — see the `viz` skill.
135
- - **dashboard.** Dashcards round-trip through `PUT /api/dashboard/:id` (no per-dashcard endpoint): `update-dashcard <dash-id> <dashcard-id>` patches one safely; `update --body '{"dashcards":[…]}'` replaces the whole set (omitted ids are deleted server-side; use negative ids for new cards). `create` accepts the **same** `dashcards` array in its initial body, so you can 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` against live server state and exit **2** with `{ok:false,errors:[…]}` on a bad ref — non-bypassable (no `--skip-validate`). `dashboard get <id>` (or `--full`) hydrates dashcards/tabs; `list` omits them. **Dashcard geometry: the grid is 24 columns wide.** Each dashcard's `{col, row, size_x, size_y}` is in grid units — `col` (0-indexed, left edge) and `size_x` are columns, `row`/`size_y` are rows; **full-width is `size_x: 24`** (`size_x: 12` is half a row — the usual cause of a card that only fills half the width, since it's a common per-chart default). Keep `col + size_x ≤ 24`, start each card's `col` at 0 for a full-width stack, and don't overlap cards (the server stores whatever you send — it won't auto-fix collisions).
133
+ - **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` against live server state and exit **2** with `{ok:false,errors:[…]}` on a bad ref — non-bypassable (no `--skip-validate`). `dashboard get <id>` (or `--full`) hydrates dashcards/tabs; `list` omits them. **Dashcard geometry: the grid is 24 columns wide.** Each dashcard's `{col, row, size_x, size_y}` is in grid units — `col` (0-indexed, left edge) and `size_x` are columns, `row`/`size_y` are rows; **full-width is `size_x: 24`** (`size_x: 12` is half a row — the usual cause of a card filling only half the width, since it's a common per-chart default). Keep `col + size_x ≤ 24`, start each card's `col` at 0 for a full-width stack, and don't overlap cards (the server stores whatever you send — it won't auto-fix collisions).
136
134
  - **snippet `--archived` is a swap, not a union** — list returns _either_ active _or_ archived rows, never both. (Same shape for `--filter archived` on dashboard/collection.)
137
135
  - **segment / measure** `update` and `archive` require a non-blank `revision_message` (audit-logged); the CLI does not synthesize it on `update`. `archive` defaults to `"Archived via mb CLI"` — override with `--revision-message`. `definition` is a flat MBQL clause (→ `mbql` skill): segment = a filter, measure = exactly one aggregation.
138
136
  - **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.
139
137
  - **setting set** parses the value as **strict JSON**: a string is `'"value"'` (inner quotes), booleans `true`/`false`, numbers bare. Wrong quoting silently errors — confirm with `setting get <key>` after. `setting get --json` works on every value type (it wraps bare-text responses into `{key, value}`).
140
138
  - **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.
141
139
  - **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 run's output table to register and returns `target_table_id`) or you get only `{run_id, final:null}`. (→ `transform` skill.)
142
- - **setup is one-shot.** `mb setup` walks `/api/setup` for a **fresh** instance only — it errors against an already-configured one. Mostly for bootstrapping local / e2e instances.
140
+ - **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.
143
141
  - **eid** translates a string entity id → numeric id: `mb eid --model <model> <eid1,eid2> --json` (EIDs are a positional used with `--model`; or pass `--body '{"entity_ids":{"card":["…"]}}'`). Entity ids are NanoIDs that can start with `-`, which the positional form misreads as a flag (shell quotes don't help — the `-` survives into argv). For an id that may start with `-`, use `--body` — the id is a JSON string value, immune to flag parsing: `mb eid --body '{"entity_ids":{"card":["-…"]}}'`. Useful when an external system hands you an entity id and a verb needs the numeric one.
144
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 every MBQL 5 clause needs. Both workflows live in the `mbql` skill.
145
143
 
146
144
  ## Specialized skills (load on demand)
147
145
 
148
- This core file is enough for any single-command task. Load the relevant skill **proactively** when intent matches — don't wing an MBQL body, a transform body, or the git-sync workflow from this overview alone. Load each via `mb skills get <name>`.
146
+ This core file is enough for any single-command task. Load the relevant skill **proactively** when intent matches — don't wing an MBQL body, a transform body, or the git-sync workflow from this overview alone. Load via `mb skills get <name>`.
147
+
148
+ **Start here for anything bigger than one command.** If the user wants an outcome rather than a single verb — "make sense of my data", "build a data model", "go from raw data to a dashboard", "be my data analyst", "set up analytics for X", "answer questions about my data" — load `robot-data-engineer` first and let it route. The rest of this list is the toolbox it routes into.
149
149
 
150
+ - **`robot-data-engineer`** — the front-door router for the whole journey (raw data → clean tables → reusable definitions → dashboards or written answers) for a non-technical user. Detects where the user is, sets up auth and autonomy once, and routes to `data-transformation` / `semantic-layer` / `visualization` / `data-analysis`. Load this when the user describes a goal, not a step.
150
151
  - **`mbql`** — authoring or fixing any MBQL query body: `mb query`, a card `dataset_query`, a transform `source.query`, a measure/segment `definition`, "aggregate and group by", reading `--dry-run` errors. The query-body reference.
151
152
  - **`viz`** — choosing a card's `display` and authoring `visualization_settings`: "make it a bar chart", "set the pie dimension/metric", "format this column as currency", "the card renders as a table instead of a chart". The presentation counterpart to `mbql`.
152
153
  - **`transform`** — "create a transform", "run a transform", authoring transform body JSON, run inspection.
154
+ - **`data-transformation`** — the higher-level workflow: turning a raw, normalized source database into a small set of clean, wide, analysis-ready tables for a non-technical user — "clean up", "flatten", "denormalize", "make sense of this database", "build analysis-ready tables". Wraps `transform` (the mechanics) with the investigate → propose → build flow.
155
+ - **`semantic-layer`** — turning clean tables into reusable definitions: "make this filter reusable", "define active customers / net revenue / MRR officially", "create a segment / measure / metric", "so everyone uses the same definition". Builds on `mbql` (the definition bodies) and `transform` (widen a table first when a definition needs more than one).
153
156
  - **`git-sync`** — "import the latest changes", "export to git", "git sync", "dirty check", "stash before pulling".
154
157
 
155
158
  If a task spans more than one, load each. Specialized skills assume the conventions above and won't repeat them. `mb skills list` enumerates everything on the installed version.
156
159
 
157
160
  ## Don't
158
161
 
159
- - **Don't run `mb auth login` for the user** — authentication is theirs (see §Auth).
160
162
  - Don't paste credentials or warehouse passwords in chat. Have the user run the storing command.
161
- - Don't put `--profile` before the verb chain — the CLI parses it as a subcommand and errors out.
162
- - Don't omit `--wait` on `transform run` / `git-sync import` for interactive flows; the next step will race the operation.
163
- - Don't drop a JSON-envelope verb's output raw into another flag. Extract with `--json | jq -r '.<field>'`.
164
- - Don't add a third-party HTTP library or shell into `curl` against `/api/...` when a `mb <verb>` exists — that bypasses retries, schema validation, and credential redaction.
163
+ - Don't shell into `curl` against `/api/...` (or add an HTTP library) when a `mb <verb>` exists — that bypasses retries, schema validation, and credential redaction.
@@ -0,0 +1,65 @@
1
+ ---
2
+ name: data-analysis
3
+ description: Answer real questions from clean, analysis-ready tables and hand back a plain-language report - an answer-finding task, not chart-building. Read the tables, turn the user's question into queries, run them on the live instance, sanity-check the numbers, write up findings the user can trust. Works over already-clean (wide, human-readable) data - survey/registration answers, event signups, customer lists, anything where the data holds the answer. Use when someone wants to "answer questions about my data", "report on who registered / signed up / responded", "what did people say", "analyze X", "explore this data", or "build me a report". For a non-technical user who knows their domain. Needs charts/dashboards? Use `visualization`. Tables still raw? Use `data-transformation` first.
4
+ allowed-tools: Read, Write, Edit, Bash, AskUserQuestion
5
+ ---
6
+
7
+ # Data Analysis
8
+
9
+ > **Shared contract (read first).** This skill is part of the `robot-data-engineer` family and follows its shared rules: audience is a non-technical user, so no database jargon (skip "normalize"/"grain"; ERD/foreign key are fine; explain "wide"/"long" the first time you use them). Ask before showing PII row-by-row (names, emails, phones) — default to aggregates. When asked for something the CLI can't do (alerts, dashboard filters), name the limit instead of erroring into raw SQL. Honor the autonomy mode the user picked. Full text and the autonomy slider live in the router — run `mb skills get robot-data-engineer` and read its **Shared Contract** if you haven't.
10
+
11
+ The user has a question and clean data that already holds the answer. Your job: find the answer, check it's right, and hand it back in plain language. You're an analyst, not a dashboard builder — the deliverable is a **trustworthy written answer**, optionally backed by a saved question they can re-open.
12
+
13
+ This skill assumes the tables are already clean (wide, human-readable). If they're raw and normalized — lots of `*_field`/`*_choice` lookups, coded columns, JSON blobs — stop and route to `data-transformation` first; don't analyze on top of a mess.
14
+
15
+ ---
16
+
17
+ ## The loop
18
+
19
+ For each question the user asks:
20
+
21
+ 1. **Find where the answer lives.** List tables (`mb table list`, `mb db schema-tables <db> <schema>`). Read the columns (`mb table fields <id>`). Clean datasets often ship the same facts two ways — a **wide** table (one row per thing, easy to read) and a **long** table (one row per attribute, easy to aggregate over many-valued answers). Pick the one that fits the question: per-person facts → wide; "which option was most popular" across a multi-select → long.
22
+
23
+ 2. **Turn the question into a query.** Write it, run it (`mb query`). Start small — a `count(*)` and a couple of sample rows to confirm you're pointed at the right table and the columns mean what you think. Then write the real query.
24
+
25
+ 3. **Sanity-check before you believe it.** A number with no cross-check is a guess. Confirm row counts against a total you trust, watch for nulls/blanks inflating or deflating a percentage, and re-read the column you grouped on — a `type/Category` column with "confirmed"/"cancelled" means your "how many registered" answer depends on which statuses you counted. State the denominator.
26
+
27
+ 4. **Report in plain language.** Lead with the answer, then how you got it. Numbers get context ("9 of 10 confirmed"), not bare figures. For free-text answers, quote a few real responses rather than only counting them — the words are the value.
28
+
29
+ ---
30
+
31
+ ## What to ask the user up front
32
+
33
+ Don't over-interrogate, but settle the things that change the answer:
34
+
35
+ - **Scope.** All-time or a window? Everyone, or only confirmed/active? A "how many registered" with no status filter and a "how many _confirmed_" are different numbers — pick the one they mean, and say which you used.
36
+ - **Cut.** Do they want the headline number, or the number broken down (by role, by company, by version)? A breakdown is usually one `GROUP BY` away and far more useful.
37
+ - **Form of the answer.** A number in chat? A short written digest? A saved question they can re-open and refilter? If they want something durable or visual, that's the `visualization` skill — hand off.
38
+
39
+ When genuinely unsure which interpretation they mean, ask — never silently pick one and present it as the answer.
40
+
41
+ ---
42
+
43
+ ## Survey / registration data — the common shape
44
+
45
+ A lot of "analyze who registered / what did people say" work lands on event or survey data, which has a recognizable shape worth calling out:
46
+
47
+ - A **per-registrant wide table** — name, company, role, status, plus one column per single-answer question. Use it for "who registered", rosters, breakdowns by role/version/company, and any per-person filter.
48
+ - A **long answers table** — one row per (registrant, question, answer). Use it for **multi-select** questions (one person picks several options, so they can't flatten into one wide column) and for "which option was chosen most". Group by the question text, then by the answer value.
49
+ - **Question definitions** — the catalog of what was asked, the answer choices, free-text vs single vs multi. Read this first to know which questions exist and how each is typed before you start counting.
50
+
51
+ Three report families cover most asks:
52
+
53
+ 1. **Roster** — who registered, with the facts that matter (company, role, status). A filtered, ordered read of the wide table.
54
+ 2. **Distribution** — how the group splits on a single-select (role, version, customer-or-not). A `GROUP BY` with counts; the agent-facing answer is "X% picked A, Y% picked B".
55
+ 3. **Open-ended digest** — what people said in free-text ("what do you want to learn / teach / discuss"). Small N usually — list the actual answers, don't just count them; the responses are the point.
56
+
57
+ ---
58
+
59
+ ## Don't
60
+
61
+ - **Don't analyze raw, un-cleaned tables.** If the data is normalized/coded/JSON, route to `data-transformation` first and analyze the clean output.
62
+ - **Don't report a number you didn't sanity-check.** No denominator, no null-check → no answer.
63
+ - **Don't silently pick a scope.** "Registered" vs "confirmed", all-time vs window — state which you used, or ask.
64
+ - **Don't build charts/dashboards here.** A written answer (and maybe one saved question) is the deliverable; if they want it visual, that's `visualization`.
65
+ - **Don't only count free-text.** Quote the real responses — the words carry the insight a count throws away.