chore: linting and prettier
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -59,11 +59,13 @@ I just merged the main branch and there's a conflict in tasks.json. My teammates
|
||||
```
|
||||
|
||||
(Agent runs:
|
||||
|
||||
```bash
|
||||
task-master move --from=10 --to=16
|
||||
task-master move --from=11 --to=17
|
||||
task-master move --from=12 --to=18
|
||||
```
|
||||
|
||||
)
|
||||
|
||||
## Analyzing complexity
|
||||
|
||||
@@ -292,6 +292,7 @@ You can reorganize tasks in various ways:
|
||||
- Moving multiple tasks at once: `--from=10,11,12 --to=16,17,18` (must have same number of IDs, Taskmaster will look through each position)
|
||||
|
||||
When moving tasks to new IDs:
|
||||
|
||||
- The system automatically creates placeholder tasks for non-existent destination IDs
|
||||
- This prevents accidental data loss during reorganization
|
||||
- Any tasks that depend on moved tasks will have their dependencies updated
|
||||
|
||||
@@ -52,7 +52,8 @@ export async function moveTaskDirect(args, log, context = {}) {
|
||||
return {
|
||||
success: false,
|
||||
error: {
|
||||
message: 'Project root is required if tasksJsonPath is not provided',
|
||||
message:
|
||||
'Project root is required if tasksJsonPath is not provided',
|
||||
code: 'MISSING_PROJECT_ROOT'
|
||||
}
|
||||
};
|
||||
@@ -64,7 +65,12 @@ export async function moveTaskDirect(args, log, context = {}) {
|
||||
enableSilentMode();
|
||||
|
||||
// Call the core moveTask function, always generate files
|
||||
const result = await moveTask(tasksPath, args.sourceId, args.destinationId, true);
|
||||
const result = await moveTask(
|
||||
tasksPath,
|
||||
args.sourceId,
|
||||
args.destinationId,
|
||||
true
|
||||
);
|
||||
|
||||
// Restore console output
|
||||
disableSilentMode();
|
||||
|
||||
@@ -116,7 +116,9 @@ export async function parsePRDDirect(args, log, context = {}) {
|
||||
}
|
||||
|
||||
if (research) {
|
||||
logWrapper.info('Research mode enabled. Using Perplexity AI for enhanced PRD analysis.');
|
||||
logWrapper.info(
|
||||
'Research mode enabled. Using Perplexity AI for enhanced PRD analysis.'
|
||||
);
|
||||
}
|
||||
|
||||
logWrapper.info(
|
||||
|
||||
@@ -52,16 +52,16 @@ export function registerAnalyzeProjectComplexityTool(server) {
|
||||
ids: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe('Comma-separated list of task IDs to analyze specifically (e.g., "1,3,5").'),
|
||||
from: z
|
||||
.coerce
|
||||
.describe(
|
||||
'Comma-separated list of task IDs to analyze specifically (e.g., "1,3,5").'
|
||||
),
|
||||
from: z.coerce
|
||||
.number()
|
||||
.int()
|
||||
.positive()
|
||||
.optional()
|
||||
.describe('Starting task ID in a range to analyze.'),
|
||||
to: z
|
||||
.coerce
|
||||
to: z.coerce
|
||||
.number()
|
||||
.int()
|
||||
.positive()
|
||||
|
||||
@@ -21,20 +21,23 @@ export function registerMoveTaskTool(server) {
|
||||
name: 'move_task',
|
||||
description: 'Move a task or subtask to a new position',
|
||||
parameters: z.object({
|
||||
from: z.string().describe(
|
||||
'ID of the task/subtask to move (e.g., "5" or "5.2"). Can be comma-separated to move multiple tasks (e.g., "5,6,7")'
|
||||
),
|
||||
to: z.string().describe(
|
||||
'ID of the destination (e.g., "7" or "7.3"). Must match the number of source IDs if comma-separated'
|
||||
),
|
||||
file: z
|
||||
from: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe('Custom path to tasks.json file'),
|
||||
.describe(
|
||||
'ID of the task/subtask to move (e.g., "5" or "5.2"). Can be comma-separated to move multiple tasks (e.g., "5,6,7")'
|
||||
),
|
||||
to: z
|
||||
.string()
|
||||
.describe(
|
||||
'ID of the destination (e.g., "7" or "7.3"). Must match the number of source IDs if comma-separated'
|
||||
),
|
||||
file: z.string().optional().describe('Custom path to tasks.json file'),
|
||||
projectRoot: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe('Root directory of the project (typically derived from session)')
|
||||
.describe(
|
||||
'Root directory of the project (typically derived from session)'
|
||||
)
|
||||
}),
|
||||
execute: withNormalizedProjectRoot(async (args, { log, session }) => {
|
||||
try {
|
||||
@@ -46,8 +49,8 @@ export function registerMoveTaskTool(server) {
|
||||
}
|
||||
|
||||
// Parse comma-separated IDs
|
||||
const fromIds = args.from.split(',').map(id => id.trim());
|
||||
const toIds = args.to.split(',').map(id => id.trim());
|
||||
const fromIds = args.from.split(',').map((id) => id.trim());
|
||||
const toIds = args.to.split(',').map((id) => id.trim());
|
||||
|
||||
// Validate matching IDs count
|
||||
if (fromIds.length !== toIds.length) {
|
||||
@@ -84,7 +87,9 @@ export function registerMoveTaskTool(server) {
|
||||
);
|
||||
|
||||
if (!result.success) {
|
||||
log.error(`Failed to move ${fromId} to ${toId}: ${result.error.message}`);
|
||||
log.error(
|
||||
`Failed to move ${fromId} to ${toId}: ${result.error.message}`
|
||||
);
|
||||
} else {
|
||||
results.push(result.data);
|
||||
}
|
||||
|
||||
@@ -53,7 +53,9 @@ export function registerParsePRDTool(server) {
|
||||
.boolean()
|
||||
.optional()
|
||||
.default(false)
|
||||
.describe('Use the research model for research-backed task generation, providing more comprehensive, accurate and up-to-date task details.'),
|
||||
.describe(
|
||||
'Use the research model for research-backed task generation, providing more comprehensive, accurate and up-to-date task details.'
|
||||
),
|
||||
projectRoot: z
|
||||
.string()
|
||||
.describe('The directory of the project. Must be an absolute path.')
|
||||
|
||||
@@ -615,15 +615,19 @@ function registerCommands(programInstance) {
|
||||
console.log(chalk.blue('Appending to existing tasks...'));
|
||||
}
|
||||
if (research) {
|
||||
console.log(chalk.blue('Using Perplexity AI for research-backed task generation'));
|
||||
console.log(
|
||||
chalk.blue(
|
||||
'Using Perplexity AI for research-backed task generation'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
spinner = ora('Parsing PRD and generating tasks...\n').start();
|
||||
await parsePRD(inputFile, outputPath, numTasks, {
|
||||
append: useAppend,
|
||||
force: useForce,
|
||||
research: research
|
||||
});
|
||||
append: useAppend,
|
||||
force: useForce,
|
||||
research: research
|
||||
});
|
||||
spinner.succeed('Tasks generated successfully!');
|
||||
} catch (error) {
|
||||
if (spinner) {
|
||||
@@ -1230,14 +1234,8 @@ function registerCommands(programInstance) {
|
||||
'-i, --id <ids>',
|
||||
'Comma-separated list of specific task IDs to analyze (e.g., "1,3,5")'
|
||||
)
|
||||
.option(
|
||||
'--from <id>',
|
||||
'Starting task ID in a range to analyze'
|
||||
)
|
||||
.option(
|
||||
'--to <id>',
|
||||
'Ending task ID in a range to analyze'
|
||||
)
|
||||
.option('--from <id>', 'Starting task ID in a range to analyze')
|
||||
.option('--to <id>', 'Ending task ID in a range to analyze')
|
||||
.action(async (options) => {
|
||||
const tasksPath = options.file || 'tasks/tasks.json';
|
||||
const outputPath = options.output;
|
||||
@@ -1253,7 +1251,9 @@ function registerCommands(programInstance) {
|
||||
} else if (options.from || options.to) {
|
||||
const fromStr = options.from ? options.from : 'first';
|
||||
const toStr = options.to ? options.to : 'last';
|
||||
console.log(chalk.blue(`Analyzing tasks in range: ${fromStr} to ${toStr}`));
|
||||
console.log(
|
||||
chalk.blue(`Analyzing tasks in range: ${fromStr} to ${toStr}`)
|
||||
);
|
||||
}
|
||||
|
||||
if (useResearch) {
|
||||
@@ -2389,8 +2389,14 @@ Examples:
|
||||
.command('move')
|
||||
.description('Move a task or subtask to a new position')
|
||||
.option('-f, --file <file>', 'Path to the tasks file', 'tasks/tasks.json')
|
||||
.option('--from <id>', 'ID of the task/subtask to move (e.g., "5" or "5.2"). Can be comma-separated to move multiple tasks (e.g., "5,6,7")')
|
||||
.option('--to <id>', 'ID of the destination (e.g., "7" or "7.3"). Must match the number of source IDs if comma-separated')
|
||||
.option(
|
||||
'--from <id>',
|
||||
'ID of the task/subtask to move (e.g., "5" or "5.2"). Can be comma-separated to move multiple tasks (e.g., "5,6,7")'
|
||||
)
|
||||
.option(
|
||||
'--to <id>',
|
||||
'ID of the destination (e.g., "7" or "7.3"). Must match the number of source IDs if comma-separated'
|
||||
)
|
||||
.action(async (options) => {
|
||||
const tasksPath = options.file;
|
||||
const sourceId = options.from;
|
||||
@@ -2409,18 +2415,18 @@ Examples:
|
||||
}
|
||||
|
||||
// Check if we're moving multiple tasks (comma-separated IDs)
|
||||
const sourceIds = sourceId.split(',').map(id => id.trim());
|
||||
const destinationIds = destinationId.split(',').map(id => id.trim());
|
||||
const sourceIds = sourceId.split(',').map((id) => id.trim());
|
||||
const destinationIds = destinationId.split(',').map((id) => id.trim());
|
||||
|
||||
// Validate that the number of source and destination IDs match
|
||||
if (sourceIds.length !== destinationIds.length) {
|
||||
console.error(
|
||||
chalk.red('Error: The number of source and destination IDs must match')
|
||||
chalk.red(
|
||||
'Error: The number of source and destination IDs must match'
|
||||
)
|
||||
);
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
'Example: task-master move --from=5,6,7 --to=10,11,12'
|
||||
)
|
||||
chalk.yellow('Example: task-master move --from=5,6,7 --to=10,11,12')
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -2428,14 +2434,18 @@ Examples:
|
||||
// If moving multiple tasks
|
||||
if (sourceIds.length > 1) {
|
||||
console.log(
|
||||
chalk.blue(`Moving multiple tasks: ${sourceIds.join(', ')} to ${destinationIds.join(', ')}...`)
|
||||
chalk.blue(
|
||||
`Moving multiple tasks: ${sourceIds.join(', ')} to ${destinationIds.join(', ')}...`
|
||||
)
|
||||
);
|
||||
|
||||
try {
|
||||
// Read tasks data once to validate destination IDs
|
||||
const tasksData = readJSON(tasksPath);
|
||||
if (!tasksData || !tasksData.tasks) {
|
||||
console.error(chalk.red(`Error: Invalid or missing tasks file at ${tasksPath}`));
|
||||
console.error(
|
||||
chalk.red(`Error: Invalid or missing tasks file at ${tasksPath}`)
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@@ -2446,20 +2456,31 @@ Examples:
|
||||
|
||||
// Skip if source and destination are the same
|
||||
if (fromId === toId) {
|
||||
console.log(chalk.yellow(`Skipping ${fromId} -> ${toId} (same ID)`));
|
||||
console.log(
|
||||
chalk.yellow(`Skipping ${fromId} -> ${toId} (same ID)`)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(chalk.blue(`Moving task/subtask ${fromId} to ${toId}...`));
|
||||
console.log(
|
||||
chalk.blue(`Moving task/subtask ${fromId} to ${toId}...`)
|
||||
);
|
||||
try {
|
||||
await moveTask(tasksPath, fromId, toId, i === sourceIds.length - 1);
|
||||
await moveTask(
|
||||
tasksPath,
|
||||
fromId,
|
||||
toId,
|
||||
i === sourceIds.length - 1
|
||||
);
|
||||
console.log(
|
||||
chalk.green(
|
||||
`✓ Successfully moved task/subtask ${fromId} to ${toId}`
|
||||
)
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(chalk.red(`Error moving ${fromId} to ${toId}: ${error.message}`));
|
||||
console.error(
|
||||
chalk.red(`Error moving ${fromId} to ${toId}: ${error.message}`)
|
||||
);
|
||||
// Continue with the next task rather than exiting
|
||||
}
|
||||
}
|
||||
@@ -2474,7 +2495,12 @@ Examples:
|
||||
);
|
||||
|
||||
try {
|
||||
const result = await moveTask(tasksPath, sourceId, destinationId, true);
|
||||
const result = await moveTask(
|
||||
tasksPath,
|
||||
sourceId,
|
||||
destinationId,
|
||||
true
|
||||
);
|
||||
console.log(
|
||||
chalk.green(
|
||||
`✓ Successfully moved task/subtask ${sourceId} to ${destinationId}`
|
||||
|
||||
@@ -244,7 +244,7 @@
|
||||
{
|
||||
"id": "google/gemini-2.5-flash-preview-05-20:thinking",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 0.15, "output": 3.50 },
|
||||
"cost_per_1m_tokens": { "input": 0.15, "output": 3.5 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 1048576
|
||||
},
|
||||
@@ -279,14 +279,14 @@
|
||||
{
|
||||
"id": "openai/gpt-4.1-mini",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 0.40, "output": 1.60 },
|
||||
"cost_per_1m_tokens": { "input": 0.4, "output": 1.6 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 1000000
|
||||
},
|
||||
{
|
||||
"id": "openai/gpt-4.1-nano",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 0.10, "output": 0.40 },
|
||||
"cost_per_1m_tokens": { "input": 0.1, "output": 0.4 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 1000000
|
||||
},
|
||||
@@ -342,14 +342,14 @@
|
||||
{
|
||||
"id": "meta-llama/llama-4-maverick",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 0.18, "output": 0.60 },
|
||||
"cost_per_1m_tokens": { "input": 0.18, "output": 0.6 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 1000000
|
||||
},
|
||||
{
|
||||
"id": "meta-llama/llama-4-scout",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 0.08, "output": 0.30 },
|
||||
"cost_per_1m_tokens": { "input": 0.08, "output": 0.3 },
|
||||
"allowed_roles": ["main", "fallback"],
|
||||
"max_tokens": 1000000
|
||||
},
|
||||
@@ -391,7 +391,7 @@
|
||||
{
|
||||
"id": "mistralai/devstral-small",
|
||||
"swe_score": 0,
|
||||
"cost_per_1m_tokens": { "input": 0.10, "output": 0.30 },
|
||||
"cost_per_1m_tokens": { "input": 0.1, "output": 0.3 },
|
||||
"allowed_roles": ["main"],
|
||||
"max_tokens": 110000
|
||||
},
|
||||
|
||||
@@ -29,8 +29,12 @@ const AiTaskDataSchema = z.object({
|
||||
testStrategy: z
|
||||
.string()
|
||||
.describe('Detailed approach for verifying task completion'),
|
||||
dependencies: z.array(z.number()).optional()
|
||||
.describe('Array of task IDs that this task depends on (must be completed before this task can start)')
|
||||
dependencies: z
|
||||
.array(z.number())
|
||||
.optional()
|
||||
.describe(
|
||||
'Array of task IDs that this task depends on (must be completed before this task can start)'
|
||||
)
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -104,14 +108,20 @@ async function addTask(
|
||||
* @param {number} depth - Current depth in the recursion
|
||||
* @return {Object} Dependency graph data
|
||||
*/
|
||||
function buildDependencyGraph(tasks, taskId, visited = new Set(), depthMap = new Map(), depth = 0) {
|
||||
function buildDependencyGraph(
|
||||
tasks,
|
||||
taskId,
|
||||
visited = new Set(),
|
||||
depthMap = new Map(),
|
||||
depth = 0
|
||||
) {
|
||||
// Skip if we've already visited this task or it doesn't exist
|
||||
if (visited.has(taskId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Find the task
|
||||
const task = tasks.find(t => t.id === taskId);
|
||||
const task = tasks.find((t) => t.id === taskId);
|
||||
if (!task) {
|
||||
return null;
|
||||
}
|
||||
@@ -128,7 +138,13 @@ async function addTask(
|
||||
const dependencyData = [];
|
||||
if (task.dependencies && task.dependencies.length > 0) {
|
||||
for (const depId of task.dependencies) {
|
||||
const depData = buildDependencyGraph(tasks, depId, visited, depthMap, depth + 1);
|
||||
const depData = buildDependencyGraph(
|
||||
tasks,
|
||||
depId,
|
||||
visited,
|
||||
depthMap,
|
||||
depth + 1
|
||||
);
|
||||
if (depData) {
|
||||
dependencyData.push(depData);
|
||||
}
|
||||
@@ -204,7 +220,12 @@ async function addTask(
|
||||
|
||||
// First pass: build a complete dependency graph for each specified dependency
|
||||
for (const depId of numericDependencies) {
|
||||
const graph = buildDependencyGraph(data.tasks, depId, new Set(), depthMap);
|
||||
const graph = buildDependencyGraph(
|
||||
data.tasks,
|
||||
depId,
|
||||
new Set(),
|
||||
depthMap
|
||||
);
|
||||
if (graph) {
|
||||
dependencyGraphs.push(graph);
|
||||
}
|
||||
@@ -244,7 +265,7 @@ async function addTask(
|
||||
|
||||
// Create a dependency map for better understanding of the task relationships
|
||||
const taskMap = {};
|
||||
data.tasks.forEach(t => {
|
||||
data.tasks.forEach((t) => {
|
||||
// For each task, only include id, title, description, and dependencies
|
||||
taskMap[t.id] = {
|
||||
id: t.id,
|
||||
@@ -257,15 +278,14 @@ async function addTask(
|
||||
|
||||
// CLI-only feedback for the dependency analysis
|
||||
if (outputFormat === 'text') {
|
||||
console.log(boxen(
|
||||
chalk.cyan.bold('Task Context Analysis') + '\n',
|
||||
{
|
||||
console.log(
|
||||
boxen(chalk.cyan.bold('Task Context Analysis') + '\n', {
|
||||
padding: { top: 0, bottom: 0, left: 1, right: 1 },
|
||||
margin: { top: 0, bottom: 0 },
|
||||
borderColor: 'cyan',
|
||||
borderStyle: 'round'
|
||||
}
|
||||
));
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Initialize variables that will be used in either branch
|
||||
@@ -276,9 +296,9 @@ async function addTask(
|
||||
if (numericDependencies.length > 0) {
|
||||
// If specific dependencies were provided, focus on them
|
||||
// Get all tasks that were found in the dependency graph
|
||||
dependentTasks = Array.from(allRelatedTaskIds).map(id =>
|
||||
data.tasks.find(t => t.id === id)
|
||||
).filter(Boolean);
|
||||
dependentTasks = Array.from(allRelatedTaskIds)
|
||||
.map((id) => data.tasks.find((t) => t.id === id))
|
||||
.filter(Boolean);
|
||||
|
||||
// Sort by depth in the dependency chain
|
||||
dependentTasks.sort((a, b) => {
|
||||
@@ -291,14 +311,21 @@ async function addTask(
|
||||
uniqueDetailedTasks = dependentTasks.slice(0, 8);
|
||||
|
||||
contextTasks = `\nThis task relates to a dependency structure with ${dependentTasks.length} related tasks in the chain.\n\nDirect dependencies:`;
|
||||
const directDeps = data.tasks.filter(t => numericDependencies.includes(t.id));
|
||||
contextTasks += `\n${directDeps.map(t => `- Task ${t.id}: ${t.title} - ${t.description}`).join('\n')}`;
|
||||
const directDeps = data.tasks.filter((t) =>
|
||||
numericDependencies.includes(t.id)
|
||||
);
|
||||
contextTasks += `\n${directDeps.map((t) => `- Task ${t.id}: ${t.title} - ${t.description}`).join('\n')}`;
|
||||
|
||||
// Add an overview of indirect dependencies if present
|
||||
const indirectDeps = dependentTasks.filter(t => !numericDependencies.includes(t.id));
|
||||
const indirectDeps = dependentTasks.filter(
|
||||
(t) => !numericDependencies.includes(t.id)
|
||||
);
|
||||
if (indirectDeps.length > 0) {
|
||||
contextTasks += `\n\nIndirect dependencies (dependencies of dependencies):`;
|
||||
contextTasks += `\n${indirectDeps.slice(0, 5).map(t => `- Task ${t.id}: ${t.title} - ${t.description}`).join('\n')}`;
|
||||
contextTasks += `\n${indirectDeps
|
||||
.slice(0, 5)
|
||||
.map((t) => `- Task ${t.id}: ${t.title} - ${t.description}`)
|
||||
.join('\n')}`;
|
||||
if (indirectDeps.length > 5) {
|
||||
contextTasks += `\n- ... and ${indirectDeps.length - 5} more indirect dependencies`;
|
||||
}
|
||||
@@ -307,8 +334,12 @@ async function addTask(
|
||||
// Add more details about each dependency, prioritizing direct dependencies
|
||||
contextTasks += `\n\nDetailed information about dependencies:`;
|
||||
for (const depTask of uniqueDetailedTasks) {
|
||||
const depthInfo = depthMap.get(depTask.id) ? ` (depth: ${depthMap.get(depTask.id)})` : '';
|
||||
const isDirect = numericDependencies.includes(depTask.id) ? " [DIRECT DEPENDENCY]" : "";
|
||||
const depthInfo = depthMap.get(depTask.id)
|
||||
? ` (depth: ${depthMap.get(depTask.id)})`
|
||||
: '';
|
||||
const isDirect = numericDependencies.includes(depTask.id)
|
||||
? ' [DIRECT DEPENDENCY]'
|
||||
: '';
|
||||
|
||||
contextTasks += `\n\n------ Task ${depTask.id}${isDirect}${depthInfo}: ${depTask.title} ------\n`;
|
||||
contextTasks += `Description: ${depTask.description}\n`;
|
||||
@@ -317,9 +348,11 @@ async function addTask(
|
||||
|
||||
// List its dependencies
|
||||
if (depTask.dependencies && depTask.dependencies.length > 0) {
|
||||
const depDeps = depTask.dependencies.map(dId => {
|
||||
const depDepTask = data.tasks.find(t => t.id === dId);
|
||||
return depDepTask ? `Task ${dId}: ${depDepTask.title}` : `Task ${dId}`;
|
||||
const depDeps = depTask.dependencies.map((dId) => {
|
||||
const depDepTask = data.tasks.find((t) => t.id === dId);
|
||||
return depDepTask
|
||||
? `Task ${dId}: ${depDepTask.title}`
|
||||
: `Task ${dId}`;
|
||||
});
|
||||
contextTasks += `Dependencies: ${depDeps.join(', ')}\n`;
|
||||
} else {
|
||||
@@ -328,9 +361,10 @@ async function addTask(
|
||||
|
||||
// Add implementation details but truncate if too long
|
||||
if (depTask.details) {
|
||||
const truncatedDetails = depTask.details.length > 400
|
||||
? depTask.details.substring(0, 400) + '... (truncated)'
|
||||
: depTask.details;
|
||||
const truncatedDetails =
|
||||
depTask.details.length > 400
|
||||
? depTask.details.substring(0, 400) + '... (truncated)'
|
||||
: depTask.details;
|
||||
contextTasks += `Implementation Details: ${truncatedDetails}\n`;
|
||||
}
|
||||
}
|
||||
@@ -340,7 +374,12 @@ async function addTask(
|
||||
contextTasks += '\n\nDependency Chain Visualization:';
|
||||
|
||||
// Helper function to format dependency chain as text
|
||||
function formatDependencyChain(node, prefix = '', isLast = true, depth = 0) {
|
||||
function formatDependencyChain(
|
||||
node,
|
||||
prefix = '',
|
||||
isLast = true,
|
||||
depth = 0
|
||||
) {
|
||||
if (depth > 3) return ''; // Limit depth to avoid excessive nesting
|
||||
|
||||
const connector = isLast ? '└── ' : '├── ';
|
||||
@@ -373,19 +412,33 @@ async function addTask(
|
||||
if (outputFormat === 'text') {
|
||||
if (directDeps.length > 0) {
|
||||
console.log(chalk.gray(` Explicitly specified dependencies:`));
|
||||
directDeps.forEach(t => {
|
||||
console.log(chalk.yellow(` • Task ${t.id}: ${truncate(t.title, 50)}`));
|
||||
directDeps.forEach((t) => {
|
||||
console.log(
|
||||
chalk.yellow(` • Task ${t.id}: ${truncate(t.title, 50)}`)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
if (indirectDeps.length > 0) {
|
||||
console.log(chalk.gray(`\n Indirect dependencies (${indirectDeps.length} total):`));
|
||||
indirectDeps.slice(0, 3).forEach(t => {
|
||||
console.log(
|
||||
chalk.gray(
|
||||
`\n Indirect dependencies (${indirectDeps.length} total):`
|
||||
)
|
||||
);
|
||||
indirectDeps.slice(0, 3).forEach((t) => {
|
||||
const depth = depthMap.get(t.id) || 0;
|
||||
console.log(chalk.cyan(` • Task ${t.id} [depth ${depth}]: ${truncate(t.title, 45)}`));
|
||||
console.log(
|
||||
chalk.cyan(
|
||||
` • Task ${t.id} [depth ${depth}]: ${truncate(t.title, 45)}`
|
||||
)
|
||||
);
|
||||
});
|
||||
if (indirectDeps.length > 3) {
|
||||
console.log(chalk.cyan(` • ... and ${indirectDeps.length - 3} more indirect dependencies`));
|
||||
console.log(
|
||||
chalk.cyan(
|
||||
` • ... and ${indirectDeps.length - 3} more indirect dependencies`
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -394,13 +447,22 @@ async function addTask(
|
||||
console.log(chalk.gray(`\n Dependency chain visualization:`));
|
||||
|
||||
// Convert dependency graph to ASCII art for terminal
|
||||
function visualizeDependencyGraph(node, prefix = '', isLast = true, depth = 0) {
|
||||
function visualizeDependencyGraph(
|
||||
node,
|
||||
prefix = '',
|
||||
isLast = true,
|
||||
depth = 0
|
||||
) {
|
||||
if (depth > 2) return; // Limit depth for display
|
||||
|
||||
const connector = isLast ? '└── ' : '├── ';
|
||||
const childPrefix = isLast ? ' ' : '│ ';
|
||||
|
||||
console.log(chalk.blue(` ${prefix}${connector}Task ${node.id}: ${truncate(node.title, 40)}`));
|
||||
console.log(
|
||||
chalk.blue(
|
||||
` ${prefix}${connector}Task ${node.id}: ${truncate(node.title, 40)}`
|
||||
)
|
||||
);
|
||||
|
||||
if (node.dependencies && node.dependencies.length > 0) {
|
||||
for (let i = 0; i < node.dependencies.length; i++) {
|
||||
@@ -445,17 +507,18 @@ async function addTask(
|
||||
};
|
||||
|
||||
// Prepare task data with dependencies expanded as titles for better semantic search
|
||||
const searchableTasks = data.tasks.map(task => {
|
||||
const searchableTasks = data.tasks.map((task) => {
|
||||
// Get titles of this task's dependencies if they exist
|
||||
const dependencyTitles = task.dependencies?.length > 0
|
||||
? task.dependencies
|
||||
.map(depId => {
|
||||
const depTask = data.tasks.find(t => t.id === depId);
|
||||
return depTask ? depTask.title : '';
|
||||
})
|
||||
.filter(title => title)
|
||||
.join(' ')
|
||||
: '';
|
||||
const dependencyTitles =
|
||||
task.dependencies?.length > 0
|
||||
? task.dependencies
|
||||
.map((depId) => {
|
||||
const depTask = data.tasks.find((t) => t.id === depId);
|
||||
return depTask ? depTask.title : '';
|
||||
})
|
||||
.filter((title) => title)
|
||||
.join(' ')
|
||||
: '';
|
||||
|
||||
return {
|
||||
...task,
|
||||
@@ -467,10 +530,11 @@ async function addTask(
|
||||
const fuse = new Fuse(searchableTasks, searchOptions);
|
||||
|
||||
// Extract significant words and phrases from the prompt
|
||||
const promptWords = prompt.toLowerCase()
|
||||
const promptWords = prompt
|
||||
.toLowerCase()
|
||||
.replace(/[^\w\s-]/g, ' ') // Replace non-alphanumeric chars with spaces
|
||||
.split(/\s+/)
|
||||
.filter(word => word.length > 3); // Words at least 4 chars
|
||||
.filter((word) => word.length > 3); // Words at least 4 chars
|
||||
|
||||
// Use the user's prompt for fuzzy search
|
||||
const fuzzyResults = fuse.search(prompt);
|
||||
@@ -478,7 +542,8 @@ async function addTask(
|
||||
// Also search for each significant word to catch different aspects
|
||||
let wordResults = [];
|
||||
for (const word of promptWords) {
|
||||
if (word.length > 5) { // Only use significant words
|
||||
if (word.length > 5) {
|
||||
// Only use significant words
|
||||
const results = fuse.search(word);
|
||||
if (results.length > 0) {
|
||||
wordResults.push(...results);
|
||||
@@ -491,19 +556,19 @@ async function addTask(
|
||||
|
||||
// Add word results that aren't already in fuzzyResults
|
||||
for (const wordResult of wordResults) {
|
||||
if (!mergedResults.some(r => r.item.id === wordResult.item.id)) {
|
||||
if (!mergedResults.some((r) => r.item.id === wordResult.item.id)) {
|
||||
mergedResults.push(wordResult);
|
||||
}
|
||||
}
|
||||
|
||||
// Group search results by relevance
|
||||
const highRelevance = mergedResults
|
||||
.filter(result => result.score < 0.25)
|
||||
.map(result => result.item);
|
||||
.filter((result) => result.score < 0.25)
|
||||
.map((result) => result.item);
|
||||
|
||||
const mediumRelevance = mergedResults
|
||||
.filter(result => result.score >= 0.25 && result.score < 0.4)
|
||||
.map(result => result.item);
|
||||
.filter((result) => result.score >= 0.25 && result.score < 0.4)
|
||||
.map((result) => result.item);
|
||||
|
||||
// Get recent tasks (newest first)
|
||||
const recentTasks = [...data.tasks]
|
||||
@@ -516,14 +581,14 @@ async function addTask(
|
||||
|
||||
// Add medium relevance if not already included
|
||||
for (const task of mediumRelevance) {
|
||||
if (!allRelevantTasks.some(t => t.id === task.id)) {
|
||||
if (!allRelevantTasks.some((t) => t.id === task.id)) {
|
||||
allRelevantTasks.push(task);
|
||||
}
|
||||
}
|
||||
|
||||
// Add recent tasks if not already included
|
||||
for (const task of recentTasks) {
|
||||
if (!allRelevantTasks.some(t => t.id === task.id)) {
|
||||
if (!allRelevantTasks.some((t) => t.id === task.id)) {
|
||||
allRelevantTasks.push(task);
|
||||
}
|
||||
}
|
||||
@@ -533,33 +598,35 @@ async function addTask(
|
||||
|
||||
// Also look for tasks with similar purposes or categories
|
||||
const purposeCategories = [
|
||||
{pattern: /(command|cli|flag)/i, label: "CLI commands"},
|
||||
{pattern: /(task|subtask|add)/i, label: "Task management"},
|
||||
{pattern: /(dependency|depend)/i, label: "Dependency handling"},
|
||||
{pattern: /(AI|model|prompt)/i, label: "AI integration"},
|
||||
{pattern: /(UI|display|show)/i, label: "User interface"},
|
||||
{pattern: /(schedule|time|cron)/i, label: "Scheduling"}, // Added scheduling category
|
||||
{pattern: /(config|setting|option)/i, label: "Configuration"} // Added configuration category
|
||||
{ pattern: /(command|cli|flag)/i, label: 'CLI commands' },
|
||||
{ pattern: /(task|subtask|add)/i, label: 'Task management' },
|
||||
{ pattern: /(dependency|depend)/i, label: 'Dependency handling' },
|
||||
{ pattern: /(AI|model|prompt)/i, label: 'AI integration' },
|
||||
{ pattern: /(UI|display|show)/i, label: 'User interface' },
|
||||
{ pattern: /(schedule|time|cron)/i, label: 'Scheduling' }, // Added scheduling category
|
||||
{ pattern: /(config|setting|option)/i, label: 'Configuration' } // Added configuration category
|
||||
];
|
||||
|
||||
promptCategory = purposeCategories.find(cat => cat.pattern.test(prompt));
|
||||
promptCategory = purposeCategories.find((cat) =>
|
||||
cat.pattern.test(prompt)
|
||||
);
|
||||
const categoryTasks = promptCategory
|
||||
? data.tasks.filter(t =>
|
||||
promptCategory.pattern.test(t.title) ||
|
||||
promptCategory.pattern.test(t.description) ||
|
||||
(t.details && promptCategory.pattern.test(t.details))
|
||||
)
|
||||
.filter(t =>
|
||||
!relatedTasks.some(rt => rt.id === t.id)
|
||||
)
|
||||
.slice(0, 3)
|
||||
? data.tasks
|
||||
.filter(
|
||||
(t) =>
|
||||
promptCategory.pattern.test(t.title) ||
|
||||
promptCategory.pattern.test(t.description) ||
|
||||
(t.details && promptCategory.pattern.test(t.details))
|
||||
)
|
||||
.filter((t) => !relatedTasks.some((rt) => rt.id === t.id))
|
||||
.slice(0, 3)
|
||||
: [];
|
||||
|
||||
// Format basic task overviews
|
||||
if (relatedTasks.length > 0) {
|
||||
contextTasks = `\nRelevant tasks identified by semantic similarity:\n${relatedTasks
|
||||
.map((t, i) => {
|
||||
const relevanceMarker = i < highRelevance.length ? "⭐ " : "";
|
||||
const relevanceMarker = i < highRelevance.length ? '⭐ ' : '';
|
||||
return `- ${relevanceMarker}Task ${t.id}: ${t.title} - ${t.description}`;
|
||||
})
|
||||
.join('\n')}`;
|
||||
@@ -571,17 +638,25 @@ async function addTask(
|
||||
.join('\n')}`;
|
||||
}
|
||||
|
||||
if (recentTasks.length > 0 && !contextTasks.includes('Recently created tasks')) {
|
||||
if (
|
||||
recentTasks.length > 0 &&
|
||||
!contextTasks.includes('Recently created tasks')
|
||||
) {
|
||||
contextTasks += `\n\nRecently created tasks:\n${recentTasks
|
||||
.filter(t => !relatedTasks.some(rt => rt.id === t.id))
|
||||
.filter((t) => !relatedTasks.some((rt) => rt.id === t.id))
|
||||
.slice(0, 3)
|
||||
.map((t) => `- Task ${t.id}: ${t.title} - ${t.description}`)
|
||||
.join('\n')}`;
|
||||
}
|
||||
|
||||
// Add detailed information about the most relevant tasks
|
||||
const allDetailedTasks = [...relatedTasks.slice(0, 5), ...categoryTasks.slice(0, 2)];
|
||||
uniqueDetailedTasks = Array.from(new Map(allDetailedTasks.map(t => [t.id, t])).values()).slice(0, 8);
|
||||
const allDetailedTasks = [
|
||||
...relatedTasks.slice(0, 5),
|
||||
...categoryTasks.slice(0, 2)
|
||||
];
|
||||
uniqueDetailedTasks = Array.from(
|
||||
new Map(allDetailedTasks.map((t) => [t.id, t])).values()
|
||||
).slice(0, 8);
|
||||
|
||||
if (uniqueDetailedTasks.length > 0) {
|
||||
contextTasks += `\n\nDetailed information about relevant tasks:`;
|
||||
@@ -592,17 +667,20 @@ async function addTask(
|
||||
contextTasks += `Priority: ${task.priority || 'medium'}\n`;
|
||||
if (task.dependencies && task.dependencies.length > 0) {
|
||||
// Format dependency list with titles
|
||||
const depList = task.dependencies.map(depId => {
|
||||
const depTask = data.tasks.find(t => t.id === depId);
|
||||
return depTask ? `Task ${depId} (${depTask.title})` : `Task ${depId}`;
|
||||
const depList = task.dependencies.map((depId) => {
|
||||
const depTask = data.tasks.find((t) => t.id === depId);
|
||||
return depTask
|
||||
? `Task ${depId} (${depTask.title})`
|
||||
: `Task ${depId}`;
|
||||
});
|
||||
contextTasks += `Dependencies: ${depList.join(', ')}\n`;
|
||||
}
|
||||
// Add implementation details but truncate if too long
|
||||
if (task.details) {
|
||||
const truncatedDetails = task.details.length > 400
|
||||
? task.details.substring(0, 400) + '... (truncated)'
|
||||
: task.details;
|
||||
const truncatedDetails =
|
||||
task.details.length > 400
|
||||
? task.details.substring(0, 400) + '... (truncated)'
|
||||
: task.details;
|
||||
contextTasks += `Implementation Details: ${truncatedDetails}\n`;
|
||||
}
|
||||
}
|
||||
@@ -613,32 +691,36 @@ async function addTask(
|
||||
|
||||
// Get pending/in-progress tasks that might be most relevant based on fuzzy search
|
||||
// Prioritize tasks from our similarity search
|
||||
const relevantTaskIds = new Set(uniqueDetailedTasks.map(t => t.id));
|
||||
const relevantTaskIds = new Set(uniqueDetailedTasks.map((t) => t.id));
|
||||
const relevantPendingTasks = data.tasks
|
||||
.filter(t =>
|
||||
(t.status === 'pending' || t.status === 'in-progress') &&
|
||||
// Either in our relevant set OR has relevant words in title/description
|
||||
(relevantTaskIds.has(t.id) ||
|
||||
promptWords.some(word =>
|
||||
t.title.toLowerCase().includes(word) ||
|
||||
t.description.toLowerCase().includes(word)
|
||||
))
|
||||
.filter(
|
||||
(t) =>
|
||||
(t.status === 'pending' || t.status === 'in-progress') &&
|
||||
// Either in our relevant set OR has relevant words in title/description
|
||||
(relevantTaskIds.has(t.id) ||
|
||||
promptWords.some(
|
||||
(word) =>
|
||||
t.title.toLowerCase().includes(word) ||
|
||||
t.description.toLowerCase().includes(word)
|
||||
))
|
||||
)
|
||||
.slice(0, 10);
|
||||
|
||||
for (const task of relevantPendingTasks) {
|
||||
const depsStr = task.dependencies && task.dependencies.length > 0
|
||||
? task.dependencies.join(', ')
|
||||
: 'None';
|
||||
const depsStr =
|
||||
task.dependencies && task.dependencies.length > 0
|
||||
? task.dependencies.join(', ')
|
||||
: 'None';
|
||||
contextTasks += `\n- Task ${task.id}: depends on [${depsStr}]`;
|
||||
}
|
||||
|
||||
// Additional analysis of common patterns
|
||||
const similarPurposeTasks = promptCategory
|
||||
? data.tasks.filter(t =>
|
||||
promptCategory.pattern.test(t.title) ||
|
||||
promptCategory.pattern.test(t.description)
|
||||
)
|
||||
? data.tasks.filter(
|
||||
(t) =>
|
||||
promptCategory.pattern.test(t.title) ||
|
||||
promptCategory.pattern.test(t.description)
|
||||
)
|
||||
: [];
|
||||
|
||||
let commonDeps = []; // Initialize commonDeps
|
||||
@@ -648,13 +730,13 @@ async function addTask(
|
||||
|
||||
// Collect dependencies from similar purpose tasks
|
||||
const similarDeps = similarPurposeTasks
|
||||
.filter(t => t.dependencies && t.dependencies.length > 0)
|
||||
.map(t => t.dependencies)
|
||||
.filter((t) => t.dependencies && t.dependencies.length > 0)
|
||||
.map((t) => t.dependencies)
|
||||
.flat();
|
||||
|
||||
// Count frequency of each dependency
|
||||
const depCounts = {};
|
||||
similarDeps.forEach(dep => {
|
||||
similarDeps.forEach((dep) => {
|
||||
depCounts[dep] = (depCounts[dep] || 0) + 1;
|
||||
});
|
||||
|
||||
@@ -666,7 +748,7 @@ async function addTask(
|
||||
if (commonDeps.length > 0) {
|
||||
contextTasks += '\nMost common dependencies for similar tasks:';
|
||||
commonDeps.forEach(([depId, count]) => {
|
||||
const depTask = data.tasks.find(t => t.id === parseInt(depId));
|
||||
const depTask = data.tasks.find((t) => t.id === parseInt(depId));
|
||||
if (depTask) {
|
||||
contextTasks += `\n- Task ${depId} (used by ${count} similar tasks): ${depTask.title}`;
|
||||
}
|
||||
@@ -676,47 +758,79 @@ async function addTask(
|
||||
|
||||
// Show fuzzy search analysis in CLI mode
|
||||
if (outputFormat === 'text') {
|
||||
console.log(chalk.gray(` Fuzzy search across ${data.tasks.length} tasks using full prompt and ${promptWords.length} keywords`));
|
||||
console.log(
|
||||
chalk.gray(
|
||||
` Fuzzy search across ${data.tasks.length} tasks using full prompt and ${promptWords.length} keywords`
|
||||
)
|
||||
);
|
||||
|
||||
if (highRelevance.length > 0) {
|
||||
console.log(chalk.gray(`\n High relevance matches (score < 0.25):`));
|
||||
highRelevance.slice(0, 5).forEach(t => {
|
||||
console.log(chalk.yellow(` • ⭐ Task ${t.id}: ${truncate(t.title, 50)}`));
|
||||
console.log(
|
||||
chalk.gray(`\n High relevance matches (score < 0.25):`)
|
||||
);
|
||||
highRelevance.slice(0, 5).forEach((t) => {
|
||||
console.log(
|
||||
chalk.yellow(` • ⭐ Task ${t.id}: ${truncate(t.title, 50)}`)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
if (mediumRelevance.length > 0) {
|
||||
console.log(chalk.gray(`\n Medium relevance matches (score < 0.4):`));
|
||||
mediumRelevance.slice(0, 3).forEach(t => {
|
||||
console.log(chalk.green(` • Task ${t.id}: ${truncate(t.title, 50)}`));
|
||||
console.log(
|
||||
chalk.gray(`\n Medium relevance matches (score < 0.4):`)
|
||||
);
|
||||
mediumRelevance.slice(0, 3).forEach((t) => {
|
||||
console.log(
|
||||
chalk.green(` • Task ${t.id}: ${truncate(t.title, 50)}`)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
if (promptCategory && categoryTasks.length > 0) {
|
||||
console.log(chalk.gray(`\n Tasks related to ${promptCategory.label}:`));
|
||||
categoryTasks.forEach(t => {
|
||||
console.log(chalk.magenta(` • Task ${t.id}: ${truncate(t.title, 50)}`));
|
||||
console.log(
|
||||
chalk.gray(`\n Tasks related to ${promptCategory.label}:`)
|
||||
);
|
||||
categoryTasks.forEach((t) => {
|
||||
console.log(
|
||||
chalk.magenta(` • Task ${t.id}: ${truncate(t.title, 50)}`)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Show dependency patterns
|
||||
if (commonDeps && commonDeps.length > 0) {
|
||||
console.log(chalk.gray(`\n Common dependency patterns for similar tasks:`));
|
||||
console.log(
|
||||
chalk.gray(`\n Common dependency patterns for similar tasks:`)
|
||||
);
|
||||
commonDeps.slice(0, 3).forEach(([depId, count]) => {
|
||||
const depTask = data.tasks.find(t => t.id === parseInt(depId));
|
||||
const depTask = data.tasks.find((t) => t.id === parseInt(depId));
|
||||
if (depTask) {
|
||||
console.log(chalk.blue(` • Task ${depId} (${count}x): ${truncate(depTask.title, 45)}`));
|
||||
console.log(
|
||||
chalk.blue(
|
||||
` • Task ${depId} (${count}x): ${truncate(depTask.title, 45)}`
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Add information about which tasks will be provided in detail
|
||||
if (uniqueDetailedTasks.length > 0) {
|
||||
console.log(chalk.gray(`\n Providing detailed context for ${uniqueDetailedTasks.length} most relevant tasks:`));
|
||||
uniqueDetailedTasks.forEach(t => {
|
||||
const isHighRelevance = highRelevance.some(ht => ht.id === t.id);
|
||||
console.log(
|
||||
chalk.gray(
|
||||
`\n Providing detailed context for ${uniqueDetailedTasks.length} most relevant tasks:`
|
||||
)
|
||||
);
|
||||
uniqueDetailedTasks.forEach((t) => {
|
||||
const isHighRelevance = highRelevance.some(
|
||||
(ht) => ht.id === t.id
|
||||
);
|
||||
const relevanceIndicator = isHighRelevance ? '⭐ ' : '';
|
||||
console.log(chalk.cyan(` • ${relevanceIndicator}Task ${t.id}: ${truncate(t.title, 40)}`));
|
||||
console.log(
|
||||
chalk.cyan(
|
||||
` • ${relevanceIndicator}Task ${t.id}: ${truncate(t.title, 40)}`
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -729,48 +843,52 @@ async function addTask(
|
||||
if (numericDependencies.length > 0) {
|
||||
// In explicit dependency mode, we used 'uniqueDetailedTasks' derived from 'dependentTasks'
|
||||
// Ensure 'uniqueDetailedTasks' from THAT scope is used or re-evaluate.
|
||||
// For simplicity, let's assume 'dependentTasks' reflects the detailed tasks.
|
||||
// For simplicity, let's assume 'dependentTasks' reflects the detailed tasks.
|
||||
actualDetailedTasksCount = dependentTasks.length;
|
||||
} else {
|
||||
// In fuzzy search mode, 'uniqueDetailedTasks' from THIS scope is correct.
|
||||
actualDetailedTasksCount = uniqueDetailedTasks ? uniqueDetailedTasks.length : 0;
|
||||
actualDetailedTasksCount = uniqueDetailedTasks
|
||||
? uniqueDetailedTasks.length
|
||||
: 0;
|
||||
}
|
||||
|
||||
// Add a visual transition to show we're moving to AI generation
|
||||
console.log(boxen(
|
||||
chalk.white.bold('AI Task Generation') +
|
||||
`\n\n${chalk.gray('Analyzing context and generating task details using AI...')}` +
|
||||
`\n${chalk.cyan('Context size: ')}${chalk.yellow(contextTasks.length.toLocaleString())} characters` +
|
||||
`\n${chalk.cyan('Dependency detection: ')}${chalk.yellow(numericDependencies.length > 0 ? 'Explicit dependencies' : 'Auto-discovery mode')}` +
|
||||
`\n${chalk.cyan('Detailed tasks: ')}${chalk.yellow(
|
||||
numericDependencies.length > 0
|
||||
? dependentTasks.length // Use length of tasks from explicit dependency path
|
||||
: uniqueDetailedTasks.length // Use length of tasks from fuzzy search path
|
||||
)}` +
|
||||
(promptCategory
|
||||
? `\n${chalk.cyan('Category detected: ')}${chalk.yellow(promptCategory.label)}`
|
||||
: ''),
|
||||
{
|
||||
padding: { top: 0, bottom: 1, left: 1, right: 1 },
|
||||
margin: { top: 1, bottom: 0 },
|
||||
borderColor: 'white',
|
||||
borderStyle: 'round'
|
||||
}
|
||||
));
|
||||
console.log(
|
||||
boxen(
|
||||
chalk.white.bold('AI Task Generation') +
|
||||
`\n\n${chalk.gray('Analyzing context and generating task details using AI...')}` +
|
||||
`\n${chalk.cyan('Context size: ')}${chalk.yellow(contextTasks.length.toLocaleString())} characters` +
|
||||
`\n${chalk.cyan('Dependency detection: ')}${chalk.yellow(numericDependencies.length > 0 ? 'Explicit dependencies' : 'Auto-discovery mode')}` +
|
||||
`\n${chalk.cyan('Detailed tasks: ')}${chalk.yellow(
|
||||
numericDependencies.length > 0
|
||||
? dependentTasks.length // Use length of tasks from explicit dependency path
|
||||
: uniqueDetailedTasks.length // Use length of tasks from fuzzy search path
|
||||
)}` +
|
||||
(promptCategory
|
||||
? `\n${chalk.cyan('Category detected: ')}${chalk.yellow(promptCategory.label)}`
|
||||
: ''),
|
||||
{
|
||||
padding: { top: 0, bottom: 1, left: 1, right: 1 },
|
||||
margin: { top: 1, bottom: 0 },
|
||||
borderColor: 'white',
|
||||
borderStyle: 'round'
|
||||
}
|
||||
)
|
||||
);
|
||||
console.log(); // Add spacing
|
||||
|
||||
// System Prompt - Enhanced for dependency awareness
|
||||
const systemPrompt =
|
||||
"You are a helpful assistant that creates well-structured tasks for a software development project. Generate a single new task based on the user's description, adhering strictly to the provided JSON schema. Pay special attention to dependencies between tasks, ensuring the new task correctly references any tasks it depends on.\n\n" +
|
||||
"When determining dependencies for a new task, follow these principles:\n" +
|
||||
"1. Select dependencies based on logical requirements - what must be completed before this task can begin.\n" +
|
||||
"2. Prioritize task dependencies that are semantically related to the functionality being built.\n" +
|
||||
"3. Consider both direct dependencies (immediately prerequisite) and indirect dependencies.\n" +
|
||||
"4. Avoid adding unnecessary dependencies - only include tasks that are genuinely prerequisite.\n" +
|
||||
"5. Consider the current status of tasks - prefer completed tasks as dependencies when possible.\n" +
|
||||
'When determining dependencies for a new task, follow these principles:\n' +
|
||||
'1. Select dependencies based on logical requirements - what must be completed before this task can begin.\n' +
|
||||
'2. Prioritize task dependencies that are semantically related to the functionality being built.\n' +
|
||||
'3. Consider both direct dependencies (immediately prerequisite) and indirect dependencies.\n' +
|
||||
'4. Avoid adding unnecessary dependencies - only include tasks that are genuinely prerequisite.\n' +
|
||||
'5. Consider the current status of tasks - prefer completed tasks as dependencies when possible.\n' +
|
||||
"6. Pay special attention to foundation tasks (1-5) but don't automatically include them without reason.\n" +
|
||||
"7. Recent tasks (higher ID numbers) may be more relevant for newer functionality.\n\n" +
|
||||
"The dependencies array should contain task IDs (numbers) of prerequisite tasks.\n";
|
||||
'7. Recent tasks (higher ID numbers) may be more relevant for newer functionality.\n\n' +
|
||||
'The dependencies array should contain task IDs (numbers) of prerequisite tasks.\n';
|
||||
|
||||
// Task Structure Description (for user prompt)
|
||||
const taskStructureDesc = `
|
||||
@@ -879,23 +997,28 @@ async function addTask(
|
||||
details: taskData.details || '',
|
||||
testStrategy: taskData.testStrategy || '',
|
||||
status: 'pending',
|
||||
dependencies: taskData.dependencies?.length ? taskData.dependencies : numericDependencies, // Use AI-suggested dependencies if available, fallback to manually specified
|
||||
dependencies: taskData.dependencies?.length
|
||||
? taskData.dependencies
|
||||
: numericDependencies, // Use AI-suggested dependencies if available, fallback to manually specified
|
||||
priority: effectivePriority,
|
||||
subtasks: [] // Initialize with empty subtasks array
|
||||
};
|
||||
|
||||
// Additional check: validate all dependencies in the AI response
|
||||
if (taskData.dependencies?.length) {
|
||||
const allValidDeps = taskData.dependencies.every(depId => {
|
||||
const allValidDeps = taskData.dependencies.every((depId) => {
|
||||
const numDepId = parseInt(depId, 10);
|
||||
return !isNaN(numDepId) && data.tasks.some(t => t.id === numDepId);
|
||||
return !isNaN(numDepId) && data.tasks.some((t) => t.id === numDepId);
|
||||
});
|
||||
|
||||
if (!allValidDeps) {
|
||||
report('AI suggested invalid dependencies. Filtering them out...', 'warn');
|
||||
newTask.dependencies = taskData.dependencies.filter(depId => {
|
||||
report(
|
||||
'AI suggested invalid dependencies. Filtering them out...',
|
||||
'warn'
|
||||
);
|
||||
newTask.dependencies = taskData.dependencies.filter((depId) => {
|
||||
const numDepId = parseInt(depId, 10);
|
||||
return !isNaN(numDepId) && data.tasks.some(t => t.id === numDepId);
|
||||
return !isNaN(numDepId) && data.tasks.some((t) => t.id === numDepId);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -950,18 +1073,18 @@ async function addTask(
|
||||
|
||||
// Check if AI added new dependencies that weren't explicitly provided
|
||||
const aiAddedDeps = newTask.dependencies.filter(
|
||||
dep => !numericDependencies.includes(dep)
|
||||
(dep) => !numericDependencies.includes(dep)
|
||||
);
|
||||
|
||||
// Check if AI removed any dependencies that were explicitly provided
|
||||
const aiRemovedDeps = numericDependencies.filter(
|
||||
dep => !newTask.dependencies.includes(dep)
|
||||
(dep) => !newTask.dependencies.includes(dep)
|
||||
);
|
||||
|
||||
// Get task titles for dependencies to display
|
||||
const depTitles = {};
|
||||
newTask.dependencies.forEach(dep => {
|
||||
const depTask = data.tasks.find(t => t.id === dep);
|
||||
newTask.dependencies.forEach((dep) => {
|
||||
const depTask = data.tasks.find((t) => t.id === dep);
|
||||
if (depTask) {
|
||||
depTitles[dep] = truncate(depTask.title, 30);
|
||||
}
|
||||
@@ -971,10 +1094,13 @@ async function addTask(
|
||||
let dependencyDisplay = '';
|
||||
if (newTask.dependencies.length > 0) {
|
||||
dependencyDisplay = chalk.white('Dependencies:') + '\n';
|
||||
newTask.dependencies.forEach(dep => {
|
||||
newTask.dependencies.forEach((dep) => {
|
||||
const isAiAdded = aiAddedDeps.includes(dep);
|
||||
const depType = isAiAdded ? chalk.yellow(' (AI suggested)') : '';
|
||||
dependencyDisplay += chalk.white(` - ${dep}: ${depTitles[dep] || 'Unknown task'}${depType}`) + '\n';
|
||||
dependencyDisplay +=
|
||||
chalk.white(
|
||||
` - ${dep}: ${depTitles[dep] || 'Unknown task'}${depType}`
|
||||
) + '\n';
|
||||
});
|
||||
} else {
|
||||
dependencyDisplay = chalk.white('Dependencies: None') + '\n';
|
||||
@@ -982,9 +1108,11 @@ async function addTask(
|
||||
|
||||
// Add info about removed dependencies if any
|
||||
if (aiRemovedDeps.length > 0) {
|
||||
dependencyDisplay += chalk.gray('\nUser-specified dependencies that were not used:') + '\n';
|
||||
aiRemovedDeps.forEach(dep => {
|
||||
const depTask = data.tasks.find(t => t.id === dep);
|
||||
dependencyDisplay +=
|
||||
chalk.gray('\nUser-specified dependencies that were not used:') +
|
||||
'\n';
|
||||
aiRemovedDeps.forEach((dep) => {
|
||||
const depTask = data.tasks.find((t) => t.id === dep);
|
||||
const title = depTask ? truncate(depTask.title, 30) : 'Unknown task';
|
||||
dependencyDisplay += chalk.gray(` - ${dep}: ${title}`) + '\n';
|
||||
});
|
||||
@@ -993,12 +1121,19 @@ async function addTask(
|
||||
// Add dependency analysis summary
|
||||
let dependencyAnalysis = '';
|
||||
if (aiAddedDeps.length > 0 || aiRemovedDeps.length > 0) {
|
||||
dependencyAnalysis = '\n' + chalk.white.bold('Dependency Analysis:') + '\n';
|
||||
dependencyAnalysis =
|
||||
'\n' + chalk.white.bold('Dependency Analysis:') + '\n';
|
||||
if (aiAddedDeps.length > 0) {
|
||||
dependencyAnalysis += chalk.green(`AI identified ${aiAddedDeps.length} additional dependencies`) + '\n';
|
||||
dependencyAnalysis +=
|
||||
chalk.green(
|
||||
`AI identified ${aiAddedDeps.length} additional dependencies`
|
||||
) + '\n';
|
||||
}
|
||||
if (aiRemovedDeps.length > 0) {
|
||||
dependencyAnalysis += chalk.yellow(`AI excluded ${aiRemovedDeps.length} user-provided dependencies`) + '\n';
|
||||
dependencyAnalysis +=
|
||||
chalk.yellow(
|
||||
`AI excluded ${aiRemovedDeps.length} user-provided dependencies`
|
||||
) + '\n';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,12 @@ async function analyzeTaskComplexity(options, context = {}) {
|
||||
const useResearch = options.research || false;
|
||||
const projectRoot = options.projectRoot;
|
||||
// New parameters for task ID filtering
|
||||
const specificIds = options.id ? options.id.split(',').map(id => parseInt(id.trim(), 10)).filter(id => !isNaN(id)) : null;
|
||||
const specificIds = options.id
|
||||
? options.id
|
||||
.split(',')
|
||||
.map((id) => parseInt(id.trim(), 10))
|
||||
.filter((id) => !isNaN(id))
|
||||
: null;
|
||||
const fromId = options.from !== undefined ? parseInt(options.from, 10) : null;
|
||||
const toId = options.to !== undefined ? parseInt(options.to, 10) : null;
|
||||
|
||||
@@ -131,31 +136,56 @@ async function analyzeTaskComplexity(options, context = {}) {
|
||||
|
||||
// Apply ID filtering if specified
|
||||
if (specificIds && specificIds.length > 0) {
|
||||
reportLog(`Filtering tasks by specific IDs: ${specificIds.join(', ')}`, 'info');
|
||||
filteredTasks = filteredTasks.filter(task => specificIds.includes(task.id));
|
||||
reportLog(
|
||||
`Filtering tasks by specific IDs: ${specificIds.join(', ')}`,
|
||||
'info'
|
||||
);
|
||||
filteredTasks = filteredTasks.filter((task) =>
|
||||
specificIds.includes(task.id)
|
||||
);
|
||||
|
||||
if (outputFormat === 'text') {
|
||||
if (filteredTasks.length === 0 && specificIds.length > 0) {
|
||||
console.log(chalk.yellow(`Warning: No active tasks found with IDs: ${specificIds.join(', ')}`));
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
`Warning: No active tasks found with IDs: ${specificIds.join(', ')}`
|
||||
)
|
||||
);
|
||||
} else if (filteredTasks.length < specificIds.length) {
|
||||
const foundIds = filteredTasks.map(t => t.id);
|
||||
const missingIds = specificIds.filter(id => !foundIds.includes(id));
|
||||
console.log(chalk.yellow(`Warning: Some requested task IDs were not found or are not active: ${missingIds.join(', ')}`));
|
||||
const foundIds = filteredTasks.map((t) => t.id);
|
||||
const missingIds = specificIds.filter(
|
||||
(id) => !foundIds.includes(id)
|
||||
);
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
`Warning: Some requested task IDs were not found or are not active: ${missingIds.join(', ')}`
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Apply range filtering if specified
|
||||
else if (fromId !== null || toId !== null) {
|
||||
const effectiveFromId = fromId !== null ? fromId : 1;
|
||||
const effectiveToId = toId !== null ? toId : Math.max(...originalData.tasks.map(t => t.id));
|
||||
const effectiveToId =
|
||||
toId !== null
|
||||
? toId
|
||||
: Math.max(...originalData.tasks.map((t) => t.id));
|
||||
|
||||
reportLog(`Filtering tasks by ID range: ${effectiveFromId} to ${effectiveToId}`, 'info');
|
||||
filteredTasks = filteredTasks.filter(task =>
|
||||
task.id >= effectiveFromId && task.id <= effectiveToId
|
||||
reportLog(
|
||||
`Filtering tasks by ID range: ${effectiveFromId} to ${effectiveToId}`,
|
||||
'info'
|
||||
);
|
||||
filteredTasks = filteredTasks.filter(
|
||||
(task) => task.id >= effectiveFromId && task.id <= effectiveToId
|
||||
);
|
||||
|
||||
if (outputFormat === 'text' && filteredTasks.length === 0) {
|
||||
console.log(chalk.yellow(`Warning: No active tasks found in range: ${effectiveFromId}-${effectiveToId}`));
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
`Warning: No active tasks found in range: ${effectiveFromId}-${effectiveToId}`
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,18 +228,26 @@ async function analyzeTaskComplexity(options, context = {}) {
|
||||
existingReport = readJSON(outputPath);
|
||||
reportLog(`Found existing complexity report at ${outputPath}`, 'info');
|
||||
|
||||
if (existingReport &&
|
||||
if (
|
||||
existingReport &&
|
||||
existingReport.complexityAnalysis &&
|
||||
Array.isArray(existingReport.complexityAnalysis)) {
|
||||
Array.isArray(existingReport.complexityAnalysis)
|
||||
) {
|
||||
// Create lookup map of existing analysis entries
|
||||
existingReport.complexityAnalysis.forEach(item => {
|
||||
existingReport.complexityAnalysis.forEach((item) => {
|
||||
existingAnalysisMap.set(item.taskId, item);
|
||||
});
|
||||
reportLog(`Existing report contains ${existingReport.complexityAnalysis.length} task analyses`, 'info');
|
||||
reportLog(
|
||||
`Existing report contains ${existingReport.complexityAnalysis.length} task analyses`,
|
||||
'info'
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (readError) {
|
||||
reportLog(`Warning: Could not read existing report: ${readError.message}`, 'warn');
|
||||
reportLog(
|
||||
`Warning: Could not read existing report: ${readError.message}`,
|
||||
'warn'
|
||||
);
|
||||
existingReport = null;
|
||||
existingAnalysisMap.clear();
|
||||
}
|
||||
@@ -217,9 +255,16 @@ async function analyzeTaskComplexity(options, context = {}) {
|
||||
if (tasksData.tasks.length === 0) {
|
||||
// If using ID filtering but no matching tasks, return existing report or empty
|
||||
if (existingReport && (specificIds || fromId !== null || toId !== null)) {
|
||||
reportLog(`No matching tasks found for analysis. Keeping existing report.`, 'info');
|
||||
reportLog(
|
||||
`No matching tasks found for analysis. Keeping existing report.`,
|
||||
'info'
|
||||
);
|
||||
if (outputFormat === 'text') {
|
||||
console.log(chalk.yellow(`No matching tasks found for analysis. Keeping existing report.`));
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
`No matching tasks found for analysis. Keeping existing report.`
|
||||
)
|
||||
);
|
||||
}
|
||||
return {
|
||||
report: existingReport,
|
||||
@@ -425,17 +470,26 @@ async function analyzeTaskComplexity(options, context = {}) {
|
||||
|
||||
if (existingReport && Array.isArray(existingReport.complexityAnalysis)) {
|
||||
// Create a map of task IDs that we just analyzed
|
||||
const analyzedTaskIds = new Set(complexityAnalysis.map(item => item.taskId));
|
||||
|
||||
// Keep existing entries that weren't in this analysis run
|
||||
const existingEntriesNotAnalyzed = existingReport.complexityAnalysis.filter(
|
||||
item => !analyzedTaskIds.has(item.taskId)
|
||||
const analyzedTaskIds = new Set(
|
||||
complexityAnalysis.map((item) => item.taskId)
|
||||
);
|
||||
|
||||
// Combine with new analysis
|
||||
finalComplexityAnalysis = [...existingEntriesNotAnalyzed, ...complexityAnalysis];
|
||||
// Keep existing entries that weren't in this analysis run
|
||||
const existingEntriesNotAnalyzed =
|
||||
existingReport.complexityAnalysis.filter(
|
||||
(item) => !analyzedTaskIds.has(item.taskId)
|
||||
);
|
||||
|
||||
reportLog(`Merged ${complexityAnalysis.length} new analyses with ${existingEntriesNotAnalyzed.length} existing entries`, 'info');
|
||||
// Combine with new analysis
|
||||
finalComplexityAnalysis = [
|
||||
...existingEntriesNotAnalyzed,
|
||||
...complexityAnalysis
|
||||
];
|
||||
|
||||
reportLog(
|
||||
`Merged ${complexityAnalysis.length} new analyses with ${existingEntriesNotAnalyzed.length} existing entries`,
|
||||
'info'
|
||||
);
|
||||
} else {
|
||||
// No existing report or invalid format, just use the new analysis
|
||||
finalComplexityAnalysis = complexityAnalysis;
|
||||
@@ -489,8 +543,12 @@ async function analyzeTaskComplexity(options, context = {}) {
|
||||
if (existingReport) {
|
||||
console.log('\nUpdated Report Summary:');
|
||||
console.log('----------------------------');
|
||||
console.log(`Total analyses in report: ${finalComplexityAnalysis.length}`);
|
||||
console.log(`Analyses from previous runs: ${finalComplexityAnalysis.length - totalAnalyzed}`);
|
||||
console.log(
|
||||
`Total analyses in report: ${finalComplexityAnalysis.length}`
|
||||
);
|
||||
console.log(
|
||||
`Analyses from previous runs: ${finalComplexityAnalysis.length - totalAnalyzed}`
|
||||
);
|
||||
console.log(`New/updated analyses: ${totalAnalyzed}`);
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ function generateTaskFiles(tasksPath, outputDir, options = {}) {
|
||||
validateAndFixDependencies(data, tasksPath);
|
||||
|
||||
// Get valid task IDs from tasks.json
|
||||
const validTaskIds = data.tasks.map(task => task.id);
|
||||
const validTaskIds = data.tasks.map((task) => task.id);
|
||||
|
||||
// Cleanup orphaned task files
|
||||
log('info', 'Checking for orphaned task files to clean up...');
|
||||
@@ -46,7 +46,7 @@ function generateTaskFiles(tasksPath, outputDir, options = {}) {
|
||||
const taskFilePattern = /^task_(\d+)\.txt$/;
|
||||
|
||||
// Filter for task files and check if they match a valid task ID
|
||||
const orphanedFiles = files.filter(file => {
|
||||
const orphanedFiles = files.filter((file) => {
|
||||
const match = file.match(taskFilePattern);
|
||||
if (match) {
|
||||
const fileTaskId = parseInt(match[1], 10);
|
||||
@@ -57,15 +57,21 @@ function generateTaskFiles(tasksPath, outputDir, options = {}) {
|
||||
|
||||
// Delete orphaned files
|
||||
if (orphanedFiles.length > 0) {
|
||||
log('info', `Found ${orphanedFiles.length} orphaned task files to remove`);
|
||||
log(
|
||||
'info',
|
||||
`Found ${orphanedFiles.length} orphaned task files to remove`
|
||||
);
|
||||
|
||||
orphanedFiles.forEach(file => {
|
||||
orphanedFiles.forEach((file) => {
|
||||
const filePath = path.join(outputDir, file);
|
||||
try {
|
||||
fs.unlinkSync(filePath);
|
||||
log('info', `Removed orphaned task file: ${file}`);
|
||||
} catch (err) {
|
||||
log('warn', `Failed to remove orphaned task file ${file}: ${err.message}`);
|
||||
log(
|
||||
'warn',
|
||||
`Failed to remove orphaned task file ${file}: ${err.message}`
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -12,209 +12,251 @@ import generateTaskFiles from './generate-task-files.js';
|
||||
* @returns {Object} Result object with moved task details
|
||||
*/
|
||||
async function moveTask(
|
||||
tasksPath,
|
||||
sourceId,
|
||||
destinationId,
|
||||
generateFiles = true
|
||||
tasksPath,
|
||||
sourceId,
|
||||
destinationId,
|
||||
generateFiles = true
|
||||
) {
|
||||
try {
|
||||
log('info', `Moving task/subtask ${sourceId} to ${destinationId}...`);
|
||||
try {
|
||||
log('info', `Moving task/subtask ${sourceId} to ${destinationId}...`);
|
||||
|
||||
// Read the existing tasks
|
||||
const data = readJSON(tasksPath);
|
||||
if (!data || !data.tasks) {
|
||||
throw new Error(`Invalid or missing tasks file at ${tasksPath}`);
|
||||
}
|
||||
// Read the existing tasks
|
||||
const data = readJSON(tasksPath);
|
||||
if (!data || !data.tasks) {
|
||||
throw new Error(`Invalid or missing tasks file at ${tasksPath}`);
|
||||
}
|
||||
|
||||
// Parse source ID to determine if it's a task or subtask
|
||||
const isSourceSubtask = sourceId.includes('.');
|
||||
let sourceTask, sourceParentTask, sourceSubtask, sourceTaskIndex, sourceSubtaskIndex;
|
||||
// Parse source ID to determine if it's a task or subtask
|
||||
const isSourceSubtask = sourceId.includes('.');
|
||||
let sourceTask,
|
||||
sourceParentTask,
|
||||
sourceSubtask,
|
||||
sourceTaskIndex,
|
||||
sourceSubtaskIndex;
|
||||
|
||||
// Parse destination ID to determine the target
|
||||
const isDestinationSubtask = destinationId.includes('.');
|
||||
let destTask, destParentTask, destSubtask, destTaskIndex, destSubtaskIndex;
|
||||
// Parse destination ID to determine the target
|
||||
const isDestinationSubtask = destinationId.includes('.');
|
||||
let destTask, destParentTask, destSubtask, destTaskIndex, destSubtaskIndex;
|
||||
|
||||
// Validate source exists
|
||||
if (isSourceSubtask) {
|
||||
// Source is a subtask
|
||||
const [parentIdStr, subtaskIdStr] = sourceId.split('.');
|
||||
const parentIdNum = parseInt(parentIdStr, 10);
|
||||
const subtaskIdNum = parseInt(subtaskIdStr, 10);
|
||||
// Validate source exists
|
||||
if (isSourceSubtask) {
|
||||
// Source is a subtask
|
||||
const [parentIdStr, subtaskIdStr] = sourceId.split('.');
|
||||
const parentIdNum = parseInt(parentIdStr, 10);
|
||||
const subtaskIdNum = parseInt(subtaskIdStr, 10);
|
||||
|
||||
sourceParentTask = data.tasks.find(t => t.id === parentIdNum);
|
||||
if (!sourceParentTask) {
|
||||
throw new Error(`Source parent task with ID ${parentIdNum} not found`);
|
||||
}
|
||||
sourceParentTask = data.tasks.find((t) => t.id === parentIdNum);
|
||||
if (!sourceParentTask) {
|
||||
throw new Error(`Source parent task with ID ${parentIdNum} not found`);
|
||||
}
|
||||
|
||||
if (!sourceParentTask.subtasks || sourceParentTask.subtasks.length === 0) {
|
||||
throw new Error(`Source parent task ${parentIdNum} has no subtasks`);
|
||||
}
|
||||
if (
|
||||
!sourceParentTask.subtasks ||
|
||||
sourceParentTask.subtasks.length === 0
|
||||
) {
|
||||
throw new Error(`Source parent task ${parentIdNum} has no subtasks`);
|
||||
}
|
||||
|
||||
sourceSubtaskIndex = sourceParentTask.subtasks.findIndex(
|
||||
st => st.id === subtaskIdNum
|
||||
);
|
||||
if (sourceSubtaskIndex === -1) {
|
||||
throw new Error(`Source subtask ${sourceId} not found`);
|
||||
}
|
||||
sourceSubtaskIndex = sourceParentTask.subtasks.findIndex(
|
||||
(st) => st.id === subtaskIdNum
|
||||
);
|
||||
if (sourceSubtaskIndex === -1) {
|
||||
throw new Error(`Source subtask ${sourceId} not found`);
|
||||
}
|
||||
|
||||
sourceSubtask = { ...sourceParentTask.subtasks[sourceSubtaskIndex] };
|
||||
} else {
|
||||
// Source is a task
|
||||
const sourceIdNum = parseInt(sourceId, 10);
|
||||
sourceTaskIndex = data.tasks.findIndex(t => t.id === sourceIdNum);
|
||||
if (sourceTaskIndex === -1) {
|
||||
throw new Error(`Source task with ID ${sourceIdNum} not found`);
|
||||
}
|
||||
sourceSubtask = { ...sourceParentTask.subtasks[sourceSubtaskIndex] };
|
||||
} else {
|
||||
// Source is a task
|
||||
const sourceIdNum = parseInt(sourceId, 10);
|
||||
sourceTaskIndex = data.tasks.findIndex((t) => t.id === sourceIdNum);
|
||||
if (sourceTaskIndex === -1) {
|
||||
throw new Error(`Source task with ID ${sourceIdNum} not found`);
|
||||
}
|
||||
|
||||
sourceTask = { ...data.tasks[sourceTaskIndex] };
|
||||
}
|
||||
sourceTask = { ...data.tasks[sourceTaskIndex] };
|
||||
}
|
||||
|
||||
// Validate destination exists
|
||||
if (isDestinationSubtask) {
|
||||
// Destination is a subtask (target will be the parent of this subtask)
|
||||
const [parentIdStr, subtaskIdStr] = destinationId.split('.');
|
||||
const parentIdNum = parseInt(parentIdStr, 10);
|
||||
const subtaskIdNum = parseInt(subtaskIdStr, 10);
|
||||
// Validate destination exists
|
||||
if (isDestinationSubtask) {
|
||||
// Destination is a subtask (target will be the parent of this subtask)
|
||||
const [parentIdStr, subtaskIdStr] = destinationId.split('.');
|
||||
const parentIdNum = parseInt(parentIdStr, 10);
|
||||
const subtaskIdNum = parseInt(subtaskIdStr, 10);
|
||||
|
||||
destParentTask = data.tasks.find(t => t.id === parentIdNum);
|
||||
if (!destParentTask) {
|
||||
throw new Error(`Destination parent task with ID ${parentIdNum} not found`);
|
||||
}
|
||||
destParentTask = data.tasks.find((t) => t.id === parentIdNum);
|
||||
if (!destParentTask) {
|
||||
throw new Error(
|
||||
`Destination parent task with ID ${parentIdNum} not found`
|
||||
);
|
||||
}
|
||||
|
||||
if (!destParentTask.subtasks || destParentTask.subtasks.length === 0) {
|
||||
throw new Error(`Destination parent task ${parentIdNum} has no subtasks`);
|
||||
}
|
||||
if (!destParentTask.subtasks || destParentTask.subtasks.length === 0) {
|
||||
throw new Error(
|
||||
`Destination parent task ${parentIdNum} has no subtasks`
|
||||
);
|
||||
}
|
||||
|
||||
destSubtaskIndex = destParentTask.subtasks.findIndex(
|
||||
st => st.id === subtaskIdNum
|
||||
);
|
||||
if (destSubtaskIndex === -1) {
|
||||
throw new Error(`Destination subtask ${destinationId} not found`);
|
||||
}
|
||||
destSubtaskIndex = destParentTask.subtasks.findIndex(
|
||||
(st) => st.id === subtaskIdNum
|
||||
);
|
||||
if (destSubtaskIndex === -1) {
|
||||
throw new Error(`Destination subtask ${destinationId} not found`);
|
||||
}
|
||||
|
||||
destSubtask = destParentTask.subtasks[destSubtaskIndex];
|
||||
} else {
|
||||
// Destination is a task
|
||||
const destIdNum = parseInt(destinationId, 10);
|
||||
destTaskIndex = data.tasks.findIndex(t => t.id === destIdNum);
|
||||
destSubtask = destParentTask.subtasks[destSubtaskIndex];
|
||||
} else {
|
||||
// Destination is a task
|
||||
const destIdNum = parseInt(destinationId, 10);
|
||||
destTaskIndex = data.tasks.findIndex((t) => t.id === destIdNum);
|
||||
|
||||
if (destTaskIndex === -1) {
|
||||
// Create placeholder for destination if it doesn't exist
|
||||
log('info', `Creating placeholder for destination task ${destIdNum}`);
|
||||
const newTask = {
|
||||
id: destIdNum,
|
||||
title: `Task ${destIdNum}`,
|
||||
description: '',
|
||||
status: 'pending',
|
||||
priority: 'medium',
|
||||
details: '',
|
||||
testStrategy: ''
|
||||
};
|
||||
if (destTaskIndex === -1) {
|
||||
// Create placeholder for destination if it doesn't exist
|
||||
log('info', `Creating placeholder for destination task ${destIdNum}`);
|
||||
const newTask = {
|
||||
id: destIdNum,
|
||||
title: `Task ${destIdNum}`,
|
||||
description: '',
|
||||
status: 'pending',
|
||||
priority: 'medium',
|
||||
details: '',
|
||||
testStrategy: ''
|
||||
};
|
||||
|
||||
// Find correct position to insert the new task
|
||||
let insertIndex = 0;
|
||||
while (insertIndex < data.tasks.length && data.tasks[insertIndex].id < destIdNum) {
|
||||
insertIndex++;
|
||||
}
|
||||
// Find correct position to insert the new task
|
||||
let insertIndex = 0;
|
||||
while (
|
||||
insertIndex < data.tasks.length &&
|
||||
data.tasks[insertIndex].id < destIdNum
|
||||
) {
|
||||
insertIndex++;
|
||||
}
|
||||
|
||||
// Insert the new task at the appropriate position
|
||||
data.tasks.splice(insertIndex, 0, newTask);
|
||||
destTaskIndex = insertIndex;
|
||||
destTask = data.tasks[destTaskIndex];
|
||||
} else {
|
||||
destTask = data.tasks[destTaskIndex];
|
||||
// Insert the new task at the appropriate position
|
||||
data.tasks.splice(insertIndex, 0, newTask);
|
||||
destTaskIndex = insertIndex;
|
||||
destTask = data.tasks[destTaskIndex];
|
||||
} else {
|
||||
destTask = data.tasks[destTaskIndex];
|
||||
|
||||
// Check if destination task is already a "real" task with content
|
||||
// Only allow moving to destination IDs that don't have meaningful content
|
||||
if (destTask.title !== `Task ${destTask.id}` ||
|
||||
destTask.description !== '' ||
|
||||
destTask.details !== '') {
|
||||
throw new Error(`Cannot move to task ID ${destIdNum} as it already contains content. Choose a different destination ID.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check if destination task is already a "real" task with content
|
||||
// Only allow moving to destination IDs that don't have meaningful content
|
||||
if (
|
||||
destTask.title !== `Task ${destTask.id}` ||
|
||||
destTask.description !== '' ||
|
||||
destTask.details !== ''
|
||||
) {
|
||||
throw new Error(
|
||||
`Cannot move to task ID ${destIdNum} as it already contains content. Choose a different destination ID.`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate that we aren't trying to move a task to itself
|
||||
if (sourceId === destinationId) {
|
||||
throw new Error('Cannot move a task/subtask to itself');
|
||||
}
|
||||
// Validate that we aren't trying to move a task to itself
|
||||
if (sourceId === destinationId) {
|
||||
throw new Error('Cannot move a task/subtask to itself');
|
||||
}
|
||||
|
||||
// Prevent moving a parent to its own subtask
|
||||
if (!isSourceSubtask && isDestinationSubtask) {
|
||||
const destParentId = parseInt(destinationId.split('.')[0], 10);
|
||||
if (parseInt(sourceId, 10) === destParentId) {
|
||||
throw new Error('Cannot move a parent task to one of its own subtasks');
|
||||
}
|
||||
}
|
||||
// Prevent moving a parent to its own subtask
|
||||
if (!isSourceSubtask && isDestinationSubtask) {
|
||||
const destParentId = parseInt(destinationId.split('.')[0], 10);
|
||||
if (parseInt(sourceId, 10) === destParentId) {
|
||||
throw new Error('Cannot move a parent task to one of its own subtasks');
|
||||
}
|
||||
}
|
||||
|
||||
// Check for circular dependency when moving tasks
|
||||
if (!isSourceSubtask && !isDestinationSubtask) {
|
||||
const sourceIdNum = parseInt(sourceId, 10);
|
||||
const destIdNum = parseInt(destinationId, 10);
|
||||
// Check for circular dependency when moving tasks
|
||||
if (!isSourceSubtask && !isDestinationSubtask) {
|
||||
const sourceIdNum = parseInt(sourceId, 10);
|
||||
const destIdNum = parseInt(destinationId, 10);
|
||||
|
||||
// Check if destination is dependent on source
|
||||
if (isTaskDependentOn(data.tasks, destTask, sourceIdNum)) {
|
||||
throw new Error(
|
||||
`Cannot move task ${sourceId} to task ${destinationId} as it would create a circular dependency`
|
||||
);
|
||||
}
|
||||
}
|
||||
// Check if destination is dependent on source
|
||||
if (isTaskDependentOn(data.tasks, destTask, sourceIdNum)) {
|
||||
throw new Error(
|
||||
`Cannot move task ${sourceId} to task ${destinationId} as it would create a circular dependency`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let movedTask;
|
||||
let movedTask;
|
||||
|
||||
// Handle different move scenarios
|
||||
if (!isSourceSubtask && !isDestinationSubtask) {
|
||||
// Check if destination is a placeholder we just created
|
||||
if (destTask.title === `Task ${destTask.id}` &&
|
||||
destTask.description === '' &&
|
||||
destTask.details === '') {
|
||||
// Case 0: Move task to a new position/ID (destination is a placeholder)
|
||||
movedTask = moveTaskToNewId(data, sourceTask, sourceTaskIndex, destTask, destTaskIndex);
|
||||
} else {
|
||||
// Case 1: Move standalone task to become a subtask of another task
|
||||
movedTask = moveTaskToTask(data, sourceTask, sourceTaskIndex, destTask);
|
||||
}
|
||||
} else if (!isSourceSubtask && isDestinationSubtask) {
|
||||
// Case 2: Move standalone task to become a subtask at a specific position
|
||||
movedTask = moveTaskToSubtaskPosition(data, sourceTask, sourceTaskIndex, destParentTask, destSubtaskIndex);
|
||||
} else if (isSourceSubtask && !isDestinationSubtask) {
|
||||
// Case 3: Move subtask to become a standalone task
|
||||
movedTask = moveSubtaskToTask(data, sourceSubtask, sourceParentTask, sourceSubtaskIndex, destTask);
|
||||
} else if (isSourceSubtask && isDestinationSubtask) {
|
||||
// Case 4: Move subtask to another parent or position
|
||||
// First check if it's the same parent
|
||||
const sourceParentId = parseInt(sourceId.split('.')[0], 10);
|
||||
const destParentId = parseInt(destinationId.split('.')[0], 10);
|
||||
// Handle different move scenarios
|
||||
if (!isSourceSubtask && !isDestinationSubtask) {
|
||||
// Check if destination is a placeholder we just created
|
||||
if (
|
||||
destTask.title === `Task ${destTask.id}` &&
|
||||
destTask.description === '' &&
|
||||
destTask.details === ''
|
||||
) {
|
||||
// Case 0: Move task to a new position/ID (destination is a placeholder)
|
||||
movedTask = moveTaskToNewId(
|
||||
data,
|
||||
sourceTask,
|
||||
sourceTaskIndex,
|
||||
destTask,
|
||||
destTaskIndex
|
||||
);
|
||||
} else {
|
||||
// Case 1: Move standalone task to become a subtask of another task
|
||||
movedTask = moveTaskToTask(data, sourceTask, sourceTaskIndex, destTask);
|
||||
}
|
||||
} else if (!isSourceSubtask && isDestinationSubtask) {
|
||||
// Case 2: Move standalone task to become a subtask at a specific position
|
||||
movedTask = moveTaskToSubtaskPosition(
|
||||
data,
|
||||
sourceTask,
|
||||
sourceTaskIndex,
|
||||
destParentTask,
|
||||
destSubtaskIndex
|
||||
);
|
||||
} else if (isSourceSubtask && !isDestinationSubtask) {
|
||||
// Case 3: Move subtask to become a standalone task
|
||||
movedTask = moveSubtaskToTask(
|
||||
data,
|
||||
sourceSubtask,
|
||||
sourceParentTask,
|
||||
sourceSubtaskIndex,
|
||||
destTask
|
||||
);
|
||||
} else if (isSourceSubtask && isDestinationSubtask) {
|
||||
// Case 4: Move subtask to another parent or position
|
||||
// First check if it's the same parent
|
||||
const sourceParentId = parseInt(sourceId.split('.')[0], 10);
|
||||
const destParentId = parseInt(destinationId.split('.')[0], 10);
|
||||
|
||||
if (sourceParentId === destParentId) {
|
||||
// Case 4a: Move subtask within the same parent (reordering)
|
||||
movedTask = reorderSubtask(sourceParentTask, sourceSubtaskIndex, destSubtaskIndex);
|
||||
} else {
|
||||
// Case 4b: Move subtask to a different parent
|
||||
movedTask = moveSubtaskToAnotherParent(
|
||||
sourceSubtask,
|
||||
sourceParentTask,
|
||||
sourceSubtaskIndex,
|
||||
destParentTask,
|
||||
destSubtaskIndex
|
||||
);
|
||||
}
|
||||
}
|
||||
if (sourceParentId === destParentId) {
|
||||
// Case 4a: Move subtask within the same parent (reordering)
|
||||
movedTask = reorderSubtask(
|
||||
sourceParentTask,
|
||||
sourceSubtaskIndex,
|
||||
destSubtaskIndex
|
||||
);
|
||||
} else {
|
||||
// Case 4b: Move subtask to a different parent
|
||||
movedTask = moveSubtaskToAnotherParent(
|
||||
sourceSubtask,
|
||||
sourceParentTask,
|
||||
sourceSubtaskIndex,
|
||||
destParentTask,
|
||||
destSubtaskIndex
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Write the updated tasks back to the file
|
||||
writeJSON(tasksPath, data);
|
||||
// Write the updated tasks back to the file
|
||||
writeJSON(tasksPath, data);
|
||||
|
||||
// Generate task files if requested
|
||||
if (generateFiles) {
|
||||
log('info', 'Regenerating task files...');
|
||||
await generateTaskFiles(tasksPath, path.dirname(tasksPath));
|
||||
}
|
||||
// Generate task files if requested
|
||||
if (generateFiles) {
|
||||
log('info', 'Regenerating task files...');
|
||||
await generateTaskFiles(tasksPath, path.dirname(tasksPath));
|
||||
}
|
||||
|
||||
return movedTask;
|
||||
} catch (error) {
|
||||
log('error', `Error moving task/subtask: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
return movedTask;
|
||||
} catch (error) {
|
||||
log('error', `Error moving task/subtask: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -226,37 +268,37 @@ async function moveTask(
|
||||
* @returns {Object} Moved task object
|
||||
*/
|
||||
function moveTaskToTask(data, sourceTask, sourceTaskIndex, destTask) {
|
||||
// Initialize subtasks array if it doesn't exist
|
||||
if (!destTask.subtasks) {
|
||||
destTask.subtasks = [];
|
||||
}
|
||||
// Initialize subtasks array if it doesn't exist
|
||||
if (!destTask.subtasks) {
|
||||
destTask.subtasks = [];
|
||||
}
|
||||
|
||||
// Find the highest subtask ID to determine the next ID
|
||||
const highestSubtaskId =
|
||||
destTask.subtasks.length > 0
|
||||
? Math.max(...destTask.subtasks.map(st => st.id))
|
||||
: 0;
|
||||
const newSubtaskId = highestSubtaskId + 1;
|
||||
// Find the highest subtask ID to determine the next ID
|
||||
const highestSubtaskId =
|
||||
destTask.subtasks.length > 0
|
||||
? Math.max(...destTask.subtasks.map((st) => st.id))
|
||||
: 0;
|
||||
const newSubtaskId = highestSubtaskId + 1;
|
||||
|
||||
// Create the new subtask from the source task
|
||||
const newSubtask = {
|
||||
...sourceTask,
|
||||
id: newSubtaskId,
|
||||
parentTaskId: destTask.id
|
||||
};
|
||||
// Create the new subtask from the source task
|
||||
const newSubtask = {
|
||||
...sourceTask,
|
||||
id: newSubtaskId,
|
||||
parentTaskId: destTask.id
|
||||
};
|
||||
|
||||
// Add to destination's subtasks
|
||||
destTask.subtasks.push(newSubtask);
|
||||
// Add to destination's subtasks
|
||||
destTask.subtasks.push(newSubtask);
|
||||
|
||||
// Remove the original task from the tasks array
|
||||
data.tasks.splice(sourceTaskIndex, 1);
|
||||
// Remove the original task from the tasks array
|
||||
data.tasks.splice(sourceTaskIndex, 1);
|
||||
|
||||
log(
|
||||
'info',
|
||||
`Moved task ${sourceTask.id} to become subtask ${destTask.id}.${newSubtaskId}`
|
||||
);
|
||||
log(
|
||||
'info',
|
||||
`Moved task ${sourceTask.id} to become subtask ${destTask.id}.${newSubtaskId}`
|
||||
);
|
||||
|
||||
return newSubtask;
|
||||
return newSubtask;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -268,38 +310,44 @@ function moveTaskToTask(data, sourceTask, sourceTaskIndex, destTask) {
|
||||
* @param {number} destSubtaskIndex - Index of the subtask before which to insert
|
||||
* @returns {Object} Moved task object
|
||||
*/
|
||||
function moveTaskToSubtaskPosition(data, sourceTask, sourceTaskIndex, destParentTask, destSubtaskIndex) {
|
||||
// Initialize subtasks array if it doesn't exist
|
||||
if (!destParentTask.subtasks) {
|
||||
destParentTask.subtasks = [];
|
||||
}
|
||||
function moveTaskToSubtaskPosition(
|
||||
data,
|
||||
sourceTask,
|
||||
sourceTaskIndex,
|
||||
destParentTask,
|
||||
destSubtaskIndex
|
||||
) {
|
||||
// Initialize subtasks array if it doesn't exist
|
||||
if (!destParentTask.subtasks) {
|
||||
destParentTask.subtasks = [];
|
||||
}
|
||||
|
||||
// Find the highest subtask ID to determine the next ID
|
||||
const highestSubtaskId =
|
||||
destParentTask.subtasks.length > 0
|
||||
? Math.max(...destParentTask.subtasks.map(st => st.id))
|
||||
: 0;
|
||||
const newSubtaskId = highestSubtaskId + 1;
|
||||
// Find the highest subtask ID to determine the next ID
|
||||
const highestSubtaskId =
|
||||
destParentTask.subtasks.length > 0
|
||||
? Math.max(...destParentTask.subtasks.map((st) => st.id))
|
||||
: 0;
|
||||
const newSubtaskId = highestSubtaskId + 1;
|
||||
|
||||
// Create the new subtask from the source task
|
||||
const newSubtask = {
|
||||
...sourceTask,
|
||||
id: newSubtaskId,
|
||||
parentTaskId: destParentTask.id
|
||||
};
|
||||
// Create the new subtask from the source task
|
||||
const newSubtask = {
|
||||
...sourceTask,
|
||||
id: newSubtaskId,
|
||||
parentTaskId: destParentTask.id
|
||||
};
|
||||
|
||||
// Insert at specific position
|
||||
destParentTask.subtasks.splice(destSubtaskIndex + 1, 0, newSubtask);
|
||||
// Insert at specific position
|
||||
destParentTask.subtasks.splice(destSubtaskIndex + 1, 0, newSubtask);
|
||||
|
||||
// Remove the original task from the tasks array
|
||||
data.tasks.splice(sourceTaskIndex, 1);
|
||||
// Remove the original task from the tasks array
|
||||
data.tasks.splice(sourceTaskIndex, 1);
|
||||
|
||||
log(
|
||||
'info',
|
||||
`Moved task ${sourceTask.id} to become subtask ${destParentTask.id}.${newSubtaskId}`
|
||||
);
|
||||
log(
|
||||
'info',
|
||||
`Moved task ${sourceTask.id} to become subtask ${destParentTask.id}.${newSubtaskId}`
|
||||
);
|
||||
|
||||
return newSubtask;
|
||||
return newSubtask;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -311,47 +359,53 @@ function moveTaskToSubtaskPosition(data, sourceTask, sourceTaskIndex, destParent
|
||||
* @param {Object} destTask - Destination task (for position reference)
|
||||
* @returns {Object} Moved task object
|
||||
*/
|
||||
function moveSubtaskToTask(data, sourceSubtask, sourceParentTask, sourceSubtaskIndex, destTask) {
|
||||
// Find the highest task ID to determine the next ID
|
||||
const highestId = Math.max(...data.tasks.map(t => t.id));
|
||||
const newTaskId = highestId + 1;
|
||||
function moveSubtaskToTask(
|
||||
data,
|
||||
sourceSubtask,
|
||||
sourceParentTask,
|
||||
sourceSubtaskIndex,
|
||||
destTask
|
||||
) {
|
||||
// Find the highest task ID to determine the next ID
|
||||
const highestId = Math.max(...data.tasks.map((t) => t.id));
|
||||
const newTaskId = highestId + 1;
|
||||
|
||||
// Create the new task from the subtask
|
||||
const newTask = {
|
||||
...sourceSubtask,
|
||||
id: newTaskId,
|
||||
priority: sourceParentTask.priority || 'medium' // Inherit priority from parent
|
||||
};
|
||||
delete newTask.parentTaskId;
|
||||
// Create the new task from the subtask
|
||||
const newTask = {
|
||||
...sourceSubtask,
|
||||
id: newTaskId,
|
||||
priority: sourceParentTask.priority || 'medium' // Inherit priority from parent
|
||||
};
|
||||
delete newTask.parentTaskId;
|
||||
|
||||
// Add the parent task as a dependency if not already present
|
||||
if (!newTask.dependencies) {
|
||||
newTask.dependencies = [];
|
||||
}
|
||||
if (!newTask.dependencies.includes(sourceParentTask.id)) {
|
||||
newTask.dependencies.push(sourceParentTask.id);
|
||||
}
|
||||
// Add the parent task as a dependency if not already present
|
||||
if (!newTask.dependencies) {
|
||||
newTask.dependencies = [];
|
||||
}
|
||||
if (!newTask.dependencies.includes(sourceParentTask.id)) {
|
||||
newTask.dependencies.push(sourceParentTask.id);
|
||||
}
|
||||
|
||||
// Find the destination index to insert the new task
|
||||
const destTaskIndex = data.tasks.findIndex(t => t.id === destTask.id);
|
||||
// Find the destination index to insert the new task
|
||||
const destTaskIndex = data.tasks.findIndex((t) => t.id === destTask.id);
|
||||
|
||||
// Insert the new task after the destination task
|
||||
data.tasks.splice(destTaskIndex + 1, 0, newTask);
|
||||
// Insert the new task after the destination task
|
||||
data.tasks.splice(destTaskIndex + 1, 0, newTask);
|
||||
|
||||
// Remove the subtask from the parent
|
||||
sourceParentTask.subtasks.splice(sourceSubtaskIndex, 1);
|
||||
// Remove the subtask from the parent
|
||||
sourceParentTask.subtasks.splice(sourceSubtaskIndex, 1);
|
||||
|
||||
// If parent has no more subtasks, remove the subtasks array
|
||||
if (sourceParentTask.subtasks.length === 0) {
|
||||
delete sourceParentTask.subtasks;
|
||||
}
|
||||
// If parent has no more subtasks, remove the subtasks array
|
||||
if (sourceParentTask.subtasks.length === 0) {
|
||||
delete sourceParentTask.subtasks;
|
||||
}
|
||||
|
||||
log(
|
||||
'info',
|
||||
`Moved subtask ${sourceParentTask.id}.${sourceSubtask.id} to become task ${newTaskId}`
|
||||
);
|
||||
log(
|
||||
'info',
|
||||
`Moved subtask ${sourceParentTask.id}.${sourceSubtask.id} to become task ${newTaskId}`
|
||||
);
|
||||
|
||||
return newTask;
|
||||
return newTask;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -362,23 +416,23 @@ function moveSubtaskToTask(data, sourceSubtask, sourceParentTask, sourceSubtaskI
|
||||
* @returns {Object} Moved subtask object
|
||||
*/
|
||||
function reorderSubtask(parentTask, sourceIndex, destIndex) {
|
||||
// Get the subtask to move
|
||||
const subtask = parentTask.subtasks[sourceIndex];
|
||||
// Get the subtask to move
|
||||
const subtask = parentTask.subtasks[sourceIndex];
|
||||
|
||||
// Remove the subtask from its current position
|
||||
parentTask.subtasks.splice(sourceIndex, 1);
|
||||
// Remove the subtask from its current position
|
||||
parentTask.subtasks.splice(sourceIndex, 1);
|
||||
|
||||
// Insert the subtask at the new position
|
||||
// If destIndex was after sourceIndex, it's now one less because we removed an item
|
||||
const adjustedDestIndex = sourceIndex < destIndex ? destIndex - 1 : destIndex;
|
||||
parentTask.subtasks.splice(adjustedDestIndex, 0, subtask);
|
||||
// Insert the subtask at the new position
|
||||
// If destIndex was after sourceIndex, it's now one less because we removed an item
|
||||
const adjustedDestIndex = sourceIndex < destIndex ? destIndex - 1 : destIndex;
|
||||
parentTask.subtasks.splice(adjustedDestIndex, 0, subtask);
|
||||
|
||||
log(
|
||||
'info',
|
||||
`Reordered subtask ${parentTask.id}.${subtask.id} within parent task ${parentTask.id}`
|
||||
);
|
||||
log(
|
||||
'info',
|
||||
`Reordered subtask ${parentTask.id}.${subtask.id} within parent task ${parentTask.id}`
|
||||
);
|
||||
|
||||
return subtask;
|
||||
return subtask;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -391,51 +445,51 @@ function reorderSubtask(parentTask, sourceIndex, destIndex) {
|
||||
* @returns {Object} Moved subtask object
|
||||
*/
|
||||
function moveSubtaskToAnotherParent(
|
||||
sourceSubtask,
|
||||
sourceParentTask,
|
||||
sourceSubtaskIndex,
|
||||
destParentTask,
|
||||
destSubtaskIndex
|
||||
sourceSubtask,
|
||||
sourceParentTask,
|
||||
sourceSubtaskIndex,
|
||||
destParentTask,
|
||||
destSubtaskIndex
|
||||
) {
|
||||
// Find the highest subtask ID in the destination parent
|
||||
const highestSubtaskId =
|
||||
destParentTask.subtasks.length > 0
|
||||
? Math.max(...destParentTask.subtasks.map(st => st.id))
|
||||
: 0;
|
||||
const newSubtaskId = highestSubtaskId + 1;
|
||||
// Find the highest subtask ID in the destination parent
|
||||
const highestSubtaskId =
|
||||
destParentTask.subtasks.length > 0
|
||||
? Math.max(...destParentTask.subtasks.map((st) => st.id))
|
||||
: 0;
|
||||
const newSubtaskId = highestSubtaskId + 1;
|
||||
|
||||
// Create the new subtask with updated parent reference
|
||||
const newSubtask = {
|
||||
...sourceSubtask,
|
||||
id: newSubtaskId,
|
||||
parentTaskId: destParentTask.id
|
||||
};
|
||||
// Create the new subtask with updated parent reference
|
||||
const newSubtask = {
|
||||
...sourceSubtask,
|
||||
id: newSubtaskId,
|
||||
parentTaskId: destParentTask.id
|
||||
};
|
||||
|
||||
// If the subtask depends on its original parent, keep that dependency
|
||||
if (!newSubtask.dependencies) {
|
||||
newSubtask.dependencies = [];
|
||||
}
|
||||
if (!newSubtask.dependencies.includes(sourceParentTask.id)) {
|
||||
newSubtask.dependencies.push(sourceParentTask.id);
|
||||
}
|
||||
// If the subtask depends on its original parent, keep that dependency
|
||||
if (!newSubtask.dependencies) {
|
||||
newSubtask.dependencies = [];
|
||||
}
|
||||
if (!newSubtask.dependencies.includes(sourceParentTask.id)) {
|
||||
newSubtask.dependencies.push(sourceParentTask.id);
|
||||
}
|
||||
|
||||
// Insert at the destination position
|
||||
destParentTask.subtasks.splice(destSubtaskIndex + 1, 0, newSubtask);
|
||||
// Insert at the destination position
|
||||
destParentTask.subtasks.splice(destSubtaskIndex + 1, 0, newSubtask);
|
||||
|
||||
// Remove the subtask from the original parent
|
||||
sourceParentTask.subtasks.splice(sourceSubtaskIndex, 1);
|
||||
// Remove the subtask from the original parent
|
||||
sourceParentTask.subtasks.splice(sourceSubtaskIndex, 1);
|
||||
|
||||
// If original parent has no more subtasks, remove the subtasks array
|
||||
if (sourceParentTask.subtasks.length === 0) {
|
||||
delete sourceParentTask.subtasks;
|
||||
}
|
||||
// If original parent has no more subtasks, remove the subtasks array
|
||||
if (sourceParentTask.subtasks.length === 0) {
|
||||
delete sourceParentTask.subtasks;
|
||||
}
|
||||
|
||||
log(
|
||||
'info',
|
||||
`Moved subtask ${sourceParentTask.id}.${sourceSubtask.id} to become subtask ${destParentTask.id}.${newSubtaskId}`
|
||||
);
|
||||
log(
|
||||
'info',
|
||||
`Moved subtask ${sourceParentTask.id}.${sourceSubtask.id} to become subtask ${destParentTask.id}.${newSubtaskId}`
|
||||
);
|
||||
|
||||
return newSubtask;
|
||||
return newSubtask;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -447,64 +501,71 @@ function moveSubtaskToAnotherParent(
|
||||
* @param {number} destTaskIndex - Index of destination task in data.tasks
|
||||
* @returns {Object} Moved task object
|
||||
*/
|
||||
function moveTaskToNewId(data, sourceTask, sourceTaskIndex, destTask, destTaskIndex) {
|
||||
// Create a copy of the source task with the new ID
|
||||
const movedTask = {
|
||||
...sourceTask,
|
||||
id: destTask.id
|
||||
};
|
||||
function moveTaskToNewId(
|
||||
data,
|
||||
sourceTask,
|
||||
sourceTaskIndex,
|
||||
destTask,
|
||||
destTaskIndex
|
||||
) {
|
||||
// Create a copy of the source task with the new ID
|
||||
const movedTask = {
|
||||
...sourceTask,
|
||||
id: destTask.id
|
||||
};
|
||||
|
||||
// Get numeric IDs for comparison
|
||||
const sourceIdNum = parseInt(sourceTask.id, 10);
|
||||
const destIdNum = parseInt(destTask.id, 10);
|
||||
// Get numeric IDs for comparison
|
||||
const sourceIdNum = parseInt(sourceTask.id, 10);
|
||||
const destIdNum = parseInt(destTask.id, 10);
|
||||
|
||||
// Handle subtasks if present
|
||||
if (sourceTask.subtasks && sourceTask.subtasks.length > 0) {
|
||||
// Update subtasks to reference the new parent ID if needed
|
||||
movedTask.subtasks = sourceTask.subtasks.map(subtask => ({
|
||||
...subtask,
|
||||
parentTaskId: destIdNum
|
||||
}));
|
||||
}
|
||||
// Handle subtasks if present
|
||||
if (sourceTask.subtasks && sourceTask.subtasks.length > 0) {
|
||||
// Update subtasks to reference the new parent ID if needed
|
||||
movedTask.subtasks = sourceTask.subtasks.map((subtask) => ({
|
||||
...subtask,
|
||||
parentTaskId: destIdNum
|
||||
}));
|
||||
}
|
||||
|
||||
// Update any dependencies in other tasks that referenced the old ID
|
||||
data.tasks.forEach(task => {
|
||||
if (task.dependencies && task.dependencies.includes(sourceIdNum)) {
|
||||
// Replace the old ID with the new ID
|
||||
const depIndex = task.dependencies.indexOf(sourceIdNum);
|
||||
task.dependencies[depIndex] = destIdNum;
|
||||
}
|
||||
// Update any dependencies in other tasks that referenced the old ID
|
||||
data.tasks.forEach((task) => {
|
||||
if (task.dependencies && task.dependencies.includes(sourceIdNum)) {
|
||||
// Replace the old ID with the new ID
|
||||
const depIndex = task.dependencies.indexOf(sourceIdNum);
|
||||
task.dependencies[depIndex] = destIdNum;
|
||||
}
|
||||
|
||||
// Also check for subtask dependencies that might reference this task
|
||||
if (task.subtasks && task.subtasks.length > 0) {
|
||||
task.subtasks.forEach(subtask => {
|
||||
if (subtask.dependencies && subtask.dependencies.includes(sourceIdNum)) {
|
||||
const depIndex = subtask.dependencies.indexOf(sourceIdNum);
|
||||
subtask.dependencies[depIndex] = destIdNum;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
// Also check for subtask dependencies that might reference this task
|
||||
if (task.subtasks && task.subtasks.length > 0) {
|
||||
task.subtasks.forEach((subtask) => {
|
||||
if (
|
||||
subtask.dependencies &&
|
||||
subtask.dependencies.includes(sourceIdNum)
|
||||
) {
|
||||
const depIndex = subtask.dependencies.indexOf(sourceIdNum);
|
||||
subtask.dependencies[depIndex] = destIdNum;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Remove the original task from its position
|
||||
data.tasks.splice(sourceTaskIndex, 1);
|
||||
// Remove the original task from its position
|
||||
data.tasks.splice(sourceTaskIndex, 1);
|
||||
|
||||
// If we're moving to a position after the original, adjust the destination index
|
||||
// since removing the original shifts everything down by 1
|
||||
const adjustedDestIndex = sourceTaskIndex < destTaskIndex ? destTaskIndex - 1 : destTaskIndex;
|
||||
// If we're moving to a position after the original, adjust the destination index
|
||||
// since removing the original shifts everything down by 1
|
||||
const adjustedDestIndex =
|
||||
sourceTaskIndex < destTaskIndex ? destTaskIndex - 1 : destTaskIndex;
|
||||
|
||||
// Remove the placeholder destination task
|
||||
data.tasks.splice(adjustedDestIndex, 1);
|
||||
// Remove the placeholder destination task
|
||||
data.tasks.splice(adjustedDestIndex, 1);
|
||||
|
||||
// Insert the moved task at the destination position
|
||||
data.tasks.splice(adjustedDestIndex, 0, movedTask);
|
||||
// Insert the moved task at the destination position
|
||||
data.tasks.splice(adjustedDestIndex, 0, movedTask);
|
||||
|
||||
log(
|
||||
'info',
|
||||
`Moved task ${sourceIdNum} to new ID ${destIdNum}`
|
||||
);
|
||||
log('info', `Moved task ${sourceIdNum} to new ID ${destIdNum}`);
|
||||
|
||||
return movedTask;
|
||||
return movedTask;
|
||||
}
|
||||
|
||||
export default moveTask;
|
||||
@@ -92,7 +92,9 @@ async function parsePRD(prdPath, tasksPath, numTasks, options = {}) {
|
||||
}
|
||||
};
|
||||
|
||||
report(`Parsing PRD file: ${prdPath}, Force: ${force}, Append: ${append}, Research: ${research}`);
|
||||
report(
|
||||
`Parsing PRD file: ${prdPath}, Force: ${force}, Append: ${append}, Research: ${research}`
|
||||
);
|
||||
|
||||
let existingTasks = [];
|
||||
let nextId = 1;
|
||||
@@ -220,7 +222,10 @@ Guidelines:
|
||||
}`;
|
||||
|
||||
// Call the unified AI service
|
||||
report(`Calling AI service to generate tasks from PRD${research ? ' with research-backed analysis' : ''}...`, 'info');
|
||||
report(
|
||||
`Calling AI service to generate tasks from PRD${research ? ' with research-backed analysis' : ''}...`,
|
||||
'info'
|
||||
);
|
||||
|
||||
// Call generateObjectService with the CORRECT schema and additional telemetry params
|
||||
aiServiceResponse = await generateObjectService({
|
||||
@@ -240,7 +245,9 @@ Guidelines:
|
||||
if (!fs.existsSync(tasksDir)) {
|
||||
fs.mkdirSync(tasksDir, { recursive: true });
|
||||
}
|
||||
logFn.success(`Successfully parsed PRD via AI service${research ? ' with research-backed analysis' : ''}.`);
|
||||
logFn.success(
|
||||
`Successfully parsed PRD via AI service${research ? ' with research-backed analysis' : ''}.`
|
||||
);
|
||||
|
||||
// Validate and Process Tasks
|
||||
// const generatedData = aiServiceResponse?.mainResult?.object;
|
||||
|
||||
@@ -1,357 +1,357 @@
|
||||
{
|
||||
"meta": {
|
||||
"generatedAt": "2025-05-22T05:48:33.026Z",
|
||||
"tasksAnalyzed": 6,
|
||||
"totalTasks": 88,
|
||||
"analysisCount": 43,
|
||||
"thresholdScore": 5,
|
||||
"projectName": "Taskmaster",
|
||||
"usedResearch": true
|
||||
},
|
||||
"complexityAnalysis": [
|
||||
{
|
||||
"taskId": 24,
|
||||
"taskTitle": "Implement AI-Powered Test Generation Command",
|
||||
"complexityScore": 7,
|
||||
"recommendedSubtasks": 5,
|
||||
"expansionPrompt": "Break down the implementation of the AI-powered test generation command into detailed subtasks covering: command structure setup, AI prompt engineering, test file generation logic, integration with Claude API, and comprehensive error handling.",
|
||||
"reasoning": "This task involves complex integration with an AI service (Claude), requires sophisticated prompt engineering, and needs to generate structured code files. The existing 3 subtasks are a good start but could be expanded to include more detailed steps for AI integration, error handling, and test file formatting."
|
||||
},
|
||||
{
|
||||
"taskId": 26,
|
||||
"taskTitle": "Implement Context Foundation for AI Operations",
|
||||
"complexityScore": 6,
|
||||
"recommendedSubtasks": 4,
|
||||
"expansionPrompt": "The current 4 subtasks for implementing the context foundation appear comprehensive. Consider if any additional subtasks are needed for testing, documentation, or integration with existing systems.",
|
||||
"reasoning": "This task involves creating a foundation for context integration with several well-defined components. The existing 4 subtasks cover the main implementation areas (context-file flag, cursor rules integration, context extraction utility, and command handler updates). The complexity is moderate as it requires careful integration with existing systems but has clear requirements."
|
||||
},
|
||||
{
|
||||
"taskId": 27,
|
||||
"taskTitle": "Implement Context Enhancements for AI Operations",
|
||||
"complexityScore": 7,
|
||||
"recommendedSubtasks": 4,
|
||||
"expansionPrompt": "The current 4 subtasks for implementing context enhancements appear well-structured. Consider if any additional subtasks are needed for testing, documentation, or performance optimization.",
|
||||
"reasoning": "This task builds upon the foundation from Task #26 and adds more sophisticated context handling features. The 4 existing subtasks cover the main implementation areas (code context extraction, task history context, PRD context integration, and context formatting). The complexity is higher than the foundation task due to the need for intelligent context selection and optimization."
|
||||
},
|
||||
{
|
||||
"taskId": 28,
|
||||
"taskTitle": "Implement Advanced ContextManager System",
|
||||
"complexityScore": 8,
|
||||
"recommendedSubtasks": 5,
|
||||
"expansionPrompt": "The current 5 subtasks for implementing the advanced ContextManager system appear comprehensive. Consider if any additional subtasks are needed for testing, documentation, or backward compatibility with previous context implementations.",
|
||||
"reasoning": "This task represents the most complex phase of the context implementation, requiring a sophisticated class design, optimization algorithms, and integration with multiple systems. The 5 existing subtasks cover the core implementation areas, but the complexity is high due to the need for intelligent context prioritization, token management, and performance monitoring."
|
||||
},
|
||||
{
|
||||
"taskId": 40,
|
||||
"taskTitle": "Implement 'plan' Command for Task Implementation Planning",
|
||||
"complexityScore": 5,
|
||||
"recommendedSubtasks": 4,
|
||||
"expansionPrompt": "The current 4 subtasks for implementing the 'plan' command appear well-structured. Consider if any additional subtasks are needed for testing, documentation, or integration with existing task management workflows.",
|
||||
"reasoning": "This task involves creating a new command that leverages AI to generate implementation plans. The existing 4 subtasks cover the main implementation areas (retrieving task content, generating plans with AI, formatting in XML, and error handling). The complexity is moderate as it builds on existing patterns for task updates but requires careful AI integration."
|
||||
},
|
||||
{
|
||||
"taskId": 41,
|
||||
"taskTitle": "Implement Visual Task Dependency Graph in Terminal",
|
||||
"complexityScore": 8,
|
||||
"recommendedSubtasks": 10,
|
||||
"expansionPrompt": "The current 10 subtasks for implementing the visual task dependency graph appear comprehensive. Consider if any additional subtasks are needed for performance optimization with large graphs or additional visualization options.",
|
||||
"reasoning": "This task involves creating a sophisticated visualization system for terminal display, which is inherently complex due to layout algorithms, ASCII/Unicode rendering, and handling complex dependency relationships. The 10 existing subtasks cover all major aspects of implementation, from CLI interface to accessibility features."
|
||||
},
|
||||
{
|
||||
"taskId": 42,
|
||||
"taskTitle": "Implement MCP-to-MCP Communication Protocol",
|
||||
"complexityScore": 9,
|
||||
"recommendedSubtasks": 8,
|
||||
"expansionPrompt": "The current 8 subtasks for implementing the MCP-to-MCP communication protocol appear well-structured. Consider if any additional subtasks are needed for security hardening, performance optimization, or comprehensive documentation.",
|
||||
"reasoning": "This task involves designing and implementing a complex communication protocol between different MCP tools and servers. It requires sophisticated adapter patterns, client-server architecture, and handling of multiple operational modes. The complexity is very high due to the need for standardization, security, and backward compatibility."
|
||||
},
|
||||
{
|
||||
"taskId": 44,
|
||||
"taskTitle": "Implement Task Automation with Webhooks and Event Triggers",
|
||||
"complexityScore": 8,
|
||||
"recommendedSubtasks": 7,
|
||||
"expansionPrompt": "The current 7 subtasks for implementing task automation with webhooks appear comprehensive. Consider if any additional subtasks are needed for security testing, rate limiting implementation, or webhook monitoring tools.",
|
||||
"reasoning": "This task involves creating a sophisticated event system with webhooks for integration with external services. The complexity is high due to the need for secure authentication, reliable delivery mechanisms, and handling of various webhook formats and protocols. The existing subtasks cover the main implementation areas but security and monitoring could be emphasized more."
|
||||
},
|
||||
{
|
||||
"taskId": 45,
|
||||
"taskTitle": "Implement GitHub Issue Import Feature",
|
||||
"complexityScore": 6,
|
||||
"recommendedSubtasks": 5,
|
||||
"expansionPrompt": "The current 5 subtasks for implementing the GitHub issue import feature appear well-structured. Consider if any additional subtasks are needed for handling GitHub API rate limiting, caching, or supporting additional issue metadata.",
|
||||
"reasoning": "This task involves integrating with the GitHub API to import issues as tasks. The complexity is moderate as it requires API authentication, data mapping, and error handling. The existing 5 subtasks cover the main implementation areas from design to end-to-end implementation."
|
||||
},
|
||||
{
|
||||
"taskId": 46,
|
||||
"taskTitle": "Implement ICE Analysis Command for Task Prioritization",
|
||||
"complexityScore": 7,
|
||||
"recommendedSubtasks": 5,
|
||||
"expansionPrompt": "The current 5 subtasks for implementing the ICE analysis command appear comprehensive. Consider if any additional subtasks are needed for visualization of ICE scores or integration with other prioritization methods.",
|
||||
"reasoning": "This task involves creating an AI-powered analysis system for task prioritization using the ICE methodology. The complexity is high due to the need for sophisticated scoring algorithms, AI integration, and report generation. The existing subtasks cover the main implementation areas from algorithm design to integration with existing systems."
|
||||
},
|
||||
{
|
||||
"taskId": 47,
|
||||
"taskTitle": "Enhance Task Suggestion Actions Card Workflow",
|
||||
"complexityScore": 6,
|
||||
"recommendedSubtasks": 6,
|
||||
"expansionPrompt": "The current 6 subtasks for enhancing the task suggestion actions card workflow appear well-structured. Consider if any additional subtasks are needed for user testing, accessibility improvements, or performance optimization.",
|
||||
"reasoning": "This task involves redesigning the UI workflow for task expansion and management. The complexity is moderate as it requires careful UX design and state management but builds on existing components. The 6 existing subtasks cover the main implementation areas from design to testing."
|
||||
},
|
||||
{
|
||||
"taskId": 48,
|
||||
"taskTitle": "Refactor Prompts into Centralized Structure",
|
||||
"complexityScore": 4,
|
||||
"recommendedSubtasks": 3,
|
||||
"expansionPrompt": "The current 3 subtasks for refactoring prompts into a centralized structure appear appropriate. Consider if any additional subtasks are needed for prompt versioning, documentation, or testing.",
|
||||
"reasoning": "This task involves a straightforward refactoring to improve code organization. The complexity is relatively low as it primarily involves moving code rather than creating new functionality. The 3 existing subtasks cover the main implementation areas from directory structure to integration."
|
||||
},
|
||||
{
|
||||
"taskId": 49,
|
||||
"taskTitle": "Implement Code Quality Analysis Command",
|
||||
"complexityScore": 8,
|
||||
"recommendedSubtasks": 6,
|
||||
"expansionPrompt": "The current 6 subtasks for implementing the code quality analysis command appear comprehensive. Consider if any additional subtasks are needed for performance optimization with large codebases or integration with existing code quality tools.",
|
||||
"reasoning": "This task involves creating a sophisticated code analysis system with pattern recognition, best practice verification, and AI-powered recommendations. The complexity is high due to the need for code parsing, complex analysis algorithms, and integration with AI services. The existing subtasks cover the main implementation areas from algorithm design to user interface."
|
||||
},
|
||||
{
|
||||
"taskId": 50,
|
||||
"taskTitle": "Implement Test Coverage Tracking System by Task",
|
||||
"complexityScore": 9,
|
||||
"recommendedSubtasks": 5,
|
||||
"expansionPrompt": "The current 5 subtasks for implementing the test coverage tracking system appear well-structured. Consider if any additional subtasks are needed for integration with CI/CD systems, performance optimization, or visualization tools.",
|
||||
"reasoning": "This task involves creating a complex system that maps test coverage to specific tasks and subtasks. The complexity is very high due to the need for sophisticated data structures, integration with coverage tools, and AI-powered test generation. The existing subtasks are comprehensive and cover the main implementation areas from data structure design to AI integration."
|
||||
},
|
||||
{
|
||||
"taskId": 51,
|
||||
"taskTitle": "Implement Perplexity Research Command",
|
||||
"complexityScore": 6,
|
||||
"recommendedSubtasks": 5,
|
||||
"expansionPrompt": "The current 5 subtasks for implementing the Perplexity research command appear comprehensive. Consider if any additional subtasks are needed for caching optimization, result formatting, or integration with other research tools.",
|
||||
"reasoning": "This task involves creating a new command that integrates with the Perplexity AI API for research. The complexity is moderate as it requires API integration, context extraction, and result formatting. The 5 existing subtasks cover the main implementation areas from API client to caching system."
|
||||
},
|
||||
{
|
||||
"taskId": 52,
|
||||
"taskTitle": "Implement Task Suggestion Command for CLI",
|
||||
"complexityScore": 6,
|
||||
"recommendedSubtasks": 5,
|
||||
"expansionPrompt": "The current 5 subtasks for implementing the task suggestion command appear well-structured. Consider if any additional subtasks are needed for suggestion quality evaluation, user feedback collection, or integration with existing task workflows.",
|
||||
"reasoning": "This task involves creating a new CLI command that generates contextually relevant task suggestions using AI. The complexity is moderate as it requires AI integration, context collection, and interactive CLI interfaces. The existing subtasks cover the main implementation areas from data collection to user interface."
|
||||
},
|
||||
{
|
||||
"taskId": 53,
|
||||
"taskTitle": "Implement Subtask Suggestion Feature for Parent Tasks",
|
||||
"complexityScore": 6,
|
||||
"recommendedSubtasks": 6,
|
||||
"expansionPrompt": "The current 6 subtasks for implementing the subtask suggestion feature appear comprehensive. Consider if any additional subtasks are needed for suggestion quality metrics, user feedback collection, or performance optimization.",
|
||||
"reasoning": "This task involves creating a feature that suggests contextually relevant subtasks for parent tasks. The complexity is moderate as it builds on existing task management systems but requires sophisticated AI integration and context analysis. The 6 existing subtasks cover the main implementation areas from validation to testing."
|
||||
},
|
||||
{
|
||||
"taskId": 55,
|
||||
"taskTitle": "Implement Positional Arguments Support for CLI Commands",
|
||||
"complexityScore": 5,
|
||||
"recommendedSubtasks": 5,
|
||||
"expansionPrompt": "The current 5 subtasks for implementing positional arguments support appear well-structured. Consider if any additional subtasks are needed for backward compatibility testing, documentation updates, or user experience improvements.",
|
||||
"reasoning": "This task involves modifying the command parsing logic to support positional arguments alongside the existing flag-based syntax. The complexity is moderate as it requires careful handling of different argument styles and edge cases. The 5 existing subtasks cover the main implementation areas from analysis to documentation."
|
||||
},
|
||||
{
|
||||
"taskId": 57,
|
||||
"taskTitle": "Enhance Task-Master CLI User Experience and Interface",
|
||||
"complexityScore": 7,
|
||||
"recommendedSubtasks": 6,
|
||||
"expansionPrompt": "The current 6 subtasks for enhancing the CLI user experience appear comprehensive. Consider if any additional subtasks are needed for accessibility testing, internationalization, or performance optimization.",
|
||||
"reasoning": "This task involves a significant overhaul of the CLI interface to improve user experience. The complexity is high due to the breadth of changes (logging, visual elements, interactive components, etc.) and the need for consistent design across all commands. The 6 existing subtasks cover the main implementation areas from log management to help systems."
|
||||
},
|
||||
{
|
||||
"taskId": 60,
|
||||
"taskTitle": "Implement Mentor System with Round-Table Discussion Feature",
|
||||
"complexityScore": 8,
|
||||
"recommendedSubtasks": 7,
|
||||
"expansionPrompt": "The current 7 subtasks for implementing the mentor system appear well-structured. Consider if any additional subtasks are needed for mentor personality consistency, discussion quality evaluation, or performance optimization with multiple mentors.",
|
||||
"reasoning": "This task involves creating a sophisticated mentor simulation system with round-table discussions. The complexity is high due to the need for personality simulation, complex LLM integration, and structured discussion management. The 7 existing subtasks cover the main implementation areas from architecture to testing."
|
||||
},
|
||||
{
|
||||
"taskId": 62,
|
||||
"taskTitle": "Add --simple Flag to Update Commands for Direct Text Input",
|
||||
"complexityScore": 4,
|
||||
"recommendedSubtasks": 8,
|
||||
"expansionPrompt": "The current 8 subtasks for implementing the --simple flag appear comprehensive. Consider if any additional subtasks are needed for user experience testing or documentation updates.",
|
||||
"reasoning": "This task involves adding a simple flag option to bypass AI processing for updates. The complexity is relatively low as it primarily involves modifying existing command handlers and adding a flag. The 8 existing subtasks are very detailed and cover all aspects of implementation from command parsing to testing."
|
||||
},
|
||||
{
|
||||
"taskId": 63,
|
||||
"taskTitle": "Add pnpm Support for the Taskmaster Package",
|
||||
"complexityScore": 5,
|
||||
"recommendedSubtasks": 8,
|
||||
"expansionPrompt": "The current 8 subtasks for adding pnpm support appear comprehensive. Consider if any additional subtasks are needed for CI/CD integration, performance comparison, or documentation updates.",
|
||||
"reasoning": "This task involves ensuring the package works correctly with pnpm as an alternative package manager. The complexity is moderate as it requires careful testing of installation processes and scripts across different environments. The 8 existing subtasks cover all major aspects from documentation to binary verification."
|
||||
},
|
||||
{
|
||||
"taskId": 64,
|
||||
"taskTitle": "Add Yarn Support for Taskmaster Installation",
|
||||
"complexityScore": 5,
|
||||
"recommendedSubtasks": 9,
|
||||
"expansionPrompt": "The current 9 subtasks for adding Yarn support appear comprehensive. Consider if any additional subtasks are needed for performance testing, CI/CD integration, or compatibility with different Yarn versions.",
|
||||
"reasoning": "This task involves ensuring the package works correctly with Yarn as an alternative package manager. The complexity is moderate as it requires careful testing of installation processes and scripts across different environments. The 9 existing subtasks are very detailed and cover all aspects from configuration to testing."
|
||||
},
|
||||
{
|
||||
"taskId": 65,
|
||||
"taskTitle": "Add Bun Support for Taskmaster Installation",
|
||||
"complexityScore": 6,
|
||||
"recommendedSubtasks": 6,
|
||||
"expansionPrompt": "The current 6 subtasks for adding Bun support appear well-structured. Consider if any additional subtasks are needed for handling Bun-specific issues, performance testing, or documentation updates.",
|
||||
"reasoning": "This task involves adding support for the newer Bun package manager. The complexity is slightly higher than the other package manager tasks due to Bun's differences from Node.js and potential compatibility issues. The 6 existing subtasks cover the main implementation areas from research to documentation."
|
||||
},
|
||||
{
|
||||
"taskId": 67,
|
||||
"taskTitle": "Add CLI JSON output and Cursor keybindings integration",
|
||||
"complexityScore": 5,
|
||||
"recommendedSubtasks": 5,
|
||||
"expansionPrompt": "The current 5 subtasks for implementing JSON output and Cursor keybindings appear well-structured. Consider if any additional subtasks are needed for testing across different operating systems, documentation updates, or user experience improvements.",
|
||||
"reasoning": "This task involves two distinct features: adding JSON output to CLI commands and creating a keybindings installation command. The complexity is moderate as it requires careful handling of different output formats and OS-specific file paths. The 5 existing subtasks cover the main implementation areas for both features."
|
||||
},
|
||||
{
|
||||
"taskId": 68,
|
||||
"taskTitle": "Ability to create tasks without parsing PRD",
|
||||
"complexityScore": 3,
|
||||
"recommendedSubtasks": 2,
|
||||
"expansionPrompt": "The current 2 subtasks for implementing task creation without PRD appear appropriate. Consider if any additional subtasks are needed for validation, error handling, or integration with existing task management workflows.",
|
||||
"reasoning": "This task involves a relatively simple modification to allow task creation without requiring a PRD document. The complexity is low as it primarily involves creating a form interface and saving functionality. The 2 existing subtasks cover the main implementation areas of UI design and data saving."
|
||||
},
|
||||
{
|
||||
"taskId": 72,
|
||||
"taskTitle": "Implement PDF Generation for Project Progress and Dependency Overview",
|
||||
"complexityScore": 7,
|
||||
"recommendedSubtasks": 6,
|
||||
"expansionPrompt": "The current 6 subtasks for implementing PDF generation appear comprehensive. Consider if any additional subtasks are needed for handling large projects, additional visualization options, or integration with existing reporting tools.",
|
||||
"reasoning": "This task involves creating a feature to generate PDF reports of project progress and dependency visualization. The complexity is high due to the need for PDF generation, data collection, and visualization integration. The 6 existing subtasks cover the main implementation areas from library selection to export options."
|
||||
},
|
||||
{
|
||||
"taskId": 75,
|
||||
"taskTitle": "Integrate Google Search Grounding for Research Role",
|
||||
"complexityScore": 5,
|
||||
"recommendedSubtasks": 4,
|
||||
"expansionPrompt": "The current 4 subtasks for integrating Google Search Grounding appear well-structured. Consider if any additional subtasks are needed for testing with different query types, error handling, or performance optimization.",
|
||||
"reasoning": "This task involves updating the AI service layer to enable Google Search Grounding for research roles. The complexity is moderate as it requires careful integration with the existing AI service architecture and conditional logic. The 4 existing subtasks cover the main implementation areas from service layer modification to testing."
|
||||
},
|
||||
{
|
||||
"taskId": 76,
|
||||
"taskTitle": "Develop E2E Test Framework for Taskmaster MCP Server (FastMCP over stdio)",
|
||||
"complexityScore": 8,
|
||||
"recommendedSubtasks": 7,
|
||||
"expansionPrompt": "The current 7 subtasks for developing the E2E test framework appear comprehensive. Consider if any additional subtasks are needed for test result reporting, CI/CD integration, or performance benchmarking.",
|
||||
"reasoning": "This task involves creating a sophisticated end-to-end testing framework for the MCP server. The complexity is high due to the need for subprocess management, protocol handling, and robust test case definition. The 7 existing subtasks cover the main implementation areas from architecture to documentation."
|
||||
},
|
||||
{
|
||||
"taskId": 77,
|
||||
"taskTitle": "Implement AI Usage Telemetry for Taskmaster (with external analytics endpoint)",
|
||||
"complexityScore": 7,
|
||||
"recommendedSubtasks": 18,
|
||||
"expansionPrompt": "The current 18 subtasks for implementing AI usage telemetry appear very comprehensive. Consider if any additional subtasks are needed for security hardening, privacy compliance, or user feedback collection.",
|
||||
"reasoning": "This task involves creating a telemetry system to track AI usage metrics. The complexity is high due to the need for secure data transmission, comprehensive data collection, and integration across multiple commands. The 18 existing subtasks are extremely detailed and cover all aspects of implementation from core utility to provider-specific updates."
|
||||
},
|
||||
{
|
||||
"taskId": 80,
|
||||
"taskTitle": "Implement Unique User ID Generation and Storage During Installation",
|
||||
"complexityScore": 4,
|
||||
"recommendedSubtasks": 5,
|
||||
"expansionPrompt": "The current 5 subtasks for implementing unique user ID generation appear well-structured. Consider if any additional subtasks are needed for privacy compliance, security auditing, or integration with the telemetry system.",
|
||||
"reasoning": "This task involves generating and storing a unique user identifier during installation. The complexity is relatively low as it primarily involves UUID generation and configuration file management. The 5 existing subtasks cover the main implementation areas from script structure to documentation."
|
||||
},
|
||||
{
|
||||
"taskId": 81,
|
||||
"taskTitle": "Task #81: Implement Comprehensive Local Telemetry System with Future Server Integration Capability",
|
||||
"complexityScore": 8,
|
||||
"recommendedSubtasks": 6,
|
||||
"expansionPrompt": "The current 6 subtasks for implementing the comprehensive local telemetry system appear well-structured. Consider if any additional subtasks are needed for data migration, storage optimization, or visualization tools.",
|
||||
"reasoning": "This task involves expanding the telemetry system to capture additional metrics and implement local storage with future server integration capability. The complexity is high due to the breadth of data collection, storage requirements, and privacy considerations. The 6 existing subtasks cover the main implementation areas from data collection to user-facing benefits."
|
||||
},
|
||||
{
|
||||
"taskId": 82,
|
||||
"taskTitle": "Update supported-models.json with token limit fields",
|
||||
"complexityScore": 3,
|
||||
"recommendedSubtasks": 1,
|
||||
"expansionPrompt": "This task appears straightforward enough to be implemented without further subtasks. Focus on researching accurate token limit values for each model and ensuring backward compatibility.",
|
||||
"reasoning": "This task involves a simple update to the supported-models.json file to include new token limit fields. The complexity is low as it primarily involves research and data entry. No subtasks are necessary as the task is well-defined and focused."
|
||||
},
|
||||
{
|
||||
"taskId": 83,
|
||||
"taskTitle": "Update config-manager.js defaults and getters",
|
||||
"complexityScore": 4,
|
||||
"recommendedSubtasks": 1,
|
||||
"expansionPrompt": "This task appears straightforward enough to be implemented without further subtasks. Focus on updating the DEFAULTS object and related getter functions while maintaining backward compatibility.",
|
||||
"reasoning": "This task involves updating the config-manager.js module to replace maxTokens with more specific token limit fields. The complexity is relatively low as it primarily involves modifying existing code rather than creating new functionality. No subtasks are necessary as the task is well-defined and focused."
|
||||
},
|
||||
{
|
||||
"taskId": 84,
|
||||
"taskTitle": "Implement token counting utility",
|
||||
"complexityScore": 5,
|
||||
"recommendedSubtasks": 1,
|
||||
"expansionPrompt": "This task appears well-defined enough to be implemented without further subtasks. Focus on implementing accurate token counting for different models and proper fallback mechanisms.",
|
||||
"reasoning": "This task involves creating a utility function to count tokens for different AI models. The complexity is moderate as it requires integration with the tiktoken library and handling different tokenization schemes. No subtasks are necessary as the task is well-defined and focused."
|
||||
},
|
||||
{
|
||||
"taskId": 69,
|
||||
"taskTitle": "Enhance Analyze Complexity for Specific Task IDs",
|
||||
"complexityScore": 7,
|
||||
"recommendedSubtasks": 6,
|
||||
"expansionPrompt": "Break down the task 'Enhance Analyze Complexity for Specific Task IDs' into 6 subtasks focusing on: 1) Core logic modification to accept ID parameters, 2) Report merging functionality, 3) CLI interface updates, 4) MCP tool integration, 5) Documentation updates, and 6) Comprehensive testing across all components.",
|
||||
"reasoning": "This task involves modifying existing functionality across multiple components (core logic, CLI, MCP) with complex logic for filtering tasks and merging reports. The implementation requires careful handling of different parameter combinations and edge cases. The task has interdependent components that need to work together seamlessly, and the report merging functionality adds significant complexity."
|
||||
},
|
||||
{
|
||||
"taskId": 70,
|
||||
"taskTitle": "Implement 'diagram' command for Mermaid diagram generation",
|
||||
"complexityScore": 6,
|
||||
"recommendedSubtasks": 5,
|
||||
"expansionPrompt": "Break down the 'diagram' command implementation into 5 subtasks: 1) Command interface and parameter handling, 2) Task data extraction and transformation to Mermaid syntax, 3) Diagram rendering with status color coding, 4) Output formatting and file export functionality, and 5) Error handling and edge case management.",
|
||||
"reasoning": "This task requires implementing a new feature rather than modifying existing code, which reduces complexity from integration challenges. However, it involves working with visualization logic, dependency mapping, and multiple output formats. The color coding based on status and handling of dependency relationships adds moderate complexity. The task is well-defined but requires careful attention to diagram formatting and error handling."
|
||||
},
|
||||
{
|
||||
"taskId": 85,
|
||||
"taskTitle": "Update ai-services-unified.js for dynamic token limits",
|
||||
"complexityScore": 7,
|
||||
"recommendedSubtasks": 5,
|
||||
"expansionPrompt": "Break down the update of ai-services-unified.js for dynamic token limits into subtasks such as: (1) Import and integrate the token counting utility, (2) Refactor _unifiedServiceRunner to calculate and enforce dynamic token limits, (3) Update error handling for token limit violations, (4) Add and verify logging for token usage, (5) Write and execute tests for various prompt and model scenarios.",
|
||||
"reasoning": "This task involves significant code changes to a core function, integration of a new utility, dynamic logic for multiple models, and robust error handling. It also requires comprehensive testing for edge cases and integration, making it moderately complex and best managed by splitting into focused subtasks."
|
||||
},
|
||||
{
|
||||
"taskId": 86,
|
||||
"taskTitle": "Update .taskmasterconfig schema and user guide",
|
||||
"complexityScore": 6,
|
||||
"recommendedSubtasks": 4,
|
||||
"expansionPrompt": "Expand this task into subtasks: (1) Draft a migration guide for users, (2) Update user documentation to explain new config fields, (3) Modify schema validation logic in config-manager.js, (4) Test and validate backward compatibility and error messaging.",
|
||||
"reasoning": "The task spans documentation, schema changes, migration guidance, and validation logic. While not algorithmically complex, it requires careful coordination and thorough testing to ensure a smooth user transition and robust validation."
|
||||
},
|
||||
{
|
||||
"taskId": 87,
|
||||
"taskTitle": "Implement validation and error handling",
|
||||
"complexityScore": 5,
|
||||
"recommendedSubtasks": 4,
|
||||
"expansionPrompt": "Decompose this task into: (1) Add validation logic for model and config loading, (2) Implement error handling and fallback mechanisms, (3) Enhance logging and reporting for token usage, (4) Develop helper functions for configuration suggestions and improvements.",
|
||||
"reasoning": "This task is primarily about adding validation, error handling, and logging. While important for robustness, the logic is straightforward and can be modularized into a few clear subtasks."
|
||||
},
|
||||
{
|
||||
"taskId": 89,
|
||||
"taskTitle": "Introduce Prioritize Command with Enhanced Priority Levels",
|
||||
"complexityScore": 6,
|
||||
"recommendedSubtasks": 5,
|
||||
"expansionPrompt": "Expand this task into: (1) Implement the prioritize command with all required flags and shorthands, (2) Update CLI output and help documentation for new priority levels, (3) Ensure backward compatibility with existing commands, (4) Add error handling for invalid inputs, (5) Write and run tests for all command scenarios.",
|
||||
"reasoning": "This CLI feature requires command parsing, updating internal logic for new priority levels, documentation, and robust error handling. The complexity is moderate due to the need for backward compatibility and comprehensive testing."
|
||||
},
|
||||
{
|
||||
"taskId": 90,
|
||||
"taskTitle": "Implement Subtask Progress Analyzer and Reporting System",
|
||||
"complexityScore": 8,
|
||||
"recommendedSubtasks": 6,
|
||||
"expansionPrompt": "Break down the analyzer implementation into: (1) Design and implement progress tracking logic, (2) Develop status validation and issue detection, (3) Build the reporting system with multiple output formats, (4) Integrate analyzer with the existing task management system, (5) Optimize for performance and scalability, (6) Write unit, integration, and performance tests.",
|
||||
"reasoning": "This is a complex, multi-faceted feature involving data analysis, reporting, integration, and performance optimization. It touches many parts of the system and requires careful design, making it one of the most complex tasks in the list."
|
||||
},
|
||||
{
|
||||
"taskId": 91,
|
||||
"taskTitle": "Implement Move Command for Tasks and Subtasks",
|
||||
"complexityScore": 7,
|
||||
"recommendedSubtasks": 5,
|
||||
"expansionPrompt": "Expand this task into: (1) Implement move logic for tasks and subtasks, (2) Handle edge cases (invalid ids, non-existent parents, circular dependencies), (3) Update CLI to support move command with flags, (4) Ensure data integrity and update relationships, (5) Write and execute tests for various move scenarios.",
|
||||
"reasoning": "Moving tasks and subtasks requires careful handling of hierarchical data, edge cases, and data integrity. The command must be robust and user-friendly, necessitating multiple focused subtasks for safe implementation."
|
||||
}
|
||||
]
|
||||
"meta": {
|
||||
"generatedAt": "2025-05-22T05:48:33.026Z",
|
||||
"tasksAnalyzed": 6,
|
||||
"totalTasks": 88,
|
||||
"analysisCount": 43,
|
||||
"thresholdScore": 5,
|
||||
"projectName": "Taskmaster",
|
||||
"usedResearch": true
|
||||
},
|
||||
"complexityAnalysis": [
|
||||
{
|
||||
"taskId": 24,
|
||||
"taskTitle": "Implement AI-Powered Test Generation Command",
|
||||
"complexityScore": 7,
|
||||
"recommendedSubtasks": 5,
|
||||
"expansionPrompt": "Break down the implementation of the AI-powered test generation command into detailed subtasks covering: command structure setup, AI prompt engineering, test file generation logic, integration with Claude API, and comprehensive error handling.",
|
||||
"reasoning": "This task involves complex integration with an AI service (Claude), requires sophisticated prompt engineering, and needs to generate structured code files. The existing 3 subtasks are a good start but could be expanded to include more detailed steps for AI integration, error handling, and test file formatting."
|
||||
},
|
||||
{
|
||||
"taskId": 26,
|
||||
"taskTitle": "Implement Context Foundation for AI Operations",
|
||||
"complexityScore": 6,
|
||||
"recommendedSubtasks": 4,
|
||||
"expansionPrompt": "The current 4 subtasks for implementing the context foundation appear comprehensive. Consider if any additional subtasks are needed for testing, documentation, or integration with existing systems.",
|
||||
"reasoning": "This task involves creating a foundation for context integration with several well-defined components. The existing 4 subtasks cover the main implementation areas (context-file flag, cursor rules integration, context extraction utility, and command handler updates). The complexity is moderate as it requires careful integration with existing systems but has clear requirements."
|
||||
},
|
||||
{
|
||||
"taskId": 27,
|
||||
"taskTitle": "Implement Context Enhancements for AI Operations",
|
||||
"complexityScore": 7,
|
||||
"recommendedSubtasks": 4,
|
||||
"expansionPrompt": "The current 4 subtasks for implementing context enhancements appear well-structured. Consider if any additional subtasks are needed for testing, documentation, or performance optimization.",
|
||||
"reasoning": "This task builds upon the foundation from Task #26 and adds more sophisticated context handling features. The 4 existing subtasks cover the main implementation areas (code context extraction, task history context, PRD context integration, and context formatting). The complexity is higher than the foundation task due to the need for intelligent context selection and optimization."
|
||||
},
|
||||
{
|
||||
"taskId": 28,
|
||||
"taskTitle": "Implement Advanced ContextManager System",
|
||||
"complexityScore": 8,
|
||||
"recommendedSubtasks": 5,
|
||||
"expansionPrompt": "The current 5 subtasks for implementing the advanced ContextManager system appear comprehensive. Consider if any additional subtasks are needed for testing, documentation, or backward compatibility with previous context implementations.",
|
||||
"reasoning": "This task represents the most complex phase of the context implementation, requiring a sophisticated class design, optimization algorithms, and integration with multiple systems. The 5 existing subtasks cover the core implementation areas, but the complexity is high due to the need for intelligent context prioritization, token management, and performance monitoring."
|
||||
},
|
||||
{
|
||||
"taskId": 40,
|
||||
"taskTitle": "Implement 'plan' Command for Task Implementation Planning",
|
||||
"complexityScore": 5,
|
||||
"recommendedSubtasks": 4,
|
||||
"expansionPrompt": "The current 4 subtasks for implementing the 'plan' command appear well-structured. Consider if any additional subtasks are needed for testing, documentation, or integration with existing task management workflows.",
|
||||
"reasoning": "This task involves creating a new command that leverages AI to generate implementation plans. The existing 4 subtasks cover the main implementation areas (retrieving task content, generating plans with AI, formatting in XML, and error handling). The complexity is moderate as it builds on existing patterns for task updates but requires careful AI integration."
|
||||
},
|
||||
{
|
||||
"taskId": 41,
|
||||
"taskTitle": "Implement Visual Task Dependency Graph in Terminal",
|
||||
"complexityScore": 8,
|
||||
"recommendedSubtasks": 10,
|
||||
"expansionPrompt": "The current 10 subtasks for implementing the visual task dependency graph appear comprehensive. Consider if any additional subtasks are needed for performance optimization with large graphs or additional visualization options.",
|
||||
"reasoning": "This task involves creating a sophisticated visualization system for terminal display, which is inherently complex due to layout algorithms, ASCII/Unicode rendering, and handling complex dependency relationships. The 10 existing subtasks cover all major aspects of implementation, from CLI interface to accessibility features."
|
||||
},
|
||||
{
|
||||
"taskId": 42,
|
||||
"taskTitle": "Implement MCP-to-MCP Communication Protocol",
|
||||
"complexityScore": 9,
|
||||
"recommendedSubtasks": 8,
|
||||
"expansionPrompt": "The current 8 subtasks for implementing the MCP-to-MCP communication protocol appear well-structured. Consider if any additional subtasks are needed for security hardening, performance optimization, or comprehensive documentation.",
|
||||
"reasoning": "This task involves designing and implementing a complex communication protocol between different MCP tools and servers. It requires sophisticated adapter patterns, client-server architecture, and handling of multiple operational modes. The complexity is very high due to the need for standardization, security, and backward compatibility."
|
||||
},
|
||||
{
|
||||
"taskId": 44,
|
||||
"taskTitle": "Implement Task Automation with Webhooks and Event Triggers",
|
||||
"complexityScore": 8,
|
||||
"recommendedSubtasks": 7,
|
||||
"expansionPrompt": "The current 7 subtasks for implementing task automation with webhooks appear comprehensive. Consider if any additional subtasks are needed for security testing, rate limiting implementation, or webhook monitoring tools.",
|
||||
"reasoning": "This task involves creating a sophisticated event system with webhooks for integration with external services. The complexity is high due to the need for secure authentication, reliable delivery mechanisms, and handling of various webhook formats and protocols. The existing subtasks cover the main implementation areas but security and monitoring could be emphasized more."
|
||||
},
|
||||
{
|
||||
"taskId": 45,
|
||||
"taskTitle": "Implement GitHub Issue Import Feature",
|
||||
"complexityScore": 6,
|
||||
"recommendedSubtasks": 5,
|
||||
"expansionPrompt": "The current 5 subtasks for implementing the GitHub issue import feature appear well-structured. Consider if any additional subtasks are needed for handling GitHub API rate limiting, caching, or supporting additional issue metadata.",
|
||||
"reasoning": "This task involves integrating with the GitHub API to import issues as tasks. The complexity is moderate as it requires API authentication, data mapping, and error handling. The existing 5 subtasks cover the main implementation areas from design to end-to-end implementation."
|
||||
},
|
||||
{
|
||||
"taskId": 46,
|
||||
"taskTitle": "Implement ICE Analysis Command for Task Prioritization",
|
||||
"complexityScore": 7,
|
||||
"recommendedSubtasks": 5,
|
||||
"expansionPrompt": "The current 5 subtasks for implementing the ICE analysis command appear comprehensive. Consider if any additional subtasks are needed for visualization of ICE scores or integration with other prioritization methods.",
|
||||
"reasoning": "This task involves creating an AI-powered analysis system for task prioritization using the ICE methodology. The complexity is high due to the need for sophisticated scoring algorithms, AI integration, and report generation. The existing subtasks cover the main implementation areas from algorithm design to integration with existing systems."
|
||||
},
|
||||
{
|
||||
"taskId": 47,
|
||||
"taskTitle": "Enhance Task Suggestion Actions Card Workflow",
|
||||
"complexityScore": 6,
|
||||
"recommendedSubtasks": 6,
|
||||
"expansionPrompt": "The current 6 subtasks for enhancing the task suggestion actions card workflow appear well-structured. Consider if any additional subtasks are needed for user testing, accessibility improvements, or performance optimization.",
|
||||
"reasoning": "This task involves redesigning the UI workflow for task expansion and management. The complexity is moderate as it requires careful UX design and state management but builds on existing components. The 6 existing subtasks cover the main implementation areas from design to testing."
|
||||
},
|
||||
{
|
||||
"taskId": 48,
|
||||
"taskTitle": "Refactor Prompts into Centralized Structure",
|
||||
"complexityScore": 4,
|
||||
"recommendedSubtasks": 3,
|
||||
"expansionPrompt": "The current 3 subtasks for refactoring prompts into a centralized structure appear appropriate. Consider if any additional subtasks are needed for prompt versioning, documentation, or testing.",
|
||||
"reasoning": "This task involves a straightforward refactoring to improve code organization. The complexity is relatively low as it primarily involves moving code rather than creating new functionality. The 3 existing subtasks cover the main implementation areas from directory structure to integration."
|
||||
},
|
||||
{
|
||||
"taskId": 49,
|
||||
"taskTitle": "Implement Code Quality Analysis Command",
|
||||
"complexityScore": 8,
|
||||
"recommendedSubtasks": 6,
|
||||
"expansionPrompt": "The current 6 subtasks for implementing the code quality analysis command appear comprehensive. Consider if any additional subtasks are needed for performance optimization with large codebases or integration with existing code quality tools.",
|
||||
"reasoning": "This task involves creating a sophisticated code analysis system with pattern recognition, best practice verification, and AI-powered recommendations. The complexity is high due to the need for code parsing, complex analysis algorithms, and integration with AI services. The existing subtasks cover the main implementation areas from algorithm design to user interface."
|
||||
},
|
||||
{
|
||||
"taskId": 50,
|
||||
"taskTitle": "Implement Test Coverage Tracking System by Task",
|
||||
"complexityScore": 9,
|
||||
"recommendedSubtasks": 5,
|
||||
"expansionPrompt": "The current 5 subtasks for implementing the test coverage tracking system appear well-structured. Consider if any additional subtasks are needed for integration with CI/CD systems, performance optimization, or visualization tools.",
|
||||
"reasoning": "This task involves creating a complex system that maps test coverage to specific tasks and subtasks. The complexity is very high due to the need for sophisticated data structures, integration with coverage tools, and AI-powered test generation. The existing subtasks are comprehensive and cover the main implementation areas from data structure design to AI integration."
|
||||
},
|
||||
{
|
||||
"taskId": 51,
|
||||
"taskTitle": "Implement Perplexity Research Command",
|
||||
"complexityScore": 6,
|
||||
"recommendedSubtasks": 5,
|
||||
"expansionPrompt": "The current 5 subtasks for implementing the Perplexity research command appear comprehensive. Consider if any additional subtasks are needed for caching optimization, result formatting, or integration with other research tools.",
|
||||
"reasoning": "This task involves creating a new command that integrates with the Perplexity AI API for research. The complexity is moderate as it requires API integration, context extraction, and result formatting. The 5 existing subtasks cover the main implementation areas from API client to caching system."
|
||||
},
|
||||
{
|
||||
"taskId": 52,
|
||||
"taskTitle": "Implement Task Suggestion Command for CLI",
|
||||
"complexityScore": 6,
|
||||
"recommendedSubtasks": 5,
|
||||
"expansionPrompt": "The current 5 subtasks for implementing the task suggestion command appear well-structured. Consider if any additional subtasks are needed for suggestion quality evaluation, user feedback collection, or integration with existing task workflows.",
|
||||
"reasoning": "This task involves creating a new CLI command that generates contextually relevant task suggestions using AI. The complexity is moderate as it requires AI integration, context collection, and interactive CLI interfaces. The existing subtasks cover the main implementation areas from data collection to user interface."
|
||||
},
|
||||
{
|
||||
"taskId": 53,
|
||||
"taskTitle": "Implement Subtask Suggestion Feature for Parent Tasks",
|
||||
"complexityScore": 6,
|
||||
"recommendedSubtasks": 6,
|
||||
"expansionPrompt": "The current 6 subtasks for implementing the subtask suggestion feature appear comprehensive. Consider if any additional subtasks are needed for suggestion quality metrics, user feedback collection, or performance optimization.",
|
||||
"reasoning": "This task involves creating a feature that suggests contextually relevant subtasks for parent tasks. The complexity is moderate as it builds on existing task management systems but requires sophisticated AI integration and context analysis. The 6 existing subtasks cover the main implementation areas from validation to testing."
|
||||
},
|
||||
{
|
||||
"taskId": 55,
|
||||
"taskTitle": "Implement Positional Arguments Support for CLI Commands",
|
||||
"complexityScore": 5,
|
||||
"recommendedSubtasks": 5,
|
||||
"expansionPrompt": "The current 5 subtasks for implementing positional arguments support appear well-structured. Consider if any additional subtasks are needed for backward compatibility testing, documentation updates, or user experience improvements.",
|
||||
"reasoning": "This task involves modifying the command parsing logic to support positional arguments alongside the existing flag-based syntax. The complexity is moderate as it requires careful handling of different argument styles and edge cases. The 5 existing subtasks cover the main implementation areas from analysis to documentation."
|
||||
},
|
||||
{
|
||||
"taskId": 57,
|
||||
"taskTitle": "Enhance Task-Master CLI User Experience and Interface",
|
||||
"complexityScore": 7,
|
||||
"recommendedSubtasks": 6,
|
||||
"expansionPrompt": "The current 6 subtasks for enhancing the CLI user experience appear comprehensive. Consider if any additional subtasks are needed for accessibility testing, internationalization, or performance optimization.",
|
||||
"reasoning": "This task involves a significant overhaul of the CLI interface to improve user experience. The complexity is high due to the breadth of changes (logging, visual elements, interactive components, etc.) and the need for consistent design across all commands. The 6 existing subtasks cover the main implementation areas from log management to help systems."
|
||||
},
|
||||
{
|
||||
"taskId": 60,
|
||||
"taskTitle": "Implement Mentor System with Round-Table Discussion Feature",
|
||||
"complexityScore": 8,
|
||||
"recommendedSubtasks": 7,
|
||||
"expansionPrompt": "The current 7 subtasks for implementing the mentor system appear well-structured. Consider if any additional subtasks are needed for mentor personality consistency, discussion quality evaluation, or performance optimization with multiple mentors.",
|
||||
"reasoning": "This task involves creating a sophisticated mentor simulation system with round-table discussions. The complexity is high due to the need for personality simulation, complex LLM integration, and structured discussion management. The 7 existing subtasks cover the main implementation areas from architecture to testing."
|
||||
},
|
||||
{
|
||||
"taskId": 62,
|
||||
"taskTitle": "Add --simple Flag to Update Commands for Direct Text Input",
|
||||
"complexityScore": 4,
|
||||
"recommendedSubtasks": 8,
|
||||
"expansionPrompt": "The current 8 subtasks for implementing the --simple flag appear comprehensive. Consider if any additional subtasks are needed for user experience testing or documentation updates.",
|
||||
"reasoning": "This task involves adding a simple flag option to bypass AI processing for updates. The complexity is relatively low as it primarily involves modifying existing command handlers and adding a flag. The 8 existing subtasks are very detailed and cover all aspects of implementation from command parsing to testing."
|
||||
},
|
||||
{
|
||||
"taskId": 63,
|
||||
"taskTitle": "Add pnpm Support for the Taskmaster Package",
|
||||
"complexityScore": 5,
|
||||
"recommendedSubtasks": 8,
|
||||
"expansionPrompt": "The current 8 subtasks for adding pnpm support appear comprehensive. Consider if any additional subtasks are needed for CI/CD integration, performance comparison, or documentation updates.",
|
||||
"reasoning": "This task involves ensuring the package works correctly with pnpm as an alternative package manager. The complexity is moderate as it requires careful testing of installation processes and scripts across different environments. The 8 existing subtasks cover all major aspects from documentation to binary verification."
|
||||
},
|
||||
{
|
||||
"taskId": 64,
|
||||
"taskTitle": "Add Yarn Support for Taskmaster Installation",
|
||||
"complexityScore": 5,
|
||||
"recommendedSubtasks": 9,
|
||||
"expansionPrompt": "The current 9 subtasks for adding Yarn support appear comprehensive. Consider if any additional subtasks are needed for performance testing, CI/CD integration, or compatibility with different Yarn versions.",
|
||||
"reasoning": "This task involves ensuring the package works correctly with Yarn as an alternative package manager. The complexity is moderate as it requires careful testing of installation processes and scripts across different environments. The 9 existing subtasks are very detailed and cover all aspects from configuration to testing."
|
||||
},
|
||||
{
|
||||
"taskId": 65,
|
||||
"taskTitle": "Add Bun Support for Taskmaster Installation",
|
||||
"complexityScore": 6,
|
||||
"recommendedSubtasks": 6,
|
||||
"expansionPrompt": "The current 6 subtasks for adding Bun support appear well-structured. Consider if any additional subtasks are needed for handling Bun-specific issues, performance testing, or documentation updates.",
|
||||
"reasoning": "This task involves adding support for the newer Bun package manager. The complexity is slightly higher than the other package manager tasks due to Bun's differences from Node.js and potential compatibility issues. The 6 existing subtasks cover the main implementation areas from research to documentation."
|
||||
},
|
||||
{
|
||||
"taskId": 67,
|
||||
"taskTitle": "Add CLI JSON output and Cursor keybindings integration",
|
||||
"complexityScore": 5,
|
||||
"recommendedSubtasks": 5,
|
||||
"expansionPrompt": "The current 5 subtasks for implementing JSON output and Cursor keybindings appear well-structured. Consider if any additional subtasks are needed for testing across different operating systems, documentation updates, or user experience improvements.",
|
||||
"reasoning": "This task involves two distinct features: adding JSON output to CLI commands and creating a keybindings installation command. The complexity is moderate as it requires careful handling of different output formats and OS-specific file paths. The 5 existing subtasks cover the main implementation areas for both features."
|
||||
},
|
||||
{
|
||||
"taskId": 68,
|
||||
"taskTitle": "Ability to create tasks without parsing PRD",
|
||||
"complexityScore": 3,
|
||||
"recommendedSubtasks": 2,
|
||||
"expansionPrompt": "The current 2 subtasks for implementing task creation without PRD appear appropriate. Consider if any additional subtasks are needed for validation, error handling, or integration with existing task management workflows.",
|
||||
"reasoning": "This task involves a relatively simple modification to allow task creation without requiring a PRD document. The complexity is low as it primarily involves creating a form interface and saving functionality. The 2 existing subtasks cover the main implementation areas of UI design and data saving."
|
||||
},
|
||||
{
|
||||
"taskId": 72,
|
||||
"taskTitle": "Implement PDF Generation for Project Progress and Dependency Overview",
|
||||
"complexityScore": 7,
|
||||
"recommendedSubtasks": 6,
|
||||
"expansionPrompt": "The current 6 subtasks for implementing PDF generation appear comprehensive. Consider if any additional subtasks are needed for handling large projects, additional visualization options, or integration with existing reporting tools.",
|
||||
"reasoning": "This task involves creating a feature to generate PDF reports of project progress and dependency visualization. The complexity is high due to the need for PDF generation, data collection, and visualization integration. The 6 existing subtasks cover the main implementation areas from library selection to export options."
|
||||
},
|
||||
{
|
||||
"taskId": 75,
|
||||
"taskTitle": "Integrate Google Search Grounding for Research Role",
|
||||
"complexityScore": 5,
|
||||
"recommendedSubtasks": 4,
|
||||
"expansionPrompt": "The current 4 subtasks for integrating Google Search Grounding appear well-structured. Consider if any additional subtasks are needed for testing with different query types, error handling, or performance optimization.",
|
||||
"reasoning": "This task involves updating the AI service layer to enable Google Search Grounding for research roles. The complexity is moderate as it requires careful integration with the existing AI service architecture and conditional logic. The 4 existing subtasks cover the main implementation areas from service layer modification to testing."
|
||||
},
|
||||
{
|
||||
"taskId": 76,
|
||||
"taskTitle": "Develop E2E Test Framework for Taskmaster MCP Server (FastMCP over stdio)",
|
||||
"complexityScore": 8,
|
||||
"recommendedSubtasks": 7,
|
||||
"expansionPrompt": "The current 7 subtasks for developing the E2E test framework appear comprehensive. Consider if any additional subtasks are needed for test result reporting, CI/CD integration, or performance benchmarking.",
|
||||
"reasoning": "This task involves creating a sophisticated end-to-end testing framework for the MCP server. The complexity is high due to the need for subprocess management, protocol handling, and robust test case definition. The 7 existing subtasks cover the main implementation areas from architecture to documentation."
|
||||
},
|
||||
{
|
||||
"taskId": 77,
|
||||
"taskTitle": "Implement AI Usage Telemetry for Taskmaster (with external analytics endpoint)",
|
||||
"complexityScore": 7,
|
||||
"recommendedSubtasks": 18,
|
||||
"expansionPrompt": "The current 18 subtasks for implementing AI usage telemetry appear very comprehensive. Consider if any additional subtasks are needed for security hardening, privacy compliance, or user feedback collection.",
|
||||
"reasoning": "This task involves creating a telemetry system to track AI usage metrics. The complexity is high due to the need for secure data transmission, comprehensive data collection, and integration across multiple commands. The 18 existing subtasks are extremely detailed and cover all aspects of implementation from core utility to provider-specific updates."
|
||||
},
|
||||
{
|
||||
"taskId": 80,
|
||||
"taskTitle": "Implement Unique User ID Generation and Storage During Installation",
|
||||
"complexityScore": 4,
|
||||
"recommendedSubtasks": 5,
|
||||
"expansionPrompt": "The current 5 subtasks for implementing unique user ID generation appear well-structured. Consider if any additional subtasks are needed for privacy compliance, security auditing, or integration with the telemetry system.",
|
||||
"reasoning": "This task involves generating and storing a unique user identifier during installation. The complexity is relatively low as it primarily involves UUID generation and configuration file management. The 5 existing subtasks cover the main implementation areas from script structure to documentation."
|
||||
},
|
||||
{
|
||||
"taskId": 81,
|
||||
"taskTitle": "Task #81: Implement Comprehensive Local Telemetry System with Future Server Integration Capability",
|
||||
"complexityScore": 8,
|
||||
"recommendedSubtasks": 6,
|
||||
"expansionPrompt": "The current 6 subtasks for implementing the comprehensive local telemetry system appear well-structured. Consider if any additional subtasks are needed for data migration, storage optimization, or visualization tools.",
|
||||
"reasoning": "This task involves expanding the telemetry system to capture additional metrics and implement local storage with future server integration capability. The complexity is high due to the breadth of data collection, storage requirements, and privacy considerations. The 6 existing subtasks cover the main implementation areas from data collection to user-facing benefits."
|
||||
},
|
||||
{
|
||||
"taskId": 82,
|
||||
"taskTitle": "Update supported-models.json with token limit fields",
|
||||
"complexityScore": 3,
|
||||
"recommendedSubtasks": 1,
|
||||
"expansionPrompt": "This task appears straightforward enough to be implemented without further subtasks. Focus on researching accurate token limit values for each model and ensuring backward compatibility.",
|
||||
"reasoning": "This task involves a simple update to the supported-models.json file to include new token limit fields. The complexity is low as it primarily involves research and data entry. No subtasks are necessary as the task is well-defined and focused."
|
||||
},
|
||||
{
|
||||
"taskId": 83,
|
||||
"taskTitle": "Update config-manager.js defaults and getters",
|
||||
"complexityScore": 4,
|
||||
"recommendedSubtasks": 1,
|
||||
"expansionPrompt": "This task appears straightforward enough to be implemented without further subtasks. Focus on updating the DEFAULTS object and related getter functions while maintaining backward compatibility.",
|
||||
"reasoning": "This task involves updating the config-manager.js module to replace maxTokens with more specific token limit fields. The complexity is relatively low as it primarily involves modifying existing code rather than creating new functionality. No subtasks are necessary as the task is well-defined and focused."
|
||||
},
|
||||
{
|
||||
"taskId": 84,
|
||||
"taskTitle": "Implement token counting utility",
|
||||
"complexityScore": 5,
|
||||
"recommendedSubtasks": 1,
|
||||
"expansionPrompt": "This task appears well-defined enough to be implemented without further subtasks. Focus on implementing accurate token counting for different models and proper fallback mechanisms.",
|
||||
"reasoning": "This task involves creating a utility function to count tokens for different AI models. The complexity is moderate as it requires integration with the tiktoken library and handling different tokenization schemes. No subtasks are necessary as the task is well-defined and focused."
|
||||
},
|
||||
{
|
||||
"taskId": 69,
|
||||
"taskTitle": "Enhance Analyze Complexity for Specific Task IDs",
|
||||
"complexityScore": 7,
|
||||
"recommendedSubtasks": 6,
|
||||
"expansionPrompt": "Break down the task 'Enhance Analyze Complexity for Specific Task IDs' into 6 subtasks focusing on: 1) Core logic modification to accept ID parameters, 2) Report merging functionality, 3) CLI interface updates, 4) MCP tool integration, 5) Documentation updates, and 6) Comprehensive testing across all components.",
|
||||
"reasoning": "This task involves modifying existing functionality across multiple components (core logic, CLI, MCP) with complex logic for filtering tasks and merging reports. The implementation requires careful handling of different parameter combinations and edge cases. The task has interdependent components that need to work together seamlessly, and the report merging functionality adds significant complexity."
|
||||
},
|
||||
{
|
||||
"taskId": 70,
|
||||
"taskTitle": "Implement 'diagram' command for Mermaid diagram generation",
|
||||
"complexityScore": 6,
|
||||
"recommendedSubtasks": 5,
|
||||
"expansionPrompt": "Break down the 'diagram' command implementation into 5 subtasks: 1) Command interface and parameter handling, 2) Task data extraction and transformation to Mermaid syntax, 3) Diagram rendering with status color coding, 4) Output formatting and file export functionality, and 5) Error handling and edge case management.",
|
||||
"reasoning": "This task requires implementing a new feature rather than modifying existing code, which reduces complexity from integration challenges. However, it involves working with visualization logic, dependency mapping, and multiple output formats. The color coding based on status and handling of dependency relationships adds moderate complexity. The task is well-defined but requires careful attention to diagram formatting and error handling."
|
||||
},
|
||||
{
|
||||
"taskId": 85,
|
||||
"taskTitle": "Update ai-services-unified.js for dynamic token limits",
|
||||
"complexityScore": 7,
|
||||
"recommendedSubtasks": 5,
|
||||
"expansionPrompt": "Break down the update of ai-services-unified.js for dynamic token limits into subtasks such as: (1) Import and integrate the token counting utility, (2) Refactor _unifiedServiceRunner to calculate and enforce dynamic token limits, (3) Update error handling for token limit violations, (4) Add and verify logging for token usage, (5) Write and execute tests for various prompt and model scenarios.",
|
||||
"reasoning": "This task involves significant code changes to a core function, integration of a new utility, dynamic logic for multiple models, and robust error handling. It also requires comprehensive testing for edge cases and integration, making it moderately complex and best managed by splitting into focused subtasks."
|
||||
},
|
||||
{
|
||||
"taskId": 86,
|
||||
"taskTitle": "Update .taskmasterconfig schema and user guide",
|
||||
"complexityScore": 6,
|
||||
"recommendedSubtasks": 4,
|
||||
"expansionPrompt": "Expand this task into subtasks: (1) Draft a migration guide for users, (2) Update user documentation to explain new config fields, (3) Modify schema validation logic in config-manager.js, (4) Test and validate backward compatibility and error messaging.",
|
||||
"reasoning": "The task spans documentation, schema changes, migration guidance, and validation logic. While not algorithmically complex, it requires careful coordination and thorough testing to ensure a smooth user transition and robust validation."
|
||||
},
|
||||
{
|
||||
"taskId": 87,
|
||||
"taskTitle": "Implement validation and error handling",
|
||||
"complexityScore": 5,
|
||||
"recommendedSubtasks": 4,
|
||||
"expansionPrompt": "Decompose this task into: (1) Add validation logic for model and config loading, (2) Implement error handling and fallback mechanisms, (3) Enhance logging and reporting for token usage, (4) Develop helper functions for configuration suggestions and improvements.",
|
||||
"reasoning": "This task is primarily about adding validation, error handling, and logging. While important for robustness, the logic is straightforward and can be modularized into a few clear subtasks."
|
||||
},
|
||||
{
|
||||
"taskId": 89,
|
||||
"taskTitle": "Introduce Prioritize Command with Enhanced Priority Levels",
|
||||
"complexityScore": 6,
|
||||
"recommendedSubtasks": 5,
|
||||
"expansionPrompt": "Expand this task into: (1) Implement the prioritize command with all required flags and shorthands, (2) Update CLI output and help documentation for new priority levels, (3) Ensure backward compatibility with existing commands, (4) Add error handling for invalid inputs, (5) Write and run tests for all command scenarios.",
|
||||
"reasoning": "This CLI feature requires command parsing, updating internal logic for new priority levels, documentation, and robust error handling. The complexity is moderate due to the need for backward compatibility and comprehensive testing."
|
||||
},
|
||||
{
|
||||
"taskId": 90,
|
||||
"taskTitle": "Implement Subtask Progress Analyzer and Reporting System",
|
||||
"complexityScore": 8,
|
||||
"recommendedSubtasks": 6,
|
||||
"expansionPrompt": "Break down the analyzer implementation into: (1) Design and implement progress tracking logic, (2) Develop status validation and issue detection, (3) Build the reporting system with multiple output formats, (4) Integrate analyzer with the existing task management system, (5) Optimize for performance and scalability, (6) Write unit, integration, and performance tests.",
|
||||
"reasoning": "This is a complex, multi-faceted feature involving data analysis, reporting, integration, and performance optimization. It touches many parts of the system and requires careful design, making it one of the most complex tasks in the list."
|
||||
},
|
||||
{
|
||||
"taskId": 91,
|
||||
"taskTitle": "Implement Move Command for Tasks and Subtasks",
|
||||
"complexityScore": 7,
|
||||
"recommendedSubtasks": 5,
|
||||
"expansionPrompt": "Expand this task into: (1) Implement move logic for tasks and subtasks, (2) Handle edge cases (invalid ids, non-existent parents, circular dependencies), (3) Update CLI to support move command with flags, (4) Ensure data integrity and update relationships, (5) Write and execute tests for various move scenarios.",
|
||||
"reasoning": "Moving tasks and subtasks requires careful handling of hierarchical data, edge cases, and data integrity. The command must be robust and user-friendly, necessitating multiple focused subtasks for safe implementation."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -371,11 +371,10 @@ describe('Unified AI Services', () => {
|
||||
|
||||
test('should skip provider with missing API key and try next in fallback sequence', async () => {
|
||||
// Setup isApiKeySet to return false for anthropic but true for perplexity
|
||||
mockIsApiKeySet
|
||||
.mockImplementation((provider, session, root) => {
|
||||
if (provider === 'anthropic') return false; // Main provider has no key
|
||||
return true; // Other providers have keys
|
||||
});
|
||||
mockIsApiKeySet.mockImplementation((provider, session, root) => {
|
||||
if (provider === 'anthropic') return false; // Main provider has no key
|
||||
return true; // Other providers have keys
|
||||
});
|
||||
|
||||
// Mock perplexity text response (since we'll skip anthropic)
|
||||
mockGeneratePerplexityText.mockResolvedValue({
|
||||
@@ -392,16 +391,28 @@ describe('Unified AI Services', () => {
|
||||
const result = await generateTextService(params);
|
||||
|
||||
// Should have gotten the perplexity response
|
||||
expect(result.mainResult).toBe('Perplexity response (skipped to research)');
|
||||
expect(result.mainResult).toBe(
|
||||
'Perplexity response (skipped to research)'
|
||||
);
|
||||
|
||||
// Should check API keys
|
||||
expect(mockIsApiKeySet).toHaveBeenCalledWith('anthropic', params.session, fakeProjectRoot);
|
||||
expect(mockIsApiKeySet).toHaveBeenCalledWith('perplexity', params.session, fakeProjectRoot);
|
||||
expect(mockIsApiKeySet).toHaveBeenCalledWith(
|
||||
'anthropic',
|
||||
params.session,
|
||||
fakeProjectRoot
|
||||
);
|
||||
expect(mockIsApiKeySet).toHaveBeenCalledWith(
|
||||
'perplexity',
|
||||
params.session,
|
||||
fakeProjectRoot
|
||||
);
|
||||
|
||||
// Should log a warning
|
||||
expect(mockLog).toHaveBeenCalledWith(
|
||||
'warn',
|
||||
expect.stringContaining(`Skipping role 'main' (Provider: anthropic): API key not set or invalid.`)
|
||||
expect.stringContaining(
|
||||
`Skipping role 'main' (Provider: anthropic): API key not set or invalid.`
|
||||
)
|
||||
);
|
||||
|
||||
// Should NOT call anthropic provider
|
||||
@@ -413,24 +424,22 @@ describe('Unified AI Services', () => {
|
||||
|
||||
test('should skip multiple providers with missing API keys and use first available', async () => {
|
||||
// Setup: Main and fallback providers have no keys, only research has a key
|
||||
mockIsApiKeySet
|
||||
.mockImplementation((provider, session, root) => {
|
||||
if (provider === 'anthropic') return false; // Main and fallback are both anthropic
|
||||
if (provider === 'perplexity') return true; // Research has a key
|
||||
return false;
|
||||
});
|
||||
mockIsApiKeySet.mockImplementation((provider, session, root) => {
|
||||
if (provider === 'anthropic') return false; // Main and fallback are both anthropic
|
||||
if (provider === 'perplexity') return true; // Research has a key
|
||||
return false;
|
||||
});
|
||||
|
||||
// Define different providers for testing multiple skips
|
||||
mockGetFallbackProvider.mockReturnValue('openai'); // Different from main
|
||||
mockGetFallbackModelId.mockReturnValue('test-openai-model');
|
||||
|
||||
// Mock isApiKeySet to return false for both main and fallback
|
||||
mockIsApiKeySet
|
||||
.mockImplementation((provider, session, root) => {
|
||||
if (provider === 'anthropic') return false; // Main provider has no key
|
||||
if (provider === 'openai') return false; // Fallback provider has no key
|
||||
return true; // Research provider has a key
|
||||
});
|
||||
mockIsApiKeySet.mockImplementation((provider, session, root) => {
|
||||
if (provider === 'anthropic') return false; // Main provider has no key
|
||||
if (provider === 'openai') return false; // Fallback provider has no key
|
||||
return true; // Research provider has a key
|
||||
});
|
||||
|
||||
// Mock perplexity text response (since we'll skip to research)
|
||||
mockGeneratePerplexityText.mockResolvedValue({
|
||||
@@ -447,21 +456,39 @@ describe('Unified AI Services', () => {
|
||||
const result = await generateTextService(params);
|
||||
|
||||
// Should have gotten the perplexity (research) response
|
||||
expect(result.mainResult).toBe('Research response after skipping main and fallback');
|
||||
expect(result.mainResult).toBe(
|
||||
'Research response after skipping main and fallback'
|
||||
);
|
||||
|
||||
// Should check API keys for all three roles
|
||||
expect(mockIsApiKeySet).toHaveBeenCalledWith('anthropic', params.session, fakeProjectRoot);
|
||||
expect(mockIsApiKeySet).toHaveBeenCalledWith('openai', params.session, fakeProjectRoot);
|
||||
expect(mockIsApiKeySet).toHaveBeenCalledWith('perplexity', params.session, fakeProjectRoot);
|
||||
expect(mockIsApiKeySet).toHaveBeenCalledWith(
|
||||
'anthropic',
|
||||
params.session,
|
||||
fakeProjectRoot
|
||||
);
|
||||
expect(mockIsApiKeySet).toHaveBeenCalledWith(
|
||||
'openai',
|
||||
params.session,
|
||||
fakeProjectRoot
|
||||
);
|
||||
expect(mockIsApiKeySet).toHaveBeenCalledWith(
|
||||
'perplexity',
|
||||
params.session,
|
||||
fakeProjectRoot
|
||||
);
|
||||
|
||||
// Should log warnings for both skipped providers
|
||||
expect(mockLog).toHaveBeenCalledWith(
|
||||
'warn',
|
||||
expect.stringContaining(`Skipping role 'main' (Provider: anthropic): API key not set or invalid.`)
|
||||
expect.stringContaining(
|
||||
`Skipping role 'main' (Provider: anthropic): API key not set or invalid.`
|
||||
)
|
||||
);
|
||||
expect(mockLog).toHaveBeenCalledWith(
|
||||
'warn',
|
||||
expect.stringContaining(`Skipping role 'fallback' (Provider: openai): API key not set or invalid.`)
|
||||
expect.stringContaining(
|
||||
`Skipping role 'fallback' (Provider: openai): API key not set or invalid.`
|
||||
)
|
||||
);
|
||||
|
||||
// Should NOT call skipped providers
|
||||
@@ -490,21 +517,29 @@ describe('Unified AI Services', () => {
|
||||
// Should log warnings for all skipped providers
|
||||
expect(mockLog).toHaveBeenCalledWith(
|
||||
'warn',
|
||||
expect.stringContaining(`Skipping role 'main' (Provider: anthropic): API key not set or invalid.`)
|
||||
expect.stringContaining(
|
||||
`Skipping role 'main' (Provider: anthropic): API key not set or invalid.`
|
||||
)
|
||||
);
|
||||
expect(mockLog).toHaveBeenCalledWith(
|
||||
'warn',
|
||||
expect.stringContaining(`Skipping role 'fallback' (Provider: anthropic): API key not set or invalid.`)
|
||||
expect.stringContaining(
|
||||
`Skipping role 'fallback' (Provider: anthropic): API key not set or invalid.`
|
||||
)
|
||||
);
|
||||
expect(mockLog).toHaveBeenCalledWith(
|
||||
'warn',
|
||||
expect.stringContaining(`Skipping role 'research' (Provider: perplexity): API key not set or invalid.`)
|
||||
expect.stringContaining(
|
||||
`Skipping role 'research' (Provider: perplexity): API key not set or invalid.`
|
||||
)
|
||||
);
|
||||
|
||||
// Should log final error
|
||||
expect(mockLog).toHaveBeenCalledWith(
|
||||
'error',
|
||||
expect.stringContaining('All roles in the sequence [main, fallback, research] failed.')
|
||||
expect.stringContaining(
|
||||
'All roles in the sequence [main, fallback, research] failed.'
|
||||
)
|
||||
);
|
||||
|
||||
// Should NOT call any providers
|
||||
@@ -548,11 +583,10 @@ describe('Unified AI Services', () => {
|
||||
const customSession = { env: { ANTHROPIC_API_KEY: 'session-api-key' } };
|
||||
|
||||
// Setup API key check to verify the session is passed correctly
|
||||
mockIsApiKeySet
|
||||
.mockImplementation((provider, session, root) => {
|
||||
// Only return true if the correct session was provided
|
||||
return session === customSession;
|
||||
});
|
||||
mockIsApiKeySet.mockImplementation((provider, session, root) => {
|
||||
// Only return true if the correct session was provided
|
||||
return session === customSession;
|
||||
});
|
||||
|
||||
// Mock the anthropic response
|
||||
mockGenerateAnthropicText.mockResolvedValue({
|
||||
@@ -569,7 +603,11 @@ describe('Unified AI Services', () => {
|
||||
const result = await generateTextService(params);
|
||||
|
||||
// Should check API key with the custom session
|
||||
expect(mockIsApiKeySet).toHaveBeenCalledWith('anthropic', customSession, fakeProjectRoot);
|
||||
expect(mockIsApiKeySet).toHaveBeenCalledWith(
|
||||
'anthropic',
|
||||
customSession,
|
||||
fakeProjectRoot
|
||||
);
|
||||
|
||||
// Should have gotten the anthropic response
|
||||
expect(result.mainResult).toBe('Anthropic response with session key');
|
||||
|
||||
@@ -256,7 +256,10 @@ describe('Config Manager Module', () => {
|
||||
configManager.validateProviderModelCombination('ollama', 'any-model')
|
||||
).toBe(false);
|
||||
expect(
|
||||
configManager.validateProviderModelCombination('openrouter', 'any/model')
|
||||
configManager.validateProviderModelCombination(
|
||||
'openrouter',
|
||||
'any/model'
|
||||
)
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
@@ -367,7 +370,8 @@ describe('Config Manager Module', () => {
|
||||
test('should merge defaults for partial config file', () => {
|
||||
// Arrange
|
||||
mockReadFileSync.mockImplementation((filePath) => {
|
||||
if (filePath === MOCK_CONFIG_PATH) return JSON.stringify(PARTIAL_CONFIG);
|
||||
if (filePath === MOCK_CONFIG_PATH)
|
||||
return JSON.stringify(PARTIAL_CONFIG);
|
||||
if (path.basename(filePath) === 'supported-models.json') {
|
||||
return JSON.stringify({
|
||||
openai: [{ id: 'gpt-4-turbo' }],
|
||||
@@ -391,7 +395,10 @@ describe('Config Manager Module', () => {
|
||||
// Assert: Construct expected merged config
|
||||
const expectedMergedConfig = {
|
||||
models: {
|
||||
main: { ...DEFAULT_CONFIG.models.main, ...PARTIAL_CONFIG.models.main },
|
||||
main: {
|
||||
...DEFAULT_CONFIG.models.main,
|
||||
...PARTIAL_CONFIG.models.main
|
||||
},
|
||||
research: { ...DEFAULT_CONFIG.models.research },
|
||||
fallback: { ...DEFAULT_CONFIG.models.fallback }
|
||||
},
|
||||
@@ -456,7 +463,9 @@ describe('Config Manager Module', () => {
|
||||
// Assert
|
||||
expect(config).toEqual(DEFAULT_CONFIG);
|
||||
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
||||
expect.stringContaining(`Permission denied. Using default configuration.`)
|
||||
expect.stringContaining(
|
||||
`Permission denied. Using default configuration.`
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
@@ -688,72 +697,182 @@ describe('Config Manager Module', () => {
|
||||
// Test cases: [providerName, envVarName, keyValue, expectedResult, testName]
|
||||
const testCases = [
|
||||
// Valid Keys
|
||||
['anthropic', 'ANTHROPIC_API_KEY', 'sk-valid-key', true, 'valid Anthropic key'],
|
||||
['openai', 'OPENAI_API_KEY', 'sk-another-valid-key', true, 'valid OpenAI key'],
|
||||
['perplexity', 'PERPLEXITY_API_KEY', 'pplx-valid', true, 'valid Perplexity key'],
|
||||
['google', 'GOOGLE_API_KEY', 'google-valid-key', true, 'valid Google key'],
|
||||
['mistral', 'MISTRAL_API_KEY', 'mistral-valid-key', true, 'valid Mistral key'],
|
||||
['openrouter', 'OPENROUTER_API_KEY', 'or-valid-key', true, 'valid OpenRouter key'],
|
||||
[
|
||||
'anthropic',
|
||||
'ANTHROPIC_API_KEY',
|
||||
'sk-valid-key',
|
||||
true,
|
||||
'valid Anthropic key'
|
||||
],
|
||||
[
|
||||
'openai',
|
||||
'OPENAI_API_KEY',
|
||||
'sk-another-valid-key',
|
||||
true,
|
||||
'valid OpenAI key'
|
||||
],
|
||||
[
|
||||
'perplexity',
|
||||
'PERPLEXITY_API_KEY',
|
||||
'pplx-valid',
|
||||
true,
|
||||
'valid Perplexity key'
|
||||
],
|
||||
[
|
||||
'google',
|
||||
'GOOGLE_API_KEY',
|
||||
'google-valid-key',
|
||||
true,
|
||||
'valid Google key'
|
||||
],
|
||||
[
|
||||
'mistral',
|
||||
'MISTRAL_API_KEY',
|
||||
'mistral-valid-key',
|
||||
true,
|
||||
'valid Mistral key'
|
||||
],
|
||||
[
|
||||
'openrouter',
|
||||
'OPENROUTER_API_KEY',
|
||||
'or-valid-key',
|
||||
true,
|
||||
'valid OpenRouter key'
|
||||
],
|
||||
['xai', 'XAI_API_KEY', 'xai-valid-key', true, 'valid XAI key'],
|
||||
['azure', 'AZURE_OPENAI_API_KEY', 'azure-valid-key', true, 'valid Azure key'],
|
||||
[
|
||||
'azure',
|
||||
'AZURE_OPENAI_API_KEY',
|
||||
'azure-valid-key',
|
||||
true,
|
||||
'valid Azure key'
|
||||
],
|
||||
|
||||
// Ollama (special case - no key needed)
|
||||
['ollama', 'OLLAMA_API_KEY', undefined, true, 'Ollama provider (no key needed)'], // OLLAMA_API_KEY might not be in keyMap
|
||||
[
|
||||
'ollama',
|
||||
'OLLAMA_API_KEY',
|
||||
undefined,
|
||||
true,
|
||||
'Ollama provider (no key needed)'
|
||||
], // OLLAMA_API_KEY might not be in keyMap
|
||||
|
||||
// Invalid / Missing Keys
|
||||
['anthropic', 'ANTHROPIC_API_KEY', undefined, false, 'missing Anthropic key'],
|
||||
[
|
||||
'anthropic',
|
||||
'ANTHROPIC_API_KEY',
|
||||
undefined,
|
||||
false,
|
||||
'missing Anthropic key'
|
||||
],
|
||||
['anthropic', 'ANTHROPIC_API_KEY', null, false, 'null Anthropic key'],
|
||||
['openai', 'OPENAI_API_KEY', '', false, 'empty OpenAI key'],
|
||||
['perplexity', 'PERPLEXITY_API_KEY', ' ', false, 'whitespace Perplexity key'],
|
||||
[
|
||||
'perplexity',
|
||||
'PERPLEXITY_API_KEY',
|
||||
' ',
|
||||
false,
|
||||
'whitespace Perplexity key'
|
||||
],
|
||||
|
||||
// Placeholder Keys
|
||||
['google', 'GOOGLE_API_KEY', 'YOUR_GOOGLE_API_KEY_HERE', false, 'placeholder Google key (YOUR_..._HERE)'],
|
||||
['mistral', 'MISTRAL_API_KEY', 'MISTRAL_KEY_HERE', false, 'placeholder Mistral key (..._KEY_HERE)'],
|
||||
['openrouter', 'OPENROUTER_API_KEY', 'ENTER_OPENROUTER_KEY_HERE', false, 'placeholder OpenRouter key (general ...KEY_HERE)'],
|
||||
[
|
||||
'google',
|
||||
'GOOGLE_API_KEY',
|
||||
'YOUR_GOOGLE_API_KEY_HERE',
|
||||
false,
|
||||
'placeholder Google key (YOUR_..._HERE)'
|
||||
],
|
||||
[
|
||||
'mistral',
|
||||
'MISTRAL_API_KEY',
|
||||
'MISTRAL_KEY_HERE',
|
||||
false,
|
||||
'placeholder Mistral key (..._KEY_HERE)'
|
||||
],
|
||||
[
|
||||
'openrouter',
|
||||
'OPENROUTER_API_KEY',
|
||||
'ENTER_OPENROUTER_KEY_HERE',
|
||||
false,
|
||||
'placeholder OpenRouter key (general ...KEY_HERE)'
|
||||
],
|
||||
|
||||
// Unknown provider
|
||||
['unknownprovider', 'UNKNOWN_KEY', 'any-key', false, 'unknown provider'],
|
||||
['unknownprovider', 'UNKNOWN_KEY', 'any-key', false, 'unknown provider']
|
||||
];
|
||||
|
||||
testCases.forEach(([providerName, envVarName, keyValue, expectedResult, testName]) => {
|
||||
test(`should return ${expectedResult} for ${testName} (CLI context)`, () => {
|
||||
// CLI context (resolveEnvVariable uses process.env or .env via projectRoot)
|
||||
mockResolveEnvVariable.mockImplementation((key) => {
|
||||
return key === envVarName ? keyValue : undefined;
|
||||
testCases.forEach(
|
||||
([providerName, envVarName, keyValue, expectedResult, testName]) => {
|
||||
test(`should return ${expectedResult} for ${testName} (CLI context)`, () => {
|
||||
// CLI context (resolveEnvVariable uses process.env or .env via projectRoot)
|
||||
mockResolveEnvVariable.mockImplementation((key) => {
|
||||
return key === envVarName ? keyValue : undefined;
|
||||
});
|
||||
expect(
|
||||
configManager.isApiKeySet(providerName, null, MOCK_PROJECT_ROOT)
|
||||
).toBe(expectedResult);
|
||||
if (providerName !== 'ollama' && providerName !== 'unknownprovider') {
|
||||
// Ollama and unknown don't try to resolve
|
||||
expect(mockResolveEnvVariable).toHaveBeenCalledWith(
|
||||
envVarName,
|
||||
null,
|
||||
MOCK_PROJECT_ROOT
|
||||
);
|
||||
}
|
||||
});
|
||||
expect(configManager.isApiKeySet(providerName, null, MOCK_PROJECT_ROOT)).toBe(expectedResult);
|
||||
if (providerName !== 'ollama' && providerName !== 'unknownprovider') { // Ollama and unknown don't try to resolve
|
||||
expect(mockResolveEnvVariable).toHaveBeenCalledWith(envVarName, null, MOCK_PROJECT_ROOT);
|
||||
}
|
||||
});
|
||||
|
||||
test(`should return ${expectedResult} for ${testName} (MCP context)`, () => {
|
||||
// MCP context (resolveEnvVariable uses session.env)
|
||||
const mcpSession = { env: { [envVarName]: keyValue } };
|
||||
mockResolveEnvVariable.mockImplementation((key, sessionArg) => {
|
||||
return sessionArg && sessionArg.env ? sessionArg.env[key] : undefined;
|
||||
test(`should return ${expectedResult} for ${testName} (MCP context)`, () => {
|
||||
// MCP context (resolveEnvVariable uses session.env)
|
||||
const mcpSession = { env: { [envVarName]: keyValue } };
|
||||
mockResolveEnvVariable.mockImplementation((key, sessionArg) => {
|
||||
return sessionArg && sessionArg.env
|
||||
? sessionArg.env[key]
|
||||
: undefined;
|
||||
});
|
||||
expect(
|
||||
configManager.isApiKeySet(providerName, mcpSession, null)
|
||||
).toBe(expectedResult);
|
||||
if (providerName !== 'ollama' && providerName !== 'unknownprovider') {
|
||||
expect(mockResolveEnvVariable).toHaveBeenCalledWith(
|
||||
envVarName,
|
||||
mcpSession,
|
||||
null
|
||||
);
|
||||
}
|
||||
});
|
||||
expect(configManager.isApiKeySet(providerName, mcpSession, null)).toBe(expectedResult);
|
||||
if (providerName !== 'ollama' && providerName !== 'unknownprovider') {
|
||||
expect(mockResolveEnvVariable).toHaveBeenCalledWith(envVarName, mcpSession, null);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
test('isApiKeySet should log a warning for an unknown provider', () => {
|
||||
mockLog.mockClear(); // Clear previous log calls
|
||||
configManager.isApiKeySet('nonexistentprovider');
|
||||
expect(mockLog).toHaveBeenCalledWith('warn', expect.stringContaining('Unknown provider name: nonexistentprovider'));
|
||||
expect(mockLog).toHaveBeenCalledWith(
|
||||
'warn',
|
||||
expect.stringContaining('Unknown provider name: nonexistentprovider')
|
||||
);
|
||||
});
|
||||
|
||||
test('isApiKeySet should handle provider names case-insensitively for keyMap lookup', () => {
|
||||
mockResolveEnvVariable.mockReturnValue('a-valid-key');
|
||||
expect(configManager.isApiKeySet('Anthropic', null, MOCK_PROJECT_ROOT)).toBe(true);
|
||||
expect(mockResolveEnvVariable).toHaveBeenCalledWith('ANTHROPIC_API_KEY', null, MOCK_PROJECT_ROOT);
|
||||
expect(
|
||||
configManager.isApiKeySet('Anthropic', null, MOCK_PROJECT_ROOT)
|
||||
).toBe(true);
|
||||
expect(mockResolveEnvVariable).toHaveBeenCalledWith(
|
||||
'ANTHROPIC_API_KEY',
|
||||
null,
|
||||
MOCK_PROJECT_ROOT
|
||||
);
|
||||
|
||||
mockResolveEnvVariable.mockReturnValue('another-valid-key');
|
||||
expect(configManager.isApiKeySet('OPENAI', null, MOCK_PROJECT_ROOT)).toBe(true);
|
||||
expect(mockResolveEnvVariable).toHaveBeenCalledWith('OPENAI_API_KEY', null, MOCK_PROJECT_ROOT);
|
||||
expect(configManager.isApiKeySet('OPENAI', null, MOCK_PROJECT_ROOT)).toBe(
|
||||
true
|
||||
);
|
||||
expect(mockResolveEnvVariable).toHaveBeenCalledWith(
|
||||
'OPENAI_API_KEY',
|
||||
null,
|
||||
MOCK_PROJECT_ROOT
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user