sync: Phase 3 Agent Features from private repo
Features synced: - LucideIconPicker component (115 icons for dimension customization) - Dimension editing modal (descriptions + icons) - Compound filters (per-column AND filters) - Improved dimension assignment with keyword tagging - queryDimensions and getDimension agent tools New files: - src/components/common/LucideIconPicker.tsx - src/tools/database/queryDimensions.ts - src/tools/database/getDimension.ts 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
57a20899a5
commit
c9d85998c1
@@ -0,0 +1,94 @@
|
||||
import { tool } from 'ai';
|
||||
import { z } from 'zod';
|
||||
import { DimensionService } from '@/services/database/dimensionService';
|
||||
import { getSQLiteClient } from '@/services/database/sqlite-client';
|
||||
|
||||
export const getDimensionTool = tool({
|
||||
description: 'Get detailed information about a specific dimension by name, including its description, priority status, and node count.',
|
||||
inputSchema: z.object({
|
||||
name: z.string().describe('The exact name of the dimension to retrieve')
|
||||
}),
|
||||
execute: async ({ name }) => {
|
||||
console.log('📁 GetDimension tool called for:', name);
|
||||
try {
|
||||
const trimmedName = name.trim();
|
||||
if (!trimmedName) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Dimension name is required',
|
||||
data: null
|
||||
};
|
||||
}
|
||||
|
||||
// Get dimension details from service
|
||||
const dimension = await DimensionService.getDimensionByName(trimmedName);
|
||||
|
||||
// Get node count for this dimension
|
||||
const sqlite = getSQLiteClient();
|
||||
const countResult = sqlite.query(`
|
||||
SELECT COUNT(*) AS count
|
||||
FROM node_dimensions
|
||||
WHERE dimension = ?
|
||||
`, [trimmedName]);
|
||||
|
||||
const nodeCount = countResult.rows.length > 0
|
||||
? Number((countResult.rows[0] as { count: number }).count)
|
||||
: 0;
|
||||
|
||||
if (!dimension) {
|
||||
// Dimension might exist in node_dimensions but not in dimensions table
|
||||
if (nodeCount > 0) {
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
name: trimmedName,
|
||||
description: null,
|
||||
isPriority: false,
|
||||
nodeCount,
|
||||
exists: true,
|
||||
hasMetadata: false
|
||||
},
|
||||
message: `Dimension "${trimmedName}" exists with ${nodeCount} nodes but has no metadata (description/priority not set).`
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: `Dimension "${trimmedName}" not found`,
|
||||
data: null
|
||||
};
|
||||
}
|
||||
|
||||
const result = {
|
||||
name: dimension.name,
|
||||
description: dimension.description,
|
||||
isPriority: dimension.is_priority,
|
||||
nodeCount,
|
||||
updatedAt: dimension.updated_at,
|
||||
exists: true,
|
||||
hasMetadata: true
|
||||
};
|
||||
|
||||
// Build descriptive message
|
||||
const parts: string[] = [];
|
||||
parts.push(`Dimension: ${result.name}`);
|
||||
if (result.isPriority) parts.push('Status: 🔒 Priority (locked)');
|
||||
parts.push(`Nodes: ${result.nodeCount}`);
|
||||
if (result.description) parts.push(`Description: ${result.description}`);
|
||||
parts.push(`Last updated: ${result.updatedAt}`);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: result,
|
||||
message: parts.join('\n')
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('GetDimension tool error:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to get dimension',
|
||||
data: null
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,108 @@
|
||||
import { tool } from 'ai';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const queryDimensionsTool = tool({
|
||||
description: 'Query dimensions with optional filtering by name, priority status, or search term. Returns dimensions with their node counts.',
|
||||
inputSchema: z.object({
|
||||
filters: z.object({
|
||||
search: z.string().describe('Search term to match against dimension names').optional(),
|
||||
isPriority: z.boolean().describe('Filter by priority (locked) status').optional(),
|
||||
limit: z.number().min(1).max(100).default(50).describe('Maximum number of results to return')
|
||||
}).optional()
|
||||
}),
|
||||
execute: async ({ filters = {} }) => {
|
||||
console.log('📁 QueryDimensions tool called with filters:', JSON.stringify(filters, null, 2));
|
||||
try {
|
||||
const limit = filters.limit || 50;
|
||||
const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000';
|
||||
|
||||
// Use existing API endpoint for dimension listing
|
||||
const response = await fetch(`${baseUrl}/api/dimensions/popular`);
|
||||
|
||||
if (!response.ok) {
|
||||
let errorMessage = 'Failed to query dimensions';
|
||||
try {
|
||||
const errorResult = await response.json();
|
||||
errorMessage = errorResult.error || errorMessage;
|
||||
} catch {
|
||||
errorMessage = `Failed to query dimensions: ${response.status} ${response.statusText}`;
|
||||
}
|
||||
return {
|
||||
success: false,
|
||||
error: errorMessage,
|
||||
data: { dimensions: [], count: 0 }
|
||||
};
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (!result.success || !result.data) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Invalid response from dimensions API',
|
||||
data: { dimensions: [], count: 0 }
|
||||
};
|
||||
}
|
||||
|
||||
// Apply filters
|
||||
let dimensions = result.data as Array<{
|
||||
dimension: string;
|
||||
count: number;
|
||||
isPriority: boolean;
|
||||
description: string | null;
|
||||
}>;
|
||||
|
||||
// Filter by search term
|
||||
if (filters.search) {
|
||||
const searchLower = filters.search.toLowerCase();
|
||||
dimensions = dimensions.filter(d =>
|
||||
d.dimension.toLowerCase().includes(searchLower)
|
||||
);
|
||||
}
|
||||
|
||||
// Filter by priority status
|
||||
if (filters.isPriority !== undefined) {
|
||||
dimensions = dimensions.filter(d => d.isPriority === filters.isPriority);
|
||||
}
|
||||
|
||||
// Apply limit
|
||||
const limitedDimensions = dimensions.slice(0, limit);
|
||||
|
||||
// Format for display
|
||||
const formattedDimensions = limitedDimensions.map(d => ({
|
||||
name: d.dimension,
|
||||
count: d.count,
|
||||
isPriority: d.isPriority,
|
||||
description: d.description
|
||||
}));
|
||||
|
||||
// Build message
|
||||
const filterParts: string[] = [];
|
||||
if (filters.search) filterParts.push(`matching "${filters.search}"`);
|
||||
if (filters.isPriority !== undefined) filterParts.push(filters.isPriority ? 'priority only' : 'non-priority only');
|
||||
const filterDesc = filterParts.length > 0 ? ` (${filterParts.join(', ')})` : '';
|
||||
|
||||
const dimensionList = formattedDimensions
|
||||
.map(d => `• ${d.name}${d.isPriority ? ' 🔒' : ''} (${d.count} nodes)${d.description ? ` - ${d.description}` : ''}`)
|
||||
.join('\n');
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
dimensions: formattedDimensions,
|
||||
count: formattedDimensions.length,
|
||||
total_available: dimensions.length,
|
||||
filters_applied: filters
|
||||
},
|
||||
message: `Found ${formattedDimensions.length} dimensions${filterDesc}:\n${dimensionList || 'No dimensions found'}`
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('QueryDimensions tool error:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to query dimensions',
|
||||
data: { dimensions: [], count: 0 }
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -11,6 +11,8 @@ import { updateDimensionTool } from '../database/updateDimension';
|
||||
import { lockDimensionTool } from '../database/lockDimension';
|
||||
import { unlockDimensionTool } from '../database/unlockDimension';
|
||||
import { deleteDimensionTool } from '../database/deleteDimension';
|
||||
import { queryDimensionsTool } from '../database/queryDimensions';
|
||||
import { getDimensionTool } from '../database/getDimension';
|
||||
import { searchContentEmbeddingsTool } from '../other/searchContentEmbeddings';
|
||||
import { webSearchTool } from '../other/webSearch';
|
||||
import { thinkTool } from '../other/think';
|
||||
@@ -27,6 +29,8 @@ const CORE_TOOLS: Record<string, any> = {
|
||||
queryNodes: queryNodesTool,
|
||||
getNodesById: getNodesByIdTool,
|
||||
queryEdge: queryEdgeTool,
|
||||
queryDimensions: queryDimensionsTool,
|
||||
getDimension: getDimensionTool,
|
||||
searchContentEmbeddings: searchContentEmbeddingsTool,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user