Compare commits
1 Commits
docs/auto-
...
docs/auto-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c27a61361a |
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
"extension": minor
|
|
||||||
---
|
|
||||||
|
|
||||||
Added a Start Build button to the VSCODE Task Properties Right Panel
|
|
||||||
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@@ -61,7 +61,7 @@ jobs:
|
|||||||
timeout-minutes: 5
|
timeout-minutes: 5
|
||||||
|
|
||||||
- name: Typecheck
|
- name: Typecheck
|
||||||
run: npm run turbo:typecheck
|
run: npm run typecheck
|
||||||
env:
|
env:
|
||||||
FORCE_COLOR: 1
|
FORCE_COLOR: 1
|
||||||
|
|
||||||
@@ -84,7 +84,7 @@ jobs:
|
|||||||
timeout-minutes: 5
|
timeout-minutes: 5
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: npm run turbo:build
|
run: npm run build
|
||||||
env:
|
env:
|
||||||
NODE_ENV: production
|
NODE_ENV: production
|
||||||
FORCE_COLOR: 1
|
FORCE_COLOR: 1
|
||||||
|
|||||||
@@ -6,10 +6,15 @@
|
|||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"types": "./src/index.ts",
|
"types": "./src/index.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts"
|
".": {
|
||||||
|
"types": "./src/index.ts",
|
||||||
|
"import": "./dist/index.js"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"files": ["dist", "README.md"],
|
"files": ["dist", "README.md"],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"build": "tsc",
|
||||||
|
"dev": "tsc --watch",
|
||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
"lint": "biome check src",
|
"lint": "biome check src",
|
||||||
"format": "biome format --write src",
|
"format": "biome format --write src",
|
||||||
@@ -43,10 +48,5 @@
|
|||||||
},
|
},
|
||||||
"keywords": ["task-master", "cli", "task-management", "productivity"],
|
"keywords": ["task-master", "cli", "task-management", "productivity"],
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"typesVersions": {
|
|
||||||
"*": {
|
|
||||||
"*": ["src/*"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,18 +17,6 @@ import {
|
|||||||
} from '@tm/core';
|
} from '@tm/core';
|
||||||
import type { StorageType } from '@tm/core/types';
|
import type { StorageType } from '@tm/core/types';
|
||||||
import * as ui from '../utils/ui.js';
|
import * as ui from '../utils/ui.js';
|
||||||
import {
|
|
||||||
displayHeader,
|
|
||||||
displayDashboards,
|
|
||||||
calculateTaskStatistics,
|
|
||||||
calculateSubtaskStatistics,
|
|
||||||
calculateDependencyStatistics,
|
|
||||||
getPriorityBreakdown,
|
|
||||||
displayRecommendedNextTask,
|
|
||||||
getTaskDescription,
|
|
||||||
displaySuggestedNextSteps,
|
|
||||||
type NextTaskInfo
|
|
||||||
} from '../ui/index.js';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Options interface for the list command
|
* Options interface for the list command
|
||||||
@@ -257,16 +245,19 @@ export class ListTasksCommand extends Command {
|
|||||||
* Display in text format with tables
|
* Display in text format with tables
|
||||||
*/
|
*/
|
||||||
private displayText(data: ListTasksResult, withSubtasks?: boolean): void {
|
private displayText(data: ListTasksResult, withSubtasks?: boolean): void {
|
||||||
const { tasks, tag } = data;
|
const { tasks, total, filtered, tag, storageType } = data;
|
||||||
|
|
||||||
// Get file path for display
|
// Header
|
||||||
const filePath = this.tmCore ? `.taskmaster/tasks/tasks.json` : undefined;
|
ui.displayBanner(`Task List${tag ? ` (${tag})` : ''}`);
|
||||||
|
|
||||||
// Display header without banner (banner already shown by main CLI)
|
// Statistics
|
||||||
displayHeader({
|
console.log(chalk.blue.bold('\n📊 Statistics:\n'));
|
||||||
tag: tag || 'master',
|
console.log(` Total tasks: ${chalk.cyan(total)}`);
|
||||||
filePath: filePath
|
console.log(` Filtered: ${chalk.cyan(filtered)}`);
|
||||||
});
|
if (tag) {
|
||||||
|
console.log(` Tag: ${chalk.cyan(tag)}`);
|
||||||
|
}
|
||||||
|
console.log(` Storage: ${chalk.cyan(storageType)}`);
|
||||||
|
|
||||||
// No tasks message
|
// No tasks message
|
||||||
if (tasks.length === 0) {
|
if (tasks.length === 0) {
|
||||||
@@ -274,50 +265,21 @@ export class ListTasksCommand extends Command {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate statistics
|
// Task table
|
||||||
const taskStats = calculateTaskStatistics(tasks);
|
console.log(chalk.blue.bold(`\n📋 Tasks (${tasks.length}):\n`));
|
||||||
const subtaskStats = calculateSubtaskStatistics(tasks);
|
|
||||||
const depStats = calculateDependencyStatistics(tasks);
|
|
||||||
const priorityBreakdown = getPriorityBreakdown(tasks);
|
|
||||||
|
|
||||||
// Find next task following the same logic as findNextTask
|
|
||||||
const nextTask = this.findNextTask(tasks);
|
|
||||||
|
|
||||||
// Display dashboard boxes
|
|
||||||
displayDashboards(
|
|
||||||
taskStats,
|
|
||||||
subtaskStats,
|
|
||||||
priorityBreakdown,
|
|
||||||
depStats,
|
|
||||||
nextTask
|
|
||||||
);
|
|
||||||
|
|
||||||
// Task table - no title, just show the table directly
|
|
||||||
console.log(
|
console.log(
|
||||||
ui.createTaskTable(tasks, {
|
ui.createTaskTable(tasks, {
|
||||||
showSubtasks: withSubtasks,
|
showSubtasks: withSubtasks,
|
||||||
showDependencies: true,
|
showDependencies: true
|
||||||
showComplexity: true // Enable complexity column
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// Display recommended next task section immediately after table
|
// Progress bar
|
||||||
if (nextTask) {
|
const completedCount = tasks.filter(
|
||||||
// Find the full task object to get description
|
(t: Task) => t.status === 'done'
|
||||||
const fullTask = tasks.find((t) => String(t.id) === String(nextTask.id));
|
).length;
|
||||||
const description = fullTask ? getTaskDescription(fullTask) : undefined;
|
console.log(chalk.blue.bold('\n📊 Overall Progress:\n'));
|
||||||
|
console.log(` ${ui.createProgressBar(completedCount, tasks.length)}`);
|
||||||
displayRecommendedNextTask({
|
|
||||||
...nextTask,
|
|
||||||
status: 'pending', // Next task is typically pending
|
|
||||||
description
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
displayRecommendedNextTask(undefined);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display suggested next steps at the end
|
|
||||||
displaySuggestedNextSteps();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -327,128 +289,6 @@ export class ListTasksCommand extends Command {
|
|||||||
this.lastResult = result;
|
this.lastResult = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Find the next task to work on
|
|
||||||
* Implements the same logic as scripts/modules/task-manager/find-next-task.js
|
|
||||||
*/
|
|
||||||
private findNextTask(tasks: Task[]): NextTaskInfo | undefined {
|
|
||||||
const priorityValues: Record<string, number> = {
|
|
||||||
critical: 4,
|
|
||||||
high: 3,
|
|
||||||
medium: 2,
|
|
||||||
low: 1
|
|
||||||
};
|
|
||||||
|
|
||||||
// Build set of completed task IDs (including subtasks)
|
|
||||||
const completedIds = new Set<string>();
|
|
||||||
tasks.forEach((t) => {
|
|
||||||
if (t.status === 'done' || t.status === 'completed') {
|
|
||||||
completedIds.add(String(t.id));
|
|
||||||
}
|
|
||||||
if (t.subtasks) {
|
|
||||||
t.subtasks.forEach((st) => {
|
|
||||||
if (st.status === 'done' || st.status === 'completed') {
|
|
||||||
completedIds.add(`${t.id}.${st.id}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// First, look for eligible subtasks in in-progress parent tasks
|
|
||||||
const candidateSubtasks: NextTaskInfo[] = [];
|
|
||||||
|
|
||||||
tasks
|
|
||||||
.filter(
|
|
||||||
(t) => t.status === 'in-progress' && t.subtasks && t.subtasks.length > 0
|
|
||||||
)
|
|
||||||
.forEach((parent) => {
|
|
||||||
parent.subtasks!.forEach((st) => {
|
|
||||||
const stStatus = (st.status || 'pending').toLowerCase();
|
|
||||||
if (stStatus !== 'pending' && stStatus !== 'in-progress') return;
|
|
||||||
|
|
||||||
// Check if dependencies are satisfied
|
|
||||||
const fullDeps =
|
|
||||||
st.dependencies?.map((d) => {
|
|
||||||
// Handle both numeric and string IDs
|
|
||||||
if (typeof d === 'string' && d.includes('.')) {
|
|
||||||
return d;
|
|
||||||
}
|
|
||||||
return `${parent.id}.${d}`;
|
|
||||||
}) ?? [];
|
|
||||||
|
|
||||||
const depsSatisfied =
|
|
||||||
fullDeps.length === 0 ||
|
|
||||||
fullDeps.every((depId) => completedIds.has(String(depId)));
|
|
||||||
|
|
||||||
if (depsSatisfied) {
|
|
||||||
candidateSubtasks.push({
|
|
||||||
id: `${parent.id}.${st.id}`,
|
|
||||||
title: st.title || `Subtask ${st.id}`,
|
|
||||||
priority: st.priority || parent.priority || 'medium',
|
|
||||||
dependencies: fullDeps.map((d) => String(d))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (candidateSubtasks.length > 0) {
|
|
||||||
// Sort by priority, then by dependencies count, then by ID
|
|
||||||
candidateSubtasks.sort((a, b) => {
|
|
||||||
const pa = priorityValues[a.priority || 'medium'] ?? 2;
|
|
||||||
const pb = priorityValues[b.priority || 'medium'] ?? 2;
|
|
||||||
if (pb !== pa) return pb - pa;
|
|
||||||
|
|
||||||
const depCountA = a.dependencies?.length || 0;
|
|
||||||
const depCountB = b.dependencies?.length || 0;
|
|
||||||
if (depCountA !== depCountB) return depCountA - depCountB;
|
|
||||||
|
|
||||||
return String(a.id).localeCompare(String(b.id));
|
|
||||||
});
|
|
||||||
return candidateSubtasks[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fall back to finding eligible top-level tasks
|
|
||||||
const eligibleTasks = tasks.filter((task) => {
|
|
||||||
// Skip non-eligible statuses
|
|
||||||
const status = (task.status || 'pending').toLowerCase();
|
|
||||||
if (status !== 'pending' && status !== 'in-progress') return false;
|
|
||||||
|
|
||||||
// Check dependencies
|
|
||||||
const deps = task.dependencies || [];
|
|
||||||
const depsSatisfied =
|
|
||||||
deps.length === 0 ||
|
|
||||||
deps.every((depId) => completedIds.has(String(depId)));
|
|
||||||
|
|
||||||
return depsSatisfied;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (eligibleTasks.length === 0) return undefined;
|
|
||||||
|
|
||||||
// Sort eligible tasks
|
|
||||||
eligibleTasks.sort((a, b) => {
|
|
||||||
// Priority (higher first)
|
|
||||||
const pa = priorityValues[a.priority || 'medium'] ?? 2;
|
|
||||||
const pb = priorityValues[b.priority || 'medium'] ?? 2;
|
|
||||||
if (pb !== pa) return pb - pa;
|
|
||||||
|
|
||||||
// Dependencies count (fewer first)
|
|
||||||
const depCountA = a.dependencies?.length || 0;
|
|
||||||
const depCountB = b.dependencies?.length || 0;
|
|
||||||
if (depCountA !== depCountB) return depCountA - depCountB;
|
|
||||||
|
|
||||||
// ID (lower first)
|
|
||||||
return Number(a.id) - Number(b.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
const nextTask = eligibleTasks[0];
|
|
||||||
return {
|
|
||||||
id: nextTask.id,
|
|
||||||
title: nextTask.title,
|
|
||||||
priority: nextTask.priority,
|
|
||||||
dependencies: nextTask.dependencies?.map((d) => String(d))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the last result (for programmatic usage)
|
* Get the last result (for programmatic usage)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -9,14 +9,6 @@ import boxen from 'boxen';
|
|||||||
import { createTaskMasterCore, type Task, type TaskMasterCore } from '@tm/core';
|
import { createTaskMasterCore, type Task, type TaskMasterCore } from '@tm/core';
|
||||||
import type { StorageType } from '@tm/core/types';
|
import type { StorageType } from '@tm/core/types';
|
||||||
import * as ui from '../utils/ui.js';
|
import * as ui from '../utils/ui.js';
|
||||||
import {
|
|
||||||
displayTaskHeader,
|
|
||||||
displayTaskProperties,
|
|
||||||
displayImplementationDetails,
|
|
||||||
displayTestStrategy,
|
|
||||||
displaySubtasks,
|
|
||||||
displaySuggestedActions
|
|
||||||
} from '../ui/components/task-detail.component.js';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Options interface for the show command
|
* Options interface for the show command
|
||||||
@@ -266,26 +258,46 @@ export class ShowCommand extends Command {
|
|||||||
|
|
||||||
const task = result.task;
|
const task = result.task;
|
||||||
|
|
||||||
// Display header with tag
|
// Header
|
||||||
displayTaskHeader(task.id, task.title);
|
console.log(
|
||||||
|
boxen(chalk.white.bold(`Task #${task.id} - ${task.title}`), {
|
||||||
|
padding: { top: 0, bottom: 0, left: 1, right: 1 },
|
||||||
|
borderColor: 'blue',
|
||||||
|
borderStyle: 'round',
|
||||||
|
margin: { top: 1 }
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
// Display task properties in table format
|
// Task details
|
||||||
displayTaskProperties(task);
|
console.log(
|
||||||
|
`\n${chalk.blue.bold('Status:')} ${ui.getStatusWithColor(task.status)}`
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
`${chalk.blue.bold('Priority:')} ${ui.getPriorityWithColor(task.priority)}`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (task.description) {
|
||||||
|
console.log(`\n${chalk.blue.bold('Description:')}`);
|
||||||
|
console.log(task.description);
|
||||||
|
}
|
||||||
|
|
||||||
// Display implementation details if available
|
|
||||||
if (task.details) {
|
if (task.details) {
|
||||||
console.log(); // Empty line for spacing
|
console.log(`\n${chalk.blue.bold('Details:')}`);
|
||||||
displayImplementationDetails(task.details);
|
console.log(task.details);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display test strategy if available
|
// Dependencies
|
||||||
if ('testStrategy' in task && task.testStrategy) {
|
if (task.dependencies && task.dependencies.length > 0) {
|
||||||
console.log(); // Empty line for spacing
|
console.log(`\n${chalk.blue.bold('Dependencies:')}`);
|
||||||
displayTestStrategy(task.testStrategy as string);
|
task.dependencies.forEach((dep) => {
|
||||||
|
console.log(` - ${chalk.cyan(dep)}`);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display subtasks if available
|
// Subtasks
|
||||||
if (task.subtasks && task.subtasks.length > 0) {
|
if (task.subtasks && task.subtasks.length > 0) {
|
||||||
|
console.log(`\n${chalk.blue.bold('Subtasks:')}`);
|
||||||
|
|
||||||
// Filter subtasks by status if provided
|
// Filter subtasks by status if provided
|
||||||
const filteredSubtasks = options.status
|
const filteredSubtasks = options.status
|
||||||
? task.subtasks.filter((sub) => sub.status === options.status)
|
? task.subtasks.filter((sub) => sub.status === options.status)
|
||||||
@@ -296,12 +308,23 @@ export class ShowCommand extends Command {
|
|||||||
chalk.gray(` No subtasks with status '${options.status}'`)
|
chalk.gray(` No subtasks with status '${options.status}'`)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
displaySubtasks(filteredSubtasks, task.id);
|
filteredSubtasks.forEach((subtask) => {
|
||||||
|
console.log(
|
||||||
|
` ${chalk.cyan(`${task.id}.${subtask.id}`)} ${ui.getStatusWithColor(subtask.status)} ${subtask.title}`
|
||||||
|
);
|
||||||
|
if (subtask.description) {
|
||||||
|
console.log(` ${chalk.gray(subtask.description)}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display suggested actions
|
if (task.testStrategy) {
|
||||||
displaySuggestedActions(task.id);
|
console.log(`\n${chalk.blue.bold('Test Strategy:')}`);
|
||||||
|
console.log(task.testStrategy);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`\n${chalk.gray('Storage: ' + result.storageType)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,567 +0,0 @@
|
|||||||
/**
|
|
||||||
* @fileoverview Dashboard components for Task Master CLI
|
|
||||||
* Displays project statistics and dependency information
|
|
||||||
*/
|
|
||||||
|
|
||||||
import chalk from 'chalk';
|
|
||||||
import boxen from 'boxen';
|
|
||||||
import type { Task, TaskPriority } from '@tm/core/types';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Statistics for task collection
|
|
||||||
*/
|
|
||||||
export interface TaskStatistics {
|
|
||||||
total: number;
|
|
||||||
done: number;
|
|
||||||
inProgress: number;
|
|
||||||
pending: number;
|
|
||||||
blocked: number;
|
|
||||||
deferred: number;
|
|
||||||
cancelled: number;
|
|
||||||
review?: number;
|
|
||||||
completionPercentage: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Statistics for dependencies
|
|
||||||
*/
|
|
||||||
export interface DependencyStatistics {
|
|
||||||
tasksWithNoDeps: number;
|
|
||||||
tasksReadyToWork: number;
|
|
||||||
tasksBlockedByDeps: number;
|
|
||||||
mostDependedOnTaskId?: number;
|
|
||||||
mostDependedOnCount?: number;
|
|
||||||
avgDependenciesPerTask: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Next task information
|
|
||||||
*/
|
|
||||||
export interface NextTaskInfo {
|
|
||||||
id: string | number;
|
|
||||||
title: string;
|
|
||||||
priority?: TaskPriority;
|
|
||||||
dependencies?: (string | number)[];
|
|
||||||
complexity?: number | string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Status breakdown for progress bars
|
|
||||||
*/
|
|
||||||
export interface StatusBreakdown {
|
|
||||||
'in-progress'?: number;
|
|
||||||
pending?: number;
|
|
||||||
blocked?: number;
|
|
||||||
deferred?: number;
|
|
||||||
cancelled?: number;
|
|
||||||
review?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a progress bar with color-coded status segments
|
|
||||||
*/
|
|
||||||
function createProgressBar(
|
|
||||||
completionPercentage: number,
|
|
||||||
width: number = 30,
|
|
||||||
statusBreakdown?: StatusBreakdown
|
|
||||||
): string {
|
|
||||||
// If no breakdown provided, use simple green bar
|
|
||||||
if (!statusBreakdown) {
|
|
||||||
const filled = Math.round((completionPercentage / 100) * width);
|
|
||||||
const empty = width - filled;
|
|
||||||
return chalk.green('█').repeat(filled) + chalk.gray('░').repeat(empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build the bar with different colored sections
|
|
||||||
// Order matches the status display: Done, Cancelled, Deferred, In Progress, Review, Pending, Blocked
|
|
||||||
let bar = '';
|
|
||||||
let charsUsed = 0;
|
|
||||||
|
|
||||||
// 1. Green filled blocks for completed tasks (done)
|
|
||||||
const completedChars = Math.round((completionPercentage / 100) * width);
|
|
||||||
if (completedChars > 0) {
|
|
||||||
bar += chalk.green('█').repeat(completedChars);
|
|
||||||
charsUsed += completedChars;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Gray filled blocks for cancelled (won't be done)
|
|
||||||
if (statusBreakdown.cancelled && charsUsed < width) {
|
|
||||||
const cancelledChars = Math.round(
|
|
||||||
(statusBreakdown.cancelled / 100) * width
|
|
||||||
);
|
|
||||||
const actualChars = Math.min(cancelledChars, width - charsUsed);
|
|
||||||
if (actualChars > 0) {
|
|
||||||
bar += chalk.gray('█').repeat(actualChars);
|
|
||||||
charsUsed += actualChars;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Gray filled blocks for deferred (won't be done now)
|
|
||||||
if (statusBreakdown.deferred && charsUsed < width) {
|
|
||||||
const deferredChars = Math.round((statusBreakdown.deferred / 100) * width);
|
|
||||||
const actualChars = Math.min(deferredChars, width - charsUsed);
|
|
||||||
if (actualChars > 0) {
|
|
||||||
bar += chalk.gray('█').repeat(actualChars);
|
|
||||||
charsUsed += actualChars;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Blue filled blocks for in-progress (actively working)
|
|
||||||
if (statusBreakdown['in-progress'] && charsUsed < width) {
|
|
||||||
const inProgressChars = Math.round(
|
|
||||||
(statusBreakdown['in-progress'] / 100) * width
|
|
||||||
);
|
|
||||||
const actualChars = Math.min(inProgressChars, width - charsUsed);
|
|
||||||
if (actualChars > 0) {
|
|
||||||
bar += chalk.blue('█').repeat(actualChars);
|
|
||||||
charsUsed += actualChars;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Magenta empty blocks for review (almost done)
|
|
||||||
if (statusBreakdown.review && charsUsed < width) {
|
|
||||||
const reviewChars = Math.round((statusBreakdown.review / 100) * width);
|
|
||||||
const actualChars = Math.min(reviewChars, width - charsUsed);
|
|
||||||
if (actualChars > 0) {
|
|
||||||
bar += chalk.magenta('░').repeat(actualChars);
|
|
||||||
charsUsed += actualChars;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. Yellow empty blocks for pending (ready to start)
|
|
||||||
if (statusBreakdown.pending && charsUsed < width) {
|
|
||||||
const pendingChars = Math.round((statusBreakdown.pending / 100) * width);
|
|
||||||
const actualChars = Math.min(pendingChars, width - charsUsed);
|
|
||||||
if (actualChars > 0) {
|
|
||||||
bar += chalk.yellow('░').repeat(actualChars);
|
|
||||||
charsUsed += actualChars;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7. Red empty blocks for blocked (can't start yet)
|
|
||||||
if (statusBreakdown.blocked && charsUsed < width) {
|
|
||||||
const blockedChars = Math.round((statusBreakdown.blocked / 100) * width);
|
|
||||||
const actualChars = Math.min(blockedChars, width - charsUsed);
|
|
||||||
if (actualChars > 0) {
|
|
||||||
bar += chalk.red('░').repeat(actualChars);
|
|
||||||
charsUsed += actualChars;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fill any remaining space with gray empty yellow blocks
|
|
||||||
if (charsUsed < width) {
|
|
||||||
bar += chalk.yellow('░').repeat(width - charsUsed);
|
|
||||||
}
|
|
||||||
|
|
||||||
return bar;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate task statistics from a list of tasks
|
|
||||||
*/
|
|
||||||
export function calculateTaskStatistics(tasks: Task[]): TaskStatistics {
|
|
||||||
const stats: TaskStatistics = {
|
|
||||||
total: tasks.length,
|
|
||||||
done: 0,
|
|
||||||
inProgress: 0,
|
|
||||||
pending: 0,
|
|
||||||
blocked: 0,
|
|
||||||
deferred: 0,
|
|
||||||
cancelled: 0,
|
|
||||||
review: 0,
|
|
||||||
completionPercentage: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
tasks.forEach((task) => {
|
|
||||||
switch (task.status) {
|
|
||||||
case 'done':
|
|
||||||
stats.done++;
|
|
||||||
break;
|
|
||||||
case 'in-progress':
|
|
||||||
stats.inProgress++;
|
|
||||||
break;
|
|
||||||
case 'pending':
|
|
||||||
stats.pending++;
|
|
||||||
break;
|
|
||||||
case 'blocked':
|
|
||||||
stats.blocked++;
|
|
||||||
break;
|
|
||||||
case 'deferred':
|
|
||||||
stats.deferred++;
|
|
||||||
break;
|
|
||||||
case 'cancelled':
|
|
||||||
stats.cancelled++;
|
|
||||||
break;
|
|
||||||
case 'review':
|
|
||||||
stats.review = (stats.review || 0) + 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
stats.completionPercentage =
|
|
||||||
stats.total > 0 ? Math.round((stats.done / stats.total) * 100) : 0;
|
|
||||||
|
|
||||||
return stats;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate subtask statistics from tasks
|
|
||||||
*/
|
|
||||||
export function calculateSubtaskStatistics(tasks: Task[]): TaskStatistics {
|
|
||||||
const stats: TaskStatistics = {
|
|
||||||
total: 0,
|
|
||||||
done: 0,
|
|
||||||
inProgress: 0,
|
|
||||||
pending: 0,
|
|
||||||
blocked: 0,
|
|
||||||
deferred: 0,
|
|
||||||
cancelled: 0,
|
|
||||||
review: 0,
|
|
||||||
completionPercentage: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
tasks.forEach((task) => {
|
|
||||||
if (task.subtasks && task.subtasks.length > 0) {
|
|
||||||
task.subtasks.forEach((subtask) => {
|
|
||||||
stats.total++;
|
|
||||||
switch (subtask.status) {
|
|
||||||
case 'done':
|
|
||||||
stats.done++;
|
|
||||||
break;
|
|
||||||
case 'in-progress':
|
|
||||||
stats.inProgress++;
|
|
||||||
break;
|
|
||||||
case 'pending':
|
|
||||||
stats.pending++;
|
|
||||||
break;
|
|
||||||
case 'blocked':
|
|
||||||
stats.blocked++;
|
|
||||||
break;
|
|
||||||
case 'deferred':
|
|
||||||
stats.deferred++;
|
|
||||||
break;
|
|
||||||
case 'cancelled':
|
|
||||||
stats.cancelled++;
|
|
||||||
break;
|
|
||||||
case 'review':
|
|
||||||
stats.review = (stats.review || 0) + 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
stats.completionPercentage =
|
|
||||||
stats.total > 0 ? Math.round((stats.done / stats.total) * 100) : 0;
|
|
||||||
|
|
||||||
return stats;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate dependency statistics
|
|
||||||
*/
|
|
||||||
export function calculateDependencyStatistics(
|
|
||||||
tasks: Task[]
|
|
||||||
): DependencyStatistics {
|
|
||||||
const completedTaskIds = new Set(
|
|
||||||
tasks.filter((t) => t.status === 'done').map((t) => t.id)
|
|
||||||
);
|
|
||||||
|
|
||||||
const tasksWithNoDeps = tasks.filter(
|
|
||||||
(t) =>
|
|
||||||
t.status !== 'done' && (!t.dependencies || t.dependencies.length === 0)
|
|
||||||
).length;
|
|
||||||
|
|
||||||
const tasksWithAllDepsSatisfied = tasks.filter(
|
|
||||||
(t) =>
|
|
||||||
t.status !== 'done' &&
|
|
||||||
t.dependencies &&
|
|
||||||
t.dependencies.length > 0 &&
|
|
||||||
t.dependencies.every((depId) => completedTaskIds.has(depId))
|
|
||||||
).length;
|
|
||||||
|
|
||||||
const tasksBlockedByDeps = tasks.filter(
|
|
||||||
(t) =>
|
|
||||||
t.status !== 'done' &&
|
|
||||||
t.dependencies &&
|
|
||||||
t.dependencies.length > 0 &&
|
|
||||||
!t.dependencies.every((depId) => completedTaskIds.has(depId))
|
|
||||||
).length;
|
|
||||||
|
|
||||||
// Calculate most depended-on task
|
|
||||||
const dependencyCount: Record<string, number> = {};
|
|
||||||
tasks.forEach((task) => {
|
|
||||||
if (task.dependencies && task.dependencies.length > 0) {
|
|
||||||
task.dependencies.forEach((depId) => {
|
|
||||||
const key = String(depId);
|
|
||||||
dependencyCount[key] = (dependencyCount[key] || 0) + 1;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let mostDependedOnTaskId: number | undefined;
|
|
||||||
let mostDependedOnCount = 0;
|
|
||||||
|
|
||||||
for (const [taskId, count] of Object.entries(dependencyCount)) {
|
|
||||||
if (count > mostDependedOnCount) {
|
|
||||||
mostDependedOnCount = count;
|
|
||||||
mostDependedOnTaskId = parseInt(taskId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate average dependencies
|
|
||||||
const totalDependencies = tasks.reduce(
|
|
||||||
(sum, task) => sum + (task.dependencies ? task.dependencies.length : 0),
|
|
||||||
0
|
|
||||||
);
|
|
||||||
const avgDependenciesPerTask =
|
|
||||||
tasks.length > 0 ? totalDependencies / tasks.length : 0;
|
|
||||||
|
|
||||||
return {
|
|
||||||
tasksWithNoDeps,
|
|
||||||
tasksReadyToWork: tasksWithNoDeps + tasksWithAllDepsSatisfied,
|
|
||||||
tasksBlockedByDeps,
|
|
||||||
mostDependedOnTaskId,
|
|
||||||
mostDependedOnCount,
|
|
||||||
avgDependenciesPerTask
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get priority counts
|
|
||||||
*/
|
|
||||||
export function getPriorityBreakdown(
|
|
||||||
tasks: Task[]
|
|
||||||
): Record<TaskPriority, number> {
|
|
||||||
const breakdown: Record<TaskPriority, number> = {
|
|
||||||
critical: 0,
|
|
||||||
high: 0,
|
|
||||||
medium: 0,
|
|
||||||
low: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
tasks.forEach((task) => {
|
|
||||||
const priority = task.priority || 'medium';
|
|
||||||
breakdown[priority]++;
|
|
||||||
});
|
|
||||||
|
|
||||||
return breakdown;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate status breakdown as percentages
|
|
||||||
*/
|
|
||||||
function calculateStatusBreakdown(stats: TaskStatistics): StatusBreakdown {
|
|
||||||
if (stats.total === 0) return {};
|
|
||||||
|
|
||||||
return {
|
|
||||||
'in-progress': (stats.inProgress / stats.total) * 100,
|
|
||||||
pending: (stats.pending / stats.total) * 100,
|
|
||||||
blocked: (stats.blocked / stats.total) * 100,
|
|
||||||
deferred: (stats.deferred / stats.total) * 100,
|
|
||||||
cancelled: (stats.cancelled / stats.total) * 100,
|
|
||||||
review: ((stats.review || 0) / stats.total) * 100
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Format status counts in the correct order with colors
|
|
||||||
* @param stats - The statistics object containing counts
|
|
||||||
* @param isSubtask - Whether this is for subtasks (affects "Done" vs "Completed" label)
|
|
||||||
*/
|
|
||||||
function formatStatusLine(
|
|
||||||
stats: TaskStatistics,
|
|
||||||
isSubtask: boolean = false
|
|
||||||
): string {
|
|
||||||
const parts: string[] = [];
|
|
||||||
|
|
||||||
// Order: Done, Cancelled, Deferred, In Progress, Review, Pending, Blocked
|
|
||||||
if (isSubtask) {
|
|
||||||
parts.push(`Completed: ${chalk.green(`${stats.done}/${stats.total}`)}`);
|
|
||||||
} else {
|
|
||||||
parts.push(`Done: ${chalk.green(stats.done)}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
parts.push(`Cancelled: ${chalk.gray(stats.cancelled)}`);
|
|
||||||
parts.push(`Deferred: ${chalk.gray(stats.deferred)}`);
|
|
||||||
|
|
||||||
// Add line break for second row
|
|
||||||
const firstLine = parts.join(' ');
|
|
||||||
parts.length = 0;
|
|
||||||
|
|
||||||
parts.push(`In Progress: ${chalk.blue(stats.inProgress)}`);
|
|
||||||
parts.push(`Review: ${chalk.magenta(stats.review || 0)}`);
|
|
||||||
parts.push(`Pending: ${chalk.yellow(stats.pending)}`);
|
|
||||||
parts.push(`Blocked: ${chalk.red(stats.blocked)}`);
|
|
||||||
|
|
||||||
const secondLine = parts.join(' ');
|
|
||||||
|
|
||||||
return firstLine + '\n' + secondLine;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display the project dashboard box
|
|
||||||
*/
|
|
||||||
export function displayProjectDashboard(
|
|
||||||
taskStats: TaskStatistics,
|
|
||||||
subtaskStats: TaskStatistics,
|
|
||||||
priorityBreakdown: Record<TaskPriority, number>
|
|
||||||
): string {
|
|
||||||
// Calculate status breakdowns using the helper function
|
|
||||||
const taskStatusBreakdown = calculateStatusBreakdown(taskStats);
|
|
||||||
const subtaskStatusBreakdown = calculateStatusBreakdown(subtaskStats);
|
|
||||||
|
|
||||||
// Create progress bars with the breakdowns
|
|
||||||
const taskProgressBar = createProgressBar(
|
|
||||||
taskStats.completionPercentage,
|
|
||||||
30,
|
|
||||||
taskStatusBreakdown
|
|
||||||
);
|
|
||||||
const subtaskProgressBar = createProgressBar(
|
|
||||||
subtaskStats.completionPercentage,
|
|
||||||
30,
|
|
||||||
subtaskStatusBreakdown
|
|
||||||
);
|
|
||||||
|
|
||||||
const taskPercentage = `${taskStats.completionPercentage}% ${taskStats.done}/${taskStats.total}`;
|
|
||||||
const subtaskPercentage = `${subtaskStats.completionPercentage}% ${subtaskStats.done}/${subtaskStats.total}`;
|
|
||||||
|
|
||||||
const content =
|
|
||||||
chalk.white.bold('Project Dashboard') +
|
|
||||||
'\n' +
|
|
||||||
`Tasks Progress: ${taskProgressBar} ${chalk.yellow(taskPercentage)}\n` +
|
|
||||||
formatStatusLine(taskStats, false) +
|
|
||||||
'\n\n' +
|
|
||||||
`Subtasks Progress: ${subtaskProgressBar} ${chalk.cyan(subtaskPercentage)}\n` +
|
|
||||||
formatStatusLine(subtaskStats, true) +
|
|
||||||
'\n\n' +
|
|
||||||
chalk.cyan.bold('Priority Breakdown:') +
|
|
||||||
'\n' +
|
|
||||||
`${chalk.red('•')} ${chalk.white('High priority:')} ${priorityBreakdown.high}\n` +
|
|
||||||
`${chalk.yellow('•')} ${chalk.white('Medium priority:')} ${priorityBreakdown.medium}\n` +
|
|
||||||
`${chalk.green('•')} ${chalk.white('Low priority:')} ${priorityBreakdown.low}`;
|
|
||||||
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display the dependency dashboard box
|
|
||||||
*/
|
|
||||||
export function displayDependencyDashboard(
|
|
||||||
depStats: DependencyStatistics,
|
|
||||||
nextTask?: NextTaskInfo
|
|
||||||
): string {
|
|
||||||
const content =
|
|
||||||
chalk.white.bold('Dependency Status & Next Task') +
|
|
||||||
'\n' +
|
|
||||||
chalk.cyan.bold('Dependency Metrics:') +
|
|
||||||
'\n' +
|
|
||||||
`${chalk.green('•')} ${chalk.white('Tasks with no dependencies:')} ${depStats.tasksWithNoDeps}\n` +
|
|
||||||
`${chalk.green('•')} ${chalk.white('Tasks ready to work on:')} ${depStats.tasksReadyToWork}\n` +
|
|
||||||
`${chalk.yellow('•')} ${chalk.white('Tasks blocked by dependencies:')} ${depStats.tasksBlockedByDeps}\n` +
|
|
||||||
`${chalk.magenta('•')} ${chalk.white('Most depended-on task:')} ${
|
|
||||||
depStats.mostDependedOnTaskId
|
|
||||||
? chalk.cyan(
|
|
||||||
`#${depStats.mostDependedOnTaskId} (${depStats.mostDependedOnCount} dependents)`
|
|
||||||
)
|
|
||||||
: chalk.gray('None')
|
|
||||||
}\n` +
|
|
||||||
`${chalk.blue('•')} ${chalk.white('Avg dependencies per task:')} ${depStats.avgDependenciesPerTask.toFixed(1)}\n\n` +
|
|
||||||
chalk.cyan.bold('Next Task to Work On:') +
|
|
||||||
'\n' +
|
|
||||||
`ID: ${nextTask ? chalk.cyan(String(nextTask.id)) : chalk.gray('N/A')} - ${
|
|
||||||
nextTask
|
|
||||||
? chalk.white.bold(nextTask.title)
|
|
||||||
: chalk.yellow('No task available')
|
|
||||||
}\n` +
|
|
||||||
`Priority: ${nextTask?.priority || chalk.gray('N/A')} Dependencies: ${
|
|
||||||
nextTask?.dependencies?.length
|
|
||||||
? chalk.cyan(nextTask.dependencies.join(', '))
|
|
||||||
: chalk.gray('None')
|
|
||||||
}\n` +
|
|
||||||
`Complexity: ${nextTask?.complexity || chalk.gray('N/A')}`;
|
|
||||||
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display dashboard boxes side by side or stacked
|
|
||||||
*/
|
|
||||||
export function displayDashboards(
|
|
||||||
taskStats: TaskStatistics,
|
|
||||||
subtaskStats: TaskStatistics,
|
|
||||||
priorityBreakdown: Record<TaskPriority, number>,
|
|
||||||
depStats: DependencyStatistics,
|
|
||||||
nextTask?: NextTaskInfo
|
|
||||||
): void {
|
|
||||||
const projectDashboardContent = displayProjectDashboard(
|
|
||||||
taskStats,
|
|
||||||
subtaskStats,
|
|
||||||
priorityBreakdown
|
|
||||||
);
|
|
||||||
const dependencyDashboardContent = displayDependencyDashboard(
|
|
||||||
depStats,
|
|
||||||
nextTask
|
|
||||||
);
|
|
||||||
|
|
||||||
// Get terminal width
|
|
||||||
const terminalWidth = process.stdout.columns || 80;
|
|
||||||
const minDashboardWidth = 50;
|
|
||||||
const minDependencyWidth = 50;
|
|
||||||
const totalMinWidth = minDashboardWidth + minDependencyWidth + 4;
|
|
||||||
|
|
||||||
// If terminal is wide enough, show side by side
|
|
||||||
if (terminalWidth >= totalMinWidth) {
|
|
||||||
const halfWidth = Math.floor(terminalWidth / 2);
|
|
||||||
const boxContentWidth = halfWidth - 4;
|
|
||||||
|
|
||||||
const dashboardBox = boxen(projectDashboardContent, {
|
|
||||||
padding: 1,
|
|
||||||
borderColor: 'blue',
|
|
||||||
borderStyle: 'round',
|
|
||||||
width: boxContentWidth,
|
|
||||||
dimBorder: false
|
|
||||||
});
|
|
||||||
|
|
||||||
const dependencyBox = boxen(dependencyDashboardContent, {
|
|
||||||
padding: 1,
|
|
||||||
borderColor: 'magenta',
|
|
||||||
borderStyle: 'round',
|
|
||||||
width: boxContentWidth,
|
|
||||||
dimBorder: false
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create side-by-side layout
|
|
||||||
const dashboardLines = dashboardBox.split('\n');
|
|
||||||
const dependencyLines = dependencyBox.split('\n');
|
|
||||||
const maxHeight = Math.max(dashboardLines.length, dependencyLines.length);
|
|
||||||
|
|
||||||
const combinedLines = [];
|
|
||||||
for (let i = 0; i < maxHeight; i++) {
|
|
||||||
const dashLine = i < dashboardLines.length ? dashboardLines[i] : '';
|
|
||||||
const depLine = i < dependencyLines.length ? dependencyLines[i] : '';
|
|
||||||
const paddedDashLine = dashLine.padEnd(halfWidth, ' ');
|
|
||||||
combinedLines.push(paddedDashLine + depLine);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(combinedLines.join('\n'));
|
|
||||||
} else {
|
|
||||||
// Show stacked vertically
|
|
||||||
const dashboardBox = boxen(projectDashboardContent, {
|
|
||||||
padding: 1,
|
|
||||||
borderColor: 'blue',
|
|
||||||
borderStyle: 'round',
|
|
||||||
margin: { top: 0, bottom: 1 }
|
|
||||||
});
|
|
||||||
|
|
||||||
const dependencyBox = boxen(dependencyDashboardContent, {
|
|
||||||
padding: 1,
|
|
||||||
borderColor: 'magenta',
|
|
||||||
borderStyle: 'round',
|
|
||||||
margin: { top: 0, bottom: 1 }
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(dashboardBox);
|
|
||||||
console.log(dependencyBox);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
/**
|
|
||||||
* @fileoverview Task Master header component
|
|
||||||
* Displays the banner, version, project info, and file path
|
|
||||||
*/
|
|
||||||
|
|
||||||
import chalk from 'chalk';
|
|
||||||
import figlet from 'figlet';
|
|
||||||
import gradient from 'gradient-string';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Header configuration options
|
|
||||||
*/
|
|
||||||
export interface HeaderOptions {
|
|
||||||
title?: string;
|
|
||||||
tag?: string;
|
|
||||||
filePath?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display the Task Master header with project info
|
|
||||||
*/
|
|
||||||
export function displayHeader(options: HeaderOptions = {}): void {
|
|
||||||
const { filePath, tag } = options;
|
|
||||||
|
|
||||||
// Display tag and file path info
|
|
||||||
if (tag) {
|
|
||||||
let tagInfo = '';
|
|
||||||
|
|
||||||
if (tag && tag !== 'master') {
|
|
||||||
tagInfo = `🏷 tag: ${chalk.cyan(tag)}`;
|
|
||||||
} else {
|
|
||||||
tagInfo = `🏷 tag: ${chalk.cyan('master')}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(tagInfo);
|
|
||||||
|
|
||||||
if (filePath) {
|
|
||||||
// Convert to absolute path if it's relative
|
|
||||||
const absolutePath = filePath.startsWith('/')
|
|
||||||
? filePath
|
|
||||||
: `${process.cwd()}/${filePath}`;
|
|
||||||
console.log(`Listing tasks from: ${chalk.dim(absolutePath)}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(); // Empty line for spacing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
/**
|
|
||||||
* @fileoverview UI components exports
|
|
||||||
*/
|
|
||||||
|
|
||||||
export * from './header.component.js';
|
|
||||||
export * from './dashboard.component.js';
|
|
||||||
export * from './next-task.component.js';
|
|
||||||
export * from './suggested-steps.component.js';
|
|
||||||
export * from './task-detail.component.js';
|
|
||||||
@@ -1,134 +0,0 @@
|
|||||||
/**
|
|
||||||
* @fileoverview Next task recommendation component
|
|
||||||
* Displays detailed information about the recommended next task
|
|
||||||
*/
|
|
||||||
|
|
||||||
import chalk from 'chalk';
|
|
||||||
import boxen from 'boxen';
|
|
||||||
import type { Task } from '@tm/core/types';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Next task display options
|
|
||||||
*/
|
|
||||||
export interface NextTaskDisplayOptions {
|
|
||||||
id: string | number;
|
|
||||||
title: string;
|
|
||||||
priority?: string;
|
|
||||||
status?: string;
|
|
||||||
dependencies?: (string | number)[];
|
|
||||||
description?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display the recommended next task section
|
|
||||||
*/
|
|
||||||
export function displayRecommendedNextTask(
|
|
||||||
task: NextTaskDisplayOptions | undefined
|
|
||||||
): void {
|
|
||||||
if (!task) {
|
|
||||||
// If no task available, show a message
|
|
||||||
console.log(
|
|
||||||
boxen(
|
|
||||||
chalk.yellow(
|
|
||||||
'No tasks available to work on. All tasks are either completed, blocked by dependencies, or in progress.'
|
|
||||||
),
|
|
||||||
{
|
|
||||||
padding: 1,
|
|
||||||
borderStyle: 'round',
|
|
||||||
borderColor: 'yellow',
|
|
||||||
title: '⚠ NO TASKS AVAILABLE ⚠',
|
|
||||||
titleAlignment: 'center'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build the content for the next task box
|
|
||||||
const content = [];
|
|
||||||
|
|
||||||
// Task header with ID and title
|
|
||||||
content.push(
|
|
||||||
`🔥 ${chalk.hex('#FF8800').bold('Next Task to Work On:')} ${chalk.yellow(`#${task.id}`)}${chalk.hex('#FF8800').bold(` - ${task.title}`)}`
|
|
||||||
);
|
|
||||||
content.push('');
|
|
||||||
|
|
||||||
// Priority and Status line
|
|
||||||
const statusLine = [];
|
|
||||||
if (task.priority) {
|
|
||||||
const priorityColor =
|
|
||||||
task.priority === 'high'
|
|
||||||
? chalk.red
|
|
||||||
: task.priority === 'medium'
|
|
||||||
? chalk.yellow
|
|
||||||
: chalk.gray;
|
|
||||||
statusLine.push(`Priority: ${priorityColor.bold(task.priority)}`);
|
|
||||||
}
|
|
||||||
if (task.status) {
|
|
||||||
const statusDisplay =
|
|
||||||
task.status === 'pending'
|
|
||||||
? chalk.yellow('○ pending')
|
|
||||||
: task.status === 'in-progress'
|
|
||||||
? chalk.blue('▶ in-progress')
|
|
||||||
: chalk.gray(task.status);
|
|
||||||
statusLine.push(`Status: ${statusDisplay}`);
|
|
||||||
}
|
|
||||||
content.push(statusLine.join(' '));
|
|
||||||
|
|
||||||
// Dependencies
|
|
||||||
const depsDisplay =
|
|
||||||
!task.dependencies || task.dependencies.length === 0
|
|
||||||
? chalk.gray('None')
|
|
||||||
: chalk.cyan(task.dependencies.join(', '));
|
|
||||||
content.push(`Dependencies: ${depsDisplay}`);
|
|
||||||
|
|
||||||
// Description if available
|
|
||||||
if (task.description) {
|
|
||||||
content.push('');
|
|
||||||
content.push(`Description: ${chalk.white(task.description)}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Action commands
|
|
||||||
content.push('');
|
|
||||||
content.push(
|
|
||||||
`${chalk.cyan('Start working:')} ${chalk.yellow(`task-master set-status --id=${task.id} --status=in-progress`)}`
|
|
||||||
);
|
|
||||||
content.push(
|
|
||||||
`${chalk.cyan('View details:')} ${chalk.yellow(`task-master show ${task.id}`)}`
|
|
||||||
);
|
|
||||||
|
|
||||||
// Display in a styled box with orange border
|
|
||||||
console.log(
|
|
||||||
boxen(content.join('\n'), {
|
|
||||||
padding: 1,
|
|
||||||
margin: { top: 1, bottom: 1 },
|
|
||||||
borderStyle: 'round',
|
|
||||||
borderColor: '#FFA500', // Orange color
|
|
||||||
title: chalk.hex('#FFA500')('⚡ RECOMMENDED NEXT TASK ⚡'),
|
|
||||||
titleAlignment: 'center',
|
|
||||||
width: process.stdout.columns * 0.97,
|
|
||||||
fullscreen: false
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get task description from the full task object
|
|
||||||
*/
|
|
||||||
export function getTaskDescription(task: Task): string | undefined {
|
|
||||||
// Try to get description from the task
|
|
||||||
// This could be from task.description or the first line of task.details
|
|
||||||
if ('description' in task && task.description) {
|
|
||||||
return task.description as string;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ('details' in task && task.details) {
|
|
||||||
// Take first sentence or line from details
|
|
||||||
const details = task.details as string;
|
|
||||||
const firstLine = details.split('\n')[0];
|
|
||||||
const firstSentence = firstLine.split('.')[0];
|
|
||||||
return firstSentence;
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
/**
|
|
||||||
* @fileoverview Suggested next steps component
|
|
||||||
* Displays helpful command suggestions at the end of the list
|
|
||||||
*/
|
|
||||||
|
|
||||||
import chalk from 'chalk';
|
|
||||||
import boxen from 'boxen';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display suggested next steps section
|
|
||||||
*/
|
|
||||||
export function displaySuggestedNextSteps(): void {
|
|
||||||
const steps = [
|
|
||||||
`${chalk.cyan('1.')} Run ${chalk.yellow('task-master next')} to see what to work on next`,
|
|
||||||
`${chalk.cyan('2.')} Run ${chalk.yellow('task-master expand --id=<id>')} to break down a task into subtasks`,
|
|
||||||
`${chalk.cyan('3.')} Run ${chalk.yellow('task-master set-status --id=<id> --status=done')} to mark a task as complete`
|
|
||||||
];
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
boxen(
|
|
||||||
chalk.white.bold('Suggested Next Steps:') + '\n\n' + steps.join('\n'),
|
|
||||||
{
|
|
||||||
padding: 1,
|
|
||||||
margin: { top: 0, bottom: 1 },
|
|
||||||
borderStyle: 'round',
|
|
||||||
borderColor: 'gray',
|
|
||||||
width: process.stdout.columns * 0.97
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,264 +0,0 @@
|
|||||||
/**
|
|
||||||
* @fileoverview Task detail component for show command
|
|
||||||
* Displays detailed task information in a structured format
|
|
||||||
*/
|
|
||||||
|
|
||||||
import chalk from 'chalk';
|
|
||||||
import boxen from 'boxen';
|
|
||||||
import Table from 'cli-table3';
|
|
||||||
import { marked, MarkedExtension } from 'marked';
|
|
||||||
import { markedTerminal } from 'marked-terminal';
|
|
||||||
import type { Task } from '@tm/core/types';
|
|
||||||
import { getStatusWithColor, getPriorityWithColor } from '../../utils/ui.js';
|
|
||||||
|
|
||||||
// Configure marked to use terminal renderer with subtle colors
|
|
||||||
marked.use(
|
|
||||||
markedTerminal({
|
|
||||||
// More subtle colors that match the overall design
|
|
||||||
code: (code: string) => {
|
|
||||||
// Custom code block handler to preserve formatting
|
|
||||||
return code
|
|
||||||
.split('\n')
|
|
||||||
.map((line) => ' ' + chalk.cyan(line))
|
|
||||||
.join('\n');
|
|
||||||
},
|
|
||||||
blockquote: chalk.gray.italic,
|
|
||||||
html: chalk.gray,
|
|
||||||
heading: chalk.white.bold, // White bold for headings
|
|
||||||
hr: chalk.gray,
|
|
||||||
listitem: chalk.white, // White for list items
|
|
||||||
paragraph: chalk.white, // White for paragraphs (default text color)
|
|
||||||
strong: chalk.white.bold, // White bold for strong text
|
|
||||||
em: chalk.white.italic, // White italic for emphasis
|
|
||||||
codespan: chalk.cyan, // Cyan for inline code (no background)
|
|
||||||
del: chalk.dim.strikethrough,
|
|
||||||
link: chalk.blue,
|
|
||||||
href: chalk.blue.underline,
|
|
||||||
// Add more explicit code block handling
|
|
||||||
showSectionPrefix: false,
|
|
||||||
unescape: true,
|
|
||||||
emoji: false,
|
|
||||||
// Try to preserve whitespace in code blocks
|
|
||||||
tab: 4,
|
|
||||||
width: 120
|
|
||||||
}) as MarkedExtension
|
|
||||||
);
|
|
||||||
|
|
||||||
// Also set marked options to preserve whitespace
|
|
||||||
marked.setOptions({
|
|
||||||
breaks: true,
|
|
||||||
gfm: true
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display the task header with tag
|
|
||||||
*/
|
|
||||||
export function displayTaskHeader(
|
|
||||||
taskId: string | number,
|
|
||||||
title: string
|
|
||||||
): void {
|
|
||||||
// Display task header box
|
|
||||||
console.log(
|
|
||||||
boxen(chalk.white.bold(`Task: #${taskId} - ${title}`), {
|
|
||||||
padding: { top: 0, bottom: 0, left: 1, right: 1 },
|
|
||||||
borderColor: 'blue',
|
|
||||||
borderStyle: 'round'
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display task properties in a table format
|
|
||||||
*/
|
|
||||||
export function displayTaskProperties(task: Task): void {
|
|
||||||
const terminalWidth = process.stdout.columns * 0.95 || 100;
|
|
||||||
// Create table for task properties - simple 2-column layout
|
|
||||||
const table = new Table({
|
|
||||||
head: [],
|
|
||||||
style: {
|
|
||||||
head: [],
|
|
||||||
border: ['grey']
|
|
||||||
},
|
|
||||||
colWidths: [
|
|
||||||
Math.floor(terminalWidth * 0.2),
|
|
||||||
Math.floor(terminalWidth * 0.8)
|
|
||||||
],
|
|
||||||
wordWrap: true
|
|
||||||
});
|
|
||||||
|
|
||||||
const deps =
|
|
||||||
task.dependencies && task.dependencies.length > 0
|
|
||||||
? task.dependencies.map((d) => String(d)).join(', ')
|
|
||||||
: 'None';
|
|
||||||
|
|
||||||
// Build the left column (labels) and right column (values)
|
|
||||||
const labels = [
|
|
||||||
chalk.cyan('ID:'),
|
|
||||||
chalk.cyan('Title:'),
|
|
||||||
chalk.cyan('Status:'),
|
|
||||||
chalk.cyan('Priority:'),
|
|
||||||
chalk.cyan('Dependencies:'),
|
|
||||||
chalk.cyan('Complexity:'),
|
|
||||||
chalk.cyan('Description:')
|
|
||||||
].join('\n');
|
|
||||||
|
|
||||||
const values = [
|
|
||||||
String(task.id),
|
|
||||||
task.title,
|
|
||||||
getStatusWithColor(task.status),
|
|
||||||
getPriorityWithColor(task.priority),
|
|
||||||
deps,
|
|
||||||
'N/A',
|
|
||||||
task.description || ''
|
|
||||||
].join('\n');
|
|
||||||
|
|
||||||
table.push([labels, values]);
|
|
||||||
|
|
||||||
console.log(table.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display implementation details in a box
|
|
||||||
*/
|
|
||||||
export function displayImplementationDetails(details: string): void {
|
|
||||||
// Handle all escaped characters properly
|
|
||||||
const cleanDetails = details
|
|
||||||
.replace(/\\n/g, '\n') // Convert \n to actual newlines
|
|
||||||
.replace(/\\t/g, '\t') // Convert \t to actual tabs
|
|
||||||
.replace(/\\"/g, '"') // Convert \" to actual quotes
|
|
||||||
.replace(/\\\\/g, '\\'); // Convert \\ to single backslash
|
|
||||||
|
|
||||||
const terminalWidth = process.stdout.columns * 0.95 || 100;
|
|
||||||
|
|
||||||
// Parse markdown to terminal-friendly format
|
|
||||||
const markdownResult = marked(cleanDetails);
|
|
||||||
const formattedDetails =
|
|
||||||
typeof markdownResult === 'string' ? markdownResult.trim() : cleanDetails; // Fallback to original if Promise
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
boxen(
|
|
||||||
chalk.white.bold('Implementation Details:') + '\n\n' + formattedDetails,
|
|
||||||
{
|
|
||||||
padding: 1,
|
|
||||||
borderStyle: 'round',
|
|
||||||
borderColor: 'cyan', // Changed to cyan to match the original
|
|
||||||
width: terminalWidth // Fixed width to match the original
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display test strategy in a box
|
|
||||||
*/
|
|
||||||
export function displayTestStrategy(testStrategy: string): void {
|
|
||||||
// Handle all escaped characters properly (same as implementation details)
|
|
||||||
const cleanStrategy = testStrategy
|
|
||||||
.replace(/\\n/g, '\n') // Convert \n to actual newlines
|
|
||||||
.replace(/\\t/g, '\t') // Convert \t to actual tabs
|
|
||||||
.replace(/\\"/g, '"') // Convert \" to actual quotes
|
|
||||||
.replace(/\\\\/g, '\\'); // Convert \\ to single backslash
|
|
||||||
|
|
||||||
const terminalWidth = process.stdout.columns * 0.95 || 100;
|
|
||||||
|
|
||||||
// Parse markdown to terminal-friendly format (same as implementation details)
|
|
||||||
const markdownResult = marked(cleanStrategy);
|
|
||||||
const formattedStrategy =
|
|
||||||
typeof markdownResult === 'string' ? markdownResult.trim() : cleanStrategy; // Fallback to original if Promise
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
boxen(chalk.white.bold('Test Strategy:') + '\n\n' + formattedStrategy, {
|
|
||||||
padding: 1,
|
|
||||||
borderStyle: 'round',
|
|
||||||
borderColor: 'cyan', // Changed to cyan to match implementation details
|
|
||||||
width: terminalWidth
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display subtasks in a table format
|
|
||||||
*/
|
|
||||||
export function displaySubtasks(
|
|
||||||
subtasks: Array<{
|
|
||||||
id: string | number;
|
|
||||||
title: string;
|
|
||||||
status: any;
|
|
||||||
description?: string;
|
|
||||||
dependencies?: string[];
|
|
||||||
}>,
|
|
||||||
parentId: string | number
|
|
||||||
): void {
|
|
||||||
const terminalWidth = process.stdout.columns * 0.95 || 100;
|
|
||||||
// Display subtasks header
|
|
||||||
console.log(
|
|
||||||
boxen(chalk.magenta.bold('Subtasks'), {
|
|
||||||
padding: { top: 0, bottom: 0, left: 1, right: 1 },
|
|
||||||
borderColor: 'magenta',
|
|
||||||
borderStyle: 'round',
|
|
||||||
margin: { top: 1, bottom: 0 }
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create subtasks table
|
|
||||||
const table = new Table({
|
|
||||||
head: [
|
|
||||||
chalk.magenta.bold('ID'),
|
|
||||||
chalk.magenta.bold('Status'),
|
|
||||||
chalk.magenta.bold('Title'),
|
|
||||||
chalk.magenta.bold('Deps')
|
|
||||||
],
|
|
||||||
style: {
|
|
||||||
head: [],
|
|
||||||
border: ['grey']
|
|
||||||
},
|
|
||||||
colWidths: [
|
|
||||||
Math.floor(terminalWidth * 0.1),
|
|
||||||
Math.floor(terminalWidth * 0.15),
|
|
||||||
Math.floor(terminalWidth * 0.6),
|
|
||||||
Math.floor(terminalWidth * 0.15)
|
|
||||||
],
|
|
||||||
wordWrap: true
|
|
||||||
});
|
|
||||||
|
|
||||||
subtasks.forEach((subtask) => {
|
|
||||||
const subtaskId = `${parentId}.${subtask.id}`;
|
|
||||||
|
|
||||||
// Format dependencies
|
|
||||||
const deps =
|
|
||||||
subtask.dependencies && subtask.dependencies.length > 0
|
|
||||||
? subtask.dependencies.join(', ')
|
|
||||||
: 'None';
|
|
||||||
|
|
||||||
table.push([
|
|
||||||
subtaskId,
|
|
||||||
getStatusWithColor(subtask.status),
|
|
||||||
subtask.title,
|
|
||||||
deps
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(table.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display suggested actions
|
|
||||||
*/
|
|
||||||
export function displaySuggestedActions(taskId: string | number): void {
|
|
||||||
console.log(
|
|
||||||
boxen(
|
|
||||||
chalk.white.bold('Suggested Actions:') +
|
|
||||||
'\n\n' +
|
|
||||||
`${chalk.cyan('1.')} Run ${chalk.yellow(`task-master set-status --id=${taskId} --status=in-progress`)} to start working\n` +
|
|
||||||
`${chalk.cyan('2.')} Run ${chalk.yellow(`task-master expand --id=${taskId}`)} to break down into subtasks\n` +
|
|
||||||
`${chalk.cyan('3.')} Run ${chalk.yellow(`task-master update-task --id=${taskId} --prompt="..."`)} to update details`,
|
|
||||||
{
|
|
||||||
padding: 1,
|
|
||||||
margin: { top: 1 },
|
|
||||||
borderStyle: 'round',
|
|
||||||
borderColor: 'green',
|
|
||||||
width: process.stdout.columns * 0.95 || 100
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
/**
|
|
||||||
* @fileoverview Main UI exports
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Export all components
|
|
||||||
export * from './components/index.js';
|
|
||||||
|
|
||||||
// Re-export existing UI utilities
|
|
||||||
export * from '../utils/ui.js';
|
|
||||||
@@ -18,39 +18,19 @@ export function getStatusWithColor(
|
|||||||
const statusConfig = {
|
const statusConfig = {
|
||||||
done: {
|
done: {
|
||||||
color: chalk.green,
|
color: chalk.green,
|
||||||
icon: '✓',
|
icon: String.fromCharCode(8730),
|
||||||
tableIcon: '✓'
|
tableIcon: String.fromCharCode(8730)
|
||||||
},
|
}, // √
|
||||||
pending: {
|
pending: { color: chalk.yellow, icon: 'o', tableIcon: 'o' },
|
||||||
color: chalk.yellow,
|
|
||||||
icon: '○',
|
|
||||||
tableIcon: '○'
|
|
||||||
},
|
|
||||||
'in-progress': {
|
'in-progress': {
|
||||||
color: chalk.hex('#FFA500'),
|
color: chalk.hex('#FFA500'),
|
||||||
icon: '▶',
|
icon: String.fromCharCode(9654),
|
||||||
tableIcon: '▶'
|
tableIcon: '>'
|
||||||
},
|
}, // ▶
|
||||||
deferred: {
|
deferred: { color: chalk.gray, icon: 'x', tableIcon: 'x' },
|
||||||
color: chalk.gray,
|
blocked: { color: chalk.red, icon: '!', tableIcon: '!' },
|
||||||
icon: 'x',
|
review: { color: chalk.magenta, icon: '?', tableIcon: '?' },
|
||||||
tableIcon: 'x'
|
cancelled: { color: chalk.gray, icon: 'X', tableIcon: 'X' }
|
||||||
},
|
|
||||||
review: {
|
|
||||||
color: chalk.magenta,
|
|
||||||
icon: '?',
|
|
||||||
tableIcon: '?'
|
|
||||||
},
|
|
||||||
cancelled: {
|
|
||||||
color: chalk.gray,
|
|
||||||
icon: 'x',
|
|
||||||
tableIcon: 'x'
|
|
||||||
},
|
|
||||||
blocked: {
|
|
||||||
color: chalk.red,
|
|
||||||
icon: '!',
|
|
||||||
tableIcon: '!'
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const config = statusConfig[status] || {
|
const config = statusConfig[status] || {
|
||||||
@@ -59,7 +39,18 @@ export function getStatusWithColor(
|
|||||||
tableIcon: 'X'
|
tableIcon: 'X'
|
||||||
};
|
};
|
||||||
|
|
||||||
const icon = forTable ? config.tableIcon : config.icon;
|
// Use simple ASCII characters for stable display
|
||||||
|
const simpleIcons = {
|
||||||
|
done: String.fromCharCode(8730), // √
|
||||||
|
pending: 'o',
|
||||||
|
'in-progress': '>',
|
||||||
|
deferred: 'x',
|
||||||
|
blocked: '!',
|
||||||
|
review: '?',
|
||||||
|
cancelled: 'X'
|
||||||
|
};
|
||||||
|
|
||||||
|
const icon = forTable ? simpleIcons[status] || 'X' : config.icon;
|
||||||
return config.color(`${icon} ${status}`);
|
return config.color(`${icon} ${status}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,24 +245,10 @@ export function createTaskTable(
|
|||||||
} = options || {};
|
} = options || {};
|
||||||
|
|
||||||
// Calculate dynamic column widths based on terminal width
|
// Calculate dynamic column widths based on terminal width
|
||||||
const terminalWidth = process.stdout.columns * 0.9 || 100;
|
const terminalWidth = process.stdout.columns || 100;
|
||||||
// Adjust column widths to better match the original layout
|
|
||||||
const baseColWidths = showComplexity
|
const baseColWidths = showComplexity
|
||||||
? [
|
? [8, Math.floor(terminalWidth * 0.35), 18, 12, 15, 12] // ID, Title, Status, Priority, Dependencies, Complexity
|
||||||
Math.floor(terminalWidth * 0.06),
|
: [8, Math.floor(terminalWidth * 0.4), 18, 12, 20]; // ID, Title, Status, Priority, Dependencies
|
||||||
Math.floor(terminalWidth * 0.4),
|
|
||||||
Math.floor(terminalWidth * 0.15),
|
|
||||||
Math.floor(terminalWidth * 0.12),
|
|
||||||
Math.floor(terminalWidth * 0.2),
|
|
||||||
Math.floor(terminalWidth * 0.12)
|
|
||||||
] // ID, Title, Status, Priority, Dependencies, Complexity
|
|
||||||
: [
|
|
||||||
Math.floor(terminalWidth * 0.08),
|
|
||||||
Math.floor(terminalWidth * 0.4),
|
|
||||||
Math.floor(terminalWidth * 0.18),
|
|
||||||
Math.floor(terminalWidth * 0.12),
|
|
||||||
Math.floor(terminalWidth * 0.2)
|
|
||||||
]; // ID, Title, Status, Priority, Dependencies
|
|
||||||
|
|
||||||
const headers = [
|
const headers = [
|
||||||
chalk.blue.bold('ID'),
|
chalk.blue.bold('ID'),
|
||||||
@@ -307,19 +284,11 @@ export function createTaskTable(
|
|||||||
];
|
];
|
||||||
|
|
||||||
if (showDependencies) {
|
if (showDependencies) {
|
||||||
// For table display, show simple format without status icons
|
row.push(formatDependenciesWithStatus(task.dependencies, tasks));
|
||||||
if (!task.dependencies || task.dependencies.length === 0) {
|
|
||||||
row.push(chalk.gray('None'));
|
|
||||||
} else {
|
|
||||||
row.push(
|
|
||||||
chalk.cyan(task.dependencies.map((d) => String(d)).join(', '))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showComplexity) {
|
if (showComplexity && 'complexity' in task) {
|
||||||
// Show N/A if no complexity score
|
row.push(getComplexityWithColor(task.complexity as number | string));
|
||||||
row.push(chalk.gray('N/A'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
table.push(row);
|
table.push(row);
|
||||||
|
|||||||
@@ -17,37 +17,18 @@ sidebarTitle: "CLI Commands"
|
|||||||
|
|
||||||
<Accordion title="List Tasks">
|
<Accordion title="List Tasks">
|
||||||
```bash
|
```bash
|
||||||
# List all tasks with enhanced dashboard view
|
# List all tasks
|
||||||
task-master list
|
task-master list
|
||||||
# Alias available
|
|
||||||
task-master ls
|
|
||||||
|
|
||||||
# List tasks with a specific status (comma-separated multiple statuses supported)
|
# List tasks with a specific status
|
||||||
task-master list --status=<status>
|
task-master list --status=<status>
|
||||||
task-master list --status=pending,in-progress
|
|
||||||
|
|
||||||
# List tasks with subtasks
|
# List tasks with subtasks
|
||||||
task-master list --with-subtasks
|
task-master list --with-subtasks
|
||||||
|
|
||||||
# Filter by tag
|
# List tasks with a specific status and include subtasks
|
||||||
task-master list --tag=<tag>
|
task-master list --status=<status> --with-subtasks
|
||||||
|
|
||||||
# Output formats: text (default), json, compact
|
|
||||||
task-master list --format=json
|
|
||||||
task-master list --format=compact
|
|
||||||
|
|
||||||
# Suppress output for programmatic use
|
|
||||||
task-master list --silent
|
|
||||||
|
|
||||||
# Specify project directory
|
|
||||||
task-master list --project=/path/to/project
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The `list` command now displays:
|
|
||||||
- **Project dashboard** with task statistics and progress bars
|
|
||||||
- **Next recommended task** based on dependencies and priority
|
|
||||||
- **Suggested next steps** for project workflow
|
|
||||||
- **Enhanced table view** with complexity scores and color-coded status
|
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
|
||||||
<Accordion title="Show Next Task">
|
<Accordion title="Show Next Task">
|
||||||
@@ -64,32 +45,23 @@ sidebarTitle: "CLI Commands"
|
|||||||
# or
|
# or
|
||||||
task-master show --id=<id>
|
task-master show --id=<id>
|
||||||
|
|
||||||
# View multiple tasks at once (comma-separated)
|
|
||||||
task-master show 1,2,3
|
|
||||||
task-master show --id=1,2,3
|
|
||||||
|
|
||||||
# View a specific subtask (e.g., subtask 2 of task 1)
|
# View a specific subtask (e.g., subtask 2 of task 1)
|
||||||
task-master show 1.2
|
task-master show 1.2
|
||||||
|
|
||||||
# Filter subtasks by status
|
# Show multiple tasks at once (comma-separated)
|
||||||
task-master show <id> --status=<status>
|
task-master show 1,2,3
|
||||||
|
task-master show --id=1,2,3
|
||||||
|
|
||||||
# Output formats: text (default), json
|
# Filter subtasks by status
|
||||||
|
task-master show <id> --status=pending
|
||||||
|
task-master show <id> --status=done
|
||||||
|
|
||||||
|
# Output in JSON format (useful for scripts)
|
||||||
task-master show <id> --format=json
|
task-master show <id> --format=json
|
||||||
|
|
||||||
# Suppress output for programmatic use
|
# Silent mode (suppress output, useful for programmatic usage)
|
||||||
task-master show <id> --silent
|
task-master show <id> --silent
|
||||||
|
|
||||||
# Specify project directory
|
|
||||||
task-master show <id> --project=/path/to/project
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The `show` command now features:
|
|
||||||
- **Enhanced task details** with markdown rendering for descriptions
|
|
||||||
- **Structured information display** with tables and sections
|
|
||||||
- **Multiple task support** for viewing several tasks simultaneously
|
|
||||||
- **Subtask filtering** by status
|
|
||||||
- **Suggested actions** for next steps on the task
|
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
|
||||||
<Accordion title="Update Tasks">
|
<Accordion title="Update Tasks">
|
||||||
|
|||||||
@@ -31,12 +31,6 @@ To see all tasks in the CLI you can use:
|
|||||||
task-master list
|
task-master list
|
||||||
```
|
```
|
||||||
|
|
||||||
The `list` command now includes a comprehensive dashboard showing:
|
|
||||||
- Project statistics with completion progress bars
|
|
||||||
- Next recommended task based on dependencies and priority
|
|
||||||
- Suggested workflow steps
|
|
||||||
- Enhanced task table with complexity indicators
|
|
||||||
|
|
||||||
To see all implementation details of an individual task, including subtasks and testing strategy, you can use Show Task:
|
To see all implementation details of an individual task, including subtasks and testing strategy, you can use Show Task:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
"description": "Task Master documentation powered by Mintlify",
|
"description": "Task Master documentation powered by Mintlify",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "mintlify dev",
|
"dev": "mintlify dev",
|
||||||
|
"build": "mintlify build",
|
||||||
"preview": "mintlify preview"
|
"preview": "mintlify preview"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Loader2, Play } from 'lucide-react';
|
import { Loader2 } from 'lucide-react';
|
||||||
import { PriorityBadge } from './PriorityBadge';
|
import { PriorityBadge } from './PriorityBadge';
|
||||||
import type { TaskMasterTask } from '../../webview/types';
|
import type { TaskMasterTask } from '../../webview/types';
|
||||||
import { useVSCodeContext } from '../../webview/contexts/VSCodeContext';
|
|
||||||
|
|
||||||
interface TaskMetadataSidebarProps {
|
interface TaskMetadataSidebarProps {
|
||||||
currentTask: TaskMasterTask;
|
currentTask: TaskMasterTask;
|
||||||
@@ -29,12 +28,10 @@ export const TaskMetadataSidebar: React.FC<TaskMetadataSidebarProps> = ({
|
|||||||
isRegenerating = false,
|
isRegenerating = false,
|
||||||
isAppending = false
|
isAppending = false
|
||||||
}) => {
|
}) => {
|
||||||
const { vscode } = useVSCodeContext();
|
|
||||||
const [isLoadingComplexity, setIsLoadingComplexity] = useState(false);
|
const [isLoadingComplexity, setIsLoadingComplexity] = useState(false);
|
||||||
const [mcpComplexityScore, setMcpComplexityScore] = useState<
|
const [mcpComplexityScore, setMcpComplexityScore] = useState<
|
||||||
number | undefined
|
number | undefined
|
||||||
>(undefined);
|
>(undefined);
|
||||||
const [isStartingTask, setIsStartingTask] = useState(false);
|
|
||||||
|
|
||||||
// Get complexity score from task
|
// Get complexity score from task
|
||||||
const currentComplexityScore = complexity?.score;
|
const currentComplexityScore = complexity?.score;
|
||||||
@@ -100,29 +97,6 @@ export const TaskMetadataSidebar: React.FC<TaskMetadataSidebarProps> = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle starting a task
|
|
||||||
const handleStartTask = () => {
|
|
||||||
if (!currentTask || isStartingTask) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsStartingTask(true);
|
|
||||||
|
|
||||||
// Send message to extension to open terminal
|
|
||||||
if (vscode) {
|
|
||||||
vscode.postMessage({
|
|
||||||
type: 'openTerminal',
|
|
||||||
taskId: currentTask.id,
|
|
||||||
taskTitle: currentTask.title
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset loading state after a short delay
|
|
||||||
setTimeout(() => {
|
|
||||||
setIsStartingTask(false);
|
|
||||||
}, 500);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Effect to handle complexity on task change
|
// Effect to handle complexity on task change
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (currentTask?.id) {
|
if (currentTask?.id) {
|
||||||
@@ -310,30 +284,6 @@ export const TaskMetadataSidebar: React.FC<TaskMetadataSidebarProps> = ({
|
|||||||
{currentTask.dependencies && currentTask.dependencies.length > 0 && (
|
{currentTask.dependencies && currentTask.dependencies.length > 0 && (
|
||||||
<div className="border-b border-textSeparator-foreground" />
|
<div className="border-b border-textSeparator-foreground" />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Start Task Button */}
|
|
||||||
<div className="mt-4">
|
|
||||||
<Button
|
|
||||||
onClick={handleStartTask}
|
|
||||||
variant="default"
|
|
||||||
size="sm"
|
|
||||||
className="w-full text-xs"
|
|
||||||
disabled={
|
|
||||||
isRegenerating ||
|
|
||||||
isAppending ||
|
|
||||||
isStartingTask ||
|
|
||||||
currentTask?.status === 'done' ||
|
|
||||||
currentTask?.status === 'in-progress'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{isStartingTask ? (
|
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
|
||||||
) : (
|
|
||||||
<Play className="w-4 h-4 mr-2" />
|
|
||||||
)}
|
|
||||||
{isStartingTask ? 'Starting...' : 'Start Task'}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -361,30 +361,6 @@ export class WebviewManager {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 'openTerminal':
|
|
||||||
// Open VS Code terminal for task execution
|
|
||||||
this.logger.log(
|
|
||||||
`Opening terminal for task ${data.taskId}: ${data.taskTitle}`
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const terminal = vscode.window.createTerminal({
|
|
||||||
name: `Task ${data.taskId}: ${data.taskTitle}`,
|
|
||||||
cwd: vscode.workspace.workspaceFolders?.[0]?.uri.fsPath
|
|
||||||
});
|
|
||||||
terminal.show();
|
|
||||||
|
|
||||||
this.logger.log('Terminal created and shown successfully');
|
|
||||||
response = { success: true };
|
|
||||||
} catch (error) {
|
|
||||||
this.logger.error('Failed to create terminal:', error);
|
|
||||||
response = {
|
|
||||||
success: false,
|
|
||||||
error: error instanceof Error ? error.message : 'Unknown error'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown message type: ${type}`);
|
throw new Error(`Unknown message type: ${type}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,8 +20,357 @@
|
|||||||
* Main entry point for globally installed package
|
* Main entry point for globally installed package
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Direct imports instead of spawning child processes
|
import { fileURLToPath } from 'url';
|
||||||
import { runCLI } from '../scripts/modules/commands.js';
|
import { dirname, resolve } from 'path';
|
||||||
|
import { createRequire } from 'module';
|
||||||
|
import { spawn } from 'child_process';
|
||||||
|
import { Command } from 'commander';
|
||||||
|
import { displayHelp, displayBanner } from '../scripts/modules/ui.js';
|
||||||
|
import { registerCommands } from '../scripts/modules/commands.js';
|
||||||
|
import { detectCamelCaseFlags } from '../scripts/modules/utils.js';
|
||||||
|
import chalk from 'chalk';
|
||||||
|
|
||||||
// Simply run the CLI directly
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
runCLI();
|
const __dirname = dirname(__filename);
|
||||||
|
const require = createRequire(import.meta.url);
|
||||||
|
|
||||||
|
// Get package information
|
||||||
|
const packageJson = require('../package.json');
|
||||||
|
const version = packageJson.version;
|
||||||
|
|
||||||
|
// Get paths to script files
|
||||||
|
const devScriptPath = resolve(__dirname, '../scripts/dev.js');
|
||||||
|
const initScriptPath = resolve(__dirname, '../scripts/init.js');
|
||||||
|
|
||||||
|
// Helper function to run dev.js with arguments
|
||||||
|
function runDevScript(args) {
|
||||||
|
// Debug: Show the transformed arguments when DEBUG=1 is set
|
||||||
|
if (process.env.DEBUG === '1') {
|
||||||
|
console.error('\nDEBUG - CLI Wrapper Analysis:');
|
||||||
|
console.error('- Original command: ' + process.argv.join(' '));
|
||||||
|
console.error('- Transformed args: ' + args.join(' '));
|
||||||
|
console.error(
|
||||||
|
'- dev.js will receive: node ' +
|
||||||
|
devScriptPath +
|
||||||
|
' ' +
|
||||||
|
args.join(' ') +
|
||||||
|
'\n'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For testing: If TEST_MODE is set, just print args and exit
|
||||||
|
if (process.env.TEST_MODE === '1') {
|
||||||
|
console.log('Would execute:');
|
||||||
|
console.log(`node ${devScriptPath} ${args.join(' ')}`);
|
||||||
|
process.exit(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const child = spawn('node', [devScriptPath, ...args], {
|
||||||
|
stdio: 'inherit',
|
||||||
|
cwd: process.cwd()
|
||||||
|
});
|
||||||
|
|
||||||
|
child.on('close', (code) => {
|
||||||
|
process.exit(code);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to detect camelCase and convert to kebab-case
|
||||||
|
const toKebabCase = (str) => str.replace(/([A-Z])/g, '-$1').toLowerCase();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a wrapper action that passes the command to dev.js
|
||||||
|
* @param {string} commandName - The name of the command
|
||||||
|
* @returns {Function} Wrapper action function
|
||||||
|
*/
|
||||||
|
function createDevScriptAction(commandName) {
|
||||||
|
return (options, cmd) => {
|
||||||
|
// Check for camelCase flags and error out with helpful message
|
||||||
|
const camelCaseFlags = detectCamelCaseFlags(process.argv);
|
||||||
|
|
||||||
|
// If camelCase flags were found, show error and exit
|
||||||
|
if (camelCaseFlags.length > 0) {
|
||||||
|
console.error('\nError: Please use kebab-case for CLI flags:');
|
||||||
|
camelCaseFlags.forEach((flag) => {
|
||||||
|
console.error(` Instead of: --${flag.original}`);
|
||||||
|
console.error(` Use: --${flag.kebabCase}`);
|
||||||
|
});
|
||||||
|
console.error(
|
||||||
|
'\nExample: task-master parse-prd --num-tasks=5 instead of --numTasks=5\n'
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since we've ensured no camelCase flags, we can now just:
|
||||||
|
// 1. Start with the command name
|
||||||
|
const args = [commandName];
|
||||||
|
|
||||||
|
// 3. Get positional arguments and explicit flags from the command line
|
||||||
|
const commandArgs = [];
|
||||||
|
const positionals = new Set(); // Track positional args we've seen
|
||||||
|
|
||||||
|
// Find the command in raw process.argv to extract args
|
||||||
|
const commandIndex = process.argv.indexOf(commandName);
|
||||||
|
if (commandIndex !== -1) {
|
||||||
|
// Process all args after the command name
|
||||||
|
for (let i = commandIndex + 1; i < process.argv.length; i++) {
|
||||||
|
const arg = process.argv[i];
|
||||||
|
|
||||||
|
if (arg.startsWith('--')) {
|
||||||
|
// It's a flag - pass through as is
|
||||||
|
commandArgs.push(arg);
|
||||||
|
// Skip the next arg if this is a flag with a value (not --flag=value format)
|
||||||
|
if (
|
||||||
|
!arg.includes('=') &&
|
||||||
|
i + 1 < process.argv.length &&
|
||||||
|
!process.argv[i + 1].startsWith('--')
|
||||||
|
) {
|
||||||
|
commandArgs.push(process.argv[++i]);
|
||||||
|
}
|
||||||
|
} else if (!positionals.has(arg)) {
|
||||||
|
// It's a positional argument we haven't seen
|
||||||
|
commandArgs.push(arg);
|
||||||
|
positionals.add(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add all command line args we collected
|
||||||
|
args.push(...commandArgs);
|
||||||
|
|
||||||
|
// 4. Add default options from Commander if not specified on command line
|
||||||
|
// Track which options we've seen on the command line
|
||||||
|
const userOptions = new Set();
|
||||||
|
for (const arg of commandArgs) {
|
||||||
|
if (arg.startsWith('--')) {
|
||||||
|
// Extract option name (without -- and value)
|
||||||
|
const name = arg.split('=')[0].slice(2);
|
||||||
|
userOptions.add(name);
|
||||||
|
|
||||||
|
// Add the kebab-case version too, to prevent duplicates
|
||||||
|
const kebabName = name.replace(/([A-Z])/g, '-$1').toLowerCase();
|
||||||
|
userOptions.add(kebabName);
|
||||||
|
|
||||||
|
// Add the camelCase version as well
|
||||||
|
const camelName = kebabName.replace(/-([a-z])/g, (_, letter) =>
|
||||||
|
letter.toUpperCase()
|
||||||
|
);
|
||||||
|
userOptions.add(camelName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add Commander-provided defaults for options not specified by user
|
||||||
|
Object.entries(options).forEach(([key, value]) => {
|
||||||
|
// Debug output to see what keys we're getting
|
||||||
|
if (process.env.DEBUG === '1') {
|
||||||
|
console.error(`DEBUG - Processing option: ${key} = ${value}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special case for numTasks > num-tasks (a known problem case)
|
||||||
|
if (key === 'numTasks') {
|
||||||
|
if (process.env.DEBUG === '1') {
|
||||||
|
console.error('DEBUG - Converting numTasks to num-tasks');
|
||||||
|
}
|
||||||
|
if (!userOptions.has('num-tasks') && !userOptions.has('numTasks')) {
|
||||||
|
args.push(`--num-tasks=${value}`);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip built-in Commander properties and options the user provided
|
||||||
|
if (
|
||||||
|
['parent', 'commands', 'options', 'rawArgs'].includes(key) ||
|
||||||
|
userOptions.has(key)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also check the kebab-case version of this key
|
||||||
|
const kebabKey = key.replace(/([A-Z])/g, '-$1').toLowerCase();
|
||||||
|
if (userOptions.has(kebabKey)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add default values, using kebab-case for the parameter name
|
||||||
|
if (value !== undefined) {
|
||||||
|
if (typeof value === 'boolean') {
|
||||||
|
if (value === true) {
|
||||||
|
args.push(`--${kebabKey}`);
|
||||||
|
} else if (value === false && key === 'generate') {
|
||||||
|
args.push('--skip-generate');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Always use kebab-case for option names
|
||||||
|
args.push(`--${kebabKey}=${value}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Special handling for parent parameter (uses -p)
|
||||||
|
if (options.parent && !args.includes('-p') && !userOptions.has('parent')) {
|
||||||
|
args.push('-p', options.parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug output for troubleshooting
|
||||||
|
if (process.env.DEBUG === '1') {
|
||||||
|
console.error('DEBUG - Command args:', commandArgs);
|
||||||
|
console.error('DEBUG - User options:', Array.from(userOptions));
|
||||||
|
console.error('DEBUG - Commander options:', options);
|
||||||
|
console.error('DEBUG - Final args:', args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the script with our processed args
|
||||||
|
runDevScript(args);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// // Special case for the 'init' command which uses a different script
|
||||||
|
// function registerInitCommand(program) {
|
||||||
|
// program
|
||||||
|
// .command('init')
|
||||||
|
// .description('Initialize a new project')
|
||||||
|
// .option('-y, --yes', 'Skip prompts and use default values')
|
||||||
|
// .option('-n, --name <name>', 'Project name')
|
||||||
|
// .option('-d, --description <description>', 'Project description')
|
||||||
|
// .option('-v, --version <version>', 'Project version')
|
||||||
|
// .option('-a, --author <author>', 'Author name')
|
||||||
|
// .option('--skip-install', 'Skip installing dependencies')
|
||||||
|
// .option('--dry-run', 'Show what would be done without making changes')
|
||||||
|
// .action((options) => {
|
||||||
|
// // Pass through any options to the init script
|
||||||
|
// const args = [
|
||||||
|
// '--yes',
|
||||||
|
// 'name',
|
||||||
|
// 'description',
|
||||||
|
// 'version',
|
||||||
|
// 'author',
|
||||||
|
// 'skip-install',
|
||||||
|
// 'dry-run'
|
||||||
|
// ]
|
||||||
|
// .filter((opt) => options[opt])
|
||||||
|
// .map((opt) => {
|
||||||
|
// if (opt === 'yes' || opt === 'skip-install' || opt === 'dry-run') {
|
||||||
|
// return `--${opt}`;
|
||||||
|
// }
|
||||||
|
// return `--${opt}=${options[opt]}`;
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const child = spawn('node', [initScriptPath, ...args], {
|
||||||
|
// stdio: 'inherit',
|
||||||
|
// cwd: process.cwd()
|
||||||
|
// });
|
||||||
|
|
||||||
|
// child.on('close', (code) => {
|
||||||
|
// process.exit(code);
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Set up the command-line interface
|
||||||
|
const program = new Command();
|
||||||
|
|
||||||
|
program
|
||||||
|
.name('task-master')
|
||||||
|
.description('Claude Task Master CLI')
|
||||||
|
.version(version)
|
||||||
|
.addHelpText('afterAll', () => {
|
||||||
|
// Use the same help display function as dev.js for consistency
|
||||||
|
displayHelp();
|
||||||
|
return ''; // Return empty string to prevent commander's default help
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add custom help option to directly call our help display
|
||||||
|
program.helpOption('-h, --help', 'Display help information');
|
||||||
|
program.on('--help', () => {
|
||||||
|
displayHelp();
|
||||||
|
});
|
||||||
|
|
||||||
|
// // Add special case commands
|
||||||
|
// registerInitCommand(program);
|
||||||
|
|
||||||
|
program
|
||||||
|
.command('dev')
|
||||||
|
.description('Run the dev.js script')
|
||||||
|
.action(() => {
|
||||||
|
const args = process.argv.slice(process.argv.indexOf('dev') + 1);
|
||||||
|
runDevScript(args);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Use a temporary Command instance to get all command definitions
|
||||||
|
const tempProgram = new Command();
|
||||||
|
registerCommands(tempProgram);
|
||||||
|
|
||||||
|
// For each command in the temp instance, add a modified version to our actual program
|
||||||
|
tempProgram.commands.forEach((cmd) => {
|
||||||
|
if (['dev'].includes(cmd.name())) {
|
||||||
|
// Skip commands we've already defined specially
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new command with the same name and description
|
||||||
|
const newCmd = program.command(cmd.name()).description(cmd.description());
|
||||||
|
|
||||||
|
// Copy all options
|
||||||
|
cmd.options.forEach((opt) => {
|
||||||
|
newCmd.option(opt.flags, opt.description, opt.defaultValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set the action to proxy to dev.js
|
||||||
|
newCmd.action(createDevScriptAction(cmd.name()));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Parse the command line arguments
|
||||||
|
program.parse(process.argv);
|
||||||
|
|
||||||
|
// Add global error handling for unknown commands and options
|
||||||
|
process.on('uncaughtException', (err) => {
|
||||||
|
// Check if this is a commander.js unknown option error
|
||||||
|
if (err.code === 'commander.unknownOption') {
|
||||||
|
const option = err.message.match(/'([^']+)'/)?.[1];
|
||||||
|
const commandArg = process.argv.find(
|
||||||
|
(arg) =>
|
||||||
|
!arg.startsWith('-') &&
|
||||||
|
arg !== 'task-master' &&
|
||||||
|
!arg.includes('/') &&
|
||||||
|
arg !== 'node'
|
||||||
|
);
|
||||||
|
const command = commandArg || 'unknown';
|
||||||
|
|
||||||
|
console.error(chalk.red(`Error: Unknown option '${option}'`));
|
||||||
|
console.error(
|
||||||
|
chalk.yellow(
|
||||||
|
`Run 'task-master ${command} --help' to see available options for this command`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this is a commander.js unknown command error
|
||||||
|
if (err.code === 'commander.unknownCommand') {
|
||||||
|
const command = err.message.match(/'([^']+)'/)?.[1];
|
||||||
|
|
||||||
|
console.error(chalk.red(`Error: Unknown command '${command}'`));
|
||||||
|
console.error(
|
||||||
|
chalk.yellow(`Run 'task-master --help' to see available commands`)
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle other uncaught exceptions
|
||||||
|
console.error(chalk.red(`Error: ${err.message}`));
|
||||||
|
if (process.env.DEBUG === '1') {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Show help if no command was provided (just 'task-master' with no args)
|
||||||
|
if (process.argv.length <= 2) {
|
||||||
|
displayBanner();
|
||||||
|
displayHelp();
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add exports at the end of the file
|
||||||
|
export { detectCamelCaseFlags };
|
||||||
|
|||||||
106
output.txt
106
output.txt
File diff suppressed because one or more lines are too long
173
package-lock.json
generated
173
package-lock.json
generated
@@ -29,7 +29,6 @@
|
|||||||
"@inquirer/search": "^3.0.15",
|
"@inquirer/search": "^3.0.15",
|
||||||
"@openrouter/ai-sdk-provider": "^0.4.5",
|
"@openrouter/ai-sdk-provider": "^0.4.5",
|
||||||
"@streamparser/json": "^0.0.22",
|
"@streamparser/json": "^0.0.22",
|
||||||
"@tm/cli": "*",
|
|
||||||
"ai": "^4.3.10",
|
"ai": "^4.3.10",
|
||||||
"ajv": "^8.17.1",
|
"ajv": "^8.17.1",
|
||||||
"ajv-formats": "^3.0.1",
|
"ajv-formats": "^3.0.1",
|
||||||
@@ -53,8 +52,6 @@
|
|||||||
"jsonrepair": "^3.13.0",
|
"jsonrepair": "^3.13.0",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"lru-cache": "^10.2.0",
|
"lru-cache": "^10.2.0",
|
||||||
"marked": "^15.0.12",
|
|
||||||
"marked-terminal": "^7.3.0",
|
|
||||||
"ollama-ai-provider": "^1.2.0",
|
"ollama-ai-provider": "^1.2.0",
|
||||||
"openai": "^4.89.0",
|
"openai": "^4.89.0",
|
||||||
"ora": "^8.2.0",
|
"ora": "^8.2.0",
|
||||||
@@ -72,7 +69,6 @@
|
|||||||
"@changesets/changelog-github": "^0.5.1",
|
"@changesets/changelog-github": "^0.5.1",
|
||||||
"@changesets/cli": "^2.28.1",
|
"@changesets/cli": "^2.28.1",
|
||||||
"@types/jest": "^29.5.14",
|
"@types/jest": "^29.5.14",
|
||||||
"@types/marked-terminal": "^6.1.1",
|
|
||||||
"concurrently": "^9.2.1",
|
"concurrently": "^9.2.1",
|
||||||
"cross-env": "^10.0.0",
|
"cross-env": "^10.0.0",
|
||||||
"dotenv-mono": "^1.5.1",
|
"dotenv-mono": "^1.5.1",
|
||||||
@@ -10104,13 +10100,6 @@
|
|||||||
"@babel/types": "^7.28.2"
|
"@babel/types": "^7.28.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/cardinal": {
|
|
||||||
"version": "2.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/cardinal/-/cardinal-2.1.1.tgz",
|
|
||||||
"integrity": "sha512-/xCVwg8lWvahHsV2wXZt4i64H1sdL+sN1Uoq7fAc8/FA6uYHjuIveDwPwvGUYp4VZiv85dVl6J/Bum3NDAOm8g==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/@types/cors": {
|
"node_modules/@types/cors": {
|
||||||
"version": "2.8.19",
|
"version": "2.8.19",
|
||||||
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz",
|
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz",
|
||||||
@@ -10271,32 +10260,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/marked-terminal": {
|
|
||||||
"version": "6.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/marked-terminal/-/marked-terminal-6.1.1.tgz",
|
|
||||||
"integrity": "sha512-DfoUqkmFDCED7eBY9vFUhJ9fW8oZcMAK5EwRDQ9drjTbpQa+DnBTQQCwWhTFVf4WsZ6yYcJTI8D91wxTWXRZZQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@types/cardinal": "^2.1",
|
|
||||||
"@types/node": "*",
|
|
||||||
"chalk": "^5.3.0",
|
|
||||||
"marked": ">=6.0.0 <12"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@types/marked-terminal/node_modules/marked": {
|
|
||||||
"version": "11.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/marked/-/marked-11.2.0.tgz",
|
|
||||||
"integrity": "sha512-HR0m3bvu0jAPYiIvLUUQtdg1g6D247//lvcekpHO1WMvbwDlwSkZAX9Lw4F4YHE1T0HaaNve0tuAWuV1UJ6vtw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"bin": {
|
|
||||||
"marked": "bin/marked.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@types/mdast": {
|
"node_modules/@types/mdast": {
|
||||||
"version": "4.0.4",
|
"version": "4.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
|
||||||
@@ -12896,6 +12859,7 @@
|
|||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
|
||||||
"integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==",
|
"integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
@@ -14930,12 +14894,6 @@
|
|||||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/emojilib": {
|
|
||||||
"version": "2.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz",
|
|
||||||
"integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/encodeurl": {
|
"node_modules/encodeurl": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
||||||
@@ -15105,6 +15063,7 @@
|
|||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
|
||||||
"integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==",
|
"integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
@@ -22343,6 +22302,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz",
|
"resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz",
|
||||||
"integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==",
|
"integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"marked": "bin/marked.js"
|
"marked": "bin/marked.js"
|
||||||
},
|
},
|
||||||
@@ -22350,54 +22310,6 @@
|
|||||||
"node": ">= 18"
|
"node": ">= 18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/marked-terminal": {
|
|
||||||
"version": "7.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-7.3.0.tgz",
|
|
||||||
"integrity": "sha512-t4rBvPsHc57uE/2nJOLmMbZCQ4tgAccAED3ngXQqW6g+TxA488JzJ+FK3lQkzBQOI1mRV/r/Kq+1ZlJ4D0owQw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"ansi-escapes": "^7.0.0",
|
|
||||||
"ansi-regex": "^6.1.0",
|
|
||||||
"chalk": "^5.4.1",
|
|
||||||
"cli-highlight": "^2.1.11",
|
|
||||||
"cli-table3": "^0.6.5",
|
|
||||||
"node-emoji": "^2.2.0",
|
|
||||||
"supports-hyperlinks": "^3.1.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16.0.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"marked": ">=1 <16"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/marked-terminal/node_modules/ansi-escapes": {
|
|
||||||
"version": "7.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.1.0.tgz",
|
|
||||||
"integrity": "sha512-YdhtCd19sKRKfAAUsrcC1wzm4JuzJoiX4pOJqIoW2qmKj5WzG/dL8uUJ0361zaXtHqK7gEhOwtAtz7t3Yq3X5g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"environment": "^1.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/marked-terminal/node_modules/ansi-regex": {
|
|
||||||
"version": "6.2.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
|
|
||||||
"integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/math-intrinsics": {
|
"node_modules/math-intrinsics": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||||
@@ -24221,33 +24133,6 @@
|
|||||||
"node": ">=10.5.0"
|
"node": ">=10.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/node-emoji": {
|
|
||||||
"version": "2.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.2.0.tgz",
|
|
||||||
"integrity": "sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@sindresorhus/is": "^4.6.0",
|
|
||||||
"char-regex": "^1.0.2",
|
|
||||||
"emojilib": "^2.4.0",
|
|
||||||
"skin-tone": "^2.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/node-emoji/node_modules/@sindresorhus/is": {
|
|
||||||
"version": "4.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
|
|
||||||
"integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sindresorhus/is?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/node-fetch": {
|
"node_modules/node-fetch": {
|
||||||
"version": "2.7.0",
|
"version": "2.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||||
@@ -27757,18 +27642,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/skin-tone": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"unicode-emoji-modifier-base": "^1.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/slash": {
|
"node_modules/slash": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
|
||||||
@@ -28572,34 +28445,6 @@
|
|||||||
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/supports-hyperlinks": {
|
|
||||||
"version": "3.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz",
|
|
||||||
"integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"has-flag": "^4.0.0",
|
|
||||||
"supports-color": "^7.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14.18"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/chalk/supports-hyperlinks?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/supports-hyperlinks/node_modules/supports-color": {
|
|
||||||
"version": "7.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
|
||||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"has-flag": "^4.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/supports-preserve-symlinks-flag": {
|
"node_modules/supports-preserve-symlinks-flag": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||||
@@ -29589,15 +29434,6 @@
|
|||||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/unicode-emoji-modifier-base": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/unicorn-magic": {
|
"node_modules/unicorn-magic": {
|
||||||
"version": "0.3.0",
|
"version": "0.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz",
|
||||||
@@ -31437,12 +31273,9 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "^1.9.4",
|
"@biomejs/biome": "^1.9.4",
|
||||||
"@tm/build-config": "*",
|
|
||||||
"@types/node": "^20.11.30",
|
"@types/node": "^20.11.30",
|
||||||
"@vitest/coverage-v8": "^2.0.5",
|
"@vitest/coverage-v8": "^2.0.5",
|
||||||
"dotenv-mono": "^1.3.14",
|
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"tsup": "^8.5.0",
|
|
||||||
"typescript": "^5.4.3",
|
"typescript": "^5.4.3",
|
||||||
"vitest": "^2.0.5"
|
"vitest": "^2.0.5"
|
||||||
},
|
},
|
||||||
|
|||||||
29
package.json
29
package.json
@@ -12,10 +12,9 @@
|
|||||||
"workspaces": ["apps/*", "packages/*", "."],
|
"workspaces": ["apps/*", "packages/*", "."],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "npm run build:build-config && tsup",
|
"build": "npm run build:build-config && tsup",
|
||||||
"dev": "tsup --watch='packages/*/src/**/*' --watch='apps/cli/src/**/*' --watch='bin/**/*' --watch='mcp-server/**/*'",
|
"dev": "tsup --watch",
|
||||||
"turbo:dev": "turbo dev",
|
"turbo:dev": "turbo dev",
|
||||||
"turbo:build": "turbo build",
|
"turbo:build": "turbo build",
|
||||||
"turbo:typecheck": "turbo typecheck",
|
|
||||||
"dev:main": "tsup --watch --onSuccess 'echo \"📦 Main package built\" && npm link'",
|
"dev:main": "tsup --watch --onSuccess 'echo \"📦 Main package built\" && npm link'",
|
||||||
"dev:legacy": "npm run build:build-config && concurrently -n \"core,cli,main\" -c \"blue,green,yellow\" \"npm run dev:core\" \"npm run dev:cli\" \"npm run dev:main\"",
|
"dev:legacy": "npm run build:build-config && concurrently -n \"core,cli,main\" -c \"blue,green,yellow\" \"npm run dev:core\" \"npm run dev:cli\" \"npm run dev:main\"",
|
||||||
"dev:core": "npm run dev -w @tm/core",
|
"dev:core": "npm run dev -w @tm/core",
|
||||||
@@ -25,11 +24,17 @@
|
|||||||
"build:build-config": "npm run build -w @tm/build-config",
|
"build:build-config": "npm run build -w @tm/build-config",
|
||||||
"build:core": "npm run build -w @tm/core",
|
"build:core": "npm run build -w @tm/core",
|
||||||
"build:cli": "npm run build -w @tm/cli",
|
"build:cli": "npm run build -w @tm/cli",
|
||||||
"test": "node --experimental-vm-modules node_modules/.bin/jest",
|
"typecheck": "turbo typecheck",
|
||||||
"test:unit": "node --experimental-vm-modules node_modules/.bin/jest --testPathPattern=unit",
|
"typecheck:all": "turbo typecheck",
|
||||||
"test:integration": "node --experimental-vm-modules node_modules/.bin/jest --testPathPattern=integration",
|
"typecheck:core": "npm run typecheck -w @tm/core",
|
||||||
"test:fails": "node --experimental-vm-modules node_modules/.bin/jest --onlyFailures",
|
"typecheck:cli": "npm run typecheck -w @tm/cli",
|
||||||
"test:watch": "node --experimental-vm-modules node_modules/.bin/jest --watch",
|
"test": "turbo test",
|
||||||
|
"test:watch": "turbo test:watch",
|
||||||
|
"test:legacy": "NODE_ENV=development node --experimental-vm-modules node_modules/.bin/jest",
|
||||||
|
"test:debug": "NODE_ENV=development node --inspect --experimental-vm-modules node_modules/.bin/jest --no-cache --verbose",
|
||||||
|
"test:unit": "NODE_ENV=development node --experimental-vm-modules node_modules/.bin/jest --testPathPattern=unit",
|
||||||
|
"test:integration": "NODE_ENV=development node --experimental-vm-modules node_modules/.bin/jest --testPathPattern=integration",
|
||||||
|
"test:fails": "NODE_ENV=development node --experimental-vm-modules node_modules/.bin/jest --onlyFailures",
|
||||||
"test:coverage": "node --experimental-vm-modules node_modules/.bin/jest --coverage",
|
"test:coverage": "node --experimental-vm-modules node_modules/.bin/jest --coverage",
|
||||||
"test:ci": "node --experimental-vm-modules node_modules/.bin/jest --coverage --ci",
|
"test:ci": "node --experimental-vm-modules node_modules/.bin/jest --coverage --ci",
|
||||||
"test:e2e": "./tests/e2e/run_e2e.sh",
|
"test:e2e": "./tests/e2e/run_e2e.sh",
|
||||||
@@ -41,7 +46,11 @@
|
|||||||
"inspector": "npx @modelcontextprotocol/inspector node dist/mcp-server.js",
|
"inspector": "npx @modelcontextprotocol/inspector node dist/mcp-server.js",
|
||||||
"mcp-server": "node dist/mcp-server.js",
|
"mcp-server": "node dist/mcp-server.js",
|
||||||
"format-check": "biome format .",
|
"format-check": "biome format .",
|
||||||
"format": "biome format . --write"
|
"format": "biome format . --write",
|
||||||
|
"lint": "turbo lint",
|
||||||
|
"lint:all": "turbo lint",
|
||||||
|
"lint:legacy": "npm run lint --workspaces --if-present",
|
||||||
|
"test:all": "turbo test"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"claude",
|
"claude",
|
||||||
@@ -73,7 +82,6 @@
|
|||||||
"@inquirer/search": "^3.0.15",
|
"@inquirer/search": "^3.0.15",
|
||||||
"@openrouter/ai-sdk-provider": "^0.4.5",
|
"@openrouter/ai-sdk-provider": "^0.4.5",
|
||||||
"@streamparser/json": "^0.0.22",
|
"@streamparser/json": "^0.0.22",
|
||||||
"@tm/cli": "*",
|
|
||||||
"ai": "^4.3.10",
|
"ai": "^4.3.10",
|
||||||
"ajv": "^8.17.1",
|
"ajv": "^8.17.1",
|
||||||
"ajv-formats": "^3.0.1",
|
"ajv-formats": "^3.0.1",
|
||||||
@@ -97,8 +105,6 @@
|
|||||||
"jsonrepair": "^3.13.0",
|
"jsonrepair": "^3.13.0",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"lru-cache": "^10.2.0",
|
"lru-cache": "^10.2.0",
|
||||||
"marked": "^15.0.12",
|
|
||||||
"marked-terminal": "^7.3.0",
|
|
||||||
"ollama-ai-provider": "^1.2.0",
|
"ollama-ai-provider": "^1.2.0",
|
||||||
"openai": "^4.89.0",
|
"openai": "^4.89.0",
|
||||||
"ora": "^8.2.0",
|
"ora": "^8.2.0",
|
||||||
@@ -133,7 +139,6 @@
|
|||||||
"@changesets/changelog-github": "^0.5.1",
|
"@changesets/changelog-github": "^0.5.1",
|
||||||
"@changesets/cli": "^2.28.1",
|
"@changesets/cli": "^2.28.1",
|
||||||
"@types/jest": "^29.5.14",
|
"@types/jest": "^29.5.14",
|
||||||
"@types/marked-terminal": "^6.1.1",
|
|
||||||
"concurrently": "^9.2.1",
|
"concurrently": "^9.2.1",
|
||||||
"cross-env": "^10.0.0",
|
"cross-env": "^10.0.0",
|
||||||
"dotenv-mono": "^1.5.1",
|
"dotenv-mono": "^1.5.1",
|
||||||
|
|||||||
@@ -3,17 +3,38 @@
|
|||||||
* Provides shared configuration that can be extended by individual packages
|
* Provides shared configuration that can be extended by individual packages
|
||||||
*/
|
*/
|
||||||
import type { Options } from 'tsup';
|
import type { Options } from 'tsup';
|
||||||
|
import * as dotenv from 'dotenv-mono';
|
||||||
|
|
||||||
|
dotenv.load();
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
'TM_PUBLIC_BASE_DOMAIN:',
|
||||||
|
process.env.TM_PUBLIC_BASE_DOMAIN,
|
||||||
|
'TM_PUBLIC_SUPABASE_URL:',
|
||||||
|
process.env.TM_PUBLIC_SUPABASE_URL,
|
||||||
|
'TM_PUBLIC_SUPABASE_ANON_KEY:',
|
||||||
|
process.env.TM_PUBLIC_SUPABASE_ANON_KEY
|
||||||
|
);
|
||||||
|
|
||||||
const isProduction = process.env.NODE_ENV === 'production';
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
const isDevelopment = !isProduction;
|
const isDevelopment = !isProduction;
|
||||||
|
|
||||||
|
const envVariables = {
|
||||||
|
TM_PUBLIC_BASE_DOMAIN: process.env.TM_PUBLIC_BASE_DOMAIN ?? '',
|
||||||
|
TM_PUBLIC_SUPABASE_URL: process.env.TM_PUBLIC_SUPABASE_URL ?? '',
|
||||||
|
TM_PUBLIC_SUPABASE_ANON_KEY: process.env.TM_PUBLIC_SUPABASE_ANON_KEY ?? ''
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('envVariables:', envVariables);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Environment helpers
|
* Environment helpers
|
||||||
*/
|
*/
|
||||||
export const env = {
|
export const env = {
|
||||||
isProduction,
|
isProduction,
|
||||||
isDevelopment,
|
isDevelopment,
|
||||||
NODE_ENV: process.env.NODE_ENV || 'development'
|
NODE_ENV: process.env.NODE_ENV || 'development',
|
||||||
|
...envVariables
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -31,6 +52,7 @@ export const baseConfig: Partial<Options> = {
|
|||||||
splitting: false,
|
splitting: false,
|
||||||
// Don't bundle any other dependencies (auto-external all node_modules)
|
// Don't bundle any other dependencies (auto-external all node_modules)
|
||||||
external: [/^[^./]/],
|
external: [/^[^./]/],
|
||||||
|
env: envVariables,
|
||||||
esbuildOptions(options) {
|
esbuildOptions(options) {
|
||||||
options.platform = 'node';
|
options.platform = 'node';
|
||||||
// Allow importing TypeScript from JavaScript
|
// Allow importing TypeScript from JavaScript
|
||||||
|
|||||||
@@ -7,19 +7,54 @@
|
|||||||
"types": "./src/index.ts",
|
"types": "./src/index.ts",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts",
|
".": {
|
||||||
"./auth": "./src/auth/index.ts",
|
"types": "./src/index.ts",
|
||||||
"./storage": "./src/storage/index.ts",
|
"import": "./dist/index.js"
|
||||||
"./config": "./src/config/index.ts",
|
},
|
||||||
"./providers": "./src/providers/index.ts",
|
"./auth": {
|
||||||
"./services": "./src/services/index.ts",
|
"types": "./src/auth/index.ts",
|
||||||
"./errors": "./src/errors/index.ts",
|
"import": "./dist/auth/index.js"
|
||||||
"./logger": "./src/logger/index.ts",
|
},
|
||||||
"./types": "./src/types/index.ts",
|
"./storage": {
|
||||||
"./interfaces": "./src/interfaces/index.ts",
|
"types": "./src/storage/index.ts",
|
||||||
"./utils": "./src/utils/index.ts"
|
"import": "./dist/storage/index.js"
|
||||||
|
},
|
||||||
|
"./config": {
|
||||||
|
"types": "./src/config/index.ts",
|
||||||
|
"import": "./dist/config/index.js"
|
||||||
|
},
|
||||||
|
"./providers": {
|
||||||
|
"types": "./src/providers/index.ts",
|
||||||
|
"import": "./dist/providers/index.js"
|
||||||
|
},
|
||||||
|
"./services": {
|
||||||
|
"types": "./src/services/index.ts",
|
||||||
|
"import": "./dist/services/index.js"
|
||||||
|
},
|
||||||
|
"./errors": {
|
||||||
|
"types": "./src/errors/index.ts",
|
||||||
|
"import": "./dist/errors/index.js"
|
||||||
|
},
|
||||||
|
"./logger": {
|
||||||
|
"types": "./src/logger/index.ts",
|
||||||
|
"import": "./dist/logger/index.js"
|
||||||
|
},
|
||||||
|
"./types": {
|
||||||
|
"types": "./src/types/index.ts",
|
||||||
|
"import": "./dist/types/index.js"
|
||||||
|
},
|
||||||
|
"./interfaces": {
|
||||||
|
"types": "./src/interfaces/index.ts",
|
||||||
|
"import": "./dist/interfaces/index.js"
|
||||||
|
},
|
||||||
|
"./utils": {
|
||||||
|
"types": "./src/utils/index.ts",
|
||||||
|
"import": "./dist/utils/index.js"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"build": "tsc",
|
||||||
|
"dev": "tsc --watch",
|
||||||
"test": "vitest run",
|
"test": "vitest run",
|
||||||
"test:watch": "vitest",
|
"test:watch": "vitest",
|
||||||
"test:coverage": "vitest run --coverage",
|
"test:coverage": "vitest run --coverage",
|
||||||
@@ -36,12 +71,9 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "^1.9.4",
|
"@biomejs/biome": "^1.9.4",
|
||||||
"@tm/build-config": "*",
|
|
||||||
"@types/node": "^20.11.30",
|
"@types/node": "^20.11.30",
|
||||||
"@vitest/coverage-v8": "^2.0.5",
|
"@vitest/coverage-v8": "^2.0.5",
|
||||||
"dotenv-mono": "^1.3.14",
|
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"tsup": "^8.5.0",
|
|
||||||
"typescript": "^5.4.3",
|
"typescript": "^5.4.3",
|
||||||
"vitest": "^2.0.5"
|
"vitest": "^2.0.5"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import packageJson from '../../package.json' with { type: 'json' };
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import { log } from '../../scripts/modules/utils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads the version from the nearest package.json relative to this file.
|
* Reads the version from the nearest package.json relative to this file.
|
||||||
@@ -6,5 +9,27 @@ import packageJson from '../../package.json' with { type: 'json' };
|
|||||||
* @returns {string} The version string or 'unknown'.
|
* @returns {string} The version string or 'unknown'.
|
||||||
*/
|
*/
|
||||||
export function getTaskMasterVersion() {
|
export function getTaskMasterVersion() {
|
||||||
return packageJson.version || 'unknown';
|
let version = 'unknown';
|
||||||
|
try {
|
||||||
|
// Get the directory of the current module (getPackageVersion.js)
|
||||||
|
const currentModuleFilename = fileURLToPath(import.meta.url);
|
||||||
|
const currentModuleDirname = path.dirname(currentModuleFilename);
|
||||||
|
// Construct the path to package.json relative to this file (../../package.json)
|
||||||
|
const packageJsonPath = path.join(
|
||||||
|
currentModuleDirname,
|
||||||
|
'..',
|
||||||
|
'..',
|
||||||
|
'package.json'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (fs.existsSync(packageJsonPath)) {
|
||||||
|
const packageJsonContent = fs.readFileSync(packageJsonPath, 'utf8');
|
||||||
|
const packageJson = JSON.parse(packageJsonContent);
|
||||||
|
version = packageJson.version;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Silently fall back to default version
|
||||||
|
log('warn', 'Could not read own package.json for version info.', error);
|
||||||
|
}
|
||||||
|
return version;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,15 +12,7 @@
|
|||||||
"strict": true,
|
"strict": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"baseUrl": ".",
|
"baseUrl": "."
|
||||||
"paths": {
|
|
||||||
"@tm/core": ["./packages/tm-core/src/index.ts"],
|
|
||||||
"@tm/core/*": ["./packages/tm-core/src/*"],
|
|
||||||
"@tm/cli": ["./apps/cli/src/index.ts"],
|
|
||||||
"@tm/cli/*": ["./apps/cli/src/*"],
|
|
||||||
"@tm/build-config": ["./packages/build-config/src/index.ts"],
|
|
||||||
"@tm/build-config/*": ["./packages/build-config/src/*"]
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"tsx": {
|
"tsx": {
|
||||||
"tsconfig": {
|
"tsconfig": {
|
||||||
|
|||||||
@@ -1,97 +1,26 @@
|
|||||||
import { defineConfig } from 'tsup';
|
import { defineConfig } from 'tsup';
|
||||||
import { baseConfig, mergeConfig } from '@tm/build-config';
|
import { baseConfig, mergeConfig } from '@tm/build-config';
|
||||||
import { load as dotenvLoad } from 'dotenv-mono';
|
|
||||||
import path from 'node:path';
|
|
||||||
import { fileURLToPath } from 'node:url';
|
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
||||||
|
|
||||||
dotenvLoad();
|
|
||||||
|
|
||||||
// Get all TM_PUBLIC_* env variables for build-time injection
|
|
||||||
const getBuildTimeEnvs = () => {
|
|
||||||
const envs: Record<string, string> = {};
|
|
||||||
for (const [key, value] of Object.entries(process.env)) {
|
|
||||||
if (key.startsWith('TM_PUBLIC_')) {
|
|
||||||
// Return the actual value, not JSON.stringify'd
|
|
||||||
envs[key] = value || '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return envs;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default defineConfig(
|
export default defineConfig(
|
||||||
mergeConfig(baseConfig, {
|
mergeConfig(baseConfig, {
|
||||||
entry: {
|
entry: {
|
||||||
'task-master': 'scripts/dev.js',
|
'task-master': 'bin/task-master.js',
|
||||||
'mcp-server': 'mcp-server/server.js'
|
'mcp-server': 'mcp-server/server.js'
|
||||||
},
|
},
|
||||||
outDir: 'dist',
|
outDir: 'dist',
|
||||||
publicDir: 'public',
|
publicDir: 'public',
|
||||||
// Override the base config's external to bundle our workspace packages
|
// Bundle our monorepo packages but keep node_modules external
|
||||||
noExternal: [/^@tm\//],
|
noExternal: [/@tm\/.*/],
|
||||||
external: [
|
// Ensure no code splitting
|
||||||
/^@supabase\//, // Keep Supabase external to avoid dynamic require issues
|
splitting: false,
|
||||||
'marked',
|
// Better watch configuration
|
||||||
'marked-terminal'
|
ignoreWatch: [
|
||||||
],
|
'dist',
|
||||||
env: getBuildTimeEnvs(),
|
'node_modules',
|
||||||
esbuildOptions(options) {
|
'.git',
|
||||||
// Set up path aliases for workspace packages
|
'tests',
|
||||||
options.alias = {
|
'*.test.*',
|
||||||
'@tm/core': path.resolve(__dirname, 'packages/tm-core/src/index.ts'),
|
'*.spec.*'
|
||||||
'@tm/core/auth': path.resolve(
|
]
|
||||||
__dirname,
|
|
||||||
'packages/tm-core/src/auth/index.ts'
|
|
||||||
),
|
|
||||||
'@tm/core/storage': path.resolve(
|
|
||||||
__dirname,
|
|
||||||
'packages/tm-core/src/storage/index.ts'
|
|
||||||
),
|
|
||||||
'@tm/core/config': path.resolve(
|
|
||||||
__dirname,
|
|
||||||
'packages/tm-core/src/config/index.ts'
|
|
||||||
),
|
|
||||||
'@tm/core/providers': path.resolve(
|
|
||||||
__dirname,
|
|
||||||
'packages/tm-core/src/providers/index.ts'
|
|
||||||
),
|
|
||||||
'@tm/core/services': path.resolve(
|
|
||||||
__dirname,
|
|
||||||
'packages/tm-core/src/services/index.ts'
|
|
||||||
),
|
|
||||||
'@tm/core/errors': path.resolve(
|
|
||||||
__dirname,
|
|
||||||
'packages/tm-core/src/errors/index.ts'
|
|
||||||
),
|
|
||||||
'@tm/core/logger': path.resolve(
|
|
||||||
__dirname,
|
|
||||||
'packages/tm-core/src/logger/index.ts'
|
|
||||||
),
|
|
||||||
'@tm/core/types': path.resolve(
|
|
||||||
__dirname,
|
|
||||||
'packages/tm-core/src/types/index.ts'
|
|
||||||
),
|
|
||||||
'@tm/core/interfaces': path.resolve(
|
|
||||||
__dirname,
|
|
||||||
'packages/tm-core/src/interfaces/index.ts'
|
|
||||||
),
|
|
||||||
'@tm/core/utils': path.resolve(
|
|
||||||
__dirname,
|
|
||||||
'packages/tm-core/src/utils/index.ts'
|
|
||||||
),
|
|
||||||
'@tm/cli': path.resolve(__dirname, 'apps/cli/src/index.ts'),
|
|
||||||
'@tm/cli/commands': path.resolve(
|
|
||||||
__dirname,
|
|
||||||
'apps/cli/src/commands/index.ts'
|
|
||||||
),
|
|
||||||
'@tm/cli/utils': path.resolve(__dirname, 'apps/cli/src/utils/index.ts'),
|
|
||||||
'@tm/cli/ui': path.resolve(__dirname, 'apps/cli/src/ui/index.ts'),
|
|
||||||
'@tm/build-config': path.resolve(
|
|
||||||
__dirname,
|
|
||||||
'packages/build-config/src/tsup.base.ts'
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
10
turbo.json
10
turbo.json
@@ -10,12 +10,22 @@
|
|||||||
"dev": {
|
"dev": {
|
||||||
"cache": false,
|
"cache": false,
|
||||||
"persistent": true,
|
"persistent": true,
|
||||||
|
"dependsOn": ["^build"],
|
||||||
"inputs": [
|
"inputs": [
|
||||||
"$TURBO_DEFAULT$",
|
"$TURBO_DEFAULT$",
|
||||||
"!{packages,apps}/**/dist/**",
|
"!{packages,apps}/**/dist/**",
|
||||||
"!{packages,apps}/**/node_modules/**"
|
"!{packages,apps}/**/node_modules/**"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"test": {
|
||||||
|
"dependsOn": ["^build"],
|
||||||
|
"inputs": [
|
||||||
|
"$TURBO_DEFAULT$",
|
||||||
|
"!{packages,apps}/**/dist/**",
|
||||||
|
"!{packages,apps}/**/node_modules/**"
|
||||||
|
],
|
||||||
|
"outputLogs": "new-only"
|
||||||
|
},
|
||||||
"test:watch": {
|
"test:watch": {
|
||||||
"cache": false,
|
"cache": false,
|
||||||
"persistent": true,
|
"persistent": true,
|
||||||
|
|||||||
Reference in New Issue
Block a user