feat: CLI & MCP progress tracking for parse-prd command (#1048)
* initial cutover * update log to debug * update tracker to pass units * update test to match new base tracker format * add streamTextService mocks * remove unused imports * Ensure the CLI waits for async main() completion * refactor to reduce code duplication * update comment * reuse function * ensure targetTag is defined in streaming mode * avoid throwing inside process.exit spy * check for null * remove reference to generate * fix formatting * fix textStream assignment * ensure no division by 0 * fix jest chalk mocks * refactor for maintainability * Improve bar chart calculation logic for consistent visual representation * use custom streaming error types; fix mocks * Update streamText extraction in parse-prd.js to match actual service response * remove check - doesn't belong here * update mocks * remove streaming test that wasn't really doing anything * add comment * make parsing logic more DRY * fix formatting * Fix textStream extraction to match actual service response * fix mock * Add a cleanup method to ensure proper resource disposal and prevent memory leaks * debounce progress updates to reduce UI flicker during rapid updates * Implement timeout protection for streaming operations (60-second timeout) with automatic fallback to non-streaming mode. * clear timeout properly * Add a maximum buffer size limit (1MB) to prevent unbounded memory growth with very large streaming responses. * fix formatting * remove duplicate mock * better docs * fix formatting * sanitize the dynamic property name * Fix incorrect remaining progress calculation * Use onError callback instead of console.warn * Remove unused chalk import * Add missing custom validator in fallback parsing configuration * add custom validator parameter in fallback parsing * chore: fix package-lock.json * chore: large code refactor * chore: increase timeout from 1 minute to 3 minutes * fix: refactor and fix streaming * Merge remote-tracking branch 'origin/next' into joedanz/parse-prd-progress * fix: cleanup and fix unit tests * chore: fix unit tests * chore: fix format * chore: run format * chore: fix weird CI unit test error * chore: fix format --------- Co-authored-by: Ralph Khreish <35776126+Crunchyman-ralph@users.noreply.github.com>
This commit is contained in:
@@ -32,7 +32,7 @@ import { TASKMASTER_TASKS_FILE } from '../../../../src/constants/paths.js';
|
||||
* @returns {Promise<Object>} - Result object with success status and data/error information.
|
||||
*/
|
||||
export async function parsePRDDirect(args, log, context = {}) {
|
||||
const { session } = context;
|
||||
const { session, reportProgress } = context;
|
||||
// Extract projectRoot from args
|
||||
const {
|
||||
input: inputArg,
|
||||
@@ -164,6 +164,7 @@ export async function parsePRDDirect(args, log, context = {}) {
|
||||
force,
|
||||
append,
|
||||
research,
|
||||
reportProgress,
|
||||
commandName: 'parse-prd',
|
||||
outputType: 'mcp'
|
||||
},
|
||||
|
||||
@@ -7,7 +7,8 @@ import { z } from 'zod';
|
||||
import {
|
||||
handleApiResult,
|
||||
withNormalizedProjectRoot,
|
||||
createErrorResponse
|
||||
createErrorResponse,
|
||||
checkProgressCapability
|
||||
} from './utils.js';
|
||||
import { parsePRDDirect } from '../core/task-master-core.js';
|
||||
import {
|
||||
@@ -64,31 +65,37 @@ export function registerParsePRDTool(server) {
|
||||
.optional()
|
||||
.describe('Append generated tasks to existing file.')
|
||||
}),
|
||||
execute: withNormalizedProjectRoot(async (args, { log, session }) => {
|
||||
try {
|
||||
const resolvedTag = resolveTag({
|
||||
projectRoot: args.projectRoot,
|
||||
tag: args.tag
|
||||
});
|
||||
const result = await parsePRDDirect(
|
||||
{
|
||||
...args,
|
||||
tag: resolvedTag
|
||||
},
|
||||
log,
|
||||
{ session }
|
||||
);
|
||||
return handleApiResult(
|
||||
result,
|
||||
log,
|
||||
'Error parsing PRD',
|
||||
undefined,
|
||||
args.projectRoot
|
||||
);
|
||||
} catch (error) {
|
||||
log.error(`Error in parse_prd: ${error.message}`);
|
||||
return createErrorResponse(`Failed to parse PRD: ${error.message}`);
|
||||
execute: withNormalizedProjectRoot(
|
||||
async (args, { log, session, reportProgress }) => {
|
||||
try {
|
||||
const resolvedTag = resolveTag({
|
||||
projectRoot: args.projectRoot,
|
||||
tag: args.tag
|
||||
});
|
||||
const progressCapability = checkProgressCapability(
|
||||
reportProgress,
|
||||
log
|
||||
);
|
||||
const result = await parsePRDDirect(
|
||||
{
|
||||
...args,
|
||||
tag: resolvedTag
|
||||
},
|
||||
log,
|
||||
{ session, reportProgress: progressCapability }
|
||||
);
|
||||
return handleApiResult(
|
||||
result,
|
||||
log,
|
||||
'Error parsing PRD',
|
||||
undefined,
|
||||
args.projectRoot
|
||||
);
|
||||
} catch (error) {
|
||||
log.error(`Error in parse_prd: ${error.message}`);
|
||||
return createErrorResponse(`Failed to parse PRD: ${error.message}`);
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -778,6 +778,77 @@ function withNormalizedProjectRoot(executeFn) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks progress reporting capability and returns the validated function or undefined.
|
||||
*
|
||||
* STANDARD PATTERN for AI-powered, long-running operations (parse-prd, expand-task, expand-all, analyze):
|
||||
*
|
||||
* This helper should be used as the first step in any MCP tool that performs long-running
|
||||
* AI operations. It validates the availability of progress reporting and provides consistent
|
||||
* logging about the capability status.
|
||||
*
|
||||
* Operations that should use this pattern:
|
||||
* - parse-prd: Parsing PRD documents with AI
|
||||
* - expand-task: Expanding tasks into subtasks
|
||||
* - expand-all: Expanding all tasks in batch
|
||||
* - analyze-complexity: Analyzing task complexity
|
||||
* - update-task: Updating tasks with AI assistance
|
||||
* - add-task: Creating new tasks with AI
|
||||
* - Any operation that makes AI service calls
|
||||
*
|
||||
* @example Basic usage in a tool's execute function:
|
||||
* ```javascript
|
||||
* import { checkProgressCapability } from './utils.js';
|
||||
*
|
||||
* async execute(args, context) {
|
||||
* const { log, reportProgress, session } = context;
|
||||
*
|
||||
* // Always validate progress capability first
|
||||
* const progressCapability = checkProgressCapability(reportProgress, log);
|
||||
*
|
||||
* // Pass to direct function - it handles undefined gracefully
|
||||
* const result = await expandTask(taskId, numSubtasks, {
|
||||
* session,
|
||||
* reportProgress: progressCapability,
|
||||
* mcpLog: log
|
||||
* });
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @example With progress reporting available:
|
||||
* ```javascript
|
||||
* // When reportProgress is available, users see real-time updates:
|
||||
* // "Starting PRD analysis (Input: 5432 tokens)..."
|
||||
* // "Task 1/10 - Implement user authentication"
|
||||
* // "Task 2/10 - Create database schema"
|
||||
* // "Task Generation Completed | Tokens: 5432/1234"
|
||||
* ```
|
||||
*
|
||||
* @example Without progress reporting (graceful degradation):
|
||||
* ```javascript
|
||||
* // When reportProgress is not available:
|
||||
* // - Operation runs normally without progress updates
|
||||
* // - Debug log: "reportProgress not available - operation will run without progress updates"
|
||||
* // - User gets final result after completion
|
||||
* ```
|
||||
*
|
||||
* @param {Function|undefined} reportProgress - The reportProgress function from MCP context.
|
||||
* Expected signature: async (progress: {progress: number, total: number, message: string}) => void
|
||||
* @param {Object} log - Logger instance with debug, info, warn, error methods
|
||||
* @returns {Function|undefined} The validated reportProgress function or undefined if not available
|
||||
*/
|
||||
function checkProgressCapability(reportProgress, log) {
|
||||
// Validate that reportProgress is available for long-running operations
|
||||
if (typeof reportProgress !== 'function') {
|
||||
log.debug(
|
||||
'reportProgress not available - operation will run without progress updates'
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return reportProgress;
|
||||
}
|
||||
|
||||
// Ensure all functions are exported
|
||||
export {
|
||||
getProjectRoot,
|
||||
@@ -792,5 +863,6 @@ export {
|
||||
createLogWrapper,
|
||||
normalizeProjectRoot,
|
||||
getRawProjectRootFromSession,
|
||||
withNormalizedProjectRoot
|
||||
withNormalizedProjectRoot,
|
||||
checkProgressCapability
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user