Files
claude-task-master/mcp-server/src/core/direct-functions/parse-prd.js
Ralph Khreish d3d9dc6ebe fix: replace tool parameter inputs with root directory paths (#147)
* wip: replace tool parameter inputs with root directory paths

* fix: moved path resolving responsibility to tools

- made path in parameters to optional for AI
- internalised path resolving using session roots

* chore: update package-lock.json

* chore: fix regressions and fix CI

* fix: make projectRoot required

* fix: add-task tool

* fix: updateTask tool

* fix: remove reportProgress

* chore: cleanup

* fix: expand-task tool

* chore: remove usless logs

* fix: dependency manager logging in mcp server
2025-04-11 18:57:43 +02:00

211 lines
6.1 KiB
JavaScript

/**
* parse-prd.js
* Direct function implementation for parsing PRD documents
*/
import path from 'path';
import fs from 'fs';
import os from 'os'; // Import os module for home directory check
import { parsePRD } from '../../../../scripts/modules/task-manager.js';
import { findTasksJsonPath } from '../utils/path-utils.js';
import {
enableSilentMode,
disableSilentMode
} from '../../../../scripts/modules/utils.js';
import {
getAnthropicClientForMCP,
getModelConfig
} from '../utils/ai-client-utils.js';
/**
* Direct function wrapper for parsing PRD documents and generating tasks.
*
* @param {Object} args - Command arguments containing input, numTasks or tasks, and output options.
* @param {Object} log - Logger object.
* @param {Object} context - Context object containing session data.
* @returns {Promise<Object>} - Result object with success status and data/error information.
*/
export async function parsePRDDirect(args, log, context = {}) {
const { session } = context; // Only extract session, not reportProgress
try {
log.info(`Parsing PRD document with args: ${JSON.stringify(args)}`);
// Initialize AI client for PRD parsing
let aiClient;
try {
aiClient = getAnthropicClientForMCP(session, log);
} catch (error) {
log.error(`Failed to initialize AI client: ${error.message}`);
return {
success: false,
error: {
code: 'AI_CLIENT_ERROR',
message: `Cannot initialize AI client: ${error.message}`
},
fromCache: false
};
}
// Validate required parameters
if (!args.projectRoot) {
const errorMessage = 'Project root is required for parsePRDDirect';
log.error(errorMessage);
return {
success: false,
error: { code: 'MISSING_PROJECT_ROOT', message: errorMessage },
fromCache: false
};
}
if (!args.input) {
const errorMessage = 'Input file path is required for parsePRDDirect';
log.error(errorMessage);
return {
success: false,
error: { code: 'MISSING_INPUT_PATH', message: errorMessage },
fromCache: false
};
}
if (!args.output) {
const errorMessage = 'Output file path is required for parsePRDDirect';
log.error(errorMessage);
return {
success: false,
error: { code: 'MISSING_OUTPUT_PATH', message: errorMessage },
fromCache: false
};
}
// Resolve input path (expecting absolute path or path relative to project root)
const projectRoot = args.projectRoot;
const inputPath = path.isAbsolute(args.input)
? args.input
: path.resolve(projectRoot, args.input);
// Verify input file exists
if (!fs.existsSync(inputPath)) {
const errorMessage = `Input file not found: ${inputPath}`;
log.error(errorMessage);
return {
success: false,
error: {
code: 'INPUT_FILE_NOT_FOUND',
message: errorMessage,
details: `Checked path: ${inputPath}\nProject root: ${projectRoot}\nInput argument: ${args.input}`
},
fromCache: false
};
}
// Resolve output path (expecting absolute path or path relative to project root)
const outputPath = path.isAbsolute(args.output)
? args.output
: path.resolve(projectRoot, args.output);
// Ensure output directory exists
const outputDir = path.dirname(outputPath);
if (!fs.existsSync(outputDir)) {
log.info(`Creating output directory: ${outputDir}`);
fs.mkdirSync(outputDir, { recursive: true });
}
// Parse number of tasks - handle both string and number values
let numTasks = 10; // Default
if (args.numTasks) {
numTasks =
typeof args.numTasks === 'string'
? parseInt(args.numTasks, 10)
: args.numTasks;
if (isNaN(numTasks)) {
numTasks = 10; // Fallback to default if parsing fails
log.warn(`Invalid numTasks value: ${args.numTasks}. Using default: 10`);
}
}
log.info(
`Preparing to parse PRD from ${inputPath} and output to ${outputPath} with ${numTasks} tasks`
);
// Create the logger wrapper for proper logging in the core function
const logWrapper = {
info: (message, ...args) => log.info(message, ...args),
warn: (message, ...args) => log.warn(message, ...args),
error: (message, ...args) => log.error(message, ...args),
debug: (message, ...args) => log.debug && log.debug(message, ...args),
success: (message, ...args) => log.info(message, ...args) // Map success to info
};
// Get model config from session
const modelConfig = getModelConfig(session);
// Enable silent mode to prevent console logs from interfering with JSON response
enableSilentMode();
try {
// Make sure the output directory exists
const outputDir = path.dirname(outputPath);
if (!fs.existsSync(outputDir)) {
log.info(`Creating output directory: ${outputDir}`);
fs.mkdirSync(outputDir, { recursive: true });
}
// Execute core parsePRD function with AI client
await parsePRD(
inputPath,
outputPath,
numTasks,
{
mcpLog: logWrapper,
session
},
aiClient,
modelConfig
);
// Since parsePRD doesn't return a value but writes to a file, we'll read the result
// to return it to the caller
if (fs.existsSync(outputPath)) {
const tasksData = JSON.parse(fs.readFileSync(outputPath, 'utf8'));
log.info(
`Successfully parsed PRD and generated ${tasksData.tasks?.length || 0} tasks`
);
return {
success: true,
data: {
message: `Successfully generated ${tasksData.tasks?.length || 0} tasks from PRD`,
taskCount: tasksData.tasks?.length || 0,
outputPath
},
fromCache: false // This operation always modifies state and should never be cached
};
} else {
const errorMessage = `Tasks file was not created at ${outputPath}`;
log.error(errorMessage);
return {
success: false,
error: { code: 'OUTPUT_FILE_NOT_CREATED', message: errorMessage },
fromCache: false
};
}
} finally {
// Always restore normal logging
disableSilentMode();
}
} catch (error) {
// Make sure to restore normal logging even if there's an error
disableSilentMode();
log.error(`Error parsing PRD: ${error.message}`);
return {
success: false,
error: {
code: 'PARSE_PRD_ERROR',
message: error.message || 'Unknown error parsing PRD'
},
fromCache: false
};
}
}