@sdt-tools/cli 0.2.0 → 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/dist/advise-tests-6DRSZMBL.js +87 -0
  2. package/dist/advise-tests-6DRSZMBL.js.map +1 -0
  3. package/dist/ai-G4MJWHTM.js +89 -0
  4. package/dist/ai-G4MJWHTM.js.map +1 -0
  5. package/dist/anonymize-QR6JGXA7.js +123 -0
  6. package/dist/anonymize-QR6JGXA7.js.map +1 -0
  7. package/dist/approval-YVHYTV53.js +73 -0
  8. package/dist/approval-YVHYTV53.js.map +1 -0
  9. package/dist/approval-chain-54KKJZS3.js +120 -0
  10. package/dist/approval-chain-54KKJZS3.js.map +1 -0
  11. package/dist/audit-log-QZFH7LUX.js +159 -0
  12. package/dist/audit-log-QZFH7LUX.js.map +1 -0
  13. package/dist/backlog-V2YUIQDL.js +76 -0
  14. package/dist/backlog-V2YUIQDL.js.map +1 -0
  15. package/dist/bisect-GEVYAVL5.js +111 -0
  16. package/dist/bisect-GEVYAVL5.js.map +1 -0
  17. package/dist/bookmarks-57LKS7P6.js +107 -0
  18. package/dist/bookmarks-57LKS7P6.js.map +1 -0
  19. package/dist/branch-W2MGMPSH.js +88 -0
  20. package/dist/branch-W2MGMPSH.js.map +1 -0
  21. package/dist/build-VNIQFKSP.js +23 -0
  22. package/dist/build-VNIQFKSP.js.map +1 -0
  23. package/dist/catalog-JLB5VCEV.js +137 -0
  24. package/dist/catalog-JLB5VCEV.js.map +1 -0
  25. package/dist/changelog-M7XGDYSY.js +220 -0
  26. package/dist/changelog-M7XGDYSY.js.map +1 -0
  27. package/dist/chunk-DGUM43GV.js +11 -0
  28. package/dist/chunk-DGUM43GV.js.map +1 -0
  29. package/dist/chunk-EWXM4KJN.js +25 -0
  30. package/dist/chunk-EWXM4KJN.js.map +1 -0
  31. package/dist/chunk-JP2EZLR5.js +50 -0
  32. package/dist/chunk-JP2EZLR5.js.map +1 -0
  33. package/dist/chunk-VM2H4LAO.js +15 -0
  34. package/dist/chunk-VM2H4LAO.js.map +1 -0
  35. package/dist/chunk-ZWY4ZRHL.js +44 -0
  36. package/dist/chunk-ZWY4ZRHL.js.map +1 -0
  37. package/dist/cli.js +511 -19014
  38. package/dist/cli.js.map +1 -1
  39. package/dist/compare-5O6UTWPJ.js +405 -0
  40. package/dist/compare-5O6UTWPJ.js.map +1 -0
  41. package/dist/compare-profiles-7ZSNIW7B.js +218 -0
  42. package/dist/compare-profiles-7ZSNIW7B.js.map +1 -0
  43. package/dist/completion-I5U5VVAX.js +82 -0
  44. package/dist/completion-I5U5VVAX.js.map +1 -0
  45. package/dist/connection-GNTZDHXF.js +133 -0
  46. package/dist/connection-GNTZDHXF.js.map +1 -0
  47. package/dist/cost-estimate-TJDDH6TO.js +328 -0
  48. package/dist/cost-estimate-TJDDH6TO.js.map +1 -0
  49. package/dist/data-compare-UK2UXAS3.js +134 -0
  50. package/dist/data-compare-UK2UXAS3.js.map +1 -0
  51. package/dist/data-fit-Q45ENBRL.js +125 -0
  52. package/dist/data-fit-Q45ENBRL.js.map +1 -0
  53. package/dist/deploy-status-UUHKVDTI.js +58 -0
  54. package/dist/deploy-status-UUHKVDTI.js.map +1 -0
  55. package/dist/design-PO6UPBL7.js +138 -0
  56. package/dist/design-PO6UPBL7.js.map +1 -0
  57. package/dist/diagnose-6IFMELFR.js +145 -0
  58. package/dist/diagnose-6IFMELFR.js.map +1 -0
  59. package/dist/discover-A7OSZAHK.js +78 -0
  60. package/dist/discover-A7OSZAHK.js.map +1 -0
  61. package/dist/docs-CVRKGUSW.js +177 -0
  62. package/dist/docs-CVRKGUSW.js.map +1 -0
  63. package/dist/drift-XDA3BDYN.js +226 -0
  64. package/dist/drift-XDA3BDYN.js.map +1 -0
  65. package/dist/drift-gate-V7QSIOGZ.js +94 -0
  66. package/dist/drift-gate-V7QSIOGZ.js.map +1 -0
  67. package/dist/error-lookup-7ZWCZJ44.js +56 -0
  68. package/dist/error-lookup-7ZWCZJ44.js.map +1 -0
  69. package/dist/errorReporting-AQXKKGZH.js +109 -0
  70. package/dist/errorReporting-AQXKKGZH.js.map +1 -0
  71. package/dist/exec-PKBHLI7T.js +121 -0
  72. package/dist/exec-PKBHLI7T.js.map +1 -0
  73. package/dist/explain-LWKJOTL7.js +192 -0
  74. package/dist/explain-LWKJOTL7.js.map +1 -0
  75. package/dist/explorer-QOVM6VBD.js +61 -0
  76. package/dist/explorer-QOVM6VBD.js.map +1 -0
  77. package/dist/export-IYYBZ5HE.js +42 -0
  78. package/dist/export-IYYBZ5HE.js.map +1 -0
  79. package/dist/extract-VMMVRQVT.js +102 -0
  80. package/dist/extract-VMMVRQVT.js.map +1 -0
  81. package/dist/features-LE6BDZ2S.js +59 -0
  82. package/dist/features-LE6BDZ2S.js.map +1 -0
  83. package/dist/feedback-M7DM2EQC.js +161 -0
  84. package/dist/feedback-M7DM2EQC.js.map +1 -0
  85. package/dist/find-EME2JG2I.js +176 -0
  86. package/dist/find-EME2JG2I.js.map +1 -0
  87. package/dist/format-TRLWLMGS.js +141 -0
  88. package/dist/format-TRLWLMGS.js.map +1 -0
  89. package/dist/generate-6NAZGZDV.js +152 -0
  90. package/dist/generate-6NAZGZDV.js.map +1 -0
  91. package/dist/graph-QNQDAUO7.js +161 -0
  92. package/dist/graph-QNQDAUO7.js.map +1 -0
  93. package/dist/history-RONA7ZTI.js +199 -0
  94. package/dist/history-RONA7ZTI.js.map +1 -0
  95. package/dist/hosts-YBXY2ZG5.js +49 -0
  96. package/dist/hosts-YBXY2ZG5.js.map +1 -0
  97. package/dist/impact-T2JSANHS.js +59 -0
  98. package/dist/impact-T2JSANHS.js.map +1 -0
  99. package/dist/import-AELYLY6A.js +32 -0
  100. package/dist/import-AELYLY6A.js.map +1 -0
  101. package/dist/import-script-2OF5BI6A.js +83 -0
  102. package/dist/import-script-2OF5BI6A.js.map +1 -0
  103. package/dist/index.cjs +71 -12
  104. package/dist/index.cjs.map +1 -1
  105. package/dist/index.js +95 -31
  106. package/dist/index.js.map +1 -1
  107. package/dist/init-SWRRJMGI.js +57 -0
  108. package/dist/init-SWRRJMGI.js.map +1 -0
  109. package/dist/install-hooks-6SIAGTAF.js +109 -0
  110. package/dist/install-hooks-6SIAGTAF.js.map +1 -0
  111. package/dist/license-OAF22PLZ.js +46 -0
  112. package/dist/license-OAF22PLZ.js.map +1 -0
  113. package/dist/lineage-EW66XJ6O.js +552 -0
  114. package/dist/lineage-EW66XJ6O.js.map +1 -0
  115. package/dist/lint-FQ2OTYTQ.js +143 -0
  116. package/dist/lint-FQ2OTYTQ.js.map +1 -0
  117. package/dist/mcp-SARDMCDV.js +344 -0
  118. package/dist/mcp-SARDMCDV.js.map +1 -0
  119. package/dist/migrate-from-dbt-JVTXPWKQ.js +156 -0
  120. package/dist/migrate-from-dbt-JVTXPWKQ.js.map +1 -0
  121. package/dist/migrate-platform-NTRTOGNR.js +91 -0
  122. package/dist/migrate-platform-NTRTOGNR.js.map +1 -0
  123. package/dist/optimize-CJYWMAWA.js +105 -0
  124. package/dist/optimize-CJYWMAWA.js.map +1 -0
  125. package/dist/perf-LL2CPCJF.js +205 -0
  126. package/dist/perf-LL2CPCJF.js.map +1 -0
  127. package/dist/pii-FBDRDQ2E.js +136 -0
  128. package/dist/pii-FBDRDQ2E.js.map +1 -0
  129. package/dist/pilot-CCQERKPH.js +29 -0
  130. package/dist/pilot-CCQERKPH.js.map +1 -0
  131. package/dist/pr-comment-S5FF4QRX.js +79 -0
  132. package/dist/pr-comment-S5FF4QRX.js.map +1 -0
  133. package/dist/preview-5U4YVCRM.js +47 -0
  134. package/dist/preview-5U4YVCRM.js.map +1 -0
  135. package/dist/profile-7VC57KD2.js +101 -0
  136. package/dist/profile-7VC57KD2.js.map +1 -0
  137. package/dist/promote-AASEFTIA.js +408 -0
  138. package/dist/promote-AASEFTIA.js.map +1 -0
  139. package/dist/publish-UMVIWH6H.js +721 -0
  140. package/dist/publish-UMVIWH6H.js.map +1 -0
  141. package/dist/purge-QMXZKCMD.js +57 -0
  142. package/dist/purge-QMXZKCMD.js.map +1 -0
  143. package/dist/query-log-6OM4GI7W.js +112 -0
  144. package/dist/query-log-6OM4GI7W.js.map +1 -0
  145. package/dist/refactor-LTZQLJ35.js +5799 -0
  146. package/dist/refactor-LTZQLJ35.js.map +1 -0
  147. package/dist/refresh-4TY2AGOU.js +38 -0
  148. package/dist/refresh-4TY2AGOU.js.map +1 -0
  149. package/dist/replay-OOC25FZN.js +117 -0
  150. package/dist/replay-OOC25FZN.js.map +1 -0
  151. package/dist/revert-ODMUVJW6.js +110 -0
  152. package/dist/revert-ODMUVJW6.js.map +1 -0
  153. package/dist/review-XXPWOBFP.js +158 -0
  154. package/dist/review-XXPWOBFP.js.map +1 -0
  155. package/dist/rollback-suggest-6G2HEKFR.js +79 -0
  156. package/dist/rollback-suggest-6G2HEKFR.js.map +1 -0
  157. package/dist/safer-alternative-QFVNLG3L.js +89 -0
  158. package/dist/safer-alternative-QFVNLG3L.js.map +1 -0
  159. package/dist/safety-7QWRSUEZ.js +168 -0
  160. package/dist/safety-7QWRSUEZ.js.map +1 -0
  161. package/dist/savings-RHIXP6IT.js +95 -0
  162. package/dist/savings-RHIXP6IT.js.map +1 -0
  163. package/dist/scan-secrets-5YCQ4UCU.js +54 -0
  164. package/dist/scan-secrets-5YCQ4UCU.js.map +1 -0
  165. package/dist/schema-CIZXCQD2.js +429 -0
  166. package/dist/schema-CIZXCQD2.js.map +1 -0
  167. package/dist/script-K7CIN2P6.js +153 -0
  168. package/dist/script-K7CIN2P6.js.map +1 -0
  169. package/dist/search-BUZ5NXZZ.js +151 -0
  170. package/dist/search-BUZ5NXZZ.js.map +1 -0
  171. package/dist/seed-76QAK276.js +96 -0
  172. package/dist/seed-76QAK276.js.map +1 -0
  173. package/dist/sketch-PTLKDIK3.js +88 -0
  174. package/dist/sketch-PTLKDIK3.js.map +1 -0
  175. package/dist/snapshot-XLPR2OZ5.js +177 -0
  176. package/dist/snapshot-XLPR2OZ5.js.map +1 -0
  177. package/dist/snippets-EK4DK5CN.js +74 -0
  178. package/dist/snippets-EK4DK5CN.js.map +1 -0
  179. package/dist/standards-7T2UY6DD.js +241 -0
  180. package/dist/standards-7T2UY6DD.js.map +1 -0
  181. package/dist/suggest-VGRYSAR6.js +39 -0
  182. package/dist/suggest-VGRYSAR6.js.map +1 -0
  183. package/dist/suggest-constraints-MY5WKUHA.js +160 -0
  184. package/dist/suggest-constraints-MY5WKUHA.js.map +1 -0
  185. package/dist/suite-TRNGZWQM.js +88 -0
  186. package/dist/suite-TRNGZWQM.js.map +1 -0
  187. package/dist/telemetry-3U2QLA2S.js +75 -0
  188. package/dist/telemetry-3U2QLA2S.js.map +1 -0
  189. package/dist/template-ZERIXVXF.js +403 -0
  190. package/dist/template-ZERIXVXF.js.map +1 -0
  191. package/dist/test-5M2ED3WT.js +169 -0
  192. package/dist/test-5M2ED3WT.js.map +1 -0
  193. package/dist/trial-U732FONV.js +31 -0
  194. package/dist/trial-U732FONV.js.map +1 -0
  195. package/dist/validate-T6D2WCOK.js +106 -0
  196. package/dist/validate-T6D2WCOK.js.map +1 -0
  197. package/dist/verify-KXVASEEG.js +76 -0
  198. package/dist/verify-KXVASEEG.js.map +1 -0
  199. package/dist/watch-I6K4BNMA.js +80 -0
  200. package/dist/watch-I6K4BNMA.js.map +1 -0
  201. package/dist/xcompare-TPFLQO6W.js +87 -0
  202. package/dist/xcompare-TPFLQO6W.js.map +1 -0
  203. package/package.json +2 -2
  204. package/dist/cli.cjs +0 -19040
  205. package/dist/cli.cjs.map +0 -1
  206. package/dist/cli.d.cts +0 -1
  207. package/dist/cli.d.ts +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/error-lookup.ts"],"sourcesContent":["/**\n * `sdt error-lookup` — look up a failure in the known-error catalog (DSR.2).\n *\n * Usage:\n * sdt error-lookup --code OLS_LICENSE_EXPIRED\n * sdt error-lookup --fingerprint abcd1234deadbeef\n * sdt error-lookup --message \"License has expired\"\n * sdt error-lookup --list\n *\n * Exit codes:\n * 0 — match found, or `--list` ran\n * 1 — no match found for the supplied input\n * 2 — usage error (no --code / --fingerprint / --message / --list passed)\n *\n * Mirrors `ddt error-lookup`.\n */\nimport { Command } from 'commander';\nimport { errorCatalog } from '@sdt-tools/core';\n\nexport function errorLookupCommand(): Command {\n const cmd = new Command('error-lookup');\n cmd\n .description('Look up a failure in the known-error catalog by code, fingerprint, or message.')\n .option('--code <code>', 'Adapter error code (e.g. OLS_LICENSE_EXPIRED).')\n .option('--fingerprint <hex>', '16-char fingerprint from a prior failure.')\n .option('--message <text>', 'Free-text error message — falls back to regex match.')\n .option('--list', 'List every entry in the catalog (with code/key/cause).', false)\n .option('--format <fmt>', 'text | json. Default text.', 'text')\n .action(\n (opts: {\n code?: string;\n fingerprint?: string;\n message?: string;\n list?: boolean;\n format?: string;\n }) => {\n const fmt = (opts.format ?? 'text').toLowerCase();\n if (opts.list) {\n const all = errorCatalog.DEFAULT_KNOWN_ERRORS;\n if (fmt === 'json') {\n process.stdout.write(JSON.stringify(all, null, 2) + '\\n');\n } else {\n for (const entry of all) {\n const codeList = entry.codes?.join(', ') ?? '—';\n process.stdout.write(\n `${entry.key}\\n codes: ${codeList}\\n cause: ${entry.causeSummary}\\n\\n`,\n );\n }\n }\n return;\n }\n if (!opts.code && !opts.fingerprint && !opts.message) {\n process.stderr.write('error-lookup: pass --code, --fingerprint, --message, or --list\\n');\n process.exitCode = 2;\n return;\n }\n const match = errorCatalog.lookupKnownError({\n code: opts.code,\n fingerprint: opts.fingerprint,\n message: opts.message,\n });\n if (!match) {\n process.stderr.write('No catalog entry matched.\\n');\n process.exitCode = 1;\n return;\n }\n if (fmt === 'json') {\n process.stdout.write(JSON.stringify(match, null, 2) + '\\n');\n } else {\n process.stdout.write(errorCatalog.formatCatalogMatch(match) + '\\n');\n }\n },\n );\n return cmd;\n}\n"],"mappings":";;;AAgBA,SAAS,eAAe;AACxB,SAAS,oBAAoB;AAEtB,SAAS,qBAA8B;AAC5C,QAAM,MAAM,IAAI,QAAQ,cAAc;AACtC,MACG,YAAY,gFAAgF,EAC5F,OAAO,iBAAiB,gDAAgD,EACxE,OAAO,uBAAuB,2CAA2C,EACzE,OAAO,oBAAoB,2DAAsD,EACjF,OAAO,UAAU,0DAA0D,KAAK,EAChF,OAAO,kBAAkB,8BAA8B,MAAM,EAC7D;AAAA,IACC,CAAC,SAMK;AACJ,YAAM,OAAO,KAAK,UAAU,QAAQ,YAAY;AAChD,UAAI,KAAK,MAAM;AACb,cAAM,MAAM,aAAa;AACzB,YAAI,QAAQ,QAAQ;AAClB,kBAAQ,OAAO,MAAM,KAAK,UAAU,KAAK,MAAM,CAAC,IAAI,IAAI;AAAA,QAC1D,OAAO;AACL,qBAAW,SAAS,KAAK;AACvB,kBAAM,WAAW,MAAM,OAAO,KAAK,IAAI,KAAK;AAC5C,oBAAQ,OAAO;AAAA,cACb,GAAG,MAAM,GAAG;AAAA,WAAc,QAAQ;AAAA,WAAc,MAAM,YAAY;AAAA;AAAA;AAAA,YACpE;AAAA,UACF;AAAA,QACF;AACA;AAAA,MACF;AACA,UAAI,CAAC,KAAK,QAAQ,CAAC,KAAK,eAAe,CAAC,KAAK,SAAS;AACpD,gBAAQ,OAAO,MAAM,kEAAkE;AACvF,gBAAQ,WAAW;AACnB;AAAA,MACF;AACA,YAAM,QAAQ,aAAa,iBAAiB;AAAA,QAC1C,MAAM,KAAK;AAAA,QACX,aAAa,KAAK;AAAA,QAClB,SAAS,KAAK;AAAA,MAChB,CAAC;AACD,UAAI,CAAC,OAAO;AACV,gBAAQ,OAAO,MAAM,6BAA6B;AAClD,gBAAQ,WAAW;AACnB;AAAA,MACF;AACA,UAAI,QAAQ,QAAQ;AAClB,gBAAQ,OAAO,MAAM,KAAK,UAAU,OAAO,MAAM,CAAC,IAAI,IAAI;AAAA,MAC5D,OAAO;AACL,gBAAQ,OAAO,MAAM,aAAa,mBAAmB,KAAK,IAAI,IAAI;AAAA,MACpE;AAAA,IACF;AAAA,EACF;AACF,SAAO;AACT;","names":[]}
