UNPKG

cline-sdk

Version:

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

366 lines 15.6 kB
"use strict"; /** * Table Knowledge Management System * * Allows registering detailed business context and semantics for database tables * that the AI can use to better understand and work with the data. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.TableKnowledgeManager = void 0; class TableKnowledgeManager { constructor(dbManager) { this.knowledgeBase = new Map(); this.dbManager = dbManager; } /** * Register detailed knowledge about a table */ addTable(knowledge, options = {}) { if (!knowledge.schemaName || !knowledge.tableName) { throw new Error("schemaName and tableName are required"); } const tableKey = `${knowledge.schemaName}.${knowledge.tableName}`; // Set defaults const fullKnowledge = { schemaName: knowledge.schemaName, tableName: knowledge.tableName, businessName: knowledge.businessName || knowledge.tableName, description: knowledge.description || "", businessPurpose: knowledge.businessPurpose || "", domain: knowledge.domain || "general", columns: knowledge.columns || [], relationships: knowledge.relationships || {}, businessRules: knowledge.businessRules || [], commonUseCases: knowledge.commonUseCases || [], dataLifecycle: knowledge.dataLifecycle || {}, performance: knowledge.performance || { primaryUsePattern: "balanced", expectedSize: "medium", }, security: knowledge.security || { accessLevel: "internal", requiresRLS: false, auditingRequired: false, encryptionRequired: false, }, tags: knowledge.tags || [], lastUpdated: new Date(), version: knowledge.version || "1.0.0", }; // Merge with existing if requested if (options.mergeWithExisting && this.knowledgeBase.has(tableKey)) { const existing = this.knowledgeBase.get(tableKey); fullKnowledge.columns = this.mergeColumns(existing.columns, fullKnowledge.columns); fullKnowledge.businessRules = [...existing.businessRules, ...fullKnowledge.businessRules]; fullKnowledge.commonUseCases = [...existing.commonUseCases, ...fullKnowledge.commonUseCases]; fullKnowledge.tags = [...new Set([...existing.tags, ...fullKnowledge.tags])]; } this.knowledgeBase.set(tableKey, fullKnowledge); console.log(`📚 Table knowledge registered: ${tableKey}`); console.log(`🏷️ Business Name: ${fullKnowledge.businessName}`); console.log(`📝 Purpose: ${fullKnowledge.businessPurpose}`); console.log(`🏗️ Domain: ${fullKnowledge.domain}`); } /** * Add column-specific knowledge */ addColumn(schemaName, tableName, columnKnowledge) { const tableKey = `${schemaName}.${tableName}`; const table = this.knowledgeBase.get(tableKey); if (!table) { throw new Error(`Table ${tableKey} not found. Register the table first with addTable()`); } // Remove existing column with same name table.columns = table.columns.filter((col) => col.name !== columnKnowledge.name); // Add new column knowledge table.columns.push(columnKnowledge); table.lastUpdated = new Date(); console.log(`📊 Column knowledge added: ${tableKey}.${columnKnowledge.name}`); } /** * Register multiple tables at once */ addTables(tables, options = {}) { for (const table of tables) { this.addTable(table, options); } } /** * Get knowledge for a specific table */ getTableKnowledge(schemaName, tableName) { const tableKey = `${schemaName}.${tableName}`; return this.knowledgeBase.get(tableKey); } /** * Get all registered tables */ getAllTables() { return Array.from(this.knowledgeBase.values()); } /** * Search tables by domain, tags, or keywords */ searchTables(query) { const results = []; for (const table of this.knowledgeBase.values()) { let matches = true; if (query.domain && table.domain !== query.domain) { matches = false; } if (query.tags && !query.tags.some((tag) => table.tags.includes(tag))) { matches = false; } if (query.keyword) { const keyword = query.keyword.toLowerCase(); const searchText = `${table.businessName} ${table.description} ${table.businessPurpose}`.toLowerCase(); if (!searchText.includes(keyword)) { matches = false; } } if (query.businessPurpose) { const purpose = query.businessPurpose.toLowerCase(); if (!table.businessPurpose.toLowerCase().includes(purpose)) { matches = false; } } if (matches) { results.push(table); } } return results; } /** * Generate AI-friendly context from registered knowledge */ generateKnowledgeContext(includeOnlyTables) { let context = "# Database Business Knowledge\n\n"; const tables = includeOnlyTables ? this.getAllTables().filter((t) => includeOnlyTables.includes(`${t.schemaName}.${t.tableName}`)) : this.getAllTables(); if (tables.length === 0) { return context + "No table knowledge has been registered.\n"; } // Group by domain const domains = new Map(); for (const table of tables) { if (!domains.has(table.domain)) { domains.set(table.domain, []); } domains.get(table.domain).push(table); } for (const [domain, domainTables] of domains) { context += `## Domain: ${domain}\n\n`; for (const table of domainTables) { context += `### ${table.businessName} (${table.schemaName}.${table.tableName})\n`; context += `**Purpose:** ${table.businessPurpose}\n`; context += `**Description:** ${table.description}\n\n`; // Columns if (table.columns.length > 0) { context += "**Columns:**\n"; for (const col of table.columns) { context += `- **${col.name}** (${col.dataType}): ${col.description}\n`; context += ` - Business Purpose: ${col.businessPurpose}\n`; if (col.examples && col.examples.length > 0) { context += ` - Examples: ${col.examples.join(", ")}\n`; } if (col.relationships) { context += ` - Relationship: ${col.relationships.relationshipType} with ${col.relationships.referencesTable}\n`; } if (col.privacy?.isPII) { context += ` - ⚠️ Contains PII (Personal Identifiable Information)\n`; } if (col.businessRules && col.businessRules.length > 0) { context += ` - Business Rules: ${col.businessRules.join("; ")}\n`; } context += "\n"; } } // Business Rules if (table.businessRules.length > 0) { context += "**Business Rules:**\n"; for (const rule of table.businessRules) { context += `- ${rule.rule}: ${rule.description}\n`; } context += "\n"; } // Common Use Cases if (table.commonUseCases.length > 0) { context += "**Common Use Cases:**\n"; for (const useCase of table.commonUseCases) { context += `- **${useCase.useCase}** (${useCase.frequency || "unknown frequency"}): ${useCase.description}\n`; if (useCase.exampleQuery) { context += ` - Example: \`${useCase.exampleQuery}\`\n`; } } context += "\n"; } // Relationships if (table.relationships.parentTables || table.relationships.childTables) { context += "**Relationships:**\n"; if (table.relationships.parentTables) { context += `- Parent Tables: ${table.relationships.parentTables.join(", ")}\n`; } if (table.relationships.childTables) { context += `- Child Tables: ${table.relationships.childTables.join(", ")}\n`; } if (table.relationships.description) { context += `- Relationship Description: ${table.relationships.description}\n`; } context += "\n"; } // Security & Privacy context += `**Security Level:** ${table.security.accessLevel}\n`; if (table.security.requiresRLS) { context += "- ⚠️ Requires Row Level Security\n"; } if (table.security.auditingRequired) { context += "- 📋 Auditing Required\n"; } if (table.security.encryptionRequired) { context += "- 🔒 Encryption Required\n"; } // Performance Hints if (table.performance.primaryUsePattern) { context += `**Performance Pattern:** ${table.performance.primaryUsePattern}\n`; } if (table.performance.expectedSize) { context += `**Expected Size:** ${table.performance.expectedSize}\n`; } // Tags if (table.tags.length > 0) { context += `**Tags:** ${table.tags.join(", ")}\n`; } context += "\n---\n\n"; } } return context; } /** * Export knowledge as JSON */ exportKnowledge() { const knowledge = Object.fromEntries(this.knowledgeBase); return JSON.stringify(knowledge, null, 2); } /** * Import knowledge from JSON */ importKnowledge(jsonData) { try { const knowledge = JSON.parse(jsonData); this.knowledgeBase = new Map(Object.entries(knowledge)); console.log(`📥 Imported knowledge for ${this.knowledgeBase.size} tables`); } catch (error) { throw new Error(`Failed to import knowledge: ${error instanceof Error ? error.message : "Unknown error"}`); } } /** * Clear all registered knowledge */ clearKnowledge() { this.knowledgeBase.clear(); console.log("🗑️ All table knowledge cleared"); } /** * Get summary statistics */ getStats() { const tables = this.getAllTables(); const domains = [...new Set(tables.map((t) => t.domain))]; const securityLevels = {}; const allTags = []; for (const table of tables) { securityLevels[table.security.accessLevel] = (securityLevels[table.security.accessLevel] || 0) + 1; allTags.push(...table.tags); } const tagCounts = allTags.reduce((acc, tag) => { acc[tag] = (acc[tag] || 0) + 1; return acc; }, {}); const mostCommonTags = Object.entries(tagCounts) .sort(([, a], [, b]) => b - a) .slice(0, 10) .map(([tag]) => tag); return { totalTables: tables.length, totalColumns: tables.reduce((sum, t) => sum + t.columns.length, 0), domains, securityLevels, mostCommonTags, }; } /** * Validate knowledge against actual database schema (if dbManager is available) */ async validateKnowledge(schemaName, tableName) { const issues = []; const suggestions = []; if (!this.dbManager) { issues.push("No database manager available for validation"); return { valid: false, issues, suggestions }; } const knowledge = this.getTableKnowledge(schemaName, tableName); if (!knowledge) { issues.push(`No knowledge registered for table ${schemaName}.${tableName}`); return { valid: false, issues, suggestions }; } try { // Get actual schema from database const schemas = await this.dbManager.introspectDatabase(); const schema = schemas.get(schemaName); const actualTable = schema?.find((t) => t.tableName === tableName); if (!actualTable) { issues.push(`Table ${schemaName}.${tableName} does not exist in database`); return { valid: false, issues, suggestions }; } // Validate columns const actualColumns = new Set(actualTable.columns.map((c) => c.columnName)); const knowledgeColumns = new Set(knowledge.columns.map((c) => c.name)); // Missing columns in knowledge for (const actualCol of actualColumns) { if (!knowledgeColumns.has(actualCol)) { suggestions.push(`Consider adding knowledge for column: ${actualCol}`); } } // Columns in knowledge but not in database for (const knowledgeCol of knowledgeColumns) { if (!actualColumns.has(knowledgeCol)) { issues.push(`Column ${knowledgeCol} exists in knowledge but not in database`); } } // Validate data types for (const col of knowledge.columns) { const actualCol = actualTable.columns.find((c) => c.columnName === col.name); if (actualCol && actualCol.dataType !== col.dataType) { issues.push(`Data type mismatch for ${col.name}: knowledge says ${col.dataType}, database has ${actualCol.dataType}`); } } } catch (error) { issues.push(`Validation error: ${error instanceof Error ? error.message : "Unknown error"}`); } return { valid: issues.length === 0, issues, suggestions, }; } mergeColumns(existing, newColumns) { const merged = [...existing]; for (const newCol of newColumns) { const existingIndex = merged.findIndex((col) => col.name === newCol.name); if (existingIndex >= 0) { // Merge column data merged[existingIndex] = { ...merged[existingIndex], ...newCol }; } else { merged.push(newCol); } } return merged; } } exports.TableKnowledgeManager = TableKnowledgeManager; //# sourceMappingURL=table-knowledge.js.map