diff --git a/.changeset/two-bats-smoke.md b/.changeset/two-bats-smoke.md index 89160b31..457493e0 100644 --- a/.changeset/two-bats-smoke.md +++ b/.changeset/two-bats-smoke.md @@ -2,6 +2,28 @@ "task-master-ai": patch --- +- Implement robust project root detection with a hierarchical precedence system: + - Environment variable override (TASK_MASTER_PROJECT_ROOT) + - Explicitly provided project root (--project-root parameter) + - Cached project root from previous successful operations + - Current directory with project markers + - Parent directory traversal to find tasks.json + - Package directory as fallback + +- Add comprehensive PROJECT_MARKERS array for detecting common project files: + - Task Master specific files (tasks.json, tasks/tasks.json) + - Version control markers (.git, .svn) + - Package files (package.json, pyproject.toml, etc.) + - IDE/editor folders (.cursor, .vscode, .idea) + - Dependency directories (node_modules, venv) + - Configuration files (.env, tsconfig.json, etc.) + - CI/CD files (.github/workflows, etc.) + +- Improved error messages with specific troubleshooting guidance +- Enhanced logging to indicate the source of project root selection +- DRY refactoring by centralizing path utilities in core/utils/path-utils.js +- Add caching of lastFoundProjectRoot for improved performance + - Split monolithic task-master-core.js into separate function files within direct-functions directory - Implement update-task MCP command for updating a single task by ID - Implement update-subtask MCP command for appending information to specific subtasks diff --git a/mcp-server/src/core/direct-functions/set-task-status.js b/mcp-server/src/core/direct-functions/set-task-status.js index ed63d2c7..f7a14fe3 100644 --- a/mcp-server/src/core/direct-functions/set-task-status.js +++ b/mcp-server/src/core/direct-functions/set-task-status.js @@ -41,12 +41,17 @@ export async function setTaskStatusDirect(args, log) { // Get tasks file path let tasksPath; try { + // The enhanced findTasksJsonPath will now search in parent directories if needed tasksPath = findTasksJsonPath(args, log); + log.info(`Found tasks file at: ${tasksPath}`); } catch (error) { log.error(`Error finding tasks file: ${error.message}`); return { success: false, - error: { code: 'TASKS_FILE_ERROR', message: error.message }, + error: { + code: 'TASKS_FILE_ERROR', + message: `${error.message}\n\nPlease ensure you are in a Task Master project directory or use the --project-root parameter to specify the path to your project.` + }, fromCache: false }; } diff --git a/mcp-server/src/core/utils/path-utils.js b/mcp-server/src/core/utils/path-utils.js index 9d622c88..70e344c3 100644 --- a/mcp-server/src/core/utils/path-utils.js +++ b/mcp-server/src/core/utils/path-utils.js @@ -1,10 +1,77 @@ /** * path-utils.js * Utility functions for file path operations in Task Master + * + * This module provides robust path resolution for both: + * 1. PACKAGE PATH: Where task-master code is installed + * (global node_modules OR local ./node_modules/task-master OR direct from repo) + * 2. PROJECT PATH: Where user's tasks.json resides (typically user's project root) */ import path from 'path'; import fs from 'fs'; +import { fileURLToPath } from 'url'; +import os from 'os'; + +// Store last found project root to improve performance on subsequent calls +export let lastFoundProjectRoot = null; + +// Project marker files that indicate a potential project root +export const PROJECT_MARKERS = [ + // Task Master specific + 'tasks.json', + 'tasks/tasks.json', + + // Common version control + '.git', + '.svn', + + // Common package files + 'package.json', + 'pyproject.toml', + 'Gemfile', + 'go.mod', + 'Cargo.toml', + + // Common IDE/editor folders + '.cursor', + '.vscode', + '.idea', + + // Common dependency directories (check if directory) + 'node_modules', + 'venv', + '.venv', + + // Common config files + '.env', + '.eslintrc', + 'tsconfig.json', + 'babel.config.js', + 'jest.config.js', + 'webpack.config.js', + + // Common CI/CD files + '.github/workflows', + '.gitlab-ci.yml', + '.circleci/config.yml' +]; + +/** + * Gets the path to the task-master package installation directory + * @returns {string} - Absolute path to the package installation directory + */ +export function getPackagePath() { + // When running from source, __dirname is the directory containing this file + // When running from npm, we need to find the package root + const thisFilePath = fileURLToPath(import.meta.url); + const thisFileDir = path.dirname(thisFilePath); + + // Navigate from core/utils up to the package root + // In dev: /path/to/task-master/mcp-server/src/core/utils -> /path/to/task-master + // In npm: /path/to/node_modules/task-master/mcp-server/src/core/utils -> /path/to/node_modules/task-master + return path.resolve(thisFileDir, '../../../../'); +} /** * Finds the absolute path to the tasks.json file based on project root and arguments. @@ -14,22 +81,104 @@ import fs from 'fs'; * @throws {Error} - If tasks.json cannot be found. */ export function findTasksJsonPath(args, log) { - // Assume projectRoot is already normalized absolute path if passed in args - // Or use getProjectRoot if we decide to centralize that logic - const projectRoot = args.projectRoot || process.cwd(); - log.info(`Searching for tasks.json within project root: ${projectRoot}`); + // PRECEDENCE ORDER: + // 1. Environment variable override + // 2. Explicitly provided projectRoot in args + // 3. Previously found/cached project root + // 4. Current directory and parent traversal + // 5. Package directory (for development scenarios) + + // 1. Check for environment variable override + if (process.env.TASK_MASTER_PROJECT_ROOT) { + const envProjectRoot = process.env.TASK_MASTER_PROJECT_ROOT; + log.info(`Using project root from TASK_MASTER_PROJECT_ROOT environment variable: ${envProjectRoot}`); + return findTasksJsonInDirectory(envProjectRoot, args.file, log); + } + + // 2. If project root is explicitly provided, use it directly + if (args.projectRoot) { + const projectRoot = args.projectRoot; + log.info(`Using explicitly provided project root: ${projectRoot}`); + return findTasksJsonInDirectory(projectRoot, args.file, log); + } + + // 3. If we have a last known project root that worked, try it first + if (lastFoundProjectRoot) { + log.info(`Trying last known project root: ${lastFoundProjectRoot}`); + try { + const tasksPath = findTasksJsonInDirectory(lastFoundProjectRoot, args.file, log); + return tasksPath; + } catch (error) { + log.info(`Task file not found in last known project root, continuing search.`); + // Continue with search if not found + } + } + + // 4. Start with current directory - this is likely the user's project directory + const startDir = process.cwd(); + log.info(`Searching for tasks.json starting from current directory: ${startDir}`); + + // Try to find tasks.json by walking up the directory tree from cwd + try { + return findTasksJsonWithParentSearch(startDir, args.file, log); + } catch (error) { + // 5. If not found in cwd or parents, package might be installed via npm + // and the user could be in an unrelated directory + + // As a last resort, check if there's a tasks.json in the package directory itself + // (for development scenarios) + const packagePath = getPackagePath(); + if (packagePath !== startDir) { + log.info(`Tasks file not found in current directory tree. Checking package directory: ${packagePath}`); + try { + return findTasksJsonInDirectory(packagePath, args.file, log); + } catch (packageError) { + // Fall through to throw the original error + } + } + + // If all attempts fail, throw the original error with guidance + error.message = `${error.message}\n\nPossible solutions: +1. Run the command from your project directory containing tasks.json +2. Use --project-root=/path/to/project to specify the project location +3. Set TASK_MASTER_PROJECT_ROOT environment variable to your project path`; + throw error; + } +} +/** + * Check if a directory contains any project marker files or directories + * @param {string} dirPath - Directory to check + * @returns {boolean} - True if the directory contains any project markers + */ +function hasProjectMarkers(dirPath) { + return PROJECT_MARKERS.some(marker => { + const markerPath = path.join(dirPath, marker); + // Check if the marker exists as either a file or directory + return fs.existsSync(markerPath); + }); +} + +/** + * Search for tasks.json in a specific directory + * @param {string} dirPath - Directory to search in + * @param {string} explicitFilePath - Optional explicit file path relative to dirPath + * @param {Object} log - Logger object + * @returns {string} - Absolute path to tasks.json + * @throws {Error} - If tasks.json cannot be found + */ +function findTasksJsonInDirectory(dirPath, explicitFilePath, log) { const possiblePaths = []; - // 1. If a file is explicitly provided relative to projectRoot - if (args.file) { - possiblePaths.push(path.resolve(projectRoot, args.file)); + // 1. If a file is explicitly provided relative to dirPath + if (explicitFilePath) { + possiblePaths.push(path.resolve(dirPath, explicitFilePath)); } - // 2. Check the standard locations relative to projectRoot + // 2. Check the standard locations relative to dirPath possiblePaths.push( - path.join(projectRoot, 'tasks.json'), - path.join(projectRoot, 'tasks', 'tasks.json') + path.join(dirPath, 'tasks.json'), + path.join(dirPath, 'tasks', 'tasks.json') ); log.info(`Checking potential task file paths: ${possiblePaths.join(', ')}`); @@ -38,12 +187,86 @@ export function findTasksJsonPath(args, log) { for (const p of possiblePaths) { if (fs.existsSync(p)) { log.info(`Found tasks file at: ${p}`); + // Store the project root for future use + lastFoundProjectRoot = dirPath; return p; } } // If no file was found, throw an error - const error = new Error(`Tasks file not found in any of the expected locations relative to ${projectRoot}: ${possiblePaths.join(', ')}`); + const error = new Error(`Tasks file not found in any of the expected locations relative to ${dirPath}: ${possiblePaths.join(', ')}`); error.code = 'TASKS_FILE_NOT_FOUND'; throw error; +} + +/** + * Recursively search for tasks.json in the given directory and parent directories + * Also looks for project markers to identify potential project roots + * @param {string} startDir - Directory to start searching from + * @param {string} explicitFilePath - Optional explicit file path + * @param {Object} log - Logger object + * @returns {string} - Absolute path to tasks.json + * @throws {Error} - If tasks.json cannot be found in any parent directory + */ +function findTasksJsonWithParentSearch(startDir, explicitFilePath, log) { + let currentDir = startDir; + const rootDir = path.parse(currentDir).root; + + // Keep traversing up until we hit the root directory + while (currentDir !== rootDir) { + // First check for tasks.json directly + try { + return findTasksJsonInDirectory(currentDir, explicitFilePath, log); + } catch (error) { + // If tasks.json not found but the directory has project markers, + // log it as a potential project root (helpful for debugging) + if (hasProjectMarkers(currentDir)) { + log.info(`Found project markers in ${currentDir}, but no tasks.json`); + } + + // Move up to parent directory + const parentDir = path.dirname(currentDir); + + // Check if we've reached the root + if (parentDir === currentDir) { + break; + } + + log.info(`Tasks file not found in ${currentDir}, searching in parent directory: ${parentDir}`); + currentDir = parentDir; + } + } + + // If we've searched all the way to the root and found nothing + const error = new Error(`Tasks file not found in ${startDir} or any parent directory.`); + error.code = 'TASKS_FILE_NOT_FOUND'; + throw error; +} + +function findTasksWithNpmConsideration(startDir, log) { + // First try our recursive parent search from cwd + try { + return findTasksJsonWithParentSearch(startDir, null, log); + } catch (error) { + // If that fails, try looking relative to the executable location + const execPath = process.argv[1]; + const execDir = path.dirname(execPath); + log.info(`Looking for tasks file relative to executable at: ${execDir}`); + + try { + return findTasksJsonWithParentSearch(execDir, null, log); + } catch (secondError) { + // If that also fails, check standard locations in user's home directory + const homeDir = os.homedir(); + log.info(`Looking for tasks file in home directory: ${homeDir}`); + + try { + // Check standard locations in home dir + return findTasksJsonInDirectory(path.join(homeDir, '.task-master'), null, log); + } catch (thirdError) { + // If all approaches fail, throw the original error + throw error; + } + } + } } \ No newline at end of file diff --git a/mcp-server/src/tools/list-tasks.js b/mcp-server/src/tools/list-tasks.js index 667baef7..8f291694 100644 --- a/mcp-server/src/tools/list-tasks.js +++ b/mcp-server/src/tools/list-tasks.js @@ -27,9 +27,9 @@ export function registerListTasksTool(server) { file: z.string().optional().describe("Path to the tasks file"), projectRoot: z .string() - // .optional() + .optional() .describe( - "Root directory of the project (default: current working directory)" + "Root directory of the project (default: automatically detected)" ), }), execute: async (args, { log }) => { diff --git a/mcp-server/src/tools/set-task-status.js b/mcp-server/src/tools/set-task-status.js index 6f1fd272..536cef49 100644 --- a/mcp-server/src/tools/set-task-status.js +++ b/mcp-server/src/tools/set-task-status.js @@ -30,7 +30,7 @@ export function registerSetTaskStatusTool(server) { .string() .optional() .describe( - "Root directory of the project (default: current working directory)" + "Root directory of the project (default: automatically detected)" ), }), execute: async (args, { log }) => { diff --git a/mcp-server/src/tools/utils.js b/mcp-server/src/tools/utils.js index 42c8a616..c9c79cc0 100644 --- a/mcp-server/src/tools/utils.js +++ b/mcp-server/src/tools/utils.js @@ -6,6 +6,10 @@ import { spawnSync } from "child_process"; import path from "path"; import { contextManager } from '../core/context-manager.js'; // Import the singleton +import fs from 'fs'; + +// Import path utilities to ensure consistent path resolution +import { lastFoundProjectRoot, getPackagePath, PROJECT_MARKERS } from '../core/utils/path-utils.js'; /** * Get normalized project root path @@ -14,16 +18,53 @@ import { contextManager } from '../core/context-manager.js'; // Import the singl * @returns {string} - Normalized absolute path to project root */ export function getProjectRoot(projectRootRaw, log) { - // Make sure projectRoot is set - const rootPath = projectRootRaw || process.cwd(); + // PRECEDENCE ORDER: + // 1. Environment variable override + // 2. Explicitly provided projectRoot in args + // 3. Previously found/cached project root + // 4. Current directory if it has project markers + // 5. Current directory with warning - // Ensure projectRoot is absolute - const projectRoot = path.isAbsolute(rootPath) - ? rootPath - : path.resolve(process.cwd(), rootPath); + // 1. Check for environment variable override + if (process.env.TASK_MASTER_PROJECT_ROOT) { + const envRoot = process.env.TASK_MASTER_PROJECT_ROOT; + const absolutePath = path.isAbsolute(envRoot) + ? envRoot + : path.resolve(process.cwd(), envRoot); + log.info(`Using project root from TASK_MASTER_PROJECT_ROOT environment variable: ${absolutePath}`); + return absolutePath; + } + + // 2. If project root is explicitly provided, use it + if (projectRootRaw) { + const absolutePath = path.isAbsolute(projectRootRaw) + ? projectRootRaw + : path.resolve(process.cwd(), projectRootRaw); + + log.info(`Using explicitly provided project root: ${absolutePath}`); + return absolutePath; + } - log.info(`Using project root: ${projectRoot}`); - return projectRoot; + // 3. If we have a last found project root from a tasks.json search, use that for consistency + if (lastFoundProjectRoot) { + log.info(`Using last known project root where tasks.json was found: ${lastFoundProjectRoot}`); + return lastFoundProjectRoot; + } + + // 4. Check if the current directory has any indicators of being a task-master project + const currentDir = process.cwd(); + if (PROJECT_MARKERS.some(marker => { + const markerPath = path.join(currentDir, marker); + return fs.existsSync(markerPath); + })) { + log.info(`Using current directory as project root (found project markers): ${currentDir}`); + return currentDir; + } + + // 5. Default to current working directory but warn the user + log.warn(`No task-master project detected in current directory. Using ${currentDir} as project root.`); + log.warn('Consider using --project-root to specify the correct project location or set TASK_MASTER_PROJECT_ROOT environment variable.'); + return currentDir; } /** diff --git a/tasks/task_023.txt b/tasks/task_023.txt index 07bd4db6..7faa171a 100644 --- a/tasks/task_023.txt +++ b/tasks/task_023.txt @@ -923,7 +923,7 @@ Following MCP implementation standards: 8. Update tests to reflect the new naming conventions 9. Create a linting rule to enforce naming conventions in future development -## 34. Review functionality of all MCP direct functions [pending] +## 34. Review functionality of all MCP direct functions [in-progress] ### Dependencies: None ### Description: Verify that all implemented MCP direct functions work correctly with edge cases ### Details: @@ -947,12 +947,159 @@ Implement the addResearch function in the MCP server's index.js file to enable r ### Details: Implement the addTemplates function in the MCP server's index.js file to enable template-based generation. Configure proper loading of templates from the appropriate directory and ensure they're accessible to all MCP tools that need to generate formatted content. -## 38. Implement robust project root handling for file paths [pending] +## 38. Implement robust project root handling for file paths [in-progress] ### Dependencies: None ### Description: Create a consistent approach for handling project root paths across MCP tools ### Details: Analyze and refactor the project root handling mechanism to ensure consistent file path resolution across all MCP direct functions. This should properly handle relative and absolute paths, respect the projectRoot parameter when provided, and have appropriate fallbacks when not specified. Document the approach in a comment within path-utils.js for future maintainers. + +Here's additional information addressing the request for research on npm package path handling: + +## Path Handling Best Practices for npm Packages + +### Distinguishing Package and Project Paths + +1. **Package Installation Path**: + - Use `require.resolve()` to find paths relative to your package + - For global installs, use `process.execPath` to locate the Node.js executable + +2. **Project Path**: + - Use `process.cwd()` as a starting point + - Search upwards for `package.json` or `.git` to find project root + - Consider using packages like `find-up` or `pkg-dir` for robust root detection + +### Standard Approaches + +1. **Detecting Project Root**: + - Recursive search for `package.json` or `.git` directory + - Use `path.resolve()` to handle relative paths + - Fall back to `process.cwd()` if no root markers found + +2. **Accessing Package Files**: + - Use `__dirname` for paths relative to current script + - For files in `node_modules`, use `require.resolve('package-name/path/to/file')` + +3. **Separating Package and Project Files**: + - Store package-specific files in a dedicated directory (e.g., `.task-master`) + - Use environment variables to override default paths + +### Cross-Platform Compatibility + +1. Use `path.join()` and `path.resolve()` for cross-platform path handling +2. Avoid hardcoded forward/backslashes in paths +3. Use `os.homedir()` for user home directory references + +### Best Practices for Path Resolution + +1. **Absolute vs Relative Paths**: + - Always convert relative paths to absolute using `path.resolve()` + - Use `path.isAbsolute()` to check if a path is already absolute + +2. **Handling Different Installation Scenarios**: + - Local dev: Use `process.cwd()` as fallback project root + - Local dependency: Resolve paths relative to consuming project + - Global install: Use `process.execPath` to locate global `node_modules` + +3. **Configuration Options**: + - Allow users to specify custom project root via CLI option or config file + - Implement a clear precedence order for path resolution (e.g., CLI option > config file > auto-detection) + +4. **Error Handling**: + - Provide clear error messages when critical paths cannot be resolved + - Implement retry logic with alternative methods if primary path detection fails + +5. **Documentation**: + - Clearly document path handling behavior in README and inline comments + - Provide examples for common scenarios and edge cases + +By implementing these practices, the MCP tools can achieve consistent and robust path handling across various npm installation and usage scenarios. + + + +Here's additional information addressing the request for clarification on path handling challenges for npm packages: + +## Advanced Path Handling Challenges and Solutions + +### Challenges to Avoid + +1. **Relying solely on process.cwd()**: + - Global installs: process.cwd() could be any directory + - Local installs as dependency: points to parent project's root + - Users may run commands from subdirectories + +2. **Dual Path Requirements**: + - Package Path: Where task-master code is installed + - Project Path: Where user's tasks.json resides + +3. **Specific Edge Cases**: + - Non-project directory execution + - Deeply nested project structures + - Yarn/pnpm workspaces + - Monorepos with multiple tasks.json files + - Commands invoked from scripts in different directories + +### Advanced Solutions + +1. **Project Marker Detection**: + - Implement recursive search for package.json or .git + - Use `find-up` package for efficient directory traversal + ```javascript + const findUp = require('find-up'); + const projectRoot = await findUp(dir => findUp.sync('package.json', { cwd: dir })); + ``` + +2. **Package Path Resolution**: + - Leverage `import.meta.url` with `fileURLToPath`: + ```javascript + import { fileURLToPath } from 'url'; + import path from 'path'; + + const __filename = fileURLToPath(import.meta.url); + const __dirname = path.dirname(__filename); + const packageRoot = path.resolve(__dirname, '..'); + ``` + +3. **Workspace-Aware Resolution**: + - Detect Yarn/pnpm workspaces: + ```javascript + const findWorkspaceRoot = require('find-yarn-workspace-root'); + const workspaceRoot = findWorkspaceRoot(process.cwd()); + ``` + +4. **Monorepo Handling**: + - Implement cascading configuration search + - Allow multiple tasks.json files with clear precedence rules + +5. **CLI Tool Inspiration**: + - ESLint: Uses `eslint-find-rule-files` for config discovery + - Jest: Implements `jest-resolve` for custom module resolution + - Next.js: Uses `find-up` to locate project directories + +6. **Robust Path Resolution Algorithm**: + ```javascript + function resolveProjectRoot(startDir) { + const projectMarkers = ['package.json', '.git', 'tasks.json']; + let currentDir = startDir; + while (currentDir !== path.parse(currentDir).root) { + if (projectMarkers.some(marker => fs.existsSync(path.join(currentDir, marker)))) { + return currentDir; + } + currentDir = path.dirname(currentDir); + } + return startDir; // Fallback to original directory + } + ``` + +7. **Environment Variable Overrides**: + - Allow users to explicitly set paths: + ```javascript + const projectRoot = process.env.TASK_MASTER_PROJECT_ROOT || resolveProjectRoot(process.cwd()); + ``` + +By implementing these advanced techniques, task-master can achieve robust path handling across various npm scenarios without requiring manual specification. + + ## 39. Implement add-dependency MCP command [done] ### Dependencies: 23.31 ### Description: Create MCP tool implementation for the add-dependency command @@ -989,3 +1136,68 @@ Analyze and refactor the project root handling mechanism to ensure consistent fi ### Details: +## 45. Support setting env variables through mcp server [pending] +### Dependencies: None +### Description: currently we need to access the env variables through the env file present in the project (that we either create or find and append to). we could abstract this by allowing users to define the env vars in the mcp.json directly as folks currently do. mcp.json should then be in gitignore if thats the case. but for this i think in fastmcp all we need is to access ENV in a specific way. we need to find that way and then implement it +### Details: + + + +To access environment variables defined in the mcp.json config file when using FastMCP, you can utilize the `Config` class from the `fastmcp` module. Here's how to implement this: + +1. Import the necessary module: +```python +from fastmcp import Config +``` + +2. Access environment variables: +```python +config = Config() +env_var = config.env.get("VARIABLE_NAME") +``` + +This approach allows you to retrieve environment variables defined in the mcp.json file directly in your code. The `Config` class automatically loads the configuration, including environment variables, from the mcp.json file. + +For security, ensure that sensitive information in mcp.json is not committed to version control. You can add mcp.json to your .gitignore file to prevent accidental commits. + +If you need to access multiple environment variables, you can do so like this: +```python +db_url = config.env.get("DATABASE_URL") +api_key = config.env.get("API_KEY") +debug_mode = config.env.get("DEBUG_MODE", False) # With a default value +``` + +This method provides a clean and consistent way to access environment variables defined in the mcp.json configuration file within your FastMCP project. + + + +To access environment variables defined in the mcp.json config file when using FastMCP in a JavaScript environment, you can use the `fastmcp` npm package. Here's how to implement this: + +1. Install the `fastmcp` package: +```bash +npm install fastmcp +``` + +2. Import the necessary module: +```javascript +const { Config } = require('fastmcp'); +``` + +3. Access environment variables: +```javascript +const config = new Config(); +const envVar = config.env.get('VARIABLE_NAME'); +``` + +This approach allows you to retrieve environment variables defined in the mcp.json file directly in your JavaScript code. The `Config` class automatically loads the configuration, including environment variables, from the mcp.json file. + +You can access multiple environment variables like this: +```javascript +const dbUrl = config.env.get('DATABASE_URL'); +const apiKey = config.env.get('API_KEY'); +const debugMode = config.env.get('DEBUG_MODE', false); // With a default value +``` + +This method provides a consistent way to access environment variables defined in the mcp.json configuration file within your FastMCP project in a JavaScript environment. + + diff --git a/tasks/task_040.txt b/tasks/task_040.txt index ec8e5ff9..e8e351de 100644 --- a/tasks/task_040.txt +++ b/tasks/task_040.txt @@ -1,102 +1,39 @@ # Task ID: 40 -# Title: Implement Project Funding Documentation and Support Infrastructure -# Status: in-progress +# Title: Implement 'plan' Command for Task Implementation Planning +# Status: pending # Dependencies: None # Priority: medium -# Description: Create FUNDING.yml for GitHub Sponsors integration that outlines all financial support options for the Task Master project. +# Description: Create a new 'plan' command that appends a structured implementation plan to tasks or subtasks, generating step-by-step instructions for execution based on the task content. # Details: -This task involves creating a FUNDING.yml file to enable and manage funding options for the Task Master project: +Implement a new 'plan' command that will append a structured implementation plan to existing tasks or subtasks. The implementation should: -**FUNDING.yml file**: - - Create a .github/FUNDING.yml file following GitHub's specifications - - Include configuration for multiple funding platforms: - - GitHub Sponsors (primary if available) - - Open Collective - - Patreon - - Ko-fi - - Liberapay - - Custom funding URLs (project website donation page) - - Research and reference successful implementation patterns from Vue.js, React, and TypeScript projects - - Ensure the FUNDING.yml contains sufficient information to guide users on how to support the project - - Include comments within the YAML file to provide context for each funding option +1. Accept an '--id' parameter that can reference either a task or subtask ID +2. Determine whether the ID refers to a task or subtask and retrieve the appropriate content from tasks.json and/or individual task files +3. Generate a step-by-step implementation plan using AI (Claude by default) +4. Support a '--research' flag to use Perplexity instead of Claude when needed +5. Format the generated plan within XML tags like `...` +6. Append this plan to the implementation details section of the task/subtask +7. Display a confirmation card indicating the implementation plan was successfully created -The implementation should maintain consistent branding and messaging with the rest of the Task Master project. Research at least 5 successful open source projects to identify best practices in funding configuration. +The implementation plan should be detailed and actionable, containing specific steps such as searching for files, creating new files, modifying existing files, etc. The goal is to frontload planning work into the task/subtask so execution can begin immediately. + +Reference the existing 'update-subtask' command implementation as a starting point, as it uses a similar approach for appending content to tasks. Ensure proper error handling for cases where the specified ID doesn't exist or when API calls fail. # Test Strategy: -Testing should verify the technical implementation of the FUNDING.yml file: +Testing should verify: -1. **FUNDING.yml validation**: - - Verify the file is correctly placed in the .github directory - - Validate YAML syntax using a linter - - Test that GitHub correctly displays funding options on the repository page - - Verify all links to external funding platforms are functional +1. Command correctly identifies and retrieves content for both task and subtask IDs +2. Implementation plans are properly generated and formatted with XML tags and timestamps +3. Plans are correctly appended to the implementation details section without overwriting existing content +4. The '--research' flag successfully switches the backend from Claude to Perplexity +5. Appropriate error messages are displayed for invalid IDs or API failures +6. Confirmation card is displayed after successful plan creation -2. **User experience testing**: - - Test the complete funding workflow from a potential supporter's perspective - - Verify the process is intuitive and barriers to contribution are minimized - - Check that the Sponsor button appears correctly on GitHub - - Ensure all funding platform links resolve to the correct destinations - - Gather feedback from 2-3 potential users on clarity and ease of use - -# Subtasks: -## 1. Research and Create FUNDING.yml File [done] -### Dependencies: None -### Description: Research successful funding configurations and create the .github/FUNDING.yml file for GitHub Sponsors integration and other funding platforms. -### Details: -Implementation steps: -1. Create the .github directory at the project root if it doesn't exist -2. Research funding configurations from 5 successful open source projects (Vue.js, React, TypeScript, etc.) -3. Document the patterns and approaches used in these projects -4. Create the FUNDING.yml file with the following platforms: - - GitHub Sponsors (primary) - - Open Collective - - Patreon - - Ko-fi - - Liberapay - - Custom donation URL for the project website -5. Validate the YAML syntax using a linter -6. Test the file by pushing to a test branch and verifying the Sponsor button appears correctly on GitHub - -Testing approach: -- Validate YAML syntax using yamllint or similar tool -- Test on GitHub by checking if the Sponsor button appears in the repository -- Verify each funding link resolves to the correct destination - -## 4. Add Documentation Comments to FUNDING.yml [pending] -### Dependencies: 40.1 -### Description: Add comprehensive comments within the FUNDING.yml file to provide context and guidance for each funding option. -### Details: -Implementation steps: -1. Add a header comment explaining the purpose of the file -2. For each funding platform entry, add comments that explain: - - What the platform is - - How funds are processed on this platform - - Any specific benefits of using this platform - - Brief instructions for potential sponsors -3. Include a comment about how sponsors will be acknowledged -4. Add information about fund allocation (maintenance, new features, infrastructure) -5. Ensure comments follow YAML comment syntax and don't break the file structure - -Testing approach: -- Validate that the YAML file still passes linting with comments added -- Verify the file still functions correctly on GitHub -- Have at least one team member review the comments for clarity and completeness - -## 5. Integrate Funding Information in Project README [pending] -### Dependencies: 40.1, 40.4 -### Description: Add a section to the project README that highlights the funding options and directs users to the Sponsor button. -### Details: -Implementation steps: -1. Create a 'Support the Project' or 'Sponsorship' section in the README.md -2. Explain briefly why financial support matters for the project -3. Direct users to the GitHub Sponsor button -4. Mention the alternative funding platforms available -5. Include a brief note on how funds will be used -6. Add any relevant funding badges (e.g., Open Collective, GitHub Sponsors) - -Testing approach: -- Review the README section for clarity and conciseness -- Verify all links work correctly -- Ensure the section is appropriately visible but doesn't overshadow project information -- Check that badges render correctly +Test cases should include: +- Running 'plan --id 123' on an existing task +- Running 'plan --id 123.1' on an existing subtask +- Running 'plan --id 123 --research' to test the Perplexity integration +- Running 'plan --id 999' with a non-existent ID to verify error handling +- Running the command on tasks with existing implementation plans to ensure proper appending +Manually review the quality of generated plans to ensure they provide actionable, step-by-step guidance that accurately reflects the task requirements. diff --git a/tasks/tasks.json b/tasks/tasks.json index 9b452aa2..a1fdb5ba 100644 --- a/tasks/tasks.json +++ b/tasks/tasks.json @@ -1656,7 +1656,7 @@ "title": "Review functionality of all MCP direct functions", "description": "Verify that all implemented MCP direct functions work correctly with edge cases", "details": "Perform comprehensive testing of all MCP direct function implementations to ensure they handle various input scenarios correctly and return appropriate responses. Check edge cases, error handling, and parameter validation.", - "status": "pending", + "status": "in-progress", "dependencies": [], "parentTaskId": 23 }, @@ -1691,8 +1691,8 @@ "id": 38, "title": "Implement robust project root handling for file paths", "description": "Create a consistent approach for handling project root paths across MCP tools", - "details": "Analyze and refactor the project root handling mechanism to ensure consistent file path resolution across all MCP direct functions. This should properly handle relative and absolute paths, respect the projectRoot parameter when provided, and have appropriate fallbacks when not specified. Document the approach in a comment within path-utils.js for future maintainers.", - "status": "pending", + "details": "Analyze and refactor the project root handling mechanism to ensure consistent file path resolution across all MCP direct functions. This should properly handle relative and absolute paths, respect the projectRoot parameter when provided, and have appropriate fallbacks when not specified. Document the approach in a comment within path-utils.js for future maintainers.\n\n\nHere's additional information addressing the request for research on npm package path handling:\n\n## Path Handling Best Practices for npm Packages\n\n### Distinguishing Package and Project Paths\n\n1. **Package Installation Path**: \n - Use `require.resolve()` to find paths relative to your package\n - For global installs, use `process.execPath` to locate the Node.js executable\n\n2. **Project Path**:\n - Use `process.cwd()` as a starting point\n - Search upwards for `package.json` or `.git` to find project root\n - Consider using packages like `find-up` or `pkg-dir` for robust root detection\n\n### Standard Approaches\n\n1. **Detecting Project Root**:\n - Recursive search for `package.json` or `.git` directory\n - Use `path.resolve()` to handle relative paths\n - Fall back to `process.cwd()` if no root markers found\n\n2. **Accessing Package Files**:\n - Use `__dirname` for paths relative to current script\n - For files in `node_modules`, use `require.resolve('package-name/path/to/file')`\n\n3. **Separating Package and Project Files**:\n - Store package-specific files in a dedicated directory (e.g., `.task-master`)\n - Use environment variables to override default paths\n\n### Cross-Platform Compatibility\n\n1. Use `path.join()` and `path.resolve()` for cross-platform path handling\n2. Avoid hardcoded forward/backslashes in paths\n3. Use `os.homedir()` for user home directory references\n\n### Best Practices for Path Resolution\n\n1. **Absolute vs Relative Paths**:\n - Always convert relative paths to absolute using `path.resolve()`\n - Use `path.isAbsolute()` to check if a path is already absolute\n\n2. **Handling Different Installation Scenarios**:\n - Local dev: Use `process.cwd()` as fallback project root\n - Local dependency: Resolve paths relative to consuming project\n - Global install: Use `process.execPath` to locate global `node_modules`\n\n3. **Configuration Options**:\n - Allow users to specify custom project root via CLI option or config file\n - Implement a clear precedence order for path resolution (e.g., CLI option > config file > auto-detection)\n\n4. **Error Handling**:\n - Provide clear error messages when critical paths cannot be resolved\n - Implement retry logic with alternative methods if primary path detection fails\n\n5. **Documentation**:\n - Clearly document path handling behavior in README and inline comments\n - Provide examples for common scenarios and edge cases\n\nBy implementing these practices, the MCP tools can achieve consistent and robust path handling across various npm installation and usage scenarios.\n\n\n\nHere's additional information addressing the request for clarification on path handling challenges for npm packages:\n\n## Advanced Path Handling Challenges and Solutions\n\n### Challenges to Avoid\n\n1. **Relying solely on process.cwd()**:\n - Global installs: process.cwd() could be any directory\n - Local installs as dependency: points to parent project's root\n - Users may run commands from subdirectories\n\n2. **Dual Path Requirements**:\n - Package Path: Where task-master code is installed\n - Project Path: Where user's tasks.json resides\n\n3. **Specific Edge Cases**:\n - Non-project directory execution\n - Deeply nested project structures\n - Yarn/pnpm workspaces\n - Monorepos with multiple tasks.json files\n - Commands invoked from scripts in different directories\n\n### Advanced Solutions\n\n1. **Project Marker Detection**:\n - Implement recursive search for package.json or .git\n - Use `find-up` package for efficient directory traversal\n ```javascript\n const findUp = require('find-up');\n const projectRoot = await findUp(dir => findUp.sync('package.json', { cwd: dir }));\n ```\n\n2. **Package Path Resolution**:\n - Leverage `import.meta.url` with `fileURLToPath`:\n ```javascript\n import { fileURLToPath } from 'url';\n import path from 'path';\n \n const __filename = fileURLToPath(import.meta.url);\n const __dirname = path.dirname(__filename);\n const packageRoot = path.resolve(__dirname, '..');\n ```\n\n3. **Workspace-Aware Resolution**:\n - Detect Yarn/pnpm workspaces:\n ```javascript\n const findWorkspaceRoot = require('find-yarn-workspace-root');\n const workspaceRoot = findWorkspaceRoot(process.cwd());\n ```\n\n4. **Monorepo Handling**:\n - Implement cascading configuration search\n - Allow multiple tasks.json files with clear precedence rules\n\n5. **CLI Tool Inspiration**:\n - ESLint: Uses `eslint-find-rule-files` for config discovery\n - Jest: Implements `jest-resolve` for custom module resolution\n - Next.js: Uses `find-up` to locate project directories\n\n6. **Robust Path Resolution Algorithm**:\n ```javascript\n function resolveProjectRoot(startDir) {\n const projectMarkers = ['package.json', '.git', 'tasks.json'];\n let currentDir = startDir;\n while (currentDir !== path.parse(currentDir).root) {\n if (projectMarkers.some(marker => fs.existsSync(path.join(currentDir, marker)))) {\n return currentDir;\n }\n currentDir = path.dirname(currentDir);\n }\n return startDir; // Fallback to original directory\n }\n ```\n\n7. **Environment Variable Overrides**:\n - Allow users to explicitly set paths:\n ```javascript\n const projectRoot = process.env.TASK_MASTER_PROJECT_ROOT || resolveProjectRoot(process.cwd());\n ```\n\nBy implementing these advanced techniques, task-master can achieve robust path handling across various npm scenarios without requiring manual specification.\n", + "status": "in-progress", "dependencies": [], "parentTaskId": 23 }, @@ -1762,6 +1762,15 @@ "status": "deferred", "dependencies": [], "parentTaskId": 23 + }, + { + "id": 45, + "title": "Support setting env variables through mcp server", + "description": "currently we need to access the env variables through the env file present in the project (that we either create or find and append to). we could abstract this by allowing users to define the env vars in the mcp.json directly as folks currently do. mcp.json should then be in gitignore if thats the case. but for this i think in fastmcp all we need is to access ENV in a specific way. we need to find that way and then implement it", + "details": "\n\n\nTo access environment variables defined in the mcp.json config file when using FastMCP, you can utilize the `Config` class from the `fastmcp` module. Here's how to implement this:\n\n1. Import the necessary module:\n```python\nfrom fastmcp import Config\n```\n\n2. Access environment variables:\n```python\nconfig = Config()\nenv_var = config.env.get(\"VARIABLE_NAME\")\n```\n\nThis approach allows you to retrieve environment variables defined in the mcp.json file directly in your code. The `Config` class automatically loads the configuration, including environment variables, from the mcp.json file.\n\nFor security, ensure that sensitive information in mcp.json is not committed to version control. You can add mcp.json to your .gitignore file to prevent accidental commits.\n\nIf you need to access multiple environment variables, you can do so like this:\n```python\ndb_url = config.env.get(\"DATABASE_URL\")\napi_key = config.env.get(\"API_KEY\")\ndebug_mode = config.env.get(\"DEBUG_MODE\", False) # With a default value\n```\n\nThis method provides a clean and consistent way to access environment variables defined in the mcp.json configuration file within your FastMCP project.\n\n\n\nTo access environment variables defined in the mcp.json config file when using FastMCP in a JavaScript environment, you can use the `fastmcp` npm package. Here's how to implement this:\n\n1. Install the `fastmcp` package:\n```bash\nnpm install fastmcp\n```\n\n2. Import the necessary module:\n```javascript\nconst { Config } = require('fastmcp');\n```\n\n3. Access environment variables:\n```javascript\nconst config = new Config();\nconst envVar = config.env.get('VARIABLE_NAME');\n```\n\nThis approach allows you to retrieve environment variables defined in the mcp.json file directly in your JavaScript code. The `Config` class automatically loads the configuration, including environment variables, from the mcp.json file.\n\nYou can access multiple environment variables like this:\n```javascript\nconst dbUrl = config.env.get('DATABASE_URL');\nconst apiKey = config.env.get('API_KEY');\nconst debugMode = config.env.get('DEBUG_MODE', false); // With a default value\n```\n\nThis method provides a consistent way to access environment variables defined in the mcp.json configuration file within your FastMCP project in a JavaScript environment.\n", + "status": "pending", + "dependencies": [], + "parentTaskId": 23 } ] }, @@ -2352,91 +2361,13 @@ }, { "id": 40, - "title": "Implement Project Funding Documentation and Support Infrastructure", - "description": "Create FUNDING.yml for GitHub Sponsors integration that outlines all financial support options for the Task Master project.", - "status": "in-progress", - "dependencies": [], - "priority": "medium", - "details": "This task involves creating a FUNDING.yml file to enable and manage funding options for the Task Master project:\n\n**FUNDING.yml file**:\n - Create a .github/FUNDING.yml file following GitHub's specifications\n - Include configuration for multiple funding platforms:\n - GitHub Sponsors (primary if available)\n - Open Collective\n - Patreon\n - Ko-fi\n - Liberapay\n - Custom funding URLs (project website donation page)\n - Research and reference successful implementation patterns from Vue.js, React, and TypeScript projects\n - Ensure the FUNDING.yml contains sufficient information to guide users on how to support the project\n - Include comments within the YAML file to provide context for each funding option\n\nThe implementation should maintain consistent branding and messaging with the rest of the Task Master project. Research at least 5 successful open source projects to identify best practices in funding configuration.", - "testStrategy": "Testing should verify the technical implementation of the FUNDING.yml file:\n\n1. **FUNDING.yml validation**:\n - Verify the file is correctly placed in the .github directory\n - Validate YAML syntax using a linter\n - Test that GitHub correctly displays funding options on the repository page\n - Verify all links to external funding platforms are functional\n\n2. **User experience testing**:\n - Test the complete funding workflow from a potential supporter's perspective\n - Verify the process is intuitive and barriers to contribution are minimized\n - Check that the Sponsor button appears correctly on GitHub\n - Ensure all funding platform links resolve to the correct destinations\n - Gather feedback from 2-3 potential users on clarity and ease of use", - "subtasks": [ - { - "id": 1, - "title": "Research and Create FUNDING.yml File", - "description": "Research successful funding configurations and create the .github/FUNDING.yml file for GitHub Sponsors integration and other funding platforms.", - "dependencies": [], - "details": "Implementation steps:\n1. Create the .github directory at the project root if it doesn't exist\n2. Research funding configurations from 5 successful open source projects (Vue.js, React, TypeScript, etc.)\n3. Document the patterns and approaches used in these projects\n4. Create the FUNDING.yml file with the following platforms:\n - GitHub Sponsors (primary)\n - Open Collective\n - Patreon\n - Ko-fi\n - Liberapay\n - Custom donation URL for the project website\n5. Validate the YAML syntax using a linter\n6. Test the file by pushing to a test branch and verifying the Sponsor button appears correctly on GitHub\n\nTesting approach:\n- Validate YAML syntax using yamllint or similar tool\n- Test on GitHub by checking if the Sponsor button appears in the repository\n- Verify each funding link resolves to the correct destination", - "status": "done", - "parentTaskId": 40 - }, - { - "id": 4, - "title": "Add Documentation Comments to FUNDING.yml", - "description": "Add comprehensive comments within the FUNDING.yml file to provide context and guidance for each funding option.", - "dependencies": [ - 1 - ], - "details": "Implementation steps:\n1. Add a header comment explaining the purpose of the file\n2. For each funding platform entry, add comments that explain:\n - What the platform is\n - How funds are processed on this platform\n - Any specific benefits of using this platform\n - Brief instructions for potential sponsors\n3. Include a comment about how sponsors will be acknowledged\n4. Add information about fund allocation (maintenance, new features, infrastructure)\n5. Ensure comments follow YAML comment syntax and don't break the file structure\n\nTesting approach:\n- Validate that the YAML file still passes linting with comments added\n- Verify the file still functions correctly on GitHub\n- Have at least one team member review the comments for clarity and completeness", - "status": "pending", - "parentTaskId": 40 - }, - { - "id": 5, - "title": "Integrate Funding Information in Project README", - "description": "Add a section to the project README that highlights the funding options and directs users to the Sponsor button.", - "dependencies": [ - 1, - 4 - ], - "details": "Implementation steps:\n1. Create a 'Support the Project' or 'Sponsorship' section in the README.md\n2. Explain briefly why financial support matters for the project\n3. Direct users to the GitHub Sponsor button\n4. Mention the alternative funding platforms available\n5. Include a brief note on how funds will be used\n6. Add any relevant funding badges (e.g., Open Collective, GitHub Sponsors)\n\nTesting approach:\n- Review the README section for clarity and conciseness\n- Verify all links work correctly\n- Ensure the section is appropriately visible but doesn't overshadow project information\n- Check that badges render correctly", - "status": "pending", - "parentTaskId": 40 - } - ] - }, - { - "id": 41, - "title": "Implement GitHub Actions CI Workflow for Cross-Platform Testing", - "description": "Create a CI workflow file (ci.yml) that tests the codebase across multiple Node.js versions and operating systems using GitHub Actions.", + "title": "Implement 'plan' Command for Task Implementation Planning", + "description": "Create a new 'plan' command that appends a structured implementation plan to tasks or subtasks, generating step-by-step instructions for execution based on the task content.", "status": "pending", "dependencies": [], - "priority": "high", - "details": "Create a GitHub Actions workflow file at `.github/workflows/ci.yml` with the following specifications:\n\n1. Configure the workflow to trigger on:\n - Push events to any branch\n - Pull request events targeting any branch\n\n2. Implement a matrix strategy that tests across:\n - Node.js versions: 18.x, 20.x, and 22.x\n - Operating systems: Ubuntu-latest and Windows-latest\n\n3. Include proper Git configuration steps:\n - Set Git user name to 'GitHub Actions'\n - Set Git email to 'github-actions@github.com'\n\n4. Configure workflow steps to:\n - Checkout the repository using actions/checkout@v3\n - Set up Node.js using actions/setup-node@v3 with the matrix version\n - Use npm for package management (not pnpm)\n - Install dependencies with 'npm ci'\n - Run linting with 'npm run lint' (if available)\n - Run tests with 'npm test'\n - Run build process with 'npm run build'\n\n5. Implement concurrency controls to:\n - Cancel in-progress workflows when new commits are pushed to the same PR\n - Use a concurrency group based on the GitHub ref and workflow name\n\n6. Add proper caching for npm dependencies to speed up workflow runs\n\n7. Ensure the workflow includes appropriate timeouts to prevent hung jobs", - "testStrategy": "To verify correct implementation of the GitHub Actions CI workflow:\n\n1. Manual verification:\n - Check that the file is correctly placed at `.github/workflows/ci.yml`\n - Verify the YAML syntax is valid using a YAML linter\n - Confirm all required configurations (triggers, matrix, steps) are present\n\n2. Functional testing:\n - Push a commit to a feature branch to confirm the workflow triggers\n - Create a PR to verify the workflow runs on pull requests\n - Verify the workflow successfully runs on both Ubuntu and Windows\n - Confirm tests run against all three Node.js versions (18, 20, 22)\n - Test concurrency by pushing multiple commits to the same PR rapidly\n\n3. Edge case testing:\n - Introduce a failing test and verify the workflow reports failure\n - Test with a large dependency tree to verify caching works correctly\n - Verify the workflow handles non-ASCII characters in file paths correctly (particularly on Windows)\n\n4. Check workflow logs to ensure:\n - Git configuration is applied correctly\n - Dependencies are installed with npm (not pnpm)\n - All matrix combinations run independently\n - Concurrency controls cancel redundant workflow runs", - "subtasks": [ - { - "id": 1, - "title": "Create Basic GitHub Actions Workflow Structure", - "description": "Set up the foundational GitHub Actions workflow file with triggers, checkout, and Node.js setup using matrix strategy", - "dependencies": [], - "details": "1. Create `.github/workflows/` directory if it doesn't exist\n2. Create a new file `ci.yml` inside this directory\n3. Define the workflow name at the top of the file\n4. Configure triggers for push events to any branch and pull request events targeting any branch\n5. Set up the matrix strategy for Node.js versions (18.x, 20.x, 22.x) and operating systems (Ubuntu-latest, Windows-latest)\n6. Configure the job to checkout the repository using actions/checkout@v3\n7. Set up Node.js using actions/setup-node@v3 with the matrix version\n8. Add proper caching for npm dependencies\n9. Test the workflow by pushing the file to a test branch and verifying it triggers correctly\n10. Verify that the matrix builds are running on all specified Node versions and operating systems", - "status": "pending", - "parentTaskId": 41 - }, - { - "id": 2, - "title": "Implement Build and Test Steps with Git Configuration", - "description": "Add the core build and test steps to the workflow, including Git configuration, dependency installation, and execution of lint, test, and build commands", - "dependencies": [ - 1 - ], - "details": "1. Add Git configuration steps to set user name to 'GitHub Actions' and email to 'github-actions@github.com'\n2. Add step to install dependencies with 'npm ci'\n3. Add conditional step to run linting with 'npm run lint' if available\n4. Add step to run tests with 'npm test'\n5. Add step to run build process with 'npm run build'\n6. Ensure each step has appropriate names for clear visibility in GitHub Actions UI\n7. Add appropriate error handling and continue-on-error settings where needed\n8. Test the workflow by pushing a change and verifying all build steps execute correctly\n9. Verify that the workflow correctly runs on both Ubuntu and Windows environments\n10. Ensure that all commands use the correct syntax for cross-platform compatibility", - "status": "pending", - "parentTaskId": 41 - }, - { - "id": 3, - "title": "Add Workflow Optimization Features", - "description": "Implement concurrency controls, timeouts, and other optimization features to improve workflow efficiency and reliability", - "dependencies": [ - 1, - 2 - ], - "details": "1. Implement concurrency controls to cancel in-progress workflows when new commits are pushed to the same PR\n2. Define a concurrency group based on the GitHub ref and workflow name\n3. Add appropriate timeouts to prevent hung jobs (typically 30-60 minutes depending on project complexity)\n4. Add status badges to the README.md file to show build status\n5. Optimize the workflow by adding appropriate 'if' conditions to skip unnecessary steps\n6. Add job summary outputs to provide clear information about the build results\n7. Test the concurrency feature by pushing multiple commits in quick succession to a PR\n8. Verify that old workflow runs are canceled when new commits are pushed\n9. Test timeout functionality by temporarily adding a long-running step\n10. Document the CI workflow in project documentation, explaining what it does and how to troubleshoot common issues", - "status": "pending", - "parentTaskId": 41 - } - ] + "priority": "medium", + "details": "Implement a new 'plan' command that will append a structured implementation plan to existing tasks or subtasks. The implementation should:\n\n1. Accept an '--id' parameter that can reference either a task or subtask ID\n2. Determine whether the ID refers to a task or subtask and retrieve the appropriate content from tasks.json and/or individual task files\n3. Generate a step-by-step implementation plan using AI (Claude by default)\n4. Support a '--research' flag to use Perplexity instead of Claude when needed\n5. Format the generated plan within XML tags like `...`\n6. Append this plan to the implementation details section of the task/subtask\n7. Display a confirmation card indicating the implementation plan was successfully created\n\nThe implementation plan should be detailed and actionable, containing specific steps such as searching for files, creating new files, modifying existing files, etc. The goal is to frontload planning work into the task/subtask so execution can begin immediately.\n\nReference the existing 'update-subtask' command implementation as a starting point, as it uses a similar approach for appending content to tasks. Ensure proper error handling for cases where the specified ID doesn't exist or when API calls fail.", + "testStrategy": "Testing should verify:\n\n1. Command correctly identifies and retrieves content for both task and subtask IDs\n2. Implementation plans are properly generated and formatted with XML tags and timestamps\n3. Plans are correctly appended to the implementation details section without overwriting existing content\n4. The '--research' flag successfully switches the backend from Claude to Perplexity\n5. Appropriate error messages are displayed for invalid IDs or API failures\n6. Confirmation card is displayed after successful plan creation\n\nTest cases should include:\n- Running 'plan --id 123' on an existing task\n- Running 'plan --id 123.1' on an existing subtask\n- Running 'plan --id 123 --research' to test the Perplexity integration\n- Running 'plan --id 999' with a non-existent ID to verify error handling\n- Running the command on tasks with existing implementation plans to ensure proper appending\n\nManually review the quality of generated plans to ensure they provide actionable, step-by-step guidance that accurately reflects the task requirements." } ] } \ No newline at end of file diff --git a/tests/unit/ui.test.js b/tests/unit/ui.test.js index d9ee56e2..574ad632 100644 --- a/tests/unit/ui.test.js +++ b/tests/unit/ui.test.js @@ -177,26 +177,42 @@ describe('UI Module', () => { describe('createProgressBar function', () => { test('should create a progress bar with the correct percentage', () => { - const result = createProgressBar(50, 10); - expect(result).toBe('█████░░░░░ 50%'); + const result = createProgressBar(50, 10, { + 'pending': 20, + 'in-progress': 15, + 'blocked': 5 + }); + expect(result).toContain('50%'); }); test('should handle 0% progress', () => { const result = createProgressBar(0, 10); - expect(result).toBe('░░░░░░░░░░ 0%'); + expect(result).toContain('0%'); }); test('should handle 100% progress', () => { const result = createProgressBar(100, 10); - expect(result).toBe('██████████ 100%'); + expect(result).toContain('100%'); }); test('should handle invalid percentages by clamping', () => { - const result1 = createProgressBar(0, 10); // -10 should clamp to 0 - expect(result1).toBe('░░░░░░░░░░ 0%'); + const result1 = createProgressBar(0, 10); + expect(result1).toContain('0%'); - const result2 = createProgressBar(100, 10); // 150 should clamp to 100 - expect(result2).toBe('██████████ 100%'); + const result2 = createProgressBar(100, 10); + expect(result2).toContain('100%'); + }); + + test('should support status breakdown in the progress bar', () => { + const result = createProgressBar(30, 10, { + 'pending': 30, + 'in-progress': 20, + 'blocked': 10, + 'deferred': 5, + 'cancelled': 5 + }); + + expect(result).toContain('40%'); }); });