diff --git a/.vscode/settings.json b/.vscode/settings.json index 904bf717..ead27faa 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,27 +1,27 @@ { - "json.schemas": [ - { - "fileMatch": ["src/prompts/*.json"], - "url": "./src/prompts/schemas/prompt-template.schema.json" - } - ], - "files.associations": { - "src/prompts/*.json": "json" - }, + "json.schemas": [ + { + "fileMatch": ["src/prompts/*.json"], + "url": "./src/prompts/schemas/prompt-template.schema.json" + } + ], + "files.associations": { + "src/prompts/*.json": "json" + }, - "json.format.enable": true, - "json.validate.enable": true, - "typescript.tsdk": "node_modules/typescript/lib", - "[typescript]": { - "editor.defaultFormatter": "biomejs.biome" - }, - "[typescriptreact]": { - "editor.defaultFormatter": "biomejs.biome" - }, - "[javascript]": { - "editor.defaultFormatter": "biomejs.biome" - }, - "[json]": { - "editor.defaultFormatter": "biomejs.biome" - } + "json.format.enable": true, + "json.validate.enable": true, + "typescript.tsdk": "node_modules/typescript/lib", + "[typescript]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[typescriptreact]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[javascript]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[json]": { + "editor.defaultFormatter": "biomejs.biome" + } } diff --git a/apps/cli/src/commands/list.command.ts b/apps/cli/src/commands/list.command.ts index 65b61d6d..e1fb92e9 100644 --- a/apps/cli/src/commands/list.command.ts +++ b/apps/cli/src/commands/list.command.ts @@ -73,7 +73,6 @@ export class ListTasksCommand extends Command { * Execute the list command */ private async executeCommand(options: ListCommandOptions): Promise { - console.log('executeCommand', options); try { // Validate options if (!this.validateOptions(options)) { diff --git a/apps/cli/src/utils/ui.ts b/apps/cli/src/utils/ui.ts index fe1e5897..caf3552d 100644 --- a/apps/cli/src/utils/ui.ts +++ b/apps/cli/src/utils/ui.ts @@ -9,33 +9,49 @@ import Table from 'cli-table3'; import type { Task, TaskStatus, TaskPriority } from '@tm/core'; /** - * Get colored status display + * Get colored status display with ASCII icons (matches scripts/modules/ui.js style) */ -export function getStatusWithColor(status: TaskStatus): string { - const statusColors: Record string> = { - pending: chalk.yellow, - 'in-progress': chalk.blue, - done: chalk.green, - deferred: chalk.gray, - cancelled: chalk.red, - blocked: chalk.magenta, - review: chalk.cyan +export function getStatusWithColor( + status: TaskStatus, + forTable: boolean = false +): string { + const statusConfig = { + done: { + color: chalk.green, + icon: String.fromCharCode(8730), + tableIcon: String.fromCharCode(8730) + }, // √ + pending: { color: chalk.yellow, icon: 'o', tableIcon: 'o' }, + 'in-progress': { + color: chalk.hex('#FFA500'), + icon: String.fromCharCode(9654), + tableIcon: '>' + }, // β–Ά + deferred: { color: chalk.gray, icon: 'x', tableIcon: 'x' }, + blocked: { color: chalk.red, icon: '!', tableIcon: '!' }, + review: { color: chalk.magenta, icon: '?', tableIcon: '?' }, + cancelled: { color: chalk.gray, icon: 'X', tableIcon: 'X' } }; - const statusEmojis: Record = { - pending: '⏳', - 'in-progress': 'πŸš€', - done: 'βœ…', - deferred: '⏸️', - cancelled: '❌', - blocked: '🚫', - review: 'πŸ‘€' + const config = statusConfig[status] || { + color: chalk.red, + icon: 'X', + tableIcon: 'X' }; - const colorFn = statusColors[status] || chalk.white; - const emoji = statusEmojis[status] || ''; + // Use simple ASCII characters for stable display + const simpleIcons = { + done: String.fromCharCode(8730), // √ + pending: 'o', + 'in-progress': '>', + deferred: 'x', + blocked: '!', + review: '?', + cancelled: 'X' + }; - return `${emoji} ${colorFn(status)}`; + const icon = forTable ? simpleIcons[status] || 'X' : config.icon; + return config.color(`${icon} ${status}`); } /** @@ -109,23 +125,23 @@ export function createProgressBar( */ export function displayBanner(title: string = 'Task Master'): void { console.log( - boxen(chalk.cyan.bold(title), { + boxen(chalk.white.bold(title), { padding: 1, margin: { top: 1, bottom: 1 }, - borderStyle: 'double', - borderColor: 'cyan', + borderStyle: 'round', + borderColor: 'blue', textAlignment: 'center' }) ); } /** - * Display an error message + * Display an error message (matches scripts/modules/ui.js style) */ export function displayError(message: string, details?: string): void { console.error( boxen( - chalk.red.bold('Error: ') + + chalk.red.bold('X Error: ') + chalk.white(message) + (details ? '\n\n' + chalk.gray(details) : ''), { @@ -142,11 +158,14 @@ export function displayError(message: string, details?: string): void { */ export function displaySuccess(message: string): void { console.log( - boxen(chalk.green.bold('βœ“ ') + chalk.white(message), { - padding: 1, - borderStyle: 'round', - borderColor: 'green' - }) + boxen( + chalk.green.bold(String.fromCharCode(8730) + ' ') + chalk.white(message), + { + padding: 1, + borderStyle: 'round', + borderColor: 'green' + } + ) ); } @@ -168,7 +187,7 @@ export function displayWarning(message: string): void { */ export function displayInfo(message: string): void { console.log( - boxen(chalk.blue.bold('β„Ή ') + chalk.white(message), { + boxen(chalk.blue.bold('i ') + chalk.white(message), { padding: 1, borderStyle: 'round', borderColor: 'blue' @@ -225,30 +244,42 @@ export function createTaskTable( showDependencies = true } = options || {}; - const headers = ['ID', 'Title', 'Status', 'Priority']; - const colWidths = [8, 40, 15, 10]; + // Calculate dynamic column widths based on terminal width + const terminalWidth = process.stdout.columns || 100; + const baseColWidths = showComplexity + ? [8, Math.floor(terminalWidth * 0.35), 18, 12, 15, 12] // ID, Title, Status, Priority, Dependencies, Complexity + : [8, Math.floor(terminalWidth * 0.4), 18, 12, 20]; // ID, Title, Status, Priority, Dependencies + + const headers = [ + chalk.blue.bold('ID'), + chalk.blue.bold('Title'), + chalk.blue.bold('Status'), + chalk.blue.bold('Priority') + ]; + const colWidths = baseColWidths.slice(0, 4); if (showDependencies) { - headers.push('Dependencies'); - colWidths.push(20); + headers.push(chalk.blue.bold('Dependencies')); + colWidths.push(baseColWidths[4]); } if (showComplexity) { - headers.push('Complexity'); - colWidths.push(12); + headers.push(chalk.blue.bold('Complexity')); + colWidths.push(baseColWidths[5] || 12); } const table = new Table({ head: headers, - style: { head: ['blue'] }, - colWidths + style: { head: [], border: [] }, + colWidths, + wordWrap: true }); tasks.forEach((task) => { const row: string[] = [ chalk.cyan(task.id.toString()), - truncate(task.title, 38), - getStatusWithColor(task.status), + truncate(task.title, colWidths[1] - 3), + getStatusWithColor(task.status, true), // Use table version getPriorityWithColor(task.priority) ]; @@ -267,8 +298,8 @@ export function createTaskTable( task.subtasks.forEach((subtask) => { const subRow: string[] = [ chalk.gray(` └─ ${subtask.id}`), - chalk.gray(truncate(subtask.title, 36)), - getStatusWithColor(subtask.status), + chalk.gray(truncate(subtask.title, colWidths[1] - 6)), + chalk.gray(getStatusWithColor(subtask.status, true)), chalk.gray(subtask.priority || 'medium') ]; @@ -276,8 +307,8 @@ export function createTaskTable( subRow.push( chalk.gray( subtask.dependencies && subtask.dependencies.length > 0 - ? subtask.dependencies.join(', ') - : 'none' + ? subtask.dependencies.map((dep) => String(dep)).join(', ') + : 'None' ) ); } diff --git a/package.json b/package.json index 6fd8987e..0ae0b673 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "workspaces": ["apps/*", "packages/*", "."], "scripts": { "build": "npm run build:packages && tsup", - "dev": "npm run build:packages && (npm run dev:packages & tsup --watch --onSuccess 'echo Build complete')", + "dev": "npm run build:packages && npm link && (npm run dev:packages & tsup --watch --onSuccess 'echo Build complete && npm link')", "dev:packages": "(cd packages/tm-core && npm run dev) & (cd apps/cli && npm run dev) & wait", "dev:core": "cd packages/tm-core && npm run dev", "dev:cli": "cd apps/cli && npm run dev", diff --git a/packages/tm-core/package.json b/packages/tm-core/package.json index e1f6aceb..8a60568a 100644 --- a/packages/tm-core/package.json +++ b/packages/tm-core/package.json @@ -3,15 +3,13 @@ "version": "1.0.0", "description": "Core library for Task Master - TypeScript task management system", "type": "module", - "main": "./dist/index.cjs", - "module": "./dist/index.js", "types": "./dist/index.d.ts", + "main": "./dist/index.js", "exports": { ".": { - "development": "./src/index.ts", - "types": "./dist/index.d.ts", + "types": "./src/index.ts", "import": "./dist/index.js", - "require": "./dist/index.cjs" + "require": "./dist/index.js" } }, "scripts": { diff --git a/packages/tm-core/src/config/config-manager.ts b/packages/tm-core/src/config/config-manager.ts index a0105ec6..060b9947 100644 --- a/packages/tm-core/src/config/config-manager.ts +++ b/packages/tm-core/src/config/config-manager.ts @@ -196,14 +196,14 @@ export class ConfigManager { * Get the currently active tag */ getActiveTag(): string { - return this.stateManager.getActiveTag(); + return this.stateManager.getCurrentTag(); } /** * Set the active tag */ async setActiveTag(tag: string): Promise { - await this.stateManager.setActiveTag(tag); + await this.stateManager.setCurrentTag(tag); } // ==================== Configuration Updates ==================== diff --git a/packages/tm-core/src/config/services/runtime-state-manager.service.ts b/packages/tm-core/src/config/services/runtime-state-manager.service.ts index be4cd860..6139289d 100644 --- a/packages/tm-core/src/config/services/runtime-state-manager.service.ts +++ b/packages/tm-core/src/config/services/runtime-state-manager.service.ts @@ -16,7 +16,7 @@ import { DEFAULT_CONFIG_VALUES } from '../../interfaces/configuration.interface. */ export interface RuntimeState { /** Currently active tag */ - activeTag: string; + currentTag: string; /** Last updated timestamp */ lastUpdated?: string; /** Additional metadata */ @@ -34,7 +34,7 @@ export class RuntimeStateManager { constructor(projectRoot: string) { this.stateFilePath = path.join(projectRoot, '.taskmaster', 'state.json'); this.currentState = { - activeTag: DEFAULT_CONFIG_VALUES.TAGS.DEFAULT_TAG + currentTag: DEFAULT_CONFIG_VALUES.TAGS.DEFAULT_TAG }; } @@ -44,11 +44,21 @@ export class RuntimeStateManager { async loadState(): Promise { try { const stateData = await fs.readFile(this.stateFilePath, 'utf-8'); - const state = JSON.parse(stateData); + const rawState = JSON.parse(stateData); - // Apply environment variable override for active tag + // Map legacy field names to current interface + const state: RuntimeState = { + currentTag: + rawState.currentTag || + rawState.activeTag || + DEFAULT_CONFIG_VALUES.TAGS.DEFAULT_TAG, + lastUpdated: rawState.lastUpdated, + metadata: rawState.metadata + }; + + // Apply environment variable override for current tag if (process.env.TASKMASTER_TAG) { - state.activeTag = process.env.TASKMASTER_TAG; + state.currentTag = process.env.TASKMASTER_TAG; } this.currentState = state; @@ -60,7 +70,7 @@ export class RuntimeStateManager { // Check environment variable if (process.env.TASKMASTER_TAG) { - this.currentState.activeTag = process.env.TASKMASTER_TAG; + this.currentState.currentTag = process.env.TASKMASTER_TAG; } return this.currentState; @@ -103,15 +113,15 @@ export class RuntimeStateManager { /** * Get the currently active tag */ - getActiveTag(): string { - return this.currentState.activeTag; + getCurrentTag(): string { + return this.currentState.currentTag; } /** - * Set the active tag + * Set the current tag */ - async setActiveTag(tag: string): Promise { - this.currentState.activeTag = tag; + async setCurrentTag(tag: string): Promise { + this.currentState.currentTag = tag; await this.saveState(); } @@ -145,7 +155,7 @@ export class RuntimeStateManager { } } this.currentState = { - activeTag: DEFAULT_CONFIG_VALUES.TAGS.DEFAULT_TAG + currentTag: DEFAULT_CONFIG_VALUES.TAGS.DEFAULT_TAG }; } } diff --git a/packages/tm-core/src/storage/file-storage.ts b/packages/tm-core/src/storage/file-storage.ts index 2f8ceed2..d715b410 100644 --- a/packages/tm-core/src/storage/file-storage.ts +++ b/packages/tm-core/src/storage/file-storage.ts @@ -128,15 +128,16 @@ export class FileStorage implements IStorage { await this.ensureDirectoryExists(); // Normalize task IDs to strings (force string IDs everywhere) - const normalizedTasks = tasks.map(task => ({ + const normalizedTasks = tasks.map((task) => ({ ...task, id: String(task.id), // Force ID to string - dependencies: task.dependencies?.map(dep => String(dep)) || [], - subtasks: task.subtasks?.map(subtask => ({ - ...subtask, - id: String(subtask.id), - parentId: String(subtask.parentId) - })) || [] + dependencies: task.dependencies?.map((dep) => String(dep)) || [], + subtasks: + task.subtasks?.map((subtask) => ({ + ...subtask, + id: String(subtask.id), + parentId: String(subtask.parentId) + })) || [] })); // Check if we need to use legacy format @@ -157,7 +158,8 @@ export class FileStorage implements IStorage { version: '1.0.0', lastModified: new Date().toISOString(), taskCount: normalizedTasks.length, - completedCount: normalizedTasks.filter((t) => t.status === 'done').length, + completedCount: normalizedTasks.filter((t) => t.status === 'done') + .length, tags: [resolvedTag] } } @@ -170,7 +172,8 @@ export class FileStorage implements IStorage { version: '1.0.0', lastModified: new Date().toISOString(), taskCount: normalizedTasks.length, - completedCount: normalizedTasks.filter((t) => t.status === 'done').length, + completedCount: normalizedTasks.filter((t) => t.status === 'done') + .length, tags: tag ? [tag] : [] } }; @@ -183,7 +186,8 @@ export class FileStorage implements IStorage { version: '1.0.0', lastModified: new Date().toISOString(), taskCount: normalizedTasks.length, - completedCount: normalizedTasks.filter((t) => t.status === 'done').length, + completedCount: normalizedTasks.filter((t) => t.status === 'done') + .length, tags: tag ? [tag] : [] } };