feat(telemetry): Integrate telemetry for expand-all, aggregate results
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.
This commit is contained in:
@@ -1129,12 +1129,6 @@ function registerCommands(programInstance) {
|
||||
{} // Pass empty context for CLI calls
|
||||
// outputFormat defaults to 'text' in expandAllTasks for CLI
|
||||
);
|
||||
// Optional: Display summary from result
|
||||
console.log(chalk.green(`Expansion Summary:`));
|
||||
console.log(chalk.green(` - Attempted: ${result.tasksToExpand}`));
|
||||
console.log(chalk.green(` - Expanded: ${result.expandedCount}`));
|
||||
console.log(chalk.yellow(` - Skipped: ${result.skippedCount}`));
|
||||
console.log(chalk.red(` - Failed: ${result.failedCount}`));
|
||||
} catch (error) {
|
||||
console.error(
|
||||
chalk.red(`Error expanding all tasks: ${error.message}`)
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
import { log, readJSON, isSilentMode } from '../utils.js';
|
||||
import { startLoadingIndicator, stopLoadingIndicator } from '../ui.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.
|
||||
@@ -14,7 +21,7 @@ import { getDebugFlag } from '../config-manager.js';
|
||||
* @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, message?: string}>} - Result summary.
|
||||
* @returns {Promise<{success: boolean, expandedCount: number, failedCount: number, skippedCount: number, tasksToExpand: number, telemetryData: Array<Object>}>} - Result summary.
|
||||
*/
|
||||
async function expandAllTasks(
|
||||
tasksPath,
|
||||
@@ -51,8 +58,8 @@ async function expandAllTasks(
|
||||
let loadingIndicator = null;
|
||||
let expandedCount = 0;
|
||||
let failedCount = 0;
|
||||
// No skipped count needed now as the filter handles it upfront
|
||||
let tasksToExpandCount = 0; // Renamed for clarity
|
||||
let tasksToExpandCount = 0;
|
||||
const allTelemetryData = []; // Still collect individual data first
|
||||
|
||||
if (!isMCPCall && outputFormat === 'text') {
|
||||
loadingIndicator = startLoadingIndicator(
|
||||
@@ -90,6 +97,7 @@ async function expandAllTasks(
|
||||
failedCount: 0,
|
||||
skippedCount: 0,
|
||||
tasksToExpand: 0,
|
||||
telemetryData: allTelemetryData,
|
||||
message: 'No tasks eligible for expansion.'
|
||||
};
|
||||
// --- End Fix ---
|
||||
@@ -97,19 +105,6 @@ async function expandAllTasks(
|
||||
|
||||
// Iterate over the already filtered tasks
|
||||
for (const task of tasksToExpand) {
|
||||
// --- Remove Redundant Check ---
|
||||
// The check below is no longer needed as the initial filter handles it
|
||||
/*
|
||||
if (task.subtasks && task.subtasks.length > 0 && !force) {
|
||||
logger.info(
|
||||
`Skipping task ${task.id}: Already has subtasks. Use --force to overwrite.`
|
||||
);
|
||||
skippedCount++;
|
||||
continue;
|
||||
}
|
||||
*/
|
||||
// --- End Removed Redundant Check ---
|
||||
|
||||
// Start indicator for individual task expansion in CLI mode
|
||||
let taskIndicator = null;
|
||||
if (!isMCPCall && outputFormat === 'text') {
|
||||
@@ -117,17 +112,23 @@ async function expandAllTasks(
|
||||
}
|
||||
|
||||
try {
|
||||
// Call the refactored expandTask function
|
||||
await expandTask(
|
||||
// Call the refactored expandTask function AND capture result
|
||||
const result = await expandTask(
|
||||
tasksPath,
|
||||
task.id,
|
||||
numSubtasks, // Pass numSubtasks, expandTask handles defaults/complexity
|
||||
numSubtasks,
|
||||
useResearch,
|
||||
additionalContext,
|
||||
context, // Pass the whole context object { session, mcpLog }
|
||||
force // Pass the force flag down
|
||||
force
|
||||
);
|
||||
expandedCount++;
|
||||
|
||||
// Collect individual telemetry data
|
||||
if (result && result.telemetryData) {
|
||||
allTelemetryData.push(result.telemetryData);
|
||||
}
|
||||
|
||||
if (taskIndicator) {
|
||||
stopLoadingIndicator(taskIndicator, `Task ${task.id} expanded.`);
|
||||
}
|
||||
@@ -146,18 +147,48 @@ async function expandAllTasks(
|
||||
}
|
||||
}
|
||||
|
||||
// Log final summary (removed skipped count from message)
|
||||
// --- AGGREGATION AND DISPLAY ---
|
||||
logger.info(
|
||||
`Expansion complete: ${expandedCount} expanded, ${failedCount} failed.`
|
||||
);
|
||||
|
||||
// Return summary (skippedCount is now 0) - Add success: true here as well for consistency
|
||||
// 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, // Indicate overall success
|
||||
success: true,
|
||||
expandedCount,
|
||||
failedCount,
|
||||
skippedCount: 0,
|
||||
tasksToExpand: tasksToExpandCount
|
||||
tasksToExpand: tasksToExpandCount,
|
||||
telemetryData: aggregatedTelemetryData
|
||||
};
|
||||
} catch (error) {
|
||||
if (loadingIndicator)
|
||||
|
||||
@@ -508,6 +508,61 @@ function detectCamelCaseFlags(args) {
|
||||
return camelCaseFlags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aggregates an array of telemetry objects into a single summary object.
|
||||
* @param {Array<Object>} telemetryArray - Array of telemetryData objects.
|
||||
* @param {string} overallCommandName - The name for the aggregated command.
|
||||
* @returns {Object|null} Aggregated telemetry object or null if input is empty.
|
||||
*/
|
||||
function _aggregateTelemetry(telemetryArray, overallCommandName) {
|
||||
if (!telemetryArray || telemetryArray.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const aggregated = {
|
||||
timestamp: new Date().toISOString(), // Use current time for aggregation time
|
||||
userId: telemetryArray[0].userId, // Assume userId is consistent
|
||||
commandName: overallCommandName,
|
||||
modelUsed: 'Multiple', // Default if models vary
|
||||
providerName: 'Multiple', // Default if providers vary
|
||||
inputTokens: 0,
|
||||
outputTokens: 0,
|
||||
totalTokens: 0,
|
||||
totalCost: 0,
|
||||
currency: telemetryArray[0].currency || 'USD' // Assume consistent currency or default
|
||||
};
|
||||
|
||||
const uniqueModels = new Set();
|
||||
const uniqueProviders = new Set();
|
||||
const uniqueCurrencies = new Set();
|
||||
|
||||
telemetryArray.forEach((item) => {
|
||||
aggregated.inputTokens += item.inputTokens || 0;
|
||||
aggregated.outputTokens += item.outputTokens || 0;
|
||||
aggregated.totalCost += item.totalCost || 0;
|
||||
uniqueModels.add(item.modelUsed);
|
||||
uniqueProviders.add(item.providerName);
|
||||
uniqueCurrencies.add(item.currency || 'USD');
|
||||
});
|
||||
|
||||
aggregated.totalTokens = aggregated.inputTokens + aggregated.outputTokens;
|
||||
aggregated.totalCost = parseFloat(aggregated.totalCost.toFixed(6)); // Fix precision
|
||||
|
||||
if (uniqueModels.size === 1) {
|
||||
aggregated.modelUsed = [...uniqueModels][0];
|
||||
}
|
||||
if (uniqueProviders.size === 1) {
|
||||
aggregated.providerName = [...uniqueProviders][0];
|
||||
}
|
||||
if (uniqueCurrencies.size > 1) {
|
||||
aggregated.currency = 'Multiple'; // Mark if currencies actually differ
|
||||
} else if (uniqueCurrencies.size === 1) {
|
||||
aggregated.currency = [...uniqueCurrencies][0];
|
||||
}
|
||||
|
||||
return aggregated;
|
||||
}
|
||||
|
||||
// Export all utility functions and configuration
|
||||
export {
|
||||
LOG_LEVELS,
|
||||
@@ -529,5 +584,6 @@ export {
|
||||
isSilentMode,
|
||||
resolveEnvVariable,
|
||||
getTaskManager,
|
||||
findProjectRoot
|
||||
findProjectRoot,
|
||||
_aggregateTelemetry
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user