scope_down_task
Simplify complex tasks efficiently with AI by adjusting their scope. Input task IDs, define strength levels, and apply custom prompts for precise task management in AI-driven development workflows.
Instructions
Decrease the complexity of one or more tasks using AI
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| file | No | Path to the tasks file (default: tasks/tasks.json) | |
| id | Yes | Comma-separated list of task IDs to scope down (e.g., "1,3,5") | |
| projectRoot | Yes | The directory of the project. Must be an absolute path. | |
| prompt | No | Custom prompt for specific scoping adjustments | |
| research | No | Whether to use research capabilities for scoping | |
| strength | No | Strength level: light, regular, or heavy (default: regular) | |
| tag | No | Tag context to operate on |
Implementation Reference
- Core handler function implementing the logic to scope down task complexity: validates inputs, adjusts task descriptions, regenerates subtasks preserving completed ones, optionally reanalyzes complexity, and updates the tasks.json file.export async function scopeDownTask( tasksPath, taskIds, strength = 'regular', customPrompt = null, context = {}, outputFormat = 'text' ) { // Validate inputs if (!validateStrength(strength)) { throw new Error( `Invalid strength level: ${strength}. Must be one of: ${VALID_STRENGTHS.join(', ')}` ); } const { projectRoot = '.', tag = 'master' } = context; // Read tasks data const data = readJSON(tasksPath, projectRoot, tag); const tasks = data?.tasks || []; // Validate all task IDs exist for (const taskId of taskIds) { if (!taskExists(tasks, taskId)) { throw new Error(`Task with ID ${taskId} not found`); } } const updatedTasks = []; let combinedTelemetryData = null; // Process each task for (const taskId of taskIds) { const taskResult = findTaskById(tasks, taskId); const task = taskResult.task; if (!task) { throw new Error(`Task with ID ${taskId} not found`); } if (outputFormat === 'text') { log('info', `Scoping down task ${taskId}: ${task.title}`); } // Get original complexity score (if available) const originalComplexity = getCurrentComplexityScore(taskId, context); if (originalComplexity && outputFormat === 'text') { log('info', `Original complexity: ${originalComplexity}/10`); } const adjustResult = await adjustTaskComplexity( task, 'down', strength, customPrompt, context ); // Regenerate subtasks based on new complexity while preserving completed work const subtaskResult = await regenerateSubtasksForComplexity( adjustResult.updatedTask, tasksPath, context, 'down', strength, originalComplexity ); // Log subtask regeneration info if in text mode if (outputFormat === 'text' && subtaskResult.regenerated) { log( 'info', `Regenerated ${subtaskResult.generated} pending subtasks (preserved ${subtaskResult.preserved} completed)` ); } // Update task in data const taskIndex = data.tasks.findIndex((t) => t.id === taskId); if (taskIndex !== -1) { data.tasks[taskIndex] = subtaskResult.updatedTask; updatedTasks.push(subtaskResult.updatedTask); } // Re-analyze complexity after scoping (if we have a session for AI calls) if (context.session && originalComplexity) { try { // Write the updated task first so complexity analysis can read it writeJSON(tasksPath, data, projectRoot, tag); // Re-analyze complexity const newComplexity = await reanalyzeTaskComplexity( subtaskResult.updatedTask, tasksPath, context ); if (newComplexity && outputFormat === 'text') { const complexityChange = newComplexity - originalComplexity; const arrow = complexityChange > 0 ? '↗️' : complexityChange < 0 ? '↘️' : '➡️'; log( 'info', `New complexity: ${originalComplexity}/10 ${arrow} ${newComplexity}/10 (${complexityChange > 0 ? '+' : ''}${complexityChange})` ); } } catch (error) { if (outputFormat === 'text') { log('warn', `Could not re-analyze complexity: ${error.message}`); } } } // Combine telemetry data if (adjustResult.telemetryData) { if (!combinedTelemetryData) { combinedTelemetryData = { ...adjustResult.telemetryData }; } else { // Sum up costs and tokens combinedTelemetryData.inputTokens += adjustResult.telemetryData.inputTokens || 0; combinedTelemetryData.outputTokens += adjustResult.telemetryData.outputTokens || 0; combinedTelemetryData.totalTokens += adjustResult.telemetryData.totalTokens || 0; combinedTelemetryData.totalCost += adjustResult.telemetryData.totalCost || 0; } } } // Write updated data writeJSON(tasksPath, data, projectRoot, tag); if (outputFormat === 'text') { log('info', `Successfully scoped down ${updatedTasks.length} task(s)`); } return { updatedTasks, telemetryData: combinedTelemetryData }; }
- Direct function wrapper for scopeDownTask, handles MCP-specific logging, silent mode, argument parsing, and error formatting.export async function scopeDownDirect(args, log, context = {}) { // Destructure expected args const { tasksJsonPath, id, strength = 'regular', prompt: customPrompt, research = false, projectRoot, tag } = args; const { session } = context; // Destructure session from context // Enable silent mode to prevent console logs from interfering with JSON response enableSilentMode(); // Create logger wrapper using the utility const mcpLog = createLogWrapper(log); try { // Check if tasksJsonPath was provided if (!tasksJsonPath) { log.error('scopeDownDirect called without tasksJsonPath'); disableSilentMode(); // Disable before returning return { success: false, error: { code: 'MISSING_ARGUMENT', message: 'tasksJsonPath is required' } }; } // Check required parameters if (!id) { log.error('Missing required parameter: id'); disableSilentMode(); return { success: false, error: { code: 'MISSING_PARAMETER', message: 'The id parameter is required for scoping down tasks' } }; } // Parse task IDs - convert to numbers as expected by scopeDownTask const taskIds = id.split(',').map((taskId) => parseInt(taskId.trim(), 10)); log.info( `Scoping down tasks: ${taskIds.join(', ')}, strength: ${strength}, research: ${research}` ); // Call the scopeDownTask function const result = await scopeDownTask( tasksJsonPath, taskIds, strength, customPrompt, { session, mcpLog, projectRoot, commandName: 'scope-down', outputType: 'mcp', tag, research }, 'json' // outputFormat ); // Restore normal logging disableSilentMode(); return { success: true, data: { updatedTasks: result.updatedTasks, tasksUpdated: result.updatedTasks.length, message: `Successfully scoped down ${result.updatedTasks.length} task(s)`, telemetryData: result.telemetryData } }; } catch (error) { // Make sure to restore normal logging even if there's an error disableSilentMode(); log.error(`Error in scopeDownDirect: ${error.message}`); return { success: false, error: { code: error.code || 'SCOPE_DOWN_ERROR', message: error.message } }; } }
- Zod schema defining input parameters for the scope_down_task tool.parameters: z.object({ id: z .string() .describe( 'Comma-separated list of task IDs to scope down (e.g., "1,3,5")' ), strength: z .string() .optional() .describe( 'Strength level: light, regular, or heavy (default: regular)' ), prompt: z .string() .optional() .describe('Custom prompt for specific scoping adjustments'), file: z .string() .optional() .describe('Path to the tasks file (default: tasks/tasks.json)'), projectRoot: z .string() .describe('The directory of the project. Must be an absolute path.'), tag: z.string().optional().describe('Tag context to operate on'), research: z .boolean() .optional() .describe('Whether to use research capabilities for scoping') }),
- mcp-server/src/tools/scope-down.js:20-103 (registration)Registration function that adds the 'scope_down_task' tool to the MCP server, including name, description, schema, and execute handler.export function registerScopeDownTool(server) { server.addTool({ name: 'scope_down_task', description: 'Decrease the complexity of one or more tasks using AI', parameters: z.object({ id: z .string() .describe( 'Comma-separated list of task IDs to scope down (e.g., "1,3,5")' ), strength: z .string() .optional() .describe( 'Strength level: light, regular, or heavy (default: regular)' ), prompt: z .string() .optional() .describe('Custom prompt for specific scoping adjustments'), file: z .string() .optional() .describe('Path to the tasks file (default: tasks/tasks.json)'), projectRoot: z .string() .describe('The directory of the project. Must be an absolute path.'), tag: z.string().optional().describe('Tag context to operate on'), research: z .boolean() .optional() .describe('Whether to use research capabilities for scoping') }), execute: withNormalizedProjectRoot(async (args, { log, session }) => { try { log.info(`Starting scope-down with args: ${JSON.stringify(args)}`); const resolvedTag = resolveTag({ projectRoot: args.projectRoot, tag: args.tag }); // Use args.projectRoot directly (guaranteed by withNormalizedProjectRoot) let tasksJsonPath; try { tasksJsonPath = findTasksPath( { projectRoot: args.projectRoot, file: args.file }, log ); } catch (error) { log.error(`Error finding tasks.json: ${error.message}`); return createErrorResponse( `Failed to find tasks.json: ${error.message}` ); } // Call the direct function const result = await scopeDownDirect( { tasksJsonPath: tasksJsonPath, id: args.id, strength: args.strength, prompt: args.prompt, research: args.research, projectRoot: args.projectRoot, tag: resolvedTag }, log, { session } ); return handleApiResult({ result, log: log, errorPrefix: 'Error scoping down task', projectRoot: args.projectRoot }); } catch (error) { log.error(`Error in scope-down tool: ${error.message}`); return createErrorResponse(error.message); } }) }); }
- mcp-server/src/tools/tool-registry.js:69-69 (registration)Central tool registry entry mapping 'scope_down_task' to its registration function registerScopeDownTool.scope_down_task: registerScopeDownTool,