@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 @@
1
+ {"version":3,"sources":["../src/commands/test.ts"],"sourcesContent":["import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { Command } from 'commander';\nimport {\n loadProject,\n parseProjectModel,\n testFramework,\n createConnection,\n getProfile,\n} from '@ddt-tools/core';\n\n/**\n * `ddt test` — declarative test framework. Mirrors `sdt test`.\n */\nexport function testCommand(): Command {\n const cmd = new Command('test').description(\n 'Declarative tests (unique / not_null / accepted_values / relationships / expression) compiled from YAML to SQL.',\n );\n\n cmd\n .command('list')\n .description('Enumerate test files and their test counts.')\n .requiredOption('-p, --project <path>', 'Path to the .ddtproj file.')\n .action(async (opts) => {\n const loaded = await loadProject(String(opts.project));\n const files = await testFramework.discoverTests(loaded.rootDir);\n if (files.length === 0) {\n console.log(\n 'No tests found. Add files under <project>/tests/<catalog>/<schema>/<table>.test.yaml.',\n );\n return;\n }\n for (const f of files) {\n console.log(` ${f.fqn.padEnd(40)} ${String(f.tests.length).padStart(3)} test(s)`);\n for (const t of f.tests) {\n const cols =\n 'columns' in t\n ? `(${(t.columns ?? []).join(', ')})`\n : 'column' in t\n ? `(${t.column})`\n : '';\n console.log(` - ${t.kind}${cols}${t.name ? ` ${t.name}` : ''}`);\n }\n }\n console.log('');\n console.log(\n `${files.length} file(s), ${files.reduce((acc, f) => acc + f.tests.length, 0)} test(s) total.`,\n );\n });\n\n cmd\n .command('render')\n .description('Compile every test YAML into SQL assertions. Offline; no workspace contact.')\n .requiredOption('-p, --project <path>', 'Path to the .ddtproj file.')\n .option('-o, --out <path>', 'Write to file. Defaults to stdout.')\n .action(async (opts) => {\n const loaded = await loadProject(String(opts.project));\n const files = await testFramework.discoverTests(loaded.rootDir);\n const sql = testFramework.renderAllTests(files);\n if (opts.out) {\n const out = path.resolve(String(opts.out));\n await fs.mkdir(path.dirname(out), { recursive: true });\n await fs.writeFile(out, sql, 'utf8');\n console.error(\n `Wrote ${out} (${sql.length} bytes, ${files.reduce((a, f) => a + f.tests.length, 0)} test(s)).`,\n );\n } else {\n process.stdout.write(sql);\n }\n });\n\n cmd\n .command('run')\n .description(\n 'Execute every test assertion against a live Databricks workspace and report pass/fail. ' +\n 'Exits 2 when any error-severity test fails. Pro tier — composes DCM item 5 (data-quality expectations) ' +\n 'with the existing dbt-test parity framework.',\n )\n .requiredOption('-p, --project <path>', 'Path to the .ddtproj file.')\n .requiredOption('-c, --connection <profile>', 'Connection profile name.')\n .option(\n '--select <pattern>',\n 'Filter tests to files whose FQN matches a glob pattern. E.g. \"main.public.*\".',\n )\n .option('--fail-fast', 'Stop on the first error-severity failure.', false)\n .option(\n '--sample <n>',\n 'Cap failing-row samples captured per test. Default 10. 0 to skip.',\n '10',\n )\n .option(\n '--update-snapshots',\n 'TEST.3: overwrite every saved snapshot under .ddt/tests/__snapshots__/ with the live rows. Use when intentional schema change makes drift expected.',\n false,\n )\n .option('--format <fmt>', 'Output format: text | json. Default text.', 'text')\n .option('-o, --out <path>', 'Write report to a file instead of stdout.')\n .action(async (opts) => {\n const loaded = await loadProject(String(opts.project));\n let files = await testFramework.discoverTests(loaded.rootDir);\n if (opts.select) {\n const re = globToRegex(String(opts.select));\n files = files.filter((f) => re.test(f.fqn));\n }\n if (files.length === 0) {\n console.log(\n 'No tests found. Add files under <project>/tests/<catalog>/<schema>/<table>.test.yaml.',\n );\n return;\n }\n const profile = await getProfile(String(opts.connection));\n const conn = createConnection(profile);\n console.error(`test run: connecting to ${profile.auth.host}…`);\n await conn.connect();\n const snapshotsDir = path.join(loaded.rootDir, '.ddt', 'tests', '__snapshots__');\n const snapshotStorage: testFramework.SnapshotStorage = {\n async read(name) {\n try {\n return await fs.readFile(path.join(snapshotsDir, `${name}.json`), 'utf8');\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw e;\n }\n },\n async write(name, body) {\n await fs.mkdir(snapshotsDir, { recursive: true });\n await fs.writeFile(path.join(snapshotsDir, `${name}.json`), body, 'utf8');\n },\n };\n let report;\n try {\n report = await testFramework.runTestFiles(files, conn, {\n failFast: opts.failFast === true,\n failingRowSampleCap: Number(opts.sample ?? '10'),\n snapshotStorage,\n updateSnapshots: opts.updateSnapshots === true,\n });\n } finally {\n await conn.disconnect().catch(() => undefined);\n }\n const output =\n String(opts.format).toLowerCase() === 'json'\n ? testFramework.renderJsonReport(report)\n : testFramework.renderTextReport(report);\n if (opts.out) {\n const out = path.resolve(String(opts.out));\n await fs.mkdir(path.dirname(out), { recursive: true });\n await fs.writeFile(out, output, 'utf8');\n console.error(\n `Wrote ${out} — ${report.summary.pass} pass / ${report.summary.fail} fail / ${report.summary.error} error.`,\n );\n } else {\n process.stdout.write(output);\n }\n if (report.summary.blocking) {\n process.exitCode = 2;\n }\n });\n\n cmd\n .command('coverage')\n .description(\n 'Report which project objects have at least one declarative test (TEST.4). ' +\n 'Offline; no workspace contact. Emits a Markdown table (default) or JSON.',\n )\n .requiredOption('-p, --project <path>', 'Path to the .ddtproj file.')\n .option('--format <fmt>', 'Output format: text | json. Default text.', 'text')\n .option('-o, --out <path>', 'Write report to a file instead of stdout.')\n .action(async (opts) => {\n const loaded = await loadProject(String(opts.project));\n const files = await testFramework.discoverTests(loaded.rootDir);\n const model = await parseProjectModel(loaded);\n const testable = new Set<string>([\n 'TABLE',\n 'MANAGED_TABLE',\n 'EXTERNAL_TABLE',\n 'VIEW',\n 'MATERIALIZED_VIEW',\n 'STREAMING_TABLE',\n ]);\n const fqns: string[] = [];\n for (const obj of model) {\n if (!testable.has(obj.objectType)) continue;\n const cat = obj.fqn.database;\n const sc = obj.fqn.schema;\n if (!cat || !sc) continue;\n fqns.push(`${cat}.${sc}.${obj.fqn.name}`);\n }\n const report = testFramework.computeTestCoverage(files, fqns);\n const output =\n String(opts.format).toLowerCase() === 'json'\n ? testFramework.renderTestCoverageJson(report)\n : testFramework.renderTestCoverageMarkdown(report);\n if (opts.out) {\n const out = path.resolve(String(opts.out));\n await fs.mkdir(path.dirname(out), { recursive: true });\n await fs.writeFile(out, output, 'utf8');\n console.error(\n `Wrote ${out} — ${report.withTests}/${report.total} object(s) covered (${report.coveragePct}%).`,\n );\n } else {\n process.stdout.write(output + '\\n');\n }\n });\n\n return cmd;\n}\n\n/** Convert a simple glob pattern (`*` wildcard, `.` literal) to a RegExp. */\nfunction globToRegex(pattern: string): RegExp {\n const escaped = pattern.replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&').replace(/\\*/g, '.*');\n return new RegExp(`^${escaped}$`, 'i');\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,31 @@
1
+ import {
2
+ logger
3
+ } from "./chunk-VM2H4LAO.js";
4
+ import "./chunk-DGUM43GV.js";
5
+
6
+ // src/commands/trial.ts
7
+ import { Command } from "commander";
8
+ import { license } from "@ddt-tools/core";
9
+ function trialCommand() {
10
+ const cmd = new Command("trial").description(
11
+ "Manage the DDT 30-day Pro trial (no account required)."
12
+ );
13
+ cmd.command("start").description("Begin a 30-day Pro trial.").action(async () => {
14
+ const record = await license.startTrial();
15
+ logger.info(`Pro trial started. Expires on ${record.expiresAt.slice(0, 10)}.`);
16
+ logger.info("Run `ddt trial status` to check remaining time.");
17
+ });
18
+ cmd.command("status").description("Show current trial state.").action(async () => {
19
+ const status = await license.getTrialStatus();
20
+ logger.info(license.describeTrialStatus(status));
21
+ });
22
+ cmd.command("reset").description("Reset trial state (dev/test only \u2014 requires DDT_DEV_RESET_TRIAL=1).").action(async () => {
23
+ await license.resetTrial();
24
+ logger.info("Trial state cleared.");
25
+ });
26
+ return cmd;
27
+ }
28
+ export {
29
+ trialCommand
30
+ };
31
+ //# sourceMappingURL=trial-GFTGYCR3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/trial.ts"],"sourcesContent":["/**\n * `ddt trial start|status|reset` — manage the local 30-day Pro trial.\n * Mirrors `sdt trial` in shape (PIL.1 + PIL.4).\n */\nimport { Command } from 'commander';\nimport { license } from '@ddt-tools/core';\nimport { logger } from '../util/logger.js';\n\nexport function trialCommand(): Command {\n const cmd = new Command('trial').description(\n 'Manage the DDT 30-day Pro trial (no account required).',\n );\n\n cmd\n .command('start')\n .description('Begin a 30-day Pro trial.')\n .action(async () => {\n const record = await license.startTrial();\n logger.info(`Pro trial started. Expires on ${record.expiresAt.slice(0, 10)}.`);\n logger.info('Run `ddt trial status` to check remaining time.');\n });\n\n cmd\n .command('status')\n .description('Show current trial state.')\n .action(async () => {\n const status = await license.getTrialStatus();\n logger.info(license.describeTrialStatus(status));\n });\n\n cmd\n .command('reset')\n .description('Reset trial state (dev/test only — requires DDT_DEV_RESET_TRIAL=1).')\n .action(async () => {\n await license.resetTrial();\n logger.info('Trial state cleared.');\n });\n\n return cmd;\n}\n"],"mappings":";;;;;;AAIA,SAAS,eAAe;AACxB,SAAS,eAAe;AAGjB,SAAS,eAAwB;AACtC,QAAM,MAAM,IAAI,QAAQ,OAAO,EAAE;AAAA,IAC/B;AAAA,EACF;AAEA,MACG,QAAQ,OAAO,EACf,YAAY,2BAA2B,EACvC,OAAO,YAAY;AAClB,UAAM,SAAS,MAAM,QAAQ,WAAW;AACxC,WAAO,KAAK,iCAAiC,OAAO,UAAU,MAAM,GAAG,EAAE,CAAC,GAAG;AAC7E,WAAO,KAAK,iDAAiD;AAAA,EAC/D,CAAC;AAEH,MACG,QAAQ,QAAQ,EAChB,YAAY,2BAA2B,EACvC,OAAO,YAAY;AAClB,UAAM,SAAS,MAAM,QAAQ,eAAe;AAC5C,WAAO,KAAK,QAAQ,oBAAoB,MAAM,CAAC;AAAA,EACjD,CAAC;AAEH,MACG,QAAQ,OAAO,EACf,YAAY,0EAAqE,EACjF,OAAO,YAAY;AAClB,UAAM,QAAQ,WAAW;AACzB,WAAO,KAAK,sBAAsB;AAAA,EACpC,CAAC;AAEH,SAAO;AACT;","names":[]}
@@ -0,0 +1,107 @@
1
+ import {
2
+ attachRelatedOptions
3
+ } from "./chunk-DL3V7UJ2.js";
4
+ import "./chunk-DGUM43GV.js";
5
+
6
+ // src/commands/validate.ts
7
+ import { promises as fs } from "fs";
8
+ import path from "path";
9
+ import { Command } from "commander";
10
+ import {
11
+ catalog,
12
+ discoverObjectFiles,
13
+ loadProject,
14
+ parseProjectModel,
15
+ validate as validateNs,
16
+ variables as variablesNs
17
+ } from "@ddt-tools/core";
18
+ function validateCommand() {
19
+ const cmd = new Command("validate");
20
+ cmd.description(
21
+ "Validate a .ddtproj \u2014 schema check; optionally resolve every object reference (--references)."
22
+ ).requiredOption("-p, --project <path>", "Path to .ddtproj").option(
23
+ "--references",
24
+ "Run the build-time semantic resolver \u2014 flag dangling/external/temp/dynamic references."
25
+ ).option(
26
+ "--columns",
27
+ "[--references] Also resolve every column reference. ERROR on missing column on a known table. Phase 2 of the SSDT-build-equivalent resolver."
28
+ ).option(
29
+ "--min-severity <s>",
30
+ "[--references] Filter findings: error | warning | info. Default info.",
31
+ "info"
32
+ ).option("--format <fmt>", "[--references] table | json. Default table.", "table").option("-o, --out <path>", "[--references] Write report to file instead of stdout.").option(
33
+ "--check-variables",
34
+ "VARSYNTAX.4 \u2014 refuse if any `$(VAR)` reference in a tracked `.sql` file is not declared in built-ins / deploymentProfiles[*].variables. Exits 1 when undeclared references are found."
35
+ ).action(async (opts) => {
36
+ const loaded = await loadProject(String(opts.project));
37
+ const files = await discoverObjectFiles(loaded);
38
+ console.log(`Project ${loaded.project.name} v${loaded.project.version} is valid.`);
39
+ console.log(
40
+ ` Scope: ${loaded.project.scope.type}${loaded.project.scope.catalog ? "/" + loaded.project.scope.catalog : ""}${loaded.project.scope.schema ? "/" + loaded.project.scope.schema : ""}`
41
+ );
42
+ console.log(` Files: ${files.length}`);
43
+ console.log(` Profiles: ${Object.keys(loaded.project.deploymentProfiles ?? {}).length}`);
44
+ if (opts.checkVariables) {
45
+ const projRoot = path.dirname(path.resolve(String(opts.project)));
46
+ const { results } = await catalog.mapPool(
47
+ files,
48
+ async (f) => {
49
+ const relPath = path.relative(projRoot, f).split(path.sep).join("/");
50
+ const content = await fs.readFile(f, "utf8");
51
+ return { path: relPath, content };
52
+ },
53
+ { concurrency: 16, stopOnError: true }
54
+ );
55
+ const fileContents = results.filter(
56
+ (r) => r !== void 0
57
+ );
58
+ const profilesBlock = loaded.project.deploymentProfiles ?? {};
59
+ const profileVariableKeys = Object.values(profilesBlock).map(
60
+ (p) => Object.keys(p.variables ?? {})
61
+ );
62
+ const report2 = variablesNs.checkVariableReferences({
63
+ files: fileContents,
64
+ deploymentProfileVariables: profileVariableKeys
65
+ });
66
+ if (report2.undeclared.length > 0) {
67
+ const msg = variablesNs.formatCheckVariablesReport(report2);
68
+ process.stdout.write(msg + "\n");
69
+ process.exitCode = 1;
70
+ return;
71
+ }
72
+ console.log(` Variables: ${report2.references.length} reference(s), all declared.`);
73
+ }
74
+ if (!opts.references) return;
75
+ console.log("");
76
+ console.log("Resolving references\u2026");
77
+ const model = await parseProjectModel(loaded);
78
+ const report = validateNs.validateProject(model, {
79
+ source: String(opts.project),
80
+ minSeverity: opts.minSeverity,
81
+ columns: Boolean(opts.columns)
82
+ });
83
+ const fmt = String(opts.format ?? "table").toLowerCase();
84
+ const payload = fmt === "json" ? JSON.stringify(report, null, 2) : validateNs.formatBuildReport(report);
85
+ if (opts.out) {
86
+ const out = path.resolve(String(opts.out));
87
+ await fs.mkdir(path.dirname(out), { recursive: true });
88
+ await fs.writeFile(out, payload + (payload.endsWith("\n") ? "" : "\n"), "utf8");
89
+ console.log(
90
+ `Wrote ${out} (${payload.length} bytes, ${report.findings.length} finding(s)).`
91
+ );
92
+ } else {
93
+ process.stdout.write(payload + (payload.endsWith("\n") ? "" : "\n"));
94
+ }
95
+ if (report.totals.error > 0) process.exitCode = 1;
96
+ });
97
+ attachRelatedOptions(cmd, [
98
+ "compare.ignoreCase",
99
+ "compare.ignoreComments",
100
+ "compare.ignoreFormattingDifferences"
101
+ ]);
102
+ return cmd;
103
+ }
104
+ export {
105
+ validateCommand
106
+ };
107
+ //# sourceMappingURL=validate-LFDEZFFH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/validate.ts"],"sourcesContent":["import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { Command } from 'commander';\nimport {\n catalog,\n discoverObjectFiles,\n loadProject,\n parseProjectModel,\n validate as validateNs,\n variables as variablesNs,\n} from '@ddt-tools/core';\nimport { attachRelatedOptions } from '../util/help-catalog.js';\n\n/**\n * `ddt validate` — schema-shape sanity check on a `.ddtproj`. With\n * `--references`, runs the build-time semantic resolver: every\n * object reference is classified as resolved / dangling / external /\n * temporary / dynamic, with warnings for unresolved references.\n *\n * Mirrors `sdt validate`. Column-level resolution is Phase 2.\n */\nexport function validateCommand(): Command {\n const cmd = new Command('validate');\n cmd\n .description(\n 'Validate a .ddtproj — schema check; optionally resolve every object reference (--references).',\n )\n .requiredOption('-p, --project <path>', 'Path to .ddtproj')\n .option(\n '--references',\n 'Run the build-time semantic resolver — flag dangling/external/temp/dynamic references.',\n )\n .option(\n '--columns',\n '[--references] Also resolve every column reference. ERROR on missing column on a known table. Phase 2 of the SSDT-build-equivalent resolver.',\n )\n .option(\n '--min-severity <s>',\n '[--references] Filter findings: error | warning | info. Default info.',\n 'info',\n )\n .option('--format <fmt>', '[--references] table | json. Default table.', 'table')\n .option('-o, --out <path>', '[--references] Write report to file instead of stdout.')\n .option(\n '--check-variables',\n 'VARSYNTAX.4 — refuse if any `$(VAR)` reference in a tracked `.sql` file is not declared in built-ins / deploymentProfiles[*].variables. Exits 1 when undeclared references are found.',\n )\n .action(async (opts) => {\n const loaded = await loadProject(String(opts.project));\n const files = await discoverObjectFiles(loaded);\n console.log(`Project ${loaded.project.name} v${loaded.project.version} is valid.`);\n console.log(\n ` Scope: ${loaded.project.scope.type}${loaded.project.scope.catalog ? '/' + loaded.project.scope.catalog : ''}${loaded.project.scope.schema ? '/' + loaded.project.scope.schema : ''}`,\n );\n console.log(` Files: ${files.length}`);\n console.log(` Profiles: ${Object.keys(loaded.project.deploymentProfiles ?? {}).length}`);\n\n if (opts.checkVariables) {\n const projRoot = path.dirname(path.resolve(String(opts.project)));\n // RH4.3 — bounded-concurrency reads (cap FDs on large projects).\n // stopOnError preserves the prior Promise.all reject-on-first-error\n // semantics; results stay dense + in input order.\n const { results } = await catalog.mapPool(\n files,\n async (f) => {\n const relPath = path.relative(projRoot, f).split(path.sep).join('/');\n const content = await fs.readFile(f, 'utf8');\n return { path: relPath, content };\n },\n { concurrency: 16, stopOnError: true },\n );\n const fileContents = results.filter(\n (r): r is { path: string; content: string } => r !== undefined,\n );\n const profilesBlock = loaded.project.deploymentProfiles ?? {};\n const profileVariableKeys = Object.values(profilesBlock).map((p) =>\n Object.keys(p.variables ?? {}),\n );\n const report = variablesNs.checkVariableReferences({\n files: fileContents,\n deploymentProfileVariables: profileVariableKeys,\n });\n if (report.undeclared.length > 0) {\n const msg = variablesNs.formatCheckVariablesReport(report);\n process.stdout.write(msg + '\\n');\n process.exitCode = 1;\n return;\n }\n console.log(` Variables: ${report.references.length} reference(s), all declared.`);\n }\n\n if (!opts.references) return;\n\n console.log('');\n console.log('Resolving references…');\n const model = await parseProjectModel(loaded);\n const report = validateNs.validateProject(model, {\n source: String(opts.project),\n minSeverity: opts.minSeverity as validateNs.BuildSeverity,\n columns: Boolean(opts.columns),\n });\n\n const fmt = String(opts.format ?? 'table').toLowerCase();\n const payload =\n fmt === 'json' ? JSON.stringify(report, null, 2) : validateNs.formatBuildReport(report);\n if (opts.out) {\n const out = path.resolve(String(opts.out));\n await fs.mkdir(path.dirname(out), { recursive: true });\n await fs.writeFile(out, payload + (payload.endsWith('\\n') ? '' : '\\n'), 'utf8');\n console.log(\n `Wrote ${out} (${payload.length} bytes, ${report.findings.length} finding(s)).`,\n );\n } else {\n process.stdout.write(payload + (payload.endsWith('\\n') ? '' : '\\n'));\n }\n\n if (report.totals.error > 0) process.exitCode = 1;\n });\n attachRelatedOptions(cmd, [\n 'compare.ignoreCase',\n 'compare.ignoreComments',\n 'compare.ignoreFormattingDifferences',\n ]);\n return cmd;\n}\n"],"mappings":"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","names":["report"]}
@@ -0,0 +1,76 @@
1
+ import "./chunk-DGUM43GV.js";
2
+
3
+ // src/commands/verify.ts
4
+ import { Command } from "commander";
5
+ import { pac } from "@ddt-tools/core";
6
+ function verifyCommand() {
7
+ const cmd = new Command("verify");
8
+ cmd.description("Recompute checksums on a .ddtpac and confirm every object matches the manifest.").requiredOption("--pac <path>", "Path to the .ddtpac to verify.").option("--json", "Emit JSON instead of human-readable output.", false).action(async (opts) => {
9
+ const contents = await pac.readPac(String(opts.pac));
10
+ const expected = contents.checksums;
11
+ const actual = pac.computeChecksums(contents.model);
12
+ const missing = [];
13
+ const mismatched = [];
14
+ const orphaned = [];
15
+ const expectedIds = new Set(Object.keys(expected));
16
+ const actualIds = new Set(Object.keys(actual));
17
+ for (const id of expectedIds) {
18
+ if (!actualIds.has(id)) {
19
+ missing.push(id);
20
+ continue;
21
+ }
22
+ if (expected[id] !== actual[id]) {
23
+ mismatched.push({ id, expected: expected[id], actual: actual[id] });
24
+ }
25
+ }
26
+ for (const id of actualIds) {
27
+ if (!expectedIds.has(id)) orphaned.push(id);
28
+ }
29
+ const ok = missing.length === 0 && mismatched.length === 0 && orphaned.length === 0;
30
+ if (opts.json) {
31
+ console.log(
32
+ JSON.stringify(
33
+ { ok, missing, mismatched, orphaned, objectCount: contents.model.length },
34
+ null,
35
+ 2
36
+ )
37
+ );
38
+ } else {
39
+ console.log(`Pac: ${opts.pac}`);
40
+ console.log(
41
+ `Project: ${contents.manifest.projectName} v${contents.manifest.projectVersion}`
42
+ );
43
+ console.log(
44
+ `Built: ${contents.manifest.builtAt} by ${contents.manifest.builtBy.name} ${contents.manifest.builtBy.version}`
45
+ );
46
+ console.log(`Objects: ${contents.model.length}`);
47
+ console.log("");
48
+ if (ok) {
49
+ console.log("\u2713 verify OK \u2014 every checksum matches.");
50
+ } else {
51
+ console.log("\u2717 verify FAILED");
52
+ if (missing.length > 0) {
53
+ console.log(` missing checksums (${missing.length}):`);
54
+ for (const id of missing) console.log(` ${id}`);
55
+ }
56
+ if (mismatched.length > 0) {
57
+ console.log(` mismatched (${mismatched.length}):`);
58
+ for (const m of mismatched)
59
+ console.log(
60
+ ` ${m.id} expected=${m.expected.slice(0, 12)}\u2026 actual=${m.actual.slice(0, 12)}\u2026`
61
+ );
62
+ }
63
+ if (orphaned.length > 0) {
64
+ console.log(` orphaned (in model but not in checksums) (${orphaned.length}):`);
65
+ for (const id of orphaned) console.log(` ${id}`);
66
+ }
67
+ }
68
+ }
69
+ if (!ok) process.exitCode = 1;
70
+ });
71
+ return cmd;
72
+ }
73
+ export {
74
+ verifyCommand
75
+ };
76
+ //# sourceMappingURL=verify-KRDYOJCR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/verify.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { pac, type DatabricksObject } from '@ddt-tools/core';\n\n/**\n * `ddt verify <pac>` — recompute checksums and confirm the .ddtpac is\n * intact. Catches tampering / corruption / partial-extraction\n * accidents. Sets up the Enterprise-tier signing chain — a signed pac\n * is `verify`+`signature-check` together.\n */\nexport function verifyCommand(): Command {\n const cmd = new Command('verify');\n cmd\n .description('Recompute checksums on a .ddtpac and confirm every object matches the manifest.')\n .requiredOption('--pac <path>', 'Path to the .ddtpac to verify.')\n .option('--json', 'Emit JSON instead of human-readable output.', false)\n .action(async (opts) => {\n const contents = await pac.readPac(String(opts.pac));\n const expected = contents.checksums;\n const actual = pac.computeChecksums(contents.model);\n\n const missing: string[] = [];\n const mismatched: Array<{ id: string; expected: string; actual: string }> = [];\n const orphaned: string[] = [];\n\n const expectedIds = new Set(Object.keys(expected));\n const actualIds = new Set(Object.keys(actual));\n for (const id of expectedIds) {\n if (!actualIds.has(id)) {\n missing.push(id);\n continue;\n }\n if (expected[id] !== actual[id]) {\n mismatched.push({ id, expected: expected[id]!, actual: actual[id]! });\n }\n }\n for (const id of actualIds) {\n if (!expectedIds.has(id)) orphaned.push(id);\n }\n\n const ok = missing.length === 0 && mismatched.length === 0 && orphaned.length === 0;\n if (opts.json) {\n console.log(\n JSON.stringify(\n { ok, missing, mismatched, orphaned, objectCount: contents.model.length },\n null,\n 2,\n ),\n );\n } else {\n console.log(`Pac: ${opts.pac}`);\n console.log(\n `Project: ${contents.manifest.projectName} v${contents.manifest.projectVersion}`,\n );\n console.log(\n `Built: ${contents.manifest.builtAt} by ${contents.manifest.builtBy.name} ${contents.manifest.builtBy.version}`,\n );\n console.log(`Objects: ${contents.model.length}`);\n console.log('');\n if (ok) {\n console.log('✓ verify OK — every checksum matches.');\n } else {\n console.log('✗ verify FAILED');\n if (missing.length > 0) {\n console.log(` missing checksums (${missing.length}):`);\n for (const id of missing) console.log(` ${id}`);\n }\n if (mismatched.length > 0) {\n console.log(` mismatched (${mismatched.length}):`);\n for (const m of mismatched)\n console.log(\n ` ${m.id} expected=${m.expected.slice(0, 12)}… actual=${m.actual.slice(0, 12)}…`,\n );\n }\n if (orphaned.length > 0) {\n console.log(` orphaned (in model but not in checksums) (${orphaned.length}):`);\n for (const id of orphaned) console.log(` ${id}`);\n }\n }\n }\n if (!ok) process.exitCode = 1;\n });\n return cmd;\n}\n\n// Silence unused-import warning when DatabricksObject is only referenced via pac.readPac\nvoid (undefined as unknown as DatabricksObject);\n"],"mappings":";;;AAAA,SAAS,eAAe;AACxB,SAAS,WAAkC;AAQpC,SAAS,gBAAyB;AACvC,QAAM,MAAM,IAAI,QAAQ,QAAQ;AAChC,MACG,YAAY,iFAAiF,EAC7F,eAAe,gBAAgB,gCAAgC,EAC/D,OAAO,UAAU,+CAA+C,KAAK,EACrE,OAAO,OAAO,SAAS;AACtB,UAAM,WAAW,MAAM,IAAI,QAAQ,OAAO,KAAK,GAAG,CAAC;AACnD,UAAM,WAAW,SAAS;AAC1B,UAAM,SAAS,IAAI,iBAAiB,SAAS,KAAK;AAElD,UAAM,UAAoB,CAAC;AAC3B,UAAM,aAAsE,CAAC;AAC7E,UAAM,WAAqB,CAAC;AAE5B,UAAM,cAAc,IAAI,IAAI,OAAO,KAAK,QAAQ,CAAC;AACjD,UAAM,YAAY,IAAI,IAAI,OAAO,KAAK,MAAM,CAAC;AAC7C,eAAW,MAAM,aAAa;AAC5B,UAAI,CAAC,UAAU,IAAI,EAAE,GAAG;AACtB,gBAAQ,KAAK,EAAE;AACf;AAAA,MACF;AACA,UAAI,SAAS,EAAE,MAAM,OAAO,EAAE,GAAG;AAC/B,mBAAW,KAAK,EAAE,IAAI,UAAU,SAAS,EAAE,GAAI,QAAQ,OAAO,EAAE,EAAG,CAAC;AAAA,MACtE;AAAA,IACF;AACA,eAAW,MAAM,WAAW;AAC1B,UAAI,CAAC,YAAY,IAAI,EAAE,EAAG,UAAS,KAAK,EAAE;AAAA,IAC5C;AAEA,UAAM,KAAK,QAAQ,WAAW,KAAK,WAAW,WAAW,KAAK,SAAS,WAAW;AAClF,QAAI,KAAK,MAAM;AACb,cAAQ;AAAA,QACN,KAAK;AAAA,UACH,EAAE,IAAI,SAAS,YAAY,UAAU,aAAa,SAAS,MAAM,OAAO;AAAA,UACxE;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF,OAAO;AACL,cAAQ,IAAI,cAAc,KAAK,GAAG,EAAE;AACpC,cAAQ;AAAA,QACN,cAAc,SAAS,SAAS,WAAW,KAAK,SAAS,SAAS,cAAc;AAAA,MAClF;AACA,cAAQ;AAAA,QACN,cAAc,SAAS,SAAS,OAAO,OAAO,SAAS,SAAS,QAAQ,IAAI,IAAI,SAAS,SAAS,QAAQ,OAAO;AAAA,MACnH;AACA,cAAQ,IAAI,cAAc,SAAS,MAAM,MAAM,EAAE;AACjD,cAAQ,IAAI,EAAE;AACd,UAAI,IAAI;AACN,gBAAQ,IAAI,iDAAuC;AAAA,MACrD,OAAO;AACL,gBAAQ,IAAI,sBAAiB;AAC7B,YAAI,QAAQ,SAAS,GAAG;AACtB,kBAAQ,IAAI,wBAAwB,QAAQ,MAAM,IAAI;AACtD,qBAAW,MAAM,QAAS,SAAQ,IAAI,OAAO,EAAE,EAAE;AAAA,QACnD;AACA,YAAI,WAAW,SAAS,GAAG;AACzB,kBAAQ,IAAI,iBAAiB,WAAW,MAAM,IAAI;AAClD,qBAAW,KAAK;AACd,oBAAQ;AAAA,cACN,OAAO,EAAE,EAAE,cAAc,EAAE,SAAS,MAAM,GAAG,EAAE,CAAC,iBAAY,EAAE,OAAO,MAAM,GAAG,EAAE,CAAC;AAAA,YACnF;AAAA,QACJ;AACA,YAAI,SAAS,SAAS,GAAG;AACvB,kBAAQ,IAAI,+CAA+C,SAAS,MAAM,IAAI;AAC9E,qBAAW,MAAM,SAAU,SAAQ,IAAI,OAAO,EAAE,EAAE;AAAA,QACpD;AAAA,MACF;AAAA,IACF;AACA,QAAI,CAAC,GAAI,SAAQ,WAAW;AAAA,EAC9B,CAAC;AACH,SAAO;AACT;","names":[]}
@@ -0,0 +1,80 @@
1
+ import "./chunk-DGUM43GV.js";
2
+
3
+ // src/commands/watch.ts
4
+ import { promises as fs } from "fs";
5
+ import path from "path";
6
+ import { Command } from "commander";
7
+ import { platformWatch } from "@ddt-tools/core";
8
+ var DEFAULT_SOURCE = "databricks";
9
+ function watchCommand() {
10
+ const cmd = new Command("watch");
11
+ cmd.description(
12
+ "Platform-feature drift detector. Diffs release-notes markdown against a cached snapshot."
13
+ );
14
+ cmd.command("bulletin").description(
15
+ "Compose a monthly bulletin: new drift (needs BACKLOG action), new covered, removed. Reads --prior snapshot JSON + --current release-notes markdown."
16
+ ).requiredOption("--current <path>", "Path to current release-notes markdown.").option("--prior <path>", "Path to prior snapshot JSON (defaults to empty prior).").option("--rules <path>", "Path to cluster-match rules JSON (ClusterMatchRule[]).").option("--overrides <path>", "Path to release-note-id \u2192 cluster override JSON.").option("--source <label>", "Source label written into a saved snapshot.", DEFAULT_SOURCE).option("--format <fmt>", "json | markdown (default markdown).", "markdown").option("--save-snapshot <path>", "Also write the parsed current set as a fresh snapshot.").action(async (opts) => {
17
+ const currentMarkdown = await fs.readFile(path.resolve(opts.current), "utf8");
18
+ const currentNotes = platformWatch.parseReleaseNotesMarkdown(currentMarkdown);
19
+ const priorNotes = opts.prior ? await readPriorSnapshot(path.resolve(opts.prior)) : [];
20
+ const rules = opts.rules ? await readClusterRules(path.resolve(opts.rules)) : [];
21
+ const overrides = opts.overrides ? await readOverrides(path.resolve(opts.overrides)) : void 0;
22
+ const bulletin = platformWatch.buildMonthlyBulletin({
23
+ prior: priorNotes,
24
+ current: currentNotes,
25
+ rules,
26
+ overrides
27
+ });
28
+ const fmt = String(opts.format ?? "markdown").toLowerCase();
29
+ if (fmt === "json") {
30
+ process.stdout.write(JSON.stringify(bulletin, null, 2) + "\n");
31
+ } else if (fmt === "markdown") {
32
+ process.stdout.write(platformWatch.renderBulletinMarkdown(bulletin) + "\n");
33
+ } else {
34
+ throw new Error(`Unknown --format: ${opts.format}. Use json | markdown.`);
35
+ }
36
+ if (opts.saveSnapshot) {
37
+ const snapshot = platformWatch.serializeReleaseNotesSnapshot(
38
+ String(opts.source ?? DEFAULT_SOURCE),
39
+ currentNotes
40
+ );
41
+ await fs.writeFile(
42
+ path.resolve(opts.saveSnapshot),
43
+ JSON.stringify(snapshot, null, 2) + "\n",
44
+ "utf8"
45
+ );
46
+ }
47
+ });
48
+ return cmd;
49
+ }
50
+ async function readPriorSnapshot(filePath) {
51
+ const raw = await fs.readFile(filePath, "utf8");
52
+ const parsed = JSON.parse(raw);
53
+ const snapshot = platformWatch.deserializeReleaseNotesSnapshot(parsed);
54
+ if (snapshot === null) {
55
+ throw new Error(
56
+ `Prior snapshot at ${filePath} is malformed or carries an unsupported version. Pass a fresh --prior or omit the flag.`
57
+ );
58
+ }
59
+ return snapshot.notes;
60
+ }
61
+ async function readClusterRules(filePath) {
62
+ const raw = await fs.readFile(filePath, "utf8");
63
+ const parsed = JSON.parse(raw);
64
+ if (!Array.isArray(parsed)) {
65
+ throw new Error(`--rules at ${filePath} must be a JSON array of ClusterMatchRule.`);
66
+ }
67
+ return parsed;
68
+ }
69
+ async function readOverrides(filePath) {
70
+ const raw = await fs.readFile(filePath, "utf8");
71
+ const parsed = JSON.parse(raw);
72
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
73
+ throw new Error(`--overrides at ${filePath} must be a JSON object of {id: cluster}.`);
74
+ }
75
+ return parsed;
76
+ }
77
+ export {
78
+ watchCommand
79
+ };
80
+ //# sourceMappingURL=watch-FSG23RR3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/watch.ts"],"sourcesContent":["/**\n * `ddt watch` — platform-feature drift bulletin (WATCH.2-paired).\n *\n * Wires the shipped `@ddt-tools/core/platformWatch` substrate trio (WATCH.1\n * parse+diff+persist, WATCH.5 coverage, WATCH.4 bulletin) into a host-\n * level CLI: read prior snapshot JSON + current release-notes markdown\n * from disk, run `buildMonthlyBulletin`, emit JSON or rendered Markdown.\n * Optional `--save-snapshot` writes the parsed current set to disk so\n * the next run has a fresh prior.\n *\n * Pure file I/O — the substrate stays I/O-free; the CLI owns the disk\n * boundary per the substrate's design contract.\n *\n * Mirrors `Snowflake/packages/cli/src/commands/watch.ts`.\n */\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { Command } from 'commander';\nimport { platformWatch } from '@ddt-tools/core';\n\nconst DEFAULT_SOURCE = 'databricks';\n\nexport function watchCommand(): Command {\n const cmd = new Command('watch');\n cmd.description(\n 'Platform-feature drift detector. Diffs release-notes markdown against a cached snapshot.',\n );\n\n cmd\n .command('bulletin')\n .description(\n 'Compose a monthly bulletin: new drift (needs BACKLOG action), new covered, removed. Reads --prior snapshot JSON + --current release-notes markdown.',\n )\n .requiredOption('--current <path>', 'Path to current release-notes markdown.')\n .option('--prior <path>', 'Path to prior snapshot JSON (defaults to empty prior).')\n .option('--rules <path>', 'Path to cluster-match rules JSON (ClusterMatchRule[]).')\n .option('--overrides <path>', 'Path to release-note-id → cluster override JSON.')\n .option('--source <label>', 'Source label written into a saved snapshot.', DEFAULT_SOURCE)\n .option('--format <fmt>', 'json | markdown (default markdown).', 'markdown')\n .option('--save-snapshot <path>', 'Also write the parsed current set as a fresh snapshot.')\n .action(async (opts) => {\n const currentMarkdown = await fs.readFile(path.resolve(opts.current), 'utf8');\n const currentNotes = platformWatch.parseReleaseNotesMarkdown(currentMarkdown);\n\n const priorNotes = opts.prior ? await readPriorSnapshot(path.resolve(opts.prior)) : [];\n const rules = opts.rules ? await readClusterRules(path.resolve(opts.rules)) : [];\n const overrides = opts.overrides\n ? await readOverrides(path.resolve(opts.overrides))\n : undefined;\n\n const bulletin = platformWatch.buildMonthlyBulletin({\n prior: priorNotes,\n current: currentNotes,\n rules,\n overrides,\n });\n\n const fmt = String(opts.format ?? 'markdown').toLowerCase();\n if (fmt === 'json') {\n process.stdout.write(JSON.stringify(bulletin, null, 2) + '\\n');\n } else if (fmt === 'markdown') {\n process.stdout.write(platformWatch.renderBulletinMarkdown(bulletin) + '\\n');\n } else {\n throw new Error(`Unknown --format: ${opts.format}. Use json | markdown.`);\n }\n\n if (opts.saveSnapshot) {\n const snapshot = platformWatch.serializeReleaseNotesSnapshot(\n String(opts.source ?? DEFAULT_SOURCE),\n currentNotes,\n );\n await fs.writeFile(\n path.resolve(opts.saveSnapshot),\n JSON.stringify(snapshot, null, 2) + '\\n',\n 'utf8',\n );\n }\n });\n\n return cmd;\n}\n\nasync function readPriorSnapshot(filePath: string): Promise<readonly platformWatch.ReleaseNote[]> {\n const raw = await fs.readFile(filePath, 'utf8');\n const parsed: unknown = JSON.parse(raw);\n const snapshot = platformWatch.deserializeReleaseNotesSnapshot(parsed);\n if (snapshot === null) {\n throw new Error(\n `Prior snapshot at ${filePath} is malformed or carries an unsupported version. Pass a fresh --prior or omit the flag.`,\n );\n }\n return snapshot.notes;\n}\n\nasync function readClusterRules(\n filePath: string,\n): Promise<readonly platformWatch.ClusterMatchRule[]> {\n const raw = await fs.readFile(filePath, 'utf8');\n const parsed: unknown = JSON.parse(raw);\n if (!Array.isArray(parsed)) {\n throw new Error(`--rules at ${filePath} must be a JSON array of ClusterMatchRule.`);\n }\n return parsed as platformWatch.ClusterMatchRule[];\n}\n\nasync function readOverrides(filePath: string): Promise<platformWatch.ReleaseNoteOverride> {\n const raw = await fs.readFile(filePath, 'utf8');\n const parsed: unknown = JSON.parse(raw);\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error(`--overrides at ${filePath} must be a JSON object of {id: cluster}.`);\n }\n return parsed as platformWatch.ReleaseNoteOverride;\n}\n"],"mappings":";;;AAeA,SAAS,YAAY,UAAU;AAC/B,OAAO,UAAU;AACjB,SAAS,eAAe;AACxB,SAAS,qBAAqB;AAE9B,IAAM,iBAAiB;AAEhB,SAAS,eAAwB;AACtC,QAAM,MAAM,IAAI,QAAQ,OAAO;AAC/B,MAAI;AAAA,IACF;AAAA,EACF;AAEA,MACG,QAAQ,UAAU,EAClB;AAAA,IACC;AAAA,EACF,EACC,eAAe,oBAAoB,yCAAyC,EAC5E,OAAO,kBAAkB,wDAAwD,EACjF,OAAO,kBAAkB,wDAAwD,EACjF,OAAO,sBAAsB,uDAAkD,EAC/E,OAAO,oBAAoB,+CAA+C,cAAc,EACxF,OAAO,kBAAkB,uCAAuC,UAAU,EAC1E,OAAO,0BAA0B,wDAAwD,EACzF,OAAO,OAAO,SAAS;AACtB,UAAM,kBAAkB,MAAM,GAAG,SAAS,KAAK,QAAQ,KAAK,OAAO,GAAG,MAAM;AAC5E,UAAM,eAAe,cAAc,0BAA0B,eAAe;AAE5E,UAAM,aAAa,KAAK,QAAQ,MAAM,kBAAkB,KAAK,QAAQ,KAAK,KAAK,CAAC,IAAI,CAAC;AACrF,UAAM,QAAQ,KAAK,QAAQ,MAAM,iBAAiB,KAAK,QAAQ,KAAK,KAAK,CAAC,IAAI,CAAC;AAC/E,UAAM,YAAY,KAAK,YACnB,MAAM,cAAc,KAAK,QAAQ,KAAK,SAAS,CAAC,IAChD;AAEJ,UAAM,WAAW,cAAc,qBAAqB;AAAA,MAClD,OAAO;AAAA,MACP,SAAS;AAAA,MACT;AAAA,MACA;AAAA,IACF,CAAC;AAED,UAAM,MAAM,OAAO,KAAK,UAAU,UAAU,EAAE,YAAY;AAC1D,QAAI,QAAQ,QAAQ;AAClB,cAAQ,OAAO,MAAM,KAAK,UAAU,UAAU,MAAM,CAAC,IAAI,IAAI;AAAA,IAC/D,WAAW,QAAQ,YAAY;AAC7B,cAAQ,OAAO,MAAM,cAAc,uBAAuB,QAAQ,IAAI,IAAI;AAAA,IAC5E,OAAO;AACL,YAAM,IAAI,MAAM,qBAAqB,KAAK,MAAM,wBAAwB;AAAA,IAC1E;AAEA,QAAI,KAAK,cAAc;AACrB,YAAM,WAAW,cAAc;AAAA,QAC7B,OAAO,KAAK,UAAU,cAAc;AAAA,QACpC;AAAA,MACF;AACA,YAAM,GAAG;AAAA,QACP,KAAK,QAAQ,KAAK,YAAY;AAAA,QAC9B,KAAK,UAAU,UAAU,MAAM,CAAC,IAAI;AAAA,QACpC;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AAEH,SAAO;AACT;AAEA,eAAe,kBAAkB,UAAiE;AAChG,QAAM,MAAM,MAAM,GAAG,SAAS,UAAU,MAAM;AAC9C,QAAM,SAAkB,KAAK,MAAM,GAAG;AACtC,QAAM,WAAW,cAAc,gCAAgC,MAAM;AACrE,MAAI,aAAa,MAAM;AACrB,UAAM,IAAI;AAAA,MACR,qBAAqB,QAAQ;AAAA,IAC/B;AAAA,EACF;AACA,SAAO,SAAS;AAClB;AAEA,eAAe,iBACb,UACoD;AACpD,QAAM,MAAM,MAAM,GAAG,SAAS,UAAU,MAAM;AAC9C,QAAM,SAAkB,KAAK,MAAM,GAAG;AACtC,MAAI,CAAC,MAAM,QAAQ,MAAM,GAAG;AAC1B,UAAM,IAAI,MAAM,cAAc,QAAQ,4CAA4C;AAAA,EACpF;AACA,SAAO;AACT;AAEA,eAAe,cAAc,UAA8D;AACzF,QAAM,MAAM,MAAM,GAAG,SAAS,UAAU,MAAM;AAC9C,QAAM,SAAkB,KAAK,MAAM,GAAG;AACtC,MAAI,WAAW,QAAQ,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAAG;AAC1E,UAAM,IAAI,MAAM,kBAAkB,QAAQ,0CAA0C;AAAA,EACtF;AACA,SAAO;AACT;","names":[]}
@@ -0,0 +1,87 @@
1
+ import {
2
+ logger
3
+ } from "./chunk-VM2H4LAO.js";
4
+ import "./chunk-DGUM43GV.js";
5
+
6
+ // src/commands/xcompare.ts
7
+ import { Command } from "commander";
8
+ import { promises as fs } from "fs";
9
+ import {
10
+ classifyCrossPlatformDeploy,
11
+ dispatchAll,
12
+ filterAssessmentByTier,
13
+ formatCrossPlatformAssessment,
14
+ isCrossPlatformDeployBlocked,
15
+ isXpmTier
16
+ } from "@ddt-tools/core/migrate";
17
+ function xcompareCommand() {
18
+ const cmd = new Command("xcompare-ir");
19
+ cmd.description(
20
+ "Cross-platform IR compare (Databricks \u2194 Snowflake). Maps source IRs through XPM-Deep substrate; emits tier-gated safety assessment. IR-level only (live-warehouse compare is a follow-up)."
21
+ );
22
+ cmd.requiredOption("--source <path>", "JSON file with an array of XpmObjectIr source objects.").option("--tier <tier>", "License tier: free | pro | team | enterprise.", "free").option("--format <format>", "Output format: markdown | json.", "markdown").option("--allow-destructive", "Permit DESTRUCTIVE verdicts (exit 0 instead of 2).", false).option(
23
+ "--allow-unmappable",
24
+ "Permit UNRECOVERABLE verdicts (exit 0 instead of 2). Implies --allow-destructive.",
25
+ false
26
+ ).action(
27
+ async (opts) => {
28
+ if (!isXpmTier(opts.tier)) {
29
+ logger.error(
30
+ `Unknown --tier "${opts.tier}". Use one of: free | pro | team | enterprise.`
31
+ );
32
+ process.exitCode = 1;
33
+ return;
34
+ }
35
+ const tier = opts.tier;
36
+ const format = (opts.format ?? "markdown").toLowerCase();
37
+ if (format !== "markdown" && format !== "json") {
38
+ logger.error(`Unknown --format "${opts.format}". Use markdown | json.`);
39
+ process.exitCode = 1;
40
+ return;
41
+ }
42
+ let raw;
43
+ try {
44
+ raw = await fs.readFile(opts.source, "utf-8");
45
+ } catch (e) {
46
+ logger.error(`Cannot read --source file '${opts.source}': ${e.message}`);
47
+ process.exitCode = 1;
48
+ return;
49
+ }
50
+ let parsed;
51
+ try {
52
+ parsed = JSON.parse(raw);
53
+ } catch (e) {
54
+ logger.error(`--source file is not valid JSON: ${e.message}`);
55
+ process.exitCode = 1;
56
+ return;
57
+ }
58
+ if (!Array.isArray(parsed)) {
59
+ logger.error(
60
+ `--source file must contain a JSON array of XpmObjectIr objects (got ${typeof parsed}).`
61
+ );
62
+ process.exitCode = 1;
63
+ return;
64
+ }
65
+ const sources = parsed;
66
+ const dispatched = dispatchAll(sources);
67
+ const assessment = classifyCrossPlatformDeploy(dispatched);
68
+ const tierFiltered = filterAssessmentByTier(assessment, tier);
69
+ if (format === "json") {
70
+ process.stdout.write(JSON.stringify(tierFiltered, null, 2) + "\n");
71
+ } else {
72
+ process.stdout.write(formatCrossPlatformAssessment(tierFiltered) + "\n");
73
+ }
74
+ if (isCrossPlatformDeployBlocked(assessment, {
75
+ destructiveOverride: opts.allowDestructive || opts.allowUnmappable,
76
+ unmappableOverride: opts.allowUnmappable
77
+ })) {
78
+ process.exitCode = 2;
79
+ }
80
+ }
81
+ );
82
+ return cmd;
83
+ }
84
+ export {
85
+ xcompareCommand
86
+ };
87
+ //# sourceMappingURL=xcompare-U4TXTTIR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/xcompare.ts"],"sourcesContent":["/**\n * `ddt xcompare-ir` — Cross-platform IR compare (XPM.31b).\n *\n * Reads a JSON file of source-platform canonical IRs (typically the\n * Databricks side; reverse-direction inputs are also accepted), runs\n * every IR through the XPM.31a dispatcher, rolls findings up with the\n * XPM.30 cross-platform safety classifier, applies the XPM.32 tier\n * gate, and prints the result.\n *\n * NOTE: this is the IR-level entry point — it does NOT connect to a\n * live warehouse. Live cross-platform compare against a `<sdt-profile>`\n * is a follow-up that requires both halves of the engine to share\n * extractor state (deferred until paid-warehouse access is available\n * for live integration testing).\n *\n * Usage:\n *\n * ddt xcompare-ir --source schema.json \\\n * --tier pro \\\n * --format markdown\n *\n * ddt xcompare-ir --source schema.json \\\n * --tier enterprise \\\n * --format json \\\n * --allow-destructive --allow-unmappable\n *\n * Input JSON shape: array of `XpmObjectIr` (see `@xpm/migrate/canonicalIr`).\n *\n * Exit codes:\n * 0 — assessment is OK / INFO / DESTRUCTIVE-with-override / UNRECOVERABLE-with-override.\n * 2 — assessment is DESTRUCTIVE without --allow-destructive, or\n * UNRECOVERABLE without --allow-unmappable.\n * 1 — invalid input / file not found / parse error.\n */\nimport { Command } from 'commander';\nimport { promises as fs } from 'node:fs';\nimport {\n classifyCrossPlatformDeploy,\n dispatchAll,\n filterAssessmentByTier,\n formatCrossPlatformAssessment,\n isCrossPlatformDeployBlocked,\n isXpmTier,\n type XpmTier,\n} from '@ddt-tools/core/migrate';\nimport type { XpmObjectIr } from '@ddt-tools/core/migrate';\nimport { logger } from '../util/logger.js';\n\nexport function xcompareCommand(): Command {\n const cmd = new Command('xcompare-ir');\n cmd.description(\n 'Cross-platform IR compare (Databricks ↔ Snowflake). Maps source IRs through XPM-Deep substrate; emits tier-gated safety assessment. IR-level only (live-warehouse compare is a follow-up).',\n );\n\n cmd\n .requiredOption('--source <path>', 'JSON file with an array of XpmObjectIr source objects.')\n .option('--tier <tier>', 'License tier: free | pro | team | enterprise.', 'free')\n .option('--format <format>', 'Output format: markdown | json.', 'markdown')\n .option('--allow-destructive', 'Permit DESTRUCTIVE verdicts (exit 0 instead of 2).', false)\n .option(\n '--allow-unmappable',\n 'Permit UNRECOVERABLE verdicts (exit 0 instead of 2). Implies --allow-destructive.',\n false,\n )\n .action(\n async (opts: {\n source: string;\n tier: string;\n format: string;\n allowDestructive: boolean;\n allowUnmappable: boolean;\n }) => {\n if (!isXpmTier(opts.tier)) {\n logger.error(\n `Unknown --tier \"${opts.tier}\". Use one of: free | pro | team | enterprise.`,\n );\n process.exitCode = 1;\n return;\n }\n const tier: XpmTier = opts.tier;\n const format = (opts.format ?? 'markdown').toLowerCase();\n if (format !== 'markdown' && format !== 'json') {\n logger.error(`Unknown --format \"${opts.format}\". Use markdown | json.`);\n process.exitCode = 1;\n return;\n }\n\n let raw: string;\n try {\n raw = await fs.readFile(opts.source, 'utf-8');\n } catch (e) {\n logger.error(`Cannot read --source file '${opts.source}': ${(e as Error).message}`);\n process.exitCode = 1;\n return;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (e) {\n logger.error(`--source file is not valid JSON: ${(e as Error).message}`);\n process.exitCode = 1;\n return;\n }\n\n if (!Array.isArray(parsed)) {\n logger.error(\n `--source file must contain a JSON array of XpmObjectIr objects (got ${typeof parsed}).`,\n );\n process.exitCode = 1;\n return;\n }\n const sources = parsed as readonly XpmObjectIr[];\n\n const dispatched = dispatchAll(sources);\n const assessment = classifyCrossPlatformDeploy(dispatched);\n const tierFiltered = filterAssessmentByTier(assessment, tier);\n\n if (format === 'json') {\n process.stdout.write(JSON.stringify(tierFiltered, null, 2) + '\\n');\n } else {\n process.stdout.write(formatCrossPlatformAssessment(tierFiltered) + '\\n');\n }\n\n if (\n isCrossPlatformDeployBlocked(assessment, {\n destructiveOverride: opts.allowDestructive || opts.allowUnmappable,\n unmappableOverride: opts.allowUnmappable,\n })\n ) {\n process.exitCode = 2;\n }\n },\n );\n\n return cmd;\n}\n"],"mappings":";;;;;;AAkCA,SAAS,eAAe;AACxB,SAAS,YAAY,UAAU;AAC/B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAIA,SAAS,kBAA2B;AACzC,QAAM,MAAM,IAAI,QAAQ,aAAa;AACrC,MAAI;AAAA,IACF;AAAA,EACF;AAEA,MACG,eAAe,mBAAmB,wDAAwD,EAC1F,OAAO,iBAAiB,iDAAiD,MAAM,EAC/E,OAAO,qBAAqB,mCAAmC,UAAU,EACzE,OAAO,uBAAuB,sDAAsD,KAAK,EACzF;AAAA,IACC;AAAA,IACA;AAAA,IACA;AAAA,EACF,EACC;AAAA,IACC,OAAO,SAMD;AACJ,UAAI,CAAC,UAAU,KAAK,IAAI,GAAG;AACzB,eAAO;AAAA,UACL,mBAAmB,KAAK,IAAI;AAAA,QAC9B;AACA,gBAAQ,WAAW;AACnB;AAAA,MACF;AACA,YAAM,OAAgB,KAAK;AAC3B,YAAM,UAAU,KAAK,UAAU,YAAY,YAAY;AACvD,UAAI,WAAW,cAAc,WAAW,QAAQ;AAC9C,eAAO,MAAM,qBAAqB,KAAK,MAAM,yBAAyB;AACtE,gBAAQ,WAAW;AACnB;AAAA,MACF;AAEA,UAAI;AACJ,UAAI;AACF,cAAM,MAAM,GAAG,SAAS,KAAK,QAAQ,OAAO;AAAA,MAC9C,SAAS,GAAG;AACV,eAAO,MAAM,8BAA8B,KAAK,MAAM,MAAO,EAAY,OAAO,EAAE;AAClF,gBAAQ,WAAW;AACnB;AAAA,MACF;AAEA,UAAI;AACJ,UAAI;AACF,iBAAS,KAAK,MAAM,GAAG;AAAA,MACzB,SAAS,GAAG;AACV,eAAO,MAAM,oCAAqC,EAAY,OAAO,EAAE;AACvE,gBAAQ,WAAW;AACnB;AAAA,MACF;AAEA,UAAI,CAAC,MAAM,QAAQ,MAAM,GAAG;AAC1B,eAAO;AAAA,UACL,uEAAuE,OAAO,MAAM;AAAA,QACtF;AACA,gBAAQ,WAAW;AACnB;AAAA,MACF;AACA,YAAM,UAAU;AAEhB,YAAM,aAAa,YAAY,OAAO;AACtC,YAAM,aAAa,4BAA4B,UAAU;AACzD,YAAM,eAAe,uBAAuB,YAAY,IAAI;AAE5D,UAAI,WAAW,QAAQ;AACrB,gBAAQ,OAAO,MAAM,KAAK,UAAU,cAAc,MAAM,CAAC,IAAI,IAAI;AAAA,MACnE,OAAO;AACL,gBAAQ,OAAO,MAAM,8BAA8B,YAAY,IAAI,IAAI;AAAA,MACzE;AAEA,UACE,6BAA6B,YAAY;AAAA,QACvC,qBAAqB,KAAK,oBAAoB,KAAK;AAAA,QACnD,oBAAoB,KAAK;AAAA,MAC3B,CAAC,GACD;AACA,gBAAQ,WAAW;AAAA,MACrB;AAAA,IACF;AAAA,EACF;AAEF,SAAO;AACT;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ddt-tools/cli",
3
- "version": "0.2.0",
3
+ "version": "0.2.5",
4
4
  "description": "DDT command-line interface. `ddt` lets you extract, build, compare, and deploy Databricks Unity Catalog schemas.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Varol Consulting LLC",
@@ -29,7 +29,7 @@
29
29
  "commander": "^14.0.0",
30
30
  "ora": "^9.4.0",
31
31
  "zod": "^4.4.3",
32
- "@ddt-tools/core": "0.2.0"
32
+ "@ddt-tools/core": "0.2.5"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/node": "^25.9.1",