UNPKG

cline-sdk

Version:

Comprehensive SDK for Cline - AI-powered development assistant with database integration, execution modes, and custom functions

392 lines 15.8 kB
"use strict"; /** * Database Schema Tool * * Allows the AI to inspect and understand database schemas, tables, and relationships */ Object.defineProperty(exports, "__esModule", { value: true }); exports.DatabaseSchemaTool = void 0; class DatabaseSchemaTool { constructor(dbManager) { this.dbManager = dbManager; } async execute(input) { const startTime = Date.now(); try { const { action, schemaName, tableName, searchTerm, includeData = false } = input; switch (action) { case "listSchemas": return this.listSchemas(startTime); case "listTables": return this.listTables(schemaName, startTime); case "describeTable": return this.describeTable(schemaName, tableName, includeData, startTime); case "getRelationships": return this.getRelationships(schemaName, tableName, startTime); case "searchTables": return this.searchTables(searchTerm, startTime); case "getContext": return this.getFullContext(startTime); default: return { success: false, error: `Unknown action: ${action}`, executionTime: Date.now() - startTime, }; } } catch (error) { return { success: false, error: error instanceof Error ? error.message : "Unknown error", executionTime: Date.now() - startTime, }; } } async listSchemas(startTime) { // Get fresh schema information const schemas = await this.dbManager.introspectDatabase(); const schemaList = Array.from(schemas.keys()).map((schemaName) => ({ schemaName, tableCount: schemas.get(schemaName)?.length || 0, tables: schemas.get(schemaName)?.map((t) => t.tableName) || [], })); return { success: true, data: schemaList, executionTime: Date.now() - startTime, }; } async listTables(schemaName, startTime) { const schemas = await this.dbManager.introspectDatabase(); if (schemaName) { const tables = schemas.get(schemaName); if (!tables) { return { success: false, error: `Schema '${schemaName}' not found`, executionTime: Date.now() - startTime, }; } const tableList = tables.map((table) => ({ tableName: table.tableName, description: table.description, columnCount: table.columns.length, primaryKeys: table.primaryKeys, hasRLS: table.rowLevelSecurity, permissions: table.permissions, })); return { success: true, data: tableList, executionTime: Date.now() - startTime, }; } else { // List all tables from all schemas const allTables = []; for (const [schema, tables] of schemas) { for (const table of tables) { allTables.push({ schemaName: schema, tableName: table.tableName, description: table.description, columnCount: table.columns.length, primaryKeys: table.primaryKeys, hasRLS: table.rowLevelSecurity, }); } } return { success: true, data: allTables, executionTime: Date.now() - startTime, }; } } async describeTable(schemaName, tableName, includeData, startTime) { if (!schemaName || !tableName) { return { success: false, error: "Both schemaName and tableName are required for describeTable action", executionTime: Date.now() - startTime, }; } const schemas = await this.dbManager.introspectDatabase(); const tables = schemas.get(schemaName); if (!tables) { return { success: false, error: `Schema '${schemaName}' not found`, executionTime: Date.now() - startTime, }; } const table = tables.find((t) => t.tableName === tableName); if (!table) { return { success: false, error: `Table '${tableName}' not found in schema '${schemaName}'`, executionTime: Date.now() - startTime, }; } let sampleData = []; if (includeData) { try { const sampleResult = await this.dbManager.executeQuery(`SELECT * FROM "${schemaName}"."${tableName}" LIMIT 5`); sampleData = sampleResult.data || []; } catch (error) { console.warn("Could not fetch sample data:", error); } } const tableInfo = { ...table, sampleData: includeData ? sampleData : undefined, relationships: this.getTableRelationships(table), usage: { queryHint: `To query this table: SELECT * FROM "${schemaName}"."${tableName}"`, insertHint: table.permissions.insert ? `To insert: INSERT INTO "${schemaName}"."${tableName}" (${table.columns .filter((c) => !c.isPrimaryKey || c.defaultValue) .map((c) => c.columnName) .join(", ")}) VALUES (...)` : "Insert not permitted", updateHint: table.permissions.update ? `To update: UPDATE "${schemaName}"."${tableName}" SET column = value WHERE condition` : "Update not permitted", deleteHint: table.permissions.delete ? `To delete: DELETE FROM "${schemaName}"."${tableName}" WHERE condition` : "Delete not permitted", }, }; return { success: true, data: tableInfo, executionTime: Date.now() - startTime, }; } getTableRelationships(table) { return { foreignKeys: table.foreignKeys.map((fk) => ({ column: fk.columnName, references: `${fk.referencedTable}.${fk.referencedColumn}`, constraint: fk.constraintName, onDelete: fk.onDelete, onUpdate: fk.onUpdate, })), indexes: table.indexes.map((idx) => ({ name: idx.indexName, columns: idx.columns, unique: idx.isUnique, type: idx.indexType, })), constraints: table.constraints.map((cons) => ({ name: cons.constraintName, type: cons.constraintType, columns: cons.columns, definition: cons.definition, })), }; } async getRelationships(schemaName, tableName, startTime) { const schemas = await this.dbManager.introspectDatabase(); if (schemaName && tableName) { // Get relationships for specific table const tables = schemas.get(schemaName); const table = tables?.find((t) => t.tableName === tableName); if (!table) { return { success: false, error: `Table '${tableName}' not found in schema '${schemaName}'`, executionTime: Date.now() - startTime, }; } const relationships = this.getTableRelationships(table); // Also find tables that reference this table const referencingTables = []; for (const [schema, schemaTables] of schemas) { for (const schemaTable of schemaTables) { for (const fk of schemaTable.foreignKeys) { if (fk.referencedTable === tableName) { referencingTables.push({ schema, table: schemaTable.tableName, column: fk.columnName, constraint: fk.constraintName, }); } } } } return { success: true, data: { table: `${schemaName}.${tableName}`, outgoingRelationships: relationships.foreignKeys, incomingRelationships: referencingTables, indexes: relationships.indexes, constraints: relationships.constraints, }, executionTime: Date.now() - startTime, }; } else { // Get all relationships in database const allRelationships = []; for (const [schema, tables] of schemas) { for (const table of tables) { for (const fk of table.foreignKeys) { allRelationships.push({ fromSchema: schema, fromTable: table.tableName, fromColumn: fk.columnName, toTable: fk.referencedTable, toColumn: fk.referencedColumn, constraint: fk.constraintName, onDelete: fk.onDelete, onUpdate: fk.onUpdate, }); } } } return { success: true, data: allRelationships, executionTime: Date.now() - startTime, }; } } async searchTables(searchTerm, startTime) { if (!searchTerm) { return { success: false, error: "Search term is required", executionTime: Date.now() - startTime, }; } const schemas = await this.dbManager.introspectDatabase(); const results = []; const term = searchTerm.toLowerCase(); for (const [schemaName, tables] of schemas) { for (const table of tables) { // Search in table name if (table.tableName.toLowerCase().includes(term)) { results.push({ type: "table", schemaName, tableName: table.tableName, match: "table name", description: table.description, }); } // Search in table description if (table.description && table.description.toLowerCase().includes(term)) { results.push({ type: "table", schemaName, tableName: table.tableName, match: "table description", description: table.description, }); } // Search in column names and descriptions for (const column of table.columns) { if (column.columnName.toLowerCase().includes(term)) { results.push({ type: "column", schemaName, tableName: table.tableName, columnName: column.columnName, match: "column name", dataType: column.dataType, description: column.description, }); } if (column.description && column.description.toLowerCase().includes(term)) { results.push({ type: "column", schemaName, tableName: table.tableName, columnName: column.columnName, match: "column description", dataType: column.dataType, description: column.description, }); } } } } return { success: true, data: { searchTerm, resultsCount: results.length, results: results.slice(0, 50), // Limit results }, executionTime: Date.now() - startTime, }; } async getFullContext(startTime) { const context = this.dbManager.getSchemaContext(); return { success: true, data: { contextLength: context.length, tables: await this.getTableSummary(), }, context: context, executionTime: Date.now() - startTime, }; } async getTableSummary() { const schemas = await this.dbManager.introspectDatabase(); const summary = {}; for (const [schemaName, tables] of schemas) { summary[schemaName] = tables.map((table) => ({ name: table.tableName, columns: table.columns.length, primaryKeys: table.primaryKeys.length, foreignKeys: table.foreignKeys.length, hasRLS: table.rowLevelSecurity, permissions: table.permissions, })); } return summary; } getDefinition() { return { name: "DatabaseSchema", description: "Inspect database schemas, tables, columns, and relationships. Essential for understanding the database structure before writing queries.", inputSchema: { type: "object", properties: { action: { type: "string", description: "The action to perform", enum: ["listSchemas", "listTables", "describeTable", "getRelationships", "searchTables", "getContext"], }, schemaName: { type: "string", description: "Name of the database schema (required for some actions)", }, tableName: { type: "string", description: "Name of the table (required for describeTable and getRelationships)", }, searchTerm: { type: "string", description: "Search term for searchTables action", }, includeData: { type: "boolean", description: "Whether to include sample data when describing a table", default: false, }, }, required: ["action"], }, }; } } exports.DatabaseSchemaTool = DatabaseSchemaTool; //# sourceMappingURL=database-schema-tool.js.map