yamchart 0.4.19 → 0.5.1

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.
@@ -0,0 +1,373 @@
1
+ import {
2
+ detail,
3
+ error,
4
+ header,
5
+ spinner,
6
+ success,
7
+ warning
8
+ } from "./chunk-HJVVHYVN.js";
9
+ import {
10
+ createConnector,
11
+ resolveConnection
12
+ } from "./chunk-45SP26I2.js";
13
+ import "./chunk-UDRJFEJU.js";
14
+ import "./chunk-23E6YT4S.js";
15
+ import "./chunk-DGUM43GV.js";
16
+
17
+ // src/commands/advisor.ts
18
+ import { readFile, readdir, access } from "fs/promises";
19
+ import { join } from "path";
20
+ import { parse as parseYaml } from "yaml";
21
+ import { createInterface } from "readline";
22
+ import pc from "picocolors";
23
+ async function loadCatalogMarkdown(projectDir) {
24
+ try {
25
+ const catalogPath = join(projectDir, ".yamchart", "catalog.md");
26
+ await access(catalogPath);
27
+ return readFile(catalogPath, "utf-8");
28
+ } catch {
29
+ return null;
30
+ }
31
+ }
32
+ async function getDbtProjectPath(projectDir, override) {
33
+ if (override) return override;
34
+ try {
35
+ const dbtSourcePath = join(projectDir, ".yamchart", "dbt-source.yaml");
36
+ const content = await readFile(dbtSourcePath, "utf-8");
37
+ const parsed = parseYaml(content);
38
+ if (parsed.path) {
39
+ return join(projectDir, parsed.path);
40
+ }
41
+ } catch {
42
+ }
43
+ return null;
44
+ }
45
+ async function buildContext(projectDir, options) {
46
+ const { parseModelMetadata } = await import("./dist-LJOCG7C5.js");
47
+ const modelsDir = join(projectDir, "models");
48
+ const yamchartModels = [];
49
+ try {
50
+ const files = await readdir(modelsDir, { recursive: true });
51
+ for (const file of files) {
52
+ if (!String(file).endsWith(".sql")) continue;
53
+ const content = await readFile(join(modelsDir, String(file)), "utf-8");
54
+ try {
55
+ const metadata = parseModelMetadata(content);
56
+ yamchartModels.push({
57
+ name: metadata.name,
58
+ description: metadata.description,
59
+ sql: metadata.sql,
60
+ params: metadata.params?.map((p) => ({ name: p.name, type: p.type })),
61
+ returns: metadata.returns?.map((r) => ({ name: r.name, type: r.type }))
62
+ });
63
+ } catch {
64
+ }
65
+ }
66
+ } catch {
67
+ }
68
+ const chartsDir = join(projectDir, "charts");
69
+ const yamchartCharts = [];
70
+ try {
71
+ const files = await readdir(chartsDir, { recursive: true });
72
+ for (const file of files) {
73
+ const fname = String(file);
74
+ if (!fname.endsWith(".yaml") && !fname.endsWith(".yml")) continue;
75
+ const content = await readFile(join(chartsDir, fname), "utf-8");
76
+ try {
77
+ const parsed = parseYaml(content);
78
+ if (parsed.name && parsed.source?.model) {
79
+ yamchartCharts.push({
80
+ name: parsed.name,
81
+ title: parsed.title,
82
+ model: parsed.source.model,
83
+ type: parsed.chart?.type ?? "unknown"
84
+ });
85
+ }
86
+ } catch {
87
+ }
88
+ }
89
+ } catch {
90
+ }
91
+ const dashboardsDir = join(projectDir, "dashboards");
92
+ const yamchartDashboards = [];
93
+ try {
94
+ const files = await readdir(dashboardsDir, { recursive: true });
95
+ for (const file of files) {
96
+ const fname = String(file);
97
+ if (!fname.endsWith(".yaml") && !fname.endsWith(".yml")) continue;
98
+ const content = await readFile(join(dashboardsDir, fname), "utf-8");
99
+ try {
100
+ const parsed = parseYaml(content);
101
+ if (parsed.name) {
102
+ const charts = [];
103
+ for (const row of parsed.layout?.rows ?? []) {
104
+ for (const widget of row.widgets ?? []) {
105
+ if (widget.ref) charts.push(widget.ref);
106
+ }
107
+ }
108
+ yamchartDashboards.push({
109
+ name: parsed.name,
110
+ title: parsed.title,
111
+ charts
112
+ });
113
+ }
114
+ } catch {
115
+ }
116
+ }
117
+ } catch {
118
+ }
119
+ const catalog = await loadCatalogMarkdown(projectDir);
120
+ const dbtPath = await getDbtProjectPath(projectDir, options.dbtPath);
121
+ let dbt = {
122
+ projectPath: dbtPath ?? "",
123
+ projectName: "unknown",
124
+ conventions: {
125
+ folderStructure: [],
126
+ namingPrefixes: {},
127
+ commonMaterializations: {},
128
+ schemaYmlPattern: "per-folder",
129
+ testPatterns: []
130
+ },
131
+ models: []
132
+ };
133
+ if (dbtPath) {
134
+ try {
135
+ const { detectConventions, listDbtModels } = await import("./dist-ZRRM3OWF.js");
136
+ const dbtProjectYml = await readFile(join(dbtPath, "dbt_project.yml"), "utf-8");
137
+ const dbtConfig = parseYaml(dbtProjectYml);
138
+ const conventions = await detectConventions(dbtPath);
139
+ const models = await listDbtModels(dbtPath);
140
+ dbt = {
141
+ projectPath: dbtPath,
142
+ projectName: dbtConfig.name ?? "unknown",
143
+ conventions,
144
+ models
145
+ };
146
+ } catch (err) {
147
+ warning(
148
+ `Could not read dbt project: ${err instanceof Error ? err.message : String(err)}`
149
+ );
150
+ }
151
+ }
152
+ let warehouse = null;
153
+ try {
154
+ const connection = await resolveConnection(projectDir, options.connection);
155
+ const connector = createConnector(connection, projectDir);
156
+ await connector.connect();
157
+ warehouse = {
158
+ connectionType: connection.type,
159
+ executeSql: async (sql) => {
160
+ const result = await connector.execute(sql);
161
+ return {
162
+ columns: result.columns.map((c) => c.name),
163
+ rows: result.rows
164
+ };
165
+ }
166
+ };
167
+ } catch {
168
+ }
169
+ return {
170
+ yamchart: {
171
+ models: yamchartModels,
172
+ charts: yamchartCharts,
173
+ dashboards: yamchartDashboards,
174
+ catalog
175
+ },
176
+ dbt,
177
+ warehouse
178
+ };
179
+ }
180
+ function formatProposal(proposal, conventions, buildModelPathFn) {
181
+ const path = buildModelPathFn(
182
+ proposal.name,
183
+ proposal.layer,
184
+ conventions,
185
+ proposal.subfolder
186
+ );
187
+ const lines = [
188
+ pc.bold(` ${proposal.name}`),
189
+ ` ${pc.dim(proposal.explanation)}`,
190
+ ` Path: ${pc.cyan(path)} (${proposal.materialization})`,
191
+ "",
192
+ pc.dim(" " + "\u2500".repeat(30)),
193
+ proposal.sql.split("\n").map((l) => ` ${pc.dim(l)}`).join("\n"),
194
+ pc.dim(" " + "\u2500".repeat(30))
195
+ ];
196
+ return lines.join("\n");
197
+ }
198
+ async function runAdvisor(projectDir, questionOrMode, options) {
199
+ const apiKey = process.env.ANTHROPIC_API_KEY;
200
+ if (!apiKey) {
201
+ error("ANTHROPIC_API_KEY environment variable is required");
202
+ detail("Set it in your .env file or shell environment");
203
+ process.exit(1);
204
+ }
205
+ const spin = spinner("Loading project context...");
206
+ const context = await buildContext(projectDir, options);
207
+ spin.stop();
208
+ console.log("");
209
+ header("dbt advisor");
210
+ if (context.dbt.projectPath) {
211
+ detail(
212
+ `dbt project: ${context.dbt.projectName} at ${context.dbt.projectPath}`
213
+ );
214
+ detail(
215
+ `dbt models: ${context.dbt.models.length} (layers: ${context.dbt.conventions.folderStructure.join(", ") || "none"})`
216
+ );
217
+ } else {
218
+ warning(
219
+ "No dbt project found. Run `yamchart sync-dbt --path <dbt-project>` to connect one."
220
+ );
221
+ }
222
+ detail(
223
+ `yamchart: ${context.yamchart.models.length} models, ${context.yamchart.charts.length} charts, ${context.yamchart.dashboards.length} dashboards`
224
+ );
225
+ detail(`catalog: ${context.yamchart.catalog ? "loaded" : "not synced"}`);
226
+ detail(
227
+ `warehouse: ${context.warehouse ? `connected (${context.warehouse.connectionType})` : "not connected"}`
228
+ );
229
+ console.log("");
230
+ const { AdvisorAgent, AnthropicProvider, buildModelPath } = await import("./dist-ZRRM3OWF.js");
231
+ const provider = new AnthropicProvider(apiKey);
232
+ const agent = new AdvisorAgent(provider);
233
+ const isAudit = questionOrMode === "audit";
234
+ if (isAudit) {
235
+ const top = options.top ?? 5;
236
+ const auditPrompt = `Run a comprehensive audit of this project. Return your top ${top} suggestions ranked by impact. For each, call propose_model with the full SQL.`;
237
+ const auditSpin = spinner("Running audit...");
238
+ const result = await agent.run(context, [
239
+ { role: "user", content: auditPrompt }
240
+ ]);
241
+ auditSpin.stop();
242
+ if (options.json) {
243
+ console.log(
244
+ JSON.stringify(
245
+ { text: result.response, proposals: result.proposals },
246
+ null,
247
+ 2
248
+ )
249
+ );
250
+ return;
251
+ }
252
+ console.log(result.response);
253
+ if (result.proposals.length > 0) {
254
+ console.log("");
255
+ header(`${result.proposals.length} suggestion(s):`);
256
+ for (let i = 0; i < result.proposals.length; i++) {
257
+ const proposal = result.proposals[i];
258
+ console.log(
259
+ `
260
+ ${pc.bold(`[${i + 1}]`)} ${formatProposal(proposal, context.dbt.conventions, buildModelPath)}`
261
+ );
262
+ }
263
+ await offerApply(context, result.proposals);
264
+ }
265
+ return;
266
+ }
267
+ if (questionOrMode) {
268
+ const spin2 = spinner("Thinking...");
269
+ const result = await agent.run(context, [
270
+ { role: "user", content: questionOrMode }
271
+ ]);
272
+ spin2.stop();
273
+ console.log(result.response);
274
+ if (result.proposals.length > 0) {
275
+ const { buildModelPath: bmp } = await import("./dist-ZRRM3OWF.js");
276
+ for (const proposal of result.proposals) {
277
+ console.log(
278
+ `
279
+ ${formatProposal(proposal, context.dbt.conventions, bmp)}`
280
+ );
281
+ }
282
+ await offerApply(context, result.proposals);
283
+ }
284
+ return;
285
+ }
286
+ console.log(
287
+ pc.dim(
288
+ ' Ask me anything, or type "audit" for a full analysis. Type "exit" to quit.\n'
289
+ )
290
+ );
291
+ const conversationHistory = [];
292
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
293
+ const prompt = () => new Promise((resolve) => rl.question(pc.green("> "), resolve));
294
+ while (true) {
295
+ const userInput = await prompt();
296
+ if (!userInput.trim()) continue;
297
+ if (userInput.trim().toLowerCase() === "exit") {
298
+ rl.close();
299
+ break;
300
+ }
301
+ if (userInput.trim().toLowerCase() === "audit") {
302
+ conversationHistory.push({
303
+ role: "user",
304
+ content: `Run a comprehensive audit. Return your top ${options.top ?? 5} suggestions ranked by impact.`
305
+ });
306
+ } else {
307
+ conversationHistory.push({ role: "user", content: userInput });
308
+ }
309
+ const spin3 = spinner("Thinking...");
310
+ const result = await agent.run(context, conversationHistory);
311
+ spin3.stop();
312
+ console.log("\n" + result.response);
313
+ conversationHistory.length = 0;
314
+ conversationHistory.push(...result.messages);
315
+ if (result.proposals.length > 0) {
316
+ const { buildModelPath: bmp } = await import("./dist-ZRRM3OWF.js");
317
+ for (const proposal of result.proposals) {
318
+ console.log(
319
+ `
320
+ ${formatProposal(proposal, context.dbt.conventions, bmp)}`
321
+ );
322
+ }
323
+ await offerApply(context, result.proposals);
324
+ }
325
+ console.log("");
326
+ }
327
+ }
328
+ async function offerApply(context, proposals) {
329
+ if (!context.dbt.projectPath) {
330
+ warning("No dbt project path configured \u2014 cannot write files");
331
+ return;
332
+ }
333
+ const { confirm } = await import("@inquirer/prompts");
334
+ const { writeDbtModel, buildModelPath, formatModelSql, updateSchemaYml } = await import("./dist-ZRRM3OWF.js");
335
+ for (const proposal of proposals) {
336
+ console.log("");
337
+ const shouldApply = await confirm({
338
+ message: `Apply ${proposal.name} to dbt project?`,
339
+ default: false
340
+ });
341
+ if (!shouldApply) continue;
342
+ const modelPath = buildModelPath(
343
+ proposal.name,
344
+ proposal.layer,
345
+ context.dbt.conventions,
346
+ proposal.subfolder
347
+ );
348
+ const sql = formatModelSql({
349
+ materialization: proposal.materialization,
350
+ sql: proposal.sql
351
+ });
352
+ await writeDbtModel(context.dbt.projectPath, modelPath, sql);
353
+ success(`Created ${modelPath}`);
354
+ const schemaDir = modelPath.split("/").slice(0, -1).join("/");
355
+ const schemaPath = `${schemaDir}/schema.yml`;
356
+ try {
357
+ await updateSchemaYml(context.dbt.projectPath, schemaPath, {
358
+ name: proposal.name,
359
+ description: proposal.description,
360
+ columns: proposal.columns
361
+ });
362
+ success(`Updated ${schemaPath}`);
363
+ } catch (err) {
364
+ warning(
365
+ `Could not update schema.yml: ${err instanceof Error ? err.message : String(err)}`
366
+ );
367
+ }
368
+ }
369
+ }
370
+ export {
371
+ runAdvisor
372
+ };
373
+ //# sourceMappingURL=advisor-23GCWKME.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/advisor.ts"],"sourcesContent":["import { readFile, readdir, access } from 'fs/promises';\nimport { join } from 'path';\nimport { parse as parseYaml } from 'yaml';\nimport { createInterface } from 'readline';\nimport pc from 'picocolors';\nimport * as output from '../utils/output.js';\nimport { resolveConnection, createConnector } from './connection-utils.js';\nimport type { Message, AdvisorContext, Proposal } from '@yamchart/advisor';\n\nexport interface AdvisorOptions {\n top?: number;\n json?: boolean;\n dbtPath?: string;\n connection?: string;\n}\n\nasync function loadCatalogMarkdown(projectDir: string): Promise<string | null> {\n try {\n const catalogPath = join(projectDir, '.yamchart', 'catalog.md');\n await access(catalogPath);\n return readFile(catalogPath, 'utf-8');\n } catch {\n return null;\n }\n}\n\nasync function getDbtProjectPath(projectDir: string, override?: string): Promise<string | null> {\n if (override) return override;\n\n try {\n const dbtSourcePath = join(projectDir, '.yamchart', 'dbt-source.yaml');\n const content = await readFile(dbtSourcePath, 'utf-8');\n const parsed = parseYaml(content) as { path?: string };\n if (parsed.path) {\n return join(projectDir, parsed.path);\n }\n } catch {\n // No saved dbt source config\n }\n\n return null;\n}\n\nasync function buildContext(\n projectDir: string,\n options: AdvisorOptions,\n): Promise<AdvisorContext> {\n // 1. Load yamchart models\n const { parseModelMetadata } = await import('@yamchart/query');\n const modelsDir = join(projectDir, 'models');\n const yamchartModels: AdvisorContext['yamchart']['models'] = [];\n\n try {\n const files = await readdir(modelsDir, { recursive: true });\n for (const file of files) {\n if (!String(file).endsWith('.sql')) continue;\n const content = await readFile(join(modelsDir, String(file)), 'utf-8');\n try {\n const metadata = parseModelMetadata(content);\n yamchartModels.push({\n name: metadata.name,\n description: metadata.description,\n sql: metadata.sql,\n params: metadata.params?.map((p) => ({ name: p.name, type: p.type })),\n returns: metadata.returns?.map((r) => ({ name: r.name, type: r.type })),\n });\n } catch {\n // Skip unparseable models\n }\n }\n } catch {\n // No models dir\n }\n\n // 2. Load yamchart charts\n const chartsDir = join(projectDir, 'charts');\n const yamchartCharts: AdvisorContext['yamchart']['charts'] = [];\n\n try {\n const files = await readdir(chartsDir, { recursive: true });\n for (const file of files) {\n const fname = String(file);\n if (!fname.endsWith('.yaml') && !fname.endsWith('.yml')) continue;\n const content = await readFile(join(chartsDir, fname), 'utf-8');\n try {\n const parsed = parseYaml(content) as {\n name?: string;\n title?: string;\n source?: { model?: string };\n chart?: { type?: string };\n };\n if (parsed.name && parsed.source?.model) {\n yamchartCharts.push({\n name: parsed.name,\n title: parsed.title,\n model: parsed.source.model,\n type: parsed.chart?.type ?? 'unknown',\n });\n }\n } catch {\n // Skip unparseable charts\n }\n }\n } catch {\n // No charts dir\n }\n\n // 3. Load yamchart dashboards\n const dashboardsDir = join(projectDir, 'dashboards');\n const yamchartDashboards: AdvisorContext['yamchart']['dashboards'] = [];\n\n try {\n const files = await readdir(dashboardsDir, { recursive: true });\n for (const file of files) {\n const fname = String(file);\n if (!fname.endsWith('.yaml') && !fname.endsWith('.yml')) continue;\n const content = await readFile(join(dashboardsDir, fname), 'utf-8');\n try {\n const parsed = parseYaml(content) as {\n name?: string;\n title?: string;\n layout?: { rows?: Array<{ widgets?: Array<{ ref?: string }> }> };\n };\n if (parsed.name) {\n const charts: string[] = [];\n for (const row of parsed.layout?.rows ?? []) {\n for (const widget of row.widgets ?? []) {\n if (widget.ref) charts.push(widget.ref);\n }\n }\n yamchartDashboards.push({\n name: parsed.name,\n title: parsed.title,\n charts,\n });\n }\n } catch {\n // Skip\n }\n }\n } catch {\n // No dashboards dir\n }\n\n // 4. Load catalog\n const catalog = await loadCatalogMarkdown(projectDir);\n\n // 5. Load dbt project\n const dbtPath = await getDbtProjectPath(projectDir, options.dbtPath);\n let dbt: AdvisorContext['dbt'] = {\n projectPath: dbtPath ?? '',\n projectName: 'unknown',\n conventions: {\n folderStructure: [],\n namingPrefixes: {},\n commonMaterializations: {},\n schemaYmlPattern: 'per-folder',\n testPatterns: [],\n },\n models: [],\n };\n\n if (dbtPath) {\n try {\n const { detectConventions, listDbtModels } = await import('@yamchart/advisor');\n const dbtProjectYml = await readFile(join(dbtPath, 'dbt_project.yml'), 'utf-8');\n const dbtConfig = parseYaml(dbtProjectYml) as { name?: string };\n\n const conventions = await detectConventions(dbtPath);\n const models = await listDbtModels(dbtPath);\n\n dbt = {\n projectPath: dbtPath,\n projectName: dbtConfig.name ?? 'unknown',\n conventions,\n models,\n };\n } catch (err) {\n output.warning(\n `Could not read dbt project: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n // 6. Set up warehouse connection (optional)\n let warehouse: AdvisorContext['warehouse'] = null;\n\n try {\n const connection = await resolveConnection(projectDir, options.connection);\n const connector = createConnector(connection, projectDir);\n await connector.connect();\n\n warehouse = {\n connectionType: connection.type,\n executeSql: async (sql: string) => {\n const result = await connector.execute(sql);\n return {\n columns: result.columns.map((c) => c.name),\n rows: result.rows,\n };\n },\n };\n } catch {\n // No connection available — advisor works without it\n }\n\n return {\n yamchart: {\n models: yamchartModels,\n charts: yamchartCharts,\n dashboards: yamchartDashboards,\n catalog,\n },\n dbt,\n warehouse,\n };\n}\n\nfunction formatProposal(\n proposal: Proposal,\n conventions: AdvisorContext['dbt']['conventions'],\n buildModelPathFn: (\n name: string,\n layer: string | undefined,\n conventions: AdvisorContext['dbt']['conventions'],\n subfolder?: string,\n ) => string,\n): string {\n const path = buildModelPathFn(\n proposal.name,\n proposal.layer,\n conventions,\n proposal.subfolder,\n );\n\n const lines = [\n pc.bold(` ${proposal.name}`),\n ` ${pc.dim(proposal.explanation)}`,\n ` Path: ${pc.cyan(path)} (${proposal.materialization})`,\n '',\n pc.dim(' ' + '\\u2500'.repeat(30)),\n proposal.sql\n .split('\\n')\n .map((l: string) => ` ${pc.dim(l)}`)\n .join('\\n'),\n pc.dim(' ' + '\\u2500'.repeat(30)),\n ];\n return lines.join('\\n');\n}\n\nexport async function runAdvisor(\n projectDir: string,\n questionOrMode: string | undefined,\n options: AdvisorOptions,\n): Promise<void> {\n const apiKey = process.env.ANTHROPIC_API_KEY;\n if (!apiKey) {\n output.error('ANTHROPIC_API_KEY environment variable is required');\n output.detail('Set it in your .env file or shell environment');\n process.exit(1);\n }\n\n const spin = output.spinner('Loading project context...');\n const context = await buildContext(projectDir, options);\n spin.stop();\n\n // Print startup banner\n console.log('');\n output.header('dbt advisor');\n if (context.dbt.projectPath) {\n output.detail(\n `dbt project: ${context.dbt.projectName} at ${context.dbt.projectPath}`,\n );\n output.detail(\n `dbt models: ${context.dbt.models.length} (layers: ${context.dbt.conventions.folderStructure.join(', ') || 'none'})`,\n );\n } else {\n output.warning(\n 'No dbt project found. Run `yamchart sync-dbt --path <dbt-project>` to connect one.',\n );\n }\n output.detail(\n `yamchart: ${context.yamchart.models.length} models, ${context.yamchart.charts.length} charts, ${context.yamchart.dashboards.length} dashboards`,\n );\n output.detail(`catalog: ${context.yamchart.catalog ? 'loaded' : 'not synced'}`);\n output.detail(\n `warehouse: ${context.warehouse ? `connected (${context.warehouse.connectionType})` : 'not connected'}`,\n );\n console.log('');\n\n // Create agent\n const { AdvisorAgent, AnthropicProvider, buildModelPath } = await import(\n '@yamchart/advisor'\n );\n const provider = new AnthropicProvider(apiKey);\n const agent = new AdvisorAgent(provider);\n\n const isAudit = questionOrMode === 'audit';\n\n if (isAudit) {\n // One-shot audit mode\n const top = options.top ?? 5;\n const auditPrompt = `Run a comprehensive audit of this project. Return your top ${top} suggestions ranked by impact. For each, call propose_model with the full SQL.`;\n\n const auditSpin = output.spinner('Running audit...');\n const result = await agent.run(context, [\n { role: 'user', content: auditPrompt },\n ]);\n auditSpin.stop();\n\n if (options.json) {\n console.log(\n JSON.stringify(\n { text: result.response, proposals: result.proposals },\n null,\n 2,\n ),\n );\n return;\n }\n\n console.log(result.response);\n\n if (result.proposals.length > 0) {\n console.log('');\n output.header(`${result.proposals.length} suggestion(s):`);\n\n for (let i = 0; i < result.proposals.length; i++) {\n const proposal = result.proposals[i]!;\n console.log(\n `\\n${pc.bold(`[${i + 1}]`)} ${formatProposal(proposal, context.dbt.conventions, buildModelPath)}`,\n );\n }\n\n // Offer to apply\n await offerApply(context, result.proposals);\n }\n return;\n }\n\n // Interactive mode\n if (questionOrMode) {\n // Single question mode\n const spin2 = output.spinner('Thinking...');\n const result = await agent.run(context, [\n { role: 'user', content: questionOrMode },\n ]);\n spin2.stop();\n\n console.log(result.response);\n\n if (result.proposals.length > 0) {\n const { buildModelPath: bmp } = await import('@yamchart/advisor');\n for (const proposal of result.proposals) {\n console.log(\n `\\n${formatProposal(proposal, context.dbt.conventions, bmp)}`,\n );\n }\n await offerApply(context, result.proposals);\n }\n return;\n }\n\n // Multi-turn interactive mode\n console.log(\n pc.dim(\n ' Ask me anything, or type \"audit\" for a full analysis. Type \"exit\" to quit.\\n',\n ),\n );\n\n const conversationHistory: Message[] = [];\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n\n const prompt = (): Promise<string> =>\n new Promise((resolve) => rl.question(pc.green('> '), resolve));\n\n while (true) {\n const userInput = await prompt();\n if (!userInput.trim()) continue;\n if (userInput.trim().toLowerCase() === 'exit') {\n rl.close();\n break;\n }\n\n if (userInput.trim().toLowerCase() === 'audit') {\n conversationHistory.push({\n role: 'user',\n content: `Run a comprehensive audit. Return your top ${options.top ?? 5} suggestions ranked by impact.`,\n });\n } else {\n conversationHistory.push({ role: 'user', content: userInput });\n }\n\n const spin3 = output.spinner('Thinking...');\n const result = await agent.run(context, conversationHistory);\n spin3.stop();\n\n console.log('\\n' + result.response);\n\n // Update conversation history with the full exchange\n conversationHistory.length = 0;\n conversationHistory.push(...result.messages);\n\n if (result.proposals.length > 0) {\n const { buildModelPath: bmp } = await import('@yamchart/advisor');\n for (const proposal of result.proposals) {\n console.log(\n `\\n${formatProposal(proposal, context.dbt.conventions, bmp)}`,\n );\n }\n await offerApply(context, result.proposals);\n }\n\n console.log('');\n }\n}\n\nasync function offerApply(\n context: AdvisorContext,\n proposals: Proposal[],\n): Promise<void> {\n if (!context.dbt.projectPath) {\n output.warning('No dbt project path configured \\u2014 cannot write files');\n return;\n }\n\n const { confirm } = await import('@inquirer/prompts');\n const { writeDbtModel, buildModelPath, formatModelSql, updateSchemaYml } =\n await import('@yamchart/advisor');\n\n for (const proposal of proposals) {\n console.log('');\n const shouldApply = await confirm({\n message: `Apply ${proposal.name} to dbt project?`,\n default: false,\n });\n\n if (!shouldApply) continue;\n\n const modelPath = buildModelPath(\n proposal.name,\n proposal.layer,\n context.dbt.conventions,\n proposal.subfolder,\n );\n\n const sql = formatModelSql({\n materialization: proposal.materialization,\n sql: proposal.sql,\n });\n\n await writeDbtModel(context.dbt.projectPath, modelPath, sql);\n output.success(`Created ${modelPath}`);\n\n // Update schema.yml\n const schemaDir = modelPath.split('/').slice(0, -1).join('/');\n const schemaPath = `${schemaDir}/schema.yml`;\n\n try {\n await updateSchemaYml(context.dbt.projectPath, schemaPath, {\n name: proposal.name,\n description: proposal.description,\n columns: proposal.columns,\n });\n output.success(`Updated ${schemaPath}`);\n } catch (err) {\n output.warning(\n `Could not update schema.yml: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAAA,SAAS,UAAU,SAAS,cAAc;AAC1C,SAAS,YAAY;AACrB,SAAS,SAAS,iBAAiB;AACnC,SAAS,uBAAuB;AAChC,OAAO,QAAQ;AAYf,eAAe,oBAAoB,YAA4C;AAC7E,MAAI;AACF,UAAM,cAAc,KAAK,YAAY,aAAa,YAAY;AAC9D,UAAM,OAAO,WAAW;AACxB,WAAO,SAAS,aAAa,OAAO;AAAA,EACtC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,kBAAkB,YAAoB,UAA2C;AAC9F,MAAI,SAAU,QAAO;AAErB,MAAI;AACF,UAAM,gBAAgB,KAAK,YAAY,aAAa,iBAAiB;AACrE,UAAM,UAAU,MAAM,SAAS,eAAe,OAAO;AACrD,UAAM,SAAS,UAAU,OAAO;AAChC,QAAI,OAAO,MAAM;AACf,aAAO,KAAK,YAAY,OAAO,IAAI;AAAA,IACrC;AAAA,EACF,QAAQ;AAAA,EAER;AAEA,SAAO;AACT;AAEA,eAAe,aACb,YACA,SACyB;AAEzB,QAAM,EAAE,mBAAmB,IAAI,MAAM,OAAO,oBAAiB;AAC7D,QAAM,YAAY,KAAK,YAAY,QAAQ;AAC3C,QAAM,iBAAuD,CAAC;AAE9D,MAAI;AACF,UAAM,QAAQ,MAAM,QAAQ,WAAW,EAAE,WAAW,KAAK,CAAC;AAC1D,eAAW,QAAQ,OAAO;AACxB,UAAI,CAAC,OAAO,IAAI,EAAE,SAAS,MAAM,EAAG;AACpC,YAAM,UAAU,MAAM,SAAS,KAAK,WAAW,OAAO,IAAI,CAAC,GAAG,OAAO;AACrE,UAAI;AACF,cAAM,WAAW,mBAAmB,OAAO;AAC3C,uBAAe,KAAK;AAAA,UAClB,MAAM,SAAS;AAAA,UACf,aAAa,SAAS;AAAA,UACtB,KAAK,SAAS;AAAA,UACd,QAAQ,SAAS,QAAQ,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,EAAE,KAAK,EAAE;AAAA,UACpE,SAAS,SAAS,SAAS,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,EAAE,KAAK,EAAE;AAAA,QACxE,CAAC;AAAA,MACH,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AAGA,QAAM,YAAY,KAAK,YAAY,QAAQ;AAC3C,QAAM,iBAAuD,CAAC;AAE9D,MAAI;AACF,UAAM,QAAQ,MAAM,QAAQ,WAAW,EAAE,WAAW,KAAK,CAAC;AAC1D,eAAW,QAAQ,OAAO;AACxB,YAAM,QAAQ,OAAO,IAAI;AACzB,UAAI,CAAC,MAAM,SAAS,OAAO,KAAK,CAAC,MAAM,SAAS,MAAM,EAAG;AACzD,YAAM,UAAU,MAAM,SAAS,KAAK,WAAW,KAAK,GAAG,OAAO;AAC9D,UAAI;AACF,cAAM,SAAS,UAAU,OAAO;AAMhC,YAAI,OAAO,QAAQ,OAAO,QAAQ,OAAO;AACvC,yBAAe,KAAK;AAAA,YAClB,MAAM,OAAO;AAAA,YACb,OAAO,OAAO;AAAA,YACd,OAAO,OAAO,OAAO;AAAA,YACrB,MAAM,OAAO,OAAO,QAAQ;AAAA,UAC9B,CAAC;AAAA,QACH;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AAGA,QAAM,gBAAgB,KAAK,YAAY,YAAY;AACnD,QAAM,qBAA+D,CAAC;AAEtE,MAAI;AACF,UAAM,QAAQ,MAAM,QAAQ,eAAe,EAAE,WAAW,KAAK,CAAC;AAC9D,eAAW,QAAQ,OAAO;AACxB,YAAM,QAAQ,OAAO,IAAI;AACzB,UAAI,CAAC,MAAM,SAAS,OAAO,KAAK,CAAC,MAAM,SAAS,MAAM,EAAG;AACzD,YAAM,UAAU,MAAM,SAAS,KAAK,eAAe,KAAK,GAAG,OAAO;AAClE,UAAI;AACF,cAAM,SAAS,UAAU,OAAO;AAKhC,YAAI,OAAO,MAAM;AACf,gBAAM,SAAmB,CAAC;AAC1B,qBAAW,OAAO,OAAO,QAAQ,QAAQ,CAAC,GAAG;AAC3C,uBAAW,UAAU,IAAI,WAAW,CAAC,GAAG;AACtC,kBAAI,OAAO,IAAK,QAAO,KAAK,OAAO,GAAG;AAAA,YACxC;AAAA,UACF;AACA,6BAAmB,KAAK;AAAA,YACtB,MAAM,OAAO;AAAA,YACb,OAAO,OAAO;AAAA,YACd;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AAGA,QAAM,UAAU,MAAM,oBAAoB,UAAU;AAGpD,QAAM,UAAU,MAAM,kBAAkB,YAAY,QAAQ,OAAO;AACnE,MAAI,MAA6B;AAAA,IAC/B,aAAa,WAAW;AAAA,IACxB,aAAa;AAAA,IACb,aAAa;AAAA,MACX,iBAAiB,CAAC;AAAA,MAClB,gBAAgB,CAAC;AAAA,MACjB,wBAAwB,CAAC;AAAA,MACzB,kBAAkB;AAAA,MAClB,cAAc,CAAC;AAAA,IACjB;AAAA,IACA,QAAQ,CAAC;AAAA,EACX;AAEA,MAAI,SAAS;AACX,QAAI;AACF,YAAM,EAAE,mBAAmB,cAAc,IAAI,MAAM,OAAO,oBAAmB;AAC7E,YAAM,gBAAgB,MAAM,SAAS,KAAK,SAAS,iBAAiB,GAAG,OAAO;AAC9E,YAAM,YAAY,UAAU,aAAa;AAEzC,YAAM,cAAc,MAAM,kBAAkB,OAAO;AACnD,YAAM,SAAS,MAAM,cAAc,OAAO;AAE1C,YAAM;AAAA,QACJ,aAAa;AAAA,QACb,aAAa,UAAU,QAAQ;AAAA,QAC/B;AAAA,QACA;AAAA,MACF;AAAA,IACF,SAAS,KAAK;AACZ,MAAO;AAAA,QACL,+BAA+B,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MACjF;AAAA,IACF;AAAA,EACF;AAGA,MAAI,YAAyC;AAE7C,MAAI;AACF,UAAM,aAAa,MAAM,kBAAkB,YAAY,QAAQ,UAAU;AACzE,UAAM,YAAY,gBAAgB,YAAY,UAAU;AACxD,UAAM,UAAU,QAAQ;AAExB,gBAAY;AAAA,MACV,gBAAgB,WAAW;AAAA,MAC3B,YAAY,OAAO,QAAgB;AACjC,cAAM,SAAS,MAAM,UAAU,QAAQ,GAAG;AAC1C,eAAO;AAAA,UACL,SAAS,OAAO,QAAQ,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA,UACzC,MAAM,OAAO;AAAA,QACf;AAAA,MACF;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AAEA,SAAO;AAAA,IACL,UAAU;AAAA,MACR,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ;AAAA,IACF;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,eACP,UACA,aACA,kBAMQ;AACR,QAAM,OAAO;AAAA,IACX,SAAS;AAAA,IACT,SAAS;AAAA,IACT;AAAA,IACA,SAAS;AAAA,EACX;AAEA,QAAM,QAAQ;AAAA,IACZ,GAAG,KAAK,KAAK,SAAS,IAAI,EAAE;AAAA,IAC5B,KAAK,GAAG,IAAI,SAAS,WAAW,CAAC;AAAA,IACjC,WAAW,GAAG,KAAK,IAAI,CAAC,KAAK,SAAS,eAAe;AAAA,IACrD;AAAA,IACA,GAAG,IAAI,OAAO,SAAS,OAAO,EAAE,CAAC;AAAA,IACjC,SAAS,IACN,MAAM,IAAI,EACV,IAAI,CAAC,MAAc,KAAK,GAAG,IAAI,CAAC,CAAC,EAAE,EACnC,KAAK,IAAI;AAAA,IACZ,GAAG,IAAI,OAAO,SAAS,OAAO,EAAE,CAAC;AAAA,EACnC;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,eAAsB,WACpB,YACA,gBACA,SACe;AACf,QAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,CAAC,QAAQ;AACX,IAAO,MAAM,oDAAoD;AACjE,IAAO,OAAO,+CAA+C;AAC7D,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,OAAc,QAAQ,4BAA4B;AACxD,QAAM,UAAU,MAAM,aAAa,YAAY,OAAO;AACtD,OAAK,KAAK;AAGV,UAAQ,IAAI,EAAE;AACd,EAAO,OAAO,aAAa;AAC3B,MAAI,QAAQ,IAAI,aAAa;AAC3B,IAAO;AAAA,MACL,gBAAgB,QAAQ,IAAI,WAAW,OAAO,QAAQ,IAAI,WAAW;AAAA,IACvE;AACA,IAAO;AAAA,MACL,eAAe,QAAQ,IAAI,OAAO,MAAM,aAAa,QAAQ,IAAI,YAAY,gBAAgB,KAAK,IAAI,KAAK,MAAM;AAAA,IACnH;AAAA,EACF,OAAO;AACL,IAAO;AAAA,MACL;AAAA,IACF;AAAA,EACF;AACA,EAAO;AAAA,IACL,aAAa,QAAQ,SAAS,OAAO,MAAM,YAAY,QAAQ,SAAS,OAAO,MAAM,YAAY,QAAQ,SAAS,WAAW,MAAM;AAAA,EACrI;AACA,EAAO,OAAO,YAAY,QAAQ,SAAS,UAAU,WAAW,YAAY,EAAE;AAC9E,EAAO;AAAA,IACL,cAAc,QAAQ,YAAY,cAAc,QAAQ,UAAU,cAAc,MAAM,eAAe;AAAA,EACvG;AACA,UAAQ,IAAI,EAAE;AAGd,QAAM,EAAE,cAAc,mBAAmB,eAAe,IAAI,MAAM,OAChE,oBACF;AACA,QAAM,WAAW,IAAI,kBAAkB,MAAM;AAC7C,QAAM,QAAQ,IAAI,aAAa,QAAQ;AAEvC,QAAM,UAAU,mBAAmB;AAEnC,MAAI,SAAS;AAEX,UAAM,MAAM,QAAQ,OAAO;AAC3B,UAAM,cAAc,8DAA8D,GAAG;AAErF,UAAM,YAAmB,QAAQ,kBAAkB;AACnD,UAAM,SAAS,MAAM,MAAM,IAAI,SAAS;AAAA,MACtC,EAAE,MAAM,QAAQ,SAAS,YAAY;AAAA,IACvC,CAAC;AACD,cAAU,KAAK;AAEf,QAAI,QAAQ,MAAM;AAChB,cAAQ;AAAA,QACN,KAAK;AAAA,UACH,EAAE,MAAM,OAAO,UAAU,WAAW,OAAO,UAAU;AAAA,UACrD;AAAA,UACA;AAAA,QACF;AAAA,MACF;AACA;AAAA,IACF;AAEA,YAAQ,IAAI,OAAO,QAAQ;AAE3B,QAAI,OAAO,UAAU,SAAS,GAAG;AAC/B,cAAQ,IAAI,EAAE;AACd,MAAO,OAAO,GAAG,OAAO,UAAU,MAAM,iBAAiB;AAEzD,eAAS,IAAI,GAAG,IAAI,OAAO,UAAU,QAAQ,KAAK;AAChD,cAAM,WAAW,OAAO,UAAU,CAAC;AACnC,gBAAQ;AAAA,UACN;AAAA,EAAK,GAAG,KAAK,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,eAAe,UAAU,QAAQ,IAAI,aAAa,cAAc,CAAC;AAAA,QACjG;AAAA,MACF;AAGA,YAAM,WAAW,SAAS,OAAO,SAAS;AAAA,IAC5C;AACA;AAAA,EACF;AAGA,MAAI,gBAAgB;AAElB,UAAM,QAAe,QAAQ,aAAa;AAC1C,UAAM,SAAS,MAAM,MAAM,IAAI,SAAS;AAAA,MACtC,EAAE,MAAM,QAAQ,SAAS,eAAe;AAAA,IAC1C,CAAC;AACD,UAAM,KAAK;AAEX,YAAQ,IAAI,OAAO,QAAQ;AAE3B,QAAI,OAAO,UAAU,SAAS,GAAG;AAC/B,YAAM,EAAE,gBAAgB,IAAI,IAAI,MAAM,OAAO,oBAAmB;AAChE,iBAAW,YAAY,OAAO,WAAW;AACvC,gBAAQ;AAAA,UACN;AAAA,EAAK,eAAe,UAAU,QAAQ,IAAI,aAAa,GAAG,CAAC;AAAA,QAC7D;AAAA,MACF;AACA,YAAM,WAAW,SAAS,OAAO,SAAS;AAAA,IAC5C;AACA;AAAA,EACF;AAGA,UAAQ;AAAA,IACN,GAAG;AAAA,MACD;AAAA,IACF;AAAA,EACF;AAEA,QAAM,sBAAiC,CAAC;AACxC,QAAM,KAAK,gBAAgB,EAAE,OAAO,QAAQ,OAAO,QAAQ,QAAQ,OAAO,CAAC;AAE3E,QAAM,SAAS,MACb,IAAI,QAAQ,CAAC,YAAY,GAAG,SAAS,GAAG,MAAM,IAAI,GAAG,OAAO,CAAC;AAE/D,SAAO,MAAM;AACX,UAAM,YAAY,MAAM,OAAO;AAC/B,QAAI,CAAC,UAAU,KAAK,EAAG;AACvB,QAAI,UAAU,KAAK,EAAE,YAAY,MAAM,QAAQ;AAC7C,SAAG,MAAM;AACT;AAAA,IACF;AAEA,QAAI,UAAU,KAAK,EAAE,YAAY,MAAM,SAAS;AAC9C,0BAAoB,KAAK;AAAA,QACvB,MAAM;AAAA,QACN,SAAS,8CAA8C,QAAQ,OAAO,CAAC;AAAA,MACzE,CAAC;AAAA,IACH,OAAO;AACL,0BAAoB,KAAK,EAAE,MAAM,QAAQ,SAAS,UAAU,CAAC;AAAA,IAC/D;AAEA,UAAM,QAAe,QAAQ,aAAa;AAC1C,UAAM,SAAS,MAAM,MAAM,IAAI,SAAS,mBAAmB;AAC3D,UAAM,KAAK;AAEX,YAAQ,IAAI,OAAO,OAAO,QAAQ;AAGlC,wBAAoB,SAAS;AAC7B,wBAAoB,KAAK,GAAG,OAAO,QAAQ;AAE3C,QAAI,OAAO,UAAU,SAAS,GAAG;AAC/B,YAAM,EAAE,gBAAgB,IAAI,IAAI,MAAM,OAAO,oBAAmB;AAChE,iBAAW,YAAY,OAAO,WAAW;AACvC,gBAAQ;AAAA,UACN;AAAA,EAAK,eAAe,UAAU,QAAQ,IAAI,aAAa,GAAG,CAAC;AAAA,QAC7D;AAAA,MACF;AACA,YAAM,WAAW,SAAS,OAAO,SAAS;AAAA,IAC5C;AAEA,YAAQ,IAAI,EAAE;AAAA,EAChB;AACF;AAEA,eAAe,WACb,SACA,WACe;AACf,MAAI,CAAC,QAAQ,IAAI,aAAa;AAC5B,IAAO,QAAQ,0DAA0D;AACzE;AAAA,EACF;AAEA,QAAM,EAAE,QAAQ,IAAI,MAAM,OAAO,mBAAmB;AACpD,QAAM,EAAE,eAAe,gBAAgB,gBAAgB,gBAAgB,IACrE,MAAM,OAAO,oBAAmB;AAElC,aAAW,YAAY,WAAW;AAChC,YAAQ,IAAI,EAAE;AACd,UAAM,cAAc,MAAM,QAAQ;AAAA,MAChC,SAAS,SAAS,SAAS,IAAI;AAAA,MAC/B,SAAS;AAAA,IACX,CAAC;AAED,QAAI,CAAC,YAAa;AAElB,UAAM,YAAY;AAAA,MAChB,SAAS;AAAA,MACT,SAAS;AAAA,MACT,QAAQ,IAAI;AAAA,MACZ,SAAS;AAAA,IACX;AAEA,UAAM,MAAM,eAAe;AAAA,MACzB,iBAAiB,SAAS;AAAA,MAC1B,KAAK,SAAS;AAAA,IAChB,CAAC;AAED,UAAM,cAAc,QAAQ,IAAI,aAAa,WAAW,GAAG;AAC3D,IAAO,QAAQ,WAAW,SAAS,EAAE;AAGrC,UAAM,YAAY,UAAU,MAAM,GAAG,EAAE,MAAM,GAAG,EAAE,EAAE,KAAK,GAAG;AAC5D,UAAM,aAAa,GAAG,SAAS;AAE/B,QAAI;AACF,YAAM,gBAAgB,QAAQ,IAAI,aAAa,YAAY;AAAA,QACzD,MAAM,SAAS;AAAA,QACf,aAAa,SAAS;AAAA,QACtB,SAAS,SAAS;AAAA,MACpB,CAAC;AACD,MAAO,QAAQ,WAAW,UAAU,EAAE;AAAA,IACxC,SAAS,KAAK;AACZ,MAAO;AAAA,QACL,gCAAgC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MAClF;AAAA,IACF;AAAA,EACF;AACF;","names":[]}