@ddt-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-YNMKVJCD.js +87 -0
  2. package/dist/advise-tests-YNMKVJCD.js.map +1 -0
  3. package/dist/ai-NTNPYEKZ.js +86 -0
  4. package/dist/ai-NTNPYEKZ.js.map +1 -0
  5. package/dist/anonymize-LERTWUQO.js +139 -0
  6. package/dist/anonymize-LERTWUQO.js.map +1 -0
  7. package/dist/approval-GGZGKIU4.js +73 -0
  8. package/dist/approval-GGZGKIU4.js.map +1 -0
  9. package/dist/approval-chain-GWJKZHVU.js +118 -0
  10. package/dist/approval-chain-GWJKZHVU.js.map +1 -0
  11. package/dist/audit-log-2PH55BU4.js +159 -0
  12. package/dist/audit-log-2PH55BU4.js.map +1 -0
  13. package/dist/backlog-QNXGOUF4.js +76 -0
  14. package/dist/backlog-QNXGOUF4.js.map +1 -0
  15. package/dist/bisect-W3XKKRWG.js +111 -0
  16. package/dist/bisect-W3XKKRWG.js.map +1 -0
  17. package/dist/bookmarks-XVOGXGMC.js +107 -0
  18. package/dist/bookmarks-XVOGXGMC.js.map +1 -0
  19. package/dist/branch-S3I2IJGQ.js +103 -0
  20. package/dist/branch-S3I2IJGQ.js.map +1 -0
  21. package/dist/build-MP3JQEFO.js +20 -0
  22. package/dist/build-MP3JQEFO.js.map +1 -0
  23. package/dist/catalog-3J3NFNXP.js +137 -0
  24. package/dist/catalog-3J3NFNXP.js.map +1 -0
  25. package/dist/changelog-ZQAH3ULB.js +216 -0
  26. package/dist/changelog-ZQAH3ULB.js.map +1 -0
  27. package/dist/chunk-2FT6HXKS.js +55 -0
  28. package/dist/chunk-2FT6HXKS.js.map +1 -0
  29. package/dist/chunk-DGUM43GV.js +11 -0
  30. package/dist/chunk-DGUM43GV.js.map +1 -0
  31. package/dist/chunk-DL3V7UJ2.js +25 -0
  32. package/dist/chunk-DL3V7UJ2.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-XFXG347C.js +40 -0
  36. package/dist/chunk-XFXG347C.js.map +1 -0
  37. package/dist/cli.js +504 -19402
  38. package/dist/cli.js.map +1 -1
  39. package/dist/compare-IOEATL6G.js +435 -0
  40. package/dist/compare-IOEATL6G.js.map +1 -0
  41. package/dist/compare-profiles-H33CXZPD.js +219 -0
  42. package/dist/compare-profiles-H33CXZPD.js.map +1 -0
  43. package/dist/completion-ZSNCQKJ2.js +89 -0
  44. package/dist/completion-ZSNCQKJ2.js.map +1 -0
  45. package/dist/connection-CDGVEFUC.js +148 -0
  46. package/dist/connection-CDGVEFUC.js.map +1 -0
  47. package/dist/cost-estimate-S2MKHT2H.js +321 -0
  48. package/dist/cost-estimate-S2MKHT2H.js.map +1 -0
  49. package/dist/data-compare-46ZI7KHL.js +128 -0
  50. package/dist/data-compare-46ZI7KHL.js.map +1 -0
  51. package/dist/data-fit-WGEPLD5S.js +127 -0
  52. package/dist/data-fit-WGEPLD5S.js.map +1 -0
  53. package/dist/deploy-status-4H5KJFRC.js +58 -0
  54. package/dist/deploy-status-4H5KJFRC.js.map +1 -0
  55. package/dist/design-ILX3ZSWW.js +135 -0
  56. package/dist/design-ILX3ZSWW.js.map +1 -0
  57. package/dist/diagnose-WPUL67E4.js +150 -0
  58. package/dist/diagnose-WPUL67E4.js.map +1 -0
  59. package/dist/discover-DEO2R5T6.js +78 -0
  60. package/dist/discover-DEO2R5T6.js.map +1 -0
  61. package/dist/docs-QNY3MUVO.js +183 -0
  62. package/dist/docs-QNY3MUVO.js.map +1 -0
  63. package/dist/drift-FDRNPWQA.js +233 -0
  64. package/dist/drift-FDRNPWQA.js.map +1 -0
  65. package/dist/drift-gate-6BWWWMHW.js +103 -0
  66. package/dist/drift-gate-6BWWWMHW.js.map +1 -0
  67. package/dist/error-lookup-4R3Y4RBC.js +56 -0
  68. package/dist/error-lookup-4R3Y4RBC.js.map +1 -0
  69. package/dist/errorReporting-LX6WT4JH.js +109 -0
  70. package/dist/errorReporting-LX6WT4JH.js.map +1 -0
  71. package/dist/exec-JOLH5LPT.js +122 -0
  72. package/dist/exec-JOLH5LPT.js.map +1 -0
  73. package/dist/explain-NS26WE2Y.js +189 -0
  74. package/dist/explain-NS26WE2Y.js.map +1 -0
  75. package/dist/explorer-GSYYYOAL.js +58 -0
  76. package/dist/explorer-GSYYYOAL.js.map +1 -0
  77. package/dist/extract-4LWEZG4O.js +152 -0
  78. package/dist/extract-4LWEZG4O.js.map +1 -0
  79. package/dist/features-KQV4OFIZ.js +54 -0
  80. package/dist/features-KQV4OFIZ.js.map +1 -0
  81. package/dist/feedback-CBLGXUEG.js +158 -0
  82. package/dist/feedback-CBLGXUEG.js.map +1 -0
  83. package/dist/find-SMXRCZ76.js +176 -0
  84. package/dist/find-SMXRCZ76.js.map +1 -0
  85. package/dist/format-HMGG6MY3.js +277 -0
  86. package/dist/format-HMGG6MY3.js.map +1 -0
  87. package/dist/generate-W7VLBDLI.js +160 -0
  88. package/dist/generate-W7VLBDLI.js.map +1 -0
  89. package/dist/graph-YYL5UYCJ.js +168 -0
  90. package/dist/graph-YYL5UYCJ.js.map +1 -0
  91. package/dist/history-GDRFP4PG.js +184 -0
  92. package/dist/history-GDRFP4PG.js.map +1 -0
  93. package/dist/hosts-DRFZTMIJ.js +45 -0
  94. package/dist/hosts-DRFZTMIJ.js.map +1 -0
  95. package/dist/impact-A4NU6CB2.js +63 -0
  96. package/dist/impact-A4NU6CB2.js.map +1 -0
  97. package/dist/import-EGOVKTLX.js +29 -0
  98. package/dist/import-EGOVKTLX.js.map +1 -0
  99. package/dist/import-script-R5RXPDH6.js +79 -0
  100. package/dist/import-script-R5RXPDH6.js.map +1 -0
  101. package/dist/index.cjs +11 -5
  102. package/dist/index.cjs.map +1 -1
  103. package/dist/index.js +8 -2
  104. package/dist/index.js.map +1 -1
  105. package/dist/init-EAOGNGXI.js +54 -0
  106. package/dist/init-EAOGNGXI.js.map +1 -0
  107. package/dist/install-hooks-G3Y5LVXK.js +109 -0
  108. package/dist/install-hooks-G3Y5LVXK.js.map +1 -0
  109. package/dist/license-Z5YSC7XQ.js +43 -0
  110. package/dist/license-Z5YSC7XQ.js.map +1 -0
  111. package/dist/lineage-C5CGVP36.js +555 -0
  112. package/dist/lineage-C5CGVP36.js.map +1 -0
  113. package/dist/lint-AQFPZ3WG.js +144 -0
  114. package/dist/lint-AQFPZ3WG.js.map +1 -0
  115. package/dist/mcp-6ZXOAF7S.js +343 -0
  116. package/dist/mcp-6ZXOAF7S.js.map +1 -0
  117. package/dist/migrate-from-dbt-K4ELOWUD.js +156 -0
  118. package/dist/migrate-from-dbt-K4ELOWUD.js.map +1 -0
  119. package/dist/migrate-platform-E7VZFPO5.js +91 -0
  120. package/dist/migrate-platform-E7VZFPO5.js.map +1 -0
  121. package/dist/optimize-WUJ5ZN5Y.js +109 -0
  122. package/dist/optimize-WUJ5ZN5Y.js.map +1 -0
  123. package/dist/perf-UULZSREY.js +200 -0
  124. package/dist/perf-UULZSREY.js.map +1 -0
  125. package/dist/pii-QHU32VML.js +146 -0
  126. package/dist/pii-QHU32VML.js.map +1 -0
  127. package/dist/pilot-BR6GVK32.js +29 -0
  128. package/dist/pilot-BR6GVK32.js.map +1 -0
  129. package/dist/pr-comment-2FOA3EXG.js +81 -0
  130. package/dist/pr-comment-2FOA3EXG.js.map +1 -0
  131. package/dist/preview-XNY422OU.js +46 -0
  132. package/dist/preview-XNY422OU.js.map +1 -0
  133. package/dist/profile-SQTBNKYS.js +98 -0
  134. package/dist/profile-SQTBNKYS.js.map +1 -0
  135. package/dist/promote-FSGUPIPD.js +417 -0
  136. package/dist/promote-FSGUPIPD.js.map +1 -0
  137. package/dist/publish-HLP3XHM5.js +766 -0
  138. package/dist/publish-HLP3XHM5.js.map +1 -0
  139. package/dist/purge-Y5IOTXKA.js +56 -0
  140. package/dist/purge-Y5IOTXKA.js.map +1 -0
  141. package/dist/query-log-SDDGMJLJ.js +112 -0
  142. package/dist/query-log-SDDGMJLJ.js.map +1 -0
  143. package/dist/refactor-TC7S43F2.js +5809 -0
  144. package/dist/refactor-TC7S43F2.js.map +1 -0
  145. package/dist/refresh-MDJYOYV5.js +39 -0
  146. package/dist/refresh-MDJYOYV5.js.map +1 -0
  147. package/dist/replay-E4664A5K.js +118 -0
  148. package/dist/replay-E4664A5K.js.map +1 -0
  149. package/dist/revert-QWQWCJJB.js +111 -0
  150. package/dist/revert-QWQWCJJB.js.map +1 -0
  151. package/dist/review-7CAVLD67.js +164 -0
  152. package/dist/review-7CAVLD67.js.map +1 -0
  153. package/dist/rollback-suggest-C6D5YFCA.js +79 -0
  154. package/dist/rollback-suggest-C6D5YFCA.js.map +1 -0
  155. package/dist/safer-alternative-QR4QEFUV.js +84 -0
  156. package/dist/safer-alternative-QR4QEFUV.js.map +1 -0
  157. package/dist/safety-OFWUFLK4.js +165 -0
  158. package/dist/safety-OFWUFLK4.js.map +1 -0
  159. package/dist/savings-MEBE4TXI.js +95 -0
  160. package/dist/savings-MEBE4TXI.js.map +1 -0
  161. package/dist/scan-secrets-XCUBMLHL.js +54 -0
  162. package/dist/scan-secrets-XCUBMLHL.js.map +1 -0
  163. package/dist/schema-7JZIG6QR.js +447 -0
  164. package/dist/schema-7JZIG6QR.js.map +1 -0
  165. package/dist/script-BMYVBHFR.js +167 -0
  166. package/dist/script-BMYVBHFR.js.map +1 -0
  167. package/dist/search-TA3C3AZT.js +151 -0
  168. package/dist/search-TA3C3AZT.js.map +1 -0
  169. package/dist/seed-W4Q3L2IU.js +101 -0
  170. package/dist/seed-W4Q3L2IU.js.map +1 -0
  171. package/dist/sketch-6B2V6FJV.js +83 -0
  172. package/dist/sketch-6B2V6FJV.js.map +1 -0
  173. package/dist/snapshot-YMVS322L.js +171 -0
  174. package/dist/snapshot-YMVS322L.js.map +1 -0
  175. package/dist/snippets-EVTN63OU.js +74 -0
  176. package/dist/snippets-EVTN63OU.js.map +1 -0
  177. package/dist/standards-FGJW3CQL.js +238 -0
  178. package/dist/standards-FGJW3CQL.js.map +1 -0
  179. package/dist/suggest-V3LVIFZ5.js +44 -0
  180. package/dist/suggest-V3LVIFZ5.js.map +1 -0
  181. package/dist/suggest-constraints-EX2FCWOQ.js +154 -0
  182. package/dist/suggest-constraints-EX2FCWOQ.js.map +1 -0
  183. package/dist/suite-YTQ3CNX5.js +85 -0
  184. package/dist/suite-YTQ3CNX5.js.map +1 -0
  185. package/dist/telemetry-KOIY3NEQ.js +90 -0
  186. package/dist/telemetry-KOIY3NEQ.js.map +1 -0
  187. package/dist/template-MUJ6X6LN.js +396 -0
  188. package/dist/template-MUJ6X6LN.js.map +1 -0
  189. package/dist/test-XFSQHR2S.js +169 -0
  190. package/dist/test-XFSQHR2S.js.map +1 -0
  191. package/dist/trial-GFTGYCR3.js +31 -0
  192. package/dist/trial-GFTGYCR3.js.map +1 -0
  193. package/dist/validate-LFDEZFFH.js +107 -0
  194. package/dist/validate-LFDEZFFH.js.map +1 -0
  195. package/dist/verify-KRDYOJCR.js +76 -0
  196. package/dist/verify-KRDYOJCR.js.map +1 -0
  197. package/dist/watch-FSG23RR3.js +80 -0
  198. package/dist/watch-FSG23RR3.js.map +1 -0
  199. package/dist/xcompare-U4TXTTIR.js +87 -0
  200. package/dist/xcompare-U4TXTTIR.js.map +1 -0
  201. package/package.json +2 -2
  202. package/dist/cli.cjs +0 -19298
  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,435 @@
