Compare commits

..

32 Commits

Author SHA1 Message Date
Eyal Toledano
292c2caf7f Merge pull request #722 from eyaltoledano/changeset-release/main
Version Packages
2025-06-08 17:43:43 -04:00
github-actions[bot]
526d64fb8a Version Packages 2025-06-08 20:39:20 +00:00
Ralph Khreish
1b86ce6c83 Merge pull request #721 from eyaltoledano/next 2025-06-08 22:38:49 +02:00
Eyal Toledano
8a86ec538e Merge pull request #717 from eyaltoledano/v016-last-touches
V016 last touches
2025-06-08 16:20:32 -04:00
Eyal Toledano
9e7387952d readme: moves up the documentation links higher up in the readme. same with the cursor one-click install. 2025-06-08 15:59:49 -04:00
Eyal Toledano
ab05f550b3 chore: adjusts changeset from minor to patch to avoid version bump to 0.17 2025-06-08 15:32:31 -04:00
Ralph Khreish
cf01fbedcf Merge pull request #686 from eyaltoledano/next 2025-06-08 19:37:46 +02:00
Ralph Khreish
d2bcbee0c2 chore: small readme nitpicks 2025-06-08 13:49:40 +02:00
Eyal Toledano
72171bd4ba Merge pull request #702 from eyaltoledano/readme-fixes-2
readme: removes the taskmaster list output
2025-06-07 23:23:01 -04:00
Eyal Toledano
9ad517231a readme: removes the taskmaster list output which is too overwhelming given its size with subtasks. may re-add later. fixes likely issues in the json for manual config in cursor and windsurf in the readme. 2025-06-07 23:21:17 -04:00
Eyal Toledano
7db3b47a47 Merge pull request #701 from eyaltoledano/readme-npm-badges
chore: updates readme with npm download badges and mentions AI Jason who is joining the taskmaster core team.
2025-06-07 23:17:02 -04:00
Eyal Toledano
3de785a99c readme: fix twitter urls. 2025-06-07 23:10:08 -04:00
Eyal Toledano
8188fdd832 fix: readme typo 2025-06-07 23:09:16 -04:00
Eyal Toledano
3fadc2f1ef chore: fixes urls in readme npm packages again 2025-06-07 23:07:12 -04:00
Eyal Toledano
dd36111367 chore: fixes urls in readme npm packages 2025-06-07 23:06:26 -04:00
Eyal Toledano
c58ab8963c chore: updates readme with npm download badges and mentions AI Jason who is joining the taskmaster core team. 2025-06-07 23:02:36 -04:00
github-actions[bot]
3eeb4721aa docs: Auto-update and format models.md 2025-06-08 02:15:32 +00:00
Eyal Toledano
7ea905f2c5 Merge pull request #699 from eyaltoledano/0.16.2-touchups
0.16.2 touchups
2025-06-07 22:15:19 -04:00
Eyal Toledano
51dd4f625b chore: changeset adjustment 2025-06-07 22:13:11 -04:00
Eyal Toledano
2e55757b26 ninja(sync): add sync-readme command for GitHub README export with UTM tracking and professional markdown formatting. Experimental 2025-06-07 22:07:35 -04:00
Eyal Toledano
54bfc72baa chore: more linting 2025-06-07 20:32:37 -04:00
Eyal Toledano
faae0b419d chore: passes tests and linting 2025-06-07 20:30:51 -04:00
Eyal Toledano
27edbd8f3f chore: changeset 2025-06-07 20:28:28 -04:00
Eyal Toledano
b1390e4ddf refactor: enhance add-task fuzzy search and fix duplicate banner display
- **Remove hardcoded category system** in add-task that always matched 'Task management'
- **Eliminate arbitrary limits** in fuzzy search results (5→25 high relevance, 3→10 medium relevance, 8→20 detailed tasks)
- **Improve semantic weighting** in Fuse.js search (details=3, description=2, title=1.5) for better relevance
- **Fix duplicate banner issue** by removing console.clear() and redundant displayBanner() calls from UI functions
- **Enhance context generation** to rely on semantic similarity rather than rigid pattern matching
- **Preserve terminal history** to address GitHub issue #553 about eating terminal lines
- **Remove displayBanner() calls** from: displayHelp, displayNextTask, displayTaskById, displayComplexityReport, set-task-status, clear-subtasks, dependency-manager functions

The add-task system now provides truly relevant task context based on semantic similarity rather than arbitrary categories and limits, while maintaining a cleaner terminal experience.

Changes span: add-task.js, ui.js, set-task-status.js, clear-subtasks.js, list-tasks.js, dependency-manager.js

Closes #553
2025-06-07 20:23:55 -04:00
Eyal Toledano
cc04d53720 chore: adds a warning when custom openrouter model is a free model which suffers from lower rate limits, restricted context, and, worst of all, no access to tool_use. 2025-06-07 18:54:11 -04:00
Eyal Toledano
bfd86eb9cc Adds qwen3-235n-a22b:free to supported models. Closes #687) 2025-06-07 18:42:11 -04:00
Eyal Toledano
9eb3842f04 fix(ai-providers): change generateObject mode from 'tool' to 'auto' for better provider compatibility
Fixes Perplexity research role failing with 'tool-mode object generation' error

The hardcoded 'tool' mode was incompatible with providers like Perplexity that support structured JSON output but not function calling/tool use

Using 'auto' mode allows the AI SDK to choose the best approach for each provider
2025-06-07 15:02:48 -04:00
Eyal Toledano
bf2053e140 feat(ui): replace emoji complexity indicators with clean filled circle characters
Replace 🟢, 🟡, 🔴 emojis with ● character in getComplexityWithColor function

Update corresponding unit tests to expect ● instead of emojis

Improves UI continuity
2025-06-07 12:57:45 -04:00
Ralph Khreish
ee0be04302 fix: update MCP tool 2025-06-07 13:29:03 +02:00
Ralph Khreish
c0707fc399 chore: upgrade fast mcp to latest version 2025-06-07 13:29:03 +02:00
Ralph Khreish
1ece6f1904 fix: findTasksPath function 2025-06-07 13:29:03 +02:00
github-actions[bot]
f4a9ad1095 chore: rc version bump 2025-06-06 18:51:19 +00:00
29 changed files with 1202 additions and 468 deletions

View File

@@ -1,7 +0,0 @@
---
"task-master-ai": patch
---
Fix double .taskmaster directory paths in file resolution utilities
- Closes #636

View File

@@ -1,5 +0,0 @@
---
"task-master-ai": patch
---
Add one-click MCP server installation for Cursor

View File

@@ -20,6 +20,7 @@
}
},
"global": {
"userId": "1234567890",
"logLevel": "info",
"debug": false,
"defaultSubtasks": 5,
@@ -27,7 +28,6 @@
"projectName": "Taskmaster",
"ollamaBaseURL": "http://localhost:11434/api",
"bedrockBaseURL": "https://bedrock.us-east-1.amazonaws.com",
"userId": "1234567890",
"azureBaseURL": "https://your-endpoint.azure.com/"
}
}

View File

@@ -1,6 +1,6 @@
# Task ID: 92
# Title: Implement Project Root Environment Variable Support in MCP Configuration
# Status: in-progress
# Status: review
# Dependencies: 1, 3, 17
# Priority: medium
# Description: Add support for a 'TASK_MASTER_PROJECT_ROOT' environment variable in MCP configuration, allowing it to be set in both mcp.json and .env, with precedence over other methods. This will define the root directory for the MCP server and take precedence over all other project root resolution methods. The implementation should be backward compatible with existing workflows that don't use this variable.
@@ -44,49 +44,49 @@ Implementation steps:
- Test with invalid or non-existent directories to verify error handling
# Subtasks:
## 92.1. Update configuration loader to check for TASK_MASTER_PROJECT_ROOT environment variable [pending]
## 1. Update configuration loader to check for TASK_MASTER_PROJECT_ROOT environment variable [pending]
### Dependencies: None
### Description: Modify the configuration loading system to check for the TASK_MASTER_PROJECT_ROOT environment variable as the primary source for project root directory. Ensure proper error handling if the variable is set but points to a non-existent or inaccessible directory.
### Details:
## 92.2. Add support for 'projectRoot' in configuration files [pending]
## 2. Add support for 'projectRoot' in configuration files [pending]
### Dependencies: None
### Description: Implement support for a 'projectRoot' key in mcp_config.toml and mcp.json configuration files as a fallback when the environment variable is not set. Update the configuration parser to recognize and validate this field.
### Details:
## 92.3. Refactor project root resolution logic with clear precedence rules [pending]
## 3. Refactor project root resolution logic with clear precedence rules [pending]
### Dependencies: None
### Description: Create a unified project root resolution function that follows the precedence order: 1) TASK_MASTER_PROJECT_ROOT environment variable, 2) 'projectRoot' in config files, 3) existing resolution methods. Ensure this function is used consistently throughout the codebase.
### Details:
## 92.4. Update all MCP tools to use the new project root resolution [pending]
## 4. Update all MCP tools to use the new project root resolution [pending]
### Dependencies: None
### Description: Identify all MCP tools and components that need to access the project root and update them to use the new resolution logic. Ensure consistent behavior across all parts of the system.
### Details:
## 92.5. Add comprehensive tests for the new project root resolution [pending]
## 5. Add comprehensive tests for the new project root resolution [pending]
### Dependencies: None
### Description: Create unit and integration tests to verify the correct behavior of the project root resolution logic under various configurations and edge cases.
### Details:
## 92.6. Update documentation with new configuration options [pending]
## 6. Update documentation with new configuration options [pending]
### Dependencies: None
### Description: Update the project documentation to clearly explain the new TASK_MASTER_PROJECT_ROOT environment variable, the 'projectRoot' configuration option, and the precedence rules. Include examples of different configuration scenarios.
### Details:
## 92.7. Implement validation for project root directory [pending]
## 7. Implement validation for project root directory [pending]
### Dependencies: None
### Description: Add validation to ensure the specified project root directory exists and has the necessary permissions. Provide clear error messages when validation fails.
### Details:
## 92.8. Implement support for loading environment variables from .env files [pending]
## 8. Implement support for loading environment variables from .env files [pending]
### Dependencies: None
### Description: Add functionality to load the TASK_MASTER_PROJECT_ROOT variable from .env files in the workspace, following best practices for environment variable management in MCP servers.
### Details:

View File

@@ -1,6 +1,6 @@
# Task ID: 95
# Title: Implement .taskmaster Directory Structure
# Status: in-progress
# Status: done
# Dependencies: 1, 3, 4, 17
# Priority: high
# Description: Consolidate all Task Master-managed files in user projects into a clean, centralized .taskmaster/ directory structure to improve organization and keep user project directories clean, based on GitHub issue #275.
@@ -105,7 +105,7 @@ Update Task Master's file handling code to use the new paths: tasks in .taskmast
### Details:
Update the task file generation system to create and read task files from .taskmaster/tasks/ instead of tasks/. Ensure all template paths are updated. Modify any path resolution logic specific to task file handling.
## 4. Implement backward compatibility logic [in-progress]
## 4. Implement backward compatibility logic [done]
### Dependencies: 95.2, 95.3
### Description: Add fallback mechanisms to support both old and new file locations during transition
### Details:
@@ -141,7 +141,7 @@ Update README.md and other documentation to reflect the new .taskmaster structur
### Details:
Create functionality to support user-defined templates in .taskmaster/templates/. Allow users to store custom task templates, PRD templates, or other reusable files. Update Task Master commands to recognize and use templates from this directory when available.
## 10. Verify clean user project directories [in-progress]
## 10. Verify clean user project directories [done]
### Dependencies: 95.8, 95.9
### Description: Ensure the new structure keeps user project root directories clean and organized
### Details:

View File

@@ -0,0 +1,37 @@
# Task ID: 96
# Title: Create Export Command for On-Demand Task File and PDF Generation
# Status: pending
# Dependencies: 2, 4, 95
# Priority: medium
# Description: Develop an 'export' CLI command that generates task files and comprehensive PDF exports on-demand, replacing automatic file generation and providing users with flexible export options.
# Details:
Implement a new 'export' command in the CLI that supports two primary modes: (1) generating individual task files on-demand (superseding the current automatic generation system), and (2) producing a comprehensive PDF export. The PDF should include: a first page with the output of 'tm list --with-subtasks', followed by individual pages for each task (using 'tm show <task_id>') and each subtask (using 'tm show <subtask_id>'). Integrate PDF generation using a robust library (e.g., pdfkit, Puppeteer, or jsPDF) to ensure high-quality output and proper pagination. Refactor or disable any existing automatic file generation logic to avoid performance overhead. Ensure the command supports flexible output paths and options for exporting only files, only PDF, or both. Update documentation and help output to reflect the new export capabilities. Consider concurrency and error handling for large projects. Ensure the export process is efficient and does not block the main CLI thread unnecessarily.
# Test Strategy:
1. Run the 'export' command with various options and verify that task files are generated only on-demand, not automatically. 2. Generate a PDF export and confirm that the first page contains the correct 'tm list --with-subtasks' output, and that each subsequent page accurately reflects the output of 'tm show <task_id>' and 'tm show <subtask_id>' for all tasks and subtasks. 3. Test exporting in projects with large numbers of tasks and subtasks to ensure performance and correctness. 4. Attempt exports with invalid paths or missing data to verify robust error handling. 5. Confirm that no automatic file generation occurs during normal task operations. 6. Review CLI help output and documentation for accuracy regarding the new export functionality.
# Subtasks:
## 1. Remove Automatic Task File Generation from Task Operations [pending]
### Dependencies: None
### Description: Eliminate all calls to generateTaskFiles() from task operations such as add-task, remove-task, set-status, and similar commands to prevent unnecessary performance overhead.
### Details:
Audit the codebase for any automatic invocations of generateTaskFiles() and remove or refactor them to ensure task files are not generated automatically during task operations.
## 2. Implement Export Command Infrastructure with On-Demand Task File Generation [pending]
### Dependencies: 96.1
### Description: Develop the CLI 'export' command infrastructure, enabling users to generate task files on-demand by invoking the preserved generateTaskFiles function only when requested.
### Details:
Create the export command with options for output paths and modes (files, PDF, or both). Ensure generateTaskFiles is only called within this command and not elsewhere.
## 3. Implement Comprehensive PDF Export Functionality [pending]
### Dependencies: 96.2
### Description: Add PDF export capability to the export command, generating a structured PDF with a first page listing all tasks and subtasks, followed by individual pages for each task and subtask, using a robust PDF library.
### Details:
Integrate a PDF generation library (e.g., pdfkit, Puppeteer, or jsPDF). Ensure the PDF includes the output of 'tm list --with-subtasks' on the first page, and uses 'tm show <task_id>' and 'tm show <subtask_id>' for subsequent pages. Handle pagination, concurrency, and error handling for large projects.
## 4. Update Documentation, Tests, and CLI Help for Export Workflow [pending]
### Dependencies: 96.2, 96.3
### Description: Revise all relevant documentation, automated tests, and CLI help output to reflect the new export-based workflow and available options.
### Details:
Update user guides, README files, and CLI help text. Add or modify tests to cover the new export command and its options. Ensure all documentation accurately describes the new workflow and usage.

