@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.
- package/README.md +200 -0
- package/dist/__tests__/cache.test.d.ts +2 -0
- package/dist/__tests__/cache.test.d.ts.map +1 -0
- package/dist/__tests__/cache.test.js +40 -0
- package/dist/__tests__/cache.test.js.map +1 -0
- package/dist/__tests__/config.test.d.ts +2 -0
- package/dist/__tests__/config.test.d.ts.map +1 -0
- package/dist/__tests__/config.test.js +113 -0
- package/dist/__tests__/config.test.js.map +1 -0
- package/dist/__tests__/templates.test.d.ts +2 -0
- package/dist/__tests__/templates.test.d.ts.map +1 -0
- package/dist/__tests__/templates.test.js +51 -0
- package/dist/__tests__/templates.test.js.map +1 -0
- package/dist/commands/analyze.d.ts +10 -0
- package/dist/commands/analyze.d.ts.map +1 -0
- package/dist/commands/analyze.js +156 -0
- package/dist/commands/analyze.js.map +1 -0
- package/dist/commands/chat.d.ts +3 -0
- package/dist/commands/chat.d.ts.map +1 -0
- package/dist/commands/chat.js +121 -0
- package/dist/commands/chat.js.map +1 -0
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +287 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/docs.d.ts +7 -0
- package/dist/commands/docs.d.ts.map +1 -0
- package/dist/commands/docs.js +208 -0
- package/dist/commands/docs.js.map +1 -0
- package/dist/commands/generate.d.ts +9 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +130 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/init.d.ts +7 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +149 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/lineage.d.ts +9 -0
- package/dist/commands/lineage.d.ts.map +1 -0
- package/dist/commands/lineage.js +186 -0
- package/dist/commands/lineage.js.map +1 -0
- package/dist/commands/migrate.d.ts +3 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +213 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/review.d.ts +8 -0
- package/dist/commands/review.d.ts.map +1 -0
- package/dist/commands/review.js +215 -0
- package/dist/commands/review.js.map +1 -0
- package/dist/commands/scan.d.ts +18 -0
- package/dist/commands/scan.d.ts.map +1 -0
- package/dist/commands/scan.js +335 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/commands/test.d.ts +3 -0
- package/dist/commands/test.d.ts.map +1 -0
- package/dist/commands/test.js +244 -0
- package/dist/commands/test.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +232 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/analyzer.d.ts +39 -0
- package/dist/utils/analyzer.d.ts.map +1 -0
- package/dist/utils/analyzer.js +310 -0
- package/dist/utils/analyzer.js.map +1 -0
- package/dist/utils/cache.d.ts +98 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +253 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/config.d.ts +56 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +108 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/generators.d.ts +8 -0
- package/dist/utils/generators.d.ts.map +1 -0
- package/dist/utils/generators.js +265 -0
- package/dist/utils/generators.js.map +1 -0
- package/dist/utils/plugins.d.ts +94 -0
- package/dist/utils/plugins.d.ts.map +1 -0
- package/dist/utils/plugins.js +164 -0
- package/dist/utils/plugins.js.map +1 -0
- package/dist/utils/templates.d.ts +57 -0
- package/dist/utils/templates.d.ts.map +1 -0
- package/dist/utils/templates.js +458 -0
- package/dist/utils/templates.js.map +1 -0
- 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 @@
|
|
|
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 @@
|
|
|
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
|