@datachonk/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +200 -0
  2. package/dist/__tests__/cache.test.d.ts +2 -0
  3. package/dist/__tests__/cache.test.d.ts.map +1 -0
  4. package/dist/__tests__/cache.test.js +40 -0
  5. package/dist/__tests__/cache.test.js.map +1 -0
  6. package/dist/__tests__/config.test.d.ts +2 -0
  7. package/dist/__tests__/config.test.d.ts.map +1 -0
  8. package/dist/__tests__/config.test.js +113 -0
  9. package/dist/__tests__/config.test.js.map +1 -0
  10. package/dist/__tests__/templates.test.d.ts +2 -0
  11. package/dist/__tests__/templates.test.d.ts.map +1 -0
  12. package/dist/__tests__/templates.test.js +51 -0
  13. package/dist/__tests__/templates.test.js.map +1 -0
  14. package/dist/commands/analyze.d.ts +10 -0
  15. package/dist/commands/analyze.d.ts.map +1 -0
  16. package/dist/commands/analyze.js +156 -0
  17. package/dist/commands/analyze.js.map +1 -0
  18. package/dist/commands/chat.d.ts +3 -0
  19. package/dist/commands/chat.d.ts.map +1 -0
  20. package/dist/commands/chat.js +121 -0
  21. package/dist/commands/chat.js.map +1 -0
  22. package/dist/commands/config.d.ts +2 -0
  23. package/dist/commands/config.d.ts.map +1 -0
  24. package/dist/commands/config.js +287 -0
  25. package/dist/commands/config.js.map +1 -0
  26. package/dist/commands/docs.d.ts +7 -0
  27. package/dist/commands/docs.d.ts.map +1 -0
  28. package/dist/commands/docs.js +208 -0
  29. package/dist/commands/docs.js.map +1 -0
  30. package/dist/commands/generate.d.ts +9 -0
  31. package/dist/commands/generate.d.ts.map +1 -0
  32. package/dist/commands/generate.js +130 -0
  33. package/dist/commands/generate.js.map +1 -0
  34. package/dist/commands/init.d.ts +7 -0
  35. package/dist/commands/init.d.ts.map +1 -0
  36. package/dist/commands/init.js +149 -0
  37. package/dist/commands/init.js.map +1 -0
  38. package/dist/commands/lineage.d.ts +9 -0
  39. package/dist/commands/lineage.d.ts.map +1 -0
  40. package/dist/commands/lineage.js +186 -0
  41. package/dist/commands/lineage.js.map +1 -0
  42. package/dist/commands/migrate.d.ts +3 -0
  43. package/dist/commands/migrate.d.ts.map +1 -0
  44. package/dist/commands/migrate.js +213 -0
  45. package/dist/commands/migrate.js.map +1 -0
  46. package/dist/commands/review.d.ts +8 -0
  47. package/dist/commands/review.d.ts.map +1 -0
  48. package/dist/commands/review.js +215 -0
  49. package/dist/commands/review.js.map +1 -0
  50. package/dist/commands/scan.d.ts +18 -0
  51. package/dist/commands/scan.d.ts.map +1 -0
  52. package/dist/commands/scan.js +335 -0
  53. package/dist/commands/scan.js.map +1 -0
  54. package/dist/commands/test.d.ts +3 -0
  55. package/dist/commands/test.d.ts.map +1 -0
  56. package/dist/commands/test.js +244 -0
  57. package/dist/commands/test.js.map +1 -0
  58. package/dist/index.d.ts +3 -0
  59. package/dist/index.d.ts.map +1 -0
  60. package/dist/index.js +232 -0
  61. package/dist/index.js.map +1 -0
  62. package/dist/utils/analyzer.d.ts +39 -0
  63. package/dist/utils/analyzer.d.ts.map +1 -0
  64. package/dist/utils/analyzer.js +310 -0
  65. package/dist/utils/analyzer.js.map +1 -0
  66. package/dist/utils/cache.d.ts +98 -0
  67. package/dist/utils/cache.d.ts.map +1 -0
  68. package/dist/utils/cache.js +253 -0
  69. package/dist/utils/cache.js.map +1 -0
  70. package/dist/utils/config.d.ts +56 -0
  71. package/dist/utils/config.d.ts.map +1 -0
  72. package/dist/utils/config.js +108 -0
  73. package/dist/utils/config.js.map +1 -0
  74. package/dist/utils/generators.d.ts +8 -0
  75. package/dist/utils/generators.d.ts.map +1 -0
  76. package/dist/utils/generators.js +265 -0
  77. package/dist/utils/generators.js.map +1 -0
  78. package/dist/utils/plugins.d.ts +94 -0
  79. package/dist/utils/plugins.d.ts.map +1 -0
  80. package/dist/utils/plugins.js +164 -0
  81. package/dist/utils/plugins.js.map +1 -0
  82. package/dist/utils/templates.d.ts +57 -0
  83. package/dist/utils/templates.d.ts.map +1 -0
  84. package/dist/utils/templates.js +458 -0
  85. package/dist/utils/templates.js.map +1 -0
  86. package/package.json +69 -0
