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:
“BeeRad”
2025-12-25 10:05:02 +11:00
co-authored by Claude Opus 4.5
parent 57a20899a5
commit c9d85998c1
8 changed files with 1630 additions and 174 deletions
+94
View File
@@ -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
};
}
}
});
+108
View File
@@ -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 }
};
}
}
});
+4
View File
@@ -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,
};