vibe-coder-mcp
Version:
Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.
866 lines (865 loc) • 34.8 kB
JavaScript
import { getStorageManager } from '../storage/storage-manager.js';
import { getVibeTaskManagerConfig } from '../../utils/config-loader.js';
import { getIdGenerator } from '../../utils/id-generator.js';
import { UnifiedSecurityEngine, createDefaultSecurityConfig } from '../unified-security-engine.js';
import logger from '../../../../logger.js';
export class TaskOperations {
static instance;
securityEngine;
constructor() {
const securityConfig = {
...createDefaultSecurityConfig(),
concurrentAccess: {
enabled: true,
maxLockDuration: 300000,
deadlockDetection: true,
lockCleanupInterval: 60000,
maxLocksPerResource: 10
}
};
this.securityEngine = UnifiedSecurityEngine.getInstance(securityConfig);
}
async acquireLock(resource, _owner, operation, options = {}) {
const result = await this.securityEngine.acquireLock(resource, operation, undefined, options.timeout);
if (result.success) {
return {
success: true,
lock: { id: result.data.lockId },
error: undefined
};
}
else {
return {
success: false,
lock: undefined,
error: result.error.message
};
}
}
async releaseLock(lockId) {
const result = await this.securityEngine.releaseLock(lockId);
return result.success;
}
async sanitizeData(data) {
const result = await this.securityEngine.sanitizeData(data);
if (result.success) {
return {
success: true,
sanitizedData: result.data.sanitizedData,
violations: result.data.violations || []
};
}
else {
return {
success: false,
sanitizedData: data,
violations: [result.error.message]
};
}
}
static getInstance() {
if (!TaskOperations.instance) {
TaskOperations.instance = new TaskOperations();
}
return TaskOperations.instance;
}
static resetInstance() {
TaskOperations.instance = undefined;
}
async createTask(params, createdBy = 'system') {
const lockIds = [];
try {
logger.info({ taskTitle: params.title, projectId: params.projectId, createdBy }, 'Creating new task');
const projectLockResult = await this.acquireLock(`project:${params.projectId}`, createdBy, 'write', {
timeout: 30000,
metadata: {
operation: 'create_task',
taskTitle: params.title
}
});
if (!projectLockResult.success) {
return {
success: false,
error: `Failed to acquire project lock: ${projectLockResult.error}`,
metadata: {
filePath: 'task-operations',
operation: 'create_task',
timestamp: new Date()
}
};
}
lockIds.push(projectLockResult.lock.id);
let resolvedEpicId = params.epicId;
if (!resolvedEpicId) {
logger.debug({ taskTitle: params.title, projectId: params.projectId }, 'Epic ID not provided, resolving automatically');
const { getEpicContextResolver } = await import('../../services/epic-context-resolver.js');
const epicResolver = getEpicContextResolver();
const epicContext = await epicResolver.resolveEpicContext({
projectId: params.projectId,
taskContext: {
title: params.title,
description: params.description,
type: params.type || 'development',
tags: params.tags || []
}
});
resolvedEpicId = epicContext.epicId;
logger.debug({ resolvedEpicId, source: epicContext.source }, 'Epic ID resolved automatically');
}
const epicLockResult = await this.acquireLock(`epic:${resolvedEpicId}`, createdBy, 'write', {
timeout: 30000,
metadata: {
operation: 'create_task',
taskTitle: params.title
}
});
if (!epicLockResult.success) {
await this.releaseLock(lockIds[0]);
return {
success: false,
error: `Failed to acquire epic lock: ${epicLockResult.error}`,
metadata: {
filePath: 'task-operations',
operation: 'create_task',
timestamp: new Date()
}
};
}
lockIds.push(epicLockResult.lock.id);
const paramsWithEpicId = {
...params,
epicId: resolvedEpicId
};
const sanitizationResult = await this.sanitizeData(paramsWithEpicId);
if (!sanitizationResult.success) {
logger.error({
violations: sanitizationResult.violations,
taskTitle: params.title
}, 'Task creation input sanitization failed');
return {
success: false,
error: `Input sanitization failed: ${sanitizationResult.violations.join(', ')}`,
metadata: {
filePath: 'task-operations',
operation: 'create_task',
timestamp: new Date()
}
};
}
const sanitizedParams = sanitizationResult.sanitizedData;
const validationResult = this.validateCreateTaskParams(sanitizedParams);
if (!validationResult.valid) {
return {
success: false,
error: `Task creation validation failed: ${validationResult.errors.join(', ')}`,
metadata: {
filePath: 'task-operations',
operation: 'create_task',
timestamp: new Date()
}
};
}
const storageManager = await getStorageManager();
const projectExists = await storageManager.projectExists(sanitizedParams.projectId);
if (!projectExists) {
return {
success: false,
error: `Project ${sanitizedParams.projectId} not found`,
metadata: {
filePath: 'task-operations',
operation: 'create_task',
timestamp: new Date()
}
};
}
const { validateEpicForTask } = await import('../../utils/epic-validator.js');
const epicValidationResult = await validateEpicForTask({
epicId: sanitizedParams.epicId,
projectId: sanitizedParams.projectId,
title: sanitizedParams.title,
description: sanitizedParams.description,
type: sanitizedParams.type,
tags: sanitizedParams.tags
});
if (!epicValidationResult.valid) {
return {
success: false,
error: `Epic validation failed: ${epicValidationResult.error || 'Unknown error'}`,
metadata: {
filePath: 'task-operations',
operation: 'create_task',
timestamp: new Date()
}
};
}
if (epicValidationResult.epicId !== sanitizedParams.epicId) {
logger.info({
originalEpicId: sanitizedParams.epicId,
resolvedEpicId: epicValidationResult.epicId,
created: epicValidationResult.created
}, 'Epic ID resolved during validation');
sanitizedParams.epicId = epicValidationResult.epicId;
}
const idGenerator = getIdGenerator();
const idResult = await idGenerator.generateTaskId();
if (!idResult.success) {
return {
success: false,
error: `Failed to generate task ID: ${idResult.error}`,
metadata: {
filePath: 'task-operations',
operation: 'create_task',
timestamp: new Date()
}
};
}
const taskId = idResult.id;
const config = await getVibeTaskManagerConfig();
if (!config) {
return {
success: false,
error: 'Failed to load task manager configuration',
metadata: {
filePath: 'task-operations',
operation: 'create_task',
timestamp: new Date()
}
};
}
const task = {
id: taskId,
title: sanitizedParams.title,
description: sanitizedParams.description,
status: 'pending',
priority: sanitizedParams.priority || 'medium',
type: sanitizedParams.type || 'development',
functionalArea: sanitizedParams.functionalArea || 'data-management',
estimatedHours: sanitizedParams.estimatedHours || 4,
epicId: sanitizedParams.epicId,
projectId: sanitizedParams.projectId,
dependencies: [],
dependents: [],
filePaths: sanitizedParams.filePaths || [],
acceptanceCriteria: sanitizedParams.acceptanceCriteria || [],
testingRequirements: {
unitTests: [],
integrationTests: [],
performanceTests: [],
coverageTarget: config.taskManager.performanceTargets.minTestCoverage
},
performanceCriteria: {
responseTime: `<${config.taskManager.performanceTargets.maxResponseTime}ms`,
memoryUsage: `<${config.taskManager.performanceTargets.maxMemoryUsage}MB`
},
qualityCriteria: {
codeQuality: ['TypeScript strict mode', 'ESLint compliance'],
documentation: ['JSDoc comments'],
typeScript: true,
eslint: true
},
integrationCriteria: {
compatibility: ['Existing MCP patterns'],
patterns: ['Tool registration pattern']
},
validationMethods: {
automated: ['Unit tests', 'Integration tests'],
manual: ['Code review']
},
assignedAgent: sanitizedParams.assignedAgent,
createdAt: new Date(),
updatedAt: new Date(),
createdBy,
tags: sanitizedParams.tags || [],
metadata: {
createdAt: new Date(),
updatedAt: new Date(),
createdBy,
tags: sanitizedParams.tags || []
}
};
const createResult = await storageManager.createTask(task);
if (!createResult.success) {
return {
success: false,
error: `Failed to save task: ${createResult.error}`,
metadata: createResult.metadata
};
}
try {
const { getEpicService } = await import('../../services/epic-service.js');
const epicService = getEpicService();
const addTaskResult = await epicService.addTaskToEpic(sanitizedParams.epicId, taskId);
if (!addTaskResult.success) {
logger.warn({
taskId,
epicId: sanitizedParams.epicId,
error: addTaskResult.error
}, 'Failed to add task to epic taskIds array');
}
else {
logger.debug({ taskId, epicId: sanitizedParams.epicId }, 'Task added to epic taskIds array');
}
}
catch (error) {
logger.warn({
err: error,
taskId,
epicId: sanitizedParams.epicId
}, 'Error updating epic with new task');
}
logger.info({ taskId, taskTitle: params.title }, 'Task created successfully');
return {
success: true,
data: createResult.data,
metadata: {
filePath: 'task-operations',
operation: 'create_task',
timestamp: new Date()
}
};
}
catch (error) {
logger.error({ err: error, taskTitle: params.title }, 'Failed to create task');
return {
success: false,
error: error instanceof Error ? error.message : String(error),
metadata: {
filePath: 'task-operations',
operation: 'create_task',
timestamp: new Date()
}
};
}
finally {
for (const lockId of lockIds) {
try {
await this.releaseLock(lockId);
}
catch (error) {
logger.error({ err: error, lockId }, 'Failed to release lock during task creation cleanup');
}
}
}
}
async getTask(taskId) {
try {
logger.debug({ taskId }, 'Getting task');
const storageManager = await getStorageManager();
return await storageManager.getTask(taskId);
}
catch (error) {
logger.error({ err: error, taskId }, 'Failed to get task');
return {
success: false,
error: error instanceof Error ? error.message : String(error),
metadata: {
filePath: 'task-operations',
operation: 'get_task',
timestamp: new Date()
}
};
}
}
async updateTask(taskId, params, updatedBy = 'system') {
try {
logger.info({ taskId, updates: Object.keys(params), updatedBy }, 'Updating task');
const validationResult = this.validateUpdateTaskParams(params);
if (!validationResult.valid) {
return {
success: false,
error: `Task update validation failed: ${validationResult.errors.join(', ')}`,
metadata: {
filePath: 'task-operations',
operation: 'update_task',
timestamp: new Date()
}
};
}
const storageManager = await getStorageManager();
const existingResult = await storageManager.getTask(taskId);
if (!existingResult.success) {
return {
success: false,
error: `Task not found: ${existingResult.error}`,
metadata: existingResult.metadata
};
}
const existingTask = existingResult.data;
const updates = {
...params,
metadata: {
...existingTask.metadata,
updatedAt: new Date(),
...(params.tags && { tags: params.tags })
}
};
const updateResult = await storageManager.updateTask(taskId, updates);
if (!updateResult.success) {
return {
success: false,
error: `Failed to update task: ${updateResult.error}`,
metadata: updateResult.metadata
};
}
logger.info({ taskId }, 'Task updated successfully');
return {
success: true,
data: updateResult.data,
metadata: {
filePath: 'task-operations',
operation: 'update_task',
timestamp: new Date()
}
};
}
catch (error) {
logger.error({ err: error, taskId }, 'Failed to update task');
return {
success: false,
error: error instanceof Error ? error.message : String(error),
metadata: {
filePath: 'task-operations',
operation: 'update_task',
timestamp: new Date()
}
};
}
}
async deleteTask(taskId, deletedBy = 'system') {
try {
logger.info({ taskId, deletedBy }, 'Deleting task');
const storageManager = await getStorageManager();
const taskExists = await storageManager.taskExists(taskId);
if (!taskExists) {
return {
success: false,
error: `Task ${taskId} not found`,
metadata: {
filePath: 'task-operations',
operation: 'delete_task',
timestamp: new Date()
}
};
}
const deleteResult = await storageManager.deleteTask(taskId);
if (!deleteResult.success) {
return {
success: false,
error: `Failed to delete task: ${deleteResult.error}`,
metadata: deleteResult.metadata
};
}
logger.info({ taskId }, 'Task deleted successfully');
return {
success: true,
metadata: {
filePath: 'task-operations',
operation: 'delete_task',
timestamp: new Date()
}
};
}
catch (error) {
logger.error({ err: error, taskId }, 'Failed to delete task');
return {
success: false,
error: error instanceof Error ? error.message : String(error),
metadata: {
filePath: 'task-operations',
operation: 'delete_task',
timestamp: new Date()
}
};
}
}
async listTasks(query) {
try {
logger.debug({ query }, 'Listing tasks');
const storageManager = await getStorageManager();
let result;
if (query?.status && query?.projectId) {
result = await storageManager.getTasksByStatus(query.status, query.projectId);
}
else if (query?.priority && query?.projectId) {
result = await storageManager.getTasksByPriority(query.priority, query.projectId);
}
else {
result = await storageManager.listTasks(query?.projectId, query?.epicId);
}
if (!result.success) {
return result;
}
let tasks = result.data;
if (query) {
tasks = this.applyTaskFilters(tasks, query);
}
return {
success: true,
data: tasks,
metadata: {
filePath: 'task-operations',
operation: 'list_tasks',
timestamp: new Date()
}
};
}
catch (error) {
logger.error({ err: error, query }, 'Failed to list tasks');
return {
success: false,
error: error instanceof Error ? error.message : String(error),
metadata: {
filePath: 'task-operations',
operation: 'list_tasks',
timestamp: new Date()
}
};
}
}
async searchTasks(searchQuery, query) {
try {
logger.debug({ searchQuery, query }, 'Searching tasks');
const storageManager = await getStorageManager();
const searchResult = await storageManager.searchTasks(searchQuery, query?.projectId);
if (!searchResult.success) {
return searchResult;
}
let tasks = searchResult.data;
if (query) {
tasks = this.applyTaskFilters(tasks, query);
}
return {
success: true,
data: tasks,
metadata: {
filePath: 'task-operations',
operation: 'search_tasks',
timestamp: new Date()
}
};
}
catch (error) {
logger.error({ err: error, searchQuery }, 'Failed to search tasks');
return {
success: false,
error: error instanceof Error ? error.message : String(error),
metadata: {
filePath: 'task-operations',
operation: 'search_tasks',
timestamp: new Date()
}
};
}
}
validateCreateTaskParams(params) {
const errors = [];
if (!params.title || typeof params.title !== 'string' || params.title.trim().length === 0) {
errors.push('Task title is required and must be a non-empty string');
}
if (params.title && params.title.length > 200) {
errors.push('Task title must be 200 characters or less');
}
if (!params.description || typeof params.description !== 'string' || params.description.trim().length === 0) {
errors.push('Task description is required and must be a non-empty string');
}
if (!params.projectId || typeof params.projectId !== 'string') {
errors.push('Project ID is required and must be a string');
}
if (!params.epicId || typeof params.epicId !== 'string') {
errors.push('Epic ID is required and must be a string');
}
if (params.estimatedHours !== undefined && (typeof params.estimatedHours !== 'number' || params.estimatedHours < 0)) {
errors.push('Estimated hours must be a non-negative number');
}
if (params.filePaths && !Array.isArray(params.filePaths)) {
errors.push('File paths must be an array of strings');
}
if (params.acceptanceCriteria && !Array.isArray(params.acceptanceCriteria)) {
errors.push('Acceptance criteria must be an array of strings');
}
if (params.tags && !Array.isArray(params.tags)) {
errors.push('Tags must be an array of strings');
}
return {
valid: errors.length === 0,
errors
};
}
validateUpdateTaskParams(params) {
const errors = [];
if (params.title !== undefined) {
if (typeof params.title !== 'string' || params.title.trim().length === 0) {
errors.push('Task title must be a non-empty string');
}
if (params.title.length > 200) {
errors.push('Task title must be 200 characters or less');
}
}
if (params.description !== undefined) {
if (typeof params.description !== 'string' || params.description.trim().length === 0) {
errors.push('Task description must be a non-empty string');
}
}
if (params.status !== undefined) {
if (!['pending', 'in_progress', 'completed', 'blocked', 'cancelled'].includes(params.status)) {
errors.push('Status must be one of: pending, in_progress, completed, blocked, cancelled');
}
}
if (params.priority !== undefined) {
if (!['low', 'medium', 'high', 'critical'].includes(params.priority)) {
errors.push('Priority must be one of: low, medium, high, critical');
}
}
if (params.estimatedHours !== undefined && (typeof params.estimatedHours !== 'number' || params.estimatedHours < 0)) {
errors.push('Estimated hours must be a non-negative number');
}
if (params.actualHours !== undefined && (typeof params.actualHours !== 'number' || params.actualHours < 0)) {
errors.push('Actual hours must be a non-negative number');
}
return {
valid: errors.length === 0,
errors
};
}
applyTaskFilters(tasks, query) {
let filtered = tasks;
if (query.type) {
filtered = filtered.filter(task => task.type === query.type);
}
if (query.assignedAgent) {
filtered = filtered.filter(task => task.assignedAgent === query.assignedAgent);
}
if (query.tags && query.tags.length > 0) {
filtered = filtered.filter(task => query.tags.some(tag => task.metadata.tags.includes(tag)));
}
if (query.createdAfter) {
filtered = filtered.filter(task => task.metadata.createdAt >= query.createdAfter);
}
if (query.createdBefore) {
filtered = filtered.filter(task => task.metadata.createdAt <= query.createdBefore);
}
if (query.offset) {
filtered = filtered.slice(query.offset);
}
if (query.limit) {
filtered = filtered.slice(0, query.limit);
}
return filtered;
}
async updateTaskStatus(taskId, status, updatedBy = 'system') {
try {
logger.info({ taskId, status, updatedBy }, 'Updating task status');
const validStatuses = ['pending', 'in_progress', 'completed', 'failed', 'blocked'];
if (!validStatuses.includes(status)) {
return {
success: false,
error: `Invalid task status: ${status}. Valid statuses are: ${validStatuses.join(', ')}`,
metadata: {
filePath: 'task-operations',
operation: 'update_task_status',
timestamp: new Date()
}
};
}
const updateParams = { status };
return await this.updateTask(taskId, updateParams, updatedBy);
}
catch (error) {
logger.error({ err: error, taskId, status }, 'Failed to update task status');
return {
success: false,
error: error instanceof Error ? error.message : String(error),
metadata: {
filePath: 'task-operations',
operation: 'update_task_status',
timestamp: new Date()
}
};
}
}
async updateTaskMetadata(taskId, metadata, updatedBy = 'system') {
try {
logger.info({ taskId, metadataKeys: Object.keys(metadata), updatedBy }, 'Updating task metadata');
const storageManager = await getStorageManager();
const existingResult = await storageManager.getTask(taskId);
if (!existingResult.success) {
return {
success: false,
error: `Task not found: ${existingResult.error}`,
metadata: existingResult.metadata
};
}
const existingTask = existingResult.data;
const mergedMetadata = {
...existingTask.metadata,
...metadata,
updatedAt: new Date(),
updatedBy
};
const updates = {
metadata: mergedMetadata
};
const updateResult = await storageManager.updateTask(taskId, updates);
if (!updateResult.success) {
return {
success: false,
error: `Failed to update task metadata: ${updateResult.error}`,
metadata: updateResult.metadata
};
}
logger.info({ taskId }, 'Task metadata updated successfully');
return {
success: true,
data: updateResult.data,
metadata: {
filePath: 'task-operations',
operation: 'update_task_metadata',
timestamp: new Date()
}
};
}
catch (error) {
logger.error({ err: error, taskId }, 'Failed to update task metadata');
return {
success: false,
error: error instanceof Error ? error.message : String(error),
metadata: {
filePath: 'task-operations',
operation: 'update_task_metadata',
timestamp: new Date()
}
};
}
}
async getTaskStatus(taskId) {
try {
logger.debug({ taskId }, 'Getting task status');
const taskResult = await this.getTask(taskId);
if (!taskResult.success) {
return {
success: false,
error: taskResult.error,
metadata: taskResult.metadata
};
}
return {
success: true,
data: taskResult.data.status,
metadata: {
filePath: 'task-operations',
operation: 'get_task_status',
timestamp: new Date()
}
};
}
catch (error) {
logger.error({ err: error, taskId }, 'Failed to get task status');
return {
success: false,
error: error instanceof Error ? error.message : String(error),
metadata: {
filePath: 'task-operations',
operation: 'get_task_status',
timestamp: new Date()
}
};
}
}
async getTaskMetadata(taskId) {
try {
logger.debug({ taskId }, 'Getting task metadata');
const taskResult = await this.getTask(taskId);
if (!taskResult.success) {
return {
success: false,
error: taskResult.error,
metadata: taskResult.metadata
};
}
return {
success: true,
data: taskResult.data.metadata || {},
metadata: {
filePath: 'task-operations',
operation: 'get_task_metadata',
timestamp: new Date()
}
};
}
catch (error) {
logger.error({ err: error, taskId }, 'Failed to get task metadata');
return {
success: false,
error: error instanceof Error ? error.message : String(error),
metadata: {
filePath: 'task-operations',
operation: 'get_task_metadata',
timestamp: new Date()
}
};
}
}
async addTaskTags(taskId, tags, updatedBy = 'system') {
try {
logger.info({ taskId, tags, updatedBy }, 'Adding task tags');
const taskResult = await this.getTask(taskId);
if (!taskResult.success) {
return {
success: false,
error: taskResult.error,
metadata: taskResult.metadata
};
}
const existingTask = taskResult.data;
const existingTags = existingTask.tags || [];
const mergedTags = [...new Set([...existingTags, ...tags])];
const updateParams = { tags: mergedTags };
return await this.updateTask(taskId, updateParams, updatedBy);
}
catch (error) {
logger.error({ err: error, taskId, tags }, 'Failed to add task tags');
return {
success: false,
error: error instanceof Error ? error.message : String(error),
metadata: {
filePath: 'task-operations',
operation: 'add_task_tags',
timestamp: new Date()
}
};
}
}
async removeTaskTags(taskId, tags, updatedBy = 'system') {
try {
logger.info({ taskId, tags, updatedBy }, 'Removing task tags');
const taskResult = await this.getTask(taskId);
if (!taskResult.success) {
return {
success: false,
error: taskResult.error,
metadata: taskResult.metadata
};
}
const existingTask = taskResult.data;
const existingTags = existingTask.tags || [];
const filteredTags = existingTags.filter(tag => !tags.includes(tag));
const updateParams = { tags: filteredTags };
return await this.updateTask(taskId, updateParams, updatedBy);
}
catch (error) {
logger.error({ err: error, taskId, tags }, 'Failed to remove task tags');
return {
success: false,
error: error instanceof Error ? error.message : String(error),
metadata: {
filePath: 'task-operations',
operation: 'remove_task_tags',
timestamp: new Date()
}
};
}
}
}
export function getTaskOperations() {
return TaskOperations.getInstance();
}