yamchart 0.10.1 → 0.10.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{advisor-5TJDNAY7.js → advisor-XTJQU7E4.js} +12 -12
- package/dist/{chunk-QUIDZO5G.js → chunk-2XJZJ6IA.js} +22 -6
- package/dist/chunk-2XJZJ6IA.js.map +1 -0
- package/dist/{chunk-UFDQ3C7Q.js → chunk-6ELSQNIH.js} +257 -257
- package/dist/chunk-6ELSQNIH.js.map +1 -0
- package/dist/{chunk-64CI3HSY.js → chunk-KDDO4UII.js} +70 -11
- package/dist/chunk-KDDO4UII.js.map +1 -0
- package/dist/{chunk-S2CH4HUZ.js → chunk-ODSPBOJW.js} +4 -4
- package/dist/chunk-OYBI433Z.js +1296 -0
- package/dist/chunk-OYBI433Z.js.map +1 -0
- package/dist/{chunk-ZBCQNWVN.js → chunk-PXFTWXSV.js} +5 -5
- package/dist/{chunk-5FHV22X2.js → chunk-XNMB25GT.js} +4 -4
- package/dist/{connection-utils-FEUWER5I.js → connection-utils-S7EFPTLY.js} +5 -5
- package/dist/{describe-MEP72B56.js → describe-TA2CTODC.js} +7 -7
- package/dist/{dev-H6SJZ5UA.js → dev-PHFYLJPJ.js} +2589 -1243
- package/dist/dev-PHFYLJPJ.js.map +1 -0
- package/dist/dev-supervisor-JXLVGEHP.js +43 -0
- package/dist/dev-supervisor-JXLVGEHP.js.map +1 -0
- package/dist/{dist-PPAD6KOM.js → dist-F6TVLP6C.js} +34 -4
- package/dist/{dist-XNCED7JW.js → dist-Q5ETETUU.js} +4 -4
- package/dist/{dist-5DQO6L2S.js → dist-YJOOH65Y.js} +9 -5
- package/dist/index.js +65 -42
- package/dist/index.js.map +1 -1
- package/dist/{init-SECXKD5G.js → init-TJJVKMEN.js} +24 -5
- package/dist/init-TJJVKMEN.js.map +1 -0
- package/dist/public/assets/DataView-V-E6oxy6.js +9 -0
- package/dist/public/assets/{EventManagement-BnmeJDQl.js → EventManagement-Do2xU5lV.js} +2 -2
- package/dist/public/assets/ExplorePage-Dg50mvGB.js +1 -0
- package/dist/public/assets/{LoginPage-CzaFkkjg.js → LoginPage-DmYGIXcu.js} +1 -1
- package/dist/public/assets/{PublicViewer-irjxqH6a.js → PublicViewer-CJTgc9OF.js} +1 -1
- package/dist/public/assets/{SetupWizard-ConWIcmy.js → SetupWizard-Bo-zmh43.js} +1 -1
- package/dist/public/assets/{ShareManagement-CP4wdwLR.js → ShareManagement-6Wp6b2Gp.js} +1 -1
- package/dist/public/assets/{SourceDetailView-DZS5518E.js → SourceDetailView-CTzSXhZc.js} +1 -1
- package/dist/public/assets/UserManagement-DQhB13T0.js +1 -0
- package/dist/public/assets/{index-jlfTO7f5.js → index-CY8xsfQH.js} +41 -41
- package/dist/public/assets/{index-C1X8RW4Z.css → index-Dkn-tJCT.css} +1 -1
- package/dist/public/assets/{index.es-CgnvEWi5.js → index.es-Bc3yys5L.js} +1 -1
- package/dist/public/assets/{jspdf.es.min-Cw5WefMt.js → jspdf.es.min-D94JEH-J.js} +3 -3
- package/dist/public/index.html +2 -2
- package/dist/{query-2H3YOPI2.js → query-AX4BOPMH.js} +5 -5
- package/dist/sample-4NUBJGIL.js +76 -0
- package/dist/sample-4NUBJGIL.js.map +1 -0
- package/dist/{search-IPE4ISFB.js → search-IBVVYN4I.js} +7 -7
- package/dist/{semantic-K3MYXXJI.js → semantic-33YIUMUV.js} +39 -4
- package/dist/semantic-33YIUMUV.js.map +1 -0
- package/dist/semantic-5WGVZ7HY.js +30 -0
- package/dist/{source-resolver-HZQLOODU.js → source-resolver-GYXIRIVK.js} +7 -7
- package/dist/source-resolver-GYXIRIVK.js.map +1 -0
- package/dist/{sync-warehouse-TUNULDUY.js → sync-warehouse-5NKENCOZ.js} +15 -11
- package/dist/sync-warehouse-5NKENCOZ.js.map +1 -0
- package/dist/{tables-K5NAN2WK.js → tables-QMZMYYV5.js} +7 -7
- package/dist/templates/default/docs/yamchart-reference.md +26 -0
- package/dist/{test-SRHVOXZB.js → test-6MCPGXJP.js} +19 -7
- package/dist/test-6MCPGXJP.js.map +1 -0
- package/package.json +2 -2
- package/dist/chunk-64CI3HSY.js.map +0 -1
- package/dist/chunk-QUIDZO5G.js.map +0 -1
- package/dist/chunk-UFDQ3C7Q.js.map +0 -1
- package/dist/chunk-ZIY22VO7.js +0 -1190
- package/dist/chunk-ZIY22VO7.js.map +0 -1
- package/dist/dev-H6SJZ5UA.js.map +0 -1
- package/dist/init-SECXKD5G.js.map +0 -1
- package/dist/public/assets/DataView-DUCz_96y.js +0 -9
- package/dist/public/assets/ExplorePage-kk4z9ldZ.js +0 -1
- package/dist/public/assets/UserManagement-AubGd9hl.js +0 -1
- package/dist/sample-ODUGGSFA.js +0 -42
- package/dist/sample-ODUGGSFA.js.map +0 -1
- package/dist/semantic-K3MYXXJI.js.map +0 -1
- package/dist/sync-warehouse-TUNULDUY.js.map +0 -1
- package/dist/test-SRHVOXZB.js.map +0 -1
- /package/dist/{advisor-5TJDNAY7.js.map → advisor-XTJQU7E4.js.map} +0 -0
- /package/dist/{chunk-S2CH4HUZ.js.map → chunk-ODSPBOJW.js.map} +0 -0
- /package/dist/{chunk-ZBCQNWVN.js.map → chunk-PXFTWXSV.js.map} +0 -0
- /package/dist/{chunk-5FHV22X2.js.map → chunk-XNMB25GT.js.map} +0 -0
- /package/dist/{connection-utils-FEUWER5I.js.map → connection-utils-S7EFPTLY.js.map} +0 -0
- /package/dist/{describe-MEP72B56.js.map → describe-TA2CTODC.js.map} +0 -0
- /package/dist/{dist-5DQO6L2S.js.map → dist-F6TVLP6C.js.map} +0 -0
- /package/dist/{dist-PPAD6KOM.js.map → dist-Q5ETETUU.js.map} +0 -0
- /package/dist/{dist-XNCED7JW.js.map → dist-YJOOH65Y.js.map} +0 -0
- /package/dist/{query-2H3YOPI2.js.map → query-AX4BOPMH.js.map} +0 -0
- /package/dist/{search-IPE4ISFB.js.map → search-IBVVYN4I.js.map} +0 -0
- /package/dist/{source-resolver-HZQLOODU.js.map → semantic-5WGVZ7HY.js.map} +0 -0
- /package/dist/{tables-K5NAN2WK.js.map → tables-QMZMYYV5.js.map} +0 -0
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { createRequire as __yamchartCreateRequire } from 'module'; if (!globalThis.require) globalThis.require = __yamchartCreateRequire(import.meta.url);
|
|
2
|
-
import {
|
|
3
|
-
generateCatalogJson,
|
|
4
|
-
generateCatalogMd,
|
|
5
|
-
generateWarehouseSection
|
|
6
|
-
} from "./chunk-WYS4ULBE.js";
|
|
7
2
|
import {
|
|
8
3
|
getColumnsQuery,
|
|
9
4
|
getDescribeQuery,
|
|
10
5
|
getTablesQuery
|
|
11
6
|
} from "./chunk-EAQXUGP6.js";
|
|
7
|
+
import {
|
|
8
|
+
generateCatalogJson,
|
|
9
|
+
generateCatalogMd,
|
|
10
|
+
generateWarehouseSection
|
|
11
|
+
} from "./chunk-WYS4ULBE.js";
|
|
12
12
|
import {
|
|
13
13
|
createConnector,
|
|
14
14
|
resolveConnection
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-XNMB25GT.js";
|
|
16
16
|
import {
|
|
17
17
|
detail,
|
|
18
18
|
error,
|
|
@@ -21,9 +21,9 @@ import {
|
|
|
21
21
|
success,
|
|
22
22
|
warning
|
|
23
23
|
} from "./chunk-HJVVHYVN.js";
|
|
24
|
-
import "./chunk-
|
|
25
|
-
import "./chunk-
|
|
26
|
-
import "./chunk-
|
|
24
|
+
import "./chunk-2XJZJ6IA.js";
|
|
25
|
+
import "./chunk-6ELSQNIH.js";
|
|
26
|
+
import "./chunk-OYBI433Z.js";
|
|
27
27
|
import "./chunk-7D4SUZUM.js";
|
|
28
28
|
|
|
29
29
|
// src/commands/warehouse-sync.ts
|
|
@@ -140,7 +140,11 @@ function mergeWithDbtCatalog(existingCatalog, warehouseModels) {
|
|
|
140
140
|
updatedColumns.push(wc);
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
|
-
|
|
143
|
+
const mergedModel = { ...model, columns: updatedColumns };
|
|
144
|
+
if (warehouseMatch.sampleRows && !model.sampleRows) {
|
|
145
|
+
mergedModel.sampleRows = warehouseMatch.sampleRows;
|
|
146
|
+
}
|
|
147
|
+
mergedModels.push(mergedModel);
|
|
144
148
|
consumedWarehouseTables.add(tableKey);
|
|
145
149
|
} else {
|
|
146
150
|
mergedModels.push(model);
|
|
@@ -392,4 +396,4 @@ async function runSyncWarehouse(projectDir, options) {
|
|
|
392
396
|
export {
|
|
393
397
|
runSyncWarehouse
|
|
394
398
|
};
|
|
395
|
-
//# sourceMappingURL=sync-warehouse-
|
|
399
|
+
//# sourceMappingURL=sync-warehouse-5NKENCOZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/warehouse-sync.ts","../src/commands/sync-warehouse.ts"],"sourcesContent":["/**\n * Core warehouse sync logic.\n *\n * Pure functions for diffing, grouping, building models, and merging catalogs,\n * plus an async orchestrator (syncWarehouse) that does I/O.\n */\n\nimport { readFile, writeFile, mkdir } from 'fs/promises';\nimport { join } from 'path';\nimport type { Connector } from '@yamchart/query';\nimport type { NormalizedColumnWithTable, NormalizedTable } from './introspection.js';\nimport { getTablesQuery, getColumnsQuery, getDescribeQuery } from './introspection.js';\nimport type { CatalogModel, CatalogData } from '../dbt/catalog.js';\nimport { generateCatalogMd, generateCatalogJson, generateWarehouseSection } from '../dbt/catalog.js';\n\n// ---------------------------------------------------------------------------\n// Interfaces\n// ---------------------------------------------------------------------------\n\nexport interface SyncState {\n syncedAt: string;\n connection: string;\n schemas: string[];\n skipSamples: boolean;\n tables: Record<string, { columnCount: number; lastAltered?: string }>;\n}\n\nexport interface WarehouseTable {\n qualifiedName: string; // \"SCHEMA.TABLE\"\n schema: string;\n name: string;\n type: string; // \"TABLE\" | \"VIEW\"\n columnCount: number;\n}\n\nexport interface SyncDiff {\n newTables: string[];\n changedTables: string[];\n unchangedTables: string[];\n removedTables: string[];\n}\n\nexport interface SyncWarehouseResult {\n tablesTotal: number;\n tablesNew: number;\n tablesChanged: number;\n tablesUnchanged: number;\n tablesRemoved: number;\n errors: string[];\n durationMs: number;\n}\n\nexport interface SyncWarehouseOptions {\n schemas?: string[];\n database?: string;\n skipSamples?: boolean;\n full?: boolean;\n onProgress?: (table: string, index: number, total: number) => void;\n}\n\n// ---------------------------------------------------------------------------\n// Pure functions\n// ---------------------------------------------------------------------------\n\n/**\n * Group a flat column list into a Map keyed by \"SCHEMA.TABLE\".\n */\nexport function groupColumnsByTable(\n columns: NormalizedColumnWithTable[],\n): Map<string, NormalizedColumnWithTable[]> {\n const map = new Map<string, NormalizedColumnWithTable[]>();\n for (const col of columns) {\n const key = `${col.schema}.${col.table}`;\n const existing = map.get(key);\n if (existing) {\n existing.push(col);\n } else {\n map.set(key, [col]);\n }\n }\n return map;\n}\n\n/**\n * Compare current warehouse tables against previous sync state.\n * All tables are \"new\" when prev is null.\n * Detects changed by column count mismatch.\n * Detects removed tables (in prev but not in current).\n */\nexport function diffSyncState(\n current: WarehouseTable[],\n prev: SyncState | null,\n): SyncDiff {\n const newTables: string[] = [];\n const changedTables: string[] = [];\n const unchangedTables: string[] = [];\n const removedTables: string[] = [];\n\n if (!prev) {\n return {\n newTables: current.map((t) => t.qualifiedName),\n changedTables: [],\n unchangedTables: [],\n removedTables: [],\n };\n }\n\n const currentNames = new Set(current.map((t) => t.qualifiedName));\n\n for (const table of current) {\n const prevEntry = prev.tables[table.qualifiedName];\n if (!prevEntry) {\n newTables.push(table.qualifiedName);\n } else if (prevEntry.columnCount !== table.columnCount) {\n changedTables.push(table.qualifiedName);\n } else {\n unchangedTables.push(table.qualifiedName);\n }\n }\n\n // Detect removed tables\n for (const name of Object.keys(prev.tables)) {\n if (!currentNames.has(name)) {\n removedTables.push(name);\n }\n }\n\n return { newTables, changedTables, unchangedTables, removedTables };\n}\n\n/**\n * Build CatalogModel entries from warehouse metadata.\n */\nexport function buildWarehouseModels(\n tables: WarehouseTable[],\n columnMap: Map<string, NormalizedColumnWithTable[]>,\n samples: Record<string, Record<string, unknown>[]>,\n): CatalogModel[] {\n const now = new Date().toISOString();\n\n return tables.map((table) => {\n const cols = columnMap.get(table.qualifiedName) || [];\n const sampleRows = samples[table.qualifiedName];\n\n return {\n name: table.name,\n path: '',\n description: '',\n table: table.qualifiedName,\n tags: ['warehouse'],\n meta: {},\n columns: cols.map((c) => ({\n name: c.name,\n data_type: c.type,\n description: '',\n hints: [],\n })),\n yamchartModels: [],\n source: 'warehouse' as const,\n tableType: table.type as 'TABLE' | 'VIEW',\n ...(sampleRows ? { sampleRows } : {}),\n lastSynced: now,\n };\n });\n}\n\n/**\n * Merge warehouse models into an existing dbt catalog.\n *\n * 1. If no existing catalog, create new with warehouseModels.\n * 2. Remove stale warehouse entries from existing.\n * 3. For tables in both dbt and warehouse (matched by `table` field, case-insensitive):\n * - dbt entry wins (keep it)\n * - Backfill data_type from warehouse columns where missing\n * - Add any warehouse columns that don't exist in the dbt entry\n * 4. Append remaining warehouse-only models.\n * 5. Return updated CatalogData.\n */\nexport function mergeWithDbtCatalog(\n existingCatalog: CatalogData | null,\n warehouseModels: CatalogModel[],\n): CatalogData {\n if (!existingCatalog) {\n return {\n syncedAt: new Date().toISOString(),\n source: { type: 'warehouse' },\n stats: { modelsIncluded: warehouseModels.length, modelsExcluded: 0 },\n models: warehouseModels,\n };\n }\n\n // Build a lookup of warehouse models by normalized table name\n const warehouseByTable = new Map<string, CatalogModel>();\n for (const wm of warehouseModels) {\n if (wm.table) {\n warehouseByTable.set(wm.table.toUpperCase(), wm);\n }\n }\n\n // Track which warehouse models were consumed by dbt merge\n const consumedWarehouseTables = new Set<string>();\n\n // Process existing catalog: keep non-warehouse entries, merge dbt+warehouse matches\n const mergedModels: CatalogModel[] = [];\n\n for (const model of existingCatalog.models) {\n // Remove stale warehouse entries — they'll be replaced by current warehouseModels\n if (model.source === 'warehouse') {\n continue;\n }\n\n // Check if this dbt model matches a warehouse table\n const tableKey = (model.table || '').toUpperCase();\n const warehouseMatch = tableKey ? warehouseByTable.get(tableKey) : undefined;\n\n if (warehouseMatch) {\n // dbt entry wins — backfill data_type and add missing columns\n const warehouseColMap = new Map<string, { data_type?: string; description: string; hints: string[] }>();\n for (const wc of warehouseMatch.columns) {\n warehouseColMap.set(wc.name.toLowerCase(), wc);\n }\n\n // Backfill data_type on existing dbt columns\n const updatedColumns = model.columns.map((col) => {\n const wc = warehouseColMap.get(col.name.toLowerCase());\n if (wc && !col.data_type && wc.data_type) {\n return { ...col, data_type: wc.data_type };\n }\n return col;\n });\n\n // Add warehouse columns not present in dbt entry\n const existingColNames = new Set(model.columns.map((c) => c.name.toLowerCase()));\n for (const wc of warehouseMatch.columns) {\n if (!existingColNames.has(wc.name.toLowerCase())) {\n updatedColumns.push(wc);\n }\n }\n\n // Carry sampleRows from the warehouse entry into the merged dbt model\n const mergedModel: typeof model = { ...model, columns: updatedColumns };\n if (warehouseMatch.sampleRows && !model.sampleRows) {\n mergedModel.sampleRows = warehouseMatch.sampleRows;\n }\n mergedModels.push(mergedModel);\n consumedWarehouseTables.add(tableKey);\n } else {\n mergedModels.push(model);\n }\n }\n\n // Append warehouse-only models (not consumed by dbt merge)\n for (const wm of warehouseModels) {\n const tableKey = (wm.table || '').toUpperCase();\n if (!consumedWarehouseTables.has(tableKey)) {\n mergedModels.push(wm);\n }\n }\n\n return {\n ...existingCatalog,\n syncedAt: new Date().toISOString(),\n models: mergedModels,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Async orchestrator\n// ---------------------------------------------------------------------------\n\n/**\n * Main warehouse sync orchestrator.\n *\n * 1. Fetch tables and columns from the warehouse.\n * 2. Diff against previous sync state.\n * 3. Sample new/changed tables (unless skipSamples).\n * 4. Build warehouse CatalogModel entries.\n * 5. Merge with existing catalog, write files.\n */\nexport async function syncWarehouse(\n projectDir: string,\n connector: Connector,\n connectionType: string,\n connectionName: string,\n options?: SyncWarehouseOptions,\n): Promise<SyncWarehouseResult> {\n const start = performance.now();\n const errors: string[] = [];\n\n // 1. Fetch tables\n let normalizedTables: NormalizedTable[] = [];\n\n if (connectionType === 'bigquery') {\n // BigQuery INFORMATION_SCHEMA is dataset-scoped — the dataset must be baked\n // into the query, so we run one query per dataset rather than filtering\n // client-side.\n if (!options?.schemas || options.schemas.length === 0) {\n throw new Error(\n 'BigQuery sync requires a dataset. Pass --schema <dataset> (or set config.dataset).',\n );\n }\n for (const dataset of options.schemas) {\n const q = getTablesQuery(connectionType, { schema: dataset });\n const res = await connector.execute(q.sql);\n normalizedTables.push(...q.normalize(res.rows));\n }\n } else {\n const tablesQuery = getTablesQuery(connectionType, {\n database: options?.database,\n });\n const tablesResult = await connector.execute(tablesQuery.sql);\n normalizedTables = tablesQuery.normalize(tablesResult.rows);\n\n // Filter by schemas if specified\n if (options?.schemas && options.schemas.length > 0) {\n const schemaSet = new Set(options.schemas.map((s) => s.toUpperCase()));\n normalizedTables = normalizedTables.filter((t) => schemaSet.has(t.schema.toUpperCase()));\n }\n }\n\n // 2. Fetch columns\n let allColumns: NormalizedColumnWithTable[] = [];\n\n if (connectionType === 'sqlite') {\n // SQLite: describe each table individually\n for (const table of normalizedTables) {\n try {\n const describeQuery = getDescribeQuery('sqlite', table.name);\n const descResult = await connector.execute(describeQuery.sql);\n const cols = describeQuery.normalize(descResult.rows);\n for (const col of cols) {\n allColumns.push({\n schema: table.schema,\n table: table.name,\n name: col.name,\n type: col.type,\n nullable: col.nullable,\n });\n }\n } catch (err) {\n errors.push(`Failed to describe ${table.name}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n } else if (connectionType === 'bigquery') {\n // Dataset-scoped, same as the tables query above.\n for (const dataset of options!.schemas!) {\n const q = getColumnsQuery(connectionType, { schema: dataset });\n const res = await connector.execute(q.sql);\n allColumns.push(...q.normalize(res.rows));\n }\n } else {\n const columnsQuery = getColumnsQuery(connectionType, {\n database: options?.database,\n });\n const columnsResult = await connector.execute(columnsQuery.sql);\n allColumns = columnsQuery.normalize(columnsResult.rows);\n\n // Filter columns by schemas if specified\n if (options?.schemas && options.schemas.length > 0) {\n const schemaSet = new Set(options.schemas.map((s) => s.toUpperCase()));\n allColumns = allColumns.filter((c) => schemaSet.has(c.schema.toUpperCase()));\n }\n }\n\n // Group columns by table\n const columnMap = groupColumnsByTable(allColumns);\n\n // Build WarehouseTable entries with column counts\n const warehouseTables: WarehouseTable[] = normalizedTables.map((t) => {\n const qualifiedName = t.schema ? `${t.schema}.${t.name}` : t.name;\n return {\n qualifiedName,\n schema: t.schema,\n name: t.name,\n type: t.type,\n columnCount: columnMap.get(qualifiedName)?.length || 0,\n };\n });\n\n // 3. Load previous sync state, compute diff\n let prevState: SyncState | null = null;\n if (!options?.full) {\n try {\n const raw = await readFile(join(projectDir, '.yamchart', 'warehouse-sync.json'), 'utf-8');\n prevState = JSON.parse(raw);\n } catch {\n /* no previous state */\n }\n }\n\n const diff = diffSyncState(warehouseTables, prevState);\n\n // 4. Sample new/changed tables (unless skipSamples)\n const samples: Record<string, Record<string, unknown>[]> = {};\n const tablesToSample = options?.skipSamples\n ? []\n : [...diff.newTables, ...diff.changedTables];\n\n for (let i = 0; i < tablesToSample.length; i++) {\n const tableName = tablesToSample[i]!;\n options?.onProgress?.(tableName, i + 1, tablesToSample.length);\n\n try {\n const sampleSql = `SELECT * FROM ${tableName} LIMIT 3`;\n const samplePromise = connector.execute(sampleSql);\n const timeoutPromise = new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error(`Timeout sampling ${tableName} (5s)`)), 5000),\n );\n const result = await Promise.race([samplePromise, timeoutPromise]);\n samples[tableName] = result.rows;\n } catch (err) {\n errors.push(`Failed to sample ${tableName}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n // 5. For unchanged tables: carry forward sampleRows from existing catalog\n let existingCatalog: CatalogData | null = null;\n try {\n const raw = await readFile(join(projectDir, '.yamchart', 'catalog.json'), 'utf-8');\n existingCatalog = JSON.parse(raw);\n } catch {\n /* no existing catalog */\n }\n\n if (existingCatalog && !options?.skipSamples) {\n for (const tableName of diff.unchangedTables) {\n const existingModel = existingCatalog.models.find(\n (m) => m.table?.toUpperCase() === tableName.toUpperCase() && m.source === 'warehouse',\n );\n if (existingModel?.sampleRows) {\n samples[tableName] = existingModel.sampleRows;\n }\n }\n }\n\n // 6. Build warehouse CatalogModel entries\n const warehouseModels = buildWarehouseModels(warehouseTables, columnMap, samples);\n\n // 7. Merge with existing catalog, write files\n const merged = mergeWithDbtCatalog(existingCatalog, warehouseModels);\n\n await mkdir(join(projectDir, '.yamchart'), { recursive: true });\n await writeFile(join(projectDir, '.yamchart', 'catalog.json'), generateCatalogJson(merged));\n\n const catalogMd = generateCatalogMd(merged);\n const syncDate = new Date().toISOString().split('T')[0]!;\n const warehouseSection = generateWarehouseSection(merged.models, connectionName, syncDate);\n const fullMd = warehouseSection ? catalogMd + '\\n' + warehouseSection : catalogMd;\n await writeFile(join(projectDir, '.yamchart', 'catalog.md'), fullMd);\n\n // Write sync state\n const syncState: SyncState = {\n syncedAt: new Date().toISOString(),\n connection: connectionName,\n schemas: options?.schemas || [],\n skipSamples: options?.skipSamples || false,\n tables: Object.fromEntries(\n warehouseTables.map((t) => [t.qualifiedName, { columnCount: t.columnCount }]),\n ),\n };\n await writeFile(join(projectDir, '.yamchart', 'warehouse-sync.json'), JSON.stringify(syncState, null, 2));\n\n const durationMs = Math.round(performance.now() - start);\n\n return {\n tablesTotal: warehouseTables.length,\n tablesNew: diff.newTables.length,\n tablesChanged: diff.changedTables.length,\n tablesUnchanged: diff.unchangedTables.length,\n tablesRemoved: diff.removedTables.length,\n errors,\n durationMs,\n };\n}\n","/**\n * CLI handler for `yamchart sync-warehouse`.\n *\n * Thin wrapper: resolves connection, creates connector, delegates to syncWarehouse().\n */\n\nimport * as output from '../utils/output.js';\nimport { resolveConnection, createConnector } from './connection-utils.js';\nimport { syncWarehouse } from './warehouse-sync.js';\nimport { readFile } from 'fs/promises';\nimport { join } from 'path';\nimport type { SyncState } from './warehouse-sync.js';\n\nexport interface SyncWarehouseCliOptions {\n connection?: string;\n schema?: string; // comma-separated → split into array\n database?: string;\n skipSamples?: boolean;\n refresh?: boolean;\n full?: boolean;\n json?: boolean;\n}\n\nexport async function runSyncWarehouse(\n projectDir: string,\n options: SyncWarehouseCliOptions,\n): Promise<void> {\n // Handle --refresh: load saved connection/schemas from previous sync state\n let connectionOverride = options.connection;\n let schemasOverride = options.schema;\n\n if (options.refresh) {\n try {\n const raw = await readFile(join(projectDir, '.yamchart', 'warehouse-sync.json'), 'utf-8');\n const prevState: SyncState = JSON.parse(raw);\n connectionOverride = connectionOverride || prevState.connection;\n if (!schemasOverride && prevState.schemas.length > 0) {\n schemasOverride = prevState.schemas.join(',');\n }\n if (!options.json) {\n output.info(`Re-syncing from ${prevState.connection} (${prevState.schemas.join(', ') || 'all schemas'})`);\n }\n } catch {\n output.error('No previous sync state found. Run sync-warehouse without --refresh first.');\n process.exit(1);\n }\n }\n\n // Resolve connection\n const connection = await resolveConnection(projectDir, connectionOverride);\n const connector = createConnector(connection, projectDir);\n\n let schemas = schemasOverride\n ? schemasOverride.split(',').map((s) => s.trim())\n : undefined;\n\n // BigQuery INFORMATION_SCHEMA is dataset-scoped; fall back to the connection's\n // configured dataset when no --schema is provided.\n if ((!schemas || schemas.length === 0) && connection.type === 'bigquery') {\n const dataset = (connection.config as { dataset?: string }).dataset;\n if (dataset) {\n schemas = [dataset];\n }\n }\n\n const spin = options.json ? null : output.spinner(`Connecting to ${connection.name} ...`);\n\n try {\n await connector.connect();\n\n if (spin) {\n spin.text = 'Fetching table metadata...';\n }\n\n const result = await syncWarehouse(\n projectDir,\n connector,\n connection.type,\n connection.name,\n {\n schemas,\n database: options.database,\n skipSamples: options.skipSamples,\n full: options.full,\n onProgress: spin\n ? (table, index, total) => {\n spin.text = `Sampling tables... ${index}/${total} (${table})`;\n }\n : undefined,\n },\n );\n\n if (spin) {\n spin.stop();\n }\n\n if (options.json) {\n console.log(JSON.stringify(result, null, 2));\n } else {\n const parts = [\n `${result.tablesNew} new`,\n `${result.tablesChanged} changed`,\n `${result.tablesUnchanged} unchanged`,\n ];\n if (result.tablesRemoved > 0) {\n parts.push(`${result.tablesRemoved} removed`);\n }\n output.success(\n `Synced ${result.tablesTotal} tables (${parts.join(', ')}) in ${(result.durationMs / 1000).toFixed(1)}s`,\n );\n output.detail('Written to .yamchart/catalog.json');\n\n if (result.errors.length > 0) {\n output.warning(`${result.errors.length} table(s) had sampling errors:`);\n for (const err of result.errors.slice(0, 5)) {\n output.detail(err);\n }\n if (result.errors.length > 5) {\n output.detail(`... and ${result.errors.length - 5} more`);\n }\n }\n }\n } finally {\n await connector.disconnect();\n }\n}\n"],"mappings":"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","names":["readFile","join"]}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { createRequire as __yamchartCreateRequire } from 'module'; if (!globalThis.require) globalThis.require = __yamchartCreateRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
resolveTablesSource
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-VJC24RKT.js";
|
|
4
|
+
} from "./chunk-PXFTWXSV.js";
|
|
6
5
|
import "./chunk-EAQXUGP6.js";
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-VJC24RKT.js";
|
|
7
|
+
import "./chunk-XNMB25GT.js";
|
|
8
|
+
import "./chunk-2XJZJ6IA.js";
|
|
9
|
+
import "./chunk-6ELSQNIH.js";
|
|
10
|
+
import "./chunk-OYBI433Z.js";
|
|
11
11
|
import "./chunk-7D4SUZUM.js";
|
|
12
12
|
|
|
13
13
|
// src/commands/tables.ts
|
|
@@ -28,4 +28,4 @@ async function listTables(projectDir, options) {
|
|
|
28
28
|
export {
|
|
29
29
|
listTables
|
|
30
30
|
};
|
|
31
|
-
//# sourceMappingURL=tables-
|
|
31
|
+
//# sourceMappingURL=tables-QMZMYYV5.js.map
|
|
@@ -828,6 +828,32 @@ auth:
|
|
|
828
828
|
- **RLS in SQL:** `{{ user.email }}`, `{{ user.role }}`, `{{ user.department }}` etc.
|
|
829
829
|
- **CLI recovery:** `yamchart reset-password --email user@example.com`
|
|
830
830
|
|
|
831
|
+
### Editing config from the UI (admin only)
|
|
832
|
+
|
|
833
|
+
Admins can add/edit/delete **data sources**, change the **knowledge database**, and edit
|
|
834
|
+
**AI providers** from the Data workspace. Edits are stored in a
|
|
835
|
+
runtime config layer (`<data-dir>/runtime-config.json`) that overrides Git config by name;
|
|
836
|
+
secret values (passwords, connection credentials, the knowledge DB URL) are encrypted at rest.
|
|
837
|
+
Saving a change triggers a brief automatic restart to apply it.
|
|
838
|
+
|
|
839
|
+
Set `YAMCHART_CONFIG_SECRET` (a base64-encoded 32-byte key, e.g. `openssl rand -base64 32`)
|
|
840
|
+
so encrypted secrets remain readable across restarts/redeploys. Without it, a machine-local
|
|
841
|
+
key is generated next to the runtime config file.
|
|
842
|
+
|
|
843
|
+
Git-defined connections remain read-only in the UI (edit them via Git); only
|
|
844
|
+
UI-created (runtime) sources can be edited or deleted there.
|
|
845
|
+
|
|
846
|
+
**Knowledge database.** Admins can change the knowledge-base Postgres URL under Data → Settings:
|
|
847
|
+
test the connection (runs migrations), optionally **Copy existing knowledge** into the new database
|
|
848
|
+
(hunches, blindspots, docs, embeddings — preserving ids and status), and **Rebuild embeddings now**.
|
|
849
|
+
Saving the URL restarts the server to reconnect. The URL is stored encrypted in the runtime config layer.
|
|
850
|
+
|
|
851
|
+
**AI providers.** Under Data → Settings, admins can edit the LLM providers (type, model,
|
|
852
|
+
endpoint, API key), the per-task routing (hunch generation, blindspot scan, answer, advisor),
|
|
853
|
+
and the embedding provider/model. A **Test** button does a tiny live call to verify the key +
|
|
854
|
+
endpoint. API keys are encrypted at rest; leaving a key field blank keeps the stored value.
|
|
855
|
+
Saving restarts the server to rebuild the providers.
|
|
856
|
+
|
|
831
857
|
## Project Config (`yamchart.yaml`)
|
|
832
858
|
|
|
833
859
|
```yaml
|
|
@@ -2,7 +2,7 @@ import { createRequire as __yamchartCreateRequire } from 'module'; if (!globalTh
|
|
|
2
2
|
import {
|
|
3
3
|
createConnector,
|
|
4
4
|
resolveConnection
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-XNMB25GT.js";
|
|
6
6
|
import {
|
|
7
7
|
detail,
|
|
8
8
|
error,
|
|
@@ -17,12 +17,16 @@ import {
|
|
|
17
17
|
renderTemplate,
|
|
18
18
|
resolveDynamicDefault,
|
|
19
19
|
runAll
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-2XJZJ6IA.js";
|
|
21
21
|
import {
|
|
22
|
+
expandCustomDateRange,
|
|
22
23
|
expandDatePreset,
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
expandRelativeDateRange,
|
|
25
|
+
isCustomDateRange,
|
|
26
|
+
isDatePreset,
|
|
27
|
+
isRelativeDateRange
|
|
28
|
+
} from "./chunk-6ELSQNIH.js";
|
|
29
|
+
import "./chunk-OYBI433Z.js";
|
|
26
30
|
import "./chunk-7D4SUZUM.js";
|
|
27
31
|
|
|
28
32
|
// src/commands/test.ts
|
|
@@ -70,7 +74,15 @@ function compileWithDefaults(sql, metadata, refs) {
|
|
|
70
74
|
}
|
|
71
75
|
}
|
|
72
76
|
}
|
|
73
|
-
if (
|
|
77
|
+
if (isRelativeDateRange(params.date_range)) {
|
|
78
|
+
const range = expandRelativeDateRange(params.date_range);
|
|
79
|
+
params.start_date = range.start_date;
|
|
80
|
+
params.end_date = range.end_date;
|
|
81
|
+
} else if (isCustomDateRange(params.date_range)) {
|
|
82
|
+
const range = expandCustomDateRange(params.date_range);
|
|
83
|
+
params.start_date = range.start_date;
|
|
84
|
+
params.end_date = range.end_date;
|
|
85
|
+
} else if (typeof params.date_range === "string" && isDatePreset(params.date_range)) {
|
|
74
86
|
const range = expandDatePreset(params.date_range);
|
|
75
87
|
if (range) {
|
|
76
88
|
params.start_date = range.start_date;
|
|
@@ -182,4 +194,4 @@ export {
|
|
|
182
194
|
formatTestOutput,
|
|
183
195
|
testProject
|
|
184
196
|
};
|
|
185
|
-
//# sourceMappingURL=test-
|
|
197
|
+
//# sourceMappingURL=test-6MCPGXJP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/test.ts"],"sourcesContent":["import { readFile, readdir } from 'fs/promises';\nimport { join, extname } from 'path';\nimport {\n parseModelMetadata,\n runAll,\n renderTemplate,\n createTemplateContext,\n expandDatePreset,\n isDatePreset,\n isCustomDateRange,\n expandCustomDateRange,\n isRelativeDateRange,\n expandRelativeDateRange,\n resolveDynamicDefault,\n type TestSuiteResult,\n type TestModelInput,\n} from '@yamchart/query';\nimport type { ModelMetadata } from '@yamchart/schema';\nimport * as output from '../utils/output.js';\nimport { resolveConnection, createConnector } from './connection-utils.js';\n\nexport interface TestOptions {\n connection?: string;\n json?: boolean;\n}\n\nexport interface TestResult {\n success: boolean;\n suite: TestSuiteResult;\n connectionName: string;\n}\n\nexport async function testProject(\n projectDir: string,\n modelFilter: string | undefined,\n options: TestOptions,\n): Promise<TestResult> {\n const connection = await resolveConnection(projectDir, options.connection);\n const connector = createConnector(connection, projectDir);\n\n const modelsDir = join(projectDir, 'models');\n const allModels = await loadModels(modelsDir);\n\n let modelsToTest = allModels;\n if (modelFilter) {\n modelsToTest = allModels.filter((m) => m.name === modelFilter);\n if (modelsToTest.length === 0) {\n throw new Error(`Model \"${modelFilter}\" not found`);\n }\n }\n\n // Build refs map: model name -> model name (identity for standalone execution)\n const refs: Record<string, string> = {};\n for (const m of allModels) {\n refs[m.name] = m.name;\n }\n\n const testInputs: TestModelInput[] = [];\n for (const model of modelsToTest) {\n try {\n const compiledSql = compileWithDefaults(model.sql, model.metadata, refs);\n testInputs.push({ compiledSql, metadata: model.metadata });\n } catch {\n // If compilation fails, include raw SQL so the error is reported during test execution\n testInputs.push({ compiledSql: model.sql, metadata: model.metadata });\n }\n }\n\n try {\n await connector.connect();\n const suite = await runAll(testInputs, connector);\n return { success: suite.failed === 0, suite, connectionName: connection.name };\n } finally {\n await connector.disconnect();\n }\n}\n\nfunction compileWithDefaults(\n sql: string,\n metadata: ModelMetadata,\n refs: Record<string, string>,\n): string {\n const params: Record<string, unknown> = {};\n if (metadata.params) {\n for (const p of metadata.params) {\n if (p.default !== undefined) {\n params[p.name] = resolveDynamicDefault(p.default);\n }\n }\n }\n\n // Expand date_range default into start_date/end_date — handles all three forms:\n // preset string (\"last_30_days\"), custom range object, or relative range object.\n if (isRelativeDateRange(params.date_range)) {\n const range = expandRelativeDateRange(params.date_range);\n params.start_date = range.start_date;\n params.end_date = range.end_date;\n } else if (isCustomDateRange(params.date_range)) {\n const range = expandCustomDateRange(params.date_range);\n params.start_date = range.start_date;\n params.end_date = range.end_date;\n } else if (typeof params.date_range === 'string' && isDatePreset(params.date_range)) {\n const range = expandDatePreset(params.date_range);\n if (range) {\n params.start_date = range.start_date;\n params.end_date = range.end_date;\n }\n }\n\n const context = createTemplateContext(params, refs);\n return renderTemplate(sql, context);\n}\n\ninterface LoadedModel {\n name: string;\n sql: string;\n metadata: ModelMetadata;\n}\n\nasync function loadModels(dir: string): Promise<LoadedModel[]> {\n const models: LoadedModel[] = [];\n\n let entries;\n try {\n entries = await readdir(dir, { withFileTypes: true });\n } catch {\n return models;\n }\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n const subModels = await loadModels(fullPath);\n models.push(...subModels);\n } else if (extname(entry.name) === '.sql') {\n const content = await readFile(fullPath, 'utf-8');\n try {\n const parsed = parseModelMetadata(content);\n models.push({ name: parsed.name, sql: parsed.sql, metadata: parsed });\n } catch {\n // Skip unparseable models\n }\n }\n }\n\n return models;\n}\n\nexport function formatTestOutput(result: TestResult, connectionName: string): void {\n const { suite } = result;\n\n const testedModels = suite.models.filter(\n (m) => m.schemaCheck || m.assertions.length > 0 || m.error,\n );\n\n output.header(`Testing ${suite.models.length} model(s) against ${connectionName}...`);\n\n for (const model of suite.models) {\n const hasChecks = model.schemaCheck || model.assertions.length > 0 || model.error;\n if (!hasChecks) continue;\n\n console.log(` ${model.modelName}`);\n\n if (model.error) {\n output.error(` error: ${model.error}`);\n continue;\n }\n\n if (model.schemaCheck) {\n formatSchemaCheck(model.schemaCheck);\n }\n\n for (const assertion of model.assertions) {\n formatAssertion(assertion);\n }\n\n output.newline();\n }\n\n // Summary line\n const totalChecks = suite.passed + suite.failed;\n const summary = `${testedModels.length} model(s), ${totalChecks} check(s): ${suite.passed} passed, ${suite.failed} failed`;\n const skippedSuffix = suite.skipped > 0 ? ` (${suite.skipped} skipped)` : '';\n const durationSuffix = ` (${suite.durationMs.toFixed(0)}ms)`;\n\n if (suite.failed === 0) {\n output.success(`${summary}${skippedSuffix}${durationSuffix}`);\n } else {\n output.error(`${summary}${skippedSuffix}${durationSuffix}`);\n }\n}\n\nfunction formatSchemaCheck(check: NonNullable<TestSuiteResult['models'][0]['schemaCheck']>): void {\n if (check.passed) {\n output.success(` schema: returns expected columns (${check.expectedColumns.join(', ')})`);\n return;\n }\n\n const issues: string[] = [];\n if (check.missingColumns.length > 0) {\n issues.push(`missing: ${check.missingColumns.join(', ')}`);\n }\n for (const m of check.typeMismatches) {\n issues.push(`${m.column}: expected ${m.expected}, got ${m.actual}`);\n }\n output.error(` schema: ${issues.join('; ')}`);\n}\n\nfunction formatAssertion(assertion: TestSuiteResult['models'][0]['assertions'][0]): void {\n const label = extractAssertionLabel(assertion.sql);\n\n if (assertion.warning) {\n output.warning(` ${label}`);\n output.detail(` ${assertion.warning}`);\n }\n\n if (assertion.error) {\n output.error(` test: ${label} -- error`);\n output.detail(` ${assertion.error}`);\n } else if (assertion.passed) {\n output.success(` test: ${label}`);\n } else {\n output.error(` test: ${label} -- ${assertion.violationCount} failing row(s)`);\n if (assertion.sampleViolations) {\n for (const row of assertion.sampleViolations.slice(0, 3)) {\n output.detail(` ${JSON.stringify(row)}`);\n }\n }\n }\n}\n\nfunction extractAssertionLabel(sql: string): string {\n const whereMatch = sql.match(/WHERE\\s+(.+?)$/i);\n if (whereMatch?.[1]) {\n const clause = whereMatch[1].trim();\n return clause.length > 60 ? clause.slice(0, 57) + '...' : clause;\n }\n return sql.length > 60 ? sql.slice(0, 57) + '...' : sql;\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yamchart",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.3",
|
|
4
4
|
"description": "Git-native business intelligence dashboards",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -76,8 +76,8 @@
|
|
|
76
76
|
"@yamchart/advisor": "0.1.0",
|
|
77
77
|
"@yamchart/auth-local": "0.1.0",
|
|
78
78
|
"@yamchart/chat": "0.1.0",
|
|
79
|
-
"@yamchart/query": "0.1.2",
|
|
80
79
|
"@yamchart/config": "0.1.2",
|
|
80
|
+
"@yamchart/query": "0.1.2",
|
|
81
81
|
"@yamchart/schema": "0.1.2",
|
|
82
82
|
"@yamchart/server": "0.1.2"
|
|
83
83
|
},
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../packages/advisor/src/dbt/knowledge.ts","../../../packages/advisor/src/dbt/project.ts","../../../packages/advisor/src/tools.ts","../../../packages/advisor/src/agent.ts","../../../packages/advisor/src/providers/anthropic.ts","../../../packages/advisor/src/providers/openai-compatible.ts","../../../packages/advisor/src/providers/factory.ts","../../../packages/advisor/src/providers/embedder.ts","../../../packages/advisor/src/dbt/writer.ts"],"sourcesContent":["import { readFile } from 'fs/promises';\nimport { join, dirname } from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst KNOWLEDGE_DIR = join(__dirname, '..', 'knowledge');\n\nexport const KNOWLEDGE_TOPICS = [\n 'materializations',\n 'incremental',\n 'macros',\n 'testing',\n 'naming',\n 'performance',\n 'sources',\n] as const;\n\nexport type KnowledgeTopic = (typeof KNOWLEDGE_TOPICS)[number];\n\nexport async function loadKnowledge(topic: string): Promise<string> {\n if (!KNOWLEDGE_TOPICS.includes(topic as KnowledgeTopic)) {\n return `Unknown topic: \"${topic}\". Available topics: ${KNOWLEDGE_TOPICS.join(', ')}`;\n }\n\n const filePath = join(KNOWLEDGE_DIR, `${topic}.md`);\n return readFile(filePath, 'utf-8');\n}\n\nexport function getKnowledgeOverview(): string {\n return `You have access to a dbt knowledge base via the get_knowledge tool. Available topics:\n${KNOWLEDGE_TOPICS.map((t) => `- ${t}`).join('\\n')}\n\nUse get_knowledge when you need specifics on materializations, incremental strategies, Jinja macros, testing, naming conventions, or performance optimization.`;\n}\n","import { readFile } from 'fs/promises';\nimport { join, basename, relative, dirname } from 'path';\nimport fg from 'fast-glob';\nimport { parse as parseYaml } from 'yaml';\n\nexport interface DbtConventions {\n folderStructure: string[];\n namingPrefixes: Record<string, string>;\n commonMaterializations: Record<string, string>;\n schemaYmlPattern: 'per-folder' | 'per-model' | 'single-file';\n testPatterns: string[];\n}\n\nexport interface DbtModelInfo {\n name: string;\n path: string;\n layer: string;\n materialization: string;\n folder: string;\n}\n\ninterface DbtProjectYml {\n name: string;\n 'model-paths'?: string[];\n model_paths?: string[];\n}\n\nasync function getModelPaths(projectPath: string): Promise<string[]> {\n const content = await readFile(join(projectPath, 'dbt_project.yml'), 'utf-8');\n const parsed = parseYaml(content) as DbtProjectYml;\n return parsed['model-paths'] ?? parsed.model_paths ?? ['models'];\n}\n\nfunction extractMaterialization(sql: string): string {\n const match = sql.match(/materialized\\s*=\\s*['\"](\\w+)['\"]/);\n return match?.[1] ?? 'view';\n}\n\nfunction detectLayer(relativePath: string): string {\n const parts = relativePath.split('/');\n return parts[0] ?? 'unknown';\n}\n\nfunction detectPrefix(\n models: Array<{ name: string; layer: string }>\n): Record<string, string> {\n const prefixesByLayer = new Map<string, Map<string, number>>();\n\n for (const model of models) {\n const underscoreIdx = model.name.indexOf('_');\n if (underscoreIdx === -1) continue;\n const prefix = model.name.slice(0, underscoreIdx + 1);\n\n if (!prefixesByLayer.has(model.layer)) {\n prefixesByLayer.set(model.layer, new Map());\n }\n const counts = prefixesByLayer.get(model.layer)!;\n counts.set(prefix, (counts.get(prefix) ?? 0) + 1);\n }\n\n const result: Record<string, string> = {};\n for (const [layer, counts] of prefixesByLayer) {\n let maxCount = 0;\n let maxPrefix = '';\n for (const [prefix, count] of counts) {\n if (count > maxCount) {\n maxCount = count;\n maxPrefix = prefix;\n }\n }\n if (maxPrefix && maxCount >= 2) {\n result[layer] = maxPrefix;\n }\n }\n\n return result;\n}\n\nfunction detectMaterializations(\n models: Array<{ layer: string; materialization: string }>\n): Record<string, string> {\n const matByLayer = new Map<string, Map<string, number>>();\n\n for (const model of models) {\n if (!matByLayer.has(model.layer)) {\n matByLayer.set(model.layer, new Map());\n }\n const counts = matByLayer.get(model.layer)!;\n counts.set(model.materialization, (counts.get(model.materialization) ?? 0) + 1);\n }\n\n const result: Record<string, string> = {};\n for (const [layer, counts] of matByLayer) {\n let maxCount = 0;\n let maxMat = '';\n for (const [mat, count] of counts) {\n if (count > maxCount) {\n maxCount = count;\n maxMat = mat;\n }\n }\n if (maxMat) result[layer] = maxMat;\n }\n\n return result;\n}\n\nasync function detectSchemaYmlPattern(\n projectPath: string,\n modelPaths: string[]\n): Promise<'per-folder' | 'per-model' | 'single-file'> {\n const ymlFiles: string[] = [];\n for (const mp of modelPaths) {\n const pattern = join(projectPath, mp, '**/*.yml');\n const files = await fg(pattern, { ignore: ['**/node_modules/**'] });\n ymlFiles.push(...files);\n }\n\n if (ymlFiles.length === 0) return 'per-folder';\n if (ymlFiles.length === 1) return 'single-file';\n\n const dirs = new Set(ymlFiles.map((f) => dirname(f)));\n return dirs.size > 1 ? 'per-folder' : 'single-file';\n}\n\nexport async function detectConventions(projectPath: string): Promise<DbtConventions> {\n const modelPaths = await getModelPaths(projectPath);\n const models = await listDbtModels(projectPath);\n\n const folders = [...new Set(models.map((m) => m.layer))].filter((l) => l !== 'unknown');\n\n return {\n folderStructure: folders,\n namingPrefixes: detectPrefix(models),\n commonMaterializations: detectMaterializations(models),\n schemaYmlPattern: await detectSchemaYmlPattern(projectPath, modelPaths),\n testPatterns: [],\n };\n}\n\nexport async function listDbtModels(projectPath: string): Promise<DbtModelInfo[]> {\n const modelPaths = await getModelPaths(projectPath);\n const models: DbtModelInfo[] = [];\n\n for (const mp of modelPaths) {\n const pattern = join(projectPath, mp, '**/*.sql');\n const files = await fg(pattern, { ignore: ['**/node_modules/**'] });\n\n for (const file of files) {\n const relPath = relative(join(projectPath, mp), file);\n const name = basename(file, '.sql');\n const sql = await readFile(file, 'utf-8');\n const materialization = extractMaterialization(sql);\n const layer = detectLayer(relPath);\n const folder = dirname(relPath);\n\n models.push({ name, path: relPath, layer, materialization, folder });\n }\n }\n\n return models;\n}\n\nexport async function readDbtModel(\n projectPath: string,\n modelName: string\n): Promise<string | null> {\n const models = await listDbtModels(projectPath);\n const model = models.find((m) => m.name === modelName);\n if (!model) return null;\n\n const modelPaths = await getModelPaths(projectPath);\n for (const mp of modelPaths) {\n const fullPath = join(projectPath, mp, model.path);\n try {\n return await readFile(fullPath, 'utf-8');\n } catch {\n continue;\n }\n }\n return null;\n}\n","import type { ToolDefinition } from './providers/types.js';\nimport type { AdvisorContext } from './context.js';\nimport { loadKnowledge } from './dbt/knowledge.js';\nimport { readDbtModel } from './dbt/project.js';\nimport { SemanticModelBuilder, SemanticQueryCompiler } from '@yamchart/query';\nimport type { SemanticFilter } from '@yamchart/schema';\n\nexport type { AdvisorContext } from './context.js';\n\nexport const TOOL_DEFINITIONS: ToolDefinition[] = [\n // Read-only tools\n {\n name: 'list_yamchart_models',\n description: 'List all yamchart SQL models with names, descriptions, parameters, and return columns',\n parameters: { type: 'object', properties: {}, required: [] },\n },\n {\n name: 'list_charts',\n description: 'List all yamchart charts showing which models they reference and their chart type',\n parameters: { type: 'object', properties: {}, required: [] },\n },\n {\n name: 'get_catalog',\n description: 'Get the dbt catalog (.yamchart/catalog.md) with upstream table schemas and column metadata',\n parameters: { type: 'object', properties: {}, required: [] },\n },\n {\n name: 'get_warehouse_table',\n description: 'Get detailed column metadata and sample rows for a specific warehouse table from the cached catalog. Instant — reads from local catalog, no live query. Use the fully-qualified name (e.g. RAW.PAYMENTS).',\n parameters: {\n type: 'object',\n properties: {\n table: { type: 'string', description: 'Fully-qualified table name (e.g. RAW.PAYMENTS, STAGING.CUSTOMERS)' },\n },\n required: ['table'],\n },\n },\n {\n name: 'get_warehouse_tables',\n description: 'Get detailed metadata for multiple warehouse tables at once from the cached catalog.',\n parameters: {\n type: 'object',\n properties: {\n tables: { type: 'array', items: { type: 'string' }, description: 'List of fully-qualified table names' },\n },\n required: ['tables'],\n },\n },\n {\n name: 'introspect_warehouse',\n description: 'Query the live warehouse. Use for discovering raw tables not yet modeled in dbt.',\n parameters: {\n type: 'object',\n properties: {\n sql: { type: 'string', description: 'SQL query to execute (e.g. SHOW TABLES, SELECT * FROM information_schema.columns)' },\n },\n required: ['sql'],\n },\n },\n {\n name: 'sample_data',\n description: 'Preview rows from a table in the warehouse',\n parameters: {\n type: 'object',\n properties: {\n table: { type: 'string', description: 'Table name (can be schema-qualified)' },\n limit: { type: 'number', description: 'Number of rows to return (default: 5)' },\n },\n required: ['table'],\n },\n },\n {\n name: 'read_dbt_model',\n description: 'Read the full SQL source of an existing dbt model',\n parameters: {\n type: 'object',\n properties: {\n model_name: { type: 'string', description: 'Name of the dbt model to read' },\n },\n required: ['model_name'],\n },\n },\n {\n name: 'list_dbt_models',\n description: 'List all dbt models with their layer (staging/intermediate/marts), materialization, and folder',\n parameters: { type: 'object', properties: {}, required: [] },\n },\n {\n name: 'get_knowledge',\n description: 'Look up dbt reference documentation on a topic: materializations, incremental, macros, testing, naming, performance',\n parameters: {\n type: 'object',\n properties: {\n topic: { type: 'string', description: 'Topic name: materializations, incremental, macros, testing, naming, performance' },\n },\n required: ['topic'],\n },\n },\n {\n name: 'list_semantic_entities',\n description: 'List all semantic entities (auto-generated from the catalog) with their measures and dimensions. Use this to understand what data is available for structured queries without writing SQL.',\n parameters: { type: 'object', properties: {}, required: [] },\n },\n {\n name: 'semantic_query',\n description: 'Run a structured query against the semantic layer. Specify an entity, measures to aggregate, and optional dimensions to group by. Returns the compiled SQL.',\n parameters: {\n type: 'object',\n properties: {\n entity: { type: 'string', description: 'Entity name (e.g. fct_revenue)' },\n measures: { type: 'array', items: { type: 'string' }, description: 'Measure names to aggregate' },\n dimensions: { type: 'array', items: { type: 'string' }, description: 'Dimension names to group by (optional)' },\n filters: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n dimension: { type: 'string' },\n operator: { type: 'string', description: 'equals, not_equals, contains, gt, gte, lt, lte, in, not_in' },\n value: { description: 'Filter value (string, number, or array for in/not_in)' },\n },\n required: ['dimension', 'operator', 'value'],\n },\n description: 'Optional filters',\n },\n limit: { type: 'number', description: 'Row limit (default: 100)' },\n },\n required: ['entity', 'measures'],\n },\n },\n\n // Action tools\n {\n name: 'propose_model',\n description: 'Propose a new dbt model. Call this when you have a suggestion to present to the user.',\n parameters: {\n type: 'object',\n properties: {\n name: { type: 'string', description: 'Model name (snake_case)' },\n description: { type: 'string', description: 'One-line description of what this model does' },\n sql: { type: 'string', description: 'Full SQL for the model (may include Jinja)' },\n layer: { type: 'string', description: 'dbt layer: staging, intermediate, or marts' },\n materialization: { type: 'string', description: 'Materialization: view, table, or incremental' },\n explanation: { type: 'string', description: 'Why this model is useful (2-3 sentences)' },\n subfolder: { type: 'string', description: 'Optional subfolder within the layer (e.g. \"stripe\", \"core\")' },\n columns: {\n type: 'array',\n description: 'Column definitions for schema.yml',\n items: {\n type: 'object',\n properties: {\n name: { type: 'string' },\n description: { type: 'string' },\n tests: { type: 'array', items: { type: 'string' } },\n },\n required: ['name'],\n },\n },\n },\n required: ['name', 'description', 'sql', 'layer', 'materialization', 'explanation'],\n },\n },\n {\n name: 'write_dbt_model',\n description: 'Write a proposed dbt model SQL file to the dbt project. Only call after proposing and user confirmation.',\n parameters: {\n type: 'object',\n properties: {\n name: { type: 'string', description: 'Model name (must match a previous proposal)' },\n },\n required: ['name'],\n },\n },\n {\n name: 'update_schema_yml',\n description: 'Add or update the model entry in the appropriate schema.yml file. Only call after writing the model.',\n parameters: {\n type: 'object',\n properties: {\n name: { type: 'string', description: 'Model name (must match a written model)' },\n },\n required: ['name'],\n },\n },\n];\n\nexport async function executeToolCall(\n context: AdvisorContext,\n toolName: string,\n input: Record<string, unknown>\n): Promise<string> {\n switch (toolName) {\n case 'list_yamchart_models':\n return JSON.stringify(\n context.yamchart.models.map((m) => ({\n name: m.name,\n description: m.description,\n params: m.params,\n returns: m.returns,\n }))\n );\n\n case 'list_charts':\n return JSON.stringify(\n context.yamchart.charts.map((c) => ({\n name: c.name,\n title: c.title,\n model: c.model,\n type: c.type,\n }))\n );\n\n case 'get_catalog':\n return context.yamchart.catalog ?? 'No dbt catalog found. Run `yamchart sync-dbt` to create one.';\n\n case 'get_warehouse_table': {\n if (!context.yamchart.catalogJson) {\n return 'Warehouse catalog not available. Run `yamchart sync-warehouse` to populate it.';\n }\n const tableName = (input.table as string).toUpperCase();\n const match = context.yamchart.catalogJson.models.find(\n (m) => (m.table || '').toUpperCase() === tableName || m.name.toUpperCase() === tableName\n );\n if (!match) {\n return JSON.stringify({ error: `Table not found in catalog: ${input.table}. Use get_catalog to see available tables.` });\n }\n return JSON.stringify({\n name: match.name,\n table: match.table,\n tableType: match.tableType,\n source: match.source,\n columns: match.columns,\n sampleRows: match.sampleRows || [],\n });\n }\n\n case 'get_warehouse_tables': {\n if (!context.yamchart.catalogJson) {\n return 'Warehouse catalog not available. Run `yamchart sync-warehouse` to populate it.';\n }\n const tableNames = (input.tables as string[]).map((t) => t.toUpperCase());\n const results = tableNames.map((name) => {\n const match = context.yamchart.catalogJson!.models.find(\n (m) => (m.table || '').toUpperCase() === name || m.name.toUpperCase() === name\n );\n if (!match) {\n return { error: `Not found: ${name}` };\n }\n return {\n name: match.name,\n table: match.table,\n tableType: match.tableType,\n source: match.source,\n columns: match.columns,\n sampleRows: match.sampleRows || [],\n };\n });\n return JSON.stringify(results);\n }\n\n case 'introspect_warehouse': {\n if (!context.warehouse) {\n return 'Warehouse introspection not available. No database connection configured.';\n }\n const sql = input.sql as string;\n try {\n const result = await context.warehouse.executeSql(sql);\n return JSON.stringify(result);\n } catch (err) {\n return JSON.stringify({ error: err instanceof Error ? err.message : String(err) });\n }\n }\n\n case 'sample_data': {\n if (!context.warehouse) {\n return 'Warehouse not available. No database connection configured.';\n }\n const table = input.table as string;\n const limit = (input.limit as number) ?? 5;\n try {\n const result = await context.warehouse.executeSql(\n `SELECT * FROM ${table} LIMIT ${limit}`\n );\n return JSON.stringify(result);\n } catch (err) {\n return JSON.stringify({ error: err instanceof Error ? err.message : String(err) });\n }\n }\n\n case 'read_dbt_model': {\n const modelName = input.model_name as string;\n const sql = await readDbtModel(context.dbt.projectPath, modelName);\n if (!sql) {\n return JSON.stringify({ error: `Model not found: ${modelName}` });\n }\n return JSON.stringify({ name: modelName, sql });\n }\n\n case 'list_dbt_models':\n return JSON.stringify(\n context.dbt.models.map((m) => ({\n name: m.name,\n path: m.path,\n layer: m.layer,\n materialization: m.materialization,\n folder: m.folder,\n }))\n );\n\n case 'get_knowledge': {\n const topic = input.topic as string;\n return loadKnowledge(topic);\n }\n\n case 'list_semantic_entities': {\n if (!context.yamchart.catalogJson) {\n return 'No catalog available. Run `yamchart sync-warehouse` or `yamchart sync-dbt` to populate it.';\n }\n const builder = new SemanticModelBuilder();\n const model = builder.build({ catalog: { models: context.yamchart.catalogJson.models } });\n return JSON.stringify(\n model.entities.map((e) => ({\n name: e.name,\n source_table: e.source_table,\n description: e.description,\n measures: e.measures.map((m) => ({ name: m.name, type: m.type })),\n dimensions: e.dimensions.map((d) => ({ name: d.name, type: d.type })),\n }))\n );\n }\n\n case 'semantic_query': {\n if (!context.yamchart.catalogJson) {\n return JSON.stringify({ error: 'No catalog available. Run `yamchart sync-warehouse` or `yamchart sync-dbt` to populate it.' });\n }\n try {\n const builder = new SemanticModelBuilder();\n const model = builder.build({ catalog: { models: context.yamchart.catalogJson.models } });\n const compiler = new SemanticQueryCompiler();\n const sql = compiler.compile(model, {\n model: input.entity as string,\n measures: (input.measures as string[]) || [],\n dimensions: (input.dimensions as string[]) || [],\n filters: (input.filters as SemanticFilter[]) || [],\n limit: (input.limit as number) || 100,\n });\n return JSON.stringify({ sql, entity: input.entity });\n } catch (err) {\n return JSON.stringify({ error: err instanceof Error ? err.message : String(err) });\n }\n }\n\n case 'propose_model':\n return JSON.stringify({\n status: 'proposed',\n name: input.name,\n description: input.description,\n sql: input.sql,\n layer: input.layer,\n materialization: input.materialization,\n explanation: input.explanation,\n subfolder: input.subfolder,\n columns: input.columns,\n });\n\n case 'write_dbt_model':\n // Actual writing happens in the CLI command, not here.\n // The agent tool just signals intent — the CLI confirms with the user.\n return JSON.stringify({\n status: 'pending_confirmation',\n name: input.name,\n message: 'Model write requested. Waiting for user confirmation.',\n });\n\n case 'update_schema_yml':\n return JSON.stringify({\n status: 'pending_confirmation',\n name: input.name,\n message: 'Schema.yml update requested. Waiting for user confirmation.',\n });\n\n default:\n return JSON.stringify({ error: `Unknown tool: ${toolName}` });\n }\n}\n","import type {\n LLMProvider,\n Message,\n ContentBlock,\n TextBlock,\n ToolUseBlock,\n ToolResultBlock,\n} from './providers/types.js';\nimport type { AdvisorContext } from './context.js';\nimport { TOOL_DEFINITIONS, executeToolCall } from './tools.js';\nimport { getKnowledgeOverview } from './dbt/knowledge.js';\n\nexport interface Proposal {\n name: string;\n description: string;\n sql: string;\n layer: string;\n materialization: string;\n explanation: string;\n subfolder?: string;\n columns?: Array<{ name: string; description?: string; tests?: string[] }>;\n}\n\nexport interface AgentResult {\n response: string;\n proposals: Proposal[];\n messages: Message[];\n}\n\nexport const SYSTEM_PROMPT = `You are a dbt advisor for yamchart projects. You analyze the BI layer (yamchart models, charts, dashboards) and the data engineering layer (dbt models, warehouse tables) to suggest improvements to dbt models.\n\nThink like a senior data engineer. Your goals:\n- Understand what the BI layer needs (charts, filters, drill-downs, date ranges)\n- Identify gaps in the dbt project (missing models, incomplete staging, opportunities for pre-aggregation)\n- Suggest new dbt models that serve the BI layer better\n- Follow the project's existing conventions (folder structure, naming, materializations)\n\nWhen proposing models:\n- Match the project's naming conventions (detect and follow existing prefixes like stg_, fct_, dim_)\n- Place models in the correct layer and folder\n- Choose appropriate materializations\n- Include column descriptions and tests in proposals\n- Explain WHY the model helps, not just WHAT it does\n- Use ref() and source() macros correctly\n\nWhen working with cross-database tables:\n- If a table lives outside the primary analytics database, recommend creating a dbt source definition in sources.yml\n- Follow the pattern: source → staging model → downstream refs\n- Use get_knowledge('sources') for detailed source patterns\n- Check the warehouse catalog (get_warehouse_tables) to identify tables in other databases\n\nWhen in audit mode:\n- Evaluate: coverage gaps, convention violations, materialization mismatches, missing tests\n- Rank suggestions by impact\n- Be concise — focus on actionable improvements\n\n${getKnowledgeOverview()}\n\nWhen you have a suggestion, call propose_model with the full SQL and metadata. Do NOT call write_dbt_model or update_schema_yml directly — the user will be prompted to confirm before writing.`;\n\nconst MAX_TOOL_ROUNDS = 15;\n\nexport class AdvisorAgent {\n private provider: LLMProvider;\n\n constructor(provider: LLMProvider) {\n this.provider = provider;\n }\n\n async run(\n context: AdvisorContext,\n userMessages: Message[]\n ): Promise<AgentResult> {\n const proposals: Proposal[] = [];\n const messages: Message[] = [...userMessages];\n\n // Build context-specific system prompt\n const systemPrompt = this.buildSystemPrompt(context);\n\n for (let round = 0; round < MAX_TOOL_ROUNDS; round++) {\n const response = await this.provider.chat({\n system: systemPrompt,\n messages,\n tools: TOOL_DEFINITIONS,\n });\n\n const textBlocks = response.content.filter(\n (b): b is TextBlock => b.type === 'text'\n );\n const toolUseBlocks = response.content.filter(\n (b): b is ToolUseBlock => b.type === 'tool_use'\n );\n\n // No tool calls — return final text response\n if (toolUseBlocks.length === 0) {\n const responseText = textBlocks.map((b) => b.text).join('\\n');\n return { response: responseText, proposals, messages };\n }\n\n // Add assistant message with tool calls\n messages.push({ role: 'assistant', content: response.content });\n\n // Execute each tool call\n const toolResults: ContentBlock[] = [];\n for (const toolUse of toolUseBlocks) {\n const result = await executeToolCall(context, toolUse.name, toolUse.input);\n\n // Collect proposals\n if (toolUse.name === 'propose_model') {\n const parsed = JSON.parse(result) as Record<string, unknown>;\n proposals.push({\n name: parsed.name as string,\n description: parsed.description as string,\n sql: toolUse.input.sql as string,\n layer: toolUse.input.layer as string,\n materialization: toolUse.input.materialization as string,\n explanation: toolUse.input.explanation as string,\n subfolder: toolUse.input.subfolder as string | undefined,\n columns: toolUse.input.columns as Proposal['columns'],\n });\n }\n\n toolResults.push({\n type: 'tool_result',\n tool_use_id: toolUse.id,\n content: result,\n } as ToolResultBlock);\n }\n\n // Add tool results as user message\n messages.push({ role: 'user', content: toolResults });\n }\n\n // Hit max rounds — return what we have\n return {\n response: 'Reached maximum tool call rounds. Here are the suggestions gathered so far.',\n proposals,\n messages,\n };\n }\n\n private buildSystemPrompt(context: AdvisorContext): string {\n const parts = [SYSTEM_PROMPT];\n\n // Add project summary\n parts.push(`\\n## Current Project Summary`);\n parts.push(`- Yamchart: ${context.yamchart.models.length} SQL models, ${context.yamchart.charts.length} charts, ${context.yamchart.dashboards.length} dashboards`);\n parts.push(`- dbt project: \"${context.dbt.projectName}\" at ${context.dbt.projectPath}`);\n parts.push(`- dbt models: ${context.dbt.models.length} (layers: ${context.dbt.conventions.folderStructure.join(', ') || 'none detected'})`);\n\n if (Object.keys(context.dbt.conventions.namingPrefixes).length > 0) {\n const prefixes = Object.entries(context.dbt.conventions.namingPrefixes)\n .map(([layer, prefix]) => `${layer}: ${prefix}`)\n .join(', ');\n parts.push(`- Naming prefixes: ${prefixes}`);\n }\n\n if (Object.keys(context.dbt.conventions.commonMaterializations).length > 0) {\n const mats = Object.entries(context.dbt.conventions.commonMaterializations)\n .map(([layer, mat]) => `${layer}: ${mat}`)\n .join(', ');\n parts.push(`- Materializations: ${mats}`);\n }\n\n parts.push(`- Schema.yml pattern: ${context.dbt.conventions.schemaYmlPattern}`);\n parts.push(`- Catalog: ${context.yamchart.catalog ? 'available' : 'not synced'}`);\n parts.push(`- Warehouse: ${context.warehouse ? `connected (${context.warehouse.connectionType})` : 'not connected'}`);\n\n return parts.join('\\n');\n }\n}\n","import Anthropic from '@anthropic-ai/sdk';\nimport type {\n LLMProvider,\n LLMResponse,\n Message,\n ToolDefinition,\n ContentBlock,\n TextBlock,\n ToolUseBlock,\n} from './types.js';\n\nexport class AnthropicProvider implements LLMProvider {\n private client: Anthropic;\n private model: string;\n\n constructor(apiKey: string, model: string = 'claude-sonnet-4-5-20250929') {\n this.client = new Anthropic({ apiKey });\n this.model = model;\n }\n\n async chat(options: {\n system: string;\n messages: Message[];\n tools: ToolDefinition[];\n maxTokens?: number;\n }): Promise<LLMResponse> {\n const tools: Anthropic.Tool[] = options.tools.map((t) => ({\n name: t.name,\n description: t.description,\n input_schema: {\n type: 'object' as const,\n properties: t.parameters.properties,\n required: t.parameters.required ?? [],\n },\n }));\n\n const messages: Anthropic.MessageParam[] = options.messages.map((m) => {\n if (typeof m.content === 'string') {\n return { role: m.role, content: m.content };\n }\n const blocks: Anthropic.ContentBlockParam[] = m.content.map((block) => {\n if (block.type === 'text') {\n return { type: 'text' as const, text: (block as TextBlock).text };\n }\n if (block.type === 'tool_use') {\n const tu = block as ToolUseBlock;\n return { type: 'tool_use' as const, id: tu.id, name: tu.name, input: tu.input };\n }\n if (block.type === 'tool_result') {\n const tr = block as { type: 'tool_result'; tool_use_id: string; content: string };\n return { type: 'tool_result' as const, tool_use_id: tr.tool_use_id, content: tr.content };\n }\n throw new Error(`Unknown block type: ${block.type}`);\n });\n return { role: m.role, content: blocks };\n });\n\n const response = await this.client.messages.create({\n model: this.model,\n max_tokens: options.maxTokens ?? 4096,\n system: options.system,\n tools,\n messages,\n });\n\n const content: ContentBlock[] = response.content.map((block) => {\n if (block.type === 'text') {\n return { type: 'text', text: block.text } as TextBlock;\n }\n if (block.type === 'tool_use') {\n return {\n type: 'tool_use',\n id: block.id,\n name: block.name,\n input: block.input as Record<string, unknown>,\n } as ToolUseBlock;\n }\n throw new Error(`Unexpected block type: ${block.type}`);\n });\n\n return {\n content,\n stopReason: response.stop_reason === 'tool_use' ? 'tool_use' : 'end',\n };\n }\n}\n","// packages/advisor/src/providers/openai-compatible.ts\nimport OpenAI from 'openai';\nimport type {\n LLMProvider,\n LLMResponse,\n Message,\n ToolDefinition,\n ContentBlock,\n TextBlock,\n ToolUseBlock,\n} from './types.js';\n\nexport interface OpenAICompatibleOptions {\n baseURL: string;\n apiKey?: string;\n model: string;\n}\n\nexport class OpenAICompatibleProvider implements LLMProvider {\n private client: OpenAI;\n private model: string;\n\n constructor(opts: OpenAICompatibleOptions) {\n this.client = new OpenAI({ baseURL: opts.baseURL, apiKey: opts.apiKey ?? 'not-needed' });\n this.model = opts.model;\n }\n\n async chat(options: {\n system: string;\n messages: Message[];\n tools: ToolDefinition[];\n maxTokens?: number;\n }): Promise<LLMResponse> {\n const tools = options.tools.map((t) => ({\n type: 'function' as const,\n function: {\n name: t.name,\n description: t.description,\n parameters: {\n type: 'object',\n properties: t.parameters.properties,\n required: t.parameters.required ?? [],\n },\n },\n }));\n\n const messages: OpenAI.Chat.ChatCompletionMessageParam[] = [\n { role: 'system', content: options.system },\n ];\n\n for (const m of options.messages) {\n if (typeof m.content === 'string') {\n messages.push({ role: m.role, content: m.content });\n continue;\n }\n // Anthropic-style content blocks → OpenAI shape.\n const text = m.content\n .filter((b): b is TextBlock => b.type === 'text')\n .map((b) => b.text)\n .join('\\n');\n const toolUses = m.content.filter((b): b is ToolUseBlock => b.type === 'tool_use');\n const toolResults = m.content.filter(\n (b): b is { type: 'tool_result'; tool_use_id: string; content: string } =>\n b.type === 'tool_result',\n );\n\n if (m.role === 'assistant' && toolUses.length) {\n messages.push({\n role: 'assistant',\n content: text || null,\n tool_calls: toolUses.map((tu) => ({\n id: tu.id,\n type: 'function',\n function: { name: tu.name, arguments: JSON.stringify(tu.input) },\n })),\n });\n } else if (toolResults.length) {\n // tool results become separate `tool` messages\n for (const tr of toolResults) {\n messages.push({ role: 'tool', tool_call_id: tr.tool_use_id, content: tr.content });\n }\n if (text) messages.push({ role: m.role, content: text });\n } else {\n messages.push({ role: m.role, content: text });\n }\n }\n\n const response = await this.client.chat.completions.create({\n model: this.model,\n max_tokens: options.maxTokens ?? 4096,\n messages,\n ...(tools.length ? { tools } : {}),\n });\n\n const choice = response.choices[0];\n const msg = choice?.message;\n const content: ContentBlock[] = [];\n\n if (msg?.content) {\n content.push({ type: 'text', text: msg.content } as TextBlock);\n }\n\n for (const tc of msg?.tool_calls ?? []) {\n let input: Record<string, unknown> = {};\n try {\n input = JSON.parse(tc.function.arguments || '{}');\n } catch {\n input = {};\n }\n content.push({ type: 'tool_use', id: tc.id, name: tc.function.name, input } as ToolUseBlock);\n }\n\n return {\n content,\n stopReason: choice?.finish_reason === 'tool_calls' ? 'tool_use' : 'end',\n };\n }\n}\n","// packages/advisor/src/providers/factory.ts\nimport type { LLMProvider } from './types.js';\nimport { AnthropicProvider } from './anthropic.js';\nimport { OpenAICompatibleProvider } from './openai-compatible.js';\n\nexport type ProviderKind = 'anthropic' | 'openai' | 'gemini' | 'ollama' | 'ollama_cloud' | 'openai_compatible';\nexport interface ProviderConfig { provider: ProviderKind; model?: string; base_url?: string; api_key_var?: string; }\nexport interface AiConfig { default?: string; providers?: Record<string, ProviderConfig>; tasks?: Record<string, string>; }\nexport type AiTask = 'hunch_generation' | 'advisor' | 'blindspot_scan' | 'answer';\n\nfunction requireKey(env: NodeJS.ProcessEnv, name: string): string {\n const v = env[name];\n if (!v) throw new Error(`Missing ${name} for the configured LLM provider.`);\n return v;\n}\nfunction requireModel(cfg: ProviderConfig): string {\n if (!cfg.model) throw new Error(`The \"${cfg.provider}\" provider requires a model.`);\n return cfg.model;\n}\n\nexport function buildProvider(cfg: ProviderConfig, env: NodeJS.ProcessEnv): LLMProvider {\n switch (cfg.provider) {\n case 'anthropic':\n return new AnthropicProvider(requireKey(env, cfg.api_key_var ?? 'ANTHROPIC_API_KEY'), cfg.model);\n case 'openai':\n return new OpenAICompatibleProvider({ baseURL: cfg.base_url ?? 'https://api.openai.com/v1', apiKey: requireKey(env, cfg.api_key_var ?? 'OPENAI_API_KEY'), model: requireModel(cfg) });\n case 'gemini':\n return new OpenAICompatibleProvider({ baseURL: cfg.base_url ?? 'https://generativelanguage.googleapis.com/v1beta/openai/', apiKey: requireKey(env, cfg.api_key_var ?? (env.GEMINI_API_KEY ? 'GEMINI_API_KEY' : 'GOOGLE_API_KEY')), model: requireModel(cfg) });\n case 'ollama':\n return new OpenAICompatibleProvider({ baseURL: cfg.base_url ?? 'http://localhost:11434/v1', apiKey: 'ollama', model: requireModel(cfg) });\n case 'ollama_cloud':\n return new OpenAICompatibleProvider({ baseURL: cfg.base_url ?? 'https://ollama.com/v1', apiKey: requireKey(env, cfg.api_key_var ?? 'OLLAMA_API_KEY'), model: requireModel(cfg) });\n case 'openai_compatible':\n if (!cfg.base_url) throw new Error('openai_compatible requires base_url.');\n return new OpenAICompatibleProvider({ baseURL: cfg.base_url, apiKey: cfg.api_key_var ? requireKey(env, cfg.api_key_var) : 'not-needed', model: requireModel(cfg) });\n }\n}\n\nexport function createProviderForTask(config: AiConfig | undefined, task: AiTask, env: NodeJS.ProcessEnv): LLMProvider {\n if (!config) return new AnthropicProvider(requireKey(env, 'ANTHROPIC_API_KEY'));\n const name = config.tasks?.[task] ?? config.default;\n const cfg = name ? config.providers?.[name] : undefined;\n if (!cfg) return new AnthropicProvider(requireKey(env, 'ANTHROPIC_API_KEY'));\n return buildProvider(cfg, env);\n}\n","import OpenAI from 'openai';\nimport type { ProviderConfig } from './factory.js';\n\nexport interface Embedder { model: string; baseURL: string; embed(texts: string[]): Promise<number[][]>; }\n\nconst PRESET_BASE: Record<string, string> = {\n ollama: 'http://localhost:11434/v1',\n ollama_cloud: 'https://ollama.com/v1',\n openai: 'https://api.openai.com/v1',\n gemini: 'https://generativelanguage.googleapis.com/v1beta/openai/',\n};\n\n// Resolve the API key per provider, mirroring buildProvider's preset logic so\n// e.g. Gemini reads GEMINI_API_KEY (not OPENAI_API_KEY). Ollama (local) needs no key.\nfunction resolveKey(c: ProviderConfig, env: NodeJS.ProcessEnv): string {\n if (c.api_key_var) return env[c.api_key_var] ?? 'not-needed';\n switch (c.provider) {\n case 'ollama': return 'ollama';\n case 'gemini': return env.GEMINI_API_KEY ?? env.GOOGLE_API_KEY ?? 'not-needed';\n case 'ollama_cloud': return env.OLLAMA_API_KEY ?? 'not-needed';\n case 'openai': return env.OPENAI_API_KEY ?? 'not-needed';\n default: return env.OPENAI_API_KEY ?? 'not-needed';\n }\n}\n\nexport function createEmbedder(cfg: ProviderConfig | undefined, env: NodeJS.ProcessEnv): Embedder | undefined {\n const c = cfg ?? { provider: 'ollama' as const, model: 'nomic-embed-text' };\n const model = c.model ?? 'nomic-embed-text';\n const baseURL = c.base_url ?? PRESET_BASE[c.provider] ?? 'http://localhost:11434/v1';\n const apiKey = resolveKey(c, env);\n const client = new OpenAI({ baseURL, apiKey });\n return {\n model, baseURL, client, // `client` is internal; cast in tests\n async embed(texts: string[]): Promise<number[][]> {\n if (texts.length === 0) return [];\n const res = await (this as unknown as { client: OpenAI }).client.embeddings.create({ model, input: texts });\n return res.data.map((d) => d.embedding as number[]);\n },\n } as Embedder & { client: OpenAI };\n}\n","import { readFile, writeFile, mkdir, access } from 'fs/promises';\nimport { join, dirname } from 'path';\nimport { parse as parseYaml, stringify as stringifyYaml } from 'yaml';\nimport type { DbtConventions } from './project.js';\n\nexport function buildModelPath(\n modelName: string,\n layer: string | undefined,\n conventions: DbtConventions,\n subfolder?: string\n): string {\n const effectiveLayer = layer ?? (conventions.folderStructure.includes('marts') ? 'marts' : conventions.folderStructure[0] ?? 'models');\n const parts = ['models', effectiveLayer];\n if (subfolder) parts.push(subfolder);\n parts.push(`${modelName}.sql`);\n return parts.join('/');\n}\n\nexport function formatModelSql(options: {\n materialization: string;\n tags?: string[];\n sql: string;\n description?: string;\n}): string {\n const lines: string[] = [];\n\n // Only add config block for non-default materializations\n if (options.materialization !== 'view') {\n const configParts: string[] = [`materialized='${options.materialization}'`];\n if (options.tags && options.tags.length > 0) {\n configParts.push(`tags=[${options.tags.map((t) => `'${t}'`).join(', ')}]`);\n }\n lines.push(`{{\\n config(\\n ${configParts.join(',\\n ')}\\n )\\n}}\\n`);\n }\n\n lines.push(options.sql);\n return lines.join('\\n');\n}\n\nexport interface SchemaColumn {\n name: string;\n description?: string;\n tests?: string[];\n}\n\nexport function buildSchemaYmlEntry(options: {\n name: string;\n description: string;\n columns?: SchemaColumn[];\n}): string {\n const model: Record<string, unknown> = {\n name: options.name,\n description: options.description,\n };\n\n if (options.columns && options.columns.length > 0) {\n model.columns = options.columns.map((col) => {\n const entry: Record<string, unknown> = { name: col.name };\n if (col.description) entry.description = col.description;\n if (col.tests && col.tests.length > 0) entry.tests = col.tests;\n return entry;\n });\n }\n\n return stringifyYaml({ models: [model] }, { indent: 2 });\n}\n\nexport async function writeDbtModel(\n projectPath: string,\n modelPath: string,\n sql: string\n): Promise<string> {\n const fullPath = join(projectPath, modelPath);\n await mkdir(dirname(fullPath), { recursive: true });\n await writeFile(fullPath, sql + '\\n', 'utf-8');\n return fullPath;\n}\n\nexport async function updateSchemaYml(\n projectPath: string,\n schemaPath: string,\n entry: { name: string; description: string; columns?: SchemaColumn[] }\n): Promise<string> {\n const fullPath = join(projectPath, schemaPath);\n\n let existing: { version?: number; models?: Array<Record<string, unknown>> } = {\n version: 2,\n models: [],\n };\n\n try {\n await access(fullPath);\n const content = await readFile(fullPath, 'utf-8');\n existing = parseYaml(content) as typeof existing;\n if (!existing.models) existing.models = [];\n } catch {\n // File doesn't exist, start fresh\n await mkdir(dirname(fullPath), { recursive: true });\n }\n\n // Check if model already exists\n const idx = existing.models!.findIndex(\n (m) => m.name === entry.name\n );\n\n const modelEntry: Record<string, unknown> = {\n name: entry.name,\n description: entry.description,\n };\n if (entry.columns && entry.columns.length > 0) {\n modelEntry.columns = entry.columns.map((col) => {\n const c: Record<string, unknown> = { name: col.name };\n if (col.description) c.description = col.description;\n if (col.tests && col.tests.length > 0) c.tests = col.tests;\n return c;\n });\n }\n\n if (idx >= 0) {\n existing.models![idx] = modelEntry;\n } else {\n existing.models!.push(modelEntry);\n }\n\n await writeFile(fullPath, stringifyYaml(existing, { indent: 2 }), 'utf-8');\n return fullPath;\n}\n"],"mappings":"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","names":["readFile","join","dirname","OpenAI","readFile","join","dirname","parseYaml"]}
|