mirror of
https://github.com/eyaltoledano/claude-task-master.git
synced 2025-07-12 11:30:51 +00:00

This commit implements AI usage telemetry for the `expand-all-tasks` command/tool and refactors its CLI output for clarity and consistency. Key Changes: 1. **Telemetry Integration for `expand-all-tasks` (Subtask 77.8):**\n - The `expandAllTasks` core logic (`scripts/modules/task-manager/expand-all-tasks.js`) now calls the `expandTask` function for each eligible task and collects the individual `telemetryData` returned.\n - A new helper function `_aggregateTelemetry` (in `utils.js`) is used to sum up token counts and costs from all individual expansions into a single `telemetryData` object for the entire `expand-all` operation.\n - The `expandAllTasksDirect` wrapper (`mcp-server/src/core/direct-functions/expand-all-tasks.js`) now receives and passes this aggregated `telemetryData` in the MCP response.\n - For CLI usage, `displayAiUsageSummary` is called once with the aggregated telemetry. 2. **Improved CLI Output for `expand-all`:**\n - The `expandAllTasks` core function now handles displaying a final "Expansion Summary" box (showing Attempted, Expanded, Skipped, Failed counts) directly after the aggregated telemetry summary.\n - This consolidates all summary output within the core function for better flow and removes redundant logging from the command action in `scripts/modules/commands.js`.\n - The summary box border is green for success and red if any expansions failed. 3. **Code Refinements:**\n - Ensured `chalk` and `boxen` are imported in `expand-all-tasks.js` for the new summary box.\n - Minor adjustments to logging messages for clarity.
209 lines
6.8 KiB
JavaScript
209 lines
6.8 KiB
JavaScript
import { log, readJSON, isSilentMode } from '../utils.js';
|
|
import {
|
|
startLoadingIndicator,
|
|
stopLoadingIndicator,
|
|
displayAiUsageSummary
|
|
} from '../ui.js';
|
|
import expandTask from './expand-task.js';
|
|
import { getDebugFlag } from '../config-manager.js';
|
|
import { _aggregateTelemetry } from '../utils.js';
|
|
import chalk from 'chalk';
|
|
import boxen from 'boxen';
|
|
|
|
/**
|
|
* Expand all eligible pending or in-progress tasks using the expandTask function.
|
|
* @param {string} tasksPath - Path to the tasks.json file
|
|
* @param {number} [numSubtasks] - Optional: Target number of subtasks per task.
|
|
* @param {boolean} [useResearch=false] - Whether to use the research AI role.
|
|
* @param {string} [additionalContext=''] - Optional additional context.
|
|
* @param {boolean} [force=false] - Force expansion even if tasks already have subtasks.
|
|
* @param {Object} context - Context object containing session and mcpLog.
|
|
* @param {Object} [context.session] - Session object from MCP.
|
|
* @param {Object} [context.mcpLog] - MCP logger object.
|
|
* @param {string} [outputFormat='text'] - Output format ('text' or 'json'). MCP calls should use 'json'.
|
|
* @returns {Promise<{success: boolean, expandedCount: number, failedCount: number, skippedCount: number, tasksToExpand: number, telemetryData: Array<Object>}>} - Result summary.
|
|
*/
|
|
async function expandAllTasks(
|
|
tasksPath,
|
|
numSubtasks, // Keep this signature, expandTask handles defaults
|
|
useResearch = false,
|
|
additionalContext = '',
|
|
force = false, // Keep force here for the filter logic
|
|
context = {},
|
|
outputFormat = 'text' // Assume text default for CLI
|
|
) {
|
|
const { session, mcpLog } = context;
|
|
const isMCPCall = !!mcpLog; // Determine if called from MCP
|
|
|
|
// Use mcpLog if available, otherwise use the default console log wrapper respecting silent mode
|
|
const logger =
|
|
mcpLog ||
|
|
(outputFormat === 'json'
|
|
? {
|
|
// Basic logger for JSON output mode
|
|
info: (msg) => {},
|
|
warn: (msg) => {},
|
|
error: (msg) => console.error(`ERROR: ${msg}`), // Still log errors
|
|
debug: (msg) => {}
|
|
}
|
|
: {
|
|
// CLI logger respecting silent mode
|
|
info: (msg) => !isSilentMode() && log('info', msg),
|
|
warn: (msg) => !isSilentMode() && log('warn', msg),
|
|
error: (msg) => !isSilentMode() && log('error', msg),
|
|
debug: (msg) =>
|
|
!isSilentMode() && getDebugFlag(session) && log('debug', msg)
|
|
});
|
|
|
|
let loadingIndicator = null;
|
|
let expandedCount = 0;
|
|
let failedCount = 0;
|
|
let tasksToExpandCount = 0;
|
|
const allTelemetryData = []; // Still collect individual data first
|
|
|
|
if (!isMCPCall && outputFormat === 'text') {
|
|
loadingIndicator = startLoadingIndicator(
|
|
'Analyzing tasks for expansion...'
|
|
);
|
|
}
|
|
|
|
try {
|
|
logger.info(`Reading tasks from ${tasksPath}`);
|
|
const data = readJSON(tasksPath);
|
|
if (!data || !data.tasks) {
|
|
throw new Error(`Invalid tasks data in ${tasksPath}`);
|
|
}
|
|
|
|
// --- Restore Original Filtering Logic ---
|
|
const tasksToExpand = data.tasks.filter(
|
|
(task) =>
|
|
(task.status === 'pending' || task.status === 'in-progress') && // Include 'in-progress'
|
|
(!task.subtasks || task.subtasks.length === 0 || force) // Check subtasks/force here
|
|
);
|
|
tasksToExpandCount = tasksToExpand.length; // Get the count from the filtered array
|
|
logger.info(`Found ${tasksToExpandCount} tasks eligible for expansion.`);
|
|
// --- End Restored Filtering Logic ---
|
|
|
|
if (loadingIndicator) {
|
|
stopLoadingIndicator(loadingIndicator, 'Analysis complete.');
|
|
}
|
|
|
|
if (tasksToExpandCount === 0) {
|
|
logger.info('No tasks eligible for expansion.');
|
|
// --- Fix: Restore success: true and add message ---
|
|
return {
|
|
success: true, // Indicate overall success despite no action
|
|
expandedCount: 0,
|
|
failedCount: 0,
|
|
skippedCount: 0,
|
|
tasksToExpand: 0,
|
|
telemetryData: allTelemetryData,
|
|
message: 'No tasks eligible for expansion.'
|
|
};
|
|
// --- End Fix ---
|
|
}
|
|
|
|
// Iterate over the already filtered tasks
|
|
for (const task of tasksToExpand) {
|
|
// Start indicator for individual task expansion in CLI mode
|
|
let taskIndicator = null;
|
|
if (!isMCPCall && outputFormat === 'text') {
|
|
taskIndicator = startLoadingIndicator(`Expanding task ${task.id}...`);
|
|
}
|
|
|
|
try {
|
|
// Call the refactored expandTask function AND capture result
|
|
const result = await expandTask(
|
|
tasksPath,
|
|
task.id,
|
|
numSubtasks,
|
|
useResearch,
|
|
additionalContext,
|
|
context, // Pass the whole context object { session, mcpLog }
|
|
force
|
|
);
|
|
expandedCount++;
|
|
|
|
// Collect individual telemetry data
|
|
if (result && result.telemetryData) {
|
|
allTelemetryData.push(result.telemetryData);
|
|
}
|
|
|
|
if (taskIndicator) {
|
|
stopLoadingIndicator(taskIndicator, `Task ${task.id} expanded.`);
|
|
}
|
|
logger.info(`Successfully expanded task ${task.id}.`);
|
|
} catch (error) {
|
|
failedCount++;
|
|
if (taskIndicator) {
|
|
stopLoadingIndicator(
|
|
taskIndicator,
|
|
`Failed to expand task ${task.id}.`,
|
|
false
|
|
);
|
|
}
|
|
logger.error(`Failed to expand task ${task.id}: ${error.message}`);
|
|
// Continue to the next task
|
|
}
|
|
}
|
|
|
|
// --- AGGREGATION AND DISPLAY ---
|
|
logger.info(
|
|
`Expansion complete: ${expandedCount} expanded, ${failedCount} failed.`
|
|
);
|
|
|
|
// Aggregate the collected telemetry data
|
|
const aggregatedTelemetryData = _aggregateTelemetry(
|
|
allTelemetryData,
|
|
'expand-all-tasks'
|
|
);
|
|
|
|
if (outputFormat === 'text') {
|
|
const summaryContent =
|
|
`${chalk.white.bold('Expansion Summary:')}\n\n` +
|
|
`${chalk.cyan('-')} Attempted: ${chalk.bold(tasksToExpandCount)}\n` +
|
|
`${chalk.green('-')} Expanded: ${chalk.bold(expandedCount)}\n` +
|
|
// Skipped count is always 0 now due to pre-filtering
|
|
`${chalk.gray('-')} Skipped: ${chalk.bold(0)}\n` +
|
|
`${chalk.red('-')} Failed: ${chalk.bold(failedCount)}`;
|
|
|
|
console.log(
|
|
boxen(summaryContent, {
|
|
padding: 1,
|
|
margin: { top: 1 },
|
|
borderColor: failedCount > 0 ? 'red' : 'green', // Red if failures, green otherwise
|
|
borderStyle: 'round'
|
|
})
|
|
);
|
|
}
|
|
|
|
if (outputFormat === 'text' && aggregatedTelemetryData) {
|
|
displayAiUsageSummary(aggregatedTelemetryData, 'cli');
|
|
}
|
|
|
|
// Return summary including the AGGREGATED telemetry data
|
|
return {
|
|
success: true,
|
|
expandedCount,
|
|
failedCount,
|
|
skippedCount: 0,
|
|
tasksToExpand: tasksToExpandCount,
|
|
telemetryData: aggregatedTelemetryData
|
|
};
|
|
} catch (error) {
|
|
if (loadingIndicator)
|
|
stopLoadingIndicator(loadingIndicator, 'Error.', false);
|
|
logger.error(`Error during expand all operation: ${error.message}`);
|
|
if (!isMCPCall && getDebugFlag(session)) {
|
|
console.error(error); // Log full stack in debug CLI mode
|
|
}
|
|
// Re-throw error for the caller to handle, the direct function will format it
|
|
throw error; // Let direct function wrapper handle formatting
|
|
/* Original re-throw:
|
|
throw new Error(`Failed to expand all tasks: ${error.message}`);
|
|
*/
|
|
}
|
|
}
|
|
|
|
export default expandAllTasks;
|