cline-sdk
Version:
Comprehensive SDK for Cline - AI-powered development assistant with database integration, execution modes, and custom functions
366 lines • 15.6 kB
JavaScript
;
/**
* 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