chore: apply requested changes

This commit is contained in:
Ralph Khreish
2026-01-07 14:26:20 +01:00
parent 3d2eca117a
commit ed87b4c1d8
6 changed files with 69 additions and 60 deletions

View File

@@ -26,8 +26,7 @@ export function registerAutopilotResumeTool(server: FastMCP) {
'Resume a previously started TDD workflow from saved state. Restores the workflow state machine and continues from where it left off.',
parameters: ResumeWorkflowSchema,
annotations: {
title: 'Resume Autopilot Workflow',
readOnlyHint: false
title: 'Resume Autopilot Workflow'
},
execute: withToolContext(
'autopilot-resume',

View File

@@ -22,6 +22,7 @@ import { getPromptManager } from '../prompt-manager.js';
import { findProjectRoot, flattenTasksWithSubtasks } from '../utils.js';
import { ContextGatherer } from '../utils/contextGatherer.js';
import { FuzzyTaskSearch } from '../utils/fuzzyTaskSearch.js';
import { normalizeSubtask } from './parse-prd/parse-prd-helpers.js';
/**
* Expand a task into subtasks using the unified AI service (generateObjectService).
@@ -331,12 +332,9 @@ async function expandTask(
if (!mainResult || !Array.isArray(mainResult.subtasks)) {
throw new Error('AI response did not include a valid subtasks array.');
}
generatedSubtasks = mainResult.subtasks.map((subtask) => ({
...subtask,
dependencies: subtask.dependencies ?? [],
status: subtask.status ?? 'pending',
testStrategy: subtask.testStrategy ?? null
}));
generatedSubtasks = mainResult.subtasks.map((subtask) =>
normalizeSubtask(subtask)
);
logger.info(`Received ${generatedSubtasks.length} subtasks from AI.`);
} catch (error) {
if (loadingIndicator) stopLoadingIndicator(loadingIndicator);

View File

@@ -23,6 +23,57 @@ export function estimateTokens(text) {
return Math.ceil(text.length / 4);
}
/**
* Normalize a subtask by applying schema defaults for missing fields
* Subtasks have: id, title, description, status, dependencies, details
* (no testStrategy, no priority - those are task-only fields)
* @param {Object} subtask - Raw subtask object from AI response
* @returns {Object} Subtask with all required fields populated
*/
export function normalizeSubtask(subtask) {
return {
...subtask,
title: subtask.title ?? '',
description: subtask.description ?? '',
details: subtask.details ?? '',
dependencies: subtask.dependencies ?? [],
status: subtask.status ?? 'pending'
};
}
/**
* Normalize a task by applying schema defaults for missing fields
* @param {Object} task - Raw task object from AI response
* @param {string} defaultPriority - Default priority to use
* @returns {Object} Task with all required fields populated
*/
export function normalizeTask(task, defaultPriority = 'medium') {
return {
...task,
status: task.status ?? 'pending',
title: task.title ?? '',
description: task.description ?? '',
dependencies: task.dependencies ?? [],
priority: task.priority ?? defaultPriority,
details: task.details ?? '',
testStrategy: task.testStrategy ?? '',
subtasks: task.subtasks
? task.subtasks.map((subtask) => normalizeSubtask(subtask))
: undefined
};
}
/**
* Normalize an array of tasks by applying schema defaults
* @param {Array} tasks - Array of raw task objects
* @param {string} defaultPriority - Default priority to use
* @returns {Array} Tasks with all required fields populated
*/
export function normalizeTasks(tasks, defaultPriority = 'medium') {
if (!Array.isArray(tasks)) return [];
return tasks.map((task) => normalizeTask(task, defaultPriority));
}
/**
* Read and validate PRD content
* @param {string} prdPath - Path to PRD file
@@ -139,23 +190,17 @@ export function processTasks(
let currentId = startId;
const taskMap = new Map();
// First pass: assign new IDs and create mapping
// First pass: normalize, assign new IDs, and create mapping
const processedTasks = rawTasks.map((task) => {
const newId = currentId++;
taskMap.set(task.id, newId);
// Apply schema defaults via normalizeTask, then override with new ID
const normalized = normalizeTask(task, defaultPriority);
return {
...task,
...normalized,
id: newId,
status: task.status || 'pending',
priority: task.priority || defaultPriority,
dependencies: Array.isArray(task.dependencies) ? task.dependencies : [],
subtasks: task.subtasks || [],
// Ensure all required fields have values
title: task.title || '',
description: task.description || '',
details: task.details || '',
testStrategy: task.testStrategy || ''
subtasks: normalized.subtasks || []
};
});

View File

@@ -71,6 +71,7 @@ export async function handleNonStreamingService(config, prompts) {
spinner.succeed('Tasks generated successfully!');
}
// Note: Schema defaults are applied later by processTasks in parsePRDCore
return {
parsedTasks: generatedData.tasks,
aiServiceResponse,

View File

@@ -460,6 +460,7 @@ async function finalizeStreamingResults(state, context) {
);
}
// Note: Schema defaults are applied later by processTasks in parsePRDCore
return {
parsedTasks: lastPartialObject.tasks,
estimatedOutputTokens: finalOutputTokens,
@@ -570,19 +571,7 @@ async function processWithGenerateObject(context, logger) {
// Extract tasks from the result (handle both direct tasks and mainResult.tasks)
const tasks = result?.mainResult || result;
// Apply defaults to ensure all required fields are present
if (tasks && Array.isArray(tasks.tasks)) {
tasks.tasks = tasks.tasks.map((task) => ({
...task,
status: task.status ?? 'pending',
title: task.title ?? '',
description: task.description ?? '',
dependencies: task.dependencies ?? [],
priority: task.priority ?? context.defaultPriority ?? 'medium',
details: task.details ?? '',
testStrategy: task.testStrategy ?? ''
}));
}
// Note: Schema defaults are applied later by processTasks in parsePRDCore
// Process the generated tasks
if (tasks && Array.isArray(tasks.tasks)) {

View File

@@ -1,15 +1,8 @@
import path from 'path';
import boxen from 'boxen';
import chalk from 'chalk';
import Table from 'cli-table3';
import {
log as consoleLog,
isSilentMode,
readJSON,
truncate,
writeJSON
} from '../utils.js';
import { log as consoleLog, readJSON, truncate, writeJSON } from '../utils.js';
import {
displayAiUsageSummary,
@@ -29,7 +22,7 @@ import { getPromptManager } from '../prompt-manager.js';
import { findProjectRoot, flattenTasksWithSubtasks } from '../utils.js';
import { ContextGatherer } from '../utils/contextGatherer.js';
import { FuzzyTaskSearch } from '../utils/fuzzyTaskSearch.js';
import { getModelConfiguration } from './models.js';
import { normalizeTasks } from './parse-prd/parse-prd-helpers.js';
/**
* Update tasks based on new context using the unified AI service.
@@ -233,25 +226,9 @@ async function updateTasks(
// With generateObject, we get structured data directly
const defaultPriority = getDefaultPriority(projectRoot) ?? 'medium';
const parsedUpdatedTasks = aiServiceResponse.mainResult.tasks.map(
(task) => ({
...task,
status: task.status ?? 'pending',
title: task.title ?? '',
description: task.description ?? '',
dependencies: task.dependencies ?? [],
priority: task.priority ?? defaultPriority,
details: task.details ?? '',
testStrategy: task.testStrategy ?? '',
subtasks: task.subtasks
? task.subtasks.map((subtask) => ({
...subtask,
dependencies: subtask.dependencies ?? [],
status: subtask.status ?? 'pending',
testStrategy: subtask.testStrategy ?? ''
}))
: null
})
const parsedUpdatedTasks = normalizeTasks(
aiServiceResponse.mainResult.tasks,
defaultPriority
);
// --- Update Tasks Data (Updated writeJSON call) ---