import { supabaseService } from '../lib/api-client.js';
import { requireAuth } from '../lib/auth.js';
import { logger } from '../lib/logger.js';
import { z } from 'zod';
// Input schemas for context aggregation tools
const GetSmartContextSchema = z.object({
query: z.string().min(1),
project_id: z.string().uuid().optional(),
context_types: z.array(z.enum(['projects', 'tasks', 'documents', 'conversations'])).default(['projects', 'tasks', 'documents']),
max_results_per_type: z.number().int().positive().max(20).default(5),
include_related: z.boolean().default(true)
});
const GetWorkspaceOverviewSchema = z.object({
include_analytics: z.boolean().default(true),
time_range: z.enum(['today', 'week', 'month', 'all']).default('week'),
focus_areas: z.array(z.enum(['productivity', 'collaboration', 'documentation', 'blockers'])).optional()
});
const GetProjectInsightsSchema = z.object({
project_id: z.string().uuid(),
insight_types: z.array(z.enum(['progress', 'bottlenecks', 'team_performance', 'documentation_health', 'ai_readiness'])).default(['progress', 'bottlenecks']),
include_recommendations: z.boolean().default(true)
});
const FindRelatedContentSchema = z.object({
entity_type: z.enum(['project', 'task', 'document']),
entity_id: z.string().uuid(),
relation_types: z.array(z.enum(['similar', 'dependent', 'linked', 'recent'])).default(['similar', 'linked']),
max_results: z.number().int().positive().max(50).default(10)
});
const GenerateContextSummarySchema = z.object({
context_data: z.object({
projects: z.array(z.any()).optional(),
tasks: z.array(z.any()).optional(),
documents: z.array(z.any()).optional(),
conversations: z.array(z.any()).optional()
}),
summary_focus: z.enum(['overview', 'action_items', 'blockers', 'opportunities']).default('overview'),
target_audience: z.enum(['developer', 'manager', 'ai_agent']).default('ai_agent')
});
/**
* Get smart context based on natural language query
*/
export const getSmartContextTool = {
name: 'get_smart_context',
description: 'Get intelligent context aggregation based on natural language query across projects, tasks, and documents',
inputSchema: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'Natural language query describing what context you need (e.g., "authentication tasks", "API documentation", "blocked items")'
},
project_id: {
type: 'string',
format: 'uuid',
description: 'Optional project ID to scope the search'
},
context_types: {
type: 'array',
items: {
type: 'string',
enum: ['projects', 'tasks', 'documents', 'conversations']
},
default: ['projects', 'tasks', 'documents'],
description: 'Types of content to include in context'
},
max_results_per_type: {
type: 'number',
minimum: 1,
maximum: 20,
default: 5,
description: 'Maximum results to return per content type'
},
include_related: {
type: 'boolean',
default: true,
description: 'Whether to include related/linked content'
}
},
required: ['query']
}
};
export const getSmartContext = requireAuth(async (args) => {
const { query, project_id, context_types, max_results_per_type, include_related } = GetSmartContextSchema.parse(args);
logger.info('Getting smart context', { query, project_id, context_types });
// Analyze query to understand intent and extract keywords
const queryAnalysis = analyzeContextQuery(query);
const context = {
query_analysis: queryAnalysis,
results: {},
related_content: {},
insights: {}
};
// Get relevant content for each requested type
for (const type of context_types) {
try {
switch (type) {
case 'projects':
context.results.projects = await getRelevantProjects(queryAnalysis, project_id, max_results_per_type);
break;
case 'tasks':
context.results.tasks = await getRelevantTasks(queryAnalysis, project_id, max_results_per_type);
break;
case 'documents':
context.results.documents = await getRelevantDocuments(queryAnalysis, project_id, max_results_per_type);
break;
case 'conversations':
context.results.conversations = await getRelevantConversations(queryAnalysis, project_id, max_results_per_type);
break;
}
}
catch (error) {
logger.error(`Error getting ${type} context:`, error);
context.results[type] = [];
}
}
// Get related content if requested
if (include_related) {
context.related_content = await findRelatedContentInternal(context.results, queryAnalysis);
}
// Generate insights and recommendations
context.insights = generateContextInsights(context.results, queryAnalysis);
context.summary = generateSmartContextSummary(context, query);
logger.info('Smart context generated', {
query,
total_results: Object.values(context.results).flat().length
});
return context;
});
/**
* Get comprehensive workspace overview
*/
export const getWorkspaceOverviewTool = {
name: 'get_workspace_overview',
description: 'Get comprehensive overview of entire workspace with analytics and insights',
inputSchema: {
type: 'object',
properties: {
include_analytics: {
type: 'boolean',
default: true,
description: 'Whether to include detailed analytics'
},
time_range: {
type: 'string',
enum: ['today', 'week', 'month', 'all'],
default: 'week',
description: 'Time range for activity analysis'
},
focus_areas: {
type: 'array',
items: {
type: 'string',
enum: ['productivity', 'collaboration', 'documentation', 'blockers']
},
description: 'Specific areas to focus analysis on'
}
}
}
};
export const getWorkspaceOverview = requireAuth(async (args) => {
const { include_analytics, time_range, focus_areas } = GetWorkspaceOverviewSchema.parse(args);
logger.info('Getting workspace overview', { time_range, focus_areas });
// Get all workspace data
const [projects, tasks, documents] = await Promise.all([
supabaseService.getProjects({}, { limit: 50 }),
supabaseService.getTasks({}, { limit: 100 }),
supabaseService.getDocuments({}, { limit: 100 })
]);
const overview = {
summary: {
total_projects: projects.length,
active_projects: projects.filter(p => p.status === 'active').length,
total_tasks: tasks.length,
completed_tasks: tasks.filter(t => t.status === 'done').length,
total_documents: documents.length,
documentation_coverage: calculateDocumentationCoverage(projects, documents)
},
project_health: analyzeProjectHealth(projects, tasks, documents),
productivity_metrics: calculateProductivityMetrics(tasks, time_range),
collaboration_insights: analyzeCollaboration(tasks, documents),
ai_readiness: assessAIReadiness(documents),
recommendations: generateWorkspaceRecommendations(projects, tasks, documents)
};
if (include_analytics) {
overview.analytics = {
task_distribution: analyzeTaskDistribution(tasks),
document_metrics: analyzeDocumentMetrics(documents),
project_velocity: calculateProjectVelocity(projects, tasks, time_range),
bottleneck_analysis: identifyBottlenecks(projects, tasks)
};
}
if (focus_areas) {
overview.focused_insights = generateFocusedInsights(overview, focus_areas);
}
return overview;
});
/**
* Get deep project insights
*/
export const getProjectInsightsTool = {
name: 'get_project_insights',
description: 'Get deep analytics and insights for a specific project',
inputSchema: {
type: 'object',
properties: {
project_id: {
type: 'string',
format: 'uuid',
description: 'The project ID to analyze'
},
insight_types: {
type: 'array',
items: {
type: 'string',
enum: ['progress', 'bottlenecks', 'team_performance', 'documentation_health', 'ai_readiness']
},
default: ['progress', 'bottlenecks'],
description: 'Types of insights to generate'
},
include_recommendations: {
type: 'boolean',
default: true,
description: 'Whether to include actionable recommendations'
}
},
required: ['project_id']
}
};
export const getProjectInsights = requireAuth(async (args) => {
const { project_id, insight_types, include_recommendations } = GetProjectInsightsSchema.parse(args);
logger.info('Getting project insights', { project_id, insight_types });
const project = await supabaseService.getProject(project_id);
const projectContext = await supabaseService.getProjectContext(project_id);
const insights = {
project_overview: {
id: project.id,
name: project.name,
status: project.status,
created_at: project.created_at,
updated_at: project.updated_at
}
};
// Generate requested insights
for (const insightType of insight_types) {
switch (insightType) {
case 'progress':
insights.progress_analysis = analyzeProjectProgress(projectContext);
break;
case 'bottlenecks':
insights.bottleneck_analysis = identifyProjectBottlenecks(projectContext);
break;
case 'team_performance':
insights.team_performance = analyzeTeamPerformance(projectContext);
break;
case 'documentation_health':
insights.documentation_health = analyzeDocumentationHealth(projectContext);
break;
case 'ai_readiness':
insights.ai_readiness = assessProjectAIReadiness(projectContext);
break;
}
}
if (include_recommendations) {
insights.recommendations = generateProjectRecommendations(insights, projectContext);
}
insights.overall_health_score = calculateOverallHealthScore(insights);
return insights;
});
/**
* Find related content across the workspace
*/
export const findRelatedContentTool = {
name: 'find_related_content',
description: 'Find content related to a specific entity (project, task, or document)',
inputSchema: {
type: 'object',
properties: {
entity_type: {
type: 'string',
enum: ['project', 'task', 'document'],
description: 'Type of entity to find related content for'
},
entity_id: {
type: 'string',
format: 'uuid',
description: 'ID of the entity'
},
relation_types: {
type: 'array',
items: {
type: 'string',
enum: ['similar', 'dependent', 'linked', 'recent']
},
default: ['similar', 'linked'],
description: 'Types of relationships to find'
},
max_results: {
type: 'number',
minimum: 1,
maximum: 50,
default: 10,
description: 'Maximum number of related items to return'
}
},
required: ['entity_type', 'entity_id']
}
};
// Using the schema defined earlier
export const findRelatedContent = requireAuth(async (args) => {
const { entity_type, entity_id, relation_types, max_results } = FindRelatedContentSchema.parse(args);
logger.info('Finding related content', { entity_type, entity_id, relation_types });
const relatedContent = {
source_entity: await getSourceEntity(entity_type, entity_id),
relationships: {}
};
for (const relationType of relation_types) {
try {
relatedContent.relationships[relationType] = await findRelationshipsByType(entity_type, entity_id, relationType, max_results);
}
catch (error) {
logger.error(`Error finding ${relationType} relationships:`, error);
relatedContent.relationships[relationType] = [];
}
}
// Calculate relationship strength and add metadata
relatedContent.analysis = analyzeRelationships(relatedContent.relationships);
relatedContent.suggestions = generateRelationshipSuggestions(relatedContent);
return relatedContent;
});
/**
* Generate context summary from aggregated data
*/
export const generateContextSummaryTool = {
name: 'generate_context_summary',
description: 'Generate intelligent summary from aggregated context data',
inputSchema: {
type: 'object',
properties: {
context_data: {
type: 'object',
properties: {
projects: { type: 'array' },
tasks: { type: 'array' },
documents: { type: 'array' },
conversations: { type: 'array' }
},
description: 'Aggregated context data to summarize'
},
summary_focus: {
type: 'string',
enum: ['overview', 'action_items', 'blockers', 'opportunities'],
default: 'overview',
description: 'Focus of the summary'
},
target_audience: {
type: 'string',
enum: ['developer', 'manager', 'ai_agent'],
default: 'ai_agent',
description: 'Target audience for the summary'
}
},
required: ['context_data']
}
};
export const generateContextSummary = requireAuth(async (args) => {
const { context_data, summary_focus, target_audience } = GenerateContextSummarySchema.parse(args);
logger.info('Generating context summary', { summary_focus, target_audience });
const summary = {
executive_summary: generateExecutiveSummary(context_data, summary_focus),
key_metrics: extractKeyMetrics(context_data),
insights: generateInsights(context_data, summary_focus),
action_items: extractActionItems(context_data),
recommendations: generateRecommendations(context_data, target_audience),
metadata: {
generated_at: new Date().toISOString(),
summary_focus,
target_audience,
data_sources: Object.keys(context_data).filter(key => context_data[key]?.length > 0)
}
};
return summary;
});
// Helper functions for context analysis
function analyzeContextQuery(query) {
const keywords = extractKeywords(query);
const intent = detectIntent(query);
const entities = extractEntities(query);
const urgency = detectUrgency(query);
return {
original_query: query,
keywords,
intent,
entities,
urgency,
search_terms: generateSearchTerms(keywords, entities)
};
}
function extractKeywords(query) {
// Simple keyword extraction
const stopWords = ['the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with', 'by'];
return query
.toLowerCase()
.split(/\s+/)
.filter(word => word.length > 2 && !stopWords.includes(word))
.filter(word => /^[a-zA-Z]+$/.test(word));
}
function detectIntent(query) {
const lowerQuery = query.toLowerCase();
if (lowerQuery.includes('block') || lowerQuery.includes('stuck') || lowerQuery.includes('issue')) {
return 'troubleshooting';
}
if (lowerQuery.includes('task') || lowerQuery.includes('todo') || lowerQuery.includes('work')) {
return 'task_management';
}
if (lowerQuery.includes('document') || lowerQuery.includes('doc') || lowerQuery.includes('readme')) {
return 'documentation';
}
if (lowerQuery.includes('project') || lowerQuery.includes('overview')) {
return 'project_overview';
}
if (lowerQuery.includes('team') || lowerQuery.includes('collaboration')) {
return 'collaboration';
}
return 'general_search';
}
function extractEntities(query) {
// Simple entity extraction based on patterns
const entities = [];
// Look for quoted terms
const quotedTerms = query.match(/"([^"]+)"/g);
if (quotedTerms) {
entities.push(...quotedTerms.map(term => term.replace(/"/g, '')));
}
// Look for technical terms (capitalized or with special chars)
const techTerms = query.match(/[A-Z][a-zA-Z]+|[a-zA-Z]+[-_][a-zA-Z]+/g);
if (techTerms) {
entities.push(...techTerms);
}
return entities;
}
function detectUrgency(query) {
const urgentWords = ['urgent', 'asap', 'emergency', 'critical', 'immediately', 'now'];
const highWords = ['important', 'priority', 'soon', 'quick', 'fast'];
const lowerQuery = query.toLowerCase();
if (urgentWords.some(word => lowerQuery.includes(word)))
return 'high';
if (highWords.some(word => lowerQuery.includes(word)))
return 'medium';
return 'low';
}
function generateSearchTerms(keywords, entities) {
return [...new Set([...keywords, ...entities])];
}
async function getRelevantProjects(queryAnalysis, projectId, limit = 5) {
const searchTerms = queryAnalysis.search_terms.join(' ');
return await supabaseService.getProjects({ search: searchTerms }, { limit }, { field: 'updated_at', order: 'desc' });
}
async function getRelevantTasks(queryAnalysis, projectId, limit = 5) {
const searchTerms = queryAnalysis.search_terms.join(' ');
return await supabaseService.getTasks({ search: searchTerms, project_id: projectId }, { limit }, { field: 'updated_at', order: 'desc' });
}
async function getRelevantDocuments(queryAnalysis, projectId, limit = 5) {
const searchTerms = queryAnalysis.search_terms.join(' ');
return await supabaseService.getDocuments({ search: searchTerms, project_id: projectId }, { limit }, { field: 'updated_at', order: 'desc' });
}
async function getRelevantConversations(queryAnalysis, projectId, limit = 5) {
// Placeholder - would implement conversation search
return [];
}
async function findRelatedContentInternal(results, queryAnalysis) {
// Find connections between different types of content
const related = {
cross_references: [],
common_topics: [],
related_projects: []
};
// This would implement more sophisticated relationship finding
return related;
}
function generateContextInsights(results, queryAnalysis) {
return {
total_items_found: Object.values(results).flat().length,
coverage: {
projects: results.projects?.length || 0,
tasks: results.tasks?.length || 0,
documents: results.documents?.length || 0,
conversations: results.conversations?.length || 0
},
relevance_score: calculateRelevanceScore(results, queryAnalysis),
patterns: identifyPatterns(results),
suggestions: generateSearchSuggestions(results, queryAnalysis)
};
}
function generateSmartContextSummary(context, originalQuery) {
const totalResults = Object.values(context.results).flat().length;
const topTypes = Object.entries(context.results)
.sort(([, a], [, b]) => b.length - a.length)
.slice(0, 2)
.map(([type]) => type);
return `Found ${totalResults} relevant items for "${originalQuery}". Primary matches in ${topTypes.join(' and ')}. ${context.insights.relevance_score > 0.7 ? 'High relevance' : 'Moderate relevance'} to query intent.`;
}
function calculateDocumentationCoverage(projects, documents) {
if (projects.length === 0)
return 0;
const projectsWithDocs = projects.filter(project => documents.some(doc => doc.project_id === project.id)).length;
return Math.round((projectsWithDocs / projects.length) * 100);
}
function analyzeProjectHealth(projects, tasks, documents) {
return {
healthy_projects: projects.filter(p => p.status === 'active').length,
at_risk_projects: projects.filter(p => {
const projectTasks = tasks.filter(t => t.project_id === p.id);
const todoTasks = projectTasks.filter(t => t.status === 'todo').length;
return todoTasks > projectTasks.length * 0.5; // More than 50% not started
}).length,
stale_projects: projects.filter(p => {
const daysSinceUpdate = (Date.now() - new Date(p.updated_at).getTime()) / (1000 * 60 * 60 * 24);
return daysSinceUpdate > 30;
}).length
};
}
function calculateProductivityMetrics(tasks, timeRange) {
const now = new Date();
let startDate;
switch (timeRange) {
case 'today':
startDate = new Date(now.getFullYear(), now.getMonth(), now.getDate());
break;
case 'week':
startDate = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
break;
case 'month':
startDate = new Date(now.getFullYear(), now.getMonth(), 1);
break;
default:
startDate = new Date(0);
}
const recentTasks = tasks.filter(task => new Date(task.updated_at).getTime() >= startDate.getTime());
const completedTasks = recentTasks.filter(task => task.status === 'done');
return {
tasks_completed: completedTasks.length,
tasks_in_progress: recentTasks.filter(task => task.status === 'in_progress').length,
completion_rate: recentTasks.length > 0 ? completedTasks.length / recentTasks.length : 0,
average_completion_time: calculateAverageCompletionTime(completedTasks)
};
}
function analyzeCollaboration(tasks, documents) {
const assignedTasks = tasks.filter(task => task.assignee_id);
// Since metadata doesn't exist in the schema, we'll count collaborative documents differently
const collaborativeDocuments = documents.filter(doc => doc.title.toLowerCase().includes('collaborative') ||
doc.title.toLowerCase().includes('team') ||
doc.document_type === 'meeting_notes');
return {
task_assignment_rate: tasks.length > 0 ? assignedTasks.length / tasks.length : 0,
collaborative_documents: collaborativeDocuments.length,
team_distribution: analyzeTeamDistribution(assignedTasks)
};
}
function assessAIReadiness(documents) {
// Since metadata doesn't exist in the schema, we'll assess AI readiness based on document type and content
const aiReadyDocs = documents.filter(doc => doc.document_type === 'technical' ||
doc.document_type === 'design' ||
doc.content.includes('AI') ||
doc.content.includes('ai_instructions'));
return {
ai_ready_documents: aiReadyDocs.length,
readiness_percentage: documents.length > 0 ? (aiReadyDocs.length / documents.length) * 100 : 0,
missing_ai_metadata: documents.length - aiReadyDocs.length,
recommendations: generateAIReadinessRecommendations(documents, aiReadyDocs)
};
}
function generateWorkspaceRecommendations(projects, tasks, documents) {
const recommendations = [];
const activeTasks = tasks.filter(t => t.status === 'in_progress');
const completedTasks = tasks.filter(t => t.status === 'done');
if (activeTasks.length > completedTasks.length * 2) {
recommendations.push('Consider focusing on completing existing tasks before starting new ones');
}
const undocumentedProjects = projects.filter(p => !documents.some(d => d.project_id === p.id && d.document_type === 'other'));
if (undocumentedProjects.length > 0) {
recommendations.push(`Add README documentation for ${undocumentedProjects.length} project(s)`);
}
const todoTasks = tasks.filter(t => t.status === 'todo');
if (todoTasks.length > tasks.filter(t => t.status === 'in_progress').length * 2) {
recommendations.push(`Focus on starting ${todoTasks.length} pending task(s) to improve team velocity`);
}
return recommendations;
}
// Additional helper functions would continue here...
// (For brevity, I'm showing the structure - the full implementation would include all remaining functions)
// Export all context aggregation tools
export const contextAggregationTools = {
getSmartContextTool,
getWorkspaceOverviewTool,
getProjectInsightsTool,
findRelatedContentTool,
generateContextSummaryTool
};
export const contextAggregationHandlers = {
get_smart_context: getSmartContext,
get_workspace_overview: getWorkspaceOverview,
get_project_insights: getProjectInsights,
find_related_content: findRelatedContent,
generate_context_summary: generateContextSummary
};
// Stub implementations for remaining helper functions
function calculateRelevanceScore(results, queryAnalysis) {
// Calculate relevance based on query analysis and results
return 0.8;
}
function identifyPatterns(results) {
// Identify patterns across results
return [];
}
function generateSearchSuggestions(results, queryAnalysis) {
// Generate suggestions for better search
return [];
}
async function getSourceEntity(entityType, entityId) {
// Get the source entity based on type
switch (entityType) {
case 'project':
return await supabaseService.getProject(entityId);
case 'task':
const tasks = await supabaseService.getTasks({ search: entityId });
return tasks.find(t => t.id === entityId);
case 'document':
return await supabaseService.getDocument(entityId);
default:
return null;
}
}
async function findRelationshipsByType(entityType, entityId, relationType, maxResults) {
// Find relationships by type
return [];
}
function analyzeRelationships(relationships) {
// Analyze relationship strength and patterns
return {};
}
function generateRelationshipSuggestions(relatedContent) {
// Generate suggestions based on relationships
return [];
}
function generateExecutiveSummary(contextData, summaryFocus) {
// Generate executive summary
return 'Executive summary of context data';
}
function extractKeyMetrics(contextData) {
// Extract key metrics from context data
return {};
}
function generateInsights(contextData, summaryFocus) {
// Generate insights from context data
return [];
}
function extractActionItems(contextData) {
// Extract action items from context data
return [];
}
function generateRecommendations(contextData, targetAudience) {
// Generate recommendations based on context
return [];
}
function analyzeProjectProgress(projectContext) {
// Analyze project progress
return {};
}
function identifyProjectBottlenecks(projectContext) {
// Identify project bottlenecks
return {};
}
function analyzeTeamPerformance(projectContext) {
// Analyze team performance
return {};
}
function analyzeDocumentationHealth(projectContext) {
// Analyze documentation health
return {};
}
function assessProjectAIReadiness(projectContext) {
// Assess project AI readiness
return {};
}
function generateProjectRecommendations(insights, projectContext) {
// Generate project recommendations
return [];
}
function calculateOverallHealthScore(insights) {
// Calculate overall health score
return 85;
}
function analyzeTaskDistribution(tasks) {
// Analyze task distribution
return {};
}
function analyzeDocumentMetrics(documents) {
// Analyze document metrics
return {};
}
function calculateProjectVelocity(projects, tasks, timeRange) {
// Calculate project velocity
return {};
}
function identifyBottlenecks(projects, tasks) {
// Identify bottlenecks
return {};
}
function generateFocusedInsights(overview, focusAreas) {
// Generate focused insights
return {};
}
function calculateAverageCompletionTime(completedTasks) {
// Calculate average completion time
return 0;
}
function analyzeTeamDistribution(assignedTasks) {
// Analyze team distribution
return {};
}
function generateAIReadinessRecommendations(documents, aiReadyDocs) {
// Generate AI readiness recommendations
return [];
}