@sdt-tools/cli 0.2.0 → 0.2.5

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 (205) 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 +506 -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-SYTH4V53.js +110 -0
  46. package/dist/connection-SYTH4V53.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-ZRNJ3VW7.js +109 -0
  70. package/dist/errorReporting-ZRNJ3VW7.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/index.cjs +36 -6
  102. package/dist/index.cjs.map +1 -1
  103. package/dist/index.js +60 -25
  104. package/dist/index.js.map +1 -1
  105. package/dist/init-SWRRJMGI.js +57 -0
  106. package/dist/init-SWRRJMGI.js.map +1 -0
  107. package/dist/install-hooks-6SIAGTAF.js +109 -0
  108. package/dist/install-hooks-6SIAGTAF.js.map +1 -0
  109. package/dist/license-OAF22PLZ.js +46 -0
  110. package/dist/license-OAF22PLZ.js.map +1 -0
  111. package/dist/lineage-EW66XJ6O.js +552 -0
  112. package/dist/lineage-EW66XJ6O.js.map +1 -0
  113. package/dist/lint-FQ2OTYTQ.js +143 -0
  114. package/dist/lint-FQ2OTYTQ.js.map +1 -0
  115. package/dist/mcp-3QI4TH4N.js +344 -0
  116. package/dist/mcp-3QI4TH4N.js.map +1 -0
  117. package/dist/migrate-from-dbt-JVTXPWKQ.js +156 -0
  118. package/dist/migrate-from-dbt-JVTXPWKQ.js.map +1 -0
  119. package/dist/migrate-platform-NTRTOGNR.js +91 -0
  120. package/dist/migrate-platform-NTRTOGNR.js.map +1 -0
  121. package/dist/optimize-CJYWMAWA.js +105 -0
  122. package/dist/optimize-CJYWMAWA.js.map +1 -0
  123. package/dist/perf-LL2CPCJF.js +205 -0
  124. package/dist/perf-LL2CPCJF.js.map +1 -0
  125. package/dist/pii-FBDRDQ2E.js +136 -0
  126. package/dist/pii-FBDRDQ2E.js.map +1 -0
  127. package/dist/pilot-CCQERKPH.js +29 -0
  128. package/dist/pilot-CCQERKPH.js.map +1 -0
  129. package/dist/pr-comment-S5FF4QRX.js +79 -0
  130. package/dist/pr-comment-S5FF4QRX.js.map +1 -0
  131. package/dist/preview-5U4YVCRM.js +47 -0
  132. package/dist/preview-5U4YVCRM.js.map +1 -0
  133. package/dist/profile-7VC57KD2.js +101 -0
  134. package/dist/profile-7VC57KD2.js.map +1 -0
  135. package/dist/promote-AASEFTIA.js +408 -0
  136. package/dist/promote-AASEFTIA.js.map +1 -0
  137. package/dist/publish-Y2J56K4Y.js +715 -0
  138. package/dist/publish-Y2J56K4Y.js.map +1 -0
  139. package/dist/purge-QMXZKCMD.js +57 -0
  140. package/dist/purge-QMXZKCMD.js.map +1 -0
  141. package/dist/query-log-6OM4GI7W.js +112 -0
  142. package/dist/query-log-6OM4GI7W.js.map +1 -0
  143. package/dist/refactor-LTZQLJ35.js +5799 -0
  144. package/dist/refactor-LTZQLJ35.js.map +1 -0
  145. package/dist/refresh-4TY2AGOU.js +38 -0
  146. package/dist/refresh-4TY2AGOU.js.map +1 -0
  147. package/dist/replay-OOC25FZN.js +117 -0
  148. package/dist/replay-OOC25FZN.js.map +1 -0
  149. package/dist/revert-ODMUVJW6.js +110 -0
  150. package/dist/revert-ODMUVJW6.js.map +1 -0
  151. package/dist/review-XXPWOBFP.js +158 -0
  152. package/dist/review-XXPWOBFP.js.map +1 -0
  153. package/dist/rollback-suggest-6G2HEKFR.js +79 -0
  154. package/dist/rollback-suggest-6G2HEKFR.js.map +1 -0
  155. package/dist/safer-alternative-QFVNLG3L.js +89 -0
  156. package/dist/safer-alternative-QFVNLG3L.js.map +1 -0
  157. package/dist/safety-7QWRSUEZ.js +168 -0
  158. package/dist/safety-7QWRSUEZ.js.map +1 -0
  159. package/dist/savings-RHIXP6IT.js +95 -0
  160. package/dist/savings-RHIXP6IT.js.map +1 -0
  161. package/dist/scan-secrets-5YCQ4UCU.js +54 -0
  162. package/dist/scan-secrets-5YCQ4UCU.js.map +1 -0
  163. package/dist/schema-CIZXCQD2.js +429 -0
  164. package/dist/schema-CIZXCQD2.js.map +1 -0
  165. package/dist/script-K7CIN2P6.js +153 -0
  166. package/dist/script-K7CIN2P6.js.map +1 -0
  167. package/dist/search-BUZ5NXZZ.js +151 -0
  168. package/dist/search-BUZ5NXZZ.js.map +1 -0
  169. package/dist/seed-76QAK276.js +96 -0
  170. package/dist/seed-76QAK276.js.map +1 -0
  171. package/dist/sketch-PTLKDIK3.js +88 -0
  172. package/dist/sketch-PTLKDIK3.js.map +1 -0
  173. package/dist/snapshot-XLPR2OZ5.js +177 -0
  174. package/dist/snapshot-XLPR2OZ5.js.map +1 -0
  175. package/dist/snippets-EK4DK5CN.js +74 -0
  176. package/dist/snippets-EK4DK5CN.js.map +1 -0
  177. package/dist/standards-7T2UY6DD.js +241 -0
  178. package/dist/standards-7T2UY6DD.js.map +1 -0
  179. package/dist/suggest-VGRYSAR6.js +39 -0
  180. package/dist/suggest-VGRYSAR6.js.map +1 -0
  181. package/dist/suggest-constraints-MY5WKUHA.js +160 -0
  182. package/dist/suggest-constraints-MY5WKUHA.js.map +1 -0
  183. package/dist/suite-TRNGZWQM.js +88 -0
  184. package/dist/suite-TRNGZWQM.js.map +1 -0
  185. package/dist/telemetry-3U2QLA2S.js +75 -0
  186. package/dist/telemetry-3U2QLA2S.js.map +1 -0
  187. package/dist/template-ZERIXVXF.js +403 -0
  188. package/dist/template-ZERIXVXF.js.map +1 -0
  189. package/dist/test-5M2ED3WT.js +169 -0
  190. package/dist/test-5M2ED3WT.js.map +1 -0
  191. package/dist/trial-U732FONV.js +31 -0
  192. package/dist/trial-U732FONV.js.map +1 -0
  193. package/dist/validate-T6D2WCOK.js +106 -0
  194. package/dist/validate-T6D2WCOK.js.map +1 -0
  195. package/dist/verify-KXVASEEG.js +76 -0
  196. package/dist/verify-KXVASEEG.js.map +1 -0
  197. package/dist/watch-I6K4BNMA.js +80 -0
  198. package/dist/watch-I6K4BNMA.js.map +1 -0
  199. package/dist/xcompare-TPFLQO6W.js +87 -0
  200. package/dist/xcompare-TPFLQO6W.js.map +1 -0
  201. package/package.json +2 -2
  202. package/dist/cli.cjs +0 -19040
  203. package/dist/cli.cjs.map +0 -1
  204. package/dist/cli.d.cts +0 -1
  205. package/dist/cli.d.ts +0 -1
