refactor: enhance add-task fuzzy search and fix duplicate banner display
- **Remove hardcoded category system** in add-task that always matched 'Task management' - **Eliminate arbitrary limits** in fuzzy search results (5→25 high relevance, 3→10 medium relevance, 8→20 detailed tasks) - **Improve semantic weighting** in Fuse.js search (details=3, description=2, title=1.5) for better relevance - **Fix duplicate banner issue** by removing console.clear() and redundant displayBanner() calls from UI functions - **Enhance context generation** to rely on semantic similarity rather than rigid pattern matching - **Preserve terminal history** to address GitHub issue #553 about eating terminal lines - **Remove displayBanner() calls** from: displayHelp, displayNextTask, displayTaskById, displayComplexityReport, set-task-status, clear-subtasks, dependency-manager functions The add-task system now provides truly relevant task context based on semantic similarity rather than arbitrary categories and limits, while maintaining a cleaner terminal experience. Changes span: add-task.js, ui.js, set-task-status.js, clear-subtasks.js, list-tasks.js, dependency-manager.js Closes #553
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"models": {
|
"models": {
|
||||||
"main": {
|
"main": {
|
||||||
"provider": "openrouter",
|
"provider": "anthropic",
|
||||||
"modelId": "qwen/qwen3-235b-a22b:free",
|
"modelId": "claude-sonnet-4-20250514",
|
||||||
"maxTokens": 50000,
|
"maxTokens": 50000,
|
||||||
"temperature": 0.2
|
"temperature": 0.2
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
# Task ID: 97
|
|
||||||
# Title: Create Taskmaster Jingle Implementation
|
|
||||||
# Status: pending
|
|
||||||
# Dependencies: 95, 57, 3, 2
|
|
||||||
# Priority: medium
|
|
||||||
# Description: Develop a musical jingle system for Taskmaster that plays sound effects during key CLI interactions to enhance user experience.
|
|
||||||
# Details:
|
|
||||||
This task involves implementing a sound system that plays audio cues during Taskmaster CLI operations. Key implementation steps include:
|
|
||||||
|
|
||||||
1. Audio System Integration:
|
|
||||||
- Research and select appropriate audio library compatible with Node.js CLI applications
|
|
||||||
- Implement cross-platform audio playback (Windows, macOS, Linux)
|
|
||||||
- Create sound configuration options in .taskmasterconfig
|
|
||||||
|
|
||||||
2. Jingle Design:
|
|
||||||
- Define sound triggers for key events (task creation, completion, errors, etc.)
|
|
||||||
- Create or source appropriate sound files (WAV/MP3 format)
|
|
||||||
- Implement volume control and mute option in settings
|
|
||||||
|
|
||||||
3. CLI Integration:
|
|
||||||
- Add sound playback to core CLI commands (init, create, update, delete)
|
|
||||||
- Implement optional sound effects toggle via command line flags
|
|
||||||
- Ensure audio playback doesn't interfere with CLI performance
|
|
||||||
|
|
||||||
4. Documentation:
|
|
||||||
- Update user guide with sound configuration instructions
|
|
||||||
- Add troubleshooting section for audio playback issues
|
|
||||||
|
|
||||||
# Test Strategy:
|
|
||||||
1. Verify audio plays correctly during each supported CLI operation
|
|
||||||
2. Test sound configuration options across different platforms
|
|
||||||
3. Confirm volume control and mute functionality works as expected
|
|
||||||
4. Validate that audio playback doesn't affect CLI performance
|
|
||||||
5. Test edge cases (no audio hardware, invalid sound files, etc.)
|
|
||||||
6. Ensure sound effects can be disabled via configuration and CLI flags
|
|
||||||
@@ -5871,22 +5871,6 @@
|
|||||||
"parentTaskId": 96
|
"parentTaskId": 96
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 97,
|
|
||||||
"title": "Create Taskmaster Jingle Implementation",
|
|
||||||
"description": "Develop a musical jingle system for Taskmaster that plays sound effects during key CLI interactions to enhance user experience.",
|
|
||||||
"details": "This task involves implementing a sound system that plays audio cues during Taskmaster CLI operations. Key implementation steps include:\n\n1. Audio System Integration:\n - Research and select appropriate audio library compatible with Node.js CLI applications\n - Implement cross-platform audio playback (Windows, macOS, Linux)\n - Create sound configuration options in .taskmasterconfig\n\n2. Jingle Design:\n - Define sound triggers for key events (task creation, completion, errors, etc.)\n - Create or source appropriate sound files (WAV/MP3 format)\n - Implement volume control and mute option in settings\n\n3. CLI Integration:\n - Add sound playback to core CLI commands (init, create, update, delete)\n - Implement optional sound effects toggle via command line flags\n - Ensure audio playback doesn't interfere with CLI performance\n\n4. Documentation:\n - Update user guide with sound configuration instructions\n - Add troubleshooting section for audio playback issues",
|
|
||||||
"testStrategy": "1. Verify audio plays correctly during each supported CLI operation\n2. Test sound configuration options across different platforms\n3. Confirm volume control and mute functionality works as expected\n4. Validate that audio playback doesn't affect CLI performance\n5. Test edge cases (no audio hardware, invalid sound files, etc.)\n6. Ensure sound effects can be disabled via configuration and CLI flags",
|
|
||||||
"status": "pending",
|
|
||||||
"dependencies": [
|
|
||||||
95,
|
|
||||||
57,
|
|
||||||
3,
|
|
||||||
2
|
|
||||||
],
|
|
||||||
"priority": "medium",
|
|
||||||
"subtasks": []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,11 @@
|
|||||||
import path from 'path';
|
import path from "path";
|
||||||
import chalk from 'chalk';
|
import chalk from "chalk";
|
||||||
import boxen from 'boxen';
|
import boxen from "boxen";
|
||||||
import Table from 'cli-table3';
|
import Table from "cli-table3";
|
||||||
|
|
||||||
import { log, readJSON, writeJSON, truncate, isSilentMode } from '../utils.js';
|
import { log, readJSON, writeJSON, truncate, isSilentMode } from "../utils.js";
|
||||||
import { displayBanner } from '../ui.js';
|
import { displayBanner } from "../ui.js";
|
||||||
import generateTaskFiles from './generate-task-files.js';
|
import generateTaskFiles from "./generate-task-files.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear subtasks from specified tasks
|
* Clear subtasks from specified tasks
|
||||||
@@ -13,140 +13,138 @@ import generateTaskFiles from './generate-task-files.js';
|
|||||||
* @param {string} taskIds - Task IDs to clear subtasks from
|
* @param {string} taskIds - Task IDs to clear subtasks from
|
||||||
*/
|
*/
|
||||||
function clearSubtasks(tasksPath, taskIds) {
|
function clearSubtasks(tasksPath, taskIds) {
|
||||||
displayBanner();
|
log("info", `Reading tasks from ${tasksPath}...`);
|
||||||
|
const data = readJSON(tasksPath);
|
||||||
|
if (!data || !data.tasks) {
|
||||||
|
log("error", "No valid tasks found.");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
log('info', `Reading tasks from ${tasksPath}...`);
|
if (!isSilentMode()) {
|
||||||
const data = readJSON(tasksPath);
|
console.log(
|
||||||
if (!data || !data.tasks) {
|
boxen(chalk.white.bold("Clearing Subtasks"), {
|
||||||
log('error', 'No valid tasks found.');
|
padding: 1,
|
||||||
process.exit(1);
|
borderColor: "blue",
|
||||||
}
|
borderStyle: "round",
|
||||||
|
margin: { top: 1, bottom: 1 },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (!isSilentMode()) {
|
// Handle multiple task IDs (comma-separated)
|
||||||
console.log(
|
const taskIdArray = taskIds.split(",").map((id) => id.trim());
|
||||||
boxen(chalk.white.bold('Clearing Subtasks'), {
|
let clearedCount = 0;
|
||||||
padding: 1,
|
|
||||||
borderColor: 'blue',
|
|
||||||
borderStyle: 'round',
|
|
||||||
margin: { top: 1, bottom: 1 }
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle multiple task IDs (comma-separated)
|
// Create a summary table for the cleared subtasks
|
||||||
const taskIdArray = taskIds.split(',').map((id) => id.trim());
|
const summaryTable = new Table({
|
||||||
let clearedCount = 0;
|
head: [
|
||||||
|
chalk.cyan.bold("Task ID"),
|
||||||
|
chalk.cyan.bold("Task Title"),
|
||||||
|
chalk.cyan.bold("Subtasks Cleared"),
|
||||||
|
],
|
||||||
|
colWidths: [10, 50, 20],
|
||||||
|
style: { head: [], border: [] },
|
||||||
|
});
|
||||||
|
|
||||||
// Create a summary table for the cleared subtasks
|
taskIdArray.forEach((taskId) => {
|
||||||
const summaryTable = new Table({
|
const id = parseInt(taskId, 10);
|
||||||
head: [
|
if (isNaN(id)) {
|
||||||
chalk.cyan.bold('Task ID'),
|
log("error", `Invalid task ID: ${taskId}`);
|
||||||
chalk.cyan.bold('Task Title'),
|
return;
|
||||||
chalk.cyan.bold('Subtasks Cleared')
|
}
|
||||||
],
|
|
||||||
colWidths: [10, 50, 20],
|
|
||||||
style: { head: [], border: [] }
|
|
||||||
});
|
|
||||||
|
|
||||||
taskIdArray.forEach((taskId) => {
|
const task = data.tasks.find((t) => t.id === id);
|
||||||
const id = parseInt(taskId, 10);
|
if (!task) {
|
||||||
if (isNaN(id)) {
|
log("error", `Task ${id} not found`);
|
||||||
log('error', `Invalid task ID: ${taskId}`);
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const task = data.tasks.find((t) => t.id === id);
|
if (!task.subtasks || task.subtasks.length === 0) {
|
||||||
if (!task) {
|
log("info", `Task ${id} has no subtasks to clear`);
|
||||||
log('error', `Task ${id} not found`);
|
summaryTable.push([
|
||||||
return;
|
id.toString(),
|
||||||
}
|
truncate(task.title, 47),
|
||||||
|
chalk.yellow("No subtasks"),
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!task.subtasks || task.subtasks.length === 0) {
|
const subtaskCount = task.subtasks.length;
|
||||||
log('info', `Task ${id} has no subtasks to clear`);
|
task.subtasks = [];
|
||||||
summaryTable.push([
|
clearedCount++;
|
||||||
id.toString(),
|
log("info", `Cleared ${subtaskCount} subtasks from task ${id}`);
|
||||||
truncate(task.title, 47),
|
|
||||||
chalk.yellow('No subtasks')
|
|
||||||
]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const subtaskCount = task.subtasks.length;
|
summaryTable.push([
|
||||||
task.subtasks = [];
|
id.toString(),
|
||||||
clearedCount++;
|
truncate(task.title, 47),
|
||||||
log('info', `Cleared ${subtaskCount} subtasks from task ${id}`);
|
chalk.green(`${subtaskCount} subtasks cleared`),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
summaryTable.push([
|
if (clearedCount > 0) {
|
||||||
id.toString(),
|
writeJSON(tasksPath, data);
|
||||||
truncate(task.title, 47),
|
|
||||||
chalk.green(`${subtaskCount} subtasks cleared`)
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (clearedCount > 0) {
|
// Show summary table
|
||||||
writeJSON(tasksPath, data);
|
if (!isSilentMode()) {
|
||||||
|
console.log(
|
||||||
|
boxen(chalk.white.bold("Subtask Clearing Summary:"), {
|
||||||
|
padding: { left: 2, right: 2, top: 0, bottom: 0 },
|
||||||
|
margin: { top: 1, bottom: 0 },
|
||||||
|
borderColor: "blue",
|
||||||
|
borderStyle: "round",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
console.log(summaryTable.toString());
|
||||||
|
}
|
||||||
|
|
||||||
// Show summary table
|
// Regenerate task files to reflect changes
|
||||||
if (!isSilentMode()) {
|
log("info", "Regenerating task files...");
|
||||||
console.log(
|
generateTaskFiles(tasksPath, path.dirname(tasksPath));
|
||||||
boxen(chalk.white.bold('Subtask Clearing Summary:'), {
|
|
||||||
padding: { left: 2, right: 2, top: 0, bottom: 0 },
|
|
||||||
margin: { top: 1, bottom: 0 },
|
|
||||||
borderColor: 'blue',
|
|
||||||
borderStyle: 'round'
|
|
||||||
})
|
|
||||||
);
|
|
||||||
console.log(summaryTable.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Regenerate task files to reflect changes
|
// Success message
|
||||||
log('info', 'Regenerating task files...');
|
if (!isSilentMode()) {
|
||||||
generateTaskFiles(tasksPath, path.dirname(tasksPath));
|
console.log(
|
||||||
|
boxen(
|
||||||
|
chalk.green(
|
||||||
|
`Successfully cleared subtasks from ${chalk.bold(clearedCount)} task(s)`
|
||||||
|
),
|
||||||
|
{
|
||||||
|
padding: 1,
|
||||||
|
borderColor: "green",
|
||||||
|
borderStyle: "round",
|
||||||
|
margin: { top: 1 },
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
// Success message
|
// Next steps suggestion
|
||||||
if (!isSilentMode()) {
|
console.log(
|
||||||
console.log(
|
boxen(
|
||||||
boxen(
|
chalk.white.bold("Next Steps:") +
|
||||||
chalk.green(
|
"\n\n" +
|
||||||
`Successfully cleared subtasks from ${chalk.bold(clearedCount)} task(s)`
|
`${chalk.cyan("1.")} Run ${chalk.yellow("task-master expand --id=<id>")} to generate new subtasks\n` +
|
||||||
),
|
`${chalk.cyan("2.")} Run ${chalk.yellow("task-master list --with-subtasks")} to verify changes`,
|
||||||
{
|
{
|
||||||
padding: 1,
|
padding: 1,
|
||||||
borderColor: 'green',
|
borderColor: "cyan",
|
||||||
borderStyle: 'round',
|
borderStyle: "round",
|
||||||
margin: { top: 1 }
|
margin: { top: 1 },
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
// Next steps suggestion
|
} else {
|
||||||
console.log(
|
if (!isSilentMode()) {
|
||||||
boxen(
|
console.log(
|
||||||
chalk.white.bold('Next Steps:') +
|
boxen(chalk.yellow("No subtasks were cleared"), {
|
||||||
'\n\n' +
|
padding: 1,
|
||||||
`${chalk.cyan('1.')} Run ${chalk.yellow('task-master expand --id=<id>')} to generate new subtasks\n` +
|
borderColor: "yellow",
|
||||||
`${chalk.cyan('2.')} Run ${chalk.yellow('task-master list --with-subtasks')} to verify changes`,
|
borderStyle: "round",
|
||||||
{
|
margin: { top: 1 },
|
||||||
padding: 1,
|
})
|
||||||
borderColor: 'cyan',
|
);
|
||||||
borderStyle: 'round',
|
}
|
||||||
margin: { top: 1 }
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!isSilentMode()) {
|
|
||||||
console.log(
|
|
||||||
boxen(chalk.yellow('No subtasks were cleared'), {
|
|
||||||
padding: 1,
|
|
||||||
borderColor: 'yellow',
|
|
||||||
borderStyle: 'round',
|
|
||||||
margin: { top: 1 }
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default clearSubtasks;
|
export default clearSubtasks;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,17 +1,17 @@
|
|||||||
import path from 'path';
|
import path from "path";
|
||||||
import chalk from 'chalk';
|
import chalk from "chalk";
|
||||||
import boxen from 'boxen';
|
import boxen from "boxen";
|
||||||
|
|
||||||
import { log, readJSON, writeJSON, findTaskById } from '../utils.js';
|
import { log, readJSON, writeJSON, findTaskById } from "../utils.js";
|
||||||
import { displayBanner } from '../ui.js';
|
import { displayBanner } from "../ui.js";
|
||||||
import { validateTaskDependencies } from '../dependency-manager.js';
|
import { validateTaskDependencies } from "../dependency-manager.js";
|
||||||
import { getDebugFlag } from '../config-manager.js';
|
import { getDebugFlag } from "../config-manager.js";
|
||||||
import updateSingleTaskStatus from './update-single-task-status.js';
|
import updateSingleTaskStatus from "./update-single-task-status.js";
|
||||||
import generateTaskFiles from './generate-task-files.js';
|
import generateTaskFiles from "./generate-task-files.js";
|
||||||
import {
|
import {
|
||||||
isValidTaskStatus,
|
isValidTaskStatus,
|
||||||
TASK_STATUS_OPTIONS
|
TASK_STATUS_OPTIONS,
|
||||||
} from '../../../src/constants/task-status.js';
|
} from "../../../src/constants/task-status.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the status of a task
|
* Set the status of a task
|
||||||
@@ -22,102 +22,100 @@ import {
|
|||||||
* @returns {Object|undefined} Result object in MCP mode, undefined in CLI mode
|
* @returns {Object|undefined} Result object in MCP mode, undefined in CLI mode
|
||||||
*/
|
*/
|
||||||
async function setTaskStatus(tasksPath, taskIdInput, newStatus, options = {}) {
|
async function setTaskStatus(tasksPath, taskIdInput, newStatus, options = {}) {
|
||||||
try {
|
try {
|
||||||
if (!isValidTaskStatus(newStatus)) {
|
if (!isValidTaskStatus(newStatus)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Error: Invalid status value: ${newStatus}. Use one of: ${TASK_STATUS_OPTIONS.join(', ')}`
|
`Error: Invalid status value: ${newStatus}. Use one of: ${TASK_STATUS_OPTIONS.join(", ")}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// Determine if we're in MCP mode by checking for mcpLog
|
// Determine if we're in MCP mode by checking for mcpLog
|
||||||
const isMcpMode = !!options?.mcpLog;
|
const isMcpMode = !!options?.mcpLog;
|
||||||
|
|
||||||
// Only display UI elements if not in MCP mode
|
// Only display UI elements if not in MCP mode
|
||||||
if (!isMcpMode) {
|
if (!isMcpMode) {
|
||||||
displayBanner();
|
console.log(
|
||||||
|
boxen(chalk.white.bold(`Updating Task Status to: ${newStatus}`), {
|
||||||
|
padding: 1,
|
||||||
|
borderColor: "blue",
|
||||||
|
borderStyle: "round",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
console.log(
|
log("info", `Reading tasks from ${tasksPath}...`);
|
||||||
boxen(chalk.white.bold(`Updating Task Status to: ${newStatus}`), {
|
const data = readJSON(tasksPath);
|
||||||
padding: 1,
|
if (!data || !data.tasks) {
|
||||||
borderColor: 'blue',
|
throw new Error(`No valid tasks found in ${tasksPath}`);
|
||||||
borderStyle: 'round'
|
}
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
log('info', `Reading tasks from ${tasksPath}...`);
|
// Handle multiple task IDs (comma-separated)
|
||||||
const data = readJSON(tasksPath);
|
const taskIds = taskIdInput.split(",").map((id) => id.trim());
|
||||||
if (!data || !data.tasks) {
|
const updatedTasks = [];
|
||||||
throw new Error(`No valid tasks found in ${tasksPath}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle multiple task IDs (comma-separated)
|
// Update each task
|
||||||
const taskIds = taskIdInput.split(',').map((id) => id.trim());
|
for (const id of taskIds) {
|
||||||
const updatedTasks = [];
|
await updateSingleTaskStatus(tasksPath, id, newStatus, data, !isMcpMode);
|
||||||
|
updatedTasks.push(id);
|
||||||
|
}
|
||||||
|
|
||||||
// Update each task
|
// Write the updated tasks to the file
|
||||||
for (const id of taskIds) {
|
writeJSON(tasksPath, data);
|
||||||
await updateSingleTaskStatus(tasksPath, id, newStatus, data, !isMcpMode);
|
|
||||||
updatedTasks.push(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write the updated tasks to the file
|
// Validate dependencies after status update
|
||||||
writeJSON(tasksPath, data);
|
log("info", "Validating dependencies after status update...");
|
||||||
|
validateTaskDependencies(data.tasks);
|
||||||
|
|
||||||
// Validate dependencies after status update
|
// Generate individual task files
|
||||||
log('info', 'Validating dependencies after status update...');
|
log("info", "Regenerating task files...");
|
||||||
validateTaskDependencies(data.tasks);
|
await generateTaskFiles(tasksPath, path.dirname(tasksPath), {
|
||||||
|
mcpLog: options.mcpLog,
|
||||||
|
});
|
||||||
|
|
||||||
// Generate individual task files
|
// Display success message - only in CLI mode
|
||||||
log('info', 'Regenerating task files...');
|
if (!isMcpMode) {
|
||||||
await generateTaskFiles(tasksPath, path.dirname(tasksPath), {
|
for (const id of updatedTasks) {
|
||||||
mcpLog: options.mcpLog
|
const task = findTaskById(data.tasks, id);
|
||||||
});
|
const taskName = task ? task.title : id;
|
||||||
|
|
||||||
// Display success message - only in CLI mode
|
console.log(
|
||||||
if (!isMcpMode) {
|
boxen(
|
||||||
for (const id of updatedTasks) {
|
chalk.white.bold(`Successfully updated task ${id} status:`) +
|
||||||
const task = findTaskById(data.tasks, id);
|
"\n" +
|
||||||
const taskName = task ? task.title : id;
|
`From: ${chalk.yellow(task ? task.status : "unknown")}\n` +
|
||||||
|
`To: ${chalk.green(newStatus)}`,
|
||||||
|
{ padding: 1, borderColor: "green", borderStyle: "round" }
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
console.log(
|
// Return success value for programmatic use
|
||||||
boxen(
|
return {
|
||||||
chalk.white.bold(`Successfully updated task ${id} status:`) +
|
success: true,
|
||||||
'\n' +
|
updatedTasks: updatedTasks.map((id) => ({
|
||||||
`From: ${chalk.yellow(task ? task.status : 'unknown')}\n` +
|
id,
|
||||||
`To: ${chalk.green(newStatus)}`,
|
status: newStatus,
|
||||||
{ padding: 1, borderColor: 'green', borderStyle: 'round' }
|
})),
|
||||||
)
|
};
|
||||||
);
|
} catch (error) {
|
||||||
}
|
log("error", `Error setting task status: ${error.message}`);
|
||||||
}
|
|
||||||
|
|
||||||
// Return success value for programmatic use
|
// Only show error UI in CLI mode
|
||||||
return {
|
if (!options?.mcpLog) {
|
||||||
success: true,
|
console.error(chalk.red(`Error: ${error.message}`));
|
||||||
updatedTasks: updatedTasks.map((id) => ({
|
|
||||||
id,
|
|
||||||
status: newStatus
|
|
||||||
}))
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
log('error', `Error setting task status: ${error.message}`);
|
|
||||||
|
|
||||||
// Only show error UI in CLI mode
|
// Pass session to getDebugFlag
|
||||||
if (!options?.mcpLog) {
|
if (getDebugFlag(options?.session)) {
|
||||||
console.error(chalk.red(`Error: ${error.message}`));
|
// Use getter
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
// Pass session to getDebugFlag
|
process.exit(1);
|
||||||
if (getDebugFlag(options?.session)) {
|
} else {
|
||||||
// Use getter
|
// In MCP mode, throw the error for the caller to handle
|
||||||
console.error(error);
|
throw error;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
process.exit(1);
|
|
||||||
} else {
|
|
||||||
// In MCP mode, throw the error for the caller to handle
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default setTaskStatus;
|
export default setTaskStatus;
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ const warmGradient = gradient(['#fb8b24', '#e36414', '#9a031e']);
|
|||||||
function displayBanner() {
|
function displayBanner() {
|
||||||
if (isSilentMode()) return;
|
if (isSilentMode()) return;
|
||||||
|
|
||||||
console.clear();
|
// console.clear(); // Removing this to avoid clearing the terminal per command
|
||||||
const bannerText = figlet.textSync('Task Master', {
|
const bannerText = figlet.textSync('Task Master', {
|
||||||
font: 'Standard',
|
font: 'Standard',
|
||||||
horizontalLayout: 'default',
|
horizontalLayout: 'default',
|
||||||
@@ -78,6 +78,8 @@ function displayBanner() {
|
|||||||
* @returns {Object} Spinner object
|
* @returns {Object} Spinner object
|
||||||
*/
|
*/
|
||||||
function startLoadingIndicator(message) {
|
function startLoadingIndicator(message) {
|
||||||
|
if (isSilentMode()) return null;
|
||||||
|
|
||||||
const spinner = ora({
|
const spinner = ora({
|
||||||
text: message,
|
text: message,
|
||||||
color: 'cyan'
|
color: 'cyan'
|
||||||
@@ -87,15 +89,75 @@ function startLoadingIndicator(message) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop a loading indicator
|
* Stop a loading indicator (basic stop, no success/fail indicator)
|
||||||
* @param {Object} spinner - Spinner object to stop
|
* @param {Object} spinner - Spinner object to stop
|
||||||
*/
|
*/
|
||||||
function stopLoadingIndicator(spinner) {
|
function stopLoadingIndicator(spinner) {
|
||||||
if (spinner && spinner.stop) {
|
if (spinner && typeof spinner.stop === 'function') {
|
||||||
spinner.stop();
|
spinner.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complete a loading indicator with success (shows checkmark)
|
||||||
|
* @param {Object} spinner - Spinner object to complete
|
||||||
|
* @param {string} message - Optional success message (defaults to current text)
|
||||||
|
*/
|
||||||
|
function succeedLoadingIndicator(spinner, message = null) {
|
||||||
|
if (spinner && typeof spinner.succeed === 'function') {
|
||||||
|
if (message) {
|
||||||
|
spinner.succeed(message);
|
||||||
|
} else {
|
||||||
|
spinner.succeed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complete a loading indicator with failure (shows X)
|
||||||
|
* @param {Object} spinner - Spinner object to fail
|
||||||
|
* @param {string} message - Optional failure message (defaults to current text)
|
||||||
|
*/
|
||||||
|
function failLoadingIndicator(spinner, message = null) {
|
||||||
|
if (spinner && typeof spinner.fail === 'function') {
|
||||||
|
if (message) {
|
||||||
|
spinner.fail(message);
|
||||||
|
} else {
|
||||||
|
spinner.fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complete a loading indicator with warning (shows warning symbol)
|
||||||
|
* @param {Object} spinner - Spinner object to warn
|
||||||
|
* @param {string} message - Optional warning message (defaults to current text)
|
||||||
|
*/
|
||||||
|
function warnLoadingIndicator(spinner, message = null) {
|
||||||
|
if (spinner && typeof spinner.warn === 'function') {
|
||||||
|
if (message) {
|
||||||
|
spinner.warn(message);
|
||||||
|
} else {
|
||||||
|
spinner.warn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complete a loading indicator with info (shows info symbol)
|
||||||
|
* @param {Object} spinner - Spinner object to complete with info
|
||||||
|
* @param {string} message - Optional info message (defaults to current text)
|
||||||
|
*/
|
||||||
|
function infoLoadingIndicator(spinner, message = null) {
|
||||||
|
if (spinner && typeof spinner.info === 'function') {
|
||||||
|
if (message) {
|
||||||
|
spinner.info(message);
|
||||||
|
} else {
|
||||||
|
spinner.info();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a colored progress bar
|
* Create a colored progress bar
|
||||||
* @param {number} percent - The completion percentage
|
* @param {number} percent - The completion percentage
|
||||||
@@ -232,14 +294,14 @@ function getStatusWithColor(status, forTable = false) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const statusConfig = {
|
const statusConfig = {
|
||||||
done: { color: chalk.green, icon: '✅', tableIcon: '✓' },
|
done: { color: chalk.green, icon: '✓', tableIcon: '✓' },
|
||||||
completed: { color: chalk.green, icon: '✅', tableIcon: '✓' },
|
completed: { color: chalk.green, icon: '✓', tableIcon: '✓' },
|
||||||
pending: { color: chalk.yellow, icon: '⏱️', tableIcon: '⏱' },
|
pending: { color: chalk.yellow, icon: '○', tableIcon: '⏱' },
|
||||||
'in-progress': { color: chalk.hex('#FFA500'), icon: '🔄', tableIcon: '►' },
|
'in-progress': { color: chalk.hex('#FFA500'), icon: '🔄', tableIcon: '►' },
|
||||||
deferred: { color: chalk.gray, icon: '⏱️', tableIcon: '⏱' },
|
deferred: { color: chalk.gray, icon: 'x', tableIcon: '⏱' },
|
||||||
blocked: { color: chalk.red, icon: '❌', tableIcon: '✗' },
|
blocked: { color: chalk.red, icon: '!', tableIcon: '✗' },
|
||||||
review: { color: chalk.magenta, icon: '👀', tableIcon: '👁' },
|
review: { color: chalk.magenta, icon: '?', tableIcon: '?' },
|
||||||
cancelled: { color: chalk.gray, icon: '❌', tableIcon: '✗' }
|
cancelled: { color: chalk.gray, icon: '❌', tableIcon: 'x' }
|
||||||
};
|
};
|
||||||
|
|
||||||
const config = statusConfig[status.toLowerCase()] || {
|
const config = statusConfig[status.toLowerCase()] || {
|
||||||
@@ -383,8 +445,6 @@ function formatDependenciesWithStatus(
|
|||||||
* Display a comprehensive help guide
|
* Display a comprehensive help guide
|
||||||
*/
|
*/
|
||||||
function displayHelp() {
|
function displayHelp() {
|
||||||
displayBanner();
|
|
||||||
|
|
||||||
// Get terminal width - moved to top of function to make it available throughout
|
// Get terminal width - moved to top of function to make it available throughout
|
||||||
const terminalWidth = process.stdout.columns || 100; // Default to 100 if can't detect
|
const terminalWidth = process.stdout.columns || 100; // Default to 100 if can't detect
|
||||||
|
|
||||||
@@ -772,8 +832,6 @@ function truncateString(str, maxLength) {
|
|||||||
* @param {string} tasksPath - Path to the tasks.json file
|
* @param {string} tasksPath - Path to the tasks.json file
|
||||||
*/
|
*/
|
||||||
async function displayNextTask(tasksPath, complexityReportPath = null) {
|
async function displayNextTask(tasksPath, complexityReportPath = null) {
|
||||||
displayBanner();
|
|
||||||
|
|
||||||
// Read the tasks file
|
// Read the tasks file
|
||||||
const data = readJSON(tasksPath);
|
const data = readJSON(tasksPath);
|
||||||
if (!data || !data.tasks) {
|
if (!data || !data.tasks) {
|
||||||
@@ -1044,8 +1102,6 @@ async function displayTaskById(
|
|||||||
complexityReportPath = null,
|
complexityReportPath = null,
|
||||||
statusFilter = null
|
statusFilter = null
|
||||||
) {
|
) {
|
||||||
displayBanner();
|
|
||||||
|
|
||||||
// Read the tasks file
|
// Read the tasks file
|
||||||
const data = readJSON(tasksPath);
|
const data = readJSON(tasksPath);
|
||||||
if (!data || !data.tasks) {
|
if (!data || !data.tasks) {
|
||||||
@@ -1500,8 +1556,6 @@ async function displayTaskById(
|
|||||||
* @param {string} reportPath - Path to the complexity report file
|
* @param {string} reportPath - Path to the complexity report file
|
||||||
*/
|
*/
|
||||||
async function displayComplexityReport(reportPath) {
|
async function displayComplexityReport(reportPath) {
|
||||||
displayBanner();
|
|
||||||
|
|
||||||
// Check if the report exists
|
// Check if the report exists
|
||||||
if (!fs.existsSync(reportPath)) {
|
if (!fs.existsSync(reportPath)) {
|
||||||
console.log(
|
console.log(
|
||||||
@@ -2472,5 +2526,8 @@ export {
|
|||||||
displayModelConfiguration,
|
displayModelConfiguration,
|
||||||
displayAvailableModels,
|
displayAvailableModels,
|
||||||
displayAiUsageSummary,
|
displayAiUsageSummary,
|
||||||
displayMultipleTasksSummary
|
succeedLoadingIndicator,
|
||||||
|
failLoadingIndicator,
|
||||||
|
warnLoadingIndicator,
|
||||||
|
infoLoadingIndicator
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user