86 lines
2.4 KiB
JavaScript
Raw Normal View History

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>
2025-08-12 16:37:07 -04:00
/**
* Non-streaming handler for PRD parsing
*/
import ora from 'ora';
import { generateObjectService } from '../../ai-services-unified.js';
import { LoggingConfig, prdResponseSchema } from './parse-prd-config.js';
import { estimateTokens } from './parse-prd-helpers.js';
/**
* Handle non-streaming AI service call
* @param {Object} config - Configuration object
* @param {Object} prompts - System and user prompts
* @returns {Promise<Object>} Generated tasks and telemetry
*/
export async function handleNonStreamingService(config, prompts) {
const logger = new LoggingConfig(config.mcpLog, config.reportProgress);
const { systemPrompt, userPrompt } = prompts;
const estimatedInputTokens = estimateTokens(systemPrompt + userPrompt);
// Initialize spinner for CLI
let spinner = null;
if (config.outputFormat === 'text' && !config.isMCP) {
spinner = ora('Parsing PRD and generating tasks...\n').start();
}
try {
// Call AI service
logger.report(
`Calling AI service to generate tasks from PRD${config.research ? ' with research-backed analysis' : ''}...`,
'info'
);
const aiServiceResponse = await generateObjectService({
role: config.research ? 'research' : 'main',
session: config.session,
projectRoot: config.projectRoot,
schema: prdResponseSchema,
objectName: 'tasks_data',
systemPrompt,
prompt: userPrompt,
commandName: 'parse-prd',
outputType: config.isMCP ? 'mcp' : 'cli'
});
// Extract generated data
let generatedData = null;
if (aiServiceResponse?.mainResult) {
if (
typeof aiServiceResponse.mainResult === 'object' &&
aiServiceResponse.mainResult !== null &&
'tasks' in aiServiceResponse.mainResult
) {
generatedData = aiServiceResponse.mainResult;
} else if (
typeof aiServiceResponse.mainResult.object === 'object' &&
aiServiceResponse.mainResult.object !== null &&
'tasks' in aiServiceResponse.mainResult.object
) {
generatedData = aiServiceResponse.mainResult.object;
}
}
if (!generatedData || !Array.isArray(generatedData.tasks)) {
throw new Error(
'AI service returned unexpected data structure after validation.'
);
}
if (spinner) {
spinner.succeed('Tasks generated successfully!');
}
return {
parsedTasks: generatedData.tasks,
aiServiceResponse,
estimatedInputTokens
};
} catch (error) {
if (spinner) {
spinner.fail(`Error parsing PRD: ${error.message}`);
}
throw error;
}
}