@sdt-tools/cli 0.2.0 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/advise-tests-6DRSZMBL.js +87 -0
- package/dist/advise-tests-6DRSZMBL.js.map +1 -0
- package/dist/ai-G4MJWHTM.js +89 -0
- package/dist/ai-G4MJWHTM.js.map +1 -0
- package/dist/anonymize-QR6JGXA7.js +123 -0
- package/dist/anonymize-QR6JGXA7.js.map +1 -0
- package/dist/approval-YVHYTV53.js +73 -0
- package/dist/approval-YVHYTV53.js.map +1 -0
- package/dist/approval-chain-54KKJZS3.js +120 -0
- package/dist/approval-chain-54KKJZS3.js.map +1 -0
- package/dist/audit-log-QZFH7LUX.js +159 -0
- package/dist/audit-log-QZFH7LUX.js.map +1 -0
- package/dist/backlog-V2YUIQDL.js +76 -0
- package/dist/backlog-V2YUIQDL.js.map +1 -0
- package/dist/bisect-GEVYAVL5.js +111 -0
- package/dist/bisect-GEVYAVL5.js.map +1 -0
- package/dist/bookmarks-57LKS7P6.js +107 -0
- package/dist/bookmarks-57LKS7P6.js.map +1 -0
- package/dist/branch-W2MGMPSH.js +88 -0
- package/dist/branch-W2MGMPSH.js.map +1 -0
- package/dist/build-VNIQFKSP.js +23 -0
- package/dist/build-VNIQFKSP.js.map +1 -0
- package/dist/catalog-JLB5VCEV.js +137 -0
- package/dist/catalog-JLB5VCEV.js.map +1 -0
- package/dist/changelog-M7XGDYSY.js +220 -0
- package/dist/changelog-M7XGDYSY.js.map +1 -0
- package/dist/chunk-DGUM43GV.js +11 -0
- package/dist/chunk-DGUM43GV.js.map +1 -0
- package/dist/chunk-EWXM4KJN.js +25 -0
- package/dist/chunk-EWXM4KJN.js.map +1 -0
- package/dist/chunk-JP2EZLR5.js +50 -0
- package/dist/chunk-JP2EZLR5.js.map +1 -0
- package/dist/chunk-VM2H4LAO.js +15 -0
- package/dist/chunk-VM2H4LAO.js.map +1 -0
- package/dist/chunk-ZWY4ZRHL.js +44 -0
- package/dist/chunk-ZWY4ZRHL.js.map +1 -0
- package/dist/cli.js +511 -19014
- package/dist/cli.js.map +1 -1
- package/dist/compare-5O6UTWPJ.js +405 -0
- package/dist/compare-5O6UTWPJ.js.map +1 -0
- package/dist/compare-profiles-7ZSNIW7B.js +218 -0
- package/dist/compare-profiles-7ZSNIW7B.js.map +1 -0
- package/dist/completion-I5U5VVAX.js +82 -0
- package/dist/completion-I5U5VVAX.js.map +1 -0
- package/dist/connection-GNTZDHXF.js +133 -0
- package/dist/connection-GNTZDHXF.js.map +1 -0
- package/dist/cost-estimate-TJDDH6TO.js +328 -0
- package/dist/cost-estimate-TJDDH6TO.js.map +1 -0
- package/dist/data-compare-UK2UXAS3.js +134 -0
- package/dist/data-compare-UK2UXAS3.js.map +1 -0
- package/dist/data-fit-Q45ENBRL.js +125 -0
- package/dist/data-fit-Q45ENBRL.js.map +1 -0
- package/dist/deploy-status-UUHKVDTI.js +58 -0
- package/dist/deploy-status-UUHKVDTI.js.map +1 -0
- package/dist/design-PO6UPBL7.js +138 -0
- package/dist/design-PO6UPBL7.js.map +1 -0
- package/dist/diagnose-6IFMELFR.js +145 -0
- package/dist/diagnose-6IFMELFR.js.map +1 -0
- package/dist/discover-A7OSZAHK.js +78 -0
- package/dist/discover-A7OSZAHK.js.map +1 -0
- package/dist/docs-CVRKGUSW.js +177 -0
- package/dist/docs-CVRKGUSW.js.map +1 -0
- package/dist/drift-XDA3BDYN.js +226 -0
- package/dist/drift-XDA3BDYN.js.map +1 -0
- package/dist/drift-gate-V7QSIOGZ.js +94 -0
- package/dist/drift-gate-V7QSIOGZ.js.map +1 -0
- package/dist/error-lookup-7ZWCZJ44.js +56 -0
- package/dist/error-lookup-7ZWCZJ44.js.map +1 -0
- package/dist/errorReporting-AQXKKGZH.js +109 -0
- package/dist/errorReporting-AQXKKGZH.js.map +1 -0
- package/dist/exec-PKBHLI7T.js +121 -0
- package/dist/exec-PKBHLI7T.js.map +1 -0
- package/dist/explain-LWKJOTL7.js +192 -0
- package/dist/explain-LWKJOTL7.js.map +1 -0
- package/dist/explorer-QOVM6VBD.js +61 -0
- package/dist/explorer-QOVM6VBD.js.map +1 -0
- package/dist/export-IYYBZ5HE.js +42 -0
- package/dist/export-IYYBZ5HE.js.map +1 -0
- package/dist/extract-VMMVRQVT.js +102 -0
- package/dist/extract-VMMVRQVT.js.map +1 -0
- package/dist/features-LE6BDZ2S.js +59 -0
- package/dist/features-LE6BDZ2S.js.map +1 -0
- package/dist/feedback-M7DM2EQC.js +161 -0
- package/dist/feedback-M7DM2EQC.js.map +1 -0
- package/dist/find-EME2JG2I.js +176 -0
- package/dist/find-EME2JG2I.js.map +1 -0
- package/dist/format-TRLWLMGS.js +141 -0
- package/dist/format-TRLWLMGS.js.map +1 -0
- package/dist/generate-6NAZGZDV.js +152 -0
- package/dist/generate-6NAZGZDV.js.map +1 -0
- package/dist/graph-QNQDAUO7.js +161 -0
- package/dist/graph-QNQDAUO7.js.map +1 -0
- package/dist/history-RONA7ZTI.js +199 -0
- package/dist/history-RONA7ZTI.js.map +1 -0
- package/dist/hosts-YBXY2ZG5.js +49 -0
- package/dist/hosts-YBXY2ZG5.js.map +1 -0
- package/dist/impact-T2JSANHS.js +59 -0
- package/dist/impact-T2JSANHS.js.map +1 -0
- package/dist/import-AELYLY6A.js +32 -0
- package/dist/import-AELYLY6A.js.map +1 -0
- package/dist/import-script-2OF5BI6A.js +83 -0
- package/dist/import-script-2OF5BI6A.js.map +1 -0
- package/dist/index.cjs +71 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +95 -31
- package/dist/index.js.map +1 -1
- package/dist/init-SWRRJMGI.js +57 -0
- package/dist/init-SWRRJMGI.js.map +1 -0
- package/dist/install-hooks-6SIAGTAF.js +109 -0
- package/dist/install-hooks-6SIAGTAF.js.map +1 -0
- package/dist/license-OAF22PLZ.js +46 -0
- package/dist/license-OAF22PLZ.js.map +1 -0
- package/dist/lineage-EW66XJ6O.js +552 -0
- package/dist/lineage-EW66XJ6O.js.map +1 -0
- package/dist/lint-FQ2OTYTQ.js +143 -0
- package/dist/lint-FQ2OTYTQ.js.map +1 -0
- package/dist/mcp-SARDMCDV.js +344 -0
- package/dist/mcp-SARDMCDV.js.map +1 -0
- package/dist/migrate-from-dbt-JVTXPWKQ.js +156 -0
- package/dist/migrate-from-dbt-JVTXPWKQ.js.map +1 -0
- package/dist/migrate-platform-NTRTOGNR.js +91 -0
- package/dist/migrate-platform-NTRTOGNR.js.map +1 -0
- package/dist/optimize-CJYWMAWA.js +105 -0
- package/dist/optimize-CJYWMAWA.js.map +1 -0
- package/dist/perf-LL2CPCJF.js +205 -0
- package/dist/perf-LL2CPCJF.js.map +1 -0
- package/dist/pii-FBDRDQ2E.js +136 -0
- package/dist/pii-FBDRDQ2E.js.map +1 -0
- package/dist/pilot-CCQERKPH.js +29 -0
- package/dist/pilot-CCQERKPH.js.map +1 -0
- package/dist/pr-comment-S5FF4QRX.js +79 -0
- package/dist/pr-comment-S5FF4QRX.js.map +1 -0
- package/dist/preview-5U4YVCRM.js +47 -0
- package/dist/preview-5U4YVCRM.js.map +1 -0
- package/dist/profile-7VC57KD2.js +101 -0
- package/dist/profile-7VC57KD2.js.map +1 -0
- package/dist/promote-AASEFTIA.js +408 -0
- package/dist/promote-AASEFTIA.js.map +1 -0
- package/dist/publish-UMVIWH6H.js +721 -0
- package/dist/publish-UMVIWH6H.js.map +1 -0
- package/dist/purge-QMXZKCMD.js +57 -0
- package/dist/purge-QMXZKCMD.js.map +1 -0
- package/dist/query-log-6OM4GI7W.js +112 -0
- package/dist/query-log-6OM4GI7W.js.map +1 -0
- package/dist/refactor-LTZQLJ35.js +5799 -0
- package/dist/refactor-LTZQLJ35.js.map +1 -0
- package/dist/refresh-4TY2AGOU.js +38 -0
- package/dist/refresh-4TY2AGOU.js.map +1 -0
- package/dist/replay-OOC25FZN.js +117 -0
- package/dist/replay-OOC25FZN.js.map +1 -0
- package/dist/revert-ODMUVJW6.js +110 -0
- package/dist/revert-ODMUVJW6.js.map +1 -0
- package/dist/review-XXPWOBFP.js +158 -0
- package/dist/review-XXPWOBFP.js.map +1 -0
- package/dist/rollback-suggest-6G2HEKFR.js +79 -0
- package/dist/rollback-suggest-6G2HEKFR.js.map +1 -0
- package/dist/safer-alternative-QFVNLG3L.js +89 -0
- package/dist/safer-alternative-QFVNLG3L.js.map +1 -0
- package/dist/safety-7QWRSUEZ.js +168 -0
- package/dist/safety-7QWRSUEZ.js.map +1 -0
- package/dist/savings-RHIXP6IT.js +95 -0
- package/dist/savings-RHIXP6IT.js.map +1 -0
- package/dist/scan-secrets-5YCQ4UCU.js +54 -0
- package/dist/scan-secrets-5YCQ4UCU.js.map +1 -0
- package/dist/schema-CIZXCQD2.js +429 -0
- package/dist/schema-CIZXCQD2.js.map +1 -0
- package/dist/script-K7CIN2P6.js +153 -0
- package/dist/script-K7CIN2P6.js.map +1 -0
- package/dist/search-BUZ5NXZZ.js +151 -0
- package/dist/search-BUZ5NXZZ.js.map +1 -0
- package/dist/seed-76QAK276.js +96 -0
- package/dist/seed-76QAK276.js.map +1 -0
- package/dist/sketch-PTLKDIK3.js +88 -0
- package/dist/sketch-PTLKDIK3.js.map +1 -0
- package/dist/snapshot-XLPR2OZ5.js +177 -0
- package/dist/snapshot-XLPR2OZ5.js.map +1 -0
- package/dist/snippets-EK4DK5CN.js +74 -0
- package/dist/snippets-EK4DK5CN.js.map +1 -0
- package/dist/standards-7T2UY6DD.js +241 -0
- package/dist/standards-7T2UY6DD.js.map +1 -0
- package/dist/suggest-VGRYSAR6.js +39 -0
- package/dist/suggest-VGRYSAR6.js.map +1 -0
- package/dist/suggest-constraints-MY5WKUHA.js +160 -0
- package/dist/suggest-constraints-MY5WKUHA.js.map +1 -0
- package/dist/suite-TRNGZWQM.js +88 -0
- package/dist/suite-TRNGZWQM.js.map +1 -0
- package/dist/telemetry-3U2QLA2S.js +75 -0
- package/dist/telemetry-3U2QLA2S.js.map +1 -0
- package/dist/template-ZERIXVXF.js +403 -0
- package/dist/template-ZERIXVXF.js.map +1 -0
- package/dist/test-5M2ED3WT.js +169 -0
- package/dist/test-5M2ED3WT.js.map +1 -0
- package/dist/trial-U732FONV.js +31 -0
- package/dist/trial-U732FONV.js.map +1 -0
- package/dist/validate-T6D2WCOK.js +106 -0
- package/dist/validate-T6D2WCOK.js.map +1 -0
- package/dist/verify-KXVASEEG.js +76 -0
- package/dist/verify-KXVASEEG.js.map +1 -0
- package/dist/watch-I6K4BNMA.js +80 -0
- package/dist/watch-I6K4BNMA.js.map +1 -0
- package/dist/xcompare-TPFLQO6W.js +87 -0
- package/dist/xcompare-TPFLQO6W.js.map +1 -0
- package/package.json +2 -2
- package/dist/cli.cjs +0 -19040
- package/dist/cli.cjs.map +0 -1
- package/dist/cli.d.cts +0 -1
- package/dist/cli.d.ts +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/data-fit.ts"],"sourcesContent":["/**\n * `sdt data-fit --source <pac> --target <pac>` — generate pre-flight\n * data-fit probe queries for narrowing type changes.\n *\n * Walks the source-vs-target compare, finds every TABLE / ICEBERG_TABLE\n * / HYBRID_TABLE / EVENT_TABLE / DYNAMIC_TABLE whose column-level diff\n * includes a type change, classifies each change via\n * `@sdt-tools/core/types.classifyTypeChange`, and for every\n * `data-fit-required` verdict emits a `SELECT count_if(...)` probe via\n * `dataFitProbeSql`.\n *\n * The probe queries are deliberately **read-only** — they run against\n * the target without modifying state. The operator runs them before\n * `sdt publish --apply` to confirm zero rows would overflow the\n * narrower type.\n *\n * Each query is wrapped in a `-- PROBE: <fqn>.<column> (<from> → <to>)`\n * header so DBA review is easy.\n */\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { Command } from 'commander';\nimport { CompareEngine, PacSource } from '@sdt-tools/core/compare';\nimport { classifyTypeChange, dataFitProbeSql } from '@sdt-tools/core/types';\nimport type { AnySnowflakeObject } from '@sdt-tools/core/model';\n\ninterface ColumnWithType {\n name: string;\n /** Either a `SnowflakeDataType` struct or a raw string (best-effort). */\n dataType: unknown;\n}\n\nfunction getColumns(obj: AnySnowflakeObject): 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 return cols as ColumnWithType[];\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 quotedFqn(obj: AnySnowflakeObject): 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 quotedCol(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>', '.sdtpac with the desired state.')\n .requiredOption('--target <path>', '.sdtpac 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 = classifyTypeChange(fromStr, toStr);\n if (check.verdict !== 'data-fit-required') continue;\n const fqn = quotedFqn(obj.source);\n const colName = pair.source.name;\n const probeSql = dataFitProbeSql(check, fqn, quotedCol(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 \\`sdt 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}.${quotedCol(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 \\`sdt 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 "@sdt-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 `sdt 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-UUHKVDTI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/deploy-status.ts"],"sourcesContent":["/**\n * `sdt deploy-status` — inspect resumable deploy checkpoints (DSR.3).\n *\n * Usage:\n * sdt deploy-status # list every resumable deploy\n * sdt deploy-status <deployId> # show one checkpoint\n * sdt deploy-status <deployId> --remove # delete a checkpoint\n * sdt deploy-status --format json # machine-readable list\n *\n * Mirrors `ddt deploy-status`.\n */\nimport path from 'node:path';\nimport { Command } from 'commander';\nimport { deployCheckpoint } from '@sdt-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 `sdt 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,138 @@
|
|
|
1
|
+
import {
|
|
2
|
+
logger
|
|
3
|
+
} from "./chunk-VM2H4LAO.js";
|
|
4
|
+
import "./chunk-DGUM43GV.js";
|
|
5
|
+
|
|
6
|
+
// src/commands/design.ts
|
|
7
|
+
import { Command } from "commander";
|
|
8
|
+
import { ai, designSnowflakeSchema, designStar } from "@sdt-tools/core";
|
|
9
|
+
var KINDS = ["star"];
|
|
10
|
+
function designCommand() {
|
|
11
|
+
const cmd = new Command("design");
|
|
12
|
+
cmd.description(
|
|
13
|
+
"AI-assist: generative schema-design scaffolds. Output always carries a REVIEW BEFORE RUNNING header."
|
|
14
|
+
);
|
|
15
|
+
cmd.command("star").description(
|
|
16
|
+
"Propose a star-schema (fact + dimensions) for the described domain. Returns a `sdt template star` invocation you can review and run."
|
|
17
|
+
).requiredOption(
|
|
18
|
+
"--description <text>",
|
|
19
|
+
'Free-form domain description. Use "-" to read from stdin.'
|
|
20
|
+
).option("--target <fqn>", "Optional target FQN (e.g. ANALYTICS.PUBLIC).").option("--context <text>", "Optional extra constraints.").option("--format <fmt>", "Output format: text | json. Default text.", "text").option(
|
|
21
|
+
"--ai-max-spend <usd>",
|
|
22
|
+
"Refuse the call if today's estimated spend \u2265 this (USD). 0 = no cap.",
|
|
23
|
+
"0"
|
|
24
|
+
).action(async (opts) => {
|
|
25
|
+
await runDesign("star", opts);
|
|
26
|
+
});
|
|
27
|
+
cmd.command("snowflake-schema").description(
|
|
28
|
+
'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 `sdt template scd1` calls + manual bridge-wiring steps.'
|
|
29
|
+
).requiredOption("--fact <name>", "Fact-table name (lowercase identifier).").requiredOption("--dims <list>", "Comma-separated list of existing dimensions on the star.").requiredOption(
|
|
30
|
+
"--refinement <text>",
|
|
31
|
+
'Free-form description of the desired snowflake refactor. Use "-" to read from stdin.'
|
|
32
|
+
).option("--target <fqn>", "Optional target FQN (e.g. ANALYTICS.CORE).").option("--context <text>", "Optional extra constraints.").option("--format <fmt>", "Output format: text | json. Default text.", "text").option(
|
|
33
|
+
"--ai-max-spend <usd>",
|
|
34
|
+
"Refuse the call if today's estimated spend \u2265 this (USD). 0 = no cap.",
|
|
35
|
+
"0"
|
|
36
|
+
).action(async (opts) => {
|
|
37
|
+
await runDesignSnowflakeSchema(opts);
|
|
38
|
+
});
|
|
39
|
+
return cmd;
|
|
40
|
+
}
|
|
41
|
+
async function runDesignSnowflakeSchema(opts) {
|
|
42
|
+
const refinement = String(opts.refinement) === "-" ? await readStdin() : String(opts.refinement);
|
|
43
|
+
const targetFqn = opts.target ? splitFqn(String(opts.target)) : void 0;
|
|
44
|
+
const dimensions = String(opts.dims).split(",").map((s) => s.trim()).filter(Boolean);
|
|
45
|
+
if (dimensions.length === 0) {
|
|
46
|
+
throw new Error("--dims must include at least one dimension.");
|
|
47
|
+
}
|
|
48
|
+
const result = await designSnowflakeSchema.designSnowflakeSchema(
|
|
49
|
+
{
|
|
50
|
+
factName: String(opts.fact),
|
|
51
|
+
dimensions,
|
|
52
|
+
refinement,
|
|
53
|
+
...targetFqn ? { targetFqn } : {},
|
|
54
|
+
...opts.context ? { additionalContext: String(opts.context) } : {}
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
completeFn: async (prompt) => {
|
|
58
|
+
const r = await ai.complete([{ role: "user", content: prompt }], {
|
|
59
|
+
feature: "design-snowflake-schema",
|
|
60
|
+
maxSpendUsd: Number(opts.aiMaxSpend ?? "0") || 0
|
|
61
|
+
});
|
|
62
|
+
return r.text;
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"sdt"
|
|
66
|
+
);
|
|
67
|
+
if (String(opts.format).toLowerCase() === "json") {
|
|
68
|
+
const { rawModelText: _omit, ...keep } = result;
|
|
69
|
+
console.log(JSON.stringify(keep, null, 2));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
console.log(result.proposedInvocation);
|
|
73
|
+
if (result.assumptions.length > 0) {
|
|
74
|
+
logger.dim("");
|
|
75
|
+
logger.dim("Model assumptions:");
|
|
76
|
+
for (const a of result.assumptions) logger.dim(` - ${a}`);
|
|
77
|
+
}
|
|
78
|
+
if (result.parseFailed) {
|
|
79
|
+
logger.warn("Model output could not be parsed \u2014 see the raw text via --format json.");
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async function runDesign(kind, opts) {
|
|
83
|
+
if (!KINDS.includes(kind)) {
|
|
84
|
+
throw new Error(`Unknown kind "${kind}". Use one of: ${KINDS.join(" | ")}`);
|
|
85
|
+
}
|
|
86
|
+
const description = String(opts.description) === "-" ? await readStdin() : String(opts.description);
|
|
87
|
+
const targetFqn = opts.target ? splitFqn(String(opts.target)) : void 0;
|
|
88
|
+
const result = await designStar.designStar(
|
|
89
|
+
{
|
|
90
|
+
description,
|
|
91
|
+
...targetFqn ? { targetFqn } : {},
|
|
92
|
+
...opts.context ? { additionalContext: String(opts.context) } : {}
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
completeFn: async (prompt) => {
|
|
96
|
+
const r = await ai.complete([{ role: "user", content: prompt }], {
|
|
97
|
+
feature: "design-star",
|
|
98
|
+
maxSpendUsd: Number(opts.aiMaxSpend ?? "0") || 0
|
|
99
|
+
});
|
|
100
|
+
return r.text;
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
"sdt"
|
|
104
|
+
);
|
|
105
|
+
if (String(opts.format).toLowerCase() === "json") {
|
|
106
|
+
const { rawModelText: _omit, ...keep } = result;
|
|
107
|
+
console.log(JSON.stringify(keep, null, 2));
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
console.log(result.proposedInvocation);
|
|
111
|
+
if (result.assumptions.length > 0) {
|
|
112
|
+
logger.dim("");
|
|
113
|
+
logger.dim("Model assumptions:");
|
|
114
|
+
for (const a of result.assumptions) logger.dim(` - ${a}`);
|
|
115
|
+
}
|
|
116
|
+
if (result.parseFailed) {
|
|
117
|
+
logger.warn("Model output could not be parsed \u2014 see the raw text via --format json.");
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function splitFqn(fqn) {
|
|
121
|
+
const parts = fqn.split(".");
|
|
122
|
+
if (parts.length === 1) return { database: parts[0] };
|
|
123
|
+
if (parts.length === 2) return { database: parts[0], schema: parts[1] };
|
|
124
|
+
throw new Error(
|
|
125
|
+
`Invalid --target "${fqn}": expected 1 or 2 dot-separated parts (database[.schema]).`
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
async function readStdin() {
|
|
129
|
+
const chunks = [];
|
|
130
|
+
for await (const chunk of process.stdin) {
|
|
131
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
132
|
+
}
|
|
133
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
134
|
+
}
|
|
135
|
+
export {
|
|
136
|
+
designCommand
|
|
137
|
+
};
|
|
138
|
+
//# sourceMappingURL=design-PO6UPBL7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/design.ts"],"sourcesContent":["/**\n * `sdt 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 `@sdt-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 `ddt design`.\n */\nimport { Command } from 'commander';\nimport { ai, designSnowflakeSchema, designStar } from '@sdt-tools/core';\nimport { logger } from '../util/logger.js';\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 `sdt 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. ANALYTICS.PUBLIC).')\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 `sdt 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. ANALYTICS.CORE).')\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 'sdt',\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 logger.dim('');\n logger.dim('Model assumptions:');\n for (const a of result.assumptions) logger.dim(` - ${a}`);\n }\n if (result.parseFailed) {\n logger.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 'sdt',\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 logger.dim('');\n logger.dim('Model assumptions:');\n for (const a of result.assumptions) logger.dim(` - ${a}`);\n }\n if (result.parseFailed) {\n logger.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 (database[.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":";;;;;;AAWA,SAAS,eAAe;AACxB,SAAS,IAAI,uBAAuB,kBAAkB;AAGtD,IAAM,QAAQ,CAAC,MAAM;AAGd,SAAS,gBAAyB;AACvC,QAAM,MAAM,IAAI,QAAQ,QAAQ;AAChC,MAAI;AAAA,IACF;AAAA,EACF;AAEA,MACG,QAAQ,MAAM,EACd;AAAA,IACC;AAAA,EAEF,EACC;AAAA,IACC;AAAA,IACA;AAAA,EACF,EACC,OAAO,kBAAkB,8CAA8C,EACvE,OAAO,oBAAoB,6BAA6B,EACxD,OAAO,kBAAkB,6CAA6C,MAAM,EAC5E;AAAA,IACC;AAAA,IACA;AAAA,IACA;AAAA,EACF,EACC,OAAO,OAAO,SAAS;AACtB,UAAM,UAAU,QAAQ,IAAI;AAAA,EAC9B,CAAC;AAEH,MACG,QAAQ,kBAAkB,EAC1B;AAAA,IACC;AAAA,EAGF,EACC,eAAe,iBAAiB,yCAAyC,EACzE,eAAe,iBAAiB,0DAA0D,EAC1F;AAAA,IACC;AAAA,IACA;AAAA,EACF,EACC,OAAO,kBAAkB,4CAA4C,EACrE,OAAO,oBAAoB,6BAA6B,EACxD,OAAO,kBAAkB,6CAA6C,MAAM,EAC5E;AAAA,IACC;AAAA,IACA;AAAA,IACA;AAAA,EACF,EACC,OAAO,OAAO,SAAS;AACtB,UAAM,yBAAyB,IAAI;AAAA,EACrC,CAAC;AAEH,SAAO;AACT;AAEA,eAAe,yBAAyB,MAA8C;AACpF,QAAM,aAAa,OAAO,KAAK,UAAU,MAAM,MAAM,MAAM,UAAU,IAAI,OAAO,KAAK,UAAU;AAC/F,QAAM,YAAY,KAAK,SAAS,SAAS,OAAO,KAAK,MAAM,CAAC,IAAI;AAChE,QAAM,aAAa,OAAO,KAAK,IAAI,EAChC,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,OAAO;AAEjB,MAAI,WAAW,WAAW,GAAG;AAC3B,UAAM,IAAI,MAAM,6CAA6C;AAAA,EAC/D;AAEA,QAAM,SAAS,MAAM,sBAAsB;AAAA,IACzC;AAAA,MACE,UAAU,OAAO,KAAK,IAAI;AAAA,MAC1B;AAAA,MACA;AAAA,MACA,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;AAAA,MACjC,GAAI,KAAK,UAAU,EAAE,mBAAmB,OAAO,KAAK,OAAO,EAAE,IAAI,CAAC;AAAA,IACpE;AAAA,IACA;AAAA,MACE,YAAY,OAAO,WAAW;AAC5B,cAAM,IAAI,MAAM,GAAG,SAAS,CAAC,EAAE,MAAM,QAAQ,SAAS,OAAO,CAAC,GAAG;AAAA,UAC/D,SAAS;AAAA,UACT,aAAa,OAAO,KAAK,cAAc,GAAG,KAAK;AAAA,QACjD,CAAC;AACD,eAAO,EAAE;AAAA,MACX;AAAA,IACF;AAAA,IACA;AAAA,EACF;AAEA,MAAI,OAAO,KAAK,MAAM,EAAE,YAAY,MAAM,QAAQ;AAChD,UAAM,EAAE,cAAc,OAAO,GAAG,KAAK,IAAI;AACzC,YAAQ,IAAI,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AACzC;AAAA,EACF;AAEA,UAAQ,IAAI,OAAO,kBAAkB;AACrC,MAAI,OAAO,YAAY,SAAS,GAAG;AACjC,WAAO,IAAI,EAAE;AACb,WAAO,IAAI,oBAAoB;AAC/B,eAAW,KAAK,OAAO,YAAa,QAAO,IAAI,OAAO,CAAC,EAAE;AAAA,EAC3D;AACA,MAAI,OAAO,aAAa;AACtB,WAAO,KAAK,6EAAwE;AAAA,EACtF;AACF;AAEA,eAAe,UAAU,MAAY,MAA8C;AACjF,MAAI,CAAC,MAAM,SAAS,IAAI,GAAG;AACzB,UAAM,IAAI,MAAM,iBAAiB,IAAI,kBAAkB,MAAM,KAAK,KAAK,CAAC,EAAE;AAAA,EAC5E;AACA,QAAM,cACJ,OAAO,KAAK,WAAW,MAAM,MAAM,MAAM,UAAU,IAAI,OAAO,KAAK,WAAW;AAChF,QAAM,YAAY,KAAK,SAAS,SAAS,OAAO,KAAK,MAAM,CAAC,IAAI;AAEhE,QAAM,SAAS,MAAM,WAAW;AAAA,IAC9B;AAAA,MACE;AAAA,MACA,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;AAAA,MACjC,GAAI,KAAK,UAAU,EAAE,mBAAmB,OAAO,KAAK,OAAO,EAAE,IAAI,CAAC;AAAA,IACpE;AAAA,IACA;AAAA,MACE,YAAY,OAAO,WAAW;AAC5B,cAAM,IAAI,MAAM,GAAG,SAAS,CAAC,EAAE,MAAM,QAAQ,SAAS,OAAO,CAAC,GAAG;AAAA,UAC/D,SAAS;AAAA,UACT,aAAa,OAAO,KAAK,cAAc,GAAG,KAAK;AAAA,QACjD,CAAC;AACD,eAAO,EAAE;AAAA,MACX;AAAA,IACF;AAAA,IACA;AAAA,EACF;AAEA,MAAI,OAAO,KAAK,MAAM,EAAE,YAAY,MAAM,QAAQ;AAChD,UAAM,EAAE,cAAc,OAAO,GAAG,KAAK,IAAI;AACzC,YAAQ,IAAI,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AACzC;AAAA,EACF;AAEA,UAAQ,IAAI,OAAO,kBAAkB;AACrC,MAAI,OAAO,YAAY,SAAS,GAAG;AACjC,WAAO,IAAI,EAAE;AACb,WAAO,IAAI,oBAAoB;AAC/B,eAAW,KAAK,OAAO,YAAa,QAAO,IAAI,OAAO,CAAC,EAAE;AAAA,EAC3D;AACA,MAAI,OAAO,aAAa;AACtB,WAAO,KAAK,6EAAwE;AAAA,EACtF;AACF;AAEA,SAAS,SAAS,KAAqD;AACrE,QAAM,QAAQ,IAAI,MAAM,GAAG;AAC3B,MAAI,MAAM,WAAW,EAAG,QAAO,EAAE,UAAU,MAAM,CAAC,EAAE;AACpD,MAAI,MAAM,WAAW,EAAG,QAAO,EAAE,UAAU,MAAM,CAAC,GAAI,QAAQ,MAAM,CAAC,EAAE;AACvE,QAAM,IAAI;AAAA,IACR,qBAAqB,GAAG;AAAA,EAC1B;AACF;AAEA,eAAe,YAA6B;AAC1C,QAAM,SAAmB,CAAC;AAC1B,mBAAiB,SAAS,QAAQ,OAAO;AACvC,WAAO,KAAK,OAAO,UAAU,WAAW,OAAO,KAAK,KAAK,IAAK,KAAgB;AAAA,EAChF;AACA,SAAO,OAAO,OAAO,MAAM,EAAE,SAAS,MAAM;AAC9C;","names":[]}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import {
|
|
2
|
+
attachExplainFlag,
|
|
3
|
+
runExplain
|
|
4
|
+
} from "./chunk-ZWY4ZRHL.js";
|
|
5
|
+
import "./chunk-VM2H4LAO.js";
|
|
6
|
+
import "./chunk-DGUM43GV.js";
|
|
7
|
+
|
|
8
|
+
// src/commands/diagnose.ts
|
|
9
|
+
import { promises as fs } from "fs";
|
|
10
|
+
import path from "path";
|
|
11
|
+
import { Command } from "commander";
|
|
12
|
+
import { diagnostics, interop, pac, project } from "@sdt-tools/core";
|
|
13
|
+
function diagnoseCommand() {
|
|
14
|
+
const cmd = new Command("diagnose");
|
|
15
|
+
cmd.description(
|
|
16
|
+
"Project-level health report: lint + lineage smells + object smells + cost smells, with reasoning."
|
|
17
|
+
).requiredOption("--source <path>", ".sdtproj or .sdtpac to analyze.").option(
|
|
18
|
+
"--category <c>",
|
|
19
|
+
"Filter to one category: lint | lineage | smell | cost | dim-modeling."
|
|
20
|
+
).option(
|
|
21
|
+
"--min-severity <s>",
|
|
22
|
+
"Filter to a minimum severity: error | warning | info. Default info (everything).",
|
|
23
|
+
"info"
|
|
24
|
+
).option(
|
|
25
|
+
"--format <fmt>",
|
|
26
|
+
"table | json | markdown | sarif. sarif emits SARIF 2.1.0 for GitHub code-scanning / Azure DevOps. Default table.",
|
|
27
|
+
"table"
|
|
28
|
+
).option("-o, --out <path>", "Output file path. Defaults to stdout.").action(
|
|
29
|
+
async (opts) => {
|
|
30
|
+
const sourcePath = String(opts.source);
|
|
31
|
+
const model = await loadModel(sourcePath);
|
|
32
|
+
const report = diagnostics.analyzeProject(model, {
|
|
33
|
+
source: sourcePath,
|
|
34
|
+
...opts.category ? { category: String(opts.category) } : {},
|
|
35
|
+
...opts.minSeverity ? { minSeverity: String(opts.minSeverity) } : {}
|
|
36
|
+
});
|
|
37
|
+
const fmt = String(opts.format ?? "table").toLowerCase();
|
|
38
|
+
let payload;
|
|
39
|
+
if (fmt === "json") {
|
|
40
|
+
payload = JSON.stringify(report, null, 2);
|
|
41
|
+
} else if (fmt === "markdown") {
|
|
42
|
+
payload = renderReportMarkdown(report);
|
|
43
|
+
} else if (fmt === "sarif") {
|
|
44
|
+
payload = JSON.stringify(interop.diagnosticReportToSarif(report), null, 2);
|
|
45
|
+
} else if (fmt === "table") {
|
|
46
|
+
payload = diagnostics.formatReport(report);
|
|
47
|
+
} else {
|
|
48
|
+
throw new Error(`Unknown --format: ${fmt}. Use table | json | markdown | sarif.`);
|
|
49
|
+
}
|
|
50
|
+
if (opts.out) {
|
|
51
|
+
const out = path.resolve(String(opts.out));
|
|
52
|
+
await fs.mkdir(path.dirname(out), { recursive: true });
|
|
53
|
+
await fs.writeFile(out, payload + (payload.endsWith("\n") ? "" : "\n"), "utf8");
|
|
54
|
+
console.error(
|
|
55
|
+
`Wrote ${out} (${payload.length} bytes, ${report.findings.length} finding(s)).`
|
|
56
|
+
);
|
|
57
|
+
} else {
|
|
58
|
+
process.stdout.write(payload + (payload.endsWith("\n") ? "" : "\n"));
|
|
59
|
+
}
|
|
60
|
+
if (report.totals.error > 0) process.exitCode = 1;
|
|
61
|
+
await runExplain({ feature: "diagnose.explain" }, opts, () => buildDiagnosePrompt(report));
|
|
62
|
+
}
|
|
63
|
+
);
|
|
64
|
+
attachExplainFlag(cmd);
|
|
65
|
+
return cmd;
|
|
66
|
+
}
|
|
67
|
+
function buildDiagnosePrompt(report) {
|
|
68
|
+
const lines = [];
|
|
69
|
+
lines.push(`Source: ${report.source || "(unknown)"}`);
|
|
70
|
+
lines.push(
|
|
71
|
+
`Totals: ${report.totals.error} error, ${report.totals.warning} warning, ${report.totals.info} info`
|
|
72
|
+
);
|
|
73
|
+
lines.push("");
|
|
74
|
+
const top = report.findings.slice().sort((a, b) => sevWeight(b.severity) - sevWeight(a.severity)).slice(0, 40);
|
|
75
|
+
lines.push(`Top ${top.length} findings (highest severity first):`);
|
|
76
|
+
for (const d of top) {
|
|
77
|
+
lines.push(`- [${d.severity}] ${d.id} \xB7 ${d.fqn || "(project)"} \u2014 ${d.message}`);
|
|
78
|
+
}
|
|
79
|
+
if (report.findings.length > top.length) {
|
|
80
|
+
lines.push(`(\u2026 ${report.findings.length - top.length} more truncated)`);
|
|
81
|
+
}
|
|
82
|
+
lines.push("");
|
|
83
|
+
lines.push(
|
|
84
|
+
"Explain the dominant themes, what they suggest about the project health, and the highest-leverage fixes the team should tackle first."
|
|
85
|
+
);
|
|
86
|
+
return lines.join("\n");
|
|
87
|
+
}
|
|
88
|
+
function sevWeight(s) {
|
|
89
|
+
return s === "error" ? 3 : s === "warning" ? 2 : 1;
|
|
90
|
+
}
|
|
91
|
+
function renderReportMarkdown(report) {
|
|
92
|
+
const lines = [];
|
|
93
|
+
lines.push(`# Diagnostic report \u2014 \`${report.source || "(no source)"}\``);
|
|
94
|
+
lines.push("");
|
|
95
|
+
lines.push(`Generated: ${report.generatedAt}`);
|
|
96
|
+
lines.push("");
|
|
97
|
+
lines.push(
|
|
98
|
+
`**Totals:** ${report.totals.error} error \xB7 ${report.totals.warning} warning \xB7 ${report.totals.info} info`
|
|
99
|
+
);
|
|
100
|
+
lines.push("");
|
|
101
|
+
lines.push("## By category");
|
|
102
|
+
lines.push("");
|
|
103
|
+
lines.push("| Category | Count |");
|
|
104
|
+
lines.push("|---|---|");
|
|
105
|
+
for (const [k, v] of Object.entries(report.byCategory)) {
|
|
106
|
+
if (v > 0) lines.push(`| ${k} | ${v} |`);
|
|
107
|
+
}
|
|
108
|
+
lines.push("");
|
|
109
|
+
if (report.findings.length === 0) {
|
|
110
|
+
lines.push("_No findings._");
|
|
111
|
+
return lines.join("\n");
|
|
112
|
+
}
|
|
113
|
+
lines.push("## Findings");
|
|
114
|
+
lines.push("");
|
|
115
|
+
for (const d of report.findings) {
|
|
116
|
+
const glyph = d.severity === "error" ? "\u{1F6D1}" : d.severity === "warning" ? "\u26A0\uFE0F" : "\u2139\uFE0F";
|
|
117
|
+
lines.push(`### ${glyph} \`${d.id}\` \u2014 ${d.fqn || "(project)"}`);
|
|
118
|
+
lines.push("");
|
|
119
|
+
lines.push(`**${d.message}**`);
|
|
120
|
+
lines.push("");
|
|
121
|
+
lines.push(`*Why:* ${d.reasoning}`);
|
|
122
|
+
lines.push("");
|
|
123
|
+
lines.push(`*Suggestion:* ${d.suggestion}`);
|
|
124
|
+
lines.push("");
|
|
125
|
+
if (d.related && d.related.length > 0) {
|
|
126
|
+
lines.push(
|
|
127
|
+
`*Related:* ${d.related.slice(0, 5).map((r) => `\`${r}\``).join(", ")}`
|
|
128
|
+
);
|
|
129
|
+
lines.push("");
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return lines.join("\n");
|
|
133
|
+
}
|
|
134
|
+
async function loadModel(sourcePath) {
|
|
135
|
+
if (sourcePath.endsWith(".sdtpac")) {
|
|
136
|
+
const c = await pac.readPac(sourcePath);
|
|
137
|
+
return c.model;
|
|
138
|
+
}
|
|
139
|
+
const loaded = await project.loadProject(sourcePath);
|
|
140
|
+
return await project.parseProjectModel(loaded);
|
|
141
|
+
}
|
|
142
|
+
export {
|
|
143
|
+
diagnoseCommand
|
|
144
|
+
};
|
|
145
|
+
//# sourceMappingURL=diagnose-6IFMELFR.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 { diagnostics, interop, pac, project } from '@sdt-tools/core';\nimport { attachExplainFlag, runExplain } from '../util/ai-explain.js';\n\n/**\n * `sdt 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 clustering, missing PK)\n * and cost smells (warehouse without AUTO_SUSPEND, long Time Travel)\n * into one ranked report. Every finding ships with a **reasoning**\n * line explaining *why* it matters — readers learn the engineering\n * motivation, not just the patch.\n *\n * Use cases:\n * - Pre-PR check: `sdt diagnose --source <proj> --min-severity warning`\n * - Periodic project audit: weekly cron emitting JSON to a dashboard\n * - Onboarding: a new engineer reads the report to learn what good\n * looks like in this codebase\n *\n * Mirrors `ddt 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>', '.sdtproj or .sdtpac 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 // Non-zero exit when at least one error-severity finding fires so\n // `sdt diagnose` can gate a CI step.\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) {\n if (sourcePath.endsWith('.sdtpac')) {\n const c = await pac.readPac(sourcePath);\n return c.model;\n }\n const loaded = await project.loadProject(sourcePath);\n return await project.parseProjectModel(loaded);\n}\n"],"mappings":";;;;;;;;AAAA,SAAS,YAAY,UAAU;AAC/B,OAAO,UAAU;AACjB,SAAS,eAAe;AACxB,SAAS,aAAa,SAAS,KAAK,eAAe;AAoB5C,SAAS,kBAA2B;AACzC,QAAM,MAAM,IAAI,QAAQ,UAAU;AAClC,MACG;AAAA,IACC;AAAA,EACF,EACC,eAAe,mBAAmB,iCAAiC,EACnE;AAAA,IACC;AAAA,IACA;AAAA,EACF,EACC;AAAA,IACC;AAAA,IACA;AAAA,IACA;AAAA,EACF,EACC;AAAA,IACC;AAAA,IACA;AAAA,IACA;AAAA,EACF,EACC,OAAO,oBAAoB,uCAAuC,EAClE;AAAA,IACC,OAAO,SAOD;AACJ,YAAM,aAAa,OAAO,KAAK,MAAM;AACrC,YAAM,QAAQ,MAAM,UAAU,UAAU;AACxC,YAAM,SAAS,YAAY,eAAe,OAAO;AAAA,QAC/C,QAAQ;AAAA,QACR,GAAI,KAAK,WACL,EAAE,UAAU,OAAO,KAAK,QAAQ,EAAoC,IACpE,CAAC;AAAA,QACL,GAAI,KAAK,cACL,EAAE,aAAa,OAAO,KAAK,WAAW,EAAoC,IAC1E,CAAC;AAAA,MACP,CAAC;AAED,YAAM,MAAM,OAAO,KAAK,UAAU,OAAO,EAAE,YAAY;AACvD,UAAI;AACJ,UAAI,QAAQ,QAAQ;AAClB,kBAAU,KAAK,UAAU,QAAQ,MAAM,CAAC;AAAA,MAC1C,WAAW,QAAQ,YAAY;AAC7B,kBAAU,qBAAqB,MAAM;AAAA,MACvC,WAAW,QAAQ,SAAS;AAC1B,kBAAU,KAAK,UAAU,QAAQ,wBAAwB,MAAM,GAAG,MAAM,CAAC;AAAA,MAC3E,WAAW,QAAQ,SAAS;AAC1B,kBAAU,YAAY,aAAa,MAAM;AAAA,MAC3C,OAAO;AACL,cAAM,IAAI,MAAM,qBAAqB,GAAG,wCAAwC;AAAA,MAClF;AAEA,UAAI,KAAK,KAAK;AACZ,cAAM,MAAM,KAAK,QAAQ,OAAO,KAAK,GAAG,CAAC;AACzC,cAAM,GAAG,MAAM,KAAK,QAAQ,GAAG,GAAG,EAAE,WAAW,KAAK,CAAC;AACrD,cAAM,GAAG,UAAU,KAAK,WAAW,QAAQ,SAAS,IAAI,IAAI,KAAK,OAAO,MAAM;AAC9E,gBAAQ;AAAA,UACN,SAAS,GAAG,KAAK,QAAQ,MAAM,WAAW,OAAO,SAAS,MAAM;AAAA,QAClE;AAAA,MACF,OAAO;AACL,gBAAQ,OAAO,MAAM,WAAW,QAAQ,SAAS,IAAI,IAAI,KAAK,KAAK;AAAA,MACrE;AAIA,UAAI,OAAO,OAAO,QAAQ,EAAG,SAAQ,WAAW;AAEhD,YAAM,WAAW,EAAE,SAAS,mBAAmB,GAAG,MAAM,MAAM,oBAAoB,MAAM,CAAC;AAAA,IAC3F;AAAA,EACF;AACF,oBAAkB,GAAG;AACrB,SAAO;AACT;AAEA,SAAS,oBAAoB,QAA8C;AACzE,QAAM,QAAkB,CAAC;AACzB,QAAM,KAAK,WAAW,OAAO,UAAU,WAAW,EAAE;AACpD,QAAM;AAAA,IACJ,WAAW,OAAO,OAAO,KAAK,WAAW,OAAO,OAAO,OAAO,aAAa,OAAO,OAAO,IAAI;AAAA,EAC/F;AACA,QAAM,KAAK,EAAE;AACb,QAAM,MAAM,OAAO,SAChB,MAAM,EACN,KAAK,CAAC,GAAG,MAAM,UAAU,EAAE,QAAQ,IAAI,UAAU,EAAE,QAAQ,CAAC,EAC5D,MAAM,GAAG,EAAE;AACd,QAAM,KAAK,OAAO,IAAI,MAAM,qCAAqC;AACjE,aAAW,KAAK,KAAK;AACnB,UAAM,KAAK,MAAM,EAAE,QAAQ,KAAK,EAAE,EAAE,SAAM,EAAE,OAAO,WAAW,WAAM,EAAE,OAAO,EAAE;AAAA,EACjF;AACA,MAAI,OAAO,SAAS,SAAS,IAAI,QAAQ;AACvC,UAAM,KAAK,WAAM,OAAO,SAAS,SAAS,IAAI,MAAM,kBAAkB;AAAA,EACxE;AACA,QAAM,KAAK,EAAE;AACb,QAAM;AAAA,IACJ;AAAA,EACF;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,UAAU,GAA2C;AAC5D,SAAO,MAAM,UAAU,IAAI,MAAM,YAAY,IAAI;AACnD;AAEA,SAAS,qBAAqB,QAA8C;AAC1E,QAAM,QAAkB,CAAC;AACzB,QAAM,KAAK,gCAA2B,OAAO,UAAU,aAAa,IAAI;AACxE,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,cAAc,OAAO,WAAW,EAAE;AAC7C,QAAM,KAAK,EAAE;AACb,QAAM;AAAA,IACJ,eAAe,OAAO,OAAO,KAAK,eAAY,OAAO,OAAO,OAAO,iBAAc,OAAO,OAAO,IAAI;AAAA,EACrG;AACA,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,gBAAgB;AAC3B,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,sBAAsB;AACjC,QAAM,KAAK,WAAW;AACtB,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,OAAO,UAAU,GAAG;AACtD,QAAI,IAAI,EAAG,OAAM,KAAK,KAAK,CAAC,MAAM,CAAC,IAAI;AAAA,EACzC;AACA,QAAM,KAAK,EAAE;AACb,MAAI,OAAO,SAAS,WAAW,GAAG;AAChC,UAAM,KAAK,gBAAgB;AAC3B,WAAO,MAAM,KAAK,IAAI;AAAA,EACxB;AACA,QAAM,KAAK,aAAa;AACxB,QAAM,KAAK,EAAE;AACb,aAAW,KAAK,OAAO,UAAU;AAC/B,UAAM,QAAQ,EAAE,aAAa,UAAU,cAAO,EAAE,aAAa,YAAY,iBAAO;AAChF,UAAM,KAAK,OAAO,KAAK,MAAM,EAAE,EAAE,aAAQ,EAAE,OAAO,WAAW,EAAE;AAC/D,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,KAAK,EAAE,OAAO,IAAI;AAC7B,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,UAAU,EAAE,SAAS,EAAE;AAClC,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,iBAAiB,EAAE,UAAU,EAAE;AAC1C,UAAM,KAAK,EAAE;AACb,QAAI,EAAE,WAAW,EAAE,QAAQ,SAAS,GAAG;AACrC,YAAM;AAAA,QACJ,cAAc,EAAE,QACb,MAAM,GAAG,CAAC,EACV,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,EACrB,KAAK,IAAI,CAAC;AAAA,MACf;AACA,YAAM,KAAK,EAAE;AAAA,IACf;AAAA,EACF;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,eAAe,UAAU,YAAoB;AAC3C,MAAI,WAAW,SAAS,SAAS,GAAG;AAClC,UAAM,IAAI,MAAM,IAAI,QAAQ,UAAU;AACtC,WAAO,EAAE;AAAA,EACX;AACA,QAAM,SAAS,MAAM,QAAQ,YAAY,UAAU;AACnD,SAAO,MAAM,QAAQ,kBAAkB,MAAM;AAC/C;","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, SDT_HINT_RULES } from "@sdt-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(), ".sdt", "discovery.json"), { force: true });
|
|
28
|
+
} catch {
|
|
29
|
+
}
|
|
30
|
+
console.log(chalk.dim(" Discovery history cleared."));
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const advisor = new FeatureAdvisor("sdt", SDT_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 sdt commands to get personalized suggestions.\n"
|
|
43
|
+
)
|
|
44
|
+
);
|
|
45
|
+
console.log(chalk.dim(" Quick start:"));
|
|
46
|
+
console.log(` ${chalk.cyan("sdt init")} initialize a new project`);
|
|
47
|
+
console.log(` ${chalk.cyan("sdt build")} build a .sdtpac artifact`);
|
|
48
|
+
console.log(` ${chalk.cyan("sdt compare")} compare project vs. live account`);
|
|
49
|
+
console.log("");
|
|
50
|
+
console.log(chalk.dim(" sdt features list \xB7 sdt 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")} sdt ${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(`sdt ${c}`)}`);
|
|
68
|
+
}
|
|
69
|
+
console.log("");
|
|
70
|
+
}
|
|
71
|
+
console.log(chalk.dim(" sdt features list \xB7 sdt explain <topic>\n"));
|
|
72
|
+
});
|
|
73
|
+
return cmd;
|
|
74
|
+
}
|
|
75
|
+
export {
|
|
76
|
+
discoverCommand
|
|
77
|
+
};
|
|
78
|
+
//# sourceMappingURL=discover-A7OSZAHK.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, SDT_HINT_RULES } from '@sdt-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(), '.sdt', '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('sdt', SDT_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 sdt commands to get personalized suggestions.\\n',\n ),\n );\n console.log(chalk.dim(' Quick start:'));\n console.log(` ${chalk.cyan('sdt init')} initialize a new project`);\n console.log(` ${chalk.cyan('sdt build')} build a .sdtpac artifact`);\n console.log(` ${chalk.cyan('sdt compare')} compare project vs. live account`);\n console.log('');\n console.log(chalk.dim(' sdt features list · sdt 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('✔')} sdt ${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(`sdt ${c}`)}`);\n }\n console.log('');\n }\n\n console.log(chalk.dim(' sdt features list · sdt 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,4CAA4C;AACxF,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":[]}
|