View File

@@ -5467,6 +5467,70 @@
}
]
},
{
"id": 92,
"title": "Implement Project Root Environment Variable Support in MCP Configuration",
"description": "Add support for a 'TASK_MASTER_PROJECT_ROOT' environment variable in MCP configuration, allowing it to be set in both mcp.json and .env, with precedence over other methods. This will define the root directory for the MCP server and take precedence over all other project root resolution methods. The implementation should be backward compatible with existing workflows that don't use this variable.",
"status": "review",
"dependencies": [
1,
3,
17
],
"priority": "medium",
"details": "Update the MCP server configuration system to support the TASK_MASTER_PROJECT_ROOT environment variable as the standard way to specify the project root directory. This provides better namespacing and avoids conflicts with other tools that might use a generic PROJECT_ROOT variable. Implement a clear precedence order for project root resolution:\n\n1. TASK_MASTER_PROJECT_ROOT environment variable (from shell or .env file)\n2. 'projectRoot' key in mcp_config.toml or mcp.json configuration files\n3. Existing resolution logic (CLI args, current working directory, etc.)\n\nModify the configuration loading logic to check for these sources in the specified order, ensuring backward compatibility. All MCP tools and components should use this standardized project root resolution logic. The TASK_MASTER_PROJECT_ROOT environment variable will be required because path resolution is delegated to the MCP client implementation, ensuring consistent behavior across different environments.\n\nImplementation steps:\n1. Identify all code locations where project root is determined (initialization, utility functions)\n2. Update configuration loaders to check for TASK_MASTER_PROJECT_ROOT in environment variables\n3. Add support for 'projectRoot' in configuration files as a fallback\n4. Refactor project root resolution logic to follow the new precedence rules\n5. Ensure all MCP tools and functions use the updated resolution logic\n6. Add comprehensive error handling for cases where TASK_MASTER_PROJECT_ROOT is not set or invalid\n7. Implement validation to ensure the specified directory exists and is accessible",
"testStrategy": "1. Write unit tests to verify that the config loader correctly reads project root from environment variables and configuration files with the expected precedence:\n - Test TASK_MASTER_PROJECT_ROOT environment variable takes precedence when set\n - Test 'projectRoot' in configuration files is used when environment variable is absent\n - Test fallback to existing resolution logic when neither is specified\n\n2. Add integration tests to ensure that the MCP server and all tools use the correct project root:\n - Test server startup with TASK_MASTER_PROJECT_ROOT set to various valid and invalid paths\n - Test configuration file loading from the specified project root\n - Test path resolution for resources relative to the project root\n\n3. Test backward compatibility:\n - Verify existing workflows function correctly without the new variables\n - Ensure no regression in projects not using the new configuration options\n\n4. Manual testing:\n - Set TASK_MASTER_PROJECT_ROOT in shell environment and verify correct behavior\n - Set TASK_MASTER_PROJECT_ROOT in .env file and verify it's properly loaded\n - Configure 'projectRoot' in configuration files and test precedence\n - Test with invalid or non-existent directories to verify error handling",
"subtasks": [
{
"id": 1,
"title": "Update configuration loader to check for TASK_MASTER_PROJECT_ROOT environment variable",
"description": "Modify the configuration loading system to check for the TASK_MASTER_PROJECT_ROOT environment variable as the primary source for project root directory. Ensure proper error handling if the variable is set but points to a non-existent or inaccessible directory.",
"status": "pending"
},
{
"id": 2,
"title": "Add support for 'projectRoot' in configuration files",
"description": "Implement support for a 'projectRoot' key in mcp_config.toml and mcp.json configuration files as a fallback when the environment variable is not set. Update the configuration parser to recognize and validate this field.",
"status": "pending"
},
{
"id": 3,
"title": "Refactor project root resolution logic with clear precedence rules",
"description": "Create a unified project root resolution function that follows the precedence order: 1) TASK_MASTER_PROJECT_ROOT environment variable, 2) 'projectRoot' in config files, 3) existing resolution methods. Ensure this function is used consistently throughout the codebase.",
"status": "pending"
},
{
"id": 4,
"title": "Update all MCP tools to use the new project root resolution",
"description": "Identify all MCP tools and components that need to access the project root and update them to use the new resolution logic. Ensure consistent behavior across all parts of the system.",
"status": "pending"
},
{
"id": 5,
"title": "Add comprehensive tests for the new project root resolution",
"description": "Create unit and integration tests to verify the correct behavior of the project root resolution logic under various configurations and edge cases.",
"status": "pending"
},
{
"id": 6,
"title": "Update documentation with new configuration options",
"description": "Update the project documentation to clearly explain the new TASK_MASTER_PROJECT_ROOT environment variable, the 'projectRoot' configuration option, and the precedence rules. Include examples of different configuration scenarios.",
"status": "pending"
},
{
"id": 7,
"title": "Implement validation for project root directory",
"description": "Add validation to ensure the specified project root directory exists and has the necessary permissions. Provide clear error messages when validation fails.",
"status": "pending"
},
{
"id": 8,
"title": "Implement support for loading environment variables from .env files",
"description": "Add functionality to load the TASK_MASTER_PROJECT_ROOT variable from .env files in the workspace, following best practices for environment variable management in MCP servers.",
"status": "pending"
}
]
},
{
"id": 93,
"title": "Implement Google Vertex AI Provider Integration",
@@ -5613,75 +5677,11 @@
}
]
},
{
"id": 92,
"title": "Implement Project Root Environment Variable Support in MCP Configuration",
"description": "Add support for a 'TASK_MASTER_PROJECT_ROOT' environment variable in MCP configuration, allowing it to be set in both mcp.json and .env, with precedence over other methods. This will define the root directory for the MCP server and take precedence over all other project root resolution methods. The implementation should be backward compatible with existing workflows that don't use this variable.",
"status": "in-progress",
"dependencies": [
1,
3,
17
],
"priority": "medium",
"details": "Update the MCP server configuration system to support the TASK_MASTER_PROJECT_ROOT environment variable as the standard way to specify the project root directory. This provides better namespacing and avoids conflicts with other tools that might use a generic PROJECT_ROOT variable. Implement a clear precedence order for project root resolution:\n\n1. TASK_MASTER_PROJECT_ROOT environment variable (from shell or .env file)\n2. 'projectRoot' key in mcp_config.toml or mcp.json configuration files\n3. Existing resolution logic (CLI args, current working directory, etc.)\n\nModify the configuration loading logic to check for these sources in the specified order, ensuring backward compatibility. All MCP tools and components should use this standardized project root resolution logic. The TASK_MASTER_PROJECT_ROOT environment variable will be required because path resolution is delegated to the MCP client implementation, ensuring consistent behavior across different environments.\n\nImplementation steps:\n1. Identify all code locations where project root is determined (initialization, utility functions)\n2. Update configuration loaders to check for TASK_MASTER_PROJECT_ROOT in environment variables\n3. Add support for 'projectRoot' in configuration files as a fallback\n4. Refactor project root resolution logic to follow the new precedence rules\n5. Ensure all MCP tools and functions use the updated resolution logic\n6. Add comprehensive error handling for cases where TASK_MASTER_PROJECT_ROOT is not set or invalid\n7. Implement validation to ensure the specified directory exists and is accessible",
"testStrategy": "1. Write unit tests to verify that the config loader correctly reads project root from environment variables and configuration files with the expected precedence:\n - Test TASK_MASTER_PROJECT_ROOT environment variable takes precedence when set\n - Test 'projectRoot' in configuration files is used when environment variable is absent\n - Test fallback to existing resolution logic when neither is specified\n\n2. Add integration tests to ensure that the MCP server and all tools use the correct project root:\n - Test server startup with TASK_MASTER_PROJECT_ROOT set to various valid and invalid paths\n - Test configuration file loading from the specified project root\n - Test path resolution for resources relative to the project root\n\n3. Test backward compatibility:\n - Verify existing workflows function correctly without the new variables\n - Ensure no regression in projects not using the new configuration options\n\n4. Manual testing:\n - Set TASK_MASTER_PROJECT_ROOT in shell environment and verify correct behavior\n - Set TASK_MASTER_PROJECT_ROOT in .env file and verify it's properly loaded\n - Configure 'projectRoot' in configuration files and test precedence\n - Test with invalid or non-existent directories to verify error handling",
"subtasks": [
{
"id": 92.1,
"title": "Update configuration loader to check for TASK_MASTER_PROJECT_ROOT environment variable",
"description": "Modify the configuration loading system to check for the TASK_MASTER_PROJECT_ROOT environment variable as the primary source for project root directory. Ensure proper error handling if the variable is set but points to a non-existent or inaccessible directory.",
"status": "pending"
},
{
"id": 92.2,
"title": "Add support for 'projectRoot' in configuration files",
"description": "Implement support for a 'projectRoot' key in mcp_config.toml and mcp.json configuration files as a fallback when the environment variable is not set. Update the configuration parser to recognize and validate this field.",
"status": "pending"
},
{
"id": 92.3,
"title": "Refactor project root resolution logic with clear precedence rules",
"description": "Create a unified project root resolution function that follows the precedence order: 1) TASK_MASTER_PROJECT_ROOT environment variable, 2) 'projectRoot' in config files, 3) existing resolution methods. Ensure this function is used consistently throughout the codebase.",
"status": "pending"
},
{
"id": 92.4,
"title": "Update all MCP tools to use the new project root resolution",
"description": "Identify all MCP tools and components that need to access the project root and update them to use the new resolution logic. Ensure consistent behavior across all parts of the system.",
"status": "pending"
},
{
"id": 92.5,
"title": "Add comprehensive tests for the new project root resolution",
"description": "Create unit and integration tests to verify the correct behavior of the project root resolution logic under various configurations and edge cases.",
"status": "pending"
},
{
"id": 92.6,
"title": "Update documentation with new configuration options",
"description": "Update the project documentation to clearly explain the new TASK_MASTER_PROJECT_ROOT environment variable, the 'projectRoot' configuration option, and the precedence rules. Include examples of different configuration scenarios.",
"status": "pending"
},
{
"id": 92.7,
"title": "Implement validation for project root directory",
"description": "Add validation to ensure the specified project root directory exists and has the necessary permissions. Provide clear error messages when validation fails.",
"status": "pending"
},
{
"id": 92.8,
"title": "Implement support for loading environment variables from .env files",
"description": "Add functionality to load the TASK_MASTER_PROJECT_ROOT variable from .env files in the workspace, following best practices for environment variable management in MCP servers.",
"status": "pending"
}
]
},
{
"id": 95,
"title": "Implement .taskmaster Directory Structure",
"description": "Consolidate all Task Master-managed files in user projects into a clean, centralized .taskmaster/ directory structure to improve organization and keep user project directories clean, based on GitHub issue #275.",
"status": "in-progress",
"status": "done",
"dependencies": [
1,
3,
@@ -5732,7 +5732,7 @@
3
],
"details": "Implement path fallback logic that checks both old and new locations when files aren't found. Add deprecation warnings when old paths are used, informing users about the new structure. Ensure error messages are clear about the transition.",
"status": "in-progress",
"status": "done",
"testStrategy": "Test with both old and new directory structures to verify fallback works correctly. Verify deprecation warnings appear when using old paths."
},
{
@@ -5804,10 +5804,73 @@
9
],
"details": "Validate that after implementing the new structure, user project root directories only contain their actual project files plus the single .taskmaster/ directory. Verify that no Task Master files are created outside of .taskmaster/. Test that users can easily add .taskmaster/ to .gitignore if they choose to exclude Task Master files from version control.",
"status": "in-progress",
"status": "done",
"testStrategy": "Test complete workflows and verify only .taskmaster/ directory is created in project root. Check that all Task Master operations respect the new file organization. Verify .gitignore compatibility."
}
]
},
{
"id": 96,
"title": "Create Export Command for On-Demand Task File and PDF Generation",
"description": "Develop an 'export' CLI command that generates task files and comprehensive PDF exports on-demand, replacing automatic file generation and providing users with flexible export options.",
"details": "Implement a new 'export' command in the CLI that supports two primary modes: (1) generating individual task files on-demand (superseding the current automatic generation system), and (2) producing a comprehensive PDF export. The PDF should include: a first page with the output of 'tm list --with-subtasks', followed by individual pages for each task (using 'tm show <task_id>') and each subtask (using 'tm show <subtask_id>'). Integrate PDF generation using a robust library (e.g., pdfkit, Puppeteer, or jsPDF) to ensure high-quality output and proper pagination. Refactor or disable any existing automatic file generation logic to avoid performance overhead. Ensure the command supports flexible output paths and options for exporting only files, only PDF, or both. Update documentation and help output to reflect the new export capabilities. Consider concurrency and error handling for large projects. Ensure the export process is efficient and does not block the main CLI thread unnecessarily.",
"testStrategy": "1. Run the 'export' command with various options and verify that task files are generated only on-demand, not automatically. 2. Generate a PDF export and confirm that the first page contains the correct 'tm list --with-subtasks' output, and that each subsequent page accurately reflects the output of 'tm show <task_id>' and 'tm show <subtask_id>' for all tasks and subtasks. 3. Test exporting in projects with large numbers of tasks and subtasks to ensure performance and correctness. 4. Attempt exports with invalid paths or missing data to verify robust error handling. 5. Confirm that no automatic file generation occurs during normal task operations. 6. Review CLI help output and documentation for accuracy regarding the new export functionality.",
"status": "pending",
"dependencies": [
2,
4,
95
],
"priority": "medium",
"subtasks": [
{
"id": 1,
"title": "Remove Automatic Task File Generation from Task Operations",
"description": "Eliminate all calls to generateTaskFiles() from task operations such as add-task, remove-task, set-status, and similar commands to prevent unnecessary performance overhead.",
"dependencies": [],
"details": "Audit the codebase for any automatic invocations of generateTaskFiles() and remove or refactor them to ensure task files are not generated automatically during task operations.",
"status": "pending",
"testStrategy": "Verify that no task file generation occurs during any task operation by running the CLI and monitoring file system changes.",
"parentTaskId": 96
},
{
"id": 2,
"title": "Implement Export Command Infrastructure with On-Demand Task File Generation",
"description": "Develop the CLI 'export' command infrastructure, enabling users to generate task files on-demand by invoking the preserved generateTaskFiles function only when requested.",
"dependencies": [
1
],
"details": "Create the export command with options for output paths and modes (files, PDF, or both). Ensure generateTaskFiles is only called within this command and not elsewhere.",
"status": "pending",
"testStrategy": "Test the export command to confirm task files are generated only when explicitly requested and that output paths and options function as intended.",
"parentTaskId": 96
},
{
"id": 3,
"title": "Implement Comprehensive PDF Export Functionality",
"description": "Add PDF export capability to the export command, generating a structured PDF with a first page listing all tasks and subtasks, followed by individual pages for each task and subtask, using a robust PDF library.",
"dependencies": [
2
],
"details": "Integrate a PDF generation library (e.g., pdfkit, Puppeteer, or jsPDF). Ensure the PDF includes the output of 'tm list --with-subtasks' on the first page, and uses 'tm show <task_id>' and 'tm show <subtask_id>' for subsequent pages. Handle pagination, concurrency, and error handling for large projects.",
"status": "pending",
"testStrategy": "Generate PDFs for projects of varying sizes and verify layout, content accuracy, and performance. Test error handling and concurrency under load.",
"parentTaskId": 96
},
{
"id": 4,
"title": "Update Documentation, Tests, and CLI Help for Export Workflow",
"description": "Revise all relevant documentation, automated tests, and CLI help output to reflect the new export-based workflow and available options.",
"dependencies": [
2,
3
],
"details": "Update user guides, README files, and CLI help text. Add or modify tests to cover the new export command and its options. Ensure all documentation accurately describes the new workflow and usage.",
"status": "pending",
"testStrategy": "Review documentation for completeness and accuracy. Run all tests to ensure coverage of the new export command and verify CLI help output.",
"parentTaskId": 96
}
]
}
]
}

