import { Command } from 'commander'; import chalk from 'chalk'; import ora from 'ora'; import axios from 'axios'; import { getApiKey, getApiUrl, getProjectId } from '../utils/config.js'; import { RoadmapChunk, ROADMAP_STATUS } from '@rigstate/shared'; export function createRoadmapCommand(): Command { return new Command('roadmap') .alias('tactical') .description('View project roadmap and task status (Tactical View)') .action(async () => { const spinner = ora('Fetching tactical overview...').start(); try { const apiKey = getApiKey(); const apiUrl = getApiUrl(); const projectId = getProjectId(); if (!projectId) { spinner.fail(chalk.red('Project context missing. Run "rigstate link".')); return; } const response = await axios.get( `${apiUrl}/api/v1/roadmap?project_id=${projectId}`, { headers: { Authorization: `Bearer ${apiKey}` } } ); if (!response.data.success) { throw new Error(response.data.error || 'Failed to fetch roadmap'); } const tasks: RoadmapChunk[] = response.data.data.roadmap || []; spinner.stop(); if (tasks.length === 0) { console.log(chalk.yellow('\nRoadmap is empty. Use the web UI to define your journey.')); return; } console.log('\n' + chalk.bold.underline('šŸ›°ļø TACTICAL OVERVIEW: PROJECT ROADMAP')); console.log(chalk.dim('──────────────────────────────────────────────')); // Initialize columns based on shared status definitions (or subset used here) const columns: Record = { 'IN_PROGRESS': [], 'ACTIVE': [], 'LOCKED': [], 'PENDING': [], 'TODO': [], 'COMPLETED': [] }; tasks.forEach((t) => { const status = t.status as string; // until API returns exact enum if (columns[status]) { columns[status].push(t); } }); // Display by importance/order displayColumn('šŸ”„ IN PROGRESS', columns.IN_PROGRESS, chalk.yellow); displayColumn('ā–¶ļø ACTIVE / NEXT', columns.ACTIVE, chalk.green); displayColumn('šŸ”’ LOCKED', columns.LOCKED, chalk.blue); displayColumn('ā³ PENDING', columns.PENDING, chalk.gray); console.log(chalk.dim('\n──────────────────────────────────────────────')); console.log(chalk.dim(`Total: ${tasks.length} tasks | Run "rigstate work" to start coding.`)); } catch (e: any) { spinner.fail(chalk.red(`\nFailed to fetch roadmap: ${e.message}`)); } }); } function displayColumn(title: string, items: any[], color: any) { if (items.length === 0) return; console.log(`\n${color.bold(title)}`); items.sort((a, b) => a.step_number - b.step_number).forEach(item => { const id = `T-${item.step_number}`.padEnd(8); const priority = item.priority === 'MVP' ? chalk.magenta(' [MVP]') : ''; console.log(` ${color('•')} ${chalk.bold(id)} ${item.title}${priority}`); }); }