Files
claude-task-master/mcp-server/src/core/direct-functions/initialize-project.js
Eyal Toledano d63964a10e refactor: Improve update-subtask, consolidate utils, update config
This commit introduces several improvements and refactorings across MCP tools, core logic, and configuration.

**Major Changes:**

1.  **Refactor updateSubtaskById:**
    - Switched from generateTextService to generateObjectService for structured AI responses, using a Zod schema (subtaskSchema) for validation.
    - Revised prompts to have the AI generate relevant content based on user request and context (parent/sibling tasks), while explicitly preventing AI from handling timestamp/tag formatting.
    - Implemented **local timestamp generation (new Date().toISOString()) and formatting** (using <info added on ...> tags) within the function *after* receiving the AI response. This ensures reliable and correctly formatted details are appended.
    - Corrected logic to append only the locally formatted, AI-generated content block to the existing subtask.details.

2.  **Consolidate MCP Utilities:**
    - Moved/consolidated the withNormalizedProjectRoot HOF into mcp-server/src/tools/utils.js.
    - Updated MCP tools (like update-subtask.js) to import withNormalizedProjectRoot from the new location.

3.  **Refactor Project Initialization:**
    - Deleted the redundant mcp-server/src/core/direct-functions/initialize-project-direct.js file.
    - Updated mcp-server/src/core/task-master-core.js to import initializeProjectDirect from its correct location (./direct-functions/initialize-project.js).

**Other Changes:**

-   Updated .taskmasterconfig fallback model to claude-3-7-sonnet-20250219.
-   Clarified model cost representation in the models tool description (taskmaster.mdc and mcp-server/src/tools/models.js).
2025-05-02 17:48:59 -04:00

105 lines
4.5 KiB
JavaScript

import { initializeProject } from '../../../../scripts/init.js'; // Import core function and its logger if needed separately
import {
enableSilentMode,
disableSilentMode
// isSilentMode // Not used directly here
} from '../../../../scripts/modules/utils.js';
import os from 'os'; // Import os module for home directory check
/**
* Direct function wrapper for initializing a project.
* Derives target directory from session, sets CWD, and calls core init logic.
* @param {object} args - Arguments containing initialization options (addAliases, skipInstall, yes, projectRoot)
* @param {object} log - The FastMCP logger instance.
* @param {object} context - The context object, must contain { session }.
* @returns {Promise<{success: boolean, data?: any, error?: {code: string, message: string}}>} - Standard result object.
*/
export async function initializeProjectDirect(args, log, context = {}) {
const { session } = context; // Keep session if core logic needs it
const homeDir = os.homedir();
log.info(`Args received in direct function: ${JSON.stringify(args)}`);
// --- Determine Target Directory ---
// TRUST the projectRoot passed from the tool layer via args
// The HOF in the tool layer already normalized and validated it came from a reliable source (args or session)
const targetDirectory = args.projectRoot;
// --- Validate the targetDirectory (basic sanity checks) ---
if (
!targetDirectory ||
typeof targetDirectory !== 'string' || // Ensure it's a string
targetDirectory === '/' ||
targetDirectory === homeDir
) {
log.error(
`Invalid target directory received from tool layer: '${targetDirectory}'`
);
return {
success: false,
error: {
code: 'INVALID_TARGET_DIRECTORY',
message: `Cannot initialize project: Invalid target directory '${targetDirectory}' received. Please ensure a valid workspace/folder is open or specified.`,
details: `Received args.projectRoot: ${args.projectRoot}` // Show what was received
},
fromCache: false
};
}
// --- Proceed with validated targetDirectory ---
log.info(`Validated target directory for initialization: ${targetDirectory}`);
const originalCwd = process.cwd();
let resultData;
let success = false;
let errorResult = null;
log.info(
`Temporarily changing CWD to ${targetDirectory} for initialization.`
);
process.chdir(targetDirectory); // Change CWD to the HOF-provided root
enableSilentMode();
try {
// Construct options ONLY from the relevant flags in args
// The core initializeProject operates in the current CWD, which we just set
const options = {
aliases: args.addAliases,
skipInstall: args.skipInstall,
yes: true // Force yes mode
};
log.info(`Initializing project with options: ${JSON.stringify(options)}`);
const result = await initializeProject(options); // Call core logic
resultData = {
message: 'Project initialized successfully.',
next_step:
'Now that the project is initialized, the next step is to create the tasks by parsing a PRD. This will create the tasks folder and the initial task files (tasks folder will be created when parse-prd is run). The parse-prd tool will require a prd.txt file as input (typically found in the project root directory, scripts/ directory). You can create a prd.txt file by asking the user about their idea, and then using the scripts/example_prd.txt file as a template to genrate a prd.txt file in scripts/. You may skip all of this if the user already has a prd.txt file. You can THEN use the parse-prd tool to create the tasks. So: step 1 after initialization is to create a prd.txt file in scripts/prd.txt or confirm the user already has one. Step 2 is to use the parse-prd tool to create the tasks. Do not bother looking for tasks after initialization, just use the parse-prd tool to create the tasks after creating a prd.txt from which to parse the tasks. You do NOT need to reinitialize the project to parse-prd.',
...result
};
success = true;
log.info(
`Project initialization completed successfully in ${targetDirectory}.`
);
} catch (error) {
log.error(`Core initializeProject failed: ${error.message}`);
errorResult = {
code: 'INITIALIZATION_FAILED',
message: `Core project initialization failed: ${error.message}`,
details: error.stack
};
success = false;
} finally {
disableSilentMode();
log.info(`Restoring original CWD: ${originalCwd}`);
process.chdir(originalCwd);
}
if (success) {
return { success: true, data: resultData, fromCache: false };
} else {
return { success: false, error: errorResult, fromCache: false };
}
}