1
+ import {
2
+ attachRelatedOptions
3
+ } from "./chunk-DL3V7UJ2.js";
4
+ import {
5
+ addMappingFlags,
6
+ buildMappingFromOptions,
7
+ describeMapping
8
+ } from "./chunk-2FT6HXKS.js";
9
+ import "./chunk-DGUM43GV.js";
10
+
11
+ // src/commands/compare.ts
12
+ import { promises as fs } from "fs";
13
+ import path from "path";
14
+ import { Command } from "commander";
15
+ import {
16
+ CompareEngine,
17
+ LiveSource,
18
+ PacSource,
19
+ ProjectSource,
20
+ ai,
21
+ compileSlice,
22
+ createConnection,
23
+ getProfile,
24
+ loadProject,
25
+ pac as pacNs,
26
+ renderHtmlReport,
27
+ safety,
28
+ typecheck,
29
+ writeCompareHistory
30
+ } from "@ddt-tools/core";
31
+
32
+ // src/util/color.ts
33
+ var ANSI = {
34
+ reset: "\x1B[0m",
35
+ bold: "\x1B[1m",
36
+ red: "\x1B[31m",
37
+ yellow: "\x1B[33m",
38
+ cyan: "\x1B[36m",
39
+ green: "\x1B[32m",
40
+ gray: "\x1B[90m"
41
+ };
42
+ function resolveColorMode(flag) {
43
+ const mode = (flag ?? "auto").toLowerCase();
44
+ if (mode === "always") return true;
45
+ if (mode === "never") return false;
46
+ if (process.env.NO_COLOR) return false;
47
+ return Boolean(process.stdout.isTTY);
48
+ }
49
+ function makeColorizer(enabled) {
50
+ if (!enabled) {
51
+ const identity = (s) => s;
52
+ return {
53
+ unrecoverable: identity,
54
+ destructive: identity,
55
+ expensive: identity,
56
+ warning: identity,
57
+ safe: identity,
58
+ dim: identity,
59
+ applyToBlock: identity
60
+ };
61
+ }
62
+ const wrap = (codes) => (s) => `${codes}${s}${ANSI.reset}`;
63
+ return {
64
+ unrecoverable: wrap(ANSI.bold + ANSI.red),
65
+ destructive: wrap(ANSI.red),
66
+ expensive: wrap(ANSI.yellow),
67
+ warning: wrap(ANSI.cyan),
68
+ safe: wrap(ANSI.green),
69
+ dim: wrap(ANSI.gray),
70
+ applyToBlock(block) {
71
+ return block.split("\n").map((line) => {
72
+ if (/UNRECOVERABLE/.test(line) || line.includes("\u{1F6D1}"))
73
+ return ANSI.bold + ANSI.red + line + ANSI.reset;
74
+ if (/DESTRUCTIVE/.test(line)) return ANSI.red + line + ANSI.reset;
75
+ if (/EXPENSIVE/.test(line)) return ANSI.yellow + line + ANSI.reset;
76
+ if (/WARNING/.test(line) || line.includes("\u26A0")) return ANSI.cyan + line + ANSI.reset;
77
+ if (/\bSAFE\b|\bOK\b/.test(line) || line.includes("\u2713"))
78
+ return ANSI.green + line + ANSI.reset;
79
+ return line;
80
+ }).join("\n");
81
+ }
82
+ };
83
+ }
84
+
85
+ // src/commands/compare.ts
86
+ async function resolveSource(arg) {
87
+ if (arg.startsWith("databricks://")) {
88
+ const rest = arg.slice("databricks://".length);
89
+ const [profileName, catalog, schema] = rest.split("/");
90
+ if (!profileName) throw new Error(`Invalid databricks URI: ${arg}`);
91
+ const profile = await getProfile(profileName);
92
+ const conn = createConnection(profile);
93
+ return new LiveSource(conn, {
94
+ ...catalog ? { catalog } : {},
95
+ ...schema ? { schema } : {}
96
+ });
97
+ }
98
+ const lower = arg.toLowerCase();
99
+ if (lower.endsWith(".ddtproj")) return new ProjectSource(arg);
100
+ if (lower.endsWith(".ddtpac")) return new PacSource(arg);
101
+ throw new Error(
102
+ `Unrecognized source: ${arg}. Use *.ddtproj, *.ddtpac, or databricks://<profile>[/catalog[/schema]].`
103
+ );
104
+ }
105
+ async function maybeLoadSliceFromSource(arg) {
106
+ const lower = arg.toLowerCase();
107
+ if (lower.endsWith(".ddtproj")) {
108
+ const loaded = await loadProject(arg);
109
+ if (!loaded.project.slice) return void 0;
110
+ return compileSlice(loaded.project.slice);
111
+ }
112
+ if (lower.endsWith(".ddtpac")) {
113
+ const srcPac = await pacNs.readPac(arg);
114
+ if (srcPac.manifest.slice) return compileSlice(srcPac.manifest.slice);
115
+ }
116
+ return void 0;
117
+ }
118
+ function compareHistoryRoot(sourceArg) {
119
+ const lower = sourceArg.toLowerCase();
120
+ if (lower.endsWith(".ddtproj") || lower.endsWith(".ddtpac")) {
121
+ return path.dirname(path.resolve(sourceArg));
122
+ }
123
+ return process.cwd();
124
+ }
125
+ function compareCommand() {
126
+ const cmd = new Command("compare");
127
+ cmd.description(
128
+ "Compare two schemas (the desired state vs the current state). Each side may be a .ddtproj, a .ddtpac, or databricks://<profile>[/catalog[/schema]]."
129
+ ).requiredOption(
130
+ "--source <ref>",
131
+ "Source / desired state: .ddtproj, .ddtpac, or databricks://<profile>[/catalog[/schema]]."
132
+ ).requiredOption(
133
+ "--target <ref>",
134
+ "Target / current state: .ddtproj, .ddtpac, or databricks://<profile>[/catalog[/schema]]."
135
+ ).option("--ignore-case", "Compare object FQNs case-insensitively.", false).option(
136
+ "--json",
137
+ "Emit a JSON CompareResult instead of human-readable output. (Alias for --format json.)",
138
+ false
139
+ ).option(
140
+ "--format <fmt>",
141
+ "Output format: summary | json | markdown. Default summary.",
142
+ "summary"
143
+ ).option("--report-html <path>", "Also write a self-contained HTML compare-report to <path>.").option(
144
+ "--no-slice",
145
+ "Disable the source pac's Project Slice (if it has one). Default: a sliced pac is partitioned automatically."
146
+ ).option(
147
+ "--explain",
148
+ "After the diff, call the configured AI provider to narrate each change in plain English with reasoning. Requires `ddt ai` to be configured.",
149
+ false
150
+ ).option(
151
+ "--color <mode>",
152
+ "Colorize severity output: always | never | auto. Default auto (color on TTY).",
153
+ "auto"
154
+ ).option(
155
+ "--type-safe",
156
+ "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.",
157
+ false
158
+ ).option(
159
+ "--break-on <severity>",
160
+ "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).",
161
+ "error"
162
+ ).option(
163
+ "--write-impact [path]",
164
+ "Write the TYPECHECK.1 impact analysis to a JSON file the VS Code extension can surface as squiggle-underlines. Default path: `.ddt/impact.json` resolved relative to CWD. Composes with `--type-safe` \u2014 the file is written before the gate decides exit code."
165
+ ).option(
166
+ "--no-history",
167
+ "Skip writing the compare-history audit record (AUDITCMP.1). Default: every compare run writes a record to `.ddt/history/compare/`, exportable via `ddt audit-log emit`."
168
+ );
169
+ addMappingFlags(cmd);
170
+ cmd.action(async (opts) => {
171
+ const nameMapping = await buildMappingFromOptions(opts);
172
+ const engine = new CompareEngine();
173
+ const source = await resolveSource(String(opts.source));
174
+ const target = await resolveSource(String(opts.target));
175
+ const slice = opts.slice === false ? void 0 : await maybeLoadSliceFromSource(String(opts.source));
176
+ const startedAt = (/* @__PURE__ */ new Date()).toISOString();
177
+ const result = await engine.compare(source, target, {
178
+ ignoreCase: !!opts.ignoreCase,
179
+ ...nameMapping ? { nameMapping } : {},
180
+ ...slice ? { sliceFilter: slice } : {}
181
+ });
182
+ if (opts.history !== false) {
183
+ const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
184
+ try {
185
+ const histAssessment = safety.assess(result);
186
+ const changed = result.summary.added + result.summary.removed + result.summary.modified;
187
+ await writeCompareHistory(compareHistoryRoot(String(opts.source)), {
188
+ startedAt,
189
+ finishedAt,
190
+ durationMs: Date.parse(finishedAt) - Date.parse(startedAt),
191
+ outcome: histAssessment.blocked ? "BLOCKED" : changed === 0 ? "NO_CHANGES" : "CHANGES_FOUND",
192
+ source: { kind: result.source.kind, label: result.source.label },
193
+ target: { kind: result.target.kind, label: result.target.label },
194
+ summary: result.summary,
195
+ sliceApplied: Boolean(slice),
196
+ ...histAssessment.blocked && histAssessment.blockReason ? { blockReason: histAssessment.blockReason } : {}
197
+ });
198
+ } catch {
199
+ }
200
+ }
201
+ if (opts.reportHtml) {
202
+ const html = renderHtmlReport(result, {
203
+ title: `Compare ${result.source.label} \u2192 ${result.target.label}`,
204
+ safety: safety.assess(result)
205
+ });
206
+ const htmlPath = path.resolve(String(opts.reportHtml));
207
+ await fs.mkdir(path.dirname(htmlPath), { recursive: true });
208
+ await fs.writeFile(htmlPath, html, "utf8");
209
+ console.error(`Wrote ${htmlPath} (${html.length} bytes).`);
210
+ }
211
+ const fmt = opts.json ? "json" : String(opts.format ?? "summary").toLowerCase();
212
+ if (fmt === "json") {
213
+ console.log(JSON.stringify(result, null, 2));
214
+ return;
215
+ }
216
+ if (fmt === "markdown") {
217
+ const assessment2 = safety.assess(result);
218
+ console.log(renderCompareMarkdown(result, assessment2));
219
+ return;
220
+ }
221
+ const s = result.summary;
222
+ console.log(`Source: ${result.source.kind}:${result.source.label}`);
223
+ console.log(`Target: ${result.target.kind}:${result.target.label}`);
224
+ const mappingSummary = describeMapping(nameMapping);
225
+ if (mappingSummary) console.log(`Mapping: ${mappingSummary}`);
226
+ if (slice) {
227
+ const outside = result.outsideScope?.length ?? 0;
228
+ const refs = result.referenced?.length ?? 0;
229
+ console.log(
230
+ `Slice active: ${result.objects.length} owned \xB7 ${outside} outside scope (untouched) \xB7 ${refs} referenced`
231
+ );
232
+ }
233
+ console.log(`Summary: +${s.added} -${s.removed} ~${s.modified} =${s.unchanged}`);
234
+ for (const o of result.objects) {
235
+ if (o.kind === "unchanged") continue;
236
+ const glyph = o.kind === "added" ? "+" : o.kind === "removed" ? "-" : "~";
237
+ console.log(` ${glyph} ${o.identity.objectType} ${o.identity.fqn}`);
238
+ if (o.kind === "modified" && o.changes) {
239
+ for (const c of o.changes) {
240
+ console.log(
241
+ ` \u2022 ${c.path}: ${JSON.stringify(c.target)} \u2192 ${JSON.stringify(c.source)}`
242
+ );
243
+ }
244
+ }
245
+ }
246
+ const assessment = safety.assess(result);
247
+ const buckets = safety.groupByReversibility(assessment);
248
+ const total = buckets.unrecoverable.length + buckets.dataImpacting.length + buckets.reversible.length;
249
+ if (total > 0) {
250
+ const colorize = makeColorizer(resolveColorMode(opts.color));
251
+ console.log("");
252
+ console.log("Safety findings (grouped by reversibility):");
253
+ const block = safety.formatReversibilityBuckets(buckets);
254
+ const colored = colorize.applyToBlock(block);
255
+ for (const line of colored.split("\n")) console.log(" " + line);
256
+ if (assessment.blocked && assessment.blockReason) {
257
+ console.error(" " + colorize.unrecoverable("BLOCKED: " + assessment.blockReason));
258
+ }
259
+ }
260
+ if (opts.explain) {
261
+ console.log("");
262
+ console.log("AI explanation:");
263
+ try {
264
+ const userPrompt = buildExplainPrompt(result, assessment);
265
+ const reply = await ai.complete(
266
+ [
267
+ { role: "system", content: SYSTEM_PROMPT },
268
+ { role: "user", content: userPrompt }
269
+ ],
270
+ { feature: "compare.explain" }
271
+ );
272
+ for (const line of reply.text.split("\n")) console.log(" " + line);
273
+ } catch (err) {
274
+ console.error(" --explain failed: " + (err instanceof Error ? err.message : String(err)));
275
+ console.error(
276
+ " Run `ddt ai status` to verify your AI provider is configured (`ddt ai test` to send a probe)."
277
+ );
278
+ }
279
+ }
280
+ if (opts.writeImpact !== void 0) {
281
+ await writeImpactJson(
282
+ result,
283
+ opts.writeImpact === true ? void 0 : String(opts.writeImpact),
284
+ String(opts.source)
285
+ );
286
+ }
287
+ if (opts.typeSafe) {
288
+ const breakOnRaw = String(opts.breakOn ?? "error").toLowerCase();
289
+ if (breakOnRaw !== "error" && breakOnRaw !== "warning") {
290
+ throw new Error(
291
+ `--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).`
292
+ );
293
+ }
294
+ const breakOn = breakOnRaw;
295
+ const impactFindings = typecheck.analyzeImpact(result);
296
+ const impactSummary = typecheck.summarizeImpact(impactFindings);
297
+ if (impactSummary.findingsCount > 0) {
298
+ console.log("");
299
+ console.log(
300
+ `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).`
301
+ );
302
+ for (const f of impactFindings) {
303
+ for (const r of f.ripples) {
304
+ const tag = r.severity === "error" ? "ERROR" : "WARN ";
305
+ console.log(
306
+ ` ${tag} ${r.fqn} \u2190 ${f.breakingChangeOn.fqn} (${f.breakingChangeOn.kind})`
307
+ );
308
+ }
309
+ }
310
+ const failOnError = impactSummary.errors > 0;
311
+ const failOnWarning = breakOn === "warning" && impactSummary.warnings > 0;
312
+ if (failOnError) {
313
+ console.error(
314
+ `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.`
315
+ );
316
+ process.exitCode = 2;
317
+ } else if (failOnWarning) {
318
+ console.error(
319
+ `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.`
320
+ );
321
+ process.exitCode = 2;
322
+ }
323
+ } else {
324
+ console.log("Type-safety gate PASSED \u2014 no breaking-change ripples detected.");
325
+ }
326
+ }
327
+ });
328
+ attachRelatedOptions(cmd, [
329
+ "compare.ignoreCase",
330
+ "compare.ignoreComments",
331
+ "compare.ignoreFormattingDifferences",
332
+ "compare.ignoreClusterBy",
333
+ "compare.ignorePartitioning",
334
+ "compare.ignoreStorageLocation"
335
+ ]);
336
+ return cmd;
337
+ }
338
+ async function writeImpactJson(result, explicitPath, sourceArg) {
339
+ const findings = typecheck.analyzeImpact(result);
340
+ const summary = typecheck.summarizeImpact(findings);
341
+ const file = {
342
+ version: typecheck.IMPACT_FILE_VERSION,
343
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
344
+ source: `${result.source.kind}:${result.source.label}`,
345
+ target: `${result.target.kind}:${result.target.label}`,
346
+ findings,
347
+ summary
348
+ };
349
+ const outPath = explicitPath ? path.resolve(explicitPath) : defaultImpactPath(sourceArg);
350
+ await fs.mkdir(path.dirname(outPath), { recursive: true });
351
+ await fs.writeFile(outPath, typecheck.serializeImpactFile(file));
352
+ console.log(
353
+ `Wrote ${outPath} \u2014 ${summary.findingsCount} finding(s), ${summary.errors} error / ${summary.warnings} warning ripple(s).`
354
+ );
355
+ }
356
+ function defaultImpactPath(sourceArg) {
357
+ const lower = sourceArg.toLowerCase();
358
+ if (lower.endsWith(".ddtproj") || lower.endsWith(".ddtpac")) {
359
+ return path.resolve(path.dirname(sourceArg), ".ddt", "impact.json");
360
+ }
361
+ return path.resolve(".ddt", "impact.json");
362
+ }
363
+ var SYSTEM_PROMPT = `You are a senior Databricks Unity Catalog 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:
364
+ - what it is (e.g. "a new fact table is added")
365
+ - why a reasonable engineer might do this (the intent)
366
+ - what to watch out for (the risk, if any)
367
+
368
+ 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.`;
369
+ function buildExplainPrompt(result, assessment) {
370
+ const lines = [];
371
+ lines.push(`Source: ${result.source.label}`);
372
+ lines.push(`Target: ${result.target.label}`);
373
+ lines.push(
374
+ `Summary: +${result.summary.added} added, -${result.summary.removed} removed, ~${result.summary.modified} modified.`
375
+ );
376
+ lines.push(
377
+ `Safety: ${assessment.unrecoverable.length} unrecoverable, ${assessment.destructive.length} destructive, ${assessment.expensive.length} expensive, ${assessment.warnings.length} warnings.`
378
+ );
379
+ lines.push("");
380
+ lines.push("Changes (up to 40):");
381
+ const changed = result.objects.filter((o) => o.kind !== "unchanged").slice(0, 40);
382
+ for (const o of changed) {
383
+ lines.push(`- ${o.kind} ${o.identity.objectType} ${o.identity.fqn}`);
384
+ }
385
+ if (result.objects.filter((o) => o.kind !== "unchanged").length > 40) {
386
+ lines.push(
387
+ ` (\u2026 ${result.objects.filter((o) => o.kind !== "unchanged").length - 40} more truncated)`
388
+ );
389
+ }
390
+ lines.push("");
391
+ lines.push(
392
+ "Please narrate this diff in plain English. Group related changes together when it helps."
393
+ );
394
+ return lines.join("\n");
395
+ }
396
+ function renderCompareMarkdown(result, assessment) {
397
+ const lines = [];
398
+ lines.push(
399
+ `# Compare: ${result.source.kind}:${result.source.label} \u2192 ${result.target.kind}:${result.target.label}`
400
+ );
401
+ lines.push("");
402
+ lines.push(
403
+ `**Summary**: +${result.summary.added} added \xB7 -${result.summary.removed} removed \xB7 ~${result.summary.modified} modified \xB7 =${result.summary.unchanged} unchanged.`
404
+ );
405
+ lines.push("");
406
+ lines.push("**Safety**:");
407
+ lines.push(`- \u{1F6D1} Unrecoverable: ${assessment.unrecoverable.length}`);
408
+ lines.push(`- \u{1F525} Destructive: ${assessment.destructive.length}`);
409
+ lines.push(`- \u23F1 Expensive: ${assessment.expensive.length}`);
410
+ lines.push(`- \u26A0 Warnings: ${assessment.warnings.length}`);
411
+ if (assessment.blocked) {
412
+ lines.push("");
413
+ lines.push(
414
+ `> **BLOCKED**: ${assessment.blockReason ?? "safety classifier refuses to proceed"}`
415
+ );
416
+ }
417
+ lines.push("");
418
+ const changed = result.objects.filter((o) => o.kind !== "unchanged");
419
+ if (changed.length === 0) {
420
+ lines.push("_No object-level changes._");
421
+ return lines.join("\n");
422
+ }
423
+ lines.push("| Kind | Type | FQN |");
424
+ lines.push("| --- | --- | --- |");
425
+ for (const o of changed) {
426
+ lines.push(`| ${o.kind} | \`${o.identity.objectType}\` | \`${o.identity.fqn}\` |`);
427
+ }
428
+ return lines.join("\n");
429
+ }
430
+ export {
431
+ SYSTEM_PROMPT,
432
+ buildExplainPrompt,
433
+ compareCommand
434
+ };
435
+ //# sourceMappingURL=compare-IOEATL6G.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/compare.ts","../src/util/color.ts"],"sourcesContent":["import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { Command } from 'commander';\nimport {\n CompareEngine,\n LiveSource,\n PacSource,\n ProjectSource,\n ai,\n compileSlice,\n createConnection,\n getProfile,\n loadProject,\n pac as pacNs,\n renderHtmlReport,\n safety,\n typecheck,\n writeCompareHistory,\n type CompareSource,\n} from '@ddt-tools/core';\nimport { addMappingFlags, buildMappingFromOptions, describeMapping } from '../util/mapping.js';\nimport { attachRelatedOptions } from '../util/help-catalog.js';\nimport { makeColorizer, resolveColorMode } from '../util/color.js';\n\n/**\n * `ddt compare` — diff two schemas in any direction. Each side may be a\n * `.ddtproj` (parsed in place), a `.ddtpac` (built artifact), or a live\n * workspace via `databricks://<profile>[/catalog[/schema]]` (CLIPARITY.6 —\n * mirrors SDT's project/pac/live compare; `databricks://` is the DDT analogue\n * of `snowflake://`).\n *\n * Supports logical-name mapping (`--map`, `--map-file`) so two schemas whose\n * catalog / schema names differ can compare semantically. See\n * `docs/LOGICAL_NAME_MAPPING.md`.\n */\n\n/**\n * Resolve a polymorphic source/target argument into a `CompareSource`.\n * Supported forms (mirrors SDT's `resolveSource`):\n * - path/to/foo.ddtproj → ProjectSource (parses `.sql` in place)\n * - path/to/foo.ddtpac → PacSource (built artifact)\n * - databricks://<profile>[/catalog[/schema]] → LiveSource (workspace extract)\n */\nasync function resolveSource(arg: string): Promise<CompareSource> {\n if (arg.startsWith('databricks://')) {\n const rest = arg.slice('databricks://'.length);\n const [profileName, catalog, schema] = rest.split('/');\n if (!profileName) throw new Error(`Invalid databricks URI: ${arg}`);\n const profile = await getProfile(profileName);\n const conn = createConnection(profile);\n return new LiveSource(conn, {\n ...(catalog ? { catalog } : {}),\n ...(schema ? { schema } : {}),\n });\n }\n const lower = arg.toLowerCase();\n if (lower.endsWith('.ddtproj')) return new ProjectSource(arg);\n if (lower.endsWith('.ddtpac')) return new PacSource(arg);\n throw new Error(\n `Unrecognized source: ${arg}. Use *.ddtproj, *.ddtpac, or databricks://<profile>[/catalog[/schema]].`,\n );\n}\n\n/**\n * Auto-load a Project Slice from the source argument. A `.ddtproj` carries its\n * slice in the project file; a `.ddtpac` carries it in the manifest. Live\n * (`databricks://`) sources have no slice. Returns `undefined` when there's no\n * slice to apply.\n */\nasync function maybeLoadSliceFromSource(\n arg: string,\n): Promise<ReturnType<typeof compileSlice> | undefined> {\n const lower = arg.toLowerCase();\n if (lower.endsWith('.ddtproj')) {\n const loaded = await loadProject(arg);\n if (!loaded.project.slice) return undefined;\n return compileSlice(loaded.project.slice);\n }\n if (lower.endsWith('.ddtpac')) {\n const srcPac = await pacNs.readPac(arg);\n if (srcPac.manifest.slice) return compileSlice(srcPac.manifest.slice);\n }\n return undefined;\n}\n\n/**\n * AUDITCMP.1 — workspace root for the compare-history record. Project / pac\n * sources anchor the audit trail next to the artifact being compared; live\n * (`databricks://`) sources fall back to CWD. Mirrors SDT's `compareHistoryRoot`.\n */\nfunction compareHistoryRoot(sourceArg: string): string {\n const lower = sourceArg.toLowerCase();\n if (lower.endsWith('.ddtproj') || lower.endsWith('.ddtpac')) {\n return path.dirname(path.resolve(sourceArg));\n }\n return process.cwd();\n}\nexport function compareCommand(): Command {\n const cmd = new Command('compare');\n cmd\n .description(\n 'Compare two schemas (the desired state vs the current state). Each side may be a .ddtproj, a .ddtpac, or databricks://<profile>[/catalog[/schema]].',\n )\n .requiredOption(\n '--source <ref>',\n 'Source / desired state: .ddtproj, .ddtpac, or databricks://<profile>[/catalog[/schema]].',\n )\n .requiredOption(\n '--target <ref>',\n 'Target / current state: .ddtproj, .ddtpac, or databricks://<profile>[/catalog[/schema]].',\n )\n .option('--ignore-case', 'Compare object FQNs case-insensitively.', false)\n .option(\n '--json',\n 'Emit a JSON CompareResult instead of human-readable output. (Alias for --format json.)',\n false,\n )\n .option(\n '--format <fmt>',\n 'Output format: summary | json | markdown. Default summary.',\n 'summary',\n )\n .option('--report-html <path>', 'Also write a self-contained HTML compare-report to <path>.')\n .option(\n '--no-slice',\n \"Disable the source pac's Project Slice (if it has one). Default: a sliced pac is partitioned 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 `ddt 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: `.ddt/impact.json` resolved relative to CWD. 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 `.ddt/history/compare/`, exportable via `ddt audit-log emit`.',\n );\n addMappingFlags(cmd);\n cmd.action(async (opts) => {\n const nameMapping = await buildMappingFromOptions(opts);\n const engine = new CompareEngine();\n const source = await resolveSource(String(opts.source));\n const target = await resolveSource(String(opts.target));\n\n // Auto-load the source's Project Slice (from a `.ddtproj` file or a\n // `.ddtpac` manifest) unless --no-slice was set. Live sources have no slice.\n const slice =\n opts.slice === false ? undefined : await maybeLoadSliceFromSource(String(opts.source));\n\n const startedAt = new Date().toISOString();\n const result = await engine.compare(source, target, {\n ignoreCase: !!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(opts.source)), {\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.reportHtml) {\n const html = renderHtmlReport(result, {\n title: `Compare ${result.source.label} → ${result.target.label}`,\n safety: safety.assess(result),\n });\n const htmlPath = path.resolve(String(opts.reportHtml));\n await fs.mkdir(path.dirname(htmlPath), { recursive: true });\n await fs.writeFile(htmlPath, html, 'utf8');\n console.error(`Wrote ${htmlPath} (${html.length} bytes).`);\n }\n\n const fmt = opts.json ? 'json' : String(opts.format ?? 'summary').toLowerCase();\n if (fmt === 'json') {\n console.log(JSON.stringify(result, null, 2));\n return;\n }\n if (fmt === 'markdown') {\n const assessment = safety.assess(result);\n console.log(renderCompareMarkdown(result, assessment));\n return;\n }\n const s = result.summary;\n console.log(`Source: ${result.source.kind}:${result.source.label}`);\n console.log(`Target: ${result.target.kind}:${result.target.label}`);\n const mappingSummary = describeMapping(nameMapping);\n if (mappingSummary) console.log(`Mapping: ${mappingSummary}`);\n if (slice) {\n const outside = result.outsideScope?.length ?? 0;\n const refs = result.referenced?.length ?? 0;\n console.log(\n `Slice active: ${result.objects.length} owned · ${outside} outside scope (untouched) · ${refs} referenced`,\n );\n }\n console.log(`Summary: +${s.added} -${s.removed} ~${s.modified} =${s.unchanged}`);\n for (const o of result.objects) {\n if (o.kind === 'unchanged') continue;\n const glyph = o.kind === 'added' ? '+' : o.kind === 'removed' ? '-' : '~';\n console.log(` ${glyph} ${o.identity.objectType} ${o.identity.fqn}`);\n if (o.kind === 'modified' && o.changes) {\n for (const c of o.changes) {\n console.log(\n ` • ${c.path}: ${JSON.stringify(c.target)} → ${JSON.stringify(c.source)}`,\n );\n }\n }\n }\n\n // Reversibility-grouped safety findings — Unrecoverable first (highest\n // urgency), then Data-impacting, then Reversible. Empty buckets are\n // skipped, and an empty assessment is silent.\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 console.log('');\n console.log('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')) console.log(' ' + line);\n if (assessment.blocked && assessment.blockReason) {\n console.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.\n if (opts.explain) {\n console.log('');\n console.log('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')) console.log(' ' + line);\n } catch (err) {\n console.error(' --explain failed: ' + (err instanceof Error ? err.message : String(err)));\n console.error(\n ' Run `ddt ai status` to verify your AI provider is configured (`ddt 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(opts.source),\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 console.log('');\n console.log(\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 console.log(\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 console.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 console.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 console.log('Type-safety gate PASSED — no breaking-change ripples detected.');\n }\n }\n });\n attachRelatedOptions(cmd, [\n 'compare.ignoreCase',\n 'compare.ignoreComments',\n 'compare.ignoreFormattingDifferences',\n 'compare.ignoreClusterBy',\n 'compare.ignorePartitioning',\n 'compare.ignoreStorageLocation',\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>/.ddt/impact.json` when the source is a `.ddtproj` / `.ddtpac`,\n * otherwise `<cwd>/.ddt/impact.json` (live `databricks://` sources). Mirrors\n * SDT's `defaultImpactPath`.\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 console.log(\n `Wrote ${outPath} — ${summary.findingsCount} finding(s), ${summary.errors} error / ${summary.warnings} warning ripple(s).`,\n );\n}\n\nfunction defaultImpactPath(sourceArg: string): string {\n const lower = sourceArg.toLowerCase();\n if (lower.endsWith('.ddtproj') || lower.endsWith('.ddtpac')) {\n return path.resolve(path.dirname(sourceArg), '.ddt', 'impact.json');\n }\n return path.resolve('.ddt', 'impact.json');\n}\n\nexport const SYSTEM_PROMPT = `You are a senior Databricks Unity Catalog 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\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\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","/**\n * ANSI severity coloring for CLI output, DDT side. Paired with\n * `Snowflake/packages/cli/src/util/color.ts` byte-for-byte at the\n * API level.\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 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 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 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"]}