feat: added complexity score handling to list tasks

This commit is contained in:
Shrey Paharia
2025-04-19 02:17:38 +05:30
parent 48a8d952bc
commit b98af1541e
2 changed files with 104 additions and 29 deletions

View File

@@ -4,11 +4,11 @@
*/ */
import { listTasks } from '../../../../scripts/modules/task-manager.js'; import { listTasks } from '../../../../scripts/modules/task-manager.js';
import { getCachedOrExecute } from '../../tools/utils.js';
import { import {
enableSilentMode, disableSilentMode,
disableSilentMode enableSilentMode
} from '../../../../scripts/modules/utils.js'; } from '../../../../scripts/modules/utils.js';
import { getCachedOrExecute } from '../../tools/utils.js';
/** /**
* Direct function wrapper for listTasks with error handling and caching. * Direct function wrapper for listTasks with error handling and caching.
@@ -19,7 +19,7 @@ import {
*/ */
export async function listTasksDirect(args, log) { export async function listTasksDirect(args, log) {
// Destructure the explicit tasksJsonPath from args // Destructure the explicit tasksJsonPath from args
const { tasksJsonPath, status, withSubtasks } = args; const { tasksJsonPath, reportPath, status, withSubtasks } = args;
if (!tasksJsonPath) { if (!tasksJsonPath) {
log.error('listTasksDirect called without tasksJsonPath'); log.error('listTasksDirect called without tasksJsonPath');
@@ -65,6 +65,21 @@ export async function listTasksDirect(args, log) {
} }
}; };
} }
// Add complexity scores to tasks if report exists
const complexityReport = readComplexityReport(reportPath);
if (complexityReport && complexityReport.complexityAnalysis) {
resultData.tasks = resultData.tasks.map((task) => {
const analysis = complexityReport.complexityAnalysis.find(
(a) => a.taskId === task.id
);
if (analysis) {
return { ...task, complexityScore: analysis.complexityScore };
}
return task;
});
}
log.info( log.info(
`Core listTasks function retrieved ${resultData.tasks.length} tasks` `Core listTasks function retrieved ${resultData.tasks.length} tasks`
); );

View File

