feat: added complexity score handling to list tasks
This commit is contained in:
@@ -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`
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user