@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,23 @@
|
|
|
1
|
+
import {
|
|
2
|
+
logger
|
|
3
|
+
} from "./chunk-VM2H4LAO.js";
|
|
4
|
+
import "./chunk-DGUM43GV.js";
|
|
5
|
+
|
|
6
|
+
// src/commands/build.ts
|
|
7
|
+
import { Command } from "commander";
|
|
8
|
+
import { buildProject } from "@sdt-tools/core/pac";
|
|
9
|
+
function buildCommand() {
|
|
10
|
+
const cmd = new Command("build");
|
|
11
|
+
cmd.description("Build a .sdtpac from a .sdtproj project.").requiredOption("-p, --project <path>", "Path to the .sdtproj file").option("-o, --out <path>", "Output .sdtpac path (default: <projectRoot>/bin/<name>.sdtpac)").action(async (opts) => {
|
|
12
|
+
const result = await buildProject(String(opts.project), {
|
|
13
|
+
outputPath: opts.out ? String(opts.out) : void 0
|
|
14
|
+
});
|
|
15
|
+
logger.success(`Built ${result.outputPath}`);
|
|
16
|
+
logger.dim(` ${result.fileCount} source file(s), ${result.objectCount} object(s) in model`);
|
|
17
|
+
});
|
|
18
|
+
return cmd;
|
|
19
|
+
}
|
|
20
|
+
export {
|
|
21
|
+
buildCommand
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=build-VNIQFKSP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/build.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { buildProject } from '@sdt-tools/core/pac';\nimport { logger } from '../util/logger.js';\n\nexport function buildCommand(): Command {\n const cmd = new Command('build');\n cmd\n .description('Build a .sdtpac from a .sdtproj project.')\n .requiredOption('-p, --project <path>', 'Path to the .sdtproj file')\n .option('-o, --out <path>', 'Output .sdtpac path (default: <projectRoot>/bin/<name>.sdtpac)')\n .action(async (opts) => {\n const result = await buildProject(String(opts.project), {\n outputPath: opts.out ? String(opts.out) : undefined,\n });\n logger.success(`Built ${result.outputPath}`);\n logger.dim(` ${result.fileCount} source file(s), ${result.objectCount} object(s) in model`);\n });\n return cmd;\n}\n"],"mappings":";;;;;;AAAA,SAAS,eAAe;AACxB,SAAS,oBAAoB;AAGtB,SAAS,eAAwB;AACtC,QAAM,MAAM,IAAI,QAAQ,OAAO;AAC/B,MACG,YAAY,0CAA0C,EACtD,eAAe,wBAAwB,2BAA2B,EAClE,OAAO,oBAAoB,gEAAgE,EAC3F,OAAO,OAAO,SAAS;AACtB,UAAM,SAAS,MAAM,aAAa,OAAO,KAAK,OAAO,GAAG;AAAA,MACtD,YAAY,KAAK,MAAM,OAAO,KAAK,GAAG,IAAI;AAAA,IAC5C,CAAC;AACD,WAAO,QAAQ,SAAS,OAAO,UAAU,EAAE;AAC3C,WAAO,IAAI,KAAK,OAAO,SAAS,oBAAoB,OAAO,WAAW,qBAAqB;AAAA,EAC7F,CAAC;AACH,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import {
|
|
2
|
+
logger
|
|
3
|
+
} from "./chunk-VM2H4LAO.js";
|
|
4
|
+
import "./chunk-DGUM43GV.js";
|
|
5
|
+
|
|
6
|
+
// src/commands/catalog.ts
|
|
7
|
+
import { Command } from "commander";
|
|
8
|
+
import { catalog } from "@sdt-tools/core";
|
|
9
|
+
import { getProfile, SnowflakeConnection } from "@sdt-tools/core/connection";
|
|
10
|
+
var BUILTIN_DATABASES = /* @__PURE__ */ new Set(["SNOWFLAKE", "SNOWFLAKE_SAMPLE_DATA", "UTIL_DB"]);
|
|
11
|
+
function catalogCommand() {
|
|
12
|
+
const cmd = new Command("catalog");
|
|
13
|
+
cmd.description(
|
|
14
|
+
"Manage the per-connection catalog cache used by the Object Explorer + EE2 intellisense."
|
|
15
|
+
);
|
|
16
|
+
cmd.command("refresh").description("Open the connection, scan every (non-builtin) database, write the catalog cache.").requiredOption("-c, --connection <name>", "Connection profile name.").option("--root <path>", "Project root. Default cwd.", process.cwd()).option("--databases <csv>", "Comma-separated list. Default: every non-builtin database.").option("--include-builtins", "Include SNOWFLAKE / SNOWFLAKE_SAMPLE_DATA / UTIL_DB.", false).option(
|
|
17
|
+
"--concurrency <n>",
|
|
18
|
+
"Bounded-concurrency cap for the bulk-scan pool. Default 10.",
|
|
19
|
+
"10"
|
|
20
|
+
).option("--no-fingerprint-skip", "Force a full scan even when fingerprints are unchanged.").action(async (opts) => {
|
|
21
|
+
const profile = await getProfile(String(opts.connection));
|
|
22
|
+
const conn = new SnowflakeConnection(profile);
|
|
23
|
+
const cache = new catalog.CatalogCache({
|
|
24
|
+
root: String(opts.root),
|
|
25
|
+
connection: profile.name
|
|
26
|
+
});
|
|
27
|
+
logger.step(`Connecting to ${profile.account} as ${profile.auth.username}\u2026`);
|
|
28
|
+
await conn.connect();
|
|
29
|
+
try {
|
|
30
|
+
const dbs = await resolveDatabases(conn, opts);
|
|
31
|
+
if (dbs.length === 0) {
|
|
32
|
+
logger.warn("No databases to scan. Pass --databases or --include-builtins.");
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
logger.dim(
|
|
36
|
+
`Scanning ${dbs.length} database(s) with concurrency ${Number(opts.concurrency)}.`
|
|
37
|
+
);
|
|
38
|
+
const cached = await cache.get();
|
|
39
|
+
const cachedByDb = new Map(cached.databases.map((d) => [d.database, d]));
|
|
40
|
+
const concurrency = Math.max(1, Number(opts.concurrency) || 10);
|
|
41
|
+
const fingerprintSkip = opts.fingerprintSkip !== false;
|
|
42
|
+
const { results, errors } = await catalog.mapPool(
|
|
43
|
+
dbs,
|
|
44
|
+
async (db) => {
|
|
45
|
+
let freshFingerprint = null;
|
|
46
|
+
try {
|
|
47
|
+
const fpRes = await conn.query(
|
|
48
|
+
catalog.fingerprintSqlForDatabase(db)
|
|
49
|
+
);
|
|
50
|
+
freshFingerprint = catalog.parseFingerprintRow(fpRes.rows);
|
|
51
|
+
} catch (err) {
|
|
52
|
+
logger.warn(
|
|
53
|
+
`fingerprint(${db}) failed; falling through to full scan: ${err.message}`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
const cachedDb = cachedByDb.get(db);
|
|
57
|
+
if (fingerprintSkip && cachedDb && cachedDb.fingerprint != null && freshFingerprint != null && catalog.isFresh(cachedDb.fingerprint, freshFingerprint)) {
|
|
58
|
+
logger.dim(` ${db}: fresh; reusing cache`);
|
|
59
|
+
return { database: db, fingerprint: cachedDb.fingerprint, schemas: cachedDb.schemas };
|
|
60
|
+
}
|
|
61
|
+
const scanRes = await conn.query(catalog.bulkScanSqlForDatabase(db));
|
|
62
|
+
const schemas = catalog.parseBulkScanRows(db, scanRes.rows);
|
|
63
|
+
const fingerprint = freshFingerprint ?? null;
|
|
64
|
+
logger.dim(
|
|
65
|
+
` ${db}: ${schemas.length} schemas / ${schemas.reduce((sum, s) => sum + s.objects.length, 0)} objects`
|
|
66
|
+
);
|
|
67
|
+
return { database: db, fingerprint, schemas };
|
|
68
|
+
},
|
|
69
|
+
{ concurrency }
|
|
70
|
+
);
|
|
71
|
+
for (const e of errors) {
|
|
72
|
+
logger.warn(`scan failed for "${dbs[e.index]}": ${e.error.message}`);
|
|
73
|
+
}
|
|
74
|
+
const snapshot = catalog.refreshFingerprints({
|
|
75
|
+
version: catalog.CATALOG_SNAPSHOT_VERSION,
|
|
76
|
+
connection: profile.name,
|
|
77
|
+
snapshotAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
78
|
+
fingerprint: null,
|
|
79
|
+
databases: results.filter((r) => !!r)
|
|
80
|
+
});
|
|
81
|
+
await cache.set(snapshot);
|
|
82
|
+
logger.success(`Wrote ${snapshot.databases.length} databases to ${cache.path}`);
|
|
83
|
+
} finally {
|
|
84
|
+
await conn.disconnect();
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
cmd.command("show").description("Pretty-print or JSON-dump the cached snapshot for a connection.").requiredOption("-c, --connection <name>", "Connection profile name.").option("--root <path>", "Project root. Default cwd.", process.cwd()).option("--json", "Emit JSON instead of summary.").action(async (opts) => {
|
|
88
|
+
const cache = new catalog.CatalogCache({
|
|
89
|
+
root: String(opts.root),
|
|
90
|
+
connection: String(opts.connection)
|
|
91
|
+
});
|
|
92
|
+
const snapshot = await cache.get();
|
|
93
|
+
if (opts.json) {
|
|
94
|
+
console.log(JSON.stringify(snapshot, null, 2));
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (snapshot.databases.length === 0) {
|
|
98
|
+
logger.warn(`Catalog cache is empty for "${opts.connection}".`);
|
|
99
|
+
logger.dim(` Cache file: ${cache.path}`);
|
|
100
|
+
logger.dim(` Run \`sdt catalog refresh --connection ${opts.connection}\` to populate.`);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
logger.success(`Snapshot for "${snapshot.connection}" \u2014 ${snapshot.snapshotAt}`);
|
|
104
|
+
logger.dim(
|
|
105
|
+
` Top-level fingerprint: ${snapshot.fingerprint != null ? new Date(snapshot.fingerprint).toISOString() : "(none)"}`
|
|
106
|
+
);
|
|
107
|
+
for (const db of snapshot.databases) {
|
|
108
|
+
const objCount = db.schemas.reduce((sum, s) => sum + s.objects.length, 0);
|
|
109
|
+
console.log(
|
|
110
|
+
` ${db.database.padEnd(32)} ${db.schemas.length} schemas / ${objCount} objects`
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
cmd.command("clear").description("Delete the cached snapshot for a connection.").requiredOption("-c, --connection <name>", "Connection profile name.").option("--root <path>", "Project root. Default cwd.", process.cwd()).action(async (opts) => {
|
|
115
|
+
const cache = new catalog.CatalogCache({
|
|
116
|
+
root: String(opts.root),
|
|
117
|
+
connection: String(opts.connection)
|
|
118
|
+
});
|
|
119
|
+
await cache.clear();
|
|
120
|
+
logger.success(`Cleared cache for "${opts.connection}".`);
|
|
121
|
+
logger.dim(` was: ${cache.path}`);
|
|
122
|
+
});
|
|
123
|
+
return cmd;
|
|
124
|
+
}
|
|
125
|
+
async function resolveDatabases(conn, opts) {
|
|
126
|
+
if (opts.databases) {
|
|
127
|
+
return String(opts.databases).split(",").map((s) => s.trim()).filter(Boolean);
|
|
128
|
+
}
|
|
129
|
+
const res = await conn.query("SHOW DATABASES");
|
|
130
|
+
const names = res.rows.map((r) => String(r.name ?? r.NAME ?? "")).filter(Boolean);
|
|
131
|
+
if (opts.includeBuiltins) return names;
|
|
132
|
+
return names.filter((n) => !BUILTIN_DATABASES.has(n.toUpperCase()));
|
|
133
|
+
}
|
|
134
|
+
export {
|
|
135
|
+
catalogCommand
|
|
136
|
+
};
|
|
137
|
+
//# sourceMappingURL=catalog-JLB5VCEV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/catalog.ts"],"sourcesContent":["/**\n * `sdt catalog` — manage the per-connection catalog cache (EE1 substrate).\n *\n * sdt catalog refresh --connection <name> [--databases <csv>] [--concurrency <n>]\n * sdt catalog show --connection <name> [--json]\n * sdt catalog clear --connection <name>\n *\n * `refresh` opens a live Snowflake connection, discovers databases via\n * `SHOW DATABASES`, runs the bulk-scan SQL per database via the bounded\n * promise pool, and writes the assembled snapshot to\n * `<root>/.sdt/cache/<conn>/catalog.msgpack` (RES.1 — was `catalog.json`\n * before 2026-05-16; the cache reader still accepts the legacy form for\n * one major version). Schema fingerprints (`MAX(LAST_ALTERED)`) are\n * checked first; databases that haven't changed since the last refresh\n * are skipped.\n *\n * `show` reads the cache file and pretty-prints (or emits JSON).\n * `clear` deletes the cache file (and its parent dir if empty).\n *\n * Object Explorer (`sdt explorer`) and the eventual EE2 intellisense\n * provider both consume this cache.\n */\nimport { Command } from 'commander';\nimport { catalog } from '@sdt-tools/core';\nimport { getProfile, SnowflakeConnection } from '@sdt-tools/core/connection';\nimport { logger } from '../util/logger.js';\n\n/**\n * Built-in databases that almost no one wants in their cache. Pass\n * `--include-builtins` to keep them.\n */\nconst BUILTIN_DATABASES = new Set(['SNOWFLAKE', 'SNOWFLAKE_SAMPLE_DATA', 'UTIL_DB']);\n\ninterface ShowDatabasesRow {\n name?: string;\n NAME?: string;\n}\n\nexport function catalogCommand(): Command {\n const cmd = new Command('catalog');\n cmd.description(\n 'Manage the per-connection catalog cache used by the Object Explorer + EE2 intellisense.',\n );\n\n cmd\n .command('refresh')\n .description('Open the connection, scan every (non-builtin) database, write the catalog cache.')\n .requiredOption('-c, --connection <name>', 'Connection profile name.')\n .option('--root <path>', 'Project root. Default cwd.', process.cwd())\n .option('--databases <csv>', 'Comma-separated list. Default: every non-builtin database.')\n .option('--include-builtins', 'Include SNOWFLAKE / SNOWFLAKE_SAMPLE_DATA / UTIL_DB.', false)\n .option(\n '--concurrency <n>',\n 'Bounded-concurrency cap for the bulk-scan pool. Default 10.',\n '10',\n )\n .option('--no-fingerprint-skip', 'Force a full scan even when fingerprints are unchanged.')\n .action(async (opts) => {\n const profile = await getProfile(String(opts.connection));\n const conn = new SnowflakeConnection(profile);\n const cache = new catalog.CatalogCache({\n root: String(opts.root),\n connection: profile.name,\n });\n\n logger.step(`Connecting to ${profile.account} as ${profile.auth.username}…`);\n await conn.connect();\n try {\n const dbs = await resolveDatabases(conn, opts);\n if (dbs.length === 0) {\n logger.warn('No databases to scan. Pass --databases or --include-builtins.');\n return;\n }\n logger.dim(\n `Scanning ${dbs.length} database(s) with concurrency ${Number(opts.concurrency)}.`,\n );\n\n const cached = await cache.get();\n const cachedByDb = new Map(cached.databases.map((d) => [d.database, d] as const));\n\n const concurrency = Math.max(1, Number(opts.concurrency) || 10);\n const fingerprintSkip = opts.fingerprintSkip !== false;\n\n const { results, errors } = await catalog.mapPool(\n dbs,\n async (db) => {\n // Cheap fingerprint probe first.\n let freshFingerprint: number | null = null;\n try {\n const fpRes = await conn.query<{ MAX_LAST_ALTERED: unknown }>(\n catalog.fingerprintSqlForDatabase(db),\n );\n freshFingerprint = catalog.parseFingerprintRow(fpRes.rows);\n } catch (err) {\n logger.warn(\n `fingerprint(${db}) failed; falling through to full scan: ${(err as Error).message}`,\n );\n }\n\n const cachedDb = cachedByDb.get(db);\n if (\n fingerprintSkip &&\n cachedDb &&\n cachedDb.fingerprint != null &&\n freshFingerprint != null &&\n catalog.isFresh(cachedDb.fingerprint, freshFingerprint)\n ) {\n logger.dim(` ${db}: fresh; reusing cache`);\n return { database: db, fingerprint: cachedDb.fingerprint, schemas: cachedDb.schemas };\n }\n\n const scanRes = await conn.query(catalog.bulkScanSqlForDatabase(db));\n const schemas = catalog.parseBulkScanRows(db, scanRes.rows);\n const fingerprint = freshFingerprint ?? null;\n logger.dim(\n ` ${db}: ${schemas.length} schemas / ${schemas.reduce((sum, s) => sum + s.objects.length, 0)} objects`,\n );\n return { database: db, fingerprint, schemas };\n },\n { concurrency },\n );\n\n for (const e of errors) {\n logger.warn(`scan failed for \"${dbs[e.index]}\": ${(e.error as Error).message}`);\n }\n\n const snapshot = catalog.refreshFingerprints({\n version: catalog.CATALOG_SNAPSHOT_VERSION,\n connection: profile.name,\n snapshotAt: new Date().toISOString(),\n fingerprint: null,\n databases: results.filter((r): r is NonNullable<typeof r> => !!r),\n });\n await cache.set(snapshot);\n\n logger.success(`Wrote ${snapshot.databases.length} databases to ${cache.path}`);\n } finally {\n await conn.disconnect();\n }\n });\n\n cmd\n .command('show')\n .description('Pretty-print or JSON-dump the cached snapshot for a connection.')\n .requiredOption('-c, --connection <name>', 'Connection profile name.')\n .option('--root <path>', 'Project root. Default cwd.', process.cwd())\n .option('--json', 'Emit JSON instead of summary.')\n .action(async (opts) => {\n const cache = new catalog.CatalogCache({\n root: String(opts.root),\n connection: String(opts.connection),\n });\n const snapshot = await cache.get();\n if (opts.json) {\n console.log(JSON.stringify(snapshot, null, 2));\n return;\n }\n if (snapshot.databases.length === 0) {\n logger.warn(`Catalog cache is empty for \"${opts.connection}\".`);\n logger.dim(` Cache file: ${cache.path}`);\n logger.dim(` Run \\`sdt catalog refresh --connection ${opts.connection}\\` to populate.`);\n return;\n }\n logger.success(`Snapshot for \"${snapshot.connection}\" — ${snapshot.snapshotAt}`);\n logger.dim(\n ` Top-level fingerprint: ${snapshot.fingerprint != null ? new Date(snapshot.fingerprint).toISOString() : '(none)'}`,\n );\n for (const db of snapshot.databases) {\n const objCount = db.schemas.reduce((sum, s) => sum + s.objects.length, 0);\n console.log(\n ` ${db.database.padEnd(32)} ${db.schemas.length} schemas / ${objCount} objects`,\n );\n }\n });\n\n cmd\n .command('clear')\n .description('Delete the cached snapshot for a connection.')\n .requiredOption('-c, --connection <name>', 'Connection profile name.')\n .option('--root <path>', 'Project root. Default cwd.', process.cwd())\n .action(async (opts) => {\n const cache = new catalog.CatalogCache({\n root: String(opts.root),\n connection: String(opts.connection),\n });\n await cache.clear();\n logger.success(`Cleared cache for \"${opts.connection}\".`);\n logger.dim(` was: ${cache.path}`);\n });\n\n return cmd;\n}\n\nasync function resolveDatabases(\n conn: SnowflakeConnection,\n opts: { databases?: unknown; includeBuiltins?: unknown },\n): Promise<string[]> {\n if (opts.databases) {\n return String(opts.databases)\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean);\n }\n const res = await conn.query<ShowDatabasesRow>('SHOW DATABASES');\n const names = res.rows.map((r) => String(r.name ?? r.NAME ?? '')).filter(Boolean);\n if (opts.includeBuiltins) return names;\n return names.filter((n) => !BUILTIN_DATABASES.has(n.toUpperCase()));\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import "./chunk-DGUM43GV.js";
|
|
2
|
+
|
|
3
|
+
// src/commands/changelog.ts
|
|
4
|
+
import { promises as fs } from "fs";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import { execFile } from "child_process";
|
|
7
|
+
import { promisify } from "util";
|
|
8
|
+
import { Command } from "commander";
|
|
9
|
+
var execFileP = promisify(execFile);
|
|
10
|
+
var CATEGORY_ORDER = [
|
|
11
|
+
{ type: "feat", heading: "\u2728 Features" },
|
|
12
|
+
{ type: "fix", heading: "\u{1F41B} Bug fixes" },
|
|
13
|
+
{ type: "perf", heading: "\u26A1 Performance" },
|
|
14
|
+
{ type: "refactor", heading: "\u267B\uFE0F Refactoring" },
|
|
15
|
+
{ type: "docs", heading: "\u{1F4DA} Documentation" },
|
|
16
|
+
{ type: "test", heading: "\u{1F9EA} Tests" },
|
|
17
|
+
{ type: "build", heading: "\u{1F527} Build / CI" },
|
|
18
|
+
{ type: "ci", heading: "\u{1F527} Build / CI" },
|
|
19
|
+
{ type: "chore", heading: "\u{1F9F9} Chores" },
|
|
20
|
+
{ type: "style", heading: "\u{1F3A8} Style" }
|
|
21
|
+
];
|
|
22
|
+
function changelogCommand() {
|
|
23
|
+
const cmd = new Command("changelog");
|
|
24
|
+
cmd.description(
|
|
25
|
+
"Generate a Markdown CHANGELOG section from git commits between two refs (Conventional Commits parser)."
|
|
26
|
+
).option("--from <ref>", "Starting git ref (exclusive). Default: previous tag.", "").option("--to <ref>", "Ending git ref (inclusive). Default: HEAD.", "HEAD").option("--repo <path>", "Path to the git repository. Default: current directory.", ".").option("--version <name>", "Version heading for the section. Default: --to ref.").option(
|
|
27
|
+
"--scope <pathspec>",
|
|
28
|
+
"Restrict to commits touching paths matching this git-pathspec (e.g. `Snowflake/packages/cli/**`). Default: all paths."
|
|
29
|
+
).option("-o, --out <path>", "Append/write to file. Default: stdout.").option(
|
|
30
|
+
"--append",
|
|
31
|
+
"When --out is set, prepend to existing file (typical changelog behavior).",
|
|
32
|
+
false
|
|
33
|
+
).option(
|
|
34
|
+
"--manifest-dir <path>",
|
|
35
|
+
'Directory containing deploy manifests (default: .sdt/history). When set, the changelog appends a "\u{1F680} Deploys" addendum: one row per manifest with step-status counts + safety summary. Manifest reads are best-effort \u2014 corrupt files are skipped silently.'
|
|
36
|
+
).action(async (opts) => {
|
|
37
|
+
const repo = path.resolve(String(opts.repo));
|
|
38
|
+
const to = String(opts.to);
|
|
39
|
+
let from = String(opts.from);
|
|
40
|
+
if (!from) {
|
|
41
|
+
from = await previousTag(repo, to);
|
|
42
|
+
}
|
|
43
|
+
const commits = await collectCommits(
|
|
44
|
+
repo,
|
|
45
|
+
from,
|
|
46
|
+
to,
|
|
47
|
+
opts.scope ? String(opts.scope) : void 0
|
|
48
|
+
);
|
|
49
|
+
const grouped = groupByType(commits);
|
|
50
|
+
const version = String(opts.version ?? to);
|
|
51
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
52
|
+
const manifestDir = opts.manifestDir ? path.resolve(String(opts.manifestDir)) : path.join(repo, ".sdt", "history");
|
|
53
|
+
const deploys = await readDeploySummaries(manifestDir);
|
|
54
|
+
const md = renderMarkdown(version, today, from, to, grouped, deploys);
|
|
55
|
+
if (opts.out) {
|
|
56
|
+
const out = path.resolve(String(opts.out));
|
|
57
|
+
let body = md;
|
|
58
|
+
if (opts.append) {
|
|
59
|
+
try {
|
|
60
|
+
const existing = await fs.readFile(out, "utf8");
|
|
61
|
+
body = `${md}
|
|
62
|
+
${existing}`;
|
|
63
|
+
} catch {
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
await fs.mkdir(path.dirname(out), { recursive: true });
|
|
67
|
+
await fs.writeFile(out, body, "utf8");
|
|
68
|
+
console.error(`Wrote ${out} (${commits.length} commit(s)).`);
|
|
69
|
+
} else {
|
|
70
|
+
process.stdout.write(md);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
return cmd;
|
|
74
|
+
}
|
|
75
|
+
async function previousTag(repo, ref) {
|
|
76
|
+
try {
|
|
77
|
+
const { stdout } = await execFileP("git", ["describe", "--tags", "--abbrev=0", `${ref}^`], {
|
|
78
|
+
cwd: repo
|
|
79
|
+
});
|
|
80
|
+
return stdout.trim();
|
|
81
|
+
} catch {
|
|
82
|
+
const { stdout } = await execFileP("git", ["rev-list", "--max-parents=0", "HEAD"], {
|
|
83
|
+
cwd: repo
|
|
84
|
+
});
|
|
85
|
+
return stdout.trim().split("\n")[0] ?? "";
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async function collectCommits(repo, from, to, scope) {
|
|
89
|
+
const range = from ? `${from}..${to}` : to;
|
|
90
|
+
const args = ["log", "--no-merges", "--pretty=format:%H%x01%s", range];
|
|
91
|
+
if (scope) {
|
|
92
|
+
args.push("--", scope);
|
|
93
|
+
}
|
|
94
|
+
const { stdout } = await execFileP("git", args, {
|
|
95
|
+
cwd: repo,
|
|
96
|
+
maxBuffer: 64 * 1024 * 1024
|
|
97
|
+
});
|
|
98
|
+
const lines = stdout.split("\n").filter(Boolean);
|
|
99
|
+
return lines.map((line) => {
|
|
100
|
+
const [sha, subject] = line.split("");
|
|
101
|
+
return parseCommit(sha, subject);
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
function parseCommit(sha, subject) {
|
|
105
|
+
const m = subject.match(/^([a-z]+)(\(([^)]+)\))?(!)?:\s*(.+)$/i);
|
|
106
|
+
if (!m) {
|
|
107
|
+
return { sha, type: "chore", scope: void 0, subject, isBreaking: false };
|
|
108
|
+
}
|
|
109
|
+
const type = m[1].toLowerCase();
|
|
110
|
+
const scope = m[3];
|
|
111
|
+
const isBreaking = !!m[4];
|
|
112
|
+
const text = m[5].trim();
|
|
113
|
+
return { sha, type, scope, subject: text, isBreaking };
|
|
114
|
+
}
|
|
115
|
+
function groupByType(commits) {
|
|
116
|
+
const out = /* @__PURE__ */ new Map();
|
|
117
|
+
for (const c of commits) {
|
|
118
|
+
const canonical = CATEGORY_ORDER.find((cat) => cat.type === c.type)?.type ?? "chore";
|
|
119
|
+
const arr = out.get(canonical) ?? [];
|
|
120
|
+
arr.push(c);
|
|
121
|
+
out.set(canonical, arr);
|
|
122
|
+
}
|
|
123
|
+
return out;
|
|
124
|
+
}
|
|
125
|
+
function renderMarkdown(version, date, from, to, grouped, deploys) {
|
|
126
|
+
const lines = [];
|
|
127
|
+
lines.push(`## ${version} \u2014 ${date}`);
|
|
128
|
+
lines.push("");
|
|
129
|
+
lines.push(`_Range: \`${from || "(initial)"}\`..\`${to}\`_`);
|
|
130
|
+
lines.push("");
|
|
131
|
+
const breaking = [...grouped.values()].flat().filter((c) => c.isBreaking);
|
|
132
|
+
if (breaking.length > 0) {
|
|
133
|
+
lines.push("### \u26A0\uFE0F BREAKING CHANGES");
|
|
134
|
+
lines.push("");
|
|
135
|
+
for (const c of breaking) {
|
|
136
|
+
lines.push(`- ${formatCommitLine(c)}`);
|
|
137
|
+
}
|
|
138
|
+
lines.push("");
|
|
139
|
+
}
|
|
140
|
+
const headingsSeen = /* @__PURE__ */ new Set();
|
|
141
|
+
for (const { heading } of CATEGORY_ORDER) {
|
|
142
|
+
if (headingsSeen.has(heading)) continue;
|
|
143
|
+
headingsSeen.add(heading);
|
|
144
|
+
const types = CATEGORY_ORDER.filter((c) => c.heading === heading).map((c) => c.type);
|
|
145
|
+
const commits = types.flatMap((t) => grouped.get(t) ?? []);
|
|
146
|
+
if (commits.length === 0) continue;
|
|
147
|
+
lines.push(`### ${heading}`);
|
|
148
|
+
lines.push("");
|
|
149
|
+
for (const c of commits) {
|
|
150
|
+
if (c.isBreaking) continue;
|
|
151
|
+
lines.push(`- ${formatCommitLine(c)}`);
|
|
152
|
+
}
|
|
153
|
+
lines.push("");
|
|
154
|
+
}
|
|
155
|
+
if (deploys.length > 0) {
|
|
156
|
+
lines.push("### \u{1F680} Deploys");
|
|
157
|
+
lines.push("");
|
|
158
|
+
for (const d of deploys) {
|
|
159
|
+
lines.push(`- ${formatDeploySummaryLine(d)}`);
|
|
160
|
+
}
|
|
161
|
+
lines.push("");
|
|
162
|
+
}
|
|
163
|
+
return lines.join("\n");
|
|
164
|
+
}
|
|
165
|
+
function formatCommitLine(c) {
|
|
166
|
+
const scope = c.scope ? `**${c.scope}**: ` : "";
|
|
167
|
+
return `${scope}${c.subject} (\`${c.sha.slice(0, 7)}\`)`;
|
|
168
|
+
}
|
|
169
|
+
async function readDeploySummaries(dir) {
|
|
170
|
+
let entries;
|
|
171
|
+
try {
|
|
172
|
+
entries = await fs.readdir(dir);
|
|
173
|
+
} catch {
|
|
174
|
+
return [];
|
|
175
|
+
}
|
|
176
|
+
const out = [];
|
|
177
|
+
for (const name of entries.sort()) {
|
|
178
|
+
if (!name.endsWith(".json")) continue;
|
|
179
|
+
try {
|
|
180
|
+
const body = await fs.readFile(path.join(dir, name), "utf8");
|
|
181
|
+
const parsed = JSON.parse(body);
|
|
182
|
+
if (parsed.version !== 1) continue;
|
|
183
|
+
const steps = Array.isArray(parsed.steps) ? parsed.steps : [];
|
|
184
|
+
const stepCounts = {};
|
|
185
|
+
for (const s of steps) {
|
|
186
|
+
const status = typeof s.status === "string" ? s.status : "unknown";
|
|
187
|
+
stepCounts[status] = (stepCounts[status] ?? 0) + 1;
|
|
188
|
+
}
|
|
189
|
+
out.push({
|
|
190
|
+
filename: name,
|
|
191
|
+
...typeof parsed.deployedAt === "string" ? { deployedAt: parsed.deployedAt } : {},
|
|
192
|
+
...typeof parsed.account === "string" ? { account: parsed.account } : {},
|
|
193
|
+
...typeof parsed.finalState === "string" ? { finalState: parsed.finalState } : {},
|
|
194
|
+
failedStepId: parsed.failedStepId ?? null,
|
|
195
|
+
stepCounts,
|
|
196
|
+
totalSteps: steps.length
|
|
197
|
+
});
|
|
198
|
+
} catch {
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return out;
|
|
202
|
+
}
|
|
203
|
+
function formatDeploySummaryLine(d) {
|
|
204
|
+
const parts = [];
|
|
205
|
+
parts.push(`\`${d.filename}\``);
|
|
206
|
+
if (d.deployedAt) parts.push(d.deployedAt);
|
|
207
|
+
if (d.account) parts.push(d.account);
|
|
208
|
+
const stateGlyph = d.finalState === "clean-success" ? "\u2705" : d.finalState === "clean-skipped" ? "\u23ED\uFE0F" : d.finalState === "dirty" ? "\u{1F6D1}" : d.finalState === "clean-partial" ? "\u26A0\uFE0F" : "\xB7";
|
|
209
|
+
parts.push(`${stateGlyph} ${d.finalState ?? "unknown"}`);
|
|
210
|
+
parts.push(`${d.totalSteps} step(s)`);
|
|
211
|
+
const statusTags = Object.entries(d.stepCounts).sort(([a], [b]) => a.localeCompare(b)).map(([s, n]) => `${s}:${n}`).join(", ");
|
|
212
|
+
if (statusTags) parts.push(`(${statusTags})`);
|
|
213
|
+
if (d.failedStepId) parts.push(`failed: ${d.failedStepId}`);
|
|
214
|
+
return parts.join(" \xB7 ");
|
|
215
|
+
}
|
|
216
|
+
export {
|
|
217
|
+
changelogCommand,
|
|
218
|
+
parseCommit
|
|
219
|
+
};
|
|
220
|
+
//# sourceMappingURL=changelog-M7XGDYSY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/changelog.ts"],"sourcesContent":["/**\n * `sdt changelog --from <ref> --to <ref>` — auto-generate a\n * Markdown CHANGELOG section from git commits between two refs.\n *\n * Parses Conventional Commits (`feat:`, `fix:`, `chore:`, `docs:`,\n * `refactor:`, `perf:`, `test:`, `build:`, `style:`, `ci:`),\n * groups commits by category, and emits a release section.\n * Deterministic — no AI dependency. Mirrors `ddt changelog`.\n *\n * Use case: release prep. Cut a tag, run\n * `sdt changelog --from v0.4.0 --to v0.5.0 -o CHANGELOG.md`,\n * commit the updated CHANGELOG with the tag.\n */\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { Command } from 'commander';\n\nconst execFileP = promisify(execFile);\n\ninterface Commit {\n sha: string;\n type: string;\n scope: string | undefined;\n subject: string;\n isBreaking: boolean;\n}\n\nconst CATEGORY_ORDER: Array<{ type: string; heading: string }> = [\n { type: 'feat', heading: '✨ Features' },\n { type: 'fix', heading: '🐛 Bug fixes' },\n { type: 'perf', heading: '⚡ Performance' },\n { type: 'refactor', heading: '♻️ Refactoring' },\n { type: 'docs', heading: '📚 Documentation' },\n { type: 'test', heading: '🧪 Tests' },\n { type: 'build', heading: '🔧 Build / CI' },\n { type: 'ci', heading: '🔧 Build / CI' },\n { type: 'chore', heading: '🧹 Chores' },\n { type: 'style', heading: '🎨 Style' },\n];\n\nexport function changelogCommand(): Command {\n const cmd = new Command('changelog');\n cmd\n .description(\n 'Generate a Markdown CHANGELOG section from git commits between two refs (Conventional Commits parser).',\n )\n .option('--from <ref>', 'Starting git ref (exclusive). Default: previous tag.', '')\n .option('--to <ref>', 'Ending git ref (inclusive). Default: HEAD.', 'HEAD')\n .option('--repo <path>', 'Path to the git repository. Default: current directory.', '.')\n .option('--version <name>', 'Version heading for the section. Default: --to ref.')\n .option(\n '--scope <pathspec>',\n 'Restrict to commits touching paths matching this git-pathspec (e.g. `Snowflake/packages/cli/**`). Default: all paths.',\n )\n .option('-o, --out <path>', 'Append/write to file. Default: stdout.')\n .option(\n '--append',\n 'When --out is set, prepend to existing file (typical changelog behavior).',\n false,\n )\n .option(\n '--manifest-dir <path>',\n 'Directory containing deploy manifests (default: .sdt/history). When set, the changelog appends a \"🚀 Deploys\" addendum: one row per manifest with step-status counts + safety summary. Manifest reads are best-effort — corrupt files are skipped silently.',\n )\n .action(async (opts) => {\n const repo = path.resolve(String(opts.repo));\n const to = String(opts.to);\n let from = String(opts.from);\n if (!from) {\n from = await previousTag(repo, to);\n }\n\n const commits = await collectCommits(\n repo,\n from,\n to,\n opts.scope ? String(opts.scope) : undefined,\n );\n const grouped = groupByType(commits);\n const version = String(opts.version ?? to);\n const today = new Date().toISOString().slice(0, 10);\n // --manifest-dir: read deploy manifests, build a per-deploy\n // addendum that surfaces step status + safety counts. Default\n // dir matches `<tool> publish --apply --manifest` convention.\n const manifestDir = opts.manifestDir\n ? path.resolve(String(opts.manifestDir))\n : path.join(repo, '.sdt', 'history');\n const deploys = await readDeploySummaries(manifestDir);\n const md = renderMarkdown(version, today, from, to, grouped, deploys);\n\n if (opts.out) {\n const out = path.resolve(String(opts.out));\n let body = md;\n if (opts.append) {\n try {\n const existing = await fs.readFile(out, 'utf8');\n body = `${md}\\n${existing}`;\n } catch {\n // file doesn't exist — write fresh.\n }\n }\n await fs.mkdir(path.dirname(out), { recursive: true });\n await fs.writeFile(out, body, 'utf8');\n console.error(`Wrote ${out} (${commits.length} commit(s)).`);\n } else {\n process.stdout.write(md);\n }\n });\n return cmd;\n}\n\nasync function previousTag(repo: string, ref: string): Promise<string> {\n try {\n const { stdout } = await execFileP('git', ['describe', '--tags', '--abbrev=0', `${ref}^`], {\n cwd: repo,\n });\n return stdout.trim();\n } catch {\n // No previous tag — fall back to the repo's root commit.\n const { stdout } = await execFileP('git', ['rev-list', '--max-parents=0', 'HEAD'], {\n cwd: repo,\n });\n return stdout.trim().split('\\n')[0] ?? '';\n }\n}\n\nasync function collectCommits(\n repo: string,\n from: string,\n to: string,\n scope?: string,\n): Promise<Commit[]> {\n const range = from ? `${from}..${to}` : to;\n const args = ['log', '--no-merges', '--pretty=format:%H%x01%s', range];\n if (scope) {\n args.push('--', scope);\n }\n const { stdout } = await execFileP('git', args, {\n cwd: repo,\n maxBuffer: 64 * 1024 * 1024,\n });\n const lines = stdout.split('\\n').filter(Boolean);\n return lines.map((line) => {\n const [sha, subject] = line.split('\\x01');\n return parseCommit(sha!, subject!);\n });\n}\n\nexport function parseCommit(sha: string, subject: string): Commit {\n // Conventional Commits: <type>(<scope>)?: <subject>\n // Breaking-change marker is a trailing `!` before the colon.\n const m = subject.match(/^([a-z]+)(\\(([^)]+)\\))?(!)?:\\s*(.+)$/i);\n if (!m) {\n return { sha, type: 'chore', scope: undefined, subject, isBreaking: false };\n }\n const type = m[1]!.toLowerCase();\n const scope = m[3];\n const isBreaking = !!m[4];\n const text = m[5]!.trim();\n return { sha, type, scope, subject: text, isBreaking };\n}\n\nfunction groupByType(commits: Commit[]): Map<string, Commit[]> {\n const out = new Map<string, Commit[]>();\n for (const c of commits) {\n // Map both 'ci' and 'build' to the same bucket via heading lookup.\n const canonical = CATEGORY_ORDER.find((cat) => cat.type === c.type)?.type ?? 'chore';\n const arr = out.get(canonical) ?? [];\n arr.push(c);\n out.set(canonical, arr);\n }\n return out;\n}\n\nfunction renderMarkdown(\n version: string,\n date: string,\n from: string,\n to: string,\n grouped: Map<string, Commit[]>,\n deploys: DeploySummary[],\n): string {\n const lines: string[] = [];\n lines.push(`## ${version} — ${date}`);\n lines.push('');\n lines.push(`_Range: \\`${from || '(initial)'}\\`..\\`${to}\\`_`);\n lines.push('');\n\n const breaking = [...grouped.values()].flat().filter((c) => c.isBreaking);\n if (breaking.length > 0) {\n lines.push('### ⚠️ BREAKING CHANGES');\n lines.push('');\n for (const c of breaking) {\n lines.push(`- ${formatCommitLine(c)}`);\n }\n lines.push('');\n }\n\n const headingsSeen = new Set<string>();\n for (const { heading } of CATEGORY_ORDER) {\n if (headingsSeen.has(heading)) continue;\n headingsSeen.add(heading);\n const types = CATEGORY_ORDER.filter((c) => c.heading === heading).map((c) => c.type);\n const commits = types.flatMap((t) => grouped.get(t) ?? []);\n if (commits.length === 0) continue;\n lines.push(`### ${heading}`);\n lines.push('');\n for (const c of commits) {\n if (c.isBreaking) continue; // Already listed under BREAKING.\n lines.push(`- ${formatCommitLine(c)}`);\n }\n lines.push('');\n }\n\n if (deploys.length > 0) {\n lines.push('### 🚀 Deploys');\n lines.push('');\n for (const d of deploys) {\n lines.push(`- ${formatDeploySummaryLine(d)}`);\n }\n lines.push('');\n }\n return lines.join('\\n');\n}\n\nfunction formatCommitLine(c: Commit): string {\n const scope = c.scope ? `**${c.scope}**: ` : '';\n return `${scope}${c.subject} (\\`${c.sha.slice(0, 7)}\\`)`;\n}\n\n/** One deploy-manifest summary row for the changelog's 🚀 addendum. */\nexport interface DeploySummary {\n /** Manifest filename (no path), useful as the link target in markdown. */\n filename: string;\n /** ISO timestamp the deploy finished, when present in the manifest. */\n deployedAt?: string;\n /** Snowflake account or Databricks workspace host. */\n account?: string;\n /** clean-success / clean-partial / dirty / clean-skipped. */\n finalState?: string;\n /** Step id of the first failure (when finalState === 'dirty'). */\n failedStepId?: string | null;\n /** Per-status counts across all steps. */\n stepCounts: Record<string, number>;\n /** Total step count. */\n totalSteps: number;\n}\n\n/** Read every `<dir>/*.json` deploy manifest into a summary row. Best-effort. */\nasync function readDeploySummaries(dir: string): Promise<DeploySummary[]> {\n let entries: string[];\n try {\n entries = await fs.readdir(dir);\n } catch {\n return [];\n }\n const out: DeploySummary[] = [];\n for (const name of entries.sort()) {\n if (!name.endsWith('.json')) continue;\n try {\n const body = await fs.readFile(path.join(dir, name), 'utf8');\n const parsed = JSON.parse(body) as {\n version?: number;\n deployedAt?: string;\n account?: string;\n finalState?: string;\n failedStepId?: string | null;\n steps?: Array<{ status?: string }>;\n };\n if (parsed.version !== 1) continue;\n const steps = Array.isArray(parsed.steps) ? parsed.steps : [];\n const stepCounts: Record<string, number> = {};\n for (const s of steps) {\n const status = typeof s.status === 'string' ? s.status : 'unknown';\n stepCounts[status] = (stepCounts[status] ?? 0) + 1;\n }\n out.push({\n filename: name,\n ...(typeof parsed.deployedAt === 'string' ? { deployedAt: parsed.deployedAt } : {}),\n ...(typeof parsed.account === 'string' ? { account: parsed.account } : {}),\n ...(typeof parsed.finalState === 'string' ? { finalState: parsed.finalState } : {}),\n failedStepId: parsed.failedStepId ?? null,\n stepCounts,\n totalSteps: steps.length,\n });\n } catch {\n // Corrupt or missing — skip. Deploy manifests are append-only;\n // a half-written file is the rare exception.\n }\n }\n return out;\n}\n\n/** Render one DeploySummary as a Markdown bullet. */\nfunction formatDeploySummaryLine(d: DeploySummary): string {\n const parts: string[] = [];\n parts.push(`\\`${d.filename}\\``);\n if (d.deployedAt) parts.push(d.deployedAt);\n if (d.account) parts.push(d.account);\n const stateGlyph =\n d.finalState === 'clean-success'\n ? '✅'\n : d.finalState === 'clean-skipped'\n ? '⏭️'\n : d.finalState === 'dirty'\n ? '🛑'\n : d.finalState === 'clean-partial'\n ? '⚠️'\n : '·';\n parts.push(`${stateGlyph} ${d.finalState ?? 'unknown'}`);\n parts.push(`${d.totalSteps} step(s)`);\n const statusTags = Object.entries(d.stepCounts)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([s, n]) => `${s}:${n}`)\n .join(', ');\n if (statusTags) parts.push(`(${statusTags})`);\n if (d.failedStepId) parts.push(`failed: ${d.failedStepId}`);\n return parts.join(' · ');\n}\n"],"mappings":";;;AAaA,SAAS,YAAY,UAAU;AAC/B,OAAO,UAAU;AACjB,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAC1B,SAAS,eAAe;AAExB,IAAM,YAAY,UAAU,QAAQ;AAUpC,IAAM,iBAA2D;AAAA,EAC/D,EAAE,MAAM,QAAQ,SAAS,kBAAa;AAAA,EACtC,EAAE,MAAM,OAAO,SAAS,sBAAe;AAAA,EACvC,EAAE,MAAM,QAAQ,SAAS,qBAAgB;AAAA,EACzC,EAAE,MAAM,YAAY,SAAS,2BAAiB;AAAA,EAC9C,EAAE,MAAM,QAAQ,SAAS,0BAAmB;AAAA,EAC5C,EAAE,MAAM,QAAQ,SAAS,kBAAW;AAAA,EACpC,EAAE,MAAM,SAAS,SAAS,uBAAgB;AAAA,EAC1C,EAAE,MAAM,MAAM,SAAS,uBAAgB;AAAA,EACvC,EAAE,MAAM,SAAS,SAAS,mBAAY;AAAA,EACtC,EAAE,MAAM,SAAS,SAAS,kBAAW;AACvC;AAEO,SAAS,mBAA4B;AAC1C,QAAM,MAAM,IAAI,QAAQ,WAAW;AACnC,MACG;AAAA,IACC;AAAA,EACF,EACC,OAAO,gBAAgB,wDAAwD,EAAE,EACjF,OAAO,cAAc,8CAA8C,MAAM,EACzE,OAAO,iBAAiB,2DAA2D,GAAG,EACtF,OAAO,oBAAoB,qDAAqD,EAChF;AAAA,IACC;AAAA,IACA;AAAA,EACF,EACC,OAAO,oBAAoB,wCAAwC,EACnE;AAAA,IACC;AAAA,IACA;AAAA,IACA;AAAA,EACF,EACC;AAAA,IACC;AAAA,IACA;AAAA,EACF,EACC,OAAO,OAAO,SAAS;AACtB,UAAM,OAAO,KAAK,QAAQ,OAAO,KAAK,IAAI,CAAC;AAC3C,UAAM,KAAK,OAAO,KAAK,EAAE;AACzB,QAAI,OAAO,OAAO,KAAK,IAAI;AAC3B,QAAI,CAAC,MAAM;AACT,aAAO,MAAM,YAAY,MAAM,EAAE;AAAA,IACnC;AAEA,UAAM,UAAU,MAAM;AAAA,MACpB;AAAA,MACA;AAAA,MACA;AAAA,MACA,KAAK,QAAQ,OAAO,KAAK,KAAK,IAAI;AAAA,IACpC;AACA,UAAM,UAAU,YAAY,OAAO;AACnC,UAAM,UAAU,OAAO,KAAK,WAAW,EAAE;AACzC,UAAM,SAAQ,oBAAI,KAAK,GAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AAIlD,UAAM,cAAc,KAAK,cACrB,KAAK,QAAQ,OAAO,KAAK,WAAW,CAAC,IACrC,KAAK,KAAK,MAAM,QAAQ,SAAS;AACrC,UAAM,UAAU,MAAM,oBAAoB,WAAW;AACrD,UAAM,KAAK,eAAe,SAAS,OAAO,MAAM,IAAI,SAAS,OAAO;AAEpE,QAAI,KAAK,KAAK;AACZ,YAAM,MAAM,KAAK,QAAQ,OAAO,KAAK,GAAG,CAAC;AACzC,UAAI,OAAO;AACX,UAAI,KAAK,QAAQ;AACf,YAAI;AACF,gBAAM,WAAW,MAAM,GAAG,SAAS,KAAK,MAAM;AAC9C,iBAAO,GAAG,EAAE;AAAA,EAAK,QAAQ;AAAA,QAC3B,QAAQ;AAAA,QAER;AAAA,MACF;AACA,YAAM,GAAG,MAAM,KAAK,QAAQ,GAAG,GAAG,EAAE,WAAW,KAAK,CAAC;AACrD,YAAM,GAAG,UAAU,KAAK,MAAM,MAAM;AACpC,cAAQ,MAAM,SAAS,GAAG,KAAK,QAAQ,MAAM,cAAc;AAAA,IAC7D,OAAO;AACL,cAAQ,OAAO,MAAM,EAAE;AAAA,IACzB;AAAA,EACF,CAAC;AACH,SAAO;AACT;AAEA,eAAe,YAAY,MAAc,KAA8B;AACrE,MAAI;AACF,UAAM,EAAE,OAAO,IAAI,MAAM,UAAU,OAAO,CAAC,YAAY,UAAU,cAAc,GAAG,GAAG,GAAG,GAAG;AAAA,MACzF,KAAK;AAAA,IACP,CAAC;AACD,WAAO,OAAO,KAAK;AAAA,EACrB,QAAQ;AAEN,UAAM,EAAE,OAAO,IAAI,MAAM,UAAU,OAAO,CAAC,YAAY,mBAAmB,MAAM,GAAG;AAAA,MACjF,KAAK;AAAA,IACP,CAAC;AACD,WAAO,OAAO,KAAK,EAAE,MAAM,IAAI,EAAE,CAAC,KAAK;AAAA,EACzC;AACF;AAEA,eAAe,eACb,MACA,MACA,IACA,OACmB;AACnB,QAAM,QAAQ,OAAO,GAAG,IAAI,KAAK,EAAE,KAAK;AACxC,QAAM,OAAO,CAAC,OAAO,eAAe,4BAA4B,KAAK;AACrE,MAAI,OAAO;AACT,SAAK,KAAK,MAAM,KAAK;AAAA,EACvB;AACA,QAAM,EAAE,OAAO,IAAI,MAAM,UAAU,OAAO,MAAM;AAAA,IAC9C,KAAK;AAAA,IACL,WAAW,KAAK,OAAO;AAAA,EACzB,CAAC;AACD,QAAM,QAAQ,OAAO,MAAM,IAAI,EAAE,OAAO,OAAO;AAC/C,SAAO,MAAM,IAAI,CAAC,SAAS;AACzB,UAAM,CAAC,KAAK,OAAO,IAAI,KAAK,MAAM,GAAM;AACxC,WAAO,YAAY,KAAM,OAAQ;AAAA,EACnC,CAAC;AACH;AAEO,SAAS,YAAY,KAAa,SAAyB;AAGhE,QAAM,IAAI,QAAQ,MAAM,uCAAuC;AAC/D,MAAI,CAAC,GAAG;AACN,WAAO,EAAE,KAAK,MAAM,SAAS,OAAO,QAAW,SAAS,YAAY,MAAM;AAAA,EAC5E;AACA,QAAM,OAAO,EAAE,CAAC,EAAG,YAAY;AAC/B,QAAM,QAAQ,EAAE,CAAC;AACjB,QAAM,aAAa,CAAC,CAAC,EAAE,CAAC;AACxB,QAAM,OAAO,EAAE,CAAC,EAAG,KAAK;AACxB,SAAO,EAAE,KAAK,MAAM,OAAO,SAAS,MAAM,WAAW;AACvD;AAEA,SAAS,YAAY,SAA0C;AAC7D,QAAM,MAAM,oBAAI,IAAsB;AACtC,aAAW,KAAK,SAAS;AAEvB,UAAM,YAAY,eAAe,KAAK,CAAC,QAAQ,IAAI,SAAS,EAAE,IAAI,GAAG,QAAQ;AAC7E,UAAM,MAAM,IAAI,IAAI,SAAS,KAAK,CAAC;AACnC,QAAI,KAAK,CAAC;AACV,QAAI,IAAI,WAAW,GAAG;AAAA,EACxB;AACA,SAAO;AACT;AAEA,SAAS,eACP,SACA,MACA,MACA,IACA,SACA,SACQ;AACR,QAAM,QAAkB,CAAC;AACzB,QAAM,KAAK,MAAM,OAAO,WAAM,IAAI,EAAE;AACpC,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,aAAa,QAAQ,WAAW,SAAS,EAAE,KAAK;AAC3D,QAAM,KAAK,EAAE;AAEb,QAAM,WAAW,CAAC,GAAG,QAAQ,OAAO,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,MAAM,EAAE,UAAU;AACxE,MAAI,SAAS,SAAS,GAAG;AACvB,UAAM,KAAK,mCAAyB;AACpC,UAAM,KAAK,EAAE;AACb,eAAW,KAAK,UAAU;AACxB,YAAM,KAAK,KAAK,iBAAiB,CAAC,CAAC,EAAE;AAAA,IACvC;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,QAAM,eAAe,oBAAI,IAAY;AACrC,aAAW,EAAE,QAAQ,KAAK,gBAAgB;AACxC,QAAI,aAAa,IAAI,OAAO,EAAG;AAC/B,iBAAa,IAAI,OAAO;AACxB,UAAM,QAAQ,eAAe,OAAO,CAAC,MAAM,EAAE,YAAY,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI;AACnF,UAAM,UAAU,MAAM,QAAQ,CAAC,MAAM,QAAQ,IAAI,CAAC,KAAK,CAAC,CAAC;AACzD,QAAI,QAAQ,WAAW,EAAG;AAC1B,UAAM,KAAK,OAAO,OAAO,EAAE;AAC3B,UAAM,KAAK,EAAE;AACb,eAAW,KAAK,SAAS;AACvB,UAAI,EAAE,WAAY;AAClB,YAAM,KAAK,KAAK,iBAAiB,CAAC,CAAC,EAAE;AAAA,IACvC;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,MAAI,QAAQ,SAAS,GAAG;AACtB,UAAM,KAAK,uBAAgB;AAC3B,UAAM,KAAK,EAAE;AACb,eAAW,KAAK,SAAS;AACvB,YAAM,KAAK,KAAK,wBAAwB,CAAC,CAAC,EAAE;AAAA,IAC9C;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,iBAAiB,GAAmB;AAC3C,QAAM,QAAQ,EAAE,QAAQ,KAAK,EAAE,KAAK,SAAS;AAC7C,SAAO,GAAG,KAAK,GAAG,EAAE,OAAO,OAAO,EAAE,IAAI,MAAM,GAAG,CAAC,CAAC;AACrD;AAqBA,eAAe,oBAAoB,KAAuC;AACxE,MAAI;AACJ,MAAI;AACF,cAAU,MAAM,GAAG,QAAQ,GAAG;AAAA,EAChC,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACA,QAAM,MAAuB,CAAC;AAC9B,aAAW,QAAQ,QAAQ,KAAK,GAAG;AACjC,QAAI,CAAC,KAAK,SAAS,OAAO,EAAG;AAC7B,QAAI;AACF,YAAM,OAAO,MAAM,GAAG,SAAS,KAAK,KAAK,KAAK,IAAI,GAAG,MAAM;AAC3D,YAAM,SAAS,KAAK,MAAM,IAAI;AAQ9B,UAAI,OAAO,YAAY,EAAG;AAC1B,YAAM,QAAQ,MAAM,QAAQ,OAAO,KAAK,IAAI,OAAO,QAAQ,CAAC;AAC5D,YAAM,aAAqC,CAAC;AAC5C,iBAAW,KAAK,OAAO;AACrB,cAAM,SAAS,OAAO,EAAE,WAAW,WAAW,EAAE,SAAS;AACzD,mBAAW,MAAM,KAAK,WAAW,MAAM,KAAK,KAAK;AAAA,MACnD;AACA,UAAI,KAAK;AAAA,QACP,UAAU;AAAA,QACV,GAAI,OAAO,OAAO,eAAe,WAAW,EAAE,YAAY,OAAO,WAAW,IAAI,CAAC;AAAA,QACjF,GAAI,OAAO,OAAO,YAAY,WAAW,EAAE,SAAS,OAAO,QAAQ,IAAI,CAAC;AAAA,QACxE,GAAI,OAAO,OAAO,eAAe,WAAW,EAAE,YAAY,OAAO,WAAW,IAAI,CAAC;AAAA,QACjF,cAAc,OAAO,gBAAgB;AAAA,QACrC;AAAA,QACA,YAAY,MAAM;AAAA,MACpB,CAAC;AAAA,IACH,QAAQ;AAAA,IAGR;AAAA,EACF;AACA,SAAO;AACT;AAGA,SAAS,wBAAwB,GAA0B;AACzD,QAAM,QAAkB,CAAC;AACzB,QAAM,KAAK,KAAK,EAAE,QAAQ,IAAI;AAC9B,MAAI,EAAE,WAAY,OAAM,KAAK,EAAE,UAAU;AACzC,MAAI,EAAE,QAAS,OAAM,KAAK,EAAE,OAAO;AACnC,QAAM,aACJ,EAAE,eAAe,kBACb,WACA,EAAE,eAAe,kBACf,iBACA,EAAE,eAAe,UACf,cACA,EAAE,eAAe,kBACf,iBACA;AACZ,QAAM,KAAK,GAAG,UAAU,IAAI,EAAE,cAAc,SAAS,EAAE;AACvD,QAAM,KAAK,GAAG,EAAE,UAAU,UAAU;AACpC,QAAM,aAAa,OAAO,QAAQ,EAAE,UAAU,EAC3C,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,EACrC,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,EAAE,EAC3B,KAAK,IAAI;AACZ,MAAI,WAAY,OAAM,KAAK,IAAI,UAAU,GAAG;AAC5C,MAAI,EAAE,aAAc,OAAM,KAAK,WAAW,EAAE,YAAY,EAAE;AAC1D,SAAO,MAAM,KAAK,QAAK;AACzB;","names":[]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
__require
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=chunk-DGUM43GV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// src/util/help-catalog.ts
|
|
2
|
+
import { DEPLOY_OPTIONS_CATALOG } from "@sdt-tools/core/options";
|
|
3
|
+
var CATALOG_BY_NAME = new Map(
|
|
4
|
+
DEPLOY_OPTIONS_CATALOG.map((entry) => [entry.name, entry])
|
|
5
|
+
);
|
|
6
|
+
function attachRelatedOptions(cmd, names) {
|
|
7
|
+
const entries = [];
|
|
8
|
+
for (const n of names) {
|
|
9
|
+
const e = CATALOG_BY_NAME.get(n);
|
|
10
|
+
if (e) entries.push(e);
|
|
11
|
+
}
|
|
12
|
+
if (entries.length === 0) return;
|
|
13
|
+
const maxName = entries.reduce((m, e) => Math.max(m, e.name.length), 0);
|
|
14
|
+
const lines = [
|
|
15
|
+
"",
|
|
16
|
+
"Related options (run `sdt explain <name>` for safety tier, default, and example):",
|
|
17
|
+
...entries.map((e) => ` ${e.name.padEnd(maxName)} ${e.summary}`)
|
|
18
|
+
];
|
|
19
|
+
cmd.addHelpText("after", lines.join("\n"));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export {
|
|
23
|
+
attachRelatedOptions
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=chunk-EWXM4KJN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/util/help-catalog.ts"],"sourcesContent":["/**\n * Per-command --help enrichments — show 3-5 related catalog entries\n * under each command's help screen, with a one-line summary and a\n * pointer to `sdt explain <name>` for the full entry.\n *\n * Free-tier discoverability: a user who opens `sdt publish --help`\n * lands on a clear list of the deployment options that govern the\n * command's safety behaviour, without needing to know they exist or\n * read the project format reference first.\n *\n * Mirrors `Databricks/packages/cli/src/util/help-catalog.ts`.\n */\nimport type { Command } from 'commander';\nimport { DEPLOY_OPTIONS_CATALOG, type OptionEntry } from '@sdt-tools/core/options';\n\nconst CATALOG_BY_NAME: Map<string, OptionEntry> = new Map(\n DEPLOY_OPTIONS_CATALOG.map((entry) => [entry.name, entry]),\n);\n\n/**\n * Attach an \"After\" help block listing the named catalog entries with\n * one-line summaries. Unknown keys are silently skipped so a stale\n * mapping never breaks --help.\n */\nexport function attachRelatedOptions(cmd: Command, names: readonly string[]): void {\n const entries: OptionEntry[] = [];\n for (const n of names) {\n const e = CATALOG_BY_NAME.get(n);\n if (e) entries.push(e);\n }\n if (entries.length === 0) return;\n const maxName = entries.reduce((m, e) => Math.max(m, e.name.length), 0);\n const lines = [\n '',\n 'Related options (run `sdt explain <name>` for safety tier, default, and example):',\n ...entries.map((e) => ` ${e.name.padEnd(maxName)} ${e.summary}`),\n ];\n cmd.addHelpText('after', lines.join('\\n'));\n}\n"],"mappings":";AAaA,SAAS,8BAAgD;AAEzD,IAAM,kBAA4C,IAAI;AAAA,EACpD,uBAAuB,IAAI,CAAC,UAAU,CAAC,MAAM,MAAM,KAAK,CAAC;AAC3D;AAOO,SAAS,qBAAqB,KAAc,OAAgC;AACjF,QAAM,UAAyB,CAAC;AAChC,aAAW,KAAK,OAAO;AACrB,UAAM,IAAI,gBAAgB,IAAI,CAAC;AAC/B,QAAI,EAAG,SAAQ,KAAK,CAAC;AAAA,EACvB;AACA,MAAI,QAAQ,WAAW,EAAG;AAC1B,QAAM,UAAU,QAAQ,OAAO,CAAC,GAAG,MAAM,KAAK,IAAI,GAAG,EAAE,KAAK,MAAM,GAAG,CAAC;AACtE,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA;AAAA,IACA,GAAG,QAAQ,IAAI,CAAC,MAAM,KAAK,EAAE,KAAK,OAAO,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE;AAAA,EACnE;AACA,MAAI,YAAY,SAAS,MAAM,KAAK,IAAI,CAAC;AAC3C;","names":[]}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// src/util/mapping.ts
|
|
2
|
+
import { promises as fs } from "fs";
|
|
3
|
+
import { Option } from "commander";
|
|
4
|
+
import { compare } from "@sdt-tools/core";
|
|
5
|
+
function addMappingFlags(cmd) {
|
|
6
|
+
return cmd.option(
|
|
7
|
+
"--map <src=tgt>",
|
|
8
|
+
"Logical-name mapping (repeatable). Examples: DB_A=DB_B, DB_A.STAGING=DB_B.RAW. Rewrites source-side FQNs and DDL body refs.",
|
|
9
|
+
(value, previous) => [...previous, value],
|
|
10
|
+
[]
|
|
11
|
+
).option("--map-file <path>", "JSON file of mappings.").addOption(
|
|
12
|
+
new Option(
|
|
13
|
+
"--map-case-sensitive",
|
|
14
|
+
"Match identifiers case-sensitively. Default: insensitive."
|
|
15
|
+
).default(false)
|
|
16
|
+
).addOption(
|
|
17
|
+
new Option(
|
|
18
|
+
"--map-rewrite-standalone",
|
|
19
|
+
"Rewrite standalone one-part refs (e.g. USE DATABASE DB_A). Default: off (safer)."
|
|
20
|
+
).default(false)
|
|
21
|
+
).addOption(
|
|
22
|
+
new Option(
|
|
23
|
+
"--map-rewrite-strings",
|
|
24
|
+
"Phase 3: also rewrite FQN-shaped tokens inside string literals (best-effort, may produce false positives in legitimate string content). Default: off."
|
|
25
|
+
).default(false)
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
async function buildMappingFromOptions(opts) {
|
|
29
|
+
const flagMappings = Array.isArray(opts.map) ? opts.map : [];
|
|
30
|
+
let mapping = { entries: [] };
|
|
31
|
+
if (opts.mapFile) {
|
|
32
|
+
const raw = await fs.readFile(String(opts.mapFile), "utf8");
|
|
33
|
+
mapping = compare.mappingFromFile(JSON.parse(raw));
|
|
34
|
+
}
|
|
35
|
+
if (flagMappings.length > 0) {
|
|
36
|
+
const fromArgs = compare.mappingFromArgs(flagMappings);
|
|
37
|
+
mapping = { entries: [...mapping.entries, ...fromArgs.entries] };
|
|
38
|
+
}
|
|
39
|
+
if (mapping.entries.length === 0) return void 0;
|
|
40
|
+
if (opts.mapCaseSensitive) mapping.caseSensitive = true;
|
|
41
|
+
if (opts.mapRewriteStandalone) mapping.rewriteStandaloneRefs = true;
|
|
42
|
+
if (opts.mapRewriteStrings) mapping.rewriteInsideStrings = true;
|
|
43
|
+
return mapping;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export {
|
|
47
|
+
addMappingFlags,
|
|
48
|
+
buildMappingFromOptions
|
|
49
|
+
};
|
|
50
|
+
//# sourceMappingURL=chunk-JP2EZLR5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/util/mapping.ts"],"sourcesContent":["import { promises as fs } from 'node:fs';\nimport { Option, type Command } from 'commander';\nimport { compare } from '@sdt-tools/core';\n\ntype NameMapping = compare.NameMapping;\n\nexport interface MappingOptions {\n map?: string[];\n mapFile?: string;\n mapCaseSensitive?: boolean;\n mapRewriteStandalone?: boolean;\n mapRewriteStrings?: boolean;\n}\n\n/** Attach `--map`, `--map-file`, `--map-case-sensitive`, `--map-rewrite-standalone`, `--map-rewrite-strings` to a command. */\nexport function addMappingFlags(cmd: Command): Command {\n return cmd\n .option(\n '--map <src=tgt>',\n 'Logical-name mapping (repeatable). Examples: DB_A=DB_B, DB_A.STAGING=DB_B.RAW. Rewrites source-side FQNs and DDL body refs.',\n (value: string, previous: string[]) => [...previous, value],\n [] as string[],\n )\n .option('--map-file <path>', 'JSON file of mappings.')\n .addOption(\n new Option(\n '--map-case-sensitive',\n 'Match identifiers case-sensitively. Default: insensitive.',\n ).default(false),\n )\n .addOption(\n new Option(\n '--map-rewrite-standalone',\n 'Rewrite standalone one-part refs (e.g. USE DATABASE DB_A). Default: off (safer).',\n ).default(false),\n )\n .addOption(\n new Option(\n '--map-rewrite-strings',\n 'Phase 3: also rewrite FQN-shaped tokens inside string literals (best-effort, may produce false positives in legitimate string content). Default: off.',\n ).default(false),\n );\n}\n\n/** Build a `NameMapping` from `--map`/`--map-file` flag values. Returns undefined when no mappings supplied. */\nexport async function buildMappingFromOptions(\n opts: MappingOptions,\n): Promise<NameMapping | undefined> {\n const flagMappings = Array.isArray(opts.map) ? opts.map : [];\n let mapping: NameMapping = { entries: [] };\n if (opts.mapFile) {\n const raw = await fs.readFile(String(opts.mapFile), 'utf8');\n mapping = compare.mappingFromFile(JSON.parse(raw));\n }\n if (flagMappings.length > 0) {\n const fromArgs = compare.mappingFromArgs(flagMappings);\n mapping = { entries: [...mapping.entries, ...fromArgs.entries] };\n }\n if (mapping.entries.length === 0) return undefined;\n if (opts.mapCaseSensitive) mapping.caseSensitive = true;\n if (opts.mapRewriteStandalone) mapping.rewriteStandaloneRefs = true;\n if (opts.mapRewriteStrings) mapping.rewriteInsideStrings = true;\n return mapping;\n}\n\n/** Render a one-line summary of a NameMapping for the operator's log output. */\nexport function describeMapping(mapping: NameMapping | undefined): string {\n if (!mapping || mapping.entries.length === 0) return '';\n return mapping.entries.map((e) => `${e.source}→${e.target}`).join(', ');\n}\n"],"mappings":";AAAA,SAAS,YAAY,UAAU;AAC/B,SAAS,cAA4B;AACrC,SAAS,eAAe;AAajB,SAAS,gBAAgB,KAAuB;AACrD,SAAO,IACJ;AAAA,IACC;AAAA,IACA;AAAA,IACA,CAAC,OAAe,aAAuB,CAAC,GAAG,UAAU,KAAK;AAAA,IAC1D,CAAC;AAAA,EACH,EACC,OAAO,qBAAqB,wBAAwB,EACpD;AAAA,IACC,IAAI;AAAA,MACF;AAAA,MACA;AAAA,IACF,EAAE,QAAQ,KAAK;AAAA,EACjB,EACC;AAAA,IACC,IAAI;AAAA,MACF;AAAA,MACA;AAAA,IACF,EAAE,QAAQ,KAAK;AAAA,EACjB,EACC;AAAA,IACC,IAAI;AAAA,MACF;AAAA,MACA;AAAA,IACF,EAAE,QAAQ,KAAK;AAAA,EACjB;AACJ;AAGA,eAAsB,wBACpB,MACkC;AAClC,QAAM,eAAe,MAAM,QAAQ,KAAK,GAAG,IAAI,KAAK,MAAM,CAAC;AAC3D,MAAI,UAAuB,EAAE,SAAS,CAAC,EAAE;AACzC,MAAI,KAAK,SAAS;AAChB,UAAM,MAAM,MAAM,GAAG,SAAS,OAAO,KAAK,OAAO,GAAG,MAAM;AAC1D,cAAU,QAAQ,gBAAgB,KAAK,MAAM,GAAG,CAAC;AAAA,EACnD;AACA,MAAI,aAAa,SAAS,GAAG;AAC3B,UAAM,WAAW,QAAQ,gBAAgB,YAAY;AACrD,cAAU,EAAE,SAAS,CAAC,GAAG,QAAQ,SAAS,GAAG,SAAS,OAAO,EAAE;AAAA,EACjE;AACA,MAAI,QAAQ,QAAQ,WAAW,EAAG,QAAO;AACzC,MAAI,KAAK,iBAAkB,SAAQ,gBAAgB;AACnD,MAAI,KAAK,qBAAsB,SAAQ,wBAAwB;AAC/D,MAAI,KAAK,kBAAmB,SAAQ,uBAAuB;AAC3D,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// src/util/logger.ts
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
var logger = {
|
|
4
|
+
info: (msg) => console.log(msg),
|
|
5
|
+
success: (msg) => console.log(chalk.green("\u2714"), msg),
|
|
6
|
+
warn: (msg) => console.warn(chalk.yellow("!"), msg),
|
|
7
|
+
error: (msg) => console.error(chalk.red("\u2716"), msg),
|
|
8
|
+
step: (msg) => console.log(chalk.cyan("\u2192"), msg),
|
|
9
|
+
dim: (msg) => console.log(chalk.gray(msg))
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export {
|
|
13
|
+
logger
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=chunk-VM2H4LAO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/util/logger.ts"],"sourcesContent":["/**\n * Minimal logger used by the CLI. Wraps chalk/ora so a future move to a\n * non-tty mode is centralised.\n */\nimport chalk from 'chalk';\n\nexport const logger = {\n info: (msg: string) => console.log(msg),\n success: (msg: string) => console.log(chalk.green('✔'), msg),\n warn: (msg: string) => console.warn(chalk.yellow('!'), msg),\n error: (msg: string) => console.error(chalk.red('✖'), msg),\n step: (msg: string) => console.log(chalk.cyan('→'), msg),\n dim: (msg: string) => console.log(chalk.gray(msg)),\n};\n"],"mappings":";AAIA,OAAO,WAAW;AAEX,IAAM,SAAS;AAAA,EACpB,MAAM,CAAC,QAAgB,QAAQ,IAAI,GAAG;AAAA,EACtC,SAAS,CAAC,QAAgB,QAAQ,IAAI,MAAM,MAAM,QAAG,GAAG,GAAG;AAAA,EAC3D,MAAM,CAAC,QAAgB,QAAQ,KAAK,MAAM,OAAO,GAAG,GAAG,GAAG;AAAA,EAC1D,OAAO,CAAC,QAAgB,QAAQ,MAAM,MAAM,IAAI,QAAG,GAAG,GAAG;AAAA,EACzD,MAAM,CAAC,QAAgB,QAAQ,IAAI,MAAM,KAAK,QAAG,GAAG,GAAG;AAAA,EACvD,KAAK,CAAC,QAAgB,QAAQ,IAAI,MAAM,KAAK,GAAG,CAAC;AACnD;","names":[]}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import {
|
|
2
|
+
logger
|
|
3
|
+
} from "./chunk-VM2H4LAO.js";
|
|
4
|
+
|
|
5
|
+
// src/util/ai-explain.ts
|
|
6
|
+
import { ai } from "@sdt-tools/core";
|
|
7
|
+
var DEFAULT_SYSTEM_PROMPT = `You are a senior Snowflake DBA reviewing structured output from the SDT CLI. Narrate it for a teammate who has not seen the underlying SQL. Be concrete and specific \u2014 reference identifiers by name. Group related items together. Skip information that is already obvious from the structured output; add the missing intent, risks, and follow-up actions. 4-10 sentences total unless the data warrants more.`;
|
|
8
|
+
function attachExplainFlag(cmd, description) {
|
|
9
|
+
const hasExplain = cmd.options.some((o) => o.long === "--explain" || o.long === "--ai-explain");
|
|
10
|
+
if (hasExplain) return cmd;
|
|
11
|
+
cmd.option(
|
|
12
|
+
"--explain",
|
|
13
|
+
description ?? "After the structured output, call the configured AI provider to narrate the result in plain English. Requires `sdt ai` to be configured.",
|
|
14
|
+
false
|
|
15
|
+
);
|
|
16
|
+
return cmd;
|
|
17
|
+
}
|
|
18
|
+
async function runExplain(ctx, opts, buildPrompt) {
|
|
19
|
+
if (!opts.explain) return;
|
|
20
|
+
const system = ctx.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;
|
|
21
|
+
logger.info("");
|
|
22
|
+
logger.info("AI explanation:");
|
|
23
|
+
try {
|
|
24
|
+
const reply = await ai.complete(
|
|
25
|
+
[
|
|
26
|
+
{ role: "system", content: system },
|
|
27
|
+
{ role: "user", content: buildPrompt() }
|
|
28
|
+
],
|
|
29
|
+
{ feature: ctx.feature }
|
|
30
|
+
);
|
|
31
|
+
for (const line of reply.text.split("\n")) logger.info(" " + line);
|
|
32
|
+
} catch (err) {
|
|
33
|
+
logger.error(" --explain failed: " + (err instanceof Error ? err.message : String(err)));
|
|
34
|
+
logger.error(
|
|
35
|
+
" Run `sdt ai status` to verify your AI provider is configured (`sdt ai test` to send a probe)."
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export {
|
|
41
|
+
attachExplainFlag,
|
|
42
|
+
runExplain
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=chunk-ZWY4ZRHL.js.map
|