chore: typescript fixes and quality of life improvements and formatting

This commit is contained in:
Ralph Khreish
2025-08-27 17:07:41 +02:00
parent fb44c58a23
commit fc46a12449
8 changed files with 143 additions and 101 deletions

View File

@@ -73,7 +73,6 @@ export class ListTasksCommand extends Command {
* Execute the list command * Execute the list command
*/ */
private async executeCommand(options: ListCommandOptions): Promise<void> { private async executeCommand(options: ListCommandOptions): Promise<void> {
console.log('executeCommand', options);
try { try {
// Validate options // Validate options
if (!this.validateOptions(options)) { if (!this.validateOptions(options)) {

View File

@@ -9,33 +9,49 @@ import Table from 'cli-table3';
import type { Task, TaskStatus, TaskPriority } from '@tm/core'; 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 { export function getStatusWithColor(
const statusColors: Record<TaskStatus, (text: string) => string> = { status: TaskStatus,
pending: chalk.yellow, forTable: boolean = false
'in-progress': chalk.blue, ): string {
done: chalk.green, const statusConfig = {
deferred: chalk.gray, done: {
cancelled: chalk.red, color: chalk.green,
blocked: chalk.magenta, icon: String.fromCharCode(8730),
review: chalk.cyan 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<TaskStatus, string> = { const config = statusConfig[status] || {
pending: '⏳', color: chalk.red,
'in-progress': '🚀', icon: 'X',
done: '', tableIcon: 'X'
deferred: '⏸️',
cancelled: '❌',
blocked: '🚫',
review: '👀'
}; };
const colorFn = statusColors[status] || chalk.white; // Use simple ASCII characters for stable display
const emoji = statusEmojis[status] || ''; 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 { export function displayBanner(title: string = 'Task Master'): void {
console.log( console.log(
boxen(chalk.cyan.bold(title), { boxen(chalk.white.bold(title), {
padding: 1, padding: 1,
margin: { top: 1, bottom: 1 }, margin: { top: 1, bottom: 1 },
borderStyle: 'double', borderStyle: 'round',
borderColor: 'cyan', borderColor: 'blue',
textAlignment: 'center' textAlignment: 'center'
}) })
); );
} }
/** /**
* Display an error message * Display an error message (matches scripts/modules/ui.js style)
*/ */
export function displayError(message: string, details?: string): void { export function displayError(message: string, details?: string): void {
console.error( console.error(
boxen( boxen(
chalk.red.bold('Error: ') + chalk.red.bold('X Error: ') +
chalk.white(message) + chalk.white(message) +
(details ? '\n\n' + chalk.gray(details) : ''), (details ? '\n\n' + chalk.gray(details) : ''),
{ {
@@ -142,11 +158,14 @@ export function displayError(message: string, details?: string): void {
*/ */
export function displaySuccess(message: string): void { export function displaySuccess(message: string): void {
console.log( console.log(
boxen(chalk.green.bold('✓ ') + chalk.white(message), { boxen(
chalk.green.bold(String.fromCharCode(8730) + ' ') + chalk.white(message),
{
padding: 1, padding: 1,
borderStyle: 'round', borderStyle: 'round',
borderColor: 'green' borderColor: 'green'
}) }
)
); );
} }
@@ -168,7 +187,7 @@ export function displayWarning(message: string): void {
*/ */
export function displayInfo(message: string): void { export function displayInfo(message: string): void {
console.log( console.log(
boxen(chalk.blue.bold(' ') + chalk.white(message), { boxen(chalk.blue.bold('i ') + chalk.white(message), {
padding: 1, padding: 1,
borderStyle: 'round', borderStyle: 'round',
borderColor: 'blue' borderColor: 'blue'
@@ -225,30 +244,42 @@ export function createTaskTable(
showDependencies = true showDependencies = true
} = options || {}; } = options || {};
const headers = ['ID', 'Title', 'Status', 'Priority']; // Calculate dynamic column widths based on terminal width
const colWidths = [8, 40, 15, 10]; 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) { if (showDependencies) {
headers.push('Dependencies'); headers.push(chalk.blue.bold('Dependencies'));
colWidths.push(20); colWidths.push(baseColWidths[4]);
} }
if (showComplexity) { if (showComplexity) {
headers.push('Complexity'); headers.push(chalk.blue.bold('Complexity'));
colWidths.push(12); colWidths.push(baseColWidths[5] || 12);
} }
const table = new Table({ const table = new Table({
head: headers, head: headers,
style: { head: ['blue'] }, style: { head: [], border: [] },
colWidths colWidths,
wordWrap: true
}); });
tasks.forEach((task) => { tasks.forEach((task) => {
const row: string[] = [ const row: string[] = [
chalk.cyan(task.id.toString()), chalk.cyan(task.id.toString()),
truncate(task.title, 38), truncate(task.title, colWidths[1] - 3),
getStatusWithColor(task.status), getStatusWithColor(task.status, true), // Use table version
getPriorityWithColor(task.priority) getPriorityWithColor(task.priority)
]; ];
@@ -267,8 +298,8 @@ export function createTaskTable(
task.subtasks.forEach((subtask) => { task.subtasks.forEach((subtask) => {
const subRow: string[] = [ const subRow: string[] = [
chalk.gray(` └─ ${subtask.id}`), chalk.gray(` └─ ${subtask.id}`),
chalk.gray(truncate(subtask.title, 36)), chalk.gray(truncate(subtask.title, colWidths[1] - 6)),
getStatusWithColor(subtask.status), chalk.gray(getStatusWithColor(subtask.status, true)),
chalk.gray(subtask.priority || 'medium') chalk.gray(subtask.priority || 'medium')
]; ];
@@ -276,8 +307,8 @@ export function createTaskTable(
subRow.push( subRow.push(
chalk.gray( chalk.gray(
subtask.dependencies && subtask.dependencies.length > 0 subtask.dependencies && subtask.dependencies.length > 0
? subtask.dependencies.join(', ') ? subtask.dependencies.map((dep) => String(dep)).join(', ')
: 'none' : 'None'
) )
); );
} }

View File

@@ -12,7 +12,7 @@
"workspaces": ["apps/*", "packages/*", "."], "workspaces": ["apps/*", "packages/*", "."],
"scripts": { "scripts": {
"build": "npm run build:packages && tsup", "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: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:core": "cd packages/tm-core && npm run dev",
"dev:cli": "cd apps/cli && npm run dev", "dev:cli": "cd apps/cli && npm run dev",

View File

@@ -3,15 +3,13 @@
"version": "1.0.0", "version": "1.0.0",
"description": "Core library for Task Master - TypeScript task management system", "description": "Core library for Task Master - TypeScript task management system",
"type": "module", "type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
"main": "./dist/index.js",
"exports": { "exports": {
".": { ".": {
"development": "./src/index.ts", "types": "./src/index.ts",
"types": "./dist/index.d.ts",
"import": "./dist/index.js", "import": "./dist/index.js",
"require": "./dist/index.cjs" "require": "./dist/index.js"
} }
}, },
"scripts": { "scripts": {

View File

@@ -196,14 +196,14 @@ export class ConfigManager {
* Get the currently active tag * Get the currently active tag
*/ */
getActiveTag(): string { getActiveTag(): string {
return this.stateManager.getActiveTag(); return this.stateManager.getCurrentTag();
} }
/** /**
* Set the active tag * Set the active tag
*/ */
async setActiveTag(tag: string): Promise<void> { async setActiveTag(tag: string): Promise<void> {
await this.stateManager.setActiveTag(tag); await this.stateManager.setCurrentTag(tag);
} }
// ==================== Configuration Updates ==================== // ==================== Configuration Updates ====================

View File

@@ -16,7 +16,7 @@ import { DEFAULT_CONFIG_VALUES } from '../../interfaces/configuration.interface.
*/ */
export interface RuntimeState { export interface RuntimeState {
/** Currently active tag */ /** Currently active tag */
activeTag: string; currentTag: string;
/** Last updated timestamp */ /** Last updated timestamp */
lastUpdated?: string; lastUpdated?: string;
/** Additional metadata */ /** Additional metadata */
@@ -34,7 +34,7 @@ export class RuntimeStateManager {
constructor(projectRoot: string) { constructor(projectRoot: string) {
this.stateFilePath = path.join(projectRoot, '.taskmaster', 'state.json'); this.stateFilePath = path.join(projectRoot, '.taskmaster', 'state.json');
this.currentState = { 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<RuntimeState> { async loadState(): Promise<RuntimeState> {
try { try {
const stateData = await fs.readFile(this.stateFilePath, 'utf-8'); 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) { if (process.env.TASKMASTER_TAG) {
state.activeTag = process.env.TASKMASTER_TAG; state.currentTag = process.env.TASKMASTER_TAG;
} }
this.currentState = state; this.currentState = state;
@@ -60,7 +70,7 @@ export class RuntimeStateManager {
// Check environment variable // Check environment variable
if (process.env.TASKMASTER_TAG) { if (process.env.TASKMASTER_TAG) {
this.currentState.activeTag = process.env.TASKMASTER_TAG; this.currentState.currentTag = process.env.TASKMASTER_TAG;
} }
return this.currentState; return this.currentState;
@@ -103,15 +113,15 @@ export class RuntimeStateManager {
/** /**
* Get the currently active tag * Get the currently active tag
*/ */
getActiveTag(): string { getCurrentTag(): string {
return this.currentState.activeTag; return this.currentState.currentTag;
} }
/** /**
* Set the active tag * Set the current tag
*/ */
async setActiveTag(tag: string): Promise<void> { async setCurrentTag(tag: string): Promise<void> {
this.currentState.activeTag = tag; this.currentState.currentTag = tag;
await this.saveState(); await this.saveState();
} }
@@ -145,7 +155,7 @@ export class RuntimeStateManager {
} }
} }
this.currentState = { this.currentState = {
activeTag: DEFAULT_CONFIG_VALUES.TAGS.DEFAULT_TAG currentTag: DEFAULT_CONFIG_VALUES.TAGS.DEFAULT_TAG
}; };
} }
} }

View File

@@ -128,11 +128,12 @@ export class FileStorage implements IStorage {
await this.ensureDirectoryExists(); await this.ensureDirectoryExists();
// Normalize task IDs to strings (force string IDs everywhere) // Normalize task IDs to strings (force string IDs everywhere)
const normalizedTasks = tasks.map(task => ({ const normalizedTasks = tasks.map((task) => ({
...task, ...task,
id: String(task.id), // Force ID to string id: String(task.id), // Force ID to string
dependencies: task.dependencies?.map(dep => String(dep)) || [], dependencies: task.dependencies?.map((dep) => String(dep)) || [],
subtasks: task.subtasks?.map(subtask => ({ subtasks:
task.subtasks?.map((subtask) => ({
...subtask, ...subtask,
id: String(subtask.id), id: String(subtask.id),
parentId: String(subtask.parentId) parentId: String(subtask.parentId)
@@ -157,7 +158,8 @@ export class FileStorage implements IStorage {
version: '1.0.0', version: '1.0.0',
lastModified: new Date().toISOString(), lastModified: new Date().toISOString(),
taskCount: normalizedTasks.length, taskCount: normalizedTasks.length,
completedCount: normalizedTasks.filter((t) => t.status === 'done').length, completedCount: normalizedTasks.filter((t) => t.status === 'done')
.length,
tags: [resolvedTag] tags: [resolvedTag]
} }
} }
@@ -170,7 +172,8 @@ export class FileStorage implements IStorage {
version: '1.0.0', version: '1.0.0',
lastModified: new Date().toISOString(), lastModified: new Date().toISOString(),
taskCount: normalizedTasks.length, taskCount: normalizedTasks.length,
completedCount: normalizedTasks.filter((t) => t.status === 'done').length, completedCount: normalizedTasks.filter((t) => t.status === 'done')
.length,
tags: tag ? [tag] : [] tags: tag ? [tag] : []
} }
}; };
@@ -183,7 +186,8 @@ export class FileStorage implements IStorage {
version: '1.0.0', version: '1.0.0',
lastModified: new Date().toISOString(), lastModified: new Date().toISOString(),
taskCount: normalizedTasks.length, taskCount: normalizedTasks.length,
completedCount: normalizedTasks.filter((t) => t.status === 'done').length, completedCount: normalizedTasks.filter((t) => t.status === 'done')
.length,
tags: tag ? [tag] : [] tags: tag ? [tag] : []
} }
}; };