Compare commits

...

2 Commits

Author SHA1 Message Date
github-actions[bot]
468106af1e docs: auto-update documentation based on changes in next branch
This PR was automatically generated to update documentation based on recent changes.

  Original commit: fix: expand_all now uses complexity analysis recommendations (#1287)\n\nCo-authored-by: Claude <noreply@anthropic.com>\n\n

  Co-authored-by: Claude <claude-assistant@anthropic.com>
2025-10-11 10:01:13 +00:00
Ralph Khreish
90e6bdcf1c fix: expand_all now uses complexity analysis recommendations (#1287)
Co-authored-by: Claude <noreply@anthropic.com>
2025-10-11 11:54:31 +02:00
9 changed files with 171 additions and 10 deletions

View File

@@ -0,0 +1,16 @@
---
"task-master-ai": minor
---
Enhance `expand_all` to intelligently use complexity analysis recommendations when expanding tasks.
The expand-all operation now automatically leverages recommendations from `analyze-complexity` to determine optimal subtask counts for each task, resulting in more accurate and context-aware task breakdowns.
Key improvements:
- Automatic integration with complexity analysis reports
- Tag-aware complexity report path resolution
- Intelligent subtask count determination based on task complexity
- Falls back to defaults when complexity analysis is unavailable
- Enhanced logging for better visibility into expansion decisions
When you run `task-master expand --all` after `task-master analyze-complexity`, Task Master now uses the recommended subtask counts from the complexity analysis instead of applying uniform defaults, ensuring each task is broken down according to its actual complexity.

View File

@@ -124,6 +124,8 @@ sidebarTitle: "CLI Commands"
# Research-backed generation for all tasks # Research-backed generation for all tasks
task-master expand --all --research task-master expand --all --research
``` ```
When you run `task-master expand --all` after `task-master analyze-complexity`, the expand operation automatically uses the recommended subtask counts from the complexity analysis instead of applying uniform defaults. This ensures each task is broken down according to its actual complexity level.
</Accordion> </Accordion>
<Accordion title="Clear Subtasks"> <Accordion title="Clear Subtasks">

View File

@@ -40,7 +40,7 @@ The MCP tools can be categorized in the same way as the core functionalities:
- **`parse_prd`**: Parses a PRD to generate tasks. - **`parse_prd`**: Parses a PRD to generate tasks.
- **`expand_task`**: Expands a task into subtasks. - **`expand_task`**: Expands a task into subtasks.
- **`expand_all`**: Expands all eligible tasks. - **`expand_all`**: Expands all eligible tasks, automatically using complexity analysis recommendations when available.
- **`analyze_project_complexity`**: Analyzes task complexity. - **`analyze_project_complexity`**: Analyzes task complexity.
- **`complexity_report`**: Displays the complexity analysis report. - **`complexity_report`**: Displays the complexity analysis report.

View File

@@ -197,7 +197,7 @@ Task Master will:
task-master analyze-complexity --research task-master analyze-complexity --research
``` ```
Review the complexity report to identify tasks that need expansion. Review the complexity report to identify tasks that need expansion and determine optimal subtask counts.
### Step 4: Expand Tasks ### Step 4: Expand Tasks
@@ -205,7 +205,7 @@ Review the complexity report to identify tasks that need expansion.
task-master expand --all --research task-master expand --all --research
``` ```
Break down complex tasks into manageable subtasks while preserving dependency chains. Break down complex tasks into manageable subtasks while preserving dependency chains. The expand operation automatically uses the complexity analysis recommendations to determine the appropriate number of subtasks for each task based on its complexity level.
--- ---

View File

@@ -8,6 +8,7 @@ import {
disableSilentMode disableSilentMode
} from '../../../../scripts/modules/utils.js'; } from '../../../../scripts/modules/utils.js';
import { createLogWrapper } from '../../tools/utils.js'; import { createLogWrapper } from '../../tools/utils.js';
import { resolveComplexityReportOutputPath } from '../../../../src/utils/path-utils.js';
/** /**
* Expand all pending tasks with subtasks (Direct Function Wrapper) * Expand all pending tasks with subtasks (Direct Function Wrapper)
@@ -25,13 +26,30 @@ import { createLogWrapper } from '../../tools/utils.js';
*/ */
export async function expandAllTasksDirect(args, log, context = {}) { export async function expandAllTasksDirect(args, log, context = {}) {
const { session } = context; // Extract session const { session } = context; // Extract session
// Destructure expected args, including projectRoot // Destructure expected args, including projectRoot and complexityReportPath
const { tasksJsonPath, num, research, prompt, force, projectRoot, tag } = const {
args; tasksJsonPath,
num,
research,
prompt,
force,
projectRoot,
tag,
complexityReportPath: providedComplexityReportPath
} = args;
// Create logger wrapper using the utility // Create logger wrapper using the utility
const mcpLog = createLogWrapper(log); const mcpLog = createLogWrapper(log);
// Use provided complexity report path or compute it
const complexityReportPath =
providedComplexityReportPath ||
resolveComplexityReportOutputPath(null, { projectRoot, tag }, log);
log.info(
`Expand all tasks will use complexity report at: ${complexityReportPath}`
);
if (!tasksJsonPath) { if (!tasksJsonPath) {
log.error('expandAllTasksDirect called without tasksJsonPath'); log.error('expandAllTasksDirect called without tasksJsonPath');
return { return {
@@ -55,14 +73,14 @@ export async function expandAllTasksDirect(args, log, context = {}) {
const additionalContext = prompt || ''; const additionalContext = prompt || '';
const forceFlag = force === true; const forceFlag = force === true;
// Call the core function, passing options and the context object { session, mcpLog, projectRoot } // Call the core function, passing options and the context object { session, mcpLog, projectRoot, tag, complexityReportPath }
const result = await expandAllTasks( const result = await expandAllTasks(
tasksJsonPath, tasksJsonPath,
numSubtasks, numSubtasks,
useResearch, useResearch,
additionalContext, additionalContext,
forceFlag, forceFlag,
{ session, mcpLog, projectRoot, tag }, { session, mcpLog, projectRoot, tag, complexityReportPath },
'json' 'json'
); );

View File

@@ -3,6 +3,7 @@ import {
findTasksPath as coreFindTasksPath, findTasksPath as coreFindTasksPath,
findPRDPath as coreFindPrdPath, findPRDPath as coreFindPrdPath,
findComplexityReportPath as coreFindComplexityReportPath, findComplexityReportPath as coreFindComplexityReportPath,
resolveComplexityReportOutputPath as coreResolveComplexityReportOutputPath,
findProjectRoot as coreFindProjectRoot, findProjectRoot as coreFindProjectRoot,
normalizeProjectRoot normalizeProjectRoot
} from '../../../../src/utils/path-utils.js'; } from '../../../../src/utils/path-utils.js';
@@ -224,6 +225,21 @@ export function findComplexityReportPath(args, log = silentLogger) {
return resolveComplexityReportPath(args, log); return resolveComplexityReportPath(args, log);
} }
/**
* Resolve complexity report output path (create if needed) - primary MCP function
* @param {string|null} [explicitPath] - Explicit path to complexity report
* @param {Object} args - Arguments object containing projectRoot and tag
* @param {Object} [log] - Log function to prevent console logging
* @returns {string} - Resolved output path for complexity report
*/
export function resolveComplexityReportOutputPath(
explicitPath,
args,
log = silentLogger
) {
return coreResolveComplexityReportOutputPath(explicitPath, args, log);
}
/** /**
* Find PRD path - primary MCP function * Find PRD path - primary MCP function
* @param {string} [explicitPath] - Explicit path to PRD file * @param {string} [explicitPath] - Explicit path to PRD file

View File

@@ -10,7 +10,10 @@ import {
withNormalizedProjectRoot withNormalizedProjectRoot
} from './utils.js'; } from './utils.js';
import { expandAllTasksDirect } from '../core/task-master-core.js'; import { expandAllTasksDirect } from '../core/task-master-core.js';
import { findTasksPath } from '../core/utils/path-utils.js'; import {
findTasksPath,
resolveComplexityReportOutputPath
} from '../core/utils/path-utils.js';
import { resolveTag } from '../../../scripts/modules/utils.js'; import { resolveTag } from '../../../scripts/modules/utils.js';
/** /**
@@ -85,6 +88,14 @@ export function registerExpandAllTool(server) {
); );
} }
// Resolve complexity report path to use recommendations from analyze-complexity
const complexityReportPath = resolveComplexityReportOutputPath(
null,
{ projectRoot: args.projectRoot, tag: resolvedTag },
log
);
log.info(`Using complexity report path: ${complexityReportPath}`);
const result = await expandAllTasksDirect( const result = await expandAllTasksDirect(
{ {
tasksJsonPath: tasksJsonPath, tasksJsonPath: tasksJsonPath,
@@ -93,7 +104,8 @@ export function registerExpandAllTool(server) {
prompt: args.prompt, prompt: args.prompt,
force: args.force, force: args.force,
projectRoot: args.projectRoot, projectRoot: args.projectRoot,
tag: resolvedTag tag: resolvedTag,
complexityReportPath
}, },
log, log,
{ session } { session }

50
output.txt Normal file

File diff suppressed because one or more lines are too long

View File

@@ -244,6 +244,53 @@ describe('expandAllTasks', () => {
); );
}); });
test('should pass complexityReportPath to expandTask when provided in context', async () => {
// Arrange
const mockComplexityReportPath =
'/test/project/.taskmaster/reports/task-complexity-report.json';
mockExpandTask.mockResolvedValue({
telemetryData: { commandName: 'expand-task', totalCost: 0.05 }
});
// Act
const result = await expandAllTasks(
mockTasksPath,
undefined, // numSubtasks not specified, should use complexity report
false,
'',
false,
{
session: mockSession,
mcpLog: mockMcpLog,
projectRoot: mockProjectRoot,
tag: 'master',
complexityReportPath: mockComplexityReportPath
},
'json'
);
// Assert
expect(result.success).toBe(true);
expect(result.expandedCount).toBe(2); // Tasks 1 and 2
// Verify expandTask was called with complexityReportPath in context
expect(mockExpandTask).toHaveBeenCalledWith(
mockTasksPath,
expect.any(Number), // task id
undefined, // numSubtasks
false, // useResearch
'', // additionalContext
expect.objectContaining({
session: mockSession,
mcpLog: mockMcpLog,
projectRoot: mockProjectRoot,
tag: 'master',
complexityReportPath: mockComplexityReportPath
}),
false // force
);
});
test('should return success with message when no tasks are eligible', async () => { test('should return success with message when no tasks are eligible', async () => {
// Arrange - Mock tasks data with no eligible tasks // Arrange - Mock tasks data with no eligible tasks
const noEligibleTasksData = { const noEligibleTasksData = {