View File

@@ -1,5 +1,84 @@
# task-master-ai
## 0.16.2
### Patch Changes
- [#695](https://github.com/eyaltoledano/claude-task-master/pull/695) [`1ece6f1`](https://github.com/eyaltoledano/claude-task-master/commit/1ece6f19048df6ae2a0b25cbfb84d2c0f430642c) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - improve findTasks algorithm for resolving tasks path
- [#695](https://github.com/eyaltoledano/claude-task-master/pull/695) [`ee0be04`](https://github.com/eyaltoledano/claude-task-master/commit/ee0be04302cc602246de5cd296291db69bc8b300) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Fix update tool on MCP giving `No valid tasks found`
- [#699](https://github.com/eyaltoledano/claude-task-master/pull/699) [`27edbd8`](https://github.com/eyaltoledano/claude-task-master/commit/27edbd8f3fe5e2ac200b80e7f27f4c0e74a074d6) Thanks [@eyaltoledano](https://github.com/eyaltoledano)! - Enhanced add-task fuzzy search intelligence and improved user experience
**Smarter Task Discovery:**
- Remove hardcoded category system that always matched "Task management"
- Eliminate arbitrary limits on fuzzy search results (5→25 high relevance, 3→10 medium relevance, 8→20 detailed tasks)
- Improve semantic weighting in Fuse.js search (details=3, description=2, title=1.5) for better relevance
- Generate context-driven task recommendations based on true semantic similarity
**Enhanced Terminal Experience:**
- Fix duplicate banner display issue that was "eating" terminal history (closes #553)
- Remove console.clear() and redundant displayBanner() calls from UI functions
- Preserve command history for better development workflow
- Streamline banner display across all commands (list, next, show, set-status, clear-subtasks, dependency commands)
**Visual Improvements:**
- Replace emoji complexity indicators with clean filled circle characters (●) for professional appearance
- Improve consistency and readability of task complexity display
**AI Provider Compatibility:**
- Change generateObject mode from 'tool' to 'auto' for better cross-provider compatibility
- Add qwen3-235n-a22b:free model support (closes #687)
- Add smart warnings for free OpenRouter models with limitations (rate limits, restricted context, no tool_use)
**Technical Improvements:**
- Enhanced context generation in add-task to rely on semantic similarity rather than rigid pattern matching
- Improved dependency analysis and common pattern detection
- Better handling of task relationships and relevance scoring
- More intelligent task suggestion algorithms
The add-task system now provides truly relevant task context based on semantic understanding rather than arbitrary categories and limits, while maintaining a cleaner and more professional terminal experience.
- [#655](https://github.com/eyaltoledano/claude-task-master/pull/655) [`edaa5fe`](https://github.com/eyaltoledano/claude-task-master/commit/edaa5fe0d56e0e4e7c4370670a7a388eebd922ac) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Fix double .taskmaster directory paths in file resolution utilities
- Closes #636
- [#671](https://github.com/eyaltoledano/claude-task-master/pull/671) [`86ea6d1`](https://github.com/eyaltoledano/claude-task-master/commit/86ea6d1dbc03eeb39f524f565b50b7017b1d2c9c) Thanks [@joedanz](https://github.com/joedanz)! - Add one-click MCP server installation for Cursor
- [#699](https://github.com/eyaltoledano/claude-task-master/pull/699) [`2e55757`](https://github.com/eyaltoledano/claude-task-master/commit/2e55757b2698ba20b78f09ec0286951297510b8e) Thanks [@eyaltoledano](https://github.com/eyaltoledano)! - Add sync-readme command for a task export to GitHub README
Introduces a new `sync-readme` command that exports your task list to your project's README.md file.
**Features:**
- **Flexible filtering**: Supports `--status` filtering (e.g., pending, done) and `--with-subtasks` flag
- **Smart content management**: Automatically replaces existing exports or appends to new READMEs
- **Metadata display**: Shows export timestamp, subtask inclusion status, and filter settings
**Usage:**
- `task-master sync-readme` - Export tasks without subtasks
- `task-master sync-readme --with-subtasks` - Include subtasks in export
- `task-master sync-readme --status=pending` - Only export pending tasks
- `task-master sync-readme --status=done --with-subtasks` - Export completed tasks with subtasks
Perfect for showcasing project progress on GitHub. Experimental. Open to feedback.
## 0.16.2-rc.0
### Patch Changes
- [#655](https://github.com/eyaltoledano/claude-task-master/pull/655) [`edaa5fe`](https://github.com/eyaltoledano/claude-task-master/commit/edaa5fe0d56e0e4e7c4370670a7a388eebd922ac) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Fix double .taskmaster directory paths in file resolution utilities
- Closes #636
- [#671](https://github.com/eyaltoledano/claude-task-master/pull/671) [`86ea6d1`](https://github.com/eyaltoledano/claude-task-master/commit/86ea6d1dbc03eeb39f524f565b50b7017b1d2c9c) Thanks [@joedanz](https://github.com/joedanz)! - Add one-click MCP server installation for Cursor
## 0.16.1
### Patch Changes

View File

@@ -2,13 +2,33 @@
[![CI](https://github.com/eyaltoledano/claude-task-master/actions/workflows/ci.yml/badge.svg)](https://github.com/eyaltoledano/claude-task-master/actions/workflows/ci.yml) [![npm version](https://badge.fury.io/js/task-master-ai.svg)](https://badge.fury.io/js/task-master-ai) [![Discord](https://dcbadge.limes.pink/api/server/https://discord.gg/taskmasterai?style=flat)](https://discord.gg/taskmasterai) [![License: MIT with Commons Clause](https://img.shields.io/badge/license-MIT%20with%20Commons%20Clause-blue.svg)](LICENSE)
### By [@eyaltoledano](https://x.com/eyaltoledano) & [@RalphEcom](https://x.com/RalphEcom)
[![NPM Downloads](https://img.shields.io/npm/d18m/task-master-ai?style=flat)](https://www.npmjs.com/package/task-master-ai) [![NPM Downloads](https://img.shields.io/npm/dm/task-master-ai?style=flat)](https://www.npmjs.com/package/task-master-ai) [![NPM Downloads](https://img.shields.io/npm/dw/task-master-ai?style=flat)](https://www.npmjs.com/package/task-master-ai)
[![Twitter Follow](https://img.shields.io/twitter/follow/eyaltoledano?style=flat)](https://x.com/eyaltoledano)
[![Twitter Follow](https://img.shields.io/twitter/follow/RalphEcom?style=flat)](https://x.com/RalphEcom)
## By [@eyaltoledano](https://x.com/eyaltoledano), [@RalphEcom](https://x.com/RalphEcom) & [@jasonzhou1993](https://x.com/jasonzhou1993)
[![Twitter Follow](https://img.shields.io/twitter/follow/eyaltoledano)](https://x.com/eyaltoledano)
[![Twitter Follow](https://img.shields.io/twitter/follow/RalphEcom)](https://x.com/RalphEcom)
[![Twitter Follow](https://img.shields.io/twitter/follow/jasonzhou1993)](https://x.com/jasonzhou1993)
A task management system for AI-driven development with Claude, designed to work seamlessly with Cursor AI.
## Documentation
For more detailed information, check out the documentation in the `docs` directory:
- [Configuration Guide](docs/configuration.md) - Set up environment variables and customize Task Master
- [Tutorial](docs/tutorial.md) - Step-by-step guide to getting started with Task Master
- [Command Reference](docs/command-reference.md) - Complete list of all available commands
- [Task Structure](docs/task-structure.md) - Understanding the task format and features
- [Example Interactions](docs/examples.md) - Common Cursor AI interaction examples
- [Migration Guide](docs/migration-guide.md) - Guide to migrating to the new project structure
##### Quick Install for Cursor 1.0+ (One-Click)
[<img src="https://cursor.com/deeplink/mcp-install-dark.png" alt="Add Task Master MCP server to Cursor" style="max-height: 26px;">](cursor://anysphere.cursor-deeplink/mcp/install?name=taskmaster-ai&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIi0tcGFja2FnZT10YXNrLW1hc3Rlci1haSIsInRhc2stbWFzdGVyLWFpIl0sImVudiI6eyJBTlRIUk9QSUNfQVBJX0tFWSI6IllPVVJfQU5USFJPUElDX0FQSV9LRVlfSEVSRSIsIlBFUlBMRVhJVFlfQVBJX0tFWSI6IllPVVJfUEVSUExFWElUWV9BUElfS0VZX0hFUkUiLCJPUEVOQUlfQVBJX0tFWSI6IllPVVJfT1BFTkFJX0tFWV9IRVJFIiwiR09PR0xFX0FQSV9LRVkiOiJZT1VSX0dPT0dMRV9LRVlfSEVSRSIsIk1JU1RSQUxfQVBJX0tFWSI6IllPVVJfTUlTVFJBTF9LRVlfSEVSRSIsIk9QRU5ST1VURVJfQVBJX0tFWSI6IllPVVJfT1BFTlJPVVRFUl9LRVlfSEVSRSIsIlhBSV9BUElfS0VZIjoiWU9VUl9YQUlfS0VZX0hFUkUiLCJBWlVSRV9PUEVOQUJFX0FQSV9LRVkiOiJZT1VSX0FaVVJFX0tFWV9IRVJFIiwiT0xMQU1BX0FQSV9LRVkiOiJZT1VSX09MTEFNQV9BUElfS0VZX0hFUkUifX0%3D)
> **Note:** After clicking the install button, you'll still need to add your API keys to the configuration. The button installs the MCP server with placeholder keys that you'll need to replace with your actual API keys.
## Requirements
Taskmaster utilizes AI across several commands, and those require a separate API key. You can use a variety of models from different AI providers provided you add your API keys. For example, if you want to use Claude 3.7, you'll need an Anthropic API key.
@@ -41,17 +61,11 @@ MCP (Model Control Protocol) lets you run Task Master directly from your editor.
| **Windsurf** | Global | `~/.codeium/windsurf/mcp_config.json` | `%USERPROFILE%\.codeium\windsurf\mcp_config.json` | `mcpServers` |
| **VS Code** | Project | `<project_folder>/.vscode/mcp.json` | `<project_folder>\.vscode\mcp.json` | `servers` |
##### Quick Install for Cursor (One-Click)
[<img src="https://cursor.com/deeplink/mcp-install-dark.png" alt="Add Task Master MCP server to Cursor" style="max-height: 32px;">](cursor://anysphere.cursor-deeplink/mcp/install?name=taskmaster-ai&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIi0tcGFja2FnZT10YXNrLW1hc3Rlci1haSIsInRhc2stbWFzdGVyLWFpIl0sImVudiI6eyJBTlRIUk9QSUNfQVBJX0tFWSI6IllPVVJfQU5USFJPUElDX0FQSV9LRVlfSEVSRSIsIlBFUlBMRVhJVFlfQVBJX0tFWSI6IllPVVJfUEVSUExFWElUWV9BUElfS0VZX0hFUkUiLCJPUEVOQUlfQVBJX0tFWSI6IllPVVJfT1BFTkFJX0tFWV9IRVJFIiwiR09PR0xFX0FQSV9LRVkiOiJZT1VSX0dPT0dMRV9LRVlfSEVSRSIsIk1JU1RSQUxfQVBJX0tFWSI6IllPVVJfTUlTVFJBTF9LRVlfSEVSRSIsIk9QRU5ST1VURVJfQVBJX0tFWSI6IllPVVJfT1BFTlJPVVRFUl9LRVlfSEVSRSIsIlhBSV9BUElfS0VZIjoiWU9VUl9YQUlfS0VZX0hFUkUiLCJBWlVSRV9PUEVOQUJFX0FQSV9LRVkiOiJZT1VSX0FaVVJFX0tFWV9IRVJFIiwiT0xMQU1BX0FQSV9LRVkiOiJZT1VSX09MTEFNQV9BUElfS0VZX0hFUkUifX0%3D)
> **Note:** After clicking the install button, you'll still need to add your API keys to the configuration. The button installs the MCP server with placeholder keys that you'll need to replace with your actual API keys.
##### Manual Configuration
###### Cursor & Windsurf (`mcpServers`)
```jsonc
```json
{
"mcpServers": {
"taskmaster-ai": {
@@ -77,7 +91,7 @@ MCP (Model Control Protocol) lets you run Task Master directly from your editor.
###### VSCode (`servers` + `type`)
```jsonc
```json
{
"servers": {
"taskmaster-ai": {
@@ -191,20 +205,9 @@ task-master next
task-master generate
```
## Documentation
For more detailed information, check out the documentation in the `docs` directory:
- [Configuration Guide](docs/configuration.md) - Set up environment variables and customize Task Master
- [Tutorial](docs/tutorial.md) - Step-by-step guide to getting started with Task Master
- [Command Reference](docs/command-reference.md) - Complete list of all available commands
- [Task Structure](docs/task-structure.md) - Understanding the task format and features
- [Example Interactions](docs/examples.md) - Common Cursor AI interaction examples
- [Migration Guide](docs/migration-guide.md) - Guide to migrating to the new project structure
## Troubleshooting
### If `task-master init` doesn't respond:
### If `task-master init` doesn't respond
Try running it with Node directly:

View File

@@ -1,4 +1,4 @@
# Available Models as of May 27, 2025
# Available Models as of June 8, 2025
## Main Models
@@ -24,6 +24,7 @@
| google | gemini-2.5-flash-preview-04-17 | — | — | — |
| google | gemini-2.0-flash | 0.754 | 0.15 | 0.6 |
| google | gemini-2.0-flash-lite | — | — | — |
| perplexity | sonar-pro | — | 3 | 15 |
| perplexity | sonar-reasoning-pro | 0.211 | 2 | 8 |
| perplexity | sonar-reasoning | 0.211 | 1 | 5 |
| xai | grok-3 | — | 3 | 15 |
@@ -70,6 +71,8 @@
| perplexity | sonar-pro | — | 3 | 15 |
| perplexity | sonar | — | 1 | 1 |
| perplexity | deep-research | 0.211 | 2 | 8 |
| perplexity | sonar-reasoning-pro | 0.211 | 2 | 8 |
| perplexity | sonar-reasoning | 0.211 | 1 | 5 |
| xai | grok-3 | — | 3 | 15 |
| xai | grok-3-fast | — | 5 | 25 |

View File

@@ -21,7 +21,7 @@ import {
*/
export async function updateTasksDirect(args, log, context = {}) {
const { session } = context;
const { from, prompt, research, file: fileArg, projectRoot } = args;
const { from, prompt, research, tasksJsonPath, projectRoot } = args;
// Create the standard logger wrapper
const logWrapper = createLogWrapper(log);
@@ -60,20 +60,15 @@ export async function updateTasksDirect(args, log, context = {}) {
};
}
// Resolve tasks file path
const tasksFile = fileArg
? path.resolve(projectRoot, fileArg)
: path.resolve(projectRoot, 'tasks', 'tasks.json');
logWrapper.info(
`Updating tasks via direct function. From: ${from}, Research: ${research}, File: ${tasksFile}, ProjectRoot: ${projectRoot}`
`Updating tasks via direct function. From: ${from}, Research: ${research}, File: ${tasksJsonPath}, ProjectRoot: ${projectRoot}`
);
enableSilentMode(); // Enable silent mode
try {
// Call the core updateTasks function
const result = await updateTasks(
tasksFile,
tasksJsonPath,
from,
prompt,
research,
@@ -93,7 +88,7 @@ export async function updateTasksDirect(args, log, context = {}) {
success: true,
data: {
message: `Successfully updated ${result.updatedTasks.length} tasks.`,
tasksFile,
tasksPath: tasksJsonPath,
updatedCount: result.updatedTasks.length,
telemetryData: result.telemetryData
}

252
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "task-master-ai",
"version": "0.16.1",
"version": "0.16.2-rc.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "task-master-ai",
"version": "0.16.1",
"version": "0.16.2-rc.0",
"license": "MIT WITH Commons-Clause",
"dependencies": {
"@ai-sdk/amazon-bedrock": "^2.2.9",
@@ -29,7 +29,7 @@
"cors": "^2.8.5",
"dotenv": "^16.3.1",
"express": "^4.21.2",
"fastmcp": "^1.20.5",
"fastmcp": "^2.2.2",
"figlet": "^1.8.0",
"fuse.js": "^7.1.0",
"gradient-string": "^3.0.0",
@@ -3593,18 +3593,19 @@
}
},
"node_modules/@modelcontextprotocol/sdk": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.8.0.tgz",
"integrity": "sha512-e06W7SwrontJDHwCawNO5SGxG+nU9AAx+jpHHZqGl/WrDBdWOpvirC+s58VpJTB5QemI4jTRcjWT4Pt3Q1NPQQ==",
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.12.1.tgz",
"integrity": "sha512-KG1CZhZfWg+u8pxeM/mByJDScJSrjjxLc8fwQqbsS8xCjBmQfMNEBTotYdNanKekepnfRI85GtgQlctLFpcYPw==",
"license": "MIT",
"dependencies": {
"ajv": "^6.12.6",
"content-type": "^1.0.5",
"cors": "^2.8.5",
"cross-spawn": "^7.0.3",
"cross-spawn": "^7.0.5",
"eventsource": "^3.0.2",
"express": "^5.0.1",
"express-rate-limit": "^7.5.0",
"pkce-challenge": "^4.1.0",
"pkce-challenge": "^5.0.0",
"raw-body": "^3.0.0",
"zod": "^3.23.8",
"zod-to-json-schema": "^3.24.1"
@@ -3668,84 +3669,45 @@
}
},
"node_modules/@modelcontextprotocol/sdk/node_modules/express": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/express/-/express-5.0.1.tgz",
"integrity": "sha512-ORF7g6qGnD+YtUG9yx4DFoqCShNMmUKiXuT5oWMHiOvt/4WFbHC6yCwQMTSBMno7AqntNCAzzcnnjowRkTL9eQ==",
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
"integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
"license": "MIT",
"dependencies": {
"accepts": "^2.0.0",
"body-parser": "^2.0.1",
"body-parser": "^2.2.0",
"content-disposition": "^1.0.0",
"content-type": "~1.0.4",
"cookie": "0.7.1",
"content-type": "^1.0.5",
"cookie": "^0.7.1",
"cookie-signature": "^1.2.1",
"debug": "4.3.6",
"depd": "2.0.0",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "^2.0.0",
"fresh": "2.0.0",
"http-errors": "2.0.0",
"debug": "^4.4.0",
"encodeurl": "^2.0.0",
"escape-html": "^1.0.3",
"etag": "^1.8.1",
"finalhandler": "^2.1.0",
"fresh": "^2.0.0",
"http-errors": "^2.0.0",
"merge-descriptors": "^2.0.0",
"methods": "~1.1.2",
"mime-types": "^3.0.0",
"on-finished": "2.4.1",
"once": "1.4.0",
"parseurl": "~1.3.3",
"proxy-addr": "~2.0.7",
"qs": "6.13.0",
"range-parser": "~1.2.1",
"router": "^2.0.0",
"safe-buffer": "5.2.1",
"on-finished": "^2.4.1",
"once": "^1.4.0",
"parseurl": "^1.3.3",
"proxy-addr": "^2.0.7",
"qs": "^6.14.0",
"range-parser": "^1.2.1",
"router": "^2.2.0",
"send": "^1.1.0",
"serve-static": "^2.1.0",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"type-is": "^2.0.0",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
"serve-static": "^2.2.0",
"statuses": "^2.0.1",
"type-is": "^2.0.1",
"vary": "^1.1.2"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/@modelcontextprotocol/sdk/node_modules/express/node_modules/debug": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz",
"integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==",
"license": "MIT",
"dependencies": {
"ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/@modelcontextprotocol/sdk/node_modules/express/node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"license": "MIT"
},
"node_modules/@modelcontextprotocol/sdk/node_modules/express/node_modules/qs": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.0.6"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/@modelcontextprotocol/sdk/node_modules/finalhandler": {
@@ -4640,6 +4602,12 @@
"node": ">=18.0.0"
}
},
"node_modules/@standard-schema/spec": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz",
"integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==",
"license": "MIT"
},
"node_modules/@tokenizer/inflate": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz",
@@ -4884,6 +4852,22 @@
}
}
},
"node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/ansi-align": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
@@ -6432,9 +6416,9 @@
}
},
"node_modules/eventsource": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.6.tgz",
"integrity": "sha512-l19WpE2m9hSuyP06+FbuUUf1G+R0SFLrtQfbRb9PRr+oimOfxQhgGCbVaXg5IvZyyTThJsxh6L/srkMiCeBPDA==",
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz",
"integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==",
"license": "MIT",
"dependencies": {
"eventsource-parser": "^3.0.1"
@@ -6636,6 +6620,12 @@
"node": ">=4"
}
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"license": "MIT"
},
"node_modules/fast-glob": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
@@ -6657,7 +6647,6 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
"dev": true,
"license": "MIT"
},
"node_modules/fast-safe-stringify": {
@@ -6690,22 +6679,24 @@
}
},
"node_modules/fastmcp": {
"version": "1.20.5",
"resolved": "https://registry.npmjs.org/fastmcp/-/fastmcp-1.20.5.tgz",
"integrity": "sha512-jwcPgMF9bcE9qsEG82YMlAG26/n5CSYsr95e60ntqWWd+3kgTBbUIasB3HfpqHLTNaQuoX6/jl18fpDcybBjcQ==",
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/fastmcp/-/fastmcp-2.2.2.tgz",
"integrity": "sha512-V6qEfOnABo7lDrwHqZQhCYd52KXzK85/ipllmUyaos8WLAjygP9NuuKcm1kiEWa0jjsFxe2kf/Y+T4PRE+0rEw==",
"license": "MIT",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.6.0",
"@modelcontextprotocol/sdk": "^1.10.2",
"@standard-schema/spec": "^1.0.0",
"execa": "^9.5.2",
"file-type": "^20.3.0",
"file-type": "^20.4.1",
"fuse.js": "^7.1.0",
"mcp-proxy": "^2.10.4",
"mcp-proxy": "^3.0.3",
"strict-event-emitter-types": "^2.0.0",
"undici": "^7.4.0",
"undici": "^7.8.0",
"uri-templates": "^0.2.0",
"xsschema": "0.3.0-beta.1",
"yargs": "^17.7.2",
"zod": "^3.24.2",
"zod-to-json-schema": "^3.24.3"
"zod": "^3.25.12",
"zod-to-json-schema": "^3.24.5"
},
"bin": {
"fastmcp": "dist/bin/fastmcp.js"
@@ -9104,6 +9095,12 @@
"integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
"license": "(AFL-2.1 OR BSD-3-Clause)"
},
"node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"license": "MIT"
},
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
@@ -9383,19 +9380,31 @@
}
},
"node_modules/mcp-proxy": {
"version": "2.12.0",
"resolved": "https://registry.npmjs.org/mcp-proxy/-/mcp-proxy-2.12.0.tgz",
"integrity": "sha512-hL2Y6EtK7vkgAOZxOQe9M4Z9g5xEnvR4ZYBKqFi/5tjhz/1jyNEz5NL87Uzv46k8iZQPVNEof/T6arEooBU5bQ==",
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/mcp-proxy/-/mcp-proxy-3.3.0.tgz",
"integrity": "sha512-xyFKQEZ64HC7lxScBHjb5fxiPoyJjjkPhwH5hWUT0oL/ttCpMGZDJrYZRGFKVJiLLkrZPAkHnMGkI+WMlyD/cg==",
"license": "MIT",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.6.0",
"eventsource": "^3.0.5",
"@modelcontextprotocol/sdk": "^1.11.4",
"eventsource": "^4.0.0",
"yargs": "^17.7.2"
},
"bin": {
"mcp-proxy": "dist/bin/mcp-proxy.js"
}
},
"node_modules/mcp-proxy/node_modules/eventsource": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-4.0.0.tgz",
"integrity": "sha512-fvIkb9qZzdMxgZrEQDyll+9oJsyaVvY92I2Re+qK0qEJ+w5s0X3dtz+M0VAPOjP1gtU3iqWyjQ0G3nvd5CLZ2g==",
"license": "MIT",
"dependencies": {
"eventsource-parser": "^3.0.1"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@@ -10085,9 +10094,9 @@
}
},
"node_modules/pkce-challenge": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-4.1.0.tgz",
"integrity": "sha512-ZBmhE1C9LcPoH9XZSdwiPtbPHZROwAnMy+kIFQVrnMCxY4Cudlz3gBOpzilgc0jOgRaiT3sIWfpMomW2ar2orQ==",
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz",
"integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==",
"license": "MIT",
"engines": {
"node": ">=16.20.0"
@@ -11319,9 +11328,9 @@
}
},
"node_modules/undici": {
"version": "7.6.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-7.6.0.tgz",
"integrity": "sha512-gaFsbThjrDGvAaD670r81RZro/s6H2PVZF640Qn0p5kZK+/rim7/mmyfp2W7VB5vOMaFM8vuFBJUaMlaZTYHlA==",
"version": "7.10.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-7.10.0.tgz",
"integrity": "sha512-u5otvFBOBZvmdjWLVW+5DAc9Nkq8f24g0O9oY7qw2JVIF1VocIFoyz9JFkuVOS2j41AufeO0xnlweJ2RLT8nGw==",
"license": "MIT",
"engines": {
"node": ">=20.18.1"
@@ -11395,6 +11404,15 @@
"browserslist": ">= 4.21.0"
}
},
"node_modules/uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
"license": "BSD-2-Clause",
"dependencies": {
"punycode": "^2.1.0"
}
},
"node_modules/uri-templates": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/uri-templates/-/uri-templates-0.2.0.tgz",
@@ -11605,6 +11623,40 @@
}
}
},
"node_modules/xsschema": {
"version": "0.3.0-beta.1",
"resolved": "https://registry.npmjs.org/xsschema/-/xsschema-0.3.0-beta.1.tgz",
"integrity": "sha512-Z7ZlPKLTc8iUKVfic0Lr66NB777wJqZl3JVLIy1vaNxx6NNTuylYm4wbK78Sgg7kHwaPRqFnuT4IliQM1sDxvg==",
"license": "MIT",
"peerDependencies": {
"@valibot/to-json-schema": "^1.0.0",
"arktype": "^2.1.16",
"effect": "^3.14.5",
"sury": "^10.0.0-rc",
"zod": "^3.25.0",
"zod-to-json-schema": "^3.24.5"
},
"peerDependenciesMeta": {
"@valibot/to-json-schema": {
"optional": true
},
"arktype": {
"optional": true
},
"effect": {
"optional": true
},
"sury": {
"optional": true
},
"zod": {
"optional": true
},
"zod-to-json-schema": {
"optional": true
}
}
},
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
@@ -11734,9 +11786,9 @@
"license": "MIT"
},
"node_modules/zod": {
"version": "3.24.2",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz",
"integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==",
"version": "3.25.56",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.56.tgz",
"integrity": "sha512-rd6eEF3BTNvQnR2e2wwolfTmUTnp70aUTqr0oaGbHifzC3BKJsoV+Gat8vxUMR1hwOKBs6El+qWehrHbCpW6SQ==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"

View File

@@ -1,6 +1,6 @@
{
"name": "task-master-ai",
"version": "0.16.1",
"version": "0.16.2",
"description": "A task management system for ambitious AI-driven development that doesn't overwhelm and confuse Cursor.",
"main": "index.js",
"type": "module",
@@ -59,7 +59,7 @@
"cors": "^2.8.5",
"dotenv": "^16.3.1",
"express": "^4.21.2",
"fastmcp": "^1.20.5",
"fastmcp": "^2.2.2",
"figlet": "^1.8.0",
"fuse.js": "^7.1.0",
"gradient-string": "^3.0.0",

View File

@@ -382,25 +382,25 @@ The output report structure is:
```json
{
"meta": {
"generatedAt": "2023-06-15T12:34:56.789Z",
"tasksAnalyzed": 20,
"thresholdScore": 5,
"projectName": "Your Project Name",
"usedResearch": true
},
"complexityAnalysis": [
{
"taskId": 8,
"taskTitle": "Develop Implementation Drift Handling",
"complexityScore": 9.5,
"recommendedSubtasks": 6,
"expansionPrompt": "Create subtasks that handle detecting...",
"reasoning": "This task requires sophisticated logic...",
"expansionCommand": "node scripts/dev.js expand --id=8 --num=6 --prompt=\"Create subtasks...\" --research"
}
// More tasks sorted by complexity score (highest first)
]
"meta": {
"generatedAt": "2023-06-15T12:34:56.789Z",
"tasksAnalyzed": 20,
"thresholdScore": 5,
"projectName": "Your Project Name",
"usedResearch": true
},
"complexityAnalysis": [
{
"taskId": 8,
"taskTitle": "Develop Implementation Drift Handling",
"complexityScore": 9.5,
"recommendedSubtasks": 6,
"expansionPrompt": "Create subtasks that handle detecting...",
"reasoning": "This task requires sophisticated logic...",
"expansionCommand": "node scripts/dev.js expand --id=8 --num=6 --prompt=\"Create subtasks...\" --research"
}
// More tasks sorted by complexity score (highest first)
]
}
```

View File

@@ -577,7 +577,8 @@ async function _unifiedServiceRunner(serviceType, params) {
lowerCaseMessage.includes('does not support tool_use') ||
lowerCaseMessage.includes('tool use is not supported') ||
lowerCaseMessage.includes('tools are not supported') ||
lowerCaseMessage.includes('function calling is not supported')
lowerCaseMessage.includes('function calling is not supported') ||
lowerCaseMessage.includes('tool use is not supported')
) {
const specificErrorMsg = `Model '${modelId || 'unknown'}' via provider '${providerName || 'unknown'}' does not support the 'tool use' required by generateObjectService. Please configure a model that supports tool/function calling for the '${currentRole}' role, or use generateTextService if structured output is not strictly required.`;
log('error', `[Tool Support Error] ${specificErrorMsg}`);

View File

@@ -87,6 +87,7 @@ import {
TASK_STATUS_OPTIONS
} from '../../src/constants/task-status.js';
import { getTaskMasterVersion } from '../../src/utils/getVersion.js';
import { syncTasksToReadme } from './sync-readme.js';
/**
* Runs the interactive setup process for model configuration.
@@ -2757,6 +2758,54 @@ Examples:
}
});
// sync-readme command
programInstance
.command('sync-readme')
.description('Sync the current task list to README.md in the project root')
.option(
'-f, --file <file>',
'Path to the tasks file',
TASKMASTER_TASKS_FILE
)
.option('--with-subtasks', 'Include subtasks in the README output')
.option(
'-s, --status <status>',
'Show only tasks matching this status (e.g., pending, done)'
)
.action(async (options) => {
const tasksPath = options.file || TASKMASTER_TASKS_FILE;
const withSubtasks = options.withSubtasks || false;
const status = options.status || null;
// Find project root
const projectRoot = findProjectRoot();
if (!projectRoot) {
console.error(
chalk.red(
'Error: Could not find project root. Make sure you are in a Task Master project directory.'
)
);
process.exit(1);
}
console.log(
chalk.blue(
`📝 Syncing tasks to README.md${withSubtasks ? ' (with subtasks)' : ''}${status ? ` (status: ${status})` : ''}...`
)
);
const success = await syncTasksToReadme(projectRoot, {
withSubtasks,
status,
tasksPath
});
if (!success) {
console.error(chalk.red('❌ Failed to sync tasks to README.md'));
process.exit(1);
}
});
return programInstance;
}

View File

@@ -563,11 +563,6 @@ function cleanupSubtaskDependencies(tasksData) {
* @param {string} tasksPath - Path to tasks.json
*/
async function validateDependenciesCommand(tasksPath, options = {}) {
// Only display banner if not in silent mode
if (!isSilentMode()) {
displayBanner();
}
log('info', 'Checking for invalid dependencies in task files...');
// Read tasks data
@@ -691,11 +686,6 @@ function countAllDependencies(tasks) {
* @param {Object} options - Options object
*/
async function fixDependenciesCommand(tasksPath, options = {}) {
// Only display banner if not in silent mode
if (!isSilentMode()) {
displayBanner();
}
log('info', 'Checking for and fixing invalid dependencies in tasks.json...');
try {

View File

@@ -153,7 +153,7 @@
"id": "sonar-pro",
"swe_score": 0,
"cost_per_1m_tokens": { "input": 3, "output": 15 },
"allowed_roles": ["research"],
"allowed_roles": ["main", "research"],
"max_tokens": 8700
},
{
@@ -174,14 +174,14 @@
"id": "sonar-reasoning-pro",
"swe_score": 0.211,
"cost_per_1m_tokens": { "input": 2, "output": 8 },
"allowed_roles": ["main", "fallback"],
"allowed_roles": ["main", "research", "fallback"],
"max_tokens": 8700
},
{
"id": "sonar-reasoning",
"swe_score": 0.211,
"cost_per_1m_tokens": { "input": 1, "output": 5 },
"allowed_roles": ["main", "fallback"],
"allowed_roles": ["main", "research", "fallback"],
"max_tokens": 8700
}
],

View File

@@ -0,0 +1,184 @@
import fs from 'fs';
import path from 'path';
import chalk from 'chalk';
import { log, findProjectRoot } from './utils.js';
import { getProjectName } from './config-manager.js';
import listTasks from './task-manager/list-tasks.js';
/**
* Creates a basic README structure if one doesn't exist
* @param {string} projectName - Name of the project
* @returns {string} - Basic README content
*/
function createBasicReadme(projectName) {
return `# ${projectName}
This project is managed using Task Master.
`;
}
/**
* Create UTM tracking URL for task-master.dev
* @param {string} projectRoot - The project root path
* @returns {string} - UTM tracked URL
*/
function createTaskMasterUrl(projectRoot) {
// Get the actual folder name from the project root path
const folderName = path.basename(projectRoot);
// Clean folder name for UTM (replace spaces/special chars with hyphens)
const cleanFolderName = folderName
.toLowerCase()
.replace(/[^a-z0-9]/g, '-')
.replace(/-+/g, '-')
.replace(/^-|-$/g, '');
const utmParams = new URLSearchParams({
utm_source: 'github-readme',
utm_medium: 'readme-export',
utm_campaign: cleanFolderName || 'task-sync',
utm_content: 'task-export-link'
});
return `https://task-master.dev?${utmParams.toString()}`;
}
/**
* Create the start marker with metadata
* @param {Object} options - Export options
* @returns {string} - Formatted start marker
*/
function createStartMarker(options) {
const { timestamp, withSubtasks, status, projectRoot } = options;
// Format status filter text
const statusText = status
? `Status filter: ${status}`
: 'Status filter: none';
const subtasksText = withSubtasks ? 'with subtasks' : 'without subtasks';
// Create the export info content
const exportInfo =
`🎯 **Taskmaster Export** - ${timestamp}\n` +
`📋 Export: ${subtasksText}${statusText}\n` +
`🔗 Powered by [Task Master](${createTaskMasterUrl(projectRoot)})`;
// Create a markdown box using code blocks and emojis to mimic our UI style
const boxContent =
`<!-- TASKMASTER_EXPORT_START -->\n` +
`> ${exportInfo.split('\n').join('\n> ')}\n\n`;
return boxContent;
}
/**
* Create the end marker
* @returns {string} - Formatted end marker
*/
function createEndMarker() {
return (
`\n> 📋 **End of Taskmaster Export** - Tasks are synced from your project using the \`sync-readme\` command.\n` +
`<!-- TASKMASTER_EXPORT_END -->\n`
);
}
/**
* Syncs the current task list to README.md at the project root
* @param {string} projectRoot - Path to the project root directory
* @param {Object} options - Options for syncing
* @param {boolean} options.withSubtasks - Include subtasks in the output (default: false)
* @param {string} options.status - Filter by status (e.g., 'pending', 'done')
* @param {string} options.tasksPath - Custom path to tasks.json
* @returns {boolean} - True if sync was successful, false otherwise
*/
export async function syncTasksToReadme(projectRoot = null, options = {}) {
try {
const actualProjectRoot = projectRoot || findProjectRoot() || '.';
const { withSubtasks = false, status, tasksPath } = options;
// Get current tasks using the list-tasks functionality with markdown-readme format
const tasksOutput = await listTasks(
tasksPath ||
path.join(actualProjectRoot, '.taskmaster', 'tasks', 'tasks.json'),
status,
null,
withSubtasks,
'markdown-readme'
);
if (!tasksOutput) {
console.log(chalk.red('❌ Failed to generate task output'));
return false;
}
// Generate timestamp and metadata
const timestamp =
new Date().toISOString().replace('T', ' ').substring(0, 19) + ' UTC';
const projectName = getProjectName(actualProjectRoot);
// Create the export markers with metadata
const startMarker = createStartMarker({
timestamp,
withSubtasks,
status,
projectRoot: actualProjectRoot
});
const endMarker = createEndMarker();
// Create the complete task section
const taskSection = startMarker + tasksOutput + endMarker;
// Read current README content
const readmePath = path.join(actualProjectRoot, 'README.md');
let readmeContent = '';
try {
readmeContent = fs.readFileSync(readmePath, 'utf8');
} catch (err) {
if (err.code === 'ENOENT') {
// Create basic README if it doesn't exist
readmeContent = createBasicReadme(projectName);
} else {
throw err;
}
}
// Check if export markers exist and replace content between them
const startComment = '<!-- TASKMASTER_EXPORT_START -->';
const endComment = '<!-- TASKMASTER_EXPORT_END -->';
let updatedContent;
const startIndex = readmeContent.indexOf(startComment);
const endIndex = readmeContent.indexOf(endComment);
if (startIndex !== -1 && endIndex !== -1) {
// Replace existing task section
const beforeTasks = readmeContent.substring(0, startIndex);
const afterTasks = readmeContent.substring(endIndex + endComment.length);
updatedContent = beforeTasks + taskSection + afterTasks;
} else {
// Append to end of README
updatedContent = readmeContent + '\n' + taskSection;
}
// Write updated content to README
fs.writeFileSync(readmePath, updatedContent, 'utf8');
console.log(chalk.green('✅ Successfully synced tasks to README.md'));
console.log(
chalk.cyan(
`📋 Export details: ${withSubtasks ? 'with' : 'without'} subtasks${status ? `, status: ${status}` : ''}`
)
);
console.log(chalk.gray(`📍 Location: ${readmePath}`));
return true;
} catch (error) {
console.log(chalk.red('❌ Failed to sync tasks to README:'), error.message);
log('error', `README sync error: ${error.message}`);
return false;
}
}
export default syncTasksToReadme;

View File

@@ -10,6 +10,8 @@ import {
getStatusWithColor,
startLoadingIndicator,
stopLoadingIndicator,
succeedLoadingIndicator,
failLoadingIndicator,
displayAiUsageSummary
} from '../ui.js';
import { readJSON, writeJSON, log as consoleLog, truncate } from '../utils.js';
@@ -279,7 +281,7 @@ async function addTask(
// CLI-only feedback for the dependency analysis
if (outputFormat === 'text') {
console.log(
boxen(chalk.cyan.bold('Task Context Analysis') + '\n', {
boxen(chalk.cyan.bold('Task Context Analysis'), {
padding: { top: 0, bottom: 0, left: 1, right: 1 },
margin: { top: 0, bottom: 0 },
borderColor: 'cyan',
@@ -492,9 +494,9 @@ async function addTask(
includeScore: true, // Return match scores
threshold: 0.4, // Lower threshold = stricter matching (range 0-1)
keys: [
{ name: 'title', weight: 2 }, // Title is most important
{ name: 'description', weight: 1.5 }, // Description is next
{ name: 'details', weight: 0.8 }, // Details is less important
{ name: 'title', weight: 1.5 }, // Title is most important
{ name: 'description', weight: 2 }, // Description is very important
{ name: 'details', weight: 3 }, // Details is most important
// Search dependencies to find tasks that depend on similar things
{ name: 'dependencyTitles', weight: 0.5 }
],
@@ -502,8 +504,8 @@ async function addTask(
shouldSort: true,
// Allow searching in nested properties
useExtendedSearch: true,
// Return up to 15 matches
limit: 15
// Return up to 50 matches
limit: 50
};
// Prepare task data with dependencies expanded as titles for better semantic search
@@ -596,32 +598,6 @@ async function addTask(
// Get top N results for context
const relatedTasks = allRelevantTasks.slice(0, 8);
// Also look for tasks with similar purposes or categories
const purposeCategories = [
{ pattern: /(command|cli|flag)/i, label: 'CLI commands' },
{ pattern: /(task|subtask|add)/i, label: 'Task management' },
{ pattern: /(dependency|depend)/i, label: 'Dependency handling' },
{ pattern: /(AI|model|prompt)/i, label: 'AI integration' },
{ pattern: /(UI|display|show)/i, label: 'User interface' },
{ pattern: /(schedule|time|cron)/i, label: 'Scheduling' }, // Added scheduling category
{ pattern: /(config|setting|option)/i, label: 'Configuration' } // Added configuration category
];
promptCategory = purposeCategories.find((cat) =>
cat.pattern.test(prompt)
);
const categoryTasks = promptCategory
? data.tasks
.filter(
(t) =>
promptCategory.pattern.test(t.title) ||
promptCategory.pattern.test(t.description) ||
(t.details && promptCategory.pattern.test(t.details))
)
.filter((t) => !relatedTasks.some((rt) => rt.id === t.id))
.slice(0, 3)
: [];
// Format basic task overviews
if (relatedTasks.length > 0) {
contextTasks = `\nRelevant tasks identified by semantic similarity:\n${relatedTasks
@@ -632,12 +608,6 @@ async function addTask(
.join('\n')}`;
}
if (categoryTasks.length > 0) {
contextTasks += `\n\nTasks related to ${promptCategory.label}:\n${categoryTasks
.map((t) => `- Task ${t.id}: ${t.title} - ${t.description}`)
.join('\n')}`;
}
if (
recentTasks.length > 0 &&
!contextTasks.includes('Recently created tasks')
@@ -650,13 +620,10 @@ async function addTask(
}
// Add detailed information about the most relevant tasks
const allDetailedTasks = [
...relatedTasks.slice(0, 5),
...categoryTasks.slice(0, 2)
];
const allDetailedTasks = [...relatedTasks.slice(0, 25)];
uniqueDetailedTasks = Array.from(
new Map(allDetailedTasks.map((t) => [t.id, t])).values()
).slice(0, 8);
).slice(0, 20);
if (uniqueDetailedTasks.length > 0) {
contextTasks += `\n\nDetailed information about relevant tasks:`;
@@ -715,18 +682,14 @@ async function addTask(
}
// Additional analysis of common patterns
const similarPurposeTasks = promptCategory
? data.tasks.filter(
(t) =>
promptCategory.pattern.test(t.title) ||
promptCategory.pattern.test(t.description)
)
: [];
const similarPurposeTasks = data.tasks.filter((t) =>
prompt.toLowerCase().includes(t.title.toLowerCase())
);
let commonDeps = []; // Initialize commonDeps
if (similarPurposeTasks.length > 0) {
contextTasks += `\n\nCommon patterns for ${promptCategory ? promptCategory.label : 'similar'} tasks:`;
contextTasks += `\n\nCommon patterns for similar tasks:`;
// Collect dependencies from similar purpose tasks
const similarDeps = similarPurposeTasks
@@ -743,7 +706,7 @@ async function addTask(
// Get most common dependencies for similar tasks
commonDeps = Object.entries(depCounts)
.sort((a, b) => b[1] - a[1])
.slice(0, 5);
.slice(0, 10);
if (commonDeps.length > 0) {
contextTasks += '\nMost common dependencies for similar tasks:';
@@ -760,7 +723,7 @@ async function addTask(
if (outputFormat === 'text') {
console.log(
chalk.gray(
` Fuzzy search across ${data.tasks.length} tasks using full prompt and ${promptWords.length} keywords`
` Context search across ${data.tasks.length} tasks using full prompt and ${promptWords.length} keywords`
)
);
@@ -768,7 +731,7 @@ async function addTask(
console.log(
chalk.gray(`\n High relevance matches (score < 0.25):`)
);
highRelevance.slice(0, 5).forEach((t) => {
highRelevance.slice(0, 25).forEach((t) => {
console.log(
chalk.yellow(` • ⭐ Task ${t.id}: ${truncate(t.title, 50)}`)
);
@@ -779,24 +742,13 @@ async function addTask(
console.log(
chalk.gray(`\n Medium relevance matches (score < 0.4):`)
);
mediumRelevance.slice(0, 3).forEach((t) => {
mediumRelevance.slice(0, 10).forEach((t) => {
console.log(
chalk.green(` • Task ${t.id}: ${truncate(t.title, 50)}`)
);
});
}
if (promptCategory && categoryTasks.length > 0) {
console.log(
chalk.gray(`\n Tasks related to ${promptCategory.label}:`)
);
categoryTasks.forEach((t) => {
console.log(
chalk.magenta(` • Task ${t.id}: ${truncate(t.title, 50)}`)
);
});
}
// Show dependency patterns
if (commonDeps && commonDeps.length > 0) {
console.log(
@@ -864,10 +816,7 @@ async function addTask(
numericDependencies.length > 0
? dependentTasks.length // Use length of tasks from explicit dependency path
: uniqueDetailedTasks.length // Use length of tasks from fuzzy search path
)}` +
(promptCategory
? `\n${chalk.cyan('Category detected: ')}${chalk.yellow(promptCategory.label)}`
: ''),
)}`,
{
padding: { top: 0, bottom: 1, left: 1, right: 1 },
margin: { top: 1, bottom: 0 },
@@ -931,7 +880,7 @@ async function addTask(
// Start the loading indicator - only for text mode
if (outputFormat === 'text') {
loadingIndicator = startLoadingIndicator(
`Generating new task with ${useResearch ? 'Research' : 'Main'} AI...\n`
`Generating new task with ${useResearch ? 'Research' : 'Main'} AI... \n`
);
}
@@ -976,17 +925,33 @@ async function addTask(
}
report('Successfully generated task data from AI.', 'success');
// Success! Show checkmark
if (loadingIndicator) {
succeedLoadingIndicator(
loadingIndicator,
'Task generated successfully'
);
loadingIndicator = null; // Clear it
}
} catch (error) {
// Failure! Show X
if (loadingIndicator) {
failLoadingIndicator(loadingIndicator, 'AI generation failed');
loadingIndicator = null;
}
report(
`DEBUG: generateObjectService caught error: ${error.message}`,
'debug'
);
report(`Error generating task with AI: ${error.message}`, 'error');
if (loadingIndicator) stopLoadingIndicator(loadingIndicator);
throw error; // Re-throw error after logging
} finally {
report('DEBUG: generateObjectService finally block reached.', 'debug');
if (loadingIndicator) stopLoadingIndicator(loadingIndicator); // Ensure indicator stops
// Clean up if somehow still running
if (loadingIndicator) {
stopLoadingIndicator(loadingIndicator);
}
}
// --- End Refactored AI Interaction ---
}
@@ -1057,7 +1022,7 @@ async function addTask(
truncate(newTask.description, 47)
]);
console.log(chalk.green(' New task created successfully:'));
console.log(chalk.green(' New task created successfully:'));
console.log(table.toString());
// Helper to get priority color

View File

@@ -13,8 +13,6 @@ import generateTaskFiles from './generate-task-files.js';
* @param {string} taskIds - Task IDs to clear subtasks from
*/
function clearSubtasks(tasksPath, taskIds) {
displayBanner();
log('info', `Reading tasks from ${tasksPath}...`);
const data = readJSON(tasksPath);
if (!data || !data.tasks) {

View File

@@ -36,11 +36,6 @@ function listTasks(
outputFormat = 'text'
) {
try {
// Only display banner for text output
if (outputFormat === 'text') {
displayBanner();
}
const data = readJSON(tasksPath); // Reads the whole tasks.json
if (!data || !data.tasks) {
throw new Error(`No valid tasks found in ${tasksPath}`);
@@ -125,86 +120,7 @@ function listTasks(
const subtaskCompletionPercentage =
totalSubtasks > 0 ? (completedSubtasks / totalSubtasks) * 100 : 0;
// For JSON output, return structured data
if (outputFormat === 'json') {
// *** Modification: Remove 'details' field for JSON output ***
const tasksWithoutDetails = filteredTasks.map((task) => {
// <-- USES filteredTasks!
// Omit 'details' from the parent task
const { details, ...taskRest } = task;
// If subtasks exist, omit 'details' from them too
if (taskRest.subtasks && Array.isArray(taskRest.subtasks)) {
taskRest.subtasks = taskRest.subtasks.map((subtask) => {
const { details: subtaskDetails, ...subtaskRest } = subtask;
return subtaskRest;
});
}
return taskRest;
});
// *** End of Modification ***
return {
tasks: tasksWithoutDetails, // <--- THIS IS THE ARRAY BEING RETURNED
filter: statusFilter || 'all', // Return the actual filter used
stats: {
total: totalTasks,
completed: doneCount,
inProgress: inProgressCount,
pending: pendingCount,
blocked: blockedCount,
deferred: deferredCount,
cancelled: cancelledCount,
completionPercentage,
subtasks: {
total: totalSubtasks,
completed: completedSubtasks,
inProgress: inProgressSubtasks,
pending: pendingSubtasks,
blocked: blockedSubtasks,
deferred: deferredSubtasks,
cancelled: cancelledSubtasks,
completionPercentage: subtaskCompletionPercentage
}
}
};
}
// ... existing code for text output ...
// Calculate status breakdowns as percentages of total
const taskStatusBreakdown = {
'in-progress': totalTasks > 0 ? (inProgressCount / totalTasks) * 100 : 0,
pending: totalTasks > 0 ? (pendingCount / totalTasks) * 100 : 0,
blocked: totalTasks > 0 ? (blockedCount / totalTasks) * 100 : 0,
deferred: totalTasks > 0 ? (deferredCount / totalTasks) * 100 : 0,
cancelled: totalTasks > 0 ? (cancelledCount / totalTasks) * 100 : 0
};
const subtaskStatusBreakdown = {
'in-progress':
totalSubtasks > 0 ? (inProgressSubtasks / totalSubtasks) * 100 : 0,
pending: totalSubtasks > 0 ? (pendingSubtasks / totalSubtasks) * 100 : 0,
blocked: totalSubtasks > 0 ? (blockedSubtasks / totalSubtasks) * 100 : 0,
deferred:
totalSubtasks > 0 ? (deferredSubtasks / totalSubtasks) * 100 : 0,
cancelled:
totalSubtasks > 0 ? (cancelledSubtasks / totalSubtasks) * 100 : 0
};
// Create progress bars with status breakdowns
const taskProgressBar = createProgressBar(
completionPercentage,
30,
taskStatusBreakdown
);
const subtaskProgressBar = createProgressBar(
subtaskCompletionPercentage,
30,
subtaskStatusBreakdown
);
// Calculate dependency statistics
// Calculate dependency statistics (moved up to be available for all output formats)
const completedTaskIds = new Set(
data.tasks
.filter((t) => t.status === 'done' || t.status === 'completed')
@@ -276,6 +192,118 @@ function listTasks(
// Find next task to work on, passing the complexity report
const nextItem = findNextTask(data.tasks, complexityReport);
// For JSON output, return structured data
if (outputFormat === 'json') {
// *** Modification: Remove 'details' field for JSON output ***
const tasksWithoutDetails = filteredTasks.map((task) => {
// <-- USES filteredTasks!
// Omit 'details' from the parent task
const { details, ...taskRest } = task;
// If subtasks exist, omit 'details' from them too
if (taskRest.subtasks && Array.isArray(taskRest.subtasks)) {
taskRest.subtasks = taskRest.subtasks.map((subtask) => {
const { details: subtaskDetails, ...subtaskRest } = subtask;
return subtaskRest;
});
}
return taskRest;
});
// *** End of Modification ***
return {
tasks: tasksWithoutDetails, // <--- THIS IS THE ARRAY BEING RETURNED
filter: statusFilter || 'all', // Return the actual filter used
stats: {
total: totalTasks,
completed: doneCount,
inProgress: inProgressCount,
pending: pendingCount,
blocked: blockedCount,
deferred: deferredCount,
cancelled: cancelledCount,
completionPercentage,
subtasks: {
total: totalSubtasks,
completed: completedSubtasks,
inProgress: inProgressSubtasks,
pending: pendingSubtasks,
blocked: blockedSubtasks,
deferred: deferredSubtasks,
cancelled: cancelledSubtasks,
completionPercentage: subtaskCompletionPercentage
}
}
};
}
// For markdown-readme output, return formatted markdown
if (outputFormat === 'markdown-readme') {
return generateMarkdownOutput(data, filteredTasks, {
totalTasks,
completedTasks,
completionPercentage,
doneCount,
inProgressCount,
pendingCount,
blockedCount,
deferredCount,
cancelledCount,
totalSubtasks,
completedSubtasks,
subtaskCompletionPercentage,
inProgressSubtasks,
pendingSubtasks,
blockedSubtasks,
deferredSubtasks,
cancelledSubtasks,
tasksWithNoDeps,
tasksReadyToWork,
tasksWithUnsatisfiedDeps,
mostDependedOnTask,
mostDependedOnTaskId,
maxDependents,
avgDependenciesPerTask,
complexityReport,
withSubtasks,
nextItem
});
}
// ... existing code for text output ...
// Calculate status breakdowns as percentages of total
const taskStatusBreakdown = {
'in-progress': totalTasks > 0 ? (inProgressCount / totalTasks) * 100 : 0,
pending: totalTasks > 0 ? (pendingCount / totalTasks) * 100 : 0,
blocked: totalTasks > 0 ? (blockedCount / totalTasks) * 100 : 0,
deferred: totalTasks > 0 ? (deferredCount / totalTasks) * 100 : 0,
cancelled: totalTasks > 0 ? (cancelledCount / totalTasks) * 100 : 0
};
const subtaskStatusBreakdown = {
'in-progress':
totalSubtasks > 0 ? (inProgressSubtasks / totalSubtasks) * 100 : 0,
pending: totalSubtasks > 0 ? (pendingSubtasks / totalSubtasks) * 100 : 0,
blocked: totalSubtasks > 0 ? (blockedSubtasks / totalSubtasks) * 100 : 0,
deferred:
totalSubtasks > 0 ? (deferredSubtasks / totalSubtasks) * 100 : 0,
cancelled:
totalSubtasks > 0 ? (cancelledSubtasks / totalSubtasks) * 100 : 0
};
// Create progress bars with status breakdowns
const taskProgressBar = createProgressBar(
completionPercentage,
30,
taskStatusBreakdown
);
const subtaskProgressBar = createProgressBar(
subtaskCompletionPercentage,
30,
subtaskStatusBreakdown
);
// Get terminal width - more reliable method
let terminalWidth;
try {
@@ -764,4 +792,232 @@ function getWorkItemDescription(item, allTasks) {
}
}
/**
* Generate markdown-formatted output for README files
* @param {Object} data - Full tasks data
* @param {Array} filteredTasks - Filtered tasks array
* @param {Object} stats - Statistics object
* @returns {string} - Formatted markdown string
*/
function generateMarkdownOutput(data, filteredTasks, stats) {
const {
totalTasks,
completedTasks,
completionPercentage,
doneCount,
inProgressCount,
pendingCount,
blockedCount,
deferredCount,
cancelledCount,
totalSubtasks,
completedSubtasks,
subtaskCompletionPercentage,
inProgressSubtasks,
pendingSubtasks,
blockedSubtasks,
deferredSubtasks,
cancelledSubtasks,
tasksWithNoDeps,
tasksReadyToWork,
tasksWithUnsatisfiedDeps,
mostDependedOnTask,
mostDependedOnTaskId,
maxDependents,
avgDependenciesPerTask,
complexityReport,
withSubtasks,
nextItem
} = stats;
let markdown = '';
// Create progress bars for markdown (using Unicode block characters)
const createMarkdownProgressBar = (percentage, width = 20) => {
const filled = Math.round((percentage / 100) * width);
const empty = width - filled;
return '█'.repeat(filled) + '░'.repeat(empty);
};
// Dashboard section
markdown += '```\n';
markdown +=
'╭─────────────────────────────────────────────────────────╮╭─────────────────────────────────────────────────────────╮\n';
markdown +=
'│ ││ │\n';
markdown +=
'│ Project Dashboard ││ Dependency Status & Next Task │\n';
markdown += `│ Tasks Progress: ${createMarkdownProgressBar(completionPercentage, 20)} ${Math.round(completionPercentage)}% ││ Dependency Metrics: │\n`;
markdown += `${Math.round(completionPercentage)}% ││ • Tasks with no dependencies: ${tasksWithNoDeps}\n`;
markdown += `│ Done: ${doneCount} In Progress: ${inProgressCount} Pending: ${pendingCount} Blocked: ${blockedCount} ││ • Tasks ready to work on: ${tasksReadyToWork}\n`;
markdown += `│ Deferred: ${deferredCount} Cancelled: ${cancelledCount} ││ • Tasks blocked by dependencies: ${tasksWithUnsatisfiedDeps}\n`;
markdown += `│ ││ • Most depended-on task: #${mostDependedOnTaskId} (${maxDependents} dependents) │\n`;
markdown += `│ Subtasks Progress: ${createMarkdownProgressBar(subtaskCompletionPercentage, 20)} ││ • Avg dependencies per task: ${avgDependenciesPerTask.toFixed(1)}\n`;
markdown += `${Math.round(subtaskCompletionPercentage)}% ${Math.round(subtaskCompletionPercentage)}% ││ │\n`;
markdown += `│ Completed: ${completedSubtasks}/${totalSubtasks} In Progress: ${inProgressSubtasks} Pending: ${pendingSubtasks} ││ Next Task to Work On: │\n`;
const nextTaskTitle = nextItem
? nextItem.title.length > 40
? nextItem.title.substring(0, 37) + '...'
: nextItem.title
: 'No task available';
markdown += `│ Blocked: ${blockedSubtasks} Deferred: ${deferredSubtasks} Cancelled: ${cancelledSubtasks} ││ ID: ${nextItem ? nextItem.id : 'N/A'} - ${nextTaskTitle}\n`;
markdown += `│ ││ Priority: ${nextItem ? nextItem.priority || 'medium' : ''} Dependencies: ${nextItem && nextItem.dependencies && nextItem.dependencies.length > 0 ? 'Some' : 'None'}\n`;
markdown += `│ Priority Breakdown: ││ Complexity: ${nextItem && nextItem.complexityScore ? '● ' + nextItem.complexityScore : 'N/A'}\n`;
markdown += `│ • High priority: ${data.tasks.filter((t) => t.priority === 'high').length} │╰─────────────────────────────────────────────────────────╯\n`;
markdown += `│ • Medium priority: ${data.tasks.filter((t) => t.priority === 'medium').length}\n`;
markdown += `│ • Low priority: ${data.tasks.filter((t) => t.priority === 'low').length}\n`;
markdown += '│ │\n';
markdown += '╰─────────────────────────────────────────────────────────╯\n';
// Tasks table
markdown +=
'┌───────────┬──────────────────────────────────────┬─────────────────┬──────────────┬───────────────────────┬───────────┐\n';
markdown +=
'│ ID │ Title │ Status │ Priority │ Dependencies │ Complexi… │\n';
markdown +=
'├───────────┼──────────────────────────────────────┼─────────────────┼──────────────┼───────────────────────┼───────────┤\n';
// Helper function to format status with symbols
const getStatusSymbol = (status) => {
switch (status) {
case 'done':
case 'completed':
return '✓ done';
case 'in-progress':
return '► in-progress';
case 'pending':
return '○ pending';
case 'blocked':
return '⭕ blocked';
case 'deferred':
return 'x deferred';
case 'cancelled':
return 'x cancelled';
case 'review':
return '? review';
default:
return status || 'pending';
}
};
// Helper function to format dependencies without color codes
const formatDependenciesForMarkdown = (deps, allTasks) => {
if (!deps || deps.length === 0) return 'None';
return deps
.map((depId) => {
const depTask = allTasks.find((t) => t.id === depId);
return depTask ? depId.toString() : depId.toString();
})
.join(', ');
};
// Process all tasks
filteredTasks.forEach((task) => {
const taskTitle = task.title; // No truncation for README
const statusSymbol = getStatusSymbol(task.status);
const priority = task.priority || 'medium';
const deps = formatDependenciesForMarkdown(task.dependencies, data.tasks);
const complexity = task.complexityScore
? `${task.complexityScore}`
: 'N/A';
markdown += `${task.id.toString().padEnd(9)}${taskTitle.substring(0, 36).padEnd(36)}${statusSymbol.padEnd(15)}${priority.padEnd(12)}${deps.substring(0, 21).padEnd(21)}${complexity.padEnd(9)}\n`;
// Add subtasks if requested
if (withSubtasks && task.subtasks && task.subtasks.length > 0) {
task.subtasks.forEach((subtask) => {
const subtaskTitle = `└─ ${subtask.title}`; // No truncation
const subtaskStatus = getStatusSymbol(subtask.status);
const subtaskDeps = formatDependenciesForMarkdown(
subtask.dependencies,
data.tasks
);
const subtaskComplexity = subtask.complexityScore
? subtask.complexityScore.toString()
: 'N/A';
markdown +=
'├───────────┼──────────────────────────────────────┼─────────────────┼──────────────┼───────────────────────┼───────────┤\n';
markdown += `${task.id}.${subtask.id}${' '.padEnd(6)}${subtaskTitle.substring(0, 36).padEnd(36)}${subtaskStatus.padEnd(15)} │ - │ ${subtaskDeps.substring(0, 21).padEnd(21)}${subtaskComplexity.padEnd(9)}\n`;
});
}
markdown +=
'├───────────┼──────────────────────────────────────┼─────────────────┼──────────────┼───────────────────────┼───────────┤\n';
});
// Close the table
markdown = markdown.slice(
0,
-1 *
'├───────────┼──────────────────────────────────────┼─────────────────┼──────────────┼───────────────────────┼───────────┤\n'
.length
);
markdown +=
'└───────────┴──────────────────────────────────────┴─────────────────┴──────────────┴───────────────────────┴───────────┘\n';
markdown += '```\n\n';
// Next task recommendation
if (nextItem) {
markdown +=
'╭────────────────────────────────────────────── ⚡ RECOMMENDED NEXT TASK ⚡ ──────────────────────────────────────────────╮\n';
markdown +=
'│ │\n';
markdown += `│ 🔥 Next Task to Work On: #${nextItem.id} - ${nextItem.title}\n`;
markdown +=
'│ │\n';
markdown += `│ Priority: ${nextItem.priority || 'medium'} Status: ${getStatusSymbol(nextItem.status)}\n`;
markdown += `│ Dependencies: ${nextItem.dependencies && nextItem.dependencies.length > 0 ? formatDependenciesForMarkdown(nextItem.dependencies, data.tasks) : 'None'}\n`;
markdown +=
'│ │\n';
markdown += `│ Description: ${getWorkItemDescription(nextItem, data.tasks)}\n`;
markdown +=
'│ │\n';
// Add subtasks if they exist
const parentTask = data.tasks.find((t) => t.id === nextItem.id);
if (parentTask && parentTask.subtasks && parentTask.subtasks.length > 0) {
markdown +=
'│ Subtasks: │\n';
parentTask.subtasks.forEach((subtask) => {
markdown += `${nextItem.id}.${subtask.id} [${subtask.status || 'pending'}] ${subtask.title}\n`;
});
markdown +=
'│ │\n';
}
markdown += `│ Start working: task-master set-status --id=${nextItem.id} --status=in-progress │\n`;
markdown += `│ View details: task-master show ${nextItem.id}\n`;
markdown +=
'│ │\n';
markdown +=
'╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n\n';
}
// Suggested next steps
markdown += '\n';
markdown +=
'╭──────────────────────────────────────────────────────────────────────────────────────╮\n';
markdown +=
'│ │\n';
markdown +=
'│ Suggested Next Steps: │\n';
markdown +=
'│ │\n';
markdown +=
'│ 1. Run task-master next to see what to work on next │\n';
markdown +=
'│ 2. Run task-master expand --id=<id> to break down a task into subtasks │\n';
markdown +=
'│ 3. Run task-master set-status --id=<id> --status=done to mark a task as complete │\n';
markdown +=
'│ │\n';
markdown +=
'╰──────────────────────────────────────────────────────────────────────────────────────╯\n';
return markdown;
}
export default listTasks;

View File

@@ -450,7 +450,14 @@ async function setModel(role, modelId, options = {}) {
openRouterModels.some((m) => m.id === modelId)
) {
determinedProvider = 'openrouter';
warningMessage = `Warning: Custom OpenRouter model '${modelId}' set. This model is not officially validated by Taskmaster and may not function as expected.`;
// Check if this is a free model (ends with :free)
if (modelId.endsWith(':free')) {
warningMessage = `Warning: OpenRouter free model '${modelId}' selected. Free models have significant limitations including lower context windows, reduced rate limits, and may not support advanced features like tool_use. Consider using the paid version '${modelId.replace(':free', '')}' for full functionality.`;
} else {
warningMessage = `Warning: Custom OpenRouter model '${modelId}' set. This model is not officially validated by Taskmaster and may not function as expected.`;
}
report('warn', warningMessage);
} else {
// Hinted as OpenRouter but not found in live check

View File

@@ -33,8 +33,6 @@ async function setTaskStatus(tasksPath, taskIdInput, newStatus, options = {}) {
// Only display UI elements if not in MCP mode
if (!isMcpMode) {
displayBanner();
console.log(
boxen(chalk.white.bold(`Updating Task Status to: ${newStatus}`), {
padding: 1,

View File

@@ -40,7 +40,7 @@ const warmGradient = gradient(['#fb8b24', '#e36414', '#9a031e']);
function displayBanner() {
if (isSilentMode()) return;
console.clear();
// console.clear(); // Removing this to avoid clearing the terminal per command
const bannerText = figlet.textSync('Task Master', {
font: 'Standard',
horizontalLayout: 'default',
@@ -78,6 +78,8 @@ function displayBanner() {
* @returns {Object} Spinner object
*/
function startLoadingIndicator(message) {
if (isSilentMode()) return null;
const spinner = ora({
text: message,
color: 'cyan'
@@ -87,15 +89,75 @@ function startLoadingIndicator(message) {
}
/**
* Stop a loading indicator
* Stop a loading indicator (basic stop, no success/fail indicator)
* @param {Object} spinner - Spinner object to stop
*/
function stopLoadingIndicator(spinner) {
if (spinner && spinner.stop) {
if (spinner && typeof spinner.stop === 'function') {
spinner.stop();
}
}
/**
* Complete a loading indicator with success (shows checkmark)
* @param {Object} spinner - Spinner object to complete
* @param {string} message - Optional success message (defaults to current text)
*/
function succeedLoadingIndicator(spinner, message = null) {
if (spinner && typeof spinner.succeed === 'function') {
if (message) {
spinner.succeed(message);
} else {
spinner.succeed();
}
}
}
/**
* Complete a loading indicator with failure (shows X)
* @param {Object} spinner - Spinner object to fail
* @param {string} message - Optional failure message (defaults to current text)
*/
function failLoadingIndicator(spinner, message = null) {
if (spinner && typeof spinner.fail === 'function') {
if (message) {
spinner.fail(message);
} else {
spinner.fail();
}
}
}
/**
* Complete a loading indicator with warning (shows warning symbol)
* @param {Object} spinner - Spinner object to warn
* @param {string} message - Optional warning message (defaults to current text)
*/
function warnLoadingIndicator(spinner, message = null) {
if (spinner && typeof spinner.warn === 'function') {
if (message) {
spinner.warn(message);
} else {
spinner.warn();
}
}
}
/**
* Complete a loading indicator with info (shows info symbol)
* @param {Object} spinner - Spinner object to complete with info
* @param {string} message - Optional info message (defaults to current text)
*/
function infoLoadingIndicator(spinner, message = null) {
if (spinner && typeof spinner.info === 'function') {
if (message) {
spinner.info(message);
} else {
spinner.info();
}
}
}
/**
* Create a colored progress bar
* @param {number} percent - The completion percentage
@@ -232,14 +294,14 @@ function getStatusWithColor(status, forTable = false) {
}
const statusConfig = {
done: { color: chalk.green, icon: '', tableIcon: '✓' },
completed: { color: chalk.green, icon: '', tableIcon: '✓' },
pending: { color: chalk.yellow, icon: '⏱️', tableIcon: '⏱' },
done: { color: chalk.green, icon: '', tableIcon: '✓' },
completed: { color: chalk.green, icon: '', tableIcon: '✓' },
pending: { color: chalk.yellow, icon: '', tableIcon: '⏱' },
'in-progress': { color: chalk.hex('#FFA500'), icon: '🔄', tableIcon: '►' },
deferred: { color: chalk.gray, icon: '⏱️', tableIcon: '⏱' },
blocked: { color: chalk.red, icon: '', tableIcon: '✗' },
review: { color: chalk.magenta, icon: '👀', tableIcon: '👁' },
cancelled: { color: chalk.gray, icon: '❌', tableIcon: '' }
deferred: { color: chalk.gray, icon: 'x', tableIcon: '⏱' },
blocked: { color: chalk.red, icon: '!', tableIcon: '✗' },
review: { color: chalk.magenta, icon: '?', tableIcon: '?' },
cancelled: { color: chalk.gray, icon: '❌', tableIcon: 'x' }
};
const config = statusConfig[status.toLowerCase()] || {
@@ -383,8 +445,6 @@ function formatDependenciesWithStatus(
* Display a comprehensive help guide
*/
function displayHelp() {
displayBanner();
// Get terminal width - moved to top of function to make it available throughout
const terminalWidth = process.stdout.columns || 100; // Default to 100 if can't detect
@@ -465,6 +525,11 @@ function displayHelp() {
args: '--id=<id> --status=<status>',
desc: `Update task status (${TASK_STATUS_OPTIONS.join(', ')})`
},
{
name: 'sync-readme',
args: '[--with-subtasks] [--status=<status>]',
desc: 'Export tasks to README.md with professional formatting'
},
{
name: 'update',
args: '--from=<id> --prompt="<context>"',
@@ -745,9 +810,9 @@ function displayHelp() {
* @returns {string} Colored complexity score
*/
function getComplexityWithColor(score) {
if (score <= 3) return chalk.green(`🟢 ${score}`);
if (score <= 6) return chalk.yellow(`🟡 ${score}`);
return chalk.red(`🔴 ${score}`);
if (score <= 3) return chalk.green(` ${score}`);
if (score <= 6) return chalk.yellow(` ${score}`);
return chalk.red(` ${score}`);
}
/**
@@ -767,8 +832,6 @@ function truncateString(str, maxLength) {
* @param {string} tasksPath - Path to the tasks.json file
*/
async function displayNextTask(tasksPath, complexityReportPath = null) {
displayBanner();
// Read the tasks file
const data = readJSON(tasksPath);
if (!data || !data.tasks) {
@@ -1039,8 +1102,6 @@ async function displayTaskById(
complexityReportPath = null,
statusFilter = null
) {
displayBanner();
// Read the tasks file
const data = readJSON(tasksPath);
if (!data || !data.tasks) {
@@ -1495,8 +1556,6 @@ async function displayTaskById(
* @param {string} reportPath - Path to the complexity report file
*/
async function displayComplexityReport(reportPath) {
displayBanner();
// Check if the report exists
if (!fs.existsSync(reportPath)) {
console.log(
@@ -2093,5 +2152,9 @@ export {
displayApiKeyStatus,
displayModelConfiguration,
displayAvailableModels,
displayAiUsageSummary
displayAiUsageSummary,
succeedLoadingIndicator,
failLoadingIndicator,
warnLoadingIndicator,
infoLoadingIndicator
};

View File

@@ -189,7 +189,7 @@ export class BaseAIProvider {
model: client(params.modelId),
messages: params.messages,
schema: params.schema,
mode: 'tool',
mode: 'auto',
maxTokens: params.maxTokens,
temperature: params.temperature
});

View File

@@ -89,11 +89,22 @@ export function findTasksPath(explicitPath = null, args = null, log = null) {
// Use the passed logger if available, otherwise use the default logger
const logger = getLoggerOrDefault(log);
// 1. If explicit path is provided, use it (highest priority)
// 1. First determine project root to use as base for all path resolution
const rawProjectRoot = args?.projectRoot || findProjectRoot();
if (!rawProjectRoot) {
logger.warn?.('Could not determine project root directory');
return null;
}
// 2. Normalize project root to prevent double .taskmaster paths
const projectRoot = normalizeProjectRoot(rawProjectRoot);
// 3. If explicit path is provided, resolve it relative to project root (highest priority)
if (explicitPath) {
const resolvedPath = path.isAbsolute(explicitPath)
? explicitPath
: path.resolve(process.cwd(), explicitPath);
: path.resolve(projectRoot, explicitPath);
if (fs.existsSync(resolvedPath)) {
logger.info?.(`Using explicit tasks path: ${resolvedPath}`);
@@ -105,21 +116,9 @@ export function findTasksPath(explicitPath = null, args = null, log = null) {
}
}
// 2. Try to get project root from args (MCP) or find it
const rawProjectRoot = args?.projectRoot || findProjectRoot();
if (!rawProjectRoot) {
logger.warn?.('Could not determine project root directory');
return null;
}
// 3. Normalize project root to prevent double .taskmaster paths
const projectRoot = normalizeProjectRoot(rawProjectRoot);
// 4. Check possible locations in order of preference
const possiblePaths = [
path.join(projectRoot, TASKMASTER_TASKS_FILE), // .taskmaster/tasks/tasks.json (NEW)
path.join(projectRoot, 'tasks.json'), // tasks.json in root (LEGACY)
path.join(projectRoot, LEGACY_TASKS_FILE) // tasks/tasks.json (LEGACY)
];

View File

@@ -22,6 +22,10 @@ jest.unstable_mockModule('../../../../../scripts/modules/ui.js', () => ({
getStatusWithColor: jest.fn((status) => status),
startLoadingIndicator: jest.fn(),
stopLoadingIndicator: jest.fn(),
succeedLoadingIndicator: jest.fn(),
failLoadingIndicator: jest.fn(),
warnLoadingIndicator: jest.fn(),
infoLoadingIndicator: jest.fn(),
displayAiUsageSummary: jest.fn()
}));

View File

@@ -82,19 +82,19 @@ describe('UI Module', () => {
test('should return done status with emoji for console output', () => {
const result = getStatusWithColor('done');
expect(result).toMatch(/done/);
expect(result).toContain('');
expect(result).toContain('');
});
test('should return pending status with emoji for console output', () => {
const result = getStatusWithColor('pending');
expect(result).toMatch(/pending/);
expect(result).toContain('⏱️');
expect(result).toContain('');
});
test('should return deferred status with emoji for console output', () => {
const result = getStatusWithColor('deferred');
expect(result).toMatch(/deferred/);
expect(result).toContain('⏱️');
expect(result).toContain('x');
});
test('should return in-progress status with emoji for console output', () => {
@@ -222,25 +222,25 @@ describe('UI Module', () => {
test('should return high complexity in red', () => {
const result = getComplexityWithColor(8);
expect(result).toMatch(/8/);
expect(result).toContain('🔴');
expect(result).toContain('');
});
test('should return medium complexity in yellow', () => {
const result = getComplexityWithColor(5);
expect(result).toMatch(/5/);
expect(result).toContain('🟡');
expect(result).toContain('');
});
test('should return low complexity in green', () => {
const result = getComplexityWithColor(3);
expect(result).toMatch(/3/);
expect(result).toContain('🟢');
expect(result).toContain('');
});
test('should handle non-numeric inputs', () => {
const result = getComplexityWithColor('high');
expect(result).toMatch(/high/);
expect(result).toContain('🔴');
expect(result).toContain('');
});
});
});