@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/data-fit.ts"],"sourcesContent":["/**\n * `ddt data-fit --source <pac> --target <pac>` — pre-flight data-fit\n * probe queries for narrowing type changes.\n *\n * Mirrors `Snowflake/packages/cli/src/commands/data-fit.ts`. Walks the\n * source-vs-target compare, finds every table-like object whose\n * column-level diff includes a type change, classifies each change\n * via `@ddt-tools/core/types.classifyTypeChange`, and for every\n * `data-fit-required` verdict emits a `SELECT count_if(...)` probe via\n * `dataFitProbeSql`. The probe queries are read-only — run them before\n * `ddt publish --apply` to confirm zero rows overflow the new type.\n */\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { Command } from 'commander';\nimport { CompareEngine, PacSource, types, type DatabricksObject } from '@ddt-tools/core';\n\ninterface ColumnWithType {\n name: string;\n dataType: unknown;\n}\n\nfunction getColumns(obj: DatabricksObject): ColumnWithType[] | undefined {\n if (!('columns' in obj)) return undefined;\n const cols = (obj as { columns?: unknown }).columns;\n if (!Array.isArray(cols)) return undefined;\n // DDT columns store the type under `.type`; normalize so the shared\n // ColumnWithType reader can find it under `.dataType` like the SDT\n // mirror. Defensive: keep `.dataType` if a producer happens to pass\n // SDT-shaped objects through this CLI.\n return (cols as { name: string; type?: unknown; dataType?: unknown }[]).map((c) => ({\n name: c.name,\n dataType: c.dataType ?? c.type,\n }));\n}\n\nfunction dataTypeToString(dt: unknown): string {\n if (typeof dt === 'string') return dt;\n if (dt && typeof dt === 'object') {\n const d = dt as {\n base?: string;\n precision?: number;\n scale?: number;\n length?: number;\n raw?: string;\n };\n if (d.raw) return d.raw;\n if (!d.base) return JSON.stringify(dt);\n if (d.precision !== undefined && d.scale !== undefined) {\n return `${d.base}(${d.precision},${d.scale})`;\n }\n if (d.length !== undefined) return `${d.base}(${d.length})`;\n return d.base;\n }\n return String(dt ?? '');\n}\n\nfunction backtickFqn(obj: DatabricksObject): string {\n const fqn = (obj as { fqn?: { database?: string; schema?: string; name: string } }).fqn;\n if (!fqn) return '<unknown>';\n return [fqn.database, fqn.schema, fqn.name]\n .filter(Boolean)\n .map((p) => `\\`${p}\\``)\n .join('.');\n}\n\nfunction backtickCol(name: string): string {\n return `\\`${name}\\``;\n}\n\nexport function dataFitCommand(): Command {\n const cmd = new Command('data-fit');\n cmd\n .description(\n 'Emit pre-flight SELECT count_if() probes for every narrowing type change in a pac↔pac compare. Run them against the live target before --apply.',\n )\n .requiredOption('--source <path>', '.ddtpac with the desired state.')\n .requiredOption('--target <path>', '.ddtpac with the current state.')\n .option('-o, --out <path>', 'Output SQL file. Default: stdout.')\n .option('--format <fmt>', 'Output format: sql | json. Default: sql.', 'sql')\n .action(async (opts) => {\n const source = new PacSource(path.resolve(String(opts.source)));\n const target = new PacSource(path.resolve(String(opts.target)));\n const engine = new CompareEngine();\n const result = await engine.compare(source, target);\n\n interface Probe {\n fqn: string;\n column: string;\n from: string;\n to: string;\n reason: string;\n sql: string;\n }\n const probes: Probe[] = [];\n\n for (const obj of result.objects) {\n if (obj.kind !== 'modified' || !obj.source || !obj.target) continue;\n const srcCols = getColumns(obj.source);\n const tgtCols = getColumns(obj.target);\n if (!srcCols || !tgtCols) continue;\n const byName = new Map<string, { source: ColumnWithType; target: ColumnWithType }>();\n for (const c of srcCols) byName.set(c.name.toUpperCase(), { source: c, target: c });\n for (const c of tgtCols) {\n const slot = byName.get(c.name.toUpperCase());\n if (slot) slot.target = c;\n }\n for (const [, pair] of byName) {\n if (pair.source === pair.target) continue;\n const fromStr = dataTypeToString(pair.target.dataType);\n const toStr = dataTypeToString(pair.source.dataType);\n if (fromStr === toStr) continue;\n const check = types.classifyTypeChange(fromStr, toStr);\n if (check.verdict !== 'data-fit-required') continue;\n const fqn = backtickFqn(obj.source);\n const colName = pair.source.name;\n const probeSql = types.dataFitProbeSql(check, fqn, backtickCol(colName));\n if (!probeSql) continue;\n probes.push({\n fqn,\n column: colName,\n from: fromStr,\n to: toStr,\n reason: check.reason,\n sql: probeSql,\n });\n }\n }\n\n if (opts.format === 'json') {\n const out = JSON.stringify({ probeCount: probes.length, probes }, null, 2);\n await emit(out, opts.out);\n } else {\n const lines: string[] = [];\n lines.push(`-- Generated by \\`ddt data-fit\\` at ${new Date().toISOString()}.`);\n lines.push(`-- ${probes.length} narrowing type change(s) need a pre-flight check.`);\n lines.push(`-- Run each query against the target; WOULD_FAIL must be 0 before --apply.`);\n lines.push('');\n for (const p of probes) {\n lines.push(`-- PROBE: ${p.fqn}.${backtickCol(p.column)} (${p.from} → ${p.to})`);\n lines.push(`-- Reason: ${p.reason}`);\n lines.push(`${p.sql};`);\n lines.push('');\n }\n if (probes.length === 0) {\n lines.push(\n '-- No narrowing type changes detected. Compare result has only safe or destructive changes;',\n );\n lines.push('-- data-fit probes do not apply.');\n }\n await emit(lines.join('\\n'), opts.out);\n }\n\n if (probes.length === 0) {\n console.error('No narrowing type changes detected — nothing to probe.');\n } else {\n console.error(\n `Generated ${probes.length} data-fit probe(s). Run each against the target and confirm WOULD_FAIL = 0 before \\`ddt publish --apply\\`.`,\n );\n }\n });\n return cmd;\n}\n\nasync function emit(text: string, out: unknown): Promise<void> {\n if (out) {\n const p = path.resolve(String(out));\n await fs.mkdir(path.dirname(p), { recursive: true });\n await fs.writeFile(p, text + (text.endsWith('\\n') ? '' : '\\n'), 'utf8');\n console.error(`Wrote ${p}.`);\n } else {\n process.stdout.write(text + (text.endsWith('\\n') ? '' : '\\n'));\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,58 @@
1
+ import "./chunk-DGUM43GV.js";
2
+
3
+ // src/commands/deploy-status.ts
4
+ import path from "path";
5
+ import { Command } from "commander";
6
+ import { deployCheckpoint } from "@ddt-tools/core";
7
+ var DEFAULT_STATE_DIR = path.join(process.cwd(), deployCheckpoint.DEFAULT_STATE_DIR_REL);
8
+ function deployStatusCommand() {
9
+ const cmd = new Command("deploy-status");
10
+ cmd.description("Show or list resumable deploy checkpoints written by `ddt publish`.").argument("[deployId]", "Deploy id to inspect. Omit to list every checkpoint.").option("--state-dir <path>", "Directory containing checkpoint JSON files.", DEFAULT_STATE_DIR).option("--remove", "Delete the checkpoint (after status display). Requires <deployId>.", false).option("--format <fmt>", "text | json. Default text.", "text").action(
11
+ async (deployId, opts) => {
12
+ const stateDir = path.resolve(opts.stateDir);
13
+ const fmt = (opts.format ?? "text").toLowerCase();
14
+ if (!deployId) {
15
+ const list = await deployCheckpoint.listCheckpoints(stateDir);
16
+ if (fmt === "json") {
17
+ process.stdout.write(JSON.stringify(list, null, 2) + "\n");
18
+ return;
19
+ }
20
+ if (list.length === 0) {
21
+ process.stdout.write("No resumable deploys.\n");
22
+ return;
23
+ }
24
+ for (const item of list) {
25
+ process.stdout.write(
26
+ `${item.deployId} ${item.state} ${item.completedSteps}/${item.totalSteps} steps profile=${item.profile}${item.env ? ` env=${item.env}` : ""} updated=${item.lastUpdatedAt}
27
+ `
28
+ );
29
+ }
30
+ return;
31
+ }
32
+ const state = await deployCheckpoint.loadCheckpoint(stateDir, deployId);
33
+ if (!state) {
34
+ process.stderr.write(
35
+ `No checkpoint found for deploy id ${JSON.stringify(deployId)} under ${stateDir}.
36
+ `
37
+ );
38
+ process.exitCode = 1;
39
+ return;
40
+ }
41
+ if (fmt === "json") {
42
+ process.stdout.write(JSON.stringify(state, null, 2) + "\n");
43
+ } else {
44
+ process.stdout.write(deployCheckpoint.formatCheckpointStatus(state) + "\n");
45
+ }
46
+ if (opts.remove) {
47
+ await deployCheckpoint.removeCheckpoint(stateDir, deployId);
48
+ process.stderr.write(`Removed checkpoint ${deployId}.
49
+ `);
50
+ }
51
+ }
52
+ );
53
+ return cmd;
54
+ }
55
+ export {
56
+ deployStatusCommand
57
+ };
58
+ //# sourceMappingURL=deploy-status-4H5KJFRC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/deploy-status.ts"],"sourcesContent":["/**\n * `ddt deploy-status` — inspect resumable deploy checkpoints (DSR.3).\n *\n * Usage:\n * ddt deploy-status # list every resumable deploy\n * ddt deploy-status <deployId> # show one checkpoint\n * ddt deploy-status <deployId> --remove # delete a checkpoint\n * ddt deploy-status --format json # machine-readable list\n *\n * Mirrors `sdt deploy-status`.\n */\nimport path from 'node:path';\nimport { Command } from 'commander';\nimport { deployCheckpoint } from '@ddt-tools/core';\n\nconst DEFAULT_STATE_DIR = path.join(process.cwd(), deployCheckpoint.DEFAULT_STATE_DIR_REL);\n\nexport function deployStatusCommand(): Command {\n const cmd = new Command('deploy-status');\n cmd\n .description('Show or list resumable deploy checkpoints written by `ddt publish`.')\n .argument('[deployId]', 'Deploy id to inspect. Omit to list every checkpoint.')\n .option('--state-dir <path>', 'Directory containing checkpoint JSON files.', DEFAULT_STATE_DIR)\n .option('--remove', 'Delete the checkpoint (after status display). Requires <deployId>.', false)\n .option('--format <fmt>', 'text | json. Default text.', 'text')\n .action(\n async (\n deployId: string | undefined,\n opts: { stateDir: string; remove?: boolean; format?: string },\n ) => {\n const stateDir = path.resolve(opts.stateDir);\n const fmt = (opts.format ?? 'text').toLowerCase();\n if (!deployId) {\n const list = await deployCheckpoint.listCheckpoints(stateDir);\n if (fmt === 'json') {\n process.stdout.write(JSON.stringify(list, null, 2) + '\\n');\n return;\n }\n if (list.length === 0) {\n process.stdout.write('No resumable deploys.\\n');\n return;\n }\n for (const item of list) {\n process.stdout.write(\n `${item.deployId} ${item.state} ${item.completedSteps}/${item.totalSteps} steps profile=${item.profile}${item.env ? ` env=${item.env}` : ''} updated=${item.lastUpdatedAt}\\n`,\n );\n }\n return;\n }\n const state = await deployCheckpoint.loadCheckpoint(stateDir, deployId);\n if (!state) {\n process.stderr.write(\n `No checkpoint found for deploy id ${JSON.stringify(deployId)} under ${stateDir}.\\n`,\n );\n process.exitCode = 1;\n return;\n }\n if (fmt === 'json') {\n process.stdout.write(JSON.stringify(state, null, 2) + '\\n');\n } else {\n process.stdout.write(deployCheckpoint.formatCheckpointStatus(state) + '\\n');\n }\n if (opts.remove) {\n await deployCheckpoint.removeCheckpoint(stateDir, deployId);\n process.stderr.write(`Removed checkpoint ${deployId}.\\n`);\n }\n },\n );\n return cmd;\n}\n"],"mappings":";;;AAWA,OAAO,UAAU;AACjB,SAAS,eAAe;AACxB,SAAS,wBAAwB;AAEjC,IAAM,oBAAoB,KAAK,KAAK,QAAQ,IAAI,GAAG,iBAAiB,qBAAqB;AAElF,SAAS,sBAA+B;AAC7C,QAAM,MAAM,IAAI,QAAQ,eAAe;AACvC,MACG,YAAY,qEAAqE,EACjF,SAAS,cAAc,sDAAsD,EAC7E,OAAO,sBAAsB,+CAA+C,iBAAiB,EAC7F,OAAO,YAAY,sEAAsE,KAAK,EAC9F,OAAO,kBAAkB,8BAA8B,MAAM,EAC7D;AAAA,IACC,OACE,UACA,SACG;AACH,YAAM,WAAW,KAAK,QAAQ,KAAK,QAAQ;AAC3C,YAAM,OAAO,KAAK,UAAU,QAAQ,YAAY;AAChD,UAAI,CAAC,UAAU;AACb,cAAM,OAAO,MAAM,iBAAiB,gBAAgB,QAAQ;AAC5D,YAAI,QAAQ,QAAQ;AAClB,kBAAQ,OAAO,MAAM,KAAK,UAAU,MAAM,MAAM,CAAC,IAAI,IAAI;AACzD;AAAA,QACF;AACA,YAAI,KAAK,WAAW,GAAG;AACrB,kBAAQ,OAAO,MAAM,yBAAyB;AAC9C;AAAA,QACF;AACA,mBAAW,QAAQ,MAAM;AACvB,kBAAQ,OAAO;AAAA,YACb,GAAG,KAAK,QAAQ,KAAK,KAAK,KAAK,KAAK,KAAK,cAAc,IAAI,KAAK,UAAU,mBAAmB,KAAK,OAAO,GAAG,KAAK,MAAM,QAAQ,KAAK,GAAG,KAAK,EAAE,aAAa,KAAK,aAAa;AAAA;AAAA,UAC/K;AAAA,QACF;AACA;AAAA,MACF;AACA,YAAM,QAAQ,MAAM,iBAAiB,eAAe,UAAU,QAAQ;AACtE,UAAI,CAAC,OAAO;AACV,gBAAQ,OAAO;AAAA,UACb,qCAAqC,KAAK,UAAU,QAAQ,CAAC,UAAU,QAAQ;AAAA;AAAA,QACjF;AACA,gBAAQ,WAAW;AACnB;AAAA,MACF;AACA,UAAI,QAAQ,QAAQ;AAClB,gBAAQ,OAAO,MAAM,KAAK,UAAU,OAAO,MAAM,CAAC,IAAI,IAAI;AAAA,MAC5D,OAAO;AACL,gBAAQ,OAAO,MAAM,iBAAiB,uBAAuB,KAAK,IAAI,IAAI;AAAA,MAC5E;AACA,UAAI,KAAK,QAAQ;AACf,cAAM,iBAAiB,iBAAiB,UAAU,QAAQ;AAC1D,gBAAQ,OAAO,MAAM,sBAAsB,QAAQ;AAAA,CAAK;AAAA,MAC1D;AAAA,IACF;AAAA,EACF;AACF,SAAO;AACT;","names":[]}
@@ -0,0 +1,135 @@
1
+ import "./chunk-DGUM43GV.js";
2
+
3
+ // src/commands/design.ts
4
+ import { Command } from "commander";
5
+ import { ai, designSnowflakeSchema, designStar } from "@ddt-tools/core";
6
+ var KINDS = ["star"];
7
+ function designCommand() {
8
+ const cmd = new Command("design");
9
+ cmd.description(
10
+ "AI-assist: generative schema-design scaffolds. Output always carries a REVIEW BEFORE RUNNING header."
11
+ );
12
+ cmd.command("star").description(
13
+ "Propose a star-schema (fact + dimensions) for the described domain. Returns a `ddt template star` invocation you can review and run."
14
+ ).requiredOption(
15
+ "--description <text>",
16
+ 'Free-form domain description. Use "-" to read from stdin.'
17
+ ).option("--target <fqn>", "Optional target FQN (e.g. main.bronze).").option("--context <text>", "Optional extra constraints.").option("--format <fmt>", "Output format: text | json. Default text.", "text").option(
18
+ "--ai-max-spend <usd>",
19
+ "Refuse the call if today's estimated spend \u2265 this (USD). 0 = no cap.",
20
+ "0"
21
+ ).action(async (opts) => {
22
+ await runDesign("star", opts);
23
+ });
24
+ cmd.command("snowflake-schema").description(
25
+ 'Refactor a star into a snowflake schema. AI proposes which dims to normalize into parent/child pairs based on a prose refinement (e.g. "normalize geography down to country"). Returns a reviewable script with `ddt template scd1` calls + manual bridge-wiring steps.'
26
+ ).requiredOption("--fact <name>", "Fact-table name (lowercase identifier).").requiredOption("--dims <list>", "Comma-separated list of existing dimensions on the star.").requiredOption(
27
+ "--refinement <text>",
28
+ 'Free-form description of the desired snowflake refactor. Use "-" to read from stdin.'
29
+ ).option("--target <fqn>", "Optional target FQN (e.g. main.bronze).").option("--context <text>", "Optional extra constraints.").option("--format <fmt>", "Output format: text | json. Default text.", "text").option(
30
+ "--ai-max-spend <usd>",
31
+ "Refuse the call if today's estimated spend \u2265 this (USD). 0 = no cap.",
32
+ "0"
33
+ ).action(async (opts) => {
34
+ await runDesignSnowflakeSchema(opts);
35
+ });
36
+ return cmd;
37
+ }
38
+ async function runDesignSnowflakeSchema(opts) {
39
+ const refinement = String(opts.refinement) === "-" ? await readStdin() : String(opts.refinement);
40
+ const targetFqn = opts.target ? splitFqn(String(opts.target)) : void 0;
41
+ const dimensions = String(opts.dims).split(",").map((s) => s.trim()).filter(Boolean);
42
+ if (dimensions.length === 0) {
43
+ throw new Error("--dims must include at least one dimension.");
44
+ }
45
+ const result = await designSnowflakeSchema.designSnowflakeSchema(
46
+ {
47
+ factName: String(opts.fact),
48
+ dimensions,
49
+ refinement,
50
+ ...targetFqn ? { targetFqn } : {},
51
+ ...opts.context ? { additionalContext: String(opts.context) } : {}
52
+ },
53
+ {
54
+ completeFn: async (prompt) => {
55
+ const r = await ai.complete([{ role: "user", content: prompt }], {
56
+ feature: "design-snowflake-schema",
57
+ maxSpendUsd: Number(opts.aiMaxSpend ?? "0") || 0
58
+ });
59
+ return r.text;
60
+ }
61
+ },
62
+ "ddt"
63
+ );
64
+ if (String(opts.format).toLowerCase() === "json") {
65
+ const { rawModelText: _omit, ...keep } = result;
66
+ console.log(JSON.stringify(keep, null, 2));
67
+ return;
68
+ }
69
+ console.log(result.proposedInvocation);
70
+ if (result.assumptions.length > 0) {
71
+ console.error("");
72
+ console.error("Model assumptions:");
73
+ for (const a of result.assumptions) console.error(` - ${a}`);
74
+ }
75
+ if (result.parseFailed) {
76
+ console.warn("Model output could not be parsed \u2014 see the raw text via --format json.");
77
+ }
78
+ }
79
+ async function runDesign(kind, opts) {
80
+ if (!KINDS.includes(kind)) {
81
+ throw new Error(`Unknown kind "${kind}". Use one of: ${KINDS.join(" | ")}`);
82
+ }
83
+ const description = String(opts.description) === "-" ? await readStdin() : String(opts.description);
84
+ const targetFqn = opts.target ? splitFqn(String(opts.target)) : void 0;
85
+ const result = await designStar.designStar(
86
+ {
87
+ description,
88
+ ...targetFqn ? { targetFqn } : {},
89
+ ...opts.context ? { additionalContext: String(opts.context) } : {}
90
+ },
91
+ {
92
+ completeFn: async (prompt) => {
93
+ const r = await ai.complete([{ role: "user", content: prompt }], {
94
+ feature: "design-star",
95
+ maxSpendUsd: Number(opts.aiMaxSpend ?? "0") || 0
96
+ });
97
+ return r.text;
98
+ }
99
+ },
100
+ "ddt"
101
+ );
102
+ if (String(opts.format).toLowerCase() === "json") {
103
+ const { rawModelText: _omit, ...keep } = result;
104
+ console.log(JSON.stringify(keep, null, 2));
105
+ return;
106
+ }
107
+ console.log(result.proposedInvocation);
108
+ if (result.assumptions.length > 0) {
109
+ console.error("");
110
+ console.error("Model assumptions:");
111
+ for (const a of result.assumptions) console.error(` - ${a}`);
112
+ }
113
+ if (result.parseFailed) {
114
+ console.warn("Model output could not be parsed \u2014 see the raw text via --format json.");
115
+ }
116
+ }
117
+ function splitFqn(fqn) {
118
+ const parts = fqn.split(".");
119
+ if (parts.length === 1) return { database: parts[0] };
120
+ if (parts.length === 2) return { database: parts[0], schema: parts[1] };
121
+ throw new Error(
122
+ `Invalid --target "${fqn}": expected 1 or 2 dot-separated parts (catalog[.schema]).`
123
+ );
124
+ }
125
+ async function readStdin() {
126
+ const chunks = [];
127
+ for await (const chunk of process.stdin) {
128
+ chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
129
+ }
130
+ return Buffer.concat(chunks).toString("utf8");
131
+ }
132
+ export {
133
+ designCommand
134
+ };
135
+ //# sourceMappingURL=design-ILX3ZSWW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/design.ts"],"sourcesContent":["/**\n * `ddt design <kind>` — AI-assisted schema-design surface.\n *\n * v1 supports `star` — generative star-schema designer that returns a\n * `template star` invocation tuned to a prose domain description.\n * Composes `@ddt-tools/core/designStar.designStar` with `ai.complete` so the\n * CLI is a thin adapter; the prompt-building + parser live in core for\n * shared testability with the MCP/LSP/VS Code hosts.\n *\n * Mirrors `sdt design`.\n */\nimport { Command } from 'commander';\nimport { ai, designSnowflakeSchema, designStar } from '@ddt-tools/core';\n\nconst KINDS = ['star'] as const;\ntype Kind = (typeof KINDS)[number];\n\nexport function designCommand(): Command {\n const cmd = new Command('design');\n cmd.description(\n 'AI-assist: generative schema-design scaffolds. Output always carries a REVIEW BEFORE RUNNING header.',\n );\n\n cmd\n .command('star')\n .description(\n 'Propose a star-schema (fact + dimensions) for the described domain. ' +\n 'Returns a `ddt template star` invocation you can review and run.',\n )\n .requiredOption(\n '--description <text>',\n 'Free-form domain description. Use \"-\" to read from stdin.',\n )\n .option('--target <fqn>', 'Optional target FQN (e.g. main.bronze).')\n .option('--context <text>', 'Optional extra constraints.')\n .option('--format <fmt>', 'Output format: text | json. Default text.', 'text')\n .option(\n '--ai-max-spend <usd>',\n \"Refuse the call if today's estimated spend ≥ this (USD). 0 = no cap.\",\n '0',\n )\n .action(async (opts) => {\n await runDesign('star', opts);\n });\n\n cmd\n .command('snowflake-schema')\n .description(\n 'Refactor a star into a snowflake schema. AI proposes which dims to normalize ' +\n 'into parent/child pairs based on a prose refinement (e.g. \"normalize geography down to country\"). ' +\n 'Returns a reviewable script with `ddt template scd1` calls + manual bridge-wiring steps.',\n )\n .requiredOption('--fact <name>', 'Fact-table name (lowercase identifier).')\n .requiredOption('--dims <list>', 'Comma-separated list of existing dimensions on the star.')\n .requiredOption(\n '--refinement <text>',\n 'Free-form description of the desired snowflake refactor. Use \"-\" to read from stdin.',\n )\n .option('--target <fqn>', 'Optional target FQN (e.g. main.bronze).')\n .option('--context <text>', 'Optional extra constraints.')\n .option('--format <fmt>', 'Output format: text | json. Default text.', 'text')\n .option(\n '--ai-max-spend <usd>',\n \"Refuse the call if today's estimated spend ≥ this (USD). 0 = no cap.\",\n '0',\n )\n .action(async (opts) => {\n await runDesignSnowflakeSchema(opts);\n });\n\n return cmd;\n}\n\nasync function runDesignSnowflakeSchema(opts: Record<string, unknown>): Promise<void> {\n const refinement = String(opts.refinement) === '-' ? await readStdin() : String(opts.refinement);\n const targetFqn = opts.target ? splitFqn(String(opts.target)) : undefined;\n const dimensions = String(opts.dims)\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean);\n\n if (dimensions.length === 0) {\n throw new Error('--dims must include at least one dimension.');\n }\n\n const result = await designSnowflakeSchema.designSnowflakeSchema(\n {\n factName: String(opts.fact),\n dimensions,\n refinement,\n ...(targetFqn ? { targetFqn } : {}),\n ...(opts.context ? { additionalContext: String(opts.context) } : {}),\n },\n {\n completeFn: async (prompt) => {\n const r = await ai.complete([{ role: 'user', content: prompt }], {\n feature: 'design-snowflake-schema',\n maxSpendUsd: Number(opts.aiMaxSpend ?? '0') || 0,\n });\n return r.text;\n },\n },\n 'ddt',\n );\n\n if (String(opts.format).toLowerCase() === 'json') {\n const { rawModelText: _omit, ...keep } = result;\n console.log(JSON.stringify(keep, null, 2));\n return;\n }\n\n console.log(result.proposedInvocation);\n if (result.assumptions.length > 0) {\n console.error('');\n console.error('Model assumptions:');\n for (const a of result.assumptions) console.error(` - ${a}`);\n }\n if (result.parseFailed) {\n console.warn('Model output could not be parsed — see the raw text via --format json.');\n }\n}\n\nasync function runDesign(kind: Kind, opts: Record<string, unknown>): Promise<void> {\n if (!KINDS.includes(kind)) {\n throw new Error(`Unknown kind \"${kind}\". Use one of: ${KINDS.join(' | ')}`);\n }\n const description =\n String(opts.description) === '-' ? await readStdin() : String(opts.description);\n const targetFqn = opts.target ? splitFqn(String(opts.target)) : undefined;\n\n const result = await designStar.designStar(\n {\n description,\n ...(targetFqn ? { targetFqn } : {}),\n ...(opts.context ? { additionalContext: String(opts.context) } : {}),\n },\n {\n completeFn: async (prompt) => {\n const r = await ai.complete([{ role: 'user', content: prompt }], {\n feature: 'design-star',\n maxSpendUsd: Number(opts.aiMaxSpend ?? '0') || 0,\n });\n return r.text;\n },\n },\n 'ddt',\n );\n\n if (String(opts.format).toLowerCase() === 'json') {\n const { rawModelText: _omit, ...keep } = result;\n console.log(JSON.stringify(keep, null, 2));\n return;\n }\n\n console.log(result.proposedInvocation);\n if (result.assumptions.length > 0) {\n console.error('');\n console.error('Model assumptions:');\n for (const a of result.assumptions) console.error(` - ${a}`);\n }\n if (result.parseFailed) {\n console.warn('Model output could not be parsed — see the raw text via --format json.');\n }\n}\n\nfunction splitFqn(fqn: string): { database?: string; schema?: string } {\n const parts = fqn.split('.');\n if (parts.length === 1) return { database: parts[0] };\n if (parts.length === 2) return { database: parts[0]!, schema: parts[1] };\n throw new Error(\n `Invalid --target \"${fqn}\": expected 1 or 2 dot-separated parts (catalog[.schema]).`,\n );\n}\n\nasync function readStdin(): Promise<string> {\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : (chunk as Buffer));\n }\n return Buffer.concat(chunks).toString('utf8');\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,150 @@
1
+ import {
2
+ attachExplainFlag,
3
+ runExplain
4
+ } from "./chunk-XFXG347C.js";
5
+ import "./chunk-DGUM43GV.js";
6
+
7
+ // src/commands/diagnose.ts
8
+ import { promises as fs } from "fs";
9
+ import path from "path";
10
+ import { Command } from "commander";
11
+ import {
12
+ diagnostics,
13
+ interop,
14
+ loadProject,
15
+ pac,
16
+ parseProjectModel
17
+ } from "@ddt-tools/core";
18
+ function diagnoseCommand() {
19
+ const cmd = new Command("diagnose");
20
+ cmd.description(
21
+ "Project-level health report: lint + lineage smells + object smells + cost smells, with reasoning."
22
+ ).requiredOption("--source <path>", ".ddtproj or .ddtpac to analyze.").option(
23
+ "--category <c>",
24
+ "Filter to one category: lint | lineage | smell | cost | dim-modeling."
25
+ ).option(
26
+ "--min-severity <s>",
27
+ "Filter to a minimum severity: error | warning | info. Default info (everything).",
28
+ "info"
29
+ ).option(
30
+ "--format <fmt>",
31
+ "table | json | markdown | sarif. sarif emits SARIF 2.1.0 for GitHub code-scanning / Azure DevOps. Default table.",
32
+ "table"
33
+ ).option("-o, --out <path>", "Output file path. Defaults to stdout.").action(
34
+ async (opts) => {
35
+ const sourcePath = String(opts.source);
36
+ const model = await loadModel(sourcePath);
37
+ const report = diagnostics.analyzeProject(model, {
38
+ source: sourcePath,
39
+ ...opts.category ? { category: String(opts.category) } : {},
40
+ ...opts.minSeverity ? { minSeverity: String(opts.minSeverity) } : {}
41
+ });
42
+ const fmt = String(opts.format ?? "table").toLowerCase();
43
+ let payload;
44
+ if (fmt === "json") {
45
+ payload = JSON.stringify(report, null, 2);
46
+ } else if (fmt === "markdown") {
47
+ payload = renderReportMarkdown(report);
48
+ } else if (fmt === "sarif") {
49
+ payload = JSON.stringify(interop.diagnosticReportToSarif(report), null, 2);
50
+ } else if (fmt === "table") {
51
+ payload = diagnostics.formatReport(report);
52
+ } else {
53
+ throw new Error(`Unknown --format: ${fmt}. Use table | json | markdown | sarif.`);
54
+ }
55
+ if (opts.out) {
56
+ const out = path.resolve(String(opts.out));
57
+ await fs.mkdir(path.dirname(out), { recursive: true });
58
+ await fs.writeFile(out, payload + (payload.endsWith("\n") ? "" : "\n"), "utf8");
59
+ console.error(
60
+ `Wrote ${out} (${payload.length} bytes, ${report.findings.length} finding(s)).`
61
+ );
62
+ } else {
63
+ process.stdout.write(payload + (payload.endsWith("\n") ? "" : "\n"));
64
+ }
65
+ if (report.totals.error > 0) process.exitCode = 1;
66
+ await runExplain({ feature: "diagnose.explain" }, opts, () => buildDiagnosePrompt(report));
67
+ }
68
+ );
69
+ attachExplainFlag(cmd);
70
+ return cmd;
71
+ }
72
+ function buildDiagnosePrompt(report) {
73
+ const lines = [];
74
+ lines.push(`Source: ${report.source || "(unknown)"}`);
75
+ lines.push(
76
+ `Totals: ${report.totals.error} error, ${report.totals.warning} warning, ${report.totals.info} info`
77
+ );
78
+ lines.push("");
79
+ const top = report.findings.slice().sort((a, b) => sevWeight(b.severity) - sevWeight(a.severity)).slice(0, 40);
80
+ lines.push(`Top ${top.length} findings (highest severity first):`);
81
+ for (const d of top) {
82
+ lines.push(`- [${d.severity}] ${d.id} \xB7 ${d.fqn || "(project)"} \u2014 ${d.message}`);
83
+ }
84
+ if (report.findings.length > top.length) {
85
+ lines.push(`(\u2026 ${report.findings.length - top.length} more truncated)`);
86
+ }
87
+ lines.push("");
88
+ lines.push(
89
+ "Explain the dominant themes, what they suggest about the project health, and the highest-leverage fixes the team should tackle first."
90
+ );
91
+ return lines.join("\n");
92
+ }
93
+ function sevWeight(s) {
94
+ return s === "error" ? 3 : s === "warning" ? 2 : 1;
95
+ }
96
+ function renderReportMarkdown(report) {
97
+ const lines = [];
98
+ lines.push(`# Diagnostic report \u2014 \`${report.source || "(no source)"}\``);
99
+ lines.push("");
100
+ lines.push(`Generated: ${report.generatedAt}`);
101
+ lines.push("");
102
+ lines.push(
103
+ `**Totals:** ${report.totals.error} error \xB7 ${report.totals.warning} warning \xB7 ${report.totals.info} info`
104
+ );
105
+ lines.push("");
106
+ lines.push("## By category");
107
+ lines.push("");
108
+ lines.push("| Category | Count |");
109
+ lines.push("|---|---|");
110
+ for (const [k, v] of Object.entries(report.byCategory)) {
111
+ if (v > 0) lines.push(`| ${k} | ${v} |`);
112
+ }
113
+ lines.push("");
114
+ if (report.findings.length === 0) {
115
+ lines.push("_No findings._");
116
+ return lines.join("\n");
117
+ }
118
+ lines.push("## Findings");
119
+ lines.push("");
120
+ for (const d of report.findings) {
121
+ const glyph = d.severity === "error" ? "\u{1F6D1}" : d.severity === "warning" ? "\u26A0\uFE0F" : "\u2139\uFE0F";
122
+ lines.push(`### ${glyph} \`${d.id}\` \u2014 ${d.fqn || "(project)"}`);
123
+ lines.push("");
124
+ lines.push(`**${d.message}**`);
125
+ lines.push("");
126
+ lines.push(`*Why:* ${d.reasoning}`);
127
+ lines.push("");
128
+ lines.push(`*Suggestion:* ${d.suggestion}`);
129
+ lines.push("");
130
+ if (d.related && d.related.length > 0) {
131
+ lines.push(
132
+ `*Related:* ${d.related.slice(0, 5).map((r) => `\`${r}\``).join(", ")}`
133
+ );
134
+ lines.push("");
135
+ }
136
+ }
137
+ return lines.join("\n");
138
+ }
139
+ async function loadModel(sourcePath) {
140
+ if (sourcePath.endsWith(".ddtpac")) {
141
+ const c = await pac.readPac(sourcePath);
142
+ return c.model;
143
+ }
144
+ const loaded = await loadProject(sourcePath);
145
+ return await parseProjectModel(loaded);
146
+ }
147
+ export {
148
+ diagnoseCommand
149
+ };
150
+ //# sourceMappingURL=diagnose-WPUL67E4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/diagnose.ts"],"sourcesContent":["import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { Command } from 'commander';\nimport {\n diagnostics,\n interop,\n loadProject,\n pac,\n parseProjectModel,\n type DatabricksObject,\n} from '@ddt-tools/core';\nimport { attachExplainFlag, runExplain } from '../util/ai-explain.js';\n\n/**\n * `ddt diagnose` — project-level health report. Bundles lint, lineage\n * smells (orphan / no-downstream / hot-table / cycle), object smells\n * (`SELECT *` in views, large tables without CLUSTER BY / Z-ORDER,\n * streaming tables without watermark / checkpoint, deletion-vector\n * gaps) and cost smells (long delta.logRetentionDuration). Every\n * finding ships with a **reasoning** line explaining *why* it matters.\n *\n * Mirrors `sdt diagnose`.\n */\nexport function diagnoseCommand(): Command {\n const cmd = new Command('diagnose');\n cmd\n .description(\n 'Project-level health report: lint + lineage smells + object smells + cost smells, with reasoning.',\n )\n .requiredOption('--source <path>', '.ddtproj or .ddtpac to analyze.')\n .option(\n '--category <c>',\n 'Filter to one category: lint | lineage | smell | cost | dim-modeling.',\n )\n .option(\n '--min-severity <s>',\n 'Filter to a minimum severity: error | warning | info. Default info (everything).',\n 'info',\n )\n .option(\n '--format <fmt>',\n 'table | json | markdown | sarif. sarif emits SARIF 2.1.0 for GitHub code-scanning / Azure DevOps. Default table.',\n 'table',\n )\n .option('-o, --out <path>', 'Output file path. Defaults to stdout.')\n .action(\n async (opts: {\n source: string;\n category?: string;\n minSeverity?: string;\n format?: string;\n out?: string;\n explain?: boolean;\n }) => {\n const sourcePath = String(opts.source);\n const model = await loadModel(sourcePath);\n const report = diagnostics.analyzeProject(model, {\n source: sourcePath,\n ...(opts.category\n ? { category: String(opts.category) as diagnostics.DiagnosticCategory }\n : {}),\n ...(opts.minSeverity\n ? { minSeverity: String(opts.minSeverity) as diagnostics.DiagnosticSeverity }\n : {}),\n });\n\n const fmt = String(opts.format ?? 'table').toLowerCase();\n let payload: string;\n if (fmt === 'json') {\n payload = JSON.stringify(report, null, 2);\n } else if (fmt === 'markdown') {\n payload = renderReportMarkdown(report);\n } else if (fmt === 'sarif') {\n payload = JSON.stringify(interop.diagnosticReportToSarif(report), null, 2);\n } else if (fmt === 'table') {\n payload = diagnostics.formatReport(report);\n } else {\n throw new Error(`Unknown --format: ${fmt}. Use table | json | markdown | sarif.`);\n }\n\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.error(\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 await runExplain({ feature: 'diagnose.explain' }, opts, () => buildDiagnosePrompt(report));\n },\n );\n attachExplainFlag(cmd);\n return cmd;\n}\n\nfunction buildDiagnosePrompt(report: diagnostics.DiagnosticReport): string {\n const lines: string[] = [];\n lines.push(`Source: ${report.source || '(unknown)'}`);\n lines.push(\n `Totals: ${report.totals.error} error, ${report.totals.warning} warning, ${report.totals.info} info`,\n );\n lines.push('');\n const top = report.findings\n .slice()\n .sort((a, b) => sevWeight(b.severity) - sevWeight(a.severity))\n .slice(0, 40);\n lines.push(`Top ${top.length} findings (highest severity first):`);\n for (const d of top) {\n lines.push(`- [${d.severity}] ${d.id} · ${d.fqn || '(project)'} — ${d.message}`);\n }\n if (report.findings.length > top.length) {\n lines.push(`(… ${report.findings.length - top.length} more truncated)`);\n }\n lines.push('');\n lines.push(\n 'Explain the dominant themes, what they suggest about the project health, and the highest-leverage fixes the team should tackle first.',\n );\n return lines.join('\\n');\n}\n\nfunction sevWeight(s: diagnostics.DiagnosticSeverity): number {\n return s === 'error' ? 3 : s === 'warning' ? 2 : 1;\n}\n\nfunction renderReportMarkdown(report: diagnostics.DiagnosticReport): string {\n const lines: string[] = [];\n lines.push(`# Diagnostic report — \\`${report.source || '(no source)'}\\``);\n lines.push('');\n lines.push(`Generated: ${report.generatedAt}`);\n lines.push('');\n lines.push(\n `**Totals:** ${report.totals.error} error · ${report.totals.warning} warning · ${report.totals.info} info`,\n );\n lines.push('');\n lines.push('## By category');\n lines.push('');\n lines.push('| Category | Count |');\n lines.push('|---|---|');\n for (const [k, v] of Object.entries(report.byCategory)) {\n if (v > 0) lines.push(`| ${k} | ${v} |`);\n }\n lines.push('');\n if (report.findings.length === 0) {\n lines.push('_No findings._');\n return lines.join('\\n');\n }\n lines.push('## Findings');\n lines.push('');\n for (const d of report.findings) {\n const glyph = d.severity === 'error' ? '🛑' : d.severity === 'warning' ? '⚠️' : 'ℹ️';\n lines.push(`### ${glyph} \\`${d.id}\\` — ${d.fqn || '(project)'}`);\n lines.push('');\n lines.push(`**${d.message}**`);\n lines.push('');\n lines.push(`*Why:* ${d.reasoning}`);\n lines.push('');\n lines.push(`*Suggestion:* ${d.suggestion}`);\n lines.push('');\n if (d.related && d.related.length > 0) {\n lines.push(\n `*Related:* ${d.related\n .slice(0, 5)\n .map((r) => `\\`${r}\\``)\n .join(', ')}`,\n );\n lines.push('');\n }\n }\n return lines.join('\\n');\n}\n\nasync function loadModel(sourcePath: string): Promise<DatabricksObject[]> {\n if (sourcePath.endsWith('.ddtpac')) {\n const c = await pac.readPac(sourcePath);\n return c.model;\n }\n const loaded = await loadProject(sourcePath);\n return await parseProjectModel(loaded);\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,78 @@
1
+ import "./chunk-DGUM43GV.js";
2
+
3
+ // src/commands/discover.ts
4
+ import { rmSync } from "fs";
5
+ import { join } from "path";
6
+ import { homedir } from "os";
7
+ import { Command } from "commander";
8
+ import chalk from "chalk";
9
+ import { FeatureAdvisor, DDT_HINT_RULES } from "@ddt-tools/core/discovery";
10
+ var IMPORTANT_COMMANDS = [
11
+ "build",
12
+ "publish",
13
+ "compare",
14
+ "drift",
15
+ "lint",
16
+ "extract",
17
+ "safety",
18
+ "verify",
19
+ "validate",
20
+ "script"
21
+ ];
22
+ function discoverCommand() {
23
+ const cmd = new Command("discover");
24
+ cmd.description("Show personalized feature suggestions based on your usage history.").option("--reset", "Clear usage history and start fresh.").action((opts) => {
25
+ if (opts.reset) {
26
+ try {
27
+ rmSync(join(homedir(), ".ddt", "discovery.json"), { force: true });
28
+ } catch {
29
+ }
30
+ console.log(chalk.dim(" Discovery history cleared."));
31
+ return;
32
+ }
33
+ const advisor = new FeatureAdvisor("ddt", DDT_HINT_RULES);
34
+ const stats = advisor.getUsageStats();
35
+ const total = Object.values(stats).reduce((a, b) => a + b, 0);
36
+ console.log("");
37
+ console.log(chalk.bold(" Feature Discovery") + chalk.dim(" \u2014 based on your usage history"));
38
+ console.log("");
39
+ if (total === 0) {
40
+ console.log(
41
+ chalk.dim(
42
+ " No usage recorded yet. Run a few ddt commands to get personalized suggestions.\n"
43
+ )
44
+ );
45
+ console.log(chalk.dim(" Quick start:"));
46
+ console.log(` ${chalk.cyan("ddt init")} initialize a new project`);
47
+ console.log(` ${chalk.cyan("ddt build")} build a .ddtpac artifact`);
48
+ console.log(` ${chalk.cyan("ddt compare")} compare project vs. Unity Catalog`);
49
+ console.log("");
50
+ console.log(chalk.dim(" ddt features list \xB7 ddt explain <topic>"));
51
+ console.log("");
52
+ return;
53
+ }
54
+ const tried = IMPORTANT_COMMANDS.filter((c) => (stats[c] ?? 0) > 0);
55
+ const untried = IMPORTANT_COMMANDS.filter((c) => (stats[c] ?? 0) === 0);
56
+ if (tried.length > 0) {
57
+ console.log(chalk.dim(" Commands you use:"));
58
+ const sorted = [...tried].sort((a, b) => (stats[b] ?? 0) - (stats[a] ?? 0));
59
+ for (const c of sorted) {
60
+ console.log(` ${chalk.green("\u2714")} ddt ${c.padEnd(14)} ${chalk.dim(`${stats[c]}\xD7`)}`);
61
+ }
62
+ console.log("");
63
+ }
64
+ if (untried.length > 0) {
65
+ console.log(chalk.dim(" Worth exploring:"));
66
+ for (const c of untried) {
67
+ console.log(` ${chalk.yellow("\u2192")} ${chalk.cyan(`ddt ${c}`)}`);
68
+ }
69
+ console.log("");
70
+ }
71
+ console.log(chalk.dim(" ddt features list \xB7 ddt explain <topic>\n"));
72
+ });
73
+ return cmd;
74
+ }
75
+ export {
76
+ discoverCommand
77
+ };
78
+ //# sourceMappingURL=discover-DEO2R5T6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/discover.ts"],"sourcesContent":["import { rmSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport { FeatureAdvisor, DDT_HINT_RULES } from '@ddt-tools/core/discovery';\n\nconst IMPORTANT_COMMANDS = [\n 'build',\n 'publish',\n 'compare',\n 'drift',\n 'lint',\n 'extract',\n 'safety',\n 'verify',\n 'validate',\n 'script',\n];\n\nexport function discoverCommand(): Command {\n const cmd = new Command('discover');\n cmd\n .description('Show personalized feature suggestions based on your usage history.')\n .option('--reset', 'Clear usage history and start fresh.')\n .action((opts) => {\n if (opts.reset as boolean) {\n try {\n rmSync(join(homedir(), '.ddt', 'discovery.json'), { force: true });\n } catch {\n /* ignore */\n }\n console.log(chalk.dim(' Discovery history cleared.'));\n return;\n }\n\n const advisor = new FeatureAdvisor('ddt', DDT_HINT_RULES);\n const stats = advisor.getUsageStats();\n const total = Object.values(stats).reduce((a, b) => a + b, 0);\n\n console.log('');\n console.log(chalk.bold(' Feature Discovery') + chalk.dim(' — based on your usage history'));\n console.log('');\n\n if (total === 0) {\n console.log(\n chalk.dim(\n ' No usage recorded yet. Run a few ddt commands to get personalized suggestions.\\n',\n ),\n );\n console.log(chalk.dim(' Quick start:'));\n console.log(` ${chalk.cyan('ddt init')} initialize a new project`);\n console.log(` ${chalk.cyan('ddt build')} build a .ddtpac artifact`);\n console.log(` ${chalk.cyan('ddt compare')} compare project vs. Unity Catalog`);\n console.log('');\n console.log(chalk.dim(' ddt features list · ddt explain <topic>'));\n console.log('');\n return;\n }\n\n const tried = IMPORTANT_COMMANDS.filter((c) => (stats[c] ?? 0) > 0);\n const untried = IMPORTANT_COMMANDS.filter((c) => (stats[c] ?? 0) === 0);\n\n if (tried.length > 0) {\n console.log(chalk.dim(' Commands you use:'));\n const sorted = [...tried].sort((a, b) => (stats[b] ?? 0) - (stats[a] ?? 0));\n for (const c of sorted) {\n console.log(` ${chalk.green('✔')} ddt ${c.padEnd(14)} ${chalk.dim(`${stats[c]}×`)}`);\n }\n console.log('');\n }\n\n if (untried.length > 0) {\n console.log(chalk.dim(' Worth exploring:'));\n for (const c of untried) {\n console.log(` ${chalk.yellow('→')} ${chalk.cyan(`ddt ${c}`)}`);\n }\n console.log('');\n }\n\n console.log(chalk.dim(' ddt features list · ddt explain <topic>\\n'));\n });\n return cmd;\n}\n"],"mappings":";;;AAAA,SAAS,cAAc;AACvB,SAAS,YAAY;AACrB,SAAS,eAAe;AACxB,SAAS,eAAe;AACxB,OAAO,WAAW;AAClB,SAAS,gBAAgB,sBAAsB;AAE/C,IAAM,qBAAqB;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,SAAS,kBAA2B;AACzC,QAAM,MAAM,IAAI,QAAQ,UAAU;AAClC,MACG,YAAY,oEAAoE,EAChF,OAAO,WAAW,sCAAsC,EACxD,OAAO,CAAC,SAAS;AAChB,QAAI,KAAK,OAAkB;AACzB,UAAI;AACF,eAAO,KAAK,QAAQ,GAAG,QAAQ,gBAAgB,GAAG,EAAE,OAAO,KAAK,CAAC;AAAA,MACnE,QAAQ;AAAA,MAER;AACA,cAAQ,IAAI,MAAM,IAAI,8BAA8B,CAAC;AACrD;AAAA,IACF;AAEA,UAAM,UAAU,IAAI,eAAe,OAAO,cAAc;AACxD,UAAM,QAAQ,QAAQ,cAAc;AACpC,UAAM,QAAQ,OAAO,OAAO,KAAK,EAAE,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC;AAE5D,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAI,MAAM,KAAK,qBAAqB,IAAI,MAAM,IAAI,qCAAgC,CAAC;AAC3F,YAAQ,IAAI,EAAE;AAEd,QAAI,UAAU,GAAG;AACf,cAAQ;AAAA,QACN,MAAM;AAAA,UACJ;AAAA,QACF;AAAA,MACF;AACA,cAAQ,IAAI,MAAM,IAAI,gBAAgB,CAAC;AACvC,cAAQ,IAAI,OAAO,MAAM,KAAK,UAAU,CAAC,uCAAuC;AAChF,cAAQ,IAAI,OAAO,MAAM,KAAK,WAAW,CAAC,sCAAsC;AAChF,cAAQ,IAAI,OAAO,MAAM,KAAK,aAAa,CAAC,6CAA6C;AACzF,cAAQ,IAAI,EAAE;AACd,cAAQ,IAAI,MAAM,IAAI,8CAA2C,CAAC;AAClE,cAAQ,IAAI,EAAE;AACd;AAAA,IACF;AAEA,UAAM,QAAQ,mBAAmB,OAAO,CAAC,OAAO,MAAM,CAAC,KAAK,KAAK,CAAC;AAClE,UAAM,UAAU,mBAAmB,OAAO,CAAC,OAAO,MAAM,CAAC,KAAK,OAAO,CAAC;AAEtE,QAAI,MAAM,SAAS,GAAG;AACpB,cAAQ,IAAI,MAAM,IAAI,qBAAqB,CAAC;AAC5C,YAAM,SAAS,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,GAAG,OAAO,MAAM,CAAC,KAAK,MAAM,MAAM,CAAC,KAAK,EAAE;AAC1E,iBAAW,KAAK,QAAQ;AACtB,gBAAQ,IAAI,OAAO,MAAM,MAAM,QAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC,IAAI,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,MAAG,CAAC,EAAE;AAAA,MACxF;AACA,cAAQ,IAAI,EAAE;AAAA,IAChB;AAEA,QAAI,QAAQ,SAAS,GAAG;AACtB,cAAQ,IAAI,MAAM,IAAI,oBAAoB,CAAC;AAC3C,iBAAW,KAAK,SAAS;AACvB,gBAAQ,IAAI,OAAO,MAAM,OAAO,QAAG,CAAC,IAAI,MAAM,KAAK,OAAO,CAAC,EAAE,CAAC,EAAE;AAAA,MAClE;AACA,cAAQ,IAAI,EAAE;AAAA,IAChB;AAEA,YAAQ,IAAI,MAAM,IAAI,gDAA6C,CAAC;AAAA,EACtE,CAAC;AACH,SAAO;AACT;","names":[]}