@@ -0,0 +1,121 @@
1
+ import { Command } from "commander";
2
+ import chalk from "chalk";
3
+ import ora from "ora";
4
+ import * as readline from "readline";
5
+ import { loadConfig } from "../utils/config.js";
6
+ export const chatCommand = new Command("chat")
7
+ .description("Interactive AI chat for dbt help and code generation")
8
+ .option("-c, --context <path>", "Include a file or directory as context")
9
+ .option("--model <model>", "AI model to use", "gpt-4o")
10
+ .action(async (options) => {
11
+ const config = loadConfig();
12
+ const apiKey = config.ai?.apiKey || process.env.DATACHONK_API_KEY;
13
+ if (!apiKey) {
14
+ console.log(chalk.red("No API key found."));
15
+ console.log(chalk.yellow("Run: datachonk config set apiKey <your-key>"));
16
+ console.log(chalk.yellow("Or set DATACHONK_API_KEY environment variable"));
17
+ process.exit(1);
18
+ }
19
+ console.log(chalk.cyan.bold("\n🐕 DataChonk AI Assistant"));
20
+ console.log(chalk.gray("Your expert dbt companion. Type 'exit' to quit.\n"));
21
+ const messages = [];
22
+ // Load context if provided
23
+ if (options.context) {
24
+ const spinner = ora("Loading context...").start();
25
+ try {
26
+ const fs = await import("fs");
27
+ const path = await import("path");
28
+ const contextPath = path.resolve(options.context);
29
+ const stat = fs.statSync(contextPath);
30
+ let contextContent = "";
31
+ if (stat.isDirectory()) {
32
+ const files = fs.readdirSync(contextPath).filter(f => f.endsWith(".sql") || f.endsWith(".yml") || f.endsWith(".yaml"));
33
+ for (const file of files.slice(0, 10)) {
34
+ const content = fs.readFileSync(path.join(contextPath, file), "utf-8");
35
+ contextContent += `\n--- ${file} ---\n${content}\n`;
36
+ }
37
+ }
38
+ else {
39
+ contextContent = fs.readFileSync(contextPath, "utf-8");
40
+ }
41
+ messages.push({
42
+ role: "user",
43
+ content: `Here is my dbt project context:\n\n${contextContent}\n\nPlease keep this in mind for our conversation.`
44
+ });
45
+ messages.push({
46
+ role: "assistant",
47
+ content: "I've loaded your dbt project context. I can see your models and configuration. How can I help you today?"
48
+ });
49
+ spinner.succeed(`Loaded context from ${options.context}`);
50
+ }
51
+ catch (error) {
52
+ spinner.fail("Failed to load context");
53
+ }
54
+ }
55
+ const rl = readline.createInterface({
56
+ input: process.stdin,
57
+ output: process.stdout,
58
+ });
59
+ const prompt = () => {
60
+ rl.question(chalk.green("\nYou: "), async (input) => {
61
+ const trimmed = input.trim();
62
+ if (trimmed.toLowerCase() === "exit" || trimmed.toLowerCase() === "quit") {
63
+ console.log(chalk.cyan("\nGoodbye! Happy modeling! 🐕\n"));
64
+ rl.close();
65
+ return;
66
+ }
67
+ if (!trimmed) {
68
+ prompt();
69
+ return;
70
+ }
71
+ messages.push({ role: "user", content: trimmed });
72
+ const spinner = ora("Thinking...").start();
73
+ try {
74
+ const { getApiUrl } = await import("../utils/config.js");
75
+ const baseUrl = getApiUrl();
76
+ const response = await fetch(`${baseUrl}/api/cli/chat`, {
77
+ method: "POST",
78
+ headers: {
79
+ "Content-Type": "application/json",
80
+ "Authorization": `Bearer ${apiKey}`,
81
+ },
82
+ body: JSON.stringify({
83
+ messages,
84
+ stream: false,
85
+ context: options.context ? {
86
+ projectPath: options.context,
87
+ } : undefined,
88
+ }),
89
+ });
90
+ if (!response.ok) {
91
+ throw new Error(`API error: ${response.status}`);
92
+ }
93
+ const data = await response.json();
94
+ const assistantMessage = data.content || data.message || "I couldn't generate a response.";
95
+ messages.push({ role: "assistant", content: assistantMessage });
96
+ spinner.stop();
97
+ console.log(chalk.cyan("\nDataChonk: ") + formatResponse(assistantMessage));
98
+ }
99
+ catch (error) {
100
+ spinner.fail("Failed to get response");
101
+ console.log(chalk.red(`Error: ${error instanceof Error ? error.message : "Unknown error"}`));
102
+ }
103
+ prompt();
104
+ });
105
+ };
106
+ prompt();
107
+ });
108
+ function formatResponse(text) {
109
+ // Format code blocks
110
+ let formatted = text.replace(/```(\w+)?\n([\s\S]*?)```/g, (_, lang, code) => {
111
+ return chalk.gray(`\n--- ${lang || "code"} ---\n`) +
112
+ chalk.white(code.trim()) +
113
+ chalk.gray("\n---\n");
114
+ });
115
+ // Format inline code
116
+ formatted = formatted.replace(/`([^`]+)`/g, chalk.yellow("$1"));
117
+ // Format bold
118
+ formatted = formatted.replace(/\*\*([^*]+)\*\*/g, chalk.bold("$1"));
119
+ return formatted;
120
+ }
121
+ //# sourceMappingURL=chat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat.js","sourceRoot":"","sources":["../../src/commands/chat.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function configCommand(action: string, key?: string, value?: string): Promise<void>;
2
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAeA,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,GAAG,CAAC,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAmHf"}
@@ -0,0 +1,287 @@
1
+ import chalk from "chalk";
2
+ import { existsSync, readFileSync, writeFileSync } from "fs";
3
+ import { join } from "path";
4
+ import yaml from "js-yaml";
5
+ import { table } from "table";
6
+ import { getConnections, saveConnection, deleteConnection, } from "../utils/config.js";
7
+ export async function configCommand(action, key, value) {
8
+ const configPath = join(process.cwd(), ".datachonk.yml");
9
+ // Handle connection subcommands
10
+ if (action === "connections" || action === "conn") {
11
+ await handleConnectionsCommand(key, value);
12
+ return;
13
+ }
14
+ if (!existsSync(configPath) && action !== "list") {
15
+ console.log(chalk.yellow("No .datachonk.yml found. Run 'datachonk init' first."));
16
+ process.exit(1);
17
+ }
18
+ switch (action) {
19
+ case "get": {
20
+ if (!key) {
21
+ console.log(chalk.red("Usage: datachonk config get <key>"));
22
+ process.exit(1);
23
+ }
24
+ const config = yaml.load(readFileSync(configPath, "utf-8"));
25
+ const value = getNestedValue(config, key);
26
+ if (value === undefined) {
27
+ console.log(chalk.gray("(not set)"));
28
+ }
29
+ else {
30
+ console.log(typeof value === "object" ? yaml.dump(value) : value);
31
+ }
32
+ break;
33
+ }
34
+ case "set": {
35
+ if (!key || value === undefined) {
36
+ console.log(chalk.red("Usage: datachonk config set <key> <value>"));
37
+ process.exit(1);
38
+ }
39
+ const config = yaml.load(readFileSync(configPath, "utf-8"));
40
+ setNestedValue(config, key, value);
41
+ writeFileSync(configPath, yaml.dump(config, { indent: 2 }));
42
+ console.log(chalk.green(`Set ${key} = ${value}`));
43
+ break;
44
+ }
45
+ case "list": {
46
+ if (!existsSync(configPath)) {
47
+ console.log(chalk.yellow("No configuration file found."));
48
+ console.log(chalk.gray("Run 'datachonk init' to create one."));
49
+ return;
50
+ }
51
+ const config = yaml.load(readFileSync(configPath, "utf-8"));
52
+ console.log(chalk.bold("\nCurrent configuration:"));
53
+ console.log(chalk.gray("-".repeat(50)));
54
+ // Show config without sensitive data
55
+ const safeConfig = { ...config };
56
+ if (safeConfig.ai && typeof safeConfig.ai === "object") {
57
+ const ai = safeConfig.ai;
58
+ if (ai.apiKey) {
59
+ ai.apiKey = "***" + String(ai.apiKey).slice(-4);
60
+ }
61
+ }
62
+ console.log(yaml.dump(safeConfig, { indent: 2 }));
63
+ break;
64
+ }
65
+ case "reset": {
66
+ const { default: inquirer } = await import("inquirer");
67
+ const { confirm } = await inquirer.prompt([
68
+ {
69
+ type: "confirm",
70
+ name: "confirm",
71
+ message: "This will reset all configuration to defaults. Continue?",
72
+ default: false,
73
+ },
74
+ ]);
75
+ if (confirm) {
76
+ const defaultConfig = {
77
+ version: 1,
78
+ warehouse: "snowflake",
79
+ modeling: {
80
+ approach: "kimball",
81
+ conventions: ["stg_prefix", "int_prefix", "fct_prefix", "dim_prefix", "snake_case"],
82
+ },
83
+ ai: {
84
+ enabled: false,
85
+ apiKey: null,
86
+ },
87
+ analysis: {
88
+ ignorePaths: ["target/**", "dbt_packages/**", "logs/**"],
89
+ ignoreRules: [],
90
+ },
91
+ generation: {
92
+ defaultMaterialization: "view",
93
+ addDescriptions: true,
94
+ addTests: true,
95
+ },
96
+ connections: {},
97
+ };
98
+ writeFileSync(configPath, yaml.dump(defaultConfig, { indent: 2 }));
99
+ console.log(chalk.green("Configuration reset to defaults"));
100
+ }
101
+ break;
102
+ }
103
+ default:
104
+ console.log(chalk.red(`Unknown action: ${action}`));
105
+ console.log(chalk.gray("Valid actions: get, set, list, reset, connections"));
106
+ process.exit(1);
107
+ }
108
+ }
109
+ async function handleConnectionsCommand(subAction, name) {
110
+ const { default: inquirer } = await import("inquirer");
111
+ switch (subAction) {
112
+ case "list":
113
+ case undefined: {
114
+ const connections = getConnections();
115
+ const entries = Object.entries(connections);
116
+ if (entries.length === 0) {
117
+ console.log(chalk.yellow("\nNo saved connections."));
118
+ console.log(chalk.gray("Run 'datachonk scan' to create one."));
119
+ return;
120
+ }
121
+ console.log(chalk.bold("\nSaved Connections:"));
122
+ console.log(chalk.gray("-".repeat(60)));
123
+ const tableData = [
124
+ [chalk.bold("Name"), chalk.bold("Type"), chalk.bold("Host/Account"), chalk.bold("Database")],
125
+ ...entries.map(([connName, conn]) => [
126
+ connName,
127
+ conn.type,
128
+ conn.host || conn.account || conn.project || "-",
129
+ conn.database || conn.dataset || "-",
130
+ ]),
131
+ ];
132
+ console.log(table(tableData));
133
+ break;
134
+ }
135
+ case "add": {
136
+ // Interactive connection creation
137
+ const answers = await inquirer.prompt([
138
+ {
139
+ type: "input",
140
+ name: "name",
141
+ message: "Connection name:",
142
+ default: name,
143
+ validate: (input) => input.length > 0 || "Name is required",
144
+ },
145
+ {
146
+ type: "list",
147
+ name: "type",
148
+ message: "Database type:",
149
+ choices: [
150
+ { name: "PostgreSQL", value: "postgres" },
151
+ { name: "MySQL", value: "mysql" },
152
+ { name: "Snowflake", value: "snowflake" },
153
+ { name: "BigQuery", value: "bigquery" },
154
+ { name: "Redshift", value: "redshift" },
155
+ ],
156
+ },
157
+ ]);
158
+ let connection = {
159
+ type: answers.type,
160
+ name: answers.name,
161
+ };
162
+ // Type-specific prompts
163
+ switch (answers.type) {
164
+ case "postgres":
165
+ case "mysql":
166
+ case "redshift": {
167
+ const dbAnswers = await inquirer.prompt([
168
+ { type: "input", name: "host", message: "Host:", default: "localhost" },
169
+ { type: "input", name: "port", message: "Port:", default: answers.type === "mysql" ? "3306" : answers.type === "redshift" ? "5439" : "5432" },
170
+ { type: "input", name: "database", message: "Database:" },
171
+ { type: "input", name: "username", message: "Username:" },
172
+ ]);
173
+ connection = { ...connection, ...dbAnswers };
174
+ break;
175
+ }
176
+ case "snowflake": {
177
+ const sfAnswers = await inquirer.prompt([
178
+ { type: "input", name: "account", message: "Account (e.g., abc123.us-east-1):" },
179
+ { type: "input", name: "username", message: "Username:" },
180
+ { type: "input", name: "warehouse", message: "Warehouse:" },
181
+ { type: "input", name: "database", message: "Database:" },
182
+ { type: "input", name: "role", message: "Role (optional):" },
183
+ ]);
184
+ connection = { ...connection, ...sfAnswers };
185
+ break;
186
+ }
187
+ case "bigquery": {
188
+ const bqAnswers = await inquirer.prompt([
189
+ { type: "input", name: "project", message: "GCP Project ID:" },
190
+ { type: "input", name: "dataset", message: "Default dataset (optional):" },
191
+ { type: "input", name: "keyFile", message: "Service account key file path (optional):" },
192
+ ]);
193
+ connection = { ...connection, ...bqAnswers };
194
+ break;
195
+ }
196
+ }
197
+ saveConnection(answers.name, connection);
198
+ console.log(chalk.green(`\nConnection '${answers.name}' saved.`));
199
+ console.log(chalk.gray("Note: Passwords are not stored. You'll be prompted during scans."));
200
+ break;
201
+ }
202
+ case "remove":
203
+ case "delete": {
204
+ if (!name) {
205
+ // Show list and prompt for selection
206
+ const connections = getConnections();
207
+ const names = Object.keys(connections);
208
+ if (names.length === 0) {
209
+ console.log(chalk.yellow("No connections to remove."));
210
+ return;
211
+ }
212
+ const { selected } = await inquirer.prompt([
213
+ {
214
+ type: "list",
215
+ name: "selected",
216
+ message: "Select connection to remove:",
217
+ choices: names,
218
+ },
219
+ ]);
220
+ name = selected;
221
+ }
222
+ const { confirm } = await inquirer.prompt([
223
+ {
224
+ type: "confirm",
225
+ name: "confirm",
226
+ message: `Remove connection '${name}'?`,
227
+ default: false,
228
+ },
229
+ ]);
230
+ if (confirm && name) {
231
+ if (deleteConnection(name)) {
232
+ console.log(chalk.green(`Connection '${name}' removed.`));
233
+ }
234
+ else {
235
+ console.log(chalk.red(`Connection '${name}' not found.`));
236
+ }
237
+ }
238
+ break;
239
+ }
240
+ default:
241
+ console.log(chalk.red(`Unknown subcommand: ${subAction}`));
242
+ console.log(chalk.gray("Valid subcommands: list, add, remove"));
243
+ }
244
+ }
245
+ function getNestedValue(obj, path) {
246
+ const keys = path.split(".");
247
+ let current = obj;
248
+ for (const key of keys) {
249
+ if (current === null || current === undefined || typeof current !== "object") {
250
+ return undefined;
251
+ }
252
+ current = current[key];
253
+ }
254
+ return current;
255
+ }
256
+ function setNestedValue(obj, path, value) {
257
+ const keys = path.split(".");
258
+ let current = obj;
259
+ for (let i = 0; i < keys.length - 1; i++) {
260
+ const key = keys[i];
261
+ if (!(key in current) || typeof current[key] !== "object") {
262
+ current[key] = {};
263
+ }
264
+ current = current[key];
265
+ }
266
+ const lastKey = keys[keys.length - 1];
267
+ // Try to parse value as JSON, boolean, or number
268
+ let parsedValue = value;
269
+ if (value === "true")
270
+ parsedValue = true;
271
+ else if (value === "false")
272
+ parsedValue = false;
273
+ else if (value === "null")
274
+ parsedValue = null;
275
+ else if (!isNaN(Number(value)))
276
+ parsedValue = Number(value);
277
+ else {
278
+ try {
279
+ parsedValue = JSON.parse(value);
280
+ }
281
+ catch {
282
+ // Keep as string
283
+ }
284
+ }
285
+ current[lastKey] = parsedValue;
286
+ }
287
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ interface DocsOptions {
2
+ enhance?: boolean;
3
+ missingOnly?: boolean;
4
+ }
5
+ export declare function docsCommand(models: string[], options: DocsOptions): Promise<void>;
6
+ export {};
7
+ //# sourceMappingURL=docs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docs.d.ts","sourceRoot":"","sources":["../../src/commands/docs.ts"],"names":[],"mappings":"AASA,UAAU,WAAW;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAsB,WAAW,CAC/B,MAAM,EAAE,MAAM,EAAE,EAChB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CAmIf"}
@@ -0,0 +1,208 @@
1
+ import chalk from "chalk";
2
+ import ora from "ora";
3
+ import { glob } from "glob";
4
+ import { readFileSync, writeFileSync, existsSync } from "fs";
5
+ import { join, dirname, basename } from "path";
6
+ import yaml from "js-yaml";
7
+ import { loadConfig } from "../utils/config.js";
8
+ import { parseModel } from "../utils/analyzer.js";
9
+ export async function docsCommand(models, options) {
10
+ const spinner = ora("Scanning models...").start();
11
+ try {
12
+ const config = loadConfig(".");
13
+ // Find all SQL files
14
+ const pattern = models.length > 0
15
+ ? models.map(m => `**/${m}.sql`)
16
+ : ["**/*.sql"];
17
+ const files = [];
18
+ for (const p of pattern) {
19
+ const matches = await glob(p, {
20
+ cwd: ".",
21
+ ignore: ["**/target/**", "**/dbt_packages/**", "**/node_modules/**"],
22
+ });
23
+ files.push(...matches);
24
+ }
25
+ if (files.length === 0) {
26
+ spinner.fail("No SQL files found");
27
+ return;
28
+ }
29
+ spinner.text = `Found ${files.length} models. Generating documentation...`;
30
+ let created = 0;
31
+ let updated = 0;
32
+ let skipped = 0;
33
+ for (const file of files) {
34
+ const content = readFileSync(file, "utf-8");
35
+ const name = basename(file, ".sql");
36
+ const model = parseModel(content, name);
37
+ // Find or create schema file
38
+ const schemaPath = file.replace(".sql", ".yml");
39
+ const dirSchemaPath = join(dirname(file), "_schema.yml");
40
+ let existingSchema = null;
41
+ let targetPath = schemaPath;
42
+ if (existsSync(schemaPath)) {
43
+ existingSchema = yaml.load(readFileSync(schemaPath, "utf-8"));
44
+ targetPath = schemaPath;
45
+ }
46
+ else if (existsSync(dirSchemaPath)) {
47
+ existingSchema = yaml.load(readFileSync(dirSchemaPath, "utf-8"));
48
+ targetPath = dirSchemaPath;
49
+ }
50
+ // Check if model already has description
51
+ if (options.missingOnly && existingSchema) {
52
+ const models = existingSchema.models;
53
+ const modelEntry = models?.find((m) => m.name === name);
54
+ if (modelEntry?.description) {
55
+ skipped++;
56
+ continue;
57
+ }
58
+ }
59
+ // Generate documentation
60
+ const description = generateDescription(name, model, content);
61
+ const columns = extractColumns(content, model);
62
+ const newModelEntry = {
63
+ name,
64
+ description,
65
+ columns: columns.map(col => ({
66
+ name: col.name,
67
+ description: col.description,
68
+ data_tests: col.isPrimaryKey ? [
69
+ { unique: null },
70
+ { not_null: null },
71
+ ] : col.isRequired ? [
72
+ { not_null: null },
73
+ ] : undefined,
74
+ })).filter(c => c.description || c.data_tests),
75
+ };
76
+ if (existingSchema) {
77
+ // Update existing schema
78
+ const models = (existingSchema.models || []);
79
+ const existingIndex = models.findIndex((m) => m.name === name);
80
+ if (existingIndex >= 0) {
81
+ if (options.enhance) {
82
+ // Merge with existing
83
+ const existing = models[existingIndex];
84
+ models[existingIndex] = {
85
+ ...existing,
86
+ description: existing.description || newModelEntry.description,
87
+ columns: mergeColumns(existing.columns, newModelEntry.columns),
88
+ };
89
+ }
90
+ updated++;
91
+ }
92
+ else {
93
+ models.push(newModelEntry);
94
+ created++;
95
+ }
96
+ existingSchema.models = models;
97
+ writeFileSync(targetPath, yaml.dump(existingSchema, { indent: 2, lineWidth: 120 }));
98
+ }
99
+ else {
100
+ // Create new schema file
101
+ const newSchema = {
102
+ version: 2,
103
+ models: [newModelEntry],
104
+ };
105
+ writeFileSync(targetPath, yaml.dump(newSchema, { indent: 2, lineWidth: 120 }));
106
+ created++;
107
+ }
108
+ }
109
+ spinner.succeed("Documentation complete");
110
+ console.log(chalk.bold("\nResults:"));
111
+ console.log(` Created: ${chalk.green(created.toString())}`);
112
+ console.log(` Updated: ${chalk.blue(updated.toString())}`);
113
+ console.log(` Skipped: ${chalk.gray(skipped.toString())}`);
114
+ console.log(chalk.gray("\nTip: Run 'dbt docs generate && dbt docs serve' to preview"));
115
+ }
116
+ catch (error) {
117
+ spinner.fail("Documentation generation failed");
118
+ console.error(chalk.red(error instanceof Error ? error.message : "Unknown error"));
119
+ process.exit(1);
120
+ }
121
+ }
122
+ function generateDescription(name, model, content) {
123
+ // Extract type from name prefix
124
+ const prefix = name.split("_")[0];
125
+ const typeName = name.replace(/^(stg|int|fct|dim|obt)_/, "");
126
+ const typeDescriptions = {
127
+ stg: `Staging model for ${typeName.replace(/_/g, " ")}. Provides cleaned and typed source data.`,
128
+ int: `Intermediate model for ${typeName.replace(/_/g, " ")}. Applies business logic and transformations.`,
129
+ fct: `Fact table for ${typeName.replace(/_/g, " ")}. Contains measurable, quantitative data.`,
130
+ dim: `Dimension table for ${typeName.replace(/_/g, " ")}. Contains descriptive attributes.`,
131
+ obt: `One Big Table combining ${typeName.replace(/_/g, " ")} data. Denormalized for analytics.`,
132
+ };
133
+ // Check for existing comments in SQL
134
+ const headerComment = content.match(/^--\s*(.+)$/m);
135
+ if (headerComment) {
136
+ return headerComment[1].trim();
137
+ }
138
+ return typeDescriptions[prefix] || `Model for ${typeName.replace(/_/g, " ")} data.`;
139
+ }
140
+ function extractColumns(content, model) {
141
+ const columns = [];
142
+ // Try to extract from final SELECT
143
+ const selectMatch = content.match(/select\s+([\s\S]+?)\s+from/i);
144
+ if (!selectMatch)
145
+ return columns;
146
+ const selectClause = selectMatch[1];
147
+ // Parse column expressions
148
+ const columnPatterns = selectClause.split(/,(?![^(]*\))/);
149
+ for (const pattern of columnPatterns) {
150
+ const trimmed = pattern.trim();
151
+ if (!trimmed || trimmed === "*")
152
+ continue;
153
+ // Extract column name (handle aliases)
154
+ const aliasMatch = trimmed.match(/(?:as\s+)?(\w+)\s*$/i);
155
+ if (!aliasMatch)
156
+ continue;
157
+ const name = aliasMatch[1];
158
+ // Generate description based on common patterns
159
+ let description = "";
160
+ let isPrimaryKey = false;
161
+ let isRequired = false;
162
+ if (name.endsWith("_id")) {
163
+ description = `Unique identifier for ${name.replace(/_id$/, "").replace(/_/g, " ")}`;
164
+ if (name === model.name.replace(/^(stg|int|fct|dim)_/, "") + "_id" ||
165
+ name === model.name.split("_").pop() + "_id") {
166
+ isPrimaryKey = true;
167
+ isRequired = true;
168
+ }
169
+ }
170
+ else if (name.endsWith("_at") || name.endsWith("_date") || name.endsWith("_timestamp")) {
171
+ description = `Timestamp of when ${name.replace(/_(at|date|timestamp)$/, "").replace(/_/g, " ")} occurred`;
172
+ }
173
+ else if (name.startsWith("is_") || name.startsWith("has_")) {
174
+ description = `Boolean flag indicating ${name.replace(/^(is_|has_)/, "").replace(/_/g, " ")}`;
175
+ }
176
+ else if (name.endsWith("_amount") || name.endsWith("_total") || name.endsWith("_count")) {
177
+ description = `Numeric value for ${name.replace(/_(amount|total|count)$/, "").replace(/_/g, " ")}`;
178
+ }
179
+ else if (name.endsWith("_name")) {
180
+ description = `Name of the ${name.replace(/_name$/, "").replace(/_/g, " ")}`;
181
+ }
182
+ else if (name.endsWith("_type") || name.endsWith("_status") || name.endsWith("_category")) {
183
+ description = `${name.split("_").pop()?.charAt(0).toUpperCase()}${name.split("_").pop()?.slice(1)} classification`;
184
+ }
185
+ // Check for NOT NULL in column definition
186
+ if (trimmed.toLowerCase().includes("not null") || trimmed.toLowerCase().includes("coalesce")) {
187
+ isRequired = true;
188
+ }
189
+ columns.push({ name, description, isPrimaryKey, isRequired });
190
+ }
191
+ return columns;
192
+ }
193
+ function mergeColumns(existing, generated) {
194
+ if (!existing)
195
+ return generated;
196
+ const merged = [...existing];
197
+ for (const col of generated) {
198
+ const existingIndex = merged.findIndex(c => c.name === col.name);
199
+ if (existingIndex < 0) {
200
+ merged.push(col);
201
+ }
202
+ else if (!merged[existingIndex].description && col.description) {
203
+ merged[existingIndex] = { ...merged[existingIndex], description: col.description };
204
+ }
205
+ }
206
+ return merged;
207
+ }
208
+ //# sourceMappingURL=docs.js.map