Compare commits

..

3 Commits

Author SHA1 Message Date
Ralph Khreish
54d50a3d38 chore: format 2025-07-10 12:50:52 +03:00
Ralph Khreish
c3662dd62f chore: re-order supported models to something that makes more sense 2025-07-10 12:45:12 +03:00
Ralph Khreish
58991b3487 fix: models command not working 2025-07-10 12:44:58 +03:00
115 changed files with 548 additions and 9283 deletions

View File

@@ -1,9 +0,0 @@
---
"task-master-ai": minor
---
Add Kiro editor rule profile support
- Add support for Kiro IDE with custom rule files and MCP configuration
- Generate rule files in `.kiro/steering/` directory with markdown format
- Include MCP server configuration with enhanced file inclusion patterns

View File

@@ -0,0 +1,5 @@
---
"task-master-ai": patch
---
Recover from `@anthropic-ai/claude-code` JSON truncation bug that caused Task Master to crash when handling large (>8 kB) structured responses. The CLI/SDK still truncates, but Task Master now detects the error, preserves buffered text, and returns a usable response instead of throwing.

View File

@@ -1,12 +0,0 @@
---
"task-master-ai": patch
---
Prevent CLAUDE.md overwrite by using Claude Code's import feature
- Task Master now creates its instructions in `.taskmaster/CLAUDE.md` instead of overwriting the user's `CLAUDE.md`
- Adds an import section to the user's CLAUDE.md that references the Task Master instructions
- Preserves existing user content in CLAUDE.md files
- Provides clean uninstall that only removes Task Master's additions
**Breaking Change**: Task Master instructions for Claude Code are now stored in `.taskmaster/CLAUDE.md` and imported into the main CLAUDE.md file. Users who previously had Task Master content directly in their CLAUDE.md will need to run `task-master rules remove claude` followed by `task-master rules add claude` to migrate to the new structure.

View File

@@ -0,0 +1,5 @@
---
"task-master-ai": patch
---
Updating dependency ai-sdk-provider-gemini-cli to 0.0.4 to address breaking change Google made to Gemini CLI and add better 'api-key' in addition to 'gemini-api-key' AI-SDK compatibility.

View File

@@ -1,7 +0,0 @@
---
"task-master-ai": patch
---
Fix: show command no longer requires complexity report file to exist
The `tm show` command was incorrectly requiring the complexity report file to exist even when not needed. Now it only validates the complexity report path when a custom report file is explicitly provided via the -r/--report option.

View File

@@ -0,0 +1,9 @@
---
"task-master-ai": minor
---
Add support for xAI Grok 4 model
- Add grok-4 model to xAI provider with $3/$15 per 1M token pricing
- Enable main, fallback, and research roles for grok-4
- Max tokens set to 131,072 (matching other xAI models)

View File

@@ -1,10 +0,0 @@
---
"task-master-ai": minor
---
Complete Groq provider integration and add MoonshotAI Kimi K2 model support
- Fixed Groq provider registration
- Added Groq API key validation
- Added GROQ_API_KEY to .env.example
- Added moonshotai/kimi-k2-instruct model with $1/$3 per 1M token pricing and 16k max output

View File

@@ -1,7 +0,0 @@
---
"task-master-ai": minor
---
feat: Add Zed editor rule profile with agent rules and MCP config
- Resolves #637

View File

@@ -1,5 +0,0 @@
---
"task-master-ai": minor
---
Add Amp rule profile with AGENT.md and MCP config

View File

@@ -0,0 +1,8 @@
---
"task-master-ai": minor
---
Add stricter validation and clearer feedback for task priority when adding new tasks
- if a task priority is invalid, it will default to medium
- made taks priority case-insensitive, essentially making HIGH and high the same value

View File

@@ -0,0 +1,5 @@
---
"task-master-ai": minor
---
Add support for MCP Sampling as AI provider, requires no API key, uses the client LLM provider

View File

@@ -0,0 +1,5 @@
---
"task-master-ai": patch
---
Unify and streamline profile system architecture for improved maintainability

View File

@@ -1,5 +0,0 @@
---
"task-master-ai": patch
---
Add MCP configuration support to Claude Code rules

View File

@@ -1,7 +0,0 @@
---
"task-master-ai": patch
---
Fixed the comprehensive taskmaster system integration via custom slash commands with proper syntax
- Provide claude clode with a complete set of of commands that can trigger task master events directly within Claude Code

View File

@@ -0,0 +1,5 @@
---
"task-master-ai": minor
---
Added Groq provider support

View File

@@ -1,5 +0,0 @@
---
"task-master-ai": patch
---
Correct MCP server name and use 'Add to Cursor' button with updated placeholder keys.

View File

@@ -1,7 +0,0 @@
---
"task-master-ai": minor
---
Add OpenCode profile with AGENTS.md and MCP config
- Resolves #965

View File

@@ -1,5 +0,0 @@
---
"task-master-ai": patch
---
Add missing API keys to .env.example and README.md

View File

@@ -0,0 +1,130 @@
# Task Master Command Reference
Comprehensive command structure for Task Master integration with Claude Code.
## Command Organization
Commands are organized hierarchically to match Task Master's CLI structure while providing enhanced Claude Code integration.
## Project Setup & Configuration
### `/project:tm/init`
- `index` - Initialize new project (handles PRD files intelligently)
- `quick` - Quick setup with auto-confirmation (-y flag)
### `/project:tm/models`
- `index` - View current AI model configuration
- `setup` - Interactive model configuration
- `set-main` - Set primary generation model
- `set-research` - Set research model
- `set-fallback` - Set fallback model
## Task Generation
### `/project:tm/parse-prd`
- `index` - Generate tasks from PRD document
- `with-research` - Enhanced parsing with research mode
### `/project:tm/generate`
- Create individual task files from tasks.json
## Task Management
### `/project:tm/list`
- `index` - Smart listing with natural language filters
- `with-subtasks` - Include subtasks in hierarchical view
- `by-status` - Filter by specific status
### `/project:tm/set-status`
- `to-pending` - Reset task to pending
- `to-in-progress` - Start working on task
- `to-done` - Mark task complete
- `to-review` - Submit for review
- `to-deferred` - Defer task
- `to-cancelled` - Cancel task
### `/project:tm/sync-readme`
- Export tasks to README.md with formatting
### `/project:tm/update`
- `index` - Update tasks with natural language
- `from-id` - Update multiple tasks from a starting point
- `single` - Update specific task
### `/project:tm/add-task`
- `index` - Add new task with AI assistance
### `/project:tm/remove-task`
- `index` - Remove task with confirmation
## Subtask Management
### `/project:tm/add-subtask`
- `index` - Add new subtask to parent
- `from-task` - Convert existing task to subtask
### `/project:tm/remove-subtask`
- Remove subtask (with optional conversion)
### `/project:tm/clear-subtasks`
- `index` - Clear subtasks from specific task
- `all` - Clear all subtasks globally
## Task Analysis & Breakdown
### `/project:tm/analyze-complexity`
- Analyze and generate expansion recommendations
### `/project:tm/complexity-report`
- Display complexity analysis report
### `/project:tm/expand`
- `index` - Break down specific task
- `all` - Expand all eligible tasks
- `with-research` - Enhanced expansion
## Task Navigation
### `/project:tm/next`
- Intelligent next task recommendation
### `/project:tm/show`
- Display detailed task information
### `/project:tm/status`
- Comprehensive project dashboard
## Dependency Management
### `/project:tm/add-dependency`
- Add task dependency
### `/project:tm/remove-dependency`
- Remove task dependency
### `/project:tm/validate-dependencies`
- Check for dependency issues
### `/project:tm/fix-dependencies`
- Automatically fix dependency problems
## Usage Patterns
### Natural Language
Most commands accept natural language arguments:
```
/project:tm/add-task create user authentication system
/project:tm/update mark all API tasks as high priority
/project:tm/list show blocked tasks
```
### ID-Based Commands
Commands requiring IDs intelligently parse from $ARGUMENTS:
```
/project:tm/show 45
/project:tm/expand 23
/project:tm/set-status/to-done 67
```
### Smart Defaults
Commands provide intelligent defaults and suggestions based on context.

View File

@@ -1,146 +0,0 @@
# Task Master Command Reference
Comprehensive command structure for Task Master integration with Claude Code.
## Command Organization
Commands are organized hierarchically to match Task Master's CLI structure while providing enhanced Claude Code integration.
## Project Setup & Configuration
### `/project:tm/init`
- `init-project` - Initialize new project (handles PRD files intelligently)
- `init-project-quick` - Quick setup with auto-confirmation (-y flag)
### `/project:tm/models`
- `view-models` - View current AI model configuration
- `setup-models` - Interactive model configuration
- `set-main` - Set primary generation model
- `set-research` - Set research model
- `set-fallback` - Set fallback model
## Task Generation
### `/project:tm/parse-prd`
- `parse-prd` - Generate tasks from PRD document
- `parse-prd-with-research` - Enhanced parsing with research mode
### `/project:tm/generate`
- `generate-tasks` - Create individual task files from tasks.json
## Task Management
### `/project:tm/list`
- `list-tasks` - Smart listing with natural language filters
- `list-tasks-with-subtasks` - Include subtasks in hierarchical view
- `list-tasks-by-status` - Filter by specific status
### `/project:tm/set-status`
- `to-pending` - Reset task to pending
- `to-in-progress` - Start working on task
- `to-done` - Mark task complete
- `to-review` - Submit for review
- `to-deferred` - Defer task
- `to-cancelled` - Cancel task
### `/project:tm/sync-readme`
- `sync-readme` - Export tasks to README.md with formatting
### `/project:tm/update`
- `update-task` - Update tasks with natural language
- `update-tasks-from-id` - Update multiple tasks from a starting point
- `update-single-task` - Update specific task
### `/project:tm/add-task`
- `add-task` - Add new task with AI assistance
### `/project:tm/remove-task`
- `remove-task` - Remove task with confirmation
## Subtask Management
### `/project:tm/add-subtask`
- `add-subtask` - Add new subtask to parent
- `convert-task-to-subtask` - Convert existing task to subtask
### `/project:tm/remove-subtask`
- `remove-subtask` - Remove subtask (with optional conversion)
### `/project:tm/clear-subtasks`
- `clear-subtasks` - Clear subtasks from specific task
- `clear-all-subtasks` - Clear all subtasks globally
## Task Analysis & Breakdown
### `/project:tm/analyze-complexity`
- `analyze-complexity` - Analyze and generate expansion recommendations
### `/project:tm/complexity-report`
- `complexity-report` - Display complexity analysis report
### `/project:tm/expand`
- `expand-task` - Break down specific task
- `expand-all-tasks` - Expand all eligible tasks
- `with-research` - Enhanced expansion
## Task Navigation
### `/project:tm/next`
- `next-task` - Intelligent next task recommendation
### `/project:tm/show`
- `show-task` - Display detailed task information
### `/project:tm/status`
- `project-status` - Comprehensive project dashboard
## Dependency Management
### `/project:tm/add-dependency`
- `add-dependency` - Add task dependency
### `/project:tm/remove-dependency`
- `remove-dependency` - Remove task dependency
### `/project:tm/validate-dependencies`
- `validate-dependencies` - Check for dependency issues
### `/project:tm/fix-dependencies`
- `fix-dependencies` - Automatically fix dependency problems
## Workflows & Automation
### `/project:tm/workflows`
- `smart-workflow` - Context-aware intelligent workflow execution
- `command-pipeline` - Chain multiple commands together
- `auto-implement-tasks` - Advanced auto-implementation with code generation
## Utilities
### `/project:tm/utils`
- `analyze-project` - Deep project analysis and insights
### `/project:tm/setup`
- `install-taskmaster` - Comprehensive installation guide
- `quick-install-taskmaster` - One-line global installation
## Usage Patterns
### Natural Language
Most commands accept natural language arguments:
```
/project:tm/add-task create user authentication system
/project:tm/update mark all API tasks as high priority
/project:tm/list show blocked tasks
```
### ID-Based Commands
Commands requiring IDs intelligently parse from $ARGUMENTS:
```
/project:tm/show 45
/project:tm/expand 23
/project:tm/set-status/to-done 67
```
### Smart Defaults
Commands provide intelligent defaults and suggestions based on context.

View File

@@ -1,10 +0,0 @@
reviews:
profile: assertive
poem: false
auto_review:
base_branches:
- rc
- beta
- alpha
- production
- next

View File

@@ -8,7 +8,6 @@ GROQ_API_KEY=YOUR_GROQ_KEY_HERE
OPENROUTER_API_KEY=YOUR_OPENROUTER_KEY_HERE
XAI_API_KEY=YOUR_XAI_KEY_HERE
AZURE_OPENAI_API_KEY=YOUR_AZURE_KEY_HERE
OLLAMA_API_KEY=YOUR_OLLAMA_API_KEY_HERE
# Google Vertex AI Configuration
VERTEX_PROJECT_ID=your-gcp-project-id

View File