@@ -0,0 +1,109 @@
1
+ import {
2
+ logger
3
+ } from "./chunk-VM2H4LAO.js";
4
+ import "./chunk-DGUM43GV.js";
5
+
6
+ // src/util/errorReporting.ts
7
+ import { createInterface } from "readline";
8
+ import * as errorReport from "@sdt-tools/core/errorReport";
9
+ var EXEMPT_COMMANDS = /* @__PURE__ */ new Set(["telemetry", "feedback", "help", "completion"]);
10
+ var activeConsent = "unset";
11
+ var uninstallHooks = null;
12
+ async function setupErrorReporting(commandName) {
13
+ if (EXEMPT_COMMANDS.has(commandName)) return;
14
+ const stored = await errorReport.readConsent();
15
+ activeConsent = stored.consent;
16
+ if (shouldPromptFirstRun(stored.consent) && process.stdout.isTTY && process.stdin.isTTY && !isCi()) {
17
+ printBetaNotice();
18
+ activeConsent = await promptFirstRunConsent() ? "on" : "off";
19
+ await errorReport.writeConsent(activeConsent === "on" ? "on" : "off");
20
+ }
21
+ if (activeConsent !== "off") {
22
+ uninstallHooks = errorReport.installProcessHooks();
23
+ }
24
+ }
25
+ async function finishErrorReporting(productVersion, transport = {}) {
26
+ await errorReport.flushErrorEvents();
27
+ await errorReport.sendUsagePing({
28
+ product: "sdt",
29
+ version: productVersion,
30
+ surface: "cli",
31
+ ...transport.fetchImpl ? { fetchImpl: transport.fetchImpl } : {}
32
+ }).catch(() => void 0);
33
+ if (!errorReport.isErrorReportingEnabled(activeConsent)) return;
34
+ const spooled = await errorReport.listSpooled(transport.dir);
35
+ if (spooled.length === 0) return;
36
+ const result = await errorReport.drainSpool({ productVersion }, transport);
37
+ if (result.sent > 0) {
38
+ logger.dim(` (${result.sent} error report${result.sent === 1 ? "" : "s"} sent \u2014 thank you)`);
39
+ }
40
+ }
41
+ async function reportCliFailure(err, productVersion, transport = {}) {
42
+ errorReport.reportError(err, "handled", "cli:main");
43
+ await errorReport.flushErrorEvents(transport.dir);
44
+ if (errorReport.isErrorReportingEnabled(activeConsent)) {
45
+ await errorReport.drainSpool({ productVersion }, transport);
46
+ return;
47
+ }
48
+ if (!process.stdout.isTTY || !process.stdin.isTTY || isCi()) return;
49
+ const yes = await promptYesNo("Report this error to the SDT team? [y/N] ", false);
50
+ if (!yes) return;
51
+ const result = await errorReport.drainSpool({ productVersion }, transport);
52
+ if (result.sent > 0) logger.dim(" Report sent \u2014 thank you.");
53
+ else logger.dim(" Could not reach the error endpoint; the report is queued locally.");
54
+ }
55
+ function teardownErrorReporting() {
56
+ uninstallHooks?.();
57
+ uninstallHooks = null;
58
+ activeConsent = "unset";
59
+ }
60
+ function activeConsentForTests() {
61
+ return activeConsent;
62
+ }
63
+ var BETA_VERSION = "0.2.6";
64
+ function shouldPromptFirstRun(consent) {
65
+ return consent === "unset";
66
+ }
67
+ function printBetaNotice() {
68
+ const lines = [
69
+ "\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510",
70
+ `\u2502 SDT ${BETA_VERSION} \u2014 Public Beta \u2502`,
71
+ "\u2502 \u2022 All features are free during the beta. \u2502",
72
+ "\u2502 \u2022 After the beta: core features stay free forever; \u2502",
73
+ "\u2502 Pro features keep working and show license notices. \u2502",
74
+ "\u2502 \u2022 AI features use your own API key (never ours). \u2502",
75
+ '\u2502 \u2022 Found a bug? Run: sdt feedback "<what happened>" \u2502',
76
+ "\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"
77
+ ];
78
+ for (const line of lines) logger.info(line);
79
+ }
80
+ async function promptFirstRunConsent() {
81
+ logger.info("SDT can report errors automatically (sanitized diagnostics + OS context,");
82
+ logger.info("never your SQL, identifiers, or credentials) so they get fixed fast.");
83
+ logger.info(errorReport.CONSENT_WARNING);
84
+ return promptYesNo("Enable automatic error reporting? [Y/n] ", true);
85
+ }
86
+ function promptYesNo(question, defaultYes) {
87
+ return new Promise((resolve) => {
88
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
89
+ rl.question(question, (answer) => {
90
+ rl.close();
91
+ const normalized = answer.trim().toLowerCase();
92
+ if (normalized === "") resolve(defaultYes);
93
+ else resolve(normalized === "y" || normalized === "yes");
94
+ });
95
+ });
96
+ }
97
+ function isCi() {
98
+ return process.env["CI"] === "true";
99
+ }
100
+ export {
101
+ activeConsentForTests,
102
+ finishErrorReporting,
103
+ printBetaNotice,
104
+ reportCliFailure,
105
+ setupErrorReporting,
106
+ shouldPromptFirstRun,
107
+ teardownErrorReporting
108
+ };
109
+ //# sourceMappingURL=errorReporting-AQXKKGZH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/util/errorReporting.ts"],"sourcesContent":["/**\n * CLI error-reporting wiring (ERR.2).\n *\n * Lazily imported from `cli.ts` hooks so the cold-start path pays zero cost\n * until a command actually runs (preAction) or fails (catch handler).\n *\n * Responsibilities:\n * - first-run consent prompt (TTY only, default Yes, one keystroke)\n * - crash-hook installation when consent allows capture\n * - end-of-run drain: spool → `POST /errors` when consent is `on`\n * - manual \"Report this error? [y/N]\" prompt when consent is NOT `on`\n * and a command fails\n *\n * @see @sdt-tools/core/errorReport (capture substrate + consent + transport)\n */\nimport { createInterface } from 'node:readline';\n// Subpath import, NOT the `@sdt-tools/core` barrel — this module runs in the\n// preAction hook of every command; the barrel would drag ~85 core modules in.\nimport * as errorReport from '@sdt-tools/core/errorReport';\nimport { logger } from './logger.js';\n\n/** Commands that must never trigger the consent prompt or auto-drain. */\nconst EXEMPT_COMMANDS = new Set(['telemetry', 'feedback', 'help', 'completion']);\n\nlet activeConsent: errorReport.ErrorReportConsent = 'unset';\nlet uninstallHooks: (() => void) | null = null;\n\n/**\n * preAction hook body. Reads (and on first run, prompts for) consent, then\n * installs crash capture unless the user opted out.\n */\nexport async function setupErrorReporting(commandName: string): Promise<void> {\n if (EXEMPT_COMMANDS.has(commandName)) return;\n const stored = await errorReport.readConsent();\n activeConsent = stored.consent;\n\n // First-run consent prompt — explicit, one keystroke, default Yes.\n // Skipped when not interactive (CI, pipes) so scripted runs never block.\n if (\n shouldPromptFirstRun(stored.consent) &&\n process.stdout.isTTY &&\n process.stdin.isTTY &&\n !isCi()\n ) {\n // Beta install-time messaging — shown ONCE, immediately before the very\n // first consent question (true first run, consent still `unset`).\n printBetaNotice();\n activeConsent = (await promptFirstRunConsent()) ? 'on' : 'off';\n await errorReport.writeConsent(activeConsent === 'on' ? 'on' : 'off');\n }\n\n // Crash capture is installed unless the user said no. Capture is local-only;\n // nothing leaves the machine without `isErrorReportingEnabled` saying so.\n if (activeConsent !== 'off') {\n uninstallHooks = errorReport.installProcessHooks();\n }\n}\n\n/**\n * postAction hook body. Flushes buffered events and, when consent is `on`,\n * drains the spool to the Worker. No-ops fast when there is nothing to send.\n * `transport` is injectable for tests.\n */\nexport async function finishErrorReporting(\n productVersion: string,\n transport: errorReport.TransportOptions = {},\n): Promise<void> {\n await errorReport.flushErrorEvents();\n\n // Anonymous usage ping (opt-in) — gated internally by the SAME consent +\n // env opt-outs as error reporting and throttled to once per 24h. It is\n // awaited here (not detached) so the postAction lifecycle has a\n // deterministic completion point: `sendUsagePing` is self-bounded (3s hard\n // timeout) and never throws, so awaiting it can block command exit by at\n // most that timeout — the same bound the spool drain below already imposes.\n // Detaching it left the send racing process teardown, which both dropped\n // pings in practice and made the wiring untestable without a sleep.\n // `transport.fetchImpl` is threaded through so tests can intercept it.\n await errorReport\n .sendUsagePing({\n product: 'sdt',\n version: productVersion,\n surface: 'cli',\n ...(transport.fetchImpl ? { fetchImpl: transport.fetchImpl } : {}),\n })\n .catch(() => undefined);\n\n if (!errorReport.isErrorReportingEnabled(activeConsent)) return;\n const spooled = await errorReport.listSpooled(transport.dir);\n if (spooled.length === 0) return;\n const result = await errorReport.drainSpool({ productVersion }, transport);\n if (result.sent > 0) {\n logger.dim(` (${result.sent} error report${result.sent === 1 ? '' : 's'} sent — thank you)`);\n }\n}\n\n/**\n * Top-level command-failure handler. Reports the error as `handled` (real\n * crashes go through `uncaughtExceptionMonitor`), then either auto-sends\n * (consent `on`) or offers a one-keystroke manual report.\n * `transport` is injectable for tests.\n */\nexport async function reportCliFailure(\n err: unknown,\n productVersion: string,\n transport: errorReport.TransportOptions = {},\n): Promise<void> {\n errorReport.reportError(err, 'handled', 'cli:main');\n await errorReport.flushErrorEvents(transport.dir);\n\n if (errorReport.isErrorReportingEnabled(activeConsent)) {\n await errorReport.drainSpool({ productVersion }, transport);\n return;\n }\n\n // Manual push path — only when interactive.\n if (!process.stdout.isTTY || !process.stdin.isTTY || isCi()) return;\n const yes = await promptYesNo('Report this error to the SDT team? [y/N] ', false);\n if (!yes) return;\n const result = await errorReport.drainSpool({ productVersion }, transport);\n if (result.sent > 0) logger.dim(' Report sent — thank you.');\n else logger.dim(' Could not reach the error endpoint; the report is queued locally.');\n}\n\n/** Uninstall crash hooks (tests). */\nexport function teardownErrorReporting(): void {\n uninstallHooks?.();\n uninstallHooks = null;\n activeConsent = 'unset';\n}\n\n/** Current consent as seen by the hooks (tests). */\nexport function activeConsentForTests(): errorReport.ErrorReportConsent {\n return activeConsent;\n}\n\n/** SDT version surfaced in the first-run beta notice. */\nconst BETA_VERSION = '0.2.6';\n\n/**\n * Whether the first-run flow (beta notice + consent prompt) should fire.\n * True ONLY on true first run — when consent has never been decided\n * (`unset`). Exported so the \"first run only\" contract is unit-testable\n * without faking a TTY.\n */\nexport function shouldPromptFirstRun(consent: errorReport.ErrorReportConsent): boolean {\n return consent === 'unset';\n}\n\n/**\n * Beta install-time messaging — printed ONCE, on true first run (consent\n * `unset`), immediately before the consent question. Tells the user, at\n * install time: it's a 30-day public beta with all features free; what\n * happens after the beta (core stays free forever, Pro features keep\n * working but show license notices); that AI features are bring-your-own\n * key; and how to report a bug. Plain ASCII box to match CLI output style.\n */\nexport function printBetaNotice(): void {\n const lines = [\n '┌─────────────────────────────────────────────────────────┐',\n `│ SDT ${BETA_VERSION} — Public Beta │`,\n '│ • All features are free during the beta. │',\n '│ • After the beta: core features stay free forever; │',\n '│ Pro features keep working and show license notices. │',\n '│ • AI features use your own API key (never ours). │',\n '│ • Found a bug? Run: sdt feedback \"<what happened>\" │',\n '└─────────────────────────────────────────────────────────┘',\n ];\n for (const line of lines) logger.info(line);\n}\n\nasync function promptFirstRunConsent(): Promise<boolean> {\n logger.info('SDT can report errors automatically (sanitized diagnostics + OS context,');\n logger.info('never your SQL, identifiers, or credentials) so they get fixed fast.');\n logger.info(errorReport.CONSENT_WARNING);\n return promptYesNo('Enable automatic error reporting? [Y/n] ', true);\n}\n\nfunction promptYesNo(question: string, defaultYes: boolean): Promise<boolean> {\n return new Promise((resolve) => {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n rl.question(question, (answer) => {\n rl.close();\n const normalized = answer.trim().toLowerCase();\n if (normalized === '') resolve(defaultYes);\n else resolve(normalized === 'y' || normalized === 'yes');\n });\n });\n}\n\nfunction isCi(): boolean {\n return process.env['CI'] === 'true';\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,121 @@
1
+ import "./chunk-DGUM43GV.js";
2
+
3
+ // src/commands/exec.ts
4
+ import { Command } from "commander";
5
+ import { promises as fs } from "fs";
6
+ import { getProfile, SnowflakeConnection } from "@sdt-tools/core/connection";
7
+ import { queryExecution } from "@sdt-tools/core";
8
+ var PROD_PATTERN = /\bprod(uction)?\b/i;
9
+ function isProductionProfile(name) {
10
+ return PROD_PATTERN.test(name);
11
+ }
12
+ async function runOnProfile(execFn, profile, sql, timeoutMs) {
13
+ const start = Date.now();
14
+ try {
15
+ const res = await execFn(profile, sql, timeoutMs);
16
+ return { ...res, profile, durationMs: Date.now() - start };
17
+ } catch (err) {
18
+ return {
19
+ profile,
20
+ status: "error",
21
+ durationMs: Date.now() - start,
22
+ error: err instanceof Error ? err.message : String(err)
23
+ };
24
+ }
25
+ }
26
+ function renderText(results) {
27
+ const lines = [];
28
+ for (const r of results) {
29
+ if (r.status === "success") {
30
+ const rows = r.rowsAffected !== void 0 ? ` (${r.rowsAffected} rows)` : "";
31
+ lines.push(` ${r.profile}: \u2713${rows} \u2014 ${(r.durationMs / 1e3).toFixed(1)}s`);
32
+ } else {
33
+ lines.push(` ${r.profile}: \u2717 ${r.error ?? "unknown error"}`);
34
+ }
35
+ }
36
+ const total = results.length;
37
+ const succeeded = results.filter((r) => r.status === "success").length;
38
+ lines.push(`
39
+ ${succeeded}/${total} profiles succeeded.`);
40
+ return lines.join("\n");
41
+ }
42
+ async function defaultExecFn(profile, sql, timeoutMs) {
43
+ const profileObj = await getProfile(profile);
44
+ const conn = new SnowflakeConnection(profileObj);
45
+ await conn.connect();
46
+ try {
47
+ const timeoutSeconds = Math.max(1, Math.ceil(timeoutMs / 1e3));
48
+ const runner = {
49
+ executeStatement: async (statement) => {
50
+ const result2 = await conn.query(statement, { timeoutSeconds });
51
+ return {
52
+ rows: result2.rows,
53
+ durationMs: result2.durationMs
54
+ };
55
+ }
56
+ };
57
+ const result = await queryExecution.executeStatements(sql, runner, {
58
+ failFast: false,
59
+ toolName: "sdt"
60
+ });
61
+ const firstError = result.statements.find((s) => s.error);
62
+ if (firstError && firstError.error) {
63
+ throw new Error(firstError.error.message);
64
+ }
65
+ const rowsAffected = result.statements.reduce((acc, s) => acc + (s.rowCount ?? 0), 0);
66
+ return { status: "success", rowsAffected };
67
+ } finally {
68
+ await conn.disconnect().catch(() => {
69
+ });
70
+ }
71
+ }
72
+ function execCommand(execFn = defaultExecFn) {
73
+ const cmd = new Command("exec");
74
+ cmd.description("Run a SQL script on one or more connection profiles in parallel.").argument("<file>", "Path to the .sql file to execute.").requiredOption("--profiles <list>", "Comma-separated connection profile names.").option("--yes", "Confirm execution against production profiles without prompting.").option("--format <fmt>", "text | json (default text).", "text").option("--timeout <ms>", "Per-profile timeout in milliseconds.", "30000").action(async (file, opts) => {
75
+ const profiles = opts.profiles.split(",").map((p) => p.trim()).filter(Boolean);
76
+ if (profiles.length === 0) throw new Error("--profiles must list at least one profile.");
77
+ const prodProfiles = profiles.filter(isProductionProfile);
78
+ if (prodProfiles.length > 0 && !opts.yes) {
79
+ throw new Error(
80
+ `Profile(s) look like production: ${prodProfiles.join(", ")}. Pass --yes to confirm.`
81
+ );
82
+ }
83
+ let sql;
84
+ try {
85
+ sql = await fs.readFile(file, "utf8");
86
+ } catch {
87
+ throw new Error(`Cannot read file: ${file}`);
88
+ }
89
+ if (!sql.trim()) throw new Error(`File is empty: ${file}`);
90
+ const timeoutMs = parseInt(String(opts.timeout ?? "30000"), 10);
91
+ const results = await Promise.all(
92
+ profiles.map((p) => runOnProfile(execFn, p, sql, timeoutMs))
93
+ );
94
+ const fmt = String(opts.format ?? "text").toLowerCase();
95
+ if (fmt === "json") {
96
+ const failed = results.filter((r) => r.status === "error").length;
97
+ process.stdout.write(
98
+ JSON.stringify(
99
+ {
100
+ results,
101
+ summary: {
102
+ total: results.length,
103
+ succeeded: results.length - failed,
104
+ failed
105
+ }
106
+ },
107
+ null,
108
+ 2
109
+ ) + "\n"
110
+ );
111
+ return;
112
+ }
113
+ if (fmt !== "text") throw new Error(`Unknown --format: ${opts.format}. Use text | json.`);
114
+ process.stdout.write(renderText(results) + "\n");
115
+ });
116
+ return cmd;
117
+ }
118
+ export {
119
+ execCommand
120
+ };
121
+ //# sourceMappingURL=exec-PKBHLI7T.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/exec.ts"],"sourcesContent":["/**\n * `sdt exec <file.sql> --profiles <list>` — AUTH.3.\n *\n * Runs a SQL script on one or more connection profiles in parallel and\n * prints a merged report. Guarded against any profile whose name matches\n * the word \"prod\" or \"production\" (case-insensitive) unless `--yes` is\n * passed.\n *\n * Mirrors `Databricks/packages/cli/src/commands/exec.ts`.\n */\nimport { Command } from 'commander';\nimport { promises as fs } from 'node:fs';\nimport { getProfile, SnowflakeConnection } from '@sdt-tools/core/connection';\nimport { queryExecution } from '@sdt-tools/core';\n\nexport interface ExecResult {\n profile: string;\n status: 'success' | 'error';\n rowsAffected?: number;\n durationMs: number;\n error?: string;\n}\n\nexport type ExecFn = (\n profile: string,\n sql: string,\n timeoutMs: number,\n) => Promise<Omit<ExecResult, 'profile' | 'durationMs'>>;\n\nconst PROD_PATTERN = /\\bprod(uction)?\\b/i;\n\nfunction isProductionProfile(name: string): boolean {\n return PROD_PATTERN.test(name);\n}\n\nasync function runOnProfile(\n execFn: ExecFn,\n profile: string,\n sql: string,\n timeoutMs: number,\n): Promise<ExecResult> {\n const start = Date.now();\n try {\n const res = await execFn(profile, sql, timeoutMs);\n return { ...res, profile, durationMs: Date.now() - start };\n } catch (err) {\n return {\n profile,\n status: 'error',\n durationMs: Date.now() - start,\n error: err instanceof Error ? err.message : String(err),\n };\n }\n}\n\nfunction renderText(results: ExecResult[]): string {\n const lines: string[] = [];\n for (const r of results) {\n if (r.status === 'success') {\n const rows = r.rowsAffected !== undefined ? ` (${r.rowsAffected} rows)` : '';\n lines.push(` ${r.profile}: ✓${rows} — ${(r.durationMs / 1000).toFixed(1)}s`);\n } else {\n lines.push(` ${r.profile}: ✗ ${r.error ?? 'unknown error'}`);\n }\n }\n const total = results.length;\n const succeeded = results.filter((r) => r.status === 'success').length;\n lines.push(`\\n${succeeded}/${total} profiles succeeded.`);\n return lines.join('\\n');\n}\n\n/**\n * Wire to the real Snowflake connection layer:\n * 1. Resolve the named profile (`~/.sdt/profiles.json`).\n * 2. Open a connection; on failure surface the underlying SDK error.\n * 3. Split the SQL via the shared `queryExecution.splitStatements`\n * (string-/comment-/dollar-quote-aware) and run each statement\n * sequentially through `SnowflakeConnection.query`. We do NOT\n * `failFast` so a script with N statements surfaces every error.\n * 4. Aggregate: success iff every statement succeeded; the first\n * error's message becomes the profile-level error. Total\n * `rowsAffected` is the sum of rowCount across statements.\n *\n * Injectable for tests via the `execFn` parameter on `execCommand`.\n */\nasync function defaultExecFn(\n profile: string,\n sql: string,\n timeoutMs: number,\n): Promise<Omit<ExecResult, 'profile' | 'durationMs'>> {\n const profileObj = await getProfile(profile);\n const conn = new SnowflakeConnection(profileObj);\n await conn.connect();\n try {\n const timeoutSeconds = Math.max(1, Math.ceil(timeoutMs / 1000));\n const runner: queryExecution.QueryRunner = {\n executeStatement: async (statement: string) => {\n const result = await conn.query(statement, { timeoutSeconds });\n return {\n rows: result.rows as Record<string, unknown>[],\n durationMs: result.durationMs,\n };\n },\n };\n const result = await queryExecution.executeStatements(sql, runner, {\n failFast: false,\n toolName: 'sdt',\n });\n const firstError = result.statements.find((s) => s.error);\n if (firstError && firstError.error) {\n throw new Error(firstError.error.message);\n }\n const rowsAffected = result.statements.reduce((acc, s) => acc + (s.rowCount ?? 0), 0);\n return { status: 'success', rowsAffected };\n } finally {\n await conn.disconnect().catch(() => {\n /* best-effort cleanup */\n });\n }\n}\n\nexport function execCommand(execFn: ExecFn = defaultExecFn): Command {\n const cmd = new Command('exec');\n cmd\n .description('Run a SQL script on one or more connection profiles in parallel.')\n .argument('<file>', 'Path to the .sql file to execute.')\n .requiredOption('--profiles <list>', 'Comma-separated connection profile names.')\n .option('--yes', 'Confirm execution against production profiles without prompting.')\n .option('--format <fmt>', 'text | json (default text).', 'text')\n .option('--timeout <ms>', 'Per-profile timeout in milliseconds.', '30000')\n .action(async (file: string, opts) => {\n const profiles = (opts.profiles as string)\n .split(',')\n .map((p) => p.trim())\n .filter(Boolean);\n if (profiles.length === 0) throw new Error('--profiles must list at least one profile.');\n\n const prodProfiles = profiles.filter(isProductionProfile);\n if (prodProfiles.length > 0 && !opts.yes) {\n throw new Error(\n `Profile(s) look like production: ${prodProfiles.join(', ')}. ` +\n `Pass --yes to confirm.`,\n );\n }\n\n let sql: string;\n try {\n sql = await fs.readFile(file, 'utf8');\n } catch {\n throw new Error(`Cannot read file: ${file}`);\n }\n if (!sql.trim()) throw new Error(`File is empty: ${file}`);\n\n const timeoutMs = parseInt(String(opts.timeout ?? '30000'), 10);\n const results = await Promise.all(\n profiles.map((p) => runOnProfile(execFn, p, sql, timeoutMs)),\n );\n\n const fmt = String(opts.format ?? 'text').toLowerCase();\n if (fmt === 'json') {\n const failed = results.filter((r) => r.status === 'error').length;\n process.stdout.write(\n JSON.stringify(\n {\n results,\n summary: {\n total: results.length,\n succeeded: results.length - failed,\n failed,\n },\n },\n null,\n 2,\n ) + '\\n',\n );\n return;\n }\n if (fmt !== 'text') throw new Error(`Unknown --format: ${opts.format}. Use text | json.`);\n process.stdout.write(renderText(results) + '\\n');\n });\n return cmd;\n}\n"],"mappings":"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","names":["result"]}
@@ -0,0 +1,192 @@
1
+ import {
2
+ logger
3
+ } from "./chunk-VM2H4LAO.js";
4
+ import "./chunk-DGUM43GV.js";
5
+
6
+ // src/commands/explain.ts
7
+ import { Command } from "commander";
8
+ import { options as optionsApi } from "@sdt-tools/core";
9
+ function explainCommand() {
10
+ const cmd = new Command("explain");
11
+ cmd.description(
12
+ "Search the options catalog. Explains what an option does, when to use it, and what it pairs with."
13
+ ).argument("[query...]", "Free-form search \u2014 flag name, alias, or text.").option(
14
+ "--tag <tag>",
15
+ "Filter by tag (e.g. compare, deployment, safety, drop, column, governance)."
16
+ ).option(
17
+ "--safety <tier>",
18
+ "Filter by safety tier (safe, review, data-impacting, destructive, unrecoverable)."
19
+ ).option("--limit <n>", "Max results to show.", "8").option("--list", "List all options (paginated by tag).").option("--format <fmt>", "Output format: text | json | markdown. Default text.", "text").action(async (queryParts, opts) => {
20
+ const query = (queryParts ?? []).join(" ").trim();
21
+ const limit = Number.parseInt(String(opts.limit ?? "8"), 10) || 8;
22
+ const safety = opts.safety;
23
+ const tag = opts.tag;
24
+ const fmt = String(opts.format ?? "text").toLowerCase();
25
+ if (opts.list) {
26
+ const all = optionsApi.listOptions({ tag, safety });
27
+ if (fmt === "json") {
28
+ console.log(JSON.stringify(all, null, 2));
29
+ } else if (fmt === "markdown") {
30
+ console.log(renderMarkdownList(all));
31
+ } else {
32
+ printSummaryList(all);
33
+ }
34
+ return;
35
+ }
36
+ if (!query && !tag && !safety) {
37
+ cmd.outputHelp();
38
+ return;
39
+ }
40
+ const hits = optionsApi.searchOptions(query, { tag, safety, limit });
41
+ if (hits.length === 0) {
42
+ if (fmt === "json") {
43
+ console.log("[]");
44
+ return;
45
+ }
46
+ logger.warn(`No matches for: "${query || `${tag ?? ""} ${safety ?? ""}`.trim()}"`);
47
+ logger.dim(" Try: sdt explain --list (full catalog)");
48
+ logger.dim(" Try: sdt explain --tag safety (browse a category)");
49
+ return;
50
+ }
51
+ if (fmt === "json") {
52
+ console.log(
53
+ JSON.stringify(
54
+ hits.map((h) => h.entry),
55
+ null,
56
+ 2
57
+ )
58
+ );
59
+ return;
60
+ }
61
+ if (fmt === "markdown") {
62
+ for (const [i, hit] of hits.entries()) {
63
+ if (i > 0) console.log("");
64
+ console.log(renderMarkdownEntry(hit.entry));
65
+ }
66
+ return;
67
+ }
68
+ for (const [i, hit] of hits.entries()) {
69
+ if (i > 0) console.log("");
70
+ printEntry(hit.entry);
71
+ }
72
+ });
73
+ return cmd;
74
+ }
75
+ function renderMarkdownEntry(entry) {
76
+ const lines = [];
77
+ const badge = entry.safety ? ` _(safety: ${entry.safety})_` : "";
78
+ lines.push(`### \`${entry.name}\`${badge}`);
79
+ lines.push("");
80
+ lines.push(`**${entry.summary}**`);
81
+ lines.push("");
82
+ lines.push(`- **short**: ${entry.short}`);
83
+ if (entry.aliases.length > 0)
84
+ lines.push(`- **aliases**: ${entry.aliases.map((a) => `\`${a}\``).join(", ")}`);
85
+ lines.push(`- **type**: \`${entry.type}\``);
86
+ lines.push(`- **default**: \`${entry.defaultValue}\``);
87
+ if (entry.enumValues)
88
+ lines.push(`- **values**: ${entry.enumValues.map((v) => `\`${v}\``).join(", ")}`);
89
+ lines.push(`- **path**: \`${entry.path}\``);
90
+ lines.push(`- **tags**: ${entry.tags.map((t) => `\`${t}\``).join(", ")}`);
91
+ if (entry.example) {
92
+ lines.push("");
93
+ lines.push("```");
94
+ lines.push(entry.example);
95
+ lines.push("```");
96
+ }
97
+ if (entry.details) {
98
+ lines.push("");
99
+ lines.push("**Details**:");
100
+ lines.push("");
101
+ for (const line of entry.details.split(/\r?\n/)) lines.push(`> ${line}`);
102
+ }
103
+ if (entry.worksWellWith && entry.worksWellWith.length > 0) {
104
+ lines.push("");
105
+ lines.push("**Works well with**:");
106
+ for (const ref of entry.worksWellWith) lines.push(`- \`${ref}\``);
107
+ }
108
+ if (entry.conflictsWith && entry.conflictsWith.length > 0) {
109
+ lines.push("");
110
+ lines.push("**Conflicts with**:");
111
+ for (const ref of entry.conflictsWith) lines.push(`- \`${ref}\``);
112
+ }
113
+ return lines.join("\n");
114
+ }
115
+ function renderMarkdownList(entries) {
116
+ const grouped = /* @__PURE__ */ new Map();
117
+ for (const entry of entries) {
118
+ const tag = entry.tags[0] ?? "misc";
119
+ const arr = grouped.get(tag) ?? [];
120
+ arr.push(entry);
121
+ grouped.set(tag, arr);
122
+ }
123
+ const lines = [];
124
+ lines.push("# Options catalog");
125
+ lines.push("");
126
+ for (const tag of [...grouped.keys()].sort()) {
127
+ lines.push(`## ${tag} (${grouped.get(tag).length})`);
128
+ lines.push("");
129
+ for (const e of grouped.get(tag)) {
130
+ const badge = e.safety ? ` _(${e.safety})_` : "";
131
+ lines.push(`- \`${e.short}\`${badge} \u2014 ${e.summary}`);
132
+ }
133
+ lines.push("");
134
+ }
135
+ return lines.join("\n");
136
+ }
137
+ function printEntry(entry) {
138
+ const tags = entry.tags.join(", ");
139
+ const safetyBadge = entry.safety ? ` [safety: ${entry.safety}]` : "";
140
+ console.log(`${entry.name}${safetyBadge}`);
141
+ console.log(` short: ${entry.short}`);
142
+ if (entry.aliases.length > 0) {
143
+ console.log(` aliases: ${entry.aliases.join(", ")}`);
144
+ }
145
+ console.log(` type: ${entry.type}`);
146
+ console.log(` default: ${entry.defaultValue}`);
147
+ if (entry.enumValues) console.log(` values: ${entry.enumValues.join(", ")}`);
148
+ console.log(` path: ${entry.path}`);
149
+ console.log(` tags: ${tags}`);
150
+ console.log(` summary: ${entry.summary}`);
151
+ console.log(` details:`);
152
+ for (const line of entry.details.split(/\r?\n/)) {
153
+ console.log(` ${line}`);
154
+ }
155
+ if (entry.example) {
156
+ console.log(` example: ${entry.example}`);
157
+ }
158
+ if (entry.worksWellWith && entry.worksWellWith.length > 0) {
159
+ console.log(` works well with:`);
160
+ for (const ref of entry.worksWellWith) console.log(` - ${ref}`);
161
+ }
162
+ if (entry.conflictsWith && entry.conflictsWith.length > 0) {
163
+ console.log(` conflicts with:`);
164
+ for (const ref of entry.conflictsWith) console.log(` - ${ref}`);
165
+ }
166
+ if (entry.seeAlso && entry.seeAlso.length > 0) {
167
+ console.log(` see also: ${entry.seeAlso.join(", ")}`);
168
+ }
169
+ }
170
+ function printSummaryList(entries) {
171
+ const grouped = /* @__PURE__ */ new Map();
172
+ for (const entry of entries) {
173
+ const tag = entry.tags[0] ?? "misc";
174
+ const arr = grouped.get(tag) ?? [];
175
+ arr.push(entry);
176
+ grouped.set(tag, arr);
177
+ }
178
+ const tagOrder = [...grouped.keys()].sort();
179
+ for (const tag of tagOrder) {
180
+ console.log(`# ${tag} (${grouped.get(tag).length})`);
181
+ for (const e of grouped.get(tag)) {
182
+ const badge = e.safety ? ` [${e.safety}]` : "";
183
+ console.log(` ${e.short}${badge}`);
184
+ console.log(` ${e.summary}`);
185
+ }
186
+ console.log("");
187
+ }
188
+ }
189
+ export {
190
+ explainCommand
191
+ };
192
+ //# sourceMappingURL=explain-LWKJOTL7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/explain.ts"],"sourcesContent":["/**\n * `sdt explain <query>` — search the options catalog and print details.\n *\n * Examples:\n * sdt explain drop table\n * sdt explain allowNarrowingTypes\n * sdt explain --tag safety\n * sdt explain --safety destructive\n *\n * See `docs/UX_PLAYBOOK.md` §2 for the full design.\n */\nimport { Command } from 'commander';\nimport { options as optionsApi } from '@sdt-tools/core';\nimport type { OptionEntry, SafetyTier } from '@sdt-tools/core/options';\nimport { logger } from '../util/logger.js';\n\nexport function explainCommand(): Command {\n const cmd = new Command('explain');\n cmd\n .description(\n 'Search the options catalog. Explains what an option does, when to use it, and what it pairs with.',\n )\n .argument('[query...]', 'Free-form search — flag name, alias, or text.')\n .option(\n '--tag <tag>',\n 'Filter by tag (e.g. compare, deployment, safety, drop, column, governance).',\n )\n .option(\n '--safety <tier>',\n 'Filter by safety tier (safe, review, data-impacting, destructive, unrecoverable).',\n )\n .option('--limit <n>', 'Max results to show.', '8')\n .option('--list', 'List all options (paginated by tag).')\n .option('--format <fmt>', 'Output format: text | json | markdown. Default text.', 'text')\n .action(async (queryParts: string[], opts) => {\n const query = (queryParts ?? []).join(' ').trim();\n const limit = Number.parseInt(String(opts.limit ?? '8'), 10) || 8;\n const safety = opts.safety as SafetyTier | undefined;\n const tag = opts.tag as string | undefined;\n const fmt = String(opts.format ?? 'text').toLowerCase();\n\n if (opts.list) {\n const all = optionsApi.listOptions({ tag, safety });\n if (fmt === 'json') {\n console.log(JSON.stringify(all, null, 2));\n } else if (fmt === 'markdown') {\n console.log(renderMarkdownList(all));\n } else {\n printSummaryList(all);\n }\n return;\n }\n\n if (!query && !tag && !safety) {\n cmd.outputHelp();\n return;\n }\n\n const hits = optionsApi.searchOptions(query, { tag, safety, limit });\n if (hits.length === 0) {\n if (fmt === 'json') {\n console.log('[]');\n return;\n }\n logger.warn(`No matches for: \"${query || `${tag ?? ''} ${safety ?? ''}`.trim()}\"`);\n logger.dim(' Try: sdt explain --list (full catalog)');\n logger.dim(' Try: sdt explain --tag safety (browse a category)');\n return;\n }\n\n if (fmt === 'json') {\n console.log(\n JSON.stringify(\n hits.map((h) => h.entry),\n null,\n 2,\n ),\n );\n return;\n }\n if (fmt === 'markdown') {\n for (const [i, hit] of hits.entries()) {\n if (i > 0) console.log('');\n console.log(renderMarkdownEntry(hit.entry));\n }\n return;\n }\n\n for (const [i, hit] of hits.entries()) {\n if (i > 0) console.log('');\n printEntry(hit.entry);\n }\n });\n return cmd;\n}\n\nfunction renderMarkdownEntry(entry: OptionEntry): string {\n const lines: string[] = [];\n const badge = entry.safety ? ` _(safety: ${entry.safety})_` : '';\n lines.push(`### \\`${entry.name}\\`${badge}`);\n lines.push('');\n lines.push(`**${entry.summary}**`);\n lines.push('');\n lines.push(`- **short**: ${entry.short}`);\n if (entry.aliases.length > 0)\n lines.push(`- **aliases**: ${entry.aliases.map((a) => `\\`${a}\\``).join(', ')}`);\n lines.push(`- **type**: \\`${entry.type}\\``);\n lines.push(`- **default**: \\`${entry.defaultValue}\\``);\n if (entry.enumValues)\n lines.push(`- **values**: ${entry.enumValues.map((v) => `\\`${v}\\``).join(', ')}`);\n lines.push(`- **path**: \\`${entry.path}\\``);\n lines.push(`- **tags**: ${entry.tags.map((t) => `\\`${t}\\``).join(', ')}`);\n if (entry.example) {\n lines.push('');\n lines.push('```');\n lines.push(entry.example);\n lines.push('```');\n }\n if (entry.details) {\n lines.push('');\n lines.push('**Details**:');\n lines.push('');\n for (const line of entry.details.split(/\\r?\\n/)) lines.push(`> ${line}`);\n }\n if (entry.worksWellWith && entry.worksWellWith.length > 0) {\n lines.push('');\n lines.push('**Works well with**:');\n for (const ref of entry.worksWellWith) lines.push(`- \\`${ref}\\``);\n }\n if (entry.conflictsWith && entry.conflictsWith.length > 0) {\n lines.push('');\n lines.push('**Conflicts with**:');\n for (const ref of entry.conflictsWith) lines.push(`- \\`${ref}\\``);\n }\n return lines.join('\\n');\n}\n\nfunction renderMarkdownList(entries: readonly OptionEntry[]): string {\n const grouped = new Map<string, OptionEntry[]>();\n for (const entry of entries) {\n const tag = entry.tags[0] ?? 'misc';\n const arr = grouped.get(tag) ?? [];\n arr.push(entry);\n grouped.set(tag, arr);\n }\n const lines: string[] = [];\n lines.push('# Options catalog');\n lines.push('');\n for (const tag of [...grouped.keys()].sort()) {\n lines.push(`## ${tag} (${grouped.get(tag)!.length})`);\n lines.push('');\n for (const e of grouped.get(tag)!) {\n const badge = e.safety ? ` _(${e.safety})_` : '';\n lines.push(`- \\`${e.short}\\`${badge} — ${e.summary}`);\n }\n lines.push('');\n }\n return lines.join('\\n');\n}\n\nfunction printEntry(entry: OptionEntry): void {\n const tags = entry.tags.join(', ');\n const safetyBadge = entry.safety ? ` [safety: ${entry.safety}]` : '';\n console.log(`${entry.name}${safetyBadge}`);\n console.log(` short: ${entry.short}`);\n if (entry.aliases.length > 0) {\n console.log(` aliases: ${entry.aliases.join(', ')}`);\n }\n console.log(` type: ${entry.type}`);\n console.log(` default: ${entry.defaultValue}`);\n if (entry.enumValues) console.log(` values: ${entry.enumValues.join(', ')}`);\n console.log(` path: ${entry.path}`);\n console.log(` tags: ${tags}`);\n console.log(` summary: ${entry.summary}`);\n console.log(` details:`);\n for (const line of entry.details.split(/\\r?\\n/)) {\n console.log(` ${line}`);\n }\n if (entry.example) {\n console.log(` example: ${entry.example}`);\n }\n if (entry.worksWellWith && entry.worksWellWith.length > 0) {\n console.log(` works well with:`);\n for (const ref of entry.worksWellWith) console.log(` - ${ref}`);\n }\n if (entry.conflictsWith && entry.conflictsWith.length > 0) {\n console.log(` conflicts with:`);\n for (const ref of entry.conflictsWith) console.log(` - ${ref}`);\n }\n if (entry.seeAlso && entry.seeAlso.length > 0) {\n console.log(` see also: ${entry.seeAlso.join(', ')}`);\n }\n}\n\nfunction printSummaryList(entries: readonly OptionEntry[]): void {\n // Group by first tag for a clean overview.\n const grouped = new Map<string, OptionEntry[]>();\n for (const entry of entries) {\n const tag = entry.tags[0] ?? 'misc';\n const arr = grouped.get(tag) ?? [];\n arr.push(entry);\n grouped.set(tag, arr);\n }\n const tagOrder = [...grouped.keys()].sort();\n for (const tag of tagOrder) {\n console.log(`# ${tag} (${grouped.get(tag)!.length})`);\n for (const e of grouped.get(tag)!) {\n const badge = e.safety ? ` [${e.safety}]` : '';\n console.log(` ${e.short}${badge}`);\n console.log(` ${e.summary}`);\n }\n console.log('');\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,61 @@
1
+ import {
2
+ logger
3
+ } from "./chunk-VM2H4LAO.js";
4
+ import "./chunk-DGUM43GV.js";
5
+
6
+ // src/commands/explorer.ts
7
+ import { Command } from "commander";
8
+ import { catalog, objectExplorer } from "@sdt-tools/core";
9
+ function explorerCommand() {
10
+ const cmd = new Command("explorer");
11
+ cmd.description(
12
+ "ASCII tree dump of the cached catalog for a connection. Run `sdt catalog refresh` first to populate."
13
+ ).requiredOption("--connection <name>", "Connection profile name (the cache subfolder).").option("--root <path>", "Project root. Default cwd.", process.cwd()).option("--filter <query>", "Typeahead substring filter (case-insensitive).").option(
14
+ "--depth <n>",
15
+ "Truncate tree at depth N (0 = root only, 1 = +databases, 2 = +schemas, 3 = +object-groups, 4 = +objects). Default unlimited.",
16
+ (v) => parseInt(v, 10)
17
+ ).option("--json", "Emit tree as JSON instead of ASCII.").action(async (opts) => {
18
+ const cache = new catalog.CatalogCache({
19
+ root: String(opts.root),
20
+ connection: String(opts.connection)
21
+ });
22
+ const snapshot = await cache.get();
23
+ let tree = objectExplorer.treeForSnapshot(snapshot);
24
+ if (opts.filter) tree = objectExplorer.filterTree(tree, String(opts.filter));
25
+ if (typeof opts.depth === "number") tree = objectExplorer.truncateTree(tree, opts.depth);
26
+ if (snapshot.databases.length === 0) {
27
+ logger.warn(`Catalog cache is empty for connection "${opts.connection}".`);
28
+ logger.dim(` Cache file: ${cache.path}`);
29
+ logger.dim(` Run \`sdt catalog refresh --connection ${opts.connection}\` to populate.`);
30
+ return;
31
+ }
32
+ if (opts.json) {
33
+ console.log(JSON.stringify(tree, null, 2));
34
+ return;
35
+ }
36
+ renderAsciiTree(tree);
37
+ });
38
+ return cmd;
39
+ }
40
+ function renderAsciiTree(root) {
41
+ const desc = root.description ? ` (${root.description})` : "";
42
+ console.log(`${root.label}${desc}`);
43
+ const children = root.children ?? [];
44
+ for (let i = 0; i < children.length; i++) {
45
+ renderChild(children[i], "", i === children.length - 1);
46
+ }
47
+ }
48
+ function renderChild(node, prefix, isLast) {
49
+ const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
50
+ const desc = node.description ? ` (${node.description})` : "";
51
+ console.log(`${prefix}${connector}${node.label}${desc}`);
52
+ const children = node.children ?? [];
53
+ const childPrefix = prefix + (isLast ? " " : "\u2502 ");
54
+ for (let i = 0; i < children.length; i++) {
55
+ renderChild(children[i], childPrefix, i === children.length - 1);
56
+ }
57
+ }
58
+ export {
59
+ explorerCommand
60
+ };
61
+ //# sourceMappingURL=explorer-QOVM6VBD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/explorer.ts"],"sourcesContent":["/**\n * `sdt explorer` — ASCII tree dump of the live db/schema/object hierarchy\n * for a connected target, read from the EE1 catalog cache.\n *\n * The eventual VS Code TreeDataProvider walks the same `treeForSnapshot`\n * shape; this CLI gives non-VS-Code users the same browsable view of the\n * cached catalog. No live queries are issued — the cache is read-only.\n * Run `sdt catalog refresh --connection <name>` first to populate it.\n *\n * Inputs:\n * --connection <name> Required. Connection profile name to load the cache for.\n * --root <path> Project root (where .sdt/cache/ lives). Default cwd.\n * --filter <query> Typeahead filter; case-insensitive substring match.\n * Keeps every ancestor of a leaf match.\n * --json Emit the tree as JSON instead of an ASCII tree.\n */\nimport { Command } from 'commander';\nimport { catalog, objectExplorer } from '@sdt-tools/core';\nimport { logger } from '../util/logger.js';\n\nexport function explorerCommand(): Command {\n const cmd = new Command('explorer');\n cmd\n .description(\n 'ASCII tree dump of the cached catalog for a connection. Run `sdt catalog refresh` first to populate.',\n )\n .requiredOption('--connection <name>', 'Connection profile name (the cache subfolder).')\n .option('--root <path>', 'Project root. Default cwd.', process.cwd())\n .option('--filter <query>', 'Typeahead substring filter (case-insensitive).')\n .option(\n '--depth <n>',\n 'Truncate tree at depth N (0 = root only, 1 = +databases, 2 = +schemas, 3 = +object-groups, 4 = +objects). Default unlimited.',\n (v) => parseInt(v, 10),\n )\n .option('--json', 'Emit tree as JSON instead of ASCII.')\n .action(async (opts) => {\n const cache = new catalog.CatalogCache({\n root: String(opts.root),\n connection: String(opts.connection),\n });\n const snapshot = await cache.get();\n let tree = objectExplorer.treeForSnapshot(snapshot);\n if (opts.filter) tree = objectExplorer.filterTree(tree, String(opts.filter));\n if (typeof opts.depth === 'number') tree = objectExplorer.truncateTree(tree, opts.depth);\n\n if (snapshot.databases.length === 0) {\n logger.warn(`Catalog cache is empty for connection \"${opts.connection}\".`);\n logger.dim(` Cache file: ${cache.path}`);\n logger.dim(` Run \\`sdt catalog refresh --connection ${opts.connection}\\` to populate.`);\n return;\n }\n\n if (opts.json) {\n console.log(JSON.stringify(tree, null, 2));\n return;\n }\n\n renderAsciiTree(tree);\n });\n return cmd;\n}\n\n/**\n * Render the explorer tree as an ASCII tree (├──, │, └──). Mirrors the\n * GNU `tree`(1) glyph set for familiarity. Root prints without a\n * connector; descendants get the standard branch glyphs.\n */\nfunction renderAsciiTree(root: objectExplorer.ExplorerNode): void {\n const desc = root.description ? ` (${root.description})` : '';\n console.log(`${root.label}${desc}`);\n const children = root.children ?? [];\n for (let i = 0; i < children.length; i++) {\n renderChild(children[i]!, '', i === children.length - 1);\n }\n}\n\nfunction renderChild(node: objectExplorer.ExplorerNode, prefix: string, isLast: boolean): void {\n const connector = isLast ? '└── ' : '├── ';\n const desc = node.description ? ` (${node.description})` : '';\n console.log(`${prefix}${connector}${node.label}${desc}`);\n const children = node.children ?? [];\n const childPrefix = prefix + (isLast ? ' ' : '│ ');\n for (let i = 0; i < children.length; i++) {\n renderChild(children[i]!, childPrefix, i === children.length - 1);\n }\n}\n"],"mappings":";;;;;;AAgBA,SAAS,eAAe;AACxB,SAAS,SAAS,sBAAsB;AAGjC,SAAS,kBAA2B;AACzC,QAAM,MAAM,IAAI,QAAQ,UAAU;AAClC,MACG;AAAA,IACC;AAAA,EACF,EACC,eAAe,uBAAuB,gDAAgD,EACtF,OAAO,iBAAiB,8BAA8B,QAAQ,IAAI,CAAC,EACnE,OAAO,oBAAoB,gDAAgD,EAC3E;AAAA,IACC;AAAA,IACA;AAAA,IACA,CAAC,MAAM,SAAS,GAAG,EAAE;AAAA,EACvB,EACC,OAAO,UAAU,qCAAqC,EACtD,OAAO,OAAO,SAAS;AACtB,UAAM,QAAQ,IAAI,QAAQ,aAAa;AAAA,MACrC,MAAM,OAAO,KAAK,IAAI;AAAA,MACtB,YAAY,OAAO,KAAK,UAAU;AAAA,IACpC,CAAC;AACD,UAAM,WAAW,MAAM,MAAM,IAAI;AACjC,QAAI,OAAO,eAAe,gBAAgB,QAAQ;AAClD,QAAI,KAAK,OAAQ,QAAO,eAAe,WAAW,MAAM,OAAO,KAAK,MAAM,CAAC;AAC3E,QAAI,OAAO,KAAK,UAAU,SAAU,QAAO,eAAe,aAAa,MAAM,KAAK,KAAK;AAEvF,QAAI,SAAS,UAAU,WAAW,GAAG;AACnC,aAAO,KAAK,0CAA0C,KAAK,UAAU,IAAI;AACzE,aAAO,IAAI,iBAAiB,MAAM,IAAI,EAAE;AACxC,aAAO,IAAI,4CAA4C,KAAK,UAAU,iBAAiB;AACvF;AAAA,IACF;AAEA,QAAI,KAAK,MAAM;AACb,cAAQ,IAAI,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AACzC;AAAA,IACF;AAEA,oBAAgB,IAAI;AAAA,EACtB,CAAC;AACH,SAAO;AACT;AAOA,SAAS,gBAAgB,MAAyC;AAChE,QAAM,OAAO,KAAK,cAAc,MAAM,KAAK,WAAW,MAAM;AAC5D,UAAQ,IAAI,GAAG,KAAK,KAAK,GAAG,IAAI,EAAE;AAClC,QAAM,WAAW,KAAK,YAAY,CAAC;AACnC,WAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,gBAAY,SAAS,CAAC,GAAI,IAAI,MAAM,SAAS,SAAS,CAAC;AAAA,EACzD;AACF;AAEA,SAAS,YAAY,MAAmC,QAAgB,QAAuB;AAC7F,QAAM,YAAY,SAAS,wBAAS;AACpC,QAAM,OAAO,KAAK,cAAc,MAAM,KAAK,WAAW,MAAM;AAC5D,UAAQ,IAAI,GAAG,MAAM,GAAG,SAAS,GAAG,KAAK,KAAK,GAAG,IAAI,EAAE;AACvD,QAAM,WAAW,KAAK,YAAY,CAAC;AACnC,QAAM,cAAc,UAAU,SAAS,SAAS;AAChD,WAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,gBAAY,SAAS,CAAC,GAAI,aAAa,MAAM,SAAS,SAAS,CAAC;AAAA,EAClE;AACF;","names":[]}
@@ -0,0 +1,42 @@
1
+ import {
2
+ logger
3
+ } from "./chunk-VM2H4LAO.js";
4
+ import "./chunk-DGUM43GV.js";
5
+
6
+ // src/commands/export.ts
7
+ import path from "path";
8
+ import { Command } from "commander";
9
+ import { exporters, compare } from "@sdt-tools/core";
10
+ function exportCommand() {
11
+ const cmd = new Command("export");
12
+ cmd.description("Export an SDT project to another format (DCM, etc.). v1 supports `dcm` only.");
13
+ cmd.command("dcm").description(
14
+ "Emit a Snowflake DCM Projects directory (manifest.yml + DEFINE files) from an .sdtproj. Best-of-both-worlds bridge \u2014 author + AI-review + lint in SDT, deploy via EXECUTE DCM PROJECT."
15
+ ).requiredOption("--source <path>", "Path to the .sdtproj to export.").requiredOption("--output <dir>", "Output directory for the DCM project.").option(
16
+ "--dcm-project <fqn>",
17
+ "Override the DCM project FQN for every configuration (e.g. PROD_DB.PUBLIC.MY_PROJECT)."
18
+ ).action(async (opts) => {
19
+ const sourcePath = path.resolve(opts.source);
20
+ const outputDir = path.resolve(opts.output);
21
+ const source = new compare.ProjectSource(sourcePath);
22
+ const model = await source.load();
23
+ const fs = await import("fs/promises");
24
+ const projectRaw = JSON.parse(await fs.readFile(sourcePath, "utf8"));
25
+ const profiles = projectRaw.deploymentProfiles ?? { default: void 0 };
26
+ const dcmProjectFqnByConfig = opts.dcmProject ? Object.fromEntries(Object.keys(profiles).map((name) => [name, opts.dcmProject])) : void 0;
27
+ const result = await exporters.exportToDcm({
28
+ projectPath: sourcePath,
29
+ project: projectRaw,
30
+ model,
31
+ outputDir,
32
+ ...dcmProjectFqnByConfig ? { dcmProjectFqnByConfig } : {}
33
+ });
34
+ logger.info(`Wrote ${result.filesWritten} DEFINE file(s) + manifest.yml to ${outputDir}`);
35
+ for (const w of result.warnings) logger.warn(w);
36
+ });
37
+ return cmd;
38
+ }
39
+ export {
40
+ exportCommand
41
+ };
42
+ //# sourceMappingURL=export-IYYBZ5HE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/export.ts"],"sourcesContent":["/**\n * `sdt export` — emit an SDT project in another format.\n *\n * v1 supports:\n * - `dcm` — Snowflake DCM Projects (manifest.yml + DEFINE files). The\n * best-of-both-worlds bridge: author + AI-review + lint in SDT,\n * deploy via DCM. See docs/CONSTRAINT_ENFORCEMENT.md and the DCM\n * parity initiative entries in docs/BACKLOG.md.\n *\n * Snowflake-only. DDT side does not ship `ddt export dcm` because DCM\n * Projects is a Snowflake-native feature.\n */\nimport path from 'node:path';\nimport { Command } from 'commander';\nimport { exporters, compare } from '@sdt-tools/core';\nimport type { project } from '@sdt-tools/core';\nimport { logger } from '../util/logger.js';\n\nexport function exportCommand(): Command {\n const cmd = new Command('export');\n cmd.description('Export an SDT project to another format (DCM, etc.). v1 supports `dcm` only.');\n\n cmd\n .command('dcm')\n .description(\n 'Emit a Snowflake DCM Projects directory (manifest.yml + DEFINE files) from an .sdtproj. ' +\n 'Best-of-both-worlds bridge — author + AI-review + lint in SDT, deploy via EXECUTE DCM PROJECT.',\n )\n .requiredOption('--source <path>', 'Path to the .sdtproj to export.')\n .requiredOption('--output <dir>', 'Output directory for the DCM project.')\n .option(\n '--dcm-project <fqn>',\n 'Override the DCM project FQN for every configuration (e.g. PROD_DB.PUBLIC.MY_PROJECT).',\n )\n .action(async (opts: { source: string; output: string; dcmProject?: string }) => {\n const sourcePath = path.resolve(opts.source);\n const outputDir = path.resolve(opts.output);\n const source = new compare.ProjectSource(sourcePath);\n const model = await source.load();\n // ProjectSource doesn't expose the raw SdtProject — read it\n // directly via fs for the manifest mapping.\n const fs = await import('node:fs/promises');\n const projectRaw = JSON.parse(await fs.readFile(sourcePath, 'utf8')) as project.SdtProject;\n const profiles = projectRaw.deploymentProfiles ?? { default: undefined };\n const dcmProjectFqnByConfig = opts.dcmProject\n ? Object.fromEntries(Object.keys(profiles).map((name) => [name, opts.dcmProject!]))\n : undefined;\n const result = await exporters.exportToDcm({\n projectPath: sourcePath,\n project: projectRaw,\n model: model as Parameters<typeof exporters.exportToDcm>[0]['model'],\n outputDir,\n ...(dcmProjectFqnByConfig ? { dcmProjectFqnByConfig } : {}),\n });\n logger.info(`Wrote ${result.filesWritten} DEFINE file(s) + manifest.yml to ${outputDir}`);\n for (const w of result.warnings) logger.warn(w);\n });\n\n return cmd;\n}\n"],"mappings":";;;;;;AAYA,OAAO,UAAU;AACjB,SAAS,eAAe;AACxB,SAAS,WAAW,eAAe;AAI5B,SAAS,gBAAyB;AACvC,QAAM,MAAM,IAAI,QAAQ,QAAQ;AAChC,MAAI,YAAY,8EAA8E;AAE9F,MACG,QAAQ,KAAK,EACb;AAAA,IACC;AAAA,EAEF,EACC,eAAe,mBAAmB,iCAAiC,EACnE,eAAe,kBAAkB,uCAAuC,EACxE;AAAA,IACC;AAAA,IACA;AAAA,EACF,EACC,OAAO,OAAO,SAAkE;AAC/E,UAAM,aAAa,KAAK,QAAQ,KAAK,MAAM;AAC3C,UAAM,YAAY,KAAK,QAAQ,KAAK,MAAM;AAC1C,UAAM,SAAS,IAAI,QAAQ,cAAc,UAAU;AACnD,UAAM,QAAQ,MAAM,OAAO,KAAK;AAGhC,UAAM,KAAK,MAAM,OAAO,aAAkB;AAC1C,UAAM,aAAa,KAAK,MAAM,MAAM,GAAG,SAAS,YAAY,MAAM,CAAC;AACnE,UAAM,WAAW,WAAW,sBAAsB,EAAE,SAAS,OAAU;AACvE,UAAM,wBAAwB,KAAK,aAC/B,OAAO,YAAY,OAAO,KAAK,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,KAAK,UAAW,CAAC,CAAC,IAChF;AACJ,UAAM,SAAS,MAAM,UAAU,YAAY;AAAA,MACzC,aAAa;AAAA,MACb,SAAS;AAAA,MACT;AAAA,MACA;AAAA,MACA,GAAI,wBAAwB,EAAE,sBAAsB,IAAI,CAAC;AAAA,IAC3D,CAAC;AACD,WAAO,KAAK,SAAS,OAAO,YAAY,qCAAqC,SAAS,EAAE;AACxF,eAAW,KAAK,OAAO,SAAU,QAAO,KAAK,CAAC;AAAA,EAChD,CAAC;AAEH,SAAO;AACT;","names":[]}