Skip to main content
Glama
list.ts•10.9 kB
import { Command } from 'commander'; import { getProjectOperations, ProjectQueryParams } from '../../core/operations/project-operations.js'; import { getTaskOperations, TaskQueryParams } from '../../core/operations/task-operations.js'; import { CLIUtils } from './index.js'; import logger from '../../../../logger.js'; /** * List command for projects, epics, and tasks */ export const listCommand = new Command('list') .description('List projects, epics, or tasks') .configureHelp({ sortSubcommands: true }); /** * List projects subcommand */ const listProjectsCommand = new Command('projects') .description('List all projects') .option('-s, --status <status>', 'Filter by status (pending|in_progress|completed|blocked|cancelled)') .option('-t, --tags <tags>', 'Filter by tags (comma-separated)') .option('--created-after <date>', 'Filter by creation date (YYYY-MM-DD)') .option('--created-before <date>', 'Filter by creation date (YYYY-MM-DD)') .option('-l, --limit <number>', 'Limit number of results', '50') .option('-o, --offset <number>', 'Offset for pagination', '0') .option('--format <format>', 'Output format (table|json|yaml)', 'table') .action(async (options) => { try { logger.info({ options }, 'Listing projects via CLI'); // Parse options const limit = parseInt(options.limit); const offset = parseInt(options.offset); if (isNaN(limit) || limit < 1) { CLIUtils.error('Limit must be a positive number'); } if (isNaN(offset) || offset < 0) { CLIUtils.error('Offset must be a non-negative number'); } const tags = CLIUtils.parseTags(options.tags); // Parse dates let createdAfter: Date | undefined; let createdBefore: Date | undefined; if (options.createdAfter) { createdAfter = new Date(options.createdAfter); if (isNaN(createdAfter.getTime())) { CLIUtils.error('Invalid created-after date format. Use YYYY-MM-DD'); } } if (options.createdBefore) { createdBefore = new Date(options.createdBefore); if (isNaN(createdBefore.getTime())) { CLIUtils.error('Invalid created-before date format. Use YYYY-MM-DD'); } } // Validate status if (options.status) { const validStatuses = ['pending', 'in_progress', 'completed', 'blocked', 'cancelled']; if (!validStatuses.includes(options.status)) { CLIUtils.error(`Invalid status. Must be one of: ${validStatuses.join(', ')}`); } } // Prepare query parameters const queryParams: ProjectQueryParams = { status: options.status, tags: tags.length > 0 ? tags : undefined, createdAfter, createdBefore, limit, offset }; // Get projects const projectOperations = getProjectOperations(); const result = await projectOperations.listProjects(queryParams); if (!result.success) { CLIUtils.error(`Failed to list projects: ${result.error}`); } const projects = result.data!; if (projects.length === 0) { CLIUtils.info('No projects found matching the criteria.'); return; } // Display projects console.log(`Found ${projects.length} project(s):\n`); if (options.format === 'table') { const displayData = projects.map(project => ({ ID: project.id, Name: CLIUtils.truncate(project.name, 30), Status: project.status, 'Tech Stack': project.techStack.languages.slice(0, 2).join(', ') + (project.techStack.languages.length > 2 ? '...' : ''), 'Created': CLIUtils.formatDate(project.metadata.createdAt).split(' ')[0], Tags: CLIUtils.truncate(project.metadata.tags.join(', '), 20) || 'None' })); console.log(CLIUtils.formatOutput(displayData, 'table')); } else { console.log(CLIUtils.formatOutput(projects, options.format)); } // Show pagination info if (projects.length === limit) { CLIUtils.info(`Showing ${limit} results. Use --offset ${offset + limit} to see more.`); } logger.info({ projectCount: projects.length }, 'Listed projects successfully via CLI'); } catch (error) { logger.error({ err: error, options }, 'Failed to list projects via CLI'); CLIUtils.error(error instanceof Error ? error.message : 'Unknown error occurred'); } }); /** * List tasks subcommand */ const listTasksCommand = new Command('tasks') .description('List tasks') .option('-p, --project <projectId>', 'Filter by project ID') .option('-e, --epic <epicId>', 'Filter by epic ID') .option('-s, --status <status>', 'Filter by status (pending|in_progress|completed|blocked|cancelled)') .option('--priority <priority>', 'Filter by priority (low|medium|high|critical)') .option('--type <type>', 'Filter by type (development|testing|documentation|research)') .option('-a, --agent <agent>', 'Filter by assigned agent') .option('-t, --tags <tags>', 'Filter by tags (comma-separated)') .option('--created-after <date>', 'Filter by creation date (YYYY-MM-DD)') .option('--created-before <date>', 'Filter by creation date (YYYY-MM-DD)') .option('-l, --limit <number>', 'Limit number of results', '50') .option('-o, --offset <number>', 'Offset for pagination', '0') .option('--format <format>', 'Output format (table|json|yaml)', 'table') .action(async (options) => { try { logger.info({ options }, 'Listing tasks via CLI'); // Parse options const limit = parseInt(options.limit); const offset = parseInt(options.offset); if (isNaN(limit) || limit < 1) { CLIUtils.error('Limit must be a positive number'); } if (isNaN(offset) || offset < 0) { CLIUtils.error('Offset must be a non-negative number'); } const tags = CLIUtils.parseTags(options.tags); // Parse dates let createdAfter: Date | undefined; let createdBefore: Date | undefined; if (options.createdAfter) { createdAfter = new Date(options.createdAfter); if (isNaN(createdAfter.getTime())) { CLIUtils.error('Invalid created-after date format. Use YYYY-MM-DD'); } } if (options.createdBefore) { createdBefore = new Date(options.createdBefore); if (isNaN(createdBefore.getTime())) { CLIUtils.error('Invalid created-before date format. Use YYYY-MM-DD'); } } // Validate enum values if (options.status) { const validStatuses = ['pending', 'in_progress', 'completed', 'blocked', 'cancelled']; if (!validStatuses.includes(options.status)) { CLIUtils.error(`Invalid status. Must be one of: ${validStatuses.join(', ')}`); } } if (options.priority) { const validPriorities = ['low', 'medium', 'high', 'critical']; if (!validPriorities.includes(options.priority)) { CLIUtils.error(`Invalid priority. Must be one of: ${validPriorities.join(', ')}`); } } if (options.type) { const validTypes = ['development', 'testing', 'documentation', 'research']; if (!validTypes.includes(options.type)) { CLIUtils.error(`Invalid type. Must be one of: ${validTypes.join(', ')}`); } } // Prepare query parameters const queryParams: TaskQueryParams = { projectId: options.project, epicId: options.epic, status: options.status, priority: options.priority, type: options.type, assignedAgent: options.agent, tags: tags.length > 0 ? tags : undefined, createdAfter, createdBefore, limit, offset }; // Get tasks const taskOperations = getTaskOperations(); const result = await taskOperations.listTasks(queryParams); if (!result.success) { CLIUtils.error(`Failed to list tasks: ${result.error}`); } const tasks = result.data!; if (tasks.length === 0) { CLIUtils.info('No tasks found matching the criteria.'); return; } // Display tasks console.log(`Found ${tasks.length} task(s):\n`); if (options.format === 'table') { const displayData = tasks.map(task => ({ ID: task.id, Title: CLIUtils.truncate(task.title, 30), Status: task.status, Priority: task.priority, Type: task.type, 'Project': task.projectId, 'Epic': task.epicId, 'Hours': task.estimatedHours, 'Agent': CLIUtils.truncate(task.assignedAgent || 'None', 15), 'Created': CLIUtils.formatDate(task.metadata.createdAt).split(' ')[0] })); console.log(CLIUtils.formatOutput(displayData, 'table')); } else { console.log(CLIUtils.formatOutput(tasks, options.format)); } // Show pagination info if (tasks.length === limit) { CLIUtils.info(`Showing ${limit} results. Use --offset ${offset + limit} to see more.`); } logger.info({ taskCount: tasks.length }, 'Listed tasks successfully via CLI'); } catch (error) { logger.error({ err: error, options }, 'Failed to list tasks via CLI'); CLIUtils.error(error instanceof Error ? error.message : 'Unknown error occurred'); } }); /** * List epics subcommand (placeholder for future implementation) */ const listEpicsCommand = new Command('epics') .description('List epics') .option('-p, --project <projectId>', 'Filter by project ID') .option('--format <format>', 'Output format (table|json|yaml)', 'table') .action(async (options) => { try { logger.info({ options }, 'Listing epics via CLI'); CLIUtils.warning('Epic listing is not yet implemented.'); CLIUtils.info('This feature will be available in a future release.'); if (options.project) { CLIUtils.info(`Would list epics for project: ${options.project}`); } else { CLIUtils.info('Would list all epics'); } } catch (error) { logger.error({ err: error, options }, 'Failed to list epics via CLI'); CLIUtils.error(error instanceof Error ? error.message : 'Unknown error occurred'); } }); // Add subcommands to list command listCommand.addCommand(listProjectsCommand); listCommand.addCommand(listTasksCommand); listCommand.addCommand(listEpicsCommand); // Add help examples listCommand.addHelpText('after', ` Examples: $ vibe-tasks list projects --status in_progress --limit 10 $ vibe-tasks list tasks --project PID-WEBAPP-001 --status pending --priority high $ vibe-tasks list tasks --agent "development-agent" --created-after 2024-01-01 $ vibe-tasks list epics --project PID-WEBAPP-001 `);

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/freshtechbro/vibe-coder-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server