@@ -1,21 +1,21 @@
{
"models": {
"main": {
"provider": "anthropic",
"modelId": "claude-3-7-sonnet-20250219",
"maxTokens": 120000,
"provider": "groq",
"modelId": "llama-3.1-8b-instant",
"maxTokens": 131072,
"temperature": 0.2
},
"research": {
"provider": "perplexity",
"modelId": "sonar",
"maxTokens": 8700,
"provider": "groq",
"modelId": "llama-3.3-70b-versatile",
"maxTokens": 32768,
"temperature": 0.1
},
"fallback": {
"provider": "anthropic",
"modelId": "claude-3-5-sonnet-20241022",
"maxTokens": 8192,
"modelId": "claude-3-7-sonnet-20250219",
"maxTokens": 128000,
"temperature": 0.2
}
},

View File

@@ -0,0 +1,23 @@
# Task ID: 1
# Title: Implement TTS Flag for Taskmaster Commands
# Status: pending
# Dependencies: 16 (Not found)
# Priority: medium
# Description: Add text-to-speech functionality to taskmaster commands with configurable voice options and audio output settings.
# Details:
Implement TTS functionality including:
- Add --tts flag to all relevant taskmaster commands (list, show, generate, etc.)
- Integrate with system TTS engines (Windows SAPI, macOS say command, Linux espeak/festival)
- Create TTS configuration options in the configuration management system
- Add voice selection options (male/female, different languages if available)
- Implement audio output settings (volume, speed, pitch)
- Add TTS-specific error handling for cases where TTS is unavailable
- Create fallback behavior when TTS fails (silent failure or text output)
- Support for reading task titles, descriptions, and status updates aloud
- Add option to read entire task lists or individual task details
- Implement TTS for command confirmations and error messages
- Create TTS output formatting to make spoken text more natural (removing markdown, formatting numbers/dates appropriately)
- Add configuration option to enable/disable TTS globally
# Test Strategy:
Test TTS functionality across different operating systems (Windows, macOS, Linux). Verify that the --tts flag works with all major commands. Test voice configuration options and ensure audio output settings are properly applied. Test error handling when TTS services are unavailable. Verify that text formatting for speech is natural and understandable. Test with various task content types including special characters, code snippets, and long descriptions. Ensure TTS can be disabled and enabled through configuration.

File diff suppressed because one or more lines are too long

View File

@@ -1,55 +1,5 @@
# task-master-ai
## 0.20.0
### Minor Changes
- [#950](https://github.com/eyaltoledano/claude-task-master/pull/950) [`699e9ee`](https://github.com/eyaltoledano/claude-task-master/commit/699e9eefb5d687b256e9402d686bdd5e3a358b4a) Thanks [@ben-vargas](https://github.com/ben-vargas)! - Add support for xAI Grok 4 model
- Add grok-4 model to xAI provider with $3/$15 per 1M token pricing
- Enable main, fallback, and research roles for grok-4
- Max tokens set to 131,072 (matching other xAI models)
- [#946](https://github.com/eyaltoledano/claude-task-master/pull/946) [`5f009a5`](https://github.com/eyaltoledano/claude-task-master/commit/5f009a5e1fc10e37be26f5135df4b7f44a9c5320) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Add stricter validation and clearer feedback for task priority when adding new tasks
- if a task priority is invalid, it will default to medium
- made taks priority case-insensitive, essentially making HIGH and high the same value
- [#863](https://github.com/eyaltoledano/claude-task-master/pull/863) [`b530657`](https://github.com/eyaltoledano/claude-task-master/commit/b53065713c8da0ae6f18eb2655397aa975004923) Thanks [@OrenMe](https://github.com/OrenMe)! - Add support for MCP Sampling as AI provider, requires no API key, uses the client LLM provider
- [#930](https://github.com/eyaltoledano/claude-task-master/pull/930) [`98d1c97`](https://github.com/eyaltoledano/claude-task-master/commit/98d1c974361a56ddbeb772b1272986b9d3913459) Thanks [@OmarElKadri](https://github.com/OmarElKadri)! - Added Groq provider support
### Patch Changes
- [#958](https://github.com/eyaltoledano/claude-task-master/pull/958) [`6c88a4a`](https://github.com/eyaltoledano/claude-task-master/commit/6c88a4a749083e3bd2d073a9240799771774495a) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Recover from `@anthropic-ai/claude-code` JSON truncation bug that caused Task Master to crash when handling large (>8 kB) structured responses. The CLI/SDK still truncates, but Task Master now detects the error, preserves buffered text, and returns a usable response instead of throwing.
- [#958](https://github.com/eyaltoledano/claude-task-master/pull/958) [`3334e40`](https://github.com/eyaltoledano/claude-task-master/commit/3334e409ae659d5223bb136ae23fd22c5e219073) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Updating dependency ai-sdk-provider-gemini-cli to 0.0.4 to address breaking change Google made to Gemini CLI and add better 'api-key' in addition to 'gemini-api-key' AI-SDK compatibility.
- [#853](https://github.com/eyaltoledano/claude-task-master/pull/853) [`95c299d`](https://github.com/eyaltoledano/claude-task-master/commit/95c299df642bd8e6d75f8fa5110ac705bcc72edf) Thanks [@joedanz](https://github.com/joedanz)! - Unify and streamline profile system architecture for improved maintainability
## 0.20.0-rc.0
### Minor Changes
- [#950](https://github.com/eyaltoledano/claude-task-master/pull/950) [`699e9ee`](https://github.com/eyaltoledano/claude-task-master/commit/699e9eefb5d687b256e9402d686bdd5e3a358b4a) Thanks [@ben-vargas](https://github.com/ben-vargas)! - Add support for xAI Grok 4 model
- Add grok-4 model to xAI provider with $3/$15 per 1M token pricing
- Enable main, fallback, and research roles for grok-4
- Max tokens set to 131,072 (matching other xAI models)
- [#946](https://github.com/eyaltoledano/claude-task-master/pull/946) [`5f009a5`](https://github.com/eyaltoledano/claude-task-master/commit/5f009a5e1fc10e37be26f5135df4b7f44a9c5320) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Add stricter validation and clearer feedback for task priority when adding new tasks
- if a task priority is invalid, it will default to medium
- made taks priority case-insensitive, essentially making HIGH and high the same value
- [#863](https://github.com/eyaltoledano/claude-task-master/pull/863) [`b530657`](https://github.com/eyaltoledano/claude-task-master/commit/b53065713c8da0ae6f18eb2655397aa975004923) Thanks [@OrenMe](https://github.com/OrenMe)! - Add support for MCP Sampling as AI provider, requires no API key, uses the client LLM provider
- [#930](https://github.com/eyaltoledano/claude-task-master/pull/930) [`98d1c97`](https://github.com/eyaltoledano/claude-task-master/commit/98d1c974361a56ddbeb772b1272986b9d3913459) Thanks [@OmarElKadri](https://github.com/OmarElKadri)! - Added Groq provider support
### Patch Changes
- [#916](https://github.com/eyaltoledano/claude-task-master/pull/916) [`6c88a4a`](https://github.com/eyaltoledano/claude-task-master/commit/6c88a4a749083e3bd2d073a9240799771774495a) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Recover from `@anthropic-ai/claude-code` JSON truncation bug that caused Task Master to crash when handling large (>8 kB) structured responses. The CLI/SDK still truncates, but Task Master now detects the error, preserves buffered text, and returns a usable response instead of throwing.
- [#916](https://github.com/eyaltoledano/claude-task-master/pull/916) [`3334e40`](https://github.com/eyaltoledano/claude-task-master/commit/3334e409ae659d5223bb136ae23fd22c5e219073) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Updating dependency ai-sdk-provider-gemini-cli to 0.0.4 to address breaking change Google made to Gemini CLI and add better 'api-key' in addition to 'gemini-api-key' AI-SDK compatibility.
- [#853](https://github.com/eyaltoledano/claude-task-master/pull/853) [`95c299d`](https://github.com/eyaltoledano/claude-task-master/commit/95c299df642bd8e6d75f8fa5110ac705bcc72edf) Thanks [@joedanz](https://github.com/joedanz)! - Unify and streamline profile system architecture for improved maintainability
## 0.19.0
### Minor Changes

View File

@@ -25,7 +25,11 @@ For more detailed information, check out the documentation in the `docs` directo
#### Quick Install for Cursor 1.0+ (One-Click)
[![Add task-master-ai MCP server to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/install-mcp?name=task-master-ai&config=eyJjb21tYW5kIjoibnB4IC15IC0tcGFja2FnZT10YXNrLW1hc3Rlci1haSB0YXNrLW1hc3Rlci1haSIsImVudiI6eyJBTlRIUk9QSUNfQVBJX0tFWSI6IllPVVJfQU5USFJPUElDX0FQSV9LRVlfSEVSRSIsIlBFUlBMRVhJVFlfQVBJX0tFWSI6IllPVVJfUEVSUExFWElUWV9BUElfS0VZX0hFUkUiLCJPUEVOQUlfQVBJX0tFWSI6IllPVVJfT1BFTkFJX0tFWV9IRVJFIiwiR09PR0xFX0FQSV9LRVkiOiJZT1VSX0dPT0dMRV9LRVlfSEVSRSIsIk1JU1RSQUxfQVBJX0tFWSI6IllPVVJfTUlTVFJBTF9LRVlfSEVSRSIsIkdST1FfQVBJX0tFWSI6IllPVVJfR1JPUV9LRVlfSEVSRSIsIk9QRU5ST1VURVJfQVBJX0tFWSI6IllPVVJfT1BFTlJPVVRFUl9LRVlfSEVSRSIsIlhBSV9BUElfS0VZIjoiWU9VUl9YQUlfS0VZX0hFUkUiLCJBWlVSRV9PUEVOQUlfQVBJX0tFWSI6IllPVVJfQVpVUkVfS0VZX0hFUkUiLCJPTExBTUFfQVBJX0tFWSI6IllPVVJfT0xMQU1BX0FQSV9LRVlfSEVSRSJ9fQ%3D%3D)
📋 Click the copy button (top-right of code block) then paste into your browser:
```text
cursor://anysphere.cursor-deeplink/mcp/install?name=taskmaster-ai&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIi0tcGFja2FnZT10YXNrLW1hc3Rlci1haSIsInRhc2stbWFzdGVyLWFpIl0sImVudiI6eyJBTlRIUk9QSUNfQVBJX0tFWSI6IllPVVJfQU5USFJPUElDX0FQSV9LRVlfSEVSRSIsIlBFUlBMRVhJVFlfQVBJX0tFWSI6IllPVVJfUEVSUExFWElUWV9BUElfS0VZX0hFUkUiLCJPUEVOQUlfQVBJX0tFWSI6IllPVVJfT1BFTkFJX0tFWV9IRVJFIiwiR09PR0xFX0FQSV9LRVkiOiJZT1VSX0dPT0dMRV9LRVlfSEVSRSIsIk1JU1RSQUxfQVBJX0tFWSI6IllPVVJfTUlTVFJBTF9LRVlfSEVSRSIsIk9QRU5ST1VURVJfQVBJX0tFWSI6IllPVVJfT1BFTlJPVVRFUl9LRVlfSEVSRSIsIlhBSV9BUElfS0VZIjoiWU9VUl9YQUlfS0VZX0hFUkUiLCJBWlVSRV9PUEVOQUlfQVBJX0tFWSI6IllPVVJfQVpVUkVfS0VZX0hFUkUiLCJPTExBTUFfQVBJX0tFWSI6IllPVVJfT0xMQU1BX0FQSV9LRVlfSEVSRSJ9fQo=
```
> **Note:** After clicking the link, you'll still need to add your API keys to the configuration. The link installs the MCP server with placeholder keys that you'll need to replace with your actual API keys.
@@ -69,7 +73,7 @@ MCP (Model Control Protocol) lets you run Task Master directly from your editor.
```json
{
"mcpServers": {
"task-master-ai": {
"taskmaster-ai": {
"command": "npx",
"args": ["-y", "--package=task-master-ai", "task-master-ai"],
"env": {
@@ -78,7 +82,6 @@ MCP (Model Control Protocol) lets you run Task Master directly from your editor.
"OPENAI_API_KEY": "YOUR_OPENAI_KEY_HERE",
"GOOGLE_API_KEY": "YOUR_GOOGLE_KEY_HERE",
"MISTRAL_API_KEY": "YOUR_MISTRAL_KEY_HERE",
"GROQ_API_KEY": "YOUR_GROQ_KEY_HERE",
"OPENROUTER_API_KEY": "YOUR_OPENROUTER_KEY_HERE",
"XAI_API_KEY": "YOUR_XAI_KEY_HERE",
"AZURE_OPENAI_API_KEY": "YOUR_AZURE_KEY_HERE",
@@ -98,7 +101,7 @@ MCP (Model Control Protocol) lets you run Task Master directly from your editor.
```json
{
"servers": {
"task-master-ai": {
"taskmaster-ai": {
"command": "npx",
"args": ["-y", "--package=task-master-ai", "task-master-ai"],
"env": {
@@ -107,11 +110,9 @@ MCP (Model Control Protocol) lets you run Task Master directly from your editor.
"OPENAI_API_KEY": "YOUR_OPENAI_KEY_HERE",
"GOOGLE_API_KEY": "YOUR_GOOGLE_KEY_HERE",
"MISTRAL_API_KEY": "YOUR_MISTRAL_KEY_HERE",
"GROQ_API_KEY": "YOUR_GROQ_KEY_HERE",
"OPENROUTER_API_KEY": "YOUR_OPENROUTER_KEY_HERE",
"XAI_API_KEY": "YOUR_XAI_KEY_HERE",
"AZURE_OPENAI_API_KEY": "YOUR_AZURE_KEY_HERE",
"OLLAMA_API_KEY": "YOUR_OLLAMA_API_KEY_HERE"
"AZURE_OPENAI_API_KEY": "YOUR_AZURE_KEY_HERE"
},
"type": "stdio"
}

View File

@@ -1,15 +0,0 @@
{
"name": "extension",
"version": "0.20.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"devDependencies": {
"typescript": "^5.8.3"
}
}

View File

@@ -1 +0,0 @@
console.log('hello world');

View File

@@ -1,113 +0,0 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */
/* Projects */
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "libReplacement": true, /* Enable lib replacement. */
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "commonjs" /* Specify what module code is generated. */,
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
// "rewriteRelativeImportExtensions": true, /* Rewrite '.ts', '.tsx', '.mts', and '.cts' file extensions in relative import paths to their JavaScript equivalent in output files. */
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
// "noUncheckedSideEffectImports": true, /* Check side effect imports. */
// "resolveJsonModule": true, /* Enable importing .json files. */
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
// "outDir": "./", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
// "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */
// "erasableSyntaxOnly": true, /* Do not allow runtime constructs that are not part of ECMAScript. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
/* Type Checking */
"strict": true /* Enable all strict type-checking options. */,
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}

View File

@@ -1,4 +1,4 @@
# Task Master AI - Agent Integration Guide
# Task Master AI - Claude Code Integration Guide
## Essential Commands

View File

@@ -1,12 +1,10 @@
# API Keys (Required to enable respective provider)
ANTHROPIC_API_KEY="your_anthropic_api_key_here" # Required: Format: sk-ant-api03-...
PERPLEXITY_API_KEY="your_perplexity_api_key_here" # Optional: Format: pplx-...
OPENAI_API_KEY="your_openai_api_key_here" # Optional, for OpenAI models. Format: sk-proj-...
OPENAI_API_KEY="your_openai_api_key_here" # Optional, for OpenAI/OpenRouter models. Format: sk-proj-...
GOOGLE_API_KEY="your_google_api_key_here" # Optional, for Google Gemini models.
MISTRAL_API_KEY="your_mistral_key_here" # Optional, for Mistral AI models.
XAI_API_KEY="YOUR_XAI_KEY_HERE" # Optional, for xAI AI models.
GROQ_API_KEY="YOUR_GROQ_KEY_HERE" # Optional, for Groq models.
OPENROUTER_API_KEY="YOUR_OPENROUTER_KEY_HERE" # Optional, for OpenRouter models.
AZURE_OPENAI_API_KEY="your_azure_key_here" # Optional, for Azure OpenAI models (requires endpoint in .taskmaster/config.json).
OLLAMA_API_KEY="your_ollama_api_key_here" # Optional: For remote Ollama servers that require authentication.
GITHUB_API_KEY="your_github_api_key_here" # Optional: For GitHub import/export features. Format: ghp_... or github_pat_...

View File

@@ -4,7 +4,30 @@ Taskmaster uses two primary methods for configuration:
1. **`.taskmaster/config.json` File (Recommended - New Structure)**
- This JSON file stores most configuration settings, including AI model selections, parameters, logging levels, and project defaults.
- This JSON file stores most configuration settings, including A5. **Usage Requirements**:
8. **Troubleshooting**:
- "MCP provider requires session context" → Ensure running in MCP environment
- See the [MCP Provider Guide](./mcp-provider-guide.md) for detailed troubleshootingust be running in an MCP context (session must be available)
- Session must provide `clientCapabilities.sampling` capability
6. **Best Practices**:
- Always configure a non-MCP fallback provider
- Use `mcp` for main/research roles when in MCP environments
- Test sampling capability before production use
7. **Setup Commands**:
```bash
# Set MCP provider for main role
task-master models set-main --provider mcp --model claude-3-5-sonnet-20241022
# Set MCP provider for research role
task-master models set-research --provider mcp --model claude-3-opus-20240229
# Verify configuration
task-master models list
```
8. **Troubleshooting**:lections, parameters, logging levels, and project defaults.
- **Location:** This file is created in the `.taskmaster/` directory when you run the `task-master models --setup` interactive setup or initialize a new project with `task-master init`.
- **Migration:** Existing projects with `.taskmasterconfig` in the root will continue to work, but should be migrated to the new structure using `task-master migrate`.
- **Management:** Use the `task-master models --setup` command (or `models` MCP tool) to interactively create and manage this file. You can also set specific models directly using `task-master models --set-<role>=<model_id>`, adding `--ollama` or `--openrouter` flags for custom models. Manual editing is possible but not recommended unless you understand the structure.
@@ -45,12 +68,11 @@ Taskmaster uses two primary methods for configuration:
"azureBaseURL": "https://your-endpoint.azure.com/openai/deployments",
"vertexProjectId": "your-gcp-project-id",
"vertexLocation": "us-central1",
"responseLanguage": "English"
"responseLanguage": "English"
}
}
```
> For MCP-specific setup and troubleshooting, see [Provider-Specific Configuration](#provider-specific-configuration).
2. **Legacy `.taskmasterconfig` File (Backward Compatibility)**
@@ -176,6 +198,8 @@ node scripts/init.js
### MCP (Model Context Protocol) Provider
The MCP provider enables Task Master to use MCP servers as AI providers. This is particularly useful when running Task Master within MCP-compatible development environments like Claude Desktop or Cursor.
1. **Prerequisites**:
- An active MCP session with sampling capability
- MCP client with sampling support (e.g. VS Code)
@@ -214,24 +238,12 @@ node scripts/init.js
- Must be running in an MCP context (session must be available)
- Session must provide `clientCapabilities.sampling` capability
6. **Best Practices**:
5. **Best Practices**:
- Always configure a non-MCP fallback provider
- Use `mcp` for main/research roles when in MCP environments
- Test sampling capability before production use
7. **Setup Commands**:
```bash
# Set MCP provider for main role
task-master models set-main --provider mcp --model claude-3-5-sonnet-20241022
# Set MCP provider for research role
task-master models set-research --provider mcp --model claude-3-opus-20240229
# Verify configuration
task-master models list
```
8. **Troubleshooting**:
6. **Troubleshooting**:
- "MCP provider requires session context" → Ensure running in MCP environment
- See the [MCP Provider Guide](./mcp-provider-guide.md) for detailed troubleshooting

View File

@@ -1,18 +1,24 @@
# Available Models as of July 16, 2025
# Available Models as of July 10, 2025
## Main Models
| Provider | Model Name | SWE Score | Input Cost | Output Cost |
| ----------- | ---------------------------------------------- | --------- | ---------- | ----------- |
| bedrock | us.anthropic.claude-3-haiku-20240307-v1:0 | 0.4 | 0.25 | 1.25 |
| bedrock | us.anthropic.claude-3-opus-20240229-v1:0 | 0.725 | 15 | 75 |
| bedrock | us.anthropic.claude-3-5-sonnet-20240620-v1:0 | 0.49 | 3 | 15 |
| bedrock | us.anthropic.claude-3-5-sonnet-20241022-v2:0 | 0.49 | 3 | 15 |
| bedrock | us.anthropic.claude-3-7-sonnet-20250219-v1:0 | 0.623 | 3 | 15 |
| bedrock | us.anthropic.claude-3-5-haiku-20241022-v1:0 | 0.4 | 0.8 | 4 |
| bedrock | us.anthropic.claude-opus-4-20250514-v1:0 | 0.725 | 15 | 75 |
| bedrock | us.anthropic.claude-sonnet-4-20250514-v1:0 | 0.727 | 3 | 15 |
| anthropic | claude-sonnet-4-20250514 | 0.727 | 3 | 15 |
| anthropic | claude-opus-4-20250514 | 0.725 | 15 | 75 |
| anthropic | claude-3-7-sonnet-20250219 | 0.623 | 3 | 15 |
| anthropic | claude-3-5-sonnet-20241022 | 0.49 | 3 | 15 |
| claude-code | opus | 0.725 | 0 | 0 |
| claude-code | sonnet | 0.727 | 0 | 0 |
| mcp | mcp-sampling | — | 0 | 0 |
| gemini-cli | gemini-2.5-pro | 0.72 | 0 | 0 |
| gemini-cli | gemini-2.5-flash | 0.71 | 0 | 0 |
| azure | gpt-4o | 0.332 | 2.5 | 10 |
| azure | gpt-4o-mini | 0.3 | 0.15 | 0.6 |
| azure | gpt-4-1 | — | 2 | 10 |
| openai | gpt-4o | 0.332 | 2.5 | 10 |
| openai | o1 | 0.489 | 15 | 60 |
| openai | o3 | 0.5 | 2 | 8 |
@@ -29,22 +35,19 @@
| google | gemini-2.5-flash-preview-04-17 | 0.604 | — | — |
| google | gemini-2.0-flash | 0.518 | 0.15 | 0.6 |
| google | gemini-2.0-flash-lite | — | — | — |
| xai | grok-3 | — | 3 | 15 |
| xai | grok-3-fast | — | 5 | 25 |
| xai | grok-4 | — | 3 | 15 |
| groq | moonshotai/kimi-k2-instruct | 0.66 | 1 | 3 |
| groq | llama-3.3-70b-versatile | 0.55 | 0.59 | 0.79 |
| groq | llama-3.1-8b-instant | 0.32 | 0.05 | 0.08 |
| groq | llama-4-scout | 0.45 | 0.11 | 0.34 |
| groq | llama-4-maverick | 0.52 | 0.5 | 0.77 |
| groq | mixtral-8x7b-32768 | 0.35 | 0.24 | 0.24 |
| groq | qwen-qwq-32b-preview | 0.4 | 0.18 | 0.18 |
| groq | deepseek-r1-distill-llama-70b | 0.52 | 0.75 | 0.99 |
| groq | gemma2-9b-it | 0.3 | 0.2 | 0.2 |
| groq | whisper-large-v3 | — | 0.11 | 0 |
| 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 |
| xai | grok-3-fast | — | 5 | 25 |
| xai | grok-4 | — | 3 | 15 |
| ollama | devstral:latest | — | 0 | 0 |
| ollama | qwen3:latest | — | 0 | 0 |
| ollama | qwen3:14b | — | 0 | 0 |
| ollama | qwen3:32b | — | 0 | 0 |
| ollama | mistral-small3.1:latest | — | 0 | 0 |
| ollama | llama3.3:latest | — | 0 | 0 |
| ollama | phi4:latest | — | 0 | 0 |
| openrouter | google/gemini-2.5-flash-preview-05-20 | — | 0.15 | 0.6 |
| openrouter | google/gemini-2.5-flash-preview-05-20:thinking | — | 0.15 | 3.5 |
| openrouter | google/gemini-2.5-pro-exp-03-25 | — | 0 | 0 |
@@ -70,36 +73,39 @@
| openrouter | mistralai/devstral-small | — | 0.1 | 0.3 |
| openrouter | mistralai/mistral-nemo | — | 0.03 | 0.07 |
| openrouter | thudm/glm-4-32b:free | — | 0 | 0 |
| ollama | devstral:latest | | 0 | 0 |
| ollama | qwen3:latest | | 0 | 0 |
| ollama | qwen3:14b | | 0 | 0 |
| ollama | qwen3:32b | | 0 | 0 |
| ollama | mistral-small3.1:latest | | 0 | 0 |
| ollama | llama3.3:latest | | 0 | 0 |
| ollama | phi4:latest | | 0 | 0 |
| azure | gpt-4o | 0.332 | 2.5 | 10 |
| azure | gpt-4o-mini | 0.3 | 0.15 | 0.6 |
| azure | gpt-4-1 | | 2 | 10 |
| bedrock | us.anthropic.claude-3-haiku-20240307-v1:0 | 0.4 | 0.25 | 1.25 |
| bedrock | us.anthropic.claude-3-opus-20240229-v1:0 | 0.725 | 15 | 75 |
| bedrock | us.anthropic.claude-3-5-sonnet-20240620-v1:0 | 0.49 | 3 | 15 |
| bedrock | us.anthropic.claude-3-5-sonnet-20241022-v2:0 | 0.49 | 3 | 15 |
| bedrock | us.anthropic.claude-3-7-sonnet-20250219-v1:0 | 0.623 | 3 | 15 |
| bedrock | us.anthropic.claude-3-5-haiku-20241022-v1:0 | 0.4 | 0.8 | 4 |
| bedrock | us.anthropic.claude-opus-4-20250514-v1:0 | 0.725 | 15 | 75 |
| bedrock | us.anthropic.claude-sonnet-4-20250514-v1:0 | 0.727 | 3 | 15 |
| groq | llama-3.3-70b-versatile | 0.55 | 0.59 | 0.79 |
| groq | llama-3.1-8b-instant | 0.32 | 0.05 | 0.08 |
| groq | llama-4-scout | 0.45 | 0.11 | 0.34 |
| groq | llama-4-maverick | 0.52 | 0.5 | 0.77 |
| groq | mixtral-8x7b-32768 | 0.35 | 0.24 | 0.24 |
| groq | qwen-qwq-32b-preview | 0.4 | 0.18 | 0.18 |
| groq | deepseek-r1-distill-llama-70b | 0.52 | 0.75 | 0.99 |
| groq | gemma2-9b-it | 0.3 | 0.2 | 0.2 |
| groq | whisper-large-v3 | — | 0.11 | 0 |
| claude-code | opus | 0.725 | 0 | 0 |
| claude-code | sonnet | 0.727 | 0 | 0 |
| mcp | mcp-sampling | | 0 | 0 |
| gemini-cli | gemini-2.5-pro | 0.72 | 0 | 0 |
| gemini-cli | gemini-2.5-flash | 0.71 | 0 | 0 |
## Research Models
| Provider | Model Name | SWE Score | Input Cost | Output Cost |
| ----------- | -------------------------------------------- | --------- | ---------- | ----------- |
| claude-code | opus | 0.725 | 0 | 0 |
| claude-code | sonnet | 0.727 | 0 | 0 |
| mcp | mcp-sampling | — | 0 | 0 |
| gemini-cli | gemini-2.5-pro | 0.72 | 0 | 0 |
| gemini-cli | gemini-2.5-flash | 0.71 | 0 | 0 |
| bedrock | us.anthropic.claude-3-opus-20240229-v1:0 | 0.725 | 15 | 75 |
| bedrock | us.anthropic.claude-3-5-sonnet-20240620-v1:0 | 0.49 | 3 | 15 |
| bedrock | us.anthropic.claude-3-5-sonnet-20241022-v2:0 | 0.49 | 3 | 15 |
| bedrock | us.anthropic.claude-3-7-sonnet-20250219-v1:0 | 0.623 | 3 | 15 |
| bedrock | us.anthropic.claude-opus-4-20250514-v1:0 | 0.725 | 15 | 75 |
| bedrock | us.anthropic.claude-sonnet-4-20250514-v1:0 | 0.727 | 3 | 15 |
| bedrock | us.deepseek.r1-v1:0 | — | 1.35 | 5.4 |
| openai | gpt-4o-search-preview | 0.33 | 2.5 | 10 |
| openai | gpt-4o-mini-search-preview | 0.3 | 0.15 | 0.6 |
| 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 |
| xai | grok-4 | — | 3 | 15 |
@@ -108,32 +114,31 @@
| groq | llama-4-maverick | 0.52 | 0.5 | 0.77 |
| groq | qwen-qwq-32b-preview | 0.4 | 0.18 | 0.18 |
| groq | deepseek-r1-distill-llama-70b | 0.52 | 0.75 | 0.99 |
| 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 |
| bedrock | us.anthropic.claude-3-opus-20240229-v1:0 | 0.725 | 15 | 75 |
| bedrock | us.anthropic.claude-3-5-sonnet-20240620-v1:0 | 0.49 | 3 | 15 |
| bedrock | us.anthropic.claude-3-5-sonnet-20241022-v2:0 | 0.49 | 3 | 15 |
| bedrock | us.anthropic.claude-3-7-sonnet-20250219-v1:0 | 0.623 | 3 | 15 |
| bedrock | us.anthropic.claude-opus-4-20250514-v1:0 | 0.725 | 15 | 75 |
| bedrock | us.anthropic.claude-sonnet-4-20250514-v1:0 | 0.727 | 3 | 15 |
| bedrock | us.deepseek.r1-v1:0 | — | 1.35 | 5.4 |
| claude-code | opus | 0.725 | 0 | 0 |
| claude-code | sonnet | 0.727 | 0 | 0 |
| mcp | mcp-sampling | | 0 | 0 |
| gemini-cli | gemini-2.5-pro | 0.72 | 0 | 0 |
| gemini-cli | gemini-2.5-flash | 0.71 | 0 | 0 |
## Fallback Models
| Provider | Model Name | SWE Score | Input Cost | Output Cost |
| ----------- | ---------------------------------------------- | --------- | ---------- | ----------- |
| bedrock | us.anthropic.claude-3-haiku-20240307-v1:0 | 0.4 | 0.25 | 1.25 |
| bedrock | us.anthropic.claude-3-opus-20240229-v1:0 | 0.725 | 15 | 75 |
| bedrock | us.anthropic.claude-3-5-sonnet-20240620-v1:0 | 0.49 | 3 | 15 |
| bedrock | us.anthropic.claude-3-5-sonnet-20241022-v2:0 | 0.49 | 3 | 15 |
| bedrock | us.anthropic.claude-3-7-sonnet-20250219-v1:0 | 0.623 | 3 | 15 |
| bedrock | us.anthropic.claude-3-5-haiku-20241022-v1:0 | 0.4 | 0.8 | 4 |
| bedrock | us.anthropic.claude-opus-4-20250514-v1:0 | 0.725 | 15 | 75 |
| bedrock | us.anthropic.claude-sonnet-4-20250514-v1:0 | 0.727 | 3 | 15 |
| anthropic | claude-sonnet-4-20250514 | 0.727 | 3 | 15 |
| anthropic | claude-opus-4-20250514 | 0.725 | 15 | 75 |
| anthropic | claude-3-7-sonnet-20250219 | 0.623 | 3 | 15 |
| anthropic | claude-3-5-sonnet-20241022 | 0.49 | 3 | 15 |
| claude-code | opus | 0.725 | 0 | 0 |
| claude-code | sonnet | 0.727 | 0 | 0 |
| mcp | mcp-sampling | — | 0 | 0 |
| gemini-cli | gemini-2.5-pro | 0.72 | 0 | 0 |
| gemini-cli | gemini-2.5-flash | 0.71 | 0 | 0 |
| azure | gpt-4o | 0.332 | 2.5 | 10 |
| azure | gpt-4o-mini | 0.3 | 0.15 | 0.6 |
| azure | gpt-4-1 | — | 2 | 10 |
| openai | gpt-4o | 0.332 | 2.5 | 10 |
| openai | o3 | 0.5 | 2 | 8 |
| openai | o4-mini | 0.45 | 1.1 | 4.4 |
@@ -142,19 +147,18 @@
| google | gemini-2.5-flash-preview-04-17 | 0.604 | — | — |
| google | gemini-2.0-flash | 0.518 | 0.15 | 0.6 |
| google | gemini-2.0-flash-lite | — | — | — |
| 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 |
| xai | grok-4 | — | 3 | 15 |
| groq | moonshotai/kimi-k2-instruct | 0.66 | 1 | 3 |
| groq | llama-3.3-70b-versatile | 0.55 | 0.59 | 0.79 |
| groq | llama-3.1-8b-instant | 0.32 | 0.05 | 0.08 |
| groq | llama-4-scout | 0.45 | 0.11 | 0.34 |
| groq | llama-4-maverick | 0.52 | 0.5 | 0.77 |
| groq | mixtral-8x7b-32768 | 0.35 | 0.24 | 0.24 |
| groq | qwen-qwq-32b-preview | 0.4 | 0.18 | 0.18 |
| groq | gemma2-9b-it | 0.3 | 0.2 | 0.2 |
| perplexity | sonar-reasoning-pro | 0.211 | 2 | 8 |
| perplexity | sonar-reasoning | 0.211 | 1 | 5 |
| ollama | devstral:latest | — | 0 | 0 |
| ollama | qwen3:latest | | 0 | 0 |
| ollama | qwen3:14b | | 0 | 0 |
| ollama | qwen3:32b | | 0 | 0 |
| ollama | mistral-small3.1:latest | — | 0 | 0 |
| ollama | llama3.3:latest | | 0 | 0 |
| ollama | phi4:latest | | 0 | 0 |
| openrouter | google/gemini-2.5-flash-preview-05-20 | — | 0.15 | 0.6 |
| openrouter | google/gemini-2.5-flash-preview-05-20:thinking | — | 0.15 | 3.5 |
| openrouter | google/gemini-2.5-pro-exp-03-25 | — | 0 | 0 |
@@ -178,21 +182,15 @@
| openrouter | mistralai/mistral-small-3.1-24b-instruct | — | 0.1 | 0.3 |
| openrouter | mistralai/mistral-nemo | — | 0.03 | 0.07 |
| openrouter | thudm/glm-4-32b:free | — | 0 | 0 |
| ollama | devstral:latest | | 0 | 0 |
| ollama | qwen3:latest | | 0 | 0 |
| ollama | qwen3:14b | | 0 | 0 |
| ollama | qwen3:32b | | 0 | 0 |
| ollama | mistral-small3.1:latest | | 0 | 0 |
| ollama | llama3.3:latest | | 0 | 0 |
| ollama | phi4:latest | | 0 | 0 |
| azure | gpt-4o | 0.332 | 2.5 | 10 |
| azure | gpt-4o-mini | 0.3 | 0.15 | 0.6 |
| azure | gpt-4-1 | — | 2 | 10 |
| bedrock | us.anthropic.claude-3-haiku-20240307-v1:0 | 0.4 | 0.25 | 1.25 |
| bedrock | us.anthropic.claude-3-opus-20240229-v1:0 | 0.725 | 15 | 75 |
| bedrock | us.anthropic.claude-3-5-sonnet-20240620-v1:0 | 0.49 | 3 | 15 |
| bedrock | us.anthropic.claude-3-5-sonnet-20241022-v2:0 | 0.49 | 3 | 15 |
| bedrock | us.anthropic.claude-3-7-sonnet-20250219-v1:0 | 0.623 | 3 | 15 |
| bedrock | us.anthropic.claude-3-5-haiku-20241022-v1:0 | 0.4 | 0.8 | 4 |
| bedrock | us.anthropic.claude-opus-4-20250514-v1:0 | 0.725 | 15 | 75 |
| bedrock | us.anthropic.claude-sonnet-4-20250514-v1:0 | 0.727 | 3 | 15 |
| groq | llama-3.3-70b-versatile | 0.55 | 0.59 | 0.79 |
| groq | llama-3.1-8b-instant | 0.32 | 0.05 | 0.08 |
| groq | llama-4-scout | 0.45 | 0.11 | 0.34 |
| groq | llama-4-maverick | 0.52 | 0.5 | 0.77 |
| groq | mixtral-8x7b-32768 | 0.35 | 0.24 | 0.24 |
| groq | qwen-qwq-32b-preview | 0.4 | 0.18 | 0.18 |
| groq | gemma2-9b-it | 0.3 | 0.2 | 0.2 |
| claude-code | opus | 0.725 | 0 | 0 |
| claude-code | sonnet | 0.727 | 0 | 0 |
| mcp | mcp-sampling | — | 0 | 0 |
| gemini-cli | gemini-2.5-pro | 0.72 | 0 | 0 |
| gemini-cli | gemini-2.5-flash | 0.71 | 0 | 0 |

View File

@@ -125,7 +125,8 @@ export async function addTaskDirect(args, log, context = {}) {
},
'json', // outputFormat
manualTaskData, // Pass the manual task data
false // research flag is false for manual creation
false, // research flag is false for manual creation
projectRoot // Pass projectRoot
);
newTaskId = result.newTaskId;
telemetryData = result.telemetryData;

37
package-lock.json generated
View File

@@ -1,17 +1,13 @@
{
"name": "task-master-ai",
"version": "0.20.0",
"version": "0.19.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "task-master-ai",
"version": "0.20.0",
"version": "0.19.0",
"license": "MIT WITH Commons-Clause",
"workspaces": [
"apps/*",
"."
],
"dependencies": {
"@ai-sdk/amazon-bedrock": "^2.2.9",
"@ai-sdk/anthropic": "^1.2.10",
@@ -84,13 +80,6 @@
"ai-sdk-provider-gemini-cli": "^0.0.4"
}
},
"apps/extension": {
"version": "0.20.0",
"license": "ISC",
"devDependencies": {
"typescript": "^5.8.3"
}
},
"node_modules/@ai-sdk/amazon-bedrock": {
"version": "2.2.10",
"resolved": "https://registry.npmjs.org/@ai-sdk/amazon-bedrock/-/amazon-bedrock-2.2.10.tgz",
@@ -7351,10 +7340,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/extension": {
"resolved": "apps/extension",
"link": true
},
"node_modules/external-editor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
@@ -12992,10 +12977,6 @@
"react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/task-master-ai": {
"resolved": "",
"link": true
},
"node_modules/term-size": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz",
@@ -13204,20 +13185,6 @@
"node": ">= 0.6"
}
},
"node_modules/typescript": {
"version": "5.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/uint8array-extras": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.4.0.tgz",

View File

@@ -1,6 +1,6 @@
{
"name": "task-master-ai",
"version": "0.20.0",
"version": "0.19.0",
"description": "A task management system for ambitious AI-driven development that doesn't overwhelm and confuse Cursor.",
"main": "index.js",
"type": "module",
@@ -9,7 +9,6 @@
"task-master-mcp": "mcp-server/server.js",
"task-master-ai": "mcp-server/server.js"
},
"workspaces": ["apps/*", "."],
"scripts": {
"test": "node --experimental-vm-modules node_modules/.bin/jest",
"test:fails": "node --experimental-vm-modules node_modules/.bin/jest --onlyFailures",

View File

@@ -8,48 +8,47 @@
// --- Core Dependencies ---
import {
MODEL_MAP,
getAzureBaseURL,
getBaseUrlForRole,
getBedrockBaseURL,
getDebugFlag,
getFallbackModelId,
getFallbackProvider,
getMainModelId,
getMainProvider,
getOllamaBaseURL,
getParametersForRole,
getResearchModelId,
getMainModelId,
getResearchProvider,
getResearchModelId,
getFallbackProvider,
getFallbackModelId,
getParametersForRole,
getResponseLanguage,
getUserId,
getVertexLocation,
getVertexProjectId,
MODEL_MAP,
getDebugFlag,
getBaseUrlForRole,
isApiKeySet,
getOllamaBaseURL,
getAzureBaseURL,
getBedrockBaseURL,
getVertexProjectId,
getVertexLocation,
providersWithoutApiKeys
} from './config-manager.js';
import {
findProjectRoot,
getCurrentTag,
log,
resolveEnvVariable
findProjectRoot,
resolveEnvVariable,
getCurrentTag
} from './utils.js';
// Import provider classes
import {
AnthropicAIProvider,
AzureProvider,
BedrockAIProvider,
ClaudeCodeProvider,
GeminiCliProvider,
GoogleAIProvider,
GroqProvider,
OllamaAIProvider,
OpenAIProvider,
OpenRouterAIProvider,
PerplexityAIProvider,
GoogleAIProvider,
OpenAIProvider,
XAIProvider,
OpenRouterAIProvider,
OllamaAIProvider,
BedrockAIProvider,
AzureProvider,
VertexAIProvider,
XAIProvider
ClaudeCodeProvider,
GeminiCliProvider
} from '../../src/ai-providers/index.js';
// Import the provider registry
@@ -62,7 +61,6 @@ const PROVIDERS = {
google: new GoogleAIProvider(),
openai: new OpenAIProvider(),
xai: new XAIProvider(),
groq: new GroqProvider(),
openrouter: new OpenRouterAIProvider(),
ollama: new OllamaAIProvider(),
bedrock: new BedrockAIProvider(),

View File

@@ -805,7 +805,7 @@ function registerCommands(programInstance) {
'-i, --input <file>',
'Path to the PRD file (alternative to positional argument)'
)
.option('-o, --output <file>', 'Output file path')
.option('-o, --output <file>', 'Output file path', TASKMASTER_TASKS_FILE)
.option(
'-n, --num-tasks <number>',
'Number of tasks to generate',
@@ -825,18 +825,14 @@ function registerCommands(programInstance) {
// Initialize TaskMaster
let taskMaster;
try {
const initOptions = {
prdPath: file || options.input || true
};
// Only include tasksPath if output is explicitly specified
if (options.output) {
initOptions.tasksPath = options.output;
}
taskMaster = initTaskMaster(initOptions);
taskMaster = initTaskMaster({
prdPath: file || options.input || true,
tasksPath: options.output || true
});
} catch (error) {
console.log(
boxen(
`${chalk.white.bold('Parse PRD Help')}\n\n${chalk.cyan('Usage:')}\n task-master parse-prd <prd-file.txt> [options]\n\n${chalk.cyan('Options:')}\n -i, --input <file> Path to the PRD file (alternative to positional argument)\n -o, --output <file> Output file path (default: .taskmaster/tasks/tasks.json)\n -n, --num-tasks <number> Number of tasks to generate (default: 10)\n -f, --force Skip confirmation when overwriting existing tasks\n --append Append new tasks to existing tasks.json instead of overwriting\n -r, --research Use Perplexity AI for research-backed task generation\n\n${chalk.cyan('Example:')}\n task-master parse-prd requirements.txt --num-tasks 15\n task-master parse-prd --input=requirements.txt\n task-master parse-prd --force\n task-master parse-prd requirements_v2.txt --append\n task-master parse-prd requirements.txt --research\n\n${chalk.yellow('Note: This command will:')}\n 1. Look for a PRD file at ${TASKMASTER_DOCS_DIR}/PRD.md by default\n 2. Use the file specified by --input or positional argument if provided\n 3. Generate tasks from the PRD and either:\n - Overwrite any existing tasks.json file (default)\n - Append to existing tasks.json if --append is used`,
`${chalk.white.bold('Parse PRD Help')}\n\n${chalk.cyan('Usage:')}\n task-master parse-prd <prd-file.txt> [options]\n\n${chalk.cyan('Options:')}\n -i, --input <file> Path to the PRD file (alternative to positional argument)\n -o, --output <file> Output file path (default: "${TASKMASTER_TASKS_FILE}")\n -n, --num-tasks <number> Number of tasks to generate (default: 10)\n -f, --force Skip confirmation when overwriting existing tasks\n --append Append new tasks to existing tasks.json instead of overwriting\n -r, --research Use Perplexity AI for research-backed task generation\n\n${chalk.cyan('Example:')}\n task-master parse-prd requirements.txt --num-tasks 15\n task-master parse-prd --input=requirements.txt\n task-master parse-prd --force\n task-master parse-prd requirements_v2.txt --append\n task-master parse-prd requirements.txt --research\n\n${chalk.yellow('Note: This command will:')}\n 1. Look for a PRD file at ${TASKMASTER_DOCS_DIR}/PRD.md by default\n 2. Use the file specified by --input or positional argument if provided\n 3. Generate tasks from the PRD and either:\n - Overwrite any existing tasks.json file (default)\n - Append to existing tasks.json if --append is used`,
{ padding: 1, borderColor: 'blue', borderStyle: 'round' }
)
);
@@ -916,17 +912,18 @@ function registerCommands(programInstance) {
}
spinner = ora('Parsing PRD and generating tasks...\n').start();
// Handle case where getTasksPath() returns null
const outputPath =
taskMaster.getTasksPath() ||
path.join(taskMaster.getProjectRoot(), TASKMASTER_TASKS_FILE);
await parsePRD(taskMaster.getPrdPath(), outputPath, numTasks, {
append: useAppend,
force: useForce,
research: research,
projectRoot: taskMaster.getProjectRoot(),
tag: tag
});
await parsePRD(
taskMaster.getPrdPath(),
taskMaster.getTasksPath(),
numTasks,
{
append: useAppend,
force: useForce,
research: research,
projectRoot: taskMaster.getProjectRoot(),
tag: tag
}
);
spinner.succeed('Tasks generated successfully!');
} catch (error) {
if (spinner) {
@@ -1500,16 +1497,10 @@ function registerCommands(programInstance) {
.option('--tag <tag>', 'Specify tag context for task operations')
.action(async (options) => {
// Initialize TaskMaster
const initOptions = {
tasksPath: options.file || true
};
// Only pass complexityReportPath if user provided a custom path
if (options.report && options.report !== COMPLEXITY_REPORT_FILE) {
initOptions.complexityReportPath = options.report;
}
const taskMaster = initTaskMaster(initOptions);
const taskMaster = initTaskMaster({
tasksPath: options.file || true,
complexityReportPath: options.report || false
});
const statusFilter = options.status;
const withSubtasks = options.withSubtasks || false;
@@ -1640,7 +1631,11 @@ function registerCommands(programInstance) {
.description(
`Analyze tasks and generate expansion recommendations${chalk.reset('')}`
)
.option('-o, --output <file>', 'Output file path for the report')
.option(
'-o, --output <file>',
'Output file path for the report',
COMPLEXITY_REPORT_FILE
)
.option(
'-m, --model <model>',
'LLM model to use for analysis (defaults to configured model)'
@@ -1668,14 +1663,10 @@ function registerCommands(programInstance) {
.option('--tag <tag>', 'Specify tag context for task operations')
.action(async (options) => {
// Initialize TaskMaster
const initOptions = {
tasksPath: options.file || true // Tasks file is required to analyze
};
// Only include complexityReportPath if output is explicitly specified
if (options.output) {
initOptions.complexityReportPath = options.output;
}
const taskMaster = initTaskMaster(initOptions);
const taskMaster = initTaskMaster({
tasksPath: options.file || true,
complexityReportPath: options.output || true
});
const tag = options.tag;
const modelOverride = options.model;
@@ -1690,13 +1681,11 @@ function registerCommands(programInstance) {
displayCurrentTagIndicator(targetTag);
// Tag-aware output file naming: master -> task-complexity-report.json, other tags -> task-complexity-report_tagname.json
const baseOutputPath =
taskMaster.getComplexityReportPath() ||
path.join(taskMaster.getProjectRoot(), COMPLEXITY_REPORT_FILE);
const baseOutputPath = taskMaster.getComplexityReportPath();
const outputPath =
options.output === COMPLEXITY_REPORT_FILE && targetTag !== 'master'
? baseOutputPath.replace('.json', `_${targetTag}.json`)
: options.output || baseOutputPath;
: baseOutputPath;
console.log(
chalk.blue(
@@ -1776,11 +1765,6 @@ function registerCommands(programInstance) {
)
.option('--tag <tag>', 'Specify tag context for task operations')
.action(async (prompt, options) => {
// Initialize TaskMaster
const taskMaster = initTaskMaster({
tasksPath: options.file || true
});
// Parameter validation
if (!prompt || typeof prompt !== 'string' || prompt.trim().length === 0) {
console.error(
@@ -2222,8 +2206,6 @@ ${result.result}
tasksPath: options.file || true
});
const projectRoot = taskMaster.getProjectRoot();
// Show current tag context
displayCurrentTagIndicator(
options.tag || getCurrentTag(taskMaster.getProjectRoot()) || 'master'
@@ -2353,14 +2335,10 @@ ${result.result}
.option('--tag <tag>', 'Specify tag context for task operations')
.action(async (taskId, options) => {
// Initialize TaskMaster
const initOptions = {
tasksPath: options.file || true
};
// Only pass complexityReportPath if user provided a custom path
if (options.report && options.report !== COMPLEXITY_REPORT_FILE) {
initOptions.complexityReportPath = options.report;
}
const taskMaster = initTaskMaster(initOptions);
const taskMaster = initTaskMaster({
tasksPath: options.file || true,
complexityReportPath: options.report || false
});
const idArg = taskId || options.id;
const statusFilter = options.status;
@@ -3479,9 +3457,6 @@ Examples:
const taskMaster = initTaskMaster({
tasksPath: options.file || false
});
const projectRoot = taskMaster.getProjectRoot();
// Validate flags: cannot use multiple provider flags simultaneously
const providerFlags = [
options.openrouter,

View File

@@ -1,21 +1,18 @@
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import chalk from 'chalk';
import { z } from 'zod';
import { AI_COMMAND_NAMES } from '../../src/constants/commands.js';
import { fileURLToPath } from 'url';
import { log, findProjectRoot, resolveEnvVariable, isEmpty } from './utils.js';
import { LEGACY_CONFIG_FILE } from '../../src/constants/paths.js';
import { findConfigPath } from '../../src/utils/path-utils.js';
import {
LEGACY_CONFIG_FILE,
TASKMASTER_DIR
} from '../../src/constants/paths.js';
import {
ALL_PROVIDERS,
VALIDATED_PROVIDERS,
CUSTOM_PROVIDERS,
CUSTOM_PROVIDERS_ARRAY,
VALIDATED_PROVIDERS
ALL_PROVIDERS
} from '../../src/constants/providers.js';
import { findConfigPath } from '../../src/utils/path-utils.js';
import { findProjectRoot, isEmpty, log, resolveEnvVariable } from './utils.js';
import { AI_COMMAND_NAMES } from '../../src/constants/commands.js';
// Calculate __dirname in ESM
const __filename = fileURLToPath(import.meta.url);
@@ -102,30 +99,17 @@ function _loadAndValidateConfig(explicitRoot = null) {
if (rootToUse) {
configSource = `found root (${rootToUse})`;
} else {
// No root found, use current working directory as fallback
// This prevents infinite loops during initialization
rootToUse = process.cwd();
configSource = `current directory (${rootToUse}) - no project markers found`;
// No root found, return defaults immediately
return defaults;
}
}
// ---> End find project root logic <---
// --- Find configuration file ---
let configPath = null;
// --- Find configuration file using centralized path utility ---
const configPath = findConfigPath(null, { projectRoot: rootToUse });
let config = { ...defaults }; // Start with a deep copy of defaults
let configExists = false;
// During initialization (no project markers), skip config file search entirely
const hasProjectMarkers =
fs.existsSync(path.join(rootToUse, TASKMASTER_DIR)) ||
fs.existsSync(path.join(rootToUse, LEGACY_CONFIG_FILE));
if (hasProjectMarkers) {
// Only try to find config if we have project markers
// This prevents the repeated warnings during init
configPath = findConfigPath(null, { projectRoot: rootToUse });
}
if (configPath) {
configExists = true;
const isLegacy = configPath.endsWith(LEGACY_CONFIG_FILE);
@@ -215,22 +199,11 @@ function _loadAndValidateConfig(explicitRoot = null) {
)
);
} else {
// Don't warn about missing config during initialization
// Only warn if this looks like an existing project (has .taskmaster dir or legacy config marker)
const hasTaskmasterDir = fs.existsSync(
path.join(rootToUse, TASKMASTER_DIR)
console.warn(
chalk.yellow(
`Warning: Configuration file not found at derived root (${rootToUse}). Using defaults.`
)
);
const hasLegacyMarker = fs.existsSync(
path.join(rootToUse, LEGACY_CONFIG_FILE)
);
if (hasTaskmasterDir || hasLegacyMarker) {
console.warn(
chalk.yellow(
`Warning: Configuration file not found at derived root (${rootToUse}). Using defaults.`
)
);
}
}
// Keep config as defaults
config = { ...defaults };
@@ -641,7 +614,6 @@ function isApiKeySet(providerName, session = null, projectRoot = null) {
azure: 'AZURE_OPENAI_API_KEY',
openrouter: 'OPENROUTER_API_KEY',
xai: 'XAI_API_KEY',
groq: 'GROQ_API_KEY',
vertex: 'GOOGLE_API_KEY', // Vertex uses the same key as Google
'claude-code': 'CLAUDE_CODE_API_KEY', // Not actually used, but included for consistency
bedrock: 'AWS_ACCESS_KEY_ID' // Bedrock uses AWS credentials
@@ -727,10 +699,6 @@ function getMcpApiKeyStatus(providerName, projectRoot = null) {
apiKeyToCheck = mcpEnv.XAI_API_KEY;
placeholderValue = 'YOUR_XAI_API_KEY_HERE';
break;
case 'groq':
apiKeyToCheck = mcpEnv.GROQ_API_KEY;
placeholderValue = 'YOUR_GROQ_API_KEY_HERE';
break;
case 'ollama':
return true; // No key needed
case 'claude-code':

View File

@@ -295,16 +295,6 @@
}
],
"groq": [
{
"id": "moonshotai/kimi-k2-instruct",
"swe_score": 0.66,
"cost_per_1m_tokens": {
"input": 1.0,
"output": 3.0
},
"allowed_roles": ["main", "fallback"],
"max_tokens": 16384
},
{
"id": "llama-3.3-70b-versatile",
"swe_score": 0.55,

View File

@@ -561,6 +561,16 @@ async function addTask(
writeJSON(tasksPath, rawData, projectRoot, targetTag);
report('DEBUG: tasks.json written.', 'debug');
// Generate markdown task files
report('Generating task files...', 'info');
report('DEBUG: Calling generateTaskFiles...', 'debug');
// Pass mcpLog if available to generateTaskFiles
await generateTaskFiles(tasksPath, path.dirname(tasksPath), {
projectRoot,
tag: targetTag
});
report('DEBUG: generateTaskFiles finished.', 'debug');
// Show success message - only for text output (CLI)
if (outputFormat === 'text') {
const table = new Table({

View File

@@ -240,7 +240,7 @@ async function analyzeTaskComplexity(options, context = {}) {
tasks: relevantTaskIds,
format: 'research'
});
gatheredContext = contextResult.context || '';
gatheredContext = contextResult;
}
} catch (contextError) {
reportLog(
@@ -406,10 +406,11 @@ async function analyzeTaskComplexity(options, context = {}) {
useResearch: useResearch
};
const variantKey = useResearch ? 'research' : 'default';
const { systemPrompt, userPrompt: prompt } = await promptManager.loadPrompt(
'analyze-complexity',
promptParams,
'default'
variantKey
);
let loadingIndicator = null;

View File

@@ -369,7 +369,7 @@ async function expandTask(
tasks: finalTaskIds,
format: 'research'
});
gatheredContext = contextResult.context || '';
gatheredContext = contextResult;
}
} catch (contextError) {
logger.warn(`Could not gather context: ${contextError.message}`);
@@ -461,44 +461,19 @@ async function expandTask(
`${combinedAdditionalContext}\n\n# Project Context\n\n${gatheredContext}`.trim();
}
// Ensure expansionPrompt is a string (handle both string and object formats)
let expansionPromptText = undefined;
if (taskAnalysis?.expansionPrompt) {
if (typeof taskAnalysis.expansionPrompt === 'string') {
expansionPromptText = taskAnalysis.expansionPrompt;
} else if (
typeof taskAnalysis.expansionPrompt === 'object' &&
taskAnalysis.expansionPrompt.text
) {
expansionPromptText = taskAnalysis.expansionPrompt.text;
}
}
// Ensure gatheredContext is a string (handle both string and object formats)
let gatheredContextText = gatheredContext;
if (typeof gatheredContext === 'object' && gatheredContext !== null) {
if (gatheredContext.data) {
gatheredContextText = gatheredContext.data;
} else if (gatheredContext.text) {
gatheredContextText = gatheredContext.text;
} else {
gatheredContextText = JSON.stringify(gatheredContext);
}
}
const promptParams = {
task: task,
subtaskCount: finalSubtaskCount,
nextSubtaskId: nextSubtaskId,
additionalContext: additionalContext,
complexityReasoningContext: complexityReasoningContext,
gatheredContext: gatheredContextText || '',
gatheredContext: gatheredContext,
useResearch: useResearch,
expansionPrompt: expansionPromptText || undefined
expansionPrompt: taskAnalysis?.expansionPrompt || null
};
let variantKey = 'default';
if (expansionPromptText) {
if (taskAnalysis?.expansionPrompt) {
variantKey = 'complexity-report';
logger.info(
`Using expansion prompt from complexity report for task ${task.id}.`

View File

@@ -205,10 +205,12 @@ async function performResearch(
}
};
// Load prompts - the research template handles detail level internally
// Select variant based on detail level
const variantKey = detailLevel; // 'low', 'medium', or 'high'
const { systemPrompt, userPrompt } = await promptManager.loadPrompt(
'research',
promptParams
promptParams,
variantKey
);
// Count tokens for system and user prompts

View File

@@ -161,7 +161,7 @@ async function updateSubtaskById(
tasks: finalTaskIds,
format: 'research'
});
gatheredContext = contextResult.context || '';
gatheredContext = contextResult;
}
} catch (contextError) {
report('warn', `Could not gather context: ${contextError.message}`);
@@ -214,7 +214,7 @@ async function updateSubtaskById(
title: parentTask.subtasks[subtaskIndex - 1].title,
status: parentTask.subtasks[subtaskIndex - 1].status
}
: undefined;
: null;
const nextSubtask =
subtaskIndex < parentTask.subtasks.length - 1
? {
@@ -222,7 +222,7 @@ async function updateSubtaskById(
title: parentTask.subtasks[subtaskIndex + 1].title,
status: parentTask.subtasks[subtaskIndex + 1].status
}
: undefined;
: null;
// Build prompts using PromptManager
const promptManager = getPromptManager();

View File

@@ -190,45 +190,8 @@ function parseUpdatedTaskFromText(text, expectedTaskId, logFn, isMCP) {
throw new Error('Parsed AI response is not a valid JSON object.');
}
// Preprocess the task to ensure subtasks have proper structure
const preprocessedTask = {
...parsedTask,
status: parsedTask.status || 'pending',
dependencies: Array.isArray(parsedTask.dependencies)
? parsedTask.dependencies
: [],
details:
typeof parsedTask.details === 'string'
? parsedTask.details
: String(parsedTask.details || ''),
testStrategy:
typeof parsedTask.testStrategy === 'string'
? parsedTask.testStrategy
: String(parsedTask.testStrategy || ''),
// Ensure subtasks is an array and each subtask has required fields
subtasks: Array.isArray(parsedTask.subtasks)
? parsedTask.subtasks.map((subtask) => ({
...subtask,
title: subtask.title || '',
description: subtask.description || '',
status: subtask.status || 'pending',
dependencies: Array.isArray(subtask.dependencies)
? subtask.dependencies
: [],
details:
typeof subtask.details === 'string'
? subtask.details
: String(subtask.details || ''),
testStrategy:
typeof subtask.testStrategy === 'string'
? subtask.testStrategy
: String(subtask.testStrategy || '')
}))
: []
};
// Validate the parsed task object using Zod
const validationResult = updatedTaskSchema.safeParse(preprocessedTask);
const validationResult = updatedTaskSchema.safeParse(parsedTask);
if (!validationResult.success) {
report('error', 'Parsed task object failed Zod validation.');
validationResult.error.errors.forEach((err) => {
@@ -383,7 +346,7 @@ async function updateTaskById(
tasks: finalTaskIds,
format: 'research'
});
gatheredContext = contextResult.context || '';
gatheredContext = contextResult;
}
} catch (contextError) {
report('warn', `Could not gather context: ${contextError.message}`);

View File

@@ -196,18 +196,7 @@ function parseUpdatedTasksFromText(text, expectedCount, logFn, isMCP) {
);
}
// Preprocess tasks to ensure required fields have proper defaults
const preprocessedTasks = parsedTasks.map((task) => ({
...task,
// Ensure subtasks is always an array (not null or undefined)
subtasks: Array.isArray(task.subtasks) ? task.subtasks : [],
// Ensure status has a default value if missing
status: task.status || 'pending',
// Ensure dependencies is always an array
dependencies: Array.isArray(task.dependencies) ? task.dependencies : []
}));
const validationResult = updatedTaskArraySchema.safeParse(preprocessedTasks);
const validationResult = updatedTaskArraySchema.safeParse(parsedTasks);
if (!validationResult.success) {
report('error', 'Parsed task array failed Zod validation.');
validationResult.error.errors.forEach((err) => {
@@ -311,7 +300,7 @@ async function updateTasks(
tasks: finalTaskIds,
format: 'research'
});
gatheredContext = contextResult.context || '';
gatheredContext = contextResult; // contextResult is a string
}
} catch (contextError) {
logFn(
@@ -453,17 +442,7 @@ async function updateTasks(
data.tasks.forEach((task, index) => {
if (updatedTasksMap.has(task.id)) {
// Only update if the task was part of the set sent to AI
const updatedTask = updatedTasksMap.get(task.id);
// Merge the updated task with the existing one to preserve fields like subtasks
data.tasks[index] = {
...task, // Keep all existing fields
...updatedTask, // Override with updated fields
// Ensure subtasks field is preserved if not provided by AI
subtasks:
updatedTask.subtasks !== undefined
? updatedTask.subtasks
: task.subtasks
};
data.tasks[index] = updatedTasksMap.get(task.id);
actualUpdateCount++;
}
});

View File

@@ -14,14 +14,6 @@ export class GroqProvider extends BaseAIProvider {
this.name = 'Groq';
}
/**
* Returns the environment variable name required for this provider's API key.
* @returns {string} The environment variable name for the Groq API key
*/
getRequiredApiKeyName() {
return 'GROQ_API_KEY';
}
/**
* Creates and returns a Groq client instance.
* @param {object} params - Parameters for client initialization

View File

@@ -1,5 +1,5 @@
/**
* @typedef {'amp' | 'claude' | 'cline' | 'codex' | 'cursor' | 'gemini' | 'kiro' | 'opencode' | 'roo' | 'trae' | 'windsurf' | 'vscode' | 'zed'} RulesProfile
* @typedef {'claude' | 'cline' | 'codex' | 'cursor' | 'gemini' | 'roo' | 'trae' | 'windsurf' | 'vscode'} RulesProfile
*/
/**
@@ -10,19 +10,15 @@
*
* @type {RulesProfile[]}
* @description Defines possible rule profile sets:
* - amp: Amp Code integration
* - claude: Claude Code integration
* - cline: Cline IDE rules
* - codex: Codex integration
* - cursor: Cursor IDE rules
* - gemini: Gemini integration
* - kiro: Kiro IDE rules
* - opencode: OpenCode integration
* - roo: Roo Code IDE rules
* - trae: Trae IDE rules
* - vscode: VS Code with GitHub Copilot integration
* - windsurf: Windsurf IDE rules
* - zed: Zed IDE rules
*
* To add a new rule profile:
* 1. Add the profile name to this array
@@ -30,19 +26,15 @@
* 3. Export it as {profile}Profile in src/profiles/index.js
*/
export const RULE_PROFILES = [
'amp',
'claude',
'cline',
'codex',
'cursor',
'gemini',
'kiro',
'opencode',
'roo',
'trae',
'vscode',
'windsurf',
'zed'
'windsurf'
];
/**

View File

@@ -1,277 +0,0 @@
// Amp profile for rule-transformer
import path from 'path';
import fs from 'fs';
import { isSilentMode, log } from '../../scripts/modules/utils.js';
import { createProfile } from './base-profile.js';
/**
* Transform standard MCP config format to Amp format
* @param {Object} mcpConfig - Standard MCP configuration object
* @returns {Object} - Transformed Amp configuration object
*/
function transformToAmpFormat(mcpConfig) {
const ampConfig = {};
// Transform mcpServers to amp.mcpServers
if (mcpConfig.mcpServers) {
ampConfig['amp.mcpServers'] = mcpConfig.mcpServers;
}
// Preserve any other existing settings
for (const [key, value] of Object.entries(mcpConfig)) {
if (key !== 'mcpServers') {
ampConfig[key] = value;
}
}
return ampConfig;
}
// Lifecycle functions for Amp profile
function onAddRulesProfile(targetDir, assetsDir) {
// Handle AGENT.md import for non-destructive integration (Amp uses AGENT.md, copies from AGENTS.md)
const sourceFile = path.join(assetsDir, 'AGENTS.md');
const userAgentFile = path.join(targetDir, 'AGENT.md');
const taskMasterAgentFile = path.join(targetDir, '.taskmaster', 'AGENT.md');
const importLine = '@./.taskmaster/AGENT.md';
const importSection = `\n## Task Master AI Instructions\n**Import Task Master's development workflow commands and guidelines, treat as if import is in the main AGENT.md file.**\n${importLine}`;
if (fs.existsSync(sourceFile)) {
try {
// Ensure .taskmaster directory exists
const taskMasterDir = path.join(targetDir, '.taskmaster');
if (!fs.existsSync(taskMasterDir)) {
fs.mkdirSync(taskMasterDir, { recursive: true });
}
// Copy Task Master instructions to .taskmaster/AGENT.md
fs.copyFileSync(sourceFile, taskMasterAgentFile);
log(
'debug',
`[Amp] Created Task Master instructions at ${taskMasterAgentFile}`
);
// Handle user's AGENT.md
if (fs.existsSync(userAgentFile)) {
// Check if import already exists
const content = fs.readFileSync(userAgentFile, 'utf8');
if (!content.includes(importLine)) {
// Append import section at the end
const updatedContent = content.trim() + '\n' + importSection + '\n';
fs.writeFileSync(userAgentFile, updatedContent);
log(
'info',
`[Amp] Added Task Master import to existing ${userAgentFile}`
);
} else {
log(
'info',
`[Amp] Task Master import already present in ${userAgentFile}`
);
}
} else {
// Create minimal AGENT.md with the import section
const minimalContent = `# Amp Instructions\n${importSection}\n`;
fs.writeFileSync(userAgentFile, minimalContent);
log('info', `[Amp] Created ${userAgentFile} with Task Master import`);
}
} catch (err) {
log('error', `[Amp] Failed to set up Amp instructions: ${err.message}`);
}
}
// MCP transformation will be handled in onPostConvertRulesProfile
}
function onRemoveRulesProfile(targetDir) {
// Clean up AGENT.md import (Amp uses AGENT.md, not AGENTS.md)
const userAgentFile = path.join(targetDir, 'AGENT.md');
const taskMasterAgentFile = path.join(targetDir, '.taskmaster', 'AGENT.md');
const importLine = '@./.taskmaster/AGENT.md';
try {
// Remove Task Master AGENT.md from .taskmaster
if (fs.existsSync(taskMasterAgentFile)) {
fs.rmSync(taskMasterAgentFile, { force: true });
log('debug', `[Amp] Removed ${taskMasterAgentFile}`);
}
// Clean up import from user's AGENT.md
if (fs.existsSync(userAgentFile)) {
const content = fs.readFileSync(userAgentFile, 'utf8');
const lines = content.split('\n');
const filteredLines = [];
let skipNextLines = 0;
// Remove the Task Master section
for (let i = 0; i < lines.length; i++) {
if (skipNextLines > 0) {
skipNextLines--;
continue;
}
// Check if this is the start of our Task Master section
if (lines[i].includes('## Task Master AI Instructions')) {
// Skip this line and the next two lines (bold text and import)
skipNextLines = 2;
continue;
}
// Also remove standalone import lines (for backward compatibility)
if (lines[i].trim() === importLine) {
continue;
}
filteredLines.push(lines[i]);
}
// Join back and clean up excessive newlines
let updatedContent = filteredLines
.join('\n')
.replace(/\n{3,}/g, '\n\n')
.trim();
// Check if file only contained our minimal template
if (updatedContent === '# Amp Instructions' || updatedContent === '') {
// File only contained our import, remove it
fs.rmSync(userAgentFile, { force: true });
log('debug', `[Amp] Removed empty ${userAgentFile}`);
} else {
// Write back without the import
fs.writeFileSync(userAgentFile, updatedContent + '\n');
log('debug', `[Amp] Removed Task Master import from ${userAgentFile}`);
}
}
} catch (err) {
log('error', `[Amp] Failed to remove Amp instructions: ${err.message}`);
}
// MCP Removal: Remove amp.mcpServers section
const mcpConfigPath = path.join(targetDir, '.vscode', 'settings.json');
if (!fs.existsSync(mcpConfigPath)) {
log('debug', '[Amp] No .vscode/settings.json found to clean up');
return;
}
try {
// Read the current config
const configContent = fs.readFileSync(mcpConfigPath, 'utf8');
const config = JSON.parse(configContent);
// Check if it has the amp.mcpServers section and task-master-ai server
if (
config['amp.mcpServers'] &&
config['amp.mcpServers']['task-master-ai']
) {
// Remove task-master-ai server
delete config['amp.mcpServers']['task-master-ai'];
// Check if there are other MCP servers in amp.mcpServers
const remainingServers = Object.keys(config['amp.mcpServers']);
if (remainingServers.length === 0) {
// No other servers, remove entire amp.mcpServers section
delete config['amp.mcpServers'];
log('debug', '[Amp] Removed empty amp.mcpServers section');
}
// Check if config is now empty
const remainingKeys = Object.keys(config);
if (remainingKeys.length === 0) {
// Config is empty, remove entire file
fs.rmSync(mcpConfigPath, { force: true });
log('info', '[Amp] Removed empty settings.json file');
// Check if .vscode directory is empty
const vscodeDirPath = path.join(targetDir, '.vscode');
if (fs.existsSync(vscodeDirPath)) {
const remainingContents = fs.readdirSync(vscodeDirPath);
if (remainingContents.length === 0) {
fs.rmSync(vscodeDirPath, { recursive: true, force: true });
log('debug', '[Amp] Removed empty .vscode directory');
}
}
} else {
// Write back the modified config
fs.writeFileSync(
mcpConfigPath,
JSON.stringify(config, null, '\t') + '\n'
);
log(
'info',
'[Amp] Removed TaskMaster from settings.json, preserved other configurations'
);
}
} else {
log('debug', '[Amp] TaskMaster not found in amp.mcpServers');
}
} catch (error) {
log('error', `[Amp] Failed to clean up settings.json: ${error.message}`);
}
}
function onPostConvertRulesProfile(targetDir, assetsDir) {
// Handle AGENT.md setup (same as onAddRulesProfile)
onAddRulesProfile(targetDir, assetsDir);
// Transform MCP config to Amp format
const mcpConfigPath = path.join(targetDir, '.vscode', 'settings.json');
if (!fs.existsSync(mcpConfigPath)) {
log('debug', '[Amp] No .vscode/settings.json found to transform');
return;
}
try {
// Read the generated standard MCP config
const mcpConfigContent = fs.readFileSync(mcpConfigPath, 'utf8');
const mcpConfig = JSON.parse(mcpConfigContent);
// Check if it's already in Amp format (has amp.mcpServers)
if (mcpConfig['amp.mcpServers']) {
log(
'info',
'[Amp] settings.json already in Amp format, skipping transformation'
);
return;
}
// Transform to Amp format
const ampConfig = transformToAmpFormat(mcpConfig);
// Write back the transformed config with proper formatting
fs.writeFileSync(
mcpConfigPath,
JSON.stringify(ampConfig, null, '\t') + '\n'
);
log('info', '[Amp] Transformed settings.json to Amp format');
log('debug', '[Amp] Renamed mcpServers to amp.mcpServers');
} catch (error) {
log('error', `[Amp] Failed to transform settings.json: ${error.message}`);
}
}
// Create and export amp profile using the base factory
export const ampProfile = createProfile({
name: 'amp',
displayName: 'Amp',
url: 'ampcode.com',
docsUrl: 'ampcode.com/manual',
profileDir: '.vscode',
rulesDir: '.',
mcpConfig: true,
mcpConfigName: 'settings.json',
includeDefaultRules: false,
fileMap: {
'AGENTS.md': '.taskmaster/AGENT.md'
},
onAdd: onAddRulesProfile,
onRemove: onRemoveRulesProfile,
onPostConvert: onPostConvertRulesProfile
});
// Export lifecycle functions separately to avoid naming conflicts
export { onAddRulesProfile, onRemoveRulesProfile, onPostConvertRulesProfile };

View File

@@ -46,9 +46,7 @@ export function createProfile(editorConfig) {
onPostConvert
} = editorConfig;
const mcpConfigPath = mcpConfigName
? path.join(profileDir, mcpConfigName)
: null;
const mcpConfigPath = mcpConfigName ? `${profileDir}/${mcpConfigName}` : null;
// Standard file mapping with custom overrides
// Use taskmaster subdirectory only if profile supports it

View File

@@ -59,63 +59,6 @@ function onAddRulesProfile(targetDir, assetsDir) {
`[Claude] An error occurred during directory copy: ${err.message}`
);
}
// Handle CLAUDE.md import for non-destructive integration
const sourceFile = path.join(assetsDir, 'AGENTS.md');
const userClaudeFile = path.join(targetDir, 'CLAUDE.md');
const taskMasterClaudeFile = path.join(targetDir, '.taskmaster', 'CLAUDE.md');
const importLine = '@./.taskmaster/CLAUDE.md';
const importSection = `\n## Task Master AI Instructions\n**Import Task Master's development workflow commands and guidelines, treat as if import is in the main CLAUDE.md file.**\n${importLine}`;
if (fs.existsSync(sourceFile)) {
try {
// Ensure .taskmaster directory exists
const taskMasterDir = path.join(targetDir, '.taskmaster');
if (!fs.existsSync(taskMasterDir)) {
fs.mkdirSync(taskMasterDir, { recursive: true });
}
// Copy Task Master instructions to .taskmaster/CLAUDE.md
fs.copyFileSync(sourceFile, taskMasterClaudeFile);
log(
'debug',
`[Claude] Created Task Master instructions at ${taskMasterClaudeFile}`
);
// Handle user's CLAUDE.md
if (fs.existsSync(userClaudeFile)) {
// Check if import already exists
const content = fs.readFileSync(userClaudeFile, 'utf8');
if (!content.includes(importLine)) {
// Append import section at the end
const updatedContent = content.trim() + '\n' + importSection + '\n';
fs.writeFileSync(userClaudeFile, updatedContent);
log(
'info',
`[Claude] Added Task Master import to existing ${userClaudeFile}`
);
} else {
log(
'info',
`[Claude] Task Master import already present in ${userClaudeFile}`
);
}
} else {
// Create minimal CLAUDE.md with the import section
const minimalContent = `# Claude Code Instructions\n${importSection}\n`;
fs.writeFileSync(userClaudeFile, minimalContent);
log(
'info',
`[Claude] Created ${userClaudeFile} with Task Master import`
);
}
} catch (err) {
log(
'error',
`[Claude] Failed to set up Claude instructions: ${err.message}`
);
}
}
}
function onRemoveRulesProfile(targetDir) {
@@ -124,146 +67,11 @@ function onRemoveRulesProfile(targetDir) {
if (removeDirectoryRecursive(claudeDir)) {
log('debug', `[Claude] Removed .claude directory from ${claudeDir}`);
}
// Clean up CLAUDE.md import
const userClaudeFile = path.join(targetDir, 'CLAUDE.md');
const taskMasterClaudeFile = path.join(targetDir, '.taskmaster', 'CLAUDE.md');
const importLine = '@./.taskmaster/CLAUDE.md';
try {
// Remove Task Master CLAUDE.md from .taskmaster
if (fs.existsSync(taskMasterClaudeFile)) {
fs.rmSync(taskMasterClaudeFile, { force: true });
log('debug', `[Claude] Removed ${taskMasterClaudeFile}`);
}
// Clean up import from user's CLAUDE.md
if (fs.existsSync(userClaudeFile)) {
const content = fs.readFileSync(userClaudeFile, 'utf8');
const lines = content.split('\n');
const filteredLines = [];
let skipNextLines = 0;
// Remove the Task Master section
for (let i = 0; i < lines.length; i++) {
if (skipNextLines > 0) {
skipNextLines--;
continue;
}
// Check if this is the start of our Task Master section
if (lines[i].includes('## Task Master AI Instructions')) {
// Skip this line and the next two lines (bold text and import)
skipNextLines = 2;
continue;
}
// Also remove standalone import lines (for backward compatibility)
if (lines[i].trim() === importLine) {
continue;
}
filteredLines.push(lines[i]);
}
// Join back and clean up excessive newlines
let updatedContent = filteredLines
.join('\n')
.replace(/\n{3,}/g, '\n\n')
.trim();
// Check if file only contained our minimal template
if (
updatedContent === '# Claude Code Instructions' ||
updatedContent === ''
) {
// File only contained our import, remove it
fs.rmSync(userClaudeFile, { force: true });
log('debug', `[Claude] Removed empty ${userClaudeFile}`);
} else {
// Write back without the import
fs.writeFileSync(userClaudeFile, updatedContent + '\n');
log(
'debug',
`[Claude] Removed Task Master import from ${userClaudeFile}`
);
}
}
} catch (err) {
log(
'error',
`[Claude] Failed to remove Claude instructions: ${err.message}`
);
}
}
/**
* Transform standard MCP config format to Claude format
* @param {Object} mcpConfig - Standard MCP configuration object
* @returns {Object} - Transformed Claude configuration object
*/
function transformToClaudeFormat(mcpConfig) {
const claudeConfig = {};
// Transform mcpServers to servers (keeping the same structure but adding type)
if (mcpConfig.mcpServers) {
claudeConfig.mcpServers = {};
for (const [serverName, serverConfig] of Object.entries(
mcpConfig.mcpServers
)) {
// Transform server configuration with type as first key
const reorderedServer = {};
// Add type: "stdio" as the first key
reorderedServer.type = 'stdio';
// Then add the rest of the properties in order
if (serverConfig.command) reorderedServer.command = serverConfig.command;
if (serverConfig.args) reorderedServer.args = serverConfig.args;
if (serverConfig.env) reorderedServer.env = serverConfig.env;
// Add any other properties that might exist
Object.keys(serverConfig).forEach((key) => {
if (!['command', 'args', 'env', 'type'].includes(key)) {
reorderedServer[key] = serverConfig[key];
}
});
claudeConfig.mcpServers[serverName] = reorderedServer;
}
}
return claudeConfig;
}
function onPostConvertRulesProfile(targetDir, assetsDir) {
// For Claude, post-convert is the same as add since we don't transform rules
onAddRulesProfile(targetDir, assetsDir);
// Transform MCP configuration to Claude format
const mcpConfigPath = path.join(targetDir, '.mcp.json');
if (fs.existsSync(mcpConfigPath)) {
try {
const mcpConfig = JSON.parse(fs.readFileSync(mcpConfigPath, 'utf8'));
const claudeConfig = transformToClaudeFormat(mcpConfig);
// Write back the transformed configuration
fs.writeFileSync(
mcpConfigPath,
JSON.stringify(claudeConfig, null, '\t') + '\n'
);
log(
'debug',
`[Claude] Transformed MCP configuration to Claude format at ${mcpConfigPath}`
);
} catch (err) {
log(
'error',
`[Claude] Failed to transform MCP configuration: ${err.message}`
);
}
}
}
// Create and export claude profile using the base factory
@@ -274,10 +82,11 @@ export const claudeProfile = createProfile({
docsUrl: 'docs.anthropic.com/en/docs/claude-code',
profileDir: '.', // Root directory
rulesDir: '.', // No specific rules directory needed
mcpConfigName: '.mcp.json', // Place MCP config in project root
mcpConfig: false,
mcpConfigName: null,
includeDefaultRules: false,
fileMap: {
'AGENTS.md': '.taskmaster/CLAUDE.md'
'AGENTS.md': 'CLAUDE.md'
},
onAdd: onAddRulesProfile,
onRemove: onRemoveRulesProfile,

View File

@@ -1,14 +1,10 @@
// Profile exports for centralized importing
export { ampProfile } from './amp.js';
export { claudeProfile } from './claude.js';
export { clineProfile } from './cline.js';
export { codexProfile } from './codex.js';
export { cursorProfile } from './cursor.js';
export { geminiProfile } from './gemini.js';
export { kiroProfile } from './kiro.js';
export { opencodeProfile } from './opencode.js';
export { rooProfile } from './roo.js';
export { traeProfile } from './trae.js';
export { vscodeProfile } from './vscode.js';
export { windsurfProfile } from './windsurf.js';
export { zedProfile } from './zed.js';

View File

@@ -1,42 +0,0 @@
// Kiro profile for rule-transformer
import { createProfile } from './base-profile.js';
// Create and export kiro profile using the base factory
export const kiroProfile = createProfile({
name: 'kiro',
displayName: 'Kiro',
url: 'kiro.dev',
docsUrl: 'kiro.dev/docs',
profileDir: '.kiro',
rulesDir: '.kiro/steering', // Kiro rules location (full path)
mcpConfig: true,
mcpConfigName: 'settings/mcp.json', // Create directly in settings subdirectory
includeDefaultRules: true, // Include default rules to get all the standard files
targetExtension: '.md',
fileMap: {
// Override specific mappings - the base profile will create:
// 'rules/cursor_rules.mdc': 'kiro_rules.md'
// 'rules/dev_workflow.mdc': 'dev_workflow.md'
// 'rules/self_improve.mdc': 'self_improve.md'
// 'rules/taskmaster.mdc': 'taskmaster.md'
// We can add additional custom mappings here if needed
},
customReplacements: [
// Core Kiro directory structure changes
{ from: /\.cursor\/rules/g, to: '.kiro/steering' },
{ from: /\.cursor\/mcp\.json/g, to: '.kiro/settings/mcp.json' },
// Fix any remaining kiro/rules references that might be created during transformation
{ from: /\.kiro\/rules/g, to: '.kiro/steering' },
// Essential markdown link transformations for Kiro structure
{
from: /\[(.+?)\]\(mdc:\.cursor\/rules\/(.+?)\.mdc\)/g,
to: '[$1](.kiro/steering/$2.md)'
},
// Kiro specific terminology
{ from: /rules directory/g, to: 'steering directory' },
{ from: /cursor rules/gi, to: 'Kiro steering files' }
]
});

View File

@@ -1,183 +0,0 @@
// Opencode profile for rule-transformer
import path from 'path';
import fs from 'fs';
import { log } from '../../scripts/modules/utils.js';
import { createProfile } from './base-profile.js';
/**
* Transform standard MCP config format to OpenCode format
* @param {Object} mcpConfig - Standard MCP configuration object
* @returns {Object} - Transformed OpenCode configuration object
*/
function transformToOpenCodeFormat(mcpConfig) {
const openCodeConfig = {
$schema: 'https://opencode.ai/config.json'
};
// Transform mcpServers to mcp
if (mcpConfig.mcpServers) {
openCodeConfig.mcp = {};
for (const [serverName, serverConfig] of Object.entries(
mcpConfig.mcpServers
)) {
// Transform server configuration
const transformedServer = {
type: 'local'
};
// Combine command and args into single command array
if (serverConfig.command && serverConfig.args) {
transformedServer.command = [
serverConfig.command,
...serverConfig.args
];
} else if (serverConfig.command) {
transformedServer.command = [serverConfig.command];
}
// Add enabled flag
transformedServer.enabled = true;
// Transform env to environment
if (serverConfig.env) {
transformedServer.environment = serverConfig.env;
}
// update with transformed config
openCodeConfig.mcp[serverName] = transformedServer;
}
}
return openCodeConfig;
}
/**
* Lifecycle function called after MCP config generation to transform to OpenCode format
* @param {string} targetDir - Target project directory
* @param {string} assetsDir - Assets directory (unused for OpenCode)
*/
function onPostConvertRulesProfile(targetDir, assetsDir) {
const openCodeConfigPath = path.join(targetDir, 'opencode.json');
if (!fs.existsSync(openCodeConfigPath)) {
log('debug', '[OpenCode] No opencode.json found to transform');
return;
}
try {
// Read the generated standard MCP config
const mcpConfigContent = fs.readFileSync(openCodeConfigPath, 'utf8');
const mcpConfig = JSON.parse(mcpConfigContent);
// Check if it's already in OpenCode format (has $schema)
if (mcpConfig.$schema) {
log(
'info',
'[OpenCode] opencode.json already in OpenCode format, skipping transformation'
);
return;
}
// Transform to OpenCode format
const openCodeConfig = transformToOpenCodeFormat(mcpConfig);
// Write back the transformed config with proper formatting
fs.writeFileSync(
openCodeConfigPath,
JSON.stringify(openCodeConfig, null, 2) + '\n'
);
log('info', '[OpenCode] Transformed opencode.json to OpenCode format');
log(
'debug',
`[OpenCode] Added schema, renamed mcpServers->mcp, combined command+args, added type/enabled, renamed env->environment`
);
} catch (error) {
log(
'error',
`[OpenCode] Failed to transform opencode.json: ${error.message}`
);
}
}
/**
* Lifecycle function called when removing OpenCode profile
* @param {string} targetDir - Target project directory
*/
function onRemoveRulesProfile(targetDir) {
const openCodeConfigPath = path.join(targetDir, 'opencode.json');
if (!fs.existsSync(openCodeConfigPath)) {
log('debug', '[OpenCode] No opencode.json found to clean up');
return;
}
try {
// Read the current config
const configContent = fs.readFileSync(openCodeConfigPath, 'utf8');
const config = JSON.parse(configContent);
// Check if it has the mcp section and taskmaster-ai server
if (config.mcp && config.mcp['taskmaster-ai']) {
// Remove taskmaster-ai server
delete config.mcp['taskmaster-ai'];
// Check if there are other MCP servers
const remainingServers = Object.keys(config.mcp);
if (remainingServers.length === 0) {
// No other servers, remove entire mcp section
delete config.mcp;
}
// Check if config is now empty (only has $schema)
const remainingKeys = Object.keys(config).filter(
(key) => key !== '$schema'
);
if (remainingKeys.length === 0) {
// Config only has schema left, remove entire file
fs.rmSync(openCodeConfigPath, { force: true });
log('info', '[OpenCode] Removed empty opencode.json file');
} else {
// Write back the modified config
fs.writeFileSync(
openCodeConfigPath,
JSON.stringify(config, null, 2) + '\n'
);
log(
'info',
'[OpenCode] Removed TaskMaster from opencode.json, preserved other configurations'
);
}
} else {
log('debug', '[OpenCode] TaskMaster not found in opencode.json');
}
} catch (error) {
log(
'error',
`[OpenCode] Failed to clean up opencode.json: ${error.message}`
);
}
}
// Create and export opencode profile using the base factory
export const opencodeProfile = createProfile({
name: 'opencode',
displayName: 'OpenCode',
url: 'opencode.ai',
docsUrl: 'opencode.ai/docs/',
profileDir: '.', // Root directory
rulesDir: '.', // Root directory for AGENTS.md
mcpConfigName: 'opencode.json', // Override default 'mcp.json'
includeDefaultRules: false,
fileMap: {
'AGENTS.md': 'AGENTS.md'
},
onPostConvert: onPostConvertRulesProfile,
onRemove: onRemoveRulesProfile
});
// Export lifecycle functions separately to avoid naming conflicts
export { onPostConvertRulesProfile, onRemoveRulesProfile };

View File

@@ -1,178 +0,0 @@
// Zed profile for rule-transformer
import path from 'path';
import fs from 'fs';
import { isSilentMode, log } from '../../scripts/modules/utils.js';
import { createProfile } from './base-profile.js';
/**
* Transform standard MCP config format to Zed format
* @param {Object} mcpConfig - Standard MCP configuration object
* @returns {Object} - Transformed Zed configuration object
*/
function transformToZedFormat(mcpConfig) {
const zedConfig = {};
// Transform mcpServers to context_servers
if (mcpConfig.mcpServers) {
zedConfig['context_servers'] = mcpConfig.mcpServers;
}
// Preserve any other existing settings
for (const [key, value] of Object.entries(mcpConfig)) {
if (key !== 'mcpServers') {
zedConfig[key] = value;
}
}
return zedConfig;
}
// Lifecycle functions for Zed profile
function onAddRulesProfile(targetDir, assetsDir) {
// MCP transformation will be handled in onPostConvertRulesProfile
// File copying is handled by the base profile via fileMap
}
function onRemoveRulesProfile(targetDir) {
// Clean up .rules (Zed uses .rules directly in root)
const userRulesFile = path.join(targetDir, '.rules');
try {
// Remove Task Master .rules
if (fs.existsSync(userRulesFile)) {
fs.rmSync(userRulesFile, { force: true });
log('debug', `[Zed] Removed ${userRulesFile}`);
}
} catch (err) {
log('error', `[Zed] Failed to remove Zed instructions: ${err.message}`);
}
// MCP Removal: Remove context_servers section
const mcpConfigPath = path.join(targetDir, '.zed', 'settings.json');
if (!fs.existsSync(mcpConfigPath)) {
log('debug', '[Zed] No .zed/settings.json found to clean up');
return;
}
try {
// Read the current config
const configContent = fs.readFileSync(mcpConfigPath, 'utf8');
const config = JSON.parse(configContent);
// Check if it has the context_servers section and task-master-ai server
if (
config['context_servers'] &&
config['context_servers']['task-master-ai']
) {
// Remove task-master-ai server
delete config['context_servers']['task-master-ai'];
// Check if there are other MCP servers in context_servers
const remainingServers = Object.keys(config['context_servers']);
if (remainingServers.length === 0) {
// No other servers, remove entire context_servers section
delete config['context_servers'];
log('debug', '[Zed] Removed empty context_servers section');
}
// Check if config is now empty
const remainingKeys = Object.keys(config);
if (remainingKeys.length === 0) {
// Config is empty, remove entire file
fs.rmSync(mcpConfigPath, { force: true });
log('info', '[Zed] Removed empty settings.json file');
// Check if .zed directory is empty
const zedDirPath = path.join(targetDir, '.zed');
if (fs.existsSync(zedDirPath)) {
const remainingContents = fs.readdirSync(zedDirPath);
if (remainingContents.length === 0) {
fs.rmSync(zedDirPath, { recursive: true, force: true });
log('debug', '[Zed] Removed empty .zed directory');
}
}
} else {
// Write back the modified config
fs.writeFileSync(
mcpConfigPath,
JSON.stringify(config, null, '\t') + '\n'
);
log(
'info',
'[Zed] Removed TaskMaster from settings.json, preserved other configurations'
);
}
} else {
log('debug', '[Zed] TaskMaster not found in context_servers');
}
} catch (error) {
log('error', `[Zed] Failed to clean up settings.json: ${error.message}`);
}
}
function onPostConvertRulesProfile(targetDir, assetsDir) {
// Handle .rules setup (same as onAddRulesProfile)
onAddRulesProfile(targetDir, assetsDir);
// Transform MCP config to Zed format
const mcpConfigPath = path.join(targetDir, '.zed', 'settings.json');
if (!fs.existsSync(mcpConfigPath)) {
log('debug', '[Zed] No .zed/settings.json found to transform');
return;
}
try {
// Read the generated standard MCP config
const mcpConfigContent = fs.readFileSync(mcpConfigPath, 'utf8');
const mcpConfig = JSON.parse(mcpConfigContent);
// Check if it's already in Zed format (has context_servers)
if (mcpConfig['context_servers']) {
log(
'info',
'[Zed] settings.json already in Zed format, skipping transformation'
);
return;
}
// Transform to Zed format
const zedConfig = transformToZedFormat(mcpConfig);
// Write back the transformed config with proper formatting
fs.writeFileSync(
mcpConfigPath,
JSON.stringify(zedConfig, null, '\t') + '\n'
);
log('info', '[Zed] Transformed settings.json to Zed format');
log('debug', '[Zed] Renamed mcpServers to context_servers');
} catch (error) {
log('error', `[Zed] Failed to transform settings.json: ${error.message}`);
}
}
// Create and export zed profile using the base factory
export const zedProfile = createProfile({
name: 'zed',
displayName: 'Zed',
url: 'zed.dev',
docsUrl: 'zed.dev/docs',
profileDir: '.zed',
rulesDir: '.',
mcpConfig: true,
mcpConfigName: 'settings.json',
includeDefaultRules: false,
fileMap: {
'AGENTS.md': '.rules'
},
onAdd: onAddRulesProfile,
onRemove: onRemoveRulesProfile,
onPostConvert: onPostConvertRulesProfile
});
// Export lifecycle functions separately to avoid naming conflicts
export { onAddRulesProfile, onRemoveRulesProfile, onPostConvertRulesProfile };

View File

@@ -218,16 +218,7 @@ export function initTaskMaster(overrides = {}) {
);
}
// Always set default paths first
// These can be overridden below if needed
paths.configPath = path.join(paths.projectRoot, TASKMASTER_CONFIG_FILE);
paths.statePath = path.join(
paths.taskMasterDir || path.join(paths.projectRoot, TASKMASTER_DIR),
'state.json'
);
paths.tasksPath = path.join(paths.projectRoot, TASKMASTER_TASKS_FILE);
// Handle overrides - only validate/resolve if explicitly provided
// Remaining paths - only resolve if key exists in overrides
if ('configPath' in overrides) {
paths.configPath = resolvePath(
'config file',

View File

@@ -113,12 +113,12 @@ export async function runInteractiveProfilesSetup() {
const hasMcpConfig = profile.mcpConfig === true;
if (!profile.includeDefaultRules) {
// Integration guide profiles (claude, codex, gemini, opencode, zed, amp) - don't include standard coding rules
// Integration guide profiles (claude, codex, gemini) - don't include standard coding rules
if (profileName === 'claude') {
description = 'Integration guide with Task Master slash commands';
} else if (profileName === 'codex') {
description = 'Comprehensive Task Master integration guide';
} else if (hasMcpConfig) {
} else if (profileName === 'gemini') {
description = 'Integration guide and MCP config';
} else {
description = 'Integration guide';
@@ -199,7 +199,7 @@ export function generateProfileSummary(profileName, addResult) {
const profileConfig = getRulesProfile(profileName);
if (!profileConfig.includeDefaultRules) {
// Integration guide profiles (claude, codex, gemini, amp)
// Integration guide profiles (claude, codex, gemini)
return `Summary for ${profileName}: Integration guide installed.`;
} else {
// Rule profiles with coding guidelines
@@ -225,7 +225,7 @@ export function generateProfileRemovalSummary(profileName, removeResult) {
const profileConfig = getRulesProfile(profileName);
if (!profileConfig.includeDefaultRules) {
// Integration guide profiles (claude, codex, gemini, amp)
// Integration guide profiles (claude, codex, gemini)
const baseMessage = `Summary for ${profileName}: Integration guide removed`;
if (removeResult.notice) {
return `${baseMessage} (${removeResult.notice})`;

View File

@@ -1,346 +0,0 @@
import { jest } from '@jest/globals';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { getRulesProfile } from '../../../src/utils/rule-transformer.js';
import { convertAllRulesToProfileRules } from '../../../src/utils/rule-transformer.js';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
describe('Amp Profile Init Functionality', () => {
let tempDir;
let ampProfile;
beforeEach(() => {
// Create temporary directory for testing
tempDir = fs.mkdtempSync(path.join(__dirname, 'temp-amp-'));
// Get the Amp profile
ampProfile = getRulesProfile('amp');
});
afterEach(() => {
// Clean up temporary directory
if (fs.existsSync(tempDir)) {
fs.rmSync(tempDir, { recursive: true, force: true });
}
});
describe('Profile Configuration', () => {
test('should have correct profile metadata', () => {
expect(ampProfile).toBeDefined();
expect(ampProfile.profileName).toBe('amp');
expect(ampProfile.displayName).toBe('Amp');
expect(ampProfile.profileDir).toBe('.vscode');
expect(ampProfile.rulesDir).toBe('.');
expect(ampProfile.mcpConfig).toBe(true);
expect(ampProfile.mcpConfigName).toBe('settings.json');
expect(ampProfile.mcpConfigPath).toBe('.vscode/settings.json');
expect(ampProfile.includeDefaultRules).toBe(false);
});
test('should have correct file mapping', () => {
expect(ampProfile.fileMap).toBeDefined();
expect(ampProfile.fileMap['AGENTS.md']).toBe('.taskmaster/AGENT.md');
});
test('should have lifecycle functions', () => {
expect(typeof ampProfile.onAddRulesProfile).toBe('function');
expect(typeof ampProfile.onRemoveRulesProfile).toBe('function');
expect(typeof ampProfile.onPostConvertRulesProfile).toBe('function');
});
});
describe('AGENT.md Handling', () => {
test('should create AGENT.md with import when none exists', () => {
// Create mock AGENTS.md source
const assetsDir = path.join(tempDir, 'assets');
fs.mkdirSync(assetsDir, { recursive: true });
fs.writeFileSync(
path.join(assetsDir, 'AGENTS.md'),
'Task Master instructions'
);
// Call onAddRulesProfile
ampProfile.onAddRulesProfile(tempDir, assetsDir);
// Check that AGENT.md was created with import
const agentFile = path.join(tempDir, 'AGENT.md');
expect(fs.existsSync(agentFile)).toBe(true);
const content = fs.readFileSync(agentFile, 'utf8');
expect(content).toContain('# Amp Instructions');
expect(content).toContain('## Task Master AI Instructions');
expect(content).toContain('@./.taskmaster/AGENT.md');
// Check that .taskmaster/AGENT.md was created
const taskMasterAgent = path.join(tempDir, '.taskmaster', 'AGENT.md');
expect(fs.existsSync(taskMasterAgent)).toBe(true);
});
test('should append import to existing AGENT.md', () => {
// Create existing AGENT.md
const existingContent =
'# My Existing Amp Instructions\n\nSome content here.';
fs.writeFileSync(path.join(tempDir, 'AGENT.md'), existingContent);
// Create mock AGENTS.md source
const assetsDir = path.join(tempDir, 'assets');
fs.mkdirSync(assetsDir, { recursive: true });
fs.writeFileSync(
path.join(assetsDir, 'AGENTS.md'),
'Task Master instructions'
);
// Call onAddRulesProfile
ampProfile.onAddRulesProfile(tempDir, assetsDir);
// Check that import was appended
const agentFile = path.join(tempDir, 'AGENT.md');
const content = fs.readFileSync(agentFile, 'utf8');
expect(content).toContain('# My Existing Amp Instructions');
expect(content).toContain('Some content here.');
expect(content).toContain('## Task Master AI Instructions');
expect(content).toContain('@./.taskmaster/AGENT.md');
});
test('should not duplicate import if already exists', () => {
// Create AGENT.md with existing import
const existingContent =
"# My Amp Instructions\n\n## Task Master AI Instructions\n**Import Task Master's development workflow commands and guidelines, treat as if import is in the main AGENT.md file.**\n@./.taskmaster/AGENT.md";
fs.writeFileSync(path.join(tempDir, 'AGENT.md'), existingContent);
// Create mock AGENTS.md source
const assetsDir = path.join(tempDir, 'assets');
fs.mkdirSync(assetsDir, { recursive: true });
fs.writeFileSync(
path.join(assetsDir, 'AGENTS.md'),
'Task Master instructions'
);
// Call onAddRulesProfile
ampProfile.onAddRulesProfile(tempDir, assetsDir);
// Check that import was not duplicated
const agentFile = path.join(tempDir, 'AGENT.md');
const content = fs.readFileSync(agentFile, 'utf8');
const importCount = (content.match(/@\.\/.taskmaster\/AGENT\.md/g) || [])
.length;
expect(importCount).toBe(1);
});
});
describe('MCP Configuration', () => {
test('should rename mcpServers to amp.mcpServers', () => {
// Create .vscode directory and settings.json with mcpServers
const vscodeDirPath = path.join(tempDir, '.vscode');
fs.mkdirSync(vscodeDirPath, { recursive: true });
const initialConfig = {
mcpServers: {
'task-master-ai': {
command: 'npx',
args: ['-y', '--package=task-master-ai', 'task-master-ai']
}
}
};
fs.writeFileSync(
path.join(vscodeDirPath, 'settings.json'),
JSON.stringify(initialConfig, null, '\t')
);
// Call onPostConvertRulesProfile (which should transform mcpServers to amp.mcpServers)
ampProfile.onPostConvertRulesProfile(
tempDir,
path.join(tempDir, 'assets')
);
// Check that mcpServers was renamed to amp.mcpServers
const settingsFile = path.join(vscodeDirPath, 'settings.json');
const content = fs.readFileSync(settingsFile, 'utf8');
const config = JSON.parse(content);
expect(config.mcpServers).toBeUndefined();
expect(config['amp.mcpServers']).toBeDefined();
expect(config['amp.mcpServers']['task-master-ai']).toBeDefined();
});
test('should not rename if amp.mcpServers already exists', () => {
// Create .vscode directory and settings.json with both mcpServers and amp.mcpServers
const vscodeDirPath = path.join(tempDir, '.vscode');
fs.mkdirSync(vscodeDirPath, { recursive: true });
const initialConfig = {
mcpServers: {
'some-other-server': {
command: 'other-command'
}
},
'amp.mcpServers': {
'task-master-ai': {
command: 'npx',
args: ['-y', '--package=task-master-ai', 'task-master-ai']
}
}
};
fs.writeFileSync(
path.join(vscodeDirPath, 'settings.json'),
JSON.stringify(initialConfig, null, '\t')
);
// Call onAddRulesProfile
ampProfile.onAddRulesProfile(tempDir, path.join(tempDir, 'assets'));
// Check that both sections remain unchanged
const settingsFile = path.join(vscodeDirPath, 'settings.json');
const content = fs.readFileSync(settingsFile, 'utf8');
const config = JSON.parse(content);
expect(config.mcpServers).toBeDefined();
expect(config.mcpServers['some-other-server']).toBeDefined();
expect(config['amp.mcpServers']).toBeDefined();
expect(config['amp.mcpServers']['task-master-ai']).toBeDefined();
});
});
describe('Removal Functionality', () => {
test('should remove AGENT.md import and clean up files', () => {
// Setup: Create AGENT.md with import and .taskmaster/AGENT.md
const agentContent =
"# My Amp Instructions\n\nSome content.\n\n## Task Master AI Instructions\n**Import Task Master's development workflow commands and guidelines, treat as if import is in the main AGENT.md file.**\n@./.taskmaster/AGENT.md\n";
fs.writeFileSync(path.join(tempDir, 'AGENT.md'), agentContent);
fs.mkdirSync(path.join(tempDir, '.taskmaster'), { recursive: true });
fs.writeFileSync(
path.join(tempDir, '.taskmaster', 'AGENT.md'),
'Task Master instructions'
);
// Call onRemoveRulesProfile
ampProfile.onRemoveRulesProfile(tempDir);
// Check that .taskmaster/AGENT.md was removed
expect(fs.existsSync(path.join(tempDir, '.taskmaster', 'AGENT.md'))).toBe(
false
);
// Check that import was removed from AGENT.md
const remainingContent = fs.readFileSync(
path.join(tempDir, 'AGENT.md'),
'utf8'
);
expect(remainingContent).not.toContain('## Task Master AI Instructions');
expect(remainingContent).not.toContain('@./.taskmaster/AGENT.md');
expect(remainingContent).toContain('# My Amp Instructions');
expect(remainingContent).toContain('Some content.');
});
test('should remove empty AGENT.md if only contained import', () => {
// Setup: Create AGENT.md with only import
const agentContent =
"# Amp Instructions\n\n## Task Master AI Instructions\n**Import Task Master's development workflow commands and guidelines, treat as if import is in the main AGENT.md file.**\n@./.taskmaster/AGENT.md";
fs.writeFileSync(path.join(tempDir, 'AGENT.md'), agentContent);
fs.mkdirSync(path.join(tempDir, '.taskmaster'), { recursive: true });
fs.writeFileSync(
path.join(tempDir, '.taskmaster', 'AGENT.md'),
'Task Master instructions'
);
// Call onRemoveRulesProfile
ampProfile.onRemoveRulesProfile(tempDir);
// Check that AGENT.md was removed
expect(fs.existsSync(path.join(tempDir, 'AGENT.md'))).toBe(false);
});
test('should remove amp.mcpServers section from settings.json', () => {
// Setup: Create .vscode/settings.json with amp.mcpServers and other settings
const vscodeDirPath = path.join(tempDir, '.vscode');
fs.mkdirSync(vscodeDirPath, { recursive: true });
const initialConfig = {
'amp.mcpServers': {
'task-master-ai': {
command: 'npx',
args: ['-y', '--package=task-master-ai', 'task-master-ai']
}
},
'other.setting': 'value'
};
fs.writeFileSync(
path.join(vscodeDirPath, 'settings.json'),
JSON.stringify(initialConfig, null, '\t')
);
// Call onRemoveRulesProfile
ampProfile.onRemoveRulesProfile(tempDir);
// Check that amp.mcpServers was removed but other settings remain
const settingsFile = path.join(vscodeDirPath, 'settings.json');
expect(fs.existsSync(settingsFile)).toBe(true);
const content = fs.readFileSync(settingsFile, 'utf8');
const config = JSON.parse(content);
expect(config['amp.mcpServers']).toBeUndefined();
expect(config['other.setting']).toBe('value');
});
test('should remove settings.json and .vscode directory if empty after removal', () => {
// Setup: Create .vscode/settings.json with only amp.mcpServers
const vscodeDirPath = path.join(tempDir, '.vscode');
fs.mkdirSync(vscodeDirPath, { recursive: true });
const initialConfig = {
'amp.mcpServers': {
'task-master-ai': {
command: 'npx',
args: ['-y', '--package=task-master-ai', 'task-master-ai']
}
}
};
fs.writeFileSync(
path.join(vscodeDirPath, 'settings.json'),
JSON.stringify(initialConfig, null, '\t')
);
// Call onRemoveRulesProfile
ampProfile.onRemoveRulesProfile(tempDir);
// Check that settings.json and .vscode directory were removed
expect(fs.existsSync(path.join(vscodeDirPath, 'settings.json'))).toBe(
false
);
expect(fs.existsSync(vscodeDirPath)).toBe(false);
});
});
describe('Full Integration', () => {
test('should work with convertAllRulesToProfileRules', () => {
// This test ensures the profile works with the full rule transformer
const result = convertAllRulesToProfileRules(tempDir, ampProfile);
expect(result.success).toBeGreaterThan(0);
expect(result.failed).toBe(0);
// Check that .taskmaster/AGENT.md was created
expect(fs.existsSync(path.join(tempDir, '.taskmaster', 'AGENT.md'))).toBe(
true
);
// Check that AGENT.md was created with import
expect(fs.existsSync(path.join(tempDir, 'AGENT.md'))).toBe(true);
const agentContent = fs.readFileSync(
path.join(tempDir, 'AGENT.md'),
'utf8'
);
expect(agentContent).toContain('@./.taskmaster/AGENT.md');
});
});
});

View File

@@ -21,22 +21,19 @@ describe('Claude Profile Initialization Functionality', () => {
expect(claudeProfileContent).toContain("displayName: 'Claude Code'");
expect(claudeProfileContent).toContain("profileDir: '.'"); // non-default
expect(claudeProfileContent).toContain("rulesDir: '.'"); // non-default
expect(claudeProfileContent).toContain("mcpConfigName: '.mcp.json'"); // non-default
expect(claudeProfileContent).toContain('mcpConfig: false'); // non-default
expect(claudeProfileContent).toContain('includeDefaultRules: false'); // non-default
expect(claudeProfileContent).toContain(
"'AGENTS.md': '.taskmaster/CLAUDE.md'"
);
expect(claudeProfileContent).toContain("'AGENTS.md': 'CLAUDE.md'");
// Check the final computed properties on the profile object
expect(claudeProfile.profileName).toBe('claude');
expect(claudeProfile.displayName).toBe('Claude Code');
expect(claudeProfile.profileDir).toBe('.');
expect(claudeProfile.rulesDir).toBe('.');
expect(claudeProfile.mcpConfig).toBe(true); // default from base profile
expect(claudeProfile.mcpConfigName).toBe('.mcp.json'); // explicitly set
expect(claudeProfile.mcpConfigPath).toBe('.mcp.json'); // computed
expect(claudeProfile.mcpConfig).toBe(false);
expect(claudeProfile.mcpConfigName).toBe(null); // computed
expect(claudeProfile.includeDefaultRules).toBe(false);
expect(claudeProfile.fileMap['AGENTS.md']).toBe('.taskmaster/CLAUDE.md');
expect(claudeProfile.fileMap['AGENTS.md']).toBe('CLAUDE.md');
});
test('claude.js has lifecycle functions for file management', () => {
@@ -47,11 +44,9 @@ describe('Claude Profile Initialization Functionality', () => {
);
});
test('claude.js handles .claude directory and .taskmaster/CLAUDE.md import in lifecycle functions', () => {
test('claude.js handles .claude directory in lifecycle functions', () => {
expect(claudeProfileContent).toContain('.claude');
expect(claudeProfileContent).toContain('copyRecursiveSync');
expect(claudeProfileContent).toContain('.taskmaster/CLAUDE.md');
expect(claudeProfileContent).toContain('@./.taskmaster/CLAUDE.md');
});
test('claude.js has proper error handling in lifecycle functions', () => {

View File

@@ -1,85 +0,0 @@
import fs from 'fs';
import path from 'path';
import { opencodeProfile } from '../../../src/profiles/opencode.js';
describe('OpenCode Profile Initialization Functionality', () => {
let opencodeProfileContent;
beforeAll(() => {
const opencodeJsPath = path.join(
process.cwd(),
'src',
'profiles',
'opencode.js'
);
opencodeProfileContent = fs.readFileSync(opencodeJsPath, 'utf8');
});
test('opencode.js has correct asset-only profile configuration', () => {
// Check for explicit, non-default values in the source file
expect(opencodeProfileContent).toContain("name: 'opencode'");
expect(opencodeProfileContent).toContain("displayName: 'OpenCode'");
expect(opencodeProfileContent).toContain("url: 'opencode.ai'");
expect(opencodeProfileContent).toContain("docsUrl: 'opencode.ai/docs/'");
expect(opencodeProfileContent).toContain("profileDir: '.'"); // non-default
expect(opencodeProfileContent).toContain("rulesDir: '.'"); // non-default
expect(opencodeProfileContent).toContain("mcpConfigName: 'opencode.json'"); // non-default
expect(opencodeProfileContent).toContain('includeDefaultRules: false'); // non-default
expect(opencodeProfileContent).toContain("'AGENTS.md': 'AGENTS.md'");
// Check the final computed properties on the profile object
expect(opencodeProfile.profileName).toBe('opencode');
expect(opencodeProfile.displayName).toBe('OpenCode');
expect(opencodeProfile.profileDir).toBe('.');
expect(opencodeProfile.rulesDir).toBe('.');
expect(opencodeProfile.mcpConfig).toBe(true); // computed from mcpConfigName
expect(opencodeProfile.mcpConfigName).toBe('opencode.json');
expect(opencodeProfile.mcpConfigPath).toBe('opencode.json'); // computed
expect(opencodeProfile.includeDefaultRules).toBe(false);
expect(opencodeProfile.fileMap['AGENTS.md']).toBe('AGENTS.md');
});
test('opencode.js has lifecycle functions for MCP config transformation', () => {
expect(opencodeProfileContent).toContain(
'function onPostConvertRulesProfile'
);
expect(opencodeProfileContent).toContain('function onRemoveRulesProfile');
expect(opencodeProfileContent).toContain('transformToOpenCodeFormat');
});
test('opencode.js handles opencode.json transformation in lifecycle functions', () => {
expect(opencodeProfileContent).toContain('opencode.json');
expect(opencodeProfileContent).toContain('transformToOpenCodeFormat');
expect(opencodeProfileContent).toContain('$schema');
expect(opencodeProfileContent).toContain('mcpServers');
expect(opencodeProfileContent).toContain('mcp');
});
test('opencode.js has proper error handling in lifecycle functions', () => {
expect(opencodeProfileContent).toContain('try {');
expect(opencodeProfileContent).toContain('} catch (error) {');
expect(opencodeProfileContent).toContain('log(');
});
test('opencode.js uses custom MCP config name', () => {
// OpenCode uses opencode.json instead of mcp.json
expect(opencodeProfileContent).toContain("mcpConfigName: 'opencode.json'");
// Should not contain mcp.json as a config value (comments are OK)
expect(opencodeProfileContent).not.toMatch(
/mcpConfigName:\s*['"]mcp\.json['"]/
);
});
test('opencode.js has transformation logic for OpenCode format', () => {
// Check for transformation function
expect(opencodeProfileContent).toContain('transformToOpenCodeFormat');
// Check for specific transformation logic
expect(opencodeProfileContent).toContain('mcpServers');
expect(opencodeProfileContent).toContain('command');
expect(opencodeProfileContent).toContain('args');
expect(opencodeProfileContent).toContain('environment');
expect(opencodeProfileContent).toContain('enabled');
expect(opencodeProfileContent).toContain('type');
});
});

Some files were not shown because too many files have changed in this diff Show More