@@ -0,0 +1,405 @@
1
+ import {
2
+ addMappingFlags,
3
+ buildMappingFromOptions
4
+ } from "./chunk-JP2EZLR5.js";
5
+ import {
6
+ attachRelatedOptions
7
+ } from "./chunk-EWXM4KJN.js";
8
+ import {
9
+ logger
10
+ } from "./chunk-VM2H4LAO.js";
11
+ import "./chunk-DGUM43GV.js";
12
+
13
+ // src/commands/compare.ts
14
+ import path from "path";
15
+ import { promises as fs } from "fs";
16
+ import { Command } from "commander";
17
+ import {
18
+ CompareEngine,
19
+ LiveSource,
20
+ PacSource,
21
+ ProjectSource,
22
+ writeCompareHistory
23
+ } from "@sdt-tools/core/compare";
24
+ import { getProfile, SnowflakeConnection } from "@sdt-tools/core/connection";
25
+ import { compileSlice, loadProject } from "@sdt-tools/core/project";
26
+ import { ai, safety, typecheck } from "@sdt-tools/core";
27
+
28
+ // src/util/color.ts
29
+ var ANSI = {
30
+ reset: "\x1B[0m",
31
+ bold: "\x1B[1m",
32
+ red: "\x1B[31m",
33
+ yellow: "\x1B[33m",
34
+ cyan: "\x1B[36m",
35
+ green: "\x1B[32m",
36
+ gray: "\x1B[90m"
37
+ };
38
+ function resolveColorMode(flag) {
39
+ const mode = (flag ?? "auto").toLowerCase();
40
+ if (mode === "always") return true;
41
+ if (mode === "never") return false;
42
+ if (process.env.NO_COLOR) return false;
43
+ return Boolean(process.stdout.isTTY);
44
+ }
45
+ function makeColorizer(enabled) {
46
+ if (!enabled) {
47
+ const identity = (s) => s;
48
+ return {
49
+ unrecoverable: identity,
50
+ destructive: identity,
51
+ expensive: identity,
52
+ warning: identity,
53
+ safe: identity,
54
+ dim: identity,
55
+ applyToBlock: identity
56
+ };
57
+ }
58
+ const wrap = (codes) => (s) => `${codes}${s}${ANSI.reset}`;
59
+ return {
60
+ unrecoverable: wrap(ANSI.bold + ANSI.red),
61
+ destructive: wrap(ANSI.red),
62
+ expensive: wrap(ANSI.yellow),
63
+ warning: wrap(ANSI.cyan),
64
+ safe: wrap(ANSI.green),
65
+ dim: wrap(ANSI.gray),
66
+ applyToBlock(block) {
67
+ return block.split("\n").map((line) => {
68
+ if (/UNRECOVERABLE/.test(line) || line.includes("\u{1F6D1}"))
69
+ return ANSI.bold + ANSI.red + line + ANSI.reset;
70
+ if (/DESTRUCTIVE/.test(line)) return ANSI.red + line + ANSI.reset;
71
+ if (/EXPENSIVE/.test(line)) return ANSI.yellow + line + ANSI.reset;
72
+ if (/WARNING/.test(line) || line.includes("\u26A0")) return ANSI.cyan + line + ANSI.reset;
73
+ if (/\bSAFE\b|\bOK\b/.test(line) || line.includes("\u2713"))
74
+ return ANSI.green + line + ANSI.reset;
75
+ return line;
76
+ }).join("\n");
77
+ }
78
+ };
79
+ }
80
+
81
+ // src/commands/compare.ts
82
+ async function maybeLoadSliceFromArg(arg) {
83
+ if (!arg.toLowerCase().endsWith(".sdtproj")) return void 0;
84
+ const loaded = await loadProject(arg);
85
+ if (!loaded.project.slice) return void 0;
86
+ return compileSlice(loaded.project.slice);
87
+ }
88
+ async function resolveSource(arg) {
89
+ if (arg.startsWith("snowflake://")) {
90
+ const rest = arg.slice("snowflake://".length);
91
+ const [profileName, db, schema] = rest.split("/");
92
+ if (!profileName) throw new Error(`Invalid snowflake URI: ${arg}`);
93
+ const profile = await getProfile(profileName);
94
+ const conn = new SnowflakeConnection(profile);
95
+ return new LiveSource(conn, { database: db, schema });
96
+ }
97
+ const lower = arg.toLowerCase();
98
+ if (lower.endsWith(".sdtproj")) return new ProjectSource(arg);
99
+ if (lower.endsWith(".sdtpac")) return new PacSource(arg);
100
+ throw new Error(
101
+ `Unrecognized source: ${arg}. Use *.sdtproj, *.sdtpac, or snowflake://<profile>[/db[/schema]].`
102
+ );
103
+ }
104
+ function compareCommand() {
105
+ const cmd = new Command("compare");
106
+ cmd.description(
107
+ "Compare two schemas. Sources may be .sdtproj, .sdtpac, or snowflake://<profile>[/db[/schema]]."
108
+ ).argument("[source]", "Left side of the comparison (or use --source)").argument("[target]", "Right side of the comparison (or use --target)").option(
109
+ "--source <path>",
110
+ "Left side of the comparison (flag form of the positional source arg)"
111
+ ).option(
112
+ "--target <path>",
113
+ "Right side of the comparison (flag form of the positional target arg)"
114
+ ).option("-o, --output <path>", "Write JSON result to this path").option("--format <format>", "Output format: json | summary | markdown", "summary").option("--ignore-case", "Treat unquoted identifiers case-insensitively", false).option(
115
+ "--no-slice",
116
+ "Disable the source project's Slice (if it has one). Default: a project's slice is applied automatically."
117
+ ).option(
118
+ "--explain",
119
+ "After the diff, call the configured AI provider to narrate each change in plain English with reasoning. Requires `sdt ai` to be configured.",
120
+ false
121
+ ).option(
122
+ "--color <mode>",
123
+ "Colorize severity output: always | never | auto. Default auto (color on TTY).",
124
+ "auto"
125
+ ).option(
126
+ "--type-safe",
127
+ "After the compare, run the TYPECHECK.1 impact analyzer. Exits with code 2 if the configured --break-on threshold is reached (default `error`). CI-friendly gate \u2014 pair with `--format json` for machine-readable output.",
128
+ false
129
+ ).option(
130
+ "--break-on <severity>",
131
+ "TYPECHECK.2 threshold for --type-safe: `error` (exit 2 on any error ripple, default) | `warning` (exit 2 on any error OR warning ripple, strict CI mode).",
132
+ "error"
133
+ ).option(
134
+ "--write-impact [path]",
135
+ "Write the TYPECHECK.1 impact analysis to a JSON file the VS Code extension can surface as squiggle-underlines. Default path: `.sdt/impact.json` resolved relative to the source `.sdtproj` directory (or CWD if the source is not a project). Composes with `--type-safe` \u2014 the file is written before the gate decides exit code."
136
+ ).option(
137
+ "--no-history",
138
+ "Skip writing the compare-history audit record (AUDITCMP.1). Default: every compare run writes a record to `.sdt/history/compare/`, exportable via `sdt audit-log emit`."
139
+ ).action(async (sourceArgPos, targetArgPos, opts) => {
140
+ const sourceArg = opts.source ? String(opts.source) : sourceArgPos ? String(sourceArgPos) : "";
141
+ const targetArg = opts.target ? String(opts.target) : targetArgPos ? String(targetArgPos) : "";
142
+ if (!sourceArg || !targetArg) {
143
+ throw new Error(
144
+ "compare needs a source and a target \u2014 pass them positionally (`sdt compare <source> <target>`) or via `--source`/`--target`."
145
+ );
146
+ }
147
+ const nameMapping = await buildMappingFromOptions(opts);
148
+ const source = await resolveSource(String(sourceArg));
149
+ const target = await resolveSource(String(targetArg));
150
+ const slice = opts.slice === false ? void 0 : await maybeLoadSliceFromArg(String(sourceArg));
151
+ const engine = new CompareEngine();
152
+ const startedAt = (/* @__PURE__ */ new Date()).toISOString();
153
+ const result = await engine.compare(source, target, {
154
+ ignoreCase: Boolean(opts.ignoreCase),
155
+ ...nameMapping ? { nameMapping } : {},
156
+ ...slice ? { sliceFilter: slice } : {}
157
+ });
158
+ if (opts.history !== false) {
159
+ const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
160
+ try {
161
+ const histAssessment = safety.assess(result);
162
+ const changed = result.summary.added + result.summary.removed + result.summary.modified;
163
+ await writeCompareHistory(compareHistoryRoot(String(sourceArg)), {
164
+ startedAt,
165
+ finishedAt,
166
+ durationMs: Date.parse(finishedAt) - Date.parse(startedAt),
167
+ outcome: histAssessment.blocked ? "BLOCKED" : changed === 0 ? "NO_CHANGES" : "CHANGES_FOUND",
168
+ source: { kind: result.source.kind, label: result.source.label },
169
+ target: { kind: result.target.kind, label: result.target.label },
170
+ summary: result.summary,
171
+ sliceApplied: Boolean(slice),
172
+ ...histAssessment.blocked && histAssessment.blockReason ? { blockReason: histAssessment.blockReason } : {}
173
+ });
174
+ } catch {
175
+ }
176
+ }
177
+ if (opts.output) {
178
+ const out = path.resolve(String(opts.output));
179
+ await fs.mkdir(path.dirname(out), { recursive: true });
180
+ await fs.writeFile(out, JSON.stringify(result, null, 2));
181
+ logger.success(`Wrote ${out}`);
182
+ }
183
+ if (opts.format === "json") {
184
+ process.stdout.write(JSON.stringify(result, null, 2) + "\n");
185
+ return;
186
+ }
187
+ if (opts.format === "markdown") {
188
+ const assessment2 = safety.assess(result);
189
+ process.stdout.write(renderCompareMarkdown(result, assessment2) + "\n");
190
+ return;
191
+ }
192
+ logger.info(
193
+ `Compare ${result.source.kind}:${result.source.label} \u2192 ${result.target.kind}:${result.target.label}`
194
+ );
195
+ if (slice) {
196
+ const outside = result.outsideScope?.length ?? 0;
197
+ const refs = result.referenced?.length ?? 0;
198
+ logger.info(
199
+ ` Slice active: ${result.objects.length} owned \xB7 ${outside} outside scope (untouched) \xB7 ${refs} referenced`
200
+ );
201
+ }
202
+ logger.info(
203
+ ` +${result.summary.added} added, -${result.summary.removed} removed, ~${result.summary.modified} modified, =${result.summary.unchanged} unchanged`
204
+ );
205
+ for (const o of result.objects) {
206
+ if (o.kind === "unchanged") continue;
207
+ logger.info(` ${o.kind.padEnd(8)} ${o.identity.objectType.padEnd(20)} ${o.identity.fqn}`);
208
+ }
209
+ const assessment = safety.assess(result);
210
+ const buckets = safety.groupByReversibility(assessment);
211
+ const total = buckets.unrecoverable.length + buckets.dataImpacting.length + buckets.reversible.length;
212
+ if (total > 0) {
213
+ const colorize = makeColorizer(resolveColorMode(opts.color));
214
+ logger.info("");
215
+ logger.info("Safety findings (grouped by reversibility):");
216
+ const block = safety.formatReversibilityBuckets(buckets);
217
+ const colored = colorize.applyToBlock(block);
218
+ for (const line of colored.split("\n")) logger.info(" " + line);
219
+ if (assessment.blocked && assessment.blockReason) {
220
+ logger.error(" " + colorize.unrecoverable("BLOCKED: " + assessment.blockReason));
221
+ }
222
+ }
223
+ if (opts.explain) {
224
+ logger.info("");
225
+ logger.info("AI explanation:");
226
+ try {
227
+ const userPrompt = buildExplainPrompt(result, assessment);
228
+ const reply = await ai.complete(
229
+ [
230
+ { role: "system", content: SYSTEM_PROMPT },
231
+ { role: "user", content: userPrompt }
232
+ ],
233
+ { feature: "compare.explain" }
234
+ );
235
+ for (const line of reply.text.split("\n")) logger.info(" " + line);
236
+ } catch (err) {
237
+ logger.error(" --explain failed: " + (err instanceof Error ? err.message : String(err)));
238
+ logger.error(
239
+ " Run `sdt ai status` to verify your AI provider is configured (`sdt ai test` to send a probe)."
240
+ );
241
+ }
242
+ }
243
+ if (opts.writeImpact !== void 0) {
244
+ await writeImpactJson(
245
+ result,
246
+ opts.writeImpact === true ? void 0 : String(opts.writeImpact),
247
+ String(sourceArg)
248
+ );
249
+ }
250
+ if (opts.typeSafe) {
251
+ const breakOnRaw = String(opts.breakOn ?? "error").toLowerCase();
252
+ if (breakOnRaw !== "error" && breakOnRaw !== "warning") {
253
+ throw new Error(
254
+ `--break-on must be 'error' or 'warning' (got '${opts.breakOn}'). 'error' fails on any error-severity ripple (default); 'warning' fails on any warning or error ripple (strict CI).`
255
+ );
256
+ }
257
+ const breakOn = breakOnRaw;
258
+ const impactFindings = typecheck.analyzeImpact(result);
259
+ const impactSummary = typecheck.summarizeImpact(impactFindings);
260
+ if (impactSummary.findingsCount > 0) {
261
+ logger.info("");
262
+ logger.info(
263
+ `Type-safety gate (--break-on ${breakOn}) \u2014 ${impactSummary.findingsCount} breaking change(s); ${impactSummary.errors} error ripple(s), ${impactSummary.warnings} warning ripple(s); ${impactSummary.affectedObjects.length} dependent object(s).`
264
+ );
265
+ for (const f of impactFindings) {
266
+ for (const r of f.ripples) {
267
+ const tag = r.severity === "error" ? "ERROR" : "WARN ";
268
+ logger.info(
269
+ ` ${tag} ${r.fqn} \u2190 ${f.breakingChangeOn.fqn} (${f.breakingChangeOn.kind})`
270
+ );
271
+ }
272
+ }
273
+ const failOnError = impactSummary.errors > 0;
274
+ const failOnWarning = breakOn === "warning" && impactSummary.warnings > 0;
275
+ if (failOnError) {
276
+ logger.error(
277
+ `Type-safety gate FAILED \u2014 ${impactSummary.errors} error ripple(s). Fix the dependents (drop / update / re-bind) before re-running compare, OR rerun with the change scoped out of the diff.`
278
+ );
279
+ process.exitCode = 2;
280
+ } else if (failOnWarning) {
281
+ logger.error(
282
+ `Type-safety gate FAILED (strict --break-on warning) \u2014 ${impactSummary.warnings} warning ripple(s). Tighten the dependent SQL to accept the new type, or drop --break-on warning to allow.`
283
+ );
284
+ process.exitCode = 2;
285
+ }
286
+ } else {
287
+ logger.info("Type-safety gate PASSED \u2014 no breaking-change ripples detected.");
288
+ }
289
+ }
290
+ });
291
+ addMappingFlags(cmd);
292
+ attachRelatedOptions(cmd, [
293
+ "compare.ignoreCase",
294
+ "compare.ignoreComments",
295
+ "compare.ignoreFormattingDifferences",
296
+ "compare.excludeObjectTypes",
297
+ "compare.excludeObjectPatterns",
298
+ "compare.includeObjectPatterns"
299
+ ]);
300
+ return cmd;
301
+ }
302
+ async function writeImpactJson(result, explicitPath, sourceArg) {
303
+ const findings = typecheck.analyzeImpact(result);
304
+ const summary = typecheck.summarizeImpact(findings);
305
+ const file = {
306
+ version: typecheck.IMPACT_FILE_VERSION,
307
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
308
+ source: `${result.source.kind}:${result.source.label}`,
309
+ target: `${result.target.kind}:${result.target.label}`,
310
+ findings,
311
+ summary
312
+ };
313
+ const outPath = explicitPath ? path.resolve(explicitPath) : defaultImpactPath(sourceArg);
314
+ await fs.mkdir(path.dirname(outPath), { recursive: true });
315
+ await fs.writeFile(outPath, typecheck.serializeImpactFile(file));
316
+ logger.success(
317
+ `Wrote ${outPath} \u2014 ${summary.findingsCount} finding(s), ${summary.errors} error / ${summary.warnings} warning ripple(s).`
318
+ );
319
+ }
320
+ function defaultImpactPath(sourceArg) {
321
+ if (sourceArg.toLowerCase().endsWith(".sdtproj")) {
322
+ return path.resolve(path.dirname(sourceArg), ".sdt", "impact.json");
323
+ }
324
+ return path.resolve(".sdt", "impact.json");
325
+ }
326
+ function compareHistoryRoot(sourceArg) {
327
+ const lower = sourceArg.toLowerCase();
328
+ if (lower.endsWith(".sdtproj") || lower.endsWith(".sdtpac")) {
329
+ return path.dirname(path.resolve(sourceArg));
330
+ }
331
+ return process.cwd();
332
+ }
333
+ var SYSTEM_PROMPT = `You are a senior Snowflake DBA reviewing a schema diff. Your job is to narrate the changes in plain English to a teammate who hasn't seen the underlying SQL. For each change, briefly say:
334
+ - what it is (e.g. "a new fact table is added")
335
+ - why a reasonable engineer might do this (the intent)
336
+ - what to watch out for (the risk, if any)
337
+
338
+ Be concrete; use the FQNs you're given. Keep it to 3-6 sentences per object. Don't repeat the raw diff back \u2014 explain it.`;
339
+ function renderCompareMarkdown(result, assessment) {
340
+ const lines = [];
341
+ lines.push(
342
+ `# Compare: ${result.source.kind}:${result.source.label} \u2192 ${result.target.kind}:${result.target.label}`
343
+ );
344
+ lines.push("");
345
+ lines.push(
346
+ `**Summary**: +${result.summary.added} added \xB7 -${result.summary.removed} removed \xB7 ~${result.summary.modified} modified \xB7 =${result.summary.unchanged} unchanged.`
347
+ );
348
+ lines.push("");
349
+ lines.push("**Safety**:");
350
+ lines.push(`- \u{1F6D1} Unrecoverable: ${assessment.unrecoverable.length}`);
351
+ lines.push(`- \u{1F525} Destructive: ${assessment.destructive.length}`);
352
+ lines.push(`- \u23F1 Expensive: ${assessment.expensive.length}`);
353
+ lines.push(`- \u26A0 Warnings: ${assessment.warnings.length}`);
354
+ if (assessment.blocked) {
355
+ lines.push("");
356
+ lines.push(
357
+ `> **BLOCKED**: ${assessment.blockReason ?? "safety classifier refuses to proceed"}`
358
+ );
359
+ }
360
+ lines.push("");
361
+ const changed = result.objects.filter((o) => o.kind !== "unchanged");
362
+ if (changed.length === 0) {
363
+ lines.push("_No object-level changes._");
364
+ return lines.join("\n");
365
+ }
366
+ lines.push("| Kind | Type | FQN |");
367
+ lines.push("| --- | --- | --- |");
368
+ for (const o of changed) {
369
+ lines.push(`| ${o.kind} | \`${o.identity.objectType}\` | \`${o.identity.fqn}\` |`);
370
+ }
371
+ return lines.join("\n");
372
+ }
373
+ function buildExplainPrompt(result, assessment) {
374
+ const lines = [];
375
+ lines.push(`Source: ${result.source.label}`);
376
+ lines.push(`Target: ${result.target.label}`);
377
+ lines.push(
378
+ `Summary: +${result.summary.added} added, -${result.summary.removed} removed, ~${result.summary.modified} modified.`
379
+ );
380
+ lines.push(
381
+ `Safety: ${assessment.unrecoverable.length} unrecoverable, ${assessment.destructive.length} destructive, ${assessment.expensive.length} expensive, ${assessment.warnings.length} warnings.`
382
+ );
383
+ lines.push("");
384
+ lines.push("Changes (up to 40):");
385
+ const changed = result.objects.filter((o) => o.kind !== "unchanged").slice(0, 40);
386
+ for (const o of changed) {
387
+ lines.push(`- ${o.kind} ${o.identity.objectType} ${o.identity.fqn}`);
388
+ }
389
+ if (result.objects.filter((o) => o.kind !== "unchanged").length > 40) {
390
+ lines.push(
391
+ ` (\u2026 ${result.objects.filter((o) => o.kind !== "unchanged").length - 40} more truncated)`
392
+ );
393
+ }
394
+ lines.push("");
395
+ lines.push(
396
+ "Please narrate this diff in plain English. Group related changes together when it helps."
397
+ );
398
+ return lines.join("\n");
399
+ }
400
+ export {
401
+ SYSTEM_PROMPT,
402
+ buildExplainPrompt,
403
+ compareCommand
404
+ };
405
+ //# sourceMappingURL=compare-5O6UTWPJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/compare.ts","../src/util/color.ts"],"sourcesContent":["import path from 'node:path';\nimport { promises as fs } from 'node:fs';\nimport { Command } from 'commander';\nimport {\n CompareEngine,\n LiveSource,\n PacSource,\n ProjectSource,\n writeCompareHistory,\n type CompareSource,\n} from '@sdt-tools/core/compare';\nimport { getProfile, SnowflakeConnection } from '@sdt-tools/core/connection';\nimport { compileSlice, loadProject } from '@sdt-tools/core/project';\nimport { ai, safety, typecheck } from '@sdt-tools/core';\nimport { logger } from '../util/logger.js';\nimport { attachRelatedOptions } from '../util/help-catalog.js';\nimport { makeColorizer, resolveColorMode } from '../util/color.js';\nimport { addMappingFlags, buildMappingFromOptions } from '../util/mapping.js';\n\n/**\n * Auto-load a slice from a `.sdtproj` argument. Returns `undefined` when\n * the argument is not a `.sdtproj` or the project doesn't declare a slice.\n * Suite-level invocations bypass this and pass a slice directly.\n */\nasync function maybeLoadSliceFromArg(\n arg: string,\n): Promise<ReturnType<typeof compileSlice> | undefined> {\n if (!arg.toLowerCase().endsWith('.sdtproj')) return undefined;\n const loaded = await loadProject(arg);\n if (!loaded.project.slice) return undefined;\n return compileSlice(loaded.project.slice);\n}\n\n/**\n * Resolve a polymorphic source/target argument. Supported forms:\n * - path/to/foo.sdtproj\n * - path/to/foo.sdtpac\n * - snowflake://<profile>[/db[/schema]]\n */\nasync function resolveSource(arg: string): Promise<CompareSource> {\n if (arg.startsWith('snowflake://')) {\n const rest = arg.slice('snowflake://'.length);\n const [profileName, db, schema] = rest.split('/');\n if (!profileName) throw new Error(`Invalid snowflake URI: ${arg}`);\n const profile = await getProfile(profileName);\n const conn = new SnowflakeConnection(profile);\n return new LiveSource(conn, { database: db, schema });\n }\n const lower = arg.toLowerCase();\n if (lower.endsWith('.sdtproj')) return new ProjectSource(arg);\n if (lower.endsWith('.sdtpac')) return new PacSource(arg);\n throw new Error(\n `Unrecognized source: ${arg}. Use *.sdtproj, *.sdtpac, or snowflake://<profile>[/db[/schema]].`,\n );\n}\n\nexport function compareCommand(): Command {\n const cmd = new Command('compare');\n cmd\n .description(\n 'Compare two schemas. Sources may be .sdtproj, .sdtpac, or snowflake://<profile>[/db[/schema]].',\n )\n .argument('[source]', 'Left side of the comparison (or use --source)')\n .argument('[target]', 'Right side of the comparison (or use --target)')\n .option(\n '--source <path>',\n 'Left side of the comparison (flag form of the positional source arg)',\n )\n .option(\n '--target <path>',\n 'Right side of the comparison (flag form of the positional target arg)',\n )\n .option('-o, --output <path>', 'Write JSON result to this path')\n .option('--format <format>', 'Output format: json | summary | markdown', 'summary')\n .option('--ignore-case', 'Treat unquoted identifiers case-insensitively', false)\n .option(\n '--no-slice',\n \"Disable the source project's Slice (if it has one). Default: a project's slice is applied automatically.\",\n )\n .option(\n '--explain',\n 'After the diff, call the configured AI provider to narrate each change in plain English with reasoning. Requires `sdt ai` to be configured.',\n false,\n )\n .option(\n '--color <mode>',\n 'Colorize severity output: always | never | auto. Default auto (color on TTY).',\n 'auto',\n )\n .option(\n '--type-safe',\n 'After the compare, run the TYPECHECK.1 impact analyzer. Exits with code 2 if the configured --break-on threshold is reached (default `error`). CI-friendly gate — pair with `--format json` for machine-readable output.',\n false,\n )\n .option(\n '--break-on <severity>',\n 'TYPECHECK.2 threshold for --type-safe: `error` (exit 2 on any error ripple, default) | `warning` (exit 2 on any error OR warning ripple, strict CI mode).',\n 'error',\n )\n .option(\n '--write-impact [path]',\n 'Write the TYPECHECK.1 impact analysis to a JSON file the VS Code extension can surface as squiggle-underlines. Default path: `.sdt/impact.json` resolved relative to the source `.sdtproj` directory (or CWD if the source is not a project). Composes with `--type-safe` — the file is written before the gate decides exit code.',\n )\n .option(\n '--no-history',\n 'Skip writing the compare-history audit record (AUDITCMP.1). Default: every compare run writes a record to `.sdt/history/compare/`, exportable via `sdt audit-log emit`.',\n )\n .action(async (sourceArgPos, targetArgPos, opts) => {\n // Accept the source/target either positionally (`sdt compare X Y`) or via\n // flags (`--source X --target Y`). The flag form mirrors `ddt compare`\n // and `sdt script`, and is what GETTING_STARTED documents; the positional\n // form is kept for back-compat. Flags win when both are given.\n const sourceArg = opts.source\n ? String(opts.source)\n : sourceArgPos\n ? String(sourceArgPos)\n : '';\n const targetArg = opts.target\n ? String(opts.target)\n : targetArgPos\n ? String(targetArgPos)\n : '';\n if (!sourceArg || !targetArg) {\n throw new Error(\n 'compare needs a source and a target — pass them positionally (`sdt compare <source> <target>`) or via `--source`/`--target`.',\n );\n }\n\n // Logical-name mapping (`--map`, `--map-file`) — rewrites source-side\n // FQNs so two schemas whose database / schema names differ can compare\n // semantically. See docs/LOGICAL_NAME_MAPPING.md. Mirrors `ddt compare`.\n const nameMapping = await buildMappingFromOptions(opts);\n const source = await resolveSource(String(sourceArg));\n const target = await resolveSource(String(targetArg));\n\n // Auto-load slice from the source `.sdtproj` unless --no-slice was set.\n // The target side (live or pac) doesn't carry a slice; it's the\n // project's slice that defines the scope.\n const slice =\n opts.slice === false ? undefined : await maybeLoadSliceFromArg(String(sourceArg));\n\n const engine = new CompareEngine();\n const startedAt = new Date().toISOString();\n const result = await engine.compare(source, target, {\n ignoreCase: Boolean(opts.ignoreCase),\n ...(nameMapping ? { nameMapping } : {}),\n ...(slice ? { sliceFilter: slice } : {}),\n });\n\n // AUDITCMP.1 — compare operations leave the same audit trail deploys\n // do. Written before the format-specific early returns so every\n // output mode is covered. Best-effort: a failed write never breaks\n // the compare itself.\n if (opts.history !== false) {\n const finishedAt = new Date().toISOString();\n try {\n const histAssessment = safety.assess(result);\n const changed = result.summary.added + result.summary.removed + result.summary.modified;\n await writeCompareHistory(compareHistoryRoot(String(sourceArg)), {\n startedAt,\n finishedAt,\n durationMs: Date.parse(finishedAt) - Date.parse(startedAt),\n outcome: histAssessment.blocked\n ? 'BLOCKED'\n : changed === 0\n ? 'NO_CHANGES'\n : 'CHANGES_FOUND',\n source: { kind: result.source.kind, label: result.source.label },\n target: { kind: result.target.kind, label: result.target.label },\n summary: result.summary,\n sliceApplied: Boolean(slice),\n ...(histAssessment.blocked && histAssessment.blockReason\n ? { blockReason: histAssessment.blockReason }\n : {}),\n });\n } catch {\n /* audit trail is best-effort; never fail the compare over it */\n }\n }\n\n if (opts.output) {\n const out = path.resolve(String(opts.output));\n await fs.mkdir(path.dirname(out), { recursive: true });\n await fs.writeFile(out, JSON.stringify(result, null, 2));\n logger.success(`Wrote ${out}`);\n }\n\n if (opts.format === 'json') {\n process.stdout.write(JSON.stringify(result, null, 2) + '\\n');\n return;\n }\n if (opts.format === 'markdown') {\n const assessment = safety.assess(result);\n process.stdout.write(renderCompareMarkdown(result, assessment) + '\\n');\n return;\n }\n logger.info(\n `Compare ${result.source.kind}:${result.source.label} → ${result.target.kind}:${result.target.label}`,\n );\n if (slice) {\n const outside = result.outsideScope?.length ?? 0;\n const refs = result.referenced?.length ?? 0;\n logger.info(\n ` Slice active: ${result.objects.length} owned · ${outside} outside scope (untouched) · ${refs} referenced`,\n );\n }\n logger.info(\n ` +${result.summary.added} added, -${result.summary.removed} removed, ~${result.summary.modified} modified, =${result.summary.unchanged} unchanged`,\n );\n for (const o of result.objects) {\n if (o.kind === 'unchanged') continue;\n logger.info(` ${o.kind.padEnd(8)} ${o.identity.objectType.padEnd(20)} ${o.identity.fqn}`);\n }\n\n // Reversibility-grouped safety findings. Three buckets — Unrecoverable\n // first (highest urgency), then Data-impacting, then Reversible.\n // Empty diff is silent; assessment.blocked is surfaced once findings\n // exist.\n const assessment = safety.assess(result);\n const buckets = safety.groupByReversibility(assessment);\n const total =\n buckets.unrecoverable.length + buckets.dataImpacting.length + buckets.reversible.length;\n if (total > 0) {\n const colorize = makeColorizer(resolveColorMode(opts.color));\n logger.info('');\n logger.info('Safety findings (grouped by reversibility):');\n const block = safety.formatReversibilityBuckets(buckets);\n const colored = colorize.applyToBlock(block);\n for (const line of colored.split('\\n')) logger.info(' ' + line);\n if (assessment.blocked && assessment.blockReason) {\n logger.error(' ' + colorize.unrecoverable('BLOCKED: ' + assessment.blockReason));\n }\n }\n\n // AI-narrated diff (--explain). Calls the configured provider with a\n // structured prompt containing the diff summary + per-object changes\n // + safety findings. Output is the assistant's plain-English narration.\n if (opts.explain) {\n logger.info('');\n logger.info('AI explanation:');\n try {\n const userPrompt = buildExplainPrompt(result, assessment);\n const reply = await ai.complete(\n [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: userPrompt },\n ],\n { feature: 'compare.explain' },\n );\n for (const line of reply.text.split('\\n')) logger.info(' ' + line);\n } catch (err) {\n logger.error(' --explain failed: ' + (err instanceof Error ? err.message : String(err)));\n logger.error(\n ' Run `sdt ai status` to verify your AI provider is configured (`sdt ai test` to send a probe).',\n );\n }\n }\n\n // --write-impact: TYPECHECK.4 — write the impact analysis to a\n // JSON file the VS Code provider reads. Always runs the analyzer\n // (even when --type-safe is off) so the editor surface is\n // independent of the CI-gate flag. Composes with --type-safe.\n if (opts.writeImpact !== undefined) {\n await writeImpactJson(\n result,\n opts.writeImpact === true ? undefined : String(opts.writeImpact),\n String(sourceArg),\n );\n }\n\n // --type-safe: TYPECHECK.2 CI gate. Runs the TYPECHECK.1 impact\n // analyzer and exits with code 2 if any error-severity ripple\n // (column-drop or table-drop reaching a dependent object) is\n // detected. Stays silent on a clean diff so this composes well\n // with --format json (which already returned above).\n if (opts.typeSafe) {\n const breakOnRaw = String(opts.breakOn ?? 'error').toLowerCase();\n if (breakOnRaw !== 'error' && breakOnRaw !== 'warning') {\n throw new Error(\n `--break-on must be 'error' or 'warning' (got '${opts.breakOn}'). 'error' fails on any error-severity ripple (default); 'warning' fails on any warning or error ripple (strict CI).`,\n );\n }\n const breakOn = breakOnRaw as 'error' | 'warning';\n const impactFindings = typecheck.analyzeImpact(result);\n const impactSummary = typecheck.summarizeImpact(impactFindings);\n if (impactSummary.findingsCount > 0) {\n logger.info('');\n logger.info(\n `Type-safety gate (--break-on ${breakOn}) — ${impactSummary.findingsCount} breaking change(s); ${impactSummary.errors} error ripple(s), ${impactSummary.warnings} warning ripple(s); ${impactSummary.affectedObjects.length} dependent object(s).`,\n );\n for (const f of impactFindings) {\n for (const r of f.ripples) {\n const tag = r.severity === 'error' ? 'ERROR' : 'WARN ';\n logger.info(\n ` ${tag} ${r.fqn} ← ${f.breakingChangeOn.fqn} (${f.breakingChangeOn.kind})`,\n );\n }\n }\n const failOnError = impactSummary.errors > 0;\n const failOnWarning = breakOn === 'warning' && impactSummary.warnings > 0;\n if (failOnError) {\n logger.error(\n `Type-safety gate FAILED — ${impactSummary.errors} error ripple(s). Fix the dependents (drop / update / re-bind) before re-running compare, OR rerun with the change scoped out of the diff.`,\n );\n process.exitCode = 2;\n } else if (failOnWarning) {\n logger.error(\n `Type-safety gate FAILED (strict --break-on warning) — ${impactSummary.warnings} warning ripple(s). Tighten the dependent SQL to accept the new type, or drop --break-on warning to allow.`,\n );\n process.exitCode = 2;\n }\n } else {\n logger.info('Type-safety gate PASSED — no breaking-change ripples detected.');\n }\n }\n });\n addMappingFlags(cmd);\n attachRelatedOptions(cmd, [\n 'compare.ignoreCase',\n 'compare.ignoreComments',\n 'compare.ignoreFormattingDifferences',\n 'compare.excludeObjectTypes',\n 'compare.excludeObjectPatterns',\n 'compare.includeObjectPatterns',\n ]);\n return cmd;\n}\n\n/**\n * TYPECHECK.4 — write the impact analysis JSON to disk so the VS Code\n * provider can surface it as squiggle-underlines. Defaults to\n * `<project-dir>/.sdt/impact.json` when the source is a `.sdtproj`,\n * otherwise `<cwd>/.sdt/impact.json`.\n */\nasync function writeImpactJson(\n result: Parameters<typeof typecheck.analyzeImpact>[0],\n explicitPath: string | undefined,\n sourceArg: string,\n): Promise<void> {\n const findings = typecheck.analyzeImpact(result);\n const summary = typecheck.summarizeImpact(findings);\n const file: typecheck.ImpactFile = {\n version: typecheck.IMPACT_FILE_VERSION,\n generatedAt: new Date().toISOString(),\n source: `${result.source.kind}:${result.source.label}`,\n target: `${result.target.kind}:${result.target.label}`,\n findings,\n summary,\n };\n const outPath = explicitPath ? path.resolve(explicitPath) : defaultImpactPath(sourceArg);\n await fs.mkdir(path.dirname(outPath), { recursive: true });\n await fs.writeFile(outPath, typecheck.serializeImpactFile(file));\n logger.success(\n `Wrote ${outPath} — ${summary.findingsCount} finding(s), ${summary.errors} error / ${summary.warnings} warning ripple(s).`,\n );\n}\n\nfunction defaultImpactPath(sourceArg: string): string {\n if (sourceArg.toLowerCase().endsWith('.sdtproj')) {\n return path.resolve(path.dirname(sourceArg), '.sdt', 'impact.json');\n }\n return path.resolve('.sdt', 'impact.json');\n}\n\n/**\n * AUDITCMP.1 — workspace root for the compare-history record. Project /\n * pac sources anchor the audit trail next to the artifact being compared;\n * live (snowflake://) sources fall back to CWD.\n */\nfunction compareHistoryRoot(sourceArg: string): string {\n const lower = sourceArg.toLowerCase();\n if (lower.endsWith('.sdtproj') || lower.endsWith('.sdtpac')) {\n return path.dirname(path.resolve(sourceArg));\n }\n return process.cwd();\n}\n\nexport const SYSTEM_PROMPT = `You are a senior Snowflake DBA reviewing a schema diff. Your job is to narrate the changes in plain English to a teammate who hasn't seen the underlying SQL. For each change, briefly say:\n - what it is (e.g. \"a new fact table is added\")\n - why a reasonable engineer might do this (the intent)\n - what to watch out for (the risk, if any)\n\nBe concrete; use the FQNs you're given. Keep it to 3-6 sentences per object. Don't repeat the raw diff back — explain it.`;\n\nfunction renderCompareMarkdown(\n result: {\n source: { kind: string; label: string };\n target: { kind: string; label: string };\n objects: Array<{ kind: string; identity: { fqn: string; objectType: string } }>;\n summary: { added: number; removed: number; modified: number; unchanged: number };\n },\n assessment: {\n unrecoverable: unknown[];\n destructive: unknown[];\n expensive: unknown[];\n warnings: unknown[];\n blocked?: boolean;\n blockReason?: string;\n },\n): string {\n const lines: string[] = [];\n lines.push(\n `# Compare: ${result.source.kind}:${result.source.label} → ${result.target.kind}:${result.target.label}`,\n );\n lines.push('');\n lines.push(\n `**Summary**: +${result.summary.added} added · -${result.summary.removed} removed · ~${result.summary.modified} modified · =${result.summary.unchanged} unchanged.`,\n );\n lines.push('');\n lines.push('**Safety**:');\n lines.push(`- 🛑 Unrecoverable: ${assessment.unrecoverable.length}`);\n lines.push(`- 🔥 Destructive: ${assessment.destructive.length}`);\n lines.push(`- ⏱ Expensive: ${assessment.expensive.length}`);\n lines.push(`- ⚠ Warnings: ${assessment.warnings.length}`);\n if (assessment.blocked) {\n lines.push('');\n lines.push(\n `> **BLOCKED**: ${assessment.blockReason ?? 'safety classifier refuses to proceed'}`,\n );\n }\n lines.push('');\n const changed = result.objects.filter((o) => o.kind !== 'unchanged');\n if (changed.length === 0) {\n lines.push('_No object-level changes._');\n return lines.join('\\n');\n }\n lines.push('| Kind | Type | FQN |');\n lines.push('| --- | --- | --- |');\n for (const o of changed) {\n lines.push(`| ${o.kind} | \\`${o.identity.objectType}\\` | \\`${o.identity.fqn}\\` |`);\n }\n return lines.join('\\n');\n}\n\nexport function buildExplainPrompt(\n result: {\n source: { label: string };\n target: { label: string };\n objects: Array<{\n kind: string;\n identity: { fqn: string; objectType: string };\n changes?: unknown[];\n }>;\n summary: { added: number; removed: number; modified: number; unchanged: number };\n },\n assessment: {\n unrecoverable: unknown[];\n destructive: unknown[];\n expensive: unknown[];\n warnings: unknown[];\n },\n): string {\n const lines: string[] = [];\n lines.push(`Source: ${result.source.label}`);\n lines.push(`Target: ${result.target.label}`);\n lines.push(\n `Summary: +${result.summary.added} added, -${result.summary.removed} removed, ~${result.summary.modified} modified.`,\n );\n lines.push(\n `Safety: ${assessment.unrecoverable.length} unrecoverable, ${assessment.destructive.length} destructive, ${assessment.expensive.length} expensive, ${assessment.warnings.length} warnings.`,\n );\n lines.push('');\n lines.push('Changes (up to 40):');\n const changed = result.objects.filter((o) => o.kind !== 'unchanged').slice(0, 40);\n for (const o of changed) {\n lines.push(`- ${o.kind} ${o.identity.objectType} ${o.identity.fqn}`);\n }\n if (result.objects.filter((o) => o.kind !== 'unchanged').length > 40) {\n lines.push(\n ` (… ${result.objects.filter((o) => o.kind !== 'unchanged').length - 40} more truncated)`,\n );\n }\n lines.push('');\n lines.push(\n 'Please narrate this diff in plain English. Group related changes together when it helps.',\n );\n return lines.join('\\n');\n}\n","/**\n * ANSI severity coloring for CLI output. Resolves the `--color` flag\n * against the current TTY state and returns a `colorize` function\n * that wraps text in the right ANSI code (or returns it unchanged\n * when color is disabled).\n *\n * Conventions used across SDT/DDT CLI surfaces:\n * - UNRECOVERABLE → bold red\n * - DESTRUCTIVE → red\n * - EXPENSIVE → yellow\n * - WARNING → cyan\n * - SAFE / OK → green\n *\n * Paired with `Databricks/packages/cli/src/util/color.ts`.\n */\nexport type ColorMode = 'always' | 'never' | 'auto';\n\nconst ANSI = {\n reset: '\\x1b[0m',\n bold: '\\x1b[1m',\n red: '\\x1b[31m',\n yellow: '\\x1b[33m',\n cyan: '\\x1b[36m',\n green: '\\x1b[32m',\n gray: '\\x1b[90m',\n};\n\nexport function resolveColorMode(flag: string | undefined): boolean {\n const mode = (flag ?? 'auto').toLowerCase() as ColorMode;\n if (mode === 'always') return true;\n if (mode === 'never') return false;\n // auto — only color when stdout is a TTY AND NO_COLOR env isn't set.\n if (process.env.NO_COLOR) return false;\n return Boolean(process.stdout.isTTY);\n}\n\nexport interface Colorizer {\n unrecoverable(s: string): string;\n destructive(s: string): string;\n expensive(s: string): string;\n warning(s: string): string;\n safe(s: string): string;\n dim(s: string): string;\n /** Apply ANSI coloring to a multi-line block based on per-line severity tokens (🛑, ⚠, ✓, etc). */\n applyToBlock(block: string): string;\n}\n\nexport function makeColorizer(enabled: boolean): Colorizer {\n if (!enabled) {\n const identity = (s: string): string => s;\n return {\n unrecoverable: identity,\n destructive: identity,\n expensive: identity,\n warning: identity,\n safe: identity,\n dim: identity,\n applyToBlock: identity,\n };\n }\n const wrap =\n (codes: string) =>\n (s: string): string =>\n `${codes}${s}${ANSI.reset}`;\n return {\n unrecoverable: wrap(ANSI.bold + ANSI.red),\n destructive: wrap(ANSI.red),\n expensive: wrap(ANSI.yellow),\n warning: wrap(ANSI.cyan),\n safe: wrap(ANSI.green),\n dim: wrap(ANSI.gray),\n applyToBlock(block: string): string {\n // Walk each line; apply the strongest matching severity color.\n // The marker tokens (🛑/⚠/✓/info-prefixes) are stable across\n // `safety.formatReversibilityBuckets` and the diagnostics output.\n return block\n .split('\\n')\n .map((line) => {\n if (/UNRECOVERABLE/.test(line) || line.includes('🛑'))\n return ANSI.bold + ANSI.red + line + ANSI.reset;\n if (/DESTRUCTIVE/.test(line)) return ANSI.red + line + ANSI.reset;\n if (/EXPENSIVE/.test(line)) return ANSI.yellow + line + ANSI.reset;\n if (/WARNING/.test(line) || line.includes('⚠')) return ANSI.cyan + line + ANSI.reset;\n if (/\\bSAFE\\b|\\bOK\\b/.test(line) || line.includes('✓'))\n return ANSI.green + line + ANSI.reset;\n return line;\n })\n .join('\\n');\n },\n };\n}\n"],"mappings":"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","names":["assessment"]}