@@ -3,9 +3,8 @@
*/ */
import { jest } from '@jest/globals'; import { jest } from '@jest/globals';
import path from 'path'; import path, { dirname } from 'path';
import { fileURLToPath } from 'url'; import { fileURLToPath } from 'url';
import { dirname } from 'path';
// Get the current module's directory // Get the current module's directory
const __filename = fileURLToPath(import.meta.url); const __filename = fileURLToPath(import.meta.url);
@@ -27,6 +26,7 @@ const mockReadJSON = jest.fn();
const mockWriteJSON = jest.fn(); const mockWriteJSON = jest.fn();
const mockEnableSilentMode = jest.fn(); const mockEnableSilentMode = jest.fn();
const mockDisableSilentMode = jest.fn(); const mockDisableSilentMode = jest.fn();
const mockReadComplexityReport = jest.fn().mockReturnValue(null);
const mockGetAnthropicClient = jest.fn().mockReturnValue({}); const mockGetAnthropicClient = jest.fn().mockReturnValue({});
const mockGetConfiguredAnthropicClient = jest.fn().mockReturnValue({}); const mockGetConfiguredAnthropicClient = jest.fn().mockReturnValue({});
@@ -130,6 +130,7 @@ jest.mock('../../../scripts/modules/utils.js', () => ({
writeJSON: mockWriteJSON, writeJSON: mockWriteJSON,
enableSilentMode: mockEnableSilentMode, enableSilentMode: mockEnableSilentMode,
disableSilentMode: mockDisableSilentMode, disableSilentMode: mockDisableSilentMode,
readComplexityReport: mockReadComplexityReport,
CONFIG: { CONFIG: {
model: 'claude-3-7-sonnet-20250219', model: 'claude-3-7-sonnet-20250219',
maxTokens: 64000, maxTokens: 64000,
@@ -160,15 +161,6 @@ jest.mock('../../../scripts/modules/task-manager.js', () => ({
})); }));
// Import dependencies after mocks are set up // Import dependencies after mocks are set up
import fs from 'fs';
import {
readJSON,
writeJSON,
enableSilentMode,
disableSilentMode
} from '../../../scripts/modules/utils.js';
import { expandTask } from '../../../scripts/modules/task-manager.js';
import { findTasksJsonPath } from '../../../mcp-server/src/core/utils/path-utils.js';
import { sampleTasks } from '../../fixtures/sample-tasks.js'; import { sampleTasks } from '../../fixtures/sample-tasks.js';
// Mock logger // Mock logger
@@ -220,6 +212,37 @@ describe('MCP Server Direct Functions', () => {
}); });
describe('listTasksDirect', () => { describe('listTasksDirect', () => {
// Sample complexity report for testing
const mockComplexityReport = {
meta: {
generatedAt: '2025-03-24T20:01:35.986Z',
tasksAnalyzed: 3,
thresholdScore: 5,
projectName: 'Test Project',
usedResearch: false
},
complexityAnalysis: [
{
taskId: 1,
taskTitle: 'Initialize Project',
complexityScore: 3,
recommendedSubtasks: 2
},
{
taskId: 2,
taskTitle: 'Create Core Functionality',
complexityScore: 8,
recommendedSubtasks: 5
},
{
taskId: 3,
taskTitle: 'Implement UI Components',
complexityScore: 6,
recommendedSubtasks: 4
}
]
};
// Test wrapper function that doesn't rely on the actual implementation // Test wrapper function that doesn't rely on the actual implementation
async function testListTasks(args, mockLogger) { async function testListTasks(args, mockLogger) {
// File not found case // File not found case
@@ -235,21 +258,35 @@ describe('MCP Server Direct Functions', () => {
}; };
} }
// Check for complexity report
const complexityReport = mockReadComplexityReport();
let tasksData = [...sampleTasks.tasks];
// Add complexity scores if report exists
if (complexityReport && complexityReport.complexityAnalysis) {
tasksData = tasksData.map((task) => {
const analysis = complexityReport.complexityAnalysis.find(
(a) => a.taskId === task.id
);
if (analysis) {
return { ...task, complexityScore: analysis.complexityScore };
}
return task;
});
}
// Success case // Success case
if (!args.status && !args.withSubtasks) { if (!args.status && !args.withSubtasks) {
return { return {
success: true, success: true,
data: { data: {
tasks: sampleTasks.tasks, tasks: tasksData,
stats: { stats: {
total: sampleTasks.tasks.length, total: tasksData.length,
completed: sampleTasks.tasks.filter((t) => t.status === 'done') completed: tasksData.filter((t) => t.status === 'done').length,
inProgress: tasksData.filter((t) => t.status === 'in-progress')
.length, .length,
inProgress: sampleTasks.tasks.filter( pending: tasksData.filter((t) => t.status === 'pending').length
(t) => t.status === 'in-progress'
).length,
pending: sampleTasks.tasks.filter((t) => t.status === 'pending')
.length
} }
}, },
fromCache: false fromCache: false
@@ -258,16 +295,14 @@ describe('MCP Server Direct Functions', () => {
// Status filter case // Status filter case
if (args.status) { if (args.status) {
const filteredTasks = sampleTasks.tasks.filter( const filteredTasks = tasksData.filter((t) => t.status === args.status);
(t) => t.status === args.status
);
return { return {
success: true, success: true,
data: { data: {
tasks: filteredTasks, tasks: filteredTasks,
filter: args.status, filter: args.status,
stats: { stats: {
total: sampleTasks.tasks.length, total: tasksData.length,
filtered: filteredTasks.length filtered: filteredTasks.length
} }
}, },
@@ -280,10 +315,10 @@ describe('MCP Server Direct Functions', () => {
return { return {
success: true, success: true,
data: { data: {
tasks: sampleTasks.tasks, tasks: tasksData,
includeSubtasks: true, includeSubtasks: true,
stats: { stats: {
total: sampleTasks.tasks.length total: tasksData.length
} }
}, },
fromCache: false fromCache: false
@@ -370,6 +405,31 @@ describe('MCP Server Direct Functions', () => {
expect(result.error.code).toBe('FILE_NOT_FOUND_ERROR'); expect(result.error.code).toBe('FILE_NOT_FOUND_ERROR');
expect(mockLogger.error).toHaveBeenCalled(); expect(mockLogger.error).toHaveBeenCalled();
}); });
test('should include complexity scores when complexity report exists', async () => {
// Arrange
mockReadComplexityReport.mockReturnValueOnce(mockComplexityReport);
const args = {
projectRoot: testProjectRoot,
file: testTasksPath,
withSubtasks: true
};
// Act
const result = await testListTasks(args, mockLogger);
console.dir(result, { depth: 5 });
// Assert
expect(result.success).toBe(true);
// Check that tasks have complexity scores from the report
mockComplexityReport.complexityAnalysis.forEach((analysis) => {
const task = result.data.tasks.find((t) => t.id === analysis.taskId);
if (task) {
expect(task.complexityScore).toBe(analysis.complexityScore);
}
});
});
}); });
describe('expandTaskDirect', () => { describe('expandTaskDirect', () => {